diff options
Diffstat (limited to 'source/blender/editors')
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, ¶ms); 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, ¶ms)) { /* 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 ®ion, + 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(®ion, 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(®ion, p1_cu, p1_re, projection.values); + ED_view3d_project_float_v2_m4(®ion, 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, + ¢er_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 = ®ion->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 = ®ion->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(®ion->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, ®ion->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 = ®ion->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(®ion->v2d.mask) - (int)restrict_column_width + 1; + if (right_column_width > 0.0f) { + int mask_x = BLI_rcti_size_x(®ion->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 = ®ion->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, ¶ms); } 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, ¶ms); } /* 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, ¶ms); } 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, ¶ms); } - - 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, ¶ms); } } 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, ¶ms); } 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, ¶ms); } 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, ¶ms); } 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, ¶ms); } } 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, ¶ms); } 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, ¶ms, 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, ¶ms, 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, ¶ms, 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, ¶ms); + + 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); |