diff options
Diffstat (limited to 'source/blender/editors/armature')
18 files changed, 1402 insertions, 718 deletions
diff --git a/source/blender/editors/armature/armature_add.c b/source/blender/editors/armature/armature_add.c index e87040279af..895b4953992 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" @@ -48,6 +53,7 @@ #include "WM_types.h" #include "ED_armature.h" +#include "ED_outliner.h" #include "ED_screen.h" #include "ED_view3d.h" @@ -217,6 +223,7 @@ static int armature_click_extrude_exec(bContext *C, wmOperator *UNUSED(op)) WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit); DEG_id_tag_update(&obedit->id, ID_RECALC_SELECT); + ED_outliner_select_sync_from_edit_bone_tag(C); return OPERATOR_FINISHED; } @@ -341,7 +348,7 @@ void postEditBoneDuplicate(struct ListBase *editbones, Object *ob) GHash *name_map = BLI_ghash_str_new(__func__); - for (EditBone *ebone_src = editbones->first; ebone_src; ebone_src = ebone_src->next) { + LISTBASE_FOREACH (EditBone *, ebone_src, editbones) { EditBone *ebone_dst = ebone_src->temp.ebone; if (!ebone_dst) { ebone_dst = ED_armature_ebone_get_mirrored(editbones, ebone_src); @@ -351,7 +358,7 @@ void postEditBoneDuplicate(struct ListBase *editbones, Object *ob) } } - for (EditBone *ebone_src = editbones->first; ebone_src; ebone_src = ebone_src->next) { + LISTBASE_FOREACH (EditBone *, ebone_src, editbones) { EditBone *ebone_dst = ebone_src->temp.ebone; if (ebone_dst) { bPoseChannel *pchan_src = BKE_pose_channel_find_name(ob->pose, ebone_src->name); @@ -375,25 +382,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 +409,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 +420,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 +444,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 (BKE_fcurves_filter(&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 = BKE_fcurve_copy(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; } - ED_armature_ebone_unique_name(editbones, eBone->name, NULL); - BLI_addtail(editbones, eBone); + 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); + + 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 +880,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); } } } +} + +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)); + } - return eBone; + 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 +973,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 +1002,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 +1025,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); } } @@ -616,6 +1051,8 @@ static int armature_duplicate_selected_exec(bContext *C, wmOperator *op) } MEM_freeN(objects); + ED_outliner_select_sync_from_edit_bone_tag(C); + return OPERATOR_FINISHED; } @@ -742,12 +1179,24 @@ static int armature_symmetrize_exec(bContext *C, wmOperator *op) } } - /* Find the selected bones and duplicate them as needed, with mirrored name */ + /* 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)); @@ -765,7 +1214,7 @@ static int armature_symmetrize_exec(bContext *C, wmOperator *op) } } - /* Run through the list and fix the pointers */ + /* Run through the list and fix the pointers. */ for (ebone_iter = arm->edbo->first; ebone_iter && ebone_iter != ebone_first_dupe; ebone_iter = ebone_iter->next) { if (ebone_iter->temp.ebone) { @@ -793,7 +1242,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 +1257,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); } } @@ -1062,7 +1525,13 @@ static int armature_extrude_exec(bContext *C, wmOperator *op) } MEM_freeN(objects); - return changed_multi ? OPERATOR_FINISHED : OPERATOR_CANCELLED; + if (!changed_multi) { + return OPERATOR_CANCELLED; + } + + ED_outliner_select_sync_from_edit_bone_tag(C); + + return OPERATOR_FINISHED; } void ARMATURE_OT_extrude(wmOperatorType *ot) @@ -1116,7 +1585,7 @@ static int armature_bone_primitive_add_exec(bContext *C, wmOperator *op) ED_armature_edit_deselect_all(obedit); - /* Create a bone */ + /* Create a bone. */ bone = ED_armature_ebone_add(obedit->data, name); copy_v3_v3(bone->head, curs); @@ -1133,6 +1602,7 @@ static int armature_bone_primitive_add_exec(bContext *C, wmOperator *op) /* note, notifier might evolve */ WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit); DEG_id_tag_update(&obedit->id, ID_RECALC_SELECT); + ED_outliner_select_sync_from_edit_bone_tag(C); return OPERATOR_FINISHED; } @@ -1223,6 +1693,7 @@ static int armature_subdivide_exec(bContext *C, wmOperator *op) /* note, notifier might evolve */ WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit); DEG_id_tag_update(&obedit->id, ID_RECALC_SELECT); + ED_outliner_select_sync_from_edit_bone_tag(C); return OPERATOR_FINISHED; } @@ -1232,7 +1703,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_edit.c b/source/blender/editors/armature/armature_edit.c index b8378c95a60..a7a705a6202 100644 --- a/source/blender/editors/armature/armature_edit.c +++ b/source/blender/editors/armature/armature_edit.c @@ -54,6 +54,7 @@ #include "WM_types.h" #include "ED_armature.h" +#include "ED_outliner.h" #include "ED_screen.h" #include "ED_view3d.h" @@ -499,7 +500,7 @@ static int armature_roll_clear_exec(bContext *C, wmOperator *op) bArmature *arm = ob->data; bool changed = false; - for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { + LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) { if (EBONE_VISIBLE(arm, ebone) && EBONE_EDITABLE(ebone)) { /* Roll func is a callback which assumes that all is well. */ ebone->roll = roll; @@ -508,7 +509,7 @@ static int armature_roll_clear_exec(bContext *C, wmOperator *op) } if (arm->flag & ARM_MIRROR_EDIT) { - for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { + LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) { if ((EBONE_VISIBLE(arm, ebone) && EBONE_EDITABLE(ebone)) == 0) { EditBone *ebone_mirr = ED_armature_ebone_get_mirrored(arm->edbo, ebone); if (ebone_mirr && (EBONE_VISIBLE(arm, ebone_mirr) && EBONE_EDITABLE(ebone_mirr))) { @@ -889,213 +890,6 @@ void ARMATURE_OT_fill(wmOperatorType *ot) /** \} */ /* -------------------------------------------------------------------- */ -/** \name Merge Operator - * \{ */ - -/* this function merges between two bones, removes them and those in-between, - * and adjusts the parent relationships for those in-between - */ -static void bones_merge( - Object *obedit, EditBone *start, EditBone *end, EditBone *endchild, ListBase *chains) -{ - bArmature *arm = obedit->data; - EditBone *ebo, *ebone, *newbone; - LinkData *chain; - float head[3], tail[3]; - - /* check if same bone */ - if (start == end) { - if (G.debug & G_DEBUG) { - printf("Error: same bone!\n"); - printf("\tstart = %s, end = %s\n", start->name, end->name); - } - } - - /* step 1: add a new bone - * - head = head/tail of start (default head) - * - tail = head/tail of end (default tail) - * - parent = parent of start - */ - if ((start->flag & BONE_TIPSEL) && (start->flag & BONE_SELECTED) == 0) { - copy_v3_v3(head, start->tail); - } - else { - copy_v3_v3(head, start->head); - } - if ((end->flag & BONE_ROOTSEL) && (end->flag & BONE_SELECTED) == 0) { - copy_v3_v3(tail, end->head); - } - else { - copy_v3_v3(tail, end->tail); - } - newbone = add_points_bone(obedit, head, tail); - newbone->parent = start->parent; - - /* TODO, copy more things to the new bone */ - newbone->flag = start->flag & (BONE_HINGE | BONE_NO_DEFORM | BONE_NO_CYCLICOFFSET | - BONE_NO_LOCAL_LOCATION | BONE_DONE); - - newbone->inherit_scale_mode = start->inherit_scale_mode; - - /* Step 2a: reparent any side chains which may be parented to any bone in the chain - * of bones to merge - potentially several tips for side chains leading to some tree exist. - */ - for (chain = chains->first; chain; chain = chain->next) { - /* Traverse down chain until we hit the bottom or if we run into the tip of the chain of bones - * we're merging (need to stop in this case to avoid corrupting this chain too!). - */ - for (ebone = chain->data; (ebone) && (ebone != end); ebone = ebone->parent) { - short found = 0; - - /* Check if this bone is parented to one in the merging chain - * ! WATCHIT: must only go check until end of checking chain - */ - for (ebo = end; (ebo) && (ebo != start->parent); ebo = ebo->parent) { - /* side-chain found? --> remap parent to new bone, then we're done with this chain :) */ - if (ebone->parent == ebo) { - ebone->parent = newbone; - found = 1; - break; - } - } - - /* carry on to the next tip now */ - if (found) { - break; - } - } - } - - /* step 2b: parent child of end to newbone (child from this chain) */ - if (endchild) { - endchild->parent = newbone; - } - - /* step 3: delete all bones between and including start and end */ - for (ebo = end; ebo; ebo = ebone) { - ebone = (ebo == start) ? (NULL) : (ebo->parent); - bone_free(arm, ebo); - } - - newbone->flag |= (BONE_ROOTSEL | BONE_TIPSEL | BONE_SELECTED); - ED_armature_edit_sync_selection(arm->edbo); -} - -static int armature_merge_exec(bContext *C, wmOperator *op) -{ - ViewLayer *view_layer = CTX_data_view_layer(C); - const short type = RNA_enum_get(op->ptr, "type"); - - uint objects_len = 0; - Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( - view_layer, CTX_wm_view3d(C), &objects_len); - - for (uint ob_index = 0; ob_index < objects_len; ob_index++) { - Object *obedit = objects[ob_index]; - bArmature *arm = obedit->data; - - /* for now, there's only really one type of merging that's performed... */ - if (type == 1) { - /* go down chains, merging bones */ - ListBase chains = {NULL, NULL}; - LinkData *chain, *nchain; - EditBone *ebo; - - armature_tag_select_mirrored(arm); - - /* get chains (ends on chains) */ - chains_find_tips(arm->edbo, &chains); - if (BLI_listbase_is_empty(&chains)) { - continue; - } - - /* each 'chain' is the last bone in the chain (with no children) */ - for (chain = chains.first; chain; chain = nchain) { - EditBone *bstart = NULL, *bend = NULL; - EditBone *bchild = NULL, *child = NULL; - - /* temporarily remove chain from list of chains */ - nchain = chain->next; - BLI_remlink(&chains, chain); - - /* only consider bones that are visible and selected */ - for (ebo = chain->data; ebo; child = ebo, ebo = ebo->parent) { - /* check if visible + selected */ - if (EBONE_VISIBLE(arm, ebo) && ((ebo->flag & BONE_CONNECTED) || (ebo->parent == NULL)) && - (ebo->flag & BONE_SELECTED)) { - /* set either end or start (end gets priority, unless it is already set) */ - if (bend == NULL) { - bend = ebo; - bchild = child; - } - else { - bstart = ebo; - } - } - else { - /* chain is broken... merge any continuous segments then clear */ - if (bstart && bend) { - bones_merge(obedit, bstart, bend, bchild, &chains); - } - - bstart = NULL; - bend = NULL; - bchild = NULL; - } - } - - /* merge from bstart to bend if something not merged */ - if (bstart && bend) { - bones_merge(obedit, bstart, bend, bchild, &chains); - } - - /* put back link */ - BLI_insertlinkbefore(&chains, nchain, chain); - } - - armature_tag_unselect(arm); - - BLI_freelistN(&chains); - } - - /* updates */ - ED_armature_edit_sync_selection(arm->edbo); - ED_armature_edit_refresh_layer_used(arm); - WM_event_add_notifier(C, NC_OBJECT | ND_POSE, obedit); - DEG_id_tag_update(&arm->id, ID_RECALC_COPY_ON_WRITE); - } - MEM_freeN(objects); - - return OPERATOR_FINISHED; -} - -void ARMATURE_OT_merge(wmOperatorType *ot) -{ - static const EnumPropertyItem merge_types[] = { - {1, "WITHIN_CHAIN", 0, "Within Chains", ""}, - {0, NULL, 0, NULL, NULL}, - }; - - /* identifiers */ - ot->name = "Merge Bones"; - ot->idname = "ARMATURE_OT_merge"; - ot->description = "Merge continuous chains of selected bones"; - - /* callbacks */ - ot->invoke = WM_menu_invoke; - ot->exec = armature_merge_exec; - ot->poll = ED_operator_editarmature; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - ot->prop = RNA_def_enum(ot->srna, "type", merge_types, 0, "Type", ""); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ /** \name Switch Direction Operator * * Currently, this does not use context loops, as context loops do not make it @@ -1396,13 +1190,13 @@ static int armature_split_exec(bContext *C, wmOperator *UNUSED(op)) Object *ob = objects[ob_index]; bArmature *arm = ob->data; - for (EditBone *bone = arm->edbo->first; bone; bone = bone->next) { + LISTBASE_FOREACH (EditBone *, bone, arm->edbo) { if (bone->parent && (bone->flag & BONE_SELECTED) != (bone->parent->flag & BONE_SELECTED)) { bone->parent = NULL; bone->flag &= ~BONE_CONNECTED; } } - for (EditBone *bone = arm->edbo->first; bone; bone = bone->next) { + LISTBASE_FOREACH (EditBone *, bone, arm->edbo) { ED_armature_ebone_select_set(bone, (bone->flag & BONE_SELECTED) != 0); } @@ -1490,6 +1284,7 @@ static int armature_delete_selected_exec(bContext *C, wmOperator *UNUSED(op)) BKE_pose_tag_recalc(CTX_data_main(C), obedit->pose); WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit); DEG_id_tag_update(&arm->id, ID_RECALC_SELECT); + ED_outliner_select_sync_from_edit_bone_tag(C); } } MEM_freeN(objects); @@ -1665,6 +1460,7 @@ static int armature_dissolve_selected_exec(bContext *C, wmOperator *UNUSED(op)) ED_armature_edit_refresh_layer_used(arm); WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit); DEG_id_tag_update(&arm->id, ID_RECALC_SELECT); + ED_outliner_select_sync_from_edit_bone_tag(C); } } MEM_freeN(objects); @@ -1715,7 +1511,7 @@ static int armature_hide_exec(bContext *C, wmOperator *op) bArmature *arm = obedit->data; bool changed = false; - for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { + LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) { if (EBONE_VISIBLE(arm, ebone)) { if ((ebone->flag & BONE_SELECTED) != invert) { ebone->flag &= ~(BONE_TIPSEL | BONE_SELECTED | BONE_ROOTSEL); @@ -1774,7 +1570,7 @@ static int armature_reveal_exec(bContext *C, wmOperator *op) bArmature *arm = obedit->data; bool changed = false; - for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { + LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) { if (arm->layer & ebone->layer) { if (ebone->flag & BONE_HIDDEN_A) { if (!(ebone->flag & BONE_UNSELECTABLE)) { diff --git a/source/blender/editors/armature/armature_intern.h b/source/blender/editors/armature/armature_intern.h index fa562ab0f44..08d82bf13c9 100644 --- a/source/blender/editors/armature/armature_intern.h +++ b/source/blender/editors/armature/armature_intern.h @@ -59,6 +59,7 @@ void ARMATURE_OT_select_mirror(struct wmOperatorType *ot); void ARMATURE_OT_select_more(struct wmOperatorType *ot); void ARMATURE_OT_select_less(struct wmOperatorType *ot); void ARMATURE_OT_select_hierarchy(struct wmOperatorType *ot); +void ARMATURE_OT_select_linked_pick(struct wmOperatorType *ot); void ARMATURE_OT_select_linked(struct wmOperatorType *ot); void ARMATURE_OT_select_similar(struct wmOperatorType *ot); void ARMATURE_OT_shortest_path_pick(struct wmOperatorType *ot); @@ -72,7 +73,6 @@ void ARMATURE_OT_hide(struct wmOperatorType *ot); void ARMATURE_OT_reveal(struct wmOperatorType *ot); void ARMATURE_OT_click_extrude(struct wmOperatorType *ot); void ARMATURE_OT_fill(struct wmOperatorType *ot); -void ARMATURE_OT_merge(struct wmOperatorType *ot); void ARMATURE_OT_separate(struct wmOperatorType *ot); void ARMATURE_OT_split(struct wmOperatorType *ot); @@ -104,6 +104,7 @@ void POSE_OT_select_all(struct wmOperatorType *ot); void POSE_OT_select_parent(struct wmOperatorType *ot); void POSE_OT_select_hierarchy(struct wmOperatorType *ot); void POSE_OT_select_linked(struct wmOperatorType *ot); +void POSE_OT_select_linked_pick(struct wmOperatorType *ot); void POSE_OT_select_constraint_target(struct wmOperatorType *ot); void POSE_OT_select_grouped(struct wmOperatorType *ot); void POSE_OT_select_mirror(struct wmOperatorType *ot); @@ -232,9 +233,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 +242,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); @@ -258,16 +250,40 @@ void armature_select_mirrored_ex(struct bArmature *arm, const int flag); void armature_select_mirrored(struct bArmature *arm); void armature_tag_unselect(struct bArmature *arm); -void *get_nearest_bone(struct bContext *C, const int xy[2], bool findunsel, struct Base **r_base); - -void *get_bone_from_selectbuffer(struct Base **bases, - uint bases_len, - bool is_editmode, - const unsigned int *buffer, - short hits, +EditBone *ED_armature_pick_ebone(struct bContext *C, + const int xy[2], bool findunsel, - bool do_nearest, struct Base **r_base); +struct bPoseChannel *ED_armature_pick_pchan(struct bContext *C, + const int xy[2], + bool findunsel, + struct Base **r_base); +struct Bone *ED_armature_pick_bone(struct bContext *C, + const int xy[2], + bool findunsel, + struct Base **r_base); + +struct EditBone *ED_armature_pick_ebone_from_selectbuffer(struct Base **bases, + uint bases_len, + const uint *buffer, + short hits, + bool findunsel, + bool do_nearest, + struct Base **r_base); +struct bPoseChannel *ED_armature_pick_pchan_from_selectbuffer(struct Base **bases, + uint bases_len, + const uint *buffer, + short hits, + bool findunsel, + bool do_nearest, + struct Base **r_base); +struct Bone *ED_armature_pick_bone_from_selectbuffer(struct Base **bases, + uint bases_len, + const uint *buffer, + short hits, + bool findunsel, + bool do_nearest, + struct Base **r_base); int bone_looper(struct Object *ob, struct Bone *bone, diff --git a/source/blender/editors/armature/armature_naming.c b/source/blender/editors/armature/armature_naming.c index 13660244547..544d86d4c47 100644 --- a/source/blender/editors/armature/armature_naming.c +++ b/source/blender/editors/armature/armature_naming.c @@ -20,6 +20,8 @@ /** \file * \ingroup edarmature + * + * This file contains functions/API's for renaming bones and/or working with them. */ #include <string.h> @@ -63,12 +65,11 @@ #include "armature_intern.h" -/* This file contains functions/API's for renaming bones and/or working with them */ - -/* ************************************************** */ -/* EditBone Names */ +/* -------------------------------------------------------------------- */ +/** \name Unique Bone Name Utility (Edit Mode) + * \{ */ -/* note: there's a unique_bone_name() too! */ +/* note: there's a ed_armature_bone_unique_name() too! */ static bool editbone_unique_check(void *arg, const char *name) { struct { @@ -92,20 +93,29 @@ void ED_armature_ebone_unique_name(ListBase *edbo, char *name, EditBone *bone) BLI_uniquename_cb(editbone_unique_check, &data, DATA_("Bone"), '.', name, sizeof(bone->name)); } -/* ************************************************** */ -/* Bone Renaming - API */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Unique Bone Name Utility (Object Mode) + * \{ */ static bool bone_unique_check(void *arg, const char *name) { return BKE_armature_find_bone_name((bArmature *)arg, name) != NULL; } -static void unique_bone_name(bArmature *arm, char *name) +static void ed_armature_bone_unique_name(bArmature *arm, char *name) { BLI_uniquename_cb( bone_unique_check, (void *)arm, DATA_("Bone"), '.', name, sizeof(((Bone *)NULL)->name)); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Bone Renaming (Object & Edit Mode API) + * \{ */ + /* helper call for armature_bone_rename */ static void constraint_bone_name_fix(Object *ob, ListBase *conlist, @@ -180,7 +190,7 @@ void ED_armature_bone_rename(Main *bmain, Bone *bone = BKE_armature_find_bone_name(arm, oldname); if (bone) { - unique_bone_name(arm, newname); + ed_armature_bone_unique_name(arm, newname); if (arm->bonehash) { BLI_assert(BLI_ghash_haskey(arm->bonehash, bone->name)); @@ -255,7 +265,7 @@ void ED_armature_bone_rename(Main *bmain, } } - if (modifiers_usesArmature(ob, arm)) { + if (BKE_modifiers_uses_armature(ob, arm)) { bDeformGroup *dg = BKE_object_defgroup_find_name(ob, oldname); if (dg) { BLI_strncpy(dg->name, newname, MAXBONENAME); @@ -307,8 +317,7 @@ void ED_armature_bone_rename(Main *bmain, } } - for (GpencilModifierData *gp_md = ob->greasepencil_modifiers.first; gp_md; - gp_md = gp_md->next) { + LISTBASE_FOREACH (GpencilModifierData *, gp_md, &ob->greasepencil_modifiers) { switch (gp_md->type) { case eGpencilModifierType_Armature: { ArmatureGpencilModifierData *mmd = (ArmatureGpencilModifierData *)gp_md; @@ -352,11 +361,11 @@ void ED_armature_bone_rename(Main *bmain, { bScreen *screen; for (screen = bmain->screens.first; screen; screen = screen->id.next) { - ScrArea *sa; + ScrArea *area; /* add regions */ - for (sa = screen->areabase.first; sa; sa = sa->next) { + for (area = screen->areabase.first; area; area = area->next) { SpaceLink *sl; - for (sl = sa->spacedata.first; sl; sl = sl->next) { + for (sl = area->spacedata.first; sl; sl = sl->next) { if (sl->spacetype == SPACE_VIEW3D) { View3D *v3d = (View3D *)sl; if (v3d->ob_center && v3d->ob_center->data == arm) { @@ -372,6 +381,12 @@ void ED_armature_bone_rename(Main *bmain, } } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Bone Flipping (Object & Edit Mode API) + * \{ */ + typedef struct BoneFlipNameData { struct BoneFlipNameData *next, *prev; char *name; @@ -399,7 +414,7 @@ void ED_armature_bones_flip_names(Main *bmain, /* First pass: generate flip names, and blindly rename. * If rename did not yield expected result, * store both bone's name and expected flipped one into temp list for second pass. */ - for (LinkData *link = bones_names->first; link; link = link->next) { + LISTBASE_FOREACH (LinkData *, link, bones_names) { char name_flip[MAXBONENAME]; char *name = link->data; @@ -426,8 +441,11 @@ void ED_armature_bones_flip_names(Main *bmain, } } -/* ************************************************** */ -/* Bone Renaming - EditMode */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Flip Bone Names (Edit Mode Operator) + * \{ */ static int armature_flip_names_exec(bContext *C, wmOperator *op) { @@ -451,7 +469,7 @@ static int armature_flip_names_exec(bContext *C, wmOperator *op) ListBase bones_names = {NULL}; - for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { + LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) { if (EBONE_VISIBLE(arm, ebone)) { if (ebone->flag & BONE_SELECTED) { BLI_addtail(&bones_names, BLI_genericNodeN(ebone->name)); @@ -511,6 +529,12 @@ void ARMATURE_OT_flip_names(wmOperatorType *ot) "(WARNING: may result in incoherent naming in some cases)"); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Bone Auto Side Names (Edit Mode Operator) + * \{ */ + static int armature_autoside_names_exec(bContext *C, wmOperator *op) { ViewLayer *view_layer = CTX_data_view_layer(C); @@ -532,7 +556,7 @@ static int armature_autoside_names_exec(bContext *C, wmOperator *op) continue; } - for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { + LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) { if (EBONE_EDITABLE(ebone)) { /* We first need to do the flipped bone, then the original one. @@ -599,3 +623,5 @@ void ARMATURE_OT_autoside_names(wmOperatorType *ot) /* settings */ ot->prop = RNA_def_enum(ot->srna, "type", axis_items, 0, "Axis", "Axis tag names with"); } + +/** \} */ diff --git a/source/blender/editors/armature/armature_ops.c b/source/blender/editors/armature/armature_ops.c index a29d0f5f158..da1b29307b1 100644 --- a/source/blender/editors/armature/armature_ops.c +++ b/source/blender/editors/armature/armature_ops.c @@ -56,6 +56,7 @@ void ED_operatortypes_armature(void) WM_operatortype_append(ARMATURE_OT_select_less); WM_operatortype_append(ARMATURE_OT_select_hierarchy); WM_operatortype_append(ARMATURE_OT_select_linked); + WM_operatortype_append(ARMATURE_OT_select_linked_pick); WM_operatortype_append(ARMATURE_OT_select_similar); WM_operatortype_append(ARMATURE_OT_shortest_path_pick); @@ -68,7 +69,6 @@ void ED_operatortypes_armature(void) WM_operatortype_append(ARMATURE_OT_reveal); WM_operatortype_append(ARMATURE_OT_click_extrude); WM_operatortype_append(ARMATURE_OT_fill); - WM_operatortype_append(ARMATURE_OT_merge); WM_operatortype_append(ARMATURE_OT_separate); WM_operatortype_append(ARMATURE_OT_split); @@ -100,6 +100,7 @@ void ED_operatortypes_armature(void) WM_operatortype_append(POSE_OT_select_parent); WM_operatortype_append(POSE_OT_select_hierarchy); WM_operatortype_append(POSE_OT_select_linked); + WM_operatortype_append(POSE_OT_select_linked_pick); WM_operatortype_append(POSE_OT_select_constraint_target); WM_operatortype_append(POSE_OT_select_grouped); WM_operatortype_append(POSE_OT_select_mirror); diff --git a/source/blender/editors/armature/armature_relations.c b/source/blender/editors/armature/armature_relations.c index 2c2bf3cd283..d3d00fc44f2 100644 --- a/source/blender/editors/armature/armature_relations.c +++ b/source/blender/editors/armature/armature_relations.c @@ -37,11 +37,12 @@ #include "BLT_translation.h" #include "BKE_action.h" +#include "BKE_anim_data.h" #include "BKE_animsys.h" #include "BKE_armature.h" #include "BKE_constraint.h" #include "BKE_context.h" -#include "BKE_fcurve.h" +#include "BKE_fcurve_driver.h" #include "BKE_layer.h" #include "BKE_main.h" #include "BKE_report.h" @@ -57,6 +58,7 @@ #include "ED_armature.h" #include "ED_object.h" +#include "ED_outliner.h" #include "ED_screen.h" #include "UI_interface.h" @@ -280,7 +282,7 @@ int join_armature_exec(bContext *C, wmOperator *op) float mat[4][4], oimat[4][4]; bool ok = false; - /* Ensure we're not in editmode and that the active object is an armature*/ + /* Ensure we're not in edit-mode and that the active object is an armature. */ if (!ob_active || ob_active->type != OB_ARMATURE) { return OPERATOR_CANCELLED; } @@ -430,7 +432,6 @@ int join_armature_exec(bContext *C, wmOperator *op) ED_armature_from_edit(bmain, arm); ED_armature_edit_free(arm); - BKE_armature_refresh_layer_used(arm); DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene); @@ -568,7 +569,7 @@ static void separate_armature_bones(Main *bmain, Object *ob, const bool is_selec if (is_select == (EBONE_VISIBLE(arm, curbone) && (curbone->flag & BONE_SELECTED))) { /* clear the bone->parent var of any bone that had this as its parent */ - for (EditBone *ebo = arm->edbo->first; ebo; ebo = ebo->next) { + LISTBASE_FOREACH (EditBone *, ebo, arm->edbo) { if (ebo->parent == curbone) { ebo->parent = NULL; /* this is needed to prevent random crashes with in ED_armature_from_edit */ @@ -578,7 +579,7 @@ static void separate_armature_bones(Main *bmain, Object *ob, const bool is_selec } /* clear the pchan->parent var of any pchan that had this as its parent */ - for (bPoseChannel *pchn = ob->pose->chanbase.first; pchn; pchn = pchn->next) { + LISTBASE_FOREACH (bPoseChannel *, pchn, &ob->pose->chanbase) { if (pchn->parent == pchan) { pchn->parent = NULL; } @@ -629,7 +630,7 @@ static int separate_armature_exec(bContext *C, wmOperator *op) bArmature *arm_old = ob_old->data; bool has_selected_bone = false; bool has_selected_any = false; - for (EditBone *ebone = arm_old->edbo->first; ebone; ebone = ebone->next) { + LISTBASE_FOREACH (EditBone *, ebone, arm_old->edbo) { if (EBONE_VISIBLE(arm_old, ebone)) { if (ebone->flag & BONE_SELECTED) { has_selected_bone = true; @@ -687,9 +688,7 @@ static int separate_armature_exec(bContext *C, wmOperator *op) /* 5) restore original conditions */ ED_armature_to_edit(ob_old->data); - ED_armature_edit_refresh_layer_used(ob_old->data); - BKE_armature_refresh_layer_used(ob_new->data); /* parents tips remain selected when connected children are removed. */ ED_armature_edit_deselect_all(ob_old); @@ -706,6 +705,7 @@ static int separate_armature_exec(bContext *C, wmOperator *op) if (ok) { BKE_report(op->reports, RPT_INFO, "Separated bones"); + ED_outliner_select_sync_from_object_tag(C); } return OPERATOR_FINISHED; @@ -835,7 +835,7 @@ static int armature_parent_set_exec(bContext *C, wmOperator *op) bool is_active_only_selected = false; if (actbone->flag & BONE_SELECTED) { is_active_only_selected = true; - for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { + LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) { if (EBONE_EDITABLE(ebone) && (ebone->flag & BONE_SELECTED)) { if (ebone != actbone) { is_active_only_selected = false; @@ -867,7 +867,7 @@ static int armature_parent_set_exec(bContext *C, wmOperator *op) */ /* Parent selected bones to the active one. */ - for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { + LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) { if (EBONE_EDITABLE(ebone) && (ebone->flag & BONE_SELECTED)) { if (ebone != actbone) { bone_connect_to_new_parent(arm->edbo, ebone, actbone, val); @@ -901,7 +901,7 @@ static int armature_parent_set_invoke(bContext *C, Object *ob = CTX_data_edit_object(C); bArmature *arm = ob->data; EditBone *actbone = arm->act_edbone; - for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { + LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) { if (EBONE_EDITABLE(ebone) && (ebone->flag & BONE_SELECTED)) { if (ebone != actbone) { if (ebone->parent != actbone) { @@ -983,7 +983,7 @@ static int armature_parent_clear_exec(bContext *C, wmOperator *op) bArmature *arm = ob->data; bool changed = false; - for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { + LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) { if (EBONE_EDITABLE(ebone)) { changed = true; break; diff --git a/source/blender/editors/armature/armature_select.c b/source/blender/editors/armature/armature_select.c index 21eccd2ca1f..4b8bbe39a16 100644 --- a/source/blender/editors/armature/armature_select.c +++ b/source/blender/editors/armature/armature_select.c @@ -30,6 +30,7 @@ #include "BLI_blenlib.h" #include "BLI_math.h" +#include "BLI_rect.h" #include "BLI_string_utils.h" #include "BKE_action.h" @@ -59,7 +60,9 @@ #define EBONE_PREV_FLAG_GET(ebone) ((void)0, (ebone)->temp.i) #define EBONE_PREV_FLAG_SET(ebone, val) ((ebone)->temp.i = val) -/* **************** PoseMode & EditMode Selection Buffer Queries *************************** */ +/* -------------------------------------------------------------------- */ +/** \name Select Buffer Queries for PoseMode & EditMode + * \{ */ Base *ED_armature_base_and_ebone_from_select_buffer(Base **bases, uint bases_len, @@ -109,14 +112,14 @@ Object *ED_armature_object_and_ebone_from_select_buffer(Object **objects, return ob; } -Base *ED_armature_base_and_bone_from_select_buffer(Base **bases, - uint bases_len, - int hit, - Bone **r_bone) +Base *ED_armature_base_and_pchan_from_select_buffer(Base **bases, + uint bases_len, + int hit, + bPoseChannel **r_pchan) { const uint hit_object = hit & 0xFFFF; Base *base = NULL; - Bone *bone = NULL; + bPoseChannel *pchan = 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) { @@ -127,30 +130,53 @@ Base *ED_armature_base_and_bone_from_select_buffer(Base **bases, if (base != NULL) { if (base->object->pose != NULL) { const uint hit_bone = (hit & ~BONESEL_ANY) >> 16; - bPoseChannel *pchan = BLI_findlink(&base->object->pose->chanbase, hit_bone); - bone = pchan ? pchan->bone : NULL; + /* pchan may be NULL. */ + pchan = BLI_findlink(&base->object->pose->chanbase, hit_bone); } } - *r_bone = bone; + *r_pchan = pchan; + return base; +} + +/* For callers that don't need the pose channel. */ +Base *ED_armature_base_and_bone_from_select_buffer(Base **bases, + uint bases_len, + int hit, + Bone **r_bone) +{ + bPoseChannel *pchan = NULL; + Base *base = ED_armature_base_and_pchan_from_select_buffer(bases, bases_len, hit, &pchan); + *r_bone = pchan ? pchan->bone : NULL; return base; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Cursor Pick from Select Buffer API + * + * Internal #ed_armature_pick_bone_from_selectbuffer_impl is exposed as: + * - #ED_armature_pick_ebone_from_selectbuffer + * - #ED_armature_pick_pchan_from_selectbuffer + * - #ED_armature_pick_bone_from_selectbuffer + * \{ */ + /* See if there are any selected bones in this buffer */ /* only bones from base are checked on */ -void *get_bone_from_selectbuffer(Base **bases, - uint bases_len, - bool is_editmode, - const unsigned int *buffer, - short hits, - bool findunsel, - bool do_nearest, - Base **r_base) +static void *ed_armature_pick_bone_from_selectbuffer_impl(const bool is_editmode, + Base **bases, + uint bases_len, + const uint *buffer, + short hits, + bool findunsel, + bool do_nearest, + Base **r_base) { - Bone *bone; + bPoseChannel *pchan; EditBone *ebone; void *firstunSel = NULL, *firstSel = NULL, *data; Base *firstunSel_base = NULL, *firstSel_base = NULL; - unsigned int hitresult; + uint hitresult; short i; bool takeNext = false; int minsel = 0xffffffff, minunsel = 0xffffffff; @@ -158,77 +184,74 @@ void *get_bone_from_selectbuffer(Base **bases, for (i = 0; i < hits; i++) { hitresult = buffer[3 + (i * 4)]; - if (!(hitresult & BONESEL_NOSEL)) { - if (hitresult & BONESEL_ANY) { /* to avoid including objects in selection */ - Base *base = NULL; - bool sel; + if (hitresult & BONESEL_ANY) { /* to avoid including objects in selection */ + Base *base = NULL; + bool sel; - hitresult &= ~(BONESEL_ANY); - /* Determine what the current bone is */ - if (is_editmode == false) { - base = ED_armature_base_and_bone_from_select_buffer(bases, bases_len, hitresult, &bone); - if (bone != NULL) { - if (findunsel) { - sel = (bone->flag & BONE_SELECTED); - } - else { - sel = !(bone->flag & BONE_SELECTED); - } - - data = bone; + hitresult &= ~(BONESEL_ANY); + /* Determine what the current bone is */ + if (is_editmode == false) { + base = ED_armature_base_and_pchan_from_select_buffer(bases, bases_len, hitresult, &pchan); + if (pchan != NULL) { + if (findunsel) { + sel = (pchan->bone->flag & BONE_SELECTED); } else { - data = NULL; - sel = 0; + sel = !(pchan->bone->flag & BONE_SELECTED); } + + data = pchan; } else { - base = ED_armature_base_and_ebone_from_select_buffer( - bases, bases_len, hitresult, &ebone); - if (findunsel) { - sel = (ebone->flag & BONE_SELECTED); + data = NULL; + sel = 0; + } + } + else { + base = ED_armature_base_and_ebone_from_select_buffer(bases, bases_len, hitresult, &ebone); + if (findunsel) { + sel = (ebone->flag & BONE_SELECTED); + } + else { + sel = !(ebone->flag & BONE_SELECTED); + } + + data = ebone; + } + + if (data) { + if (sel) { + if (do_nearest) { + if (minsel > buffer[4 * i + 1]) { + firstSel = data; + firstSel_base = base; + minsel = buffer[4 * i + 1]; + } } else { - sel = !(ebone->flag & BONE_SELECTED); + if (!firstSel) { + firstSel = data; + firstSel_base = base; + } + takeNext = 1; } - - data = ebone; } - - if (data) { - if (sel) { - if (do_nearest) { - if (minsel > buffer[4 * i + 1]) { - firstSel = data; - firstSel_base = base; - minsel = buffer[4 * i + 1]; - } - } - else { - if (!firstSel) { - firstSel = data; - firstSel_base = base; - } - takeNext = 1; + else { + if (do_nearest) { + if (minunsel > buffer[4 * i + 1]) { + firstunSel = data; + firstunSel_base = base; + minunsel = buffer[4 * i + 1]; } } else { - if (do_nearest) { - if (minunsel > buffer[4 * i + 1]) { - firstunSel = data; - firstunSel_base = base; - minunsel = buffer[4 * i + 1]; - } + if (!firstunSel) { + firstunSel = data; + firstunSel_base = base; } - else { - if (!firstunSel) { - firstunSel = data; - firstunSel_base = base; - } - if (takeNext) { - *r_base = base; - return data; - } + if (takeNext) { + *r_base = base; + return data; } } } @@ -246,22 +269,74 @@ void *get_bone_from_selectbuffer(Base **bases, } } -/* used by posemode as well editmode */ -/* only checks scene->basact! */ -/* x and y are mouse coords (area space) */ -void *get_nearest_bone(bContext *C, const int xy[2], bool findunsel, Base **r_base) +EditBone *ED_armature_pick_ebone_from_selectbuffer(Base **bases, + uint bases_len, + const uint *buffer, + short hits, + bool findunsel, + bool do_nearest, + Base **r_base) +{ + const bool is_editmode = true; + return ed_armature_pick_bone_from_selectbuffer_impl( + is_editmode, bases, bases_len, buffer, hits, findunsel, do_nearest, r_base); +} + +bPoseChannel *ED_armature_pick_pchan_from_selectbuffer(Base **bases, + uint bases_len, + const uint *buffer, + short hits, + bool findunsel, + bool do_nearest, + Base **r_base) +{ + const bool is_editmode = false; + return ed_armature_pick_bone_from_selectbuffer_impl( + is_editmode, bases, bases_len, buffer, hits, findunsel, do_nearest, r_base); +} + +Bone *ED_armature_pick_bone_from_selectbuffer(Base **bases, + uint bases_len, + const uint *buffer, + short hits, + bool findunsel, + bool do_nearest, + Base **r_base) +{ + bPoseChannel *pchan = ED_armature_pick_pchan_from_selectbuffer( + bases, bases_len, buffer, hits, findunsel, do_nearest, r_base); + return pchan ? pchan->bone : NULL; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Cursor Pick API + * + * Internal #ed_armature_pick_bone_impl is exposed as: + * - #ED_armature_pick_ebone + * - #ED_armature_pick_pchan + * - #ED_armature_pick_bone + * \{ */ + +/** + * \param xy: Cursor coordinates (area space). + * \return An #EditBone when is_editmode, otherwise a #bPoseChannel. + * \note Only checks objects in the current mode (edit-mode or pose-mode). + */ +static void *ed_armature_pick_bone_impl( + const bool is_editmode, bContext *C, const int xy[2], bool findunsel, Base **r_base) { Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); ViewContext vc; rcti rect; - unsigned int buffer[MAXPICKBUF]; + uint buffer[MAXPICKBUF]; short hits; ED_view3d_viewcontext_init(C, &vc, depsgraph); + BLI_assert((vc.obedit != NULL) == is_editmode); - // rect.xmin = ... mouseco! - rect.xmin = rect.xmax = xy[0]; - rect.ymin = rect.ymax = xy[1]; + BLI_rcti_init_pt_radius(&rect, xy, 0); hits = view3d_opengl_select( &vc, buffer, MAXPICKBUF, &rect, VIEW3D_SELECT_PICK_NEAREST, VIEW3D_SELECT_FILTER_NOP); @@ -284,123 +359,287 @@ void *get_nearest_bone(bContext *C, const int xy[2], bool findunsel, Base **r_ba bases = BKE_object_pose_base_array_get(vc.view_layer, vc.v3d, &bases_len); } - void *bone = get_bone_from_selectbuffer( - bases, bases_len, vc.obedit != NULL, buffer, hits, findunsel, true, r_base); + void *bone = ed_armature_pick_bone_from_selectbuffer_impl( + is_editmode, bases, bases_len, buffer, hits, findunsel, true, r_base); MEM_freeN(bases); + return bone; } return NULL; } -/* **************** EditMode stuff ********************** */ +EditBone *ED_armature_pick_ebone(bContext *C, const int xy[2], bool findunsel, Base **r_base) +{ + const bool is_editmode = true; + return ed_armature_pick_bone_impl(is_editmode, C, xy, findunsel, r_base); +} -static int armature_select_linked_invoke(bContext *C, wmOperator *op, const wmEvent *event) +bPoseChannel *ED_armature_pick_pchan(bContext *C, const int xy[2], bool findunsel, Base **r_base) { - bArmature *arm; - EditBone *bone, *curBone, *next; - const bool sel = !RNA_boolean_get(op->ptr, "deselect"); + const bool is_editmode = false; + return ed_armature_pick_bone_impl(is_editmode, C, xy, findunsel, r_base); +} - view3d_operator_needs_opengl(C); - BKE_object_update_select_id(CTX_data_main(C)); +Bone *ED_armature_pick_bone(bContext *C, const int xy[2], bool findunsel, Base **r_base) +{ + bPoseChannel *pchan = ED_armature_pick_pchan(C, xy, findunsel, r_base); + return pchan ? pchan->bone : NULL; +} - Base *base = NULL; - bone = get_nearest_bone(C, event->mval, true, &base); +/** \} */ - if (!bone) { - return OPERATOR_CANCELLED; +/* -------------------------------------------------------------------- */ +/** \name Select Linked Implementation + * + * Shared logic for select linked all/pick. + * + * Use #BONE_DONE flag to select linked. + * \{ */ + +/** + * \param all_forks: Control how chains are stepped over. + * true: select all connected bones traveling up & down forks. + * false: select all parents and all children, but not the children of the root bone. + */ +static bool armature_select_linked_impl(Object *ob, const bool select, const bool all_forks) +{ + bool changed = false; + bArmature *arm = ob->data; + + /* Implementation note, this flood-fills selected bones with the 'TOUCH' flag, + * even though this is a loop-within a loop, walking up the parent chain only touches new bones. + * Bones that have been touched are skipped, so the complexity is OK. */ + + enum { + /* Bone has been walked over, it's LINK value can be read. */ + TOUCH = (1 << 0), + /* When TOUCH has been set, this flag can be checked to see if the bone is connected. */ + LINK = (1 << 1), + }; + +#define CHECK_PARENT(ebone) \ + (((ebone)->flag & BONE_CONNECTED) && \ + ((ebone)->parent ? EBONE_SELECTABLE(arm, (ebone)->parent) : false)) + + LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) { + ebone->temp.i = 0; } - arm = base->object->data; + /* Select parents. */ + LISTBASE_FOREACH (EditBone *, ebone_iter, arm->edbo) { + if (ebone_iter->temp.i & TOUCH) { + continue; + } + if ((ebone_iter->flag & BONE_DONE) == 0) { + continue; + } + + ebone_iter->temp.i |= TOUCH | LINK; - /* Select parents */ - for (curBone = bone; curBone; curBone = next) { - if ((curBone->flag & BONE_UNSELECTABLE) == 0) { - if (sel) { - curBone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + /* We have an un-touched link. */ + for (EditBone *ebone = ebone_iter; ebone; ebone = CHECK_PARENT(ebone) ? ebone->parent : NULL) { + ED_armature_ebone_select_set(ebone, select); + changed = true; + + if (all_forks) { + ebone->temp.i |= (TOUCH | LINK); } else { - curBone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + ebone->temp.i |= TOUCH; + } + /* Don't walk onto links (messes up 'all_forks' logic). */ + if (ebone->parent && ebone->parent->temp.i & LINK) { + break; } } + } - if (curBone->flag & BONE_CONNECTED) { - next = curBone->parent; + /* Select children. */ + LISTBASE_FOREACH (EditBone *, ebone_iter, arm->edbo) { + /* No need to 'touch' this bone as it won't be walked over when scanning up the chain. */ + if (!CHECK_PARENT(ebone_iter)) { + continue; } - else { - next = NULL; + if (ebone_iter->temp.i & TOUCH) { + continue; } - } - /* Select children */ - while (bone) { - for (curBone = arm->edbo->first; curBone; curBone = next) { - next = curBone->next; - if ((curBone->parent == bone) && (curBone->flag & BONE_UNSELECTABLE) == 0) { - if (curBone->flag & BONE_CONNECTED) { - if (sel) { - curBone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - } - else { - curBone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - } - bone = curBone; - break; - } - else { - bone = NULL; - break; + /* First check if we're marked. */ + EditBone *ebone_touched_parent = NULL; + for (EditBone *ebone = ebone_iter; ebone; ebone = CHECK_PARENT(ebone) ? ebone->parent : NULL) { + if (ebone->temp.i & TOUCH) { + ebone_touched_parent = ebone; + break; + } + ebone->temp.i |= TOUCH; + } + + if ((ebone_touched_parent != NULL) && (ebone_touched_parent->temp.i & LINK)) { + for (EditBone *ebone = ebone_iter; ebone != ebone_touched_parent; ebone = ebone->parent) { + if ((ebone->temp.i & LINK) == 0) { + ebone->temp.i |= LINK; + ED_armature_ebone_select_set(ebone, select); + changed = true; } } } - if (!curBone) { - bone = NULL; + } + +#undef CHECK_PARENT + + if (changed) { + ED_armature_edit_sync_selection(arm->edbo); + DEG_id_tag_update(&arm->id, ID_RECALC_COPY_ON_WRITE); + WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, ob); + } + + return changed; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Select Linked Operator + * \{ */ + +static int armature_select_linked_exec(bContext *C, wmOperator *op) +{ + const bool all_forks = RNA_boolean_get(op->ptr, "all_forks"); + + bool changed_multi = false; + 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( + view_layer, CTX_wm_view3d(C), &objects_len); + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *ob = objects[ob_index]; + bArmature *arm = ob->data; + + bool found = false; + LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) { + if (EBONE_VISIBLE(arm, ebone) && + (ebone->flag & (BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL))) { + ebone->flag |= BONE_DONE; + found = true; + } + else { + ebone->flag &= ~BONE_DONE; + } + } + + if (found) { + if (armature_select_linked_impl(ob, true, all_forks)) { + changed_multi = true; + } } } + MEM_freeN(objects); - ED_outliner_select_sync_from_edit_bone_tag(C); + if (changed_multi) { + ED_outliner_select_sync_from_edit_bone_tag(C); + } + return OPERATOR_FINISHED; +} - ED_armature_edit_sync_selection(arm->edbo); +void ARMATURE_OT_select_linked(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Select Linked All"; + ot->idname = "ARMATURE_OT_select_linked"; + ot->description = "Select all bones linked by parent/child connections to the current selection"; - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, base->object); - DEG_id_tag_update(&arm->id, ID_RECALC_COPY_ON_WRITE); + /* api callbacks */ + ot->exec = armature_select_linked_exec; + ot->poll = ED_operator_editarmature; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* Leave disabled by default as this matches pose mode. */ + RNA_def_boolean(ot->srna, "all_forks", 0, "All Forks", "Follow forks in the parents chain"); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Select Linked (Cursor Pick) Operator + * \{ */ + +static int armature_select_linked_pick_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + const bool select = !RNA_boolean_get(op->ptr, "deselect"); + const bool all_forks = RNA_boolean_get(op->ptr, "all_forks"); + + view3d_operator_needs_opengl(C); + BKE_object_update_select_id(CTX_data_main(C)); + + Base *base = NULL; + EditBone *ebone_active = ED_armature_pick_ebone(C, event->mval, true, &base); + + if (ebone_active == NULL) { + return OPERATOR_CANCELLED; + } + + bArmature *arm = base->object->data; + if (!EBONE_SELECTABLE(arm, ebone_active)) { + return OPERATOR_CANCELLED; + } + + /* Initialize flags. */ + LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) { + ebone->flag &= ~BONE_DONE; + } + ebone_active->flag |= BONE_DONE; + + if (armature_select_linked_impl(base->object, select, all_forks)) { + ED_outliner_select_sync_from_edit_bone_tag(C); + } return OPERATOR_FINISHED; } -static bool armature_select_linked_poll(bContext *C) +static bool armature_select_linked_pick_poll(bContext *C) { return (ED_operator_view3d_active(C) && ED_operator_editarmature(C)); } -void ARMATURE_OT_select_linked(wmOperatorType *ot) +void ARMATURE_OT_select_linked_pick(wmOperatorType *ot) { /* identifiers */ - ot->name = "Select Connected"; - ot->idname = "ARMATURE_OT_select_linked"; - ot->description = "Select bones related to selected ones by parent/child relationships"; + ot->name = "Select Linked"; + ot->idname = "ARMATURE_OT_select_linked_pick"; + ot->description = "(De)select bones linked by parent/child connections under the mouse cursor"; /* api callbacks */ /* leave 'exec' unset */ - ot->invoke = armature_select_linked_invoke; - ot->poll = armature_select_linked_poll; + ot->invoke = armature_select_linked_pick_invoke; + ot->poll = armature_select_linked_pick_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", ""); + /* Leave disabled by default as this matches pose mode. */ + RNA_def_boolean(ot->srna, "all_forks", 0, "All Forks", "Follow forks in the parents chain"); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Select Buffer Queries EditMode + * \{ */ + /* utility function for get_nearest_editbonepoint */ -static int selectbuffer_ret_hits_12(unsigned int *UNUSED(buffer), const int hits12) +static int selectbuffer_ret_hits_12(uint *UNUSED(buffer), const int hits12) { return hits12; } -static int selectbuffer_ret_hits_5(unsigned int *buffer, const int hits12, const int hits5) +static int selectbuffer_ret_hits_5(uint *buffer, const int hits12, const int hits5) { const int offs = 4 * hits12; - memcpy(buffer, buffer + offs, 4 * hits5 * sizeof(unsigned int)); + memcpy(buffer, buffer + offs, 4 * hits5 * sizeof(uint)); return hits5; } @@ -414,45 +653,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 = -1, .base = NULL, .ebone = NULL}, + result_bias = {.hitresult = -1, .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; @@ -506,87 +728,144 @@ cache_end: /* See if there are any selected bones in this group */ 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 + * - Offset is always set to the active bone. + * - The object & bone indices subtracted by the 'offset.as_u32' value. + * Unsigned subtraction wrapping means we always select the next bone in the cycle. + */ + struct { + union { + uint32_t as_u32; + struct { +#ifdef __BIG_ENDIAN__ + uint16_t ob; + uint16_t bone; +#else + uint16_t bone; + uint16_t ob; +#endif + }; + } offset, 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); + /* Offset from the current active bone, so we cycle onto the next. */ + cycle_order.offset.ob = ob_index; + cycle_order.offset.bone = bone_index; + /* The value of the active bone (with offset subtracted, a signal to always overwrite). */ + cycle_order.best.as_u32 = 0; + } + for (int i = 0; i < hits; i++) { const uint hitresult = buffer[3 + (i * 4)]; - if (!(hitresult & BONESEL_NOSEL)) { - Base *base = NULL; - EditBone *ebone; - base = ED_armature_base_and_ebone_from_select_buffer( - bases, bases_len, hitresult, &ebone); - /* If this fails, selection code is setting the selection ID's incorrectly. */ - BLI_assert(base && ebone); - - int dep; + + Base *base = NULL; + EditBone *ebone; + base = ED_armature_base_and_ebone_from_select_buffer(bases, bases_len, hitresult, &ebone); + /* If this fails, selection code is setting the selection ID's incorrectly. */ + BLI_assert(base && ebone); + + /* 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) { - dep = 1; + bias = 4; } else if ((hitresult & BONESEL_TIP) && (ebone->flag & BONE_TIPSEL) == 0) { - dep = 1; + bias = 4; } else { - dep = 2; + bias = 3; } } else { - dep = 1; + bias = 4; } } else { /* bone found */ if (findunsel) { if ((ebone->flag & BONE_SELECTED) == 0) { - dep = 3; + bias = 2; } else { - dep = 4; + bias = 1; } } else { - dep = 3; + bias = 2; } } - if (ebone == ebone_next_act) { - dep -= 1; + if (bias > bias_max) { + bias_max = bias; + + result_bias.hitresult = hitresult; + result_bias.base = base; + result_bias.ebone = ebone; } + } - if (dep < dep_min) { - dep_min = dep; - best.hitresult = hitresult; - best.base = base; - best.ebone = ebone; + /* Cycle selected items (objects & bones). */ + if (use_cycle) { + cycle_order.test.ob = hitresult & 0xFFFF; + cycle_order.test.bone = (hitresult & ~BONESEL_ANY) >> 16; + if (ebone == ebone_active_orig) { + BLI_assert(cycle_order.test.ob == cycle_order.offset.ob); + BLI_assert(cycle_order.test.bone == cycle_order.offset.bone); + } + /* Subtraction as a single value is needed to support cycling through bones + * from multiple objects. So once the last bone is selected, + * the bits for the bone index wrap into the object, + * causing the next object to be stepped onto. */ + cycle_order.test.as_u32 -= cycle_order.offset.as_u32; + + /* Even though this logic avoids stepping onto the active bone, + * always set the 'best' value for the first time. + * Otherwise ensure the value is the smallest it can be, + * relative to the active bone, as long as it's not the active bone. */ + if ((cycle_order.best.as_u32 == 0) || + (cycle_order.test.as_u32 && (cycle_order.test.as_u32 < cycle_order.best.as_u32))) { + cycle_order.best = cycle_order.test; + 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 != -1) { + *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; @@ -595,11 +874,17 @@ cache_end: return NULL; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Select Utility Functions + * \{ */ + bool ED_armature_edit_deselect_all(Object *obedit) { bArmature *arm = obedit->data; bool changed = false; - for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { + LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) { if (ebone->flag & (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL)) { ebone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); changed = true; @@ -612,7 +897,7 @@ bool ED_armature_edit_deselect_all_visible(Object *obedit) { bArmature *arm = obedit->data; bool changed = false; - for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { + LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) { /* first and foremost, bone must be visible and selected */ if (EBONE_VISIBLE(arm, ebone)) { if (ebone->flag & (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL)) { @@ -661,17 +946,11 @@ bool ED_armature_edit_deselect_all_visible_multi(bContext *C) return changed_multi; } -/* accounts for connected parents */ -static int ebone_select_flag(EditBone *ebone) -{ - if (ebone->parent && (ebone->flag & BONE_CONNECTED)) { - return ((ebone->parent->flag & BONE_TIPSEL) ? BONE_ROOTSEL : 0) | - (ebone->flag & (BONE_SELECTED | BONE_TIPSEL)); - } - else { - return ebone->flag & (BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL); - } -} +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Select Cursor Pick API + * \{ */ /* context: editmode armature in view3d */ bool ED_armature_edit_select_pick( @@ -784,7 +1063,7 @@ bool ED_armature_edit_select_pick( ED_armature_edit_sync_selection(arm->edbo); /* then now check for active status */ - if (ebone_select_flag(nearBone)) { + if (ED_armature_ebone_selectflag_get(nearBone)) { arm->act_edbone = nearBone; } @@ -800,6 +1079,8 @@ bool ED_armature_edit_select_pick( return false; } +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Select Op From Tagged * @@ -893,7 +1174,7 @@ bool ED_armature_edit_select_op_from_tagged(bArmature *arm, const int sel_op) /* Initialize flags. */ { - for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { + LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) { /* Flush the parent flag to this bone * so we don't need to check the parent when adjusting the selection. */ @@ -915,7 +1196,7 @@ bool ED_armature_edit_select_op_from_tagged(bArmature *arm, const int sel_op) } /* Apply selection from bone selection flags. */ - for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { + LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) { if (ebone->temp.i != 0) { int is_ignore_flag = ((ebone->temp.i << 16) & (BONESEL_ROOT | BONESEL_TIP)); int is_inside_flag = (ebone->temp.i & (BONESEL_ROOT | BONESEL_TIP | BONESEL_BONE)); @@ -935,7 +1216,7 @@ bool ED_armature_edit_select_op_from_tagged(bArmature *arm, const int sel_op) if (changed) { /* Cleanup flags. */ - for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { + LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) { if (ebone->flag & BONE_DONE) { SWAP(int, ebone->temp.i, ebone->flag); ebone->flag |= BONE_DONE; @@ -948,7 +1229,7 @@ bool ED_armature_edit_select_op_from_tagged(bArmature *arm, const int sel_op) } } - for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { + LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) { if (ebone->flag & BONE_DONE) { if ((ebone->flag & BONE_CONNECTED) && ebone->parent) { bool is_parent_tip_changed = (ebone->parent->flag & BONE_TIPSEL) != @@ -984,7 +1265,9 @@ bool ED_armature_edit_select_op_from_tagged(bArmature *arm, const int sel_op) /** \} */ -/* **************** Selections ******************/ +/* -------------------------------------------------------------------- */ +/** \name (De)Select All Operator + * \{ */ static int armature_de_select_all_exec(bContext *C, wmOperator *op) { @@ -1003,7 +1286,7 @@ static int armature_de_select_all_exec(bContext *C, wmOperator *op) CTX_DATA_END; } - /* Set the flags */ + /* Set the flags. */ CTX_DATA_BEGIN (C, EditBone *, ebone, visible_bones) { /* ignore bone if selection can't change */ switch (action) { @@ -1063,7 +1346,11 @@ void ARMATURE_OT_select_all(wmOperatorType *ot) WM_operator_properties_select_all(ot); } -/**************** Select more/less **************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Select More/Less Implementation + * \{ */ static void armature_select_more(bArmature *arm, EditBone *ebone) { @@ -1150,6 +1437,12 @@ static void armature_select_more_less(Object *ob, bool more) ED_armature_edit_sync_selection(arm->edbo); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Select More Operator + * \{ */ + static int armature_de_select_more_exec(bContext *C, wmOperator *UNUSED(op)) { ViewLayer *view_layer = CTX_data_view_layer(C); @@ -1183,6 +1476,12 @@ void ARMATURE_OT_select_more(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Select Less Operator + * \{ */ + static int armature_de_select_less_exec(bContext *C, wmOperator *UNUSED(op)) { ViewLayer *view_layer = CTX_data_view_layer(C); @@ -1216,6 +1515,12 @@ void ARMATURE_OT_select_less(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Select Similar + * \{ */ + enum { SIMEDBONE_CHILDREN = 1, SIMEDBONE_CHILDREN_IMMEDIATE, @@ -1270,7 +1575,7 @@ static void select_similar_length(bContext *C, const float thresh) bArmature *arm = ob->data; bool changed = false; - for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { + LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) { if (EBONE_SELECTABLE(arm, ebone)) { const float len_iter = bone_length_squared_worldspace_get(ob, ebone); if ((len_iter > len_min) && (len_iter < len_max)) { @@ -1318,7 +1623,7 @@ static void select_similar_direction(bContext *C, const float thresh) bArmature *arm = ob->data; bool changed = false; - for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { + LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) { if (EBONE_SELECTABLE(arm, ebone)) { float dir[3]; bone_direction_worldspace_get(ob, ebone, dir); @@ -1352,7 +1657,7 @@ static void select_similar_layer(bContext *C) bArmature *arm = ob->data; bool changed = false; - for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { + LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) { if (EBONE_SELECTABLE(arm, ebone)) { if (ebone->layer & ebone_act->layer) { ED_armature_ebone_select_set(ebone, true); @@ -1392,7 +1697,7 @@ static void select_similar_prefix(bContext *C) bool changed = false; /* Find matches */ - for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { + LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) { if (EBONE_SELECTABLE(arm, ebone)) { char prefix_other[MAXBONENAME]; BLI_string_split_prefix(ebone->name, prefix_other, body_tmp, sizeof(ebone->name)); @@ -1434,7 +1739,7 @@ static void select_similar_suffix(bContext *C) bool changed = false; /* Find matches */ - for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { + LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) { if (EBONE_SELECTABLE(arm, ebone)) { char suffix_other[MAXBONENAME]; BLI_string_split_suffix(ebone->name, body_tmp, suffix_other, sizeof(ebone->name)); @@ -1469,7 +1774,7 @@ static void select_similar_data_pchan(bContext *C, const size_t bytes_size, cons } const char *data_active = (const char *)POINTER_OFFSET(pchan_active, offset); - for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { + LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) { if (EBONE_SELECTABLE(arm, ebone)) { const bPoseChannel *pchan = BKE_pose_channel_find_name(obedit->pose, ebone->name); if (pchan) { @@ -1504,11 +1809,11 @@ static void select_similar_children(bContext *C) bArmature *arm = obedit->data; EditBone *ebone_act = CTX_data_active_bone(C); - for (EditBone *ebone_iter = arm->edbo->first; ebone_iter; ebone_iter = ebone_iter->next) { + LISTBASE_FOREACH (EditBone *, ebone_iter, arm->edbo) { ebone_iter->temp.ebone = ebone_iter->parent; } - for (EditBone *ebone_iter = arm->edbo->first; ebone_iter; ebone_iter = ebone_iter->next) { + LISTBASE_FOREACH (EditBone *, ebone_iter, arm->edbo) { is_ancestor(ebone_iter, ebone_act); if (ebone_iter->temp.ebone == ebone_act && EBONE_SELECTABLE(arm, ebone_iter)) { @@ -1526,7 +1831,7 @@ static void select_similar_children_immediate(bContext *C) bArmature *arm = obedit->data; EditBone *ebone_act = CTX_data_active_bone(C); - for (EditBone *ebone_iter = arm->edbo->first; ebone_iter; ebone_iter = ebone_iter->next) { + LISTBASE_FOREACH (EditBone *, ebone_iter, arm->edbo) { if (ebone_iter->parent == ebone_act && EBONE_SELECTABLE(arm, ebone_iter)) { ED_armature_ebone_select_set(ebone_iter, true); } @@ -1546,7 +1851,7 @@ static void select_similar_siblings(bContext *C) return; } - for (EditBone *ebone_iter = arm->edbo->first; ebone_iter; ebone_iter = ebone_iter->next) { + LISTBASE_FOREACH (EditBone *, ebone_iter, arm->edbo) { if (ebone_iter->parent == ebone_act->parent && EBONE_SELECTABLE(arm, ebone_iter)) { ED_armature_ebone_select_set(ebone_iter, true); } @@ -1631,7 +1936,11 @@ void ARMATURE_OT_select_similar(wmOperatorType *ot) RNA_def_float(ot->srna, "threshold", 0.1f, 0.0f, 1.0f, "Threshold", "", 0.0f, 1.0f); } -/* ********************* select hierarchy operator ************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Select Hierarchy Operator + * \{ */ /* No need to convert to multi-objects. Just like we keep the non-active bones * selected we then keep the non-active objects untouched (selected/unselected). */ @@ -1737,7 +2046,11 @@ void ARMATURE_OT_select_hierarchy(wmOperatorType *ot) RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend the selection"); } -/****************** Mirror Select ****************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Select Mirror Operator + * \{ */ /** * \note clone of #pose_select_mirror_exec keep in sync @@ -1822,7 +2135,11 @@ void ARMATURE_OT_select_mirror(wmOperatorType *ot) RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend the selection"); } -/****************** Select Path ****************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Select Path Operator + * \{ */ static bool armature_shortest_path_select( bArmature *arm, EditBone *ebone_parent, EditBone *ebone_child, bool use_parent, bool is_test) @@ -1866,7 +2183,7 @@ static int armature_shortest_path_pick_invoke(bContext *C, wmOperator *op, const BKE_object_update_select_id(CTX_data_main(C)); ebone_src = arm->act_edbone; - ebone_dst = get_nearest_bone(C, event->mval, false, &base_dst); + ebone_dst = ED_armature_pick_ebone(C, event->mval, false, &base_dst); /* fallback to object selection */ if (ELEM(NULL, ebone_src, ebone_dst) || (ebone_src == ebone_dst)) { @@ -1948,3 +2265,5 @@ void ARMATURE_OT_shortest_path_pick(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } + +/** \} */ diff --git a/source/blender/editors/armature/armature_skinning.c b/source/blender/editors/armature/armature_skinning.c index 87f980db7b9..61d8856afbc 100644 --- a/source/blender/editors/armature/armature_skinning.c +++ b/source/blender/editors/armature/armature_skinning.c @@ -418,7 +418,7 @@ static void add_verts_to_dgroups(ReportList *reports, BKE_mesh_foreach_mapped_vert_coords_get(me_eval, verts, mesh->totvert); vertsfilled = 1; } - else if (modifiers_findByType(ob, eModifierType_Subsurf)) { + else if (BKE_modifiers_findby_type(ob, eModifierType_Subsurf)) { /* is subsurf on? Lets use the verts on the limit surface then. * = same amount of vertices as mesh, but vertices moved to the * subsurfed position, like for 'optimal'. */ @@ -459,7 +459,7 @@ static void add_verts_to_dgroups(ReportList *reports, } /* only generated in some cases but can call anyway */ - ED_mesh_mirror_spatial_table(ob, NULL, NULL, NULL, 'e'); + ED_mesh_mirror_spatial_table_end(ob); /* free the memory allocated */ MEM_freeN(bonelist); diff --git a/source/blender/editors/armature/armature_utils.c b/source/blender/editors/armature/armature_utils.c index 76361f6785b..cf7f6699e5e 100644 --- a/source/blender/editors/armature/armature_utils.c +++ b/source/blender/editors/armature/armature_utils.c @@ -92,7 +92,7 @@ void ED_armature_edit_validate_active(struct bArmature *arm) void ED_armature_edit_refresh_layer_used(bArmature *arm) { arm->layer_used = 0; - for (EditBone *ebo = arm->edbo->first; ebo; ebo = ebo->next) { + LISTBASE_FOREACH (EditBone *, ebo, arm->edbo) { arm->layer_used |= ebo->layer; } } @@ -142,7 +142,7 @@ void bone_free(bArmature *arm, EditBone *bone) } /* Clear references from other edit bones. */ - for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { + LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) { if (ebone->bbone_next == bone) { ebone->bbone_next = NULL; } @@ -196,13 +196,12 @@ bool ED_armature_ebone_is_child_recursive(EditBone *ebone_parent, EditBone *ebon * \param ebone_child_tot: Size of the ebone_child array * \return The shared parent or NULL. */ -EditBone *ED_armature_ebone_find_shared_parent(EditBone *ebone_child[], - const unsigned int ebone_child_tot) +EditBone *ED_armature_ebone_find_shared_parent(EditBone *ebone_child[], const uint ebone_child_tot) { - unsigned int i; + uint i; EditBone *ebone_iter; -#define EBONE_TEMP_UINT(ebone) (*((unsigned int *)(&((ebone)->temp)))) +#define EBONE_TEMP_UINT(ebone) (*((uint *)(&((ebone)->temp)))) /* clear all */ for (i = 0; i < ebone_child_tot; i++) { @@ -473,7 +472,7 @@ void ED_armature_ebone_transform_mirror_update(bArmature *arm, EditBone *ebo, bo void ED_armature_edit_transform_mirror_update(Object *obedit) { bArmature *arm = obedit->data; - for (EditBone *ebo = arm->edbo->first; ebo; ebo = ebo->next) { + LISTBASE_FOREACH (EditBone *, ebo, arm->edbo) { ED_armature_ebone_transform_mirror_update(arm, ebo, true); } } @@ -482,10 +481,10 @@ void ED_armature_edit_transform_mirror_update(Object *obedit) /* Armature EditMode Conversions */ /* converts Bones to EditBone list, used for tools as well */ -static EditBone *make_boneList_rec(ListBase *edbo, - ListBase *bones, - EditBone *parent, - Bone *actBone) +static EditBone *make_boneList_recursive(ListBase *edbo, + ListBase *bones, + EditBone *parent, + Bone *actBone) { EditBone *eBone; EditBone *eBoneAct = NULL; @@ -562,9 +561,9 @@ static EditBone *make_boneList_rec(ListBase *edbo, BLI_addtail(edbo, eBone); - /* Add children if necessary */ + /* Add children if necessary. */ if (curBone->childbase.first) { - eBoneTest = make_boneList_rec(edbo, &curBone->childbase, eBone, actBone); + eBoneTest = make_boneList_recursive(edbo, &curBone->childbase, eBone, actBone); if (eBoneTest) { eBoneAct = eBoneTest; } @@ -581,7 +580,7 @@ static EditBone *make_boneList_rec(ListBase *edbo, static EditBone *find_ebone_link(ListBase *edbo, Bone *link) { if (link != NULL) { - for (EditBone *ebone = edbo->first; ebone; ebone = ebone->next) { + LISTBASE_FOREACH (EditBone *, ebone, edbo) { if (ebone->temp.bone == link) { return ebone; } @@ -595,9 +594,9 @@ EditBone *make_boneList(ListBase *edbo, ListBase *bones, struct Bone *actBone) { BLI_assert(!edbo->first && !edbo->last); - EditBone *active = make_boneList_rec(edbo, bones, NULL, actBone); + EditBone *active = make_boneList_recursive(edbo, bones, NULL, actBone); - for (EditBone *ebone = edbo->first; ebone; ebone = ebone->next) { + LISTBASE_FOREACH (EditBone *, ebone, edbo) { Bone *bone = ebone->temp.bone; /* Convert custom B-Bone handle links. */ @@ -721,7 +720,7 @@ void ED_armature_from_edit(Main *bmain, bArmature *arm) } } - /* Copy the bones from the editData into the armature */ + /* Copy the bones from the edit-data into the armature. */ for (eBone = arm->edbo->first; eBone; eBone = eBone->next) { newBone = MEM_callocN(sizeof(Bone), "bone"); eBone->temp.bone = newBone; /* Associate the real Bones with the EditBones */ @@ -819,7 +818,7 @@ void ED_armature_edit_free(struct bArmature *arm) { EditBone *eBone; - /* Clear the editbones list */ + /* Clear the edit-bones list. */ if (arm->edbo) { if (arm->edbo->first) { for (eBone = arm->edbo->first; eBone; eBone = eBone->next) { diff --git a/source/blender/editors/armature/editarmature_undo.c b/source/blender/editors/armature/editarmature_undo.c index c73f8f69fdd..a3a73f8d509 100644 --- a/source/blender/editors/armature/editarmature_undo.c +++ b/source/blender/editors/armature/editarmature_undo.c @@ -29,6 +29,7 @@ #include "DNA_object_types.h" #include "BLI_array_utils.h" +#include "BLI_listbase.h" #include "BKE_context.h" #include "BKE_layer.h" @@ -94,7 +95,7 @@ static void *undoarm_from_editarm(UndoArmature *uarm, bArmature *arm) ED_armature_ebone_listbase_temp_clear(&uarm->lb); - for (EditBone *ebone = uarm->lb.first; ebone; ebone = ebone->next) { + LISTBASE_FOREACH (EditBone *, ebone, &uarm->lb) { uarm->undo_size += sizeof(EditBone); } diff --git a/source/blender/editors/armature/meshlaplacian.c b/source/blender/editors/armature/meshlaplacian.c index 6028ddb216f..d8a6a22a7df 100644 --- a/source/blender/editors/armature/meshlaplacian.c +++ b/source/blender/editors/armature/meshlaplacian.c @@ -1549,7 +1549,7 @@ static void meshdeform_matrix_solve(MeshDeformModifierData *mmd, MeshDeformBind } } else { - modifier_setError(&mmd->modifier, "Failed to find bind solution (increase precision?)"); + BKE_modifier_set_error(&mmd->modifier, "Failed to find bind solution (increase precision?)"); error("Mesh Deform: failed to find bind solution."); break; } @@ -1753,7 +1753,7 @@ void ED_mesh_deform_bind_callback(MeshDeformModifierData *mmd, int totvert, float cagemat[4][4]) { - MeshDeformModifierData *mmd_orig = (MeshDeformModifierData *)modifier_get_original( + MeshDeformModifierData *mmd_orig = (MeshDeformModifierData *)BKE_modifier_get_original( &mmd->modifier); MeshDeformBind mdb; MVert *mvert; @@ -1799,7 +1799,7 @@ void ED_mesh_deform_bind_callback(MeshDeformModifierData *mmd, MEM_freeN(mdb.vertexcos); /* compact weights */ - modifier_mdef_compact_influences((ModifierData *)mmd_orig); + BKE_modifier_mdef_compact_influences((ModifierData *)mmd_orig); end_progress_bar(); waitcursor(0); diff --git a/source/blender/editors/armature/pose_edit.c b/source/blender/editors/armature/pose_edit.c index 09b6cd9c8cc..0cd3afc9cf9 100644 --- a/source/blender/editors/armature/pose_edit.c +++ b/source/blender/editors/armature/pose_edit.c @@ -33,7 +33,7 @@ #include "DNA_scene_types.h" #include "BKE_action.h" -#include "BKE_anim.h" +#include "BKE_anim_visualization.h" #include "BKE_armature.h" #include "BKE_context.h" #include "BKE_deform.h" @@ -75,12 +75,12 @@ /* matches logic with ED_operator_posemode_context() */ Object *ED_pose_object_from_context(bContext *C) { - ScrArea *sa = CTX_wm_area(C); + ScrArea *area = CTX_wm_area(C); Object *ob; /* Since this call may also be used from the buttons window, * we need to check for where to get the object. */ - if (sa && sa->spacetype == SPACE_PROPERTIES) { + if (area && area->spacetype == SPACE_PROPERTIES) { ob = ED_object_context(C); } else { @@ -665,6 +665,11 @@ static int pose_bone_rotmode_exec(bContext *C, wmOperator *op) /* set rotation mode of selected bones */ CTX_DATA_BEGIN_WITH_ID (C, bPoseChannel *, pchan, selected_pose_bones, Object *, ob) { + /* use API Method for conversions... */ + BKE_rotMode_change_values( + pchan->quat, pchan->eul, pchan->rotAxis, &pchan->rotAngle, pchan->rotmode, (short)mode); + + /* finally, set the new rotation type */ pchan->rotmode = mode; if (prev_ob != ob) { @@ -901,8 +906,6 @@ static int pose_bone_layers_exec(bContext *C, wmOperator *op) RNA_boolean_set_array(&ptr, "layers", layers); if (prev_ob != ob) { - BKE_armature_refresh_layer_used(ob->data); - /* Note, notifier might evolve. */ WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); DEG_id_tag_update((ID *)ob->data, ID_RECALC_COPY_ON_WRITE); diff --git a/source/blender/editors/armature/pose_group.c b/source/blender/editors/armature/pose_group.c index 9cd87f476d4..c10e204e3a4 100644 --- a/source/blender/editors/armature/pose_group.c +++ b/source/blender/editors/armature/pose_group.c @@ -44,6 +44,7 @@ #include "WM_types.h" #include "ED_armature.h" +#include "ED_outliner.h" #include "ED_screen.h" #include "UI_interface.h" @@ -484,6 +485,7 @@ static int pose_group_select_exec(bContext *C, wmOperator *UNUSED(op)) bArmature *arm = ob->data; DEG_id_tag_update(&arm->id, ID_RECALC_SELECT); WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); + ED_outliner_select_sync_from_pose_bone_tag(C); return OPERATOR_FINISHED; } @@ -518,6 +520,7 @@ static int pose_group_deselect_exec(bContext *C, wmOperator *UNUSED(op)) bArmature *arm = ob->data; DEG_id_tag_update(&arm->id, ID_RECALC_SELECT); WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); + ED_outliner_select_sync_from_pose_bone_tag(C); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/armature/pose_lib.c b/source/blender/editors/armature/pose_lib.c index f759808d08e..aa57fb5844d 100644 --- a/source/blender/editors/armature/pose_lib.c +++ b/source/blender/editors/armature/pose_lib.c @@ -161,16 +161,16 @@ static TimeMarker *poselib_get_active_pose(bAction *act) /* XXX C can be zero */ static Object *get_poselib_object(bContext *C) { - ScrArea *sa; + ScrArea *area; /* sanity check */ if (C == NULL) { return NULL; } - sa = CTX_wm_area(C); + area = CTX_wm_area(C); - if (sa && (sa->spacetype == SPACE_PROPERTIES)) { + if (area && (area->spacetype == SPACE_PROPERTIES)) { return ED_object_context(C); } else { @@ -626,7 +626,7 @@ static int poselib_remove_exec(bContext *C, wmOperator *op) /* remove relevant keyframes */ for (fcu = act->curves.first; fcu; fcu = fcu->next) { BezTriple *bezt; - unsigned int i; + uint i; if (fcu->bezt) { for (i = 0, bezt = fcu->bezt; i < fcu->totvert; i++, bezt++) { @@ -870,7 +870,7 @@ typedef struct tPoseLib_PreviewData { /** active scene. */ Scene *scene; /** active area. */ - ScrArea *sa; + ScrArea *area; /** RNA-Pointer to Object 'ob' .*/ PointerRNA rna_ptr; @@ -1171,7 +1171,7 @@ static void poselib_preview_apply(bContext *C, wmOperator *op) /* do header print - if interactively previewing */ if (pld->state == PL_PREVIEW_RUNNING) { if (pld->flag & PL_PREVIEW_SHOWORIGINAL) { - ED_area_status_text(pld->sa, TIP_("PoseLib Previewing Pose: [Showing Original Pose]")); + ED_area_status_text(pld->area, TIP_("PoseLib Previewing Pose: [Showing Original Pose]")); ED_workspace_status_text(C, TIP_("Use Tab to start previewing poses again")); } else if (pld->searchstr[0]) { @@ -1200,7 +1200,7 @@ static void poselib_preview_apply(bContext *C, wmOperator *op) "Current Pose - \"%s\""), tempstr, markern); - ED_area_status_text(pld->sa, pld->headerstr); + ED_area_status_text(pld->area, pld->headerstr); ED_workspace_status_text(C, TIP_("Use ScrollWheel or PageUp/Down to change pose")); } else { @@ -1208,7 +1208,7 @@ static void poselib_preview_apply(bContext *C, wmOperator *op) sizeof(pld->headerstr), TIP_("PoseLib Previewing Pose: \"%s\""), pld->marker->name); - ED_area_status_text(pld->sa, pld->headerstr); + ED_area_status_text(pld->area, pld->headerstr); ED_workspace_status_text(C, NULL); } } @@ -1323,9 +1323,7 @@ static void poselib_preview_get_next(tPoseLib_PreviewData *pld, int step) } /* specially handle events for searching */ -static void poselib_preview_handle_search(tPoseLib_PreviewData *pld, - unsigned short event, - char ascii) +static void poselib_preview_handle_search(tPoseLib_PreviewData *pld, ushort event, char ascii) { /* try doing some form of string manipulation first */ switch (event) { @@ -1633,7 +1631,7 @@ static void poselib_preview_init_data(bContext *C, wmOperator *op) pld->act = (ob) ? (ob->poselib) : NULL; pld->scene = CTX_data_scene(C); - pld->sa = CTX_wm_area(C); + pld->area = CTX_wm_area(C); /* get starting pose based on RNA-props for this operator */ if (pose_index == -1) { @@ -1704,7 +1702,7 @@ static void poselib_preview_cleanup(bContext *C, wmOperator *op) TimeMarker *marker = pld->marker; /* redraw the header so that it doesn't show any of our stuff anymore */ - ED_area_status_text(pld->sa, NULL); + ED_area_status_text(pld->area, NULL); ED_workspace_status_text(C, NULL); /* this signal does one recalc on pose, then unlocks, so ESC or edit will work */ diff --git a/source/blender/editors/armature/pose_select.c b/source/blender/editors/armature/pose_select.c index 07be2baf1ae..9525fcf2154 100644 --- a/source/blender/editors/armature/pose_select.c +++ b/source/blender/editors/armature/pose_select.c @@ -141,7 +141,7 @@ void ED_pose_bone_select(Object *ob, bPoseChannel *pchan, bool select) bool ED_armature_pose_select_pick_with_buffer(ViewLayer *view_layer, View3D *v3d, Base *base, - const unsigned int *buffer, + const uint *buffer, short hits, bool extend, bool deselect, @@ -156,12 +156,12 @@ bool ED_armature_pose_select_pick_with_buffer(ViewLayer *view_layer, } Object *ob_act = OBACT(view_layer); - Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer); + BLI_assert(OBEDIT_FROM_VIEW_LAYER(view_layer) == NULL); /* Callers happen to already get the active base */ Base *base_dummy = NULL; - nearBone = get_bone_from_selectbuffer( - &base, 1, obedit != NULL, buffer, hits, 1, do_nearest, &base_dummy); + nearBone = ED_armature_pick_bone_from_selectbuffer( + &base, 1, buffer, hits, 1, do_nearest, &base_dummy); /* if the bone cannot be affected, don't do anything */ if ((nearBone) && !(nearBone->flag & BONE_UNSELECTABLE)) { @@ -263,7 +263,7 @@ void ED_armature_pose_select_in_wpaint_mode(ViewLayer *view_layer, Base *base_se Object *ob_active = OBACT(view_layer); BLI_assert(ob_active && (ob_active->mode & OB_MODE_WEIGHT_PAINT)); VirtualModifierData virtualModifierData; - ModifierData *md = modifiers_getVirtualModifierList(ob_active, &virtualModifierData); + ModifierData *md = BKE_modifiers_get_virtual_modifierlist(ob_active, &virtualModifierData); for (; md; md = md->next) { if (md->type == eModifierType_Armature) { ArmatureModifierData *amd = (ArmatureModifierData *)md; @@ -323,7 +323,7 @@ bool ED_pose_deselect_all(Object *ob, int select_mode, const bool ignore_visibil static bool ed_pose_is_any_selected(Object *ob, bool ignore_visibility) { bArmature *arm = ob->data; - for (bPoseChannel *pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { + LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) { if (ignore_visibility || PBONE_VISIBLE(arm, pchan->bone)) { if (pchan->bone->flag & BONE_SELECTED) { return true; @@ -413,7 +413,7 @@ static int pose_select_connected_invoke(bContext *C, wmOperator *op, const wmEve view3d_operator_needs_opengl(C); Base *base = NULL; - bone = get_nearest_bone(C, event->mval, !extend, &base); + bone = ED_armature_pick_bone(C, event->mval, !extend, &base); if (!bone) { return OPERATOR_CANCELLED; @@ -443,7 +443,7 @@ static int pose_select_connected_invoke(bContext *C, wmOperator *op, const wmEve } /* Select children */ - for (curBone = bone->childbase.first; curBone; curBone = next) { + for (curBone = bone->childbase.first; curBone; curBone = curBone->next) { selectconnected_posebonechildren(base->object, curBone, extend); } @@ -454,22 +454,22 @@ static int pose_select_connected_invoke(bContext *C, wmOperator *op, const wmEve return OPERATOR_FINISHED; } -static bool pose_select_linked_poll(bContext *C) +static bool pose_select_linked_pick_poll(bContext *C) { return (ED_operator_view3d_active(C) && ED_operator_posemode(C)); } -void POSE_OT_select_linked(wmOperatorType *ot) +void POSE_OT_select_linked_pick(wmOperatorType *ot) { /* identifiers */ ot->name = "Select Connected"; - ot->idname = "POSE_OT_select_linked"; - ot->description = "Select bones related to selected ones by parent/child relationships"; + ot->idname = "POSE_OT_select_linked_pick"; + ot->description = "Select bones linked by parent/child connections under the mouse cursor"; /* callbacks */ /* leave 'exec' unset */ ot->invoke = pose_select_connected_invoke; - ot->poll = pose_select_linked_poll; + ot->poll = pose_select_linked_pick_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -482,6 +482,62 @@ void POSE_OT_select_linked(wmOperatorType *ot) "Extend selection instead of deselecting everything first"); } +static int pose_select_linked_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Bone *curBone, *next = NULL; + + CTX_DATA_BEGIN_WITH_ID (C, bPoseChannel *, pchan, visible_pose_bones, Object *, ob) { + if ((pchan->bone->flag & BONE_SELECTED) == 0) { + continue; + } + + bArmature *arm = ob->data; + + /* Select parents */ + for (curBone = pchan->bone; curBone; curBone = next) { + if (PBONE_SELECTABLE(arm, curBone)) { + curBone->flag |= BONE_SELECTED; + + if (curBone->flag & BONE_CONNECTED) { + next = curBone->parent; + } + else { + next = NULL; + } + } + else { + next = NULL; + } + } + + /* Select children */ + for (curBone = pchan->bone->childbase.first; curBone; curBone = curBone->next) { + selectconnected_posebonechildren(ob, curBone, false); + } + ED_pose_bone_select_tag_update(ob); + } + CTX_DATA_END; + + ED_outliner_select_sync_from_pose_bone_tag(C); + + return OPERATOR_FINISHED; +} + +void POSE_OT_select_linked(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Select Connected"; + ot->idname = "POSE_OT_select_linked"; + ot->description = "Select all bones linked by parent/child connections to the current selection"; + + /* callbacks */ + ot->exec = pose_select_linked_exec; + ot->poll = ED_operator_posemode; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + /* -------------------------------------- */ static int pose_de_select_all_exec(bContext *C, wmOperator *op) @@ -497,7 +553,7 @@ static int pose_de_select_all_exec(bContext *C, wmOperator *op) Object *ob_prev = NULL; - /* Set the flags */ + /* Set the flags. */ CTX_DATA_BEGIN_WITH_ID (C, bPoseChannel *, pchan, visible_pose_bones, Object *, ob) { bArmature *arm = ob->data; pose_do_bone_select(pchan, action); diff --git a/source/blender/editors/armature/pose_slide.c b/source/blender/editors/armature/pose_slide.c index 336d09f71b5..d9621dba730 100644 --- a/source/blender/editors/armature/pose_slide.c +++ b/source/blender/editors/armature/pose_slide.c @@ -86,7 +86,7 @@ typedef struct tPoseSlideOp { /** current scene */ Scene *scene; /** area that we're operating in (needed for modal()) */ - ScrArea *sa; + ScrArea *area; /** region that we're operating in (needed for modal()) */ ARegion *region; /** len of the PoseSlideObject array. */ @@ -197,7 +197,7 @@ static int pose_slide_init(bContext *C, wmOperator *op, ePoseSlide_Modes mode) /* get info from context */ pso->scene = CTX_data_scene(C); - pso->sa = CTX_wm_area(C); /* only really needed when doing modal() */ + pso->area = CTX_wm_area(C); /* only really needed when doing modal() */ pso->region = CTX_wm_region(C); /* only really needed when doing modal() */ pso->cframe = pso->scene->r.cfra; @@ -544,71 +544,56 @@ static void pose_slide_apply_quat(tPoseSlideOp *pso, tPChanFCurveLink *pfl) /* only if all channels exist, proceed */ if (fcu_w && fcu_x && fcu_y && fcu_z) { - float quat_prev[4], quat_prev_orig[4]; - float quat_next[4], quat_next_orig[4]; - float quat_curr[4], quat_curr_orig[4]; float quat_final[4]; - copy_qt_qt(quat_curr_orig, pchan->quat); - - /* get 2 quats */ - quat_prev_orig[0] = evaluate_fcurve(fcu_w, prevFrameF); - quat_prev_orig[1] = evaluate_fcurve(fcu_x, prevFrameF); - quat_prev_orig[2] = evaluate_fcurve(fcu_y, prevFrameF); - quat_prev_orig[3] = evaluate_fcurve(fcu_z, prevFrameF); - - quat_next_orig[0] = evaluate_fcurve(fcu_w, nextFrameF); - quat_next_orig[1] = evaluate_fcurve(fcu_x, nextFrameF); - quat_next_orig[2] = evaluate_fcurve(fcu_y, nextFrameF); - quat_next_orig[3] = evaluate_fcurve(fcu_z, nextFrameF); - - normalize_qt_qt(quat_prev, quat_prev_orig); - normalize_qt_qt(quat_next, quat_next_orig); - normalize_qt_qt(quat_curr, quat_curr_orig); - /* perform blending */ if (pso->mode == POSESLIDE_BREAKDOWN) { /* Just perform the interpolation between quat_prev and * quat_next using pso->percentage as a guide. */ - interp_qt_qtqt(quat_final, quat_prev, quat_next, pso->percentage); - } - else if (pso->mode == POSESLIDE_PUSH) { - float quat_diff[4]; + float quat_prev[4]; + float quat_next[4]; + + quat_prev[0] = evaluate_fcurve(fcu_w, prevFrameF); + quat_prev[1] = evaluate_fcurve(fcu_x, prevFrameF); + quat_prev[2] = evaluate_fcurve(fcu_y, prevFrameF); + quat_prev[3] = evaluate_fcurve(fcu_z, prevFrameF); - /* calculate the delta transform from the previous to the current */ - /* TODO: investigate ways to favor one transform more? */ - sub_qt_qtqt(quat_diff, quat_curr, quat_prev); + quat_next[0] = evaluate_fcurve(fcu_w, nextFrameF); + quat_next[1] = evaluate_fcurve(fcu_x, nextFrameF); + quat_next[2] = evaluate_fcurve(fcu_y, nextFrameF); + quat_next[3] = evaluate_fcurve(fcu_z, nextFrameF); - /* increase the original by the delta transform, by an amount determined by percentage */ - add_qt_qtqt(quat_final, quat_curr, quat_diff, pso->percentage); + normalize_qt(quat_prev); + normalize_qt(quat_next); - normalize_qt(quat_final); + interp_qt_qtqt(quat_final, quat_prev, quat_next, pso->percentage); } else { - BLI_assert(pso->mode == POSESLIDE_RELAX); - float quat_interp[4], quat_final_prev[4]; - /* TODO: maybe a sensitivity ctrl on top of this is needed */ - int iters = (int)ceil(10.0f * pso->percentage); + /* POSESLIDE_PUSH and POSESLIDE_RELAX. */ + float quat_breakdown[4]; + float quat_curr[4]; - copy_qt_qt(quat_final, quat_curr); + copy_qt_qt(quat_curr, pchan->quat); - /* perform this blending several times until a satisfactory result is reached */ - while (iters-- > 0) { - /* calculate the interpolation between the endpoints */ - interp_qt_qtqt(quat_interp, - quat_prev, - quat_next, - (cframe - pso->prevFrame) / (pso->nextFrame - pso->prevFrame)); + quat_breakdown[0] = evaluate_fcurve(fcu_w, cframe); + quat_breakdown[1] = evaluate_fcurve(fcu_x, cframe); + quat_breakdown[2] = evaluate_fcurve(fcu_y, cframe); + quat_breakdown[3] = evaluate_fcurve(fcu_z, cframe); - normalize_qt_qt(quat_final_prev, quat_final); + normalize_qt(quat_breakdown); + normalize_qt(quat_curr); - /* tricky interpolations - blending between original and new */ - interp_qt_qtqt(quat_final, quat_final_prev, quat_interp, 1.0f / 6.0f); + if (pso->mode == POSESLIDE_PUSH) { + interp_qt_qtqt(quat_final, quat_breakdown, quat_curr, 1.0f + pso->percentage); + } + else { + BLI_assert(pso->mode == POSESLIDE_RELAX); + interp_qt_qtqt(quat_final, quat_curr, quat_breakdown, pso->percentage); } } /* Apply final to the pose bone, keeping compatible for similar keyframe positions. */ - quat_to_compatible_quat(pchan->quat, quat_final, quat_curr_orig); + quat_to_compatible_quat(pchan->quat, quat_final, pchan->quat); } /* free the path now */ @@ -904,7 +889,7 @@ static void pose_slide_draw_status(tPoseSlideOp *pso) limits_str); } - ED_area_status_text(pso->sa, status_str); + ED_area_status_text(pso->area, status_str); } /* common code for invoke() methods */ @@ -1071,7 +1056,7 @@ static int pose_slide_modal(bContext *C, wmOperator *op, const wmEvent *event) case EVT_PADENTER: { if (event->val == KM_PRESS) { /* return to normal cursor and header status */ - ED_area_status_text(pso->sa, NULL); + ED_area_status_text(pso->area, NULL); WM_cursor_modal_restore(win); /* insert keyframes as required... */ @@ -1088,7 +1073,7 @@ static int pose_slide_modal(bContext *C, wmOperator *op, const wmEvent *event) case RIGHTMOUSE: { if (event->val == KM_PRESS) { /* return to normal cursor and header status */ - ED_area_status_text(pso->sa, NULL); + ED_area_status_text(pso->area, NULL); WM_cursor_modal_restore(win); /* reset transforms back to original state */ @@ -1254,51 +1239,60 @@ static int pose_slide_exec_common(bContext *C, wmOperator *op, tPoseSlideOp *pso return OPERATOR_FINISHED; } -/* common code for defining RNA properties */ -/* TODO: Skip save on these? */ +/** + * Common code for defining RNA properties. + */ static void pose_slide_opdef_properties(wmOperatorType *ot) { - RNA_def_float_percentage(ot->srna, - "percentage", - 0.5f, - 0.0f, - 1.0f, - "Percentage", - "Weighting factor for which keyframe is favored more", - 0.0, - 1.0); - - RNA_def_int(ot->srna, - "prev_frame", - 0, - MINAFRAME, - MAXFRAME, - "Previous Keyframe", - "Frame number of keyframe immediately before the current frame", - 0, - 50); - RNA_def_int(ot->srna, - "next_frame", - 0, - MINAFRAME, - MAXFRAME, - "Next Keyframe", - "Frame number of keyframe immediately after the current frame", - 0, - 50); - - RNA_def_enum(ot->srna, - "channels", - prop_channels_types, - PS_TFM_ALL, - "Channels", - "Set of properties that are affected"); - RNA_def_enum(ot->srna, - "axis_lock", - prop_axis_lock_types, - 0, - "Axis Lock", - "Transform axis to restrict effects to"); + PropertyRNA *prop; + + prop = RNA_def_float_percentage(ot->srna, + "percentage", + 0.5f, + 0.0f, + 1.0f, + "Percentage", + "Weighting factor for which keyframe is favored more", + 0.0, + 1.0); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + + prop = RNA_def_int(ot->srna, + "prev_frame", + 0, + MINAFRAME, + MAXFRAME, + "Previous Keyframe", + "Frame number of keyframe immediately before the current frame", + 0, + 50); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + + prop = RNA_def_int(ot->srna, + "next_frame", + 0, + MINAFRAME, + MAXFRAME, + "Next Keyframe", + "Frame number of keyframe immediately after the current frame", + 0, + 50); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + + prop = RNA_def_enum(ot->srna, + "channels", + prop_channels_types, + PS_TFM_ALL, + "Channels", + "Set of properties that are affected"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_enum(ot->srna, + "axis_lock", + prop_axis_lock_types, + 0, + "Axis Lock", + "Transform axis to restrict effects to"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); } /* ------------------------------------ */ diff --git a/source/blender/editors/armature/pose_transform.c b/source/blender/editors/armature/pose_transform.c index fe79894a351..1d2bf152777 100644 --- a/source/blender/editors/armature/pose_transform.c +++ b/source/blender/editors/armature/pose_transform.c @@ -207,12 +207,12 @@ typedef struct ApplyArmature_ParentState { } ApplyArmature_ParentState; /* Recursive walk for Apply To Selected mode; pstate NULL unless child of an applied bone. */ -static void applyarmature_process_selected_rec(bArmature *arm, - bPose *pose, - bPose *pose_eval, - Bone *bone, - ListBase *selected, - ApplyArmature_ParentState *pstate) +static void applyarmature_process_selected_recursive(bArmature *arm, + bPose *pose, + bPose *pose_eval, + Bone *bone, + ListBase *selected, + ApplyArmature_ParentState *pstate) { bPoseChannel *pchan = BKE_pose_channel_find_name(pose, bone->name); const bPoseChannel *pchan_eval = BKE_pose_channel_find_name(pose_eval, bone->name); @@ -333,8 +333,8 @@ static void applyarmature_process_selected_rec(bArmature *arm, pstate = &new_pstate; } - for (Bone *child = bone->childbase.first; child; child = child->next) { - applyarmature_process_selected_rec(arm, pose, pose_eval, child, selected, pstate); + LISTBASE_FOREACH (Bone *, child, &bone->childbase) { + applyarmature_process_selected_recursive(arm, pose, pose_eval, child, selected, pstate); } } @@ -389,8 +389,9 @@ static int apply_armature_pose2bones_exec(bContext *C, wmOperator *op) if (use_selected) { /* The selected only mode requires a recursive walk to handle parent-child relations. */ - for (Bone *bone = arm->bonebase.first; bone; bone = bone->next) { - applyarmature_process_selected_rec(arm, pose, ob_eval->pose, bone, &selected_bones, NULL); + LISTBASE_FOREACH (Bone *, bone, &arm->bonebase) { + applyarmature_process_selected_recursive( + arm, pose, ob_eval->pose, bone, &selected_bones, NULL); } BLI_freelistN(&selected_bones); @@ -1239,7 +1240,7 @@ static int pose_clear_user_transforms_exec(bContext *C, wmOperator *op) workob.adt = ob->adt; workob.pose = dummyPose; - BKE_animsys_evaluate_animdata(scene, &workob.id, workob.adt, cframe, ADT_RECALC_ANIM, false); + BKE_animsys_evaluate_animdata(&workob.id, workob.adt, cframe, ADT_RECALC_ANIM, false); /* copy back values, but on selected bones only */ for (pchan = dummyPose->chanbase.first; pchan; pchan = pchan->next) { diff --git a/source/blender/editors/armature/pose_utils.c b/source/blender/editors/armature/pose_utils.c index efb568178d9..e2038bdd2a3 100644 --- a/source/blender/editors/armature/pose_utils.c +++ b/source/blender/editors/armature/pose_utils.c @@ -32,7 +32,7 @@ #include "DNA_scene_types.h" #include "BKE_action.h" -#include "BKE_animsys.h" +#include "BKE_anim_data.h" #include "BKE_armature.h" #include "BKE_idprop.h" #include "BKE_layer.h" |