diff options
Diffstat (limited to 'source/blender/editors')
35 files changed, 972 insertions, 366 deletions
diff --git a/source/blender/editors/animation/anim_filter.c b/source/blender/editors/animation/anim_filter.c index 38a19e85661..c054691ca38 100644 --- a/source/blender/editors/animation/anim_filter.c +++ b/source/blender/editors/animation/anim_filter.c @@ -1844,11 +1844,15 @@ static size_t animdata_filter_gpencil(bAnimContext *ac, ViewLayer *view_layer = (ViewLayer *)ac->view_layer; Base *base; - /* Active scene's GPencil block first - No parent item needed... */ - if (scene->gpd) { - items += animdata_filter_gpencil_data(anim_data, ads, scene->gpd, filter_mode); + /* Include all annotation datablocks. */ + if (((ads->filterflag & ADS_FILTER_ONLYSEL) == 0) || + (ads->filterflag & ADS_FILTER_INCL_HIDDEN)) { + LISTBASE_FOREACH (bGPdata *, gpd, &ac->bmain->gpencils) { + if (gpd->flag & GP_DATA_ANNOTATIONS) { + items += animdata_filter_gpencil_data(anim_data, ads, gpd, filter_mode); + } + } } - /* Objects in the scene */ for (base = view_layer->object_bases.first; base; base = base->next) { /* Only consider this object if it has got some GP data (saving on all the other tests) */ diff --git a/source/blender/editors/armature/armature_add.c b/source/blender/editors/armature/armature_add.c index f6875a6e158..dfb274fdefe 100644 --- a/source/blender/editors/armature/armature_add.c +++ b/source/blender/editors/armature/armature_add.c @@ -22,6 +22,7 @@ * \ingroup edarmature */ +#include "DNA_anim_types.h" #include "DNA_armature_types.h" #include "DNA_constraint_types.h" #include "DNA_object_types.h" @@ -38,8 +39,12 @@ #include "BKE_constraint.h" #include "BKE_context.h" #include "BKE_deform.h" +#include "BKE_fcurve.h" #include "BKE_idprop.h" #include "BKE_layer.h" +#include "BKE_lib_id.h" +#include "BKE_library.h" +#include "BKE_main.h" #include "RNA_access.h" #include "RNA_define.h" @@ -375,25 +380,20 @@ void postEditBoneDuplicate(struct ListBase *editbones, Object *ob) BLI_ghash_free(name_map, NULL, NULL); } -/* - * Note: When duplicating cross objects, editbones here is the list of bones - * from the SOURCE object but ob is the DESTINATION object - * */ -void updateDuplicateSubtargetObjects(EditBone *dupBone, +static void updateDuplicateSubtarget(EditBone *dup_bone, ListBase *editbones, - Object *src_ob, - Object *dst_ob) + Object *ob, + bool lookup_mirror_subtarget) { - /* If an edit bone has been duplicated, lets - * update it's constraints if the subtarget - * they point to has also been duplicated + /* If an edit bone has been duplicated, lets update it's constraints if the + * subtarget they point to has also been duplicated. */ EditBone *oldtarget, *newtarget; bPoseChannel *pchan; bConstraint *curcon; ListBase *conlist; - if ((pchan = BKE_pose_channel_verify(dst_ob->pose, dupBone->name))) { + if ((pchan = BKE_pose_channel_verify(ob->pose, dup_bone->name))) { if ((conlist = &pchan->constraints)) { for (curcon = conlist->first; curcon; curcon = curcon->next) { /* does this constraint have a subtarget in @@ -407,8 +407,7 @@ void updateDuplicateSubtargetObjects(EditBone *dupBone, cti->get_constraint_targets(curcon, &targets); for (ct = targets.first; ct; ct = ct->next) { - if ((ct->tar == src_ob) && (ct->subtarget[0])) { - ct->tar = dst_ob; /* update target */ + if ((ct->tar == ob) && (ct->subtarget[0])) { oldtarget = get_named_editbone(editbones, ct->subtarget); if (oldtarget) { /* was the subtarget bone duplicated too? If @@ -419,6 +418,17 @@ void updateDuplicateSubtargetObjects(EditBone *dupBone, newtarget = oldtarget->temp.ebone; BLI_strncpy(ct->subtarget, newtarget->name, sizeof(ct->subtarget)); } + else if (lookup_mirror_subtarget) { + /* The subtarget was not selected for duplication, try to see if a mirror bone of + * the current target exists */ + char name_flip[MAXBONENAME]; + + BLI_string_flip_side_name(name_flip, oldtarget->name, false, sizeof(name_flip)); + newtarget = get_named_editbone(editbones, name_flip); + if (newtarget) { + BLI_strncpy(ct->subtarget, newtarget->name, sizeof(ct->subtarget)); + } + } } } } @@ -432,32 +442,434 @@ void updateDuplicateSubtargetObjects(EditBone *dupBone, } } -void updateDuplicateSubtarget(EditBone *dupBone, ListBase *editbones, Object *ob) +static void updateDuplicateActionConstraintSettings(EditBone *dup_bone, + EditBone *orig_bone, + Object *ob, + bConstraint *curcon) { - updateDuplicateSubtargetObjects(dupBone, editbones, ob, ob); + bActionConstraint *act_con = (bActionConstraint *)curcon->data; + bAction *act = (bAction *)act_con->act; + + float mat[4][4]; + + unit_m4(mat); + bPoseChannel *target_pchan = BKE_pose_channel_find_name(ob->pose, act_con->subtarget); + BKE_constraint_mat_convertspace( + ob, target_pchan, mat, curcon->tarspace, CONSTRAINT_SPACE_LOCAL, false); + + float max_axis_val = 0; + int max_axis = 0; + /* Which axis represents X now. IE, which axis defines the mirror plane. */ + for (int i = 0; i < 3; i++) { + float cur_val = fabsf(mat[0][i]); + if (cur_val > max_axis_val) { + max_axis = i; + max_axis_val = cur_val; + } + } + + /* data->type is mapped as follows for backwards compatibility: + * 00,01,02 - rotation (it used to be like this) + * 10,11,12 - scaling + * 20,21,22 - location + */ + /* Mirror the target range */ + if (act_con->type < 10 && act_con->type != max_axis) { + /* Y or Z rotation */ + act_con->min = -act_con->min; + act_con->max = -act_con->max; + } + else if (act_con->type == max_axis + 10) { + /* X scaling */ + } + else if (act_con->type == max_axis + 20) { + /* X location */ + float imat[4][4]; + + invert_m4_m4(imat, mat); + + float min_vec[3], max_vec[3]; + + zero_v3(min_vec); + zero_v3(max_vec); + + min_vec[0] = act_con->min; + max_vec[0] = act_con->max; + + /* convert values into local object space */ + mul_m4_v3(mat, min_vec); + mul_m4_v3(mat, max_vec); + + min_vec[0] *= -1; + max_vec[0] *= -1; + + /* convert back to the settings space */ + mul_m4_v3(imat, min_vec); + mul_m4_v3(imat, max_vec); + + act_con->min = min_vec[0]; + act_con->max = max_vec[0]; + } + + /* See if there is any channels that uses this bone */ + ListBase ani_curves; + BLI_listbase_clear(&ani_curves); + if (list_find_data_fcurves(&ani_curves, &act->curves, "pose.bones[", orig_bone->name)) { + /* Create a copy and mirror the animation */ + for (LinkData *ld = ani_curves.first; ld; ld = ld->next) { + FCurve *old_curve = ld->data; + FCurve *new_curve = copy_fcurve(old_curve); + bActionGroup *agrp; + + char *old_path = new_curve->rna_path; + + new_curve->rna_path = BLI_str_replaceN(old_path, orig_bone->name, dup_bone->name); + MEM_freeN(old_path); + + /* Flip the animation */ + int i; + BezTriple *bezt; + for (i = 0, bezt = new_curve->bezt; i < new_curve->totvert; i++, bezt++) { + const size_t slength = strlen(new_curve->rna_path); + bool flip = false; + if (BLI_strn_endswith(new_curve->rna_path, "location", slength) && + new_curve->array_index == 0) { + flip = true; + } + else if (BLI_strn_endswith(new_curve->rna_path, "rotation_quaternion", slength) && + ELEM(new_curve->array_index, 2, 3)) { + flip = true; + } + else if (BLI_strn_endswith(new_curve->rna_path, "rotation_euler", slength) && + ELEM(new_curve->array_index, 1, 2)) { + flip = true; + } + else if (BLI_strn_endswith(new_curve->rna_path, "rotation_axis_angle", slength) && + ELEM(new_curve->array_index, 2, 3)) { + flip = true; + } + + if (flip) { + bezt->vec[0][1] *= -1; + bezt->vec[1][1] *= -1; + bezt->vec[2][1] *= -1; + } + } + + /* Make sure that a action group name for the new bone exists */ + agrp = BKE_action_group_find_name(act, dup_bone->name); + + if (agrp == NULL) { + agrp = action_groups_add_new(act, dup_bone->name); + } + BLI_assert(agrp != NULL); + action_groups_add_channel(act, agrp, new_curve); + } + } + BLI_freelistN(&ani_curves); + + /* Make deps graph aware of our changes */ + DEG_id_tag_update(&act->id, ID_RECALC_ANIMATION_NO_FLUSH); } -EditBone *duplicateEditBoneObjects( - EditBone *curBone, const char *name, ListBase *editbones, Object *src_ob, Object *dst_ob) +static void updateDuplicateKinematicConstraintSettings(bConstraint *curcon) { - EditBone *eBone = MEM_mallocN(sizeof(EditBone), "addup_editbone"); + /* IK constraint */ + bKinematicConstraint *ik = (bKinematicConstraint *)curcon->data; + ik->poleangle = -M_PI - ik->poleangle; + /* Wrap the angle to the +/-180.0f range (default soft limit of the input boxes). */ + ik->poleangle = angle_wrap_rad(ik->poleangle); +} - /* Copy data from old bone to new bone */ - memcpy(eBone, curBone, sizeof(EditBone)); +static void updateDuplicateLocRotConstraintSettings(Object *ob, + bPoseChannel *pchan, + bConstraint *curcon) +{ + /* This code assumes that bRotLimitConstraint and bLocLimitConstraint have the same fields in + * the same memory locations. */ + BLI_assert(sizeof(bLocLimitConstraint) == sizeof(bRotLimitConstraint)); - curBone->temp.ebone = eBone; - eBone->temp.ebone = curBone; + bRotLimitConstraint *limit = (bRotLimitConstraint *)curcon->data; + float local_mat[4][4], imat[4][4]; - if (name != NULL) { - BLI_strncpy(eBone->name, name, sizeof(eBone->name)); + float min_vec[3], max_vec[3]; + + min_vec[0] = limit->xmin; + min_vec[1] = limit->ymin; + min_vec[2] = limit->zmin; + + max_vec[0] = limit->xmax; + max_vec[1] = limit->ymax; + max_vec[2] = limit->zmax; + + unit_m4(local_mat); + + BKE_constraint_mat_convertspace( + ob, pchan, local_mat, curcon->ownspace, CONSTRAINT_SPACE_LOCAL, false); + + if (curcon->type == CONSTRAINT_TYPE_ROTLIMIT) { + /* Zero out any location translation */ + local_mat[3][0] = local_mat[3][1] = local_mat[3][2] = 0; + } + + invert_m4_m4(imat, local_mat); + /* convert values into local object space */ + mul_m4_v3(local_mat, min_vec); + mul_m4_v3(local_mat, max_vec); + + if (curcon->type == CONSTRAINT_TYPE_ROTLIMIT) { + float min_copy[3]; + + copy_v3_v3(min_copy, min_vec); + + min_vec[1] = max_vec[1] * -1; + min_vec[2] = max_vec[2] * -1; + + max_vec[1] = min_copy[1] * -1; + max_vec[2] = min_copy[2] * -1; + } + else { + float min_x_copy = min_vec[0]; + + min_vec[0] = max_vec[0] * -1; + max_vec[0] = min_x_copy * -1; + } + + /* convert back to the settings space */ + mul_m4_v3(imat, min_vec); + mul_m4_v3(imat, max_vec); + + limit->xmin = min_vec[0]; + limit->ymin = min_vec[1]; + limit->zmin = min_vec[2]; + + limit->xmax = max_vec[0]; + limit->ymax = max_vec[1]; + limit->zmax = max_vec[2]; +} + +static void updateDuplicateTransformConstraintSettings(Object *ob, + bPoseChannel *pchan, + bConstraint *curcon) +{ + bTransformConstraint *trans = (bTransformConstraint *)curcon->data; + + float target_mat[4][4], own_mat[4][4], imat[4][4]; + + unit_m4(own_mat); + BKE_constraint_mat_convertspace( + ob, pchan, own_mat, curcon->ownspace, CONSTRAINT_SPACE_LOCAL, false); + + /* ###Source map mirroring### */ + float old_min, old_max; + + /* Source location */ + invert_m4_m4(imat, own_mat); + + /* convert values into local object space */ + mul_m4_v3(own_mat, trans->from_min); + mul_m4_v3(own_mat, trans->from_max); + + old_min = trans->from_min[0]; + old_max = trans->from_max[0]; + + trans->from_min[0] = -old_max; + trans->from_max[0] = -old_min; + + /* convert back to the settings space */ + mul_m4_v3(imat, trans->from_min); + mul_m4_v3(imat, trans->from_max); + + /* Source rotation */ + + /* Zero out any location translation */ + own_mat[3][0] = own_mat[3][1] = own_mat[3][2] = 0; + + invert_m4_m4(imat, own_mat); + + /* convert values into local object space */ + mul_m4_v3(own_mat, trans->from_min_rot); + mul_m4_v3(own_mat, trans->from_max_rot); + + old_min = trans->from_min_rot[1]; + old_max = trans->from_max_rot[1]; + + trans->from_min_rot[1] = old_max * -1; + trans->from_max_rot[1] = old_min * -1; + + old_min = trans->from_min_rot[2]; + old_max = trans->from_max_rot[2]; + + trans->from_min_rot[2] = old_max * -1; + trans->from_max_rot[2] = old_min * -1; + + /* convert back to the settings space */ + mul_m4_v3(imat, trans->from_min_rot); + mul_m4_v3(imat, trans->from_max_rot); + + /* Source scale does not require any mirroring */ + + /* ###Destination map mirroring### */ + float temp_vec[3]; + float imat_rot[4][4]; + + bPoseChannel *target_pchan = BKE_pose_channel_find_name(ob->pose, trans->subtarget); + unit_m4(target_mat); + BKE_constraint_mat_convertspace( + ob, target_pchan, target_mat, curcon->tarspace, CONSTRAINT_SPACE_LOCAL, false); + + invert_m4_m4(imat, target_mat); + /* convert values into local object space */ + mul_m4_v3(target_mat, trans->to_min); + mul_m4_v3(target_mat, trans->to_max); + mul_m4_v3(target_mat, trans->to_min_scale); + mul_m4_v3(target_mat, trans->to_max_scale); + + /* Zero out any location translation */ + target_mat[3][0] = target_mat[3][1] = target_mat[3][2] = 0; + invert_m4_m4(imat_rot, target_mat); + + mul_m4_v3(target_mat, trans->to_min_rot); + mul_m4_v3(target_mat, trans->to_max_rot); + + /* TODO(sebpa): This does not support euler order, but doing so will make this way more complex. + * For now we have decided to not support all corner cases and advanced setups. */ + + /* Helper variables to denote the axis in trans->map */ + const char X = 0; + const char Y = 1; + const char Z = 2; + + switch (trans->to) { + case TRANS_SCALE: + copy_v3_v3(temp_vec, trans->to_max_scale); + + for (int i = 0; i < 3; i++) { + if ((trans->from == TRANS_LOCATION && trans->map[i] == X) || + (trans->from == TRANS_ROTATION && trans->map[i] != X)) { + /* X Loc to X/Y/Z Scale: Min/Max Flipped */ + /* Y Rot to X/Y/Z Scale: Min/Max Flipped */ + /* Z Rot to X/Y/Z Scale: Min/Max Flipped */ + trans->to_max_scale[i] = trans->to_min_scale[i]; + trans->to_min_scale[i] = temp_vec[i]; + } + } + break; + case TRANS_LOCATION: + /* Invert the X location */ + trans->to_min[0] *= -1; + trans->to_max[0] *= -1; + + copy_v3_v3(temp_vec, trans->to_max); + + for (int i = 0; i < 3; i++) { + if ((trans->from == TRANS_LOCATION && trans->map[i] == X) || + (trans->from == TRANS_ROTATION && trans->map[i] != X)) { + /* X Loc to X/Y/Z Loc: Min/Max Flipped (and Inverted) + * Y Rot to X/Y/Z Loc: Min/Max Flipped + * Z Rot to X/Y/Z Loc: Min/Max Flipped */ + trans->to_max[i] = trans->to_min[i]; + trans->to_min[i] = temp_vec[i]; + } + } + break; + case TRANS_ROTATION: + /* Invert the Z rotation */ + trans->to_min_rot[2] *= -1; + trans->to_max_rot[2] *= -1; + + if ((trans->from == TRANS_LOCATION && trans->map[1] != X) || + (trans->from == TRANS_ROTATION && trans->map[1] != Y) || trans->from == TRANS_SCALE) { + /* Invert the Y rotation */ + trans->to_min_rot[1] *= -1; + trans->to_max_rot[1] *= -1; + } + + copy_v3_v3(temp_vec, trans->to_max_rot); + + for (int i = 0; i < 3; i++) { + if ((trans->from == TRANS_LOCATION && trans->map[i] == X && i != 1) || + (trans->from == TRANS_ROTATION && trans->map[i] == Y && i != 1) || + (trans->from == TRANS_ROTATION && trans->map[i] == Z)) { + /* X Loc to X/Z Rot: Flipped + * Y Rot to X/Z Rot: Flipped + * Z Rot to X/Y/Z rot: Flipped */ + trans->to_max_rot[i] = trans->to_min_rot[i]; + trans->to_min_rot[i] = temp_vec[i]; + } + } + break; + } + /* convert back to the settings space */ + mul_m4_v3(imat, trans->to_min); + mul_m4_v3(imat, trans->to_max); + mul_m4_v3(imat_rot, trans->to_min_rot); + mul_m4_v3(imat_rot, trans->to_max_rot); + mul_m4_v3(imat, trans->to_min_scale); + mul_m4_v3(imat, trans->to_max_scale); +} + +static void updateDuplicateConstraintSettings(EditBone *dup_bone, EditBone *orig_bone, Object *ob) +{ + /* If an edit bone has been duplicated, lets update it's constraints if the + * subtarget they point to has also been duplicated. + */ + bPoseChannel *pchan; + bConstraint *curcon; + ListBase *conlist; + + if ((pchan = BKE_pose_channel_verify(ob->pose, dup_bone->name)) == NULL || + (conlist = &pchan->constraints) == NULL) { + return; + } + + for (curcon = conlist->first; curcon; curcon = curcon->next) { + switch (curcon->type) { + case CONSTRAINT_TYPE_ACTION: + updateDuplicateActionConstraintSettings(dup_bone, orig_bone, ob, curcon); + break; + case CONSTRAINT_TYPE_KINEMATIC: + updateDuplicateKinematicConstraintSettings(curcon); + break; + case CONSTRAINT_TYPE_LOCLIMIT: + case CONSTRAINT_TYPE_ROTLIMIT: + updateDuplicateLocRotConstraintSettings(ob, pchan, curcon); + break; + case CONSTRAINT_TYPE_TRANSFORM: + updateDuplicateTransformConstraintSettings(ob, pchan, curcon); + break; + } + } +} + +static void updateDuplicateCustomBoneShapes(bContext *C, EditBone *dup_bone, Object *ob) +{ + if (ob->pose == NULL) { + return; } + bPoseChannel *pchan; + pchan = BKE_pose_channel_verify(ob->pose, dup_bone->name); - ED_armature_ebone_unique_name(editbones, eBone->name, NULL); - BLI_addtail(editbones, eBone); + if (pchan->custom != NULL) { + Main *bmain = CTX_data_main(C); + char name_flip[MAX_ID_NAME - 2]; + /* Skip the first two chars in the object name as those are used to store object type */ + BLI_string_flip_side_name(name_flip, pchan->custom->id.name + 2, false, sizeof(name_flip)); + Object *shape_ob = (Object *)BKE_libblock_find_name(bmain, ID_OB, name_flip); + + if (shape_ob != NULL) { + /* A flipped shape object exists, use it! */ + pchan->custom = shape_ob; + } + } +} + +static void copy_pchan(EditBone *src_bone, EditBone *dst_bone, Object *src_ob, Object *dst_ob) +{ /* copy the ID property */ - if (curBone->prop) { - eBone->prop = IDP_CopyProperty(curBone->prop); + if (src_bone->prop) { + dst_bone->prop = IDP_CopyProperty(src_bone->prop); } /* Lets duplicate the list of constraints that the @@ -466,25 +878,46 @@ EditBone *duplicateEditBoneObjects( if (src_ob->pose) { bPoseChannel *chanold, *channew; - chanold = BKE_pose_channel_verify(src_ob->pose, curBone->name); + chanold = BKE_pose_channel_verify(src_ob->pose, src_bone->name); if (chanold) { /* WARNING: this creates a new posechannel, but there will not be an attached bone * yet as the new bones created here are still 'EditBones' not 'Bones'. */ - channew = BKE_pose_channel_verify(dst_ob->pose, eBone->name); + channew = BKE_pose_channel_verify(dst_ob->pose, dst_bone->name); if (channew) { BKE_pose_channel_copy_data(channew, chanold); } } } +} - return eBone; +EditBone *duplicateEditBoneObjects( + EditBone *cur_bone, const char *name, ListBase *editbones, Object *src_ob, Object *dst_ob) +{ + EditBone *e_bone = MEM_mallocN(sizeof(EditBone), "addup_editbone"); + + /* Copy data from old bone to new bone */ + memcpy(e_bone, cur_bone, sizeof(EditBone)); + + cur_bone->temp.ebone = e_bone; + e_bone->temp.ebone = cur_bone; + + if (name != NULL) { + BLI_strncpy(e_bone->name, name, sizeof(e_bone->name)); + } + + ED_armature_ebone_unique_name(editbones, e_bone->name, NULL); + BLI_addtail(editbones, e_bone); + + copy_pchan(cur_bone, e_bone, src_ob, dst_ob); + + return e_bone; } -EditBone *duplicateEditBone(EditBone *curBone, const char *name, ListBase *editbones, Object *ob) +EditBone *duplicateEditBone(EditBone *cur_bone, const char *name, ListBase *editbones, Object *ob) { - return duplicateEditBoneObjects(curBone, name, editbones, ob, ob); + return duplicateEditBoneObjects(cur_bone, name, editbones, ob, ob); } static int armature_duplicate_selected_exec(bContext *C, wmOperator *op) @@ -538,8 +971,8 @@ static int armature_duplicate_selected_exec(bContext *C, wmOperator *op) BLI_string_flip_side_name( new_bone_name_buff, ebone_iter->name, false, sizeof(new_bone_name_buff)); - /* Only use flipped name if not yet in use. Otherwise we'd get again inconsistent namings - * (different numbers), better keep default behavior in this case. */ + /* Only use flipped name if not yet in use. Otherwise we'd get again inconsistent + * namings (different numbers), better keep default behavior in this case. */ if (ED_armature_ebone_find_name(arm->edbo, new_bone_name_buff) == NULL) { new_bone_name = new_bone_name_buff; } @@ -567,13 +1000,13 @@ static int armature_duplicate_selected_exec(bContext *C, wmOperator *op) } else if (ebone_iter->parent->temp.ebone) { /* If this bone has a parent that was duplicated, - * Set the duplicate->parent to the curBone->parent->temp + * Set the duplicate->parent to the cur_bone->parent->temp */ ebone->parent = ebone_iter->parent->temp.ebone; } else { /* If this bone has a parent that IS not selected, - * Set the duplicate->parent to the curBone->parent + * Set the duplicate->parent to the cur_bone->parent */ ebone->parent = (EditBone *)ebone_iter->parent; ebone->flag &= ~BONE_CONNECTED; @@ -590,7 +1023,7 @@ static int armature_duplicate_selected_exec(bContext *C, wmOperator *op) /* Lets try to fix any constraint subtargets that might * have been duplicated */ - updateDuplicateSubtarget(ebone, arm->edbo, ob); + updateDuplicateSubtarget(ebone, arm->edbo, ob, false); } } @@ -745,9 +1178,21 @@ static int armature_symmetrize_exec(bContext *C, wmOperator *op) /* Find the selected bones and duplicate them as needed, with mirrored name. */ for (ebone_iter = arm->edbo->first; ebone_iter && ebone_iter != ebone_first_dupe; ebone_iter = ebone_iter->next) { - if (EBONE_VISIBLE(arm, ebone_iter) && (ebone_iter->flag & BONE_SELECTED) && - /* will be set if the mirror bone already exists (no need to make a new one) */ - (ebone_iter->temp.ebone == NULL)) { + if (EBONE_VISIBLE(arm, ebone_iter) && (ebone_iter->flag & BONE_SELECTED)) { + if (ebone_iter->temp.ebone != NULL) { + /* This will be set if the mirror bone already exists (no need to make a new one) + * but we do need to make sure that the 'pchan' settings (constraints etc) + * is synchronized. */ + bPoseChannel *pchan; + /* Make sure we clean up the old data before overwriting it */ + pchan = BKE_pose_channel_verify(obedit->pose, ebone_iter->temp.ebone->name); + BKE_pose_channel_free(pchan); + /* Sync pchan data */ + copy_pchan(ebone_iter, ebone_iter->temp.ebone, obedit, obedit); + /* Sync scale mode */ + ebone_iter->temp.ebone->inherit_scale_mode = ebone_iter->inherit_scale_mode; + continue; + } char name_flip[MAXBONENAME]; BLI_string_flip_side_name(name_flip, ebone_iter->name, false, sizeof(name_flip)); @@ -793,7 +1238,12 @@ static int armature_symmetrize_exec(bContext *C, wmOperator *op) * then we can assume the parent has no L/R but is a center bone. * So just use the same parent for both. */ - ebone->flag &= ~BONE_CONNECTED; + + if (ebone->head[axis] != 0.0f) { + /* The mirrored bone doesn't start on the mirror axis, so assume that this one should + * not be connected to the old parent */ + ebone->flag &= ~BONE_CONNECTED; + } } ebone->parent = ebone_parent; @@ -803,10 +1253,19 @@ static int armature_symmetrize_exec(bContext *C, wmOperator *op) ebone->bbone_prev = get_symmetrized_bone(arm, ebone_iter->bbone_prev); ebone->bbone_next = get_symmetrized_bone(arm, ebone_iter->bbone_next); + /* Sync bbone handle types */ + ebone->bbone_prev_type = ebone_iter->bbone_prev_type; + ebone->bbone_next_type = ebone_iter->bbone_next_type; + /* Lets try to fix any constraint subtargets that might * have been duplicated */ - updateDuplicateSubtarget(ebone, arm->edbo, obedit); + updateDuplicateSubtarget(ebone, arm->edbo, obedit, true); + /* Try to update constraint options so that they are mirrored as well + * (need to supply bone_iter as well in case we are working with existing bones) */ + updateDuplicateConstraintSettings(ebone, ebone_iter, obedit); + /* Mirror bone shapes if possible */ + updateDuplicateCustomBoneShapes(C, ebone, obedit); } } @@ -1232,7 +1691,7 @@ void ARMATURE_OT_subdivide(wmOperatorType *ot) PropertyRNA *prop; /* identifiers */ - ot->name = "Subdivide Multi"; + ot->name = "Subdivide"; ot->idname = "ARMATURE_OT_subdivide"; ot->description = "Break selected bones into chains of smaller bones"; diff --git a/source/blender/editors/armature/armature_intern.h b/source/blender/editors/armature/armature_intern.h index a454461b144..3d41fd5f0c6 100644 --- a/source/blender/editors/armature/armature_intern.h +++ b/source/blender/editors/armature/armature_intern.h @@ -232,9 +232,6 @@ struct EditBone *duplicateEditBone(struct EditBone *curBone, const char *name, struct ListBase *editbones, struct Object *ob); -void updateDuplicateSubtarget(struct EditBone *dupBone, - struct ListBase *editbones, - struct Object *ob); /* duplicate method (cross objects) */ /* editbones is the target list */ @@ -244,12 +241,6 @@ struct EditBone *duplicateEditBoneObjects(struct EditBone *curBone, struct Object *src_ob, struct Object *dst_ob); -/* editbones is the source list */ -void updateDuplicateSubtargetObjects(struct EditBone *dupBone, - struct ListBase *editbones, - struct Object *src_ob, - struct Object *dst_ob); - EditBone *add_points_bone(struct Object *obedit, float head[3], float tail[3]); void bone_free(struct bArmature *arm, struct EditBone *bone); diff --git a/source/blender/editors/armature/armature_select.c b/source/blender/editors/armature/armature_select.c index 4b938fb0072..98f067af148 100644 --- a/source/blender/editors/armature/armature_select.c +++ b/source/blender/editors/armature/armature_select.c @@ -657,45 +657,28 @@ static EditBone *get_nearest_editbonepoint( uint hitresult; Base *base; EditBone *ebone; - } best = { - .hitresult = BONESEL_NOSEL, - .base = NULL, - .ebone = NULL, - }; + } *result = NULL, + + result_cycle = {.hitresult = BONESEL_NOSEL, .base = NULL, .ebone = NULL}, + result_bias = {.hitresult = BONESEL_NOSEL, .base = NULL, .ebone = NULL}; /* find the bone after the current active bone, so as to bump up its chances in selection. * this way overlapping bones will cycle selection state as with objects. */ - EditBone *ebone_next_act = ((bArmature *)vc->obedit->data)->act_edbone; - { - bArmature *arm = (bArmature *)vc->obedit->data; - if (ebone_next_act && EBONE_VISIBLE(arm, ebone_next_act) && - ebone_next_act->flag & (BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL)) { - ebone_next_act = ebone_next_act->next ? ebone_next_act->next : arm->edbo->first; - } - else { - ebone_next_act = NULL; - } + Object *obedit_orig = vc->obedit; + EditBone *ebone_active_orig = ((bArmature *)obedit_orig->data)->act_edbone; + if (ebone_active_orig == NULL) { + use_cycle = false; } - bool do_nearest = false; - - /* define if we use solid nearest select or not */ if (use_cycle) { static int last_mval[2] = {-100, -100}; - - if (!XRAY_ACTIVE(vc->v3d)) { - do_nearest = true; - if (len_manhattan_v2v2_int(vc->mval, last_mval) <= WM_EVENT_CURSOR_MOTION_THRESHOLD) { - do_nearest = false; - } + if ((len_manhattan_v2v2_int(vc->mval, last_mval) <= WM_EVENT_CURSOR_MOTION_THRESHOLD) == 0) { + use_cycle = false; } copy_v2_v2_int(last_mval, vc->mval); } - else { - if (!XRAY_ACTIVE(vc->v3d)) { - do_nearest = true; - } - } + + const bool do_nearest = !(XRAY_ACTIVE(vc->v3d) || use_cycle); /* matching logic from 'mixed_bones_object_selectbuffer' */ int hits = 0; @@ -750,13 +733,39 @@ cache_end: if (hits > 0) { if (hits == 1) { if (!(buffer[3] & BONESEL_NOSEL)) { - best.hitresult = buffer[3]; - best.base = ED_armature_base_and_ebone_from_select_buffer( - bases, bases_len, best.hitresult, &best.ebone); + result_bias.hitresult = buffer[3]; + result_bias.base = ED_armature_base_and_ebone_from_select_buffer( + bases, bases_len, result_bias.hitresult, &result_bias.ebone); } } else { - int dep_min = 5; + int bias_max = INT_MIN; + + /* Track cycle variables. */ + struct { + union { + uint32_t cmp; + struct { +#ifdef __BIG_ENDIAN__ + uint16_t ob; + uint16_t bone; +#else + uint16_t bone; + uint16_t ob; +#endif + } index; + } active, test, best; + } cycle_order; + + if (use_cycle) { + bArmature *arm = obedit_orig->data; + int ob_index = obedit_orig->runtime.select_id & 0xFFFF; + int bone_index = BLI_findindex(arm->edbo, ebone_active_orig); + cycle_order.active.index.ob = ob_index; + cycle_order.active.index.bone = bone_index; + cycle_order.best.cmp = 0xffffffff; + } + for (int i = 0; i < hits; i++) { const uint hitresult = buffer[3 + (i * 4)]; if (!(hitresult & BONESEL_NOSEL)) { @@ -767,69 +776,98 @@ cache_end: /* If this fails, selection code is setting the selection ID's incorrectly. */ BLI_assert(base && ebone); - int dep; - /* clicks on bone points get advantage */ - if (hitresult & (BONESEL_ROOT | BONESEL_TIP)) { - /* but also the unselected one */ - if (findunsel) { - if ((hitresult & BONESEL_ROOT) && (ebone->flag & BONE_ROOTSEL) == 0) { - dep = 1; - } - else if ((hitresult & BONESEL_TIP) && (ebone->flag & BONE_TIPSEL) == 0) { - dep = 1; + /* Prioritized selection. */ + { + int bias; + /* clicks on bone points get advantage */ + if (hitresult & (BONESEL_ROOT | BONESEL_TIP)) { + /* but also the unselected one */ + if (findunsel) { + if ((hitresult & BONESEL_ROOT) && (ebone->flag & BONE_ROOTSEL) == 0) { + bias = 4; + } + else if ((hitresult & BONESEL_TIP) && (ebone->flag & BONE_TIPSEL) == 0) { + bias = 4; + } + else { + bias = 3; + } } else { - dep = 2; + bias = 4; } } else { - dep = 1; - } - } - else { - /* bone found */ - if (findunsel) { - if ((ebone->flag & BONE_SELECTED) == 0) { - dep = 3; + /* bone found */ + if (findunsel) { + if ((ebone->flag & BONE_SELECTED) == 0) { + bias = 2; + } + else { + bias = 1; + } } else { - dep = 4; + bias = 2; } } - else { - dep = 3; + + if (bias > bias_max) { + bias_max = bias; + + result_bias.hitresult = hitresult; + result_bias.base = base; + result_bias.ebone = ebone; } } - if (ebone == ebone_next_act) { - dep -= 1; - } + /* Cycle selected items (objects & bones). */ + if (use_cycle) { + bool found = false; + cycle_order.test.index.ob = hitresult & 0xFFFF; + cycle_order.test.index.bone = (hitresult & ~BONESEL_ANY) >> 16; + if (ebone == ebone_active_orig) { + BLI_assert(cycle_order.test.index.ob == cycle_order.active.index.ob); + BLI_assert(cycle_order.test.index.bone == cycle_order.active.index.bone); + } + cycle_order.test.cmp -= cycle_order.active.cmp; - if (dep < dep_min) { - dep_min = dep; - best.hitresult = hitresult; - best.base = base; - best.ebone = ebone; + if (cycle_order.test.cmp < cycle_order.best.cmp && ebone != ebone_active_orig) { + cycle_order.best.cmp = cycle_order.test.cmp; + found = true; + } + else if (ELEM(result_cycle.ebone, NULL, ebone_active_orig)) { + /* Let the active bone become selected, but don't set the cycle order. */ + found = true; + } + + if (found) { + result_cycle.hitresult = hitresult; + result_cycle.base = base; + result_cycle.ebone = ebone; + } } } } } - if (!(best.hitresult & BONESEL_NOSEL)) { - *r_base = best.base; + result = (use_cycle && result_cycle.ebone) ? &result_cycle : &result_bias; + + if (!(result->hitresult & BONESEL_NOSEL)) { + *r_base = result->base; *r_selmask = 0; - if (best.hitresult & BONESEL_ROOT) { + if (result->hitresult & BONESEL_ROOT) { *r_selmask |= BONE_ROOTSEL; } - if (best.hitresult & BONESEL_TIP) { + if (result->hitresult & BONESEL_TIP) { *r_selmask |= BONE_TIPSEL; } - if (best.hitresult & BONESEL_BONE) { + if (result->hitresult & BONESEL_BONE) { *r_selmask |= BONE_SELECTED; } MEM_freeN(bases); - return best.ebone; + return result->ebone; } } *r_selmask = 0; diff --git a/source/blender/editors/gpencil/gpencil_convert.c b/source/blender/editors/gpencil/gpencil_convert.c index 28e632d2b82..e89903adf5f 100644 --- a/source/blender/editors/gpencil/gpencil_convert.c +++ b/source/blender/editors/gpencil/gpencil_convert.c @@ -1822,7 +1822,7 @@ void GPENCIL_OT_image_to_grease_pencil(wmOperatorType *ot) 0.0001f, 10.0f, "Point Size", - "Size used for graese pencil points", + "Size used for grease pencil points", 0.001f, 1.0f); RNA_def_property_flag(ot->prop, PROP_SKIP_SAVE); diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c index 40068b0fb85..9de15c411cc 100644 --- a/source/blender/editors/gpencil/gpencil_paint.c +++ b/source/blender/editors/gpencil/gpencil_paint.c @@ -3127,6 +3127,34 @@ static tGPsdata *gpencil_stroke_begin(bContext *C, wmOperator *op) return op->customdata; } +/* Apply pressure change depending of the angle of the stroke for a segment. */ +static void gp_brush_angle_segment(tGPsdata *p, tGPspoint *pt_prev, tGPspoint *pt) +{ + Brush *brush = p->brush; + /* Sensitivity. */ + const float sen = brush->gpencil_settings->draw_angle_factor; + /* Default angle of brush in radians */ + const float angle = brush->gpencil_settings->draw_angle; + + float mvec[2]; + float fac; + float mpressure; + + /* angle vector of the brush with full thickness */ + float v0[2] = {cos(angle), sin(angle)}; + + mvec[0] = pt->x - pt_prev->x; + mvec[1] = pt->y - pt_prev->y; + normalize_v2(mvec); + + fac = 1.0f - fabs(dot_v2v2(v0, mvec)); /* 0.0 to 1.0 */ + /* interpolate with previous point for smoother transitions */ + mpressure = interpf(pt->pressure - (sen * fac), pt_prev->pressure, 0.3f); + pt->pressure = mpressure; + + CLAMP(pt->pressure, pt_prev->pressure * 0.5f, 1.0f); +} + /* Add arc points between two mouse events using the previous segment to determine the vertice of * the arc. * /+ CTL @@ -3209,6 +3237,11 @@ static void gpencil_add_arc_points(tGPsdata *p, float mval[2], int segments) pt->pressure = pt_prev->pressure; pt->strength = pt_prev->strength; + /* Apply angle of stroke to brush size. */ + if (brush_settings->draw_angle_factor != 0.0f) { + gp_brush_angle_segment(p, pt_prev, pt); + } + /* Apply randomness to pressure. */ if (brush_settings->draw_random_press > 0.0f) { float rand = BLI_rng_get_float(p->rng) * 2.0f - 1.0f; diff --git a/source/blender/editors/include/ED_mesh.h b/source/blender/editors/include/ED_mesh.h index 7f69bcb25cd..20e54df1ccb 100644 --- a/source/blender/editors/include/ED_mesh.h +++ b/source/blender/editors/include/ED_mesh.h @@ -139,6 +139,12 @@ bool BMBVH_EdgeVisible(struct BMBVHTree *tree, struct View3D *v3d, struct Object *obedit); +void EDBM_project_snap_verts(struct bContext *C, + struct Depsgraph *depsgraph, + struct ARegion *region, + struct Object *obedit, + struct BMEditMesh *em); + /* editmesh_automerge.c */ void EDBM_automerge(struct Object *ob, bool update, const char hflag, const float dist); void EDBM_automerge_and_split(struct Object *ob, @@ -293,13 +299,6 @@ void ED_operatortypes_mesh(void); void ED_operatormacros_mesh(void); void ED_keymap_mesh(struct wmKeyConfig *keyconf); -/* editmesh_tools.c (could be moved) */ -void EDBM_project_snap_verts(struct bContext *C, - struct Depsgraph *depsgraph, - struct ARegion *region, - struct Object *obedit, - struct BMEditMesh *em); - /* editface.c */ void paintface_flush_flags(struct bContext *C, struct Object *ob, short flag); bool paintface_mouse_select(struct bContext *C, diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c index 339124db355..18666daa8b8 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -790,6 +790,9 @@ static bool ui_but_update_from_old_block(const bContext *C, SWAP(ListBase, but->extra_op_icons, oldbut->extra_op_icons); + SWAP(uiButSearchArgFreeFunc, oldbut->search_arg_free_func, but->search_arg_free_func); + SWAP(void *, oldbut->search_arg, but->search_arg); + /* copy hardmin for list rows to prevent 'sticking' highlight to mouse position * when scrolling without moving mouse (see [#28432]) */ if (ELEM(oldbut->type, UI_BTYPE_ROW, UI_BTYPE_LISTROW)) { diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index 942f19eb4e9..5573d9b2edb 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -1014,7 +1014,11 @@ typedef struct uiRNACollectionSearch { PointerRNA search_ptr; PropertyRNA *search_prop; - bool *but_changed; /* pointer to uiBut.changed */ + uiBut *search_but; + /* Let UI_butstore_ API update search_but pointer above over redraws. */ + uiButStore *butstore; + /* Block has to be stored for freeing butstore (uiBut.block doesn't work with undo). */ + uiBlock *butstore_block; } uiRNACollectionSearch; void ui_rna_collection_search_cb(const struct bContext *C, void *arg, diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c index 44def6a2c09..92779c83d9a 100644 --- a/source/blender/editors/interface/interface_layout.c +++ b/source/blender/editors/interface/interface_layout.c @@ -2577,6 +2577,13 @@ static void search_id_collection(StructRNA *ptype, PointerRNA *r_ptr, PropertyRN RNA_STRUCT_END; } +static void ui_rna_collection_search_free_cb(void *ptr) +{ + uiRNACollectionSearch *coll_search = ptr; + UI_butstore_free(coll_search->butstore_block, coll_search->butstore); + MEM_freeN(ptr); +} + void ui_but_add_search( uiBut *but, PointerRNA *ptr, PropertyRNA *prop, PointerRNA *searchptr, PropertyRNA *searchprop) { @@ -2609,7 +2616,10 @@ void ui_but_add_search( coll_search->target_prop = prop; coll_search->search_ptr = *searchptr; coll_search->search_prop = searchprop; - coll_search->but_changed = &but->changed; + coll_search->search_but = but; + coll_search->butstore_block = but->block; + coll_search->butstore = UI_butstore_create(coll_search->butstore_block); + UI_butstore_register(coll_search->butstore, &coll_search->search_but); if (RNA_property_type(prop) == PROP_ENUM) { /* XXX, this will have a menu string, @@ -2621,7 +2631,7 @@ void ui_but_add_search( ui_searchbox_create_generic, ui_rna_collection_search_cb, coll_search, - MEM_freeN, + ui_rna_collection_search_free_cb, NULL, NULL); } diff --git a/source/blender/editors/interface/interface_panel.c b/source/blender/editors/interface/interface_panel.c index b3854cfc4ae..d1c61925d40 100644 --- a/source/blender/editors/interface/interface_panel.c +++ b/source/blender/editors/interface/interface_panel.c @@ -56,7 +56,9 @@ #include "UI_resources.h" #include "UI_view2d.h" +#include "GPU_batch_presets.h" #include "GPU_immediate.h" +#include "GPU_matrix.h" #include "GPU_state.h" #include "interface_intern.h" @@ -544,59 +546,6 @@ static void ui_draw_panel_scalewidget(uint pos, const rcti *rect) GPU_blend(false); } -static void immRectf_tris_color_ex( - uint pos, float x1, float y1, float x2, float y2, uint col, const float color[3]) -{ - immAttr4fv(col, color); - immVertex2f(pos, x1, y1); - immAttr4fv(col, color); - immVertex2f(pos, x2, y1); - immAttr4fv(col, color); - immVertex2f(pos, x2, y2); - - immAttr4fv(col, color); - immVertex2f(pos, x1, y1); - immAttr4fv(col, color); - immVertex2f(pos, x2, y2); - immAttr4fv(col, color); - immVertex2f(pos, x1, y2); -} - -static void ui_draw_panel_dragwidget(uint pos, uint col, const rctf *rect) -{ - float col_high[4], col_dark[4]; - const int col_tint = 84; - - const int px = (int)U.pixelsize; - const int px_zoom = max_ii(round_fl_to_int(BLI_rctf_size_y(rect) / 22.0f), 1); - - const int box_margin = max_ii(round_fl_to_int((float)(px_zoom * 2.0f)), px); - const int box_size = max_ii(round_fl_to_int((BLI_rctf_size_y(rect) / 8.0f) - px), px); - - const int x_min = rect->xmin; - const int y_min = rect->ymin; - const int y_ofs = max_ii(round_fl_to_int(BLI_rctf_size_y(rect) / 2.5f), px); - const int x_ofs = y_ofs; - int i_x, i_y; - - UI_GetThemeColorShade4fv(TH_PANEL_HEADER, col_tint, col_high); - UI_GetThemeColorShade4fv(TH_PANEL_BACK, -col_tint, col_dark); - - /* draw multiple boxes */ - immBegin(GPU_PRIM_TRIS, 4 * 2 * (6 * 2)); - for (i_x = 0; i_x < 4; i_x++) { - for (i_y = 0; i_y < 2; i_y++) { - const int x_co = (x_min + x_ofs) + (i_x * (box_size + box_margin)); - const int y_co = (y_min + y_ofs) + (i_y * (box_size + box_margin)); - - immRectf_tris_color_ex( - pos, x_co - box_size, y_co - px_zoom, x_co, (y_co + box_size) - px_zoom, col, col_dark); - immRectf_tris_color_ex(pos, x_co - box_size, y_co, x_co, y_co + box_size, col, col_high); - } - } - immEnd(); -} - /* For button layout next to label. */ void UI_panel_label_offset(uiBlock *block, int *r_x, int *r_y) { @@ -755,24 +704,27 @@ void ui_draw_aligned_panel(uiStyle *style, ui_draw_aligned_panel_header(style, block, &titlerect, 'h', show_background); if (show_drag) { - uint col; - GPUVertFormat *format = immVertexFormat(); - pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - col = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); - /* itemrect smaller */ + const float scale = 0.7; itemrect.xmax = headrect.xmax - (0.2f * UI_UNIT_X); itemrect.xmin = itemrect.xmax - BLI_rcti_size_y(&headrect); itemrect.ymin = headrect.ymin; itemrect.ymax = headrect.ymax; + BLI_rctf_scale(&itemrect, scale); - BLI_rctf_scale(&itemrect, 0.7f); - immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR); - ui_draw_panel_dragwidget(pos, col, &itemrect); - immUnbindProgram(); + GPU_matrix_push(); + GPU_matrix_translate_2f(itemrect.xmin, itemrect.ymin); + + const int col_tint = 84; + float col_high[4], col_dark[4]; + UI_GetThemeColorShade4fv(TH_PANEL_HEADER, col_tint, col_high); + UI_GetThemeColorShade4fv(TH_PANEL_BACK, -col_tint, col_dark); - /* Restore format for the following draws. */ - pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + GPUBatch *batch = GPU_batch_preset_panel_drag_widget( + U.pixelsize, col_high, col_dark, BLI_rcti_size_y(&headrect) * scale); + GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_FLAT_COLOR); + GPU_batch_draw(batch); + GPU_matrix_pop(); } } @@ -1622,10 +1574,13 @@ static void ui_handle_panel_header( } else { /* collapse */ if (ctrl) { - panels_collapse_all(area, region, block->panel); + /* Only collapse all for parent panels. */ + if (block->panel->type != NULL && block->panel->type->parent == NULL) { + panels_collapse_all(area, region, block->panel); - /* reset the view - we don't want to display a view without content */ - UI_view2d_offset(®ion->v2d, 0.0f, 1.0f); + /* reset the view - we don't want to display a view without content */ + UI_view2d_offset(®ion->v2d, 0.0f, 1.0f); + } } if (block->panel->flag & PNL_CLOSED) { diff --git a/source/blender/editors/interface/interface_utils.c b/source/blender/editors/interface/interface_utils.c index a69837e9b51..2d687781b61 100644 --- a/source/blender/editors/interface/interface_utils.c +++ b/source/blender/editors/interface/interface_utils.c @@ -399,7 +399,7 @@ void ui_rna_collection_search_cb(const struct bContext *C, int i = 0, iconid = 0, flag = RNA_property_flag(data->target_prop); ListBase *items_list = MEM_callocN(sizeof(ListBase), "items_list"); CollItemSearch *cis; - const bool skip_filter = (data->but_changed && !(*data->but_changed)); + const bool skip_filter = data->search_but && !data->search_but->changed; /* build a temporary list of relevant items first */ RNA_PROP_BEGIN (&data->search_ptr, itemptr, data->search_prop) { @@ -647,6 +647,7 @@ void UI_butstore_free(uiBlock *block, uiButStore *bs_handle) } BLI_freelistN(&bs_handle->items); + BLI_assert(BLI_findindex(&block->butstore, bs_handle) != -1); BLI_remlink(&block->butstore, bs_handle); MEM_freeN(bs_handle); @@ -747,8 +748,7 @@ void UI_butstore_update(uiBlock *block) /* move this list to the new block */ if (block->oldblock) { if (block->oldblock->butstore.first) { - block->butstore = block->oldblock->butstore; - BLI_listbase_clear(&block->oldblock->butstore); + BLI_movelisttolist(&block->butstore, &block->oldblock->butstore); } } diff --git a/source/blender/editors/mesh/editmesh_select.c b/source/blender/editors/mesh/editmesh_select.c index f01511fa513..3847f32b19a 100644 --- a/source/blender/editors/mesh/editmesh_select.c +++ b/source/blender/editors/mesh/editmesh_select.c @@ -3201,8 +3201,12 @@ static int edbm_select_linked_exec(bContext *C, wmOperator *op) BMEdge *e; BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) { if (!BMO_edge_flag_test(bm, e, BMO_ELE_TAG)) { - BM_elem_flag_disable(e->v1, BM_ELEM_TAG); - BM_elem_flag_disable(e->v2, BM_ELEM_TAG); + /* Check the edge for selected faces, + * this supports stepping off isolated vertices which would otherwise be ignored. */ + if (BM_edge_is_any_face_flag_test(e, BM_ELEM_SELECT)) { + BM_elem_flag_disable(e->v1, BM_ELEM_TAG); + BM_elem_flag_disable(e->v2, BM_ELEM_TAG); + } } } } @@ -3258,10 +3262,13 @@ static int edbm_select_linked_exec(bContext *C, wmOperator *op) if (delimit) { BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) { - BM_elem_flag_set( - e, - BM_ELEM_TAG, - (BM_elem_flag_test(e, BM_ELEM_SELECT) && BMO_edge_flag_test(bm, e, BMO_ELE_TAG))); + /* Check the edge for selected faces, + * this supports stepping off isolated edges which would otherwise be ignored. */ + BM_elem_flag_set(e, + BM_ELEM_TAG, + (BM_elem_flag_test(e, BM_ELEM_SELECT) && + (BMO_edge_flag_test(bm, e, BMO_ELE_TAG) || + !BM_edge_is_any_face_flag_test(e, BM_ELEM_SELECT)))); } } else { diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c index b9ac24e1d03..ba3520f2217 100644 --- a/source/blender/editors/mesh/editmesh_tools.c +++ b/source/blender/editors/mesh/editmesh_tools.c @@ -71,7 +71,6 @@ #include "ED_object.h" #include "ED_screen.h" #include "ED_transform.h" -#include "ED_transform_snap_object_context.h" #include "ED_uvedit.h" #include "ED_view3d.h" @@ -410,45 +409,6 @@ void MESH_OT_unsubdivide(wmOperatorType *ot) ot->srna, "iterations", 2, 1, 1000, "Iterations", "Number of times to unsubdivide", 1, 100); } -void EDBM_project_snap_verts( - bContext *C, Depsgraph *depsgraph, ARegion *region, Object *obedit, BMEditMesh *em) -{ - Main *bmain = CTX_data_main(C); - BMIter iter; - BMVert *eve; - - ED_view3d_init_mats_rv3d(obedit, region->regiondata); - - struct SnapObjectContext *snap_context = ED_transform_snap_object_context_create_view3d( - bmain, CTX_data_scene(C), 0, region, CTX_wm_view3d(C)); - - BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) { - if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) { - float mval[2], co_proj[3]; - if (ED_view3d_project_float_object(region, eve->co, mval, V3D_PROJ_TEST_NOP) == - V3D_PROJ_RET_OK) { - if (ED_transform_snap_object_project_view3d(snap_context, - depsgraph, - SCE_SNAP_MODE_FACE, - &(const struct SnapObjectParams){ - .snap_select = SNAP_NOT_ACTIVE, - .use_object_edit_cage = false, - .use_occlusion_test = true, - }, - mval, - NULL, - NULL, - co_proj, - NULL)) { - mul_v3_m4v3(eve->co, obedit->imat, co_proj); - } - } - } - } - - ED_transform_snap_object_context_destroy(snap_context); -} - /** \} */ /* -------------------------------------------------------------------- */ @@ -1186,6 +1146,12 @@ void MESH_OT_mark_sharp(wmOperatorType *ot) RNA_def_property_flag(prop, PROP_SKIP_SAVE); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Connect Vertex Path Operator + * \{ */ + static bool edbm_connect_vert_pair(BMEditMesh *em, struct Mesh *me, wmOperator *op) { BMesh *bm = em->bm; @@ -1311,7 +1277,7 @@ void MESH_OT_vert_connect(wmOperatorType *ot) /** \} */ /* -------------------------------------------------------------------- */ -/** \name Split Concave Faces Operator +/** \name Connect Vertex Path Operator * \{ */ /** @@ -1600,6 +1566,12 @@ void MESH_OT_vert_connect_path(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Connect Concave Operator + * \{ */ + static int edbm_vert_connect_concave_exec(bContext *C, wmOperator *op) { ViewLayer *view_layer = CTX_data_view_layer(C); @@ -3210,7 +3182,7 @@ void MESH_OT_merge(wmOperatorType *ot) /** \} */ /* -------------------------------------------------------------------- */ -/** \name Remove Doubles Operator +/** \name Merge By Distance Operator * \{ */ static int edbm_remove_doubles_exec(bContext *C, wmOperator *op) @@ -7812,7 +7784,11 @@ wmKeyMap *point_normals_modal_keymap(wmKeyConfig *keyconf) #define CLNORS_VALID_VEC_LEN (1e-4f) -/********************** 'Point to' Loop Normals **********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Loop Normals 'Point To' Operator + * \{ */ enum { EDBM_CLNOR_POINTTO_MODE_COORDINATES = 1, @@ -7830,7 +7806,7 @@ static EnumPropertyItem clnors_pointto_mode_items[] = { }; /* Initialize loop normal data */ -static int point_normals_init(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +static bool point_normals_init(bContext *C, wmOperator *op) { Object *obedit = CTX_data_edit_object(C); BMEditMesh *em = BKE_editmesh_from_object(obedit); @@ -7842,14 +7818,29 @@ static int point_normals_init(bContext *C, wmOperator *op, const wmEvent *UNUSED op->customdata = lnors_ed_arr; - return lnors_ed_arr->totloop; + return (lnors_ed_arr->totloop != 0); } -static void point_normals_free(bContext *C, wmOperator *op) +static bool point_normals_ensure(bContext *C, wmOperator *op) { - BMLoopNorEditDataArray *lnors_ed_arr = op->customdata; - BM_loop_normal_editdata_array_free(lnors_ed_arr); - op->customdata = NULL; + if (op->customdata != NULL) { + return true; + } + return point_normals_init(C, op); +} + +static void point_normals_free(wmOperator *op) +{ + if (op->customdata != NULL) { + BMLoopNorEditDataArray *lnors_ed_arr = op->customdata; + BM_loop_normal_editdata_array_free(lnors_ed_arr); + op->customdata = NULL; + } +} + +static void point_normals_cancel(bContext *C, wmOperator *op) +{ + point_normals_free(op); ED_area_status_text(CTX_wm_area(C), NULL); } @@ -7966,6 +7957,13 @@ static void point_normals_apply(bContext *C, wmOperator *op, float target[3], co static int edbm_point_normals_modal(bContext *C, wmOperator *op, const wmEvent *event) { + /* As this operator passes events through, we can't be sure the user didn't exit edit-mode. + * or performed some other operation. */ + if (!WM_operator_poll(C, op->type)) { + point_normals_cancel(C, op); + return OPERATOR_CANCELLED; + } + View3D *v3d = CTX_wm_view3d(C); Scene *scene = CTX_data_scene(C); Object *obedit = CTX_data_edit_object(C); @@ -8136,23 +8134,37 @@ static int edbm_point_normals_modal(bContext *C, wmOperator *op, const wmEvent * if (!ELEM(ret, OPERATOR_CANCELLED, OPERATOR_FINISHED)) { RNA_property_float_set_array(op->ptr, prop_target, target); } - point_normals_apply(C, op, target, do_reset); - EDBM_update_generic(obedit->data, true, false); /* Recheck bools. */ - point_normals_update_header(C, op); + if (point_normals_ensure(C, op)) { + point_normals_apply(C, op, target, do_reset); + EDBM_update_generic(obedit->data, true, false); /* Recheck bools. */ + point_normals_update_header(C, op); + } + else { + ret = OPERATOR_CANCELLED; + } } if (ELEM(ret, OPERATOR_CANCELLED, OPERATOR_FINISHED)) { - point_normals_free(C, op); + point_normals_cancel(C, op); } + /* If we allow other tools to run, we can't be sure if they will re-allocate + * the data this operator uses, see: T68159. + * Free the data here, then use #point_normals_ensure to add it back on demand. */ + if (ret == OPERATOR_PASS_THROUGH) { + /* Don't free on mouse-move, causes creation/freeing of the loop data in an inefficient way. */ + if (!ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) { + point_normals_free(op); + } + } return ret; } -static int edbm_point_normals_invoke(bContext *C, wmOperator *op, const wmEvent *event) +static int edbm_point_normals_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { - if (!point_normals_init(C, op, event)) { - point_normals_free(C, op); + if (!point_normals_init(C, op)) { + point_normals_cancel(C, op); return OPERATOR_CANCELLED; } @@ -8169,8 +8181,8 @@ static int edbm_point_normals_exec(bContext *C, wmOperator *op) { Object *obedit = CTX_data_edit_object(C); - if (!point_normals_init(C, op, NULL)) { - point_normals_free(C, op); + if (!point_normals_init(C, op)) { + point_normals_cancel(C, op); return OPERATOR_CANCELLED; } @@ -8183,7 +8195,7 @@ static int edbm_point_normals_exec(bContext *C, wmOperator *op) point_normals_apply(C, op, target, false); EDBM_update_generic(obedit->data, true, false); - point_normals_free(C, op); + point_normals_cancel(C, op); return OPERATOR_FINISHED; } @@ -8228,7 +8240,7 @@ void MESH_OT_point_normals(struct wmOperatorType *ot) ot->modal = edbm_point_normals_modal; ot->poll = ED_operator_editmesh; ot->ui = edbm_point_normals_ui; - ot->cancel = point_normals_free; + ot->cancel = point_normals_cancel; /* flags */ ot->flag = OPTYPE_BLOCKING | OPTYPE_REGISTER | OPTYPE_UNDO; @@ -8273,7 +8285,7 @@ void MESH_OT_point_normals(struct wmOperatorType *ot) /** \} */ /* -------------------------------------------------------------------- */ -/** \name Split/Merge Loop Normals +/** \name Split/Merge Loop Normals Operator * \{ */ static void normals_merge(BMesh *bm, BMLoopNorEditDataArray *lnors_ed_arr) @@ -8484,7 +8496,7 @@ void MESH_OT_split_normals(struct wmOperatorType *ot) /** \} */ /* -------------------------------------------------------------------- */ -/** \name Average Loop Normals +/** \name Average Loop Normals Operator * \{ */ enum { @@ -8716,7 +8728,7 @@ void MESH_OT_average_normals(struct wmOperatorType *ot) /** \} */ /* -------------------------------------------------------------------- */ -/** \name Custom Normal Interface Tools +/** \name Custom Normal Interface Tools Operator * \{ */ enum { @@ -8952,6 +8964,12 @@ void MESH_OT_normals_tools(struct wmOperatorType *ot) "Copy Absolute coordinates or Normal vector"); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Set Normals from Faces Operator + * \{ */ + static int edbm_set_normals_from_faces_exec(bContext *C, wmOperator *op) { ViewLayer *view_layer = CTX_data_view_layer(C); @@ -9059,7 +9077,13 @@ void MESH_OT_set_normals_from_faces(struct wmOperatorType *ot) RNA_def_boolean(ot->srna, "keep_sharp", 0, "Keep Sharp Edges", "Do not set sharp edges to face"); } -static int edbm_smoothen_normals_exec(bContext *C, wmOperator *op) +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Smooth Normal Vectors Operator + * \{ */ + +static int edbm_smooth_normals_exec(bContext *C, wmOperator *op) { ViewLayer *view_layer = CTX_data_view_layer(C); uint objects_len = 0; @@ -9112,7 +9136,7 @@ static int edbm_smoothen_normals_exec(bContext *C, wmOperator *op) float current_normal[3]; if (normalize_v3(smooth_normal[i]) < CLNORS_VALID_VEC_LEN) { - /* Skip in case smoothen normal is invalid... */ + /* Skip in case the smooth normal is invalid. */ continue; } @@ -9126,7 +9150,7 @@ static int edbm_smoothen_normals_exec(bContext *C, wmOperator *op) add_v3_v3(current_normal, smooth_normal[i]); if (normalize_v3(current_normal) < CLNORS_VALID_VEC_LEN) { - /* Skip in case smoothen normal is invalid... */ + /* Skip in case the smoothed normal is invalid. */ continue; } @@ -9144,15 +9168,15 @@ static int edbm_smoothen_normals_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } -void MESH_OT_smoothen_normals(struct wmOperatorType *ot) +void MESH_OT_smooth_normals(struct wmOperatorType *ot) { /* identifiers */ ot->name = "Smooth Normals Vectors"; - ot->description = "Smoothen custom normals based on adjacent vertex normals"; - ot->idname = "MESH_OT_smoothen_normals"; + ot->description = "Smooth custom normals based on adjacent vertex normals"; + ot->idname = "MESH_OT_smooth_normals"; /* api callbacks */ - ot->exec = edbm_smoothen_normals_exec; + ot->exec = edbm_smooth_normals_exec; ot->poll = ED_operator_editmesh; /* flags */ diff --git a/source/blender/editors/mesh/editmesh_utils.c b/source/blender/editors/mesh/editmesh_utils.c index 998e0d736e6..a26003d78ed 100644 --- a/source/blender/editors/mesh/editmesh_utils.c +++ b/source/blender/editors/mesh/editmesh_utils.c @@ -52,6 +52,7 @@ #include "ED_mesh.h" #include "ED_screen.h" +#include "ED_transform_snap_object_context.h" #include "ED_uvedit.h" #include "ED_view3d.h" @@ -1638,3 +1639,48 @@ bool BMBVH_EdgeVisible(struct BMBVHTree *tree, } /** \} */ + +/* -------------------------------------------------------------------- */ +/** \name BMesh Vertex Projection API + * \{ */ + +void EDBM_project_snap_verts( + bContext *C, Depsgraph *depsgraph, ARegion *region, Object *obedit, BMEditMesh *em) +{ + Main *bmain = CTX_data_main(C); + BMIter iter; + BMVert *eve; + + ED_view3d_init_mats_rv3d(obedit, region->regiondata); + + struct SnapObjectContext *snap_context = ED_transform_snap_object_context_create_view3d( + bmain, CTX_data_scene(C), 0, region, CTX_wm_view3d(C)); + + BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) { + if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) { + float mval[2], co_proj[3]; + if (ED_view3d_project_float_object(region, eve->co, mval, V3D_PROJ_TEST_NOP) == + V3D_PROJ_RET_OK) { + if (ED_transform_snap_object_project_view3d(snap_context, + depsgraph, + SCE_SNAP_MODE_FACE, + &(const struct SnapObjectParams){ + .snap_select = SNAP_NOT_ACTIVE, + .use_object_edit_cage = false, + .use_occlusion_test = true, + }, + mval, + NULL, + NULL, + co_proj, + NULL)) { + mul_v3_m4v3(eve->co, obedit->imat, co_proj); + } + } + } + } + + ED_transform_snap_object_context_destroy(snap_context); +} + +/** \} */ diff --git a/source/blender/editors/mesh/mesh_intern.h b/source/blender/editors/mesh/mesh_intern.h index 594429d4925..5e70069456b 100644 --- a/source/blender/editors/mesh/mesh_intern.h +++ b/source/blender/editors/mesh/mesh_intern.h @@ -248,7 +248,7 @@ void MESH_OT_split_normals(struct wmOperatorType *ot); void MESH_OT_normals_tools(struct wmOperatorType *ot); void MESH_OT_set_normals_from_faces(struct wmOperatorType *ot); void MESH_OT_average_normals(struct wmOperatorType *ot); -void MESH_OT_smoothen_normals(struct wmOperatorType *ot); +void MESH_OT_smooth_normals(struct wmOperatorType *ot); void MESH_OT_mod_weighted_strength(struct wmOperatorType *ot); /* *** editmesh_mask_extract.c *** */ diff --git a/source/blender/editors/mesh/mesh_ops.c b/source/blender/editors/mesh/mesh_ops.c index c61fe39b5fd..66b2c66f2aa 100644 --- a/source/blender/editors/mesh/mesh_ops.c +++ b/source/blender/editors/mesh/mesh_ops.c @@ -203,7 +203,7 @@ void ED_operatortypes_mesh(void) WM_operatortype_append(MESH_OT_normals_tools); WM_operatortype_append(MESH_OT_set_normals_from_faces); WM_operatortype_append(MESH_OT_average_normals); - WM_operatortype_append(MESH_OT_smoothen_normals); + WM_operatortype_append(MESH_OT_smooth_normals); WM_operatortype_append(MESH_OT_mod_weighted_strength); } diff --git a/source/blender/editors/object/object_bake_api.c b/source/blender/editors/object/object_bake_api.c index d625b770164..debad321583 100644 --- a/source/blender/editors/object/object_bake_api.c +++ b/source/blender/editors/object/object_bake_api.c @@ -446,7 +446,8 @@ static bool bake_object_check(ViewLayer *view_layer, Object *ob, ReportList *rep for (i = 0; i < ob->totcol; i++) { bNodeTree *ntree = NULL; bNode *node = NULL; - ED_object_get_active_image(ob, i + 1, &image, NULL, &node, &ntree); + const int mat_nr = i + 1; + ED_object_get_active_image(ob, mat_nr, &image, NULL, &node, &ntree); if (image) { ImBuf *ibuf; @@ -481,7 +482,7 @@ static bool bake_object_check(ViewLayer *view_layer, Object *ob, ReportList *rep } } else { - Material *mat = BKE_object_material_get(ob, i); + Material *mat = BKE_object_material_get(ob, mat_nr); if (mat != NULL) { BKE_reportf(reports, RPT_INFO, diff --git a/source/blender/editors/sculpt_paint/sculpt_dyntopo.c b/source/blender/editors/sculpt_paint/sculpt_dyntopo.c index 5f75c1d6813..0ab3b8cd14e 100644 --- a/source/blender/editors/sculpt_paint/sculpt_dyntopo.c +++ b/source/blender/editors/sculpt_paint/sculpt_dyntopo.c @@ -42,8 +42,8 @@ #include "BKE_modifier.h" #include "BKE_object.h" #include "BKE_paint.h" -#include "BKE_pbvh.h" #include "BKE_particle.h" +#include "BKE_pbvh.h" #include "BKE_pointcache.h" #include "BKE_scene.h" #include "BKE_screen.h" @@ -141,10 +141,7 @@ void SCULPT_dyntopo_node_layers_add(SculptSession *ss) ss->bm->pdata.layers[cd_node_layer_index].flag |= CD_FLAG_TEMPORARY; } -void SCULPT_dynamic_topology_enable_ex(Main *bmain, - Depsgraph *depsgraph, - Scene *scene, - Object *ob) +void SCULPT_dynamic_topology_enable_ex(Main *bmain, Depsgraph *depsgraph, Scene *scene, Object *ob) { SculptSession *ss = ob->sculpt; Mesh *me = ob->data; @@ -281,9 +278,9 @@ void SCULPT_dynamic_topology_disable(bContext *C, SculptUndoNode *unode) } void sculpt_dynamic_topology_disable_with_undo(Main *bmain, - Depsgraph *depsgraph, - Scene *scene, - Object *ob) + Depsgraph *depsgraph, + Scene *scene, + Object *ob) { SculptSession *ss = ob->sculpt; if (ss->bm) { @@ -331,7 +328,6 @@ static int sculpt_dynamic_topology_toggle_exec(bContext *C, wmOperator *UNUSED(o return OPERATOR_FINISHED; } - static int dyntopo_warning_popup(bContext *C, wmOperatorType *ot, enum eDynTopoWarnFlag flag) { uiPopupMenu *pup = UI_popup_menu_begin(C, IFACE_("Warning!"), ICON_ERROR); diff --git a/source/blender/editors/space_buttons/buttons_ops.c b/source/blender/editors/space_buttons/buttons_ops.c index e2affa7bc36..9af623d8065 100644 --- a/source/blender/editors/space_buttons/buttons_ops.c +++ b/source/blender/editors/space_buttons/buttons_ops.c @@ -110,7 +110,7 @@ static int file_browse_exec(bContext *C, wmOperator *op) if (BLI_is_dir(path)) { /* do this first so '//' isnt converted to '//\' on windows */ - BLI_add_slash(path); + BLI_path_slash_ensure(path); if (is_relative) { BLI_strncpy(path, str, FILE_MAX); BLI_path_rel(path, BKE_main_blendfile_path(bmain)); @@ -122,7 +122,7 @@ static int file_browse_exec(bContext *C, wmOperator *op) } } else { - char *const lslash = (char *)BLI_last_slash(str); + char *const lslash = (char *)BLI_path_slash_rfind(str); if (lslash) { lslash[1] = '\0'; } @@ -187,7 +187,7 @@ static int file_browse_invoke(bContext *C, wmOperator *op, const wmEvent *event) PointerRNA props_ptr; if (event->alt) { - char *lslash = (char *)BLI_last_slash(str); + char *lslash = (char *)BLI_path_slash_rfind(str); if (lslash) { *lslash = '\0'; } diff --git a/source/blender/editors/space_clip/clip_buttons.c b/source/blender/editors/space_clip/clip_buttons.c index dc85b3959e1..3b1cc6fcab0 100644 --- a/source/blender/editors/space_clip/clip_buttons.c +++ b/source/blender/editors/space_clip/clip_buttons.c @@ -872,7 +872,7 @@ void uiTemplateMovieclipInformation(uiLayout *layout, if (framenr <= clip->len) { BKE_movieclip_filename_for_frame(clip, user, filepath); - file = BLI_last_slash(filepath); + file = BLI_path_slash_rfind(filepath); } else { file = "-"; diff --git a/source/blender/editors/space_clip/clip_ops.c b/source/blender/editors/space_clip/clip_ops.c index 48bc885405d..266212fdfd7 100644 --- a/source/blender/editors/space_clip/clip_ops.c +++ b/source/blender/editors/space_clip/clip_ops.c @@ -286,7 +286,7 @@ static int open_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event) BLI_strncpy(path, clip->name, sizeof(path)); BLI_path_abs(path, CTX_data_main(C)->name); - BLI_parent_dir(path); + BLI_path_parent_dir(path); } else { BLI_strncpy(path, U.textudir, sizeof(path)); diff --git a/source/blender/editors/space_file/file_ops.c b/source/blender/editors/space_file/file_ops.c index 7cce243696d..a5263378850 100644 --- a/source/blender/editors/space_file/file_ops.c +++ b/source/blender/editors/space_file/file_ops.c @@ -211,7 +211,7 @@ static FileSelect file_select_do(bContext *C, int selected_idx, bool do_diropen) else { if (is_parent_dir) { /* avoids /../../ */ - BLI_parent_dir(params->dir); + BLI_path_parent_dir(params->dir); if (params->recursion_level > 1) { /* Disable 'dirtree' recursion when going up in tree. */ @@ -220,9 +220,9 @@ static FileSelect file_select_do(bContext *C, int selected_idx, bool do_diropen) } } else { - BLI_cleanup_dir(BKE_main_blendfile_path(bmain), params->dir); + BLI_path_normalize_dir(BKE_main_blendfile_path(bmain), params->dir); strcat(params->dir, file->relpath); - BLI_add_slash(params->dir); + BLI_path_slash_ensure(params->dir); } ED_file_change_dir(C); @@ -940,7 +940,7 @@ static int bookmark_select_exec(bContext *C, wmOperator *op) RNA_property_string_get(op->ptr, prop, entry); BLI_strncpy(params->dir, entry, sizeof(params->dir)); - BLI_cleanup_dir(BKE_main_blendfile_path(bmain), params->dir); + BLI_path_normalize_dir(BKE_main_blendfile_path(bmain), params->dir); ED_file_change_dir(C); WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL); @@ -1627,12 +1627,12 @@ static int file_exec(bContext *C, wmOperator *exec_op) } if (FILENAME_IS_PARENT(file->relpath)) { - BLI_parent_dir(sfile->params->dir); + BLI_path_parent_dir(sfile->params->dir); } else { - BLI_cleanup_path(BKE_main_blendfile_path(bmain), sfile->params->dir); + BLI_path_normalize(BKE_main_blendfile_path(bmain), sfile->params->dir); BLI_path_append(sfile->params->dir, sizeof(sfile->params->dir) - 1, file->relpath); - BLI_add_slash(sfile->params->dir); + BLI_path_slash_ensure(sfile->params->dir); } if (file->redirection_path) { STRNCPY(sfile->params->dir, file->redirection_path); @@ -1754,8 +1754,8 @@ static int file_parent_exec(bContext *C, wmOperator *UNUSED(unused)) SpaceFile *sfile = CTX_wm_space_file(C); if (sfile->params) { - if (BLI_parent_dir(sfile->params->dir)) { - BLI_cleanup_dir(BKE_main_blendfile_path(bmain), sfile->params->dir); + if (BLI_path_parent_dir(sfile->params->dir)) { + BLI_path_normalize_dir(BKE_main_blendfile_path(bmain), sfile->params->dir); ED_file_change_dir(C); if (sfile->params->recursion_level > 1) { /* Disable 'dirtree' recursion when going up in tree. */ @@ -2274,7 +2274,7 @@ static void file_expand_directory(bContext *C) sfile->params->dir[3] = '\0'; } else if (BLI_path_is_unc(sfile->params->dir)) { - BLI_cleanup_unc(sfile->params->dir, FILE_MAX_LIBEXTRA); + BLI_path_normalize_unc(sfile->params->dir, FILE_MAX_LIBEXTRA); } #endif } @@ -2291,7 +2291,7 @@ static bool can_create_dir(const char *dir) if (BLI_path_is_unc(dir)) { char parent[PATH_MAX]; BLI_strncpy(parent, dir, PATH_MAX); - BLI_parent_dir(parent); + BLI_path_parent_dir(parent); return BLI_is_dir(parent); } return true; @@ -2338,7 +2338,7 @@ void file_directory_enter_handle(bContext *C, void *UNUSED(arg_unused), void *UN } } - BLI_cleanup_dir(BKE_main_blendfile_path(bmain), sfile->params->dir); + BLI_path_normalize_dir(BKE_main_blendfile_path(bmain), sfile->params->dir); if (filelist_is_dir(sfile->files, sfile->params->dir)) { if (!STREQ(sfile->params->dir, old_dir)) { /* Avoids flickering when nothing's changed. */ @@ -2423,7 +2423,7 @@ void file_filename_enter_handle(bContext *C, void *UNUSED(arg_unused), void *arg /* if directory, open it and empty filename field */ if (filelist_is_dir(sfile->files, filepath)) { - BLI_cleanup_dir(BKE_main_blendfile_path(bmain), filepath); + BLI_path_normalize_dir(BKE_main_blendfile_path(bmain), filepath); BLI_strncpy(sfile->params->dir, filepath, sizeof(sfile->params->dir)); sfile->params->file[0] = '\0'; ED_file_change_dir(C); @@ -2487,7 +2487,7 @@ static bool file_filenum_poll(bContext *C) } /** - * Looks for a string of digits within name (using BLI_stringdec) and adjusts it by add. + * Looks for a string of digits within name (using BLI_path_sequence_decode) and adjusts it by add. */ static void filenum_newname(char *name, size_t name_size, int add) { @@ -2496,7 +2496,7 @@ static void filenum_newname(char *name, size_t name_size, int add) int pic; ushort digits; - pic = BLI_stringdec(name, head, tail, &digits); + pic = BLI_path_sequence_decode(name, head, tail, &digits); /* are we going from 100 -> 99 or from 10 -> 9 */ if (add < 0 && digits > 0) { @@ -2514,7 +2514,7 @@ static void filenum_newname(char *name, size_t name_size, int add) if (pic < 0) { pic = 0; } - BLI_stringenc(name_temp, head, tail, digits, pic); + BLI_path_sequence_encode(name_temp, head, tail, digits, pic); BLI_strncpy(name, name_temp, name_size); } diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index 126430986a5..778c27e1a17 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -635,7 +635,7 @@ static bool is_hidden_dot_filename(const char *filename, FileListInternEntry *fi /* filename might actually be a piece of path, in which case we have to check all its parts. */ bool hidden = false; - char *sep = (char *)BLI_last_slash(filename); + char *sep = (char *)BLI_path_slash_rfind(filename); if (!hidden && sep) { char tmp_filename[FILE_MAX_LIBEXTRA]; @@ -654,7 +654,7 @@ static bool is_hidden_dot_filename(const char *filename, FileListInternEntry *fi break; } *sep = '\0'; - sep = (char *)BLI_last_slash(tmp_filename); + sep = (char *)BLI_path_slash_rfind(tmp_filename); } } return hidden; @@ -1023,7 +1023,7 @@ static int filelist_geticon_ex(FileDirEntry *file, FSMenuEntry *tfsm = ED_fsmenu_get_category(fsmenu, categories[i]); char fullpath[FILE_MAX_LIBEXTRA]; BLI_join_dirfile(fullpath, sizeof(fullpath), root, file->relpath); - BLI_add_slash(fullpath); + BLI_path_slash_ensure(fullpath); for (; tfsm; tfsm = tfsm->next) { if (STREQ(tfsm->path, fullpath)) { /* Never want a little folder inside a large one. */ @@ -1112,7 +1112,7 @@ int filelist_geticon(struct FileList *filelist, const int index, const bool is_m static void parent_dir_until_exists_or_default_root(char *dir) { - if (!BLI_parent_dir_until_exists(dir)) { + if (!BLI_path_parent_dir_until_exists(dir)) { #ifdef WIN32 get_default_root(dir); #else @@ -1624,7 +1624,7 @@ void filelist_setdir(struct FileList *filelist, char *r_dir) { BLI_assert(strlen(r_dir) < FILE_MAX_LIBEXTRA); - BLI_cleanup_dir(BKE_main_blendfile_path_from_global(), r_dir); + BLI_path_normalize_dir(BKE_main_blendfile_path_from_global(), r_dir); const bool is_valid_path = filelist->checkdirf(filelist, r_dir, true); BLI_assert(is_valid_path); UNUSED_VARS_NDEBUG(is_valid_path); @@ -2448,7 +2448,7 @@ static int groupname_to_code(const char *group) BLI_assert(group); BLI_strncpy(buf, group, sizeof(buf)); - lslash = (char *)BLI_last_slash(buf); + lslash = (char *)BLI_path_slash_rfind(buf); if (lslash) { lslash[0] = '\0'; } @@ -2816,7 +2816,7 @@ static void filelist_readjob_do(const bool do_lib, BLI_strncpy(dir, filelist->filelist.root, sizeof(dir)); BLI_strncpy(filter_glob, filelist->filter_data.filter_glob, sizeof(filter_glob)); - BLI_cleanup_dir(main_name, dir); + BLI_path_normalize_dir(main_name, dir); td_dir->dir = BLI_strdup(dir); while (!BLI_stack_is_empty(todo_dirs) && !(*stop)) { @@ -2842,7 +2842,7 @@ static void filelist_readjob_do(const bool do_lib, * Note that in the end, this means we 'cache' valid relative subdir once here, * this is actually better. */ BLI_strncpy(rel_subdir, subdir, sizeof(rel_subdir)); - BLI_cleanup_dir(root, rel_subdir); + BLI_path_normalize_dir(root, rel_subdir); BLI_path_rel(rel_subdir, root); if (do_lib) { @@ -2884,7 +2884,7 @@ static void filelist_readjob_do(const bool do_lib, else { /* We have a directory we want to list, add it to todo list! */ BLI_join_dirfile(dir, sizeof(dir), root, entry->relpath); - BLI_cleanup_dir(main_name, dir); + BLI_path_normalize_dir(main_name, dir); td_dir = BLI_stack_push_r(todo_dirs); td_dir->level = recursion_level + 1; td_dir->dir = BLI_strdup(dir); diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c index 67c0fae3565..6b594c02c15 100644 --- a/source/blender/editors/space_file/filesel.c +++ b/source/blender/editors/space_file/filesel.c @@ -156,7 +156,7 @@ short ED_fileselect_set_params(SpaceFile *sfile) } if (params->dir[0]) { - BLI_cleanup_dir(blendfile_path, params->dir); + BLI_path_normalize_dir(blendfile_path, params->dir); BLI_path_abs(params->dir, blendfile_path); } @@ -924,7 +924,7 @@ int autocomplete_directory(struct bContext *C, char *str, void *UNUSED(arg_v)) match = UI_autocomplete_end(autocpl, str); if (match == AUTOCOMPLETE_FULL_MATCH) { - BLI_add_slash(str); + BLI_path_slash_ensure(str); } } } diff --git a/source/blender/editors/space_image/image_buttons.c b/source/blender/editors/space_image/image_buttons.c index 873091bd68d..3658ebae3a2 100644 --- a/source/blender/editors/space_image/image_buttons.c +++ b/source/blender/editors/space_image/image_buttons.c @@ -1276,7 +1276,7 @@ void uiTemplateImageInfo(uiLayout *layout, bContext *C, Image *ima, ImageUser *i } else if (ima->source == IMA_SRC_SEQUENCE && ibuf) { /* Image sequence frame number + filename */ - const char *filename = BLI_last_slash(ibuf->name); + const char *filename = BLI_path_slash_rfind(ibuf->name); filename = (filename == NULL) ? ibuf->name : filename + 1; BLI_snprintf(str, MAX_IMAGE_INFO_LEN, TIP_("Frame %d: %s"), framenr, filename); } diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c index 67e64c52051..14245327bdd 100644 --- a/source/blender/editors/space_image/image_ops.c +++ b/source/blender/editors/space_image/image_ops.c @@ -1794,7 +1794,8 @@ static int image_save_options_init(Main *bmain, } /* append UDIM numbering if not present */ - if (ima->source == IMA_SRC_TILED && (BLI_stringdec(ima->name, NULL, NULL, NULL) != 1001)) { + if (ima->source == IMA_SRC_TILED && + (BLI_path_sequence_decode(ima->name, NULL, NULL, NULL) != 1001)) { int len = strlen(opts->filepath); STR_CONCAT(opts->filepath, len, ".1001"); } diff --git a/source/blender/editors/space_image/image_sequence.c b/source/blender/editors/space_image/image_sequence.c index 8b298045597..e65924d0eb9 100644 --- a/source/blender/editors/space_image/image_sequence.c +++ b/source/blender/editors/space_image/image_sequence.c @@ -74,7 +74,7 @@ static void image_sequence_get_frame_ranges(wmOperator *op, ListBase *ranges) ImageFrame *frame = MEM_callocN(sizeof(ImageFrame), "image_frame"); /* use the first file in the list as base filename */ - frame->framenr = BLI_stringdec(filename, head, tail, &digits); + frame->framenr = BLI_path_sequence_decode(filename, head, tail, &digits); /* still in the same sequence */ if (do_frame_range && (range != NULL) && (STREQLEN(base_head, head, FILE_MAX)) && @@ -127,7 +127,7 @@ static int image_get_udim(char *filepath, ListBase *udim_tiles) ushort digits; char base_head[FILE_MAX], base_tail[FILE_MAX]; - int id = BLI_stringdec(filename, base_head, base_tail, &digits); + int id = BLI_path_sequence_decode(filename, base_head, base_tail, &digits); if (id < 1001 || id >= IMA_UDIM_MAX) { return 0; @@ -144,7 +144,7 @@ static int image_get_udim(char *filepath, ListBase *udim_tiles) continue; } char head[FILE_MAX], tail[FILE_MAX]; - id = BLI_stringdec(dir[i].relname, head, tail, &digits); + id = BLI_path_sequence_decode(dir[i].relname, head, tail, &digits); if (digits > 4 || !(STREQLEN(base_head, head, FILE_MAX)) || !(STREQLEN(base_tail, tail, FILE_MAX))) { @@ -166,7 +166,7 @@ static int image_get_udim(char *filepath, ListBase *udim_tiles) if (is_udim && has_primary) { char primary_filename[FILE_MAX]; - BLI_stringenc(primary_filename, base_head, base_tail, digits, 1001); + BLI_path_sequence_encode(primary_filename, base_head, base_tail, digits, 1001); BLI_join_dirfile(filepath, FILE_MAX, dirname, primary_filename); return max_udim - 1000; } diff --git a/source/blender/editors/space_node/node_group.c b/source/blender/editors/space_node/node_group.c index 0f296fd293a..2617384d046 100644 --- a/source/blender/editors/space_node/node_group.c +++ b/source/blender/editors/space_node/node_group.c @@ -198,11 +198,11 @@ static int node_group_ungroup(Main *bmain, bNodeTree *ntree, bNode *gnode) } /* wgroup is a temporary copy of the NodeTree we're merging in - * - all of wgroup's nodes are transferred across to their new home + * - all of wgroup's nodes are copied across to their new home * - ngroup (i.e. the source NodeTree) is left unscathed - * - temp copy. don't change ID usercount + * - temp copy. do change ID usercount for the copies */ - wgroup = ntreeCopyTree_ex_new_pointers(ngroup, bmain, false); + wgroup = ntreeCopyTree_ex_new_pointers(ngroup, bmain, true); /* Add the nodes into the ntree */ for (node = wgroup->nodes.first; node; node = nextnode) { @@ -356,8 +356,8 @@ static int node_group_ungroup(Main *bmain, bNodeTree *ntree, bNode *gnode) nodeRemoveNode(bmain, ntree, node, false); } - /* delete the group instance */ - nodeRemoveNode(bmain, ntree, gnode, false); + /* delete the group instance and dereference group tree */ + nodeRemoveNode(bmain, ntree, gnode, true); ntree->update |= NTREE_UPDATE_NODES | NTREE_UPDATE_LINKS; diff --git a/source/blender/editors/space_outliner/outliner_select.c b/source/blender/editors/space_outliner/outliner_select.c index ec1595eb930..64d86293fb7 100644 --- a/source/blender/editors/space_outliner/outliner_select.c +++ b/source/blender/editors/space_outliner/outliner_select.c @@ -312,6 +312,7 @@ static eOLDrawState tree_element_set_active_object(bContext *C, Scene *sce; Base *base; Object *ob = NULL; + TreeElement *te_ob = NULL; /* if id is not object, we search back */ if (te->idcode == ID_OB) { @@ -355,8 +356,12 @@ static eOLDrawState tree_element_set_active_object(bContext *C, } } - parent_tselem = TREESTORE(outliner_find_id(soops, &soops->tree, (ID *)ob)); - if (base) { + te_ob = outliner_find_id(soops, &soops->tree, (ID *)ob); + if (te_ob != NULL) { + parent_tselem = TREESTORE(te_ob); + } + + if (!ELEM(NULL, parent_tselem, base)) { if (set == OL_SETSEL_EXTEND) { /* swap select */ if (base->flag & BASE_SELECTED) { diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index 93ff5263148..5d8851d5e3d 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -220,6 +220,10 @@ void color3ubv_from_seq(Scene *curscene, Sequence *seq, uchar col[3]) } } +/** + * \param x1, x2, y1, y2: The starting and end X value to draw the wave, same for y1 and y2. + * \param stepsize: The width of a pixel. + */ static void draw_seq_waveform(View2D *v2d, const bContext *C, SpaceSeq *sseq, @@ -231,9 +235,6 @@ static void draw_seq_waveform(View2D *v2d, float y2, float stepsize) { - /* X1, x2 is the starting and end X value to draw the wave, same for y1 and y2. - * Stepsize is width of a pixel. */ - /* Offset x1 and x2 values, to match view min/max, if strip is out of bounds. */ int x1_offset = max_ff(v2d->cur.xmin, x1); int x2_offset = min_ff(v2d->cur.xmax + 1.0f, x2); @@ -353,10 +354,10 @@ static void draw_seq_waveform(View2D *v2d, } } -/* Don't use SEQ_BEGIN/SEQ_END here, because it changes seq->depth, - * which is needed for tranform. */ static void drawmeta_contents(Scene *scene, Sequence *seqm, float x1, float y1, float x2, float y2) { + /* Don't use SEQ_BEGIN/SEQ_END here, + * because it changes seq->depth, which is needed for transform. */ Sequence *seq; uchar col[4]; @@ -988,9 +989,11 @@ static void fcurve_batch_add_verts(GPUVertBuf *vbo, *vert_count += 2; } -/* Draw f-curves as darkened regions of the strip. - * - Volume for sound strips. - * - Opacity for the other types. */ +/** + * Draw f-curves as darkened regions of the strip: + * - Volume for sound strips. + * - Opacity for the other types. + */ static void draw_seq_fcurve( Scene *scene, View2D *v2d, Sequence *seq, float x1, float y1, float x2, float y2, float pixelx) { diff --git a/source/blender/editors/space_sequencer/sequencer_scopes.c b/source/blender/editors/space_sequencer/sequencer_scopes.c index b08c35623af..b06bc7ad8b7 100644 --- a/source/blender/editors/space_sequencer/sequencer_scopes.c +++ b/source/blender/editors/space_sequencer/sequencer_scopes.c @@ -451,8 +451,8 @@ typedef struct MakeHistogramViewData { } MakeHistogramViewData; static void make_histogram_view_from_ibuf_byte_fn(void *__restrict userdata, - const int y, - const TaskParallelTLS *__restrict tls) + const int y, + const TaskParallelTLS *__restrict tls) { MakeHistogramViewData *data = userdata; const ImBuf *ibuf = data->ibuf; @@ -552,8 +552,8 @@ BLI_INLINE int get_bin_float(float f) } static void make_histogram_view_from_ibuf_float_fn(void *__restrict userdata, - const int y, - const TaskParallelTLS *__restrict tls) + const int y, + const TaskParallelTLS *__restrict tls) { const MakeHistogramViewData *data = userdata; const ImBuf *ibuf = data->ibuf; diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c index c48c86bd423..c1e890ed5f1 100644 --- a/source/blender/editors/transform/transform.c +++ b/source/blender/editors/transform/transform.c @@ -1767,6 +1767,7 @@ void saveTransform(bContext *C, TransInfo *t, wmOperator *op) } else { orientation = V3D_ORIENT_GLOBAL; + unit_m3(t->spacemtx); } if ((prop = RNA_struct_find_property(op->ptr, "orient_axis"))) { diff --git a/source/blender/editors/transform/transform_convert.c b/source/blender/editors/transform/transform_convert.c index 1e751f67c9d..0beacf1244a 100644 --- a/source/blender/editors/transform/transform_convert.c +++ b/source/blender/editors/transform/transform_convert.c @@ -377,23 +377,25 @@ static short apply_targetless_ik(Object *ob) } for (; segcount; segcount--) { Bone *bone; - float rmat[4][4] /*, tmat[4][4], imat[4][4]*/; + float mat[4][4]; /* pose_mat(b) = pose_mat(b-1) * offs_bone * channel * constraint * IK */ - /* we put in channel the entire result of rmat = (channel * constraint * IK) */ - /* pose_mat(b) = pose_mat(b-1) * offs_bone * rmat */ - /* rmat = pose_mat(b) * inv(pose_mat(b-1) * offs_bone ) */ + /* we put in channel the entire result of mat = (channel * constraint * IK) */ + /* pose_mat(b) = pose_mat(b-1) * offs_bone * mat */ + /* mat = pose_mat(b) * inv(pose_mat(b-1) * offs_bone ) */ parchan = chanlist[segcount - 1]; bone = parchan->bone; bone->flag |= BONE_TRANSFORM; /* ensures it gets an auto key inserted */ - BKE_armature_mat_pose_to_bone(parchan, parchan->pose_mat, rmat); - + BKE_armature_mat_pose_to_bone(parchan, parchan->pose_mat, mat); /* apply and decompose, doesn't work for constraints or non-uniform scale well */ { float rmat3[3][3], qrmat[3][3], imat3[3][3], smat[3][3]; - copy_m3_m4(rmat3, rmat); + + copy_m3_m4(rmat3, mat); + /* Make sure that our rotation matrix only contains rotation and not scale. */ + normalize_m3(rmat3); /* rotation */ /* [#22409] is partially caused by this, as slight numeric error introduced during @@ -413,7 +415,7 @@ static short apply_targetless_ik(Object *ob) /* causes problems with some constraints (e.g. childof), so disable this */ /* as it is IK shouldn't affect location directly */ - /* copy_v3_v3(parchan->loc, rmat[3]); */ + /* copy_v3_v3(parchan->loc, mat[3]); */ } } diff --git a/source/blender/editors/undo/memfile_undo.c b/source/blender/editors/undo/memfile_undo.c index 086aa6c4c9d..ebd5b2272b1 100644 --- a/source/blender/editors/undo/memfile_undo.c +++ b/source/blender/editors/undo/memfile_undo.c @@ -25,14 +25,17 @@ #include "BLI_ghash.h" +#include "DNA_node_types.h" #include "DNA_object_enums.h" #include "DNA_object_types.h" +#include "DNA_scene_types.h" #include "BKE_blender_undo.h" #include "BKE_context.h" #include "BKE_lib_id.h" #include "BKE_lib_query.h" #include "BKE_main.h" +#include "BKE_node.h" #include "BKE_scene.h" #include "BKE_undo_system.h" @@ -106,7 +109,6 @@ static int memfile_undosys_step_id_reused_cb(LibraryIDLinkCallbackData *cb_data) ID *id_self = cb_data->id_self; ID **id_pointer = cb_data->id_pointer; BLI_assert((id_self->tag & LIB_TAG_UNDO_OLD_ID_REUSED) != 0); - Main *bmain = cb_data->user_data; ID *id = *id_pointer; if (id != NULL && id->lib == NULL && (id->tag & LIB_TAG_UNDO_OLD_ID_REUSED) == 0) { @@ -129,9 +131,6 @@ static int memfile_undosys_step_id_reused_cb(LibraryIDLinkCallbackData *cb_data) } } - /* In case an old, re-used ID is using a newly read data-block (i.e. one of its ID pointers got - * updated), we have to tell the depsgraph about it. */ - DEG_id_tag_update_ex(bmain, id_self, ID_RECALC_COPY_ON_WRITE); return do_stop_iter ? IDWALK_RET_STOP_ITER : IDWALK_RET_NOP; } @@ -219,10 +218,35 @@ static void memfile_undosys_step_decode(struct bContext *C, BKE_library_foreach_ID_link( bmain, id, memfile_undosys_step_id_reused_cb, bmain, IDWALK_READONLY); } + + /* Tag depsgraph to update data-block for changes that happened between the + * current and the target state, see direct_link_id_restore_recalc(). */ + if (id->recalc) { + DEG_id_tag_update_ex(bmain, id, id->recalc); + } } FOREACH_MAIN_ID_END; - BKE_main_id_tag_all(bmain, LIB_TAG_UNDO_OLD_ID_REUSED, false); + FOREACH_MAIN_ID_BEGIN (bmain, id) { + /* Clear temporary tag. */ + id->tag &= ~LIB_TAG_UNDO_OLD_ID_REUSED; + + /* We only start accumulating from this point, any tags set up to here + * are already part of the current undo state. This is done in a second + * loop because DEG_id_tag_update may set tags on other datablocks. */ + id->recalc_undo_accumulated = 0; + bNodeTree *nodetree = ntreeFromID(id); + if (nodetree != NULL) { + nodetree->id.recalc_undo_accumulated = 0; + } + if (GS(id->name) == ID_SCE) { + Scene *scene = (Scene *)id; + if (scene->master_collection != NULL) { + scene->master_collection->id.recalc_undo_accumulated = 0; + } + } + } + FOREACH_MAIN_ID_END; } WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, CTX_data_scene(C)); |