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:
authorYimingWu <xp8110@outlook.com>2019-09-04 06:30:16 +0300
committerYimingWu <xp8110@outlook.com>2019-09-04 06:30:16 +0300
commit55a6fc0be3ffa823052aba45bec12c6902e7bd53 (patch)
treefb8cb4e411e22fcb49e443ea4da636660618877f /source/blender/editors
parentafac2afbfcdb06a2d2655e63d0b8bc35b1aba18e (diff)
parentda25aca2677ec2b566cad1809eebceee22b28b53 (diff)
Merge remote-tracking branch 'origin/master' into soc-2019-npr
Diffstat (limited to 'source/blender/editors')
-rw-r--r--source/blender/editors/animation/anim_channels_defines.c2
-rw-r--r--source/blender/editors/animation/anim_channels_edit.c1
-rw-r--r--source/blender/editors/animation/anim_filter.c5
-rw-r--r--source/blender/editors/animation/anim_ipo_utils.c2
-rw-r--r--source/blender/editors/animation/anim_markers.c2
-rw-r--r--source/blender/editors/animation/anim_motion_paths.c5
-rw-r--r--source/blender/editors/animation/drivers.c50
-rw-r--r--source/blender/editors/animation/fmodifier_ui.c4
-rw-r--r--source/blender/editors/animation/keyframes_draw.c4
-rw-r--r--source/blender/editors/animation/keyframing.c36
-rw-r--r--source/blender/editors/animation/keyingsets.c18
-rw-r--r--source/blender/editors/armature/armature_add.c2
-rw-r--r--source/blender/editors/armature/armature_edit.c34
-rw-r--r--source/blender/editors/armature/armature_intern.h1
-rw-r--r--source/blender/editors/armature/armature_select.c16
-rw-r--r--source/blender/editors/armature/armature_utils.c47
-rw-r--r--source/blender/editors/armature/pose_select.c17
-rw-r--r--source/blender/editors/armature/pose_slide.c2
-rw-r--r--source/blender/editors/curve/editcurve.c8
-rw-r--r--source/blender/editors/curve/editcurve_paint.c4
-rw-r--r--source/blender/editors/curve/editcurve_select.c2
-rw-r--r--source/blender/editors/curve/editfont.c214
-rw-r--r--source/blender/editors/gizmo_library/gizmo_types/arrow3d_gizmo.c4
-rw-r--r--source/blender/editors/gizmo_library/gizmo_types/cage3d_gizmo.c2
-rw-r--r--source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c1
-rw-r--r--source/blender/editors/gpencil/annotate_paint.c11
-rw-r--r--source/blender/editors/gpencil/gpencil_add_monkey.c2
-rw-r--r--source/blender/editors/gpencil/gpencil_add_stroke.c2
-rw-r--r--source/blender/editors/gpencil/gpencil_brush.c159
-rw-r--r--source/blender/editors/gpencil/gpencil_data.c49
-rw-r--r--source/blender/editors/gpencil/gpencil_edit.c177
-rw-r--r--source/blender/editors/gpencil/gpencil_fill.c31
-rw-r--r--source/blender/editors/gpencil/gpencil_intern.h23
-rw-r--r--source/blender/editors/gpencil/gpencil_merge.c2
-rw-r--r--source/blender/editors/gpencil/gpencil_paint.c35
-rw-r--r--source/blender/editors/gpencil/gpencil_primitive.c12
-rw-r--r--source/blender/editors/gpencil/gpencil_select.c86
-rw-r--r--source/blender/editors/gpencil/gpencil_utils.c23
-rw-r--r--source/blender/editors/include/BIF_glutil.h1
-rw-r--r--source/blender/editors/include/ED_armature.h17
-rw-r--r--source/blender/editors/include/ED_clip.h1
-rw-r--r--source/blender/editors/include/ED_fileselect.h47
-rw-r--r--source/blender/editors/include/ED_gizmo_library.h2
-rw-r--r--source/blender/editors/include/ED_gpencil.h5
-rw-r--r--source/blender/editors/include/ED_image.h1
-rw-r--r--source/blender/editors/include/ED_keyframes_draw.h1
-rw-r--r--source/blender/editors/include/ED_keyframing.h2
-rw-r--r--source/blender/editors/include/ED_markers.h1
-rw-r--r--source/blender/editors/include/ED_mesh.h47
-rw-r--r--source/blender/editors/include/ED_object.h10
-rw-r--r--source/blender/editors/include/ED_outliner.h14
-rw-r--r--source/blender/editors/include/ED_screen.h3
-rw-r--r--source/blender/editors/include/ED_sculpt.h3
-rw-r--r--source/blender/editors/include/ED_time_scrub_ui.h1
-rw-r--r--source/blender/editors/include/ED_transform.h24
-rw-r--r--source/blender/editors/include/ED_transform_snap_object_context.h4
-rw-r--r--source/blender/editors/include/ED_uvedit.h1
-rw-r--r--source/blender/editors/include/ED_view3d.h7
-rw-r--r--source/blender/editors/include/UI_icons.h10
-rw-r--r--source/blender/editors/include/UI_interface.h3
-rw-r--r--source/blender/editors/include/UI_resources.h1
-rw-r--r--source/blender/editors/interface/interface.c24
-rw-r--r--source/blender/editors/interface/interface_anim.c4
-rw-r--r--source/blender/editors/interface/interface_context_menu.c42
-rw-r--r--source/blender/editors/interface/interface_draw.c2
-rw-r--r--source/blender/editors/interface/interface_eyedropper_datablock.c53
-rw-r--r--source/blender/editors/interface/interface_eyedropper_driver.c6
-rw-r--r--source/blender/editors/interface/interface_handlers.c30
-rw-r--r--source/blender/editors/interface/interface_icons.c11
-rw-r--r--source/blender/editors/interface/interface_layout.c10
-rw-r--r--source/blender/editors/interface/interface_ops.c124
-rw-r--r--source/blender/editors/interface/interface_region_popup.c8
-rw-r--r--source/blender/editors/interface/interface_region_tooltip.c10
-rw-r--r--source/blender/editors/interface/interface_templates.c83
-rw-r--r--source/blender/editors/interface/interface_utils.c4
-rw-r--r--source/blender/editors/interface/interface_widgets.c70
-rw-r--r--source/blender/editors/interface/resources.c4
-rw-r--r--source/blender/editors/interface/view2d_ops.c2
-rw-r--r--source/blender/editors/mask/mask_shapekey.c2
-rw-r--r--source/blender/editors/mesh/CMakeLists.txt1
-rw-r--r--source/blender/editors/mesh/editmesh_automerge.c517
-rw-r--r--source/blender/editors/mesh/editmesh_extrude_spin_gizmo.c13
-rw-r--r--source/blender/editors/mesh/editmesh_knife.c4
-rw-r--r--source/blender/editors/mesh/editmesh_polybuild.c189
-rw-r--r--source/blender/editors/mesh/editmesh_preselect_elem.c206
-rw-r--r--source/blender/editors/mesh/editmesh_select.c191
-rw-r--r--source/blender/editors/mesh/editmesh_tools.c14
-rw-r--r--source/blender/editors/mesh/editmesh_utils.c20
-rw-r--r--source/blender/editors/mesh/mesh_data.c126
-rw-r--r--source/blender/editors/mesh/mesh_intern.h2
-rw-r--r--source/blender/editors/mesh/mesh_ops.c21
-rw-r--r--source/blender/editors/object/CMakeLists.txt1
-rw-r--r--source/blender/editors/object/object_add.c24
-rw-r--r--source/blender/editors/object/object_constraint.c22
-rw-r--r--source/blender/editors/object/object_data_transform.c333
-rw-r--r--source/blender/editors/object/object_edit.c43
-rw-r--r--source/blender/editors/object/object_gpencil_modifier.c4
-rw-r--r--source/blender/editors/object/object_hook.c4
-rw-r--r--source/blender/editors/object/object_modifier.c12
-rw-r--r--source/blender/editors/object/object_relations.c22
-rw-r--r--source/blender/editors/object/object_remesh.c4
-rw-r--r--source/blender/editors/object/object_select.c19
-rw-r--r--source/blender/editors/object/object_shader_fx.c4
-rw-r--r--source/blender/editors/object/object_transform.c61
-rw-r--r--source/blender/editors/object/object_vgroup.c2
-rw-r--r--source/blender/editors/physics/particle_object.c11
-rw-r--r--source/blender/editors/physics/physics_intern.h1
-rw-r--r--source/blender/editors/physics/physics_pointcache.c12
-rw-r--r--source/blender/editors/render/render_intern.h1
-rw-r--r--source/blender/editors/render/render_shading.c8
-rw-r--r--source/blender/editors/render/render_view.c4
-rw-r--r--source/blender/editors/screen/area.c39
-rw-r--r--source/blender/editors/screen/screen_context.c2
-rw-r--r--source/blender/editors/screen/screen_edit.c98
-rw-r--r--source/blender/editors/screen/screen_ops.c331
-rw-r--r--source/blender/editors/sculpt_paint/paint_cursor.c229
-rw-r--r--source/blender/editors/sculpt_paint/paint_image_proj.c4
-rw-r--r--source/blender/editors/sculpt_paint/paint_intern.h1
-rw-r--r--source/blender/editors/sculpt_paint/paint_stroke.c118
-rw-r--r--source/blender/editors/sculpt_paint/sculpt.c223
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_intern.h18
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_undo.c2
-rw-r--r--source/blender/editors/sound/sound_ops.c2
-rw-r--r--source/blender/editors/space_action/action_data.c2
-rw-r--r--source/blender/editors/space_action/action_draw.c352
-rw-r--r--source/blender/editors/space_action/action_intern.h2
-rw-r--r--source/blender/editors/space_action/action_select.c10
-rw-r--r--source/blender/editors/space_action/space_action.c2
-rw-r--r--source/blender/editors/space_buttons/buttons_context.c12
-rw-r--r--source/blender/editors/space_buttons/buttons_ops.c2
-rw-r--r--source/blender/editors/space_buttons/buttons_texture.c2
-rw-r--r--source/blender/editors/space_buttons/space_buttons.c4
-rw-r--r--source/blender/editors/space_clip/clip_dopesheet_draw.c3
-rw-r--r--source/blender/editors/space_console/space_console.c3
-rw-r--r--source/blender/editors/space_file/file_draw.c637
-rw-r--r--source/blender/editors/space_file/file_intern.h16
-rw-r--r--source/blender/editors/space_file/file_ops.c144
-rw-r--r--source/blender/editors/space_file/file_panels.c1
-rw-r--r--source/blender/editors/space_file/filelist.c166
-rw-r--r--source/blender/editors/space_file/filelist.h6
-rw-r--r--source/blender/editors/space_file/filesel.c274
-rw-r--r--source/blender/editors/space_file/space_file.c118
-rw-r--r--source/blender/editors/space_graph/graph_buttons.c9
-rw-r--r--source/blender/editors/space_graph/graph_draw.c6
-rw-r--r--source/blender/editors/space_graph/graph_edit.c2
-rw-r--r--source/blender/editors/space_graph/graph_intern.h1
-rw-r--r--source/blender/editors/space_graph/space_graph.c2
-rw-r--r--source/blender/editors/space_image/image_buttons.c2
-rw-r--r--source/blender/editors/space_image/image_intern.h1
-rw-r--r--source/blender/editors/space_image/image_ops.c3
-rw-r--r--source/blender/editors/space_image/space_image.c2
-rw-r--r--source/blender/editors/space_info/info_stats.c2
-rw-r--r--source/blender/editors/space_info/textview.c2
-rw-r--r--source/blender/editors/space_nla/nla_buttons.c6
-rw-r--r--source/blender/editors/space_nla/nla_channels.c6
-rw-r--r--source/blender/editors/space_nla/nla_draw.c4
-rw-r--r--source/blender/editors/space_nla/space_nla.c2
-rw-r--r--source/blender/editors/space_node/drawnode.c46
-rw-r--r--source/blender/editors/space_node/node_draw.c143
-rw-r--r--source/blender/editors/space_node/node_edit.c10
-rw-r--r--source/blender/editors/space_node/node_select.c16
-rw-r--r--source/blender/editors/space_node/node_templates.c2
-rw-r--r--source/blender/editors/space_outliner/CMakeLists.txt1
-rw-r--r--source/blender/editors/space_outliner/outliner_collections.c8
-rw-r--r--source/blender/editors/space_outliner/outliner_dragdrop.c191
-rw-r--r--source/blender/editors/space_outliner/outliner_draw.c262
-rw-r--r--source/blender/editors/space_outliner/outliner_edit.c255
-rw-r--r--source/blender/editors/space_outliner/outliner_intern.h35
-rw-r--r--source/blender/editors/space_outliner/outliner_ops.c1
-rw-r--r--source/blender/editors/space_outliner/outliner_select.c449
-rw-r--r--source/blender/editors/space_outliner/outliner_sync.c575
-rw-r--r--source/blender/editors/space_outliner/outliner_tools.c149
-rw-r--r--source/blender/editors/space_outliner/outliner_tree.c25
-rw-r--r--source/blender/editors/space_outliner/outliner_utils.c140
-rw-r--r--source/blender/editors/space_outliner/space_outliner.c21
-rw-r--r--source/blender/editors/space_sequencer/sequencer_draw.c5
-rw-r--r--source/blender/editors/space_sequencer/sequencer_edit.c2
-rw-r--r--source/blender/editors/space_sequencer/sequencer_intern.h1
-rw-r--r--source/blender/editors/space_sequencer/sequencer_select.c23
-rw-r--r--source/blender/editors/space_sequencer/space_sequencer.c2
-rw-r--r--source/blender/editors/space_text/space_text.c3
-rw-r--r--source/blender/editors/space_text/text_ops.c2
-rw-r--r--source/blender/editors/space_view3d/drawobject.c2
-rw-r--r--source/blender/editors/space_view3d/space_view3d.c4
-rw-r--r--source/blender/editors/space_view3d/view3d_camera_control.c7
-rw-r--r--source/blender/editors/space_view3d/view3d_draw.c118
-rw-r--r--source/blender/editors/space_view3d/view3d_draw_legacy.c1
-rw-r--r--source/blender/editors/space_view3d/view3d_edit.c7
-rw-r--r--source/blender/editors/space_view3d/view3d_gizmo_camera.c8
-rw-r--r--source/blender/editors/space_view3d/view3d_gizmo_preselect_type.c126
-rw-r--r--source/blender/editors/space_view3d/view3d_gizmo_ruler.c18
-rw-r--r--source/blender/editors/space_view3d/view3d_intern.h7
-rw-r--r--source/blender/editors/space_view3d/view3d_select.c47
-rw-r--r--source/blender/editors/space_view3d/view3d_utils.c4
-rw-r--r--source/blender/editors/transform/transform.c4
-rw-r--r--source/blender/editors/transform/transform.h10
-rw-r--r--source/blender/editors/transform/transform_conversions.c592
-rw-r--r--source/blender/editors/transform/transform_generics.c11
-rw-r--r--source/blender/editors/transform/transform_gizmo_3d.c3
-rw-r--r--source/blender/editors/transform/transform_input.c2
-rw-r--r--source/blender/editors/transform/transform_ops.c26
-rw-r--r--source/blender/editors/transform/transform_snap.c95
-rw-r--r--source/blender/editors/transform/transform_snap_object.c226
-rw-r--r--source/blender/editors/undo/ed_undo.c5
-rw-r--r--source/blender/editors/uvedit/uvedit_draw.c58
-rw-r--r--source/blender/editors/uvedit/uvedit_intern.h1
-rw-r--r--source/blender/editors/uvedit/uvedit_ops.c2
-rw-r--r--source/blender/editors/uvedit/uvedit_unwrap_ops.c2
208 files changed, 7599 insertions, 2713 deletions
diff --git a/source/blender/editors/animation/anim_channels_defines.c b/source/blender/editors/animation/anim_channels_defines.c
index 1649744ba8d..d80b96f0d74 100644
--- a/source/blender/editors/animation/anim_channels_defines.c
+++ b/source/blender/editors/animation/anim_channels_defines.c
@@ -4847,7 +4847,7 @@ void ANIM_channel_draw_widgets(const bContext *C,
/* step 4) draw text - check if renaming widget is in use... */
if (is_being_renamed) {
- PointerRNA ptr = {{NULL}};
+ PointerRNA ptr = {NULL};
PropertyRNA *prop = NULL;
/* draw renaming widget if we can get RNA pointer for it
diff --git a/source/blender/editors/animation/anim_channels_edit.c b/source/blender/editors/animation/anim_channels_edit.c
index 7e913014a87..61b8e4a2341 100644
--- a/source/blender/editors/animation/anim_channels_edit.c
+++ b/source/blender/editors/animation/anim_channels_edit.c
@@ -3114,6 +3114,7 @@ static int mouse_anim_channels(bContext *C, bAnimContext *ac, int channel_index,
ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, gpl, ANIMTYPE_GPLAYER);
/* update other layer status */
BKE_gpencil_layer_setactive(gpd, gpl);
+ BKE_gpencil_layer_autolock_set(gpd);
}
/* Grease Pencil updates */
diff --git a/source/blender/editors/animation/anim_filter.c b/source/blender/editors/animation/anim_filter.c
index a78a63f1347..48493c9e961 100644
--- a/source/blender/editors/animation/anim_filter.c
+++ b/source/blender/editors/animation/anim_filter.c
@@ -630,9 +630,8 @@ static bAnimListElem *make_new_animlistelem(void *data,
/* do specifics */
switch (datatype) {
case ANIMTYPE_SUMMARY: {
- /* nothing to include for now... this is just a dummy wrappy around all the other channels
- * in the DopeSheet, and gets included at the start of the list
- */
+ /* Nothing to include for now... this is just a dummy wrapper around
+ * all the other channels in the DopeSheet, and gets included at the start of the list. */
ale->key_data = NULL;
ale->datatype = ALE_ALL;
break;
diff --git a/source/blender/editors/animation/anim_ipo_utils.c b/source/blender/editors/animation/anim_ipo_utils.c
index fad9a1a8e49..5b729c856c0 100644
--- a/source/blender/editors/animation/anim_ipo_utils.c
+++ b/source/blender/editors/animation/anim_ipo_utils.c
@@ -122,7 +122,7 @@ int getname_anim_fcurve(char *name, ID *id, FCurve *fcu)
MEM_freeN(constName);
}
}
- else if (ptr.data != ptr.id.data) {
+ else if (ptr.data != ptr.owner_id) {
PropertyRNA *nameprop = RNA_struct_name_property(ptr.type);
if (nameprop) {
/* this gets a string which will need to be freed */
diff --git a/source/blender/editors/animation/anim_markers.c b/source/blender/editors/animation/anim_markers.c
index ded59466370..8c873eb6b45 100644
--- a/source/blender/editors/animation/anim_markers.c
+++ b/source/blender/editors/animation/anim_markers.c
@@ -486,7 +486,7 @@ static void draw_marker(
float name_y = UI_DPI_FAC * 18;
/* Give an offset to the marker name when selected,
* or when near the current frame (5 frames range, starting from the current one). */
- if ((marker->flag & SELECT) || (IN_RANGE_INCL(marker->frame, cfra, cfra - 4))) {
+ if ((marker->flag & SELECT) || (IN_RANGE_INCL(marker->frame, cfra - 4, cfra))) {
name_y += UI_DPI_FAC * 10;
}
draw_marker_name(fstyle, marker, xpos, name_y);
diff --git a/source/blender/editors/animation/anim_motion_paths.c b/source/blender/editors/animation/anim_motion_paths.c
index 7a5b57b1ce6..bd4886817cd 100644
--- a/source/blender/editors/animation/anim_motion_paths.c
+++ b/source/blender/editors/animation/anim_motion_paths.c
@@ -60,9 +60,8 @@ typedef struct MPathTarget {
Object *ob; /* source object */
bPoseChannel *pchan; /* source posechannel (if applicable) */
- /* "Evaluated" Copies (these come from the background COW copie
- * that provide all the coordinates we want to save off)
- */
+ /* "Evaluated" Copies (these come from the background COW copy
+ * that provide all the coordinates we want to save off). */
Object *ob_eval; /* evaluated object */
} MPathTarget;
diff --git a/source/blender/editors/animation/drivers.c b/source/blender/editors/animation/drivers.c
index 935d11a388f..7b9e6a10f44 100644
--- a/source/blender/editors/animation/drivers.c
+++ b/source/blender/editors/animation/drivers.c
@@ -218,7 +218,7 @@ static int add_driver_with_target(ReportList *UNUSED(reports),
/* Create a driver variable for the target
* - For transform properties, we want to automatically use "transform channel" instead
- * (The only issue is with quat rotations vs euler channels...)
+ * (The only issue is with quaternion rotations vs euler channels...)
* - To avoid problems with transform properties depending on the final transform that they
* control (thus creating pseudo-cycles - see T48734), we don't use transform channels
* when both the source and destinations are in same places.
@@ -934,7 +934,7 @@ static const EnumPropertyItem *driver_mapping_type_itemsf(bContext *C,
EnumPropertyItem *input = prop_driver_create_mapping_types;
EnumPropertyItem *item = NULL;
- PointerRNA ptr = {{NULL}};
+ PointerRNA ptr = {NULL};
PropertyRNA *prop = NULL;
int index;
@@ -946,7 +946,7 @@ static const EnumPropertyItem *driver_mapping_type_itemsf(bContext *C,
UI_context_active_but_prop_get(C, &ptr, &prop, &index);
- if (ptr.id.data && ptr.data && prop && RNA_property_animateable(&ptr, prop)) {
+ if (ptr.owner_id && ptr.data && prop && RNA_property_animateable(&ptr, prop)) {
const bool is_array = RNA_property_array_check(prop);
while (input->identifier) {
@@ -971,7 +971,7 @@ static const EnumPropertyItem *driver_mapping_type_itemsf(bContext *C,
static bool add_driver_button_poll(bContext *C)
{
- PointerRNA ptr = {{NULL}};
+ PointerRNA ptr = {NULL};
PropertyRNA *prop = NULL;
int index;
bool driven, special;
@@ -979,7 +979,7 @@ static bool add_driver_button_poll(bContext *C)
/* this operator can only run if there's a property button active, and it can be animated */
UI_context_active_but_prop_get(C, &ptr, &prop, &index);
- if (!(ptr.id.data && ptr.data && prop)) {
+ if (!(ptr.owner_id && ptr.data && prop)) {
return false;
}
if (!RNA_property_animateable(&ptr, prop)) {
@@ -995,7 +995,7 @@ static bool add_driver_button_poll(bContext *C)
* (i.e. "manual/add later"). */
static int add_driver_button_none(bContext *C, wmOperator *op, short mapping_type)
{
- PointerRNA ptr = {{NULL}};
+ PointerRNA ptr = {NULL};
PropertyRNA *prop = NULL;
int index;
int success = 0;
@@ -1006,12 +1006,13 @@ static int add_driver_button_none(bContext *C, wmOperator *op, short mapping_typ
index = -1;
}
- if (ptr.id.data && ptr.data && prop && RNA_property_animateable(&ptr, prop)) {
+ if (ptr.owner_id && ptr.data && prop && RNA_property_animateable(&ptr, prop)) {
char *path = BKE_animdata_driver_path_hack(C, &ptr, prop, NULL);
short flags = CREATEDRIVER_WITH_DEFAULT_DVAR;
if (path) {
- success += ANIM_add_driver(op->reports, ptr.id.data, path, index, flags, DRIVER_TYPE_PYTHON);
+ success += ANIM_add_driver(
+ op->reports, ptr.owner_id, path, index, flags, DRIVER_TYPE_PYTHON);
MEM_freeN(path);
}
}
@@ -1095,28 +1096,29 @@ static void UNUSED_FUNCTION(ANIM_OT_driver_button_add_menu)(wmOperatorType *ot)
static int add_driver_button_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
- PointerRNA ptr = {{NULL}};
+ PointerRNA ptr = {NULL};
PropertyRNA *prop = NULL;
int index;
/* try to find driver using property retrieved from UI */
UI_context_active_but_prop_get(C, &ptr, &prop, &index);
- if (ptr.id.data && ptr.data && prop && RNA_property_animateable(&ptr, prop)) {
+ if (ptr.owner_id && ptr.data && prop && RNA_property_animateable(&ptr, prop)) {
/* 1) Create a new "empty" driver for this property */
char *path = BKE_animdata_driver_path_hack(C, &ptr, prop, NULL);
short flags = CREATEDRIVER_WITH_DEFAULT_DVAR;
short success = 0;
if (path) {
- success += ANIM_add_driver(op->reports, ptr.id.data, path, index, flags, DRIVER_TYPE_PYTHON);
+ success += ANIM_add_driver(
+ op->reports, ptr.owner_id, path, index, flags, DRIVER_TYPE_PYTHON);
MEM_freeN(path);
}
if (success) {
/* send updates */
UI_context_update_anim_flag(C);
- DEG_id_tag_update(ptr.id.data, ID_RECALC_COPY_ON_WRITE);
+ DEG_id_tag_update(ptr.owner_id, ID_RECALC_COPY_ON_WRITE);
DEG_relations_tag_update(CTX_data_main(C));
WM_event_add_notifier(C, NC_ANIMATION | ND_FCURVES_ORDER, NULL);
}
@@ -1149,7 +1151,7 @@ void ANIM_OT_driver_button_add(wmOperatorType *ot)
static int remove_driver_button_exec(bContext *C, wmOperator *op)
{
- PointerRNA ptr = {{NULL}};
+ PointerRNA ptr = {NULL};
PropertyRNA *prop = NULL;
short success = 0;
int index;
@@ -1162,11 +1164,11 @@ static int remove_driver_button_exec(bContext *C, wmOperator *op)
index = -1;
}
- if (ptr.id.data && ptr.data && prop) {
+ if (ptr.owner_id && ptr.data && prop) {
char *path = BKE_animdata_driver_path_hack(C, &ptr, prop, NULL);
if (path) {
- success = ANIM_remove_driver(op->reports, ptr.id.data, path, index, 0);
+ success = ANIM_remove_driver(op->reports, ptr.owner_id, path, index, 0);
MEM_freeN(path);
}
@@ -1205,14 +1207,14 @@ void ANIM_OT_driver_button_remove(wmOperatorType *ot)
static int edit_driver_button_exec(bContext *C, wmOperator *op)
{
- PointerRNA ptr = {{NULL}};
+ PointerRNA ptr = {NULL};
PropertyRNA *prop = NULL;
int index;
/* try to find driver using property retrieved from UI */
UI_context_active_but_prop_get(C, &ptr, &prop, &index);
- if (ptr.id.data && ptr.data && prop) {
+ if (ptr.owner_id && ptr.data && prop) {
UI_popover_panel_invoke(C, "GRAPH_PT_drivers_popover", true, op->reports);
}
@@ -1239,7 +1241,7 @@ void ANIM_OT_driver_button_edit(wmOperatorType *ot)
static int copy_driver_button_exec(bContext *C, wmOperator *op)
{
- PointerRNA ptr = {{NULL}};
+ PointerRNA ptr = {NULL};
PropertyRNA *prop = NULL;
short success = 0;
int index;
@@ -1247,12 +1249,12 @@ static int copy_driver_button_exec(bContext *C, wmOperator *op)
/* try to create driver using property retrieved from UI */
UI_context_active_but_prop_get(C, &ptr, &prop, &index);
- if (ptr.id.data && ptr.data && prop && RNA_property_animateable(&ptr, prop)) {
+ if (ptr.owner_id && ptr.data && prop && RNA_property_animateable(&ptr, prop)) {
char *path = BKE_animdata_driver_path_hack(C, &ptr, prop, NULL);
if (path) {
/* only copy the driver for the button that this was involved for */
- success = ANIM_copy_driver(op->reports, ptr.id.data, path, index, 0);
+ success = ANIM_copy_driver(op->reports, ptr.owner_id, path, index, 0);
UI_context_update_anim_flag(C);
@@ -1283,7 +1285,7 @@ void ANIM_OT_copy_driver_button(wmOperatorType *ot)
static int paste_driver_button_exec(bContext *C, wmOperator *op)
{
- PointerRNA ptr = {{NULL}};
+ PointerRNA ptr = {NULL};
PropertyRNA *prop = NULL;
short success = 0;
int index;
@@ -1291,18 +1293,18 @@ static int paste_driver_button_exec(bContext *C, wmOperator *op)
/* try to create driver using property retrieved from UI */
UI_context_active_but_prop_get(C, &ptr, &prop, &index);
- if (ptr.id.data && ptr.data && prop && RNA_property_animateable(&ptr, prop)) {
+ if (ptr.owner_id && ptr.data && prop && RNA_property_animateable(&ptr, prop)) {
char *path = BKE_animdata_driver_path_hack(C, &ptr, prop, NULL);
if (path) {
/* only copy the driver for the button that this was involved for */
- success = ANIM_paste_driver(op->reports, ptr.id.data, path, index, 0);
+ success = ANIM_paste_driver(op->reports, ptr.owner_id, path, index, 0);
UI_context_update_anim_flag(C);
DEG_relations_tag_update(CTX_data_main(C));
- DEG_id_tag_update(ptr.id.data, ID_RECALC_ANIMATION);
+ DEG_id_tag_update(ptr.owner_id, ID_RECALC_ANIMATION);
WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME_PROP, NULL); // XXX
diff --git a/source/blender/editors/animation/fmodifier_ui.c b/source/blender/editors/animation/fmodifier_ui.c
index b42e8102c5b..705351522f8 100644
--- a/source/blender/editors/animation/fmodifier_ui.c
+++ b/source/blender/editors/animation/fmodifier_ui.c
@@ -197,7 +197,7 @@ static void draw_modifier__generator(uiLayout *layout,
&data->poly_order,
1,
100,
- 0,
+ 1,
0,
TIP_("'Order' of the Polynomial (for a polynomial with n terms, 'order' is n-1)"));
UI_but_func_set(but, validate_fmodifier_cb, fcm, fcurve_owner_id);
@@ -335,7 +335,7 @@ static void draw_modifier__generator(uiLayout *layout,
&data->poly_order,
1,
100,
- 0,
+ 1,
0,
TIP_("'Order' of the Polynomial (for a polynomial with n terms, 'order' is n-1)"));
UI_but_func_set(but, validate_fmodifier_cb, fcm, fcurve_owner_id);
diff --git a/source/blender/editors/animation/keyframes_draw.c b/source/blender/editors/animation/keyframes_draw.c
index 889d27480df..ca7e0eae136 100644
--- a/source/blender/editors/animation/keyframes_draw.c
+++ b/source/blender/editors/animation/keyframes_draw.c
@@ -814,8 +814,10 @@ static void draw_keylist(View2D *v2d,
uint outline_color_id = GPU_vertformat_attr_add(
format, "outlineColor", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
uint flags_id = GPU_vertformat_attr_add(format, "flags", GPU_COMP_U32, 1, GPU_FETCH_INT);
- immBindBuiltinProgram(GPU_SHADER_KEYFRAME_DIAMOND);
+
GPU_program_point_size(true);
+ immBindBuiltinProgram(GPU_SHADER_KEYFRAME_DIAMOND);
+ immUniform1f("outline_scale", 1.0f);
immUniform2f(
"ViewportSize", BLI_rcti_size_x(&v2d->mask) + 1, BLI_rcti_size_y(&v2d->mask) + 1);
immBegin(GPU_PRIM_POINTS, key_len);
diff --git a/source/blender/editors/animation/keyframing.c b/source/blender/editors/animation/keyframing.c
index dcc596e67e1..0f8b8742659 100644
--- a/source/blender/editors/animation/keyframing.c
+++ b/source/blender/editors/animation/keyframing.c
@@ -222,7 +222,7 @@ FCurve *verify_fcurve(Main *bmain,
/* sync bone group colors if applicable */
if (ptr && (ptr->type == &RNA_PoseBone)) {
- Object *ob = (Object *)ptr->id.data;
+ Object *ob = (Object *)ptr->owner_id;
bPoseChannel *pchan = (bPoseChannel *)ptr->data;
bPose *pose = ob->pose;
bActionGroup *grp;
@@ -286,7 +286,7 @@ void update_autoflags_fcurve(FCurve *fcu, bContext *C, ReportList *reports, Poin
PropertyRNA *prop;
int old_flag = fcu->flag;
- if ((ptr->id.data == NULL) && (ptr->data == NULL)) {
+ if ((ptr->owner_id == NULL) && (ptr->data == NULL)) {
BKE_report(reports, RPT_ERROR, "No RNA pointer available to retrieve values for this fcurve");
return;
}
@@ -294,7 +294,7 @@ void update_autoflags_fcurve(FCurve *fcu, bContext *C, ReportList *reports, Poin
/* try to get property we should be affecting */
if (RNA_path_resolve_property(ptr, fcu->rna_path, &tmp_ptr, &prop) == false) {
/* property not found... */
- const char *idname = (ptr->id.data) ? ((ID *)ptr->id.data)->name : TIP_("<No ID pointer>");
+ const char *idname = (ptr->owner_id) ? ptr->owner_id->name : TIP_("<No ID pointer>");
BKE_reportf(reports,
RPT_ERROR,
@@ -1203,7 +1203,7 @@ bool insert_keyframe_direct(ReportList *reports,
}
/* if no property given yet, try to validate from F-Curve info */
- if ((ptr.id.data == NULL) && (ptr.data == NULL)) {
+ if ((ptr.owner_id == NULL) && (ptr.data == NULL)) {
BKE_report(
reports, RPT_ERROR, "No RNA pointer available to retrieve values for keyframing from");
return false;
@@ -1214,7 +1214,7 @@ bool insert_keyframe_direct(ReportList *reports,
/* try to get property we should be affecting */
if (RNA_path_resolve_property(&ptr, fcu->rna_path, &tmp_ptr, &prop) == false) {
/* property not found... */
- const char *idname = (ptr.id.data) ? ((ID *)ptr.id.data)->name : TIP_("<No ID pointer>");
+ const char *idname = (ptr.owner_id) ? ptr.owner_id->name : TIP_("<No ID pointer>");
BKE_reportf(reports,
RPT_ERROR,
@@ -1544,9 +1544,9 @@ static bool delete_keyframe_fcurve(AnimData *adt, FCurve *fcu, float cfra)
static void deg_tag_after_keyframe_delete(Main *bmain, ID *id, AnimData *adt)
{
if (adt->action == NULL) {
- /* In the case last f-curve wes removed need to inform dependency graph
+ /* In the case last f-curve was removed need to inform dependency graph
* about relations update, since it needs to get rid of animation operation
- * for this datablock. */
+ * for this data-block. */
DEG_id_tag_update_ex(bmain, id, ID_RECALC_ANIMATION_NO_FLUSH);
DEG_relations_tag_update(bmain);
}
@@ -2350,7 +2350,7 @@ static int insert_key_button_exec(bContext *C, wmOperator *op)
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
ToolSettings *ts = scene->toolsettings;
- PointerRNA ptr = {{NULL}};
+ PointerRNA ptr = {NULL};
PropertyRNA *prop = NULL;
char *path;
uiBut *but;
@@ -2369,7 +2369,7 @@ static int insert_key_button_exec(bContext *C, wmOperator *op)
return (OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH);
}
- if ((ptr.id.data && ptr.data && prop) && RNA_property_animateable(&ptr, prop)) {
+ if ((ptr.owner_id && ptr.data && prop) && RNA_property_animateable(&ptr, prop)) {
if (ptr.type == &RNA_NlaStrip) {
/* Handle special properties for NLA Strips, whose F-Curves are stored on the
* strips themselves. These are stored separately or else the properties will
@@ -2435,7 +2435,7 @@ static int insert_key_button_exec(bContext *C, wmOperator *op)
success = insert_keyframe(bmain,
op->reports,
- ptr.id.data,
+ ptr.owner_id,
NULL,
group,
path,
@@ -2473,7 +2473,7 @@ static int insert_key_button_exec(bContext *C, wmOperator *op)
}
if (success) {
- ID *id = ptr.id.data;
+ ID *id = ptr.owner_id;
AnimData *adt = BKE_animdata_from_id(id);
if (adt->action != NULL) {
DEG_id_tag_update(&adt->action->id, ID_RECALC_ANIMATION_NO_FLUSH);
@@ -2513,7 +2513,7 @@ void ANIM_OT_keyframe_insert_button(wmOperatorType *ot)
static int delete_key_button_exec(bContext *C, wmOperator *op)
{
Scene *scene = CTX_data_scene(C);
- PointerRNA ptr = {{NULL}};
+ PointerRNA ptr = {NULL};
PropertyRNA *prop = NULL;
Main *bmain = CTX_data_main(C);
char *path;
@@ -2528,13 +2528,13 @@ static int delete_key_button_exec(bContext *C, wmOperator *op)
return (OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH);
}
- if (ptr.id.data && ptr.data && prop) {
+ if (ptr.owner_id && ptr.data && prop) {
if (BKE_nlastrip_has_curves_for_property(&ptr, prop)) {
/* Handle special properties for NLA Strips, whose F-Curves are stored on the
* strips themselves. These are stored separately or else the properties will
* not have any effect.
*/
- ID *id = ptr.id.data;
+ ID *id = ptr.owner_id;
NlaStrip *strip = (NlaStrip *)ptr.data;
FCurve *fcu = list_find_fcurve(&strip->fcurves, RNA_property_identifier(prop), 0);
@@ -2577,7 +2577,7 @@ static int delete_key_button_exec(bContext *C, wmOperator *op)
}
success = delete_keyframe(
- bmain, op->reports, ptr.id.data, NULL, NULL, path, index, cfra, 0);
+ bmain, op->reports, ptr.owner_id, NULL, NULL, path, index, cfra, 0);
MEM_freeN(path);
}
else if (G.debug & G_DEBUG) {
@@ -2622,7 +2622,7 @@ void ANIM_OT_keyframe_delete_button(wmOperatorType *ot)
static int clear_key_button_exec(bContext *C, wmOperator *op)
{
- PointerRNA ptr = {{NULL}};
+ PointerRNA ptr = {NULL};
PropertyRNA *prop = NULL;
Main *bmain = CTX_data_main(C);
char *path;
@@ -2636,7 +2636,7 @@ static int clear_key_button_exec(bContext *C, wmOperator *op)
return (OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH);
}
- if (ptr.id.data && ptr.data && prop) {
+ if (ptr.owner_id && ptr.data && prop) {
path = RNA_path_from_ID_to_property(&ptr, prop);
if (path) {
@@ -2645,7 +2645,7 @@ static int clear_key_button_exec(bContext *C, wmOperator *op)
index = -1;
}
- success += clear_keyframe(bmain, op->reports, ptr.id.data, NULL, NULL, path, index, 0);
+ success += clear_keyframe(bmain, op->reports, ptr.owner_id, NULL, NULL, path, index, 0);
MEM_freeN(path);
}
else if (G.debug & G_DEBUG) {
diff --git a/source/blender/editors/animation/keyingsets.c b/source/blender/editors/animation/keyingsets.c
index ccd0fc54611..258c0e4f1f6 100644
--- a/source/blender/editors/animation/keyingsets.c
+++ b/source/blender/editors/animation/keyingsets.c
@@ -287,7 +287,7 @@ static int add_keyingset_button_exec(bContext *C, wmOperator *op)
Scene *scene = CTX_data_scene(C);
KeyingSet *ks = NULL;
PropertyRNA *prop = NULL;
- PointerRNA ptr = {{NULL}};
+ PointerRNA ptr = {NULL};
char *path = NULL;
short success = 0;
int index = 0, pflag = 0;
@@ -332,7 +332,7 @@ static int add_keyingset_button_exec(bContext *C, wmOperator *op)
}
/* check if property is able to be added */
- if (ptr.id.data && ptr.data && prop && RNA_property_animateable(&ptr, prop)) {
+ if (ptr.owner_id && ptr.data && prop && RNA_property_animateable(&ptr, prop)) {
path = RNA_path_from_ID_to_property(&ptr, prop);
if (path) {
@@ -348,7 +348,7 @@ static int add_keyingset_button_exec(bContext *C, wmOperator *op)
}
/* add path to this setting */
- BKE_keyingset_add_path(ks, ptr.id.data, NULL, path, index, pflag, KSP_GROUP_KSNAME);
+ BKE_keyingset_add_path(ks, ptr.owner_id, NULL, path, index, pflag, KSP_GROUP_KSNAME);
ks->active_path = BLI_listbase_count(&ks->paths);
success = 1;
@@ -393,7 +393,7 @@ static int remove_keyingset_button_exec(bContext *C, wmOperator *op)
Scene *scene = CTX_data_scene(C);
KeyingSet *ks = NULL;
PropertyRNA *prop = NULL;
- PointerRNA ptr = {{NULL}};
+ PointerRNA ptr = {NULL};
char *path = NULL;
short success = 0;
int index = 0;
@@ -420,14 +420,14 @@ static int remove_keyingset_button_exec(bContext *C, wmOperator *op)
ks = BLI_findlink(&scene->keyingsets, scene->active_keyingset - 1);
}
- if (ptr.id.data && ptr.data && prop) {
+ if (ptr.owner_id && ptr.data && prop) {
path = RNA_path_from_ID_to_property(&ptr, prop);
if (path) {
KS_Path *ksp;
/* try to find a path matching this description */
- ksp = BKE_keyingset_find_path(ks, ptr.id.data, ks->name, path, index, KSP_GROUP_KSNAME);
+ ksp = BKE_keyingset_find_path(ks, ptr.owner_id, ks->name, path, index, KSP_GROUP_KSNAME);
if (ksp) {
BKE_keyingset_free_path(ks, ksp);
@@ -712,9 +712,9 @@ int ANIM_scene_get_keyingset_index(Scene *scene, KeyingSet *ks)
}
}
- /* still here, so try builtins list too
- * - builtins are from (<= -1)
- * - none/invalid is (= 0)
+ /* Still here, so try built-ins list too:
+ * - Built-ins are from (<= -1).
+ * - None/Invalid is (= 0).
*/
index = BLI_findindex(&builtin_keyingsets, ks);
if (index != -1) {
diff --git a/source/blender/editors/armature/armature_add.c b/source/blender/editors/armature/armature_add.c
index d2fa77f90be..1073034383d 100644
--- a/source/blender/editors/armature/armature_add.c
+++ b/source/blender/editors/armature/armature_add.c
@@ -769,7 +769,7 @@ static int armature_symmetrize_exec(bContext *C, wmOperator *op)
EditBone *ebone = ebone_iter->temp.ebone;
- /* copy flags incase bone is pre-existing data */
+ /* Copy flags in case bone is pre-existing data. */
ebone->flag = (ebone->flag & ~flag_copy) | (ebone_iter->flag & flag_copy);
if (ebone_iter->parent == NULL) {
diff --git a/source/blender/editors/armature/armature_edit.c b/source/blender/editors/armature/armature_edit.c
index 4e6661b1d15..c4c10549da3 100644
--- a/source/blender/editors/armature/armature_edit.c
+++ b/source/blender/editors/armature/armature_edit.c
@@ -62,22 +62,10 @@
/* ************************** Object Tools Exports ******************************* */
/* NOTE: these functions are exported to the Object module to be called from the tools there */
-void ED_armature_transform_apply(Main *bmain, Object *ob, float mat[4][4], const bool do_props)
-{
- bArmature *arm = ob->data;
-
- /* Put the armature into editmode */
- ED_armature_to_edit(arm);
-
- /* Transform the bones */
- ED_armature_transform_bones(arm, mat, do_props);
-
- /* Turn the list into an armature */
- ED_armature_from_edit(bmain, arm);
- ED_armature_edit_free(arm);
-}
-
-void ED_armature_transform_bones(struct bArmature *arm, float mat[4][4], const bool do_props)
+/**
+ * See #BKE_armature_transform for object-mode transform.
+ */
+void ED_armature_edit_transform(bArmature *arm, const float mat[4][4], const bool do_props)
{
EditBone *ebone;
float scale = mat4_to_scale(mat); /* store the scale of the matrix here to use on envelopes */
@@ -114,21 +102,13 @@ void ED_armature_transform_bones(struct bArmature *arm, float mat[4][4], const b
}
}
-void ED_armature_transform(Main *bmain, bArmature *arm, float mat[4][4], const bool do_props)
+void ED_armature_transform(bArmature *arm, const float mat[4][4], const bool do_props)
{
if (arm->edbo) {
- ED_armature_transform_bones(arm, mat, do_props);
+ ED_armature_edit_transform(arm, mat, do_props);
}
else {
- /* Put the armature into editmode */
- ED_armature_to_edit(arm);
-
- /* Transform the bones */
- ED_armature_transform_bones(arm, mat, do_props);
-
- /* Go back to object mode*/
- ED_armature_from_edit(bmain, arm);
- ED_armature_edit_free(arm);
+ BKE_armature_transform(arm, mat, do_props);
}
}
diff --git a/source/blender/editors/armature/armature_intern.h b/source/blender/editors/armature/armature_intern.h
index 569eb7e2e04..fa562ab0f44 100644
--- a/source/blender/editors/armature/armature_intern.h
+++ b/source/blender/editors/armature/armature_intern.h
@@ -30,7 +30,6 @@ struct wmOperatorType;
struct Base;
struct Object;
struct Scene;
-struct bAction;
struct bContext;
struct bPoseChannel;
diff --git a/source/blender/editors/armature/armature_select.c b/source/blender/editors/armature/armature_select.c
index 23ddf77e63d..eff621d7b71 100644
--- a/source/blender/editors/armature/armature_select.c
+++ b/source/blender/editors/armature/armature_select.c
@@ -46,6 +46,7 @@
#include "ED_armature.h"
#include "ED_object.h"
+#include "ED_outliner.h"
#include "ED_screen.h"
#include "ED_select_utils.h"
#include "ED_view3d.h"
@@ -356,6 +357,8 @@ static int armature_select_linked_invoke(bContext *C, wmOperator *op, const wmEv
}
}
+ ED_outliner_select_sync_from_edit_bone_tag(C);
+
ED_armature_edit_sync_selection(arm->edbo);
WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, base->object);
@@ -1027,6 +1030,8 @@ static int armature_de_select_all_exec(bContext *C, wmOperator *op)
}
CTX_DATA_END;
+ ED_outliner_select_sync_from_edit_bone_tag(C);
+
WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, NULL);
return OPERATOR_FINISHED;
@@ -1148,6 +1153,8 @@ static int armature_de_select_more_exec(bContext *C, wmOperator *UNUSED(op))
WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob);
}
MEM_freeN(objects);
+
+ ED_outliner_select_sync_from_edit_bone_tag(C);
return OPERATOR_FINISHED;
}
@@ -1178,6 +1185,8 @@ static int armature_de_select_less_exec(bContext *C, wmOperator *UNUSED(op))
WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob);
}
MEM_freeN(objects);
+
+ ED_outliner_select_sync_from_edit_bone_tag(C);
return OPERATOR_FINISHED;
}
@@ -1569,6 +1578,8 @@ static int armature_select_similar_exec(bContext *C, wmOperator *op)
#undef STRUCT_SIZE_AND_OFFSET
+ ED_outliner_select_sync_from_edit_bone_tag(C);
+
return OPERATOR_FINISHED;
}
@@ -1663,6 +1674,8 @@ static int armature_select_hierarchy_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
+ ED_outliner_select_sync_from_edit_bone_tag(C);
+
ED_armature_edit_sync_selection(arm->edbo);
WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob);
@@ -1748,6 +1761,8 @@ static int armature_select_mirror_exec(bContext *C, wmOperator *op)
arm->act_edbone = ebone_mirror_act;
}
+ ED_outliner_select_sync_from_edit_bone_tag(C);
+
ED_armature_edit_sync_selection(arm->edbo);
WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob);
@@ -1876,6 +1891,7 @@ static int armature_shortest_path_pick_invoke(bContext *C, wmOperator *op, const
if (changed) {
arm->act_edbone = ebone_dst;
+ ED_outliner_select_sync_from_edit_bone_tag(C);
ED_armature_edit_sync_selection(arm->edbo);
WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit);
diff --git a/source/blender/editors/armature/armature_utils.c b/source/blender/editors/armature/armature_utils.c
index d8777b7e0b7..cd299906b4c 100644
--- a/source/blender/editors/armature/armature_utils.c
+++ b/source/blender/editors/armature/armature_utils.c
@@ -381,23 +381,37 @@ void armature_tag_unselect(bArmature *arm)
void ED_armature_ebone_transform_mirror_update(bArmature *arm, EditBone *ebo, bool check_select)
{
+ /* TODO When this function is called by property updates,
+ * cancelling the value change will not restore mirrored bone correctly. */
+
+ /* Currently check_select==true when this function is called from a transform operator,
+ * eg. from 3d viewport. */
+
/* no layer check, correct mirror is more important */
if (!check_select || ebo->flag & (BONE_TIPSEL | BONE_ROOTSEL)) {
EditBone *eboflip = ED_armature_ebone_get_mirrored(arm->edbo, ebo);
if (eboflip) {
- /* we assume X-axis flipping for now */
- if (check_select && ebo->flag & BONE_TIPSEL) {
- EditBone *children;
+ /* We assume X-axis flipping for now. */
+
+ /* Always mirror roll, since it can be changed by moving either head or tail. */
+ eboflip->roll = -ebo->roll;
+
+ if (!check_select || ebo->flag & BONE_TIPSEL) {
+ /* Mirror tail properties. */
eboflip->tail[0] = -ebo->tail[0];
eboflip->tail[1] = ebo->tail[1];
eboflip->tail[2] = ebo->tail[2];
eboflip->rad_tail = ebo->rad_tail;
- eboflip->roll = -ebo->roll;
eboflip->curve_out_x = -ebo->curve_out_x;
+ eboflip->curve_out_y = ebo->curve_out_y;
+ eboflip->scale_out_x = ebo->scale_out_x;
+ eboflip->scale_out_y = ebo->scale_out_y;
+ eboflip->ease2 = ebo->ease2;
eboflip->roll2 = -ebo->roll2;
- /* Also move connected children, in case children's name aren't mirrored properly */
+ /* Also move connected children, in case children's name aren't mirrored properly. */
+ EditBone *children;
for (children = arm->edbo->first; children; children = children->next) {
if (children->parent == eboflip && children->flag & BONE_CONNECTED) {
copy_v3_v3(children->head, eboflip->tail);
@@ -405,32 +419,39 @@ void ED_armature_ebone_transform_mirror_update(bArmature *arm, EditBone *ebo, bo
}
}
}
+
if (!check_select || ebo->flag & BONE_ROOTSEL) {
+ /* Mirror head properties. */
eboflip->head[0] = -ebo->head[0];
eboflip->head[1] = ebo->head[1];
eboflip->head[2] = ebo->head[2];
eboflip->rad_head = ebo->rad_head;
- eboflip->roll = -ebo->roll;
+
eboflip->curve_in_x = -ebo->curve_in_x;
+ eboflip->curve_in_y = ebo->curve_in_y;
+ eboflip->scale_in_x = ebo->scale_in_x;
+ eboflip->scale_in_y = ebo->scale_in_y;
+ eboflip->ease1 = ebo->ease1;
eboflip->roll1 = -ebo->roll1;
- /* Also move connected parent, in case parent's name isn't mirrored properly */
+ /* Also move connected parent, in case parent's name isn't mirrored properly. */
if (eboflip->parent && eboflip->flag & BONE_CONNECTED) {
EditBone *parent = eboflip->parent;
copy_v3_v3(parent->tail, eboflip->head);
parent->rad_tail = ebo->rad_head;
}
}
+
if (!check_select || ebo->flag & BONE_SELECTED) {
+ /* Mirror bone body properties (both head and tail are selected). */
+ /* TODO: These values can also be changed from pose mode,
+ * so only mirroring them in edit mode is not ideal. */
eboflip->dist = ebo->dist;
- eboflip->roll = -ebo->roll;
+ eboflip->weight = ebo->weight;
+
+ eboflip->segments = ebo->segments;
eboflip->xwidth = ebo->xwidth;
eboflip->zwidth = ebo->zwidth;
-
- eboflip->curve_in_x = -ebo->curve_in_x;
- eboflip->curve_out_x = -ebo->curve_out_x;
- eboflip->roll1 = -ebo->roll1;
- eboflip->roll2 = -ebo->roll2;
}
}
}
diff --git a/source/blender/editors/armature/pose_select.c b/source/blender/editors/armature/pose_select.c
index 8434fee6e78..a59067e60c1 100644
--- a/source/blender/editors/armature/pose_select.c
+++ b/source/blender/editors/armature/pose_select.c
@@ -54,6 +54,7 @@
#include "ED_keyframing.h"
#include "ED_mesh.h"
#include "ED_object.h"
+#include "ED_outliner.h"
#include "ED_screen.h"
#include "ED_select_utils.h"
#include "ED_view3d.h"
@@ -449,6 +450,8 @@ static int pose_select_connected_invoke(bContext *C, wmOperator *op, const wmEve
selectconnected_posebonechildren(base->object, curBone, extend);
}
+ ED_outliner_select_sync_from_pose_bone_tag(C);
+
ED_pose_bone_select_tag_update(base->object);
return OPERATOR_FINISHED;
@@ -514,6 +517,8 @@ static int pose_de_select_all_exec(bContext *C, wmOperator *op)
}
CTX_DATA_END;
+ ED_outliner_select_sync_from_pose_bone_tag(C);
+
WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, NULL);
return OPERATOR_FINISHED;
@@ -560,6 +565,8 @@ static int pose_select_parent_exec(bContext *C, wmOperator *UNUSED(op))
return OPERATOR_CANCELLED;
}
+ ED_outliner_select_sync_from_pose_bone_tag(C);
+
ED_pose_bone_select_tag_update(ob);
return OPERATOR_FINISHED;
}
@@ -624,6 +631,8 @@ static int pose_select_constraint_target_exec(bContext *C, wmOperator *UNUSED(op
return OPERATOR_CANCELLED;
}
+ ED_outliner_select_sync_from_pose_bone_tag(C);
+
return OPERATOR_FINISHED;
}
@@ -712,6 +721,8 @@ static int pose_select_hierarchy_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
+ ED_outliner_select_sync_from_pose_bone_tag(C);
+
ED_pose_bone_select_tag_update(ob);
return OPERATOR_FINISHED;
@@ -796,7 +807,7 @@ static bool pose_select_same_group(bContext *C, bool extend)
group_flags = NULL;
ob_index = -1;
ob_prev = NULL;
- CTX_DATA_BEGIN_WITH_ID (C, bPoseChannel *, pchan, visible_pose_bones, Object, *ob) {
+ CTX_DATA_BEGIN_WITH_ID (C, bPoseChannel *, pchan, visible_pose_bones, Object *, ob) {
if (ob != ob_prev) {
ob_index++;
group_flags = group_flags_array + (ob_index * groups_len);
@@ -1061,6 +1072,8 @@ static int pose_select_grouped_exec(bContext *C, wmOperator *op)
/* report done status */
if (changed) {
+ ED_outliner_select_sync_from_pose_bone_tag(C);
+
return OPERATOR_FINISHED;
}
else {
@@ -1172,6 +1185,8 @@ static int pose_select_mirror_exec(bContext *C, wmOperator *op)
}
MEM_freeN(objects);
+ ED_outliner_select_sync_from_pose_bone_tag(C);
+
return OPERATOR_FINISHED;
}
diff --git a/source/blender/editors/armature/pose_slide.c b/source/blender/editors/armature/pose_slide.c
index c8e79b879a4..6274eb549da 100644
--- a/source/blender/editors/armature/pose_slide.c
+++ b/source/blender/editors/armature/pose_slide.c
@@ -442,7 +442,7 @@ static void pose_slide_apply_props(tPoseSlideOp *pso,
tPChanFCurveLink *pfl,
const char prop_prefix[])
{
- PointerRNA ptr = {{NULL}};
+ PointerRNA ptr = {NULL};
LinkData *ld;
int len = strlen(pfl->pchan_path);
diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c
index c93531bb6cc..d7650db546d 100644
--- a/source/blender/editors/curve/editcurve.c
+++ b/source/blender/editors/curve/editcurve.c
@@ -51,6 +51,7 @@
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_build.h"
+#include "DEG_depsgraph_query.h"
#include "WM_api.h"
#include "WM_types.h"
@@ -5679,6 +5680,7 @@ static int add_vertex_invoke(bContext *C, wmOperator *op, const wmEvent *event)
},
mval,
NULL,
+ NULL,
location,
NULL);
@@ -7104,14 +7106,15 @@ static int match_texture_space_exec(bContext *C, wmOperator *UNUSED(op))
(void)depsgraph;
Object *object = CTX_data_active_object(C);
+ Object *object_eval = DEG_get_evaluated_object(depsgraph, object);
Curve *curve = (Curve *)object->data;
float min[3], max[3], size[3], loc[3];
int a;
- BLI_assert(object->runtime.curve_cache != NULL);
+ BLI_assert(object_eval->runtime.curve_cache != NULL);
INIT_MINMAX(min, max);
- BKE_displist_minmax(&object->runtime.curve_cache->disp, min, max);
+ BKE_displist_minmax(&object_eval->runtime.curve_cache->disp, min, max);
mid_v3_v3v3(loc, min, max);
@@ -7138,6 +7141,7 @@ static int match_texture_space_exec(bContext *C, wmOperator *UNUSED(op))
curve->texflag &= ~CU_AUTOSPACE;
WM_event_add_notifier(C, NC_GEOM | ND_DATA, curve);
+ DEG_id_tag_update(&curve->id, ID_RECALC_GEOMETRY);
return OPERATOR_FINISHED;
}
diff --git a/source/blender/editors/curve/editcurve_paint.c b/source/blender/editors/curve/editcurve_paint.c
index 5e0053782d4..c7c19aa2d02 100644
--- a/source/blender/editors/curve/editcurve_paint.c
+++ b/source/blender/editors/curve/editcurve_paint.c
@@ -120,7 +120,7 @@ struct CurveDrawData {
struct {
float mouse[2];
- /* used incase we can't calculate the depth */
+ /* Used in case we can't calculate the depth. */
float location_world[3];
float location_world_valid[3];
@@ -1053,7 +1053,7 @@ static int curve_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event)
const bool is_modal = RNA_boolean_get(op->ptr, "wait_for_input");
- /* fallback (incase we can't find the depth on first test) */
+ /* Fallback (in case we can't find the depth on first test). */
{
const float mval_fl[2] = {UNPACK2(event->mval)};
float center[3];
diff --git a/source/blender/editors/curve/editcurve_select.c b/source/blender/editors/curve/editcurve_select.c
index d3f0ebfda3c..f67ccf1e4bd 100644
--- a/source/blender/editors/curve/editcurve_select.c
+++ b/source/blender/editors/curve/editcurve_select.c
@@ -1417,7 +1417,7 @@ void CURVE_OT_select_nth(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Checker Deselect";
- ot->description = "Deselect every other vertex";
+ ot->description = "Deselect every Nth point starting from the active one";
ot->idname = "CURVE_OT_select_nth";
/* api callbacks */
diff --git a/source/blender/editors/curve/editfont.c b/source/blender/editors/curve/editfont.c
index f0c1abff201..781eb2634fb 100644
--- a/source/blender/editors/curve/editfont.c
+++ b/source/blender/editors/curve/editfont.c
@@ -71,7 +71,11 @@
static int kill_selection(Object *obedit, int ins);
-/************************* utilities ******************************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Internal Utilities
+ * \{ */
static wchar_t findaccent(wchar_t char1, unsigned int code)
{
@@ -440,8 +444,48 @@ static void text_update_edited(bContext *C, Object *obedit, int mode)
WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
}
+static int kill_selection(Object *obedit, int ins) /* 1 == new character */
+{
+ Curve *cu = obedit->data;
+ EditFont *ef = cu->editfont;
+ int selend, selstart, direction;
+ int offset = 0;
+ int getfrom;
+
+ direction = BKE_vfont_select_get(obedit, &selstart, &selend);
+ if (direction) {
+ int size;
+ if (ins) {
+ offset = 1;
+ }
+ if (ef->pos >= selstart) {
+ ef->pos = selstart + offset;
+ }
+ if ((direction == -1) && ins) {
+ selstart++;
+ selend++;
+ }
+ getfrom = selend + offset;
+ if (ins == 0) {
+ getfrom++;
+ }
+ size = (ef->len * sizeof(wchar_t)) - (selstart * sizeof(wchar_t)) + (offset * sizeof(wchar_t));
+ memmove(ef->textbuf + selstart, ef->textbuf + getfrom, size);
+ memmove(ef->textbufinfo + selstart,
+ ef->textbufinfo + getfrom,
+ ((ef->len - selstart) + offset) * sizeof(CharInfo));
+ ef->len -= ((selend - selstart) + 1);
+ ef->selstart = ef->selend = 0;
+ }
+
+ return (direction);
+}
+
+/** \} */
+
/* -------------------------------------------------------------------- */
-/* Generic Paste Functions */
+/** \name Generic Paste Functions
+ * \{ */
/* text_update_edited(C, scene, obedit, 1, FO_EDIT); */
static bool font_paste_wchar(Object *obedit,
@@ -506,8 +550,11 @@ static bool font_paste_utf8(bContext *C, const char *str, const size_t str_len)
return retval;
}
+/** \} */
+
/* -------------------------------------------------------------------- */
-/* Paste From File*/
+/** \name Paste From File Operator
+ * \{ */
static int paste_from_file(bContext *C, ReportList *reports, const char *filename)
{
@@ -585,7 +632,11 @@ void FONT_OT_text_paste_from_file(wmOperatorType *ot)
FILE_SORT_ALPHA);
}
-/******************* text to object operator ********************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Text To Object
+ * \{ */
static void txt_add_object(bContext *C, TextLine *firstline, int totline, const float offset[3])
{
@@ -703,46 +754,11 @@ void ED_text_to_object(bContext *C, Text *text, const bool split_lines)
}
}
-/********************** utilities ***************************/
-
-static int kill_selection(Object *obedit, int ins) /* 1 == new character */
-{
- Curve *cu = obedit->data;
- EditFont *ef = cu->editfont;
- int selend, selstart, direction;
- int offset = 0;
- int getfrom;
-
- direction = BKE_vfont_select_get(obedit, &selstart, &selend);
- if (direction) {
- int size;
- if (ins) {
- offset = 1;
- }
- if (ef->pos >= selstart) {
- ef->pos = selstart + offset;
- }
- if ((direction == -1) && ins) {
- selstart++;
- selend++;
- }
- getfrom = selend + offset;
- if (ins == 0) {
- getfrom++;
- }
- size = (ef->len * sizeof(wchar_t)) - (selstart * sizeof(wchar_t)) + (offset * sizeof(wchar_t));
- memmove(ef->textbuf + selstart, ef->textbuf + getfrom, size);
- memmove(ef->textbufinfo + selstart,
- ef->textbufinfo + getfrom,
- ((ef->len - selstart) + offset) * sizeof(CharInfo));
- ef->len -= ((selend - selstart) + 1);
- ef->selstart = ef->selend = 0;
- }
-
- return (direction);
-}
+/** \} */
-/******************* set style operator ********************/
+/* -------------------------------------------------------------------- */
+/** \name Set Style Operator
+ * \{ */
static const EnumPropertyItem style_items[] = {
{CU_CHINFO_BOLD, "BOLD", 0, "Bold", ""},
@@ -806,7 +822,11 @@ void FONT_OT_style_set(wmOperatorType *ot)
RNA_def_boolean(ot->srna, "clear", 0, "Clear", "Clear style rather than setting it");
}
-/******************* toggle style operator ********************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Toggle Style Operator
+ * \{ */
static int toggle_style_exec(bContext *C, wmOperator *op)
{
@@ -845,8 +865,11 @@ void FONT_OT_style_toggle(wmOperatorType *ot)
ot->srna, "style", style_items, CU_CHINFO_BOLD, "Style", "Style to set selection to");
}
+/** \} */
+
/* -------------------------------------------------------------------- */
-/* Select All */
+/** \name Select All Operator
+ * \{ */
static int font_select_all_exec(bContext *C, wmOperator *UNUSED(op))
{
@@ -883,7 +906,11 @@ void FONT_OT_select_all(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
-/******************* copy text operator ********************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Copy Text Operator
+ * \{ */
static void copy_selection(Object *obedit)
{
@@ -932,7 +959,11 @@ void FONT_OT_text_copy(wmOperatorType *ot)
ot->poll = ED_operator_editfont;
}
-/******************* cut text operator ********************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Cut Text Operator
+ * \{ */
static int cut_text_exec(bContext *C, wmOperator *UNUSED(op))
{
@@ -966,7 +997,11 @@ void FONT_OT_text_cut(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
-/******************* paste text operator ********************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Paste Text Operator
+ * \{ */
static bool paste_selection(Object *obedit, ReportList *reports)
{
@@ -1066,7 +1101,11 @@ void FONT_OT_text_paste(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
-/************************ move operator ************************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Move Operator
+ * \{ */
static const EnumPropertyItem move_type_items[] = {
{LINE_BEGIN, "LINE_BEGIN", 0, "Line Begin", ""},
@@ -1232,7 +1271,11 @@ void FONT_OT_move(wmOperatorType *ot)
RNA_def_enum(ot->srna, "type", move_type_items, LINE_BEGIN, "Type", "Where to move cursor to");
}
-/******************* move select operator ********************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Move Select Operator
+ * \{ */
static int move_select_exec(bContext *C, wmOperator *op)
{
@@ -1264,7 +1307,11 @@ void FONT_OT_move_select(wmOperatorType *ot)
"Where to move cursor to, to make a selection");
}
-/************************* change spacing **********************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Change Spacing
+ * \{ */
static int change_spacing_exec(bContext *C, wmOperator *op)
{
@@ -1314,7 +1361,11 @@ void FONT_OT_change_spacing(wmOperatorType *ot)
20);
}
-/************************* change character **********************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Change Character
+ * \{ */
static int change_character_exec(bContext *C, wmOperator *op)
{
@@ -1368,7 +1419,11 @@ void FONT_OT_change_character(wmOperatorType *ot)
255);
}
-/******************* line break operator ********************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Line Break Operator
+ * \{ */
static int line_break_exec(bContext *C, wmOperator *UNUSED(op))
{
@@ -1400,7 +1455,11 @@ void FONT_OT_line_break(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
-/******************* delete operator **********************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Delete Operator
+ * \{ */
static const EnumPropertyItem delete_type_items[] = {
{DEL_NEXT_CHAR, "NEXT_CHARACTER", 0, "Next Character", ""},
@@ -1549,7 +1608,11 @@ void FONT_OT_delete(wmOperatorType *ot)
"Which part of the text to delete");
}
-/*********************** insert text operator *************************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Insert Text Operator
+ * \{ */
static int insert_text_exec(bContext *C, wmOperator *op)
{
@@ -1700,7 +1763,12 @@ void FONT_OT_text_insert(wmOperatorType *ot)
"Next typed character will strike through previous, for special character input");
}
-/*********************** textbox add operator *************************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Textbox Add Operator
+ * \{ */
+
static int textbox_add_exec(bContext *C, wmOperator *UNUSED(op))
{
Object *obedit = CTX_data_active_object(C);
@@ -1736,7 +1804,11 @@ void FONT_OT_textbox_add(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
-/*********************** textbox remove operator *************************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Textbox Remove Operator
+ * \{ */
static int textbox_remove_exec(bContext *C, wmOperator *op)
{
@@ -1778,7 +1850,11 @@ void FONT_OT_textbox_remove(wmOperatorType *ot)
RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "The current text box", 0, INT_MAX);
}
-/***************** editmode enter/exit ********************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Editmode Enter/Exit
+ * \{ */
void ED_curve_editfont_make(Object *obedit)
{
@@ -1851,7 +1927,11 @@ void ED_curve_editfont_free(Object *obedit)
BKE_curve_editfont_free((Curve *)obedit->data);
}
-/********************** set case operator *********************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Set Case Operator
+ * \{ */
static const EnumPropertyItem case_items[] = {
{CASE_LOWER, "LOWER", 0, "Lower", ""},
@@ -1920,7 +2000,11 @@ void FONT_OT_case_set(wmOperatorType *ot)
RNA_def_enum(ot->srna, "case", case_items, CASE_LOWER, "Case", "Lower or upper case");
}
-/********************** toggle case operator *********************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Toggle Case Operator
+ * \{ */
static int toggle_case_exec(bContext *C, wmOperator *UNUSED(op))
{
@@ -2031,7 +2115,7 @@ static int open_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)
if (pprop->prop) {
idptr = RNA_property_pointer_get((PointerRNA *)pprop, pprop->prop);
- vfont = idptr.id.data;
+ vfont = (VFont *)idptr.owner_id;
}
path = (vfont && !BKE_vfont_is_builtin(vfont)) ? vfont->name : U.fontdir;
@@ -2071,7 +2155,11 @@ void FONT_OT_open(wmOperatorType *ot)
FILE_SORT_ALPHA);
}
-/******************* delete operator *********************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Delete Operator
+ * \{ */
static int font_unlink_exec(bContext *C, wmOperator *op)
{
@@ -2190,3 +2278,5 @@ bool ED_curve_editfont_select_pick(
return false;
}
}
+
+/** \} */
diff --git a/source/blender/editors/gizmo_library/gizmo_types/arrow3d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/arrow3d_gizmo.c
index d23965269ab..fa9c0f1fbb2 100644
--- a/source/blender/editors/gizmo_library/gizmo_types/arrow3d_gizmo.c
+++ b/source/blender/editors/gizmo_library/gizmo_types/arrow3d_gizmo.c
@@ -363,8 +363,8 @@ static void gizmo_arrow_exit(bContext *C, wmGizmo *gz, const bool cancel)
const bool is_prop_valid = WM_gizmo_target_property_is_valid(gz_prop);
if (!cancel) {
- /* Assign incase applying the operation needs an updated offset
- * editmesh bisect needs this. */
+ /* Assign in case applying the operation needs an updated offset
+ * edit-mesh bisect needs this. */
if (is_prop_valid) {
const int transform_flag = RNA_enum_get(arrow->gizmo.ptr, "transform");
const bool constrained = (transform_flag & ED_GIZMO_ARROW_XFORM_FLAG_CONSTRAINED) != 0;
diff --git a/source/blender/editors/gizmo_library/gizmo_types/cage3d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/cage3d_gizmo.c
index e2a86469da1..406f76bc65e 100644
--- a/source/blender/editors/gizmo_library/gizmo_types/cage3d_gizmo.c
+++ b/source/blender/editors/gizmo_library/gizmo_types/cage3d_gizmo.c
@@ -358,7 +358,7 @@ static void gizmo_cage3d_draw_intern(
bool show = false;
if (gz->highlight_part == ED_GIZMO_CAGE3D_PART_TRANSLATE) {
/* Only show if we're drawing the center handle
- * otherwise the entire rectangle is the hotspot. */
+ * otherwise the entire rectangle is the hot-spot. */
if (draw_options & ED_GIZMO_CAGE2D_DRAW_FLAG_XFORM_CENTER_HANDLE) {
show = true;
}
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 21f779b72b1..37ee95d5058 100644
--- a/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c
+++ b/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c
@@ -283,6 +283,7 @@ static int gizmo_move_modal(bContext *C,
.use_occlusion_test = true,
},
mval_fl,
+ NULL,
&dist_px,
co,
NULL)) {
diff --git a/source/blender/editors/gpencil/annotate_paint.c b/source/blender/editors/gpencil/annotate_paint.c
index 07b61751b22..22f1753a810 100644
--- a/source/blender/editors/gpencil/annotate_paint.c
+++ b/source/blender/editors/gpencil/annotate_paint.c
@@ -1156,7 +1156,7 @@ static tGPsdata *gp_session_initpaint(bContext *C)
/* create new context data */
p = MEM_callocN(sizeof(tGPsdata), "Annotation Drawing Data");
- /* Try to initialise context data
+ /* Try to initialize context data
* WARNING: This may not always succeed (e.g. using GP in an annotation-only context)
*/
if (gp_session_initdata(C, p) == 0) {
@@ -1252,15 +1252,6 @@ static void gp_paint_initstroke(tGPsdata *p, eGPencil_PaintModes paintmode, Deps
/* Ensure active frame is set correctly... */
p->gpf = p->gpl->actframe;
- /* Restrict eraser to only affecting selected strokes, if the "selection mask" is on
- * (though this is only available in editmode)
- */
- if (p->gpd->flag & GP_DATA_STROKE_EDITMODE) {
- if (ts->gp_sculpt.flag & GP_SCULPT_SETT_FLAG_SELECT_MASK) {
- p->flags |= GP_PAINTFLAG_SELECTMASK;
- }
- }
-
if (has_layer_to_erase == false) {
p->status = GP_STATUS_CAPTURE;
// if (G.debug & G_DEBUG)
diff --git a/source/blender/editors/gpencil/gpencil_add_monkey.c b/source/blender/editors/gpencil/gpencil_add_monkey.c
index c4528518009..139697ad0e3 100644
--- a/source/blender/editors/gpencil/gpencil_add_monkey.c
+++ b/source/blender/editors/gpencil/gpencil_add_monkey.c
@@ -54,7 +54,7 @@ static int gpencil_monkey_color(
short *totcol = give_totcolp(ob);
Material *ma = NULL;
for (short i = 0; i < *totcol; i++) {
- ma = give_current_material(ob, i + 1);
+ ma = BKE_material_gpencil_get(ob, i + 1);
if (STREQ(ma->id.name, pct->name)) {
return i;
}
diff --git a/source/blender/editors/gpencil/gpencil_add_stroke.c b/source/blender/editors/gpencil/gpencil_add_stroke.c
index 80e239c9ae5..74617599eaa 100644
--- a/source/blender/editors/gpencil/gpencil_add_stroke.c
+++ b/source/blender/editors/gpencil/gpencil_add_stroke.c
@@ -53,7 +53,7 @@ static int gp_stroke_material(Main *bmain, Object *ob, const ColorTemplate *pct,
short *totcol = give_totcolp(ob);
Material *ma = NULL;
for (short i = 0; i < *totcol; i++) {
- ma = give_current_material(ob, i + 1);
+ ma = BKE_material_gpencil_get(ob, i + 1);
if (STREQ(ma->id.name, pct->name)) {
return i;
}
diff --git a/source/blender/editors/gpencil/gpencil_brush.c b/source/blender/editors/gpencil/gpencil_brush.c
index 9124c0f5d51..d1c4f271321 100644
--- a/source/blender/editors/gpencil/gpencil_brush.c
+++ b/source/blender/editors/gpencil/gpencil_brush.c
@@ -105,6 +105,7 @@ typedef struct tGP_BrushEditData {
eGP_Sculpt_Types brush_type;
eGP_Sculpt_Types brush_type_old;
eGP_Sculpt_Flag flag;
+ eGP_Sculpt_SelectMaskFlag mask;
/* Space Conversion Data */
GP_SpaceConversion gsc;
@@ -133,8 +134,8 @@ typedef struct tGP_BrushEditData {
/* - effect vector (e.g. 2D/3D translation for grab brush) */
float dvec[3];
- /* rotation for derived data */
- float rot;
+ /* rotation for evaluated data */
+ float rot_eval;
/* - multiframe falloff factor */
float mf_falloff;
@@ -300,6 +301,20 @@ static float gp_brush_influence_calc(tGP_BrushEditData *gso, const int radius, c
return influence;
}
+/* Force recal filling data */
+static void gp_recalc_geometry(bGPDstroke *gps)
+{
+ bGPDstroke *gps_orig = gps->runtime.gps_orig;
+ if (gps_orig) {
+ gps_orig->flag |= GP_STROKE_RECALC_GEOMETRY;
+ gps_orig->tot_triangles = 0;
+ }
+ else {
+ gps->flag |= GP_STROKE_RECALC_GEOMETRY;
+ gps->tot_triangles = 0;
+ }
+}
+
/* ************************************************ */
/* Brush Callbacks */
/* This section defines the callbacks used by each brush to perform their magic.
@@ -313,7 +328,7 @@ static float gp_brush_influence_calc(tGP_BrushEditData *gso, const int radius, c
* smooth-brush implementation to test the algorithm for stroke smoothing. */
static bool gp_brush_smooth_apply(tGP_BrushEditData *gso,
bGPDstroke *gps,
- float UNUSED(rot),
+ float UNUSED(rot_eval),
int pt_index,
const int radius,
const int co[2])
@@ -341,7 +356,7 @@ static bool gp_brush_smooth_apply(tGP_BrushEditData *gso,
BKE_gpencil_smooth_stroke_uv(gps, pt_index, inf);
}
- gps->flag |= GP_STROKE_RECALC_GEOMETRY;
+ gp_recalc_geometry(gps);
return true;
}
@@ -352,7 +367,7 @@ static bool gp_brush_smooth_apply(tGP_BrushEditData *gso,
/* Make lines thicker or thinner by the specified amounts */
static bool gp_brush_thickness_apply(tGP_BrushEditData *gso,
bGPDstroke *gps,
- float UNUSED(rot),
+ float UNUSED(rot_eval),
int pt_index,
const int radius,
const int co[2])
@@ -396,7 +411,7 @@ static bool gp_brush_thickness_apply(tGP_BrushEditData *gso,
/* Make color more or less transparent by the specified amounts */
static bool gp_brush_strength_apply(tGP_BrushEditData *gso,
bGPDstroke *gps,
- float UNUSED(rot),
+ float UNUSED(rot_eval),
int pt_index,
const int radius,
const int co[2])
@@ -443,7 +458,7 @@ typedef struct tGPSB_Grab_StrokeData {
/* array of influence weights for each of the included points */
float *weights;
/* angles to calc transformation */
- float *rot;
+ float *rot_eval;
/* capacity of the arrays */
int capacity;
@@ -471,7 +486,7 @@ static void gp_brush_grab_stroke_init(tGP_BrushEditData *gso, bGPDstroke *gps)
memset(data->points, 0, sizeof(int) * data->capacity);
memset(data->weights, 0, sizeof(float) * data->capacity);
- memset(data->rot, 0, sizeof(float) * data->capacity);
+ memset(data->rot_eval, 0, sizeof(float) * data->capacity);
}
else {
/* Create new instance */
@@ -482,7 +497,7 @@ static void gp_brush_grab_stroke_init(tGP_BrushEditData *gso, bGPDstroke *gps)
data->points = MEM_callocN(sizeof(int) * data->capacity, "GP Stroke Grab Indices");
data->weights = MEM_callocN(sizeof(float) * data->capacity, "GP Stroke Grab Weights");
- data->rot = MEM_callocN(sizeof(float) * data->capacity, "GP Stroke Grab X");
+ data->rot_eval = MEM_callocN(sizeof(float) * data->capacity, "GP Stroke Grab Rotations");
/* hook up to the cache */
BLI_ghash_insert(gso->stroke_customdata, gps, data);
@@ -492,7 +507,7 @@ static void gp_brush_grab_stroke_init(tGP_BrushEditData *gso, bGPDstroke *gps)
/* store references to stroke points in the initial stage */
static bool gp_brush_grab_store_points(tGP_BrushEditData *gso,
bGPDstroke *gps,
- float rot,
+ float rot_eval,
int pt_index,
const int radius,
const int co[2])
@@ -506,7 +521,7 @@ static bool gp_brush_grab_store_points(tGP_BrushEditData *gso,
/* insert this point into the set of affected points */
data->points[data->size] = pt_index;
data->weights[data->size] = inf;
- data->rot[data->size] = rot;
+ data->rot_eval[data->size] = rot_eval;
data->size++;
/* done */
@@ -530,10 +545,10 @@ static void gp_brush_grab_calc_dvec(tGP_BrushEditData *gso)
mval_f[0] = (float)(gso->mval[0] - gso->mval_prev[0]);
mval_f[1] = (float)(gso->mval[1] - gso->mval_prev[1]);
- /* apply derived data transformation */
- if (gso->rot != 0.0f) {
- const float cval = cos(gso->rot);
- const float sval = sin(gso->rot);
+ /* apply evaluated data transformation */
+ if (gso->rot_eval != 0.0f) {
+ const float cval = cos(gso->rot_eval);
+ const float sval = sin(gso->rot_eval);
float r[2];
r[0] = (mval_f[0] * cval) - (mval_f[1] * sval);
r[1] = (mval_f[0] * sval) + (mval_f[1] * cval);
@@ -564,8 +579,8 @@ static void gp_brush_grab_apply_cached(tGP_BrushEditData *gso,
bGPDspoint *pt = &gps->points[data->points[i]];
float delta[3] = {0.0f};
- /* get derived transformation */
- gso->rot = data->rot[i];
+ /* get evaluated transformation */
+ gso->rot_eval = data->rot_eval[i];
gp_brush_grab_calc_dvec(gso);
/* adjust the amount of displacement to apply */
@@ -586,7 +601,7 @@ static void gp_brush_grab_apply_cached(tGP_BrushEditData *gso,
/* compute lock axis */
gpsculpt_compute_lock_axis(gso, pt, save_pt);
}
- gps->flag |= GP_STROKE_RECALC_GEOMETRY;
+ gp_recalc_geometry(gps);
}
/* free customdata used for handling this stroke */
@@ -597,7 +612,7 @@ static void gp_brush_grab_stroke_free(void *ptr)
/* free arrays */
MEM_SAFE_FREE(data->points);
MEM_SAFE_FREE(data->weights);
- MEM_SAFE_FREE(data->rot);
+ MEM_SAFE_FREE(data->rot_eval);
/* ... and this item itself, since it was also allocated */
MEM_freeN(data);
@@ -608,7 +623,7 @@ static void gp_brush_grab_stroke_free(void *ptr)
/* NOTE: Depends on gp_brush_grab_calc_dvec() */
static bool gp_brush_push_apply(tGP_BrushEditData *gso,
bGPDstroke *gps,
- float UNUSED(rot),
+ float UNUSED(rot_eval),
int pt_index,
const int radius,
const int co[2])
@@ -676,7 +691,7 @@ static void gp_brush_calc_midpoint(tGP_BrushEditData *gso)
/* Shrink distance between midpoint and this point... */
static bool gp_brush_pinch_apply(tGP_BrushEditData *gso,
bGPDstroke *gps,
- float UNUSED(rot),
+ float UNUSED(rot_eval),
int pt_index,
const int radius,
const int co[2])
@@ -721,7 +736,7 @@ static bool gp_brush_pinch_apply(tGP_BrushEditData *gso,
/* compute lock axis */
gpsculpt_compute_lock_axis(gso, pt, save_pt);
- gps->flag |= GP_STROKE_RECALC_GEOMETRY;
+ gp_recalc_geometry(gps);
/* done */
return true;
@@ -735,7 +750,7 @@ static bool gp_brush_pinch_apply(tGP_BrushEditData *gso,
static bool gp_brush_twist_apply(tGP_BrushEditData *gso,
bGPDstroke *gps,
- float UNUSED(rot),
+ float UNUSED(rot_eval),
int pt_index,
const int radius,
const int co[2])
@@ -809,7 +824,7 @@ static bool gp_brush_twist_apply(tGP_BrushEditData *gso,
}
}
- gps->flag |= GP_STROKE_RECALC_GEOMETRY;
+ gp_recalc_geometry(gps);
/* done */
return true;
@@ -820,7 +835,7 @@ static bool gp_brush_twist_apply(tGP_BrushEditData *gso,
/* Apply some random jitter to the point */
static bool gp_brush_randomize_apply(tGP_BrushEditData *gso,
bGPDstroke *gps,
- float UNUSED(rot),
+ float UNUSED(rot_eval),
int pt_index,
const int radius,
const int co[2])
@@ -929,7 +944,7 @@ static bool gp_brush_randomize_apply(tGP_BrushEditData *gso,
CLAMP(pt->uv_rot, -M_PI_2, M_PI_2);
}
- gps->flag |= GP_STROKE_RECALC_GEOMETRY;
+ gp_recalc_geometry(gps);
/* done */
return true;
@@ -939,7 +954,7 @@ static bool gp_brush_randomize_apply(tGP_BrushEditData *gso,
/* Change weight paint for vertex groups */
static bool gp_brush_weight_apply(tGP_BrushEditData *gso,
bGPDstroke *gps,
- float UNUSED(rot),
+ float UNUSED(rot_eval),
int pt_index,
const int radius,
const int co[2])
@@ -1175,7 +1190,7 @@ static void gp_brush_clone_adjust(tGP_BrushEditData *gso)
size_t snum;
/* Compute the amount of movement to apply (overwrites dvec) */
- gso->rot = 0.0f;
+ gso->rot_eval = 0.0f;
gp_brush_grab_calc_dvec(gso);
/* For each of the stored strokes, apply the offset to each point */
@@ -1323,6 +1338,9 @@ static bool gpsculpt_brush_init(bContext *C, wmOperator *op)
gso->sa = CTX_wm_area(C);
gso->ar = CTX_wm_region(C);
+ /* save mask */
+ gso->mask = ts->gpencil_selectmode_sculpt;
+
/* multiframe settings */
gso->is_multiframe = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gso->gpd);
gso->use_multiframe_falloff = (ts->gp_sculpt.flag & GP_SCULPT_SETT_FLAG_FRAME_FALLOFF) != 0;
@@ -1486,35 +1504,38 @@ static void gpsculpt_brush_init_stroke(tGP_BrushEditData *gso)
/* Apply ----------------------------------------------- */
-/* Get angle of the segment relative to the original segment before any transformation */
-static float gpsculpt_transform_rot_get(GP_SpaceConversion *gsc,
- bGPDstroke *gps_derived,
- bGPDspoint *pt_derived,
- int idx_derived)
+/* Get angle of the segment relative to the original segment before any transformation
+ * For strokes with one point only this is impossible to calculate because there isn't a
+ * valid reference point.
+ */
+static float gpsculpt_rotation_eval_get(GP_SpaceConversion *gsc,
+ bGPDstroke *gps_eval,
+ bGPDspoint *pt_eval,
+ int idx_eval)
{
- bGPDstroke *gps_orig = gps_derived->runtime.gps_orig;
- bGPDspoint *pt_orig = &gps_orig->points[pt_derived->runtime.idx_orig];
- bGPDspoint *pt_derived_prev = NULL;
+ bGPDstroke *gps_orig = gps_eval->runtime.gps_orig;
+ bGPDspoint *pt_orig = &gps_orig->points[pt_eval->runtime.idx_orig];
+ bGPDspoint *pt_prev_eval = NULL;
bGPDspoint *pt_orig_prev = NULL;
- if (idx_derived != 0) {
- pt_derived_prev = &gps_derived->points[idx_derived - 1];
+ if (idx_eval != 0) {
+ pt_prev_eval = &gps_eval->points[idx_eval - 1];
}
else {
- if (gps_derived->totpoints > 1) {
- pt_derived_prev = &gps_derived->points[idx_derived + 1];
+ if (gps_eval->totpoints > 1) {
+ pt_prev_eval = &gps_eval->points[idx_eval + 1];
}
else {
return 0.0f;
}
}
- if (pt_derived->runtime.idx_orig != 0) {
- pt_orig_prev = &gps_orig->points[pt_derived->runtime.idx_orig - 1];
+ if (pt_eval->runtime.idx_orig != 0) {
+ pt_orig_prev = &gps_orig->points[pt_eval->runtime.idx_orig - 1];
}
else {
if (gps_orig->totpoints > 1) {
- pt_orig_prev = &gps_orig->points[pt_derived->runtime.idx_orig + 1];
+ pt_orig_prev = &gps_orig->points[pt_eval->runtime.idx_orig + 1];
}
else {
return 0.0f;
@@ -1522,17 +1543,17 @@ static float gpsculpt_transform_rot_get(GP_SpaceConversion *gsc,
}
/* create 2D vectors of the stroke segments */
- float v_orig_a[2], v_orig_b[2], v_derived_a[2], v_derived_b[2];
+ float v_orig_a[2], v_orig_b[2], v_eval_a[2], v_eval_b[2];
gp_point_3d_to_xy(gsc, GP_STROKE_3DSPACE, &pt_orig->x, v_orig_a);
gp_point_3d_to_xy(gsc, GP_STROKE_3DSPACE, &pt_orig_prev->x, v_orig_b);
sub_v2_v2(v_orig_a, v_orig_b);
- gp_point_3d_to_xy(gsc, GP_STROKE_3DSPACE, &pt_derived->x, v_derived_a);
- gp_point_3d_to_xy(gsc, GP_STROKE_3DSPACE, &pt_derived_prev->x, v_derived_b);
- sub_v2_v2(v_derived_a, v_derived_b);
+ gp_point_3d_to_xy(gsc, GP_STROKE_3DSPACE, &pt_eval->x, v_eval_a);
+ gp_point_3d_to_xy(gsc, GP_STROKE_3DSPACE, &pt_prev_eval->x, v_eval_b);
+ sub_v2_v2(v_eval_a, v_eval_b);
- return angle_v2v2(v_orig_a, v_derived_a);
+ return angle_v2v2(v_orig_a, v_eval_a);
}
/* Apply brush operation to points in this stroke */
@@ -1555,7 +1576,7 @@ static bool gpsculpt_brush_do_stroke(tGP_BrushEditData *gso,
int i;
bool include_last = false;
bool changed = false;
- float rot = 0.0f;
+ float rot_eval = 0.0f;
if (gps->totpoints == 1) {
bGPDspoint pt_temp;
pt = &gps->points[0];
@@ -1570,8 +1591,8 @@ static bool gpsculpt_brush_do_stroke(tGP_BrushEditData *gso,
if (len_v2v2_int(mval_i, pc1) <= radius) {
/* apply operation to this point */
if (pt->runtime.pt_orig != NULL) {
- rot = gpsculpt_transform_rot_get(&gso->gsc, gps, pt, 0);
- changed = apply(gso, gps->runtime.gps_orig, rot, pt->runtime.idx_orig, radius, pc1);
+ rot_eval = gpsculpt_rotation_eval_get(&gso->gsc, gps, pt, 0);
+ changed = apply(gso, gps->runtime.gps_orig, rot_eval, pt->runtime.idx_orig, radius, pc1);
}
}
}
@@ -1587,7 +1608,7 @@ static bool gpsculpt_brush_do_stroke(tGP_BrushEditData *gso,
/* Skip if neither one is selected
* (and we are only allowed to edit/consider selected points) */
- if ((gso->settings->flag & GP_SCULPT_SETT_FLAG_SELECT_MASK) && (!gso->is_weight_mode)) {
+ if ((GPENCIL_ANY_SCULPT_MASK(gso->mask)) && (!gso->is_weight_mode)) {
if (!(pt1->flag & GP_SPOINT_SELECT) && !(pt2->flag & GP_SPOINT_SELECT)) {
include_last = false;
continue;
@@ -1615,8 +1636,8 @@ static bool gpsculpt_brush_do_stroke(tGP_BrushEditData *gso,
/* To each point individually... */
pt = &gps->points[i];
if (pt->runtime.pt_orig != NULL) {
- rot = gpsculpt_transform_rot_get(&gso->gsc, gps, pt, i);
- ok = apply(gso, gps->runtime.gps_orig, rot, pt->runtime.idx_orig, radius, pc1);
+ rot_eval = gpsculpt_rotation_eval_get(&gso->gsc, gps, pt, i);
+ ok = apply(gso, gps->runtime.gps_orig, rot_eval, pt->runtime.idx_orig, radius, pc1);
}
/* Only do the second point if this is the last segment,
@@ -1630,8 +1651,8 @@ static bool gpsculpt_brush_do_stroke(tGP_BrushEditData *gso,
if (i + 1 == gps->totpoints - 1) {
pt = &gps->points[i + 1];
if (pt->runtime.pt_orig != NULL) {
- rot = gpsculpt_transform_rot_get(&gso->gsc, gps, pt, i + 1);
- ok |= apply(gso, gps->runtime.gps_orig, rot, pt->runtime.idx_orig, radius, pc2);
+ rot_eval = gpsculpt_rotation_eval_get(&gso->gsc, gps, pt, i + 1);
+ ok |= apply(gso, gps->runtime.gps_orig, rot_eval, pt->runtime.idx_orig, radius, pc2);
include_last = false;
}
}
@@ -1649,8 +1670,9 @@ static bool gpsculpt_brush_do_stroke(tGP_BrushEditData *gso,
*/
pt = &gps->points[i];
if (pt->runtime.pt_orig != NULL) {
- rot = gpsculpt_transform_rot_get(&gso->gsc, gps, pt, i);
- changed |= apply(gso, gps->runtime.gps_orig, rot, pt->runtime.idx_orig, radius, pc1);
+ rot_eval = gpsculpt_rotation_eval_get(&gso->gsc, gps, pt, i);
+ changed |= apply(
+ gso, gps->runtime.gps_orig, rot_eval, pt->runtime.idx_orig, radius, pc1);
include_last = false;
}
}
@@ -1752,10 +1774,7 @@ static bool gpsculpt_brush_do_frame(
break;
}
/* Triangulation must be calculated if changed */
- if (changed) {
- gps->flag |= GP_STROKE_RECALC_GEOMETRY;
- gps->tot_triangles = 0;
- }
+ gp_recalc_geometry(gps);
}
return changed;
@@ -1777,7 +1796,7 @@ static bool gpsculpt_brush_apply_standard(bContext *C, tGP_BrushEditData *gso)
case GP_SCULPT_TYPE_PUSH: /* Push points */
{
/* calculate amount of displacement to apply */
- gso->rot = 0.0f;
+ gso->rot_eval = 0.0f;
gp_brush_grab_calc_dvec(gso);
break;
}
@@ -1793,7 +1812,7 @@ static bool gpsculpt_brush_apply_standard(bContext *C, tGP_BrushEditData *gso)
case GP_SCULPT_TYPE_RANDOMIZE: /* Random jitter */
{
/* compute the displacement vector for the cursor (in data space) */
- gso->rot = 0.0f;
+ gso->rot_eval = 0.0f;
gp_brush_grab_calc_dvec(gso);
break;
}
@@ -1808,10 +1827,10 @@ static bool gpsculpt_brush_apply_standard(bContext *C, tGP_BrushEditData *gso)
if (gpl->actframe == NULL) {
continue;
}
- /* Get derived frames array data */
- int derived_idx = BLI_findindex(&gpd->layers, gpl);
- bGPDframe *derived_gpf = &ob_eval->runtime.derived_frames[derived_idx];
- if (derived_gpf == NULL) {
+ /* Get evaluated frames array data */
+ int idx_eval = BLI_findindex(&gpd->layers, gpl);
+ bGPDframe *gpf_eval = &ob_eval->runtime.gpencil_evaluated_frames[idx_eval];
+ if (gpf_eval == NULL) {
continue;
}
@@ -1845,14 +1864,14 @@ static bool gpsculpt_brush_apply_standard(bContext *C, tGP_BrushEditData *gso)
/* affect strokes in this frame */
changed |= gpsculpt_brush_do_frame(
- C, gso, gpl, (gpf == gpl->actframe) ? derived_gpf : gpf, diff_mat);
+ C, gso, gpl, (gpf == gpl->actframe) ? gpf_eval : gpf, diff_mat);
}
}
}
else {
/* Apply to active frame's strokes */
gso->mf_falloff = 1.0f;
- changed |= gpsculpt_brush_do_frame(C, gso, gpl, derived_gpf, diff_mat);
+ changed |= gpsculpt_brush_do_frame(C, gso, gpl, gpf_eval, diff_mat);
}
}
CTX_DATA_END;
diff --git a/source/blender/editors/gpencil/gpencil_data.c b/source/blender/editors/gpencil/gpencil_data.c
index 0928913aad5..e763eec1b0d 100644
--- a/source/blender/editors/gpencil/gpencil_data.c
+++ b/source/blender/editors/gpencil/gpencil_data.c
@@ -105,7 +105,7 @@ static bool gp_data_add_poll(bContext *C)
/* add new datablock - wrapper around API */
static int gp_data_add_exec(bContext *C, wmOperator *op)
{
- PointerRNA gpd_owner = {{NULL}};
+ PointerRNA gpd_owner = {NULL};
bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, &gpd_owner);
bool is_annotation = ED_gpencil_data_owner_is_annotation(&gpd_owner);
@@ -231,7 +231,7 @@ void GPENCIL_OT_data_unlink(wmOperatorType *ot)
/* add new layer - wrapper around API */
static int gp_layer_add_exec(bContext *C, wmOperator *op)
{
- PointerRNA gpd_owner = {{NULL}};
+ PointerRNA gpd_owner = {NULL};
bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, &gpd_owner);
bool is_annotation = ED_gpencil_data_owner_is_annotation(&gpd_owner);
@@ -538,10 +538,12 @@ static int gp_layer_duplicate_object_exec(bContext *C, wmOperator *op)
* otherwise add the slot with the material
*/
Material *ma_src = give_current_material(ob_src, gps_src->mat_nr + 1);
- int idx = BKE_gpencil_object_material_ensure(bmain, ob_dst, ma_src);
+ if (ma_src != NULL) {
+ int idx = BKE_gpencil_object_material_ensure(bmain, ob_dst, ma_src);
- /* Reassign the stroke material to the right slot in destination object. */
- gps_dst->mat_nr = idx;
+ /* Reassign the stroke material to the right slot in destination object. */
+ gps_dst->mat_nr = idx;
+ }
/* add new stroke to frame */
BLI_addtail(&gpf_dst->strokes, gps_dst);
@@ -840,6 +842,10 @@ static int gp_hide_exec(bContext *C, wmOperator *op)
if (gpl != layer) {
gpl->flag |= GP_LAYER_HIDE;
}
+ else {
+ /* Be sure the active layer is unhidden. */
+ gpl->flag &= ~GP_LAYER_HIDE;
+ }
}
}
else {
@@ -1419,7 +1425,7 @@ static int gp_stroke_change_color_exec(bContext *C, wmOperator *op)
bGPdata *gpd = ED_gpencil_data_get_active(C);
Object *ob = CTX_data_active_object(C);
if (name[0] == '\0') {
- ma = give_current_material(ob, ob->actcol);
+ ma = BKE_material_gpencil_get(ob, ob->actcol);
}
else {
ma = (Material *)BKE_libblock_find_name(bmain, ID_MA, name);
@@ -1539,9 +1545,10 @@ static int gp_stroke_lock_color_exec(bContext *C, wmOperator *UNUSED(op))
}
/* unlock color */
Material *tmp_ma = give_current_material(ob, gps->mat_nr + 1);
-
- tmp_ma->gp_style->flag &= ~GP_STYLE_COLOR_LOCKED;
- DEG_id_tag_update(&tmp_ma->id, ID_RECALC_COPY_ON_WRITE);
+ if (tmp_ma) {
+ tmp_ma->gp_style->flag &= ~GP_STYLE_COLOR_LOCKED;
+ DEG_id_tag_update(&tmp_ma->id, ID_RECALC_COPY_ON_WRITE);
+ }
}
}
}
@@ -2291,7 +2298,7 @@ int ED_gpencil_join_objects_exec(bContext *C, wmOperator *op)
short *totcol = give_totcolp(ob_src);
for (short i = 0; i < *totcol; i++) {
- Material *tmp_ma = give_current_material(ob_src, i + 1);
+ Material *tmp_ma = BKE_material_gpencil_get(ob_src, i + 1);
BKE_gpencil_object_material_ensure(bmain, ob_dst, tmp_ma);
}
@@ -2325,7 +2332,7 @@ int ED_gpencil_join_objects_exec(bContext *C, wmOperator *op)
for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
/* Reassign material. Look old material and try to find in destination. */
- ma_src = give_current_material(ob_src, gps->mat_nr + 1);
+ ma_src = BKE_material_gpencil_get(ob_src, gps->mat_nr + 1);
gps->mat_nr = BKE_gpencil_object_material_ensure(bmain, ob_dst, ma_src);
bGPDspoint *pt;
@@ -2433,7 +2440,7 @@ static int gpencil_lock_layer_exec(bContext *C, wmOperator *UNUSED(op))
}
for (short i = 0; i < *totcol; i++) {
- ma = give_current_material(ob, i + 1);
+ ma = BKE_material_gpencil_get(ob, i + 1);
if (ma) {
gp_style = ma->gp_style;
gp_style->flag |= GP_STYLE_COLOR_LOCKED;
@@ -2453,7 +2460,7 @@ static int gpencil_lock_layer_exec(bContext *C, wmOperator *UNUSED(op))
continue;
}
- ma = give_current_material(ob, gps->mat_nr + 1);
+ ma = BKE_material_gpencil_get(ob, gps->mat_nr + 1);
DEG_id_tag_update(&ma->id, ID_RECALC_COPY_ON_WRITE);
gp_style = ma->gp_style;
@@ -2496,7 +2503,7 @@ static int gpencil_color_isolate_exec(bContext *C, wmOperator *op)
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
Object *ob = CTX_data_active_object(C);
- Material *active_ma = give_current_material(ob, ob->actcol);
+ Material *active_ma = BKE_material_gpencil_get(ob, ob->actcol);
MaterialGPencilStyle *active_color = BKE_material_gpencil_settings_get(ob, ob->actcol);
MaterialGPencilStyle *gp_style;
@@ -2516,7 +2523,7 @@ static int gpencil_color_isolate_exec(bContext *C, wmOperator *op)
Material *ma = NULL;
short *totcol = give_totcolp(ob);
for (short i = 0; i < *totcol; i++) {
- ma = give_current_material(ob, i + 1);
+ ma = BKE_material_gpencil_get(ob, i + 1);
/* Skip if this is the active one */
if ((ma == NULL) || (ma == active_ma)) {
continue;
@@ -2536,7 +2543,7 @@ static int gpencil_color_isolate_exec(bContext *C, wmOperator *op)
if (isolate) {
/* Set flags on all "other" colors */
for (short i = 0; i < *totcol; i++) {
- ma = give_current_material(ob, i + 1);
+ ma = BKE_material_gpencil_get(ob, i + 1);
if (ma == NULL) {
continue;
}
@@ -2553,7 +2560,7 @@ static int gpencil_color_isolate_exec(bContext *C, wmOperator *op)
else {
/* Clear flags - Restore everything else */
for (short i = 0; i < *totcol; i++) {
- ma = give_current_material(ob, i + 1);
+ ma = BKE_material_gpencil_get(ob, i + 1);
if (ma == NULL) {
continue;
}
@@ -2618,7 +2625,7 @@ static int gpencil_color_hide_exec(bContext *C, wmOperator *op)
/* hide unselected */
MaterialGPencilStyle *color = NULL;
for (short i = 0; i < *totcol; i++) {
- ma = give_current_material(ob, i + 1);
+ ma = BKE_material_gpencil_get(ob, i + 1);
if (ma) {
color = ma->gp_style;
if (active_color != color) {
@@ -2681,7 +2688,7 @@ static int gpencil_color_reveal_exec(bContext *C, wmOperator *UNUSED(op))
MaterialGPencilStyle *gp_style = NULL;
for (short i = 0; i < *totcol; i++) {
- ma = give_current_material(ob, i + 1);
+ ma = BKE_material_gpencil_get(ob, i + 1);
if (ma) {
gp_style = ma->gp_style;
gp_style->flag &= ~GP_STYLE_COLOR_HIDE;
@@ -2734,7 +2741,7 @@ static int gpencil_color_lock_all_exec(bContext *C, wmOperator *UNUSED(op))
MaterialGPencilStyle *gp_style = NULL;
for (short i = 0; i < *totcol; i++) {
- ma = give_current_material(ob, i + 1);
+ ma = BKE_material_gpencil_get(ob, i + 1);
if (ma) {
gp_style = ma->gp_style;
gp_style->flag |= GP_STYLE_COLOR_LOCKED;
@@ -2787,7 +2794,7 @@ static int gpencil_color_unlock_all_exec(bContext *C, wmOperator *UNUSED(op))
MaterialGPencilStyle *gp_style = NULL;
for (short i = 0; i < *totcol; i++) {
- ma = give_current_material(ob, i + 1);
+ ma = BKE_material_gpencil_get(ob, i + 1);
if (ma) {
gp_style = ma->gp_style;
gp_style->flag &= ~GP_STYLE_COLOR_LOCKED;
diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c
index cc229fcb383..12b3792e36e 100644
--- a/source/blender/editors/gpencil/gpencil_edit.c
+++ b/source/blender/editors/gpencil/gpencil_edit.c
@@ -222,6 +222,17 @@ void GPENCIL_OT_editmode_toggle(wmOperatorType *ot)
}
/* set select mode */
+static bool gpencil_selectmode_toggle_poll(bContext *C)
+{
+ /* edit only supported with grease pencil objects */
+ Object *ob = CTX_data_active_object(C);
+ if ((ob == NULL) || (ob->type != OB_GPENCIL) || (ob->mode != OB_MODE_EDIT_GPENCIL)) {
+ return false;
+ }
+
+ return ED_operator_view3d_active(C);
+}
+
static int gpencil_selectmode_toggle_exec(bContext *C, wmOperator *op)
{
Scene *scene = CTX_data_scene(C);
@@ -229,7 +240,7 @@ static int gpencil_selectmode_toggle_exec(bContext *C, wmOperator *op)
const int mode = RNA_int_get(op->ptr, "mode");
/* Just set mode */
- ts->gpencil_selectmode = mode;
+ ts->gpencil_selectmode_edit = mode;
WM_main_add_notifier(NC_SCENE | ND_TOOLSETTINGS, NULL);
DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE);
@@ -248,7 +259,7 @@ void GPENCIL_OT_selectmode_toggle(wmOperatorType *ot)
/* callbacks */
ot->exec = gpencil_selectmode_toggle_exec;
- ot->poll = gp_strokes_edit3d_poll;
+ ot->poll = gpencil_selectmode_toggle_poll;
/* flags */
ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
@@ -1180,10 +1191,15 @@ static int gp_strokes_copy_exec(bContext *C, wmOperator *op)
GHash *ma_to_name = gp_strokes_copypastebuf_colors_material_to_name_create(bmain);
for (bGPDstroke *gps = gp_strokes_copypastebuf.first; gps; gps = gps->next) {
if (ED_gpencil_stroke_can_use(C, gps)) {
+ Material *ma = give_current_material(ob, gps->mat_nr + 1);
+ /* Avoid default material. */
+ if (ma == NULL) {
+ continue;
+ }
+
char **ma_name_val;
if (!BLI_ghash_ensure_p(
gp_strokes_copypastebuf_colors, &gps->mat_nr, (void ***)&ma_name_val)) {
- Material *ma = give_current_material(ob, gps->mat_nr + 1);
char *ma_name = BLI_ghash_lookup(ma_to_name, ma);
*ma_name_val = MEM_dupallocN(ma_name);
}
@@ -1230,8 +1246,8 @@ static bool gp_strokes_paste_poll(bContext *C)
}
typedef enum eGP_PasteMode {
- GP_COPY_ONLY = -1,
- GP_COPY_MERGE = 1,
+ GP_COPY_BY_LAYER = -1,
+ GP_COPY_TO_ACTIVE = 1,
} eGP_PasteMode;
static int gp_strokes_paste_exec(bContext *C, wmOperator *op)
@@ -1264,7 +1280,7 @@ static int gp_strokes_paste_exec(bContext *C, wmOperator *op)
/* no active layer - let's just create one */
gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true);
}
- else if ((gpencil_layer_is_editable(gpl) == false) && (type == GP_COPY_MERGE)) {
+ else if ((gpencil_layer_is_editable(gpl) == false) && (type == GP_COPY_TO_ACTIVE)) {
BKE_report(
op->reports, RPT_ERROR, "Can not paste strokes when active layer is hidden or locked");
return OPERATOR_CANCELLED;
@@ -1317,7 +1333,7 @@ static int gp_strokes_paste_exec(bContext *C, wmOperator *op)
for (bGPDstroke *gps = gp_strokes_copypastebuf.first; gps; gps = gps->next) {
if (ED_gpencil_stroke_can_use(C, gps)) {
/* Need to verify if layer exists */
- if (type != GP_COPY_MERGE) {
+ if (type != GP_COPY_TO_ACTIVE) {
gpl = BLI_findstring(&gpd->layers, gps->runtime.tmp_layerinfo, offsetof(bGPDlayer, info));
if (gpl == NULL) {
/* no layer - use active (only if layer deleted before paste) */
@@ -1350,7 +1366,7 @@ static int gp_strokes_paste_exec(bContext *C, wmOperator *op)
/* Remap material */
Material *ma = BLI_ghash_lookup(new_colors, POINTER_FROM_INT(new_stroke->mat_nr));
new_stroke->mat_nr = BKE_gpencil_object_material_get_index(ob, ma);
- BLI_assert(new_stroke->mat_nr >= 0); /* have to add the material first */
+ CLAMP_MIN(new_stroke->mat_nr, 0);
}
}
}
@@ -1368,15 +1384,15 @@ static int gp_strokes_paste_exec(bContext *C, wmOperator *op)
void GPENCIL_OT_paste(wmOperatorType *ot)
{
static const EnumPropertyItem copy_type[] = {
- {GP_COPY_ONLY, "COPY", 0, "Copy", ""},
- {GP_COPY_MERGE, "MERGE", 0, "Merge", ""},
+ {GP_COPY_TO_ACTIVE, "ACTIVE", 0, "Paste to Active", ""},
+ {GP_COPY_BY_LAYER, "LAYER", 0, "Paste by Layer", ""},
{0, NULL, 0, NULL, NULL},
};
/* identifiers */
ot->name = "Paste Strokes";
ot->idname = "GPENCIL_OT_paste";
- ot->description = "Paste previously copied strokes or copy and merge in active layer";
+ ot->description = "Paste previously copied strokes to active layer or to original layer";
/* callbacks */
ot->exec = gp_strokes_paste_exec;
@@ -1386,33 +1402,18 @@ void GPENCIL_OT_paste(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
- ot->prop = RNA_def_enum(ot->srna, "type", copy_type, 0, "Type", "");
+ ot->prop = RNA_def_enum(ot->srna, "type", copy_type, GP_COPY_TO_ACTIVE, "Type", "");
}
/* ******************* Move To Layer ****************************** */
-static int gp_move_to_layer_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(evt))
-{
- uiPopupMenu *pup;
- uiLayout *layout;
-
- /* call the menu, which will call this operator again, hence the canceled */
- pup = UI_popup_menu_begin(C, op->type->name, ICON_NONE);
- layout = UI_popup_menu_layout(pup);
- uiItemsEnumO(layout, "GPENCIL_OT_move_to_layer", "layer");
- UI_popup_menu_end(C, pup);
-
- return OPERATOR_INTERFACE;
-}
-
-// FIXME: allow moving partial strokes
static int gp_move_to_layer_exec(bContext *C, wmOperator *op)
{
bGPdata *gpd = CTX_data_gpencil_data(C);
Scene *scene = CTX_data_scene(C);
bGPDlayer *target_layer = NULL;
ListBase strokes = {NULL, NULL};
- int layer_num = RNA_enum_get(op->ptr, "layer");
+ int layer_num = RNA_int_get(op->ptr, "layer");
const bool use_autolock = (bool)(gpd->flag & GP_DATA_AUTOLOCK_LAYERS);
if (GPENCIL_MULTIEDIT_SESSIONS_ON(gpd)) {
@@ -1425,23 +1426,16 @@ static int gp_move_to_layer_exec(bContext *C, wmOperator *op)
gpd->flag &= ~GP_DATA_AUTOLOCK_LAYERS;
}
- /* Get layer or create new one */
- if (layer_num == -1) {
- /* Create layer */
- target_layer = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true);
- }
- else {
- /* Try to get layer */
- target_layer = BLI_findlink(&gpd->layers, layer_num);
+ /* Try to get layer */
+ target_layer = BLI_findlink(&gpd->layers, layer_num);
- if (target_layer == NULL) {
- /* back autolock status */
- if (use_autolock) {
- gpd->flag |= GP_DATA_AUTOLOCK_LAYERS;
- }
- BKE_reportf(op->reports, RPT_ERROR, "There is no layer number %d", layer_num);
- return OPERATOR_CANCELLED;
+ if (target_layer == NULL) {
+ /* back autolock status */
+ if (use_autolock) {
+ gpd->flag |= GP_DATA_AUTOLOCK_LAYERS;
}
+ BKE_reportf(op->reports, RPT_ERROR, "There is no layer number %d", layer_num);
+ return OPERATOR_CANCELLED;
}
/* Extract all strokes to move to this layer
@@ -1509,16 +1503,14 @@ void GPENCIL_OT_move_to_layer(wmOperatorType *ot)
"Move selected strokes to another layer"; // XXX: allow moving individual points too?
/* callbacks */
- ot->invoke = gp_move_to_layer_invoke;
ot->exec = gp_move_to_layer_exec;
ot->poll = gp_stroke_edit_poll; // XXX?
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
- /* gp layer to use (dynamic enum) */
- ot->prop = RNA_def_enum(ot->srna, "layer", DummyRNA_DEFAULT_items, 0, "Grease Pencil Layer", "");
- RNA_def_enum_funcs(ot->prop, ED_gpencil_layers_with_new_enum_itemf);
+ /* GPencil layer to use. */
+ ot->prop = RNA_def_int(ot->srna, "layer", 0, 0, INT_MAX, "Grease Pencil Layer", "", 0, INT_MAX);
}
/* ********************* Add Blank Frame *************************** */
@@ -3333,7 +3325,7 @@ typedef enum eGP_ReprojectModes {
GP_REPROJECT_FRONT = 0,
GP_REPROJECT_SIDE,
GP_REPROJECT_TOP,
- /* On same plane, parallel to viewplane */
+ /* On same plane, parallel to view-plane. */
GP_REPROJECT_VIEW,
/* Reprojected on to the scene geometry */
GP_REPROJECT_SURFACE,
@@ -3344,11 +3336,14 @@ static int gp_strokes_reproject_exec(bContext *C, wmOperator *op)
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
Scene *scene = CTX_data_scene(C);
+ Main *bmain = CTX_data_main(C);
ToolSettings *ts = CTX_data_tool_settings(C);
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
Object *ob = CTX_data_active_object(C);
ARegion *ar = CTX_wm_region(C);
RegionView3D *rv3d = ar->regiondata;
+ SnapObjectContext *sctx = NULL;
+ int oldframe = (int)DEG_get_ctime(depsgraph);
GP_SpaceConversion gsc = {NULL};
eGP_ReprojectModes mode = RNA_enum_get(op->ptr, "type");
@@ -3358,28 +3353,33 @@ static int gp_strokes_reproject_exec(bContext *C, wmOperator *op)
/* init space conversion stuff */
gp_point_conversion_init(C, &gsc);
- /* init autodist for geometry projection */
- if (mode == GP_REPROJECT_SURFACE) {
- view3d_region_operator_needs_opengl(CTX_wm_window(C), gsc.ar);
- ED_view3d_autodist_init(depsgraph, gsc.ar, CTX_wm_view3d(C), 0);
- }
-
- // TODO: For deforming geometry workflow, create new frames?
+ int cfra_prv = INT_MIN;
+ /* init snap context for geometry projection */
+ sctx = ED_transform_snap_object_context_create_view3d(
+ bmain, scene, depsgraph, 0, ar, CTX_wm_view3d(C));
/* Go through each editable + selected stroke, adjusting each of its points one by one... */
GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
if (gps->flag & GP_STROKE_SELECT) {
+
+ /* update frame to get the new location of objects */
+ if ((mode == GP_REPROJECT_SURFACE) && (cfra_prv != gpf_->framenum)) {
+ cfra_prv = gpf_->framenum;
+ CFRA = gpf_->framenum;
+ BKE_scene_graph_update_for_newframe(depsgraph, bmain);
+ }
+
bGPDspoint *pt;
int i;
/* Adjust each point */
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
float xy[2];
- /* 3D to Screenspace */
- /* Note: We can't use gp_point_to_xy() here because that uses ints for the screenspace
- * coordinates, resulting in lost precision, which in turn causes stairstepping
- * artifacts in the final points.
- */
+ /* 3D to Screen-space */
+ /* Note: We can't use gp_point_to_xy() here because that uses ints for the screen-space
+ * coordinates, resulting in lost precision, which in turn causes stair-stepping
+ * artifacts in the final points. */
+
bGPDspoint pt2;
gp_point_to_parent_space(pt, gpstroke_iter.diff_mat, &pt2);
gp_point_to_xy_fl(&gsc, gps, &pt2, &xy[0], &xy[1]);
@@ -3428,26 +3428,34 @@ static int gp_strokes_reproject_exec(bContext *C, wmOperator *op)
/* apply parent again */
gp_apply_parent_point(depsgraph, ob, gpd, gpl, pt);
}
- /* Project screenspace back to 3D space (from current perspective)
- * so that all points have been treated the same way
- */
+ /* Project screen-space back to 3D space (from current perspective)
+ * so that all points have been treated the same way. */
else if (mode == GP_REPROJECT_VIEW) {
- /* Planar - All on same plane parallel to the viewplane */
+ /* Planar - All on same plane parallel to the view-plane. */
gp_point_xy_to_3d(&gsc, scene, xy, &pt->x);
}
else {
/* Geometry - Snap to surfaces of visible geometry */
- /* XXX: There will be precision loss (possible stairstep artifacts)
- * from this conversion to satisfy the API's */
- const int screen_co[2] = {(int)xy[0], (int)xy[1]};
-
- int depth_margin = 0; // XXX: 4 for strokes, 0 for normal
- float depth;
-
- /* XXX: The proper procedure computes the depths into an array,
- * to have smooth transitions when all else fails... */
- if (ED_view3d_autodist_depth(gsc.ar, screen_co, depth_margin, &depth)) {
- ED_view3d_autodist_simple(gsc.ar, screen_co, &pt->x, 0, &depth);
+ float ray_start[3];
+ float ray_normal[3];
+ /* magic value for initial depth copied from the default
+ * value of Python's Scene.ray_cast function
+ */
+ float depth = 1.70141e+38f;
+ float location[3] = {0.0f, 0.0f, 0.0f};
+ float normal[3] = {0.0f, 0.0f, 0.0f};
+
+ ED_view3d_win_to_ray(ar, xy, &ray_start[0], &ray_normal[0]);
+ if (ED_transform_snap_object_project_ray(sctx,
+ &(const struct SnapObjectParams){
+ .snap_select = SNAP_ALL,
+ },
+ &ray_start[0],
+ &ray_normal[0],
+ &depth,
+ &location[0],
+ &normal[0])) {
+ copy_v3_v3(&pt->x, location);
}
else {
/* Default to planar */
@@ -3464,6 +3472,15 @@ static int gp_strokes_reproject_exec(bContext *C, wmOperator *op)
}
GP_EDITABLE_STROKES_END(gpstroke_iter);
+ /* return frame state and DB to original state */
+ CFRA = oldframe;
+ BKE_scene_graph_update_for_newframe(depsgraph, bmain);
+
+ if (sctx != NULL) {
+ ED_transform_snap_object_context_destroy(sctx);
+ }
+
+ /* update changed data */
DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
return OPERATOR_FINISHED;
@@ -3549,7 +3566,7 @@ static void gp_smooth_stroke(bContext *C, wmOperator *op)
}
if (smooth_thickness) {
/* thickness need to repeat process several times */
- for (int r2 = 0; r2 < r * 10; r2++) {
+ for (int r2 = 0; r2 < r * 20; r2++) {
BKE_gpencil_smooth_stroke_thickness(gps, i, factor);
}
}
@@ -4055,7 +4072,7 @@ static int gp_stroke_separate_exec(bContext *C, wmOperator *op)
/* add duplicate materials */
/* XXX same material can be in multiple slots. */
- ma = give_current_material(ob, gps->mat_nr + 1);
+ ma = BKE_material_gpencil_get(ob, gps->mat_nr + 1);
idx = BKE_gpencil_object_material_ensure(bmain, ob_dst, ma);
@@ -4128,7 +4145,7 @@ static int gp_stroke_separate_exec(bContext *C, wmOperator *op)
if (ED_gpencil_stroke_can_use(C, gps) == false) {
continue;
}
- ma = give_current_material(ob, gps->mat_nr + 1);
+ ma = BKE_material_gpencil_get(ob, gps->mat_nr + 1);
gps->mat_nr = BKE_gpencil_object_material_ensure(bmain, ob_dst, ma);
}
}
@@ -4305,7 +4322,7 @@ void GPENCIL_OT_stroke_smooth(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
- prop = RNA_def_int(ot->srna, "repeat", 1, 1, 10, "Repeat", "", 1, 5);
+ prop = RNA_def_int(ot->srna, "repeat", 1, 1, 50, "Repeat", "", 1, 20);
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
RNA_def_float(ot->srna, "factor", 0.5f, 0.0f, 2.0f, "Factor", "", 0.0f, 2.0f);
@@ -4441,7 +4458,7 @@ static int gpencil_cutter_lasso_select(bContext *C,
if ((pt->flag & GP_SPOINT_SELECT) || (pt->flag & GP_SPOINT_TAG)) {
continue;
}
- /* convert point coords to screenspace */
+ /* convert point coords to screen-space */
const bool is_inside = is_inside_fn(gps, pt, &gsc, gpstroke_iter.diff_mat, user_data);
if (is_inside) {
tot_inside++;
diff --git a/source/blender/editors/gpencil/gpencil_fill.c b/source/blender/editors/gpencil/gpencil_fill.c
index 5637e755198..372973e1040 100644
--- a/source/blender/editors/gpencil/gpencil_fill.c
+++ b/source/blender/editors/gpencil/gpencil_fill.c
@@ -48,6 +48,7 @@
#include "BKE_context.h"
#include "BKE_screen.h"
#include "BKE_paint.h"
+#include "BKE_report.h"
#include "ED_gpencil.h"
#include "ED_screen.h"
@@ -1038,7 +1039,15 @@ static void gpencil_stroke_from_buffer(tGPDfill *tgpf)
gps->flag |= GP_STROKE_CYCLIC;
gps->flag |= GP_STROKE_3DSPACE;
- gps->mat_nr = BKE_gpencil_object_material_ensure(tgpf->bmain, tgpf->ob, tgpf->mat);
+ gps->mat_nr = BKE_gpencil_object_material_get_index_from_brush(tgpf->ob, brush);
+ if (gps->mat_nr < 0) {
+ if (tgpf->ob->actcol - 1 < 0) {
+ gps->mat_nr = 0;
+ }
+ else {
+ gps->mat_nr = tgpf->ob->actcol - 1;
+ }
+ }
/* allocate memory for storage points */
gps->totpoints = tgpf->sbuffer_used;
@@ -1346,8 +1355,28 @@ static int gpencil_fill_init(bContext *C, wmOperator *op)
/* start of interactive part of operator */
static int gpencil_fill_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
+ Object *ob = CTX_data_active_object(C);
+ ToolSettings *ts = CTX_data_tool_settings(C);
+ Brush *brush = BKE_paint_brush(&ts->gp_paint->paint);
tGPDfill *tgpf = NULL;
+ /* Fill tool needs a material (cannot use default material) */
+ bool valid = true;
+ if ((brush) && (brush->gpencil_settings->flag & GP_BRUSH_MATERIAL_PINNED)) {
+ if (brush->gpencil_settings->material == NULL) {
+ valid = false;
+ }
+ }
+ else {
+ if (give_current_material(ob, ob->actcol) == NULL) {
+ valid = false;
+ }
+ }
+ if (!valid) {
+ BKE_report(op->reports, RPT_ERROR, "Fill tool needs active material.");
+ return OPERATOR_CANCELLED;
+ }
+
/* try to initialize context data needed */
if (!gpencil_fill_init(C, op)) {
gpencil_fill_exit(C, op);
diff --git a/source/blender/editors/gpencil/gpencil_intern.h b/source/blender/editors/gpencil/gpencil_intern.h
index 7f36eb50802..f7fc7e34460 100644
--- a/source/blender/editors/gpencil/gpencil_intern.h
+++ b/source/blender/editors/gpencil/gpencil_intern.h
@@ -637,8 +637,12 @@ struct GP_EditableStrokes_Iter {
} \
(void)0
+#define GPENCIL_ANY_SCULPT_MASK(flag) \
+ ((flag & (GP_SCULPT_MASK_SELECTMODE_POINT | GP_SCULPT_MASK_SELECTMODE_STROKE | \
+ GP_SCULPT_MASK_SELECTMODE_SEGMENT)))
+
/**
- * Iterate over all editable strokes using derived data in the current context,
+ * Iterate over all editable strokes using evaluated data in the current context,
* stopping on each usable layer + stroke pair (i.e. gpl and gps)
* to perform some operations on the stroke.
*
@@ -647,15 +651,15 @@ struct GP_EditableStrokes_Iter {
* \param gps: The identifier to use for current stroke being processed.
* Choose a suitable value to avoid name clashes.
*/
-#define GP_DERIVED_STROKES_BEGIN(gpstroke_iter, C, gpl, gps) \
+#define GP_EVALUATED_STROKES_BEGIN(gpstroke_iter, C, gpl, gps) \
{ \
struct GP_EditableStrokes_Iter gpstroke_iter = {{{0}}}; \
- Depsgraph *depsgraph_ = CTX_data_depsgraph_pointer(C); \
+ Depsgraph *depsgraph_ = CTX_data_ensure_evaluated_depsgraph(C); \
Object *obact_ = CTX_data_active_object(C); \
Object *obeval_ = DEG_get_evaluated_object(depsgraph_, obact_); \
bGPdata *gpd_ = CTX_data_gpencil_data(C); \
const bool is_multiedit_ = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd_); \
- int derived_idx = 0; \
+ int idx_eval = 0; \
for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { \
if (gpencil_layer_is_editable(gpl)) { \
bGPDframe *init_gpf_ = gpl->actframe; \
@@ -665,10 +669,11 @@ struct GP_EditableStrokes_Iter {
for (bGPDframe *gpf_ = init_gpf_; gpf_; gpf_ = gpf_->next) { \
if ((gpf_ == gpl->actframe) || ((gpf_->flag & GP_FRAME_SELECT) && is_multiedit_)) { \
ED_gpencil_parent_location(depsgraph_, obact_, gpd_, gpl, gpstroke_iter.diff_mat); \
- /* get derived frame with modifiers applied */ \
- bGPDframe *derived_gpf_ = &obeval_->runtime.derived_frames[derived_idx]; \
+ invert_m4_m4(gpstroke_iter.inverse_diff_mat, gpstroke_iter.diff_mat); \
+ /* get evaluated frame with modifiers applied */ \
+ bGPDframe *gpf_eval_ = &obeval_->runtime.gpencil_evaluated_frames[idx_eval]; \
/* loop over strokes */ \
- for (bGPDstroke *gps = derived_gpf_->strokes.first; gps; gps = gps->next) { \
+ for (bGPDstroke *gps = gpf_eval_->strokes.first; gps; gps = gps->next) { \
/* skip strokes that are invalid for current view */ \
if (ED_gpencil_stroke_can_use(C, gps) == false) \
continue; \
@@ -677,7 +682,7 @@ struct GP_EditableStrokes_Iter {
continue; \
/* ... Do Stuff With Strokes ... */
-#define GP_DERIVED_STROKES_END(gpstroke_iter) \
+#define GP_EVALUATED_STROKES_END(gpstroke_iter) \
} \
} \
if (!is_multiedit_) { \
@@ -685,7 +690,7 @@ struct GP_EditableStrokes_Iter {
} \
} \
} \
- derived_idx++; \
+ idx_eval++; \
} \
} \
(void)0
diff --git a/source/blender/editors/gpencil/gpencil_merge.c b/source/blender/editors/gpencil/gpencil_merge.c
index cb11bb4cd63..930911ffac5 100644
--- a/source/blender/editors/gpencil/gpencil_merge.c
+++ b/source/blender/editors/gpencil/gpencil_merge.c
@@ -449,7 +449,7 @@ static bool gp_strokes_merge_poll(bContext *C)
/* check material */
Material *ma = NULL;
- ma = give_current_material(ob, ob->actcol);
+ ma = BKE_material_gpencil_get(ob, ob->actcol);
if ((ma == NULL) || (ma->gp_style == NULL)) {
return false;
}
diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c
index a5425d64c2e..3217c94eebd 100644
--- a/source/blender/editors/gpencil/gpencil_paint.c
+++ b/source/blender/editors/gpencil/gpencil_paint.c
@@ -423,7 +423,7 @@ static void gp_stroke_convertcoords(tGPsdata *p, const float mval[2], float out[
/* add small offset to keep stroke over the surface */
if ((depth) && (gpd->zdepth_offset > 0.0f) && (*p->align_flag & GP_PROJECT_DEPTH_VIEW)) {
- *depth *= (1.0f - gpd->zdepth_offset);
+ *depth *= (1.0f - (gpd->zdepth_offset / 1000.0f));
}
int mval_i[2];
@@ -563,7 +563,7 @@ static void gp_brush_angle(bGPdata *gpd, Brush *brush, tGPspoint *pt, const floa
static void gp_smooth_buffer(tGPsdata *p, float inf, int idx)
{
bGPdata *gpd = p->gpd;
- short num_points = gpd->runtime.sbuffer_used;
+ const short num_points = gpd->runtime.sbuffer_used;
/* Do nothing if not enough points to smooth out */
if ((num_points < 3) || (idx < 3) || (inf == 0.0f)) {
@@ -571,10 +571,7 @@ static void gp_smooth_buffer(tGPsdata *p, float inf, int idx)
}
tGPspoint *points = (tGPspoint *)gpd->runtime.sbuffer;
- float steps = 4.0f;
- if (idx < 4) {
- steps--;
- }
+ const float steps = (idx < 4) ? 3.0f : 4.0f;
tGPspoint *pta = idx >= 4 ? &points[idx - 4] : NULL;
tGPspoint *ptb = idx >= 3 ? &points[idx - 3] : NULL;
@@ -583,29 +580,36 @@ static void gp_smooth_buffer(tGPsdata *p, float inf, int idx)
float sco[2] = {0.0f};
float a[2], b[2], c[2], d[2];
+ float pressure = 0.0f;
const float average_fac = 1.0f / steps;
/* Compute smoothed coordinate by taking the ones nearby */
if (pta) {
copy_v2_v2(a, &pta->x);
madd_v2_v2fl(sco, a, average_fac);
+ pressure += pta->pressure * average_fac;
}
if (ptb) {
copy_v2_v2(b, &ptb->x);
madd_v2_v2fl(sco, b, average_fac);
+ pressure += ptb->pressure * average_fac;
}
if (ptc) {
copy_v2_v2(c, &ptc->x);
madd_v2_v2fl(sco, c, average_fac);
+ pressure += ptc->pressure * average_fac;
}
if (ptd) {
copy_v2_v2(d, &ptd->x);
madd_v2_v2fl(sco, d, average_fac);
+ pressure += ptd->pressure * average_fac;
}
- /* Based on influence factor, blend between original and optimal smoothed coordinate */
+ /* Based on influence factor, blend between original and optimal smoothed coordinate. */
interp_v2_v2v2(c, c, sco, inf);
copy_v2_v2(&ptc->x, c);
+ /* Interpolate pressure. */
+ ptc->pressure = interpf(ptc->pressure, pressure, inf);
}
/* add current stroke-point to buffer (returns whether point was successfully added) */
@@ -1240,6 +1244,14 @@ static void gp_stroke_newfrombuffer(tGPsdata *p)
/* Save material index */
gps->mat_nr = BKE_gpencil_object_material_get_index_from_brush(p->ob, p->brush);
+ if (gps->mat_nr < 0) {
+ if (p->ob->actcol - 1 < 0) {
+ gps->mat_nr = 0;
+ }
+ else {
+ gps->mat_nr = p->ob->actcol - 1;
+ }
+ }
/* calculate UVs along the stroke */
ED_gpencil_calc_stroke_uv(obact, gps);
@@ -2128,15 +2140,6 @@ static void gp_paint_initstroke(tGPsdata *p, eGPencil_PaintModes paintmode, Deps
/* Ensure this gets set... */
p->gpf = p->gpl->actframe;
- /* Restrict eraser to only affecting selected strokes, if the "selection mask" is on
- * (though this is only available in editmode)
- */
- if (p->gpd->flag & GP_DATA_STROKE_EDITMODE) {
- if (ts->gp_sculpt.flag & GP_SCULPT_SETT_FLAG_SELECT_MASK) {
- p->flags |= GP_PAINTFLAG_SELECTMASK;
- }
- }
-
if (has_layer_to_erase == false) {
p->status = GP_STATUS_ERROR;
return;
diff --git a/source/blender/editors/gpencil/gpencil_primitive.c b/source/blender/editors/gpencil/gpencil_primitive.c
index 25913fe821a..8d4c75d2e8c 100644
--- a/source/blender/editors/gpencil/gpencil_primitive.c
+++ b/source/blender/editors/gpencil/gpencil_primitive.c
@@ -330,7 +330,15 @@ static void gp_primitive_set_initdata(bContext *C, tGPDprimitive *tgpi)
gps->flag |= GP_STROKE_3DSPACE;
- gps->mat_nr = BKE_gpencil_object_material_get_index(tgpi->ob, tgpi->mat);
+ gps->mat_nr = BKE_gpencil_object_material_get_index_from_brush(tgpi->ob, tgpi->brush);
+ if (gps->mat_nr < 0) {
+ if (tgpi->ob->actcol - 1 < 0) {
+ gps->mat_nr = 0;
+ }
+ else {
+ gps->mat_nr = tgpi->ob->actcol - 1;
+ }
+ }
/* allocate memory for storage points, but keep empty */
gps->totpoints = 0;
@@ -959,7 +967,7 @@ static void gp_primitive_update_strokes(bContext *C, tGPDprimitive *tgpi)
/* add small offset to keep stroke over the surface */
if ((depth_arr) && (gpd->zdepth_offset > 0.0f)) {
- depth_arr[i] *= (1.0f - gpd->zdepth_offset);
+ depth_arr[i] *= (1.0f - (gpd->zdepth_offset / 1000.0f));
}
/* convert screen-coordinates to 3D coordinates */
diff --git a/source/blender/editors/gpencil/gpencil_select.c b/source/blender/editors/gpencil/gpencil_select.c
index 01a2ac757d6..f4484624d5a 100644
--- a/source/blender/editors/gpencil/gpencil_select.c
+++ b/source/blender/editors/gpencil/gpencil_select.c
@@ -67,13 +67,30 @@
/** \name Shared Utilities
* \{ */
+/* Convert sculpt mask mode to Select mode */
+static int gpencil_select_mode_from_sculpt(eGP_Sculpt_SelectMaskFlag mode)
+{
+ if (mode & GP_SCULPT_MASK_SELECTMODE_POINT) {
+ return GP_SELECTMODE_POINT;
+ }
+ else if (mode & GP_SCULPT_MASK_SELECTMODE_STROKE) {
+ return GP_SELECTMODE_STROKE;
+ }
+ else if (mode & GP_SCULPT_MASK_SELECTMODE_SEGMENT) {
+ return GP_SELECTMODE_SEGMENT;
+ }
+ else {
+ return GP_SELECTMODE_POINT;
+ }
+}
+
static bool gpencil_select_poll(bContext *C)
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
if (GPENCIL_SCULPT_MODE(gpd)) {
ToolSettings *ts = CTX_data_tool_settings(C);
- if ((ts->gp_sculpt.flag & GP_SCULPT_SETT_FLAG_SELECT_MASK) == 0) {
+ if (!(GPENCIL_ANY_SCULPT_MASK(ts->gpencil_selectmode_sculpt))) {
return false;
}
}
@@ -94,6 +111,19 @@ static bool gpencil_select_poll(bContext *C)
/* -------------------------------------------------------------------- */
/** \name Select All Operator
* \{ */
+static bool gpencil_select_all_poll(bContext *C)
+{
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+
+ /* we just need some visible strokes, and to be in editmode or other modes only to catch event */
+ if (GPENCIL_ANY_MODE(gpd)) {
+ if (gpd->layers.first) {
+ return true;
+ }
+ }
+
+ return false;
+}
static int gpencil_select_all_exec(bContext *C, wmOperator *op)
{
@@ -110,6 +140,14 @@ static int gpencil_select_all_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
+ /* For sculpt mode, if mask is disable, only allows deselect */
+ if (GPENCIL_SCULPT_MODE(gpd)) {
+ ToolSettings *ts = CTX_data_tool_settings(C);
+ if ((!(GPENCIL_ANY_SCULPT_MASK(ts->gpencil_selectmode_sculpt))) && (action != SEL_DESELECT)) {
+ return OPERATOR_CANCELLED;
+ }
+ }
+
ED_gpencil_select_toggle_all(C, action);
/* updates */
@@ -132,7 +170,7 @@ void GPENCIL_OT_select_all(wmOperatorType *ot)
/* callbacks */
ot->exec = gpencil_select_all_exec;
- ot->poll = gpencil_select_poll;
+ ot->poll = gpencil_select_all_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -928,7 +966,11 @@ static int gpencil_circle_select_exec(bContext *C, wmOperator *op)
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
ToolSettings *ts = CTX_data_tool_settings(C);
- const int selectmode = ts->gpencil_selectmode;
+ Object *ob = CTX_data_active_object(C);
+
+ const int selectmode = (ob && ob->mode == OB_MODE_SCULPT_GPENCIL) ?
+ gpencil_select_mode_from_sculpt(ts->gpencil_selectmode_sculpt) :
+ ts->gpencil_selectmode_edit;
const float scale = ts->gp_sculpt.isect_threshold;
/* if not edit/sculpt mode, the event is catched but not processed */
@@ -972,12 +1014,12 @@ static int gpencil_circle_select_exec(bContext *C, wmOperator *op)
rect.ymax = my + radius;
/* find visible strokes, and select if hit */
- GP_DERIVED_STROKES_BEGIN(gpstroke_iter, C, gpl, gps)
+ GP_EVALUATED_STROKES_BEGIN(gpstroke_iter, C, gpl, gps)
{
changed |= gp_stroke_do_circle_sel(
gpl, gps, &gsc, mx, my, radius, select, &rect, gpstroke_iter.diff_mat, selectmode, scale);
}
- GP_DERIVED_STROKES_END(gpstroke_iter);
+ GP_EVALUATED_STROKES_END(gpstroke_iter);
/* updates */
if (changed) {
@@ -1035,12 +1077,18 @@ static int gpencil_generic_select_exec(bContext *C,
GPencilTestFn is_inside_fn,
void *user_data)
{
+ Object *ob = CTX_data_active_object(C);
bGPdata *gpd = ED_gpencil_data_get_active(C);
ToolSettings *ts = CTX_data_tool_settings(C);
ScrArea *sa = CTX_wm_area(C);
- const bool strokemode = ((ts->gpencil_selectmode == GP_SELECTMODE_STROKE) &&
+
+ const short selectmode = (ob && ob->mode == OB_MODE_SCULPT_GPENCIL) ?
+ gpencil_select_mode_from_sculpt(ts->gpencil_selectmode_sculpt) :
+ ts->gpencil_selectmode_edit;
+
+ const bool strokemode = ((selectmode == GP_SELECTMODE_STROKE) &&
((gpd->flag & GP_DATA_STROKE_PAINTMODE) == 0));
- const bool segmentmode = ((ts->gpencil_selectmode == GP_SELECTMODE_SEGMENT) &&
+ const bool segmentmode = ((selectmode == GP_SELECTMODE_SEGMENT) &&
((gpd->flag & GP_DATA_STROKE_PAINTMODE) == 0));
const eSelectOp sel_op = RNA_enum_get(op->ptr, "mode");
const float scale = ts->gp_sculpt.isect_threshold;
@@ -1075,7 +1123,7 @@ static int gpencil_generic_select_exec(bContext *C,
}
/* select/deselect points */
- GP_DERIVED_STROKES_BEGIN(gpstroke_iter, C, gpl, gps)
+ GP_EVALUATED_STROKES_BEGIN(gpstroke_iter, C, gpl, gps)
{
bGPDspoint *pt;
@@ -1088,7 +1136,6 @@ static int gpencil_generic_select_exec(bContext *C,
/* convert point coords to screenspace */
const bool is_inside = is_inside_fn(gps, pt, &gsc, gpstroke_iter.diff_mat, user_data);
-
if (strokemode == false) {
const bool is_select = (pt->runtime.pt_orig->flag & GP_SPOINT_SELECT) != 0;
const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
@@ -1143,7 +1190,7 @@ static int gpencil_generic_select_exec(bContext *C,
/* Ensure that stroke selection is in sync with its points */
BKE_gpencil_stroke_sync_selection(gps->runtime.gps_orig);
}
- GP_DERIVED_STROKES_END(gpstroke_iter);
+ GP_EVALUATED_STROKES_END(gpstroke_iter);
/* if paint mode,delete selected points */
if (gpd->flag & GP_DATA_STROKE_PAINTMODE) {
@@ -1318,6 +1365,7 @@ static void deselect_all_selected(bContext *C)
static int gpencil_select_exec(bContext *C, wmOperator *op)
{
ScrArea *sa = CTX_wm_area(C);
+ Object *ob = CTX_data_active_object(C);
bGPdata *gpd = ED_gpencil_data_get_active(C);
ToolSettings *ts = CTX_data_tool_settings(C);
const float scale = ts->gp_sculpt.isect_threshold;
@@ -1348,8 +1396,12 @@ static int gpencil_select_exec(bContext *C, wmOperator *op)
}
/* if select mode is stroke, use whole stroke */
- if (ts->gpencil_selectmode == GP_SELECTMODE_STROKE) {
- whole = true;
+ if ((ob) && (ob->mode == OB_MODE_SCULPT_GPENCIL)) {
+ whole = (bool)(gpencil_select_mode_from_sculpt(ts->gpencil_selectmode_sculpt) ==
+ GP_SELECTMODE_STROKE);
+ }
+ else {
+ whole = (bool)(ts->gpencil_selectmode_edit == GP_SELECTMODE_STROKE);
}
/* init space conversion stuff */
@@ -1360,7 +1412,7 @@ static int gpencil_select_exec(bContext *C, wmOperator *op)
/* First Pass: Find stroke point which gets hit */
/* XXX: maybe we should go from the top of the stack down instead... */
- GP_DERIVED_STROKES_BEGIN(gpstroke_iter, C, gpl, gps)
+ GP_EVALUATED_STROKES_BEGIN(gpstroke_iter, C, gpl, gps)
{
bGPDspoint *pt;
int i;
@@ -1393,7 +1445,7 @@ static int gpencil_select_exec(bContext *C, wmOperator *op)
}
}
}
- GP_DERIVED_STROKES_END(gpstroke_iter);
+ GP_EVALUATED_STROKES_END(gpstroke_iter);
/* Abort if nothing hit... */
if (ELEM(NULL, hit_stroke, hit_point)) {
@@ -1454,7 +1506,11 @@ static int gpencil_select_exec(bContext *C, wmOperator *op)
hit_stroke->flag |= GP_STROKE_SELECT;
/* expand selection to segment */
- if (ts->gpencil_selectmode == GP_SELECTMODE_SEGMENT) {
+ const short selectmode = (ob && ob->mode == OB_MODE_SCULPT_GPENCIL) ?
+ gpencil_select_mode_from_sculpt(ts->gpencil_selectmode_sculpt) :
+ ts->gpencil_selectmode_edit;
+
+ if (selectmode == GP_SELECTMODE_SEGMENT) {
float r_hita[3], r_hitb[3];
bool hit_select = (bool)(hit_point->flag & GP_SPOINT_SELECT);
ED_gpencil_select_stroke_segment(
diff --git a/source/blender/editors/gpencil/gpencil_utils.c b/source/blender/editors/gpencil/gpencil_utils.c
index 2a0f16a4bbf..d7492b5be4d 100644
--- a/source/blender/editors/gpencil/gpencil_utils.c
+++ b/source/blender/editors/gpencil/gpencil_utils.c
@@ -401,17 +401,7 @@ const EnumPropertyItem *ED_gpencil_layers_with_new_enum_itemf(bContext *C,
/* Create new layer */
/* TODO: have some way of specifying that we don't want this? */
- {
- /* "New Layer" entry */
- item_tmp.identifier = "__CREATE__";
- item_tmp.name = "New Layer";
- item_tmp.value = -1;
- item_tmp.icon = ICON_ADD;
- RNA_enum_item_add(&item, &totitem, &item_tmp);
- /* separator */
- RNA_enum_item_add_separator(&item, &totitem);
- }
const int tot = BLI_listbase_count(&gpd->layers);
/* Existing layers */
for (gpl = gpd->layers.last, i = 0; gpl; gpl = gpl->prev, i++) {
@@ -428,6 +418,17 @@ const EnumPropertyItem *ED_gpencil_layers_with_new_enum_itemf(bContext *C,
RNA_enum_item_add(&item, &totitem, &item_tmp);
}
+ {
+ /* separator */
+ RNA_enum_item_add_separator(&item, &totitem);
+
+ /* "New Layer" entry */
+ item_tmp.identifier = "__CREATE__";
+ item_tmp.name = "New Layer";
+ item_tmp.value = -1;
+ item_tmp.icon = ICON_ADD;
+ RNA_enum_item_add(&item, &totitem, &item_tmp);
+ }
RNA_enum_item_end(&item, &totitem);
*r_free = true;
@@ -2063,7 +2064,7 @@ void ED_gpencil_update_color_uv(Main *bmain, Material *mat)
if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false) {
continue;
}
- gps_ma = give_current_material(ob, gps->mat_nr + 1);
+ gps_ma = BKE_material_gpencil_get(ob, gps->mat_nr + 1);
/* update */
if ((gps_ma) && (gps_ma == mat)) {
ED_gpencil_calc_stroke_uv(ob, gps);
diff --git a/source/blender/editors/include/BIF_glutil.h b/source/blender/editors/include/BIF_glutil.h
index 101a65d151a..a49f02d28fe 100644
--- a/source/blender/editors/include/BIF_glutil.h
+++ b/source/blender/editors/include/BIF_glutil.h
@@ -24,7 +24,6 @@
#ifndef __BIF_GLUTIL_H__
#define __BIF_GLUTIL_H__
-struct rctf;
struct rcti;
struct ColorManagedDisplaySettings;
diff --git a/source/blender/editors/include/ED_armature.h b/source/blender/editors/include/ED_armature.h
index 6629eed8328..48d0a5fe8be 100644
--- a/source/blender/editors/include/ED_armature.h
+++ b/source/blender/editors/include/ED_armature.h
@@ -42,7 +42,6 @@ struct ReportList;
struct Scene;
struct UndoType;
struct View3D;
-struct ViewContext;
struct ViewLayer;
struct bArmature;
struct bContext;
@@ -75,12 +74,14 @@ typedef struct EditBone {
int flag;
int layer;
+ /* Envelope distance & weight */
float dist, weight;
/** put them in order! transform uses this as scale */
float xwidth, length, zwidth;
float rad_head, rad_tail;
/* Bendy-Bone parameters */
+ short segments;
float roll1, roll2;
float curve_in_x, curve_in_y;
float curve_out_x, curve_out_y;
@@ -91,8 +92,6 @@ typedef struct EditBone {
/** for envelope scaling */
float oldlength;
- short segments;
-
/** Type of next/prev bone handles */
char bbone_prev_type;
char bbone_next_type;
@@ -229,15 +228,9 @@ void ED_armature_edit_transform_mirror_update(struct Object *obedit);
void ED_armature_origin_set(
struct Main *bmain, struct Object *ob, const float cursor[3], int centermode, int around);
-void ED_armature_transform_bones(struct bArmature *arm, float mat[4][4], const bool do_props);
-void ED_armature_transform_apply(struct Main *bmain,
- struct Object *ob,
- float mat[4][4],
- const bool do_props);
-void ED_armature_transform(struct Main *bmain,
- struct bArmature *arm,
- float mat[4][4],
- const bool do_props);
+void ED_armature_edit_transform(struct bArmature *arm, const float mat[4][4], const bool do_props);
+
+void ED_armature_transform(struct bArmature *arm, const float mat[4][4], const bool do_props);
#define ARM_GROUPS_NAME 1
#define ARM_GROUPS_ENVELOPE 2
diff --git a/source/blender/editors/include/ED_clip.h b/source/blender/editors/include/ED_clip.h
index 7cee5a358f4..22bf22e04d3 100644
--- a/source/blender/editors/include/ED_clip.h
+++ b/source/blender/editors/include/ED_clip.h
@@ -29,7 +29,6 @@ struct ImBuf;
struct Main;
struct Mask;
struct MovieClip;
-struct Scene;
struct SpaceClip;
struct bContext;
struct bScreen;
diff --git a/source/blender/editors/include/ED_fileselect.h b/source/blender/editors/include/ED_fileselect.h
index 7273f857a41..dc42237d804 100644
--- a/source/blender/editors/include/ED_fileselect.h
+++ b/source/blender/editors/include/ED_fileselect.h
@@ -34,17 +34,32 @@ struct wmWindowManager;
#define FILE_LAYOUT_HOR 1
#define FILE_LAYOUT_VER 2
-#define MAX_FILE_COLUMN 4
-
-typedef enum FileListColumns {
+typedef enum FileAttributeColumnType {
+ COLUMN_NONE = -1,
COLUMN_NAME = 0,
- COLUMN_DATE,
- COLUMN_TIME,
+ COLUMN_DATETIME,
COLUMN_SIZE,
-} FileListColumns;
+
+ ATTRIBUTE_COLUMN_MAX
+} FileAttributeColumnType;
+
+typedef struct FileAttributeColumn {
+ /** UI name for this column */
+ const char *name;
+
+ float width;
+ /* The sort type to use when sorting by this column. */
+ int sort_type; /* eFileSortType */
+
+ /* Alignment of column texts, header text is always left aligned */
+ int text_align; /* eFontStyle_Align */
+} FileAttributeColumn;
typedef struct FileLayout {
/* view settings - XXX - move into own struct */
+ int offset_top;
+ /* Height of the header for the different FileAttributeColumn's. */
+ int attribute_column_header_h;
int prv_w;
int prv_h;
int tile_w;
@@ -54,13 +69,17 @@ typedef struct FileLayout {
int prv_border_x;
int prv_border_y;
int rows;
- int columns;
+ /* Those are the major layout columns the files are distributed across, not to be confused with
+ * 'attribute_columns' array below. */
+ int flow_columns;
int width;
int height;
int flag;
int dirty;
int textheight;
- float column_widths[MAX_FILE_COLUMN];
+ /* The columns for each item (name, modification date/time, size). Not to be confused with the
+ * 'flow_columns' above. */
+ FileAttributeColumn attribute_columns[ATTRIBUTE_COLUMN_MAX];
/* When we change display size, we may have to update static strings like size of files... */
short curr_size;
@@ -72,6 +91,7 @@ typedef struct FileSelection {
} FileSelection;
struct rcti;
+struct View2D;
struct FileSelectParams *ED_fileselect_get_params(struct SpaceFile *sfile);
@@ -87,6 +107,17 @@ int ED_fileselect_layout_numfiles(FileLayout *layout, struct ARegion *ar);
int ED_fileselect_layout_offset(FileLayout *layout, int x, int y);
FileSelection ED_fileselect_layout_offset_rect(FileLayout *layout, const struct rcti *rect);
+void ED_fileselect_layout_maskrect(const FileLayout *layout,
+ const struct View2D *v2d,
+ struct rcti *r_rect);
+bool ED_fileselect_layout_is_inside_pt(const FileLayout *layout,
+ const struct View2D *v2d,
+ int x,
+ int y);
+bool ED_fileselect_layout_isect_rect(const FileLayout *layout,
+ const struct View2D *v2d,
+ const struct rcti *rect,
+ struct rcti *r_dst);
void ED_fileselect_layout_tilepos(FileLayout *layout, int tile, int *x, int *y);
void ED_operatormacros_file(void);
diff --git a/source/blender/editors/include/ED_gizmo_library.h b/source/blender/editors/include/ED_gizmo_library.h
index f14648bc026..a3e2e643509 100644
--- a/source/blender/editors/include/ED_gizmo_library.h
+++ b/source/blender/editors/include/ED_gizmo_library.h
@@ -43,10 +43,8 @@ void ED_gizmotypes_value_2d(void);
void ED_gizmogrouptypes_value_2d(void);
struct Object;
-struct Scene;
struct bContext;
struct wmGizmo;
-struct wmGizmoGroup;
/* -------------------------------------------------------------------- */
/* Shape Presets
diff --git a/source/blender/editors/include/ED_gpencil.h b/source/blender/editors/include/ED_gpencil.h
index 6410e6630a7..470eb58c72b 100644
--- a/source/blender/editors/include/ED_gpencil.h
+++ b/source/blender/editors/include/ED_gpencil.h
@@ -27,7 +27,6 @@
struct ID;
struct ListBase;
struct PointerRNA;
-struct rcti;
struct Brush;
struct bGPDframe;
@@ -39,13 +38,11 @@ struct tGPspoint;
struct ARegion;
struct Depsgraph;
-struct EvaluationContext;
struct Main;
struct RegionView3D;
struct ReportList;
struct Scene;
struct ScrArea;
-struct ToolSettings;
struct View3D;
struct ViewLayer;
struct bContext;
@@ -58,8 +55,6 @@ struct bAnimContext;
struct wmKeyConfig;
struct wmOperator;
-struct wmWindow;
-struct wmWindowManager;
/* ------------- Grease-Pencil Runtime Data ---------------- */
diff --git a/source/blender/editors/include/ED_image.h b/source/blender/editors/include/ED_image.h
index a09e1d579fd..7d69f86dbf8 100644
--- a/source/blender/editors/include/ED_image.h
+++ b/source/blender/editors/include/ED_image.h
@@ -31,7 +31,6 @@ struct ImageUser;
struct ReportList;
struct Scene;
struct SpaceImage;
-struct ToolSettings;
struct ViewLayer;
struct bContext;
struct wmWindowManager;
diff --git a/source/blender/editors/include/ED_keyframes_draw.h b/source/blender/editors/include/ED_keyframes_draw.h
index 05f641abe91..2630feaa9d3 100644
--- a/source/blender/editors/include/ED_keyframes_draw.h
+++ b/source/blender/editors/include/ED_keyframes_draw.h
@@ -31,7 +31,6 @@ struct FCurve;
struct ListBase;
struct MaskLayer;
struct Object;
-struct Palette;
struct Scene;
struct View2D;
struct bAction;
diff --git a/source/blender/editors/include/ED_keyframing.h b/source/blender/editors/include/ED_keyframing.h
index bbeeeade822..42e5add2ef0 100644
--- a/source/blender/editors/include/ED_keyframing.h
+++ b/source/blender/editors/include/ED_keyframing.h
@@ -44,8 +44,6 @@ struct bPoseChannel;
struct ReportList;
struct bContext;
-struct Depsgraph;
-
struct EnumPropertyItem;
struct PointerRNA;
struct PropertyRNA;
diff --git a/source/blender/editors/include/ED_markers.h b/source/blender/editors/include/ED_markers.h
index a883187327f..a9e57ecc13d 100644
--- a/source/blender/editors/include/ED_markers.h
+++ b/source/blender/editors/include/ED_markers.h
@@ -29,7 +29,6 @@ struct TimeMarker;
struct bAnimContext;
struct bContext;
struct wmKeyConfig;
-struct wmKeyMap;
/* Drawing API ------------------------------ */
diff --git a/source/blender/editors/include/ED_mesh.h b/source/blender/editors/include/ED_mesh.h
index 5ebba4e5d25..ee4798430ef 100644
--- a/source/blender/editors/include/ED_mesh.h
+++ b/source/blender/editors/include/ED_mesh.h
@@ -45,7 +45,6 @@ struct Mesh;
struct Object;
struct ReportList;
struct Scene;
-struct ToolSettings;
struct UndoType;
struct UvMapVert;
struct UvMapVert;
@@ -55,7 +54,6 @@ struct View3D;
struct ViewContext;
struct bContext;
struct bDeformGroup;
-struct rcti;
struct wmKeyConfig;
struct wmOperator;
@@ -105,7 +103,9 @@ bool EDBM_vert_color_check(struct BMEditMesh *em);
bool EDBM_mesh_hide(struct BMEditMesh *em, bool swap);
bool EDBM_mesh_reveal(struct BMEditMesh *em, bool select);
-void EDBM_update_generic(struct BMEditMesh *em, const bool do_tessface, const bool is_destructive);
+void EDBM_update_generic(struct BMEditMesh *em,
+ const bool do_tessellation,
+ const bool is_destructive);
struct UvElementMap *BM_uv_element_map_create(struct BMesh *bm,
const bool selected,
@@ -138,13 +138,21 @@ bool BMBVH_EdgeVisible(struct BMBVHTree *tree,
struct View3D *v3d,
struct Object *obedit);
+/* editmesh_automerge.c */
+void EDBM_automerge(struct Object *ob, bool update, const char hflag, const float dist);
+void EDBM_automerge_and_split(struct Object *ob,
+ bool split_edges,
+ bool split_faces,
+ bool update,
+ const char hflag,
+ const float dist);
+
/* editmesh_undo.c */
void ED_mesh_undosys_type(struct UndoType *ut);
/* editmesh_select.c */
void EDBM_select_mirrored(
struct BMEditMesh *em, const int axis, const bool extend, int *r_totmirr, int *r_totfail);
-void EDBM_automerge(struct Scene *scene, struct Object *ob, bool update, const char hflag);
struct BMVert *EDBM_vert_find_nearest_ex(struct ViewContext *vc,
float *r_dist,
@@ -188,8 +196,11 @@ bool EDBM_unified_findnearest(struct ViewContext *vc,
bool EDBM_unified_findnearest_from_raycast(struct ViewContext *vc,
struct Base **bases,
const uint bases_len,
- bool use_boundary,
- int *r_base_index,
+ bool use_boundary_vertices,
+ bool use_boundary_edges,
+ int *r_base_index_vert,
+ int *r_base_index_edge,
+ int *r_base_index_face,
struct BMVert **r_eve,
struct BMEdge **r_eed,
struct BMFace **r_efa);
@@ -239,15 +250,30 @@ void EDBM_preselect_edgering_update_from_edge(struct EditMesh_PreSelEdgeRing *ps
/* editmesh_preselect_elem.c */
struct EditMesh_PreSelElem;
+typedef enum eEditMesh_PreSelPreviewAction {
+ PRESELECT_ACTION_TRANSFORM = 1,
+ PRESELECT_ACTION_CREATE = 2,
+ PRESELECT_ACTION_DELETE = 3,
+} eEditMesh_PreSelPreviewAction;
+
struct EditMesh_PreSelElem *EDBM_preselect_elem_create(void);
void EDBM_preselect_elem_destroy(struct EditMesh_PreSelElem *psel);
void EDBM_preselect_elem_clear(struct EditMesh_PreSelElem *psel);
+void EDBM_preselect_preview_clear(struct EditMesh_PreSelElem *psel);
void EDBM_preselect_elem_draw(struct EditMesh_PreSelElem *psel, const float matrix[4][4]);
void EDBM_preselect_elem_update_from_single(struct EditMesh_PreSelElem *psel,
struct BMesh *bm,
struct BMElem *ele,
const float (*coords)[3]);
+void EDBM_preselect_elem_update_preview(struct EditMesh_PreSelElem *psel,
+ struct ViewContext *vc,
+ struct BMesh *bm,
+ struct BMElem *ele,
+ const int mval[2]);
+void EDBM_preselect_action_set(struct EditMesh_PreSelElem *psel,
+ eEditMesh_PreSelPreviewAction action);
+eEditMesh_PreSelPreviewAction EDBM_preselect_action_get(struct EditMesh_PreSelElem *psel);
/* mesh_ops.c */
void ED_operatortypes_mesh(void);
void ED_operatormacros_mesh(void);
@@ -358,21 +384,14 @@ void ED_mesh_geometry_add(
struct Mesh *mesh, struct ReportList *reports, int verts, int edges, int faces);
#endif
void ED_mesh_polys_add(struct Mesh *mesh, struct ReportList *reports, int count);
-void ED_mesh_tessfaces_add(struct Mesh *mesh, struct ReportList *reports, int count);
void ED_mesh_edges_add(struct Mesh *mesh, struct ReportList *reports, int count);
void ED_mesh_loops_add(struct Mesh *mesh, struct ReportList *reports, int count);
void ED_mesh_vertices_add(struct Mesh *mesh, struct ReportList *reports, int count);
-void ED_mesh_faces_remove(struct Mesh *mesh, struct ReportList *reports, int count);
void ED_mesh_edges_remove(struct Mesh *mesh, struct ReportList *reports, int count);
void ED_mesh_vertices_remove(struct Mesh *mesh, struct ReportList *reports, int count);
-void ED_mesh_calc_tessface(struct Mesh *mesh, bool free_mpoly);
-void ED_mesh_update(struct Mesh *mesh,
- struct bContext *C,
- bool calc_edges,
- bool calc_edges_loose,
- bool calc_tessface);
+void ED_mesh_update(struct Mesh *mesh, struct bContext *C, bool calc_edges, bool calc_edges_loose);
void ED_mesh_uv_texture_ensure(struct Mesh *me, const char *name);
int ED_mesh_uv_texture_add(struct Mesh *me,
diff --git a/source/blender/editors/include/ED_object.h b/source/blender/editors/include/ED_object.h
index 7b9a96e4d07..c481c19a552 100644
--- a/source/blender/editors/include/ED_object.h
+++ b/source/blender/editors/include/ED_object.h
@@ -34,7 +34,6 @@ struct EnumPropertyItem;
struct EnumPropertyItem;
struct ID;
struct Main;
-struct Menu;
struct ModifierData;
struct Object;
struct PointerRNA;
@@ -44,16 +43,15 @@ struct Scene;
struct ShaderFxData;
struct View3D;
struct ViewLayer;
+struct XFormObjectData;
struct bConstraint;
struct bContext;
struct bFaceMap;
struct bPoseChannel;
struct uiLayout;
struct wmKeyConfig;
-struct wmKeyMap;
struct wmOperator;
struct wmOperatorType;
-struct wmWindow;
struct wmWindowManager;
#include "DNA_object_enums.h"
@@ -403,6 +401,12 @@ bool ED_object_jump_to_bone(struct bContext *C,
void ED_object_facemap_face_add(struct Object *ob, struct bFaceMap *fmap, int facenum);
void ED_object_facemap_face_remove(struct Object *ob, struct bFaceMap *fmap, int facenum);
+/* object_data_transform.c */
+struct XFormObjectData *ED_object_data_xform_create(struct ID *id);
+void ED_object_data_xform_destroy(struct XFormObjectData *xod);
+
+void ED_object_data_xform_by_mat4(struct XFormObjectData *xod, const float mat[4][4]);
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/editors/include/ED_outliner.h b/source/blender/editors/include/ED_outliner.h
index e94aedc2b2b..3015d8d9cdc 100644
--- a/source/blender/editors/include/ED_outliner.h
+++ b/source/blender/editors/include/ED_outliner.h
@@ -30,4 +30,18 @@ bool ED_outliner_collections_editor_poll(struct bContext *C);
void ED_outliner_selected_objects_get(const struct bContext *C, struct ListBase *objects);
+Base *ED_outliner_give_base_under_cursor(struct bContext *C, const int mval[2]);
+
+void ED_outliner_select_sync_from_object_tag(struct bContext *C);
+void ED_outliner_select_sync_from_edit_bone_tag(struct bContext *C);
+void ED_outliner_select_sync_from_pose_bone_tag(struct bContext *C);
+void ED_outliner_select_sync_from_sequence_tag(struct bContext *C);
+void ED_outliner_select_sync_from_all_tag(struct bContext *C);
+
+bool ED_outliner_select_sync_is_dirty(const struct bContext *C);
+
+void ED_outliner_select_sync_from_outliner(struct bContext *C, struct SpaceOutliner *soops);
+
+void ED_outliner_select_sync_flag_outliners(const struct bContext *C);
+
#endif /* __ED_OUTLINER_H__ */
diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h
index c7ee7be49b5..d0fab134dcc 100644
--- a/source/blender/editors/include/ED_screen.h
+++ b/source/blender/editors/include/ED_screen.h
@@ -39,10 +39,8 @@ struct Depsgraph;
struct IDProperty;
struct Main;
struct MenuType;
-struct PropertyRNA;
struct Scene;
struct SpaceLink;
-struct ViewLayer;
struct WorkSpace;
struct WorkSpaceInstanceHook;
struct bContext;
@@ -50,7 +48,6 @@ struct bScreen;
struct rcti;
struct uiBlock;
struct uiLayout;
-struct wmEvent;
struct wmKeyConfig;
struct wmMsgBus;
struct wmMsgSubscribeKey;
diff --git a/source/blender/editors/include/ED_sculpt.h b/source/blender/editors/include/ED_sculpt.h
index 034e002f86a..d907ba4e581 100644
--- a/source/blender/editors/include/ED_sculpt.h
+++ b/source/blender/editors/include/ED_sculpt.h
@@ -25,10 +25,7 @@
#define __ED_SCULPT_H__
struct ARegion;
-struct ListBase;
struct Object;
-struct RegionView3D;
-struct UndoStep;
struct UndoType;
struct ViewContext;
struct bContext;
diff --git a/source/blender/editors/include/ED_time_scrub_ui.h b/source/blender/editors/include/ED_time_scrub_ui.h
index b43e674224c..f010c45d939 100644
--- a/source/blender/editors/include/ED_time_scrub_ui.h
+++ b/source/blender/editors/include/ED_time_scrub_ui.h
@@ -24,7 +24,6 @@
#ifndef __ED_TIME_SCRUB_UI_H__
#define __ED_TIME_SCRUB_UI_H__
-struct View2DGrid;
struct bContext;
struct bDopeSheet;
struct wmEvent;
diff --git a/source/blender/editors/include/ED_transform.h b/source/blender/editors/include/ED_transform.h
index 3605a245187..d8b65aa5975 100644
--- a/source/blender/editors/include/ED_transform.h
+++ b/source/blender/editors/include/ED_transform.h
@@ -26,18 +26,11 @@
/* ******************* Registration Function ********************** */
-struct ARegion;
-struct ListBase;
-struct Main;
struct Object;
struct SnapObjectContext;
struct SnapObjectParams;
-struct View3D;
-struct WorkSpace;
struct bContext;
-struct wmEvent;
struct wmKeyConfig;
-struct wmKeyMap;
struct wmOperatorType;
void ED_keymap_transform(struct wmKeyConfig *keyconf);
@@ -97,6 +90,10 @@ enum TfmMode {
#define CTX_PAINT_CURVE (1 << 8)
#define CTX_GPENCIL_STROKES (1 << 9)
#define CTX_CURSOR (1 << 10)
+/** When transforming object's, adjust the object data so it stays in the same place. */
+#define CTX_OBMODE_XFORM_OBDATA (1 << 11)
+/** Transform object parents without moving their children. */
+#define CTX_OBMODE_XFORM_SKIP_CHILDREN (1 << 12)
/* Standalone call to get the transformation center corresponding to the current situation
* returns 1 if successful, 0 otherwise (usually means there's no selection)
@@ -112,7 +109,6 @@ struct Scene;
struct TransInfo;
struct wmGizmoGroup;
struct wmGizmoGroupType;
-struct wmOperator;
/* UNUSED */
// int BIF_snappingSupported(struct Object *obedit);
@@ -197,12 +193,12 @@ bool peelObjectsSnapContext(struct SnapObjectContext *sctx,
float r_no[3],
float *r_thickness);
-bool snapObjectsTransform(struct TransInfo *t,
- const float mval[2],
- float *dist_px,
- /* return args */
- float r_loc[3],
- float r_no[3]);
+short snapObjectsTransform(struct TransInfo *t,
+ const float mval[2],
+ float *dist_px,
+ /* return args */
+ float r_loc[3],
+ float r_no[3]);
bool snapNodesTransform(struct TransInfo *t,
const int mval[2],
/* return args */
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 f27523bb1f8..40e0005b487 100644
--- a/source/blender/editors/include/ED_transform_snap_object_context.h
+++ b/source/blender/editors/include/ED_transform_snap_object_context.h
@@ -32,8 +32,6 @@ struct Main;
struct Object;
struct Scene;
struct View3D;
-struct ViewLayer;
-struct bContext;
/* transform_snap_object.c */
@@ -125,6 +123,7 @@ short ED_transform_snap_object_project_view3d_ex(struct SnapObjectContext *sctx,
const unsigned short snap_to,
const struct SnapObjectParams *params,
const float mval[2],
+ const float prev_co[3],
float *dist_px,
float r_loc[3],
float r_no[3],
@@ -135,6 +134,7 @@ bool ED_transform_snap_object_project_view3d(struct SnapObjectContext *sctx,
const unsigned short snap_to,
const struct SnapObjectParams *params,
const float mval[2],
+ const float prev_co[3],
float *dist_px,
/* return args */
float r_loc[3],
diff --git a/source/blender/editors/include/ED_uvedit.h b/source/blender/editors/include/ED_uvedit.h
index 8dd2dab5209..1856ad8454b 100644
--- a/source/blender/editors/include/ED_uvedit.h
+++ b/source/blender/editors/include/ED_uvedit.h
@@ -37,7 +37,6 @@ struct Object;
struct Scene;
struct SpaceImage;
struct ToolSettings;
-struct View3D;
struct ViewLayer;
struct bNode;
struct wmKeyConfig;
diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h
index 67dfb184d19..139b306b533 100644
--- a/source/blender/editors/include/ED_view3d.h
+++ b/source/blender/editors/include/ED_view3d.h
@@ -38,8 +38,6 @@ struct Camera;
struct CustomData_MeshMasks;
struct Depsgraph;
struct EditBone;
-struct GPUFX;
-struct GPUFXSettings;
struct GPUOffScreen;
struct GPUViewport;
struct ID;
@@ -58,7 +56,6 @@ struct View3D;
struct View3DShading;
struct ViewContext;
struct ViewLayer;
-struct WorkSpace;
struct bContext;
struct bPoseChannel;
struct bScreen;
@@ -690,6 +687,10 @@ bool ED_view3d_distance_set_from_location(struct RegionView3D *rv3d,
float ED_scene_grid_scale(struct Scene *scene, const char **grid_unit);
float ED_view3d_grid_scale(struct Scene *scene, struct View3D *v3d, const char **grid_unit);
+void ED_view3d_grid_steps(struct Scene *scene,
+ struct View3D *v3d,
+ struct RegionView3D *rv3d,
+ float *r_grid_steps);
float ED_view3d_grid_view_scale(struct Scene *scene,
struct View3D *v3d,
struct RegionView3D *rv3d,
diff --git a/source/blender/editors/include/UI_icons.h b/source/blender/editors/include/UI_icons.h
index 03a49943ed3..41ac1b6b452 100644
--- a/source/blender/editors/include/UI_icons.h
+++ b/source/blender/editors/include/UI_icons.h
@@ -483,7 +483,7 @@ DEF_ICON(UNDERLINE)
DEF_ICON(SMALL_CAPS)
DEF_ICON_BLANK(742)
DEF_ICON_BLANK(743)
-DEF_ICON_BLANK(744)
+DEF_ICON_MODIFIER(CON_ACTION)
DEF_ICON_BLANK(745)
DEF_ICON_BLANK(746)
DEF_ICON_BLANK(747)
@@ -667,8 +667,8 @@ DEF_ICON(PARTICLE_PATH)
/* EDITING */
DEF_ICON_BLANK(669)
DEF_ICON_BLANK(670)
-DEF_ICON_BLANK(671)
-DEF_ICON_BLANK(672)
+DEF_ICON(SNAP_PERPENDICULAR)
+DEF_ICON(SNAP_MIDPOINT)
DEF_ICON(SNAP_OFF)
DEF_ICON(SNAP_ON)
DEF_ICON(SNAP_NORMAL)
@@ -866,8 +866,8 @@ DEF_ICON(SEQ_LUMA_WAVEFORM)
DEF_ICON(SEQ_CHROMA_SCOPE)
DEF_ICON(SEQ_HISTOGRAM)
DEF_ICON(SEQ_SPLITVIEW)
-DEF_ICON_BLANK(870)
-DEF_ICON_BLANK(871)
+DEF_ICON(SEQ_STRIP_META)
+DEF_ICON(SEQ_STRIP_DUPLICATE)
DEF_ICON_BLANK(872)
DEF_ICON(IMAGE_RGB) // XXX CHANGE TO STRAIGHT ALPHA, Z ETC
DEF_ICON(IMAGE_RGB_ALPHA)
diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h
index 5ef3e5d8987..aa1044647c8 100644
--- a/source/blender/editors/include/UI_interface.h
+++ b/source/blender/editors/include/UI_interface.h
@@ -31,7 +31,6 @@
/* Struct Declarations */
struct ARegion;
-struct ARegionType;
struct AutoComplete;
struct ID;
struct IDProperty;
@@ -90,7 +89,7 @@ typedef struct uiPopupBlockHandle uiPopupBlockHandle;
* For #ARegion.overlap regions, pass events though if they don't overlap
* the regions contents (the usable part of the #View2D and buttons).
*
- * The margin is needed so it's not possible to accidentally click inbetween buttons.
+ * The margin is needed so it's not possible to accidentally click in between buttons.
*/
#define UI_REGION_OVERLAP_MARGIN (U.widget_unit / 3)
diff --git a/source/blender/editors/include/UI_resources.h b/source/blender/editors/include/UI_resources.h
index 1db584f0f59..a73e2655b7d 100644
--- a/source/blender/editors/include/UI_resources.h
+++ b/source/blender/editors/include/UI_resources.h
@@ -255,6 +255,7 @@ typedef enum ThemeColorID {
TH_MATCH, /* highlight color for search matches */
TH_SELECT_HIGHLIGHT, /* highlight color for selected outliner item */
+ TH_SELECT_ACTIVE, /* highlight color for active outliner item */
TH_SELECTED_OBJECT, /* selected object color for outliner */
TH_ACTIVE_OBJECT, /* active object color for outliner */
TH_EDITED_OBJECT, /* edited object color for outliner */
diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c
index 7a123599be5..ee354df3a25 100644
--- a/source/blender/editors/interface/interface.c
+++ b/source/blender/editors/interface/interface.c
@@ -908,12 +908,12 @@ void UI_but_execute(const bContext *C, ARegion *ar, uiBut *but)
* returns false if undo needs to be disabled. */
static bool ui_but_is_rna_undo(const uiBut *but)
{
- if (but->rnapoin.id.data) {
+ if (but->rnapoin.owner_id) {
/* avoid undo push for buttons who's ID are screen or wm level
* we could disable undo for buttons with no ID too but may have
* unforeseen consequences, so best check for ID's we _know_ are not
* handled by undo - campbell */
- ID *id = but->rnapoin.id.data;
+ ID *id = but->rnapoin.owner_id;
if (ID_CHECK_UNDO(id) == false) {
return false;
}
@@ -1226,8 +1226,8 @@ static bool ui_but_event_property_operator_string(const bContext *C,
*/
char *data_path = NULL;
- if (ptr->id.data) {
- ID *id = ptr->id.data;
+ if (ptr->owner_id) {
+ ID *id = ptr->owner_id;
if (GS(id->name) == ID_SCR) {
/* screen/editor property
@@ -1731,7 +1731,7 @@ static void ui_block_message_subscribe(ARegion *ar, struct wmMsgBus *mbus, uiBlo
if ((but_prev && (but_prev->rnaprop == but->rnaprop) &&
(but_prev->rnapoin.type == but->rnapoin.type) &&
(but_prev->rnapoin.data == but->rnapoin.data) &&
- (but_prev->rnapoin.id.data == but->rnapoin.id.data)) == false) {
+ (but_prev->rnapoin.owner_id == but->rnapoin.owner_id)) == false) {
/* TODO: could make this into utility function. */
WM_msg_subscribe_rna(mbus,
&but->rnapoin,
@@ -2055,7 +2055,7 @@ bool ui_but_is_compatible(const uiBut *but_a, const uiBut *but_b)
}
if (but_a->rnaprop) {
- /* skip 'rnapoin.data', 'rnapoin.id.data'
+ /* skip 'rnapoin.data', 'rnapoin.owner_id'
* allow different data to have the same props edited at once */
if (but_a->rnapoin.type != but_b->rnapoin.type) {
return false;
@@ -2479,7 +2479,7 @@ void ui_but_string_get_ex(uiBut *but,
/* uiBut.custom_data points to data this tab represents (e.g. workspace).
* uiBut.rnapoin/prop store an active value (e.g. active workspace). */
- RNA_pointer_create(but->rnapoin.id.data, ptr_type, but->custom_data, &ptr);
+ RNA_pointer_create(but->rnapoin.owner_id, ptr_type, but->custom_data, &ptr);
buf = RNA_struct_name_get_alloc(&ptr, str, maxlen, &buf_len);
}
else if (type == PROP_STRING) {
@@ -2822,7 +2822,7 @@ bool ui_but_string_set(bContext *C, uiBut *but, const char *str)
/* uiBut.custom_data points to data this tab represents (e.g. workspace).
* uiBut.rnapoin/prop store an active value (e.g. active workspace). */
- RNA_pointer_create(but->rnapoin.id.data, ptr_type, but->custom_data, &ptr);
+ RNA_pointer_create(but->rnapoin.owner_id, ptr_type, but->custom_data, &ptr);
prop = RNA_struct_name_property(ptr_type);
if (RNA_property_editable(&ptr, prop)) {
RNA_property_string_set(&ptr, prop, str);
@@ -3586,6 +3586,14 @@ static uiBut *ui_def_but(uiBlock *block,
(a1 != 0.0f && a1 != 1.0f)) == false);
}
+ /* Number buttons must have a click-step,
+ * assert instead of correcting the value to ensure the caller knows what they're doing. */
+ if ((type & BUTTYPE) == UI_BTYPE_NUM) {
+ if (ELEM((type & UI_BUT_POIN_TYPES), UI_BUT_POIN_CHAR, UI_BUT_POIN_SHORT, UI_BUT_POIN_INT)) {
+ BLI_assert((int)a1 > 0);
+ }
+ }
+
if (type & UI_BUT_POIN_TYPES) { /* a pointer is required */
if (poin == NULL) {
BLI_assert(0);
diff --git a/source/blender/editors/interface/interface_anim.c b/source/blender/editors/interface/interface_anim.c
index 4d87cc22ef2..168c6051327 100644
--- a/source/blender/editors/interface/interface_anim.c
+++ b/source/blender/editors/interface/interface_anim.c
@@ -228,7 +228,7 @@ bool ui_but_anim_expression_create(uiBut *but, const char *str)
/* make sure we have animdata for this */
/* FIXME: until materials can be handled by depsgraph,
* don't allow drivers to be created for them */
- id = (ID *)but->rnapoin.id.data;
+ id = but->rnapoin.owner_id;
if ((id == NULL) || (GS(id->name) == ID_MA) || (GS(id->name) == ID_TE)) {
if (G.debug & G_DEBUG) {
printf("ERROR: create expression failed - invalid data-block for adding drivers (%p)\n", id);
@@ -314,7 +314,7 @@ void ui_but_anim_autokey(bContext *C, uiBut *but, Scene *scene, float cfra)
}
}
else {
- id = but->rnapoin.id.data;
+ id = but->rnapoin.owner_id;
/* TODO: this should probably respect the keyingset only option for anim */
if (autokeyframe_cfra_can_key(scene, id)) {
diff --git a/source/blender/editors/interface/interface_context_menu.c b/source/blender/editors/interface/interface_context_menu.c
index 22b75da4968..580ff527bf6 100644
--- a/source/blender/editors/interface/interface_context_menu.c
+++ b/source/blender/editors/interface/interface_context_menu.c
@@ -176,11 +176,21 @@ static uiBlock *menu_change_shortcut(bContext *C, ARegion *ar, void *arg)
UI_block_flag_enable(block, UI_BLOCK_MOVEMOUSE_QUIT);
UI_block_direction_set(block, UI_DIR_CENTER_Y);
- layout = UI_block_layout(block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, 0, 0, 200, 20, 0, style);
-
+ layout = UI_block_layout(block,
+ UI_LAYOUT_VERTICAL,
+ UI_LAYOUT_PANEL,
+ 0,
+ 0,
+ U.widget_unit * 10,
+ U.widget_unit * 2,
+ 0,
+ style);
+
+ uiItemL(layout, CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Change Shortcut"), ICON_HAND);
uiItemR(layout, &ptr, "type", UI_ITEM_R_FULL_EVENT | UI_ITEM_R_IMMEDIATE, "", ICON_NONE);
- UI_block_bounds_set_popup(block, 6, (const int[2]){-50, 26});
+ UI_block_bounds_set_popup(
+ block, 6 * U.dpi_fac, (const int[2]){-100 * U.dpi_fac, 36 * U.dpi_fac});
shortcut_free_operator_property(prop);
@@ -227,11 +237,21 @@ static uiBlock *menu_add_shortcut(bContext *C, ARegion *ar, void *arg)
UI_block_func_handle_set(block, but_shortcut_name_func, but);
UI_block_direction_set(block, UI_DIR_CENTER_Y);
- layout = UI_block_layout(block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, 0, 0, 200, 20, 0, style);
-
+ layout = UI_block_layout(block,
+ UI_LAYOUT_VERTICAL,
+ UI_LAYOUT_PANEL,
+ 0,
+ 0,
+ U.widget_unit * 10,
+ U.widget_unit * 2,
+ 0,
+ style);
+
+ uiItemL(layout, CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Assign Shortcut"), ICON_HAND);
uiItemR(layout, &ptr, "type", UI_ITEM_R_FULL_EVENT | UI_ITEM_R_IMMEDIATE, "", ICON_NONE);
- UI_block_bounds_set_popup(block, 6, (const int[2]){-50, 26});
+ UI_block_bounds_set_popup(
+ block, 6 * U.dpi_fac, (const int[2]){-100 * U.dpi_fac, 36 * U.dpi_fac});
#ifdef USE_KEYMAP_ADD_HACK
g_kmi_id_hack = kmi_id;
@@ -470,8 +490,9 @@ static void ui_but_menu_add_path_operators(uiLayout *layout, PointerRNA *ptr, Pr
bool ui_popup_context_menu_for_button(bContext *C, uiBut *but)
{
- /* having this menu for some buttons makes no sense */
- if (but->type == UI_BTYPE_IMAGE) {
+ /* ui_but_is_interactive() may let some buttons through that should not get a context menu - it
+ * doesn't make sense for them. */
+ if (ELEM(but->type, UI_BTYPE_LABEL, UI_BTYPE_IMAGE)) {
return false;
}
@@ -904,7 +925,7 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but)
ICON_NONE,
"UI_OT_copy_data_path_button");
- if (ptr->id.data && !is_whole_array &&
+ if (ptr->owner_id && !is_whole_array &&
ELEM(type, PROP_BOOLEAN, PROP_INT, PROP_FLOAT, PROP_ENUM)) {
uiItemO(layout,
CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Copy As New Driver"),
@@ -1222,7 +1243,7 @@ void ui_popup_context_menu_for_panel(bContext *C, ARegion *ar, Panel *pa)
sizeof(tmpstr),
"%s" UI_SEP_CHAR_S "%s",
IFACE_("Pin"),
- IFACE_("Shift+Left Mouse"));
+ IFACE_("Shift Left Mouse"));
uiItemR(layout, &ptr, "use_pin", 0, tmpstr, ICON_NONE);
/* evil, force shortcut flag */
@@ -1230,6 +1251,7 @@ void ui_popup_context_menu_for_panel(bContext *C, ARegion *ar, Panel *pa)
uiBlock *block = uiLayoutGetBlock(layout);
uiBut *but = block->buttons.last;
but->flag |= UI_BUT_HAS_SEP_CHAR;
+ but->drawflag |= UI_BUT_HAS_SHORTCUT;
}
}
UI_popup_menu_end(C, pup);
diff --git a/source/blender/editors/interface/interface_draw.c b/source/blender/editors/interface/interface_draw.c
index 76630de96db..b7a0812c5f2 100644
--- a/source/blender/editors/interface/interface_draw.c
+++ b/source/blender/editors/interface/interface_draw.c
@@ -750,7 +750,7 @@ void ui_draw_but_IMAGE(ARegion *UNUSED(ar),
/**
* Draw title and text safe areas.
*
- * \Note This functionn is to be used with the 2D dashed shader enabled.
+ * \note This function is to be used with the 2D dashed shader enabled.
*
* \param pos: is a PRIM_FLOAT, 2, GPU_FETCH_FLOAT vertex attribute.
* \param line_origin: is a PRIM_FLOAT, 2, GPU_FETCH_FLOAT vertex attribute.
diff --git a/source/blender/editors/interface/interface_eyedropper_datablock.c b/source/blender/editors/interface/interface_eyedropper_datablock.c
index 658aa4f67f9..336fae45895 100644
--- a/source/blender/editors/interface/interface_eyedropper_datablock.c
+++ b/source/blender/editors/interface/interface_eyedropper_datablock.c
@@ -51,6 +51,7 @@
#include "ED_space_api.h"
#include "ED_screen.h"
#include "ED_view3d.h"
+#include "ED_outliner.h"
#include "interface_intern.h"
#include "interface_eyedropper_intern.h"
@@ -67,6 +68,7 @@ typedef struct DataDropper {
ID *init_id; /* for resetting on cancel */
+ ScrArea *cursor_area; /* Area under the cursor */
ARegionType *art;
void *draw_handle_pixel;
char name[200];
@@ -103,6 +105,7 @@ static int datadropper_init(bContext *C, wmOperator *op)
ddr->is_undo = UI_but_flag_is_set(but, UI_BUT_UNDO);
+ ddr->cursor_area = CTX_wm_area(C);
ddr->art = art;
ddr->draw_handle_pixel = ED_region_draw_cb_activate(
art, datadropper_draw_cb, ddr, REGION_DRAW_POST_PIXEL);
@@ -115,7 +118,7 @@ static int datadropper_init(bContext *C, wmOperator *op)
ddr->idcode_name = TIP_(BKE_idcode_to_name(ddr->idcode));
PointerRNA ptr = RNA_property_pointer_get(&ddr->ptr, ddr->prop);
- ddr->init_id = ptr.id.data;
+ ddr->init_id = ptr.owner_id;
return true;
}
@@ -141,7 +144,7 @@ static void datadropper_exit(bContext *C, wmOperator *op)
/* *** datadropper id helper functions *** */
/**
- * \brief get the ID from the screen.
+ * \brief get the ID from the 3D view or outliner.
*/
static void datadropper_id_sample_pt(bContext *C, DataDropper *ddr, int mx, int my, ID **r_id)
{
@@ -155,7 +158,7 @@ static void datadropper_id_sample_pt(bContext *C, DataDropper *ddr, int mx, int
ddr->name[0] = '\0';
if (sa) {
- if (sa->spacetype == SPACE_VIEW3D) {
+ if (ELEM(sa->spacetype, SPACE_VIEW3D, SPACE_OUTLINER)) {
ARegion *ar = BKE_area_find_region_xy(sa, RGN_TYPE_WINDOW, mx, my);
if (ar) {
const int mval[2] = {mx - ar->winrct.xmin, my - ar->winrct.ymin};
@@ -167,7 +170,13 @@ static void datadropper_id_sample_pt(bContext *C, DataDropper *ddr, int mx, int
/* grr, always draw else we leave stale text */
ED_region_tag_redraw(ar);
- base = ED_view3d_give_base_under_cursor(C, mval);
+ if (sa->spacetype == SPACE_VIEW3D) {
+ base = ED_view3d_give_base_under_cursor(C, mval);
+ }
+ else {
+ base = ED_outliner_give_base_under_cursor(C, mval);
+ }
+
if (base) {
Object *ob = base->object;
ID *id = NULL;
@@ -213,7 +222,7 @@ static bool datadropper_id_set(bContext *C, DataDropper *ddr, ID *id)
ptr_value = RNA_property_pointer_get(&ddr->ptr, ddr->prop);
- return (ptr_value.id.data == id);
+ return (ptr_value.owner_id == id);
}
/* single point sample & set */
@@ -232,6 +241,36 @@ static void datadropper_cancel(bContext *C, wmOperator *op)
datadropper_exit(C, op);
}
+/* To switch the draw callback when region under mouse event changes */
+static void datadropper_set_draw_callback_region(bContext *C,
+ DataDropper *ddr,
+ const int mx,
+ const int my)
+{
+ bScreen *screen = CTX_wm_screen(C);
+ ScrArea *sa = BKE_screen_find_area_xy(screen, -1, mx, my);
+
+ if (sa) {
+ /* If spacetype changed */
+ if (sa->spacetype != ddr->cursor_area->spacetype) {
+ /* Remove old callback */
+ ED_region_draw_cb_exit(ddr->art, ddr->draw_handle_pixel);
+
+ /* Redraw old area */
+ ARegion *ar = BKE_area_find_region_type(ddr->cursor_area, RGN_TYPE_WINDOW);
+ ED_region_tag_redraw(ar);
+
+ /* Set draw callback in new region */
+ ARegionType *art = BKE_regiontype_from_id(sa->type, RGN_TYPE_WINDOW);
+
+ ddr->cursor_area = sa;
+ ddr->art = art;
+ ddr->draw_handle_pixel = ED_region_draw_cb_activate(
+ art, datadropper_draw_cb, ddr, REGION_DRAW_POST_PIXEL);
+ }
+ }
+}
+
/* main modal status check */
static int datadropper_modal(bContext *C, wmOperator *op, const wmEvent *event)
{
@@ -260,6 +299,10 @@ static int datadropper_modal(bContext *C, wmOperator *op, const wmEvent *event)
}
else if (event->type == MOUSEMOVE) {
ID *id = NULL;
+
+ /* Set the region for eyedropper cursor text drawing */
+ datadropper_set_draw_callback_region(C, ddr, event->x, event->y);
+
datadropper_id_sample_pt(C, ddr, event->x, event->y, &id);
}
diff --git a/source/blender/editors/interface/interface_eyedropper_driver.c b/source/blender/editors/interface/interface_eyedropper_driver.c
index c1aee190cd3..e6fc52bc3bc 100644
--- a/source/blender/editors/interface/interface_eyedropper_driver.c
+++ b/source/blender/editors/interface/interface_eyedropper_driver.c
@@ -116,10 +116,10 @@ static void driverdropper_sample(bContext *C, wmOperator *op, const wmEvent *eve
/* Now create driver(s) */
if (target_path && dst_path) {
int success = ANIM_add_driver_with_target(op->reports,
- ddr->ptr.id.data,
+ ddr->ptr.owner_id,
dst_path,
ddr->index,
- target_ptr->id.data,
+ target_ptr->owner_id,
target_path,
target_index,
flag,
@@ -130,7 +130,7 @@ static void driverdropper_sample(bContext *C, wmOperator *op, const wmEvent *eve
/* send updates */
UI_context_update_anim_flag(C);
DEG_relations_tag_update(CTX_data_main(C));
- DEG_id_tag_update(ddr->ptr.id.data, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
+ DEG_id_tag_update(ddr->ptr.owner_id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_ANIMATION | ND_FCURVES_ORDER, NULL); // XXX
}
}
diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c
index 0cb0dbdc85c..55980099116 100644
--- a/source/blender/editors/interface/interface_handlers.c
+++ b/source/blender/editors/interface/interface_handlers.c
@@ -757,7 +757,7 @@ static void ui_apply_but_undo(uiBut *but)
}
/* Optionally override undo when undo system doesn't support storing properties. */
- if (but->rnapoin.id.data) {
+ if (but->rnapoin.owner_id) {
/* Exception for renaming ID data, we always need undo pushes in this case,
* because undo systems track data by their ID, see: T67002. */
extern PropertyRNA rna_ID_name;
@@ -765,7 +765,7 @@ static void ui_apply_but_undo(uiBut *but)
/* pass */
}
else {
- ID *id = but->rnapoin.id.data;
+ ID *id = but->rnapoin.owner_id;
if (!ED_undo_is_legacy_compatible_for_property(but->block->evil_C, id)) {
str = "";
}
@@ -1558,7 +1558,7 @@ static bool ui_selectcontext_begin(bContext *C, uiBut *but, uiSelectContextStore
if (use_path_from_id) {
/* Path relative to ID. */
lprop = NULL;
- RNA_id_pointer_create(link->ptr.id.data, &idptr);
+ RNA_id_pointer_create(link->ptr.owner_id, &idptr);
RNA_path_resolve_property(&idptr, path, &lptr, &lprop);
}
else if (path) {
@@ -2760,7 +2760,7 @@ static void ui_textedit_set_cursor_pos(uiBut *but, uiHandleButtonData *data, con
startx += UI_DPI_ICON_SIZE / aspect;
}
}
- /* but this extra .05 makes clicks inbetween characters feel nicer */
+ /* But this extra .05 makes clicks in between characters feel nicer. */
startx += ((UI_TEXT_MARGIN_X + 0.05f) * U.widget_unit) / aspect;
/* mouse dragged outside the widget to the left */
@@ -4800,18 +4800,19 @@ static int ui_do_but_NUM(
if (click) {
/* we can click on the side arrows to increment/decrement,
* or click inside to edit the value directly */
- const float softmin = but->softmin;
- const float softmax = but->softmax;
if (!ui_but_is_float(but)) {
/* Integer Value. */
if (but->drawflag & (UI_BUT_ACTIVE_LEFT | UI_BUT_ACTIVE_RIGHT)) {
button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
+
const int value_step = (int)but->a1;
BLI_assert(value_step > 0);
+ const int softmin = round_fl_to_int_clamp(but->softmin);
+ const int softmax = round_fl_to_int_clamp(but->softmax);
const double value_test = (but->drawflag & UI_BUT_ACTIVE_LEFT) ?
- (double)max_ii((int)softmin, (int)data->value - value_step) :
- (double)min_ii((int)softmax, (int)data->value + value_step);
+ (double)max_ii(softmin, (int)data->value - value_step) :
+ (double)min_ii(softmax, (int)data->value + value_step);
if (value_test != data->value) {
data->value = (double)value_test;
}
@@ -4828,11 +4829,14 @@ static int ui_do_but_NUM(
/* Float Value. */
if (but->drawflag & (UI_BUT_ACTIVE_LEFT | UI_BUT_ACTIVE_RIGHT)) {
button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
+
const double value_step = (double)but->a1 * UI_PRECISION_FLOAT_SCALE;
BLI_assert(value_step > 0.0f);
const double value_test = (but->drawflag & UI_BUT_ACTIVE_LEFT) ?
- (double)max_ff(softmin, (float)(data->value - value_step)) :
- (double)min_ff(softmax, (float)(data->value + value_step));
+ (double)max_ff(but->softmin,
+ (float)(data->value - value_step)) :
+ (double)min_ff(but->softmax,
+ (float)(data->value + value_step));
if (value_test != data->value) {
data->value = value_test;
}
@@ -5484,7 +5488,7 @@ static bool ui_numedit_but_UNITVEC(
static void ui_palette_set_active(uiBut *but)
{
if ((int)(but->a1) == UI_PALETTE_COLOR) {
- Palette *palette = but->rnapoin.id.data;
+ Palette *palette = (Palette *)but->rnapoin.owner_id;
PaletteColor *color = but->rnapoin.data;
palette->active_color = BLI_findindex(&palette->colors, color);
}
@@ -5547,7 +5551,7 @@ static int ui_do_but_COLOR(bContext *C, uiBut *but, uiHandleButtonData *data, co
}
else if ((int)(but->a1) == UI_PALETTE_COLOR && event->type == DELKEY &&
event->val == KM_PRESS) {
- Palette *palette = but->rnapoin.id.data;
+ Palette *palette = (Palette *)but->rnapoin.owner_id;
PaletteColor *color = but->rnapoin.data;
BKE_palette_color_remove(palette, color);
@@ -8184,7 +8188,7 @@ static int ui_handle_button_event(bContext *C, const wmEvent *event, uiBut *but)
else {
/* Do this so we can still mouse-up, closing the menu and running the button.
* This is nice to support but there are times when the button gets left pressed.
- * Keep disavled for now. */
+ * Keep disabled for now. */
WM_event_remove_timer(data->wm, data->window, data->hold_action_timer);
data->hold_action_timer = NULL;
}
diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c
index e9aa18394fa..7ada4755a64 100644
--- a/source/blender/editors/interface/interface_icons.c
+++ b/source/blender/editors/interface/interface_icons.c
@@ -154,8 +154,8 @@ typedef struct IconType {
} IconType;
/* ******************* STATIC LOCAL VARS ******************* */
-/* static here to cache results of icon directory scan, so it's not
- * scanning the filesystem each time the menu is drawn */
+/* Static here to cache results of icon directory scan, so it's not
+ * scanning the file-system each time the menu is drawn. */
static struct ListBase iconfilelist = {NULL, NULL};
static IconTexture icongltex = {{0, 0}, 0, 0, 0, 0.0f, 0.0f};
@@ -317,8 +317,9 @@ static void vicon_keytype_draw_wrapper(
format, "outlineColor", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
uint flags_id = GPU_vertformat_attr_add(format, "flags", GPU_COMP_U32, 1, GPU_FETCH_INT);
- immBindBuiltinProgram(GPU_SHADER_KEYFRAME_DIAMOND);
GPU_program_point_size(true);
+ immBindBuiltinProgram(GPU_SHADER_KEYFRAME_DIAMOND);
+ immUniform1f("outline_scale", 1.0f);
immUniform2f("ViewportSize", -1.0f, -1.0f);
immBegin(GPU_PRIM_POINTS, 1);
@@ -2136,7 +2137,7 @@ int UI_rnaptr_icon_get(bContext *C, PointerRNA *ptr, int rnaicon, const bool big
/* try ID, material, texture or dynapaint slot */
if (RNA_struct_is_ID(ptr->type)) {
- id = ptr->id.data;
+ id = ptr->owner_id;
}
else if (RNA_struct_is_a(ptr->type, &RNA_MaterialSlot)) {
id = RNA_pointer_get(ptr, "material").data;
@@ -2243,6 +2244,8 @@ int UI_idcode_icon_get(const int idcode)
return ICON_FONT_DATA;
case ID_WO:
return ICON_WORLD_DATA;
+ case ID_WS:
+ return ICON_WORKSPACE;
default:
return ICON_NONE;
}
diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c
index 78eed98eb77..529cb0712df 100644
--- a/source/blender/editors/interface/interface_layout.c
+++ b/source/blender/editors/interface/interface_layout.c
@@ -1180,11 +1180,6 @@ static uiBut *uiItemFullO_ptr_ex(uiLayout *layout,
assert(but->optype != NULL);
- /* text alignment for toolbar buttons */
- if ((layout->root->type == UI_LAYOUT_TOOLBAR) && !icon) {
- but->drawflag |= UI_BUT_TEXT_LEFT;
- }
-
if (flag & UI_ITEM_R_NO_BG) {
layout->emboss = prev_emboss;
}
@@ -1878,7 +1873,8 @@ void uiItemFullR(uiLayout *layout,
uiBut *but;
} ui_decorate = {
.use_prop_decorate = (((layout->item.flag & UI_ITEM_PROP_DECORATE) != 0) &&
- (use_prop_sep && ptr->id.data && id_can_have_animdata(ptr->id.data))),
+ (use_prop_sep && ptr->owner_id &&
+ id_can_have_animdata(ptr->owner_id))),
};
#endif /* UI_PROP_DECORATE */
@@ -2951,7 +2947,7 @@ void uiItemLDrag(uiLayout *layout, PointerRNA *ptr, const char *name, int icon)
if (ptr && ptr->type) {
if (RNA_struct_is_ID(ptr->type)) {
- UI_but_drag_set_id(but, ptr->id.data);
+ UI_but_drag_set_id(but, ptr->owner_id);
}
}
}
diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c
index 6051bf5ca40..68d21e88211 100644
--- a/source/blender/editors/interface/interface_ops.c
+++ b/source/blender/editors/interface/interface_ops.c
@@ -27,7 +27,7 @@
#include "DNA_armature_types.h"
#include "DNA_screen_types.h"
-#include "DNA_text_types.h" /* for UI_OT_reports_to_text */
+#include "DNA_text_types.h"
#include "DNA_object_types.h" /* for OB_DATA_SUPPORT_ID */
#include "BLI_blenlib.h"
@@ -45,7 +45,7 @@
#include "BKE_node.h"
#include "BKE_report.h"
#include "BKE_screen.h"
-#include "BKE_text.h" /* for UI_OT_reports_to_text */
+#include "BKE_text.h"
#include "IMB_colormanagement.h"
@@ -86,7 +86,7 @@ static bool copy_data_path_button_poll(bContext *C)
UI_context_active_but_prop_get(C, &ptr, &prop, &index);
- if (ptr.id.data && ptr.data && prop) {
+ if (ptr.owner_id && ptr.data && prop) {
path = RNA_path_from_ID_to_property(&ptr, prop);
if (path) {
@@ -100,29 +100,33 @@ static bool copy_data_path_button_poll(bContext *C)
static int copy_data_path_button_exec(bContext *C, wmOperator *op)
{
+ Main *bmain = CTX_data_main(C);
PointerRNA ptr;
PropertyRNA *prop;
char *path;
int index;
+ ID *id;
const bool full_path = RNA_boolean_get(op->ptr, "full_path");
/* try to create driver using property retrieved from UI */
UI_context_active_but_prop_get(C, &ptr, &prop, &index);
- if (ptr.id.data != NULL) {
-
+ if (ptr.owner_id != NULL) {
if (full_path) {
-
if (prop) {
- path = RNA_path_full_property_py_ex(&ptr, prop, index, true);
+ path = RNA_path_full_property_py_ex(bmain, &ptr, prop, index, true);
}
else {
- path = RNA_path_full_struct_py(&ptr);
+ path = RNA_path_full_struct_py(bmain, &ptr);
}
}
else {
- path = RNA_path_from_ID_to_property(&ptr, prop);
+ path = RNA_path_from_real_ID_to_property_index(bmain, &ptr, prop, 0, -1, &id);
+
+ if (!path) {
+ path = RNA_path_from_ID_to_property(&ptr, prop);
+ }
}
if (path) {
@@ -171,7 +175,7 @@ static bool copy_as_driver_button_poll(bContext *C)
UI_context_active_but_prop_get(C, &ptr, &prop, &index);
- if (ptr.id.data && ptr.data && prop &&
+ if (ptr.owner_id && ptr.data && prop &&
ELEM(RNA_property_type(prop), PROP_BOOLEAN, PROP_INT, PROP_FLOAT, PROP_ENUM) &&
(index >= 0 || !RNA_property_array_check(prop))) {
path = RNA_path_from_ID_to_property(&ptr, prop);
@@ -185,8 +189,9 @@ static bool copy_as_driver_button_poll(bContext *C)
return 0;
}
-static int copy_as_driver_button_exec(bContext *C, wmOperator *UNUSED(op))
+static int copy_as_driver_button_exec(bContext *C, wmOperator *op)
{
+ Main *bmain = CTX_data_main(C);
PointerRNA ptr;
PropertyRNA *prop;
int index;
@@ -194,15 +199,20 @@ static int copy_as_driver_button_exec(bContext *C, wmOperator *UNUSED(op))
/* try to create driver using property retrieved from UI */
UI_context_active_but_prop_get(C, &ptr, &prop, &index);
- if (ptr.id.data && ptr.data && prop) {
+ if (ptr.owner_id && ptr.data && prop) {
+ ID *id;
int dim = RNA_property_array_dimension(&ptr, prop, NULL);
- char *path = RNA_path_from_ID_to_property_index(&ptr, prop, dim, index);
+ char *path = RNA_path_from_real_ID_to_property_index(bmain, &ptr, prop, dim, index, &id);
if (path) {
- ANIM_copy_as_driver(ptr.id.data, path, RNA_property_identifier(prop));
+ ANIM_copy_as_driver(id, path, RNA_property_identifier(prop));
MEM_freeN(path);
return OPERATOR_FINISHED;
}
+ else {
+ BKE_reportf(op->reports, RPT_ERROR, "Could not compute a valid data path");
+ return OPERATOR_CANCELLED;
+ }
}
return OPERATOR_CANCELLED;
@@ -287,7 +297,7 @@ static void UI_OT_copy_python_command_button(wmOperatorType *ot)
static int operator_button_property_finish(bContext *C, PointerRNA *ptr, PropertyRNA *prop)
{
- ID *id = ptr->id.data;
+ ID *id = ptr->owner_id;
/* perform updates required for this property */
RNA_property_update(C, ptr, prop);
@@ -542,7 +552,7 @@ static int override_type_set_button_exec(bContext *C, wmOperator *op)
/* try to reset the nominated setting to its default value */
UI_context_active_but_prop_get(C, &ptr, &prop, &index);
- BLI_assert(ptr.id.data != NULL);
+ BLI_assert(ptr.owner_id != NULL);
if (all) {
index = -1;
@@ -605,7 +615,7 @@ static bool override_remove_button_poll(bContext *C)
const int override_status = RNA_property_override_library_status(&ptr, prop, index);
- return (ptr.data && ptr.id.data && prop && (override_status & RNA_OVERRIDE_STATUS_OVERRIDDEN));
+ return (ptr.data && ptr.owner_id && prop && (override_status & RNA_OVERRIDE_STATUS_OVERRIDDEN));
}
static int override_remove_button_exec(bContext *C, wmOperator *op)
@@ -619,7 +629,7 @@ static int override_remove_button_exec(bContext *C, wmOperator *op)
/* try to reset the nominated setting to its default value */
UI_context_active_but_prop_get(C, &ptr, &prop, &index);
- ID *id = ptr.id.data;
+ ID *id = ptr.owner_id;
IDOverrideLibraryProperty *oprop = RNA_property_override_property_find(&ptr, prop);
BLI_assert(oprop != NULL);
BLI_assert(id != NULL && id->override_library != NULL);
@@ -629,8 +639,9 @@ static int override_remove_button_exec(bContext *C, wmOperator *op)
/* We need source (i.e. linked data) to restore values of deleted overrides...
* If this is an override template, we obviously do not need to restore anything. */
if (!is_template) {
+ PropertyRNA *src_prop;
RNA_id_pointer_create(id->override_library->reference, &id_refptr);
- if (!RNA_path_resolve(&id_refptr, oprop->rna_path, &src, NULL)) {
+ if (!RNA_path_resolve_property(&id_refptr, oprop->rna_path, &src, &src_prop)) {
BLI_assert(0 && "Failed to create matching source (linked data) RNA pointer");
}
}
@@ -720,7 +731,7 @@ bool UI_context_copy_to_selected_list(bContext *C,
CollectionPointerLink *link;
for (link = lb.first; link; link = link->next) {
bPoseChannel *pchan = link->ptr.data;
- RNA_pointer_create(link->ptr.id.data, &RNA_Bone, pchan->bone, &link->ptr);
+ RNA_pointer_create(link->ptr.owner_id, &RNA_Bone, pchan->bone, &link->ptr);
}
}
@@ -739,7 +750,7 @@ bool UI_context_copy_to_selected_list(bContext *C,
/* Get the node we're editing */
if (RNA_struct_is_a(ptr->type, &RNA_NodeSocket)) {
- bNodeTree *ntree = ptr->id.data;
+ bNodeTree *ntree = (bNodeTree *)ptr->owner_id;
bNodeSocket *sock = ptr->data;
if (nodeFindNode(ntree, sock, &node, NULL)) {
if ((path = RNA_path_resolve_from_type_to_property(ptr, prop, &RNA_Node)) != NULL) {
@@ -773,8 +784,8 @@ bool UI_context_copy_to_selected_list(bContext *C,
*r_lb = lb;
*r_path = path;
}
- else if (ptr->id.data) {
- ID *id = ptr->id.data;
+ else if (ptr->owner_id) {
+ ID *id = ptr->owner_id;
if (GS(id->name) == ID_OB) {
*r_lb = CTX_data_collection_get(C, "selected_editable_objects");
@@ -792,7 +803,7 @@ bool UI_context_copy_to_selected_list(bContext *C,
CollectionPointerLink *link, *link_next;
for (link = lb.first; link; link = link->next) {
- Object *ob = link->ptr.id.data;
+ Object *ob = (Object *)link->ptr.owner_id;
if (ob->data) {
ID *id_data = ob->data;
id_data->tag |= LIB_TAG_DOIT;
@@ -800,7 +811,7 @@ bool UI_context_copy_to_selected_list(bContext *C,
}
for (link = lb.first; link; link = link_next) {
- Object *ob = link->ptr.id.data;
+ Object *ob = (Object *)link->ptr.owner_id;
ID *id_data = ob->data;
link_next = link->next;
@@ -872,7 +883,7 @@ static bool copy_to_selected_button(bContext *C, bool all, bool poll)
if (use_path_from_id) {
/* Path relative to ID. */
lprop = NULL;
- RNA_id_pointer_create(link->ptr.id.data, &idptr);
+ RNA_id_pointer_create(link->ptr.owner_id, &idptr);
RNA_path_resolve_property(&idptr, path, &lptr, &lprop);
}
else if (path) {
@@ -982,12 +993,12 @@ static bool jump_to_target_ptr(bContext *C, PointerRNA ptr, const bool poll)
/* Find the containing Object. */
ViewLayer *view_layer = CTX_data_view_layer(C);
Base *base = NULL;
- const short id_type = GS(((ID *)ptr.id.data)->name);
+ const short id_type = GS(ptr.owner_id->name);
if (id_type == ID_OB) {
- base = BKE_view_layer_base_find(view_layer, ptr.id.data);
+ base = BKE_view_layer_base_find(view_layer, (Object *)ptr.owner_id);
}
else if (OB_DATA_SUPPORT_ID(id_type)) {
- base = ED_object_find_first_by_data_id(view_layer, ptr.id.data);
+ base = ED_object_find_first_by_data_id(view_layer, ptr.owner_id);
}
bool ok = false;
@@ -1096,60 +1107,6 @@ static void UI_OT_jump_to_target_button(wmOperatorType *ot)
/** \} */
/* -------------------------------------------------------------------- */
-/** \name Reports to Textblock Operator
- * \{ */
-
-/* FIXME: this is just a temporary operator so that we can see all the reports somewhere
- * when there are too many to display...
- */
-
-static bool reports_to_text_poll(bContext *C)
-{
- return CTX_wm_reports(C) != NULL;
-}
-
-static int reports_to_text_exec(bContext *C, wmOperator *UNUSED(op))
-{
- ReportList *reports = CTX_wm_reports(C);
- Main *bmain = CTX_data_main(C);
- Text *txt;
- char *str;
-
- /* create new text-block to write to */
- txt = BKE_text_add(bmain, "Recent Reports");
-
- /* convert entire list to a display string, and add this to the text-block
- * - if commandline debug option enabled, show debug reports too
- * - otherwise, up to info (which is what users normally see)
- */
- str = BKE_reports_string(reports, (G.debug & G_DEBUG) ? RPT_DEBUG : RPT_INFO);
-
- if (str) {
- BKE_text_write(txt, str);
- MEM_freeN(str);
-
- return OPERATOR_FINISHED;
- }
- else {
- return OPERATOR_CANCELLED;
- }
-}
-
-static void UI_OT_reports_to_textblock(wmOperatorType *ot)
-{
- /* identifiers */
- ot->name = "Reports to Text Block";
- ot->idname = "UI_OT_reports_to_textblock";
- ot->description = "Write the reports ";
-
- /* callbacks */
- ot->poll = reports_to_text_poll;
- ot->exec = reports_to_text_exec;
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
/** \name Edit Python Source Operator
* \{ */
@@ -1750,7 +1707,6 @@ void ED_operatortypes_ui(void)
WM_operatortype_append(UI_OT_override_remove_button);
WM_operatortype_append(UI_OT_copy_to_selected_button);
WM_operatortype_append(UI_OT_jump_to_target_button);
- WM_operatortype_append(UI_OT_reports_to_textblock); /* XXX: temp? */
WM_operatortype_append(UI_OT_drop_color);
#ifdef WITH_PYTHON
WM_operatortype_append(UI_OT_editsource);
diff --git a/source/blender/editors/interface/interface_region_popup.c b/source/blender/editors/interface/interface_region_popup.c
index f3e18cc2f24..2073117d51c 100644
--- a/source/blender/editors/interface/interface_region_popup.c
+++ b/source/blender/editors/interface/interface_region_popup.c
@@ -690,9 +690,11 @@ uiBlock *ui_popup_block_refresh(bContext *C,
/* Avoid menu moving down and losing cursor focus by keeping it at
* the same height. */
if (handle->refresh && handle->prev_block_rect.ymax > block->rect.ymax) {
- float offset = handle->prev_block_rect.ymax - block->rect.ymax;
- UI_block_translate(block, 0, offset);
- block->rect.ymin = handle->prev_block_rect.ymin;
+ if (block->bounds_type != UI_BLOCK_BOUNDS_POPUP_CENTER) {
+ float offset = handle->prev_block_rect.ymax - block->rect.ymax;
+ UI_block_translate(block, 0, offset);
+ block->rect.ymin = handle->prev_block_rect.ymin;
+ }
}
handle->prev_block_rect = block->rect;
diff --git a/source/blender/editors/interface/interface_region_tooltip.c b/source/blender/editors/interface/interface_region_tooltip.c
index 6aad9e41e7d..7387fe5eb1c 100644
--- a/source/blender/editors/interface/interface_region_tooltip.c
+++ b/source/blender/editors/interface/interface_region_tooltip.c
@@ -765,8 +765,8 @@ static uiTooltipData *ui_tooltip_data_from_button(bContext *C, uiBut *but)
}
}
- if (but->rnapoin.id.data) {
- const ID *id = but->rnapoin.id.data;
+ if (but->rnapoin.owner_id) {
+ const ID *id = but->rnapoin.owner_id;
if (ID_IS_LINKED(id)) {
uiTooltipField *field = text_field_add(data,
&(uiTooltipFormat){
@@ -847,7 +847,7 @@ static uiTooltipData *ui_tooltip_data_from_button(bContext *C, uiBut *but)
}
}
- if (but->rnapoin.id.data) {
+ if (but->rnapoin.owner_id) {
uiTooltipField *field = text_field_add(data,
&(uiTooltipFormat){
.style = UI_TIP_STYLE_MONO,
@@ -860,10 +860,10 @@ static uiTooltipData *ui_tooltip_data_from_button(bContext *C, uiBut *but)
/* move ownership (no need for re-alloc) */
if (but->rnaprop) {
field->text = RNA_path_full_property_py_ex(
- &but->rnapoin, but->rnaprop, but->rnaindex, true);
+ CTX_data_main(C), &but->rnapoin, but->rnaprop, but->rnaindex, true);
}
else {
- field->text = RNA_path_full_struct_py(&but->rnapoin);
+ field->text = RNA_path_full_struct_py(CTX_data_main(C), &but->rnapoin);
}
}
}
diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c
index 37eb1770f68..f53bef877c4 100644
--- a/source/blender/editors/interface/interface_templates.c
+++ b/source/blender/editors/interface/interface_templates.c
@@ -121,7 +121,7 @@ static void template_add_button_search_menu(const bContext *C,
{
PointerRNA active_ptr = RNA_property_pointer_get(ptr, prop);
ID *id = (active_ptr.data && RNA_struct_is_ID(active_ptr.type)) ? active_ptr.data : NULL;
- const ID *idfrom = ptr->id.data;
+ const ID *idfrom = ptr->owner_id;
const StructRNA *type = active_ptr.type ? active_ptr.type : RNA_property_pointer_type(ptr, prop);
uiBut *but;
@@ -310,7 +310,7 @@ static bool id_search_add(const bContext *C,
uiSearchItems *items,
ID *id)
{
- ID *id_from = template_ui->ptr.id.data;
+ ID *id_from = template_ui->ptr.owner_id;
if (!((flag & PROP_ID_SELF_CHECK) && id == id_from)) {
@@ -401,7 +401,7 @@ static void id_search_cb_objects_from_scene(const bContext *C,
TemplateID *template_ui = (TemplateID *)arg_template;
ListBase *lb = template_ui->idlb;
Scene *scene = NULL;
- ID *id_from = template_ui->ptr.id.data;
+ ID *id_from = template_ui->ptr.owner_id;
if (id_from && GS(id_from->name) == ID_SCE) {
scene = (Scene *)id_from;
@@ -538,7 +538,7 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event)
break;
case UI_ID_OVERRIDE:
if (id && id->override_library) {
- BKE_override_library_free(&id->override_library);
+ BKE_override_library_free(&id->override_library, true);
/* reassign to get get proper updates/notifiers */
idptr = RNA_property_pointer_get(&template_ui->ptr, template_ui->prop);
RNA_property_pointer_set(&template_ui->ptr, template_ui->prop, idptr, NULL);
@@ -667,7 +667,7 @@ static uiBut *template_id_def_new_but(uiBlock *block,
const bool use_tab_but,
int but_height)
{
- ID *idfrom = template_ui->ptr.id.data;
+ ID *idfrom = template_ui->ptr.owner_id;
uiBut *but;
const int w = id ? UI_UNIT_X : id_open ? UI_UNIT_X * 3 : UI_UNIT_X * 6;
const int but_type = use_tab_but ? UI_BTYPE_TAB : UI_BTYPE_BUT;
@@ -769,7 +769,7 @@ static void template_ID(bContext *C,
idptr = RNA_property_pointer_get(&template_ui->ptr, template_ui->prop);
id = idptr.data;
- idfrom = template_ui->ptr.id.data;
+ idfrom = template_ui->ptr.owner_id;
// lb = template_ui->idlb;
block = uiLayoutGetBlock(layout);
@@ -2037,7 +2037,7 @@ uiLayout *uiTemplateModifier(uiLayout *layout, bContext *C, PointerRNA *ptr)
return NULL;
}
- ob = ptr->id.data;
+ ob = (Object *)ptr->owner_id;
md = ptr->data;
if (!ob || !(GS(ob->id.name) == ID_OB)) {
@@ -2179,7 +2179,7 @@ uiLayout *uiTemplateGpencilModifier(uiLayout *layout, bContext *UNUSED(C), Point
return NULL;
}
- ob = ptr->id.data;
+ ob = (Object *)ptr->owner_id;
md = ptr->data;
if (!ob || !(GS(ob->id.name) == ID_OB)) {
@@ -2297,7 +2297,7 @@ uiLayout *uiTemplateShaderFx(uiLayout *layout, bContext *UNUSED(C), PointerRNA *
return NULL;
}
- ob = ptr->id.data;
+ ob = (Object *)ptr->owner_id;
fx = ptr->data;
if (!ob || !(GS(ob->id.name) == ID_OB)) {
@@ -2523,7 +2523,7 @@ static uiLayout *draw_constraint(uiLayout *layout, Object *ob, bConstraint *con)
/* enabled */
UI_block_emboss_set(block, UI_EMBOSS_NONE);
- uiItemR(row, &ptr, "mute", 0, "", (con->flag & CONSTRAINT_OFF) ? ICON_HIDE_ON : ICON_HIDE_OFF);
+ uiItemR(row, &ptr, "mute", 0, "", 0);
UI_block_emboss_set(block, UI_EMBOSS);
uiLayoutSetOperatorContext(row, WM_OP_INVOKE_DEFAULT);
@@ -2579,7 +2579,7 @@ uiLayout *uiTemplateConstraint(uiLayout *layout, PointerRNA *ptr)
return NULL;
}
- ob = ptr->id.data;
+ ob = (Object *)ptr->owner_id;
con = ptr->data;
if (!ob || !(GS(ob->id.name) == ID_OB)) {
@@ -3098,7 +3098,7 @@ static void colorband_buttons_layout(uiLayout *layout,
float ys = butr->ymin;
PointerRNA ptr;
- RNA_pointer_create(cb->ptr.id.data, &RNA_ColorRamp, coba, &ptr);
+ RNA_pointer_create(cb->ptr.owner_id, &RNA_ColorRamp, coba, &ptr);
split = uiLayoutSplit(layout, 0.4f, false);
@@ -3190,7 +3190,7 @@ static void colorband_buttons_layout(uiLayout *layout,
if (coba->tot) {
CBData *cbd = coba->data + coba->cur;
- RNA_pointer_create(cb->ptr.id.data, &RNA_ColorRampElement, cbd, &ptr);
+ RNA_pointer_create(cb->ptr.owner_id, &RNA_ColorRampElement, cbd, &ptr);
if (!expand) {
split = uiLayoutSplit(layout, 0.3f, false);
@@ -3207,7 +3207,7 @@ static void colorband_buttons_layout(uiLayout *layout,
&coba->cur,
0.0,
(float)(MAX2(0, coba->tot - 1)),
- 0,
+ 1,
0,
TIP_("Choose active color stop"));
row = uiLayoutRow(split, false);
@@ -3237,7 +3237,7 @@ static void colorband_buttons_layout(uiLayout *layout,
&coba->cur,
0.0,
(float)(MAX2(0, coba->tot - 1)),
- 0,
+ 1,
0,
TIP_("Choose active color stop"));
row = uiLayoutRow(subsplit, false);
@@ -3283,7 +3283,7 @@ void uiTemplateColorRamp(uiLayout *layout, PointerRNA *ptr, const char *propname
block = uiLayoutAbsoluteBlock(layout);
- id = cptr.id.data;
+ id = cptr.owner_id;
UI_block_lock_set(block, (id && ID_IS_LINKED(id)), ERROR_LIBDATA_MESSAGE);
colorband_buttons_layout(layout, block, cptr.data, &rect, cb, expand);
@@ -3745,12 +3745,11 @@ static uiBlock *curvemap_clipping_func(bContext *C, ARegion *ar, void *cumap_v)
float width = 8 * UI_UNIT_X;
block = UI_block_begin(C, ar, __func__, UI_EMBOSS);
-
- /* use this for a fake extra empty space around the buttons */
- uiDefBut(block, UI_BTYPE_LABEL, 0, "", -4, 16, width + 8, 6 * UI_UNIT_Y, NULL, 0, 0, 0, 0, "");
+ UI_block_flag_enable(block, UI_BLOCK_KEEP_OPEN | UI_BLOCK_MOVEMOUSE_QUIT);
+ UI_block_theme_style_set(block, UI_BLOCK_THEME_STYLE_POPUP);
bt = uiDefButBitI(block,
- UI_BTYPE_TOGGLE,
+ UI_BTYPE_CHECKBOX,
CUMA_DO_CLIP,
1,
IFACE_("Use Clipping"),
@@ -3770,7 +3769,7 @@ static uiBlock *curvemap_clipping_func(bContext *C, ARegion *ar, void *cumap_v)
uiDefButF(block,
UI_BTYPE_NUM,
0,
- IFACE_("Min X "),
+ IFACE_("Min X:"),
0,
4 * UI_UNIT_Y,
width,
@@ -3784,7 +3783,7 @@ static uiBlock *curvemap_clipping_func(bContext *C, ARegion *ar, void *cumap_v)
uiDefButF(block,
UI_BTYPE_NUM,
0,
- IFACE_("Min Y "),
+ IFACE_("Min Y:"),
0,
3 * UI_UNIT_Y,
width,
@@ -3798,7 +3797,7 @@ static uiBlock *curvemap_clipping_func(bContext *C, ARegion *ar, void *cumap_v)
uiDefButF(block,
UI_BTYPE_NUM,
0,
- IFACE_("Max X "),
+ IFACE_("Max X:"),
0,
2 * UI_UNIT_Y,
width,
@@ -3812,7 +3811,7 @@ static uiBlock *curvemap_clipping_func(bContext *C, ARegion *ar, void *cumap_v)
uiDefButF(block,
UI_BTYPE_NUM,
0,
- IFACE_("Max Y "),
+ IFACE_("Max Y:"),
0,
UI_UNIT_Y,
width,
@@ -3824,7 +3823,8 @@ static uiBlock *curvemap_clipping_func(bContext *C, ARegion *ar, void *cumap_v)
2,
"");
- UI_block_direction_set(block, UI_DIR_RIGHT);
+ UI_block_bounds_set_normal(block, 0.3f * U.widget_unit);
+ UI_block_direction_set(block, UI_DIR_DOWN);
return block;
}
@@ -4405,7 +4405,7 @@ void uiTemplateCurveMapping(uiLayout *layout,
cb->ptr = *ptr;
cb->prop = prop;
- id = cptr.id.data;
+ id = cptr.owner_id;
UI_block_lock_set(block, (id && ID_IS_LINKED(id)), ERROR_LIBDATA_MESSAGE);
curvemap_buttons_layout(layout, &cptr, type, levels, brush, neg_slope, tone, cb);
@@ -6378,7 +6378,7 @@ void uiTemplateReportsBanner(uiLayout *layout, bContext *C)
if (reports->list.first != reports->list.last) {
uiDefIconButO(block,
UI_BTYPE_BUT,
- "UI_OT_reports_to_textblock",
+ "SCREEN_OT_info_log_show",
WM_OP_INVOKE_REGION_WIN,
icon,
2,
@@ -6392,22 +6392,17 @@ void uiTemplateReportsBanner(uiLayout *layout, bContext *C)
block, UI_BTYPE_LABEL, 0, icon, 2, 0, UI_UNIT_X, UI_UNIT_Y, NULL, 0.0f, 0.0f, 0, 0, "");
}
- UI_block_emboss_set(block, UI_EMBOSS);
-
- uiDefBut(block,
- UI_BTYPE_LABEL,
- 0,
- report->message,
- UI_UNIT_X + 5,
- 0,
- UI_UNIT_X + width,
- UI_UNIT_Y,
- NULL,
- 0.0f,
- 0.0f,
- 0,
- 0,
- "");
+ but = uiDefButO(block,
+ UI_BTYPE_BUT,
+ "SCREEN_OT_info_log_show",
+ WM_OP_INVOKE_REGION_WIN,
+ report->message,
+ UI_UNIT_X + 5,
+ 0,
+ UI_UNIT_X + width,
+ UI_UNIT_Y,
+ "Show in Info Log");
+ rgba_float_to_uchar(but->col, rti->col);
}
void uiTemplateInputStatus(uiLayout *layout, struct bContext *C)
@@ -6627,7 +6622,7 @@ static uiBlock *component_menu(bContext *C, ARegion *ar, void *args_v)
uiItemR(layout, &args->ptr, args->propname, UI_ITEM_R_EXPAND, "", ICON_NONE);
- UI_block_bounds_set_normal(block, 6);
+ UI_block_bounds_set_normal(block, 0.3f * U.widget_unit);
UI_block_direction_set(block, UI_DIR_DOWN);
return block;
diff --git a/source/blender/editors/interface/interface_utils.c b/source/blender/editors/interface/interface_utils.c
index 9c8787d002f..63e382e2280 100644
--- a/source/blender/editors/interface/interface_utils.c
+++ b/source/blender/editors/interface/interface_utils.c
@@ -405,7 +405,7 @@ void ui_rna_collection_search_cb(const struct bContext *C,
RNA_PROP_BEGIN (&data->search_ptr, itemptr, data->search_prop) {
if (flag & PROP_ID_SELF_CHECK) {
- if (itemptr.data == data->target_ptr.id.data) {
+ if (itemptr.data == data->target_ptr.owner_id) {
continue;
}
}
@@ -566,7 +566,7 @@ int UI_calc_float_precision(int prec, double value)
bool UI_but_online_manual_id(const uiBut *but, char *r_str, size_t maxlength)
{
- if (but->rnapoin.id.data && but->rnapoin.data && but->rnaprop) {
+ if (but->rnapoin.owner_id && but->rnapoin.data && but->rnaprop) {
BLI_snprintf(r_str,
maxlength,
"%s.%s",
diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c
index 9c4d628cec5..8d22c5b85bb 100644
--- a/source/blender/editors/interface/interface_widgets.c
+++ b/source/blender/editors/interface/interface_widgets.c
@@ -2188,7 +2188,7 @@ static void widget_draw_text(const uiFontStyle *fstyle,
drawstr_right = strrchr(drawstr + but->ofs, ':');
if (drawstr_right) {
drawstr_right++;
- drawstr_left_len = (drawstr_right - drawstr);
+ drawstr_left_len = (drawstr_right - drawstr - 1);
while (*drawstr_right == ' ') {
drawstr_right++;
@@ -2587,6 +2587,10 @@ static void widget_state(uiWidgetType *wt, int state, int drawflag)
}
}
else {
+ if (state & UI_BUT_ACTIVE_DEFAULT) {
+ copy_v4_v4_uchar(wt->wcol.inner, wt->wcol.inner_sel);
+ copy_v4_v4_uchar(wt->wcol.text, wt->wcol.text_sel);
+ }
if (color_blend != NULL) {
widget_state_blend(wt->wcol.inner, color_blend, wcol_state->blend);
}
@@ -2709,10 +2713,10 @@ static void widget_state_menu_item(uiWidgetType *wt, int state, int UNUSED(drawf
/* active and disabled (not so common) */
if ((state & UI_BUT_DISABLED) && (state & UI_ACTIVE)) {
- widget_state_blend(wt->wcol.text, wt->wcol.text_sel, 0.5f);
/* draw the backdrop at low alpha, helps navigating with keys
* when disabled items are active */
- copy_v4_v4_uchar(wt->wcol.inner, wt->wcol.inner_sel);
+ wt->wcol.text[3] = 128;
+ widget_state_blend(wt->wcol.inner, wt->wcol.text, 0.5f);
wt->wcol.inner[3] = 64;
}
else {
@@ -3268,20 +3272,6 @@ static void ui_draw_but_HSV_v(uiBut *but, const rcti *rect)
ui_hsv_cursor(x, y);
}
-/* Generic round-box drawing. */
-static void ui_draw_roundbox(const rcti *rect, const float rad, const uiWidgetColors *wcol)
-{
- uiWidgetBase wtb;
- widget_init(&wtb);
- round_box_edges(&wtb, UI_CNR_ALL, rect, rad);
- widgetbase_draw(&wtb, wcol);
-
- /* We are drawing on top of widget bases. Flush cache. */
- GPU_blend(true);
- UI_widgetbase_draw_cache_flush();
- GPU_blend(false);
-}
-
/* ************ separator, for menus etc ***************** */
static void ui_draw_separator(const rcti *rect, const uiWidgetColors *wcol)
{
@@ -3802,7 +3792,7 @@ static void widget_swatch(
widgetbase_draw_ex(&wtb, wcol, show_alpha_checkers);
if (but->a1 == UI_PALETTE_COLOR &&
- ((Palette *)but->rnapoin.id.data)->active_color == (int)but->a2) {
+ ((Palette *)but->rnapoin.owner_id)->active_color == (int)but->a2) {
float width = rect->xmax - rect->xmin;
float height = rect->ymax - rect->ymin;
/* find color luminance and change it slightly */
@@ -4497,10 +4487,6 @@ void ui_draw_but(const bContext *C, ARegion *ar, uiStyle *style, uiBut *but, rct
const uiFontStyle *fstyle = &style->widget;
uiWidgetType *wt = NULL;
-#ifdef USE_UI_POPOVER_ONCE
- const rcti rect_orig = *rect;
-#endif
-
/* handle menus separately */
if (but->dt == UI_EMBOSS_PULLDOWN) {
switch (but->type) {
@@ -4786,6 +4772,14 @@ void ui_draw_but(const bContext *C, ARegion *ar, uiStyle *style, uiBut *but, rct
ui_widget_color_disabled(wt);
}
+#ifdef USE_UI_POPOVER_ONCE
+ if (but->block->flag & UI_BLOCK_POPOVER_ONCE) {
+ if ((state & UI_ACTIVE) && ui_but_is_popover_once_compat(but)) {
+ state |= UI_BUT_ACTIVE_DEFAULT;
+ }
+ }
+#endif
+
wt->state(wt, state, drawflag);
if (wt->custom) {
wt->custom(but, &wt->wcol, rect, state, roundboxalign);
@@ -4798,42 +4792,10 @@ void ui_draw_but(const bContext *C, ARegion *ar, uiStyle *style, uiBut *but, rct
GPU_blend(true);
}
- bool show_semi_highlight = false;
-
-#ifdef USE_UI_POPOVER_ONCE
- if (but->block->flag & UI_BLOCK_POPOVER_ONCE) {
- if ((state & UI_ACTIVE) && ui_but_is_popover_once_compat(but)) {
- show_semi_highlight = true;
- }
- }
-#endif
- if (but->flag & UI_BUT_ACTIVE_DEFAULT) {
- show_semi_highlight = true;
- }
-
- if (show_semi_highlight) {
- uiWidgetType wt_back = *wt;
- uiWidgetType *wt_temp = widget_type(UI_WTYPE_MENU_ITEM);
- wt_temp->state(wt_temp, state, drawflag);
- copy_v4_v4_uchar(wt->wcol.inner, wt->wcol.inner_sel);
- wt->wcol.inner[3] = 128;
- wt->wcol.roundness = 0.5f;
- ui_draw_roundbox(&rect_orig,
- 0.25f * min_ff(BLI_rcti_size_x(&rect_orig), BLI_rcti_size_y(&rect_orig)),
- &wt_temp->wcol);
- *wt = wt_back;
- }
-
wt->text(fstyle, &wt->wcol, but, rect);
if (disabled) {
GPU_blend(false);
}
-
- // if (state & (UI_BUT_DISABLED | UI_BUT_INACTIVE)) {
- // if (but->dt != UI_EMBOSS_PULLDOWN) {
- // widget_disabled(&disablerect);
- // }
- // }
}
}
diff --git a/source/blender/editors/interface/resources.c b/source/blender/editors/interface/resources.c
index 1e74ee50dab..93a93fb6918 100644
--- a/source/blender/editors/interface/resources.c
+++ b/source/blender/editors/interface/resources.c
@@ -794,6 +794,10 @@ const uchar *UI_ThemeGetColorPtr(bTheme *btheme, int spacetype, int colorid)
cp = ts->selected_highlight;
break;
+ case TH_SELECT_ACTIVE:
+ cp = ts->active;
+ break;
+
case TH_SELECTED_OBJECT:
cp = ts->selected_object;
break;
diff --git a/source/blender/editors/interface/view2d_ops.c b/source/blender/editors/interface/view2d_ops.c
index f32fcffabd4..032fb7e4cc2 100644
--- a/source/blender/editors/interface/view2d_ops.c
+++ b/source/blender/editors/interface/view2d_ops.c
@@ -1844,7 +1844,7 @@ static void scroller_activate_init(bContext *C,
*/
scrollers = UI_view2d_scrollers_calc(v2d, NULL);
- /* use a union of 'cur' & 'tot' incase the current view is far outside 'tot'. In this cases
+ /* Use a union of 'cur' & 'tot' in case the current view is far outside 'tot'. In this cases
* moving the scroll bars has far too little effect and the view can get stuck T31476. */
tot_cur_union = v2d->tot;
BLI_rctf_union(&tot_cur_union, &v2d->cur);
diff --git a/source/blender/editors/mask/mask_shapekey.c b/source/blender/editors/mask/mask_shapekey.c
index edc22943124..1abe805192d 100644
--- a/source/blender/editors/mask/mask_shapekey.c
+++ b/source/blender/editors/mask/mask_shapekey.c
@@ -81,6 +81,7 @@ void MASK_OT_shape_key_insert(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Insert Shape Key";
+ ot->description = "Insert mask shape keyframe for active mask layer at the current frame";
ot->idname = "MASK_OT_shape_key_insert";
/* api callbacks */
@@ -129,6 +130,7 @@ void MASK_OT_shape_key_clear(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Clear Shape Key";
+ ot->description = "Remove mask shape keyframe for active mask layer at the current frame";
ot->idname = "MASK_OT_shape_key_clear";
/* api callbacks */
diff --git a/source/blender/editors/mesh/CMakeLists.txt b/source/blender/editors/mesh/CMakeLists.txt
index 9a779db4812..d7d020ae19d 100644
--- a/source/blender/editors/mesh/CMakeLists.txt
+++ b/source/blender/editors/mesh/CMakeLists.txt
@@ -43,6 +43,7 @@ set(SRC
editface.c
editmesh_add.c
editmesh_add_gizmo.c
+ editmesh_automerge.c
editmesh_bevel.c
editmesh_bisect.c
editmesh_extrude.c
diff --git a/source/blender/editors/mesh/editmesh_automerge.c b/source/blender/editors/mesh/editmesh_automerge.c
new file mode 100644
index 00000000000..82f53aafad8
--- /dev/null
+++ b/source/blender/editors/mesh/editmesh_automerge.c
@@ -0,0 +1,517 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2019 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup edmesh
+ *
+ * Utility functions for merging geometry once transform has finished:
+ *
+ * - #EDBM_automerge
+ * - #EDBM_automerge_and_split
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_bitmap.h"
+#include "BLI_math.h"
+#include "BLI_sort.h"
+
+#include "BKE_bvhutils.h"
+#include "BKE_editmesh.h"
+
+#include "WM_api.h"
+
+#include "ED_mesh.h"
+#include "ED_screen.h"
+#include "ED_transform.h"
+
+#include "DNA_object_types.h"
+
+#include "DEG_depsgraph.h"
+
+/* use bmesh operator flags for a few operators */
+#define BMO_ELE_TAG 1
+
+/* -------------------------------------------------------------------- */
+/** \name Auto-Merge Selection
+ *
+ * Used after transform operations.
+ * \{ */
+
+void EDBM_automerge(Object *obedit, bool update, const char hflag, const float dist)
+{
+ BMEditMesh *em = BKE_editmesh_from_object(obedit);
+ BMesh *bm = em->bm;
+ int totvert_prev = bm->totvert;
+
+ BMOperator findop, weldop;
+
+ /* Search for doubles among all vertices, but only merge non-VERT_KEEP
+ * vertices into VERT_KEEP vertices. */
+ BMO_op_initf(bm,
+ &findop,
+ BMO_FLAG_DEFAULTS,
+ "find_doubles verts=%av keep_verts=%Hv dist=%f",
+ hflag,
+ dist);
+
+ BMO_op_exec(bm, &findop);
+
+ /* weld the vertices */
+ BMO_op_init(bm, &weldop, BMO_FLAG_DEFAULTS, "weld_verts");
+ BMO_slot_copy(&findop, slots_out, "targetmap.out", &weldop, slots_in, "targetmap");
+ BMO_op_exec(bm, &weldop);
+
+ BMO_op_finish(bm, &findop);
+ BMO_op_finish(bm, &weldop);
+
+ if ((totvert_prev != bm->totvert) && update) {
+ EDBM_update_generic(em, true, true);
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Auto-Merge & Split Selection
+ *
+ * Used after transform operations.
+ * \{ */
+
+struct EDBMSplitEdge {
+ BMVert *v;
+ BMEdge *e;
+ float lambda;
+};
+
+struct EDBMSplitBestFaceData {
+ BMEdge **edgenet;
+ int edgenet_len;
+
+ /**
+ * Track the range of vertices in edgenet along the faces normal,
+ * find the lowest since it's most likely to be most co-planar with the face.
+ */
+ float best_face_range_on_normal_axis;
+ BMFace *r_best_face;
+};
+
+struct EDBMSplitEdgeData {
+ BMesh *bm;
+
+ BMEdge *r_edge;
+ float r_lambda;
+};
+
+static bool edbm_vert_pair_share_best_splittable_face_cb(BMFace *f,
+ BMLoop *UNUSED(l_a),
+ BMLoop *UNUSED(l_b),
+ void *userdata)
+{
+ struct EDBMSplitBestFaceData *data = userdata;
+ float no[3], min = FLT_MAX, max = -FLT_MAX;
+ copy_v3_v3(no, f->no);
+
+ BMVert *verts[2] = {NULL};
+ BMEdge **e_iter = &data->edgenet[0];
+ for (int i = data->edgenet_len; i--; e_iter++) {
+ BMIter iter;
+ BMVert *v;
+ BM_ITER_ELEM (v, &iter, *e_iter, BM_VERTS_OF_EDGE) {
+ if (!ELEM(v, verts[0], verts[1])) {
+ float dot = dot_v3v3(v->co, no);
+ if (dot < min) {
+ min = dot;
+ }
+ if (dot > max) {
+ max = dot;
+ }
+ }
+ }
+ verts[0] = (*e_iter)->v1;
+ verts[1] = (*e_iter)->v2;
+ }
+
+ const float test_face_range_on_normal_axis = max - min;
+ if (test_face_range_on_normal_axis < data->best_face_range_on_normal_axis) {
+ data->best_face_range_on_normal_axis = test_face_range_on_normal_axis;
+ data->r_best_face = f;
+ }
+
+ return false;
+}
+
+/* find the best splittable face between the two vertices. */
+static bool edbm_vert_pair_share_splittable_face_cb(BMFace *UNUSED(f),
+ BMLoop *l_a,
+ BMLoop *l_b,
+ void *userdata)
+{
+ float(*data)[3] = userdata;
+ float *v_a_co = data[0];
+ float *v_a_b_dir = data[1];
+
+ float lambda;
+ if (isect_ray_seg_v3(v_a_co, v_a_b_dir, l_a->prev->v->co, l_a->next->v->co, &lambda)) {
+ if (IN_RANGE(lambda, 0.0f, 1.0f)) {
+ return true;
+ }
+ else if (isect_ray_seg_v3(v_a_co, v_a_b_dir, l_b->prev->v->co, l_b->next->v->co, &lambda)) {
+ return IN_RANGE(lambda, 0.0f, 1.0f);
+ }
+ }
+ return false;
+}
+
+static void edbm_automerge_weld_linked_wire_edges_into_linked_faces(BMesh *bm,
+ BMVert *v,
+ BMEdge **r_edgenet[],
+ int *r_edgenet_alloc_len)
+{
+ BMEdge **edgenet = *r_edgenet;
+ int edgenet_alloc_len = *r_edgenet_alloc_len;
+
+ BMIter iter;
+ BMEdge *e;
+ BM_ITER_ELEM (e, &iter, v, BM_EDGES_OF_VERT) {
+ int edgenet_len = 0;
+ BMVert *v_other = v;
+ while (BM_edge_is_wire(e)) {
+ if (edgenet_alloc_len == edgenet_len) {
+ edgenet_alloc_len = (edgenet_alloc_len + 1) * 2;
+ edgenet = MEM_reallocN(edgenet, (edgenet_alloc_len) * sizeof(*edgenet));
+ }
+ edgenet[edgenet_len++] = e;
+ v_other = BM_edge_other_vert(e, v_other);
+ if (v_other == v) {
+ /* Endless loop. */
+ break;
+ }
+
+ BMEdge *e_next = BM_DISK_EDGE_NEXT(e, v_other);
+ if (e_next == e) {
+ /* Vert is wire_endpoint */
+ edgenet_len = 0;
+ break;
+ }
+ e = e_next;
+ }
+
+ BMLoop *dummy;
+ BMFace *best_face;
+ if (edgenet_len == 0) {
+ /* Nothing to do. */
+ continue;
+ }
+ if (edgenet_len == 1) {
+ float data[2][3];
+ copy_v3_v3(data[0], v_other->co);
+ sub_v3_v3v3(data[1], v->co, data[0]);
+ best_face = BM_vert_pair_shared_face_cb(
+ v_other, v, true, edbm_vert_pair_share_splittable_face_cb, &data, &dummy, &dummy);
+ }
+ else {
+ struct EDBMSplitBestFaceData data = {
+ .edgenet = edgenet,
+ .edgenet_len = edgenet_len,
+ .best_face_range_on_normal_axis = FLT_MAX,
+ .r_best_face = NULL,
+ };
+ BM_vert_pair_shared_face_cb(
+ v_other, v, true, edbm_vert_pair_share_best_splittable_face_cb, &data, &dummy, &dummy);
+
+ best_face = data.r_best_face;
+ }
+
+ if (best_face) {
+ BM_face_split_edgenet(bm, best_face, edgenet, edgenet_len, NULL, NULL);
+ }
+ }
+
+ *r_edgenet = edgenet;
+ *r_edgenet_alloc_len = edgenet_alloc_len;
+}
+
+static void ebbm_automerge_and_split_find_duplicate_cb(void *userdata,
+ int index,
+ const float co[3],
+ BVHTreeNearest *nearest)
+{
+ struct EDBMSplitEdgeData *data = userdata;
+ BMEdge *e = BM_edge_at_index(data->bm, index);
+ float lambda = line_point_factor_v3_ex(co, e->v1->co, e->v2->co, 0.0f, -1.0f);
+ if (IN_RANGE(lambda, 0.0f, 1.0f)) {
+ float near_co[3];
+ interp_v3_v3v3(near_co, e->v1->co, e->v2->co, lambda);
+ float dist_sq = len_squared_v3v3(near_co, co);
+ if (dist_sq < nearest->dist_sq) {
+ nearest->dist_sq = dist_sq;
+ nearest->index = index;
+
+ data->r_edge = e;
+ data->r_lambda = lambda;
+ }
+ }
+}
+
+static int edbm_automerge_and_split_sort_cmp_by_keys_cb(const void *index1_v,
+ const void *index2_v,
+ void *keys_v)
+{
+ const struct EDBMSplitEdge *cuts = keys_v;
+ const int *index1 = (int *)index1_v;
+ const int *index2 = (int *)index2_v;
+
+ if (cuts[*index1].lambda > cuts[*index2].lambda) {
+ return 1;
+ }
+ else {
+ return -1;
+ }
+}
+
+void EDBM_automerge_and_split(Object *obedit,
+ bool split_edges,
+ bool split_faces,
+ bool update,
+ const char hflag,
+ const float dist)
+{
+ bool ok = false;
+
+ BMEditMesh *em = BKE_editmesh_from_object(obedit);
+ BMesh *bm = em->bm;
+ BMOperator findop, weldop;
+ BMOpSlot *slot_targetmap;
+ BMIter iter;
+ BMVert *v;
+
+ /* tag and count the verts to be tested. */
+ BM_mesh_elem_toolflags_ensure(bm);
+ int verts_len = 0;
+ BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
+ if (BM_elem_flag_test(v, hflag)) {
+ BM_elem_flag_enable(v, BM_ELEM_TAG);
+ BMO_vert_flag_enable(bm, v, BMO_ELE_TAG);
+ verts_len++;
+ }
+ else {
+ BM_elem_flag_disable(v, BM_ELEM_TAG);
+ }
+ }
+
+ /* Search for doubles among all vertices, but only merge non-BMO_ELE_TAG
+ * vertices into BMO_ELE_TAG vertices. */
+ BMO_op_initf(bm, &findop, 0, "find_doubles verts=%av keep_verts=%Fv dist=%f", BMO_ELE_TAG, dist);
+ BMO_op_exec(bm, &findop);
+
+ /* Init weld_verts operator to later fill the targetmap. */
+ BMO_op_init(bm, &weldop, 0, "weld_verts");
+ BMO_slot_copy(&findop, slots_out, "targetmap.out", &weldop, slots_in, "targetmap");
+
+ slot_targetmap = BMO_slot_get(weldop.slots_in, "targetmap");
+
+ /* Remove duplicate vertices from the split edge test and check and split faces. */
+ GHashIterator gh_iter;
+ GHash *ghash_targetmap = BMO_SLOT_AS_GHASH(slot_targetmap);
+ GHASH_ITER (gh_iter, ghash_targetmap) {
+ v = BLI_ghashIterator_getKey(&gh_iter);
+ BMVert *v_dst = BLI_ghashIterator_getValue(&gh_iter);
+ if (!BM_elem_flag_test(v, BM_ELEM_TAG)) {
+ /* Should this happen? */
+ SWAP(BMVert *, v, v_dst);
+ }
+ BLI_assert(BM_elem_flag_test(v, BM_ELEM_TAG));
+ BM_elem_flag_disable(v, BM_ELEM_TAG);
+
+ ok = true;
+ verts_len--;
+ }
+
+ int totedge = bm->totedge;
+ if (totedge == 0 || verts_len == 0) {
+ split_edges = false;
+ }
+
+ if (split_edges) {
+ /* Count and tag edges. */
+ BMEdge *e;
+ int edges_len = 0;
+ BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
+ if (!BM_elem_flag_test(e, BM_ELEM_HIDDEN) && !BM_elem_flag_test(e->v1, BM_ELEM_TAG) &&
+ !BM_elem_flag_test(e->v2, BM_ELEM_TAG)) {
+ BM_elem_flag_enable(e, BM_ELEM_TAG);
+ edges_len++;
+ }
+ else {
+ BM_elem_flag_disable(e, BM_ELEM_TAG);
+ }
+ }
+
+ if (edges_len) {
+ /* Use `e->head.index` to count intersections. */
+ bm->elem_index_dirty &= ~BM_EDGE;
+
+ /* Create a BVHTree of edges with `dist` as epsilon. */
+ BVHTree *tree_edges = BLI_bvhtree_new(edges_len, dist, 2, 6);
+ int i;
+ BM_ITER_MESH_INDEX (e, &iter, bm, BM_EDGES_OF_MESH, i) {
+ if (BM_elem_flag_test(e, BM_ELEM_TAG)) {
+ float co[2][3];
+ copy_v3_v3(co[0], e->v1->co);
+ copy_v3_v3(co[1], e->v2->co);
+
+ BLI_bvhtree_insert(tree_edges, i, co[0], 2);
+
+ e->head.index = 0;
+ }
+ }
+ BLI_bvhtree_balance(tree_edges);
+
+ struct EDBMSplitEdge *cuts_iter, *cuts;
+
+ /* Store all intersections in this array. */
+ cuts = MEM_mallocN(verts_len * sizeof(*cuts), __func__);
+ cuts_iter = &cuts[0];
+
+ int cuts_len = 0;
+ int cut_edges_len = 0;
+ float dist_sq = SQUARE(dist);
+ struct EDBMSplitEdgeData data = {bm};
+
+ /* Start the search for intersections. */
+ BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
+ if (BM_elem_flag_test(v, BM_ELEM_TAG)) {
+ float co[3];
+ copy_v3_v3(co, v->co);
+ int e_index = BLI_bvhtree_find_nearest_first(
+ tree_edges, co, dist_sq, ebbm_automerge_and_split_find_duplicate_cb, &data);
+
+ if (e_index != -1) {
+ e = data.r_edge;
+ e->head.index++;
+
+ cuts_iter->v = v;
+ cuts_iter->e = e;
+ cuts_iter->lambda = data.r_lambda;
+ cuts_iter++;
+ cuts_len++;
+
+ if (BM_elem_flag_test(e, BM_ELEM_TAG)) {
+ BM_elem_flag_disable(e, BM_ELEM_TAG);
+ cut_edges_len++;
+ }
+ }
+ }
+ }
+ BLI_bvhtree_free(tree_edges);
+
+ if (cuts_len) {
+ /* Map intersections per edge. */
+ union {
+ struct {
+ int cuts_len;
+ int cuts_index[];
+ };
+ int as_int[0];
+ } * e_map_iter, *e_map;
+
+ e_map = MEM_mallocN((cut_edges_len * sizeof(*e_map)) +
+ (cuts_len * sizeof(*(e_map->cuts_index))),
+ __func__);
+
+ int map_len = 0;
+ cuts_iter = &cuts[0];
+ for (i = 0; i < cuts_len; i++, cuts_iter++) {
+ e = cuts_iter->e;
+ if (!BM_elem_flag_test(e, BM_ELEM_TAG)) {
+ BM_elem_flag_enable(e, BM_ELEM_TAG);
+ int e_cuts_len = e->head.index;
+
+ e_map_iter = (void *)&e_map->as_int[map_len];
+ e_map_iter->cuts_len = e_cuts_len;
+ e_map_iter->cuts_index[0] = i;
+
+ /* Use `e->head.index` to indicate which slot to fill with the `cuts` index. */
+ e->head.index = map_len + 1;
+ map_len += 1 + e_cuts_len;
+ }
+ else {
+ e_map->as_int[++e->head.index] = i;
+ }
+ }
+
+ /* Split Edges and Faces. */
+ for (i = 0; i < map_len;
+ e_map_iter = (void *)&e_map->as_int[i], i += 1 + e_map_iter->cuts_len) {
+
+ /* sort by lambda. */
+ BLI_qsort_r(e_map_iter->cuts_index,
+ e_map_iter->cuts_len,
+ sizeof(*(e_map->cuts_index)),
+ edbm_automerge_and_split_sort_cmp_by_keys_cb,
+ cuts);
+
+ float lambda, lambda_prev = 0.0f;
+ for (int j = 0; j < e_map_iter->cuts_len; j++) {
+ cuts_iter = &cuts[e_map_iter->cuts_index[j]];
+ lambda = (cuts_iter->lambda - lambda_prev) / (1.0f - lambda_prev);
+ lambda_prev = cuts_iter->lambda;
+ v = cuts_iter->v;
+ e = cuts_iter->e;
+
+ BMVert *v_new = BM_edge_split(bm, e, e->v1, NULL, lambda);
+
+ BMO_slot_map_elem_insert(&weldop, slot_targetmap, v_new, v);
+ }
+ }
+
+ ok = true;
+ MEM_freeN(e_map);
+ }
+
+ MEM_freeN(cuts);
+ }
+ }
+
+ BMO_op_exec(bm, &weldop);
+
+ BMEdge **edgenet = NULL;
+ int edgenet_alloc_len = 0;
+ if (split_faces) {
+ GHASH_ITER (gh_iter, ghash_targetmap) {
+ v = BLI_ghashIterator_getValue(&gh_iter);
+ BLI_assert(BM_elem_flag_test(v, hflag) || hflag == BM_ELEM_TAG);
+ edbm_automerge_weld_linked_wire_edges_into_linked_faces(bm, v, &edgenet, &edgenet_alloc_len);
+ }
+ }
+
+ if (edgenet) {
+ MEM_freeN(edgenet);
+ }
+
+ BMO_op_finish(bm, &findop);
+ BMO_op_finish(bm, &weldop);
+
+ if (LIKELY(ok) && update) {
+ EDBM_update_generic(em, true, true);
+ }
+}
+
+/** \} */
diff --git a/source/blender/editors/mesh/editmesh_extrude_spin_gizmo.c b/source/blender/editors/mesh/editmesh_extrude_spin_gizmo.c
index 74700e59e99..7155348fed5 100644
--- a/source/blender/editors/mesh/editmesh_extrude_spin_gizmo.c
+++ b/source/blender/editors/mesh/editmesh_extrude_spin_gizmo.c
@@ -1008,8 +1008,17 @@ static void gizmo_mesh_spin_redo_setup(const bContext *C, wmGizmoGroup *gzgroup)
});
}
- /* Become modal as soon as it's started. */
- gizmo_mesh_spin_redo_modal_from_setup(C, gzgroup);
+ wmWindow *win = CTX_wm_window(C);
+ if (win && win->active) {
+ bScreen *screen = WM_window_get_active_screen(win);
+ if (screen->active_region) {
+ ARegion *ar = CTX_wm_region(C);
+ if (screen->active_region == ar) {
+ /* Become modal as soon as it's started. */
+ gizmo_mesh_spin_redo_modal_from_setup(C, gzgroup);
+ }
+ }
+ }
}
static void gizmo_mesh_spin_redo_draw_prepare(const bContext *UNUSED(C), wmGizmoGroup *gzgroup)
diff --git a/source/blender/editors/mesh/editmesh_knife.c b/source/blender/editors/mesh/editmesh_knife.c
index bb584094580..61f9dc43c0f 100644
--- a/source/blender/editors/mesh/editmesh_knife.c
+++ b/source/blender/editors/mesh/editmesh_knife.c
@@ -2658,7 +2658,7 @@ static void knifetool_init_bmbvh(KnifeTool_OpData *kcd)
Object *obedit_eval = (Object *)DEG_get_evaluated_id(kcd->vc.depsgraph, &kcd->em->ob->id);
BMEditMesh *em_eval = BKE_editmesh_from_object(obedit_eval);
- kcd->cagecos = (const float(*)[3])BKE_editmesh_vertexCos_get(
+ kcd->cagecos = (const float(*)[3])BKE_editmesh_vert_coords_alloc(
kcd->vc.depsgraph, em_eval, scene_eval, NULL);
kcd->bmbvh = BKE_bmbvh_new_from_editmesh(
@@ -2949,7 +2949,7 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event)
case KNF_MODAL_ADD_CUT_CLOSED:
if (kcd->mode == MODE_DRAGGING) {
- /* shouldn't be possible with default key-layout, just incase... */
+ /* Shouldn't be possible with default key-layout, just in case. */
if (kcd->is_drag_hold) {
kcd->is_drag_hold = false;
knifetool_update_mval(kcd, kcd->curr.mval);
diff --git a/source/blender/editors/mesh/editmesh_polybuild.c b/source/blender/editors/mesh/editmesh_polybuild.c
index 088d1672cc9..21c850160dd 100644
--- a/source/blender/editors/mesh/editmesh_polybuild.c
+++ b/source/blender/editors/mesh/editmesh_polybuild.c
@@ -122,15 +122,160 @@ static bool edbm_preselect_or_active_init_viewcontext(bContext *C,
return ok;
}
+static int edbm_polybuild_transform_at_cursor_invoke(bContext *C,
+ wmOperator *UNUSED(op),
+ const wmEvent *UNUSED(event))
+{
+ ViewContext vc;
+ Base *basact = NULL;
+ BMElem *ele_act = NULL;
+ edbm_preselect_or_active_init_viewcontext(C, &vc, &basact, &ele_act);
+ BMEditMesh *em = vc.em;
+ BMesh *bm = em->bm;
+
+ invert_m4_m4(vc.obedit->imat, vc.obedit->obmat);
+ ED_view3d_init_mats_rv3d(vc.obedit, vc.rv3d);
+
+ if (!ele_act) {
+ return OPERATOR_CANCELLED;
+ }
+
+ edbm_selectmode_ensure(vc.scene, vc.em, SCE_SELECT_VERTEX);
+
+ edbm_flag_disable_all_multi(vc.view_layer, vc.v3d, BM_ELEM_SELECT);
+
+ if (ele_act->head.htype == BM_VERT) {
+ BM_vert_select_set(bm, (BMVert *)ele_act, true);
+ }
+ if (ele_act->head.htype == BM_EDGE) {
+ BM_edge_select_set(bm, (BMEdge *)ele_act, true);
+ }
+ if (ele_act->head.htype == BM_FACE) {
+ BM_face_select_set(bm, (BMFace *)ele_act, true);
+ }
+
+ EDBM_mesh_normals_update(em);
+ EDBM_update_generic(em, true, true);
+ if (basact != NULL) {
+ if (vc.view_layer->basact != basact) {
+ ED_object_base_activate(C, basact);
+ }
+ }
+ BM_select_history_store(bm, ele_act);
+ WM_event_add_mousemove(C);
+ return OPERATOR_FINISHED;
+}
+
+void MESH_OT_polybuild_transform_at_cursor(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Poly Build Transform at Cursor";
+ ot->idname = "MESH_OT_polybuild_transform_at_cursor";
+
+ /* api callbacks */
+ ot->invoke = edbm_polybuild_transform_at_cursor_invoke;
+ ot->poll = EDBM_view3d_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* to give to transform */
+ Transform_Properties(ot, P_PROPORTIONAL | P_MIRROR_DUMMY);
+}
+
+static int edbm_polybuild_delete_at_cursor_invoke(bContext *C,
+ wmOperator *op,
+ const wmEvent *UNUSED(event))
+{
+ bool changed = false;
+
+ ViewContext vc;
+ Base *basact = NULL;
+ BMElem *ele_act = NULL;
+ edbm_preselect_or_active_init_viewcontext(C, &vc, &basact, &ele_act);
+ BMEditMesh *em = vc.em;
+ BMesh *bm = em->bm;
+
+ invert_m4_m4(vc.obedit->imat, vc.obedit->obmat);
+ ED_view3d_init_mats_rv3d(vc.obedit, vc.rv3d);
+
+ if (!ele_act) {
+ return OPERATOR_CANCELLED;
+ }
+
+ edbm_selectmode_ensure(vc.scene, vc.em, SCE_SELECT_VERTEX);
+
+ if (ele_act->head.htype == BM_FACE) {
+ BMFace *f_act = (BMFace *)ele_act;
+ EDBM_flag_disable_all(em, BM_ELEM_TAG);
+ BM_elem_flag_enable(f_act, BM_ELEM_TAG);
+ if (!EDBM_op_callf(em, op, "delete geom=%hf context=%i", BM_ELEM_TAG, DEL_FACES)) {
+ return OPERATOR_CANCELLED;
+ }
+ changed = true;
+ }
+ if (ele_act->head.htype == BM_VERT) {
+ BMVert *v_act = (BMVert *)ele_act;
+ if (BM_vert_is_edge_pair(v_act)) {
+ BM_edge_collapse(bm, v_act->e, v_act, true, true);
+ changed = true;
+ }
+ else {
+ EDBM_flag_disable_all(em, BM_ELEM_TAG);
+ BM_elem_flag_enable(v_act, BM_ELEM_TAG);
+
+ if (!EDBM_op_callf(em,
+ op,
+ "dissolve_verts verts=%hv use_face_split=%b use_boundary_tear=%b",
+ BM_ELEM_TAG,
+ false,
+ false)) {
+ return OPERATOR_CANCELLED;
+ }
+ changed = true;
+ }
+ }
+
+ if (changed) {
+ EDBM_mesh_normals_update(em);
+ EDBM_update_generic(em, true, true);
+ if (basact != NULL) {
+ if (vc.view_layer->basact != basact) {
+ ED_object_base_activate(C, basact);
+ }
+ }
+ WM_event_add_mousemove(C);
+ return OPERATOR_FINISHED;
+ }
+ else {
+ return OPERATOR_CANCELLED;
+ }
+}
+
+void MESH_OT_polybuild_delete_at_cursor(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Poly Build Delete at Cursor";
+ ot->idname = "MESH_OT_polybuild_delete_at_cursor";
+
+ /* api callbacks */
+ ot->invoke = edbm_polybuild_delete_at_cursor_invoke;
+ ot->poll = EDBM_view3d_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* to give to transform */
+ Transform_Properties(ot, P_PROPORTIONAL | P_MIRROR_DUMMY);
+}
+
/** \} */
/* -------------------------------------------------------------------- */
/** \name Face at Cursor
* \{ */
-static int edbm_polybuild_face_at_cursor_invoke(bContext *C,
- wmOperator *UNUSED(op),
- const wmEvent *event)
+static int edbm_polybuild_face_at_cursor_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
float center[3];
bool changed = false;
@@ -168,20 +313,27 @@ static int edbm_polybuild_face_at_cursor_invoke(bContext *C,
mul_m4_v3(vc.obedit->obmat, center);
ED_view3d_win_to_3d_int(vc.v3d, vc.ar, center, event->mval, center);
mul_m4_v3(vc.obedit->imat, center);
-
- BMVert *v_tri[3];
- v_tri[0] = e_act->v1;
- v_tri[1] = e_act->v2;
- v_tri[2] = BM_vert_create(bm, center, NULL, BM_CREATE_NOP);
- if (e_act->l && e_act->l->v == v_tri[0]) {
- SWAP(BMVert *, v_tri[0], v_tri[1]);
+ if (f_reference->len == 3 && RNA_boolean_get(op->ptr, "create_quads")) {
+ const float fac = line_point_factor_v3(center, e_act->v1->co, e_act->v2->co);
+ BMVert *v_new = BM_edge_split(bm, e_act, e_act->v1, NULL, CLAMPIS(fac, 0.0f, 1.0f));
+ copy_v3_v3(v_new->co, center);
+ edbm_flag_disable_all_multi(vc.view_layer, vc.v3d, BM_ELEM_SELECT);
+ BM_vert_select_set(bm, v_new, true);
+ BM_select_history_store(bm, v_new);
+ }
+ else {
+ BMVert *v_tri[3];
+ v_tri[0] = e_act->v1;
+ v_tri[1] = e_act->v2;
+ v_tri[2] = BM_vert_create(bm, center, NULL, BM_CREATE_NOP);
+ if (e_act->l && e_act->l->v == v_tri[0]) {
+ SWAP(BMVert *, v_tri[0], v_tri[1]);
+ }
+ BM_face_create_verts(bm, v_tri, 3, f_reference, BM_CREATE_NOP, true);
+ edbm_flag_disable_all_multi(vc.view_layer, vc.v3d, BM_ELEM_SELECT);
+ BM_vert_select_set(bm, v_tri[2], true);
+ BM_select_history_store(bm, v_tri[2]);
}
- // BMFace *f_new =
- BM_face_create_verts(bm, v_tri, 3, f_reference, BM_CREATE_NOP, true);
-
- edbm_flag_disable_all_multi(vc.view_layer, vc.v3d, BM_ELEM_SELECT);
- BM_vert_select_set(bm, v_tri[2], true);
- BM_select_history_store(bm, v_tri[2]);
changed = true;
}
else if (ele_act->head.htype == BM_VERT) {
@@ -281,6 +433,11 @@ void MESH_OT_polybuild_face_at_cursor(wmOperatorType *ot)
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+ RNA_def_boolean(ot->srna,
+ "create_quads",
+ true,
+ "Create quads",
+ "Automatically split edges in triangles to maintain quad topology");
/* to give to transform */
Transform_Properties(ot, P_PROPORTIONAL | P_MIRROR_DUMMY);
}
diff --git a/source/blender/editors/mesh/editmesh_preselect_elem.c b/source/blender/editors/mesh/editmesh_preselect_elem.c
index a3e684a5493..05c4da68355 100644
--- a/source/blender/editors/mesh/editmesh_preselect_elem.c
+++ b/source/blender/editors/mesh/editmesh_preselect_elem.c
@@ -75,20 +75,49 @@ struct EditMesh_PreSelElem {
float (*verts)[3];
int verts_len;
+
+ float (*preview_tris)[3][3];
+ int preview_tris_len;
+ float (*preview_lines)[2][3];
+ int preview_lines_len;
+
+ eEditMesh_PreSelPreviewAction preview_action;
};
+void EDBM_preselect_action_set(struct EditMesh_PreSelElem *psel,
+ eEditMesh_PreSelPreviewAction action)
+{
+ psel->preview_action = action;
+}
+
+eEditMesh_PreSelPreviewAction EDBM_preselect_action_get(struct EditMesh_PreSelElem *psel)
+{
+ return psel->preview_action;
+}
+
struct EditMesh_PreSelElem *EDBM_preselect_elem_create(void)
{
struct EditMesh_PreSelElem *psel = MEM_callocN(sizeof(*psel), __func__);
+ psel->preview_action = PRESELECT_ACTION_TRANSFORM;
return psel;
}
void EDBM_preselect_elem_destroy(struct EditMesh_PreSelElem *psel)
{
EDBM_preselect_elem_clear(psel);
+ EDBM_preselect_preview_clear(psel);
MEM_freeN(psel);
}
+void EDBM_preselect_preview_clear(struct EditMesh_PreSelElem *psel)
+{
+ MEM_SAFE_FREE(psel->preview_tris);
+ psel->preview_tris_len = 0;
+
+ MEM_SAFE_FREE(psel->preview_lines);
+ psel->preview_lines_len = 0;
+}
+
void EDBM_preselect_elem_clear(struct EditMesh_PreSelElem *psel)
{
MEM_SAFE_FREE(psel->edges);
@@ -112,9 +141,42 @@ void EDBM_preselect_elem_draw(struct EditMesh_PreSelElem *psel, const float matr
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
- immUniformColor3ub(255, 0, 255);
+
+ immUniformColor4ub(141, 171, 186, 100);
+ if (psel->preview_action != PRESELECT_ACTION_TRANSFORM) {
+ if (psel->preview_tris_len > 0) {
+ immBegin(GPU_PRIM_TRIS, psel->preview_tris_len * 3);
+
+ for (int i = 0; i < psel->preview_tris_len; i++) {
+ immVertex3fv(pos, psel->preview_tris[i][0]);
+ immVertex3fv(pos, psel->preview_tris[i][1]);
+ immVertex3fv(pos, psel->preview_tris[i][2]);
+ }
+ immEnd();
+ }
+
+ if (psel->preview_lines_len > 0) {
+
+ immUniformColor4ub(3, 161, 252, 200);
+ GPU_line_width(2.0f);
+ immBegin(GPU_PRIM_LINES, psel->preview_lines_len * 2);
+ for (int i = 0; i < psel->preview_lines_len; i++) {
+ immVertex3fv(pos, psel->preview_lines[i][0]);
+ immVertex3fv(pos, psel->preview_lines[i][1]);
+ }
+ immEnd();
+ }
+ }
+
+ if (psel->preview_action == PRESELECT_ACTION_DELETE) {
+ immUniformColor4ub(252, 49, 10, 200);
+ }
+ else {
+ immUniformColor4ub(3, 161, 252, 200);
+ }
if (psel->edges_len > 0) {
+ GPU_line_width(3.0f);
immBegin(GPU_PRIM_LINES, psel->edges_len * 2);
for (int i = 0; i < psel->edges_len; i++) {
@@ -126,7 +188,7 @@ void EDBM_preselect_elem_draw(struct EditMesh_PreSelElem *psel, const float matr
}
if (psel->verts_len > 0) {
- GPU_point_size(3.0f);
+ GPU_point_size(4.0f);
immBegin(GPU_PRIM_POINTS, psel->verts_len);
@@ -167,6 +229,122 @@ static void view3d_preselect_mesh_elem_update_from_edge(struct EditMesh_PreSelEl
psel->edges_len = 1;
}
+static void view3d_preselect_update_preview_triangle_from_vert(struct EditMesh_PreSelElem *psel,
+ ViewContext *vc,
+ BMesh *UNUSED(bm),
+ BMVert *eed,
+ const int mval[2])
+{
+ BMVert *v_act = eed;
+ BMEdge *e_pair[2] = {NULL};
+ float center[3];
+
+ if (v_act->e != NULL) {
+ for (uint allow_wire = 0; allow_wire < 2 && (e_pair[1] == NULL); allow_wire++) {
+ int i = 0;
+ BMEdge *e_iter = v_act->e;
+ do {
+ if ((BM_elem_flag_test(e_iter, BM_ELEM_HIDDEN) == false) &&
+ (allow_wire ? BM_edge_is_wire(e_iter) : BM_edge_is_boundary(e_iter))) {
+ if (i == 2) {
+ e_pair[0] = e_pair[1] = NULL;
+ break;
+ }
+ e_pair[i++] = e_iter;
+ }
+ } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, v_act)) != v_act->e);
+ }
+ }
+
+ if (e_pair[1] != NULL) {
+ mul_v3_m4v3(center, vc->obedit->obmat, v_act->co);
+ ED_view3d_win_to_3d_int(vc->v3d, vc->ar, center, mval, center);
+ mul_m4_v3(vc->obedit->imat, center);
+
+ psel->preview_tris = MEM_mallocN(sizeof(*psel->preview_tris) * 2, __func__);
+ psel->preview_lines = MEM_mallocN(sizeof(*psel->preview_lines) * 4, __func__);
+
+ copy_v3_v3(psel->preview_tris[0][0], e_pair[0]->v1->co);
+ copy_v3_v3(psel->preview_tris[0][1], e_pair[0]->v2->co);
+ copy_v3_v3(psel->preview_tris[0][2], center);
+
+ copy_v3_v3(psel->preview_tris[1][0], e_pair[1]->v1->co);
+ copy_v3_v3(psel->preview_tris[1][1], e_pair[1]->v2->co);
+ copy_v3_v3(psel->preview_tris[1][2], center);
+
+ copy_v3_v3(psel->preview_lines[0][0], e_pair[0]->v1->co);
+ copy_v3_v3(psel->preview_lines[0][1], e_pair[0]->v2->co);
+
+ copy_v3_v3(psel->preview_lines[1][0], e_pair[1]->v1->co);
+ copy_v3_v3(psel->preview_lines[1][1], e_pair[1]->v2->co);
+
+ copy_v3_v3(psel->preview_lines[2][0], center);
+ if (e_pair[0]->v1 == v_act) {
+ copy_v3_v3(psel->preview_lines[2][1], e_pair[0]->v2->co);
+ }
+ else {
+ copy_v3_v3(psel->preview_lines[2][1], e_pair[0]->v1->co);
+ }
+
+ copy_v3_v3(psel->preview_lines[3][0], center);
+ if (e_pair[1]->v1 == v_act) {
+ copy_v3_v3(psel->preview_lines[3][1], e_pair[1]->v2->co);
+ }
+ else {
+ copy_v3_v3(psel->preview_lines[3][1], e_pair[1]->v1->co);
+ }
+ psel->preview_tris_len = 2;
+ psel->preview_lines_len = 4;
+ }
+}
+
+static void view3d_preselect_update_preview_triangle_from_face(struct EditMesh_PreSelElem *psel,
+ ViewContext *UNUSED(vc),
+ BMesh *UNUSED(bm),
+ BMFace *efa,
+ const int UNUSED(mval[2]))
+{
+ float(*preview_lines)[2][3] = MEM_mallocN(sizeof(*psel->edges) * efa->len, __func__);
+ BMLoop *l_iter, *l_first;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(efa);
+ int i = 0;
+ do {
+ vcos_get_pair(&l_iter->e->v1, preview_lines[i++], NULL);
+ } while ((l_iter = l_iter->next) != l_first);
+ psel->preview_lines = preview_lines;
+ psel->preview_lines_len = efa->len;
+}
+
+static void view3d_preselect_update_preview_triangle_from_edge(struct EditMesh_PreSelElem *psel,
+ ViewContext *vc,
+ BMesh *UNUSED(bm),
+ BMEdge *eed,
+ const int mval[2])
+{
+ float center[3];
+ psel->preview_tris = MEM_mallocN(sizeof(*psel->preview_tris), __func__);
+ psel->preview_lines = MEM_mallocN(sizeof(*psel->preview_lines) * 3, __func__);
+ mid_v3_v3v3(center, eed->v1->co, eed->v2->co);
+ mul_m4_v3(vc->obedit->obmat, center);
+ ED_view3d_win_to_3d_int(vc->v3d, vc->ar, center, mval, center);
+ mul_m4_v3(vc->obedit->imat, center);
+
+ copy_v3_v3(psel->preview_tris[0][0], eed->v1->co);
+ copy_v3_v3(psel->preview_tris[0][1], eed->v2->co);
+ copy_v3_v3(psel->preview_tris[0][2], center);
+
+ copy_v3_v3(psel->preview_lines[0][0], eed->v1->co);
+ copy_v3_v3(psel->preview_lines[0][1], eed->v2->co);
+
+ copy_v3_v3(psel->preview_lines[1][0], eed->v2->co);
+ copy_v3_v3(psel->preview_lines[1][1], center);
+
+ copy_v3_v3(psel->preview_lines[2][0], center);
+ copy_v3_v3(psel->preview_lines[2][1], eed->v1->co);
+ psel->preview_tris_len = 1;
+ psel->preview_lines_len = 3;
+}
+
static void view3d_preselect_mesh_elem_update_from_face(struct EditMesh_PreSelElem *psel,
BMesh *UNUSED(bm),
BMFace *efa,
@@ -209,4 +387,28 @@ void EDBM_preselect_elem_update_from_single(struct EditMesh_PreSelElem *psel,
}
}
+void EDBM_preselect_elem_update_preview(struct EditMesh_PreSelElem *psel,
+ struct ViewContext *vc,
+ struct BMesh *bm,
+ struct BMElem *ele,
+ const int mval[2])
+{
+ EDBM_preselect_preview_clear(psel);
+
+ switch (ele->head.htype) {
+ case BM_VERT:
+ if (EDBM_preselect_action_get(psel) == PRESELECT_ACTION_CREATE) {
+ view3d_preselect_update_preview_triangle_from_vert(psel, vc, bm, (BMVert *)ele, mval);
+ }
+ break;
+ case BM_EDGE:
+ view3d_preselect_update_preview_triangle_from_edge(psel, vc, bm, (BMEdge *)ele, mval);
+ break;
+ case BM_FACE:
+ view3d_preselect_update_preview_triangle_from_face(psel, vc, bm, (BMFace *)ele, mval);
+ break;
+ default:
+ BLI_assert(0);
+ }
+}
/** \} */
diff --git a/source/blender/editors/mesh/editmesh_select.c b/source/blender/editors/mesh/editmesh_select.c
index 94ffd9a34d6..c0bd9e9f14c 100644
--- a/source/blender/editors/mesh/editmesh_select.c
+++ b/source/blender/editors/mesh/editmesh_select.c
@@ -24,7 +24,6 @@
#include "MEM_guardedalloc.h"
#include "BLI_bitmap.h"
-#include "BLI_bitmap_draw_2d.h"
#include "BLI_listbase.h"
#include "BLI_linklist.h"
#include "BLI_linklist_stack.h"
@@ -35,13 +34,9 @@
#include "BKE_context.h"
#include "BKE_report.h"
-#include "BKE_paint.h"
#include "BKE_editmesh.h"
#include "BKE_layer.h"
-#include "IMB_imbuf_types.h"
-#include "IMB_imbuf.h"
-
#include "WM_api.h"
#include "WM_types.h"
@@ -67,7 +62,6 @@
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_query.h"
-#include "DRW_engine.h"
#include "DRW_select_buffer.h"
#include "mesh_intern.h" /* own include */
@@ -172,30 +166,6 @@ void EDBM_select_mirrored(
/** \} */
/* -------------------------------------------------------------------- */
-/** \name Select Auto-Merge
- *
- * Used after transform operations.
- * \{ */
-
-void EDBM_automerge(Scene *scene, Object *obedit, bool update, const char hflag)
-{
- bool ok;
- BMEditMesh *em = BKE_editmesh_from_object(obedit);
-
- ok = BMO_op_callf(em->bm,
- BMO_FLAG_DEFAULTS,
- "automerge verts=%hv dist=%f",
- hflag,
- scene->toolsettings->doublimit);
-
- if (LIKELY(ok) && update) {
- EDBM_update_generic(em, true, true);
- }
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
/** \name Back-Buffer OpenGL Selection
* \{ */
@@ -1031,8 +1001,11 @@ bool EDBM_unified_findnearest(ViewContext *vc,
bool EDBM_unified_findnearest_from_raycast(ViewContext *vc,
Base **bases,
const uint bases_len,
- bool use_boundary,
- int *r_base_index,
+ bool use_boundary_vertices,
+ bool use_boundary_edges,
+ int *r_base_index_vert,
+ int *r_base_index_edge,
+ int *r_base_index_face,
struct BMVert **r_eve,
struct BMEdge **r_eed,
struct BMFace **r_efa)
@@ -1045,10 +1018,30 @@ bool EDBM_unified_findnearest_from_raycast(ViewContext *vc,
uint base_index;
BMElem *ele;
} best = {0, NULL};
+ /* Currently unused, keep since we may want to pick the best. */
+ UNUSED_VARS(best);
+
+ struct {
+ uint base_index;
+ BMElem *ele;
+ } best_vert = {0, NULL};
+
+ struct {
+ uint base_index;
+ BMElem *ele;
+ } best_edge = {0, NULL};
+
+ struct {
+ uint base_index;
+ BMElem *ele;
+ } best_face = {0, NULL};
if (ED_view3d_win_to_ray_clipped(
vc->depsgraph, vc->ar, vc->v3d, mval_fl, ray_origin, ray_direction, true)) {
float dist_sq_best = FLT_MAX;
+ float dist_sq_best_vert = FLT_MAX;
+ float dist_sq_best_edge = FLT_MAX;
+ float dist_sq_best_face = FLT_MAX;
const bool use_vert = (r_eve != NULL);
const bool use_edge = (r_eed != NULL);
@@ -1078,18 +1071,23 @@ bool EDBM_unified_findnearest_from_raycast(ViewContext *vc,
BM_mesh_elem_index_ensure(bm, BM_VERT);
}
- if (use_boundary && (use_vert || use_edge)) {
+ if ((use_boundary_vertices || use_boundary_edges) && (use_vert || use_edge)) {
BMEdge *e;
BMIter eiter;
BM_ITER_MESH (e, &eiter, bm, BM_EDGES_OF_MESH) {
if ((BM_elem_flag_test(e, BM_ELEM_HIDDEN) == false) && (BM_edge_is_boundary(e))) {
- if (use_vert) {
+ if (use_vert && use_boundary_vertices) {
for (uint j = 0; j < 2; j++) {
BMVert *v = *((&e->v1) + j);
float point[3];
mul_v3_m4v3(point, obedit->obmat, coords ? coords[BM_elem_index_get(v)] : v->co);
const float dist_sq_test = dist_squared_to_ray_v3_normalized(
ray_origin, ray_direction, point);
+ if (dist_sq_test < dist_sq_best_vert) {
+ dist_sq_best_vert = dist_sq_test;
+ best_vert.base_index = base_index;
+ best_vert.ele = (BMElem *)v;
+ }
if (dist_sq_test < dist_sq_best) {
dist_sq_best = dist_sq_test;
best.base_index = base_index;
@@ -1098,7 +1096,7 @@ bool EDBM_unified_findnearest_from_raycast(ViewContext *vc,
}
}
- if (use_edge) {
+ if (use_edge && use_boundary_edges) {
float point[3];
#if 0
const float dist_sq_test = dist_squared_ray_to_seg_v3(
@@ -1114,6 +1112,11 @@ bool EDBM_unified_findnearest_from_raycast(ViewContext *vc,
mul_m4_v3(obedit->obmat, point);
const float dist_sq_test = dist_squared_to_ray_v3_normalized(
ray_origin, ray_direction, point);
+ if (dist_sq_test < dist_sq_best_edge) {
+ dist_sq_best_edge = dist_sq_test;
+ best_edge.base_index = base_index;
+ best_edge.ele = (BMElem *)e;
+ }
if (dist_sq_test < dist_sq_best) {
dist_sq_best = dist_sq_test;
best.base_index = base_index;
@@ -1124,46 +1127,55 @@ bool EDBM_unified_findnearest_from_raycast(ViewContext *vc,
}
}
}
- else {
- /* Non boundary case. */
- if (use_vert) {
- BMVert *v;
- BMIter viter;
- BM_ITER_MESH (v, &viter, bm, BM_VERTS_OF_MESH) {
- if (BM_elem_flag_test(v, BM_ELEM_HIDDEN) == false) {
- float point[3];
- mul_v3_m4v3(point, obedit->obmat, v->co);
- const float dist_sq_test = dist_squared_to_ray_v3_normalized(
- ray_origin, ray_direction, v->co);
- if (dist_sq_test < dist_sq_best) {
- dist_sq_best = dist_sq_test;
- best.base_index = base_index;
- best.ele = (BMElem *)v;
- }
+ /* Non boundary case. */
+ if (use_vert && !use_boundary_vertices) {
+ BMVert *v;
+ BMIter viter;
+ BM_ITER_MESH (v, &viter, bm, BM_VERTS_OF_MESH) {
+ if (BM_elem_flag_test(v, BM_ELEM_HIDDEN) == false) {
+ float point[3];
+ mul_v3_m4v3(point, obedit->obmat, coords ? coords[BM_elem_index_get(v)] : v->co);
+ const float dist_sq_test = dist_squared_to_ray_v3_normalized(
+ ray_origin, ray_direction, point);
+ if (dist_sq_test < dist_sq_best_vert) {
+ dist_sq_best_vert = dist_sq_test;
+ best_vert.base_index = base_index;
+ best_vert.ele = (BMElem *)v;
+ }
+ if (dist_sq_test < dist_sq_best) {
+ dist_sq_best = dist_sq_test;
+ best.base_index = base_index;
+ best.ele = (BMElem *)v;
}
}
}
- if (use_edge) {
- BMEdge *e;
- BMIter eiter;
- BM_ITER_MESH (e, &eiter, bm, BM_EDGES_OF_MESH) {
- if (BM_elem_flag_test(e, BM_ELEM_HIDDEN) == false) {
- float point[3];
- if (coords) {
- mid_v3_v3v3(
- point, coords[BM_elem_index_get(e->v1)], coords[BM_elem_index_get(e->v2)]);
- }
- else {
- mid_v3_v3v3(point, e->v1->co, e->v2->co);
- }
- mul_m4_v3(obedit->obmat, point);
- const float dist_sq_test = dist_squared_to_ray_v3_normalized(
- ray_origin, ray_direction, point);
- if (dist_sq_test < dist_sq_best) {
- dist_sq_best = dist_sq_test;
- best.base_index = base_index;
- best.ele = (BMElem *)e;
- }
+ }
+
+ if (use_edge && !use_boundary_edges) {
+ BMEdge *e;
+ BMIter eiter;
+ BM_ITER_MESH (e, &eiter, bm, BM_EDGES_OF_MESH) {
+ if (BM_elem_flag_test(e, BM_ELEM_HIDDEN) == false) {
+ float point[3];
+ if (coords) {
+ mid_v3_v3v3(
+ point, coords[BM_elem_index_get(e->v1)], coords[BM_elem_index_get(e->v2)]);
+ }
+ else {
+ mid_v3_v3v3(point, e->v1->co, e->v2->co);
+ }
+ mul_m4_v3(obedit->obmat, point);
+ const float dist_sq_test = dist_squared_to_ray_v3_normalized(
+ ray_origin, ray_direction, point);
+ if (dist_sq_test < dist_sq_best_edge) {
+ dist_sq_best_edge = dist_sq_test;
+ best_edge.base_index = base_index;
+ best_edge.ele = (BMElem *)e;
+ }
+ if (dist_sq_test < dist_sq_best) {
+ dist_sq_best = dist_sq_test;
+ best.base_index = base_index;
+ best.ele = (BMElem *)e;
}
}
}
@@ -1184,6 +1196,11 @@ bool EDBM_unified_findnearest_from_raycast(ViewContext *vc,
mul_m4_v3(obedit->obmat, point);
const float dist_sq_test = dist_squared_to_ray_v3_normalized(
ray_origin, ray_direction, point);
+ if (dist_sq_test < dist_sq_best_face) {
+ dist_sq_best_face = dist_sq_test;
+ best_face.base_index = base_index;
+ best_face.ele = (BMElem *)f;
+ }
if (dist_sq_test < dist_sq_best) {
dist_sq_best = dist_sq_test;
best.base_index = base_index;
@@ -1195,7 +1212,10 @@ bool EDBM_unified_findnearest_from_raycast(ViewContext *vc,
}
}
- *r_base_index = best.base_index;
+ *r_base_index_vert = best_vert.base_index;
+ *r_base_index_edge = best_edge.base_index;
+ *r_base_index_face = best_face.base_index;
+
if (r_eve) {
*r_eve = NULL;
}
@@ -1206,22 +1226,17 @@ bool EDBM_unified_findnearest_from_raycast(ViewContext *vc,
*r_efa = NULL;
}
- if (best.ele) {
- switch (best.ele->head.htype) {
- case BM_VERT:
- *r_eve = (BMVert *)best.ele;
- break;
- case BM_EDGE:
- *r_eed = (BMEdge *)best.ele;
- break;
- case BM_FACE:
- *r_efa = (BMFace *)best.ele;
- break;
- default:
- BLI_assert(0);
- }
+ if (best_vert.ele) {
+ *r_eve = (BMVert *)best_vert.ele;
+ }
+ if (best_edge.ele) {
+ *r_eed = (BMEdge *)best_edge.ele;
}
- return (best.ele != NULL);
+ if (best_face.ele) {
+ *r_efa = (BMFace *)best_face.ele;
+ }
+
+ return (best_vert.ele != NULL || best_edge.ele != NULL || best_face.ele != NULL);
}
/** \} */
diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c
index 65c058556f5..0c4db012786 100644
--- a/source/blender/editors/mesh/editmesh_tools.c
+++ b/source/blender/editors/mesh/editmesh_tools.c
@@ -437,6 +437,7 @@ void EDBM_project_snap_verts(bContext *C, Depsgraph *depsgraph, ARegion *ar, BME
},
mval,
NULL,
+ NULL,
co_proj,
NULL)) {
mul_v3_m4v3(eve->co, obedit->imat, co_proj);
@@ -3130,12 +3131,7 @@ static int edbm_remove_doubles_exec(bContext *C, wmOperator *op)
BM_mesh_elem_hflag_enable_test(em->bm, htype_select, BM_ELEM_TAG, true, true, BM_ELEM_SELECT);
if (use_unselected) {
- EDBM_op_init(em, &bmop, op, "automerge verts=%hv dist=%f", BM_ELEM_SELECT, threshold);
- BMO_op_exec(em->bm, &bmop);
-
- if (!EDBM_op_finish(em, &bmop, op, true)) {
- continue;
- }
+ EDBM_automerge(obedit, false, BM_ELEM_SELECT, threshold);
}
else {
EDBM_op_init(em, &bmop, op, "find_doubles verts=%hv dist=%f", BM_ELEM_SELECT, threshold);
@@ -4040,7 +4036,7 @@ static void mesh_separate_material_assign_mat_nr(Main *bmain, Object *ob, const
ma_obdata = NULL;
}
- BKE_material_clear_id(bmain, obdata, true);
+ BKE_material_clear_id(bmain, obdata);
BKE_material_resize_object(bmain, ob, 1, true);
BKE_material_resize_id(bmain, obdata, 1, true);
@@ -4051,7 +4047,7 @@ static void mesh_separate_material_assign_mat_nr(Main *bmain, Object *ob, const
id_us_plus((ID *)ma_obdata);
}
else {
- BKE_material_clear_id(bmain, obdata, true);
+ BKE_material_clear_id(bmain, obdata);
BKE_material_resize_object(bmain, ob, 0, true);
BKE_material_resize_id(bmain, obdata, 0, true);
}
@@ -5710,7 +5706,7 @@ void MESH_OT_dissolve_limited(wmOperatorType *ot)
"use_dissolve_boundaries",
false,
"All Boundaries",
- "Dissolve all vertices inbetween face boundaries");
+ "Dissolve all vertices in between face boundaries");
RNA_def_enum_flag(ot->srna,
"delimit",
rna_enum_mesh_delimit_mode_items,
diff --git a/source/blender/editors/mesh/editmesh_utils.c b/source/blender/editors/mesh/editmesh_utils.c
index d7ed14184fa..522c2f32d27 100644
--- a/source/blender/editors/mesh/editmesh_utils.c
+++ b/source/blender/editors/mesh/editmesh_utils.c
@@ -85,7 +85,7 @@ void EDBM_redo_state_restore(BMBackup backup, BMEditMesh *em, int recalctess)
tmpbm = NULL;
if (recalctess) {
- BKE_editmesh_tessface_calc(em);
+ BKE_editmesh_looptri_calc(em);
}
}
@@ -105,7 +105,7 @@ void EDBM_redo_state_free(BMBackup *backup, BMEditMesh *em, int recalctess)
backup->bmcopy = NULL;
if (recalctess && em) {
- BKE_editmesh_tessface_calc(em);
+ BKE_editmesh_looptri_calc(em);
}
}
@@ -162,7 +162,7 @@ bool EDBM_op_finish(BMEditMesh *em, BMOperator *bmop, wmOperator *op, const bool
/* when copying, tessellation isn't to for faster copying,
* but means we need to re-tessellate here */
if (em->looptris == NULL) {
- BKE_editmesh_tessface_calc(em);
+ BKE_editmesh_looptri_calc(em);
}
if (em->ob) {
@@ -292,10 +292,6 @@ void EDBM_mesh_make(Object *ob, const int select_mode, const bool add_key_index)
Mesh *me = ob->data;
BMesh *bm;
- if (UNLIKELY(!me->mpoly && me->totface)) {
- BKE_mesh_convert_mfaces_to_mpolys(me);
- }
-
bm = BKE_mesh_to_bmesh(me,
ob,
add_key_index,
@@ -347,10 +343,6 @@ void EDBM_mesh_load(Main *bmain, Object *ob)
.calc_object_remap = true,
}));
-#ifdef USE_TESSFACE_DEFAULT
- BKE_mesh_tessface_calc(me);
-#endif
-
/* Free derived mesh. usually this would happen through depsgraph but there
* are exceptions like file save that will not cause this, and we want to
* avoid ending up with an invalid derived mesh then.
@@ -1397,15 +1389,15 @@ void EDBM_stats_update(BMEditMesh *em)
/* so many tools call these that we better make it a generic function.
*/
-void EDBM_update_generic(BMEditMesh *em, const bool do_tessface, const bool is_destructive)
+void EDBM_update_generic(BMEditMesh *em, const bool do_tessellation, const bool is_destructive)
{
Object *ob = em->ob;
/* order of calling isn't important */
DEG_id_tag_update(ob->data, ID_RECALC_GEOMETRY);
WM_main_add_notifier(NC_GEOM | ND_DATA, ob->data);
- if (do_tessface) {
- BKE_editmesh_tessface_calc(em);
+ if (do_tessellation) {
+ BKE_editmesh_looptri_calc(em);
}
if (is_destructive) {
diff --git a/source/blender/editors/mesh/mesh_data.c b/source/blender/editors/mesh/mesh_data.c
index ee8de9d8ea9..569994bead1 100644
--- a/source/blender/editors/mesh/mesh_data.c
+++ b/source/blender/editors/mesh/mesh_data.c
@@ -289,18 +289,14 @@ int ED_mesh_uv_texture_add(Mesh *me, const char *name, const bool active_set, co
if (me->mloopuv && do_init) {
CustomData_add_layer_named(
&me->ldata, CD_MLOOPUV, CD_DUPLICATE, me->mloopuv, me->totloop, name);
- CustomData_add_layer_named(
- &me->fdata, CD_MTFACE, CD_DUPLICATE, me->mtface, me->totface, name);
is_init = true;
}
else {
CustomData_add_layer_named(&me->ldata, CD_MLOOPUV, CD_DEFAULT, NULL, me->totloop, name);
- CustomData_add_layer_named(&me->fdata, CD_MTFACE, CD_DEFAULT, NULL, me->totface, name);
}
if (active_set || layernum_dst == 0) {
CustomData_set_layer_active(&me->ldata, CD_MLOOPUV, layernum_dst);
- CustomData_set_layer_active(&me->fdata, CD_MTFACE, layernum_dst);
}
BKE_mesh_update_customdata_pointers(me, true);
@@ -418,16 +414,13 @@ int ED_mesh_color_add(Mesh *me, const char *name, const bool active_set, const b
if (me->mloopcol && do_init) {
CustomData_add_layer_named(
&me->ldata, CD_MLOOPCOL, CD_DUPLICATE, me->mloopcol, me->totloop, name);
- CustomData_add_layer_named(&me->fdata, CD_MCOL, CD_DUPLICATE, me->mcol, me->totface, name);
}
else {
CustomData_add_layer_named(&me->ldata, CD_MLOOPCOL, CD_DEFAULT, NULL, me->totloop, name);
- CustomData_add_layer_named(&me->fdata, CD_MCOL, CD_DEFAULT, NULL, me->totface, name);
}
if (active_set || layernum == 0) {
CustomData_set_layer_active(&me->ldata, CD_MLOOPCOL, layernum);
- CustomData_set_layer_active(&me->fdata, CD_MCOL, layernum);
}
BKE_mesh_update_customdata_pointers(me, true);
@@ -885,18 +878,8 @@ void MESH_OT_customdata_custom_splitnormals_clear(wmOperatorType *ot)
/************************** Add Geometry Layers *************************/
-void ED_mesh_update(
- Mesh *mesh, bContext *C, bool calc_edges, bool calc_edges_loose, bool calc_tessface)
+void ED_mesh_update(Mesh *mesh, bContext *C, bool calc_edges, bool calc_edges_loose)
{
- bool tessface_input = false;
-
- if (mesh->totface > 0 && mesh->totpoly == 0) {
- BKE_mesh_convert_mfaces_to_mpolys(mesh);
-
- /* would only be converting back again, don't bother */
- tessface_input = true;
- }
-
if (calc_edges_loose && mesh->totedge) {
BKE_mesh_calc_edges_loose(mesh);
}
@@ -905,15 +888,8 @@ void ED_mesh_update(
BKE_mesh_calc_edges(mesh, calc_edges, true);
}
- if (calc_tessface) {
- if (tessface_input == false) {
- BKE_mesh_tessface_calc(mesh);
- }
- }
- else {
- /* default state is not to have tessface's so make sure this is the case */
- BKE_mesh_tessface_clear(mesh);
- }
+ /* Default state is not to have tessface's so make sure this is the case. */
+ BKE_mesh_tessface_clear(mesh);
BKE_mesh_calc_normals(mesh);
@@ -988,39 +964,6 @@ static void mesh_add_edges(Mesh *mesh, int len)
mesh->totedge = totedge;
}
-static void mesh_add_tessfaces(Mesh *mesh, int len)
-{
- CustomData fdata;
- MFace *mface;
- int i, totface;
-
- if (len == 0) {
- return;
- }
-
- totface = mesh->totface + len; /* new face count */
-
- /* update customdata */
- CustomData_copy(&mesh->fdata, &fdata, CD_MASK_MESH.fmask, CD_DEFAULT, totface);
- CustomData_copy_data(&mesh->fdata, &fdata, 0, 0, mesh->totface);
-
- if (!CustomData_has_layer(&fdata, CD_MFACE)) {
- CustomData_add_layer(&fdata, CD_MFACE, CD_CALLOC, NULL, totface);
- }
-
- CustomData_free(&mesh->fdata, mesh->totface);
- mesh->fdata = fdata;
- BKE_mesh_update_customdata_pointers(mesh, true);
-
- /* set default flags */
- mface = &mesh->mface[mesh->totface];
- for (i = 0; i < len; i++, mface++) {
- mface->flag = ME_FACE_SEL;
- }
-
- mesh->totface = totface;
-}
-
static void mesh_add_loops(Mesh *mesh, int len)
{
CustomData ldata;
@@ -1109,20 +1052,6 @@ static void mesh_remove_edges(Mesh *mesh, int len)
mesh->totedge = totedge;
}
-static void mesh_remove_faces(Mesh *mesh, int len)
-{
- int totface;
-
- if (len == 0) {
- return;
- }
-
- totface = mesh->totface - len; /* new face count */
- CustomData_free_elem(&mesh->fdata, totface, len);
-
- mesh->totface = totface;
-}
-
#if 0
void ED_mesh_geometry_add(Mesh *mesh, ReportList *reports, int verts, int edges, int faces)
{
@@ -1143,21 +1072,6 @@ void ED_mesh_geometry_add(Mesh *mesh, ReportList *reports, int verts, int edges,
}
#endif
-void ED_mesh_tessfaces_add(Mesh *mesh, ReportList *reports, int count)
-{
- if (mesh->edit_mesh) {
- BKE_report(reports, RPT_ERROR, "Cannot add tessfaces in edit mode");
- return;
- }
-
- if (mesh->mpoly) {
- BKE_report(reports, RPT_ERROR, "Cannot add tessfaces to a mesh that already has polygons");
- return;
- }
-
- mesh_add_tessfaces(mesh, count);
-}
-
void ED_mesh_edges_add(Mesh *mesh, ReportList *reports, int count)
{
if (mesh->edit_mesh) {
@@ -1178,20 +1092,6 @@ void ED_mesh_vertices_add(Mesh *mesh, ReportList *reports, int count)
mesh_add_verts(mesh, count);
}
-void ED_mesh_faces_remove(Mesh *mesh, ReportList *reports, int count)
-{
- if (mesh->edit_mesh) {
- BKE_report(reports, RPT_ERROR, "Cannot remove faces in edit mode");
- return;
- }
- else if (count > mesh->totface) {
- BKE_report(reports, RPT_ERROR, "Cannot remove more faces than the mesh contains");
- return;
- }
-
- mesh_remove_faces(mesh, count);
-}
-
void ED_mesh_edges_remove(Mesh *mesh, ReportList *reports, int count)
{
if (mesh->edit_mesh) {
@@ -1240,26 +1140,6 @@ void ED_mesh_polys_add(Mesh *mesh, ReportList *reports, int count)
mesh_add_polys(mesh, count);
}
-void ED_mesh_calc_tessface(Mesh *mesh, bool free_mpoly)
-{
- if (mesh->edit_mesh) {
- BKE_editmesh_tessface_calc(mesh->edit_mesh);
- }
- else {
- BKE_mesh_tessface_calc(mesh);
- }
- if (free_mpoly) {
- CustomData_free(&mesh->ldata, mesh->totloop);
- CustomData_free(&mesh->pdata, mesh->totpoly);
- mesh->totloop = 0;
- mesh->totpoly = 0;
- mesh->mloop = NULL;
- mesh->mloopcol = NULL;
- mesh->mloopuv = NULL;
- mesh->mpoly = NULL;
- }
-}
-
void ED_mesh_report_mirror_ex(wmOperator *op, int totmirr, int totfail, char selectmode)
{
const char *elem_type;
diff --git a/source/blender/editors/mesh/mesh_intern.h b/source/blender/editors/mesh/mesh_intern.h
index 8d585977105..3558a07c6fb 100644
--- a/source/blender/editors/mesh/mesh_intern.h
+++ b/source/blender/editors/mesh/mesh_intern.h
@@ -122,6 +122,8 @@ void MESH_GGT_spin_redo(struct wmGizmoGroupType *gzgt);
void MESH_OT_polybuild_face_at_cursor(struct wmOperatorType *ot);
void MESH_OT_polybuild_split_at_cursor(struct wmOperatorType *ot);
void MESH_OT_polybuild_dissolve_at_cursor(struct wmOperatorType *ot);
+void MESH_OT_polybuild_transform_at_cursor(struct wmOperatorType *ot);
+void MESH_OT_polybuild_delete_at_cursor(struct wmOperatorType *ot);
/* *** editmesh_inset.c *** */
void MESH_OT_inset(struct wmOperatorType *ot);
diff --git a/source/blender/editors/mesh/mesh_ops.c b/source/blender/editors/mesh/mesh_ops.c
index c3387dcfc09..9b6e991a9f5 100644
--- a/source/blender/editors/mesh/mesh_ops.c
+++ b/source/blender/editors/mesh/mesh_ops.c
@@ -145,6 +145,8 @@ void ED_operatortypes_mesh(void)
WM_operatortype_append(MESH_OT_polybuild_face_at_cursor);
WM_operatortype_append(MESH_OT_polybuild_split_at_cursor);
WM_operatortype_append(MESH_OT_polybuild_dissolve_at_cursor);
+ WM_operatortype_append(MESH_OT_polybuild_transform_at_cursor);
+ WM_operatortype_append(MESH_OT_polybuild_delete_at_cursor);
WM_operatortype_append(MESH_OT_uv_texture_add);
WM_operatortype_append(MESH_OT_uv_texture_remove);
@@ -331,6 +333,25 @@ void ED_operatormacros_mesh(void)
otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate");
RNA_boolean_set(otmacro->ptr, "use_proportional_edit", false);
RNA_boolean_set(otmacro->ptr, "mirror", false);
+
+ ot = WM_operatortype_append_macro("MESH_OT_polybuild_transform_at_cursor_move",
+ "Transform at Cursor Move",
+ "",
+ OPTYPE_UNDO | OPTYPE_REGISTER);
+ WM_operatortype_macro_define(ot, "MESH_OT_polybuild_transform_at_cursor");
+ otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate");
+ RNA_boolean_set(otmacro->ptr, "use_proportional_edit", false);
+ RNA_boolean_set(otmacro->ptr, "mirror", false);
+
+ ot = WM_operatortype_append_macro("MESH_OT_polybuild_extrude_at_cursor_move",
+ "Extrude at Cursor Move",
+ "",
+ OPTYPE_UNDO | OPTYPE_REGISTER);
+ WM_operatortype_macro_define(ot, "MESH_OT_polybuild_transform_at_cursor");
+ otmacro = WM_operatortype_macro_define(ot, "MESH_OT_extrude_edges_indiv");
+ otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate");
+ RNA_boolean_set(otmacro->ptr, "use_proportional_edit", false);
+ RNA_boolean_set(otmacro->ptr, "mirror", false);
}
/* note mesh keymap also for other space? */
diff --git a/source/blender/editors/object/CMakeLists.txt b/source/blender/editors/object/CMakeLists.txt
index 2490f88b5eb..aabfa78cf58 100644
--- a/source/blender/editors/object/CMakeLists.txt
+++ b/source/blender/editors/object/CMakeLists.txt
@@ -48,6 +48,7 @@ set(SRC
object_collection.c
object_constraint.c
object_data_transfer.c
+ object_data_transform.c
object_edit.c
object_facemap_ops.c
object_gpencil_modifier.c
diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c
index db1aa6ca37f..3d5ec3d4ed5 100644
--- a/source/blender/editors/object/object_add.c
+++ b/source/blender/editors/object/object_add.c
@@ -103,6 +103,7 @@
#include "ED_mesh.h"
#include "ED_node.h"
#include "ED_object.h"
+#include "ED_outliner.h"
#include "ED_physics.h"
#include "ED_render.h"
#include "ED_screen.h"
@@ -502,6 +503,8 @@ Object *ED_object_add_type(bContext *C,
/* TODO(sergey): Use proper flag for tagging here. */
DEG_id_tag_update(&scene->id, 0);
+ ED_outliner_select_sync_from_object_tag(C);
+
return ob;
}
@@ -2383,13 +2386,22 @@ static int convert_exec(bContext *C, wmOperator *op)
BKE_object_free_curve_cache(newob);
}
else if (target == OB_GPENCIL) {
- /* Create a new grease pencil object */
- if (gpencil_ob == NULL) {
- const float *cur = scene->cursor.location;
- ushort local_view_bits = (v3d && v3d->localvd) ? v3d->local_view_uuid : 0;
- gpencil_ob = ED_gpencil_add_object(C, scene, cur, local_view_bits);
+ if (ob->type != OB_CURVE) {
+ BKE_report(
+ op->reports, RPT_ERROR, "Convert Surfaces to Grease Pencil is not supported.");
+ }
+ else {
+ /* Create a new grease pencil object only if it was not created before.
+ * All curves selected are converted as strokes of the same grease pencil object.
+ * Nurbs Surface are not supported.
+ */
+ if (gpencil_ob == NULL) {
+ const float *cur = scene->cursor.location;
+ ushort local_view_bits = (v3d && v3d->localvd) ? v3d->local_view_uuid : 0;
+ gpencil_ob = ED_gpencil_add_object(C, scene, cur, local_view_bits);
+ }
+ BKE_gpencil_convert_curve(bmain, scene, gpencil_ob, ob, false, false, true);
}
- BKE_gpencil_convert_curve(bmain, scene, gpencil_ob, ob, gpencil_lines, use_collections);
}
}
else if (ob->type == OB_MBALL && target == OB_MESH) {
diff --git a/source/blender/editors/object/object_constraint.c b/source/blender/editors/object/object_constraint.c
index 7343dba420f..8981221cb9c 100644
--- a/source/blender/editors/object/object_constraint.c
+++ b/source/blender/editors/object/object_constraint.c
@@ -493,8 +493,12 @@ static void test_constraint(
CONSTRAINT_TYPE_CLAMPTO,
CONSTRAINT_TYPE_SPLINEIK)) {
if (ct->tar) {
+ /* The object type check is only needed here in case we have a placeholder
+ * object assigned (because the library containing the curve is missing).
+ *
+ * In other cases it should be impossible to have a type mismatch.
+ */
if (ct->tar->type != OB_CURVE) {
- ct->tar = NULL;
con->flag |= CONSTRAINT_DISABLE;
}
else {
@@ -507,8 +511,12 @@ static void test_constraint(
}
else if (con->type == CONSTRAINT_TYPE_ARMATURE) {
if (ct->tar) {
+ /* The object type check is only needed here in case we have a placeholder
+ * object assigned (because the library containing the armature is missing).
+ *
+ * In other cases it should be impossible to have a type mismatch.
+ */
if (ct->tar->type != OB_ARMATURE) {
- ct->tar = NULL;
con->flag |= CONSTRAINT_DISABLE;
}
else if (!BKE_armature_find_bone_name(BKE_armature_from_object(ct->tar),
@@ -636,7 +644,7 @@ static const EnumPropertyItem constraint_owner_items[] = {
static bool edit_constraint_poll_generic(bContext *C, StructRNA *rna_type)
{
PointerRNA ptr = CTX_data_pointer_get_type(C, "constraint", rna_type);
- Object *ob = (ptr.id.data) ? ptr.id.data : ED_object_active_context(C);
+ Object *ob = (ptr.owner_id) ? (Object *)ptr.owner_id : ED_object_active_context(C);
if (!ptr.data) {
CTX_wm_operator_poll_msg_set(C, "Context missing 'constraint'");
@@ -648,7 +656,7 @@ static bool edit_constraint_poll_generic(bContext *C, StructRNA *rna_type)
return 0;
}
- if (ID_IS_LINKED(ob) || (ptr.id.data && ID_IS_LINKED(ptr.id.data))) {
+ if (ID_IS_LINKED(ob) || (ptr.owner_id && ID_IS_LINKED(ptr.owner_id))) {
CTX_wm_operator_poll_msg_set(C, "Cannot edit library data");
return 0;
}
@@ -680,7 +688,7 @@ static void edit_constraint_properties(wmOperatorType *ot)
static int edit_constraint_invoke_properties(bContext *C, wmOperator *op)
{
PointerRNA ptr = CTX_data_pointer_get_type(C, "constraint", &RNA_Constraint);
- Object *ob = (ptr.id.data) ? ptr.id.data : ED_object_active_context(C);
+ Object *ob = (ptr.owner_id) ? (Object *)ptr.owner_id : ED_object_active_context(C);
bConstraint *con;
ListBase *list;
@@ -1422,14 +1430,14 @@ void ED_object_constraint_dependency_tag_update(Main *bmain, Object *ob, bConstr
static bool constraint_poll(bContext *C)
{
PointerRNA ptr = CTX_data_pointer_get_type(C, "constraint", &RNA_Constraint);
- return (ptr.id.data && ptr.data);
+ return (ptr.owner_id && ptr.data);
}
static int constraint_delete_exec(bContext *C, wmOperator *UNUSED(op))
{
Main *bmain = CTX_data_main(C);
PointerRNA ptr = CTX_data_pointer_get_type(C, "constraint", &RNA_Constraint);
- Object *ob = ptr.id.data;
+ Object *ob = (Object *)ptr.owner_id;
bConstraint *con = ptr.data;
ListBase *lb = get_constraint_lb(ob, con, NULL);
diff --git a/source/blender/editors/object/object_data_transform.c b/source/blender/editors/object/object_data_transform.c
new file mode 100644
index 00000000000..ee86c79ead5
--- /dev/null
+++ b/source/blender/editors/object/object_data_transform.c
@@ -0,0 +1,333 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup edobj
+ *
+ * Use to transform object origins only.
+ *
+ * This is a small API to store & apply transformations to object data,
+ * where a transformation matrix can be continually applied ontop of the original values
+ * so we don't loose precision over time.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "DNA_anim_types.h"
+#include "DNA_armature_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_meta_types.h"
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_collection_types.h"
+#include "DNA_lattice_types.h"
+
+#include "BLI_math.h"
+#include "BLI_listbase.h"
+#include "BLI_utildefines.h"
+
+#include "BKE_curve.h"
+#include "BKE_mball.h"
+#include "BKE_mesh.h"
+#include "BKE_armature.h"
+#include "BKE_lattice.h"
+
+#include "DEG_depsgraph.h"
+
+#include "WM_types.h"
+
+#include "ED_object.h"
+
+#include "MEM_guardedalloc.h"
+
+/* -------------------------------------------------------------------- */
+/** \name Internal Transform Get/Apply
+ *
+ * Some object data types don't have utility functions to access their transformation data.
+ * Define these locally.
+ *
+ * \{ */
+
+/* Armature */
+
+struct ElemData_Armature {
+ float tail[3];
+ float head[3];
+ float roll;
+ float arm_tail[3];
+ float arm_head[3];
+ float arm_roll;
+ float rad_tail;
+ float rad_head;
+ float dist;
+ float xwidth;
+ float zwidth;
+};
+
+static struct ElemData_Armature *armature_coords_and_quats_get_recurse(
+ const ListBase *bone_base, struct ElemData_Armature *elem_array)
+{
+ struct ElemData_Armature *elem = elem_array;
+ for (const Bone *bone = bone_base->first; bone; bone = bone->next) {
+
+#define COPY_PTR(member) memcpy(elem->member, bone->member, sizeof(bone->member))
+#define COPY_VAL(member) memcpy(&elem->member, &bone->member, sizeof(bone->member))
+ COPY_PTR(head);
+ COPY_PTR(tail);
+ COPY_VAL(roll);
+ COPY_PTR(arm_head);
+ COPY_PTR(arm_tail);
+ COPY_VAL(arm_roll);
+ COPY_VAL(rad_tail);
+ COPY_VAL(rad_head);
+ COPY_VAL(dist);
+ COPY_VAL(xwidth);
+ COPY_VAL(zwidth);
+#undef COPY_PTR
+#undef COPY_VAL
+
+ elem = armature_coords_and_quats_get_recurse(&bone->childbase, elem + 1);
+ }
+ return elem;
+}
+
+static void armature_coords_and_quats_get(const bArmature *arm,
+ struct ElemData_Armature *elem_array)
+{
+ armature_coords_and_quats_get_recurse(&arm->bonebase, elem_array);
+}
+
+static const struct ElemData_Armature *armature_coords_and_quats_apply_with_mat4_recurse(
+ ListBase *bone_base, const struct ElemData_Armature *elem_array, const float mat[4][4])
+{
+ const struct ElemData_Armature *elem = elem_array;
+ for (Bone *bone = bone_base->first; bone; bone = bone->next) {
+
+#define COPY_PTR(member) memcpy(bone->member, elem->member, sizeof(bone->member))
+#define COPY_VAL(member) memcpy(&bone->member, &elem->member, sizeof(bone->member))
+ COPY_PTR(head);
+ COPY_PTR(tail);
+ COPY_VAL(roll);
+ COPY_PTR(arm_head);
+ COPY_PTR(arm_tail);
+ COPY_VAL(arm_roll);
+ COPY_VAL(rad_tail);
+ COPY_VAL(rad_head);
+ COPY_VAL(dist);
+ COPY_VAL(xwidth);
+ COPY_VAL(zwidth);
+#undef COPY_PTR
+#undef COPY_VAL
+
+ elem = armature_coords_and_quats_apply_with_mat4_recurse(&bone->childbase, elem + 1, mat);
+ }
+ return elem;
+}
+
+static void armature_coords_and_quats_apply_with_mat4(bArmature *arm,
+ const struct ElemData_Armature *elem_array,
+ const float mat[4][4])
+{
+ armature_coords_and_quats_apply_with_mat4_recurse(&arm->bonebase, elem_array, mat);
+ BKE_armature_transform(arm, mat, true);
+}
+
+/* MetaBall */
+
+struct ElemData_MetaBall {
+ float co[3];
+ float quat[4];
+ float exp[3];
+ float rad;
+};
+
+static void metaball_coords_and_quats_get(const MetaBall *mb, struct ElemData_MetaBall *elem_array)
+{
+ struct ElemData_MetaBall *elem = elem_array;
+ for (const MetaElem *ml = mb->elems.first; ml; ml = ml->next, elem++) {
+ copy_v3_v3(elem->co, &ml->x);
+ copy_qt_qt(elem->quat, ml->quat);
+ copy_v3_v3(elem->exp, &ml->expx);
+ elem->rad = ml->rad;
+ }
+}
+
+static void metaball_coords_and_quats_apply_with_mat4(MetaBall *mb,
+ const struct ElemData_MetaBall *elem_array,
+ const float mat[4][4])
+{
+ const struct ElemData_MetaBall *elem = elem_array;
+ for (MetaElem *ml = mb->elems.first; ml; ml = ml->next, elem++) {
+ copy_v3_v3(&ml->x, elem->co);
+ copy_qt_qt(ml->quat, elem->quat);
+ copy_v3_v3(&ml->expx, elem->exp);
+ ml->rad = elem->rad;
+ }
+ BKE_mball_transform(mb, mat, true);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Public Object Data Storage API
+ *
+ * Used for interactively transforming object data.
+ *
+ * Store object data transformation in an opaque struct.
+ * \{ */
+
+struct XFormObjectData {
+ ID *id;
+};
+
+struct XFormObjectData_Mesh {
+ struct XFormObjectData base;
+ float elem_array[0][3];
+};
+
+struct XFormObjectData_Lattice {
+ struct XFormObjectData base;
+ float elem_array[0][3];
+};
+
+struct XFormObjectData_Curve {
+ struct XFormObjectData base;
+ float elem_array[0][3];
+};
+
+struct XFormObjectData_Armature {
+ struct XFormObjectData base;
+ struct ElemData_Armature elem_array[0];
+};
+
+struct XFormObjectData_MetaBall {
+ struct XFormObjectData base;
+ struct ElemData_MetaBall elem_array[0];
+};
+
+struct XFormObjectData *ED_object_data_xform_create(ID *id)
+{
+ struct XFormObjectData *xod_base = NULL;
+ switch (GS(id->name)) {
+ case ID_ME: {
+ Mesh *me = (Mesh *)id;
+ const int elem_array_len = me->totvert;
+ struct XFormObjectData_Mesh *xod = MEM_mallocN(
+ sizeof(*xod) + (sizeof(*xod->elem_array) * elem_array_len), __func__);
+ BKE_mesh_vert_coords_get(me, xod->elem_array);
+ xod_base = &xod->base;
+ break;
+ }
+ case ID_LT: {
+ Lattice *lt = (Lattice *)id;
+ const int elem_array_len = lt->pntsu * lt->pntsv * lt->pntsw;
+ struct XFormObjectData_Lattice *xod = MEM_mallocN(
+ sizeof(*xod) + (sizeof(*xod->elem_array) * elem_array_len), __func__);
+ BKE_lattice_vert_coords_get(lt, xod->elem_array);
+ xod_base = &xod->base;
+ break;
+ }
+ case ID_CU: {
+ Curve *cu = (Curve *)id;
+ const short ob_type = BKE_curve_type_get(cu);
+ if (ob_type == OB_FONT) {
+ /* We could support translation. */
+ break;
+ }
+ const int elem_array_len = BKE_nurbList_verts_count(&cu->nurb);
+ struct XFormObjectData_Curve *xod = MEM_mallocN(
+ sizeof(*xod) + (sizeof(*xod->elem_array) * elem_array_len), __func__);
+ BKE_curve_nurbs_vert_coords_get(&cu->nurb, xod->elem_array, elem_array_len);
+ xod_base = &xod->base;
+ break;
+ }
+ case ID_AR: {
+ bArmature *arm = (bArmature *)id;
+ const int elem_array_len = BKE_armature_bonelist_count(&arm->bonebase);
+ struct XFormObjectData_Armature *xod = MEM_mallocN(
+ sizeof(*xod) + (sizeof(*xod->elem_array) * elem_array_len), __func__);
+ armature_coords_and_quats_get(arm, xod->elem_array);
+ xod_base = &xod->base;
+ break;
+ }
+ case ID_MB: {
+ MetaBall *mb = (MetaBall *)id;
+ const int elem_array_len = BLI_listbase_count(&mb->elems);
+ struct XFormObjectData_MetaBall *xod = MEM_mallocN(
+ sizeof(*xod) + (sizeof(*xod->elem_array) * elem_array_len), __func__);
+ metaball_coords_and_quats_get(mb, xod->elem_array);
+ xod_base = &xod->base;
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+ if (xod_base) {
+ xod_base->id = id;
+ }
+ return xod_base;
+}
+
+void ED_object_data_xform_destroy(struct XFormObjectData *xod)
+{
+ MEM_freeN(xod);
+}
+
+void ED_object_data_xform_by_mat4(struct XFormObjectData *xod_base, const float mat[4][4])
+{
+ switch (GS(xod_base->id->name)) {
+ case ID_ME: {
+ Mesh *me = (Mesh *)xod_base->id;
+ struct XFormObjectData_Mesh *xod = (struct XFormObjectData_Mesh *)xod_base;
+ BKE_mesh_vert_coords_apply_with_mat4(me, xod->elem_array, mat);
+ break;
+ }
+ case ID_LT: {
+ Lattice *lt = (Lattice *)xod_base->id;
+ struct XFormObjectData_Lattice *xod = (struct XFormObjectData_Lattice *)xod_base;
+ BKE_lattice_vert_coords_apply_with_mat4(lt, xod->elem_array, mat);
+ break;
+ }
+ case ID_CU: {
+ Curve *cu = (Curve *)xod_base->id;
+ struct XFormObjectData_Curve *xod = (struct XFormObjectData_Curve *)xod_base;
+ BKE_curve_nurbs_vert_coords_apply_with_mat4(&cu->nurb, xod->elem_array, mat, true);
+ break;
+ }
+ case ID_AR: {
+ bArmature *arm = (bArmature *)xod_base->id;
+ struct XFormObjectData_Armature *xod = (struct XFormObjectData_Armature *)xod_base;
+ armature_coords_and_quats_apply_with_mat4(arm, xod->elem_array, mat);
+ break;
+ }
+ case ID_MB: {
+ MetaBall *mb = (MetaBall *)xod_base->id;
+ struct XFormObjectData_MetaBall *xod = (struct XFormObjectData_MetaBall *)xod_base;
+ metaball_coords_and_quats_apply_with_mat4(mb, xod->elem_array, mat);
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+}
+
+/** \} */
diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c
index ed40a4eb948..33c7ffefb8b 100644
--- a/source/blender/editors/object/object_edit.c
+++ b/source/blender/editors/object/object_edit.c
@@ -116,6 +116,7 @@
typedef struct MoveToCollectionData MoveToCollectionData;
static void move_to_collection_menus_items(struct uiLayout *layout,
struct MoveToCollectionData *menu);
+static ListBase selected_objects_get(bContext *C);
/* ************* XXX **************** */
static void error(const char *UNUSED(arg))
@@ -593,7 +594,7 @@ bool ED_object_editmode_enter_ex(Main *bmain, Scene *scene, Object *ob, int flag
if (LIKELY(em)) {
/* order doesn't matter */
EDBM_mesh_normals_update(em);
- BKE_editmesh_tessface_calc(em);
+ BKE_editmesh_looptri_calc(em);
}
WM_main_add_notifier(NC_SCENE | ND_MODE | NS_EDITMODE_MESH, NULL);
@@ -1199,7 +1200,7 @@ static int shade_smooth_exec(bContext *C, wmOperator *op)
}
if (ob->type == OB_MESH) {
- BKE_mesh_smooth_flag_set(ob, !clear);
+ BKE_mesh_smooth_flag_set(ob->data, !clear);
BKE_mesh_batch_cache_dirty_tag(ob->data, BKE_MESH_BATCH_DIRTY_ALL);
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
@@ -1460,6 +1461,23 @@ void OBJECT_OT_mode_set_or_submode(wmOperatorType *ot)
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
}
+static ListBase selected_objects_get(bContext *C)
+{
+ ListBase objects = {NULL};
+
+ if (CTX_wm_space_outliner(C) != NULL) {
+ ED_outliner_selected_objects_get(C, &objects);
+ }
+ else {
+ CTX_DATA_BEGIN (C, Object *, ob, selected_objects) {
+ BLI_addtail(&objects, BLI_genericNodeN(ob));
+ }
+ CTX_DATA_END;
+ }
+
+ return objects;
+}
+
static bool move_to_collection_poll(bContext *C)
{
if (CTX_wm_space_outliner(C) != NULL) {
@@ -1472,7 +1490,7 @@ static bool move_to_collection_poll(bContext *C)
return false;
}
- return ED_operator_object_active_editable(C);
+ return ED_operator_objectmode(C);
}
}
@@ -1498,15 +1516,7 @@ static int move_to_collection_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- if (CTX_wm_space_outliner(C) != NULL) {
- ED_outliner_selected_objects_get(C, &objects);
- }
- else {
- CTX_DATA_BEGIN (C, Object *, ob, selected_objects) {
- BLI_addtail(&objects, BLI_genericNodeN(ob));
- }
- CTX_DATA_END;
- }
+ objects = selected_objects_get(C);
if (is_new) {
char new_collection_name[MAX_NAME];
@@ -1650,6 +1660,13 @@ static int move_to_collection_invoke(bContext *C, wmOperator *op, const wmEvent
{
Scene *scene = CTX_data_scene(C);
+ ListBase objects = selected_objects_get(C);
+ if (BLI_listbase_is_empty(&objects)) {
+ BKE_report(op->reports, RPT_ERROR, "No objects selected");
+ return OPERATOR_CANCELLED;
+ }
+ BLI_freelistN(&objects);
+
/* Reset the menus data for the current master collection, and free previously allocated data. */
move_to_collection_menus_free(&master_collection_menu);
@@ -1674,7 +1691,7 @@ static int move_to_collection_invoke(bContext *C, wmOperator *op, const wmEvent
return move_to_collection_exec(C, op);
}
- Collection *master_collection = BKE_collection_master(scene);
+ Collection *master_collection = scene->master_collection;
/* We need the data to be allocated so it's available during menu drawing.
* Technically we could use wmOperator->customdata. However there is no free callback
diff --git a/source/blender/editors/object/object_gpencil_modifier.c b/source/blender/editors/object/object_gpencil_modifier.c
index 075cd5acad6..9138e65dd2f 100644
--- a/source/blender/editors/object/object_gpencil_modifier.c
+++ b/source/blender/editors/object/object_gpencil_modifier.c
@@ -390,7 +390,7 @@ void OBJECT_OT_gpencil_modifier_add(wmOperatorType *ot)
static int gpencil_edit_modifier_poll_generic(bContext *C, StructRNA *rna_type, int obtype_flag)
{
PointerRNA ptr = CTX_data_pointer_get_type(C, "modifier", rna_type);
- Object *ob = (ptr.id.data) ? ptr.id.data : ED_object_active_context(C);
+ Object *ob = (ptr.owner_id) ? (Object *)ptr.owner_id : ED_object_active_context(C);
if (!ob || ID_IS_LINKED(ob)) {
return 0;
@@ -398,7 +398,7 @@ static int gpencil_edit_modifier_poll_generic(bContext *C, StructRNA *rna_type,
if (obtype_flag && ((1 << ob->type) & obtype_flag) == 0) {
return 0;
}
- if (ptr.id.data && ID_IS_LINKED(ptr.id.data)) {
+ if (ptr.owner_id && ID_IS_LINKED(ptr.owner_id)) {
return 0;
}
diff --git a/source/blender/editors/object/object_hook.c b/source/blender/editors/object/object_hook.c
index 15c5ea40991..bcbf7ddf9a6 100644
--- a/source/blender/editors/object/object_hook.c
+++ b/source/blender/editors/object/object_hook.c
@@ -352,7 +352,7 @@ static bool object_hook_index_array(Main *bmain,
em = me->edit_mesh;
EDBM_mesh_normals_update(em);
- BKE_editmesh_tessface_calc(em);
+ BKE_editmesh_looptri_calc(em);
/* check selected vertices first */
if (return_editmesh_indexar(em, r_tot, r_indexar, r_cent) == 0) {
@@ -436,7 +436,7 @@ static void object_hook_from_context(
HookModifierData *hmd;
if (ptr->data) { /* if modifier context is available, use that */
- ob = ptr->id.data;
+ ob = (Object *)ptr->owner_id;
hmd = ptr->data;
}
else { /* use the provided property */
diff --git a/source/blender/editors/object/object_modifier.c b/source/blender/editors/object/object_modifier.c
index 8818fd71190..88d01936882 100644
--- a/source/blender/editors/object/object_modifier.c
+++ b/source/blender/editors/object/object_modifier.c
@@ -52,6 +52,7 @@
#include "BKE_editmesh.h"
#include "BKE_effect.h"
#include "BKE_global.h"
+#include "BKE_gpencil_modifier.h"
#include "BKE_key.h"
#include "BKE_lattice.h"
#include "BKE_library.h"
@@ -110,6 +111,9 @@ static void object_force_modifier_update_for_bind(Depsgraph *depsgraph, Object *
else if (ELEM(ob->type, OB_CURVE, OB_SURF, OB_FONT)) {
BKE_displist_make_curveTypes(depsgraph, scene_eval, ob_eval, false, false);
}
+ else if (ob->type == OB_GPENCIL) {
+ BKE_gpencil_modifiers_calc(depsgraph, scene_eval, ob_eval);
+ }
}
static void object_force_modifier_bind_simple_options(Depsgraph *depsgraph,
@@ -714,9 +718,9 @@ static int modifier_apply_obdata(
RPT_INFO,
"Applied modifier only changed CV points, not tessellated/bevel vertices");
- vertexCos = BKE_curve_nurbs_vertexCos_get(&curve_eval->nurb, &numVerts);
+ vertexCos = BKE_curve_nurbs_vert_coords_alloc(&curve_eval->nurb, &numVerts);
mti->deformVerts(md_eval, &mectx, NULL, vertexCos, numVerts);
- BK_curve_nurbs_vertexCos_apply(&curve->nurb, vertexCos);
+ BKE_curve_nurbs_vert_coords_apply(&curve->nurb, vertexCos, false);
MEM_freeN(vertexCos);
@@ -918,7 +922,7 @@ bool edit_modifier_poll_generic(bContext *C,
const bool is_editmode_allowed)
{
PointerRNA ptr = CTX_data_pointer_get_type(C, "modifier", rna_type);
- Object *ob = (ptr.id.data) ? ptr.id.data : ED_object_active_context(C);
+ Object *ob = (ptr.owner_id) ? (Object *)ptr.owner_id : ED_object_active_context(C);
if (!ob || ID_IS_LINKED(ob)) {
return 0;
@@ -926,7 +930,7 @@ bool edit_modifier_poll_generic(bContext *C,
if (obtype_flag && ((1 << ob->type) & obtype_flag) == 0) {
return 0;
}
- if (ptr.id.data && ID_IS_LINKED(ptr.id.data)) {
+ if (ptr.owner_id && ID_IS_LINKED(ptr.owner_id)) {
return 0;
}
diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c
index a69f4872e72..06c360ed1cd 100644
--- a/source/blender/editors/object/object_relations.c
+++ b/source/blender/editors/object/object_relations.c
@@ -149,7 +149,7 @@ static int vertex_parent_set_exec(bContext *C, wmOperator *op)
em = me->edit_mesh;
EDBM_mesh_normals_update(em);
- BKE_editmesh_tessface_calc(em);
+ BKE_editmesh_looptri_calc(em);
/* Make sure the evaluated mesh is updated.
*
@@ -1434,7 +1434,7 @@ static int make_links_scene_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- Collection *collection_to = BKE_collection_master(scene_to);
+ Collection *collection_to = scene_to->master_collection;
CTX_DATA_BEGIN (C, Base *, base, selected_bases) {
BKE_collection_object_add(bmain, collection_to, base->object);
}
@@ -1744,7 +1744,7 @@ static Collection *single_object_users_collection(Main *bmain,
}
/* Since master collection has already be duplicated as part of scene copy,
- * we do not duplictae it here.
+ * we do not duplicate it here.
* However, this means its children need to be re-added manually here,
* otherwise their parent lists are empty (which will lead to crashes, see T63101). */
CollectionChild *child_next, *child = collection->children.first;
@@ -1771,7 +1771,7 @@ static void single_object_users(
Main *bmain, Scene *scene, View3D *v3d, const int flag, const bool copy_collections)
{
/* duplicate all the objects of the scene (and matching collections, if required). */
- Collection *master_collection = BKE_collection_master(scene);
+ Collection *master_collection = scene->master_collection;
single_object_users_collection(bmain, scene, master_collection, flag, copy_collections, true);
/* duplicate collections that consist entirely of duplicated objects */
@@ -2426,6 +2426,8 @@ static int make_override_library_exec(bContext *C, wmOperator *op)
if (!ID_IS_LINKED(obact) && obact->instance_collection != NULL &&
ID_IS_LINKED(obact->instance_collection)) {
+ BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
+
Object *obcollection = obact;
Collection *collection = obcollection->instance_collection;
@@ -2471,7 +2473,11 @@ static int make_override_library_exec(bContext *C, wmOperator *op)
DEG_id_tag_update_ex(bmain, &new_ob->id, ID_RECALC_TRANSFORM | ID_RECALC_BASE_FLAGS);
}
/* parent to 'collection' empty */
- if (new_ob->parent == NULL) {
+ /* Disabled for now, according to some artist this is probably not really useful anyway.
+ * And it breaks things like objects parented to bones
+ * (most likely due to missing proper setting of inverse parent matrix?)... */
+ /* Note: we might even actually want to get rid of that instanciating empty... */
+ if (0 && new_ob->parent == NULL) {
new_ob->parent = obcollection;
}
if (new_ob == (Object *)obact->id.newid) {
@@ -2499,7 +2505,7 @@ static int make_override_library_exec(bContext *C, wmOperator *op)
/* Cleanup. */
BKE_main_id_clear_newpoins(bmain);
- BKE_main_id_tag_listbase(&bmain->objects, LIB_TAG_DOIT, false);
+ BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
}
/* Else, poll func ensures us that ID_IS_LINKED(obact) is true. */
else if (obact->type == OB_ARMATURE) {
@@ -2518,7 +2524,7 @@ static int make_override_library_exec(bContext *C, wmOperator *op)
/* Cleanup. */
BKE_main_id_clear_newpoins(bmain);
- BKE_main_id_tag_listbase(&bmain->objects, LIB_TAG_DOIT, false);
+ BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
}
/* TODO: probably more cases where we want to do automated smart things in the future! */
else {
@@ -2706,7 +2712,7 @@ static int object_unlink_data_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- id = pprop.ptr.id.data;
+ id = pprop.ptr.owner_id;
if (GS(id->name) == ID_OB) {
Object *ob = (Object *)id;
diff --git a/source/blender/editors/object/object_remesh.c b/source/blender/editors/object/object_remesh.c
index 1bcac9b4739..5f464084a9b 100644
--- a/source/blender/editors/object/object_remesh.c
+++ b/source/blender/editors/object/object_remesh.c
@@ -108,7 +108,7 @@ static int voxel_remesh_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- Mesh *obj_mesh_copy;
+ Mesh *obj_mesh_copy = NULL;
if (mesh->flag & ME_REMESH_REPROJECT_PAINT_MASK) {
obj_mesh_copy = BKE_mesh_new_nomain_from_template(mesh, mesh->totvert, 0, 0, 0, 0);
CustomData_copy(
@@ -126,7 +126,7 @@ static int voxel_remesh_exec(bContext *C, wmOperator *op)
}
if (mesh->flag & ME_REMESH_SMOOTH_NORMALS) {
- BKE_mesh_smooth_flag_set(ob, true);
+ BKE_mesh_smooth_flag_set(ob->data, true);
}
if (ob->mode == OB_MODE_SCULPT) {
diff --git a/source/blender/editors/object/object_select.c b/source/blender/editors/object/object_select.c
index da06707ebac..28242b986f1 100644
--- a/source/blender/editors/object/object_select.c
+++ b/source/blender/editors/object/object_select.c
@@ -69,6 +69,7 @@
#include "ED_armature.h"
#include "ED_object.h"
+#include "ED_outliner.h"
#include "ED_screen.h"
#include "ED_select_utils.h"
#include "ED_keyframing.h"
@@ -436,6 +437,8 @@ static int object_select_by_type_exec(bContext *C, wmOperator *op)
DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
+ ED_outliner_select_sync_from_object_tag(C);
+
return OPERATOR_FINISHED;
}
@@ -717,6 +720,7 @@ static int object_select_linked_exec(bContext *C, wmOperator *op)
if (changed) {
DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
+ ED_outliner_select_sync_from_object_tag(C);
return OPERATOR_FINISHED;
}
@@ -1100,6 +1104,7 @@ static int object_select_grouped_exec(bContext *C, wmOperator *op)
if (changed) {
DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
+ ED_outliner_select_sync_from_object_tag(C);
return OPERATOR_FINISHED;
}
@@ -1150,6 +1155,8 @@ static int object_select_all_exec(bContext *C, wmOperator *op)
DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
+ ED_outliner_select_sync_from_object_tag(C);
+
return OPERATOR_FINISHED;
}
else if (any_visible == false) {
@@ -1218,6 +1225,8 @@ static int object_select_same_collection_exec(bContext *C, wmOperator *op)
DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
+ ED_outliner_select_sync_from_object_tag(C);
+
return OPERATOR_FINISHED;
}
@@ -1281,6 +1290,8 @@ static int object_select_mirror_exec(bContext *C, wmOperator *op)
DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
+ ED_outliner_select_sync_from_object_tag(C);
+
return OPERATOR_FINISHED;
}
@@ -1369,6 +1380,9 @@ static int object_select_more_exec(bContext *C, wmOperator *UNUSED(op))
Scene *scene = CTX_data_scene(C);
DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
+
+ ED_outliner_select_sync_from_object_tag(C);
+
return OPERATOR_FINISHED;
}
else {
@@ -1399,6 +1413,9 @@ static int object_select_less_exec(bContext *C, wmOperator *UNUSED(op))
Scene *scene = CTX_data_scene(C);
DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
+
+ ED_outliner_select_sync_from_object_tag(C);
+
return OPERATOR_FINISHED;
}
else {
@@ -1448,6 +1465,8 @@ static int object_select_random_exec(bContext *C, wmOperator *op)
DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
+ ED_outliner_select_sync_from_object_tag(C);
+
return OPERATOR_FINISHED;
}
diff --git a/source/blender/editors/object/object_shader_fx.c b/source/blender/editors/object/object_shader_fx.c
index 457d2421253..db0c8c54186 100644
--- a/source/blender/editors/object/object_shader_fx.c
+++ b/source/blender/editors/object/object_shader_fx.c
@@ -294,7 +294,7 @@ void OBJECT_OT_shaderfx_add(wmOperatorType *ot)
static bool edit_shaderfx_poll_generic(bContext *C, StructRNA *rna_type, int obtype_flag)
{
PointerRNA ptr = CTX_data_pointer_get_type(C, "shaderfx", rna_type);
- Object *ob = (ptr.id.data) ? ptr.id.data : ED_object_active_context(C);
+ Object *ob = (ptr.owner_id) ? (Object *)ptr.owner_id : ED_object_active_context(C);
if (!ptr.data) {
CTX_wm_operator_poll_msg_set(C, "Context missing 'shaderfx'");
@@ -307,7 +307,7 @@ static bool edit_shaderfx_poll_generic(bContext *C, StructRNA *rna_type, int obt
if (obtype_flag && ((1 << ob->type) & obtype_flag) == 0) {
return 0;
}
- if (ptr.id.data && ID_IS_LINKED(ptr.id.data)) {
+ if (ptr.owner_id && ID_IS_LINKED(ptr.owner_id)) {
return 0;
}
diff --git a/source/blender/editors/object/object_transform.c b/source/blender/editors/object/object_transform.c
index 975aa0f5bac..2a8b306b085 100644
--- a/source/blender/editors/object/object_transform.c
+++ b/source/blender/editors/object/object_transform.c
@@ -77,7 +77,9 @@
#include "object_intern.h"
-/*************************** Clear Transformation ****************************/
+/* -------------------------------------------------------------------- */
+/** \name Clear Transformation Utilities
+ * \{ */
/* clear location of object */
static void object_clear_loc(Object *ob, const bool clear_delta)
@@ -284,8 +286,6 @@ static void object_clear_scale(Object *ob, const bool clear_delta)
}
}
-/* --------------- */
-
/* generic exec for clear-transform operators */
static int object_clear_transform_generic_exec(bContext *C,
wmOperator *op,
@@ -329,7 +329,11 @@ static int object_clear_transform_generic_exec(bContext *C,
return OPERATOR_FINISHED;
}
-/* --------------- */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Clear Location Operator
+ * \{ */
static int object_location_clear_exec(bContext *C, wmOperator *op)
{
@@ -359,6 +363,12 @@ void OBJECT_OT_location_clear(wmOperatorType *ot)
"Clear delta location in addition to clearing the normal location transform");
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Clear Rotation Operator
+ * \{ */
+
static int object_rotation_clear_exec(bContext *C, wmOperator *op)
{
return object_clear_transform_generic_exec(C, op, object_clear_rot, ANIM_KS_ROTATION_ID);
@@ -387,6 +397,12 @@ void OBJECT_OT_rotation_clear(wmOperatorType *ot)
"Clear delta rotation in addition to clearing the normal rotation transform");
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Clear Scale Operator
+ * \{ */
+
static int object_scale_clear_exec(bContext *C, wmOperator *op)
{
return object_clear_transform_generic_exec(C, op, object_clear_scale, ANIM_KS_SCALING_ID);
@@ -415,7 +431,11 @@ void OBJECT_OT_scale_clear(wmOperatorType *ot)
"Clear delta scale in addition to clearing the normal scale transform");
}
-/* --------------- */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Clear Origin Operator
+ * \{ */
static int object_origin_clear_exec(bContext *C, wmOperator *UNUSED(op))
{
@@ -457,7 +477,11 @@ void OBJECT_OT_origin_clear(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
-/*************************** Apply Transformation ****************************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Apply Transformation Operator
+ * \{ */
/* use this when the loc/size/rot of the parent has changed but the children
* should stay in the same place, e.g. for apply-size-rot or object center */
@@ -744,7 +768,8 @@ static int apply_objects_internal(bContext *C,
BKE_mesh_calc_normals(me);
}
else if (ob->type == OB_ARMATURE) {
- ED_armature_transform_apply(bmain, ob, mat, do_props);
+ bArmature *arm = ob->data;
+ BKE_armature_transform(arm, mat, do_props);
}
else if (ob->type == OB_LATTICE) {
Lattice *lt = ob->data;
@@ -960,7 +985,11 @@ void OBJECT_OT_transform_apply(wmOperatorType *ot)
"Modify properties such as curve vertex radius, font size and bone envelope");
}
-/********************* Set Object Center ************************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Set Object Center Operator
+ * \{ */
enum {
GEOMETRY_TO_ORIGIN = 0,
@@ -1152,7 +1181,8 @@ static int object_origin_set_exec(bContext *C, wmOperator *op)
else if (ELEM(ob->type, OB_CURVE, OB_SURF)) {
Curve *cu = ob->data;
- if (centermode == ORIGIN_TO_CURSOR) { /* done */
+ if (centermode == ORIGIN_TO_CURSOR) {
+ /* done */
}
else if (around == V3D_AROUND_CENTER_MEDIAN) {
BKE_curve_center_median(cu, cent);
@@ -1244,7 +1274,8 @@ static int object_origin_set_exec(bContext *C, wmOperator *op)
else if (ob->type == OB_MBALL) {
MetaBall *mb = ob->data;
- if (centermode == ORIGIN_TO_CURSOR) { /* done */
+ if (centermode == ORIGIN_TO_CURSOR) {
+ /* done */
}
else if (around == V3D_AROUND_CENTER_MEDIAN) {
BKE_mball_center_median(mb, cent);
@@ -1270,7 +1301,8 @@ static int object_origin_set_exec(bContext *C, wmOperator *op)
else if (ob->type == OB_LATTICE) {
Lattice *lt = ob->data;
- if (centermode == ORIGIN_TO_CURSOR) { /* done */
+ if (centermode == ORIGIN_TO_CURSOR) {
+ /* done */
}
else if (around == V3D_AROUND_CENTER_MEDIAN) {
BKE_lattice_center_median(lt, cent);
@@ -1323,11 +1355,6 @@ static int object_origin_set_exec(bContext *C, wmOperator *op)
invert_m4_m4(inverse_diff_mat, diff_mat);
for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) {
for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
- /* skip strokes that are invalid for current view */
- if (ED_gpencil_stroke_can_use(C, gps) == false) {
- continue;
- }
-
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
float mpt[3];
mul_v3_m4v3(mpt, inverse_diff_mat, &pt->x);
@@ -1509,6 +1536,8 @@ void OBJECT_OT_origin_set(wmOperatorType *ot)
RNA_def_enum(ot->srna, "center", prop_set_bounds_types, V3D_AROUND_CENTER_MEDIAN, "Center", "");
}
+/** \} */
+
/* -------------------------------------------------------------------- */
/** \name Transform Axis Target
*
diff --git a/source/blender/editors/object/object_vgroup.c b/source/blender/editors/object/object_vgroup.c
index 08fe5e818b2..ce385b12608 100644
--- a/source/blender/editors/object/object_vgroup.c
+++ b/source/blender/editors/object/object_vgroup.c
@@ -3794,7 +3794,7 @@ static int vgroup_sort_name(const void *def_a_ptr, const void *def_b_ptr)
const bDeformGroup *def_a = def_a_ptr;
const bDeformGroup *def_b = def_b_ptr;
- return BLI_natstrcmp(def_a->name, def_b->name);
+ return BLI_strcasecmp_natural(def_a->name, def_b->name);
}
/**
diff --git a/source/blender/editors/physics/particle_object.c b/source/blender/editors/physics/particle_object.c
index 0999d1f3588..fbaf02b7b3f 100644
--- a/source/blender/editors/physics/particle_object.c
+++ b/source/blender/editors/physics/particle_object.c
@@ -186,7 +186,7 @@ static int new_particle_settings_exec(bContext *C, wmOperator *UNUSED(op))
part = BKE_particlesettings_add(bmain, "ParticleSettings");
}
- ob = ptr.id.data;
+ ob = (Object *)ptr.owner_id;
if (psys->part) {
id_us_min(&psys->part->id);
@@ -226,7 +226,7 @@ static int new_particle_target_exec(bContext *C, wmOperator *UNUSED(op))
Main *bmain = CTX_data_main(C);
PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem);
ParticleSystem *psys = ptr.data;
- Object *ob = ptr.id.data;
+ Object *ob = (Object *)ptr.owner_id;
ParticleTarget *pt;
@@ -273,7 +273,7 @@ static int remove_particle_target_exec(bContext *C, wmOperator *UNUSED(op))
Main *bmain = CTX_data_main(C);
PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem);
ParticleSystem *psys = ptr.data;
- Object *ob = ptr.id.data;
+ Object *ob = (Object *)ptr.owner_id;
ParticleTarget *pt;
@@ -323,7 +323,7 @@ static int target_move_up_exec(bContext *C, wmOperator *UNUSED(op))
{
PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem);
ParticleSystem *psys = ptr.data;
- Object *ob = ptr.id.data;
+ Object *ob = (Object *)ptr.owner_id;
ParticleTarget *pt;
if (!psys) {
@@ -363,7 +363,7 @@ static int target_move_down_exec(bContext *C, wmOperator *UNUSED(op))
{
PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem);
ParticleSystem *psys = ptr.data;
- Object *ob = ptr.id.data;
+ Object *ob = (Object *)ptr.owner_id;
ParticleTarget *pt;
if (!psys) {
@@ -1140,6 +1140,7 @@ static bool copy_particle_systems_to_object(const bContext *C,
/* append to the object */
BLI_addtail(&ob_to->particlesystem, psys);
+ psys_unique_name(ob_to, psys, psys->name);
/* add a particle system modifier for each system */
md = modifier_new(eModifierType_ParticleSystem);
diff --git a/source/blender/editors/physics/physics_intern.h b/source/blender/editors/physics/physics_intern.h
index 9ebbba07fdf..fc2f3d21bb6 100644
--- a/source/blender/editors/physics/physics_intern.h
+++ b/source/blender/editors/physics/physics_intern.h
@@ -30,7 +30,6 @@ struct PTCacheEdit;
struct ParticleSystem;
struct PointCache;
struct Scene;
-struct ViewLayer;
struct wmOperatorType;
/* particle_edit.c */
diff --git a/source/blender/editors/physics/physics_pointcache.c b/source/blender/editors/physics/physics_pointcache.c
index bc2f1d6cef6..700a94e4f93 100644
--- a/source/blender/editors/physics/physics_pointcache.c
+++ b/source/blender/editors/physics/physics_pointcache.c
@@ -57,7 +57,7 @@ static bool ptcache_bake_all_poll(bContext *C)
static bool ptcache_poll(bContext *C)
{
PointerRNA ptr = CTX_data_pointer_get_type(C, "point_cache", &RNA_PointCache);
- return (ptr.data && ptr.id.data);
+ return (ptr.data && ptr.owner_id);
}
typedef struct PointCacheJob {
@@ -165,7 +165,7 @@ static PTCacheBaker *ptcache_baker_create(bContext *C, wmOperator *op, bool all)
if (!all) {
PointerRNA ptr = CTX_data_pointer_get_type(C, "point_cache", &RNA_PointCache);
- Object *ob = ptr.id.data;
+ Object *ob = (Object *)ptr.owner_id;
PointCache *cache = ptr.data;
baker->pid = BKE_ptcache_id_find(ob, baker->scene, cache);
}
@@ -300,7 +300,7 @@ static int ptcache_free_bake_exec(bContext *C, wmOperator *UNUSED(op))
{
PointerRNA ptr = CTX_data_pointer_get_type(C, "point_cache", &RNA_PointCache);
PointCache *cache = ptr.data;
- Object *ob = ptr.id.data;
+ Object *ob = (Object *)ptr.owner_id;
ptcache_free_bake(cache);
@@ -312,7 +312,7 @@ static int ptcache_bake_from_cache_exec(bContext *C, wmOperator *UNUSED(op))
{
PointerRNA ptr = CTX_data_pointer_get_type(C, "point_cache", &RNA_PointCache);
PointCache *cache = ptr.data;
- Object *ob = ptr.id.data;
+ Object *ob = (Object *)ptr.owner_id;
cache->flag |= PTCACHE_BAKED;
@@ -372,7 +372,7 @@ static int ptcache_add_new_exec(bContext *C, wmOperator *UNUSED(op))
{
Scene *scene = CTX_data_scene(C);
PointerRNA ptr = CTX_data_pointer_get_type(C, "point_cache", &RNA_PointCache);
- Object *ob = ptr.id.data;
+ Object *ob = (Object *)ptr.owner_id;
PointCache *cache = ptr.data;
PTCacheID pid = BKE_ptcache_id_find(ob, scene, cache);
@@ -392,7 +392,7 @@ static int ptcache_remove_exec(bContext *C, wmOperator *UNUSED(op))
{
PointerRNA ptr = CTX_data_pointer_get_type(C, "point_cache", &RNA_PointCache);
Scene *scene = CTX_data_scene(C);
- Object *ob = ptr.id.data;
+ Object *ob = (Object *)ptr.owner_id;
PointCache *cache = ptr.data;
PTCacheID pid = BKE_ptcache_id_find(ob, scene, cache);
diff --git a/source/blender/editors/render/render_intern.h b/source/blender/editors/render/render_intern.h
index 50f0b53c037..88b913b84ca 100644
--- a/source/blender/editors/render/render_intern.h
+++ b/source/blender/editors/render/render_intern.h
@@ -24,7 +24,6 @@
#ifndef __RENDER_INTERN_H__
#define __RENDER_INTERN_H__
-struct RenderEngine;
struct ScrArea;
struct bContext;
struct wmOperatorType;
diff --git a/source/blender/editors/render/render_shading.c b/source/blender/editors/render/render_shading.c
index 01740c13c9d..9f13431f25a 100644
--- a/source/blender/editors/render/render_shading.c
+++ b/source/blender/editors/render/render_shading.c
@@ -1836,7 +1836,7 @@ void SCENE_OT_freestyle_stroke_material_create(wmOperatorType *ot)
static int texture_slot_move_exec(bContext *C, wmOperator *op)
{
- ID *id = CTX_data_pointer_get_type(C, "texture_slot", &RNA_TextureSlot).id.data;
+ ID *id = CTX_data_pointer_get_type(C, "texture_slot", &RNA_TextureSlot).owner_id;
if (id) {
MTex **mtex_ar, *mtexswap;
@@ -2031,7 +2031,7 @@ static void paste_mtex_copybuf(ID *id)
static int copy_mtex_exec(bContext *C, wmOperator *UNUSED(op))
{
- ID *id = CTX_data_pointer_get_type(C, "texture_slot", &RNA_TextureSlot).id.data;
+ ID *id = CTX_data_pointer_get_type(C, "texture_slot", &RNA_TextureSlot).owner_id;
if (id == NULL) {
/* copying empty slot */
@@ -2046,7 +2046,7 @@ static int copy_mtex_exec(bContext *C, wmOperator *UNUSED(op))
static bool copy_mtex_poll(bContext *C)
{
- ID *id = CTX_data_pointer_get_type(C, "texture_slot", &RNA_TextureSlot).id.data;
+ ID *id = CTX_data_pointer_get_type(C, "texture_slot", &RNA_TextureSlot).owner_id;
return (id != NULL);
}
@@ -2069,7 +2069,7 @@ void TEXTURE_OT_slot_copy(wmOperatorType *ot)
static int paste_mtex_exec(bContext *C, wmOperator *UNUSED(op))
{
- ID *id = CTX_data_pointer_get_type(C, "texture_slot", &RNA_TextureSlot).id.data;
+ ID *id = CTX_data_pointer_get_type(C, "texture_slot", &RNA_TextureSlot).owner_id;
if (id == NULL) {
Material *ma = CTX_data_pointer_get_type(C, "material", &RNA_Material).data;
diff --git a/source/blender/editors/render/render_view.c b/source/blender/editors/render/render_view.c
index cd5edcdc3f4..3154d5d0985 100644
--- a/source/blender/editors/render/render_view.c
+++ b/source/blender/editors/render/render_view.c
@@ -223,8 +223,8 @@ ScrArea *render_view_open(bContext *C, int mx, int my, ReportList *reports)
/* get the correct image, and scale it */
sima->image = BKE_image_verify_viewer(bmain, IMA_TYPE_R_RESULT, "Render Result");
- /* if we're rendering to full screen, set appropriate hints on image editor
- * so it can restore properly on pressing esc */
+ /* If we're rendering to full screen, set appropriate hints on image editor
+ * so it can restore properly on pressing escape. */
if (sa->full) {
sima->flag |= SI_FULLWINDOW;
diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c
index 1775a0c55a2..b0c9e3b9378 100644
--- a/source/blender/editors/screen/area.c
+++ b/source/blender/editors/screen/area.c
@@ -1052,16 +1052,11 @@ static void region_azones_scrollbars_initialize(ScrArea *sa, ARegion *ar)
}
/* *************************************************************** */
-
-static void region_azones_add(const bScreen *screen, ScrArea *sa, ARegion *ar, const int alignment)
+static void region_azones_add_edge(ScrArea *sa,
+ ARegion *ar,
+ const int alignment,
+ const bool is_fullscreen)
{
- const bool is_fullscreen = screen->state == SCREENFULL;
-
- /* Only display tab or icons when the header region is hidden
- * (not the tool header - they overlap). */
- if (ar->regiontype == RGN_TYPE_TOOL_HEADER) {
- return;
- }
/* edge code (t b l r) is along which area edge azone will be drawn */
if (alignment == RGN_ALIGN_TOP) {
@@ -1076,6 +1071,25 @@ static void region_azones_add(const bScreen *screen, ScrArea *sa, ARegion *ar, c
else if (alignment == RGN_ALIGN_LEFT) {
region_azone_edge_initialize(sa, ar, AE_RIGHT_TO_TOPLEFT, is_fullscreen);
}
+}
+
+static void region_azones_add(const bScreen *screen, ScrArea *sa, ARegion *ar)
+{
+ const bool is_fullscreen = screen->state == SCREENFULL;
+
+ /* Only display tab or icons when the header region is hidden
+ * (not the tool header - they overlap). */
+ if (ar->regiontype == RGN_TYPE_TOOL_HEADER) {
+ return;
+ }
+
+ region_azones_add_edge(sa, ar, RGN_ALIGN_ENUM_FROM_MASK(ar->alignment), is_fullscreen);
+
+ /* For a split region also continue the azone edge from the next region if this region is aligned
+ * with the next */
+ if ((ar->alignment & RGN_SPLIT_PREV) && ar->prev) {
+ region_azones_add_edge(sa, ar, RGN_ALIGN_ENUM_FROM_MASK(ar->prev->alignment), is_fullscreen);
+ }
if (is_fullscreen) {
fullscreen_azone_initialize(sa, ar);
@@ -1251,9 +1265,6 @@ static void region_rect_recursive(
else if (ED_area_is_global(sa)) {
prefsizey = ED_region_global_size_y();
}
- else if (ar->regiontype == RGN_TYPE_UI && sa->spacetype == SPACE_FILE) {
- prefsizey = UI_UNIT_Y * 2 + (UI_UNIT_Y / 2);
- }
else {
prefsizey = UI_DPI_FAC * (ar->sizey > 1 ? ar->sizey + 0.5f : ar->type->prefsizey);
}
@@ -1695,7 +1706,7 @@ void ED_area_update_region_sizes(wmWindowManager *wm, wmWindow *win, ScrArea *ar
}
/* Some AZones use View2D data which is only updated in region init, so call that first! */
- region_azones_add(screen, area, ar, RGN_ALIGN_ENUM_FROM_MASK(ar->alignment));
+ region_azones_add(screen, area, ar);
}
ED_area_azones_update(area, &win->eventstate->x);
@@ -1766,7 +1777,7 @@ void ED_area_initialize(wmWindowManager *wm, wmWindow *win, ScrArea *sa)
}
/* Some AZones use View2D data which is only updated in region init, so call that first! */
- region_azones_add(screen, sa, ar, RGN_ALIGN_ENUM_FROM_MASK(ar->alignment));
+ region_azones_add(screen, sa, ar);
}
/* Avoid re-initializing tools while resizing the window. */
diff --git a/source/blender/editors/screen/screen_context.c b/source/blender/editors/screen/screen_context.c
index 2eb7f732534..6f8b25f782b 100644
--- a/source/blender/editors/screen/screen_context.c
+++ b/source/blender/editors/screen/screen_context.c
@@ -533,7 +533,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult
gpd_ptr = ED_gpencil_data_get_pointers_direct((ID *)sc, sa, scene, obact, &ptr);
if (gpd_ptr) {
- CTX_data_pointer_set(result, ptr.id.data, ptr.type, ptr.data);
+ CTX_data_pointer_set(result, ptr.owner_id, ptr.type, ptr.data);
return 1;
}
}
diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c
index dddc33e3ad0..b37aa47aba6 100644
--- a/source/blender/editors/screen/screen_edit.c
+++ b/source/blender/editors/screen/screen_edit.c
@@ -292,33 +292,45 @@ void screen_new_activate_prepare(const wmWindow *win, bScreen *screen_new)
/* used with join operator */
int area_getorientation(ScrArea *sa, ScrArea *sb)
{
- ScrVert *sav1, *sav2, *sav3, *sav4;
- ScrVert *sbv1, *sbv2, *sbv3, *sbv4;
-
if (sa == NULL || sb == NULL) {
return -1;
}
- sav1 = sa->v1;
- sav2 = sa->v2;
- sav3 = sa->v3;
- sav4 = sa->v4;
- sbv1 = sb->v1;
- sbv2 = sb->v2;
- sbv3 = sb->v3;
- sbv4 = sb->v4;
+ ScrVert *saBL = sa->v1;
+ ScrVert *saTL = sa->v2;
+ ScrVert *saTR = sa->v3;
+ ScrVert *saBR = sa->v4;
- if (sav1 == sbv4 && sav2 == sbv3) { /* sa to right of sb = W */
- return 0;
+ ScrVert *sbBL = sb->v1;
+ ScrVert *sbTL = sb->v2;
+ ScrVert *sbTR = sb->v3;
+ ScrVert *sbBR = sb->v4;
+
+ int tolerance = U.pixelsize * 4;
+
+ if (saBL->vec.x == sbBR->vec.x && saTL->vec.x == sbTR->vec.x) { /* sa to right of sb = W */
+ if ((ABS(saBL->vec.y - sbBR->vec.y) <= tolerance) &&
+ (ABS(saTL->vec.y - sbTR->vec.y) <= tolerance)) {
+ return 0;
+ }
}
- else if (sav2 == sbv1 && sav3 == sbv4) { /* sa to bottom of sb = N */
- return 1;
+ else if (saTL->vec.y == sbBL->vec.y && saTR->vec.y == sbBR->vec.y) { /* sa to bottom of sb = N */
+ if ((ABS(saTL->vec.x - sbBL->vec.x) <= tolerance) &&
+ (ABS(saTR->vec.x - sbBR->vec.x) <= tolerance)) {
+ return 1;
+ }
}
- else if (sav3 == sbv2 && sav4 == sbv1) { /* sa to left of sb = E */
- return 2;
+ else if (saTR->vec.x == sbTL->vec.x && saBR->vec.x == sbBL->vec.x) { /* sa to left of sb = E */
+ if ((ABS(saTR->vec.y - sbTL->vec.y) <= tolerance) &&
+ (ABS(saBR->vec.y - sbBL->vec.y) <= tolerance)) {
+ return 2;
+ }
}
- else if (sav1 == sbv2 && sav4 == sbv3) { /* sa on top of sb = S*/
- return 3;
+ else if (saBL->vec.y == sbTL->vec.y && saBR->vec.y == sbTR->vec.y) { /* sa on top of sb = S*/
+ if ((ABS(saBL->vec.x - sbTL->vec.x) <= tolerance) &&
+ (ABS(saBR->vec.x - sbTR->vec.x) <= tolerance)) {
+ return 3;
+ }
}
return -1;
@@ -329,36 +341,50 @@ int area_getorientation(ScrArea *sa, ScrArea *sb)
*/
int screen_area_join(bContext *C, bScreen *scr, ScrArea *sa1, ScrArea *sa2)
{
- int dir;
-
- dir = area_getorientation(sa1, sa2);
- /*printf("dir is : %i\n", dir);*/
+ int dir = area_getorientation(sa1, sa2);
if (dir == -1) {
return 0;
}
- if (dir == 0) {
- sa1->v1 = sa2->v1;
- sa1->v2 = sa2->v2;
+ /* Align areas if they are not. Do sanity checking before getting here. */
+
+ if (dir == 0 || dir == 2) {
+ /* horizontal join, so vertically align source vert to target */
+ sa2->v1->vec.y = sa1->v1->vec.y; /* vertical align sa1 BL */
+ sa2->v2->vec.y = sa1->v2->vec.y; /* vertical align sa1 TL */
+ sa2->v3->vec.y = sa1->v3->vec.y; /* vertical align sa1 TR */
+ sa2->v4->vec.y = sa1->v4->vec.y; /* vertical align sa1 BR */
+ }
+ else {
+ /* vertical join, so horizontally align source verts to target */
+ sa2->v1->vec.x = sa1->v1->vec.x; /* vertical align sa1 BL */
+ sa2->v2->vec.x = sa1->v2->vec.x; /* vertical align sa1 TL */
+ sa2->v3->vec.x = sa1->v3->vec.x; /* vertical align sa1 TR */
+ sa2->v4->vec.x = sa1->v4->vec.x; /* vertical align sa1 BR */
+ }
+
+ if (dir == 0) { /* sa1 to right of sa2 = W */
+ sa1->v1 = sa2->v1; /* BL */
+ sa1->v2 = sa2->v2; /* TL */
screen_geom_edge_add(scr, sa1->v2, sa1->v3);
screen_geom_edge_add(scr, sa1->v1, sa1->v4);
}
- else if (dir == 1) {
- sa1->v2 = sa2->v2;
- sa1->v3 = sa2->v3;
+ else if (dir == 1) { /* sa1 to bottom of sa2 = N */
+ sa1->v2 = sa2->v2; /* TL */
+ sa1->v3 = sa2->v3; /* TR */
screen_geom_edge_add(scr, sa1->v1, sa1->v2);
screen_geom_edge_add(scr, sa1->v3, sa1->v4);
}
- else if (dir == 2) {
- sa1->v3 = sa2->v3;
- sa1->v4 = sa2->v4;
+ else if (dir == 2) { /* sa1 to left of sa2 = E */
+ sa1->v3 = sa2->v3; /* TR */
+ sa1->v4 = sa2->v4; /* BR */
screen_geom_edge_add(scr, sa1->v2, sa1->v3);
screen_geom_edge_add(scr, sa1->v1, sa1->v4);
}
- else if (dir == 3) {
- sa1->v1 = sa2->v1;
- sa1->v4 = sa2->v4;
+ else if (dir == 3) { /* sa1 on top of sa2 = S */
+ sa1->v1 = sa2->v1; /* BL */
+ sa1->v4 = sa2->v4; /* BR */
screen_geom_edge_add(scr, sa1->v1, sa1->v2);
screen_geom_edge_add(scr, sa1->v3, sa1->v4);
}
@@ -1116,7 +1142,7 @@ void ED_screen_full_prevspace(bContext *C, ScrArea *sa)
void ED_screen_restore_temp_type(bContext *C, ScrArea *sa)
{
- /* incase nether functions below run */
+ /* In case nether functions below run. */
ED_area_tag_redraw(sa);
if (sa->flag & AREA_FLAG_TEMP_TYPE) {
diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c
index 8377fd0e128..50e5597ac0c 100644
--- a/source/blender/editors/screen/screen_ops.c
+++ b/source/blender/editors/screen/screen_ops.c
@@ -1121,6 +1121,47 @@ static void SCREEN_OT_actionzone(wmOperatorType *ot)
/** \} */
/* -------------------------------------------------------------------- */
+/** \name Area edge detection utility
+ * \{ */
+
+static ScrEdge *screen_area_edge_from_cursor(const bContext *C,
+ const int cursor[2],
+ ScrArea **r_sa1,
+ ScrArea **r_sa2)
+{
+ wmWindow *win = CTX_wm_window(C);
+ bScreen *sc = CTX_wm_screen(C);
+ ScrEdge *actedge;
+ rcti window_rect;
+ WM_window_rect_calc(win, &window_rect);
+ actedge = screen_geom_area_map_find_active_scredge(
+ AREAMAP_FROM_SCREEN(sc), &window_rect, cursor[0], cursor[1]);
+ *r_sa1 = NULL;
+ *r_sa2 = NULL;
+ if (actedge == NULL) {
+ return NULL;
+ }
+ int borderwidth = (4 * UI_DPI_FAC);
+ ScrArea *sa1, *sa2;
+ if (screen_geom_edge_is_horizontal(actedge)) {
+ sa1 = BKE_screen_find_area_xy(sc, SPACE_TYPE_ANY, cursor[0], cursor[1] + borderwidth);
+ sa2 = BKE_screen_find_area_xy(sc, SPACE_TYPE_ANY, cursor[0], cursor[1] - borderwidth);
+ }
+ else {
+ sa1 = BKE_screen_find_area_xy(sc, SPACE_TYPE_ANY, cursor[0] + borderwidth, cursor[1]);
+ sa2 = BKE_screen_find_area_xy(sc, SPACE_TYPE_ANY, cursor[0] - borderwidth, cursor[1]);
+ }
+ bool isGlobal = ((sa1 && ED_area_is_global(sa1)) || (sa2 && ED_area_is_global(sa2)));
+ if (!isGlobal) {
+ *r_sa1 = sa1;
+ *r_sa2 = sa2;
+ }
+ return actedge;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Swap Area Operator
* \{ */
@@ -1139,6 +1180,7 @@ static void SCREEN_OT_actionzone(wmOperatorType *ot)
* callbacks:
*
* invoke() gets called on shift+lmb drag in action-zone
+ * exec() execute without any user interaction, based on properties
* call init(), add handler
*
* modal() accept modal events while doing it
@@ -1229,6 +1271,19 @@ static int area_swap_modal(bContext *C, wmOperator *op, const wmEvent *event)
return OPERATOR_RUNNING_MODAL;
}
+static int area_swap_exec(bContext *C, wmOperator *op)
+{
+ ScrArea *sa1, *sa2;
+ int cursor[2];
+ RNA_int_get_array(op->ptr, "cursor", cursor);
+ screen_area_edge_from_cursor(C, cursor, &sa1, &sa2);
+ if (sa1 == NULL || sa2 == NULL) {
+ return OPERATOR_CANCELLED;
+ }
+ ED_area_swapspace(C, sa1, sa2);
+ return OPERATOR_FINISHED;
+}
+
static void SCREEN_OT_area_swap(wmOperatorType *ot)
{
ot->name = "Swap Areas";
@@ -1237,10 +1292,15 @@ static void SCREEN_OT_area_swap(wmOperatorType *ot)
ot->invoke = area_swap_invoke;
ot->modal = area_swap_modal;
- ot->poll = ED_operator_areaactive;
+ ot->exec = area_swap_exec;
+ ot->poll = screen_active_editable;
ot->cancel = area_swap_cancel;
ot->flag = OPTYPE_BLOCKING;
+
+ /* rna */
+ RNA_def_int_vector(
+ ot->srna, "cursor", 2, NULL, INT_MIN, INT_MAX, "Cursor", "", INT_MIN, INT_MAX);
}
/** \} */
@@ -2458,6 +2518,14 @@ static int area_max_regionsize(ScrArea *sa, ARegion *scalear, AZEdge edge)
return dist;
}
+static bool is_split_edge(const int alignment, const AZEdge edge)
+{
+ return ((alignment == RGN_ALIGN_BOTTOM) && (edge == AE_TOP_TO_BOTTOMRIGHT)) ||
+ ((alignment == RGN_ALIGN_TOP) && (edge == AE_BOTTOM_TO_TOPLEFT)) ||
+ ((alignment == RGN_ALIGN_LEFT) && (edge == AE_RIGHT_TO_TOPLEFT)) ||
+ ((alignment == RGN_ALIGN_RIGHT) && (edge == AE_LEFT_TO_TOPRIGHT));
+}
+
static int region_scale_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
sActionzoneData *sad = event->customdata;
@@ -2476,7 +2544,16 @@ static int region_scale_invoke(bContext *C, wmOperator *op, const wmEvent *event
op->customdata = rmd;
rmd->az = az;
- rmd->ar = az->ar;
+ /* special case for region within region - this allows the scale of
+ * the parent region if the azone edge is not the edge splitting
+ * both regions */
+ if ((az->ar->alignment & RGN_SPLIT_PREV) && az->ar->prev &&
+ !is_split_edge(RGN_ALIGN_ENUM_FROM_MASK(az->ar->alignment), az->edge)) {
+ rmd->ar = az->ar->prev;
+ }
+ else {
+ rmd->ar = az->ar;
+ }
rmd->sa = sad->sa1;
rmd->edge = az->edge;
rmd->origx = event->x;
@@ -3163,40 +3240,19 @@ static void area_join_draw_cb(const struct wmWindow *UNUSED(win), void *userdata
/* validate selection inside screen, set variables OK */
/* return 0: init failed */
-/* XXX todo: find edge based on (x,y) and set other area? */
-static int area_join_init(bContext *C, wmOperator *op)
+static int area_join_init(bContext *C, wmOperator *op, ScrArea *sa1, ScrArea *sa2)
{
- const wmWindow *win = CTX_wm_window(C);
- bScreen *screen = CTX_wm_screen(C);
- ScrArea *sa1, *sa2;
- sAreaJoinData *jd = NULL;
- int x1, y1;
- int x2, y2;
-
- /* required properties, make negative to get return 0 if not set by caller */
- x1 = RNA_int_get(op->ptr, "min_x");
- y1 = RNA_int_get(op->ptr, "min_y");
- x2 = RNA_int_get(op->ptr, "max_x");
- y2 = RNA_int_get(op->ptr, "max_y");
-
- sa1 = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, x1, y1);
- if (sa1 == NULL) {
- sa1 = BKE_screen_area_map_find_area_xy(&win->global_areas, SPACE_TYPE_ANY, x1, y1);
- }
- sa2 = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, x2, y2);
- if (sa2 == NULL) {
- sa2 = BKE_screen_area_map_find_area_xy(&win->global_areas, SPACE_TYPE_ANY, x2, y2);
- }
- if ((sa1 && ED_area_is_global(sa1)) || (sa2 && ED_area_is_global(sa2))) {
- BKE_report(
- op->reports, RPT_ERROR, "Global areas (Top Bar, Status Bar) do not support joining");
- return 0;
+ if (sa1 == NULL || sa2 == NULL) {
+ /* Get areas from cursor location if not specified. */
+ int cursor[2];
+ RNA_int_get_array(op->ptr, "cursor", cursor);
+ screen_area_edge_from_cursor(C, cursor, &sa1, &sa2);
}
- else if (sa1 == NULL || sa2 == NULL || sa1 == sa2) {
+ if (sa1 == NULL || sa2 == NULL) {
return 0;
}
- jd = (sAreaJoinData *)MEM_callocN(sizeof(sAreaJoinData), "op_area_join");
+ sAreaJoinData *jd = MEM_callocN(sizeof(sAreaJoinData), "op_area_join");
jd->sa1 = sa1;
jd->sa2 = sa2;
@@ -3249,7 +3305,7 @@ static void area_join_exit(bContext *C, wmOperator *op)
static int area_join_exec(bContext *C, wmOperator *op)
{
- if (!area_join_init(C, op)) {
+ if (!area_join_init(C, op, NULL, NULL)) {
return OPERATOR_CANCELLED;
}
@@ -3279,16 +3335,11 @@ static int area_join_invoke(bContext *C, wmOperator *op, const wmEvent *event)
if (sad->sa1 == sad->sa2) {
return OPERATOR_PASS_THROUGH;
}
-
- /* prepare operator state vars */
- RNA_int_set(op->ptr, "min_x", sad->sa1->totrct.xmin);
- RNA_int_set(op->ptr, "min_y", sad->sa1->totrct.ymin);
- RNA_int_set(op->ptr, "max_x", sad->sa2->totrct.xmin);
- RNA_int_set(op->ptr, "max_y", sad->sa2->totrct.ymin);
- }
-
- if (!area_join_init(C, op)) {
- return OPERATOR_CANCELLED;
+ else {
+ if (!area_join_init(C, op, sad->sa1, sad->sa2)) {
+ return OPERATOR_CANCELLED;
+ }
+ }
}
/* add temp handler */
@@ -3309,7 +3360,14 @@ static int area_join_modal(bContext *C, wmOperator *op, const wmEvent *event)
{
bScreen *sc = CTX_wm_screen(C);
wmWindow *win = CTX_wm_window(C);
- sAreaJoinData *jd = (sAreaJoinData *)op->customdata;
+ sAreaJoinData *jd;
+
+ if (op->customdata == NULL) {
+ if (!area_join_init(C, op, NULL, NULL)) {
+ return OPERATOR_CANCELLED;
+ }
+ }
+ jd = (sAreaJoinData *)op->customdata;
/* execute the events */
switch (event->type) {
@@ -3419,10 +3477,8 @@ static void SCREEN_OT_area_join(wmOperatorType *ot)
ot->flag = OPTYPE_BLOCKING | OPTYPE_INTERNAL;
/* rna */
- RNA_def_int(ot->srna, "min_x", -100, INT_MIN, INT_MAX, "X 1", "", INT_MIN, INT_MAX);
- RNA_def_int(ot->srna, "min_y", -100, INT_MIN, INT_MAX, "Y 1", "", INT_MIN, INT_MAX);
- RNA_def_int(ot->srna, "max_x", -100, INT_MIN, INT_MAX, "X 2", "", INT_MIN, INT_MAX);
- RNA_def_int(ot->srna, "max_y", -100, INT_MIN, INT_MAX, "Y 2", "", INT_MIN, INT_MAX);
+ RNA_def_int_vector(
+ ot->srna, "cursor", 2, NULL, INT_MIN, INT_MAX, "Cursor", "", INT_MIN, INT_MAX);
}
/** \} */
@@ -3433,36 +3489,74 @@ static void SCREEN_OT_area_join(wmOperatorType *ot)
static int screen_area_options_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
- const wmWindow *win = CTX_wm_window(C);
- const bScreen *sc = CTX_wm_screen(C);
uiPopupMenu *pup;
uiLayout *layout;
PointerRNA ptr;
- ScrEdge *actedge;
- rcti window_rect;
- WM_window_rect_calc(win, &window_rect);
- actedge = screen_geom_area_map_find_active_scredge(
- AREAMAP_FROM_SCREEN(sc), &window_rect, event->x, event->y);
+ ScrArea *sa1, *sa2;
- if (actedge == NULL) {
+ if (screen_area_edge_from_cursor(C, &event->x, &sa1, &sa2) == NULL) {
return OPERATOR_CANCELLED;
}
pup = UI_popup_menu_begin(C, WM_operatortype_name(op->type, op->ptr), ICON_NONE);
layout = UI_popup_menu_layout(pup);
- uiItemFullO(
- layout, "SCREEN_OT_area_split", NULL, ICON_NONE, NULL, WM_OP_INVOKE_DEFAULT, 0, &ptr);
- /* store initial mouse cursor position */
+ /* Vertical Split */
+ uiItemFullO(layout,
+ "SCREEN_OT_area_split",
+ IFACE_("Vertical Split"),
+ ICON_NONE,
+ NULL,
+ WM_OP_INVOKE_DEFAULT,
+ 0,
+ &ptr);
+ /* store initial mouse cursor position. */
+ RNA_int_set_array(&ptr, "cursor", &event->x);
+ RNA_enum_set(&ptr, "direction", 'v');
+
+ /* Horizontal Split */
+ uiItemFullO(layout,
+ "SCREEN_OT_area_split",
+ IFACE_("Horizontal Split"),
+ ICON_NONE,
+ NULL,
+ WM_OP_INVOKE_DEFAULT,
+ 0,
+ &ptr);
+ /* store initial mouse cursor position. */
RNA_int_set_array(&ptr, "cursor", &event->x);
+ RNA_enum_set(&ptr, "direction", 'h');
- uiItemFullO(layout, "SCREEN_OT_area_join", NULL, ICON_NONE, NULL, WM_OP_INVOKE_DEFAULT, 0, &ptr);
- /* mouse cursor on edge, '4' can fail on wide edges... */
- RNA_int_set(&ptr, "min_x", event->x + 4);
- RNA_int_set(&ptr, "min_y", event->y + 4);
- RNA_int_set(&ptr, "max_x", event->x - 4);
- RNA_int_set(&ptr, "max_y", event->y - 4);
+ if (sa1 && sa2) {
+ uiItemS(layout);
+ }
+
+ /* Join needs two very similar areas. */
+ if (sa1 && sa2 && (area_getorientation(sa1, sa2) != -1)) {
+ uiItemFullO(layout,
+ "SCREEN_OT_area_join",
+ IFACE_("Join Areas"),
+ ICON_NONE,
+ NULL,
+ WM_OP_INVOKE_DEFAULT,
+ 0,
+ &ptr);
+ RNA_int_set_array(&ptr, "cursor", &event->x);
+ }
+
+ /* Swap just needs two areas. */
+ if (sa1 && sa2) {
+ uiItemFullO(layout,
+ "SCREEN_OT_area_swap",
+ IFACE_("Swap Areas"),
+ ICON_NONE,
+ NULL,
+ WM_OP_EXEC_DEFAULT,
+ 0,
+ &ptr);
+ RNA_int_set_array(&ptr, "cursor", &event->x);
+ }
UI_popup_menu_end(C, pup);
@@ -3660,7 +3754,7 @@ static void SCREEN_OT_redo_last(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Redo Last";
- ot->description = "Display menu for last action performed";
+ ot->description = "Display parameters for last action performed";
ot->idname = "SCREEN_OT_redo_last";
/* api callbacks */
@@ -3711,7 +3805,7 @@ static int region_quadview_exec(bContext *C, wmOperator *op)
/* some rules... */
if (ar->regiontype != RGN_TYPE_WINDOW) {
- BKE_report(op->reports, RPT_ERROR, "Only window region can be 4-splitted");
+ BKE_report(op->reports, RPT_ERROR, "Only window region can be 4-split");
}
else if (ar->alignment == RGN_ALIGN_QSPLIT) {
/* Exit quad-view */
@@ -3742,7 +3836,7 @@ static int region_quadview_exec(bContext *C, wmOperator *op)
rv3d->viewlock = 0;
rv3d->rflag &= ~RV3D_CLIPPING;
- /* accumulate locks, incase they're mixed */
+ /* Accumulate locks, in case they're mixed. */
for (ar_iter = sa->regionbase.first; ar_iter; ar_iter = ar_iter->next) {
if (ar_iter->regiontype == RGN_TYPE_WINDOW) {
RegionView3D *rv3d_iter = ar_iter->regiondata;
@@ -3764,7 +3858,7 @@ static int region_quadview_exec(bContext *C, wmOperator *op)
WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL);
}
else if (ar->next) {
- BKE_report(op->reports, RPT_ERROR, "Only last region can be 4-splitted");
+ BKE_report(op->reports, RPT_ERROR, "Only last region can be 4-split");
}
else {
/* Enter quad-view */
@@ -3842,6 +3936,65 @@ static void SCREEN_OT_region_quadview(wmOperatorType *ot)
/** \} */
/* -------------------------------------------------------------------- */
+/** \name Region Toggle Operator
+ * \{ */
+
+static int region_toggle_exec(bContext *C, wmOperator *op)
+{
+ PropertyRNA *prop = RNA_struct_find_property(op->ptr, "region_type");
+ ARegion *region;
+
+ if (RNA_property_is_set(op->ptr, prop)) {
+ region = BKE_area_find_region_type(CTX_wm_area(C), RNA_property_enum_get(op->ptr, prop));
+ }
+ else {
+ region = CTX_wm_region(C);
+ }
+
+ if (region) {
+ ED_region_toggle_hidden(C, region);
+ }
+ ED_region_tag_redraw(region);
+
+ return OPERATOR_FINISHED;
+}
+
+static bool region_toggle_poll(bContext *C)
+{
+ ScrArea *area = CTX_wm_area(C);
+
+ /* don't flip anything around in topbar */
+ if (area && area->spacetype == SPACE_TOPBAR) {
+ CTX_wm_operator_poll_msg_set(C, "Toggling regions in the Top-bar is not allowed");
+ return 0;
+ }
+
+ return ED_operator_areaactive(C);
+}
+
+static void SCREEN_OT_region_toggle(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Toggle Region";
+ ot->idname = "SCREEN_OT_region_toggle";
+ ot->description = "Hide or unhide the region";
+
+ /* api callbacks */
+ ot->exec = region_toggle_exec;
+ ot->poll = region_toggle_poll;
+ ot->flag = 0;
+
+ RNA_def_enum(ot->srna,
+ "region_type",
+ rna_enum_region_type_items,
+ 0,
+ "Region Type",
+ "Type of the region to toggle");
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Region Flip Operator
* \{ */
@@ -4714,7 +4867,7 @@ static void SCREEN_OT_userpref_show(struct wmOperatorType *ot)
static int drivers_editor_show_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
- PointerRNA ptr = {{NULL}};
+ PointerRNA ptr = {NULL};
PropertyRNA *prop = NULL;
int index = -1;
uiBut *but = NULL;
@@ -4775,6 +4928,40 @@ static void SCREEN_OT_drivers_editor_show(struct wmOperatorType *ot)
/** \} */
/* -------------------------------------------------------------------- */
+/** \name Show Info Log Operator
+ * \{ */
+
+static int info_log_show_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ int sizex = 900 * UI_DPI_FAC;
+ int sizey = 580 * UI_DPI_FAC;
+ int shift_y = 480;
+
+ /* changes context! */
+ if (WM_window_open_temp(C, event->x, event->y + shift_y, sizex, sizey, WM_WINDOW_INFO) != NULL) {
+ return OPERATOR_FINISHED;
+ }
+ else {
+ BKE_report(op->reports, RPT_ERROR, "Failed to open window!");
+ return OPERATOR_CANCELLED;
+ }
+}
+
+static void SCREEN_OT_info_log_show(struct wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Show Info Log";
+ ot->description = "Show info log in a separate window";
+ ot->idname = "SCREEN_OT_info_log_show";
+
+ /* api callbacks */
+ ot->invoke = info_log_show_invoke;
+ ot->poll = ED_operator_screenactive;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name New Screen Operator
* \{ */
@@ -4903,7 +5090,9 @@ static void region_blend_end(bContext *C, ARegion *ar, const bool is_running)
WM_event_remove_timer(CTX_wm_manager(C), NULL, ar->regiontimer); /* frees rgi */
ar->regiontimer = NULL;
}
-/* assumes that *ar itself is not a splitted version from previous region */
+/**
+ * \note Assumes that \a ar itself is not a split version from previous region.
+ */
void ED_region_visibility_change_update_animated(bContext *C, ScrArea *sa, ARegion *ar)
{
wmWindowManager *wm = CTX_wm_manager(C);
@@ -5231,6 +5420,7 @@ void ED_operatortypes_screen(void)
WM_operatortype_append(SCREEN_OT_area_swap);
WM_operatortype_append(SCREEN_OT_region_quadview);
WM_operatortype_append(SCREEN_OT_region_scale);
+ WM_operatortype_append(SCREEN_OT_region_toggle);
WM_operatortype_append(SCREEN_OT_region_flip);
WM_operatortype_append(SCREEN_OT_header_toggle_menus);
WM_operatortype_append(SCREEN_OT_region_context_menu);
@@ -5241,6 +5431,7 @@ void ED_operatortypes_screen(void)
WM_operatortype_append(SCREEN_OT_screenshot);
WM_operatortype_append(SCREEN_OT_userpref_show);
WM_operatortype_append(SCREEN_OT_drivers_editor_show);
+ WM_operatortype_append(SCREEN_OT_info_log_show);
WM_operatortype_append(SCREEN_OT_region_blend);
WM_operatortype_append(SCREEN_OT_space_type_set_or_cycle);
WM_operatortype_append(SCREEN_OT_space_context_cycle);
diff --git a/source/blender/editors/sculpt_paint/paint_cursor.c b/source/blender/editors/sculpt_paint/paint_cursor.c
index 65e10f98753..4a552fb3744 100644
--- a/source/blender/editors/sculpt_paint/paint_cursor.c
+++ b/source/blender/editors/sculpt_paint/paint_cursor.c
@@ -45,13 +45,17 @@
#include "BKE_node.h"
#include "BKE_paint.h"
#include "BKE_colortools.h"
+#include "BKE_object.h"
#include "WM_api.h"
+#include "wm_cursors.h"
#include "IMB_imbuf_types.h"
#include "ED_view3d.h"
+#include "DEG_depsgraph.h"
+
#include "GPU_draw.h"
#include "GPU_immediate.h"
#include "GPU_immediate_util.h"
@@ -602,7 +606,7 @@ static bool sculpt_get_brush_geometry(bContext *C,
/* Draw an overlay that shows what effect the brush's texture will
* have on brush strength */
-static void paint_draw_tex_overlay(UnifiedPaintSettings *ups,
+static bool paint_draw_tex_overlay(UnifiedPaintSettings *ups,
Brush *brush,
ViewContext *vc,
int x,
@@ -622,7 +626,7 @@ static void paint_draw_tex_overlay(UnifiedPaintSettings *ups,
if (!(mtex->tex) ||
!((mtex->brush_map_mode == MTEX_MAP_MODE_STENCIL) ||
(valid && ELEM(mtex->brush_map_mode, MTEX_MAP_MODE_VIEW, MTEX_MAP_MODE_TILED)))) {
- return;
+ return false;
}
if (load_tex(brush, vc, zoom, col, primary)) {
@@ -728,18 +732,19 @@ static void paint_draw_tex_overlay(UnifiedPaintSettings *ups,
GPU_matrix_pop();
}
}
+ return true;
}
/* Draw an overlay that shows what effect the brush's texture will
* have on brush strength */
-static void paint_draw_cursor_overlay(
+static bool paint_draw_cursor_overlay(
UnifiedPaintSettings *ups, Brush *brush, ViewContext *vc, int x, int y, float zoom)
{
rctf quad;
/* check for overlay mode */
if (!(brush->overlay_flags & BRUSH_OVERLAY_CURSOR)) {
- return;
+ return false;
}
if (load_tex_cursor(brush, vc, zoom)) {
@@ -811,9 +816,10 @@ static void paint_draw_cursor_overlay(
GPU_matrix_pop();
}
}
+ return true;
}
-static void paint_draw_alpha_overlay(UnifiedPaintSettings *ups,
+static bool paint_draw_alpha_overlay(UnifiedPaintSettings *ups,
Brush *brush,
ViewContext *vc,
int x,
@@ -824,6 +830,9 @@ static void paint_draw_alpha_overlay(UnifiedPaintSettings *ups,
/* Color means that primary brush texture is colored and
* secondary is used for alpha/mask control. */
bool col = ELEM(mode, PAINT_MODE_TEXTURE_3D, PAINT_MODE_TEXTURE_2D, PAINT_MODE_VERTEX);
+
+ bool alpha_overlay_active = false;
+
eOverlayControlFlags flags = BKE_paint_get_overlay_flags();
gpuPushAttr(GPU_DEPTH_BUFFER_BIT | GPU_BLEND_BIT);
@@ -836,26 +845,28 @@ static void paint_draw_alpha_overlay(UnifiedPaintSettings *ups,
/* Colored overlay should be drawn separately. */
if (col) {
if (!(flags & PAINT_OVERLAY_OVERRIDE_PRIMARY)) {
- paint_draw_tex_overlay(ups, brush, vc, x, y, zoom, true, true);
+ alpha_overlay_active = paint_draw_tex_overlay(ups, brush, vc, x, y, zoom, true, true);
}
if (!(flags & PAINT_OVERLAY_OVERRIDE_SECONDARY)) {
- paint_draw_tex_overlay(ups, brush, vc, x, y, zoom, false, false);
+ alpha_overlay_active = paint_draw_tex_overlay(ups, brush, vc, x, y, zoom, false, false);
}
if (!(flags & PAINT_OVERLAY_OVERRIDE_CURSOR)) {
- paint_draw_cursor_overlay(ups, brush, vc, x, y, zoom);
+ alpha_overlay_active = paint_draw_cursor_overlay(ups, brush, vc, x, y, zoom);
}
}
else {
if (!(flags & PAINT_OVERLAY_OVERRIDE_PRIMARY) && (mode != PAINT_MODE_WEIGHT)) {
- paint_draw_tex_overlay(ups, brush, vc, x, y, zoom, false, true);
+ alpha_overlay_active = paint_draw_tex_overlay(ups, brush, vc, x, y, zoom, false, true);
}
if (!(flags & PAINT_OVERLAY_OVERRIDE_CURSOR)) {
- paint_draw_cursor_overlay(ups, brush, vc, x, y, zoom);
+ alpha_overlay_active = paint_draw_cursor_overlay(ups, brush, vc, x, y, zoom);
}
}
GPU_matrix_pop();
gpuPopAttr();
+
+ return alpha_overlay_active;
}
BLI_INLINE void draw_tri_point(
@@ -1074,6 +1085,98 @@ static bool ommit_cursor_drawing(Paint *paint, ePaintMode mode, Brush *brush)
return true;
}
+static void cursor_draw_point_screen_space(const uint gpuattr,
+ const ARegion *ar,
+ float true_location[3],
+ float obmat[4][4])
+{
+ float translation_vertex_cursor[3], location[3];
+ copy_v3_v3(location, true_location);
+ mul_m4_v3(obmat, location);
+ ED_view3d_project(ar, location, translation_vertex_cursor);
+ imm_draw_circle_fill_3d(
+ gpuattr, translation_vertex_cursor[0], translation_vertex_cursor[1], 3, 10);
+}
+
+static void cursor_draw_tiling_preview(const uint gpuattr,
+ const ARegion *ar,
+ float true_location[3],
+ Sculpt *sd,
+ Object *ob,
+ float radius)
+{
+ BoundBox *bb = BKE_object_boundbox_get(ob);
+ float orgLoc[3], location[3];
+ int dim, tile_pass = 0;
+ int start[3];
+ int end[3];
+ int cur[3];
+ const float *bbMin = bb->vec[0];
+ const float *bbMax = bb->vec[6];
+ const float *step = sd->paint.tile_offset;
+
+ copy_v3_v3(orgLoc, true_location);
+ for (dim = 0; dim < 3; ++dim) {
+ if ((sd->paint.symmetry_flags & (PAINT_TILE_X << dim)) && step[dim] > 0) {
+ start[dim] = (bbMin[dim] - orgLoc[dim] - radius) / step[dim];
+ end[dim] = (bbMax[dim] - orgLoc[dim] + radius) / step[dim];
+ }
+ else
+ start[dim] = end[dim] = 0;
+ }
+ copy_v3_v3_int(cur, start);
+ for (cur[0] = start[0]; cur[0] <= end[0]; cur[0]++) {
+ for (cur[1] = start[1]; cur[1] <= end[1]; cur[1]++) {
+ for (cur[2] = start[2]; cur[2] <= end[2]; cur[2]++) {
+ if (!cur[0] && !cur[1] && !cur[2])
+ continue; /* skip tile at orgLoc, this was already handled before all others */
+ tile_pass++;
+ for (dim = 0; dim < 3; dim++) {
+ location[dim] = cur[dim] * step[dim] + orgLoc[dim];
+ }
+ cursor_draw_point_screen_space(gpuattr, ar, location, ob->obmat);
+ }
+ }
+ }
+}
+
+static void cursor_draw_point_with_symmetry(const uint gpuattr,
+ const ARegion *ar,
+ const float true_location[3],
+ Sculpt *sd,
+ Object *ob,
+ float radius)
+{
+ const char symm = sd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL;
+ float location[3], symm_rot_mat[4][4];
+
+ for (int i = 0; i <= symm; ++i) {
+ if (i == 0 || (symm & i && (symm != 5 || i != 3) && (symm != 6 || (i != 3 && i != 5)))) {
+
+ /* Axis Symmetry */
+ flip_v3_v3(location, true_location, (char)i);
+ cursor_draw_point_screen_space(gpuattr, ar, location, ob->obmat);
+
+ /* Tiling */
+ cursor_draw_tiling_preview(gpuattr, ar, location, sd, ob, radius);
+
+ /* Radial Symmetry */
+ for (char raxis = 0; raxis < 3; raxis++) {
+ for (int r = 1; r < sd->radial_symm[raxis]; r++) {
+ float angle = 2 * M_PI * r / sd->radial_symm[(int)raxis];
+ flip_v3_v3(location, true_location, (char)i);
+ unit_m4(symm_rot_mat);
+ rotate_m4(symm_rot_mat, raxis + 'X', angle);
+ mul_m4_v3(symm_rot_mat, location);
+
+ cursor_draw_tiling_preview(gpuattr, ar, location, sd, ob, radius);
+ cursor_draw_point_screen_space(gpuattr, ar, location, ob->obmat);
+ }
+ }
+ }
+ }
+}
+
static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused))
{
Scene *scene = CTX_data_scene(C);
@@ -1121,7 +1224,7 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused))
}
/* draw overlay */
- paint_draw_alpha_overlay(ups, brush, &vc, x, y, zoomx, mode);
+ bool alpha_overlay_active = paint_draw_alpha_overlay(ups, brush, &vc, x, y, zoomx, mode);
/* TODO: as sculpt and other paint modes are unified, this
* special mode of drawing will go away */
@@ -1158,12 +1261,12 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused))
}
/* make lines pretty */
- GPU_line_width(1.0f);
+ GPU_line_width(2.0f);
GPU_blend(true); /* TODO: also set blend mode? */
GPU_line_smooth(true);
- uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
/* set brush color */
immUniformColor3fvAlpha(outline_col, outline_alpha);
@@ -1176,7 +1279,103 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused))
/* outer at half alpha */
immUniformColor3fvAlpha(outline_col, outline_alpha * 0.5f);
}
- imm_draw_circle_wire_2d(pos, translation[0], translation[1], final_radius, 40);
+
+ /* Only sculpt mode cursor for now */
+
+ /* Disable for PBVH_GRIDS */
+ SculptSession *ss = vc.obact->sculpt;
+ bool is_multires = ss && ss->pbvh && BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS;
+
+ if ((mode == PAINT_MODE_SCULPT) && ss && !is_multires &&
+ !(brush->falloff_shape & BRUSH_AIRBRUSH)) {
+ Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
+ wmWindow *win = CTX_wm_window(C);
+
+ /* Update WM mouse cursor, disable when the 3D brush cursor is enabled */
+ if (sd->paint.brush->overlay_flags & BRUSH_OVERLAY_CURSOR) {
+ WM_cursor_set(win, CURSOR_STD);
+ }
+ else {
+ WM_cursor_set(win, CURSOR_EDIT);
+ }
+
+ if (!ups->stroke_active) {
+ SculptCursorGeometryInfo gi;
+ float mouse[2] = {x - ar->winrct.xmin, y - ar->winrct.ymin};
+ if (sculpt_cursor_geometry_info_update(C, &gi, mouse, true) && !alpha_overlay_active) {
+
+ float rds;
+ if (!BKE_brush_use_locked_size(scene, brush)) {
+ rds = paint_calc_object_space_radius(&vc, gi.location, BKE_brush_size_get(scene, brush));
+ }
+ else {
+ rds = BKE_brush_unprojected_radius_get(scene, brush);
+ }
+
+ wmViewport(&ar->winrct);
+
+ /* Draw 3D active vertex preview with symmetry*/
+ if (len_v3v3(gi.active_vertex_co, gi.location) < rds) {
+ cursor_draw_point_with_symmetry(pos, ar, gi.active_vertex_co, sd, vc.obact, rds);
+ }
+
+ /* Draw 3D brush cursor */
+ GPU_matrix_push_projection();
+ ED_view3d_draw_setup_view(CTX_wm_window(C),
+ CTX_data_depsgraph_pointer(C),
+ CTX_data_scene(C),
+ ar,
+ CTX_wm_view3d(C),
+ NULL,
+ NULL,
+ NULL);
+
+ float cursor_trans[4][4], cursor_rot[4][4];
+ float z_axis[4] = {0.0f, 0.0f, 1.0f, 0.0f};
+ float quat[4];
+
+ copy_m4_m4(cursor_trans, vc.obact->obmat);
+ translate_m4(cursor_trans, gi.location[0], gi.location[1], gi.location[2]);
+ rotation_between_vecs_to_quat(quat, z_axis, gi.normal);
+ quat_to_mat4(cursor_rot, quat);
+
+ GPU_matrix_push();
+ GPU_matrix_mul(cursor_trans);
+ GPU_matrix_mul(cursor_rot);
+ imm_draw_circle_wire_3d(pos, 0, 0, rds, 40);
+ GPU_matrix_pop();
+
+ GPU_matrix_pop_projection();
+
+ wmWindowViewport(win);
+ }
+ else {
+ /* Draw default cursor when the mouse is not over the mesh or there are no supported
+ * overlays active */
+ GPU_line_width(1.0f);
+ imm_draw_circle_wire_3d(pos, translation[0], translation[1], final_radius, 40);
+ }
+ }
+ else {
+ if (vc.obact->sculpt->cache && !vc.obact->sculpt->cache->first_time) {
+ /* Draw cursor location preview when the stroke is active using the data from StrokeCache
+ */
+ float cursor_location[3];
+ wmViewport(&ar->winrct);
+ copy_v3_v3(cursor_location, ss->cache->true_location);
+ if (ss->cache->brush->sculpt_tool == SCULPT_TOOL_GRAB) {
+ add_v3_v3(cursor_location, ss->cache->grab_delta);
+ }
+ cursor_draw_point_with_symmetry(pos, ar, cursor_location, sd, vc.obact, ss->cache->radius);
+ wmWindowViewport(win);
+ }
+ }
+ }
+ else {
+ /* Draw default cursor in unsupported modes */
+ GPU_line_width(1.0f);
+ imm_draw_circle_wire_3d(pos, translation[0], translation[1], final_radius, 40);
+ }
immUnbindProgram();
diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c
index 342d0b6e820..397b2981ace 100644
--- a/source/blender/editors/sculpt_paint/paint_image_proj.c
+++ b/source/blender/editors/sculpt_paint/paint_image_proj.c
@@ -1719,9 +1719,9 @@ static float project_paint_uvpixel_mask(const ProjPaintState *ps,
normalize_v3(no);
}
else {
- /* incase the */
#if 1
- /* normalizing per pixel isn't optimal, we could cache or check ps->*/
+ /* In case the normalizing per pixel isn't optimal,
+ * we could cache or access from evaluated mesh. */
normal_tri_v3(no,
ps->mvert_eval[lt_vtri[0]].co,
ps->mvert_eval[lt_vtri[1]].co,
diff --git a/source/blender/editors/sculpt_paint/paint_intern.h b/source/blender/editors/sculpt_paint/paint_intern.h
index ea4814857ba..5efedf69fe4 100644
--- a/source/blender/editors/sculpt_paint/paint_intern.h
+++ b/source/blender/editors/sculpt_paint/paint_intern.h
@@ -33,7 +33,6 @@ struct ListBase;
struct MTex;
struct Object;
struct Paint;
-struct PaintCurve;
struct PaintStroke;
struct PointerRNA;
struct RegionView3D;
diff --git a/source/blender/editors/sculpt_paint/paint_stroke.c b/source/blender/editors/sculpt_paint/paint_stroke.c
index 694dae49d30..b4b1ae3b0af 100644
--- a/source/blender/editors/sculpt_paint/paint_stroke.c
+++ b/source/blender/editors/sculpt_paint/paint_stroke.c
@@ -57,6 +57,7 @@
#include "IMB_imbuf_types.h"
#include "paint_intern.h"
+#include "sculpt_intern.h"
#include <float.h>
#include <math.h>
@@ -93,6 +94,8 @@ typedef struct PaintStroke {
int cur_sample;
float last_mouse_position[2];
+ float last_scene_space_position[3];
+ bool stroke_over_mesh;
/* space distance covered so far */
float stroke_distance;
@@ -233,6 +236,17 @@ static bool paint_tool_require_location(Brush *brush, ePaintMode mode)
return true;
}
+static bool paint_stroke_use_scene_spacing(Brush *brush, ePaintMode mode)
+{
+ switch (mode) {
+ case PAINT_MODE_SCULPT:
+ return brush->flag & BRUSH_SCENE_SPACING;
+ default:
+ break;
+ }
+ return false;
+}
+
static bool paint_tool_require_inbetween_mouse_events(Brush *brush, ePaintMode mode)
{
switch (mode) {
@@ -523,6 +537,10 @@ static void paint_brush_stroke_add_step(bContext *C,
copy_v2_v2(stroke->last_mouse_position, mouse_in);
stroke->last_pressure = pressure;
+ if (paint_stroke_use_scene_spacing(brush, mode)) {
+ sculpt_stroke_get_location(C, stroke->last_scene_space_position, stroke->last_mouse_position);
+ }
+
if (paint_stroke_use_jitter(mode, brush, stroke->stroke_mode == BRUSH_STROKE_INVERT)) {
float delta[2];
float factor = stroke->zoom_2d;
@@ -600,14 +618,32 @@ static bool paint_smooth_stroke(PaintStroke *stroke,
return true;
}
-static float paint_space_stroke_spacing(const Scene *scene,
+static float paint_space_stroke_spacing(bContext *C,
+ const Scene *scene,
PaintStroke *stroke,
float size_pressure,
float spacing_pressure)
{
- /* brushes can have a minimum size of 1.0 but with pressure it can be smaller then a pixel
- * causing very high step sizes, hanging blender [#32381] */
- const float size_clamp = max_ff(1.0f, BKE_brush_size_get(scene, stroke->brush) * size_pressure);
+ Paint *paint = BKE_paint_get_active_from_context(C);
+ ePaintMode mode = BKE_paintmode_get_active_from_context(C);
+ Brush *brush = BKE_paint_brush(paint);
+ float size_clamp = 0.0f;
+ float size = BKE_brush_size_get(scene, stroke->brush) * size_pressure;
+ if (paint_stroke_use_scene_spacing(brush, mode)) {
+ if (!BKE_brush_use_locked_size(scene, brush)) {
+ size_clamp = paint_calc_object_space_radius(
+ &stroke->vc, stroke->last_scene_space_position, size);
+ }
+ else {
+ size_clamp = BKE_brush_unprojected_radius_get(scene, brush) * size_pressure;
+ }
+ }
+ else {
+ /* brushes can have a minimum size of 1.0 but with pressure it can be smaller then a pixel
+ * causing very high step sizes, hanging blender [#32381] */
+ size_clamp = max_ff(1.0f, size);
+ }
+
float spacing = stroke->brush->spacing;
/* apply spacing pressure */
@@ -619,7 +655,12 @@ static float paint_space_stroke_spacing(const Scene *scene,
* the fact that brush can be scaled there. */
spacing *= stroke->zoom_2d;
- return max_ff(1.0, size_clamp * spacing / 50.0f);
+ if (paint_stroke_use_scene_spacing(brush, mode)) {
+ return size_clamp * spacing / 50.0f;
+ }
+ else {
+ return max_ff(1.0, size_clamp * spacing / 50.0f);
+ }
}
static float paint_stroke_overlapped_curve(Brush *br, float x, float spacing)
@@ -677,14 +718,18 @@ static float paint_stroke_integrate_overlap(Brush *br, float factor)
}
}
-static float paint_space_stroke_spacing_variable(
- const Scene *scene, PaintStroke *stroke, float pressure, float dpressure, float length)
+static float paint_space_stroke_spacing_variable(bContext *C,
+ const Scene *scene,
+ PaintStroke *stroke,
+ float pressure,
+ float dpressure,
+ float length)
{
if (BKE_brush_use_size_pressure(scene, stroke->brush)) {
/* use pressure to modify size. set spacing so that at 100%, the circles
* are aligned nicely with no overlap. for this the spacing needs to be
* the average of the previous and next size. */
- float s = paint_space_stroke_spacing(scene, stroke, 1.0f, pressure);
+ float s = paint_space_stroke_spacing(C, scene, stroke, 1.0f, pressure);
float q = s * dpressure / (2.0f * length);
float pressure_fac = (1.0f + q) / (1.0f - q);
@@ -692,14 +737,15 @@ static float paint_space_stroke_spacing_variable(
float new_size_pressure = stroke->last_pressure * pressure_fac;
/* average spacing */
- float last_spacing = paint_space_stroke_spacing(scene, stroke, last_size_pressure, pressure);
- float new_spacing = paint_space_stroke_spacing(scene, stroke, new_size_pressure, pressure);
+ float last_spacing = paint_space_stroke_spacing(
+ C, scene, stroke, last_size_pressure, pressure);
+ float new_spacing = paint_space_stroke_spacing(C, scene, stroke, new_size_pressure, pressure);
return 0.5f * (last_spacing + new_spacing);
}
else {
/* no size pressure */
- return paint_space_stroke_spacing(scene, stroke, 1.0f, pressure);
+ return paint_space_stroke_spacing(C, scene, stroke, 1.0f, pressure);
}
}
@@ -711,29 +757,57 @@ static int paint_space_stroke(bContext *C,
float final_pressure)
{
const Scene *scene = CTX_data_scene(C);
+ ARegion *ar = CTX_wm_region(C);
PaintStroke *stroke = op->customdata;
UnifiedPaintSettings *ups = stroke->ups;
+ Paint *paint = BKE_paint_get_active_from_context(C);
+ ePaintMode mode = BKE_paintmode_get_active_from_context(C);
+ Brush *brush = BKE_paint_brush(paint);
int cnt = 0;
float pressure, dpressure;
float mouse[2], dmouse[2];
+ float scene_space_position[3], d_scene_space_position[3], final_scene_space_position[3];
float length;
- float no_pressure_spacing = paint_space_stroke_spacing(scene, stroke, 1.0f, 1.0f);
-
- sub_v2_v2v2(dmouse, final_mouse, stroke->last_mouse_position);
-
+ float no_pressure_spacing = paint_space_stroke_spacing(C, scene, stroke, 1.0f, 1.0f);
pressure = stroke->last_pressure;
dpressure = final_pressure - stroke->last_pressure;
-
+ sub_v2_v2v2(dmouse, final_mouse, stroke->last_mouse_position);
length = normalize_v2(dmouse);
+ if (paint_stroke_use_scene_spacing(brush, mode)) {
+ bool hit = sculpt_stroke_get_location(C, scene_space_position, final_mouse);
+ if (hit && stroke->stroke_over_mesh) {
+ sub_v3_v3v3(d_scene_space_position, scene_space_position, stroke->last_scene_space_position);
+ length = len_v3(d_scene_space_position);
+ stroke->stroke_over_mesh = true;
+ }
+ else {
+ length = 0.0f;
+ stroke->stroke_over_mesh = hit;
+ if (stroke->stroke_over_mesh) {
+ copy_v3_v3(stroke->last_scene_space_position, scene_space_position);
+ }
+ }
+ }
+
while (length > 0.0f) {
float spacing = paint_space_stroke_spacing_variable(
- scene, stroke, pressure, dpressure, length);
+ C, scene, stroke, pressure, dpressure, length);
if (length >= spacing) {
- mouse[0] = stroke->last_mouse_position[0] + dmouse[0] * spacing;
- mouse[1] = stroke->last_mouse_position[1] + dmouse[1] * spacing;
+ if (paint_stroke_use_scene_spacing(brush, mode)) {
+ normalize_v3(d_scene_space_position);
+ mul_v3_v3fl(final_scene_space_position, d_scene_space_position, spacing);
+ add_v3_v3v3(final_scene_space_position,
+ stroke->last_scene_space_position,
+ final_scene_space_position);
+ ED_view3d_project(ar, final_scene_space_position, mouse);
+ }
+ else {
+ mouse[0] = stroke->last_mouse_position[0] + dmouse[0] * spacing;
+ mouse[1] = stroke->last_mouse_position[1] + dmouse[1] * spacing;
+ }
pressure = stroke->last_pressure + (spacing / length) * dpressure;
ups->overlap_factor = paint_stroke_integrate_overlap(stroke->brush,
@@ -1079,7 +1153,7 @@ static bool paint_stroke_curve_end(bContext *C, wmOperator *op, PaintStroke *str
if (br->flag & BRUSH_CURVE) {
UnifiedPaintSettings *ups = &CTX_data_tool_settings(C)->unified_paint_settings;
const Scene *scene = CTX_data_scene(C);
- const float spacing = paint_space_stroke_spacing(scene, stroke, 1.0f, 1.0f);
+ const float spacing = paint_space_stroke_spacing(C, scene, stroke, 1.0f, 1.0f);
PaintCurve *pc = br->paint_curve;
PaintCurvePoint *pcp;
float length_residue = 0.0f;
@@ -1250,6 +1324,10 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event)
if (!stroke->stroke_started) {
stroke->last_pressure = sample_average.pressure;
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_scene_space_position, sample_average.mouse);
+ }
stroke->stroke_started = stroke->test_start(C, op, sample_average.mouse);
BLI_assert((stroke->stroke_started & ~1) == 0); /* 0/1 */
diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c
index 440c4d42cae..5aa913ad006 100644
--- a/source/blender/editors/sculpt_paint/sculpt.c
+++ b/source/blender/editors/sculpt_paint/sculpt.c
@@ -96,7 +96,18 @@
/* Do not use these functions while working with PBVH_GRIDS data in SculptSession */
-/* TODO: why is this kept, should it be removed? */
+static float *sculpt_vertex_co_get(SculptSession *ss, int index)
+{
+ switch (BKE_pbvh_type(ss->pbvh)) {
+ case PBVH_FACES:
+ return ss->mvert[index].co;
+ case PBVH_BMESH:
+ return BM_vert_at_index(BKE_pbvh_get_bmesh(ss->pbvh), index)->co;
+ default:
+ return NULL;
+ }
+}
+
#if 0 /* UNUSED */
static int sculpt_active_vertex_get(SculptSession *ss)
@@ -136,18 +147,6 @@ static void sculpt_vertex_normal_get(SculptSession *ss, int index, float no[3])
}
}
-static float *sculpt_vertex_co_get(SculptSession *ss, int index)
-{
- switch (BKE_pbvh_type(ss->pbvh)) {
- case PBVH_FACES:
- return ss->mvert[index].co;
- case PBVH_BMESH:
- return BM_vert_at_index(BKE_pbvh_get_bmesh(ss->pbvh), index)->co;
- default:
- return NULL;
- }
-}
-
static void sculpt_vertex_co_set(SculptSession *ss, int index, float co[3])
{
switch (BKE_pbvh_type(ss->pbvh)) {
@@ -748,17 +747,26 @@ void ED_sculpt_redraw_planes_get(float planes[4][4], ARegion *ar, Object *ob)
void sculpt_brush_test_init(SculptSession *ss, SculptBrushTest *test)
{
- RegionView3D *rv3d = ss->cache->vc->rv3d;
+ RegionView3D *rv3d = ss->cache ? ss->cache->vc->rv3d : ss->rv3d;
+
+ test->radius_squared = ss->cache ? ss->cache->radius_squared :
+ ss->cursor_radius * ss->cursor_radius;
+ if (ss->cache) {
+ copy_v3_v3(test->location, ss->cache->location);
+ test->mirror_symmetry_pass = ss->cache->mirror_symmetry_pass;
+ }
+ else {
+ copy_v3_v3(test->location, ss->cursor_location);
+ test->mirror_symmetry_pass = 0;
+ }
- test->radius_squared = ss->cache->radius_squared;
- copy_v3_v3(test->location, ss->cache->location);
test->dist = 0.0f; /* just for initialize */
/* Only for 2D projection. */
zero_v4(test->plane_view);
zero_v4(test->plane_tool);
- test->mirror_symmetry_pass = ss->cache->mirror_symmetry_pass;
+ test->mirror_symmetry_pass = ss->cache ? ss->cache->mirror_symmetry_pass : 0;
if (rv3d->rflag & RV3D_CLIPPING) {
test->clip_rv3d = rv3d;
@@ -1050,7 +1058,7 @@ static void calc_area_normal_and_center_task_cb(void *__restrict userdata,
int private_count[2] = {0};
bool use_original = false;
- if (ss->cache->original) {
+ if (ss->cache && ss->cache->original) {
unode = sculpt_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_COORDS);
use_original = (unode->co || unode->bm_entry);
}
@@ -1059,6 +1067,13 @@ static void calc_area_normal_and_center_task_cb(void *__restrict userdata,
SculptBrushTestFn sculpt_brush_test_sq_fn = sculpt_brush_test_init_with_falloff_shape(
ss, &test, data->brush->falloff_shape);
+ /* Update the test radius to sample the normal using the normal radius of the brush */
+ if (data->brush->ob_mode == OB_MODE_SCULPT) {
+ float test_radius = sqrtf(test.radius_squared);
+ test_radius *= data->brush->normal_radius_factor;
+ test.radius_squared = test_radius * test_radius;
+ }
+
/* when the mesh is edited we can't rely on original coords
* (original mesh may not even have verts in brush radius) */
if (use_original && data->has_bm_orco) {
@@ -1120,6 +1135,8 @@ static void calc_area_normal_and_center_task_cb(void *__restrict userdata,
const float *no;
int flip_index;
+ data->any_vertex_sampled = true;
+
if (use_original) {
normal_short_to_float_v3(no_buf, no_s);
no = no_buf;
@@ -1134,7 +1151,8 @@ static void calc_area_normal_and_center_task_cb(void *__restrict userdata,
}
}
- flip_index = (dot_v3v3(ss->cache->view_normal, no) <= 0.0f);
+ flip_index = (dot_v3v3(ss->cache ? ss->cache->view_normal : ss->cursor_view_normal, no) <=
+ 0.0f);
if (area_cos) {
add_v3_v3(private_co[flip_index], co);
}
@@ -1223,7 +1241,7 @@ static void calc_area_normal(
}
/* expose 'calc_area_normal' externally. */
-void sculpt_pbvh_calc_area_normal(const Brush *brush,
+bool sculpt_pbvh_calc_area_normal(const Brush *brush,
Object *ob,
PBVHNode **nodes,
int totnode,
@@ -1249,6 +1267,7 @@ void sculpt_pbvh_calc_area_normal(const Brush *brush,
.area_cos = NULL,
.area_nos = area_nos,
.count = count,
+ .any_vertex_sampled = false,
};
BLI_mutex_init(&data.mutex);
@@ -1265,6 +1284,8 @@ void sculpt_pbvh_calc_area_normal(const Brush *brush,
break;
}
}
+
+ return data.any_vertex_sampled;
}
/* this calculates flatten center and area normal together,
@@ -1508,7 +1529,8 @@ float tex_strength(SculptSession *ss,
bool sculpt_search_sphere_cb(PBVHNode *node, void *data_v)
{
SculptSearchSphereData *data = data_v;
- float *center = data->ss->cache->location, nearest[3];
+ float *center, nearest[3];
+ center = data->ss->cache ? data->ss->cache->location : data->ss->cursor_location;
float t[3], bb_min[3], bb_max[3];
int i;
@@ -1585,12 +1607,13 @@ static PBVHNode **sculpt_pbvh_gather_generic(Object *ob,
SculptSession *ss = ob->sculpt;
PBVHNode **nodes = NULL;
- /* Build a list of all nodes that are potentially within the brush's area of influence */
+ /* Build a list of all nodes that are potentially within the cursor or brush's area of influence
+ */
if (brush->falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) {
SculptSearchSphereData data = {
.ss = ss,
.sd = sd,
- .radius_squared = SQUARE(ss->cache->radius * radius_scale),
+ .radius_squared = ss->cache ? SQUARE(ss->cache->radius * radius_scale) : ss->cursor_radius,
.original = use_original,
};
BKE_pbvh_search_gather(ss->pbvh, sculpt_search_sphere_cb, &data, &nodes, r_totnode);
@@ -1602,7 +1625,7 @@ static PBVHNode **sculpt_pbvh_gather_generic(Object *ob,
SculptSearchCircleData data = {
.ss = ss,
.sd = sd,
- .radius_squared = SQUARE(ss->cache->radius * radius_scale),
+ .radius_squared = ss->cache ? SQUARE(ss->cache->radius * radius_scale) : ss->cursor_radius,
.original = use_original,
.dist_ray_to_aabb_precalc = &dist_ray_to_aabb_precalc,
};
@@ -1964,10 +1987,14 @@ typedef struct SculptDoBrushSmoothGridDataChunk {
typedef struct {
SculptSession *ss;
const float *ray_start;
+ const float *ray_normal;
bool hit;
float depth;
bool original;
+ int active_vertex_index;
+ float *face_normal;
+
struct IsectRayPrecalc isect_precalc;
} SculptRaycastData;
@@ -3987,7 +4014,7 @@ static void do_gravity(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, fl
BLI_task_parallel_range(0, totnode, &data, do_gravity_task_cb_ex, &settings);
}
-void sculpt_vertcos_to_key(Object *ob, KeyBlock *kb, float (*vertCos)[3])
+void sculpt_vertcos_to_key(Object *ob, KeyBlock *kb, const float (*vertCos)[3])
{
Mesh *me = (Mesh *)ob->data;
float(*ofs)[3] = NULL;
@@ -4349,7 +4376,7 @@ static void sculpt_update_keyblock(Object *ob)
vertCos = ss->orig_cos;
}
else {
- vertCos = BKE_pbvh_get_vertCos(ss->pbvh);
+ vertCos = BKE_pbvh_vert_coords_alloc(ss->pbvh);
}
if (vertCos) {
@@ -4513,7 +4540,7 @@ static void do_tiled(
float orgLoc[3]; /* position of the "prototype" stroke for tiling */
copy_v3_v3(orgLoc, cache->location);
- for (dim = 0; dim < 3; ++dim) {
+ for (dim = 0; dim < 3; dim++) {
if ((sd->paint.symmetry_flags & (PAINT_TILE_X << dim)) && step[dim] > 0) {
start[dim] = (bbMin[dim] - orgLoc[dim] - radius) / step[dim];
end[dim] = (bbMax[dim] - orgLoc[dim] + radius) / step[dim];
@@ -4529,16 +4556,16 @@ static void do_tiled(
/* now do it for all the tiles */
copy_v3_v3_int(cur, start);
- for (cur[0] = start[0]; cur[0] <= end[0]; ++cur[0]) {
- for (cur[1] = start[1]; cur[1] <= end[1]; ++cur[1]) {
- for (cur[2] = start[2]; cur[2] <= end[2]; ++cur[2]) {
+ for (cur[0] = start[0]; cur[0] <= end[0]; cur[0]++) {
+ for (cur[1] = start[1]; cur[1] <= end[1]; cur[1]++) {
+ for (cur[2] = start[2]; cur[2] <= end[2]; cur[2]++) {
if (!cur[0] && !cur[1] && !cur[2]) {
continue; /* skip tile at orgLoc, this was already handled before all others */
}
++cache->tile_pass;
- for (dim = 0; dim < 3; ++dim) {
+ for (dim = 0; dim < 3; dim++) {
cache->location[dim] = cur[dim] * step[dim] + orgLoc[dim];
cache->plane_offset[dim] = cur[dim] * step[dim];
}
@@ -5154,10 +5181,11 @@ static bool sculpt_any_smooth_mode(const Brush *brush, StrokeCache *cache, int s
static void sculpt_stroke_modifiers_check(const bContext *C, Object *ob, const Brush *brush)
{
SculptSession *ss = ob->sculpt;
+ View3D *v3d = CTX_wm_view3d(C);
- if (ss->kb || ss->modifiers_active) {
+ bool need_pmap = sculpt_any_smooth_mode(brush, ss->cache, 0);
+ if (ss->kb || ss->modifiers_active || (!BKE_sculptsession_use_pbvh_draw(ob, v3d) && need_pmap)) {
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
- bool need_pmap = sculpt_any_smooth_mode(brush, ss->cache, 0);
BKE_sculpt_update_object_for_edit(depsgraph, ob, need_pmap, false);
}
}
@@ -5186,8 +5214,11 @@ static void sculpt_raycast_cb(PBVHNode *node, void *data_v, float *tmin)
origco,
use_origco,
srd->ray_start,
+ srd->ray_normal,
&srd->isect_precalc,
- &srd->depth)) {
+ &srd->depth,
+ &srd->active_vertex_index,
+ srd->face_normal)) {
srd->hit = 1;
*tmin = srd->depth;
}
@@ -5275,6 +5306,120 @@ static float sculpt_raycast_init(ViewContext *vc,
return dist;
}
+/* 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 */
+bool sculpt_cursor_geometry_info_update(bContext *C,
+ SculptCursorGeometryInfo *out,
+ const float mouse[2],
+ bool use_sampled_normal)
+{
+ Scene *scene = CTX_data_scene(C);
+ Sculpt *sd = scene->toolsettings->sculpt;
+ Object *ob;
+ SculptSession *ss;
+ ViewContext vc;
+ const Brush *brush = BKE_paint_brush(BKE_paint_get_active_from_context(C));
+ float ray_start[3], ray_end[3], ray_normal[3], depth, face_normal[3], sampled_normal[3],
+ mat[3][3];
+ float viewDir[3] = {0.0f, 0.0f, 1.0f};
+ int totnode;
+ bool original = false, hit = false;
+
+ ED_view3d_viewcontext_init(C, &vc);
+
+ ob = vc.obact;
+ ss = ob->sculpt;
+
+ if (!ss->pbvh) {
+ copy_v3_fl(out->location, 0.0f);
+ copy_v3_fl(out->normal, 0.0f);
+ copy_v3_fl(out->active_vertex_co, 0.0f);
+ return false;
+ }
+
+ /* PBVH raycast to get active vertex and face normal */
+ depth = sculpt_raycast_init(&vc, mouse, ray_start, ray_end, ray_normal, original);
+ sculpt_stroke_modifiers_check(C, ob, brush);
+
+ SculptRaycastData srd = {
+ .original = original,
+ .ss = ob->sculpt,
+ .hit = 0,
+ .ray_start = ray_start,
+ .ray_normal = ray_normal,
+ .depth = depth,
+ .face_normal = face_normal,
+ };
+ isect_ray_tri_watertight_v3_precalc(&srd.isect_precalc, ray_normal);
+ BKE_pbvh_raycast(ss->pbvh, sculpt_raycast_cb, &srd, ray_start, ray_normal, srd.original);
+
+ /* Cursor is not over the mesh, return default values */
+ if (!srd.hit) {
+ copy_v3_fl(out->location, 0.0f);
+ copy_v3_fl(out->normal, 0.0f);
+ copy_v3_fl(out->active_vertex_co, 0.0f);
+ return false;
+ }
+
+ /* Update the active vertex of the SculptSession */
+ ss->active_vertex_index = srd.active_vertex_index;
+
+ copy_v3_v3(out->active_vertex_co, sculpt_vertex_co_get(ss, srd.active_vertex_index));
+ copy_v3_v3(out->location, ray_normal);
+ mul_v3_fl(out->location, srd.depth);
+ add_v3_v3(out->location, ray_start);
+
+ /* Option to return the face normal directly for performance o accuracy reasons */
+ if (!use_sampled_normal) {
+ copy_v3_v3(out->normal, srd.face_normal);
+ return hit;
+ }
+
+ /* Sampled normal calculation */
+ const float radius_scale = 1.0f;
+ float radius;
+
+ /* Update cursor data in SculptSession */
+ invert_m4_m4(ob->imat, ob->obmat);
+ copy_m3_m4(mat, vc.rv3d->viewinv);
+ mul_m3_v3(mat, viewDir);
+ copy_m3_m4(mat, ob->imat);
+ mul_m3_v3(mat, viewDir);
+ normalize_v3_v3(ss->cursor_view_normal, viewDir);
+ copy_v3_v3(ss->cursor_normal, srd.face_normal);
+ copy_v3_v3(ss->cursor_location, out->location);
+ ss->rv3d = vc.rv3d;
+
+ if (!BKE_brush_use_locked_size(scene, brush)) {
+ radius = paint_calc_object_space_radius(&vc, out->location, BKE_brush_size_get(scene, brush));
+ }
+ else {
+ radius = BKE_brush_unprojected_radius_get(scene, brush);
+ }
+ ss->cursor_radius = radius;
+
+ PBVHNode **nodes = sculpt_pbvh_gather_generic(ob, sd, brush, original, radius_scale, &totnode);
+
+ /* In case there are no nodes under the cursor, return the face normal */
+ if (!totnode) {
+ MEM_freeN(nodes);
+ copy_v3_v3(out->normal, srd.face_normal);
+ return true;
+ }
+
+ /* Calculate the sampled normal */
+ if (sculpt_pbvh_calc_area_normal(brush, ob, nodes, totnode, true, sampled_normal)) {
+ copy_v3_v3(out->normal, sampled_normal);
+ }
+ else {
+ /* Use face normal when there are no vertices to sample inside the cursor radius */
+ copy_v3_v3(out->normal, srd.face_normal);
+ }
+ MEM_freeN(nodes);
+ return true;
+}
+
/* Do a raycast in the tree to find the 3d brush location
* (This allows us to ignore the GL depth buffer)
* Returns 0 if the ray doesn't hit the mesh, non-zero otherwise
@@ -5284,7 +5429,7 @@ bool sculpt_stroke_get_location(bContext *C, float out[3], const float mouse[2])
Object *ob;
SculptSession *ss;
StrokeCache *cache;
- float ray_start[3], ray_end[3], ray_normal[3], depth;
+ float ray_start[3], ray_end[3], ray_normal[3], depth, face_normal[3];
bool original;
ViewContext vc;
@@ -5302,14 +5447,21 @@ bool sculpt_stroke_get_location(bContext *C, float out[3], const float mouse[2])
depth = sculpt_raycast_init(&vc, mouse, ray_start, ray_end, ray_normal, original);
+ if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) {
+ BM_mesh_elem_table_ensure(ss->bm, BM_VERT);
+ BM_mesh_elem_index_ensure(ss->bm, BM_VERT);
+ }
+
bool hit = false;
{
SculptRaycastData srd;
srd.ss = ob->sculpt;
srd.ray_start = ray_start;
+ srd.ray_normal = ray_normal;
srd.hit = 0;
srd.depth = depth;
srd.original = original;
+ srd.face_normal = face_normal;
isect_ray_tri_watertight_v3_precalc(&srd.isect_precalc, ray_normal);
BKE_pbvh_raycast(ss->pbvh, sculpt_raycast_cb, &srd, ray_start, ray_normal, srd.original);
@@ -5577,7 +5729,6 @@ static void sculpt_stroke_update_step(bContext *C,
}
do_symmetrical_brush_actions(sd, ob, do_brush_action, ups);
-
sculpt_combine_proxies(sd, ob);
/* hack to fix noise texture tearing mesh */
diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h
index e646accf108..9f1cb7a53a4 100644
--- a/source/blender/editors/sculpt_paint/sculpt_intern.h
+++ b/source/blender/editors/sculpt_paint/sculpt_intern.h
@@ -34,9 +34,7 @@
#include "BKE_pbvh.h"
struct KeyBlock;
-struct Main;
struct Object;
-struct SculptOrigVertData;
struct SculptUndoNode;
struct bContext;
@@ -47,7 +45,18 @@ bool sculpt_poll(struct bContext *C);
bool sculpt_poll_view3d(struct bContext *C);
/* Stroke */
+
+typedef struct SculptCursorGeometryInfo {
+ float location[3];
+ float normal[3];
+ float active_vertex_co[3];
+} SculptCursorGeometryInfo;
+
bool sculpt_stroke_get_location(struct bContext *C, float out[3], const float mouse[2]);
+bool sculpt_cursor_geometry_info_update(bContext *C,
+ SculptCursorGeometryInfo *out,
+ const float mouse[2],
+ bool use_sampled_normal);
/* Dynamic topology */
void sculpt_pbvh_clear(Object *ob);
@@ -164,6 +173,7 @@ typedef struct SculptThreadedTaskData {
float (*area_cos)[3];
float (*area_nos)[3];
int *count;
+ bool any_vertex_sampled;
ThreadMutex mutex;
@@ -228,7 +238,7 @@ float tex_strength(struct SculptSession *ss,
const int thread_id);
/* just for vertex paint. */
-void sculpt_pbvh_calc_area_normal(const struct Brush *brush,
+bool sculpt_pbvh_calc_area_normal(const struct Brush *brush,
Object *ob,
PBVHNode **nodes,
int totnode,
@@ -342,7 +352,7 @@ SculptUndoNode *sculpt_undo_get_node(PBVHNode *node);
void sculpt_undo_push_begin(const char *name);
void sculpt_undo_push_end(void);
-void sculpt_vertcos_to_key(Object *ob, KeyBlock *kb, float (*vertCos)[3]);
+void sculpt_vertcos_to_key(Object *ob, KeyBlock *kb, const float (*vertCos)[3]);
void sculpt_update_object_bounding_box(struct Object *ob);
diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.c b/source/blender/editors/sculpt_paint/sculpt_undo.c
index 3a3487227a3..cb8afd5d7fa 100644
--- a/source/blender/editors/sculpt_paint/sculpt_undo.c
+++ b/source/blender/editors/sculpt_paint/sculpt_undo.c
@@ -189,7 +189,7 @@ static bool sculpt_undo_restore_coords(bContext *C, Depsgraph *depsgraph, Sculpt
/* pbvh uses it's own mvert array, so coords should be */
/* propagated to pbvh here */
- BKE_pbvh_apply_vertCos(ss->pbvh, vertCos, ss->kb->totelem);
+ BKE_pbvh_vert_coords_apply(ss->pbvh, vertCos, ss->kb->totelem);
MEM_freeN(vertCos);
}
diff --git a/source/blender/editors/sound/sound_ops.c b/source/blender/editors/sound/sound_ops.c
index 51231ccf634..4e710d31cbb 100644
--- a/source/blender/editors/sound/sound_ops.c
+++ b/source/blender/editors/sound/sound_ops.c
@@ -710,7 +710,7 @@ static void SOUND_OT_mixdown(wmOperatorType *ot)
FILE_TYPE_FOLDER | FILE_TYPE_SOUND,
FILE_SPECIAL,
FILE_SAVE,
- WM_FILESEL_FILEPATH | WM_FILESEL_RELPATH,
+ WM_FILESEL_FILEPATH | WM_FILESEL_RELPATH | WM_FILESEL_SHOW_PROPS,
FILE_DEFAULTDISPLAY,
FILE_SORT_ALPHA);
#ifdef WITH_AUDASPACE
diff --git a/source/blender/editors/space_action/action_data.c b/source/blender/editors/space_action/action_data.c
index bc7f8a0f79d..5ceaefd6309 100644
--- a/source/blender/editors/space_action/action_data.c
+++ b/source/blender/editors/space_action/action_data.c
@@ -220,7 +220,7 @@ static int action_new_exec(bContext *C, wmOperator *UNUSED(op))
PointerRNA oldptr;
oldptr = RNA_property_pointer_get(&ptr, prop);
- oldact = (bAction *)oldptr.id.data;
+ oldact = (bAction *)oldptr.owner_id;
/* stash the old action to prevent it from being lost */
if (ptr.type == &RNA_AnimData) {
diff --git a/source/blender/editors/space_action/action_draw.c b/source/blender/editors/space_action/action_draw.c
index a7b6f399187..0c57113956d 100644
--- a/source/blender/editors/space_action/action_draw.c
+++ b/source/blender/editors/space_action/action_draw.c
@@ -352,179 +352,231 @@ void draw_channel_strips(bAnimContext *ac, SpaceAction *saction, ARegion *ar)
/* ************************************************************************* */
/* Timeline - Caches */
-void timeline_draw_cache(SpaceAction *saction, Object *ob, Scene *scene)
+static bool timeline_cache_is_hidden_by_setting(SpaceAction *saction, PTCacheID *pid)
{
- PTCacheID *pid;
- ListBase pidlist;
- const float cache_draw_height = (4.0f * UI_DPI_FAC * U.pixelsize);
- float yoffs = 0.f;
-
- if (!(saction->cache_display & TIME_CACHE_DISPLAY) || (!ob)) {
- return;
+ switch (pid->type) {
+ case PTCACHE_TYPE_SOFTBODY:
+ if ((saction->cache_display & TIME_CACHE_SOFTBODY) == 0) {
+ return true;
+ }
+ break;
+ case PTCACHE_TYPE_PARTICLES:
+ if ((saction->cache_display & TIME_CACHE_PARTICLES) == 0) {
+ return true;
+ }
+ break;
+ case PTCACHE_TYPE_CLOTH:
+ if ((saction->cache_display & TIME_CACHE_CLOTH) == 0) {
+ return true;
+ }
+ break;
+ case PTCACHE_TYPE_SMOKE_DOMAIN:
+ case PTCACHE_TYPE_SMOKE_HIGHRES:
+ if ((saction->cache_display & TIME_CACHE_SMOKE) == 0) {
+ return true;
+ }
+ break;
+ case PTCACHE_TYPE_DYNAMICPAINT:
+ if ((saction->cache_display & TIME_CACHE_DYNAMICPAINT) == 0) {
+ return true;
+ }
+ break;
+ case PTCACHE_TYPE_RIGIDBODY:
+ if ((saction->cache_display & TIME_CACHE_RIGIDBODY) == 0) {
+ return true;
+ }
+ break;
}
+ return false;
+}
- BKE_ptcache_ids_from_object(&pidlist, ob, scene, 0);
+static void timeline_cache_color_get(PTCacheID *pid, float color[4])
+{
+ switch (pid->type) {
+ case PTCACHE_TYPE_SOFTBODY:
+ color[0] = 1.0;
+ color[1] = 0.4;
+ color[2] = 0.02;
+ color[3] = 0.1;
+ break;
+ case PTCACHE_TYPE_PARTICLES:
+ color[0] = 1.0;
+ color[1] = 0.1;
+ color[2] = 0.02;
+ color[3] = 0.1;
+ break;
+ case PTCACHE_TYPE_CLOTH:
+ color[0] = 0.1;
+ color[1] = 0.1;
+ color[2] = 0.75;
+ color[3] = 0.1;
+ break;
+ case PTCACHE_TYPE_SMOKE_DOMAIN:
+ case PTCACHE_TYPE_SMOKE_HIGHRES:
+ color[0] = 0.2;
+ color[1] = 0.2;
+ color[2] = 0.2;
+ color[3] = 0.1;
+ break;
+ case PTCACHE_TYPE_DYNAMICPAINT:
+ color[0] = 1.0;
+ color[1] = 0.1;
+ color[2] = 0.75;
+ color[3] = 0.1;
+ break;
+ case PTCACHE_TYPE_RIGIDBODY:
+ color[0] = 1.0;
+ color[1] = 0.6;
+ color[2] = 0.0;
+ color[3] = 0.1;
+ break;
+ default:
+ color[0] = 1.0;
+ color[1] = 0.0;
+ color[2] = 1.0;
+ color[3] = 0.1;
+ BLI_assert(0);
+ break;
+ }
+}
- uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+static void timeline_cache_modify_color_based_on_state(PointCache *cache, float color[4])
+{
+ if (cache->flag & PTCACHE_BAKED) {
+ color[0] -= 0.4f;
+ color[1] -= 0.4f;
+ color[2] -= 0.4f;
+ }
+ else if (cache->flag & PTCACHE_OUTDATED) {
+ color[0] += 0.4f;
+ color[1] += 0.4f;
+ color[2] += 0.4f;
+ }
+}
- /* iterate over pointcaches on the active object, and draw each one's range */
- for (pid = pidlist.first; pid; pid = pid->next) {
- float col[4];
+static bool timeline_cache_find_next_cached_segment(PointCache *cache,
+ int search_start_frame,
+ int *r_segment_start,
+ int *r_segment_end)
+{
+ int offset = cache->startframe;
+ int current = search_start_frame;
- switch (pid->type) {
- case PTCACHE_TYPE_SOFTBODY:
- if (!(saction->cache_display & TIME_CACHE_SOFTBODY)) {
- continue;
- }
- break;
- case PTCACHE_TYPE_PARTICLES:
- if (!(saction->cache_display & TIME_CACHE_PARTICLES)) {
- continue;
- }
- break;
- case PTCACHE_TYPE_CLOTH:
- if (!(saction->cache_display & TIME_CACHE_CLOTH)) {
- continue;
- }
- break;
- case PTCACHE_TYPE_SMOKE_DOMAIN:
- case PTCACHE_TYPE_SMOKE_HIGHRES:
- if (!(saction->cache_display & TIME_CACHE_SMOKE)) {
- continue;
- }
- break;
- case PTCACHE_TYPE_DYNAMICPAINT:
- if (!(saction->cache_display & TIME_CACHE_DYNAMICPAINT)) {
- continue;
- }
- break;
- case PTCACHE_TYPE_RIGIDBODY:
- if (!(saction->cache_display & TIME_CACHE_RIGIDBODY)) {
- continue;
- }
- break;
+ /* Find segment start frame. */
+ while (true) {
+ if (current > cache->endframe) {
+ return false;
}
-
- if (pid->cache->cached_frames == NULL) {
- continue;
+ if (cache->cached_frames[current - offset]) {
+ *r_segment_start = current;
+ break;
}
+ current++;
+ }
- GPU_matrix_push();
- GPU_matrix_translate_2f(0.0, (float)V2D_SCROLL_HANDLE_HEIGHT + yoffs);
- GPU_matrix_scale_2f(1.0, cache_draw_height);
-
- switch (pid->type) {
- case PTCACHE_TYPE_SOFTBODY:
- col[0] = 1.0;
- col[1] = 0.4;
- col[2] = 0.02;
- col[3] = 0.1;
- break;
- case PTCACHE_TYPE_PARTICLES:
- col[0] = 1.0;
- col[1] = 0.1;
- col[2] = 0.02;
- col[3] = 0.1;
- break;
- case PTCACHE_TYPE_CLOTH:
- col[0] = 0.1;
- col[1] = 0.1;
- col[2] = 0.75;
- col[3] = 0.1;
- break;
- case PTCACHE_TYPE_SMOKE_DOMAIN:
- case PTCACHE_TYPE_SMOKE_HIGHRES:
- col[0] = 0.2;
- col[1] = 0.2;
- col[2] = 0.2;
- col[3] = 0.1;
- break;
- case PTCACHE_TYPE_DYNAMICPAINT:
- col[0] = 1.0;
- col[1] = 0.1;
- col[2] = 0.75;
- col[3] = 0.1;
- break;
- case PTCACHE_TYPE_RIGIDBODY:
- col[0] = 1.0;
- col[1] = 0.6;
- col[2] = 0.0;
- col[3] = 0.1;
- break;
- default:
- col[0] = 1.0;
- col[1] = 0.0;
- col[2] = 1.0;
- col[3] = 0.1;
- BLI_assert(0);
- break;
+ /* Find segment end frame. */
+ while (true) {
+ if (current > cache->endframe) {
+ *r_segment_end = current - 1;
+ return true;
+ }
+ if (!cache->cached_frames[current - offset]) {
+ *r_segment_end = current - 1;
+ return true;
}
+ current++;
+ }
+}
+
+static uint timeline_cache_segments_count(PointCache *cache)
+{
+ uint count = 0;
+
+ int current = cache->startframe;
+ int segment_start;
+ int segment_end;
+ while (timeline_cache_find_next_cached_segment(cache, current, &segment_start, &segment_end)) {
+ count++;
+ current = segment_end + 1;
+ }
+
+ return count;
+}
- const int sta = pid->cache->startframe, end = pid->cache->endframe;
+static void timeline_cache_draw_cached_segments(PointCache *cache, uint pos_id)
+{
+ uint segments_count = timeline_cache_segments_count(cache);
+ if (segments_count == 0) {
+ return;
+ }
- GPU_blend(true);
+ immBeginAtMost(GPU_PRIM_TRIS, segments_count * 6);
- immUniformColor4fv(col);
- immRectf(pos, (float)sta, 0.0, (float)end, 1.0);
+ int current = cache->startframe;
+ int segment_start;
+ int segment_end;
+ while (timeline_cache_find_next_cached_segment(cache, current, &segment_start, &segment_end)) {
+ immRectf_fast(pos_id, segment_start - 0.5f, 0, segment_end + 0.5f, 1.0f);
+ current = segment_end + 1;
+ }
- col[3] = 0.4f;
- if (pid->cache->flag & PTCACHE_BAKED) {
- col[0] -= 0.4f;
- col[1] -= 0.4f;
- col[2] -= 0.4f;
- }
- else if (pid->cache->flag & PTCACHE_OUTDATED) {
- col[0] += 0.4f;
- col[1] += 0.4f;
- col[2] += 0.4f;
- }
+ immEnd();
+}
- immUniformColor4fv(col);
+static void timeline_cache_draw_single(PTCacheID *pid, float y_offset, float height, uint pos_id)
+{
+ GPU_matrix_push();
+ GPU_matrix_translate_2f(0.0, (float)V2D_SCROLL_HANDLE_HEIGHT + y_offset);
+ GPU_matrix_scale_2f(1.0, height);
- {
- /* draw a quad for each chunk of consecutive cached frames */
- const int chunk_tot = 32;
- int chunk_len = 0;
- int ista = 0, iend = -1;
+ float color[4];
+ timeline_cache_color_get(pid, color);
- for (int i = sta; i <= end; i++) {
- if (pid->cache->cached_frames[i - sta]) {
- if (chunk_len == 0) {
- immBeginAtMost(GPU_PRIM_TRIS, chunk_tot * 6);
- }
- if (ista > iend) {
- chunk_len++;
- ista = i;
- }
- iend = i;
- }
- else {
- if (ista <= iend) {
- immRectf_fast(pos, (float)ista - 0.5f, 0.0f, (float)iend + 0.5f, 1.0f);
- iend = ista - 1;
- }
- if (chunk_len >= chunk_tot) {
- immEnd();
- chunk_len = 0;
- }
- }
- }
- if (ista <= iend) {
- immRectf_fast(pos, (float)ista - 0.5f, 0.0f, (float)iend + 0.5f, 1.0f);
- }
- if (chunk_len != 0) {
- immEnd();
- }
+ immUniformColor4fv(color);
+ immRectf(pos_id, (float)pid->cache->startframe, 0.0, (float)pid->cache->endframe, 1.0);
+
+ color[3] = 0.4f;
+ timeline_cache_modify_color_based_on_state(pid->cache, color);
+ immUniformColor4fv(color);
+
+ timeline_cache_draw_cached_segments(pid->cache, pos_id);
+
+ GPU_matrix_pop();
+}
+
+void timeline_draw_cache(SpaceAction *saction, Object *ob, Scene *scene)
+{
+ if ((saction->cache_display & TIME_CACHE_DISPLAY) == 0 || ob == NULL) {
+ return;
+ }
+
+ ListBase pidlist;
+ BKE_ptcache_ids_from_object(&pidlist, ob, scene, 0);
+
+ uint pos_id = GPU_vertformat_attr_add(
+ immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+
+ GPU_blend(true);
+
+ /* Iterate over pointcaches on the active object, and draw each one's range. */
+ float y_offset = 0.0f;
+ const float cache_draw_height = 4.0f * UI_DPI_FAC * U.pixelsize;
+ for (PTCacheID *pid = pidlist.first; pid; pid = pid->next) {
+ if (timeline_cache_is_hidden_by_setting(saction, pid)) {
+ continue;
}
- GPU_blend(false);
+ if (pid->cache->cached_frames == NULL) {
+ continue;
+ }
- GPU_matrix_pop();
+ timeline_cache_draw_single(pid, y_offset, cache_draw_height, pos_id);
- yoffs += cache_draw_height;
+ y_offset += cache_draw_height;
}
+ GPU_blend(false);
immUnbindProgram();
BLI_freelistN(&pidlist);
diff --git a/source/blender/editors/space_action/action_intern.h b/source/blender/editors/space_action/action_intern.h
index eaca7968a34..c227b794ae7 100644
--- a/source/blender/editors/space_action/action_intern.h
+++ b/source/blender/editors/space_action/action_intern.h
@@ -28,9 +28,7 @@ struct ARegion;
struct ARegionType;
struct Object;
struct Scene;
-struct ScrArea;
struct SpaceAction;
-struct View2D;
struct bAnimContext;
struct bContext;
struct wmOperatorType;
diff --git a/source/blender/editors/space_action/action_select.c b/source/blender/editors/space_action/action_select.c
index 9e2634b183a..cbf4d0628e6 100644
--- a/source/blender/editors/space_action/action_select.c
+++ b/source/blender/editors/space_action/action_select.c
@@ -1717,22 +1717,26 @@ static void mouse_action_keys(bAnimContext *ac,
/* Highlight GPencil Layer */
if (ale != NULL && ale->data != NULL && ale->type == ANIMTYPE_GPLAYER) {
+ bGPdata *gpd = (bGPdata *)ale->id;
bGPDlayer *gpl = ale->data;
gpl->flag |= GP_LAYER_SELECT;
- // gpencil_layer_setactive(gpd, gpl);
+ /* Update other layer status. */
+ if (BKE_gpencil_layer_getactive(gpd) != gpl) {
+ BKE_gpencil_layer_setactive(gpd, gpl);
+ BKE_gpencil_layer_autolock_set(gpd);
+ WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+ }
}
}
else if (ac->datatype == ANIMCONT_MASK) {
/* deselect all other channels first */
ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
- /* Highlight GPencil Layer */
if (ale != NULL && ale->data != NULL && ale->type == ANIMTYPE_MASKLAYER) {
MaskLayer *masklay = ale->data;
masklay->flag |= MASK_LAYERFLAG_SELECT;
- // gpencil_layer_setactive(gpd, gpl);
}
}
}
diff --git a/source/blender/editors/space_action/space_action.c b/source/blender/editors/space_action/space_action.c
index 3c879b03126..1685852dd02 100644
--- a/source/blender/editors/space_action/space_action.c
+++ b/source/blender/editors/space_action/space_action.c
@@ -373,7 +373,7 @@ static void saction_channel_region_message_subscribe(const struct bContext *UNUS
* so just whitelist the entire structs for updates
*/
{
- wmMsgParams_RNA msg_key_params = {{{0}}};
+ wmMsgParams_RNA msg_key_params = {{0}};
StructRNA *type_array[] = {
&RNA_DopeSheet, /* dopesheet filters */
diff --git a/source/blender/editors/space_buttons/buttons_context.c b/source/blender/editors/space_buttons/buttons_context.c
index 78d5ed0f98a..56a0f472010 100644
--- a/source/blender/editors/space_buttons/buttons_context.c
+++ b/source/blender/editors/space_buttons/buttons_context.c
@@ -76,7 +76,7 @@ static int set_pointer_type(ButsContextPath *path, bContextDataResult *result, S
ptr = &path->ptr[a];
if (RNA_struct_is_a(ptr->type, type)) {
- CTX_data_pointer_set(result, ptr->id.data, ptr->type, ptr->data);
+ CTX_data_pointer_set(result, ptr->owner_id, ptr->type, ptr->data);
return 1;
}
}
@@ -930,7 +930,7 @@ int buttons_context(const bContext *C, const char *member, bContextDataResult *r
if (ct->user && ct->user->ptr.data) {
ButsTextureUser *user = ct->user;
- CTX_data_pointer_set(result, user->ptr.id.data, user->ptr.type, user->ptr.data);
+ CTX_data_pointer_set(result, user->ptr.owner_id, user->ptr.type, user->ptr.data);
}
return 1;
@@ -1018,7 +1018,7 @@ int buttons_context(const bContext *C, const char *member, bContextDataResult *r
PointerRNA *ptr = get_pointer_type(path, &RNA_ParticleSettings);
if (ptr && ptr->data) {
- CTX_data_pointer_set(result, ptr->id.data, &RNA_ParticleSettings, ptr->data);
+ CTX_data_pointer_set(result, ptr->owner_id, &RNA_ParticleSettings, ptr->data);
return 1;
}
else {
@@ -1027,7 +1027,7 @@ int buttons_context(const bContext *C, const char *member, bContextDataResult *r
if (ptr && ptr->data) {
ParticleSettings *part = ((ParticleSystem *)ptr->data)->part;
- CTX_data_pointer_set(result, ptr->id.data, &RNA_ParticleSettings, part);
+ CTX_data_pointer_set(result, ptr->owner_id, &RNA_ParticleSettings, part);
return 1;
}
}
@@ -1285,8 +1285,8 @@ ID *buttons_context_id_path(const bContext *C)
}
}
- if (ptr->id.data) {
- return ptr->id.data;
+ if (ptr->owner_id) {
+ return ptr->owner_id;
}
}
}
diff --git a/source/blender/editors/space_buttons/buttons_ops.c b/source/blender/editors/space_buttons/buttons_ops.c
index 500efe4bb4d..3ea6d183969 100644
--- a/source/blender/editors/space_buttons/buttons_ops.c
+++ b/source/blender/editors/space_buttons/buttons_ops.c
@@ -103,7 +103,7 @@ static int file_browse_exec(bContext *C, wmOperator *op)
/* add slash for directories, important for some properties */
if (RNA_property_subtype(fbo->prop) == PROP_DIRPATH) {
const bool is_relative = RNA_boolean_get(op->ptr, "relative_path");
- id = fbo->ptr.id.data;
+ id = fbo->ptr.owner_id;
BLI_strncpy(path, str, FILE_MAX);
BLI_path_abs(path, id ? ID_BLEND_PATH(bmain, id) : BKE_main_blendfile_path(bmain));
diff --git a/source/blender/editors/space_buttons/buttons_texture.c b/source/blender/editors/space_buttons/buttons_texture.c
index 87ea011e0a7..03c8fbb4e66 100644
--- a/source/blender/editors/space_buttons/buttons_texture.c
+++ b/source/blender/editors/space_buttons/buttons_texture.c
@@ -385,7 +385,7 @@ static void template_texture_select(bContext *C, void *user_p, void *UNUSED(arg)
if (user->ptr.type == &RNA_ParticleSettingsTextureSlot) {
/* stupid exception for particle systems which still uses influence
* from the old texture system, set the active texture slots as well */
- ParticleSettings *part = user->ptr.id.data;
+ ParticleSettings *part = (ParticleSettings *)user->ptr.owner_id;
int a;
for (a = 0; a < MAX_MTEX; a++) {
diff --git a/source/blender/editors/space_buttons/space_buttons.c b/source/blender/editors/space_buttons/space_buttons.c
index b3690bf2fe3..6008d06e376 100644
--- a/source/blender/editors/space_buttons/space_buttons.c
+++ b/source/blender/editors/space_buttons/space_buttons.c
@@ -404,8 +404,6 @@ static void buttons_area_listener(wmWindow *UNUSED(win),
buttons_area_redraw(sa, BCONTEXT_DATA); /* autotexpace flag */
break;
case ND_POSE:
- buttons_area_redraw(sa, BCONTEXT_DATA);
- break;
case ND_BONE_ACTIVE:
case ND_BONE_SELECT:
buttons_area_redraw(sa, BCONTEXT_BONE);
@@ -567,7 +565,7 @@ static void buttons_id_remap(ScrArea *UNUSED(sa), SpaceLink *slink, ID *old_id,
int i;
for (i = 0; i < path->len; i++) {
- if (path->ptr[i].id.data == old_id) {
+ if (path->ptr[i].owner_id == old_id) {
break;
}
}
diff --git a/source/blender/editors/space_clip/clip_dopesheet_draw.c b/source/blender/editors/space_clip/clip_dopesheet_draw.c
index 599a92ff77f..3c2d3eb1d97 100644
--- a/source/blender/editors/space_clip/clip_dopesheet_draw.c
+++ b/source/blender/editors/space_clip/clip_dopesheet_draw.c
@@ -222,8 +222,9 @@ void clip_draw_dopesheet_main(SpaceClip *sc, ARegion *ar, Scene *scene)
format, "outlineColor", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
uint flags_id = GPU_vertformat_attr_add(format, "flags", GPU_COMP_U32, 1, GPU_FETCH_INT);
- immBindBuiltinProgram(GPU_SHADER_KEYFRAME_DIAMOND);
GPU_program_point_size(true);
+ immBindBuiltinProgram(GPU_SHADER_KEYFRAME_DIAMOND);
+ immUniform1f("outline_scale", 1.0f);
immUniform2f(
"ViewportSize", BLI_rcti_size_x(&v2d->mask) + 1, BLI_rcti_size_y(&v2d->mask) + 1);
immBegin(GPU_PRIM_POINTS, keyframe_len);
diff --git a/source/blender/editors/space_console/space_console.c b/source/blender/editors/space_console/space_console.c
index 999255aef88..f5c02dbd724 100644
--- a/source/blender/editors/space_console/space_console.c
+++ b/source/blender/editors/space_console/space_console.c
@@ -26,6 +26,7 @@
#include "BLI_blenlib.h"
#include "BLI_utildefines.h"
+#include "BKE_global.h"
#include "BKE_context.h"
#include "BKE_screen.h"
@@ -173,7 +174,7 @@ static void id_drop_copy(wmDrag *drag, wmDropBox *drop)
ID *id = WM_drag_ID(drag, 0);
/* copy drag path to properties */
- char *text = RNA_path_full_ID_py(id);
+ char *text = RNA_path_full_ID_py(G_MAIN, id);
RNA_string_set(drop->ptr, "text", text);
MEM_freeN(text);
}
diff --git a/source/blender/editors/space_file/file_draw.c b/source/blender/editors/space_file/file_draw.c
index 3a6d59c1dbf..0083fc244d8 100644
--- a/source/blender/editors/space_file/file_draw.c
+++ b/source/blender/editors/space_file/file_draw.c
@@ -26,6 +26,7 @@
#include <errno.h>
#include "BLI_blenlib.h"
+#include "BLI_fileops_types.h"
#include "BLI_utildefines.h"
#include "BLI_math.h"
@@ -77,234 +78,6 @@ static char *file_draw_tooltip_func(bContext *UNUSED(C), void *argN, const char
return BLI_strdup(dyn_tooltip);
}
-/* Note: This function uses pixelspace (0, 0, winx, winy), not view2d.
- * The controls are laid out as follows:
- *
- * -------------------------------------------
- * | Directory input | execute |
- * -------------------------------------------
- * | Filename input | + | - | cancel |
- * -------------------------------------------
- *
- * The input widgets will stretch to fill any excess space.
- * When there isn't enough space for all controls to be shown, they are
- * hidden in this order: x/-, execute/cancel, input widgets.
- */
-void file_draw_buttons(const bContext *C, ARegion *ar)
-{
- /* Button layout. */
- const int max_x = ar->winx - 10;
- const int line1_y = ar->winy - (IMASEL_BUTTONS_HEIGHT / 2 + IMASEL_BUTTONS_MARGIN);
- const int line2_y = line1_y - (IMASEL_BUTTONS_HEIGHT / 2 + IMASEL_BUTTONS_MARGIN);
- const int input_minw = 20;
- const int btn_h = UI_UNIT_Y;
- const int btn_fn_w = UI_UNIT_X;
- const int btn_minw = 80;
- const int btn_margin = 20;
- const int separator = 4;
-
- /* Additional locals. */
- char uiblockstr[32];
- int loadbutton;
- int fnumbuttons;
- int min_x = 10;
- int chan_offs = 0;
- int available_w = max_x - min_x;
- int line1_w = available_w;
- int line2_w = available_w;
-
- uiBut *but;
- uiBlock *block;
- SpaceFile *sfile = CTX_wm_space_file(C);
- FileSelectParams *params = ED_fileselect_get_params(sfile);
- ARegion *artmp;
- const bool is_browse_only = (sfile->op == NULL);
-
- /* Initialize UI block. */
- BLI_snprintf(uiblockstr, sizeof(uiblockstr), "win %p", (void *)ar);
- block = UI_block_begin(C, ar, uiblockstr, UI_EMBOSS);
-
- /* exception to make space for collapsed region icon */
- for (artmp = CTX_wm_area(C)->regionbase.first; artmp; artmp = artmp->next) {
- if (artmp->regiontype == RGN_TYPE_TOOLS && artmp->flag & RGN_FLAG_HIDDEN) {
- chan_offs = 16;
- min_x += chan_offs;
- available_w -= chan_offs;
- }
- }
-
- /* Is there enough space for the execute / cancel buttons? */
-
- if (is_browse_only) {
- loadbutton = 0;
- }
- else {
- const uiFontStyle *fstyle = UI_FSTYLE_WIDGET;
- loadbutton = UI_fontstyle_string_width(fstyle, params->title) + btn_margin;
- CLAMP_MIN(loadbutton, btn_minw);
- if (available_w <= loadbutton + separator + input_minw) {
- loadbutton = 0;
- }
- }
-
- if (loadbutton) {
- line1_w -= (loadbutton + separator);
- line2_w = line1_w;
- }
-
- /* Is there enough space for file number increment/decrement buttons? */
- fnumbuttons = 2 * btn_fn_w;
- if (!loadbutton || line2_w <= fnumbuttons + separator + input_minw) {
- fnumbuttons = 0;
- }
- else {
- line2_w -= (fnumbuttons + separator);
- }
-
- /* Text input fields for directory and file. */
- if (available_w > 0) {
- const struct FileDirEntry *file = sfile->files ?
- filelist_file(sfile->files, params->active_file) :
- NULL;
- int overwrite_alert = file_draw_check_exists(sfile);
- const bool is_active_dir = file && (file->typeflag & FILE_TYPE_FOLDER);
-
- /* callbacks for operator check functions */
- UI_block_func_set(block, file_draw_check_cb, NULL, NULL);
-
- but = uiDefBut(block,
- UI_BTYPE_TEXT,
- -1,
- "",
- min_x,
- line1_y,
- line1_w - chan_offs,
- btn_h,
- params->dir,
- 0.0,
- (float)FILE_MAX,
- 0,
- 0,
- TIP_("File path"));
- UI_but_func_complete_set(but, autocomplete_directory, NULL);
- UI_but_flag_enable(but, UI_BUT_NO_UTF8);
- UI_but_flag_disable(but, UI_BUT_UNDO);
- UI_but_funcN_set(but, file_directory_enter_handle, NULL, but);
-
- /* TODO, directory editing is non-functional while a library is loaded
- * until this is properly supported just disable it. */
- if (sfile->files && filelist_lib(sfile->files)) {
- UI_but_flag_enable(but, UI_BUT_DISABLED);
- }
-
- if ((params->flag & FILE_DIRSEL_ONLY) == 0) {
- but = uiDefBut(
- block,
- UI_BTYPE_TEXT,
- -1,
- "",
- min_x,
- line2_y,
- line2_w - chan_offs,
- btn_h,
- is_active_dir ? (char *)"" : params->file,
- 0.0,
- (float)FILE_MAXFILE,
- 0,
- 0,
- TIP_(overwrite_alert ? N_("File name, overwrite existing") : N_("File name")));
- UI_but_func_complete_set(but, autocomplete_file, NULL);
- UI_but_flag_enable(but, UI_BUT_NO_UTF8);
- UI_but_flag_disable(but, UI_BUT_UNDO);
- /* silly workaround calling NFunc to ensure this does not get called
- * immediate ui_apply_but_func but only after button deactivates */
- UI_but_funcN_set(but, file_filename_enter_handle, NULL, but);
-
- /* check if this overrides a file and if the operator option is used */
- if (overwrite_alert) {
- UI_but_flag_enable(but, UI_BUT_REDALERT);
- }
- }
-
- /* clear func */
- UI_block_func_set(block, NULL, NULL, NULL);
- }
-
- /* Filename number increment / decrement buttons. */
- if (fnumbuttons && (params->flag & FILE_DIRSEL_ONLY) == 0) {
- UI_block_align_begin(block);
- but = uiDefIconButO(block,
- UI_BTYPE_BUT,
- "FILE_OT_filenum",
- 0,
- ICON_REMOVE,
- min_x + line2_w + separator - chan_offs,
- line2_y,
- btn_fn_w,
- btn_h,
- TIP_("Decrement the filename number"));
- RNA_int_set(UI_but_operator_ptr_get(but), "increment", -1);
-
- but = uiDefIconButO(block,
- UI_BTYPE_BUT,
- "FILE_OT_filenum",
- 0,
- ICON_ADD,
- min_x + line2_w + separator + btn_fn_w - chan_offs,
- line2_y,
- btn_fn_w,
- btn_h,
- TIP_("Increment the filename number"));
- RNA_int_set(UI_but_operator_ptr_get(but), "increment", 1);
- UI_block_align_end(block);
- }
-
- /* Execute / cancel buttons. */
- if (loadbutton) {
- const struct FileDirEntry *file = sfile->files ?
- filelist_file(sfile->files, params->active_file) :
- NULL;
- char const *str_exec;
-
- if (file && FILENAME_IS_PARENT(file->relpath)) {
- str_exec = IFACE_("Parent Directory");
- }
- else if (file && file->typeflag & FILE_TYPE_DIR) {
- str_exec = IFACE_("Open Directory");
- }
- else {
- str_exec = params->title; /* params->title is already translated! */
- }
-
- but = uiDefButO(block,
- UI_BTYPE_BUT,
- "FILE_OT_execute",
- WM_OP_EXEC_REGION_WIN,
- str_exec,
- max_x - loadbutton,
- line1_y,
- loadbutton,
- btn_h,
- "");
- /* Just a display hint. */
- UI_but_flag_enable(but, UI_BUT_ACTIVE_DEFAULT);
-
- uiDefButO(block,
- UI_BTYPE_BUT,
- "FILE_OT_cancel",
- WM_OP_EXEC_REGION_WIN,
- IFACE_("Cancel"),
- max_x - loadbutton,
- line2_y,
- loadbutton,
- btn_h,
- "");
- }
-
- UI_block_end(C, block);
- UI_block_draw(C, block);
-}
-
static void draw_tile(int sx, int sy, int width, int height, int colorid, int shade)
{
float color[4];
@@ -349,7 +122,7 @@ static void file_draw_string(int sx,
rcti rect;
char fname[FILE_MAXFILE];
- if (string[0] == '\0') {
+ if (string[0] == '\0' || width < 1) {
return;
}
@@ -362,7 +135,7 @@ static void file_draw_string(int sx,
/* no text clipping needed, UI_fontstyle_draw does it but is a bit too strict
* (for buttons it works) */
rect.xmin = sx;
- rect.xmax = (int)(sx + ceil(width + 5.0f / UI_DPI_FAC));
+ rect.xmax = sx + round_fl_to_int(width);
rect.ymin = sy - height;
rect.ymax = sy;
@@ -404,8 +177,8 @@ static void file_draw_preview(uiBlock *block,
float scaledx, scaledy;
float scale;
int ex, ey;
- bool use_dropshadow = !is_icon && (typeflags & FILE_TYPE_IMAGE);
- float col[4] = {1.0f, 1.0f, 1.0f, 1.0f};
+ bool use_dropshadow = !is_icon &&
+ (typeflags & (FILE_TYPE_IMAGE | FILE_TYPE_MOVIE | FILE_TYPE_BLENDER));
BLI_assert(imb != NULL);
@@ -442,13 +215,27 @@ static void file_draw_preview(uiBlock *block,
/* shadow */
if (use_dropshadow) {
- UI_draw_box_shadow(220, (float)xco, (float)yco, (float)(xco + ex), (float)(yco + ey));
+ UI_draw_box_shadow(128, (float)xco, (float)yco, (float)(xco + ex), (float)(yco + ey));
}
GPU_blend(true);
- /* the image */
- if (!is_icon && typeflags & FILE_TYPE_FTFONT) {
+ /* the large image */
+
+ float col[4] = {1.0f, 1.0f, 1.0f, 1.0f};
+ if (is_icon) {
+ /* File and Folder icons draw with lowered opacity until we add themes */
+ col[3] = 0.6f;
+ /* Use dark images if background is light */
+ float bg[3];
+ UI_GetThemeColor3fv(TH_BACK, bg);
+ if (rgb_to_grayscale(bg) > 0.5f) {
+ col[0] = 0;
+ col[1] = 0;
+ col[2] = 0;
+ }
+ }
+ else if (typeflags & FILE_TYPE_FTFONT) {
UI_GetThemeColor4fv(TH_TEXT, col);
}
@@ -477,30 +264,61 @@ static void file_draw_preview(uiBlock *block,
GPU_blend_set_func_separate(
GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
- if (icon) {
- UI_icon_draw_ex((float)xco + (7 * UI_DPI_FAC),
- (float)yco + (7 * UI_DPI_FAC),
- icon,
- icon_aspect,
- 1.0f,
- 0.0f,
- NULL,
- false);
+ if (icon && (icon != ICON_FILE_FONT)) {
+ /* size of center icon is scaled to fit container and UI scale */
+ float icon_x, icon_y;
+
+ if (is_icon) {
+ const float icon_size = 16.0f / icon_aspect * U.dpi_fac;
+ float icon_opacity = MIN2(icon_aspect, 0.7);
+ uchar icon_color[4] = {255, 255, 255, 255};
+ float bg[3];
+ /* base this off theme color of file or folder later */
+ UI_GetThemeColor3fv(TH_BACK, bg);
+ if (rgb_to_grayscale(bg) > 0.5f) {
+ icon_color[0] = 0;
+ icon_color[1] = 0;
+ icon_color[2] = 0;
+ }
+ icon_x = xco + (ex / 2.0f) - (icon_size / 2.0f);
+ icon_y = yco + (ey / 2.0f) - (icon_size * ((typeflags & FILE_TYPE_DIR) ? 0.78f : 0.65f));
+ UI_icon_draw_ex(
+ icon_x, icon_y, icon, icon_aspect / U.dpi_fac, icon_opacity, 0.0f, icon_color, false);
+ }
+ else {
+ const uchar dark[4] = {0, 0, 0, 255};
+ const uchar light[4] = {255, 255, 255, 255};
+
+ /* Smaller, fainter icon for preview image thumbnail. */
+ icon_x = xco + (2.0f * UI_DPI_FAC);
+ icon_y = yco + (2.0f * UI_DPI_FAC);
+
+ UI_icon_draw_ex(icon_x + 1, icon_y - 1, icon, 1.0f / U.dpi_fac, 0.2f, 0.0f, dark, false);
+ UI_icon_draw_ex(icon_x, icon_y, icon, 1.0f / U.dpi_fac, 0.6f, 0.0f, light, false);
+ }
}
/* border */
if (use_dropshadow) {
GPUVertFormat *format = immVertexFormat();
- uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ uint pos_attr = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ uint col_attr = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
- immUniformColor4f(0.0f, 0.0f, 0.0f, 0.4f);
- imm_draw_box_wire_2d(pos, (float)xco, (float)yco, (float)(xco + ex), (float)(yco + ey));
+ immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR);
+ immBegin(GPU_PRIM_LINE_LOOP, 4);
+ immAttr4f(col_attr, 1.0f, 1.0f, 1.0f, 0.15f);
+ immVertex2f(pos_attr, (float)xco + 1, (float)(yco + ey));
+ immAttr4f(col_attr, 1.0f, 1.0f, 1.0f, 0.2f);
+ immVertex2f(pos_attr, (float)(xco + ex), (float)(yco + ey));
+ immAttr4f(col_attr, 0.0f, 0.0f, 0.0f, 0.2f);
+ immVertex2f(pos_attr, (float)(xco + ex), (float)yco + 1);
+ immAttr4f(col_attr, 0.0f, 0.0f, 0.0f, 0.3f);
+ immVertex2f(pos_attr, (float)xco + 1, (float)yco + 1);
+ immEnd();
immUnbindProgram();
}
but = uiDefBut(block, UI_BTYPE_LABEL, 0, "", xco, yco, ex, ey, NULL, 0.0, 0.0, 0, 0, NULL);
- UI_but_func_tooltip_set(but, file_draw_tooltip_func, BLI_strdup(path));
/* dragregion */
if (drag) {
@@ -557,6 +375,7 @@ static void renamebutton_cb(bContext *C, void *UNUSED(arg1), char *oldname)
static void draw_background(FileLayout *layout, View2D *v2d)
{
+ const int item_height = layout->tile_h + (2 * layout->tile_border_y);
int i;
int sy;
@@ -565,9 +384,11 @@ static void draw_background(FileLayout *layout, View2D *v2d)
immUniformThemeColorShade(TH_BACK, -7);
/* alternating flat shade background */
- for (i = 0; (i <= layout->rows); i += 2) {
- sy = (int)v2d->cur.ymax - i * (layout->tile_h + 2 * layout->tile_border_y) -
- layout->tile_border_y;
+ for (i = 2; (i <= layout->rows + 1); i += 2) {
+ sy = (int)v2d->cur.ymax - layout->offset_top - i * item_height - layout->tile_border_y;
+
+ /* Offsett pattern slightly to add scroll effect. */
+ sy += round_fl_to_int(item_height * (v2d->tot.ymax - v2d->cur.ymax) / item_height);
immRectf(pos,
v2d->cur.xmin,
@@ -632,6 +453,176 @@ static void draw_dividers(FileLayout *layout, View2D *v2d)
}
}
+static void draw_columnheader_background(const FileLayout *layout, const View2D *v2d)
+{
+ uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+
+ immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immUniformThemeColorShade(TH_BACK, 11);
+
+ immRectf(pos,
+ v2d->cur.xmin,
+ v2d->cur.ymax - layout->attribute_column_header_h,
+ v2d->cur.xmax,
+ v2d->cur.ymax);
+
+ immUnbindProgram();
+}
+
+static void draw_columnheader_columns(const FileSelectParams *params,
+ FileLayout *layout,
+ const View2D *v2d,
+ const uchar text_col[4])
+{
+ const float divider_pad = 0.2 * layout->attribute_column_header_h;
+ int sx = v2d->cur.xmin, sy = v2d->cur.ymax;
+
+ for (FileAttributeColumnType column_type = 0; column_type < ATTRIBUTE_COLUMN_MAX;
+ column_type++) {
+ if (!file_attribute_column_type_enabled(params, column_type)) {
+ continue;
+ }
+ const FileAttributeColumn *column = &layout->attribute_columns[column_type];
+
+ /* Active sort type triangle */
+ if (params->sort == column->sort_type) {
+ float tri_color[4];
+
+ rgba_uchar_to_float(tri_color, text_col);
+ UI_draw_icon_tri(sx + column->width - (0.3f * U.widget_unit) -
+ ATTRIBUTE_COLUMN_PADDING / 2.0f,
+ sy + (0.1f * U.widget_unit) - (layout->attribute_column_header_h / 2),
+ (params->flag & FILE_SORT_INVERT) ? 't' : 'v',
+ tri_color);
+ }
+
+ file_draw_string(sx + ATTRIBUTE_COLUMN_PADDING,
+ sy - layout->tile_border_y,
+ IFACE_(column->name),
+ column->width - 2 * ATTRIBUTE_COLUMN_PADDING,
+ layout->attribute_column_header_h - layout->tile_border_y,
+ UI_STYLE_TEXT_LEFT,
+ text_col);
+
+ /* Separator line */
+ if (column_type != COLUMN_NAME) {
+ uint pos = GPU_vertformat_attr_add(
+ immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+
+ immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immUniformThemeColorShade(TH_BACK, -10);
+ immBegin(GPU_PRIM_LINES, 2);
+ immVertex2f(pos, sx - 1, sy - divider_pad);
+ immVertex2f(pos, sx - 1, sy - layout->attribute_column_header_h + divider_pad);
+ immEnd();
+ immUnbindProgram();
+ }
+
+ sx += column->width;
+ }
+
+ /* Vertical separator lines line */
+ {
+ uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immUniformThemeColorShade(TH_BACK, -10);
+ immBegin(GPU_PRIM_LINES, 4);
+ immVertex2f(pos, v2d->cur.xmin, sy);
+ immVertex2f(pos, v2d->cur.xmax, sy);
+ immVertex2f(pos, v2d->cur.xmin, sy - layout->attribute_column_header_h);
+ immVertex2f(pos, v2d->cur.xmax, sy - layout->attribute_column_header_h);
+ immEnd();
+ immUnbindProgram();
+ }
+}
+
+/**
+ * Updates the stat string stored in file->entry if necessary.
+ */
+static const char *filelist_get_details_column_string(FileAttributeColumnType column,
+ const FileDirEntry *file,
+ const bool small_size,
+ const bool update_stat_strings)
+{
+ switch (column) {
+ case COLUMN_DATETIME:
+ if (!(file->typeflag & FILE_TYPE_BLENDERLIB) && !FILENAME_IS_CURRPAR(file->relpath)) {
+ if ((file->entry->datetime_str[0] == '\0') || update_stat_strings) {
+ char date[FILELIST_DIRENTRY_DATE_LEN], time[FILELIST_DIRENTRY_TIME_LEN];
+ bool is_today, is_yesterday;
+
+ BLI_filelist_entry_datetime_to_string(
+ NULL, file->entry->time, small_size, time, date, &is_today, &is_yesterday);
+
+ if (is_today || is_yesterday) {
+ BLI_strncpy(date, is_today ? N_("Today") : N_("Yesterday"), sizeof(date));
+ }
+ BLI_snprintf(
+ file->entry->datetime_str, sizeof(file->entry->datetime_str), "%s %s", date, time);
+ }
+
+ return file->entry->datetime_str;
+ }
+ break;
+ case COLUMN_SIZE:
+ if ((file->typeflag & (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)) ||
+ !(file->typeflag & (FILE_TYPE_DIR | FILE_TYPE_BLENDERLIB))) {
+ if ((file->entry->size_str[0] == '\0') || update_stat_strings) {
+ BLI_filelist_entry_size_to_string(
+ NULL, file->entry->size, small_size, file->entry->size_str);
+ }
+
+ return file->entry->size_str;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return NULL;
+}
+
+static void draw_details_columns(const FileSelectParams *params,
+ const FileLayout *layout,
+ const FileDirEntry *file,
+ const int pos_x,
+ const int pos_y,
+ const uchar text_col[4])
+{
+ const bool small_size = SMALL_SIZE_CHECK(params->thumbnail_size);
+ const bool update_stat_strings = small_size != SMALL_SIZE_CHECK(layout->curr_size);
+ int sx = pos_x - layout->tile_border_x - (UI_UNIT_X * 0.1f), sy = pos_y;
+
+ for (FileAttributeColumnType column_type = 0; column_type < ATTRIBUTE_COLUMN_MAX;
+ column_type++) {
+ const FileAttributeColumn *column = &layout->attribute_columns[column_type];
+
+ /* Name column is not a detail column (should already be drawn), always skip here. */
+ if (column_type == COLUMN_NAME) {
+ sx += column->width;
+ continue;
+ }
+ if (!file_attribute_column_type_enabled(params, column_type)) {
+ continue;
+ }
+
+ const char *str = filelist_get_details_column_string(
+ column_type, file, small_size, update_stat_strings);
+
+ if (str) {
+ file_draw_string(sx + ATTRIBUTE_COLUMN_PADDING,
+ sy - layout->tile_border_y,
+ IFACE_(str),
+ column->width - 2 * ATTRIBUTE_COLUMN_PADDING,
+ layout->tile_h,
+ column->text_align,
+ text_col);
+ }
+
+ sx += column->width;
+ }
+}
+
void file_draw_list(const bContext *C, ARegion *ar)
{
SpaceFile *sfile = CTX_wm_space_file(C);
@@ -652,18 +643,14 @@ void file_draw_list(const bContext *C, ARegion *ar)
bool is_icon;
eFontStyle_Align align;
bool do_drag;
- int column_space = 0.6f * UI_UNIT_X;
unsigned char text_col[4];
- const bool small_size = SMALL_SIZE_CHECK(params->thumbnail_size);
- const bool update_stat_strings = small_size != SMALL_SIZE_CHECK(layout->curr_size);
- const float thumb_icon_aspect = sqrtf(64.0f / (float)(params->thumbnail_size));
+ const bool draw_columnheader = (params->display == FILE_VERTICALDISPLAY);
+ const float thumb_icon_aspect = MIN2(64.0f / (float)(params->thumbnail_size), 1.0f);
numfiles = filelist_files_ensure(files);
if (params->display != FILE_IMGDISPLAY) {
-
draw_background(layout, v2d);
-
draw_dividers(layout, v2d);
}
@@ -679,13 +666,14 @@ void file_draw_list(const bContext *C, ARegion *ar)
numfiles_layout += layout->rows;
}
else {
- numfiles_layout += layout->columns;
+ numfiles_layout += layout->flow_columns;
}
filelist_file_cache_slidingwindow_set(files, numfiles_layout);
- textwidth = (FILE_IMGDISPLAY == params->display) ? layout->tile_w :
- (int)layout->column_widths[COLUMN_NAME];
+ textwidth = (FILE_IMGDISPLAY == params->display) ?
+ layout->tile_w :
+ round_fl_to_int(layout->attribute_columns[COLUMN_NAME].width);
textheight = (int)(layout->textheight * 3.0 / 2.0 + 0.5);
align = (FILE_IMGDISPLAY == params->display) ? UI_STYLE_TEXT_CENTER : UI_STYLE_TEXT_LEFT;
@@ -719,11 +707,16 @@ void file_draw_list(const bContext *C, ARegion *ar)
BLF_batch_draw_begin();
+ UI_GetThemeColor4ubv(TH_TEXT, text_col);
+
for (i = offset; (i < numfiles) && (i < offset + numfiles_layout); i++) {
unsigned int file_selflag;
char path[FILE_MAX_LIBEXTRA];
+ int padx = 0.1f * UI_UNIT_X;
+ int icon_ofs = 0;
+
ED_fileselect_layout_tilepos(layout, i, &sx, &sy);
- sx += (int)(v2d->tot.xmin + 0.1f * UI_UNIT_X);
+ sx += (int)(v2d->tot.xmin + padx);
sy = (int)(v2d->tot.ymax - sy);
file = filelist_file(files, i);
@@ -737,15 +730,14 @@ void file_draw_list(const bContext *C, ARegion *ar)
int colorid = (file_selflag & FILE_SEL_SELECTED) ? TH_HILITE : TH_BACK;
int shade = (params->highlight_file == i) || (file_selflag & FILE_SEL_HIGHLIGHTED) ? 35 :
0;
+ const short width = ELEM(params->display, FILE_VERTICALDISPLAY, FILE_HORIZONTALDISPLAY) ?
+ layout->tile_w - (2 * padx) :
+ layout->tile_w;
BLI_assert(i == 0 || !FILENAME_IS_CURRPAR(file->relpath));
- draw_tile(sx,
- sy - 1,
- layout->tile_w + 4,
- sfile->layout->tile_h + layout->tile_border_y,
- colorid,
- shade);
+ draw_tile(
+ sx, sy - 1, width, sfile->layout->tile_h + layout->tile_border_y, colorid, shade);
}
}
UI_draw_roundbox_corner_set(UI_CNR_NONE);
@@ -778,38 +770,28 @@ void file_draw_list(const bContext *C, ARegion *ar)
file_draw_icon(block,
path,
sx,
- sy - (UI_UNIT_Y / 6),
+ sy - layout->tile_border_y,
filelist_geticon(files, i, true),
ICON_DEFAULT_WIDTH_SCALE,
ICON_DEFAULT_HEIGHT_SCALE,
do_drag);
- sx += ICON_DEFAULT_WIDTH_SCALE + 0.2f * UI_UNIT_X;
+ icon_ofs += ICON_DEFAULT_WIDTH_SCALE + 0.2f * UI_UNIT_X;
}
- UI_GetThemeColor4ubv(TH_TEXT, text_col);
-
if (file_selflag & FILE_SEL_EDITING) {
uiBut *but;
- short width;
-
- if (params->display == FILE_SHORTDISPLAY) {
- width = layout->tile_w - (ICON_DEFAULT_WIDTH_SCALE + 0.2f * UI_UNIT_X);
- }
- else if (params->display == FILE_LONGDISPLAY) {
- width = layout->column_widths[COLUMN_NAME] + (column_space * 3.5f);
- }
- else {
- BLI_assert(params->display == FILE_IMGDISPLAY);
- width = textwidth;
- }
+ const short width = (params->display == FILE_IMGDISPLAY) ?
+ textwidth :
+ layout->attribute_columns[COLUMN_NAME].width -
+ ATTRIBUTE_COLUMN_PADDING;
but = uiDefBut(block,
UI_BTYPE_TEXT,
1,
"",
- sx,
+ sx + icon_ofs,
sy - layout->tile_h - 0.15f * UI_UNIT_X,
- width,
+ width - icon_ofs,
textheight,
sfile->params->renamefile,
1.0f,
@@ -825,74 +807,19 @@ void file_draw_list(const bContext *C, ARegion *ar)
sfile->files, file, FILE_SEL_REMOVE, FILE_SEL_EDITING, CHECK_ALL);
}
}
-
- if (!(file_selflag & FILE_SEL_EDITING)) {
- int tpos = (FILE_IMGDISPLAY == params->display) ? sy - layout->tile_h + layout->textheight :
- sy;
- file_draw_string(sx + 1, tpos, file->name, (float)textwidth, textheight, align, text_col);
- }
-
- sx += (int)layout->column_widths[COLUMN_NAME] + column_space;
- if (params->display == FILE_SHORTDISPLAY) {
- if ((file->typeflag & (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)) ||
- !(file->typeflag & (FILE_TYPE_DIR | FILE_TYPE_BLENDERLIB))) {
- if ((file->entry->size_str[0] == '\0') || update_stat_strings) {
- BLI_filelist_entry_size_to_string(
- NULL, file->entry->size, small_size, file->entry->size_str);
- }
- file_draw_string(sx,
- sy,
- file->entry->size_str,
- layout->column_widths[COLUMN_SIZE],
- layout->tile_h,
- align,
- text_col);
- }
- sx += (int)layout->column_widths[COLUMN_SIZE] + column_space;
+ else {
+ const int txpos = (params->display == FILE_IMGDISPLAY) ? sx : sx + 1 + icon_ofs;
+ const int typos = (params->display == FILE_IMGDISPLAY) ?
+ sy - layout->tile_h + layout->textheight :
+ sy - layout->tile_border_y;
+ const int twidth = (params->display == FILE_IMGDISPLAY) ?
+ textwidth :
+ textwidth - 1 - icon_ofs - padx - layout->tile_border_x;
+ file_draw_string(txpos, typos, file->name, (float)twidth, textheight, align, text_col);
}
- else if (params->display == FILE_LONGDISPLAY) {
- if (!(file->typeflag & FILE_TYPE_BLENDERLIB) && !FILENAME_IS_CURRPAR(file->relpath)) {
- if ((file->entry->date_str[0] == '\0') || update_stat_strings) {
- BLI_filelist_entry_datetime_to_string(
- NULL, file->entry->time, small_size, file->entry->time_str, file->entry->date_str);
- }
- file_draw_string(sx,
- sy,
- file->entry->date_str,
- layout->column_widths[COLUMN_DATE],
- layout->tile_h,
- align,
- text_col);
- sx += (int)layout->column_widths[COLUMN_DATE] + column_space;
- file_draw_string(sx,
- sy,
- file->entry->time_str,
- layout->column_widths[COLUMN_TIME],
- layout->tile_h,
- align,
- text_col);
- sx += (int)layout->column_widths[COLUMN_TIME] + column_space;
- }
- else {
- sx += (int)layout->column_widths[COLUMN_DATE] + column_space;
- sx += (int)layout->column_widths[COLUMN_TIME] + column_space;
- }
- if ((file->typeflag & (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)) ||
- !(file->typeflag & (FILE_TYPE_DIR | FILE_TYPE_BLENDERLIB))) {
- if ((file->entry->size_str[0] == '\0') || update_stat_strings) {
- BLI_filelist_entry_size_to_string(
- NULL, file->entry->size, small_size, file->entry->size_str);
- }
- file_draw_string(sx,
- sy,
- file->entry->size_str,
- layout->column_widths[COLUMN_SIZE],
- layout->tile_h,
- align,
- text_col);
- }
- sx += (int)layout->column_widths[COLUMN_SIZE] + column_space;
+ if (params->display != FILE_IMGDISPLAY) {
+ draw_details_columns(params, layout, file, sx, sy, text_col);
}
}
@@ -901,5 +828,11 @@ void file_draw_list(const bContext *C, ARegion *ar)
UI_block_end(C, block);
UI_block_draw(C, block);
+ /* Draw last, on top of file list. */
+ if (draw_columnheader) {
+ draw_columnheader_background(layout, v2d);
+ draw_columnheader_columns(params, layout, v2d, text_col);
+ }
+
layout->curr_size = params->thumbnail_size;
}
diff --git a/source/blender/editors/space_file/file_intern.h b/source/blender/editors/space_file/file_intern.h
index bad25511dd5..61f13098783 100644
--- a/source/blender/editors/space_file/file_intern.h
+++ b/source/blender/editors/space_file/file_intern.h
@@ -30,9 +30,11 @@ struct ARegion;
struct ARegionType;
struct FileSelectParams;
struct SpaceFile;
+struct View2D;
/* file_ops.c */
struct ARegion *file_tools_region(struct ScrArea *sa);
+struct ARegion *file_tool_props_region(struct ScrArea *sa);
/* file_draw.c */
#define TILE_BORDER_X (UI_UNIT_X / 4)
@@ -42,9 +44,10 @@ struct ARegion *file_tools_region(struct ScrArea *sa);
#define IMASEL_BUTTONS_HEIGHT (UI_UNIT_Y * 2)
#define IMASEL_BUTTONS_MARGIN (UI_UNIT_Y / 6)
+#define ATTRIBUTE_COLUMN_PADDING (0.5f * UI_UNIT_X)
+
#define SMALL_SIZE_CHECK(_size) ((_size) < 64) /* Related to FileSelectParams.thumbnail_size. */
-void file_draw_buttons(const bContext *C, ARegion *ar);
void file_calc_previews(const bContext *C, ARegion *ar);
void file_draw_list(const bContext *C, ARegion *ar);
@@ -64,6 +67,7 @@ typedef enum WalkSelectDirection {
} WalkSelectDirections;
void FILE_OT_highlight(struct wmOperatorType *ot);
+void FILE_OT_sort_column_ui_context(struct wmOperatorType *ot);
void FILE_OT_select(struct wmOperatorType *ot);
void FILE_OT_select_walk(struct wmOperatorType *ot);
void FILE_OT_select_all(struct wmOperatorType *ot);
@@ -112,6 +116,16 @@ void file_operator_to_sfile(bContext *C, struct SpaceFile *sfile, struct wmOpera
/* filesel.c */
void fileselect_file_set(SpaceFile *sfile, const int index);
+bool file_attribute_column_type_enabled(const FileSelectParams *params,
+ FileAttributeColumnType column);
+bool file_attribute_column_header_is_inside(const struct View2D *v2d,
+ const FileLayout *layout,
+ int x,
+ int y);
+FileAttributeColumnType file_attribute_column_type_find_isect(const View2D *v2d,
+ const FileSelectParams *params,
+ FileLayout *layout,
+ int x);
float file_string_width(const char *str);
float file_font_pointsize(void);
diff --git a/source/blender/editors/space_file/file_ops.c b/source/blender/editors/space_file/file_ops.c
index eb5f02b6e13..5168300d21e 100644
--- a/source/blender/editors/space_file/file_ops.c
+++ b/source/blender/editors/space_file/file_ops.c
@@ -78,7 +78,12 @@ static FileSelection find_file_mouse_rect(SpaceFile *sfile, ARegion *ar, const r
BLI_rctf_rcti_copy(&rect_region_fl, rect_region);
+ /* Okay, manipulating v2d rects here is hacky... */
+ v2d->mask.ymax -= sfile->layout->offset_top;
+ v2d->cur.ymax -= sfile->layout->offset_top;
UI_view2d_region_to_view_rctf(v2d, &rect_region_fl, &rect_view_fl);
+ v2d->mask.ymax += sfile->layout->offset_top;
+ v2d->cur.ymax += sfile->layout->offset_top;
BLI_rcti_init(&rect_view,
(int)(v2d->tot.xmin + rect_view_fl.xmin),
@@ -190,7 +195,6 @@ static FileSelect file_select_do(bContext *C, int selected_idx, bool do_diropen)
const bool is_parent_dir = FILENAME_IS_PARENT(file->relpath);
if (do_diropen == false) {
- params->file[0] = '\0';
retval = FILE_SELECT_DIR;
}
/* the path is too long and we are not going up! */
@@ -262,8 +266,8 @@ static void file_ensure_inside_viewbounds(ARegion *ar, SpaceFile *sfile, const i
cur->ymax = cur->ymin + ar->winy;
}
/* up */
- else if (cur->ymax < rect.ymax) {
- cur->ymax = rect.ymax + layout->tile_border_y;
+ else if ((cur->ymax - layout->offset_top) < rect.ymax) {
+ cur->ymax = rect.ymax + layout->tile_border_y + layout->offset_top;
cur->ymin = cur->ymax - ar->winy;
}
/* left - also use if tile is wider than viewbounds so view is aligned to file name */
@@ -278,7 +282,7 @@ static void file_ensure_inside_viewbounds(ARegion *ar, SpaceFile *sfile, const i
}
else {
BLI_assert(cur->xmin <= rect.xmin && cur->xmax >= rect.xmax && cur->ymin <= rect.ymin &&
- cur->ymax >= rect.ymax);
+ (cur->ymax - layout->offset_top) >= rect.ymax);
changed = false;
}
@@ -384,7 +388,7 @@ static int file_box_select_modal(bContext *C, wmOperator *op, const wmEvent *eve
if (result == OPERATOR_RUNNING_MODAL) {
WM_operator_properties_border_to_rcti(op, &rect);
- BLI_rcti_isect(&(ar->v2d.mask), &rect, &rect);
+ ED_fileselect_layout_isect_rect(sfile->layout, &ar->v2d, &rect, &rect);
sel = file_selection_get(C, &rect, 0);
if ((sel.first != params->sel_first) || (sel.last != params->sel_last)) {
@@ -440,13 +444,13 @@ static int file_box_select_exec(bContext *C, wmOperator *op)
file_deselect_all(sfile, FILE_SEL_SELECTED);
}
- BLI_rcti_isect(&(ar->v2d.mask), &rect, &rect);
+ ED_fileselect_layout_isect_rect(sfile->layout, &ar->v2d, &rect, &rect);
ret = file_select(C, &rect, select ? FILE_SEL_ADD : FILE_SEL_REMOVE, false, false);
/* unselect '..' parent entry - it's not supposed to be selected if more than
* one file is selected */
- filelist_entry_select_index_set(sfile->files, 0, FILE_SEL_REMOVE, FILE_SEL_SELECTED, CHECK_ALL);
+ filelist_entry_parent_select_set(sfile->files, FILE_SEL_REMOVE, FILE_SEL_SELECTED, CHECK_ALL);
if (FILE_SELECT_DIR == ret) {
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL);
@@ -493,7 +497,7 @@ static int file_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
rect.xmin = rect.xmax = event->mval[0];
rect.ymin = rect.ymax = event->mval[1];
- if (!BLI_rcti_isect_pt(&ar->v2d.mask, rect.xmin, rect.ymin)) {
+ if (!ED_fileselect_layout_is_inside_pt(sfile->layout, &ar->v2d, rect.xmin, rect.ymin)) {
return OPERATOR_CANCELLED;
}
@@ -514,8 +518,7 @@ static int file_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
if (extend) {
/* unselect '..' parent entry - it's not supposed to be selected if more
* than one file is selected */
- filelist_entry_select_index_set(
- sfile->files, 0, FILE_SEL_REMOVE, FILE_SEL_SELECTED, CHECK_ALL);
+ filelist_entry_parent_select_set(sfile->files, FILE_SEL_REMOVE, FILE_SEL_SELECTED, CHECK_ALL);
}
if (FILE_SELECT_DIR == ret) {
@@ -580,6 +583,11 @@ static bool file_walk_select_selection_set(bContext *C,
BLI_assert(params);
+ if (numfiles == 0) {
+ /* No files visible, nothing to do. */
+ return false;
+ }
+
if (has_selection) {
if (extend && filelist_entry_select_index_get(files, active_old, CHECK_ALL) &&
filelist_entry_select_index_get(files, active_new, CHECK_ALL)) {
@@ -609,7 +617,7 @@ static bool file_walk_select_selection_set(bContext *C,
}
/* select first file */
else if (ELEM(direction, FILE_SELECT_WALK_DOWN, FILE_SELECT_WALK_RIGHT)) {
- params->active_file = active = extend ? 1 : 0;
+ params->active_file = active = 0;
}
else {
BLI_assert(0);
@@ -626,7 +634,7 @@ static bool file_walk_select_selection_set(bContext *C,
/* unselect '..' parent entry - it's not supposed to be selected if more
* than one file is selected */
- filelist_entry_select_index_set(files, 0, FILE_SEL_REMOVE, FILE_SEL_SELECTED, CHECK_ALL);
+ filelist_entry_parent_select_set(files, FILE_SEL_REMOVE, FILE_SEL_SELECTED, CHECK_ALL);
}
else {
/* deselect all first */
@@ -641,11 +649,6 @@ static bool file_walk_select_selection_set(bContext *C,
if (fill) {
FileSelection sel = {MIN2(active, last_sel), MAX2(active, last_sel)};
- /* clamping selection to not include '..' parent entry */
- if (sel.first == 0) {
- sel.first = 1;
- }
-
/* fill selection between last and first selected file */
filelist_entries_select_index_range_set(
files, &sel, deselect ? FILE_SEL_REMOVE : FILE_SEL_ADD, FILE_SEL_SELECTED, CHECK_ALL);
@@ -653,6 +656,12 @@ static bool file_walk_select_selection_set(bContext *C,
if (deselect) {
filelist_entry_select_index_set(files, active, FILE_SEL_ADD, FILE_SEL_SELECTED, CHECK_ALL);
}
+
+ /* unselect '..' parent entry - it's not supposed to be selected if more
+ * than one file is selected */
+ if ((sel.last - sel.first) > 1) {
+ filelist_entry_parent_select_set(files, FILE_SEL_REMOVE, FILE_SEL_SELECTED, CHECK_ALL);
+ }
}
else {
filelist_entry_select_index_set(
@@ -688,10 +697,15 @@ static bool file_walk_select_do(bContext *C,
/* *** get all needed files for handling selection *** */
+ if (numfiles == 0) {
+ /* No files visible, nothing to do. */
+ return false;
+ }
+
if (has_selection) {
ARegion *ar = CTX_wm_region(C);
FileLayout *layout = ED_fileselect_get_layout(sfile, ar);
- const int idx_shift = (layout->flag & FILE_LAYOUT_HOR) ? layout->rows : layout->columns;
+ const int idx_shift = (layout->flag & FILE_LAYOUT_HOR) ? layout->rows : layout->flow_columns;
if ((layout->flag & FILE_LAYOUT_HOR && direction == FILE_SELECT_WALK_UP) ||
(layout->flag & FILE_LAYOUT_VER && direction == FILE_SELECT_WALK_LEFT)) {
@@ -718,7 +732,7 @@ static bool file_walk_select_do(bContext *C,
BLI_assert(0);
}
- if (!IN_RANGE(active_new, 0, numfiles)) {
+ if (!IN_RANGE(active_new, -1, numfiles)) {
if (extend) {
/* extend to invalid file -> abort */
return false;
@@ -1185,7 +1199,7 @@ int file_highlight_set(SpaceFile *sfile, ARegion *ar, int mx, int my)
mx -= ar->winrct.xmin;
my -= ar->winrct.ymin;
- if (BLI_rcti_isect_pt(&ar->v2d.mask, mx, my)) {
+ if (ED_fileselect_layout_is_inside_pt(sfile->layout, v2d, mx, my)) {
float fx, fy;
int highlight_file;
@@ -1234,6 +1248,53 @@ void FILE_OT_highlight(struct wmOperatorType *ot)
ot->poll = ED_operator_file_active;
}
+static int file_column_sort_ui_context_invoke(bContext *C,
+ wmOperator *UNUSED(op),
+ const wmEvent *event)
+{
+ const ARegion *ar = CTX_wm_region(C);
+ SpaceFile *sfile = CTX_wm_space_file(C);
+
+ if (file_attribute_column_header_is_inside(
+ &ar->v2d, sfile->layout, event->mval[0], event->mval[1])) {
+ const FileAttributeColumnType column_type = file_attribute_column_type_find_isect(
+ &ar->v2d, sfile->params, sfile->layout, event->mval[0]);
+
+ if (column_type != COLUMN_NONE) {
+ const FileAttributeColumn *column = &sfile->layout->attribute_columns[column_type];
+
+ if (column->sort_type != FILE_SORT_NONE) {
+ if (sfile->params->sort == column->sort_type) {
+ /* Already sorting by selected column -> toggle sort invert (three state logic). */
+ sfile->params->flag ^= FILE_SORT_INVERT;
+ }
+ else {
+ sfile->params->sort = column->sort_type;
+ sfile->params->flag &= ~FILE_SORT_INVERT;
+ }
+
+ WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL);
+ }
+ }
+ }
+
+ return OPERATOR_PASS_THROUGH;
+}
+
+void FILE_OT_sort_column_ui_context(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Sort from Column";
+ ot->description = "Change sorting to use column under cursor";
+ ot->idname = "FILE_OT_sort_column_ui_context";
+
+ /* api callbacks */
+ ot->invoke = file_column_sort_ui_context_invoke;
+ ot->poll = ED_operator_file_active;
+
+ ot->flag = OPTYPE_INTERNAL;
+}
+
int file_cancel_exec(bContext *C, wmOperator *UNUSED(unused))
{
wmWindowManager *wm = CTX_wm_manager(C);
@@ -1713,7 +1774,7 @@ static int file_smoothscroll_invoke(bContext *C, wmOperator *UNUSED(op), const w
/* Number of items in a block (i.e. lines in a column in horizontal layout, or columns in a line
* in vertical layout).
*/
- const int items_block_size = is_horizontal ? sfile->layout->rows : sfile->layout->columns;
+ const int items_block_size = is_horizontal ? sfile->layout->rows : sfile->layout->flow_columns;
/* Scroll offset is the first file in the row/column we are editing in. */
if (sfile->scroll_offset == 0) {
@@ -1998,7 +2059,6 @@ void FILE_OT_directory_new(struct wmOperatorType *ot)
ot->idname = "FILE_OT_directory_new";
/* api callbacks */
- ot->invoke = WM_operator_confirm;
ot->exec = file_directory_new_exec;
ot->poll = ED_operator_file_active; /* <- important, handler is on window level */
@@ -2260,10 +2320,29 @@ ARegion *file_tools_region(ScrArea *sa)
arnew->regiontype = RGN_TYPE_TOOLS;
arnew->alignment = RGN_ALIGN_LEFT;
- ar = MEM_callocN(sizeof(ARegion), "tool props for file");
- BLI_insertlinkafter(&sa->regionbase, arnew, ar);
- ar->regiontype = RGN_TYPE_TOOL_PROPS;
- ar->alignment = RGN_ALIGN_BOTTOM | RGN_SPLIT_PREV;
+ return arnew;
+}
+
+ARegion *file_tool_props_region(ScrArea *sa)
+{
+ ARegion *ar, *arnew;
+
+ if ((ar = BKE_area_find_region_type(sa, RGN_TYPE_TOOL_PROPS)) != NULL) {
+ return ar;
+ }
+
+ /* add subdiv level; after execute region */
+ ar = BKE_area_find_region_type(sa, RGN_TYPE_EXECUTE);
+
+ /* is error! */
+ if (ar == NULL) {
+ return NULL;
+ }
+
+ arnew = MEM_callocN(sizeof(ARegion), "tool props for file");
+ BLI_insertlinkafter(&sa->regionbase, ar, arnew);
+ arnew->regiontype = RGN_TYPE_TOOL_PROPS;
+ arnew->alignment = RGN_ALIGN_RIGHT;
return arnew;
}
@@ -2292,6 +2371,17 @@ void FILE_OT_bookmark_toggle(struct wmOperatorType *ot)
ot->poll = ED_operator_file_active; /* <- important, handler is on window level */
}
+static bool file_filenum_poll(bContext *C)
+{
+ SpaceFile *sfile = CTX_wm_space_file(C);
+
+ if (!ED_operator_file_active(C)) {
+ return false;
+ }
+
+ return sfile->params && (sfile->params->action_type == FILE_SAVE);
+}
+
/**
* Looks for a string of digits within name (using BLI_stringdec) and adjusts it by add.
*/
@@ -2349,7 +2439,7 @@ void FILE_OT_filenum(struct wmOperatorType *ot)
/* api callbacks */
ot->exec = file_filenum_exec;
- ot->poll = ED_operator_file_active; /* <- important, handler is on window level */
+ ot->poll = file_filenum_poll;
/* props */
RNA_def_int(ot->srna, "increment", 1, -100, 100, "Increment", "", -100, 100);
diff --git a/source/blender/editors/space_file/file_panels.c b/source/blender/editors/space_file/file_panels.c
index d9a6e70121f..b41358f575f 100644
--- a/source/blender/editors/space_file/file_panels.c
+++ b/source/blender/editors/space_file/file_panels.c
@@ -103,6 +103,7 @@ void file_panels_register(ARegionType *art)
strcpy(pt->idname, "FILE_PT_operator");
strcpy(pt->label, N_("Operator"));
strcpy(pt->translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA);
+ pt->flag = PNL_NO_HEADER;
pt->poll = file_panel_operator_poll;
pt->draw_header = file_panel_operator_header;
pt->draw = file_panel_operator;
diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c
index f7dda1defe8..46302e0d087 100644
--- a/source/blender/editors/space_file/filelist.c
+++ b/source/blender/editors/space_file/filelist.c
@@ -329,25 +329,20 @@ enum {
FL_IS_PENDING = 1 << 2,
FL_NEED_SORTING = 1 << 3,
FL_NEED_FILTERING = 1 << 4,
+ FL_SORT_INVERT = 1 << 5,
};
-#define SPECIAL_IMG_SIZE 48
-#define SPECIAL_IMG_ROWS 4
-#define SPECIAL_IMG_COLS 4
+#define SPECIAL_IMG_SIZE 256
+#define SPECIAL_IMG_ROWS 1
+#define SPECIAL_IMG_COLS 6
enum {
- SPECIAL_IMG_FOLDER = 0,
- SPECIAL_IMG_PARENT = 1,
- SPECIAL_IMG_REFRESH = 2,
- SPECIAL_IMG_BLENDFILE = 3,
- SPECIAL_IMG_SOUNDFILE = 4,
- SPECIAL_IMG_MOVIEFILE = 5,
- SPECIAL_IMG_PYTHONFILE = 6,
- SPECIAL_IMG_TEXTFILE = 7,
- SPECIAL_IMG_FONTFILE = 8,
- SPECIAL_IMG_UNKNOWNFILE = 9,
- SPECIAL_IMG_LOADING = 10,
- SPECIAL_IMG_BACKUP = 11,
+ SPECIAL_IMG_DOCUMENT = 0,
+ SPECIAL_IMG_FOLDER = 1,
+ SPECIAL_IMG_PARENT = 2,
+ SPECIAL_IMG_DRIVE_FIXED = 3,
+ SPECIAL_IMG_DRIVE_ATTACHED = 4,
+ SPECIAL_IMG_DRIVE_REMOTE = 5,
SPECIAL_IMG_MAX,
};
@@ -369,6 +364,19 @@ static void filelist_cache_clear(FileListEntryCache *cache, size_t new_size);
/* ********** Sort helpers ********** */
+struct FileSortData {
+ bool inverted;
+};
+
+static int compare_apply_inverted(int val, const struct FileSortData *sort_data)
+{
+ return sort_data->inverted ? -val : val;
+}
+
+/**
+ * Handles inverted sorting itself (currently there's nothing to invert), so if this returns non-0,
+ * it should be used as-is and not inverted.
+ */
static int compare_direntry_generic(const FileListInternEntry *entry1,
const FileListInternEntry *entry2)
{
@@ -420,10 +428,11 @@ static int compare_direntry_generic(const FileListInternEntry *entry1,
return 0;
}
-static int compare_name(void *UNUSED(user_data), const void *a1, const void *a2)
+static int compare_name(void *user_data, const void *a1, const void *a2)
{
const FileListInternEntry *entry1 = a1;
const FileListInternEntry *entry2 = a2;
+ const struct FileSortData *sort_data = user_data;
char *name1, *name2;
int ret;
@@ -434,13 +443,14 @@ static int compare_name(void *UNUSED(user_data), const void *a1, const void *a2)
name1 = entry1->name;
name2 = entry2->name;
- return BLI_natstrcmp(name1, name2);
+ return compare_apply_inverted(BLI_strcasecmp_natural(name1, name2), sort_data);
}
-static int compare_date(void *UNUSED(user_data), const void *a1, const void *a2)
+static int compare_date(void *user_data, const void *a1, const void *a2)
{
const FileListInternEntry *entry1 = a1;
const FileListInternEntry *entry2 = a2;
+ const struct FileSortData *sort_data = user_data;
char *name1, *name2;
int64_t time1, time2;
int ret;
@@ -452,22 +462,23 @@ static int compare_date(void *UNUSED(user_data), const void *a1, const void *a2)
time1 = (int64_t)entry1->st.st_mtime;
time2 = (int64_t)entry2->st.st_mtime;
if (time1 < time2) {
- return 1;
+ return compare_apply_inverted(1, sort_data);
}
if (time1 > time2) {
- return -1;
+ return compare_apply_inverted(-1, sort_data);
}
name1 = entry1->name;
name2 = entry2->name;
- return BLI_natstrcmp(name1, name2);
+ return compare_apply_inverted(BLI_strcasecmp_natural(name1, name2), sort_data);
}
-static int compare_size(void *UNUSED(user_data), const void *a1, const void *a2)
+static int compare_size(void *user_data, const void *a1, const void *a2)
{
const FileListInternEntry *entry1 = a1;
const FileListInternEntry *entry2 = a2;
+ const struct FileSortData *sort_data = user_data;
char *name1, *name2;
uint64_t size1, size2;
int ret;
@@ -479,22 +490,23 @@ static int compare_size(void *UNUSED(user_data), const void *a1, const void *a2)
size1 = entry1->st.st_size;
size2 = entry2->st.st_size;
if (size1 < size2) {
- return 1;
+ return compare_apply_inverted(1, sort_data);
}
if (size1 > size2) {
- return -1;
+ return compare_apply_inverted(-1, sort_data);
}
name1 = entry1->name;
name2 = entry2->name;
- return BLI_natstrcmp(name1, name2);
+ return compare_apply_inverted(BLI_strcasecmp_natural(name1, name2), sort_data);
}
-static int compare_extension(void *UNUSED(user_data), const void *a1, const void *a2)
+static int compare_extension(void *user_data, const void *a1, const void *a2)
{
const FileListInternEntry *entry1 = a1;
const FileListInternEntry *entry2 = a2;
+ const struct FileSortData *sort_data = user_data;
char *name1, *name2;
int ret;
@@ -516,10 +528,10 @@ static int compare_extension(void *UNUSED(user_data), const void *a1, const void
return -1;
}
if (entry1->blentype < entry2->blentype) {
- return -1;
+ return compare_apply_inverted(-1, sort_data);
}
if (entry1->blentype > entry2->blentype) {
- return 1;
+ return compare_apply_inverted(1, sort_data);
}
}
else {
@@ -539,48 +551,58 @@ static int compare_extension(void *UNUSED(user_data), const void *a1, const void
}
if ((ret = BLI_strcasecmp(sufix1, sufix2))) {
- return ret;
+ return compare_apply_inverted(ret, sort_data);
}
}
name1 = entry1->name;
name2 = entry2->name;
- return BLI_natstrcmp(name1, name2);
+ return compare_apply_inverted(BLI_strcasecmp_natural(name1, name2), sort_data);
}
void filelist_sort(struct FileList *filelist)
{
if ((filelist->flags & FL_NEED_SORTING) && (filelist->sort != FILE_SORT_NONE)) {
+ void *sort_cb = NULL;
+
switch (filelist->sort) {
case FILE_SORT_ALPHA:
- BLI_listbase_sort_r(&filelist->filelist_intern.entries, compare_name, NULL);
+ sort_cb = compare_name;
break;
case FILE_SORT_TIME:
- BLI_listbase_sort_r(&filelist->filelist_intern.entries, compare_date, NULL);
+ sort_cb = compare_date;
break;
case FILE_SORT_SIZE:
- BLI_listbase_sort_r(&filelist->filelist_intern.entries, compare_size, NULL);
+ sort_cb = compare_size;
break;
case FILE_SORT_EXTENSION:
- BLI_listbase_sort_r(&filelist->filelist_intern.entries, compare_extension, NULL);
+ sort_cb = compare_extension;
break;
case FILE_SORT_NONE: /* Should never reach this point! */
default:
BLI_assert(0);
break;
}
+ BLI_listbase_sort_r(
+ &filelist->filelist_intern.entries,
+ sort_cb,
+ &(struct FileSortData){.inverted = (filelist->flags & FL_SORT_INVERT) != 0});
filelist_filter_clear(filelist);
filelist->flags &= ~FL_NEED_SORTING;
}
}
-void filelist_setsorting(struct FileList *filelist, const short sort)
+void filelist_setsorting(struct FileList *filelist, const short sort, bool invert_sort)
{
- if (filelist->sort != sort) {
+ const bool was_invert_sort = filelist->flags & FL_SORT_INVERT;
+
+ if ((filelist->sort != sort) || (was_invert_sort != invert_sort)) {
filelist->sort = sort;
filelist->flags |= FL_NEED_SORTING;
+ filelist->flags = invert_sort ? (filelist->flags | FL_SORT_INVERT) :
+ (filelist->flags & ~FL_SORT_INVERT);
}
}
@@ -635,9 +657,9 @@ static bool is_filtered_file(FileListInternEntry *file,
{
bool is_filtered = !is_hidden_file(file->relpath, filter);
- if (is_filtered && (filter->flags & FLF_DO_FILTER) && !FILENAME_IS_CURRPAR(file->relpath)) {
+ if (is_filtered && !FILENAME_IS_CURRPAR(file->relpath)) {
/* We only check for types if some type are enabled in filtering. */
- if (filter->filter) {
+ if (filter->filter && (filter->flags & FLF_DO_FILTER)) {
if (file->typeflag & FILE_TYPE_DIR) {
if (file->typeflag &
(FILE_TYPE_BLENDERLIB | FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)) {
@@ -657,6 +679,7 @@ static bool is_filtered_file(FileListInternEntry *file,
}
}
}
+ /* If there's a filter string, apply it as filter even if FLF_DO_FILTER is not set. */
if (is_filtered && (filter->filter_search[0] != '\0')) {
if (fnmatch(filter->filter_search, file->relpath, FNM_CASEFOLD) != 0) {
is_filtered = false;
@@ -676,9 +699,9 @@ static bool is_filtered_lib(FileListInternEntry *file, const char *root, FileLis
if (BLO_library_path_explode(path, dir, &group, &name)) {
is_filtered = !is_hidden_file(file->relpath, filter);
- if (is_filtered && (filter->flags & FLF_DO_FILTER) && !FILENAME_IS_CURRPAR(file->relpath)) {
+ if (is_filtered && !FILENAME_IS_CURRPAR(file->relpath)) {
/* We only check for types if some type are enabled in filtering. */
- if (filter->filter || filter->filter_id) {
+ if ((filter->filter || filter->filter_id) && (filter->flags & FLF_DO_FILTER)) {
if (file->typeflag & FILE_TYPE_DIR) {
if (file->typeflag &
(FILE_TYPE_BLENDERLIB | FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)) {
@@ -704,6 +727,7 @@ static bool is_filtered_lib(FileListInternEntry *file, const char *root, FileLis
}
}
}
+ /* If there's a filter string, apply it as filter even if FLF_DO_FILTER is not set. */
if (is_filtered && (filter->filter_search[0] != '\0')) {
if (fnmatch(filter->filter_search, file->relpath, FNM_CASEFOLD) != 0) {
is_filtered = false;
@@ -904,42 +928,12 @@ static ImBuf *filelist_geticon_image_ex(const unsigned int typeflag, const char
if (FILENAME_IS_PARENT(relpath)) {
ibuf = gSpecialFileImages[SPECIAL_IMG_PARENT];
}
- else if (FILENAME_IS_CURRENT(relpath)) {
- ibuf = gSpecialFileImages[SPECIAL_IMG_REFRESH];
- }
else {
ibuf = gSpecialFileImages[SPECIAL_IMG_FOLDER];
}
}
- else if (typeflag & FILE_TYPE_BLENDER) {
- ibuf = gSpecialFileImages[SPECIAL_IMG_BLENDFILE];
- }
- else if (typeflag & FILE_TYPE_BLENDERLIB) {
- ibuf = gSpecialFileImages[SPECIAL_IMG_UNKNOWNFILE];
- }
- else if (typeflag & (FILE_TYPE_MOVIE)) {
- ibuf = gSpecialFileImages[SPECIAL_IMG_MOVIEFILE];
- }
- else if (typeflag & FILE_TYPE_SOUND) {
- ibuf = gSpecialFileImages[SPECIAL_IMG_SOUNDFILE];
- }
- else if (typeflag & FILE_TYPE_PYSCRIPT) {
- ibuf = gSpecialFileImages[SPECIAL_IMG_PYTHONFILE];
- }
- else if (typeflag & FILE_TYPE_FTFONT) {
- ibuf = gSpecialFileImages[SPECIAL_IMG_FONTFILE];
- }
- else if (typeflag & FILE_TYPE_TEXT) {
- ibuf = gSpecialFileImages[SPECIAL_IMG_TEXTFILE];
- }
- else if (typeflag & FILE_TYPE_IMAGE) {
- ibuf = gSpecialFileImages[SPECIAL_IMG_LOADING];
- }
- else if (typeflag & FILE_TYPE_BLENDER_BACKUP) {
- ibuf = gSpecialFileImages[SPECIAL_IMG_BACKUP];
- }
else {
- ibuf = gSpecialFileImages[SPECIAL_IMG_UNKNOWNFILE];
+ ibuf = gSpecialFileImages[SPECIAL_IMG_DOCUMENT];
}
return ibuf;
@@ -1001,10 +995,13 @@ static int filelist_geticon_ex(const int typeflag,
return ICON_FILE_BLANK;
}
else if (typeflag & FILE_TYPE_COLLADA) {
- return ICON_FILE_BLANK;
+ return ICON_FILE_3D;
}
else if (typeflag & FILE_TYPE_ALEMBIC) {
- return ICON_FILE_BLANK;
+ return ICON_FILE_3D;
+ }
+ else if (typeflag & FILE_TYPE_OBJECT_IO) {
+ return ICON_FILE_3D;
}
else if (typeflag & FILE_TYPE_TEXT) {
return ICON_FILE_TEXT;
@@ -1243,7 +1240,8 @@ static void filelist_cache_previews_clear(FileListEntryCache *cache)
BLI_task_pool_cancel(cache->previews_pool);
while ((preview = BLI_thread_queue_pop_timeout(cache->previews_done, 0))) {
- // printf("%s: DONE %d - %s - %p\n", __func__, preview->index, preview->path, preview->img);
+ // printf("%s: DONE %d - %s - %p\n", __func__, preview->index, preview->path,
+ // preview->img);
if (preview->img) {
IMB_freeImBuf(preview->img);
}
@@ -2128,6 +2126,9 @@ int ED_path_extension_type(const char *path)
else if (BLI_path_extension_check(path, ".abc")) {
return FILE_TYPE_ALEMBIC;
}
+ else if (BLI_path_extension_check_n(path, ".obj", ".3ds", ".fbx", ".glb", ".gltf", NULL)) {
+ return FILE_TYPE_OBJECT_IO;
+ }
else if (BLI_path_extension_check_array(path, imb_ext_image)) {
return FILE_TYPE_IMAGE;
}
@@ -2177,9 +2178,9 @@ int ED_file_extension_icon(const char *path)
case FILE_TYPE_BTX:
return ICON_FILE_BLANK;
case FILE_TYPE_COLLADA:
- return ICON_FILE_BLANK;
case FILE_TYPE_ALEMBIC:
- return ICON_FILE_BLANK;
+ case FILE_TYPE_OBJECT_IO:
+ return ICON_FILE_3D;
case FILE_TYPE_TEXT:
return ICON_FILE_TEXT;
default:
@@ -2298,6 +2299,19 @@ unsigned int filelist_entry_select_index_get(FileList *filelist,
return 0;
}
+/**
+ * Set selection of the '..' parent entry, but only if it's actually visible.
+ */
+void filelist_entry_parent_select_set(FileList *filelist,
+ FileSelType select,
+ unsigned int flag,
+ FileCheckType check)
+{
+ if ((filelist->filter_data.flags & FLF_HIDE_PARENT) == 0) {
+ filelist_entry_select_index_set(filelist, 0, select, flag, check);
+ }
+}
+
/* WARNING! dir must be FILE_MAX_LIBEXTRA long! */
bool filelist_islibrary(struct FileList *filelist, char *dir, char **group)
{
diff --git a/source/blender/editors/space_file/filelist.h b/source/blender/editors/space_file/filelist.h
index caf77246797..9b1107294ff 100644
--- a/source/blender/editors/space_file/filelist.h
+++ b/source/blender/editors/space_file/filelist.h
@@ -55,7 +55,7 @@ void folderlist_pushdir(struct ListBase *folderlist, const char *dir);
const char *folderlist_peeklastdir(struct ListBase *folderdist);
int folderlist_clear_next(struct SpaceFile *sfile);
-void filelist_setsorting(struct FileList *filelist, const short sort);
+void filelist_setsorting(struct FileList *filelist, const short sort, bool invert_sort);
void filelist_sort(struct FileList *filelist);
void filelist_setfilter_options(struct FileList *filelist,
@@ -117,6 +117,10 @@ unsigned int filelist_entry_select_get(struct FileList *filelist,
unsigned int filelist_entry_select_index_get(struct FileList *filelist,
const int index,
FileCheckType check);
+void filelist_entry_parent_select_set(struct FileList *filelist,
+ FileSelType select,
+ unsigned int flag,
+ FileCheckType check);
void filelist_setrecursion(struct FileList *filelist, const int recursion_level);
diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c
index db42d007b8e..3223fe0c6ce 100644
--- a/source/blender/editors/space_file/filesel.c
+++ b/source/blender/editors/space_file/filesel.c
@@ -49,6 +49,8 @@
#include "BLI_utildefines.h"
#include "BLI_fnmatch.h"
+#include "BLT_translation.h"
+
#include "BKE_appdir.h"
#include "BKE_context.h"
#include "BKE_main.h"
@@ -69,6 +71,8 @@
#include "file_intern.h"
#include "filelist.h"
+#define VERTLIST_MAJORCOLUMN_WIDTH (25 * UI_UNIT_X)
+
FileSelectParams *ED_fileselect_get_params(struct SpaceFile *sfile)
{
if (!sfile->params) {
@@ -99,6 +103,8 @@ short ED_fileselect_set_params(SpaceFile *sfile)
sfile->params->filter_glob[0] = '\0';
/* set the default thumbnails size */
sfile->params->thumbnail_size = 128;
+ /* Show size column by default. */
+ sfile->params->details_flags = FILE_DETAILS_SIZE | FILE_DETAILS_DATETIME;
}
params = sfile->params;
@@ -161,6 +167,10 @@ short ED_fileselect_set_params(SpaceFile *sfile)
params->flag &= ~FILE_DIRSEL_ONLY;
}
+ if ((prop = RNA_struct_find_property(op->ptr, "hide_props_region"))) {
+ params->flag |= RNA_property_boolean_get(op->ptr, prop) ? FILE_HIDE_TOOL_PROPS : 0;
+ }
+
params->filter = 0;
if ((prop = RNA_struct_find_property(op->ptr, "filter_blender"))) {
params->filter |= RNA_property_boolean_get(op->ptr, prop) ? FILE_TYPE_BLENDER : 0;
@@ -261,6 +271,10 @@ short ED_fileselect_set_params(SpaceFile *sfile)
params->sort = FILE_SORT_ALPHA;
}
+ if ((prop = RNA_struct_find_property(op->ptr, "action_type"))) {
+ params->action_type = RNA_property_enum_get(op->ptr, prop);
+ }
+
if (params->display == FILE_DEFAULTDISPLAY) {
if (params->display_previous == FILE_DEFAULTDISPLAY) {
if (U.uiflag & USER_SHOW_THUMBNAILS) {
@@ -268,11 +282,11 @@ short ED_fileselect_set_params(SpaceFile *sfile)
params->display = FILE_IMGDISPLAY;
}
else {
- params->display = FILE_SHORTDISPLAY;
+ params->display = FILE_VERTICALDISPLAY;
}
}
else {
- params->display = FILE_SHORTDISPLAY;
+ params->display = FILE_VERTICALDISPLAY;
}
}
else {
@@ -293,7 +307,7 @@ short ED_fileselect_set_params(SpaceFile *sfile)
params->type = FILE_UNIX;
params->flag |= FILE_HIDE_DOT;
params->flag &= ~FILE_DIRSEL_ONLY;
- params->display = FILE_SHORTDISPLAY;
+ params->display = FILE_VERTICALDISPLAY;
params->display_previous = FILE_DEFAULTDISPLAY;
params->sort = FILE_SORT_ALPHA;
params->filter = 0;
@@ -344,7 +358,7 @@ void ED_fileselect_reset_params(SpaceFile *sfile)
void fileselect_file_set(SpaceFile *sfile, const int index)
{
const struct FileDirEntry *file = filelist_file(sfile->files, index);
- if (file && file->relpath && file->relpath[0] && !(file->typeflag & FILE_TYPE_FOLDER)) {
+ if (file && file->relpath && file->relpath[0] && !(file->typeflag & FILE_TYPE_DIR)) {
BLI_strncpy(sfile->params->file, file->relpath, FILE_MAXFILE);
}
}
@@ -372,10 +386,10 @@ int ED_fileselect_layout_numfiles(FileLayout *layout, ARegion *ar)
}
else {
const int y_item = layout->tile_h + (2 * layout->tile_border_y);
- const int y_view = (int)(BLI_rctf_size_y(&ar->v2d.cur));
+ const int y_view = (int)(BLI_rctf_size_y(&ar->v2d.cur)) - layout->offset_top;
const int y_over = y_item - (y_view % y_item);
numfiles = (int)((float)(y_view + y_over) / (float)(y_item));
- return numfiles * layout->columns;
+ return numfiles * layout->flow_columns;
}
}
@@ -395,19 +409,19 @@ FileSelection ED_fileselect_layout_offset_rect(FileLayout *layout, const rcti *r
}
colmin = (rect->xmin) / (layout->tile_w + 2 * layout->tile_border_x);
- rowmin = (rect->ymin) / (layout->tile_h + 2 * layout->tile_border_y);
+ rowmin = (rect->ymin - layout->offset_top) / (layout->tile_h + 2 * layout->tile_border_y);
colmax = (rect->xmax) / (layout->tile_w + 2 * layout->tile_border_x);
- rowmax = (rect->ymax) / (layout->tile_h + 2 * layout->tile_border_y);
+ rowmax = (rect->ymax - layout->offset_top) / (layout->tile_h + 2 * layout->tile_border_y);
- if (is_inside(colmin, rowmin, layout->columns, layout->rows) ||
- is_inside(colmax, rowmax, layout->columns, layout->rows)) {
- CLAMP(colmin, 0, layout->columns - 1);
+ if (is_inside(colmin, rowmin, layout->flow_columns, layout->rows) ||
+ is_inside(colmax, rowmax, layout->flow_columns, layout->rows)) {
+ CLAMP(colmin, 0, layout->flow_columns - 1);
CLAMP(rowmin, 0, layout->rows - 1);
- CLAMP(colmax, 0, layout->columns - 1);
+ CLAMP(colmax, 0, layout->flow_columns - 1);
CLAMP(rowmax, 0, layout->rows - 1);
}
- if ((colmin > layout->columns - 1) || (rowmin > layout->rows - 1)) {
+ if ((colmin > layout->flow_columns - 1) || (rowmin > layout->rows - 1)) {
sel.first = -1;
}
else {
@@ -415,10 +429,10 @@ FileSelection ED_fileselect_layout_offset_rect(FileLayout *layout, const rcti *r
sel.first = layout->rows * colmin + rowmin;
}
else {
- sel.first = colmin + layout->columns * rowmin;
+ sel.first = colmin + layout->flow_columns * rowmin;
}
}
- if ((colmax > layout->columns - 1) || (rowmax > layout->rows - 1)) {
+ if ((colmax > layout->flow_columns - 1) || (rowmax > layout->rows - 1)) {
sel.last = -1;
}
else {
@@ -426,7 +440,7 @@ FileSelection ED_fileselect_layout_offset_rect(FileLayout *layout, const rcti *r
sel.last = layout->rows * colmax + rowmax;
}
else {
- sel.last = colmax + layout->columns * rowmax;
+ sel.last = colmax + layout->flow_columns * rowmax;
}
}
@@ -443,9 +457,9 @@ int ED_fileselect_layout_offset(FileLayout *layout, int x, int y)
}
offsetx = (x) / (layout->tile_w + 2 * layout->tile_border_x);
- offsety = (y) / (layout->tile_h + 2 * layout->tile_border_y);
+ offsety = (y - layout->offset_top) / (layout->tile_h + 2 * layout->tile_border_y);
- if (offsetx > layout->columns - 1) {
+ if (offsetx > layout->flow_columns - 1) {
return -1;
}
if (offsety > layout->rows - 1) {
@@ -456,25 +470,121 @@ int ED_fileselect_layout_offset(FileLayout *layout, int x, int y)
active_file = layout->rows * offsetx + offsety;
}
else {
- active_file = offsetx + layout->columns * offsety;
+ active_file = offsetx + layout->flow_columns * offsety;
}
return active_file;
}
+/**
+ * Get the currently visible bounds of the layout in screen space. Matches View2D.mask minus the
+ * top column-header row.
+ */
+void ED_fileselect_layout_maskrect(const FileLayout *layout, const View2D *v2d, rcti *r_rect)
+{
+ *r_rect = v2d->mask;
+ r_rect->ymax -= layout->offset_top;
+}
+
+bool ED_fileselect_layout_is_inside_pt(const FileLayout *layout, const View2D *v2d, int x, int y)
+{
+ rcti maskrect;
+ ED_fileselect_layout_maskrect(layout, v2d, &maskrect);
+ return BLI_rcti_isect_pt(&maskrect, x, y);
+}
+
+bool ED_fileselect_layout_isect_rect(const FileLayout *layout,
+ const View2D *v2d,
+ const rcti *rect,
+ rcti *r_dst)
+{
+ rcti maskrect;
+ ED_fileselect_layout_maskrect(layout, v2d, &maskrect);
+ return BLI_rcti_isect(&maskrect, rect, r_dst);
+}
+
void ED_fileselect_layout_tilepos(FileLayout *layout, int tile, int *x, int *y)
{
if (layout->flag == FILE_LAYOUT_HOR) {
*x = layout->tile_border_x +
(tile / layout->rows) * (layout->tile_w + 2 * layout->tile_border_x);
- *y = layout->tile_border_y +
+ *y = layout->offset_top + layout->tile_border_y +
(tile % layout->rows) * (layout->tile_h + 2 * layout->tile_border_y);
}
else {
*x = layout->tile_border_x +
- ((tile) % layout->columns) * (layout->tile_w + 2 * layout->tile_border_x);
- *y = layout->tile_border_y +
- ((tile) / layout->columns) * (layout->tile_h + 2 * layout->tile_border_y);
+ ((tile) % layout->flow_columns) * (layout->tile_w + 2 * layout->tile_border_x);
+ *y = layout->offset_top + layout->tile_border_y +
+ ((tile) / layout->flow_columns) * (layout->tile_h + 2 * layout->tile_border_y);
+ }
+}
+
+/**
+ * Check if the region coordinate defined by \a x and \a y are inside the column header.
+ */
+bool file_attribute_column_header_is_inside(const View2D *v2d,
+ const FileLayout *layout,
+ int x,
+ int y)
+{
+ rcti header_rect = v2d->mask;
+ header_rect.ymin = header_rect.ymax - layout->attribute_column_header_h;
+ return BLI_rcti_isect_pt(&header_rect, x, y);
+}
+
+bool file_attribute_column_type_enabled(const FileSelectParams *params,
+ FileAttributeColumnType column)
+{
+ switch (column) {
+ case COLUMN_NAME:
+ /* Always enabled */
+ return true;
+ case COLUMN_DATETIME:
+ return (params->details_flags & FILE_DETAILS_DATETIME) != 0;
+ case COLUMN_SIZE:
+ return (params->details_flags & FILE_DETAILS_SIZE) != 0;
+ default:
+ return false;
+ }
+}
+
+/**
+ * Find the column type at region coordinate given by \a x (y doesn't matter for this).
+ */
+FileAttributeColumnType file_attribute_column_type_find_isect(const View2D *v2d,
+ const FileSelectParams *params,
+ FileLayout *layout,
+ int x)
+{
+ float mx, my;
+ int offset_tile;
+
+ UI_view2d_region_to_view(v2d, x, v2d->mask.ymax - layout->offset_top - 1, &mx, &my);
+ offset_tile = ED_fileselect_layout_offset(
+ layout, (int)(v2d->tot.xmin + mx), (int)(v2d->tot.ymax - my));
+ if (offset_tile > -1) {
+ int tile_x, tile_y;
+ int pos_x = 0;
+ int rel_x; /* x relative to the hovered tile */
+
+ ED_fileselect_layout_tilepos(layout, offset_tile, &tile_x, &tile_y);
+ /* Column header drawing doesn't use left tile border, so subtract it. */
+ rel_x = mx - (tile_x - layout->tile_border_x);
+
+ for (FileAttributeColumnType column = 0; column < ATTRIBUTE_COLUMN_MAX; column++) {
+ if (!file_attribute_column_type_enabled(params, column)) {
+ continue;
+ }
+ const int width = layout->attribute_columns[column].width;
+
+ if (IN_RANGE(rel_x, pos_x, pos_x + width)) {
+ return column;
+ }
+
+ pos_x += width;
+ }
}
+
+ return COLUMN_NONE;
}
float file_string_width(const char *str)
@@ -512,20 +622,52 @@ float file_font_pointsize(void)
#endif
}
-static void column_widths(FileSelectParams *params, struct FileLayout *layout)
+static void file_attribute_columns_widths(const FileSelectParams *params, FileLayout *layout)
{
- int i;
+ FileAttributeColumn *columns = layout->attribute_columns;
const bool small_size = SMALL_SIZE_CHECK(params->thumbnail_size);
+ const int pad = small_size ? 0 : ATTRIBUTE_COLUMN_PADDING * 2;
- for (i = 0; i < MAX_FILE_COLUMN; ++i) {
- layout->column_widths[i] = 0;
+ for (int i = 0; i < ATTRIBUTE_COLUMN_MAX; ++i) {
+ layout->attribute_columns[i].width = 0;
}
- layout->column_widths[COLUMN_NAME] = ((float)params->thumbnail_size / 8.0f) * UI_UNIT_X;
/* Biggest possible reasonable values... */
- layout->column_widths[COLUMN_DATE] = file_string_width(small_size ? "23/08/89" : "23-Dec-89");
- layout->column_widths[COLUMN_TIME] = file_string_width("23:59");
- layout->column_widths[COLUMN_SIZE] = file_string_width(small_size ? "98.7 M" : "98.7 MiB");
+ columns[COLUMN_DATETIME].width = file_string_width(small_size ? "23/08/89" :
+ "23 Dec 6789, 23:59") +
+ pad;
+ columns[COLUMN_SIZE].width = file_string_width(small_size ? "98.7 M" : "098.7 MB") + pad;
+ if (params->display == FILE_IMGDISPLAY) {
+ columns[COLUMN_NAME].width = ((float)params->thumbnail_size / 8.0f) * UI_UNIT_X;
+ }
+ /* Name column uses remaining width */
+ else {
+ int remwidth = layout->tile_w;
+ for (FileAttributeColumnType column_type = ATTRIBUTE_COLUMN_MAX - 1; column_type >= 0;
+ column_type--) {
+ if ((column_type == COLUMN_NAME) ||
+ !file_attribute_column_type_enabled(params, column_type)) {
+ continue;
+ }
+ remwidth -= columns[column_type].width;
+ }
+ columns[COLUMN_NAME].width = remwidth;
+ }
+}
+
+static void file_attribute_columns_init(const FileSelectParams *params, FileLayout *layout)
+{
+ file_attribute_columns_widths(params, layout);
+
+ layout->attribute_columns[COLUMN_NAME].name = N_("Name");
+ layout->attribute_columns[COLUMN_NAME].sort_type = FILE_SORT_ALPHA;
+ layout->attribute_columns[COLUMN_NAME].text_align = UI_STYLE_TEXT_LEFT;
+ layout->attribute_columns[COLUMN_DATETIME].name = N_("Date Modified");
+ layout->attribute_columns[COLUMN_DATETIME].sort_type = FILE_SORT_TIME;
+ layout->attribute_columns[COLUMN_DATETIME].text_align = UI_STYLE_TEXT_LEFT;
+ layout->attribute_columns[COLUMN_SIZE].name = N_("Size");
+ layout->attribute_columns[COLUMN_SIZE].sort_type = FILE_SORT_SIZE;
+ layout->attribute_columns[COLUMN_SIZE].text_align = UI_STYLE_TEXT_RIGHT;
}
void ED_fileselect_init_layout(struct SpaceFile *sfile, ARegion *ar)
@@ -533,7 +675,6 @@ void ED_fileselect_init_layout(struct SpaceFile *sfile, ARegion *ar)
FileSelectParams *params = ED_fileselect_get_params(sfile);
FileLayout *layout = NULL;
View2D *v2d = &ar->v2d;
- int maxlen = 0;
int numfiles;
int textheight;
@@ -560,57 +701,66 @@ void ED_fileselect_init_layout(struct SpaceFile *sfile, ARegion *ar)
layout->tile_w = layout->prv_w + 2 * layout->prv_border_x;
layout->tile_h = layout->prv_h + 2 * layout->prv_border_y + textheight;
layout->width = (int)(BLI_rctf_size_x(&v2d->cur) - 2 * layout->tile_border_x);
- layout->columns = layout->width / (layout->tile_w + 2 * layout->tile_border_x);
- if (layout->columns > 0) {
- layout->rows = numfiles / layout->columns + 1; // XXX dirty, modulo is zero
+ layout->flow_columns = layout->width / (layout->tile_w + 2 * layout->tile_border_x);
+ layout->attribute_column_header_h = 0;
+ layout->offset_top = 0;
+ if (layout->flow_columns > 0) {
+ layout->rows = numfiles / layout->flow_columns + 1; // XXX dirty, modulo is zero
}
else {
- layout->columns = 1;
+ layout->flow_columns = 1;
layout->rows = numfiles + 1; // XXX dirty, modulo is zero
}
layout->height = sfile->layout->rows * (layout->tile_h + 2 * layout->tile_border_y) +
- layout->tile_border_y * 2;
+ layout->tile_border_y * 2 - layout->offset_top;
layout->flag = FILE_LAYOUT_VER;
}
- else {
- int column_space = 0.6f * UI_UNIT_X;
- int column_icon_space = 0.2f * UI_UNIT_X;
+ else if (params->display == FILE_VERTICALDISPLAY) {
+ int rowcount;
- layout->prv_w = 0;
- layout->prv_h = 0;
+ layout->prv_w = ((float)params->thumbnail_size / 20.0f) * UI_UNIT_X;
+ layout->prv_h = ((float)params->thumbnail_size / 20.0f) * UI_UNIT_Y;
+ layout->tile_border_x = 0.4f * UI_UNIT_X;
+ layout->tile_border_y = 0.1f * UI_UNIT_Y;
+ layout->tile_h = textheight * 3 / 2;
+ layout->width = (int)(BLI_rctf_size_x(&v2d->cur) - 2 * layout->tile_border_x);
+ layout->tile_w = layout->width;
+ layout->flow_columns = 1;
+ layout->attribute_column_header_h = layout->tile_h * 1.2f + 2 * layout->tile_border_y;
+ layout->offset_top = layout->attribute_column_header_h;
+ rowcount = (int)(BLI_rctf_size_y(&v2d->cur) - layout->offset_top - 2 * layout->tile_border_y) /
+ (layout->tile_h + 2 * layout->tile_border_y);
+ file_attribute_columns_init(params, layout);
+
+ layout->rows = MAX2(rowcount, numfiles);
+ BLI_assert(layout->rows != 0);
+ layout->height = sfile->layout->rows * (layout->tile_h + 2 * layout->tile_border_y) +
+ layout->tile_border_y * 2 + layout->offset_top;
+ layout->flag = FILE_LAYOUT_VER;
+ }
+ else if (params->display == FILE_HORIZONTALDISPLAY) {
+ layout->prv_w = ((float)params->thumbnail_size / 20.0f) * UI_UNIT_X;
+ layout->prv_h = ((float)params->thumbnail_size / 20.0f) * UI_UNIT_Y;
layout->tile_border_x = 0.4f * UI_UNIT_X;
layout->tile_border_y = 0.1f * UI_UNIT_Y;
- layout->prv_border_x = 0;
- layout->prv_border_y = 0;
layout->tile_h = textheight * 3 / 2;
+ layout->attribute_column_header_h = 0;
+ layout->offset_top = layout->attribute_column_header_h;
layout->height = (int)(BLI_rctf_size_y(&v2d->cur) - 2 * layout->tile_border_y);
/* Padding by full scrollbar H is too much, can overlap tile border Y. */
layout->rows = (layout->height - V2D_SCROLL_HEIGHT + layout->tile_border_y) /
(layout->tile_h + 2 * layout->tile_border_y);
+ layout->tile_w = VERTLIST_MAJORCOLUMN_WIDTH;
+ file_attribute_columns_init(params, layout);
- column_widths(params, layout);
-
- if (params->display == FILE_SHORTDISPLAY) {
- maxlen = ICON_DEFAULT_WIDTH_SCALE + column_icon_space +
- (int)layout->column_widths[COLUMN_NAME] + column_space +
- (int)layout->column_widths[COLUMN_SIZE] + column_space;
- }
- else {
- maxlen = ICON_DEFAULT_WIDTH_SCALE + column_icon_space +
- (int)layout->column_widths[COLUMN_NAME] + column_space +
- (int)layout->column_widths[COLUMN_DATE] + column_space +
- (int)layout->column_widths[COLUMN_TIME] + column_space +
- (int)layout->column_widths[COLUMN_SIZE] + column_space;
- }
- layout->tile_w = maxlen;
if (layout->rows > 0) {
- layout->columns = numfiles / layout->rows + 1; // XXX dirty, modulo is zero
+ layout->flow_columns = numfiles / layout->rows + 1; // XXX dirty, modulo is zero
}
else {
layout->rows = 1;
- layout->columns = numfiles + 1; // XXX dirty, modulo is zero
+ layout->flow_columns = numfiles + 1; // XXX dirty, modulo is zero
}
- layout->width = sfile->layout->columns * (layout->tile_w + 2 * layout->tile_border_x) +
+ layout->width = sfile->layout->flow_columns * (layout->tile_w + 2 * layout->tile_border_x) +
layout->tile_border_x * 2;
layout->flag = FILE_LAYOUT_HOR;
}
diff --git a/source/blender/editors/space_file/space_file.c b/source/blender/editors/space_file/space_file.c
index 1fd878e4662..1befdd52d7d 100644
--- a/source/blender/editors/space_file/space_file.c
+++ b/source/blender/editors/space_file/space_file.c
@@ -72,23 +72,40 @@ static SpaceLink *file_new(const ScrArea *UNUSED(area), const Scene *UNUSED(scen
/* Ignore user preference "USER_HEADER_BOTTOM" here (always show top for new types). */
ar->alignment = RGN_ALIGN_TOP;
+ /* ui list region */
+ ar = MEM_callocN(sizeof(ARegion), "ui region for file");
+ BLI_addtail(&sfile->regionbase, ar);
+ ar->regiontype = RGN_TYPE_UI;
+ ar->alignment = RGN_ALIGN_TOP;
+ ar->flag |= RGN_FLAG_DYNAMIC_SIZE;
+
/* Tools region */
ar = MEM_callocN(sizeof(ARegion), "tools region for file");
BLI_addtail(&sfile->regionbase, ar);
ar->regiontype = RGN_TYPE_TOOLS;
ar->alignment = RGN_ALIGN_LEFT;
+ /* Tools region (lower split region) */
+ ar = MEM_callocN(sizeof(ARegion), "lower tools region for file");
+ BLI_addtail(&sfile->regionbase, ar);
+ ar->regiontype = RGN_TYPE_TOOLS;
+ ar->alignment = RGN_ALIGN_BOTTOM | RGN_SPLIT_PREV;
+ ar->flag |= RGN_FLAG_DYNAMIC_SIZE;
+
+ /* Execute region */
+ ar = MEM_callocN(sizeof(ARegion), "execute region for file");
+ BLI_addtail(&sfile->regionbase, ar);
+ ar->regiontype = RGN_TYPE_EXECUTE;
+ ar->alignment = RGN_ALIGN_BOTTOM;
+ ar->flag |= RGN_FLAG_DYNAMIC_SIZE;
+ /* Tool props region is added as needed. */
+#if 0
/* Tool props (aka operator) region */
ar = MEM_callocN(sizeof(ARegion), "tool props region for file");
BLI_addtail(&sfile->regionbase, ar);
ar->regiontype = RGN_TYPE_TOOL_PROPS;
- ar->alignment = RGN_ALIGN_BOTTOM | RGN_SPLIT_PREV;
-
- /* ui list region */
- ar = MEM_callocN(sizeof(ARegion), "ui region for file");
- BLI_addtail(&sfile->regionbase, ar);
- ar->regiontype = RGN_TYPE_UI;
- ar->alignment = RGN_ALIGN_TOP;
+ ar->alignment = RGN_ALIGN_RIGHT;
+#endif
/* main region */
ar = MEM_callocN(sizeof(ARegion), "main region for file");
@@ -204,6 +221,7 @@ static SpaceLink *file_duplicate(SpaceLink *sl)
static void file_refresh(const bContext *C, ScrArea *sa)
{
wmWindowManager *wm = CTX_wm_manager(C);
+ wmWindow *win = CTX_wm_window(C);
SpaceFile *sfile = CTX_wm_space_file(C);
FileSelectParams *params = ED_fileselect_get_params(sfile);
struct FSMenu *fsmenu = ED_fsmenu_get();
@@ -217,15 +235,16 @@ static void file_refresh(const bContext *C, ScrArea *sa)
}
filelist_setdir(sfile->files, params->dir);
filelist_setrecursion(sfile->files, params->recursion_level);
- filelist_setsorting(sfile->files, params->sort);
- filelist_setfilter_options(sfile->files,
- (params->flag & FILE_FILTER) != 0,
- (params->flag & FILE_HIDE_DOT) != 0,
- false, /* TODO hide_parent, should be controllable? */
- params->filter,
- params->filter_id,
- params->filter_glob,
- params->filter_search);
+ filelist_setsorting(sfile->files, params->sort, params->flag & FILE_SORT_INVERT);
+ filelist_setfilter_options(
+ sfile->files,
+ (params->flag & FILE_FILTER) != 0,
+ (params->flag & FILE_HIDE_DOT) != 0,
+ true, /* Just always hide parent, prefer to not add an extra user option for this. */
+ params->filter,
+ params->filter_id,
+ params->filter_glob,
+ params->filter_search);
/* Update the active indices of bookmarks & co. */
sfile->systemnr = fsmenu_get_active_indices(fsmenu, FS_CATEGORY_SYSTEM, params->dir);
@@ -254,7 +273,7 @@ static void file_refresh(const bContext *C, ScrArea *sa)
else {
filelist_cache_previews_set(sfile->files, false);
if (sfile->previews_timer) {
- WM_event_remove_timer_notifier(wm, CTX_wm_window(C), sfile->previews_timer);
+ WM_event_remove_timer_notifier(wm, win, sfile->previews_timer);
sfile->previews_timer = NULL;
}
}
@@ -269,10 +288,20 @@ static void file_refresh(const bContext *C, ScrArea *sa)
/* Might be called with NULL sa, see file_main_region_draw() below. */
if (sa && BKE_area_find_region_type(sa, RGN_TYPE_TOOLS) == NULL) {
- /* Create TOOLS/TOOL_PROPS regions. */
+ /* Create TOOLS region. */
file_tools_region(sa);
- ED_area_initialize(wm, CTX_wm_window(C), sa);
+ ED_area_initialize(wm, win, sa);
+ }
+ if (sa && sfile->op && BKE_area_find_region_type(sa, RGN_TYPE_TOOL_PROPS) == NULL) {
+ /* Create TOOL_PROPS region. */
+ ARegion *region_props = file_tool_props_region(sa);
+
+ if (params->flag & FILE_HIDE_TOOL_PROPS) {
+ region_props->flag |= RGN_FLAG_HIDDEN;
+ }
+
+ ED_area_initialize(wm, win, sa);
}
ED_area_tag_redraw(sa);
@@ -406,6 +435,11 @@ static void file_main_region_draw(const bContext *C, ARegion *ar)
v2d->keepofs &= ~V2D_LOCKOFS_Y;
v2d->keepofs |= V2D_LOCKOFS_X;
}
+ else if (params->display == FILE_VERTICALDISPLAY) {
+ v2d->scroll = V2D_SCROLL_RIGHT;
+ v2d->keepofs &= ~V2D_LOCKOFS_Y;
+ v2d->keepofs |= V2D_LOCKOFS_X;
+ }
else {
v2d->scroll = V2D_SCROLL_BOTTOM;
v2d->keepofs &= ~V2D_LOCKOFS_X;
@@ -439,7 +473,9 @@ static void file_main_region_draw(const bContext *C, ARegion *ar)
UI_view2d_view_restore(C);
/* scrollers */
- scrollers = UI_view2d_scrollers_calc(v2d, NULL);
+ rcti view_rect;
+ ED_fileselect_layout_maskrect(sfile->layout, v2d, &view_rect);
+ scrollers = UI_view2d_scrollers_calc(v2d, &view_rect);
UI_view2d_scrollers_draw(v2d, scrollers);
UI_view2d_scrollers_free(scrollers);
}
@@ -452,6 +488,7 @@ static void file_operatortypes(void)
WM_operatortype_append(FILE_OT_select_box);
WM_operatortype_append(FILE_OT_select_bookmark);
WM_operatortype_append(FILE_OT_highlight);
+ WM_operatortype_append(FILE_OT_sort_column_ui_context);
WM_operatortype_append(FILE_OT_execute);
WM_operatortype_append(FILE_OT_cancel);
WM_operatortype_append(FILE_OT_parent);
@@ -538,7 +575,8 @@ static void file_ui_region_init(wmWindowManager *wm, ARegion *ar)
{
wmKeyMap *keymap;
- UI_view2d_region_reinit(&ar->v2d, V2D_COMMONVIEW_HEADER, ar->winx, ar->winy);
+ ED_region_panels_init(wm, ar);
+ ar->v2d.keepzoom |= V2D_LOCKZOOM_X | V2D_LOCKZOOM_Y;
/* own keymap */
keymap = WM_keymap_ensure(wm->defaultconf, "File Browser", SPACE_FILE, 0);
@@ -550,22 +588,18 @@ static void file_ui_region_init(wmWindowManager *wm, ARegion *ar)
static void file_ui_region_draw(const bContext *C, ARegion *ar)
{
- float col[3];
- /* clear */
- UI_GetThemeColor3fv(TH_BACK, col);
- GPU_clear_color(col[0], col[1], col[2], 0.0);
- GPU_clear(GPU_COLOR_BIT);
-
- /* scrolling here is just annoying, disable it */
- ar->v2d.cur.ymax = BLI_rctf_size_y(&ar->v2d.cur);
- ar->v2d.cur.ymin = 0;
-
- /* set view2d view matrix for scrolling (without scrollers) */
- UI_view2d_view_ortho(&ar->v2d);
+ ED_region_panels(C, ar);
+}
- file_draw_buttons(C, ar);
+static void file_execution_region_init(wmWindowManager *wm, ARegion *ar)
+{
+ ED_region_panels_init(wm, ar);
+ ar->v2d.keepzoom |= V2D_LOCKZOOM_X | V2D_LOCKZOOM_Y;
+}
- UI_view2d_view_restore(C);
+static void file_execution_region_draw(const bContext *C, ARegion *ar)
+{
+ ED_region_panels(C, ar);
}
static void file_ui_region_listener(wmWindow *UNUSED(win),
@@ -656,13 +690,21 @@ void ED_spacetype_file(void)
/* regions: ui */
art = MEM_callocN(sizeof(ARegionType), "spacetype file region");
art->regionid = RGN_TYPE_UI;
- art->prefsizey = 60;
art->keymapflag = ED_KEYMAP_UI;
art->listener = file_ui_region_listener;
art->init = file_ui_region_init;
art->draw = file_ui_region_draw;
BLI_addhead(&st->regiontypes, art);
+ /* regions: execution */
+ art = MEM_callocN(sizeof(ARegionType), "spacetype file region");
+ art->regionid = RGN_TYPE_EXECUTE;
+ art->keymapflag = ED_KEYMAP_UI;
+ art->listener = file_ui_region_listener;
+ art->init = file_execution_region_init;
+ art->draw = file_execution_region_draw;
+ BLI_addhead(&st->regiontypes, art);
+
/* regions: channels (directories) */
art = MEM_callocN(sizeof(ARegionType), "spacetype file region");
art->regionid = RGN_TYPE_TOOLS;
@@ -677,8 +719,8 @@ void ED_spacetype_file(void)
/* regions: tool properties */
art = MEM_callocN(sizeof(ARegionType), "spacetype file operator region");
art->regionid = RGN_TYPE_TOOL_PROPS;
- art->prefsizex = 0;
- art->prefsizey = 360;
+ art->prefsizex = 240;
+ art->prefsizey = 60;
art->keymapflag = ED_KEYMAP_UI;
art->listener = file_tools_region_listener;
art->init = file_tools_region_init;
diff --git a/source/blender/editors/space_graph/graph_buttons.c b/source/blender/editors/space_graph/graph_buttons.c
index c727c5225c9..708d91a82bb 100644
--- a/source/blender/editors/space_graph/graph_buttons.c
+++ b/source/blender/editors/space_graph/graph_buttons.c
@@ -861,6 +861,11 @@ static void graph_panel_driverVar__transChan(uiLayout *layout, ID *id, DriverVar
sub = uiLayoutColumn(layout, true);
uiItemR(sub, &dtar_ptr, "transform_type", 0, NULL, ICON_NONE);
+
+ if (ELEM(dtar->transChan, DTAR_TRANSCHAN_ROTX, DTAR_TRANSCHAN_ROTY, DTAR_TRANSCHAN_ROTZ)) {
+ uiItemR(sub, &dtar_ptr, "rotation_mode", 0, IFACE_("Mode"), ICON_NONE);
+ }
+
uiItemR(sub, &dtar_ptr, "transform_space", 0, IFACE_("Space"), ICON_NONE);
}
@@ -1236,7 +1241,7 @@ static void graph_panel_drivers_popover(const bContext *C, Panel *pa)
{
uiLayout *layout = pa->layout;
- PointerRNA ptr = {{NULL}};
+ PointerRNA ptr = {NULL};
PropertyRNA *prop = NULL;
int index = -1;
uiBut *but = NULL;
@@ -1258,7 +1263,7 @@ static void graph_panel_drivers_popover(const bContext *C, Panel *pa)
/* Populate Panel - With a combination of the contents of the Driven and Driver panels */
if (fcu && fcu->driver) {
- ID *id = ptr.id.data;
+ ID *id = ptr.owner_id;
PointerRNA ptr_fcurve;
RNA_pointer_create(id, &RNA_FCurve, fcu, &ptr_fcurve);
diff --git a/source/blender/editors/space_graph/graph_draw.c b/source/blender/editors/space_graph/graph_draw.c
index e7ba498fc11..e18a440f7c0 100644
--- a/source/blender/editors/space_graph/graph_draw.c
+++ b/source/blender/editors/space_graph/graph_draw.c
@@ -205,7 +205,7 @@ static void draw_fcurve_keyframe_vertices(FCurve *fcu, View2D *v2d, bool edit, u
{
immBindBuiltinProgram(GPU_SHADER_2D_POINT_UNIFORM_SIZE_UNIFORM_COLOR_AA);
- immUniform1f("size", UI_GetThemeValuef(TH_VERTEX_SIZE) * U.pixelsize);
+ immUniform1f("size", UI_GetThemeValuef(TH_VERTEX_SIZE) * U.dpi_fac);
draw_fcurve_selected_keyframe_vertices(fcu, v2d, edit, false, pos);
draw_fcurve_selected_keyframe_vertices(fcu, v2d, edit, true, pos);
@@ -269,8 +269,8 @@ static void draw_fcurve_handle_vertices(FCurve *fcu,
immBindBuiltinProgram(GPU_SHADER_2D_POINT_UNIFORM_SIZE_UNIFORM_COLOR_OUTLINE_AA);
/* set handle size */
- immUniform1f("size", (1.4f * UI_GetThemeValuef(TH_HANDLE_VERTEX_SIZE)) * U.pixelsize);
- immUniform1f("outlineWidth", 1.5f * U.pixelsize);
+ immUniform1f("size", (1.4f * UI_GetThemeValuef(TH_HANDLE_VERTEX_SIZE)) * U.dpi_fac);
+ immUniform1f("outlineWidth", 1.5f * U.dpi_fac);
draw_fcurve_selected_handle_vertices(fcu, v2d, false, sel_handle_only, pos);
draw_fcurve_selected_handle_vertices(fcu, v2d, true, sel_handle_only, pos);
diff --git a/source/blender/editors/space_graph/graph_edit.c b/source/blender/editors/space_graph/graph_edit.c
index b624e21937f..329067de545 100644
--- a/source/blender/editors/space_graph/graph_edit.c
+++ b/source/blender/editors/space_graph/graph_edit.c
@@ -1538,7 +1538,7 @@ void GRAPH_OT_sound_bake(wmOperatorType *ot)
FILE_TYPE_FOLDER | FILE_TYPE_SOUND | FILE_TYPE_MOVIE,
FILE_SPECIAL,
FILE_OPENFILE,
- WM_FILESEL_FILEPATH,
+ WM_FILESEL_FILEPATH | WM_FILESEL_SHOW_PROPS,
FILE_DEFAULTDISPLAY,
FILE_SORT_ALPHA);
RNA_def_float(ot->srna,
diff --git a/source/blender/editors/space_graph/graph_intern.h b/source/blender/editors/space_graph/graph_intern.h
index 168c38b66a4..320240221b5 100644
--- a/source/blender/editors/space_graph/graph_intern.h
+++ b/source/blender/editors/space_graph/graph_intern.h
@@ -26,7 +26,6 @@
struct ARegion;
struct ARegionType;
-struct ScrArea;
struct SpaceGraph;
struct bAnimContext;
struct bAnimListElem;
diff --git a/source/blender/editors/space_graph/space_graph.c b/source/blender/editors/space_graph/space_graph.c
index 6c5ebf77bf4..91e5ab61dd9 100644
--- a/source/blender/editors/space_graph/space_graph.c
+++ b/source/blender/editors/space_graph/space_graph.c
@@ -527,7 +527,7 @@ static void graph_region_message_subscribe(const struct bContext *UNUSED(C),
* so just whitelist the entire structs for updates
*/
{
- wmMsgParams_RNA msg_key_params = {{{0}}};
+ wmMsgParams_RNA msg_key_params = {{0}};
StructRNA *type_array[] = {
&RNA_DopeSheet, /* dopesheet filters */
diff --git a/source/blender/editors/space_image/image_buttons.c b/source/blender/editors/space_image/image_buttons.c
index f1a29a1542d..64e1c02590e 100644
--- a/source/blender/editors/space_image/image_buttons.c
+++ b/source/blender/editors/space_image/image_buttons.c
@@ -991,7 +991,7 @@ void uiTemplateImage(uiLayout *layout,
void uiTemplateImageSettings(uiLayout *layout, PointerRNA *imfptr, bool color_management)
{
ImageFormatData *imf = imfptr->data;
- ID *id = imfptr->id.data;
+ ID *id = imfptr->owner_id;
PointerRNA display_settings_ptr;
PropertyRNA *prop;
const int depth_ok = BKE_imtype_valid_depths(imf->imtype);
diff --git a/source/blender/editors/space_image/image_intern.h b/source/blender/editors/space_image/image_intern.h
index 2c723f45e94..1abb6715fdb 100644
--- a/source/blender/editors/space_image/image_intern.h
+++ b/source/blender/editors/space_image/image_intern.h
@@ -27,7 +27,6 @@
/* internal exports only */
struct ARegion;
struct ARegionType;
-struct ScrArea;
struct SpaceImage;
struct bContext;
struct bNodeTree;
diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c
index 05ba82b8bde..e338a450db6 100644
--- a/source/blender/editors/space_image/image_ops.c
+++ b/source/blender/editors/space_image/image_ops.c
@@ -1431,7 +1431,7 @@ static int image_open_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(
Image *oldima;
oldptr = RNA_property_pointer_get(&ptr, prop);
- oldima = (Image *)oldptr.id.data;
+ oldima = (Image *)oldptr.owner_id;
/* unlikely to fail but better avoid strange crash */
if (oldima && GS(oldima->id.name) == ID_IM) {
ima = oldima;
@@ -1773,6 +1773,7 @@ static int image_save_options_init(Main *bmain,
}
else {
BLI_snprintf(opts->filepath, sizeof(opts->filepath), "//%s", ima->id.name + 2);
+ BLI_path_make_safe(opts->filepath);
BLI_path_abs(opts->filepath, is_prev_save ? G.ima : BKE_main_blendfile_path(bmain));
}
}
diff --git a/source/blender/editors/space_image/space_image.c b/source/blender/editors/space_image/space_image.c
index 33e77d3623e..5fa4fe3e077 100644
--- a/source/blender/editors/space_image/space_image.c
+++ b/source/blender/editors/space_image/space_image.c
@@ -586,7 +586,7 @@ static void image_main_region_draw(const bContext *C, ARegion *ar)
float col[3];
/* XXX This is in order to draw UI batches with the DRW
- * olg context since we now use it for drawing the entire area */
+ * old context since we now use it for drawing the entire area. */
gpu_batch_presets_reset();
GPUViewport *viewport =
diff --git a/source/blender/editors/space_info/info_stats.c b/source/blender/editors/space_info/info_stats.c
index 946274de882..106edc290d5 100644
--- a/source/blender/editors/space_info/info_stats.c
+++ b/source/blender/editors/space_info/info_stats.c
@@ -574,7 +574,7 @@ void ED_info_stats_clear(ViewLayer *view_layer)
const char *ED_info_stats_string(Main *bmain, Scene *scene, ViewLayer *view_layer)
{
- /* Loopin through dependency graph when interface is locked in not safe.
+ /* Looping through dependency graph when interface is locked in not safe.
* Thew interface is marked as locked when jobs wants to modify the
* dependency graph. */
wmWindowManager *wm = bmain->wm.first;
diff --git a/source/blender/editors/space_info/textview.c b/source/blender/editors/space_info/textview.c
index 24d3008b1e7..97d5faa9c13 100644
--- a/source/blender/editors/space_info/textview.c
+++ b/source/blender/editors/space_info/textview.c
@@ -313,7 +313,7 @@ int textview_draw(
/* constants for the sequencer context */
cdc.font_id = font_id;
cdc.cwidth = (int)BLF_fixed_width(font_id);
- assert(cdc.cwidth > 0);
+ BLI_assert(cdc.cwidth > 0);
cdc.lheight = tvc->lheight;
cdc.lofs = -BLF_descender(font_id);
/* note, scroll bar must be already subtracted () */
diff --git a/source/blender/editors/space_nla/nla_buttons.c b/source/blender/editors/space_nla/nla_buttons.c
index 719efc7eeac..126b20a028e 100644
--- a/source/blender/editors/space_nla/nla_buttons.c
+++ b/source/blender/editors/space_nla/nla_buttons.c
@@ -268,8 +268,8 @@ static void nla_panel_animdata(const bContext *C, Panel *pa)
/* icon + id-block name of block where AnimData came from to prevent
* accidentally changing the properties of the wrong action
*/
- if (adt_ptr.id.data) {
- ID *id = adt_ptr.id.data;
+ if (adt_ptr.owner_id) {
+ ID *id = adt_ptr.owner_id;
PointerRNA id_ptr;
RNA_id_pointer_create(id, &id_ptr);
@@ -513,7 +513,7 @@ static void nla_panel_modifiers(const bContext *C, Panel *pa)
for (fcm = strip->modifiers.first; fcm; fcm = fcm->next) {
col = uiLayoutColumn(pa->layout, true);
- ANIM_uiTemplate_fmodifier_draw(col, strip_ptr.id.data, &strip->modifiers, fcm);
+ ANIM_uiTemplate_fmodifier_draw(col, strip_ptr.owner_id, &strip->modifiers, fcm);
}
}
diff --git a/source/blender/editors/space_nla/nla_channels.c b/source/blender/editors/space_nla/nla_channels.c
index e5c116e85de..456eb783706 100644
--- a/source/blender/editors/space_nla/nla_channels.c
+++ b/source/blender/editors/space_nla/nla_channels.c
@@ -446,7 +446,7 @@ static int nlachannels_pushdown_exec(bContext *C, wmOperator *op)
/* get anim-channel to use (or more specifically, the animdata block behind it) */
if (channel_index == -1) {
- PointerRNA adt_ptr = {{NULL}};
+ PointerRNA adt_ptr = {NULL};
/* active animdata block */
if (nla_panel_context(C, &adt_ptr, NULL, NULL) == 0 || (adt_ptr.data == NULL)) {
@@ -458,7 +458,7 @@ static int nlachannels_pushdown_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
else {
- id = adt_ptr.id.data;
+ id = adt_ptr.owner_id;
adt = adt_ptr.data;
}
}
@@ -581,7 +581,7 @@ static int nla_action_unlink_exec(bContext *C, wmOperator *op)
/* do unlinking */
if (adt && adt->action) {
bool force_delete = RNA_boolean_get(op->ptr, "force_delete");
- ED_animedit_unlink_action(C, adt_ptr.id.data, adt, adt->action, op->reports, force_delete);
+ ED_animedit_unlink_action(C, adt_ptr.owner_id, adt, adt->action, op->reports, force_delete);
}
return OPERATOR_FINISHED;
diff --git a/source/blender/editors/space_nla/nla_draw.c b/source/blender/editors/space_nla/nla_draw.c
index 5cf9646210e..9d6ccd6fe35 100644
--- a/source/blender/editors/space_nla/nla_draw.c
+++ b/source/blender/editors/space_nla/nla_draw.c
@@ -141,8 +141,10 @@ static void nla_action_draw_keyframes(
uint outline_color_id = GPU_vertformat_attr_add(
format, "outlineColor", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
uint flags_id = GPU_vertformat_attr_add(format, "flags", GPU_COMP_U32, 1, GPU_FETCH_INT);
- immBindBuiltinProgram(GPU_SHADER_KEYFRAME_DIAMOND);
+
GPU_program_point_size(true);
+ immBindBuiltinProgram(GPU_SHADER_KEYFRAME_DIAMOND);
+ immUniform1f("outline_scale", 1.0f);
immUniform2f("ViewportSize", BLI_rcti_size_x(&v2d->mask) + 1, BLI_rcti_size_y(&v2d->mask) + 1);
immBegin(GPU_PRIM_POINTS, key_len);
diff --git a/source/blender/editors/space_nla/space_nla.c b/source/blender/editors/space_nla/space_nla.c
index 3bdd2804efc..f274f3c93ec 100644
--- a/source/blender/editors/space_nla/space_nla.c
+++ b/source/blender/editors/space_nla/space_nla.c
@@ -522,7 +522,7 @@ static void nla_channel_region_message_subscribe(const struct bContext *UNUSED(C
* so just whitelist the entire struct for updates
*/
{
- wmMsgParams_RNA msg_key_params = {{{0}}};
+ wmMsgParams_RNA msg_key_params = {{0}};
StructRNA *type_array[] = {
&RNA_DopeSheet,
};
diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.c
index 748e485f614..b89f163f579 100644
--- a/source/blender/editors/space_node/drawnode.c
+++ b/source/blender/editors/space_node/drawnode.c
@@ -24,6 +24,7 @@
#include "BLI_blenlib.h"
#include "BLI_math.h"
+#include "BLI_system.h"
#include "DNA_node_types.h"
#include "DNA_object_types.h"
@@ -90,7 +91,7 @@ static void node_buts_value(uiLayout *layout, bContext *UNUSED(C), PointerRNA *p
/* first output stores value */
bNodeSocket *output = node->outputs.first;
PointerRNA sockptr;
- RNA_pointer_create(ptr->id.data, &RNA_NodeSocket, output, &sockptr);
+ RNA_pointer_create(ptr->owner_id, &RNA_NodeSocket, output, &sockptr);
uiItemR(layout, &sockptr, "default_value", 0, "", ICON_NONE);
}
@@ -102,7 +103,7 @@ static void node_buts_rgb(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr
bNodeSocket *output = node->outputs.first;
PointerRNA sockptr;
uiLayout *col;
- RNA_pointer_create(ptr->id.data, &RNA_NodeSocket, output, &sockptr);
+ RNA_pointer_create(ptr->owner_id, &RNA_NodeSocket, output, &sockptr);
col = uiLayoutColumn(layout, false);
uiTemplateColorPicker(col, &sockptr, "default_value", 1, 0, 0, 0);
@@ -113,7 +114,7 @@ static void node_buts_mix_rgb(uiLayout *layout, bContext *UNUSED(C), PointerRNA
{
uiLayout *row, *col;
- bNodeTree *ntree = (bNodeTree *)ptr->id.data;
+ bNodeTree *ntree = (bNodeTree *)ptr->owner_id;
col = uiLayoutColumn(layout, false);
row = uiLayoutRow(col, true);
@@ -193,7 +194,7 @@ static void node_buts_normal(uiLayout *layout, bContext *UNUSED(C), PointerRNA *
/* first output stores normal */
bNodeSocket *output = node->outputs.first;
PointerRNA sockptr;
- RNA_pointer_create(ptr->id.data, &RNA_NodeSocket, output, &sockptr);
+ RNA_pointer_create(ptr->owner_id, &RNA_NodeSocket, output, &sockptr);
uiItemR(layout, &sockptr, "default_value", 0, "", ICON_NONE);
}
@@ -1186,6 +1187,11 @@ static void node_shader_buts_ambient_occlusion(uiLayout *layout,
uiItemR(layout, ptr, "only_local", 0, NULL, ICON_NONE);
}
+static void node_shader_buts_white_noise(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "dimensions", 0, "", ICON_NONE);
+}
+
/* only once called */
static void node_shader_set_butfunc(bNodeType *ntype)
{
@@ -1220,7 +1226,7 @@ static void node_shader_set_butfunc(bNodeType *ntype)
case SH_NODE_MATH:
ntype->draw_buttons = node_buts_math;
break;
- case SH_NODE_VECT_MATH:
+ case SH_NODE_VECTOR_MATH:
ntype->draw_buttons = node_shader_buts_vect_math;
break;
case SH_NODE_VECT_TRANSFORM:
@@ -1330,6 +1336,9 @@ static void node_shader_set_butfunc(bNodeType *ntype)
case SH_NODE_AMBIENT_OCCLUSION:
ntype->draw_buttons = node_shader_buts_ambient_occlusion;
break;
+ case SH_NODE_TEX_WHITE_NOISE:
+ ntype->draw_buttons = node_shader_buts_white_noise;
+ break;
}
}
@@ -1363,7 +1372,7 @@ static void node_composit_buts_image(uiLayout *layout, bContext *C, PointerRNA *
bNode *node = ptr->data;
PointerRNA imaptr, iuserptr;
- RNA_pointer_create((ID *)ptr->id.data, &RNA_ImageUser, node->storage, &iuserptr);
+ RNA_pointer_create(ptr->owner_id, &RNA_ImageUser, node->storage, &iuserptr);
uiLayoutSetContextPointer(layout, "image_user", &iuserptr);
uiTemplateID(layout,
C,
@@ -1390,7 +1399,7 @@ static void node_composit_buts_image_ex(uiLayout *layout, bContext *C, PointerRN
bNode *node = ptr->data;
PointerRNA iuserptr;
- RNA_pointer_create((ID *)ptr->id.data, &RNA_ImageUser, node->storage, &iuserptr);
+ RNA_pointer_create(ptr->owner_id, &RNA_ImageUser, node->storage, &iuserptr);
uiLayoutSetContextPointer(layout, "image_user", &iuserptr);
uiTemplateImage(layout, C, ptr, "image", &iuserptr, 0, 1);
}
@@ -1951,7 +1960,7 @@ static void node_composit_buts_file_output_ex(uiLayout *layout, bContext *C, Poi
}
/* XXX collection lookup does not return the ID part of the pointer,
* setting this manually here */
- active_input_ptr.id.data = ptr->id.data;
+ active_input_ptr.owner_id = ptr->owner_id;
col = uiLayoutColumn(row, true);
ot = WM_operatortype_find("NODE_OT_output_file_move_active_socket", false);
@@ -2695,6 +2704,10 @@ static void node_composit_buts_denoise(uiLayout *layout, bContext *UNUSED(C), Po
{
#ifndef WITH_OPENIMAGEDENOISE
uiItemL(layout, IFACE_("Disabled, built without OpenImageDenoise"), ICON_ERROR);
+#else
+ if (!BLI_cpu_support_sse41()) {
+ uiItemL(layout, IFACE_("Disabled, CPU with SSE4.1 is required"), ICON_ERROR);
+ }
#endif
uiItemR(layout, ptr, "use_hdr", 0, NULL, ICON_NONE);
@@ -2959,7 +2972,7 @@ static void node_texture_buts_proc(uiLayout *layout, bContext *UNUSED(C), Pointe
{
PointerRNA tex_ptr;
bNode *node = ptr->data;
- ID *id = ptr->id.data;
+ ID *id = ptr->owner_id;
Tex *tex = (Tex *)node->storage;
uiLayout *col, *row;
@@ -3053,7 +3066,7 @@ static void node_texture_buts_image_ex(uiLayout *layout, bContext *C, PointerRNA
bNode *node = ptr->data;
PointerRNA iuserptr;
- RNA_pointer_create((ID *)ptr->id.data, &RNA_ImageUser, node->storage, &iuserptr);
+ RNA_pointer_create(ptr->owner_id, &RNA_ImageUser, node->storage, &iuserptr);
uiTemplateImage(layout, C, ptr, "image", &iuserptr, 0, 0);
}
@@ -3115,7 +3128,7 @@ static void node_texture_set_butfunc(bNodeType *ntype)
static void node_property_update_default(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr)
{
- bNodeTree *ntree = ptr->id.data;
+ bNodeTree *ntree = (bNodeTree *)ptr->owner_id;
bNode *node = ptr->data;
ED_node_tag_update_nodetree(bmain, ntree, node);
}
@@ -3285,7 +3298,7 @@ static void node_file_output_socket_draw(bContext *C,
PointerRNA *ptr,
PointerRNA *node_ptr)
{
- bNodeTree *ntree = ptr->id.data;
+ bNodeTree *ntree = (bNodeTree *)ptr->owner_id;
bNodeSocket *sock = ptr->data;
uiLayout *row;
PointerRNA inputptr, imfptr;
@@ -3358,8 +3371,13 @@ static void std_node_socket_draw(
uiTemplateComponentMenu(layout, ptr, "default_value", text);
}
else {
- uiLayout *column = uiLayoutColumn(layout, true);
- uiItemR(column, ptr, "default_value", 0, text, 0);
+ if (sock->typeinfo->subtype == PROP_DIRECTION) {
+ uiItemR(layout, ptr, "default_value", 0, "", ICON_NONE);
+ }
+ else {
+ uiLayout *column = uiLayoutColumn(layout, true);
+ uiItemR(column, ptr, "default_value", 0, text, ICON_NONE);
+ }
}
break;
case SOCK_RGBA:
diff --git a/source/blender/editors/space_node/node_draw.c b/source/blender/editors/space_node/node_draw.c
index 1c3544077c4..6dc2182b684 100644
--- a/source/blender/editors/space_node/node_draw.c
+++ b/source/blender/editors/space_node/node_draw.c
@@ -713,15 +713,28 @@ static void node_draw_mute_line(View2D *v2d, SpaceNode *snode, bNode *node)
GPU_blend(false);
}
-static void node_socket_circle_draw(const bContext *C,
- bNodeTree *ntree,
- PointerRNA node_ptr,
- bNodeSocket *sock,
- unsigned pos,
- unsigned col)
+/* flags used in gpu_shader_keyframe_diamond_frag.glsl */
+#define MARKER_SHAPE_DIAMOND 0x1
+#define MARKER_SHAPE_SQUARE 0xC
+#define MARKER_SHAPE_CIRCLE 0x2
+#define MARKER_SHAPE_INNER_DOT 0x10
+
+static void node_socket_draw(const bContext *C,
+ bNodeTree *ntree,
+ PointerRNA node_ptr,
+ bNodeSocket *sock,
+ unsigned pos_id,
+ unsigned col_id,
+ unsigned shape_id,
+ unsigned size_id,
+ unsigned outline_col_id,
+ float size,
+ bool selected)
{
PointerRNA ptr;
float color[4];
+ float outline_color[4];
+ unsigned int flags = 0;
RNA_pointer_create((ID *)ntree, &RNA_NodeSocket, sock, &ptr);
sock->typeinfo->draw_color((bContext *)C, &ptr, &node_ptr, color);
@@ -731,8 +744,44 @@ static void node_socket_circle_draw(const bContext *C,
color[3] *= 0.25f;
}
- immAttr4fv(col, color);
- immVertex2f(pos, sock->locx, sock->locy);
+ if (selected) {
+ UI_GetThemeColor4fv(TH_TEXT_HI, outline_color);
+ outline_color[3] = 0.9f;
+ }
+ else {
+ copy_v4_fl(outline_color, 0.0f);
+ outline_color[3] = 0.6f;
+ }
+
+ /* sets shape flags */
+ switch (sock->display_shape) {
+ case SOCK_DISPLAY_SHAPE_DIAMOND:
+ case SOCK_DISPLAY_SHAPE_DIAMOND_DOT:
+ flags = MARKER_SHAPE_DIAMOND;
+ break;
+ case SOCK_DISPLAY_SHAPE_SQUARE:
+ case SOCK_DISPLAY_SHAPE_SQUARE_DOT:
+ flags = MARKER_SHAPE_SQUARE;
+ break;
+ default:
+ case SOCK_DISPLAY_SHAPE_CIRCLE:
+ case SOCK_DISPLAY_SHAPE_CIRCLE_DOT:
+ flags = MARKER_SHAPE_CIRCLE;
+ break;
+ }
+
+ if (ELEM(sock->display_shape,
+ SOCK_DISPLAY_SHAPE_DIAMOND_DOT,
+ SOCK_DISPLAY_SHAPE_SQUARE_DOT,
+ SOCK_DISPLAY_SHAPE_CIRCLE_DOT)) {
+ flags |= MARKER_SHAPE_INNER_DOT;
+ }
+
+ immAttr4fv(col_id, color);
+ immAttr1u(shape_id, flags);
+ immAttr1f(size_id, size);
+ immAttr4fv(outline_col_id, outline_color);
+ immVertex2f(pos_id, sock->locx, sock->locy);
}
/* ************** Socket callbacks *********** */
@@ -888,26 +937,28 @@ void node_draw_sockets(View2D *v2d,
PointerRNA node_ptr;
RNA_pointer_create((ID *)ntree, &RNA_Node, node, &node_ptr);
- float scale;
- UI_view2d_scale_get(v2d, &scale, NULL);
+ bool selected = false;
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);
+ uint pos_id = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ uint col_id = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
+ uint shape_id = GPU_vertformat_attr_add(format, "flags", GPU_COMP_U32, 1, GPU_FETCH_INT);
+ uint size_id = GPU_vertformat_attr_add(format, "size", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
+ uint outline_col_id = GPU_vertformat_attr_add(
+ format, "outlineColor", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
GPU_blend(true);
GPU_program_point_size(true);
-
- immBindBuiltinProgram(GPU_SHADER_2D_POINT_UNIFORM_SIZE_VARYING_COLOR_OUTLINE_AA);
+ immBindBuiltinProgram(GPU_SHADER_KEYFRAME_DIAMOND);
+ immUniform1f("outline_scale", 0.7f);
+ immUniform2f("ViewportSize", -1.0f, -1.0f);
/* set handle size */
- immUniform1f("size", 2.0f * NODE_SOCKSIZE * scale); /* 2 * size to have diameter */
+ float scale;
+ UI_view2d_scale_get(v2d, &scale, NULL);
+ scale *= 2.25f * NODE_SOCKSIZE;
if (!select_all) {
- /* outline for unselected sockets */
- immUniform1f("outlineWidth", 1.0f);
- immUniform4f("outlineColor", 0.0f, 0.0f, 0.0f, 0.6f);
-
immBeginAtMost(GPU_PRIM_POINTS, total_input_len + total_output_len);
}
@@ -923,7 +974,17 @@ void node_draw_sockets(View2D *v2d,
continue;
}
- node_socket_circle_draw(C, ntree, node_ptr, sock, pos, col);
+ node_socket_draw(C,
+ ntree,
+ node_ptr,
+ sock,
+ pos_id,
+ col_id,
+ shape_id,
+ size_id,
+ outline_col_id,
+ scale,
+ selected);
}
/* socket outputs */
@@ -938,7 +999,17 @@ void node_draw_sockets(View2D *v2d,
continue;
}
- node_socket_circle_draw(C, ntree, node_ptr, sock, pos, col);
+ node_socket_draw(C,
+ ntree,
+ node_ptr,
+ sock,
+ pos_id,
+ col_id,
+ shape_id,
+ size_id,
+ outline_col_id,
+ scale,
+ selected);
}
}
@@ -949,10 +1020,8 @@ void node_draw_sockets(View2D *v2d,
/* go back and draw selected sockets */
if (selected_input_len + selected_output_len > 0) {
/* outline for selected sockets */
- float c[3];
- UI_GetThemeColor3fv(TH_TEXT_HI, c);
- immUniform4f("outlineColor", c[0], c[1], c[2], 1.0f);
- immUniform1f("outlineWidth", 1.5f);
+
+ selected = true;
immBegin(GPU_PRIM_POINTS, selected_input_len + selected_output_len);
@@ -963,7 +1032,17 @@ void node_draw_sockets(View2D *v2d,
continue;
}
if (select_all || (sock->flag & SELECT)) {
- node_socket_circle_draw(C, ntree, node_ptr, sock, pos, col);
+ node_socket_draw(C,
+ ntree,
+ node_ptr,
+ sock,
+ pos_id,
+ col_id,
+ shape_id,
+ size_id,
+ outline_col_id,
+ scale,
+ selected);
if (--selected_input_len == 0) {
break; /* stop as soon as last one is drawn */
}
@@ -978,7 +1057,17 @@ void node_draw_sockets(View2D *v2d,
continue;
}
if (select_all || (sock->flag & SELECT)) {
- node_socket_circle_draw(C, ntree, node_ptr, sock, pos, col);
+ node_socket_draw(C,
+ ntree,
+ node_ptr,
+ sock,
+ pos_id,
+ col_id,
+ shape_id,
+ size_id,
+ outline_col_id,
+ scale,
+ selected);
if (--selected_output_len == 0) {
break; /* stop as soon as last one is drawn */
}
diff --git a/source/blender/editors/space_node/node_edit.c b/source/blender/editors/space_node/node_edit.c
index d31256a1425..dcf901216f5 100644
--- a/source/blender/editors/space_node/node_edit.c
+++ b/source/blender/editors/space_node/node_edit.c
@@ -1838,7 +1838,7 @@ static int node_output_file_add_socket_exec(bContext *C, wmOperator *op)
if (ptr.data) {
node = ptr.data;
- ntree = ptr.id.data;
+ ntree = (bNodeTree *)ptr.owner_id;
}
else if (snode && snode->edittree) {
ntree = snode->edittree;
@@ -1886,7 +1886,7 @@ static int node_output_file_remove_active_socket_exec(bContext *C, wmOperator *U
if (ptr.data) {
node = ptr.data;
- ntree = ptr.id.data;
+ ntree = (bNodeTree *)ptr.owner_id;
}
else if (snode && snode->edittree) {
ntree = snode->edittree;
@@ -2535,7 +2535,7 @@ static int node_shader_script_update_exec(bContext *C, wmOperator *op)
/* get node */
if (nodeptr.data) {
- ntree_base = nodeptr.id.data;
+ ntree_base = (bNodeTree *)nodeptr.owner_id;
node = nodeptr.data;
}
else if (snode && snode->edittree) {
@@ -2734,7 +2734,7 @@ static int node_cryptomatte_add_socket_exec(bContext *C, wmOperator *UNUSED(op))
if (ptr.data) {
node = ptr.data;
- ntree = ptr.id.data;
+ ntree = (bNodeTree *)ptr.owner_id;
}
else if (snode && snode->edittree) {
ntree = snode->edittree;
@@ -2778,7 +2778,7 @@ static int node_cryptomatte_remove_socket_exec(bContext *C, wmOperator *UNUSED(o
if (ptr.data) {
node = ptr.data;
- ntree = ptr.id.data;
+ ntree = (bNodeTree *)ptr.owner_id;
}
else if (snode && snode->edittree) {
ntree = snode->edittree;
diff --git a/source/blender/editors/space_node/node_select.c b/source/blender/editors/space_node/node_select.c
index 78f36719880..450cf28cce1 100644
--- a/source/blender/editors/space_node/node_select.c
+++ b/source/blender/editors/space_node/node_select.c
@@ -1234,8 +1234,18 @@ static uiBlock *node_find_menu(bContext *C, ARegion *ar, void *arg_op)
UI_block_flag_enable(block, UI_BLOCK_LOOP | UI_BLOCK_MOVEMOUSE_QUIT | UI_BLOCK_SEARCH_MENU);
UI_block_theme_style_set(block, UI_BLOCK_THEME_STYLE_POPUP);
- but = uiDefSearchBut(
- block, search, 0, ICON_VIEWZOOM, sizeof(search), 10, 10, 9 * UI_UNIT_X, UI_UNIT_Y, 0, 0, "");
+ but = uiDefSearchBut(block,
+ search,
+ 0,
+ ICON_VIEWZOOM,
+ sizeof(search),
+ 10,
+ 10,
+ UI_searchbox_size_x(),
+ UI_UNIT_Y,
+ 0,
+ 0,
+ "");
UI_but_func_search_set(but, NULL, node_find_cb, op->type, false, node_find_call_cb, NULL);
UI_but_flag_enable(but, UI_BUT_ACTIVATE_ON_INIT);
@@ -1256,7 +1266,7 @@ static uiBlock *node_find_menu(bContext *C, ARegion *ar, void *arg_op)
NULL);
/* Move it downwards, mouse over button. */
- UI_block_bounds_set_popup(block, 6, (const int[2]){0, -UI_UNIT_Y});
+ UI_block_bounds_set_popup(block, 0.3f * U.widget_unit, (const int[2]){0, -UI_UNIT_Y});
return block;
}
diff --git a/source/blender/editors/space_node/node_templates.c b/source/blender/editors/space_node/node_templates.c
index 8cc57a82fe0..423dec13c69 100644
--- a/source/blender/editors/space_node/node_templates.c
+++ b/source/blender/editors/space_node/node_templates.c
@@ -441,7 +441,7 @@ static int ui_node_item_name_compare(const void *a, const void *b)
{
const bNodeType *type_a = *(const bNodeType **)a;
const bNodeType *type_b = *(const bNodeType **)b;
- return BLI_natstrcmp(type_a->ui_name, type_b->ui_name);
+ return BLI_strcasecmp_natural(type_a->ui_name, type_b->ui_name);
}
static bool ui_node_item_special_poll(const bNodeTree *UNUSED(ntree), const bNodeType *ntype)
diff --git a/source/blender/editors/space_outliner/CMakeLists.txt b/source/blender/editors/space_outliner/CMakeLists.txt
index d235dd47136..616915dbc2c 100644
--- a/source/blender/editors/space_outliner/CMakeLists.txt
+++ b/source/blender/editors/space_outliner/CMakeLists.txt
@@ -41,6 +41,7 @@ set(SRC
outliner_edit.c
outliner_ops.c
outliner_select.c
+ outliner_sync.c
outliner_tools.c
outliner_tree.c
outliner_utils.c
diff --git a/source/blender/editors/space_outliner/outliner_collections.c b/source/blender/editors/space_outliner/outliner_collections.c
index 4740c412083..309446db83b 100644
--- a/source/blender/editors/space_outliner/outliner_collections.c
+++ b/source/blender/editors/space_outliner/outliner_collections.c
@@ -89,7 +89,7 @@ Collection *outliner_collection_from_tree_element(const TreeElement *te)
}
else if (ELEM(tselem->type, TSE_SCENE_COLLECTION_BASE, TSE_VIEW_COLLECTION_BASE)) {
Scene *scene = (Scene *)tselem->id;
- return BKE_collection_master(scene);
+ return scene->master_collection;
}
else if (tselem->type == 0 && te->idcode == ID_GR) {
return (Collection *)tselem->id;
@@ -199,7 +199,7 @@ static int collection_new_exec(bContext *C, wmOperator *op)
}
if (data.collection == NULL || ID_IS_LINKED(data.collection)) {
- data.collection = BKE_collection_master(scene);
+ data.collection = scene->master_collection;
}
if (ID_IS_LINKED(scene)) {
@@ -514,14 +514,14 @@ static int collection_duplicate_exec(bContext *C, wmOperator *op)
* This can happen when a whole scene is linked e.g. */
if (parent != NULL && ID_IS_LINKED(parent)) {
Scene *scene = CTX_data_scene(C);
- parent = ID_IS_LINKED(scene) ? NULL : BKE_collection_master(scene);
+ parent = ID_IS_LINKED(scene) ? NULL : scene->master_collection;
}
else if (parent != NULL && (parent->flag & COLLECTION_IS_MASTER) != 0) {
Scene *scene = BKE_collection_master_scene_search(bmain, parent);
BLI_assert(scene != NULL);
if (ID_IS_LINKED(scene)) {
scene = CTX_data_scene(C);
- parent = ID_IS_LINKED(scene) ? NULL : BKE_collection_master(scene);
+ parent = ID_IS_LINKED(scene) ? NULL : scene->master_collection;
}
}
diff --git a/source/blender/editors/space_outliner/outliner_dragdrop.c b/source/blender/editors/space_outliner/outliner_dragdrop.c
index d8276aa2bbc..6e30157d216 100644
--- a/source/blender/editors/space_outliner/outliner_dragdrop.c
+++ b/source/blender/editors/space_outliner/outliner_dragdrop.c
@@ -326,38 +326,57 @@ static bool parent_drop_poll(bContext *C,
return false;
}
-static int parent_drop_exec(bContext *C, wmOperator *op)
+static void parent_drop_set_parents(
+ bContext *C, ReportList *reports, wmDragID *drag, Object *parent, short parent_type)
{
- Object *par = NULL, *ob = NULL;
Main *bmain = CTX_data_main(C);
- Scene *scene = CTX_data_scene(C);
- int partype = -1;
- char parname[MAX_NAME], childname[MAX_NAME];
+ SpaceOutliner *soops = CTX_wm_space_outliner(C);
- partype = RNA_enum_get(op->ptr, "type");
- RNA_string_get(op->ptr, "parent", parname);
- par = (Object *)BKE_libblock_find_name(bmain, ID_OB, parname);
- RNA_string_get(op->ptr, "child", childname);
- ob = (Object *)BKE_libblock_find_name(bmain, ID_OB, childname);
+ TreeElement *te = outliner_find_id(soops, &soops->tree, &parent->id);
+ Scene *scene = (Scene *)outliner_search_back(soops, te, ID_SCE);
- if (ID_IS_LINKED(ob)) {
- BKE_report(op->reports, RPT_INFO, "Can't edit library linked object");
- return OPERATOR_CANCELLED;
+ if (scene == NULL) {
+ /* currently outliner organized in a way, that if there's no parent scene
+ * element for object it means that all displayed objects belong to
+ * active scene and parenting them is allowed (sergey)
+ */
+
+ scene = CTX_data_scene(C);
}
- ED_object_parent_set(op->reports, C, scene, ob, par, partype, false, false, NULL);
+ bool parent_set = false;
+ bool linked_objects = false;
- DEG_relations_tag_update(bmain);
- WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
- WM_event_add_notifier(C, NC_OBJECT | ND_PARENT, NULL);
+ for (wmDragID *drag_id = drag; drag_id; drag_id = drag_id->next) {
+ if (GS(drag_id->id->name) == ID_OB) {
+ Object *object = (Object *)drag_id->id;
- return OPERATOR_FINISHED;
+ /* Do nothing to linked data */
+ if (ID_IS_LINKED(object)) {
+ linked_objects = true;
+ continue;
+ }
+
+ if (ED_object_parent_set(
+ reports, C, scene, object, parent, parent_type, false, false, NULL)) {
+ parent_set = true;
+ }
+ }
+ }
+
+ if (linked_objects) {
+ BKE_report(reports, RPT_INFO, "Can't edit library linked object(s)");
+ }
+
+ if (parent_set) {
+ DEG_relations_tag_update(bmain);
+ WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
+ WM_event_add_notifier(C, NC_OBJECT | ND_PARENT, NULL);
+ }
}
static int parent_drop_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
- Main *bmain = CTX_data_main(C);
- SpaceOutliner *soops = CTX_wm_space_outliner(C);
TreeElement *te = outliner_drop_find(C, event);
TreeStoreElem *tselem = te ? TREESTORE(te) : NULL;
@@ -374,107 +393,15 @@ static int parent_drop_invoke(bContext *C, wmOperator *op, const wmEvent *event)
if (ob == par) {
return OPERATOR_CANCELLED;
}
- if (ID_IS_LINKED(ob)) {
- BKE_report(op->reports, RPT_INFO, "Can't edit library linked object");
- return OPERATOR_CANCELLED;
- }
-
- char childname[MAX_NAME];
- char parname[MAX_NAME];
- STRNCPY(childname, ob->id.name + 2);
- STRNCPY(parname, par->id.name + 2);
- RNA_string_set(op->ptr, "child", childname);
- RNA_string_set(op->ptr, "parent", parname);
- Scene *scene = (Scene *)outliner_search_back(soops, te, ID_SCE);
-
- if (scene == NULL) {
- /* currently outlier organized in a way, that if there's no parent scene
- * element for object it means that all displayed objects belong to
- * active scene and parenting them is allowed (sergey)
- */
-
- scene = CTX_data_scene(C);
+ if (event->custom != EVT_DATA_DRAGDROP) {
+ return OPERATOR_CANCELLED;
}
- if ((par->type != OB_ARMATURE) && (par->type != OB_CURVE) && (par->type != OB_LATTICE)) {
- int partype = 0;
- if (ED_object_parent_set(op->reports, C, scene, ob, par, partype, false, false, NULL)) {
- DEG_relations_tag_update(bmain);
- WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
- WM_event_add_notifier(C, NC_OBJECT | ND_PARENT, NULL);
- }
- }
- else {
- /* Menu creation */
- wmOperatorType *ot = WM_operatortype_find("OUTLINER_OT_parent_drop", false);
- uiPopupMenu *pup = UI_popup_menu_begin(C, IFACE_("Set Parent To"), ICON_NONE);
- uiLayout *layout = UI_popup_menu_layout(pup);
- PointerRNA ptr;
-
- /* Cannot use uiItemEnumO()... have multiple properties to set. */
- uiItemFullO_ptr(layout, ot, IFACE_("Object"), 0, NULL, WM_OP_EXEC_DEFAULT, 0, &ptr);
- RNA_string_set(&ptr, "parent", parname);
- RNA_string_set(&ptr, "child", childname);
- RNA_enum_set(&ptr, "type", PAR_OBJECT);
-
- /* par becomes parent, make the associated menus */
- if (par->type == OB_ARMATURE) {
- uiItemFullO_ptr(layout, ot, IFACE_("Armature Deform"), 0, NULL, WM_OP_EXEC_DEFAULT, 0, &ptr);
- RNA_string_set(&ptr, "parent", parname);
- RNA_string_set(&ptr, "child", childname);
- RNA_enum_set(&ptr, "type", PAR_ARMATURE);
-
- uiItemFullO_ptr(
- layout, ot, IFACE_(" With Empty Groups"), 0, NULL, WM_OP_EXEC_DEFAULT, 0, &ptr);
- RNA_string_set(&ptr, "parent", parname);
- RNA_string_set(&ptr, "child", childname);
- RNA_enum_set(&ptr, "type", PAR_ARMATURE_NAME);
-
- uiItemFullO_ptr(
- layout, ot, IFACE_(" With Envelope Weights"), 0, NULL, WM_OP_EXEC_DEFAULT, 0, &ptr);
- RNA_string_set(&ptr, "parent", parname);
- RNA_string_set(&ptr, "child", childname);
- RNA_enum_set(&ptr, "type", PAR_ARMATURE_ENVELOPE);
-
- uiItemFullO_ptr(
- layout, ot, IFACE_(" With Automatic Weights"), 0, NULL, WM_OP_EXEC_DEFAULT, 0, &ptr);
- RNA_string_set(&ptr, "parent", parname);
- RNA_string_set(&ptr, "child", childname);
- RNA_enum_set(&ptr, "type", PAR_ARMATURE_AUTO);
-
- uiItemFullO_ptr(layout, ot, IFACE_("Bone"), 0, NULL, WM_OP_EXEC_DEFAULT, 0, &ptr);
- RNA_string_set(&ptr, "parent", parname);
- RNA_string_set(&ptr, "child", childname);
- RNA_enum_set(&ptr, "type", PAR_BONE);
- }
- else if (par->type == OB_CURVE) {
- uiItemFullO_ptr(layout, ot, IFACE_("Curve Deform"), 0, NULL, WM_OP_EXEC_DEFAULT, 0, &ptr);
- RNA_string_set(&ptr, "parent", parname);
- RNA_string_set(&ptr, "child", childname);
- RNA_enum_set(&ptr, "type", PAR_CURVE);
-
- uiItemFullO_ptr(layout, ot, IFACE_("Follow Path"), 0, NULL, WM_OP_EXEC_DEFAULT, 0, &ptr);
- RNA_string_set(&ptr, "parent", parname);
- RNA_string_set(&ptr, "child", childname);
- RNA_enum_set(&ptr, "type", PAR_FOLLOW);
-
- uiItemFullO_ptr(layout, ot, IFACE_("Path Constraint"), 0, NULL, WM_OP_EXEC_DEFAULT, 0, &ptr);
- RNA_string_set(&ptr, "parent", parname);
- RNA_string_set(&ptr, "child", childname);
- RNA_enum_set(&ptr, "type", PAR_PATH_CONST);
- }
- else if (par->type == OB_LATTICE) {
- uiItemFullO_ptr(layout, ot, IFACE_("Lattice Deform"), 0, NULL, WM_OP_EXEC_DEFAULT, 0, &ptr);
- RNA_string_set(&ptr, "parent", parname);
- RNA_string_set(&ptr, "child", childname);
- RNA_enum_set(&ptr, "type", PAR_LATTICE);
- }
-
- UI_popup_menu_end(C, pup);
+ ListBase *lb = event->customdata;
+ wmDrag *drag = lb->first;
- return OPERATOR_INTERFACE;
- }
+ parent_drop_set_parents(C, op->reports, drag->ids.first, par, PAR_OBJECT);
return OPERATOR_FINISHED;
}
@@ -488,17 +415,11 @@ void OUTLINER_OT_parent_drop(wmOperatorType *ot)
/* api callbacks */
ot->invoke = parent_drop_invoke;
- ot->exec = parent_drop_exec;
ot->poll = ED_operator_outliner_active;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
-
- /* properties */
- RNA_def_string(ot->srna, "child", "Object", MAX_NAME, "Child", "Child Object");
- RNA_def_string(ot->srna, "parent", "Object", MAX_NAME, "Parent", "Parent Object");
- RNA_def_enum(ot->srna, "type", prop_make_parent_types, 0, "Type", "");
}
/* ******************** Parent Clear Operator *********************** */
@@ -549,13 +470,21 @@ static bool parent_clear_poll(bContext *C,
static int parent_clear_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
{
Main *bmain = CTX_data_main(C);
- Object *ob = (Object *)WM_drag_ID_from_event(event, ID_OB);
- if (ob == NULL) {
+ if (event->custom != EVT_DATA_DRAGDROP) {
return OPERATOR_CANCELLED;
}
- ED_object_parent_clear(ob, 0);
+ ListBase *lb = event->customdata;
+ wmDrag *drag = lb->first;
+
+ for (wmDragID *drag_id = drag->ids.first; drag_id; drag_id = drag_id->next) {
+ if (GS(drag_id->id->name) == ID_OB) {
+ Object *object = (Object *)drag_id->id;
+
+ ED_object_parent_clear(object, 0);
+ }
+ }
DEG_relations_tag_update(bmain);
WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
@@ -608,7 +537,7 @@ static int scene_drop_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent
Collection *collection;
if (scene != CTX_data_scene(C)) {
/* when linking to an inactive scene link to the master collection */
- collection = BKE_collection_master(scene);
+ collection = scene->master_collection;
}
else {
collection = CTX_data_collection(C);
@@ -966,6 +895,12 @@ static int outliner_item_drag_drop_invoke(bContext *C,
return (OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH);
}
+ float view_mval[2];
+ UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &view_mval[0], &view_mval[1]);
+ if (outliner_item_is_co_within_close_toggle(te, view_mval[0])) {
+ return (OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH);
+ }
+
wmDrag *drag = WM_event_start_drag(C, data.icon, WM_DRAG_ID, NULL, 0.0, WM_DRAG_NOP);
if (ELEM(GS(data.drag_id->name), ID_OB, ID_GR)) {
@@ -1032,7 +967,7 @@ static int outliner_item_drag_drop_invoke(bContext *C,
}
else {
Scene *scene = CTX_data_scene(C);
- parent = BKE_collection_master(scene);
+ parent = scene->master_collection;
}
WM_drag_add_ID(drag, id, &parent->id);
diff --git a/source/blender/editors/space_outliner/outliner_draw.c b/source/blender/editors/space_outliner/outliner_draw.c
index e4881a6f13d..a2ca3254b30 100644
--- a/source/blender/editors/space_outliner/outliner_draw.c
+++ b/source/blender/editors/space_outliner/outliner_draw.c
@@ -31,6 +31,7 @@
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "DNA_sequence_types.h"
+#include "DNA_constraint_types.h"
#include "DNA_object_force_types.h"
#include "BLI_math.h"
@@ -60,6 +61,7 @@
#include "ED_armature.h"
#include "ED_keyframing.h"
#include "ED_object.h"
+#include "ED_outliner.h"
#include "ED_screen.h"
#include "WM_api.h"
@@ -175,8 +177,9 @@ static void restrictbutton_r_lay_cb(bContext *C, void *poin, void *UNUSED(poin2)
WM_event_add_notifier(C, NC_SCENE | ND_RENDER_OPTIONS, poin);
}
-static void restrictbutton_bone_visibility_cb(bContext *C, void *UNUSED(poin), void *poin2)
+static void restrictbutton_bone_visibility_cb(bContext *C, void *poin, void *poin2)
{
+ bArmature *arm = (bArmature *)poin;
Bone *bone = (Bone *)poin2;
if (bone->flag & BONE_HIDDEN_P) {
bone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
@@ -187,6 +190,7 @@ static void restrictbutton_bone_visibility_cb(bContext *C, void *UNUSED(poin), v
}
WM_event_add_notifier(C, NC_OBJECT | ND_POSE, NULL);
+ DEG_id_tag_update(&arm->id, ID_RECALC_COPY_ON_WRITE);
}
static void restrictbutton_bone_select_cb(bContext *C, void *UNUSED(poin), void *poin2)
@@ -848,6 +852,7 @@ typedef struct RestrictProperties {
PropertyRNA *layer_collection_holdout, *layer_collection_indirect_only,
*layer_collection_hide_viewport;
PropertyRNA *modifier_show_viewport, *modifier_show_render;
+ PropertyRNA *constraint_enable;
} RestrictProperties;
/* We don't care about the value of the property
@@ -865,6 +870,7 @@ typedef struct RestrictPropertiesActive {
bool layer_collection_hide_viewport;
bool modifier_show_viewport;
bool modifier_show_render;
+ bool constraint_enable;
} RestrictPropertiesActive;
static void outliner_restrict_properties_enable_collection_set(
@@ -878,6 +884,7 @@ static void outliner_restrict_properties_enable_collection_set(
props_active->layer_collection_indirect_only = false;
props_active->object_hide_render = false;
props_active->modifier_show_render = false;
+ props_active->constraint_enable = false;
}
}
@@ -891,6 +898,7 @@ static void outliner_restrict_properties_enable_collection_set(
props_active->object_hide_viewport = false;
props_active->base_hide_viewport = false;
props_active->modifier_show_viewport = false;
+ props_active->constraint_enable = false;
}
}
@@ -995,6 +1003,8 @@ static void outliner_draw_restrictbuts(uiBlock *block,
props.modifier_show_viewport = RNA_struct_type_find_property(&RNA_Modifier, "show_viewport");
props.modifier_show_render = RNA_struct_type_find_property(&RNA_Modifier, "show_render");
+ props.constraint_enable = RNA_struct_type_find_property(&RNA_Constraint, "mute");
+
props.initialized = true;
}
@@ -1181,6 +1191,35 @@ static void outliner_draw_restrictbuts(uiBlock *block,
}
}
}
+ else if (tselem->type == TSE_CONSTRAINT) {
+ bConstraint *con = (bConstraint *)te->directdata;
+
+ PointerRNA ptr;
+ RNA_pointer_create(tselem->id, &RNA_Constraint, con, &ptr);
+
+ if (soops->show_restrict_flags & SO_RESTRICT_HIDE) {
+ bt = uiDefIconButR_prop(block,
+ UI_BTYPE_ICON_TOGGLE,
+ 0,
+ 0,
+ (int)(ar->v2d.cur.xmax - restrict_offsets.hide),
+ te->ys,
+ UI_UNIT_X,
+ UI_UNIT_Y,
+ &ptr,
+ props.constraint_enable,
+ -1,
+ 0,
+ 0,
+ -1,
+ -1,
+ NULL);
+ UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
+ if (!props_active.constraint_enable) {
+ UI_but_flag_enable(bt, UI_BUT_INACTIVE);
+ }
+ }
+ }
else if (tselem->type == TSE_MODIFIER) {
ModifierData *md = (ModifierData *)te->directdata;
@@ -1243,7 +1282,7 @@ static void outliner_draw_restrictbuts(uiBlock *block,
UI_BTYPE_ICON_TOGGLE,
BONE_HIDDEN_P,
0,
- ICON_HIDE_OFF,
+ ICON_RESTRICT_VIEW_OFF,
(int)(ar->v2d.cur.xmax - restrict_offsets.viewport),
te->ys,
UI_UNIT_X,
@@ -1878,6 +1917,9 @@ TreeElementIcon tree_element_get_icon(TreeStoreElem *tselem, TreeElement *te)
case TSE_DEFGROUP_BASE:
data.icon = ICON_GROUP_VERTEX;
break;
+ case TSE_DEFGROUP:
+ data.icon = ICON_GROUP_VERTEX;
+ break;
case TSE_BONE:
case TSE_EBONE:
data.icon = ICON_BONE_DATA;
@@ -1885,6 +1927,100 @@ TreeElementIcon tree_element_get_icon(TreeStoreElem *tselem, TreeElement *te)
case TSE_CONSTRAINT_BASE:
data.icon = ICON_CONSTRAINT;
break;
+ case TSE_CONSTRAINT: {
+ bConstraint *con = te->directdata;
+ switch ((eBConstraint_Types)con->type) {
+ case CONSTRAINT_TYPE_CAMERASOLVER:
+ data.icon = ICON_CON_CAMERASOLVER;
+ break;
+ case CONSTRAINT_TYPE_FOLLOWTRACK:
+ data.icon = ICON_CON_FOLLOWTRACK;
+ break;
+ case CONSTRAINT_TYPE_OBJECTSOLVER:
+ data.icon = ICON_CON_OBJECTSOLVER;
+ break;
+ case CONSTRAINT_TYPE_LOCLIKE:
+ data.icon = ICON_CON_LOCLIKE;
+ break;
+ case CONSTRAINT_TYPE_ROTLIKE:
+ data.icon = ICON_CON_ROTLIKE;
+ break;
+ case CONSTRAINT_TYPE_SIZELIKE:
+ data.icon = ICON_CON_SIZELIKE;
+ break;
+ case CONSTRAINT_TYPE_TRANSLIKE:
+ data.icon = ICON_CON_TRANSLIKE;
+ break;
+ case CONSTRAINT_TYPE_DISTLIMIT:
+ data.icon = ICON_CON_DISTLIMIT;
+ break;
+ case CONSTRAINT_TYPE_LOCLIMIT:
+ data.icon = ICON_CON_LOCLIMIT;
+ break;
+ case CONSTRAINT_TYPE_ROTLIMIT:
+ data.icon = ICON_CON_ROTLIMIT;
+ break;
+ case CONSTRAINT_TYPE_SIZELIMIT:
+ data.icon = ICON_CON_SIZELIMIT;
+ break;
+ case CONSTRAINT_TYPE_SAMEVOL:
+ data.icon = ICON_CON_SAMEVOL;
+ break;
+ case CONSTRAINT_TYPE_TRANSFORM:
+ data.icon = ICON_CON_TRANSFORM;
+ break;
+ case CONSTRAINT_TYPE_TRANSFORM_CACHE:
+ data.icon = ICON_CON_TRANSFORM_CACHE;
+ break;
+ case CONSTRAINT_TYPE_CLAMPTO:
+ data.icon = ICON_CON_CLAMPTO;
+ break;
+ case CONSTRAINT_TYPE_DAMPTRACK:
+ data.icon = ICON_CON_TRACKTO;
+ break;
+ case CONSTRAINT_TYPE_KINEMATIC:
+ data.icon = ICON_CON_KINEMATIC;
+ break;
+ case CONSTRAINT_TYPE_LOCKTRACK:
+ data.icon = ICON_CON_LOCKTRACK;
+ break;
+ case CONSTRAINT_TYPE_SPLINEIK:
+ data.icon = ICON_CON_SPLINEIK;
+ break;
+ case CONSTRAINT_TYPE_STRETCHTO:
+ data.icon = ICON_CON_STRETCHTO;
+ break;
+ case CONSTRAINT_TYPE_TRACKTO:
+ data.icon = ICON_CON_TRACKTO;
+ break;
+ case CONSTRAINT_TYPE_ACTION:
+ data.icon = ICON_CON_ACTION;
+ break;
+ case CONSTRAINT_TYPE_ARMATURE:
+ data.icon = ICON_CON_ARMATURE;
+ break;
+ case CONSTRAINT_TYPE_CHILDOF:
+ data.icon = ICON_CON_CHILDOF;
+ break;
+ case CONSTRAINT_TYPE_MINMAX:
+ data.icon = ICON_CON_FLOOR;
+ break;
+ case CONSTRAINT_TYPE_FOLLOWPATH:
+ data.icon = ICON_CON_FOLLOWPATH;
+ break;
+ case CONSTRAINT_TYPE_PIVOT:
+ data.icon = ICON_CON_PIVOT;
+ break;
+ case CONSTRAINT_TYPE_SHRINKWRAP:
+ data.icon = ICON_CON_SHRINKWRAP;
+ break;
+
+ default:
+ data.icon = ICON_DOT;
+ break;
+ }
+ break;
+ }
case TSE_MODIFIER_BASE:
data.icon = ICON_MODIFIER_DATA;
break;
@@ -2137,30 +2273,64 @@ TreeElementIcon tree_element_get_icon(TreeStoreElem *tselem, TreeElement *te)
data.icon = ICON_GROUP_BONE;
break;
case TSE_SEQUENCE:
- if (te->idcode == SEQ_TYPE_MOVIE) {
- data.icon = ICON_SEQUENCE;
- }
- else if (te->idcode == SEQ_TYPE_META) {
- data.icon = ICON_DOT;
- }
- else if (te->idcode == SEQ_TYPE_SCENE) {
- data.icon = ICON_SCENE;
- }
- else if (te->idcode == SEQ_TYPE_SOUND_RAM) {
- data.icon = ICON_SOUND;
- }
- else if (te->idcode == SEQ_TYPE_IMAGE) {
- data.icon = ICON_IMAGE;
- }
- else {
- data.icon = ICON_PARTICLES;
+ switch (te->idcode) {
+ case SEQ_TYPE_SCENE:
+ data.icon = ICON_SCENE_DATA;
+ break;
+ case SEQ_TYPE_MOVIECLIP:
+ data.icon = ICON_TRACKER;
+ break;
+ case SEQ_TYPE_MASK:
+ data.icon = ICON_MOD_MASK;
+ break;
+ case SEQ_TYPE_MOVIE:
+ data.icon = ICON_FILE_MOVIE;
+ break;
+ case SEQ_TYPE_SOUND_RAM:
+ data.icon = ICON_SOUND;
+ break;
+ case SEQ_TYPE_IMAGE:
+ data.icon = ICON_FILE_IMAGE;
+ break;
+ case SEQ_TYPE_COLOR:
+ case SEQ_TYPE_ADJUSTMENT:
+ data.icon = ICON_COLOR;
+ break;
+ case SEQ_TYPE_TEXT:
+ data.icon = ICON_FONT_DATA;
+ break;
+ case SEQ_TYPE_ADD:
+ case SEQ_TYPE_SUB:
+ case SEQ_TYPE_MUL:
+ case SEQ_TYPE_OVERDROP:
+ case SEQ_TYPE_ALPHAOVER:
+ case SEQ_TYPE_ALPHAUNDER:
+ case SEQ_TYPE_COLORMIX:
+ case SEQ_TYPE_MULTICAM:
+ case SEQ_TYPE_TRANSFORM:
+ case SEQ_TYPE_SPEED:
+ case SEQ_TYPE_GLOW:
+ case SEQ_TYPE_GAUSSIAN_BLUR:
+ data.icon = ICON_SHADERFX;
+ break;
+ case SEQ_TYPE_CROSS:
+ case SEQ_TYPE_GAMCROSS:
+ case SEQ_TYPE_WIPE:
+ data.icon = ICON_ARROW_LEFTRIGHT;
+ break;
+ case SEQ_TYPE_META:
+ data.icon = ICON_SEQ_STRIP_META;
+ break;
+ default:
+ data.icon = ICON_DOT;
+ break;
}
break;
case TSE_SEQ_STRIP:
data.icon = ICON_LIBRARY_DATA_DIRECT;
break;
case TSE_SEQUENCE_DUP:
- data.icon = ICON_OBJECT_DATA;
+ data.icon = ICON_SEQ_STRIP_DUPLICATE;
break;
case TSE_RNA_STRUCT:
if (RNA_struct_is_ID(te->rnaptr.type)) {
@@ -2459,7 +2629,11 @@ static void tselem_draw_icon(uiBlock *block,
return;
}
+ /* Icon is covered by restrict buttons */
if (!is_clickable || x >= xmax) {
+ /* Reduce alpha to match icon buttons */
+ alpha *= 0.8f;
+
/* placement of icons, copied from interface_widgets.c */
float aspect = (0.8f * UI_UNIT_Y) / ICON_DEFAULT_HEIGHT;
x += 2.0f * aspect;
@@ -2567,7 +2741,6 @@ static void outliner_draw_iconrow_doit(uiBlock *block,
float ufac = UI_UNIT_X / 20.0f;
float icon_color[4], icon_border[4];
outliner_icon_background_colors(icon_color, icon_border);
- icon_color[3] *= alpha_fac;
if (active == OL_DRAWSEL_ACTIVE) {
UI_GetThemeColor4fv(TH_EDITED_OBJECT, icon_color);
icon_border[3] = 0.3f;
@@ -2592,6 +2765,9 @@ static void outliner_draw_iconrow_doit(uiBlock *block,
GPU_blend(true); /* Roundbox disables. */
}
+ if (tselem->flag & TSE_HIGHLIGHTED) {
+ alpha_fac += 0.5;
+ }
tselem_draw_icon(block, xmax, (float)*offsx, (float)ys, tselem, te, alpha_fac, false);
te->xs = *offsx;
te->ys = ys;
@@ -2599,7 +2775,12 @@ static void outliner_draw_iconrow_doit(uiBlock *block,
if (num_elements > 1) {
outliner_draw_iconrow_number(fstyle, *offsx, ys, num_elements);
+ te->flag |= TE_ICONROW_MERGED;
}
+ else {
+ te->flag |= TE_ICONROW;
+ }
+
(*offsx) += UI_UNIT_X;
}
@@ -2609,7 +2790,7 @@ static void outliner_draw_iconrow_doit(uiBlock *block,
* We use a continuum of indices until we get to the object data-blocks
* and we then make room for the object types.
*/
-static int tree_element_id_type_to_index(TreeElement *te)
+int tree_element_id_type_to_index(TreeElement *te)
{
TreeStoreElem *tselem = TREESTORE(te);
@@ -2739,7 +2920,7 @@ static void outliner_set_coord_tree_element(TreeElement *te, int startx, int sta
TreeElement *ten;
/* closed items may be displayed in row of parent, don't change their coordinate! */
- if ((te->flag & TE_ICONROW) == 0) {
+ if ((te->flag & TE_ICONROW) == 0 && (te->flag & TE_ICONROW_MERGED) == 0) {
/* store coord and continue, we need coordinates for elements outside view too */
te->xs = startx;
te->ys = starty;
@@ -3193,6 +3374,7 @@ static void outliner_draw_highlights_recursive(unsigned pos,
const SpaceOutliner *soops,
const ListBase *lb,
const float col_selection[4],
+ const float col_active[4],
const float col_highlight[4],
const float col_searchmatch[4],
int start_x,
@@ -3206,7 +3388,11 @@ static void outliner_draw_highlights_recursive(unsigned pos,
const int start_y = *io_start_y;
/* selection status */
- if (tselem->flag & TSE_SELECTED) {
+ if ((tselem->flag & TSE_ACTIVE) && (tselem->flag & TSE_SELECTED)) {
+ immUniformColor4fv(col_active);
+ immRecti(pos, 0, start_y, (int)ar->v2d.cur.xmax, start_y + UI_UNIT_Y);
+ }
+ else if (tselem->flag & TSE_SELECTED) {
immUniformColor4fv(col_selection);
immRecti(pos, 0, start_y, (int)ar->v2d.cur.xmax, start_y + UI_UNIT_Y);
}
@@ -3260,6 +3446,7 @@ static void outliner_draw_highlights_recursive(unsigned pos,
soops,
&te->subtree,
col_selection,
+ col_active,
col_highlight,
col_searchmatch,
start_x + UI_UNIT_X,
@@ -3271,10 +3458,12 @@ static void outliner_draw_highlights_recursive(unsigned pos,
static void outliner_draw_highlights(ARegion *ar, SpaceOutliner *soops, int startx, int *starty)
{
const float col_highlight[4] = {1.0f, 1.0f, 1.0f, 0.13f};
- float col_selection[4], col_searchmatch[4];
+ float col_selection[4], col_active[4], col_searchmatch[4];
UI_GetThemeColor3fv(TH_SELECT_HIGHLIGHT, col_selection);
col_selection[3] = 1.0f; /* no alpha */
+ UI_GetThemeColor3fv(TH_SELECT_ACTIVE, col_active);
+ col_active[3] = 1.0f; /* no alpha */
UI_GetThemeColor4fv(TH_MATCH, col_searchmatch);
col_searchmatch[3] = 0.5f;
@@ -3282,8 +3471,16 @@ static void outliner_draw_highlights(ARegion *ar, SpaceOutliner *soops, int star
GPUVertFormat *format = immVertexFormat();
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
- outliner_draw_highlights_recursive(
- pos, ar, soops, &soops->tree, col_selection, col_highlight, col_searchmatch, startx, starty);
+ outliner_draw_highlights_recursive(pos,
+ ar,
+ soops,
+ &soops->tree,
+ col_selection,
+ col_active,
+ col_highlight,
+ col_searchmatch,
+ startx,
+ starty);
immUnbindProgram();
GPU_blend(false);
}
@@ -3439,6 +3636,17 @@ void draw_outliner(const bContext *C)
outliner_build_tree(mainvar, scene, view_layer, soops, ar); // always
+ /* If global sync select is dirty, flag other outliners */
+ if (ED_outliner_select_sync_is_dirty(C)) {
+ ED_outliner_select_sync_flag_outliners(C);
+ }
+
+ /* Sync selection state from view layer */
+ if (!ELEM(soops->outlinevis, SO_LIBRARIES, SO_DATA_API, SO_ID_ORPHANS) &&
+ soops->flag & SO_SYNC_SELECT) {
+ outliner_sync_selection(C, soops);
+ }
+
/* force display to pixel coords */
v2d->flag |= (V2D_PIXELOFS_X | V2D_PIXELOFS_Y);
/* set matrix for 2d-view controls */
diff --git a/source/blender/editors/space_outliner/outliner_edit.c b/source/blender/editors/space_outliner/outliner_edit.c
index de6e89e47c4..943993cb810 100644
--- a/source/blender/editors/space_outliner/outliner_edit.c
+++ b/source/blender/editors/space_outliner/outliner_edit.c
@@ -101,9 +101,15 @@ static int outliner_highlight_update(bContext *C, wmOperator *UNUSED(op), const
ARegion *ar = CTX_wm_region(C);
SpaceOutliner *soops = CTX_wm_space_outliner(C);
- const float my = UI_view2d_region_to_view_y(&ar->v2d, event->mval[1]);
- TreeElement *hovered_te = outliner_find_item_at_y(soops, &soops->tree, my);
+ float view_mval[2];
+ UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &view_mval[0], &view_mval[1]);
+
+ TreeElement *hovered_te = outliner_find_item_at_y(soops, &soops->tree, view_mval[1]);
+
+ if (hovered_te) {
+ hovered_te = outliner_find_item_at_x_in_row(soops, hovered_te, view_mval[0], NULL);
+ }
bool changed = false;
if (!hovered_te || !(hovered_te->store_elem->flag & TSE_HIGHLIGHTED)) {
@@ -134,59 +140,108 @@ void OUTLINER_OT_highlight_update(wmOperatorType *ot)
/* Toggle Open/Closed ------------------------------------------- */
-static int do_outliner_item_openclose(
- bContext *C, SpaceOutliner *soops, TreeElement *te, const bool all, const float mval[2])
+/* Open or close a tree element, optionally toggling all children recursively */
+void outliner_item_openclose(TreeElement *te, bool open, bool toggle_all)
{
+ TreeStoreElem *tselem = TREESTORE(te);
- if (mval[1] > te->ys && mval[1] < te->ys + UI_UNIT_Y) {
- TreeStoreElem *tselem = TREESTORE(te);
+ if (open) {
+ tselem->flag &= ~TSE_CLOSED;
+ }
+ else {
+ tselem->flag |= TSE_CLOSED;
+ }
- /* all below close/open? */
- if (all) {
- tselem->flag &= ~TSE_CLOSED;
- outliner_flag_set(
- &te->subtree, TSE_CLOSED, !outliner_flag_is_any_test(&te->subtree, TSE_CLOSED, 1));
- }
- else {
- if (tselem->flag & TSE_CLOSED) {
- tselem->flag &= ~TSE_CLOSED;
- }
- else {
- tselem->flag |= TSE_CLOSED;
+ if (toggle_all) {
+ outliner_flag_set(&te->subtree, TSE_CLOSED, !open);
+ }
+}
+
+typedef struct OpenCloseData {
+ TreeStoreElem *prev_tselem;
+ bool open;
+ int x_location;
+} OpenCloseData;
+
+static int outliner_item_openclose_modal(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ ARegion *ar = CTX_wm_region(C);
+ SpaceOutliner *soops = CTX_wm_space_outliner(C);
+
+ float view_mval[2];
+ UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &view_mval[0], &view_mval[1]);
+
+ if (event->type == MOUSEMOVE) {
+ TreeElement *te = outliner_find_item_at_y(soops, &soops->tree, view_mval[1]);
+
+ OpenCloseData *data = (OpenCloseData *)op->customdata;
+
+ /* Only openclose if mouse is not over the previously toggled element */
+ if (te && TREESTORE(te) != data->prev_tselem) {
+
+ /* Only toggle openclose on the same level as the first clicked element */
+ if (te->xs == data->x_location) {
+ outliner_item_openclose(te, data->open, false);
+ ED_region_tag_redraw(ar);
}
}
- return 1;
+ if (te) {
+ data->prev_tselem = TREESTORE(te);
+ }
+ else {
+ data->prev_tselem = NULL;
+ }
}
+ else if (event->val == KM_RELEASE) {
+ MEM_freeN(op->customdata);
- for (te = te->subtree.first; te; te = te->next) {
- if (do_outliner_item_openclose(C, soops, te, all, mval)) {
- return 1;
- }
+ return OPERATOR_FINISHED;
}
- return 0;
+
+ return OPERATOR_RUNNING_MODAL;
}
-/* event can enterkey, then it opens/closes */
-static int outliner_item_openclose(bContext *C, wmOperator *op, const wmEvent *event)
+static int outliner_item_openclose_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
ARegion *ar = CTX_wm_region(C);
SpaceOutliner *soops = CTX_wm_space_outliner(C);
- TreeElement *te;
- float fmval[2];
- const bool all = RNA_boolean_get(op->ptr, "all");
- UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]);
+ const bool toggle_all = RNA_boolean_get(op->ptr, "all");
- for (te = soops->tree.first; te; te = te->next) {
- if (do_outliner_item_openclose(C, soops, te, all, fmval)) {
- break;
+ float view_mval[2];
+ UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &view_mval[0], &view_mval[1]);
+
+ TreeElement *te = outliner_find_item_at_y(soops, &soops->tree, view_mval[1]);
+
+ if (te && outliner_item_is_co_within_close_toggle(te, view_mval[0])) {
+ TreeStoreElem *tselem = TREESTORE(te);
+
+ const bool open = (tselem->flag & TSE_CLOSED) ||
+ (toggle_all && (outliner_flag_is_any_test(&te->subtree, TSE_CLOSED, 1)));
+
+ outliner_item_openclose(te, open, toggle_all);
+ ED_region_tag_redraw(ar);
+
+ /* Only toggle once for single click toggling */
+ if (event->type == LEFTMOUSE) {
+ return OPERATOR_FINISHED;
}
- }
- ED_region_tag_redraw(ar);
+ /* Store last expanded tselem and x coordinate of disclosure triangle */
+ OpenCloseData *toggle_data = MEM_callocN(sizeof(OpenCloseData), "open_close_data");
+ toggle_data->prev_tselem = tselem;
+ toggle_data->open = open;
+ toggle_data->x_location = te->xs;
- return OPERATOR_FINISHED;
+ /* Store the first clicked on element */
+ op->customdata = toggle_data;
+
+ WM_event_add_modal_handler(C, op);
+ return OPERATOR_RUNNING_MODAL;
+ }
+
+ return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
}
void OUTLINER_OT_item_openclose(wmOperatorType *ot)
@@ -195,11 +250,12 @@ void OUTLINER_OT_item_openclose(wmOperatorType *ot)
ot->idname = "OUTLINER_OT_item_openclose";
ot->description = "Toggle whether item under cursor is enabled or closed";
- ot->invoke = outliner_item_openclose;
+ ot->invoke = outliner_item_openclose_invoke;
+ ot->modal = outliner_item_openclose_modal;
ot->poll = ED_operator_outliner_active;
- RNA_def_boolean(ot->srna, "all", 1, "All", "Close or open all items");
+ RNA_def_boolean(ot->srna, "all", false, "All", "Close or open all items");
}
/* -------------------------------------------------------------------- */
@@ -330,10 +386,10 @@ void item_rename_cb(bContext *C,
do_item_rename(ar, te, tselem, reports);
}
-static int do_outliner_item_rename(ReportList *reports,
- ARegion *ar,
- TreeElement *te,
- const float mval[2])
+static void do_outliner_item_rename(ReportList *reports,
+ ARegion *ar,
+ TreeElement *te,
+ const float mval[2])
{
if (mval[1] > te->ys && mval[1] < te->ys + UI_UNIT_Y) {
TreeStoreElem *tselem = TREESTORE(te);
@@ -341,17 +397,12 @@ static int do_outliner_item_rename(ReportList *reports,
/* click on name */
if (mval[0] > te->xs + UI_UNIT_X * 2 && mval[0] < te->xend) {
do_item_rename(ar, te, tselem, reports);
- return 1;
}
- return 0;
}
for (te = te->subtree.first; te; te = te->next) {
- if (do_outliner_item_rename(reports, ar, te, mval)) {
- return 1;
- }
+ do_outliner_item_rename(reports, ar, te, mval);
}
- return 0;
}
static int outliner_item_rename(bContext *C, wmOperator *op, const wmEvent *event)
@@ -360,25 +411,34 @@ static int outliner_item_rename(bContext *C, wmOperator *op, const wmEvent *even
SpaceOutliner *soops = CTX_wm_space_outliner(C);
TreeElement *te;
float fmval[2];
- bool changed = false;
- UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]);
+ /* Rename active element if key pressed, otherwise rename element at cursor coordinates */
+ if (event->val == KM_PRESS) {
+ TreeElement *active_element = outliner_find_element_with_flag(&soops->tree, TSE_ACTIVE);
- for (te = soops->tree.first; te; te = te->next) {
- if (do_outliner_item_rename(op->reports, ar, te, fmval)) {
- changed = true;
- break;
+ if (active_element) {
+ do_item_rename(ar, active_element, TREESTORE(active_element), op->reports);
+ }
+ else {
+ BKE_report(op->reports, RPT_WARNING, "No active item to rename");
}
}
+ else {
+ UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]);
- return changed ? OPERATOR_FINISHED : OPERATOR_PASS_THROUGH;
+ for (te = soops->tree.first; te; te = te->next) {
+ do_outliner_item_rename(op->reports, ar, te, fmval);
+ }
+ }
+
+ return OPERATOR_FINISHED;
}
void OUTLINER_OT_item_rename(wmOperatorType *ot)
{
ot->name = "Rename";
ot->idname = "OUTLINER_OT_item_rename";
- ot->description = "Rename item under cursor";
+ ot->description = "Rename the active element";
ot->invoke = outliner_item_rename;
@@ -1103,6 +1163,10 @@ static int outliner_select_all_exec(bContext *C, wmOperator *op)
break;
}
+ if (soops->flag & SO_SYNC_SELECT) {
+ ED_outliner_select_sync_from_outliner(C, soops);
+ }
+
DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
ED_region_tag_redraw_no_rebuild(ar);
@@ -1179,20 +1243,17 @@ static int outliner_open_back(TreeElement *te)
return retval;
}
-static int outliner_show_active_exec(bContext *C, wmOperator *UNUSED(op))
+/* Return element representing the active base or bone in the outliner, or NULL if none exists */
+static TreeElement *outliner_show_active_get_element(bContext *C,
+ SpaceOutliner *so,
+ ViewLayer *view_layer)
{
- SpaceOutliner *so = CTX_wm_space_outliner(C);
- ViewLayer *view_layer = CTX_data_view_layer(C);
- ARegion *ar = CTX_wm_region(C);
- View2D *v2d = &ar->v2d;
-
TreeElement *te;
- int xdelta, ytop;
Object *obact = OBACT(view_layer);
if (!obact) {
- return OPERATOR_CANCELLED;
+ return NULL;
}
te = outliner_find_id(so, &so->tree, &obact->id);
@@ -1215,25 +1276,50 @@ static int outliner_show_active_exec(bContext *C, wmOperator *UNUSED(op))
}
}
- if (te) {
- /* open up tree to active object/bone */
+ return te;
+}
+
+static void outliner_show_active(SpaceOutliner *so, ARegion *ar, TreeElement *te, ID *id)
+{
+ /* open up tree to active object/bone */
+ if (TREESTORE(te)->id == id) {
if (outliner_open_back(te)) {
outliner_set_coordinates(ar, so);
}
+ return;
+ }
+
+ for (TreeElement *ten = te->subtree.first; ten; ten = ten->next) {
+ outliner_show_active(so, ar, ten, id);
+ }
+}
+
+static int outliner_show_active_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ SpaceOutliner *so = CTX_wm_space_outliner(C);
+ ViewLayer *view_layer = CTX_data_view_layer(C);
+ ARegion *ar = CTX_wm_region(C);
+ View2D *v2d = &ar->v2d;
+
+ TreeElement *active_element = outliner_show_active_get_element(C, so, view_layer);
- /* make te->ys center of view */
- ytop = te->ys + BLI_rcti_size_y(&v2d->mask) / 2;
- if (ytop > 0) {
- ytop = 0;
+ if (active_element) {
+ ID *id = TREESTORE(active_element)->id;
+
+ /* Expand all elements in the outliner with matching ID */
+ for (TreeElement *te = so->tree.first; te; te = te->next) {
+ outliner_show_active(so, ar, te, id);
}
- v2d->cur.ymax = (float)ytop;
- v2d->cur.ymin = (float)(ytop - BLI_rcti_size_y(&v2d->mask));
+ /* Center view on first element found */
+ int size_y = BLI_rcti_size_y(&v2d->mask) + 1;
+ int ytop = (active_element->ys + (size_y / 2));
+ int delta_y = ytop - v2d->cur.ymax;
- /* make te->xs ==> te->xend center of view */
- xdelta = (int)(te->xs - v2d->cur.xmin);
- v2d->cur.xmin += xdelta;
- v2d->cur.xmax += xdelta;
+ outliner_scroll_view(ar, delta_y);
+ }
+ else {
+ return OPERATOR_CANCELLED;
}
ED_region_tag_redraw_no_rebuild(ar);
@@ -1259,18 +1345,15 @@ void OUTLINER_OT_show_active(wmOperatorType *ot)
static int outliner_scroll_page_exec(bContext *C, wmOperator *op)
{
ARegion *ar = CTX_wm_region(C);
- int dy = BLI_rcti_size_y(&ar->v2d.mask);
- int up = 0;
+ int size_y = BLI_rcti_size_y(&ar->v2d.mask) + 1;
- if (RNA_boolean_get(op->ptr, "up")) {
- up = 1;
- }
+ bool up = RNA_boolean_get(op->ptr, "up");
- if (up == 0) {
- dy = -dy;
+ if (!up) {
+ size_y = -size_y;
}
- ar->v2d.cur.ymin += dy;
- ar->v2d.cur.ymax += dy;
+
+ outliner_scroll_view(ar, size_y);
ED_region_tag_redraw_no_rebuild(ar);
@@ -1697,7 +1780,7 @@ static void tree_element_to_path(TreeElement *te,
/* no ID, so check if entry is RNA-struct,
* and if that RNA-struct is an ID datablock to extract info from. */
if (tse->type == TSE_RNA_STRUCT) {
- /* ptr->data not ptr->id.data seems to be the one we want,
+ /* ptr->data not ptr->owner_id seems to be the one we want,
* since ptr->data is sometimes the owner of this ID? */
if (RNA_struct_is_ID(ptr->type)) {
*id = (ID *)ptr->data;
diff --git a/source/blender/editors/space_outliner/outliner_intern.h b/source/blender/editors/space_outliner/outliner_intern.h
index fa28d119244..95e37dea249 100644
--- a/source/blender/editors/space_outliner/outliner_intern.h
+++ b/source/blender/editors/space_outliner/outliner_intern.h
@@ -40,7 +40,6 @@ struct TreeStoreElem;
struct ViewLayer;
struct bContext;
struct bPoseChannel;
-struct wmEvent;
struct wmKeyConfig;
struct wmOperatorType;
@@ -50,6 +49,14 @@ typedef enum TreeElementInsertType {
TE_INSERT_INTO,
} TreeElementInsertType;
+/* Use generic walk select after D4771 is committed */
+typedef enum WalkSelectDirection {
+ OUTLINER_SELECT_WALK_UP,
+ OUTLINER_SELECT_WALK_DOWN,
+ OUTLINER_SELECT_WALK_LEFT,
+ OUTLINER_SELECT_WALK_RIGHT,
+} WalkSelectDirection;
+
typedef enum TreeTraversalAction {
/* Continue traversal regularly, don't skip children. */
TRAVERSE_CONTINUE = 0,
@@ -131,6 +138,9 @@ enum {
TE_DISABLED = (1 << 4),
TE_DRAGGING = (1 << 5),
TE_CHILD_NOT_IN_COLLECTION = (1 << 6),
+ /* Child elements of the same type in the icon-row are drawn merged as one icon.
+ * This flag is set for an element that is part of these merged child icons. */
+ TE_ICONROW_MERGED = (1 << 7),
};
/* button events */
@@ -223,6 +233,8 @@ void outliner_collection_isolate_flag(struct Scene *scene,
const char *propname,
const bool value);
+int tree_element_id_type_to_index(TreeElement *te);
+
/* outliner_select.c -------------------------------------------- */
eOLDrawState tree_element_type_active(struct bContext *C,
struct Scene *scene,
@@ -253,6 +265,10 @@ void outliner_object_mode_toggle(struct bContext *C,
ViewLayer *view_layer,
Base *base);
+void outliner_element_activate(struct SpaceOutliner *soops, struct TreeStoreElem *tselem);
+
+bool outliner_item_is_co_within_close_toggle(TreeElement *te, float view_co_x);
+
/* outliner_edit.c ---------------------------------------------- */
typedef void (*outliner_operation_cb)(struct bContext *C,
struct ReportList *,
@@ -337,6 +353,8 @@ void item_object_mode_exit_cb(struct bContext *C,
void outliner_set_coordinates(struct ARegion *ar, struct SpaceOutliner *soops);
+void outliner_item_openclose(TreeElement *te, bool open, bool toggle_all);
+
/* outliner_dragdrop.c */
void outliner_dropboxes(void);
@@ -364,6 +382,7 @@ void OUTLINER_OT_show_active(struct wmOperatorType *ot);
void OUTLINER_OT_show_hierarchy(struct wmOperatorType *ot);
void OUTLINER_OT_select_box(struct wmOperatorType *ot);
+void OUTLINER_OT_select_walk(struct wmOperatorType *ot);
void OUTLINER_OT_select_all(struct wmOperatorType *ot);
void OUTLINER_OT_expanded_toggle(struct wmOperatorType *ot);
@@ -380,6 +399,10 @@ void OUTLINER_OT_orphans_purge(struct wmOperatorType *ot);
/* outliner_tools.c ---------------------------------------------- */
+void merged_element_search_menu_invoke(struct bContext *C,
+ TreeElement *parent_te,
+ TreeElement *activate_te);
+
void OUTLINER_OT_operation(struct wmOperatorType *ot);
void OUTLINER_OT_scene_operation(struct wmOperatorType *ot);
void OUTLINER_OT_object_operation(struct wmOperatorType *ot);
@@ -439,7 +462,8 @@ TreeElement *outliner_find_item_at_y(const SpaceOutliner *soops,
float view_co_y);
TreeElement *outliner_find_item_at_x_in_row(const SpaceOutliner *soops,
const TreeElement *parent_te,
- float view_co_x);
+ float view_co_x,
+ bool *multiple_objects);
TreeElement *outliner_find_tse(struct SpaceOutliner *soops, const TreeStoreElem *tse);
TreeElement *outliner_find_tree_element(ListBase *lb, const TreeStoreElem *store_elem);
TreeElement *outliner_find_parent_element(ListBase *lb,
@@ -456,5 +480,12 @@ bool outliner_tree_traverse(const SpaceOutliner *soops,
TreeTraversalFunc func,
void *customdata);
float outliner_restrict_columns_width(const struct SpaceOutliner *soops);
+TreeElement *outliner_find_element_with_flag(const ListBase *lb, short flag);
+bool outliner_is_element_visible(const TreeElement *te);
+void outliner_scroll_view(struct ARegion *ar, int delta_y);
+
+/* outliner_sync.c ---------------------------------------------- */
+
+void outliner_sync_selection(const struct bContext *C, struct SpaceOutliner *soops);
#endif /* __OUTLINER_INTERN_H__ */
diff --git a/source/blender/editors/space_outliner/outliner_ops.c b/source/blender/editors/space_outliner/outliner_ops.c
index f155a2d5f89..4b57d4ad771 100644
--- a/source/blender/editors/space_outliner/outliner_ops.c
+++ b/source/blender/editors/space_outliner/outliner_ops.c
@@ -50,6 +50,7 @@ void outliner_operatortypes(void)
WM_operatortype_append(OUTLINER_OT_highlight_update);
WM_operatortype_append(OUTLINER_OT_item_activate);
WM_operatortype_append(OUTLINER_OT_select_box);
+ WM_operatortype_append(OUTLINER_OT_select_walk);
WM_operatortype_append(OUTLINER_OT_item_openclose);
WM_operatortype_append(OUTLINER_OT_item_rename);
WM_operatortype_append(OUTLINER_OT_item_drag_drop);
diff --git a/source/blender/editors/space_outliner/outliner_select.c b/source/blender/editors/space_outliner/outliner_select.c
index 7f45c4d22fa..44e67fa1508 100644
--- a/source/blender/editors/space_outliner/outliner_select.c
+++ b/source/blender/editors/space_outliner/outliner_select.c
@@ -51,14 +51,16 @@
#include "BKE_workspace.h"
#include "DEG_depsgraph.h"
+#include "DEG_depsgraph_build.h"
#include "ED_armature.h"
+#include "ED_gpencil.h"
#include "ED_object.h"
+#include "ED_outliner.h"
#include "ED_screen.h"
#include "ED_select_utils.h"
#include "ED_sequencer.h"
#include "ED_undo.h"
-#include "ED_gpencil.h"
#include "WM_api.h"
#include "WM_types.h"
@@ -251,9 +253,7 @@ static eOLDrawState active_viewlayer(bContext *C,
}
/**
- * Select object tree:
- * CTRL+LMB: Select/Deselect object and all children.
- * CTRL+SHIFT+LMB: Add/Remove object and all children.
+ * Select object tree
*/
static void do_outliner_object_select_recursive(ViewLayer *view_layer,
Object *ob_parent,
@@ -450,9 +450,9 @@ static eOLDrawState tree_element_active_material(bContext *C,
return OL_DRAWSEL_NONE;
}
-static eOLDrawState tree_element_active_camera(bContext *UNUSED(C),
+static eOLDrawState tree_element_active_camera(bContext *C,
Scene *scene,
- ViewLayer *UNUSED(sl),
+ ViewLayer *UNUSED(view_layer),
SpaceOutliner *soops,
TreeElement *te,
const eOLSetState set)
@@ -460,10 +460,21 @@ static eOLDrawState tree_element_active_camera(bContext *UNUSED(C),
Object *ob = (Object *)outliner_search_back(soops, te, ID_OB);
if (set != OL_SETSEL_NONE) {
+ scene->camera = ob;
+
+ Main *bmain = CTX_data_main(C);
+ wmWindowManager *wm = bmain->wm.first;
+
+ WM_windows_scene_data_sync(&wm->windows, scene);
+ DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE);
+ DEG_relations_tag_update(bmain);
+ WM_event_add_notifier(C, NC_SCENE | NA_EDITED, NULL);
+
return OL_DRAWSEL_NONE;
}
-
- return scene->camera == ob;
+ else {
+ return scene->camera == ob;
+ }
}
static eOLDrawState tree_element_active_world(bContext *C,
@@ -1083,6 +1094,13 @@ eOLDrawState tree_element_type_active(bContext *C,
/* ================================================ */
+/* Activate a tree store element and set the walk navigation start element */
+void outliner_element_activate(SpaceOutliner *soops, TreeStoreElem *tselem)
+{
+ outliner_flag_set(&soops->tree, TSE_ACTIVE | TSE_ACTIVE_WALK, false);
+ tselem->flag |= TSE_ACTIVE | TSE_ACTIVE_WALK;
+}
+
/**
* Action when clicking to activate an item (typically under the mouse cursor),
* but don't do any cursor intersection checks.
@@ -1114,7 +1132,8 @@ static void do_outliner_item_activate_tree_element(bContext *C,
else if (tselem->type == TSE_POSE_BASE) {
/* Support pose mode toggle, keeping the active object as is. */
}
- else {
+ else if (soops->flag & SO_SYNC_SELECT) {
+ /* Only activate when synced selection is enabled */
tree_element_set_active_object(C,
scene,
view_layer,
@@ -1125,6 +1144,9 @@ static void do_outliner_item_activate_tree_element(bContext *C,
recursive && tselem->type == 0);
}
+ /* Mark as active in the outliner */
+ outliner_element_activate(soops, tselem);
+
if (tselem->type == 0) { // the lib blocks
/* editmode? */
if (te->idcode == ID_SCE) {
@@ -1189,7 +1211,7 @@ static void do_outliner_item_activate_tree_element(bContext *C,
tree_element_active(C, scene, view_layer, soops, te, OL_SETSEL_NORMAL, false);
}
}
- else {
+ else if (soops->flag & SO_SYNC_SELECT) {
tree_element_type_active(C,
scene,
view_layer,
@@ -1211,7 +1233,8 @@ void outliner_item_select(SpaceOutliner *soops,
const bool toggle)
{
TreeStoreElem *tselem = TREESTORE(te);
- const short new_flag = toggle ? (tselem->flag ^ TSE_SELECTED) : (tselem->flag | TSE_SELECTED);
+ const short new_flag = (toggle && (tselem->flag & TSE_ACTIVE)) ? (tselem->flag ^ TSE_SELECTED) :
+ (tselem->flag | TSE_SELECTED);
if (extend == false) {
outliner_flag_set(&soops->tree, TSE_SELECTED, false);
@@ -1219,24 +1242,72 @@ void outliner_item_select(SpaceOutliner *soops,
tselem->flag = new_flag;
}
-static void outliner_item_toggle_closed(TreeElement *te, const bool toggle_children)
+static bool do_outliner_range_select_recursive(ListBase *lb,
+ TreeElement *active,
+ TreeElement *cursor,
+ bool selecting)
{
- TreeStoreElem *tselem = TREESTORE(te);
- if (toggle_children) {
- tselem->flag &= ~TSE_CLOSED;
+ for (TreeElement *te = lb->first; te; te = te->next) {
+ TreeStoreElem *tselem = TREESTORE(te);
- const bool all_opened = !outliner_flag_is_any_test(&te->subtree, TSE_CLOSED, 1);
- outliner_flag_set(&te->subtree, TSE_CLOSED, all_opened);
- }
- else {
- tselem->flag ^= TSE_CLOSED;
+ if (selecting) {
+ tselem->flag |= TSE_SELECTED;
+ }
+
+ /* Set state for selection */
+ if (te == active || te == cursor) {
+ selecting = !selecting;
+ }
+
+ if (selecting) {
+ tselem->flag |= TSE_SELECTED;
+ }
+
+ /* Don't look inside closed elements */
+ if (!(tselem->flag & TSE_CLOSED)) {
+ selecting = do_outliner_range_select_recursive(&te->subtree, active, cursor, selecting);
+ }
}
+
+ return selecting;
}
-static bool outliner_item_is_co_within_close_toggle(TreeElement *te, float view_co_x)
+/* Select a range of items between cursor and active element */
+static void do_outliner_range_select(bContext *C,
+ SpaceOutliner *soops,
+ TreeElement *cursor,
+ const bool extend)
{
- return ((te->flag & TE_ICONROW) == 0) && (view_co_x > te->xs) &&
- (view_co_x < te->xs + UI_UNIT_X);
+ TreeElement *active = outliner_find_element_with_flag(&soops->tree, TSE_ACTIVE);
+
+ /* If no active element exists, activate the element under the cursor */
+ if (!active) {
+ outliner_item_select(soops, cursor, false, false);
+ outliner_item_do_activate_from_tree_element(C, cursor, TREESTORE(cursor), false, false);
+ return;
+ }
+
+ TreeStoreElem *tselem = TREESTORE(active);
+ const bool active_selected = (tselem->flag & TSE_SELECTED);
+
+ if (!extend) {
+ outliner_flag_set(&soops->tree, TSE_SELECTED, false);
+ }
+
+ /* Select active if under cursor */
+ if (active == cursor) {
+ TREESTORE(cursor)->flag |= TSE_SELECTED;
+ return;
+ }
+
+ /* If active is not selected, select the element under the cursor */
+ if (!active_selected || !outliner_is_element_visible(active)) {
+ outliner_item_select(soops, cursor, false, false);
+ outliner_item_do_activate_from_tree_element(C, cursor, TREESTORE(cursor), false, false);
+ return;
+ }
+
+ do_outliner_range_select_recursive(&soops->tree, active, cursor, false);
}
static bool outliner_is_co_within_restrict_columns(const SpaceOutliner *soops,
@@ -1247,7 +1318,7 @@ static bool outliner_is_co_within_restrict_columns(const SpaceOutliner *soops,
}
/**
- * A version of #outliner_item_do_acticate_from_cursor that takes the tree element directly.
+ * A version of #outliner_item_do_activate_from_cursor that takes the tree element directly.
* and doesn't depend on the pointer position.
*
* This allows us to simulate clicking on an item without dealing with the mouse cursor.
@@ -1271,10 +1342,11 @@ void outliner_item_do_activate_from_tree_element(
static int outliner_item_do_activate_from_cursor(bContext *C,
const int mval[2],
const bool extend,
- const bool recursive,
+ const bool use_range,
const bool deselect_all)
{
ARegion *ar = CTX_wm_region(C);
+ Scene *scene = CTX_data_scene(C);
SpaceOutliner *soops = CTX_wm_space_outliner(C);
TreeElement *te;
float view_mval[2];
@@ -1292,28 +1364,36 @@ static int outliner_item_do_activate_from_cursor(bContext *C,
changed = true;
}
}
- else if (outliner_item_is_co_within_close_toggle(te, view_mval[0])) {
- outliner_item_toggle_closed(te, extend);
- changed = true;
- rebuild_tree = true;
+ /* Don't allow toggle on scene collection */
+ else if ((TREESTORE(te)->type != TSE_VIEW_COLLECTION_BASE) &&
+ outliner_item_is_co_within_close_toggle(te, view_mval[0])) {
+ return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
}
else {
- Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- /* the row may also contain children, if one is hovered we want this instead of current te */
- TreeElement *activate_te = outliner_find_item_at_x_in_row(soops, te, view_mval[0]);
- TreeStoreElem *activate_tselem = TREESTORE(activate_te);
- outliner_item_select(soops, activate_te, extend, extend);
+ /* The row may also contain children, if one is hovered we want this instead of current te */
+ bool merged_elements = false;
+ TreeElement *activate_te = outliner_find_item_at_x_in_row(
+ soops, te, view_mval[0], &merged_elements);
- /* Only change modes when clicking on the icon/text,
- * otherwise we can't easily select without changing modes. */
- if ((te->flag & TE_ICONROW) == 0) {
- if (view_mval[0] >= te->xs && view_mval[0] <= te->xend) {
- do_outliner_item_activate_tree_element(
- C, scene, view_layer, soops, activate_te, activate_tselem, extend, recursive);
- }
+ /* If the selected icon was an aggregate of multiple elements, run the search popup */
+ if (merged_elements) {
+ merged_element_search_menu_invoke(C, te, activate_te);
+ return OPERATOR_CANCELLED;
+ }
+
+ TreeStoreElem *activate_tselem = TREESTORE(activate_te);
+
+ if (use_range) {
+ do_outliner_range_select(C, soops, activate_te, extend);
+ }
+ else {
+ outliner_item_select(soops, activate_te, extend, extend);
+ do_outliner_item_activate_tree_element(
+ C, scene, view_layer, soops, activate_te, activate_tselem, extend, false);
}
+
changed = true;
}
@@ -1324,7 +1404,10 @@ static int outliner_item_do_activate_from_cursor(bContext *C,
else {
ED_region_tag_redraw_no_rebuild(ar);
}
- ED_undo_push(C, "Outliner selection change");
+
+ if (soops->flag & SO_SYNC_SELECT) {
+ ED_outliner_select_sync_from_outliner(C, soops);
+ }
}
return OPERATOR_FINISHED;
@@ -1334,9 +1417,9 @@ static int outliner_item_do_activate_from_cursor(bContext *C,
static int outliner_item_activate_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
const bool extend = RNA_boolean_get(op->ptr, "extend");
- const bool recursive = RNA_boolean_get(op->ptr, "recursive");
+ const bool use_range = RNA_boolean_get(op->ptr, "extend_range");
const bool deselect_all = RNA_boolean_get(op->ptr, "deselect_all");
- return outliner_item_do_activate_from_cursor(C, event->mval, extend, recursive, deselect_all);
+ return outliner_item_do_activate_from_cursor(C, event->mval, extend, use_range, deselect_all);
}
void OUTLINER_OT_item_activate(wmOperatorType *ot)
@@ -1349,9 +1432,14 @@ void OUTLINER_OT_item_activate(wmOperatorType *ot)
ot->poll = ED_operator_outliner_active;
+ ot->flag |= OPTYPE_REGISTER | OPTYPE_UNDO;
+
PropertyRNA *prop;
RNA_def_boolean(ot->srna, "extend", true, "Extend", "Extend selection for activation");
- RNA_def_boolean(ot->srna, "recursive", false, "Recursive", "Select Objects and their children");
+ prop = RNA_def_boolean(
+ ot->srna, "extend_range", false, "Extend Range", "Select a range from active element");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+
prop = RNA_def_boolean(ot->srna,
"deselect_all",
false,
@@ -1409,9 +1497,44 @@ static int outliner_box_select_exec(bContext *C, wmOperator *op)
WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
ED_region_tag_redraw(ar);
+ if (soops->flag & SO_SYNC_SELECT) {
+ ED_outliner_select_sync_from_outliner(C, soops);
+ }
+
return OPERATOR_FINISHED;
}
+/* Find if x coordinate is over an icon or name */
+static bool outliner_item_is_co_over_name_icons(TreeElement *te, float view_co_x)
+{
+ /* Special case: count area left of Scene Collection as empty space */
+ bool outside_left = (TREESTORE(te)->type == TSE_VIEW_COLLECTION_BASE) ?
+ (view_co_x > te->xs + UI_UNIT_X) :
+ (view_co_x > te->xs);
+
+ return outside_left && (view_co_x < te->xend);
+}
+
+static int outliner_box_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ SpaceOutliner *soops = CTX_wm_space_outliner(C);
+ ARegion *ar = CTX_wm_region(C);
+ float view_mval[2];
+ const bool tweak = RNA_boolean_get(op->ptr, "tweak");
+
+ UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &view_mval[0], &view_mval[1]);
+
+ /* Find element clicked on */
+ TreeElement *te = outliner_find_item_at_y(soops, &soops->tree, view_mval[1]);
+
+ /* Pass through if click is over name or icons, or not tweak event */
+ if (te && tweak && outliner_item_is_co_over_name_icons(te, view_mval[0])) {
+ return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
+ }
+
+ return WM_gesture_box_invoke(C, op, event);
+}
+
void OUTLINER_OT_select_box(wmOperatorType *ot)
{
/* identifiers */
@@ -1420,7 +1543,7 @@ void OUTLINER_OT_select_box(wmOperatorType *ot)
ot->description = "Use box selection to select tree elements";
/* api callbacks */
- ot->invoke = WM_gesture_box_invoke;
+ ot->invoke = outliner_box_select_invoke;
ot->exec = outliner_box_select_exec;
ot->modal = WM_gesture_box_modal;
ot->cancel = WM_gesture_box_cancel;
@@ -1431,8 +1554,242 @@ void OUTLINER_OT_select_box(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
+ PropertyRNA *prop;
+
+ prop = RNA_def_boolean(
+ ot->srna, "tweak", false, "Tweak", "Tweak gesture from empty space for box selection");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+
WM_operator_properties_gesture_box(ot);
WM_operator_properties_select_operation_simple(ot);
}
/* ****************************************************** */
+
+/* **************** Walk Select Tool ****************** */
+
+/* Given a tree element return the rightmost child that is visible in the outliner */
+static TreeElement *outliner_find_rightmost_visible_child(SpaceOutliner *soops, TreeElement *te)
+{
+ while (te->subtree.last) {
+ if (TSELEM_OPEN(TREESTORE(te), soops)) {
+ te = te->subtree.last;
+ }
+ else {
+ break;
+ }
+ }
+ return te;
+}
+
+/* Find previous visible element in the tree */
+static TreeElement *outliner_find_previous_element(SpaceOutliner *soops, TreeElement *walk_element)
+{
+ if (walk_element->prev) {
+ walk_element = outliner_find_rightmost_visible_child(soops, walk_element->prev);
+ }
+ else if (walk_element->parent) {
+ /* Use parent if at beginning of list */
+ walk_element = walk_element->parent;
+ }
+
+ return walk_element;
+}
+
+/* Recursively search up the tree until a successor to a given element is found */
+static TreeElement *outliner_element_find_successor_in_parents(TreeElement *te)
+{
+ TreeElement *successor = te;
+ while (successor->parent) {
+ if (successor->parent->next) {
+ te = successor->parent->next;
+ break;
+ }
+ else {
+ successor = successor->parent;
+ }
+ }
+
+ return te;
+}
+
+/* Find next visible element in the tree */
+static TreeElement *outliner_find_next_element(SpaceOutliner *soops, TreeElement *walk_element)
+{
+ TreeStoreElem *tselem = TREESTORE(walk_element);
+
+ if (TSELEM_OPEN(tselem, soops) && walk_element->subtree.first) {
+ walk_element = walk_element->subtree.first;
+ }
+ else if (walk_element->next) {
+ walk_element = walk_element->next;
+ }
+ else {
+ walk_element = outliner_element_find_successor_in_parents(walk_element);
+ }
+
+ return walk_element;
+}
+
+static TreeElement *do_outliner_select_walk(SpaceOutliner *soops,
+ TreeElement *walk_element,
+ const int direction,
+ const bool extend,
+ const bool toggle_all)
+{
+ TreeStoreElem *tselem = TREESTORE(walk_element);
+
+ if (!extend) {
+ outliner_flag_set(&soops->tree, TSE_SELECTED, false);
+ }
+ tselem->flag &= ~TSE_ACTIVE_WALK;
+
+ switch (direction) {
+ case OUTLINER_SELECT_WALK_UP:
+ walk_element = outliner_find_previous_element(soops, walk_element);
+ break;
+ case OUTLINER_SELECT_WALK_DOWN:
+ walk_element = outliner_find_next_element(soops, walk_element);
+ break;
+ case OUTLINER_SELECT_WALK_LEFT:
+ outliner_item_openclose(walk_element, false, toggle_all);
+ break;
+ case OUTLINER_SELECT_WALK_RIGHT:
+ outliner_item_openclose(walk_element, true, toggle_all);
+ break;
+ }
+
+ TreeStoreElem *tselem_new = TREESTORE(walk_element);
+
+ /* If new element is already selected, deselect the previous element */
+ if (extend) {
+ tselem->flag = (tselem_new->flag & TSE_SELECTED) ? (tselem->flag & ~TSE_SELECTED) :
+ (tselem->flag | TSE_SELECTED);
+ }
+
+ tselem_new->flag |= TSE_SELECTED | TSE_ACTIVE_WALK;
+
+ return walk_element;
+}
+
+/* Find walk select element, or set it if it does not exist.
+ * Changed is set to true if walk element is found, false if it was set */
+static TreeElement *find_walk_select_start_element(SpaceOutliner *soops, bool *changed)
+{
+ TreeElement *walk_element = outliner_find_element_with_flag(&soops->tree, TSE_ACTIVE_WALK);
+
+ *changed = false;
+
+ /* If no walk element exists, start from active */
+ if (!walk_element) {
+ TreeElement *active_element = outliner_find_element_with_flag(&soops->tree, TSE_ACTIVE);
+
+ /* If no active element exists, use the first element in the tree */
+ if (!active_element) {
+ walk_element = soops->tree.first;
+ }
+ else {
+ walk_element = active_element;
+ }
+
+ *changed = true;
+ }
+
+ /* If walk element is not visible, set that element's first visible parent as walk element */
+ if (!outliner_is_element_visible(walk_element)) {
+ TREESTORE(walk_element)->flag &= ~TSE_ACTIVE_WALK;
+
+ while (!outliner_is_element_visible(walk_element)) {
+ walk_element = walk_element->parent;
+ }
+ *changed = true;
+ }
+
+ return walk_element;
+}
+
+/* Scroll the outliner when the walk element reaches the top or bottom boundary */
+static void outliner_walk_scroll(ARegion *ar, TreeElement *te)
+{
+ /* Account for the header height */
+ int y_max = ar->v2d.cur.ymax - UI_UNIT_Y;
+ int y_min = ar->v2d.cur.ymin;
+
+ /* Scroll if walked position is beyond the border */
+ if (te->ys > y_max) {
+ outliner_scroll_view(ar, te->ys - y_max);
+ }
+ else if (te->ys < y_min) {
+ outliner_scroll_view(ar, -(y_min - te->ys));
+ }
+}
+
+static int outliner_walk_select_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+{
+ SpaceOutliner *soops = CTX_wm_space_outliner(C);
+ ARegion *ar = CTX_wm_region(C);
+
+ const short direction = RNA_enum_get(op->ptr, "direction");
+ const bool extend = RNA_boolean_get(op->ptr, "extend");
+ const bool toggle_all = RNA_boolean_get(op->ptr, "toggle_all");
+
+ bool changed;
+ TreeElement *walk_element = find_walk_select_start_element(soops, &changed);
+
+ /* If finding the starting walk select element did not move the element, proceed to walk */
+ if (!changed) {
+ walk_element = do_outliner_select_walk(soops, walk_element, direction, extend, toggle_all);
+ }
+ else {
+ TREESTORE(walk_element)->flag |= TSE_SELECTED | TSE_ACTIVE_WALK;
+ }
+
+ /* Scroll outliner to focus on walk element */
+ outliner_walk_scroll(ar, walk_element);
+
+ if (soops->flag & SO_SYNC_SELECT) {
+ ED_outliner_select_sync_from_outliner(C, soops);
+ }
+ ED_region_tag_redraw(ar);
+
+ return OPERATOR_FINISHED;
+}
+
+void OUTLINER_OT_select_walk(wmOperatorType *ot)
+{
+ static const EnumPropertyItem direction_items[] = {
+ {OUTLINER_SELECT_WALK_UP, "UP", 0, "Up", ""},
+ {OUTLINER_SELECT_WALK_DOWN, "DOWN", 0, "Down", ""},
+ {OUTLINER_SELECT_WALK_LEFT, "LEFT", 0, "Left", ""},
+ {OUTLINER_SELECT_WALK_RIGHT, "RIGHT", 0, "Right", ""},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ /* identifiers */
+ ot->name = "Walk Select";
+ ot->idname = "OUTLINER_OT_select_walk";
+ ot->description = "Use walk navigation to select tree elements";
+
+ /* api callbacks */
+ ot->invoke = outliner_walk_select_invoke;
+ ot->poll = ED_operator_outliner_active;
+
+ ot->flag |= OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* properties */
+ PropertyRNA *prop;
+ prop = RNA_def_enum(ot->srna,
+ "direction",
+ direction_items,
+ 0,
+ "Walk Direction",
+ "Select element in this direction");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+ prop = RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend selection on walk");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+ prop = RNA_def_boolean(
+ ot->srna, "toggle_all", false, "Toggle All", "Toggle open/close hierarchy");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+}
+
+/* ****************************************************** */
diff --git a/source/blender/editors/space_outliner/outliner_sync.c b/source/blender/editors/space_outliner/outliner_sync.c
new file mode 100644
index 00000000000..29c820bce92
--- /dev/null
+++ b/source/blender/editors/space_outliner/outliner_sync.c
@@ -0,0 +1,575 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2004 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup spoutliner
+ */
+
+#include <stdio.h>
+
+#include "DNA_armature_types.h"
+#include "DNA_layer_types.h"
+#include "DNA_outliner_types.h"
+#include "DNA_screen_types.h"
+#include "DNA_sequence_types.h"
+#include "DNA_space_types.h"
+
+#include "BLI_compiler_compat.h"
+#include "BLI_ghash.h"
+
+#include "BKE_armature.h"
+#include "BKE_context.h"
+#include "BKE_layer.h"
+#include "BKE_main.h"
+#include "BKE_sequencer.h"
+
+#include "DEG_depsgraph.h"
+
+#include "ED_armature.h"
+#include "ED_object.h"
+#include "ED_outliner.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "outliner_intern.h"
+
+/* Functions for tagging outliner selection syncing is dirty from operators */
+void ED_outliner_select_sync_from_object_tag(bContext *C)
+{
+ wmWindowManager *wm = CTX_wm_manager(C);
+ wm->outliner_sync_select_dirty |= WM_OUTLINER_SYNC_SELECT_FROM_OBJECT;
+}
+
+void ED_outliner_select_sync_from_edit_bone_tag(bContext *C)
+{
+ wmWindowManager *wm = CTX_wm_manager(C);
+ wm->outliner_sync_select_dirty |= WM_OUTLINER_SYNC_SELECT_FROM_EDIT_BONE;
+}
+
+void ED_outliner_select_sync_from_pose_bone_tag(bContext *C)
+{
+ wmWindowManager *wm = CTX_wm_manager(C);
+ wm->outliner_sync_select_dirty |= WM_OUTLINER_SYNC_SELECT_FROM_POSE_BONE;
+}
+
+void ED_outliner_select_sync_from_sequence_tag(bContext *C)
+{
+ wmWindowManager *wm = CTX_wm_manager(C);
+ wm->outliner_sync_select_dirty |= WM_OUTLINER_SYNC_SELECT_FROM_SEQUENCE;
+}
+
+void ED_outliner_select_sync_from_all_tag(bContext *C)
+{
+ wmWindowManager *wm = CTX_wm_manager(C);
+ wm->outliner_sync_select_dirty |= WM_OUTLINER_SYNC_SELECT_FROM_ALL;
+}
+
+bool ED_outliner_select_sync_is_dirty(const bContext *C)
+{
+ wmWindowManager *wm = CTX_wm_manager(C);
+ return wm->outliner_sync_select_dirty & WM_OUTLINER_SYNC_SELECT_FROM_ALL;
+}
+
+/* Copy sync select dirty flag from window manager to all outliners to be synced lazily on draw */
+void ED_outliner_select_sync_flag_outliners(const bContext *C)
+{
+ Main *bmain = CTX_data_main(C);
+ wmWindowManager *wm = CTX_wm_manager(C);
+
+ for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) {
+ for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) {
+ for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) {
+ if (sl->spacetype == SPACE_OUTLINER) {
+ SpaceOutliner *soutliner = (SpaceOutliner *)sl;
+
+ soutliner->sync_select_dirty |= wm->outliner_sync_select_dirty;
+ }
+ }
+ }
+ }
+
+ /* Clear global sync flag */
+ wm->outliner_sync_select_dirty = 0;
+}
+
+/**
+ * Outliner sync select dirty flags are not enough to determine which types to sync,
+ * outliner display mode also needs to be considered. This stores the types of data
+ * to sync to increase code clarity.
+ */
+typedef struct SyncSelectTypes {
+ bool object;
+ bool edit_bone;
+ bool pose_bone;
+ bool sequence;
+} SyncSelectTypes;
+
+/**
+ * Set which types of data to sync when syncing selection from the outliner based on object
+ * interaction mode and outliner display mode
+ */
+static void outliner_sync_select_from_outliner_set_types(bContext *C,
+ SpaceOutliner *soops,
+ SyncSelectTypes *sync_types)
+{
+ Object *obact = CTX_data_active_object(C);
+ Object *obedit = CTX_data_edit_object(C);
+
+ const bool sequence_view = soops->outlinevis == SO_SEQUENCE;
+
+ sync_types->object = !sequence_view;
+ sync_types->edit_bone = !sequence_view && (obedit && obedit->type == OB_ARMATURE);
+ sync_types->pose_bone = !sequence_view && (obact && obact->mode == OB_MODE_POSE);
+ sync_types->sequence = sequence_view;
+}
+
+/**
+ * Current dirty flags and outliner display mode determine which type of syncing should occur.
+ * This is to ensure sync flag data is not lost on sync in the wrong display mode.
+ * Returns true if a sync is needed.
+ */
+static bool outliner_sync_select_to_outliner_set_types(const bContext *C,
+ SpaceOutliner *soops,
+ SyncSelectTypes *sync_types)
+{
+ Object *obact = CTX_data_active_object(C);
+ Object *obedit = CTX_data_edit_object(C);
+
+ const bool sequence_view = soops->outlinevis == SO_SEQUENCE;
+
+ sync_types->object = !sequence_view &&
+ (soops->sync_select_dirty & WM_OUTLINER_SYNC_SELECT_FROM_OBJECT);
+ sync_types->edit_bone = !sequence_view && (obedit && obedit->type == OB_ARMATURE) &&
+ (soops->sync_select_dirty & WM_OUTLINER_SYNC_SELECT_FROM_EDIT_BONE);
+ sync_types->pose_bone = !sequence_view && (obact && obact->mode == OB_MODE_POSE) &&
+ (soops->sync_select_dirty & WM_OUTLINER_SYNC_SELECT_FROM_POSE_BONE);
+ sync_types->sequence = sequence_view &&
+ (soops->sync_select_dirty & WM_OUTLINER_SYNC_SELECT_FROM_SEQUENCE);
+
+ return sync_types->object || sync_types->edit_bone || sync_types->pose_bone ||
+ sync_types->sequence;
+}
+
+/**
+ * Stores items selected from a sync from the outliner. Prevents syncing the selection
+ * state of the last instance of an object linked in multiple collections.
+ */
+typedef struct SelectedItems {
+ GSet *objects;
+ GSet *edit_bones;
+ GSet *pose_bones;
+} SelectedItems;
+
+static void selected_items_init(SelectedItems *selected_items)
+{
+ selected_items->objects = BLI_gset_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, __func__);
+ selected_items->edit_bones = BLI_gset_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, __func__);
+ selected_items->pose_bones = BLI_gset_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, __func__);
+}
+
+static void selected_items_free(SelectedItems *selected_items)
+{
+ BLI_gset_free(selected_items->objects, NULL);
+ BLI_gset_free(selected_items->edit_bones, NULL);
+ BLI_gset_free(selected_items->pose_bones, NULL);
+}
+
+/* Check if an instance of this object been selected by the sync */
+static bool is_object_selected(GSet *selected_objects, Base *base)
+{
+ return BLI_gset_haskey(selected_objects, base);
+}
+
+/* Check if an instance of this edit bone been selected by the sync */
+static bool is_edit_bone_selected(GSet *selected_ebones, EditBone *ebone)
+{
+ return BLI_gset_haskey(selected_ebones, ebone);
+}
+
+/* Check if an instance of this pose bone been selected by the sync */
+static bool is_pose_bone_selected(GSet *selected_pbones, bPoseChannel *pchan)
+{
+ return BLI_gset_haskey(selected_pbones, pchan);
+}
+
+/* Add element's data to selected item set */
+static void add_selected_item(GSet *selected, void *data)
+{
+ BLI_gset_add(selected, data);
+}
+
+static void outliner_select_sync_to_object(ViewLayer *view_layer,
+ TreeElement *te,
+ TreeStoreElem *tselem,
+ GSet *selected_objects)
+{
+ Object *ob = (Object *)tselem->id;
+ Base *base = (te->directdata) ? (Base *)te->directdata :
+ BKE_view_layer_base_find(view_layer, ob);
+
+ if (base && (base->flag & BASE_SELECTABLE)) {
+ if (tselem->flag & TSE_SELECTED) {
+ ED_object_base_select(base, BA_SELECT);
+
+ add_selected_item(selected_objects, base);
+ }
+ else if (!is_object_selected(selected_objects, base)) {
+ ED_object_base_select(base, BA_DESELECT);
+ }
+ }
+}
+
+static void outliner_select_sync_to_edit_bone(ViewLayer *view_layer,
+ TreeElement *te,
+ TreeStoreElem *tselem,
+ GSet *selected_ebones)
+{
+ bArmature *arm = (bArmature *)tselem->id;
+ EditBone *ebone = (EditBone *)te->directdata;
+
+ short bone_flag = ebone->flag;
+
+ if (EBONE_SELECTABLE(arm, ebone)) {
+ if (tselem->flag & TSE_SELECTED) {
+ ebone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
+
+ add_selected_item(selected_ebones, ebone);
+ }
+ else if (!is_edit_bone_selected(selected_ebones, ebone)) {
+ ebone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
+ }
+ }
+
+ /* Tag if selection changed */
+ if (bone_flag != ebone->flag) {
+ Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer);
+ DEG_id_tag_update(&arm->id, ID_RECALC_SELECT);
+ WM_main_add_notifier(NC_OBJECT | ND_BONE_SELECT, obedit);
+ }
+}
+
+static void outliner_select_sync_to_pose_bone(TreeElement *te,
+ TreeStoreElem *tselem,
+ GSet *selected_pbones)
+{
+ Object *ob = (Object *)tselem->id;
+ bArmature *arm = ob->data;
+ bPoseChannel *pchan = (bPoseChannel *)te->directdata;
+
+ short bone_flag = pchan->bone->flag;
+
+ if (PBONE_SELECTABLE(arm, pchan->bone)) {
+ if (tselem->flag & TSE_SELECTED) {
+ pchan->bone->flag |= BONE_SELECTED;
+
+ add_selected_item(selected_pbones, pchan);
+ }
+ else if (!is_pose_bone_selected(selected_pbones, pchan)) {
+ pchan->bone->flag &= ~BONE_SELECTED;
+ }
+ }
+
+ /* Tag if selection changed */
+ if (bone_flag != pchan->bone->flag) {
+ DEG_id_tag_update(&arm->id, ID_RECALC_SELECT);
+ WM_main_add_notifier(NC_OBJECT | ND_BONE_SELECT, ob);
+ }
+}
+
+static void outliner_select_sync_to_sequence(Scene *scene, TreeStoreElem *tselem)
+{
+ Sequence *seq = (Sequence *)tselem->id;
+
+ if (tselem->flag & TSE_ACTIVE) {
+ BKE_sequencer_active_set(scene, seq);
+ }
+
+ if (tselem->flag & TSE_SELECTED) {
+ seq->flag |= SELECT;
+ }
+ else {
+ seq->flag &= ~SELECT;
+ }
+}
+
+/** Sync select and active flags from outliner to active view layer, bones, and sequencer. */
+static void outliner_sync_selection_from_outliner(Scene *scene,
+ ViewLayer *view_layer,
+ ListBase *tree,
+ const SyncSelectTypes *sync_types,
+ SelectedItems *selected_items)
+{
+
+ for (TreeElement *te = tree->first; te; te = te->next) {
+ TreeStoreElem *tselem = TREESTORE(te);
+
+ if (tselem->type == 0 && te->idcode == ID_OB) {
+ if (sync_types->object) {
+ outliner_select_sync_to_object(view_layer, te, tselem, selected_items->objects);
+ }
+ }
+ else if (tselem->type == TSE_EBONE) {
+ if (sync_types->edit_bone) {
+ outliner_select_sync_to_edit_bone(view_layer, te, tselem, selected_items->edit_bones);
+ }
+ }
+ else if (tselem->type == TSE_POSE_CHANNEL) {
+ if (sync_types->pose_bone) {
+ outliner_select_sync_to_pose_bone(te, tselem, selected_items->pose_bones);
+ }
+ }
+ else if (tselem->type == TSE_SEQUENCE) {
+ if (sync_types->sequence) {
+ outliner_select_sync_to_sequence(scene, tselem);
+ }
+ }
+
+ outliner_sync_selection_from_outliner(
+ scene, view_layer, &te->subtree, sync_types, selected_items);
+ }
+}
+
+/* Set clean outliner and mark other outliners for syncing */
+void ED_outliner_select_sync_from_outliner(bContext *C, SpaceOutliner *soops)
+{
+ /* Don't sync in certain outliner display modes */
+ if (ELEM(soops->outlinevis, SO_LIBRARIES, SO_DATA_API, SO_ID_ORPHANS)) {
+ return;
+ }
+
+ Scene *scene = CTX_data_scene(C);
+ ViewLayer *view_layer = CTX_data_view_layer(C);
+
+ SyncSelectTypes sync_types;
+ outliner_sync_select_from_outliner_set_types(C, soops, &sync_types);
+
+ /* To store elements that have been selected to prevent linked object sync errors */
+ SelectedItems selected_items;
+
+ selected_items_init(&selected_items);
+
+ outliner_sync_selection_from_outliner(
+ scene, view_layer, &soops->tree, &sync_types, &selected_items);
+
+ selected_items_free(&selected_items);
+
+ /* Tag for updates and clear dirty flag toprevent a sync to the outliner on draw */
+ if (sync_types.object) {
+ soops->sync_select_dirty &= ~WM_OUTLINER_SYNC_SELECT_FROM_OBJECT;
+ DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
+ WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
+ }
+ else if (sync_types.edit_bone) {
+ soops->sync_select_dirty &= ~WM_OUTLINER_SYNC_SELECT_FROM_EDIT_BONE;
+ }
+ else if (sync_types.pose_bone) {
+ soops->sync_select_dirty &= ~WM_OUTLINER_SYNC_SELECT_FROM_POSE_BONE;
+ }
+ if (sync_types.sequence) {
+ soops->sync_select_dirty &= ~WM_OUTLINER_SYNC_SELECT_FROM_SEQUENCE;
+ WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER | NA_SELECTED, scene);
+ }
+}
+
+static void outliner_select_sync_from_object(ViewLayer *view_layer,
+ SpaceOutliner *soops,
+ Object *obact,
+ TreeElement *te,
+ TreeStoreElem *tselem)
+{
+ Object *ob = (Object *)tselem->id;
+ Base *base = (te->directdata) ? (Base *)te->directdata :
+ BKE_view_layer_base_find(view_layer, ob);
+ const bool is_selected = (base != NULL) && ((base->flag & BASE_SELECTED) != 0);
+
+ if (base && (ob == obact)) {
+ outliner_element_activate(soops, tselem);
+ }
+ else {
+ tselem->flag &= ~TSE_ACTIVE;
+ }
+
+ if (is_selected) {
+ tselem->flag |= TSE_SELECTED;
+ }
+ else {
+ tselem->flag &= ~TSE_SELECTED;
+ }
+}
+
+static void outliner_select_sync_from_edit_bone(SpaceOutliner *soops,
+ EditBone *ebone_active,
+ TreeElement *te,
+ TreeStoreElem *tselem)
+{
+ EditBone *ebone = (EditBone *)te->directdata;
+
+ if (ebone == ebone_active) {
+ outliner_element_activate(soops, tselem);
+ }
+ else {
+ tselem->flag &= ~TSE_ACTIVE;
+ }
+
+ if (ebone->flag & BONE_SELECTED) {
+ tselem->flag |= TSE_SELECTED;
+ }
+ else {
+ tselem->flag &= ~TSE_SELECTED;
+ }
+}
+
+static void outliner_select_sync_from_pose_bone(SpaceOutliner *soops,
+ bPoseChannel *pchan_active,
+ TreeElement *te,
+ TreeStoreElem *tselem)
+{
+ bPoseChannel *pchan = (bPoseChannel *)te->directdata;
+ Bone *bone = pchan->bone;
+
+ if (pchan == pchan_active) {
+ outliner_element_activate(soops, tselem);
+ }
+ else {
+ tselem->flag &= ~TSE_ACTIVE;
+ }
+
+ if (bone->flag & BONE_SELECTED) {
+ tselem->flag |= TSE_SELECTED;
+ }
+ else {
+ tselem->flag &= ~TSE_SELECTED;
+ }
+}
+
+static void outliner_select_sync_from_sequence(SpaceOutliner *soops,
+ Sequence *sequence_active,
+ TreeStoreElem *tselem)
+{
+ Sequence *seq = (Sequence *)tselem->id;
+
+ if (seq == sequence_active) {
+ outliner_element_activate(soops, tselem);
+ }
+ else {
+ tselem->flag &= ~TSE_ACTIVE;
+ }
+
+ if (seq->flag & SELECT) {
+ tselem->flag |= TSE_SELECTED;
+ }
+ else {
+ tselem->flag &= ~TSE_SELECTED;
+ }
+}
+
+/**
+ * Contains active object, bones, and sequence for syncing to prevent getting active data
+ * repeatedly throughout syncing to the outliner.
+ */
+typedef struct SyncSelectActiveData {
+ Object *object;
+ EditBone *edit_bone;
+ bPoseChannel *pose_channel;
+ Sequence *sequence;
+} SyncSelectActiveData;
+
+/** Sync select and active flags from active view layer, bones, and sequences to the outliner. */
+static void outliner_sync_selection_to_outliner(ViewLayer *view_layer,
+ SpaceOutliner *soops,
+ ListBase *tree,
+ SyncSelectActiveData *active_data,
+ const SyncSelectTypes *sync_types)
+{
+ for (TreeElement *te = tree->first; te; te = te->next) {
+ TreeStoreElem *tselem = TREESTORE(te);
+
+ if (tselem->type == 0 && te->idcode == ID_OB) {
+ if (sync_types->object) {
+ outliner_select_sync_from_object(view_layer, soops, active_data->object, te, tselem);
+ }
+ }
+ else if (tselem->type == TSE_EBONE) {
+ if (sync_types->edit_bone) {
+ outliner_select_sync_from_edit_bone(soops, active_data->edit_bone, te, tselem);
+ }
+ }
+ else if (tselem->type == TSE_POSE_CHANNEL) {
+ if (sync_types->pose_bone) {
+ outliner_select_sync_from_pose_bone(soops, active_data->pose_channel, te, tselem);
+ }
+ }
+ else if (tselem->type == TSE_SEQUENCE) {
+ if (sync_types->sequence) {
+ outliner_select_sync_from_sequence(soops, active_data->sequence, tselem);
+ }
+ }
+ else {
+ tselem->flag &= ~TSE_SELECTED;
+ }
+
+ /* Sync subtree elements */
+ outliner_sync_selection_to_outliner(view_layer, soops, &te->subtree, active_data, sync_types);
+ }
+}
+
+/* Get active data from context */
+static void get_sync_select_active_data(const bContext *C, SyncSelectActiveData *active_data)
+{
+ Scene *scene = CTX_data_scene(C);
+ ViewLayer *view_layer = CTX_data_view_layer(C);
+ active_data->object = OBACT(view_layer);
+ active_data->edit_bone = CTX_data_active_bone(C);
+ active_data->pose_channel = CTX_data_active_pose_bone(C);
+ active_data->sequence = BKE_sequencer_active_get(scene);
+}
+
+/* If outliner is dirty sync selection from view layer and sequwncer */
+void outliner_sync_selection(const bContext *C, SpaceOutliner *soops)
+{
+ /* Set which types of data to sync from sync dirty flag and outliner display mode */
+ SyncSelectTypes sync_types;
+ const bool sync_required = outliner_sync_select_to_outliner_set_types(C, soops, &sync_types);
+
+ if (sync_required) {
+ ViewLayer *view_layer = CTX_data_view_layer(C);
+
+ /* Store active object, bones, and sequence */
+ SyncSelectActiveData active_data;
+ get_sync_select_active_data(C, &active_data);
+
+ outliner_sync_selection_to_outliner(
+ view_layer, soops, &soops->tree, &active_data, &sync_types);
+
+ /* Keep any unsynced data in the dirty flag */
+ if (sync_types.object) {
+ soops->sync_select_dirty &= ~WM_OUTLINER_SYNC_SELECT_FROM_OBJECT;
+ }
+ if (sync_types.edit_bone) {
+ soops->sync_select_dirty &= ~WM_OUTLINER_SYNC_SELECT_FROM_EDIT_BONE;
+ }
+ if (sync_types.pose_bone) {
+ soops->sync_select_dirty &= ~WM_OUTLINER_SYNC_SELECT_FROM_POSE_BONE;
+ }
+ if (sync_types.sequence) {
+ soops->sync_select_dirty &= ~WM_OUTLINER_SYNC_SELECT_FROM_SEQUENCE;
+ }
+ }
+}
diff --git a/source/blender/editors/space_outliner/outliner_tools.c b/source/blender/editors/space_outliner/outliner_tools.c
index f9905cc4fcd..a2d988f1142 100644
--- a/source/blender/editors/space_outliner/outliner_tools.c
+++ b/source/blender/editors/space_outliner/outliner_tools.c
@@ -63,6 +63,7 @@
#include "ED_armature.h"
#include "ED_object.h"
+#include "ED_outliner.h"
#include "ED_scene.h"
#include "ED_screen.h"
#include "ED_sequencer.h"
@@ -298,7 +299,7 @@ static void unlink_collection_cb(bContext *C,
}
else if (GS(tsep->id->name) == ID_SCE) {
Scene *scene = (Scene *)tsep->id;
- Collection *parent = BKE_collection_master(scene);
+ Collection *parent = scene->master_collection;
id_fake_user_set(&collection->id);
BKE_collection_child_remove(bmain, parent, collection);
DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE);
@@ -337,7 +338,7 @@ static void unlink_object_cb(bContext *C,
}
else if (GS(tsep->id->name) == ID_SCE) {
Scene *scene = (Scene *)tsep->id;
- Collection *parent = BKE_collection_master(scene);
+ Collection *parent = scene->master_collection;
BKE_collection_object_remove(bmain, parent, ob, true);
DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE);
DEG_relations_tag_update(bmain);
@@ -478,6 +479,129 @@ void OUTLINER_OT_scene_operation(wmOperatorType *ot)
}
/* ******************************************** */
+/**
+ * Stores the parent and a child element of a merged icon-row icon for
+ * the merged select popup menu. The sub-tree of the parent is searched and
+ * the child is needed to only show elements of the same type in the popup.
+ */
+typedef struct MergedSearchData {
+ TreeElement *parent_element;
+ TreeElement *select_element;
+} MergedSearchData;
+
+static void merged_element_search_cb_recursive(
+ const ListBase *tree, short tselem_type, short type, const char *str, uiSearchItems *items)
+{
+ char name[64];
+ int iconid;
+
+ for (TreeElement *te = tree->first; te; te = te->next) {
+ TreeStoreElem *tselem = TREESTORE(te);
+
+ if (tree_element_id_type_to_index(te) == type && tselem_type == tselem->type) {
+ if (BLI_strcasestr(te->name, str)) {
+ BLI_strncpy(name, te->name, 64);
+
+ iconid = tree_element_get_icon(tselem, te).icon;
+
+ /* Don't allow duplicate named items */
+ if (UI_search_items_find_index(items, name) == -1) {
+ if (!UI_search_item_add(items, name, te, iconid)) {
+ break;
+ }
+ }
+ }
+ }
+
+ merged_element_search_cb_recursive(&te->subtree, tselem_type, type, str, items);
+ }
+}
+
+/* Get a list of elements that match the search string */
+static void merged_element_search_cb(const bContext *UNUSED(C),
+ void *data,
+ const char *str,
+ uiSearchItems *items)
+{
+ MergedSearchData *search_data = (MergedSearchData *)data;
+ TreeElement *parent = search_data->parent_element;
+ TreeElement *te = search_data->select_element;
+
+ int type = tree_element_id_type_to_index(te);
+
+ merged_element_search_cb_recursive(&parent->subtree, TREESTORE(te)->type, type, str, items);
+}
+
+/* Activate an element from the merged element search menu */
+static void merged_element_search_call_cb(struct bContext *C, void *UNUSED(arg1), void *element)
+{
+ SpaceOutliner *soops = CTX_wm_space_outliner(C);
+ TreeElement *te = (TreeElement *)element;
+
+ outliner_item_select(soops, te, false, false);
+ outliner_item_do_activate_from_tree_element(C, te, te->store_elem, false, false);
+
+ if (soops->flag & SO_SYNC_SELECT) {
+ ED_outliner_select_sync_from_outliner(C, soops);
+ }
+}
+
+/** Merged element search menu
+ * Created on activation of a merged or aggregated icon-row icon.
+ */
+static uiBlock *merged_element_search_menu(bContext *C, ARegion *ar, void *data)
+{
+ static char search[64] = "";
+ uiBlock *block;
+ uiBut *but;
+
+ /* Clear search on each menu creation */
+ *search = '\0';
+
+ block = UI_block_begin(C, ar, __func__, UI_EMBOSS);
+ UI_block_flag_enable(block, UI_BLOCK_LOOP | UI_BLOCK_MOVEMOUSE_QUIT | UI_BLOCK_SEARCH_MENU);
+ UI_block_theme_style_set(block, UI_BLOCK_THEME_STYLE_POPUP);
+
+ short menu_width = 10 * UI_UNIT_X;
+ but = uiDefSearchBut(
+ block, search, 0, ICON_VIEWZOOM, sizeof(search), 10, 10, menu_width, UI_UNIT_Y, 0, 0, "");
+ UI_but_func_search_set(
+ but, NULL, merged_element_search_cb, data, false, merged_element_search_call_cb, NULL);
+ UI_but_flag_enable(but, UI_BUT_ACTIVATE_ON_INIT);
+
+ /* Fake button to hold space for search items */
+ uiDefBut(block,
+ UI_BTYPE_LABEL,
+ 0,
+ "",
+ 10,
+ 10 - UI_searchbox_size_y(),
+ menu_width,
+ UI_searchbox_size_y(),
+ NULL,
+ 0,
+ 0,
+ 0,
+ 0,
+ NULL);
+
+ /* Center the menu on the cursor */
+ UI_block_bounds_set_popup(block, 6, (const int[2]){-(menu_width / 2), 0});
+
+ return block;
+}
+
+void merged_element_search_menu_invoke(bContext *C,
+ TreeElement *parent_te,
+ TreeElement *activate_te)
+{
+ MergedSearchData *select_data = MEM_callocN(sizeof(MergedSearchData), "merge_search_data");
+ select_data->parent_element = parent_te;
+ select_data->select_element = activate_te;
+
+ UI_popup_block_invoke(C, merged_element_search_menu, select_data, MEM_freeN);
+}
+
static void object_select_cb(bContext *C,
ReportList *UNUSED(reports),
Scene *UNUSED(scene),
@@ -556,12 +680,7 @@ static void object_delete_cb(bContext *C,
if (ob == CTX_data_edit_object(C)) {
ED_object_editmode_exit(C, EM_FREEDATA);
}
- ED_object_base_free_and_unlink(CTX_data_main(C), scene, ob);
- /* leave for ED_outliner_id_unref to handle */
-#if 0
- te->directdata = NULL;
- tselem->id = NULL;
-#endif
+ BKE_id_delete(bmain, ob);
}
}
@@ -654,7 +773,7 @@ static void singleuser_action_cb(bContext *C,
if (id) {
IdAdtTemplate *iat = (IdAdtTemplate *)tsep->id;
- PointerRNA ptr = {{NULL}};
+ PointerRNA ptr = {NULL};
PropertyRNA *prop;
RNA_pointer_create(&iat->id, &RNA_AnimData, iat->adt, &ptr);
@@ -677,7 +796,7 @@ static void singleuser_world_cb(bContext *C,
/* need to use parent scene not just scene, otherwise may end up getting wrong one */
if (id) {
Scene *parscene = (Scene *)tsep->id;
- PointerRNA ptr = {{NULL}};
+ PointerRNA ptr = {NULL};
PropertyRNA *prop;
RNA_id_pointer_create(&parscene->id, &ptr);
@@ -1080,11 +1199,6 @@ static void object_delete_hierarchy_cb(bContext *C,
}
outline_delete_hierarchy(C, reports, scene, base);
- /* leave for ED_outliner_id_unref to handle */
-#if 0
- te->directdata = NULL;
- tselem->id = NULL;
-#endif
}
DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
@@ -1168,11 +1282,6 @@ static void object_batch_delete_hierarchy_cb(bContext *C,
}
outline_batch_delete_hierarchy(reports, CTX_data_main(C), view_layer, scene, base);
- /* leave for ED_outliner_id_unref to handle */
-#if 0
- te->directdata = NULL;
- tselem->id = NULL;
-#endif
}
}
diff --git a/source/blender/editors/space_outliner/outliner_tree.c b/source/blender/editors/space_outliner/outliner_tree.c
index cc062467dbe..fd6a052b84d 100644
--- a/source/blender/editors/space_outliner/outliner_tree.c
+++ b/source/blender/editors/space_outliner/outliner_tree.c
@@ -297,7 +297,7 @@ static void outliner_add_scene_contents(SpaceOutliner *soops,
ViewLayer *view_layer;
for (view_layer = sce->view_layers.first; view_layer; view_layer = view_layer->next) {
- TreeElement *tenlay = outliner_add_element(soops, &ten->subtree, sce, te, TSE_R_LAYER, 0);
+ TreeElement *tenlay = outliner_add_element(soops, &ten->subtree, sce, ten, TSE_R_LAYER, 0);
tenlay->name = view_layer->name;
tenlay->directdata = view_layer;
}
@@ -314,7 +314,7 @@ static void outliner_add_scene_contents(SpaceOutliner *soops,
ten = outliner_add_element(soops, lb, sce, te, TSE_SCENE_OBJECTS_BASE, 0);
ten->name = IFACE_("Objects");
FOREACH_SCENE_OBJECT_BEGIN (sce, ob) {
- outliner_add_element(soops, &ten->subtree, ob, NULL, 0, 0);
+ outliner_add_element(soops, &ten->subtree, ob, ten, 0, 0);
}
FOREACH_SCENE_OBJECT_END;
outliner_make_object_parent_hierarchy(&ten->subtree);
@@ -766,7 +766,7 @@ static TreeElement *outliner_add_element(
ID *id = idv;
if (ELEM(type, TSE_RNA_STRUCT, TSE_RNA_PROPERTY, TSE_RNA_ARRAY_ELEM)) {
- id = ((PointerRNA *)idv)->id.data;
+ id = ((PointerRNA *)idv)->owner_id;
if (!id) {
id = ((PointerRNA *)idv)->data;
}
@@ -1601,7 +1601,7 @@ typedef struct tTreeSort {
short idcode;
} tTreeSort;
-/* alphabetical comparator, tryping to put objects first */
+/* alphabetical comparator, trying to put objects first */
static int treesort_alpha_ob(const void *v1, const void *v2)
{
const tTreeSort *x1 = v1, *x2 = v2;
@@ -1627,7 +1627,7 @@ static int treesort_alpha_ob(const void *v1, const void *v2)
return (x1->te->flag & TE_CHILD_NOT_IN_COLLECTION) ? 1 : -1;
}
- comp = strcmp(x1->name, x2->name);
+ comp = BLI_strcasecmp_natural(x1->name, x2->name);
if (comp > 0) {
return 1;
@@ -1659,7 +1659,7 @@ static int treesort_alpha(const void *v1, const void *v2)
const tTreeSort *x1 = v1, *x2 = v2;
int comp;
- comp = strcmp(x1->name, x2->name);
+ comp = BLI_strcasecmp_natural(x1->name, x2->name);
if (comp > 0) {
return 1;
@@ -1697,7 +1697,7 @@ static int treesort_obtype_alpha(const void *v1, const void *v2)
}
}
else {
- int comp = strcmp(x1->name, x2->name);
+ int comp = BLI_strcasecmp_natural(x1->name, x2->name);
if (comp > 0) {
return 1;
@@ -2008,6 +2008,9 @@ static int outliner_exclude_filter_get(SpaceOutliner *soops)
case SO_FILTER_OB_VISIBLE:
exclude_filter |= SO_FILTER_OB_STATE_VISIBLE;
break;
+ case SO_FILTER_OB_HIDDEN:
+ exclude_filter |= SO_FILTER_OB_STATE_HIDDEN;
+ break;
case SO_FILTER_OB_SELECTED:
exclude_filter |= SO_FILTER_OB_STATE_SELECTED;
break;
@@ -2086,6 +2089,11 @@ static bool outliner_element_visible_get(ViewLayer *view_layer,
return false;
}
}
+ else if (exclude_filter & SO_FILTER_OB_STATE_HIDDEN) {
+ if ((base->flag & BASE_VISIBLE) != 0) {
+ return false;
+ }
+ }
else if (exclude_filter & SO_FILTER_OB_STATE_SELECTED) {
if ((base->flag & BASE_SELECTED) == 0) {
return false;
@@ -2339,7 +2347,8 @@ void outliner_build_tree(
te = outliner_add_element(soops, &soops->tree, sce, NULL, 0, 0);
tselem = TREESTORE(te);
- if (sce == scene && show_opened) {
+ /* New scene elements open by default */
+ if ((sce == scene && show_opened) || !tselem->used) {
tselem->flag &= ~TSE_CLOSED;
}
diff --git a/source/blender/editors/space_outliner/outliner_utils.c b/source/blender/editors/space_outliner/outliner_utils.c
index f57dce97b38..5dfdf6f129b 100644
--- a/source/blender/editors/space_outliner/outliner_utils.c
+++ b/source/blender/editors/space_outliner/outliner_utils.c
@@ -24,11 +24,15 @@
#include "BLI_utildefines.h"
#include "DNA_action_types.h"
+#include "DNA_screen_types.h"
#include "DNA_space_types.h"
+#include "BKE_context.h"
#include "BKE_outliner_treehash.h"
+#include "BKE_layer.h"
#include "ED_armature.h"
+#include "ED_outliner.h"
#include "UI_interface.h"
#include "UI_view2d.h"
@@ -62,6 +66,38 @@ TreeElement *outliner_find_item_at_y(const SpaceOutliner *soops,
return NULL;
}
+static TreeElement *outliner_find_item_at_x_in_row_recursive(const TreeElement *parent_te,
+ float view_co_x,
+ bool *r_merged)
+{
+ TreeElement *child_te = parent_te->subtree.first;
+
+ bool over_element = false;
+
+ while (child_te) {
+ over_element = (view_co_x > child_te->xs) && (view_co_x < child_te->xend);
+ if ((child_te->flag & TE_ICONROW) && over_element) {
+ return child_te;
+ }
+ else if ((child_te->flag & TE_ICONROW_MERGED) && over_element) {
+ if (r_merged) {
+ *r_merged = true;
+ }
+ return child_te;
+ }
+
+ TreeElement *te = outliner_find_item_at_x_in_row_recursive(child_te, view_co_x, r_merged);
+ if (te != child_te) {
+ return te;
+ }
+
+ child_te = child_te->next;
+ }
+
+ /* return parent if no child is hovered */
+ return (TreeElement *)parent_te;
+}
+
/**
* Collapsed items can show their children as click-able icons. This function tries to find
* such an icon that represents the child item at x-coordinate \a view_co_x (view-space).
@@ -70,24 +106,14 @@ TreeElement *outliner_find_item_at_y(const SpaceOutliner *soops,
*/
TreeElement *outliner_find_item_at_x_in_row(const SpaceOutliner *soops,
const TreeElement *parent_te,
- float view_co_x)
+ float view_co_x,
+ bool *r_merged)
{
- /* if parent_te is opened, it doesn't show childs in row */
+ /* if parent_te is opened, it doesn't show children in row */
if (!TSELEM_OPEN(TREESTORE(parent_te), soops)) {
- /* no recursion, items can only display their direct children in the row */
- for (TreeElement *child_te = parent_te->subtree.first;
- /* don't look further if co_x is smaller than child position*/
- child_te && view_co_x >= child_te->xs;
-
- child_te = child_te->next) {
- if ((child_te->flag & TE_ICONROW) && (view_co_x > child_te->xs) &&
- (view_co_x < child_te->xend)) {
- return child_te;
- }
- }
+ return outliner_find_item_at_x_in_row_recursive(parent_te, view_co_x, r_merged);
}
- /* return parent if no child is hovered */
return (TreeElement *)parent_te;
}
@@ -300,3 +326,89 @@ float outliner_restrict_columns_width(const SpaceOutliner *soops)
}
return (num_columns * UI_UNIT_X + V2D_SCROLL_WIDTH);
}
+
+/* Find first tree element in tree with matching treestore flag */
+TreeElement *outliner_find_element_with_flag(const ListBase *lb, short flag)
+{
+ for (TreeElement *te = lb->first; te; te = te->next) {
+ if ((TREESTORE(te)->flag & flag) == flag) {
+ return te;
+ }
+ TreeElement *active_element = outliner_find_element_with_flag(&te->subtree, flag);
+ if (active_element) {
+ return active_element;
+ }
+ }
+ return NULL;
+}
+
+/* Find if element is visible in the outliner tree */
+bool outliner_is_element_visible(const TreeElement *te)
+{
+ TreeStoreElem *tselem;
+
+ while (te->parent) {
+ tselem = TREESTORE(te->parent);
+
+ if (tselem->flag & TSE_CLOSED) {
+ return false;
+ }
+ else {
+ te = te->parent;
+ }
+ }
+
+ return true;
+}
+
+/* Find if x coordinate is over element disclosure toggle */
+bool outliner_item_is_co_within_close_toggle(TreeElement *te, float view_co_x)
+{
+ return (view_co_x > te->xs) && (view_co_x < te->xs + UI_UNIT_X);
+}
+
+/* Scroll view vertically while keeping within total bounds */
+void outliner_scroll_view(ARegion *ar, int delta_y)
+{
+ int y_min = MIN2(ar->v2d.cur.ymin, ar->v2d.tot.ymin);
+
+ ar->v2d.cur.ymax += delta_y;
+ ar->v2d.cur.ymin += delta_y;
+
+ /* Adjust view if delta placed view outside total area */
+ int offset;
+ if (ar->v2d.cur.ymax > -UI_UNIT_Y) {
+ offset = ar->v2d.cur.ymax;
+ ar->v2d.cur.ymax -= offset;
+ ar->v2d.cur.ymin -= offset;
+ }
+ else if (ar->v2d.cur.ymin < y_min) {
+ offset = y_min - ar->v2d.cur.ymin;
+ ar->v2d.cur.ymax += offset;
+ ar->v2d.cur.ymin += offset;
+ }
+}
+
+/* Get base of object under cursor. Used for eyedropper tool */
+Base *ED_outliner_give_base_under_cursor(bContext *C, const int mval[2])
+{
+ ARegion *ar = CTX_wm_region(C);
+ ViewLayer *view_layer = CTX_data_view_layer(C);
+ SpaceOutliner *soops = CTX_wm_space_outliner(C);
+ TreeElement *te;
+ Base *base = NULL;
+ float view_mval[2];
+
+ UI_view2d_region_to_view(&ar->v2d, mval[0], mval[1], &view_mval[0], &view_mval[1]);
+
+ te = outliner_find_item_at_y(soops, &soops->tree, view_mval[1]);
+ if (te) {
+ TreeStoreElem *tselem = TREESTORE(te);
+ if (tselem->type == 0) {
+ Object *ob = (Object *)tselem->id;
+ base = (te->directdata) ? (Base *)te->directdata : BKE_view_layer_base_find(view_layer, ob);
+ }
+ }
+
+ return base;
+}
diff --git a/source/blender/editors/space_outliner/space_outliner.c b/source/blender/editors/space_outliner/space_outliner.c
index 091efc56c09..79880c68120 100644
--- a/source/blender/editors/space_outliner/space_outliner.c
+++ b/source/blender/editors/space_outliner/space_outliner.c
@@ -131,6 +131,9 @@ static void outliner_main_region_listener(wmWindow *UNUSED(win),
ED_region_tag_redraw(ar);
break;
}
+ if (wmn->action & NA_EDITED) {
+ ED_region_tag_redraw(ar);
+ }
break;
case NC_OBJECT:
switch (wmn->data) {
@@ -145,13 +148,8 @@ static void outliner_main_region_listener(wmWindow *UNUSED(win),
ED_region_tag_redraw(ar);
break;
case ND_CONSTRAINT:
- switch (wmn->action) {
- case NA_ADDED:
- case NA_REMOVED:
- case NA_RENAME:
- ED_region_tag_redraw(ar);
- break;
- }
+ /* all constraint actions now, for reordering */
+ ED_region_tag_redraw(ar);
break;
case ND_MODIFIER:
/* all modifier actions now */
@@ -304,6 +302,8 @@ static SpaceLink *outliner_new(const ScrArea *UNUSED(area), const Scene *UNUSED(
soutliner->filter_id_type = ID_GR;
soutliner->show_restrict_flags = SO_RESTRICT_ENABLE | SO_RESTRICT_HIDE;
soutliner->outlinevis = SO_VIEW_LAYER;
+ soutliner->sync_select_dirty |= WM_OUTLINER_SYNC_SELECT_FROM_ALL;
+ soutliner->flag |= SO_SYNC_SELECT;
/* header */
ar = MEM_callocN(sizeof(ARegion), "header for outliner");
@@ -349,6 +349,9 @@ static SpaceLink *outliner_duplicate(SpaceLink *sl)
soutlinern->treestore = NULL;
soutlinern->treehash = NULL;
+ soutlinern->flag |= (soutliner->flag & SO_SYNC_SELECT);
+ soutlinern->sync_select_dirty = WM_OUTLINER_SYNC_SELECT_FROM_ALL;
+
return (SpaceLink *)soutlinern;
}
@@ -415,7 +418,7 @@ void ED_spacetype_outliner(void)
/* regions: main window */
art = MEM_callocN(sizeof(ARegionType), "spacetype outliner region");
art->regionid = RGN_TYPE_WINDOW;
- art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_VIEW2D | ED_KEYMAP_FRAMES;
+ art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_VIEW2D;
art->init = outliner_main_region_init;
art->draw = outliner_main_region_draw;
@@ -428,7 +431,7 @@ void ED_spacetype_outliner(void)
art = MEM_callocN(sizeof(ARegionType), "spacetype outliner header region");
art->regionid = RGN_TYPE_HEADER;
art->prefsizey = HEADERY;
- art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_VIEW2D | ED_KEYMAP_FRAMES | ED_KEYMAP_HEADER;
+ art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_VIEW2D | ED_KEYMAP_HEADER;
art->init = outliner_header_region_init;
art->draw = outliner_header_region_draw;
diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c
index 07350b5269e..b15acb12d00 100644
--- a/source/blender/editors/space_sequencer/sequencer_draw.c
+++ b/source/blender/editors/space_sequencer/sequencer_draw.c
@@ -1045,9 +1045,8 @@ ImBuf *sequencer_ibuf_get(struct Main *bmain,
bmain, depsgraph, scene, rectx, recty, proxy_size, false, &context);
context.view_id = BKE_scene_multiview_view_id_get(&scene->r, viewname);
- /* sequencer could start rendering, in this case we need to be sure it wouldn't be canceled
- * by Esc pressed somewhere in the past
- */
+ /* Sequencer could start rendering, in this case we need to be sure it wouldn't be canceled
+ * by Escape pressed somewhere in the past. */
G.is_break = false;
/* Rendering can change OGL context. Save & Restore framebuffer. */
diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c
index ed0303564c6..22b73c32bfe 100644
--- a/source/blender/editors/space_sequencer/sequencer_edit.c
+++ b/source/blender/editors/space_sequencer/sequencer_edit.c
@@ -3405,6 +3405,7 @@ void SEQUENCER_OT_copy(wmOperatorType *ot)
/* identifiers */
ot->name = "Copy";
ot->idname = "SEQUENCER_OT_copy";
+ ot->description = "Copy selected strips to clipboard";
/* api callbacks */
ot->exec = sequencer_copy_exec;
@@ -3470,6 +3471,7 @@ void SEQUENCER_OT_paste(wmOperatorType *ot)
/* identifiers */
ot->name = "Paste";
ot->idname = "SEQUENCER_OT_paste";
+ ot->description = "Paste strips from clipboard";
/* api callbacks */
ot->exec = sequencer_paste_exec;
diff --git a/source/blender/editors/space_sequencer/sequencer_intern.h b/source/blender/editors/space_sequencer/sequencer_intern.h
index dd04260d80e..851d3b5f3aa 100644
--- a/source/blender/editors/space_sequencer/sequencer_intern.h
+++ b/source/blender/editors/space_sequencer/sequencer_intern.h
@@ -34,7 +34,6 @@ struct ARegionType;
struct Depsgraph;
struct Main;
struct Scene;
-struct ScrArea;
struct Sequence;
struct SpaceSeq;
struct StripElem;
diff --git a/source/blender/editors/space_sequencer/sequencer_select.c b/source/blender/editors/space_sequencer/sequencer_select.c
index 57f86059d9d..affb6d3fd88 100644
--- a/source/blender/editors/space_sequencer/sequencer_select.c
+++ b/source/blender/editors/space_sequencer/sequencer_select.c
@@ -41,6 +41,7 @@
/* for menu/popup icons etc etc*/
+#include "ED_outliner.h"
#include "ED_screen.h"
#include "ED_sequencer.h"
#include "ED_select_utils.h"
@@ -49,6 +50,7 @@
/* own include */
#include "sequencer_intern.h"
+
static void *find_nearest_marker(int UNUSED(d1), int UNUSED(d2))
{
return NULL;
@@ -254,6 +256,8 @@ static int sequencer_de_select_all_exec(bContext *C, wmOperator *op)
}
}
+ ED_outliner_select_sync_from_sequence_tag(C);
+
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER | NA_SELECTED, scene);
return OPERATOR_FINISHED;
@@ -293,6 +297,8 @@ static int sequencer_select_inverse_exec(bContext *C, wmOperator *UNUSED(op))
}
}
+ ED_outliner_select_sync_from_sequence_tag(C);
+
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER | NA_SELECTED, scene);
return OPERATOR_FINISHED;
@@ -542,6 +548,8 @@ static int sequencer_select_invoke(bContext *C, wmOperator *op, const wmEvent *e
}
}
+ ED_outliner_select_sync_from_sequence_tag(C);
+
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER | NA_SELECTED, scene);
/* allowing tweaks */
@@ -668,6 +676,8 @@ static int sequencer_select_more_exec(bContext *C, wmOperator *UNUSED(op))
return OPERATOR_CANCELLED;
}
+ ED_outliner_select_sync_from_sequence_tag(C);
+
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER | NA_SELECTED, scene);
return OPERATOR_FINISHED;
@@ -699,6 +709,8 @@ static int sequencer_select_less_exec(bContext *C, wmOperator *UNUSED(op))
return OPERATOR_CANCELLED;
}
+ ED_outliner_select_sync_from_sequence_tag(C);
+
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER | NA_SELECTED, scene);
return OPERATOR_FINISHED;
@@ -750,6 +762,8 @@ static int sequencer_select_linked_pick_invoke(bContext *C, wmOperator *op, cons
selected = select_more_less_seq__internal(scene, 1, 1);
}
+ ED_outliner_select_sync_from_sequence_tag(C);
+
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER | NA_SELECTED, scene);
return OPERATOR_FINISHED;
@@ -784,6 +798,8 @@ static int sequencer_select_linked_exec(bContext *C, wmOperator *UNUSED(op))
selected = select_more_less_seq__internal(scene, true, true);
}
+ ED_outliner_select_sync_from_sequence_tag(C);
+
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER | NA_SELECTED, scene);
return OPERATOR_FINISHED;
@@ -832,6 +848,8 @@ static int sequencer_select_handles_exec(bContext *C, wmOperator *op)
}
}
+ ED_outliner_select_sync_from_sequence_tag(C);
+
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER | NA_SELECTED, scene);
return OPERATOR_FINISHED;
@@ -876,6 +894,8 @@ static int sequencer_select_active_side_exec(bContext *C, wmOperator *op)
select_active_side(
ed->seqbasep, RNA_enum_get(op->ptr, "side"), seq_act->machine, seq_act->startdisp);
+ ED_outliner_select_sync_from_sequence_tag(C);
+
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER | NA_SELECTED, scene);
return OPERATOR_FINISHED;
@@ -934,6 +954,8 @@ static int sequencer_box_select_exec(bContext *C, wmOperator *op)
}
}
+ ED_outliner_select_sync_from_sequence_tag(C);
+
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER | NA_SELECTED, scene);
return OPERATOR_FINISHED;
@@ -1311,6 +1333,7 @@ static int sequencer_select_grouped_exec(bContext *C, wmOperator *op)
}
if (changed) {
+ ED_outliner_select_sync_from_sequence_tag(C);
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER | NA_SELECTED, scene);
return OPERATOR_FINISHED;
}
diff --git a/source/blender/editors/space_sequencer/space_sequencer.c b/source/blender/editors/space_sequencer/space_sequencer.c
index 9aa9d14cbc8..e1cf6d00b90 100644
--- a/source/blender/editors/space_sequencer/space_sequencer.c
+++ b/source/blender/editors/space_sequencer/space_sequencer.c
@@ -597,7 +597,7 @@ static void sequencer_main_region_message_subscribe(const struct bContext *UNUSE
&RNA_SequenceModifier,
&RNA_SequenceColorBalanceData,
};
- wmMsgParams_RNA msg_key_params = {{{0}}};
+ wmMsgParams_RNA msg_key_params = {{0}};
for (int i = 0; i < ARRAY_SIZE(type_array); i++) {
msg_key_params.ptr.type = type_array[i];
WM_msg_subscribe_rna_params(
diff --git a/source/blender/editors/space_text/space_text.c b/source/blender/editors/space_text/space_text.c
index c1a3c79b0d8..9f39313b9ab 100644
--- a/source/blender/editors/space_text/space_text.c
+++ b/source/blender/editors/space_text/space_text.c
@@ -29,6 +29,7 @@
#include "BLI_blenlib.h"
+#include "BKE_global.h"
#include "BKE_context.h"
#include "BKE_library.h"
#include "BKE_screen.h"
@@ -356,7 +357,7 @@ static void text_drop_paste(wmDrag *drag, wmDropBox *drop)
ID *id = WM_drag_ID(drag, 0);
/* copy drag path to properties */
- text = RNA_path_full_ID_py(id);
+ text = RNA_path_full_ID_py(G_MAIN, id);
RNA_string_set(drop->ptr, "text", text);
MEM_freeN(text);
}
diff --git a/source/blender/editors/space_text/text_ops.c b/source/blender/editors/space_text/text_ops.c
index e1550deb659..caefb5070fb 100644
--- a/source/blender/editors/space_text/text_ops.c
+++ b/source/blender/editors/space_text/text_ops.c
@@ -142,7 +142,7 @@ static char *buf_tabs_to_spaces(const char *in_buf, const int tab_size)
BLI_INLINE int text_pixel_x_to_column(SpaceText *st, const int x)
{
- /* add half the char width so mouse cursor selection is inbetween letters */
+ /* Add half the char width so mouse cursor selection is in between letters. */
return (x + (st->cwidth / 2)) / st->cwidth;
}
diff --git a/source/blender/editors/space_view3d/drawobject.c b/source/blender/editors/space_view3d/drawobject.c
index 38e8d285c77..b412a72cce1 100644
--- a/source/blender/editors/space_view3d/drawobject.c
+++ b/source/blender/editors/space_view3d/drawobject.c
@@ -145,7 +145,7 @@ void ED_draw_object_facemap(Depsgraph *depsgraph,
facemap_data = CustomData_get_layer(&me->pdata, CD_FACEMAP);
- /* use gawain immediate mode fore now */
+ /* Make a batch and free it each time for now. */
const int looptris_len = poly_to_tri_count(mpoly_len, mloop_len);
const int vbo_len_capacity = looptris_len * 3;
int vbo_len_used = 0;
diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c
index 7acff998e2b..8c4f2f62602 100644
--- a/source/blender/editors/space_view3d/space_view3d.c
+++ b/source/blender/editors/space_view3d/space_view3d.c
@@ -992,7 +992,7 @@ static void view3d_main_region_message_subscribe(const struct bContext *C,
* accepting some redundant redraws.
*
* For other space types we might try avoid this, keep the 3D view as an exceptional case! */
- wmMsgParams_RNA msg_key_params = {{{0}}};
+ wmMsgParams_RNA msg_key_params = {{0}};
/* Only subscribe to types. */
StructRNA *type_array[] = {
@@ -1177,7 +1177,7 @@ static void view3d_header_region_message_subscribe(const struct bContext *UNUSED
struct ARegion *ar,
struct wmMsgBus *mbus)
{
- wmMsgParams_RNA msg_key_params = {{{0}}};
+ wmMsgParams_RNA msg_key_params = {{0}};
/* Only subscribe to types. */
StructRNA *type_array[] = {
diff --git a/source/blender/editors/space_view3d/view3d_camera_control.c b/source/blender/editors/space_view3d/view3d_camera_control.c
index 0045094542f..260546738f4 100644
--- a/source/blender/editors/space_view3d/view3d_camera_control.c
+++ b/source/blender/editors/space_view3d/view3d_camera_control.c
@@ -87,8 +87,7 @@ typedef struct View3DCameraControl {
float ofs_backup[3];
/* backup the views offset in case the user cancels flying in non camera mode */
- /* backup the views quat in case the user cancels flying in non camera mode.
- * (quat for view, eul for camera) */
+ /* backup the views quat in case the user cancels flying in non camera mode. */
float rot_backup[4];
/* remember if were ortho or not, only used for restoring the view if it was a ortho view */
char persp_backup;
@@ -209,8 +208,8 @@ void ED_view3d_cameracontrol_update(View3DCameraControl *vctrl,
const bool do_rotate,
const bool do_translate)
{
- /* we are in camera view so apply the view ofs and quat to the view matrix and set the camera
- * to the view */
+ /* We are in camera view so apply the view offset and rotation to the view matrix
+ * and set the camera to the view. */
Scene *scene = vctrl->ctx_scene;
View3D *v3d = vctrl->ctx_v3d;
diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c
index 6c534ee1b98..f6afa112f08 100644
--- a/source/blender/editors/space_view3d/view3d_draw.c
+++ b/source/blender/editors/space_view3d/view3d_draw.c
@@ -859,6 +859,51 @@ float ED_view3d_grid_scale(Scene *scene, View3D *v3d, const char **grid_unit)
return v3d->grid * ED_scene_grid_scale(scene, grid_unit);
}
+#define STEPS_LEN 8
+void ED_view3d_grid_steps(Scene *scene,
+ View3D *v3d,
+ RegionView3D *rv3d,
+ float r_grid_steps[STEPS_LEN])
+{
+ const void *usys;
+ int i, len;
+ bUnit_GetSystem(scene->unit.system, B_UNIT_LENGTH, &usys, &len);
+ float grid_scale = v3d->grid;
+
+ if (usys) {
+ if (rv3d->view == RV3D_VIEW_USER) {
+ /* Skip steps */
+ len = bUnit_GetBaseUnit(usys) + 1;
+ }
+
+ grid_scale /= scene->unit.scale_length;
+
+ for (i = 0; i < len; i++) {
+ r_grid_steps[i] = (float)bUnit_GetScaler(usys, len - 1 - i) * grid_scale;
+ }
+ for (; i < STEPS_LEN; i++) {
+ /* Fill last slots */
+ r_grid_steps[i] = 10.0f * r_grid_steps[i - 1];
+ }
+ }
+ else {
+ if (rv3d->view != RV3D_VIEW_USER) {
+ /* Allow 3 more subdivisions. */
+ grid_scale /= powf(v3d->gridsubdiv, 3);
+ }
+ int subdiv = 1;
+ for (i = 0;; i++) {
+ r_grid_steps[i] = grid_scale * subdiv;
+
+ if (i == STEPS_LEN - 1) {
+ break;
+ }
+ subdiv *= v3d->gridsubdiv;
+ }
+ }
+}
+#undef STEPS_LEN
+
/* Simulates the grid scale that is actually viewed.
* The actual code is seen in `object_grid_frag.glsl` (see `grid_res`).
* Currently the simulation is only done when RV3D_VIEW_IS_AXIS. */
@@ -867,24 +912,35 @@ float ED_view3d_grid_view_scale(Scene *scene,
RegionView3D *rv3d,
const char **grid_unit)
{
- float grid_scale = ED_view3d_grid_scale(scene, v3d, grid_unit);
+ float grid_scale;
if (!rv3d->is_persp && RV3D_VIEW_IS_AXIS(rv3d->view)) {
/* Decrease the distance between grid snap points depending on zoom. */
- float grid_subdiv = v3d->gridsubdiv;
- if (grid_subdiv > 1) {
- /* Allow 3 more subdivisions (see OBJECT_engine_init). */
- grid_scale /= powf(grid_subdiv, 3);
-
- /* `3.0` was a value obtained by trial and error in order to get
- * a nice snap distance.*/
- float grid_res = 3.0 * (rv3d->dist / v3d->lens);
- float lvl = (logf(grid_res / grid_scale) / logf(grid_subdiv));
+ /* `0.38` was a value visually obtained in order to get a snap distance
+ * that matches previous versions Blender.*/
+ float min_dist = 0.38f * (rv3d->dist / v3d->lens);
+ float grid_steps[8];
+ ED_view3d_grid_steps(scene, v3d, rv3d, grid_steps);
+ int i;
+ for (i = 0; i < ARRAY_SIZE(grid_steps); i++) {
+ grid_scale = grid_steps[i];
+ if (grid_scale > min_dist) {
+ break;
+ }
+ }
- CLAMP_MIN(lvl, 0.0f);
+ if (grid_unit) {
+ const void *usys;
+ int len;
+ bUnit_GetSystem(scene->unit.system, B_UNIT_LENGTH, &usys, &len);
- grid_scale *= pow(grid_subdiv, (int)lvl);
+ if (usys) {
+ *grid_unit = bUnit_GetNameDisplay(usys, len - i - 1);
+ }
}
}
+ else {
+ grid_scale = ED_view3d_grid_scale(scene, v3d, grid_unit);
+ }
return grid_scale;
}
@@ -1360,6 +1416,27 @@ static void draw_selected_name(
BLF_disable(font_id, BLF_SHADOW);
}
+static void draw_grid_unit_name(
+ Scene *scene, RegionView3D *rv3d, View3D *v3d, int xoffset, int *yoffset)
+{
+ if (!rv3d->is_persp && RV3D_VIEW_IS_AXIS(rv3d->view)) {
+ const char *grid_unit = NULL;
+ ED_view3d_grid_view_scale(scene, v3d, rv3d, &grid_unit);
+
+ if (grid_unit) {
+ char numstr[32] = "";
+ UI_FontThemeColor(BLF_default(), TH_TEXT_HI);
+ if (v3d->grid != 1.0f) {
+ BLI_snprintf(numstr, sizeof(numstr), "%s x %.4g", grid_unit, v3d->grid);
+ }
+
+ *yoffset -= U.widget_unit;
+ BLF_draw_default_ascii(
+ xoffset, *yoffset, 0.0f, numstr[0] ? numstr : grid_unit, sizeof(numstr));
+ }
+ }
+}
+
/**
* Information drawn on top of the solid plates and composed data
*/
@@ -1421,19 +1498,10 @@ void view3d_draw_region_info(const bContext *C, ARegion *ar)
draw_selected_name(scene, view_layer, ob, xoffset, &yoffset);
}
-#if 0 /* TODO */
- if (grid_unit) { /* draw below the viewport name */
- char numstr[32] = "";
-
- UI_FontThemeColor(BLF_default(), TH_TEXT_HI);
- if (v3d->grid != 1.0f) {
- BLI_snprintf(numstr, sizeof(numstr), "%s x %.4g", grid_unit, v3d->grid);
- }
-
- *yoffset -= U.widget_unit;
- BLF_draw_default_ascii(xoffset, *yoffset, numstr[0] ? numstr : grid_unit, sizeof(numstr));
+ if (v3d->gridflag & (V3D_SHOW_FLOOR | V3D_SHOW_X | V3D_SHOW_Y | V3D_SHOW_Z)) {
+ /* draw below the viewport name */
+ draw_grid_unit_name(scene, rv3d, v3d, xoffset, &yoffset);
}
-#endif
}
if ((v3d->overlay.flag & V3D_OVERLAY_HIDE_TEXT) == 0) {
@@ -1492,7 +1560,7 @@ void view3d_main_region_draw(const bContext *C, ARegion *ar)
GPU_pass_cache_garbage_collect();
/* XXX This is in order to draw UI batches with the DRW
- * olg context since we now use it for drawing the entire area */
+ * old context since we now use it for drawing the entire area. */
gpu_batch_presets_reset();
/* No depth test for drawing action zones afterwards. */
diff --git a/source/blender/editors/space_view3d/view3d_draw_legacy.c b/source/blender/editors/space_view3d/view3d_draw_legacy.c
index 02ad481e6ce..d5772e5052a 100644
--- a/source/blender/editors/space_view3d/view3d_draw_legacy.c
+++ b/source/blender/editors/space_view3d/view3d_draw_legacy.c
@@ -162,6 +162,7 @@ static void validate_object_select_id(
Object *obact_eval = DEG_get_evaluated_object(depsgraph, obact);
BLI_assert(ar->regiontype == RGN_TYPE_WINDOW);
+ UNUSED_VARS_NDEBUG(ar);
if (obact_eval && (obact_eval->mode & (OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT) ||
BKE_paint_select_face_test(obact_eval))) {
diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c
index 9a9fe81a13e..e76ef2b0458 100644
--- a/source/blender/editors/space_view3d/view3d_edit.c
+++ b/source/blender/editors/space_view3d/view3d_edit.c
@@ -404,9 +404,7 @@ static void viewops_data_create(bContext *C,
if (viewops_flag & VIEWOPS_FLAG_PERSP_ENSURE) {
if (ED_view3d_persp_ensure(depsgraph, vod->v3d, vod->ar)) {
/* If we're switching from camera view to the perspective one,
- * need to tag viewport update, so camera vuew and borders
- * are properly updated.
- */
+ * need to tag viewport update, so camera view and borders are properly updated. */
ED_region_tag_redraw(vod->ar);
}
}
@@ -4946,6 +4944,7 @@ void ED_view3d_cursor3d_position_rotation(bContext *C,
.use_object_edit_cage = false,
},
mval_fl,
+ NULL,
&dist_px,
ray_co,
ray_no,
@@ -5052,7 +5051,7 @@ void ED_view3d_cursor3d_update(bContext *C,
{
struct wmMsgBus *mbus = CTX_wm_message_bus(C);
- wmMsgParams_RNA msg_key_params = {{{0}}};
+ wmMsgParams_RNA msg_key_params = {{0}};
RNA_pointer_create(&scene->id, &RNA_View3DCursor, &scene->cursor, &msg_key_params.ptr);
WM_msg_publish_rna_params(mbus, &msg_key_params);
}
diff --git a/source/blender/editors/space_view3d/view3d_gizmo_camera.c b/source/blender/editors/space_view3d/view3d_gizmo_camera.c
index 1bb10697936..42931d5abb5 100644
--- a/source/blender/editors/space_view3d/view3d_gizmo_camera.c
+++ b/source/blender/editors/space_view3d/view3d_gizmo_camera.c
@@ -100,7 +100,7 @@ static void WIDGETGROUP_camera_setup(const bContext *C, wmGizmoGroup *gzgroup)
wmGizmo *gz;
gz = cagzgroup->dop_dist = WM_gizmo_new_ptr(gzt_arrow, gzgroup, NULL);
RNA_enum_set(gz->ptr, "draw_style", ED_GIZMO_ARROW_STYLE_CROSS);
- WM_gizmo_set_flag(gz, WM_GIZMO_DRAW_HOVER, true);
+ WM_gizmo_set_flag(gz, WM_GIZMO_DRAW_HOVER | WM_GIZMO_DRAW_NO_SCALE, true);
UI_GetThemeColor3fv(TH_GIZMO_A, gz->color);
UI_GetThemeColor3fv(TH_GIZMO_HI, gz->color_hi);
@@ -152,9 +152,11 @@ static void WIDGETGROUP_camera_refresh(const bContext *C, wmGizmoGroup *gzgroup)
WM_gizmo_set_scale(cagzgroup->dop_dist, ca->drawsize);
WM_gizmo_set_flag(cagzgroup->dop_dist, WM_GIZMO_HIDDEN, false);
- /* need to set property here for undo. TODO would prefer to do this in _init */
+ /* Need to set property here for undo. TODO would prefer to do this in _init */
+ PointerRNA camera_dof_ptr;
+ RNA_pointer_create(&ca->id, &RNA_CameraDOFSettings, &ca->dof, &camera_dof_ptr);
WM_gizmo_target_property_def_rna(
- cagzgroup->dop_dist, "offset", &camera_ptr, "dof.focus_distance", -1);
+ cagzgroup->dop_dist, "offset", &camera_dof_ptr, "focus_distance", -1);
}
else {
WM_gizmo_set_flag(cagzgroup->dop_dist, WM_GIZMO_HIDDEN, true);
diff --git a/source/blender/editors/space_view3d/view3d_gizmo_preselect_type.c b/source/blender/editors/space_view3d/view3d_gizmo_preselect_type.c
index a984e339305..68159e2d684 100644
--- a/source/blender/editors/space_view3d/view3d_gizmo_preselect_type.c
+++ b/source/blender/editors/space_view3d/view3d_gizmo_preselect_type.c
@@ -77,7 +77,20 @@ static void gizmo_preselect_elem_draw(const bContext *UNUSED(C), wmGizmo *gz)
static int gizmo_preselect_elem_test_select(bContext *C, wmGizmo *gz, const int mval[2])
{
+ wmEvent *event = CTX_wm_window(C)->eventstate;
MeshElemGizmo3D *gz_ele = (MeshElemGizmo3D *)gz;
+
+ /* Hack: Switch action mode based on key input */
+ const bool is_ctrl_pressed = WM_event_modifier_flag(event) & KM_CTRL;
+ const bool is_shift_pressed = WM_event_modifier_flag(event) & KM_SHIFT;
+ EDBM_preselect_action_set(gz_ele->psel, PRESELECT_ACTION_TRANSFORM);
+ if (is_ctrl_pressed && !is_shift_pressed) {
+ EDBM_preselect_action_set(gz_ele->psel, PRESELECT_ACTION_CREATE);
+ }
+ if (!is_ctrl_pressed && is_shift_pressed) {
+ EDBM_preselect_action_set(gz_ele->psel, PRESELECT_ACTION_DELETE);
+ }
+
struct {
Object *ob;
BMElem *ele;
@@ -87,18 +100,6 @@ static int gizmo_preselect_elem_test_select(bContext *C, wmGizmo *gz, const int
.dist = ED_view3d_select_dist_px(),
};
- struct {
- int base_index;
- int vert_index;
- int edge_index;
- int face_index;
- } prev = {
- .base_index = gz_ele->base_index,
- .vert_index = gz_ele->vert_index,
- .edge_index = gz_ele->edge_index,
- .face_index = gz_ele->face_index,
- };
-
{
ViewLayer *view_layer = CTX_data_view_layer(C);
View3D *v3d = CTX_wm_view3d(C);
@@ -115,32 +116,66 @@ static int gizmo_preselect_elem_test_select(bContext *C, wmGizmo *gz, const int
{
/* TODO: support faces. */
- int base_index = -1;
+ int base_index_vert = -1;
+ int base_index_edge = -1;
+ int base_index_face = -1;
BMVert *eve_test;
BMEdge *eed_test;
+ BMFace *efa_test;
if (EDBM_unified_findnearest_from_raycast(&vc,
gz_ele->bases,
gz_ele->bases_len,
+ false,
true,
- &base_index,
+ &base_index_vert,
+ &base_index_edge,
+ &base_index_face,
&eve_test,
&eed_test,
- NULL)) {
- Base *base = gz_ele->bases[base_index];
- best.ob = base->object;
- if (eve_test) {
- best.ele = (BMElem *)eve_test;
- }
- else if (eed_test) {
- best.ele = (BMElem *)eed_test;
+ &efa_test)) {
+ if (EDBM_preselect_action_get(gz_ele->psel) == PRESELECT_ACTION_DELETE) {
+ /* Delete action */
+ if (efa_test) {
+ best.ele = (BMElem *)efa_test;
+ best.base_index = base_index_face;
+ }
}
+
else {
- BLI_assert(0);
+ /* Transform and create action */
+ if (eed_test) {
+ best.ele = (BMElem *)eed_test;
+ best.base_index = base_index_edge;
+ }
+ }
+
+ /* All actions use same vertex pre-selection. */
+ /* Re-topology should always prioritize edge pre-selection.
+ * Only pre-select a vertex when the cursor is really close to it. */
+ if (eve_test) {
+ BMVert *vert = (BMVert *)eve_test;
+ float vert_p_co[3], vert_co[3];
+ float mval_f[2] = {UNPACK2(vc.mval)};
+ mul_v3_m4v3(vert_co, gz_ele->bases[base_index_vert]->object->obmat, vert->co);
+ ED_view3d_project(vc.ar, vert_co, vert_p_co);
+ float len = len_v2v2(vert_p_co, mval_f);
+ if (len < 35) {
+ best.ele = (BMElem *)eve_test;
+ best.base_index = base_index_vert;
+ }
+ if (!BM_vert_is_boundary(vert) &&
+ EDBM_preselect_action_get(gz_ele->psel) != PRESELECT_ACTION_DELETE) {
+ best.ele = (BMElem *)eve_test;
+ best.base_index = base_index_vert;
+ }
}
- best.base_index = base_index;
+
/* Check above should never fail, if it does it's an internal error. */
BLI_assert(best.base_index != -1);
+
+ Base *base = gz_ele->bases[best.base_index];
+ best.ob = base->object;
}
}
@@ -167,32 +202,30 @@ static int gizmo_preselect_elem_test_select(bContext *C, wmGizmo *gz, const int
}
}
- if ((prev.base_index == gz_ele->base_index) && (prev.vert_index == gz_ele->vert_index) &&
- (prev.edge_index == gz_ele->edge_index) && (prev.face_index == gz_ele->face_index)) {
- /* pass (only recalculate on change) */
- }
- else {
- if (best.ele) {
- const float(*coords)[3] = NULL;
- {
- Object *ob = gz_ele->bases[gz_ele->base_index]->object;
- Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
- Mesh *me_eval = (Mesh *)DEG_get_evaluated_id(depsgraph, ob->data);
- if (me_eval->runtime.edit_data) {
- coords = me_eval->runtime.edit_data->vertexCos;
- }
+ if (best.ele) {
+ const float(*coords)[3] = NULL;
+ {
+ Object *ob = gz_ele->bases[gz_ele->base_index]->object;
+ Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ Mesh *me_eval = (Mesh *)DEG_get_evaluated_id(depsgraph, ob->data);
+ if (me_eval->runtime.edit_data) {
+ coords = me_eval->runtime.edit_data->vertexCos;
}
- EDBM_preselect_elem_update_from_single(gz_ele->psel, bm, best.ele, coords);
- }
- else {
- EDBM_preselect_elem_clear(gz_ele->psel);
}
+ EDBM_preselect_elem_update_from_single(gz_ele->psel, bm, best.ele, coords);
+ EDBM_preselect_elem_update_preview(gz_ele->psel, &vc, bm, best.ele, mval);
+ }
+ else {
+ EDBM_preselect_elem_clear(gz_ele->psel);
+ EDBM_preselect_preview_clear(gz_ele->psel);
+ }
- RNA_int_set(gz->ptr, "object_index", gz_ele->base_index);
- RNA_int_set(gz->ptr, "vert_index", gz_ele->vert_index);
- RNA_int_set(gz->ptr, "edge_index", gz_ele->edge_index);
- RNA_int_set(gz->ptr, "face_index", gz_ele->face_index);
+ RNA_int_set(gz->ptr, "object_index", gz_ele->base_index);
+ RNA_int_set(gz->ptr, "vert_index", gz_ele->vert_index);
+ RNA_int_set(gz->ptr, "edge_index", gz_ele->edge_index);
+ RNA_int_set(gz->ptr, "face_index", gz_ele->face_index);
+ if (best.ele) {
ARegion *ar = CTX_wm_region(C);
ED_region_tag_redraw(ar);
}
@@ -471,5 +504,4 @@ void ED_view3d_gizmo_mesh_preselect_get_active(bContext *C,
}
}
}
-
/** \} */
diff --git a/source/blender/editors/space_view3d/view3d_gizmo_ruler.c b/source/blender/editors/space_view3d/view3d_gizmo_ruler.c
index 97a12c7100e..f4e3dc85447 100644
--- a/source/blender/editors/space_view3d/view3d_gizmo_ruler.c
+++ b/source/blender/editors/space_view3d/view3d_gizmo_ruler.c
@@ -343,6 +343,7 @@ static bool view3d_ruler_item_mousemove(RulerInfo *ruler_info,
.use_object_edit_cage = true,
},
mval_fl,
+ NULL,
&dist_px,
co,
ray_normal)) {
@@ -363,16 +364,31 @@ static bool view3d_ruler_item_mousemove(RulerInfo *ruler_info,
}
else if (do_snap) {
const float mval_fl[2] = {UNPACK2(mval)};
+ float *prev_point = NULL;
+
+ if (inter->co_index != 1) {
+ if (ruler_item->flag & RULERITEM_USE_ANGLE) {
+ prev_point = ruler_item->co[1];
+ }
+ else if (inter->co_index == 0) {
+ prev_point = ruler_item->co[2];
+ }
+ else {
+ prev_point = ruler_item->co[0];
+ }
+ }
if (ED_transform_snap_object_project_view3d(
ruler_info->snap_context,
- (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE),
+ (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE |
+ SCE_SNAP_MODE_EDGE_MIDPOINT | SCE_SNAP_MODE_EDGE_PERPENDICULAR),
&(const struct SnapObjectParams){
.snap_select = SNAP_ALL,
.use_object_edit_cage = true,
.use_occlusion_test = true,
},
mval_fl,
+ prev_point,
&dist_px,
co,
NULL)) {
diff --git a/source/blender/editors/space_view3d/view3d_intern.h b/source/blender/editors/space_view3d/view3d_intern.h
index 3a6825aacb4..6b5c27b68f4 100644
--- a/source/blender/editors/space_view3d/view3d_intern.h
+++ b/source/blender/editors/space_view3d/view3d_intern.h
@@ -30,18 +30,11 @@
struct ARegion;
struct ARegionType;
-struct Base;
struct BoundBox;
struct Depsgraph;
-struct GPUBatch;
-struct Mesh;
struct Object;
-struct SmokeDomainSettings;
struct ViewLayer;
-struct bAnimVizSettings;
struct bContext;
-struct bMotionPath;
-struct bPoseChannel;
struct wmGizmoGroupType;
struct wmGizmoType;
struct wmKeyConfig;
diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c
index 725f655c8ce..1c9ce142165 100644
--- a/source/blender/editors/space_view3d/view3d_select.c
+++ b/source/blender/editors/space_view3d/view3d_select.c
@@ -88,6 +88,7 @@
#include "ED_particle.h"
#include "ED_mesh.h"
#include "ED_object.h"
+#include "ED_outliner.h"
#include "ED_screen.h"
#include "ED_select_utils.h"
#include "ED_sculpt.h"
@@ -1280,9 +1281,15 @@ static bool view3d_lasso_select(
}
else if (ob && (ob->mode & OB_MODE_POSE)) {
changed_multi |= do_lasso_select_pose(vc, mcords, moves, sel_op);
+ if (changed_multi) {
+ ED_outliner_select_sync_from_pose_bone_tag(C);
+ }
}
else {
changed_multi |= do_lasso_select_objects(vc, mcords, moves, sel_op);
+ if (changed_multi) {
+ ED_outliner_select_sync_from_object_tag(C);
+ }
}
}
else { /* Edit Mode */
@@ -1303,12 +1310,15 @@ static bool view3d_lasso_select(
break;
case OB_ARMATURE:
changed = do_lasso_select_armature(vc, mcords, moves, sel_op);
+ if (changed) {
+ ED_outliner_select_sync_from_edit_bone_tag(C);
+ }
break;
case OB_MBALL:
changed = do_lasso_select_meta(vc, mcords, moves, sel_op);
break;
default:
- assert(!"lasso select on incorrect object type");
+ BLI_assert(!"lasso select on incorrect object type");
break;
}
@@ -1488,6 +1498,9 @@ static int object_select_menu_exec(bContext *C, wmOperator *op)
Scene *scene = CTX_data_scene(C);
DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
+
+ ED_outliner_select_sync_from_object_tag(C);
+
return OPERATOR_FINISHED;
}
else {
@@ -2350,6 +2363,9 @@ static int view3d_select_exec(bContext *C, wmOperator *op)
if (!retval && deselect_all) {
retval = ED_armature_edit_deselect_all_visible_multi(C);
}
+ if (retval) {
+ ED_outliner_select_sync_from_edit_bone_tag(C);
+ }
}
else if (obedit->type == OB_LATTICE) {
retval = ED_lattice_select_pick(C, location, extend, deselect, toggle);
@@ -2410,6 +2426,15 @@ static int view3d_select_exec(bContext *C, wmOperator *op)
DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
}
}
+
+ if (retval) {
+ if (obact && obact->mode & OB_MODE_POSE) {
+ ED_outliner_select_sync_from_pose_bone_tag(C);
+ }
+ else {
+ ED_outliner_select_sync_from_object_tag(C);
+ }
+ }
}
/* Pass-through allows tweaks
@@ -3230,6 +3255,7 @@ static int view3d_box_select_exec(bContext *C, wmOperator *op)
if (changed) {
DEG_id_tag_update(&vc.obedit->id, ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, vc.obedit);
+ ED_outliner_select_sync_from_edit_bone_tag(C);
}
break;
case OB_LATTICE:
@@ -3240,7 +3266,7 @@ static int view3d_box_select_exec(bContext *C, wmOperator *op)
}
break;
default:
- assert(!"box select on incorrect object type");
+ BLI_assert(!"box select on incorrect object type");
break;
}
changed_multi |= changed;
@@ -3264,9 +3290,15 @@ static int view3d_box_select_exec(bContext *C, wmOperator *op)
}
else if (vc.obact && vc.obact->mode & OB_MODE_POSE) {
changed_multi = do_pose_box_select(C, &vc, &rect, sel_op);
+ if (changed_multi) {
+ ED_outliner_select_sync_from_pose_bone_tag(C);
+ }
}
else { /* object mode with none active */
changed_multi = do_object_box_select(C, &vc, &rect, sel_op);
+ if (changed_multi) {
+ ED_outliner_select_sync_from_object_tag(C);
+ }
}
}
@@ -3890,7 +3922,8 @@ static bool mball_circle_select(ViewContext *vc,
/** Callbacks for circle selection in Editmode */
-static bool obedit_circle_select(ViewContext *vc,
+static bool obedit_circle_select(bContext *C,
+ ViewContext *vc,
wmGenericUserData *wm_userdata,
const eSelectOp sel_op,
const int mval[2],
@@ -3911,6 +3944,9 @@ static bool obedit_circle_select(ViewContext *vc,
break;
case OB_ARMATURE:
changed = armature_circle_select(vc, sel_op, mval, rad);
+ if (changed) {
+ ED_outliner_select_sync_from_edit_bone_tag(C);
+ }
break;
case OB_MBALL:
changed = mball_circle_select(vc, sel_op, mval, rad);
@@ -3999,7 +4035,7 @@ static int view3d_circle_select_exec(bContext *C, wmOperator *op)
obedit = vc.obedit;
if (obedit) {
- obedit_circle_select(&vc, wm_userdata, sel_op, mval, (float)radius);
+ obedit_circle_select(C, &vc, wm_userdata, sel_op, mval, (float)radius);
}
else if (BKE_paint_select_face_test(obact)) {
paint_facesel_circle_select(&vc, wm_userdata, sel_op, mval, (float)radius);
@@ -4009,6 +4045,7 @@ static int view3d_circle_select_exec(bContext *C, wmOperator *op)
}
else if (obact->mode & OB_MODE_POSE) {
pose_circle_select(&vc, sel_op, mval, (float)radius);
+ ED_outliner_select_sync_from_pose_bone_tag(C);
}
else {
BLI_assert(0);
@@ -4029,6 +4066,8 @@ static int view3d_circle_select_exec(bContext *C, wmOperator *op)
if (object_circle_select(&vc, sel_op, mval, (float)radius)) {
DEG_id_tag_update(&vc.scene->id, ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, vc.scene);
+
+ ED_outliner_select_sync_from_object_tag(C);
}
}
diff --git a/source/blender/editors/space_view3d/view3d_utils.c b/source/blender/editors/space_view3d/view3d_utils.c
index 7f930f1d876..1af94e3ade5 100644
--- a/source/blender/editors/space_view3d/view3d_utils.c
+++ b/source/blender/editors/space_view3d/view3d_utils.c
@@ -622,8 +622,8 @@ bool ED_view3d_camera_autokey(
/**
* Call after modifying a locked view.
*
- * \note Not every view edit currently auto-keys (numpad for eg),
- * this is complicated because of smoothview.
+ * \note Not every view edit currently auto-keys (num-pad for eg),
+ * this is complicated because of smooth-view.
*/
bool ED_view3d_camera_lock_autokey(View3D *v3d,
RegionView3D *rv3d,
diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c
index 14995144c5c..c344b6c8649 100644
--- a/source/blender/editors/transform/transform.c
+++ b/source/blender/editors/transform/transform.c
@@ -3520,7 +3520,7 @@ static void initShear_mouseInputMode(TransInfo *t)
copy_v3_v3(dir, t->orient_matrix[t->orient_axis_ortho]);
}
else {
- cross_v3_v3v3(dir, t->orient_matrix[t->orient_axis_ortho], t->orient_matrix[t->orient_axis]);
+ cross_v3_v3v3(dir, t->orient_matrix[t->orient_axis], t->orient_matrix[t->orient_axis_ortho]);
}
/* Without this, half the gizmo handles move in the opposite direction. */
@@ -6601,7 +6601,7 @@ static void slide_origdata_interp_data_vert(SlideOrigData *sod,
* and we do not want to mess up other shape keys */
BM_loop_interp_from_face(bm, l, f_copy, false, false);
- /* make sure face-attributes are correct (e.g. MTexPoly) */
+ /* make sure face-attributes are correct (e.g. #MLoopUV, #MLoopCol) */
BM_elem_attrs_copy_ex(sod->bm_origfaces, bm, f_copy, l->f, 0x0, CD_MASK_NORMAL);
/* weight the loop */
diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h
index b023199fa1b..45efde24d83 100644
--- a/source/blender/editors/transform/transform.h
+++ b/source/blender/editors/transform/transform.h
@@ -42,7 +42,6 @@ struct EditBone;
struct NumInput;
struct Object;
struct RNG;
-struct RenderEngineType;
struct ReportList;
struct Scene;
struct ScrArea;
@@ -52,7 +51,6 @@ struct TransDataContainer;
struct TransInfo;
struct TransSnap;
struct TransformOrientation;
-struct View3D;
struct ViewLayer;
struct bConstraint;
struct bContext;
@@ -85,7 +83,9 @@ typedef struct TransSnap {
bool snap_self;
bool peel;
bool snap_spatial_grid;
- short status;
+ char status;
+ /* Snapped Element Type (currently for objects only). */
+ char snapElem;
/** snapping from this point (in global-space). */
float snapPoint[3];
/** to this point (in global-space). */
@@ -375,6 +375,7 @@ typedef struct VertSlideParams {
typedef struct BoneInitData {
struct EditBone *bone;
float tail[3];
+ float rad_head;
float rad_tail;
float roll;
float head[3];
@@ -1164,4 +1165,7 @@ bool checkUseAxisMatrix(TransInfo *t);
th != tc_end; \
th++, i++)
+void trans_obdata_in_obmode_update_all(struct TransInfo *t);
+void trans_obchild_in_obmode_update_all(struct TransInfo *t);
+
#endif
diff --git a/source/blender/editors/transform/transform_conversions.c b/source/blender/editors/transform/transform_conversions.c
index ef9d23d1db8..611992c1d3c 100644
--- a/source/blender/editors/transform/transform_conversions.c
+++ b/source/blender/editors/transform/transform_conversions.c
@@ -52,6 +52,7 @@
#include "BLI_string.h"
#include "BLI_bitmap.h"
#include "BLI_rect.h"
+#include "BLI_kdtree.h"
#include "BKE_action.h"
#include "BKE_animsys.h"
@@ -235,8 +236,9 @@ static void sort_trans_data_selected_first(TransInfo *t)
}
}
-/* distance calculated from not-selected vertex to nearest selected vertex
- * warning; this is loops inside loop, has minor N^2 issues, but by sorting list it is OK */
+/**
+ * Distance calculated from not-selected vertex to nearest selected vertex.
+ */
static void set_prop_dist(TransInfo *t, const bool with_dist)
{
int a;
@@ -255,54 +257,124 @@ static void set_prop_dist(TransInfo *t, const bool with_dist)
}
}
+ /* Count number of selected. */
+ int td_table_len = 0;
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
- TransData *tob = tc->data;
- for (a = 0; a < tc->data_len; a++, tob++) {
+ TransData *td = tc->data;
+ for (a = 0; a < tc->data_len; a++, td++) {
+ if (td->flag & TD_SELECTED) {
+ td_table_len++;
+ }
+ else {
+ /* By definition transform-data has selected items in beginning. */
+ break;
+ }
+ }
+ }
- tob->rdist = 0.0f; // init, it was mallocced
+ /* Pointers to selected's #TransData.
+ * Used to find #TransData from the index returned by #BLI_kdtree_find_nearest. */
+ TransData **td_table = MEM_mallocN(sizeof(*td_table) * td_table_len, __func__);
- if ((tob->flag & TD_SELECTED) == 0) {
- TransData *td;
- int i;
- float dist_sq, vec[3];
+ /* Create and fill kd-tree of selected's positions - in global or proj_vec space. */
+ KDTree_3d *td_tree = BLI_kdtree_3d_new(td_table_len);
- tob->rdist = -1.0f; // signal for next loop
+ int td_table_index = 0;
+ FOREACH_TRANS_DATA_CONTAINER (t, tc) {
+ TransData *td = tc->data;
+ for (a = 0; a < tc->data_len; a++, td++) {
+ if (td->flag & TD_SELECTED) {
+ /* Initialize, it was mallocced. */
+ float vec[3];
+ td->rdist = 0.0f;
+
+ if (use_island) {
+ if (tc->use_local_mat) {
+ mul_v3_m4v3(vec, tc->mat, td->iloc);
+ }
+ else {
+ mul_v3_m3v3(vec, td->mtx, td->iloc);
+ }
+ }
+ else {
+ if (tc->use_local_mat) {
+ mul_v3_m4v3(vec, tc->mat, td->center);
+ }
+ else {
+ mul_v3_m3v3(vec, td->mtx, td->center);
+ }
+ }
- for (i = 0, td = tc->data; i < tc->data_len; i++, td++) {
- if (td->flag & TD_SELECTED) {
- if (use_island) {
- sub_v3_v3v3(vec, tob->iloc, td->iloc);
- }
- else {
- sub_v3_v3v3(vec, tob->center, td->center);
- }
- mul_m3_v3(tob->mtx, vec);
+ if (proj_vec) {
+ float vec_p[3];
+ project_v3_v3v3(vec_p, vec, proj_vec);
+ sub_v3_v3(vec, vec_p);
+ }
- if (proj_vec) {
- float vec_p[3];
- project_v3_v3v3(vec_p, vec, proj_vec);
- sub_v3_v3(vec, vec_p);
- }
+ BLI_kdtree_3d_insert(td_tree, td_table_index, vec);
+ td_table[td_table_index++] = td;
+ }
+ else {
+ /* By definition transform-data has selected items in beginning. */
+ break;
+ }
+ }
+ }
+ BLI_assert(td_table_index == td_table_len);
- dist_sq = len_squared_v3(vec);
- if ((tob->rdist == -1.0f) || (dist_sq < SQUARE(tob->rdist))) {
- tob->rdist = sqrtf(dist_sq);
- if (use_island) {
- copy_v3_v3(tob->center, td->center);
- copy_m3_m3(tob->axismtx, td->axismtx);
- }
- }
+ BLI_kdtree_3d_balance(td_tree);
+
+ /* For each non-selected vertex, find distance to the nearest selected vertex. */
+ FOREACH_TRANS_DATA_CONTAINER (t, tc) {
+ TransData *td = tc->data;
+ for (a = 0; a < tc->data_len; a++, td++) {
+ if ((td->flag & TD_SELECTED) == 0) {
+ float vec[3];
+
+ if (use_island) {
+ if (tc->use_local_mat) {
+ mul_v3_m4v3(vec, tc->mat, td->iloc);
}
else {
- break; /* by definition transdata has selected items in beginning */
+ mul_v3_m3v3(vec, td->mtx, td->iloc);
}
}
+ else {
+ if (tc->use_local_mat) {
+ mul_v3_m4v3(vec, tc->mat, td->center);
+ }
+ else {
+ mul_v3_m3v3(vec, td->mtx, td->center);
+ }
+ }
+
+ if (proj_vec) {
+ float vec_p[3];
+ project_v3_v3v3(vec_p, vec, proj_vec);
+ sub_v3_v3(vec, vec_p);
+ }
+
+ KDTreeNearest_3d nearest;
+ const int td_index = BLI_kdtree_3d_find_nearest(td_tree, vec, &nearest);
+
+ td->rdist = -1.0f;
+ if (td_index != -1) {
+ td->rdist = nearest.dist;
+ if (use_island) {
+ copy_v3_v3(td->center, td_table[td_index]->center);
+ copy_m3_m3(td->axismtx, td_table[td_index]->axismtx);
+ }
+ }
+
if (with_dist) {
- tob->dist = tob->rdist;
+ td->dist = td->rdist;
}
}
}
}
+
+ BLI_kdtree_3d_free(td_tree);
+ MEM_freeN(td_table);
}
/* ************************** CONVERSIONS ************************* */
@@ -1445,6 +1517,7 @@ void restoreBones(TransDataContainer *tc)
ebo = bid->bone;
ebo->dist = bid->dist;
+ ebo->rad_head = bid->rad_head;
ebo->rad_tail = bid->rad_tail;
ebo->roll = bid->roll;
ebo->xwidth = bid->xwidth;
@@ -1710,6 +1783,7 @@ static void createTransArmatureVerts(TransInfo *t)
if (eboflip) {
bid[i].bone = eboflip;
bid[i].dist = eboflip->dist;
+ bid[i].rad_head = eboflip->rad_head;
bid[i].rad_tail = eboflip->rad_tail;
bid[i].roll = eboflip->roll;
bid[i].xwidth = eboflip->xwidth;
@@ -6462,11 +6536,14 @@ static void flush_trans_object_base_deps_flag(Depsgraph *depsgraph, Object *obje
depsgraph, &object->id, DEG_OB_COMP_TRANSFORM, set_trans_object_base_deps_flag_cb, NULL);
}
-static void trans_object_base_deps_flag_finish(ViewLayer *view_layer)
+static void trans_object_base_deps_flag_finish(const TransInfo *t, ViewLayer *view_layer)
{
- for (Base *base = view_layer->object_bases.first; base; base = base->next) {
- if (base->object->id.tag & LIB_TAG_DOIT) {
- base->flag_legacy |= BA_SNAP_FIX_DEPS_FIASCO;
+
+ if ((t->options & CTX_OBMODE_XFORM_OBDATA) == 0) {
+ for (Base *base = view_layer->object_bases.first; base; base = base->next) {
+ if (base->object->id.tag & LIB_TAG_DOIT) {
+ base->flag_legacy |= BA_SNAP_FIX_DEPS_FIASCO;
+ }
}
}
}
@@ -6495,7 +6572,7 @@ static void set_trans_object_base_flags(TransInfo *t)
trans_object_base_deps_flag_prepare(view_layer);
/* Traverse all bases and set all possible flags. */
for (Base *base = view_layer->object_bases.first; base; base = base->next) {
- base->flag_legacy &= ~BA_WAS_SEL;
+ base->flag_legacy &= ~(BA_WAS_SEL | BA_TRANSFORM_LOCKED_IN_PLACE);
if (BASE_SELECTED_EDITABLE(v3d, base)) {
Object *ob = base->object;
Object *parsel = ob->parent;
@@ -6528,7 +6605,7 @@ static void set_trans_object_base_flags(TransInfo *t)
/* Store temporary bits in base indicating that base is being modified
* (directly or indirectly) by transforming objects.
*/
- trans_object_base_deps_flag_finish(view_layer);
+ trans_object_base_deps_flag_finish(t, view_layer);
}
static bool mark_children(Object *ob)
@@ -6596,7 +6673,7 @@ static int count_proportional_objects(TransInfo *t)
/* Store temporary bits in base indicating that base is being modified
* (directly or indirectly) by transforming objects.
*/
- trans_object_base_deps_flag_finish(view_layer);
+ trans_object_base_deps_flag_finish(t, view_layer);
return total;
}
@@ -6611,7 +6688,8 @@ static void clear_trans_object_base_flags(TransInfo *t)
}
base->flag_legacy &= ~(BA_WAS_SEL | BA_SNAP_FIX_DEPS_FIASCO | BA_TEMP_TAG |
- BA_TRANSFORM_CHILD | BA_TRANSFORM_PARENT);
+ BA_TRANSFORM_CHILD | BA_TRANSFORM_PARENT |
+ BA_TRANSFORM_LOCKED_IN_PLACE);
}
}
@@ -7049,7 +7127,15 @@ static void special_aftertrans_update__mesh(bContext *UNUSED(C), TransInfo *t)
hflag = BM_ELEM_SELECT;
}
- EDBM_automerge(t->scene, tc->obedit, true, hflag);
+ if (t->scene->toolsettings->automerge & AUTO_MERGE) {
+ if (t->scene->toolsettings->automerge & AUTO_MERGE_AND_SPLIT) {
+ EDBM_automerge_and_split(
+ tc->obedit, true, true, true, hflag, t->scene->toolsettings->doublimit);
+ }
+ else {
+ EDBM_automerge(tc->obedit, true, hflag, t->scene->toolsettings->doublimit);
+ }
+ }
/* Special case, this is needed or faces won't re-select.
* Flush selected edges to faces. */
@@ -7433,7 +7519,7 @@ void special_aftertrans_update(bContext *C, TransInfo *t)
else if (t->flag & T_POSE && (t->mode == TFM_BONESIZE)) {
/* Handle the exception where for TFM_BONESIZE in edit mode we pretend to be
* in pose mode (to use bone orientation matrix),
- * in that case we don't do operations like autokeyframing. */
+ * in that case we don't do operations like auto-keyframing. */
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
ob = tc->poseobj;
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
@@ -7604,6 +7690,278 @@ int special_transform_moving(TransInfo *t)
return 0;
}
+/* -------------------------------------------------------------------- */
+/** \name Object Mode Custom Data
+ * \{ */
+
+typedef struct TransDataObject {
+
+ /**
+ * Object to object data transform table.
+ * Don't add these to transform data because we may want to include child objects
+ * which aren't being transformed.
+ * - The key is object data #ID.
+ * - The value is #XFormObjectData_Extra.
+ */
+ struct GHash *obdata_in_obmode_map;
+
+ /**
+ * Transform
+ * - The key is object data #Object.
+ * - The value is #XFormObjectSkipChild.
+ */
+ struct GHash *obchild_in_obmode_map;
+
+} TransDataObject;
+
+/** \} */
+
+static void trans_obdata_in_obmode_free_all(TransDataObject *tdo);
+static void trans_obchild_in_obmode_free_all(TransDataObject *tdo);
+
+static void freeTransObjectCustomData(TransInfo *t,
+ TransDataContainer *UNUSED(tc),
+ TransCustomData *custom_data)
+{
+ TransDataObject *tdo = custom_data->data;
+ custom_data->data = NULL;
+
+ if (t->options & CTX_OBMODE_XFORM_OBDATA) {
+ trans_obdata_in_obmode_free_all(tdo);
+ }
+
+ if (t->options & CTX_OBMODE_XFORM_SKIP_CHILDREN) {
+ trans_obchild_in_obmode_free_all(tdo);
+ }
+ MEM_freeN(tdo);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Object Data in Object Mode
+ *
+ * Use to implement 'Affect Only Origins' feature.
+ * We need this to be detached from transform data because,
+ * unlike transforming regular objects, we need to transform the children.
+ *
+ * \{ */
+
+struct XFormObjectData_Extra {
+ Object *ob;
+ float obmat_orig[4][4];
+ struct XFormObjectData *xod;
+};
+
+static void trans_obdata_in_obmode_ensure_object(TransDataObject *tdo, Object *ob)
+{
+ if (tdo->obdata_in_obmode_map == NULL) {
+ tdo->obdata_in_obmode_map = BLI_ghash_ptr_new(__func__);
+ }
+
+ void **xf_p;
+ if (!BLI_ghash_ensure_p(tdo->obdata_in_obmode_map, ob->data, &xf_p)) {
+ struct XFormObjectData_Extra *xf = MEM_mallocN(sizeof(*xf), __func__);
+ copy_m4_m4(xf->obmat_orig, ob->obmat);
+ xf->ob = ob;
+ /* Result may be NULL, that's OK. */
+ xf->xod = ED_object_data_xform_create(ob->data);
+ *xf_p = xf;
+ }
+}
+
+void trans_obdata_in_obmode_update_all(TransInfo *t)
+{
+ TransDataObject *tdo = t->custom.type.data;
+ if (tdo->obdata_in_obmode_map == NULL) {
+ return;
+ }
+
+ struct Main *bmain = CTX_data_main(t->context);
+ BKE_scene_graph_evaluated_ensure(t->depsgraph, bmain);
+
+ GHashIterator gh_iter;
+ GHASH_ITER (gh_iter, tdo->obdata_in_obmode_map) {
+ ID *id = BLI_ghashIterator_getKey(&gh_iter);
+ struct XFormObjectData_Extra *xf = BLI_ghashIterator_getValue(&gh_iter);
+ if (xf->xod == NULL) {
+ continue;
+ }
+
+ Object *ob_eval = DEG_get_evaluated_object(t->depsgraph, xf->ob);
+ float imat[4][4], dmat[4][4];
+ invert_m4_m4(imat, xf->obmat_orig);
+ mul_m4_m4m4(dmat, imat, ob_eval->obmat);
+ invert_m4(dmat);
+
+ ED_object_data_xform_by_mat4(xf->xod, dmat);
+ DEG_id_tag_update(id, ID_RECALC_GEOMETRY);
+ }
+}
+
+/** Callback for #GHash free. */
+static void trans_obdata_in_obmode_free_elem(void *xf_p)
+{
+ struct XFormObjectData_Extra *xf = xf_p;
+ if (xf->xod) {
+ ED_object_data_xform_destroy(xf->xod);
+ }
+ MEM_freeN(xf);
+}
+
+static void trans_obdata_in_obmode_free_all(TransDataObject *tdo)
+{
+ if (tdo->obdata_in_obmode_map != NULL) {
+ BLI_ghash_free(tdo->obdata_in_obmode_map, NULL, trans_obdata_in_obmode_free_elem);
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Object Child Skip
+ *
+ * Don't transform unselected children, this is done using the parent inverse matrix.
+ *
+ * \note The complex logic here is caused by mixed selection within a single selection chain,
+ * otherwise we only need #OB_SKIP_CHILD_PARENT_IS_XFORM for single objects.
+ *
+ * \{ */
+
+enum {
+ /**
+ * The parent is transformed, this is held in place.
+ */
+ OB_SKIP_CHILD_PARENT_IS_XFORM = 1,
+ /**
+ * The same as #OB_SKIP_CHILD_PARENT_IS_XFORM,
+ * however this objects parent isn't transformed directly.
+ */
+ OB_SKIP_CHILD_PARENT_IS_XFORM_INDIRECT = 3,
+ /**
+ * Use the parent invert matrix to apply transformation,
+ * this is needed, because breaks in the selection chain prevents this from being transformed.
+ * This is used to add the transform which would have been added
+ * if there weren't breaks in the parent/child chain.
+ */
+ OB_SKIP_CHILD_PARENT_APPLY_TRANSFORM = 2,
+};
+
+struct XFormObjectSkipChild {
+ float obmat_orig[4][4];
+ float parent_obmat_orig[4][4];
+ float parent_obmat_inv_orig[4][4];
+ float parent_recurse_obmat_orig[4][4];
+ float parentinv_orig[4][4];
+ Object *ob_parent_recurse;
+ int mode;
+};
+
+static void trans_obchild_in_obmode_ensure_object(TransDataObject *tdo,
+ Object *ob,
+ Object *ob_parent_recurse,
+ int mode)
+{
+ if (tdo->obchild_in_obmode_map == NULL) {
+ tdo->obchild_in_obmode_map = BLI_ghash_ptr_new(__func__);
+ }
+
+ void **xf_p;
+ if (!BLI_ghash_ensure_p(tdo->obchild_in_obmode_map, ob, &xf_p)) {
+ struct XFormObjectSkipChild *xf = MEM_mallocN(sizeof(*xf), __func__);
+ copy_m4_m4(xf->parentinv_orig, ob->parentinv);
+ copy_m4_m4(xf->obmat_orig, ob->obmat);
+ copy_m4_m4(xf->parent_obmat_orig, ob->parent->obmat);
+ invert_m4_m4(xf->parent_obmat_inv_orig, ob->parent->obmat);
+ if (ob_parent_recurse) {
+ copy_m4_m4(xf->parent_recurse_obmat_orig, ob_parent_recurse->obmat);
+ }
+ xf->mode = mode;
+ xf->ob_parent_recurse = ob_parent_recurse;
+ *xf_p = xf;
+ }
+}
+
+void trans_obchild_in_obmode_update_all(TransInfo *t)
+{
+ TransDataObject *tdo = t->custom.type.data;
+ if (tdo->obchild_in_obmode_map == NULL) {
+ return;
+ }
+
+ struct Main *bmain = CTX_data_main(t->context);
+ BKE_scene_graph_evaluated_ensure(t->depsgraph, bmain);
+
+ GHashIterator gh_iter;
+ GHASH_ITER (gh_iter, tdo->obchild_in_obmode_map) {
+ Object *ob = BLI_ghashIterator_getKey(&gh_iter);
+ struct XFormObjectSkipChild *xf = BLI_ghashIterator_getValue(&gh_iter);
+
+ /* The following blocks below assign 'dmat'. */
+ float dmat[4][4];
+
+ if (xf->mode == OB_SKIP_CHILD_PARENT_IS_XFORM) {
+ /* Parent is transformed, this isn't so compensate. */
+ Object *ob_parent_eval = DEG_get_evaluated_object(t->depsgraph, ob->parent);
+ mul_m4_m4m4(dmat, xf->parent_obmat_inv_orig, ob_parent_eval->obmat);
+ invert_m4(dmat);
+ }
+ else if (xf->mode == OB_SKIP_CHILD_PARENT_IS_XFORM_INDIRECT) {
+ /* Calculate parent matrix (from the root transform). */
+ Object *ob_parent_recurse_eval = DEG_get_evaluated_object(t->depsgraph,
+ xf->ob_parent_recurse);
+ float parent_recurse_obmat_inv[4][4];
+ invert_m4_m4(parent_recurse_obmat_inv, ob_parent_recurse_eval->obmat);
+ mul_m4_m4m4(dmat, xf->parent_recurse_obmat_orig, parent_recurse_obmat_inv);
+ invert_m4(dmat);
+ float parent_obmat_calc[4][4];
+ mul_m4_m4m4(parent_obmat_calc, dmat, xf->parent_obmat_orig);
+
+ /* Apply to the parent inverse matrix. */
+ mul_m4_m4m4(dmat, xf->parent_obmat_inv_orig, parent_obmat_calc);
+ invert_m4(dmat);
+ }
+ else {
+ BLI_assert(xf->mode == OB_SKIP_CHILD_PARENT_APPLY_TRANSFORM);
+ /* Transform this - without transform data. */
+ Object *ob_parent_recurse_eval = DEG_get_evaluated_object(t->depsgraph,
+ xf->ob_parent_recurse);
+ float parent_recurse_obmat_inv[4][4];
+ invert_m4_m4(parent_recurse_obmat_inv, ob_parent_recurse_eval->obmat);
+ mul_m4_m4m4(dmat, xf->parent_recurse_obmat_orig, parent_recurse_obmat_inv);
+ invert_m4(dmat);
+ float obmat_calc[4][4];
+ mul_m4_m4m4(obmat_calc, dmat, xf->obmat_orig);
+ /* obmat_calc is just obmat. */
+
+ /* Get the matrices relative to the parent. */
+ float obmat_parent_relative_orig[4][4];
+ float obmat_parent_relative_calc[4][4];
+ float obmat_parent_relative_inv_orig[4][4];
+
+ mul_m4_m4m4(obmat_parent_relative_orig, xf->parent_obmat_inv_orig, xf->obmat_orig);
+ mul_m4_m4m4(obmat_parent_relative_calc, xf->parent_obmat_inv_orig, obmat_calc);
+ invert_m4_m4(obmat_parent_relative_inv_orig, obmat_parent_relative_orig);
+
+ /* Apply to the parent inverse matrix. */
+ mul_m4_m4m4(dmat, obmat_parent_relative_calc, obmat_parent_relative_inv_orig);
+ }
+
+ mul_m4_m4m4(ob->parentinv, dmat, xf->parentinv_orig);
+
+ DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM);
+ }
+}
+
+static void trans_obchild_in_obmode_free_all(TransDataObject *tdo)
+{
+ if (tdo->obchild_in_obmode_map != NULL) {
+ BLI_ghash_free(tdo->obchild_in_obmode_map, NULL, MEM_freeN);
+ }
+}
+
+/** \} */
+
static void createTransObject(bContext *C, TransInfo *t)
{
TransData *td = NULL;
@@ -7630,6 +7988,10 @@ static void createTransObject(bContext *C, TransInfo *t)
td = tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransOb");
tx = tc->data_ext = MEM_callocN(tc->data_len * sizeof(TransDataExtension), "TransObExtension");
+ TransDataObject *tdo = MEM_callocN(sizeof(*tdo), __func__);
+ t->custom.type.data = tdo;
+ t->custom.type.free_cb = freeTransObjectCustomData;
+
CTX_DATA_BEGIN (C, Base *, base, selected_bases) {
Object *ob = base->object;
@@ -7648,6 +8010,24 @@ static void createTransObject(bContext *C, TransInfo *t)
td->flag |= TD_SKIP;
}
+ if (t->options & CTX_OBMODE_XFORM_OBDATA) {
+ ID *id = ob->data;
+ if (!id || id->lib) {
+ td->flag |= TD_SKIP;
+ }
+ else if (BKE_object_is_in_editmode(ob)) {
+ /* The object could have edit-mode data from another view-layer,
+ * it's such a corner-case it can be skipped for now - Campbell. */
+ td->flag |= TD_SKIP;
+ }
+ }
+
+ if (t->options & CTX_OBMODE_XFORM_OBDATA) {
+ if ((td->flag & TD_SKIP) == 0) {
+ trans_obdata_in_obmode_ensure_object(tdo, ob);
+ }
+ }
+
ObjectToTransData(t, td, ob);
td->val = NULL;
td++;
@@ -7679,6 +8059,123 @@ static void createTransObject(bContext *C, TransInfo *t)
}
}
}
+
+ if (t->options & CTX_OBMODE_XFORM_OBDATA) {
+ GSet *objects_in_transdata = BLI_gset_ptr_new_ex(__func__, tc->data_len);
+ td = tc->data;
+ for (int i = 0; i < tc->data_len; i++, td++) {
+ if ((td->flag & TD_SKIP) == 0) {
+ BLI_gset_add(objects_in_transdata, td->ob);
+ }
+ }
+
+ ViewLayer *view_layer = t->view_layer;
+ View3D *v3d = t->view;
+
+ for (Base *base = view_layer->object_bases.first; base; base = base->next) {
+ Object *ob = base->object;
+
+ /* if base is not selected, not a parent of selection
+ * or not a child of selection and it is editable and selectable */
+ if ((base->flag_legacy & BA_WAS_SEL) && (base->flag & BASE_SELECTED) == 0 &&
+ BASE_EDITABLE(v3d, base) && BASE_SELECTABLE(v3d, base)) {
+
+ Object *ob_parent = ob->parent;
+ if (ob_parent != NULL) {
+ if (!BLI_gset_haskey(objects_in_transdata, ob)) {
+ bool parent_in_transdata = false;
+ while (ob_parent != NULL) {
+ if (BLI_gset_haskey(objects_in_transdata, ob_parent)) {
+ parent_in_transdata = true;
+ break;
+ }
+ ob_parent = ob_parent->parent;
+ }
+ if (parent_in_transdata) {
+ trans_obdata_in_obmode_ensure_object(tdo, ob);
+ }
+ }
+ }
+ }
+ }
+ BLI_gset_free(objects_in_transdata, NULL);
+ }
+
+ if (t->options & CTX_OBMODE_XFORM_SKIP_CHILDREN) {
+
+#define BASE_XFORM_INDIRECT(base) \
+ ((base->flag_legacy & BA_WAS_SEL) && (base->flag & BASE_SELECTED) == 0)
+
+ GSet *objects_in_transdata = BLI_gset_ptr_new_ex(__func__, tc->data_len);
+ GHash *objects_parent_root = BLI_ghash_ptr_new_ex(__func__, tc->data_len);
+ td = tc->data;
+ for (int i = 0; i < tc->data_len; i++, td++) {
+ if ((td->flag & TD_SKIP) == 0) {
+ BLI_gset_add(objects_in_transdata, td->ob);
+ }
+ }
+
+ ViewLayer *view_layer = t->view_layer;
+
+ for (Base *base = view_layer->object_bases.first; base; base = base->next) {
+ Object *ob = base->object;
+ if (ob->parent != NULL) {
+ if (ob->parent && !BLI_gset_haskey(objects_in_transdata, ob->parent) &&
+ !BLI_gset_haskey(objects_in_transdata, ob)) {
+ if (((base->flag_legacy & BA_WAS_SEL) && (base->flag & BASE_SELECTED) == 0)) {
+ Base *base_parent = BKE_view_layer_base_find(view_layer, ob->parent);
+ if (base_parent && !BASE_XFORM_INDIRECT(base_parent)) {
+ Object *ob_parent_recurse = ob->parent;
+ if (ob_parent_recurse != NULL) {
+ while (ob_parent_recurse != NULL) {
+ if (BLI_gset_haskey(objects_in_transdata, ob_parent_recurse)) {
+ break;
+ }
+ ob_parent_recurse = ob_parent_recurse->parent;
+ }
+
+ if (ob_parent_recurse) {
+ trans_obchild_in_obmode_ensure_object(
+ tdo, ob, ob_parent_recurse, OB_SKIP_CHILD_PARENT_APPLY_TRANSFORM);
+ BLI_ghash_insert(objects_parent_root, ob, ob_parent_recurse);
+ base->flag_legacy |= BA_TRANSFORM_LOCKED_IN_PLACE;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ for (Base *base = view_layer->object_bases.first; base; base = base->next) {
+ Object *ob = base->object;
+
+ if (BASE_XFORM_INDIRECT(base) || BLI_gset_haskey(objects_in_transdata, ob)) {
+ /* pass. */
+ }
+ else if (ob->parent != NULL) {
+ Base *base_parent = BKE_view_layer_base_find(view_layer, ob->parent);
+ if (base_parent) {
+ if (BASE_XFORM_INDIRECT(base_parent) ||
+ BLI_gset_haskey(objects_in_transdata, ob->parent)) {
+ trans_obchild_in_obmode_ensure_object(tdo, ob, NULL, OB_SKIP_CHILD_PARENT_IS_XFORM);
+ base->flag_legacy |= BA_TRANSFORM_LOCKED_IN_PLACE;
+ }
+ else {
+ Object *ob_parent_recurse = BLI_ghash_lookup(objects_parent_root, ob->parent);
+ if (ob_parent_recurse) {
+ trans_obchild_in_obmode_ensure_object(
+ tdo, ob, ob_parent_recurse, OB_SKIP_CHILD_PARENT_IS_XFORM_INDIRECT);
+ }
+ }
+ }
+ }
+ }
+ BLI_gset_free(objects_in_transdata, NULL);
+ BLI_ghash_free(objects_parent_root, NULL, NULL);
+
+#undef BASE_XFORM_INDIRECT
+ }
}
/* transcribe given node into TransData2D for Transforming */
@@ -9665,6 +10162,13 @@ void createTransData(bContext *C, TransInfo *t)
/* Needed for correct Object.obmat after duplication, see: T62135. */
BKE_scene_graph_evaluated_ensure(t->depsgraph, CTX_data_main(t->context));
+ if ((scene->toolsettings->transform_flag & SCE_XFORM_DATA_ORIGIN) != 0) {
+ t->options |= CTX_OBMODE_XFORM_OBDATA;
+ }
+ if ((scene->toolsettings->transform_flag & SCE_XFORM_SKIP_CHILDREN) != 0) {
+ t->options |= CTX_OBMODE_XFORM_SKIP_CHILDREN;
+ }
+
createTransObject(C, t);
countAndCleanTransDataContainer(t);
t->flag |= T_OBJECT;
diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c
index 969e2558abb..7ae140e1815 100644
--- a/source/blender/editors/transform/transform_generics.c
+++ b/source/blender/editors/transform/transform_generics.c
@@ -81,6 +81,7 @@
#include "BKE_workspace.h"
#include "DEG_depsgraph.h"
+#include "DEG_depsgraph_query.h"
#include "ED_anim_api.h"
#include "ED_armature.h"
@@ -900,7 +901,7 @@ static void recalcData_objects(TransInfo *t)
DEG_id_tag_update(tc->obedit->data, 0); /* sets recalc flags */
BMEditMesh *em = BKE_editmesh_from_object(tc->obedit);
EDBM_mesh_normals_update(em);
- BKE_editmesh_tessface_calc(em);
+ BKE_editmesh_looptri_calc(em);
}
}
else if (t->obedit_type == OB_ARMATURE) { /* no recalc flag, does pose */
@@ -1149,6 +1150,14 @@ static void recalcData_objects(TransInfo *t)
/* Update motion paths once for all transformed objects. */
ED_objects_recalculate_paths(t->context, t->scene, true);
}
+
+ if (t->options & CTX_OBMODE_XFORM_SKIP_CHILDREN) {
+ trans_obchild_in_obmode_update_all(t);
+ }
+
+ if (t->options & CTX_OBMODE_XFORM_OBDATA) {
+ trans_obdata_in_obmode_update_all(t);
+ }
}
}
diff --git a/source/blender/editors/transform/transform_gizmo_3d.c b/source/blender/editors/transform/transform_gizmo_3d.c
index fbfeb4f53de..355cf139abf 100644
--- a/source/blender/editors/transform/transform_gizmo_3d.c
+++ b/source/blender/editors/transform/transform_gizmo_3d.c
@@ -1392,7 +1392,8 @@ void drawDial3d(const TransInfo *t)
scale *= ED_view3d_pixel_size_no_ui_scale(t->ar->regiondata, mat_final[3]);
mul_mat3_m4_fl(mat_final, scale);
- if ((t->tsnap.mode & (SCE_SNAP_MODE_INCREMENT | SCE_SNAP_MODE_GRID)) && activeSnap(t)) {
+ if (activeSnap(t) && (!transformModeUseSnap(t) ||
+ (t->tsnap.mode & (SCE_SNAP_MODE_INCREMENT | SCE_SNAP_MODE_GRID)))) {
increment = (t->modifiers & MOD_PRECISION) ? t->snap[2] : t->snap[1];
}
else {
diff --git a/source/blender/editors/transform/transform_input.c b/source/blender/editors/transform/transform_input.c
index 6ebed88878f..e771fe43bd8 100644
--- a/source/blender/editors/transform/transform_input.c
+++ b/source/blender/editors/transform/transform_input.c
@@ -299,7 +299,7 @@ static void calcSpringFactor(MouseInput *mi)
void initMouseInputMode(TransInfo *t, MouseInput *mi, MouseInputMode mode)
{
- /* incase we allocate a new value */
+ /* In case we allocate a new value. */
void *mi_data_prev = mi->data;
mi->use_virtual_mval = true;
diff --git a/source/blender/editors/transform/transform_ops.c b/source/blender/editors/transform/transform_ops.c
index f8e33fe70ad..604c5bbc083 100644
--- a/source/blender/editors/transform/transform_ops.c
+++ b/source/blender/editors/transform/transform_ops.c
@@ -139,22 +139,22 @@ const EnumPropertyItem rna_enum_transform_mode_types[] = {
{TFM_CREASE, "CREASE", 0, "Crease", ""},
{TFM_MIRROR, "MIRROR", 0, "Mirror", ""},
{TFM_BONESIZE, "BONE_SIZE", 0, "Bonesize", ""},
- {TFM_BONE_ENVELOPE, "BONE_ENVELOPE", 0, "Bone_Envelope", ""},
- {TFM_BONE_ENVELOPE_DIST, "BONE_ENVELOPE_DIST", 0, "Bone_Envelope_Distance", ""},
- {TFM_CURVE_SHRINKFATTEN, "CURVE_SHRINKFATTEN", 0, "Curve_Shrinkfatten", ""},
- {TFM_MASK_SHRINKFATTEN, "MASK_SHRINKFATTEN", 0, "Mask_Shrinkfatten", ""},
- {TFM_GPENCIL_SHRINKFATTEN, "GPENCIL_SHRINKFATTEN", 0, "GPencil_Shrinkfatten", ""},
- {TFM_BONE_ROLL, "BONE_ROLL", 0, "Bone_Roll", ""},
- {TFM_TIME_TRANSLATE, "TIME_TRANSLATE", 0, "Time_Translate", ""},
- {TFM_TIME_SLIDE, "TIME_SLIDE", 0, "Time_Slide", ""},
- {TFM_TIME_SCALE, "TIME_SCALE", 0, "Time_Scale", ""},
- {TFM_TIME_EXTEND, "TIME_EXTEND", 0, "Time_Extend", ""},
- {TFM_BAKE_TIME, "BAKE_TIME", 0, "Bake_Time", ""},
+ {TFM_BONE_ENVELOPE, "BONE_ENVELOPE", 0, "Bone Envelope", ""},
+ {TFM_BONE_ENVELOPE_DIST, "BONE_ENVELOPE_DIST", 0, "Bone Envelope Distance", ""},
+ {TFM_CURVE_SHRINKFATTEN, "CURVE_SHRINKFATTEN", 0, "Curve Shrinkfatten", ""},
+ {TFM_MASK_SHRINKFATTEN, "MASK_SHRINKFATTEN", 0, "Mask Shrinkfatten", ""},
+ {TFM_GPENCIL_SHRINKFATTEN, "GPENCIL_SHRINKFATTEN", 0, "GPencil Shrinkfatten", ""},
+ {TFM_BONE_ROLL, "BONE_ROLL", 0, "Bone Roll", ""},
+ {TFM_TIME_TRANSLATE, "TIME_TRANSLATE", 0, "Time Translate", ""},
+ {TFM_TIME_SLIDE, "TIME_SLIDE", 0, "Time Slide", ""},
+ {TFM_TIME_SCALE, "TIME_SCALE", 0, "Time Scale", ""},
+ {TFM_TIME_EXTEND, "TIME_EXTEND", 0, "Time Extend", ""},
+ {TFM_BAKE_TIME, "BAKE_TIME", 0, "Bake Time", ""},
{TFM_BWEIGHT, "BWEIGHT", 0, "Bweight", ""},
{TFM_ALIGN, "ALIGN", 0, "Align", ""},
{TFM_EDGE_SLIDE, "EDGESLIDE", 0, "Edge Slide", ""},
{TFM_SEQ_SLIDE, "SEQSLIDE", 0, "Sequence Slide", ""},
- {TFM_GPENCIL_OPACITY, "GPENCIL_OPACITY", 0, "GPencil_Opacity", ""},
+ {TFM_GPENCIL_OPACITY, "GPENCIL_OPACITY", 0, "GPencil Opacity", ""},
{0, NULL, 0, NULL, NULL},
};
@@ -573,7 +573,7 @@ void Transform_Properties(struct wmOperatorType *ot, int flags)
if (flags & P_ORIENT_AXIS_ORTHO) {
prop = RNA_def_property(ot->srna, "orient_axis_ortho", PROP_ENUM, PROP_NONE);
RNA_def_property_ui_text(prop, "Axis Ortho", "");
- RNA_def_property_enum_default(prop, 1);
+ RNA_def_property_enum_default(prop, 0);
RNA_def_property_enum_items(prop, rna_enum_axis_xyz_items);
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
}
diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c
index dbcc6c1b04a..571ce7a6bc2 100644
--- a/source/blender/editors/transform/transform_snap.c
+++ b/source/blender/editors/transform/transform_snap.c
@@ -166,7 +166,10 @@ void drawSnapping(const struct bContext *C, TransInfo *t)
activeCol[3] = 192;
if (t->spacetype == SPACE_VIEW3D) {
- if (validSnap(t)) {
+ bool draw_target = (t->tsnap.status & TARGET_INIT) &&
+ (t->scene->toolsettings->snap_mode & SCE_SNAP_MODE_EDGE_PERPENDICULAR);
+
+ if (draw_target || validSnap(t)) {
TransSnapPoint *p;
RegionView3D *rv3d = CTX_wm_region_view3d(C);
float imat[4][4];
@@ -214,6 +217,48 @@ void drawSnapping(const struct bContext *C, TransInfo *t)
immEnd();
}
+ if (draw_target) {
+ /* Draw snapTarget */
+ float targ_co[3], vx[3], vy[3], v1[3], v2[3], v3[3], v4[4];
+ copy_v3_v3(targ_co, t->tsnap.snapTarget);
+ float px_size = 0.75f * size * ED_view3d_pixel_size(rv3d, targ_co);
+
+ mul_v3_v3fl(vx, imat[0], px_size);
+ mul_v3_v3fl(vy, imat[1], px_size);
+
+ add_v3_v3v3(v1, vx, vy);
+ sub_v3_v3v3(v2, vx, vy);
+ negate_v3_v3(v3, v1);
+ negate_v3_v3(v4, v2);
+
+ add_v3_v3(v1, targ_co);
+ add_v3_v3(v2, targ_co);
+ add_v3_v3(v3, targ_co);
+ add_v3_v3(v4, targ_co);
+
+ immUniformColor4ubv(col);
+ immBegin(GPU_PRIM_LINES, 4);
+ immVertex3fv(pos, v3);
+ immVertex3fv(pos, v1);
+ immVertex3fv(pos, v4);
+ immVertex3fv(pos, v2);
+ immEnd();
+
+ if (t->tsnap.snapElem & SCE_SNAP_MODE_EDGE_PERPENDICULAR) {
+ immUnbindProgram();
+
+ immBindBuiltinProgram(GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR);
+ immUniform1f("dash_width", 6.0f * U.pixelsize);
+ immUniform1f("dash_factor", 1.0f / 4.0f);
+ immUniformColor4ubv(col);
+
+ immBegin(GPU_PRIM_LINES, 2);
+ immVertex3fv(pos, targ_co);
+ immVertex3fv(pos, t->tsnap.snapPoint);
+ immEnd();
+ }
+ }
+
immUnbindProgram();
GPU_depth_test(true);
@@ -328,6 +373,7 @@ void applyProject(TransInfo *t)
.use_occlusion_test = false,
},
mval_fl,
+ NULL,
0,
loc,
no)) {
@@ -586,12 +632,18 @@ static void initSnappingMode(TransInfo *t)
else if (t->tsnap.applySnap != NULL && // A snapping function actually exist
(obedit_type == -1)) // Object Mode
{
- /* In "Edit Strokes" mode,
- * snap tool can perform snap to selected or active objects (see T49632)
- * TODO: perform self snap in gpencil_strokes */
- t->tsnap.modeSelect = (((t->options & (CTX_GPENCIL_STROKES | CTX_CURSOR)) != 0) ?
- SNAP_ALL :
- SNAP_NOT_SELECTED);
+
+ 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). */
+ t->tsnap.modeSelect = SNAP_ALL;
+ }
+ else {
+ t->tsnap.modeSelect = SNAP_NOT_SELECTED;
+ }
}
else {
/* Grid if snap is not possible */
@@ -993,18 +1045,25 @@ static void CalcSnapGeometry(TransInfo *t, float *UNUSED(vec))
float no[3];
float mval[2];
bool found = false;
+ short snap_elem = 0;
float dist_px = SNAP_MIN_DISTANCE; // Use a user defined value here
mval[0] = t->mval[0];
mval[1] = t->mval[1];
- if (t->tsnap.mode & (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE)) {
+ if (t->tsnap.mode & (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE |
+ SCE_SNAP_MODE_EDGE_MIDPOINT | SCE_SNAP_MODE_EDGE_PERPENDICULAR)) {
zero_v3(no); /* objects won't set this */
- found = snapObjectsTransform(t, mval, &dist_px, loc, no);
+ snap_elem = snapObjectsTransform(t, mval, &dist_px, loc, no);
+ found = snap_elem != 0;
}
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);
+
+ if (found) {
+ snap_elem = SCE_SNAP_MODE_VOLUME;
+ }
}
if (found == true) {
@@ -1016,6 +1075,8 @@ static void CalcSnapGeometry(TransInfo *t, float *UNUSED(vec))
else {
t->tsnap.status &= ~POINT_INIT;
}
+
+ t->tsnap.snapElem = (char)snap_elem;
}
else if (t->spacetype == SPACE_IMAGE && t->obedit_type == OB_MESH) {
if (t->tsnap.mode & SCE_SNAP_MODE_VERTEX) {
@@ -1186,7 +1247,11 @@ static void TargetSnapClosest(TransInfo *t)
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
TransData *td = tc->data;
for (td = tc->data, i = 0; i < tc->data_len && td->flag & TD_SELECTED; i++, td++) {
- struct BoundBox *bb = BKE_object_boundbox_get(td->ob);
+ const BoundBox *bb = NULL;
+
+ if ((t->options & CTX_OBMODE_XFORM_OBDATA) == 0) {
+ bb = BKE_object_boundbox_get(td->ob);
+ }
/* use boundbox if possible */
if (bb) {
@@ -1259,10 +1324,10 @@ static void TargetSnapClosest(TransInfo *t)
}
}
-bool snapObjectsTransform(
+short snapObjectsTransform(
TransInfo *t, const float mval[2], float *dist_px, float r_loc[3], float r_no[3])
{
- return ED_transform_snap_object_project_view3d(
+ return ED_transform_snap_object_project_view3d_ex(
t->tsnap.object_context,
t->scene->toolsettings->snap_mode,
&(const struct SnapObjectParams){
@@ -1271,9 +1336,13 @@ bool snapObjectsTransform(
.use_occlusion_test = t->scene->toolsettings->snap_mode != SCE_SNAP_MODE_FACE,
},
mval,
+ t->tsnap.snapTarget,
dist_px,
r_loc,
- r_no);
+ r_no,
+ NULL,
+ NULL,
+ NULL);
}
/******************** PEELING *********************************/
diff --git a/source/blender/editors/transform/transform_snap_object.c b/source/blender/editors/transform/transform_snap_object.c
index 67bd107ab49..d3cf52ce3fd 100644
--- a/source/blender/editors/transform/transform_snap_object.c
+++ b/source/blender/editors/transform/transform_snap_object.c
@@ -221,22 +221,40 @@ static void iter_snap_objects(SnapObjectContext *sctx,
Base *base_act = view_layer->basact;
for (Base *base = view_layer->object_bases.first; base != NULL; base = base->next) {
- if ((BASE_VISIBLE(v3d, base)) && (base->flag_legacy & BA_SNAP_FIX_DEPS_FIASCO) == 0 &&
- !((snap_select == SNAP_NOT_SELECTED &&
- ((base->flag & BASE_SELECTED) || (base->flag_legacy & BA_WAS_SEL))) ||
- (snap_select == SNAP_NOT_ACTIVE && base == base_act))) {
- Object *obj_eval = DEG_get_evaluated_object(sctx->depsgraph, base->object);
- if (obj_eval->transflag & OB_DUPLI) {
- DupliObject *dupli_ob;
- ListBase *lb = object_duplilist(sctx->depsgraph, sctx->scene, obj_eval);
- for (dupli_ob = lb->first; dupli_ob; dupli_ob = dupli_ob->next) {
- sob_callback(sctx, use_object_edit_cage, dupli_ob->ob, dupli_ob->mat, data);
- }
- free_object_duplilist(lb);
+
+ if (!BASE_VISIBLE(v3d, base)) {
+ continue;
+ }
+
+ if (base->flag_legacy & BA_TRANSFORM_LOCKED_IN_PLACE) {
+ /* pass */
+ }
+ else if (base->flag_legacy & BA_SNAP_FIX_DEPS_FIASCO) {
+ continue;
+ }
+
+ if (snap_select == SNAP_NOT_SELECTED) {
+ if ((base->flag & BASE_SELECTED) || (base->flag_legacy & BA_WAS_SEL)) {
+ continue;
+ }
+ }
+ else if (snap_select == SNAP_NOT_ACTIVE) {
+ if (base == base_act) {
+ continue;
}
+ }
- sob_callback(sctx, use_object_edit_cage, obj_eval, obj_eval->obmat, data);
+ Object *obj_eval = DEG_get_evaluated_object(sctx->depsgraph, base->object);
+ if (obj_eval->transflag & OB_DUPLI) {
+ DupliObject *dupli_ob;
+ ListBase *lb = object_duplilist(sctx->depsgraph, sctx->scene, obj_eval);
+ for (dupli_ob = lb->first; dupli_ob; dupli_ob = dupli_ob->next) {
+ sob_callback(sctx, use_object_edit_cage, dupli_ob->ob, dupli_ob->mat, data);
+ }
+ free_object_duplilist(lb);
}
+
+ sob_callback(sctx, use_object_edit_cage, obj_eval, obj_eval->obmat, data);
}
}
@@ -583,37 +601,31 @@ static bool raycastEditMesh(SnapObjectContext *sctx,
}
if (treedata->tree == NULL) {
- BVHCache **bvh_cache = NULL;
- BLI_bitmap *elem_mask = NULL;
- BMEditMesh *em_orig;
- int looptri_num_active = -1;
-
/* Get original version of the edit_mesh. */
- em_orig = BKE_editmesh_from_object(DEG_get_original_object(ob));
+ BMEditMesh *em_orig = BKE_editmesh_from_object(DEG_get_original_object(ob));
if (sctx->callbacks.edit_mesh.test_face_fn) {
BMesh *bm = em_orig->bm;
BLI_assert(poly_to_tri_count(bm->totface, bm->totloop) == em_orig->tottri);
- elem_mask = BLI_BITMAP_NEW(em_orig->tottri, __func__);
- looptri_num_active = BM_iter_mesh_bitmap_from_filter_tessface(
+ BLI_bitmap *elem_mask = BLI_BITMAP_NEW(em_orig->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_orig, elem_mask, looptri_num_active, 0.0f, 4, 6, 0, NULL);
+
+ MEM_freeN(elem_mask);
}
else {
/* Only cache if bvhtree is created without a mask.
* This helps keep a standardized bvhtree in cache. */
- bvh_cache = em_bvh_cache;
+ BKE_bvhtree_from_editmesh_get(treedata, em_orig, 4, BVHTREE_FROM_EM_LOOPTRI, em_bvh_cache);
}
- bvhtree_from_editmesh_looptri_ex(
- treedata, em_orig, elem_mask, looptri_num_active, 0.0f, 4, 6, bvh_cache);
-
- if (elem_mask) {
- MEM_freeN(elem_mask);
- }
if (treedata->tree == NULL) {
return retval;
}
@@ -1260,7 +1272,8 @@ static short snap_mesh_polygon(SnapObjectContext *sctx,
const MPoly *mp = &((SnapObjectData_Mesh *)sod)->poly[*r_index];
const MLoop *ml = &treedata->loop[mp->loopstart];
- if (snapdata->snap_to_flag & SCE_SNAP_MODE_EDGE) {
+ if (snapdata->snap_to_flag &
+ (SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_EDGE_MIDPOINT | SCE_SNAP_MODE_EDGE_PERPENDICULAR)) {
elem = SCE_SNAP_MODE_EDGE;
BLI_assert(treedata->edge != NULL);
for (int i = mp->totloop; i--; ml++) {
@@ -1297,7 +1310,8 @@ static short snap_mesh_polygon(SnapObjectContext *sctx,
BMFace *f = BM_face_at_index(em->bm, *r_index);
BMLoop *l_iter, *l_first;
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
- if (snapdata->snap_to_flag & SCE_SNAP_MODE_EDGE) {
+ if (snapdata->snap_to_flag &
+ (SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_EDGE_MIDPOINT | SCE_SNAP_MODE_EDGE_PERPENDICULAR)) {
elem = SCE_SNAP_MODE_EDGE;
BM_mesh_elem_index_ensure(em->bm, BM_EDGE);
BM_mesh_elem_table_ensure(em->bm, BM_VERT | BM_EDGE);
@@ -1352,6 +1366,7 @@ static short snap_mesh_edge_verts_mixed(SnapObjectContext *sctx,
Object *ob,
float obmat[4][4],
float original_dist_px,
+ const float prev_co[3],
/* read/write args */
float *dist_px,
/* return args */
@@ -1420,19 +1435,74 @@ static short snap_mesh_edge_verts_mixed(SnapObjectContext *sctx,
&lambda)) {
/* do nothing */
}
- else if (lambda < 0.25f || 0.75f < lambda) {
- int v_id = lambda < 0.5f ? 0 : 1;
+ else {
+ if (snapdata->snap_to_flag & SCE_SNAP_MODE_VERTEX) {
+ if (lambda < 0.25f || 0.75f < lambda) {
+ int v_id = lambda < 0.5f ? 0 : 1;
- if (test_projected_vert_dist(&neasrest_precalc,
- NULL,
- 0,
- nearest2d.is_persp,
- v_pair[v_id],
- &nearest.dist_sq,
- nearest.co)) {
- nearest.index = vindex[v_id];
- nearest2d.copy_vert_no(vindex[v_id], nearest.no, nearest2d.userdata);
- elem = SCE_SNAP_MODE_VERTEX;
+ if (test_projected_vert_dist(&neasrest_precalc,
+ NULL,
+ 0,
+ nearest2d.is_persp,
+ v_pair[v_id],
+ &nearest.dist_sq,
+ nearest.co)) {
+ nearest.index = vindex[v_id];
+ nearest2d.copy_vert_no(vindex[v_id], nearest.no, nearest2d.userdata);
+ elem = SCE_SNAP_MODE_VERTEX;
+ }
+ }
+ }
+
+ if (snapdata->snap_to_flag & SCE_SNAP_MODE_EDGE_MIDPOINT) {
+ if (0.375f < lambda && lambda < 0.625f) {
+ float vmid[3];
+ mid_v3_v3v3(vmid, v_pair[0], v_pair[1]);
+
+ if (test_projected_vert_dist(&neasrest_precalc,
+ NULL,
+ 0,
+ nearest2d.is_persp,
+ vmid,
+ &nearest.dist_sq,
+ nearest.co)) {
+ float v_nor[2][3];
+ nearest2d.copy_vert_no(vindex[0], v_nor[0], nearest2d.userdata);
+ nearest2d.copy_vert_no(vindex[1], v_nor[1], nearest2d.userdata);
+ mid_v3_v3v3(nearest.no, v_nor[0], v_nor[1]);
+ nearest.index = *r_index;
+ elem = SCE_SNAP_MODE_EDGE_MIDPOINT;
+ }
+ }
+ }
+
+ if (prev_co && (snapdata->snap_to_flag & SCE_SNAP_MODE_EDGE_PERPENDICULAR)) {
+ float v_near[3], va_g[3], vb_g[3];
+
+ mul_v3_m4v3(va_g, obmat, v_pair[0]);
+ mul_v3_m4v3(vb_g, obmat, v_pair[1]);
+ lambda = line_point_factor_v3(prev_co, va_g, vb_g);
+
+ if (IN_RANGE(lambda, 0.0f, 1.0f)) {
+ interp_v3_v3v3(v_near, va_g, vb_g, lambda);
+
+ if ((len_squared_v3v3(prev_co, v_near) > FLT_EPSILON) &&
+ test_projected_vert_dist(&neasrest_precalc,
+ NULL,
+ 0,
+ nearest2d.is_persp,
+ v_near,
+ &nearest.dist_sq,
+ nearest.co)) {
+ float v_nor[2][3];
+ nearest2d.copy_vert_no(vindex[0], v_nor[0], nearest2d.userdata);
+ nearest2d.copy_vert_no(vindex[1], v_nor[1], nearest2d.userdata);
+ mid_v3_v3v3(nearest.no, v_nor[0], v_nor[1]);
+
+ nearest.index = *r_index;
+ elem = SCE_SNAP_MODE_EDGE_PERPENDICULAR;
+ }
+ }
}
}
@@ -1440,7 +1510,9 @@ static short snap_mesh_edge_verts_mixed(SnapObjectContext *sctx,
*dist_px = sqrtf(nearest.dist_sq);
copy_v3_v3(r_loc, nearest.co);
- mul_m4_v3(obmat, r_loc);
+ if (elem != SCE_SNAP_MODE_EDGE_PERPENDICULAR) {
+ mul_m4_v3(obmat, r_loc);
+ }
if (r_no) {
float imat[4][4];
@@ -1926,13 +1998,13 @@ static short snapMesh(SnapObjectContext *sctx,
{
BLI_assert(snapdata->snap_to_flag != SCE_SNAP_MODE_FACE);
- if ((snapdata->snap_to_flag & ~SCE_SNAP_MODE_FACE) == SCE_SNAP_MODE_EDGE) {
- if (me->totedge == 0) {
+ if ((snapdata->snap_to_flag & ~SCE_SNAP_MODE_FACE) == SCE_SNAP_MODE_VERTEX) {
+ if (me->totvert == 0) {
return 0;
}
}
else {
- if (me->totvert == 0) {
+ if (me->totedge == 0) {
return 0;
}
}
@@ -2066,7 +2138,8 @@ static short snapMesh(SnapObjectContext *sctx,
last_index = nearest.index;
}
- if (snapdata->snap_to_flag & SCE_SNAP_MODE_EDGE) {
+ if (snapdata->snap_to_flag &
+ (SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_EDGE_MIDPOINT | SCE_SNAP_MODE_EDGE_PERPENDICULAR)) {
if (bvhtree[0]) {
/* snap to loose edges */
BLI_bvhtree_find_nearest_projected(bvhtree[0],
@@ -2164,13 +2237,13 @@ static short snapEditMesh(SnapObjectContext *sctx,
{
BLI_assert(snapdata->snap_to_flag != SCE_SNAP_MODE_FACE);
- if ((snapdata->snap_to_flag & ~SCE_SNAP_MODE_FACE) == SCE_SNAP_MODE_EDGE) {
- if (em->bm->totedge == 0) {
+ if ((snapdata->snap_to_flag & ~SCE_SNAP_MODE_FACE) == SCE_SNAP_MODE_VERTEX) {
+ if (em->bm->totvert == 0) {
return 0;
}
}
else {
- if (em->bm->totvert == 0) {
+ if (em->bm->totedge == 0) {
return 0;
}
}
@@ -2223,16 +2296,17 @@ static short snapEditMesh(SnapObjectContext *sctx,
sctx->callbacks.edit_mesh.user_data);
bvhtree_from_editmesh_verts_ex(
- treedata_vert, em, verts_mask, verts_num_active, 0.0f, 2, 6);
+ treedata_vert, em, verts_mask, verts_num_active, 0.0f, 2, 6, 0, NULL);
MEM_freeN(verts_mask);
}
else {
- bvhtree_from_editmesh_verts(treedata_vert, em, 0.0f, 2, 6, em_bvh_cache);
+ BKE_bvhtree_from_editmesh_get(treedata_vert, em, 2, BVHTREE_FROM_EM_VERTS, em_bvh_cache);
}
}
}
- if (snapdata->snap_to_flag & SCE_SNAP_MODE_EDGE) {
+ if (snapdata->snap_to_flag &
+ (SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_EDGE_MIDPOINT | SCE_SNAP_MODE_EDGE_PERPENDICULAR)) {
if (sod->bvh_trees[1] == NULL) {
sod->bvh_trees[1] = BLI_memarena_calloc(sctx->cache.mem_arena, sizeof(**sod->bvh_trees));
}
@@ -2258,11 +2332,11 @@ static short snapEditMesh(SnapObjectContext *sctx,
sctx->callbacks.edit_mesh.user_data);
bvhtree_from_editmesh_edges_ex(
- treedata_edge, em, edges_mask, edges_num_active, 0.0f, 2, 6);
+ treedata_edge, em, edges_mask, edges_num_active, 0.0f, 2, 6, 0, NULL);
MEM_freeN(edges_mask);
}
else {
- bvhtree_from_editmesh_edges(treedata_edge, em, 0.0f, 2, 6, em_bvh_cache);
+ BKE_bvhtree_from_editmesh_get(treedata_edge, em, 2, BVHTREE_FROM_EM_EDGES, em_bvh_cache);
}
}
}
@@ -2279,7 +2353,6 @@ static short snapEditMesh(SnapObjectContext *sctx,
.index = -1,
.dist_sq = dist_px_sq,
};
- int last_index = nearest.index;
short elem = SCE_SNAP_MODE_VERTEX;
float tobmat[4][4], clip_planes_local[MAX_CLIPPLANE_LEN][4];
@@ -2300,11 +2373,12 @@ static short snapEditMesh(SnapObjectContext *sctx,
&nearest,
cb_snap_vert,
&nearest2d);
-
- last_index = nearest.index;
}
- if (treedata_edge && snapdata->snap_to_flag & SCE_SNAP_MODE_EDGE) {
+ if (treedata_edge && snapdata->snap_to_flag & (SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_EDGE_MIDPOINT |
+ SCE_SNAP_MODE_EDGE_PERPENDICULAR)) {
+ int last_index = nearest.index;
+ nearest.index = -1;
BM_mesh_elem_table_ensure(em->bm, BM_EDGE | BM_VERT);
BLI_bvhtree_find_nearest_projected(treedata_edge->tree,
lpmat,
@@ -2316,9 +2390,12 @@ static short snapEditMesh(SnapObjectContext *sctx,
cb_snap_edge,
&nearest2d);
- if (last_index != nearest.index) {
+ if (nearest.index != -1) {
elem = SCE_SNAP_MODE_EDGE;
}
+ else {
+ nearest.index = last_index;
+ }
}
if (nearest.index != -1) {
@@ -2700,6 +2777,7 @@ static short transform_snap_context_project_view3d_mixed_impl(
const unsigned short snap_to_flag,
const struct SnapObjectParams *params,
const float mval[2],
+ const float prev_co[3],
float *dist_px,
float r_loc[3],
float r_no[3],
@@ -2707,7 +2785,8 @@ static short transform_snap_context_project_view3d_mixed_impl(
Object **r_ob,
float r_obmat[4][4])
{
- BLI_assert((snap_to_flag & (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE)) !=
+ BLI_assert((snap_to_flag & (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE |
+ SCE_SNAP_MODE_EDGE_MIDPOINT | SCE_SNAP_MODE_EDGE_PERPENDICULAR)) !=
0);
short retval = 0;
@@ -2733,7 +2812,7 @@ static short transform_snap_context_project_view3d_mixed_impl(
ray_normal,
ray_start,
true)) {
- return false;
+ return 0;
}
float dummy_ray_depth = BVH_RAYCAST_DIST_MAX;
@@ -2746,7 +2825,8 @@ static short transform_snap_context_project_view3d_mixed_impl(
}
}
- if (snap_to_flag & (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE)) {
+ if (snap_to_flag & (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_EDGE_MIDPOINT |
+ SCE_SNAP_MODE_EDGE_PERPENDICULAR)) {
short elem;
float dist_px_tmp = *dist_px;
@@ -2799,11 +2879,18 @@ static short transform_snap_context_project_view3d_mixed_impl(
retval = elem;
}
- if ((retval == SCE_SNAP_MODE_EDGE) && (snapdata.snap_to_flag & SCE_SNAP_MODE_VERTEX)) {
- retval = snap_mesh_edge_verts_mixed(
- sctx, &snapdata, ob, obmat, *dist_px, &dist_px_tmp, loc, no, &index);
+ if ((retval == SCE_SNAP_MODE_EDGE) &&
+ (snap_to_flag & (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE_MIDPOINT |
+ SCE_SNAP_MODE_EDGE_PERPENDICULAR))) {
+ elem = snap_mesh_edge_verts_mixed(
+ sctx, &snapdata, ob, obmat, *dist_px, prev_co, &dist_px_tmp, loc, no, &index);
+ }
+
+ if (elem) {
+ retval = elem;
}
+ retval &= snap_to_flag;
*dist_px = dist_px_tmp;
}
@@ -2831,6 +2918,7 @@ short ED_transform_snap_object_project_view3d_ex(SnapObjectContext *sctx,
const unsigned short snap_to,
const struct SnapObjectParams *params,
const float mval[2],
+ const float prev_co[3],
float *dist_px,
float r_loc[3],
float r_no[3],
@@ -2839,7 +2927,7 @@ short ED_transform_snap_object_project_view3d_ex(SnapObjectContext *sctx,
float r_obmat[4][4])
{
return transform_snap_context_project_view3d_mixed_impl(
- sctx, snap_to, params, mval, dist_px, r_loc, r_no, r_index, r_ob, r_obmat) != 0;
+ sctx, snap_to, params, mval, prev_co, dist_px, r_loc, r_no, r_index, r_ob, r_obmat);
}
/**
@@ -2849,6 +2937,7 @@ short ED_transform_snap_object_project_view3d_ex(SnapObjectContext *sctx,
*
* \param sctx: Snap context.
* \param mval: Screenspace coordinate.
+ * \param prev_co: Coordinate for perpendicular point calculation (optional).
* \param dist_px: Maximum distance to snap (in pixels).
* \param r_co: hit location.
* \param r_no: hit normal (optional).
@@ -2858,12 +2947,13 @@ bool ED_transform_snap_object_project_view3d(SnapObjectContext *sctx,
const unsigned short snap_to,
const struct SnapObjectParams *params,
const float mval[2],
+ const float prev_co[3],
float *dist_px,
float r_loc[3],
float r_no[3])
{
return ED_transform_snap_object_project_view3d_ex(
- sctx, snap_to, params, mval, dist_px, r_loc, r_no, NULL, NULL, NULL) != 0;
+ sctx, snap_to, params, mval, prev_co, dist_px, r_loc, r_no, NULL, NULL, NULL) != 0;
}
/**
diff --git a/source/blender/editors/undo/ed_undo.c b/source/blender/editors/undo/ed_undo.c
index c84d7539cce..e0f4d1cf465 100644
--- a/source/blender/editors/undo/ed_undo.c
+++ b/source/blender/editors/undo/ed_undo.c
@@ -53,6 +53,7 @@
#include "ED_gpencil.h"
#include "ED_render.h"
#include "ED_object.h"
+#include "ED_outliner.h"
#include "ED_screen.h"
#include "ED_undo.h"
@@ -390,6 +391,8 @@ static int ed_undo_exec(bContext *C, wmOperator *op)
/* Keep button under the cursor active. */
WM_event_add_mousemove(C);
}
+
+ ED_outliner_select_sync_from_all_tag(C);
return ret;
}
@@ -417,6 +420,8 @@ static int ed_redo_exec(bContext *C, wmOperator *op)
/* Keep button under the cursor active. */
WM_event_add_mousemove(C);
}
+
+ ED_outliner_select_sync_from_all_tag(C);
return ret;
}
diff --git a/source/blender/editors/uvedit/uvedit_draw.c b/source/blender/editors/uvedit/uvedit_draw.c
index d97714061c0..fe761f65702 100644
--- a/source/blender/editors/uvedit/uvedit_draw.c
+++ b/source/blender/editors/uvedit/uvedit_draw.c
@@ -205,14 +205,14 @@ static void draw_uvs_shadow(SpaceImage *UNUSED(sima),
Object *obedit,
Depsgraph *depsgraph)
{
- Object *eval_ob = DEG_get_evaluated_object(depsgraph, obedit);
- Mesh *me = eval_ob->data;
+ Object *ob_eval = DEG_get_evaluated_object(depsgraph, obedit);
+ Mesh *me = ob_eval->data;
float col[4];
UI_GetThemeColor4fv(TH_UV_SHADOW, col);
DRW_mesh_batch_cache_validate(me);
GPUBatch *edges = DRW_mesh_batch_cache_get_uv_edges(me);
- DRW_mesh_batch_cache_create_requested(eval_ob, me, scene, false, false);
+ DRW_mesh_batch_cache_create_requested(ob_eval, me, scene, false, false);
if (edges) {
GPU_batch_program_set_builtin(edges, GPU_SHADER_2D_UV_UNIFORM_COLOR);
@@ -223,8 +223,8 @@ static void draw_uvs_shadow(SpaceImage *UNUSED(sima),
static void draw_uvs_texpaint(Scene *scene, Object *ob, Depsgraph *depsgraph)
{
- Object *eval_ob = DEG_get_evaluated_object(depsgraph, ob);
- Mesh *me = eval_ob->data;
+ Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob);
+ Mesh *me = ob_eval->data;
ToolSettings *ts = scene->toolsettings;
float col[4];
UI_GetThemeColor4fv(TH_UV_SHADOW, col);
@@ -235,7 +235,7 @@ static void draw_uvs_texpaint(Scene *scene, Object *ob, Depsgraph *depsgraph)
DRW_mesh_batch_cache_validate(me);
GPUBatch *geom = DRW_mesh_batch_cache_get_uv_edges(me);
- DRW_mesh_batch_cache_create_requested(eval_ob, me, scene, false, false);
+ DRW_mesh_batch_cache_create_requested(ob_eval, me, scene, false, false);
GPU_batch_program_set_builtin(geom, GPU_SHADER_2D_UV_UNIFORM_COLOR);
GPU_batch_uniform_4fv(geom, "color", col);
@@ -246,7 +246,7 @@ static void draw_uvs_texpaint(Scene *scene, Object *ob, Depsgraph *depsgraph)
MPoly *mpoly = me->mpoly;
uint draw_start = 0;
uint idx = 0;
- bool prev_ma_match = (mpoly->mat_nr == (eval_ob->actcol - 1));
+ bool prev_ma_match = (mpoly->mat_nr == (ob_eval->actcol - 1));
GPU_matrix_bind(geom->interface);
GPU_batch_bind(geom);
@@ -255,7 +255,7 @@ static void draw_uvs_texpaint(Scene *scene, Object *ob, Depsgraph *depsgraph)
* we can use multi draw indirect drawcalls for this.
* (not implemented in GPU module at the time of writing). */
for (int a = 0; a < me->totpoly; a++, mpoly++) {
- bool ma_match = (mpoly->mat_nr == (eval_ob->actcol - 1));
+ bool ma_match = (mpoly->mat_nr == (ob_eval->actcol - 1));
if (ma_match != prev_ma_match) {
if (ma_match == false) {
GPU_batch_draw_advanced(geom, draw_start, idx - draw_start, 0, 0);
@@ -282,13 +282,13 @@ static void draw_uvs_texpaint(Scene *scene, Object *ob, Depsgraph *depsgraph)
static void draw_uvs(SpaceImage *sima, Scene *scene, Object *obedit, Depsgraph *depsgraph)
{
GPUBatch *faces, *edges, *verts, *facedots;
- Object *eval_ob = DEG_get_evaluated_object(depsgraph, obedit);
+ Object *ob_eval = DEG_get_evaluated_object(depsgraph, obedit);
const ToolSettings *ts = scene->toolsettings;
float col1[4], col2[4], col3[4], transparent[4] = {0.0f, 0.0f, 0.0f, 0.0f};
if (sima->flag & SI_DRAWSHADOW) {
bool is_cage_like_final_meshes = false;
- Mesh *me = (Mesh *)eval_ob->data;
+ Mesh *me = (Mesh *)ob_eval->data;
BMEditMesh *embm = me->edit_mesh;
is_cage_like_final_meshes = embm && embm->mesh_eval_final &&
embm->mesh_eval_final->runtime.is_original;
@@ -300,7 +300,7 @@ static void draw_uvs(SpaceImage *sima, Scene *scene, Object *obedit, Depsgraph *
}
}
- uvedit_get_batches(eval_ob, sima, scene, &faces, &edges, &verts, &facedots);
+ uvedit_get_batches(ob_eval, sima, scene, &faces, &edges, &verts, &facedots);
bool interpedges;
bool draw_stretch = (sima->flag & SI_DRAW_STRETCH) != 0;
@@ -367,33 +367,33 @@ static void draw_uvs(SpaceImage *sima, Scene *scene, Object *obedit, Depsgraph *
break;
}
case SI_UVDT_BLACK:
- case SI_UVDT_WHITE: {
- GPU_line_width(1.0f);
- GPU_batch_program_set_builtin(edges, GPU_SHADER_2D_UNIFORM_COLOR);
- if (sima->dt_uv == SI_UVDT_WHITE) {
- GPU_batch_uniform_4f(edges, "color", 1.0f, 1.0f, 1.0f, 1.0f);
- }
- else {
- GPU_batch_uniform_4f(edges, "color", 0.0f, 0.0f, 0.0f, 1.0f);
- }
- GPU_batch_draw(edges);
- break;
- }
+ case SI_UVDT_WHITE:
case SI_UVDT_OUTLINE: {
/* We could modify the vbo's data filling
* instead of modifying the provoking vert. */
glProvokingVertex(GL_FIRST_VERTEX_CONVENTION);
- UI_GetThemeColor4fv(TH_WIRE_EDIT, col1);
UI_GetThemeColor4fv(TH_EDGE_SELECT, col2);
GPU_batch_program_set_builtin(
edges, (interpedges) ? GPU_SHADER_2D_UV_EDGES_SMOOTH : GPU_SHADER_2D_UV_EDGES);
- /* Black Outline. */
- GPU_line_width(3.0f);
- GPU_batch_uniform_4f(edges, "edgeColor", 0.0f, 0.0f, 0.0f, 1.0f);
- GPU_batch_uniform_4f(edges, "selectColor", 0.0f, 0.0f, 0.0f, 1.0f);
- GPU_batch_draw(edges);
+
+ if (sima->dt_uv == SI_UVDT_OUTLINE) {
+ /* Black Outline. */
+ GPU_line_width(3.0f);
+ GPU_batch_uniform_4f(edges, "edgeColor", 0.0f, 0.0f, 0.0f, 1.0f);
+ GPU_batch_uniform_4f(edges, "selectColor", 0.0f, 0.0f, 0.0f, 1.0f);
+ GPU_batch_draw(edges);
+
+ UI_GetThemeColor4fv(TH_WIRE_EDIT, col1);
+ }
+ else if (sima->dt_uv == SI_UVDT_WHITE) {
+ copy_v4_fl4(col1, 1.0f, 1.0f, 1.0f, 1.0f);
+ }
+ else {
+ copy_v4_fl4(col1, 0.0f, 0.0f, 0.0f, 1.0f);
+ }
+
/* Inner Line. Use depth test to insure selection is drawn on top. */
GPU_depth_test(true);
GPU_line_width(1.0f);
diff --git a/source/blender/editors/uvedit/uvedit_intern.h b/source/blender/editors/uvedit/uvedit_intern.h
index 3f544ad90d1..7bc6b048585 100644
--- a/source/blender/editors/uvedit/uvedit_intern.h
+++ b/source/blender/editors/uvedit/uvedit_intern.h
@@ -24,7 +24,6 @@
#ifndef __UVEDIT_INTERN_H__
#define __UVEDIT_INTERN_H__
-struct BMEditMesh;
struct BMFace;
struct BMLoop;
struct Image;
diff --git a/source/blender/editors/uvedit/uvedit_ops.c b/source/blender/editors/uvedit/uvedit_ops.c
index dcf1d04ffb3..cd7e5f9ba09 100644
--- a/source/blender/editors/uvedit/uvedit_ops.c
+++ b/source/blender/editors/uvedit/uvedit_ops.c
@@ -138,7 +138,7 @@ static int UNUSED_FUNCTION(ED_operator_uvmap_mesh)(bContext *C)
if (ob && ob->type == OB_MESH) {
Mesh *me = ob->data;
- if (CustomData_get_layer(&me->fdata, CD_MTFACE) != NULL) {
+ if (CustomData_get_layer(&me->ldata, CD_MLOOPUV) != NULL) {
return 1;
}
}
diff --git a/source/blender/editors/uvedit/uvedit_unwrap_ops.c b/source/blender/editors/uvedit/uvedit_unwrap_ops.c
index a42a6eba3ff..2d8f40ea5af 100644
--- a/source/blender/editors/uvedit/uvedit_unwrap_ops.c
+++ b/source/blender/editors/uvedit/uvedit_unwrap_ops.c
@@ -524,7 +524,7 @@ static ParamHandle *construct_param_handle_subsurfed(Scene *scene,
smd.levels = smd_real->levels;
smd.subdivType = smd_real->subdivType;
- initialDerived = CDDM_from_editbmesh(em, false, false);
+ initialDerived = CDDM_from_editbmesh(em, false);
derivedMesh = subsurf_make_derived_from_derived(
initialDerived, &smd, scene, NULL, SUBSURF_IN_EDIT_MODE);