Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/editors')
-rw-r--r--source/blender/editors/animation/anim_channels_edit.c2
-rw-r--r--source/blender/editors/animation/keyframes_edit.c4
-rw-r--r--source/blender/editors/animation/keyframes_general.c62
-rw-r--r--source/blender/editors/animation/keyingsets.c67
-rw-r--r--source/blender/editors/armature/armature_relations.c9
-rw-r--r--source/blender/editors/armature/armature_select.c239
-rw-r--r--source/blender/editors/armature/pose_select.c154
-rw-r--r--source/blender/editors/armature/pose_slide.c4
-rw-r--r--source/blender/editors/armature/pose_transform.c2
-rw-r--r--source/blender/editors/asset/intern/asset_indexer.cc7
-rw-r--r--source/blender/editors/curve/curve_intern.h1
-rw-r--r--source/blender/editors/curve/editcurve.c175
-rw-r--r--source/blender/editors/curve/editfont.c33
-rw-r--r--source/blender/editors/curves/intern/curves_add.cc4
-rw-r--r--source/blender/editors/datafiles/CMakeLists.txt13
-rw-r--r--source/blender/editors/geometry/geometry_attributes.cc4
-rw-r--r--source/blender/editors/gpencil/gpencil_convert.c56
-rw-r--r--source/blender/editors/gpencil/gpencil_fill.c9
-rw-r--r--source/blender/editors/include/ED_armature.h28
-rw-r--r--source/blender/editors/include/ED_curve.h18
-rw-r--r--source/blender/editors/include/ED_keyframes_edit.h2
-rw-r--r--source/blender/editors/include/ED_lattice.h9
-rw-r--r--source/blender/editors/include/ED_mask.h1
-rw-r--r--source/blender/editors/include/ED_mball.h16
-rw-r--r--source/blender/editors/include/ED_mesh.h15
-rw-r--r--source/blender/editors/include/ED_particle.h6
-rw-r--r--source/blender/editors/include/ED_select_utils.h25
-rw-r--r--source/blender/editors/include/UI_interface.h10
-rw-r--r--source/blender/editors/interface/interface.c12
-rw-r--r--source/blender/editors/interface/interface_draw.c4
-rw-r--r--source/blender/editors/interface/interface_handlers.c11
-rw-r--r--source/blender/editors/interface/interface_intern.h3
-rw-r--r--source/blender/editors/interface/interface_layout.c4
-rw-r--r--source/blender/editors/interface/interface_ops.c3
-rw-r--r--source/blender/editors/interface/interface_templates.c7
-rw-r--r--source/blender/editors/interface/interface_utils.c24
-rw-r--r--source/blender/editors/interface/interface_widgets.c3
-rw-r--r--source/blender/editors/io/io_collada.c8
-rw-r--r--source/blender/editors/io/io_obj.c9
-rw-r--r--source/blender/editors/lattice/editlattice_select.c93
-rw-r--r--source/blender/editors/mask/mask_draw.c15
-rw-r--r--source/blender/editors/mesh/editface.c94
-rw-r--r--source/blender/editors/mesh/editmesh_mask_extract.c1
-rw-r--r--source/blender/editors/mesh/editmesh_path.c6
-rw-r--r--source/blender/editors/mesh/editmesh_rip_edge.c3
-rw-r--r--source/blender/editors/mesh/editmesh_select.c181
-rw-r--r--source/blender/editors/mesh/editmesh_tools.c9
-rw-r--r--source/blender/editors/mesh/editmesh_undo.c1
-rw-r--r--source/blender/editors/metaball/mball_edit.c242
-rw-r--r--source/blender/editors/object/CMakeLists.txt2
-rw-r--r--source/blender/editors/object/object_bake_api.c1
-rw-r--r--source/blender/editors/object/object_gpencil_modifier.c2
-rw-r--r--source/blender/editors/object/object_intern.h2
-rw-r--r--source/blender/editors/object/object_modifier.c2
-rw-r--r--source/blender/editors/object/object_transform.cc (renamed from source/blender/editors/object/object_transform.c)341
-rw-r--r--source/blender/editors/physics/dynamicpaint_ops.c14
-rw-r--r--source/blender/editors/physics/particle_edit.c165
-rw-r--r--source/blender/editors/render/render_internal.cc15
-rw-r--r--source/blender/editors/render/render_opengl.cc12
-rw-r--r--source/blender/editors/render/render_preview.cc24
-rw-r--r--source/blender/editors/scene/CMakeLists.txt2
-rw-r--r--source/blender/editors/screen/screendump.c3
-rw-r--r--source/blender/editors/sculpt_paint/CMakeLists.txt10
-rw-r--r--source/blender/editors/sculpt_paint/curves_sculpt_3d_brush.cc232
-rw-r--r--source/blender/editors/sculpt_paint/curves_sculpt_add.cc792
-rw-r--r--source/blender/editors/sculpt_paint/curves_sculpt_comb.cc377
-rw-r--r--source/blender/editors/sculpt_paint/curves_sculpt_delete.cc105
-rw-r--r--source/blender/editors/sculpt_paint/curves_sculpt_intern.hh53
-rw-r--r--source/blender/editors/sculpt_paint/curves_sculpt_ops.cc345
-rw-r--r--source/blender/editors/sculpt_paint/curves_sculpt_snake_hook.cc163
-rw-r--r--source/blender/editors/sculpt_paint/paint_mask.c7
-rw-r--r--source/blender/editors/sculpt_paint/paint_stroke.c14
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_boundary.c9
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_dyntopo.c1
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_face_set.c4
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_smooth.c2
-rw-r--r--source/blender/editors/space_action/action_edit.c4
-rw-r--r--source/blender/editors/space_action/space_action.c4
-rw-r--r--source/blender/editors/space_clip/tracking_ops.c30
-rw-r--r--source/blender/editors/space_clip/tracking_ops_track.c2
-rw-r--r--source/blender/editors/space_file/CMakeLists.txt3
-rw-r--r--source/blender/editors/space_file/filelist.c171
-rw-r--r--source/blender/editors/space_file/fsmenu.c12
-rw-r--r--source/blender/editors/space_graph/graph_intern.h1
-rw-r--r--source/blender/editors/space_graph/graph_ops.c1
-rw-r--r--source/blender/editors/space_graph/graph_slider_ops.c134
-rw-r--r--source/blender/editors/space_image/CMakeLists.txt3
-rw-r--r--source/blender/editors/space_image/image_buttons.c31
-rw-r--r--source/blender/editors/space_image/image_ops.c50
-rw-r--r--source/blender/editors/space_nla/space_nla.c6
-rw-r--r--source/blender/editors/space_node/link_drag_search.cc3
-rw-r--r--source/blender/editors/space_node/node_add.cc107
-rw-r--r--source/blender/editors/space_node/node_draw.cc7
-rw-r--r--source/blender/editors/space_node/node_edit.cc35
-rw-r--r--source/blender/editors/space_node/node_intern.hh2
-rw-r--r--source/blender/editors/space_node/node_ops.cc2
-rw-r--r--source/blender/editors/space_node/node_templates.cc7
-rw-r--r--source/blender/editors/space_node/node_view.cc86
-rw-r--r--source/blender/editors/space_node/space_node.cc11
-rw-r--r--source/blender/editors/space_outliner/outliner_collections.cc2
-rw-r--r--source/blender/editors/space_outliner/outliner_draw.cc127
-rw-r--r--source/blender/editors/space_outliner/outliner_edit.cc11
-rw-r--r--source/blender/editors/space_outliner/outliner_intern.hh2
-rw-r--r--source/blender/editors/space_outliner/outliner_select.cc2
-rw-r--r--source/blender/editors/space_outliner/outliner_tools.cc35
-rw-r--r--source/blender/editors/space_outliner/outliner_tree.cc1
-rw-r--r--source/blender/editors/space_outliner/outliner_utils.cc6
-rw-r--r--source/blender/editors/space_outliner/space_outliner.cc2
-rw-r--r--source/blender/editors/space_outliner/tree/tree_display.hh3
-rw-r--r--source/blender/editors/space_outliner/tree/tree_display_override_library.cc145
-rw-r--r--source/blender/editors/space_outliner/tree/tree_display_view_layer.cc9
-rw-r--r--source/blender/editors/space_outliner/tree/tree_element.cc1
-rw-r--r--source/blender/editors/space_outliner/tree/tree_element.hh3
-rw-r--r--source/blender/editors/space_outliner/tree/tree_element_id.cc11
-rw-r--r--source/blender/editors/space_outliner/tree/tree_element_id.hh1
-rw-r--r--source/blender/editors/space_outliner/tree/tree_element_overrides.cc9
-rw-r--r--source/blender/editors/space_outliner/tree/tree_element_overrides.hh12
-rw-r--r--source/blender/editors/space_outliner/tree/tree_element_rna.cc2
-rw-r--r--source/blender/editors/space_sequencer/sequencer_draw.c4
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_column.cc5
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_column_values.hh11
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc14
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh4
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_layout.cc2
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_row_filter.cc2
-rw-r--r--source/blender/editors/space_view3d/view3d_select.c1226
-rw-r--r--source/blender/editors/transform/transform.c6
-rw-r--r--source/blender/editors/transform/transform_snap.c4
-rw-r--r--source/blender/editors/transform/transform_snap_object.c67
-rw-r--r--source/blender/editors/util/select_utils.c14
-rw-r--r--source/blender/editors/uvedit/uvedit_parametrizer.c4
-rw-r--r--source/blender/editors/uvedit/uvedit_select.c235
-rw-r--r--source/blender/editors/uvedit/uvedit_unwrap_ops.c1
133 files changed, 4567 insertions, 2535 deletions
diff --git a/source/blender/editors/animation/anim_channels_edit.c b/source/blender/editors/animation/anim_channels_edit.c
index df418b204f9..08379be36fa 100644
--- a/source/blender/editors/animation/anim_channels_edit.c
+++ b/source/blender/editors/animation/anim_channels_edit.c
@@ -1131,7 +1131,7 @@ static void rearrange_nla_channels(bAnimContext *ac, AnimData *adt, eRearrangeAn
{
AnimChanRearrangeFp rearrange_func;
ListBase anim_data_visible = {NULL, NULL};
- const bool is_liboverride = ID_IS_OVERRIDE_LIBRARY(ac->obact);
+ const bool is_liboverride = (ac->obact != NULL) ? ID_IS_OVERRIDE_LIBRARY(ac->obact) : false;
/* hack: invert mode so that functions will work in right order */
mode *= -1;
diff --git a/source/blender/editors/animation/keyframes_edit.c b/source/blender/editors/animation/keyframes_edit.c
index f18873cc22b..ed40845a47c 100644
--- a/source/blender/editors/animation/keyframes_edit.c
+++ b/source/blender/editors/animation/keyframes_edit.c
@@ -1292,8 +1292,8 @@ void ANIM_fcurve_equalize_keyframes_loop(FCurve *fcu,
{
uint i;
BezTriple *bezt;
- const float flat_direction_left[2] = {-handle_length, 0.f};
- const float flat_direction_right[2] = {handle_length, 0.f};
+ const float flat_direction_left[2] = {-handle_length, 0.0f};
+ const float flat_direction_right[2] = {handle_length, 0.0f};
/* Loop through an F-Curves keyframes. */
for (bezt = fcu->bezt, i = 0; i < fcu->totvert; bezt++, i++) {
diff --git a/source/blender/editors/animation/keyframes_general.c b/source/blender/editors/animation/keyframes_general.c
index 24ba62fc0f7..aec2b0f769a 100644
--- a/source/blender/editors/animation/keyframes_general.c
+++ b/source/blender/editors/animation/keyframes_general.c
@@ -382,6 +382,68 @@ void blend_to_neighbor_fcurve_segment(FCurve *fcu, FCurveSegment *segment, const
/* ---------------- */
+float get_default_rna_value(FCurve *fcu, PropertyRNA *prop, PointerRNA *ptr)
+{
+ const int len = RNA_property_array_length(ptr, prop);
+
+ float default_value = 0;
+ /* Find the default value of that property. */
+ switch (RNA_property_type(prop)) {
+ case PROP_BOOLEAN:
+ if (len) {
+ default_value = RNA_property_boolean_get_default_index(ptr, prop, fcu->array_index);
+ }
+ else {
+ default_value = RNA_property_boolean_get_default(ptr, prop);
+ }
+ break;
+ case PROP_INT:
+ if (len) {
+ default_value = RNA_property_int_get_default_index(ptr, prop, fcu->array_index);
+ }
+ else {
+ default_value = RNA_property_int_get_default(ptr, prop);
+ }
+ break;
+ case PROP_FLOAT:
+ if (len) {
+ default_value = RNA_property_float_get_default_index(ptr, prop, fcu->array_index);
+ }
+ else {
+ default_value = RNA_property_float_get_default(ptr, prop);
+ }
+ break;
+
+ default:
+ break;
+ }
+ return default_value;
+}
+
+/* This function blends the selected keyframes to the default value of the property the fcurve
+ * drives. */
+void blend_to_default_fcurve(PointerRNA *id_ptr, FCurve *fcu, const float factor)
+{
+ PointerRNA ptr;
+ PropertyRNA *prop;
+
+ /* Check if path is valid. */
+ if (!RNA_path_resolve_property(id_ptr, fcu->rna_path, &ptr, &prop)) {
+ return;
+ }
+
+ const float default_value = get_default_rna_value(fcu, prop, &ptr);
+
+ /* Blend selected keys to default */
+ for (int i = 0; i < fcu->totvert; i++) {
+ if (fcu->bezt[i].f2 & SELECT) {
+ fcu->bezt[i].vec[1][1] = interpf(default_value, fcu->bezt[i].vec[1][1], factor);
+ }
+ }
+}
+
+/* ---------------- */
+
void breakdown_fcurve_segment(FCurve *fcu, FCurveSegment *segment, const float factor)
{
BezTriple left_bezt = fcurve_segment_start_get(fcu, segment->start_index);
diff --git a/source/blender/editors/animation/keyingsets.c b/source/blender/editors/animation/keyingsets.c
index dcf8835c911..6fcdd21bad8 100644
--- a/source/blender/editors/animation/keyingsets.c
+++ b/source/blender/editors/animation/keyingsets.c
@@ -715,54 +715,55 @@ const EnumPropertyItem *ANIM_keying_sets_enum_itemf(bContext *C,
PropertyRNA *UNUSED(prop),
bool *r_free)
{
+ Scene *scene = CTX_data_scene(C);
KeyingSet *ks;
EnumPropertyItem *item = NULL, item_tmp = {0};
int totitem = 0;
int i = 0;
- if (C != NULL) {
- Scene *scene = CTX_data_scene(C);
- /* active Keying Set
- * - only include entry if it exists
- */
- if (scene->active_keyingset) {
- /* active Keying Set */
- item_tmp.identifier = "__ACTIVE__";
- item_tmp.name = "Active Keying Set";
- item_tmp.value = i;
- RNA_enum_item_add(&item, &totitem, &item_tmp);
+ if (C == NULL) {
+ return DummyRNA_DEFAULT_items;
+ }
- /* separator */
- RNA_enum_item_add_separator(&item, &totitem);
- }
+ /* active Keying Set
+ * - only include entry if it exists
+ */
+ if (scene->active_keyingset) {
+ /* active Keying Set */
+ item_tmp.identifier = "__ACTIVE__";
+ item_tmp.name = "Active Keying Set";
+ item_tmp.value = i;
+ RNA_enum_item_add(&item, &totitem, &item_tmp);
+
+ /* separator */
+ RNA_enum_item_add_separator(&item, &totitem);
+ }
- i++;
+ i++;
- /* user-defined Keying Sets
- * - these are listed in the order in which they were defined for the active scene
- */
- if (scene->keyingsets.first) {
- for (ks = scene->keyingsets.first; ks; ks = ks->next, i++) {
- if (ANIM_keyingset_context_ok_poll(C, ks)) {
- item_tmp.identifier = ks->idname;
- item_tmp.name = ks->name;
- item_tmp.description = ks->description;
- item_tmp.value = i;
- RNA_enum_item_add(&item, &totitem, &item_tmp);
- }
+ /* user-defined Keying Sets
+ * - these are listed in the order in which they were defined for the active scene
+ */
+ if (scene->keyingsets.first) {
+ for (ks = scene->keyingsets.first; ks; ks = ks->next, i++) {
+ if (ANIM_keyingset_context_ok_poll(C, ks)) {
+ item_tmp.identifier = ks->idname;
+ item_tmp.name = ks->name;
+ item_tmp.description = ks->description;
+ item_tmp.value = i;
+ RNA_enum_item_add(&item, &totitem, &item_tmp);
}
-
- /* separator */
- RNA_enum_item_add_separator(&item, &totitem);
}
+
+ /* separator */
+ RNA_enum_item_add_separator(&item, &totitem);
}
/* builtin Keying Sets */
i = -1;
for (ks = builtin_keyingsets.first; ks; ks = ks->next, i--) {
- /* Only show #KeyingSet if context is suitable or if there is no context which is needed
- * to support key-bindings to be assigned since key bindings are not context aware. */
- if ((C == NULL) || ANIM_keyingset_context_ok_poll(C, ks)) {
+ /* only show KeyingSet if context is suitable */
+ if (ANIM_keyingset_context_ok_poll(C, ks)) {
item_tmp.identifier = ks->idname;
item_tmp.name = ks->name;
item_tmp.description = ks->description;
diff --git a/source/blender/editors/armature/armature_relations.c b/source/blender/editors/armature/armature_relations.c
index 45d64807e65..be4829c02be 100644
--- a/source/blender/editors/armature/armature_relations.c
+++ b/source/blender/editors/armature/armature_relations.c
@@ -377,6 +377,15 @@ int ED_armature_join_objects_exec(bContext *C, wmOperator *op)
BKE_pose_channels_hash_free(pose);
}
+ /* Armature ID itself is not freed below, however it has been modified (and is now completely
+ * empty). This needs to be told to the depsgraph, it will also ensure that the global
+ * memfile undo system properly detects the change.
+ *
+ * FIXME: Modifying an existing obdata because we are joining an object using it into another
+ * object is a very questionable behavior, which also does not match with other object types
+ * joining. */
+ DEG_id_tag_update_ex(bmain, &curarm->id, ID_RECALC_GEOMETRY);
+
/* Fix all the drivers (and animation data) */
BKE_fcurves_main_cb(bmain, joined_armature_fix_animdata_cb, &afd);
BLI_ghash_free(afd.names_map, MEM_freeN, NULL);
diff --git a/source/blender/editors/armature/armature_select.c b/source/blender/editors/armature/armature_select.c
index d7240782840..08d5d6558e0 100644
--- a/source/blender/editors/armature/armature_select.c
+++ b/source/blender/editors/armature/armature_select.c
@@ -951,127 +951,180 @@ bool ED_armature_edit_select_pick_bone(bContext *C,
Base *basact,
EditBone *ebone,
const int selmask,
- const bool extend,
- const bool deselect,
- const bool toggle)
+ const struct SelectPick_Params *params)
{
- if (!ebone) {
- return false;
- }
-
ViewLayer *view_layer = CTX_data_view_layer(C);
View3D *v3d = CTX_wm_view3d(C);
+ bool changed = false;
+ bool found = false;
- BLI_assert(BKE_object_is_in_editmode(basact->object));
- bArmature *arm = basact->object->data;
-
- if (!EBONE_SELECTABLE(arm, ebone)) {
- return false;
+ if (ebone) {
+ bArmature *arm = basact->object->data;
+ if (EBONE_SELECTABLE(arm, ebone)) {
+ found = true;
+ }
}
- if (!extend && !deselect && !toggle) {
- uint bases_len = 0;
- Base **bases = BKE_view_layer_array_from_bases_in_edit_mode_unique_data(
- view_layer, v3d, &bases_len);
- ED_armature_edit_deselect_all_multi_ex(bases, bases_len);
- MEM_freeN(bases);
+ if (params->sel_op == SEL_OP_SET) {
+ if ((found && params->select_passthrough) &&
+ (ED_armature_ebone_selectflag_get(ebone) & selmask)) {
+ found = false;
+ }
+ else if (found || params->deselect_all) {
+ /* Deselect everything. */
+ uint bases_len = 0;
+ Base **bases = BKE_view_layer_array_from_bases_in_edit_mode_unique_data(
+ view_layer, v3d, &bases_len);
+ ED_armature_edit_deselect_all_multi_ex(bases, bases_len);
+ MEM_freeN(bases);
+ changed = true;
+ }
}
- /* By definition the non-root connected bones have no root point drawn,
- * so a root selection needs to be delivered to the parent tip. */
+ if (found) {
+ BLI_assert(BKE_object_is_in_editmode(basact->object));
+ bArmature *arm = basact->object->data;
- if (selmask & BONE_SELECTED) {
- if (ebone->parent && (ebone->flag & BONE_CONNECTED)) {
- /* Bone is in a chain. */
- if (extend) {
- /* Select this bone. */
- ebone->flag |= BONE_TIPSEL;
- ebone->parent->flag |= BONE_TIPSEL;
- }
- else if (deselect) {
- /* Deselect this bone. */
- ebone->flag &= ~(BONE_TIPSEL | BONE_SELECTED);
- /* Only deselect parent tip if it is not selected. */
- if (!(ebone->parent->flag & BONE_SELECTED)) {
- ebone->parent->flag &= ~BONE_TIPSEL;
- }
- }
- else if (toggle) {
- /* Toggle inverts this bone's selection. */
- if (ebone->flag & BONE_SELECTED) {
- /* Deselect this bone. */
- ebone->flag &= ~(BONE_TIPSEL | BONE_SELECTED);
- /* Only deselect parent tip if it is not selected. */
- if (!(ebone->parent->flag & BONE_SELECTED)) {
- ebone->parent->flag &= ~BONE_TIPSEL;
+ /* By definition the non-root connected bones have no root point drawn,
+ * so a root selection needs to be delivered to the parent tip. */
+
+ if (selmask & BONE_SELECTED) {
+ if (ebone->parent && (ebone->flag & BONE_CONNECTED)) {
+
+ /* Bone is in a chain. */
+ switch (params->sel_op) {
+ case SEL_OP_ADD: {
+ /* Select this bone. */
+ ebone->flag |= BONE_TIPSEL;
+ ebone->parent->flag |= BONE_TIPSEL;
+ break;
+ }
+ case SEL_OP_SUB: {
+ /* Deselect this bone. */
+ ebone->flag &= ~(BONE_TIPSEL | BONE_SELECTED);
+ /* Only deselect parent tip if it is not selected. */
+ if (!(ebone->parent->flag & BONE_SELECTED)) {
+ ebone->parent->flag &= ~BONE_TIPSEL;
+ }
+ break;
+ }
+ case SEL_OP_XOR: {
+ /* Toggle inverts this bone's selection. */
+ if (ebone->flag & BONE_SELECTED) {
+ /* Deselect this bone. */
+ ebone->flag &= ~(BONE_TIPSEL | BONE_SELECTED);
+ /* Only deselect parent tip if it is not selected. */
+ if (!(ebone->parent->flag & BONE_SELECTED)) {
+ ebone->parent->flag &= ~BONE_TIPSEL;
+ }
+ }
+ else {
+ /* Select this bone. */
+ ebone->flag |= BONE_TIPSEL;
+ ebone->parent->flag |= BONE_TIPSEL;
+ }
+ break;
+ }
+ case SEL_OP_SET: {
+ /* Select this bone. */
+ ebone->flag |= BONE_TIPSEL;
+ ebone->parent->flag |= BONE_TIPSEL;
+ break;
+ }
+ case SEL_OP_AND: {
+ BLI_assert_unreachable(); /* Doesn't make sense for picking. */
+ break;
}
- }
- else {
- /* Select this bone. */
- ebone->flag |= BONE_TIPSEL;
- ebone->parent->flag |= BONE_TIPSEL;
}
}
else {
- /* Select this bone. */
- ebone->flag |= BONE_TIPSEL;
- ebone->parent->flag |= BONE_TIPSEL;
+ switch (params->sel_op) {
+ case SEL_OP_ADD: {
+ ebone->flag |= (BONE_TIPSEL | BONE_ROOTSEL);
+ break;
+ }
+ case SEL_OP_SUB: {
+ ebone->flag &= ~(BONE_TIPSEL | BONE_ROOTSEL);
+ break;
+ }
+ case SEL_OP_XOR: {
+ /* Toggle inverts this bone's selection. */
+ if (ebone->flag & BONE_SELECTED) {
+ ebone->flag &= ~(BONE_TIPSEL | BONE_ROOTSEL);
+ }
+ else {
+ ebone->flag |= (BONE_TIPSEL | BONE_ROOTSEL);
+ }
+ break;
+ }
+ case SEL_OP_SET: {
+ ebone->flag |= (BONE_TIPSEL | BONE_ROOTSEL);
+ break;
+ }
+ case SEL_OP_AND: {
+ BLI_assert_unreachable(); /* Doesn't make sense for picking. */
+ break;
+ }
+ }
}
}
else {
- if (extend) {
- ebone->flag |= (BONE_TIPSEL | BONE_ROOTSEL);
- }
- else if (deselect) {
- ebone->flag &= ~(BONE_TIPSEL | BONE_ROOTSEL);
- }
- else if (toggle) {
- /* Toggle inverts this bone's selection. */
- if (ebone->flag & BONE_SELECTED) {
- ebone->flag &= ~(BONE_TIPSEL | BONE_ROOTSEL);
+ switch (params->sel_op) {
+ case SEL_OP_ADD: {
+ ebone->flag |= selmask;
+ break;
}
- else {
- ebone->flag |= (BONE_TIPSEL | BONE_ROOTSEL);
+ case SEL_OP_SUB: {
+ ebone->flag &= ~selmask;
+ break;
+ }
+ case SEL_OP_XOR: {
+ if (ebone->flag & selmask) {
+ ebone->flag &= ~selmask;
+ }
+ else {
+ ebone->flag |= selmask;
+ }
+ break;
+ }
+ case SEL_OP_SET: {
+ ebone->flag |= selmask;
+ break;
+ }
+ case SEL_OP_AND: {
+ BLI_assert_unreachable(); /* Doesn't make sense for picking. */
+ break;
}
- }
- else {
- ebone->flag |= (BONE_TIPSEL | BONE_ROOTSEL);
}
}
- }
- else {
- if (extend) {
- ebone->flag |= selmask;
- }
- else if (deselect) {
- ebone->flag &= ~selmask;
- }
- else if (toggle && (ebone->flag & selmask)) {
- ebone->flag &= ~selmask;
- }
- else {
- ebone->flag |= selmask;
+
+ ED_armature_edit_sync_selection(arm->edbo);
+
+ /* Then now check for active status. */
+ if (ED_armature_ebone_selectflag_get(ebone)) {
+ arm->act_edbone = ebone;
}
- }
- ED_armature_edit_sync_selection(arm->edbo);
+ if (view_layer->basact != basact) {
+ ED_object_base_activate(C, basact);
+ }
- /* Then now check for active status. */
- if (ED_armature_ebone_selectflag_get(ebone)) {
- arm->act_edbone = ebone;
+ WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, basact->object);
+ DEG_id_tag_update(&arm->id, ID_RECALC_COPY_ON_WRITE);
+ changed = true;
}
- if (view_layer->basact != basact) {
- ED_object_base_activate(C, basact);
+ if (changed) {
+ ED_outliner_select_sync_from_edit_bone_tag(C);
}
- WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, basact->object);
- DEG_id_tag_update(&arm->id, ID_RECALC_COPY_ON_WRITE);
- return true;
+ return changed || found;
}
-bool ED_armature_edit_select_pick(
- bContext *C, const int mval[2], bool extend, bool deselect, bool toggle)
+bool ED_armature_edit_select_pick(bContext *C,
+ const int mval[2],
+ const struct SelectPick_Params *params)
+
{
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
ViewContext vc;
@@ -1084,7 +1137,7 @@ bool ED_armature_edit_select_pick(
vc.mval[1] = mval[1];
nearBone = get_nearest_editbonepoint(&vc, true, true, &basact, &selmask);
- return ED_armature_edit_select_pick_bone(C, basact, nearBone, selmask, extend, deselect, toggle);
+ return ED_armature_edit_select_pick_bone(C, basact, nearBone, selmask, params);
}
/** \} */
diff --git a/source/blender/editors/armature/pose_select.c b/source/blender/editors/armature/pose_select.c
index 12238280b06..8790a10f3e5 100644
--- a/source/blender/editors/armature/pose_select.c
+++ b/source/blender/editors/armature/pose_select.c
@@ -121,43 +121,27 @@ void ED_pose_bone_select(Object *ob, bPoseChannel *pchan, bool select)
}
}
-void ED_armature_pose_select_pick_bone(ViewLayer *view_layer,
+bool ED_armature_pose_select_pick_bone(ViewLayer *view_layer,
View3D *v3d,
Object *ob,
Bone *bone,
- const bool extend,
- const bool deselect,
- const bool toggle)
+ const struct SelectPick_Params *params)
{
- if (!ob || !ob->pose) {
- return;
- }
-
- Object *ob_act = OBACT(view_layer);
- BLI_assert(OBEDIT_FROM_VIEW_LAYER(view_layer) == NULL);
-
- /* If the bone cannot be affected, don't do anything. */
- if (bone == NULL || (bone->flag & BONE_UNSELECTABLE)) {
- return;
- }
- bArmature *arm = ob->data;
+ bool found = false;
+ bool changed = false;
- /* Since we do unified select, we don't shift+select a bone if the
- * armature object was not active yet.
- * NOTE(campbell): special exception for armature mode so we can do multi-select
- * we could check for multi-select explicitly but think its fine to
- * always give predictable behavior in weight paint mode. */
- if ((ob_act == NULL) || ((ob_act != ob) && (ob_act->mode & OB_MODE_ALL_WEIGHT_PAINT) == 0)) {
- /* When we are entering into posemode via toggle-select,
- * from another active object - always select the bone. */
- if (!extend && !deselect && toggle) {
- /* Re-select the bone again later in this function. */
- bone->flag &= ~BONE_SELECTED;
+ if (ob || ob->pose) {
+ if (bone && ((bone->flag & BONE_UNSELECTABLE) == 0)) {
+ found = true;
}
}
- if (!extend && !deselect && !toggle) {
- {
+ if (params->sel_op == SEL_OP_SET) {
+ if ((found && params->select_passthrough) && (bone->flag & BONE_SELECTED)) {
+ found = false;
+ }
+ else if (found || params->deselect_all) {
+ /* Deselect everything. */
/* Don't use 'BKE_object_pose_base_array_get_unique'
* because we may be selecting from object mode. */
FOREACH_VISIBLE_BASE_BEGIN (view_layer, v3d, base_iter) {
@@ -169,56 +153,94 @@ void ED_armature_pose_select_pick_bone(ViewLayer *view_layer,
}
}
FOREACH_VISIBLE_BASE_END;
+ changed = true;
}
- bone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
- arm->act_bone = bone;
}
- else {
- if (extend) {
- bone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
- arm->act_bone = bone;
- }
- else if (deselect) {
- bone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
+
+ if (found) {
+ Object *ob_act = OBACT(view_layer);
+ BLI_assert(OBEDIT_FROM_VIEW_LAYER(view_layer) == NULL);
+
+ /* If the bone cannot be affected, don't do anything. */
+ bArmature *arm = ob->data;
+
+ /* Since we do unified select, we don't shift+select a bone if the
+ * armature object was not active yet.
+ * NOTE(campbell): special exception for armature mode so we can do multi-select
+ * we could check for multi-select explicitly but think its fine to
+ * always give predictable behavior in weight paint mode. */
+ if ((ob_act == NULL) || ((ob_act != ob) && (ob_act->mode & OB_MODE_ALL_WEIGHT_PAINT) == 0)) {
+ /* When we are entering into posemode via toggle-select,
+ * from another active object - always select the bone. */
+ if (params->sel_op == SEL_OP_SET) {
+ /* Re-select the bone again later in this function. */
+ bone->flag &= ~BONE_SELECTED;
+ }
}
- else if (toggle) {
- if (bone->flag & BONE_SELECTED) {
- /* If not active, we make it active. */
- if (bone != arm->act_bone) {
- arm->act_bone = bone;
+
+ switch (params->sel_op) {
+ case SEL_OP_ADD: {
+ bone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
+ arm->act_bone = bone;
+ break;
+ }
+ case SEL_OP_SUB: {
+ bone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
+ break;
+ }
+ case SEL_OP_XOR: {
+ if (bone->flag & BONE_SELECTED) {
+ /* If not active, we make it active. */
+ if (bone != arm->act_bone) {
+ arm->act_bone = bone;
+ }
+ else {
+ bone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
+ }
}
else {
- bone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
+ bone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
+ arm->act_bone = bone;
}
+ break;
}
- else {
+ case SEL_OP_SET: {
bone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
arm->act_bone = bone;
+ break;
+ }
+ case SEL_OP_AND: {
+ BLI_assert_unreachable(); /* Doesn't make sense for picking. */
+ break;
}
}
- }
- if (ob_act) {
- /* In weightpaint we select the associated vertex group too. */
- if (ob_act->mode & OB_MODE_ALL_WEIGHT_PAINT) {
- if (bone == arm->act_bone) {
- ED_vgroup_select_by_name(ob_act, bone->name);
- DEG_id_tag_update(&ob_act->id, ID_RECALC_GEOMETRY);
+ if (ob_act) {
+ /* In weightpaint we select the associated vertex group too. */
+ if (ob_act->mode & OB_MODE_ALL_WEIGHT_PAINT) {
+ if (bone == arm->act_bone) {
+ ED_vgroup_select_by_name(ob_act, bone->name);
+ DEG_id_tag_update(&ob_act->id, ID_RECALC_GEOMETRY);
+ }
}
- }
- /* If there are some dependencies for visualizing armature state
- * (e.g. Mask Modifier in 'Armature' mode), force update.
- */
- else if (arm->flag & ARM_HAS_VIZ_DEPS) {
- /* NOTE: ob not ob_act here is intentional - it's the source of the
- * bones being selected [T37247]
+ /* If there are some dependencies for visualizing armature state
+ * (e.g. Mask Modifier in 'Armature' mode), force update.
*/
- DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
+ else if (arm->flag & ARM_HAS_VIZ_DEPS) {
+ /* NOTE: ob not ob_act here is intentional - it's the source of the
+ * bones being selected [T37247]
+ */
+ DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
+ }
+
+ /* Tag armature for copy-on-write update (since act_bone is in armature not object). */
+ DEG_id_tag_update(&arm->id, ID_RECALC_COPY_ON_WRITE);
}
- /* Tag armature for copy-on-write update (since act_bone is in armature not object). */
- DEG_id_tag_update(&arm->id, ID_RECALC_COPY_ON_WRITE);
+ changed = true;
}
+
+ return changed || found;
}
bool ED_armature_pose_select_pick_with_buffer(ViewLayer *view_layer,
@@ -226,9 +248,7 @@ bool ED_armature_pose_select_pick_with_buffer(ViewLayer *view_layer,
Base *base,
const struct GPUSelectResult *buffer,
const short hits,
- bool extend,
- bool deselect,
- bool toggle,
+ const struct SelectPick_Params *params,
bool do_nearest)
{
Object *ob = base->object;
@@ -243,9 +263,7 @@ bool ED_armature_pose_select_pick_with_buffer(ViewLayer *view_layer,
nearBone = ED_armature_pick_bone_from_selectbuffer(
&base, 1, buffer, hits, 1, do_nearest, &base_dummy);
- ED_armature_pose_select_pick_bone(view_layer, v3d, ob, nearBone, extend, deselect, toggle);
-
- return nearBone != NULL;
+ return ED_armature_pose_select_pick_bone(view_layer, v3d, ob, nearBone, params);
}
void ED_armature_pose_select_in_wpaint_mode(ViewLayer *view_layer, Base *base_select)
diff --git a/source/blender/editors/armature/pose_slide.c b/source/blender/editors/armature/pose_slide.c
index fb349b78d71..cf04cdba859 100644
--- a/source/blender/editors/armature/pose_slide.c
+++ b/source/blender/editors/armature/pose_slide.c
@@ -896,7 +896,7 @@ static void pose_slide_draw_status(bContext *C, tPoseSlideOp *pso)
strcpy(mode_str, TIP_("Breakdown"));
break;
case POSESLIDE_BLEND:
- strcpy(mode_str, TIP_("Blend To Neighbor"));
+ strcpy(mode_str, TIP_("Blend to Neighbor"));
break;
default:
@@ -1722,7 +1722,7 @@ static int pose_slide_blend_to_neighbors_exec(bContext *C, wmOperator *op)
void POSE_OT_blend_to_neighbors(wmOperatorType *ot)
{
/* Identifiers. */
- ot->name = "Blend To Neighbor";
+ ot->name = "Blend to Neighbor";
ot->idname = "POSE_OT_blend_to_neighbor";
ot->description = "Blend from current position to previous or next keyframe";
diff --git a/source/blender/editors/armature/pose_transform.c b/source/blender/editors/armature/pose_transform.c
index 90d4ef60598..f0b0218d7e0 100644
--- a/source/blender/editors/armature/pose_transform.c
+++ b/source/blender/editors/armature/pose_transform.c
@@ -1100,7 +1100,7 @@ static void pchan_clear_rot(bPoseChannel *pchan)
copy_v3_v3(pchan->eul, eul);
}
}
- } /* Duplicated in source/blender/editors/object/object_transform.c */
+ } /* Duplicated in source/blender/editors/object/object_transform.cc */
else {
if (pchan->rotmode == ROT_MODE_QUAT) {
unit_qt(pchan->quat);
diff --git a/source/blender/editors/asset/intern/asset_indexer.cc b/source/blender/editors/asset/intern/asset_indexer.cc
index 49c4002eee8..3cc3638c299 100644
--- a/source/blender/editors/asset/intern/asset_indexer.cc
+++ b/source/blender/editors/asset/intern/asset_indexer.cc
@@ -39,7 +39,6 @@ using namespace blender::bke;
using namespace blender::bke::idprop;
/**
- * \file asset_indexer.cc
* \brief Indexer for asset libraries.
*
* Indexes are stored per input file. Each index can contain zero to multiple asset entries.
@@ -494,15 +493,15 @@ struct AssetLibraryIndex {
return;
}
struct direntry *dir_entries = nullptr;
- int num_entries = BLI_filelist_dir_contents(index_path, &dir_entries);
- for (int i = 0; i < num_entries; i++) {
+ const int dir_entries_num = BLI_filelist_dir_contents(index_path, &dir_entries);
+ for (int i = 0; i < dir_entries_num; i++) {
struct direntry *entry = &dir_entries[i];
if (BLI_str_endswith(entry->relname, ".index.json")) {
unused_file_indices.add_as(std::string(entry->path));
}
}
- BLI_filelist_free(dir_entries, num_entries);
+ BLI_filelist_free(dir_entries, dir_entries_num);
}
void mark_as_used(const std::string &filename)
diff --git a/source/blender/editors/curve/curve_intern.h b/source/blender/editors/curve/curve_intern.h
index c3ea36fcda5..8bfabe23ea1 100644
--- a/source/blender/editors/curve/curve_intern.h
+++ b/source/blender/editors/curve/curve_intern.h
@@ -181,6 +181,7 @@ void SURFACE_OT_primitive_nurbs_surface_sphere_add(struct wmOperatorType *ot);
void SURFACE_OT_primitive_nurbs_surface_torus_add(struct wmOperatorType *ot);
/* editcurve_query.c */
+
bool ED_curve_pick_vert(struct ViewContext *vc,
short sel,
struct Nurb **r_nurb,
diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c
index 2dcddd01670..5ff63e767f6 100644
--- a/source/blender/editors/curve/editcurve.c
+++ b/source/blender/editors/curve/editcurve.c
@@ -44,6 +44,7 @@
#include "ED_object.h"
#include "ED_outliner.h"
#include "ED_screen.h"
+#include "ED_select_utils.h"
#include "ED_transform.h"
#include "ED_transform_snap_object_context.h"
#include "ED_types.h"
@@ -4722,8 +4723,9 @@ void CURVE_OT_make_segment(wmOperatorType *ot)
/** \name Pick Select from 3D View
* \{ */
-bool ED_curve_editnurb_select_pick(
- bContext *C, const int mval[2], bool extend, bool deselect, bool toggle)
+bool ED_curve_editnurb_select_pick(bContext *C,
+ const int mval[2],
+ const struct SelectPick_Params *params)
{
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
ViewContext vc;
@@ -4732,18 +4734,21 @@ bool ED_curve_editnurb_select_pick(
BPoint *bp = NULL;
Base *basact = NULL;
short hand;
+ bool changed = false;
view3d_operator_needs_opengl(C);
ED_view3d_viewcontext_init(C, &vc, depsgraph);
copy_v2_v2_int(vc.mval, mval);
- if (ED_curve_pick_vert(&vc, 1, &nu, &bezt, &bp, &hand, &basact)) {
- Object *obedit = basact->object;
- Curve *cu = obedit->data;
- ListBase *editnurb = object_editcurve_get(obedit);
- const void *vert = BKE_curve_vert_active_get(cu);
+ bool found = ED_curve_pick_vert(&vc, 1, &nu, &bezt, &bp, &hand, &basact);
- if (!extend && !deselect && !toggle) {
+ if (params->sel_op == SEL_OP_SET) {
+ if ((found && params->select_passthrough) &&
+ (((bezt ? (&bezt->f1)[hand] : bp->f1) & SELECT) != 0)) {
+ found = false;
+ }
+ else if (found || params->deselect_all) {
+ /* Deselect everything. */
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
vc.view_layer, vc.v3d, &objects_len);
@@ -4756,105 +4761,123 @@ bool ED_curve_editnurb_select_pick(
WM_event_add_notifier(C, NC_GEOM | ND_SELECT, ob_iter->data);
}
MEM_freeN(objects);
+ changed = true;
}
+ }
- if (extend) {
- if (bezt) {
- if (hand == 1) {
- select_beztriple(bezt, SELECT, SELECT, HIDDEN);
- }
- else {
- if (hand == 0) {
- bezt->f1 |= SELECT;
+ if (found) {
+ Object *obedit = basact->object;
+ Curve *cu = obedit->data;
+ ListBase *editnurb = object_editcurve_get(obedit);
+ const void *vert = BKE_curve_vert_active_get(cu);
+
+ switch (params->sel_op) {
+ case SEL_OP_ADD: {
+ if (bezt) {
+ if (hand == 1) {
+ select_beztriple(bezt, SELECT, SELECT, HIDDEN);
}
else {
- bezt->f3 |= SELECT;
- }
- }
- BKE_curve_nurb_vert_active_set(cu, nu, bezt);
- }
- else {
- select_bpoint(bp, SELECT, SELECT, HIDDEN);
- BKE_curve_nurb_vert_active_set(cu, nu, bp);
- }
- }
- else if (deselect) {
- if (bezt) {
- if (hand == 1) {
- select_beztriple(bezt, DESELECT, SELECT, HIDDEN);
- if (bezt == vert) {
- cu->actvert = CU_ACT_NONE;
+ if (hand == 0) {
+ bezt->f1 |= SELECT;
+ }
+ else {
+ bezt->f3 |= SELECT;
+ }
}
- }
- else if (hand == 0) {
- bezt->f1 &= ~SELECT;
+ BKE_curve_nurb_vert_active_set(cu, nu, bezt);
}
else {
- bezt->f3 &= ~SELECT;
- }
- }
- else {
- select_bpoint(bp, DESELECT, SELECT, HIDDEN);
- if (bp == vert) {
- cu->actvert = CU_ACT_NONE;
+ select_bpoint(bp, SELECT, SELECT, HIDDEN);
+ BKE_curve_nurb_vert_active_set(cu, nu, bp);
}
+ break;
}
- }
- else if (toggle) {
- if (bezt) {
- if (hand == 1) {
- if (bezt->f2 & SELECT) {
+ case SEL_OP_SUB: {
+ if (bezt) {
+ if (hand == 1) {
select_beztriple(bezt, DESELECT, SELECT, HIDDEN);
if (bezt == vert) {
cu->actvert = CU_ACT_NONE;
}
}
+ else if (hand == 0) {
+ bezt->f1 &= ~SELECT;
+ }
else {
- select_beztriple(bezt, SELECT, SELECT, HIDDEN);
- BKE_curve_nurb_vert_active_set(cu, nu, bezt);
+ bezt->f3 &= ~SELECT;
}
}
- else if (hand == 0) {
- bezt->f1 ^= SELECT;
- }
else {
- bezt->f3 ^= SELECT;
- }
- }
- else {
- if (bp->f1 & SELECT) {
select_bpoint(bp, DESELECT, SELECT, HIDDEN);
if (bp == vert) {
cu->actvert = CU_ACT_NONE;
}
}
+ break;
+ }
+ case SEL_OP_XOR: {
+ if (bezt) {
+ if (hand == 1) {
+ if (bezt->f2 & SELECT) {
+ select_beztriple(bezt, DESELECT, SELECT, HIDDEN);
+ if (bezt == vert) {
+ cu->actvert = CU_ACT_NONE;
+ }
+ }
+ else {
+ select_beztriple(bezt, SELECT, SELECT, HIDDEN);
+ BKE_curve_nurb_vert_active_set(cu, nu, bezt);
+ }
+ }
+ else if (hand == 0) {
+ bezt->f1 ^= SELECT;
+ }
+ else {
+ bezt->f3 ^= SELECT;
+ }
+ }
else {
- select_bpoint(bp, SELECT, SELECT, HIDDEN);
- BKE_curve_nurb_vert_active_set(cu, nu, bp);
+ if (bp->f1 & SELECT) {
+ select_bpoint(bp, DESELECT, SELECT, HIDDEN);
+ if (bp == vert) {
+ cu->actvert = CU_ACT_NONE;
+ }
+ }
+ else {
+ select_bpoint(bp, SELECT, SELECT, HIDDEN);
+ BKE_curve_nurb_vert_active_set(cu, nu, bp);
+ }
}
+ break;
}
- }
- else {
- BKE_nurbList_flag_set(editnurb, SELECT, false);
+ case SEL_OP_SET: {
+ BKE_nurbList_flag_set(editnurb, SELECT, false);
- if (bezt) {
+ if (bezt) {
- if (hand == 1) {
- select_beztriple(bezt, SELECT, SELECT, HIDDEN);
- }
- else {
- if (hand == 0) {
- bezt->f1 |= SELECT;
+ if (hand == 1) {
+ select_beztriple(bezt, SELECT, SELECT, HIDDEN);
}
else {
- bezt->f3 |= SELECT;
+ if (hand == 0) {
+ bezt->f1 |= SELECT;
+ }
+ else {
+ bezt->f3 |= SELECT;
+ }
}
+ BKE_curve_nurb_vert_active_set(cu, nu, bezt);
+ }
+ else {
+ select_bpoint(bp, SELECT, SELECT, HIDDEN);
+ BKE_curve_nurb_vert_active_set(cu, nu, bp);
}
- BKE_curve_nurb_vert_active_set(cu, nu, bezt);
+ break;
}
- else {
- select_bpoint(bp, SELECT, SELECT, HIDDEN);
- BKE_curve_nurb_vert_active_set(cu, nu, bp);
+ case SEL_OP_AND: {
+ BLI_assert_unreachable(); /* Doesn't make sense for picking. */
+ break;
}
}
@@ -4876,10 +4899,10 @@ bool ED_curve_editnurb_select_pick(
DEG_id_tag_update(obedit->data, ID_RECALC_SELECT | ID_RECALC_COPY_ON_WRITE);
WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
- return true;
+ changed = true;
}
- return false;
+ return changed || found;
}
/** \} */
diff --git a/source/blender/editors/curve/editfont.c b/source/blender/editors/curve/editfont.c
index 02c7f3856e8..611dbb2e80c 100644
--- a/source/blender/editors/curve/editfont.c
+++ b/source/blender/editors/curve/editfont.c
@@ -526,16 +526,16 @@ static bool font_paste_utf8(bContext *C, const char *str, const size_t str_len)
/** \name Paste From File Operator
* \{ */
-static int paste_from_file(bContext *C, ReportList *reports, const char *filename)
+static int paste_from_file(bContext *C, ReportList *reports, const char *filepath)
{
Object *obedit = CTX_data_edit_object(C);
char *strp;
size_t filelen;
int retval;
- strp = BLI_file_read_text_as_mem(filename, 1, &filelen);
+ strp = BLI_file_read_text_as_mem(filepath, 1, &filelen);
if (strp == NULL) {
- BKE_reportf(reports, RPT_ERROR, "Failed to open file '%s'", filename);
+ BKE_reportf(reports, RPT_ERROR, "Failed to open file '%s'", filepath);
return OPERATOR_CANCELLED;
}
strp[filelen] = 0;
@@ -545,7 +545,7 @@ static int paste_from_file(bContext *C, ReportList *reports, const char *filenam
retval = OPERATOR_FINISHED;
}
else {
- BKE_reportf(reports, RPT_ERROR, "File too long %s", filename);
+ BKE_reportf(reports, RPT_ERROR, "File too long %s", filepath);
retval = OPERATOR_CANCELLED;
}
@@ -556,12 +556,12 @@ static int paste_from_file(bContext *C, ReportList *reports, const char *filenam
static int paste_from_file_exec(bContext *C, wmOperator *op)
{
- char *path;
+ char *filepath;
int retval;
- path = RNA_string_get_alloc(op->ptr, "filepath", NULL, 0, NULL);
- retval = paste_from_file(C, op->reports, path);
- MEM_freeN(path);
+ filepath = RNA_string_get_alloc(op->ptr, "filepath", NULL, 0, NULL);
+ retval = paste_from_file(C, op->reports, filepath);
+ MEM_freeN(filepath);
return retval;
}
@@ -2091,7 +2091,7 @@ static int font_open_exec(bContext *C, wmOperator *op)
static int open_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
VFont *vfont = NULL;
- const char *path;
+ const char *filepath;
PointerRNA idptr;
PropertyPointerRNA *pprop;
@@ -2106,13 +2106,13 @@ static int open_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)
vfont = (VFont *)idptr.owner_id;
}
- path = (vfont && !BKE_vfont_is_builtin(vfont)) ? vfont->filepath : U.fontdir;
+ filepath = (vfont && !BKE_vfont_is_builtin(vfont)) ? vfont->filepath : U.fontdir;
if (RNA_struct_property_is_set(op->ptr, "filepath")) {
return font_open_exec(C, op);
}
- RNA_string_set(op->ptr, "filepath", path);
+ RNA_string_set(op->ptr, "filepath", filepath);
WM_event_add_fileselect(C, op);
return OPERATOR_RUNNING_MODAL;
@@ -2184,7 +2184,10 @@ void FONT_OT_unlink(wmOperatorType *ot)
}
bool ED_curve_editfont_select_pick(
- bContext *C, const int mval[2], bool extend, bool deselect, bool toggle)
+ bContext *C,
+ const int mval[2],
+ /* NOTE: `params->deselect_all` is ignored as only one text-box is active at once. */
+ const struct SelectPick_Params *params)
{
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
Object *obedit = CTX_data_edit_object(C);
@@ -2203,9 +2206,7 @@ bool ED_curve_editfont_select_pick(
ED_view3d_init_mats_rv3d(vc.obedit, vc.rv3d);
/* currently only select active */
- (void)extend;
- (void)deselect;
- (void)toggle;
+ (void)params;
for (i_iter = 0; i_iter < cu->totbox; i_iter++) {
int i = (i_iter + i_actbox) % cu->totbox;
@@ -2257,6 +2258,8 @@ bool ED_curve_editfont_select_pick(
if (cu->actbox != actbox_select) {
cu->actbox = actbox_select;
WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
+ /* TODO: support #ID_RECALC_SELECT. */
+ DEG_id_tag_update(obedit->data, ID_RECALC_COPY_ON_WRITE);
}
return true;
}
diff --git a/source/blender/editors/curves/intern/curves_add.cc b/source/blender/editors/curves/intern/curves_add.cc
index 9cde23451dc..17108619a4d 100644
--- a/source/blender/editors/curves/intern/curves_add.cc
+++ b/source/blender/editors/curves/intern/curves_add.cc
@@ -21,7 +21,7 @@ bke::CurvesGeometry primitive_random_sphere(const int curves_size, const int poi
float *radius_data = (float *)CustomData_add_layer_named(
&curves.point_data, CD_PROP_FLOAT, CD_DEFAULT, nullptr, curves.point_size, "radius");
- MutableSpan<float> radii{radius_data, curves.points_size()};
+ MutableSpan<float> radii{radius_data, curves.points_num()};
for (const int i : offsets.index_range()) {
offsets[i] = points_per_curve * i;
@@ -30,7 +30,7 @@ bke::CurvesGeometry primitive_random_sphere(const int curves_size, const int poi
RandomNumberGenerator rng;
for (const int i : curves.curves_range()) {
- const IndexRange curve_range = curves.range_for_curve(i);
+ const IndexRange curve_range = curves.points_for_curve(i);
MutableSpan<float3> curve_positions = positions.slice(curve_range);
MutableSpan<float> curve_radii = radii.slice(curve_range);
diff --git a/source/blender/editors/datafiles/CMakeLists.txt b/source/blender/editors/datafiles/CMakeLists.txt
index edbdc1ee96f..d58bbec01cd 100644
--- a/source/blender/editors/datafiles/CMakeLists.txt
+++ b/source/blender/editors/datafiles/CMakeLists.txt
@@ -595,9 +595,9 @@ set(ICON_NAMES
axis_top
layer_used
layer_active
- outliner_ob_hair
- outliner_data_hair
- hair_data
+ outliner_ob_curves
+ outliner_data_curves
+ curves_data
outliner_ob_pointcloud
outliner_data_pointcloud
pointcloud_data
@@ -652,7 +652,7 @@ set(ICON_NAMES
matsphere
matcube
monkey
- hair
+ curves
aliased
antialiased
mat_sphere_sky
@@ -770,6 +770,11 @@ set_property(GLOBAL PROPERTY ICON_GEOM_NAMES
ops.curve.extrude_move
ops.curve.radius
ops.curve.vertex_random
+ ops.curves.sculpt_add
+ ops.curves.sculpt_comb
+ ops.curves.sculpt_cut
+ ops.curves.sculpt_delete
+ ops.curves.sculpt_grow
ops.generic.cursor
ops.generic.select
ops.generic.select_box
diff --git a/source/blender/editors/geometry/geometry_attributes.cc b/source/blender/editors/geometry/geometry_attributes.cc
index 77cd5d9221f..6225a68f53c 100644
--- a/source/blender/editors/geometry/geometry_attributes.cc
+++ b/source/blender/editors/geometry/geometry_attributes.cc
@@ -36,10 +36,6 @@
namespace blender::ed::geometry {
-using fn::CPPType;
-using fn::GArray;
-using fn::GVArray;
-
/*********************** Attribute Operators ************************/
static bool geometry_attributes_poll(bContext *C)
diff --git a/source/blender/editors/gpencil/gpencil_convert.c b/source/blender/editors/gpencil/gpencil_convert.c
index d4518f21586..4f9468cc9c4 100644
--- a/source/blender/editors/gpencil/gpencil_convert.c
+++ b/source/blender/editors/gpencil/gpencil_convert.c
@@ -207,7 +207,7 @@ typedef struct tGpTimingData {
int seed;
/* Data set from points, used to compute final timing FCurve */
- int num_points, cur_point;
+ int points_num, cur_point;
/* Distances */
float *dists;
@@ -229,29 +229,29 @@ typedef struct tGpTimingData {
/* Init point buffers for timing data.
* Note this assumes we only grow those arrays!
*/
-static void gpencil_timing_data_set_nbr(tGpTimingData *gtd, const int nbr)
+static void gpencil_timing_data_set_num(tGpTimingData *gtd, const int num)
{
float *tmp;
- BLI_assert(nbr > gtd->num_points);
+ BLI_assert(num > gtd->points_num);
/* distances */
tmp = gtd->dists;
- gtd->dists = MEM_callocN(sizeof(float) * nbr, __func__);
+ gtd->dists = MEM_callocN(sizeof(float) * num, __func__);
if (tmp) {
- memcpy(gtd->dists, tmp, sizeof(float) * gtd->num_points);
+ memcpy(gtd->dists, tmp, sizeof(float) * gtd->points_num);
MEM_freeN(tmp);
}
/* times */
tmp = gtd->times;
- gtd->times = MEM_callocN(sizeof(float) * nbr, __func__);
+ gtd->times = MEM_callocN(sizeof(float) * num, __func__);
if (tmp) {
- memcpy(gtd->times, tmp, sizeof(float) * gtd->num_points);
+ memcpy(gtd->times, tmp, sizeof(float) * gtd->points_num);
MEM_freeN(tmp);
}
- gtd->num_points = nbr;
+ gtd->points_num = num;
}
/* add stroke point to timing buffers */
@@ -297,15 +297,15 @@ static void gpencil_timing_data_add_point(tGpTimingData *gtd,
static int gpencil_find_end_of_stroke_idx(tGpTimingData *gtd,
RNG *rng,
const int idx,
- const int nbr_gaps,
- int *nbr_done_gaps,
+ const int gaps_count,
+ int *gaps_done_count,
const float tot_gaps_time,
const float delta_time,
float *next_delta_time)
{
int j;
- for (j = idx + 1; j < gtd->num_points; j++) {
+ for (j = idx + 1; j < gtd->points_num; j++) {
if (gtd->times[j] < 0) {
gtd->times[j] = -gtd->times[j];
if (gtd->mode == GP_STROKECONVERT_TIMING_CUSTOMGAP) {
@@ -316,7 +316,7 @@ static int gpencil_find_end_of_stroke_idx(tGpTimingData *gtd,
/* We want gaps that are in gtd->gap_duration +/- gtd->gap_randomness range,
* and which sum to exactly tot_gaps_time...
*/
- int rem_gaps = nbr_gaps - (*nbr_done_gaps);
+ int rem_gaps = gaps_count - (*gaps_done_count);
if (rem_gaps < 2) {
/* Last gap, just give remaining time! */
*next_delta_time = tot_gaps_time;
@@ -327,7 +327,7 @@ static int gpencil_find_end_of_stroke_idx(tGpTimingData *gtd,
/* This code ensures that if the first gaps
* have been shorter than average gap_duration, next gaps
* will tend to be longer (i.e. try to recover the lateness), and vice-versa! */
- delta = delta_time - (gtd->gap_duration * (*nbr_done_gaps));
+ delta = delta_time - (gtd->gap_duration * (*gaps_done_count));
/* Clamp min between [-gap_randomness, 0.0], with lower delta giving higher min */
min = -gtd->gap_randomness - delta;
@@ -343,7 +343,7 @@ static int gpencil_find_end_of_stroke_idx(tGpTimingData *gtd,
*next_delta_time += gtd->gap_duration;
}
}
- (*nbr_done_gaps)++;
+ (*gaps_done_count)++;
break;
}
}
@@ -353,14 +353,14 @@ static int gpencil_find_end_of_stroke_idx(tGpTimingData *gtd,
static void gpencil_stroke_path_animation_preprocess_gaps(tGpTimingData *gtd,
RNG *rng,
- int *nbr_gaps,
+ int *gaps_count,
float *r_tot_gaps_time)
{
float delta_time = 0.0f;
- for (int i = 0; i < gtd->num_points; i++) {
+ for (int i = 0; i < gtd->points_num; i++) {
if (gtd->times[i] < 0 && i) {
- (*nbr_gaps)++;
+ (*gaps_count)++;
gtd->times[i] = -gtd->times[i] - delta_time;
delta_time += gtd->times[i] - gtd->times[i - 1];
gtd->times[i] = -gtd->times[i - 1]; /* Temp marker, values *have* to be different! */
@@ -371,7 +371,7 @@ static void gpencil_stroke_path_animation_preprocess_gaps(tGpTimingData *gtd,
}
gtd->tot_time -= delta_time;
- *r_tot_gaps_time = (float)(*nbr_gaps) * gtd->gap_duration;
+ *r_tot_gaps_time = (float)(*gaps_count) * gtd->gap_duration;
gtd->tot_time += *r_tot_gaps_time;
if (gtd->gap_randomness > 0.0f) {
BLI_rng_srandom(rng, gtd->seed);
@@ -387,7 +387,7 @@ static void gpencil_stroke_path_animation_add_keyframes(ReportList *reports,
tGpTimingData *gtd,
RNG *rng,
const float time_range,
- const int nbr_gaps,
+ const int gaps_count,
const float tot_gaps_time)
{
/* Use actual recorded timing! */
@@ -399,20 +399,20 @@ static void gpencil_stroke_path_animation_add_keyframes(ReportList *reports,
/* CustomGaps specific */
float delta_time = 0.0f, next_delta_time = 0.0f;
- int nbr_done_gaps = 0;
+ int gaps_done_count = 0;
/* This is a bit tricky, as:
* - We can't add arbitrarily close points on FCurve (in time).
* - We *must* have all "caps" points of all strokes in FCurve, as much as possible!
*/
- for (int i = 0; i < gtd->num_points; i++) {
+ for (int i = 0; i < gtd->points_num; i++) {
/* If new stroke... */
if (i > end_stroke_idx) {
start_stroke_idx = i;
delta_time = next_delta_time;
/* find end of that new stroke */
end_stroke_idx = gpencil_find_end_of_stroke_idx(
- gtd, rng, i, nbr_gaps, &nbr_done_gaps, tot_gaps_time, delta_time, &next_delta_time);
+ gtd, rng, i, gaps_count, &gaps_done_count, tot_gaps_time, delta_time, &next_delta_time);
/* This one should *never* be negative! */
end_stroke_time = time_start +
((gtd->times[end_stroke_idx] + delta_time) / gtd->tot_time * time_range);
@@ -502,7 +502,7 @@ static void gpencil_stroke_path_animation(bContext *C,
FCurve *fcu;
PointerRNA ptr;
PropertyRNA *prop = NULL;
- int nbr_gaps = 0;
+ int gaps_count = 0;
if (gtd->mode == GP_STROKECONVERT_TIMING_NONE) {
return;
@@ -571,7 +571,7 @@ static void gpencil_stroke_path_animation(bContext *C,
/* Pre-process gaps, in case we don't want to keep their original timing */
if (gtd->mode == GP_STROKECONVERT_TIMING_CUSTOMGAP) {
- gpencil_stroke_path_animation_preprocess_gaps(gtd, rng, &nbr_gaps, &tot_gaps_time);
+ gpencil_stroke_path_animation_preprocess_gaps(gtd, rng, &gaps_count, &tot_gaps_time);
}
if (gtd->realtime) {
@@ -582,7 +582,7 @@ static void gpencil_stroke_path_animation(bContext *C,
}
gpencil_stroke_path_animation_add_keyframes(
- reports, ptr, prop, depsgraph, fcu, cu, gtd, rng, time_range, nbr_gaps, tot_gaps_time);
+ reports, ptr, prop, depsgraph, fcu, cu, gtd, rng, time_range, gaps_count, tot_gaps_time);
BLI_rng_free(rng);
}
@@ -684,7 +684,7 @@ static void gpencil_stroke_to_path(bContext *C,
}
if (do_gtd) {
- gpencil_timing_data_set_nbr(gtd, nu->pntsu);
+ gpencil_timing_data_set_num(gtd, nu->pntsu);
}
/* If needed, make the link between both strokes with two zero-radius additional points */
@@ -929,7 +929,7 @@ static void gpencil_stroke_to_bezier(bContext *C,
}
if (do_gtd) {
- gpencil_timing_data_set_nbr(gtd, nu->pntsu);
+ gpencil_timing_data_set_num(gtd, nu->pntsu);
}
tot = gps->totpoints;
@@ -1536,7 +1536,7 @@ static int gpencil_convert_layer_exec(bContext *C, wmOperator *op)
gtd.gap_randomness = RNA_float_get(op->ptr, "gap_randomness");
gtd.gap_randomness = min_ff(gtd.gap_randomness, gtd.gap_duration);
gtd.seed = RNA_int_get(op->ptr, "seed");
- gtd.num_points = gtd.cur_point = 0;
+ gtd.points_num = gtd.cur_point = 0;
gtd.dists = gtd.times = NULL;
gtd.tot_dist = gtd.tot_time = gtd.gap_tot_time = 0.0f;
gtd.inittime = 0.0;
diff --git a/source/blender/editors/gpencil/gpencil_fill.c b/source/blender/editors/gpencil/gpencil_fill.c
index 069493025dc..45a2247c65e 100644
--- a/source/blender/editors/gpencil/gpencil_fill.c
+++ b/source/blender/editors/gpencil/gpencil_fill.c
@@ -1387,6 +1387,15 @@ static void gpencil_get_outline_points(tGPDfill *tgpf, const bool dilate)
current_check_co[1] = boundary_co[1] + offset[offset_idx][1];
int image_idx = ibuf->x * current_check_co[1] + current_check_co[0];
+ /* Check if the index is inside the image. If the index is outside is
+ * because the algorithm is unable to find the outline of the figure. This is
+ * possible for negative filling when click inside a figure instead of
+ * clicking outside.
+ * If the index is out of range, finish the filling. */
+ if (image_idx > imagesize - 1) {
+ start_found = false;
+ break;
+ }
get_pixel(ibuf, image_idx, rgba);
/* find next boundary pixel */
diff --git a/source/blender/editors/include/ED_armature.h b/source/blender/editors/include/ED_armature.h
index 2f5f82e332c..84efc875be7 100644
--- a/source/blender/editors/include/ED_armature.h
+++ b/source/blender/editors/include/ED_armature.h
@@ -27,6 +27,7 @@ struct MeshDeformModifierData;
struct Object;
struct ReportList;
struct Scene;
+struct SelectPick_Params;
struct UndoType;
struct View3D;
struct ViewLayer;
@@ -164,18 +165,20 @@ bool ED_armature_edit_deselect_all_visible(struct Object *obedit);
bool ED_armature_edit_deselect_all_multi_ex(struct Base **bases, uint bases_len);
bool ED_armature_edit_deselect_all_visible_multi_ex(struct Base **bases, uint bases_len);
bool ED_armature_edit_deselect_all_visible_multi(struct bContext *C);
+/**
+ * \return True when pick finds an element or the selection changed.
+ */
bool ED_armature_edit_select_pick_bone(struct bContext *C,
struct Base *basact,
struct EditBone *ebone,
int selmask,
- bool extend,
- bool deselect,
- bool toggle);
+ const struct SelectPick_Params *params);
/**
* Bone selection picking for armature edit-mode in the view3d.
*/
-bool ED_armature_edit_select_pick(
- struct bContext *C, const int mval[2], bool extend, bool deselect, bool toggle);
+bool ED_armature_edit_select_pick(struct bContext *C,
+ const int mval[2],
+ const struct SelectPick_Params *params);
/**
* Perform a selection operation on elements which have been 'touched',
* use for lasso & border select but can be used elsewhere too.
@@ -305,25 +308,26 @@ void ED_pose_recalculate_paths(struct bContext *C,
/* pose_select.c */
-void ED_armature_pose_select_pick_bone(struct ViewLayer *view_layer,
+/**
+ * \return True when pick finds an element or the selection changed.
+ */
+bool ED_armature_pose_select_pick_bone(struct ViewLayer *view_layer,
struct View3D *v3d,
struct Object *ob,
struct Bone *bone,
- bool extend,
- bool deselect,
- bool toggle);
+ const struct SelectPick_Params *params);
/**
* Called for mode-less pose selection.
* assumes the active object is still on old situation.
+ *
+ * \return True when pick finds an element or the selection changed.
*/
bool ED_armature_pose_select_pick_with_buffer(struct ViewLayer *view_layer,
struct View3D *v3d,
struct Base *base,
const struct GPUSelectResult *buffer,
short hits,
- bool extend,
- bool deselect,
- bool toggle,
+ const struct SelectPick_Params *params,
bool do_nearest);
/**
* While in weight-paint mode, a single pose may be active as well.
diff --git a/source/blender/editors/include/ED_curve.h b/source/blender/editors/include/ED_curve.h
index c97f97a2ddc..6097e7c69d9 100644
--- a/source/blender/editors/include/ED_curve.h
+++ b/source/blender/editors/include/ED_curve.h
@@ -19,6 +19,7 @@ struct EditNurb;
struct Main;
struct Nurb;
struct Object;
+struct SelectPick_Params;
struct Text;
struct UndoType;
struct View3D;
@@ -46,8 +47,12 @@ void ED_curve_editnurb_load(struct Main *bmain, struct Object *obedit);
void ED_curve_editnurb_make(struct Object *obedit);
void ED_curve_editnurb_free(struct Object *obedit);
-bool ED_curve_editnurb_select_pick(
- struct bContext *C, const int mval[2], bool extend, bool deselect, bool toggle);
+/**
+ * \return True when pick finds an element or the selection changed.
+ */
+bool ED_curve_editnurb_select_pick(struct bContext *C,
+ const int mval[2],
+ const struct SelectPick_Params *params);
struct Nurb *ED_curve_add_nurbs_primitive(
struct bContext *C, struct Object *obedit, float mat[4][4], int type, int newob);
@@ -100,10 +105,13 @@ int ED_curve_updateAnimPaths(struct Main *bmain, struct Curve *cu);
bool ED_curve_active_center(struct Curve *cu, float center[3]);
/**
- * TextBox selection
+ * Text box selection.
+ *
+ * \return True when pick finds an element or the selection changed.
*/
-bool ED_curve_editfont_select_pick(
- struct bContext *C, const int mval[2], bool extend, bool deselect, bool toggle);
+bool ED_curve_editfont_select_pick(struct bContext *C,
+ const int mval[2],
+ const struct SelectPick_Params *params);
/* editfont_undo.c */
diff --git a/source/blender/editors/include/ED_keyframes_edit.h b/source/blender/editors/include/ED_keyframes_edit.h
index 90e8dbad272..3a0bb9738ae 100644
--- a/source/blender/editors/include/ED_keyframes_edit.h
+++ b/source/blender/editors/include/ED_keyframes_edit.h
@@ -382,6 +382,7 @@ void delete_fcurve_key(struct FCurve *fcu, int index, bool do_recalc);
bool delete_fcurve_keys(struct FCurve *fcu);
void clear_fcurve_keys(struct FCurve *fcu);
void duplicate_fcurve_keys(struct FCurve *fcu);
+float get_default_rna_value(struct FCurve *fcu, struct PropertyRNA *prop, struct PointerRNA *ptr);
typedef struct FCurveSegment {
struct FCurveSegment *next, *prev;
@@ -404,6 +405,7 @@ void blend_to_neighbor_fcurve_segment(struct FCurve *fcu,
float factor);
void breakdown_fcurve_segment(struct FCurve *fcu, struct FCurveSegment *segment, float factor);
bool decimate_fcurve(struct bAnimListElem *ale, float remove_ratio, float error_sq_max);
+void blend_to_default_fcurve(struct PointerRNA *id_ptr, struct FCurve *fcu, float factor);
/**
* Use a weighted moving-means method to reduce intensity of fluctuations.
*/
diff --git a/source/blender/editors/include/ED_lattice.h b/source/blender/editors/include/ED_lattice.h
index eddf69e1cb6..1b9311cbacf 100644
--- a/source/blender/editors/include/ED_lattice.h
+++ b/source/blender/editors/include/ED_lattice.h
@@ -13,6 +13,7 @@ extern "C" {
struct Base;
struct Object;
+struct SelectPick_Params;
struct UndoType;
struct wmKeyConfig;
@@ -24,8 +25,12 @@ void ED_keymap_lattice(struct wmKeyConfig *keyconf);
/* editlattice_select.c */
bool ED_lattice_flags_set(struct Object *obedit, int flag);
-bool ED_lattice_select_pick(
- struct bContext *C, const int mval[2], bool extend, bool deselect, bool toggle);
+/**
+ * \return True when pick finds an element or the selection changed.
+ */
+bool ED_lattice_select_pick(struct bContext *C,
+ const int mval[2],
+ const struct SelectPick_Params *params);
bool ED_lattice_deselect_all_multi_ex(struct Base **bases, uint bases_len);
bool ED_lattice_deselect_all_multi(struct bContext *C);
diff --git a/source/blender/editors/include/ED_mask.h b/source/blender/editors/include/ED_mask.h
index bc01f76f20d..7039d6d9fc7 100644
--- a/source/blender/editors/include/ED_mask.h
+++ b/source/blender/editors/include/ED_mask.h
@@ -63,7 +63,6 @@ bool ED_mask_selected_minmax(const struct bContext *C,
/* mask_draw.c */
-void ED_mask_draw(const struct bContext *C, char draw_flag, char draw_type);
/**
* Sets up the opengl context.
* width, height are to match the values from #ED_mask_get_size().
diff --git a/source/blender/editors/include/ED_mball.h b/source/blender/editors/include/ED_mball.h
index e0c921ea0db..7f2c4dff311 100644
--- a/source/blender/editors/include/ED_mball.h
+++ b/source/blender/editors/include/ED_mball.h
@@ -12,7 +12,9 @@ extern "C" {
#endif
struct Base;
+struct MetaElem;
struct Object;
+struct SelectPick_Params;
struct UndoType;
struct bContext;
struct wmKeyConfig;
@@ -31,11 +33,19 @@ struct MetaElem *ED_mball_add_primitive(struct bContext *C,
float dia,
int type);
+struct Base *ED_mball_base_and_elem_from_select_buffer(struct Base **bases,
+ uint bases_len,
+ const uint select_id,
+ struct MetaElem **r_ml);
+
/**
- * Select MetaElement with mouse click (user can select radius circle or stiffness circle).
+ * Select meta-element with mouse click (user can select radius circle or stiffness circle).
+ *
+ * \return True when pick finds an element or the selection changed.
*/
-bool ED_mball_select_pick(
- struct bContext *C, const int mval[2], bool extend, bool deselect, bool toggle);
+bool ED_mball_select_pick(struct bContext *C,
+ const int mval[2],
+ const struct SelectPick_Params *params);
bool ED_mball_deselect_all_multi_ex(struct Base **bases, uint bases_len);
bool ED_mball_deselect_all_multi(struct bContext *C);
diff --git a/source/blender/editors/include/ED_mesh.h b/source/blender/editors/include/ED_mesh.h
index 68e46dfa0e5..03ca4cd062b 100644
--- a/source/blender/editors/include/ED_mesh.h
+++ b/source/blender/editors/include/ED_mesh.h
@@ -31,6 +31,7 @@ struct Mesh;
struct Object;
struct ReportList;
struct Scene;
+struct SelectPick_Params;
struct UndoType;
struct UvMapVert;
struct UvVertMap;
@@ -268,8 +269,9 @@ bool EDBM_unified_findnearest_from_raycast(struct ViewContext *vc,
struct BMEdge **r_eed,
struct BMFace **r_efa);
-bool EDBM_select_pick(
- struct bContext *C, const int mval[2], bool extend, bool deselect, bool toggle);
+bool EDBM_select_pick(struct bContext *C,
+ const int mval[2],
+ const struct SelectPick_Params *params);
/**
* When switching select mode, makes sure selection is consistent for editing
@@ -387,12 +389,13 @@ void ED_keymap_mesh(struct wmKeyConfig *keyconf);
* use in object mode when selecting faces (while painting).
*/
void paintface_flush_flags(struct bContext *C, struct Object *ob, short flag);
+/**
+ * \return True when pick finds an element or the selection changed.
+ */
bool paintface_mouse_select(struct bContext *C,
- struct Object *ob,
const int mval[2],
- bool extend,
- bool deselect,
- bool toggle);
+ const struct SelectPick_Params *params,
+ struct Object *ob);
bool paintface_deselect_all_visible(struct bContext *C,
struct Object *ob,
int action,
diff --git a/source/blender/editors/include/ED_particle.h b/source/blender/editors/include/ED_particle.h
index a4797ff167c..553aa444891 100644
--- a/source/blender/editors/include/ED_particle.h
+++ b/source/blender/editors/include/ED_particle.h
@@ -16,6 +16,7 @@ struct PTCacheEdit;
struct ParticleEditSettings;
struct ParticleSystem;
struct Scene;
+struct SelectPick_Params;
struct UndoType;
struct ViewLayer;
struct bContext;
@@ -54,8 +55,9 @@ void PE_update_object(struct Depsgraph *depsgraph,
/* selection tools */
-bool PE_mouse_particles(
- struct bContext *C, const int mval[2], bool extend, bool deselect, bool toggle);
+bool PE_mouse_particles(struct bContext *C,
+ const int mval[2],
+ const struct SelectPick_Params *params);
bool PE_box_select(struct bContext *C, const struct rcti *rect, int sel_op);
bool PE_circle_select(struct bContext *C,
struct wmGenericUserData *wm_userdata,
diff --git a/source/blender/editors/include/ED_select_utils.h b/source/blender/editors/include/ED_select_utils.h
index 26d8d0a3d8c..b9ef5c283aa 100644
--- a/source/blender/editors/include/ED_select_utils.h
+++ b/source/blender/editors/include/ED_select_utils.h
@@ -70,6 +70,31 @@ bool ED_select_similar_compare_float_tree(const struct KDTree_1d *tree,
*/
eSelectOp ED_select_op_modal(eSelectOp sel_op, bool is_first);
+/** Argument passed to picking functions. */
+struct SelectPick_Params {
+ /**
+ * - #SEL_OP_ADD named "extend" from operators.
+ * - #SEL_OP_SUB named "deselect" from operators.
+ * - #SEL_OP_XOR named "toggle" from operators.
+ * - #SEL_OP_AND (never used for picking).
+ * - #SEL_OP_SET use when "extend", "deselect" and "toggle" are all disabled.
+ */
+ eSelectOp sel_op;
+ /** Deselect all, even when there is nothing found at the cursor location. */
+ bool deselect_all;
+ /**
+ * When selecting an element that is already selected, do nothing (passthrough).
+ * don't even make it active.
+ * Use to implement tweaking to move the selection without first de-selecting.
+ */
+ bool select_passthrough;
+};
+
+/**
+ * Utility to get #eSelectPickMode from booleans for convenience.
+ */
+eSelectOp ED_select_op_from_booleans(bool extend, bool deselect, bool toggle);
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h
index d28700c64ee..d8415064d2f 100644
--- a/source/blender/editors/include/UI_interface.h
+++ b/source/blender/editors/include/UI_interface.h
@@ -157,6 +157,8 @@ enum {
UI_BLOCK_POPOVER_ONCE = 1 << 22,
/** Always show key-maps, even for non-menus. */
UI_BLOCK_SHOW_SHORTCUT_ALWAYS = 1 << 23,
+ /** Don't show library override state for buttons in this block. */
+ UI_BLOCK_NO_DRAW_OVERRIDDEN_STATE = 1 << 24,
/** The block is only used during the search process and will not be drawn.
* Currently just for the case of a closed panel's sub-panel (and its sub-panels). */
UI_BLOCK_SEARCH_ONLY = 1 << 25,
@@ -1610,6 +1612,14 @@ uiBut *uiDefAutoButR(uiBlock *block,
int y,
int width,
int height);
+void uiDefAutoButsArrayR(uiBlock *block,
+ PointerRNA *ptr,
+ PropertyRNA *prop,
+ const int icon,
+ const int x,
+ const int y,
+ const int tot_width,
+ const int height);
/**
* \a check_prop callback filters functions to avoid drawing certain properties,
* in cases where PROP_HIDDEN flag can't be used for a property.
diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c
index 3619a7ce317..e83a8761e12 100644
--- a/source/blender/editors/interface/interface.c
+++ b/source/blender/editors/interface/interface.c
@@ -4238,28 +4238,28 @@ static void ui_def_but_rna__menu(bContext *UNUSED(C), uiLayout *layout, void *bu
int totitems = 0;
int categories = 0;
- int nbr_entries_nosepr = 0;
+ int entries_nosepr_count = 0;
for (const EnumPropertyItem *item = item_array; item->identifier; item++, totitems++) {
if (!item->identifier[0]) {
/* inconsistent, but menus with categories do not look good flipped */
if (item->name) {
block->flag |= UI_BLOCK_NO_FLIP;
categories++;
- nbr_entries_nosepr++;
+ entries_nosepr_count++;
}
- /* We do not want simple separators in nbr_entries_nosepr count */
+ /* We do not want simple separators in `entries_nosepr_count`. */
continue;
}
- nbr_entries_nosepr++;
+ entries_nosepr_count++;
}
/* Columns and row estimation. Ignore simple separators here. */
- int columns = (nbr_entries_nosepr + 20) / 20;
+ int columns = (entries_nosepr_count + 20) / 20;
if (columns < 1) {
columns = 1;
}
if (columns > 8) {
- columns = (nbr_entries_nosepr + 25) / 25;
+ columns = (entries_nosepr_count + 25) / 25;
}
int rows = totitems / columns;
diff --git a/source/blender/editors/interface/interface_draw.c b/source/blender/editors/interface/interface_draw.c
index decd8c03d70..c02024bc82d 100644
--- a/source/blender/editors/interface/interface_draw.c
+++ b/source/blender/editors/interface/interface_draw.c
@@ -546,13 +546,13 @@ void ui_draw_but_HISTOGRAM(ARegion *UNUSED(region),
#undef HISTOGRAM_TOT_GRID_LINES
-static void waveform_draw_one(float *waveform, int nbr, const float col[3])
+static void waveform_draw_one(float *waveform, int waveform_num, const float col[3])
{
GPUVertFormat format = {0};
const uint pos_id = GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
GPUVertBuf *vbo = GPU_vertbuf_create_with_format(&format);
- GPU_vertbuf_data_alloc(vbo, nbr);
+ GPU_vertbuf_data_alloc(vbo, waveform_num);
GPU_vertbuf_attr_fill(vbo, pos_id, waveform);
diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c
index 8677b1ed78a..8935df7b581 100644
--- a/source/blender/editors/interface/interface_handlers.c
+++ b/source/blender/editors/interface/interface_handlers.c
@@ -7969,7 +7969,16 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent *
}
if (but->flag & UI_BUT_DISABLED) {
- return WM_UI_HANDLER_BREAK;
+ /* It's important to continue here instead of breaking since breaking causes the event to be
+ * considered "handled", preventing further click/drag events from being generated.
+ *
+ * An example of where this is needed is dragging node-sockets, where dragging a node-socket
+ * could exit the button before the drag threshold was reached, disable the button then break
+ * handling of the #MOUSEMOVE event preventing the socket being dragged entirely, see: T96255.
+ *
+ * Region level event handling is responsible for preventing events being passed
+ * through to parts of the UI that are logically behind this button, see: T92364. */
+ return WM_UI_HANDLER_CONTINUE;
}
switch (but->type) {
diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h
index 694ae78ca9e..a7a2409ef17 100644
--- a/source/blender/editors/interface/interface_intern.h
+++ b/source/blender/editors/interface/interface_intern.h
@@ -1275,7 +1275,8 @@ void ui_layout_remove_but(uiLayout *layout, const uiBut *but);
*/
bool ui_layout_replace_but_ptr(uiLayout *layout, const void *old_but_ptr, uiBut *new_but);
/**
- * \note May reallocate \a but, so the possibly new address is returned.
+ * \note May reallocate \a but, so the possibly new address is returned. May also override the
+ * #UI_BUT_DISABLED flag depending on if a search pointer-property pair was provided/found.
*/
uiBut *ui_but_add_search(uiBut *but,
PointerRNA *ptr,
diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c
index 17f0ae1f2d4..cbc21bd481f 100644
--- a/source/blender/editors/interface/interface_layout.c
+++ b/source/blender/editors/interface/interface_layout.c
@@ -2751,6 +2751,10 @@ uiBut *ui_but_add_search(
ui_rna_collection_search_arg_free_fn,
NULL,
NULL);
+ /* If this is called multiple times for the same button, an earlier call may have taken the
+ * else branch below so the button was disabled. Now we have a searchprop, so it can be enabled
+ * again. */
+ but->flag &= ~UI_BUT_DISABLED;
}
else if (but->type == UI_BTYPE_SEARCH_MENU) {
/* In case we fail to find proper searchprop,
diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c
index 54bd1a2cebb..0f4f0ef48ff 100644
--- a/source/blender/editors/interface/interface_ops.c
+++ b/source/blender/editors/interface/interface_ops.c
@@ -1976,6 +1976,9 @@ static void UI_OT_drop_name(wmOperatorType *ot)
static bool ui_list_focused_poll(bContext *C)
{
const ARegion *region = CTX_wm_region(C);
+ if (!region) {
+ return false;
+ }
const wmWindow *win = CTX_wm_window(C);
const uiList *list = UI_list_find_mouse_over(region, win->eventstate);
diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c
index ad7c71e3be9..0e417fda911 100644
--- a/source/blender/editors/interface/interface_templates.c
+++ b/source/blender/editors/interface/interface_templates.c
@@ -6261,16 +6261,13 @@ void uiTemplateColormanagedViewSettings(uiLayout *layout,
ColorManagedViewSettings *view_settings = view_transform_ptr.data;
uiLayout *col = uiLayoutColumn(layout, false);
-
- uiLayout *row = uiLayoutRow(col, false);
- uiItemR(row, &view_transform_ptr, "view_transform", 0, IFACE_("View"), ICON_NONE);
+ uiItemR(col, &view_transform_ptr, "view_transform", 0, IFACE_("View"), ICON_NONE);
+ uiItemR(col, &view_transform_ptr, "look", 0, IFACE_("Look"), ICON_NONE);
col = uiLayoutColumn(layout, false);
uiItemR(col, &view_transform_ptr, "exposure", 0, NULL, ICON_NONE);
uiItemR(col, &view_transform_ptr, "gamma", 0, NULL, ICON_NONE);
- uiItemR(col, &view_transform_ptr, "look", 0, IFACE_("Look"), ICON_NONE);
-
col = uiLayoutColumn(layout, false);
uiItemR(col, &view_transform_ptr, "use_curve_mapping", 0, NULL, ICON_NONE);
if (view_settings->flag & COLORMANAGE_VIEW_USE_CURVES) {
diff --git a/source/blender/editors/interface/interface_utils.c b/source/blender/editors/interface/interface_utils.c
index 814bd956096..728d42a9353 100644
--- a/source/blender/editors/interface/interface_utils.c
+++ b/source/blender/editors/interface/interface_utils.c
@@ -320,6 +320,7 @@ uiBut *uiDefAutoButR(uiBlock *block,
-1,
-1,
NULL);
+ ui_but_add_search(but, ptr, prop, NULL, NULL);
break;
}
case PROP_COLLECTION: {
@@ -338,6 +339,29 @@ uiBut *uiDefAutoButR(uiBlock *block,
return but;
}
+void uiDefAutoButsArrayR(uiBlock *block,
+ PointerRNA *ptr,
+ PropertyRNA *prop,
+ const int icon,
+ const int x,
+ const int y,
+ const int tot_width,
+ const int height)
+{
+ const int len = RNA_property_array_length(ptr, prop);
+ if (len == 0) {
+ return;
+ }
+
+ const int item_width = tot_width / len;
+
+ UI_block_align_begin(block);
+ for (int i = 0; i < len; i++) {
+ uiDefAutoButR(block, ptr, prop, i, "", icon, x + i * item_width, y, item_width, height);
+ }
+ UI_block_align_end(block);
+}
+
eAutoPropButsReturn uiDefAutoButsRNA(uiLayout *layout,
PointerRNA *ptr,
bool (*check_prop)(PointerRNA *ptr,
diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c
index 35cf952b5ce..b33cab3cbc6 100644
--- a/source/blender/editors/interface/interface_widgets.c
+++ b/source/blender/editors/interface/interface_widgets.c
@@ -4963,6 +4963,9 @@ void ui_draw_but(const bContext *C, struct ARegion *region, uiStyle *style, uiBu
}
}
#endif
+ if (but->block->flag & UI_BLOCK_NO_DRAW_OVERRIDDEN_STATE) {
+ state &= ~UI_BUT_OVERRIDDEN;
+ }
const float zoom = 1.0f / but->block->aspect;
wt->state(wt, state, drawflag, but->emboss);
diff --git a/source/blender/editors/io/io_collada.c b/source/blender/editors/io/io_collada.c
index 1d5c9d717d0..dc62212bf53 100644
--- a/source/blender/editors/io/io_collada.c
+++ b/source/blender/editors/io/io_collada.c
@@ -221,14 +221,6 @@ static int wm_collada_export_exec(bContext *C, wmOperator *op)
export_settings.limit_precision = limit_precision != 0;
export_settings.keep_bind_info = keep_bind_info != 0;
- int includeFilter = OB_REL_NONE;
- if (export_settings.include_armatures) {
- includeFilter |= OB_REL_MOD_ARMATURE;
- }
- if (export_settings.include_children) {
- includeFilter |= OB_REL_CHILDREN_RECURSIVE;
- }
-
export_count = collada_export(C, &export_settings);
if (export_count == 0) {
diff --git a/source/blender/editors/io/io_obj.c b/source/blender/editors/io/io_obj.c
index f253f63946b..df15191916a 100644
--- a/source/blender/editors/io/io_obj.c
+++ b/source/blender/editors/io/io_obj.c
@@ -293,11 +293,8 @@ void WM_OT_obj_export(struct wmOperatorType *ot)
0.01,
1000.0f);
/* File Writer options. */
- RNA_def_boolean(ot->srna,
- "apply_modifiers",
- true,
- "Apply Modifiers",
- "Apply modifiers to exported meshes");
+ RNA_def_boolean(
+ ot->srna, "apply_modifiers", true, "Apply Modifiers", "Apply modifiers to exported meshes");
RNA_def_enum(ot->srna,
"export_eval_mode",
io_obj_export_evaluation_mode,
@@ -345,7 +342,7 @@ void WM_OT_obj_export(struct wmOperatorType *ot)
"export_material_groups",
false,
"Export Material Groups",
- "Append mesh name and material name to object name, separated by a '_'");
+ "Generate an OBJ group for each part of a geometry using a different material");
RNA_def_boolean(
ot->srna,
"export_vertex_groups",
diff --git a/source/blender/editors/lattice/editlattice_select.c b/source/blender/editors/lattice/editlattice_select.c
index d1635078126..ea99c6e23ff 100644
--- a/source/blender/editors/lattice/editlattice_select.c
+++ b/source/blender/editors/lattice/editlattice_select.c
@@ -556,15 +556,18 @@ void LATTICE_OT_select_ungrouped(wmOperatorType *ot)
* Gets called via generic mouse select operator.
* \{ */
-static void findnearestLattvert__doClosest(void *userData, BPoint *bp, const float screen_co[2])
+struct NearestLatticeVert_UserData {
+ BPoint *bp;
+ float dist;
+ /** When true, the existing selection gets a disadvantage. */
+ bool select;
+ float mval_fl[2];
+ bool is_changed;
+};
+
+static void findnearestLattvert__doClosest(void *user_data, BPoint *bp, const float screen_co[2])
{
- struct {
- BPoint *bp;
- float dist;
- int select;
- float mval_fl[2];
- bool is_changed;
- } *data = userData;
+ struct NearestLatticeVert_UserData *data = user_data;
float dist_test = len_manhattan_v2v2(data->mval_fl, screen_co);
if ((bp->f1 & SELECT) && data->select) {
@@ -578,21 +581,12 @@ static void findnearestLattvert__doClosest(void *userData, BPoint *bp, const flo
}
}
-static BPoint *findnearestLattvert(ViewContext *vc, int sel, Base **r_base)
+static BPoint *findnearestLattvert(ViewContext *vc, bool select, Base **r_base)
{
- /* (sel == 1): selected gets a disadvantage */
- /* in nurb and bezt or bp the nearest is written */
- /* return 0 1 2: handlepunt */
- struct {
- BPoint *bp;
- float dist;
- int select;
- float mval_fl[2];
- bool is_changed;
- } data = {NULL};
+ struct NearestLatticeVert_UserData data = {NULL};
data.dist = ED_view3d_select_dist_px();
- data.select = sel;
+ data.select = select;
data.mval_fl[0] = vc->mval[0];
data.mval_fl[1] = vc->mval[1];
@@ -616,24 +610,27 @@ static BPoint *findnearestLattvert(ViewContext *vc, int sel, Base **r_base)
return data.bp;
}
-bool ED_lattice_select_pick(
- bContext *C, const int mval[2], bool extend, bool deselect, bool toggle)
+bool ED_lattice_select_pick(bContext *C, const int mval[2], const struct SelectPick_Params *params)
{
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
ViewContext vc;
BPoint *bp = NULL;
Base *basact = NULL;
+ bool changed = false;
ED_view3d_viewcontext_init(C, &vc, depsgraph);
vc.mval[0] = mval[0];
vc.mval[1] = mval[1];
bp = findnearestLattvert(&vc, true, &basact);
- if (bp) {
- ED_view3d_viewcontext_init_object(&vc, basact->object);
- Lattice *lt = ((Lattice *)vc.obedit->data)->editlatt->latt;
+ bool found = (bp != NULL);
- if (!extend && !deselect && !toggle) {
+ if (params->sel_op == SEL_OP_SET) {
+ if ((found && params->select_passthrough) && (bp->f1 & SELECT)) {
+ found = false;
+ }
+ else if (found || params->deselect_all) {
+ /* Deselect everything. */
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
vc.view_layer, vc.v3d, &objects_len);
@@ -645,20 +642,36 @@ bool ED_lattice_select_pick(
}
}
MEM_freeN(objects);
+ changed = true;
}
+ }
- if (extend) {
- bp->f1 |= SELECT;
- }
- else if (deselect) {
- bp->f1 &= ~SELECT;
- }
- else if (toggle) {
- bp->f1 ^= SELECT; /* swap */
- }
- else {
- ED_lattice_flags_set(vc.obedit, 0);
- bp->f1 |= SELECT;
+ if (found) {
+ ED_view3d_viewcontext_init_object(&vc, basact->object);
+ Lattice *lt = ((Lattice *)vc.obedit->data)->editlatt->latt;
+
+ switch (params->sel_op) {
+ case SEL_OP_ADD: {
+ bp->f1 |= SELECT;
+ break;
+ }
+ case SEL_OP_SUB: {
+ bp->f1 &= ~SELECT;
+ break;
+ }
+ case SEL_OP_XOR: {
+ bp->f1 ^= SELECT; /* swap */
+ break;
+ }
+ case SEL_OP_SET: {
+ ED_lattice_flags_set(vc.obedit, 0);
+ bp->f1 |= SELECT;
+ break;
+ }
+ case SEL_OP_AND: {
+ BLI_assert_unreachable(); /* Doesn't make sense for picking. */
+ break;
+ }
}
if (bp->f1 & SELECT) {
@@ -675,10 +688,10 @@ bool ED_lattice_select_pick(
DEG_id_tag_update(vc.obedit->data, ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_GEOM | ND_SELECT, vc.obedit->data);
- return true;
+ changed = true;
}
- return false;
+ return changed || found;
}
/** \} */
diff --git a/source/blender/editors/mask/mask_draw.c b/source/blender/editors/mask/mask_draw.c
index 8fe3b74f9c9..60232dee109 100644
--- a/source/blender/editors/mask/mask_draw.c
+++ b/source/blender/editors/mask/mask_draw.c
@@ -638,21 +638,6 @@ static void draw_mask_layers(const bContext *C,
GPU_blend(GPU_BLEND_NONE);
}
-void ED_mask_draw(const bContext *C, const char draw_flag, const char draw_type)
-{
- ScrArea *area = CTX_wm_area(C);
- Mask *mask = CTX_data_edit_mask(C);
- int width, height;
-
- if (!mask) {
- return;
- }
-
- ED_mask_get_size(area, &width, &height);
-
- draw_mask_layers(C, mask, draw_flag, draw_type, width, height);
-}
-
static float *mask_rasterize(Mask *mask, const int width, const int height)
{
MaskRasterHandle *handle;
diff --git a/source/blender/editors/mesh/editface.c b/source/blender/editors/mesh/editface.c
index 8a1e12c76c5..a5c6adaa43e 100644
--- a/source/blender/editors/mesh/editface.c
+++ b/source/blender/editors/mesh/editface.c
@@ -366,59 +366,77 @@ bool paintface_minmax(Object *ob, float r_min[3], float r_max[3])
return ok;
}
-bool paintface_mouse_select(
- struct bContext *C, Object *ob, const int mval[2], bool extend, bool deselect, bool toggle)
+bool paintface_mouse_select(struct bContext *C,
+ const int mval[2],
+ const struct SelectPick_Params *params,
+ Object *ob)
{
Mesh *me;
- MPoly *mpoly_sel;
+ MPoly *mpoly_sel = NULL;
uint index;
+ bool changed = false;
+ bool found = false;
/* Get the face under the cursor */
me = BKE_mesh_from_object(ob);
- if (!ED_mesh_pick_face(C, ob, mval, ED_MESH_PICK_DEFAULT_FACE_DIST, &index)) {
- return false;
- }
-
- if (index >= me->totpoly) {
- return false;
- }
-
- mpoly_sel = me->mpoly + index;
- if (mpoly_sel->flag & ME_HIDE) {
- return false;
+ if (ED_mesh_pick_face(C, ob, mval, ED_MESH_PICK_DEFAULT_FACE_DIST, &index)) {
+ if (index < me->totpoly) {
+ mpoly_sel = me->mpoly + index;
+ if ((mpoly_sel->flag & ME_HIDE) == 0) {
+ found = true;
+ }
+ }
}
- /* clear flags */
- if (!extend && !deselect && !toggle) {
- paintface_deselect_all_visible(C, ob, SEL_DESELECT, false);
+ if (params->sel_op == SEL_OP_SET) {
+ if ((found && params->select_passthrough) && (mpoly_sel->flag & ME_FACE_SEL)) {
+ found = false;
+ }
+ else if (found || params->deselect_all) {
+ /* Deselect everything. */
+ changed |= paintface_deselect_all_visible(C, ob, SEL_DESELECT, false);
+ }
}
- me->act_face = (int)index;
+ if (found) {
+ me->act_face = (int)index;
- if (extend) {
- mpoly_sel->flag |= ME_FACE_SEL;
- }
- else if (deselect) {
- mpoly_sel->flag &= ~ME_FACE_SEL;
- }
- else if (toggle) {
- if (mpoly_sel->flag & ME_FACE_SEL) {
- mpoly_sel->flag &= ~ME_FACE_SEL;
- }
- else {
- mpoly_sel->flag |= ME_FACE_SEL;
+ switch (params->sel_op) {
+ case SEL_OP_ADD: {
+ mpoly_sel->flag |= ME_FACE_SEL;
+ break;
+ }
+ case SEL_OP_SUB: {
+ mpoly_sel->flag &= ~ME_FACE_SEL;
+ break;
+ }
+ case SEL_OP_XOR: {
+ if (mpoly_sel->flag & ME_FACE_SEL) {
+ mpoly_sel->flag &= ~ME_FACE_SEL;
+ }
+ else {
+ mpoly_sel->flag |= ME_FACE_SEL;
+ }
+ break;
+ }
+ case SEL_OP_SET: {
+ mpoly_sel->flag |= ME_FACE_SEL;
+ break;
+ }
+ case SEL_OP_AND: {
+ BLI_assert_unreachable(); /* Doesn't make sense for picking. */
+ break;
+ }
}
- }
- else {
- mpoly_sel->flag |= ME_FACE_SEL;
- }
- /* image window redraw */
+ /* image window redraw */
- paintface_flush_flags(C, ob, SELECT);
- ED_region_tag_redraw(CTX_wm_region(C)); /* XXX: should redraw all 3D views. */
- return true;
+ paintface_flush_flags(C, ob, SELECT);
+ ED_region_tag_redraw(CTX_wm_region(C)); /* XXX: should redraw all 3D views. */
+ changed = true;
+ }
+ return changed || found;
}
void paintvert_flush_flags(Object *ob)
diff --git a/source/blender/editors/mesh/editmesh_mask_extract.c b/source/blender/editors/mesh/editmesh_mask_extract.c
index faf449a77aa..d3fc83eedd7 100644
--- a/source/blender/editors/mesh/editmesh_mask_extract.c
+++ b/source/blender/editors/mesh/editmesh_mask_extract.c
@@ -108,6 +108,7 @@ static int geometry_extract_apply(bContext *C,
new_mesh,
(&(struct BMeshFromMeshParams){
.calc_face_normal = true,
+ .calc_vert_normal = true,
}));
BMEditMesh *em = BKE_editmesh_create(bm);
diff --git a/source/blender/editors/mesh/editmesh_path.c b/source/blender/editors/mesh/editmesh_path.c
index dbda9fa7746..f2e7150e791 100644
--- a/source/blender/editors/mesh/editmesh_path.c
+++ b/source/blender/editors/mesh/editmesh_path.c
@@ -28,6 +28,7 @@
#include "ED_mesh.h"
#include "ED_object.h"
#include "ED_screen.h"
+#include "ED_select_utils.h"
#include "ED_uvedit.h"
#include "ED_view3d.h"
@@ -700,7 +701,10 @@ static int edbm_shortest_path_pick_invoke(bContext *C, wmOperator *op, const wmE
/* TODO(dfelinto): right now we try to find the closest element twice.
* The ideal is to refactor EDBM_select_pick so it doesn't
* have to pick the nearest vert/edge/face again. */
- EDBM_select_pick(C, event->mval, true, false, false);
+ const struct SelectPick_Params params = {
+ .sel_op = SEL_OP_ADD,
+ };
+ EDBM_select_pick(C, event->mval, &params);
return OPERATOR_FINISHED;
}
diff --git a/source/blender/editors/mesh/editmesh_rip_edge.c b/source/blender/editors/mesh/editmesh_rip_edge.c
index 6b3a796ab0d..85426acb905 100644
--- a/source/blender/editors/mesh/editmesh_rip_edge.c
+++ b/source/blender/editors/mesh/editmesh_rip_edge.c
@@ -125,7 +125,7 @@ static int edbm_rip_edge_invoke(bContext *C, wmOperator *UNUSED(op), const wmEve
#ifdef USE_TRICKY_EXTEND
/* first check if we can select the edge to split based on selection-only */
- int tot_sel = 0, tot = 0;
+ int tot_sel = 0;
BM_ITER_ELEM (e, &eiter, v, BM_EDGES_OF_VERT) {
if (!BM_elem_flag_test(e, BM_ELEM_HIDDEN)) {
@@ -133,7 +133,6 @@ static int edbm_rip_edge_invoke(bContext *C, wmOperator *UNUSED(op), const wmEve
e_best = e;
tot_sel += 1;
}
- tot += 1;
}
}
if (tot_sel != 1) {
diff --git a/source/blender/editors/mesh/editmesh_select.c b/source/blender/editors/mesh/editmesh_select.c
index 8784b1a90d9..9c8c5c45cb7 100644
--- a/source/blender/editors/mesh/editmesh_select.c
+++ b/source/blender/editors/mesh/editmesh_select.c
@@ -2016,7 +2016,7 @@ void MESH_OT_select_interior_faces(wmOperatorType *ot)
* Gets called via generic mouse select operator.
* \{ */
-bool EDBM_select_pick(bContext *C, const int mval[2], bool extend, bool deselect, bool toggle)
+bool EDBM_select_pick(bContext *C, const int mval[2], const struct SelectPick_Params *params)
{
ViewContext vc;
@@ -2033,100 +2033,153 @@ bool EDBM_select_pick(bContext *C, const int mval[2], bool extend, bool deselect
uint bases_len = 0;
Base **bases = BKE_view_layer_array_from_bases_in_edit_mode(vc.view_layer, vc.v3d, &bases_len);
- bool ok = false;
-
- if (unified_findnearest(&vc, bases, bases_len, &base_index_active, &eve, &eed, &efa)) {
- Base *basact = bases[base_index_active];
- ED_view3d_viewcontext_init_object(&vc, basact->object);
+ bool changed = false;
+ bool found = unified_findnearest(&vc, bases, bases_len, &base_index_active, &eve, &eed, &efa);
- /* Deselect everything */
- if (extend == false && deselect == false && toggle == false) {
+ if (params->sel_op == SEL_OP_SET) {
+ BMElem *ele = efa ? (BMElem *)efa : (eed ? (BMElem *)eed : (BMElem *)eve);
+ if ((found && params->select_passthrough) && BM_elem_flag_test(ele, BM_ELEM_SELECT)) {
+ found = false;
+ }
+ else if (found || params->deselect_all) {
+ /* Deselect everything. */
for (uint base_index = 0; base_index < bases_len; base_index++) {
Base *base_iter = bases[base_index];
Object *ob_iter = base_iter->object;
EDBM_flag_disable_all(BKE_editmesh_from_object(ob_iter), BM_ELEM_SELECT);
- if (basact->object != ob_iter) {
- DEG_id_tag_update(ob_iter->data, ID_RECALC_SELECT);
- WM_event_add_notifier(C, NC_GEOM | ND_SELECT, ob_iter->data);
- }
+ DEG_id_tag_update(ob_iter->data, ID_RECALC_SELECT);
+ WM_event_add_notifier(C, NC_GEOM | ND_SELECT, ob_iter->data);
}
+ changed = true;
}
+ }
+
+ if (found) {
+ Base *basact = bases[base_index_active];
+ ED_view3d_viewcontext_init_object(&vc, basact->object);
if (efa) {
- if (extend) {
- /* set the last selected face */
- BM_mesh_active_face_set(vc.em->bm, efa);
-
- /* Work-around: deselect first, so we can guarantee it will */
- /* be active even if it was already selected */
- BM_select_history_remove(vc.em->bm, efa);
- BM_face_select_set(vc.em->bm, efa, false);
- BM_select_history_store(vc.em->bm, efa);
- BM_face_select_set(vc.em->bm, efa, true);
- }
- else if (deselect) {
- BM_select_history_remove(vc.em->bm, efa);
- BM_face_select_set(vc.em->bm, efa, false);
- }
- else {
- /* set the last selected face */
- BM_mesh_active_face_set(vc.em->bm, efa);
+ switch (params->sel_op) {
+ case SEL_OP_ADD: {
+ BM_mesh_active_face_set(vc.em->bm, efa);
- if (!BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
+ /* Work-around: deselect first, so we can guarantee it will
+ * be active even if it was already selected. */
+ BM_select_history_remove(vc.em->bm, efa);
+ BM_face_select_set(vc.em->bm, efa, false);
BM_select_history_store(vc.em->bm, efa);
BM_face_select_set(vc.em->bm, efa, true);
+ break;
}
- else if (toggle) {
+ case SEL_OP_SUB: {
BM_select_history_remove(vc.em->bm, efa);
BM_face_select_set(vc.em->bm, efa, false);
+ break;
+ }
+ case SEL_OP_XOR: {
+ BM_mesh_active_face_set(vc.em->bm, efa);
+ if (!BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
+ BM_select_history_store(vc.em->bm, efa);
+ BM_face_select_set(vc.em->bm, efa, true);
+ }
+ else {
+ BM_select_history_remove(vc.em->bm, efa);
+ BM_face_select_set(vc.em->bm, efa, false);
+ }
+ break;
+ }
+ case SEL_OP_SET: {
+ BM_mesh_active_face_set(vc.em->bm, efa);
+ if (!BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
+ BM_select_history_store(vc.em->bm, efa);
+ BM_face_select_set(vc.em->bm, efa, true);
+ }
+ break;
+ }
+ case SEL_OP_AND: {
+ BLI_assert_unreachable(); /* Doesn't make sense for picking. */
+ break;
}
}
}
else if (eed) {
- if (extend) {
- /* Work-around: deselect first, so we can guarantee it will */
- /* be active even if it was already selected */
- BM_select_history_remove(vc.em->bm, eed);
- BM_edge_select_set(vc.em->bm, eed, false);
- BM_select_history_store(vc.em->bm, eed);
- BM_edge_select_set(vc.em->bm, eed, true);
- }
- else if (deselect) {
- BM_select_history_remove(vc.em->bm, eed);
- BM_edge_select_set(vc.em->bm, eed, false);
- }
- else {
- if (!BM_elem_flag_test(eed, BM_ELEM_SELECT)) {
+
+ switch (params->sel_op) {
+ case SEL_OP_ADD: {
+ /* Work-around: deselect first, so we can guarantee it will
+ * be active even if it was already selected. */
+ BM_select_history_remove(vc.em->bm, eed);
+ BM_edge_select_set(vc.em->bm, eed, false);
BM_select_history_store(vc.em->bm, eed);
BM_edge_select_set(vc.em->bm, eed, true);
+ break;
}
- else if (toggle) {
+ case SEL_OP_SUB: {
BM_select_history_remove(vc.em->bm, eed);
BM_edge_select_set(vc.em->bm, eed, false);
+ break;
+ }
+ case SEL_OP_XOR: {
+ if (!BM_elem_flag_test(eed, BM_ELEM_SELECT)) {
+ BM_select_history_store(vc.em->bm, eed);
+ BM_edge_select_set(vc.em->bm, eed, true);
+ }
+ else {
+ BM_select_history_remove(vc.em->bm, eed);
+ BM_edge_select_set(vc.em->bm, eed, false);
+ }
+ break;
+ }
+ case SEL_OP_SET: {
+ if (!BM_elem_flag_test(eed, BM_ELEM_SELECT)) {
+ BM_select_history_store(vc.em->bm, eed);
+ BM_edge_select_set(vc.em->bm, eed, true);
+ }
+ break;
+ }
+ case SEL_OP_AND: {
+ BLI_assert_unreachable(); /* Doesn't make sense for picking. */
+ break;
}
}
}
else if (eve) {
- if (extend) {
- /* Work-around: deselect first, so we can guarantee it will */
- /* be active even if it was already selected */
- BM_select_history_remove(vc.em->bm, eve);
- BM_vert_select_set(vc.em->bm, eve, false);
- BM_select_history_store(vc.em->bm, eve);
- BM_vert_select_set(vc.em->bm, eve, true);
- }
- else if (deselect) {
- BM_select_history_remove(vc.em->bm, eve);
- BM_vert_select_set(vc.em->bm, eve, false);
- }
- else {
- if (!BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
+ switch (params->sel_op) {
+ case SEL_OP_ADD: {
+ /* Work-around: deselect first, so we can guarantee it will
+ * be active even if it was already selected. */
+ BM_select_history_remove(vc.em->bm, eve);
+ BM_vert_select_set(vc.em->bm, eve, false);
BM_select_history_store(vc.em->bm, eve);
BM_vert_select_set(vc.em->bm, eve, true);
+ break;
}
- else if (toggle) {
+ case SEL_OP_SUB: {
BM_select_history_remove(vc.em->bm, eve);
BM_vert_select_set(vc.em->bm, eve, false);
+ break;
+ }
+ case SEL_OP_XOR: {
+ if (!BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
+ BM_select_history_store(vc.em->bm, eve);
+ BM_vert_select_set(vc.em->bm, eve, true);
+ }
+ else {
+ BM_select_history_remove(vc.em->bm, eve);
+ BM_vert_select_set(vc.em->bm, eve, false);
+ }
+ break;
+ }
+ case SEL_OP_SET: {
+ if (!BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
+ BM_select_history_store(vc.em->bm, eve);
+ BM_vert_select_set(vc.em->bm, eve, true);
+ }
+ break;
+ }
+ case SEL_OP_AND: {
+ BLI_assert_unreachable(); /* Doesn't make sense for picking. */
+ break;
}
}
}
@@ -2168,12 +2221,12 @@ bool EDBM_select_pick(bContext *C, const int mval[2], bool extend, bool deselect
DEG_id_tag_update(vc.obedit->data, ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_GEOM | ND_SELECT, vc.obedit->data);
- ok = true;
+ changed = true;
}
MEM_freeN(bases);
- return ok;
+ return changed;
}
/** \} */
diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c
index 54cc3efe986..2181b652d16 100644
--- a/source/blender/editors/mesh/editmesh_tools.c
+++ b/source/blender/editors/mesh/editmesh_tools.c
@@ -57,6 +57,7 @@
#include "ED_object.h"
#include "ED_outliner.h"
#include "ED_screen.h"
+#include "ED_select_utils.h"
#include "ED_transform.h"
#include "ED_uvedit.h"
#include "ED_view3d.h"
@@ -2301,7 +2302,7 @@ static int edbm_edge_rotate_selected_exec(bContext *C, wmOperator *op)
BMIter iter;
const bool use_ccw = RNA_boolean_get(op->ptr, "use_ccw");
- int tot_rotate_all = 0, tot_failed_all = 0;
+ int tot_failed_all = 0;
bool no_selected_edges = true, invalid_selected_edges = true;
ViewLayer *view_layer = CTX_data_view_layer(C);
@@ -2359,7 +2360,6 @@ static int edbm_edge_rotate_selected_exec(bContext *C, wmOperator *op)
const int tot_rotate = BMO_slot_buffer_len(bmop.slots_out, "edges.out");
const int tot_failed = tot - tot_rotate;
- tot_rotate_all += tot_rotate;
tot_failed_all += tot_failed;
if (tot_failed != 0) {
@@ -8540,7 +8540,10 @@ static int edbm_point_normals_modal(bContext *C, wmOperator *op, const wmEvent *
case EDBM_CLNOR_MODAL_POINTTO_SET_USE_SELECTED:
new_mode = EDBM_CLNOR_POINTTO_MODE_COORDINATES;
view3d_operator_needs_opengl(C);
- if (EDBM_select_pick(C, event->mval, false, false, false)) {
+ const struct SelectPick_Params params = {
+ .sel_op = SEL_OP_SET,
+ };
+ if (EDBM_select_pick(C, event->mval, &params)) {
/* Point to newly selected active. */
ED_object_calc_active_center_for_editmode(obedit, false, target);
diff --git a/source/blender/editors/mesh/editmesh_undo.c b/source/blender/editors/mesh/editmesh_undo.c
index 9c7d712a739..ecc5f8f8ef5 100644
--- a/source/blender/editors/mesh/editmesh_undo.c
+++ b/source/blender/editors/mesh/editmesh_undo.c
@@ -673,6 +673,7 @@ static void undomesh_to_editmesh(UndoMesh *um, Object *ob, BMEditMesh *em)
(&(struct BMeshFromMeshParams){
/* Handled with tessellation. */
.calc_face_normal = false,
+ .calc_vert_normal = false,
.active_shapekey = um->shapenr,
}));
diff --git a/source/blender/editors/metaball/mball_edit.c b/source/blender/editors/metaball/mball_edit.c
index 136d0d46c68..06a649e5b6c 100644
--- a/source/blender/editors/metaball/mball_edit.c
+++ b/source/blender/editors/metaball/mball_edit.c
@@ -736,14 +736,43 @@ void MBALL_OT_reveal_metaelems(wmOperatorType *ot)
/** \name Select Pick Utility
* \{ */
-bool ED_mball_select_pick(bContext *C, const int mval[2], bool extend, bool deselect, bool toggle)
+Base *ED_mball_base_and_elem_from_select_buffer(Base **bases,
+ uint bases_len,
+ const uint select_id,
+ MetaElem **r_ml)
+{
+ const uint hit_object = select_id & 0xFFFF;
+ Base *base = NULL;
+ MetaElem *ml = NULL;
+ /* TODO(campbell): optimize, eg: sort & binary search. */
+ for (uint base_index = 0; base_index < bases_len; base_index++) {
+ if (bases[base_index]->object->runtime.select_id == hit_object) {
+ base = bases[base_index];
+ break;
+ }
+ }
+ if (base != NULL) {
+ const uint hit_elem = (select_id & ~MBALLSEL_ANY) >> 16;
+ MetaBall *mb = base->object->data;
+ ml = BLI_findlink(mb->editelems, hit_elem);
+ }
+ *r_ml = ml;
+ return base;
+}
+
+static bool ed_mball_findnearest_metaelem(bContext *C,
+ const int mval[2],
+ bool use_cycle,
+ Base **r_base,
+ MetaElem **r_ml,
+ uint *r_selmask)
{
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
- static MetaElem *startelem = NULL;
ViewContext vc;
int a, hits;
GPUSelectResult buffer[MAXPICKELEMS];
rcti rect;
+ bool found = false;
ED_view3d_viewcontext_init(C, &vc, depsgraph);
@@ -753,131 +782,140 @@ bool ED_mball_select_pick(bContext *C, const int mval[2], bool extend, bool dese
buffer,
ARRAY_SIZE(buffer),
&rect,
- VIEW3D_SELECT_PICK_NEAREST,
+ use_cycle ? VIEW3D_SELECT_PICK_ALL : VIEW3D_SELECT_PICK_NEAREST,
VIEW3D_SELECT_FILTER_NOP);
- FOREACH_BASE_IN_EDIT_MODE_BEGIN (vc.view_layer, vc.v3d, base) {
- ED_view3d_viewcontext_init_object(&vc, base->object);
- MetaBall *mb = (MetaBall *)base->object->data;
- MetaElem *ml, *ml_act = NULL;
+ if (hits == 0) {
+ return false;
+ }
- /* does startelem exist? */
- ml = mb->editelems->first;
- while (ml) {
- if (ml == startelem) {
- break;
+ uint bases_len = 0;
+ Base **bases = BKE_view_layer_array_from_bases_in_edit_mode(vc.view_layer, vc.v3d, &bases_len);
+
+ int hit_cycle_offset = 0;
+ if (use_cycle) {
+ /* When cycling, use the hit directly after the current active meta-element (when set). */
+ const int base_index = vc.obact->runtime.select_id;
+ MetaBall *mb = (MetaBall *)vc.obact->data;
+ MetaElem *ml = mb->lastelem;
+ if (ml && (ml->flag & SELECT)) {
+ const int ml_index = BLI_findindex(mb->editelems, ml);
+ BLI_assert(ml_index != -1);
+
+ /* Count backwards in case the active meta-element has multiple entries,
+ * ensure this steps onto the next meta-element. */
+ a = hits;
+ while (a--) {
+ const int select_id = buffer[a].id;
+ if (select_id == -1) {
+ continue;
+ }
+
+ if (((select_id & 0xFFFF) == base_index) &&
+ ((select_id & ~MBALLSEL_ANY) >> 16 == ml_index)) {
+ hit_cycle_offset = a + 1;
+ break;
+ }
}
- ml = ml->next;
}
+ }
- if (ml == NULL) {
- startelem = mb->editelems->first;
+ for (a = 0; a < hits; a++) {
+ const int index = (hit_cycle_offset == 0) ? a : ((a + hit_cycle_offset) % hits);
+ const uint select_id = buffer[index].id;
+ if (select_id == -1) {
+ continue;
}
- if (hits > 0) {
- int metaelem_id = 0;
- ml = startelem;
- while (ml) {
- for (a = 0; a < hits; a++) {
- const int hitresult = buffer[a].id;
- if (hitresult == -1) {
- continue;
- }
+ MetaElem *ml;
+ Base *base = ED_mball_base_and_elem_from_select_buffer(bases, bases_len, select_id, &ml);
+ if (ml == NULL) {
+ continue;
+ }
+ *r_base = base;
+ *r_ml = ml;
+ *r_selmask = select_id & MBALLSEL_ANY;
+ found = true;
+ break;
+ }
- const uint hit_object = hitresult & 0xFFFF;
- if (vc.obedit->runtime.select_id != hit_object) {
- continue;
- }
+ MEM_freeN(bases);
- if (metaelem_id != (hitresult & 0xFFFF0000 & ~MBALLSEL_ANY)) {
- continue;
- }
+ return found;
+}
- if (hitresult & MBALLSEL_RADIUS) {
- ml->flag |= MB_SCALE_RAD;
- ml_act = ml;
- break;
- }
+bool ED_mball_select_pick(bContext *C, const int mval[2], const struct SelectPick_Params *params)
+{
+ Base *base = NULL;
+ MetaElem *ml = NULL;
+ uint selmask = 0;
- if (hitresult & MBALLSEL_STIFF) {
- ml->flag &= ~MB_SCALE_RAD;
- ml_act = ml;
- break;
- }
- }
+ bool changed = false;
- if (ml_act) {
- break;
- }
- ml = ml->next;
- if (ml == NULL) {
- ml = mb->editelems->first;
- }
- if (ml == startelem) {
- break;
- }
+ bool found = ed_mball_findnearest_metaelem(C, mval, true, &base, &ml, &selmask);
- metaelem_id += 0x10000;
- }
+ if (params->sel_op == SEL_OP_SET) {
+ if ((found && params->select_passthrough) && (ml->flag & SELECT)) {
+ found = false;
+ }
+ else if (found || params->deselect_all) {
+ /* Deselect everything. */
+ changed |= ED_mball_deselect_all_multi(C);
+ }
+ }
- /* When some metaelem was found, then it is necessary to select or deselect it. */
- if (ml_act) {
- if (!extend && !deselect && !toggle) {
- uint objects_len;
- Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- vc.view_layer, vc.v3d, &objects_len);
- for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
- Object *ob_iter = objects[ob_index];
-
- if (ob_iter == base->object) {
- continue;
- }
-
- BKE_mball_deselect_all((MetaBall *)ob_iter->data);
- DEG_id_tag_update(ob_iter->data, ID_RECALC_SELECT);
- WM_event_add_notifier(C, NC_GEOM | ND_SELECT, ob_iter->data);
- }
- MEM_freeN(objects);
- }
+ if (found) {
+ if (selmask & MBALLSEL_RADIUS) {
+ ml->flag |= MB_SCALE_RAD;
+ }
+ else if (selmask & MBALLSEL_STIFF) {
+ ml->flag &= ~MB_SCALE_RAD;
+ }
- if (extend) {
- ml_act->flag |= SELECT;
- }
- else if (deselect) {
- ml_act->flag &= ~SELECT;
- }
- else if (toggle) {
- if (ml_act->flag & SELECT) {
- ml_act->flag &= ~SELECT;
- }
- else {
- ml_act->flag |= SELECT;
- }
+ switch (params->sel_op) {
+ case SEL_OP_ADD: {
+ ml->flag |= SELECT;
+ break;
+ }
+ case SEL_OP_SUB: {
+ ml->flag &= ~SELECT;
+ break;
+ }
+ case SEL_OP_XOR: {
+ if (ml->flag & SELECT) {
+ ml->flag &= ~SELECT;
}
else {
- /* Deselect all existing metaelems */
- BKE_mball_deselect_all(mb);
-
- /* Select only metaelem clicked on */
- ml_act->flag |= SELECT;
+ ml->flag |= SELECT;
}
+ break;
+ }
+ case SEL_OP_SET: {
+ /* Deselect has already been performed. */
+ ml->flag |= SELECT;
+ break;
+ }
+ case SEL_OP_AND: {
+ BLI_assert_unreachable(); /* Doesn't make sense for picking. */
+ break;
+ }
+ }
- mb->lastelem = ml_act;
-
- DEG_id_tag_update(&mb->id, ID_RECALC_SELECT);
- WM_event_add_notifier(C, NC_GEOM | ND_SELECT, mb);
+ ViewLayer *view_layer = CTX_data_view_layer(C);
+ MetaBall *mb = (MetaBall *)base->object->data;
+ mb->lastelem = ml;
- if (vc.view_layer->basact != base) {
- ED_object_base_activate(C, base);
- }
+ DEG_id_tag_update(&mb->id, ID_RECALC_SELECT);
+ WM_event_add_notifier(C, NC_GEOM | ND_SELECT, mb);
- return true;
- }
+ if (view_layer->basact != base) {
+ ED_object_base_activate(C, base);
}
+
+ changed = true;
}
- FOREACH_BASE_IN_EDIT_MODE_END;
- return false;
+ return changed || found;
}
/** \} */
diff --git a/source/blender/editors/object/CMakeLists.txt b/source/blender/editors/object/CMakeLists.txt
index 6f8763fa2bb..02feccc211a 100644
--- a/source/blender/editors/object/CMakeLists.txt
+++ b/source/blender/editors/object/CMakeLists.txt
@@ -51,7 +51,7 @@ set(SRC
object_select.c
object_shader_fx.c
object_shapekey.c
- object_transform.c
+ object_transform.cc
object_utils.c
object_vgroup.c
object_volume.c
diff --git a/source/blender/editors/object/object_bake_api.c b/source/blender/editors/object/object_bake_api.c
index 3529574a3a4..efcb47f76ab 100644
--- a/source/blender/editors/object/object_bake_api.c
+++ b/source/blender/editors/object/object_bake_api.c
@@ -23,6 +23,7 @@
#include "BKE_context.h"
#include "BKE_global.h"
#include "BKE_image.h"
+#include "BKE_image_format.h"
#include "BKE_layer.h"
#include "BKE_lib_id.h"
#include "BKE_main.h"
diff --git a/source/blender/editors/object/object_gpencil_modifier.c b/source/blender/editors/object/object_gpencil_modifier.c
index 9b62823ea8a..d0a6a5d44c0 100644
--- a/source/blender/editors/object/object_gpencil_modifier.c
+++ b/source/blender/editors/object/object_gpencil_modifier.c
@@ -403,7 +403,7 @@ void OBJECT_OT_gpencil_modifier_add(wmOperatorType *ot)
/* properties */
prop = RNA_def_enum(ot->srna,
"type",
- rna_enum_object_modifier_type_items,
+ rna_enum_object_greasepencil_modifier_type_items,
eGpencilModifierType_Thick,
"Type",
"");
diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h
index 60a49fa6945..490c495dad5 100644
--- a/source/blender/editors/object/object_intern.h
+++ b/source/blender/editors/object/object_intern.h
@@ -28,7 +28,7 @@ enum eObject_Hook_Add_Mode {
/* internal exports only */
-/* object_transform.c */
+/* object_transform.cc */
void OBJECT_OT_location_clear(struct wmOperatorType *ot);
void OBJECT_OT_rotation_clear(struct wmOperatorType *ot);
diff --git a/source/blender/editors/object/object_modifier.c b/source/blender/editors/object/object_modifier.c
index b0c8646dd04..7fedc2c6265 100644
--- a/source/blender/editors/object/object_modifier.c
+++ b/source/blender/editors/object/object_modifier.c
@@ -2219,7 +2219,7 @@ static int multires_unsubdivide_exec(bContext *C, wmOperator *op)
int new_levels = multiresModifier_rebuild_subdiv(depsgraph, object, mmd, 1, true);
if (new_levels == 0) {
- BKE_report(op->reports, RPT_ERROR, "Not valid subdivisions found to rebuild a lower level");
+ BKE_report(op->reports, RPT_ERROR, "No valid subdivisions found to rebuild a lower level");
return OPERATOR_CANCELLED;
}
diff --git a/source/blender/editors/object/object_transform.c b/source/blender/editors/object/object_transform.cc
index 9e82abf4d07..24425b5a991 100644
--- a/source/blender/editors/object/object_transform.c
+++ b/source/blender/editors/object/object_transform.cc
@@ -5,8 +5,8 @@
* \ingroup edobj
*/
-#include <stdlib.h>
-#include <string.h>
+#include <cstdlib>
+#include <cstring>
#include "DNA_anim_types.h"
#include "DNA_armature_types.h"
@@ -19,10 +19,12 @@
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
-#include "BLI_array.h"
+#include "BLI_array.hh"
#include "BLI_listbase.h"
#include "BLI_math.h"
+#include "BLI_math_vector.hh"
#include "BLI_utildefines.h"
+#include "BLI_vector.hh"
#include "BKE_armature.h"
#include "BKE_context.h"
@@ -64,6 +66,10 @@
#include "object_intern.h"
+using blender::Array;
+using blender::float2;
+using blender::Vector;
+
/* -------------------------------------------------------------------- */
/** \name Clear Transformation Utilities
* \{ */
@@ -283,25 +289,20 @@ static int object_clear_transform_generic_exec(bContext *C,
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- /* May be NULL. */
+ /* May be null. */
View3D *v3d = CTX_wm_view3d(C);
KeyingSet *ks;
const bool clear_delta = RNA_boolean_get(op->ptr, "clear_delta");
- BLI_assert(!ELEM(NULL, clear_func, default_ksName));
+ BLI_assert(!ELEM(nullptr, clear_func, default_ksName));
- Object **objects = NULL;
- uint objects_len = 0;
- {
- BLI_array_declare(objects);
- FOREACH_SELECTED_EDITABLE_OBJECT_BEGIN (view_layer, v3d, ob) {
- BLI_array_append(objects, ob);
- }
- FOREACH_SELECTED_EDITABLE_OBJECT_END;
- objects_len = BLI_array_len(objects);
+ Vector<Object *> objects;
+ FOREACH_SELECTED_EDITABLE_OBJECT_BEGIN (view_layer, v3d, ob) {
+ objects.append(ob);
}
+ FOREACH_SELECTED_EDITABLE_OBJECT_END;
- if (objects == NULL) {
+ if (objects.is_empty()) {
return OPERATOR_CANCELLED;
}
@@ -310,14 +311,14 @@ static int object_clear_transform_generic_exec(bContext *C,
SCE_XFORM_SKIP_CHILDREN);
const bool use_transform_data_origin = (scene->toolsettings->transform_flag &
SCE_XFORM_DATA_ORIGIN);
- struct XFormObjectSkipChild_Container *xcs = NULL;
- struct XFormObjectData_Container *xds = NULL;
+ struct XFormObjectSkipChild_Container *xcs = nullptr;
+ struct XFormObjectData_Container *xds = nullptr;
if (use_transform_skip_children) {
BKE_scene_graph_evaluated_ensure(depsgraph, bmain);
xcs = ED_object_xform_skip_child_container_create();
ED_object_xform_skip_child_container_item_ensure_from_array(
- xcs, view_layer, objects, objects_len);
+ xcs, view_layer, objects.data(), objects.size());
}
if (use_transform_data_origin) {
BKE_scene_graph_evaluated_ensure(depsgraph, bmain);
@@ -327,9 +328,7 @@ static int object_clear_transform_generic_exec(bContext *C,
/* get KeyingSet to use */
ks = ANIM_get_keyingset_for_autokeying(scene, default_ksName);
- for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
- Object *ob = objects[ob_index];
-
+ for (Object *ob : objects) {
if (use_transform_data_origin) {
ED_object_data_xform_container_item_ensure(xds, ob);
}
@@ -342,7 +341,6 @@ static int object_clear_transform_generic_exec(bContext *C,
/* tag for updates */
DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM);
}
- MEM_freeN(objects);
if (use_transform_skip_children) {
ED_object_xform_skip_child_container_update_all(xcs, bmain, depsgraph);
@@ -355,7 +353,7 @@ static int object_clear_transform_generic_exec(bContext *C,
}
/* this is needed so children are also updated */
- WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
+ WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, nullptr);
return OPERATOR_FINISHED;
}
@@ -488,7 +486,7 @@ static int object_origin_clear_exec(bContext *C, wmOperator *UNUSED(op))
}
CTX_DATA_END;
- WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
+ WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, nullptr);
return OPERATOR_FINISHED;
}
@@ -519,12 +517,11 @@ void OBJECT_OT_origin_clear(wmOperatorType *ot)
static void ignore_parent_tx(Main *bmain, Depsgraph *depsgraph, Scene *scene, Object *ob)
{
Object workob;
- Object *ob_child;
Scene *scene_eval = DEG_get_evaluated_scene(depsgraph);
/* a change was made, adjust the children to compensate */
- for (ob_child = bmain->objects.first; ob_child; ob_child = ob_child->id.next) {
+ LISTBASE_FOREACH (Object *, ob_child, &bmain->objects) {
if (ob_child->parent == ob) {
Object *ob_child_eval = DEG_get_evaluated_object(depsgraph, ob_child);
BKE_object_apply_mat4(ob_child_eval, ob_child_eval->obmat, true, false);
@@ -549,7 +546,7 @@ static void append_sorted_object_parent_hierarchy(Object *root_object,
Object **sorted_objects,
int *object_index)
{
- if (!ELEM(object->parent, NULL, root_object)) {
+ if (!ELEM(object->parent, nullptr, root_object)) {
append_sorted_object_parent_hierarchy(
root_object, object->parent, sorted_objects, object_index);
}
@@ -560,7 +557,7 @@ static void append_sorted_object_parent_hierarchy(Object *root_object,
}
}
-static Object **sorted_selected_editable_objects(bContext *C, int *r_num_objects)
+static Array<Object *> sorted_selected_editable_objects(bContext *C)
{
Main *bmain = CTX_data_main(C);
@@ -573,23 +570,20 @@ static Object **sorted_selected_editable_objects(bContext *C, int *r_num_objects
}
CTX_DATA_END;
if (num_objects == 0) {
- *r_num_objects = 0;
- return NULL;
+ return {};
}
/* Append all the objects. */
- Object **sorted_objects = MEM_malloc_arrayN(num_objects, sizeof(Object *), "sorted objects");
+ Array<Object *> sorted_objects(num_objects);
int object_index = 0;
CTX_DATA_BEGIN (C, Object *, object, selected_editable_objects) {
if ((object->id.tag & LIB_TAG_DOIT) == 0) {
continue;
}
- append_sorted_object_parent_hierarchy(object, object, sorted_objects, &object_index);
+ append_sorted_object_parent_hierarchy(object, object, sorted_objects.data(), &object_index);
}
CTX_DATA_END;
- *r_num_objects = num_objects;
-
return sorted_objects;
}
@@ -617,11 +611,11 @@ static int apply_objects_internal(bContext *C,
OB_SURF,
OB_FONT,
OB_GPENCIL)) {
- ID *obdata = ob->data;
+ ID *obdata = static_cast<ID *>(ob->data);
if (ID_REAL_USERS(obdata) > 1) {
BKE_reportf(reports,
RPT_ERROR,
- "Cannot apply to a multi user: Object \"%s\", %s \"%s\", aborting",
+ R"(Cannot apply to a multi user: Object "%s", %s "%s", aborting)",
ob->id.name + 2,
BKE_idtype_idcode_to_name(GS(obdata->name)),
obdata->name + 2);
@@ -631,7 +625,7 @@ static int apply_objects_internal(bContext *C,
if (ID_IS_LINKED(obdata)) {
BKE_reportf(reports,
RPT_ERROR,
- "Cannot apply to library data: Object \"%s\", %s \"%s\", aborting",
+ R"(Cannot apply to library data: Object "%s", %s "%s", aborting)",
ob->id.name + 2,
BKE_idtype_idcode_to_name(GS(obdata->name)),
obdata->name + 2);
@@ -640,16 +634,14 @@ static int apply_objects_internal(bContext *C,
}
if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF)) {
- ID *obdata = ob->data;
- Curve *cu;
-
- cu = ob->data;
+ ID *obdata = static_cast<ID *>(ob->data);
+ Curve *cu = static_cast<Curve *>(ob->data);
if (((ob->type == OB_CURVES_LEGACY) && !(cu->flag & CU_3D)) && (apply_rot || apply_loc)) {
BKE_reportf(
reports,
RPT_ERROR,
- "Rotation/Location can't apply to a 2D curve: Object \"%s\", %s \"%s\", aborting",
+ R"(Rotation/Location can't apply to a 2D curve: Object "%s", %s "%s", aborting)",
ob->id.name + 2,
BKE_idtype_idcode_to_name(GS(obdata->name)),
obdata->name + 2);
@@ -658,7 +650,7 @@ static int apply_objects_internal(bContext *C,
if (cu->key) {
BKE_reportf(reports,
RPT_ERROR,
- "Can't apply to a curve with shape-keys: Object \"%s\", %s \"%s\", aborting",
+ R"(Can't apply to a curve with shape-keys: Object "%s", %s "%s", aborting)",
ob->id.name + 2,
BKE_idtype_idcode_to_name(GS(obdata->name)),
obdata->name + 2);
@@ -675,7 +667,7 @@ static int apply_objects_internal(bContext *C,
}
if (ob->type == OB_GPENCIL) {
- bGPdata *gpd = ob->data;
+ bGPdata *gpd = static_cast<bGPdata *>(ob->data);
if (gpd) {
if (gpd->layers.first) {
/* Unsupported configuration */
@@ -684,7 +676,7 @@ static int apply_objects_internal(bContext *C,
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
/* Parented layers aren't supported as we can't easily re-evaluate
* the scene to sample parent movement */
- if (gpl->parent == NULL) {
+ if (gpl->parent == nullptr) {
has_unparented_layers = true;
break;
}
@@ -706,7 +698,7 @@ static int apply_objects_internal(bContext *C,
BKE_reportf(
reports,
RPT_ERROR,
- "Can't apply to GP data-block with no layers: Object \"%s\", %s \"%s\", aborting",
+ R"(Can't apply to GP data-block with no layers: Object "%s", %s "%s", aborting)",
ob->id.name + 2,
BKE_idtype_idcode_to_name(ID_GD),
gpd->id.name + 2);
@@ -715,7 +707,7 @@ static int apply_objects_internal(bContext *C,
}
if (ob->type == OB_LAMP) {
- Light *la = ob->data;
+ Light *la = static_cast<Light *>(ob->data);
if (la->type == LA_AREA) {
if (apply_rot || apply_loc) {
BKE_reportf(reports,
@@ -736,15 +728,12 @@ static int apply_objects_internal(bContext *C,
changed = false;
/* now execute */
- int num_objects;
- Object **objects = sorted_selected_editable_objects(C, &num_objects);
- if (objects == NULL) {
+ Array<Object *> objects = sorted_selected_editable_objects(C);
+ if (objects.is_empty()) {
return OPERATOR_CANCELLED;
}
- for (int object_index = 0; object_index < num_objects; object_index++) {
- Object *ob = objects[object_index];
-
+ for (Object *ob : objects) {
/* calculate rotation/scale matrix */
if (apply_scale && apply_rot) {
BKE_object_to_mat3(ob, rsmat);
@@ -786,7 +775,7 @@ static int apply_objects_internal(bContext *C,
/* apply to object data */
if (ob->type == OB_MESH) {
- Mesh *me = ob->data;
+ Mesh *me = static_cast<Mesh *>(ob->data);
if (apply_scale) {
multiresModifier_scale_disp(depsgraph, scene, ob);
@@ -799,25 +788,25 @@ static int apply_objects_internal(bContext *C,
BKE_mesh_normals_tag_dirty(me);
}
else if (ob->type == OB_ARMATURE) {
- bArmature *arm = ob->data;
+ bArmature *arm = static_cast<bArmature *>(ob->data);
BKE_armature_transform(arm, mat, do_props);
}
else if (ob->type == OB_LATTICE) {
- Lattice *lt = ob->data;
+ Lattice *lt = static_cast<Lattice *>(ob->data);
BKE_lattice_transform(lt, mat, true);
}
else if (ob->type == OB_MBALL) {
- MetaBall *mb = ob->data;
+ MetaBall *mb = static_cast<MetaBall *>(ob->data);
BKE_mball_transform(mb, mat, do_props);
}
else if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF)) {
- Curve *cu = ob->data;
+ Curve *cu = static_cast<Curve *>(ob->data);
scale = mat3_to_scale(rsmat);
BKE_curve_transform_ex(cu, mat, true, do_props, scale);
}
else if (ob->type == OB_FONT) {
- Curve *cu = ob->data;
+ Curve *cu = static_cast<Curve *>(ob->data);
scale = mat3_to_scale(rsmat);
@@ -834,7 +823,7 @@ static int apply_objects_internal(bContext *C,
}
}
else if (ob->type == OB_GPENCIL) {
- bGPdata *gpd = ob->data;
+ bGPdata *gpd = static_cast<bGPdata *>(ob->data);
BKE_gpencil_transform(gpd, mat);
}
else if (ob->type == OB_CAMERA) {
@@ -870,7 +859,7 @@ static int apply_objects_internal(bContext *C,
}
}
else if (ob->type == OB_LAMP) {
- Light *la = ob->data;
+ Light *la = static_cast<Light *>(ob->data);
if (la->type != LA_AREA) {
continue;
}
@@ -911,7 +900,8 @@ static int apply_objects_internal(bContext *C,
BKE_object_where_is_calc(depsgraph, scene, ob_eval);
if (ob->type == OB_ARMATURE) {
/* needed for bone parents */
- BKE_armature_copy_bone_transforms(ob_eval->data, ob->data);
+ BKE_armature_copy_bone_transforms(static_cast<bArmature *>(ob_eval->data),
+ static_cast<bArmature *>(ob->data));
BKE_pose_where_is(depsgraph, scene, ob_eval);
}
@@ -922,14 +912,12 @@ static int apply_objects_internal(bContext *C,
changed = true;
}
- MEM_freeN(objects);
-
if (!changed) {
BKE_report(reports, RPT_WARNING, "Objects have no data to transform");
return OPERATOR_CANCELLED;
}
- WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
+ WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, nullptr);
return OPERATOR_FINISHED;
}
@@ -956,7 +944,7 @@ static int visual_transform_apply_exec(bContext *C, wmOperator *UNUSED(op))
return OPERATOR_CANCELLED;
}
- WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
+ WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, nullptr);
return OPERATOR_FINISHED;
}
@@ -1034,7 +1022,6 @@ static int object_origin_set_exec(bContext *C, wmOperator *op)
Object *obact = CTX_data_active_object(C);
Object *obedit = CTX_data_edit_object(C);
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
- Object *tob;
float cent[3], cent_neg[3], centn[3];
const float *cursor = scene->cursor.location;
int centermode = RNA_enum_get(op->ptr, "type");
@@ -1068,7 +1055,7 @@ static int object_origin_set_exec(bContext *C, wmOperator *op)
if (obedit) {
if (obedit->type == OB_MESH) {
- Mesh *me = obedit->data;
+ Mesh *me = static_cast<Mesh *>(obedit->data);
BMEditMesh *em = me->edit_mesh;
BMVert *eve;
BMIter iter;
@@ -1107,25 +1094,24 @@ static int object_origin_set_exec(bContext *C, wmOperator *op)
}
}
- int num_objects;
- Object **objects = sorted_selected_editable_objects(C, &num_objects);
- if (objects == NULL) {
+ Array<Object *> objects = sorted_selected_editable_objects(C);
+ if (objects.is_empty()) {
return OPERATOR_CANCELLED;
}
/* reset flags */
- for (int object_index = 0; object_index < num_objects; object_index++) {
+ for (int object_index = 0; object_index < objects.size(); object_index++) {
Object *ob = objects[object_index];
ob->flag &= ~OB_DONE;
/* move active first */
if (ob == obact) {
- memmove(&objects[1], objects, object_index * sizeof(Object *));
+ memmove(&objects[1], objects.data(), object_index * sizeof(Object *));
objects[0] = ob;
}
}
- for (tob = bmain->objects.first; tob; tob = tob->id.next) {
+ LISTBASE_FOREACH (Object *, tob, &bmain->objects) {
if (tob->data) {
((ID *)tob->data)->tag &= ~LIB_TAG_DOIT;
}
@@ -1134,8 +1120,7 @@ static int object_origin_set_exec(bContext *C, wmOperator *op)
}
}
- for (int object_index = 0; object_index < num_objects; object_index++) {
- Object *ob = objects[object_index];
+ for (Object *ob : objects) {
if (ob->flag & OB_DONE) {
continue;
}
@@ -1149,8 +1134,8 @@ static int object_origin_set_exec(bContext *C, wmOperator *op)
mul_m4_v3(ob->imat, cent);
}
- if (ob->data == NULL) {
- /* special support for dupligroups */
+ if (ob->data == nullptr) {
+ /* Special support for instanced collections. */
if ((ob->transflag & OB_DUPLICOLLECTION) && ob->instance_collection &&
(ob->instance_collection->id.tag & LIB_TAG_DOIT) == 0) {
if (ID_IS_LINKED(ob->instance_collection)) {
@@ -1182,8 +1167,8 @@ static int object_origin_set_exec(bContext *C, wmOperator *op)
tot_lib_error++;
}
else if (ob->type == OB_MESH) {
- if (obedit == NULL) {
- Mesh *me = ob->data;
+ if (obedit == nullptr) {
+ Mesh *me = static_cast<Mesh *>(ob->data);
if (centermode == ORIGIN_TO_CURSOR) {
/* done */
@@ -1202,7 +1187,7 @@ static int object_origin_set_exec(bContext *C, wmOperator *op)
}
negate_v3_v3(cent_neg, cent);
- BKE_mesh_translate(me, cent_neg, 1);
+ BKE_mesh_translate(me, cent_neg, true);
tot_change++;
me->id.tag |= LIB_TAG_DOIT;
@@ -1210,7 +1195,7 @@ static int object_origin_set_exec(bContext *C, wmOperator *op)
}
}
else if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF)) {
- Curve *cu = ob->data;
+ Curve *cu = static_cast<Curve *>(ob->data);
if (centermode == ORIGIN_TO_CURSOR) {
/* done */
@@ -1228,7 +1213,7 @@ static int object_origin_set_exec(bContext *C, wmOperator *op)
}
negate_v3_v3(cent_neg, cent);
- BKE_curve_translate(cu, cent_neg, 1);
+ BKE_curve_translate(cu, cent_neg, true);
tot_change++;
cu->id.tag |= LIB_TAG_DOIT;
@@ -1244,9 +1229,9 @@ static int object_origin_set_exec(bContext *C, wmOperator *op)
else if (ob->type == OB_FONT) {
/* Get from bounding-box. */
- Curve *cu = ob->data;
+ Curve *cu = static_cast<Curve *>(ob->data);
- if (ob->runtime.bb == NULL && (centermode != ORIGIN_TO_CURSOR)) {
+ if (ob->runtime.bb == nullptr && (centermode != ORIGIN_TO_CURSOR)) {
/* Do nothing. */
}
else {
@@ -1270,7 +1255,7 @@ static int object_origin_set_exec(bContext *C, wmOperator *op)
}
}
else if (ob->type == OB_ARMATURE) {
- bArmature *arm = ob->data;
+ bArmature *arm = static_cast<bArmature *>(ob->data);
if (ID_REAL_USERS(arm) > 1) {
#if 0
@@ -1291,7 +1276,8 @@ static int object_origin_set_exec(bContext *C, wmOperator *op)
Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob);
BKE_object_transform_copy(ob_eval, ob);
- BKE_armature_copy_bone_transforms(ob_eval->data, ob->data);
+ BKE_armature_copy_bone_transforms(static_cast<bArmature *>(ob_eval->data),
+ static_cast<bArmature *>(ob->data));
BKE_object_where_is_calc(depsgraph, scene, ob_eval);
BKE_pose_where_is(depsgraph, scene, ob_eval); /* needed for bone parents */
@@ -1303,7 +1289,7 @@ static int object_origin_set_exec(bContext *C, wmOperator *op)
}
}
else if (ob->type == OB_MBALL) {
- MetaBall *mb = ob->data;
+ MetaBall *mb = static_cast<MetaBall *>(ob->data);
if (centermode == ORIGIN_TO_CURSOR) {
/* done */
@@ -1330,7 +1316,7 @@ static int object_origin_set_exec(bContext *C, wmOperator *op)
}
}
else if (ob->type == OB_LATTICE) {
- Lattice *lt = ob->data;
+ Lattice *lt = static_cast<Lattice *>(ob->data);
if (centermode == ORIGIN_TO_CURSOR) {
/* done */
@@ -1343,14 +1329,14 @@ static int object_origin_set_exec(bContext *C, wmOperator *op)
}
negate_v3_v3(cent_neg, cent);
- BKE_lattice_translate(lt, cent_neg, 1);
+ BKE_lattice_translate(lt, cent_neg, true);
tot_change++;
lt->id.tag |= LIB_TAG_DOIT;
do_inverse_offset = true;
}
else if (ob->type == OB_GPENCIL) {
- bGPdata *gpd = ob->data;
+ bGPdata *gpd = static_cast<bGPdata *>(ob->data);
float gpcenter[3];
if (gpd) {
if (centermode == ORIGIN_TO_GEOMETRY) {
@@ -1394,7 +1380,7 @@ static int object_origin_set_exec(bContext *C, wmOperator *op)
}
/* Apply transform to edit-curve. */
- if (gps->editcurve != NULL) {
+ if (gps->editcurve != nullptr) {
for (i = 0; i < gps->editcurve->tot_curve_points; i++) {
BezTriple *bezt = &gps->editcurve->curve_points[i].bezt;
for (int j = 0; j < 3; j++) {
@@ -1444,7 +1430,8 @@ static int object_origin_set_exec(bContext *C, wmOperator *op)
BKE_object_where_is_calc(depsgraph, scene, ob_eval);
if (ob->type == OB_ARMATURE) {
/* needed for bone parents */
- BKE_armature_copy_bone_transforms(ob_eval->data, ob->data);
+ BKE_armature_copy_bone_transforms(static_cast<bArmature *>(ob_eval->data),
+ static_cast<bArmature *>(ob->data));
BKE_pose_where_is(depsgraph, scene, ob_eval);
}
@@ -1455,9 +1442,7 @@ static int object_origin_set_exec(bContext *C, wmOperator *op)
//{
/* use existing context looper */
- for (int other_object_index = 0; other_object_index < num_objects; other_object_index++) {
- Object *ob_other = objects[other_object_index];
-
+ for (Object *ob_other : objects) {
if ((ob_other->flag & OB_DONE) == 0 &&
((ob->data && (ob->data == ob_other->data)) ||
(ob->instance_collection == ob_other->instance_collection &&
@@ -1473,7 +1458,8 @@ static int object_origin_set_exec(bContext *C, wmOperator *op)
BKE_object_where_is_calc(depsgraph, scene, ob_other_eval);
if (ob_other->type == OB_ARMATURE) {
/* needed for bone parents */
- BKE_armature_copy_bone_transforms(ob_eval->data, ob->data);
+ BKE_armature_copy_bone_transforms(static_cast<bArmature *>(ob_eval->data),
+ static_cast<bArmature *>(ob->data));
BKE_pose_where_is(depsgraph, scene, ob_other_eval);
}
ignore_parent_tx(bmain, depsgraph, scene, ob_other);
@@ -1482,9 +1468,8 @@ static int object_origin_set_exec(bContext *C, wmOperator *op)
// CTX_DATA_END;
}
}
- MEM_freeN(objects);
- for (tob = bmain->objects.first; tob; tob = tob->id.next) {
+ LISTBASE_FOREACH (Object *, tob, &bmain->objects) {
if (tob->data && (((ID *)tob->data)->tag & LIB_TAG_DOIT)) {
BKE_object_batch_cache_dirty_tag(tob);
DEG_id_tag_update(&tob->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
@@ -1497,7 +1482,7 @@ static int object_origin_set_exec(bContext *C, wmOperator *op)
}
if (tot_change) {
- WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
+ WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, nullptr);
}
/* Warn if any errors occurred */
@@ -1550,13 +1535,13 @@ void OBJECT_OT_origin_set(wmOperatorType *ot)
"Origin to Center of Mass (Volume)",
"Calculate the center of mass from the volume (must be manifold geometry with consistent "
"normals)"},
- {0, NULL, 0, NULL, NULL},
+ {0, nullptr, 0, nullptr, nullptr},
};
static const EnumPropertyItem prop_set_bounds_types[] = {
{V3D_AROUND_CENTER_MEDIAN, "MEDIAN", 0, "Median Center", ""},
{V3D_AROUND_CENTER_BOUNDS, "BOUNDS", 0, "Bounds Center", ""},
- {0, NULL, 0, NULL, NULL},
+ {0, nullptr, 0, nullptr, nullptr},
};
/* identifiers */
@@ -1622,26 +1607,23 @@ struct XFormAxisData {
bool is_normal_valid;
} prev;
- struct XFormAxisItem *object_data;
- uint object_data_len;
+ Vector<XFormAxisItem> object_data;
bool is_translate;
int init_event;
};
#ifdef USE_FAKE_DEPTH_INIT
-static void object_transform_axis_target_calc_depth_init(struct XFormAxisData *xfd,
- const int mval[2])
+static void object_transform_axis_target_calc_depth_init(XFormAxisData *xfd, const int mval[2])
{
- struct XFormAxisItem *item = xfd->object_data;
float view_co_a[3], view_co_b[3];
- const float mval_fl[2] = {UNPACK2(mval)};
+ const float2 mval_fl = {static_cast<float>(mval[0]), static_cast<float>(mval[1])};
ED_view3d_win_to_ray(xfd->vc.region, mval_fl, view_co_a, view_co_b);
add_v3_v3(view_co_b, view_co_a);
float center[3] = {0.0f};
int center_tot = 0;
- for (int i = 0; i < xfd->object_data_len; i++, item++) {
- const Object *ob = item->ob;
+ for (XFormAxisItem &item : xfd->object_data) {
+ const Object *ob = item.ob;
const float *ob_co_a = ob->obmat[3];
float ob_co_b[3];
add_v3_v3v3(ob_co_b, ob->obmat[3], ob->obmat[2]);
@@ -1664,7 +1646,7 @@ static void object_transform_axis_target_calc_depth_init(struct XFormAxisData *x
static bool object_is_target_compat(const Object *ob)
{
if (ob->type == OB_LAMP) {
- const Light *la = ob->data;
+ const Light *la = static_cast<Light *>(ob->data);
if (ELEM(la->type, LA_SUN, LA_SPOT, LA_AREA)) {
return true;
}
@@ -1680,8 +1662,7 @@ static bool object_is_target_compat(const Object *ob)
static void object_transform_axis_target_free_data(wmOperator *op)
{
- struct XFormAxisData *xfd = op->customdata;
- struct XFormAxisItem *item = xfd->object_data;
+ XFormAxisData *xfd = static_cast<XFormAxisData *>(op->customdata);
#ifdef USE_RENDER_OVERRIDE
if (xfd->depths) {
@@ -1689,12 +1670,11 @@ static void object_transform_axis_target_free_data(wmOperator *op)
}
#endif
- for (int i = 0; i < xfd->object_data_len; i++, item++) {
- MEM_freeN(item->obtfm);
+ for (XFormAxisItem &item : xfd->object_data) {
+ MEM_freeN(item.obtfm);
}
- MEM_freeN(xfd->object_data);
- MEM_freeN(xfd);
- op->customdata = NULL;
+ MEM_delete(xfd);
+ op->customdata = nullptr;
}
/* We may want to expose as alternative to: BKE_object_apply_rotation */
@@ -1755,12 +1735,11 @@ static bool object_orient_to_location(Object *ob,
static void object_transform_axis_target_cancel(bContext *C, wmOperator *op)
{
- struct XFormAxisData *xfd = op->customdata;
- struct XFormAxisItem *item = xfd->object_data;
- for (int i = 0; i < xfd->object_data_len; i++, item++) {
- BKE_object_tfm_restore(item->ob, item->obtfm);
- DEG_id_tag_update(&item->ob->id, ID_RECALC_TRANSFORM);
- WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, item->ob);
+ XFormAxisData *xfd = static_cast<XFormAxisData *>(op->customdata);
+ for (XFormAxisItem &item : xfd->object_data) {
+ BKE_object_tfm_restore(item.ob, item.obtfm);
+ DEG_id_tag_update(&item.ob->id, ID_RECALC_TRANSFORM);
+ WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, item.ob);
}
object_transform_axis_target_free_data(op);
@@ -1772,7 +1751,7 @@ static int object_transform_axis_target_invoke(bContext *C, wmOperator *op, cons
ViewContext vc;
ED_view3d_viewcontext_init(C, &vc, depsgraph);
- if (vc.obact == NULL || !object_is_target_compat(vc.obact)) {
+ if (vc.obact == nullptr || !object_is_target_compat(vc.obact)) {
/* Falls back to texture space transform. */
return OPERATOR_PASS_THROUGH;
}
@@ -1782,22 +1761,23 @@ static int object_transform_axis_target_invoke(bContext *C, wmOperator *op, cons
vc.v3d->flag2 |= V3D_HIDE_OVERLAYS;
#endif
- ViewDepths *depths = NULL;
- ED_view3d_depth_override(vc.depsgraph, vc.region, vc.v3d, NULL, V3D_DEPTH_NO_GPENCIL, &depths);
+ ViewDepths *depths = nullptr;
+ ED_view3d_depth_override(
+ vc.depsgraph, vc.region, vc.v3d, nullptr, V3D_DEPTH_NO_GPENCIL, &depths);
#ifdef USE_RENDER_OVERRIDE
vc.v3d->flag2 = flag2_prev;
#endif
- if (depths == NULL) {
+ if (depths == nullptr) {
BKE_report(op->reports, RPT_WARNING, "Unable to access depth buffer, using view plane");
return OPERATOR_CANCELLED;
}
ED_region_tag_redraw(vc.region);
- struct XFormAxisData *xfd;
- xfd = op->customdata = MEM_callocN(sizeof(struct XFormAxisData), __func__);
+ XFormAxisData *xfd = MEM_new<XFormAxisData>(__func__);
+ op->customdata = xfd;
/* Don't change this at runtime. */
xfd->vc = vc;
@@ -1812,41 +1792,25 @@ static int object_transform_axis_target_invoke(bContext *C, wmOperator *op, cons
xfd->init_event = WM_userdef_event_type_from_keymap_type(event->type);
- {
- struct XFormAxisItem *object_data = NULL;
- BLI_array_declare(object_data);
-
- struct XFormAxisItem *item = BLI_array_append_ret(object_data);
- item->ob = xfd->vc.obact;
-
- CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) {
- if ((ob != xfd->vc.obact) && object_is_target_compat(ob)) {
- item = BLI_array_append_ret(object_data);
- item->ob = ob;
- }
- }
- CTX_DATA_END;
+ xfd->object_data.append({});
+ xfd->object_data.last().ob = xfd->vc.obact;
- xfd->object_data = object_data;
- xfd->object_data_len = BLI_array_len(object_data);
-
- if (xfd->object_data_len != BLI_array_len(object_data)) {
- xfd->object_data = MEM_reallocN(xfd->object_data,
- xfd->object_data_len * sizeof(*xfd->object_data));
+ CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) {
+ if ((ob != xfd->vc.obact) && object_is_target_compat(ob)) {
+ xfd->object_data.append({});
+ xfd->object_data.last().ob = ob;
}
}
+ CTX_DATA_END;
- {
- struct XFormAxisItem *item = xfd->object_data;
- for (int i = 0; i < xfd->object_data_len; i++, item++) {
- item->obtfm = BKE_object_tfm_backup(item->ob);
- BKE_object_rot_to_mat3(item->ob, item->rot_mat, true);
+ for (XFormAxisItem &item : xfd->object_data) {
+ item.obtfm = BKE_object_tfm_backup(item.ob);
+ BKE_object_rot_to_mat3(item.ob, item.rot_mat, true);
- /* Detect negative scale matrix. */
- float full_mat3[3][3];
- BKE_object_to_mat3(item->ob, full_mat3);
- item->is_z_flip = dot_v3v3(item->rot_mat[2], full_mat3[2]) < 0.0f;
- }
+ /* Detect negative scale matrix. */
+ float full_mat3[3][3];
+ BKE_object_to_mat3(item.ob, full_mat3);
+ item.is_z_flip = dot_v3v3(item.rot_mat[2], full_mat3[2]) < 0.0f;
}
WM_event_add_modal_handler(C, op);
@@ -1856,7 +1820,7 @@ static int object_transform_axis_target_invoke(bContext *C, wmOperator *op, cons
static int object_transform_axis_target_modal(bContext *C, wmOperator *op, const wmEvent *event)
{
- struct XFormAxisData *xfd = op->customdata;
+ XFormAxisData *xfd = static_cast<XFormAxisData *>(op->customdata);
ARegion *region = xfd->vc.region;
view3d_operator_needs_opengl(C);
@@ -1922,36 +1886,35 @@ static int object_transform_axis_target_modal(bContext *C, wmOperator *op, const
{
#ifdef USE_RELATIVE_ROTATION
- if (is_translate_init && xfd->object_data_len > 1) {
+ if (is_translate_init && xfd->object_data.size() > 1) {
float xform_rot_offset_inv_first[3][3];
- struct XFormAxisItem *item = xfd->object_data;
- for (int i = 0; i < xfd->object_data_len; i++, item++) {
- copy_m3_m4(item->xform_rot_offset, item->ob->obmat);
- normalize_m3(item->xform_rot_offset);
+ for (const int i : xfd->object_data.index_range()) {
+ XFormAxisItem &item = xfd->object_data[i];
+ copy_m3_m4(item.xform_rot_offset, item.ob->obmat);
+ normalize_m3(item.xform_rot_offset);
if (i == 0) {
invert_m3_m3(xform_rot_offset_inv_first, xfd->object_data[0].xform_rot_offset);
}
else {
- mul_m3_m3m3(item->xform_rot_offset,
- item->xform_rot_offset,
- xform_rot_offset_inv_first);
+ mul_m3_m3m3(
+ item.xform_rot_offset, item.xform_rot_offset, xform_rot_offset_inv_first);
}
}
}
#endif
- struct XFormAxisItem *item = xfd->object_data;
- for (int i = 0; i < xfd->object_data_len; i++, item++) {
+ for (const int i : xfd->object_data.index_range()) {
+ XFormAxisItem &item = xfd->object_data[i];
if (is_translate_init) {
float ob_axis[3];
- item->xform_dist = len_v3v3(item->ob->obmat[3], location_world);
- normalize_v3_v3(ob_axis, item->ob->obmat[2]);
+ item.xform_dist = len_v3v3(item.ob->obmat[3], location_world);
+ normalize_v3_v3(ob_axis, item.ob->obmat[2]);
/* Scale to avoid adding distance when moving between surfaces. */
if (normal_found) {
float scale = fabsf(dot_v3v3(ob_axis, normal));
- item->xform_dist *= scale;
+ item.xform_dist *= scale;
}
}
@@ -1961,13 +1924,13 @@ static int object_transform_axis_target_modal(bContext *C, wmOperator *op, const
copy_v3_v3(target_normal, normal);
}
else {
- normalize_v3_v3(target_normal, item->ob->obmat[2]);
+ normalize_v3_v3(target_normal, item.ob->obmat[2]);
}
#ifdef USE_RELATIVE_ROTATION
if (normal_found) {
if (i != 0) {
- mul_m3_v3(item->xform_rot_offset, target_normal);
+ mul_m3_v3(item.xform_rot_offset, target_normal);
}
}
#endif
@@ -1975,17 +1938,17 @@ static int object_transform_axis_target_modal(bContext *C, wmOperator *op, const
float loc[3];
copy_v3_v3(loc, location_world);
- madd_v3_v3fl(loc, target_normal, item->xform_dist);
- object_apply_location(item->ob, loc);
+ madd_v3_v3fl(loc, target_normal, item.xform_dist);
+ object_apply_location(item.ob, loc);
/* so orient behaves as expected */
- copy_v3_v3(item->ob->obmat[3], loc);
+ copy_v3_v3(item.ob->obmat[3], loc);
}
object_orient_to_location(
- item->ob, item->rot_mat, item->rot_mat[2], location_world, item->is_z_flip);
+ item.ob, item.rot_mat, item.rot_mat[2], location_world, item.is_z_flip);
- DEG_id_tag_update(&item->ob->id, ID_RECALC_TRANSFORM);
- WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, item->ob);
+ DEG_id_tag_update(&item.ob->id, ID_RECALC_TRANSFORM);
+ WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, item.ob);
}
if (normal_found) {
copy_v3_v3(xfd->prev.normal, normal);
@@ -1994,15 +1957,11 @@ static int object_transform_axis_target_modal(bContext *C, wmOperator *op, const
}
}
else {
- struct XFormAxisItem *item = xfd->object_data;
- for (int i = 0; i < xfd->object_data_len; i++, item++) {
- if (object_orient_to_location(item->ob,
- item->rot_mat,
- item->rot_mat[2],
- location_world,
- item->is_z_flip)) {
- DEG_id_tag_update(&item->ob->id, ID_RECALC_TRANSFORM);
- WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, item->ob);
+ for (XFormAxisItem &item : xfd->object_data) {
+ if (object_orient_to_location(
+ item.ob, item.rot_mat, item.rot_mat[2], location_world, item.is_z_flip)) {
+ DEG_id_tag_update(&item.ob->id, ID_RECALC_TRANSFORM);
+ WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, item.ob);
}
}
xfd->prev.is_normal_valid = false;
diff --git a/source/blender/editors/physics/dynamicpaint_ops.c b/source/blender/editors/physics/dynamicpaint_ops.c
index 5bc062eb177..e8ceb97ed7a 100644
--- a/source/blender/editors/physics/dynamicpaint_ops.c
+++ b/source/blender/editors/physics/dynamicpaint_ops.c
@@ -399,27 +399,27 @@ static void dynamicPaint_bakeImageSequence(DynamicPaintBakeJob *job)
* Save output images
*/
{
- char filename[FILE_MAX];
+ char filepath[FILE_MAX];
/* primary output layer */
if (surface->flags & MOD_DPAINT_OUT1) {
/* set filepath */
BLI_join_dirfile(
- filename, sizeof(filename), surface->image_output_path, surface->output_name);
- BLI_path_frame(filename, frame, 4);
+ filepath, sizeof(filepath), surface->image_output_path, surface->output_name);
+ BLI_path_frame(filepath, frame, 4);
/* save image */
- dynamicPaint_outputSurfaceImage(surface, filename, 0);
+ dynamicPaint_outputSurfaceImage(surface, filepath, 0);
}
/* secondary output */
if (surface->flags & MOD_DPAINT_OUT2 && surface->type == MOD_DPAINT_SURFACE_T_PAINT) {
/* set filepath */
BLI_join_dirfile(
- filename, sizeof(filename), surface->image_output_path, surface->output_name2);
- BLI_path_frame(filename, frame, 4);
+ filepath, sizeof(filepath), surface->image_output_path, surface->output_name2);
+ BLI_path_frame(filepath, frame, 4);
/* save image */
- dynamicPaint_outputSurfaceImage(surface, filename, 1);
+ dynamicPaint_outputSurfaceImage(surface, filepath, 1);
}
}
}
diff --git a/source/blender/editors/physics/particle_edit.c b/source/blender/editors/physics/particle_edit.c
index fc815ebe682..4a639e227f7 100644
--- a/source/blender/editors/physics/particle_edit.c
+++ b/source/blender/editors/physics/particle_edit.c
@@ -487,6 +487,8 @@ typedef struct PEData {
int select_action;
int select_toggle_action;
bool is_changed;
+
+ void *user_data;
} PEData;
static void PE_set_data(bContext *C, PEData *data)
@@ -1721,46 +1723,6 @@ static void select_keys(PEData *data,
point->flag |= PEP_EDIT_RECALC;
}
-static void extend_key_select(PEData *data, int point_index, int key_index, bool UNUSED(is_inside))
-{
- PTCacheEdit *edit = data->edit;
- PTCacheEditPoint *point = edit->points + point_index;
- PTCacheEditKey *key = point->keys + key_index;
-
- if ((key->flag & PEK_SELECT) == 0) {
- key->flag |= PEK_SELECT;
- point->flag |= PEP_EDIT_RECALC;
- data->is_changed = true;
- }
-}
-
-static void deselect_key_select(PEData *data,
- int point_index,
- int key_index,
- bool UNUSED(is_inside))
-{
- PTCacheEdit *edit = data->edit;
- PTCacheEditPoint *point = edit->points + point_index;
- PTCacheEditKey *key = point->keys + key_index;
-
- if ((key->flag & PEK_SELECT) != 0) {
- key->flag &= ~PEK_SELECT;
- point->flag |= PEP_EDIT_RECALC;
- data->is_changed = true;
- }
-}
-
-static void toggle_key_select(PEData *data, int point_index, int key_index, bool UNUSED(is_inside))
-{
- PTCacheEdit *edit = data->edit;
- PTCacheEditPoint *point = edit->points + point_index;
- PTCacheEditKey *key = point->keys + key_index;
-
- key->flag ^= PEK_SELECT;
- point->flag |= PEP_EDIT_RECALC;
- data->is_changed = true;
-}
-
/** \} */
/* -------------------------------------------------------------------- */
@@ -1862,13 +1824,50 @@ void PARTICLE_OT_select_all(wmOperatorType *ot)
/** \name Pick Select Operator
* \{ */
-bool PE_mouse_particles(bContext *C, const int mval[2], bool extend, bool deselect, bool toggle)
+struct NearestParticleData {
+ PTCacheEditPoint *point;
+ PTCacheEditKey *key;
+};
+
+static void nearest_key_fn(PEData *data, int point_index, int key_index, bool UNUSED(is_inside))
+{
+ PTCacheEdit *edit = data->edit;
+ PTCacheEditPoint *point = edit->points + point_index;
+ PTCacheEditKey *key = point->keys + key_index;
+
+ struct NearestParticleData *user_data = data->user_data;
+ user_data->point = point;
+ user_data->key = key;
+ data->is_changed = true;
+}
+
+static bool pe_nearest_point_and_key(bContext *C,
+ const int mval[2],
+ PTCacheEditPoint **r_point,
+ PTCacheEditKey **r_key)
+{
+ struct NearestParticleData user_data = {NULL};
+
+ PEData data;
+ PE_set_view3d_data(C, &data);
+ data.mval = mval;
+ data.rad = ED_view3d_select_dist_px();
+
+ data.user_data = &user_data;
+ for_mouse_hit_keys(&data, nearest_key_fn, PSEL_NEAREST);
+ bool found = data.is_changed;
+ PE_data_free(&data);
+
+ *r_point = user_data.point;
+ *r_key = user_data.key;
+ return found;
+}
+
+bool PE_mouse_particles(bContext *C, const int mval[2], const struct SelectPick_Params *params)
{
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
Scene *scene = CTX_data_scene(C);
Object *ob = CTX_data_active_object(C);
- POINT_P;
- KEY_K;
PTCacheEdit *edit = PE_get_current(depsgraph, scene, ob);
@@ -1876,39 +1875,67 @@ bool PE_mouse_particles(bContext *C, const int mval[2], bool extend, bool desele
return false;
}
- if (!extend && !deselect && !toggle) {
- LOOP_VISIBLE_POINTS {
- LOOP_SELECTED_KEYS {
- key->flag &= ~PEK_SELECT;
- point->flag |= PEP_EDIT_RECALC;
- }
- }
- }
+ PTCacheEditPoint *point;
+ PTCacheEditKey *key;
- PEData data;
- PE_set_view3d_data(C, &data);
- data.mval = mval;
- data.rad = ED_view3d_select_dist_px();
+ bool changed = false;
+ bool found = pe_nearest_point_and_key(C, mval, &point, &key);
- /* 1 = nearest only */
- if (extend) {
- for_mouse_hit_keys(&data, extend_key_select, PSEL_NEAREST);
- }
- else if (deselect) {
- for_mouse_hit_keys(&data, deselect_key_select, PSEL_NEAREST);
- }
- else {
- for_mouse_hit_keys(&data, toggle_key_select, PSEL_NEAREST);
+ if (params->sel_op == SEL_OP_SET) {
+ if ((found && params->select_passthrough) && (key->flag & PEK_SELECT)) {
+ found = false;
+ }
+ else if (found || params->deselect_all) {
+ /* Deselect everything. */
+ changed |= PE_deselect_all_visible_ex(edit);
+ }
}
- if (data.is_changed) {
- PE_update_selection(data.depsgraph, scene, ob, 1);
- WM_event_add_notifier(C, NC_OBJECT | ND_PARTICLE | NA_SELECTED, data.ob);
+ if (found) {
+ switch (params->sel_op) {
+ case SEL_OP_ADD: {
+ if ((key->flag & PEK_SELECT) == 0) {
+ key->flag |= PEK_SELECT;
+ point->flag |= PEP_EDIT_RECALC;
+ changed = true;
+ }
+ break;
+ }
+ case SEL_OP_SUB: {
+ if ((key->flag & PEK_SELECT) != 0) {
+ key->flag &= ~PEK_SELECT;
+ point->flag |= PEP_EDIT_RECALC;
+ changed = true;
+ }
+ break;
+ }
+ case SEL_OP_XOR: {
+ key->flag ^= PEK_SELECT;
+ point->flag |= PEP_EDIT_RECALC;
+ changed = true;
+ break;
+ }
+ case SEL_OP_SET: {
+ if ((key->flag & PEK_SELECT) == 0) {
+ key->flag |= PEK_SELECT;
+ point->flag |= PEP_EDIT_RECALC;
+ changed = true;
+ }
+ break;
+ }
+ case SEL_OP_AND: {
+ BLI_assert_unreachable(); /* Doesn't make sense for picking. */
+ break;
+ }
+ }
}
- PE_data_free(&data);
+ if (changed) {
+ PE_update_selection(depsgraph, scene, ob, 1);
+ WM_event_add_notifier(C, NC_OBJECT | ND_PARTICLE | NA_SELECTED, ob);
+ }
- return true;
+ return changed || found;
}
/** \} */
diff --git a/source/blender/editors/render/render_internal.cc b/source/blender/editors/render/render_internal.cc
index 33b68dfb47b..e5d2a765ca1 100644
--- a/source/blender/editors/render/render_internal.cc
+++ b/source/blender/editors/render/render_internal.cc
@@ -31,6 +31,7 @@
#include "BKE_context.h"
#include "BKE_global.h"
#include "BKE_image.h"
+#include "BKE_image_format.h"
#include "BKE_lib_id.h"
#include "BKE_main.h"
#include "BKE_node.h"
@@ -101,7 +102,7 @@ struct RenderJob {
/* called inside thread! */
static bool image_buffer_calc_tile_rect(const RenderResult *rr,
const ImBuf *ibuf,
- volatile rcti *renrect,
+ rcti *renrect,
rcti *r_ibuf_rect,
int *r_offset_x,
int *r_offset_y)
@@ -355,7 +356,14 @@ static int screen_render_exec(bContext *C, wmOperator *op)
scene->r.frame_step);
}
else {
- RE_RenderFrame(re, mainp, scene, single_layer, camera_override, scene->r.cfra, is_write_still);
+ RE_RenderFrame(re,
+ mainp,
+ scene,
+ single_layer,
+ camera_override,
+ scene->r.cfra,
+ scene->r.subframe,
+ is_write_still);
}
RE_SetReports(re, nullptr);
@@ -549,7 +557,7 @@ static void render_image_update_pass_and_layer(RenderJob *rj, RenderResult *rr,
}
}
-static void image_rect_update(void *rjv, RenderResult *rr, volatile rcti *renrect)
+static void image_rect_update(void *rjv, RenderResult *rr, rcti *renrect)
{
RenderJob *rj = static_cast<RenderJob *>(rjv);
Image *ima = rj->image;
@@ -655,6 +663,7 @@ static void render_startjob(void *rjv, short *stop, short *do_update, float *pro
rj->single_layer,
rj->camera_override,
rj->scene->r.cfra,
+ rj->scene->r.subframe,
rj->write_still);
}
diff --git a/source/blender/editors/render/render_opengl.cc b/source/blender/editors/render/render_opengl.cc
index be66e87f2e5..fbdc1086874 100644
--- a/source/blender/editors/render/render_opengl.cc
+++ b/source/blender/editors/render/render_opengl.cc
@@ -34,6 +34,8 @@
#include "BKE_fcurve.h"
#include "BKE_global.h"
#include "BKE_image.h"
+#include "BKE_image_format.h"
+#include "BKE_image_save.h"
#include "BKE_lib_query.h"
#include "BKE_main.h"
#include "BKE_report.h"
@@ -312,13 +314,13 @@ static void screen_opengl_render_doit(const bContext *C, OGLRender *oglrender, R
imb_freerectfloatImBuf(out);
}
BLI_assert((oglrender->sizex == ibuf->x) && (oglrender->sizey == ibuf->y));
- RE_render_result_rect_from_ibuf(rr, &scene->r, out, oglrender->view_id);
+ RE_render_result_rect_from_ibuf(rr, out, oglrender->view_id);
IMB_freeImBuf(out);
}
else if (gpd) {
/* If there are no strips, Grease Pencil still needs a buffer to draw on */
ImBuf *out = IMB_allocImBuf(oglrender->sizex, oglrender->sizey, 32, IB_rect);
- RE_render_result_rect_from_ibuf(rr, &scene->r, out, oglrender->view_id);
+ RE_render_result_rect_from_ibuf(rr, out, oglrender->view_id);
IMB_freeImBuf(out);
}
@@ -414,7 +416,7 @@ static void screen_opengl_render_doit(const bContext *C, OGLRender *oglrender, R
if ((scene->r.stamp & R_STAMP_ALL) && (scene->r.stamp & R_STAMP_DRAW)) {
BKE_image_stamp_buf(scene, camera, nullptr, rect, rectf, rr->rectx, rr->recty, 4);
}
- RE_render_result_rect_from_ibuf(rr, &scene->r, ibuf_result, oglrender->view_id);
+ RE_render_result_rect_from_ibuf(rr, ibuf_result, oglrender->view_id);
IMB_freeImBuf(ibuf_result);
}
}
@@ -439,7 +441,7 @@ static void screen_opengl_render_write(OGLRender *oglrender)
/* write images as individual images or stereo */
BKE_render_result_stamp_info(scene, scene->camera, rr, false);
- ok = RE_WriteRenderViewsImage(oglrender->reports, rr, scene, false, name);
+ ok = BKE_image_render_write(oglrender->reports, rr, scene, false, name);
RE_ReleaseResultImage(oglrender->re);
@@ -1070,7 +1072,7 @@ static void write_result_func(TaskPool *__restrict pool, void *task_data_v)
nullptr);
BKE_render_result_stamp_info(scene, scene->camera, rr, false);
- ok = RE_WriteRenderViewsImage(nullptr, rr, scene, true, name);
+ ok = BKE_image_render_write(nullptr, rr, scene, true, name);
if (!ok) {
BKE_reportf(&reports, RPT_ERROR, "Write error: cannot save %s", name);
}
diff --git a/source/blender/editors/render/render_preview.cc b/source/blender/editors/render/render_preview.cc
index cfb88cd7868..ef0f0b6225c 100644
--- a/source/blender/editors/render/render_preview.cc
+++ b/source/blender/editors/render/render_preview.cc
@@ -459,11 +459,11 @@ static Scene *preview_prepare_scene(
if (sce) {
ViewLayer *view_layer = static_cast<ViewLayer *>(sce->view_layers.first);
- /* Only enable the combined renderpass */
+ /* Only enable the combined render-pass. */
view_layer->passflag = SCE_PASS_COMBINED;
view_layer->eevee.render_passes = 0;
- /* this flag tells render to not execute depsgraph or ipos etc */
+ /* This flag tells render to not execute depsgraph or F-Curves etc. */
sce->r.scemode |= R_BUTS_PREVIEW;
BLI_strncpy(sce->r.engine, scene->r.engine, sizeof(sce->r.engine));
@@ -987,9 +987,7 @@ static void action_preview_render(IconPreview *preview, IconPreviewSize *preview
* \{ */
/* inside thread, called by renderer, sets job update value */
-static void shader_preview_update(void *spv,
- RenderResult *UNUSED(rr),
- volatile struct rcti *UNUSED(rect))
+static void shader_preview_update(void *spv, RenderResult *UNUSED(rr), struct rcti *UNUSED(rect))
{
ShaderPreview *sp = static_cast<ShaderPreview *>(spv);
@@ -1681,19 +1679,19 @@ class PreviewLoadJob {
PreviewLoadJob();
~PreviewLoadJob();
- static PreviewLoadJob &ensure_job(wmWindowManager *, wmWindow *);
- static void load_jobless(PreviewImage *, eIconSizes);
+ static PreviewLoadJob &ensure_job(wmWindowManager *wm, wmWindow *win);
+ static void load_jobless(PreviewImage *preview, eIconSizes icon_size);
- void push_load_request(PreviewImage *, eIconSizes);
+ void push_load_request(PreviewImage *preview, eIconSizes icon_size);
private:
- static void run_fn(void *, short *, short *, float *);
- static void update_fn(void *);
- static void end_fn(void *);
- static void free_fn(void *);
+ static void run_fn(void *customdata, short *stop, short *do_update, float *progress);
+ static void update_fn(void *customdata);
+ static void end_fn(void *customdata);
+ static void free_fn(void *customdata);
/** Mark a single requested preview as being done, remove the request. */
- static void finish_request(RequestedPreview &);
+ static void finish_request(RequestedPreview &request);
};
PreviewLoadJob::PreviewLoadJob() : todo_queue_(BLI_thread_queue_init())
diff --git a/source/blender/editors/scene/CMakeLists.txt b/source/blender/editors/scene/CMakeLists.txt
index 7f687212066..12043ac2957 100644
--- a/source/blender/editors/scene/CMakeLists.txt
+++ b/source/blender/editors/scene/CMakeLists.txt
@@ -8,8 +8,8 @@ set(INC
../../depsgraph
../../makesdna
../../makesrna
- ../../windowmanager
../../sequencer
+ ../../windowmanager
)
set(INC_SYS
diff --git a/source/blender/editors/screen/screendump.c b/source/blender/editors/screen/screendump.c
index ff77f9910fb..5464d0a347d 100644
--- a/source/blender/editors/screen/screendump.c
+++ b/source/blender/editors/screen/screendump.c
@@ -24,6 +24,7 @@
#include "BKE_context.h"
#include "BKE_global.h"
#include "BKE_image.h"
+#include "BKE_image_format.h"
#include "BKE_main.h"
#include "BKE_report.h"
#include "BKE_screen.h"
@@ -71,7 +72,7 @@ static int screenshot_data_create(bContext *C, wmOperator *op, ScrArea *area)
scd->crop = area->totrct;
}
- BKE_imformat_defaults(&scd->im_format);
+ BKE_image_format_init(&scd->im_format, false);
op->customdata = scd;
diff --git a/source/blender/editors/sculpt_paint/CMakeLists.txt b/source/blender/editors/sculpt_paint/CMakeLists.txt
index ccbdb3c4145..c422c8c2033 100644
--- a/source/blender/editors/sculpt_paint/CMakeLists.txt
+++ b/source/blender/editors/sculpt_paint/CMakeLists.txt
@@ -13,8 +13,8 @@ set(INC
../../gpu
../../imbuf
../../makesdna
- ../../nodes
../../makesrna
+ ../../nodes
../../render
../../windowmanager
../../../../intern/atomic
@@ -27,15 +27,20 @@ set(INC
)
set(SRC
+ curves_sculpt_3d_brush.cc
+ curves_sculpt_add.cc
+ curves_sculpt_comb.cc
+ curves_sculpt_delete.cc
curves_sculpt_ops.cc
+ curves_sculpt_snake_hook.cc
paint_cursor.c
paint_curve.c
paint_curve_undo.c
paint_hide.c
paint_image.cc
- paint_image_ops_paint.cc
paint_image_2d.c
paint_image_2d_curve_mask.cc
+ paint_image_ops_paint.cc
paint_image_proj.c
paint_mask.c
paint_ops.c
@@ -72,6 +77,7 @@ set(SRC
sculpt_uv.c
curves_sculpt_intern.h
+ curves_sculpt_intern.hh
paint_intern.h
sculpt_intern.h
)
diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_3d_brush.cc b/source/blender/editors/sculpt_paint/curves_sculpt_3d_brush.cc
new file mode 100644
index 00000000000..945bb09c0c6
--- /dev/null
+++ b/source/blender/editors/sculpt_paint/curves_sculpt_3d_brush.cc
@@ -0,0 +1,232 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include <algorithm>
+
+#include "curves_sculpt_intern.hh"
+
+#include "BKE_bvhutils.h"
+#include "BKE_context.h"
+#include "BKE_curves.hh"
+
+#include "ED_view3d.h"
+
+#include "UI_interface.h"
+
+#include "BLI_enumerable_thread_specific.hh"
+#include "BLI_task.hh"
+
+/**
+ * The code below uses a prefix naming convention to indicate the coordinate space:
+ * cu: Local space of the curves object that is being edited.
+ * su: Local space of the surface object.
+ * wo: World space.
+ * re: 2D coordinates within the region.
+ */
+
+namespace blender::ed::sculpt_paint {
+
+struct BrushPositionCandidate {
+ /** 3D position of the brush. */
+ float3 position_cu;
+ /** Squared distance from the mouse position in screen space. */
+ float distance_sq_re = FLT_MAX;
+ /** Measure for how far away the candidate is from the camera. */
+ float depth_sq_cu = FLT_MAX;
+};
+
+/**
+ * Determine the 3D position of a brush based on curve segments under a screen position.
+ */
+static std::optional<float3> find_curves_brush_position(const CurvesGeometry &curves,
+ const float3 &ray_start_cu,
+ const float3 &ray_end_cu,
+ const float brush_radius_re,
+ ARegion &region,
+ RegionView3D &rv3d,
+ Object &object)
+{
+ /* This value might have to be adjusted based on user feedback. */
+ const float brush_inner_radius_re = std::min<float>(brush_radius_re, (float)UI_UNIT_X / 3.0f);
+ const float brush_inner_radius_sq_re = pow2f(brush_inner_radius_re);
+
+ float4x4 projection;
+ ED_view3d_ob_project_mat_get(&rv3d, &object, projection.values);
+
+ float2 brush_pos_re;
+ ED_view3d_project_float_v2_m4(&region, ray_start_cu, brush_pos_re, projection.values);
+
+ const float max_depth_sq_cu = math::distance_squared(ray_start_cu, ray_end_cu);
+
+ /* Contains the logic that checks if `b` is a better candidate than `a`. */
+ auto is_better_candidate = [&](const BrushPositionCandidate &a,
+ const BrushPositionCandidate &b) {
+ if (b.distance_sq_re <= brush_inner_radius_sq_re) {
+ if (a.distance_sq_re > brush_inner_radius_sq_re) {
+ /* New candidate is in inner radius while old one is not. */
+ return true;
+ }
+ if (b.depth_sq_cu < a.depth_sq_cu) {
+ /* Both candidates are in inner radius, but new one is closer to the camera. */
+ return true;
+ }
+ }
+ else if (b.distance_sq_re < a.distance_sq_re) {
+ /* Both candidates are outside of inner radius, but new on is closer to the brush center. */
+ return true;
+ }
+ return false;
+ };
+
+ auto update_if_better = [&](BrushPositionCandidate &a, const BrushPositionCandidate &b) {
+ if (is_better_candidate(a, b)) {
+ a = b;
+ }
+ };
+
+ const Span<float3> positions = curves.positions();
+
+ BrushPositionCandidate best_candidate = threading::parallel_reduce(
+ curves.curves_range(),
+ 128,
+ BrushPositionCandidate(),
+ [&](IndexRange curves_range, const BrushPositionCandidate &init) {
+ BrushPositionCandidate best_candidate = init;
+
+ for (const int curve_i : curves_range) {
+ const IndexRange points = curves.points_for_curve(curve_i);
+ const int tot_segments = points.size() - 1;
+
+ for (const int segment_i : IndexRange(tot_segments)) {
+ const float3 &p1_cu = positions[points[segment_i]];
+ const float3 &p2_cu = positions[points[segment_i] + 1];
+
+ float2 p1_re, p2_re;
+ ED_view3d_project_float_v2_m4(&region, p1_cu, p1_re, projection.values);
+ ED_view3d_project_float_v2_m4(&region, p2_cu, p2_re, projection.values);
+
+ float2 closest_re;
+ const float lambda = closest_to_line_segment_v2(
+ closest_re, brush_pos_re, p1_re, p2_re);
+
+ const float3 closest_cu = math::interpolate(p1_cu, p2_cu, lambda);
+ const float depth_sq_cu = math::distance_squared(ray_start_cu, closest_cu);
+ if (depth_sq_cu > max_depth_sq_cu) {
+ continue;
+ }
+
+ const float distance_sq_re = math::distance_squared(brush_pos_re, closest_re);
+
+ BrushPositionCandidate candidate;
+ candidate.position_cu = closest_cu;
+ candidate.depth_sq_cu = depth_sq_cu;
+ candidate.distance_sq_re = distance_sq_re;
+
+ update_if_better(best_candidate, candidate);
+ }
+ }
+ return best_candidate;
+ },
+ [&](const BrushPositionCandidate &a, const BrushPositionCandidate &b) {
+ return is_better_candidate(a, b) ? b : a;
+ });
+
+ if (best_candidate.distance_sq_re == FLT_MAX) {
+ /* Nothing found. */
+ return std::nullopt;
+ }
+
+ return best_candidate.position_cu;
+}
+
+std::optional<CurvesBrush3D> sample_curves_3d_brush(bContext &C,
+ Object &curves_object,
+ const float2 &brush_pos_re,
+ const float brush_radius_re)
+{
+ Depsgraph *depsgraph = CTX_data_depsgraph_pointer(&C);
+ ARegion *region = CTX_wm_region(&C);
+ View3D *v3d = CTX_wm_view3d(&C);
+ RegionView3D *rv3d = CTX_wm_region_view3d(&C);
+
+ Curves &curves_id = *static_cast<Curves *>(curves_object.data);
+ CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry);
+ Object *surface_object = curves_id.surface;
+
+ float3 center_ray_start_wo, center_ray_end_wo;
+ ED_view3d_win_to_segment_clipped(
+ depsgraph, region, v3d, brush_pos_re, center_ray_start_wo, center_ray_end_wo, true);
+
+ /* Shorten ray when the surface object is hit. */
+ if (surface_object != nullptr) {
+ const float4x4 surface_to_world_mat = surface_object->obmat;
+ const float4x4 world_to_surface_mat = surface_to_world_mat.inverted();
+
+ Mesh &surface = *static_cast<Mesh *>(surface_object->data);
+ BVHTreeFromMesh surface_bvh;
+ BKE_bvhtree_from_mesh_get(&surface_bvh, &surface, BVHTREE_FROM_LOOPTRI, 2);
+ BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh); });
+
+ const float3 center_ray_start_su = world_to_surface_mat * center_ray_start_wo;
+ float3 center_ray_end_su = world_to_surface_mat * center_ray_end_wo;
+ const float3 center_ray_direction_su = math::normalize(center_ray_end_su -
+ center_ray_start_su);
+
+ BVHTreeRayHit center_ray_hit;
+ center_ray_hit.dist = FLT_MAX;
+ center_ray_hit.index = -1;
+ BLI_bvhtree_ray_cast(surface_bvh.tree,
+ center_ray_start_su,
+ center_ray_direction_su,
+ 0.0f,
+ &center_ray_hit,
+ surface_bvh.raycast_callback,
+ &surface_bvh);
+ if (center_ray_hit.index >= 0) {
+ const float3 hit_position_su = center_ray_hit.co;
+ if (math::distance(center_ray_start_su, center_ray_end_su) >
+ math::distance(center_ray_start_su, hit_position_su)) {
+ center_ray_end_su = hit_position_su;
+ center_ray_end_wo = surface_to_world_mat * center_ray_end_su;
+ }
+ }
+ }
+
+ const float4x4 curves_to_world_mat = curves_object.obmat;
+ const float4x4 world_to_curves_mat = curves_to_world_mat.inverted();
+
+ const float3 center_ray_start_cu = world_to_curves_mat * center_ray_start_wo;
+ const float3 center_ray_end_cu = world_to_curves_mat * center_ray_end_wo;
+
+ const std::optional<float3> brush_position_optional_cu = find_curves_brush_position(
+ curves,
+ center_ray_start_cu,
+ center_ray_end_cu,
+ brush_radius_re,
+ *region,
+ *rv3d,
+ curves_object);
+ if (!brush_position_optional_cu.has_value()) {
+ /* Nothing found. */
+ return std::nullopt;
+ }
+ const float3 brush_position_cu = *brush_position_optional_cu;
+
+ /* Determine the 3D brush radius. */
+ float3 radius_ray_start_wo, radius_ray_end_wo;
+ ED_view3d_win_to_segment_clipped(depsgraph,
+ region,
+ v3d,
+ brush_pos_re + float2(brush_radius_re, 0.0f),
+ radius_ray_start_wo,
+ radius_ray_end_wo,
+ true);
+ const float3 radius_ray_start_cu = world_to_curves_mat * radius_ray_start_wo;
+ const float3 radius_ray_end_cu = world_to_curves_mat * radius_ray_end_wo;
+
+ CurvesBrush3D brush_3d;
+ brush_3d.position_cu = brush_position_cu;
+ brush_3d.radius_cu = dist_to_line_v3(brush_position_cu, radius_ray_start_cu, radius_ray_end_cu);
+ return brush_3d;
+}
+
+} // namespace blender::ed::sculpt_paint
diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_add.cc b/source/blender/editors/sculpt_paint/curves_sculpt_add.cc
new file mode 100644
index 00000000000..809511d0106
--- /dev/null
+++ b/source/blender/editors/sculpt_paint/curves_sculpt_add.cc
@@ -0,0 +1,792 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include <algorithm>
+
+#include "curves_sculpt_intern.hh"
+
+#include "BLI_float4x4.hh"
+#include "BLI_kdtree.h"
+#include "BLI_rand.hh"
+#include "BLI_vector.hh"
+
+#include "PIL_time.h"
+
+#include "DEG_depsgraph.h"
+
+#include "BKE_attribute_math.hh"
+#include "BKE_brush.h"
+#include "BKE_bvhutils.h"
+#include "BKE_context.h"
+#include "BKE_curves.hh"
+#include "BKE_mesh.h"
+#include "BKE_mesh_runtime.h"
+#include "BKE_paint.h"
+#include "BKE_spline.hh"
+
+#include "DNA_brush_enums.h"
+#include "DNA_brush_types.h"
+#include "DNA_curves_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_object_types.h"
+#include "DNA_screen_types.h"
+#include "DNA_space_types.h"
+
+#include "ED_screen.h"
+#include "ED_view3d.h"
+
+/**
+ * The code below uses a prefix naming convention to indicate the coordinate space:
+ * cu: Local space of the curves object that is being edited.
+ * su: Local space of the surface object.
+ * wo: World space.
+ * re: 2D coordinates within the region.
+ */
+
+namespace blender::ed::sculpt_paint {
+
+using bke::CurvesGeometry;
+
+class AddOperation : public CurvesSculptStrokeOperation {
+ private:
+ /** Used when some data should be interpolated from existing curves. */
+ KDTree_3d *curve_roots_kdtree_ = nullptr;
+
+ friend struct AddOperationExecutor;
+
+ public:
+ ~AddOperation() override
+ {
+ if (curve_roots_kdtree_ != nullptr) {
+ BLI_kdtree_3d_free(curve_roots_kdtree_);
+ }
+ }
+
+ void on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) override;
+};
+
+static void initialize_straight_curve_positions(const float3 &p1,
+ const float3 &p2,
+ MutableSpan<float3> r_positions)
+{
+ const float step = 1.0f / (float)(r_positions.size() - 1);
+ for (const int i : r_positions.index_range()) {
+ r_positions[i] = math::interpolate(p1, p2, i * step);
+ }
+}
+
+/**
+ * Utility class that actually executes the update when the stroke is updated. That's useful
+ * because it avoids passing a very large number of parameters between functions.
+ */
+struct AddOperationExecutor {
+ AddOperation *self_ = nullptr;
+ Depsgraph *depsgraph_ = nullptr;
+ Scene *scene_ = nullptr;
+ Object *object_ = nullptr;
+ ARegion *region_ = nullptr;
+ View3D *v3d_ = nullptr;
+ Curves *curves_id_ = nullptr;
+ CurvesGeometry *curves_ = nullptr;
+
+ Object *surface_ob_ = nullptr;
+ Mesh *surface_ = nullptr;
+ Span<MLoopTri> surface_looptris_;
+ Span<float3> corner_normals_su_;
+
+ CurvesSculpt *curves_sculpt_ = nullptr;
+ Brush *brush_ = nullptr;
+
+ float brush_radius_re_;
+ float2 brush_pos_re_;
+
+ bool use_front_face_;
+ bool interpolate_length_;
+ bool interpolate_shape_;
+ bool use_interpolation_;
+ float new_curve_length_;
+ int add_amount_;
+ int points_per_curve_ = 8;
+
+ /** Various matrices to convert between coordinate spaces. */
+ float4x4 curves_to_world_mat_;
+ float4x4 world_to_curves_mat_;
+ float4x4 world_to_surface_mat_;
+ float4x4 surface_to_world_mat_;
+ float4x4 surface_to_curves_mat_;
+ float4x4 surface_to_curves_normal_mat_;
+
+ BVHTreeFromMesh surface_bvh_;
+
+ int tot_old_curves_;
+ int tot_old_points_;
+
+ struct AddedPoints {
+ Vector<float3> positions_cu;
+ Vector<float3> bary_coords;
+ Vector<int> looptri_indices;
+ };
+
+ void execute(AddOperation &self, bContext *C, const StrokeExtension &stroke_extension)
+ {
+ self_ = &self;
+ depsgraph_ = CTX_data_depsgraph_pointer(C);
+ scene_ = CTX_data_scene(C);
+ object_ = CTX_data_active_object(C);
+ region_ = CTX_wm_region(C);
+ v3d_ = CTX_wm_view3d(C);
+
+ curves_id_ = static_cast<Curves *>(object_->data);
+ curves_ = &CurvesGeometry::wrap(curves_id_->geometry);
+
+ if (curves_id_->surface == nullptr || curves_id_->surface->type != OB_MESH) {
+ return;
+ }
+
+ curves_to_world_mat_ = object_->obmat;
+ world_to_curves_mat_ = curves_to_world_mat_.inverted();
+
+ surface_ob_ = curves_id_->surface;
+ surface_ = static_cast<Mesh *>(surface_ob_->data);
+ surface_to_world_mat_ = surface_ob_->obmat;
+ world_to_surface_mat_ = surface_to_world_mat_.inverted();
+ surface_to_curves_mat_ = world_to_curves_mat_ * surface_to_world_mat_;
+ surface_to_curves_normal_mat_ = surface_to_curves_mat_.inverted().transposed();
+
+ if (!CustomData_has_layer(&surface_->ldata, CD_NORMAL)) {
+ BKE_mesh_calc_normals_split(surface_);
+ }
+ corner_normals_su_ = {
+ reinterpret_cast<const float3 *>(CustomData_get_layer(&surface_->ldata, CD_NORMAL)),
+ surface_->totloop};
+
+ curves_sculpt_ = scene_->toolsettings->curves_sculpt;
+ brush_ = BKE_paint_brush(&curves_sculpt_->paint);
+ brush_radius_re_ = BKE_brush_size_get(scene_, brush_);
+ brush_pos_re_ = stroke_extension.mouse_position;
+
+ use_front_face_ = brush_->flag & BRUSH_FRONTFACE;
+ const eBrushFalloffShape falloff_shape = static_cast<eBrushFalloffShape>(
+ brush_->falloff_shape);
+ add_amount_ = std::max(0, brush_->curves_sculpt_settings->add_amount);
+ interpolate_length_ = curves_sculpt_->flag & CURVES_SCULPT_FLAG_INTERPOLATE_LENGTH;
+ interpolate_shape_ = curves_sculpt_->flag & CURVES_SCULPT_FLAG_INTERPOLATE_SHAPE;
+ use_interpolation_ = interpolate_length_ || interpolate_shape_;
+ new_curve_length_ = curves_sculpt_->curve_length;
+
+ tot_old_curves_ = curves_->curves_num();
+ tot_old_points_ = curves_->points_num();
+
+ if (add_amount_ == 0) {
+ return;
+ }
+
+ RandomNumberGenerator rng{(uint32_t)(PIL_check_seconds_timer() * 1000000.0f)};
+
+ BKE_bvhtree_from_mesh_get(&surface_bvh_, surface_, BVHTREE_FROM_LOOPTRI, 2);
+ BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh_); });
+
+ surface_looptris_ = {BKE_mesh_runtime_looptri_ensure(surface_),
+ BKE_mesh_runtime_looptri_len(surface_)};
+
+ /* Sample points on the surface using one of multiple strategies. */
+ AddedPoints added_points;
+ if (add_amount_ == 1) {
+ this->sample_in_center(added_points);
+ }
+ else if (falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) {
+ this->sample_projected(rng, added_points);
+ }
+ else if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) {
+ this->sample_spherical(rng, added_points);
+ }
+ else {
+ BLI_assert_unreachable();
+ }
+
+ if (added_points.bary_coords.is_empty()) {
+ /* No new points have been added. */
+ return;
+ }
+
+ if (use_interpolation_) {
+ this->ensure_curve_roots_kdtree();
+ }
+
+ const int tot_added_curves = added_points.bary_coords.size();
+ const int tot_added_points = tot_added_curves * points_per_curve_;
+
+ curves_->resize(curves_->points_num() + tot_added_points,
+ curves_->curves_num() + tot_added_curves);
+
+ threading::parallel_invoke([&]() { this->initialize_curve_offsets(tot_added_curves); },
+ [&]() { this->initialize_attributes(added_points); });
+
+ DEG_id_tag_update(&curves_id_->id, ID_RECALC_GEOMETRY);
+ ED_region_tag_redraw(region_);
+ }
+
+ float3 get_bary_coords(const Mesh &mesh, const MLoopTri &looptri, const float3 position) const
+ {
+ const float3 &v0 = mesh.mvert[mesh.mloop[looptri.tri[0]].v].co;
+ const float3 &v1 = mesh.mvert[mesh.mloop[looptri.tri[1]].v].co;
+ const float3 &v2 = mesh.mvert[mesh.mloop[looptri.tri[2]].v].co;
+ float3 bary_coords;
+ interp_weights_tri_v3(bary_coords, v0, v1, v2, position);
+ return bary_coords;
+ }
+
+ /**
+ * Sample a single point exactly at the mouse position.
+ */
+ void sample_in_center(AddedPoints &r_added_points)
+ {
+ float3 ray_start_wo, ray_end_wo;
+ ED_view3d_win_to_segment_clipped(
+ depsgraph_, region_, v3d_, brush_pos_re_, ray_start_wo, ray_end_wo, true);
+ const float3 ray_start_su = world_to_surface_mat_ * ray_start_wo;
+ const float3 ray_end_su = world_to_surface_mat_ * ray_end_wo;
+ const float3 ray_direction_su = math::normalize(ray_end_su - ray_start_su);
+
+ BVHTreeRayHit ray_hit;
+ ray_hit.dist = FLT_MAX;
+ ray_hit.index = -1;
+ BLI_bvhtree_ray_cast(surface_bvh_.tree,
+ ray_start_su,
+ ray_direction_su,
+ 0.0f,
+ &ray_hit,
+ surface_bvh_.raycast_callback,
+ &surface_bvh_);
+
+ if (ray_hit.index == -1) {
+ return;
+ }
+
+ const int looptri_index = ray_hit.index;
+ const float3 brush_pos_su = ray_hit.co;
+ const float3 bary_coords = this->get_bary_coords(
+ *surface_, surface_looptris_[looptri_index], brush_pos_su);
+
+ const float3 brush_pos_cu = surface_to_curves_mat_ * brush_pos_su;
+
+ r_added_points.positions_cu.append(brush_pos_cu);
+ r_added_points.bary_coords.append(bary_coords);
+ r_added_points.looptri_indices.append(looptri_index);
+ }
+
+ /**
+ * Sample points by shooting rays within the brush radius in the 3D view.
+ */
+ void sample_projected(RandomNumberGenerator &rng, AddedPoints &r_added_points)
+ {
+ const int max_iterations = std::max(100'000, add_amount_ * 10);
+ int current_iteration = 0;
+ while (r_added_points.bary_coords.size() < add_amount_) {
+ if (current_iteration++ >= max_iterations) {
+ break;
+ }
+
+ const float r = brush_radius_re_ * std::sqrt(rng.get_float());
+ const float angle = rng.get_float() * 2.0f * M_PI;
+ const float2 pos_re = brush_pos_re_ + r * float2(std::cos(angle), std::sin(angle));
+
+ float3 ray_start_wo, ray_end_wo;
+ ED_view3d_win_to_segment_clipped(
+ depsgraph_, region_, v3d_, pos_re, ray_start_wo, ray_end_wo, true);
+ const float3 ray_start_su = world_to_surface_mat_ * ray_start_wo;
+ const float3 ray_end_su = world_to_surface_mat_ * ray_end_wo;
+ const float3 ray_direction_su = math::normalize(ray_end_su - ray_start_su);
+
+ BVHTreeRayHit ray_hit;
+ ray_hit.dist = FLT_MAX;
+ ray_hit.index = -1;
+ BLI_bvhtree_ray_cast(surface_bvh_.tree,
+ ray_start_su,
+ ray_direction_su,
+ 0.0f,
+ &ray_hit,
+ surface_bvh_.raycast_callback,
+ &surface_bvh_);
+
+ if (ray_hit.index == -1) {
+ continue;
+ }
+
+ if (use_front_face_) {
+ const float3 normal_su = ray_hit.no;
+ if (math::dot(ray_direction_su, normal_su) >= 0.0f) {
+ continue;
+ }
+ }
+
+ const int looptri_index = ray_hit.index;
+ const float3 pos_su = ray_hit.co;
+
+ const float3 bary_coords = this->get_bary_coords(
+ *surface_, surface_looptris_[looptri_index], pos_su);
+
+ const float3 pos_cu = surface_to_curves_mat_ * pos_su;
+
+ r_added_points.positions_cu.append(pos_cu);
+ r_added_points.bary_coords.append(bary_coords);
+ r_added_points.looptri_indices.append(looptri_index);
+ }
+ }
+
+ /**
+ * Sample points in a 3D sphere around the surface position that the mouse hovers over.
+ */
+ void sample_spherical(RandomNumberGenerator &rng, AddedPoints &r_added_points)
+ {
+ /* Find ray that starts in the center of the brush. */
+ float3 brush_ray_start_wo, brush_ray_end_wo;
+ ED_view3d_win_to_segment_clipped(
+ depsgraph_, region_, v3d_, brush_pos_re_, brush_ray_start_wo, brush_ray_end_wo, true);
+ const float3 brush_ray_start_su = world_to_surface_mat_ * brush_ray_start_wo;
+ const float3 brush_ray_end_su = world_to_surface_mat_ * brush_ray_end_wo;
+ const float3 brush_ray_direction_su = math::normalize(brush_ray_end_su - brush_ray_start_su);
+
+ /* Find ray that starts on the boundary of the brush. That is used to compute the brush radius
+ * in 3D. */
+ float3 brush_radius_ray_start_wo, brush_radius_ray_end_wo;
+ ED_view3d_win_to_segment_clipped(depsgraph_,
+ region_,
+ v3d_,
+ brush_pos_re_ + float2(brush_radius_re_, 0),
+ brush_radius_ray_start_wo,
+ brush_radius_ray_end_wo,
+ true);
+ const float3 brush_radius_ray_start_su = world_to_surface_mat_ * brush_radius_ray_start_wo;
+ const float3 brush_radius_ray_end_su = world_to_surface_mat_ * brush_radius_ray_end_wo;
+
+ BVHTreeRayHit ray_hit;
+ ray_hit.dist = FLT_MAX;
+ ray_hit.index = -1;
+ BLI_bvhtree_ray_cast(surface_bvh_.tree,
+ brush_ray_start_su,
+ brush_ray_direction_su,
+ 0.0f,
+ &ray_hit,
+ surface_bvh_.raycast_callback,
+ &surface_bvh_);
+
+ if (ray_hit.index == -1) {
+ return;
+ }
+
+ /* Compute brush radius. */
+ const float3 brush_pos_su = ray_hit.co;
+ const float brush_radius_su = dist_to_line_v3(
+ brush_pos_su, brush_radius_ray_start_su, brush_radius_ray_end_su);
+ const float brush_radius_sq_su = pow2f(brush_radius_su);
+
+ /* Find surface triangles within brush radius. */
+ Vector<int> looptri_indices;
+ if (use_front_face_) {
+ BLI_bvhtree_range_query_cpp(
+ *surface_bvh_.tree,
+ brush_pos_su,
+ brush_radius_su,
+ [&](const int index, const float3 &UNUSED(co), const float UNUSED(dist_sq)) {
+ const MLoopTri &looptri = surface_looptris_[index];
+ const float3 v0_su = surface_->mvert[surface_->mloop[looptri.tri[0]].v].co;
+ const float3 v1_su = surface_->mvert[surface_->mloop[looptri.tri[1]].v].co;
+ const float3 v2_su = surface_->mvert[surface_->mloop[looptri.tri[2]].v].co;
+ float3 normal_su;
+ normal_tri_v3(normal_su, v0_su, v1_su, v2_su);
+ if (math::dot(normal_su, brush_ray_direction_su) >= 0.0f) {
+ return;
+ }
+ looptri_indices.append(index);
+ });
+ }
+ else {
+ BLI_bvhtree_range_query_cpp(
+ *surface_bvh_.tree,
+ brush_pos_su,
+ brush_radius_su,
+ [&](const int index, const float3 &UNUSED(co), const float UNUSED(dist_sq)) {
+ looptri_indices.append(index);
+ });
+ }
+
+ /* Density used for sampling points. This does not have to be exact, because the loop below
+ * automatically runs until enough samples have been found. If too many samples are found, some
+ * will be discarded afterwards. */
+ const float brush_plane_area_su = M_PI * brush_radius_sq_su;
+ const float approximate_density_su = add_amount_ / brush_plane_area_su;
+
+ /* Used for switching between two triangle sampling strategies. */
+ const float area_threshold = brush_plane_area_su;
+
+ /* Usually one or two iterations should be enough. */
+ const int max_iterations = 5;
+ int current_iteration = 0;
+
+ while (r_added_points.bary_coords.size() < add_amount_) {
+ if (current_iteration++ >= max_iterations) {
+ break;
+ }
+
+ for (const int looptri_index : looptri_indices) {
+ const MLoopTri &looptri = surface_looptris_[looptri_index];
+
+ const float3 v0_su = surface_->mvert[surface_->mloop[looptri.tri[0]].v].co;
+ const float3 v1_su = surface_->mvert[surface_->mloop[looptri.tri[1]].v].co;
+ const float3 v2_su = surface_->mvert[surface_->mloop[looptri.tri[2]].v].co;
+
+ const float looptri_area_su = area_tri_v3(v0_su, v1_su, v2_su);
+
+ if (looptri_area_su < area_threshold) {
+ /* The triangle is small compared to the brush radius. Sample by generating random
+ * barycentric coordinates. */
+ const int amount = rng.round_probabilistic(approximate_density_su * looptri_area_su);
+ for ([[maybe_unused]] const int i : IndexRange(amount)) {
+ const float3 bary_coord = rng.get_barycentric_coordinates();
+ const float3 point_pos_su = attribute_math::mix3(bary_coord, v0_su, v1_su, v2_su);
+ const float distance_to_brush_sq_su = math::distance_squared(point_pos_su,
+ brush_pos_su);
+ if (distance_to_brush_sq_su > brush_radius_sq_su) {
+ continue;
+ }
+
+ r_added_points.bary_coords.append(bary_coord);
+ r_added_points.looptri_indices.append(looptri_index);
+ r_added_points.positions_cu.append(surface_to_curves_mat_ * point_pos_su);
+ }
+ }
+ else {
+ /* The triangle is large compared to the brush radius. Sample by generating random points
+ * on the triangle plane within the brush radius. */
+ float3 normal_su;
+ normal_tri_v3(normal_su, v0_su, v1_su, v2_su);
+
+ float3 brush_pos_proj_su = brush_pos_su;
+ project_v3_plane(brush_pos_proj_su, normal_su, v0_su);
+
+ const float proj_distance_sq_su = math::distance_squared(brush_pos_proj_su,
+ brush_pos_su);
+ const float brush_radius_factor_sq = 1.0f -
+ std::min(1.0f,
+ proj_distance_sq_su / brush_radius_sq_su);
+ const float radius_proj_sq_su = brush_radius_sq_su * brush_radius_factor_sq;
+ const float radius_proj_su = std::sqrt(radius_proj_sq_su);
+ const float circle_area_su = M_PI * radius_proj_su;
+
+ const int amount = rng.round_probabilistic(approximate_density_su * circle_area_su);
+
+ const float3 axis_1_su = math::normalize(v1_su - v0_su) * radius_proj_su;
+ const float3 axis_2_su = math::normalize(math::cross(
+ axis_1_su, math::cross(axis_1_su, v2_su - v0_su))) *
+ radius_proj_su;
+
+ for ([[maybe_unused]] const int i : IndexRange(amount)) {
+ const float r = std::sqrt(rng.get_float());
+ const float angle = rng.get_float() * 2.0f * M_PI;
+ const float x = r * std::cos(angle);
+ const float y = r * std::sin(angle);
+ const float3 point_pos_su = brush_pos_proj_su + axis_1_su * x + axis_2_su * y;
+ if (!isect_point_tri_prism_v3(point_pos_su, v0_su, v1_su, v2_su)) {
+ /* Sampled point is not in the triangle. */
+ continue;
+ }
+
+ float3 bary_coord;
+ interp_weights_tri_v3(bary_coord, v0_su, v1_su, v2_su, point_pos_su);
+
+ r_added_points.bary_coords.append(bary_coord);
+ r_added_points.looptri_indices.append(looptri_index);
+ r_added_points.positions_cu.append(surface_to_curves_mat_ * point_pos_su);
+ }
+ }
+ }
+ }
+
+ /* Remove samples when there are too many. */
+ while (r_added_points.bary_coords.size() > add_amount_) {
+ const int index_to_remove = rng.get_int32(r_added_points.bary_coords.size());
+ r_added_points.bary_coords.remove_and_reorder(index_to_remove);
+ r_added_points.looptri_indices.remove_and_reorder(index_to_remove);
+ r_added_points.positions_cu.remove_and_reorder(index_to_remove);
+ }
+ }
+
+ void ensure_curve_roots_kdtree()
+ {
+ if (self_->curve_roots_kdtree_ == nullptr) {
+ self_->curve_roots_kdtree_ = BLI_kdtree_3d_new(curves_->curves_num());
+ for (const int curve_i : curves_->curves_range()) {
+ const int root_point_i = curves_->offsets()[curve_i];
+ const float3 &root_pos_cu = curves_->positions()[root_point_i];
+ BLI_kdtree_3d_insert(self_->curve_roots_kdtree_, curve_i, root_pos_cu);
+ }
+ BLI_kdtree_3d_balance(self_->curve_roots_kdtree_);
+ }
+ }
+
+ void initialize_curve_offsets(const int tot_added_curves)
+ {
+ MutableSpan<int> offsets = curves_->offsets();
+ threading::parallel_for(IndexRange(tot_added_curves), 1024, [&](const IndexRange range) {
+ for (const int i : range) {
+ const int curve_i = tot_old_curves_ + i;
+ offsets[curve_i + 1] = tot_old_points_ + (i + 1) * points_per_curve_;
+ }
+ });
+ }
+
+ struct NeighborInfo {
+ /* Curve index of the neighbor. */
+ int index;
+ /* The weights of all neighbors of a new curve add up to 1. */
+ float weight;
+ };
+ static constexpr int max_neighbors = 5;
+ using NeighborsVector = Vector<NeighborInfo, max_neighbors>;
+
+ void initialize_attributes(const AddedPoints &added_points)
+ {
+ Array<NeighborsVector> neighbors_per_curve;
+ if (use_interpolation_) {
+ neighbors_per_curve = this->find_curve_neighbors(added_points);
+ }
+
+ Array<float> new_lengths_cu(added_points.bary_coords.size());
+ if (interpolate_length_) {
+ this->interpolate_lengths(neighbors_per_curve, new_lengths_cu);
+ }
+ else {
+ new_lengths_cu.fill(new_curve_length_);
+ }
+
+ Array<float3> new_normals_su = this->compute_normals_for_added_curves_su(added_points);
+ this->initialize_surface_attachment(added_points);
+
+ if (interpolate_shape_) {
+ this->initialize_position_with_interpolation(
+ added_points, neighbors_per_curve, new_normals_su, new_lengths_cu);
+ }
+ else {
+ this->initialize_position_without_interpolation(
+ added_points, new_lengths_cu, new_normals_su);
+ }
+ }
+
+ Array<NeighborsVector> find_curve_neighbors(const AddedPoints &added_points)
+ {
+ const int tot_added_curves = added_points.bary_coords.size();
+ Array<NeighborsVector> neighbors_per_curve(tot_added_curves);
+ threading::parallel_for(IndexRange(tot_added_curves), 128, [&](const IndexRange range) {
+ for (const int i : range) {
+ const float3 root_cu = added_points.positions_cu[i];
+ std::array<KDTreeNearest_3d, max_neighbors> nearest_n;
+ const int found_neighbors = BLI_kdtree_3d_find_nearest_n(
+ self_->curve_roots_kdtree_, root_cu, nearest_n.data(), max_neighbors);
+ float tot_weight = 0.0f;
+ for (const int neighbor_i : IndexRange(found_neighbors)) {
+ KDTreeNearest_3d &nearest = nearest_n[neighbor_i];
+ const float weight = 1.0f / std::max(nearest.dist, 0.00001f);
+ tot_weight += weight;
+ neighbors_per_curve[i].append({nearest.index, weight});
+ }
+ /* Normalize weights. */
+ for (NeighborInfo &neighbor : neighbors_per_curve[i]) {
+ neighbor.weight /= tot_weight;
+ }
+ }
+ });
+ return neighbors_per_curve;
+ }
+
+ void interpolate_lengths(const Span<NeighborsVector> neighbors_per_curve,
+ MutableSpan<float> r_lengths)
+ {
+ const Span<float3> positions_cu = curves_->positions();
+
+ threading::parallel_for(r_lengths.index_range(), 128, [&](const IndexRange range) {
+ for (const int added_curve_i : range) {
+ const Span<NeighborInfo> neighbors = neighbors_per_curve[added_curve_i];
+ float length_sum = 0.0f;
+ for (const NeighborInfo &neighbor : neighbors) {
+ const IndexRange neighbor_points = curves_->points_for_curve(neighbor.index);
+ float neighbor_length = 0.0f;
+ const int tot_segments = neighbor_points.size() - 1;
+ for (const int segment_i : IndexRange(tot_segments)) {
+ const float3 &p1 = positions_cu[neighbor_points[segment_i]];
+ const float3 &p2 = positions_cu[neighbor_points[segment_i] + 1];
+ neighbor_length += math::distance(p1, p2);
+ }
+ length_sum += neighbor.weight * neighbor_length;
+ }
+ const float length = neighbors.is_empty() ? new_curve_length_ : length_sum;
+ r_lengths[added_curve_i] = length;
+ }
+ });
+ }
+
+ float3 compute_point_normal_su(const int looptri_index, const float3 &bary_coord)
+ {
+ const MLoopTri &looptri = surface_looptris_[looptri_index];
+ const int l0 = looptri.tri[0];
+ const int l1 = looptri.tri[1];
+ const int l2 = looptri.tri[2];
+
+ const float3 &l0_normal_su = corner_normals_su_[l0];
+ const float3 &l1_normal_su = corner_normals_su_[l1];
+ const float3 &l2_normal_su = corner_normals_su_[l2];
+
+ const float3 normal_su = math::normalize(
+ attribute_math::mix3(bary_coord, l0_normal_su, l1_normal_su, l2_normal_su));
+ return normal_su;
+ }
+
+ Array<float3> compute_normals_for_added_curves_su(const AddedPoints &added_points)
+ {
+ Array<float3> normals_su(added_points.bary_coords.size());
+ threading::parallel_for(normals_su.index_range(), 256, [&](const IndexRange range) {
+ for (const int i : range) {
+ const int looptri_index = added_points.looptri_indices[i];
+ const float3 &bary_coord = added_points.bary_coords[i];
+ normals_su[i] = this->compute_point_normal_su(looptri_index, bary_coord);
+ }
+ });
+ return normals_su;
+ }
+
+ void initialize_surface_attachment(const AddedPoints &added_points)
+ {
+ MutableSpan<int> surface_triangle_indices = curves_->surface_triangle_indices();
+ MutableSpan<float2> surface_triangle_coords = curves_->surface_triangle_coords();
+ threading::parallel_for(
+ added_points.bary_coords.index_range(), 1024, [&](const IndexRange range) {
+ for (const int i : range) {
+ const int curve_i = tot_old_curves_ + i;
+ surface_triangle_indices[curve_i] = added_points.looptri_indices[i];
+ surface_triangle_coords[curve_i] = float2(added_points.bary_coords[i]);
+ }
+ });
+ }
+
+ /**
+ * Initialize new curves so that they are just a straight line in the normal direction.
+ */
+ void initialize_position_without_interpolation(const AddedPoints &added_points,
+ const Span<float> lengths_cu,
+ const MutableSpan<float3> normals_su)
+ {
+ MutableSpan<float3> positions_cu = curves_->positions();
+
+ threading::parallel_for(
+ added_points.bary_coords.index_range(), 256, [&](const IndexRange range) {
+ for (const int i : range) {
+ const int first_point_i = tot_old_points_ + i * points_per_curve_;
+ const float3 &root_cu = added_points.positions_cu[i];
+ const float length = lengths_cu[i];
+ const float3 &normal_su = normals_su[i];
+ const float3 normal_cu = math::normalize(surface_to_curves_normal_mat_ * normal_su);
+ const float3 tip_cu = root_cu + length * normal_cu;
+
+ initialize_straight_curve_positions(
+ root_cu, tip_cu, positions_cu.slice(first_point_i, points_per_curve_));
+ }
+ });
+ }
+
+ /**
+ * Use neighboring curves to determine the shape.
+ */
+ void initialize_position_with_interpolation(const AddedPoints &added_points,
+ const Span<NeighborsVector> neighbors_per_curve,
+ const Span<float3> new_normals_su,
+ const Span<float> new_lengths_cu)
+ {
+ MutableSpan<float3> positions_cu = curves_->positions();
+ const Span<int> surface_triangle_indices = curves_->surface_triangle_indices();
+ const Span<float2> surface_triangle_coords = curves_->surface_triangle_coords();
+
+ threading::parallel_for(
+ added_points.bary_coords.index_range(), 256, [&](const IndexRange range) {
+ for (const int i : range) {
+ const Span<NeighborInfo> neighbors = neighbors_per_curve[i];
+
+ const float length_cu = new_lengths_cu[i];
+ const float3 &normal_su = new_normals_su[i];
+ const float3 normal_cu = math::normalize(surface_to_curves_normal_mat_ * normal_su);
+
+ const float3 &root_cu = added_points.positions_cu[i];
+ const int first_point_i = tot_old_points_ + i * points_per_curve_;
+
+ if (neighbors.is_empty()) {
+ /* If there are no neighbors, just make a straight line. */
+ const float3 tip_cu = root_cu + length_cu * normal_cu;
+ initialize_straight_curve_positions(
+ root_cu, tip_cu, positions_cu.slice(first_point_i, points_per_curve_));
+ continue;
+ }
+
+ positions_cu.slice(first_point_i, points_per_curve_).fill(root_cu);
+
+ for (const NeighborInfo &neighbor : neighbors) {
+ const int neighbor_curve_i = neighbor.index;
+ const int neighbor_looptri_index = surface_triangle_indices[neighbor_curve_i];
+
+ float3 neighbor_bary_coord{surface_triangle_coords[neighbor_curve_i]};
+ neighbor_bary_coord.z = 1.0f - neighbor_bary_coord.x - neighbor_bary_coord.y;
+
+ const float3 neighbor_normal_su = this->compute_point_normal_su(
+ neighbor_looptri_index, neighbor_bary_coord);
+ const float3 neighbor_normal_cu = math::normalize(surface_to_curves_normal_mat_ *
+ neighbor_normal_su);
+
+ /* The rotation matrix used to transform relative coordinates of the neighbor curve
+ * to the new curve. */
+ float normal_rotation_cu[3][3];
+ rotation_between_vecs_to_mat3(normal_rotation_cu, neighbor_normal_cu, normal_cu);
+
+ const IndexRange neighbor_points = curves_->points_for_curve(neighbor_curve_i);
+ const float3 &neighbor_root_cu = positions_cu[neighbor_points[0]];
+
+ /* Use a temporary #PolySpline, because that's the easiest way to resample an
+ * existing curve right now. Resampling is necessary if the length of the new curve
+ * does not match the length of the neighbors or the number of handle points is
+ * different. */
+ PolySpline neighbor_spline;
+ neighbor_spline.resize(neighbor_points.size());
+ neighbor_spline.positions().copy_from(positions_cu.slice(neighbor_points));
+ neighbor_spline.mark_cache_invalid();
+
+ const float neighbor_length_cu = neighbor_spline.length();
+ const float length_factor = std::min(1.0f, length_cu / neighbor_length_cu);
+
+ const float resample_factor = (1.0f / (points_per_curve_ - 1.0f)) * length_factor;
+ for (const int j : IndexRange(points_per_curve_)) {
+ const Spline::LookupResult lookup = neighbor_spline.lookup_evaluated_factor(
+ j * resample_factor);
+ const float index_factor = lookup.evaluated_index + lookup.factor;
+ float3 p;
+ neighbor_spline.sample_with_index_factors<float3>(
+ neighbor_spline.positions(), {&index_factor, 1}, {&p, 1});
+ const float3 relative_coord = p - neighbor_root_cu;
+ float3 rotated_relative_coord = relative_coord;
+ mul_m3_v3(normal_rotation_cu, rotated_relative_coord);
+ positions_cu[first_point_i + j] += neighbor.weight * rotated_relative_coord;
+ }
+ }
+ }
+ });
+ }
+};
+
+void AddOperation::on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension)
+{
+ AddOperationExecutor executor;
+ executor.execute(*this, C, stroke_extension);
+}
+
+std::unique_ptr<CurvesSculptStrokeOperation> new_add_operation()
+{
+ return std::make_unique<AddOperation>();
+}
+
+} // namespace blender::ed::sculpt_paint
diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_comb.cc b/source/blender/editors/sculpt_paint/curves_sculpt_comb.cc
new file mode 100644
index 00000000000..d062fe32cfe
--- /dev/null
+++ b/source/blender/editors/sculpt_paint/curves_sculpt_comb.cc
@@ -0,0 +1,377 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include <algorithm>
+
+#include "curves_sculpt_intern.hh"
+
+#include "BLI_float4x4.hh"
+#include "BLI_index_mask_ops.hh"
+#include "BLI_kdtree.h"
+#include "BLI_rand.hh"
+#include "BLI_vector.hh"
+
+#include "PIL_time.h"
+
+#include "DEG_depsgraph.h"
+
+#include "BKE_attribute_math.hh"
+#include "BKE_brush.h"
+#include "BKE_bvhutils.h"
+#include "BKE_context.h"
+#include "BKE_curves.hh"
+#include "BKE_mesh.h"
+#include "BKE_mesh_runtime.h"
+#include "BKE_paint.h"
+
+#include "DNA_brush_enums.h"
+#include "DNA_brush_types.h"
+#include "DNA_curves_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_object_types.h"
+#include "DNA_screen_types.h"
+#include "DNA_space_types.h"
+
+#include "ED_screen.h"
+#include "ED_view3d.h"
+
+#include "UI_interface.h"
+
+/**
+ * The code below uses a prefix naming convention to indicate the coordinate space:
+ * cu: Local space of the curves object that is being edited.
+ * su: Local space of the surface object.
+ * wo: World space.
+ * re: 2D coordinates within the region.
+ */
+
+namespace blender::ed::sculpt_paint {
+
+using blender::bke::CurvesGeometry;
+using threading::EnumerableThreadSpecific;
+
+/**
+ * Moves individual points under the brush and does a length preservation step afterwards.
+ */
+class CombOperation : public CurvesSculptStrokeOperation {
+ private:
+ /** Last mouse position. */
+ float2 brush_pos_last_re_;
+
+ /** Only used when a 3D brush is used. */
+ CurvesBrush3D brush_3d_;
+
+ /** Length of each segment indexed by the index of the first point in the segment. */
+ Array<float> segment_lengths_cu_;
+
+ friend struct CombOperationExecutor;
+
+ public:
+ void on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) override;
+};
+
+/**
+ * Utility class that actually executes the update when the stroke is updated. That's useful
+ * because it avoids passing a very large number of parameters between functions.
+ */
+struct CombOperationExecutor {
+ CombOperation *self_ = nullptr;
+ bContext *C_ = nullptr;
+ Depsgraph *depsgraph_ = nullptr;
+ Scene *scene_ = nullptr;
+ Object *object_ = nullptr;
+ ARegion *region_ = nullptr;
+ View3D *v3d_ = nullptr;
+ RegionView3D *rv3d_ = nullptr;
+
+ CurvesSculpt *curves_sculpt_ = nullptr;
+ Brush *brush_ = nullptr;
+ float brush_radius_re_;
+ float brush_strength_;
+
+ eBrushFalloffShape falloff_shape_;
+
+ Curves *curves_id_ = nullptr;
+ CurvesGeometry *curves_ = nullptr;
+
+ const Object *surface_ob_ = nullptr;
+ const Mesh *surface_ = nullptr;
+ Span<MLoopTri> surface_looptris_;
+
+ float2 brush_pos_prev_re_;
+ float2 brush_pos_re_;
+ float2 brush_pos_diff_re_;
+ float brush_pos_diff_length_re_;
+
+ float4x4 curves_to_world_mat_;
+ float4x4 world_to_curves_mat_;
+ float4x4 surface_to_world_mat_;
+ float4x4 world_to_surface_mat_;
+
+ BVHTreeFromMesh surface_bvh_;
+
+ void execute(CombOperation &self, bContext *C, const StrokeExtension &stroke_extension)
+ {
+ self_ = &self;
+
+ BLI_SCOPED_DEFER([&]() { self_->brush_pos_last_re_ = stroke_extension.mouse_position; });
+
+ C_ = C;
+ depsgraph_ = CTX_data_depsgraph_pointer(C);
+ scene_ = CTX_data_scene(C);
+ object_ = CTX_data_active_object(C);
+ region_ = CTX_wm_region(C);
+ v3d_ = CTX_wm_view3d(C);
+ rv3d_ = CTX_wm_region_view3d(C);
+
+ curves_sculpt_ = scene_->toolsettings->curves_sculpt;
+ brush_ = BKE_paint_brush(&curves_sculpt_->paint);
+ brush_radius_re_ = BKE_brush_size_get(scene_, brush_);
+ brush_strength_ = BKE_brush_alpha_get(scene_, brush_);
+
+ curves_to_world_mat_ = object_->obmat;
+ world_to_curves_mat_ = curves_to_world_mat_.inverted();
+
+ falloff_shape_ = static_cast<eBrushFalloffShape>(brush_->falloff_shape);
+
+ curves_id_ = static_cast<Curves *>(object_->data);
+ curves_ = &CurvesGeometry::wrap(curves_id_->geometry);
+
+ brush_pos_prev_re_ = self_->brush_pos_last_re_;
+ brush_pos_re_ = stroke_extension.mouse_position;
+ brush_pos_diff_re_ = brush_pos_re_ - brush_pos_prev_re_;
+ brush_pos_diff_length_re_ = math::length(brush_pos_diff_re_);
+
+ surface_ob_ = curves_id_->surface;
+ if (surface_ob_ != nullptr) {
+ surface_ = static_cast<const Mesh *>(surface_ob_->data);
+ surface_looptris_ = {BKE_mesh_runtime_looptri_ensure(surface_),
+ BKE_mesh_runtime_looptri_len(surface_)};
+ surface_to_world_mat_ = surface_ob_->obmat;
+ world_to_surface_mat_ = surface_to_world_mat_.inverted();
+ BKE_bvhtree_from_mesh_get(&surface_bvh_, surface_, BVHTREE_FROM_LOOPTRI, 2);
+ }
+
+ BLI_SCOPED_DEFER([&]() {
+ if (surface_ob_ != nullptr) {
+ free_bvhtree_from_mesh(&surface_bvh_);
+ }
+ });
+
+ if (stroke_extension.is_first) {
+ if (falloff_shape_ == PAINT_FALLOFF_SHAPE_SPHERE) {
+ this->initialize_spherical_brush_reference_point();
+ }
+ this->initialize_segment_lengths();
+ /* Combing does nothing when there is no mouse movement, so return directly. */
+ return;
+ }
+
+ EnumerableThreadSpecific<Vector<int>> changed_curves;
+
+ if (falloff_shape_ == PAINT_FALLOFF_SHAPE_TUBE) {
+ this->comb_projected(changed_curves);
+ }
+ else if (falloff_shape_ == PAINT_FALLOFF_SHAPE_SPHERE) {
+ this->comb_spherical(changed_curves);
+ }
+ else {
+ BLI_assert_unreachable();
+ }
+
+ this->restore_segment_lengths(changed_curves);
+
+ curves_->tag_positions_changed();
+ DEG_id_tag_update(&curves_id_->id, ID_RECALC_GEOMETRY);
+ ED_region_tag_redraw(region_);
+ }
+
+ /**
+ * Do combing in screen space.
+ */
+ void comb_projected(EnumerableThreadSpecific<Vector<int>> &r_changed_curves)
+ {
+ MutableSpan<float3> positions_cu = curves_->positions();
+
+ float4x4 projection;
+ ED_view3d_ob_project_mat_get(rv3d_, object_, projection.values);
+
+ const float brush_radius_sq_re = pow2f(brush_radius_re_);
+
+ threading::parallel_for(curves_->curves_range(), 256, [&](const IndexRange curves_range) {
+ Vector<int> &local_changed_curves = r_changed_curves.local();
+ for (const int curve_i : curves_range) {
+ bool curve_changed = false;
+ const IndexRange points = curves_->points_for_curve(curve_i);
+ for (const int point_i : points.drop_front(1)) {
+ const float3 old_pos_cu = positions_cu[point_i];
+
+ /* Find the position of the point in screen space. */
+ float2 old_pos_re;
+ ED_view3d_project_float_v2_m4(region_, old_pos_cu, old_pos_re, projection.values);
+
+ const float distance_to_brush_sq_re = dist_squared_to_line_segment_v2(
+ old_pos_re, brush_pos_prev_re_, brush_pos_re_);
+ if (distance_to_brush_sq_re > brush_radius_sq_re) {
+ /* Ignore the point because it's too far away. */
+ continue;
+ }
+
+ const float distance_to_brush_re = std::sqrt(distance_to_brush_sq_re);
+ /* A falloff that is based on how far away the point is from the stroke. */
+ const float radius_falloff = BKE_brush_curve_strength(
+ brush_, distance_to_brush_re, brush_radius_re_);
+ /* Combine the falloff and brush strength. */
+ const float weight = brush_strength_ * radius_falloff;
+
+ /* Offset the old point position in screen space and transform it back into 3D space. */
+ const float2 new_position_re = old_pos_re + brush_pos_diff_re_ * weight;
+ float3 new_position_wo;
+ ED_view3d_win_to_3d(
+ v3d_, region_, curves_to_world_mat_ * old_pos_cu, new_position_re, new_position_wo);
+ const float3 new_position_cu = world_to_curves_mat_ * new_position_wo;
+ positions_cu[point_i] = new_position_cu;
+
+ curve_changed = true;
+ }
+ if (curve_changed) {
+ local_changed_curves.append(curve_i);
+ }
+ }
+ });
+ }
+
+ /**
+ * Do combing in 3D space.
+ */
+ void comb_spherical(EnumerableThreadSpecific<Vector<int>> &r_changed_curves)
+ {
+ MutableSpan<float3> positions_cu = curves_->positions();
+
+ float4x4 projection;
+ ED_view3d_ob_project_mat_get(rv3d_, object_, projection.values);
+
+ float3 brush_start_wo, brush_end_wo;
+ ED_view3d_win_to_3d(v3d_,
+ region_,
+ curves_to_world_mat_ * self_->brush_3d_.position_cu,
+ brush_pos_prev_re_,
+ brush_start_wo);
+ ED_view3d_win_to_3d(v3d_,
+ region_,
+ curves_to_world_mat_ * self_->brush_3d_.position_cu,
+ brush_pos_re_,
+ brush_end_wo);
+ const float3 brush_start_cu = world_to_curves_mat_ * brush_start_wo;
+ const float3 brush_end_cu = world_to_curves_mat_ * brush_end_wo;
+
+ const float3 brush_diff_cu = brush_end_cu - brush_start_cu;
+
+ const float brush_radius_cu = self_->brush_3d_.radius_cu;
+ const float brush_radius_sq_cu = pow2f(brush_radius_cu);
+
+ threading::parallel_for(curves_->curves_range(), 256, [&](const IndexRange curves_range) {
+ Vector<int> &local_changed_curves = r_changed_curves.local();
+ for (const int curve_i : curves_range) {
+ bool curve_changed = false;
+ const IndexRange points = curves_->points_for_curve(curve_i);
+ for (const int point_i : points.drop_front(1)) {
+ const float3 pos_old_cu = positions_cu[point_i];
+
+ /* Compute distance to the brush. */
+ const float distance_to_brush_sq_cu = dist_squared_to_line_segment_v3(
+ pos_old_cu, brush_start_cu, brush_end_cu);
+ if (distance_to_brush_sq_cu > brush_radius_sq_cu) {
+ /* Ignore the point because it's too far away. */
+ continue;
+ }
+
+ const float distance_to_brush_cu = std::sqrt(distance_to_brush_sq_cu);
+
+ /* A falloff that is based on how far away the point is from the stroke. */
+ const float radius_falloff = BKE_brush_curve_strength(
+ brush_, distance_to_brush_cu, brush_radius_cu);
+ /* Combine the falloff and brush strength. */
+ const float weight = brush_strength_ * radius_falloff;
+
+ /* Update the point position. */
+ positions_cu[point_i] = pos_old_cu + weight * brush_diff_cu;
+ curve_changed = true;
+ }
+ if (curve_changed) {
+ local_changed_curves.append(curve_i);
+ }
+ }
+ });
+ }
+
+ /**
+ * Sample depth under mouse by looking at curves and the surface.
+ */
+ void initialize_spherical_brush_reference_point()
+ {
+ std::optional<CurvesBrush3D> brush_3d = sample_curves_3d_brush(
+ *C_, *object_, brush_pos_re_, brush_radius_re_);
+ if (brush_3d.has_value()) {
+ self_->brush_3d_ = *brush_3d;
+ }
+ }
+
+ /**
+ * Remember the initial length of all curve segments. This allows restoring the length after
+ * combing.
+ */
+ void initialize_segment_lengths()
+ {
+ const Span<float3> positions_cu = curves_->positions();
+ self_->segment_lengths_cu_.reinitialize(curves_->points_num());
+ threading::parallel_for(curves_->curves_range(), 128, [&](const IndexRange range) {
+ for (const int curve_i : range) {
+ const IndexRange points = curves_->points_for_curve(curve_i);
+ for (const int point_i : points.drop_back(1)) {
+ const float3 &p1_cu = positions_cu[point_i];
+ const float3 &p2_cu = positions_cu[point_i + 1];
+ const float length_cu = math::distance(p1_cu, p2_cu);
+ self_->segment_lengths_cu_[point_i] = length_cu;
+ }
+ }
+ });
+ }
+
+ /**
+ * Restore previously stored length for each segment in the changed curves.
+ */
+ void restore_segment_lengths(EnumerableThreadSpecific<Vector<int>> &changed_curves)
+ {
+ const Span<float> expected_lengths_cu = self_->segment_lengths_cu_;
+ MutableSpan<float3> positions_cu = curves_->positions();
+
+ threading::parallel_for_each(changed_curves, [&](const Vector<int> &changed_curves) {
+ threading::parallel_for(changed_curves.index_range(), 256, [&](const IndexRange range) {
+ for (const int curve_i : changed_curves.as_span().slice(range)) {
+ const IndexRange points = curves_->points_for_curve(curve_i);
+ for (const int segment_i : IndexRange(points.size() - 1)) {
+ const float3 &p1_cu = positions_cu[points[segment_i]];
+ float3 &p2_cu = positions_cu[points[segment_i] + 1];
+ const float3 direction = math::normalize(p2_cu - p1_cu);
+ const float expected_length_cu = expected_lengths_cu[points[segment_i]];
+ p2_cu = p1_cu + direction * expected_length_cu;
+ }
+ }
+ });
+ });
+ }
+};
+
+void CombOperation::on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension)
+{
+ CombOperationExecutor executor;
+ executor.execute(*this, C, stroke_extension);
+}
+
+std::unique_ptr<CurvesSculptStrokeOperation> new_comb_operation()
+{
+ return std::make_unique<CombOperation>();
+}
+
+} // namespace blender::ed::sculpt_paint
diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_delete.cc b/source/blender/editors/sculpt_paint/curves_sculpt_delete.cc
new file mode 100644
index 00000000000..ae87f414dd5
--- /dev/null
+++ b/source/blender/editors/sculpt_paint/curves_sculpt_delete.cc
@@ -0,0 +1,105 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include <algorithm>
+
+#include "curves_sculpt_intern.hh"
+
+#include "BLI_float4x4.hh"
+#include "BLI_index_mask_ops.hh"
+#include "BLI_kdtree.h"
+#include "BLI_rand.hh"
+#include "BLI_vector.hh"
+
+#include "PIL_time.h"
+
+#include "DEG_depsgraph.h"
+
+#include "BKE_attribute_math.hh"
+#include "BKE_brush.h"
+#include "BKE_bvhutils.h"
+#include "BKE_context.h"
+#include "BKE_curves.hh"
+#include "BKE_mesh.h"
+#include "BKE_mesh_runtime.h"
+#include "BKE_paint.h"
+
+#include "DNA_brush_enums.h"
+#include "DNA_brush_types.h"
+#include "DNA_curves_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_object_types.h"
+#include "DNA_screen_types.h"
+#include "DNA_space_types.h"
+
+#include "ED_screen.h"
+#include "ED_view3d.h"
+
+namespace blender::ed::sculpt_paint {
+
+using blender::bke::CurvesGeometry;
+
+class DeleteOperation : public CurvesSculptStrokeOperation {
+ private:
+ float2 last_mouse_position_;
+
+ public:
+ void on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) override
+ {
+ Scene &scene = *CTX_data_scene(C);
+ Object &object = *CTX_data_active_object(C);
+ ARegion *region = CTX_wm_region(C);
+ RegionView3D *rv3d = CTX_wm_region_view3d(C);
+
+ CurvesSculpt &curves_sculpt = *scene.toolsettings->curves_sculpt;
+ Brush &brush = *BKE_paint_brush(&curves_sculpt.paint);
+ const float brush_radius = BKE_brush_size_get(&scene, &brush);
+
+ float4x4 projection;
+ ED_view3d_ob_project_mat_get(rv3d, &object, projection.values);
+
+ Curves &curves_id = *static_cast<Curves *>(object.data);
+ CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry);
+ MutableSpan<float3> positions = curves.positions();
+
+ const float2 mouse_start = stroke_extension.is_first ? stroke_extension.mouse_position :
+ last_mouse_position_;
+ const float2 mouse_end = stroke_extension.mouse_position;
+
+ /* Find indices of curves that have to be removed. */
+ Vector<int64_t> indices;
+ const IndexMask curves_to_remove = index_mask_ops::find_indices_based_on_predicate(
+ curves.curves_range(), 512, indices, [&](const int curve_i) {
+ const IndexRange point_range = curves.points_for_curve(curve_i);
+ for (const int segment_i : IndexRange(point_range.size() - 1)) {
+ const float3 pos1 = positions[point_range[segment_i]];
+ const float3 pos2 = positions[point_range[segment_i + 1]];
+
+ float2 pos1_proj, pos2_proj;
+ ED_view3d_project_float_v2_m4(region, pos1, pos1_proj, projection.values);
+ ED_view3d_project_float_v2_m4(region, pos2, pos2_proj, projection.values);
+
+ const float dist = dist_seg_seg_v2(pos1_proj, pos2_proj, mouse_start, mouse_end);
+ if (dist <= brush_radius) {
+ return true;
+ }
+ }
+ return false;
+ });
+
+ curves.remove_curves(curves_to_remove);
+
+ curves.tag_positions_changed();
+ DEG_id_tag_update(&curves_id.id, ID_RECALC_GEOMETRY);
+ ED_region_tag_redraw(region);
+
+ last_mouse_position_ = stroke_extension.mouse_position;
+ }
+};
+
+std::unique_ptr<CurvesSculptStrokeOperation> new_delete_operation()
+{
+ return std::make_unique<DeleteOperation>();
+}
+
+} // namespace blender::ed::sculpt_paint
diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh b/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh
new file mode 100644
index 00000000000..d021627921f
--- /dev/null
+++ b/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh
@@ -0,0 +1,53 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+#include <optional>
+
+#include "curves_sculpt_intern.h"
+
+#include "BLI_math_vector.hh"
+
+#include "BKE_curves.hh"
+
+struct ARegion;
+struct RegionView3D;
+struct Object;
+
+namespace blender::ed::sculpt_paint {
+
+using bke::CurvesGeometry;
+
+struct StrokeExtension {
+ bool is_first;
+ float2 mouse_position;
+};
+
+/**
+ * Base class for stroke based operations in curves sculpt mode.
+ */
+class CurvesSculptStrokeOperation {
+ public:
+ virtual ~CurvesSculptStrokeOperation() = default;
+ virtual void on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) = 0;
+};
+
+std::unique_ptr<CurvesSculptStrokeOperation> new_add_operation();
+std::unique_ptr<CurvesSculptStrokeOperation> new_comb_operation();
+std::unique_ptr<CurvesSculptStrokeOperation> new_delete_operation();
+std::unique_ptr<CurvesSculptStrokeOperation> new_snake_hook_operation();
+
+struct CurvesBrush3D {
+ float3 position_cu;
+ float radius_cu;
+};
+
+/**
+ * Find 3d brush position based on cursor position for curves sculpting.
+ */
+std::optional<CurvesBrush3D> sample_curves_3d_brush(bContext &C,
+ Object &curves_object,
+ const float2 &brush_pos_re,
+ float brush_radius_re);
+
+} // namespace blender::ed::sculpt_paint
diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc
index 63202f3902a..382f0529daa 100644
--- a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc
+++ b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc
@@ -40,6 +40,7 @@
#include "PIL_time.h"
#include "curves_sculpt_intern.h"
+#include "curves_sculpt_intern.hh"
#include "paint_intern.h"
/* -------------------------------------------------------------------- */
@@ -68,318 +69,11 @@ bool CURVES_SCULPT_mode_poll_view3d(bContext *C)
namespace blender::ed::sculpt_paint {
using blender::bke::CurvesGeometry;
-using blender::fn::CPPType;
/* -------------------------------------------------------------------- */
/** \name * SCULPT_CURVES_OT_brush_stroke
* \{ */
-struct StrokeExtension {
- bool is_first;
- float2 mouse_position;
-};
-
-/**
- * Base class for stroke based operations in curves sculpt mode.
- */
-class CurvesSculptStrokeOperation {
- public:
- virtual ~CurvesSculptStrokeOperation() = default;
- virtual void on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) = 0;
-};
-
-class DeleteOperation : public CurvesSculptStrokeOperation {
- private:
- float2 last_mouse_position_;
-
- public:
- void on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) override
- {
- Scene &scene = *CTX_data_scene(C);
- Object &object = *CTX_data_active_object(C);
- ARegion *region = CTX_wm_region(C);
- RegionView3D *rv3d = CTX_wm_region_view3d(C);
-
- CurvesSculpt &curves_sculpt = *scene.toolsettings->curves_sculpt;
- Brush &brush = *BKE_paint_brush(&curves_sculpt.paint);
- const float brush_radius = BKE_brush_size_get(&scene, &brush);
-
- float4x4 projection;
- ED_view3d_ob_project_mat_get(rv3d, &object, projection.values);
-
- Curves &curves_id = *static_cast<Curves *>(object.data);
- CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry);
- MutableSpan<float3> positions = curves.positions();
-
- const float2 mouse_start = stroke_extension.is_first ? stroke_extension.mouse_position :
- last_mouse_position_;
- const float2 mouse_end = stroke_extension.mouse_position;
-
- /* Find indices of curves that have to be removed. */
- Vector<int64_t> indices;
- const IndexMask curves_to_remove = index_mask_ops::find_indices_based_on_predicate(
- curves.curves_range(), 512, indices, [&](const int curve_i) {
- const IndexRange point_range = curves.range_for_curve(curve_i);
- for (const int segment_i : IndexRange(point_range.size() - 1)) {
- const float3 pos1 = positions[point_range[segment_i]];
- const float3 pos2 = positions[point_range[segment_i + 1]];
-
- float2 pos1_proj, pos2_proj;
- ED_view3d_project_float_v2_m4(region, pos1, pos1_proj, projection.values);
- ED_view3d_project_float_v2_m4(region, pos2, pos2_proj, projection.values);
-
- const float dist = dist_seg_seg_v2(pos1_proj, pos2_proj, mouse_start, mouse_end);
- if (dist <= brush_radius) {
- return true;
- }
- }
- return false;
- });
-
- curves.remove_curves(curves_to_remove);
-
- curves.tag_positions_changed();
- DEG_id_tag_update(&curves_id.id, ID_RECALC_GEOMETRY);
- ED_region_tag_redraw(region);
-
- last_mouse_position_ = stroke_extension.mouse_position;
- }
-};
-
-/**
- * Moves individual points under the brush and does a length preservation step afterwards.
- */
-class CombOperation : public CurvesSculptStrokeOperation {
- private:
- float2 last_mouse_position_;
-
- public:
- void on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) override
- {
- BLI_SCOPED_DEFER([&]() { last_mouse_position_ = stroke_extension.mouse_position; });
-
- if (stroke_extension.is_first) {
- return;
- }
-
- Scene &scene = *CTX_data_scene(C);
- Object &object = *CTX_data_active_object(C);
- ARegion *region = CTX_wm_region(C);
- View3D *v3d = CTX_wm_view3d(C);
- RegionView3D *rv3d = CTX_wm_region_view3d(C);
-
- CurvesSculpt &curves_sculpt = *scene.toolsettings->curves_sculpt;
- Brush &brush = *BKE_paint_brush(&curves_sculpt.paint);
- const float brush_radius = BKE_brush_size_get(&scene, &brush);
- const float brush_strength = BKE_brush_alpha_get(&scene, &brush);
-
- const float4x4 ob_mat = object.obmat;
- const float4x4 ob_imat = ob_mat.inverted();
-
- float4x4 projection;
- ED_view3d_ob_project_mat_get(rv3d, &object, projection.values);
-
- Curves &curves_id = *static_cast<Curves *>(object.data);
- CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry);
- MutableSpan<float3> positions = curves.positions();
-
- const float2 mouse_prev = last_mouse_position_;
- const float2 mouse_cur = stroke_extension.mouse_position;
- const float2 mouse_diff = mouse_cur - mouse_prev;
- const float mouse_diff_len = math::length(mouse_diff);
-
- threading::parallel_for(curves.curves_range(), 256, [&](const IndexRange curves_range) {
- for (const int curve_i : curves_range) {
- const IndexRange curve_points = curves.range_for_curve(curve_i);
- /* Compute lengths of the segments. Those are used to make sure that the lengths don't
- * change. */
- Vector<float, 16> segment_lengths(curve_points.size() - 1);
- for (const int segment_i : IndexRange(curve_points.size() - 1)) {
- const float3 &p1 = positions[curve_points[segment_i]];
- const float3 &p2 = positions[curve_points[segment_i] + 1];
- const float length = math::distance(p1, p2);
- segment_lengths[segment_i] = length;
- }
- bool curve_changed = false;
- for (const int point_i : curve_points.drop_front(1)) {
- const float3 old_position = positions[point_i];
-
- /* Find the position of the point in screen space. */
- float2 old_position_screen;
- ED_view3d_project_float_v2_m4(
- region, old_position, old_position_screen, projection.values);
-
- /* Project the point onto the line drawn by the mouse. Note, it's projected on the
- * infinite line, not only on the line segment. */
- float2 old_position_screen_proj;
- /* t is 0 when the point is closest to the previous mouse position and 1 when it's
- * closest to the current mouse position. */
- const float t = closest_to_line_v2(
- old_position_screen_proj, old_position_screen, mouse_prev, mouse_cur);
-
- /* Compute the distance to the mouse line segment. */
- const float2 old_position_screen_proj_segment = mouse_prev +
- std::clamp(t, 0.0f, 1.0f) * mouse_diff;
- const float distance_screen = math::distance(old_position_screen,
- old_position_screen_proj_segment);
- if (distance_screen > brush_radius) {
- /* Ignore the point because it's too far away. */
- continue;
- }
- /* Compute a falloff that is based on how far along the point along the last stroke
- * segment is. */
- const float t_overshoot = brush_radius / mouse_diff_len;
- const float t_falloff = 1.0f - std::max(t, 0.0f) / (1.0f + t_overshoot);
- /* A falloff that is based on how far away the point is from the stroke. */
- const float radius_falloff = pow2f(1.0f - distance_screen / brush_radius);
- /* Combine the different falloffs and brush strength. */
- const float weight = brush_strength * t_falloff * radius_falloff;
-
- /* Offset the old point position in screen space and transform it back into 3D space. */
- const float2 new_position_screen = old_position_screen + mouse_diff * weight;
- float3 new_position;
- ED_view3d_win_to_3d(
- v3d, region, ob_mat * old_position, new_position_screen, new_position);
- new_position = ob_imat * new_position;
- positions[point_i] = new_position;
-
- curve_changed = true;
- }
- if (!curve_changed) {
- continue;
- }
- /* Ensure that the length of each segment stays the same. */
- for (const int segment_i : IndexRange(curve_points.size() - 1)) {
- const float3 &p1 = positions[curve_points[segment_i]];
- float3 &p2 = positions[curve_points[segment_i] + 1];
- const float3 direction = math::normalize(p2 - p1);
- const float desired_length = segment_lengths[segment_i];
- p2 = p1 + direction * desired_length;
- }
- }
- });
-
- curves.tag_positions_changed();
- DEG_id_tag_update(&curves_id.id, ID_RECALC_GEOMETRY);
- ED_region_tag_redraw(region);
- }
-};
-
-/**
- * Drags the tip point of each curve and resamples the rest of the curve.
- */
-class SnakeHookOperation : public CurvesSculptStrokeOperation {
- private:
- float2 last_mouse_position_;
-
- public:
- void on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) override
- {
- BLI_SCOPED_DEFER([&]() { last_mouse_position_ = stroke_extension.mouse_position; });
-
- if (stroke_extension.is_first) {
- return;
- }
-
- Scene &scene = *CTX_data_scene(C);
- Object &object = *CTX_data_active_object(C);
- ARegion *region = CTX_wm_region(C);
- View3D *v3d = CTX_wm_view3d(C);
- RegionView3D *rv3d = CTX_wm_region_view3d(C);
-
- CurvesSculpt &curves_sculpt = *scene.toolsettings->curves_sculpt;
- Brush &brush = *BKE_paint_brush(&curves_sculpt.paint);
- const float brush_radius = BKE_brush_size_get(&scene, &brush);
- const float brush_strength = BKE_brush_alpha_get(&scene, &brush);
-
- const float4x4 ob_mat = object.obmat;
- const float4x4 ob_imat = ob_mat.inverted();
-
- float4x4 projection;
- ED_view3d_ob_project_mat_get(rv3d, &object, projection.values);
-
- Curves &curves_id = *static_cast<Curves *>(object.data);
- CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry);
- MutableSpan<float3> positions = curves.positions();
-
- const float2 mouse_prev = last_mouse_position_;
- const float2 mouse_cur = stroke_extension.mouse_position;
- const float2 mouse_diff = mouse_cur - mouse_prev;
-
- threading::parallel_for(curves.curves_range(), 256, [&](const IndexRange curves_range) {
- for (const int curve_i : curves_range) {
- const IndexRange curve_points = curves.range_for_curve(curve_i);
- const int last_point_i = curve_points.last();
-
- const float3 old_position = positions[last_point_i];
-
- float2 old_position_screen;
- ED_view3d_project_float_v2_m4(
- region, old_position, old_position_screen, projection.values);
-
- const float distance_screen = math::distance(old_position_screen, mouse_prev);
- if (distance_screen > brush_radius) {
- continue;
- }
-
- const float radius_falloff = pow2f(1.0f - distance_screen / brush_radius);
- const float weight = brush_strength * radius_falloff;
-
- const float2 new_position_screen = old_position_screen + mouse_diff * weight;
- float3 new_position;
- ED_view3d_win_to_3d(v3d, region, ob_mat * old_position, new_position_screen, new_position);
- new_position = ob_imat * new_position;
-
- this->move_last_point_and_resample(positions, curve_points, new_position);
- }
- });
-
- curves.tag_positions_changed();
- DEG_id_tag_update(&curves_id.id, ID_RECALC_GEOMETRY);
- ED_region_tag_redraw(region);
- }
-
- void move_last_point_and_resample(MutableSpan<float3> positions,
- const IndexRange curve_points,
- const float3 &new_last_point_position) const
- {
- Vector<float> old_lengths;
- old_lengths.append(0.0f);
- /* Used to (1) normalize the segment sizes over time and (2) support making zero-length
- * segments */
- const float extra_length = 0.001f;
- for (const int segment_i : IndexRange(curve_points.size() - 1)) {
- const float3 &p1 = positions[curve_points[segment_i]];
- const float3 &p2 = positions[curve_points[segment_i] + 1];
- const float length = math::distance(p1, p2);
- old_lengths.append(old_lengths.last() + length + extra_length);
- }
- Vector<float> point_factors;
- for (float &old_length : old_lengths) {
- point_factors.append(old_length / old_lengths.last());
- }
-
- PolySpline new_spline;
- new_spline.resize(curve_points.size());
- MutableSpan<float3> new_spline_positions = new_spline.positions();
- for (const int i : IndexRange(curve_points.size() - 1)) {
- new_spline_positions[i] = positions[curve_points[i]];
- }
- new_spline_positions.last() = new_last_point_position;
- new_spline.mark_cache_invalid();
-
- for (const int i : IndexRange(curve_points.size())) {
- const float factor = point_factors[i];
- const Spline::LookupResult lookup = new_spline.lookup_evaluated_factor(factor);
- const float index_factor = lookup.evaluated_index + lookup.factor;
- float3 p;
- new_spline.sample_with_index_factors<float3>(
- new_spline_positions, {&index_factor, 1}, {&p, 1});
- positions[curve_points[i]] = p;
- }
- }
-};
-
/**
* Resamples the curves to a shorter length.
*/
@@ -423,7 +117,7 @@ class ShrinkOperation : public CurvesSculptStrokeOperation {
threading::parallel_for(curves.curves_range(), 256, [&](const IndexRange curves_range) {
for (const int curve_i : curves_range) {
- const IndexRange curve_points = curves.range_for_curve(curve_i);
+ const IndexRange curve_points = curves.points_for_curve(curve_i);
const int last_point_i = curve_points.last();
const float3 old_tip_position = positions[last_point_i];
@@ -492,7 +186,7 @@ class ShrinkOperation : public CurvesSculptStrokeOperation {
}
};
-class AddOperation : public CurvesSculptStrokeOperation {
+class DensityAddOperation : public CurvesSculptStrokeOperation {
private:
/** Contains the root points of the curves that existed before this operation started. */
KDTree_3d *old_kdtree_ = nullptr;
@@ -513,7 +207,7 @@ class AddOperation : public CurvesSculptStrokeOperation {
};
public:
- ~AddOperation() override
+ ~DensityAddOperation() override
{
if (old_kdtree_ != nullptr) {
BLI_kdtree_3d_free(old_kdtree_);
@@ -610,7 +304,7 @@ class AddOperation : public CurvesSculptStrokeOperation {
if (old_kdtree_ == nullptr && minimum_distance > 0.0f) {
old_kdtree_ = this->kdtree_from_curve_roots_and_positions(curves, curves.curves_range(), {});
- old_kdtree_size_ = curves.curves_size();
+ old_kdtree_size_ = curves.curves_num();
}
float density;
@@ -683,13 +377,6 @@ class AddOperation : public CurvesSculptStrokeOperation {
return kdtree;
}
- int float_to_int_amount(float amount_f, RandomNumberGenerator &rng)
- {
- const float add_probability = fractf(amount_f);
- const bool add_point = add_probability > rng.get_float();
- return (int)amount_f + (int)add_point;
- }
-
bool is_too_close_to_existing_point(const float3 position, const float minimum_distance) const
{
if (old_kdtree_ == nullptr) {
@@ -744,7 +431,7 @@ class AddOperation : public CurvesSculptStrokeOperation {
* the triangle directly. If the triangle is larger than the brush, distribute new points
* in a circle on the triangle plane. */
if (looptri_area < area_threshold) {
- const int amount = this->float_to_int_amount(looptri_area * density, looptri_rng);
+ const int amount = looptri_rng.round_probabilistic(looptri_area * density);
threading::parallel_for(IndexRange(amount), 512, [&](const IndexRange amount_range) {
RandomNumberGenerator point_rng{rng_base_seed + looptri_index * 1000 +
@@ -781,7 +468,7 @@ class AddOperation : public CurvesSculptStrokeOperation {
const float radius_proj = std::sqrt(radius_proj_sq);
const float circle_area = M_PI * radius_proj_sq;
- const int amount = this->float_to_int_amount(circle_area * density, rng);
+ const int amount = rng.round_probabilistic(circle_area * density);
const float3 axis_1 = math::normalize(v1 - v0) * radius_proj;
const float3 axis_2 = math::normalize(
@@ -838,7 +525,7 @@ class AddOperation : public CurvesSculptStrokeOperation {
{
Array<bool> elimination_mask(points.positions.size(), false);
- const int curves_added_previously = curves.curves_size() - old_kdtree_size_;
+ const int curves_added_previously = curves.curves_num() - old_kdtree_size_;
KDTree_3d *new_points_kdtree = this->kdtree_from_curve_roots_and_positions(
curves, IndexRange(old_kdtree_size_, curves_added_previously), points.positions);
@@ -902,14 +589,14 @@ class AddOperation : public CurvesSculptStrokeOperation {
const int tot_new_curves = new_points.positions.size();
const int points_per_curve = 8;
- curves.resize(curves.points_size() + tot_new_curves * points_per_curve,
- curves.curves_size() + tot_new_curves);
+ curves.resize(curves.points_num() + tot_new_curves * points_per_curve,
+ curves.curves_num() + tot_new_curves);
MutableSpan<int> offsets = curves.offsets();
MutableSpan<float3> positions = curves.positions();
for (const int i : IndexRange(tot_new_curves)) {
- const int curve_i = curves.curves_size() - tot_new_curves + i;
+ const int curve_i = curves.curves_num() - tot_new_curves + i;
const int first_point_i = offsets[curve_i];
offsets[curve_i + 1] = offsets[curve_i] + points_per_curve;
@@ -932,13 +619,15 @@ static std::unique_ptr<CurvesSculptStrokeOperation> start_brush_operation(bConte
Brush &brush = *BKE_paint_brush(&curves_sculpt.paint);
switch (brush.curves_sculpt_tool) {
case CURVES_SCULPT_TOOL_COMB:
- return std::make_unique<CombOperation>();
+ return new_comb_operation();
case CURVES_SCULPT_TOOL_DELETE:
- return std::make_unique<DeleteOperation>();
+ return new_delete_operation();
case CURVES_SCULPT_TOOL_SNAKE_HOOK:
- return std::make_unique<SnakeHookOperation>();
+ return new_snake_hook_operation();
+ case CURVES_SCULPT_TOOL_ADD:
+ return new_add_operation();
case CURVES_SCULPT_TOOL_TEST1:
- return std::make_unique<AddOperation>();
+ return std::make_unique<DensityAddOperation>();
case CURVES_SCULPT_TOOL_TEST2:
return std::make_unique<ShrinkOperation>();
}
diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_snake_hook.cc b/source/blender/editors/sculpt_paint/curves_sculpt_snake_hook.cc
new file mode 100644
index 00000000000..682cd3b47ca
--- /dev/null
+++ b/source/blender/editors/sculpt_paint/curves_sculpt_snake_hook.cc
@@ -0,0 +1,163 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include <algorithm>
+
+#include "curves_sculpt_intern.hh"
+
+#include "BLI_float4x4.hh"
+#include "BLI_index_mask_ops.hh"
+#include "BLI_kdtree.h"
+#include "BLI_rand.hh"
+#include "BLI_vector.hh"
+
+#include "PIL_time.h"
+
+#include "DEG_depsgraph.h"
+
+#include "BKE_attribute_math.hh"
+#include "BKE_brush.h"
+#include "BKE_bvhutils.h"
+#include "BKE_context.h"
+#include "BKE_curves.hh"
+#include "BKE_mesh.h"
+#include "BKE_mesh_runtime.h"
+#include "BKE_paint.h"
+#include "BKE_spline.hh"
+
+#include "DNA_brush_enums.h"
+#include "DNA_brush_types.h"
+#include "DNA_curves_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_object_types.h"
+#include "DNA_screen_types.h"
+#include "DNA_space_types.h"
+
+#include "ED_screen.h"
+#include "ED_view3d.h"
+
+namespace blender::ed::sculpt_paint {
+
+using blender::bke::CurvesGeometry;
+
+/**
+ * Drags the tip point of each curve and resamples the rest of the curve.
+ */
+class SnakeHookOperation : public CurvesSculptStrokeOperation {
+ private:
+ float2 last_mouse_position_;
+
+ public:
+ void on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) override
+ {
+ BLI_SCOPED_DEFER([&]() { last_mouse_position_ = stroke_extension.mouse_position; });
+
+ if (stroke_extension.is_first) {
+ return;
+ }
+
+ Scene &scene = *CTX_data_scene(C);
+ Object &object = *CTX_data_active_object(C);
+ ARegion *region = CTX_wm_region(C);
+ View3D *v3d = CTX_wm_view3d(C);
+ RegionView3D *rv3d = CTX_wm_region_view3d(C);
+
+ CurvesSculpt &curves_sculpt = *scene.toolsettings->curves_sculpt;
+ Brush &brush = *BKE_paint_brush(&curves_sculpt.paint);
+ const float brush_radius = BKE_brush_size_get(&scene, &brush);
+ const float brush_strength = BKE_brush_alpha_get(&scene, &brush);
+
+ const float4x4 ob_mat = object.obmat;
+ const float4x4 ob_imat = ob_mat.inverted();
+
+ float4x4 projection;
+ ED_view3d_ob_project_mat_get(rv3d, &object, projection.values);
+
+ Curves &curves_id = *static_cast<Curves *>(object.data);
+ CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry);
+ MutableSpan<float3> positions = curves.positions();
+
+ const float2 mouse_prev = last_mouse_position_;
+ const float2 mouse_cur = stroke_extension.mouse_position;
+ const float2 mouse_diff = mouse_cur - mouse_prev;
+
+ threading::parallel_for(curves.curves_range(), 256, [&](const IndexRange curves_range) {
+ for (const int curve_i : curves_range) {
+ const IndexRange curve_points = curves.points_for_curve(curve_i);
+ const int last_point_i = curve_points.last();
+
+ const float3 old_position = positions[last_point_i];
+
+ float2 old_position_screen;
+ ED_view3d_project_float_v2_m4(
+ region, old_position, old_position_screen, projection.values);
+
+ const float distance_screen = math::distance(old_position_screen, mouse_prev);
+ if (distance_screen > brush_radius) {
+ continue;
+ }
+
+ const float radius_falloff = pow2f(1.0f - distance_screen / brush_radius);
+ const float weight = brush_strength * radius_falloff;
+
+ const float2 new_position_screen = old_position_screen + mouse_diff * weight;
+ float3 new_position;
+ ED_view3d_win_to_3d(v3d, region, ob_mat * old_position, new_position_screen, new_position);
+ new_position = ob_imat * new_position;
+
+ this->move_last_point_and_resample(positions, curve_points, new_position);
+ }
+ });
+
+ curves.tag_positions_changed();
+ DEG_id_tag_update(&curves_id.id, ID_RECALC_GEOMETRY);
+ ED_region_tag_redraw(region);
+ }
+
+ void move_last_point_and_resample(MutableSpan<float3> positions,
+ const IndexRange curve_points,
+ const float3 &new_last_point_position) const
+ {
+ Vector<float> old_lengths;
+ old_lengths.append(0.0f);
+ /* Used to (1) normalize the segment sizes over time and (2) support making zero-length
+ * segments */
+ const float extra_length = 0.001f;
+ for (const int segment_i : IndexRange(curve_points.size() - 1)) {
+ const float3 &p1 = positions[curve_points[segment_i]];
+ const float3 &p2 = positions[curve_points[segment_i] + 1];
+ const float length = math::distance(p1, p2);
+ old_lengths.append(old_lengths.last() + length + extra_length);
+ }
+ Vector<float> point_factors;
+ for (float &old_length : old_lengths) {
+ point_factors.append(old_length / old_lengths.last());
+ }
+
+ PolySpline new_spline;
+ new_spline.resize(curve_points.size());
+ MutableSpan<float3> new_spline_positions = new_spline.positions();
+ for (const int i : IndexRange(curve_points.size() - 1)) {
+ new_spline_positions[i] = positions[curve_points[i]];
+ }
+ new_spline_positions.last() = new_last_point_position;
+ new_spline.mark_cache_invalid();
+
+ for (const int i : IndexRange(curve_points.size())) {
+ const float factor = point_factors[i];
+ const Spline::LookupResult lookup = new_spline.lookup_evaluated_factor(factor);
+ const float index_factor = lookup.evaluated_index + lookup.factor;
+ float3 p;
+ new_spline.sample_with_index_factors<float3>(
+ new_spline_positions, {&index_factor, 1}, {&p, 1});
+ positions[curve_points[i]] = p;
+ }
+ }
+};
+
+std::unique_ptr<CurvesSculptStrokeOperation> new_snake_hook_operation()
+{
+ return std::make_unique<SnakeHookOperation>();
+}
+
+} // namespace blender::ed::sculpt_paint
diff --git a/source/blender/editors/sculpt_paint/paint_mask.c b/source/blender/editors/sculpt_paint/paint_mask.c
index b85d2d0aec8..5e89a4823db 100644
--- a/source/blender/editors/sculpt_paint/paint_mask.c
+++ b/source/blender/editors/sculpt_paint/paint_mask.c
@@ -962,6 +962,7 @@ static void sculpt_gesture_trim_normals_update(SculptGestureContext *sgcontext)
trim_mesh,
(&(struct BMeshFromMeshParams){
.calc_face_normal = true,
+ .calc_vert_normal = true,
}));
BM_mesh_elem_hflag_enable_all(bm, BM_FACE, BM_ELEM_TAG, false);
BMO_op_callf(bm,
@@ -1075,7 +1076,7 @@ static void sculpt_gesture_trim_geometry_generate(SculptGestureContext *sgcontex
const int trim_totpolys = (2 * (tot_screen_points - 2)) + (2 * tot_screen_points);
trim_operation->mesh = BKE_mesh_new_nomain(
trim_totverts, 0, 0, trim_totpolys * 3, trim_totpolys);
- trim_operation->true_mesh_co = MEM_malloc_arrayN(trim_totverts, 3 * sizeof(float), "mesh orco");
+ trim_operation->true_mesh_co = MEM_malloc_arrayN(trim_totverts, sizeof(float[3]), "mesh orco");
float depth_front = trim_operation->depth_front;
float depth_back = trim_operation->depth_back;
@@ -1129,7 +1130,7 @@ static void sculpt_gesture_trim_geometry_generate(SculptGestureContext *sgcontex
/* Get the triangulation for the front/back poly. */
const int tot_tris_face = tot_screen_points - 2;
- uint(*r_tris)[3] = MEM_malloc_arrayN(tot_tris_face, 3 * sizeof(uint), "tris");
+ uint(*r_tris)[3] = MEM_malloc_arrayN(tot_tris_face, sizeof(uint[3]), "tris");
BLI_polyfill_calc(screen_points, tot_screen_points, 0, r_tris);
/* Write the front face triangle indices. */
@@ -1214,12 +1215,14 @@ static void sculpt_gesture_apply_trim(SculptGestureContext *sgcontext)
trim_mesh,
&((struct BMeshFromMeshParams){
.calc_face_normal = true,
+ .calc_vert_normal = true,
}));
BM_mesh_bm_from_me(bm,
sculpt_mesh,
&((struct BMeshFromMeshParams){
.calc_face_normal = true,
+ .calc_vert_normal = true,
}));
const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop);
diff --git a/source/blender/editors/sculpt_paint/paint_stroke.c b/source/blender/editors/sculpt_paint/paint_stroke.c
index 1705e36363e..0f7b8ad1f3d 100644
--- a/source/blender/editors/sculpt_paint/paint_stroke.c
+++ b/source/blender/editors/sculpt_paint/paint_stroke.c
@@ -792,7 +792,7 @@ static int paint_space_stroke(bContext *C,
Paint *paint = BKE_paint_get_active_from_context(C);
ePaintMode mode = BKE_paintmode_get_active_from_context(C);
Brush *brush = BKE_paint_brush(paint);
- int cnt = 0;
+ int count = 0;
const bool use_scene_spacing = paint_stroke_use_scene_spacing(brush, mode);
float d_world_space_position[3] = {0.0f};
@@ -855,14 +855,14 @@ static int paint_space_stroke(bContext *C,
pressure = stroke->last_pressure;
dpressure = final_pressure - stroke->last_pressure;
- cnt++;
+ count++;
}
else {
break;
}
}
- return cnt;
+ return count;
}
/**** Public API ****/
@@ -986,6 +986,11 @@ static void stroke_done(bContext *C, wmOperator *op, PaintStroke *stroke)
paint_stroke_free(C, op, stroke);
}
+static bool curves_sculpt_brush_uses_spacing(const eBrushCurvesSculptTool tool)
+{
+ return ELEM(tool, CURVES_SCULPT_TOOL_ADD);
+}
+
bool paint_space_stroke_enabled(Brush *br, ePaintMode mode)
{
if ((br->flag & BRUSH_SPACE) == 0) {
@@ -1000,7 +1005,8 @@ bool paint_space_stroke_enabled(Brush *br, ePaintMode mode)
return true;
}
- if (mode == PAINT_MODE_SCULPT_CURVES) {
+ if (mode == PAINT_MODE_SCULPT_CURVES &&
+ !curves_sculpt_brush_uses_spacing(br->curves_sculpt_tool)) {
return false;
}
diff --git a/source/blender/editors/sculpt_paint/sculpt_boundary.c b/source/blender/editors/sculpt_paint/sculpt_boundary.c
index 70f8f2127b4..8bf09ce3d05 100644
--- a/source/blender/editors/sculpt_paint/sculpt_boundary.c
+++ b/source/blender/editors/sculpt_paint/sculpt_boundary.c
@@ -532,9 +532,8 @@ static void sculpt_boundary_bend_data_init(SculptSession *ss, SculptBoundary *bo
{
const int totvert = SCULPT_vertex_count_get(ss);
boundary->bend.pivot_rotation_axis = MEM_calloc_arrayN(
- totvert, 3 * sizeof(float), "pivot rotation axis");
- boundary->bend.pivot_positions = MEM_calloc_arrayN(
- totvert, 3 * sizeof(float), "pivot positions");
+ totvert, sizeof(float[3]), "pivot rotation axis");
+ boundary->bend.pivot_positions = MEM_calloc_arrayN(totvert, sizeof(float[3]), "pivot positions");
for (int i = 0; i < totvert; i++) {
if (boundary->edit_info[i].num_propagation_steps != boundary->max_propagation_steps) {
@@ -567,7 +566,7 @@ static void sculpt_boundary_bend_data_init(SculptSession *ss, SculptBoundary *bo
static void sculpt_boundary_slide_data_init(SculptSession *ss, SculptBoundary *boundary)
{
const int totvert = SCULPT_vertex_count_get(ss);
- boundary->slide.directions = MEM_calloc_arrayN(totvert, 3 * sizeof(float), "slide directions");
+ boundary->slide.directions = MEM_calloc_arrayN(totvert, sizeof(float[3]), "slide directions");
for (int i = 0; i < totvert; i++) {
if (boundary->edit_info[i].num_propagation_steps != boundary->max_propagation_steps) {
@@ -592,7 +591,7 @@ static void sculpt_boundary_twist_data_init(SculptSession *ss, SculptBoundary *b
{
zero_v3(boundary->twist.pivot_position);
float(*poly_verts)[3] = MEM_malloc_arrayN(
- boundary->num_vertices, sizeof(float) * 3, "poly verts");
+ boundary->num_vertices, sizeof(float[3]), "poly verts");
for (int i = 0; i < boundary->num_vertices; i++) {
add_v3_v3(boundary->twist.pivot_position, SCULPT_vertex_co_get(ss, boundary->vertices[i]));
copy_v3_v3(poly_verts[i], SCULPT_vertex_co_get(ss, boundary->vertices[i]));
diff --git a/source/blender/editors/sculpt_paint/sculpt_dyntopo.c b/source/blender/editors/sculpt_paint/sculpt_dyntopo.c
index 0e26eb9b4b6..58da5adc5e3 100644
--- a/source/blender/editors/sculpt_paint/sculpt_dyntopo.c
+++ b/source/blender/editors/sculpt_paint/sculpt_dyntopo.c
@@ -147,6 +147,7 @@ void SCULPT_dynamic_topology_enable_ex(Main *bmain, Depsgraph *depsgraph, Scene
me,
(&(struct BMeshFromMeshParams){
.calc_face_normal = true,
+ .calc_vert_normal = true,
.use_shapekey = true,
.active_shapekey = ob->shapenr,
}));
diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set.c b/source/blender/editors/sculpt_paint/sculpt_face_set.c
index 5f6b8bf9b19..23bc9fbb54d 100644
--- a/source/blender/editors/sculpt_paint/sculpt_face_set.c
+++ b/source/blender/editors/sculpt_paint/sculpt_face_set.c
@@ -379,6 +379,7 @@ static int sculpt_face_set_create_exec(bContext *C, wmOperator *op)
mesh,
(&(struct BMeshFromMeshParams){
.calc_face_normal = true,
+ .calc_vert_normal = true,
}));
BMIter iter;
@@ -574,6 +575,7 @@ static void sculpt_face_sets_init_flood_fill(Object *ob,
mesh,
(&(struct BMeshFromMeshParams){
.calc_face_normal = true,
+ .calc_vert_normal = true,
}));
BLI_bitmap *visited_faces = BLI_BITMAP_NEW(mesh->totpoly, "visited faces");
@@ -652,6 +654,7 @@ static void sculpt_face_sets_init_loop(Object *ob, const int mode)
mesh,
(&(struct BMeshFromMeshParams){
.calc_face_normal = true,
+ .calc_vert_normal = true,
}));
BMIter iter;
BMFace *f;
@@ -1184,6 +1187,7 @@ static void sculpt_face_set_delete_geometry(Object *ob,
mesh,
(&(struct BMeshFromMeshParams){
.calc_face_normal = true,
+ .calc_vert_normal = true,
}));
BM_mesh_elem_table_init(bm, BM_FACE);
diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.c b/source/blender/editors/sculpt_paint/sculpt_smooth.c
index 858c6c4e279..482bdf97d78 100644
--- a/source/blender/editors/sculpt_paint/sculpt_smooth.c
+++ b/source/blender/editors/sculpt_paint/sculpt_smooth.c
@@ -251,7 +251,7 @@ static void SCULPT_enhance_details_brush(Sculpt *sd,
if (SCULPT_stroke_is_first_brush_step(ss->cache)) {
const int totvert = SCULPT_vertex_count_get(ss);
ss->cache->detail_directions = MEM_malloc_arrayN(
- totvert, 3 * sizeof(float), "details directions");
+ totvert, sizeof(float[3]), "details directions");
for (int i = 0; i < totvert; i++) {
float avg[3];
diff --git a/source/blender/editors/space_action/action_edit.c b/source/blender/editors/space_action/action_edit.c
index f0ada312d82..d33cf70e117 100644
--- a/source/blender/editors/space_action/action_edit.c
+++ b/source/blender/editors/space_action/action_edit.c
@@ -1706,8 +1706,8 @@ static const EnumPropertyItem prop_actkeys_snap_types[] = {
"NEAREST_FRAME",
0,
"Selection to Nearest Frame",
- "Snap selected keyframes to the nearest (whole) frame (use to fix accidental subframe "
- "offsets)"},
+ "Snap selected keyframes to the nearest (whole) frame "
+ "(use to fix accidental sub-frame offsets)"},
{ACTKEYS_SNAP_NEAREST_SECOND,
"NEAREST_SECOND",
0,
diff --git a/source/blender/editors/space_action/space_action.c b/source/blender/editors/space_action/space_action.c
index 20e9d21455f..7eba3d49616 100644
--- a/source/blender/editors/space_action/space_action.c
+++ b/source/blender/editors/space_action/space_action.c
@@ -170,12 +170,8 @@ static void action_main_region_draw(const bContext *C, ARegion *region)
bAnimContext ac;
View2D *v2d = &region->v2d;
short marker_flag = 0;
- short cfra_flag = 0;
UI_view2d_view_ortho(v2d);
- if (saction->flag & SACTION_DRAWTIME) {
- cfra_flag |= DRAWCFRA_UNIT_SECONDS;
- }
/* clear and setup matrix */
UI_ThemeClearColor(TH_BACK);
diff --git a/source/blender/editors/space_clip/tracking_ops.c b/source/blender/editors/space_clip/tracking_ops.c
index 0d1ff71e567..e7bdbfe7c68 100644
--- a/source/blender/editors/space_clip/tracking_ops.c
+++ b/source/blender/editors/space_clip/tracking_ops.c
@@ -271,7 +271,6 @@ static int delete_marker_exec(bContext *C, wmOperator *UNUSED(op))
MovieClip *clip = ED_space_clip_get_clip(sc);
MovieTracking *tracking = &clip->tracking;
const int framenr = ED_space_clip_get_clip_frame_number(sc);
- bool has_selection = false;
bool changed = false;
ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking);
@@ -281,7 +280,6 @@ static int delete_marker_exec(bContext *C, wmOperator *UNUSED(op))
if (TRACK_VIEW_SELECTED(sc, track)) {
MovieTrackingMarker *marker = BKE_tracking_marker_get_exact(track, framenr);
if (marker != NULL) {
- has_selection |= track->markersnr > 1;
clip_delete_marker(C, clip, track, marker);
changed = true;
}
@@ -878,24 +876,24 @@ static int slide_marker_modal(bContext *C, wmOperator *op, const wmEvent *event)
BKE_tracking_marker_clamp(data->marker, CLAMP_PAT_DIM);
}
else if (data->action == SLIDE_ACTION_TILT_SIZE) {
- float start[2], end[2];
- float scale = 1.0f, angle = 0.0f;
- float mval[2];
-
- if (data->accurate) {
- mval[0] = data->mval[0] + (event->mval[0] - data->mval[0]) / 5.0f;
- mval[1] = data->mval[1] + (event->mval[1] - data->mval[1]) / 5.0f;
- }
- else {
- mval[0] = event->mval[0];
- mval[1] = event->mval[1];
- }
+ const float mouse_delta[2] = {dx, dy};
+ /* Vector which connects marker position with tilt/scale sliding area before sliding
+ * began. */
+ float start[2];
sub_v2_v2v2(start, data->spos, data->old_pos);
+ start[0] *= data->width;
+ start[1] *= data->height;
- ED_clip_point_stable_pos(sc, region, mval[0], mval[1], &end[0], &end[1]);
+ /* Vector which connects marker position with tilt/scale sliding area with the sliding
+ * delta applied. */
+ float end[2];
+ add_v2_v2v2(end, data->spos, mouse_delta);
sub_v2_v2(end, data->old_pos);
+ end[0] *= data->width;
+ end[1] *= data->height;
+ float scale = 1.0f;
if (len_squared_v2(start) != 0.0f) {
scale = len_v2(end) / len_v2(start);
@@ -904,7 +902,7 @@ static int slide_marker_modal(bContext *C, wmOperator *op, const wmEvent *event)
}
}
- angle = -angle_signed_v2v2(start, end);
+ const float angle = -angle_signed_v2v2(start, end);
for (int a = 0; a < 4; a++) {
float vec[2];
diff --git a/source/blender/editors/space_clip/tracking_ops_track.c b/source/blender/editors/space_clip/tracking_ops_track.c
index f9cbce40deb..d5223d57490 100644
--- a/source/blender/editors/space_clip/tracking_ops_track.c
+++ b/source/blender/editors/space_clip/tracking_ops_track.c
@@ -211,6 +211,8 @@ static void track_markers_startjob(
TrackMarkersJob *tmj = (TrackMarkersJob *)tmv;
int framenr = tmj->sfra;
+ BKE_autotrack_context_start(tmj->context);
+
while (framenr != tmj->efra) {
if (tmj->delay > 0) {
/* Tracking should happen with fixed fps. Calculate time
diff --git a/source/blender/editors/space_file/CMakeLists.txt b/source/blender/editors/space_file/CMakeLists.txt
index c4c6fa01025..b8c28e354da 100644
--- a/source/blender/editors/space_file/CMakeLists.txt
+++ b/source/blender/editors/space_file/CMakeLists.txt
@@ -79,6 +79,9 @@ if(WITH_IMAGE_HDR)
add_definitions(-DWITH_HDR)
endif()
+if(WITH_IMAGE_WEBP)
+ add_definitions(-DWITH_WEBP)
+endif()
if(WITH_FREESTYLE)
add_definitions(-DWITH_FREESTYLE)
diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c
index 363e19a8905..ceac53bde6b 100644
--- a/source/blender/editors/space_file/filelist.c
+++ b/source/blender/editors/space_file/filelist.c
@@ -851,6 +851,20 @@ static bool is_filtered_file_relpath(const FileListInternEntry *file, const File
return fnmatch(filter->filter_search, file->relpath, FNM_CASEFOLD) == 0;
}
+/**
+ * Apply the filter string as matching pattern on file name.
+ * \return true when the file should be in the result set, false if it should be filtered out.
+ */
+static bool is_filtered_file_name(const FileListInternEntry *file, const FileListFilter *filter)
+{
+ if (filter->filter_search[0] == '\0') {
+ return true;
+ }
+
+ /* If there's a filter string, apply it as filter even if FLF_DO_FILTER is not set. */
+ return fnmatch(filter->filter_search, file->name, FNM_CASEFOLD) == 0;
+}
+
/** \return true when the file should be in the result set, false if it should be filtered out. */
static bool is_filtered_file_type(const FileListInternEntry *file, const FileListFilter *filter)
{
@@ -890,7 +904,8 @@ static bool is_filtered_file(FileListInternEntry *file,
const char *UNUSED(root),
FileListFilter *filter)
{
- return is_filtered_file_type(file, filter) && is_filtered_file_relpath(file, filter);
+ return is_filtered_file_type(file, filter) &&
+ (is_filtered_file_relpath(file, filter) || is_filtered_file_name(file, filter));
}
static bool is_filtered_id_file_type(const FileListInternEntry *file,
@@ -1041,10 +1056,10 @@ void filelist_tag_needs_filtering(FileList *filelist)
void filelist_filter(FileList *filelist)
{
int num_filtered = 0;
- const int num_files = filelist->filelist.nbr_entries;
+ const int num_files = filelist->filelist.entries_num;
FileListInternEntry **filtered_tmp, *file;
- if (ELEM(filelist->filelist.nbr_entries, FILEDIR_NBR_ENTRIES_UNSET, 0)) {
+ if (ELEM(filelist->filelist.entries_num, FILEDIR_NBR_ENTRIES_UNSET, 0)) {
return;
}
@@ -1084,8 +1099,8 @@ void filelist_filter(FileList *filelist)
memcpy(filelist->filelist_intern.filtered,
filtered_tmp,
sizeof(*filelist->filelist_intern.filtered) * (size_t)num_filtered);
- filelist->filelist.nbr_entries_filtered = num_filtered;
- // printf("Filetered: %d over %d entries\n", num_filtered, filelist->filelist.nbr_entries);
+ filelist->filelist.entries_filtered_num = num_filtered;
+ // printf("Filetered: %d over %d entries\n", num_filtered, filelist->filelist.entries_num);
filelist_cache_clear(&filelist->filelist_cache, filelist->filelist_cache.size);
filelist->flags &= ~FL_NEED_FILTERING;
@@ -1537,8 +1552,8 @@ static void filelist_direntryarr_free(FileDirEntryArr *array)
#else
BLI_assert(BLI_listbase_is_empty(&array->entries));
#endif
- array->nbr_entries = FILEDIR_NBR_ENTRIES_UNSET;
- array->nbr_entries_filtered = FILEDIR_NBR_ENTRIES_UNSET;
+ array->entries_num = FILEDIR_NBR_ENTRIES_UNSET;
+ array->entries_filtered_num = FILEDIR_NBR_ENTRIES_UNSET;
}
static void filelist_intern_entry_free(FileListInternEntry *entry)
@@ -1859,7 +1874,7 @@ FileList *filelist_new(short type)
filelist_cache_init(&p->filelist_cache, FILELIST_ENTRYCACHESIZE_DEFAULT);
p->selection_state = BLI_ghash_new(BLI_ghashutil_inthash_p, BLI_ghashutil_intcmp, __func__);
- p->filelist.nbr_entries = FILEDIR_NBR_ENTRIES_UNSET;
+ p->filelist.entries_num = FILEDIR_NBR_ENTRIES_UNSET;
filelist_settype(p, type);
return p;
@@ -1964,9 +1979,9 @@ static void filelist_clear_main_files(FileList *filelist,
const int removed_files = filelist_intern_free_main_files(&filelist->filelist_intern);
- filelist->filelist.nbr_entries -= removed_files;
- filelist->filelist.nbr_entries_filtered = FILEDIR_NBR_ENTRIES_UNSET;
- BLI_assert(filelist->filelist.nbr_entries > FILEDIR_NBR_ENTRIES_UNSET);
+ filelist->filelist.entries_num -= removed_files;
+ filelist->filelist.entries_filtered_num = FILEDIR_NBR_ENTRIES_UNSET;
+ BLI_assert(filelist->filelist.entries_num > FILEDIR_NBR_ENTRIES_UNSET);
if (do_selection && filelist->selection_state) {
BLI_ghash_clear(filelist->selection_state, NULL, NULL);
@@ -2152,7 +2167,7 @@ int filelist_files_ensure(FileList *filelist)
filelist_filter(filelist);
}
- return filelist->filelist.nbr_entries_filtered;
+ return filelist->filelist.entries_filtered_num;
}
static FileDirEntry *filelist_file_create_entry(FileList *filelist, const int index)
@@ -2211,7 +2226,7 @@ FileDirEntry *filelist_file_ex(struct FileList *filelist, const int index, const
const size_t cache_size = cache->size;
int old_index;
- if ((index < 0) || (index >= filelist->filelist.nbr_entries_filtered)) {
+ if ((index < 0) || (index >= filelist->filelist.entries_filtered_num)) {
return ret;
}
@@ -2259,7 +2274,7 @@ FileDirEntry *filelist_file(struct FileList *filelist, int index)
int filelist_file_find_path(struct FileList *filelist, const char *filename)
{
- if (filelist->filelist.nbr_entries_filtered == FILEDIR_NBR_ENTRIES_UNSET) {
+ if (filelist->filelist.entries_filtered_num == FILEDIR_NBR_ENTRIES_UNSET) {
return -1;
}
@@ -2267,7 +2282,7 @@ int filelist_file_find_path(struct FileList *filelist, const char *filename)
* This is only used to find again renamed entry,
* annoying but looks hairy to get rid of it currently. */
- for (int fidx = 0; fidx < filelist->filelist.nbr_entries_filtered; fidx++) {
+ for (int fidx = 0; fidx < filelist->filelist.entries_filtered_num; fidx++) {
FileListInternEntry *entry = filelist->filelist_intern.filtered[fidx];
if (STREQ(entry->relpath, filename)) {
return fidx;
@@ -2279,11 +2294,11 @@ int filelist_file_find_path(struct FileList *filelist, const char *filename)
int filelist_file_find_id(const FileList *filelist, const ID *id)
{
- if (filelist->filelist.nbr_entries_filtered == FILEDIR_NBR_ENTRIES_UNSET) {
+ if (filelist->filelist.entries_filtered_num == FILEDIR_NBR_ENTRIES_UNSET) {
return -1;
}
- for (int fidx = 0; fidx < filelist->filelist.nbr_entries_filtered; fidx++) {
+ for (int fidx = 0; fidx < filelist->filelist.entries_filtered_num; fidx++) {
FileListInternEntry *entry = filelist->filelist_intern.filtered[fidx];
if (entry->local_data.id == id) {
return fidx;
@@ -2393,23 +2408,23 @@ bool filelist_file_cache_block(struct FileList *filelist, const int index)
FileListEntryCache *cache = &filelist->filelist_cache;
const size_t cache_size = cache->size;
- const int nbr_entries = filelist->filelist.nbr_entries_filtered;
+ const int entries_num = filelist->filelist.entries_filtered_num;
int start_index = max_ii(0, index - (cache_size / 2));
- int end_index = min_ii(nbr_entries, index + (cache_size / 2));
+ int end_index = min_ii(entries_num, index + (cache_size / 2));
int i;
const bool full_refresh = (filelist->flags & FL_IS_READY) == 0;
- if ((index < 0) || (index >= nbr_entries)) {
- // printf("Wrong index %d ([%d:%d])", index, 0, nbr_entries);
+ if ((index < 0) || (index >= entries_num)) {
+ // printf("Wrong index %d ([%d:%d])", index, 0, entries_num);
return false;
}
/* Maximize cached range! */
if ((end_index - start_index) < cache_size) {
if (start_index == 0) {
- end_index = min_ii(nbr_entries, start_index + cache_size);
+ end_index = min_ii(entries_num, start_index + cache_size);
}
- else if (end_index == nbr_entries) {
+ else if (end_index == entries_num) {
start_index = max_ii(0, end_index - cache_size);
}
}
@@ -2846,7 +2861,7 @@ int ED_file_extension_icon(const char *path)
int filelist_needs_reading(FileList *filelist)
{
- return (filelist->filelist.nbr_entries == FILEDIR_NBR_ENTRIES_UNSET) ||
+ return (filelist->filelist.entries_num == FILEDIR_NBR_ENTRIES_UNSET) ||
filelist_needs_force_reset(filelist);
}
@@ -2911,8 +2926,8 @@ void filelist_entries_select_index_range_set(
FileList *filelist, FileSelection *sel, FileSelType select, uint flag, FileCheckType check)
{
/* select all valid files between first and last indicated */
- if ((sel->first >= 0) && (sel->first < filelist->filelist.nbr_entries_filtered) &&
- (sel->last >= 0) && (sel->last < filelist->filelist.nbr_entries_filtered)) {
+ if ((sel->first >= 0) && (sel->first < filelist->filelist.entries_filtered_num) &&
+ (sel->last >= 0) && (sel->last < filelist->filelist.entries_filtered_num)) {
int current_file;
for (current_file = sel->first; current_file <= sel->last; current_file++) {
filelist_entry_select_index_set(filelist, current_file, select, flag, check);
@@ -2948,7 +2963,7 @@ uint filelist_entry_select_index_get(FileList *filelist, const int index, FileCh
bool filelist_entry_is_selected(FileList *filelist, const int index)
{
- BLI_assert(index >= 0 && index < filelist->filelist.nbr_entries_filtered);
+ BLI_assert(index >= 0 && index < filelist->filelist.entries_filtered_num);
FileListInternEntry *intern_entry = filelist->filelist_intern.filtered[index];
/* BLI_ghash_lookup returns NULL if not found, which gets mapped to 0, which gets mapped to
@@ -3015,13 +3030,13 @@ static int filelist_readjob_list_dir(const char *root,
const bool skip_currpar)
{
struct direntry *files;
- int nbr_files, nbr_entries = 0;
+ int entries_num = 0;
/* Full path of the item. */
char full_path[FILE_MAX];
- nbr_files = BLI_filelist_dir_contents(root, &files);
+ const int files_num = BLI_filelist_dir_contents(root, &files);
if (files) {
- int i = nbr_files;
+ int i = files_num;
while (i--) {
FileListInternEntry *entry;
@@ -3095,11 +3110,11 @@ static int filelist_readjob_list_dir(const char *root,
#endif
BLI_addtail(entries, entry);
- nbr_entries++;
+ entries_num++;
}
- BLI_filelist_free(files, nbr_files);
+ BLI_filelist_free(files, files_num);
}
- return nbr_entries;
+ return entries_num;
}
typedef enum ListLibOptions {
@@ -3355,13 +3370,13 @@ static void filelist_readjob_main_recursive(Main *bmain, FileList *filelist)
if (filelist->dir[0] == 0) {
/* make directories */
# ifdef WITH_FREESTYLE
- filelist->filelist.nbr_entries = 27;
+ filelist->filelist.entries_num = 27;
# else
- filelist->filelist.nbr_entries = 26;
+ filelist->filelist.entries_num = 26;
# endif
- filelist_resize(filelist, filelist->filelist.nbr_entries);
+ filelist_resize(filelist, filelist->filelist.entries_num);
- for (a = 0; a < filelist->filelist.nbr_entries; a++) {
+ for (a = 0; a < filelist->filelist.entries_num; a++) {
filelist->filelist.entries[a].typeflag |= FILE_TYPE_DIR;
}
@@ -3404,20 +3419,20 @@ static void filelist_readjob_main_recursive(Main *bmain, FileList *filelist)
return;
}
- filelist->filelist.nbr_entries = 0;
+ filelist->filelist.entries_num = 0;
for (id = lb->first; id; id = id->next) {
if (!(filelist->filter_data.flags & FLF_HIDE_DOT) || id->name[2] != '.') {
- filelist->filelist.nbr_entries++;
+ filelist->filelist.entries_num++;
}
}
/* XXX TODO: if data-browse or append/link #FLF_HIDE_PARENT has to be set. */
if (!(filelist->filter_data.flags & FLF_HIDE_PARENT)) {
- filelist->filelist.nbr_entries++;
+ filelist->filelist.entries_num++;
}
- if (filelist->filelist.nbr_entries > 0) {
- filelist_resize(filelist, filelist->filelist.nbr_entries);
+ if (filelist->filelist.entries_num > 0) {
+ filelist_resize(filelist, filelist->filelist.entries_num);
}
files = filelist->filelist.entries;
@@ -3523,11 +3538,11 @@ typedef struct FileListReadJob {
static void filelist_readjob_append_entries(FileListReadJob *job_params,
ListBase *from_entries,
- int nbr_from_entries,
+ int from_entries_num,
short *do_update)
{
- BLI_assert(BLI_listbase_count(from_entries) == nbr_from_entries);
- if (nbr_from_entries <= 0) {
+ BLI_assert(BLI_listbase_count(from_entries) == from_entries_num);
+ if (from_entries_num <= 0) {
*do_update = false;
return;
}
@@ -3535,7 +3550,7 @@ static void filelist_readjob_append_entries(FileListReadJob *job_params,
FileList *filelist = job_params->tmp_filelist; /* Use the thread-safe filelist queue. */
BLI_mutex_lock(&job_params->lock);
BLI_movelisttolist(&filelist->filelist.entries, from_entries);
- filelist->filelist.nbr_entries += nbr_from_entries;
+ filelist->filelist.entries_num += from_entries_num;
BLI_mutex_unlock(&job_params->lock);
*do_update = true;
@@ -3591,7 +3606,7 @@ static void filelist_readjob_recursive_dir_add_items(const bool do_lib,
char filter_glob[FILE_MAXFILE];
const char *root = filelist->filelist.root;
const int max_recursion = filelist->max_recursion;
- int nbr_done_dirs = 0, nbr_todo_dirs = 1;
+ int dirs_done_count = 0, dirs_todo_count = 1;
todo_dirs = BLI_stack_new(sizeof(*td_dir), __func__);
td_dir = BLI_stack_push_r(todo_dirs);
@@ -3611,7 +3626,7 @@ static void filelist_readjob_recursive_dir_add_items(const bool do_lib,
while (!BLI_stack_is_empty(todo_dirs) && !(*stop)) {
FileListInternEntry *entry;
- int nbr_entries = 0;
+ int entries_num = 0;
char *subdir;
char rel_subdir[FILE_MAX_LIBEXTRA];
@@ -3651,15 +3666,15 @@ static void filelist_readjob_recursive_dir_add_items(const bool do_lib,
if (filelist->asset_library_ref) {
list_lib_options |= LIST_LIB_ASSETS_ONLY;
}
- nbr_entries = filelist_readjob_list_lib(
+ entries_num = filelist_readjob_list_lib(
subdir, &entries, list_lib_options, &indexer_runtime);
- if (nbr_entries > 0) {
+ if (entries_num > 0) {
is_lib = true;
}
}
if (!is_lib) {
- nbr_entries = filelist_readjob_list_dir(
+ entries_num = filelist_readjob_list_dir(
subdir, &entries, filter_glob, do_lib, job_params->main_name, skip_currpar);
}
@@ -3683,14 +3698,14 @@ static void filelist_readjob_recursive_dir_add_items(const bool do_lib,
td_dir = BLI_stack_push_r(todo_dirs);
td_dir->level = recursion_level + 1;
td_dir->dir = BLI_strdup(dir);
- nbr_todo_dirs++;
+ dirs_todo_count++;
}
}
- filelist_readjob_append_entries(job_params, &entries, nbr_entries, do_update);
+ filelist_readjob_append_entries(job_params, &entries, entries_num, do_update);
- nbr_done_dirs++;
- *progress = (float)nbr_done_dirs / (float)nbr_todo_dirs;
+ dirs_done_count++;
+ *progress = (float)dirs_done_count / (float)dirs_todo_count;
MEM_freeN(subdir);
}
@@ -3723,10 +3738,10 @@ static void filelist_readjob_do(const bool do_lib,
// BLI_assert(filelist->filtered == NULL);
BLI_assert(BLI_listbase_is_empty(&filelist->filelist.entries) &&
- (filelist->filelist.nbr_entries == FILEDIR_NBR_ENTRIES_UNSET));
+ (filelist->filelist.entries_num == FILEDIR_NBR_ENTRIES_UNSET));
/* A valid, but empty directory from now. */
- filelist->filelist.nbr_entries = 0;
+ filelist->filelist.entries_num = 0;
filelist_readjob_recursive_dir_add_items(do_lib, job_params, stop, do_update, progress);
}
@@ -3797,7 +3812,7 @@ static void filelist_readjob_main_assets_add_items(FileListReadJob *job_params,
FileListInternEntry *entry;
ListBase tmp_entries = {0};
ID *id_iter;
- int nbr_entries = 0;
+ int entries_num = 0;
/* Make sure no IDs are added/removed/reallocated in the main thread while this is running in
* parallel. */
@@ -3820,19 +3835,19 @@ static void filelist_readjob_main_assets_add_items(FileListReadJob *job_params,
entry->local_data.preview_image = BKE_asset_metadata_preview_get_from_id(id_iter->asset_data,
id_iter);
entry->local_data.id = id_iter;
- nbr_entries++;
+ entries_num++;
BLI_addtail(&tmp_entries, entry);
}
FOREACH_MAIN_ID_END;
BKE_main_unlock(job_params->current_main);
- if (nbr_entries) {
+ if (entries_num) {
*do_update = true;
BLI_movelisttolist(&filelist->filelist.entries, &tmp_entries);
- filelist->filelist.nbr_entries += nbr_entries;
- filelist->filelist.nbr_entries_filtered = -1;
+ filelist->filelist.entries_num += entries_num;
+ filelist->filelist.entries_filtered_num = -1;
}
}
@@ -3857,10 +3872,10 @@ static void filelist_readjob_asset_library(FileListReadJob *job_params,
FileList *filelist = job_params->tmp_filelist; /* Use the thread-safe filelist queue. */
BLI_assert(BLI_listbase_is_empty(&filelist->filelist.entries) &&
- (filelist->filelist.nbr_entries == FILEDIR_NBR_ENTRIES_UNSET));
+ (filelist->filelist.entries_num == FILEDIR_NBR_ENTRIES_UNSET));
/* A valid, but empty file-list from now. */
- filelist->filelist.nbr_entries = 0;
+ filelist->filelist.entries_num = 0;
/* NOP if already read. */
filelist_readjob_load_asset_library_data(job_params, do_update);
@@ -3889,12 +3904,12 @@ static void filelist_readjob_main_assets(FileListReadJob *job_params,
{
FileList *filelist = job_params->tmp_filelist; /* Use the thread-safe filelist queue. */
BLI_assert(BLI_listbase_is_empty(&filelist->filelist.entries) &&
- (filelist->filelist.nbr_entries == FILEDIR_NBR_ENTRIES_UNSET));
+ (filelist->filelist.entries_num == FILEDIR_NBR_ENTRIES_UNSET));
filelist_readjob_load_asset_library_data(job_params, do_update);
/* A valid, but empty file-list from now. */
- filelist->filelist.nbr_entries = 0;
+ filelist->filelist.entries_num = 0;
filelist_readjob_main_assets_add_items(job_params, stop, do_update, progress);
}
@@ -3917,7 +3932,7 @@ static void filelist_readjob_startjob(void *flrjv, short *stop, short *do_update
FileListReadJob *flrj = flrjv;
// printf("START filelist reading (%d files, main thread: %d)\n",
- // flrj->filelist->filelist.nbr_entries, BLI_thread_is_main());
+ // flrj->filelist->filelist.entries_num, BLI_thread_is_main());
BLI_mutex_lock(&flrj->lock);
@@ -3926,7 +3941,7 @@ static void filelist_readjob_startjob(void *flrjv, short *stop, short *do_update
flrj->tmp_filelist = MEM_dupallocN(flrj->filelist);
BLI_listbase_clear(&flrj->tmp_filelist->filelist.entries);
- flrj->tmp_filelist->filelist.nbr_entries = FILEDIR_NBR_ENTRIES_UNSET;
+ flrj->tmp_filelist->filelist.entries_num = FILEDIR_NBR_ENTRIES_UNSET;
flrj->tmp_filelist->filelist_intern.filtered = NULL;
BLI_listbase_clear(&flrj->tmp_filelist->filelist_intern.entries);
@@ -3958,18 +3973,18 @@ static void filelist_readjob_update(void *flrjv)
FileListReadJob *flrj = flrjv;
FileListIntern *fl_intern = &flrj->filelist->filelist_intern;
ListBase new_entries = {NULL};
- int nbr_entries, new_nbr_entries = 0;
+ int entries_num, new_entries_num = 0;
BLI_movelisttolist(&new_entries, &fl_intern->entries);
- nbr_entries = flrj->filelist->filelist.nbr_entries;
+ entries_num = flrj->filelist->filelist.entries_num;
BLI_mutex_lock(&flrj->lock);
- if (flrj->tmp_filelist->filelist.nbr_entries > 0) {
+ if (flrj->tmp_filelist->filelist.entries_num > 0) {
/* We just move everything out of 'thread context' into final list. */
- new_nbr_entries = flrj->tmp_filelist->filelist.nbr_entries;
+ new_entries_num = flrj->tmp_filelist->filelist.entries_num;
BLI_movelisttolist(&new_entries, &flrj->tmp_filelist->filelist.entries);
- flrj->tmp_filelist->filelist.nbr_entries = 0;
+ flrj->tmp_filelist->filelist.entries_num = 0;
}
if (flrj->tmp_filelist->asset_library) {
@@ -3983,7 +3998,7 @@ static void filelist_readjob_update(void *flrjv)
BLI_mutex_unlock(&flrj->lock);
- if (new_nbr_entries) {
+ if (new_entries_num) {
/* Do not clear selection cache, we can assume already 'selected' UIDs are still valid! Keep
* the asset library data we just read. */
filelist_clear_ex(flrj->filelist, false, true, false);
@@ -3991,9 +4006,9 @@ static void filelist_readjob_update(void *flrjv)
flrj->filelist->flags |= (FL_NEED_SORTING | FL_NEED_FILTERING);
}
- /* if no new_nbr_entries, this is NOP */
+ /* if no new_entries_num, this is NOP */
BLI_movelisttolist(&fl_intern->entries, &new_entries);
- flrj->filelist->filelist.nbr_entries = MAX2(nbr_entries, 0) + new_nbr_entries;
+ flrj->filelist->filelist.entries_num = MAX2(entries_num, 0) + new_entries_num;
}
static void filelist_readjob_endjob(void *flrjv)
@@ -4011,11 +4026,11 @@ static void filelist_readjob_free(void *flrjv)
{
FileListReadJob *flrj = flrjv;
- // printf("END filelist reading (%d files)\n", flrj->filelist->filelist.nbr_entries);
+ // printf("END filelist reading (%d files)\n", flrj->filelist->filelist.entries_num);
if (flrj->tmp_filelist) {
/* tmp_filelist shall never ever be filtered! */
- BLI_assert(flrj->tmp_filelist->filelist.nbr_entries == 0);
+ BLI_assert(flrj->tmp_filelist->filelist.entries_num == 0);
BLI_assert(BLI_listbase_is_empty(&flrj->tmp_filelist->filelist.entries));
filelist_freelib(flrj->tmp_filelist);
diff --git a/source/blender/editors/space_file/fsmenu.c b/source/blender/editors/space_file/fsmenu.c
index 847bf89bba8..ae0e5b23d55 100644
--- a/source/blender/editors/space_file/fsmenu.c
+++ b/source/blender/editors/space_file/fsmenu.c
@@ -968,13 +968,13 @@ void fsmenu_read_system(struct FSMenu *fsmenu, int read_bookmarks)
/* Check gvfs shares. */
const char *const xdg_runtime_dir = BLI_getenv("XDG_RUNTIME_DIR");
if (xdg_runtime_dir != NULL) {
- struct direntry *dir;
+ struct direntry *dirs;
char name[FILE_MAX];
BLI_join_dirfile(name, sizeof(name), xdg_runtime_dir, "gvfs/");
- const uint dir_len = BLI_filelist_dir_contents(name, &dir);
- for (uint i = 0; i < dir_len; i++) {
- if (dir[i].type & S_IFDIR) {
- const char *dirname = dir[i].relname;
+ const uint dirs_num = BLI_filelist_dir_contents(name, &dirs);
+ for (uint i = 0; i < dirs_num; i++) {
+ if (dirs[i].type & S_IFDIR) {
+ const char *dirname = dirs[i].relname;
if (dirname[0] != '.') {
/* Dir names contain a lot of unwanted text.
* Assuming every entry ends with the share name */
@@ -992,7 +992,7 @@ void fsmenu_read_system(struct FSMenu *fsmenu, int read_bookmarks)
}
}
}
- BLI_filelist_free(dir, dir_len);
+ BLI_filelist_free(dirs, dirs_num);
}
# endif
diff --git a/source/blender/editors/space_graph/graph_intern.h b/source/blender/editors/space_graph/graph_intern.h
index fe8005892cf..2eb64e5b115 100644
--- a/source/blender/editors/space_graph/graph_intern.h
+++ b/source/blender/editors/space_graph/graph_intern.h
@@ -113,6 +113,7 @@ void GRAPH_OT_clean(struct wmOperatorType *ot);
void GRAPH_OT_blend_to_neighbor(struct wmOperatorType *ot);
void GRAPH_OT_breakdown(struct wmOperatorType *ot);
void GRAPH_OT_decimate(struct wmOperatorType *ot);
+void GRAPH_OT_blend_to_default(struct wmOperatorType *ot);
void GRAPH_OT_sample(struct wmOperatorType *ot);
void GRAPH_OT_bake(struct wmOperatorType *ot);
void GRAPH_OT_unbake(struct wmOperatorType *ot);
diff --git a/source/blender/editors/space_graph/graph_ops.c b/source/blender/editors/space_graph/graph_ops.c
index b00e069470d..128925d4591 100644
--- a/source/blender/editors/space_graph/graph_ops.c
+++ b/source/blender/editors/space_graph/graph_ops.c
@@ -457,6 +457,7 @@ void graphedit_operatortypes(void)
WM_operatortype_append(GRAPH_OT_decimate);
WM_operatortype_append(GRAPH_OT_blend_to_neighbor);
WM_operatortype_append(GRAPH_OT_breakdown);
+ WM_operatortype_append(GRAPH_OT_blend_to_default);
WM_operatortype_append(GRAPH_OT_euler_filter);
WM_operatortype_append(GRAPH_OT_delete);
WM_operatortype_append(GRAPH_OT_duplicate);
diff --git a/source/blender/editors/space_graph/graph_slider_ops.c b/source/blender/editors/space_graph/graph_slider_ops.c
index 1a3355b0139..313f6ca1561 100644
--- a/source/blender/editors/space_graph/graph_slider_ops.c
+++ b/source/blender/editors/space_graph/graph_slider_ops.c
@@ -550,7 +550,7 @@ void GRAPH_OT_decimate(wmOperatorType *ot)
/** \} */
/* -------------------------------------------------------------------- */
-/** \name Blend To Neighbor Operator
+/** \name Blend to Neighbor Operator
* \{ */
static void blend_to_neighbor_graph_keys(bAnimContext *ac, float factor)
@@ -584,7 +584,7 @@ static void blend_to_neighbor_draw_status_header(bContext *C, tGraphSliderOp *gs
ED_slider_status_string_get(gso->slider, slider_string, UI_MAX_DRAW_STR);
- strcpy(mode_str, TIP_("Blend To Neighbor"));
+ strcpy(mode_str, TIP_("Blend to Neighbor"));
if (hasNumInput(&gso->num)) {
char str_ofs[NUM_STR_REP_LEN];
@@ -652,7 +652,7 @@ static int blend_to_neighbor_exec(bContext *C, wmOperator *op)
void GRAPH_OT_blend_to_neighbor(wmOperatorType *ot)
{
/* Identifiers. */
- ot->name = "Blend To Neighbor";
+ ot->name = "Blend to Neighbor";
ot->idname = "GRAPH_OT_blend_to_neighbor";
ot->description = "Blend selected keyframes to their left or right neighbor";
@@ -802,3 +802,131 @@ void GRAPH_OT_breakdown(wmOperatorType *ot)
}
/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Blend to Default Value Operator
+ * \{ */
+
+static void blend_to_default_graph_keys(bAnimContext *ac, const float factor)
+{
+ ListBase anim_data = {NULL, NULL};
+ ANIM_animdata_filter(ac, &anim_data, OPERATOR_DATA_FILTER, ac->data, ac->datatype);
+
+ LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
+ FCurve *fcu = (FCurve *)ale->key_data;
+
+ /* Check if the curves actually have any points. */
+ if (fcu == NULL || fcu->bezt == NULL || fcu->totvert == 0) {
+ continue;
+ }
+
+ PointerRNA id_ptr;
+ RNA_id_pointer_create(ale->id, &id_ptr);
+
+ blend_to_default_fcurve(&id_ptr, fcu, factor);
+ ale->update |= ANIM_UPDATE_DEFAULT;
+ }
+
+ ANIM_animdata_update(ac, &anim_data);
+ ANIM_animdata_freelist(&anim_data);
+}
+
+static void blend_to_default_draw_status_header(bContext *C, tGraphSliderOp *gso)
+{
+ char status_str[UI_MAX_DRAW_STR];
+ char mode_str[32];
+ char slider_string[UI_MAX_DRAW_STR];
+
+ ED_slider_status_string_get(gso->slider, slider_string, UI_MAX_DRAW_STR);
+
+ strcpy(mode_str, TIP_("Blend to Default Value"));
+
+ if (hasNumInput(&gso->num)) {
+ char str_ofs[NUM_STR_REP_LEN];
+
+ outputNumInput(&gso->num, str_ofs, &gso->scene->unit);
+
+ BLI_snprintf(status_str, sizeof(status_str), "%s: %s", mode_str, str_ofs);
+ }
+ else {
+ BLI_snprintf(status_str, sizeof(status_str), "%s: %s", mode_str, slider_string);
+ }
+
+ ED_workspace_status_text(C, status_str);
+}
+
+static void blend_to_default_modal_update(bContext *C, wmOperator *op)
+{
+ tGraphSliderOp *gso = op->customdata;
+
+ blend_to_default_draw_status_header(C, gso);
+
+ /* Set notifier that keyframes have changed. */
+ reset_bezts(gso);
+ const float factor = ED_slider_factor_get(gso->slider);
+ RNA_property_float_set(op->ptr, gso->factor_prop, factor);
+ blend_to_default_graph_keys(&gso->ac, factor);
+ WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
+}
+
+static int blend_to_default_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ const int invoke_result = graph_slider_invoke(C, op, event);
+
+ if (invoke_result == OPERATOR_CANCELLED) {
+ return invoke_result;
+ }
+
+ tGraphSliderOp *gso = op->customdata;
+ gso->modal_update = blend_to_default_modal_update;
+ gso->factor_prop = RNA_struct_find_property(op->ptr, "factor");
+ blend_to_default_draw_status_header(C, gso);
+
+ return invoke_result;
+}
+
+static int blend_to_default_exec(bContext *C, wmOperator *op)
+{
+ bAnimContext ac;
+
+ if (ANIM_animdata_get_context(C, &ac) == 0) {
+ return OPERATOR_CANCELLED;
+ }
+
+ const float factor = RNA_float_get(op->ptr, "factor");
+
+ blend_to_default_graph_keys(&ac, factor);
+
+ /* Set notifier that keyframes have changed. */
+ WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void GRAPH_OT_blend_to_default(wmOperatorType *ot)
+{
+ /* Identifiers. */
+ ot->name = "Blend to Default Value";
+ ot->idname = "GRAPH_OT_blend_to_default";
+ ot->description = "Blend selected keys to their default value from their current position";
+
+ /* API callbacks. */
+ ot->invoke = blend_to_default_invoke;
+ ot->modal = graph_slider_modal;
+ ot->exec = blend_to_default_exec;
+ ot->poll = graphop_editable_keyframes_poll;
+
+ /* Flags. */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ RNA_def_float_factor(ot->srna,
+ "factor",
+ 1.0f / 3.0f,
+ -FLT_MAX,
+ FLT_MAX,
+ "Factor",
+ "How much to blend to the default value",
+ 0.0f,
+ 1.0f);
+}
+/** \} */
diff --git a/source/blender/editors/space_image/CMakeLists.txt b/source/blender/editors/space_image/CMakeLists.txt
index f5cc6083b25..c385420b18e 100644
--- a/source/blender/editors/space_image/CMakeLists.txt
+++ b/source/blender/editors/space_image/CMakeLists.txt
@@ -60,6 +60,9 @@ if(WITH_IMAGE_CINEON)
add_definitions(-DWITH_CINEON)
endif()
+if(WITH_IMAGE_WEBP)
+ add_definitions(-DWITH_WEBP)
+endif()
blender_add_lib(bf_editor_space_image "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/space_image/image_buttons.c b/source/blender/editors/space_image/image_buttons.c
index 0af32a717a4..208928afc1f 100644
--- a/source/blender/editors/space_image/image_buttons.c
+++ b/source/blender/editors/space_image/image_buttons.c
@@ -20,6 +20,7 @@
#include "BKE_context.h"
#include "BKE_image.h"
+#include "BKE_image_format.h"
#include "BKE_node.h"
#include "BKE_scene.h"
#include "BKE_screen.h"
@@ -957,14 +958,11 @@ void uiTemplateImageSettings(uiLayout *layout, PointerRNA *imfptr, bool color_ma
{
ImageFormatData *imf = imfptr->data;
ID *id = imfptr->owner_id;
- PointerRNA display_settings_ptr;
- PropertyRNA *prop;
const int depth_ok = BKE_imtype_valid_depths(imf->imtype);
/* some settings depend on this being a scene that's rendered */
const bool is_render_out = (id && GS(id->name) == ID_SCE);
uiLayout *col;
- bool show_preview = false;
col = uiLayoutColumn(layout, false);
@@ -1004,7 +1002,6 @@ void uiTemplateImageSettings(uiLayout *layout, PointerRNA *imfptr, bool color_ma
}
if (is_render_out && ELEM(imf->imtype, R_IMF_IMTYPE_OPENEXR, R_IMF_IMTYPE_MULTILAYER)) {
- show_preview = true;
uiItemR(col, imfptr, "use_preview", 0, NULL, ICON_NONE);
}
@@ -1036,18 +1033,22 @@ void uiTemplateImageSettings(uiLayout *layout, PointerRNA *imfptr, bool color_ma
uiItemR(col, imfptr, "tiff_codec", 0, NULL, ICON_NONE);
}
- /* color management */
- if (color_management && (!BKE_imtype_requires_linear_float(imf->imtype) ||
- (show_preview && imf->flag & R_IMF_FLAG_PREVIEW_JPG))) {
- prop = RNA_struct_find_property(imfptr, "display_settings");
- display_settings_ptr = RNA_property_pointer_get(imfptr, prop);
-
- col = uiLayoutColumn(layout, false);
- uiItemL(col, IFACE_("Color Management"), ICON_NONE);
-
- uiItemR(col, &display_settings_ptr, "display_device", 0, NULL, ICON_NONE);
+ /* Override color management */
+ if (color_management) {
+ uiItemS(col);
+ uiItemR(col, imfptr, "color_management", 0, NULL, ICON_NONE);
- uiTemplateColormanagedViewSettings(col, NULL, imfptr, "view_settings");
+ if (imf->color_management == R_IMF_COLOR_MANAGEMENT_OVERRIDE) {
+ if (BKE_imtype_requires_linear_float(imf->imtype)) {
+ PointerRNA linear_settings_ptr = RNA_pointer_get(imfptr, "linear_colorspace_settings");
+ uiItemR(col, &linear_settings_ptr, "name", 0, IFACE_("Color Space"), ICON_NONE);
+ }
+ else {
+ PointerRNA display_settings_ptr = RNA_pointer_get(imfptr, "display_settings");
+ uiItemR(col, &display_settings_ptr, "display_device", 0, NULL, ICON_NONE);
+ uiTemplateColormanagedViewSettings(col, NULL, imfptr, "view_settings");
+ }
+ }
}
}
diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c
index 3721ea81c04..1c4a1d7e8c9 100644
--- a/source/blender/editors/space_image/image_ops.c
+++ b/source/blender/editors/space_image/image_ops.c
@@ -38,6 +38,7 @@
#include "BKE_global.h"
#include "BKE_icons.h"
#include "BKE_image.h"
+#include "BKE_image_format.h"
#include "BKE_image_save.h"
#include "BKE_layer.h"
#include "BKE_lib_id.h"
@@ -53,7 +54,7 @@
#include "IMB_imbuf.h"
#include "IMB_imbuf_types.h"
#include "IMB_moviecache.h"
-#include "intern/openexr/openexr_multi.h"
+#include "IMB_openexr.h"
#include "RE_pipeline.h"
@@ -1736,7 +1737,7 @@ static int image_save_options_init(Main *bmain,
if (ELEM(ima->type, IMA_TYPE_R_RESULT, IMA_TYPE_COMPOSITE)) {
/* imtype */
- opts->im_format = scene->r.im_format;
+ BKE_image_format_init_for_write(&opts->im_format, scene, NULL);
is_depth_set = true;
if (!BKE_image_is_multiview(ima)) {
/* In case multiview is disabled,
@@ -1752,14 +1753,18 @@ static int image_save_options_init(Main *bmain,
opts->im_format.planes = ibuf->planes;
}
else {
- BKE_imbuf_to_image_format(&opts->im_format, ibuf);
+ BKE_image_format_from_imbuf(&opts->im_format, ibuf);
}
/* use the multiview image settings as the default */
opts->im_format.stereo3d_format = *ima->stereo3d_format;
opts->im_format.views_format = ima->views_format;
+
+ BKE_image_format_color_management_copy_from_scene(&opts->im_format, scene);
}
+ opts->im_format.color_management = R_IMF_COLOR_MANAGEMENT_FOLLOW_SCENE;
+
if (ima->source == IMA_SRC_TILED) {
BLI_strncpy(opts->filepath, ima->filepath, sizeof(opts->filepath));
BLI_path_abs(opts->filepath, ID_BLEND_PATH_FROM_GLOBAL(&ima->id));
@@ -1809,13 +1814,6 @@ static int image_save_options_init(Main *bmain,
STR_CONCAT(opts->filepath, len, ".<UDIM>");
}
}
-
- /* color management */
- BKE_color_managed_display_settings_copy(&opts->im_format.display_settings,
- &scene->display_settings);
-
- BKE_color_managed_view_settings_free(&opts->im_format.view_settings);
- BKE_color_managed_view_settings_copy(&opts->im_format.view_settings, &scene->view_settings);
}
BKE_image_release_ibuf(ima, ibuf, lock);
@@ -1829,8 +1827,8 @@ static void image_save_options_from_op(Main *bmain,
ImageFormatData *imf)
{
if (imf) {
- BKE_color_managed_view_settings_free(&opts->im_format.view_settings);
- opts->im_format = *imf;
+ BKE_image_format_free(&opts->im_format);
+ BKE_image_format_copy(&opts->im_format, imf);
}
if (RNA_struct_property_is_set(op->ptr, "filepath")) {
@@ -1843,8 +1841,8 @@ static void image_save_options_to_op(ImageSaveOptions *opts, wmOperator *op)
{
if (op->customdata) {
ImageSaveData *isd = op->customdata;
- BKE_color_managed_view_settings_free(&isd->im_format.view_settings);
- isd->im_format = opts->im_format;
+ BKE_image_format_free(&isd->im_format);
+ BKE_image_format_copy(&isd->im_format, &opts->im_format);
}
RNA_string_set(op->ptr, "filepath", opts->filepath);
@@ -1878,7 +1876,7 @@ static void image_save_as_free(wmOperator *op)
{
if (op->customdata) {
ImageSaveData *isd = op->customdata;
- BKE_color_managed_view_settings_free(&isd->im_format.view_settings);
+ BKE_image_format_free(&isd->im_format);
MEM_freeN(op->customdata);
op->customdata = NULL;
@@ -1920,6 +1918,8 @@ static int image_save_as_exec(bContext *C, wmOperator *op)
BKE_image_free_packedfiles(image);
}
+ BKE_image_save_options_free(&opts);
+
image_save_as_free(op);
return OPERATOR_FINISHED;
@@ -1948,6 +1948,7 @@ static int image_save_as_invoke(bContext *C, wmOperator *op, const wmEvent *UNUS
BKE_image_save_options_init(&opts, bmain, scene);
if (image_save_options_init(bmain, &opts, ima, iuser, true, save_as_render) == 0) {
+ BKE_image_save_options_free(&opts);
return OPERATOR_CANCELLED;
}
image_save_options_to_op(&opts, op);
@@ -1964,7 +1965,7 @@ static int image_save_as_invoke(bContext *C, wmOperator *op, const wmEvent *UNUS
isd->image = ima;
isd->iuser = iuser;
- memcpy(&isd->im_format, &opts.im_format, sizeof(opts.im_format));
+ BKE_image_format_copy(&isd->im_format, &opts.im_format);
op->customdata = isd;
/* show multiview save options only if image has multiviews */
@@ -1974,6 +1975,7 @@ static int image_save_as_invoke(bContext *C, wmOperator *op, const wmEvent *UNUS
RNA_property_boolean_set(op->ptr, prop, BKE_image_is_multiview(ima));
image_filesel(C, op, opts.filepath);
+ BKE_image_save_options_free(&opts);
return OPERATOR_RUNNING_MODAL;
}
@@ -2001,15 +2003,21 @@ static void image_save_as_draw(bContext *UNUSED(C), wmOperator *op)
ImageSaveData *isd = op->customdata;
PointerRNA imf_ptr;
const bool is_multiview = RNA_boolean_get(op->ptr, "show_multiview");
+ const bool use_color_management = RNA_boolean_get(op->ptr, "save_as_render");
- /* image template */
- RNA_pointer_create(NULL, &RNA_ImageFormatSettings, &isd->im_format, &imf_ptr);
- uiTemplateImageSettings(layout, &imf_ptr, false);
+ uiLayoutSetPropSep(layout, true);
+ uiLayoutSetPropDecorate(layout, false);
/* main draw call */
uiDefAutoButsRNA(
layout, op->ptr, image_save_as_draw_check_prop, NULL, NULL, UI_BUT_LABEL_ALIGN_NONE, false);
+ uiItemS(layout);
+
+ /* image template */
+ RNA_pointer_create(NULL, &RNA_ImageFormatSettings, &isd->im_format, &imf_ptr);
+ uiTemplateImageSettings(layout, &imf_ptr, use_color_management);
+
/* multiview template */
if (is_multiview) {
uiTemplateImageFormatViews(layout, &imf_ptr, op->ptr);
@@ -2132,6 +2140,7 @@ static int image_save_exec(bContext *C, wmOperator *op)
BKE_image_save_options_init(&opts, bmain, scene);
if (image_save_options_init(bmain, &opts, image, iuser, false, false) == 0) {
+ BKE_image_save_options_free(&opts);
return OPERATOR_CANCELLED;
}
image_save_options_from_op(bmain, &opts, op, NULL);
@@ -2147,7 +2156,7 @@ static int image_save_exec(bContext *C, wmOperator *op)
ok = true;
}
- BKE_color_managed_view_settings_free(&opts.im_format.view_settings);
+ BKE_image_save_options_free(&opts);
if (ok) {
return OPERATOR_FINISHED;
@@ -2399,6 +2408,7 @@ bool ED_image_save_all_modified(const bContext *C, ReportList *reports)
bool saved_successfully = BKE_image_save(reports, bmain, ima, NULL, &opts);
ok = ok && saved_successfully;
}
+ BKE_image_save_options_free(&opts);
}
}
}
diff --git a/source/blender/editors/space_nla/space_nla.c b/source/blender/editors/space_nla/space_nla.c
index 3e7784d0364..42d3d841f4b 100644
--- a/source/blender/editors/space_nla/space_nla.c
+++ b/source/blender/editors/space_nla/space_nla.c
@@ -216,7 +216,6 @@ static void nla_main_region_draw(const bContext *C, ARegion *region)
Scene *scene = CTX_data_scene(C);
bAnimContext ac;
View2D *v2d = &region->v2d;
- short cfra_flag = 0;
/* clear and setup matrix */
UI_ThemeClearColor(TH_BACK);
@@ -240,11 +239,6 @@ static void nla_main_region_draw(const bContext *C, ARegion *region)
UI_view2d_text_cache_draw(region);
}
- /* current frame */
- if (snla->flag & SNLA_DRAWTIME) {
- cfra_flag |= DRAWCFRA_UNIT_SECONDS;
- }
-
/* markers */
UI_view2d_view_orthoSpecial(region, v2d, 1);
int marker_draw_flag = DRAW_MARKERS_MARGIN;
diff --git a/source/blender/editors/space_node/link_drag_search.cc b/source/blender/editors/space_node/link_drag_search.cc
index ccd3333fcc5..c524de2c55d 100644
--- a/source/blender/editors/space_node/link_drag_search.cc
+++ b/source/blender/editors/space_node/link_drag_search.cc
@@ -121,9 +121,6 @@ static void gather_socket_link_operations(bNodeTree &node_tree,
Vector<SocketLinkOperation> &search_link_ops)
{
NODE_TYPES_BEGIN (node_type) {
- if (StringRef(node_type->idname).find("Legacy") != StringRef::not_found) {
- continue;
- }
const char *disabled_hint;
if (!(node_type->poll && node_type->poll(node_type, &node_tree, &disabled_hint))) {
continue;
diff --git a/source/blender/editors/space_node/node_add.cc b/source/blender/editors/space_node/node_add.cc
index 30bd0fb528b..7fb15d69ab5 100644
--- a/source/blender/editors/space_node/node_add.cc
+++ b/source/blender/editors/space_node/node_add.cc
@@ -517,113 +517,6 @@ void NODE_OT_add_object(wmOperatorType *ot)
/** \} */
/* -------------------------------------------------------------------- */
-/** \name Add Node Texture Operator
- * \{ */
-
-static Tex *node_add_texture_get_and_poll_texture_node_tree(Main *bmain, wmOperator *op)
-{
- if (RNA_struct_property_is_set(op->ptr, "session_uuid")) {
- const uint32_t session_uuid = (uint32_t)RNA_int_get(op->ptr, "session_uuid");
- return (Tex *)BKE_libblock_find_session_uuid(bmain, ID_TE, session_uuid);
- }
-
- char name[MAX_ID_NAME - 2];
- RNA_string_get(op->ptr, "name", name);
- return (Tex *)BKE_libblock_find_name(bmain, ID_TE, name);
-}
-
-static int node_add_texture_exec(bContext *C, wmOperator *op)
-{
- Main *bmain = CTX_data_main(C);
- SpaceNode *snode = CTX_wm_space_node(C);
- bNodeTree *ntree = snode->edittree;
- Tex *texture;
-
- if (!(texture = node_add_texture_get_and_poll_texture_node_tree(bmain, op))) {
- return OPERATOR_CANCELLED;
- }
-
- ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
-
- bNode *texture_node = node_add_node(*C,
- nullptr,
- GEO_NODE_LEGACY_ATTRIBUTE_SAMPLE_TEXTURE,
- snode->runtime->cursor[0],
- snode->runtime->cursor[1]);
- if (!texture_node) {
- BKE_report(op->reports, RPT_WARNING, "Could not add texture node");
- return OPERATOR_CANCELLED;
- }
-
- texture_node->id = &texture->id;
- id_us_plus(&texture->id);
-
- nodeSetActive(ntree, texture_node);
- ED_node_tree_propagate_change(C, bmain, ntree);
- DEG_relations_tag_update(bmain);
-
- return OPERATOR_FINISHED;
-}
-
-static int node_add_texture_invoke(bContext *C, wmOperator *op, const wmEvent *event)
-{
- ARegion *region = CTX_wm_region(C);
- SpaceNode *snode = CTX_wm_space_node(C);
-
- /* Convert mouse coordinates to v2d space. */
- UI_view2d_region_to_view(&region->v2d,
- event->mval[0],
- event->mval[1],
- &snode->runtime->cursor[0],
- &snode->runtime->cursor[1]);
-
- snode->runtime->cursor[0] /= UI_DPI_FAC;
- snode->runtime->cursor[1] /= UI_DPI_FAC;
-
- return node_add_texture_exec(C, op);
-}
-
-static bool node_add_texture_poll(bContext *C)
-{
- const SpaceNode *snode = CTX_wm_space_node(C);
- return ED_operator_node_editable(C) && ELEM(snode->nodetree->type, NTREE_GEOMETRY) &&
- !UI_but_active_drop_name(C);
-}
-
-void NODE_OT_add_texture(wmOperatorType *ot)
-{
- PropertyRNA *prop;
-
- /* identifiers */
- ot->name = "Add Node Texture";
- ot->description = "Add a texture to the current node editor";
- ot->idname = "NODE_OT_add_texture";
-
- /* callbacks */
- ot->exec = node_add_texture_exec;
- ot->invoke = node_add_texture_invoke;
- ot->poll = node_add_texture_poll;
-
- /* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
-
- RNA_def_string(
- ot->srna, "name", "Texture", MAX_ID_NAME - 2, "Name", "Data-block name to assign");
- prop = RNA_def_int(ot->srna,
- "session_uuid",
- 0,
- INT32_MIN,
- INT32_MAX,
- "Session UUID",
- "Session UUID of the data-block to assign",
- INT32_MIN,
- INT32_MAX);
- RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE));
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
/** \name Add Node Collection Operator
* \{ */
diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc
index e638816e3fc..7f0c426922b 100644
--- a/source/blender/editors/space_node/node_draw.cc
+++ b/source/blender/editors/space_node/node_draw.cc
@@ -73,11 +73,10 @@
#include "node_intern.hh" /* own include */
-using blender::fn::CPPType;
+using blender::GPointer;
using blender::fn::FieldCPPType;
using blender::fn::FieldInput;
using blender::fn::GField;
-using blender::fn::GPointer;
namespace geo_log = blender::nodes::geometry_nodes_eval_log;
extern "C" {
@@ -1418,8 +1417,6 @@ static int node_error_type_to_icon(const geo_log::NodeWarningType type)
return ICON_ERROR;
case geo_log::NodeWarningType::Info:
return ICON_INFO;
- case geo_log::NodeWarningType::Legacy:
- return ICON_ERROR;
}
BLI_assert(false);
@@ -1430,8 +1427,6 @@ static uint8_t node_error_type_priority(const geo_log::NodeWarningType type)
{
switch (type) {
case geo_log::NodeWarningType::Error:
- return 4;
- case geo_log::NodeWarningType::Legacy:
return 3;
case geo_log::NodeWarningType::Warning:
return 2;
diff --git a/source/blender/editors/space_node/node_edit.cc b/source/blender/editors/space_node/node_edit.cc
index 1ca2f877398..956bb581ee6 100644
--- a/source/blender/editors/space_node/node_edit.cc
+++ b/source/blender/editors/space_node/node_edit.cc
@@ -18,6 +18,7 @@
#include "BKE_context.h"
#include "BKE_global.h"
#include "BKE_image.h"
+#include "BKE_image_format.h"
#include "BKE_lib_id.h"
#include "BKE_main.h"
#include "BKE_material.h"
@@ -274,28 +275,14 @@ static void compo_startjob(void *cjv,
/* 1 is do_previews */
if ((cj->scene->r.scemode & R_MULTIVIEW) == 0) {
- ntreeCompositExecTree(cj->scene,
- ntree,
- &cj->scene->r,
- false,
- true,
- &scene->view_settings,
- &scene->display_settings,
- "");
+ ntreeCompositExecTree(cj->scene, ntree, &cj->scene->r, false, true, "");
}
else {
LISTBASE_FOREACH (SceneRenderView *, srv, &scene->r.views) {
if (BKE_scene_multiview_is_render_view_active(&scene->r, srv) == false) {
continue;
}
- ntreeCompositExecTree(cj->scene,
- ntree,
- &cj->scene->r,
- false,
- true,
- &scene->view_settings,
- &scene->display_settings,
- srv->name);
+ ntreeCompositExecTree(cj->scene, ntree, &cj->scene->r, false, true, srv->name);
}
}
@@ -897,19 +884,15 @@ struct NodeSizeWidget {
int directions;
};
-static void node_resize_init(bContext *C,
- wmOperator *op,
- const wmEvent *UNUSED(event),
- const bNode *node,
- NodeResizeDirection dir)
+static void node_resize_init(
+ bContext *C, wmOperator *op, const float cursor[2], const bNode *node, NodeResizeDirection dir)
{
- SpaceNode *snode = CTX_wm_space_node(C);
-
NodeSizeWidget *nsw = MEM_cnew<NodeSizeWidget>(__func__);
op->customdata = nsw;
- nsw->mxstart = snode->runtime->cursor[0] * UI_DPI_FAC;
- nsw->mystart = snode->runtime->cursor[1] * UI_DPI_FAC;
+
+ nsw->mxstart = cursor[0];
+ nsw->mystart = cursor[1];
/* store old */
nsw->oldlocx = node->locx;
@@ -1068,7 +1051,7 @@ static int node_resize_invoke(bContext *C, wmOperator *op, const wmEvent *event)
return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
}
- node_resize_init(C, op, event, node, dir);
+ node_resize_init(C, op, cursor, node, dir);
return OPERATOR_RUNNING_MODAL;
}
diff --git a/source/blender/editors/space_node/node_intern.hh b/source/blender/editors/space_node/node_intern.hh
index 319f97e57f5..cd40573607d 100644
--- a/source/blender/editors/space_node/node_intern.hh
+++ b/source/blender/editors/space_node/node_intern.hh
@@ -178,7 +178,6 @@ bool space_node_view_flag(
void NODE_OT_view_all(wmOperatorType *ot);
void NODE_OT_view_selected(wmOperatorType *ot);
-void NODE_OT_geometry_node_view_legacy(wmOperatorType *ot);
void NODE_OT_backimage_move(wmOperatorType *ot);
void NODE_OT_backimage_zoom(wmOperatorType *ot);
@@ -241,7 +240,6 @@ void NODE_OT_add_reroute(wmOperatorType *ot);
void NODE_OT_add_group(wmOperatorType *ot);
void NODE_OT_add_object(wmOperatorType *ot);
void NODE_OT_add_collection(wmOperatorType *ot);
-void NODE_OT_add_texture(wmOperatorType *ot);
void NODE_OT_add_file(wmOperatorType *ot);
void NODE_OT_add_mask(wmOperatorType *ot);
void NODE_OT_new_node_tree(wmOperatorType *ot);
diff --git a/source/blender/editors/space_node/node_ops.cc b/source/blender/editors/space_node/node_ops.cc
index e9903299300..ce000aba1da 100644
--- a/source/blender/editors/space_node/node_ops.cc
+++ b/source/blender/editors/space_node/node_ops.cc
@@ -37,7 +37,6 @@ void node_operatortypes()
WM_operatortype_append(NODE_OT_view_all);
WM_operatortype_append(NODE_OT_view_selected);
- WM_operatortype_append(NODE_OT_geometry_node_view_legacy);
WM_operatortype_append(NODE_OT_mute_toggle);
WM_operatortype_append(NODE_OT_hide_toggle);
@@ -79,7 +78,6 @@ void node_operatortypes()
WM_operatortype_append(NODE_OT_add_group);
WM_operatortype_append(NODE_OT_add_object);
WM_operatortype_append(NODE_OT_add_collection);
- WM_operatortype_append(NODE_OT_add_texture);
WM_operatortype_append(NODE_OT_add_file);
WM_operatortype_append(NODE_OT_add_mask);
diff --git a/source/blender/editors/space_node/node_templates.cc b/source/blender/editors/space_node/node_templates.cc
index 8cd87574465..b63cb2eeee5 100644
--- a/source/blender/editors/space_node/node_templates.cc
+++ b/source/blender/editors/space_node/node_templates.cc
@@ -69,11 +69,11 @@ static bool node_link_item_compare(bNode *node, NodeLinkItem *item)
return true;
}
-static void node_link_item_apply(Main *bmain, bNode *node, NodeLinkItem *item)
+static void node_link_item_apply(bNodeTree *ntree, bNode *node, NodeLinkItem *item)
{
if (ELEM(node->type, NODE_GROUP, NODE_CUSTOM_GROUP)) {
node->id = (ID *)item->ngroup;
- BKE_ntree_update_main_tree(bmain, item->ngroup, nullptr);
+ BKE_ntree_update_tag_node_property(ntree, node);
}
else {
/* nothing to do for now */
@@ -237,7 +237,8 @@ static void node_socket_add_replace(const bContext *C,
nodePositionRelative(node_from, node_to, sock_from_tmp, sock_to);
}
- node_link_item_apply(bmain, node_from, item);
+ node_link_item_apply(ntree, node_from, item);
+ ED_node_tree_propagate_change(C, bmain, ntree);
}
nodeSetActive(ntree, node_from);
diff --git a/source/blender/editors/space_node/node_view.cc b/source/blender/editors/space_node/node_view.cc
index 36fdcf37fd7..f5f5a9e6f67 100644
--- a/source/blender/editors/space_node/node_view.cc
+++ b/source/blender/editors/space_node/node_view.cc
@@ -686,90 +686,4 @@ void NODE_OT_backimage_sample(wmOperatorType *ot)
/** \} */
-/* -------------------------------------------------------------------- */
-/** \name View Geometry Nodes Legacy Operator
- *
- * This operator should be removed when the 2.93 legacy nodes are removed.
- * \{ */
-
-static int space_node_view_geometry_nodes_legacy(bContext *C, SpaceNode *snode, wmOperator *op)
-{
- ARegion *region = CTX_wm_region(C);
-
- /* Only use the node editor's active node tree. Otherwise this will be too complicated. */
- bNodeTree *node_tree = snode->nodetree;
- if (node_tree == nullptr || node_tree->type != NTREE_GEOMETRY) {
- return OPERATOR_CANCELLED;
- }
-
- bool found_legacy_node = false;
- LISTBASE_FOREACH_BACKWARD (bNode *, node, &node_tree->nodes) {
- StringRef idname{node->idname};
- if (idname.find("Legacy") == StringRef::not_found) {
- node->flag &= ~NODE_SELECT;
- }
- else {
- found_legacy_node = true;
- node->flag |= NODE_SELECT;
- }
- }
-
- if (!found_legacy_node) {
- WM_report(RPT_INFO, "Legacy node not found, may be in nested node group");
- }
-
- const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
- if (space_node_view_flag(*C, *snode, *region, NODE_SELECT, smooth_viewtx)) {
- return OPERATOR_FINISHED;
- }
- return OPERATOR_CANCELLED;
-}
-
-static int geometry_node_view_legacy_exec(bContext *C, wmOperator *op)
-{
- /* Allow running this operator directly in a specific node editor. */
- if (SpaceNode *snode = CTX_wm_space_node(C)) {
- return space_node_view_geometry_nodes_legacy(C, snode, op);
- }
-
- /* Since the operator is meant to be called from a button in the modifier panel, the node tree
- * must be found from the screen, using the largest node editor if there is more than one. */
- if (ScrArea *area = BKE_screen_find_big_area(CTX_wm_screen(C), SPACE_NODE, 0)) {
- if (SpaceNode *snode = static_cast<SpaceNode *>(area->spacedata.first)) {
- ScrArea *old_area = CTX_wm_area(C);
- ARegion *old_region = CTX_wm_region(C);
-
- /* Override the context since it is used by the View2D panning code. */
- CTX_wm_area_set(C, area);
- CTX_wm_region_set(C, static_cast<ARegion *>(area->regionbase.last));
- const int result = space_node_view_geometry_nodes_legacy(C, snode, op);
- CTX_wm_area_set(C, old_area);
- CTX_wm_region_set(C, old_region);
- return result;
- }
- }
-
- return OPERATOR_CANCELLED;
-}
-
-static bool geometry_node_view_legacy_poll(bContext *C)
-{
- /* Allow direct execution in a node editor, but also affecting any visible node editor. */
- return ED_operator_node_active(C) || BKE_screen_find_big_area(CTX_wm_screen(C), SPACE_NODE, 0);
-}
-
-void NODE_OT_geometry_node_view_legacy(wmOperatorType *ot)
-{
- ot->name = "View Deprecated Geometry Nodes";
- ot->idname = "NODE_OT_geometry_node_view_legacy";
- ot->description = "Select and view legacy geometry nodes in the node editor";
-
- ot->exec = geometry_node_view_legacy_exec;
- ot->poll = geometry_node_view_legacy_poll;
-
- ot->flag = OPTYPE_INTERNAL;
-}
-
-/** \} */
-
} // namespace blender::ed::space_node
diff --git a/source/blender/editors/space_node/space_node.cc b/source/blender/editors/space_node/space_node.cc
index a1fa0517c63..82b850653be 100644
--- a/source/blender/editors/space_node/space_node.cc
+++ b/source/blender/editors/space_node/space_node.cc
@@ -622,11 +622,6 @@ static bool node_collection_drop_poll(bContext *UNUSED(C),
return WM_drag_is_ID_type(drag, ID_GR);
}
-static bool node_texture_drop_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent *UNUSED(event))
-{
- return WM_drag_is_ID_type(drag, ID_TE);
-}
-
static bool node_ima_drop_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent *UNUSED(event))
{
if (drag->type == WM_DRAG_PATH) {
@@ -687,12 +682,6 @@ static void node_dropboxes()
WM_drag_free_imported_drag_ID,
nullptr);
WM_dropbox_add(lb,
- "NODE_OT_add_texture",
- node_texture_drop_poll,
- node_id_drop_copy,
- WM_drag_free_imported_drag_ID,
- nullptr);
- WM_dropbox_add(lb,
"NODE_OT_add_group",
node_group_drop_poll,
node_group_drop_copy,
diff --git a/source/blender/editors/space_outliner/outliner_collections.cc b/source/blender/editors/space_outliner/outliner_collections.cc
index 716d5b67fe3..a4ff44512ef 100644
--- a/source/blender/editors/space_outliner/outliner_collections.cc
+++ b/source/blender/editors/space_outliner/outliner_collections.cc
@@ -301,7 +301,7 @@ static TreeTraversalAction collection_find_data_to_edit(TreeElement *te, void *c
if (ID_IS_OVERRIDE_LIBRARY_REAL(collection)) {
if (ID_IS_OVERRIDE_LIBRARY_HIERARCHY_ROOT(collection)) {
- if (!data->is_liboverride_hierarchy_root_allowed) {
+ if (!(data->is_liboverride_hierarchy_root_allowed || data->is_liboverride_allowed)) {
return TRAVERSE_SKIP_CHILDS;
}
}
diff --git a/source/blender/editors/space_outliner/outliner_draw.cc b/source/blender/editors/space_outliner/outliner_draw.cc
index 4ef0bbbcde8..9857abb3da7 100644
--- a/source/blender/editors/space_outliner/outliner_draw.cc
+++ b/source/blender/editors/space_outliner/outliner_draw.cc
@@ -1082,7 +1082,7 @@ static void outliner_draw_restrictbuts(uiBlock *block,
}
BLI_assert((restrict_column_offset * UI_UNIT_X + V2D_SCROLL_WIDTH) ==
- outliner_restrict_columns_width(space_outliner));
+ outliner_right_columns_width(space_outliner));
/* Create buttons. */
uiBut *bt;
@@ -1779,11 +1779,67 @@ static void outliner_draw_userbuts(uiBlock *block,
}
}
-static bool outliner_draw_overrides_buts(uiBlock *block,
- ARegion *region,
- SpaceOutliner *space_outliner,
- ListBase *lb,
- const bool is_open)
+static void outliner_draw_overrides_rna_buts(uiBlock *block,
+ const ARegion *region,
+ const SpaceOutliner *space_outliner,
+ const ListBase *lb,
+ const int x)
+{
+ const float pad_x = 2.0f * UI_DPI_FAC;
+ const float pad_y = 0.5f * U.pixelsize;
+ const float item_max_width = round_fl_to_int(OL_RNA_COL_SIZEX - 2 * pad_x);
+ const float item_height = round_fl_to_int(UI_UNIT_Y - 2.0f * pad_y);
+
+ LISTBASE_FOREACH (const TreeElement *, te, lb) {
+ const TreeStoreElem *tselem = TREESTORE(te);
+ if (TSELEM_OPEN(tselem, space_outliner)) {
+ outliner_draw_overrides_rna_buts(block, region, space_outliner, &te->subtree, x);
+ }
+
+ if (!outliner_is_element_in_view(te, &region->v2d)) {
+ continue;
+ }
+ if (tselem->type != TSE_LIBRARY_OVERRIDE) {
+ continue;
+ }
+
+ TreeElementOverridesProperty &override_elem = *tree_element_cast<TreeElementOverridesProperty>(
+ te);
+
+ PointerRNA *ptr = &override_elem.override_rna_ptr;
+ PropertyRNA *prop = &override_elem.override_rna_prop;
+ const PropertyType prop_type = RNA_property_type(prop);
+
+ uiBut *auto_but = uiDefAutoButR(block,
+ ptr,
+ prop,
+ -1,
+ (prop_type == PROP_ENUM) ? nullptr : "",
+ ICON_NONE,
+ x + pad_x,
+ te->ys + pad_y,
+ item_max_width,
+ item_height);
+ /* Added the button successfully, nothing else to do. Otherwise, cases for multiple buttons
+ * need to be handled. */
+ if (auto_but) {
+ continue;
+ }
+
+ if (!auto_but) {
+ /* TODO what if the array is longer, and doesn't fit nicely? What about multi-dimension
+ * arrays? */
+ uiDefAutoButsArrayR(
+ block, ptr, prop, ICON_NONE, x + pad_x, te->ys + pad_y, item_max_width, item_height);
+ }
+ }
+}
+
+static bool outliner_draw_overrides_warning_buts(uiBlock *block,
+ ARegion *region,
+ SpaceOutliner *space_outliner,
+ ListBase *lb,
+ const bool is_open)
{
bool any_item_has_warnings = false;
@@ -1829,7 +1885,7 @@ static bool outliner_draw_overrides_buts(uiBlock *block,
break;
}
- const bool any_child_has_warnings = outliner_draw_overrides_buts(
+ const bool any_child_has_warnings = outliner_draw_overrides_warning_buts(
block,
region,
space_outliner,
@@ -1863,28 +1919,20 @@ static bool outliner_draw_overrides_buts(uiBlock *block,
return any_item_has_warnings;
}
-static void outliner_draw_rnacols(ARegion *region, int sizex)
+static void outliner_draw_separator(ARegion *region, const int x)
{
View2D *v2d = &region->v2d;
- float miny = v2d->cur.ymin;
- if (miny < v2d->tot.ymin) {
- miny = v2d->tot.ymin;
- }
-
GPU_line_width(1.0f);
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
immUniformThemeColorShadeAlpha(TH_BACK, -15, -200);
- immBegin(GPU_PRIM_LINES, 4);
-
- immVertex2f(pos, sizex, v2d->cur.ymax);
- immVertex2f(pos, sizex, miny);
+ immBegin(GPU_PRIM_LINES, 2);
- immVertex2f(pos, sizex + OL_RNA_COL_SIZEX, v2d->cur.ymax);
- immVertex2f(pos, sizex + OL_RNA_COL_SIZEX, miny);
+ immVertex2f(pos, x, v2d->cur.ymax);
+ immVertex2f(pos, x, v2d->cur.ymin);
immEnd();
@@ -3637,7 +3685,7 @@ static void outliner_draw_tree(bContext *C,
const TreeViewContext *tvc,
ARegion *region,
SpaceOutliner *space_outliner,
- const float restrict_column_width,
+ const float right_column_width,
const bool use_mode_column,
const bool use_warning_column,
TreeElement **te_edit)
@@ -3672,8 +3720,8 @@ static void outliner_draw_tree(bContext *C,
/* Set scissor so tree elements or lines can't overlap restriction icons. */
int scissor[4] = {0};
- if (restrict_column_width > 0.0f) {
- int mask_x = BLI_rcti_size_x(&region->v2d.mask) - (int)restrict_column_width + 1;
+ if (right_column_width > 0.0f) {
+ int mask_x = BLI_rcti_size_x(&region->v2d.mask) - (int)right_column_width + 1;
CLAMP_MIN(mask_x, 0);
GPU_scissor_get(scissor);
@@ -3699,11 +3747,11 @@ static void outliner_draw_tree(bContext *C,
(te->flag & TE_DRAGGING) != 0,
startx,
&starty,
- restrict_column_width,
+ right_column_width,
te_edit);
}
- if (restrict_column_width > 0.0f) {
+ if (right_column_width > 0.0f) {
/* Reset scissor. */
GPU_scissor(UNPACK4(scissor));
}
@@ -3754,21 +3802,21 @@ static int outliner_data_api_buttons_start_x(int max_tree_width)
static int outliner_width(SpaceOutliner *space_outliner,
int max_tree_width,
- float restrict_column_width)
+ float right_column_width)
{
if (space_outliner->outlinevis == SO_DATA_API) {
return outliner_data_api_buttons_start_x(max_tree_width) + OL_RNA_COL_SIZEX + 10 * UI_DPI_FAC;
}
- return max_tree_width + restrict_column_width;
+ return max_tree_width + right_column_width;
}
static void outliner_update_viewable_area(ARegion *region,
SpaceOutliner *space_outliner,
int tree_width,
int tree_height,
- float restrict_column_width)
+ float right_column_width)
{
- int sizex = outliner_width(space_outliner, tree_width, restrict_column_width);
+ int sizex = outliner_width(space_outliner, tree_width, right_column_width);
int sizey = tree_height;
/* Extend size to allow for horizontal scrollbar and extra offset. */
@@ -3829,7 +3877,7 @@ void draw_outliner(const bContext *C)
space_outliner->runtime->tree_display->hasWarnings();
/* Draw outliner stuff (background, hierarchy lines and names). */
- const float restrict_column_width = outliner_restrict_columns_width(space_outliner);
+ const float right_column_width = outliner_right_columns_width(space_outliner);
outliner_back(region);
block = UI_block_begin(C, region, __func__, UI_EMBOSS);
outliner_draw_tree((bContext *)C,
@@ -3837,7 +3885,7 @@ void draw_outliner(const bContext *C)
&tvc,
region,
space_outliner,
- restrict_column_width,
+ right_column_width,
use_mode_column,
use_warning_column,
&te_edit);
@@ -3852,7 +3900,8 @@ void draw_outliner(const bContext *C)
if (space_outliner->outlinevis == SO_DATA_API) {
int buttons_start_x = outliner_data_api_buttons_start_x(tree_width);
/* draw rna buttons */
- outliner_draw_rnacols(region, buttons_start_x);
+ outliner_draw_separator(region, buttons_start_x);
+ outliner_draw_separator(region, buttons_start_x + OL_RNA_COL_SIZEX);
UI_block_emboss_set(block, UI_EMBOSS);
outliner_draw_rnabuts(block, region, space_outliner, buttons_start_x, &space_outliner->tree);
@@ -3864,9 +3913,17 @@ void draw_outliner(const bContext *C)
}
else if (space_outliner->outlinevis == SO_OVERRIDES_LIBRARY) {
/* Draw overrides status columns. */
- outliner_draw_overrides_buts(block, region, space_outliner, &space_outliner->tree, true);
+ outliner_draw_overrides_warning_buts(
+ block, region, space_outliner, &space_outliner->tree, true);
+
+ UI_block_emboss_set(block, UI_EMBOSS);
+ UI_block_flag_enable(block, UI_BLOCK_NO_DRAW_OVERRIDDEN_STATE);
+ const int x = region->v2d.cur.xmax - right_column_width;
+ outliner_draw_separator(region, x);
+ outliner_draw_overrides_rna_buts(block, region, space_outliner, &space_outliner->tree, x);
+ UI_block_emboss_set(block, UI_EMBOSS_NONE_OR_STATUS);
}
- else if (restrict_column_width > 0.0f) {
+ else if (right_column_width > 0.0f) {
/* draw restriction columns */
RestrictPropertiesActive props_active;
memset(&props_active, 1, sizeof(RestrictPropertiesActive));
@@ -3893,7 +3950,7 @@ void draw_outliner(const bContext *C)
/* Draw edit buttons if necessary. */
if (te_edit) {
- outliner_buttons(C, block, region, restrict_column_width, te_edit);
+ outliner_buttons(C, block, region, right_column_width, te_edit);
}
UI_block_end(C, block);
@@ -3901,7 +3958,7 @@ void draw_outliner(const bContext *C)
/* Update total viewable region. */
outliner_update_viewable_area(
- region, space_outliner, tree_width, tree_height, restrict_column_width);
+ region, space_outliner, tree_width, tree_height, right_column_width);
}
/** \} */
diff --git a/source/blender/editors/space_outliner/outliner_edit.cc b/source/blender/editors/space_outliner/outliner_edit.cc
index a60e082f6a5..ae67e7108bf 100644
--- a/source/blender/editors/space_outliner/outliner_edit.cc
+++ b/source/blender/editors/space_outliner/outliner_edit.cc
@@ -447,6 +447,17 @@ static void id_delete(bContext *C, ReportList *reports, TreeElement *te, TreeSto
(tselem->type == TSE_LAYER_COLLECTION));
UNUSED_VARS_NDEBUG(te);
+ if (ID_IS_OVERRIDE_LIBRARY(id)) {
+ if (!ID_IS_OVERRIDE_LIBRARY_REAL(id) ||
+ (id->override_library->flag & IDOVERRIDE_LIBRARY_FLAG_NO_HIERARCHY) == 0) {
+ BKE_reportf(reports,
+ RPT_WARNING,
+ "Cannot delete library override id '%s', it is part of an override hierarchy",
+ id->name);
+ return;
+ }
+ }
+
if (te->idcode == ID_LI && ((Library *)id)->parent != nullptr) {
BKE_reportf(reports, RPT_WARNING, "Cannot delete indirectly linked library '%s'", id->name);
return;
diff --git a/source/blender/editors/space_outliner/outliner_intern.hh b/source/blender/editors/space_outliner/outliner_intern.hh
index a9bdcc56787..7970841b4fd 100644
--- a/source/blender/editors/space_outliner/outliner_intern.hh
+++ b/source/blender/editors/space_outliner/outliner_intern.hh
@@ -638,7 +638,7 @@ bool outliner_tree_traverse(const SpaceOutliner *space_outliner,
int filter_tselem_flag,
TreeTraversalFunc func,
void *customdata);
-float outliner_restrict_columns_width(const struct SpaceOutliner *space_outliner);
+float outliner_right_columns_width(const struct SpaceOutliner *space_outliner);
/**
* Find first tree element in tree with matching tree-store flag.
*/
diff --git a/source/blender/editors/space_outliner/outliner_select.cc b/source/blender/editors/space_outliner/outliner_select.cc
index a583eb0364f..a202ded6deb 100644
--- a/source/blender/editors/space_outliner/outliner_select.cc
+++ b/source/blender/editors/space_outliner/outliner_select.cc
@@ -1551,7 +1551,7 @@ static bool outliner_is_co_within_restrict_columns(const SpaceOutliner *space_ou
const ARegion *region,
float view_co_x)
{
- return (view_co_x > region->v2d.cur.xmax - outliner_restrict_columns_width(space_outliner));
+ return (view_co_x > region->v2d.cur.xmax - outliner_right_columns_width(space_outliner));
}
bool outliner_is_co_within_mode_column(SpaceOutliner *space_outliner, const float view_mval[2])
diff --git a/source/blender/editors/space_outliner/outliner_tools.cc b/source/blender/editors/space_outliner/outliner_tools.cc
index 612baaa0752..0aea4521204 100644
--- a/source/blender/editors/space_outliner/outliner_tools.cc
+++ b/source/blender/editors/space_outliner/outliner_tools.cc
@@ -1677,6 +1677,12 @@ void OUTLINER_OT_object_operation(wmOperatorType *ot)
using OutlinerDeleteFn = void (*)(bContext *C, ReportList *reports, Scene *scene, Object *ob);
+typedef struct ObjectEditData {
+ GSet *objects_set;
+ bool is_liboverride_allowed;
+ bool is_liboverride_hierarchy_root_allowed;
+} ObjectEditData;
+
static void outliner_do_object_delete(bContext *C,
ReportList *reports,
Scene *scene,
@@ -1693,7 +1699,8 @@ static void outliner_do_object_delete(bContext *C,
static TreeTraversalAction outliner_find_objects_to_delete(TreeElement *te, void *customdata)
{
- GSet *objects_to_delete = (GSet *)customdata;
+ ObjectEditData *data = reinterpret_cast<ObjectEditData *>(customdata);
+ GSet *objects_to_delete = data->objects_set;
TreeStoreElem *tselem = TREESTORE(te);
if (outliner_is_collection_tree_element(te)) {
@@ -1708,9 +1715,15 @@ static TreeTraversalAction outliner_find_objects_to_delete(TreeElement *te, void
ID *id = tselem->id;
if (ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
- if (!ID_IS_OVERRIDE_LIBRARY_HIERARCHY_ROOT(id)) {
- /* Only allow deletion of liboverride objects if they are root overrides. */
- return TRAVERSE_SKIP_CHILDS;
+ if (ID_IS_OVERRIDE_LIBRARY_HIERARCHY_ROOT(id)) {
+ if (!(data->is_liboverride_hierarchy_root_allowed || data->is_liboverride_allowed)) {
+ return TRAVERSE_SKIP_CHILDS;
+ }
+ }
+ else {
+ if (!data->is_liboverride_allowed) {
+ return TRAVERSE_SKIP_CHILDS;
+ }
}
}
@@ -1732,27 +1745,31 @@ static int outliner_delete_exec(bContext *C, wmOperator *op)
/* Get selected objects skipping duplicates to prevent deleting objects linked to multiple
* collections twice */
- GSet *objects_to_delete = BLI_gset_ptr_new(__func__);
+ ObjectEditData object_delete_data = {};
+ object_delete_data.objects_set = BLI_gset_ptr_new(__func__);
+ object_delete_data.is_liboverride_allowed = false;
+ object_delete_data.is_liboverride_hierarchy_root_allowed = delete_hierarchy;
outliner_tree_traverse(space_outliner,
&space_outliner->tree,
0,
TSE_SELECTED,
outliner_find_objects_to_delete,
- objects_to_delete);
+ &object_delete_data);
if (delete_hierarchy) {
BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
outliner_do_object_delete(
- C, op->reports, scene, objects_to_delete, object_batch_delete_hierarchy_fn);
+ C, op->reports, scene, object_delete_data.objects_set, object_batch_delete_hierarchy_fn);
BKE_id_multi_tagged_delete(bmain);
}
else {
- outliner_do_object_delete(C, op->reports, scene, objects_to_delete, outliner_object_delete_fn);
+ outliner_do_object_delete(
+ C, op->reports, scene, object_delete_data.objects_set, outliner_object_delete_fn);
}
- BLI_gset_free(objects_to_delete, nullptr);
+ BLI_gset_free(object_delete_data.objects_set, nullptr);
outliner_collection_delete(C, bmain, scene, op->reports, delete_hierarchy);
diff --git a/source/blender/editors/space_outliner/outliner_tree.cc b/source/blender/editors/space_outliner/outliner_tree.cc
index 1a772287dfa..19fe40b612e 100644
--- a/source/blender/editors/space_outliner/outliner_tree.cc
+++ b/source/blender/editors/space_outliner/outliner_tree.cc
@@ -900,7 +900,6 @@ TreeElement *outliner_add_element(SpaceOutliner *space_outliner,
/* ID types not (fully) ported to new design yet. */
if (te->abstract_element->expandPoll(*space_outliner)) {
outliner_add_id_contents(space_outliner, te, tselem, id);
- te->abstract_element->postExpand(*space_outliner);
}
}
else if (ELEM(type,
diff --git a/source/blender/editors/space_outliner/outliner_utils.cc b/source/blender/editors/space_outliner/outliner_utils.cc
index 556f87617f6..ed5a2108d3c 100644
--- a/source/blender/editors/space_outliner/outliner_utils.cc
+++ b/source/blender/editors/space_outliner/outliner_utils.cc
@@ -314,7 +314,7 @@ bool outliner_tree_traverse(const SpaceOutliner *space_outliner,
return true;
}
-float outliner_restrict_columns_width(const SpaceOutliner *space_outliner)
+float outliner_right_columns_width(const SpaceOutliner *space_outliner)
{
int num_columns = 0;
@@ -322,8 +322,10 @@ float outliner_restrict_columns_width(const SpaceOutliner *space_outliner)
case SO_DATA_API:
case SO_SEQUENCE:
case SO_LIBRARIES:
- case SO_OVERRIDES_LIBRARY:
return 0.0f;
+ case SO_OVERRIDES_LIBRARY:
+ num_columns = OL_RNA_COL_SIZEX / UI_UNIT_X;
+ break;
case SO_ID_ORPHANS:
num_columns = 3;
break;
diff --git a/source/blender/editors/space_outliner/space_outliner.cc b/source/blender/editors/space_outliner/space_outliner.cc
index f75182d25a0..97dc659155f 100644
--- a/source/blender/editors/space_outliner/space_outliner.cc
+++ b/source/blender/editors/space_outliner/space_outliner.cc
@@ -371,7 +371,7 @@ static void outliner_init(wmWindowManager *UNUSED(wm), ScrArea *area)
static SpaceLink *outliner_duplicate(SpaceLink *sl)
{
SpaceOutliner *space_outliner = (SpaceOutliner *)sl;
- SpaceOutliner *space_outliner_new = MEM_new<SpaceOutliner>(__func__, *space_outliner);
+ SpaceOutliner *space_outliner_new = MEM_cnew<SpaceOutliner>(__func__, *space_outliner);
BLI_listbase_clear(&space_outliner_new->tree);
space_outliner_new->treestore = nullptr;
diff --git a/source/blender/editors/space_outliner/tree/tree_display.hh b/source/blender/editors/space_outliner/tree/tree_display.hh
index bdca1954a9c..a60d3339042 100644
--- a/source/blender/editors/space_outliner/tree/tree_display.hh
+++ b/source/blender/editors/space_outliner/tree/tree_display.hh
@@ -136,8 +136,7 @@ class TreeDisplayOverrideLibrary final : public AbstractTreeDisplay {
ListBase buildTree(const TreeSourceData &source_data) override;
private:
- TreeElement *add_library_contents(Main &, ListBase &, Library *);
- bool override_library_id_filter_poll(const Library *lib, ID *id) const;
+ ListBase add_library_contents(Main &);
short id_filter_get() const;
};
diff --git a/source/blender/editors/space_outliner/tree/tree_display_override_library.cc b/source/blender/editors/space_outliner/tree/tree_display_override_library.cc
index f94727ba356..b5c0a10c834 100644
--- a/source/blender/editors/space_outliner/tree/tree_display_override_library.cc
+++ b/source/blender/editors/space_outliner/tree/tree_display_override_library.cc
@@ -32,72 +32,22 @@ TreeDisplayOverrideLibrary::TreeDisplayOverrideLibrary(SpaceOutliner &space_outl
ListBase TreeDisplayOverrideLibrary::buildTree(const TreeSourceData &source_data)
{
- ListBase tree = {nullptr};
-
- {
- /* current file first - mainvar provides tselem with unique pointer - not used */
- TreeElement *ten = add_library_contents(*source_data.bmain, tree, nullptr);
- TreeStoreElem *tselem;
-
- if (ten) {
- tselem = TREESTORE(ten);
- if (!tselem->used) {
- tselem->flag &= ~TSE_CLOSED;
- }
- }
- }
+ ListBase tree = add_library_contents(*source_data.bmain);
- for (ID *id : List<ID>(source_data.bmain->libraries)) {
- Library *lib = reinterpret_cast<Library *>(id);
- TreeElement *ten = add_library_contents(*source_data.bmain, tree, lib);
- /* NULL-check matters, due to filtering there may not be a new element. */
- if (ten) {
- lib->id.newid = (ID *)ten;
+ for (TreeElement *top_level_te : List<TreeElement>(tree)) {
+ TreeStoreElem *tselem = TREESTORE(top_level_te);
+ if (!tselem->used) {
+ tselem->flag &= ~TSE_CLOSED;
}
}
- /* make hierarchy */
- for (TreeElement *ten : List<TreeElement>(tree)) {
- if (ten == tree.first) {
- /* First item is main, skip. */
- continue;
- }
-
- TreeStoreElem *tselem = TREESTORE(ten);
- Library *lib = (Library *)tselem->id;
- BLI_assert(!lib || (GS(lib->id.name) == ID_LI));
- if (!lib || !lib->parent) {
- continue;
- }
-
- TreeElement *parent = (TreeElement *)lib->parent->id.newid;
-
- if (tselem->id->tag & LIB_TAG_INDIRECT) {
- /* Only remove from 'first level' if lib is not also directly used. */
- BLI_remlink(&tree, ten);
- BLI_addtail(&parent->subtree, ten);
- ten->parent = parent;
- }
- else {
- /* Else, make a new copy of the libtree for our parent. */
- TreeElement *dupten = add_library_contents(*source_data.bmain, parent->subtree, lib);
- if (dupten) {
- dupten->parent = parent;
- }
- }
- }
- /* restore newid pointers */
- for (ID *library_id : List<ID>(source_data.bmain->libraries)) {
- library_id->newid = nullptr;
- }
-
return tree;
}
-TreeElement *TreeDisplayOverrideLibrary::add_library_contents(Main &mainvar,
- ListBase &lb,
- Library *lib)
+ListBase TreeDisplayOverrideLibrary::add_library_contents(Main &mainvar)
{
+ ListBase tree = {nullptr};
+
const short filter_id_type = id_filter_get();
ListBase *lbarray[INDEX_ID_MAX];
@@ -110,7 +60,6 @@ TreeElement *TreeDisplayOverrideLibrary::add_library_contents(Main &mainvar,
tot = set_listbasepointers(&mainvar, lbarray);
}
- TreeElement *tenlib = nullptr;
for (int a = 0; a < tot; a++) {
if (!lbarray[a] || !lbarray[a]->first) {
continue;
@@ -118,56 +67,51 @@ TreeElement *TreeDisplayOverrideLibrary::add_library_contents(Main &mainvar,
ID *id = nullptr;
- /* check if there's data in current lib */
+ /* check if there's data in current id list */
for (ID *id_iter : List<ID>(lbarray[a])) {
- if (id_iter->lib == lib && ID_IS_OVERRIDE_LIBRARY_REAL(id_iter)) {
+ if (ID_IS_OVERRIDE_LIBRARY_REAL(id_iter)) {
id = id_iter;
break;
}
}
- if (id != nullptr) {
- if (!tenlib) {
- /* Create library tree element on demand, depending if there are any data-blocks. */
- if (lib) {
- tenlib = outliner_add_element(&space_outliner_, &lb, lib, nullptr, TSE_SOME_ID, 0);
- }
- else {
- tenlib = outliner_add_element(&space_outliner_, &lb, &mainvar, nullptr, TSE_ID_BASE, 0);
- tenlib->name = IFACE_("Current File");
- }
- if (tenlib->flag & TE_HAS_WARNING) {
- has_warnings = true;
- }
- }
+ if (id == nullptr) {
+ continue;
+ }
- /* Create data-block list parent element on demand. */
- TreeElement *ten;
+ /* Create data-block list parent element on demand. */
+ TreeElement *id_base_te = nullptr;
+ ListBase *lb_to_expand = &tree;
- if (filter_id_type) {
- ten = tenlib;
- }
- else {
- ten = outliner_add_element(
- &space_outliner_, &tenlib->subtree, lbarray[a], nullptr, TSE_ID_BASE, 0);
- ten->directdata = lbarray[a];
- ten->name = outliner_idcode_to_plural(GS(id->name));
- }
+ if (!filter_id_type) {
+ id_base_te = outliner_add_element(
+ &space_outliner_, &tree, lbarray[a], nullptr, TSE_ID_BASE, 0);
+ id_base_te->directdata = lbarray[a];
+ id_base_te->name = outliner_idcode_to_plural(GS(id->name));
- for (ID *id : List<ID>(lbarray[a])) {
- if (override_library_id_filter_poll(lib, id)) {
- TreeElement *override_tree_element = outliner_add_element(
- &space_outliner_, &ten->subtree, id, ten, TSE_LIBRARY_OVERRIDE_BASE, 0);
+ lb_to_expand = &id_base_te->subtree;
+ }
- if (BLI_listbase_is_empty(&override_tree_element->subtree)) {
- outliner_free_tree_element(override_tree_element, &ten->subtree);
- }
+ for (ID *id : List<ID>(lbarray[a])) {
+ if (ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
+ TreeElement *override_tree_element = outliner_add_element(
+ &space_outliner_, lb_to_expand, id, id_base_te, TSE_LIBRARY_OVERRIDE_BASE, 0);
+
+ if (BLI_listbase_is_empty(&override_tree_element->subtree)) {
+ outliner_free_tree_element(override_tree_element, lb_to_expand);
}
}
}
}
- return tenlib;
+ /* Remove ID base elements that turn out to be empty. */
+ LISTBASE_FOREACH_MUTABLE (TreeElement *, te, &tree) {
+ if (BLI_listbase_is_empty(&te->subtree)) {
+ outliner_free_tree_element(te, &tree);
+ }
+ }
+
+ return tree;
}
short TreeDisplayOverrideLibrary::id_filter_get() const
@@ -178,17 +122,4 @@ short TreeDisplayOverrideLibrary::id_filter_get() const
return 0;
}
-bool TreeDisplayOverrideLibrary::override_library_id_filter_poll(const Library *lib, ID *id) const
-{
- if (id->lib != lib) {
- return false;
- }
-
- if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
- return false;
- }
-
- return true;
-}
-
} // namespace blender::ed::outliner
diff --git a/source/blender/editors/space_outliner/tree/tree_display_view_layer.cc b/source/blender/editors/space_outliner/tree/tree_display_view_layer.cc
index 0ee5059a54d..19811e45b90 100644
--- a/source/blender/editors/space_outliner/tree/tree_display_view_layer.cc
+++ b/source/blender/editors/space_outliner/tree/tree_display_view_layer.cc
@@ -154,15 +154,6 @@ void TreeDisplayViewLayer::add_layer_collections_recursive(ListBase &tree,
if (!exclude && show_objects_) {
add_layer_collection_objects(ten->subtree, *lc, *ten);
}
-
- const bool lib_overrides_visible = !exclude && (!SUPPORT_FILTER_OUTLINER(&space_outliner_) ||
- ((space_outliner_.filter &
- SO_FILTER_NO_LIB_OVERRIDE) == 0));
-
- if (lib_overrides_visible && ID_IS_OVERRIDE_LIBRARY_REAL(&lc->collection->id)) {
- outliner_add_element(
- &space_outliner_, &ten->subtree, &lc->collection->id, ten, TSE_LIBRARY_OVERRIDE_BASE, 0);
- }
}
}
diff --git a/source/blender/editors/space_outliner/tree/tree_element.cc b/source/blender/editors/space_outliner/tree/tree_element.cc
index 3c2023d7905..ca67aad00db 100644
--- a/source/blender/editors/space_outliner/tree/tree_element.cc
+++ b/source/blender/editors/space_outliner/tree/tree_element.cc
@@ -107,7 +107,6 @@ void tree_element_expand(const AbstractTreeElement &tree_element, SpaceOutliner
return;
}
tree_element.expand(space_outliner);
- tree_element.postExpand(space_outliner);
}
bool tree_element_warnings_get(TreeElement *te, int *r_icon, const char **r_message)
diff --git a/source/blender/editors/space_outliner/tree/tree_element.hh b/source/blender/editors/space_outliner/tree/tree_element.hh
index 2fbc86705b9..6f2d803ae96 100644
--- a/source/blender/editors/space_outliner/tree/tree_element.hh
+++ b/source/blender/editors/space_outliner/tree/tree_element.hh
@@ -40,9 +40,6 @@ class AbstractTreeElement {
{
return true;
}
- virtual void postExpand(SpaceOutliner &) const
- {
- }
/**
* Just while transitioning to the new tree-element design: Some types are only partially ported,
diff --git a/source/blender/editors/space_outliner/tree/tree_element_id.cc b/source/blender/editors/space_outliner/tree/tree_element_id.cc
index 64c73f57107..ef5e056f229 100644
--- a/source/blender/editors/space_outliner/tree/tree_element_id.cc
+++ b/source/blender/editors/space_outliner/tree/tree_element_id.cc
@@ -93,17 +93,6 @@ TreeElementID::TreeElementID(TreeElement &legacy_te, ID &id)
legacy_te_.idcode = GS(id.name);
}
-void TreeElementID::postExpand(SpaceOutliner &space_outliner) const
-{
- const bool lib_overrides_visible = !SUPPORT_FILTER_OUTLINER(&space_outliner) ||
- ((space_outliner.filter & SO_FILTER_NO_LIB_OVERRIDE) == 0);
-
- if (lib_overrides_visible && ID_IS_OVERRIDE_LIBRARY_REAL(&id_)) {
- outliner_add_element(
- &space_outliner, &legacy_te_.subtree, &id_, &legacy_te_, TSE_LIBRARY_OVERRIDE_BASE, 0);
- }
-}
-
bool TreeElementID::expandPoll(const SpaceOutliner &space_outliner) const
{
const TreeStoreElem *tsepar = legacy_te_.parent ? TREESTORE(legacy_te_.parent) : nullptr;
diff --git a/source/blender/editors/space_outliner/tree/tree_element_id.hh b/source/blender/editors/space_outliner/tree/tree_element_id.hh
index 75dc7e737e2..b7519fe06f9 100644
--- a/source/blender/editors/space_outliner/tree/tree_element_id.hh
+++ b/source/blender/editors/space_outliner/tree/tree_element_id.hh
@@ -24,7 +24,6 @@ class TreeElementID : public AbstractTreeElement {
static std::unique_ptr<TreeElementID> createFromID(TreeElement &legacy_te, ID &id);
- void postExpand(SpaceOutliner &) const override;
bool expandPoll(const SpaceOutliner &) const override;
/**
diff --git a/source/blender/editors/space_outliner/tree/tree_element_overrides.cc b/source/blender/editors/space_outliner/tree/tree_element_overrides.cc
index 64c390d29b3..857f5577e59 100644
--- a/source/blender/editors/space_outliner/tree/tree_element_overrides.cc
+++ b/source/blender/editors/space_outliner/tree/tree_element_overrides.cc
@@ -73,7 +73,8 @@ void TreeElementOverridesBase::expand(SpaceOutliner &space_outliner) const
}
}
- TreeElementOverridesData data = {id, *override_prop, is_rna_path_valid};
+ TreeElementOverridesData data = {
+ id, *override_prop, override_rna_ptr, *override_rna_prop, is_rna_path_valid};
outliner_add_element(
&space_outliner, &legacy_te_.subtree, &data, &legacy_te_, TSE_LIBRARY_OVERRIDE, index++);
}
@@ -81,11 +82,13 @@ void TreeElementOverridesBase::expand(SpaceOutliner &space_outliner) const
TreeElementOverridesProperty::TreeElementOverridesProperty(TreeElement &legacy_te,
TreeElementOverridesData &override_data)
- : AbstractTreeElement(legacy_te), override_prop_(override_data.override_property)
+ : AbstractTreeElement(legacy_te),
+ override_rna_ptr(override_data.override_rna_ptr),
+ override_rna_prop(override_data.override_rna_prop)
{
BLI_assert(legacy_te.store_elem->type == TSE_LIBRARY_OVERRIDE);
- legacy_te.name = override_prop_.rna_path;
+ legacy_te.name = override_data.override_property.rna_path;
/* Abusing this for now, better way to do it is also pending current refactor of the whole tree
* code to use C++. */
legacy_te.directdata = POINTER_FROM_UINT(override_data.is_rna_path_valid);
diff --git a/source/blender/editors/space_outliner/tree/tree_element_overrides.hh b/source/blender/editors/space_outliner/tree/tree_element_overrides.hh
index 1987efcf6f6..a2d1409f193 100644
--- a/source/blender/editors/space_outliner/tree/tree_element_overrides.hh
+++ b/source/blender/editors/space_outliner/tree/tree_element_overrides.hh
@@ -6,13 +6,21 @@
#pragma once
+#include "RNA_types.h"
+
#include "tree_element.hh"
+struct ID;
+struct IDOverrideLibraryProperty;
+
namespace blender::ed::outliner {
struct TreeElementOverridesData {
ID &id;
IDOverrideLibraryProperty &override_property;
+ PointerRNA &override_rna_ptr;
+ PropertyRNA &override_rna_prop;
+
bool is_rna_path_valid;
};
@@ -27,7 +35,9 @@ class TreeElementOverridesBase final : public AbstractTreeElement {
};
class TreeElementOverridesProperty final : public AbstractTreeElement {
- IDOverrideLibraryProperty &override_prop_;
+ public:
+ PointerRNA override_rna_ptr;
+ PropertyRNA &override_rna_prop;
public:
TreeElementOverridesProperty(TreeElement &legacy_te, TreeElementOverridesData &override_data);
diff --git a/source/blender/editors/space_outliner/tree/tree_element_rna.cc b/source/blender/editors/space_outliner/tree/tree_element_rna.cc
index abc7cd8f8ce..914104f1f06 100644
--- a/source/blender/editors/space_outliner/tree/tree_element_rna.cc
+++ b/source/blender/editors/space_outliner/tree/tree_element_rna.cc
@@ -52,7 +52,7 @@ bool TreeElementRNACommon::isRNAValid() const
return rna_ptr_.data != nullptr;
}
-bool TreeElementRNACommon::expandPoll(const SpaceOutliner &) const
+bool TreeElementRNACommon::expandPoll(const SpaceOutliner &UNUSED(space_outliner)) const
{
return isRNAValid();
}
diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c
index 9370b349cb4..0ed366209f6 100644
--- a/source/blender/editors/space_sequencer/sequencer_draw.c
+++ b/source/blender/editors/space_sequencer/sequencer_draw.c
@@ -2681,7 +2681,6 @@ void draw_timeline_seq(const bContext *C, ARegion *region)
Editing *ed = SEQ_editing_get(scene);
SpaceSeq *sseq = CTX_wm_space_seq(C);
View2D *v2d = &region->v2d;
- short cfra_flag = 0;
float col[3];
seq_prefetch_wm_notify(C, scene);
@@ -2728,9 +2727,6 @@ void draw_timeline_seq(const bContext *C, ARegion *region)
}
UI_view2d_view_ortho(v2d);
- if ((sseq->flag & SEQ_DRAWFRAMES) == 0) {
- cfra_flag |= DRAWCFRA_UNIT_SECONDS;
- }
UI_view2d_view_orthoSpecial(region, v2d, 1);
int marker_draw_flag = DRAW_MARKERS_MARGIN;
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_column.cc b/source/blender/editors/space_spreadsheet/spreadsheet_column.cc
index 964738ff2c1..19fe61f0ed3 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_column.cc
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_column.cc
@@ -5,6 +5,7 @@
#include "MEM_guardedalloc.h"
#include "BLI_color.hh"
+#include "BLI_cpp_type.hh"
#include "BLI_hash.hh"
#include "BLI_math_vec_types.hh"
#include "BLI_string.h"
@@ -12,14 +13,12 @@
#include "BKE_geometry_set.hh"
-#include "FN_cpp_type.hh"
-
#include "spreadsheet_column.hh"
#include "spreadsheet_column_values.hh"
namespace blender::ed::spreadsheet {
-eSpreadsheetColumnValueType cpp_type_to_column_type(const fn::CPPType &type)
+eSpreadsheetColumnValueType cpp_type_to_column_type(const CPPType &type)
{
if (type.is<bool>()) {
return SPREADSHEET_VALUE_TYPE_BOOL;
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_column_values.hh b/source/blender/editors/space_spreadsheet/spreadsheet_column_values.hh
index 454518016bc..7cf9238d34e 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_column_values.hh
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_column_values.hh
@@ -4,15 +4,14 @@
#include "DNA_space_types.h"
+#include "BLI_generic_virtual_array.hh"
#include "BLI_string_ref.hh"
-#include "FN_generic_virtual_array.hh"
-
namespace blender::ed::spreadsheet {
struct CellDrawParams;
-eSpreadsheetColumnValueType cpp_type_to_column_type(const fn::CPPType &type);
+eSpreadsheetColumnValueType cpp_type_to_column_type(const CPPType &type);
/**
* This represents a column in a spreadsheet. It has a name and provides a value for all the cells
@@ -22,10 +21,10 @@ class ColumnValues final {
protected:
std::string name_;
- fn::GVArray data_;
+ GVArray data_;
public:
- ColumnValues(std::string name, fn::GVArray data) : name_(std::move(name)), data_(std::move(data))
+ ColumnValues(std::string name, GVArray data) : name_(std::move(name)), data_(std::move(data))
{
/* The array should not be empty. */
BLI_assert(data_);
@@ -48,7 +47,7 @@ class ColumnValues final {
return data_.size();
}
- const fn::GVArray &data() const
+ const GVArray &data() const
{
return data_;
}
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc
index 3c94c466da1..0ad64db1b6d 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc
@@ -53,11 +53,11 @@ void ExtraColumns::foreach_default_column_ids(
std::unique_ptr<ColumnValues> ExtraColumns::get_column_values(
const SpreadsheetColumnID &column_id) const
{
- const fn::GSpan *values = columns_.lookup_ptr(column_id.name);
+ const GSpan *values = columns_.lookup_ptr(column_id.name);
if (values == nullptr) {
return {};
}
- return std::make_unique<ColumnValues>(column_id.name, fn::GVArray::ForSpan(*values));
+ return std::make_unique<ColumnValues>(column_id.name, GVArray::ForSpan(*values));
}
void GeometryDataSource::foreach_default_column_ids(
@@ -199,7 +199,7 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values(
if (!attribute) {
return {};
}
- fn::GVArray varray = std::move(attribute.varray);
+ GVArray varray = std::move(attribute.varray);
if (attribute.domain != domain_) {
return {};
}
@@ -462,7 +462,7 @@ static void find_fields_to_evaluate(const SpaceSpreadsheet *sspreadsheet,
}
if (const geo_log::GenericValueLog *generic_value_log =
dynamic_cast<const geo_log::GenericValueLog *>(value_log)) {
- fn::GPointer value = generic_value_log->value();
+ GPointer value = generic_value_log->value();
r_fields.add("Viewer", fn::make_constant_field(*value.type(), value.get()));
}
}
@@ -508,7 +508,7 @@ class GeometryComponentCacheValue : public SpreadsheetCache::Value {
public:
/* Stores the result of fields evaluated on a geometry component. Without this, fields would have
* to be reevaluated on every redraw. */
- Map<std::pair<AttributeDomain, GField>, fn::GArray<>> arrays;
+ Map<std::pair<AttributeDomain, GField>, GArray<>> arrays;
};
static void add_fields_as_extra_columns(SpaceSpreadsheet *sspreadsheet,
@@ -529,8 +529,8 @@ static void add_fields_as_extra_columns(SpaceSpreadsheet *sspreadsheet,
const GField &field = item.value;
/* Use the cached evaluated array if it exists, otherwise evaluate the field now. */
- fn::GArray<> &evaluated_array = cache.arrays.lookup_or_add_cb({domain, field}, [&]() {
- fn::GArray<> evaluated_array(field.cpp_type(), domain_size);
+ GArray<> &evaluated_array = cache.arrays.lookup_or_add_cb({domain, field}, [&]() {
+ GArray<> evaluated_array(field.cpp_type(), domain_size);
bke::GeometryComponentFieldContext field_context{component, domain};
fn::FieldEvaluator field_evaluator{field_context, domain_size};
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh
index 303f495e3df..8b281e5a558 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh
@@ -21,10 +21,10 @@ namespace blender::ed::spreadsheet {
class ExtraColumns {
private:
/** Maps column names to their data. The data is actually stored in the spreadsheet cache. */
- Map<std::string, fn::GSpan> columns_;
+ Map<std::string, GSpan> columns_;
public:
- void add(std::string name, fn::GSpan data)
+ void add(std::string name, GSpan data)
{
columns_.add(std::move(name), data);
}
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc b/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc
index 33fd7329e6d..db466f8ccf3 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc
@@ -89,7 +89,7 @@ class SpreadsheetLayoutDrawer : public SpreadsheetDrawer {
return;
}
- const fn::GVArray &data = column.data();
+ const GVArray &data = column.data();
if (data.type().is<int>()) {
const int value = data.get<int>(real_index);
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_row_filter.cc b/source/blender/editors/space_spreadsheet/spreadsheet_row_filter.cc
index 1fddd751d78..e45317c2a5c 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_row_filter.cc
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_row_filter.cc
@@ -42,7 +42,7 @@ static void apply_row_filter(const SpreadsheetRowFilter &row_filter,
Vector<int64_t> &new_indices)
{
const ColumnValues &column = *columns.lookup(row_filter.column_name);
- const fn::GVArray &column_data = column.data();
+ const GVArray &column_data = column.data();
if (column_data.type().is<float>()) {
const float value = row_filter.value_float;
switch (row_filter.operation) {
diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c
index 52b4fc1d8aa..d9388bc82ef 100644
--- a/source/blender/editors/space_view3d/view3d_select.c
+++ b/source/blender/editors/space_view3d/view3d_select.c
@@ -43,6 +43,7 @@
#include "IMB_imbuf.h"
#include "IMB_imbuf_types.h"
+#include "BKE_action.h"
#include "BKE_armature.h"
#include "BKE_context.h"
#include "BKE_curve.h"
@@ -1534,7 +1535,7 @@ void VIEW3D_OT_select_menu(wmOperatorType *ot)
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
- /* keyingset to use (dynamic enum) */
+ /* #Object.id.name to select (dynamic enum). */
prop = RNA_def_enum(ot->srna, "name", DummyRNA_NULL_items, 0, "Object Name", "");
RNA_def_enum_funcs(prop, object_select_menu_enum_itemf);
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_ENUM_NO_TRANSLATE);
@@ -1548,16 +1549,18 @@ void VIEW3D_OT_select_menu(wmOperatorType *ot)
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
}
-static Base *object_mouse_select_menu(bContext *C,
- ViewContext *vc,
- const GPUSelectResult *buffer,
- const int hits,
- const int mval[2],
- bool extend,
- bool deselect,
- bool toggle)
+/**
+ * \return True when a menu was activated.
+ */
+static bool object_mouse_select_menu(bContext *C,
+ ViewContext *vc,
+ const GPUSelectResult *buffer,
+ const int hits,
+ const int mval[2],
+ const struct SelectPick_Params *params,
+ Base **r_basact)
{
- short baseCount = 0;
+ int base_count = 0;
bool ok;
LinkNodePair linklist = {NULL, NULL};
@@ -1586,23 +1589,26 @@ static Base *object_mouse_select_menu(bContext *C,
}
if (ok) {
- baseCount++;
+ base_count++;
BLI_linklist_append(&linklist, base);
- if (baseCount == SEL_MENU_SIZE) {
+ if (base_count == SEL_MENU_SIZE) {
break;
}
}
}
CTX_DATA_END;
- if (baseCount == 0) {
- return NULL;
+ *r_basact = NULL;
+
+ if (base_count == 0) {
+ return false;
}
- if (baseCount == 1) {
+ if (base_count == 1) {
Base *base = (Base *)linklist.list->link;
BLI_linklist_free(linklist.list, NULL);
- return base;
+ *r_basact = base;
+ return false;
}
/* UI, full in static array values that we later use in an enum function */
@@ -1624,22 +1630,25 @@ static Base *object_mouse_select_menu(bContext *C,
PointerRNA ptr;
WM_operator_properties_create_ptr(&ptr, ot);
- RNA_boolean_set(&ptr, "extend", extend);
- RNA_boolean_set(&ptr, "deselect", deselect);
- RNA_boolean_set(&ptr, "toggle", toggle);
+ RNA_boolean_set(&ptr, "extend", params->sel_op == SEL_OP_ADD);
+ RNA_boolean_set(&ptr, "deselect", params->sel_op == SEL_OP_SUB);
+ RNA_boolean_set(&ptr, "toggle", params->sel_op == SEL_OP_XOR);
WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &ptr, NULL);
WM_operator_properties_free(&ptr);
BLI_linklist_free(linklist.list, NULL);
- return NULL;
+ return true;
}
static int bone_select_menu_exec(bContext *C, wmOperator *op)
{
const int name_index = RNA_enum_get(op->ptr, "name");
- const bool extend = RNA_boolean_get(op->ptr, "extend");
- const bool deselect = RNA_boolean_get(op->ptr, "deselect");
- const bool toggle = RNA_boolean_get(op->ptr, "toggle");
+
+ const struct SelectPick_Params params = {
+ .sel_op = ED_select_op_from_booleans(RNA_boolean_get(op->ptr, "extend"),
+ RNA_boolean_get(op->ptr, "deselect"),
+ RNA_boolean_get(op->ptr, "toggle")),
+ };
View3D *v3d = CTX_wm_view3d(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
@@ -1653,21 +1662,20 @@ static int bone_select_menu_exec(bContext *C, wmOperator *op)
BLI_assert(BASE_SELECTABLE(v3d, basact));
- if (basact->object->mode == OB_MODE_EDIT) {
+ if (basact->object->mode & OB_MODE_EDIT) {
EditBone *ebone = (EditBone *)object_mouse_select_menu_data[name_index].item_ptr;
- ED_armature_edit_select_pick_bone(C, basact, ebone, BONE_SELECTED, extend, deselect, toggle);
+ ED_armature_edit_select_pick_bone(C, basact, ebone, BONE_SELECTED, &params);
}
else {
bPoseChannel *pchan = (bPoseChannel *)object_mouse_select_menu_data[name_index].item_ptr;
- ED_armature_pose_select_pick_bone(
- view_layer, v3d, basact->object, pchan->bone, extend, deselect, toggle);
+ ED_armature_pose_select_pick_bone(view_layer, v3d, basact->object, pchan->bone, &params);
}
/* Weak but ensures we activate the menu again before using the enum. */
memset(object_mouse_select_menu_data, 0, sizeof(object_mouse_select_menu_data));
/* We make the armature selected:
- * Not-selected active object in posemode won't work well for tools. */
+ * Not-selected active object in pose-mode won't work well for tools. */
ED_object_base_select(basact, BA_SELECT);
WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, basact->object);
@@ -1675,14 +1683,22 @@ static int bone_select_menu_exec(bContext *C, wmOperator *op)
/* In weight-paint, we use selected bone to select vertex-group,
* so don't switch to new active object. */
- if (oldbasact && (oldbasact->object->mode & OB_MODE_ALL_WEIGHT_PAINT)) {
- /* Prevent activating.
- * Selection causes this to be considered the 'active' pose in weight-paint mode.
- * Eventually this limitation may be removed.
- * For now, de-select all other pose objects deforming this mesh. */
- ED_armature_pose_select_in_wpaint_mode(view_layer, basact);
-
- basact = NULL;
+ if (oldbasact) {
+ if (basact->object->mode & OB_MODE_EDIT) {
+ /* Pass. */
+ }
+ else if (oldbasact->object->mode & OB_MODE_ALL_WEIGHT_PAINT) {
+ /* Prevent activating.
+ * Selection causes this to be considered the 'active' pose in weight-paint mode.
+ * Eventually this limitation may be removed.
+ * For now, de-select all other pose objects deforming this mesh. */
+ ED_armature_pose_select_in_wpaint_mode(view_layer, basact);
+ }
+ else {
+ if (oldbasact != basact) {
+ ED_object_base_activate(C, basact);
+ }
+ }
}
/* Undo? */
@@ -1712,7 +1728,7 @@ void VIEW3D_OT_bone_select_menu(wmOperatorType *ot)
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
- /* keyingset to use (dynamic enum) */
+ /* #Object.id.name to select (dynamic enum). */
prop = RNA_def_enum(ot->srna, "name", DummyRNA_NULL_items, 0, "Bone Name", "");
RNA_def_enum_funcs(prop, object_select_menu_enum_itemf);
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_ENUM_NO_TRANSLATE);
@@ -1725,17 +1741,19 @@ void VIEW3D_OT_bone_select_menu(wmOperatorType *ot)
prop = RNA_def_boolean(ot->srna, "toggle", 0, "Toggle", "");
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
}
+
+/**
+ * \return True when a menu was activated.
+ */
static bool bone_mouse_select_menu(bContext *C,
const GPUSelectResult *buffer,
const int hits,
const bool is_editmode,
- const bool extend,
- const bool deselect,
- const bool toggle)
+ const struct SelectPick_Params *params)
{
BLI_assert(buffer);
- short baseCount = 0;
+ int bone_count = 0;
LinkNodePair base_list = {NULL, NULL};
LinkNodePair bone_list = {NULL, NULL};
GSet *added_bones = BLI_gset_ptr_new("Bone mouse select menu");
@@ -1794,12 +1812,12 @@ static bool bone_mouse_select_menu(bContext *C,
const bool is_duplicate_bone = BLI_gset_haskey(added_bones, bone_ptr);
if (!is_duplicate_bone) {
- baseCount++;
+ bone_count++;
BLI_linklist_append(&base_list, bone_base);
BLI_linklist_append(&bone_list, bone_ptr);
BLI_gset_insert(added_bones, bone_ptr);
- if (baseCount == SEL_MENU_SIZE) {
+ if (bone_count == SEL_MENU_SIZE) {
break;
}
}
@@ -1807,10 +1825,10 @@ static bool bone_mouse_select_menu(bContext *C,
BLI_gset_free(added_bones, NULL);
- if (baseCount == 0) {
+ if (bone_count == 0) {
return false;
}
- if (baseCount == 1) {
+ if (bone_count == 1) {
BLI_linklist_free(base_list.list, NULL);
BLI_linklist_free(bone_list.list, NULL);
return false;
@@ -1847,9 +1865,9 @@ static bool bone_mouse_select_menu(bContext *C,
PointerRNA ptr;
WM_operator_properties_create_ptr(&ptr, ot);
- RNA_boolean_set(&ptr, "extend", extend);
- RNA_boolean_set(&ptr, "deselect", deselect);
- RNA_boolean_set(&ptr, "toggle", toggle);
+ RNA_boolean_set(&ptr, "extend", params->sel_op == SEL_OP_ADD);
+ RNA_boolean_set(&ptr, "deselect", params->sel_op == SEL_OP_SUB);
+ RNA_boolean_set(&ptr, "toggle", params->sel_op == SEL_OP_XOR);
WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &ptr, NULL);
WM_operator_properties_free(&ptr);
@@ -2030,6 +2048,40 @@ static int mixed_bones_object_selectbuffer_extended(ViewContext *vc,
}
/**
+ * Compare result of 'GPU_select': 'GPUSelectResult',
+ * Needed for stable sorting, so cycling through all items near the cursor behaves predictably.
+ */
+static int gpu_select_buffer_depth_id_cmp(const void *sel_a_p, const void *sel_b_p)
+{
+ GPUSelectResult *a = (GPUSelectResult *)sel_a_p;
+ GPUSelectResult *b = (GPUSelectResult *)sel_b_p;
+
+ if (a->depth < b->depth) {
+ return -1;
+ }
+ if (a->depth > b->depth) {
+ return 1;
+ }
+
+ /* Depths match, sort by id. */
+ uint sel_a = a->id;
+ uint sel_b = b->id;
+
+#ifdef __BIG_ENDIAN__
+ BLI_endian_switch_uint32(&sel_a);
+ BLI_endian_switch_uint32(&sel_b);
+#endif
+
+ if (sel_a < sel_b) {
+ return -1;
+ }
+ if (sel_a > sel_b) {
+ return 1;
+ }
+ return 0;
+}
+
+/**
* \param has_bones: When true, skip non-bone hits, also allow bases to be used
* that are visible but not select-able,
* since you may be in pose mode with an un-selectable object.
@@ -2039,115 +2091,204 @@ static int mixed_bones_object_selectbuffer_extended(ViewContext *vc,
static Base *mouse_select_eval_buffer(ViewContext *vc,
const GPUSelectResult *buffer,
int hits,
- Base *startbase,
- bool has_bones,
bool do_nearest,
- int *r_sub_selection)
+ bool has_bones,
+ bool do_bones_get_priotity,
+ int *r_select_id_subelem)
{
ViewLayer *view_layer = vc->view_layer;
View3D *v3d = vc->v3d;
- Base *base, *basact = NULL;
int a;
- int sub_selection_id = 0;
+
+ bool found = false;
+ int select_id = 0;
+ int select_id_subelem = 0;
if (do_nearest) {
uint min = 0xFFFFFFFF;
- int selcol = 0, notcol = 0;
+ int hit_index = -1;
- if (has_bones) {
+ if (has_bones && do_bones_get_priotity) {
/* we skip non-bone hits */
for (a = 0; a < hits; a++) {
if (min > buffer[a].depth && (buffer[a].id & 0xFFFF0000)) {
min = buffer[a].depth;
- selcol = buffer[a].id & 0xFFFF;
- sub_selection_id = (buffer[a].id & 0xFFFF0000) >> 16;
+ hit_index = a;
}
}
}
else {
- /* only exclude active object when it is selected... */
- if (BASACT(view_layer) && (BASACT(view_layer)->flag & BASE_SELECTED) && hits > 1) {
- notcol = BASACT(view_layer)->object->runtime.select_id;
- }
for (a = 0; a < hits; a++) {
- if (min > buffer[a].depth && notcol != (buffer[a].id & 0xFFFF)) {
+ /* Any object. */
+ if (min > buffer[a].depth) {
min = buffer[a].depth;
- selcol = buffer[a].id & 0xFFFF;
- sub_selection_id = (buffer[a].id & 0xFFFF0000) >> 16;
+ hit_index = a;
}
}
- }
- base = FIRSTBASE(view_layer);
- while (base) {
- if (has_bones ? BASE_VISIBLE(v3d, base) : BASE_SELECTABLE(v3d, base)) {
- if (base->object->runtime.select_id == selcol) {
- break;
+ /* Find the best active & non-active hits.
+ * NOTE(@campbellbarton): Checking if `hits > 1` isn't a reliable way to know
+ * if there are multiple objects selected since it's possible the same object
+ * generates multiple hits, either from:
+ * - Multiple sub-components (bones & camera tracks).
+ * - Multiple selectable elements such as the object center and the geometry.
+ *
+ * For this reason, keep track of the best hit as well as the best hit that
+ * excludes the selected & active object, using this value when it's valid. */
+ if ((hit_index != -1) &&
+ /* Special case, cycling away from the active object should only be done when it
+ * doesn't have a bone selection, otherwise selecting sub-elements is difficult. */
+ ((buffer[hit_index].id & 0xFFFF0000) == 0) &&
+ /* Only exclude active object when it is selected. */
+ (BASACT(view_layer) && (BASACT(view_layer)->flag & BASE_SELECTED)) &&
+ /* Allow disabling this behavior entirely. */
+ (U.experimental.use_select_nearest_on_first_click == false)) {
+
+ const int select_id_active = BASACT(view_layer)->object->runtime.select_id;
+
+ /* Check if `hit_index` is the current active object. */
+ if ((buffer[hit_index].id & 0xFFFF) == select_id_active) {
+ uint min_not_active = 0xFFFFFFFF;
+ int hit_index_not_active = -1;
+ for (a = 0; a < hits; a++) {
+ /* Any object other than the active-selected. */
+ if (select_id_active == (buffer[a].id & 0xFFFF)) {
+ continue;
+ }
+ if (min_not_active > buffer[a].depth) {
+ min_not_active = buffer[a].depth;
+ hit_index_not_active = a;
+ }
+ }
+
+ /* When the active was selected, first try to use the index
+ * for the best non-active hit that was found. */
+ if (hit_index_not_active != -1) {
+ hit_index = hit_index_not_active;
+ }
}
}
- base = base->next;
}
- if (base) {
- basact = base;
+
+ if (hit_index != -1) {
+ select_id = buffer[hit_index].id & 0xFFFF;
+ select_id_subelem = (buffer[hit_index].id & 0xFFFF0000) >> 16;
+ found = true;
+ /* No need to set `min` to `buffer[hit_index].depth`, it's not used from now on. */
}
}
else {
- base = startbase;
- while (base) {
- /* skip objects with select restriction, to prevent prematurely ending this loop
- * with an un-selectable choice */
- if (has_bones ? (base->flag & BASE_VISIBLE_VIEWLAYER) == 0 :
- (base->flag & BASE_SELECTABLE) == 0) {
- base = base->next;
- if (base == NULL) {
- base = FIRSTBASE(view_layer);
- }
- if (base == startbase) {
- break;
+ {
+ GPUSelectResult *buffer_sorted = MEM_mallocN(sizeof(*buffer_sorted) * hits, __func__);
+ memcpy(buffer_sorted, buffer, sizeof(*buffer_sorted) * hits);
+ /* Remove non-bone objects. */
+ if (has_bones && do_bones_get_priotity) {
+ /* Loop backwards to reduce re-ordering. */
+ for (a = hits - 1; a >= 0; a--) {
+ if ((buffer_sorted[a].id & 0xFFFF0000) == 0) {
+ buffer_sorted[a] = buffer_sorted[--hits];
+ }
}
}
+ qsort(buffer_sorted, hits, sizeof(GPUSelectResult), gpu_select_buffer_depth_id_cmp);
+ buffer = buffer_sorted;
+ }
- if (has_bones ? BASE_VISIBLE(v3d, base) : BASE_SELECTABLE(v3d, base)) {
- for (a = 0; a < hits; a++) {
- if (has_bones) {
- /* skip non-bone objects */
- if (buffer[a].id & 0xFFFF0000) {
- if (base->object->runtime.select_id == (buffer[a].id & 0xFFFF)) {
- basact = base;
- }
- }
- }
- else {
- if (base->object->runtime.select_id == (buffer[a].id & 0xFFFF)) {
- basact = base;
- }
+ int hit_index = -1;
+
+ /* It's possible there are no hits (all objects contained bones). */
+ if (hits > 0) {
+ /* Only exclude active object when it is selected. */
+ if (BASACT(view_layer) && (BASACT(view_layer)->flag & BASE_SELECTED)) {
+ const int select_id_active = BASACT(view_layer)->object->runtime.select_id;
+ for (int i_next = 0, i_prev = hits - 1; i_next < hits; i_prev = i_next++) {
+ if ((select_id_active == (buffer[i_prev].id & 0xFFFF)) &&
+ (select_id_active != (buffer[i_next].id & 0xFFFF))) {
+ hit_index = i_next;
+ break;
}
}
}
- if (basact) {
- break;
+ /* When the active object is unselected or not in `buffer`, use the nearest. */
+ if (hit_index == -1) {
+ /* Just pick the nearest. */
+ hit_index = 0;
}
+ }
- base = base->next;
- if (base == NULL) {
- base = FIRSTBASE(view_layer);
- }
- if (base == startbase) {
- break;
- }
+ if (hit_index != -1) {
+ select_id = buffer[hit_index].id & 0xFFFF;
+ select_id_subelem = (buffer[hit_index].id & 0xFFFF0000) >> 16;
+ found = true;
}
+ MEM_freeN((void *)buffer);
}
- if (basact && r_sub_selection) {
- *r_sub_selection = sub_selection_id;
+ Base *basact = NULL;
+ if (found) {
+ for (Base *base = FIRSTBASE(view_layer); base; base = base->next) {
+ if (has_bones ? BASE_VISIBLE(v3d, base) : BASE_SELECTABLE(v3d, base)) {
+ if (base->object->runtime.select_id == select_id) {
+ basact = base;
+ break;
+ }
+ }
+ }
+
+ if (basact && r_select_id_subelem) {
+ *r_select_id_subelem = select_id_subelem;
+ }
}
return basact;
}
+static Base *mouse_select_object_center(ViewContext *vc, Base *startbase, const int mval[2])
+{
+ ARegion *region = vc->region;
+ ViewLayer *view_layer = vc->view_layer;
+ View3D *v3d = vc->v3d;
+
+ Base *oldbasact = BASACT(view_layer);
+
+ const float mval_fl[2] = {(float)mval[0], (float)mval[1]};
+ float dist = ED_view3d_select_dist_px() * 1.3333f;
+ Base *basact = NULL;
+
+ /* Put the active object at a disadvantage to cycle through other objects. */
+ const float penalty_dist = 10.0f * UI_DPI_FAC;
+ Base *base = startbase;
+ while (base) {
+ if (BASE_SELECTABLE(v3d, base)) {
+ float screen_co[2];
+ if (ED_view3d_project_float_global(
+ region, base->object->obmat[3], screen_co, V3D_PROJ_TEST_CLIP_DEFAULT) ==
+ V3D_PROJ_RET_OK) {
+ float dist_test = len_manhattan_v2v2(mval_fl, screen_co);
+ if (base == oldbasact) {
+ dist_test += penalty_dist;
+ }
+ if (dist_test < dist) {
+ dist = dist_test;
+ basact = base;
+ }
+ }
+ }
+ base = base->next;
+
+ if (base == NULL) {
+ base = FIRSTBASE(view_layer);
+ }
+ if (base == startbase) {
+ break;
+ }
+ }
+ return basact;
+}
+
static Base *ed_view3d_give_base_under_cursor_ex(bContext *C,
const int mval[2],
int *r_material_slot)
@@ -2176,13 +2317,8 @@ static Base *ed_view3d_give_base_under_cursor_ex(bContext *C,
if (hits > 0) {
const bool has_bones = (r_material_slot == NULL) && selectbuffer_has_bones(buffer, hits);
- basact = mouse_select_eval_buffer(&vc,
- buffer,
- hits,
- vc.view_layer->object_bases.first,
- has_bones,
- do_nearest,
- r_material_slot);
+ basact = mouse_select_eval_buffer(
+ &vc, buffer, hits, do_nearest, has_bones, true, r_material_slot);
}
return basact;
@@ -2237,346 +2373,483 @@ static void deselect_all_tracks(MovieTracking *tracking)
}
}
-/* mval is region coords */
-static bool ed_object_select_pick(bContext *C,
- const int mval[2],
- bool extend,
- bool deselect,
- bool toggle,
- bool obcenter,
- bool enumerate,
- bool object)
+static bool ed_object_select_pick_camera_track(bContext *C,
+ Scene *scene,
+ Base *basact,
+ MovieClip *clip,
+ const struct GPUSelectResult *buffer,
+ const short hits,
+ const struct SelectPick_Params *params)
{
- Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
- ViewContext vc;
- /* setup view context for argument to callbacks */
- ED_view3d_viewcontext_init(C, &vc, depsgraph);
+ bool changed = false;
+ bool found = false;
- const ARegion *region = CTX_wm_region(C);
- Scene *scene = CTX_data_scene(C);
- ViewLayer *view_layer = CTX_data_view_layer(C);
- View3D *v3d = CTX_wm_view3d(C);
- /* Don't set when the context has no active object (hidden), see: T60807. */
- const Base *oldbasact = vc.obact ? BASACT(view_layer) : NULL;
- Base *base, *startbase = NULL, *basact = NULL;
- const eObjectMode object_mode = oldbasact ? oldbasact->object->mode : OB_MODE_OBJECT;
- bool is_obedit;
- float dist = ED_view3d_select_dist_px() * 1.3333f;
- bool retval = false;
- int hits;
- const float mval_fl[2] = {(float)mval[0], (float)mval[1]};
+ MovieTracking *tracking = &clip->tracking;
+ ListBase *tracksbase = NULL;
+ MovieTrackingTrack *track = NULL;
- is_obedit = (vc.obedit != NULL);
- if (object) {
- /* Signal for #view3d_opengl_select to skip edit-mode objects. */
- vc.obedit = NULL;
- }
+ for (int i = 0; i < hits; i++) {
+ const int hitresult = buffer[i].id;
- /* In pose mode we don't want to mess with object selection. */
- const bool is_pose_mode = (vc.obact && vc.obact->mode & OB_MODE_POSE);
+ /* If there's bundles in buffer select bundles first,
+ * so non-camera elements should be ignored in buffer. */
+ if (basact->object->runtime.select_id != (hitresult & 0xFFFF)) {
+ continue;
+ }
+ /* Index of bundle is 1<<16-based. if there's no "bone" index
+ * in height word, this buffer value belongs to camera. not to bundle. */
+ if ((hitresult & 0xFFFF0000) == 0) {
+ continue;
+ }
- /* always start list from basact in wire mode */
- startbase = FIRSTBASE(view_layer);
- if (oldbasact && oldbasact->next) {
- startbase = oldbasact->next;
+ track = BKE_tracking_track_get_indexed(&clip->tracking, hitresult >> 16, &tracksbase);
+ found = true;
+ break;
}
- /* This block uses the control key to make the object selected
- * by its center point rather than its contents */
-
- /* In edit-mode do not activate. */
- if (obcenter) {
-
- /* NOTE: shift+alt goes to group-flush-selecting. */
- if (enumerate) {
- basact = object_mouse_select_menu(C, &vc, NULL, 0, mval, extend, deselect, toggle);
+ /* Note `params->deselect_all` is ignored for tracks as in this case
+ * all objects will be de-selected (not tracks). */
+ if (params->sel_op == SEL_OP_SET) {
+ if ((found && params->select_passthrough) && TRACK_SELECTED(track)) {
+ found = false;
}
- else {
- base = startbase;
- while (base) {
- if (BASE_SELECTABLE(v3d, base)) {
- float screen_co[2];
- if (ED_view3d_project_float_global(
- region, base->object->obmat[3], screen_co, V3D_PROJ_TEST_CLIP_DEFAULT) ==
- V3D_PROJ_RET_OK) {
- float dist_temp = len_manhattan_v2v2(mval_fl, screen_co);
- if (base == oldbasact) {
- dist_temp += 10.0f;
- }
- if (dist_temp < dist) {
- dist = dist_temp;
- basact = base;
- }
- }
- }
- base = base->next;
+ else if (found /* `|| params->deselect_all` */) {
+ /* Deselect everything. */
+ deselect_all_tracks(tracking);
+ changed = true;
+ }
+ }
- if (base == NULL) {
- base = FIRSTBASE(view_layer);
+ if (found) {
+ switch (params->sel_op) {
+ case SEL_OP_ADD: {
+ BKE_tracking_track_select(tracksbase, track, TRACK_AREA_ALL, true);
+ break;
+ }
+ case SEL_OP_SUB: {
+ BKE_tracking_track_deselect(track, TRACK_AREA_ALL);
+ break;
+ }
+ case SEL_OP_XOR: {
+ if (TRACK_SELECTED(track)) {
+ BKE_tracking_track_deselect(track, TRACK_AREA_ALL);
}
- if (base == startbase) {
- break;
+ else {
+ BKE_tracking_track_select(tracksbase, track, TRACK_AREA_ALL, true);
}
+ break;
}
- }
- if (scene->toolsettings->object_flag & SCE_OBJECT_MODE_LOCK) {
- if (is_obedit == false) {
- if (basact && !BKE_object_is_mode_compat(basact->object, object_mode)) {
- if (object_mode == OB_MODE_OBJECT) {
- struct Main *bmain = CTX_data_main(C);
- ED_object_mode_generic_exit(bmain, vc.depsgraph, scene, basact->object);
- }
- if (!BKE_object_is_mode_compat(basact->object, object_mode)) {
- basact = NULL;
- }
- }
+ case SEL_OP_SET: {
+ BKE_tracking_track_select(tracksbase, track, TRACK_AREA_ALL, false);
+ break;
+ }
+ case SEL_OP_AND: {
+ BLI_assert_unreachable(); /* Doesn't make sense for picking. */
+ break;
}
}
+
+ DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
+ DEG_id_tag_update(&clip->id, ID_RECALC_SELECT);
+ WM_event_add_notifier(C, NC_MOVIECLIP | ND_SELECT, track);
+ WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
+
+ changed = true;
}
- else {
- GPUSelectResult buffer[MAXPICKELEMS];
- bool do_nearest;
- // TIMEIT_START(select_time);
+ return changed || found;
+}
- /* if objects have posemode set, the bones are in the same selection buffer */
- const eV3DSelectObjectFilter select_filter = ((object == false) ?
- ED_view3d_select_filter_from_mode(scene,
- vc.obact) :
- VIEW3D_SELECT_FILTER_NOP);
- hits = mixed_bones_object_selectbuffer_extended(
- &vc, buffer, ARRAY_SIZE(buffer), mval, select_filter, true, enumerate, &do_nearest);
+/**
+ * Cursor selection picking for object & pose-mode.
+ *
+ * \param mval: Region relative cursor coordinates.
+ * \param params: Selection parameters.
+ * \param center: Select by the cursors on-screen distances to the center/origin
+ * instead of the geometry any other contents of the item being selected.
+ * This could be used to select by bones by their origin too, currently it's only used for objects.
+ * \param enumerate: Show a menu for objects at the cursor location.
+ * Otherwise fall-through to non-menu selection.
+ * \param object_only: Only select objects (not bones / track markers).
+ */
+static bool ed_object_select_pick(bContext *C,
+ const int mval[2],
+ const struct SelectPick_Params *params,
+ const bool center,
+ const bool enumerate,
+ const bool object_only)
+{
+ Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ ViewContext vc;
+ /* Setup view context for argument to callbacks. */
+ ED_view3d_viewcontext_init(C, &vc, depsgraph);
- // TIMEIT_END(select_time);
+ Scene *scene = vc.scene;
+ View3D *v3d = vc.v3d;
- if (hits > 0) {
- /* NOTE: bundles are handling in the same way as bones. */
- const bool has_bones = object ? false : selectbuffer_has_bones(buffer, hits);
+ /* Menu activation may find a base to make active (if it only finds a single item to select). */
+ Base *basact_override = NULL;
- /* NOTE: shift+alt goes to group-flush-selecting. */
- if (enumerate) {
- if (has_bones &&
- bone_mouse_select_menu(C, buffer, hits, false, extend, deselect, toggle)) {
- basact = NULL;
+ const bool is_obedit = (vc.obedit != NULL);
+ if (object_only) {
+ /* Signal for #view3d_opengl_select to skip edit-mode objects. */
+ vc.obedit = NULL;
+ }
+
+ /* Set for GPU depth buffer picking, leave NULL when selecting by center. */
+ struct {
+ GPUSelectResult buffer[MAXPICKELEMS];
+ int hits;
+ bool do_nearest;
+ bool has_bones;
+ } *gpu = NULL;
+
+ /* First handle menu selection, early exit if a menu opens
+ * since this takes ownership of the selection action.
+ *
+ * Even when there is no menu `basact_override` may be set to avoid having to re-find
+ * the item under the cursor. */
+
+ if (center == false) {
+ gpu = MEM_mallocN(sizeof(*gpu), __func__);
+ gpu->do_nearest = false;
+ gpu->has_bones = false;
+
+ /* If objects have pose-mode set, the bones are in the same selection buffer. */
+ const eV3DSelectObjectFilter select_filter = ((object_only == false) ?
+ ED_view3d_select_filter_from_mode(scene,
+ vc.obact) :
+ VIEW3D_SELECT_FILTER_NOP);
+ gpu->hits = mixed_bones_object_selectbuffer_extended(&vc,
+ gpu->buffer,
+ ARRAY_SIZE(gpu->buffer),
+ mval,
+ select_filter,
+ true,
+ enumerate,
+ &gpu->do_nearest);
+ gpu->has_bones = (object_only && gpu->hits > 0) ?
+ false :
+ selectbuffer_has_bones(gpu->buffer, gpu->hits);
+ }
+
+ /* First handle menu selection, early exit when a menu was opened.
+ * Otherwise fall through to regular selection. */
+ if (enumerate) {
+ bool has_menu = false;
+ if (center) {
+ if (object_mouse_select_menu(C, &vc, NULL, 0, mval, params, &basact_override)) {
+ has_menu = true;
+ }
+ }
+ else {
+ if (gpu->hits != 0) {
+ if (gpu->has_bones && bone_mouse_select_menu(C, gpu->buffer, gpu->hits, false, params)) {
+ has_menu = true;
}
- else {
- basact = object_mouse_select_menu(C, &vc, buffer, hits, mval, extend, deselect, toggle);
+ else if (object_mouse_select_menu(
+ C, &vc, gpu->buffer, gpu->hits, mval, params, &basact_override)) {
+ has_menu = true;
}
}
- else {
- basact = mouse_select_eval_buffer(
- &vc, buffer, hits, startbase, has_bones, do_nearest, NULL);
- }
-
- if (has_bones && basact) {
- if (basact->object->type == OB_CAMERA) {
- MovieClip *clip = BKE_object_movieclip_get(scene, basact->object, false);
- if (clip != NULL && oldbasact == basact) {
- bool changed = false;
-
- for (int i = 0; i < hits; i++) {
- const int hitresult = buffer[i].id;
-
- /* if there's bundles in buffer select bundles first,
- * so non-camera elements should be ignored in buffer */
- if (basact->object->runtime.select_id != (hitresult & 0xFFFF)) {
- continue;
- }
-
- /* index of bundle is 1<<16-based. if there's no "bone" index
- * in height word, this buffer value belongs to camera. not to bundle
- */
- if (hitresult & 0xFFFF0000) {
- MovieTracking *tracking = &clip->tracking;
- ListBase *tracksbase;
- MovieTrackingTrack *track;
-
- track = BKE_tracking_track_get_indexed(
- &clip->tracking, hitresult >> 16, &tracksbase);
+ }
- if (TRACK_SELECTED(track) && extend) {
- changed = false;
- BKE_tracking_track_deselect(track, TRACK_AREA_ALL);
- }
- else {
- int oldsel = TRACK_SELECTED(track) ? 1 : 0;
- if (!extend) {
- deselect_all_tracks(tracking);
- }
+ /* Let the menu handle any further actions. */
+ if (has_menu) {
+ if (gpu != NULL) {
+ MEM_freeN(gpu);
+ }
+ return false;
+ }
+ }
- BKE_tracking_track_select(tracksbase, track, TRACK_AREA_ALL, extend);
+ /* No menu, continue with selection. */
- if (oldsel != (TRACK_SELECTED(track) ? 1 : 0)) {
- changed = true;
- }
- }
+ ViewLayer *view_layer = vc.view_layer;
+ /* Don't set when the context has no active object (hidden), see: T60807. */
+ const Base *oldbasact = vc.obact ? BASACT(view_layer) : NULL;
+ /* Always start list from `basact` when cycling the selection. */
+ Base *startbase = (oldbasact && oldbasact->next) ? oldbasact->next : FIRSTBASE(view_layer);
- ED_object_base_select(basact, BA_SELECT);
+ /* The next object's base to make active. */
+ Base *basact = NULL;
+ const eObjectMode object_mode = oldbasact ? oldbasact->object->mode : OB_MODE_OBJECT;
- retval = true;
+ /* When enabled, don't attempt any further selection. */
+ bool handled = false;
- DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
- DEG_id_tag_update(&clip->id, ID_RECALC_SELECT);
- WM_event_add_notifier(C, NC_MOVIECLIP | ND_SELECT, track);
- WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
+ /* Split `changed` into data-types so their associated updates can be properly performed.
+ * This is also needed as multiple changes may happen at once.
+ * Selecting a pose-bone or track can also select the object for e.g. */
+ bool changed_object = false;
+ bool changed_pose = false;
+ bool changed_track = false;
- break;
- }
- }
+ /* Handle setting the new base active (even when `handled == true`). */
+ bool use_activate_selected_base = false;
- if (!changed) {
- /* fallback to regular object selection if no new bundles were selected,
- * allows to select object parented to reconstruction object */
- basact = mouse_select_eval_buffer(&vc, buffer, hits, startbase, 0, do_nearest, NULL);
- }
+ if (center) {
+ if (basact_override) {
+ basact = basact_override;
+ }
+ else {
+ basact = mouse_select_object_center(&vc, startbase, mval);
+ }
+ }
+ else {
+ if (basact_override) {
+ basact = basact_override;
+ }
+ else {
+ /* Regarding bone priority.
+ *
+ * - When in pose-bone, it's useful that any selection containing a bone
+ * gets priority over other geometry (background scenery for example).
+ *
+ * - When in object-mode, don't prioritize bones as it would cause
+ * pose-objects behind other objects to get priority
+ * (mainly noticeable when #SCE_OBJECT_MODE_LOCK is disabled).
+ *
+ * This way prioritizing based on pose-mode has a bias to stay in pose-mode
+ * without having to enforce this through locking the object mode. */
+ bool do_bones_get_priotity = (object_mode & OB_MODE_POSE) != 0;
+
+ basact = (gpu->hits > 0) ? mouse_select_eval_buffer(&vc,
+ gpu->buffer,
+ gpu->hits,
+ gpu->do_nearest,
+ gpu->has_bones,
+ do_bones_get_priotity,
+ NULL) :
+ NULL;
+ }
+
+ /* Select pose-bones or camera-tracks. */
+ if (((gpu->hits > 0) && gpu->has_bones) ||
+ /* Special case, even when there are no hits, pose logic may de-select all bones. */
+ ((gpu->hits == 0) && (object_mode & OB_MODE_POSE))) {
+
+ if (basact && (gpu->has_bones && (basact->object->type == OB_CAMERA))) {
+ MovieClip *clip = BKE_object_movieclip_get(scene, basact->object, false);
+ if (clip != NULL) {
+ if (ed_object_select_pick_camera_track(
+ C, scene, basact, clip, gpu->buffer, gpu->hits, params)) {
+ ED_object_base_select(basact, BA_SELECT);
+ /* Don't set `handled` here as the object activation may be necessary. */
+ changed_object = true;
+
+ changed_track = true;
+ }
+ else {
+ /* Fallback to regular object selection if no new bundles were selected,
+ * allows to select object parented to reconstruction object. */
+ basact = mouse_select_eval_buffer(
+ &vc, gpu->buffer, gpu->hits, gpu->do_nearest, false, false, NULL);
}
}
- else if (ED_armature_pose_select_pick_with_buffer(view_layer,
- v3d,
- basact,
- buffer,
- hits,
- extend,
- deselect,
- toggle,
- do_nearest)) {
- /* then bone is found */
-
- /* we make the armature selected:
- * not-selected active object in posemode won't work well for tools */
+ }
+ else if (ED_armature_pose_select_pick_with_buffer(view_layer,
+ v3d,
+ basact ? basact : (Base *)oldbasact,
+ gpu->buffer,
+ gpu->hits,
+ params,
+ gpu->do_nearest)) {
+
+ changed_pose = true;
+
+ /* When there is no `baseact` this will have operated on `oldbasact`,
+ * allowing #SelectPick_Params.deselect_all work in pose-mode.
+ * In this case no object operations are needed. */
+ if (basact != NULL) {
+ /* By convention the armature-object is selected when in pose-mode.
+ * While leaving it unselected will work, leaving pose-mode would leave the object
+ * active + unselected which isn't ideal when performing other actions on the object. */
ED_object_base_select(basact, BA_SELECT);
+ changed_object = true;
- retval = true;
WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, basact->object);
WM_event_add_notifier(C, NC_OBJECT | ND_BONE_ACTIVE, basact->object);
- DEG_id_tag_update(&scene->id, ID_RECALC_BASE_FLAGS);
- /* In weight-paint, we use selected bone to select vertex-group,
- * so don't switch to new active object. */
- if (oldbasact && (oldbasact->object->mode & OB_MODE_ALL_WEIGHT_PAINT)) {
- /* Prevent activating.
- * Selection causes this to be considered the 'active' pose in weight-paint mode.
- * Eventually this limitation may be removed.
- * For now, de-select all other pose objects deforming this mesh. */
- ED_armature_pose_select_in_wpaint_mode(view_layer, basact);
+ /* In weight-paint, we use selected bone to select vertex-group.
+ * In this case the active object mustn't change as it would leave weight-paint mode. */
+ if (oldbasact) {
+ if (oldbasact->object->mode & OB_MODE_ALL_WEIGHT_PAINT) {
+ /* Prevent activating.
+ * Selection causes this to be considered the 'active' pose in weight-paint mode.
+ * Eventually this limitation may be removed.
+ * For now, de-select all other pose objects deforming this mesh. */
+ ED_armature_pose_select_in_wpaint_mode(view_layer, basact);
+
+ handled = true;
+ }
+ else if ((object_mode & OB_MODE_POSE) && (basact->object->mode & OB_MODE_POSE)) {
+ /* Within pose-mode, keep the current selection when switching pose bones,
+ * this is noticeable when in pose mode with multiple objects at once.
+ * Where selecting the bone of a different object would de-select this one.
+ * After that, exiting pose-mode would only have the active armature selected.
+ * This matches multi-object edit-mode behavior. */
+ handled = true;
+
+ if (oldbasact != basact) {
+ use_activate_selected_base = true;
+ }
+ }
+ else {
+ /* Don't set `handled` here as the object selection may be necessary
+ * when starting out in object-mode and moving into pose-mode,
+ * when moving from pose to object-mode using object selection also makes sense. */
+ }
+ }
+ }
+ }
+ /* Prevent bone/track selecting to pass on to object selecting. */
+ if (basact == oldbasact) {
+ handled = true;
+ }
+ }
+ }
+ if (handled == false) {
+ if (scene->toolsettings->object_flag & SCE_OBJECT_MODE_LOCK) {
+ /* No special logic in edit-mode. */
+ if (is_obedit == false) {
+ if (basact && !BKE_object_is_mode_compat(basact->object, object_mode)) {
+ if (object_mode == OB_MODE_OBJECT) {
+ struct Main *bmain = vc.bmain;
+ ED_object_mode_generic_exit(bmain, vc.depsgraph, scene, basact->object);
+ }
+ if (!BKE_object_is_mode_compat(basact->object, object_mode)) {
basact = NULL;
}
}
- /* prevent bone selecting to pass on to object selecting */
- if (basact == oldbasact) {
- basact = NULL;
- }
- }
- if (scene->toolsettings->object_flag & SCE_OBJECT_MODE_LOCK) {
- if (is_obedit == false) {
- if (basact && !BKE_object_is_mode_compat(basact->object, object_mode)) {
- if (object_mode == OB_MODE_OBJECT) {
- struct Main *bmain = CTX_data_main(C);
- ED_object_mode_generic_exit(bmain, vc.depsgraph, scene, basact->object);
- }
- if (!BKE_object_is_mode_compat(basact->object, object_mode)) {
- basact = NULL;
- }
+ /* Disallow switching modes,
+ * special exception for edit-mode - vertex-parent operator. */
+ if (basact && oldbasact) {
+ if ((oldbasact->object->mode != basact->object->mode) &&
+ (oldbasact->object->mode & basact->object->mode) == 0) {
+ basact = NULL;
}
}
}
}
}
- if (scene->toolsettings->object_flag & SCE_OBJECT_MODE_LOCK) {
- /* Disallow switching modes,
- * special exception for edit-mode - vertex-parent operator. */
- if (is_obedit == false) {
- if (oldbasact && basact) {
- if ((oldbasact->object->mode != basact->object->mode) &&
- (oldbasact->object->mode & basact->object->mode) == 0) {
- basact = NULL;
+ /* Ensure code above doesn't change the active base. This code is already fairly involved,
+ * it's best if changing the active object is localized to a single place. */
+ BLI_assert(oldbasact == (vc.obact ? BASACT(view_layer) : NULL));
+
+ bool found = (basact != NULL);
+ if ((handled == false) && (vc.obedit == NULL)) {
+ /* Object-mode (pose mode will have been handled already). */
+ if (params->sel_op == SEL_OP_SET) {
+ if ((found && params->select_passthrough) && (basact->flag & BASE_SELECTED)) {
+ found = false;
+ /* NOTE(@campbellbarton): Experimental behavior to set active even keeping the selection
+ * without this it's inconvenient to set the active object. */
+ if (basact != oldbasact) {
+ use_activate_selected_base = true;
+ }
+ }
+ else if (found || params->deselect_all) {
+ /* Deselect everything. */
+ /* `basact` may be NULL. */
+ if (ED_view3d_object_deselect_all_except(view_layer, basact)) {
+ changed_object = true;
}
}
}
}
- /* Ensure code above doesn't change the active base. */
- BLI_assert(oldbasact == (vc.obact ? BASACT(view_layer) : NULL));
-
- /* so, do we have something selected? */
- if (basact) {
- retval = true;
+ if ((handled == false) && found) {
if (vc.obedit) {
- /* only do select */
+ /* Only do the select (use for setting vertex parents & hooks).
+ * In edit-mode do not activate. */
ED_view3d_object_deselect_all_except(view_layer, basact);
ED_object_base_select(basact, BA_SELECT);
+
+ changed_object = true;
}
- /* also prevent making it active on mouse selection */
+ /* Also prevent making it active on mouse selection. */
else if (BASE_SELECTABLE(v3d, basact)) {
- const bool use_activate_selected_base = (oldbasact != basact) && (is_obedit == false);
- if (extend) {
- ED_object_base_select(basact, BA_SELECT);
- }
- else if (deselect) {
- ED_object_base_select(basact, BA_DESELECT);
- }
- else if (toggle) {
- if (basact->flag & BASE_SELECTED) {
- /* Keep selected if the base is to be activated. */
- if (use_activate_selected_base == false) {
- ED_object_base_select(basact, BA_DESELECT);
- }
- }
- else {
+ use_activate_selected_base |= (oldbasact != basact) && (is_obedit == false);
+
+ switch (params->sel_op) {
+ case SEL_OP_ADD: {
ED_object_base_select(basact, BA_SELECT);
+ break;
}
- }
- else {
- /* When enabled, this puts other objects out of multi pose-mode. */
- if (is_pose_mode == false || (basact->object->mode & OB_MODE_POSE) == 0) {
+ case SEL_OP_SUB: {
+ ED_object_base_select(basact, BA_DESELECT);
+ break;
+ }
+ case SEL_OP_XOR: {
+ if (basact->flag & BASE_SELECTED) {
+ /* Keep selected if the base is to be activated. */
+ if (use_activate_selected_base == false) {
+ ED_object_base_select(basact, BA_DESELECT);
+ }
+ }
+ else {
+ ED_object_base_select(basact, BA_SELECT);
+ }
+ break;
+ }
+ case SEL_OP_SET: {
ED_view3d_object_deselect_all_except(view_layer, basact);
ED_object_base_select(basact, BA_SELECT);
+ break;
}
- }
-
- if (use_activate_selected_base) {
- ED_object_base_activate(C, basact); /* adds notifier */
- if ((scene->toolsettings->object_flag & SCE_OBJECT_MODE_LOCK) == 0) {
- WM_toolsystem_update_from_context_view3d(C);
+ case SEL_OP_AND: {
+ BLI_assert_unreachable(); /* Doesn't make sense for picking. */
+ break;
}
}
- /* Set special modes for grease pencil
- * The grease pencil modes are not real modes, but a hack to make the interface
- * consistent, so need some tricks to keep UI synchronized */
- /* XXX: This stuff needs reviewing (Aligorith) */
- if (false && (((oldbasact) && oldbasact->object->type == OB_GPENCIL) ||
- (basact->object->type == OB_GPENCIL))) {
- /* set cursor */
- if (ELEM(basact->object->mode,
- OB_MODE_PAINT_GPENCIL,
- OB_MODE_SCULPT_GPENCIL,
- OB_MODE_WEIGHT_GPENCIL,
- OB_MODE_VERTEX_GPENCIL)) {
- ED_gpencil_toggle_brush_cursor(C, true, NULL);
- }
- else {
- /* TODO: maybe is better use restore */
- ED_gpencil_toggle_brush_cursor(C, false, NULL);
- }
- }
+ changed_object = true;
}
+ }
+ /* Perform the activation even when 'handled', since this is used to ensure
+ * the object from the pose-bone selected is also activated. */
+ if (use_activate_selected_base && (basact != NULL)) {
+ changed_object = true;
+ ED_object_base_activate(C, basact); /* adds notifier */
+ if ((scene->toolsettings->object_flag & SCE_OBJECT_MODE_LOCK) == 0) {
+ WM_toolsystem_update_from_context_view3d(C);
+ }
+ }
+
+ if (changed_object) {
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 retval;
+ if (changed_pose) {
+ ED_outliner_select_sync_from_pose_bone_tag(C);
+ }
+
+ if (gpu != NULL) {
+ MEM_freeN(gpu);
+ }
+
+ return (changed_object || changed_pose || changed_track);
}
-/* mouse selection in weight paint */
-/* gets called via generic mouse select operator */
-static bool ed_wpaint_vertex_select_pick(
- bContext *C, const int mval[2], bool extend, bool deselect, bool toggle, Object *obact)
+/**
+ * Mouse selection in weight paint.
+ * Called via generic mouse select operator.
+ *
+ * \return True when pick finds an element or the selection changed.
+ */
+static bool ed_wpaint_vertex_select_pick(bContext *C,
+ const int mval[2],
+ const struct SelectPick_Params *params,
+ Object *obact)
{
View3D *v3d = CTX_wm_view3d(C);
const bool use_zbuf = !XRAY_ENABLED(v3d);
@@ -2584,21 +2857,44 @@ static bool ed_wpaint_vertex_select_pick(
Mesh *me = obact->data; /* already checked for NULL */
uint index = 0;
MVert *mv;
+ bool changed = false;
- if (ED_mesh_pick_vert(C, obact, mval, ED_MESH_PICK_DEFAULT_VERT_DIST, use_zbuf, &index)) {
- mv = &me->mvert[index];
- if (extend) {
- mv->flag |= SELECT;
- }
- else if (deselect) {
- mv->flag &= ~SELECT;
+ bool found = ED_mesh_pick_vert(C, obact, mval, ED_MESH_PICK_DEFAULT_VERT_DIST, use_zbuf, &index);
+
+ if (params->sel_op == SEL_OP_SET) {
+ if ((found && params->select_passthrough) && (me->mvert[index].flag & SELECT)) {
+ found = false;
}
- else if (toggle) {
- mv->flag ^= SELECT;
+ else if (found || params->deselect_all) {
+ /* Deselect everything. */
+ changed |= paintface_deselect_all_visible(C, obact, SEL_DESELECT, false);
}
- else {
- paintvert_deselect_all_visible(obact, SEL_DESELECT, false);
- mv->flag |= SELECT;
+ }
+
+ if (found) {
+ mv = &me->mvert[index];
+ switch (params->sel_op) {
+ case SEL_OP_ADD: {
+ mv->flag |= SELECT;
+ break;
+ }
+ case SEL_OP_SUB: {
+ mv->flag &= ~SELECT;
+ break;
+ }
+ case SEL_OP_XOR: {
+ mv->flag ^= SELECT;
+ break;
+ }
+ case SEL_OP_SET: {
+ paintvert_deselect_all_visible(obact, SEL_DESELECT, false);
+ mv->flag |= SELECT;
+ break;
+ }
+ case SEL_OP_AND: {
+ BLI_assert_unreachable(); /* Doesn't make sense for picking. */
+ break;
+ }
}
/* update mselect */
@@ -2610,10 +2906,15 @@ static bool ed_wpaint_vertex_select_pick(
}
paintvert_flush_flags(obact);
+
+ changed = true;
+ }
+
+ if (changed) {
paintvert_tag_select_update(C, obact);
- return true;
}
- return false;
+
+ return changed || found;
}
static int view3d_select_exec(bContext *C, wmOperator *op)
@@ -2621,29 +2922,36 @@ static int view3d_select_exec(bContext *C, wmOperator *op)
Scene *scene = CTX_data_scene(C);
Object *obedit = CTX_data_edit_object(C);
Object *obact = CTX_data_active_object(C);
- bool extend = RNA_boolean_get(op->ptr, "extend");
- bool deselect = RNA_boolean_get(op->ptr, "deselect");
- bool deselect_all = RNA_boolean_get(op->ptr, "deselect_all");
- bool toggle = RNA_boolean_get(op->ptr, "toggle");
+ const struct SelectPick_Params params = {
+ .sel_op = ED_select_op_from_booleans(RNA_boolean_get(op->ptr, "extend"),
+ RNA_boolean_get(op->ptr, "deselect"),
+ RNA_boolean_get(op->ptr, "toggle")),
+ .deselect_all = RNA_boolean_get(op->ptr, "deselect_all"),
+ .select_passthrough = RNA_boolean_get(op->ptr, "select_passthrough"),
+
+ };
bool center = RNA_boolean_get(op->ptr, "center");
bool enumerate = RNA_boolean_get(op->ptr, "enumerate");
/* Only force object select for edit-mode to support vertex parenting,
* or paint-select to allow pose bone select with vert/face select. */
- bool object = (RNA_boolean_get(op->ptr, "object") &&
- (obedit || BKE_paint_select_elem_test(obact) ||
- /* so its possible to select bones in weight-paint mode (LMB select) */
- (obact && (obact->mode & OB_MODE_ALL_WEIGHT_PAINT) &&
- BKE_object_pose_armature_get(obact))));
-
- bool retval = false;
- int location[2];
+ bool object_only = (RNA_boolean_get(op->ptr, "object") &&
+ (obedit || BKE_paint_select_elem_test(obact) ||
+ /* so its possible to select bones in weight-paint mode (LMB select) */
+ (obact && (obact->mode & OB_MODE_ALL_WEIGHT_PAINT) &&
+ BKE_object_pose_armature_get(obact))));
+
+ /* This could be called "changed_or_found" since this is true when there is an element
+ * under the cursor to select, even if it happens that the selection & active state doesn't
+ * actually change. This is important so undo pushes are predictable. */
+ bool changed = false;
+ int mval[2];
- RNA_int_get_array(op->ptr, "location", location);
+ RNA_int_get_array(op->ptr, "location", mval);
view3d_operator_needs_opengl(C);
BKE_object_update_select_id(CTX_data_main(C));
- if (object) {
+ if (object_only) {
obedit = NULL;
obact = NULL;
@@ -2653,12 +2961,9 @@ static int view3d_select_exec(bContext *C, wmOperator *op)
center = false;
}
- if (obedit && object == false) {
+ if (obedit && object_only == false) {
if (obedit->type == OB_MESH) {
- retval = EDBM_select_pick(C, location, extend, deselect, toggle);
- if (!retval && deselect_all) {
- retval = EDBM_mesh_deselect_all_multi(C);
- }
+ changed = EDBM_select_pick(C, mval, &params);
}
else if (obedit->type == OB_ARMATURE) {
if (enumerate) {
@@ -2667,107 +2972,50 @@ static int view3d_select_exec(bContext *C, wmOperator *op)
ED_view3d_viewcontext_init(C, &vc, depsgraph);
GPUSelectResult buffer[MAXPICKELEMS];
- const int hits = mixed_bones_object_selectbuffer(&vc,
- buffer,
- ARRAY_SIZE(buffer),
- location,
- VIEW3D_SELECT_FILTER_NOP,
- false,
- true,
- false);
- retval = bone_mouse_select_menu(C, buffer, hits, true, extend, deselect, toggle);
- }
- if (!retval) {
- retval = ED_armature_edit_select_pick(C, location, extend, deselect, toggle);
+ const int hits = mixed_bones_object_selectbuffer(
+ &vc, buffer, ARRAY_SIZE(buffer), mval, VIEW3D_SELECT_FILTER_NOP, false, true, false);
+ changed = bone_mouse_select_menu(C, buffer, hits, true, &params);
}
-
- if (!retval && deselect_all) {
- retval = ED_armature_edit_deselect_all_visible_multi(C);
- }
- if (retval) {
- ED_outliner_select_sync_from_edit_bone_tag(C);
+ if (!changed) {
+ changed = ED_armature_edit_select_pick(C, mval, &params);
}
}
else if (obedit->type == OB_LATTICE) {
- retval = ED_lattice_select_pick(C, location, extend, deselect, toggle);
- if (!retval && deselect_all) {
- retval = ED_lattice_deselect_all_multi(C);
- }
+ changed = ED_lattice_select_pick(C, mval, &params);
}
else if (ELEM(obedit->type, OB_CURVES_LEGACY, OB_SURF)) {
- retval = ED_curve_editnurb_select_pick(C, location, extend, deselect, toggle);
- if (!retval && deselect_all) {
- retval = ED_curve_deselect_all_multi(C);
- }
+ changed = ED_curve_editnurb_select_pick(C, mval, &params);
}
else if (obedit->type == OB_MBALL) {
- retval = ED_mball_select_pick(C, location, extend, deselect, toggle);
- if (!retval && deselect_all) {
- retval = ED_mball_deselect_all_multi(C);
- }
+ changed = ED_mball_select_pick(C, mval, &params);
}
else if (obedit->type == OB_FONT) {
- retval = ED_curve_editfont_select_pick(C, location, extend, deselect, toggle);
- if (!retval && deselect_all) {
- /* pass */
- }
- }
- if (retval) {
- WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
+ changed = ED_curve_editfont_select_pick(C, mval, &params);
}
}
else if (obact && obact->mode & OB_MODE_PARTICLE_EDIT) {
- retval = PE_mouse_particles(C, location, extend, deselect, toggle);
- if (!retval && deselect_all) {
- retval = PE_deselect_all_visible(C);
- }
+ changed = PE_mouse_particles(C, mval, &params);
}
else if (obact && BKE_paint_select_face_test(obact)) {
- retval = paintface_mouse_select(C, obact, location, extend, deselect, toggle);
- if (!retval && deselect_all) {
- retval = paintface_deselect_all_visible(C, CTX_data_active_object(C), SEL_DESELECT, true);
- }
+ changed = paintface_mouse_select(C, mval, &params, obact);
}
else if (BKE_paint_select_vert_test(obact)) {
- retval = ed_wpaint_vertex_select_pick(C, location, extend, deselect, toggle, obact);
- if (!retval && deselect_all) {
- retval = paintvert_deselect_all_visible(obact, SEL_DESELECT, false);
- if (retval) {
- paintvert_tag_select_update(C, obact);
- }
- }
+ changed = ed_wpaint_vertex_select_pick(C, mval, &params, obact);
}
else {
- retval = ed_object_select_pick(
- C, location, extend, deselect, toggle, center, enumerate, object);
- if (!retval && deselect_all) {
- if (ED_pose_object_from_context(C)) {
- retval = ED_pose_deselect_all_multi(C, SEL_DESELECT, false);
- }
- else {
- retval = ED_object_base_deselect_all(
- CTX_data_view_layer(C), CTX_wm_view3d(C), SEL_DESELECT);
- 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);
- }
- }
+ changed = ed_object_select_pick(C, mval, &params, center, enumerate, object_only);
}
+ /* Pass-through flag may be cleared, see #WM_operator_flag_only_pass_through_on_press. */
+
/* Pass-through allows tweaks
* FINISHED to signal one operator worked */
- if (retval) {
+ if (changed) {
WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
return OPERATOR_PASS_THROUGH | OPERATOR_FINISHED;
}
- return OPERATOR_PASS_THROUGH; /* nothing selected, just passthrough */
+ /* Nothing selected, just passthrough. */
+ return OPERATOR_PASS_THROUGH | OPERATOR_CANCELLED;
}
static int view3d_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c
index 92d312cebce..975f4370425 100644
--- a/source/blender/editors/transform/transform.c
+++ b/source/blender/editors/transform/transform.c
@@ -1519,12 +1519,6 @@ void saveTransform(bContext *C, TransInfo *t, wmOperator *op)
wmMsgParams_RNA msg_key_params = {{0}};
RNA_pointer_create(&t->scene->id, &RNA_ToolSettings, ts, &msg_key_params.ptr);
- _WM_MESSAGE_EXTERN_BEGIN;
- extern PropertyRNA rna_ToolSettings_use_snap;
- extern PropertyRNA rna_ToolSettings_use_snap_node;
- extern PropertyRNA rna_ToolSettings_use_snap_sequencer;
- extern PropertyRNA rna_ToolSettings_use_snap_uv;
- _WM_MESSAGE_EXTERN_END;
if (t->spacetype == SPACE_NODE) {
snap_flag_ptr = &ts->snap_flag_node;
msg_key_params.prop = &rna_ToolSettings_use_snap_node;
diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c
index 7ace6343985..bf898b9053f 100644
--- a/source/blender/editors/transform/transform_snap.c
+++ b/source/blender/editors/transform/transform_snap.c
@@ -567,10 +567,10 @@ static char snap_flag_from_spacetype(TransInfo *t)
if (t->spacetype == SPACE_NODE) {
return ts->snap_flag_node;
}
- else if (t->spacetype == SPACE_IMAGE) {
+ if (t->spacetype == SPACE_IMAGE) {
return ts->snap_uv_flag;
}
- else if (t->spacetype == SPACE_SEQ) {
+ if (t->spacetype == SPACE_SEQ) {
return ts->snap_flag_seq;
}
return ts->snap_flag;
diff --git a/source/blender/editors/transform/transform_snap_object.c b/source/blender/editors/transform/transform_snap_object.c
index 8b7133892ff..87053fe03d1 100644
--- a/source/blender/editors/transform/transform_snap_object.c
+++ b/source/blender/editors/transform/transform_snap_object.c
@@ -440,6 +440,44 @@ typedef void (*IterSnapObjsCallback)(SnapObjectContext *sctx,
bool is_object_active,
void *data);
+static bool snap_object_is_snappable(const SnapObjectContext *sctx,
+ const eSnapSelect snap_select,
+ const Base *base_act,
+ const Base *base,
+ const bool is_in_object_mode)
+{
+ if (!BASE_VISIBLE(sctx->runtime.v3d, base)) {
+ return false;
+ }
+
+ if ((snap_select == SNAP_ALL) || (base->flag_legacy & BA_TRANSFORM_LOCKED_IN_PLACE)) {
+ return true;
+ }
+
+ if (base->flag_legacy & BA_SNAP_FIX_DEPS_FIASCO) {
+ return false;
+ }
+
+ if (snap_select == SNAP_NOT_ACTIVE) {
+ return base_act == base;
+ }
+
+ if (snap_select == SNAP_NOT_SELECTED) {
+ if (is_in_object_mode) {
+ return !((base->flag & BASE_SELECTED) || (base->flag_legacy & BA_WAS_SEL));
+ }
+
+ /* What is selectable or not is part of the object and depends on the mode. */
+ return true;
+ }
+
+ if (snap_select == SNAP_SELECTABLE) {
+ return (base->flag & BASE_SELECTABLE) != 0;
+ }
+
+ return true;
+}
+
/**
* Walks through all objects in the scene to create the list of objects to snap.
*/
@@ -458,38 +496,13 @@ static void iter_snap_objects(SnapObjectContext *sctx,
return;
}
+ const bool is_in_object_mode = !base_act || base_act->object->mode == OB_MODE_OBJECT;
for (Base *base = view_layer->object_bases.first; base != NULL; base = base->next) {
- if (!BASE_VISIBLE(sctx->runtime.v3d, base)) {
- continue;
- }
-
- if ((snap_select == SNAP_ALL) || (base->flag_legacy & BA_TRANSFORM_LOCKED_IN_PLACE)) {
- /* pass */
- }
- else if (base->flag_legacy & BA_SNAP_FIX_DEPS_FIASCO) {
+ if (!snap_object_is_snappable(sctx, snap_select, base_act, base, is_in_object_mode)) {
continue;
}
const bool is_object_active = (base == base_act);
- if (snap_select == SNAP_NOT_ACTIVE) {
- if (is_object_active) {
- continue;
- }
- }
- else if (snap_select == SNAP_NOT_SELECTED) {
- if (is_object_active && base->object->mode != OB_MODE_OBJECT) {
- /* Pass. Consider the selection of elements being edited. */
- }
- else if ((base->flag & BASE_SELECTED) || (base->flag_legacy & BA_WAS_SEL)) {
- continue;
- }
- }
- else if (snap_select == SNAP_SELECTABLE) {
- if (!(base->flag & BASE_SELECTABLE)) {
- continue;
- }
- }
-
Object *obj_eval = DEG_get_evaluated_object(sctx->runtime.depsgraph, base->object);
if (obj_eval->transflag & OB_DUPLI || BKE_object_has_geometry_set_instances(obj_eval)) {
ListBase *lb = object_duplilist(sctx->runtime.depsgraph, sctx->scene, obj_eval);
diff --git a/source/blender/editors/util/select_utils.c b/source/blender/editors/util/select_utils.c
index b6893c3032a..380c7ed0e43 100644
--- a/source/blender/editors/util/select_utils.c
+++ b/source/blender/editors/util/select_utils.c
@@ -112,3 +112,17 @@ bool ED_select_similar_compare_float_tree(const KDTree_1d *tree,
return false;
}
+
+eSelectOp ED_select_op_from_booleans(const bool extend, const bool deselect, const bool toggle)
+{
+ if (extend) {
+ return SEL_OP_ADD;
+ }
+ if (deselect) {
+ return SEL_OP_SUB;
+ }
+ if (toggle) {
+ return SEL_OP_XOR;
+ }
+ return SEL_OP_SET;
+}
diff --git a/source/blender/editors/uvedit/uvedit_parametrizer.c b/source/blender/editors/uvedit/uvedit_parametrizer.c
index 3f7c7745bff..5ad326c19e5 100644
--- a/source/blender/editors/uvedit/uvedit_parametrizer.c
+++ b/source/blender/editors/uvedit/uvedit_parametrizer.c
@@ -4021,7 +4021,7 @@ static void p_smooth(PChart *chart)
PFace *f;
int j, it2, maxiter2, it;
int nedges = chart->nedges, nwheel, gridx, gridy;
- int edgesx, edgesy, nsize, esize, i, x, y, maxiter, totiter;
+ int edgesx, edgesy, nsize, esize, i, x, y, maxiter;
float minv[2], maxv[2], median, invmedian, avglen2d, avglen3d;
float center[2], dx, dy, *nodes, dlimit, d, *oldnodesx, *oldnodesy;
float *nodesx, *nodesy, *hedges, *vedges, climit, moved, padding;
@@ -4185,7 +4185,6 @@ static void p_smooth(PChart *chart)
/* smooth the grid */
maxiter = 10;
- totiter = 0;
climit = 0.00001f * nsize;
for (it = 0; it < maxiter; it++) {
@@ -4210,7 +4209,6 @@ static void p_smooth(PChart *chart)
for (it2 = 0; it2 < maxiter2; it2++) {
d = 0.0f;
- totiter += 1;
memcpy(oldnodesx, nodesx, sizeof(float) * nsize);
memcpy(oldnodesy, nodesy, sizeof(float) * nsize);
diff --git a/source/blender/editors/uvedit/uvedit_select.c b/source/blender/editors/uvedit/uvedit_select.c
index 938b798f4b6..ed4aa6985c4 100644
--- a/source/blender/editors/uvedit/uvedit_select.c
+++ b/source/blender/editors/uvedit/uvedit_select.c
@@ -2388,12 +2388,11 @@ void UV_OT_select_all(wmOperatorType *ot)
/** \name Mouse Select Operator
* \{ */
-static int uv_mouse_select_multi(bContext *C,
- Object **objects,
- uint objects_len,
- const float co[2],
- const bool extend,
- const bool deselect_all)
+static bool uv_mouse_select_multi(bContext *C,
+ Object **objects,
+ uint objects_len,
+ const float co[2],
+ const struct SelectPick_Params *params)
{
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
const ARegion *region = CTX_wm_region(C);
@@ -2477,117 +2476,145 @@ static int uv_mouse_select_multi(bContext *C,
}
}
- if (!found_item) {
- if (deselect_all) {
- uv_select_all_perform_multi(scene, objects, objects_len, SEL_DESELECT);
+ bool found = found_item;
+ bool changed = false;
+
+ bool is_selected = false;
+ if (found) {
+ Object *obedit = hit.ob;
+ BMEditMesh *em = BKE_editmesh_from_object(obedit);
+ const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
+ if (selectmode == UV_SELECT_FACE) {
+ is_selected = uvedit_face_select_test(scene, hit.efa, cd_loop_uv_offset);
+ }
+ else if (selectmode == UV_SELECT_EDGE) {
+ is_selected = uvedit_edge_select_test(scene, hit.l, cd_loop_uv_offset);
+ }
+ else { /* Vertex or island. */
+ is_selected = uvedit_uv_select_test(scene, hit.l, cd_loop_uv_offset);
+ }
+ }
+ if (params->sel_op == SEL_OP_SET) {
+ if ((found && params->select_passthrough) && is_selected) {
+ found = false;
+ }
+ else if (found || params->deselect_all) {
+ /* Deselect everything. */
+ uv_select_all_perform_multi(scene, objects, objects_len, SEL_DESELECT);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
uv_select_tag_update_for_object(depsgraph, ts, obedit);
}
-
- return OPERATOR_PASS_THROUGH | OPERATOR_FINISHED;
+ changed = true;
}
- return OPERATOR_CANCELLED;
}
- Object *obedit = hit.ob;
- BMEditMesh *em = BKE_editmesh_from_object(obedit);
- const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
+ if (found) {
+ Object *obedit = hit.ob;
+ BMEditMesh *em = BKE_editmesh_from_object(obedit);
+ const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
- /* do selection */
- if (selectmode == UV_SELECT_ISLAND) {
- if (!extend) {
- uv_select_all_perform_multi_ex(scene, objects, objects_len, SEL_DESELECT, obedit);
- }
- /* Current behavior of 'extend'
- * is actually toggling, so pass extend flag as 'toggle' here */
- uv_select_linked_multi(scene, objects, objects_len, &hit, false, false, extend, false);
- }
- else if (extend) {
- bool select = true;
- if (selectmode == UV_SELECT_VERTEX) {
- /* (de)select uv vertex */
- select = !uvedit_uv_select_test(scene, hit.l, cd_loop_uv_offset);
- uvedit_uv_select_set_with_sticky(scene, em, hit.l, select, true, cd_loop_uv_offset);
- flush = 1;
- }
- else if (selectmode == UV_SELECT_EDGE) {
- /* (de)select edge */
- select = !(uvedit_edge_select_test(scene, hit.l, cd_loop_uv_offset));
- uvedit_edge_select_set_with_sticky(scene, em, hit.l, select, true, cd_loop_uv_offset);
- flush = 1;
- }
- else if (selectmode == UV_SELECT_FACE) {
- /* (de)select face */
- select = !(uvedit_face_select_test(scene, hit.efa, cd_loop_uv_offset));
- uvedit_face_select_set_with_sticky(scene, em, hit.efa, select, true, cd_loop_uv_offset);
- flush = -1;
+ if (selectmode == UV_SELECT_ISLAND) {
+ const bool extend = params->sel_op == SEL_OP_ADD;
+ const bool deselect = params->sel_op == SEL_OP_SUB;
+ const bool toggle = params->sel_op == SEL_OP_XOR;
+ /* Current behavior of 'extend'
+ * is actually toggling, so pass extend flag as 'toggle' here */
+ uv_select_linked_multi(scene, objects, objects_len, &hit, extend, deselect, toggle, false);
+ /* TODO: check if this actually changed. */
+ changed = true;
}
+ else {
+ BLI_assert(ELEM(selectmode, UV_SELECT_VERTEX, UV_SELECT_EDGE, UV_SELECT_FACE));
+ bool select_value = false;
+ switch (params->sel_op) {
+ case SEL_OP_ADD: {
+ select_value = true;
+ break;
+ }
+ case SEL_OP_SUB: {
+ select_value = false;
+ break;
+ }
+ case SEL_OP_XOR: {
+ select_value = !is_selected;
+ break;
+ }
+ case SEL_OP_SET: {
+ /* Deselect has already been performed. */
+ select_value = true;
+ break;
+ }
+ case SEL_OP_AND: {
+ BLI_assert_unreachable(); /* Doesn't make sense for picking. */
+ break;
+ }
+ }
- /* de-selecting an edge may deselect a face too - validate */
- if (ts->uv_flag & UV_SYNC_SELECTION) {
- if (select == false) {
- BM_select_history_validate(em->bm);
+ if (selectmode == UV_SELECT_FACE) {
+ uvedit_face_select_set_with_sticky(
+ scene, em, hit.efa, select_value, true, cd_loop_uv_offset);
+ flush = 1;
+ }
+ else if (selectmode == UV_SELECT_EDGE) {
+ uvedit_edge_select_set_with_sticky(
+ scene, em, hit.l, select_value, true, cd_loop_uv_offset);
+ flush = 1;
+ }
+ else if (selectmode == UV_SELECT_VERTEX) {
+ uvedit_uv_select_set_with_sticky(scene, em, hit.l, select_value, true, cd_loop_uv_offset);
+ flush = 1;
+ }
+ else {
+ BLI_assert_unreachable();
}
- }
- /* (de)select sticky uv nodes */
- if (sticky != SI_STICKY_DISABLE) {
- flush = select ? 1 : -1;
- }
- }
- else {
- const bool select = true;
- /* deselect all */
- uv_select_all_perform_multi(scene, objects, objects_len, SEL_DESELECT);
+ /* De-selecting an edge may deselect a face too - validate. */
+ if (ts->uv_flag & UV_SYNC_SELECTION) {
+ if (select_value == false) {
+ BM_select_history_validate(em->bm);
+ }
+ }
- if (selectmode == UV_SELECT_VERTEX) {
- /* select vertex */
- uvedit_uv_select_set_with_sticky(scene, em, hit.l, select, true, cd_loop_uv_offset);
- flush = 1;
- }
- else if (selectmode == UV_SELECT_EDGE) {
- /* select edge */
- uvedit_edge_select_set_with_sticky(scene, em, hit.l, select, true, cd_loop_uv_offset);
- flush = 1;
- }
- else if (selectmode == UV_SELECT_FACE) {
- /* select face */
- uvedit_face_select_set_with_sticky(scene, em, hit.efa, select, true, cd_loop_uv_offset);
- flush = 1;
+ /* (de)select sticky UV nodes. */
+ if (sticky != SI_STICKY_DISABLE) {
+ flush = select_value ? 1 : -1;
+ }
+
+ changed = true;
}
- }
- if (ts->uv_flag & UV_SYNC_SELECTION) {
- if (flush != 0) {
- EDBM_selectmode_flush(em);
+ if (ts->uv_flag & UV_SYNC_SELECTION) {
+ if (flush != 0) {
+ EDBM_selectmode_flush(em);
+ }
+ }
+ else {
+ /* Setting the selection implies a single element, which doesn't need to be flushed. */
+ if (params->sel_op != SEL_OP_SET) {
+ ED_uvedit_selectmode_flush(scene, em);
+ }
}
- }
- /* #extend=false implies single vertex selection, which doesn't need to be flushed. */
- else if (extend) {
- ED_uvedit_selectmode_flush(scene, em);
}
- for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
- Object *obiter = objects[ob_index];
- uv_select_tag_update_for_object(depsgraph, ts, obiter);
+ if (changed && found) {
+ /* Only update the `hit` object as de-selecting all will have refreshed the others. */
+ Object *obedit = hit.ob;
+ uv_select_tag_update_for_object(depsgraph, ts, obedit);
}
- return OPERATOR_PASS_THROUGH | OPERATOR_FINISHED;
+ return changed || found;
}
-static int uv_mouse_select(bContext *C,
- const float co[2],
- const bool extend,
- const bool deselect_all)
+static bool uv_mouse_select(bContext *C, const float co[2], const struct SelectPick_Params *params)
{
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
view_layer, ((View3D *)NULL), &objects_len);
- int ret = uv_mouse_select_multi(C, objects, objects_len, co, extend, deselect_all);
+ bool changed = uv_mouse_select_multi(C, objects, objects_len, co, params);
MEM_freeN(objects);
- return ret;
+ return changed;
}
static int uv_select_exec(bContext *C, wmOperator *op)
@@ -2595,10 +2622,20 @@ static int uv_select_exec(bContext *C, wmOperator *op)
float co[2];
RNA_float_get_array(op->ptr, "location", co);
- const bool extend = RNA_boolean_get(op->ptr, "extend");
- const bool deselect_all = RNA_boolean_get(op->ptr, "deselect_all");
+ const struct SelectPick_Params params = {
+ .sel_op = ED_select_op_from_booleans(RNA_boolean_get(op->ptr, "extend"),
+ RNA_boolean_get(op->ptr, "deselect"),
+ RNA_boolean_get(op->ptr, "toggle")),
+ .deselect_all = RNA_boolean_get(op->ptr, "deselect_all"),
+ .select_passthrough = RNA_boolean_get(op->ptr, "select_passthrough"),
+ };
- return uv_mouse_select(C, co, extend, deselect_all);
+ const bool changed = uv_mouse_select(C, co, &params);
+
+ if (changed) {
+ return OPERATOR_FINISHED | OPERATOR_PASS_THROUGH;
+ }
+ return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
}
static int uv_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
@@ -2629,18 +2666,8 @@ void UV_OT_select(wmOperatorType *ot)
/* properties */
PropertyRNA *prop;
- prop = RNA_def_boolean(ot->srna,
- "extend",
- 0,
- "Extend",
- "Extend selection rather than clearing the existing selection");
- RNA_def_property_flag(prop, PROP_SKIP_SAVE);
- prop = RNA_def_boolean(ot->srna,
- "deselect_all",
- false,
- "Deselect On Nothing",
- "Deselect all when nothing under the cursor");
- RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+
+ WM_operator_properties_mouse_select(ot);
prop = RNA_def_float_vector(
ot->srna,
diff --git a/source/blender/editors/uvedit/uvedit_unwrap_ops.c b/source/blender/editors/uvedit/uvedit_unwrap_ops.c
index 3ed99052753..63300656fda 100644
--- a/source/blender/editors/uvedit/uvedit_unwrap_ops.c
+++ b/source/blender/editors/uvedit/uvedit_unwrap_ops.c
@@ -2969,6 +2969,7 @@ void ED_uvedit_add_simple_uvs(Main *bmain, const Scene *scene, Object *ob)
me,
(&(struct BMeshFromMeshParams){
.calc_face_normal = true,
+ .calc_vert_normal = true,
}));
/* select all uv loops first - pack parameters needs this to make sure charts are registered */
ED_uvedit_select_all(bm);