diff options
Diffstat (limited to 'source/blender/editors/armature')
20 files changed, 13874 insertions, 13464 deletions
diff --git a/source/blender/editors/armature/CMakeLists.txt b/source/blender/editors/armature/CMakeLists.txt index 27eddd7e1dd..274fa86184d 100644 --- a/source/blender/editors/armature/CMakeLists.txt +++ b/source/blender/editors/armature/CMakeLists.txt @@ -16,55 +16,55 @@ # ***** END GPL LICENSE BLOCK ***** set(INC - ../include - ../../blenkernel - ../../blenlib - ../../blentranslation - ../../depsgraph - ../../gpu - ../../makesdna - ../../makesrna - ../../windowmanager - ../../../../intern/clog - ../../../../intern/guardedalloc - ../../../../intern/eigen - ../../../../intern/glew-mx + ../include + ../../blenkernel + ../../blenlib + ../../blentranslation + ../../depsgraph + ../../gpu + ../../makesdna + ../../makesrna + ../../windowmanager + ../../../../intern/clog + ../../../../intern/guardedalloc + ../../../../intern/eigen + ../../../../intern/glew-mx ) set(INC_SYS - ${GLEW_INCLUDE_PATH} + ${GLEW_INCLUDE_PATH} ) set(SRC - armature_add.c - armature_edit.c - armature_naming.c - armature_ops.c - armature_relations.c - armature_select.c - armature_skinning.c - armature_utils.c - editarmature_undo.c - meshlaplacian.c - pose_edit.c - pose_group.c - pose_lib.c - pose_select.c - pose_slide.c - pose_transform.c - pose_utils.c + armature_add.c + armature_edit.c + armature_naming.c + armature_ops.c + armature_relations.c + armature_select.c + armature_skinning.c + armature_utils.c + editarmature_undo.c + meshlaplacian.c + pose_edit.c + pose_group.c + pose_lib.c + pose_select.c + pose_slide.c + pose_transform.c + pose_utils.c - armature_intern.h - meshlaplacian.h + armature_intern.h + meshlaplacian.h ) set(LIB - bf_blenkernel - bf_blenlib + bf_blenkernel + bf_blenlib ) if(WITH_INTERNATIONAL) - add_definitions(-DWITH_INTERNATIONAL) + add_definitions(-DWITH_INTERNATIONAL) endif() add_definitions(${GL_DEFINITIONS}) diff --git a/source/blender/editors/armature/armature_add.c b/source/blender/editors/armature/armature_add.c index 20978a42812..6943d6bdc54 100644 --- a/source/blender/editors/armature/armature_add.c +++ b/source/blender/editors/armature/armature_add.c @@ -59,59 +59,58 @@ /* XXX should be used everywhere, now it mallocs bones still locally in functions */ EditBone *ED_armature_ebone_add(bArmature *arm, const char *name) { - EditBone *bone = MEM_callocN(sizeof(EditBone), "eBone"); - - BLI_strncpy(bone->name, name, sizeof(bone->name)); - ED_armature_ebone_unique_name(arm->edbo, bone->name, NULL); - - BLI_addtail(arm->edbo, bone); - - bone->flag |= BONE_TIPSEL; - bone->weight = 1.0f; - bone->dist = 0.25f; - bone->xwidth = 0.1f; - bone->zwidth = 0.1f; - bone->rad_head = 0.10f; - bone->rad_tail = 0.05f; - bone->segments = 1; - bone->layer = arm->layer; - - /* Bendy-Bone parameters */ - bone->roll1 = 0.0f; - bone->roll2 = 0.0f; - bone->curveInX = 0.0f; - bone->curveInY = 0.0f; - bone->curveOutX = 0.0f; - bone->curveOutY = 0.0f; - bone->ease1 = 1.0f; - bone->ease2 = 1.0f; - bone->scaleIn = 1.0f; - bone->scaleOut = 1.0f; - - return bone; + EditBone *bone = MEM_callocN(sizeof(EditBone), "eBone"); + + BLI_strncpy(bone->name, name, sizeof(bone->name)); + ED_armature_ebone_unique_name(arm->edbo, bone->name, NULL); + + BLI_addtail(arm->edbo, bone); + + bone->flag |= BONE_TIPSEL; + bone->weight = 1.0f; + bone->dist = 0.25f; + bone->xwidth = 0.1f; + bone->zwidth = 0.1f; + bone->rad_head = 0.10f; + bone->rad_tail = 0.05f; + bone->segments = 1; + bone->layer = arm->layer; + + /* Bendy-Bone parameters */ + bone->roll1 = 0.0f; + bone->roll2 = 0.0f; + bone->curveInX = 0.0f; + bone->curveInY = 0.0f; + bone->curveOutX = 0.0f; + bone->curveOutY = 0.0f; + bone->ease1 = 1.0f; + bone->ease2 = 1.0f; + bone->scaleIn = 1.0f; + bone->scaleOut = 1.0f; + + return bone; } EditBone *ED_armature_ebone_add_primitive(Object *obedit_arm, float length, bool view_aligned) { - bArmature *arm = obedit_arm->data; - EditBone *bone; + bArmature *arm = obedit_arm->data; + EditBone *bone; - ED_armature_edit_deselect_all(obedit_arm); + ED_armature_edit_deselect_all(obedit_arm); - /* Create a bone */ - bone = ED_armature_ebone_add(arm, "Bone"); + /* Create a bone */ + bone = ED_armature_ebone_add(arm, "Bone"); - arm->act_edbone = bone; + arm->act_edbone = bone; - zero_v3(bone->head); - zero_v3(bone->tail); + zero_v3(bone->head); + zero_v3(bone->tail); - bone->tail[view_aligned ? 1 : 2] = length; + bone->tail[view_aligned ? 1 : 2] = length; - return bone; + return bone; } - /* previously addvert_armature */ /* the ctrl-click method */ @@ -124,517 +123,520 @@ EditBone *ED_armature_ebone_add_primitive(Object *obedit_arm, float length, bool */ static int armature_click_extrude_exec(bContext *C, wmOperator *UNUSED(op)) { - bArmature *arm; - EditBone *ebone, *newbone, *flipbone; - float mat[3][3], imat[3][3]; - int a, to_root = 0; - Object *obedit; - Scene *scene; - - scene = CTX_data_scene(C); - obedit = CTX_data_edit_object(C); - arm = obedit->data; - - /* find the active or selected bone */ - for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { - if (EBONE_VISIBLE(arm, ebone)) { - if (ebone->flag & BONE_TIPSEL || arm->act_edbone == ebone) - break; - } - } - - if (ebone == NULL) { - for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { - if (EBONE_VISIBLE(arm, ebone)) { - if (ebone->flag & BONE_ROOTSEL || arm->act_edbone == ebone) - break; - } - } - if (ebone == NULL) - return OPERATOR_CANCELLED; - - to_root = 1; - } - - ED_armature_edit_deselect_all(obedit); - - /* we re-use code for mirror editing... */ - flipbone = NULL; - if (arm->flag & ARM_MIRROR_EDIT) - flipbone = ED_armature_ebone_get_mirrored(arm->edbo, ebone); - - for (a = 0; a < 2; a++) { - if (a == 1) { - if (flipbone == NULL) - break; - else { - SWAP(EditBone *, flipbone, ebone); - } - } - - newbone = ED_armature_ebone_add(arm, ebone->name); - arm->act_edbone = newbone; - - if (to_root) { - copy_v3_v3(newbone->head, ebone->head); - newbone->rad_head = ebone->rad_tail; - newbone->parent = ebone->parent; - } - else { - copy_v3_v3(newbone->head, ebone->tail); - newbone->rad_head = ebone->rad_tail; - newbone->parent = ebone; - newbone->flag |= BONE_CONNECTED; - } - - const View3DCursor *curs = &scene->cursor; - copy_v3_v3(newbone->tail, curs->location); - sub_v3_v3v3(newbone->tail, newbone->tail, obedit->obmat[3]); - - if (a == 1) - newbone->tail[0] = -newbone->tail[0]; - - copy_m3_m4(mat, obedit->obmat); - invert_m3_m3(imat, mat); - mul_m3_v3(imat, newbone->tail); - - newbone->length = len_v3v3(newbone->head, newbone->tail); - newbone->rad_tail = newbone->length * 0.05f; - newbone->dist = newbone->length * 0.25f; - - } - - ED_armature_edit_sync_selection(arm->edbo); - - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit); - - return OPERATOR_FINISHED; + bArmature *arm; + EditBone *ebone, *newbone, *flipbone; + float mat[3][3], imat[3][3]; + int a, to_root = 0; + Object *obedit; + Scene *scene; + + scene = CTX_data_scene(C); + obedit = CTX_data_edit_object(C); + arm = obedit->data; + + /* find the active or selected bone */ + for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { + if (EBONE_VISIBLE(arm, ebone)) { + if (ebone->flag & BONE_TIPSEL || arm->act_edbone == ebone) + break; + } + } + + if (ebone == NULL) { + for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { + if (EBONE_VISIBLE(arm, ebone)) { + if (ebone->flag & BONE_ROOTSEL || arm->act_edbone == ebone) + break; + } + } + if (ebone == NULL) + return OPERATOR_CANCELLED; + + to_root = 1; + } + + ED_armature_edit_deselect_all(obedit); + + /* we re-use code for mirror editing... */ + flipbone = NULL; + if (arm->flag & ARM_MIRROR_EDIT) + flipbone = ED_armature_ebone_get_mirrored(arm->edbo, ebone); + + for (a = 0; a < 2; a++) { + if (a == 1) { + if (flipbone == NULL) + break; + else { + SWAP(EditBone *, flipbone, ebone); + } + } + + newbone = ED_armature_ebone_add(arm, ebone->name); + arm->act_edbone = newbone; + + if (to_root) { + copy_v3_v3(newbone->head, ebone->head); + newbone->rad_head = ebone->rad_tail; + newbone->parent = ebone->parent; + } + else { + copy_v3_v3(newbone->head, ebone->tail); + newbone->rad_head = ebone->rad_tail; + newbone->parent = ebone; + newbone->flag |= BONE_CONNECTED; + } + + const View3DCursor *curs = &scene->cursor; + copy_v3_v3(newbone->tail, curs->location); + sub_v3_v3v3(newbone->tail, newbone->tail, obedit->obmat[3]); + + if (a == 1) + newbone->tail[0] = -newbone->tail[0]; + + copy_m3_m4(mat, obedit->obmat); + invert_m3_m3(imat, mat); + mul_m3_v3(imat, newbone->tail); + + newbone->length = len_v3v3(newbone->head, newbone->tail); + newbone->rad_tail = newbone->length * 0.05f; + newbone->dist = newbone->length * 0.25f; + } + + ED_armature_edit_sync_selection(arm->edbo); + + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit); + + return OPERATOR_FINISHED; } static int armature_click_extrude_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - /* TODO most of this code is copied from set3dcursor_invoke, - * it would be better to reuse code in set3dcursor_invoke */ + /* TODO most of this code is copied from set3dcursor_invoke, + * it would be better to reuse code in set3dcursor_invoke */ - /* temporarily change 3d cursor position */ - Scene *scene; - ARegion *ar; - View3D *v3d; - float tvec[3], oldcurs[3], mval_f[2]; - int retv; + /* temporarily change 3d cursor position */ + Scene *scene; + ARegion *ar; + View3D *v3d; + float tvec[3], oldcurs[3], mval_f[2]; + int retv; - scene = CTX_data_scene(C); - ar = CTX_wm_region(C); - v3d = CTX_wm_view3d(C); + scene = CTX_data_scene(C); + ar = CTX_wm_region(C); + v3d = CTX_wm_view3d(C); - View3DCursor *cursor = &scene->cursor; + View3DCursor *cursor = &scene->cursor; - copy_v3_v3(oldcurs, cursor->location); + copy_v3_v3(oldcurs, cursor->location); - copy_v2fl_v2i(mval_f, event->mval); - ED_view3d_win_to_3d(v3d, ar, cursor->location, mval_f, tvec); - copy_v3_v3(cursor->location, tvec); + copy_v2fl_v2i(mval_f, event->mval); + ED_view3d_win_to_3d(v3d, ar, cursor->location, mval_f, tvec); + copy_v3_v3(cursor->location, tvec); - /* extrude to the where new cursor is and store the operation result */ - retv = armature_click_extrude_exec(C, op); + /* extrude to the where new cursor is and store the operation result */ + retv = armature_click_extrude_exec(C, op); - /* restore previous 3d cursor position */ - copy_v3_v3(cursor->location, oldcurs); + /* restore previous 3d cursor position */ + copy_v3_v3(cursor->location, oldcurs); - return retv; + return retv; } void ARMATURE_OT_click_extrude(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Click-Extrude"; - ot->idname = "ARMATURE_OT_click_extrude"; - ot->description = "Create a new bone going from the last selected joint to the mouse position"; + /* identifiers */ + ot->name = "Click-Extrude"; + ot->idname = "ARMATURE_OT_click_extrude"; + ot->description = "Create a new bone going from the last selected joint to the mouse position"; - /* api callbacks */ - ot->invoke = armature_click_extrude_invoke; - ot->exec = armature_click_extrude_exec; - ot->poll = ED_operator_editarmature; + /* api callbacks */ + ot->invoke = armature_click_extrude_invoke; + ot->exec = armature_click_extrude_exec; + ot->poll = ED_operator_editarmature; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - /* props */ + /* props */ } /* adds an EditBone between the nominated locations (should be in the right space) */ EditBone *add_points_bone(Object *obedit, float head[3], float tail[3]) { - EditBone *ebo; + EditBone *ebo; - ebo = ED_armature_ebone_add(obedit->data, "Bone"); + ebo = ED_armature_ebone_add(obedit->data, "Bone"); - copy_v3_v3(ebo->head, head); - copy_v3_v3(ebo->tail, tail); + copy_v3_v3(ebo->head, head); + copy_v3_v3(ebo->tail, tail); - return ebo; + return ebo; } - static EditBone *get_named_editbone(ListBase *edbo, const char *name) { - EditBone *eBone; + EditBone *eBone; - if (name) { - for (eBone = edbo->first; eBone; eBone = eBone->next) { - if (STREQ(name, eBone->name)) - return eBone; - } - } + if (name) { + for (eBone = edbo->first; eBone; eBone = eBone->next) { + if (STREQ(name, eBone->name)) + return eBone; + } + } - return NULL; + return NULL; } /* Call this before doing any duplications * */ void preEditBoneDuplicate(ListBase *editbones) { - /* clear temp */ - ED_armature_ebone_listbase_temp_clear(editbones); + /* clear temp */ + ED_armature_ebone_listbase_temp_clear(editbones); } /** * Helper function for #postEditBoneDuplicate, * return the destination pchan from the original. */ -static bPoseChannel *pchan_duplicate_map(const bPose *pose, GHash *name_map, bPoseChannel *pchan_src) +static bPoseChannel *pchan_duplicate_map(const bPose *pose, + GHash *name_map, + bPoseChannel *pchan_src) { - bPoseChannel *pchan_dst = NULL; - const char *name_src = pchan_src->name; - const char *name_dst = BLI_ghash_lookup(name_map, name_src); - if (name_dst) { - pchan_dst = BKE_pose_channel_find_name(pose, name_dst); - } - - if (pchan_dst == NULL) { - pchan_dst = pchan_src; - } - - return pchan_dst; + bPoseChannel *pchan_dst = NULL; + const char *name_src = pchan_src->name; + const char *name_dst = BLI_ghash_lookup(name_map, name_src); + if (name_dst) { + pchan_dst = BKE_pose_channel_find_name(pose, name_dst); + } + + if (pchan_dst == NULL) { + pchan_dst = pchan_src; + } + + return pchan_dst; } void postEditBoneDuplicate(struct ListBase *editbones, Object *ob) { - if (ob->pose == NULL) { - return; - } - - BKE_pose_channels_hash_free(ob->pose); - BKE_pose_channels_hash_make(ob->pose); - - GHash *name_map = BLI_ghash_str_new(__func__); - - for (EditBone *ebone_src = editbones->first; ebone_src; ebone_src = ebone_src->next) { - EditBone *ebone_dst = ebone_src->temp.ebone; - if (!ebone_dst) { - ebone_dst = ED_armature_ebone_get_mirrored(editbones, ebone_src); - } - if (ebone_dst) { - BLI_ghash_insert(name_map, ebone_src->name, ebone_dst->name); - } - } - - for (EditBone *ebone_src = editbones->first; ebone_src; ebone_src = ebone_src->next) { - EditBone *ebone_dst = ebone_src->temp.ebone; - if (ebone_dst) { - bPoseChannel *pchan_src = BKE_pose_channel_find_name(ob->pose, ebone_src->name); - if (pchan_src) { - bPoseChannel *pchan_dst = BKE_pose_channel_find_name(ob->pose, ebone_dst->name); - if (pchan_dst) { - if (pchan_src->custom_tx) { - pchan_dst->custom_tx = pchan_duplicate_map(ob->pose, name_map, pchan_src->custom_tx); - } - if (pchan_src->bbone_prev) { - pchan_dst->bbone_prev = pchan_duplicate_map(ob->pose, name_map, pchan_src->bbone_prev); - } - if (pchan_src->bbone_next) { - pchan_dst->bbone_next = pchan_duplicate_map(ob->pose, name_map, pchan_src->bbone_next); - } - } - } - } - } - - BLI_ghash_free(name_map, NULL, NULL); + if (ob->pose == NULL) { + return; + } + + BKE_pose_channels_hash_free(ob->pose); + BKE_pose_channels_hash_make(ob->pose); + + GHash *name_map = BLI_ghash_str_new(__func__); + + for (EditBone *ebone_src = editbones->first; ebone_src; ebone_src = ebone_src->next) { + EditBone *ebone_dst = ebone_src->temp.ebone; + if (!ebone_dst) { + ebone_dst = ED_armature_ebone_get_mirrored(editbones, ebone_src); + } + if (ebone_dst) { + BLI_ghash_insert(name_map, ebone_src->name, ebone_dst->name); + } + } + + for (EditBone *ebone_src = editbones->first; ebone_src; ebone_src = ebone_src->next) { + EditBone *ebone_dst = ebone_src->temp.ebone; + if (ebone_dst) { + bPoseChannel *pchan_src = BKE_pose_channel_find_name(ob->pose, ebone_src->name); + if (pchan_src) { + bPoseChannel *pchan_dst = BKE_pose_channel_find_name(ob->pose, ebone_dst->name); + if (pchan_dst) { + if (pchan_src->custom_tx) { + pchan_dst->custom_tx = pchan_duplicate_map(ob->pose, name_map, pchan_src->custom_tx); + } + if (pchan_src->bbone_prev) { + pchan_dst->bbone_prev = pchan_duplicate_map(ob->pose, name_map, pchan_src->bbone_prev); + } + if (pchan_src->bbone_next) { + pchan_dst->bbone_next = pchan_duplicate_map(ob->pose, name_map, pchan_src->bbone_next); + } + } + } + } + } + + 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, ListBase *editbones, Object *src_ob, Object *dst_ob) +void updateDuplicateSubtargetObjects(EditBone *dupBone, + ListBase *editbones, + Object *src_ob, + Object *dst_ob) { - /* 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 ((conlist = &pchan->constraints)) { - for (curcon = conlist->first; curcon; curcon = curcon->next) { - /* does this constraint have a subtarget in - * this armature? - */ - const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(curcon); - ListBase targets = {NULL, NULL}; - bConstraintTarget *ct; - - if (cti && cti->get_constraint_targets) { - 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 */ - oldtarget = get_named_editbone(editbones, ct->subtarget); - if (oldtarget) { - /* was the subtarget bone duplicated too? If - * so, update the constraint to point at the - * duplicate of the old subtarget. - */ - if (oldtarget->temp.ebone) { - newtarget = oldtarget->temp.ebone; - BLI_strncpy(ct->subtarget, newtarget->name, sizeof(ct->subtarget)); - } - } - } - } - - if (cti->flush_constraint_targets) - cti->flush_constraint_targets(curcon, &targets, 0); - } - } - } - } + /* 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 ((conlist = &pchan->constraints)) { + for (curcon = conlist->first; curcon; curcon = curcon->next) { + /* does this constraint have a subtarget in + * this armature? + */ + const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(curcon); + ListBase targets = {NULL, NULL}; + bConstraintTarget *ct; + + if (cti && cti->get_constraint_targets) { + 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 */ + oldtarget = get_named_editbone(editbones, ct->subtarget); + if (oldtarget) { + /* was the subtarget bone duplicated too? If + * so, update the constraint to point at the + * duplicate of the old subtarget. + */ + if (oldtarget->temp.ebone) { + newtarget = oldtarget->temp.ebone; + BLI_strncpy(ct->subtarget, newtarget->name, sizeof(ct->subtarget)); + } + } + } + } + + if (cti->flush_constraint_targets) + cti->flush_constraint_targets(curcon, &targets, 0); + } + } + } + } } void updateDuplicateSubtarget(EditBone *dupBone, ListBase *editbones, Object *ob) { - updateDuplicateSubtargetObjects(dupBone, editbones, ob, ob); + updateDuplicateSubtargetObjects(dupBone, editbones, ob, ob); } - -EditBone *duplicateEditBoneObjects(EditBone *curBone, const char *name, ListBase *editbones, - Object *src_ob, Object *dst_ob) +EditBone *duplicateEditBoneObjects( + EditBone *curBone, const char *name, ListBase *editbones, Object *src_ob, Object *dst_ob) { - EditBone *eBone = MEM_mallocN(sizeof(EditBone), "addup_editbone"); + EditBone *eBone = MEM_mallocN(sizeof(EditBone), "addup_editbone"); - /* Copy data from old bone to new bone */ - memcpy(eBone, curBone, sizeof(EditBone)); + /* Copy data from old bone to new bone */ + memcpy(eBone, curBone, sizeof(EditBone)); - curBone->temp.ebone = eBone; - eBone->temp.ebone = curBone; + curBone->temp.ebone = eBone; + eBone->temp.ebone = curBone; - if (name != NULL) { - BLI_strncpy(eBone->name, name, sizeof(eBone->name)); - } + if (name != NULL) { + BLI_strncpy(eBone->name, name, sizeof(eBone->name)); + } - ED_armature_ebone_unique_name(editbones, eBone->name, NULL); - BLI_addtail(editbones, eBone); + ED_armature_ebone_unique_name(editbones, eBone->name, NULL); + BLI_addtail(editbones, eBone); - /* copy the ID property */ - if (curBone->prop) - eBone->prop = IDP_CopyProperty(curBone->prop); + /* copy the ID property */ + if (curBone->prop) + eBone->prop = IDP_CopyProperty(curBone->prop); - /* Lets duplicate the list of constraints that the - * current bone has. - */ - if (src_ob->pose) { - bPoseChannel *chanold, *channew; + /* Lets duplicate the list of constraints that the + * current bone has. + */ + if (src_ob->pose) { + bPoseChannel *chanold, *channew; - chanold = BKE_pose_channel_verify(src_ob->pose, curBone->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); + chanold = BKE_pose_channel_verify(src_ob->pose, curBone->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); - if (channew) { - BKE_pose_channel_copy_data(channew, chanold); - } - } - } + if (channew) { + BKE_pose_channel_copy_data(channew, chanold); + } + } + } - return eBone; + return eBone; } EditBone *duplicateEditBone(EditBone *curBone, const char *name, ListBase *editbones, Object *ob) { - return duplicateEditBoneObjects(curBone, name, editbones, ob, ob); + return duplicateEditBoneObjects(curBone, name, editbones, ob, ob); } static int armature_duplicate_selected_exec(bContext *C, wmOperator *op) { - ViewLayer *view_layer = CTX_data_view_layer(C); - const bool do_flip_names = RNA_boolean_get(op->ptr, "do_flip_names"); - - /* cancel if nothing selected */ - if (CTX_DATA_COUNT(C, selected_bones) == 0) - return OPERATOR_CANCELLED; - - 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++) { - EditBone *ebone_iter; - /* The beginning of the duplicated bones in the edbo list */ - EditBone *ebone_first_dupe = NULL; - - Object *ob = objects[ob_index]; - bArmature *arm = ob->data; - - ED_armature_edit_sync_selection(arm->edbo); // XXX why is this needed? - - preEditBoneDuplicate(arm->edbo); - - /* Select mirrored bones */ - if (arm->flag & ARM_MIRROR_EDIT) { - for (ebone_iter = arm->edbo->first; ebone_iter; ebone_iter = ebone_iter->next) { - if (EBONE_VISIBLE(arm, ebone_iter) && - (ebone_iter->flag & BONE_SELECTED)) - { - EditBone *ebone; - - ebone = ED_armature_ebone_get_mirrored(arm->edbo, ebone_iter); - if (ebone) { - ebone->flag |= BONE_SELECTED; - } - } - } - } - - /* Find the selected bones and duplicate them as needed */ - 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)) - { - EditBone *ebone; - char new_bone_name_buff[MAXBONENAME]; - char *new_bone_name = ebone_iter->name; - - if (do_flip_names) { - 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. */ - if (ED_armature_ebone_find_name(arm->edbo, new_bone_name_buff) == NULL) { - new_bone_name = new_bone_name_buff; - } - } - - ebone = duplicateEditBone(ebone_iter, new_bone_name, arm->edbo, ob); - - if (!ebone_first_dupe) { - ebone_first_dupe = ebone; - } - } - } - - /* Run though 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_VISIBLE(arm, ebone_iter) && - (ebone_iter->flag & BONE_SELECTED)) - { - EditBone *ebone = ebone_iter->temp.ebone; - - if (!ebone_iter->parent) { - /* If this bone has no parent, - * Set the duplicate->parent to NULL - */ - ebone->parent = NULL; - } - 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 - */ - 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 - */ - ebone->parent = (EditBone *) ebone_iter->parent; - ebone->flag &= ~BONE_CONNECTED; - } - - /* Update custom handle links. */ - if (ebone_iter->bbone_prev && ebone_iter->bbone_prev->temp.ebone) { - ebone->bbone_prev = ebone_iter->bbone_prev->temp.ebone; - } - if (ebone_iter->bbone_next && ebone_iter->bbone_next->temp.ebone) { - ebone->bbone_next = ebone_iter->bbone_next->temp.ebone; - } - - /* Lets try to fix any constraint subtargets that might - * have been duplicated - */ - updateDuplicateSubtarget(ebone, arm->edbo, ob); - } - } - - /* correct the active bone */ - if (arm->act_edbone && arm->act_edbone->temp.ebone) { - arm->act_edbone = arm->act_edbone->temp.ebone; - } - - /* Deselect the old bones and select the new ones */ - 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 | BONE_TIPSEL | BONE_ROOTSEL); - } - } - - postEditBoneDuplicate(arm->edbo, ob); - - ED_armature_edit_validate_active(arm); - - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); - } - MEM_freeN(objects); - - return OPERATOR_FINISHED; + ViewLayer *view_layer = CTX_data_view_layer(C); + const bool do_flip_names = RNA_boolean_get(op->ptr, "do_flip_names"); + + /* cancel if nothing selected */ + if (CTX_DATA_COUNT(C, selected_bones) == 0) + return OPERATOR_CANCELLED; + + 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++) { + EditBone *ebone_iter; + /* The beginning of the duplicated bones in the edbo list */ + EditBone *ebone_first_dupe = NULL; + + Object *ob = objects[ob_index]; + bArmature *arm = ob->data; + + ED_armature_edit_sync_selection(arm->edbo); // XXX why is this needed? + + preEditBoneDuplicate(arm->edbo); + + /* Select mirrored bones */ + if (arm->flag & ARM_MIRROR_EDIT) { + for (ebone_iter = arm->edbo->first; ebone_iter; ebone_iter = ebone_iter->next) { + if (EBONE_VISIBLE(arm, ebone_iter) && (ebone_iter->flag & BONE_SELECTED)) { + EditBone *ebone; + + ebone = ED_armature_ebone_get_mirrored(arm->edbo, ebone_iter); + if (ebone) { + ebone->flag |= BONE_SELECTED; + } + } + } + } + + /* Find the selected bones and duplicate them as needed */ + 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)) { + EditBone *ebone; + char new_bone_name_buff[MAXBONENAME]; + char *new_bone_name = ebone_iter->name; + + if (do_flip_names) { + 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. */ + if (ED_armature_ebone_find_name(arm->edbo, new_bone_name_buff) == NULL) { + new_bone_name = new_bone_name_buff; + } + } + + ebone = duplicateEditBone(ebone_iter, new_bone_name, arm->edbo, ob); + + if (!ebone_first_dupe) { + ebone_first_dupe = ebone; + } + } + } + + /* Run though 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_VISIBLE(arm, ebone_iter) && (ebone_iter->flag & BONE_SELECTED)) { + EditBone *ebone = ebone_iter->temp.ebone; + + if (!ebone_iter->parent) { + /* If this bone has no parent, + * Set the duplicate->parent to NULL + */ + ebone->parent = NULL; + } + 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 + */ + 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 + */ + ebone->parent = (EditBone *)ebone_iter->parent; + ebone->flag &= ~BONE_CONNECTED; + } + + /* Update custom handle links. */ + if (ebone_iter->bbone_prev && ebone_iter->bbone_prev->temp.ebone) { + ebone->bbone_prev = ebone_iter->bbone_prev->temp.ebone; + } + if (ebone_iter->bbone_next && ebone_iter->bbone_next->temp.ebone) { + ebone->bbone_next = ebone_iter->bbone_next->temp.ebone; + } + + /* Lets try to fix any constraint subtargets that might + * have been duplicated + */ + updateDuplicateSubtarget(ebone, arm->edbo, ob); + } + } + + /* correct the active bone */ + if (arm->act_edbone && arm->act_edbone->temp.ebone) { + arm->act_edbone = arm->act_edbone->temp.ebone; + } + + /* Deselect the old bones and select the new ones */ + 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 | BONE_TIPSEL | BONE_ROOTSEL); + } + } + + postEditBoneDuplicate(arm->edbo, ob); + + ED_armature_edit_validate_active(arm); + + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); + } + MEM_freeN(objects); + + return OPERATOR_FINISHED; } - void ARMATURE_OT_duplicate(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Duplicate Selected Bone(s)"; - ot->idname = "ARMATURE_OT_duplicate"; - ot->description = "Make copies of the selected bones within the same armature"; - - /* api callbacks */ - ot->exec = armature_duplicate_selected_exec; - ot->poll = ED_operator_editarmature; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - RNA_def_boolean( - ot->srna, "do_flip_names", false, - "Flip Names", "Try to flip names of the bones, if possible, instead of adding a number extension"); + /* identifiers */ + ot->name = "Duplicate Selected Bone(s)"; + ot->idname = "ARMATURE_OT_duplicate"; + ot->description = "Make copies of the selected bones within the same armature"; + + /* api callbacks */ + ot->exec = armature_duplicate_selected_exec; + ot->poll = ED_operator_editarmature; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + RNA_def_boolean( + ot->srna, + "do_flip_names", + false, + "Flip Names", + "Try to flip names of the bones, if possible, instead of adding a number extension"); } /* Get the duplicated or existing mirrored copy of the bone. */ static EditBone *get_symmetrized_bone(bArmature *arm, EditBone *bone) { - if (bone == NULL) { - return NULL; - } - else if (bone->temp.ebone != NULL) { - return bone->temp.ebone; - } - else { - EditBone *mirror = ED_armature_ebone_get_mirrored(arm->edbo, bone); - return (mirror != NULL) ? mirror : bone; - } + if (bone == NULL) { + return NULL; + } + else if (bone->temp.ebone != NULL) { + return bone->temp.ebone; + } + else { + EditBone *mirror = ED_armature_ebone_get_mirrored(arm->edbo, bone); + return (mirror != NULL) ? mirror : bone; + } } /** @@ -643,222 +645,220 @@ static EditBone *get_symmetrized_bone(bArmature *arm, EditBone *bone) */ static int armature_symmetrize_exec(bContext *C, wmOperator *op) { - ViewLayer *view_layer = CTX_data_view_layer(C); - const int direction = RNA_enum_get(op->ptr, "direction"); - const int axis = 0; - - /* cancel if nothing selected */ - if (CTX_DATA_COUNT(C, selected_bones) == 0) { - return OPERATOR_CANCELLED; - } - - 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; - - EditBone *ebone_iter; - /* The beginning of the duplicated mirrored bones in the edbo list */ - EditBone *ebone_first_dupe = NULL; - - ED_armature_edit_sync_selection(arm->edbo); // XXX why is this needed? - - preEditBoneDuplicate(arm->edbo); - - /* Select mirrored bones */ - for (ebone_iter = arm->edbo->first; ebone_iter; ebone_iter = ebone_iter->next) { - if (EBONE_VISIBLE(arm, ebone_iter) && - (ebone_iter->flag & BONE_SELECTED)) - { - char name_flip[MAXBONENAME]; - - BLI_string_flip_side_name(name_flip, ebone_iter->name, false, sizeof(name_flip)); - - if (STREQ(name_flip, ebone_iter->name)) { - /* if the name matches, we don't have the potential to be mirrored, just skip */ - ebone_iter->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - } - else { - EditBone *ebone = ED_armature_ebone_find_name(arm->edbo, name_flip); - - if (ebone) { - if ((ebone->flag & BONE_SELECTED) == 0) { - /* simple case, we're selected, the other bone isn't! */ - ebone_iter->temp.ebone = ebone; - } - else { - /* complicated - choose which direction to copy */ - float axis_delta; - - axis_delta = ebone->head[axis] - ebone_iter->head[axis]; - if (axis_delta == 0.0f) { - axis_delta = ebone->tail[axis] - ebone_iter->tail[axis]; - } - - if (axis_delta == 0.0f) { - /* both mirrored bones exist and point to eachother and overlap exactly. - * - * in this case there's no well defined solution, so de-select both and skip. - */ - ebone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - ebone_iter->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - } - else { - EditBone *ebone_src, *ebone_dst; - if (((axis_delta < 0.0f) ? -1 : 1) == direction) { - ebone_src = ebone; - ebone_dst = ebone_iter; - } - else { - ebone_src = ebone_iter; - ebone_dst = ebone; - } - - ebone_src->temp.ebone = ebone_dst; - ebone_dst->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - } - } - } - } - } - } - - /* 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)) - { - char name_flip[MAXBONENAME]; - - BLI_string_flip_side_name(name_flip, ebone_iter->name, false, sizeof(name_flip)); - - /* bones must have a side-suffix */ - if (!STREQ(name_flip, ebone_iter->name)) { - EditBone *ebone; - - ebone = duplicateEditBone(ebone_iter, name_flip, arm->edbo, obedit); - - if (!ebone_first_dupe) { - ebone_first_dupe = ebone; - } - } - } - } - - /* 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) { - /* copy all flags except for ... */ - const int flag_copy = ((int)~0) & ~(BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL); - - EditBone *ebone = ebone_iter->temp.ebone; - - /* copy flags incase bone is pre-existing data */ - ebone->flag = (ebone->flag & ~flag_copy) | (ebone_iter->flag & flag_copy); - - if (ebone_iter->parent == NULL) { - /* If this bone has no parent, - * Set the duplicate->parent to NULL - */ - ebone->parent = NULL; - ebone->flag &= ~BONE_CONNECTED; - } - else { - /* the parent may have been duplicated, if not lookup the mirror parent */ - EditBone *ebone_parent = get_symmetrized_bone(arm, ebone_iter->parent); - - if (ebone_parent == ebone_iter->parent) { - /* If the mirror lookup failed, (but the current bone has a parent) - * 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; - } - - ebone->parent = ebone_parent; - } - - /* Update custom handle links. */ - ebone->bbone_prev = get_symmetrized_bone(arm, ebone_iter->bbone_prev); - ebone->bbone_next = get_symmetrized_bone(arm, ebone_iter->bbone_next); - - /* Lets try to fix any constraint subtargets that might - * have been duplicated - */ - updateDuplicateSubtarget(ebone, arm->edbo, obedit); - } - } - - ED_armature_edit_transform_mirror_update(obedit); - - /* Selected bones now have their 'temp' pointer set, - * so we don't need this anymore */ - - /* Deselect the old bones and select the new ones */ - 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 | BONE_TIPSEL | BONE_ROOTSEL); - } - } - - /* New bones will be selected, but some of the bones may already exist */ - for (ebone_iter = arm->edbo->first; - ebone_iter && ebone_iter != ebone_first_dupe; - ebone_iter = ebone_iter->next) - { - EditBone *ebone = ebone_iter->temp.ebone; - if (ebone && EBONE_SELECTABLE(arm, ebone)) { - ED_armature_ebone_select_set(ebone, true); - } - } - - /* correct the active bone */ - if (arm->act_edbone && arm->act_edbone->temp.ebone) { - arm->act_edbone = arm->act_edbone->temp.ebone; - } - - postEditBoneDuplicate(arm->edbo, obedit); - - ED_armature_edit_validate_active(arm); - - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit); - } - MEM_freeN(objects); - - return OPERATOR_FINISHED; + ViewLayer *view_layer = CTX_data_view_layer(C); + const int direction = RNA_enum_get(op->ptr, "direction"); + const int axis = 0; + + /* cancel if nothing selected */ + if (CTX_DATA_COUNT(C, selected_bones) == 0) { + return OPERATOR_CANCELLED; + } + + 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; + + EditBone *ebone_iter; + /* The beginning of the duplicated mirrored bones in the edbo list */ + EditBone *ebone_first_dupe = NULL; + + ED_armature_edit_sync_selection(arm->edbo); // XXX why is this needed? + + preEditBoneDuplicate(arm->edbo); + + /* Select mirrored bones */ + for (ebone_iter = arm->edbo->first; ebone_iter; ebone_iter = ebone_iter->next) { + if (EBONE_VISIBLE(arm, ebone_iter) && (ebone_iter->flag & BONE_SELECTED)) { + char name_flip[MAXBONENAME]; + + BLI_string_flip_side_name(name_flip, ebone_iter->name, false, sizeof(name_flip)); + + if (STREQ(name_flip, ebone_iter->name)) { + /* if the name matches, we don't have the potential to be mirrored, just skip */ + ebone_iter->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + } + else { + EditBone *ebone = ED_armature_ebone_find_name(arm->edbo, name_flip); + + if (ebone) { + if ((ebone->flag & BONE_SELECTED) == 0) { + /* simple case, we're selected, the other bone isn't! */ + ebone_iter->temp.ebone = ebone; + } + else { + /* complicated - choose which direction to copy */ + float axis_delta; + + axis_delta = ebone->head[axis] - ebone_iter->head[axis]; + if (axis_delta == 0.0f) { + axis_delta = ebone->tail[axis] - ebone_iter->tail[axis]; + } + + if (axis_delta == 0.0f) { + /* both mirrored bones exist and point to eachother and overlap exactly. + * + * in this case there's no well defined solution, so de-select both and skip. + */ + ebone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + ebone_iter->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + } + else { + EditBone *ebone_src, *ebone_dst; + if (((axis_delta < 0.0f) ? -1 : 1) == direction) { + ebone_src = ebone; + ebone_dst = ebone_iter; + } + else { + ebone_src = ebone_iter; + ebone_dst = ebone; + } + + ebone_src->temp.ebone = ebone_dst; + ebone_dst->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + } + } + } + } + } + } + + /* 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)) { + char name_flip[MAXBONENAME]; + + BLI_string_flip_side_name(name_flip, ebone_iter->name, false, sizeof(name_flip)); + + /* bones must have a side-suffix */ + if (!STREQ(name_flip, ebone_iter->name)) { + EditBone *ebone; + + ebone = duplicateEditBone(ebone_iter, name_flip, arm->edbo, obedit); + + if (!ebone_first_dupe) { + ebone_first_dupe = ebone; + } + } + } + } + + /* 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) { + /* copy all flags except for ... */ + const int flag_copy = ((int)~0) & ~(BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL); + + EditBone *ebone = ebone_iter->temp.ebone; + + /* copy flags incase bone is pre-existing data */ + ebone->flag = (ebone->flag & ~flag_copy) | (ebone_iter->flag & flag_copy); + + if (ebone_iter->parent == NULL) { + /* If this bone has no parent, + * Set the duplicate->parent to NULL + */ + ebone->parent = NULL; + ebone->flag &= ~BONE_CONNECTED; + } + else { + /* the parent may have been duplicated, if not lookup the mirror parent */ + EditBone *ebone_parent = get_symmetrized_bone(arm, ebone_iter->parent); + + if (ebone_parent == ebone_iter->parent) { + /* If the mirror lookup failed, (but the current bone has a parent) + * 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; + } + + ebone->parent = ebone_parent; + } + + /* Update custom handle links. */ + ebone->bbone_prev = get_symmetrized_bone(arm, ebone_iter->bbone_prev); + ebone->bbone_next = get_symmetrized_bone(arm, ebone_iter->bbone_next); + + /* Lets try to fix any constraint subtargets that might + * have been duplicated + */ + updateDuplicateSubtarget(ebone, arm->edbo, obedit); + } + } + + ED_armature_edit_transform_mirror_update(obedit); + + /* Selected bones now have their 'temp' pointer set, + * so we don't need this anymore */ + + /* Deselect the old bones and select the new ones */ + 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 | BONE_TIPSEL | BONE_ROOTSEL); + } + } + + /* New bones will be selected, but some of the bones may already exist */ + for (ebone_iter = arm->edbo->first; ebone_iter && ebone_iter != ebone_first_dupe; + ebone_iter = ebone_iter->next) { + EditBone *ebone = ebone_iter->temp.ebone; + if (ebone && EBONE_SELECTABLE(arm, ebone)) { + ED_armature_ebone_select_set(ebone, true); + } + } + + /* correct the active bone */ + if (arm->act_edbone && arm->act_edbone->temp.ebone) { + arm->act_edbone = arm->act_edbone->temp.ebone; + } + + postEditBoneDuplicate(arm->edbo, obedit); + + ED_armature_edit_validate_active(arm); + + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit); + } + MEM_freeN(objects); + + return OPERATOR_FINISHED; } /* following conventions from #MESH_OT_symmetrize */ void ARMATURE_OT_symmetrize(wmOperatorType *ot) { - /* subset of 'rna_enum_symmetrize_direction_items' */ - static const EnumPropertyItem arm_symmetrize_direction_items[] = { - {-1, "NEGATIVE_X", 0, "-X to +X", ""}, - {+1, "POSITIVE_X", 0, "+X to -X", ""}, - {0, NULL, 0, NULL, NULL}, - }; - - /* identifiers */ - ot->name = "Symmetrize"; - ot->idname = "ARMATURE_OT_symmetrize"; - ot->description = "Enforce symmetry, make copies of the selection or use existing"; - - /* api callbacks */ - ot->exec = armature_symmetrize_exec; - ot->poll = ED_operator_editarmature; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - ot->prop = RNA_def_enum( - ot->srna, "direction", arm_symmetrize_direction_items, -1, - "Direction", "Which sides to copy from and to (when both are selected)"); + /* subset of 'rna_enum_symmetrize_direction_items' */ + static const EnumPropertyItem arm_symmetrize_direction_items[] = { + {-1, "NEGATIVE_X", 0, "-X to +X", ""}, + {+1, "POSITIVE_X", 0, "+X to -X", ""}, + {0, NULL, 0, NULL, NULL}, + }; + + /* identifiers */ + ot->name = "Symmetrize"; + ot->idname = "ARMATURE_OT_symmetrize"; + ot->description = "Enforce symmetry, make copies of the selection or use existing"; + + /* api callbacks */ + ot->exec = armature_symmetrize_exec; + ot->poll = ED_operator_editarmature; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + ot->prop = RNA_def_enum(ot->srna, + "direction", + arm_symmetrize_direction_items, + -1, + "Direction", + "Which sides to copy from and to (when both are selected)"); } /* ------------------------------------------ */ @@ -868,186 +868,189 @@ void ARMATURE_OT_symmetrize(wmOperatorType *ot) /* if forked && mirror-edit: makes two bones with flipped names */ static int armature_extrude_exec(bContext *C, wmOperator *op) { - ViewLayer *view_layer = CTX_data_view_layer(C); - const bool forked = RNA_boolean_get(op->ptr, "forked"); - bool changed_multi = false; - - 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 forked_iter = forked; - - EditBone *newbone = NULL, *ebone, *flipbone, *first = NULL; - int a, totbone = 0, do_extrude; - - /* since we allow root extrude too, we have to make sure selection is OK */ - for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { - if (EBONE_VISIBLE(arm, ebone)) { - if (ebone->flag & BONE_ROOTSEL) { - if (ebone->parent && (ebone->flag & BONE_CONNECTED)) { - if (ebone->parent->flag & BONE_TIPSEL) - ebone->flag &= ~BONE_ROOTSEL; - } - } - } - } - - /* Duplicate the necessary bones */ - for (ebone = arm->edbo->first; ((ebone) && (ebone != first)); ebone = ebone->next) { - if (EBONE_VISIBLE(arm, ebone)) { - /* we extrude per definition the tip */ - do_extrude = false; - if (ebone->flag & (BONE_TIPSEL | BONE_SELECTED)) { - do_extrude = true; - } - else if (ebone->flag & BONE_ROOTSEL) { - /* but, a bone with parent deselected we do the root... */ - if (ebone->parent && (ebone->parent->flag & BONE_TIPSEL)) { - /* pass */ - } - else { - do_extrude = 2; - } - } - - if (do_extrude) { - /* we re-use code for mirror editing... */ - flipbone = NULL; - if (arm->flag & ARM_MIRROR_EDIT) { - flipbone = ED_armature_ebone_get_mirrored(arm->edbo, ebone); - if (flipbone) { - forked_iter = 0; // we extrude 2 different bones - if (flipbone->flag & (BONE_TIPSEL | BONE_ROOTSEL | BONE_SELECTED)) - /* don't want this bone to be selected... */ - flipbone->flag &= ~(BONE_TIPSEL | BONE_SELECTED | BONE_ROOTSEL); - } - if ((flipbone == NULL) && (forked_iter)) - flipbone = ebone; - } - - for (a = 0; a < 2; a++) { - if (a == 1) { - if (flipbone == NULL) - break; - else { - SWAP(EditBone *, flipbone, ebone); - } - } - - totbone++; - newbone = MEM_callocN(sizeof(EditBone), "extrudebone"); - - if (do_extrude == true) { - copy_v3_v3(newbone->head, ebone->tail); - copy_v3_v3(newbone->tail, newbone->head); - newbone->parent = ebone; - - /* copies it, in case mirrored bone */ - newbone->flag = ebone->flag & (BONE_TIPSEL | BONE_RELATIVE_PARENTING); - - if (newbone->parent) newbone->flag |= BONE_CONNECTED; - } - else { - copy_v3_v3(newbone->head, ebone->head); - copy_v3_v3(newbone->tail, ebone->head); - newbone->parent = ebone->parent; - - newbone->flag = BONE_TIPSEL; - - if (newbone->parent && (ebone->flag & BONE_CONNECTED)) { - newbone->flag |= BONE_CONNECTED; - } - } - - newbone->weight = ebone->weight; - newbone->dist = ebone->dist; - newbone->xwidth = ebone->xwidth; - newbone->zwidth = ebone->zwidth; - newbone->rad_head = ebone->rad_tail; // don't copy entire bone... - newbone->rad_tail = ebone->rad_tail; - newbone->segments = 1; - newbone->layer = ebone->layer; - - /* Bendy-Bone parameters */ - newbone->roll1 = ebone->roll1; - newbone->roll2 = ebone->roll2; - newbone->curveInX = ebone->curveInX; - newbone->curveInY = ebone->curveInY; - newbone->curveOutX = ebone->curveOutX; - newbone->curveOutY = ebone->curveOutY; - newbone->ease1 = ebone->ease1; - newbone->ease2 = ebone->ease2; - newbone->scaleIn = ebone->scaleIn; - newbone->scaleOut = ebone->scaleOut; - - - BLI_strncpy(newbone->name, ebone->name, sizeof(newbone->name)); - - if (flipbone && forked_iter) { // only set if mirror edit - if (strlen(newbone->name) < (MAXBONENAME - 2)) { - if (a == 0) strcat(newbone->name, "_L"); - else strcat(newbone->name, "_R"); - } - } - ED_armature_ebone_unique_name(arm->edbo, newbone->name, NULL); - - /* Add the new bone to the list */ - BLI_addtail(arm->edbo, newbone); - if (!first) - first = newbone; - - /* restore ebone if we were flipping */ - if (a == 1 && flipbone) - SWAP(EditBone *, flipbone, ebone); - } - } - - /* Deselect the old bone */ - ebone->flag &= ~(BONE_TIPSEL | BONE_SELECTED | BONE_ROOTSEL); - } - } - /* if only one bone, make this one active */ - if (totbone == 1 && first) { - arm->act_edbone = first; - } - else { - arm->act_edbone = newbone; - } - - if (totbone == 0) { - continue; - } - - changed_multi = true; - - /* Transform the endpoints */ - ED_armature_edit_sync_selection(arm->edbo); - - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); - } - MEM_freeN(objects); - - return changed_multi ? OPERATOR_FINISHED : OPERATOR_CANCELLED; + ViewLayer *view_layer = CTX_data_view_layer(C); + const bool forked = RNA_boolean_get(op->ptr, "forked"); + bool changed_multi = false; + + 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 forked_iter = forked; + + EditBone *newbone = NULL, *ebone, *flipbone, *first = NULL; + int a, totbone = 0, do_extrude; + + /* since we allow root extrude too, we have to make sure selection is OK */ + for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { + if (EBONE_VISIBLE(arm, ebone)) { + if (ebone->flag & BONE_ROOTSEL) { + if (ebone->parent && (ebone->flag & BONE_CONNECTED)) { + if (ebone->parent->flag & BONE_TIPSEL) + ebone->flag &= ~BONE_ROOTSEL; + } + } + } + } + + /* Duplicate the necessary bones */ + for (ebone = arm->edbo->first; ((ebone) && (ebone != first)); ebone = ebone->next) { + if (EBONE_VISIBLE(arm, ebone)) { + /* we extrude per definition the tip */ + do_extrude = false; + if (ebone->flag & (BONE_TIPSEL | BONE_SELECTED)) { + do_extrude = true; + } + else if (ebone->flag & BONE_ROOTSEL) { + /* but, a bone with parent deselected we do the root... */ + if (ebone->parent && (ebone->parent->flag & BONE_TIPSEL)) { + /* pass */ + } + else { + do_extrude = 2; + } + } + + if (do_extrude) { + /* we re-use code for mirror editing... */ + flipbone = NULL; + if (arm->flag & ARM_MIRROR_EDIT) { + flipbone = ED_armature_ebone_get_mirrored(arm->edbo, ebone); + if (flipbone) { + forked_iter = 0; // we extrude 2 different bones + if (flipbone->flag & (BONE_TIPSEL | BONE_ROOTSEL | BONE_SELECTED)) + /* don't want this bone to be selected... */ + flipbone->flag &= ~(BONE_TIPSEL | BONE_SELECTED | BONE_ROOTSEL); + } + if ((flipbone == NULL) && (forked_iter)) + flipbone = ebone; + } + + for (a = 0; a < 2; a++) { + if (a == 1) { + if (flipbone == NULL) + break; + else { + SWAP(EditBone *, flipbone, ebone); + } + } + + totbone++; + newbone = MEM_callocN(sizeof(EditBone), "extrudebone"); + + if (do_extrude == true) { + copy_v3_v3(newbone->head, ebone->tail); + copy_v3_v3(newbone->tail, newbone->head); + newbone->parent = ebone; + + /* copies it, in case mirrored bone */ + newbone->flag = ebone->flag & (BONE_TIPSEL | BONE_RELATIVE_PARENTING); + + if (newbone->parent) + newbone->flag |= BONE_CONNECTED; + } + else { + copy_v3_v3(newbone->head, ebone->head); + copy_v3_v3(newbone->tail, ebone->head); + newbone->parent = ebone->parent; + + newbone->flag = BONE_TIPSEL; + + if (newbone->parent && (ebone->flag & BONE_CONNECTED)) { + newbone->flag |= BONE_CONNECTED; + } + } + + newbone->weight = ebone->weight; + newbone->dist = ebone->dist; + newbone->xwidth = ebone->xwidth; + newbone->zwidth = ebone->zwidth; + newbone->rad_head = ebone->rad_tail; // don't copy entire bone... + newbone->rad_tail = ebone->rad_tail; + newbone->segments = 1; + newbone->layer = ebone->layer; + + /* Bendy-Bone parameters */ + newbone->roll1 = ebone->roll1; + newbone->roll2 = ebone->roll2; + newbone->curveInX = ebone->curveInX; + newbone->curveInY = ebone->curveInY; + newbone->curveOutX = ebone->curveOutX; + newbone->curveOutY = ebone->curveOutY; + newbone->ease1 = ebone->ease1; + newbone->ease2 = ebone->ease2; + newbone->scaleIn = ebone->scaleIn; + newbone->scaleOut = ebone->scaleOut; + + BLI_strncpy(newbone->name, ebone->name, sizeof(newbone->name)); + + if (flipbone && forked_iter) { // only set if mirror edit + if (strlen(newbone->name) < (MAXBONENAME - 2)) { + if (a == 0) + strcat(newbone->name, "_L"); + else + strcat(newbone->name, "_R"); + } + } + ED_armature_ebone_unique_name(arm->edbo, newbone->name, NULL); + + /* Add the new bone to the list */ + BLI_addtail(arm->edbo, newbone); + if (!first) + first = newbone; + + /* restore ebone if we were flipping */ + if (a == 1 && flipbone) + SWAP(EditBone *, flipbone, ebone); + } + } + + /* Deselect the old bone */ + ebone->flag &= ~(BONE_TIPSEL | BONE_SELECTED | BONE_ROOTSEL); + } + } + /* if only one bone, make this one active */ + if (totbone == 1 && first) { + arm->act_edbone = first; + } + else { + arm->act_edbone = newbone; + } + + if (totbone == 0) { + continue; + } + + changed_multi = true; + + /* Transform the endpoints */ + ED_armature_edit_sync_selection(arm->edbo); + + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); + } + MEM_freeN(objects); + + return changed_multi ? OPERATOR_FINISHED : OPERATOR_CANCELLED; } void ARMATURE_OT_extrude(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Extrude"; - ot->idname = "ARMATURE_OT_extrude"; - ot->description = "Create new bones from the selected joints"; + /* identifiers */ + ot->name = "Extrude"; + ot->idname = "ARMATURE_OT_extrude"; + ot->description = "Create new bones from the selected joints"; - /* api callbacks */ - ot->exec = armature_extrude_exec; - ot->poll = ED_operator_editarmature; + /* api callbacks */ + ot->exec = armature_extrude_exec; + ot->poll = ED_operator_editarmature; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - /* props */ - RNA_def_boolean(ot->srna, "forked", 0, "Forked", ""); + /* props */ + RNA_def_boolean(ot->srna, "forked", 0, "Forked", ""); } /* ********************** Bone Add *************************************/ @@ -1056,62 +1059,62 @@ void ARMATURE_OT_extrude(wmOperatorType *ot) static int armature_bone_primitive_add_exec(bContext *C, wmOperator *op) { - RegionView3D *rv3d = CTX_wm_region_view3d(C); - Object *obedit = CTX_data_edit_object(C); - EditBone *bone; - float obmat[3][3], curs[3], viewmat[3][3], totmat[3][3], imat[3][3]; - char name[MAXBONENAME]; + RegionView3D *rv3d = CTX_wm_region_view3d(C); + Object *obedit = CTX_data_edit_object(C); + EditBone *bone; + float obmat[3][3], curs[3], viewmat[3][3], totmat[3][3], imat[3][3]; + char name[MAXBONENAME]; - RNA_string_get(op->ptr, "name", name); + RNA_string_get(op->ptr, "name", name); - copy_v3_v3(curs, CTX_data_scene(C)->cursor.location); + copy_v3_v3(curs, CTX_data_scene(C)->cursor.location); - /* Get inverse point for head and orientation for tail */ - invert_m4_m4(obedit->imat, obedit->obmat); - mul_m4_v3(obedit->imat, curs); + /* Get inverse point for head and orientation for tail */ + invert_m4_m4(obedit->imat, obedit->obmat); + mul_m4_v3(obedit->imat, curs); - if (rv3d && (U.flag & USER_ADD_VIEWALIGNED)) - copy_m3_m4(obmat, rv3d->viewmat); - else unit_m3(obmat); + if (rv3d && (U.flag & USER_ADD_VIEWALIGNED)) + copy_m3_m4(obmat, rv3d->viewmat); + else + unit_m3(obmat); - copy_m3_m4(viewmat, obedit->obmat); - mul_m3_m3m3(totmat, obmat, viewmat); - invert_m3_m3(imat, totmat); + copy_m3_m4(viewmat, obedit->obmat); + mul_m3_m3m3(totmat, obmat, viewmat); + invert_m3_m3(imat, totmat); - ED_armature_edit_deselect_all(obedit); + ED_armature_edit_deselect_all(obedit); - /* Create a bone */ - bone = ED_armature_ebone_add(obedit->data, name); + /* Create a bone */ + bone = ED_armature_ebone_add(obedit->data, name); - copy_v3_v3(bone->head, curs); + copy_v3_v3(bone->head, curs); - if (rv3d && (U.flag & USER_ADD_VIEWALIGNED)) - add_v3_v3v3(bone->tail, bone->head, imat[1]); // bone with unit length 1 - else - add_v3_v3v3(bone->tail, bone->head, imat[2]); // bone with unit length 1, pointing up Z + if (rv3d && (U.flag & USER_ADD_VIEWALIGNED)) + add_v3_v3v3(bone->tail, bone->head, imat[1]); // bone with unit length 1 + else + add_v3_v3v3(bone->tail, bone->head, imat[2]); // bone with unit length 1, pointing up Z - /* note, notifier might evolve */ - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit); + /* note, notifier might evolve */ + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit); - return OPERATOR_FINISHED; + return OPERATOR_FINISHED; } void ARMATURE_OT_bone_primitive_add(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Add Bone"; - ot->idname = "ARMATURE_OT_bone_primitive_add"; - ot->description = "Add a new bone located at the 3D-Cursor"; - - /* api callbacks */ - ot->exec = armature_bone_primitive_add_exec; - ot->poll = ED_operator_editarmature; + /* identifiers */ + ot->name = "Add Bone"; + ot->idname = "ARMATURE_OT_bone_primitive_add"; + ot->description = "Add a new bone located at the 3D-Cursor"; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* api callbacks */ + ot->exec = armature_bone_primitive_add_exec; + ot->poll = ED_operator_editarmature; - RNA_def_string(ot->srna, "name", "Bone", MAXBONENAME, "Name", "Name of the newly created bone"); + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + RNA_def_string(ot->srna, "name", "Bone", MAXBONENAME, "Name", "Name of the newly created bone"); } /* ********************** Subdivide *******************************/ @@ -1124,86 +1127,86 @@ void ARMATURE_OT_bone_primitive_add(wmOperatorType *ot) static int armature_subdivide_exec(bContext *C, wmOperator *op) { - Object *obedit = CTX_data_edit_object(C); - EditBone *newbone, *tbone; - int cuts, i; - - /* there may not be a number_cuts property defined (for 'simple' subdivide) */ - cuts = RNA_int_get(op->ptr, "number_cuts"); - - /* loop over all editable bones */ - // XXX the old code did this in reverse order though! - CTX_DATA_BEGIN_WITH_ID(C, EditBone *, ebone, selected_editable_bones, bArmature *, arm) - { - for (i = cuts + 1; i > 1; i--) { - /* compute cut ratio first */ - float cutratio = 1.0f / (float)i; - float cutratioI = 1.0f - cutratio; - - float val1[3]; - float val2[3]; - float val3[3]; - - newbone = MEM_mallocN(sizeof(EditBone), "ebone subdiv"); - *newbone = *ebone; - BLI_addtail(arm->edbo, newbone); - - /* calculate location of newbone->head */ - copy_v3_v3(val1, ebone->head); - copy_v3_v3(val2, ebone->tail); - copy_v3_v3(val3, newbone->head); - - val3[0] = val1[0] * cutratio + val2[0] * cutratioI; - val3[1] = val1[1] * cutratio + val2[1] * cutratioI; - val3[2] = val1[2] * cutratio + val2[2] * cutratioI; - - copy_v3_v3(newbone->head, val3); - copy_v3_v3(newbone->tail, ebone->tail); - copy_v3_v3(ebone->tail, newbone->head); - - newbone->rad_head = ((ebone->rad_head * cutratio) + (ebone->rad_tail * cutratioI)); - ebone->rad_tail = newbone->rad_head; - - newbone->flag |= BONE_CONNECTED; - - newbone->prop = NULL; - - ED_armature_ebone_unique_name(arm->edbo, newbone->name, NULL); - - /* correct parent bones */ - for (tbone = arm->edbo->first; tbone; tbone = tbone->next) { - if (tbone->parent == ebone) - tbone->parent = newbone; - } - newbone->parent = ebone; - } - } - CTX_DATA_END; - - /* note, notifier might evolve */ - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit); - - return OPERATOR_FINISHED; + Object *obedit = CTX_data_edit_object(C); + EditBone *newbone, *tbone; + int cuts, i; + + /* there may not be a number_cuts property defined (for 'simple' subdivide) */ + cuts = RNA_int_get(op->ptr, "number_cuts"); + + /* loop over all editable bones */ + // XXX the old code did this in reverse order though! + CTX_DATA_BEGIN_WITH_ID(C, EditBone *, ebone, selected_editable_bones, bArmature *, arm) + { + for (i = cuts + 1; i > 1; i--) { + /* compute cut ratio first */ + float cutratio = 1.0f / (float)i; + float cutratioI = 1.0f - cutratio; + + float val1[3]; + float val2[3]; + float val3[3]; + + newbone = MEM_mallocN(sizeof(EditBone), "ebone subdiv"); + *newbone = *ebone; + BLI_addtail(arm->edbo, newbone); + + /* calculate location of newbone->head */ + copy_v3_v3(val1, ebone->head); + copy_v3_v3(val2, ebone->tail); + copy_v3_v3(val3, newbone->head); + + val3[0] = val1[0] * cutratio + val2[0] * cutratioI; + val3[1] = val1[1] * cutratio + val2[1] * cutratioI; + val3[2] = val1[2] * cutratio + val2[2] * cutratioI; + + copy_v3_v3(newbone->head, val3); + copy_v3_v3(newbone->tail, ebone->tail); + copy_v3_v3(ebone->tail, newbone->head); + + newbone->rad_head = ((ebone->rad_head * cutratio) + (ebone->rad_tail * cutratioI)); + ebone->rad_tail = newbone->rad_head; + + newbone->flag |= BONE_CONNECTED; + + newbone->prop = NULL; + + ED_armature_ebone_unique_name(arm->edbo, newbone->name, NULL); + + /* correct parent bones */ + for (tbone = arm->edbo->first; tbone; tbone = tbone->next) { + if (tbone->parent == ebone) + tbone->parent = newbone; + } + newbone->parent = ebone; + } + } + CTX_DATA_END; + + /* note, notifier might evolve */ + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit); + + return OPERATOR_FINISHED; } void ARMATURE_OT_subdivide(wmOperatorType *ot) { - PropertyRNA *prop; + PropertyRNA *prop; - /* identifiers */ - ot->name = "Subdivide Multi"; - ot->idname = "ARMATURE_OT_subdivide"; - ot->description = "Break selected bones into chains of smaller bones"; + /* identifiers */ + ot->name = "Subdivide Multi"; + ot->idname = "ARMATURE_OT_subdivide"; + ot->description = "Break selected bones into chains of smaller bones"; - /* api callbacks */ - ot->exec = armature_subdivide_exec; - ot->poll = ED_operator_editarmature; + /* api callbacks */ + ot->exec = armature_subdivide_exec; + ot->poll = ED_operator_editarmature; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - /* Properties */ - prop = RNA_def_int(ot->srna, "number_cuts", 1, 1, 1000, "Number of Cuts", "", 1, 10); - /* avoid re-using last var because it can cause _very_ high poly meshes and annoy users (or worse crash) */ - RNA_def_property_flag(prop, PROP_SKIP_SAVE); + /* Properties */ + prop = RNA_def_int(ot->srna, "number_cuts", 1, 1, 1000, "Number of Cuts", "", 1, 10); + /* avoid re-using last var because it can cause _very_ high poly meshes and annoy users (or worse crash) */ + RNA_def_property_flag(prop, PROP_SKIP_SAVE); } diff --git a/source/blender/editors/armature/armature_edit.c b/source/blender/editors/armature/armature_edit.c index e3db0ca8f2c..dfcd0bf69f7 100644 --- a/source/blender/editors/armature/armature_edit.c +++ b/source/blender/editors/armature/armature_edit.c @@ -64,135 +64,136 @@ void ED_armature_transform_apply(Main *bmain, Object *ob, float mat[4][4], const bool do_props) { - bArmature *arm = ob->data; + bArmature *arm = ob->data; - /* Put the armature into editmode */ - ED_armature_to_edit(arm); + /* Put the armature into editmode */ + ED_armature_to_edit(arm); - /* Transform the bones */ - ED_armature_transform_bones(arm, mat, do_props); + /* Transform the bones */ + ED_armature_transform_bones(arm, mat, do_props); - /* Turn the list into an armature */ - ED_armature_from_edit(bmain, arm); - ED_armature_edit_free(arm); + /* Turn the list into an armature */ + ED_armature_from_edit(bmain, arm); + ED_armature_edit_free(arm); } void ED_armature_transform_bones(struct bArmature *arm, float mat[4][4], const bool do_props) { - EditBone *ebone; - float scale = mat4_to_scale(mat); /* store the scale of the matrix here to use on envelopes */ - float mat3[3][3]; - - copy_m3_m4(mat3, mat); - normalize_m3(mat3); - /* Do the rotations */ - for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { - float tmat[3][3]; - - /* find the current bone's roll matrix */ - ED_armature_ebone_to_mat3(ebone, tmat); - - /* transform the roll matrix */ - mul_m3_m3m3(tmat, mat3, tmat); - - /* transform the bone */ - mul_m4_v3(mat, ebone->head); - mul_m4_v3(mat, ebone->tail); - - /* apply the transformed roll back */ - mat3_to_vec_roll(tmat, NULL, &ebone->roll); - - if (do_props) { - ebone->rad_head *= scale; - ebone->rad_tail *= scale; - ebone->dist *= scale; - - /* we could be smarter and scale by the matrix along the x & z axis */ - ebone->xwidth *= scale; - ebone->zwidth *= scale; - } - } + EditBone *ebone; + float scale = mat4_to_scale(mat); /* store the scale of the matrix here to use on envelopes */ + float mat3[3][3]; + + copy_m3_m4(mat3, mat); + normalize_m3(mat3); + /* Do the rotations */ + for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { + float tmat[3][3]; + + /* find the current bone's roll matrix */ + ED_armature_ebone_to_mat3(ebone, tmat); + + /* transform the roll matrix */ + mul_m3_m3m3(tmat, mat3, tmat); + + /* transform the bone */ + mul_m4_v3(mat, ebone->head); + mul_m4_v3(mat, ebone->tail); + + /* apply the transformed roll back */ + mat3_to_vec_roll(tmat, NULL, &ebone->roll); + + if (do_props) { + ebone->rad_head *= scale; + ebone->rad_tail *= scale; + ebone->dist *= scale; + + /* we could be smarter and scale by the matrix along the x & z axis */ + ebone->xwidth *= scale; + ebone->zwidth *= scale; + } + } } void ED_armature_transform(Main *bmain, bArmature *arm, float mat[4][4], const bool do_props) { - if (arm->edbo) { - ED_armature_transform_bones(arm, mat, do_props); - } - else { - /* Put the armature into editmode */ - ED_armature_to_edit(arm); - - /* Transform the bones */ - ED_armature_transform_bones(arm, mat, do_props); - - /* Go back to object mode*/ - ED_armature_from_edit(bmain, arm); - ED_armature_edit_free(arm); - } + if (arm->edbo) { + ED_armature_transform_bones(arm, mat, do_props); + } + else { + /* Put the armature into editmode */ + ED_armature_to_edit(arm); + + /* Transform the bones */ + ED_armature_transform_bones(arm, mat, do_props); + + /* Go back to object mode*/ + ED_armature_from_edit(bmain, arm); + ED_armature_edit_free(arm); + } } /* exported for use in editors/object/ */ /* 0 == do center, 1 == center new, 2 == center cursor */ -void ED_armature_origin_set(Main *bmain, Object *ob, const float cursor[3], int centermode, int around) +void ED_armature_origin_set( + Main *bmain, Object *ob, const float cursor[3], int centermode, int around) { - const bool is_editmode = BKE_object_is_in_editmode(ob); - EditBone *ebone; - bArmature *arm = ob->data; - float cent[3]; - - /* Put the armature into editmode */ - if (is_editmode == false) { - ED_armature_to_edit(arm); - } - - /* Find the centerpoint */ - if (centermode == 2) { - copy_v3_v3(cent, cursor); - invert_m4_m4(ob->imat, ob->obmat); - mul_m4_v3(ob->imat, cent); - } - else { - if (around == V3D_AROUND_CENTER_MEDIAN) { - int total = 0; - zero_v3(cent); - for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { - total += 2; - add_v3_v3(cent, ebone->head); - add_v3_v3(cent, ebone->tail); - } - if (total) { - mul_v3_fl(cent, 1.0f / (float)total); - } - } - else { - float min[3], max[3]; - INIT_MINMAX(min, max); - for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { - minmax_v3v3_v3(min, max, ebone->head); - minmax_v3v3_v3(min, max, ebone->tail); - } - mid_v3_v3v3(cent, min, max); - } - } - - /* Do the adjustments */ - for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { - sub_v3_v3(ebone->head, cent); - sub_v3_v3(ebone->tail, cent); - } - - /* Turn the list into an armature */ - if (is_editmode == false) { - ED_armature_from_edit(bmain, arm); - ED_armature_edit_free(arm); - } - - /* Adjust object location for new centerpoint */ - if (centermode && (is_editmode == false)) { - mul_mat3_m4_v3(ob->obmat, cent); /* omit translation part */ - add_v3_v3(ob->loc, cent); - } + const bool is_editmode = BKE_object_is_in_editmode(ob); + EditBone *ebone; + bArmature *arm = ob->data; + float cent[3]; + + /* Put the armature into editmode */ + if (is_editmode == false) { + ED_armature_to_edit(arm); + } + + /* Find the centerpoint */ + if (centermode == 2) { + copy_v3_v3(cent, cursor); + invert_m4_m4(ob->imat, ob->obmat); + mul_m4_v3(ob->imat, cent); + } + else { + if (around == V3D_AROUND_CENTER_MEDIAN) { + int total = 0; + zero_v3(cent); + for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { + total += 2; + add_v3_v3(cent, ebone->head); + add_v3_v3(cent, ebone->tail); + } + if (total) { + mul_v3_fl(cent, 1.0f / (float)total); + } + } + else { + float min[3], max[3]; + INIT_MINMAX(min, max); + for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { + minmax_v3v3_v3(min, max, ebone->head); + minmax_v3v3_v3(min, max, ebone->tail); + } + mid_v3_v3v3(cent, min, max); + } + } + + /* Do the adjustments */ + for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { + sub_v3_v3(ebone->head, cent); + sub_v3_v3(ebone->tail, cent); + } + + /* Turn the list into an armature */ + if (is_editmode == false) { + ED_armature_from_edit(bmain, arm); + ED_armature_edit_free(arm); + } + + /* Adjust object location for new centerpoint */ + if (centermode && (is_editmode == false)) { + mul_mat3_m4_v3(ob->obmat, cent); /* omit translation part */ + add_v3_v3(ob->loc, cent); + } } /* ********************************* Roll ******************************* */ @@ -200,651 +201,676 @@ void ED_armature_origin_set(Main *bmain, Object *ob, const float cursor[3], int /* adjust bone roll to align Z axis with vector * vec is in local space and is normalized */ -float ED_armature_ebone_roll_to_vector(const EditBone *bone, const float align_axis[3], const bool axis_only) +float ED_armature_ebone_roll_to_vector(const EditBone *bone, + const float align_axis[3], + const bool axis_only) { - float mat[3][3], nor[3]; - float vec[3], align_axis_proj[3], roll = 0.0f; + float mat[3][3], nor[3]; + float vec[3], align_axis_proj[3], roll = 0.0f; - BLI_ASSERT_UNIT_V3(align_axis); + BLI_ASSERT_UNIT_V3(align_axis); - sub_v3_v3v3(nor, bone->tail, bone->head); + sub_v3_v3v3(nor, bone->tail, bone->head); - /* If tail == head or the bone is aligned with the axis... */ - if (normalize_v3(nor) <= FLT_EPSILON || (fabsf(dot_v3v3(align_axis, nor)) >= (1.0f - FLT_EPSILON))) { - return roll; - } + /* If tail == head or the bone is aligned with the axis... */ + if (normalize_v3(nor) <= FLT_EPSILON || + (fabsf(dot_v3v3(align_axis, nor)) >= (1.0f - FLT_EPSILON))) { + return roll; + } - vec_roll_to_mat3_normalized(nor, 0.0f, mat); + vec_roll_to_mat3_normalized(nor, 0.0f, mat); - /* project the new_up_axis along the normal */ - project_v3_v3v3_normalized(vec, align_axis, nor); - sub_v3_v3v3(align_axis_proj, align_axis, vec); + /* project the new_up_axis along the normal */ + project_v3_v3v3_normalized(vec, align_axis, nor); + sub_v3_v3v3(align_axis_proj, align_axis, vec); - if (axis_only) { - if (angle_v3v3(align_axis_proj, mat[2]) > (float)(M_PI_2)) { - negate_v3(align_axis_proj); - } - } + if (axis_only) { + if (angle_v3v3(align_axis_proj, mat[2]) > (float)(M_PI_2)) { + negate_v3(align_axis_proj); + } + } - roll = angle_v3v3(align_axis_proj, mat[2]); + roll = angle_v3v3(align_axis_proj, mat[2]); - cross_v3_v3v3(vec, mat[2], align_axis_proj); + cross_v3_v3v3(vec, mat[2], align_axis_proj); - if (dot_v3v3(vec, nor) < 0.0f) { - return -roll; - } - return roll; + if (dot_v3v3(vec, nor) < 0.0f) { + return -roll; + } + return roll; } /* note, ranges arithmetic is used below */ typedef enum eCalcRollTypes { - /* pos */ - CALC_ROLL_POS_X = 0, - CALC_ROLL_POS_Y, - CALC_ROLL_POS_Z, - - CALC_ROLL_TAN_POS_X, - CALC_ROLL_TAN_POS_Z, - - /* neg */ - CALC_ROLL_NEG_X, - CALC_ROLL_NEG_Y, - CALC_ROLL_NEG_Z, - - CALC_ROLL_TAN_NEG_X, - CALC_ROLL_TAN_NEG_Z, - - /* no sign */ - CALC_ROLL_ACTIVE, - CALC_ROLL_VIEW, - CALC_ROLL_CURSOR, + /* pos */ + CALC_ROLL_POS_X = 0, + CALC_ROLL_POS_Y, + CALC_ROLL_POS_Z, + + CALC_ROLL_TAN_POS_X, + CALC_ROLL_TAN_POS_Z, + + /* neg */ + CALC_ROLL_NEG_X, + CALC_ROLL_NEG_Y, + CALC_ROLL_NEG_Z, + + CALC_ROLL_TAN_NEG_X, + CALC_ROLL_TAN_NEG_Z, + + /* no sign */ + CALC_ROLL_ACTIVE, + CALC_ROLL_VIEW, + CALC_ROLL_CURSOR, } eCalcRollTypes; static const EnumPropertyItem prop_calc_roll_types[] = { - {0, "", 0, N_("Positive"), ""}, - {CALC_ROLL_TAN_POS_X, "POS_X", 0, "Local +X Tangent", ""}, - {CALC_ROLL_TAN_POS_Z, "POS_Z", 0, "Local +Z Tangent", ""}, + {0, "", 0, N_("Positive"), ""}, + {CALC_ROLL_TAN_POS_X, "POS_X", 0, "Local +X Tangent", ""}, + {CALC_ROLL_TAN_POS_Z, "POS_Z", 0, "Local +Z Tangent", ""}, - {CALC_ROLL_POS_X, "GLOBAL_POS_X", 0, "Global +X Axis", ""}, - {CALC_ROLL_POS_Y, "GLOBAL_POS_Y", 0, "Global +Y Axis", ""}, - {CALC_ROLL_POS_Z, "GLOBAL_POS_Z", 0, "Global +Z Axis", ""}, + {CALC_ROLL_POS_X, "GLOBAL_POS_X", 0, "Global +X Axis", ""}, + {CALC_ROLL_POS_Y, "GLOBAL_POS_Y", 0, "Global +Y Axis", ""}, + {CALC_ROLL_POS_Z, "GLOBAL_POS_Z", 0, "Global +Z Axis", ""}, - {0, "", 0, N_("Negative"), ""}, + {0, "", 0, N_("Negative"), ""}, - {CALC_ROLL_TAN_NEG_X, "NEG_X", 0, "Local -X Tangent", ""}, - {CALC_ROLL_TAN_NEG_Z, "NEG_Z", 0, "Local -Z Tangent", ""}, + {CALC_ROLL_TAN_NEG_X, "NEG_X", 0, "Local -X Tangent", ""}, + {CALC_ROLL_TAN_NEG_Z, "NEG_Z", 0, "Local -Z Tangent", ""}, - {CALC_ROLL_NEG_X, "GLOBAL_NEG_X", 0, "Global -X Axis", ""}, - {CALC_ROLL_NEG_Y, "GLOBAL_NEG_Y", 0, "Global -Y Axis", ""}, - {CALC_ROLL_NEG_Z, "GLOBAL_NEG_Z", 0, "Global -Z Axis", ""}, + {CALC_ROLL_NEG_X, "GLOBAL_NEG_X", 0, "Global -X Axis", ""}, + {CALC_ROLL_NEG_Y, "GLOBAL_NEG_Y", 0, "Global -Y Axis", ""}, + {CALC_ROLL_NEG_Z, "GLOBAL_NEG_Z", 0, "Global -Z Axis", ""}, - {0, "", 0, N_("Other"), ""}, - {CALC_ROLL_ACTIVE, "ACTIVE", 0, "Active Bone", ""}, - {CALC_ROLL_VIEW, "VIEW", 0, "View Axis", ""}, - {CALC_ROLL_CURSOR, "CURSOR", 0, "Cursor", ""}, - {0, NULL, 0, NULL, NULL}, + {0, "", 0, N_("Other"), ""}, + {CALC_ROLL_ACTIVE, "ACTIVE", 0, "Active Bone", ""}, + {CALC_ROLL_VIEW, "VIEW", 0, "View Axis", ""}, + {CALC_ROLL_CURSOR, "CURSOR", 0, "Cursor", ""}, + {0, NULL, 0, NULL, NULL}, }; - static int armature_calc_roll_exec(bContext *C, wmOperator *op) { - ViewLayer *view_layer = CTX_data_view_layer(C); - Object *ob_active = CTX_data_edit_object(C); - int ret = OPERATOR_FINISHED; - - eCalcRollTypes type = RNA_enum_get(op->ptr, "type"); - const bool axis_only = RNA_boolean_get(op->ptr, "axis_only"); - /* axis_flip when matching the active bone never makes sense */ - bool axis_flip = ((type >= CALC_ROLL_ACTIVE) ? RNA_boolean_get(op->ptr, "axis_flip") : - (type >= CALC_ROLL_TAN_NEG_X) ? true : false); - - 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 changed = false; - - float imat[3][3]; - EditBone *ebone; - - if ((type >= CALC_ROLL_NEG_X) && (type <= CALC_ROLL_TAN_NEG_Z)) { - type -= (CALC_ROLL_ACTIVE - CALC_ROLL_NEG_X); - axis_flip = true; - } - - copy_m3_m4(imat, ob->obmat); - invert_m3(imat); - - if (type == CALC_ROLL_CURSOR) { /* Cursor */ - Scene *scene = CTX_data_scene(C); - float cursor_local[3]; - const View3DCursor *cursor = &scene->cursor; - - invert_m4_m4(ob->imat, ob->obmat); - copy_v3_v3(cursor_local, cursor->location); - mul_m4_v3(ob->imat, cursor_local); - - /* cursor */ - for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { - if (EBONE_VISIBLE(arm, ebone) && EBONE_EDITABLE(ebone)) { - float cursor_rel[3]; - sub_v3_v3v3(cursor_rel, cursor_local, ebone->head); - if (axis_flip) negate_v3(cursor_rel); - if (normalize_v3(cursor_rel) != 0.0f) { - ebone->roll = ED_armature_ebone_roll_to_vector(ebone, cursor_rel, axis_only); - changed = true; - } - } - } - } - else if (ELEM(type, CALC_ROLL_TAN_POS_X, CALC_ROLL_TAN_POS_Z)) { - for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { - if (ebone->parent) { - bool is_edit = (EBONE_VISIBLE(arm, ebone) && EBONE_EDITABLE(ebone)); - bool is_edit_parent = (EBONE_VISIBLE(arm, ebone->parent) && EBONE_EDITABLE(ebone->parent)); - - if (is_edit || is_edit_parent) { - EditBone *ebone_other = ebone->parent; - float dir_a[3]; - float dir_b[3]; - float vec[3]; - bool is_vec_zero; - - sub_v3_v3v3(dir_a, ebone->tail, ebone->head); - normalize_v3(dir_a); - - /* find the first bone in the chane with a different direction */ - do { - sub_v3_v3v3(dir_b, ebone_other->head, ebone_other->tail); - normalize_v3(dir_b); - - if (type == CALC_ROLL_TAN_POS_Z) { - cross_v3_v3v3(vec, dir_a, dir_b); - } - else { - add_v3_v3v3(vec, dir_a, dir_b); - } - } while ((is_vec_zero = (normalize_v3(vec) < 0.00001f)) && - (ebone_other = ebone_other->parent)); - - if (!is_vec_zero) { - if (axis_flip) negate_v3(vec); - - if (is_edit) { - ebone->roll = ED_armature_ebone_roll_to_vector(ebone, vec, axis_only); - changed = true; - } - - /* parentless bones use cross product with child */ - if (is_edit_parent) { - if (ebone->parent->parent == NULL) { - ebone->parent->roll = ED_armature_ebone_roll_to_vector(ebone->parent, vec, axis_only); - changed = true; - } - } - } - } - } - } - } - else { - float vec[3] = {0.0f, 0.0f, 0.0f}; - if (type == CALC_ROLL_VIEW) { /* View */ - RegionView3D *rv3d = CTX_wm_region_view3d(C); - if (rv3d == NULL) { - BKE_report(op->reports, RPT_ERROR, "No region view3d available"); - ret = OPERATOR_CANCELLED; - goto cleanup; - } - - copy_v3_v3(vec, rv3d->viewinv[2]); - mul_m3_v3(imat, vec); - } - else if (type == CALC_ROLL_ACTIVE) { - float mat[3][3]; - bArmature *arm_active = ob_active->data; - ebone = (EditBone *)arm_active->act_edbone; - if (ebone == NULL) { - BKE_report(op->reports, RPT_ERROR, "No active bone set"); - ret = OPERATOR_CANCELLED; - goto cleanup; - } - - ED_armature_ebone_to_mat3(ebone, mat); - copy_v3_v3(vec, mat[2]); - } - else { /* Axis */ - assert(type <= 5); - if (type < 3) vec[type] = 1.0f; - else vec[type - 2] = -1.0f; - mul_m3_v3(imat, vec); - normalize_v3(vec); - } - - if (axis_flip) negate_v3(vec); - - for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { - if (EBONE_VISIBLE(arm, ebone) && EBONE_EDITABLE(ebone)) { - /* roll func is a callback which assumes that all is well */ - ebone->roll = ED_armature_ebone_roll_to_vector(ebone, vec, axis_only); - changed = true; - } - } - } - - if (arm->flag & ARM_MIRROR_EDIT) { - for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { - 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))) { - ebone->roll = -ebone_mirr->roll; - } - } - } - } - - if (changed) { - /* note, notifier might evolve */ - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); - } - } + ViewLayer *view_layer = CTX_data_view_layer(C); + Object *ob_active = CTX_data_edit_object(C); + int ret = OPERATOR_FINISHED; + + eCalcRollTypes type = RNA_enum_get(op->ptr, "type"); + const bool axis_only = RNA_boolean_get(op->ptr, "axis_only"); + /* axis_flip when matching the active bone never makes sense */ + bool axis_flip = ((type >= CALC_ROLL_ACTIVE) ? RNA_boolean_get(op->ptr, "axis_flip") : + (type >= CALC_ROLL_TAN_NEG_X) ? true : false); + + 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 changed = false; + + float imat[3][3]; + EditBone *ebone; + + if ((type >= CALC_ROLL_NEG_X) && (type <= CALC_ROLL_TAN_NEG_Z)) { + type -= (CALC_ROLL_ACTIVE - CALC_ROLL_NEG_X); + axis_flip = true; + } + + copy_m3_m4(imat, ob->obmat); + invert_m3(imat); + + if (type == CALC_ROLL_CURSOR) { /* Cursor */ + Scene *scene = CTX_data_scene(C); + float cursor_local[3]; + const View3DCursor *cursor = &scene->cursor; + + invert_m4_m4(ob->imat, ob->obmat); + copy_v3_v3(cursor_local, cursor->location); + mul_m4_v3(ob->imat, cursor_local); + + /* cursor */ + for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { + if (EBONE_VISIBLE(arm, ebone) && EBONE_EDITABLE(ebone)) { + float cursor_rel[3]; + sub_v3_v3v3(cursor_rel, cursor_local, ebone->head); + if (axis_flip) + negate_v3(cursor_rel); + if (normalize_v3(cursor_rel) != 0.0f) { + ebone->roll = ED_armature_ebone_roll_to_vector(ebone, cursor_rel, axis_only); + changed = true; + } + } + } + } + else if (ELEM(type, CALC_ROLL_TAN_POS_X, CALC_ROLL_TAN_POS_Z)) { + for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { + if (ebone->parent) { + bool is_edit = (EBONE_VISIBLE(arm, ebone) && EBONE_EDITABLE(ebone)); + bool is_edit_parent = (EBONE_VISIBLE(arm, ebone->parent) && + EBONE_EDITABLE(ebone->parent)); + + if (is_edit || is_edit_parent) { + EditBone *ebone_other = ebone->parent; + float dir_a[3]; + float dir_b[3]; + float vec[3]; + bool is_vec_zero; + + sub_v3_v3v3(dir_a, ebone->tail, ebone->head); + normalize_v3(dir_a); + + /* find the first bone in the chane with a different direction */ + do { + sub_v3_v3v3(dir_b, ebone_other->head, ebone_other->tail); + normalize_v3(dir_b); + + if (type == CALC_ROLL_TAN_POS_Z) { + cross_v3_v3v3(vec, dir_a, dir_b); + } + else { + add_v3_v3v3(vec, dir_a, dir_b); + } + } while ((is_vec_zero = (normalize_v3(vec) < 0.00001f)) && + (ebone_other = ebone_other->parent)); + + if (!is_vec_zero) { + if (axis_flip) + negate_v3(vec); + + if (is_edit) { + ebone->roll = ED_armature_ebone_roll_to_vector(ebone, vec, axis_only); + changed = true; + } + + /* parentless bones use cross product with child */ + if (is_edit_parent) { + if (ebone->parent->parent == NULL) { + ebone->parent->roll = ED_armature_ebone_roll_to_vector( + ebone->parent, vec, axis_only); + changed = true; + } + } + } + } + } + } + } + else { + float vec[3] = {0.0f, 0.0f, 0.0f}; + if (type == CALC_ROLL_VIEW) { /* View */ + RegionView3D *rv3d = CTX_wm_region_view3d(C); + if (rv3d == NULL) { + BKE_report(op->reports, RPT_ERROR, "No region view3d available"); + ret = OPERATOR_CANCELLED; + goto cleanup; + } + + copy_v3_v3(vec, rv3d->viewinv[2]); + mul_m3_v3(imat, vec); + } + else if (type == CALC_ROLL_ACTIVE) { + float mat[3][3]; + bArmature *arm_active = ob_active->data; + ebone = (EditBone *)arm_active->act_edbone; + if (ebone == NULL) { + BKE_report(op->reports, RPT_ERROR, "No active bone set"); + ret = OPERATOR_CANCELLED; + goto cleanup; + } + + ED_armature_ebone_to_mat3(ebone, mat); + copy_v3_v3(vec, mat[2]); + } + else { /* Axis */ + assert(type <= 5); + if (type < 3) + vec[type] = 1.0f; + else + vec[type - 2] = -1.0f; + mul_m3_v3(imat, vec); + normalize_v3(vec); + } + + if (axis_flip) + negate_v3(vec); + + for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { + if (EBONE_VISIBLE(arm, ebone) && EBONE_EDITABLE(ebone)) { + /* roll func is a callback which assumes that all is well */ + ebone->roll = ED_armature_ebone_roll_to_vector(ebone, vec, axis_only); + changed = true; + } + } + } + + if (arm->flag & ARM_MIRROR_EDIT) { + for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { + 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))) { + ebone->roll = -ebone_mirr->roll; + } + } + } + } + + if (changed) { + /* note, notifier might evolve */ + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); + } + } cleanup: - MEM_freeN(objects); - return ret; + MEM_freeN(objects); + return ret; } void ARMATURE_OT_calculate_roll(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Recalculate Roll"; - ot->idname = "ARMATURE_OT_calculate_roll"; - ot->description = "Automatically fix alignment of select bones' axes"; - - /* api callbacks */ - ot->invoke = WM_menu_invoke; - ot->exec = armature_calc_roll_exec; - ot->poll = ED_operator_editarmature; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - ot->prop = RNA_def_enum(ot->srna, "type", prop_calc_roll_types, CALC_ROLL_TAN_POS_X, "Type", ""); - RNA_def_boolean(ot->srna, "axis_flip", 0, "Flip Axis", "Negate the alignment axis"); - RNA_def_boolean(ot->srna, "axis_only", 0, "Shortest Rotation", "Ignore the axis direction, use the shortest rotation to align"); + /* identifiers */ + ot->name = "Recalculate Roll"; + ot->idname = "ARMATURE_OT_calculate_roll"; + ot->description = "Automatically fix alignment of select bones' axes"; + + /* api callbacks */ + ot->invoke = WM_menu_invoke; + ot->exec = armature_calc_roll_exec; + ot->poll = ED_operator_editarmature; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + ot->prop = RNA_def_enum(ot->srna, "type", prop_calc_roll_types, CALC_ROLL_TAN_POS_X, "Type", ""); + RNA_def_boolean(ot->srna, "axis_flip", 0, "Flip Axis", "Negate the alignment axis"); + RNA_def_boolean(ot->srna, + "axis_only", + 0, + "Shortest Rotation", + "Ignore the axis direction, use the shortest rotation to align"); } static int armature_roll_clear_exec(bContext *C, wmOperator *op) { - ViewLayer *view_layer = CTX_data_view_layer(C); - const float roll = RNA_float_get(op->ptr, "roll"); - - 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 changed = false; - - for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { - if (EBONE_VISIBLE(arm, ebone) && EBONE_EDITABLE(ebone)) { - /* Roll func is a callback which assumes that all is well. */ - ebone->roll = roll; - changed = true; - } - } - - if (arm->flag & ARM_MIRROR_EDIT) { - for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { - 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))) { - ebone->roll = -ebone_mirr->roll; - changed = true; - } - } - } - } - - if (changed) { - /* Note, notifier might evolve. */ - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); - } - } - MEM_freeN(objects); - - return OPERATOR_FINISHED; + ViewLayer *view_layer = CTX_data_view_layer(C); + const float roll = RNA_float_get(op->ptr, "roll"); + + 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 changed = false; + + for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { + if (EBONE_VISIBLE(arm, ebone) && EBONE_EDITABLE(ebone)) { + /* Roll func is a callback which assumes that all is well. */ + ebone->roll = roll; + changed = true; + } + } + + if (arm->flag & ARM_MIRROR_EDIT) { + for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { + 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))) { + ebone->roll = -ebone_mirr->roll; + changed = true; + } + } + } + } + + if (changed) { + /* Note, notifier might evolve. */ + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); + } + } + MEM_freeN(objects); + + return OPERATOR_FINISHED; } void ARMATURE_OT_roll_clear(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Clear Roll"; - ot->idname = "ARMATURE_OT_roll_clear"; - ot->description = "Clear roll for selected bones"; - - /* api callbacks */ - ot->exec = armature_roll_clear_exec; - ot->poll = ED_operator_editarmature; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - RNA_def_float_rotation( - ot->srna, "roll", 0, NULL, DEG2RADF(-360.0f), DEG2RADF(360.0f), - "Roll", "", DEG2RADF(-360.0f), DEG2RADF(360.0f)); + /* identifiers */ + ot->name = "Clear Roll"; + ot->idname = "ARMATURE_OT_roll_clear"; + ot->description = "Clear roll for selected bones"; + + /* api callbacks */ + ot->exec = armature_roll_clear_exec; + ot->poll = ED_operator_editarmature; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + RNA_def_float_rotation(ot->srna, + "roll", + 0, + NULL, + DEG2RADF(-360.0f), + DEG2RADF(360.0f), + "Roll", + "", + DEG2RADF(-360.0f), + DEG2RADF(360.0f)); } /* ******************************** Chain-Based Tools ********************************* */ /* temporary data-structure for merge/fill bones */ typedef struct EditBonePoint { - struct EditBonePoint *next, *prev; + struct EditBonePoint *next, *prev; - EditBone *head_owner; /* EditBone which uses this point as a 'head' point */ - EditBone *tail_owner; /* EditBone which uses this point as a 'tail' point */ + EditBone *head_owner; /* EditBone which uses this point as a 'head' point */ + EditBone *tail_owner; /* EditBone which uses this point as a 'tail' point */ - float vec[3]; /* the actual location of the point in local/EditMode space */ + float vec[3]; /* the actual location of the point in local/EditMode space */ } EditBonePoint; /* find chain-tips (i.e. bones without children) */ static void chains_find_tips(ListBase *edbo, ListBase *list) { - EditBone *curBone, *ebo; - LinkData *ld; - - /* note: this is potentially very slow ... there's got to be a better way */ - for (curBone = edbo->first; curBone; curBone = curBone->next) { - short stop = 0; - - /* is this bone contained within any existing chain? (skip if so) */ - for (ld = list->first; ld; ld = ld->next) { - for (ebo = ld->data; ebo; ebo = ebo->parent) { - if (ebo == curBone) { - stop = 1; - break; - } - } - - if (stop) break; - } - /* skip current bone if it is part of an existing chain */ - if (stop) continue; - - /* is any existing chain part of the chain formed by this bone? */ - stop = 0; - for (ebo = curBone->parent; ebo; ebo = ebo->parent) { - for (ld = list->first; ld; ld = ld->next) { - if (ld->data == ebo) { - ld->data = curBone; - stop = 1; - break; - } - } - - if (stop) break; - } - /* current bone has already been added to a chain? */ - if (stop) continue; - - /* add current bone to a new chain */ - ld = MEM_callocN(sizeof(LinkData), "BoneChain"); - ld->data = curBone; - BLI_addtail(list, ld); - } + EditBone *curBone, *ebo; + LinkData *ld; + + /* note: this is potentially very slow ... there's got to be a better way */ + for (curBone = edbo->first; curBone; curBone = curBone->next) { + short stop = 0; + + /* is this bone contained within any existing chain? (skip if so) */ + for (ld = list->first; ld; ld = ld->next) { + for (ebo = ld->data; ebo; ebo = ebo->parent) { + if (ebo == curBone) { + stop = 1; + break; + } + } + + if (stop) + break; + } + /* skip current bone if it is part of an existing chain */ + if (stop) + continue; + + /* is any existing chain part of the chain formed by this bone? */ + stop = 0; + for (ebo = curBone->parent; ebo; ebo = ebo->parent) { + for (ld = list->first; ld; ld = ld->next) { + if (ld->data == ebo) { + ld->data = curBone; + stop = 1; + break; + } + } + + if (stop) + break; + } + /* current bone has already been added to a chain? */ + if (stop) + continue; + + /* add current bone to a new chain */ + ld = MEM_callocN(sizeof(LinkData), "BoneChain"); + ld->data = curBone; + BLI_addtail(list, ld); + } } /* --------------------- */ static void fill_add_joint(EditBone *ebo, short eb_tail, ListBase *points) { - EditBonePoint *ebp; - float vec[3]; - short found = 0; - - if (eb_tail) { - copy_v3_v3(vec, ebo->tail); - } - else { - copy_v3_v3(vec, ebo->head); - } - - for (ebp = points->first; ebp; ebp = ebp->next) { - if (equals_v3v3(ebp->vec, vec)) { - if (eb_tail) { - if ((ebp->head_owner) && (ebp->head_owner->parent == ebo)) { - /* so this bone's tail owner is this bone */ - ebp->tail_owner = ebo; - found = 1; - break; - } - } - else { - if ((ebp->tail_owner) && (ebo->parent == ebp->tail_owner)) { - /* so this bone's head owner is this bone */ - ebp->head_owner = ebo; - found = 1; - break; - } - } - } - } - - /* allocate a new point if no existing point was related */ - if (found == 0) { - ebp = MEM_callocN(sizeof(EditBonePoint), "EditBonePoint"); - - if (eb_tail) { - copy_v3_v3(ebp->vec, ebo->tail); - ebp->tail_owner = ebo; - } - else { - copy_v3_v3(ebp->vec, ebo->head); - ebp->head_owner = ebo; - } - - BLI_addtail(points, ebp); - } + EditBonePoint *ebp; + float vec[3]; + short found = 0; + + if (eb_tail) { + copy_v3_v3(vec, ebo->tail); + } + else { + copy_v3_v3(vec, ebo->head); + } + + for (ebp = points->first; ebp; ebp = ebp->next) { + if (equals_v3v3(ebp->vec, vec)) { + if (eb_tail) { + if ((ebp->head_owner) && (ebp->head_owner->parent == ebo)) { + /* so this bone's tail owner is this bone */ + ebp->tail_owner = ebo; + found = 1; + break; + } + } + else { + if ((ebp->tail_owner) && (ebo->parent == ebp->tail_owner)) { + /* so this bone's head owner is this bone */ + ebp->head_owner = ebo; + found = 1; + break; + } + } + } + } + + /* allocate a new point if no existing point was related */ + if (found == 0) { + ebp = MEM_callocN(sizeof(EditBonePoint), "EditBonePoint"); + + if (eb_tail) { + copy_v3_v3(ebp->vec, ebo->tail); + ebp->tail_owner = ebo; + } + else { + copy_v3_v3(ebp->vec, ebo->head); + ebp->head_owner = ebo; + } + + BLI_addtail(points, ebp); + } } /* bone adding between selected joints */ static int armature_fill_bones_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); - View3D *v3d = CTX_wm_view3d(C); - ListBase points = {NULL, NULL}; - EditBone *newbone = NULL; - int count; - bool mixed_object_error = false; - - /* loop over all bones, and only consider if visible */ - bArmature *arm = NULL; - CTX_DATA_BEGIN_WITH_ID(C, EditBone *, ebone, visible_bones, bArmature *, arm_iter) - { - bool check = false; - if (!(ebone->flag & BONE_CONNECTED) && (ebone->flag & BONE_ROOTSEL)) { - fill_add_joint(ebone, 0, &points); - check = true; - } - if (ebone->flag & BONE_TIPSEL) { - fill_add_joint(ebone, 1, &points); - check = true; - } - - if (check) { - if (arm && (arm != arm_iter)) { - mixed_object_error = true; - } - arm = arm_iter; - } - } - CTX_DATA_END; - - /* the number of joints determines how we fill: - * 1) between joint and cursor (joint=head, cursor=tail) - * 2) between the two joints (order is dependent on active-bone/hierarchy) - * 3+) error (a smarter method involving finding chains needs to be worked out - */ - count = BLI_listbase_count(&points); - - if (count == 0) { - BKE_report(op->reports, RPT_ERROR, "No joints selected"); - return OPERATOR_CANCELLED; - } - else if (mixed_object_error) { - BKE_report(op->reports, RPT_ERROR, "Bones for different objects selected"); - BLI_freelistN(&points); - return OPERATOR_CANCELLED; - } - - Object *obedit = NULL; - { - ViewLayer *view_layer = CTX_data_view_layer(C); - FOREACH_OBJECT_IN_EDIT_MODE_BEGIN (view_layer, v3d, ob_iter) { - if (ob_iter->data == arm) { - obedit = ob_iter; - } - } - FOREACH_OBJECT_IN_MODE_END; - } - BLI_assert(obedit != NULL); - - if (count == 1) { - EditBonePoint *ebp; - float curs[3]; - - /* Get Points - selected joint */ - ebp = points.first; - - /* Get points - cursor (tail) */ - invert_m4_m4(obedit->imat, obedit->obmat); - mul_v3_m4v3(curs, obedit->imat, scene->cursor.location); - - /* Create a bone */ - newbone = add_points_bone(obedit, ebp->vec, curs); - } - else if (count == 2) { - EditBonePoint *ebp_a, *ebp_b; - float head[3], tail[3]; - short headtail = 0; - - /* check that the points don't belong to the same bone */ - ebp_a = (EditBonePoint *)points.first; - ebp_b = ebp_a->next; - - if (((ebp_a->head_owner == ebp_b->tail_owner) && (ebp_a->head_owner != NULL)) || - ((ebp_a->tail_owner == ebp_b->head_owner) && (ebp_a->tail_owner != NULL))) - { - BKE_report(op->reports, RPT_ERROR, "Same bone selected..."); - BLI_freelistN(&points); - return OPERATOR_CANCELLED; - } - - /* find which one should be the 'head' */ - if ((ebp_a->head_owner && ebp_b->head_owner) || (ebp_a->tail_owner && ebp_b->tail_owner)) { - /* use active, nice predictable */ - if (arm->act_edbone && ELEM(arm->act_edbone, ebp_a->head_owner, ebp_a->tail_owner)) { - headtail = 1; - } - else if (arm->act_edbone && ELEM(arm->act_edbone, ebp_b->head_owner, ebp_b->tail_owner)) { - headtail = 2; - } - else { - /* rule: whichever one is closer to 3d-cursor */ - float curs[3]; - float dist_sq_a, dist_sq_b; - - /* get cursor location */ - invert_m4_m4(obedit->imat, obedit->obmat); - mul_v3_m4v3(curs, obedit->imat, scene->cursor.location); - - /* get distances */ - dist_sq_a = len_squared_v3v3(ebp_a->vec, curs); - dist_sq_b = len_squared_v3v3(ebp_b->vec, curs); - - /* compare distances - closer one therefore acts as direction for bone to go */ - headtail = (dist_sq_a < dist_sq_b) ? 2 : 1; - } - } - else if (ebp_a->head_owner) { - headtail = 1; - } - else if (ebp_b->head_owner) { - headtail = 2; - } - - /* assign head/tail combinations */ - if (headtail == 2) { - copy_v3_v3(head, ebp_a->vec); - copy_v3_v3(tail, ebp_b->vec); - } - else if (headtail == 1) { - copy_v3_v3(head, ebp_b->vec); - copy_v3_v3(tail, ebp_a->vec); - } - - /* add new bone and parent it to the appropriate end */ - if (headtail) { - newbone = add_points_bone(obedit, head, tail); - - /* do parenting (will need to set connected flag too) */ - if (headtail == 2) { - /* ebp tail or head - tail gets priority */ - if (ebp_a->tail_owner) - newbone->parent = ebp_a->tail_owner; - else - newbone->parent = ebp_a->head_owner; - } - else { - /* ebp_b tail or head - tail gets priority */ - if (ebp_b->tail_owner) - newbone->parent = ebp_b->tail_owner; - else - newbone->parent = ebp_b->head_owner; - } - - /* don't set for bone connecting two head points of bones */ - if (ebp_a->tail_owner || ebp_b->tail_owner) { - newbone->flag |= BONE_CONNECTED; - } - } - } - else { - BKE_reportf(op->reports, RPT_ERROR, "Too many points selected: %d", count); - BLI_freelistN(&points); - return OPERATOR_CANCELLED; - } - - if (newbone) { - ED_armature_edit_deselect_all(obedit); - arm->act_edbone = newbone; - newbone->flag |= BONE_TIPSEL; - } - - /* updates */ - WM_event_add_notifier(C, NC_OBJECT | ND_POSE, obedit); - - /* free points */ - BLI_freelistN(&points); - - return OPERATOR_FINISHED; + Scene *scene = CTX_data_scene(C); + View3D *v3d = CTX_wm_view3d(C); + ListBase points = {NULL, NULL}; + EditBone *newbone = NULL; + int count; + bool mixed_object_error = false; + + /* loop over all bones, and only consider if visible */ + bArmature *arm = NULL; + CTX_DATA_BEGIN_WITH_ID(C, EditBone *, ebone, visible_bones, bArmature *, arm_iter) + { + bool check = false; + if (!(ebone->flag & BONE_CONNECTED) && (ebone->flag & BONE_ROOTSEL)) { + fill_add_joint(ebone, 0, &points); + check = true; + } + if (ebone->flag & BONE_TIPSEL) { + fill_add_joint(ebone, 1, &points); + check = true; + } + + if (check) { + if (arm && (arm != arm_iter)) { + mixed_object_error = true; + } + arm = arm_iter; + } + } + CTX_DATA_END; + + /* the number of joints determines how we fill: + * 1) between joint and cursor (joint=head, cursor=tail) + * 2) between the two joints (order is dependent on active-bone/hierarchy) + * 3+) error (a smarter method involving finding chains needs to be worked out + */ + count = BLI_listbase_count(&points); + + if (count == 0) { + BKE_report(op->reports, RPT_ERROR, "No joints selected"); + return OPERATOR_CANCELLED; + } + else if (mixed_object_error) { + BKE_report(op->reports, RPT_ERROR, "Bones for different objects selected"); + BLI_freelistN(&points); + return OPERATOR_CANCELLED; + } + + Object *obedit = NULL; + { + ViewLayer *view_layer = CTX_data_view_layer(C); + FOREACH_OBJECT_IN_EDIT_MODE_BEGIN (view_layer, v3d, ob_iter) { + if (ob_iter->data == arm) { + obedit = ob_iter; + } + } + FOREACH_OBJECT_IN_MODE_END; + } + BLI_assert(obedit != NULL); + + if (count == 1) { + EditBonePoint *ebp; + float curs[3]; + + /* Get Points - selected joint */ + ebp = points.first; + + /* Get points - cursor (tail) */ + invert_m4_m4(obedit->imat, obedit->obmat); + mul_v3_m4v3(curs, obedit->imat, scene->cursor.location); + + /* Create a bone */ + newbone = add_points_bone(obedit, ebp->vec, curs); + } + else if (count == 2) { + EditBonePoint *ebp_a, *ebp_b; + float head[3], tail[3]; + short headtail = 0; + + /* check that the points don't belong to the same bone */ + ebp_a = (EditBonePoint *)points.first; + ebp_b = ebp_a->next; + + if (((ebp_a->head_owner == ebp_b->tail_owner) && (ebp_a->head_owner != NULL)) || + ((ebp_a->tail_owner == ebp_b->head_owner) && (ebp_a->tail_owner != NULL))) { + BKE_report(op->reports, RPT_ERROR, "Same bone selected..."); + BLI_freelistN(&points); + return OPERATOR_CANCELLED; + } + + /* find which one should be the 'head' */ + if ((ebp_a->head_owner && ebp_b->head_owner) || (ebp_a->tail_owner && ebp_b->tail_owner)) { + /* use active, nice predictable */ + if (arm->act_edbone && ELEM(arm->act_edbone, ebp_a->head_owner, ebp_a->tail_owner)) { + headtail = 1; + } + else if (arm->act_edbone && ELEM(arm->act_edbone, ebp_b->head_owner, ebp_b->tail_owner)) { + headtail = 2; + } + else { + /* rule: whichever one is closer to 3d-cursor */ + float curs[3]; + float dist_sq_a, dist_sq_b; + + /* get cursor location */ + invert_m4_m4(obedit->imat, obedit->obmat); + mul_v3_m4v3(curs, obedit->imat, scene->cursor.location); + + /* get distances */ + dist_sq_a = len_squared_v3v3(ebp_a->vec, curs); + dist_sq_b = len_squared_v3v3(ebp_b->vec, curs); + + /* compare distances - closer one therefore acts as direction for bone to go */ + headtail = (dist_sq_a < dist_sq_b) ? 2 : 1; + } + } + else if (ebp_a->head_owner) { + headtail = 1; + } + else if (ebp_b->head_owner) { + headtail = 2; + } + + /* assign head/tail combinations */ + if (headtail == 2) { + copy_v3_v3(head, ebp_a->vec); + copy_v3_v3(tail, ebp_b->vec); + } + else if (headtail == 1) { + copy_v3_v3(head, ebp_b->vec); + copy_v3_v3(tail, ebp_a->vec); + } + + /* add new bone and parent it to the appropriate end */ + if (headtail) { + newbone = add_points_bone(obedit, head, tail); + + /* do parenting (will need to set connected flag too) */ + if (headtail == 2) { + /* ebp tail or head - tail gets priority */ + if (ebp_a->tail_owner) + newbone->parent = ebp_a->tail_owner; + else + newbone->parent = ebp_a->head_owner; + } + else { + /* ebp_b tail or head - tail gets priority */ + if (ebp_b->tail_owner) + newbone->parent = ebp_b->tail_owner; + else + newbone->parent = ebp_b->head_owner; + } + + /* don't set for bone connecting two head points of bones */ + if (ebp_a->tail_owner || ebp_b->tail_owner) { + newbone->flag |= BONE_CONNECTED; + } + } + } + else { + BKE_reportf(op->reports, RPT_ERROR, "Too many points selected: %d", count); + BLI_freelistN(&points); + return OPERATOR_CANCELLED; + } + + if (newbone) { + ED_armature_edit_deselect_all(obedit); + arm->act_edbone = newbone; + newbone->flag |= BONE_TIPSEL; + } + + /* updates */ + WM_event_add_notifier(C, NC_OBJECT | ND_POSE, obedit); + + /* free points */ + BLI_freelistN(&points); + + return OPERATOR_FINISHED; } void ARMATURE_OT_fill(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Fill Between Joints"; - ot->idname = "ARMATURE_OT_fill"; - ot->description = "Add bone between selected joint(s) and/or 3D-Cursor"; + /* identifiers */ + ot->name = "Fill Between Joints"; + ot->idname = "ARMATURE_OT_fill"; + ot->description = "Add bone between selected joint(s) and/or 3D-Cursor"; - /* callbacks */ - ot->exec = armature_fill_bones_exec; - ot->poll = ED_operator_editarmature; + /* callbacks */ + ot->exec = armature_fill_bones_exec; + ot->poll = ED_operator_editarmature; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } /* --------------------- */ @@ -852,194 +878,193 @@ void ARMATURE_OT_fill(wmOperatorType *ot) /* 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) +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_SCALE | - BONE_NO_CYCLICOFFSET | BONE_NO_LOCAL_LOCATION | BONE_DONE); - - /* 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); + 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_SCALE | + BONE_NO_CYCLICOFFSET | BONE_NO_LOCAL_LOCATION | BONE_DONE); + + /* 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); - WM_event_add_notifier(C, NC_OBJECT | ND_POSE, obedit); - } - MEM_freeN(objects); - - return OPERATOR_FINISHED; + 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); + WM_event_add_notifier(C, NC_OBJECT | ND_POSE, obedit); + } + 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", ""); + 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", ""); } /* --------------------- */ @@ -1053,125 +1078,126 @@ void ARMATURE_OT_merge(wmOperatorType *ot) /* helper to clear BONE_TRANSFORM flags */ static void armature_clear_swap_done_flags(bArmature *arm) { - EditBone *ebone; + EditBone *ebone; - for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { - ebone->flag &= ~BONE_TRANSFORM; - } + for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { + ebone->flag &= ~BONE_TRANSFORM; + } } static int armature_switch_direction_exec(bContext *C, wmOperator *UNUSED(op)) { - 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; - - ListBase chains = {NULL, NULL}; - LinkData *chain; - - /* get chains of bones (ends on chains) */ - chains_find_tips(arm->edbo, &chains); - if (BLI_listbase_is_empty(&chains)) { - continue; - } - - /* ensure that mirror bones will also be operated on */ - armature_tag_select_mirrored(arm); - - /* clear BONE_TRANSFORM flags - * - used to prevent duplicate/canceling operations from occurring [#34123] - * - BONE_DONE cannot be used here as that's already used for mirroring - */ - armature_clear_swap_done_flags(arm); - - /* loop over chains, only considering selected and visible bones */ - for (chain = chains.first; chain; chain = chain->next) { - EditBone *ebo, *child = NULL, *parent = NULL; - - /* loop over bones in chain */ - for (ebo = chain->data; ebo; ebo = parent) { - /* parent is this bone's original parent - * - we store this, as the next bone that is checked is this one - * but the value of ebo->parent may change here... - */ - parent = ebo->parent; - - /* skip bone if already handled... [#34123] */ - if ((ebo->flag & BONE_TRANSFORM) == 0) { - /* only if selected and editable */ - if (EBONE_VISIBLE(arm, ebo) && EBONE_EDITABLE(ebo)) { - /* swap head and tail coordinates */ - swap_v3_v3(ebo->head, ebo->tail); - - /* do parent swapping: - * - use 'child' as new parent - * - connected flag is only set if points are coincidental - */ - ebo->parent = child; - if ((child) && equals_v3v3(ebo->head, child->tail)) - ebo->flag |= BONE_CONNECTED; - else - ebo->flag &= ~BONE_CONNECTED; - - /* get next bones - * - child will become the new parent of next bone - */ - child = ebo; - } - else { - /* not swapping this bone, however, if its 'parent' got swapped, unparent us from it - * as it will be facing in opposite direction - */ - if ((parent) && (EBONE_VISIBLE(arm, parent) && EBONE_EDITABLE(parent))) { - ebo->parent = NULL; - ebo->flag &= ~BONE_CONNECTED; - } - - /* get next bones - * - child will become new parent of next bone (not swapping occurred, - * so set to NULL to prevent infinite-loop) - */ - child = NULL; - } - - /* tag as done (to prevent double-swaps) */ - ebo->flag |= BONE_TRANSFORM; - } - } - } - - /* free chains */ - BLI_freelistN(&chains); - - /* clear temp flags */ - armature_clear_swap_done_flags(arm); - armature_tag_unselect(arm); - - /* note, notifier might evolve */ - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); - } - MEM_freeN(objects); - - return OPERATOR_FINISHED; + 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; + + ListBase chains = {NULL, NULL}; + LinkData *chain; + + /* get chains of bones (ends on chains) */ + chains_find_tips(arm->edbo, &chains); + if (BLI_listbase_is_empty(&chains)) { + continue; + } + + /* ensure that mirror bones will also be operated on */ + armature_tag_select_mirrored(arm); + + /* clear BONE_TRANSFORM flags + * - used to prevent duplicate/canceling operations from occurring [#34123] + * - BONE_DONE cannot be used here as that's already used for mirroring + */ + armature_clear_swap_done_flags(arm); + + /* loop over chains, only considering selected and visible bones */ + for (chain = chains.first; chain; chain = chain->next) { + EditBone *ebo, *child = NULL, *parent = NULL; + + /* loop over bones in chain */ + for (ebo = chain->data; ebo; ebo = parent) { + /* parent is this bone's original parent + * - we store this, as the next bone that is checked is this one + * but the value of ebo->parent may change here... + */ + parent = ebo->parent; + + /* skip bone if already handled... [#34123] */ + if ((ebo->flag & BONE_TRANSFORM) == 0) { + /* only if selected and editable */ + if (EBONE_VISIBLE(arm, ebo) && EBONE_EDITABLE(ebo)) { + /* swap head and tail coordinates */ + swap_v3_v3(ebo->head, ebo->tail); + + /* do parent swapping: + * - use 'child' as new parent + * - connected flag is only set if points are coincidental + */ + ebo->parent = child; + if ((child) && equals_v3v3(ebo->head, child->tail)) + ebo->flag |= BONE_CONNECTED; + else + ebo->flag &= ~BONE_CONNECTED; + + /* get next bones + * - child will become the new parent of next bone + */ + child = ebo; + } + else { + /* not swapping this bone, however, if its 'parent' got swapped, unparent us from it + * as it will be facing in opposite direction + */ + if ((parent) && (EBONE_VISIBLE(arm, parent) && EBONE_EDITABLE(parent))) { + ebo->parent = NULL; + ebo->flag &= ~BONE_CONNECTED; + } + + /* get next bones + * - child will become new parent of next bone (not swapping occurred, + * so set to NULL to prevent infinite-loop) + */ + child = NULL; + } + + /* tag as done (to prevent double-swaps) */ + ebo->flag |= BONE_TRANSFORM; + } + } + } + + /* free chains */ + BLI_freelistN(&chains); + + /* clear temp flags */ + armature_clear_swap_done_flags(arm); + armature_tag_unselect(arm); + + /* note, notifier might evolve */ + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); + } + MEM_freeN(objects); + + return OPERATOR_FINISHED; } void ARMATURE_OT_switch_direction(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Switch Direction"; - ot->idname = "ARMATURE_OT_switch_direction"; - ot->description = "Change the direction that a chain of bones points in (head <-> tail swap)"; + /* identifiers */ + ot->name = "Switch Direction"; + ot->idname = "ARMATURE_OT_switch_direction"; + ot->description = "Change the direction that a chain of bones points in (head <-> tail swap)"; - /* api callbacks */ - ot->exec = armature_switch_direction_exec; - ot->poll = ED_operator_editarmature; + /* api callbacks */ + ot->exec = armature_switch_direction_exec; + ot->poll = ED_operator_editarmature; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } /* ********************************* Align ******************************* */ @@ -1179,538 +1205,544 @@ void ARMATURE_OT_switch_direction(wmOperatorType *ot) /* helper to fix a ebone position if its parent has moved due to alignment*/ static void fix_connected_bone(EditBone *ebone) { - float diff[3]; + float diff[3]; - if (!(ebone->parent) || !(ebone->flag & BONE_CONNECTED) || equals_v3v3(ebone->parent->tail, ebone->head)) - return; + if (!(ebone->parent) || !(ebone->flag & BONE_CONNECTED) || + equals_v3v3(ebone->parent->tail, ebone->head)) + return; - /* if the parent has moved we translate child's head and tail accordingly */ - sub_v3_v3v3(diff, ebone->parent->tail, ebone->head); - add_v3_v3(ebone->head, diff); - add_v3_v3(ebone->tail, diff); + /* if the parent has moved we translate child's head and tail accordingly */ + sub_v3_v3v3(diff, ebone->parent->tail, ebone->head); + add_v3_v3(ebone->head, diff); + add_v3_v3(ebone->tail, diff); } /* helper to recursively find chains of connected bones starting at ebone and fix their position */ static void fix_editbone_connected_children(ListBase *edbo, EditBone *ebone) { - EditBone *selbone; - - for (selbone = edbo->first; selbone; selbone = selbone->next) { - if ((selbone->parent) && (selbone->parent == ebone) && (selbone->flag & BONE_CONNECTED)) { - fix_connected_bone(selbone); - fix_editbone_connected_children(edbo, selbone); - } - } + EditBone *selbone; + + for (selbone = edbo->first; selbone; selbone = selbone->next) { + if ((selbone->parent) && (selbone->parent == ebone) && (selbone->flag & BONE_CONNECTED)) { + fix_connected_bone(selbone); + fix_editbone_connected_children(edbo, selbone); + } + } } static void bone_align_to_bone(ListBase *edbo, EditBone *selbone, EditBone *actbone) { - float selboneaxis[3], actboneaxis[3], length; + float selboneaxis[3], actboneaxis[3], length; - sub_v3_v3v3(actboneaxis, actbone->tail, actbone->head); - normalize_v3(actboneaxis); + sub_v3_v3v3(actboneaxis, actbone->tail, actbone->head); + normalize_v3(actboneaxis); - sub_v3_v3v3(selboneaxis, selbone->tail, selbone->head); - length = len_v3(selboneaxis); + sub_v3_v3v3(selboneaxis, selbone->tail, selbone->head); + length = len_v3(selboneaxis); - mul_v3_fl(actboneaxis, length); - add_v3_v3v3(selbone->tail, selbone->head, actboneaxis); - selbone->roll = actbone->roll; + mul_v3_fl(actboneaxis, length); + add_v3_v3v3(selbone->tail, selbone->head, actboneaxis); + selbone->roll = actbone->roll; - /* if the bone being aligned has connected descendants they must be moved - * according to their parent new position, otherwise they would be left - * in an inconsistent state: connected but away from the parent*/ - fix_editbone_connected_children(edbo, selbone); + /* if the bone being aligned has connected descendants they must be moved + * according to their parent new position, otherwise they would be left + * in an inconsistent state: connected but away from the parent*/ + fix_editbone_connected_children(edbo, selbone); } static int armature_align_bones_exec(bContext *C, wmOperator *op) { - Object *ob = CTX_data_edit_object(C); - bArmature *arm = (bArmature *)ob->data; - EditBone *actbone = CTX_data_active_bone(C); - EditBone *actmirb = NULL; - int num_selected_bones; - - /* there must be an active bone */ - if (actbone == NULL) { - BKE_report(op->reports, RPT_ERROR, "Operation requires an active bone"); - return OPERATOR_CANCELLED; - } - else if (arm->flag & ARM_MIRROR_EDIT) { - /* For X-Axis Mirror Editing option, we may need a mirror copy of actbone - * - if there's a mirrored copy of selbone, try to find a mirrored copy of actbone - * (i.e. selbone="child.L" and actbone="parent.L", find "child.R" and "parent.R"). - * This is useful for arm-chains, for example parenting lower arm to upper arm - * - if there's no mirrored copy of actbone (i.e. actbone = "parent.C" or "parent") - * then just use actbone. Useful when doing upper arm to spine. - */ - actmirb = ED_armature_ebone_get_mirrored(arm->edbo, actbone); - if (actmirb == NULL) - actmirb = actbone; - } - - /* if there is only 1 selected bone, we assume that that is the active bone, - * since a user will need to have clicked on a bone (thus selecting it) to make it active - */ - num_selected_bones = CTX_DATA_COUNT(C, selected_editable_bones); - if (num_selected_bones <= 1) { - /* When only the active bone is selected, and it has a parent, - * align it to the parent, as that is the only possible outcome. - */ - if (actbone->parent) { - bone_align_to_bone(arm->edbo, actbone, actbone->parent); - - if ((arm->flag & ARM_MIRROR_EDIT) && (actmirb->parent)) - bone_align_to_bone(arm->edbo, actmirb, actmirb->parent); - - BKE_reportf(op->reports, RPT_INFO, "Aligned bone '%s' to parent", actbone->name); - } - } - else { - /* Align 'selected' bones to the active one - * - the context iterator contains both selected bones and their mirrored copies, - * so we assume that unselected bones are mirrored copies of some selected bone - * - since the active one (and/or its mirror) will also be selected, we also need - * to check that we are not trying to operate on them, since such an operation - * would cause errors - */ - - /* align selected bones to the active one */ - CTX_DATA_BEGIN(C, EditBone *, ebone, selected_editable_bones) - { - if (ELEM(ebone, actbone, actmirb) == 0) { - if (ebone->flag & BONE_SELECTED) - bone_align_to_bone(arm->edbo, ebone, actbone); - else - bone_align_to_bone(arm->edbo, ebone, actmirb); - } - } - CTX_DATA_END; - - BKE_reportf(op->reports, RPT_INFO, "%d bones aligned to bone '%s'", num_selected_bones, actbone->name); - } - - /* note, notifier might evolve */ - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); - - return OPERATOR_FINISHED; + Object *ob = CTX_data_edit_object(C); + bArmature *arm = (bArmature *)ob->data; + EditBone *actbone = CTX_data_active_bone(C); + EditBone *actmirb = NULL; + int num_selected_bones; + + /* there must be an active bone */ + if (actbone == NULL) { + BKE_report(op->reports, RPT_ERROR, "Operation requires an active bone"); + return OPERATOR_CANCELLED; + } + else if (arm->flag & ARM_MIRROR_EDIT) { + /* For X-Axis Mirror Editing option, we may need a mirror copy of actbone + * - if there's a mirrored copy of selbone, try to find a mirrored copy of actbone + * (i.e. selbone="child.L" and actbone="parent.L", find "child.R" and "parent.R"). + * This is useful for arm-chains, for example parenting lower arm to upper arm + * - if there's no mirrored copy of actbone (i.e. actbone = "parent.C" or "parent") + * then just use actbone. Useful when doing upper arm to spine. + */ + actmirb = ED_armature_ebone_get_mirrored(arm->edbo, actbone); + if (actmirb == NULL) + actmirb = actbone; + } + + /* if there is only 1 selected bone, we assume that that is the active bone, + * since a user will need to have clicked on a bone (thus selecting it) to make it active + */ + num_selected_bones = CTX_DATA_COUNT(C, selected_editable_bones); + if (num_selected_bones <= 1) { + /* When only the active bone is selected, and it has a parent, + * align it to the parent, as that is the only possible outcome. + */ + if (actbone->parent) { + bone_align_to_bone(arm->edbo, actbone, actbone->parent); + + if ((arm->flag & ARM_MIRROR_EDIT) && (actmirb->parent)) + bone_align_to_bone(arm->edbo, actmirb, actmirb->parent); + + BKE_reportf(op->reports, RPT_INFO, "Aligned bone '%s' to parent", actbone->name); + } + } + else { + /* Align 'selected' bones to the active one + * - the context iterator contains both selected bones and their mirrored copies, + * so we assume that unselected bones are mirrored copies of some selected bone + * - since the active one (and/or its mirror) will also be selected, we also need + * to check that we are not trying to operate on them, since such an operation + * would cause errors + */ + + /* align selected bones to the active one */ + CTX_DATA_BEGIN (C, EditBone *, ebone, selected_editable_bones) { + if (ELEM(ebone, actbone, actmirb) == 0) { + if (ebone->flag & BONE_SELECTED) + bone_align_to_bone(arm->edbo, ebone, actbone); + else + bone_align_to_bone(arm->edbo, ebone, actmirb); + } + } + CTX_DATA_END; + + BKE_reportf( + op->reports, RPT_INFO, "%d bones aligned to bone '%s'", num_selected_bones, actbone->name); + } + + /* note, notifier might evolve */ + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); + + return OPERATOR_FINISHED; } void ARMATURE_OT_align(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Align Bones"; - ot->idname = "ARMATURE_OT_align"; - ot->description = "Align selected bones to the active bone (or to their parent)"; + /* identifiers */ + ot->name = "Align Bones"; + ot->idname = "ARMATURE_OT_align"; + ot->description = "Align selected bones to the active bone (or to their parent)"; - /* api callbacks */ - ot->exec = armature_align_bones_exec; - ot->poll = ED_operator_editarmature; + /* api callbacks */ + ot->exec = armature_align_bones_exec; + ot->poll = ED_operator_editarmature; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } /* ********************************* Split ******************************* */ static int armature_split_exec(bContext *C, wmOperator *UNUSED(op)) { - 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; - - for (EditBone *bone = arm->edbo->first; bone; bone = bone->next) { - 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) { - ED_armature_ebone_select_set(bone, (bone->flag & BONE_SELECTED) != 0); - } - - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); - } - - MEM_freeN(objects); - return OPERATOR_FINISHED; + 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; + + for (EditBone *bone = arm->edbo->first; bone; bone = bone->next) { + 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) { + ED_armature_ebone_select_set(bone, (bone->flag & BONE_SELECTED) != 0); + } + + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); + } + + MEM_freeN(objects); + return OPERATOR_FINISHED; } void ARMATURE_OT_split(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Split"; - ot->idname = "ARMATURE_OT_split"; - ot->description = "Split off selected bones from connected unselected bones"; + /* identifiers */ + ot->name = "Split"; + ot->idname = "ARMATURE_OT_split"; + ot->description = "Split off selected bones from connected unselected bones"; - /* api callbacks */ - ot->exec = armature_split_exec; - ot->poll = ED_operator_editarmature; + /* api callbacks */ + ot->exec = armature_split_exec; + ot->poll = ED_operator_editarmature; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } /* ********************************* Delete ******************************* */ static bool armature_delete_ebone_cb(const char *bone_name, void *arm_p) { - bArmature *arm = arm_p; - EditBone *ebone; + bArmature *arm = arm_p; + EditBone *ebone; - ebone = ED_armature_ebone_find_name(arm->edbo, bone_name); - return (ebone && (ebone->flag & BONE_SELECTED) && (arm->layer & ebone->layer)); + ebone = ED_armature_ebone_find_name(arm->edbo, bone_name); + return (ebone && (ebone->flag & BONE_SELECTED) && (arm->layer & ebone->layer)); } /* previously delete_armature */ /* only editmode! */ static int armature_delete_selected_exec(bContext *C, wmOperator *UNUSED(op)) { - EditBone *curBone, *ebone_next; - bool changed_multi = false; - - /* cancel if nothing selected */ - if (CTX_DATA_COUNT(C, selected_bones) == 0) - return OPERATOR_CANCELLED; - - 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 *obedit = objects[ob_index]; - bArmature *arm = obedit->data; - bool changed = false; - - armature_select_mirrored(arm); - - BKE_pose_channels_remove(obedit, armature_delete_ebone_cb, arm); - - for (curBone = arm->edbo->first; curBone; curBone = ebone_next) { - ebone_next = curBone->next; - if (arm->layer & curBone->layer) { - if (curBone->flag & BONE_SELECTED) { - if (curBone == arm->act_edbone) arm->act_edbone = NULL; - ED_armature_ebone_remove(arm, curBone); - changed = true; - } - } - } - - if (changed) { - changed_multi = true; - - ED_armature_edit_sync_selection(arm->edbo); - BKE_pose_tag_recalc(CTX_data_main(C), obedit->pose); - - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit); - } - } - MEM_freeN(objects); - - if (!changed_multi) { - return OPERATOR_CANCELLED; - } - - return OPERATOR_FINISHED; + EditBone *curBone, *ebone_next; + bool changed_multi = false; + + /* cancel if nothing selected */ + if (CTX_DATA_COUNT(C, selected_bones) == 0) + return OPERATOR_CANCELLED; + + 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 *obedit = objects[ob_index]; + bArmature *arm = obedit->data; + bool changed = false; + + armature_select_mirrored(arm); + + BKE_pose_channels_remove(obedit, armature_delete_ebone_cb, arm); + + for (curBone = arm->edbo->first; curBone; curBone = ebone_next) { + ebone_next = curBone->next; + if (arm->layer & curBone->layer) { + if (curBone->flag & BONE_SELECTED) { + if (curBone == arm->act_edbone) + arm->act_edbone = NULL; + ED_armature_ebone_remove(arm, curBone); + changed = true; + } + } + } + + if (changed) { + changed_multi = true; + + ED_armature_edit_sync_selection(arm->edbo); + BKE_pose_tag_recalc(CTX_data_main(C), obedit->pose); + + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit); + } + } + MEM_freeN(objects); + + if (!changed_multi) { + return OPERATOR_CANCELLED; + } + + return OPERATOR_FINISHED; } void ARMATURE_OT_delete(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Delete Selected Bone(s)"; - ot->idname = "ARMATURE_OT_delete"; - ot->description = "Remove selected bones from the armature"; - - /* api callbacks */ - ot->invoke = WM_operator_confirm; - ot->exec = armature_delete_selected_exec; - ot->poll = ED_operator_editarmature; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* identifiers */ + ot->name = "Delete Selected Bone(s)"; + ot->idname = "ARMATURE_OT_delete"; + ot->description = "Remove selected bones from the armature"; + + /* api callbacks */ + ot->invoke = WM_operator_confirm; + ot->exec = armature_delete_selected_exec; + ot->poll = ED_operator_editarmature; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } static bool armature_dissolve_ebone_cb(const char *bone_name, void *arm_p) { - bArmature *arm = arm_p; - EditBone *ebone; + bArmature *arm = arm_p; + EditBone *ebone; - ebone = ED_armature_ebone_find_name(arm->edbo, bone_name); - return (ebone && (ebone->flag & BONE_DONE)); + ebone = ED_armature_ebone_find_name(arm->edbo, bone_name); + return (ebone && (ebone->flag & BONE_DONE)); } static int armature_dissolve_selected_exec(bContext *C, wmOperator *UNUSED(op)) { - ViewLayer *view_layer = CTX_data_view_layer(C); - EditBone *ebone, *ebone_next; - bool changed_multi = false; - - 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; - bool changed = false; - - /* store for mirror */ - GHash *ebone_flag_orig = NULL; - int ebone_num = 0; - - for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { - ebone->temp.p = NULL; - ebone->flag &= ~BONE_DONE; - ebone_num++; - } - - if (arm->flag & ARM_MIRROR_EDIT) { - GHashIterator gh_iter; - - ebone_flag_orig = BLI_ghash_ptr_new_ex(__func__, ebone_num); - for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { - union { int flag; void *p; } val = {0}; - val.flag = ebone->flag; - BLI_ghash_insert(ebone_flag_orig, ebone, val.p); - } - - armature_select_mirrored_ex(arm, BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL); - - GHASH_ITER (gh_iter, ebone_flag_orig) { - union { int flag; void *p; } *val_p = (void *)BLI_ghashIterator_getValue_p(&gh_iter); - ebone = BLI_ghashIterator_getKey(&gh_iter); - val_p->flag = ebone->flag & ~val_p->flag; - } - } - - for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { - if (ebone->parent && ebone->flag & BONE_CONNECTED) { - if (ebone->parent->temp.ebone == ebone->parent) { - /* ignore */ - } - else if (ebone->parent->temp.ebone) { - /* set ignored */ - ebone->parent->temp.ebone = ebone->parent; - } - else { - /* set child */ - ebone->parent->temp.ebone = ebone; - } - } - } - - /* cleanup multiple used bones */ - for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { - if (ebone->temp.ebone == ebone) { - ebone->temp.ebone = NULL; - } - } - - for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { - /* break connections for unseen bones */ - if (((arm->layer & ebone->layer) && - ((ED_armature_ebone_selectflag_get(ebone) & (BONE_TIPSEL | BONE_SELECTED)))) == 0) - { - ebone->temp.ebone = NULL; - } - - if (((arm->layer & ebone->layer) && - ((ED_armature_ebone_selectflag_get(ebone) & (BONE_ROOTSEL | BONE_SELECTED)))) == 0) - { - if (ebone->parent && (ebone->flag & BONE_CONNECTED)) { - ebone->parent->temp.ebone = NULL; - } - - } - } - - for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { - - if (ebone->parent && - (ebone->parent->temp.ebone == ebone)) - { - ebone->flag |= BONE_DONE; - } - } - - BKE_pose_channels_remove(obedit, armature_dissolve_ebone_cb, arm); - - for (ebone = arm->edbo->first; ebone; ebone = ebone_next) { - ebone_next = ebone->next; - - if (ebone->flag & BONE_DONE) { - copy_v3_v3(ebone->parent->tail, ebone->tail); - ebone->parent->rad_tail = ebone->rad_tail; - SET_FLAG_FROM_TEST(ebone->parent->flag, ebone->flag & BONE_TIPSEL, BONE_TIPSEL); - - ED_armature_ebone_remove_ex(arm, ebone, false); - changed = true; - } - } - - if (changed) { - for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { - if (ebone->parent && - ebone->parent->temp.ebone && - (ebone->flag & BONE_CONNECTED)) - { - ebone->rad_head = ebone->parent->rad_tail; - } - } - - if (arm->flag & ARM_MIRROR_EDIT) { - for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { - union { int flag; void *p; } *val_p = (void *)BLI_ghash_lookup_p(ebone_flag_orig, ebone); - if (val_p && val_p->flag) { - ebone->flag &= ~val_p->flag; - } - } - } - } - - if (arm->flag & ARM_MIRROR_EDIT) { - BLI_ghash_free(ebone_flag_orig, NULL, NULL); - } - - if (changed) { - changed_multi = true; - ED_armature_edit_sync_selection(arm->edbo); - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit); - } - } - MEM_freeN(objects); - - if (!changed_multi) { - return OPERATOR_CANCELLED; - } - - return OPERATOR_FINISHED; + ViewLayer *view_layer = CTX_data_view_layer(C); + EditBone *ebone, *ebone_next; + bool changed_multi = false; + + 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; + bool changed = false; + + /* store for mirror */ + GHash *ebone_flag_orig = NULL; + int ebone_num = 0; + + for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { + ebone->temp.p = NULL; + ebone->flag &= ~BONE_DONE; + ebone_num++; + } + + if (arm->flag & ARM_MIRROR_EDIT) { + GHashIterator gh_iter; + + ebone_flag_orig = BLI_ghash_ptr_new_ex(__func__, ebone_num); + for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { + union { + int flag; + void *p; + } val = {0}; + val.flag = ebone->flag; + BLI_ghash_insert(ebone_flag_orig, ebone, val.p); + } + + armature_select_mirrored_ex(arm, BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL); + + GHASH_ITER (gh_iter, ebone_flag_orig) { + union { + int flag; + void *p; + } *val_p = (void *)BLI_ghashIterator_getValue_p(&gh_iter); + ebone = BLI_ghashIterator_getKey(&gh_iter); + val_p->flag = ebone->flag & ~val_p->flag; + } + } + + for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { + if (ebone->parent && ebone->flag & BONE_CONNECTED) { + if (ebone->parent->temp.ebone == ebone->parent) { + /* ignore */ + } + else if (ebone->parent->temp.ebone) { + /* set ignored */ + ebone->parent->temp.ebone = ebone->parent; + } + else { + /* set child */ + ebone->parent->temp.ebone = ebone; + } + } + } + + /* cleanup multiple used bones */ + for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { + if (ebone->temp.ebone == ebone) { + ebone->temp.ebone = NULL; + } + } + + for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { + /* break connections for unseen bones */ + if (((arm->layer & ebone->layer) && + ((ED_armature_ebone_selectflag_get(ebone) & (BONE_TIPSEL | BONE_SELECTED)))) == 0) { + ebone->temp.ebone = NULL; + } + + if (((arm->layer & ebone->layer) && + ((ED_armature_ebone_selectflag_get(ebone) & (BONE_ROOTSEL | BONE_SELECTED)))) == 0) { + if (ebone->parent && (ebone->flag & BONE_CONNECTED)) { + ebone->parent->temp.ebone = NULL; + } + } + } + + for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { + + if (ebone->parent && (ebone->parent->temp.ebone == ebone)) { + ebone->flag |= BONE_DONE; + } + } + + BKE_pose_channels_remove(obedit, armature_dissolve_ebone_cb, arm); + + for (ebone = arm->edbo->first; ebone; ebone = ebone_next) { + ebone_next = ebone->next; + + if (ebone->flag & BONE_DONE) { + copy_v3_v3(ebone->parent->tail, ebone->tail); + ebone->parent->rad_tail = ebone->rad_tail; + SET_FLAG_FROM_TEST(ebone->parent->flag, ebone->flag & BONE_TIPSEL, BONE_TIPSEL); + + ED_armature_ebone_remove_ex(arm, ebone, false); + changed = true; + } + } + + if (changed) { + for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { + if (ebone->parent && ebone->parent->temp.ebone && (ebone->flag & BONE_CONNECTED)) { + ebone->rad_head = ebone->parent->rad_tail; + } + } + + if (arm->flag & ARM_MIRROR_EDIT) { + for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { + union { + int flag; + void *p; + } *val_p = (void *)BLI_ghash_lookup_p(ebone_flag_orig, ebone); + if (val_p && val_p->flag) { + ebone->flag &= ~val_p->flag; + } + } + } + } + + if (arm->flag & ARM_MIRROR_EDIT) { + BLI_ghash_free(ebone_flag_orig, NULL, NULL); + } + + if (changed) { + changed_multi = true; + ED_armature_edit_sync_selection(arm->edbo); + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit); + } + } + MEM_freeN(objects); + + if (!changed_multi) { + return OPERATOR_CANCELLED; + } + + return OPERATOR_FINISHED; } void ARMATURE_OT_dissolve(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Dissolve Selected Bone(s)"; - ot->idname = "ARMATURE_OT_dissolve"; - ot->description = "Dissolve selected bones from the armature"; + /* identifiers */ + ot->name = "Dissolve Selected Bone(s)"; + ot->idname = "ARMATURE_OT_dissolve"; + ot->description = "Dissolve selected bones from the armature"; - /* api callbacks */ - ot->exec = armature_dissolve_selected_exec; - ot->poll = ED_operator_editarmature; + /* api callbacks */ + ot->exec = armature_dissolve_selected_exec; + ot->poll = ED_operator_editarmature; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } - - /* ********************************* Show/Hide ******************************* */ static int armature_hide_exec(bContext *C, wmOperator *op) { - ViewLayer *view_layer = CTX_data_view_layer(C); - const int invert = RNA_boolean_get(op->ptr, "unselected") ? BONE_SELECTED : 0; - - /* cancel if nothing selected */ - if (CTX_DATA_COUNT(C, selected_bones) == 0) - return OPERATOR_CANCELLED; - - 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; - bool changed = false; - - for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { - if (EBONE_VISIBLE(arm, ebone)) { - if ((ebone->flag & BONE_SELECTED) != invert) { - ebone->flag &= ~(BONE_TIPSEL | BONE_SELECTED | BONE_ROOTSEL); - ebone->flag |= BONE_HIDDEN_A; - changed = true; - } - } - } - - if (!changed) { - continue; - } - ED_armature_edit_validate_active(arm); - ED_armature_edit_sync_selection(arm->edbo); - - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit); - } - MEM_freeN(objects); - return OPERATOR_FINISHED; + ViewLayer *view_layer = CTX_data_view_layer(C); + const int invert = RNA_boolean_get(op->ptr, "unselected") ? BONE_SELECTED : 0; + + /* cancel if nothing selected */ + if (CTX_DATA_COUNT(C, selected_bones) == 0) + return OPERATOR_CANCELLED; + + 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; + bool changed = false; + + for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { + if (EBONE_VISIBLE(arm, ebone)) { + if ((ebone->flag & BONE_SELECTED) != invert) { + ebone->flag &= ~(BONE_TIPSEL | BONE_SELECTED | BONE_ROOTSEL); + ebone->flag |= BONE_HIDDEN_A; + changed = true; + } + } + } + + if (!changed) { + continue; + } + ED_armature_edit_validate_active(arm); + ED_armature_edit_sync_selection(arm->edbo); + + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit); + } + MEM_freeN(objects); + return OPERATOR_FINISHED; } void ARMATURE_OT_hide(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Hide Selected"; - ot->idname = "ARMATURE_OT_hide"; - ot->description = "Tag selected bones to not be visible in Edit Mode"; + /* identifiers */ + ot->name = "Hide Selected"; + ot->idname = "ARMATURE_OT_hide"; + ot->description = "Tag selected bones to not be visible in Edit Mode"; - /* api callbacks */ - ot->exec = armature_hide_exec; - ot->poll = ED_operator_editarmature; + /* api callbacks */ + ot->exec = armature_hide_exec; + ot->poll = ED_operator_editarmature; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - /* props */ - RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected"); + /* props */ + RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected"); } static int armature_reveal_exec(bContext *C, wmOperator *op) { - ViewLayer *view_layer = CTX_data_view_layer(C); - const bool select = RNA_boolean_get(op->ptr, "select"); - 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; - bool changed = false; - - for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { - if (arm->layer & ebone->layer) { - if (ebone->flag & BONE_HIDDEN_A) { - if (!(ebone->flag & BONE_UNSELECTABLE)) { - SET_FLAG_FROM_TEST(ebone->flag, select, (BONE_TIPSEL | BONE_SELECTED | BONE_ROOTSEL)); - } - ebone->flag &= ~BONE_HIDDEN_A; - changed = true; - } - } - } - - if (changed) { - ED_armature_edit_validate_active(arm); - ED_armature_edit_sync_selection(arm->edbo); - - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit); - } - } - MEM_freeN(objects); - return OPERATOR_FINISHED; + ViewLayer *view_layer = CTX_data_view_layer(C); + const bool select = RNA_boolean_get(op->ptr, "select"); + 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; + bool changed = false; + + for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { + if (arm->layer & ebone->layer) { + if (ebone->flag & BONE_HIDDEN_A) { + if (!(ebone->flag & BONE_UNSELECTABLE)) { + SET_FLAG_FROM_TEST(ebone->flag, select, (BONE_TIPSEL | BONE_SELECTED | BONE_ROOTSEL)); + } + ebone->flag &= ~BONE_HIDDEN_A; + changed = true; + } + } + } + + if (changed) { + ED_armature_edit_validate_active(arm); + ED_armature_edit_sync_selection(arm->edbo); + + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit); + } + } + MEM_freeN(objects); + return OPERATOR_FINISHED; } void ARMATURE_OT_reveal(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Reveal Hidden"; - ot->idname = "ARMATURE_OT_reveal"; - ot->description = "Reveal all bones hidden in Edit Mode"; + /* identifiers */ + ot->name = "Reveal Hidden"; + ot->idname = "ARMATURE_OT_reveal"; + ot->description = "Reveal all bones hidden in Edit Mode"; - /* api callbacks */ - ot->exec = armature_reveal_exec; - ot->poll = ED_operator_editarmature; + /* api callbacks */ + ot->exec = armature_reveal_exec; + ot->poll = ED_operator_editarmature; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - RNA_def_boolean(ot->srna, "select", true, "Select", ""); + RNA_def_boolean(ot->srna, "select", true, "Select", ""); } diff --git a/source/blender/editors/armature/armature_intern.h b/source/blender/editors/armature/armature_intern.h index 7fe4810762e..09e4c1acae7 100644 --- a/source/blender/editors/armature/armature_intern.h +++ b/source/blender/editors/armature/armature_intern.h @@ -138,37 +138,37 @@ void POSE_OT_bone_layers(struct wmOperatorType *ot); /* Temporary data linking PoseChannels with the F-Curves they affect */ typedef struct tPChanFCurveLink { - struct tPChanFCurveLink *next, *prev; - - /** Object this Pose Channel belongs to. */ - struct Object *ob; - - /** F-Curves for this PoseChannel (wrapped with LinkData) */ - ListBase fcurves; - /** Pose Channel which data is attached to */ - struct bPoseChannel *pchan; - - /** RNA Path to this Pose Channel (needs to be freed when we're done) */ - char *pchan_path; - - /** transform values at start of operator (to be restored before each modal step) */ - float oldloc[3]; - float oldrot[3]; - float oldscale[3]; - float oldquat[4]; - float oldangle; - float oldaxis[3]; - - /** old bbone values (to be restored along with the transform properties) */ - float roll1, roll2; - /** (NOTE: we haven't renamed these this time, as their names are already long enough) */ - float curveInX, curveInY; - float curveOutX, curveOutY; - float ease1, ease2; - float scaleIn, scaleOut; - - /** copy of custom properties at start of operator (to be restored before each modal step) */ - struct IDProperty *oldprops; + struct tPChanFCurveLink *next, *prev; + + /** Object this Pose Channel belongs to. */ + struct Object *ob; + + /** F-Curves for this PoseChannel (wrapped with LinkData) */ + ListBase fcurves; + /** Pose Channel which data is attached to */ + struct bPoseChannel *pchan; + + /** RNA Path to this Pose Channel (needs to be freed when we're done) */ + char *pchan_path; + + /** transform values at start of operator (to be restored before each modal step) */ + float oldloc[3]; + float oldrot[3]; + float oldscale[3]; + float oldquat[4]; + float oldangle; + float oldaxis[3]; + + /** old bbone values (to be restored along with the transform properties) */ + float roll1, roll2; + /** (NOTE: we haven't renamed these this time, as their names are already long enough) */ + float curveInX, curveInY; + float curveOutX, curveOutY; + float ease1, ease2; + float scaleIn, scaleOut; + + /** copy of custom properties at start of operator (to be restored before each modal step) */ + struct IDProperty *oldprops; } tPChanFCurveLink; /* ----------- */ @@ -179,7 +179,10 @@ void poseAnim_mapping_free(ListBase *pfLinks); void poseAnim_mapping_refresh(struct bContext *C, struct Scene *scene, struct Object *ob); void poseAnim_mapping_reset(ListBase *pfLinks); -void poseAnim_mapping_autoKeyframe(struct bContext *C, struct Scene *scene, ListBase *pfLinks, float cframe); +void poseAnim_mapping_autoKeyframe(struct bContext *C, + struct Scene *scene, + ListBase *pfLinks, + float cframe); LinkData *poseAnim_mapping_getNextFCurve(ListBase *fcuLinks, LinkData *prev, const char *path); @@ -222,15 +225,27 @@ EditBone *make_boneList(struct ListBase *edbo, struct ListBase *bones, struct Bo /* duplicate method */ void preEditBoneDuplicate(struct ListBase *editbones); void postEditBoneDuplicate(struct ListBase *editbones, struct Object *ob); -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); +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 */ -struct EditBone *duplicateEditBoneObjects(struct EditBone *curBone, const char *name, struct ListBase *editbones, struct Object *src_ob, struct Object *dst_ob); +struct EditBone *duplicateEditBoneObjects(struct EditBone *curBone, + const char *name, + struct ListBase *editbones, + 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); +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); @@ -240,18 +255,20 @@ 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_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, - bool findunsel, bool do_nearest, - struct Base **r_base); +void *get_bone_from_selectbuffer(struct Base **bases, + uint bases_len, + bool is_editmode, + const unsigned int *buffer, + short hits, + bool findunsel, + bool do_nearest, + struct Base **r_base); -int bone_looper(struct Object *ob, struct Bone *bone, void *data, +int bone_looper(struct Object *ob, + struct Bone *bone, + void *data, int (*bone_func)(struct Object *, struct Bone *, void *)); - #endif /* __ARMATURE_INTERN_H__ */ diff --git a/source/blender/editors/armature/armature_naming.c b/source/blender/editors/armature/armature_naming.c index 6d4babe9674..b1f0297bb29 100644 --- a/source/blender/editors/armature/armature_naming.c +++ b/source/blender/editors/armature/armature_naming.c @@ -71,18 +71,24 @@ /* note: there's a unique_bone_name() too! */ static bool editbone_unique_check(void *arg, const char *name) { - struct {ListBase *lb; void *bone; } *data = arg; - EditBone *dupli = ED_armature_ebone_find_name(data->lb, name); - return dupli && dupli != data->bone; + struct { + ListBase *lb; + void *bone; + } *data = arg; + EditBone *dupli = ED_armature_ebone_find_name(data->lb, name); + return dupli && dupli != data->bone; } void ED_armature_ebone_unique_name(ListBase *edbo, char *name, EditBone *bone) { - struct {ListBase *lb; void *bone; } data; - data.lb = edbo; - data.bone = bone; - - BLI_uniquename_cb(editbone_unique_check, &data, DATA_("Bone"), '.', name, sizeof(bone->name)); + struct { + ListBase *lb; + void *bone; + } data; + data.lb = edbo; + data.bone = bone; + + BLI_uniquename_cb(editbone_unique_check, &data, DATA_("Bone"), '.', name, sizeof(bone->name)); } /* ************************************************** */ @@ -90,261 +96,265 @@ void ED_armature_ebone_unique_name(ListBase *edbo, char *name, EditBone *bone) static bool bone_unique_check(void *arg, const char *name) { - return BKE_armature_find_bone_name((bArmature *)arg, name) != NULL; + return BKE_armature_find_bone_name((bArmature *)arg, name) != NULL; } static void unique_bone_name(bArmature *arm, char *name) { - BLI_uniquename_cb(bone_unique_check, (void *)arm, DATA_("Bone"), '.', name, sizeof(((Bone *)NULL)->name)); + BLI_uniquename_cb( + bone_unique_check, (void *)arm, DATA_("Bone"), '.', name, sizeof(((Bone *)NULL)->name)); } /* helper call for armature_bone_rename */ -static void constraint_bone_name_fix(Object *ob, ListBase *conlist, const char *oldname, const char *newname) +static void constraint_bone_name_fix(Object *ob, + ListBase *conlist, + const char *oldname, + const char *newname) { - bConstraint *curcon; - bConstraintTarget *ct; - - for (curcon = conlist->first; curcon; curcon = curcon->next) { - const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(curcon); - ListBase targets = {NULL, NULL}; - - /* constraint targets */ - if (cti && cti->get_constraint_targets) { - cti->get_constraint_targets(curcon, &targets); - - for (ct = targets.first; ct; ct = ct->next) { - if (ct->tar == ob) { - if (STREQ(ct->subtarget, oldname)) { - BLI_strncpy(ct->subtarget, newname, MAXBONENAME); - } - } - } - - if (cti->flush_constraint_targets) - cti->flush_constraint_targets(curcon, &targets, 0); - } - - /* action constraints */ - if (curcon->type == CONSTRAINT_TYPE_ACTION) { - bActionConstraint *actcon = (bActionConstraint *)curcon->data; - BKE_action_fix_paths_rename(&ob->id, actcon->act, "pose.bones", oldname, newname, 0, 0, 1); - } - } + bConstraint *curcon; + bConstraintTarget *ct; + + for (curcon = conlist->first; curcon; curcon = curcon->next) { + const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(curcon); + ListBase targets = {NULL, NULL}; + + /* constraint targets */ + if (cti && cti->get_constraint_targets) { + cti->get_constraint_targets(curcon, &targets); + + for (ct = targets.first; ct; ct = ct->next) { + if (ct->tar == ob) { + if (STREQ(ct->subtarget, oldname)) { + BLI_strncpy(ct->subtarget, newname, MAXBONENAME); + } + } + } + + if (cti->flush_constraint_targets) + cti->flush_constraint_targets(curcon, &targets, 0); + } + + /* action constraints */ + if (curcon->type == CONSTRAINT_TYPE_ACTION) { + bActionConstraint *actcon = (bActionConstraint *)curcon->data; + BKE_action_fix_paths_rename(&ob->id, actcon->act, "pose.bones", oldname, newname, 0, 0, 1); + } + } } /* called by UI for renaming a bone */ /* warning: make sure the original bone was not renamed yet! */ /* seems messy, but that's what you get with not using pointers but channel names :) */ -void ED_armature_bone_rename(Main *bmain, bArmature *arm, const char *oldnamep, const char *newnamep) +void ED_armature_bone_rename(Main *bmain, + bArmature *arm, + const char *oldnamep, + const char *newnamep) { - Object *ob; - char newname[MAXBONENAME]; - char oldname[MAXBONENAME]; - - /* names better differ! */ - if (!STREQLEN(oldnamep, newnamep, MAXBONENAME)) { - - /* we alter newname string... so make copy */ - BLI_strncpy(newname, newnamep, MAXBONENAME); - /* we use oldname for search... so make copy */ - BLI_strncpy(oldname, oldnamep, MAXBONENAME); - - /* now check if we're in editmode, we need to find the unique name */ - if (arm->edbo) { - EditBone *eBone = ED_armature_ebone_find_name(arm->edbo, oldname); - - if (eBone) { - ED_armature_ebone_unique_name(arm->edbo, newname, NULL); - BLI_strncpy(eBone->name, newname, MAXBONENAME); - } - else { - return; - } - } - else { - Bone *bone = BKE_armature_find_bone_name(arm, oldname); - - if (bone) { - unique_bone_name(arm, newname); - BLI_strncpy(bone->name, newname, MAXBONENAME); - } - else { - return; - } - } - - /* force copy on write to update database */ - DEG_id_tag_update(&arm->id, ID_RECALC_COPY_ON_WRITE); - - /* do entire dbase - objects */ - for (ob = bmain->objects.first; ob; ob = ob->id.next) { - ModifierData *md; - - /* we have the object using the armature */ - if (arm == ob->data) { - Object *cob; - - /* Rename the pose channel, if it exists */ - if (ob->pose) { - bPoseChannel *pchan = BKE_pose_channel_find_name(ob->pose, oldname); - if (pchan) { - GHash *gh = ob->pose->chanhash; - - /* remove the old hash entry, and replace with the new name */ - if (gh) { - BLI_assert(BLI_ghash_haskey(gh, pchan->name)); - BLI_ghash_remove(gh, pchan->name, NULL, NULL); - } - - BLI_strncpy(pchan->name, newname, MAXBONENAME); - - if (gh) { - BLI_ghash_insert(gh, pchan->name, pchan); - } - } - - BLI_assert(BKE_pose_channels_is_valid(ob->pose) == true); - } - - /* Update any object constraints to use the new bone name */ - for (cob = bmain->objects.first; cob; cob = cob->id.next) { - if (cob->constraints.first) - constraint_bone_name_fix(ob, &cob->constraints, oldname, newname); - if (cob->pose) { - bPoseChannel *pchan; - for (pchan = cob->pose->chanbase.first; pchan; pchan = pchan->next) { - constraint_bone_name_fix(ob, &pchan->constraints, oldname, newname); - } - } - } - } - - /* See if an object is parented to this armature */ - if (ob->parent && (ob->parent->data == arm)) { - if (ob->partype == PARBONE) { - /* bone name in object */ - if (STREQ(ob->parsubstr, oldname)) - BLI_strncpy(ob->parsubstr, newname, MAXBONENAME); - } - } - - if (modifiers_usesArmature(ob, arm)) { - bDeformGroup *dg = defgroup_find_name(ob, oldname); - if (dg) { - BLI_strncpy(dg->name, newname, MAXBONENAME); - } - } - - /* fix modifiers that might be using this name */ - for (md = ob->modifiers.first; md; md = md->next) { - switch (md->type) { - case eModifierType_Hook: - { - HookModifierData *hmd = (HookModifierData *)md; - - if (hmd->object && (hmd->object->data == arm)) { - if (STREQ(hmd->subtarget, oldname)) - BLI_strncpy(hmd->subtarget, newname, MAXBONENAME); - } - break; - } - case eModifierType_UVWarp: - { - UVWarpModifierData *umd = (UVWarpModifierData *)md; - - if (umd->object_src && (umd->object_src->data == arm)) { - if (STREQ(umd->bone_src, oldname)) - BLI_strncpy(umd->bone_src, newname, MAXBONENAME); - } - if (umd->object_dst && (umd->object_dst->data == arm)) { - if (STREQ(umd->bone_dst, oldname)) - BLI_strncpy(umd->bone_dst, newname, MAXBONENAME); - } - break; - } - default: - break; - } - } - - /* fix grease pencil modifiers and vertex groups */ - if (ob->type == OB_GPENCIL) { - - bGPdata *gpd = (bGPdata *)ob->data; - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { - if ((gpl->parent != NULL) && (gpl->parent->data == arm)) { - if (STREQ(gpl->parsubstr, oldname)) - BLI_strncpy(gpl->parsubstr, newname, MAXBONENAME); - } - } - - for (GpencilModifierData *gp_md = ob->greasepencil_modifiers.first; gp_md; gp_md = gp_md->next) { - switch (gp_md->type) { - case eGpencilModifierType_Armature: - { - ArmatureGpencilModifierData *mmd = (ArmatureGpencilModifierData *)gp_md; - if (mmd->object && mmd->object->data == arm) { - bDeformGroup *dg = defgroup_find_name(ob, oldname); - if (dg) { - BLI_strncpy(dg->name, newname, MAXBONENAME); - } - } - break; - } - case eGpencilModifierType_Hook: - { - HookGpencilModifierData *hgp_md = (HookGpencilModifierData *)gp_md; - if (hgp_md->object && (hgp_md->object->data == arm)) { - if (STREQ(hgp_md->subtarget, oldname)) - BLI_strncpy(hgp_md->subtarget, newname, MAXBONENAME); - } - break; - } - default: - break; - } - } - } - DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE); - } - - /* Fix all animdata that may refer to this bone - we can't just do the ones attached to objects, since - * other ID-blocks may have drivers referring to this bone [#29822] - */ - // XXX: the ID here is for armatures, but most bone drivers are actually on the object instead... - { - - BKE_animdata_fix_paths_rename_all(&arm->id, "pose.bones", oldname, newname); - } - - /* correct view locking */ - { - bScreen *screen; - for (screen = bmain->screens.first; screen; screen = screen->id.next) { - ScrArea *sa; - /* add regions */ - for (sa = screen->areabase.first; sa; sa = sa->next) { - SpaceLink *sl; - for (sl = sa->spacedata.first; sl; sl = sl->next) { - if (sl->spacetype == SPACE_VIEW3D) { - View3D *v3d = (View3D *)sl; - if (v3d->ob_centre && v3d->ob_centre->data == arm) { - if (STREQ(v3d->ob_centre_bone, oldname)) { - BLI_strncpy(v3d->ob_centre_bone, newname, MAXBONENAME); - } - } - } - } - } - } - } - } + Object *ob; + char newname[MAXBONENAME]; + char oldname[MAXBONENAME]; + + /* names better differ! */ + if (!STREQLEN(oldnamep, newnamep, MAXBONENAME)) { + + /* we alter newname string... so make copy */ + BLI_strncpy(newname, newnamep, MAXBONENAME); + /* we use oldname for search... so make copy */ + BLI_strncpy(oldname, oldnamep, MAXBONENAME); + + /* now check if we're in editmode, we need to find the unique name */ + if (arm->edbo) { + EditBone *eBone = ED_armature_ebone_find_name(arm->edbo, oldname); + + if (eBone) { + ED_armature_ebone_unique_name(arm->edbo, newname, NULL); + BLI_strncpy(eBone->name, newname, MAXBONENAME); + } + else { + return; + } + } + else { + Bone *bone = BKE_armature_find_bone_name(arm, oldname); + + if (bone) { + unique_bone_name(arm, newname); + BLI_strncpy(bone->name, newname, MAXBONENAME); + } + else { + return; + } + } + + /* force copy on write to update database */ + DEG_id_tag_update(&arm->id, ID_RECALC_COPY_ON_WRITE); + + /* do entire dbase - objects */ + for (ob = bmain->objects.first; ob; ob = ob->id.next) { + ModifierData *md; + + /* we have the object using the armature */ + if (arm == ob->data) { + Object *cob; + + /* Rename the pose channel, if it exists */ + if (ob->pose) { + bPoseChannel *pchan = BKE_pose_channel_find_name(ob->pose, oldname); + if (pchan) { + GHash *gh = ob->pose->chanhash; + + /* remove the old hash entry, and replace with the new name */ + if (gh) { + BLI_assert(BLI_ghash_haskey(gh, pchan->name)); + BLI_ghash_remove(gh, pchan->name, NULL, NULL); + } + + BLI_strncpy(pchan->name, newname, MAXBONENAME); + + if (gh) { + BLI_ghash_insert(gh, pchan->name, pchan); + } + } + + BLI_assert(BKE_pose_channels_is_valid(ob->pose) == true); + } + + /* Update any object constraints to use the new bone name */ + for (cob = bmain->objects.first; cob; cob = cob->id.next) { + if (cob->constraints.first) + constraint_bone_name_fix(ob, &cob->constraints, oldname, newname); + if (cob->pose) { + bPoseChannel *pchan; + for (pchan = cob->pose->chanbase.first; pchan; pchan = pchan->next) { + constraint_bone_name_fix(ob, &pchan->constraints, oldname, newname); + } + } + } + } + + /* See if an object is parented to this armature */ + if (ob->parent && (ob->parent->data == arm)) { + if (ob->partype == PARBONE) { + /* bone name in object */ + if (STREQ(ob->parsubstr, oldname)) + BLI_strncpy(ob->parsubstr, newname, MAXBONENAME); + } + } + + if (modifiers_usesArmature(ob, arm)) { + bDeformGroup *dg = defgroup_find_name(ob, oldname); + if (dg) { + BLI_strncpy(dg->name, newname, MAXBONENAME); + } + } + + /* fix modifiers that might be using this name */ + for (md = ob->modifiers.first; md; md = md->next) { + switch (md->type) { + case eModifierType_Hook: { + HookModifierData *hmd = (HookModifierData *)md; + + if (hmd->object && (hmd->object->data == arm)) { + if (STREQ(hmd->subtarget, oldname)) + BLI_strncpy(hmd->subtarget, newname, MAXBONENAME); + } + break; + } + case eModifierType_UVWarp: { + UVWarpModifierData *umd = (UVWarpModifierData *)md; + + if (umd->object_src && (umd->object_src->data == arm)) { + if (STREQ(umd->bone_src, oldname)) + BLI_strncpy(umd->bone_src, newname, MAXBONENAME); + } + if (umd->object_dst && (umd->object_dst->data == arm)) { + if (STREQ(umd->bone_dst, oldname)) + BLI_strncpy(umd->bone_dst, newname, MAXBONENAME); + } + break; + } + default: + break; + } + } + + /* fix grease pencil modifiers and vertex groups */ + if (ob->type == OB_GPENCIL) { + + bGPdata *gpd = (bGPdata *)ob->data; + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + if ((gpl->parent != NULL) && (gpl->parent->data == arm)) { + if (STREQ(gpl->parsubstr, oldname)) + BLI_strncpy(gpl->parsubstr, newname, MAXBONENAME); + } + } + + for (GpencilModifierData *gp_md = ob->greasepencil_modifiers.first; gp_md; + gp_md = gp_md->next) { + switch (gp_md->type) { + case eGpencilModifierType_Armature: { + ArmatureGpencilModifierData *mmd = (ArmatureGpencilModifierData *)gp_md; + if (mmd->object && mmd->object->data == arm) { + bDeformGroup *dg = defgroup_find_name(ob, oldname); + if (dg) { + BLI_strncpy(dg->name, newname, MAXBONENAME); + } + } + break; + } + case eGpencilModifierType_Hook: { + HookGpencilModifierData *hgp_md = (HookGpencilModifierData *)gp_md; + if (hgp_md->object && (hgp_md->object->data == arm)) { + if (STREQ(hgp_md->subtarget, oldname)) + BLI_strncpy(hgp_md->subtarget, newname, MAXBONENAME); + } + break; + } + default: + break; + } + } + } + DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE); + } + + /* Fix all animdata that may refer to this bone - we can't just do the ones attached to objects, since + * other ID-blocks may have drivers referring to this bone [#29822] + */ + // XXX: the ID here is for armatures, but most bone drivers are actually on the object instead... + { + + BKE_animdata_fix_paths_rename_all(&arm->id, "pose.bones", oldname, newname); + } + + /* correct view locking */ + { + bScreen *screen; + for (screen = bmain->screens.first; screen; screen = screen->id.next) { + ScrArea *sa; + /* add regions */ + for (sa = screen->areabase.first; sa; sa = sa->next) { + SpaceLink *sl; + for (sl = sa->spacedata.first; sl; sl = sl->next) { + if (sl->spacetype == SPACE_VIEW3D) { + View3D *v3d = (View3D *)sl; + if (v3d->ob_centre && v3d->ob_centre->data == arm) { + if (STREQ(v3d->ob_centre_bone, oldname)) { + BLI_strncpy(v3d->ob_centre_bone, newname, MAXBONENAME); + } + } + } + } + } + } + } + } } typedef struct BoneFlipNameData { - struct BoneFlipNameData *next, *prev; - char *name; - char name_flip[MAXBONENAME]; + struct BoneFlipNameData *next, *prev; + char *name; + char name_flip[MAXBONENAME]; } BoneFlipNameData; /** @@ -357,38 +367,41 @@ typedef struct BoneFlipNameData { * \param bones_names: List of BoneConflict elems. * \param do_strip_numbers: if set, try to get rid of dot-numbers at end of bone names. */ -void ED_armature_bones_flip_names(Main *bmain, bArmature *arm, ListBase *bones_names, const bool do_strip_numbers) +void ED_armature_bones_flip_names(Main *bmain, + bArmature *arm, + ListBase *bones_names, + const bool do_strip_numbers) { - ListBase bones_names_conflicts = {NULL}; - BoneFlipNameData *bfn; - - /* 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) { - char name_flip[MAXBONENAME]; - char *name = link->data; - - /* WARNING: if do_strip_numbers is set, expect completely mismatched names in cases like - * Bone.R, Bone.R.001, Bone.R.002, etc. */ - BLI_string_flip_side_name(name_flip, name, do_strip_numbers, sizeof(name_flip)); - - ED_armature_bone_rename(bmain, arm, name, name_flip); - - if (!STREQ(name, name_flip)) { - bfn = alloca(sizeof(BoneFlipNameData)); - bfn->name = name; - BLI_strncpy(bfn->name_flip, name_flip, sizeof(bfn->name_flip)); - BLI_addtail(&bones_names_conflicts, bfn); - } - } - - /* Second pass to handle the bones that have naming conflicts with other bones. - * Note that if the other bone was not selected, its name was not flipped, so conflict remains and that second - * rename simply generates a new numbered alternative name. */ - for (bfn = bones_names_conflicts.first; bfn; bfn = bfn->next) { - ED_armature_bone_rename(bmain, arm, bfn->name, bfn->name_flip); - } + ListBase bones_names_conflicts = {NULL}; + BoneFlipNameData *bfn; + + /* 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) { + char name_flip[MAXBONENAME]; + char *name = link->data; + + /* WARNING: if do_strip_numbers is set, expect completely mismatched names in cases like + * Bone.R, Bone.R.001, Bone.R.002, etc. */ + BLI_string_flip_side_name(name_flip, name, do_strip_numbers, sizeof(name_flip)); + + ED_armature_bone_rename(bmain, arm, name, name_flip); + + if (!STREQ(name, name_flip)) { + bfn = alloca(sizeof(BoneFlipNameData)); + bfn->name = name; + BLI_strncpy(bfn->name_flip, name_flip, sizeof(bfn->name_flip)); + BLI_addtail(&bones_names_conflicts, bfn); + } + } + + /* Second pass to handle the bones that have naming conflicts with other bones. + * Note that if the other bone was not selected, its name was not flipped, so conflict remains and that second + * rename simply generates a new numbered alternative name. */ + for (bfn = bones_names_conflicts.first; bfn; bfn = bfn->next) { + ED_armature_bone_rename(bmain, arm, bfn->name, bfn->name_flip); + } } /* ************************************************** */ @@ -396,165 +409,171 @@ void ED_armature_bones_flip_names(Main *bmain, bArmature *arm, ListBase *bones_n static int armature_flip_names_exec(bContext *C, wmOperator *op) { - Main *bmain = CTX_data_main(C); - ViewLayer *view_layer = CTX_data_view_layer(C); - Object *ob_active = CTX_data_edit_object(C); - - const bool do_strip_numbers = RNA_boolean_get(op->ptr, "do_strip_numbers"); - - 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; - - /* Paranoia check. */ - if (ob_active->pose == NULL) { - continue; - } - - ListBase bones_names = {NULL}; - - for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { - if (EBONE_VISIBLE(arm, ebone)) { - if (ebone->flag & BONE_SELECTED) { - BLI_addtail(&bones_names, BLI_genericNodeN(ebone->name)); - - if (arm->flag & ARM_MIRROR_EDIT) { - EditBone *flipbone = ED_armature_ebone_get_mirrored(arm->edbo, ebone); - if ((flipbone) && !(flipbone->flag & BONE_SELECTED)) { - BLI_addtail(&bones_names, BLI_genericNodeN(flipbone->name)); - } - } - } - } - } - - if (BLI_listbase_is_empty(&bones_names)) { - continue; - } - - ED_armature_bones_flip_names(bmain, arm, &bones_names, do_strip_numbers); - - BLI_freelistN(&bones_names); - - /* since we renamed stuff... */ - DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); - - /* copied from #rna_Bone_update_renamed */ - /* redraw view */ - WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data); - - /* update animation channels */ - WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN, ob->data); - - } - MEM_freeN(objects); - - return OPERATOR_FINISHED; + Main *bmain = CTX_data_main(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + Object *ob_active = CTX_data_edit_object(C); + + const bool do_strip_numbers = RNA_boolean_get(op->ptr, "do_strip_numbers"); + + 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; + + /* Paranoia check. */ + if (ob_active->pose == NULL) { + continue; + } + + ListBase bones_names = {NULL}; + + for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { + if (EBONE_VISIBLE(arm, ebone)) { + if (ebone->flag & BONE_SELECTED) { + BLI_addtail(&bones_names, BLI_genericNodeN(ebone->name)); + + if (arm->flag & ARM_MIRROR_EDIT) { + EditBone *flipbone = ED_armature_ebone_get_mirrored(arm->edbo, ebone); + if ((flipbone) && !(flipbone->flag & BONE_SELECTED)) { + BLI_addtail(&bones_names, BLI_genericNodeN(flipbone->name)); + } + } + } + } + } + + if (BLI_listbase_is_empty(&bones_names)) { + continue; + } + + ED_armature_bones_flip_names(bmain, arm, &bones_names, do_strip_numbers); + + BLI_freelistN(&bones_names); + + /* since we renamed stuff... */ + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); + + /* copied from #rna_Bone_update_renamed */ + /* redraw view */ + WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data); + + /* update animation channels */ + WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN, ob->data); + } + MEM_freeN(objects); + + return OPERATOR_FINISHED; } void ARMATURE_OT_flip_names(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Flip Names"; - ot->idname = "ARMATURE_OT_flip_names"; - ot->description = "Flips (and corrects) the axis suffixes of the names of selected bones"; - - /* api callbacks */ - ot->exec = armature_flip_names_exec; - ot->poll = ED_operator_editarmature; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - RNA_def_boolean(ot->srna, "do_strip_numbers", false, "Strip Numbers", - "Try to remove right-most dot-number from flipped names " - "(WARNING: may result in incoherent naming in some cases)"); + /* identifiers */ + ot->name = "Flip Names"; + ot->idname = "ARMATURE_OT_flip_names"; + ot->description = "Flips (and corrects) the axis suffixes of the names of selected bones"; + + /* api callbacks */ + ot->exec = armature_flip_names_exec; + ot->poll = ED_operator_editarmature; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + RNA_def_boolean(ot->srna, + "do_strip_numbers", + false, + "Strip Numbers", + "Try to remove right-most dot-number from flipped names " + "(WARNING: may result in incoherent naming in some cases)"); } static int armature_autoside_names_exec(bContext *C, wmOperator *op) { - ViewLayer *view_layer = CTX_data_view_layer(C); - Main *bmain = CTX_data_main(C); - char newname[MAXBONENAME]; - const short axis = RNA_enum_get(op->ptr, "type"); - bool changed_multi = false; - - 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 changed = false; - - /* Paranoia checks. */ - if (ELEM(NULL, ob, ob->pose)) { - continue; - } - - for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { - if (EBONE_EDITABLE(ebone)) { - - /* We first need to do the flipped bone, then the original one. - * Otherwise we can't find the flipped one because of the bone name change. */ - if (arm->flag & ARM_MIRROR_EDIT) { - EditBone *flipbone = ED_armature_ebone_get_mirrored(arm->edbo, ebone); - if ((flipbone) && !(flipbone->flag & BONE_SELECTED)) { - BLI_strncpy(newname, flipbone->name, sizeof(newname)); - if (bone_autoside_name(newname, 1, axis, flipbone->head[axis], flipbone->tail[axis])) { - ED_armature_bone_rename(bmain, arm, flipbone->name, newname); - changed = true; - } - } - } - - BLI_strncpy(newname, ebone->name, sizeof(newname)); - if (bone_autoside_name(newname, 1, axis, ebone->head[axis], ebone->tail[axis])) { - ED_armature_bone_rename(bmain, arm, ebone->name, newname); - changed = true; - } - } - } - - if (!changed) { - continue; - } - - changed_multi = true; - - /* Since we renamed stuff... */ - DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); - - /* Note, notifier might evolve. */ - WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); - } - MEM_freeN(objects); - return changed_multi ? OPERATOR_FINISHED : OPERATOR_CANCELLED; + ViewLayer *view_layer = CTX_data_view_layer(C); + Main *bmain = CTX_data_main(C); + char newname[MAXBONENAME]; + const short axis = RNA_enum_get(op->ptr, "type"); + bool changed_multi = false; + + 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 changed = false; + + /* Paranoia checks. */ + if (ELEM(NULL, ob, ob->pose)) { + continue; + } + + for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { + if (EBONE_EDITABLE(ebone)) { + + /* We first need to do the flipped bone, then the original one. + * Otherwise we can't find the flipped one because of the bone name change. */ + if (arm->flag & ARM_MIRROR_EDIT) { + EditBone *flipbone = ED_armature_ebone_get_mirrored(arm->edbo, ebone); + if ((flipbone) && !(flipbone->flag & BONE_SELECTED)) { + BLI_strncpy(newname, flipbone->name, sizeof(newname)); + if (bone_autoside_name(newname, 1, axis, flipbone->head[axis], flipbone->tail[axis])) { + ED_armature_bone_rename(bmain, arm, flipbone->name, newname); + changed = true; + } + } + } + + BLI_strncpy(newname, ebone->name, sizeof(newname)); + if (bone_autoside_name(newname, 1, axis, ebone->head[axis], ebone->tail[axis])) { + ED_armature_bone_rename(bmain, arm, ebone->name, newname); + changed = true; + } + } + } + + if (!changed) { + continue; + } + + changed_multi = true; + + /* Since we renamed stuff... */ + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); + + /* Note, notifier might evolve. */ + WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); + } + MEM_freeN(objects); + return changed_multi ? OPERATOR_FINISHED : OPERATOR_CANCELLED; } void ARMATURE_OT_autoside_names(wmOperatorType *ot) { - static const EnumPropertyItem axis_items[] = { - {0, "XAXIS", 0, "X-Axis", "Left/Right"}, - {1, "YAXIS", 0, "Y-Axis", "Front/Back"}, - {2, "ZAXIS", 0, "Z-Axis", "Top/Bottom"}, - {0, NULL, 0, NULL, NULL}, - }; - - /* identifiers */ - ot->name = "AutoName by Axis"; - ot->idname = "ARMATURE_OT_autoside_names"; - ot->description = "Automatically renames the selected bones according to which side of the target axis they fall on"; - - /* api callbacks */ - ot->invoke = WM_menu_invoke; - ot->exec = armature_autoside_names_exec; - ot->poll = ED_operator_editarmature; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* settings */ - ot->prop = RNA_def_enum(ot->srna, "type", axis_items, 0, "Axis", "Axis tag names with"); + static const EnumPropertyItem axis_items[] = { + {0, "XAXIS", 0, "X-Axis", "Left/Right"}, + {1, "YAXIS", 0, "Y-Axis", "Front/Back"}, + {2, "ZAXIS", 0, "Z-Axis", "Top/Bottom"}, + {0, NULL, 0, NULL, NULL}, + }; + + /* identifiers */ + ot->name = "AutoName by Axis"; + ot->idname = "ARMATURE_OT_autoside_names"; + ot->description = + "Automatically renames the selected bones according to which side of the target axis they " + "fall on"; + + /* api callbacks */ + ot->invoke = WM_menu_invoke; + ot->exec = armature_autoside_names_exec; + ot->poll = ED_operator_editarmature; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* 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 36ca3dd486d..c820077fbf9 100644 --- a/source/blender/editors/armature/armature_ops.c +++ b/source/blender/editors/armature/armature_ops.c @@ -38,160 +38,163 @@ /* Both operators ARMATURE_OT_xxx and POSE_OT_xxx here */ void ED_operatortypes_armature(void) { - /* EDIT ARMATURE */ - WM_operatortype_append(ARMATURE_OT_bone_primitive_add); - - WM_operatortype_append(ARMATURE_OT_align); - WM_operatortype_append(ARMATURE_OT_calculate_roll); - WM_operatortype_append(ARMATURE_OT_roll_clear); - WM_operatortype_append(ARMATURE_OT_switch_direction); - WM_operatortype_append(ARMATURE_OT_subdivide); - - WM_operatortype_append(ARMATURE_OT_parent_set); - WM_operatortype_append(ARMATURE_OT_parent_clear); - - WM_operatortype_append(ARMATURE_OT_select_all); - WM_operatortype_append(ARMATURE_OT_select_mirror); - WM_operatortype_append(ARMATURE_OT_select_more); - 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_similar); - WM_operatortype_append(ARMATURE_OT_shortest_path_pick); - - WM_operatortype_append(ARMATURE_OT_delete); - WM_operatortype_append(ARMATURE_OT_dissolve); - WM_operatortype_append(ARMATURE_OT_duplicate); - WM_operatortype_append(ARMATURE_OT_symmetrize); - WM_operatortype_append(ARMATURE_OT_extrude); - WM_operatortype_append(ARMATURE_OT_hide); - 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); - - WM_operatortype_append(ARMATURE_OT_autoside_names); - WM_operatortype_append(ARMATURE_OT_flip_names); - - WM_operatortype_append(ARMATURE_OT_layers_show_all); - WM_operatortype_append(ARMATURE_OT_armature_layers); - WM_operatortype_append(ARMATURE_OT_bone_layers); - - /* POSE */ - WM_operatortype_append(POSE_OT_hide); - WM_operatortype_append(POSE_OT_reveal); - - WM_operatortype_append(POSE_OT_armature_apply); - WM_operatortype_append(POSE_OT_visual_transform_apply); - - WM_operatortype_append(POSE_OT_rot_clear); - WM_operatortype_append(POSE_OT_loc_clear); - WM_operatortype_append(POSE_OT_scale_clear); - WM_operatortype_append(POSE_OT_transforms_clear); - WM_operatortype_append(POSE_OT_user_transforms_clear); - - WM_operatortype_append(POSE_OT_copy); - WM_operatortype_append(POSE_OT_paste); - - WM_operatortype_append(POSE_OT_select_all); - - 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_constraint_target); - WM_operatortype_append(POSE_OT_select_grouped); - WM_operatortype_append(POSE_OT_select_mirror); - - WM_operatortype_append(POSE_OT_group_add); - WM_operatortype_append(POSE_OT_group_remove); - WM_operatortype_append(POSE_OT_group_move); - WM_operatortype_append(POSE_OT_group_sort); - WM_operatortype_append(POSE_OT_group_assign); - WM_operatortype_append(POSE_OT_group_unassign); - WM_operatortype_append(POSE_OT_group_select); - WM_operatortype_append(POSE_OT_group_deselect); - - WM_operatortype_append(POSE_OT_paths_calculate); - WM_operatortype_append(POSE_OT_paths_update); - WM_operatortype_append(POSE_OT_paths_clear); - WM_operatortype_append(POSE_OT_paths_range_update); - - WM_operatortype_append(POSE_OT_autoside_names); - WM_operatortype_append(POSE_OT_flip_names); - - WM_operatortype_append(POSE_OT_rotation_mode_set); - - WM_operatortype_append(POSE_OT_quaternions_flip); - - WM_operatortype_append(POSE_OT_bone_layers); - - WM_operatortype_append(POSE_OT_propagate); - - /* POSELIB */ - WM_operatortype_append(POSELIB_OT_browse_interactive); - WM_operatortype_append(POSELIB_OT_apply_pose); - - WM_operatortype_append(POSELIB_OT_pose_add); - WM_operatortype_append(POSELIB_OT_pose_remove); - WM_operatortype_append(POSELIB_OT_pose_rename); - WM_operatortype_append(POSELIB_OT_pose_move); - - WM_operatortype_append(POSELIB_OT_new); - WM_operatortype_append(POSELIB_OT_unlink); - - WM_operatortype_append(POSELIB_OT_action_sanitize); - - /* POSE SLIDING */ - WM_operatortype_append(POSE_OT_push); - WM_operatortype_append(POSE_OT_relax); - WM_operatortype_append(POSE_OT_breakdown); + /* EDIT ARMATURE */ + WM_operatortype_append(ARMATURE_OT_bone_primitive_add); + + WM_operatortype_append(ARMATURE_OT_align); + WM_operatortype_append(ARMATURE_OT_calculate_roll); + WM_operatortype_append(ARMATURE_OT_roll_clear); + WM_operatortype_append(ARMATURE_OT_switch_direction); + WM_operatortype_append(ARMATURE_OT_subdivide); + + WM_operatortype_append(ARMATURE_OT_parent_set); + WM_operatortype_append(ARMATURE_OT_parent_clear); + + WM_operatortype_append(ARMATURE_OT_select_all); + WM_operatortype_append(ARMATURE_OT_select_mirror); + WM_operatortype_append(ARMATURE_OT_select_more); + 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_similar); + WM_operatortype_append(ARMATURE_OT_shortest_path_pick); + + WM_operatortype_append(ARMATURE_OT_delete); + WM_operatortype_append(ARMATURE_OT_dissolve); + WM_operatortype_append(ARMATURE_OT_duplicate); + WM_operatortype_append(ARMATURE_OT_symmetrize); + WM_operatortype_append(ARMATURE_OT_extrude); + WM_operatortype_append(ARMATURE_OT_hide); + 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); + + WM_operatortype_append(ARMATURE_OT_autoside_names); + WM_operatortype_append(ARMATURE_OT_flip_names); + + WM_operatortype_append(ARMATURE_OT_layers_show_all); + WM_operatortype_append(ARMATURE_OT_armature_layers); + WM_operatortype_append(ARMATURE_OT_bone_layers); + + /* POSE */ + WM_operatortype_append(POSE_OT_hide); + WM_operatortype_append(POSE_OT_reveal); + + WM_operatortype_append(POSE_OT_armature_apply); + WM_operatortype_append(POSE_OT_visual_transform_apply); + + WM_operatortype_append(POSE_OT_rot_clear); + WM_operatortype_append(POSE_OT_loc_clear); + WM_operatortype_append(POSE_OT_scale_clear); + WM_operatortype_append(POSE_OT_transforms_clear); + WM_operatortype_append(POSE_OT_user_transforms_clear); + + WM_operatortype_append(POSE_OT_copy); + WM_operatortype_append(POSE_OT_paste); + + WM_operatortype_append(POSE_OT_select_all); + + 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_constraint_target); + WM_operatortype_append(POSE_OT_select_grouped); + WM_operatortype_append(POSE_OT_select_mirror); + + WM_operatortype_append(POSE_OT_group_add); + WM_operatortype_append(POSE_OT_group_remove); + WM_operatortype_append(POSE_OT_group_move); + WM_operatortype_append(POSE_OT_group_sort); + WM_operatortype_append(POSE_OT_group_assign); + WM_operatortype_append(POSE_OT_group_unassign); + WM_operatortype_append(POSE_OT_group_select); + WM_operatortype_append(POSE_OT_group_deselect); + + WM_operatortype_append(POSE_OT_paths_calculate); + WM_operatortype_append(POSE_OT_paths_update); + WM_operatortype_append(POSE_OT_paths_clear); + WM_operatortype_append(POSE_OT_paths_range_update); + + WM_operatortype_append(POSE_OT_autoside_names); + WM_operatortype_append(POSE_OT_flip_names); + + WM_operatortype_append(POSE_OT_rotation_mode_set); + + WM_operatortype_append(POSE_OT_quaternions_flip); + + WM_operatortype_append(POSE_OT_bone_layers); + + WM_operatortype_append(POSE_OT_propagate); + + /* POSELIB */ + WM_operatortype_append(POSELIB_OT_browse_interactive); + WM_operatortype_append(POSELIB_OT_apply_pose); + + WM_operatortype_append(POSELIB_OT_pose_add); + WM_operatortype_append(POSELIB_OT_pose_remove); + WM_operatortype_append(POSELIB_OT_pose_rename); + WM_operatortype_append(POSELIB_OT_pose_move); + + WM_operatortype_append(POSELIB_OT_new); + WM_operatortype_append(POSELIB_OT_unlink); + + WM_operatortype_append(POSELIB_OT_action_sanitize); + + /* POSE SLIDING */ + WM_operatortype_append(POSE_OT_push); + WM_operatortype_append(POSE_OT_relax); + WM_operatortype_append(POSE_OT_breakdown); } void ED_operatormacros_armature(void) { - wmOperatorType *ot; - wmOperatorTypeMacro *otmacro; - - ot = WM_operatortype_append_macro("ARMATURE_OT_duplicate_move", "Duplicate", - "Make copies of the selected bones within the same armature and move them", - OPTYPE_UNDO | OPTYPE_REGISTER); - WM_operatortype_macro_define(ot, "ARMATURE_OT_duplicate"); - otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate"); - RNA_enum_set(otmacro->ptr, "proportional", 0); - - ot = WM_operatortype_append_macro("ARMATURE_OT_extrude_move", "Extrude", - "Create new bones from the selected joints and move them", - OPTYPE_UNDO | OPTYPE_REGISTER); - otmacro = WM_operatortype_macro_define(ot, "ARMATURE_OT_extrude"); - RNA_boolean_set(otmacro->ptr, "forked", false); - otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate"); - RNA_enum_set(otmacro->ptr, "proportional", 0); - - /* XXX would it be nicer to just be able to have standard extrude_move, but set the forked property separate? - * that would require fixing a properties bug 19733 */ - ot = WM_operatortype_append_macro("ARMATURE_OT_extrude_forked", "Extrude Forked", - "Create new bones from the selected joints and move them", - OPTYPE_UNDO | OPTYPE_REGISTER); - otmacro = WM_operatortype_macro_define(ot, "ARMATURE_OT_extrude"); - RNA_boolean_set(otmacro->ptr, "forked", true); - otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate"); - RNA_enum_set(otmacro->ptr, "proportional", 0); + wmOperatorType *ot; + wmOperatorTypeMacro *otmacro; + + ot = WM_operatortype_append_macro( + "ARMATURE_OT_duplicate_move", + "Duplicate", + "Make copies of the selected bones within the same armature and move them", + OPTYPE_UNDO | OPTYPE_REGISTER); + WM_operatortype_macro_define(ot, "ARMATURE_OT_duplicate"); + otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate"); + RNA_enum_set(otmacro->ptr, "proportional", 0); + + ot = WM_operatortype_append_macro("ARMATURE_OT_extrude_move", + "Extrude", + "Create new bones from the selected joints and move them", + OPTYPE_UNDO | OPTYPE_REGISTER); + otmacro = WM_operatortype_macro_define(ot, "ARMATURE_OT_extrude"); + RNA_boolean_set(otmacro->ptr, "forked", false); + otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate"); + RNA_enum_set(otmacro->ptr, "proportional", 0); + + /* XXX would it be nicer to just be able to have standard extrude_move, but set the forked property separate? + * that would require fixing a properties bug 19733 */ + ot = WM_operatortype_append_macro("ARMATURE_OT_extrude_forked", + "Extrude Forked", + "Create new bones from the selected joints and move them", + OPTYPE_UNDO | OPTYPE_REGISTER); + otmacro = WM_operatortype_macro_define(ot, "ARMATURE_OT_extrude"); + RNA_boolean_set(otmacro->ptr, "forked", true); + otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate"); + RNA_enum_set(otmacro->ptr, "proportional", 0); } void ED_keymap_armature(wmKeyConfig *keyconf) { - wmKeyMap *keymap; + wmKeyMap *keymap; - /* Armature ------------------------ */ - /* only set in editmode armature, by space_view3d listener */ - keymap = WM_keymap_ensure(keyconf, "Armature", 0, 0); - keymap->poll = ED_operator_editarmature; - - /* Pose ------------------------ */ - /* only set in posemode, by space_view3d listener */ - keymap = WM_keymap_ensure(keyconf, "Pose", 0, 0); - keymap->poll = ED_operator_posemode; + /* Armature ------------------------ */ + /* only set in editmode armature, by space_view3d listener */ + keymap = WM_keymap_ensure(keyconf, "Armature", 0, 0); + keymap->poll = ED_operator_editarmature; + /* Pose ------------------------ */ + /* only set in posemode, by space_view3d listener */ + keymap = WM_keymap_ensure(keyconf, "Pose", 0, 0); + keymap->poll = ED_operator_posemode; } diff --git a/source/blender/editors/armature/armature_relations.c b/source/blender/editors/armature/armature_relations.c index 5928e1cd12c..6226059e794 100644 --- a/source/blender/editors/armature/armature_relations.c +++ b/source/blender/editors/armature/armature_relations.c @@ -67,55 +67,53 @@ /* NOTE: no operator define here as this is exported to the Object-level operator */ static void joined_armature_fix_links_constraints( - Object *tarArm, Object *srcArm, bPoseChannel *pchan, EditBone *curbone, - ListBase *lb) + Object *tarArm, Object *srcArm, bPoseChannel *pchan, EditBone *curbone, ListBase *lb) { - bConstraint *con; - - for (con = lb->first; con; con = con->next) { - const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con); - ListBase targets = {NULL, NULL}; - bConstraintTarget *ct; - - /* constraint targets */ - if (cti && cti->get_constraint_targets) { - cti->get_constraint_targets(con, &targets); - - for (ct = targets.first; ct; ct = ct->next) { - if (ct->tar == srcArm) { - if (ct->subtarget[0] == '\0') { - ct->tar = tarArm; - } - else if (STREQ(ct->subtarget, pchan->name)) { - ct->tar = tarArm; - BLI_strncpy(ct->subtarget, curbone->name, sizeof(ct->subtarget)); - } - } - } - - if (cti->flush_constraint_targets) - cti->flush_constraint_targets(con, &targets, 0); - } - - /* action constraint? (pose constraints only) */ - if (con->type == CONSTRAINT_TYPE_ACTION) { - bActionConstraint *data = con->data; - - if (data->act) { - BKE_action_fix_paths_rename(&tarArm->id, data->act, "pose.bones[", - pchan->name, curbone->name, 0, 0, false); - } - } - - } + bConstraint *con; + + for (con = lb->first; con; con = con->next) { + const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con); + ListBase targets = {NULL, NULL}; + bConstraintTarget *ct; + + /* constraint targets */ + if (cti && cti->get_constraint_targets) { + cti->get_constraint_targets(con, &targets); + + for (ct = targets.first; ct; ct = ct->next) { + if (ct->tar == srcArm) { + if (ct->subtarget[0] == '\0') { + ct->tar = tarArm; + } + else if (STREQ(ct->subtarget, pchan->name)) { + ct->tar = tarArm; + BLI_strncpy(ct->subtarget, curbone->name, sizeof(ct->subtarget)); + } + } + } + + if (cti->flush_constraint_targets) + cti->flush_constraint_targets(con, &targets, 0); + } + + /* action constraint? (pose constraints only) */ + if (con->type == CONSTRAINT_TYPE_ACTION) { + bActionConstraint *data = con->data; + + if (data->act) { + BKE_action_fix_paths_rename( + &tarArm->id, data->act, "pose.bones[", pchan->name, curbone->name, 0, 0, false); + } + } + } } /* userdata for joined_armature_fix_animdata_cb() */ typedef struct tJoinArmature_AdtFixData { - Object *srcArm; - Object *tarArm; + Object *srcArm; + Object *tarArm; - GHash *names_map; + GHash *names_map; } tJoinArmature_AdtFixData; /* Callback to pass to BKE_animdata_main_cb() for fixing driver ID's to point to the new ID */ @@ -124,282 +122,281 @@ typedef struct tJoinArmature_AdtFixData { */ static void joined_armature_fix_animdata_cb(ID *id, FCurve *fcu, void *user_data) { - tJoinArmature_AdtFixData *afd = (tJoinArmature_AdtFixData *)user_data; - ID *src_id = &afd->srcArm->id; - ID *dst_id = &afd->tarArm->id; - - GHashIterator gh_iter; - - /* Fix paths - If this is the target object, it will have some "dirty" paths */ - if ((id == src_id) && strstr(fcu->rna_path, "pose.bones[")) { - GHASH_ITER(gh_iter, afd->names_map) { - const char *old_name = BLI_ghashIterator_getKey(&gh_iter); - const char *new_name = BLI_ghashIterator_getValue(&gh_iter); - - /* only remap if changed; this still means there will be some - * waste if there aren't many drivers/keys */ - if (!STREQ(old_name, new_name) && strstr(fcu->rna_path, old_name)) { - fcu->rna_path = BKE_animsys_fix_rna_path_rename(id, fcu->rna_path, "pose.bones", - old_name, new_name, 0, 0, false); - - /* we don't want to apply a second remapping on this driver now, - * so stop trying names, but keep fixing drivers - */ - break; - } - } - } - - - /* Driver targets */ - if (fcu->driver) { - ChannelDriver *driver = fcu->driver; - DriverVar *dvar; - - /* Fix driver references to invalid ID's */ - for (dvar = driver->variables.first; dvar; dvar = dvar->next) { - /* only change the used targets, since the others will need fixing manually anyway */ - DRIVER_TARGETS_USED_LOOPER_BEGIN(dvar) - { - /* change the ID's used... */ - if (dtar->id == src_id) { - dtar->id = dst_id; - - /* also check on the subtarget... - * XXX: We duplicate the logic from drivers_path_rename_fix() here, with our own - * little twists so that we know that it isn't going to clobber the wrong data - */ - if ((dtar->rna_path && strstr(dtar->rna_path, "pose.bones[")) || (dtar->pchan_name[0])) { - GHASH_ITER(gh_iter, afd->names_map) { - const char *old_name = BLI_ghashIterator_getKey(&gh_iter); - const char *new_name = BLI_ghashIterator_getValue(&gh_iter); - - /* only remap if changed */ - if (!STREQ(old_name, new_name)) { - if ((dtar->rna_path) && strstr(dtar->rna_path, old_name)) { - /* Fix up path */ - dtar->rna_path = BKE_animsys_fix_rna_path_rename(id, dtar->rna_path, "pose.bones", - old_name, new_name, 0, 0, false); - break; /* no need to try any more names for bone path */ - } - else if (STREQ(dtar->pchan_name, old_name)) { - /* Change target bone name */ - BLI_strncpy(dtar->pchan_name, new_name, sizeof(dtar->pchan_name)); - break; /* no need to try any more names for bone subtarget */ - } - } - } - } - } - } - DRIVER_TARGETS_LOOPER_END; - } - } + tJoinArmature_AdtFixData *afd = (tJoinArmature_AdtFixData *)user_data; + ID *src_id = &afd->srcArm->id; + ID *dst_id = &afd->tarArm->id; + + GHashIterator gh_iter; + + /* Fix paths - If this is the target object, it will have some "dirty" paths */ + if ((id == src_id) && strstr(fcu->rna_path, "pose.bones[")) { + GHASH_ITER (gh_iter, afd->names_map) { + const char *old_name = BLI_ghashIterator_getKey(&gh_iter); + const char *new_name = BLI_ghashIterator_getValue(&gh_iter); + + /* only remap if changed; this still means there will be some + * waste if there aren't many drivers/keys */ + if (!STREQ(old_name, new_name) && strstr(fcu->rna_path, old_name)) { + fcu->rna_path = BKE_animsys_fix_rna_path_rename( + id, fcu->rna_path, "pose.bones", old_name, new_name, 0, 0, false); + + /* we don't want to apply a second remapping on this driver now, + * so stop trying names, but keep fixing drivers + */ + break; + } + } + } + + /* Driver targets */ + if (fcu->driver) { + ChannelDriver *driver = fcu->driver; + DriverVar *dvar; + + /* Fix driver references to invalid ID's */ + for (dvar = driver->variables.first; dvar; dvar = dvar->next) { + /* only change the used targets, since the others will need fixing manually anyway */ + DRIVER_TARGETS_USED_LOOPER_BEGIN (dvar) { + /* change the ID's used... */ + if (dtar->id == src_id) { + dtar->id = dst_id; + + /* also check on the subtarget... + * XXX: We duplicate the logic from drivers_path_rename_fix() here, with our own + * little twists so that we know that it isn't going to clobber the wrong data + */ + if ((dtar->rna_path && strstr(dtar->rna_path, "pose.bones[")) || (dtar->pchan_name[0])) { + GHASH_ITER (gh_iter, afd->names_map) { + const char *old_name = BLI_ghashIterator_getKey(&gh_iter); + const char *new_name = BLI_ghashIterator_getValue(&gh_iter); + + /* only remap if changed */ + if (!STREQ(old_name, new_name)) { + if ((dtar->rna_path) && strstr(dtar->rna_path, old_name)) { + /* Fix up path */ + dtar->rna_path = BKE_animsys_fix_rna_path_rename( + id, dtar->rna_path, "pose.bones", old_name, new_name, 0, 0, false); + break; /* no need to try any more names for bone path */ + } + else if (STREQ(dtar->pchan_name, old_name)) { + /* Change target bone name */ + BLI_strncpy(dtar->pchan_name, new_name, sizeof(dtar->pchan_name)); + break; /* no need to try any more names for bone subtarget */ + } + } + } + } + } + } + DRIVER_TARGETS_LOOPER_END; + } + } } /* Helper function for armature joining - link fixing */ -static void joined_armature_fix_links(Main *bmain, Object *tarArm, Object *srcArm, bPoseChannel *pchan, EditBone *curbone) +static void joined_armature_fix_links( + Main *bmain, Object *tarArm, Object *srcArm, bPoseChannel *pchan, EditBone *curbone) { - Object *ob; - bPose *pose; - bPoseChannel *pchant; - - /* let's go through all objects in database */ - for (ob = bmain->objects.first; ob; ob = ob->id.next) { - /* do some object-type specific things */ - if (ob->type == OB_ARMATURE) { - pose = ob->pose; - for (pchant = pose->chanbase.first; pchant; pchant = pchant->next) { - joined_armature_fix_links_constraints(tarArm, srcArm, pchan, curbone, &pchant->constraints); - } - } - - /* fix object-level constraints */ - if (ob != srcArm) { - joined_armature_fix_links_constraints(tarArm, srcArm, pchan, curbone, &ob->constraints); - } - - /* See if an object is parented to this armature */ - if (ob->parent && (ob->parent == srcArm)) { - /* Is object parented to a bone of this src armature? */ - if (ob->partype == PARBONE) { - /* bone name in object */ - if (STREQ(ob->parsubstr, pchan->name)) { - BLI_strncpy(ob->parsubstr, curbone->name, sizeof(ob->parsubstr)); - } - } - - /* make tar armature be new parent */ - ob->parent = tarArm; - } - } + Object *ob; + bPose *pose; + bPoseChannel *pchant; + + /* let's go through all objects in database */ + for (ob = bmain->objects.first; ob; ob = ob->id.next) { + /* do some object-type specific things */ + if (ob->type == OB_ARMATURE) { + pose = ob->pose; + for (pchant = pose->chanbase.first; pchant; pchant = pchant->next) { + joined_armature_fix_links_constraints( + tarArm, srcArm, pchan, curbone, &pchant->constraints); + } + } + + /* fix object-level constraints */ + if (ob != srcArm) { + joined_armature_fix_links_constraints(tarArm, srcArm, pchan, curbone, &ob->constraints); + } + + /* See if an object is parented to this armature */ + if (ob->parent && (ob->parent == srcArm)) { + /* Is object parented to a bone of this src armature? */ + if (ob->partype == PARBONE) { + /* bone name in object */ + if (STREQ(ob->parsubstr, pchan->name)) { + BLI_strncpy(ob->parsubstr, curbone->name, sizeof(ob->parsubstr)); + } + } + + /* make tar armature be new parent */ + ob->parent = tarArm; + } + } } /* join armature exec is exported for use in object->join objects operator... */ int join_armature_exec(bContext *C, wmOperator *op) { - Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); - Object *ob_active = CTX_data_active_object(C); - bArmature *arm = (ob_active) ? ob_active->data : NULL; - bPose *pose, *opose; - bPoseChannel *pchan, *pchann; - EditBone *curbone; - float mat[4][4], oimat[4][4]; - bool ok = false; - - /* Ensure we're not in editmode and that the active object is an armature*/ - if (!ob_active || ob_active->type != OB_ARMATURE) - return OPERATOR_CANCELLED; - if (!arm || arm->edbo) - return OPERATOR_CANCELLED; - - CTX_DATA_BEGIN(C, Object *, ob_iter, selected_editable_objects) - { - if (ob_iter == ob_active) { - ok = true; - break; - } - } - CTX_DATA_END; - - /* that way the active object is always selected */ - if (ok == false) { - BKE_report(op->reports, RPT_WARNING, "Active object is not a selected armature"); - return OPERATOR_CANCELLED; - } - - /* Get editbones of active armature to add editbones to */ - ED_armature_to_edit(arm); - - /* get pose of active object and move it out of posemode */ - pose = ob_active->pose; - ob_active->mode &= ~OB_MODE_POSE; - - CTX_DATA_BEGIN(C, Object *, ob_iter, selected_editable_objects) - { - if ((ob_iter->type == OB_ARMATURE) && (ob_iter != ob_active)) { - tJoinArmature_AdtFixData afd = {NULL}; - bArmature *curarm = ob_iter->data; - - /* we assume that each armature datablock is only used in a single place */ - BLI_assert(ob_active->data != ob_iter->data); - - /* init callback data for fixing up AnimData links later */ - afd.srcArm = ob_iter; - afd.tarArm = ob_active; - afd.names_map = BLI_ghash_str_new("join_armature_adt_fix"); - - /* Make a list of editbones in current armature */ - ED_armature_to_edit(ob_iter->data); - - /* Get Pose of current armature */ - opose = ob_iter->pose; - ob_iter->mode &= ~OB_MODE_POSE; - //BASACT->flag &= ~OB_MODE_POSE; - - /* Find the difference matrix */ - invert_m4_m4(oimat, ob_active->obmat); - mul_m4_m4m4(mat, oimat, ob_iter->obmat); - - /* Copy bones and posechannels from the object to the edit armature */ - for (pchan = opose->chanbase.first; pchan; pchan = pchann) { - pchann = pchan->next; - curbone = ED_armature_ebone_find_name(curarm->edbo, pchan->name); - - /* Get new name */ - ED_armature_ebone_unique_name(arm->edbo, curbone->name, NULL); - BLI_ghash_insert(afd.names_map, BLI_strdup(pchan->name), curbone->name); - - /* Transform the bone */ - { - float premat[4][4]; - float postmat[4][4]; - float difmat[4][4]; - float imat[4][4]; - float temp[3][3]; - - /* Get the premat */ - ED_armature_ebone_to_mat3(curbone, temp); - - unit_m4(premat); /* mul_m4_m3m4 only sets 3x3 part */ - mul_m4_m3m4(premat, temp, mat); - - mul_m4_v3(mat, curbone->head); - mul_m4_v3(mat, curbone->tail); - - /* Get the postmat */ - ED_armature_ebone_to_mat3(curbone, temp); - copy_m4_m3(postmat, temp); - - /* Find the roll */ - invert_m4_m4(imat, premat); - mul_m4_m4m4(difmat, imat, postmat); - - curbone->roll -= atan2f(difmat[2][0], difmat[2][2]); - } - - /* Fix Constraints and Other Links to this Bone and Armature */ - joined_armature_fix_links(bmain, ob_active, ob_iter, pchan, curbone); - - /* Rename pchan */ - BLI_strncpy(pchan->name, curbone->name, sizeof(pchan->name)); - - /* Jump Ship! */ - BLI_remlink(curarm->edbo, curbone); - BLI_addtail(arm->edbo, curbone); - - BLI_remlink(&opose->chanbase, pchan); - BLI_addtail(&pose->chanbase, pchan); - BKE_pose_channels_hash_free(opose); - BKE_pose_channels_hash_free(pose); - } - - /* Fix all the drivers (and animation data) */ - BKE_fcurves_main_cb(bmain, joined_armature_fix_animdata_cb, &afd); - BLI_ghash_free(afd.names_map, MEM_freeN, NULL); - - /* Only copy over animdata now, after all the remapping has been done, - * so that we don't have to worry about ambiguities re which armature - * a bone came from! - */ - if (ob_iter->adt) { - if (ob_active->adt == NULL) { - /* no animdata, so just use a copy of the whole thing */ - ob_active->adt = BKE_animdata_copy(bmain, ob_iter->adt, 0); - } - else { - /* merge in data - we'll fix the drivers manually */ - BKE_animdata_merge_copy(bmain, &ob_active->id, &ob_iter->id, ADT_MERGECOPY_KEEP_DST, false); - } - } - - if (curarm->adt) { - if (arm->adt == NULL) { - /* no animdata, so just use a copy of the whole thing */ - arm->adt = BKE_animdata_copy(bmain, curarm->adt, 0); - } - else { - /* merge in data - we'll fix the drivers manually */ - BKE_animdata_merge_copy(bmain, &arm->id, &curarm->id, ADT_MERGECOPY_KEEP_DST, false); - } - } - - /* Free the old object data */ - ED_object_base_free_and_unlink(bmain, scene, ob_iter); - } - } - CTX_DATA_END; - - DEG_relations_tag_update(bmain); /* because we removed object(s) */ - - ED_armature_from_edit(bmain, arm); - ED_armature_edit_free(arm); - - DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); - WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene); - - return OPERATOR_FINISHED; + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + Object *ob_active = CTX_data_active_object(C); + bArmature *arm = (ob_active) ? ob_active->data : NULL; + bPose *pose, *opose; + bPoseChannel *pchan, *pchann; + EditBone *curbone; + float mat[4][4], oimat[4][4]; + bool ok = false; + + /* Ensure we're not in editmode and that the active object is an armature*/ + if (!ob_active || ob_active->type != OB_ARMATURE) + return OPERATOR_CANCELLED; + if (!arm || arm->edbo) + return OPERATOR_CANCELLED; + + CTX_DATA_BEGIN (C, Object *, ob_iter, selected_editable_objects) { + if (ob_iter == ob_active) { + ok = true; + break; + } + } + CTX_DATA_END; + + /* that way the active object is always selected */ + if (ok == false) { + BKE_report(op->reports, RPT_WARNING, "Active object is not a selected armature"); + return OPERATOR_CANCELLED; + } + + /* Get editbones of active armature to add editbones to */ + ED_armature_to_edit(arm); + + /* get pose of active object and move it out of posemode */ + pose = ob_active->pose; + ob_active->mode &= ~OB_MODE_POSE; + + CTX_DATA_BEGIN (C, Object *, ob_iter, selected_editable_objects) { + if ((ob_iter->type == OB_ARMATURE) && (ob_iter != ob_active)) { + tJoinArmature_AdtFixData afd = {NULL}; + bArmature *curarm = ob_iter->data; + + /* we assume that each armature datablock is only used in a single place */ + BLI_assert(ob_active->data != ob_iter->data); + + /* init callback data for fixing up AnimData links later */ + afd.srcArm = ob_iter; + afd.tarArm = ob_active; + afd.names_map = BLI_ghash_str_new("join_armature_adt_fix"); + + /* Make a list of editbones in current armature */ + ED_armature_to_edit(ob_iter->data); + + /* Get Pose of current armature */ + opose = ob_iter->pose; + ob_iter->mode &= ~OB_MODE_POSE; + //BASACT->flag &= ~OB_MODE_POSE; + + /* Find the difference matrix */ + invert_m4_m4(oimat, ob_active->obmat); + mul_m4_m4m4(mat, oimat, ob_iter->obmat); + + /* Copy bones and posechannels from the object to the edit armature */ + for (pchan = opose->chanbase.first; pchan; pchan = pchann) { + pchann = pchan->next; + curbone = ED_armature_ebone_find_name(curarm->edbo, pchan->name); + + /* Get new name */ + ED_armature_ebone_unique_name(arm->edbo, curbone->name, NULL); + BLI_ghash_insert(afd.names_map, BLI_strdup(pchan->name), curbone->name); + + /* Transform the bone */ + { + float premat[4][4]; + float postmat[4][4]; + float difmat[4][4]; + float imat[4][4]; + float temp[3][3]; + + /* Get the premat */ + ED_armature_ebone_to_mat3(curbone, temp); + + unit_m4(premat); /* mul_m4_m3m4 only sets 3x3 part */ + mul_m4_m3m4(premat, temp, mat); + + mul_m4_v3(mat, curbone->head); + mul_m4_v3(mat, curbone->tail); + + /* Get the postmat */ + ED_armature_ebone_to_mat3(curbone, temp); + copy_m4_m3(postmat, temp); + + /* Find the roll */ + invert_m4_m4(imat, premat); + mul_m4_m4m4(difmat, imat, postmat); + + curbone->roll -= atan2f(difmat[2][0], difmat[2][2]); + } + + /* Fix Constraints and Other Links to this Bone and Armature */ + joined_armature_fix_links(bmain, ob_active, ob_iter, pchan, curbone); + + /* Rename pchan */ + BLI_strncpy(pchan->name, curbone->name, sizeof(pchan->name)); + + /* Jump Ship! */ + BLI_remlink(curarm->edbo, curbone); + BLI_addtail(arm->edbo, curbone); + + BLI_remlink(&opose->chanbase, pchan); + BLI_addtail(&pose->chanbase, pchan); + BKE_pose_channels_hash_free(opose); + BKE_pose_channels_hash_free(pose); + } + + /* Fix all the drivers (and animation data) */ + BKE_fcurves_main_cb(bmain, joined_armature_fix_animdata_cb, &afd); + BLI_ghash_free(afd.names_map, MEM_freeN, NULL); + + /* Only copy over animdata now, after all the remapping has been done, + * so that we don't have to worry about ambiguities re which armature + * a bone came from! + */ + if (ob_iter->adt) { + if (ob_active->adt == NULL) { + /* no animdata, so just use a copy of the whole thing */ + ob_active->adt = BKE_animdata_copy(bmain, ob_iter->adt, 0); + } + else { + /* merge in data - we'll fix the drivers manually */ + BKE_animdata_merge_copy( + bmain, &ob_active->id, &ob_iter->id, ADT_MERGECOPY_KEEP_DST, false); + } + } + + if (curarm->adt) { + if (arm->adt == NULL) { + /* no animdata, so just use a copy of the whole thing */ + arm->adt = BKE_animdata_copy(bmain, curarm->adt, 0); + } + else { + /* merge in data - we'll fix the drivers manually */ + BKE_animdata_merge_copy(bmain, &arm->id, &curarm->id, ADT_MERGECOPY_KEEP_DST, false); + } + } + + /* Free the old object data */ + ED_object_base_free_and_unlink(bmain, scene, ob_iter); + } + } + CTX_DATA_END; + + DEG_relations_tag_update(bmain); /* because we removed object(s) */ + + ED_armature_from_edit(bmain, arm); + ED_armature_edit_free(arm); + + DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); + WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene); + + return OPERATOR_FINISHED; } /* *********************************** Separate *********************************************** */ @@ -407,103 +404,103 @@ int join_armature_exec(bContext *C, wmOperator *op) /* Helper function for armature separating - link fixing */ static void separated_armature_fix_links(Main *bmain, Object *origArm, Object *newArm) { - Object *ob; - bPoseChannel *pchan; - bConstraint *con; - ListBase *opchans, *npchans; - - /* get reference to list of bones in original and new armatures */ - opchans = &origArm->pose->chanbase; - npchans = &newArm->pose->chanbase; - - /* let's go through all objects in database */ - for (ob = bmain->objects.first; ob; ob = ob->id.next) { - /* do some object-type specific things */ - if (ob->type == OB_ARMATURE) { - for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { - for (con = pchan->constraints.first; con; con = con->next) { - const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con); - ListBase targets = {NULL, NULL}; - bConstraintTarget *ct; - - /* constraint targets */ - if (cti && cti->get_constraint_targets) { - cti->get_constraint_targets(con, &targets); - - for (ct = targets.first; ct; ct = ct->next) { - /* any targets which point to original armature are redirected to the new one only if: - * - the target isn't origArm/newArm itself - * - the target is one that can be found in newArm/origArm - */ - if (ct->subtarget[0] != 0) { - if (ct->tar == origArm) { - if (BLI_findstring(npchans, ct->subtarget, offsetof(bPoseChannel, name))) { - ct->tar = newArm; - } - } - else if (ct->tar == newArm) { - if (BLI_findstring(opchans, ct->subtarget, offsetof(bPoseChannel, name))) { - ct->tar = origArm; - } - } - } - } - - if (cti->flush_constraint_targets) { - cti->flush_constraint_targets(con, &targets, 0); - } - } - } - } - } - - /* fix object-level constraints */ - if (ob != origArm) { - for (con = ob->constraints.first; con; con = con->next) { - const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con); - ListBase targets = {NULL, NULL}; - bConstraintTarget *ct; - - /* constraint targets */ - if (cti && cti->get_constraint_targets) { - cti->get_constraint_targets(con, &targets); - - for (ct = targets.first; ct; ct = ct->next) { - /* any targets which point to original armature are redirected to the new one only if: - * - the target isn't origArm/newArm itself - * - the target is one that can be found in newArm/origArm - */ - if (ct->subtarget[0] != '\0') { - if (ct->tar == origArm) { - if (BLI_findstring(npchans, ct->subtarget, offsetof(bPoseChannel, name))) { - ct->tar = newArm; - } - } - else if (ct->tar == newArm) { - if (BLI_findstring(opchans, ct->subtarget, offsetof(bPoseChannel, name))) { - ct->tar = origArm; - } - } - } - } - - if (cti->flush_constraint_targets) { - cti->flush_constraint_targets(con, &targets, 0); - } - } - } - } - - /* See if an object is parented to this armature */ - if (ob->parent && (ob->parent == origArm)) { - /* Is object parented to a bone of this src armature? */ - if ((ob->partype == PARBONE) && (ob->parsubstr[0] != '\0')) { - if (BLI_findstring(npchans, ob->parsubstr, offsetof(bPoseChannel, name))) { - ob->parent = newArm; - } - } - } - } + Object *ob; + bPoseChannel *pchan; + bConstraint *con; + ListBase *opchans, *npchans; + + /* get reference to list of bones in original and new armatures */ + opchans = &origArm->pose->chanbase; + npchans = &newArm->pose->chanbase; + + /* let's go through all objects in database */ + for (ob = bmain->objects.first; ob; ob = ob->id.next) { + /* do some object-type specific things */ + if (ob->type == OB_ARMATURE) { + for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { + for (con = pchan->constraints.first; con; con = con->next) { + const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con); + ListBase targets = {NULL, NULL}; + bConstraintTarget *ct; + + /* constraint targets */ + if (cti && cti->get_constraint_targets) { + cti->get_constraint_targets(con, &targets); + + for (ct = targets.first; ct; ct = ct->next) { + /* any targets which point to original armature are redirected to the new one only if: + * - the target isn't origArm/newArm itself + * - the target is one that can be found in newArm/origArm + */ + if (ct->subtarget[0] != 0) { + if (ct->tar == origArm) { + if (BLI_findstring(npchans, ct->subtarget, offsetof(bPoseChannel, name))) { + ct->tar = newArm; + } + } + else if (ct->tar == newArm) { + if (BLI_findstring(opchans, ct->subtarget, offsetof(bPoseChannel, name))) { + ct->tar = origArm; + } + } + } + } + + if (cti->flush_constraint_targets) { + cti->flush_constraint_targets(con, &targets, 0); + } + } + } + } + } + + /* fix object-level constraints */ + if (ob != origArm) { + for (con = ob->constraints.first; con; con = con->next) { + const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con); + ListBase targets = {NULL, NULL}; + bConstraintTarget *ct; + + /* constraint targets */ + if (cti && cti->get_constraint_targets) { + cti->get_constraint_targets(con, &targets); + + for (ct = targets.first; ct; ct = ct->next) { + /* any targets which point to original armature are redirected to the new one only if: + * - the target isn't origArm/newArm itself + * - the target is one that can be found in newArm/origArm + */ + if (ct->subtarget[0] != '\0') { + if (ct->tar == origArm) { + if (BLI_findstring(npchans, ct->subtarget, offsetof(bPoseChannel, name))) { + ct->tar = newArm; + } + } + else if (ct->tar == newArm) { + if (BLI_findstring(opchans, ct->subtarget, offsetof(bPoseChannel, name))) { + ct->tar = origArm; + } + } + } + } + + if (cti->flush_constraint_targets) { + cti->flush_constraint_targets(con, &targets, 0); + } + } + } + } + + /* See if an object is parented to this armature */ + if (ob->parent && (ob->parent == origArm)) { + /* Is object parented to a bone of this src armature? */ + if ((ob->partype == PARBONE) && (ob->parsubstr[0] != '\0')) { + if (BLI_findstring(npchans, ob->parsubstr, offsetof(bPoseChannel, name))) { + ob->parent = newArm; + } + } + } + } } /* Helper function for armature separating - remove certain bones from the given armature @@ -512,429 +509,432 @@ static void separated_armature_fix_links(Main *bmain, Object *origArm, Object *n */ static void separate_armature_bones(Main *bmain, Object *ob, short sel) { - bArmature *arm = (bArmature *)ob->data; - bPoseChannel *pchan, *pchann; - EditBone *curbone; - - /* make local set of editbones to manipulate here */ - ED_armature_to_edit(arm); - - /* go through pose-channels, checking if a bone should be removed */ - for (pchan = ob->pose->chanbase.first; pchan; pchan = pchann) { - pchann = pchan->next; - curbone = ED_armature_ebone_find_name(arm->edbo, pchan->name); - - /* check if bone needs to be removed */ - if ( (sel && (curbone->flag & BONE_SELECTED)) || - (!sel && !(curbone->flag & BONE_SELECTED)) ) - { - EditBone *ebo; - bPoseChannel *pchn; - - /* clear the bone->parent var of any bone that had this as its parent */ - for (ebo = arm->edbo->first; ebo; ebo = ebo->next) { - if (ebo->parent == curbone) { - ebo->parent = NULL; - /* this is needed to prevent random crashes with in ED_armature_from_edit */ - ebo->temp.p = NULL; - ebo->flag &= ~BONE_CONNECTED; - } - } - - /* clear the pchan->parent var of any pchan that had this as its parent */ - for (pchn = ob->pose->chanbase.first; pchn; pchn = pchn->next) { - if (pchn->parent == pchan) { - pchn->parent = NULL; - } - if (pchn->bbone_next == pchan) { - pchn->bbone_next = NULL; - } - if (pchn->bbone_prev == pchan) { - pchn->bbone_prev = NULL; - } - } - - /* free any of the extra-data this pchan might have */ - BKE_pose_channel_free(pchan); - BKE_pose_channels_hash_free(ob->pose); - - /* get rid of unneeded bone */ - bone_free(arm, curbone); - BLI_freelinkN(&ob->pose->chanbase, pchan); - } - } - - /* exit editmode (recalculates pchans too) */ - ED_armature_from_edit(bmain, ob->data); - ED_armature_edit_free(ob->data); + bArmature *arm = (bArmature *)ob->data; + bPoseChannel *pchan, *pchann; + EditBone *curbone; + + /* make local set of editbones to manipulate here */ + ED_armature_to_edit(arm); + + /* go through pose-channels, checking if a bone should be removed */ + for (pchan = ob->pose->chanbase.first; pchan; pchan = pchann) { + pchann = pchan->next; + curbone = ED_armature_ebone_find_name(arm->edbo, pchan->name); + + /* check if bone needs to be removed */ + if ((sel && (curbone->flag & BONE_SELECTED)) || (!sel && !(curbone->flag & BONE_SELECTED))) { + EditBone *ebo; + bPoseChannel *pchn; + + /* clear the bone->parent var of any bone that had this as its parent */ + for (ebo = arm->edbo->first; ebo; ebo = ebo->next) { + if (ebo->parent == curbone) { + ebo->parent = NULL; + /* this is needed to prevent random crashes with in ED_armature_from_edit */ + ebo->temp.p = NULL; + ebo->flag &= ~BONE_CONNECTED; + } + } + + /* clear the pchan->parent var of any pchan that had this as its parent */ + for (pchn = ob->pose->chanbase.first; pchn; pchn = pchn->next) { + if (pchn->parent == pchan) { + pchn->parent = NULL; + } + if (pchn->bbone_next == pchan) { + pchn->bbone_next = NULL; + } + if (pchn->bbone_prev == pchan) { + pchn->bbone_prev = NULL; + } + } + + /* free any of the extra-data this pchan might have */ + BKE_pose_channel_free(pchan); + BKE_pose_channels_hash_free(ob->pose); + + /* get rid of unneeded bone */ + bone_free(arm, curbone); + BLI_freelinkN(&ob->pose->chanbase, pchan); + } + } + + /* exit editmode (recalculates pchans too) */ + ED_armature_from_edit(bmain, ob->data); + ED_armature_edit_free(ob->data); } /* separate selected bones into their armature */ static int separate_armature_exec(bContext *C, wmOperator *op) { - Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); - ViewLayer *view_layer = CTX_data_view_layer(C); - bool ok = false; + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + bool ok = false; - /* set wait cursor in case this takes a while */ - WM_cursor_wait(1); + /* set wait cursor in case this takes a while */ + WM_cursor_wait(1); - uint bases_len = 0; - Base **bases = BKE_view_layer_array_from_bases_in_edit_mode_unique_data(view_layer, CTX_wm_view3d(C), &bases_len); + uint bases_len = 0; + Base **bases = BKE_view_layer_array_from_bases_in_edit_mode_unique_data( + view_layer, CTX_wm_view3d(C), &bases_len); - CTX_DATA_BEGIN(C, Base *, base, visible_bases) - { - ED_object_base_select(base, BA_DESELECT); - } - CTX_DATA_END; + CTX_DATA_BEGIN (C, Base *, base, visible_bases) { + ED_object_base_select(base, BA_DESELECT); + } + CTX_DATA_END; - for (uint base_index = 0; base_index < bases_len; base_index++) { - Base *base_iter = bases[base_index]; - Object *obedit = base_iter->object; + for (uint base_index = 0; base_index < bases_len; base_index++) { + Base *base_iter = bases[base_index]; + Object *obedit = base_iter->object; - Object *oldob, *newob; - Base *oldbase, *newbase; + Object *oldob, *newob; + Base *oldbase, *newbase; - /* we are going to do this as follows (unlike every other instance of separate): - * 1. exit editmode +posemode for active armature/base. Take note of what this is. - * 2. duplicate base - BASACT is the new one now - * 3. for each of the two armatures, enter editmode -> remove appropriate bones -> exit editmode + recalc - * 4. fix constraint links - * 5. make original armature active and enter editmode - */ + /* we are going to do this as follows (unlike every other instance of separate): + * 1. exit editmode +posemode for active armature/base. Take note of what this is. + * 2. duplicate base - BASACT is the new one now + * 3. for each of the two armatures, enter editmode -> remove appropriate bones -> exit editmode + recalc + * 4. fix constraint links + * 5. make original armature active and enter editmode + */ - /* 1) only edit-base selected */ - ED_object_base_select(base_iter, BA_SELECT); + /* 1) only edit-base selected */ + ED_object_base_select(base_iter, BA_SELECT); - /* 1) store starting settings and exit editmode */ - oldob = obedit; - oldbase = base_iter; - oldob->mode &= ~OB_MODE_POSE; - //oldbase->flag &= ~OB_POSEMODE; + /* 1) store starting settings and exit editmode */ + oldob = obedit; + oldbase = base_iter; + oldob->mode &= ~OB_MODE_POSE; + //oldbase->flag &= ~OB_POSEMODE; - ED_armature_from_edit(bmain, obedit->data); - ED_armature_edit_free(obedit->data); + ED_armature_from_edit(bmain, obedit->data); + ED_armature_edit_free(obedit->data); - /* 2) duplicate base */ + /* 2) duplicate base */ - /* only duplicate linked armature */ - newbase = ED_object_add_duplicate(bmain, scene, view_layer, oldbase, USER_DUP_ARM); + /* only duplicate linked armature */ + newbase = ED_object_add_duplicate(bmain, scene, view_layer, oldbase, USER_DUP_ARM); - DEG_relations_tag_update(bmain); + DEG_relations_tag_update(bmain); - newob = newbase->object; - newbase->flag &= ~BASE_SELECTED; + newob = newbase->object; + newbase->flag &= ~BASE_SELECTED; + /* 3) remove bones that shouldn't still be around on both armatures */ + separate_armature_bones(bmain, oldob, 1); + separate_armature_bones(bmain, newob, 0); - /* 3) remove bones that shouldn't still be around on both armatures */ - separate_armature_bones(bmain, oldob, 1); - separate_armature_bones(bmain, newob, 0); + /* 4) fix links before depsgraph flushes */ // err... or after? + separated_armature_fix_links(bmain, oldob, newob); + DEG_id_tag_update(&oldob->id, ID_RECALC_GEOMETRY); /* this is the original one */ + DEG_id_tag_update(&newob->id, ID_RECALC_GEOMETRY); /* this is the separated one */ - /* 4) fix links before depsgraph flushes */ // err... or after? - separated_armature_fix_links(bmain, oldob, newob); + /* 5) restore original conditions */ + obedit = oldob; - DEG_id_tag_update(&oldob->id, ID_RECALC_GEOMETRY); /* this is the original one */ - DEG_id_tag_update(&newob->id, ID_RECALC_GEOMETRY); /* this is the separated one */ + ED_armature_to_edit(obedit->data); + /* parents tips remain selected when connected children are removed. */ + ED_armature_edit_deselect_all(obedit); - /* 5) restore original conditions */ - obedit = oldob; + ok = true; - ED_armature_to_edit(obedit->data); + /* note, notifier might evolve */ + WM_event_add_notifier(C, NC_OBJECT | ND_POSE, obedit); + } + MEM_freeN(bases); - /* parents tips remain selected when connected children are removed. */ - ED_armature_edit_deselect_all(obedit); + /* recalc/redraw + cleanup */ + WM_cursor_wait(0); - ok = true; + if (ok) { + BKE_report(op->reports, RPT_INFO, "Separated bones"); + } - /* note, notifier might evolve */ - WM_event_add_notifier(C, NC_OBJECT | ND_POSE, obedit); - } - MEM_freeN(bases); - - /* recalc/redraw + cleanup */ - WM_cursor_wait(0); - - if (ok) { - BKE_report(op->reports, RPT_INFO, "Separated bones"); - } - - return OPERATOR_FINISHED; + return OPERATOR_FINISHED; } void ARMATURE_OT_separate(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Separate Bones"; - ot->idname = "ARMATURE_OT_separate"; - ot->description = "Isolate selected bones into a separate armature"; - - /* callbacks */ - ot->invoke = WM_operator_confirm; - ot->exec = separate_armature_exec; - ot->poll = ED_operator_editarmature; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* identifiers */ + ot->name = "Separate Bones"; + ot->idname = "ARMATURE_OT_separate"; + ot->description = "Isolate selected bones into a separate armature"; + + /* callbacks */ + ot->invoke = WM_operator_confirm; + ot->exec = separate_armature_exec; + ot->poll = ED_operator_editarmature; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } /* ******************************************** Parenting ************************************************* */ /* armature parenting options */ #define ARM_PAR_CONNECT 1 -#define ARM_PAR_OFFSET 2 - +#define ARM_PAR_OFFSET 2 /* check for null, before calling! */ static void bone_connect_to_existing_parent(EditBone *bone) { - bone->flag |= BONE_CONNECTED; - copy_v3_v3(bone->head, bone->parent->tail); - bone->rad_head = bone->parent->rad_tail; + bone->flag |= BONE_CONNECTED; + copy_v3_v3(bone->head, bone->parent->tail); + bone->rad_head = bone->parent->rad_tail; } -static void bone_connect_to_new_parent(ListBase *edbo, EditBone *selbone, EditBone *actbone, short mode) +static void bone_connect_to_new_parent(ListBase *edbo, + EditBone *selbone, + EditBone *actbone, + short mode) { - EditBone *ebone; - float offset[3]; - - if ((selbone->parent) && (selbone->flag & BONE_CONNECTED)) - selbone->parent->flag &= ~(BONE_TIPSEL); - - /* make actbone the parent of selbone */ - selbone->parent = actbone; - - /* in actbone tree we cannot have a loop */ - for (ebone = actbone->parent; ebone; ebone = ebone->parent) { - if (ebone->parent == selbone) { - ebone->parent = NULL; - ebone->flag &= ~BONE_CONNECTED; - } - } - - if (mode == ARM_PAR_CONNECT) { - /* Connected: Child bones will be moved to the parent tip */ - selbone->flag |= BONE_CONNECTED; - sub_v3_v3v3(offset, actbone->tail, selbone->head); - - copy_v3_v3(selbone->head, actbone->tail); - selbone->rad_head = actbone->rad_tail; - - add_v3_v3(selbone->tail, offset); - - /* offset for all its children */ - for (ebone = edbo->first; ebone; ebone = ebone->next) { - EditBone *par; - - for (par = ebone->parent; par; par = par->parent) { - if (par == selbone) { - add_v3_v3(ebone->head, offset); - add_v3_v3(ebone->tail, offset); - break; - } - } - } - } - else { - /* Offset: Child bones will retain their distance from the parent tip */ - selbone->flag &= ~BONE_CONNECTED; - } + EditBone *ebone; + float offset[3]; + + if ((selbone->parent) && (selbone->flag & BONE_CONNECTED)) + selbone->parent->flag &= ~(BONE_TIPSEL); + + /* make actbone the parent of selbone */ + selbone->parent = actbone; + + /* in actbone tree we cannot have a loop */ + for (ebone = actbone->parent; ebone; ebone = ebone->parent) { + if (ebone->parent == selbone) { + ebone->parent = NULL; + ebone->flag &= ~BONE_CONNECTED; + } + } + + if (mode == ARM_PAR_CONNECT) { + /* Connected: Child bones will be moved to the parent tip */ + selbone->flag |= BONE_CONNECTED; + sub_v3_v3v3(offset, actbone->tail, selbone->head); + + copy_v3_v3(selbone->head, actbone->tail); + selbone->rad_head = actbone->rad_tail; + + add_v3_v3(selbone->tail, offset); + + /* offset for all its children */ + for (ebone = edbo->first; ebone; ebone = ebone->next) { + EditBone *par; + + for (par = ebone->parent; par; par = par->parent) { + if (par == selbone) { + add_v3_v3(ebone->head, offset); + add_v3_v3(ebone->tail, offset); + break; + } + } + } + } + else { + /* Offset: Child bones will retain their distance from the parent tip */ + selbone->flag &= ~BONE_CONNECTED; + } } - static const EnumPropertyItem prop_editarm_make_parent_types[] = { - {ARM_PAR_CONNECT, "CONNECTED", 0, "Connected", ""}, - {ARM_PAR_OFFSET, "OFFSET", 0, "Keep Offset", ""}, - {0, NULL, 0, NULL, NULL}, + {ARM_PAR_CONNECT, "CONNECTED", 0, "Connected", ""}, + {ARM_PAR_OFFSET, "OFFSET", 0, "Keep Offset", ""}, + {0, NULL, 0, NULL, NULL}, }; static int armature_parent_set_exec(bContext *C, wmOperator *op) { - Object *ob = CTX_data_edit_object(C); - bArmature *arm = (bArmature *)ob->data; - EditBone *actbone = CTX_data_active_bone(C); - EditBone *actmirb = NULL; - short val = RNA_enum_get(op->ptr, "type"); - - /* there must be an active bone */ - if (actbone == NULL) { - BKE_report(op->reports, RPT_ERROR, "Operation requires an active bone"); - return OPERATOR_CANCELLED; - } - else if (arm->flag & ARM_MIRROR_EDIT) { - /* For X-Axis Mirror Editing option, we may need a mirror copy of actbone - * - if there's a mirrored copy of selbone, try to find a mirrored copy of actbone - * (i.e. selbone="child.L" and actbone="parent.L", find "child.R" and "parent.R"). - * This is useful for arm-chains, for example parenting lower arm to upper arm - * - if there's no mirrored copy of actbone (i.e. actbone = "parent.C" or "parent") - * then just use actbone. Useful when doing upper arm to spine. - */ - actmirb = ED_armature_ebone_get_mirrored(arm->edbo, actbone); - if (actmirb == NULL) - actmirb = actbone; - } - - /* if there is only 1 selected bone, we assume that that is the active bone, - * since a user will need to have clicked on a bone (thus selecting it) to make it active - */ - if (CTX_DATA_COUNT(C, selected_editable_bones) <= 1) { - /* When only the active bone is selected, and it has a parent, - * connect it to the parent, as that is the only possible outcome. - */ - if (actbone->parent) { - bone_connect_to_existing_parent(actbone); - - if ((arm->flag & ARM_MIRROR_EDIT) && (actmirb->parent)) - bone_connect_to_existing_parent(actmirb); - } - } - else { - /* Parent 'selected' bones to the active one - * - the context iterator contains both selected bones and their mirrored copies, - * so we assume that unselected bones are mirrored copies of some selected bone - * - since the active one (and/or its mirror) will also be selected, we also need - * to check that we are not trying to operate on them, since such an operation - * would cause errors - */ - - /* parent selected bones to the active one */ - CTX_DATA_BEGIN(C, EditBone *, ebone, selected_editable_bones) - { - if (ELEM(ebone, actbone, actmirb) == 0) { - if (ebone->flag & BONE_SELECTED) - bone_connect_to_new_parent(arm->edbo, ebone, actbone, val); - else - bone_connect_to_new_parent(arm->edbo, ebone, actmirb, val); - } - } - CTX_DATA_END; - } - - - /* note, notifier might evolve */ - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); - - return OPERATOR_FINISHED; + Object *ob = CTX_data_edit_object(C); + bArmature *arm = (bArmature *)ob->data; + EditBone *actbone = CTX_data_active_bone(C); + EditBone *actmirb = NULL; + short val = RNA_enum_get(op->ptr, "type"); + + /* there must be an active bone */ + if (actbone == NULL) { + BKE_report(op->reports, RPT_ERROR, "Operation requires an active bone"); + return OPERATOR_CANCELLED; + } + else if (arm->flag & ARM_MIRROR_EDIT) { + /* For X-Axis Mirror Editing option, we may need a mirror copy of actbone + * - if there's a mirrored copy of selbone, try to find a mirrored copy of actbone + * (i.e. selbone="child.L" and actbone="parent.L", find "child.R" and "parent.R"). + * This is useful for arm-chains, for example parenting lower arm to upper arm + * - if there's no mirrored copy of actbone (i.e. actbone = "parent.C" or "parent") + * then just use actbone. Useful when doing upper arm to spine. + */ + actmirb = ED_armature_ebone_get_mirrored(arm->edbo, actbone); + if (actmirb == NULL) + actmirb = actbone; + } + + /* if there is only 1 selected bone, we assume that that is the active bone, + * since a user will need to have clicked on a bone (thus selecting it) to make it active + */ + if (CTX_DATA_COUNT(C, selected_editable_bones) <= 1) { + /* When only the active bone is selected, and it has a parent, + * connect it to the parent, as that is the only possible outcome. + */ + if (actbone->parent) { + bone_connect_to_existing_parent(actbone); + + if ((arm->flag & ARM_MIRROR_EDIT) && (actmirb->parent)) + bone_connect_to_existing_parent(actmirb); + } + } + else { + /* Parent 'selected' bones to the active one + * - the context iterator contains both selected bones and their mirrored copies, + * so we assume that unselected bones are mirrored copies of some selected bone + * - since the active one (and/or its mirror) will also be selected, we also need + * to check that we are not trying to operate on them, since such an operation + * would cause errors + */ + + /* parent selected bones to the active one */ + CTX_DATA_BEGIN (C, EditBone *, ebone, selected_editable_bones) { + if (ELEM(ebone, actbone, actmirb) == 0) { + if (ebone->flag & BONE_SELECTED) + bone_connect_to_new_parent(arm->edbo, ebone, actbone, val); + else + bone_connect_to_new_parent(arm->edbo, ebone, actmirb, val); + } + } + CTX_DATA_END; + } + + /* note, notifier might evolve */ + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); + + return OPERATOR_FINISHED; } -static int armature_parent_set_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *UNUSED(event)) +static int armature_parent_set_invoke(bContext *C, + wmOperator *UNUSED(op), + const wmEvent *UNUSED(event)) { - EditBone *actbone = CTX_data_active_bone(C); - uiPopupMenu *pup = UI_popup_menu_begin(C, CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Make Parent"), ICON_NONE); - uiLayout *layout = UI_popup_menu_layout(pup); - int allchildbones = 0; + EditBone *actbone = CTX_data_active_bone(C); + uiPopupMenu *pup = UI_popup_menu_begin( + C, CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Make Parent"), ICON_NONE); + uiLayout *layout = UI_popup_menu_layout(pup); + int allchildbones = 0; - CTX_DATA_BEGIN(C, EditBone *, ebone, selected_editable_bones) - { - if (ebone != actbone) { - if (ebone->parent != actbone) allchildbones = 1; - } - } - CTX_DATA_END; + CTX_DATA_BEGIN (C, EditBone *, ebone, selected_editable_bones) { + if (ebone != actbone) { + if (ebone->parent != actbone) + allchildbones = 1; + } + } + CTX_DATA_END; - uiItemEnumO(layout, "ARMATURE_OT_parent_set", NULL, 0, "type", ARM_PAR_CONNECT); + uiItemEnumO(layout, "ARMATURE_OT_parent_set", NULL, 0, "type", ARM_PAR_CONNECT); - /* ob becomes parent, make the associated menus */ - if (allchildbones) - uiItemEnumO(layout, "ARMATURE_OT_parent_set", NULL, 0, "type", ARM_PAR_OFFSET); + /* ob becomes parent, make the associated menus */ + if (allchildbones) + uiItemEnumO(layout, "ARMATURE_OT_parent_set", NULL, 0, "type", ARM_PAR_OFFSET); - UI_popup_menu_end(C, pup); + UI_popup_menu_end(C, pup); - return OPERATOR_INTERFACE; + return OPERATOR_INTERFACE; } void ARMATURE_OT_parent_set(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Make Parent"; - ot->idname = "ARMATURE_OT_parent_set"; - ot->description = "Set the active bone as the parent of the selected bones"; + /* identifiers */ + ot->name = "Make Parent"; + ot->idname = "ARMATURE_OT_parent_set"; + ot->description = "Set the active bone as the parent of the selected bones"; - /* api callbacks */ - ot->invoke = armature_parent_set_invoke; - ot->exec = armature_parent_set_exec; - ot->poll = ED_operator_editarmature; + /* api callbacks */ + ot->invoke = armature_parent_set_invoke; + ot->exec = armature_parent_set_exec; + ot->poll = ED_operator_editarmature; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - RNA_def_enum(ot->srna, "type", prop_editarm_make_parent_types, 0, "ParentType", "Type of parenting"); + RNA_def_enum( + ot->srna, "type", prop_editarm_make_parent_types, 0, "ParentType", "Type of parenting"); } - - static const EnumPropertyItem prop_editarm_clear_parent_types[] = { - {1, "CLEAR", 0, "Clear Parent", ""}, - {2, "DISCONNECT", 0, "Disconnect Bone", ""}, - {0, NULL, 0, NULL, NULL}, + {1, "CLEAR", 0, "Clear Parent", ""}, + {2, "DISCONNECT", 0, "Disconnect Bone", ""}, + {0, NULL, 0, NULL, NULL}, }; static void editbone_clear_parent(EditBone *ebone, int mode) { - if (ebone->parent) { - /* for nice selection */ - ebone->parent->flag &= ~(BONE_TIPSEL); - } - - if (mode == 1) ebone->parent = NULL; - ebone->flag &= ~BONE_CONNECTED; + if (ebone->parent) { + /* for nice selection */ + ebone->parent->flag &= ~(BONE_TIPSEL); + } + + if (mode == 1) + ebone->parent = NULL; + ebone->flag &= ~BONE_CONNECTED; } static int armature_parent_clear_exec(bContext *C, wmOperator *op) { - ViewLayer *view_layer = CTX_data_view_layer(C); - const int val = RNA_enum_get(op->ptr, "type"); - - CTX_DATA_BEGIN(C, EditBone *, ebone, selected_editable_bones) - { - editbone_clear_parent(ebone, val); - } - CTX_DATA_END; - - 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 changed = false; - - for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { - if (EBONE_EDITABLE(ebone)) { - changed = true; - break; - } - } - - if (!changed) { - continue; - } - - ED_armature_edit_sync_selection(arm->edbo); - - /* Note, notifier might evolve. */ - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); - } - MEM_freeN(objects); - - return OPERATOR_FINISHED; + ViewLayer *view_layer = CTX_data_view_layer(C); + const int val = RNA_enum_get(op->ptr, "type"); + + CTX_DATA_BEGIN (C, EditBone *, ebone, selected_editable_bones) { + editbone_clear_parent(ebone, val); + } + CTX_DATA_END; + + 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 changed = false; + + for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { + if (EBONE_EDITABLE(ebone)) { + changed = true; + break; + } + } + + if (!changed) { + continue; + } + + ED_armature_edit_sync_selection(arm->edbo); + + /* Note, notifier might evolve. */ + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); + } + MEM_freeN(objects); + + return OPERATOR_FINISHED; } void ARMATURE_OT_parent_clear(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Clear Parent"; - ot->idname = "ARMATURE_OT_parent_clear"; - ot->description = "Remove the parent-child relationship between selected bones and their parents"; - - /* api callbacks */ - ot->invoke = WM_menu_invoke; - ot->exec = armature_parent_clear_exec; - ot->poll = ED_operator_editarmature; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - ot->prop = RNA_def_enum(ot->srna, "type", prop_editarm_clear_parent_types, 0, "ClearType", "What way to clear parenting"); + /* identifiers */ + ot->name = "Clear Parent"; + ot->idname = "ARMATURE_OT_parent_clear"; + ot->description = + "Remove the parent-child relationship between selected bones and their parents"; + + /* api callbacks */ + ot->invoke = WM_menu_invoke; + ot->exec = armature_parent_clear_exec; + ot->poll = ED_operator_editarmature; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + ot->prop = RNA_def_enum(ot->srna, + "type", + prop_editarm_clear_parent_types, + 0, + "ClearType", + "What way to clear parenting"); } diff --git a/source/blender/editors/armature/armature_select.c b/source/blender/editors/armature/armature_select.c index 4aab80bcbdd..0c493672d9e 100644 --- a/source/blender/editors/armature/armature_select.c +++ b/source/blender/editors/armature/armature_select.c @@ -60,695 +60,713 @@ /* **************** PoseMode & EditMode Selection Buffer Queries *************************** */ -Base *ED_armature_base_and_ebone_from_select_buffer( - Base **bases, uint bases_len, int hit, EditBone **r_ebone) +Base *ED_armature_base_and_ebone_from_select_buffer(Base **bases, + uint bases_len, + int hit, + EditBone **r_ebone) { - const uint hit_object = hit & 0xFFFF; - Base *base = NULL; - EditBone *ebone = NULL; - /* TODO(campbell): optimize, eg: sort & binary search. */ - for (uint base_index = 0; base_index < bases_len; base_index++) { - if (bases[base_index]->object->select_id == hit_object) { - base = bases[base_index]; - break; - } - } - if (base != NULL) { - const uint hit_bone = (hit & ~BONESEL_ANY) >> 16; - bArmature *arm = base->object->data; - ebone = BLI_findlink(arm->edbo, hit_bone); - } - *r_ebone = ebone; - return base; + const uint hit_object = hit & 0xFFFF; + Base *base = NULL; + EditBone *ebone = NULL; + /* TODO(campbell): optimize, eg: sort & binary search. */ + for (uint base_index = 0; base_index < bases_len; base_index++) { + if (bases[base_index]->object->select_id == hit_object) { + base = bases[base_index]; + break; + } + } + if (base != NULL) { + const uint hit_bone = (hit & ~BONESEL_ANY) >> 16; + bArmature *arm = base->object->data; + ebone = BLI_findlink(arm->edbo, hit_bone); + } + *r_ebone = ebone; + return base; } -Object *ED_armature_object_and_ebone_from_select_buffer( - Object **objects, uint objects_len, int hit, EditBone **r_ebone) +Object *ED_armature_object_and_ebone_from_select_buffer(Object **objects, + uint objects_len, + int hit, + EditBone **r_ebone) { - const uint hit_object = hit & 0xFFFF; - Object *ob = NULL; - EditBone *ebone = NULL; - /* TODO(campbell): optimize, eg: sort & binary search. */ - for (uint ob_index = 0; ob_index < objects_len; ob_index++) { - if (objects[ob_index]->select_id == hit_object) { - ob = objects[ob_index]; - break; - } - } - if (ob != NULL) { - const uint hit_bone = (hit & ~BONESEL_ANY) >> 16; - bArmature *arm = ob->data; - ebone = BLI_findlink(arm->edbo, hit_bone); - } - *r_ebone = ebone; - return ob; + const uint hit_object = hit & 0xFFFF; + Object *ob = NULL; + EditBone *ebone = NULL; + /* TODO(campbell): optimize, eg: sort & binary search. */ + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + if (objects[ob_index]->select_id == hit_object) { + ob = objects[ob_index]; + break; + } + } + if (ob != NULL) { + const uint hit_bone = (hit & ~BONESEL_ANY) >> 16; + bArmature *arm = ob->data; + ebone = BLI_findlink(arm->edbo, hit_bone); + } + *r_ebone = ebone; + 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_bone_from_select_buffer(Base **bases, + uint bases_len, + int hit, + Bone **r_bone) { - const uint hit_object = hit & 0xFFFF; - Base *base = NULL; - Bone *bone = NULL; - /* TODO(campbell): optimize, eg: sort & binary search. */ - for (uint base_index = 0; base_index < bases_len; base_index++) { - if (bases[base_index]->object->select_id == hit_object) { - base = bases[base_index]; - break; - } - } - 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; - } - } - *r_bone = bone; - return base; + const uint hit_object = hit & 0xFFFF; + Base *base = NULL; + Bone *bone = NULL; + /* TODO(campbell): optimize, eg: sort & binary search. */ + for (uint base_index = 0; base_index < bases_len; base_index++) { + if (bases[base_index]->object->select_id == hit_object) { + base = bases[base_index]; + break; + } + } + 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; + } + } + *r_bone = bone; + return base; } /* 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) +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) { - Bone *bone; - EditBone *ebone; - void *firstunSel = NULL, *firstSel = NULL, *data; - Base *firstunSel_base = NULL, *firstSel_base = NULL; - unsigned int hitresult; - short i; - bool takeNext = false; - int minsel = 0xffffffff, minunsel = 0xffffffff; - - 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; - - 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; - } - else { - 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 { - 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 (!firstunSel) { - firstunSel = data; - firstunSel_base = base; - } - if (takeNext) { - *r_base = base; - return data; - } - } - } - } - } - } - } - - if (firstunSel) { - *r_base = firstunSel_base; - return firstunSel; - } - else { - *r_base = firstSel_base; - return firstSel; - } + Bone *bone; + EditBone *ebone; + void *firstunSel = NULL, *firstSel = NULL, *data; + Base *firstunSel_base = NULL, *firstSel_base = NULL; + unsigned int hitresult; + short i; + bool takeNext = false; + int minsel = 0xffffffff, minunsel = 0xffffffff; + + 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; + + 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; + } + else { + 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 { + 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 (!firstunSel) { + firstunSel = data; + firstunSel_base = base; + } + if (takeNext) { + *r_base = base; + return data; + } + } + } + } + } + } + } + + if (firstunSel) { + *r_base = firstunSel_base; + return firstunSel; + } + else { + *r_base = firstSel_base; + return firstSel; + } } /* 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) +void *get_nearest_bone(bContext *C, const int xy[2], bool findunsel, Base **r_base) { - ViewContext vc; - rcti rect; - unsigned int buffer[MAXPICKBUF]; - short hits; - - ED_view3d_viewcontext_init(C, &vc); - - // rect.xmin = ... mouseco! - rect.xmin = rect.xmax = xy[0]; - rect.ymin = rect.ymax = xy[1]; - - hits = view3d_opengl_select( - &vc, buffer, MAXPICKBUF, &rect, - VIEW3D_SELECT_PICK_NEAREST, VIEW3D_SELECT_FILTER_NOP); - - *r_base = NULL; - - if (hits > 0) { - uint bases_len = 0; - Base **bases; - - if (vc.obedit != NULL) { - bases = BKE_view_layer_array_from_bases_in_mode( - vc.view_layer, vc.v3d, &bases_len, { - .object_mode = OB_MODE_EDIT, - }); - } - else { - 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); - - MEM_freeN(bases); - return bone; - } - return NULL; + ViewContext vc; + rcti rect; + unsigned int buffer[MAXPICKBUF]; + short hits; + + ED_view3d_viewcontext_init(C, &vc); + + // rect.xmin = ... mouseco! + rect.xmin = rect.xmax = xy[0]; + rect.ymin = rect.ymax = xy[1]; + + hits = view3d_opengl_select( + &vc, buffer, MAXPICKBUF, &rect, VIEW3D_SELECT_PICK_NEAREST, VIEW3D_SELECT_FILTER_NOP); + + *r_base = NULL; + + if (hits > 0) { + uint bases_len = 0; + Base **bases; + + if (vc.obedit != NULL) { + bases = BKE_view_layer_array_from_bases_in_mode(vc.view_layer, + vc.v3d, + &bases_len, + { + .object_mode = OB_MODE_EDIT, + }); + } + else { + 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); + + MEM_freeN(bases); + return bone; + } + return NULL; } /* **************** EditMode stuff ********************** */ static int armature_select_linked_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - bArmature *arm; - EditBone *bone, *curBone, *next; - const bool sel = !RNA_boolean_get(op->ptr, "deselect"); - - view3d_operator_needs_opengl(C); - - Base *base = NULL; - bone = get_nearest_bone(C, event->mval, true, &base); - - if (!bone) - return OPERATOR_CANCELLED; - - arm = base->object->data; - - /* Select parents */ - for (curBone = bone; curBone; curBone = next) { - if ((curBone->flag & BONE_UNSELECTABLE) == 0) { - if (sel) { - curBone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - } - else { - curBone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - } - } - - if (curBone->flag & BONE_CONNECTED) - next = curBone->parent; - else - next = NULL; - } - - /* 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; - } - } - } - if (!curBone) - bone = NULL; - } - - ED_armature_edit_sync_selection(arm->edbo); - - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, base->object); - - return OPERATOR_FINISHED; + bArmature *arm; + EditBone *bone, *curBone, *next; + const bool sel = !RNA_boolean_get(op->ptr, "deselect"); + + view3d_operator_needs_opengl(C); + + Base *base = NULL; + bone = get_nearest_bone(C, event->mval, true, &base); + + if (!bone) + return OPERATOR_CANCELLED; + + arm = base->object->data; + + /* Select parents */ + for (curBone = bone; curBone; curBone = next) { + if ((curBone->flag & BONE_UNSELECTABLE) == 0) { + if (sel) { + curBone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + } + else { + curBone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + } + } + + if (curBone->flag & BONE_CONNECTED) + next = curBone->parent; + else + next = NULL; + } + + /* 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; + } + } + } + if (!curBone) + bone = NULL; + } + + ED_armature_edit_sync_selection(arm->edbo); + + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, base->object); + + return OPERATOR_FINISHED; } static bool armature_select_linked_poll(bContext *C) { - return (ED_operator_view3d_active(C) && ED_operator_editarmature(C)); + return (ED_operator_view3d_active(C) && ED_operator_editarmature(C)); } void ARMATURE_OT_select_linked(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"; + /* identifiers */ + ot->name = "Select Connected"; + ot->idname = "ARMATURE_OT_select_linked"; + ot->description = "Select bones related to selected ones by parent/child relationships"; - /* api callbacks */ - /* leave 'exec' unset */ - ot->invoke = armature_select_linked_invoke; - ot->poll = armature_select_linked_poll; + /* api callbacks */ + /* leave 'exec' unset */ + ot->invoke = armature_select_linked_invoke; + ot->poll = armature_select_linked_poll; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", ""); + RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", ""); } /* utility function for get_nearest_editbonepoint */ static int selectbuffer_ret_hits_12(unsigned int *UNUSED(buffer), const int hits12) { - return hits12; + return hits12; } static int selectbuffer_ret_hits_5(unsigned int *buffer, const int hits12, const int hits5) { - const int offs = 4 * hits12; - memcpy(buffer, buffer + offs, 4 * hits5 * sizeof(unsigned int)); - return hits5; + const int offs = 4 * hits12; + memcpy(buffer, buffer + offs, 4 * hits5 * sizeof(unsigned int)); + return hits5; } /* does bones and points */ /* note that BONE ROOT only gets drawn for root bones (or without IK) */ static EditBone *get_nearest_editbonepoint( - ViewContext *vc, - bool findunsel, bool use_cycle, - Base **r_base, int *r_selmask) + ViewContext *vc, bool findunsel, bool use_cycle, Base **r_base, int *r_selmask) { - uint buffer[MAXPICKBUF]; - struct { - uint hitresult; - Base *base; - EditBone *ebone; - } best = { - .hitresult = BONESEL_NOSEL, - .base = NULL, - .ebone = NULL, - }; - - /* find the bone after the current active bone, so as to bump up its chances in selection. - * this way overlapping bones will cycle selection state as with objects. */ - EditBone *ebone_next_act = ((bArmature *)vc->obedit->data)->act_edbone; - { - bArmature *arm = (bArmature *)vc->obedit->data; - if (ebone_next_act && - EBONE_VISIBLE(arm, ebone_next_act) && - ebone_next_act->flag & (BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL)) - { - ebone_next_act = ebone_next_act->next ? ebone_next_act->next : arm->edbo->first; - } - else { - ebone_next_act = NULL; - } - } - - 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; - } - } - copy_v2_v2_int(last_mval, vc->mval); - } - else { - if (!XRAY_ACTIVE(vc->v3d)) { - do_nearest = true; - } - } - - /* matching logic from 'mixed_bones_object_selectbuffer' */ - int hits = 0; - - /* we _must_ end cache before return, use 'goto cache_end' */ - view3d_opengl_select_cache_begin(); - - { - const int select_mode = (do_nearest ? VIEW3D_SELECT_PICK_NEAREST : VIEW3D_SELECT_PICK_ALL); - const eV3DSelectObjectFilter select_filter = VIEW3D_SELECT_FILTER_NOP; - - rcti rect; - BLI_rcti_init_pt_radius(&rect, vc->mval, 12); - const int hits12 = view3d_opengl_select(vc, buffer, MAXPICKBUF, &rect, select_mode, select_filter); - if (hits12 == 1) { - hits = selectbuffer_ret_hits_12(buffer, hits12); - goto cache_end; - } - else if (hits12 > 0) { - int offs; - - offs = 4 * hits12; - BLI_rcti_init_pt_radius(&rect, vc->mval, 5); - const int hits5 = view3d_opengl_select( - vc, buffer + offs, MAXPICKBUF - offs, &rect, - select_mode, select_filter); - - if (hits5 == 1) { - hits = selectbuffer_ret_hits_5(buffer, hits12, hits5); - goto cache_end; - } - - if (hits5 > 0) { hits = selectbuffer_ret_hits_5(buffer, hits12, hits5); goto cache_end; } - else { hits = selectbuffer_ret_hits_12(buffer, hits12); goto cache_end; } - } - } + uint buffer[MAXPICKBUF]; + struct { + uint hitresult; + Base *base; + EditBone *ebone; + } best = { + .hitresult = BONESEL_NOSEL, + .base = NULL, + .ebone = NULL, + }; + + /* find the bone after the current active bone, so as to bump up its chances in selection. + * this way overlapping bones will cycle selection state as with objects. */ + EditBone *ebone_next_act = ((bArmature *)vc->obedit->data)->act_edbone; + { + bArmature *arm = (bArmature *)vc->obedit->data; + if (ebone_next_act && EBONE_VISIBLE(arm, ebone_next_act) && + ebone_next_act->flag & (BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL)) { + ebone_next_act = ebone_next_act->next ? ebone_next_act->next : arm->edbo->first; + } + else { + ebone_next_act = NULL; + } + } + + 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; + } + } + copy_v2_v2_int(last_mval, vc->mval); + } + else { + if (!XRAY_ACTIVE(vc->v3d)) { + do_nearest = true; + } + } + + /* matching logic from 'mixed_bones_object_selectbuffer' */ + int hits = 0; + + /* we _must_ end cache before return, use 'goto cache_end' */ + view3d_opengl_select_cache_begin(); + + { + const int select_mode = (do_nearest ? VIEW3D_SELECT_PICK_NEAREST : VIEW3D_SELECT_PICK_ALL); + const eV3DSelectObjectFilter select_filter = VIEW3D_SELECT_FILTER_NOP; + + rcti rect; + BLI_rcti_init_pt_radius(&rect, vc->mval, 12); + const int hits12 = view3d_opengl_select( + vc, buffer, MAXPICKBUF, &rect, select_mode, select_filter); + if (hits12 == 1) { + hits = selectbuffer_ret_hits_12(buffer, hits12); + goto cache_end; + } + else if (hits12 > 0) { + int offs; + + offs = 4 * hits12; + BLI_rcti_init_pt_radius(&rect, vc->mval, 5); + const int hits5 = view3d_opengl_select( + vc, buffer + offs, MAXPICKBUF - offs, &rect, select_mode, select_filter); + + if (hits5 == 1) { + hits = selectbuffer_ret_hits_5(buffer, hits12, hits5); + goto cache_end; + } + + if (hits5 > 0) { + hits = selectbuffer_ret_hits_5(buffer, hits12, hits5); + goto cache_end; + } + else { + hits = selectbuffer_ret_hits_12(buffer, hits12); + goto cache_end; + } + } + } cache_end: - view3d_opengl_select_cache_end(); - - uint bases_len; - Base **bases = BKE_view_layer_array_from_bases_in_edit_mode_unique_data(vc->view_layer, vc->v3d, &bases_len); - - /* 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); - } - } - else { - int dep_min = 5; - 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; - /* clicks on bone points get advantage */ - if (hitresult & (BONESEL_ROOT | BONESEL_TIP)) { - /* but also the unselected one */ - if (findunsel) { - if ( (hitresult & BONESEL_ROOT) && (ebone->flag & BONE_ROOTSEL) == 0) - dep = 1; - else if ( (hitresult & BONESEL_TIP) && (ebone->flag & BONE_TIPSEL) == 0) - dep = 1; - else - dep = 2; - } - else { - dep = 1; - } - } - else { - /* bone found */ - if (findunsel) { - if ((ebone->flag & BONE_SELECTED) == 0) - dep = 3; - else - dep = 4; - } - else { - dep = 3; - } - } - - if (ebone == ebone_next_act) { - dep -= 1; - } - - if (dep < dep_min) { - dep_min = dep; - best.hitresult = hitresult; - best.base = base; - best.ebone = ebone; - } - } - } - } - - if (!(best.hitresult & BONESEL_NOSEL)) { - *r_base = best.base; - - *r_selmask = 0; - if (best.hitresult & BONESEL_ROOT) { - *r_selmask |= BONE_ROOTSEL; - } - if (best.hitresult & BONESEL_TIP) { - *r_selmask |= BONE_TIPSEL; - } - if (best.hitresult & BONESEL_BONE) { - *r_selmask |= BONE_SELECTED; - } - MEM_freeN(bases); - return best.ebone; - } - } - *r_selmask = 0; - *r_base = NULL; - MEM_freeN(bases); - return NULL; + view3d_opengl_select_cache_end(); + + uint bases_len; + Base **bases = BKE_view_layer_array_from_bases_in_edit_mode_unique_data( + vc->view_layer, vc->v3d, &bases_len); + + /* 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); + } + } + else { + int dep_min = 5; + 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; + /* clicks on bone points get advantage */ + if (hitresult & (BONESEL_ROOT | BONESEL_TIP)) { + /* but also the unselected one */ + if (findunsel) { + if ((hitresult & BONESEL_ROOT) && (ebone->flag & BONE_ROOTSEL) == 0) + dep = 1; + else if ((hitresult & BONESEL_TIP) && (ebone->flag & BONE_TIPSEL) == 0) + dep = 1; + else + dep = 2; + } + else { + dep = 1; + } + } + else { + /* bone found */ + if (findunsel) { + if ((ebone->flag & BONE_SELECTED) == 0) + dep = 3; + else + dep = 4; + } + else { + dep = 3; + } + } + + if (ebone == ebone_next_act) { + dep -= 1; + } + + if (dep < dep_min) { + dep_min = dep; + best.hitresult = hitresult; + best.base = base; + best.ebone = ebone; + } + } + } + } + + if (!(best.hitresult & BONESEL_NOSEL)) { + *r_base = best.base; + + *r_selmask = 0; + if (best.hitresult & BONESEL_ROOT) { + *r_selmask |= BONE_ROOTSEL; + } + if (best.hitresult & BONESEL_TIP) { + *r_selmask |= BONE_TIPSEL; + } + if (best.hitresult & BONESEL_BONE) { + *r_selmask |= BONE_SELECTED; + } + MEM_freeN(bases); + return best.ebone; + } + } + *r_selmask = 0; + *r_base = NULL; + MEM_freeN(bases); + return NULL; } 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) { - if (ebone->flag & (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL)) { - ebone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - changed = true; - } - } - return changed; + bArmature *arm = obedit->data; + bool changed = false; + for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { + if (ebone->flag & (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL)) { + ebone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + changed = true; + } + } + return changed; } 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) { - /* first and foremost, bone must be visible and selected */ - if (EBONE_VISIBLE(arm, ebone)) { - if (ebone->flag & (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL)) { - ebone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - changed = true; - } - } - } - - if (changed) { - ED_armature_edit_sync_selection(arm->edbo); - } - return changed; + bArmature *arm = obedit->data; + bool changed = false; + for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { + /* first and foremost, bone must be visible and selected */ + if (EBONE_VISIBLE(arm, ebone)) { + if (ebone->flag & (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL)) { + ebone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + changed = true; + } + } + } + + if (changed) { + ED_armature_edit_sync_selection(arm->edbo); + } + return changed; } - bool ED_armature_edit_deselect_all_multi_ex(struct Base **bases, uint bases_len) { - bool changed_multi = false; - for (uint base_index = 0; base_index < bases_len; base_index++) { - Object *obedit = bases[base_index]->object; - changed_multi |= ED_armature_edit_deselect_all(obedit); - } - return changed_multi; + bool changed_multi = false; + for (uint base_index = 0; base_index < bases_len; base_index++) { + Object *obedit = bases[base_index]->object; + changed_multi |= ED_armature_edit_deselect_all(obedit); + } + return changed_multi; } bool ED_armature_edit_deselect_all_visible_multi_ex(struct Base **bases, uint bases_len) { - bool changed_multi = false; - for (uint base_index = 0; base_index < bases_len; base_index++) { - Object *obedit = bases[base_index]->object; - changed_multi |= ED_armature_edit_deselect_all_visible(obedit); - } - return changed_multi; + bool changed_multi = false; + for (uint base_index = 0; base_index < bases_len; base_index++) { + Object *obedit = bases[base_index]->object; + changed_multi |= ED_armature_edit_deselect_all_visible(obedit); + } + return changed_multi; } bool ED_armature_edit_deselect_all_visible_multi(bContext *C) { - ViewContext vc; - ED_view3d_viewcontext_init(C, &vc); - uint bases_len = 0; - Base **bases = BKE_view_layer_array_from_bases_in_edit_mode_unique_data(vc.view_layer, vc.v3d, &bases_len); - bool changed_multi = ED_armature_edit_deselect_all_multi_ex(bases, bases_len); - MEM_freeN(bases); - return changed_multi; + ViewContext vc; + ED_view3d_viewcontext_init(C, &vc); + uint bases_len = 0; + Base **bases = BKE_view_layer_array_from_bases_in_edit_mode_unique_data( + vc.view_layer, vc.v3d, &bases_len); + bool changed_multi = ED_armature_edit_deselect_all_multi_ex(bases, bases_len); + MEM_freeN(bases); + 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); - } + 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); + } } /* context: editmode armature in view3d */ -bool ED_armature_edit_select_pick(bContext *C, const int mval[2], bool extend, bool deselect, bool toggle) +bool ED_armature_edit_select_pick( + bContext *C, const int mval[2], bool extend, bool deselect, bool toggle) { - ViewContext vc; - EditBone *nearBone = NULL; - int selmask; - Base *basact = NULL; - - ED_view3d_viewcontext_init(C, &vc); - vc.mval[0] = mval[0]; - vc.mval[1] = mval[1]; - - nearBone = get_nearest_editbonepoint(&vc, true, true, &basact, &selmask); - if (nearBone) { - ED_view3d_viewcontext_init_object(&vc, basact->object); - bArmature *arm = vc.obedit->data; - - if (!EBONE_SELECTABLE(arm, nearBone)) { - return false; - } - - if (!extend && !deselect && !toggle) { - uint bases_len = 0; - Base **bases = BKE_view_layer_array_from_bases_in_edit_mode_unique_data(vc.view_layer, vc.v3d, &bases_len); - ED_armature_edit_deselect_all_multi_ex(bases, bases_len); - MEM_freeN(bases); - } - - /* by definition the non-root connected bones have no root point drawn, - * so a root selection needs to be delivered to the parent tip */ - - if (selmask & BONE_SELECTED) { - if (nearBone->parent && (nearBone->flag & BONE_CONNECTED)) { - /* click in a chain */ - if (extend) { - /* select this bone */ - nearBone->flag |= BONE_TIPSEL; - nearBone->parent->flag |= BONE_TIPSEL; - } - else if (deselect) { - /* deselect this bone */ - nearBone->flag &= ~(BONE_TIPSEL | BONE_SELECTED); - /* only deselect parent tip if it is not selected */ - if (!(nearBone->parent->flag & BONE_SELECTED)) - nearBone->parent->flag &= ~BONE_TIPSEL; - } - else if (toggle) { - /* hold shift inverts this bone's selection */ - if (nearBone->flag & BONE_SELECTED) { - /* deselect this bone */ - nearBone->flag &= ~(BONE_TIPSEL | BONE_SELECTED); - /* only deselect parent tip if it is not selected */ - if (!(nearBone->parent->flag & BONE_SELECTED)) - nearBone->parent->flag &= ~BONE_TIPSEL; - } - else { - /* select this bone */ - nearBone->flag |= BONE_TIPSEL; - nearBone->parent->flag |= BONE_TIPSEL; - } - } - else { - /* select this bone */ - nearBone->flag |= BONE_TIPSEL; - nearBone->parent->flag |= BONE_TIPSEL; - } - } - else { - if (extend) { - nearBone->flag |= (BONE_TIPSEL | BONE_ROOTSEL); - } - else if (deselect) { - nearBone->flag &= ~(BONE_TIPSEL | BONE_ROOTSEL); - } - else if (toggle) { - /* hold shift inverts this bone's selection */ - if (nearBone->flag & BONE_SELECTED) - nearBone->flag &= ~(BONE_TIPSEL | BONE_ROOTSEL); - else - nearBone->flag |= (BONE_TIPSEL | BONE_ROOTSEL); - } - else - nearBone->flag |= (BONE_TIPSEL | BONE_ROOTSEL); - } - } - else { - if (extend) - nearBone->flag |= selmask; - else if (deselect) - nearBone->flag &= ~selmask; - else if (toggle && (nearBone->flag & selmask)) - nearBone->flag &= ~selmask; - else - nearBone->flag |= selmask; - } - - ED_armature_edit_sync_selection(arm->edbo); - - /* then now check for active status */ - if (ebone_select_flag(nearBone)) { - arm->act_edbone = nearBone; - } - - if (vc.view_layer->basact != basact) { - ED_object_base_activate(C, basact); - } - - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, basact->object); - return true; - } - - return false; + ViewContext vc; + EditBone *nearBone = NULL; + int selmask; + Base *basact = NULL; + + ED_view3d_viewcontext_init(C, &vc); + vc.mval[0] = mval[0]; + vc.mval[1] = mval[1]; + + nearBone = get_nearest_editbonepoint(&vc, true, true, &basact, &selmask); + if (nearBone) { + ED_view3d_viewcontext_init_object(&vc, basact->object); + bArmature *arm = vc.obedit->data; + + if (!EBONE_SELECTABLE(arm, nearBone)) { + return false; + } + + if (!extend && !deselect && !toggle) { + uint bases_len = 0; + Base **bases = BKE_view_layer_array_from_bases_in_edit_mode_unique_data( + vc.view_layer, vc.v3d, &bases_len); + ED_armature_edit_deselect_all_multi_ex(bases, bases_len); + MEM_freeN(bases); + } + + /* by definition the non-root connected bones have no root point drawn, + * so a root selection needs to be delivered to the parent tip */ + + if (selmask & BONE_SELECTED) { + if (nearBone->parent && (nearBone->flag & BONE_CONNECTED)) { + /* click in a chain */ + if (extend) { + /* select this bone */ + nearBone->flag |= BONE_TIPSEL; + nearBone->parent->flag |= BONE_TIPSEL; + } + else if (deselect) { + /* deselect this bone */ + nearBone->flag &= ~(BONE_TIPSEL | BONE_SELECTED); + /* only deselect parent tip if it is not selected */ + if (!(nearBone->parent->flag & BONE_SELECTED)) + nearBone->parent->flag &= ~BONE_TIPSEL; + } + else if (toggle) { + /* hold shift inverts this bone's selection */ + if (nearBone->flag & BONE_SELECTED) { + /* deselect this bone */ + nearBone->flag &= ~(BONE_TIPSEL | BONE_SELECTED); + /* only deselect parent tip if it is not selected */ + if (!(nearBone->parent->flag & BONE_SELECTED)) + nearBone->parent->flag &= ~BONE_TIPSEL; + } + else { + /* select this bone */ + nearBone->flag |= BONE_TIPSEL; + nearBone->parent->flag |= BONE_TIPSEL; + } + } + else { + /* select this bone */ + nearBone->flag |= BONE_TIPSEL; + nearBone->parent->flag |= BONE_TIPSEL; + } + } + else { + if (extend) { + nearBone->flag |= (BONE_TIPSEL | BONE_ROOTSEL); + } + else if (deselect) { + nearBone->flag &= ~(BONE_TIPSEL | BONE_ROOTSEL); + } + else if (toggle) { + /* hold shift inverts this bone's selection */ + if (nearBone->flag & BONE_SELECTED) + nearBone->flag &= ~(BONE_TIPSEL | BONE_ROOTSEL); + else + nearBone->flag |= (BONE_TIPSEL | BONE_ROOTSEL); + } + else + nearBone->flag |= (BONE_TIPSEL | BONE_ROOTSEL); + } + } + else { + if (extend) + nearBone->flag |= selmask; + else if (deselect) + nearBone->flag &= ~selmask; + else if (toggle && (nearBone->flag & selmask)) + nearBone->flag &= ~selmask; + else + nearBone->flag |= selmask; + } + + ED_armature_edit_sync_selection(arm->edbo); + + /* then now check for active status */ + if (ebone_select_flag(nearBone)) { + arm->act_edbone = nearBone; + } + + if (vc.view_layer->basact != basact) { + ED_object_base_activate(C, basact); + } + + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, basact->object); + return true; + } + + return false; } /* -------------------------------------------------------------------- */ @@ -757,69 +775,73 @@ bool ED_armature_edit_select_pick(bContext *C, const int mval[2], bool extend, b * Implements #ED_armature_edit_select_op_from_tagged * \{ */ -static bool armature_edit_select_op_apply( - bArmature *arm, EditBone *ebone, const eSelectOp sel_op, int is_ignore_flag, int is_inside_flag) +static bool armature_edit_select_op_apply(bArmature *arm, + EditBone *ebone, + const eSelectOp sel_op, + int is_ignore_flag, + int is_inside_flag) { - BLI_assert(!(is_ignore_flag & ~(BONESEL_ROOT | BONESEL_TIP))); - BLI_assert(!(is_inside_flag & ~(BONESEL_ROOT | BONESEL_TIP | BONESEL_BONE))); - BLI_assert(EBONE_VISIBLE(arm, ebone)); - bool changed = false; - bool is_point_done = false; - int points_proj_tot = 0; - BLI_assert(ebone->flag == ebone->temp.i); - const int ebone_flag_prev = ebone->flag; - - if ((is_ignore_flag & BONE_ROOTSEL) == 0) { - points_proj_tot++; - const bool is_select = ebone->flag & BONE_ROOTSEL; - const bool is_inside = is_inside_flag & BONESEL_ROOT; - const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside); - if (sel_op_result != -1) { - if (sel_op_result == 0 || EBONE_SELECTABLE(arm, ebone)) { - SET_FLAG_FROM_TEST(ebone->flag, sel_op_result, BONE_ROOTSEL); - } - } - is_point_done |= is_inside; - } - - if ((is_ignore_flag & BONE_TIPSEL) == 0) { - points_proj_tot++; - const bool is_select = ebone->flag & BONE_TIPSEL; - const bool is_inside = is_inside_flag & BONESEL_TIP; - const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside); - if (sel_op_result != -1) { - if (sel_op_result == 0 || EBONE_SELECTABLE(arm, ebone)) { - SET_FLAG_FROM_TEST(ebone->flag, sel_op_result, BONE_TIPSEL); - } - } - is_point_done |= is_inside; - } - - /* if one of points selected, we skip the bone itself */ - if ((is_point_done == false) && (points_proj_tot == 2)) { - const bool is_select = ebone->flag & BONE_SELECTED; - { - const bool is_inside = is_inside_flag & BONESEL_BONE; - const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside); - if (sel_op_result != -1) { - if (sel_op_result == 0 || EBONE_SELECTABLE(arm, ebone)) { - SET_FLAG_FROM_TEST(ebone->flag, sel_op_result, BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL); - } - } - } - - changed = true; - } - changed |= is_point_done; - - if (ebone_flag_prev != ebone->flag) { - ebone->temp.i = ebone->flag; - ebone->flag = ebone_flag_prev; - ebone->flag = ebone_flag_prev | BONE_DONE; - changed = true; - } - - return changed; + BLI_assert(!(is_ignore_flag & ~(BONESEL_ROOT | BONESEL_TIP))); + BLI_assert(!(is_inside_flag & ~(BONESEL_ROOT | BONESEL_TIP | BONESEL_BONE))); + BLI_assert(EBONE_VISIBLE(arm, ebone)); + bool changed = false; + bool is_point_done = false; + int points_proj_tot = 0; + BLI_assert(ebone->flag == ebone->temp.i); + const int ebone_flag_prev = ebone->flag; + + if ((is_ignore_flag & BONE_ROOTSEL) == 0) { + points_proj_tot++; + const bool is_select = ebone->flag & BONE_ROOTSEL; + const bool is_inside = is_inside_flag & BONESEL_ROOT; + const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside); + if (sel_op_result != -1) { + if (sel_op_result == 0 || EBONE_SELECTABLE(arm, ebone)) { + SET_FLAG_FROM_TEST(ebone->flag, sel_op_result, BONE_ROOTSEL); + } + } + is_point_done |= is_inside; + } + + if ((is_ignore_flag & BONE_TIPSEL) == 0) { + points_proj_tot++; + const bool is_select = ebone->flag & BONE_TIPSEL; + const bool is_inside = is_inside_flag & BONESEL_TIP; + const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside); + if (sel_op_result != -1) { + if (sel_op_result == 0 || EBONE_SELECTABLE(arm, ebone)) { + SET_FLAG_FROM_TEST(ebone->flag, sel_op_result, BONE_TIPSEL); + } + } + is_point_done |= is_inside; + } + + /* if one of points selected, we skip the bone itself */ + if ((is_point_done == false) && (points_proj_tot == 2)) { + const bool is_select = ebone->flag & BONE_SELECTED; + { + const bool is_inside = is_inside_flag & BONESEL_BONE; + const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside); + if (sel_op_result != -1) { + if (sel_op_result == 0 || EBONE_SELECTABLE(arm, ebone)) { + SET_FLAG_FROM_TEST( + ebone->flag, sel_op_result, BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL); + } + } + } + + changed = true; + } + changed |= is_point_done; + + if (ebone_flag_prev != ebone->flag) { + ebone->temp.i = ebone->flag; + ebone->flag = ebone_flag_prev; + ebone->flag = ebone_flag_prev | BONE_DONE; + changed = true; + } + + return changed; } /** @@ -836,101 +858,97 @@ static bool armature_edit_select_op_apply( */ bool ED_armature_edit_select_op_from_tagged(bArmature *arm, const int sel_op) { - bool changed = false; - - /* Initialize flags. */ - { - for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { - - /* Flush the parent flag to this bone - * so we don't need to check the parent when adjusting the selection. */ - if ((ebone->flag & BONE_CONNECTED) && ebone->parent) { - if (ebone->parent->flag & BONE_TIPSEL) { - ebone->flag |= BONE_ROOTSEL; - } - else { - ebone->flag &= ~BONE_ROOTSEL; - } - - /* Flush the 'temp.i' flag. */ - if (ebone->parent->temp.i & BONESEL_TIP) { - ebone->temp.i |= BONESEL_ROOT; - } - } - ebone->flag &= ~BONE_DONE; - } - } - - /* Apply selection from bone selection flags. */ - for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { - 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)); - - /* Use as previous bone flag from now on. */ - ebone->temp.i = ebone->flag; - - /* When there is a partial selection without both endpoints, only select an endpoint. */ - if ((is_inside_flag & BONESEL_BONE) && - (is_inside_flag & (BONESEL_ROOT | BONESEL_TIP)) && - ((is_inside_flag & (BONESEL_ROOT | BONESEL_TIP)) != (BONESEL_ROOT | BONESEL_TIP))) - { - is_inside_flag &= ~BONESEL_BONE; - } - - changed |= armature_edit_select_op_apply(arm, ebone, sel_op, is_ignore_flag, is_inside_flag); - } - } - - if (changed) { - /* Cleanup flags. */ - for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { - if (ebone->flag & BONE_DONE) { - SWAP(int, ebone->temp.i, ebone->flag); - ebone->flag |= BONE_DONE; - if ((ebone->flag & BONE_CONNECTED) && ebone->parent) { - if ((ebone->parent->flag & BONE_DONE) == 0) { - /* Checked below. */ - ebone->parent->temp.i = ebone->parent->flag; - } - } - } - } - - for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { - if (ebone->flag & BONE_DONE) { - if ((ebone->flag & BONE_CONNECTED) && ebone->parent) { - bool is_parent_tip_changed = (ebone->parent->flag & BONE_TIPSEL) != (ebone->parent->temp.i & BONE_TIPSEL); - if ((ebone->temp.i & BONE_ROOTSEL) == 0) { - if ((ebone->flag & BONE_ROOTSEL) != 0) { - ebone->parent->flag |= BONE_TIPSEL; - } - } - else { - if ((ebone->flag & BONE_ROOTSEL) == 0) { - ebone->parent->flag &= ~BONE_TIPSEL; - - } - } - - - if (is_parent_tip_changed == false) { - /* Keep tip selected if the parent remains selected. */ - if (ebone->parent->flag & BONE_SELECTED) { - ebone->parent->flag |= BONE_TIPSEL; - } - } - - } - ebone->flag &= ~BONE_DONE; - } - } - - ED_armature_edit_sync_selection(arm->edbo); - ED_armature_edit_validate_active(arm); - } - - return changed; + bool changed = false; + + /* Initialize flags. */ + { + for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { + + /* Flush the parent flag to this bone + * so we don't need to check the parent when adjusting the selection. */ + if ((ebone->flag & BONE_CONNECTED) && ebone->parent) { + if (ebone->parent->flag & BONE_TIPSEL) { + ebone->flag |= BONE_ROOTSEL; + } + else { + ebone->flag &= ~BONE_ROOTSEL; + } + + /* Flush the 'temp.i' flag. */ + if (ebone->parent->temp.i & BONESEL_TIP) { + ebone->temp.i |= BONESEL_ROOT; + } + } + ebone->flag &= ~BONE_DONE; + } + } + + /* Apply selection from bone selection flags. */ + for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { + 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)); + + /* Use as previous bone flag from now on. */ + ebone->temp.i = ebone->flag; + + /* When there is a partial selection without both endpoints, only select an endpoint. */ + if ((is_inside_flag & BONESEL_BONE) && (is_inside_flag & (BONESEL_ROOT | BONESEL_TIP)) && + ((is_inside_flag & (BONESEL_ROOT | BONESEL_TIP)) != (BONESEL_ROOT | BONESEL_TIP))) { + is_inside_flag &= ~BONESEL_BONE; + } + + changed |= armature_edit_select_op_apply(arm, ebone, sel_op, is_ignore_flag, is_inside_flag); + } + } + + if (changed) { + /* Cleanup flags. */ + for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { + if (ebone->flag & BONE_DONE) { + SWAP(int, ebone->temp.i, ebone->flag); + ebone->flag |= BONE_DONE; + if ((ebone->flag & BONE_CONNECTED) && ebone->parent) { + if ((ebone->parent->flag & BONE_DONE) == 0) { + /* Checked below. */ + ebone->parent->temp.i = ebone->parent->flag; + } + } + } + } + + for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { + if (ebone->flag & BONE_DONE) { + if ((ebone->flag & BONE_CONNECTED) && ebone->parent) { + bool is_parent_tip_changed = (ebone->parent->flag & BONE_TIPSEL) != + (ebone->parent->temp.i & BONE_TIPSEL); + if ((ebone->temp.i & BONE_ROOTSEL) == 0) { + if ((ebone->flag & BONE_ROOTSEL) != 0) { + ebone->parent->flag |= BONE_TIPSEL; + } + } + else { + if ((ebone->flag & BONE_ROOTSEL) == 0) { + ebone->parent->flag &= ~BONE_TIPSEL; + } + } + + if (is_parent_tip_changed == false) { + /* Keep tip selected if the parent remains selected. */ + if (ebone->parent->flag & BONE_SELECTED) { + ebone->parent->flag |= BONE_TIPSEL; + } + } + } + ebone->flag &= ~BONE_DONE; + } + } + + ED_armature_edit_sync_selection(arm->edbo); + ED_armature_edit_validate_active(arm); + } + + return changed; } /** \} */ @@ -939,613 +957,612 @@ bool ED_armature_edit_select_op_from_tagged(bArmature *arm, const int sel_op) static int armature_de_select_all_exec(bContext *C, wmOperator *op) { - int action = RNA_enum_get(op->ptr, "action"); - - if (action == SEL_TOGGLE) { - /* Determine if there are any selected bones - * And therefore whether we are selecting or deselecting */ - action = SEL_SELECT; - CTX_DATA_BEGIN(C, EditBone *, ebone, visible_bones) - { - if (ebone->flag & (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL)) { - action = SEL_DESELECT; - break; - } - } - CTX_DATA_END; - } - - /* Set the flags */ - CTX_DATA_BEGIN(C, EditBone *, ebone, visible_bones) - { - /* ignore bone if selection can't change */ - switch (action) { - case SEL_SELECT: - if ((ebone->flag & BONE_UNSELECTABLE) == 0) { - ebone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - if (ebone->parent) { - ebone->parent->flag |= (BONE_TIPSEL); - } - } - break; - case SEL_DESELECT: - ebone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - break; - case SEL_INVERT: - if (ebone->flag & BONE_SELECTED) { - ebone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - } - else { - if ((ebone->flag & BONE_UNSELECTABLE) == 0) { - ebone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - if (ebone->parent) { - ebone->parent->flag |= (BONE_TIPSEL); - } - } - } - break; - } - } - CTX_DATA_END; - - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, NULL); - - return OPERATOR_FINISHED; + int action = RNA_enum_get(op->ptr, "action"); + + if (action == SEL_TOGGLE) { + /* Determine if there are any selected bones + * And therefore whether we are selecting or deselecting */ + action = SEL_SELECT; + CTX_DATA_BEGIN (C, EditBone *, ebone, visible_bones) { + if (ebone->flag & (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL)) { + action = SEL_DESELECT; + break; + } + } + CTX_DATA_END; + } + + /* Set the flags */ + CTX_DATA_BEGIN (C, EditBone *, ebone, visible_bones) { + /* ignore bone if selection can't change */ + switch (action) { + case SEL_SELECT: + if ((ebone->flag & BONE_UNSELECTABLE) == 0) { + ebone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + if (ebone->parent) { + ebone->parent->flag |= (BONE_TIPSEL); + } + } + break; + case SEL_DESELECT: + ebone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + break; + case SEL_INVERT: + if (ebone->flag & BONE_SELECTED) { + ebone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + } + else { + if ((ebone->flag & BONE_UNSELECTABLE) == 0) { + ebone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + if (ebone->parent) { + ebone->parent->flag |= (BONE_TIPSEL); + } + } + } + break; + } + } + CTX_DATA_END; + + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, NULL); + + return OPERATOR_FINISHED; } void ARMATURE_OT_select_all(wmOperatorType *ot) { - /* identifiers */ - ot->name = "(De)select All"; - ot->idname = "ARMATURE_OT_select_all"; - ot->description = "Toggle selection status of all bones"; + /* identifiers */ + ot->name = "(De)select All"; + ot->idname = "ARMATURE_OT_select_all"; + ot->description = "Toggle selection status of all bones"; - /* api callbacks */ - ot->exec = armature_de_select_all_exec; - ot->poll = ED_operator_editarmature; + /* api callbacks */ + ot->exec = armature_de_select_all_exec; + ot->poll = ED_operator_editarmature; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - WM_operator_properties_select_all(ot); + WM_operator_properties_select_all(ot); } /**************** Select more/less **************/ static void armature_select_more(bArmature *arm, EditBone *ebone) { - if ((EBONE_PREV_FLAG_GET(ebone) & (BONE_ROOTSEL | BONE_TIPSEL)) != 0) { - if (EBONE_SELECTABLE(arm, ebone)) { - ED_armature_ebone_select_set(ebone, true); - } - } - - if (ebone->parent && (ebone->flag & BONE_CONNECTED)) { - /* to parent */ - if ((EBONE_PREV_FLAG_GET(ebone) & BONE_ROOTSEL) != 0) { - if (EBONE_SELECTABLE(arm, ebone->parent)) { - ED_armature_ebone_selectflag_enable(ebone->parent, (BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL)); - } - } - - /* from parent (difference from select less) */ - if ((EBONE_PREV_FLAG_GET(ebone->parent) & BONE_TIPSEL) != 0) { - if (EBONE_SELECTABLE(arm, ebone)) { - ED_armature_ebone_selectflag_enable(ebone, (BONE_SELECTED | BONE_ROOTSEL)); - } - } - } + if ((EBONE_PREV_FLAG_GET(ebone) & (BONE_ROOTSEL | BONE_TIPSEL)) != 0) { + if (EBONE_SELECTABLE(arm, ebone)) { + ED_armature_ebone_select_set(ebone, true); + } + } + + if (ebone->parent && (ebone->flag & BONE_CONNECTED)) { + /* to parent */ + if ((EBONE_PREV_FLAG_GET(ebone) & BONE_ROOTSEL) != 0) { + if (EBONE_SELECTABLE(arm, ebone->parent)) { + ED_armature_ebone_selectflag_enable(ebone->parent, + (BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL)); + } + } + + /* from parent (difference from select less) */ + if ((EBONE_PREV_FLAG_GET(ebone->parent) & BONE_TIPSEL) != 0) { + if (EBONE_SELECTABLE(arm, ebone)) { + ED_armature_ebone_selectflag_enable(ebone, (BONE_SELECTED | BONE_ROOTSEL)); + } + } + } } static void armature_select_less(bArmature *UNUSED(arm), EditBone *ebone) { - if ((EBONE_PREV_FLAG_GET(ebone) & (BONE_ROOTSEL | BONE_TIPSEL)) != (BONE_ROOTSEL | BONE_TIPSEL)) { - ED_armature_ebone_select_set(ebone, false); - } - - if (ebone->parent && (ebone->flag & BONE_CONNECTED)) { - /* to parent */ - if ((EBONE_PREV_FLAG_GET(ebone) & BONE_SELECTED) == 0) { - ED_armature_ebone_selectflag_disable(ebone->parent, (BONE_SELECTED | BONE_TIPSEL)); - } - - /* from parent (difference from select more) */ - if ((EBONE_PREV_FLAG_GET(ebone->parent) & BONE_SELECTED) == 0) { - ED_armature_ebone_selectflag_disable(ebone, (BONE_SELECTED | BONE_ROOTSEL)); - } - } + if ((EBONE_PREV_FLAG_GET(ebone) & (BONE_ROOTSEL | BONE_TIPSEL)) != + (BONE_ROOTSEL | BONE_TIPSEL)) { + ED_armature_ebone_select_set(ebone, false); + } + + if (ebone->parent && (ebone->flag & BONE_CONNECTED)) { + /* to parent */ + if ((EBONE_PREV_FLAG_GET(ebone) & BONE_SELECTED) == 0) { + ED_armature_ebone_selectflag_disable(ebone->parent, (BONE_SELECTED | BONE_TIPSEL)); + } + + /* from parent (difference from select more) */ + if ((EBONE_PREV_FLAG_GET(ebone->parent) & BONE_SELECTED) == 0) { + ED_armature_ebone_selectflag_disable(ebone, (BONE_SELECTED | BONE_ROOTSEL)); + } + } } static void armature_select_more_less(Object *ob, bool more) { - bArmature *arm = (bArmature *)ob->data; - EditBone *ebone; - - /* XXX, eventually we shouldn't need this - campbell */ - ED_armature_edit_sync_selection(arm->edbo); - - /* count bones & store selection state */ - for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { - EBONE_PREV_FLAG_SET(ebone, ED_armature_ebone_selectflag_get(ebone)); - } - - /* do selection */ - for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { - if (EBONE_VISIBLE(arm, ebone)) { - if (more) { - armature_select_more(arm, ebone); - } - else { - armature_select_less(arm, ebone); - } - } - } - - for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { - if (EBONE_VISIBLE(arm, ebone)) { - if (more == false) { - if (ebone->flag & BONE_SELECTED) { - ED_armature_ebone_select_set(ebone, true); - } - } - } - ebone->temp.p = NULL; - } - - ED_armature_edit_sync_selection(arm->edbo); + bArmature *arm = (bArmature *)ob->data; + EditBone *ebone; + + /* XXX, eventually we shouldn't need this - campbell */ + ED_armature_edit_sync_selection(arm->edbo); + + /* count bones & store selection state */ + for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { + EBONE_PREV_FLAG_SET(ebone, ED_armature_ebone_selectflag_get(ebone)); + } + + /* do selection */ + for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { + if (EBONE_VISIBLE(arm, ebone)) { + if (more) { + armature_select_more(arm, ebone); + } + else { + armature_select_less(arm, ebone); + } + } + } + + for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { + if (EBONE_VISIBLE(arm, ebone)) { + if (more == false) { + if (ebone->flag & BONE_SELECTED) { + ED_armature_ebone_select_set(ebone, true); + } + } + } + ebone->temp.p = NULL; + } + + ED_armature_edit_sync_selection(arm->edbo); } static int armature_de_select_more_exec(bContext *C, wmOperator *UNUSED(op)) { - 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]; - armature_select_more_less(ob, true); - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); - } - MEM_freeN(objects); - return OPERATOR_FINISHED; + 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]; + armature_select_more_less(ob, true); + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); + } + MEM_freeN(objects); + return OPERATOR_FINISHED; } void ARMATURE_OT_select_more(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Select More"; - ot->idname = "ARMATURE_OT_select_more"; - ot->description = "Select those bones connected to the initial selection"; + /* identifiers */ + ot->name = "Select More"; + ot->idname = "ARMATURE_OT_select_more"; + ot->description = "Select those bones connected to the initial selection"; - /* api callbacks */ - ot->exec = armature_de_select_more_exec; - ot->poll = ED_operator_editarmature; + /* api callbacks */ + ot->exec = armature_de_select_more_exec; + ot->poll = ED_operator_editarmature; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } static int armature_de_select_less_exec(bContext *C, wmOperator *UNUSED(op)) { - 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]; - armature_select_more_less(ob, false); - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); - } - MEM_freeN(objects); - return OPERATOR_FINISHED; + 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]; + armature_select_more_less(ob, false); + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); + } + MEM_freeN(objects); + return OPERATOR_FINISHED; } void ARMATURE_OT_select_less(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Select Less"; - ot->idname = "ARMATURE_OT_select_less"; - ot->description = "Deselect those bones at the boundary of each selection region"; + /* identifiers */ + ot->name = "Select Less"; + ot->idname = "ARMATURE_OT_select_less"; + ot->description = "Deselect those bones at the boundary of each selection region"; - /* api callbacks */ - ot->exec = armature_de_select_less_exec; - ot->poll = ED_operator_editarmature; + /* api callbacks */ + ot->exec = armature_de_select_less_exec; + ot->poll = ED_operator_editarmature; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } enum { - SIMEDBONE_CHILDREN = 1, - SIMEDBONE_CHILDREN_IMMEDIATE, - SIMEDBONE_SIBLINGS, - SIMEDBONE_LENGTH, - SIMEDBONE_DIRECTION, - SIMEDBONE_PREFIX, - SIMEDBONE_SUFFIX, - SIMEDBONE_LAYER, - SIMEDBONE_GROUP, - SIMEDBONE_SHAPE, + SIMEDBONE_CHILDREN = 1, + SIMEDBONE_CHILDREN_IMMEDIATE, + SIMEDBONE_SIBLINGS, + SIMEDBONE_LENGTH, + SIMEDBONE_DIRECTION, + SIMEDBONE_PREFIX, + SIMEDBONE_SUFFIX, + SIMEDBONE_LAYER, + SIMEDBONE_GROUP, + SIMEDBONE_SHAPE, }; static const EnumPropertyItem prop_similar_types[] = { - {SIMEDBONE_CHILDREN, "CHILDREN", 0, "Children", ""}, - {SIMEDBONE_CHILDREN_IMMEDIATE, "CHILDREN_IMMEDIATE", 0, "Immediate children", ""}, - {SIMEDBONE_SIBLINGS, "SIBLINGS", 0, "Siblings", ""}, - {SIMEDBONE_LENGTH, "LENGTH", 0, "Length", ""}, - {SIMEDBONE_DIRECTION, "DIRECTION", 0, "Direction (Y axis)", ""}, - {SIMEDBONE_PREFIX, "PREFIX", 0, "Prefix", ""}, - {SIMEDBONE_SUFFIX, "SUFFIX", 0, "Suffix", ""}, - {SIMEDBONE_LAYER, "LAYER", 0, "Layer", ""}, - {SIMEDBONE_GROUP, "GROUP", 0, "Group", ""}, - {SIMEDBONE_SHAPE, "SHAPE", 0, "Shape", ""}, - {0, NULL, 0, NULL, NULL}, + {SIMEDBONE_CHILDREN, "CHILDREN", 0, "Children", ""}, + {SIMEDBONE_CHILDREN_IMMEDIATE, "CHILDREN_IMMEDIATE", 0, "Immediate children", ""}, + {SIMEDBONE_SIBLINGS, "SIBLINGS", 0, "Siblings", ""}, + {SIMEDBONE_LENGTH, "LENGTH", 0, "Length", ""}, + {SIMEDBONE_DIRECTION, "DIRECTION", 0, "Direction (Y axis)", ""}, + {SIMEDBONE_PREFIX, "PREFIX", 0, "Prefix", ""}, + {SIMEDBONE_SUFFIX, "SUFFIX", 0, "Suffix", ""}, + {SIMEDBONE_LAYER, "LAYER", 0, "Layer", ""}, + {SIMEDBONE_GROUP, "GROUP", 0, "Group", ""}, + {SIMEDBONE_SHAPE, "SHAPE", 0, "Shape", ""}, + {0, NULL, 0, NULL, NULL}, }; static float bone_length_squared_worldspace_get(Object *ob, EditBone *ebone) { - float v1[3], v2[3]; - mul_v3_mat3_m4v3(v1, ob->obmat, ebone->head); - mul_v3_mat3_m4v3(v2, ob->obmat, ebone->tail); - return len_squared_v3v3(v1, v2); + float v1[3], v2[3]; + mul_v3_mat3_m4v3(v1, ob->obmat, ebone->head); + mul_v3_mat3_m4v3(v2, ob->obmat, ebone->tail); + return len_squared_v3v3(v1, v2); } static void select_similar_length(bContext *C, const float thresh) { - ViewLayer *view_layer = CTX_data_view_layer(C); - Object *ob_act = CTX_data_edit_object(C); - EditBone *ebone_act = CTX_data_active_bone(C); - - /* Thresh is always relative to current length. */ - const float len = bone_length_squared_worldspace_get(ob_act, ebone_act); - const float len_min = len / (1.0f + (thresh - FLT_EPSILON)); - const float len_max = len * (1.0f + (thresh + FLT_EPSILON)); - - 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 changed = false; - - for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { - 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)) - { - ED_armature_ebone_select_set(ebone, true); - changed = true; - } - } - } - - if (changed) { - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); - } - } - MEM_freeN(objects); + ViewLayer *view_layer = CTX_data_view_layer(C); + Object *ob_act = CTX_data_edit_object(C); + EditBone *ebone_act = CTX_data_active_bone(C); + + /* Thresh is always relative to current length. */ + const float len = bone_length_squared_worldspace_get(ob_act, ebone_act); + const float len_min = len / (1.0f + (thresh - FLT_EPSILON)); + const float len_max = len * (1.0f + (thresh + FLT_EPSILON)); + + 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 changed = false; + + for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { + 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)) { + ED_armature_ebone_select_set(ebone, true); + changed = true; + } + } + } + + if (changed) { + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); + } + } + MEM_freeN(objects); } static void bone_direction_worldspace_get(Object *ob, EditBone *ebone, float *r_dir) { - float v1[3], v2[3]; - copy_v3_v3(v1, ebone->head); - copy_v3_v3(v2, ebone->tail); + float v1[3], v2[3]; + copy_v3_v3(v1, ebone->head); + copy_v3_v3(v2, ebone->tail); - mul_m4_v3(ob->obmat, v1); - mul_m4_v3(ob->obmat, v2); + mul_m4_v3(ob->obmat, v1); + mul_m4_v3(ob->obmat, v2); - sub_v3_v3v3(r_dir, v1, v2); - normalize_v3(r_dir); + sub_v3_v3v3(r_dir, v1, v2); + normalize_v3(r_dir); } static void select_similar_direction(bContext *C, const float thresh) { - ViewLayer *view_layer = CTX_data_view_layer(C); - Object *ob_act = CTX_data_edit_object(C); - EditBone *ebone_act = CTX_data_active_bone(C); - - float dir_act[3]; - bone_direction_worldspace_get(ob_act, ebone_act, dir_act); - - 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 changed = false; - - for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { - if (EBONE_SELECTABLE(arm, ebone)) { - float dir[3]; - bone_direction_worldspace_get(ob, ebone, dir); - - if (angle_v3v3(dir_act, dir) / (float)M_PI < (thresh + FLT_EPSILON)) { - ED_armature_ebone_select_set(ebone, true); - changed = true; - } - } - } - - if (changed) { - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); - } - } - MEM_freeN(objects); + ViewLayer *view_layer = CTX_data_view_layer(C); + Object *ob_act = CTX_data_edit_object(C); + EditBone *ebone_act = CTX_data_active_bone(C); + + float dir_act[3]; + bone_direction_worldspace_get(ob_act, ebone_act, dir_act); + + 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 changed = false; + + for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { + if (EBONE_SELECTABLE(arm, ebone)) { + float dir[3]; + bone_direction_worldspace_get(ob, ebone, dir); + + if (angle_v3v3(dir_act, dir) / (float)M_PI < (thresh + FLT_EPSILON)) { + ED_armature_ebone_select_set(ebone, true); + changed = true; + } + } + } + + if (changed) { + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); + } + } + MEM_freeN(objects); } static void select_similar_layer(bContext *C) { - ViewLayer *view_layer = CTX_data_view_layer(C); - EditBone *ebone_act = CTX_data_active_bone(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 changed = false; - - for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { - if (EBONE_SELECTABLE(arm, ebone)) { - if (ebone->layer & ebone_act->layer) { - ED_armature_ebone_select_set(ebone, true); - changed = true; - } - } - } - - if (changed) { - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); - } - } - MEM_freeN(objects); + ViewLayer *view_layer = CTX_data_view_layer(C); + EditBone *ebone_act = CTX_data_active_bone(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 changed = false; + + for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { + if (EBONE_SELECTABLE(arm, ebone)) { + if (ebone->layer & ebone_act->layer) { + ED_armature_ebone_select_set(ebone, true); + changed = true; + } + } + } + + if (changed) { + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); + } + } + MEM_freeN(objects); } static void select_similar_prefix(bContext *C) { - ViewLayer *view_layer = CTX_data_view_layer(C); - EditBone *ebone_act = CTX_data_active_bone(C); - - char body_tmp[MAXBONENAME]; - char prefix_act[MAXBONENAME]; - - BLI_string_split_prefix(ebone_act->name, prefix_act, body_tmp, sizeof(ebone_act->name)); - - if (prefix_act[0] == '\0') { - return; - } - - 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 changed = false; - - /* Find matches */ - for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { - if (EBONE_SELECTABLE(arm, ebone)) { - char prefix_other[MAXBONENAME]; - BLI_string_split_prefix(ebone->name, prefix_other, body_tmp, sizeof(ebone->name)); - if (STREQ(prefix_act, prefix_other)) { - ED_armature_ebone_select_set(ebone, true); - changed = true; - } - } - } - - if (changed) { - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); - } - } - MEM_freeN(objects); + ViewLayer *view_layer = CTX_data_view_layer(C); + EditBone *ebone_act = CTX_data_active_bone(C); + + char body_tmp[MAXBONENAME]; + char prefix_act[MAXBONENAME]; + + BLI_string_split_prefix(ebone_act->name, prefix_act, body_tmp, sizeof(ebone_act->name)); + + if (prefix_act[0] == '\0') { + return; + } + + 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 changed = false; + + /* Find matches */ + for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { + if (EBONE_SELECTABLE(arm, ebone)) { + char prefix_other[MAXBONENAME]; + BLI_string_split_prefix(ebone->name, prefix_other, body_tmp, sizeof(ebone->name)); + if (STREQ(prefix_act, prefix_other)) { + ED_armature_ebone_select_set(ebone, true); + changed = true; + } + } + } + + if (changed) { + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); + } + } + MEM_freeN(objects); } static void select_similar_suffix(bContext *C) { - ViewLayer *view_layer = CTX_data_view_layer(C); - EditBone *ebone_act = CTX_data_active_bone(C); - - char body_tmp[MAXBONENAME]; - char suffix_act[MAXBONENAME]; - - BLI_string_split_suffix(ebone_act->name, body_tmp, suffix_act, sizeof(ebone_act->name)); - - if (suffix_act[0] == '\0') - return; - - 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 changed = false; - - /* Find matches */ - for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { - if (EBONE_SELECTABLE(arm, ebone)) { - char suffix_other[MAXBONENAME]; - BLI_string_split_suffix(ebone->name, body_tmp, suffix_other, sizeof(ebone->name)); - if (STREQ(suffix_act, suffix_other)) { - ED_armature_ebone_select_set(ebone, true); - changed = true; - } - } - } - - if (changed) { - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); - } - } - MEM_freeN(objects); + ViewLayer *view_layer = CTX_data_view_layer(C); + EditBone *ebone_act = CTX_data_active_bone(C); + + char body_tmp[MAXBONENAME]; + char suffix_act[MAXBONENAME]; + + BLI_string_split_suffix(ebone_act->name, body_tmp, suffix_act, sizeof(ebone_act->name)); + + if (suffix_act[0] == '\0') + return; + + 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 changed = false; + + /* Find matches */ + for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { + if (EBONE_SELECTABLE(arm, ebone)) { + char suffix_other[MAXBONENAME]; + BLI_string_split_suffix(ebone->name, body_tmp, suffix_other, sizeof(ebone->name)); + if (STREQ(suffix_act, suffix_other)) { + ED_armature_ebone_select_set(ebone, true); + changed = true; + } + } + } + + if (changed) { + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); + } + } + MEM_freeN(objects); } /** Use for matching any pose channel data. */ -static void select_similar_data_pchan( - bContext *C, - const size_t bytes_size, const int offset) +static void select_similar_data_pchan(bContext *C, const size_t bytes_size, const int offset) { - Object *obedit = CTX_data_edit_object(C); - bArmature *arm = obedit->data; - EditBone *ebone_act = CTX_data_active_bone(C); - - const bPoseChannel *pchan_active = BKE_pose_channel_find_name(obedit->pose, ebone_act->name); - const char *data_active = (const char *)POINTER_OFFSET(pchan_active, offset); - for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { - if (EBONE_SELECTABLE(arm, ebone)) { - const bPoseChannel *pchan = BKE_pose_channel_find_name(obedit->pose, ebone->name); - if (pchan) { - const char *data_test = (const char *)POINTER_OFFSET(pchan, offset); - if (memcmp(data_active, data_test, bytes_size) == 0) { - ED_armature_ebone_select_set(ebone, true); - } - } - } - } - - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit); + Object *obedit = CTX_data_edit_object(C); + bArmature *arm = obedit->data; + EditBone *ebone_act = CTX_data_active_bone(C); + + const bPoseChannel *pchan_active = BKE_pose_channel_find_name(obedit->pose, ebone_act->name); + const char *data_active = (const char *)POINTER_OFFSET(pchan_active, offset); + for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { + if (EBONE_SELECTABLE(arm, ebone)) { + const bPoseChannel *pchan = BKE_pose_channel_find_name(obedit->pose, ebone->name); + if (pchan) { + const char *data_test = (const char *)POINTER_OFFSET(pchan, offset); + if (memcmp(data_active, data_test, bytes_size) == 0) { + ED_armature_ebone_select_set(ebone, true); + } + } + } + } + + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit); } static void is_ancestor(EditBone *bone, EditBone *ancestor) { - if (bone->temp.ebone == ancestor || bone->temp.ebone == NULL) - return; + if (bone->temp.ebone == ancestor || bone->temp.ebone == NULL) + return; - if (bone->temp.ebone->temp.ebone != NULL && bone->temp.ebone->temp.ebone != ancestor) - is_ancestor(bone->temp.ebone, ancestor); + if (bone->temp.ebone->temp.ebone != NULL && bone->temp.ebone->temp.ebone != ancestor) + is_ancestor(bone->temp.ebone, ancestor); - bone->temp.ebone = bone->temp.ebone->temp.ebone; + bone->temp.ebone = bone->temp.ebone->temp.ebone; } static void select_similar_children(bContext *C) { - Object *obedit = CTX_data_edit_object(C); - bArmature *arm = obedit->data; - EditBone *ebone_act = CTX_data_active_bone(C); + Object *obedit = CTX_data_edit_object(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) { - ebone_iter->temp.ebone = ebone_iter->parent; - } + for (EditBone *ebone_iter = arm->edbo->first; ebone_iter; ebone_iter = ebone_iter->next) { + ebone_iter->temp.ebone = ebone_iter->parent; + } - for (EditBone *ebone_iter = arm->edbo->first; ebone_iter; ebone_iter = ebone_iter->next) { - is_ancestor(ebone_iter, ebone_act); + for (EditBone *ebone_iter = arm->edbo->first; ebone_iter; ebone_iter = ebone_iter->next) { + is_ancestor(ebone_iter, ebone_act); - if (ebone_iter->temp.ebone == ebone_act && EBONE_SELECTABLE(arm, ebone_iter)) - ED_armature_ebone_select_set(ebone_iter, true); - } + if (ebone_iter->temp.ebone == ebone_act && EBONE_SELECTABLE(arm, ebone_iter)) + ED_armature_ebone_select_set(ebone_iter, true); + } - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit); + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit); } static void select_similar_children_immediate(bContext *C) { - Object *obedit = CTX_data_edit_object(C); - bArmature *arm = obedit->data; - EditBone *ebone_act = CTX_data_active_bone(C); + Object *obedit = CTX_data_edit_object(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) { - if (ebone_iter->parent == ebone_act && EBONE_SELECTABLE(arm, ebone_iter)) { - ED_armature_ebone_select_set(ebone_iter, true); - } - } + for (EditBone *ebone_iter = arm->edbo->first; ebone_iter; ebone_iter = ebone_iter->next) { + if (ebone_iter->parent == ebone_act && EBONE_SELECTABLE(arm, ebone_iter)) { + ED_armature_ebone_select_set(ebone_iter, true); + } + } - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit); + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit); } static void select_similar_siblings(bContext *C) { - Object *obedit = CTX_data_edit_object(C); - bArmature *arm = obedit->data; - EditBone *ebone_act = CTX_data_active_bone(C); + Object *obedit = CTX_data_edit_object(C); + bArmature *arm = obedit->data; + EditBone *ebone_act = CTX_data_active_bone(C); - if (ebone_act->parent == NULL) { - return; - } + if (ebone_act->parent == NULL) { + return; + } - for (EditBone *ebone_iter = arm->edbo->first; ebone_iter; ebone_iter = ebone_iter->next) { - if (ebone_iter->parent == ebone_act->parent && EBONE_SELECTABLE(arm, ebone_iter)) { - ED_armature_ebone_select_set(ebone_iter, true); - } - } + for (EditBone *ebone_iter = arm->edbo->first; ebone_iter; ebone_iter = ebone_iter->next) { + if (ebone_iter->parent == ebone_act->parent && EBONE_SELECTABLE(arm, ebone_iter)) { + ED_armature_ebone_select_set(ebone_iter, true); + } + } - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit); + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit); } static int armature_select_similar_exec(bContext *C, wmOperator *op) { - /* Get props */ - int type = RNA_enum_get(op->ptr, "type"); - float thresh = RNA_float_get(op->ptr, "threshold"); + /* Get props */ + int type = RNA_enum_get(op->ptr, "type"); + float thresh = RNA_float_get(op->ptr, "threshold"); - /* Check for active bone */ - if (CTX_data_active_bone(C) == NULL) { - BKE_report(op->reports, RPT_ERROR, "Operation requires an active bone"); - return OPERATOR_CANCELLED; - } + /* Check for active bone */ + if (CTX_data_active_bone(C) == NULL) { + BKE_report(op->reports, RPT_ERROR, "Operation requires an active bone"); + return OPERATOR_CANCELLED; + } #define STRUCT_SIZE_AND_OFFSET(_struct, _member) \ - sizeof(((_struct *)NULL)->_member), offsetof(_struct, _member) - - switch (type) { - case SIMEDBONE_CHILDREN: - select_similar_children(C); - break; - case SIMEDBONE_CHILDREN_IMMEDIATE: - select_similar_children_immediate(C); - break; - case SIMEDBONE_SIBLINGS: - select_similar_siblings(C); - break; - case SIMEDBONE_LENGTH: - select_similar_length(C, thresh); - break; - case SIMEDBONE_DIRECTION: - select_similar_direction(C, thresh); - break; - case SIMEDBONE_PREFIX: - select_similar_prefix(C); - break; - case SIMEDBONE_SUFFIX: - select_similar_suffix(C); - break; - case SIMEDBONE_LAYER: - select_similar_layer(C); - break; - case SIMEDBONE_GROUP: - select_similar_data_pchan( - C, - STRUCT_SIZE_AND_OFFSET(bPoseChannel, agrp_index)); - break; - case SIMEDBONE_SHAPE: - select_similar_data_pchan( - C, - STRUCT_SIZE_AND_OFFSET(bPoseChannel, custom)); - break; - } + sizeof(((_struct *)NULL)->_member), offsetof(_struct, _member) + + switch (type) { + case SIMEDBONE_CHILDREN: + select_similar_children(C); + break; + case SIMEDBONE_CHILDREN_IMMEDIATE: + select_similar_children_immediate(C); + break; + case SIMEDBONE_SIBLINGS: + select_similar_siblings(C); + break; + case SIMEDBONE_LENGTH: + select_similar_length(C, thresh); + break; + case SIMEDBONE_DIRECTION: + select_similar_direction(C, thresh); + break; + case SIMEDBONE_PREFIX: + select_similar_prefix(C); + break; + case SIMEDBONE_SUFFIX: + select_similar_suffix(C); + break; + case SIMEDBONE_LAYER: + select_similar_layer(C); + break; + case SIMEDBONE_GROUP: + select_similar_data_pchan(C, STRUCT_SIZE_AND_OFFSET(bPoseChannel, agrp_index)); + break; + case SIMEDBONE_SHAPE: + select_similar_data_pchan(C, STRUCT_SIZE_AND_OFFSET(bPoseChannel, custom)); + break; + } #undef STRUCT_SIZE_AND_OFFSET - return OPERATOR_FINISHED; + return OPERATOR_FINISHED; } void ARMATURE_OT_select_similar(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Select Similar"; - ot->idname = "ARMATURE_OT_select_similar"; - - /* callback functions */ - ot->invoke = WM_menu_invoke; - ot->exec = armature_select_similar_exec; - ot->poll = ED_operator_editarmature; - ot->description = "Select similar bones by property types"; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - ot->prop = RNA_def_enum(ot->srna, "type", prop_similar_types, SIMEDBONE_LENGTH, "Type", ""); - RNA_def_float(ot->srna, "threshold", 0.1f, 0.0f, 1.0f, "Threshold", "", 0.0f, 1.0f); + /* identifiers */ + ot->name = "Select Similar"; + ot->idname = "ARMATURE_OT_select_similar"; + + /* callback functions */ + ot->invoke = WM_menu_invoke; + ot->exec = armature_select_similar_exec; + ot->poll = ED_operator_editarmature; + ot->description = "Select similar bones by property types"; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + ot->prop = RNA_def_enum(ot->srna, "type", prop_similar_types, SIMEDBONE_LENGTH, "Type", ""); + RNA_def_float(ot->srna, "threshold", 0.1f, 0.0f, 1.0f, "Threshold", "", 0.0f, 1.0f); } /* ********************* select hierarchy operator ************** */ @@ -1554,103 +1571,101 @@ void ARMATURE_OT_select_similar(wmOperatorType *ot) * selected we then keep the non-active objects untouched (selected/unselected). */ static int armature_select_hierarchy_exec(bContext *C, wmOperator *op) { - Object *ob = CTX_data_edit_object(C); - EditBone *ebone_active; - int direction = RNA_enum_get(op->ptr, "direction"); - const bool add_to_sel = RNA_boolean_get(op->ptr, "extend"); - bool changed = false; - bArmature *arm = (bArmature *)ob->data; - - ebone_active = arm->act_edbone; - if (ebone_active == NULL) { - return OPERATOR_CANCELLED; - } - - if (direction == BONE_SELECT_PARENT) { - if (ebone_active->parent) { - EditBone *ebone_parent; - - ebone_parent = ebone_active->parent; - - if (EBONE_SELECTABLE(arm, ebone_parent)) { - arm->act_edbone = ebone_parent; - - if (!add_to_sel) { - ED_armature_ebone_select_set(ebone_active, false); - } - ED_armature_ebone_select_set(ebone_parent, true); - - changed = true; - } - } - - } - else { /* BONE_SELECT_CHILD */ - EditBone *ebone_iter, *ebone_child = NULL; - int pass; - - /* first pass, only connected bones (the logical direct child) */ - for (pass = 0; pass < 2 && (ebone_child == NULL); pass++) { - for (ebone_iter = arm->edbo->first; ebone_iter; ebone_iter = ebone_iter->next) { - /* possible we have multiple children, some invisible */ - if (EBONE_SELECTABLE(arm, ebone_iter)) { - if (ebone_iter->parent == ebone_active) { - if ((pass == 1) || (ebone_iter->flag & BONE_CONNECTED)) { - ebone_child = ebone_iter; - break; - } - } - } - } - } - - if (ebone_child) { - arm->act_edbone = ebone_child; - - if (!add_to_sel) { - ED_armature_ebone_select_set(ebone_active, false); - } - ED_armature_ebone_select_set(ebone_child, true); - - changed = true; - } - } - - if (changed == false) { - return OPERATOR_CANCELLED; - } - - ED_armature_edit_sync_selection(arm->edbo); - - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); - - return OPERATOR_FINISHED; + Object *ob = CTX_data_edit_object(C); + EditBone *ebone_active; + int direction = RNA_enum_get(op->ptr, "direction"); + const bool add_to_sel = RNA_boolean_get(op->ptr, "extend"); + bool changed = false; + bArmature *arm = (bArmature *)ob->data; + + ebone_active = arm->act_edbone; + if (ebone_active == NULL) { + return OPERATOR_CANCELLED; + } + + if (direction == BONE_SELECT_PARENT) { + if (ebone_active->parent) { + EditBone *ebone_parent; + + ebone_parent = ebone_active->parent; + + if (EBONE_SELECTABLE(arm, ebone_parent)) { + arm->act_edbone = ebone_parent; + + if (!add_to_sel) { + ED_armature_ebone_select_set(ebone_active, false); + } + ED_armature_ebone_select_set(ebone_parent, true); + + changed = true; + } + } + } + else { /* BONE_SELECT_CHILD */ + EditBone *ebone_iter, *ebone_child = NULL; + int pass; + + /* first pass, only connected bones (the logical direct child) */ + for (pass = 0; pass < 2 && (ebone_child == NULL); pass++) { + for (ebone_iter = arm->edbo->first; ebone_iter; ebone_iter = ebone_iter->next) { + /* possible we have multiple children, some invisible */ + if (EBONE_SELECTABLE(arm, ebone_iter)) { + if (ebone_iter->parent == ebone_active) { + if ((pass == 1) || (ebone_iter->flag & BONE_CONNECTED)) { + ebone_child = ebone_iter; + break; + } + } + } + } + } + + if (ebone_child) { + arm->act_edbone = ebone_child; + + if (!add_to_sel) { + ED_armature_ebone_select_set(ebone_active, false); + } + ED_armature_ebone_select_set(ebone_child, true); + + changed = true; + } + } + + if (changed == false) { + return OPERATOR_CANCELLED; + } + + ED_armature_edit_sync_selection(arm->edbo); + + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); + + return OPERATOR_FINISHED; } void ARMATURE_OT_select_hierarchy(wmOperatorType *ot) { - static const EnumPropertyItem direction_items[] = { - {BONE_SELECT_PARENT, "PARENT", 0, "Select Parent", ""}, - {BONE_SELECT_CHILD, "CHILD", 0, "Select Child", ""}, - {0, NULL, 0, NULL, NULL}, - }; - - /* identifiers */ - ot->name = "Select Hierarchy"; - ot->idname = "ARMATURE_OT_select_hierarchy"; - ot->description = "Select immediate parent/children of selected bones"; - - /* api callbacks */ - ot->exec = armature_select_hierarchy_exec; - ot->poll = ED_operator_editarmature; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* props */ - RNA_def_enum(ot->srna, "direction", direction_items, - BONE_SELECT_PARENT, "Direction", ""); - RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend the selection"); + static const EnumPropertyItem direction_items[] = { + {BONE_SELECT_PARENT, "PARENT", 0, "Select Parent", ""}, + {BONE_SELECT_CHILD, "CHILD", 0, "Select Child", ""}, + {0, NULL, 0, NULL, NULL}, + }; + + /* identifiers */ + ot->name = "Select Hierarchy"; + ot->idname = "ARMATURE_OT_select_hierarchy"; + ot->description = "Select immediate parent/children of selected bones"; + + /* api callbacks */ + ot->exec = armature_select_hierarchy_exec; + ot->poll = ED_operator_editarmature; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* props */ + RNA_def_enum(ot->srna, "direction", direction_items, BONE_SELECT_PARENT, "Direction", ""); + RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend the selection"); } /****************** Mirror Select ****************/ @@ -1660,202 +1675,199 @@ void ARMATURE_OT_select_hierarchy(wmOperatorType *ot) */ static int armature_select_mirror_exec(bContext *C, wmOperator *op) { - ViewLayer *view_layer = CTX_data_view_layer(C); - const bool active_only = RNA_boolean_get(op->ptr, "only_active"); - const bool extend = RNA_boolean_get(op->ptr, "extend"); - - 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; - - EditBone *ebone, *ebone_mirror_act = NULL; - - for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { - const int flag = ED_armature_ebone_selectflag_get(ebone); - EBONE_PREV_FLAG_SET(ebone, flag); - } - - for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { - if (EBONE_SELECTABLE(arm, ebone)) { - EditBone *ebone_mirror; - int flag_new = extend ? EBONE_PREV_FLAG_GET(ebone) : 0; - - if ((ebone_mirror = ED_armature_ebone_get_mirrored(arm->edbo, ebone)) && - (EBONE_VISIBLE(arm, ebone_mirror))) - { - const int flag_mirror = EBONE_PREV_FLAG_GET(ebone_mirror); - flag_new |= flag_mirror; - - if (ebone == arm->act_edbone) { - ebone_mirror_act = ebone_mirror; - } - - /* skip all but the active or its mirror */ - if (active_only && !ELEM(arm->act_edbone, ebone, ebone_mirror)) { - continue; - } - } - - ED_armature_ebone_selectflag_set(ebone, flag_new); - } - } - - if (ebone_mirror_act) { - arm->act_edbone = ebone_mirror_act; - } - - ED_armature_edit_sync_selection(arm->edbo); - - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); - } - MEM_freeN(objects); - - return OPERATOR_FINISHED; + ViewLayer *view_layer = CTX_data_view_layer(C); + const bool active_only = RNA_boolean_get(op->ptr, "only_active"); + const bool extend = RNA_boolean_get(op->ptr, "extend"); + + 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; + + EditBone *ebone, *ebone_mirror_act = NULL; + + for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { + const int flag = ED_armature_ebone_selectflag_get(ebone); + EBONE_PREV_FLAG_SET(ebone, flag); + } + + for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { + if (EBONE_SELECTABLE(arm, ebone)) { + EditBone *ebone_mirror; + int flag_new = extend ? EBONE_PREV_FLAG_GET(ebone) : 0; + + if ((ebone_mirror = ED_armature_ebone_get_mirrored(arm->edbo, ebone)) && + (EBONE_VISIBLE(arm, ebone_mirror))) { + const int flag_mirror = EBONE_PREV_FLAG_GET(ebone_mirror); + flag_new |= flag_mirror; + + if (ebone == arm->act_edbone) { + ebone_mirror_act = ebone_mirror; + } + + /* skip all but the active or its mirror */ + if (active_only && !ELEM(arm->act_edbone, ebone, ebone_mirror)) { + continue; + } + } + + ED_armature_ebone_selectflag_set(ebone, flag_new); + } + } + + if (ebone_mirror_act) { + arm->act_edbone = ebone_mirror_act; + } + + ED_armature_edit_sync_selection(arm->edbo); + + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); + } + MEM_freeN(objects); + + return OPERATOR_FINISHED; } void ARMATURE_OT_select_mirror(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Flip Active/Selected Bone"; - ot->idname = "ARMATURE_OT_select_mirror"; - ot->description = "Mirror the bone selection"; - - /* api callbacks */ - ot->exec = armature_select_mirror_exec; - ot->poll = ED_operator_editarmature; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - RNA_def_boolean(ot->srna, "only_active", false, "Active Only", "Only operate on the active bone"); - RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend the selection"); + /* identifiers */ + ot->name = "Flip Active/Selected Bone"; + ot->idname = "ARMATURE_OT_select_mirror"; + ot->description = "Mirror the bone selection"; + + /* api callbacks */ + ot->exec = armature_select_mirror_exec; + ot->poll = ED_operator_editarmature; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + RNA_def_boolean( + ot->srna, "only_active", false, "Active Only", "Only operate on the active bone"); + RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend the selection"); } - /****************** Select Path ****************/ -static bool armature_shortest_path_select(bArmature *arm, EditBone *ebone_parent, EditBone *ebone_child, - bool use_parent, bool is_test) +static bool armature_shortest_path_select( + bArmature *arm, EditBone *ebone_parent, EditBone *ebone_child, bool use_parent, bool is_test) { - do { + do { - if (!use_parent && (ebone_child == ebone_parent)) - break; + if (!use_parent && (ebone_child == ebone_parent)) + break; - if (is_test) { - if (!EBONE_SELECTABLE(arm, ebone_child)) { - return false; - } - } - else { - ED_armature_ebone_selectflag_set(ebone_child, (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL)); - } + if (is_test) { + if (!EBONE_SELECTABLE(arm, ebone_child)) { + return false; + } + } + else { + ED_armature_ebone_selectflag_set(ebone_child, (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL)); + } - if (ebone_child == ebone_parent) - break; + if (ebone_child == ebone_parent) + break; - ebone_child = ebone_child->parent; - } while (true); + ebone_child = ebone_child->parent; + } while (true); - return true; + return true; } static int armature_shortest_path_pick_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - Object *obedit = CTX_data_edit_object(C); - bArmature *arm = obedit->data; - EditBone *ebone_src, *ebone_dst; - EditBone *ebone_isect_parent = NULL; - EditBone *ebone_isect_child[2]; - bool changed; - Base *base_dst = NULL; - - view3d_operator_needs_opengl(C); - - ebone_src = arm->act_edbone; - ebone_dst = get_nearest_bone(C, event->mval, false, &base_dst); - - /* fallback to object selection */ - if (ELEM(NULL, ebone_src, ebone_dst) || (ebone_src == ebone_dst)) { - return OPERATOR_PASS_THROUGH; - } - - if (base_dst && base_dst->object != obedit) { - /* Disconnected, ignore. */ - return OPERATOR_CANCELLED; - } - - ebone_isect_child[0] = ebone_src; - ebone_isect_child[1] = ebone_dst; - - - /* ensure 'ebone_src' is the parent of 'ebone_dst', or set 'ebone_isect_parent' */ - if (ED_armature_ebone_is_child_recursive(ebone_src, ebone_dst)) { - /* pass */ - } - else if (ED_armature_ebone_is_child_recursive(ebone_dst, ebone_src)) { - SWAP(EditBone *, ebone_src, ebone_dst); - } - else if ((ebone_isect_parent = ED_armature_ebone_find_shared_parent(ebone_isect_child, 2))) { - /* pass */ - } - else { - /* disconnected bones */ - return OPERATOR_CANCELLED; - } - - - if (ebone_isect_parent) { - if (armature_shortest_path_select(arm, ebone_isect_parent, ebone_src, false, true) && - armature_shortest_path_select(arm, ebone_isect_parent, ebone_dst, false, true)) - { - armature_shortest_path_select(arm, ebone_isect_parent, ebone_src, false, false); - armature_shortest_path_select(arm, ebone_isect_parent, ebone_dst, false, false); - changed = true; - } - else { - /* unselectable */ - changed = false; - } - } - else { - if (armature_shortest_path_select(arm, ebone_src, ebone_dst, true, true)) { - armature_shortest_path_select(arm, ebone_src, ebone_dst, true, false); - changed = true; - } - else { - /* unselectable */ - changed = false; - } - } - - if (changed) { - arm->act_edbone = ebone_dst; - ED_armature_edit_sync_selection(arm->edbo); - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit); - - return OPERATOR_FINISHED; - } - else { - BKE_report(op->reports, RPT_WARNING, "Unselectable bone in chain"); - return OPERATOR_CANCELLED; - } + Object *obedit = CTX_data_edit_object(C); + bArmature *arm = obedit->data; + EditBone *ebone_src, *ebone_dst; + EditBone *ebone_isect_parent = NULL; + EditBone *ebone_isect_child[2]; + bool changed; + Base *base_dst = NULL; + + view3d_operator_needs_opengl(C); + + ebone_src = arm->act_edbone; + ebone_dst = get_nearest_bone(C, event->mval, false, &base_dst); + + /* fallback to object selection */ + if (ELEM(NULL, ebone_src, ebone_dst) || (ebone_src == ebone_dst)) { + return OPERATOR_PASS_THROUGH; + } + + if (base_dst && base_dst->object != obedit) { + /* Disconnected, ignore. */ + return OPERATOR_CANCELLED; + } + + ebone_isect_child[0] = ebone_src; + ebone_isect_child[1] = ebone_dst; + + /* ensure 'ebone_src' is the parent of 'ebone_dst', or set 'ebone_isect_parent' */ + if (ED_armature_ebone_is_child_recursive(ebone_src, ebone_dst)) { + /* pass */ + } + else if (ED_armature_ebone_is_child_recursive(ebone_dst, ebone_src)) { + SWAP(EditBone *, ebone_src, ebone_dst); + } + else if ((ebone_isect_parent = ED_armature_ebone_find_shared_parent(ebone_isect_child, 2))) { + /* pass */ + } + else { + /* disconnected bones */ + return OPERATOR_CANCELLED; + } + + if (ebone_isect_parent) { + if (armature_shortest_path_select(arm, ebone_isect_parent, ebone_src, false, true) && + armature_shortest_path_select(arm, ebone_isect_parent, ebone_dst, false, true)) { + armature_shortest_path_select(arm, ebone_isect_parent, ebone_src, false, false); + armature_shortest_path_select(arm, ebone_isect_parent, ebone_dst, false, false); + changed = true; + } + else { + /* unselectable */ + changed = false; + } + } + else { + if (armature_shortest_path_select(arm, ebone_src, ebone_dst, true, true)) { + armature_shortest_path_select(arm, ebone_src, ebone_dst, true, false); + changed = true; + } + else { + /* unselectable */ + changed = false; + } + } + + if (changed) { + arm->act_edbone = ebone_dst; + ED_armature_edit_sync_selection(arm->edbo); + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit); + + return OPERATOR_FINISHED; + } + else { + BKE_report(op->reports, RPT_WARNING, "Unselectable bone in chain"); + return OPERATOR_CANCELLED; + } } void ARMATURE_OT_shortest_path_pick(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Pick Shortest Path"; - ot->idname = "ARMATURE_OT_shortest_path_pick"; - ot->description = "Select shortest path between two bones"; + /* identifiers */ + ot->name = "Pick Shortest Path"; + ot->idname = "ARMATURE_OT_shortest_path_pick"; + ot->description = "Select shortest path between two bones"; - /* api callbacks */ - ot->invoke = armature_shortest_path_pick_invoke; - ot->poll = ED_operator_editarmature; + /* api callbacks */ + ot->invoke = armature_shortest_path_pick_invoke; + ot->poll = ED_operator_editarmature; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* 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 5aec55bd1ed..afbeb8e4377 100644 --- a/source/blender/editors/armature/armature_skinning.c +++ b/source/blender/editors/armature/armature_skinning.c @@ -59,395 +59,436 @@ static int bone_skinnable_cb(Object *UNUSED(ob), Bone *bone, void *datap) { - /* Bones that are deforming - * are regarded to be "skinnable" and are eligible for - * auto-skinning. - * - * This function performs 2 functions: - * - * a) It returns 1 if the bone is skinnable. - * If we loop over all bones with this - * function, we can count the number of - * skinnable bones. - * b) If the pointer data is non null, - * it is treated like a handle to a - * bone pointer -- the bone pointer - * is set to point at this bone, and - * the pointer the handle points to - * is incremented to point to the - * next member of an array of pointers - * to bones. This way we can loop using - * this function to construct an array of - * pointers to bones that point to all - * skinnable bones. - */ - Bone ***hbone; - int a, segments; - struct { Object *armob; void *list; int heat; bool is_weight_paint; } *data = datap; - - if (!(data->is_weight_paint) || !(bone->flag & BONE_HIDDEN_P)) { - if (!(bone->flag & BONE_NO_DEFORM)) { - if (data->heat && data->armob->pose && BKE_pose_channel_find_name(data->armob->pose, bone->name)) - segments = bone->segments; - else - segments = 1; - - if (data->list != NULL) { - hbone = (Bone ***) &data->list; - - for (a = 0; a < segments; a++) { - **hbone = bone; - (*hbone)++; - } - } - return segments; - } - } - return 0; + /* Bones that are deforming + * are regarded to be "skinnable" and are eligible for + * auto-skinning. + * + * This function performs 2 functions: + * + * a) It returns 1 if the bone is skinnable. + * If we loop over all bones with this + * function, we can count the number of + * skinnable bones. + * b) If the pointer data is non null, + * it is treated like a handle to a + * bone pointer -- the bone pointer + * is set to point at this bone, and + * the pointer the handle points to + * is incremented to point to the + * next member of an array of pointers + * to bones. This way we can loop using + * this function to construct an array of + * pointers to bones that point to all + * skinnable bones. + */ + Bone ***hbone; + int a, segments; + struct { + Object *armob; + void *list; + int heat; + bool is_weight_paint; + } *data = datap; + + if (!(data->is_weight_paint) || !(bone->flag & BONE_HIDDEN_P)) { + if (!(bone->flag & BONE_NO_DEFORM)) { + if (data->heat && data->armob->pose && + BKE_pose_channel_find_name(data->armob->pose, bone->name)) + segments = bone->segments; + else + segments = 1; + + if (data->list != NULL) { + hbone = (Bone ***)&data->list; + + for (a = 0; a < segments; a++) { + **hbone = bone; + (*hbone)++; + } + } + return segments; + } + } + return 0; } static int vgroup_add_unique_bone_cb(Object *ob, Bone *bone, void *UNUSED(ptr)) { - /* This group creates a vertex group to ob that has the - * same name as bone (provided the bone is skinnable). - * If such a vertex group already exist the routine exits. - */ - if (!(bone->flag & BONE_NO_DEFORM)) { - if (!defgroup_find_name(ob, bone->name)) { - BKE_object_defgroup_add_name(ob, bone->name); - return 1; - } - } - return 0; + /* This group creates a vertex group to ob that has the + * same name as bone (provided the bone is skinnable). + * If such a vertex group already exist the routine exits. + */ + if (!(bone->flag & BONE_NO_DEFORM)) { + if (!defgroup_find_name(ob, bone->name)) { + BKE_object_defgroup_add_name(ob, bone->name); + return 1; + } + } + return 0; } static int dgroup_skinnable_cb(Object *ob, Bone *bone, void *datap) { - /* Bones that are deforming - * are regarded to be "skinnable" and are eligible for - * auto-skinning. - * - * This function performs 2 functions: - * - * a) If the bone is skinnable, it creates - * a vertex group for ob that has - * the name of the skinnable bone - * (if one doesn't exist already). - * b) If the pointer data is non null, - * it is treated like a handle to a - * bDeformGroup pointer -- the - * bDeformGroup pointer is set to point - * to the deform group with the bone's - * name, and the pointer the handle - * points to is incremented to point to the - * next member of an array of pointers - * to bDeformGroups. This way we can loop using - * this function to construct an array of - * pointers to bDeformGroups, all with names - * of skinnable bones. - */ - bDeformGroup ***hgroup, *defgroup = NULL; - int a, segments; - struct { Object *armob; void *list; int heat; bool is_weight_paint; } *data = datap; - bArmature *arm = data->armob->data; - - if (!data->is_weight_paint || !(bone->flag & BONE_HIDDEN_P)) { - if (!(bone->flag & BONE_NO_DEFORM)) { - if (data->heat && data->armob->pose && BKE_pose_channel_find_name(data->armob->pose, bone->name)) - segments = bone->segments; - else - segments = 1; - - if (!data->is_weight_paint || ((arm->layer & bone->layer) && (bone->flag & BONE_SELECTED))) { - if (!(defgroup = defgroup_find_name(ob, bone->name))) { - defgroup = BKE_object_defgroup_add_name(ob, bone->name); - } - else if (defgroup->flag & DG_LOCK_WEIGHT) { - /* In case vgroup already exists and is locked, do not modify it here. See T43814. */ - defgroup = NULL; - } - } - - if (data->list != NULL) { - hgroup = (bDeformGroup ***) &data->list; - - for (a = 0; a < segments; a++) { - **hgroup = defgroup; - (*hgroup)++; - } - } - return segments; - } - } - return 0; + /* Bones that are deforming + * are regarded to be "skinnable" and are eligible for + * auto-skinning. + * + * This function performs 2 functions: + * + * a) If the bone is skinnable, it creates + * a vertex group for ob that has + * the name of the skinnable bone + * (if one doesn't exist already). + * b) If the pointer data is non null, + * it is treated like a handle to a + * bDeformGroup pointer -- the + * bDeformGroup pointer is set to point + * to the deform group with the bone's + * name, and the pointer the handle + * points to is incremented to point to the + * next member of an array of pointers + * to bDeformGroups. This way we can loop using + * this function to construct an array of + * pointers to bDeformGroups, all with names + * of skinnable bones. + */ + bDeformGroup ***hgroup, *defgroup = NULL; + int a, segments; + struct { + Object *armob; + void *list; + int heat; + bool is_weight_paint; + } *data = datap; + bArmature *arm = data->armob->data; + + if (!data->is_weight_paint || !(bone->flag & BONE_HIDDEN_P)) { + if (!(bone->flag & BONE_NO_DEFORM)) { + if (data->heat && data->armob->pose && + BKE_pose_channel_find_name(data->armob->pose, bone->name)) + segments = bone->segments; + else + segments = 1; + + if (!data->is_weight_paint || ((arm->layer & bone->layer) && (bone->flag & BONE_SELECTED))) { + if (!(defgroup = defgroup_find_name(ob, bone->name))) { + defgroup = BKE_object_defgroup_add_name(ob, bone->name); + } + else if (defgroup->flag & DG_LOCK_WEIGHT) { + /* In case vgroup already exists and is locked, do not modify it here. See T43814. */ + defgroup = NULL; + } + } + + if (data->list != NULL) { + hgroup = (bDeformGroup ***)&data->list; + + for (a = 0; a < segments; a++) { + **hgroup = defgroup; + (*hgroup)++; + } + } + return segments; + } + } + return 0; } -static void envelope_bone_weighting( - Object *ob, Mesh *mesh, float (*verts)[3], int numbones, Bone **bonelist, - bDeformGroup **dgrouplist, bDeformGroup **dgroupflip, - float (*root)[3], float (*tip)[3], const int *selected, float scale) +static void envelope_bone_weighting(Object *ob, + Mesh *mesh, + float (*verts)[3], + int numbones, + Bone **bonelist, + bDeformGroup **dgrouplist, + bDeformGroup **dgroupflip, + float (*root)[3], + float (*tip)[3], + const int *selected, + float scale) { - /* Create vertex group weights from envelopes */ - - Bone *bone; - bDeformGroup *dgroup; - float distance; - int i, iflip, j; - bool use_topology = (mesh->editflag & ME_EDIT_MIRROR_TOPO) != 0; - bool use_mask = false; - - if ((ob->mode & OB_MODE_WEIGHT_PAINT) && - (mesh->editflag & (ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL))) - { - use_mask = true; - } - - /* for each vertex in the mesh */ - for (i = 0; i < mesh->totvert; i++) { - - if (use_mask && !(mesh->mvert[i].flag & SELECT)) { - continue; - } - - iflip = (dgroupflip) ? mesh_get_x_mirror_vert(ob, NULL, i, use_topology) : -1; - - /* for each skinnable bone */ - for (j = 0; j < numbones; ++j) { - if (!selected[j]) - continue; - - bone = bonelist[j]; - dgroup = dgrouplist[j]; - - /* store the distance-factor from the vertex to the bone */ - distance = distfactor_to_bone(verts[i], root[j], tip[j], - bone->rad_head * scale, bone->rad_tail * scale, bone->dist * scale); - - /* add the vert to the deform group if (weight != 0.0) */ - if (distance != 0.0f) - ED_vgroup_vert_add(ob, dgroup, i, distance, WEIGHT_REPLACE); - else - ED_vgroup_vert_remove(ob, dgroup, i); - - /* do same for mirror */ - if (dgroupflip && dgroupflip[j] && iflip != -1) { - if (distance != 0.0f) - ED_vgroup_vert_add(ob, dgroupflip[j], iflip, distance, - WEIGHT_REPLACE); - else - ED_vgroup_vert_remove(ob, dgroupflip[j], iflip); - } - } - } + /* Create vertex group weights from envelopes */ + + Bone *bone; + bDeformGroup *dgroup; + float distance; + int i, iflip, j; + bool use_topology = (mesh->editflag & ME_EDIT_MIRROR_TOPO) != 0; + bool use_mask = false; + + if ((ob->mode & OB_MODE_WEIGHT_PAINT) && + (mesh->editflag & (ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL))) { + use_mask = true; + } + + /* for each vertex in the mesh */ + for (i = 0; i < mesh->totvert; i++) { + + if (use_mask && !(mesh->mvert[i].flag & SELECT)) { + continue; + } + + iflip = (dgroupflip) ? mesh_get_x_mirror_vert(ob, NULL, i, use_topology) : -1; + + /* for each skinnable bone */ + for (j = 0; j < numbones; ++j) { + if (!selected[j]) + continue; + + bone = bonelist[j]; + dgroup = dgrouplist[j]; + + /* store the distance-factor from the vertex to the bone */ + distance = distfactor_to_bone(verts[i], + root[j], + tip[j], + bone->rad_head * scale, + bone->rad_tail * scale, + bone->dist * scale); + + /* add the vert to the deform group if (weight != 0.0) */ + if (distance != 0.0f) + ED_vgroup_vert_add(ob, dgroup, i, distance, WEIGHT_REPLACE); + else + ED_vgroup_vert_remove(ob, dgroup, i); + + /* do same for mirror */ + if (dgroupflip && dgroupflip[j] && iflip != -1) { + if (distance != 0.0f) + ED_vgroup_vert_add(ob, dgroupflip[j], iflip, distance, WEIGHT_REPLACE); + else + ED_vgroup_vert_remove(ob, dgroupflip[j], iflip); + } + } + } } -static void add_verts_to_dgroups( - ReportList *reports, Depsgraph *depsgraph, Scene *UNUSED(scene), Object *ob, Object *par, - int heat, const bool mirror) +static void add_verts_to_dgroups(ReportList *reports, + Depsgraph *depsgraph, + Scene *UNUSED(scene), + Object *ob, + Object *par, + int heat, + const bool mirror) { - /* This functions implements the automatic computation of vertex group - * weights, either through envelopes or using a heat equilibrium. - * - * This function can be called both when parenting a mesh to an armature, - * or in weightpaint + posemode. In the latter case selection is taken - * into account and vertex weights can be mirrored. - * - * The mesh vertex positions used are either the final deformed coords - * from the evaluated mesh in weightpaint mode, the final subsurf coords - * when parenting, or simply the original mesh coords. - */ - - bArmature *arm = par->data; - Bone **bonelist, *bone; - bDeformGroup **dgrouplist, **dgroupflip; - bDeformGroup *dgroup; - bPoseChannel *pchan; - Mesh *mesh; - Mat4 bbone_array[MAX_BBONE_SUBDIV], *bbone = NULL; - float (*root)[3], (*tip)[3], (*verts)[3]; - int *selected; - int numbones, vertsfilled = 0, i, j, segments = 0; - const bool wpmode = (ob->mode & OB_MODE_WEIGHT_PAINT); - struct { Object *armob; void *list; int heat; bool is_weight_paint; } looper_data; - - looper_data.armob = par; - looper_data.heat = heat; - looper_data.list = NULL; - looper_data.is_weight_paint = wpmode; - - /* count the number of skinnable bones */ - numbones = bone_looper(ob, arm->bonebase.first, &looper_data, bone_skinnable_cb); - - if (numbones == 0) - return; - - if (BKE_object_defgroup_data_create(ob->data) == NULL) - return; - - /* create an array of pointer to bones that are skinnable - * and fill it with all of the skinnable bones */ - bonelist = MEM_callocN(numbones * sizeof(Bone *), "bonelist"); - looper_data.list = bonelist; - bone_looper(ob, arm->bonebase.first, &looper_data, bone_skinnable_cb); - - /* create an array of pointers to the deform groups that - * correspond to the skinnable bones (creating them - * as necessary. */ - dgrouplist = MEM_callocN(numbones * sizeof(bDeformGroup *), "dgrouplist"); - dgroupflip = MEM_callocN(numbones * sizeof(bDeformGroup *), "dgroupflip"); - - looper_data.list = dgrouplist; - bone_looper(ob, arm->bonebase.first, &looper_data, dgroup_skinnable_cb); - - /* create an array of root and tip positions transformed into - * global coords */ - root = MEM_callocN(numbones * sizeof(float) * 3, "root"); - tip = MEM_callocN(numbones * sizeof(float) * 3, "tip"); - selected = MEM_callocN(numbones * sizeof(int), "selected"); - - for (j = 0; j < numbones; ++j) { - bone = bonelist[j]; - dgroup = dgrouplist[j]; - - /* handle bbone */ - if (heat) { - if (segments == 0) { - segments = 1; - bbone = NULL; - - if ((par->pose) && (pchan = BKE_pose_channel_find_name(par->pose, bone->name))) { - if (bone->segments > 1) { - segments = bone->segments; - BKE_pchan_bbone_spline_setup(pchan, true, false, bbone_array); - bbone = bbone_array; - } - } - } - - segments--; - } - - /* compute root and tip */ - if (bbone) { - mul_v3_m4v3(root[j], bone->arm_mat, bbone[segments].mat[3]); - if ((segments + 1) < bone->segments) { - mul_v3_m4v3(tip[j], bone->arm_mat, bbone[segments + 1].mat[3]); - } - else { - copy_v3_v3(tip[j], bone->arm_tail); - } - } - else { - copy_v3_v3(root[j], bone->arm_head); - copy_v3_v3(tip[j], bone->arm_tail); - } - - mul_m4_v3(par->obmat, root[j]); - mul_m4_v3(par->obmat, tip[j]); - - /* set selected */ - if (wpmode) { - if ((arm->layer & bone->layer) && (bone->flag & BONE_SELECTED)) - selected[j] = 1; - } - else - selected[j] = 1; - - /* find flipped group */ - if (dgroup && mirror) { - char name_flip[MAXBONENAME]; - - BLI_string_flip_side_name(name_flip, dgroup->name, false, sizeof(name_flip)); - dgroupflip[j] = defgroup_find_name(ob, name_flip); - } - } - - /* create verts */ - mesh = (Mesh *)ob->data; - verts = MEM_callocN(mesh->totvert * sizeof(*verts), "closestboneverts"); - - if (wpmode) { - /* if in weight paint mode, use final verts from evaluated mesh */ - Scene *scene_eval = DEG_get_evaluated_scene(depsgraph); - Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob); - Mesh *me_eval = mesh_get_eval_final(depsgraph, scene_eval, ob_eval, &CD_MASK_BAREMESH); - - BKE_mesh_foreach_mapped_vert_coords_get(me_eval, verts, mesh->totvert); - vertsfilled = 1; - } - else if (modifiers_findByType(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'. */ - subsurf_calculate_limit_positions(mesh, verts); - vertsfilled = 1; - } - - /* transform verts to global space */ - for (i = 0; i < mesh->totvert; i++) { - if (!vertsfilled) - copy_v3_v3(verts[i], mesh->mvert[i].co); - mul_m4_v3(ob->obmat, verts[i]); - } - - /* compute the weights based on gathered vertices and bones */ - if (heat) { - const char *error = NULL; - - heat_bone_weighting( - ob, mesh, verts, numbones, dgrouplist, dgroupflip, - root, tip, selected, &error); - if (error) { - BKE_report(reports, RPT_WARNING, error); - } - } - else { - envelope_bone_weighting( - ob, mesh, verts, numbones, bonelist, dgrouplist, - dgroupflip, root, tip, selected, mat4_to_scale(par->obmat)); - } - - /* only generated in some cases but can call anyway */ - ED_mesh_mirror_spatial_table(ob, NULL, NULL, NULL, 'e'); - - /* free the memory allocated */ - MEM_freeN(bonelist); - MEM_freeN(dgrouplist); - MEM_freeN(dgroupflip); - MEM_freeN(root); - MEM_freeN(tip); - MEM_freeN(selected); - MEM_freeN(verts); + /* This functions implements the automatic computation of vertex group + * weights, either through envelopes or using a heat equilibrium. + * + * This function can be called both when parenting a mesh to an armature, + * or in weightpaint + posemode. In the latter case selection is taken + * into account and vertex weights can be mirrored. + * + * The mesh vertex positions used are either the final deformed coords + * from the evaluated mesh in weightpaint mode, the final subsurf coords + * when parenting, or simply the original mesh coords. + */ + + bArmature *arm = par->data; + Bone **bonelist, *bone; + bDeformGroup **dgrouplist, **dgroupflip; + bDeformGroup *dgroup; + bPoseChannel *pchan; + Mesh *mesh; + Mat4 bbone_array[MAX_BBONE_SUBDIV], *bbone = NULL; + float(*root)[3], (*tip)[3], (*verts)[3]; + int *selected; + int numbones, vertsfilled = 0, i, j, segments = 0; + const bool wpmode = (ob->mode & OB_MODE_WEIGHT_PAINT); + struct { + Object *armob; + void *list; + int heat; + bool is_weight_paint; + } looper_data; + + looper_data.armob = par; + looper_data.heat = heat; + looper_data.list = NULL; + looper_data.is_weight_paint = wpmode; + + /* count the number of skinnable bones */ + numbones = bone_looper(ob, arm->bonebase.first, &looper_data, bone_skinnable_cb); + + if (numbones == 0) + return; + + if (BKE_object_defgroup_data_create(ob->data) == NULL) + return; + + /* create an array of pointer to bones that are skinnable + * and fill it with all of the skinnable bones */ + bonelist = MEM_callocN(numbones * sizeof(Bone *), "bonelist"); + looper_data.list = bonelist; + bone_looper(ob, arm->bonebase.first, &looper_data, bone_skinnable_cb); + + /* create an array of pointers to the deform groups that + * correspond to the skinnable bones (creating them + * as necessary. */ + dgrouplist = MEM_callocN(numbones * sizeof(bDeformGroup *), "dgrouplist"); + dgroupflip = MEM_callocN(numbones * sizeof(bDeformGroup *), "dgroupflip"); + + looper_data.list = dgrouplist; + bone_looper(ob, arm->bonebase.first, &looper_data, dgroup_skinnable_cb); + + /* create an array of root and tip positions transformed into + * global coords */ + root = MEM_callocN(numbones * sizeof(float) * 3, "root"); + tip = MEM_callocN(numbones * sizeof(float) * 3, "tip"); + selected = MEM_callocN(numbones * sizeof(int), "selected"); + + for (j = 0; j < numbones; ++j) { + bone = bonelist[j]; + dgroup = dgrouplist[j]; + + /* handle bbone */ + if (heat) { + if (segments == 0) { + segments = 1; + bbone = NULL; + + if ((par->pose) && (pchan = BKE_pose_channel_find_name(par->pose, bone->name))) { + if (bone->segments > 1) { + segments = bone->segments; + BKE_pchan_bbone_spline_setup(pchan, true, false, bbone_array); + bbone = bbone_array; + } + } + } + + segments--; + } + + /* compute root and tip */ + if (bbone) { + mul_v3_m4v3(root[j], bone->arm_mat, bbone[segments].mat[3]); + if ((segments + 1) < bone->segments) { + mul_v3_m4v3(tip[j], bone->arm_mat, bbone[segments + 1].mat[3]); + } + else { + copy_v3_v3(tip[j], bone->arm_tail); + } + } + else { + copy_v3_v3(root[j], bone->arm_head); + copy_v3_v3(tip[j], bone->arm_tail); + } + + mul_m4_v3(par->obmat, root[j]); + mul_m4_v3(par->obmat, tip[j]); + + /* set selected */ + if (wpmode) { + if ((arm->layer & bone->layer) && (bone->flag & BONE_SELECTED)) + selected[j] = 1; + } + else + selected[j] = 1; + + /* find flipped group */ + if (dgroup && mirror) { + char name_flip[MAXBONENAME]; + + BLI_string_flip_side_name(name_flip, dgroup->name, false, sizeof(name_flip)); + dgroupflip[j] = defgroup_find_name(ob, name_flip); + } + } + + /* create verts */ + mesh = (Mesh *)ob->data; + verts = MEM_callocN(mesh->totvert * sizeof(*verts), "closestboneverts"); + + if (wpmode) { + /* if in weight paint mode, use final verts from evaluated mesh */ + Scene *scene_eval = DEG_get_evaluated_scene(depsgraph); + Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob); + Mesh *me_eval = mesh_get_eval_final(depsgraph, scene_eval, ob_eval, &CD_MASK_BAREMESH); + + BKE_mesh_foreach_mapped_vert_coords_get(me_eval, verts, mesh->totvert); + vertsfilled = 1; + } + else if (modifiers_findByType(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'. */ + subsurf_calculate_limit_positions(mesh, verts); + vertsfilled = 1; + } + + /* transform verts to global space */ + for (i = 0; i < mesh->totvert; i++) { + if (!vertsfilled) + copy_v3_v3(verts[i], mesh->mvert[i].co); + mul_m4_v3(ob->obmat, verts[i]); + } + + /* compute the weights based on gathered vertices and bones */ + if (heat) { + const char *error = NULL; + + heat_bone_weighting( + ob, mesh, verts, numbones, dgrouplist, dgroupflip, root, tip, selected, &error); + if (error) { + BKE_report(reports, RPT_WARNING, error); + } + } + else { + envelope_bone_weighting(ob, + mesh, + verts, + numbones, + bonelist, + dgrouplist, + dgroupflip, + root, + tip, + selected, + mat4_to_scale(par->obmat)); + } + + /* only generated in some cases but can call anyway */ + ED_mesh_mirror_spatial_table(ob, NULL, NULL, NULL, 'e'); + + /* free the memory allocated */ + MEM_freeN(bonelist); + MEM_freeN(dgrouplist); + MEM_freeN(dgroupflip); + MEM_freeN(root); + MEM_freeN(tip); + MEM_freeN(selected); + MEM_freeN(verts); } -void ED_object_vgroup_calc_from_armature( - ReportList *reports, Depsgraph *depsgraph, Scene *scene, Object *ob, Object *par, - const int mode, const bool mirror) +void ED_object_vgroup_calc_from_armature(ReportList *reports, + Depsgraph *depsgraph, + Scene *scene, + Object *ob, + Object *par, + const int mode, + const bool mirror) { - /* Lets try to create some vertex groups - * based on the bones of the parent armature. - */ - bArmature *arm = par->data; - - if (mode == ARM_GROUPS_NAME) { - const int defbase_tot = BLI_listbase_count(&ob->defbase); - int defbase_add; - /* Traverse the bone list, trying to create empty vertex - * groups corresponding to the bone. - */ - defbase_add = bone_looper(ob, arm->bonebase.first, NULL, vgroup_add_unique_bone_cb); - - if (defbase_add) { - /* its possible there are DWeight's outside the range of the current - * objects deform groups, in this case the new groups wont be empty [#33889] */ - ED_vgroup_data_clamp_range(ob->data, defbase_tot); - } - } - else if (ELEM(mode, ARM_GROUPS_ENVELOPE, ARM_GROUPS_AUTO)) { - /* Traverse the bone list, trying to create vertex groups - * that are populated with the vertices for which the - * bone is closest. - */ - add_verts_to_dgroups(reports, depsgraph, scene, ob, par, (mode == ARM_GROUPS_AUTO), mirror); - } + /* Lets try to create some vertex groups + * based on the bones of the parent armature. + */ + bArmature *arm = par->data; + + if (mode == ARM_GROUPS_NAME) { + const int defbase_tot = BLI_listbase_count(&ob->defbase); + int defbase_add; + /* Traverse the bone list, trying to create empty vertex + * groups corresponding to the bone. + */ + defbase_add = bone_looper(ob, arm->bonebase.first, NULL, vgroup_add_unique_bone_cb); + + if (defbase_add) { + /* its possible there are DWeight's outside the range of the current + * objects deform groups, in this case the new groups wont be empty [#33889] */ + ED_vgroup_data_clamp_range(ob->data, defbase_tot); + } + } + else if (ELEM(mode, ARM_GROUPS_ENVELOPE, ARM_GROUPS_AUTO)) { + /* Traverse the bone list, trying to create vertex groups + * that are populated with the vertices for which the + * bone is closest. + */ + add_verts_to_dgroups(reports, depsgraph, scene, ob, par, (mode == ARM_GROUPS_AUTO), mirror); + } } diff --git a/source/blender/editors/armature/armature_utils.c b/source/blender/editors/armature/armature_utils.c index 465209eb9c5..50289197ed4 100644 --- a/source/blender/editors/armature/armature_utils.c +++ b/source/blender/editors/armature/armature_utils.c @@ -50,34 +50,34 @@ /* Sync selection to parent for connected children */ void ED_armature_edit_sync_selection(ListBase *edbo) { - EditBone *ebo; - - for (ebo = edbo->first; ebo; ebo = ebo->next) { - /* if bone is not selectable, we shouldn't alter this setting... */ - if ((ebo->flag & BONE_UNSELECTABLE) == 0) { - if ((ebo->flag & BONE_CONNECTED) && (ebo->parent)) { - if (ebo->parent->flag & BONE_TIPSEL) - ebo->flag |= BONE_ROOTSEL; - else - ebo->flag &= ~BONE_ROOTSEL; - } - - if ((ebo->flag & BONE_TIPSEL) && (ebo->flag & BONE_ROOTSEL)) - ebo->flag |= BONE_SELECTED; - else - ebo->flag &= ~BONE_SELECTED; - } - } + EditBone *ebo; + + for (ebo = edbo->first; ebo; ebo = ebo->next) { + /* if bone is not selectable, we shouldn't alter this setting... */ + if ((ebo->flag & BONE_UNSELECTABLE) == 0) { + if ((ebo->flag & BONE_CONNECTED) && (ebo->parent)) { + if (ebo->parent->flag & BONE_TIPSEL) + ebo->flag |= BONE_ROOTSEL; + else + ebo->flag &= ~BONE_ROOTSEL; + } + + if ((ebo->flag & BONE_TIPSEL) && (ebo->flag & BONE_ROOTSEL)) + ebo->flag |= BONE_SELECTED; + else + ebo->flag &= ~BONE_SELECTED; + } + } } void ED_armature_edit_validate_active(struct bArmature *arm) { - EditBone *ebone = arm->act_edbone; + EditBone *ebone = arm->act_edbone; - if (ebone) { - if (ebone->flag & BONE_HIDDEN_A) - arm->act_edbone = NULL; - } + if (ebone) { + if (ebone->flag & BONE_HIDDEN_A) + arm->act_edbone = NULL; + } } /* *************************************************************** */ @@ -85,31 +85,30 @@ void ED_armature_edit_validate_active(struct bArmature *arm) /* XXX bone_looper is only to be used when we want to access settings * (i.e. editability/visibility/selected) that context doesn't offer */ -int bone_looper(Object *ob, Bone *bone, void *data, - int (*bone_func)(Object *, Bone *, void *)) +int bone_looper(Object *ob, Bone *bone, void *data, int (*bone_func)(Object *, Bone *, void *)) { - /* We want to apply the function bone_func to every bone - * in an armature -- feed bone_looper the first bone and - * a pointer to the bone_func and watch it go!. The int count - * can be useful for counting bones with a certain property - * (e.g. skinnable) - */ - int count = 0; + /* We want to apply the function bone_func to every bone + * in an armature -- feed bone_looper the first bone and + * a pointer to the bone_func and watch it go!. The int count + * can be useful for counting bones with a certain property + * (e.g. skinnable) + */ + int count = 0; - if (bone) { - /* only do bone_func if the bone is non null */ - count += bone_func(ob, bone, data); + if (bone) { + /* only do bone_func if the bone is non null */ + count += bone_func(ob, bone, data); - /* try to execute bone_func for the first child */ - count += bone_looper(ob, bone->childbase.first, data, bone_func); + /* try to execute bone_func for the first child */ + count += bone_looper(ob, bone->childbase.first, data, bone_func); - /* try to execute bone_func for the next bone at this - * depth of the recursion. - */ - count += bone_looper(ob, bone->next, data, bone_func); - } + /* try to execute bone_func for the next bone at this + * depth of the recursion. + */ + count += bone_looper(ob, bone->next, data, bone_func); + } - return count; + return count; } /* *************************************************************** */ @@ -117,25 +116,25 @@ int bone_looper(Object *ob, Bone *bone, void *data, void bone_free(bArmature *arm, EditBone *bone) { - if (arm->act_edbone == bone) - arm->act_edbone = NULL; + if (arm->act_edbone == bone) + arm->act_edbone = NULL; - if (bone->prop) { - IDP_FreeProperty(bone->prop); - MEM_freeN(bone->prop); - } + if (bone->prop) { + IDP_FreeProperty(bone->prop); + MEM_freeN(bone->prop); + } - /* Clear references from other edit bones. */ - for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { - if (ebone->bbone_next == bone) { - ebone->bbone_next = NULL; - } - if (ebone->bbone_prev == bone) { - ebone->bbone_prev = NULL; - } - } + /* Clear references from other edit bones. */ + for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { + if (ebone->bbone_next == bone) { + ebone->bbone_next = NULL; + } + if (ebone->bbone_prev == bone) { + ebone->bbone_prev = NULL; + } + } - BLI_freelinkN(arm->edbo, bone); + BLI_freelinkN(arm->edbo, bone); } /** @@ -143,33 +142,33 @@ void bone_free(bArmature *arm, EditBone *bone) */ void ED_armature_ebone_remove_ex(bArmature *arm, EditBone *exBone, bool clear_connected) { - EditBone *curBone; + EditBone *curBone; - /* Find any bones that refer to this bone */ - for (curBone = arm->edbo->first; curBone; curBone = curBone->next) { - if (curBone->parent == exBone) { - curBone->parent = exBone->parent; - if (clear_connected) { - curBone->flag &= ~BONE_CONNECTED; - } - } - } + /* Find any bones that refer to this bone */ + for (curBone = arm->edbo->first; curBone; curBone = curBone->next) { + if (curBone->parent == exBone) { + curBone->parent = exBone->parent; + if (clear_connected) { + curBone->flag &= ~BONE_CONNECTED; + } + } + } - bone_free(arm, exBone); + bone_free(arm, exBone); } void ED_armature_ebone_remove(bArmature *arm, EditBone *exBone) { - ED_armature_ebone_remove_ex(arm, exBone, true); + ED_armature_ebone_remove_ex(arm, exBone, true); } bool ED_armature_ebone_is_child_recursive(EditBone *ebone_parent, EditBone *ebone_child) { - for (ebone_child = ebone_child->parent; ebone_child; ebone_child = ebone_child->parent) { - if (ebone_child == ebone_parent) - return true; - } - return false; + for (ebone_child = ebone_child->parent; ebone_child; ebone_child = ebone_child->parent) { + if (ebone_child == ebone_parent) + return true; + } + return false; } /** @@ -179,81 +178,82 @@ 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 unsigned int ebone_child_tot) { - unsigned int i; - EditBone *ebone_iter; + unsigned int i; + EditBone *ebone_iter; #define EBONE_TEMP_UINT(ebone) (*((unsigned int *)(&((ebone)->temp)))) - /* clear all */ - for (i = 0; i < ebone_child_tot; i++) { - for (ebone_iter = ebone_child[i]; ebone_iter; ebone_iter = ebone_iter->parent) { - EBONE_TEMP_UINT(ebone_iter) = 0; - } - } - - /* accumulate */ - for (i = 0; i < ebone_child_tot; i++) { - for (ebone_iter = ebone_child[i]->parent; ebone_iter; ebone_iter = ebone_iter->parent) { - EBONE_TEMP_UINT(ebone_iter) += 1; - } - } - - /* only need search the first chain */ - for (ebone_iter = ebone_child[0]->parent; ebone_iter; ebone_iter = ebone_iter->parent) { - if (EBONE_TEMP_UINT(ebone_iter) == ebone_child_tot) { - return ebone_iter; - } - } + /* clear all */ + for (i = 0; i < ebone_child_tot; i++) { + for (ebone_iter = ebone_child[i]; ebone_iter; ebone_iter = ebone_iter->parent) { + EBONE_TEMP_UINT(ebone_iter) = 0; + } + } + + /* accumulate */ + for (i = 0; i < ebone_child_tot; i++) { + for (ebone_iter = ebone_child[i]->parent; ebone_iter; ebone_iter = ebone_iter->parent) { + EBONE_TEMP_UINT(ebone_iter) += 1; + } + } + + /* only need search the first chain */ + for (ebone_iter = ebone_child[0]->parent; ebone_iter; ebone_iter = ebone_iter->parent) { + if (EBONE_TEMP_UINT(ebone_iter) == ebone_child_tot) { + return ebone_iter; + } + } #undef EBONE_TEMP_UINT - return NULL; + return NULL; } void ED_armature_ebone_to_mat3(EditBone *ebone, float mat[3][3]) { - float delta[3]; + float delta[3]; - /* Find the current bone matrix */ - sub_v3_v3v3(delta, ebone->tail, ebone->head); - vec_roll_to_mat3(delta, ebone->roll, mat); + /* Find the current bone matrix */ + sub_v3_v3v3(delta, ebone->tail, ebone->head); + vec_roll_to_mat3(delta, ebone->roll, mat); } void ED_armature_ebone_to_mat4(EditBone *ebone, float mat[4][4]) { - float m3[3][3]; + float m3[3][3]; - ED_armature_ebone_to_mat3(ebone, m3); + ED_armature_ebone_to_mat3(ebone, m3); - copy_m4_m3(mat, m3); - copy_v3_v3(mat[3], ebone->head); + copy_m4_m3(mat, m3); + copy_v3_v3(mat[3], ebone->head); } void ED_armature_ebone_from_mat3(EditBone *ebone, float mat[3][3]) { - float vec[3], roll; - const float len = len_v3v3(ebone->head, ebone->tail); + float vec[3], roll; + const float len = len_v3v3(ebone->head, ebone->tail); - mat3_to_vec_roll(mat, vec, &roll); + mat3_to_vec_roll(mat, vec, &roll); - madd_v3_v3v3fl(ebone->tail, ebone->head, vec, len); - ebone->roll = roll; + madd_v3_v3v3fl(ebone->tail, ebone->head, vec, len); + ebone->roll = roll; } void ED_armature_ebone_from_mat4(EditBone *ebone, float mat[4][4]) { - float mat3[3][3]; + float mat3[3][3]; - copy_m3_m4(mat3, mat); - /* We want normalized matrix here, to be consistent with ebone_to_mat. */ - BLI_ASSERT_UNIT_M3(mat3); + copy_m3_m4(mat3, mat); + /* We want normalized matrix here, to be consistent with ebone_to_mat. */ + BLI_ASSERT_UNIT_M3(mat3); - sub_v3_v3(ebone->tail, ebone->head); - copy_v3_v3(ebone->head, mat[3]); - add_v3_v3(ebone->tail, mat[3]); - ED_armature_ebone_from_mat3(ebone, mat3); + sub_v3_v3(ebone->tail, ebone->head); + copy_v3_v3(ebone->head, mat[3]); + add_v3_v3(ebone->tail, mat[3]); + ED_armature_ebone_from_mat3(ebone, mat3); } /** @@ -261,10 +261,9 @@ void ED_armature_ebone_from_mat4(EditBone *ebone, float mat[4][4]) */ EditBone *ED_armature_ebone_find_name(const ListBase *edbo, const char *name) { - return BLI_findstring(edbo, name, offsetof(EditBone, name)); + return BLI_findstring(edbo, name, offsetof(EditBone, name)); } - /* *************************************************************** */ /* Mirroring */ @@ -273,18 +272,18 @@ EditBone *ED_armature_ebone_find_name(const ListBase *edbo, const char *name) */ EditBone *ED_armature_ebone_get_mirrored(const ListBase *edbo, EditBone *ebo) { - char name_flip[MAXBONENAME]; + char name_flip[MAXBONENAME]; - if (ebo == NULL) - return NULL; + if (ebo == NULL) + return NULL; - BLI_string_flip_side_name(name_flip, ebo->name, false, sizeof(name_flip)); + BLI_string_flip_side_name(name_flip, ebo->name, false, sizeof(name_flip)); - if (!STREQ(name_flip, ebo->name)) { - return ED_armature_ebone_find_name(edbo, name_flip); - } + if (!STREQ(name_flip, ebo->name)) { + return ED_armature_ebone_find_name(edbo, name_flip); + } - return NULL; + return NULL; } /* ------------------------------------- */ @@ -293,70 +292,69 @@ EditBone *ED_armature_ebone_get_mirrored(const ListBase *edbo, EditBone *ebo) * it leaves mirrored bones selected then too, which is a good indication of what happened */ void armature_select_mirrored_ex(bArmature *arm, const int flag) { - BLI_assert((flag & ~(BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL)) == 0); - /* Select mirrored bones */ - if (arm->flag & ARM_MIRROR_EDIT) { - EditBone *curBone, *ebone_mirr; - - for (curBone = arm->edbo->first; curBone; curBone = curBone->next) { - if (arm->layer & curBone->layer) { - if (curBone->flag & flag) { - ebone_mirr = ED_armature_ebone_get_mirrored(arm->edbo, curBone); - if (ebone_mirr) - ebone_mirr->flag |= (curBone->flag & flag); - } - } - } - } + BLI_assert((flag & ~(BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL)) == 0); + /* Select mirrored bones */ + if (arm->flag & ARM_MIRROR_EDIT) { + EditBone *curBone, *ebone_mirr; + for (curBone = arm->edbo->first; curBone; curBone = curBone->next) { + if (arm->layer & curBone->layer) { + if (curBone->flag & flag) { + ebone_mirr = ED_armature_ebone_get_mirrored(arm->edbo, curBone); + if (ebone_mirr) + ebone_mirr->flag |= (curBone->flag & flag); + } + } + } + } } void armature_select_mirrored(bArmature *arm) { - armature_select_mirrored_ex(arm, BONE_SELECTED); + armature_select_mirrored_ex(arm, BONE_SELECTED); } void armature_tag_select_mirrored(bArmature *arm) { - EditBone *curBone; - - /* always untag */ - for (curBone = arm->edbo->first; curBone; curBone = curBone->next) { - curBone->flag &= ~BONE_DONE; - } - - /* Select mirrored bones */ - if (arm->flag & ARM_MIRROR_EDIT) { - for (curBone = arm->edbo->first; curBone; curBone = curBone->next) { - if (arm->layer & curBone->layer) { - if (curBone->flag & (BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL)) { - EditBone *ebone_mirr = ED_armature_ebone_get_mirrored(arm->edbo, curBone); - if (ebone_mirr && (ebone_mirr->flag & BONE_SELECTED) == 0) { - ebone_mirr->flag |= BONE_DONE; - } - } - } - } - - for (curBone = arm->edbo->first; curBone; curBone = curBone->next) { - if (curBone->flag & BONE_DONE) { - EditBone *ebone_mirr = ED_armature_ebone_get_mirrored(arm->edbo, curBone); - curBone->flag |= ebone_mirr->flag & (BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL); - } - } - } + EditBone *curBone; + + /* always untag */ + for (curBone = arm->edbo->first; curBone; curBone = curBone->next) { + curBone->flag &= ~BONE_DONE; + } + + /* Select mirrored bones */ + if (arm->flag & ARM_MIRROR_EDIT) { + for (curBone = arm->edbo->first; curBone; curBone = curBone->next) { + if (arm->layer & curBone->layer) { + if (curBone->flag & (BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL)) { + EditBone *ebone_mirr = ED_armature_ebone_get_mirrored(arm->edbo, curBone); + if (ebone_mirr && (ebone_mirr->flag & BONE_SELECTED) == 0) { + ebone_mirr->flag |= BONE_DONE; + } + } + } + } + + for (curBone = arm->edbo->first; curBone; curBone = curBone->next) { + if (curBone->flag & BONE_DONE) { + EditBone *ebone_mirr = ED_armature_ebone_get_mirrored(arm->edbo, curBone); + curBone->flag |= ebone_mirr->flag & (BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL); + } + } + } } /* only works when tagged */ void armature_tag_unselect(bArmature *arm) { - EditBone *curBone; + EditBone *curBone; - for (curBone = arm->edbo->first; curBone; curBone = curBone->next) { - if (curBone->flag & BONE_DONE) { - curBone->flag &= ~(BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL | BONE_DONE); - } - } + for (curBone = arm->edbo->first; curBone; curBone = curBone->next) { + if (curBone->flag & BONE_DONE) { + curBone->flag &= ~(BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL | BONE_DONE); + } + } } /* ------------------------------------- */ @@ -365,186 +363,189 @@ void armature_tag_unselect(bArmature *arm) /* context; editmode armature, with mirror editing enabled */ void ED_armature_edit_transform_mirror_update(Object *obedit) { - bArmature *arm = obedit->data; - EditBone *ebo, *eboflip; - - for (ebo = arm->edbo->first; ebo; ebo = ebo->next) { - /* no layer check, correct mirror is more important */ - if (ebo->flag & (BONE_TIPSEL | BONE_ROOTSEL)) { - eboflip = ED_armature_ebone_get_mirrored(arm->edbo, ebo); - - if (eboflip) { - /* we assume X-axis flipping for now */ - if (ebo->flag & BONE_TIPSEL) { - EditBone *children; - - eboflip->tail[0] = -ebo->tail[0]; - eboflip->tail[1] = ebo->tail[1]; - eboflip->tail[2] = ebo->tail[2]; - eboflip->rad_tail = ebo->rad_tail; - eboflip->roll = -ebo->roll; - eboflip->curveOutX = -ebo->curveOutX; - eboflip->roll2 = -ebo->roll2; - - /* Also move connected children, in case children's name aren't mirrored properly */ - for (children = arm->edbo->first; children; children = children->next) { - if (children->parent == eboflip && children->flag & BONE_CONNECTED) { - copy_v3_v3(children->head, eboflip->tail); - children->rad_head = ebo->rad_tail; - } - } - } - if (ebo->flag & BONE_ROOTSEL) { - eboflip->head[0] = -ebo->head[0]; - eboflip->head[1] = ebo->head[1]; - eboflip->head[2] = ebo->head[2]; - eboflip->rad_head = ebo->rad_head; - eboflip->roll = -ebo->roll; - eboflip->curveInX = -ebo->curveInX; - eboflip->roll1 = -ebo->roll1; - - /* Also move connected parent, in case parent's name isn't mirrored properly */ - if (eboflip->parent && eboflip->flag & BONE_CONNECTED) { - EditBone *parent = eboflip->parent; - copy_v3_v3(parent->tail, eboflip->head); - parent->rad_tail = ebo->rad_head; - } - } - if (ebo->flag & BONE_SELECTED) { - eboflip->dist = ebo->dist; - eboflip->roll = -ebo->roll; - eboflip->xwidth = ebo->xwidth; - eboflip->zwidth = ebo->zwidth; - - eboflip->curveInX = -ebo->curveInX; - eboflip->curveOutX = -ebo->curveOutX; - eboflip->roll1 = -ebo->roll1; - eboflip->roll2 = -ebo->roll2; - } - } - } - } + bArmature *arm = obedit->data; + EditBone *ebo, *eboflip; + + for (ebo = arm->edbo->first; ebo; ebo = ebo->next) { + /* no layer check, correct mirror is more important */ + if (ebo->flag & (BONE_TIPSEL | BONE_ROOTSEL)) { + eboflip = ED_armature_ebone_get_mirrored(arm->edbo, ebo); + + if (eboflip) { + /* we assume X-axis flipping for now */ + if (ebo->flag & BONE_TIPSEL) { + EditBone *children; + + eboflip->tail[0] = -ebo->tail[0]; + eboflip->tail[1] = ebo->tail[1]; + eboflip->tail[2] = ebo->tail[2]; + eboflip->rad_tail = ebo->rad_tail; + eboflip->roll = -ebo->roll; + eboflip->curveOutX = -ebo->curveOutX; + eboflip->roll2 = -ebo->roll2; + + /* Also move connected children, in case children's name aren't mirrored properly */ + for (children = arm->edbo->first; children; children = children->next) { + if (children->parent == eboflip && children->flag & BONE_CONNECTED) { + copy_v3_v3(children->head, eboflip->tail); + children->rad_head = ebo->rad_tail; + } + } + } + if (ebo->flag & BONE_ROOTSEL) { + eboflip->head[0] = -ebo->head[0]; + eboflip->head[1] = ebo->head[1]; + eboflip->head[2] = ebo->head[2]; + eboflip->rad_head = ebo->rad_head; + eboflip->roll = -ebo->roll; + eboflip->curveInX = -ebo->curveInX; + eboflip->roll1 = -ebo->roll1; + + /* Also move connected parent, in case parent's name isn't mirrored properly */ + if (eboflip->parent && eboflip->flag & BONE_CONNECTED) { + EditBone *parent = eboflip->parent; + copy_v3_v3(parent->tail, eboflip->head); + parent->rad_tail = ebo->rad_head; + } + } + if (ebo->flag & BONE_SELECTED) { + eboflip->dist = ebo->dist; + eboflip->roll = -ebo->roll; + eboflip->xwidth = ebo->xwidth; + eboflip->zwidth = ebo->zwidth; + + eboflip->curveInX = -ebo->curveInX; + eboflip->curveOutX = -ebo->curveOutX; + eboflip->roll1 = -ebo->roll1; + eboflip->roll2 = -ebo->roll2; + } + } + } + } } /* *************************************************************** */ /* 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) -{ - EditBone *eBone; - EditBone *eBoneAct = NULL; - EditBone *eBoneTest = NULL; - Bone *curBone; - - for (curBone = bones->first; curBone; curBone = curBone->next) { - eBone = MEM_callocN(sizeof(EditBone), "make_editbone"); - eBone->temp.bone = curBone; - - /* Copy relevant data from bone to eBone - * Keep selection logic in sync with ED_armature_edit_sync_selection. - */ - eBone->parent = parent; - BLI_strncpy(eBone->name, curBone->name, sizeof(eBone->name)); - eBone->flag = curBone->flag; - - /* fix selection flags */ - if (eBone->flag & BONE_SELECTED) { - /* if the bone is selected the copy its root selection to the parents tip */ - eBone->flag |= BONE_TIPSEL; - if (eBone->parent && (eBone->flag & BONE_CONNECTED)) { - eBone->parent->flag |= BONE_TIPSEL; - } - - /* For connected bones, take care when changing the selection when we have a connected parent, - * this flag is a copy of '(eBone->parent->flag & BONE_TIPSEL)'. */ - eBone->flag |= BONE_ROOTSEL; - } - else { - /* if the bone is not selected, but connected to its parent - * always use the parents tip selection state */ - if (eBone->parent && (eBone->flag & BONE_CONNECTED)) { - eBone->flag &= ~BONE_ROOTSEL; - } - } - - copy_v3_v3(eBone->head, curBone->arm_head); - copy_v3_v3(eBone->tail, curBone->arm_tail); - eBone->roll = curBone->arm_roll; - - /* rest of stuff copy */ - eBone->length = curBone->length; - eBone->dist = curBone->dist; - eBone->weight = curBone->weight; - eBone->xwidth = curBone->xwidth; - eBone->zwidth = curBone->zwidth; - eBone->rad_head = curBone->rad_head; - eBone->rad_tail = curBone->rad_tail; - eBone->segments = curBone->segments; - eBone->layer = curBone->layer; - - /* Bendy-Bone parameters */ - eBone->roll1 = curBone->roll1; - eBone->roll2 = curBone->roll2; - eBone->curveInX = curBone->curveInX; - eBone->curveInY = curBone->curveInY; - eBone->curveOutX = curBone->curveOutX; - eBone->curveOutY = curBone->curveOutY; - eBone->ease1 = curBone->ease1; - eBone->ease2 = curBone->ease2; - eBone->scaleIn = curBone->scaleIn; - eBone->scaleOut = curBone->scaleOut; - - eBone->bbone_prev_type = curBone->bbone_prev_type; - eBone->bbone_next_type = curBone->bbone_next_type; - - if (curBone->prop) - eBone->prop = IDP_CopyProperty(curBone->prop); - - BLI_addtail(edbo, eBone); - - /* Add children if necessary */ - if (curBone->childbase.first) { - eBoneTest = make_boneList_rec(edbo, &curBone->childbase, eBone, actBone); - if (eBoneTest) - eBoneAct = eBoneTest; - } - - if (curBone == actBone) - eBoneAct = eBone; - } - - return eBoneAct; +static EditBone *make_boneList_rec(ListBase *edbo, + ListBase *bones, + EditBone *parent, + Bone *actBone) +{ + EditBone *eBone; + EditBone *eBoneAct = NULL; + EditBone *eBoneTest = NULL; + Bone *curBone; + + for (curBone = bones->first; curBone; curBone = curBone->next) { + eBone = MEM_callocN(sizeof(EditBone), "make_editbone"); + eBone->temp.bone = curBone; + + /* Copy relevant data from bone to eBone + * Keep selection logic in sync with ED_armature_edit_sync_selection. + */ + eBone->parent = parent; + BLI_strncpy(eBone->name, curBone->name, sizeof(eBone->name)); + eBone->flag = curBone->flag; + + /* fix selection flags */ + if (eBone->flag & BONE_SELECTED) { + /* if the bone is selected the copy its root selection to the parents tip */ + eBone->flag |= BONE_TIPSEL; + if (eBone->parent && (eBone->flag & BONE_CONNECTED)) { + eBone->parent->flag |= BONE_TIPSEL; + } + + /* For connected bones, take care when changing the selection when we have a connected parent, + * this flag is a copy of '(eBone->parent->flag & BONE_TIPSEL)'. */ + eBone->flag |= BONE_ROOTSEL; + } + else { + /* if the bone is not selected, but connected to its parent + * always use the parents tip selection state */ + if (eBone->parent && (eBone->flag & BONE_CONNECTED)) { + eBone->flag &= ~BONE_ROOTSEL; + } + } + + copy_v3_v3(eBone->head, curBone->arm_head); + copy_v3_v3(eBone->tail, curBone->arm_tail); + eBone->roll = curBone->arm_roll; + + /* rest of stuff copy */ + eBone->length = curBone->length; + eBone->dist = curBone->dist; + eBone->weight = curBone->weight; + eBone->xwidth = curBone->xwidth; + eBone->zwidth = curBone->zwidth; + eBone->rad_head = curBone->rad_head; + eBone->rad_tail = curBone->rad_tail; + eBone->segments = curBone->segments; + eBone->layer = curBone->layer; + + /* Bendy-Bone parameters */ + eBone->roll1 = curBone->roll1; + eBone->roll2 = curBone->roll2; + eBone->curveInX = curBone->curveInX; + eBone->curveInY = curBone->curveInY; + eBone->curveOutX = curBone->curveOutX; + eBone->curveOutY = curBone->curveOutY; + eBone->ease1 = curBone->ease1; + eBone->ease2 = curBone->ease2; + eBone->scaleIn = curBone->scaleIn; + eBone->scaleOut = curBone->scaleOut; + + eBone->bbone_prev_type = curBone->bbone_prev_type; + eBone->bbone_next_type = curBone->bbone_next_type; + + if (curBone->prop) + eBone->prop = IDP_CopyProperty(curBone->prop); + + BLI_addtail(edbo, eBone); + + /* Add children if necessary */ + if (curBone->childbase.first) { + eBoneTest = make_boneList_rec(edbo, &curBone->childbase, eBone, actBone); + if (eBoneTest) + eBoneAct = eBoneTest; + } + + if (curBone == actBone) + eBoneAct = eBone; + } + + return eBoneAct; } static EditBone *find_ebone_link(ListBase *edbo, Bone *link) { - if (link != NULL) { - for (EditBone *ebone = edbo->first; ebone; ebone = ebone->next) { - if (ebone->temp.bone == link) { - return ebone; - } - } - } + if (link != NULL) { + for (EditBone *ebone = edbo->first; ebone; ebone = ebone->next) { + if (ebone->temp.bone == link) { + return ebone; + } + } + } - return NULL; + return NULL; } EditBone *make_boneList(ListBase *edbo, ListBase *bones, struct Bone *actBone) { - BLI_assert(!edbo->first && !edbo->last); + BLI_assert(!edbo->first && !edbo->last); - EditBone *active = make_boneList_rec(edbo, bones, NULL, actBone); + EditBone *active = make_boneList_rec(edbo, bones, NULL, actBone); - for (EditBone *ebone = edbo->first; ebone; ebone = ebone->next) { - Bone *bone = ebone->temp.bone; + for (EditBone *ebone = edbo->first; ebone; ebone = ebone->next) { + Bone *bone = ebone->temp.bone; - /* Convert custom B-Bone handle links. */ - ebone->bbone_prev = find_ebone_link(edbo, bone->bbone_prev); - ebone->bbone_next = find_ebone_link(edbo, bone->bbone_next); - } + /* Convert custom B-Bone handle links. */ + ebone->bbone_prev = find_ebone_link(edbo, bone->bbone_prev); + ebone->bbone_next = find_ebone_link(edbo, bone->bbone_next); + } - return active; + return active; } /** @@ -559,217 +560,217 @@ EditBone *make_boneList(ListBase *edbo, ListBase *bones, struct Bone *actBone) */ static void armature_finalize_restpose(ListBase *bonelist, ListBase *editbonelist) { - Bone *curBone; - EditBone *ebone; + Bone *curBone; + EditBone *ebone; - for (curBone = bonelist->first; curBone; curBone = curBone->next) { - /* Set bone's local head/tail. - * Note that it's important to use final parent's restpose (arm_mat) here, instead of setting those values - * from editbone's matrix (see T46010). */ - if (curBone->parent) { - float parmat_inv[4][4]; + for (curBone = bonelist->first; curBone; curBone = curBone->next) { + /* Set bone's local head/tail. + * Note that it's important to use final parent's restpose (arm_mat) here, instead of setting those values + * from editbone's matrix (see T46010). */ + if (curBone->parent) { + float parmat_inv[4][4]; - invert_m4_m4(parmat_inv, curBone->parent->arm_mat); + invert_m4_m4(parmat_inv, curBone->parent->arm_mat); - /* Get the new head and tail */ - sub_v3_v3v3(curBone->head, curBone->arm_head, curBone->parent->arm_tail); - sub_v3_v3v3(curBone->tail, curBone->arm_tail, curBone->parent->arm_tail); + /* Get the new head and tail */ + sub_v3_v3v3(curBone->head, curBone->arm_head, curBone->parent->arm_tail); + sub_v3_v3v3(curBone->tail, curBone->arm_tail, curBone->parent->arm_tail); - mul_mat3_m4_v3(parmat_inv, curBone->head); - mul_mat3_m4_v3(parmat_inv, curBone->tail); - } - else { - copy_v3_v3(curBone->head, curBone->arm_head); - copy_v3_v3(curBone->tail, curBone->arm_tail); - } + mul_mat3_m4_v3(parmat_inv, curBone->head); + mul_mat3_m4_v3(parmat_inv, curBone->tail); + } + else { + copy_v3_v3(curBone->head, curBone->arm_head); + copy_v3_v3(curBone->tail, curBone->arm_tail); + } - /* Set local matrix and arm_mat (restpose). - * Do not recurse into children here, armature_finalize_restpose() is already recursive. */ - BKE_armature_where_is_bone(curBone, curBone->parent, false); + /* Set local matrix and arm_mat (restpose). + * Do not recurse into children here, armature_finalize_restpose() is already recursive. */ + BKE_armature_where_is_bone(curBone, curBone->parent, false); - /* Find the associated editbone */ - for (ebone = editbonelist->first; ebone; ebone = ebone->next) { - if (ebone->temp.bone == curBone) { - float premat[3][3]; - float postmat[3][3]; - float difmat[3][3]; - float imat[3][3]; + /* Find the associated editbone */ + for (ebone = editbonelist->first; ebone; ebone = ebone->next) { + if (ebone->temp.bone == curBone) { + float premat[3][3]; + float postmat[3][3]; + float difmat[3][3]; + float imat[3][3]; - /* Get the ebone premat and its inverse. */ - ED_armature_ebone_to_mat3(ebone, premat); - invert_m3_m3(imat, premat); + /* Get the ebone premat and its inverse. */ + ED_armature_ebone_to_mat3(ebone, premat); + invert_m3_m3(imat, premat); - /* Get the bone postmat. */ - copy_m3_m4(postmat, curBone->arm_mat); + /* Get the bone postmat. */ + copy_m3_m4(postmat, curBone->arm_mat); - mul_m3_m3m3(difmat, imat, postmat); + mul_m3_m3m3(difmat, imat, postmat); #if 0 - printf("Bone %s\n", curBone->name); - print_m4("premat", premat); - print_m4("postmat", postmat); - print_m4("difmat", difmat); - printf("Roll = %f\n", RAD2DEGF(-atan2(difmat[2][0], difmat[2][2]))); + printf("Bone %s\n", curBone->name); + print_m4("premat", premat); + print_m4("postmat", postmat); + print_m4("difmat", difmat); + printf("Roll = %f\n", RAD2DEGF(-atan2(difmat[2][0], difmat[2][2]))); #endif - curBone->roll = -atan2f(difmat[2][0], difmat[2][2]); + curBone->roll = -atan2f(difmat[2][0], difmat[2][2]); - /* and set restposition again */ - BKE_armature_where_is_bone(curBone, curBone->parent, false); - break; - } - } + /* and set restposition again */ + BKE_armature_where_is_bone(curBone, curBone->parent, false); + break; + } + } - /* Recurse into children... */ - armature_finalize_restpose(&curBone->childbase, editbonelist); - } + /* Recurse into children... */ + armature_finalize_restpose(&curBone->childbase, editbonelist); + } } /* put EditMode back in Object */ void ED_armature_from_edit(Main *bmain, bArmature *arm) { - EditBone *eBone, *neBone; - Bone *newBone; - Object *obt; - - /* armature bones */ - BKE_armature_bonelist_free(&arm->bonebase); - arm->act_bone = NULL; - - /* remove zero sized bones, this gives unstable restposes */ - for (eBone = arm->edbo->first; eBone; eBone = neBone) { - float len_sq = len_squared_v3v3(eBone->head, eBone->tail); - neBone = eBone->next; - if (len_sq <= SQUARE(0.000001f)) { /* FLT_EPSILON is too large? */ - EditBone *fBone; - - /* Find any bones that refer to this bone */ - for (fBone = arm->edbo->first; fBone; fBone = fBone->next) { - if (fBone->parent == eBone) - fBone->parent = eBone->parent; - } - if (G.debug & G_DEBUG) - printf("Warning: removed zero sized bone: %s\n", eBone->name); - bone_free(arm, eBone); - } - } - - /* Copy the bones from the editData 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 */ - - BLI_strncpy(newBone->name, eBone->name, sizeof(newBone->name)); - copy_v3_v3(newBone->arm_head, eBone->head); - copy_v3_v3(newBone->arm_tail, eBone->tail); - newBone->arm_roll = eBone->roll; - - newBone->flag = eBone->flag; - - if (eBone == arm->act_edbone) { - /* don't change active selection, this messes up separate which uses - * editmode toggle and can separate active bone which is de-selected originally */ - - /* important, editbones can be active with only 1 point selected */ - /* newBone->flag |= BONE_SELECTED; */ - arm->act_bone = newBone; - } - newBone->roll = 0.0f; - - newBone->weight = eBone->weight; - newBone->dist = eBone->dist; - - newBone->xwidth = eBone->xwidth; - newBone->zwidth = eBone->zwidth; - newBone->rad_head = eBone->rad_head; - newBone->rad_tail = eBone->rad_tail; - newBone->segments = eBone->segments; - newBone->layer = eBone->layer; - - /* Bendy-Bone parameters */ - newBone->roll1 = eBone->roll1; - newBone->roll2 = eBone->roll2; - newBone->curveInX = eBone->curveInX; - newBone->curveInY = eBone->curveInY; - newBone->curveOutX = eBone->curveOutX; - newBone->curveOutY = eBone->curveOutY; - newBone->ease1 = eBone->ease1; - newBone->ease2 = eBone->ease2; - newBone->scaleIn = eBone->scaleIn; - newBone->scaleOut = eBone->scaleOut; - - newBone->bbone_prev_type = eBone->bbone_prev_type; - newBone->bbone_next_type = eBone->bbone_next_type; - - if (eBone->prop) - newBone->prop = IDP_CopyProperty(eBone->prop); - } - - /* Fix parenting in a separate pass to ensure ebone->bone connections are valid at this point. - * Do not set bone->head/tail here anymore, - * using EditBone data for that is not OK since our later fiddling with parent's arm_mat - * (for roll conversion) may have some small but visible impact on locations (T46010). */ - for (eBone = arm->edbo->first; eBone; eBone = eBone->next) { - newBone = eBone->temp.bone; - if (eBone->parent) { - newBone->parent = eBone->parent->temp.bone; - BLI_addtail(&newBone->parent->childbase, newBone); - } - /* ...otherwise add this bone to the armature's bonebase */ - else { - BLI_addtail(&arm->bonebase, newBone); - } - - /* Also transfer B-Bone custom handles. */ - if (eBone->bbone_prev) { - newBone->bbone_prev = eBone->bbone_prev->temp.bone; - } - if (eBone->bbone_next) { - newBone->bbone_next = eBone->bbone_next->temp.bone; - } - } - - /* Finalize definition of restpose data (roll, bone_mat, arm_mat, head/tail...). */ - armature_finalize_restpose(&arm->bonebase, arm->edbo); - - /* so all users of this armature should get rebuilt */ - for (obt = bmain->objects.first; obt; obt = obt->id.next) { - if (obt->data == arm) { - BKE_pose_rebuild(bmain, obt, arm, true); - } - } - - DEG_id_tag_update(&arm->id, 0); + EditBone *eBone, *neBone; + Bone *newBone; + Object *obt; + + /* armature bones */ + BKE_armature_bonelist_free(&arm->bonebase); + arm->act_bone = NULL; + + /* remove zero sized bones, this gives unstable restposes */ + for (eBone = arm->edbo->first; eBone; eBone = neBone) { + float len_sq = len_squared_v3v3(eBone->head, eBone->tail); + neBone = eBone->next; + if (len_sq <= SQUARE(0.000001f)) { /* FLT_EPSILON is too large? */ + EditBone *fBone; + + /* Find any bones that refer to this bone */ + for (fBone = arm->edbo->first; fBone; fBone = fBone->next) { + if (fBone->parent == eBone) + fBone->parent = eBone->parent; + } + if (G.debug & G_DEBUG) + printf("Warning: removed zero sized bone: %s\n", eBone->name); + bone_free(arm, eBone); + } + } + + /* Copy the bones from the editData 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 */ + + BLI_strncpy(newBone->name, eBone->name, sizeof(newBone->name)); + copy_v3_v3(newBone->arm_head, eBone->head); + copy_v3_v3(newBone->arm_tail, eBone->tail); + newBone->arm_roll = eBone->roll; + + newBone->flag = eBone->flag; + + if (eBone == arm->act_edbone) { + /* don't change active selection, this messes up separate which uses + * editmode toggle and can separate active bone which is de-selected originally */ + + /* important, editbones can be active with only 1 point selected */ + /* newBone->flag |= BONE_SELECTED; */ + arm->act_bone = newBone; + } + newBone->roll = 0.0f; + + newBone->weight = eBone->weight; + newBone->dist = eBone->dist; + + newBone->xwidth = eBone->xwidth; + newBone->zwidth = eBone->zwidth; + newBone->rad_head = eBone->rad_head; + newBone->rad_tail = eBone->rad_tail; + newBone->segments = eBone->segments; + newBone->layer = eBone->layer; + + /* Bendy-Bone parameters */ + newBone->roll1 = eBone->roll1; + newBone->roll2 = eBone->roll2; + newBone->curveInX = eBone->curveInX; + newBone->curveInY = eBone->curveInY; + newBone->curveOutX = eBone->curveOutX; + newBone->curveOutY = eBone->curveOutY; + newBone->ease1 = eBone->ease1; + newBone->ease2 = eBone->ease2; + newBone->scaleIn = eBone->scaleIn; + newBone->scaleOut = eBone->scaleOut; + + newBone->bbone_prev_type = eBone->bbone_prev_type; + newBone->bbone_next_type = eBone->bbone_next_type; + + if (eBone->prop) + newBone->prop = IDP_CopyProperty(eBone->prop); + } + + /* Fix parenting in a separate pass to ensure ebone->bone connections are valid at this point. + * Do not set bone->head/tail here anymore, + * using EditBone data for that is not OK since our later fiddling with parent's arm_mat + * (for roll conversion) may have some small but visible impact on locations (T46010). */ + for (eBone = arm->edbo->first; eBone; eBone = eBone->next) { + newBone = eBone->temp.bone; + if (eBone->parent) { + newBone->parent = eBone->parent->temp.bone; + BLI_addtail(&newBone->parent->childbase, newBone); + } + /* ...otherwise add this bone to the armature's bonebase */ + else { + BLI_addtail(&arm->bonebase, newBone); + } + + /* Also transfer B-Bone custom handles. */ + if (eBone->bbone_prev) { + newBone->bbone_prev = eBone->bbone_prev->temp.bone; + } + if (eBone->bbone_next) { + newBone->bbone_next = eBone->bbone_next->temp.bone; + } + } + + /* Finalize definition of restpose data (roll, bone_mat, arm_mat, head/tail...). */ + armature_finalize_restpose(&arm->bonebase, arm->edbo); + + /* so all users of this armature should get rebuilt */ + for (obt = bmain->objects.first; obt; obt = obt->id.next) { + if (obt->data == arm) { + BKE_pose_rebuild(bmain, obt, arm, true); + } + } + + DEG_id_tag_update(&arm->id, 0); } void ED_armature_edit_free(struct bArmature *arm) { - EditBone *eBone; + EditBone *eBone; - /* Clear the editbones list */ - if (arm->edbo) { - if (arm->edbo->first) { - for (eBone = arm->edbo->first; eBone; eBone = eBone->next) { - if (eBone->prop) { - IDP_FreeProperty(eBone->prop); - MEM_freeN(eBone->prop); - } - } + /* Clear the editbones list */ + if (arm->edbo) { + if (arm->edbo->first) { + for (eBone = arm->edbo->first; eBone; eBone = eBone->next) { + if (eBone->prop) { + IDP_FreeProperty(eBone->prop); + MEM_freeN(eBone->prop); + } + } - BLI_freelistN(arm->edbo); - } - MEM_freeN(arm->edbo); - arm->edbo = NULL; - arm->act_edbone = NULL; - } + BLI_freelistN(arm->edbo); + } + MEM_freeN(arm->edbo); + arm->edbo = NULL; + arm->act_edbone = NULL; + } } /* Put armature in EditMode */ void ED_armature_to_edit(bArmature *arm) { - ED_armature_edit_free(arm); - arm->edbo = MEM_callocN(sizeof(ListBase), "edbo armature"); - arm->act_edbone = make_boneList(arm->edbo, &arm->bonebase, arm->act_bone); + ED_armature_edit_free(arm); + arm->edbo = MEM_callocN(sizeof(ListBase), "edbo armature"); + arm->act_edbone = make_boneList(arm->edbo, &arm->bonebase, arm->act_bone); } /* *************************************************************** */ @@ -779,59 +780,59 @@ void ED_armature_to_edit(bArmature *arm) void ED_armature_ebone_listbase_free(ListBase *lb) { - EditBone *ebone, *ebone_next; + EditBone *ebone, *ebone_next; - for (ebone = lb->first; ebone; ebone = ebone_next) { - ebone_next = ebone->next; + for (ebone = lb->first; ebone; ebone = ebone_next) { + ebone_next = ebone->next; - if (ebone->prop) { - IDP_FreeProperty(ebone->prop); - MEM_freeN(ebone->prop); - } + if (ebone->prop) { + IDP_FreeProperty(ebone->prop); + MEM_freeN(ebone->prop); + } - MEM_freeN(ebone); - } + MEM_freeN(ebone); + } - BLI_listbase_clear(lb); + BLI_listbase_clear(lb); } void ED_armature_ebone_listbase_copy(ListBase *lb_dst, ListBase *lb_src) { - EditBone *ebone_src; - EditBone *ebone_dst; - - BLI_assert(BLI_listbase_is_empty(lb_dst)); - - for (ebone_src = lb_src->first; ebone_src; ebone_src = ebone_src->next) { - ebone_dst = MEM_dupallocN(ebone_src); - if (ebone_dst->prop) { - ebone_dst->prop = IDP_CopyProperty(ebone_dst->prop); - } - ebone_src->temp.ebone = ebone_dst; - BLI_addtail(lb_dst, ebone_dst); - } - - /* set pointers */ - for (ebone_dst = lb_dst->first; ebone_dst; ebone_dst = ebone_dst->next) { - if (ebone_dst->parent) { - ebone_dst->parent = ebone_dst->parent->temp.ebone; - } - if (ebone_dst->bbone_next) { - ebone_dst->bbone_next = ebone_dst->bbone_next->temp.ebone; - } - if (ebone_dst->bbone_prev) { - ebone_dst->bbone_prev = ebone_dst->bbone_prev->temp.ebone; - } - } + EditBone *ebone_src; + EditBone *ebone_dst; + + BLI_assert(BLI_listbase_is_empty(lb_dst)); + + for (ebone_src = lb_src->first; ebone_src; ebone_src = ebone_src->next) { + ebone_dst = MEM_dupallocN(ebone_src); + if (ebone_dst->prop) { + ebone_dst->prop = IDP_CopyProperty(ebone_dst->prop); + } + ebone_src->temp.ebone = ebone_dst; + BLI_addtail(lb_dst, ebone_dst); + } + + /* set pointers */ + for (ebone_dst = lb_dst->first; ebone_dst; ebone_dst = ebone_dst->next) { + if (ebone_dst->parent) { + ebone_dst->parent = ebone_dst->parent->temp.ebone; + } + if (ebone_dst->bbone_next) { + ebone_dst->bbone_next = ebone_dst->bbone_next->temp.ebone; + } + if (ebone_dst->bbone_prev) { + ebone_dst->bbone_prev = ebone_dst->bbone_prev->temp.ebone; + } + } } void ED_armature_ebone_listbase_temp_clear(ListBase *lb) { - EditBone *ebone; - /* be sure they don't hang ever */ - for (ebone = lb->first; ebone; ebone = ebone->next) { - ebone->temp.p = NULL; - } + EditBone *ebone; + /* be sure they don't hang ever */ + for (ebone = lb->first; ebone; ebone = ebone->next) { + ebone->temp.p = NULL; + } } /* *************************************************************** */ @@ -841,54 +842,54 @@ void ED_armature_ebone_listbase_temp_clear(ListBase *lb) int ED_armature_ebone_selectflag_get(const EditBone *ebone) { - if (ebone->parent && (ebone->flag & BONE_CONNECTED)) { - return ((ebone->flag & (BONE_SELECTED | BONE_TIPSEL)) | - ((ebone->parent->flag & BONE_TIPSEL) ? BONE_ROOTSEL : 0)); - } - else { - return (ebone->flag & (BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL)); - } + if (ebone->parent && (ebone->flag & BONE_CONNECTED)) { + return ((ebone->flag & (BONE_SELECTED | BONE_TIPSEL)) | + ((ebone->parent->flag & BONE_TIPSEL) ? BONE_ROOTSEL : 0)); + } + else { + return (ebone->flag & (BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL)); + } } void ED_armature_ebone_selectflag_set(EditBone *ebone, int flag) { - flag = flag & (BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL); + flag = flag & (BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL); - if (ebone->parent && (ebone->flag & BONE_CONNECTED)) { - ebone->flag &= ~(BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL); - ebone->parent->flag &= ~BONE_TIPSEL; + if (ebone->parent && (ebone->flag & BONE_CONNECTED)) { + ebone->flag &= ~(BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL); + ebone->parent->flag &= ~BONE_TIPSEL; - ebone->flag |= flag; - ebone->parent->flag |= (flag & BONE_ROOTSEL) ? BONE_TIPSEL : 0; - } - else { - ebone->flag &= ~(BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL); - ebone->flag |= flag; - } + ebone->flag |= flag; + ebone->parent->flag |= (flag & BONE_ROOTSEL) ? BONE_TIPSEL : 0; + } + else { + ebone->flag &= ~(BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL); + ebone->flag |= flag; + } } void ED_armature_ebone_selectflag_enable(EditBone *ebone, int flag) { - BLI_assert((flag & (BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL)) != 0); - ED_armature_ebone_selectflag_set(ebone, ebone->flag | flag); + BLI_assert((flag & (BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL)) != 0); + ED_armature_ebone_selectflag_set(ebone, ebone->flag | flag); } void ED_armature_ebone_selectflag_disable(EditBone *ebone, int flag) { - BLI_assert((flag & (BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL)) != 0); - ED_armature_ebone_selectflag_set(ebone, ebone->flag & ~flag); + BLI_assert((flag & (BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL)) != 0); + ED_armature_ebone_selectflag_set(ebone, ebone->flag & ~flag); } /* could be used in more places */ void ED_armature_ebone_select_set(EditBone *ebone, bool select) { - int flag; - if (select) { - BLI_assert((ebone->flag & BONE_UNSELECTABLE) == 0); - flag = (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - } - else { - flag = 0; - } - ED_armature_ebone_selectflag_set(ebone, flag); + int flag; + if (select) { + BLI_assert((ebone->flag & BONE_UNSELECTABLE) == 0); + flag = (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + } + else { + flag = 0; + } + ED_armature_ebone_selectflag_set(ebone, flag); } diff --git a/source/blender/editors/armature/editarmature_undo.c b/source/blender/editors/armature/editarmature_undo.c index 39e8371a824..3a2440af2da 100644 --- a/source/blender/editors/armature/editarmature_undo.c +++ b/source/blender/editors/armature/editarmature_undo.c @@ -23,7 +23,6 @@ #include "MEM_guardedalloc.h" - #include "CLG_log.h" #include "DNA_armature_types.h" @@ -53,69 +52,69 @@ static CLG_LogRef LOG = {"ed.undo.armature"}; * \{ */ typedef struct UndoArmature { - EditBone *act_edbone; - ListBase lb; - size_t undo_size; + EditBone *act_edbone; + ListBase lb; + size_t undo_size; } UndoArmature; static void undoarm_to_editarm(UndoArmature *uarm, bArmature *arm) { - EditBone *ebone; + EditBone *ebone; - ED_armature_ebone_listbase_free(arm->edbo); - ED_armature_ebone_listbase_copy(arm->edbo, &uarm->lb); + ED_armature_ebone_listbase_free(arm->edbo); + ED_armature_ebone_listbase_copy(arm->edbo, &uarm->lb); - /* active bone */ - if (uarm->act_edbone) { - ebone = uarm->act_edbone; - arm->act_edbone = ebone->temp.ebone; - } - else { - arm->act_edbone = NULL; - } + /* active bone */ + if (uarm->act_edbone) { + ebone = uarm->act_edbone; + arm->act_edbone = ebone->temp.ebone; + } + else { + arm->act_edbone = NULL; + } - ED_armature_ebone_listbase_temp_clear(arm->edbo); + ED_armature_ebone_listbase_temp_clear(arm->edbo); } static void *undoarm_from_editarm(UndoArmature *uarm, bArmature *arm) { - BLI_assert(BLI_array_is_zeroed(uarm, 1)); + BLI_assert(BLI_array_is_zeroed(uarm, 1)); - /* TODO: include size of ID-properties. */ - uarm->undo_size = 0; + /* TODO: include size of ID-properties. */ + uarm->undo_size = 0; - ED_armature_ebone_listbase_copy(&uarm->lb, arm->edbo); + ED_armature_ebone_listbase_copy(&uarm->lb, arm->edbo); - /* active bone */ - if (arm->act_edbone) { - EditBone *ebone = arm->act_edbone; - uarm->act_edbone = ebone->temp.ebone; - } + /* active bone */ + if (arm->act_edbone) { + EditBone *ebone = arm->act_edbone; + uarm->act_edbone = ebone->temp.ebone; + } - ED_armature_ebone_listbase_temp_clear(&uarm->lb); + ED_armature_ebone_listbase_temp_clear(&uarm->lb); - for (EditBone *ebone = uarm->lb.first; ebone; ebone = ebone->next) { - uarm->undo_size += sizeof(EditBone); - } + for (EditBone *ebone = uarm->lb.first; ebone; ebone = ebone->next) { + uarm->undo_size += sizeof(EditBone); + } - return uarm; + return uarm; } static void undoarm_free_data(UndoArmature *uarm) { - ED_armature_ebone_listbase_free(&uarm->lb); + ED_armature_ebone_listbase_free(&uarm->lb); } static Object *editarm_object_from_context(bContext *C) { - Object *obedit = CTX_data_edit_object(C); - if (obedit && obedit->type == OB_ARMATURE) { - bArmature *arm = obedit->data; - if (arm->edbo != NULL) { - return obedit; - } - } - return NULL; + Object *obedit = CTX_data_edit_object(C); + if (obedit && obedit->type == OB_ARMATURE) { + bArmature *arm = obedit->data; + if (arm->edbo != NULL) { + return obedit; + } + } + return NULL; } /** \} */ @@ -127,112 +126,124 @@ static Object *editarm_object_from_context(bContext *C) * \{ */ typedef struct ArmatureUndoStep_Elem { - struct ArmatureUndoStep_Elem *next, *prev; - UndoRefID_Object obedit_ref; - UndoArmature data; + struct ArmatureUndoStep_Elem *next, *prev; + UndoRefID_Object obedit_ref; + UndoArmature data; } ArmatureUndoStep_Elem; typedef struct ArmatureUndoStep { - UndoStep step; - ArmatureUndoStep_Elem *elems; - uint elems_len; + UndoStep step; + ArmatureUndoStep_Elem *elems; + uint elems_len; } ArmatureUndoStep; static bool armature_undosys_poll(bContext *C) { - return editarm_object_from_context(C) != NULL; + return editarm_object_from_context(C) != NULL; } -static bool armature_undosys_step_encode(struct bContext *C, struct Main *UNUSED(bmain), UndoStep *us_p) +static bool armature_undosys_step_encode(struct bContext *C, + struct Main *UNUSED(bmain), + UndoStep *us_p) { - ArmatureUndoStep *us = (ArmatureUndoStep *)us_p; - - /* Important not to use the 3D view when getting objects because all objects - * outside of this list will be moved out of edit-mode when reading back undo steps. */ - 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, NULL, &objects_len); - - us->elems = MEM_callocN(sizeof(*us->elems) * objects_len, __func__); - us->elems_len = objects_len; - - for (uint i = 0; i < objects_len; i++) { - Object *ob = objects[i]; - ArmatureUndoStep_Elem *elem = &us->elems[i]; - - elem->obedit_ref.ptr = ob; - bArmature *arm = elem->obedit_ref.ptr->data; - undoarm_from_editarm(&elem->data, arm); - us->step.data_size += elem->data.undo_size; - } - MEM_freeN(objects); - return true; + ArmatureUndoStep *us = (ArmatureUndoStep *)us_p; + + /* Important not to use the 3D view when getting objects because all objects + * outside of this list will be moved out of edit-mode when reading back undo steps. */ + 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, NULL, &objects_len); + + us->elems = MEM_callocN(sizeof(*us->elems) * objects_len, __func__); + us->elems_len = objects_len; + + for (uint i = 0; i < objects_len; i++) { + Object *ob = objects[i]; + ArmatureUndoStep_Elem *elem = &us->elems[i]; + + elem->obedit_ref.ptr = ob; + bArmature *arm = elem->obedit_ref.ptr->data; + undoarm_from_editarm(&elem->data, arm); + us->step.data_size += elem->data.undo_size; + } + MEM_freeN(objects); + return true; } -static void armature_undosys_step_decode(struct bContext *C, struct Main *UNUSED(bmain), UndoStep *us_p, int UNUSED(dir)) +static void armature_undosys_step_decode(struct bContext *C, + struct Main *UNUSED(bmain), + UndoStep *us_p, + int UNUSED(dir)) { - ArmatureUndoStep *us = (ArmatureUndoStep *)us_p; - - /* Load all our objects into edit-mode, clear everything else. */ - ED_undo_object_editmode_restore_helper(C, &us->elems[0].obedit_ref.ptr, us->elems_len, sizeof(*us->elems)); - - BLI_assert(armature_undosys_poll(C)); - - for (uint i = 0; i < us->elems_len; i++) { - ArmatureUndoStep_Elem *elem = &us->elems[i]; - Object *obedit = elem->obedit_ref.ptr; - bArmature *arm = obedit->data; - if (arm->edbo == NULL) { - /* Should never fail, may not crash but can give odd behavior. */ - CLOG_ERROR(&LOG, "name='%s', failed to enter edit-mode for object '%s', undo state invalid", us_p->name, obedit->id.name); - continue; - } - undoarm_to_editarm(&elem->data, arm); - DEG_id_tag_update(&obedit->id, ID_RECALC_GEOMETRY); - } - - /* The first element is always active */ - ED_undo_object_set_active_or_warn(CTX_data_view_layer(C), us->elems[0].obedit_ref.ptr, us_p->name, &LOG); - - WM_event_add_notifier(C, NC_GEOM | ND_DATA, NULL); + ArmatureUndoStep *us = (ArmatureUndoStep *)us_p; + + /* Load all our objects into edit-mode, clear everything else. */ + ED_undo_object_editmode_restore_helper( + C, &us->elems[0].obedit_ref.ptr, us->elems_len, sizeof(*us->elems)); + + BLI_assert(armature_undosys_poll(C)); + + for (uint i = 0; i < us->elems_len; i++) { + ArmatureUndoStep_Elem *elem = &us->elems[i]; + Object *obedit = elem->obedit_ref.ptr; + bArmature *arm = obedit->data; + if (arm->edbo == NULL) { + /* Should never fail, may not crash but can give odd behavior. */ + CLOG_ERROR(&LOG, + "name='%s', failed to enter edit-mode for object '%s', undo state invalid", + us_p->name, + obedit->id.name); + continue; + } + undoarm_to_editarm(&elem->data, arm); + DEG_id_tag_update(&obedit->id, ID_RECALC_GEOMETRY); + } + + /* The first element is always active */ + ED_undo_object_set_active_or_warn( + CTX_data_view_layer(C), us->elems[0].obedit_ref.ptr, us_p->name, &LOG); + + WM_event_add_notifier(C, NC_GEOM | ND_DATA, NULL); } static void armature_undosys_step_free(UndoStep *us_p) { - ArmatureUndoStep *us = (ArmatureUndoStep *)us_p; + ArmatureUndoStep *us = (ArmatureUndoStep *)us_p; - for (uint i = 0; i < us->elems_len; i++) { - ArmatureUndoStep_Elem *elem = &us->elems[i]; - undoarm_free_data(&elem->data); - } - MEM_freeN(us->elems); + for (uint i = 0; i < us->elems_len; i++) { + ArmatureUndoStep_Elem *elem = &us->elems[i]; + undoarm_free_data(&elem->data); + } + MEM_freeN(us->elems); } -static void armature_undosys_foreach_ID_ref( - UndoStep *us_p, UndoTypeForEachIDRefFn foreach_ID_ref_fn, void *user_data) +static void armature_undosys_foreach_ID_ref(UndoStep *us_p, + UndoTypeForEachIDRefFn foreach_ID_ref_fn, + void *user_data) { - ArmatureUndoStep *us = (ArmatureUndoStep *)us_p; + ArmatureUndoStep *us = (ArmatureUndoStep *)us_p; - for (uint i = 0; i < us->elems_len; i++) { - ArmatureUndoStep_Elem *elem = &us->elems[i]; - foreach_ID_ref_fn(user_data, ((UndoRefID *)&elem->obedit_ref)); - } + for (uint i = 0; i < us->elems_len; i++) { + ArmatureUndoStep_Elem *elem = &us->elems[i]; + foreach_ID_ref_fn(user_data, ((UndoRefID *)&elem->obedit_ref)); + } } /* Export for ED_undo_sys. */ void ED_armature_undosys_type(UndoType *ut) { - ut->name = "Edit Armature"; - ut->poll = armature_undosys_poll; - ut->step_encode = armature_undosys_step_encode; - ut->step_decode = armature_undosys_step_decode; - ut->step_free = armature_undosys_step_free; + ut->name = "Edit Armature"; + ut->poll = armature_undosys_poll; + ut->step_encode = armature_undosys_step_encode; + ut->step_decode = armature_undosys_step_decode; + ut->step_free = armature_undosys_step_free; - ut->step_foreach_ID_ref = armature_undosys_foreach_ID_ref; + ut->step_foreach_ID_ref = armature_undosys_foreach_ID_ref; - ut->use_context = true; + ut->use_context = true; - ut->step_size = sizeof(ArmatureUndoStep); + ut->step_size = sizeof(ArmatureUndoStep); } /** \} */ diff --git a/source/blender/editors/armature/meshlaplacian.c b/source/blender/editors/armature/meshlaplacian.c index 66bd7a8db80..febe2a49174 100644 --- a/source/blender/editors/armature/meshlaplacian.c +++ b/source/blender/editors/armature/meshlaplacian.c @@ -49,53 +49,63 @@ #include "meshlaplacian.h" /* ************* XXX *************** */ -static void waitcursor(int UNUSED(val)) {} -static void progress_bar(int UNUSED(dummy_val), const char *UNUSED(dummy)) {} -static void start_progress_bar(void) {} -static void end_progress_bar(void) {} -static void error(const char *str) { printf("error: %s\n", str); } +static void waitcursor(int UNUSED(val)) +{ +} +static void progress_bar(int UNUSED(dummy_val), const char *UNUSED(dummy)) +{ +} +static void start_progress_bar(void) +{ +} +static void end_progress_bar(void) +{ +} +static void error(const char *str) +{ + printf("error: %s\n", str); +} /* ************* XXX *************** */ - /************************** Laplacian System *****************************/ struct LaplacianSystem { - LinearSolver *context; /* linear solver */ - - int totvert, totface; - - float **verts; /* vertex coordinates */ - float *varea; /* vertex weights for laplacian computation */ - char *vpinned; /* vertex pinning */ - int (*faces)[3]; /* face vertex indices */ - float (*fweights)[3]; /* cotangent weights per face */ - - int areaweights; /* use area in cotangent weights? */ - int storeweights; /* store cotangent weights in fweights */ - bool variablesdone; /* variables set in linear system */ - - EdgeHash *edgehash; /* edge hash for construction */ - - struct HeatWeighting { - const MLoopTri *mlooptri; - const MLoop *mloop; /* needed to find vertices by index */ - int totvert; - int tottri; - float (*verts)[3]; /* vertex coordinates */ - float (*vnors)[3]; /* vertex normals */ - - float (*root)[3]; /* bone root */ - float (*tip)[3]; /* bone tip */ - float (*source)[3]; /* vertex source */ - int numsource; - - float *H; /* diagonal H matrix */ - float *p; /* values from all p vectors */ - float *mindist; /* minimum distance to a bone for all vertices */ - - BVHTree *bvhtree; /* ray tracing acceleration structure */ - const MLoopTri **vltree; /* a looptri that the vertex belongs to */ - } heat; + LinearSolver *context; /* linear solver */ + + int totvert, totface; + + float **verts; /* vertex coordinates */ + float *varea; /* vertex weights for laplacian computation */ + char *vpinned; /* vertex pinning */ + int (*faces)[3]; /* face vertex indices */ + float (*fweights)[3]; /* cotangent weights per face */ + + int areaweights; /* use area in cotangent weights? */ + int storeweights; /* store cotangent weights in fweights */ + bool variablesdone; /* variables set in linear system */ + + EdgeHash *edgehash; /* edge hash for construction */ + + struct HeatWeighting { + const MLoopTri *mlooptri; + const MLoop *mloop; /* needed to find vertices by index */ + int totvert; + int tottri; + float (*verts)[3]; /* vertex coordinates */ + float (*vnors)[3]; /* vertex normals */ + + float (*root)[3]; /* bone root */ + float (*tip)[3]; /* bone tip */ + float (*source)[3]; /* vertex source */ + int numsource; + + float *H; /* diagonal H matrix */ + float *p; /* values from all p vectors */ + float *mindist; /* minimum distance to a bone for all vertices */ + + BVHTree *bvhtree; /* ray tracing acceleration structure */ + const MLoopTri **vltree; /* a looptri that the vertex belongs to */ + } heat; }; /* Laplacian matrix construction */ @@ -110,676 +120,685 @@ struct LaplacianSystem { static void laplacian_increase_edge_count(EdgeHash *edgehash, int v1, int v2) { - void **p; + void **p; - if (BLI_edgehash_ensure_p(edgehash, v1, v2, &p)) - *p = (void *)((intptr_t)*p + (intptr_t)1); - else - *p = (void *)((intptr_t)1); + if (BLI_edgehash_ensure_p(edgehash, v1, v2, &p)) + *p = (void *)((intptr_t)*p + (intptr_t)1); + else + *p = (void *)((intptr_t)1); } static int laplacian_edge_count(EdgeHash *edgehash, int v1, int v2) { - return (int)(intptr_t)BLI_edgehash_lookup(edgehash, v1, v2); + return (int)(intptr_t)BLI_edgehash_lookup(edgehash, v1, v2); } static void laplacian_triangle_area(LaplacianSystem *sys, int i1, int i2, int i3) { - float t1, t2, t3, len1, len2, len3, area; - float *varea = sys->varea, *v1, *v2, *v3; - int obtuse = 0; - - v1 = sys->verts[i1]; - v2 = sys->verts[i2]; - v3 = sys->verts[i3]; - - t1 = cotangent_tri_weight_v3(v1, v2, v3); - t2 = cotangent_tri_weight_v3(v2, v3, v1); - t3 = cotangent_tri_weight_v3(v3, v1, v2); - - if (angle_v3v3v3(v2, v1, v3) > DEG2RADF(90.0f)) obtuse = 1; - else if (angle_v3v3v3(v1, v2, v3) > DEG2RADF(90.0f)) obtuse = 2; - else if (angle_v3v3v3(v1, v3, v2) > DEG2RADF(90.0f)) obtuse = 3; - - if (obtuse > 0) { - area = area_tri_v3(v1, v2, v3); - - varea[i1] += (obtuse == 1) ? area : area * 0.5f; - varea[i2] += (obtuse == 2) ? area : area * 0.5f; - varea[i3] += (obtuse == 3) ? area : area * 0.5f; - } - else { - len1 = len_v3v3(v2, v3); - len2 = len_v3v3(v1, v3); - len3 = len_v3v3(v1, v2); - - t1 *= len1 * len1; - t2 *= len2 * len2; - t3 *= len3 * len3; - - varea[i1] += (t2 + t3) * 0.25f; - varea[i2] += (t1 + t3) * 0.25f; - varea[i3] += (t1 + t2) * 0.25f; - } + float t1, t2, t3, len1, len2, len3, area; + float *varea = sys->varea, *v1, *v2, *v3; + int obtuse = 0; + + v1 = sys->verts[i1]; + v2 = sys->verts[i2]; + v3 = sys->verts[i3]; + + t1 = cotangent_tri_weight_v3(v1, v2, v3); + t2 = cotangent_tri_weight_v3(v2, v3, v1); + t3 = cotangent_tri_weight_v3(v3, v1, v2); + + if (angle_v3v3v3(v2, v1, v3) > DEG2RADF(90.0f)) + obtuse = 1; + else if (angle_v3v3v3(v1, v2, v3) > DEG2RADF(90.0f)) + obtuse = 2; + else if (angle_v3v3v3(v1, v3, v2) > DEG2RADF(90.0f)) + obtuse = 3; + + if (obtuse > 0) { + area = area_tri_v3(v1, v2, v3); + + varea[i1] += (obtuse == 1) ? area : area * 0.5f; + varea[i2] += (obtuse == 2) ? area : area * 0.5f; + varea[i3] += (obtuse == 3) ? area : area * 0.5f; + } + else { + len1 = len_v3v3(v2, v3); + len2 = len_v3v3(v1, v3); + len3 = len_v3v3(v1, v2); + + t1 *= len1 * len1; + t2 *= len2 * len2; + t3 *= len3 * len3; + + varea[i1] += (t2 + t3) * 0.25f; + varea[i2] += (t1 + t3) * 0.25f; + varea[i3] += (t1 + t2) * 0.25f; + } } static void laplacian_triangle_weights(LaplacianSystem *sys, int f, int i1, int i2, int i3) { - float t1, t2, t3; - float *varea = sys->varea, *v1, *v2, *v3; + float t1, t2, t3; + float *varea = sys->varea, *v1, *v2, *v3; - v1 = sys->verts[i1]; - v2 = sys->verts[i2]; - v3 = sys->verts[i3]; + v1 = sys->verts[i1]; + v2 = sys->verts[i2]; + v3 = sys->verts[i3]; - /* instead of *0.5 we divided by the number of faces of the edge, it still - * needs to be verified that this is indeed the correct thing to do! */ - t1 = cotangent_tri_weight_v3(v1, v2, v3) / laplacian_edge_count(sys->edgehash, i2, i3); - t2 = cotangent_tri_weight_v3(v2, v3, v1) / laplacian_edge_count(sys->edgehash, i3, i1); - t3 = cotangent_tri_weight_v3(v3, v1, v2) / laplacian_edge_count(sys->edgehash, i1, i2); + /* instead of *0.5 we divided by the number of faces of the edge, it still + * needs to be verified that this is indeed the correct thing to do! */ + t1 = cotangent_tri_weight_v3(v1, v2, v3) / laplacian_edge_count(sys->edgehash, i2, i3); + t2 = cotangent_tri_weight_v3(v2, v3, v1) / laplacian_edge_count(sys->edgehash, i3, i1); + t3 = cotangent_tri_weight_v3(v3, v1, v2) / laplacian_edge_count(sys->edgehash, i1, i2); - EIG_linear_solver_matrix_add(sys->context, i1, i1, (t2 + t3) * varea[i1]); - EIG_linear_solver_matrix_add(sys->context, i2, i2, (t1 + t3) * varea[i2]); - EIG_linear_solver_matrix_add(sys->context, i3, i3, (t1 + t2) * varea[i3]); + EIG_linear_solver_matrix_add(sys->context, i1, i1, (t2 + t3) * varea[i1]); + EIG_linear_solver_matrix_add(sys->context, i2, i2, (t1 + t3) * varea[i2]); + EIG_linear_solver_matrix_add(sys->context, i3, i3, (t1 + t2) * varea[i3]); - EIG_linear_solver_matrix_add(sys->context, i1, i2, -t3 * varea[i1]); - EIG_linear_solver_matrix_add(sys->context, i2, i1, -t3 * varea[i2]); + EIG_linear_solver_matrix_add(sys->context, i1, i2, -t3 * varea[i1]); + EIG_linear_solver_matrix_add(sys->context, i2, i1, -t3 * varea[i2]); - EIG_linear_solver_matrix_add(sys->context, i2, i3, -t1 * varea[i2]); - EIG_linear_solver_matrix_add(sys->context, i3, i2, -t1 * varea[i3]); + EIG_linear_solver_matrix_add(sys->context, i2, i3, -t1 * varea[i2]); + EIG_linear_solver_matrix_add(sys->context, i3, i2, -t1 * varea[i3]); - EIG_linear_solver_matrix_add(sys->context, i3, i1, -t2 * varea[i3]); - EIG_linear_solver_matrix_add(sys->context, i1, i3, -t2 * varea[i1]); + EIG_linear_solver_matrix_add(sys->context, i3, i1, -t2 * varea[i3]); + EIG_linear_solver_matrix_add(sys->context, i1, i3, -t2 * varea[i1]); - if (sys->storeweights) { - sys->fweights[f][0] = t1 * varea[i1]; - sys->fweights[f][1] = t2 * varea[i2]; - sys->fweights[f][2] = t3 * varea[i3]; - } + if (sys->storeweights) { + sys->fweights[f][0] = t1 * varea[i1]; + sys->fweights[f][1] = t2 * varea[i2]; + sys->fweights[f][2] = t3 * varea[i3]; + } } static LaplacianSystem *laplacian_system_construct_begin(int totvert, int totface, int lsq) { - LaplacianSystem *sys; + LaplacianSystem *sys; - sys = MEM_callocN(sizeof(LaplacianSystem), "LaplacianSystem"); + sys = MEM_callocN(sizeof(LaplacianSystem), "LaplacianSystem"); - sys->verts = MEM_callocN(sizeof(float *) * totvert, "LaplacianSystemVerts"); - sys->vpinned = MEM_callocN(sizeof(char) * totvert, "LaplacianSystemVpinned"); - sys->faces = MEM_callocN(sizeof(int) * 3 * totface, "LaplacianSystemFaces"); + sys->verts = MEM_callocN(sizeof(float *) * totvert, "LaplacianSystemVerts"); + sys->vpinned = MEM_callocN(sizeof(char) * totvert, "LaplacianSystemVpinned"); + sys->faces = MEM_callocN(sizeof(int) * 3 * totface, "LaplacianSystemFaces"); - sys->totvert = 0; - sys->totface = 0; + sys->totvert = 0; + sys->totface = 0; - sys->areaweights = 1; - sys->storeweights = 0; + sys->areaweights = 1; + sys->storeweights = 0; - /* create linear solver */ - if (lsq) - sys->context = EIG_linear_least_squares_solver_new(0, totvert, 1); - else - sys->context = EIG_linear_solver_new(0, totvert, 1); + /* create linear solver */ + if (lsq) + sys->context = EIG_linear_least_squares_solver_new(0, totvert, 1); + else + sys->context = EIG_linear_solver_new(0, totvert, 1); - return sys; + return sys; } void laplacian_add_vertex(LaplacianSystem *sys, float *co, int pinned) { - sys->verts[sys->totvert] = co; - sys->vpinned[sys->totvert] = pinned; - sys->totvert++; + sys->verts[sys->totvert] = co; + sys->vpinned[sys->totvert] = pinned; + sys->totvert++; } void laplacian_add_triangle(LaplacianSystem *sys, int v1, int v2, int v3) { - sys->faces[sys->totface][0] = v1; - sys->faces[sys->totface][1] = v2; - sys->faces[sys->totface][2] = v3; - sys->totface++; + sys->faces[sys->totface][0] = v1; + sys->faces[sys->totface][1] = v2; + sys->faces[sys->totface][2] = v3; + sys->totface++; } static void laplacian_system_construct_end(LaplacianSystem *sys) { - int (*face)[3]; - int a, totvert = sys->totvert, totface = sys->totface; + int(*face)[3]; + int a, totvert = sys->totvert, totface = sys->totface; - laplacian_begin_solve(sys, 0); + laplacian_begin_solve(sys, 0); - sys->varea = MEM_callocN(sizeof(float) * totvert, "LaplacianSystemVarea"); + sys->varea = MEM_callocN(sizeof(float) * totvert, "LaplacianSystemVarea"); - sys->edgehash = BLI_edgehash_new_ex(__func__, BLI_EDGEHASH_SIZE_GUESS_FROM_POLYS(sys->totface)); - for (a = 0, face = sys->faces; a < sys->totface; a++, face++) { - laplacian_increase_edge_count(sys->edgehash, (*face)[0], (*face)[1]); - laplacian_increase_edge_count(sys->edgehash, (*face)[1], (*face)[2]); - laplacian_increase_edge_count(sys->edgehash, (*face)[2], (*face)[0]); - } + sys->edgehash = BLI_edgehash_new_ex(__func__, BLI_EDGEHASH_SIZE_GUESS_FROM_POLYS(sys->totface)); + for (a = 0, face = sys->faces; a < sys->totface; a++, face++) { + laplacian_increase_edge_count(sys->edgehash, (*face)[0], (*face)[1]); + laplacian_increase_edge_count(sys->edgehash, (*face)[1], (*face)[2]); + laplacian_increase_edge_count(sys->edgehash, (*face)[2], (*face)[0]); + } - if (sys->areaweights) - for (a = 0, face = sys->faces; a < sys->totface; a++, face++) - laplacian_triangle_area(sys, (*face)[0], (*face)[1], (*face)[2]); + if (sys->areaweights) + for (a = 0, face = sys->faces; a < sys->totface; a++, face++) + laplacian_triangle_area(sys, (*face)[0], (*face)[1], (*face)[2]); - for (a = 0; a < totvert; a++) { - if (sys->areaweights) { - if (sys->varea[a] != 0.0f) - sys->varea[a] = 0.5f / sys->varea[a]; - } - else - sys->varea[a] = 1.0f; + for (a = 0; a < totvert; a++) { + if (sys->areaweights) { + if (sys->varea[a] != 0.0f) + sys->varea[a] = 0.5f / sys->varea[a]; + } + else + sys->varea[a] = 1.0f; - /* for heat weighting */ - if (sys->heat.H) - EIG_linear_solver_matrix_add(sys->context, a, a, sys->heat.H[a]); - } + /* for heat weighting */ + if (sys->heat.H) + EIG_linear_solver_matrix_add(sys->context, a, a, sys->heat.H[a]); + } - if (sys->storeweights) - sys->fweights = MEM_callocN(sizeof(float) * 3 * totface, "LaplacianFWeight"); + if (sys->storeweights) + sys->fweights = MEM_callocN(sizeof(float) * 3 * totface, "LaplacianFWeight"); - for (a = 0, face = sys->faces; a < totface; a++, face++) - laplacian_triangle_weights(sys, a, (*face)[0], (*face)[1], (*face)[2]); + for (a = 0, face = sys->faces; a < totface; a++, face++) + laplacian_triangle_weights(sys, a, (*face)[0], (*face)[1], (*face)[2]); - MEM_freeN(sys->faces); - sys->faces = NULL; + MEM_freeN(sys->faces); + sys->faces = NULL; - if (sys->varea) { - MEM_freeN(sys->varea); - sys->varea = NULL; - } + if (sys->varea) { + MEM_freeN(sys->varea); + sys->varea = NULL; + } - BLI_edgehash_free(sys->edgehash, NULL); - sys->edgehash = NULL; + BLI_edgehash_free(sys->edgehash, NULL); + sys->edgehash = NULL; } static void laplacian_system_delete(LaplacianSystem *sys) { - if (sys->verts) MEM_freeN(sys->verts); - if (sys->varea) MEM_freeN(sys->varea); - if (sys->vpinned) MEM_freeN(sys->vpinned); - if (sys->faces) MEM_freeN(sys->faces); - if (sys->fweights) MEM_freeN(sys->fweights); - - EIG_linear_solver_delete(sys->context); - MEM_freeN(sys); + if (sys->verts) + MEM_freeN(sys->verts); + if (sys->varea) + MEM_freeN(sys->varea); + if (sys->vpinned) + MEM_freeN(sys->vpinned); + if (sys->faces) + MEM_freeN(sys->faces); + if (sys->fweights) + MEM_freeN(sys->fweights); + + EIG_linear_solver_delete(sys->context); + MEM_freeN(sys); } void laplacian_begin_solve(LaplacianSystem *sys, int index) { - int a; - - if (!sys->variablesdone) { - if (index >= 0) { - for (a = 0; a < sys->totvert; a++) { - if (sys->vpinned[a]) { - EIG_linear_solver_variable_set(sys->context, 0, a, sys->verts[a][index]); - EIG_linear_solver_variable_lock(sys->context, a); - } - } - } - - sys->variablesdone = true; - } + int a; + + if (!sys->variablesdone) { + if (index >= 0) { + for (a = 0; a < sys->totvert; a++) { + if (sys->vpinned[a]) { + EIG_linear_solver_variable_set(sys->context, 0, a, sys->verts[a][index]); + EIG_linear_solver_variable_lock(sys->context, a); + } + } + } + + sys->variablesdone = true; + } } void laplacian_add_right_hand_side(LaplacianSystem *sys, int v, float value) { - EIG_linear_solver_right_hand_side_add(sys->context, 0, v, value); + EIG_linear_solver_right_hand_side_add(sys->context, 0, v, value); } int laplacian_system_solve(LaplacianSystem *sys) { - sys->variablesdone = false; + sys->variablesdone = false; - //EIG_linear_solver_print_matrix(sys->context, ); + //EIG_linear_solver_print_matrix(sys->context, ); - return EIG_linear_solver_solve(sys->context); + return EIG_linear_solver_solve(sys->context); } float laplacian_system_get_solution(LaplacianSystem *sys, int v) { - return EIG_linear_solver_variable_get(sys->context, 0, v); + return EIG_linear_solver_variable_get(sys->context, 0, v); } /************************* Heat Bone Weighting ******************************/ /* From "Automatic Rigging and Animation of 3D Characters" * Ilya Baran and Jovan Popovic, SIGGRAPH 2007 */ -#define C_WEIGHT 1.0f -#define WEIGHT_LIMIT_START 0.05f -#define WEIGHT_LIMIT_END 0.025f -#define DISTANCE_EPSILON 1e-4f +#define C_WEIGHT 1.0f +#define WEIGHT_LIMIT_START 0.05f +#define WEIGHT_LIMIT_END 0.025f +#define DISTANCE_EPSILON 1e-4f typedef struct BVHCallbackUserData { - float start[3]; - float vec[3]; - LaplacianSystem *sys; + float start[3]; + float vec[3]; + LaplacianSystem *sys; } BVHCallbackUserData; static void bvh_callback(void *userdata, int index, const BVHTreeRay *ray, BVHTreeRayHit *hit) { - BVHCallbackUserData *data = (struct BVHCallbackUserData *)userdata; - const MLoopTri *lt = &data->sys->heat.mlooptri[index]; - const MLoop *mloop = data->sys->heat.mloop; - float (*verts)[3] = data->sys->heat.verts; - const float *vtri_co[3]; - float dist_test; + BVHCallbackUserData *data = (struct BVHCallbackUserData *)userdata; + const MLoopTri *lt = &data->sys->heat.mlooptri[index]; + const MLoop *mloop = data->sys->heat.mloop; + float(*verts)[3] = data->sys->heat.verts; + const float *vtri_co[3]; + float dist_test; - vtri_co[0] = verts[mloop[lt->tri[0]].v]; - vtri_co[1] = verts[mloop[lt->tri[1]].v]; - vtri_co[2] = verts[mloop[lt->tri[2]].v]; + vtri_co[0] = verts[mloop[lt->tri[0]].v]; + vtri_co[1] = verts[mloop[lt->tri[1]].v]; + vtri_co[2] = verts[mloop[lt->tri[2]].v]; #ifdef USE_KDOPBVH_WATERTIGHT - if (isect_ray_tri_watertight_v3(data->start, ray->isect_precalc, UNPACK3(vtri_co), &dist_test, NULL)) + if (isect_ray_tri_watertight_v3( + data->start, ray->isect_precalc, UNPACK3(vtri_co), &dist_test, NULL)) #else - UNUSED_VARS(ray); - if (isect_ray_tri_v3(data->start, data->vec, UNPACK3(vtri_co), &dist_test, NULL)) + UNUSED_VARS(ray); + if (isect_ray_tri_v3(data->start, data->vec, UNPACK3(vtri_co), &dist_test, NULL)) #endif - { - if (dist_test < hit->dist) { - float n[3]; - normal_tri_v3(n, UNPACK3(vtri_co)); - if (dot_v3v3(n, data->vec) < -1e-5f) { - hit->index = index; - hit->dist = dist_test; - } - } - } + { + if (dist_test < hit->dist) { + float n[3]; + normal_tri_v3(n, UNPACK3(vtri_co)); + if (dot_v3v3(n, data->vec) < -1e-5f) { + hit->index = index; + hit->dist = dist_test; + } + } + } } /* Raytracing for vertex to bone/vertex visibility */ static void heat_ray_tree_create(LaplacianSystem *sys) { - const MLoopTri *looptri = sys->heat.mlooptri; - const MLoop *mloop = sys->heat.mloop; - float (*verts)[3] = sys->heat.verts; - int tottri = sys->heat.tottri; - int totvert = sys->heat.totvert; - int a; - - sys->heat.bvhtree = BLI_bvhtree_new(tottri, 0.0f, 4, 6); - sys->heat.vltree = MEM_callocN(sizeof(MLoopTri *) * totvert, "HeatVFaces"); - - for (a = 0; a < tottri; a++) { - const MLoopTri *lt = &looptri[a]; - float bb[6]; - int vtri[3]; - - vtri[0] = mloop[lt->tri[0]].v; - vtri[1] = mloop[lt->tri[1]].v; - vtri[2] = mloop[lt->tri[2]].v; - - INIT_MINMAX(bb, bb + 3); - minmax_v3v3_v3(bb, bb + 3, verts[vtri[0]]); - minmax_v3v3_v3(bb, bb + 3, verts[vtri[1]]); - minmax_v3v3_v3(bb, bb + 3, verts[vtri[2]]); - - BLI_bvhtree_insert(sys->heat.bvhtree, a, bb, 2); - - //Setup inverse pointers to use on isect.orig - sys->heat.vltree[vtri[0]] = lt; - sys->heat.vltree[vtri[1]] = lt; - sys->heat.vltree[vtri[2]] = lt; - } - - BLI_bvhtree_balance(sys->heat.bvhtree); + const MLoopTri *looptri = sys->heat.mlooptri; + const MLoop *mloop = sys->heat.mloop; + float(*verts)[3] = sys->heat.verts; + int tottri = sys->heat.tottri; + int totvert = sys->heat.totvert; + int a; + + sys->heat.bvhtree = BLI_bvhtree_new(tottri, 0.0f, 4, 6); + sys->heat.vltree = MEM_callocN(sizeof(MLoopTri *) * totvert, "HeatVFaces"); + + for (a = 0; a < tottri; a++) { + const MLoopTri *lt = &looptri[a]; + float bb[6]; + int vtri[3]; + + vtri[0] = mloop[lt->tri[0]].v; + vtri[1] = mloop[lt->tri[1]].v; + vtri[2] = mloop[lt->tri[2]].v; + + INIT_MINMAX(bb, bb + 3); + minmax_v3v3_v3(bb, bb + 3, verts[vtri[0]]); + minmax_v3v3_v3(bb, bb + 3, verts[vtri[1]]); + minmax_v3v3_v3(bb, bb + 3, verts[vtri[2]]); + + BLI_bvhtree_insert(sys->heat.bvhtree, a, bb, 2); + + //Setup inverse pointers to use on isect.orig + sys->heat.vltree[vtri[0]] = lt; + sys->heat.vltree[vtri[1]] = lt; + sys->heat.vltree[vtri[2]] = lt; + } + + BLI_bvhtree_balance(sys->heat.bvhtree); } static int heat_ray_source_visible(LaplacianSystem *sys, int vertex, int source) { - BVHTreeRayHit hit; - BVHCallbackUserData data; - const MLoopTri *lt; - float end[3]; - int visible; + BVHTreeRayHit hit; + BVHCallbackUserData data; + const MLoopTri *lt; + float end[3]; + int visible; - lt = sys->heat.vltree[vertex]; - if (lt == NULL) - return 1; + lt = sys->heat.vltree[vertex]; + if (lt == NULL) + return 1; - data.sys = sys; - copy_v3_v3(data.start, sys->heat.verts[vertex]); + data.sys = sys; + copy_v3_v3(data.start, sys->heat.verts[vertex]); - closest_to_line_segment_v3(end, data.start, sys->heat.root[source], sys->heat.tip[source]); + closest_to_line_segment_v3(end, data.start, sys->heat.root[source], sys->heat.tip[source]); - sub_v3_v3v3(data.vec, end, data.start); - madd_v3_v3v3fl(data.start, data.start, data.vec, 1e-5); - mul_v3_fl(data.vec, 1.0f - 2e-5f); + sub_v3_v3v3(data.vec, end, data.start); + madd_v3_v3v3fl(data.start, data.start, data.vec, 1e-5); + mul_v3_fl(data.vec, 1.0f - 2e-5f); - /* pass normalized vec + distance to bvh */ - hit.index = -1; - hit.dist = normalize_v3(data.vec); + /* pass normalized vec + distance to bvh */ + hit.index = -1; + hit.dist = normalize_v3(data.vec); - visible = BLI_bvhtree_ray_cast(sys->heat.bvhtree, data.start, data.vec, 0.0f, &hit, bvh_callback, (void *)&data) == -1; + visible = + BLI_bvhtree_ray_cast( + sys->heat.bvhtree, data.start, data.vec, 0.0f, &hit, bvh_callback, (void *)&data) == -1; - return visible; + return visible; } static float heat_source_distance(LaplacianSystem *sys, int vertex, int source) { - float closest[3], d[3], dist, cosine; + float closest[3], d[3], dist, cosine; - /* compute euclidian distance */ - closest_to_line_segment_v3(closest, sys->heat.verts[vertex], sys->heat.root[source], sys->heat.tip[source]); + /* compute euclidian distance */ + closest_to_line_segment_v3( + closest, sys->heat.verts[vertex], sys->heat.root[source], sys->heat.tip[source]); - sub_v3_v3v3(d, sys->heat.verts[vertex], closest); - dist = normalize_v3(d); + sub_v3_v3v3(d, sys->heat.verts[vertex], closest); + dist = normalize_v3(d); - /* if the vertex normal does not point along the bone, increase distance */ - cosine = dot_v3v3(d, sys->heat.vnors[vertex]); + /* if the vertex normal does not point along the bone, increase distance */ + cosine = dot_v3v3(d, sys->heat.vnors[vertex]); - return dist / (0.5f * (cosine + 1.001f)); + return dist / (0.5f * (cosine + 1.001f)); } static int heat_source_closest(LaplacianSystem *sys, int vertex, int source) { - float dist; + float dist; - dist = heat_source_distance(sys, vertex, source); + dist = heat_source_distance(sys, vertex, source); - if (dist <= sys->heat.mindist[vertex] * (1.0f + DISTANCE_EPSILON)) - if (heat_ray_source_visible(sys, vertex, source)) - return 1; + if (dist <= sys->heat.mindist[vertex] * (1.0f + DISTANCE_EPSILON)) + if (heat_ray_source_visible(sys, vertex, source)) + return 1; - return 0; + return 0; } static void heat_set_H(LaplacianSystem *sys, int vertex) { - float dist, mindist, h; - int j, numclosest = 0; + float dist, mindist, h; + int j, numclosest = 0; - mindist = 1e10; + mindist = 1e10; - /* compute minimum distance */ - for (j = 0; j < sys->heat.numsource; j++) { - dist = heat_source_distance(sys, vertex, j); + /* compute minimum distance */ + for (j = 0; j < sys->heat.numsource; j++) { + dist = heat_source_distance(sys, vertex, j); - if (dist < mindist) - mindist = dist; - } + if (dist < mindist) + mindist = dist; + } - sys->heat.mindist[vertex] = mindist; + sys->heat.mindist[vertex] = mindist; - /* count number of sources with approximately this minimum distance */ - for (j = 0; j < sys->heat.numsource; j++) - if (heat_source_closest(sys, vertex, j)) - numclosest++; + /* count number of sources with approximately this minimum distance */ + for (j = 0; j < sys->heat.numsource; j++) + if (heat_source_closest(sys, vertex, j)) + numclosest++; - sys->heat.p[vertex] = (numclosest > 0) ? 1.0f / numclosest : 0.0f; + sys->heat.p[vertex] = (numclosest > 0) ? 1.0f / numclosest : 0.0f; - /* compute H entry */ - if (numclosest > 0) { - mindist = max_ff(mindist, 1e-4f); - h = numclosest * C_WEIGHT / (mindist * mindist); - } - else - h = 0.0f; + /* compute H entry */ + if (numclosest > 0) { + mindist = max_ff(mindist, 1e-4f); + h = numclosest * C_WEIGHT / (mindist * mindist); + } + else + h = 0.0f; - sys->heat.H[vertex] = h; + sys->heat.H[vertex] = h; } static void heat_calc_vnormals(LaplacianSystem *sys) { - float fnor[3]; - int a, v1, v2, v3, (*face)[3]; + float fnor[3]; + int a, v1, v2, v3, (*face)[3]; - sys->heat.vnors = MEM_callocN(sizeof(float) * 3 * sys->totvert, "HeatVNors"); + sys->heat.vnors = MEM_callocN(sizeof(float) * 3 * sys->totvert, "HeatVNors"); - for (a = 0, face = sys->faces; a < sys->totface; a++, face++) { - v1 = (*face)[0]; - v2 = (*face)[1]; - v3 = (*face)[2]; + for (a = 0, face = sys->faces; a < sys->totface; a++, face++) { + v1 = (*face)[0]; + v2 = (*face)[1]; + v3 = (*face)[2]; - normal_tri_v3(fnor, sys->verts[v1], sys->verts[v2], sys->verts[v3]); + normal_tri_v3(fnor, sys->verts[v1], sys->verts[v2], sys->verts[v3]); - add_v3_v3(sys->heat.vnors[v1], fnor); - add_v3_v3(sys->heat.vnors[v2], fnor); - add_v3_v3(sys->heat.vnors[v3], fnor); - } + add_v3_v3(sys->heat.vnors[v1], fnor); + add_v3_v3(sys->heat.vnors[v2], fnor); + add_v3_v3(sys->heat.vnors[v3], fnor); + } - for (a = 0; a < sys->totvert; a++) - normalize_v3(sys->heat.vnors[a]); + for (a = 0; a < sys->totvert; a++) + normalize_v3(sys->heat.vnors[a]); } static void heat_laplacian_create(LaplacianSystem *sys) { - const MLoopTri *mlooptri = sys->heat.mlooptri, *lt; - const MLoop *mloop = sys->heat.mloop; - int tottri = sys->heat.tottri; - int totvert = sys->heat.totvert; - int a; - - /* heat specific definitions */ - sys->heat.mindist = MEM_callocN(sizeof(float) * totvert, "HeatMinDist"); - sys->heat.H = MEM_callocN(sizeof(float) * totvert, "HeatH"); - sys->heat.p = MEM_callocN(sizeof(float) * totvert, "HeatP"); - - /* add verts and faces to laplacian */ - for (a = 0; a < totvert; a++) - laplacian_add_vertex(sys, sys->heat.verts[a], 0); - - for (a = 0, lt = mlooptri; a < tottri; a++, lt++) { - int vtri[3]; - vtri[0] = mloop[lt->tri[0]].v; - vtri[1] = mloop[lt->tri[1]].v; - vtri[2] = mloop[lt->tri[2]].v; - laplacian_add_triangle(sys, UNPACK3(vtri)); - } - - /* for distance computation in set_H */ - heat_calc_vnormals(sys); - - for (a = 0; a < totvert; a++) - heat_set_H(sys, a); + const MLoopTri *mlooptri = sys->heat.mlooptri, *lt; + const MLoop *mloop = sys->heat.mloop; + int tottri = sys->heat.tottri; + int totvert = sys->heat.totvert; + int a; + + /* heat specific definitions */ + sys->heat.mindist = MEM_callocN(sizeof(float) * totvert, "HeatMinDist"); + sys->heat.H = MEM_callocN(sizeof(float) * totvert, "HeatH"); + sys->heat.p = MEM_callocN(sizeof(float) * totvert, "HeatP"); + + /* add verts and faces to laplacian */ + for (a = 0; a < totvert; a++) + laplacian_add_vertex(sys, sys->heat.verts[a], 0); + + for (a = 0, lt = mlooptri; a < tottri; a++, lt++) { + int vtri[3]; + vtri[0] = mloop[lt->tri[0]].v; + vtri[1] = mloop[lt->tri[1]].v; + vtri[2] = mloop[lt->tri[2]].v; + laplacian_add_triangle(sys, UNPACK3(vtri)); + } + + /* for distance computation in set_H */ + heat_calc_vnormals(sys); + + for (a = 0; a < totvert; a++) + heat_set_H(sys, a); } static void heat_system_free(LaplacianSystem *sys) { - BLI_bvhtree_free(sys->heat.bvhtree); - MEM_freeN((void *)sys->heat.vltree); - MEM_freeN((void *)sys->heat.mlooptri); - - MEM_freeN(sys->heat.mindist); - MEM_freeN(sys->heat.H); - MEM_freeN(sys->heat.p); - MEM_freeN(sys->heat.vnors); + BLI_bvhtree_free(sys->heat.bvhtree); + MEM_freeN((void *)sys->heat.vltree); + MEM_freeN((void *)sys->heat.mlooptri); + + MEM_freeN(sys->heat.mindist); + MEM_freeN(sys->heat.H); + MEM_freeN(sys->heat.p); + MEM_freeN(sys->heat.vnors); } static float heat_limit_weight(float weight) { - float t; - - if (weight < WEIGHT_LIMIT_END) { - return 0.0f; - } - else if (weight < WEIGHT_LIMIT_START) { - t = (weight - WEIGHT_LIMIT_END) / (WEIGHT_LIMIT_START - WEIGHT_LIMIT_END); - return t * WEIGHT_LIMIT_START; - } - else - return weight; + float t; + + if (weight < WEIGHT_LIMIT_END) { + return 0.0f; + } + else if (weight < WEIGHT_LIMIT_START) { + t = (weight - WEIGHT_LIMIT_END) / (WEIGHT_LIMIT_START - WEIGHT_LIMIT_END); + return t * WEIGHT_LIMIT_START; + } + else + return weight; } -void heat_bone_weighting( - Object *ob, Mesh *me, float (*verts)[3], int numsource, - bDeformGroup **dgrouplist, bDeformGroup **dgroupflip, - float (*root)[3], float (*tip)[3], int *selected, const char **err_str) +void heat_bone_weighting(Object *ob, + Mesh *me, + float (*verts)[3], + int numsource, + bDeformGroup **dgrouplist, + bDeformGroup **dgroupflip, + float (*root)[3], + float (*tip)[3], + int *selected, + const char **err_str) { - LaplacianSystem *sys; - MLoopTri *mlooptri; - MPoly *mp; - MLoop *ml; - float solution, weight; - int *vertsflipped = NULL, *mask = NULL; - int a, tottri, j, bbone, firstsegment, lastsegment; - bool use_topology = (me->editflag & ME_EDIT_MIRROR_TOPO) != 0; - - MVert *mvert = me->mvert; - bool use_vert_sel = (me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0; - bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; - - *err_str = NULL; - - /* bone heat needs triangulated faces */ - tottri = poly_to_tri_count(me->totpoly, me->totloop); - - /* count triangles and create mask */ - if (ob->mode & OB_MODE_WEIGHT_PAINT && - (use_face_sel || use_vert_sel)) - { - mask = MEM_callocN(sizeof(int) * me->totvert, "heat_bone_weighting mask"); - - /* (added selectedVerts content for vertex mask, they used to just equal 1) */ - if (use_vert_sel) { - for (a = 0, mp = me->mpoly; a < me->totpoly; mp++, a++) { - for (j = 0, ml = me->mloop + mp->loopstart; j < mp->totloop; j++, ml++) { - mask[ml->v] = (mvert[ml->v].flag & SELECT) != 0; - } - } - } - else if (use_face_sel) { - for (a = 0, mp = me->mpoly; a < me->totpoly; mp++, a++) { - if (mp->flag & ME_FACE_SEL) { - for (j = 0, ml = me->mloop + mp->loopstart; j < mp->totloop; j++, ml++) { - mask[ml->v] = 1; - } - } - } - } - } - - /* create laplacian */ - sys = laplacian_system_construct_begin(me->totvert, tottri, 1); - - sys->heat.tottri = poly_to_tri_count(me->totpoly, me->totloop); - mlooptri = MEM_mallocN(sizeof(*sys->heat.mlooptri) * sys->heat.tottri, __func__); - - BKE_mesh_recalc_looptri( - me->mloop, me->mpoly, - me->mvert, - me->totloop, me->totpoly, - mlooptri); - - sys->heat.mlooptri = mlooptri; - sys->heat.mloop = me->mloop; - sys->heat.totvert = me->totvert; - sys->heat.verts = verts; - sys->heat.root = root; - sys->heat.tip = tip; - sys->heat.numsource = numsource; - - heat_ray_tree_create(sys); - heat_laplacian_create(sys); - - laplacian_system_construct_end(sys); - - if (dgroupflip) { - vertsflipped = MEM_callocN(sizeof(int) * me->totvert, "vertsflipped"); - for (a = 0; a < me->totvert; a++) - vertsflipped[a] = mesh_get_x_mirror_vert(ob, NULL, a, use_topology); - } - - /* compute weights per bone */ - for (j = 0; j < numsource; j++) { - if (!selected[j]) - continue; - - firstsegment = (j == 0 || dgrouplist[j - 1] != dgrouplist[j]); - lastsegment = (j == numsource - 1 || dgrouplist[j] != dgrouplist[j + 1]); - bbone = !(firstsegment && lastsegment); - - /* clear weights */ - if (bbone && firstsegment) { - for (a = 0; a < me->totvert; a++) { - if (mask && !mask[a]) - continue; - - ED_vgroup_vert_remove(ob, dgrouplist[j], a); - if (vertsflipped && dgroupflip[j] && vertsflipped[a] >= 0) - ED_vgroup_vert_remove(ob, dgroupflip[j], vertsflipped[a]); - } - } - - /* fill right hand side */ - laplacian_begin_solve(sys, -1); - - for (a = 0; a < me->totvert; a++) - if (heat_source_closest(sys, a, j)) - laplacian_add_right_hand_side(sys, a, - sys->heat.H[a] * sys->heat.p[a]); - - /* solve */ - if (laplacian_system_solve(sys)) { - /* load solution into vertex groups */ - for (a = 0; a < me->totvert; a++) { - if (mask && !mask[a]) - continue; - - solution = laplacian_system_get_solution(sys, a); - - if (bbone) { - if (solution > 0.0f) - ED_vgroup_vert_add(ob, dgrouplist[j], a, solution, - WEIGHT_ADD); - } - else { - weight = heat_limit_weight(solution); - if (weight > 0.0f) - ED_vgroup_vert_add(ob, dgrouplist[j], a, weight, - WEIGHT_REPLACE); - else - ED_vgroup_vert_remove(ob, dgrouplist[j], a); - } - - /* do same for mirror */ - if (vertsflipped && dgroupflip[j] && vertsflipped[a] >= 0) { - if (bbone) { - if (solution > 0.0f) - ED_vgroup_vert_add(ob, dgroupflip[j], vertsflipped[a], - solution, WEIGHT_ADD); - } - else { - weight = heat_limit_weight(solution); - if (weight > 0.0f) - ED_vgroup_vert_add(ob, dgroupflip[j], vertsflipped[a], - weight, WEIGHT_REPLACE); - else - ED_vgroup_vert_remove(ob, dgroupflip[j], vertsflipped[a]); - } - } - } - } - else if (*err_str == NULL) { - *err_str = N_("Bone Heat Weighting: failed to find solution for one or more bones"); - break; - } - - /* remove too small vertex weights */ - if (bbone && lastsegment) { - for (a = 0; a < me->totvert; a++) { - if (mask && !mask[a]) - continue; - - weight = ED_vgroup_vert_weight(ob, dgrouplist[j], a); - weight = heat_limit_weight(weight); - if (weight <= 0.0f) - ED_vgroup_vert_remove(ob, dgrouplist[j], a); - - if (vertsflipped && dgroupflip[j] && vertsflipped[a] >= 0) { - weight = ED_vgroup_vert_weight(ob, dgroupflip[j], vertsflipped[a]); - weight = heat_limit_weight(weight); - if (weight <= 0.0f) - ED_vgroup_vert_remove(ob, dgroupflip[j], vertsflipped[a]); - } - } - } - } - - /* free */ - if (vertsflipped) MEM_freeN(vertsflipped); - if (mask) MEM_freeN(mask); - - heat_system_free(sys); - - laplacian_system_delete(sys); + LaplacianSystem *sys; + MLoopTri *mlooptri; + MPoly *mp; + MLoop *ml; + float solution, weight; + int *vertsflipped = NULL, *mask = NULL; + int a, tottri, j, bbone, firstsegment, lastsegment; + bool use_topology = (me->editflag & ME_EDIT_MIRROR_TOPO) != 0; + + MVert *mvert = me->mvert; + bool use_vert_sel = (me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0; + bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; + + *err_str = NULL; + + /* bone heat needs triangulated faces */ + tottri = poly_to_tri_count(me->totpoly, me->totloop); + + /* count triangles and create mask */ + if (ob->mode & OB_MODE_WEIGHT_PAINT && (use_face_sel || use_vert_sel)) { + mask = MEM_callocN(sizeof(int) * me->totvert, "heat_bone_weighting mask"); + + /* (added selectedVerts content for vertex mask, they used to just equal 1) */ + if (use_vert_sel) { + for (a = 0, mp = me->mpoly; a < me->totpoly; mp++, a++) { + for (j = 0, ml = me->mloop + mp->loopstart; j < mp->totloop; j++, ml++) { + mask[ml->v] = (mvert[ml->v].flag & SELECT) != 0; + } + } + } + else if (use_face_sel) { + for (a = 0, mp = me->mpoly; a < me->totpoly; mp++, a++) { + if (mp->flag & ME_FACE_SEL) { + for (j = 0, ml = me->mloop + mp->loopstart; j < mp->totloop; j++, ml++) { + mask[ml->v] = 1; + } + } + } + } + } + + /* create laplacian */ + sys = laplacian_system_construct_begin(me->totvert, tottri, 1); + + sys->heat.tottri = poly_to_tri_count(me->totpoly, me->totloop); + mlooptri = MEM_mallocN(sizeof(*sys->heat.mlooptri) * sys->heat.tottri, __func__); + + BKE_mesh_recalc_looptri(me->mloop, me->mpoly, me->mvert, me->totloop, me->totpoly, mlooptri); + + sys->heat.mlooptri = mlooptri; + sys->heat.mloop = me->mloop; + sys->heat.totvert = me->totvert; + sys->heat.verts = verts; + sys->heat.root = root; + sys->heat.tip = tip; + sys->heat.numsource = numsource; + + heat_ray_tree_create(sys); + heat_laplacian_create(sys); + + laplacian_system_construct_end(sys); + + if (dgroupflip) { + vertsflipped = MEM_callocN(sizeof(int) * me->totvert, "vertsflipped"); + for (a = 0; a < me->totvert; a++) + vertsflipped[a] = mesh_get_x_mirror_vert(ob, NULL, a, use_topology); + } + + /* compute weights per bone */ + for (j = 0; j < numsource; j++) { + if (!selected[j]) + continue; + + firstsegment = (j == 0 || dgrouplist[j - 1] != dgrouplist[j]); + lastsegment = (j == numsource - 1 || dgrouplist[j] != dgrouplist[j + 1]); + bbone = !(firstsegment && lastsegment); + + /* clear weights */ + if (bbone && firstsegment) { + for (a = 0; a < me->totvert; a++) { + if (mask && !mask[a]) + continue; + + ED_vgroup_vert_remove(ob, dgrouplist[j], a); + if (vertsflipped && dgroupflip[j] && vertsflipped[a] >= 0) + ED_vgroup_vert_remove(ob, dgroupflip[j], vertsflipped[a]); + } + } + + /* fill right hand side */ + laplacian_begin_solve(sys, -1); + + for (a = 0; a < me->totvert; a++) + if (heat_source_closest(sys, a, j)) + laplacian_add_right_hand_side(sys, a, sys->heat.H[a] * sys->heat.p[a]); + + /* solve */ + if (laplacian_system_solve(sys)) { + /* load solution into vertex groups */ + for (a = 0; a < me->totvert; a++) { + if (mask && !mask[a]) + continue; + + solution = laplacian_system_get_solution(sys, a); + + if (bbone) { + if (solution > 0.0f) + ED_vgroup_vert_add(ob, dgrouplist[j], a, solution, WEIGHT_ADD); + } + else { + weight = heat_limit_weight(solution); + if (weight > 0.0f) + ED_vgroup_vert_add(ob, dgrouplist[j], a, weight, WEIGHT_REPLACE); + else + ED_vgroup_vert_remove(ob, dgrouplist[j], a); + } + + /* do same for mirror */ + if (vertsflipped && dgroupflip[j] && vertsflipped[a] >= 0) { + if (bbone) { + if (solution > 0.0f) + ED_vgroup_vert_add(ob, dgroupflip[j], vertsflipped[a], solution, WEIGHT_ADD); + } + else { + weight = heat_limit_weight(solution); + if (weight > 0.0f) + ED_vgroup_vert_add(ob, dgroupflip[j], vertsflipped[a], weight, WEIGHT_REPLACE); + else + ED_vgroup_vert_remove(ob, dgroupflip[j], vertsflipped[a]); + } + } + } + } + else if (*err_str == NULL) { + *err_str = N_("Bone Heat Weighting: failed to find solution for one or more bones"); + break; + } + + /* remove too small vertex weights */ + if (bbone && lastsegment) { + for (a = 0; a < me->totvert; a++) { + if (mask && !mask[a]) + continue; + + weight = ED_vgroup_vert_weight(ob, dgrouplist[j], a); + weight = heat_limit_weight(weight); + if (weight <= 0.0f) + ED_vgroup_vert_remove(ob, dgrouplist[j], a); + + if (vertsflipped && dgroupflip[j] && vertsflipped[a] >= 0) { + weight = ED_vgroup_vert_weight(ob, dgroupflip[j], vertsflipped[a]); + weight = heat_limit_weight(weight); + if (weight <= 0.0f) + ED_vgroup_vert_remove(ob, dgroupflip[j], vertsflipped[a]); + } + } + } + } + + /* free */ + if (vertsflipped) + MEM_freeN(vertsflipped); + if (mask) + MEM_freeN(mask); + + heat_system_free(sys); + + laplacian_system_delete(sys); } /************************** Harmonic Coordinates ****************************/ @@ -789,7 +808,7 @@ void heat_bone_weighting( #define EPSILON 0.0001f -#define MESHDEFORM_TAG_UNTYPED 0 +#define MESHDEFORM_TAG_UNTYPED 0 #define MESHDEFORM_TAG_BOUNDARY 1 #define MESHDEFORM_TAG_INTERIOR 2 #define MESHDEFORM_TAG_EXTERIOR 3 @@ -800,828 +819,874 @@ void heat_bone_weighting( #define MESHDEFORM_MIN_INFLUENCE 0.0005f static const int MESHDEFORM_OFFSET[7][3] = { - {0, 0, 0}, {1, 0, 0}, {-1, 0, 0}, {0, 1, 0}, {0, -1, 0}, {0, 0, 1}, {0, 0, -1}, + {0, 0, 0}, + {1, 0, 0}, + {-1, 0, 0}, + {0, 1, 0}, + {0, -1, 0}, + {0, 0, 1}, + {0, 0, -1}, }; typedef struct MDefBoundIsect { - /* intersection on the cage 'cagecos' */ - float co[3]; - /* non-facing intersections are considered interior */ - bool facing; - /* ray-cast index aligned with MPoly (ray-hit-triangle isn't needed) */ - int poly_index; - /* distance from 'co' to the ray-cast start (clamped to avoid zero division) */ - float len; - /* weights aligned with the MPoly's loop indices */ - float poly_weights[0]; + /* intersection on the cage 'cagecos' */ + float co[3]; + /* non-facing intersections are considered interior */ + bool facing; + /* ray-cast index aligned with MPoly (ray-hit-triangle isn't needed) */ + int poly_index; + /* distance from 'co' to the ray-cast start (clamped to avoid zero division) */ + float len; + /* weights aligned with the MPoly's loop indices */ + float poly_weights[0]; } MDefBoundIsect; typedef struct MDefBindInfluence { - struct MDefBindInfluence *next; - float weight; - int vertex; + struct MDefBindInfluence *next; + float weight; + int vertex; } MDefBindInfluence; typedef struct MeshDeformBind { - /* grid dimensions */ - float min[3], max[3]; - float width[3], halfwidth[3]; - int size, size3; - - /* meshes */ - Mesh *cagemesh; - float (*cagecos)[3]; - float (*vertexcos)[3]; - int totvert, totcagevert; - - /* grids */ - MemArena *memarena; - MDefBoundIsect *(*boundisect)[6]; - int *semibound; - int *tag; - float *phi, *totalphi; - - /* mesh stuff */ - int *inside; - float *weights; - MDefBindInfluence **dyngrid; - float cagemat[4][4]; - - /* direct solver */ - int *varidx; - - BVHTree *bvhtree; - BVHTreeFromMesh bvhdata; - - /* avoid DM function calls during intersections */ - struct { - const MPoly *mpoly; - const MLoop *mloop; - const MLoopTri *looptri; - const float (*poly_nors)[3]; - } cagemesh_cache; + /* grid dimensions */ + float min[3], max[3]; + float width[3], halfwidth[3]; + int size, size3; + + /* meshes */ + Mesh *cagemesh; + float (*cagecos)[3]; + float (*vertexcos)[3]; + int totvert, totcagevert; + + /* grids */ + MemArena *memarena; + MDefBoundIsect *(*boundisect)[6]; + int *semibound; + int *tag; + float *phi, *totalphi; + + /* mesh stuff */ + int *inside; + float *weights; + MDefBindInfluence **dyngrid; + float cagemat[4][4]; + + /* direct solver */ + int *varidx; + + BVHTree *bvhtree; + BVHTreeFromMesh bvhdata; + + /* avoid DM function calls during intersections */ + struct { + const MPoly *mpoly; + const MLoop *mloop; + const MLoopTri *looptri; + const float (*poly_nors)[3]; + } cagemesh_cache; } MeshDeformBind; typedef struct MeshDeformIsect { - float start[3]; - float vec[3]; - float vec_length; - float lambda; + float start[3]; + float vec[3]; + float vec_length; + float lambda; - bool isect; - float u, v; + bool isect; + float u, v; } MeshDeformIsect; /* ray intersection */ struct MeshRayCallbackData { - MeshDeformBind *mdb; - MeshDeformIsect *isec; + MeshDeformBind *mdb; + MeshDeformIsect *isec; }; -static void harmonic_ray_callback(void *userdata, int index, const BVHTreeRay *ray, BVHTreeRayHit *hit) +static void harmonic_ray_callback(void *userdata, + int index, + const BVHTreeRay *ray, + BVHTreeRayHit *hit) { - struct MeshRayCallbackData *data = userdata; - MeshDeformBind *mdb = data->mdb; - const MLoop *mloop = mdb->cagemesh_cache.mloop; - const MLoopTri *looptri = mdb->cagemesh_cache.looptri, *lt; - const float (*poly_nors)[3] = mdb->cagemesh_cache.poly_nors; - MeshDeformIsect *isec = data->isec; - float no[3], co[3], dist; - float *face[3]; - - lt = &looptri[index]; - - face[0] = mdb->cagecos[mloop[lt->tri[0]].v]; - face[1] = mdb->cagecos[mloop[lt->tri[1]].v]; - face[2] = mdb->cagecos[mloop[lt->tri[2]].v]; - - bool isect_ray_tri = isect_ray_tri_watertight_v3( - ray->origin, ray->isect_precalc, UNPACK3(face), &dist, NULL); - - if (!isect_ray_tri || dist > isec->vec_length) { - return; - } - - if (poly_nors) { - copy_v3_v3(no, poly_nors[lt->poly]); - } - else { - normal_tri_v3(no, UNPACK3(face)); - } - - madd_v3_v3v3fl(co, ray->origin, ray->direction, dist); - dist /= isec->vec_length; - if (dist < hit->dist) { - hit->index = index; - hit->dist = dist; - copy_v3_v3(hit->co, co); - - isec->isect = (dot_v3v3(no, ray->direction) <= 0.0f); - isec->lambda = dist; - } + struct MeshRayCallbackData *data = userdata; + MeshDeformBind *mdb = data->mdb; + const MLoop *mloop = mdb->cagemesh_cache.mloop; + const MLoopTri *looptri = mdb->cagemesh_cache.looptri, *lt; + const float(*poly_nors)[3] = mdb->cagemesh_cache.poly_nors; + MeshDeformIsect *isec = data->isec; + float no[3], co[3], dist; + float *face[3]; + + lt = &looptri[index]; + + face[0] = mdb->cagecos[mloop[lt->tri[0]].v]; + face[1] = mdb->cagecos[mloop[lt->tri[1]].v]; + face[2] = mdb->cagecos[mloop[lt->tri[2]].v]; + + bool isect_ray_tri = isect_ray_tri_watertight_v3( + ray->origin, ray->isect_precalc, UNPACK3(face), &dist, NULL); + + if (!isect_ray_tri || dist > isec->vec_length) { + return; + } + + if (poly_nors) { + copy_v3_v3(no, poly_nors[lt->poly]); + } + else { + normal_tri_v3(no, UNPACK3(face)); + } + + madd_v3_v3v3fl(co, ray->origin, ray->direction, dist); + dist /= isec->vec_length; + if (dist < hit->dist) { + hit->index = index; + hit->dist = dist; + copy_v3_v3(hit->co, co); + + isec->isect = (dot_v3v3(no, ray->direction) <= 0.0f); + isec->lambda = dist; + } } -static MDefBoundIsect *meshdeform_ray_tree_intersect(MeshDeformBind *mdb, const float co1[3], const float co2[3]) +static MDefBoundIsect *meshdeform_ray_tree_intersect(MeshDeformBind *mdb, + const float co1[3], + const float co2[3]) { - BVHTreeRayHit hit; - MeshDeformIsect isect_mdef; - struct MeshRayCallbackData data = { - mdb, - &isect_mdef, - }; - float end[3], vec_normal[3]; - - /* happens binding when a cage has no faces */ - if (UNLIKELY(mdb->bvhtree == NULL)) - return NULL; - - /* setup isec */ - memset(&isect_mdef, 0, sizeof(isect_mdef)); - isect_mdef.lambda = 1e10f; - - copy_v3_v3(isect_mdef.start, co1); - copy_v3_v3(end, co2); - sub_v3_v3v3(isect_mdef.vec, end, isect_mdef.start); - isect_mdef.vec_length = normalize_v3_v3(vec_normal, isect_mdef.vec); - - hit.index = -1; - hit.dist = BVH_RAYCAST_DIST_MAX; - if (BLI_bvhtree_ray_cast_ex(mdb->bvhtree, isect_mdef.start, vec_normal, - 0.0, &hit, harmonic_ray_callback, &data, BVH_RAYCAST_WATERTIGHT) != -1) - { - const MLoop *mloop = mdb->cagemesh_cache.mloop; - const MLoopTri *lt = &mdb->cagemesh_cache.looptri[hit.index]; - const MPoly *mp = &mdb->cagemesh_cache.mpoly[lt->poly]; - const float (*cagecos)[3] = mdb->cagecos; - const float len = isect_mdef.lambda; - MDefBoundIsect *isect; - - float (*mp_cagecos)[3] = BLI_array_alloca(mp_cagecos, mp->totloop); - int i; - - /* create MDefBoundIsect, and extra for 'poly_weights[]' */ - isect = BLI_memarena_alloc(mdb->memarena, sizeof(*isect) + (sizeof(float) * mp->totloop)); - - /* compute intersection coordinate */ - madd_v3_v3v3fl(isect->co, co1, isect_mdef.vec, len); - - isect->facing = isect_mdef.isect; - - isect->poly_index = lt->poly; - - isect->len = max_ff(len_v3v3(co1, isect->co), MESHDEFORM_LEN_THRESHOLD); - - /* compute mean value coordinates for interpolation */ - for (i = 0; i < mp->totloop; i++) { - copy_v3_v3(mp_cagecos[i], cagecos[mloop[mp->loopstart + i].v]); - } - - interp_weights_poly_v3(isect->poly_weights, mp_cagecos, mp->totloop, isect->co); - - return isect; - } - - return NULL; + BVHTreeRayHit hit; + MeshDeformIsect isect_mdef; + struct MeshRayCallbackData data = { + mdb, + &isect_mdef, + }; + float end[3], vec_normal[3]; + + /* happens binding when a cage has no faces */ + if (UNLIKELY(mdb->bvhtree == NULL)) + return NULL; + + /* setup isec */ + memset(&isect_mdef, 0, sizeof(isect_mdef)); + isect_mdef.lambda = 1e10f; + + copy_v3_v3(isect_mdef.start, co1); + copy_v3_v3(end, co2); + sub_v3_v3v3(isect_mdef.vec, end, isect_mdef.start); + isect_mdef.vec_length = normalize_v3_v3(vec_normal, isect_mdef.vec); + + hit.index = -1; + hit.dist = BVH_RAYCAST_DIST_MAX; + if (BLI_bvhtree_ray_cast_ex(mdb->bvhtree, + isect_mdef.start, + vec_normal, + 0.0, + &hit, + harmonic_ray_callback, + &data, + BVH_RAYCAST_WATERTIGHT) != -1) { + const MLoop *mloop = mdb->cagemesh_cache.mloop; + const MLoopTri *lt = &mdb->cagemesh_cache.looptri[hit.index]; + const MPoly *mp = &mdb->cagemesh_cache.mpoly[lt->poly]; + const float(*cagecos)[3] = mdb->cagecos; + const float len = isect_mdef.lambda; + MDefBoundIsect *isect; + + float(*mp_cagecos)[3] = BLI_array_alloca(mp_cagecos, mp->totloop); + int i; + + /* create MDefBoundIsect, and extra for 'poly_weights[]' */ + isect = BLI_memarena_alloc(mdb->memarena, sizeof(*isect) + (sizeof(float) * mp->totloop)); + + /* compute intersection coordinate */ + madd_v3_v3v3fl(isect->co, co1, isect_mdef.vec, len); + + isect->facing = isect_mdef.isect; + + isect->poly_index = lt->poly; + + isect->len = max_ff(len_v3v3(co1, isect->co), MESHDEFORM_LEN_THRESHOLD); + + /* compute mean value coordinates for interpolation */ + for (i = 0; i < mp->totloop; i++) { + copy_v3_v3(mp_cagecos[i], cagecos[mloop[mp->loopstart + i].v]); + } + + interp_weights_poly_v3(isect->poly_weights, mp_cagecos, mp->totloop, isect->co); + + return isect; + } + + return NULL; } static int meshdeform_inside_cage(MeshDeformBind *mdb, float *co) { - MDefBoundIsect *isect; - float outside[3], start[3], dir[3]; - int i; + MDefBoundIsect *isect; + float outside[3], start[3], dir[3]; + int i; - for (i = 1; i <= 6; i++) { - outside[0] = co[0] + (mdb->max[0] - mdb->min[0] + 1.0f) * MESHDEFORM_OFFSET[i][0]; - outside[1] = co[1] + (mdb->max[1] - mdb->min[1] + 1.0f) * MESHDEFORM_OFFSET[i][1]; - outside[2] = co[2] + (mdb->max[2] - mdb->min[2] + 1.0f) * MESHDEFORM_OFFSET[i][2]; + for (i = 1; i <= 6; i++) { + outside[0] = co[0] + (mdb->max[0] - mdb->min[0] + 1.0f) * MESHDEFORM_OFFSET[i][0]; + outside[1] = co[1] + (mdb->max[1] - mdb->min[1] + 1.0f) * MESHDEFORM_OFFSET[i][1]; + outside[2] = co[2] + (mdb->max[2] - mdb->min[2] + 1.0f) * MESHDEFORM_OFFSET[i][2]; - copy_v3_v3(start, co); - sub_v3_v3v3(dir, outside, start); - normalize_v3(dir); + copy_v3_v3(start, co); + sub_v3_v3v3(dir, outside, start); + normalize_v3(dir); - isect = meshdeform_ray_tree_intersect(mdb, start, outside); - if (isect && !isect->facing) - return 1; - } + isect = meshdeform_ray_tree_intersect(mdb, start, outside); + if (isect && !isect->facing) + return 1; + } - return 0; + return 0; } /* solving */ BLI_INLINE int meshdeform_index(MeshDeformBind *mdb, int x, int y, int z, int n) { - int size = mdb->size; + int size = mdb->size; - x += MESHDEFORM_OFFSET[n][0]; - y += MESHDEFORM_OFFSET[n][1]; - z += MESHDEFORM_OFFSET[n][2]; + x += MESHDEFORM_OFFSET[n][0]; + y += MESHDEFORM_OFFSET[n][1]; + z += MESHDEFORM_OFFSET[n][2]; - if (x < 0 || x >= mdb->size) - return -1; - if (y < 0 || y >= mdb->size) - return -1; - if (z < 0 || z >= mdb->size) - return -1; + if (x < 0 || x >= mdb->size) + return -1; + if (y < 0 || y >= mdb->size) + return -1; + if (z < 0 || z >= mdb->size) + return -1; - return x + y * size + z * size * size; + return x + y * size + z * size * size; } -BLI_INLINE void meshdeform_cell_center(MeshDeformBind *mdb, int x, int y, int z, int n, float *center) +BLI_INLINE void meshdeform_cell_center( + MeshDeformBind *mdb, int x, int y, int z, int n, float *center) { - x += MESHDEFORM_OFFSET[n][0]; - y += MESHDEFORM_OFFSET[n][1]; - z += MESHDEFORM_OFFSET[n][2]; + x += MESHDEFORM_OFFSET[n][0]; + y += MESHDEFORM_OFFSET[n][1]; + z += MESHDEFORM_OFFSET[n][2]; - center[0] = mdb->min[0] + x * mdb->width[0] + mdb->halfwidth[0]; - center[1] = mdb->min[1] + y * mdb->width[1] + mdb->halfwidth[1]; - center[2] = mdb->min[2] + z * mdb->width[2] + mdb->halfwidth[2]; + center[0] = mdb->min[0] + x * mdb->width[0] + mdb->halfwidth[0]; + center[1] = mdb->min[1] + y * mdb->width[1] + mdb->halfwidth[1]; + center[2] = mdb->min[2] + z * mdb->width[2] + mdb->halfwidth[2]; } static void meshdeform_add_intersections(MeshDeformBind *mdb, int x, int y, int z) { - MDefBoundIsect *isect; - float center[3], ncenter[3]; - int i, a; - - a = meshdeform_index(mdb, x, y, z, 0); - meshdeform_cell_center(mdb, x, y, z, 0, center); - - /* check each outgoing edge for intersection */ - for (i = 1; i <= 6; i++) { - if (meshdeform_index(mdb, x, y, z, i) == -1) - continue; - - meshdeform_cell_center(mdb, x, y, z, i, ncenter); - - isect = meshdeform_ray_tree_intersect(mdb, center, ncenter); - if (isect) { - mdb->boundisect[a][i - 1] = isect; - mdb->tag[a] = MESHDEFORM_TAG_BOUNDARY; - } - } + MDefBoundIsect *isect; + float center[3], ncenter[3]; + int i, a; + + a = meshdeform_index(mdb, x, y, z, 0); + meshdeform_cell_center(mdb, x, y, z, 0, center); + + /* check each outgoing edge for intersection */ + for (i = 1; i <= 6; i++) { + if (meshdeform_index(mdb, x, y, z, i) == -1) + continue; + + meshdeform_cell_center(mdb, x, y, z, i, ncenter); + + isect = meshdeform_ray_tree_intersect(mdb, center, ncenter); + if (isect) { + mdb->boundisect[a][i - 1] = isect; + mdb->tag[a] = MESHDEFORM_TAG_BOUNDARY; + } + } } static void meshdeform_bind_floodfill(MeshDeformBind *mdb) { - int *stack, *tag = mdb->tag; - int a, b, i, xyz[3], stacksize, size = mdb->size; - - stack = MEM_callocN(sizeof(int) * mdb->size3, "MeshDeformBindStack"); - - /* we know lower left corner is EXTERIOR because of padding */ - tag[0] = MESHDEFORM_TAG_EXTERIOR; - stack[0] = 0; - stacksize = 1; - - /* floodfill exterior tag */ - while (stacksize > 0) { - a = stack[--stacksize]; - - xyz[2] = a / (size * size); - xyz[1] = (a - xyz[2] * size * size) / size; - xyz[0] = a - xyz[1] * size - xyz[2] * size * size; - - for (i = 1; i <= 6; i++) { - b = meshdeform_index(mdb, xyz[0], xyz[1], xyz[2], i); - - if (b != -1) { - if (tag[b] == MESHDEFORM_TAG_UNTYPED || - (tag[b] == MESHDEFORM_TAG_BOUNDARY && !mdb->boundisect[a][i - 1])) - { - tag[b] = MESHDEFORM_TAG_EXTERIOR; - stack[stacksize++] = b; - } - } - } - } - - /* other cells are interior */ - for (a = 0; a < size * size * size; a++) - if (tag[a] == MESHDEFORM_TAG_UNTYPED) - tag[a] = MESHDEFORM_TAG_INTERIOR; + int *stack, *tag = mdb->tag; + int a, b, i, xyz[3], stacksize, size = mdb->size; + + stack = MEM_callocN(sizeof(int) * mdb->size3, "MeshDeformBindStack"); + + /* we know lower left corner is EXTERIOR because of padding */ + tag[0] = MESHDEFORM_TAG_EXTERIOR; + stack[0] = 0; + stacksize = 1; + + /* floodfill exterior tag */ + while (stacksize > 0) { + a = stack[--stacksize]; + + xyz[2] = a / (size * size); + xyz[1] = (a - xyz[2] * size * size) / size; + xyz[0] = a - xyz[1] * size - xyz[2] * size * size; + + for (i = 1; i <= 6; i++) { + b = meshdeform_index(mdb, xyz[0], xyz[1], xyz[2], i); + + if (b != -1) { + if (tag[b] == MESHDEFORM_TAG_UNTYPED || + (tag[b] == MESHDEFORM_TAG_BOUNDARY && !mdb->boundisect[a][i - 1])) { + tag[b] = MESHDEFORM_TAG_EXTERIOR; + stack[stacksize++] = b; + } + } + } + } + + /* other cells are interior */ + for (a = 0; a < size * size * size; a++) + if (tag[a] == MESHDEFORM_TAG_UNTYPED) + tag[a] = MESHDEFORM_TAG_INTERIOR; #if 0 - { - int tb, ti, te, ts; - tb = ti = te = ts = 0; - for (a = 0; a < size * size * size; a++) - if (tag[a] == MESHDEFORM_TAG_BOUNDARY) - tb++; - else if (tag[a] == MESHDEFORM_TAG_INTERIOR) - ti++; - else if (tag[a] == MESHDEFORM_TAG_EXTERIOR) { - te++; - - if (mdb->semibound[a]) - ts++; - } - - printf("interior %d exterior %d boundary %d semi-boundary %d\n", ti, te, tb, ts); - } + { + int tb, ti, te, ts; + tb = ti = te = ts = 0; + for (a = 0; a < size * size * size; a++) + if (tag[a] == MESHDEFORM_TAG_BOUNDARY) + tb++; + else if (tag[a] == MESHDEFORM_TAG_INTERIOR) + ti++; + else if (tag[a] == MESHDEFORM_TAG_EXTERIOR) { + te++; + + if (mdb->semibound[a]) + ts++; + } + + printf("interior %d exterior %d boundary %d semi-boundary %d\n", ti, te, tb, ts); + } #endif - MEM_freeN(stack); + MEM_freeN(stack); } -static float meshdeform_boundary_phi(const MeshDeformBind *mdb, const MDefBoundIsect *isect, int cagevert) +static float meshdeform_boundary_phi(const MeshDeformBind *mdb, + const MDefBoundIsect *isect, + int cagevert) { - const MLoop *mloop = mdb->cagemesh_cache.mloop; - const MPoly *mp = &mdb->cagemesh_cache.mpoly[isect->poly_index]; - int i; + const MLoop *mloop = mdb->cagemesh_cache.mloop; + const MPoly *mp = &mdb->cagemesh_cache.mpoly[isect->poly_index]; + int i; - for (i = 0; i < mp->totloop; i++) { - if (mloop[mp->loopstart + i].v == cagevert) { - return isect->poly_weights[i]; - } - } + for (i = 0; i < mp->totloop; i++) { + if (mloop[mp->loopstart + i].v == cagevert) { + return isect->poly_weights[i]; + } + } - return 0.0f; + return 0.0f; } -static float meshdeform_interp_w(MeshDeformBind *mdb, float *gridvec, float *UNUSED(vec), int UNUSED(cagevert)) +static float meshdeform_interp_w(MeshDeformBind *mdb, + float *gridvec, + float *UNUSED(vec), + int UNUSED(cagevert)) { - float dvec[3], ivec[3], wx, wy, wz, result = 0.0f; - float weight, totweight = 0.0f; - int i, a, x, y, z; - - for (i = 0; i < 3; i++) { - ivec[i] = (int)gridvec[i]; - dvec[i] = gridvec[i] - ivec[i]; - } - - for (i = 0; i < 8; i++) { - if (i & 1) { x = ivec[0] + 1; wx = dvec[0]; } - else { x = ivec[0]; wx = 1.0f - dvec[0]; } - - if (i & 2) { y = ivec[1] + 1; wy = dvec[1]; } - else { y = ivec[1]; wy = 1.0f - dvec[1]; } - - if (i & 4) { z = ivec[2] + 1; wz = dvec[2]; } - else { z = ivec[2]; wz = 1.0f - dvec[2]; } - - CLAMP(x, 0, mdb->size - 1); - CLAMP(y, 0, mdb->size - 1); - CLAMP(z, 0, mdb->size - 1); - - a = meshdeform_index(mdb, x, y, z, 0); - weight = wx * wy * wz; - result += weight * mdb->phi[a]; - totweight += weight; - } - - if (totweight > 0.0f) - result /= totweight; - - return result; + float dvec[3], ivec[3], wx, wy, wz, result = 0.0f; + float weight, totweight = 0.0f; + int i, a, x, y, z; + + for (i = 0; i < 3; i++) { + ivec[i] = (int)gridvec[i]; + dvec[i] = gridvec[i] - ivec[i]; + } + + for (i = 0; i < 8; i++) { + if (i & 1) { + x = ivec[0] + 1; + wx = dvec[0]; + } + else { + x = ivec[0]; + wx = 1.0f - dvec[0]; + } + + if (i & 2) { + y = ivec[1] + 1; + wy = dvec[1]; + } + else { + y = ivec[1]; + wy = 1.0f - dvec[1]; + } + + if (i & 4) { + z = ivec[2] + 1; + wz = dvec[2]; + } + else { + z = ivec[2]; + wz = 1.0f - dvec[2]; + } + + CLAMP(x, 0, mdb->size - 1); + CLAMP(y, 0, mdb->size - 1); + CLAMP(z, 0, mdb->size - 1); + + a = meshdeform_index(mdb, x, y, z, 0); + weight = wx * wy * wz; + result += weight * mdb->phi[a]; + totweight += weight; + } + + if (totweight > 0.0f) + result /= totweight; + + return result; } static void meshdeform_check_semibound(MeshDeformBind *mdb, int x, int y, int z) { - int i, a; + int i, a; - a = meshdeform_index(mdb, x, y, z, 0); - if (mdb->tag[a] != MESHDEFORM_TAG_EXTERIOR) - return; + a = meshdeform_index(mdb, x, y, z, 0); + if (mdb->tag[a] != MESHDEFORM_TAG_EXTERIOR) + return; - for (i = 1; i <= 6; i++) - if (mdb->boundisect[a][i - 1]) - mdb->semibound[a] = 1; + for (i = 1; i <= 6; i++) + if (mdb->boundisect[a][i - 1]) + mdb->semibound[a] = 1; } static float meshdeform_boundary_total_weight(MeshDeformBind *mdb, int x, int y, int z) { - float weight, totweight = 0.0f; - int i, a; + float weight, totweight = 0.0f; + int i, a; - a = meshdeform_index(mdb, x, y, z, 0); + a = meshdeform_index(mdb, x, y, z, 0); - /* count weight for neighbor cells */ - for (i = 1; i <= 6; i++) { - if (meshdeform_index(mdb, x, y, z, i) == -1) - continue; + /* count weight for neighbor cells */ + for (i = 1; i <= 6; i++) { + if (meshdeform_index(mdb, x, y, z, i) == -1) + continue; - if (mdb->boundisect[a][i - 1]) - weight = 1.0f / mdb->boundisect[a][i - 1]->len; - else if (!mdb->semibound[a]) - weight = 1.0f / mdb->width[0]; - else - weight = 0.0f; + if (mdb->boundisect[a][i - 1]) + weight = 1.0f / mdb->boundisect[a][i - 1]->len; + else if (!mdb->semibound[a]) + weight = 1.0f / mdb->width[0]; + else + weight = 0.0f; - totweight += weight; - } + totweight += weight; + } - return totweight; + return totweight; } -static void meshdeform_matrix_add_cell(MeshDeformBind *mdb, LinearSolver *context, int x, int y, int z) +static void meshdeform_matrix_add_cell( + MeshDeformBind *mdb, LinearSolver *context, int x, int y, int z) { - MDefBoundIsect *isect; - float weight, totweight; - int i, a, acenter; - - acenter = meshdeform_index(mdb, x, y, z, 0); - if (mdb->tag[acenter] == MESHDEFORM_TAG_EXTERIOR) - return; - - EIG_linear_solver_matrix_add(context, mdb->varidx[acenter], mdb->varidx[acenter], 1.0f); - - totweight = meshdeform_boundary_total_weight(mdb, x, y, z); - for (i = 1; i <= 6; i++) { - a = meshdeform_index(mdb, x, y, z, i); - if (a == -1 || mdb->tag[a] == MESHDEFORM_TAG_EXTERIOR) - continue; - - isect = mdb->boundisect[acenter][i - 1]; - if (!isect) { - weight = (1.0f / mdb->width[0]) / totweight; - EIG_linear_solver_matrix_add(context, mdb->varidx[acenter], mdb->varidx[a], -weight); - } - } + MDefBoundIsect *isect; + float weight, totweight; + int i, a, acenter; + + acenter = meshdeform_index(mdb, x, y, z, 0); + if (mdb->tag[acenter] == MESHDEFORM_TAG_EXTERIOR) + return; + + EIG_linear_solver_matrix_add(context, mdb->varidx[acenter], mdb->varidx[acenter], 1.0f); + + totweight = meshdeform_boundary_total_weight(mdb, x, y, z); + for (i = 1; i <= 6; i++) { + a = meshdeform_index(mdb, x, y, z, i); + if (a == -1 || mdb->tag[a] == MESHDEFORM_TAG_EXTERIOR) + continue; + + isect = mdb->boundisect[acenter][i - 1]; + if (!isect) { + weight = (1.0f / mdb->width[0]) / totweight; + EIG_linear_solver_matrix_add(context, mdb->varidx[acenter], mdb->varidx[a], -weight); + } + } } -static void meshdeform_matrix_add_rhs(MeshDeformBind *mdb, LinearSolver *context, int x, int y, int z, int cagevert) +static void meshdeform_matrix_add_rhs( + MeshDeformBind *mdb, LinearSolver *context, int x, int y, int z, int cagevert) { - MDefBoundIsect *isect; - float rhs, weight, totweight; - int i, a, acenter; - - acenter = meshdeform_index(mdb, x, y, z, 0); - if (mdb->tag[acenter] == MESHDEFORM_TAG_EXTERIOR) - return; - - totweight = meshdeform_boundary_total_weight(mdb, x, y, z); - for (i = 1; i <= 6; i++) { - a = meshdeform_index(mdb, x, y, z, i); - if (a == -1) - continue; - - isect = mdb->boundisect[acenter][i - 1]; - - if (isect) { - weight = (1.0f / isect->len) / totweight; - rhs = weight * meshdeform_boundary_phi(mdb, isect, cagevert); - EIG_linear_solver_right_hand_side_add(context, 0, mdb->varidx[acenter], rhs); - } - } + MDefBoundIsect *isect; + float rhs, weight, totweight; + int i, a, acenter; + + acenter = meshdeform_index(mdb, x, y, z, 0); + if (mdb->tag[acenter] == MESHDEFORM_TAG_EXTERIOR) + return; + + totweight = meshdeform_boundary_total_weight(mdb, x, y, z); + for (i = 1; i <= 6; i++) { + a = meshdeform_index(mdb, x, y, z, i); + if (a == -1) + continue; + + isect = mdb->boundisect[acenter][i - 1]; + + if (isect) { + weight = (1.0f / isect->len) / totweight; + rhs = weight * meshdeform_boundary_phi(mdb, isect, cagevert); + EIG_linear_solver_right_hand_side_add(context, 0, mdb->varidx[acenter], rhs); + } + } } -static void meshdeform_matrix_add_semibound_phi(MeshDeformBind *mdb, int x, int y, int z, int cagevert) +static void meshdeform_matrix_add_semibound_phi( + MeshDeformBind *mdb, int x, int y, int z, int cagevert) { - MDefBoundIsect *isect; - float rhs, weight, totweight; - int i, a; - - a = meshdeform_index(mdb, x, y, z, 0); - if (!mdb->semibound[a]) - return; - - mdb->phi[a] = 0.0f; - - totweight = meshdeform_boundary_total_weight(mdb, x, y, z); - for (i = 1; i <= 6; i++) { - isect = mdb->boundisect[a][i - 1]; - - if (isect) { - weight = (1.0f / isect->len) / totweight; - rhs = weight * meshdeform_boundary_phi(mdb, isect, cagevert); - mdb->phi[a] += rhs; - } - } + MDefBoundIsect *isect; + float rhs, weight, totweight; + int i, a; + + a = meshdeform_index(mdb, x, y, z, 0); + if (!mdb->semibound[a]) + return; + + mdb->phi[a] = 0.0f; + + totweight = meshdeform_boundary_total_weight(mdb, x, y, z); + for (i = 1; i <= 6; i++) { + isect = mdb->boundisect[a][i - 1]; + + if (isect) { + weight = (1.0f / isect->len) / totweight; + rhs = weight * meshdeform_boundary_phi(mdb, isect, cagevert); + mdb->phi[a] += rhs; + } + } } -static void meshdeform_matrix_add_exterior_phi(MeshDeformBind *mdb, int x, int y, int z, int UNUSED(cagevert)) +static void meshdeform_matrix_add_exterior_phi( + MeshDeformBind *mdb, int x, int y, int z, int UNUSED(cagevert)) { - float phi, totweight; - int i, a, acenter; - - acenter = meshdeform_index(mdb, x, y, z, 0); - if (mdb->tag[acenter] != MESHDEFORM_TAG_EXTERIOR || mdb->semibound[acenter]) - return; - - phi = 0.0f; - totweight = 0.0f; - for (i = 1; i <= 6; i++) { - a = meshdeform_index(mdb, x, y, z, i); - - if (a != -1 && mdb->semibound[a]) { - phi += mdb->phi[a]; - totweight += 1.0f; - } - } - - if (totweight != 0.0f) - mdb->phi[acenter] = phi / totweight; + float phi, totweight; + int i, a, acenter; + + acenter = meshdeform_index(mdb, x, y, z, 0); + if (mdb->tag[acenter] != MESHDEFORM_TAG_EXTERIOR || mdb->semibound[acenter]) + return; + + phi = 0.0f; + totweight = 0.0f; + for (i = 1; i <= 6; i++) { + a = meshdeform_index(mdb, x, y, z, i); + + if (a != -1 && mdb->semibound[a]) { + phi += mdb->phi[a]; + totweight += 1.0f; + } + } + + if (totweight != 0.0f) + mdb->phi[acenter] = phi / totweight; } static void meshdeform_matrix_solve(MeshDeformModifierData *mmd, MeshDeformBind *mdb) { - LinearSolver *context; - float vec[3], gridvec[3]; - int a, b, x, y, z, totvar; - char message[256]; - - /* setup variable indices */ - mdb->varidx = MEM_callocN(sizeof(int) * mdb->size3, "MeshDeformDSvaridx"); - for (a = 0, totvar = 0; a < mdb->size3; a++) - mdb->varidx[a] = (mdb->tag[a] == MESHDEFORM_TAG_EXTERIOR) ? -1 : totvar++; - - if (totvar == 0) { - MEM_freeN(mdb->varidx); - return; - } - - progress_bar(0, "Starting mesh deform solve"); - - /* setup linear solver */ - context = EIG_linear_solver_new(totvar, totvar, 1); - - /* build matrix */ - for (z = 0; z < mdb->size; z++) - for (y = 0; y < mdb->size; y++) - for (x = 0; x < mdb->size; x++) - meshdeform_matrix_add_cell(mdb, context, x, y, z); - - /* solve for each cage vert */ - for (a = 0; a < mdb->totcagevert; a++) { - /* fill in right hand side and solve */ - for (z = 0; z < mdb->size; z++) - for (y = 0; y < mdb->size; y++) - for (x = 0; x < mdb->size; x++) - meshdeform_matrix_add_rhs(mdb, context, x, y, z, a); - - if (EIG_linear_solver_solve(context)) { - for (z = 0; z < mdb->size; z++) - for (y = 0; y < mdb->size; y++) - for (x = 0; x < mdb->size; x++) - meshdeform_matrix_add_semibound_phi(mdb, x, y, z, a); - - for (z = 0; z < mdb->size; z++) - for (y = 0; y < mdb->size; y++) - for (x = 0; x < mdb->size; x++) - meshdeform_matrix_add_exterior_phi(mdb, x, y, z, a); - - for (b = 0; b < mdb->size3; b++) { - if (mdb->tag[b] != MESHDEFORM_TAG_EXTERIOR) - mdb->phi[b] = EIG_linear_solver_variable_get(context, 0, mdb->varidx[b]); - mdb->totalphi[b] += mdb->phi[b]; - } - - if (mdb->weights) { - /* static bind : compute weights for each vertex */ - for (b = 0; b < mdb->totvert; b++) { - if (mdb->inside[b]) { - copy_v3_v3(vec, mdb->vertexcos[b]); - gridvec[0] = (vec[0] - mdb->min[0] - mdb->halfwidth[0]) / mdb->width[0]; - gridvec[1] = (vec[1] - mdb->min[1] - mdb->halfwidth[1]) / mdb->width[1]; - gridvec[2] = (vec[2] - mdb->min[2] - mdb->halfwidth[2]) / mdb->width[2]; - - mdb->weights[b * mdb->totcagevert + a] = meshdeform_interp_w(mdb, gridvec, vec, a); - } - } - } - else { - MDefBindInfluence *inf; - - /* dynamic bind */ - for (b = 0; b < mdb->size3; b++) { - if (mdb->phi[b] >= MESHDEFORM_MIN_INFLUENCE) { - inf = BLI_memarena_alloc(mdb->memarena, sizeof(*inf)); - inf->vertex = a; - inf->weight = mdb->phi[b]; - inf->next = mdb->dyngrid[b]; - mdb->dyngrid[b] = inf; - } - } - } - } - else { - modifier_setError(&mmd->modifier, "Failed to find bind solution (increase precision?)"); - error("Mesh Deform: failed to find bind solution."); - break; - } - - BLI_snprintf(message, sizeof(message), "Mesh deform solve %d / %d |||", a + 1, mdb->totcagevert); - progress_bar((float)(a + 1) / (float)(mdb->totcagevert), message); - } + LinearSolver *context; + float vec[3], gridvec[3]; + int a, b, x, y, z, totvar; + char message[256]; + + /* setup variable indices */ + mdb->varidx = MEM_callocN(sizeof(int) * mdb->size3, "MeshDeformDSvaridx"); + for (a = 0, totvar = 0; a < mdb->size3; a++) + mdb->varidx[a] = (mdb->tag[a] == MESHDEFORM_TAG_EXTERIOR) ? -1 : totvar++; + + if (totvar == 0) { + MEM_freeN(mdb->varidx); + return; + } + + progress_bar(0, "Starting mesh deform solve"); + + /* setup linear solver */ + context = EIG_linear_solver_new(totvar, totvar, 1); + + /* build matrix */ + for (z = 0; z < mdb->size; z++) + for (y = 0; y < mdb->size; y++) + for (x = 0; x < mdb->size; x++) + meshdeform_matrix_add_cell(mdb, context, x, y, z); + + /* solve for each cage vert */ + for (a = 0; a < mdb->totcagevert; a++) { + /* fill in right hand side and solve */ + for (z = 0; z < mdb->size; z++) + for (y = 0; y < mdb->size; y++) + for (x = 0; x < mdb->size; x++) + meshdeform_matrix_add_rhs(mdb, context, x, y, z, a); + + if (EIG_linear_solver_solve(context)) { + for (z = 0; z < mdb->size; z++) + for (y = 0; y < mdb->size; y++) + for (x = 0; x < mdb->size; x++) + meshdeform_matrix_add_semibound_phi(mdb, x, y, z, a); + + for (z = 0; z < mdb->size; z++) + for (y = 0; y < mdb->size; y++) + for (x = 0; x < mdb->size; x++) + meshdeform_matrix_add_exterior_phi(mdb, x, y, z, a); + + for (b = 0; b < mdb->size3; b++) { + if (mdb->tag[b] != MESHDEFORM_TAG_EXTERIOR) + mdb->phi[b] = EIG_linear_solver_variable_get(context, 0, mdb->varidx[b]); + mdb->totalphi[b] += mdb->phi[b]; + } + + if (mdb->weights) { + /* static bind : compute weights for each vertex */ + for (b = 0; b < mdb->totvert; b++) { + if (mdb->inside[b]) { + copy_v3_v3(vec, mdb->vertexcos[b]); + gridvec[0] = (vec[0] - mdb->min[0] - mdb->halfwidth[0]) / mdb->width[0]; + gridvec[1] = (vec[1] - mdb->min[1] - mdb->halfwidth[1]) / mdb->width[1]; + gridvec[2] = (vec[2] - mdb->min[2] - mdb->halfwidth[2]) / mdb->width[2]; + + mdb->weights[b * mdb->totcagevert + a] = meshdeform_interp_w(mdb, gridvec, vec, a); + } + } + } + else { + MDefBindInfluence *inf; + + /* dynamic bind */ + for (b = 0; b < mdb->size3; b++) { + if (mdb->phi[b] >= MESHDEFORM_MIN_INFLUENCE) { + inf = BLI_memarena_alloc(mdb->memarena, sizeof(*inf)); + inf->vertex = a; + inf->weight = mdb->phi[b]; + inf->next = mdb->dyngrid[b]; + mdb->dyngrid[b] = inf; + } + } + } + } + else { + modifier_setError(&mmd->modifier, "Failed to find bind solution (increase precision?)"); + error("Mesh Deform: failed to find bind solution."); + break; + } + + BLI_snprintf( + message, sizeof(message), "Mesh deform solve %d / %d |||", a + 1, mdb->totcagevert); + progress_bar((float)(a + 1) / (float)(mdb->totcagevert), message); + } #if 0 - /* sanity check */ - for (b = 0; b < mdb->size3; b++) - if (mdb->tag[b] != MESHDEFORM_TAG_EXTERIOR) - if (fabsf(mdb->totalphi[b] - 1.0f) > 1e-4f) - printf("totalphi deficiency [%s|%d] %d: %.10f\n", - (mdb->tag[b] == MESHDEFORM_TAG_INTERIOR) ? "interior" : "boundary", mdb->semibound[b], mdb->varidx[b], mdb->totalphi[b]); + /* sanity check */ + for (b = 0; b < mdb->size3; b++) + if (mdb->tag[b] != MESHDEFORM_TAG_EXTERIOR) + if (fabsf(mdb->totalphi[b] - 1.0f) > 1e-4f) + printf("totalphi deficiency [%s|%d] %d: %.10f\n", + (mdb->tag[b] == MESHDEFORM_TAG_INTERIOR) ? "interior" : "boundary", mdb->semibound[b], mdb->varidx[b], mdb->totalphi[b]); #endif - /* free */ - MEM_freeN(mdb->varidx); + /* free */ + MEM_freeN(mdb->varidx); - EIG_linear_solver_delete(context); + EIG_linear_solver_delete(context); } static void harmonic_coordinates_bind(MeshDeformModifierData *mmd, MeshDeformBind *mdb) { - MDefBindInfluence *inf; - MDefInfluence *mdinf; - MDefCell *cell; - float center[3], vec[3], maxwidth, totweight; - int a, b, x, y, z, totinside, offset; - - /* compute bounding box of the cage mesh */ - INIT_MINMAX(mdb->min, mdb->max); - - for (a = 0; a < mdb->totcagevert; a++) - minmax_v3v3_v3(mdb->min, mdb->max, mdb->cagecos[a]); - - /* allocate memory */ - mdb->size = (2 << (mmd->gridsize - 1)) + 2; - mdb->size3 = mdb->size * mdb->size * mdb->size; - mdb->tag = MEM_callocN(sizeof(int) * mdb->size3, "MeshDeformBindTag"); - mdb->phi = MEM_callocN(sizeof(float) * mdb->size3, "MeshDeformBindPhi"); - mdb->totalphi = MEM_callocN(sizeof(float) * mdb->size3, "MeshDeformBindTotalPhi"); - mdb->boundisect = MEM_callocN(sizeof(*mdb->boundisect) * mdb->size3, "MDefBoundIsect"); - mdb->semibound = MEM_callocN(sizeof(int) * mdb->size3, "MDefSemiBound"); - mdb->bvhtree = BKE_bvhtree_from_mesh_get(&mdb->bvhdata, mdb->cagemesh, BVHTREE_FROM_LOOPTRI, 4); - mdb->inside = MEM_callocN(sizeof(int) * mdb->totvert, "MDefInside"); - - if (mmd->flag & MOD_MDEF_DYNAMIC_BIND) - mdb->dyngrid = MEM_callocN(sizeof(MDefBindInfluence *) * mdb->size3, "MDefDynGrid"); - else - mdb->weights = MEM_callocN(sizeof(float) * mdb->totvert * mdb->totcagevert, "MDefWeights"); - - mdb->memarena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, "harmonic coords arena"); - BLI_memarena_use_calloc(mdb->memarena); - - /* initialize data from 'cagedm' for reuse */ - { - Mesh *me = mdb->cagemesh; - mdb->cagemesh_cache.mpoly = me->mpoly; - mdb->cagemesh_cache.mloop = me->mloop; - mdb->cagemesh_cache.looptri = BKE_mesh_runtime_looptri_ensure(me); - /* can be NULL */ - mdb->cagemesh_cache.poly_nors = CustomData_get_layer(&me->pdata, CD_NORMAL); - } - - /* make bounding box equal size in all directions, add padding, and compute - * width of the cells */ - maxwidth = -1.0f; - for (a = 0; a < 3; a++) - if (mdb->max[a] - mdb->min[a] > maxwidth) - maxwidth = mdb->max[a] - mdb->min[a]; - - for (a = 0; a < 3; a++) { - center[a] = (mdb->min[a] + mdb->max[a]) * 0.5f; - mdb->min[a] = center[a] - maxwidth * 0.5f; - mdb->max[a] = center[a] + maxwidth * 0.5f; - - mdb->width[a] = (mdb->max[a] - mdb->min[a]) / (mdb->size - 4); - mdb->min[a] -= 2.1f * mdb->width[a]; - mdb->max[a] += 2.1f * mdb->width[a]; - - mdb->width[a] = (mdb->max[a] - mdb->min[a]) / mdb->size; - mdb->halfwidth[a] = mdb->width[a] * 0.5f; - } - - progress_bar(0, "Setting up mesh deform system"); - - totinside = 0; - for (a = 0; a < mdb->totvert; a++) { - copy_v3_v3(vec, mdb->vertexcos[a]); - mdb->inside[a] = meshdeform_inside_cage(mdb, vec); - if (mdb->inside[a]) - totinside++; - } - - /* free temporary MDefBoundIsects */ - BLI_memarena_free(mdb->memarena); - mdb->memarena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, "harmonic coords arena"); - - /* start with all cells untyped */ - for (a = 0; a < mdb->size3; a++) - mdb->tag[a] = MESHDEFORM_TAG_UNTYPED; - - /* detect intersections and tag boundary cells */ - for (z = 0; z < mdb->size; z++) - for (y = 0; y < mdb->size; y++) - for (x = 0; x < mdb->size; x++) - meshdeform_add_intersections(mdb, x, y, z); - - /* compute exterior and interior tags */ - meshdeform_bind_floodfill(mdb); - - for (z = 0; z < mdb->size; z++) - for (y = 0; y < mdb->size; y++) - for (x = 0; x < mdb->size; x++) - meshdeform_check_semibound(mdb, x, y, z); - - /* solve */ - meshdeform_matrix_solve(mmd, mdb); - - /* assign results */ - if (mmd->flag & MOD_MDEF_DYNAMIC_BIND) { - mmd->totinfluence = 0; - for (a = 0; a < mdb->size3; a++) - for (inf = mdb->dyngrid[a]; inf; inf = inf->next) - mmd->totinfluence++; - - /* convert MDefBindInfluences to smaller MDefInfluences */ - mmd->dyngrid = MEM_callocN(sizeof(MDefCell) * mdb->size3, "MDefDynGrid"); - mmd->dyninfluences = MEM_callocN(sizeof(MDefInfluence) * mmd->totinfluence, "MDefInfluence"); - offset = 0; - for (a = 0; a < mdb->size3; a++) { - cell = &mmd->dyngrid[a]; - cell->offset = offset; - - totweight = 0.0f; - mdinf = mmd->dyninfluences + cell->offset; - for (inf = mdb->dyngrid[a]; inf; inf = inf->next, mdinf++) { - mdinf->weight = inf->weight; - mdinf->vertex = inf->vertex; - totweight += mdinf->weight; - cell->totinfluence++; - } - - if (totweight > 0.0f) { - mdinf = mmd->dyninfluences + cell->offset; - for (b = 0; b < cell->totinfluence; b++, mdinf++) - mdinf->weight /= totweight; - } - - offset += cell->totinfluence; - } - - mmd->dynverts = mdb->inside; - mmd->dyngridsize = mdb->size; - copy_v3_v3(mmd->dyncellmin, mdb->min); - mmd->dyncellwidth = mdb->width[0]; - MEM_freeN(mdb->dyngrid); - } - else { - mmd->bindweights = mdb->weights; - MEM_freeN(mdb->inside); - } - - MEM_freeN(mdb->tag); - MEM_freeN(mdb->phi); - MEM_freeN(mdb->totalphi); - MEM_freeN(mdb->boundisect); - MEM_freeN(mdb->semibound); - BLI_memarena_free(mdb->memarena); - free_bvhtree_from_mesh(&mdb->bvhdata); + MDefBindInfluence *inf; + MDefInfluence *mdinf; + MDefCell *cell; + float center[3], vec[3], maxwidth, totweight; + int a, b, x, y, z, totinside, offset; + + /* compute bounding box of the cage mesh */ + INIT_MINMAX(mdb->min, mdb->max); + + for (a = 0; a < mdb->totcagevert; a++) + minmax_v3v3_v3(mdb->min, mdb->max, mdb->cagecos[a]); + + /* allocate memory */ + mdb->size = (2 << (mmd->gridsize - 1)) + 2; + mdb->size3 = mdb->size * mdb->size * mdb->size; + mdb->tag = MEM_callocN(sizeof(int) * mdb->size3, "MeshDeformBindTag"); + mdb->phi = MEM_callocN(sizeof(float) * mdb->size3, "MeshDeformBindPhi"); + mdb->totalphi = MEM_callocN(sizeof(float) * mdb->size3, "MeshDeformBindTotalPhi"); + mdb->boundisect = MEM_callocN(sizeof(*mdb->boundisect) * mdb->size3, "MDefBoundIsect"); + mdb->semibound = MEM_callocN(sizeof(int) * mdb->size3, "MDefSemiBound"); + mdb->bvhtree = BKE_bvhtree_from_mesh_get(&mdb->bvhdata, mdb->cagemesh, BVHTREE_FROM_LOOPTRI, 4); + mdb->inside = MEM_callocN(sizeof(int) * mdb->totvert, "MDefInside"); + + if (mmd->flag & MOD_MDEF_DYNAMIC_BIND) + mdb->dyngrid = MEM_callocN(sizeof(MDefBindInfluence *) * mdb->size3, "MDefDynGrid"); + else + mdb->weights = MEM_callocN(sizeof(float) * mdb->totvert * mdb->totcagevert, "MDefWeights"); + + mdb->memarena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, "harmonic coords arena"); + BLI_memarena_use_calloc(mdb->memarena); + + /* initialize data from 'cagedm' for reuse */ + { + Mesh *me = mdb->cagemesh; + mdb->cagemesh_cache.mpoly = me->mpoly; + mdb->cagemesh_cache.mloop = me->mloop; + mdb->cagemesh_cache.looptri = BKE_mesh_runtime_looptri_ensure(me); + /* can be NULL */ + mdb->cagemesh_cache.poly_nors = CustomData_get_layer(&me->pdata, CD_NORMAL); + } + + /* make bounding box equal size in all directions, add padding, and compute + * width of the cells */ + maxwidth = -1.0f; + for (a = 0; a < 3; a++) + if (mdb->max[a] - mdb->min[a] > maxwidth) + maxwidth = mdb->max[a] - mdb->min[a]; + + for (a = 0; a < 3; a++) { + center[a] = (mdb->min[a] + mdb->max[a]) * 0.5f; + mdb->min[a] = center[a] - maxwidth * 0.5f; + mdb->max[a] = center[a] + maxwidth * 0.5f; + + mdb->width[a] = (mdb->max[a] - mdb->min[a]) / (mdb->size - 4); + mdb->min[a] -= 2.1f * mdb->width[a]; + mdb->max[a] += 2.1f * mdb->width[a]; + + mdb->width[a] = (mdb->max[a] - mdb->min[a]) / mdb->size; + mdb->halfwidth[a] = mdb->width[a] * 0.5f; + } + + progress_bar(0, "Setting up mesh deform system"); + + totinside = 0; + for (a = 0; a < mdb->totvert; a++) { + copy_v3_v3(vec, mdb->vertexcos[a]); + mdb->inside[a] = meshdeform_inside_cage(mdb, vec); + if (mdb->inside[a]) + totinside++; + } + + /* free temporary MDefBoundIsects */ + BLI_memarena_free(mdb->memarena); + mdb->memarena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, "harmonic coords arena"); + + /* start with all cells untyped */ + for (a = 0; a < mdb->size3; a++) + mdb->tag[a] = MESHDEFORM_TAG_UNTYPED; + + /* detect intersections and tag boundary cells */ + for (z = 0; z < mdb->size; z++) + for (y = 0; y < mdb->size; y++) + for (x = 0; x < mdb->size; x++) + meshdeform_add_intersections(mdb, x, y, z); + + /* compute exterior and interior tags */ + meshdeform_bind_floodfill(mdb); + + for (z = 0; z < mdb->size; z++) + for (y = 0; y < mdb->size; y++) + for (x = 0; x < mdb->size; x++) + meshdeform_check_semibound(mdb, x, y, z); + + /* solve */ + meshdeform_matrix_solve(mmd, mdb); + + /* assign results */ + if (mmd->flag & MOD_MDEF_DYNAMIC_BIND) { + mmd->totinfluence = 0; + for (a = 0; a < mdb->size3; a++) + for (inf = mdb->dyngrid[a]; inf; inf = inf->next) + mmd->totinfluence++; + + /* convert MDefBindInfluences to smaller MDefInfluences */ + mmd->dyngrid = MEM_callocN(sizeof(MDefCell) * mdb->size3, "MDefDynGrid"); + mmd->dyninfluences = MEM_callocN(sizeof(MDefInfluence) * mmd->totinfluence, "MDefInfluence"); + offset = 0; + for (a = 0; a < mdb->size3; a++) { + cell = &mmd->dyngrid[a]; + cell->offset = offset; + + totweight = 0.0f; + mdinf = mmd->dyninfluences + cell->offset; + for (inf = mdb->dyngrid[a]; inf; inf = inf->next, mdinf++) { + mdinf->weight = inf->weight; + mdinf->vertex = inf->vertex; + totweight += mdinf->weight; + cell->totinfluence++; + } + + if (totweight > 0.0f) { + mdinf = mmd->dyninfluences + cell->offset; + for (b = 0; b < cell->totinfluence; b++, mdinf++) + mdinf->weight /= totweight; + } + + offset += cell->totinfluence; + } + + mmd->dynverts = mdb->inside; + mmd->dyngridsize = mdb->size; + copy_v3_v3(mmd->dyncellmin, mdb->min); + mmd->dyncellwidth = mdb->width[0]; + MEM_freeN(mdb->dyngrid); + } + else { + mmd->bindweights = mdb->weights; + MEM_freeN(mdb->inside); + } + + MEM_freeN(mdb->tag); + MEM_freeN(mdb->phi); + MEM_freeN(mdb->totalphi); + MEM_freeN(mdb->boundisect); + MEM_freeN(mdb->semibound); + BLI_memarena_free(mdb->memarena); + free_bvhtree_from_mesh(&mdb->bvhdata); } -void ED_mesh_deform_bind_callback( - MeshDeformModifierData *mmd, Mesh *cagemesh, - float *vertexcos, int totvert, float cagemat[4][4]) +void ED_mesh_deform_bind_callback(MeshDeformModifierData *mmd, + Mesh *cagemesh, + float *vertexcos, + int totvert, + float cagemat[4][4]) { - MeshDeformModifierData *mmd_orig = - (MeshDeformModifierData *)modifier_get_original(&mmd->modifier); - MeshDeformBind mdb; - MVert *mvert; - int a; + MeshDeformModifierData *mmd_orig = (MeshDeformModifierData *)modifier_get_original( + &mmd->modifier); + MeshDeformBind mdb; + MVert *mvert; + int a; - waitcursor(1); - start_progress_bar(); + waitcursor(1); + start_progress_bar(); - memset(&mdb, 0, sizeof(MeshDeformBind)); + memset(&mdb, 0, sizeof(MeshDeformBind)); - /* get mesh and cage mesh */ - mdb.vertexcos = MEM_callocN(sizeof(float) * 3 * totvert, "MeshDeformCos"); - mdb.totvert = totvert; + /* get mesh and cage mesh */ + mdb.vertexcos = MEM_callocN(sizeof(float) * 3 * totvert, "MeshDeformCos"); + mdb.totvert = totvert; - mdb.cagemesh = cagemesh; - mdb.totcagevert = mdb.cagemesh->totvert; - mdb.cagecos = MEM_callocN(sizeof(*mdb.cagecos) * mdb.totcagevert, "MeshDeformBindCos"); - copy_m4_m4(mdb.cagemat, cagemat); + mdb.cagemesh = cagemesh; + mdb.totcagevert = mdb.cagemesh->totvert; + mdb.cagecos = MEM_callocN(sizeof(*mdb.cagecos) * mdb.totcagevert, "MeshDeformBindCos"); + copy_m4_m4(mdb.cagemat, cagemat); - mvert = mdb.cagemesh->mvert; - for (a = 0; a < mdb.totcagevert; a++) - copy_v3_v3(mdb.cagecos[a], mvert[a].co); - for (a = 0; a < mdb.totvert; a++) - mul_v3_m4v3(mdb.vertexcos[a], mdb.cagemat, vertexcos + a * 3); + mvert = mdb.cagemesh->mvert; + for (a = 0; a < mdb.totcagevert; a++) + copy_v3_v3(mdb.cagecos[a], mvert[a].co); + for (a = 0; a < mdb.totvert; a++) + mul_v3_m4v3(mdb.vertexcos[a], mdb.cagemat, vertexcos + a * 3); - /* solve */ - harmonic_coordinates_bind(mmd_orig, &mdb); + /* solve */ + harmonic_coordinates_bind(mmd_orig, &mdb); - /* assign bind variables */ - mmd_orig->bindcagecos = (float *)mdb.cagecos; - mmd_orig->totvert = mdb.totvert; - mmd_orig->totcagevert = mdb.totcagevert; - copy_m4_m4(mmd_orig->bindmat, mmd_orig->object->obmat); + /* assign bind variables */ + mmd_orig->bindcagecos = (float *)mdb.cagecos; + mmd_orig->totvert = mdb.totvert; + mmd_orig->totcagevert = mdb.totcagevert; + copy_m4_m4(mmd_orig->bindmat, mmd_orig->object->obmat); - /* transform bindcagecos to world space */ - for (a = 0; a < mdb.totcagevert; a++) - mul_m4_v3(mmd_orig->object->obmat, mmd_orig->bindcagecos + a * 3); + /* transform bindcagecos to world space */ + for (a = 0; a < mdb.totcagevert; a++) + mul_m4_v3(mmd_orig->object->obmat, mmd_orig->bindcagecos + a * 3); - /* free */ - MEM_freeN(mdb.vertexcos); + /* free */ + MEM_freeN(mdb.vertexcos); - /* compact weights */ - modifier_mdef_compact_influences((ModifierData *)mmd_orig); + /* compact weights */ + modifier_mdef_compact_influences((ModifierData *)mmd_orig); - end_progress_bar(); - waitcursor(0); + end_progress_bar(); + waitcursor(0); } diff --git a/source/blender/editors/armature/meshlaplacian.h b/source/blender/editors/armature/meshlaplacian.h index cf0c1de6b67..ef4759eab4a 100644 --- a/source/blender/editors/armature/meshlaplacian.h +++ b/source/blender/editors/armature/meshlaplacian.h @@ -20,7 +20,6 @@ * \ingroup edarmature */ - #ifndef __MESHLAPLACIAN_H__ #define __MESHLAPLACIAN_H__ @@ -49,11 +48,16 @@ float laplacian_system_get_solution(LaplacianSystem *sys, int v); /* Heat Weighting */ -void heat_bone_weighting( - struct Object *ob, struct Mesh *me, float (*verts)[3], - int numbones, struct bDeformGroup **dgrouplist, - struct bDeformGroup **dgroupflip, float (*root)[3], float (*tip)[3], - int *selected, const char **error); +void heat_bone_weighting(struct Object *ob, + struct Mesh *me, + float (*verts)[3], + int numbones, + struct bDeformGroup **dgrouplist, + struct bDeformGroup **dgroupflip, + float (*root)[3], + float (*tip)[3], + int *selected, + const char **error); #ifdef RIGID_DEFORM /* As-Rigid-As-Possible Deformation */ diff --git a/source/blender/editors/armature/pose_edit.c b/source/blender/editors/armature/pose_edit.c index 9323e00db65..51641041ea1 100644 --- a/source/blender/editors/armature/pose_edit.c +++ b/source/blender/editors/armature/pose_edit.c @@ -65,7 +65,6 @@ #include "armature_intern.h" - #define DEBUG_TIME #include "PIL_time.h" @@ -73,81 +72,80 @@ # include "PIL_time_utildefines.h" #endif - /* matches logic with ED_operator_posemode_context() */ Object *ED_pose_object_from_context(bContext *C) { - ScrArea *sa = 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) { - ob = ED_object_context(C); - } - else { - ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); - } - - return ob; + ScrArea *sa = 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) { + ob = ED_object_context(C); + } + else { + ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); + } + + return ob; } /* This function is used to process the necessary updates for */ bool ED_object_posemode_enter_ex(struct Main *bmain, Object *ob) { - BLI_assert(!ID_IS_LINKED(ob)); - bool ok = false; - - switch (ob->type) { - case OB_ARMATURE: - ob->restore_mode = ob->mode; - ob->mode |= OB_MODE_POSE; - /* Inform all CoW versions that we changed the mode. */ - DEG_id_tag_update_ex(bmain, &ob->id, ID_RECALC_COPY_ON_WRITE); - ok = true; - - break; - default: - break; - } - - return ok; + BLI_assert(!ID_IS_LINKED(ob)); + bool ok = false; + + switch (ob->type) { + case OB_ARMATURE: + ob->restore_mode = ob->mode; + ob->mode |= OB_MODE_POSE; + /* Inform all CoW versions that we changed the mode. */ + DEG_id_tag_update_ex(bmain, &ob->id, ID_RECALC_COPY_ON_WRITE); + ok = true; + + break; + default: + break; + } + + return ok; } bool ED_object_posemode_enter(bContext *C, Object *ob) { - ReportList *reports = CTX_wm_reports(C); - if (ID_IS_LINKED(ob)) { - BKE_report(reports, RPT_WARNING, "Cannot pose libdata"); - return false; - } - struct Main *bmain = CTX_data_main(C); - bool ok = ED_object_posemode_enter_ex(bmain, ob); - if (ok) { - WM_event_add_notifier(C, NC_SCENE | ND_MODE | NS_MODE_POSE, NULL); - } - return ok; + ReportList *reports = CTX_wm_reports(C); + if (ID_IS_LINKED(ob)) { + BKE_report(reports, RPT_WARNING, "Cannot pose libdata"); + return false; + } + struct Main *bmain = CTX_data_main(C); + bool ok = ED_object_posemode_enter_ex(bmain, ob); + if (ok) { + WM_event_add_notifier(C, NC_SCENE | ND_MODE | NS_MODE_POSE, NULL); + } + return ok; } bool ED_object_posemode_exit_ex(struct Main *bmain, Object *ob) { - bool ok = false; - if (ob) { - ob->restore_mode = ob->mode; - ob->mode &= ~OB_MODE_POSE; - - /* Inform all CoW versions that we changed the mode. */ - DEG_id_tag_update_ex(bmain, &ob->id, ID_RECALC_COPY_ON_WRITE); - ok = true; - } - return ok; + bool ok = false; + if (ob) { + ob->restore_mode = ob->mode; + ob->mode &= ~OB_MODE_POSE; + + /* Inform all CoW versions that we changed the mode. */ + DEG_id_tag_update_ex(bmain, &ob->id, ID_RECALC_COPY_ON_WRITE); + ok = true; + } + return ok; } bool ED_object_posemode_exit(bContext *C, Object *ob) { - struct Main *bmain = CTX_data_main(C); - bool ok = ED_object_posemode_exit_ex(bmain, ob); - if (ok) { - WM_event_add_notifier(C, NC_SCENE | ND_MODE | NS_MODE_OBJECT, NULL); - } - return ok; + struct Main *bmain = CTX_data_main(C); + bool ok = ED_object_posemode_exit_ex(bmain, ob); + if (ok) { + WM_event_add_notifier(C, NC_SCENE | ND_MODE | NS_MODE_OBJECT, NULL); + } + return ok; } /* if a selected or active bone is protected, throw error (oonly if warn == 1) and return 1 */ @@ -155,25 +153,25 @@ bool ED_object_posemode_exit(bContext *C, Object *ob) #if 0 /* UNUSED 2.5 */ static bool pose_has_protected_selected(Object *ob, short warn) { - /* check protection */ - if (ob->proxy) { - bPoseChannel *pchan; - bArmature *arm = ob->data; - - for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { - if (pchan->bone && (pchan->bone->layer & arm->layer)) { - if (pchan->bone->layer & arm->layer_protected) { - if (pchan->bone->flag & BONE_SELECTED) - break; - } - } - } - if (pchan) { - if (warn) error("Cannot change Proxy protected bones"); - return 1; - } - } - return 0; + /* check protection */ + if (ob->proxy) { + bPoseChannel *pchan; + bArmature *arm = ob->data; + + for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { + if (pchan->bone && (pchan->bone->layer & arm->layer)) { + if (pchan->bone->layer & arm->layer_protected) { + if (pchan->bone->flag & BONE_SELECTED) + break; + } + } + } + if (pchan) { + if (warn) error("Cannot change Proxy protected bones"); + return 1; + } + } + return 0; } #endif @@ -187,101 +185,100 @@ static bool pose_has_protected_selected(Object *ob, short warn) */ void ED_pose_recalculate_paths(bContext *C, Scene *scene, Object *ob, bool current_frame_only) { - /* Transform doesn't always have context available to do update. */ - if (C == NULL) { - return; - } + /* Transform doesn't always have context available to do update. */ + if (C == NULL) { + return; + } - Main *bmain = CTX_data_main(C); - Depsgraph *depsgraph = CTX_data_depsgraph(C); - ListBase targets = {NULL, NULL}; - bool free_depsgraph = false; + Main *bmain = CTX_data_main(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + ListBase targets = {NULL, NULL}; + bool free_depsgraph = false; - /* Override depsgraph with a filtered, simpler copy */ - if (!current_frame_only && G.debug_value != -1) { - DEG_FilterQuery query = {{0}}; + /* Override depsgraph with a filtered, simpler copy */ + if (!current_frame_only && G.debug_value != -1) { + DEG_FilterQuery query = {{0}}; - DEG_FilterTarget *dft_ob = MEM_callocN(sizeof(DEG_FilterTarget), "DEG_FilterTarget"); - dft_ob->id = &ob->id; - BLI_addtail(&query.targets, dft_ob); + DEG_FilterTarget *dft_ob = MEM_callocN(sizeof(DEG_FilterTarget), "DEG_FilterTarget"); + dft_ob->id = &ob->id; + BLI_addtail(&query.targets, dft_ob); #ifdef DEBUG_TIME - TIMEIT_START(filter_pose_depsgraph); + TIMEIT_START(filter_pose_depsgraph); #endif - depsgraph = DEG_graph_filter(depsgraph, bmain, &query); + depsgraph = DEG_graph_filter(depsgraph, bmain, &query); #ifdef DEBUG_TIME - TIMEIT_END(filter_pose_depsgraph); + TIMEIT_END(filter_pose_depsgraph); #endif - free_depsgraph = true; - MEM_freeN(dft_ob); + free_depsgraph = true; + MEM_freeN(dft_ob); #ifdef DEBUG_TIME - TIMEIT_START(filter_pose_update); + TIMEIT_START(filter_pose_update); #endif - BKE_scene_graph_update_tagged(depsgraph, bmain); + BKE_scene_graph_update_tagged(depsgraph, bmain); #ifdef DEBUG_TIME - TIMEIT_END(filter_pose_update); + TIMEIT_END(filter_pose_update); #endif - } + } - /* set flag to force recalc, then grab the relevant bones to target */ - ob->pose->avs.recalc |= ANIMVIZ_RECALC_PATHS; - animviz_get_object_motionpaths(ob, &targets); + /* set flag to force recalc, then grab the relevant bones to target */ + ob->pose->avs.recalc |= ANIMVIZ_RECALC_PATHS; + animviz_get_object_motionpaths(ob, &targets); - /* recalculate paths, then free */ + /* recalculate paths, then free */ #ifdef DEBUG_TIME - TIMEIT_START(pose_path_calc); + TIMEIT_START(pose_path_calc); #endif - animviz_calc_motionpaths(depsgraph, bmain, scene, &targets, !free_depsgraph, current_frame_only); + animviz_calc_motionpaths(depsgraph, bmain, scene, &targets, !free_depsgraph, current_frame_only); #ifdef DEBUG_TIME - TIMEIT_END(pose_path_calc); + TIMEIT_END(pose_path_calc); #endif - BLI_freelistN(&targets); + BLI_freelistN(&targets); - if (!current_frame_only) { - /* Tag armature object for copy on write - so paths will draw/redraw. - * For currently frame only we update evaluated object directly. */ - DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE); - } + if (!current_frame_only) { + /* Tag armature object for copy on write - so paths will draw/redraw. + * For currently frame only we update evaluated object directly. */ + DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE); + } - /* Free temporary depsgraph instance */ - if (free_depsgraph) { - DEG_graph_free(depsgraph); - } + /* Free temporary depsgraph instance */ + if (free_depsgraph) { + DEG_graph_free(depsgraph); + } } - /* show popup to determine settings */ static int pose_calculate_paths_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { - Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); + Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); - if (ELEM(NULL, ob, ob->pose)) - return OPERATOR_CANCELLED; + if (ELEM(NULL, ob, ob->pose)) + return OPERATOR_CANCELLED; - /* set default settings from existing/stored settings */ - { - bAnimVizSettings *avs = &ob->pose->avs; - PointerRNA avs_ptr; + /* set default settings from existing/stored settings */ + { + bAnimVizSettings *avs = &ob->pose->avs; + PointerRNA avs_ptr; - RNA_int_set(op->ptr, "start_frame", avs->path_sf); - RNA_int_set(op->ptr, "end_frame", avs->path_ef); + RNA_int_set(op->ptr, "start_frame", avs->path_sf); + RNA_int_set(op->ptr, "end_frame", avs->path_ef); - RNA_pointer_create(NULL, &RNA_AnimVizMotionPaths, avs, &avs_ptr); - RNA_enum_set(op->ptr, "bake_location", RNA_enum_get(&avs_ptr, "bake_location")); - } + RNA_pointer_create(NULL, &RNA_AnimVizMotionPaths, avs, &avs_ptr); + RNA_enum_set(op->ptr, "bake_location", RNA_enum_get(&avs_ptr, "bake_location")); + } - /* show popup dialog to allow editing of range... */ - // FIXME: hardcoded dimensions here are just arbitrary - return WM_operator_props_dialog_popup(C, op, 200, 200); + /* show popup dialog to allow editing of range... */ + // FIXME: hardcoded dimensions here are just arbitrary + return WM_operator_props_dialog_popup(C, op, 200, 200); } /* For the object with pose/action: create path curves for selected bones @@ -289,120 +286,135 @@ static int pose_calculate_paths_invoke(bContext *C, wmOperator *op, const wmEven */ static int pose_calculate_paths_exec(bContext *C, wmOperator *op) { - Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); - Scene *scene = CTX_data_scene(C); + Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); + Scene *scene = CTX_data_scene(C); - if (ELEM(NULL, ob, ob->pose)) - return OPERATOR_CANCELLED; + if (ELEM(NULL, ob, ob->pose)) + return OPERATOR_CANCELLED; - /* grab baking settings from operator settings */ - { - bAnimVizSettings *avs = &ob->pose->avs; - PointerRNA avs_ptr; + /* grab baking settings from operator settings */ + { + bAnimVizSettings *avs = &ob->pose->avs; + PointerRNA avs_ptr; - avs->path_sf = RNA_int_get(op->ptr, "start_frame"); - avs->path_ef = RNA_int_get(op->ptr, "end_frame"); + avs->path_sf = RNA_int_get(op->ptr, "start_frame"); + avs->path_ef = RNA_int_get(op->ptr, "end_frame"); - RNA_pointer_create(NULL, &RNA_AnimVizMotionPaths, avs, &avs_ptr); - RNA_enum_set(&avs_ptr, "bake_location", RNA_enum_get(op->ptr, "bake_location")); - } + RNA_pointer_create(NULL, &RNA_AnimVizMotionPaths, avs, &avs_ptr); + RNA_enum_set(&avs_ptr, "bake_location", RNA_enum_get(op->ptr, "bake_location")); + } - /* set up path data for bones being calculated */ - CTX_DATA_BEGIN (C, bPoseChannel *, pchan, selected_pose_bones_from_active_object) - { - /* verify makes sure that the selected bone has a bone with the appropriate settings */ - animviz_verify_motionpaths(op->reports, scene, ob, pchan); - } - CTX_DATA_END; + /* set up path data for bones being calculated */ + CTX_DATA_BEGIN (C, bPoseChannel *, pchan, selected_pose_bones_from_active_object) { + /* verify makes sure that the selected bone has a bone with the appropriate settings */ + animviz_verify_motionpaths(op->reports, scene, ob, pchan); + } + CTX_DATA_END; #ifdef DEBUG_TIME - TIMEIT_START(recalc_pose_paths); + TIMEIT_START(recalc_pose_paths); #endif - /* calculate the bones that now have motionpaths... */ - /* TODO: only make for the selected bones? */ - ED_pose_recalculate_paths(C, scene, ob, false); + /* calculate the bones that now have motionpaths... */ + /* TODO: only make for the selected bones? */ + ED_pose_recalculate_paths(C, scene, ob, false); #ifdef DEBUG_TIME - TIMEIT_END(recalc_pose_paths); + TIMEIT_END(recalc_pose_paths); #endif - /* notifiers for updates */ - WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); + /* notifiers for updates */ + WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); - return OPERATOR_FINISHED; + return OPERATOR_FINISHED; } void POSE_OT_paths_calculate(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Calculate Bone Paths"; - ot->idname = "POSE_OT_paths_calculate"; - ot->description = "Calculate paths for the selected bones"; - - /* api callbacks */ - ot->invoke = pose_calculate_paths_invoke; - ot->exec = pose_calculate_paths_exec; - ot->poll = ED_operator_posemode_exclusive; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - RNA_def_int(ot->srna, "start_frame", 1, MINAFRAME, MAXFRAME, "Start", - "First frame to calculate bone paths on", MINFRAME, MAXFRAME / 2.0); - RNA_def_int(ot->srna, "end_frame", 250, MINAFRAME, MAXFRAME, "End", - "Last frame to calculate bone paths on", MINFRAME, MAXFRAME / 2.0); - - RNA_def_enum(ot->srna, "bake_location", rna_enum_motionpath_bake_location_items, - MOTIONPATH_BAKE_HEADS, - "Bake Location", - "Which point on the bones is used when calculating paths"); + /* identifiers */ + ot->name = "Calculate Bone Paths"; + ot->idname = "POSE_OT_paths_calculate"; + ot->description = "Calculate paths for the selected bones"; + + /* api callbacks */ + ot->invoke = pose_calculate_paths_invoke; + ot->exec = pose_calculate_paths_exec; + ot->poll = ED_operator_posemode_exclusive; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + RNA_def_int(ot->srna, + "start_frame", + 1, + MINAFRAME, + MAXFRAME, + "Start", + "First frame to calculate bone paths on", + MINFRAME, + MAXFRAME / 2.0); + RNA_def_int(ot->srna, + "end_frame", + 250, + MINAFRAME, + MAXFRAME, + "End", + "Last frame to calculate bone paths on", + MINFRAME, + MAXFRAME / 2.0); + + RNA_def_enum(ot->srna, + "bake_location", + rna_enum_motionpath_bake_location_items, + MOTIONPATH_BAKE_HEADS, + "Bake Location", + "Which point on the bones is used when calculating paths"); } /* --------- */ static bool pose_update_paths_poll(bContext *C) { - if (ED_operator_posemode_exclusive(C)) { - Object *ob = CTX_data_active_object(C); - return (ob->pose->avs.path_bakeflag & MOTIONPATH_BAKE_HAS_PATHS) != 0; - } + if (ED_operator_posemode_exclusive(C)) { + Object *ob = CTX_data_active_object(C); + return (ob->pose->avs.path_bakeflag & MOTIONPATH_BAKE_HAS_PATHS) != 0; + } - return false; + return false; } static int pose_update_paths_exec(bContext *C, wmOperator *UNUSED(op)) { - Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); - Scene *scene = CTX_data_scene(C); + Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); + Scene *scene = CTX_data_scene(C); - if (ELEM(NULL, ob, scene)) - return OPERATOR_CANCELLED; + if (ELEM(NULL, ob, scene)) + return OPERATOR_CANCELLED; - /* calculate the bones that now have motionpaths... */ - /* TODO: only make for the selected bones? */ - ED_pose_recalculate_paths(C, scene, ob, false); + /* calculate the bones that now have motionpaths... */ + /* TODO: only make for the selected bones? */ + ED_pose_recalculate_paths(C, scene, ob, false); - /* notifiers for updates */ - WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); + /* notifiers for updates */ + WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); - return OPERATOR_FINISHED; + return OPERATOR_FINISHED; } void POSE_OT_paths_update(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Update Bone Paths"; - ot->idname = "POSE_OT_paths_update"; - ot->description = "Recalculate paths for bones that already have them"; + /* identifiers */ + ot->name = "Update Bone Paths"; + ot->idname = "POSE_OT_paths_update"; + ot->description = "Recalculate paths for bones that already have them"; - /* api callbakcs */ - ot->exec = pose_update_paths_exec; - ot->poll = pose_update_paths_poll; + /* api callbakcs */ + ot->exec = pose_update_paths_exec; + ot->poll = pose_update_paths_poll; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } /* --------- */ @@ -410,354 +422,358 @@ void POSE_OT_paths_update(wmOperatorType *ot) /* for the object with pose/action: clear path curves for selected bones only */ static void ED_pose_clear_paths(Object *ob, bool only_selected) { - bPoseChannel *pchan; - bool skipped = false; - - if (ELEM(NULL, ob, ob->pose)) - return; - - /* free the motionpath blocks for all bones - This is easier for users to quickly clear all */ - for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { - if (pchan->mpath) { - if ((only_selected == false) || ((pchan->bone) && (pchan->bone->flag & BONE_SELECTED))) { - animviz_free_motionpath(pchan->mpath); - pchan->mpath = NULL; - } - else { - skipped = true; - } - } - } - - /* if nothing was skipped, there should be no paths left! */ - if (skipped == false) - ob->pose->avs.path_bakeflag &= ~MOTIONPATH_BAKE_HAS_PATHS; - - /* tag armature object for copy on write - so removed paths don't still show */ - DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE); + bPoseChannel *pchan; + bool skipped = false; + + if (ELEM(NULL, ob, ob->pose)) + return; + + /* free the motionpath blocks for all bones - This is easier for users to quickly clear all */ + for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { + if (pchan->mpath) { + if ((only_selected == false) || ((pchan->bone) && (pchan->bone->flag & BONE_SELECTED))) { + animviz_free_motionpath(pchan->mpath); + pchan->mpath = NULL; + } + else { + skipped = true; + } + } + } + + /* if nothing was skipped, there should be no paths left! */ + if (skipped == false) + ob->pose->avs.path_bakeflag &= ~MOTIONPATH_BAKE_HAS_PATHS; + + /* tag armature object for copy on write - so removed paths don't still show */ + DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE); } /* operator callback - wrapper for the backend function */ static int pose_clear_paths_exec(bContext *C, wmOperator *op) { - Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); - bool only_selected = RNA_boolean_get(op->ptr, "only_selected"); + Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); + bool only_selected = RNA_boolean_get(op->ptr, "only_selected"); - /* only continue if there's an object */ - if (ELEM(NULL, ob, ob->pose)) - return OPERATOR_CANCELLED; + /* only continue if there's an object */ + if (ELEM(NULL, ob, ob->pose)) + return OPERATOR_CANCELLED; - /* use the backend function for this */ - ED_pose_clear_paths(ob, only_selected); + /* use the backend function for this */ + ED_pose_clear_paths(ob, only_selected); - /* notifiers for updates */ - WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); + /* notifiers for updates */ + WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); - return OPERATOR_FINISHED; + return OPERATOR_FINISHED; } /* operator callback/wrapper */ static int pose_clear_paths_invoke(bContext *C, wmOperator *op, const wmEvent *evt) { - if ((evt->shift) && !RNA_struct_property_is_set(op->ptr, "only_selected")) { - RNA_boolean_set(op->ptr, "only_selected", true); - } - return pose_clear_paths_exec(C, op); + if ((evt->shift) && !RNA_struct_property_is_set(op->ptr, "only_selected")) { + RNA_boolean_set(op->ptr, "only_selected", true); + } + return pose_clear_paths_exec(C, op); } void POSE_OT_paths_clear(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Clear Bone Paths"; - ot->idname = "POSE_OT_paths_clear"; - ot->description = "Clear path caches for all bones, hold Shift key for selected bones only"; - - /* api callbacks */ - ot->invoke = pose_clear_paths_invoke; - ot->exec = pose_clear_paths_exec; - ot->poll = ED_operator_posemode_exclusive; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - ot->prop = RNA_def_boolean(ot->srna, "only_selected", false, "Only Selected", - "Only clear paths from selected bones"); - RNA_def_property_flag(ot->prop, PROP_SKIP_SAVE); + /* identifiers */ + ot->name = "Clear Bone Paths"; + ot->idname = "POSE_OT_paths_clear"; + ot->description = "Clear path caches for all bones, hold Shift key for selected bones only"; + + /* api callbacks */ + ot->invoke = pose_clear_paths_invoke; + ot->exec = pose_clear_paths_exec; + ot->poll = ED_operator_posemode_exclusive; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + ot->prop = RNA_def_boolean( + ot->srna, "only_selected", false, "Only Selected", "Only clear paths from selected bones"); + RNA_def_property_flag(ot->prop, PROP_SKIP_SAVE); } /* --------- */ static int pose_update_paths_range_exec(bContext *C, wmOperator *UNUSED(op)) { - Scene *scene = CTX_data_scene(C); - Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); + Scene *scene = CTX_data_scene(C); + Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); - if (ELEM(NULL, scene, ob, ob->pose)) { - return OPERATOR_CANCELLED; - } + if (ELEM(NULL, scene, ob, ob->pose)) { + return OPERATOR_CANCELLED; + } - /* use Preview Range or Full Frame Range - whichever is in use */ - ob->pose->avs.path_sf = PSFRA; - ob->pose->avs.path_ef = PEFRA; + /* use Preview Range or Full Frame Range - whichever is in use */ + ob->pose->avs.path_sf = PSFRA; + ob->pose->avs.path_ef = PEFRA; - /* tag for updates */ - DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE); - WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); + /* tag for updates */ + DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE); + WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); - return OPERATOR_FINISHED; + return OPERATOR_FINISHED; } void POSE_OT_paths_range_update(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Update Range from Scene"; - ot->idname = "POSE_OT_paths_range_update"; - ot->description = "Update frame range for motion paths from the Scene's current frame range"; + /* identifiers */ + ot->name = "Update Range from Scene"; + ot->idname = "POSE_OT_paths_range_update"; + ot->description = "Update frame range for motion paths from the Scene's current frame range"; - /* callbacks */ - ot->exec = pose_update_paths_range_exec; - ot->poll = ED_operator_posemode_exclusive; + /* callbacks */ + ot->exec = pose_update_paths_range_exec; + ot->poll = ED_operator_posemode_exclusive; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } /* ********************************************** */ static int pose_flip_names_exec(bContext *C, wmOperator *op) { - Main *bmain = CTX_data_main(C); - ViewLayer *view_layer = CTX_data_view_layer(C); - View3D *v3d = CTX_wm_view3d(C); - const bool do_strip_numbers = RNA_boolean_get(op->ptr, "do_strip_numbers"); + Main *bmain = CTX_data_main(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + View3D *v3d = CTX_wm_view3d(C); + const bool do_strip_numbers = RNA_boolean_get(op->ptr, "do_strip_numbers"); - FOREACH_OBJECT_IN_MODE_BEGIN (view_layer, v3d, OB_ARMATURE, OB_MODE_POSE, ob) - { - bArmature *arm = ob->data; - ListBase bones_names = {NULL}; + FOREACH_OBJECT_IN_MODE_BEGIN (view_layer, v3d, OB_ARMATURE, OB_MODE_POSE, ob) { + bArmature *arm = ob->data; + ListBase bones_names = {NULL}; - FOREACH_PCHAN_SELECTED_IN_OBJECT_BEGIN (ob, pchan) - { - BLI_addtail(&bones_names, BLI_genericNodeN(pchan->name)); - } - FOREACH_PCHAN_SELECTED_IN_OBJECT_END; + FOREACH_PCHAN_SELECTED_IN_OBJECT_BEGIN (ob, pchan) { + BLI_addtail(&bones_names, BLI_genericNodeN(pchan->name)); + } + FOREACH_PCHAN_SELECTED_IN_OBJECT_END; - ED_armature_bones_flip_names(bmain, arm, &bones_names, do_strip_numbers); + ED_armature_bones_flip_names(bmain, arm, &bones_names, do_strip_numbers); - BLI_freelistN(&bones_names); + BLI_freelistN(&bones_names); - /* since we renamed stuff... */ - DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); + /* since we renamed stuff... */ + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); - /* note, notifier might evolve */ - WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); - } - FOREACH_OBJECT_IN_MODE_END; + /* note, notifier might evolve */ + WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); + } + FOREACH_OBJECT_IN_MODE_END; - return OPERATOR_FINISHED; + return OPERATOR_FINISHED; } void POSE_OT_flip_names(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Flip Names"; - ot->idname = "POSE_OT_flip_names"; - ot->description = "Flips (and corrects) the axis suffixes of the names of selected bones"; - - /* api callbacks */ - ot->exec = pose_flip_names_exec; - ot->poll = ED_operator_posemode_local; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - RNA_def_boolean(ot->srna, "do_strip_numbers", false, "Strip Numbers", - "Try to remove right-most dot-number from flipped names " - "(WARNING: may result in incoherent naming in some cases)"); + /* identifiers */ + ot->name = "Flip Names"; + ot->idname = "POSE_OT_flip_names"; + ot->description = "Flips (and corrects) the axis suffixes of the names of selected bones"; + + /* api callbacks */ + ot->exec = pose_flip_names_exec; + ot->poll = ED_operator_posemode_local; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + RNA_def_boolean(ot->srna, + "do_strip_numbers", + false, + "Strip Numbers", + "Try to remove right-most dot-number from flipped names " + "(WARNING: may result in incoherent naming in some cases)"); } /* ------------------ */ static int pose_autoside_names_exec(bContext *C, wmOperator *op) { - Main *bmain = CTX_data_main(C); - char newname[MAXBONENAME]; - short axis = RNA_enum_get(op->ptr, "axis"); - Object *ob_prev = NULL; - - /* loop through selected bones, auto-naming them */ - CTX_DATA_BEGIN_WITH_ID(C, bPoseChannel *, pchan, selected_pose_bones, Object *, ob) - { - bArmature *arm = ob->data; - BLI_strncpy(newname, pchan->name, sizeof(newname)); - if (bone_autoside_name(newname, 1, axis, pchan->bone->head[axis], pchan->bone->tail[axis])) { - ED_armature_bone_rename(bmain, arm, pchan->name, newname); - } - - if (ob_prev != ob) { - /* since we renamed stuff... */ - DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); - - /* note, notifier might evolve */ - WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); - ob_prev = ob; - } - } - CTX_DATA_END; - - return OPERATOR_FINISHED; + Main *bmain = CTX_data_main(C); + char newname[MAXBONENAME]; + short axis = RNA_enum_get(op->ptr, "axis"); + Object *ob_prev = NULL; + + /* loop through selected bones, auto-naming them */ + CTX_DATA_BEGIN_WITH_ID(C, bPoseChannel *, pchan, selected_pose_bones, Object *, ob) + { + bArmature *arm = ob->data; + BLI_strncpy(newname, pchan->name, sizeof(newname)); + if (bone_autoside_name(newname, 1, axis, pchan->bone->head[axis], pchan->bone->tail[axis])) { + ED_armature_bone_rename(bmain, arm, pchan->name, newname); + } + + if (ob_prev != ob) { + /* since we renamed stuff... */ + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); + + /* note, notifier might evolve */ + WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); + ob_prev = ob; + } + } + CTX_DATA_END; + + return OPERATOR_FINISHED; } void POSE_OT_autoside_names(wmOperatorType *ot) { - static const EnumPropertyItem axis_items[] = { - {0, "XAXIS", 0, "X-Axis", "Left/Right"}, - {1, "YAXIS", 0, "Y-Axis", "Front/Back"}, - {2, "ZAXIS", 0, "Z-Axis", "Top/Bottom"}, - {0, NULL, 0, NULL, NULL}, - }; - - /* identifiers */ - ot->name = "AutoName by Axis"; - ot->idname = "POSE_OT_autoside_names"; - ot->description = "Automatically renames the selected bones according to which side of the target axis they fall on"; - - /* api callbacks */ - ot->invoke = WM_menu_invoke; - ot->exec = pose_autoside_names_exec; - ot->poll = ED_operator_posemode; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* settings */ - ot->prop = RNA_def_enum(ot->srna, "axis", axis_items, 0, "Axis", "Axis tag names with"); + static const EnumPropertyItem axis_items[] = { + {0, "XAXIS", 0, "X-Axis", "Left/Right"}, + {1, "YAXIS", 0, "Y-Axis", "Front/Back"}, + {2, "ZAXIS", 0, "Z-Axis", "Top/Bottom"}, + {0, NULL, 0, NULL, NULL}, + }; + + /* identifiers */ + ot->name = "AutoName by Axis"; + ot->idname = "POSE_OT_autoside_names"; + ot->description = + "Automatically renames the selected bones according to which side of the target axis they " + "fall on"; + + /* api callbacks */ + ot->invoke = WM_menu_invoke; + ot->exec = pose_autoside_names_exec; + ot->poll = ED_operator_posemode; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* settings */ + ot->prop = RNA_def_enum(ot->srna, "axis", axis_items, 0, "Axis", "Axis tag names with"); } /* ********************************************** */ static int pose_bone_rotmode_exec(bContext *C, wmOperator *op) { - const int mode = RNA_enum_get(op->ptr, "type"); - Object *prev_ob = NULL; - - /* set rotation mode of selected bones */ - CTX_DATA_BEGIN_WITH_ID (C, bPoseChannel *, pchan, selected_pose_bones, Object *, ob) - { - pchan->rotmode = mode; - - if (prev_ob != ob) { - /* Notifiers and updates. */ - DEG_id_tag_update((ID *)ob, ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, ob); - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); - prev_ob = ob; - } - } - CTX_DATA_END; - - return OPERATOR_FINISHED; + const int mode = RNA_enum_get(op->ptr, "type"); + Object *prev_ob = NULL; + + /* set rotation mode of selected bones */ + CTX_DATA_BEGIN_WITH_ID(C, bPoseChannel *, pchan, selected_pose_bones, Object *, ob) + { + pchan->rotmode = mode; + + if (prev_ob != ob) { + /* Notifiers and updates. */ + DEG_id_tag_update((ID *)ob, ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, ob); + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); + prev_ob = ob; + } + } + CTX_DATA_END; + + return OPERATOR_FINISHED; } void POSE_OT_rotation_mode_set(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Set Rotation Mode"; - ot->idname = "POSE_OT_rotation_mode_set"; - ot->description = "Set the rotation representation used by selected bones"; - - /* callbacks */ - ot->invoke = WM_menu_invoke; - ot->exec = pose_bone_rotmode_exec; - ot->poll = ED_operator_posemode; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - ot->prop = RNA_def_enum( - ot->srna, "type", rna_enum_object_rotation_mode_items, 0, "Rotation Mode", ""); + /* identifiers */ + ot->name = "Set Rotation Mode"; + ot->idname = "POSE_OT_rotation_mode_set"; + ot->description = "Set the rotation representation used by selected bones"; + + /* callbacks */ + ot->invoke = WM_menu_invoke; + ot->exec = pose_bone_rotmode_exec; + ot->poll = ED_operator_posemode; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + ot->prop = RNA_def_enum( + ot->srna, "type", rna_enum_object_rotation_mode_items, 0, "Rotation Mode", ""); } /* ********************************************** */ static bool armature_layers_poll(bContext *C) { - /* Armature layers operators can be used in posemode OR editmode for armatures */ - return ED_operator_posemode(C) || ED_operator_editarmature(C); + /* Armature layers operators can be used in posemode OR editmode for armatures */ + return ED_operator_posemode(C) || ED_operator_editarmature(C); } static bArmature *armature_layers_get_data(Object **ob) { - bArmature *arm = NULL; - - /* Sanity checking and handling of posemode. */ - if (*ob) { - Object *tob = BKE_object_pose_armature_get(*ob); - if (tob) { - *ob = tob; - arm = (*ob)->data; - } - else if ((*ob)->type == OB_ARMATURE) { - arm = (*ob)->data; - } - } - - return arm; + bArmature *arm = NULL; + + /* Sanity checking and handling of posemode. */ + if (*ob) { + Object *tob = BKE_object_pose_armature_get(*ob); + if (tob) { + *ob = tob; + arm = (*ob)->data; + } + else if ((*ob)->type == OB_ARMATURE) { + arm = (*ob)->data; + } + } + + return arm; } /* Show all armature layers */ static int pose_armature_layers_showall_exec(bContext *C, wmOperator *op) { - Object *ob = CTX_data_active_object(C); - bArmature *arm = armature_layers_get_data(&ob); - PointerRNA ptr; - int maxLayers = (RNA_boolean_get(op->ptr, "all")) ? 32 : 16; - /* hardcoded for now - we can only have 32 armature layers, so this should be fine... */ - bool layers[32] = {false}; - int i; - - /* sanity checking */ - if (arm == NULL) - return OPERATOR_CANCELLED; - - /* use RNA to set the layers - * although it would be faster to just set directly using bitflags, we still - * need to setup a RNA pointer so that we get the "update" callbacks for free... - */ - RNA_id_pointer_create(&arm->id, &ptr); - - for (i = 0; i < maxLayers; i++) - layers[i] = 1; - - RNA_boolean_set_array(&ptr, "layers", layers); - - /* note, notifier might evolve */ - WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); - DEG_id_tag_update(&arm->id, ID_RECALC_COPY_ON_WRITE); - - /* done */ - return OPERATOR_FINISHED; + Object *ob = CTX_data_active_object(C); + bArmature *arm = armature_layers_get_data(&ob); + PointerRNA ptr; + int maxLayers = (RNA_boolean_get(op->ptr, "all")) ? 32 : 16; + /* hardcoded for now - we can only have 32 armature layers, so this should be fine... */ + bool layers[32] = {false}; + int i; + + /* sanity checking */ + if (arm == NULL) + return OPERATOR_CANCELLED; + + /* use RNA to set the layers + * although it would be faster to just set directly using bitflags, we still + * need to setup a RNA pointer so that we get the "update" callbacks for free... + */ + RNA_id_pointer_create(&arm->id, &ptr); + + for (i = 0; i < maxLayers; i++) + layers[i] = 1; + + RNA_boolean_set_array(&ptr, "layers", layers); + + /* note, notifier might evolve */ + WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); + DEG_id_tag_update(&arm->id, ID_RECALC_COPY_ON_WRITE); + + /* done */ + return OPERATOR_FINISHED; } void ARMATURE_OT_layers_show_all(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Show All Layers"; - ot->idname = "ARMATURE_OT_layers_show_all"; - ot->description = "Make all armature layers visible"; + /* identifiers */ + ot->name = "Show All Layers"; + ot->idname = "ARMATURE_OT_layers_show_all"; + ot->description = "Make all armature layers visible"; - /* callbacks */ - ot->exec = pose_armature_layers_showall_exec; - ot->poll = armature_layers_poll; + /* callbacks */ + ot->exec = pose_armature_layers_showall_exec; + ot->poll = armature_layers_poll; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - /* properties */ - ot->prop = RNA_def_boolean(ot->srna, "all", 1, "All Layers", "Enable all layers or just the first 16 (top row)"); + /* properties */ + ot->prop = RNA_def_boolean( + ot->srna, "all", 1, "All Layers", "Enable all layers or just the first 16 (top row)"); } /* ------------------- */ @@ -765,69 +781,70 @@ void ARMATURE_OT_layers_show_all(wmOperatorType *ot) /* Present a popup to get the layers that should be used */ static int armature_layers_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - Object *ob = CTX_data_active_object(C); - bArmature *arm = armature_layers_get_data(&ob); - PointerRNA ptr; - /* hardcoded for now - we can only have 32 armature layers, so this should be fine... */ - bool layers[32]; - - /* sanity checking */ - if (arm == NULL) - return OPERATOR_CANCELLED; - - /* get RNA pointer to armature data to use that to retrieve the layers as ints to init the operator */ - RNA_id_pointer_create((ID *)arm, &ptr); - RNA_boolean_get_array(&ptr, "layers", layers); - RNA_boolean_set_array(op->ptr, "layers", layers); - - /* part to sync with other similar operators... */ - return WM_operator_props_popup(C, op, event); + Object *ob = CTX_data_active_object(C); + bArmature *arm = armature_layers_get_data(&ob); + PointerRNA ptr; + /* hardcoded for now - we can only have 32 armature layers, so this should be fine... */ + bool layers[32]; + + /* sanity checking */ + if (arm == NULL) + return OPERATOR_CANCELLED; + + /* get RNA pointer to armature data to use that to retrieve the layers as ints to init the operator */ + RNA_id_pointer_create((ID *)arm, &ptr); + RNA_boolean_get_array(&ptr, "layers", layers); + RNA_boolean_set_array(op->ptr, "layers", layers); + + /* part to sync with other similar operators... */ + return WM_operator_props_popup(C, op, event); } /* Set the visible layers for the active armature (edit and pose modes) */ static int armature_layers_exec(bContext *C, wmOperator *op) { - Object *ob = CTX_data_active_object(C); - bArmature *arm = armature_layers_get_data(&ob); - PointerRNA ptr; - /* hardcoded for now - we can only have 32 armature layers, so this should be fine... */ - bool layers[32]; + Object *ob = CTX_data_active_object(C); + bArmature *arm = armature_layers_get_data(&ob); + PointerRNA ptr; + /* hardcoded for now - we can only have 32 armature layers, so this should be fine... */ + bool layers[32]; - if (arm == NULL) { - return OPERATOR_CANCELLED; - } + if (arm == NULL) { + return OPERATOR_CANCELLED; + } - /* get the values set in the operator properties */ - RNA_boolean_get_array(op->ptr, "layers", layers); + /* get the values set in the operator properties */ + RNA_boolean_get_array(op->ptr, "layers", layers); - /* get pointer for armature, and write data there... */ - RNA_id_pointer_create((ID *)arm, &ptr); - RNA_boolean_set_array(&ptr, "layers", layers); + /* get pointer for armature, and write data there... */ + RNA_id_pointer_create((ID *)arm, &ptr); + RNA_boolean_set_array(&ptr, "layers", layers); - /* note, notifier might evolve */ - WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); - DEG_id_tag_update(&arm->id, ID_RECALC_COPY_ON_WRITE); + /* note, notifier might evolve */ + WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); + DEG_id_tag_update(&arm->id, ID_RECALC_COPY_ON_WRITE); - return OPERATOR_FINISHED; + return OPERATOR_FINISHED; } void ARMATURE_OT_armature_layers(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Change Armature Layers"; - ot->idname = "ARMATURE_OT_armature_layers"; - ot->description = "Change the visible armature layers"; - - /* callbacks */ - ot->invoke = armature_layers_invoke; - ot->exec = armature_layers_exec; - ot->poll = armature_layers_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - RNA_def_boolean_layer_member(ot->srna, "layers", 32, NULL, "Layer", "Armature layers to make visible"); + /* identifiers */ + ot->name = "Change Armature Layers"; + ot->idname = "ARMATURE_OT_armature_layers"; + ot->description = "Change the visible armature layers"; + + /* callbacks */ + ot->invoke = armature_layers_invoke; + ot->exec = armature_layers_exec; + ot->poll = armature_layers_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + RNA_def_boolean_layer_member( + ot->srna, "layers", 32, NULL, "Layer", "Armature layers to make visible"); } /* ------------------- */ @@ -835,75 +852,75 @@ void ARMATURE_OT_armature_layers(wmOperatorType *ot) /* Present a popup to get the layers that should be used */ static int pose_bone_layers_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - /* hardcoded for now - we can only have 32 armature layers, so this should be fine... */ - bool layers[32] = {0}; - - /* get layers that are active already */ - CTX_DATA_BEGIN (C, bPoseChannel *, pchan, selected_pose_bones) - { - short bit; - - /* loop over the bits for this pchan's layers, adding layers where they're needed */ - for (bit = 0; bit < 32; bit++) { - layers[bit] = (pchan->bone->layer & (1u << bit)) != 0; - } - } - CTX_DATA_END; - - /* copy layers to operator */ - RNA_boolean_set_array(op->ptr, "layers", layers); - - /* part to sync with other similar operators... */ - return WM_operator_props_popup(C, op, event); + /* hardcoded for now - we can only have 32 armature layers, so this should be fine... */ + bool layers[32] = {0}; + + /* get layers that are active already */ + CTX_DATA_BEGIN (C, bPoseChannel *, pchan, selected_pose_bones) { + short bit; + + /* loop over the bits for this pchan's layers, adding layers where they're needed */ + for (bit = 0; bit < 32; bit++) { + layers[bit] = (pchan->bone->layer & (1u << bit)) != 0; + } + } + CTX_DATA_END; + + /* copy layers to operator */ + RNA_boolean_set_array(op->ptr, "layers", layers); + + /* part to sync with other similar operators... */ + return WM_operator_props_popup(C, op, event); } /* Set the visible layers for the active armature (edit and pose modes) */ static int pose_bone_layers_exec(bContext *C, wmOperator *op) { - PointerRNA ptr; - /* hardcoded for now - we can only have 32 armature layers, so this should be fine... */ - bool layers[32]; - - /* get the values set in the operator properties */ - RNA_boolean_get_array(op->ptr, "layers", layers); - - Object *prev_ob = NULL; - - /* set layers of pchans based on the values set in the operator props */ - CTX_DATA_BEGIN_WITH_ID (C, bPoseChannel *, pchan, selected_pose_bones, Object *, ob) - { - /* get pointer for pchan, and write flags this way */ - RNA_pointer_create((ID *)ob->data, &RNA_Bone, pchan->bone, &ptr); - RNA_boolean_set_array(&ptr, "layers", layers); - - if (prev_ob != ob) { - /* 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); - prev_ob = ob; - } - } - CTX_DATA_END; - return OPERATOR_FINISHED; + PointerRNA ptr; + /* hardcoded for now - we can only have 32 armature layers, so this should be fine... */ + bool layers[32]; + + /* get the values set in the operator properties */ + RNA_boolean_get_array(op->ptr, "layers", layers); + + Object *prev_ob = NULL; + + /* set layers of pchans based on the values set in the operator props */ + CTX_DATA_BEGIN_WITH_ID(C, bPoseChannel *, pchan, selected_pose_bones, Object *, ob) + { + /* get pointer for pchan, and write flags this way */ + RNA_pointer_create((ID *)ob->data, &RNA_Bone, pchan->bone, &ptr); + RNA_boolean_set_array(&ptr, "layers", layers); + + if (prev_ob != ob) { + /* 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); + prev_ob = ob; + } + } + CTX_DATA_END; + return OPERATOR_FINISHED; } void POSE_OT_bone_layers(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Change Bone Layers"; - ot->idname = "POSE_OT_bone_layers"; - ot->description = "Change the layers that the selected bones belong to"; - - /* callbacks */ - ot->invoke = pose_bone_layers_invoke; - ot->exec = pose_bone_layers_exec; - ot->poll = ED_operator_posemode_exclusive; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - RNA_def_boolean_layer_member(ot->srna, "layers", 32, NULL, "Layer", "Armature layers that bone belongs to"); + /* identifiers */ + ot->name = "Change Bone Layers"; + ot->idname = "POSE_OT_bone_layers"; + ot->description = "Change the layers that the selected bones belong to"; + + /* callbacks */ + ot->invoke = pose_bone_layers_invoke; + ot->exec = pose_bone_layers_exec; + ot->poll = ED_operator_posemode_exclusive; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + RNA_def_boolean_layer_member( + ot->srna, "layers", 32, NULL, "Layer", "Armature layers that bone belongs to"); } /* ------------------- */ @@ -911,73 +928,73 @@ void POSE_OT_bone_layers(wmOperatorType *ot) /* Present a popup to get the layers that should be used */ static int armature_bone_layers_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - /* hardcoded for now - we can only have 32 armature layers, so this should be fine... */ - bool layers[32] = {0}; - - /* get layers that are active already */ - CTX_DATA_BEGIN (C, EditBone *, ebone, selected_editable_bones) - { - short bit; - - /* loop over the bits for this pchan's layers, adding layers where they're needed */ - for (bit = 0; bit < 32; bit++) { - if (ebone->layer & (1u << bit)) { - layers[bit] = 1; - } - } - } - CTX_DATA_END; - - /* copy layers to operator */ - RNA_boolean_set_array(op->ptr, "layers", layers); - - /* part to sync with other similar operators... */ - return WM_operator_props_popup(C, op, event); + /* hardcoded for now - we can only have 32 armature layers, so this should be fine... */ + bool layers[32] = {0}; + + /* get layers that are active already */ + CTX_DATA_BEGIN (C, EditBone *, ebone, selected_editable_bones) { + short bit; + + /* loop over the bits for this pchan's layers, adding layers where they're needed */ + for (bit = 0; bit < 32; bit++) { + if (ebone->layer & (1u << bit)) { + layers[bit] = 1; + } + } + } + CTX_DATA_END; + + /* copy layers to operator */ + RNA_boolean_set_array(op->ptr, "layers", layers); + + /* part to sync with other similar operators... */ + return WM_operator_props_popup(C, op, event); } /* Set the visible layers for the active armature (edit and pose modes) */ static int armature_bone_layers_exec(bContext *C, wmOperator *op) { - Object *ob = CTX_data_edit_object(C); - PointerRNA ptr; - /* hardcoded for now - we can only have 32 armature layers, so this should be fine... */ - bool layers[32]; - - /* get the values set in the operator properties */ - RNA_boolean_get_array(op->ptr, "layers", layers); - - /* set layers of pchans based on the values set in the operator props */ - CTX_DATA_BEGIN_WITH_ID (C, EditBone *, ebone, selected_editable_bones, bArmature *, arm) - { - /* get pointer for pchan, and write flags this way */ - RNA_pointer_create((ID *)arm, &RNA_EditBone, ebone, &ptr); - RNA_boolean_set_array(&ptr, "layers", layers); - } - CTX_DATA_END; - - /* note, notifier might evolve */ - WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); - - return OPERATOR_FINISHED; + Object *ob = CTX_data_edit_object(C); + PointerRNA ptr; + /* hardcoded for now - we can only have 32 armature layers, so this should be fine... */ + bool layers[32]; + + /* get the values set in the operator properties */ + RNA_boolean_get_array(op->ptr, "layers", layers); + + /* set layers of pchans based on the values set in the operator props */ + CTX_DATA_BEGIN_WITH_ID(C, EditBone *, ebone, selected_editable_bones, bArmature *, arm) + { + /* get pointer for pchan, and write flags this way */ + RNA_pointer_create((ID *)arm, &RNA_EditBone, ebone, &ptr); + RNA_boolean_set_array(&ptr, "layers", layers); + } + CTX_DATA_END; + + /* note, notifier might evolve */ + WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); + + return OPERATOR_FINISHED; } void ARMATURE_OT_bone_layers(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Change Bone Layers"; - ot->idname = "ARMATURE_OT_bone_layers"; - ot->description = "Change the layers that the selected bones belong to"; - - /* callbacks */ - ot->invoke = armature_bone_layers_invoke; - ot->exec = armature_bone_layers_exec; - ot->poll = ED_operator_editarmature; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - RNA_def_boolean_layer_member(ot->srna, "layers", 32, NULL, "Layer", "Armature layers that bone belongs to"); + /* identifiers */ + ot->name = "Change Bone Layers"; + ot->idname = "ARMATURE_OT_bone_layers"; + ot->description = "Change the layers that the selected bones belong to"; + + /* callbacks */ + ot->invoke = armature_bone_layers_invoke; + ot->exec = armature_bone_layers_exec; + ot->poll = ED_operator_editarmature; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + RNA_def_boolean_layer_member( + ot->srna, "layers", 32, NULL, "Layer", "Armature layers that bone belongs to"); } /* ********************************************** */ @@ -985,132 +1002,133 @@ void ARMATURE_OT_bone_layers(wmOperatorType *ot) static int hide_pose_bone_fn(Object *ob, Bone *bone, void *ptr) { - bArmature *arm = ob->data; - const bool hide_select = (bool)POINTER_AS_INT(ptr); - int count = 0; - if (arm->layer & bone->layer) { - if (((bone->flag & BONE_SELECTED) != 0) == hide_select) { - bone->flag |= BONE_HIDDEN_P; - /* only needed when 'hide_select' is true, but harmless. */ - bone->flag &= ~BONE_SELECTED; - if (arm->act_bone == bone) { - arm->act_bone = NULL; - } - count += 1; - } - } - return count; + bArmature *arm = ob->data; + const bool hide_select = (bool)POINTER_AS_INT(ptr); + int count = 0; + if (arm->layer & bone->layer) { + if (((bone->flag & BONE_SELECTED) != 0) == hide_select) { + bone->flag |= BONE_HIDDEN_P; + /* only needed when 'hide_select' is true, but harmless. */ + bone->flag &= ~BONE_SELECTED; + if (arm->act_bone == bone) { + arm->act_bone = NULL; + } + count += 1; + } + } + return count; } /* active object is armature in posemode, poll checked */ static int pose_hide_exec(bContext *C, wmOperator *op) { - ViewLayer *view_layer = CTX_data_view_layer(C); - uint objects_len; - Object **objects = BKE_object_pose_array_get_unique(view_layer, CTX_wm_view3d(C), &objects_len); - bool changed_multi = false; - - const int hide_select = !RNA_boolean_get(op->ptr, "unselected"); - void *hide_select_p = POINTER_FROM_INT(hide_select); - - for (uint ob_index = 0; ob_index < objects_len; ob_index++) { - Object *ob_iter = objects[ob_index]; - bArmature *arm = ob_iter->data; - - if (ob_iter->proxy != NULL) { - BKE_report(op->reports, RPT_INFO, "Undo of hiding can only be done with Reveal Selected"); - } - - bool changed = bone_looper(ob_iter, arm->bonebase.first, hide_select_p, hide_pose_bone_fn) != 0; - if (changed) { - changed_multi = true; - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob_iter); - DEG_id_tag_update(&arm->id, ID_RECALC_COPY_ON_WRITE); - } - } - MEM_freeN(objects); - - return changed_multi ? OPERATOR_FINISHED : OPERATOR_CANCELLED; + ViewLayer *view_layer = CTX_data_view_layer(C); + uint objects_len; + Object **objects = BKE_object_pose_array_get_unique(view_layer, CTX_wm_view3d(C), &objects_len); + bool changed_multi = false; + + const int hide_select = !RNA_boolean_get(op->ptr, "unselected"); + void *hide_select_p = POINTER_FROM_INT(hide_select); + + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *ob_iter = objects[ob_index]; + bArmature *arm = ob_iter->data; + + if (ob_iter->proxy != NULL) { + BKE_report(op->reports, RPT_INFO, "Undo of hiding can only be done with Reveal Selected"); + } + + bool changed = bone_looper(ob_iter, arm->bonebase.first, hide_select_p, hide_pose_bone_fn) != + 0; + if (changed) { + changed_multi = true; + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob_iter); + DEG_id_tag_update(&arm->id, ID_RECALC_COPY_ON_WRITE); + } + } + MEM_freeN(objects); + + return changed_multi ? OPERATOR_FINISHED : OPERATOR_CANCELLED; } void POSE_OT_hide(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Hide Selected"; - ot->idname = "POSE_OT_hide"; - ot->description = "Tag selected bones to not be visible in Pose Mode"; + /* identifiers */ + ot->name = "Hide Selected"; + ot->idname = "POSE_OT_hide"; + ot->description = "Tag selected bones to not be visible in Pose Mode"; - /* api callbacks */ - ot->exec = pose_hide_exec; - ot->poll = ED_operator_posemode; + /* api callbacks */ + ot->exec = pose_hide_exec; + ot->poll = ED_operator_posemode; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - /* props */ - RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", ""); + /* props */ + RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", ""); } static int show_pose_bone_cb(Object *ob, Bone *bone, void *data) { - const bool select = POINTER_AS_INT(data); - - bArmature *arm = ob->data; - int count = 0; - if (arm->layer & bone->layer) { - if (bone->flag & BONE_HIDDEN_P) { - if (!(bone->flag & BONE_UNSELECTABLE)) { - SET_FLAG_FROM_TEST(bone->flag, select, BONE_SELECTED); - } - bone->flag &= ~BONE_HIDDEN_P; - count += 1; - } - } - - return count; + const bool select = POINTER_AS_INT(data); + + bArmature *arm = ob->data; + int count = 0; + if (arm->layer & bone->layer) { + if (bone->flag & BONE_HIDDEN_P) { + if (!(bone->flag & BONE_UNSELECTABLE)) { + SET_FLAG_FROM_TEST(bone->flag, select, BONE_SELECTED); + } + bone->flag &= ~BONE_HIDDEN_P; + count += 1; + } + } + + return count; } /* active object is armature in posemode, poll checked */ static int pose_reveal_exec(bContext *C, wmOperator *op) { - ViewLayer *view_layer = CTX_data_view_layer(C); - uint objects_len; - Object **objects = BKE_object_pose_array_get_unique(view_layer, CTX_wm_view3d(C), &objects_len); - bool changed_multi = false; - const bool select = RNA_boolean_get(op->ptr, "select"); - void *select_p = POINTER_FROM_INT(select); - - for (uint ob_index = 0; ob_index < objects_len; ob_index++) { - Object *ob_iter = objects[ob_index]; - bArmature *arm = ob_iter->data; - - bool changed = bone_looper(ob_iter, arm->bonebase.first, select_p, show_pose_bone_cb); - if (changed) { - changed_multi = true; - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob_iter); - DEG_id_tag_update(&arm->id, ID_RECALC_COPY_ON_WRITE); - } - } - MEM_freeN(objects); - - return changed_multi ? OPERATOR_FINISHED : OPERATOR_CANCELLED; + ViewLayer *view_layer = CTX_data_view_layer(C); + uint objects_len; + Object **objects = BKE_object_pose_array_get_unique(view_layer, CTX_wm_view3d(C), &objects_len); + bool changed_multi = false; + const bool select = RNA_boolean_get(op->ptr, "select"); + void *select_p = POINTER_FROM_INT(select); + + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *ob_iter = objects[ob_index]; + bArmature *arm = ob_iter->data; + + bool changed = bone_looper(ob_iter, arm->bonebase.first, select_p, show_pose_bone_cb); + if (changed) { + changed_multi = true; + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob_iter); + DEG_id_tag_update(&arm->id, ID_RECALC_COPY_ON_WRITE); + } + } + MEM_freeN(objects); + + return changed_multi ? OPERATOR_FINISHED : OPERATOR_CANCELLED; } void POSE_OT_reveal(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Reveal Selected"; - ot->idname = "POSE_OT_reveal"; - ot->description = "Reveal all bones hidden in Pose Mode"; + /* identifiers */ + ot->name = "Reveal Selected"; + ot->idname = "POSE_OT_reveal"; + ot->description = "Reveal all bones hidden in Pose Mode"; - /* api callbacks */ - ot->exec = pose_reveal_exec; - ot->poll = ED_operator_posemode; + /* api callbacks */ + ot->exec = pose_reveal_exec; + ot->poll = ED_operator_posemode; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - RNA_def_boolean(ot->srna, "select", true, "Select", ""); + RNA_def_boolean(ot->srna, "select", true, "Select", ""); } /* ********************************************** */ @@ -1118,49 +1136,53 @@ void POSE_OT_reveal(wmOperatorType *ot) static int pose_flip_quats_exec(bContext *C, wmOperator *UNUSED(op)) { - Scene *scene = CTX_data_scene(C); - KeyingSet *ks = ANIM_builtin_keyingset_get_named(NULL, ANIM_KS_LOC_ROT_SCALE_ID); - - bool changed_multi = false; - - ViewLayer *view_layer = CTX_data_view_layer(C); - View3D *v3d = CTX_wm_view3d(C); - FOREACH_OBJECT_IN_MODE_BEGIN (view_layer, v3d, OB_ARMATURE, OB_MODE_POSE, ob_iter) { - bool changed = false; - /* loop through all selected pchans, flipping and keying (as needed) */ - FOREACH_PCHAN_SELECTED_IN_OBJECT_BEGIN (ob_iter, pchan) { - /* only if bone is using quaternion rotation */ - if (pchan->rotmode == ROT_MODE_QUAT) { - changed = true; - /* quaternions have 720 degree range */ - negate_v4(pchan->quat); - - ED_autokeyframe_pchan(C, scene, ob_iter, pchan, ks); - } - } FOREACH_PCHAN_SELECTED_IN_OBJECT_END; - - if (changed) { - changed_multi = true; - /* notifiers and updates */ - DEG_id_tag_update(&ob_iter->id, ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, ob_iter); - } - } FOREACH_OBJECT_IN_MODE_END; - - return changed_multi ? OPERATOR_FINISHED : OPERATOR_CANCELLED; + Scene *scene = CTX_data_scene(C); + KeyingSet *ks = ANIM_builtin_keyingset_get_named(NULL, ANIM_KS_LOC_ROT_SCALE_ID); + + bool changed_multi = false; + + ViewLayer *view_layer = CTX_data_view_layer(C); + View3D *v3d = CTX_wm_view3d(C); + FOREACH_OBJECT_IN_MODE_BEGIN (view_layer, v3d, OB_ARMATURE, OB_MODE_POSE, ob_iter) { + bool changed = false; + /* loop through all selected pchans, flipping and keying (as needed) */ + FOREACH_PCHAN_SELECTED_IN_OBJECT_BEGIN (ob_iter, pchan) { + /* only if bone is using quaternion rotation */ + if (pchan->rotmode == ROT_MODE_QUAT) { + changed = true; + /* quaternions have 720 degree range */ + negate_v4(pchan->quat); + + ED_autokeyframe_pchan(C, scene, ob_iter, pchan, ks); + } + } + FOREACH_PCHAN_SELECTED_IN_OBJECT_END; + + if (changed) { + changed_multi = true; + /* notifiers and updates */ + DEG_id_tag_update(&ob_iter->id, ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, ob_iter); + } + } + FOREACH_OBJECT_IN_MODE_END; + + return changed_multi ? OPERATOR_FINISHED : OPERATOR_CANCELLED; } void POSE_OT_quaternions_flip(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Flip Quats"; - ot->idname = "POSE_OT_quaternions_flip"; - ot->description = "Flip quaternion values to achieve desired rotations, while maintaining the same orientations"; - - /* callbacks */ - ot->exec = pose_flip_quats_exec; - ot->poll = ED_operator_posemode; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* identifiers */ + ot->name = "Flip Quats"; + ot->idname = "POSE_OT_quaternions_flip"; + ot->description = + "Flip quaternion values to achieve desired rotations, while maintaining the same " + "orientations"; + + /* callbacks */ + ot->exec = pose_flip_quats_exec; + ot->poll = ED_operator_posemode; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } diff --git a/source/blender/editors/armature/pose_group.c b/source/blender/editors/armature/pose_group.c index 596e64b54fe..0d100a09dfd 100644 --- a/source/blender/editors/armature/pose_group.c +++ b/source/blender/editors/armature/pose_group.c @@ -56,68 +56,67 @@ static int pose_group_add_exec(bContext *C, wmOperator *UNUSED(op)) { - Object *ob = ED_pose_object_from_context(C); + Object *ob = ED_pose_object_from_context(C); - /* only continue if there's an object and pose */ - if (ELEM(NULL, ob, ob->pose)) - return OPERATOR_CANCELLED; + /* only continue if there's an object and pose */ + if (ELEM(NULL, ob, ob->pose)) + return OPERATOR_CANCELLED; - /* for now, just call the API function for this */ - BKE_pose_add_group(ob->pose, NULL); + /* for now, just call the API function for this */ + BKE_pose_add_group(ob->pose, NULL); - /* notifiers for updates */ - WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); + /* notifiers for updates */ + WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); - return OPERATOR_FINISHED; + return OPERATOR_FINISHED; } void POSE_OT_group_add(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Add Bone Group"; - ot->idname = "POSE_OT_group_add"; - ot->description = "Add a new bone group"; + /* identifiers */ + ot->name = "Add Bone Group"; + ot->idname = "POSE_OT_group_add"; + ot->description = "Add a new bone group"; - /* api callbacks */ - ot->exec = pose_group_add_exec; - ot->poll = ED_operator_posemode_context; + /* api callbacks */ + ot->exec = pose_group_add_exec; + ot->poll = ED_operator_posemode_context; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } - static int pose_group_remove_exec(bContext *C, wmOperator *UNUSED(op)) { - Object *ob = ED_pose_object_from_context(C); + Object *ob = ED_pose_object_from_context(C); - /* only continue if there's an object and pose */ - if (ELEM(NULL, ob, ob->pose)) - return OPERATOR_CANCELLED; + /* only continue if there's an object and pose */ + if (ELEM(NULL, ob, ob->pose)) + return OPERATOR_CANCELLED; - /* for now, just call the API function for this */ - BKE_pose_remove_group_index(ob->pose, ob->pose->active_group); + /* for now, just call the API function for this */ + BKE_pose_remove_group_index(ob->pose, ob->pose->active_group); - /* notifiers for updates */ - WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); - DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE); + /* notifiers for updates */ + WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); + DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE); - return OPERATOR_FINISHED; + return OPERATOR_FINISHED; } void POSE_OT_group_remove(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Remove Bone Group"; - ot->idname = "POSE_OT_group_remove"; - ot->description = "Remove the active bone group"; + /* identifiers */ + ot->name = "Remove Bone Group"; + ot->idname = "POSE_OT_group_remove"; + ot->description = "Remove the active bone group"; - /* api callbacks */ - ot->exec = pose_group_remove_exec; - ot->poll = ED_operator_posemode_context; + /* api callbacks */ + ot->exec = pose_group_remove_exec; + ot->poll = ED_operator_posemode_context; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } /* ------------ */ @@ -125,397 +124,397 @@ void POSE_OT_group_remove(wmOperatorType *ot) /* invoke callback which presents a list of bone-groups for the user to choose from */ static int pose_groups_menu_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { - Object *ob = ED_pose_object_from_context(C); - bPose *pose; - PropertyRNA *prop = RNA_struct_find_property(op->ptr, "type"); - - uiPopupMenu *pup; - uiLayout *layout; - bActionGroup *grp; - int i; - - /* only continue if there's an object, and a pose there too */ - if (ELEM(NULL, ob, ob->pose)) - return OPERATOR_CANCELLED; - pose = ob->pose; - - /* If group index is set, try to use it! */ - if (RNA_property_is_set(op->ptr, prop)) { - const int num_groups = BLI_listbase_count(&pose->agroups); - const int group = RNA_property_int_get(op->ptr, prop); - - /* just use the active group index, and call the exec callback for the calling operator */ - if (group > 0 && group <= num_groups) { - return op->type->exec(C, op); - } - } - - /* if there's no active group (or active is invalid), create a new menu to find it */ - if (pose->active_group <= 0) { - /* create a new menu, and start populating it with group names */ - pup = UI_popup_menu_begin(C, op->type->name, ICON_NONE); - layout = UI_popup_menu_layout(pup); - - /* special entry - allow to create new group, then use that - * (not to be used for removing though) - */ - if (strstr(op->idname, "assign")) { - uiItemIntO(layout, "New Group", ICON_NONE, op->idname, "type", 0); - uiItemS(layout); - } - - /* add entries for each group */ - for (grp = pose->agroups.first, i = 1; grp; grp = grp->next, i++) - uiItemIntO(layout, grp->name, ICON_NONE, op->idname, "type", i); - - /* finish building the menu, and process it (should result in calling self again) */ - UI_popup_menu_end(C, pup); - - return OPERATOR_INTERFACE; - } - else { - /* just use the active group index, and call the exec callback for the calling operator */ - RNA_int_set(op->ptr, "type", pose->active_group); - return op->type->exec(C, op); - } + Object *ob = ED_pose_object_from_context(C); + bPose *pose; + PropertyRNA *prop = RNA_struct_find_property(op->ptr, "type"); + + uiPopupMenu *pup; + uiLayout *layout; + bActionGroup *grp; + int i; + + /* only continue if there's an object, and a pose there too */ + if (ELEM(NULL, ob, ob->pose)) + return OPERATOR_CANCELLED; + pose = ob->pose; + + /* If group index is set, try to use it! */ + if (RNA_property_is_set(op->ptr, prop)) { + const int num_groups = BLI_listbase_count(&pose->agroups); + const int group = RNA_property_int_get(op->ptr, prop); + + /* just use the active group index, and call the exec callback for the calling operator */ + if (group > 0 && group <= num_groups) { + return op->type->exec(C, op); + } + } + + /* if there's no active group (or active is invalid), create a new menu to find it */ + if (pose->active_group <= 0) { + /* create a new menu, and start populating it with group names */ + pup = UI_popup_menu_begin(C, op->type->name, ICON_NONE); + layout = UI_popup_menu_layout(pup); + + /* special entry - allow to create new group, then use that + * (not to be used for removing though) + */ + if (strstr(op->idname, "assign")) { + uiItemIntO(layout, "New Group", ICON_NONE, op->idname, "type", 0); + uiItemS(layout); + } + + /* add entries for each group */ + for (grp = pose->agroups.first, i = 1; grp; grp = grp->next, i++) + uiItemIntO(layout, grp->name, ICON_NONE, op->idname, "type", i); + + /* finish building the menu, and process it (should result in calling self again) */ + UI_popup_menu_end(C, pup); + + return OPERATOR_INTERFACE; + } + else { + /* just use the active group index, and call the exec callback for the calling operator */ + RNA_int_set(op->ptr, "type", pose->active_group); + return op->type->exec(C, op); + } } /* Assign selected pchans to the bone group that the user selects */ static int pose_group_assign_exec(bContext *C, wmOperator *op) { - Object *ob = ED_pose_object_from_context(C); - bPose *pose; - bool done = false; - - /* only continue if there's an object, and a pose there too */ - if (ELEM(NULL, ob, ob->pose)) - return OPERATOR_CANCELLED; - - pose = ob->pose; - - /* set the active group number to the one from operator props - * - if 0 after this, make a new group... - */ - pose->active_group = RNA_int_get(op->ptr, "type"); - if (pose->active_group == 0) - BKE_pose_add_group(ob->pose, NULL); - - /* add selected bones to group then */ - FOREACH_PCHAN_SELECTED_IN_OBJECT_BEGIN (ob, pchan) - { - pchan->agrp_index = pose->active_group; - done = true; - } - FOREACH_PCHAN_SELECTED_IN_OBJECT_END; - - /* notifiers for updates */ - WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); - DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE); - - /* report done status */ - if (done) - return OPERATOR_FINISHED; - else - return OPERATOR_CANCELLED; + Object *ob = ED_pose_object_from_context(C); + bPose *pose; + bool done = false; + + /* only continue if there's an object, and a pose there too */ + if (ELEM(NULL, ob, ob->pose)) + return OPERATOR_CANCELLED; + + pose = ob->pose; + + /* set the active group number to the one from operator props + * - if 0 after this, make a new group... + */ + pose->active_group = RNA_int_get(op->ptr, "type"); + if (pose->active_group == 0) + BKE_pose_add_group(ob->pose, NULL); + + /* add selected bones to group then */ + FOREACH_PCHAN_SELECTED_IN_OBJECT_BEGIN (ob, pchan) { + pchan->agrp_index = pose->active_group; + done = true; + } + FOREACH_PCHAN_SELECTED_IN_OBJECT_END; + + /* notifiers for updates */ + WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); + DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE); + + /* report done status */ + if (done) + return OPERATOR_FINISHED; + else + return OPERATOR_CANCELLED; } void POSE_OT_group_assign(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Add Selected to Bone Group"; - ot->idname = "POSE_OT_group_assign"; - ot->description = "Add selected bones to the chosen bone group"; + /* identifiers */ + ot->name = "Add Selected to Bone Group"; + ot->idname = "POSE_OT_group_assign"; + ot->description = "Add selected bones to the chosen bone group"; - /* api callbacks */ - ot->invoke = pose_groups_menu_invoke; - ot->exec = pose_group_assign_exec; - ot->poll = ED_operator_posemode_context; + /* api callbacks */ + ot->invoke = pose_groups_menu_invoke; + ot->exec = pose_group_assign_exec; + ot->poll = ED_operator_posemode_context; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - /* properties */ - RNA_def_int(ot->srna, "type", 0, 0, INT_MAX, "Bone Group Index", "", 0, 10); + /* properties */ + RNA_def_int(ot->srna, "type", 0, 0, INT_MAX, "Bone Group Index", "", 0, 10); } - static int pose_group_unassign_exec(bContext *C, wmOperator *UNUSED(op)) { - Object *ob = ED_pose_object_from_context(C); - bool done = false; - - /* only continue if there's an object, and a pose there too */ - if (ELEM(NULL, ob, ob->pose)) - return OPERATOR_CANCELLED; - - /* find selected bones to remove from all bone groups */ - FOREACH_PCHAN_SELECTED_IN_OBJECT_BEGIN (ob, pchan) - { - if (pchan->agrp_index) { - pchan->agrp_index = 0; - done = true; - } - } - FOREACH_PCHAN_SELECTED_IN_OBJECT_END; - - /* notifiers for updates */ - WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); - DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE); - - /* report done status */ - if (done) - return OPERATOR_FINISHED; - else - return OPERATOR_CANCELLED; + Object *ob = ED_pose_object_from_context(C); + bool done = false; + + /* only continue if there's an object, and a pose there too */ + if (ELEM(NULL, ob, ob->pose)) + return OPERATOR_CANCELLED; + + /* find selected bones to remove from all bone groups */ + FOREACH_PCHAN_SELECTED_IN_OBJECT_BEGIN (ob, pchan) { + if (pchan->agrp_index) { + pchan->agrp_index = 0; + done = true; + } + } + FOREACH_PCHAN_SELECTED_IN_OBJECT_END; + + /* notifiers for updates */ + WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); + DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE); + + /* report done status */ + if (done) + return OPERATOR_FINISHED; + else + return OPERATOR_CANCELLED; } void POSE_OT_group_unassign(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Remove Selected from Bone Groups"; - ot->idname = "POSE_OT_group_unassign"; - ot->description = "Remove selected bones from all bone groups"; + /* identifiers */ + ot->name = "Remove Selected from Bone Groups"; + ot->idname = "POSE_OT_group_unassign"; + ot->description = "Remove selected bones from all bone groups"; - /* api callbacks */ - ot->exec = pose_group_unassign_exec; - ot->poll = ED_operator_posemode_context; + /* api callbacks */ + ot->exec = pose_group_unassign_exec; + ot->poll = ED_operator_posemode_context; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } static int group_move_exec(bContext *C, wmOperator *op) { - Object *ob = ED_pose_object_from_context(C); - bPose *pose = (ob) ? ob->pose : NULL; - bPoseChannel *pchan; - bActionGroup *grp; - int dir = RNA_enum_get(op->ptr, "direction"); - - if (ELEM(NULL, ob, pose)) - return OPERATOR_CANCELLED; - if (pose->active_group <= 0) - return OPERATOR_CANCELLED; - - /* get group to move */ - grp = BLI_findlink(&pose->agroups, pose->active_group - 1); - if (grp == NULL) - return OPERATOR_CANCELLED; - - /* move bone group */ - if (BLI_listbase_link_move(&pose->agroups, grp, dir)) { - int grpIndexA = pose->active_group; - int grpIndexB = grpIndexA + dir; - - pose->active_group += dir; - /* fix changed bone group indices in bones (swap grpIndexA with grpIndexB) */ - for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { - if (pchan->agrp_index == grpIndexB) { - pchan->agrp_index = grpIndexA; - } - else if (pchan->agrp_index == grpIndexA) { - pchan->agrp_index = grpIndexB; - } - } - - /* notifiers for updates */ - WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); - } - - return OPERATOR_FINISHED; + Object *ob = ED_pose_object_from_context(C); + bPose *pose = (ob) ? ob->pose : NULL; + bPoseChannel *pchan; + bActionGroup *grp; + int dir = RNA_enum_get(op->ptr, "direction"); + + if (ELEM(NULL, ob, pose)) + return OPERATOR_CANCELLED; + if (pose->active_group <= 0) + return OPERATOR_CANCELLED; + + /* get group to move */ + grp = BLI_findlink(&pose->agroups, pose->active_group - 1); + if (grp == NULL) + return OPERATOR_CANCELLED; + + /* move bone group */ + if (BLI_listbase_link_move(&pose->agroups, grp, dir)) { + int grpIndexA = pose->active_group; + int grpIndexB = grpIndexA + dir; + + pose->active_group += dir; + /* fix changed bone group indices in bones (swap grpIndexA with grpIndexB) */ + for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { + if (pchan->agrp_index == grpIndexB) { + pchan->agrp_index = grpIndexA; + } + else if (pchan->agrp_index == grpIndexA) { + pchan->agrp_index = grpIndexB; + } + } + + /* notifiers for updates */ + WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); + } + + return OPERATOR_FINISHED; } void POSE_OT_group_move(wmOperatorType *ot) { - static const EnumPropertyItem group_slot_move[] = { - {-1, "UP", 0, "Up", ""}, - {1, "DOWN", 0, "Down", ""}, - {0, NULL, 0, NULL, NULL}, - }; - - /* identifiers */ - ot->name = "Move Bone Group"; - ot->idname = "POSE_OT_group_move"; - ot->description = "Change position of active Bone Group in list of Bone Groups"; - - /* api callbacks */ - ot->exec = group_move_exec; - ot->poll = ED_operator_posemode_context; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - RNA_def_enum(ot->srna, "direction", group_slot_move, 0, "Direction", - "Direction to move the active Bone Group towards"); + static const EnumPropertyItem group_slot_move[] = { + {-1, "UP", 0, "Up", ""}, + {1, "DOWN", 0, "Down", ""}, + {0, NULL, 0, NULL, NULL}, + }; + + /* identifiers */ + ot->name = "Move Bone Group"; + ot->idname = "POSE_OT_group_move"; + ot->description = "Change position of active Bone Group in list of Bone Groups"; + + /* api callbacks */ + ot->exec = group_move_exec; + ot->poll = ED_operator_posemode_context; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + RNA_def_enum(ot->srna, + "direction", + group_slot_move, + 0, + "Direction", + "Direction to move the active Bone Group towards"); } /* bone group sort element */ typedef struct tSortActionGroup { - bActionGroup *agrp; - int index; + bActionGroup *agrp; + int index; } tSortActionGroup; /* compare bone groups by name */ static int compare_agroup(const void *sgrp_a_ptr, const void *sgrp_b_ptr) { - const tSortActionGroup *sgrp_a = sgrp_a_ptr; - const tSortActionGroup *sgrp_b = sgrp_b_ptr; + const tSortActionGroup *sgrp_a = sgrp_a_ptr; + const tSortActionGroup *sgrp_b = sgrp_b_ptr; - return strcmp(sgrp_a->agrp->name, sgrp_b->agrp->name); + return strcmp(sgrp_a->agrp->name, sgrp_b->agrp->name); } static int group_sort_exec(bContext *C, wmOperator *UNUSED(op)) { - Object *ob = ED_pose_object_from_context(C); - bPose *pose = (ob) ? ob->pose : NULL; - bPoseChannel *pchan; - tSortActionGroup *agrp_array; - bActionGroup *agrp; - int agrp_count; - int i; - - if (ELEM(NULL, ob, pose)) - return OPERATOR_CANCELLED; - if (pose->active_group <= 0) - return OPERATOR_CANCELLED; - - /* create temporary array with bone groups and indices */ - agrp_count = BLI_listbase_count(&pose->agroups); - agrp_array = MEM_mallocN(sizeof(tSortActionGroup) * agrp_count, "sort bone groups"); - for (agrp = pose->agroups.first, i = 0; agrp; agrp = agrp->next, i++) { - BLI_assert(i < agrp_count); - agrp_array[i].agrp = agrp; - agrp_array[i].index = i + 1; - } - - /* sort bone groups by name */ - qsort(agrp_array, agrp_count, sizeof(tSortActionGroup), compare_agroup); - - /* create sorted bone group list from sorted array */ - BLI_listbase_clear(&pose->agroups); - for (i = 0; i < agrp_count; i++) { - BLI_addtail(&pose->agroups, agrp_array[i].agrp); - } - - /* fix changed bone group indizes in bones */ - for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { - for (i = 0; i < agrp_count; i++) { - if (pchan->agrp_index == agrp_array[i].index) { - pchan->agrp_index = i + 1; - break; - } - } - } - - /* free temp resources */ - MEM_freeN(agrp_array); - - /* notifiers for updates */ - WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); - DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE); - - return OPERATOR_FINISHED; + Object *ob = ED_pose_object_from_context(C); + bPose *pose = (ob) ? ob->pose : NULL; + bPoseChannel *pchan; + tSortActionGroup *agrp_array; + bActionGroup *agrp; + int agrp_count; + int i; + + if (ELEM(NULL, ob, pose)) + return OPERATOR_CANCELLED; + if (pose->active_group <= 0) + return OPERATOR_CANCELLED; + + /* create temporary array with bone groups and indices */ + agrp_count = BLI_listbase_count(&pose->agroups); + agrp_array = MEM_mallocN(sizeof(tSortActionGroup) * agrp_count, "sort bone groups"); + for (agrp = pose->agroups.first, i = 0; agrp; agrp = agrp->next, i++) { + BLI_assert(i < agrp_count); + agrp_array[i].agrp = agrp; + agrp_array[i].index = i + 1; + } + + /* sort bone groups by name */ + qsort(agrp_array, agrp_count, sizeof(tSortActionGroup), compare_agroup); + + /* create sorted bone group list from sorted array */ + BLI_listbase_clear(&pose->agroups); + for (i = 0; i < agrp_count; i++) { + BLI_addtail(&pose->agroups, agrp_array[i].agrp); + } + + /* fix changed bone group indizes in bones */ + for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { + for (i = 0; i < agrp_count; i++) { + if (pchan->agrp_index == agrp_array[i].index) { + pchan->agrp_index = i + 1; + break; + } + } + } + + /* free temp resources */ + MEM_freeN(agrp_array); + + /* notifiers for updates */ + WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); + DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE); + + return OPERATOR_FINISHED; } void POSE_OT_group_sort(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Sort Bone Groups"; - ot->idname = "POSE_OT_group_sort"; - ot->description = "Sort Bone Groups by their names in ascending order"; + /* identifiers */ + ot->name = "Sort Bone Groups"; + ot->idname = "POSE_OT_group_sort"; + ot->description = "Sort Bone Groups by their names in ascending order"; - /* api callbacks */ - ot->exec = group_sort_exec; - ot->poll = ED_operator_posemode_context; + /* api callbacks */ + ot->exec = group_sort_exec; + ot->poll = ED_operator_posemode_context; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } static void pose_group_select(Object *ob, bool select) { - bPose *pose = ob->pose; - - FOREACH_PCHAN_VISIBLE_IN_OBJECT_BEGIN (ob, pchan) - { - if ((pchan->bone->flag & BONE_UNSELECTABLE) == 0) { - if (select) { - if (pchan->agrp_index == pose->active_group) - pchan->bone->flag |= BONE_SELECTED; - } - else { - if (pchan->agrp_index == pose->active_group) - pchan->bone->flag &= ~BONE_SELECTED; - } - } - } - FOREACH_PCHAN_VISIBLE_IN_OBJECT_END; + bPose *pose = ob->pose; + + FOREACH_PCHAN_VISIBLE_IN_OBJECT_BEGIN (ob, pchan) { + if ((pchan->bone->flag & BONE_UNSELECTABLE) == 0) { + if (select) { + if (pchan->agrp_index == pose->active_group) + pchan->bone->flag |= BONE_SELECTED; + } + else { + if (pchan->agrp_index == pose->active_group) + pchan->bone->flag &= ~BONE_SELECTED; + } + } + } + FOREACH_PCHAN_VISIBLE_IN_OBJECT_END; } static int pose_group_select_exec(bContext *C, wmOperator *UNUSED(op)) { - Object *ob = ED_pose_object_from_context(C); + Object *ob = ED_pose_object_from_context(C); - /* only continue if there's an object, and a pose there too */ - if (ELEM(NULL, ob, ob->pose)) - return OPERATOR_CANCELLED; + /* only continue if there's an object, and a pose there too */ + if (ELEM(NULL, ob, ob->pose)) + return OPERATOR_CANCELLED; - pose_group_select(ob, 1); + pose_group_select(ob, 1); - /* notifiers for updates */ - bArmature *arm = ob->data; - DEG_id_tag_update(&arm->id, ID_RECALC_SELECT); - WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); + /* notifiers for updates */ + bArmature *arm = ob->data; + DEG_id_tag_update(&arm->id, ID_RECALC_SELECT); + WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); - return OPERATOR_FINISHED; + return OPERATOR_FINISHED; } void POSE_OT_group_select(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Select Bones of Bone Group"; - ot->idname = "POSE_OT_group_select"; - ot->description = "Select bones in active Bone Group"; + /* identifiers */ + ot->name = "Select Bones of Bone Group"; + ot->idname = "POSE_OT_group_select"; + ot->description = "Select bones in active Bone Group"; - /* api callbacks */ - ot->exec = pose_group_select_exec; - ot->poll = ED_operator_posemode_context; + /* api callbacks */ + ot->exec = pose_group_select_exec; + ot->poll = ED_operator_posemode_context; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } static int pose_group_deselect_exec(bContext *C, wmOperator *UNUSED(op)) { - Object *ob = ED_pose_object_from_context(C); + Object *ob = ED_pose_object_from_context(C); - /* only continue if there's an object, and a pose there too */ - if (ELEM(NULL, ob, ob->pose)) - return OPERATOR_CANCELLED; + /* only continue if there's an object, and a pose there too */ + if (ELEM(NULL, ob, ob->pose)) + return OPERATOR_CANCELLED; - pose_group_select(ob, 0); + pose_group_select(ob, 0); - /* notifiers for updates */ - bArmature *arm = ob->data; - DEG_id_tag_update(&arm->id, ID_RECALC_SELECT); - WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); + /* notifiers for updates */ + bArmature *arm = ob->data; + DEG_id_tag_update(&arm->id, ID_RECALC_SELECT); + WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); - return OPERATOR_FINISHED; + return OPERATOR_FINISHED; } void POSE_OT_group_deselect(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Deselect Bone Group"; - ot->idname = "POSE_OT_group_deselect"; - ot->description = "Deselect bones of active Bone Group"; + /* identifiers */ + ot->name = "Deselect Bone Group"; + ot->idname = "POSE_OT_group_deselect"; + ot->description = "Deselect bones of active Bone Group"; - /* api callbacks */ - ot->exec = pose_group_deselect_exec; - ot->poll = ED_operator_posemode_context; + /* api callbacks */ + ot->exec = pose_group_deselect_exec; + ot->poll = ED_operator_posemode_context; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } /* ********************************************** */ diff --git a/source/blender/editors/armature/pose_lib.c b/source/blender/editors/armature/pose_lib.c index 41df3086fa8..443bcdde028 100644 --- a/source/blender/editors/armature/pose_lib.c +++ b/source/blender/editors/armature/pose_lib.c @@ -72,7 +72,9 @@ /* ******* XXX ********** */ -static void action_set_activemarker(void *UNUSED(a), void *UNUSED(b), void *UNUSED(c)) {} +static void action_set_activemarker(void *UNUSED(a), void *UNUSED(b), void *UNUSED(c)) +{ +} /* ************************************************************* */ /* == POSE-LIBRARY TOOL FOR BLENDER == @@ -93,84 +95,84 @@ static void action_set_activemarker(void *UNUSED(a), void *UNUSED(b), void *UNUS */ /* ************************************************************* */ - /* gets the first available frame in poselib to store a pose on * - frames start from 1, and a pose should occur on every frame... 0 is error! */ static int poselib_get_free_index(bAction *act) { - TimeMarker *marker; - int low = 0, high = 0; - bool changed = false; - - /* sanity checks */ - if (ELEM(NULL, act, act->markers.first)) return 1; - - /* As poses are not stored in chronological order, we must iterate over this list - * a few times until we don't make any new discoveries (mostly about the lower bound). - * Prevents problems with deleting then trying to add new poses [#27412] - */ - do { - changed = false; - - for (marker = act->markers.first; marker; marker = marker->next) { - /* only increase low if value is 1 greater than low, to find "gaps" where - * poses were removed from the poselib - */ - if (marker->frame == (low + 1)) { - low++; - changed = true; - } - - /* value replaces high if it is the highest value encountered yet */ - if (marker->frame > high) { - high = marker->frame; - changed = true; - } - } - } while (changed != 0); - - /* - if low is not equal to high, then low+1 is a gap - * - if low is equal to high, then high+1 is the next index (add at end) - */ - if (low < high) - return (low + 1); - else - return (high + 1); + TimeMarker *marker; + int low = 0, high = 0; + bool changed = false; + + /* sanity checks */ + if (ELEM(NULL, act, act->markers.first)) + return 1; + + /* As poses are not stored in chronological order, we must iterate over this list + * a few times until we don't make any new discoveries (mostly about the lower bound). + * Prevents problems with deleting then trying to add new poses [#27412] + */ + do { + changed = false; + + for (marker = act->markers.first; marker; marker = marker->next) { + /* only increase low if value is 1 greater than low, to find "gaps" where + * poses were removed from the poselib + */ + if (marker->frame == (low + 1)) { + low++; + changed = true; + } + + /* value replaces high if it is the highest value encountered yet */ + if (marker->frame > high) { + high = marker->frame; + changed = true; + } + } + } while (changed != 0); + + /* - if low is not equal to high, then low+1 is a gap + * - if low is equal to high, then high+1 is the next index (add at end) + */ + if (low < high) + return (low + 1); + else + return (high + 1); } /* returns the active pose for a poselib */ static TimeMarker *poselib_get_active_pose(bAction *act) { - if ((act) && (act->active_marker)) - return BLI_findlink(&act->markers, act->active_marker - 1); - else - return NULL; + if ((act) && (act->active_marker)) + return BLI_findlink(&act->markers, act->active_marker - 1); + else + return NULL; } /* Get object that Pose Lib should be found on */ /* XXX C can be zero */ static Object *get_poselib_object(bContext *C) { - ScrArea *sa; + ScrArea *sa; - /* sanity check */ - if (C == NULL) - return NULL; + /* sanity check */ + if (C == NULL) + return NULL; - sa = CTX_wm_area(C); + sa = CTX_wm_area(C); - if (sa && (sa->spacetype == SPACE_PROPERTIES)) - return ED_object_context(C); - else - return BKE_object_pose_armature_get(CTX_data_active_object(C)); + if (sa && (sa->spacetype == SPACE_PROPERTIES)) + return ED_object_context(C); + else + return BKE_object_pose_armature_get(CTX_data_active_object(C)); } /* Poll callback for operators that require existing PoseLib data (with poses) to work */ static bool has_poselib_pose_data_poll(bContext *C) { - Object *ob = get_poselib_object(C); - return (ob && ob->poselib); + Object *ob = get_poselib_object(C); + return (ob && ob->poselib); } /* Poll callback for operators that require existing PoseLib data (with poses) @@ -178,8 +180,8 @@ static bool has_poselib_pose_data_poll(bContext *C) */ static bool has_poselib_pose_data_for_editing_poll(bContext *C) { - Object *ob = get_poselib_object(C); - return (ob && ob->poselib && !ID_IS_LINKED(ob->poselib)); + Object *ob = get_poselib_object(C); + return (ob && ob->poselib && !ID_IS_LINKED(ob->poselib)); } /* ----------------------------------- */ @@ -187,29 +189,29 @@ static bool has_poselib_pose_data_for_editing_poll(bContext *C) /* Initialize a new poselib (whether it is needed or not) */ static bAction *poselib_init_new(Main *bmain, Object *ob) { - /* sanity checks - only for armatures */ - if (ELEM(NULL, ob, ob->pose)) - return NULL; + /* sanity checks - only for armatures */ + if (ELEM(NULL, ob, ob->pose)) + return NULL; - /* init object's poselib action (unlink old one if there) */ - if (ob->poselib) - id_us_min(&ob->poselib->id); + /* init object's poselib action (unlink old one if there) */ + if (ob->poselib) + id_us_min(&ob->poselib->id); - ob->poselib = BKE_action_add(bmain, "PoseLib"); - ob->poselib->idroot = ID_OB; + ob->poselib = BKE_action_add(bmain, "PoseLib"); + ob->poselib->idroot = ID_OB; - return ob->poselib; + return ob->poselib; } /* Initialize a new poselib (checks if that needs to happen) */ static bAction *poselib_validate(Main *bmain, Object *ob) { - if (ELEM(NULL, ob, ob->pose)) - return NULL; - else if (ob->poselib == NULL) - return poselib_init_new(bmain, ob); - else - return ob->poselib; + if (ELEM(NULL, ob, ob->pose)) + return NULL; + else if (ob->poselib == NULL) + return poselib_init_new(bmain, ob); + else + return ob->poselib; } /* ************************************************************* */ @@ -217,70 +219,70 @@ static bAction *poselib_validate(Main *bmain, Object *ob) static int poselib_new_exec(bContext *C, wmOperator *UNUSED(op)) { - Main *bmain = CTX_data_main(C); - Object *ob = get_poselib_object(C); + Main *bmain = CTX_data_main(C); + Object *ob = get_poselib_object(C); - /* sanity checks */ - if (ob == NULL) - return OPERATOR_CANCELLED; + /* sanity checks */ + if (ob == NULL) + return OPERATOR_CANCELLED; - /* new method here deals with the rest... */ - poselib_init_new(bmain, ob); + /* new method here deals with the rest... */ + poselib_init_new(bmain, ob); - /* notifier here might evolve? */ - WM_event_add_notifier(C, NC_OBJECT | ND_POSE, NULL); + /* notifier here might evolve? */ + WM_event_add_notifier(C, NC_OBJECT | ND_POSE, NULL); - return OPERATOR_FINISHED; + return OPERATOR_FINISHED; } void POSELIB_OT_new(wmOperatorType *ot) { - /* identifiers */ - ot->name = "New Pose Library"; - ot->idname = "POSELIB_OT_new"; - ot->description = "Add New Pose Library to active Object"; + /* identifiers */ + ot->name = "New Pose Library"; + ot->idname = "POSELIB_OT_new"; + ot->description = "Add New Pose Library to active Object"; - /* callbacks */ - ot->exec = poselib_new_exec; - ot->poll = ED_operator_posemode; + /* callbacks */ + ot->exec = poselib_new_exec; + ot->poll = ED_operator_posemode; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } /* ------------------------------------------------ */ static int poselib_unlink_exec(bContext *C, wmOperator *UNUSED(op)) { - Object *ob = get_poselib_object(C); + Object *ob = get_poselib_object(C); - /* sanity checks */ - if (ELEM(NULL, ob, ob->poselib)) - return OPERATOR_CANCELLED; + /* sanity checks */ + if (ELEM(NULL, ob, ob->poselib)) + return OPERATOR_CANCELLED; - /* there should be a poselib (we just checked above!), so just lower its user count and remove */ - id_us_min(&ob->poselib->id); - ob->poselib = NULL; + /* there should be a poselib (we just checked above!), so just lower its user count and remove */ + id_us_min(&ob->poselib->id); + ob->poselib = NULL; - /* notifier here might evolve? */ - WM_event_add_notifier(C, NC_OBJECT | ND_POSE, NULL); + /* notifier here might evolve? */ + WM_event_add_notifier(C, NC_OBJECT | ND_POSE, NULL); - return OPERATOR_FINISHED; + return OPERATOR_FINISHED; } void POSELIB_OT_unlink(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Unlink Pose Library"; - ot->idname = "POSELIB_OT_unlink"; - ot->description = "Remove Pose Library from active Object"; + /* identifiers */ + ot->name = "Unlink Pose Library"; + ot->idname = "POSELIB_OT_unlink"; + ot->description = "Remove Pose Library from active Object"; - /* callbacks */ - ot->exec = poselib_unlink_exec; - ot->poll = has_poselib_pose_data_poll; + /* callbacks */ + ot->exec = poselib_unlink_exec; + ot->poll = has_poselib_pose_data_poll; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } /* ************************************************************* */ @@ -291,81 +293,81 @@ void POSELIB_OT_unlink(wmOperatorType *ot) */ static int poselib_sanitize_exec(bContext *C, wmOperator *op) { - Object *ob = get_poselib_object(C); - bAction *act = (ob) ? ob->poselib : NULL; - DLRBT_Tree keys; - ActKeyColumn *ak; - TimeMarker *marker, *markern; - - /* validate action */ - if (act == NULL) { - BKE_report(op->reports, RPT_WARNING, "No action to validate"); - return OPERATOR_CANCELLED; - } - - /* determine which frames have keys */ - BLI_dlrbTree_init(&keys); - action_to_keylist(NULL, act, &keys, 0); - - /* for each key, make sure there is a corresponding pose */ - for (ak = keys.first; ak; ak = ak->next) { - /* check if any pose matches this */ - /* TODO: don't go looking through the list like this every time... */ - for (marker = act->markers.first; marker; marker = marker->next) { - if (IS_EQ((double)marker->frame, (double)ak->cfra)) { - marker->flag = -1; - break; - } - } - - /* add new if none found */ - if (marker == NULL) { - /* add pose to poselib */ - marker = MEM_callocN(sizeof(TimeMarker), "ActionMarker"); - - BLI_snprintf(marker->name, sizeof(marker->name), "F%d Pose", (int)ak->cfra); - - marker->frame = (int)ak->cfra; - marker->flag = -1; - - BLI_addtail(&act->markers, marker); - } - } - - /* remove all untagged poses (unused), and remove all tags */ - for (marker = act->markers.first; marker; marker = markern) { - markern = marker->next; - - if (marker->flag != -1) - BLI_freelinkN(&act->markers, marker); - else - marker->flag = 0; - } - - /* free temp memory */ - BLI_dlrbTree_free(&keys); - - /* send notifiers for this - using keyframe editing notifiers, since action - * may be being shown in anim editors as active action - */ - WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); - - return OPERATOR_FINISHED; + Object *ob = get_poselib_object(C); + bAction *act = (ob) ? ob->poselib : NULL; + DLRBT_Tree keys; + ActKeyColumn *ak; + TimeMarker *marker, *markern; + + /* validate action */ + if (act == NULL) { + BKE_report(op->reports, RPT_WARNING, "No action to validate"); + return OPERATOR_CANCELLED; + } + + /* determine which frames have keys */ + BLI_dlrbTree_init(&keys); + action_to_keylist(NULL, act, &keys, 0); + + /* for each key, make sure there is a corresponding pose */ + for (ak = keys.first; ak; ak = ak->next) { + /* check if any pose matches this */ + /* TODO: don't go looking through the list like this every time... */ + for (marker = act->markers.first; marker; marker = marker->next) { + if (IS_EQ((double)marker->frame, (double)ak->cfra)) { + marker->flag = -1; + break; + } + } + + /* add new if none found */ + if (marker == NULL) { + /* add pose to poselib */ + marker = MEM_callocN(sizeof(TimeMarker), "ActionMarker"); + + BLI_snprintf(marker->name, sizeof(marker->name), "F%d Pose", (int)ak->cfra); + + marker->frame = (int)ak->cfra; + marker->flag = -1; + + BLI_addtail(&act->markers, marker); + } + } + + /* remove all untagged poses (unused), and remove all tags */ + for (marker = act->markers.first; marker; marker = markern) { + markern = marker->next; + + if (marker->flag != -1) + BLI_freelinkN(&act->markers, marker); + else + marker->flag = 0; + } + + /* free temp memory */ + BLI_dlrbTree_free(&keys); + + /* send notifiers for this - using keyframe editing notifiers, since action + * may be being shown in anim editors as active action + */ + WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); + + return OPERATOR_FINISHED; } void POSELIB_OT_action_sanitize(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Sanitize Pose Library Action"; - ot->idname = "POSELIB_OT_action_sanitize"; - ot->description = "Make action suitable for use as a Pose Library"; + /* identifiers */ + ot->name = "Sanitize Pose Library Action"; + ot->idname = "POSELIB_OT_action_sanitize"; + ot->description = "Make action suitable for use as a Pose Library"; - /* callbacks */ - ot->exec = poselib_sanitize_exec; - ot->poll = has_poselib_pose_data_for_editing_poll; + /* callbacks */ + ot->exec = poselib_sanitize_exec; + ot->poll = has_poselib_pose_data_for_editing_poll; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } /* ------------------------------------------ */ @@ -373,615 +375,631 @@ void POSELIB_OT_action_sanitize(wmOperatorType *ot) /* Poll callback for adding poses to a PoseLib */ static bool poselib_add_poll(bContext *C) { - /* There are 2 cases we need to be careful with: - * 1) When this operator is invoked from a hotkey, there may be no PoseLib yet - * 2) If a PoseLib already exists, we can't edit the action if it is a lib-linked - * actions, as data will be lost when saving the file - */ - if (ED_operator_posemode(C)) { - Object *ob = get_poselib_object(C); - if (ob) { - if ((ob->poselib == NULL) || !ID_IS_LINKED(ob->poselib)) { - return true; - } - } - } - return false; + /* There are 2 cases we need to be careful with: + * 1) When this operator is invoked from a hotkey, there may be no PoseLib yet + * 2) If a PoseLib already exists, we can't edit the action if it is a lib-linked + * actions, as data will be lost when saving the file + */ + if (ED_operator_posemode(C)) { + Object *ob = get_poselib_object(C); + if (ob) { + if ((ob->poselib == NULL) || !ID_IS_LINKED(ob->poselib)) { + return true; + } + } + } + return false; } static void poselib_add_menu_invoke__replacemenu(bContext *C, uiLayout *layout, void *UNUSED(arg)) { - Object *ob = get_poselib_object(C); - bAction *act = ob->poselib; /* never NULL */ - TimeMarker *marker; - - wmOperatorType *ot = WM_operatortype_find("POSELIB_OT_pose_add", 1); - - BLI_assert(ot != NULL); - - /* set the operator execution context correctly */ - uiLayoutSetOperatorContext(layout, WM_OP_EXEC_DEFAULT); - - /* add each marker to this menu */ - for (marker = act->markers.first; marker; marker = marker->next) { - PointerRNA props_ptr; - uiItemFullO_ptr( - layout, ot, - marker->name, ICON_ARMATURE_DATA, NULL, - WM_OP_EXEC_DEFAULT, 0, &props_ptr); - RNA_int_set(&props_ptr, "frame", marker->frame); - RNA_string_set(&props_ptr, "name", marker->name); - } -} - -static int poselib_add_menu_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) -{ - Scene *scene = CTX_data_scene(C); - Object *ob = get_poselib_object(C); - bPose *pose = (ob) ? ob->pose : NULL; - uiPopupMenu *pup; - uiLayout *layout; - - /* sanity check */ - if (ELEM(NULL, ob, pose)) - return OPERATOR_CANCELLED; - - /* start building */ - pup = UI_popup_menu_begin(C, op->type->name, ICON_NONE); - layout = UI_popup_menu_layout(pup); - uiLayoutSetOperatorContext(layout, WM_OP_EXEC_DEFAULT); + Object *ob = get_poselib_object(C); + bAction *act = ob->poselib; /* never NULL */ + TimeMarker *marker; - /* add new (adds to the first unoccupied frame) */ - uiItemIntO(layout, IFACE_("Add New"), ICON_NONE, "POSELIB_OT_pose_add", "frame", poselib_get_free_index(ob->poselib)); + wmOperatorType *ot = WM_operatortype_find("POSELIB_OT_pose_add", 1); - /* check if we have any choices to add a new pose in any other way */ - if ((ob->poselib) && (ob->poselib->markers.first)) { - /* add new (on current frame) */ - uiItemIntO(layout, IFACE_("Add New (Current Frame)"), ICON_NONE, "POSELIB_OT_pose_add", "frame", CFRA); + BLI_assert(ot != NULL); - /* replace existing - submenu */ - uiItemMenuF(layout, IFACE_("Replace Existing..."), 0, poselib_add_menu_invoke__replacemenu, NULL); - } + /* set the operator execution context correctly */ + uiLayoutSetOperatorContext(layout, WM_OP_EXEC_DEFAULT); - UI_popup_menu_end(C, pup); - - /* this operator is only for a menu, not used further */ - return OPERATOR_INTERFACE; + /* add each marker to this menu */ + for (marker = act->markers.first; marker; marker = marker->next) { + PointerRNA props_ptr; + uiItemFullO_ptr( + layout, ot, marker->name, ICON_ARMATURE_DATA, NULL, WM_OP_EXEC_DEFAULT, 0, &props_ptr); + RNA_int_set(&props_ptr, "frame", marker->frame); + RNA_string_set(&props_ptr, "name", marker->name); + } } +static int poselib_add_menu_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + Scene *scene = CTX_data_scene(C); + Object *ob = get_poselib_object(C); + bPose *pose = (ob) ? ob->pose : NULL; + uiPopupMenu *pup; + uiLayout *layout; + + /* sanity check */ + if (ELEM(NULL, ob, pose)) + return OPERATOR_CANCELLED; + + /* start building */ + pup = UI_popup_menu_begin(C, op->type->name, ICON_NONE); + layout = UI_popup_menu_layout(pup); + uiLayoutSetOperatorContext(layout, WM_OP_EXEC_DEFAULT); + + /* add new (adds to the first unoccupied frame) */ + uiItemIntO(layout, + IFACE_("Add New"), + ICON_NONE, + "POSELIB_OT_pose_add", + "frame", + poselib_get_free_index(ob->poselib)); + + /* check if we have any choices to add a new pose in any other way */ + if ((ob->poselib) && (ob->poselib->markers.first)) { + /* add new (on current frame) */ + uiItemIntO(layout, + IFACE_("Add New (Current Frame)"), + ICON_NONE, + "POSELIB_OT_pose_add", + "frame", + CFRA); + + /* replace existing - submenu */ + uiItemMenuF( + layout, IFACE_("Replace Existing..."), 0, poselib_add_menu_invoke__replacemenu, NULL); + } + + UI_popup_menu_end(C, pup); + + /* this operator is only for a menu, not used further */ + return OPERATOR_INTERFACE; +} static int poselib_add_exec(bContext *C, wmOperator *op) { - Main *bmain = CTX_data_main(C); - Object *ob = get_poselib_object(C); - bAction *act = poselib_validate(bmain, ob); - bPose *pose = (ob) ? ob->pose : NULL; - TimeMarker *marker; - KeyingSet *ks; - int frame = RNA_int_get(op->ptr, "frame"); - char name[64]; - - /* sanity check (invoke should have checked this anyway) */ - if (ELEM(NULL, ob, pose)) - return OPERATOR_CANCELLED; - - /* get name to give to pose */ - RNA_string_get(op->ptr, "name", name); - - /* add pose to poselib - replaces any existing pose there - * - for the 'replace' option, this should end up finding the appropriate marker, - * so no new one will be added - */ - for (marker = act->markers.first; marker; marker = marker->next) { - if (marker->frame == frame) { - BLI_strncpy(marker->name, name, sizeof(marker->name)); - break; - } - } - if (marker == NULL) { - marker = MEM_callocN(sizeof(TimeMarker), "ActionMarker"); - - BLI_strncpy(marker->name, name, sizeof(marker->name)); - marker->frame = frame; - - BLI_addtail(&act->markers, marker); - } - - /* validate name */ - BLI_uniquename(&act->markers, marker, DATA_("Pose"), '.', offsetof(TimeMarker, name), sizeof(marker->name)); - - /* use Keying Set to determine what to store for the pose */ - - /* this includes custom props :)*/ - ks = ANIM_builtin_keyingset_get_named(NULL, ANIM_KS_WHOLE_CHARACTER_SELECTED_ID); - - ANIM_apply_keyingset(C, NULL, act, ks, MODIFYKEY_MODE_INSERT, (float)frame); - - /* store new 'active' pose number */ - act->active_marker = BLI_listbase_count(&act->markers); - DEG_id_tag_update(&act->id, ID_RECALC_COPY_ON_WRITE); - - /* done */ - return OPERATOR_FINISHED; + Main *bmain = CTX_data_main(C); + Object *ob = get_poselib_object(C); + bAction *act = poselib_validate(bmain, ob); + bPose *pose = (ob) ? ob->pose : NULL; + TimeMarker *marker; + KeyingSet *ks; + int frame = RNA_int_get(op->ptr, "frame"); + char name[64]; + + /* sanity check (invoke should have checked this anyway) */ + if (ELEM(NULL, ob, pose)) + return OPERATOR_CANCELLED; + + /* get name to give to pose */ + RNA_string_get(op->ptr, "name", name); + + /* add pose to poselib - replaces any existing pose there + * - for the 'replace' option, this should end up finding the appropriate marker, + * so no new one will be added + */ + for (marker = act->markers.first; marker; marker = marker->next) { + if (marker->frame == frame) { + BLI_strncpy(marker->name, name, sizeof(marker->name)); + break; + } + } + if (marker == NULL) { + marker = MEM_callocN(sizeof(TimeMarker), "ActionMarker"); + + BLI_strncpy(marker->name, name, sizeof(marker->name)); + marker->frame = frame; + + BLI_addtail(&act->markers, marker); + } + + /* validate name */ + BLI_uniquename( + &act->markers, marker, DATA_("Pose"), '.', offsetof(TimeMarker, name), sizeof(marker->name)); + + /* use Keying Set to determine what to store for the pose */ + + /* this includes custom props :)*/ + ks = ANIM_builtin_keyingset_get_named(NULL, ANIM_KS_WHOLE_CHARACTER_SELECTED_ID); + + ANIM_apply_keyingset(C, NULL, act, ks, MODIFYKEY_MODE_INSERT, (float)frame); + + /* store new 'active' pose number */ + act->active_marker = BLI_listbase_count(&act->markers); + DEG_id_tag_update(&act->id, ID_RECALC_COPY_ON_WRITE); + + /* done */ + return OPERATOR_FINISHED; } void POSELIB_OT_pose_add(wmOperatorType *ot) { - /* identifiers */ - ot->name = "PoseLib Add Pose"; - ot->idname = "POSELIB_OT_pose_add"; - ot->description = "Add the current Pose to the active Pose Library"; - - /* api callbacks */ - ot->invoke = poselib_add_menu_invoke; - ot->exec = poselib_add_exec; - ot->poll = poselib_add_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - RNA_def_int(ot->srna, "frame", 1, 0, INT_MAX, "Frame", "Frame to store pose on", 0, INT_MAX); - RNA_def_string(ot->srna, "name", "Pose", 64, "Pose Name", "Name of newly added Pose"); + /* identifiers */ + ot->name = "PoseLib Add Pose"; + ot->idname = "POSELIB_OT_pose_add"; + ot->description = "Add the current Pose to the active Pose Library"; + + /* api callbacks */ + ot->invoke = poselib_add_menu_invoke; + ot->exec = poselib_add_exec; + ot->poll = poselib_add_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + RNA_def_int(ot->srna, "frame", 1, 0, INT_MAX, "Frame", "Frame to store pose on", 0, INT_MAX); + RNA_def_string(ot->srna, "name", "Pose", 64, "Pose Name", "Name of newly added Pose"); } /* ----- */ /* can be called with C == NULL */ -static const EnumPropertyItem *poselib_stored_pose_itemf(bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free) +static const EnumPropertyItem *poselib_stored_pose_itemf(bContext *C, + PointerRNA *UNUSED(ptr), + PropertyRNA *UNUSED(prop), + bool *r_free) { - Object *ob = get_poselib_object(C); - bAction *act = (ob) ? ob->poselib : NULL; - TimeMarker *marker; - EnumPropertyItem *item = NULL, item_tmp = {0}; - int totitem = 0; - int i = 0; - - if (C == NULL) { - return DummyRNA_NULL_items; - } - - /* check that the action exists */ - if (act) { - /* add each marker to the list */ - for (marker = act->markers.first, i = 0; marker; marker = marker->next, i++) { - item_tmp.identifier = item_tmp.name = marker->name; - item_tmp.icon = ICON_ARMATURE_DATA; - item_tmp.value = i; - RNA_enum_item_add(&item, &totitem, &item_tmp); - } - } - - RNA_enum_item_end(&item, &totitem); - *r_free = true; - - return item; + Object *ob = get_poselib_object(C); + bAction *act = (ob) ? ob->poselib : NULL; + TimeMarker *marker; + EnumPropertyItem *item = NULL, item_tmp = {0}; + int totitem = 0; + int i = 0; + + if (C == NULL) { + return DummyRNA_NULL_items; + } + + /* check that the action exists */ + if (act) { + /* add each marker to the list */ + for (marker = act->markers.first, i = 0; marker; marker = marker->next, i++) { + item_tmp.identifier = item_tmp.name = marker->name; + item_tmp.icon = ICON_ARMATURE_DATA; + item_tmp.value = i; + RNA_enum_item_add(&item, &totitem, &item_tmp); + } + } + + RNA_enum_item_end(&item, &totitem); + *r_free = true; + + return item; } static int poselib_remove_exec(bContext *C, wmOperator *op) { - Object *ob = get_poselib_object(C); - bAction *act = (ob) ? ob->poselib : NULL; - TimeMarker *marker; - int marker_index; - FCurve *fcu; - PropertyRNA *prop; - - /* check if valid poselib */ - if (act == NULL) { - BKE_report(op->reports, RPT_ERROR, "Object does not have pose lib data"); - return OPERATOR_CANCELLED; - } - - prop = RNA_struct_find_property(op->ptr, "pose"); - if (RNA_property_is_set(op->ptr, prop)) { - marker_index = RNA_property_enum_get(op->ptr, prop); - } - else { - marker_index = act->active_marker - 1; - } - - /* get index (and pointer) of pose to remove */ - marker = BLI_findlink(&act->markers, marker_index); - if (marker == NULL) { - BKE_reportf(op->reports, RPT_ERROR, "Invalid pose specified %d", marker_index); - return OPERATOR_CANCELLED; - } - - /* remove relevant keyframes */ - for (fcu = act->curves.first; fcu; fcu = fcu->next) { - BezTriple *bezt; - unsigned int i; - - if (fcu->bezt) { - for (i = 0, bezt = fcu->bezt; i < fcu->totvert; i++, bezt++) { - /* check if remove */ - if (IS_EQF(bezt->vec[1][0], (float)marker->frame)) { - delete_fcurve_key(fcu, i, 1); - break; - } - } - } - } - - /* remove poselib from list */ - BLI_freelinkN(&act->markers, marker); - - /* fix active pose number */ - act->active_marker = 0; - - /* send notifiers for this - using keyframe editing notifiers, since action - * may be being shown in anim editors as active action - */ - WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); - DEG_id_tag_update(&act->id, ID_RECALC_COPY_ON_WRITE); - - /* done */ - return OPERATOR_FINISHED; + Object *ob = get_poselib_object(C); + bAction *act = (ob) ? ob->poselib : NULL; + TimeMarker *marker; + int marker_index; + FCurve *fcu; + PropertyRNA *prop; + + /* check if valid poselib */ + if (act == NULL) { + BKE_report(op->reports, RPT_ERROR, "Object does not have pose lib data"); + return OPERATOR_CANCELLED; + } + + prop = RNA_struct_find_property(op->ptr, "pose"); + if (RNA_property_is_set(op->ptr, prop)) { + marker_index = RNA_property_enum_get(op->ptr, prop); + } + else { + marker_index = act->active_marker - 1; + } + + /* get index (and pointer) of pose to remove */ + marker = BLI_findlink(&act->markers, marker_index); + if (marker == NULL) { + BKE_reportf(op->reports, RPT_ERROR, "Invalid pose specified %d", marker_index); + return OPERATOR_CANCELLED; + } + + /* remove relevant keyframes */ + for (fcu = act->curves.first; fcu; fcu = fcu->next) { + BezTriple *bezt; + unsigned int i; + + if (fcu->bezt) { + for (i = 0, bezt = fcu->bezt; i < fcu->totvert; i++, bezt++) { + /* check if remove */ + if (IS_EQF(bezt->vec[1][0], (float)marker->frame)) { + delete_fcurve_key(fcu, i, 1); + break; + } + } + } + } + + /* remove poselib from list */ + BLI_freelinkN(&act->markers, marker); + + /* fix active pose number */ + act->active_marker = 0; + + /* send notifiers for this - using keyframe editing notifiers, since action + * may be being shown in anim editors as active action + */ + WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); + DEG_id_tag_update(&act->id, ID_RECALC_COPY_ON_WRITE); + + /* done */ + return OPERATOR_FINISHED; } void POSELIB_OT_pose_remove(wmOperatorType *ot) { - PropertyRNA *prop; - - /* identifiers */ - ot->name = "PoseLib Remove Pose"; - ot->idname = "POSELIB_OT_pose_remove"; - ot->description = "Remove nth pose from the active Pose Library"; - - /* api callbacks */ - ot->invoke = WM_menu_invoke; - ot->exec = poselib_remove_exec; - ot->poll = has_poselib_pose_data_for_editing_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - prop = RNA_def_enum(ot->srna, "pose", DummyRNA_NULL_items, 0, "Pose", "The pose to remove"); - RNA_def_enum_funcs(prop, poselib_stored_pose_itemf); - RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE); - ot->prop = prop; + PropertyRNA *prop; + + /* identifiers */ + ot->name = "PoseLib Remove Pose"; + ot->idname = "POSELIB_OT_pose_remove"; + ot->description = "Remove nth pose from the active Pose Library"; + + /* api callbacks */ + ot->invoke = WM_menu_invoke; + ot->exec = poselib_remove_exec; + ot->poll = has_poselib_pose_data_for_editing_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + prop = RNA_def_enum(ot->srna, "pose", DummyRNA_NULL_items, 0, "Pose", "The pose to remove"); + RNA_def_enum_funcs(prop, poselib_stored_pose_itemf); + RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE); + ot->prop = prop; } static int poselib_rename_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - Object *ob = get_poselib_object(C); - bAction *act = (ob) ? ob->poselib : NULL; - TimeMarker *marker; - - /* check if valid poselib */ - if (act == NULL) { - BKE_report(op->reports, RPT_ERROR, "Object does not have pose lib data"); - return OPERATOR_CANCELLED; - } - - /* get index (and pointer) of pose to remove */ - marker = BLI_findlink(&act->markers, act->active_marker - 1); - if (marker == NULL) { - BKE_report(op->reports, RPT_ERROR, "Invalid index for pose"); - return OPERATOR_CANCELLED; - } - else { - /* use the existing name of the marker as the name, and use the active marker as the one to rename */ - RNA_enum_set(op->ptr, "pose", act->active_marker - 1); - RNA_string_set(op->ptr, "name", marker->name); - } - - /* part to sync with other similar operators... */ - return WM_operator_props_popup_confirm(C, op, event); + Object *ob = get_poselib_object(C); + bAction *act = (ob) ? ob->poselib : NULL; + TimeMarker *marker; + + /* check if valid poselib */ + if (act == NULL) { + BKE_report(op->reports, RPT_ERROR, "Object does not have pose lib data"); + return OPERATOR_CANCELLED; + } + + /* get index (and pointer) of pose to remove */ + marker = BLI_findlink(&act->markers, act->active_marker - 1); + if (marker == NULL) { + BKE_report(op->reports, RPT_ERROR, "Invalid index for pose"); + return OPERATOR_CANCELLED; + } + else { + /* use the existing name of the marker as the name, and use the active marker as the one to rename */ + RNA_enum_set(op->ptr, "pose", act->active_marker - 1); + RNA_string_set(op->ptr, "name", marker->name); + } + + /* part to sync with other similar operators... */ + return WM_operator_props_popup_confirm(C, op, event); } static int poselib_rename_exec(bContext *C, wmOperator *op) { - Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); - bAction *act = (ob) ? ob->poselib : NULL; - TimeMarker *marker; - char newname[64]; - - /* check if valid poselib */ - if (act == NULL) { - BKE_report(op->reports, RPT_ERROR, "Object does not have pose lib data"); - return OPERATOR_CANCELLED; - } - - /* get index (and pointer) of pose to remove */ - marker = BLI_findlink(&act->markers, RNA_enum_get(op->ptr, "pose")); - if (marker == NULL) { - BKE_report(op->reports, RPT_ERROR, "Invalid index for pose"); - return OPERATOR_CANCELLED; - } - - /* get new name */ - RNA_string_get(op->ptr, "name", newname); - - /* copy name and validate it */ - BLI_strncpy(marker->name, newname, sizeof(marker->name)); - BLI_uniquename(&act->markers, marker, DATA_("Pose"), '.', offsetof(TimeMarker, name), sizeof(marker->name)); - - /* send notifiers for this - using keyframe editing notifiers, since action - * may be being shown in anim editors as active action - */ - WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); - - /* done */ - return OPERATOR_FINISHED; + Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); + bAction *act = (ob) ? ob->poselib : NULL; + TimeMarker *marker; + char newname[64]; + + /* check if valid poselib */ + if (act == NULL) { + BKE_report(op->reports, RPT_ERROR, "Object does not have pose lib data"); + return OPERATOR_CANCELLED; + } + + /* get index (and pointer) of pose to remove */ + marker = BLI_findlink(&act->markers, RNA_enum_get(op->ptr, "pose")); + if (marker == NULL) { + BKE_report(op->reports, RPT_ERROR, "Invalid index for pose"); + return OPERATOR_CANCELLED; + } + + /* get new name */ + RNA_string_get(op->ptr, "name", newname); + + /* copy name and validate it */ + BLI_strncpy(marker->name, newname, sizeof(marker->name)); + BLI_uniquename( + &act->markers, marker, DATA_("Pose"), '.', offsetof(TimeMarker, name), sizeof(marker->name)); + + /* send notifiers for this - using keyframe editing notifiers, since action + * may be being shown in anim editors as active action + */ + WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); + + /* done */ + return OPERATOR_FINISHED; } void POSELIB_OT_pose_rename(wmOperatorType *ot) { - PropertyRNA *prop; - - /* identifiers */ - ot->name = "PoseLib Rename Pose"; - ot->idname = "POSELIB_OT_pose_rename"; - ot->description = "Rename specified pose from the active Pose Library"; - - /* api callbacks */ - ot->invoke = poselib_rename_invoke; - ot->exec = poselib_rename_exec; - ot->poll = has_poselib_pose_data_for_editing_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - /* NOTE: name not pose is the operator's "main" property, - * so that it will get activated in the popup for easy renaming */ - ot->prop = RNA_def_string(ot->srna, "name", "RenamedPose", 64, "New Pose Name", "New name for pose"); - prop = RNA_def_enum(ot->srna, "pose", DummyRNA_NULL_items, 0, "Pose", "The pose to rename"); - RNA_def_enum_funcs(prop, poselib_stored_pose_itemf); - RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE); + PropertyRNA *prop; + + /* identifiers */ + ot->name = "PoseLib Rename Pose"; + ot->idname = "POSELIB_OT_pose_rename"; + ot->description = "Rename specified pose from the active Pose Library"; + + /* api callbacks */ + ot->invoke = poselib_rename_invoke; + ot->exec = poselib_rename_exec; + ot->poll = has_poselib_pose_data_for_editing_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + /* NOTE: name not pose is the operator's "main" property, + * so that it will get activated in the popup for easy renaming */ + ot->prop = RNA_def_string( + ot->srna, "name", "RenamedPose", 64, "New Pose Name", "New name for pose"); + prop = RNA_def_enum(ot->srna, "pose", DummyRNA_NULL_items, 0, "Pose", "The pose to rename"); + RNA_def_enum_funcs(prop, poselib_stored_pose_itemf); + RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE); } static int poselib_move_exec(bContext *C, wmOperator *op) { - Object *ob = get_poselib_object(C); - bAction *act = (ob) ? ob->poselib : NULL; - TimeMarker *marker; - int marker_index; - int dir; - PropertyRNA *prop; - - /* check if valid poselib */ - if (act == NULL) { - BKE_report(op->reports, RPT_ERROR, "Object does not have pose lib data"); - return OPERATOR_CANCELLED; - } - - prop = RNA_struct_find_property(op->ptr, "pose"); - if (RNA_property_is_set(op->ptr, prop)) { - marker_index = RNA_property_enum_get(op->ptr, prop); - } - else { - marker_index = act->active_marker - 1; - } - - /* get index (and pointer) of pose to remove */ - marker = BLI_findlink(&act->markers, marker_index); - if (marker == NULL) { - BKE_reportf(op->reports, RPT_ERROR, "Invalid pose specified %d", marker_index); - return OPERATOR_CANCELLED; - } - - dir = RNA_enum_get(op->ptr, "direction"); - - /* move pose */ - if (BLI_listbase_link_move(&act->markers, marker, dir)) { - act->active_marker = marker_index + dir + 1; - - /* send notifiers for this - using keyframe editing notifiers, since action - * may be being shown in anim editors as active action - */ - WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); - } - else { - return OPERATOR_CANCELLED; - } - - /* done */ - return OPERATOR_FINISHED; + Object *ob = get_poselib_object(C); + bAction *act = (ob) ? ob->poselib : NULL; + TimeMarker *marker; + int marker_index; + int dir; + PropertyRNA *prop; + + /* check if valid poselib */ + if (act == NULL) { + BKE_report(op->reports, RPT_ERROR, "Object does not have pose lib data"); + return OPERATOR_CANCELLED; + } + + prop = RNA_struct_find_property(op->ptr, "pose"); + if (RNA_property_is_set(op->ptr, prop)) { + marker_index = RNA_property_enum_get(op->ptr, prop); + } + else { + marker_index = act->active_marker - 1; + } + + /* get index (and pointer) of pose to remove */ + marker = BLI_findlink(&act->markers, marker_index); + if (marker == NULL) { + BKE_reportf(op->reports, RPT_ERROR, "Invalid pose specified %d", marker_index); + return OPERATOR_CANCELLED; + } + + dir = RNA_enum_get(op->ptr, "direction"); + + /* move pose */ + if (BLI_listbase_link_move(&act->markers, marker, dir)) { + act->active_marker = marker_index + dir + 1; + + /* send notifiers for this - using keyframe editing notifiers, since action + * may be being shown in anim editors as active action + */ + WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); + } + else { + return OPERATOR_CANCELLED; + } + + /* done */ + return OPERATOR_FINISHED; } void POSELIB_OT_pose_move(wmOperatorType *ot) { - PropertyRNA *prop; - static const EnumPropertyItem pose_lib_pose_move[] = { - {-1, "UP", 0, "Up", ""}, - {1, "DOWN", 0, "Down", ""}, - {0, NULL, 0, NULL, NULL}, - }; - - /* identifiers */ - ot->name = "PoseLib Move Pose"; - ot->idname = "POSELIB_OT_pose_move"; - ot->description = "Move the pose up or down in the active Pose Library"; - - /* api callbacks */ - ot->invoke = WM_menu_invoke; - ot->exec = poselib_move_exec; - ot->poll = has_poselib_pose_data_for_editing_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - prop = RNA_def_enum(ot->srna, "pose", DummyRNA_NULL_items, 0, "Pose", "The pose to move"); - RNA_def_enum_funcs(prop, poselib_stored_pose_itemf); - RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE); - ot->prop = prop; - - RNA_def_enum(ot->srna, "direction", pose_lib_pose_move, 0, "Direction", - "Direction to move the chosen pose towards"); + PropertyRNA *prop; + static const EnumPropertyItem pose_lib_pose_move[] = { + {-1, "UP", 0, "Up", ""}, + {1, "DOWN", 0, "Down", ""}, + {0, NULL, 0, NULL, NULL}, + }; + + /* identifiers */ + ot->name = "PoseLib Move Pose"; + ot->idname = "POSELIB_OT_pose_move"; + ot->description = "Move the pose up or down in the active Pose Library"; + + /* api callbacks */ + ot->invoke = WM_menu_invoke; + ot->exec = poselib_move_exec; + ot->poll = has_poselib_pose_data_for_editing_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + prop = RNA_def_enum(ot->srna, "pose", DummyRNA_NULL_items, 0, "Pose", "The pose to move"); + RNA_def_enum_funcs(prop, poselib_stored_pose_itemf); + RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE); + ot->prop = prop; + + RNA_def_enum(ot->srna, + "direction", + pose_lib_pose_move, + 0, + "Direction", + "Direction to move the chosen pose towards"); } - - /* ************************************************************* */ /* Pose-Lib Browsing/Previewing Operator */ /* Simple struct for storing settings/data for use during PoseLib preview */ typedef struct tPoseLib_PreviewData { - /** tPoseLib_Backup structs for restoring poses. */ - ListBase backups; - /** LinkData structs storing list of poses which match the current search-string. */ - ListBase searchp; - - /** active scene. */ - Scene *scene; - /** active area. */ - ScrArea *sa; - - /** RNA-Pointer to Object 'ob' .*/ - PointerRNA rna_ptr; - /** object to work on. */ - Object *ob; - /** object's armature data. */ - bArmature *arm; - /** object's pose. */ - bPose *pose; - /** poselib to use. */ - bAction *act; - /** 'active' pose. */ - TimeMarker *marker; - - /** total number of elements to work on. */ - int totcount; - - /** state of main loop. */ - short state; - /** redraw/update settings during main loop. */ - short redraw; - /** flags for various settings. */ - short flag; - - /** position of cursor in searchstr (cursor occurs before the item at the nominated index) */ - short search_cursor; - /** (Part of) Name to search for to filter poses that get shown. */ - char searchstr[64]; - /** Previously set searchstr (from last loop run), - * so that we can detected when to rebuild searchp. */ - char searchold[64]; - - /** Info-text to print in header. */ - char headerstr[UI_MAX_DRAW_STR]; + /** tPoseLib_Backup structs for restoring poses. */ + ListBase backups; + /** LinkData structs storing list of poses which match the current search-string. */ + ListBase searchp; + + /** active scene. */ + Scene *scene; + /** active area. */ + ScrArea *sa; + + /** RNA-Pointer to Object 'ob' .*/ + PointerRNA rna_ptr; + /** object to work on. */ + Object *ob; + /** object's armature data. */ + bArmature *arm; + /** object's pose. */ + bPose *pose; + /** poselib to use. */ + bAction *act; + /** 'active' pose. */ + TimeMarker *marker; + + /** total number of elements to work on. */ + int totcount; + + /** state of main loop. */ + short state; + /** redraw/update settings during main loop. */ + short redraw; + /** flags for various settings. */ + short flag; + + /** position of cursor in searchstr (cursor occurs before the item at the nominated index) */ + short search_cursor; + /** (Part of) Name to search for to filter poses that get shown. */ + char searchstr[64]; + /** Previously set searchstr (from last loop run), + * so that we can detected when to rebuild searchp. */ + char searchold[64]; + + /** Info-text to print in header. */ + char headerstr[UI_MAX_DRAW_STR]; } tPoseLib_PreviewData; /* defines for tPoseLib_PreviewData->state values */ enum { - PL_PREVIEW_ERROR = -1, - PL_PREVIEW_RUNNING, - PL_PREVIEW_CONFIRM, - PL_PREVIEW_CANCEL, - PL_PREVIEW_RUNONCE, + PL_PREVIEW_ERROR = -1, + PL_PREVIEW_RUNNING, + PL_PREVIEW_CONFIRM, + PL_PREVIEW_CANCEL, + PL_PREVIEW_RUNONCE, }; /* defines for tPoseLib_PreviewData->redraw values */ enum { - PL_PREVIEW_NOREDRAW = 0, - PL_PREVIEW_REDRAWALL, - PL_PREVIEW_REDRAWHEADER, + PL_PREVIEW_NOREDRAW = 0, + PL_PREVIEW_REDRAWALL, + PL_PREVIEW_REDRAWHEADER, }; /* defines for tPoseLib_PreviewData->flag values */ enum { - PL_PREVIEW_FIRSTTIME = (1 << 0), - PL_PREVIEW_SHOWORIGINAL = (1 << 1), - PL_PREVIEW_ANY_BONE_SELECTED = (1 << 2), + PL_PREVIEW_FIRSTTIME = (1 << 0), + PL_PREVIEW_SHOWORIGINAL = (1 << 1), + PL_PREVIEW_ANY_BONE_SELECTED = (1 << 2), }; /* ---------------------------- */ /* simple struct for storing backup info */ typedef struct tPoseLib_Backup { - struct tPoseLib_Backup *next, *prev; + struct tPoseLib_Backup *next, *prev; - bPoseChannel *pchan; /* pose channel backups are for */ + bPoseChannel *pchan; /* pose channel backups are for */ - bPoseChannel olddata; /* copy of pose channel's old data (at start) */ - IDProperty *oldprops; /* copy (needs freeing) of pose channel's properties (at start) */ + bPoseChannel olddata; /* copy of pose channel's old data (at start) */ + IDProperty *oldprops; /* copy (needs freeing) of pose channel's properties (at start) */ } tPoseLib_Backup; /* Makes a copy of the current pose for restoration purposes - doesn't do constraints currently */ static void poselib_backup_posecopy(tPoseLib_PreviewData *pld) { - bActionGroup *agrp; - bPoseChannel *pchan; - bool selected = false; - - /* determine whether any bone is selected. */ - LISTBASE_FOREACH (bPoseChannel *, bchan, &pld->pose->chanbase) { - selected = bchan->bone != NULL && bchan->bone->flag & BONE_SELECTED; - if (selected) { - pld->flag |= PL_PREVIEW_ANY_BONE_SELECTED; - break; - } - } - if (!selected) { - pld->flag &= ~PL_PREVIEW_ANY_BONE_SELECTED; - } - - /* for each posechannel that has an actionchannel in */ - for (agrp = pld->act->groups.first; agrp; agrp = agrp->next) { - /* try to find posechannel */ - pchan = BKE_pose_channel_find_name(pld->pose, agrp->name); - - /* backup data if available */ - if (pchan) { - tPoseLib_Backup *plb; - - /* store backup */ - plb = MEM_callocN(sizeof(tPoseLib_Backup), "tPoseLib_Backup"); - - plb->pchan = pchan; - memcpy(&plb->olddata, plb->pchan, sizeof(bPoseChannel)); - - if (pchan->prop) - plb->oldprops = IDP_CopyProperty(pchan->prop); - - BLI_addtail(&pld->backups, plb); - - /* mark as being affected */ - pld->totcount++; - } - } + bActionGroup *agrp; + bPoseChannel *pchan; + bool selected = false; + + /* determine whether any bone is selected. */ + LISTBASE_FOREACH (bPoseChannel *, bchan, &pld->pose->chanbase) { + selected = bchan->bone != NULL && bchan->bone->flag & BONE_SELECTED; + if (selected) { + pld->flag |= PL_PREVIEW_ANY_BONE_SELECTED; + break; + } + } + if (!selected) { + pld->flag &= ~PL_PREVIEW_ANY_BONE_SELECTED; + } + + /* for each posechannel that has an actionchannel in */ + for (agrp = pld->act->groups.first; agrp; agrp = agrp->next) { + /* try to find posechannel */ + pchan = BKE_pose_channel_find_name(pld->pose, agrp->name); + + /* backup data if available */ + if (pchan) { + tPoseLib_Backup *plb; + + /* store backup */ + plb = MEM_callocN(sizeof(tPoseLib_Backup), "tPoseLib_Backup"); + + plb->pchan = pchan; + memcpy(&plb->olddata, plb->pchan, sizeof(bPoseChannel)); + + if (pchan->prop) + plb->oldprops = IDP_CopyProperty(pchan->prop); + + BLI_addtail(&pld->backups, plb); + + /* mark as being affected */ + pld->totcount++; + } + } } /* Restores original pose */ static void poselib_backup_restore(tPoseLib_PreviewData *pld) { - tPoseLib_Backup *plb; + tPoseLib_Backup *plb; - for (plb = pld->backups.first; plb; plb = plb->next) { - /* copy most of data straight back */ - memcpy(plb->pchan, &plb->olddata, sizeof(bPoseChannel)); + for (plb = pld->backups.first; plb; plb = plb->next) { + /* copy most of data straight back */ + memcpy(plb->pchan, &plb->olddata, sizeof(bPoseChannel)); - /* just overwrite values of properties from the stored copies (there should be some) */ - if (plb->oldprops) - IDP_SyncGroupValues(plb->pchan->prop, plb->oldprops); + /* just overwrite values of properties from the stored copies (there should be some) */ + if (plb->oldprops) + IDP_SyncGroupValues(plb->pchan->prop, plb->oldprops); - /* TODO: constraints settings aren't restored yet, - * even though these could change (though not that likely) */ - } + /* TODO: constraints settings aren't restored yet, + * even though these could change (though not that likely) */ + } } /* Free list of backups, including any side data it may use */ static void poselib_backup_free_data(tPoseLib_PreviewData *pld) { - tPoseLib_Backup *plb, *plbn; + tPoseLib_Backup *plb, *plbn; - for (plb = pld->backups.first; plb; plb = plbn) { - plbn = plb->next; + for (plb = pld->backups.first; plb; plb = plbn) { + plbn = plb->next; - /* free custom data */ - if (plb->oldprops) { - IDP_FreeProperty(plb->oldprops); - MEM_freeN(plb->oldprops); - } + /* free custom data */ + if (plb->oldprops) { + IDP_FreeProperty(plb->oldprops); + MEM_freeN(plb->oldprops); + } - /* free backup element now */ - BLI_freelinkN(&pld->backups, plb); - } + /* free backup element now */ + BLI_freelinkN(&pld->backups, plb); + } } /* ---------------------------- */ @@ -993,186 +1011,187 @@ static void poselib_backup_free_data(tPoseLib_PreviewData *pld) */ static void poselib_apply_pose(tPoseLib_PreviewData *pld) { - PointerRNA *ptr = &pld->rna_ptr; - bArmature *arm = pld->arm; - bPose *pose = pld->pose; - bPoseChannel *pchan; - bAction *act = pld->act; - bActionGroup *agrp; - - KeyframeEditData ked = {{NULL}}; - KeyframeEditFunc group_ok_cb; - int frame = 1; - const bool any_bone_selected = pld->flag & PL_PREVIEW_ANY_BONE_SELECTED; - - /* get the frame */ - if (pld->marker) - frame = pld->marker->frame; - else - return; - - - /* init settings for testing groups for keyframes */ - group_ok_cb = ANIM_editkeyframes_ok(BEZT_OK_FRAMERANGE); - ked.f1 = ((float)frame) - 0.5f; - ked.f2 = ((float)frame) + 0.5f; - - /* start applying - only those channels which have a key at this point in time! */ - for (agrp = act->groups.first; agrp; agrp = agrp->next) { - /* check if group has any keyframes */ - if (ANIM_animchanneldata_keyframes_loop(&ked, NULL, agrp, ALE_GROUP, NULL, group_ok_cb, NULL)) { - /* has keyframe on this frame, so try to get a PoseChannel with this name */ - pchan = BKE_pose_channel_find_name(pose, agrp->name); - - if (pchan) { - bool ok = 0; - - /* check if this bone should get any animation applied */ - if (!any_bone_selected) { - /* if no bones are selected, then any bone is ok */ - ok = 1; - } - else if (pchan->bone) { - /* only ok if bone is visible and selected */ - if ((pchan->bone->flag & BONE_SELECTED) && - (pchan->bone->flag & BONE_HIDDEN_P) == 0 && - (pchan->bone->layer & arm->layer)) - { - ok = 1; - } - } - - if (ok) - animsys_evaluate_action_group(ptr, act, agrp, (float)frame); - } - } - } + PointerRNA *ptr = &pld->rna_ptr; + bArmature *arm = pld->arm; + bPose *pose = pld->pose; + bPoseChannel *pchan; + bAction *act = pld->act; + bActionGroup *agrp; + + KeyframeEditData ked = {{NULL}}; + KeyframeEditFunc group_ok_cb; + int frame = 1; + const bool any_bone_selected = pld->flag & PL_PREVIEW_ANY_BONE_SELECTED; + + /* get the frame */ + if (pld->marker) + frame = pld->marker->frame; + else + return; + + /* init settings for testing groups for keyframes */ + group_ok_cb = ANIM_editkeyframes_ok(BEZT_OK_FRAMERANGE); + ked.f1 = ((float)frame) - 0.5f; + ked.f2 = ((float)frame) + 0.5f; + + /* start applying - only those channels which have a key at this point in time! */ + for (agrp = act->groups.first; agrp; agrp = agrp->next) { + /* check if group has any keyframes */ + if (ANIM_animchanneldata_keyframes_loop( + &ked, NULL, agrp, ALE_GROUP, NULL, group_ok_cb, NULL)) { + /* has keyframe on this frame, so try to get a PoseChannel with this name */ + pchan = BKE_pose_channel_find_name(pose, agrp->name); + + if (pchan) { + bool ok = 0; + + /* check if this bone should get any animation applied */ + if (!any_bone_selected) { + /* if no bones are selected, then any bone is ok */ + ok = 1; + } + else if (pchan->bone) { + /* only ok if bone is visible and selected */ + if ((pchan->bone->flag & BONE_SELECTED) && (pchan->bone->flag & BONE_HIDDEN_P) == 0 && + (pchan->bone->layer & arm->layer)) { + ok = 1; + } + } + + if (ok) + animsys_evaluate_action_group(ptr, act, agrp, (float)frame); + } + } + } } /* Auto-keys/tags bones affected by the pose used from the poselib */ static void poselib_keytag_pose(bContext *C, Scene *scene, tPoseLib_PreviewData *pld) { - bPose *pose = pld->pose; - bPoseChannel *pchan; - bAction *act = pld->act; - bActionGroup *agrp; - - KeyingSet *ks = ANIM_get_keyingset_for_autokeying(scene, ANIM_KS_WHOLE_CHARACTER_ID); - ListBase dsources = {NULL, NULL}; - bool autokey = autokeyframe_cfra_can_key(scene, &pld->ob->id); - const bool any_bone_selected = pld->flag & PL_PREVIEW_ANY_BONE_SELECTED; - - /* start tagging/keying */ - for (agrp = act->groups.first; agrp; agrp = agrp->next) { - /* only for selected bones unless there aren't any selected, in which case all are included */ - pchan = BKE_pose_channel_find_name(pose, agrp->name); - - if (pchan) { - if (!any_bone_selected || ((pchan->bone) && (pchan->bone->flag & BONE_SELECTED))) { - if (autokey) { - /* add datasource override for the PoseChannel, to be used later */ - ANIM_relative_keyingset_add_source(&dsources, &pld->ob->id, &RNA_PoseBone, pchan); - - /* clear any unkeyed tags */ - if (pchan->bone) - pchan->bone->flag &= ~BONE_UNKEYED; - } - else { - /* add unkeyed tags */ - if (pchan->bone) - pchan->bone->flag |= BONE_UNKEYED; - } - } - } - } - - /* perform actual auto-keying now */ - if (autokey) { - /* insert keyframes for all relevant bones in one go */ - ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, (float)CFRA); - BLI_freelistN(&dsources); - } - - /* send notifiers for this */ - WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); + bPose *pose = pld->pose; + bPoseChannel *pchan; + bAction *act = pld->act; + bActionGroup *agrp; + + KeyingSet *ks = ANIM_get_keyingset_for_autokeying(scene, ANIM_KS_WHOLE_CHARACTER_ID); + ListBase dsources = {NULL, NULL}; + bool autokey = autokeyframe_cfra_can_key(scene, &pld->ob->id); + const bool any_bone_selected = pld->flag & PL_PREVIEW_ANY_BONE_SELECTED; + + /* start tagging/keying */ + for (agrp = act->groups.first; agrp; agrp = agrp->next) { + /* only for selected bones unless there aren't any selected, in which case all are included */ + pchan = BKE_pose_channel_find_name(pose, agrp->name); + + if (pchan) { + if (!any_bone_selected || ((pchan->bone) && (pchan->bone->flag & BONE_SELECTED))) { + if (autokey) { + /* add datasource override for the PoseChannel, to be used later */ + ANIM_relative_keyingset_add_source(&dsources, &pld->ob->id, &RNA_PoseBone, pchan); + + /* clear any unkeyed tags */ + if (pchan->bone) + pchan->bone->flag &= ~BONE_UNKEYED; + } + else { + /* add unkeyed tags */ + if (pchan->bone) + pchan->bone->flag |= BONE_UNKEYED; + } + } + } + } + + /* perform actual auto-keying now */ + if (autokey) { + /* insert keyframes for all relevant bones in one go */ + ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, (float)CFRA); + BLI_freelistN(&dsources); + } + + /* send notifiers for this */ + WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); } /* Apply the relevant changes to the pose */ static void poselib_preview_apply(bContext *C, wmOperator *op) { - tPoseLib_PreviewData *pld = (tPoseLib_PreviewData *)op->customdata; - - /* only recalc pose (and its dependencies) if pose has changed */ - if (pld->redraw == PL_PREVIEW_REDRAWALL) { - /* don't clear pose if firsttime */ - if ((pld->flag & PL_PREVIEW_FIRSTTIME) == 0) - poselib_backup_restore(pld); - else - pld->flag &= ~PL_PREVIEW_FIRSTTIME; - - /* pose should be the right one to draw (unless we're temporarily not showing it) */ - if ((pld->flag & PL_PREVIEW_SHOWORIGINAL) == 0) { - RNA_int_set(op->ptr, "pose_index", BLI_findindex(&pld->act->markers, pld->marker)); - poselib_apply_pose(pld); - } - else - RNA_int_set(op->ptr, "pose_index", -2); /* -2 means don't apply any pose */ - - /* old optimize trick... this enforces to bypass the depsgraph - * - note: code copied from transform_generics.c -> recalcData() - */ - // FIXME: shouldn't this use the builtin stuff? - if ((pld->arm->flag & ARM_DELAYDEFORM) == 0) - DEG_id_tag_update(&pld->ob->id, ID_RECALC_GEOMETRY); /* sets recalc flags */ - else - BKE_pose_where_is(CTX_data_depsgraph(C), pld->scene, pld->ob); - } - - /* do header print - if interactively previewing */ - if (pld->state == PL_PREVIEW_RUNNING) { - if (pld->flag & PL_PREVIEW_SHOWORIGINAL) { - ED_area_status_text(pld->sa, IFACE_("PoseLib Previewing Pose: [Showing Original Pose]")); - ED_workspace_status_text(C, IFACE_("Use Tab to start previewing poses again")); - } - else if (pld->searchstr[0]) { - char tempstr[65]; - char markern[64]; - short index; - - /* get search-string */ - index = pld->search_cursor; - - if (index >= 0 && index < sizeof(tempstr) - 1) { - memcpy(&tempstr[0], &pld->searchstr[0], index); - tempstr[index] = '|'; - memcpy(&tempstr[index + 1], &pld->searchstr[index], (sizeof(tempstr) - 1) - index); - } - else { - BLI_strncpy(tempstr, pld->searchstr, sizeof(tempstr)); - } - - /* get marker name */ - BLI_strncpy(markern, pld->marker ? pld->marker->name : "No Matches", sizeof(markern)); - - BLI_snprintf(pld->headerstr, sizeof(pld->headerstr), - IFACE_("PoseLib Previewing Pose: Filter - [%s] | " - "Current Pose - \"%s\""), - tempstr, markern); - ED_area_status_text(pld->sa, pld->headerstr); - ED_workspace_status_text(C, IFACE_("Use ScrollWheel or PageUp/Down to change pose")); - } - else { - BLI_snprintf(pld->headerstr, sizeof(pld->headerstr), - IFACE_("PoseLib Previewing Pose: \"%s\""), - pld->marker->name); - ED_area_status_text(pld->sa, pld->headerstr); - ED_workspace_status_text(C, NULL); - } - } - - /* request drawing of view + clear redraw flag */ - WM_event_add_notifier(C, NC_OBJECT | ND_POSE, pld->ob); - pld->redraw = PL_PREVIEW_NOREDRAW; + tPoseLib_PreviewData *pld = (tPoseLib_PreviewData *)op->customdata; + + /* only recalc pose (and its dependencies) if pose has changed */ + if (pld->redraw == PL_PREVIEW_REDRAWALL) { + /* don't clear pose if firsttime */ + if ((pld->flag & PL_PREVIEW_FIRSTTIME) == 0) + poselib_backup_restore(pld); + else + pld->flag &= ~PL_PREVIEW_FIRSTTIME; + + /* pose should be the right one to draw (unless we're temporarily not showing it) */ + if ((pld->flag & PL_PREVIEW_SHOWORIGINAL) == 0) { + RNA_int_set(op->ptr, "pose_index", BLI_findindex(&pld->act->markers, pld->marker)); + poselib_apply_pose(pld); + } + else + RNA_int_set(op->ptr, "pose_index", -2); /* -2 means don't apply any pose */ + + /* old optimize trick... this enforces to bypass the depsgraph + * - note: code copied from transform_generics.c -> recalcData() + */ + // FIXME: shouldn't this use the builtin stuff? + if ((pld->arm->flag & ARM_DELAYDEFORM) == 0) + DEG_id_tag_update(&pld->ob->id, ID_RECALC_GEOMETRY); /* sets recalc flags */ + else + BKE_pose_where_is(CTX_data_depsgraph(C), pld->scene, pld->ob); + } + + /* do header print - if interactively previewing */ + if (pld->state == PL_PREVIEW_RUNNING) { + if (pld->flag & PL_PREVIEW_SHOWORIGINAL) { + ED_area_status_text(pld->sa, IFACE_("PoseLib Previewing Pose: [Showing Original Pose]")); + ED_workspace_status_text(C, IFACE_("Use Tab to start previewing poses again")); + } + else if (pld->searchstr[0]) { + char tempstr[65]; + char markern[64]; + short index; + + /* get search-string */ + index = pld->search_cursor; + + if (index >= 0 && index < sizeof(tempstr) - 1) { + memcpy(&tempstr[0], &pld->searchstr[0], index); + tempstr[index] = '|'; + memcpy(&tempstr[index + 1], &pld->searchstr[index], (sizeof(tempstr) - 1) - index); + } + else { + BLI_strncpy(tempstr, pld->searchstr, sizeof(tempstr)); + } + + /* get marker name */ + BLI_strncpy(markern, pld->marker ? pld->marker->name : "No Matches", sizeof(markern)); + + BLI_snprintf(pld->headerstr, + sizeof(pld->headerstr), + IFACE_("PoseLib Previewing Pose: Filter - [%s] | " + "Current Pose - \"%s\""), + tempstr, + markern); + ED_area_status_text(pld->sa, pld->headerstr); + ED_workspace_status_text(C, IFACE_("Use ScrollWheel or PageUp/Down to change pose")); + } + else { + BLI_snprintf(pld->headerstr, + sizeof(pld->headerstr), + IFACE_("PoseLib Previewing Pose: \"%s\""), + pld->marker->name); + ED_area_status_text(pld->sa, pld->headerstr); + ED_workspace_status_text(C, NULL); + } + } + + /* request drawing of view + clear redraw flag */ + WM_event_add_notifier(C, NC_OBJECT | ND_POSE, pld->ob); + pld->redraw = PL_PREVIEW_NOREDRAW; } /* ---------------------------- */ @@ -1182,351 +1201,373 @@ static void poselib_preview_apply(bContext *C, wmOperator *op) */ static void poselib_preview_get_next(tPoseLib_PreviewData *pld, int step) { - /* stop if not going anywhere, as we assume that there is a direction to move in */ - if (step == 0) - return; - - /* search-string dictates a special approach */ - if (pld->searchstr[0]) { - TimeMarker *marker; - LinkData *ld, *ldn, *ldc; - - /* free and rebuild if needed (i.e. if search-str changed) */ - if (!STREQ(pld->searchstr, pld->searchold)) { - /* free list of temporary search matches */ - BLI_freelistN(&pld->searchp); - - /* generate a new list of search matches */ - for (marker = pld->act->markers.first; marker; marker = marker->next) { - /* does the name partially match? - * - don't worry about case, to make it easier for users to quickly input a name (or - * part of one), which is the whole point of this feature - */ - if (BLI_strcasestr(marker->name, pld->searchstr)) { - /* make link-data to store reference to it */ - ld = MEM_callocN(sizeof(LinkData), "PoseMatch"); - ld->data = marker; - BLI_addtail(&pld->searchp, ld); - } - } - - /* set current marker to NULL (so that we start from first) */ - pld->marker = NULL; - } - - /* check if any matches */ - if (BLI_listbase_is_empty(&pld->searchp)) { - pld->marker = NULL; - return; - } - - /* find first match */ - for (ldc = pld->searchp.first; ldc; ldc = ldc->next) { - if (ldc->data == pld->marker) - break; - } - if (ldc == NULL) - ldc = pld->searchp.first; - - /* Loop through the matches in a cyclic fashion, incrementing/decrementing step as appropriate - * until step == 0. At this point, marker should be the correct marker. - */ - if (step > 0) { - for (ld = ldc; ld && step; ld = ldn, step--) - ldn = (ld->next) ? ld->next : pld->searchp.first; - } - else { - for (ld = ldc; ld && step; ld = ldn, step++) - ldn = (ld->prev) ? ld->prev : pld->searchp.last; - } - - /* set marker */ - if (ld) - pld->marker = ld->data; - } - else { - TimeMarker *marker, *next; - - /* if no marker, because we just ended searching, then set that to the start of the list */ - if (pld->marker == NULL) - pld->marker = pld->act->markers.first; - - /* Loop through the markers in a cyclic fashion, incrementing/decrementing step as appropriate - * until step == 0. At this point, marker should be the correct marker. - */ - if (step > 0) { - for (marker = pld->marker; marker && step; marker = next, step--) - next = (marker->next) ? marker->next : pld->act->markers.first; - } - else { - for (marker = pld->marker; marker && step; marker = next, step++) - next = (marker->prev) ? marker->prev : pld->act->markers.last; - } - - /* it should be fairly impossible for marker to be NULL */ - if (marker) - pld->marker = marker; - } + /* stop if not going anywhere, as we assume that there is a direction to move in */ + if (step == 0) + return; + + /* search-string dictates a special approach */ + if (pld->searchstr[0]) { + TimeMarker *marker; + LinkData *ld, *ldn, *ldc; + + /* free and rebuild if needed (i.e. if search-str changed) */ + if (!STREQ(pld->searchstr, pld->searchold)) { + /* free list of temporary search matches */ + BLI_freelistN(&pld->searchp); + + /* generate a new list of search matches */ + for (marker = pld->act->markers.first; marker; marker = marker->next) { + /* does the name partially match? + * - don't worry about case, to make it easier for users to quickly input a name (or + * part of one), which is the whole point of this feature + */ + if (BLI_strcasestr(marker->name, pld->searchstr)) { + /* make link-data to store reference to it */ + ld = MEM_callocN(sizeof(LinkData), "PoseMatch"); + ld->data = marker; + BLI_addtail(&pld->searchp, ld); + } + } + + /* set current marker to NULL (so that we start from first) */ + pld->marker = NULL; + } + + /* check if any matches */ + if (BLI_listbase_is_empty(&pld->searchp)) { + pld->marker = NULL; + return; + } + + /* find first match */ + for (ldc = pld->searchp.first; ldc; ldc = ldc->next) { + if (ldc->data == pld->marker) + break; + } + if (ldc == NULL) + ldc = pld->searchp.first; + + /* Loop through the matches in a cyclic fashion, incrementing/decrementing step as appropriate + * until step == 0. At this point, marker should be the correct marker. + */ + if (step > 0) { + for (ld = ldc; ld && step; ld = ldn, step--) + ldn = (ld->next) ? ld->next : pld->searchp.first; + } + else { + for (ld = ldc; ld && step; ld = ldn, step++) + ldn = (ld->prev) ? ld->prev : pld->searchp.last; + } + + /* set marker */ + if (ld) + pld->marker = ld->data; + } + else { + TimeMarker *marker, *next; + + /* if no marker, because we just ended searching, then set that to the start of the list */ + if (pld->marker == NULL) + pld->marker = pld->act->markers.first; + + /* Loop through the markers in a cyclic fashion, incrementing/decrementing step as appropriate + * until step == 0. At this point, marker should be the correct marker. + */ + if (step > 0) { + for (marker = pld->marker; marker && step; marker = next, step--) + next = (marker->next) ? marker->next : pld->act->markers.first; + } + else { + for (marker = pld->marker; marker && step; marker = next, step++) + next = (marker->prev) ? marker->prev : pld->act->markers.last; + } + + /* it should be fairly impossible for marker to be NULL */ + if (marker) + pld->marker = marker; + } } /* 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, + unsigned short event, + char ascii) { - /* try doing some form of string manipulation first */ - switch (event) { - case BACKSPACEKEY: - if (pld->searchstr[0] && pld->search_cursor) { - short len = strlen(pld->searchstr); - short index = pld->search_cursor; - short i; - - for (i = index; i <= len; i++) - pld->searchstr[i - 1] = pld->searchstr[i]; - - pld->search_cursor--; - - poselib_preview_get_next(pld, 1); - pld->redraw = PL_PREVIEW_REDRAWALL; - return; - } - break; - - case DELKEY: - if (pld->searchstr[0] && pld->searchstr[1]) { - short len = strlen(pld->searchstr); - short index = pld->search_cursor; - int i; - - if (index < len) { - for (i = index; i < len; i++) - pld->searchstr[i] = pld->searchstr[i + 1]; - - poselib_preview_get_next(pld, 1); - pld->redraw = PL_PREVIEW_REDRAWALL; - return; - } - } - break; - } - - if (ascii) { - /* character to add to the string */ - short index = pld->search_cursor; - short len = (pld->searchstr[0]) ? strlen(pld->searchstr) : 0; - short i; - - if (len) { - for (i = len; i > index; i--) - pld->searchstr[i] = pld->searchstr[i - 1]; - } - else - pld->searchstr[1] = 0; - - pld->searchstr[index] = ascii; - pld->search_cursor++; - - poselib_preview_get_next(pld, 1); - pld->redraw = PL_PREVIEW_REDRAWALL; - } + /* try doing some form of string manipulation first */ + switch (event) { + case BACKSPACEKEY: + if (pld->searchstr[0] && pld->search_cursor) { + short len = strlen(pld->searchstr); + short index = pld->search_cursor; + short i; + + for (i = index; i <= len; i++) + pld->searchstr[i - 1] = pld->searchstr[i]; + + pld->search_cursor--; + + poselib_preview_get_next(pld, 1); + pld->redraw = PL_PREVIEW_REDRAWALL; + return; + } + break; + + case DELKEY: + if (pld->searchstr[0] && pld->searchstr[1]) { + short len = strlen(pld->searchstr); + short index = pld->search_cursor; + int i; + + if (index < len) { + for (i = index; i < len; i++) + pld->searchstr[i] = pld->searchstr[i + 1]; + + poselib_preview_get_next(pld, 1); + pld->redraw = PL_PREVIEW_REDRAWALL; + return; + } + } + break; + } + + if (ascii) { + /* character to add to the string */ + short index = pld->search_cursor; + short len = (pld->searchstr[0]) ? strlen(pld->searchstr) : 0; + short i; + + if (len) { + for (i = len; i > index; i--) + pld->searchstr[i] = pld->searchstr[i - 1]; + } + else + pld->searchstr[1] = 0; + + pld->searchstr[index] = ascii; + pld->search_cursor++; + + poselib_preview_get_next(pld, 1); + pld->redraw = PL_PREVIEW_REDRAWALL; + } } /* handle events for poselib_preview_poses */ static int poselib_preview_handle_event(bContext *UNUSED(C), wmOperator *op, const wmEvent *event) { - tPoseLib_PreviewData *pld = op->customdata; - int ret = OPERATOR_RUNNING_MODAL; - - /* only accept 'press' event, and ignore 'release', so that we don't get double actions */ - if (ELEM(event->val, KM_PRESS, KM_NOTHING) == 0) { - //printf("PoseLib: skipping event with type '%s' and val %d\n", WM_key_event_string(event->type, false), event->val); - return ret; - } - - /* backup stuff that needs to occur before every operation - * - make a copy of searchstr, so that we know if cache needs to be rebuilt - */ - BLI_strncpy(pld->searchold, pld->searchstr, sizeof(pld->searchold)); - - /* if we're currently showing the original pose, only certain events are handled */ - if (pld->flag & PL_PREVIEW_SHOWORIGINAL) { - switch (event->type) { - /* exit - cancel */ - case ESCKEY: - case RIGHTMOUSE: - pld->state = PL_PREVIEW_CANCEL; - break; - - /* exit - confirm */ - case LEFTMOUSE: - case RETKEY: - case PADENTER: - case SPACEKEY: - pld->state = PL_PREVIEW_CONFIRM; - break; - - /* view manipulation */ - /* we add pass through here, so that the operators responsible for these can still run, - * even though we still maintain control (as RUNNING_MODAL flag is still set too) - */ - case PAD0: case PAD1: case PAD2: case PAD3: case PAD4: - case PAD5: case PAD6: case PAD7: case PAD8: case PAD9: - case PADPLUSKEY: case PADMINUS: - case MIDDLEMOUSE: case MOUSEMOVE: - //pld->redraw = PL_PREVIEW_REDRAWHEADER; - ret = OPERATOR_PASS_THROUGH; - break; - - /* quicky compare to original */ - case TABKEY: - pld->flag &= ~PL_PREVIEW_SHOWORIGINAL; - pld->redraw = PL_PREVIEW_REDRAWALL; - break; - } - - /* EXITS HERE... */ - return ret; - } - - /* NORMAL EVENT HANDLING... */ - /* searching takes priority over normal activity */ - switch (event->type) { - /* exit - cancel */ - case ESCKEY: - case RIGHTMOUSE: - pld->state = PL_PREVIEW_CANCEL; - break; - - /* exit - confirm */ - case LEFTMOUSE: - case RETKEY: - case PADENTER: - case SPACEKEY: - pld->state = PL_PREVIEW_CONFIRM; - break; - - /* toggle between original pose and poselib pose*/ - case TABKEY: - pld->flag |= PL_PREVIEW_SHOWORIGINAL; - pld->redraw = PL_PREVIEW_REDRAWALL; - break; - - /* change to previous pose (cyclic) */ - case PAGEUPKEY: - case WHEELUPMOUSE: - poselib_preview_get_next(pld, -1); - pld->redraw = PL_PREVIEW_REDRAWALL; - break; - - /* change to next pose (cyclic) */ - case PAGEDOWNKEY: - case WHEELDOWNMOUSE: - poselib_preview_get_next(pld, 1); - pld->redraw = PL_PREVIEW_REDRAWALL; - break; - - /* jump 5 poses (cyclic, back) */ - case DOWNARROWKEY: - poselib_preview_get_next(pld, -5); - pld->redraw = PL_PREVIEW_REDRAWALL; - break; - - /* jump 5 poses (cyclic, forward) */ - case UPARROWKEY: - poselib_preview_get_next(pld, 5); - pld->redraw = PL_PREVIEW_REDRAWALL; - break; - - /* change to next pose or searching cursor control */ - case RIGHTARROWKEY: - if (pld->searchstr[0]) { - /* move text-cursor to the right */ - if (pld->search_cursor < strlen(pld->searchstr)) - pld->search_cursor++; - pld->redraw = PL_PREVIEW_REDRAWHEADER; - } - else { - /* change to next pose (cyclic) */ - poselib_preview_get_next(pld, 1); - pld->redraw = PL_PREVIEW_REDRAWALL; - } - break; - - /* change to next pose or searching cursor control */ - case LEFTARROWKEY: - if (pld->searchstr[0]) { - /* move text-cursor to the left */ - if (pld->search_cursor) - pld->search_cursor--; - pld->redraw = PL_PREVIEW_REDRAWHEADER; - } - else { - /* change to previous pose (cyclic) */ - poselib_preview_get_next(pld, -1); - pld->redraw = PL_PREVIEW_REDRAWALL; - } - break; - - /* change to first pose or start of searching string */ - case HOMEKEY: - if (pld->searchstr[0]) { - pld->search_cursor = 0; - pld->redraw = PL_PREVIEW_REDRAWHEADER; - } - else { - /* change to first pose */ - pld->marker = pld->act->markers.first; - pld->act->active_marker = 1; - - pld->redraw = PL_PREVIEW_REDRAWALL; - } - break; - - /* change to last pose or start of searching string */ - case ENDKEY: - if (pld->searchstr[0]) { - pld->search_cursor = strlen(pld->searchstr); - pld->redraw = PL_PREVIEW_REDRAWHEADER; - } - else { - /* change to last pose */ - pld->marker = pld->act->markers.last; - pld->act->active_marker = BLI_listbase_count(&pld->act->markers); - - pld->redraw = PL_PREVIEW_REDRAWALL; - } - break; - - /* view manipulation */ - /* we add pass through here, so that the operators responsible for these can still run, - * even though we still maintain control (as RUNNING_MODAL flag is still set too) - */ - case MIDDLEMOUSE: case MOUSEMOVE: - //pld->redraw = PL_PREVIEW_REDRAWHEADER; - ret = OPERATOR_PASS_THROUGH; - break; - - /* view manipulation, or searching */ - case PAD0: case PAD1: case PAD2: case PAD3: case PAD4: - case PAD5: case PAD6: case PAD7: case PAD8: case PAD9: - case PADPLUSKEY: case PADMINUS: - if (pld->searchstr[0]) { - /* searching... */ - poselib_preview_handle_search(pld, event->type, event->ascii); - } - else { - /* view manipulation (see above) */ - //pld->redraw = PL_PREVIEW_REDRAWHEADER; - ret = OPERATOR_PASS_THROUGH; - } - break; - - /* otherwise, assume that searching might be able to handle it */ - default: - poselib_preview_handle_search(pld, event->type, event->ascii); - break; - } - - return ret; + tPoseLib_PreviewData *pld = op->customdata; + int ret = OPERATOR_RUNNING_MODAL; + + /* only accept 'press' event, and ignore 'release', so that we don't get double actions */ + if (ELEM(event->val, KM_PRESS, KM_NOTHING) == 0) { + //printf("PoseLib: skipping event with type '%s' and val %d\n", WM_key_event_string(event->type, false), event->val); + return ret; + } + + /* backup stuff that needs to occur before every operation + * - make a copy of searchstr, so that we know if cache needs to be rebuilt + */ + BLI_strncpy(pld->searchold, pld->searchstr, sizeof(pld->searchold)); + + /* if we're currently showing the original pose, only certain events are handled */ + if (pld->flag & PL_PREVIEW_SHOWORIGINAL) { + switch (event->type) { + /* exit - cancel */ + case ESCKEY: + case RIGHTMOUSE: + pld->state = PL_PREVIEW_CANCEL; + break; + + /* exit - confirm */ + case LEFTMOUSE: + case RETKEY: + case PADENTER: + case SPACEKEY: + pld->state = PL_PREVIEW_CONFIRM; + break; + + /* view manipulation */ + /* we add pass through here, so that the operators responsible for these can still run, + * even though we still maintain control (as RUNNING_MODAL flag is still set too) + */ + case PAD0: + case PAD1: + case PAD2: + case PAD3: + case PAD4: + case PAD5: + case PAD6: + case PAD7: + case PAD8: + case PAD9: + case PADPLUSKEY: + case PADMINUS: + case MIDDLEMOUSE: + case MOUSEMOVE: + //pld->redraw = PL_PREVIEW_REDRAWHEADER; + ret = OPERATOR_PASS_THROUGH; + break; + + /* quicky compare to original */ + case TABKEY: + pld->flag &= ~PL_PREVIEW_SHOWORIGINAL; + pld->redraw = PL_PREVIEW_REDRAWALL; + break; + } + + /* EXITS HERE... */ + return ret; + } + + /* NORMAL EVENT HANDLING... */ + /* searching takes priority over normal activity */ + switch (event->type) { + /* exit - cancel */ + case ESCKEY: + case RIGHTMOUSE: + pld->state = PL_PREVIEW_CANCEL; + break; + + /* exit - confirm */ + case LEFTMOUSE: + case RETKEY: + case PADENTER: + case SPACEKEY: + pld->state = PL_PREVIEW_CONFIRM; + break; + + /* toggle between original pose and poselib pose*/ + case TABKEY: + pld->flag |= PL_PREVIEW_SHOWORIGINAL; + pld->redraw = PL_PREVIEW_REDRAWALL; + break; + + /* change to previous pose (cyclic) */ + case PAGEUPKEY: + case WHEELUPMOUSE: + poselib_preview_get_next(pld, -1); + pld->redraw = PL_PREVIEW_REDRAWALL; + break; + + /* change to next pose (cyclic) */ + case PAGEDOWNKEY: + case WHEELDOWNMOUSE: + poselib_preview_get_next(pld, 1); + pld->redraw = PL_PREVIEW_REDRAWALL; + break; + + /* jump 5 poses (cyclic, back) */ + case DOWNARROWKEY: + poselib_preview_get_next(pld, -5); + pld->redraw = PL_PREVIEW_REDRAWALL; + break; + + /* jump 5 poses (cyclic, forward) */ + case UPARROWKEY: + poselib_preview_get_next(pld, 5); + pld->redraw = PL_PREVIEW_REDRAWALL; + break; + + /* change to next pose or searching cursor control */ + case RIGHTARROWKEY: + if (pld->searchstr[0]) { + /* move text-cursor to the right */ + if (pld->search_cursor < strlen(pld->searchstr)) + pld->search_cursor++; + pld->redraw = PL_PREVIEW_REDRAWHEADER; + } + else { + /* change to next pose (cyclic) */ + poselib_preview_get_next(pld, 1); + pld->redraw = PL_PREVIEW_REDRAWALL; + } + break; + + /* change to next pose or searching cursor control */ + case LEFTARROWKEY: + if (pld->searchstr[0]) { + /* move text-cursor to the left */ + if (pld->search_cursor) + pld->search_cursor--; + pld->redraw = PL_PREVIEW_REDRAWHEADER; + } + else { + /* change to previous pose (cyclic) */ + poselib_preview_get_next(pld, -1); + pld->redraw = PL_PREVIEW_REDRAWALL; + } + break; + + /* change to first pose or start of searching string */ + case HOMEKEY: + if (pld->searchstr[0]) { + pld->search_cursor = 0; + pld->redraw = PL_PREVIEW_REDRAWHEADER; + } + else { + /* change to first pose */ + pld->marker = pld->act->markers.first; + pld->act->active_marker = 1; + + pld->redraw = PL_PREVIEW_REDRAWALL; + } + break; + + /* change to last pose or start of searching string */ + case ENDKEY: + if (pld->searchstr[0]) { + pld->search_cursor = strlen(pld->searchstr); + pld->redraw = PL_PREVIEW_REDRAWHEADER; + } + else { + /* change to last pose */ + pld->marker = pld->act->markers.last; + pld->act->active_marker = BLI_listbase_count(&pld->act->markers); + + pld->redraw = PL_PREVIEW_REDRAWALL; + } + break; + + /* view manipulation */ + /* we add pass through here, so that the operators responsible for these can still run, + * even though we still maintain control (as RUNNING_MODAL flag is still set too) + */ + case MIDDLEMOUSE: + case MOUSEMOVE: + //pld->redraw = PL_PREVIEW_REDRAWHEADER; + ret = OPERATOR_PASS_THROUGH; + break; + + /* view manipulation, or searching */ + case PAD0: + case PAD1: + case PAD2: + case PAD3: + case PAD4: + case PAD5: + case PAD6: + case PAD7: + case PAD8: + case PAD9: + case PADPLUSKEY: + case PADMINUS: + if (pld->searchstr[0]) { + /* searching... */ + poselib_preview_handle_search(pld, event->type, event->ascii); + } + else { + /* view manipulation (see above) */ + //pld->redraw = PL_PREVIEW_REDRAWHEADER; + ret = OPERATOR_PASS_THROUGH; + } + break; + + /* otherwise, assume that searching might be able to handle it */ + default: + poselib_preview_handle_search(pld, event->type, event->ascii); + break; + } + + return ret; } /* ---------------------------- */ @@ -1534,277 +1575,295 @@ static int poselib_preview_handle_event(bContext *UNUSED(C), wmOperator *op, con /* Init PoseLib Previewing data */ static void poselib_preview_init_data(bContext *C, wmOperator *op) { - tPoseLib_PreviewData *pld; - Object *ob = get_poselib_object(C); - int pose_index = RNA_int_get(op->ptr, "pose_index"); - - /* set up preview state info */ - op->customdata = pld = MEM_callocN(sizeof(tPoseLib_PreviewData), "PoseLib Preview Data"); - - /* get basic data */ - pld->ob = ob; - pld->arm = (ob) ? (ob->data) : NULL; - pld->pose = (ob) ? (ob->pose) : NULL; - pld->act = (ob) ? (ob->poselib) : NULL; - - pld->scene = CTX_data_scene(C); - pld->sa = CTX_wm_area(C); - - /* get starting pose based on RNA-props for this operator */ - if (pose_index == -1) - pld->marker = poselib_get_active_pose(pld->act); - else if (pose_index == -2) - pld->flag |= PL_PREVIEW_SHOWORIGINAL; - else - pld->marker = (pld->act) ? BLI_findlink(&pld->act->markers, pose_index) : NULL; - - /* check if valid poselib */ - if (ELEM(NULL, pld->ob, pld->pose, pld->arm)) { - BKE_report(op->reports, RPT_ERROR, "Pose lib is only for armatures in pose mode"); - pld->state = PL_PREVIEW_ERROR; - return; - } - if (pld->act == NULL) { - BKE_report(op->reports, RPT_ERROR, "Object does not have a valid pose lib"); - pld->state = PL_PREVIEW_ERROR; - return; - } - if (pld->marker == NULL) { - if (pld->act->markers.first) { - /* just use first one then... */ - pld->marker = pld->act->markers.first; - if (pose_index > -2) - BKE_report(op->reports, RPT_WARNING, "Pose lib had no active pose"); - } - else { - BKE_report(op->reports, RPT_ERROR, "Pose lib has no poses to preview/apply"); - pld->state = PL_PREVIEW_ERROR; - return; - } - } - - /* get ID pointer for applying poses */ - RNA_id_pointer_create(&ob->id, &pld->rna_ptr); - - /* make backups for restoring pose */ - poselib_backup_posecopy(pld); - - /* set flags for running */ - pld->state = PL_PREVIEW_RUNNING; - pld->redraw = PL_PREVIEW_REDRAWALL; - pld->flag |= PL_PREVIEW_FIRSTTIME; - - /* set depsgraph flags */ - /* make sure the lock is set OK, unlock can be accidentally saved? */ - pld->pose->flag |= POSE_LOCKED; - pld->pose->flag &= ~POSE_DO_UNLOCK; - - /* clear strings + search */ - pld->headerstr[0] = pld->searchstr[0] = pld->searchold[0] = '\0'; - pld->search_cursor = 0; + tPoseLib_PreviewData *pld; + Object *ob = get_poselib_object(C); + int pose_index = RNA_int_get(op->ptr, "pose_index"); + + /* set up preview state info */ + op->customdata = pld = MEM_callocN(sizeof(tPoseLib_PreviewData), "PoseLib Preview Data"); + + /* get basic data */ + pld->ob = ob; + pld->arm = (ob) ? (ob->data) : NULL; + pld->pose = (ob) ? (ob->pose) : NULL; + pld->act = (ob) ? (ob->poselib) : NULL; + + pld->scene = CTX_data_scene(C); + pld->sa = CTX_wm_area(C); + + /* get starting pose based on RNA-props for this operator */ + if (pose_index == -1) + pld->marker = poselib_get_active_pose(pld->act); + else if (pose_index == -2) + pld->flag |= PL_PREVIEW_SHOWORIGINAL; + else + pld->marker = (pld->act) ? BLI_findlink(&pld->act->markers, pose_index) : NULL; + + /* check if valid poselib */ + if (ELEM(NULL, pld->ob, pld->pose, pld->arm)) { + BKE_report(op->reports, RPT_ERROR, "Pose lib is only for armatures in pose mode"); + pld->state = PL_PREVIEW_ERROR; + return; + } + if (pld->act == NULL) { + BKE_report(op->reports, RPT_ERROR, "Object does not have a valid pose lib"); + pld->state = PL_PREVIEW_ERROR; + return; + } + if (pld->marker == NULL) { + if (pld->act->markers.first) { + /* just use first one then... */ + pld->marker = pld->act->markers.first; + if (pose_index > -2) + BKE_report(op->reports, RPT_WARNING, "Pose lib had no active pose"); + } + else { + BKE_report(op->reports, RPT_ERROR, "Pose lib has no poses to preview/apply"); + pld->state = PL_PREVIEW_ERROR; + return; + } + } + + /* get ID pointer for applying poses */ + RNA_id_pointer_create(&ob->id, &pld->rna_ptr); + + /* make backups for restoring pose */ + poselib_backup_posecopy(pld); + + /* set flags for running */ + pld->state = PL_PREVIEW_RUNNING; + pld->redraw = PL_PREVIEW_REDRAWALL; + pld->flag |= PL_PREVIEW_FIRSTTIME; + + /* set depsgraph flags */ + /* make sure the lock is set OK, unlock can be accidentally saved? */ + pld->pose->flag |= POSE_LOCKED; + pld->pose->flag &= ~POSE_DO_UNLOCK; + + /* clear strings + search */ + pld->headerstr[0] = pld->searchstr[0] = pld->searchold[0] = '\0'; + pld->search_cursor = 0; } /* After previewing poses */ static void poselib_preview_cleanup(bContext *C, wmOperator *op) { - tPoseLib_PreviewData *pld = (tPoseLib_PreviewData *)op->customdata; - Scene *scene = pld->scene; - Object *ob = pld->ob; - bPose *pose = pld->pose; - bArmature *arm = pld->arm; - bAction *act = pld->act; - 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_workspace_status_text(C, NULL); - - /* this signal does one recalc on pose, then unlocks, so ESC or edit will work */ - pose->flag |= POSE_DO_UNLOCK; - - /* clear pose if canceled */ - if (pld->state == PL_PREVIEW_CANCEL) { - poselib_backup_restore(pld); - - /* old optimize trick... this enforces to bypass the depgraph - * - note: code copied from transform_generics.c -> recalcData() - */ - if ((arm->flag & ARM_DELAYDEFORM) == 0) - DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); /* sets recalc flags */ - else - BKE_pose_where_is(CTX_data_depsgraph(C), scene, ob); - } - else if (pld->state == PL_PREVIEW_CONFIRM) { - /* tag poses as appropriate */ - poselib_keytag_pose(C, scene, pld); - - /* change active pose setting */ - act->active_marker = BLI_findindex(&act->markers, marker) + 1; - action_set_activemarker(act, marker, NULL); - - /* Update event for pose and deformation children */ - DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); - - /* updates */ - if (IS_AUTOKEY_MODE(scene, NORMAL)) { - //remake_action_ipos(ob->action); - } - else - BKE_pose_where_is(CTX_data_depsgraph(C), scene, ob); - } - - /* Request final redraw of the view. */ - WM_event_add_notifier(C, NC_OBJECT | ND_POSE, pld->ob); - - /* free memory used for backups and searching */ - poselib_backup_free_data(pld); - BLI_freelistN(&pld->searchp); - - /* free temp data for operator */ - MEM_freeN(pld); - op->customdata = NULL; + tPoseLib_PreviewData *pld = (tPoseLib_PreviewData *)op->customdata; + Scene *scene = pld->scene; + Object *ob = pld->ob; + bPose *pose = pld->pose; + bArmature *arm = pld->arm; + bAction *act = pld->act; + 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_workspace_status_text(C, NULL); + + /* this signal does one recalc on pose, then unlocks, so ESC or edit will work */ + pose->flag |= POSE_DO_UNLOCK; + + /* clear pose if canceled */ + if (pld->state == PL_PREVIEW_CANCEL) { + poselib_backup_restore(pld); + + /* old optimize trick... this enforces to bypass the depgraph + * - note: code copied from transform_generics.c -> recalcData() + */ + if ((arm->flag & ARM_DELAYDEFORM) == 0) + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); /* sets recalc flags */ + else + BKE_pose_where_is(CTX_data_depsgraph(C), scene, ob); + } + else if (pld->state == PL_PREVIEW_CONFIRM) { + /* tag poses as appropriate */ + poselib_keytag_pose(C, scene, pld); + + /* change active pose setting */ + act->active_marker = BLI_findindex(&act->markers, marker) + 1; + action_set_activemarker(act, marker, NULL); + + /* Update event for pose and deformation children */ + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); + + /* updates */ + if (IS_AUTOKEY_MODE(scene, NORMAL)) { + //remake_action_ipos(ob->action); + } + else + BKE_pose_where_is(CTX_data_depsgraph(C), scene, ob); + } + + /* Request final redraw of the view. */ + WM_event_add_notifier(C, NC_OBJECT | ND_POSE, pld->ob); + + /* free memory used for backups and searching */ + poselib_backup_free_data(pld); + BLI_freelistN(&pld->searchp); + + /* free temp data for operator */ + MEM_freeN(pld); + op->customdata = NULL; } /* End previewing operation */ static int poselib_preview_exit(bContext *C, wmOperator *op) { - tPoseLib_PreviewData *pld = op->customdata; - int exit_state = pld->state; + tPoseLib_PreviewData *pld = op->customdata; + int exit_state = pld->state; - /* finish up */ - poselib_preview_cleanup(C, op); + /* finish up */ + poselib_preview_cleanup(C, op); - if (ELEM(exit_state, PL_PREVIEW_CANCEL, PL_PREVIEW_ERROR)) - return OPERATOR_CANCELLED; - else - return OPERATOR_FINISHED; + if (ELEM(exit_state, PL_PREVIEW_CANCEL, PL_PREVIEW_ERROR)) + return OPERATOR_CANCELLED; + else + return OPERATOR_FINISHED; } /* Cancel previewing operation (called when exiting Blender) */ static void poselib_preview_cancel(bContext *C, wmOperator *op) { - poselib_preview_exit(C, op); + poselib_preview_exit(C, op); } /* main modal status check */ static int poselib_preview_modal(bContext *C, wmOperator *op, const wmEvent *event) { - tPoseLib_PreviewData *pld = op->customdata; - int ret; + tPoseLib_PreviewData *pld = op->customdata; + int ret; - /* 1) check state to see if we're still running */ - if (pld->state != PL_PREVIEW_RUNNING) - return poselib_preview_exit(C, op); + /* 1) check state to see if we're still running */ + if (pld->state != PL_PREVIEW_RUNNING) + return poselib_preview_exit(C, op); - /* 2) handle events */ - ret = poselib_preview_handle_event(C, op, event); + /* 2) handle events */ + ret = poselib_preview_handle_event(C, op, event); - /* 3) apply changes and redraw, otherwise, confirming goes wrong */ - if (pld->redraw) - poselib_preview_apply(C, op); + /* 3) apply changes and redraw, otherwise, confirming goes wrong */ + if (pld->redraw) + poselib_preview_apply(C, op); - return ret; + return ret; } /* Modal Operator init */ static int poselib_preview_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { - tPoseLib_PreviewData *pld; + tPoseLib_PreviewData *pld; - /* check if everything is ok, and init settings for modal operator */ - poselib_preview_init_data(C, op); - pld = (tPoseLib_PreviewData *)op->customdata; + /* check if everything is ok, and init settings for modal operator */ + poselib_preview_init_data(C, op); + pld = (tPoseLib_PreviewData *)op->customdata; - if (pld->state == PL_PREVIEW_ERROR) { - /* an error occurred, so free temp mem used */ - poselib_preview_cleanup(C, op); - return OPERATOR_CANCELLED; - } + if (pld->state == PL_PREVIEW_ERROR) { + /* an error occurred, so free temp mem used */ + poselib_preview_cleanup(C, op); + return OPERATOR_CANCELLED; + } - /* do initial apply to have something to look at */ - poselib_preview_apply(C, op); + /* do initial apply to have something to look at */ + poselib_preview_apply(C, op); - /* add temp handler if we're running as a modal operator */ - WM_event_add_modal_handler(C, op); + /* add temp handler if we're running as a modal operator */ + WM_event_add_modal_handler(C, op); - return OPERATOR_RUNNING_MODAL; + return OPERATOR_RUNNING_MODAL; } /* Repeat operator */ static int poselib_preview_exec(bContext *C, wmOperator *op) { - tPoseLib_PreviewData *pld; + tPoseLib_PreviewData *pld; - /* check if everything is ok, and init settings for modal operator */ - poselib_preview_init_data(C, op); - pld = (tPoseLib_PreviewData *)op->customdata; + /* check if everything is ok, and init settings for modal operator */ + poselib_preview_init_data(C, op); + pld = (tPoseLib_PreviewData *)op->customdata; - if (pld->state == PL_PREVIEW_ERROR) { - /* an error occurred, so free temp mem used */ - poselib_preview_cleanup(C, op); - return OPERATOR_CANCELLED; - } + if (pld->state == PL_PREVIEW_ERROR) { + /* an error occurred, so free temp mem used */ + poselib_preview_cleanup(C, op); + return OPERATOR_CANCELLED; + } - /* the exec() callback is effectively a 'run-once' scenario, so set the state to that - * so that everything draws correctly - */ - pld->state = PL_PREVIEW_RUNONCE; + /* the exec() callback is effectively a 'run-once' scenario, so set the state to that + * so that everything draws correctly + */ + pld->state = PL_PREVIEW_RUNONCE; - /* apply the active pose */ - poselib_preview_apply(C, op); + /* apply the active pose */ + poselib_preview_apply(C, op); - /* now, set the status to exit */ - pld->state = PL_PREVIEW_CONFIRM; + /* now, set the status to exit */ + pld->state = PL_PREVIEW_CONFIRM; - /* cleanup */ - return poselib_preview_exit(C, op); + /* cleanup */ + return poselib_preview_exit(C, op); } void POSELIB_OT_browse_interactive(wmOperatorType *ot) { - /* identifiers */ - ot->name = "PoseLib Browse Poses"; - ot->idname = "POSELIB_OT_browse_interactive"; - ot->description = "Interactively browse poses in 3D-View"; - - /* callbacks */ - ot->invoke = poselib_preview_invoke; - ot->modal = poselib_preview_modal; - ot->cancel = poselib_preview_cancel; - ot->exec = poselib_preview_exec; - ot->poll = has_poselib_pose_data_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING; - - /* properties */ - // TODO: make the pose_index into a proper enum instead of a cryptic int... - ot->prop = RNA_def_int(ot->srna, "pose_index", -1, -2, INT_MAX, "Pose", "Index of the pose to apply (-2 for no change to pose, -1 for poselib active pose)", 0, INT_MAX); - - // XXX: percentage vs factor? - /* not used yet */ + /* identifiers */ + ot->name = "PoseLib Browse Poses"; + ot->idname = "POSELIB_OT_browse_interactive"; + ot->description = "Interactively browse poses in 3D-View"; + + /* callbacks */ + ot->invoke = poselib_preview_invoke; + ot->modal = poselib_preview_modal; + ot->cancel = poselib_preview_cancel; + ot->exec = poselib_preview_exec; + ot->poll = has_poselib_pose_data_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING; + + /* properties */ + // TODO: make the pose_index into a proper enum instead of a cryptic int... + ot->prop = RNA_def_int( + ot->srna, + "pose_index", + -1, + -2, + INT_MAX, + "Pose", + "Index of the pose to apply (-2 for no change to pose, -1 for poselib active pose)", + 0, + INT_MAX); + + // XXX: percentage vs factor? + /* not used yet */ #if 0 - RNA_def_float_factor( - ot->srna, "blend_factor", 1.0f, 0.0f, 1.0f, "Blend Factor", - "Amount that the pose is applied on top of the existing poses", 0.0f, 1.0f); + RNA_def_float_factor( + ot->srna, "blend_factor", 1.0f, 0.0f, 1.0f, "Blend Factor", + "Amount that the pose is applied on top of the existing poses", 0.0f, 1.0f); #endif } void POSELIB_OT_apply_pose(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Apply Pose Library Pose"; - ot->idname = "POSELIB_OT_apply_pose"; - ot->description = "Apply specified Pose Library pose to the rig"; - - /* callbacks */ - ot->exec = poselib_preview_exec; - ot->poll = has_poselib_pose_data_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - /* TODO: make the pose_index into a proper enum instead of a cryptic int... */ - ot->prop = RNA_def_int(ot->srna, "pose_index", -1, -2, INT_MAX, "Pose", "Index of the pose to apply (-2 for no change to pose, -1 for poselib active pose)", 0, INT_MAX); + /* identifiers */ + ot->name = "Apply Pose Library Pose"; + ot->idname = "POSELIB_OT_apply_pose"; + ot->description = "Apply specified Pose Library pose to the rig"; + + /* callbacks */ + ot->exec = poselib_preview_exec; + ot->poll = has_poselib_pose_data_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + /* TODO: make the pose_index into a proper enum instead of a cryptic int... */ + ot->prop = RNA_def_int( + ot->srna, + "pose_index", + -1, + -2, + INT_MAX, + "Pose", + "Index of the pose to apply (-2 for no change to pose, -1 for poselib active pose)", + 0, + INT_MAX); } diff --git a/source/blender/editors/armature/pose_select.c b/source/blender/editors/armature/pose_select.c index 5ef61f0f6c6..b825d821fe8 100644 --- a/source/blender/editors/armature/pose_select.c +++ b/source/blender/editors/armature/pose_select.c @@ -63,172 +63,178 @@ #define PBONE_PREV_FLAG_GET(pchan) ((void)0, (POINTER_AS_INT((pchan)->temp))) #define PBONE_PREV_FLAG_SET(pchan, val) ((pchan)->temp = POINTER_FROM_INT(val)) - /* ***************** Pose Select Utilities ********************* */ /* Note: SEL_TOGGLE is assumed to have already been handled! */ static void pose_do_bone_select(bPoseChannel *pchan, const int select_mode) { - /* select pchan only if selectable, but deselect works always */ - switch (select_mode) { - case SEL_SELECT: - if (!(pchan->bone->flag & BONE_UNSELECTABLE)) - pchan->bone->flag |= BONE_SELECTED; - break; - case SEL_DESELECT: - pchan->bone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - break; - case SEL_INVERT: - if (pchan->bone->flag & BONE_SELECTED) { - pchan->bone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - } - else if (!(pchan->bone->flag & BONE_UNSELECTABLE)) { - pchan->bone->flag |= BONE_SELECTED; - } - break; - } + /* select pchan only if selectable, but deselect works always */ + switch (select_mode) { + case SEL_SELECT: + if (!(pchan->bone->flag & BONE_UNSELECTABLE)) + pchan->bone->flag |= BONE_SELECTED; + break; + case SEL_DESELECT: + pchan->bone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + break; + case SEL_INVERT: + if (pchan->bone->flag & BONE_SELECTED) { + pchan->bone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + } + else if (!(pchan->bone->flag & BONE_UNSELECTABLE)) { + pchan->bone->flag |= BONE_SELECTED; + } + break; + } } void ED_pose_bone_select_tag_update(Object *ob) { - BLI_assert(ob->type == OB_ARMATURE); - bArmature *arm = ob->data; - WM_main_add_notifier(NC_OBJECT | ND_BONE_SELECT, ob); - WM_main_add_notifier(NC_GEOM | ND_DATA, ob); + BLI_assert(ob->type == OB_ARMATURE); + bArmature *arm = ob->data; + WM_main_add_notifier(NC_OBJECT | ND_BONE_SELECT, ob); + WM_main_add_notifier(NC_GEOM | ND_DATA, ob); - if (arm->flag & ARM_HAS_VIZ_DEPS) { - /* mask modifier ('armature' mode), etc. */ - DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); - } + if (arm->flag & ARM_HAS_VIZ_DEPS) { + /* mask modifier ('armature' mode), etc. */ + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); + } - DEG_id_tag_update(&arm->id, ID_RECALC_SELECT); + DEG_id_tag_update(&arm->id, ID_RECALC_SELECT); } - /* Utility method for changing the selection status of a bone */ void ED_pose_bone_select(Object *ob, bPoseChannel *pchan, bool select) { - bArmature *arm; - - /* sanity checks */ - // XXX: actually, we can probably still get away with no object - at most we have no updates - if (ELEM(NULL, ob, ob->pose, pchan, pchan->bone)) - return; - - arm = ob->data; - - /* can only change selection state if bone can be modified */ - if (PBONE_SELECTABLE(arm, pchan->bone)) { - /* change selection state - activate too if selected */ - if (select) { - pchan->bone->flag |= BONE_SELECTED; - arm->act_bone = pchan->bone; - } - else { - pchan->bone->flag &= ~BONE_SELECTED; - arm->act_bone = NULL; - } - - // TODO: select and activate corresponding vgroup? - ED_pose_bone_select_tag_update(ob); - } + bArmature *arm; + + /* sanity checks */ + // XXX: actually, we can probably still get away with no object - at most we have no updates + if (ELEM(NULL, ob, ob->pose, pchan, pchan->bone)) + return; + + arm = ob->data; + + /* can only change selection state if bone can be modified */ + if (PBONE_SELECTABLE(arm, pchan->bone)) { + /* change selection state - activate too if selected */ + if (select) { + pchan->bone->flag |= BONE_SELECTED; + arm->act_bone = pchan->bone; + } + else { + pchan->bone->flag &= ~BONE_SELECTED; + arm->act_bone = NULL; + } + + // TODO: select and activate corresponding vgroup? + ED_pose_bone_select_tag_update(ob); + } } /* called from editview.c, for mode-less pose selection */ /* assumes scene obact and basact is still on old situation */ -bool ED_armature_pose_select_pick_with_buffer( - ViewLayer *view_layer, View3D *v3d, Base *base, const unsigned int *buffer, short hits, - bool extend, bool deselect, bool toggle, bool do_nearest) +bool ED_armature_pose_select_pick_with_buffer(ViewLayer *view_layer, + View3D *v3d, + Base *base, + const unsigned int *buffer, + short hits, + bool extend, + bool deselect, + bool toggle, + bool do_nearest) { - Object *ob = base->object; - Bone *nearBone; - - if (!ob || !ob->pose) return 0; - - Object *ob_act = OBACT(view_layer); - Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer); - - /* 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); - - /* if the bone cannot be affected, don't do anything */ - if ((nearBone) && !(nearBone->flag & BONE_UNSELECTABLE)) { - bArmature *arm = ob->data; - - /* since we do unified select, we don't shift+select a bone if the - * armature object was not active yet. - * note, special exception for armature mode so we can do multi-select - * we could check for multi-select explicitly but think its fine to - * always give predictable behavior in weight paint mode - campbell */ - if ((ob_act == NULL) || ((ob_act != ob) && (ob_act->mode & OB_MODE_WEIGHT_PAINT) == 0)) { - /* when we are entering into posemode via toggle-select, - * from another active object - always select the bone. */ - if (!extend && !deselect && toggle) { - /* re-select below */ - nearBone->flag &= ~BONE_SELECTED; - } - } - - if (!extend && !deselect && !toggle) { - { - uint bases_len = 0; - Base **bases = BKE_object_pose_base_array_get_unique(view_layer, v3d, &bases_len); - ED_pose_deselect_all_multi_ex(bases, bases_len, SEL_DESELECT, true); - MEM_freeN(bases); - } - nearBone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - arm->act_bone = nearBone; - } - else { - if (extend) { - nearBone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - arm->act_bone = nearBone; - } - else if (deselect) { - nearBone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - } - else if (toggle) { - if (nearBone->flag & BONE_SELECTED) { - /* if not active, we make it active */ - if (nearBone != arm->act_bone) { - arm->act_bone = nearBone; - } - else { - nearBone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - } - } - else { - nearBone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - arm->act_bone = nearBone; - } - } - } - - if (ob_act) { - /* in weightpaint we select the associated vertex group too */ - if (ob_act->mode & OB_MODE_WEIGHT_PAINT) { - if (nearBone == arm->act_bone) { - ED_vgroup_select_by_name(ob_act, nearBone->name); - DEG_id_tag_update(&ob_act->id, ID_RECALC_GEOMETRY); - } - } - /* if there are some dependencies for visualizing armature state - * (e.g. Mask Modifier in 'Armature' mode), force update - */ - else if (arm->flag & ARM_HAS_VIZ_DEPS) { - /* NOTE: ob not ob_act here is intentional - it's the source of the - * bones being selected [T37247] - */ - DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); - } - - /* tag armature for copy-on-write update (since act_bone is in armature not object) */ - DEG_id_tag_update(&arm->id, ID_RECALC_COPY_ON_WRITE); - } - } - - return nearBone != NULL; + Object *ob = base->object; + Bone *nearBone; + + if (!ob || !ob->pose) + return 0; + + Object *ob_act = OBACT(view_layer); + Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer); + + /* 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); + + /* if the bone cannot be affected, don't do anything */ + if ((nearBone) && !(nearBone->flag & BONE_UNSELECTABLE)) { + bArmature *arm = ob->data; + + /* since we do unified select, we don't shift+select a bone if the + * armature object was not active yet. + * note, special exception for armature mode so we can do multi-select + * we could check for multi-select explicitly but think its fine to + * always give predictable behavior in weight paint mode - campbell */ + if ((ob_act == NULL) || ((ob_act != ob) && (ob_act->mode & OB_MODE_WEIGHT_PAINT) == 0)) { + /* when we are entering into posemode via toggle-select, + * from another active object - always select the bone. */ + if (!extend && !deselect && toggle) { + /* re-select below */ + nearBone->flag &= ~BONE_SELECTED; + } + } + + if (!extend && !deselect && !toggle) { + { + uint bases_len = 0; + Base **bases = BKE_object_pose_base_array_get_unique(view_layer, v3d, &bases_len); + ED_pose_deselect_all_multi_ex(bases, bases_len, SEL_DESELECT, true); + MEM_freeN(bases); + } + nearBone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + arm->act_bone = nearBone; + } + else { + if (extend) { + nearBone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + arm->act_bone = nearBone; + } + else if (deselect) { + nearBone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + } + else if (toggle) { + if (nearBone->flag & BONE_SELECTED) { + /* if not active, we make it active */ + if (nearBone != arm->act_bone) { + arm->act_bone = nearBone; + } + else { + nearBone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + } + } + else { + nearBone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + arm->act_bone = nearBone; + } + } + } + + if (ob_act) { + /* in weightpaint we select the associated vertex group too */ + if (ob_act->mode & OB_MODE_WEIGHT_PAINT) { + if (nearBone == arm->act_bone) { + ED_vgroup_select_by_name(ob_act, nearBone->name); + DEG_id_tag_update(&ob_act->id, ID_RECALC_GEOMETRY); + } + } + /* if there are some dependencies for visualizing armature state + * (e.g. Mask Modifier in 'Armature' mode), force update + */ + else if (arm->flag & ARM_HAS_VIZ_DEPS) { + /* NOTE: ob not ob_act here is intentional - it's the source of the + * bones being selected [T37247] + */ + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); + } + + /* tag armature for copy-on-write update (since act_bone is in armature not object) */ + DEG_id_tag_update(&arm->id, ID_RECALC_COPY_ON_WRITE); + } + } + + return nearBone != NULL; } /* 'select_mode' is usual SEL_SELECT/SEL_DESELECT/SEL_TOGGLE/SEL_INVERT. @@ -236,340 +242,349 @@ bool ED_armature_pose_select_pick_with_buffer( * (hidden or on hidden layers). */ bool ED_pose_deselect_all(Object *ob, int select_mode, const bool ignore_visibility) { - bArmature *arm = ob->data; - bPoseChannel *pchan; - - /* we call this from outliner too */ - if (ob->pose == NULL) { - return false; - } - - /* Determine if we're selecting or deselecting */ - if (select_mode == SEL_TOGGLE) { - select_mode = SEL_SELECT; - for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { - if (ignore_visibility || PBONE_VISIBLE(arm, pchan->bone)) { - if (pchan->bone->flag & BONE_SELECTED) { - select_mode = SEL_DESELECT; - break; - } - } - } - } - - /* Set the flags accordingly */ - bool changed = false; - for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { - /* ignore the pchan if it isn't visible or if its selection cannot be changed */ - if (ignore_visibility || PBONE_VISIBLE(arm, pchan->bone)) { - int flag_prev = pchan->bone->flag; - pose_do_bone_select(pchan, select_mode); - changed = (changed || flag_prev != pchan->bone->flag); - } - } - return changed; + bArmature *arm = ob->data; + bPoseChannel *pchan; + + /* we call this from outliner too */ + if (ob->pose == NULL) { + return false; + } + + /* Determine if we're selecting or deselecting */ + if (select_mode == SEL_TOGGLE) { + select_mode = SEL_SELECT; + for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { + if (ignore_visibility || PBONE_VISIBLE(arm, pchan->bone)) { + if (pchan->bone->flag & BONE_SELECTED) { + select_mode = SEL_DESELECT; + break; + } + } + } + } + + /* Set the flags accordingly */ + bool changed = false; + for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { + /* ignore the pchan if it isn't visible or if its selection cannot be changed */ + if (ignore_visibility || PBONE_VISIBLE(arm, pchan->bone)) { + int flag_prev = pchan->bone->flag; + pose_do_bone_select(pchan, select_mode); + changed = (changed || flag_prev != pchan->bone->flag); + } + } + return changed; } 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) { - if (ignore_visibility || PBONE_VISIBLE(arm, pchan->bone)) { - if (pchan->bone->flag & BONE_SELECTED) { - return true; - } - } - } - return false; + bArmature *arm = ob->data; + for (bPoseChannel *pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { + if (ignore_visibility || PBONE_VISIBLE(arm, pchan->bone)) { + if (pchan->bone->flag & BONE_SELECTED) { + return true; + } + } + } + return false; } static bool ed_pose_is_any_selected_multi(Base **bases, uint bases_len, bool ignore_visibility) { - for (uint base_index = 0; base_index < bases_len; base_index++) { - Object *ob_iter = bases[base_index]->object; - if (ed_pose_is_any_selected(ob_iter, ignore_visibility)) { - return true; - } - } - return false; + for (uint base_index = 0; base_index < bases_len; base_index++) { + Object *ob_iter = bases[base_index]->object; + if (ed_pose_is_any_selected(ob_iter, ignore_visibility)) { + return true; + } + } + return false; } -bool ED_pose_deselect_all_multi_ex(Base **bases, uint bases_len, int select_mode, const bool ignore_visibility) +bool ED_pose_deselect_all_multi_ex(Base **bases, + uint bases_len, + int select_mode, + const bool ignore_visibility) { - if (select_mode == SEL_TOGGLE) { - select_mode = ed_pose_is_any_selected_multi( - bases, bases_len, ignore_visibility) ? SEL_DESELECT : SEL_SELECT; - } - - bool changed_multi = false; - for (uint base_index = 0; base_index < bases_len; base_index++) { - Object *ob_iter = bases[base_index]->object; - if (ED_pose_deselect_all(ob_iter, select_mode, ignore_visibility)) { - ED_pose_bone_select_tag_update(ob_iter); - changed_multi = true; - } - } - return changed_multi; + if (select_mode == SEL_TOGGLE) { + select_mode = ed_pose_is_any_selected_multi(bases, bases_len, ignore_visibility) ? + SEL_DESELECT : + SEL_SELECT; + } + + bool changed_multi = false; + for (uint base_index = 0; base_index < bases_len; base_index++) { + Object *ob_iter = bases[base_index]->object; + if (ED_pose_deselect_all(ob_iter, select_mode, ignore_visibility)) { + ED_pose_bone_select_tag_update(ob_iter); + changed_multi = true; + } + } + return changed_multi; } - bool ED_pose_deselect_all_multi(bContext *C, int select_mode, const bool ignore_visibility) { - ViewContext vc; - ED_view3d_viewcontext_init(C, &vc); - uint bases_len = 0; - Base **bases = BKE_view_layer_array_from_bases_in_mode(vc.view_layer, vc.v3d, &bases_len, {.object_mode = OB_MODE_POSE,}); - bool changed_multi = ED_pose_deselect_all_multi_ex(bases, bases_len, select_mode, ignore_visibility); - MEM_freeN(bases); - return changed_multi; + ViewContext vc; + ED_view3d_viewcontext_init(C, &vc); + uint bases_len = 0; + Base **bases = BKE_view_layer_array_from_bases_in_mode(vc.view_layer, + vc.v3d, + &bases_len, + { + .object_mode = OB_MODE_POSE, + }); + bool changed_multi = ED_pose_deselect_all_multi_ex( + bases, bases_len, select_mode, ignore_visibility); + MEM_freeN(bases); + return changed_multi; } /* ***************** Selections ********************** */ static void selectconnected_posebonechildren(Object *ob, Bone *bone, int extend) { - Bone *curBone; + Bone *curBone; - /* stop when unconnected child is encountered, or when unselectable bone is encountered */ - if (!(bone->flag & BONE_CONNECTED) || (bone->flag & BONE_UNSELECTABLE)) - return; + /* stop when unconnected child is encountered, or when unselectable bone is encountered */ + if (!(bone->flag & BONE_CONNECTED) || (bone->flag & BONE_UNSELECTABLE)) + return; - if (extend) - bone->flag &= ~BONE_SELECTED; - else - bone->flag |= BONE_SELECTED; + if (extend) + bone->flag &= ~BONE_SELECTED; + else + bone->flag |= BONE_SELECTED; - for (curBone = bone->childbase.first; curBone; curBone = curBone->next) - selectconnected_posebonechildren(ob, curBone, extend); + for (curBone = bone->childbase.first; curBone; curBone = curBone->next) + selectconnected_posebonechildren(ob, curBone, extend); } /* within active object context */ /* previously known as "selectconnected_posearmature" */ static int pose_select_connected_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - Bone *bone, *curBone, *next = NULL; - const bool extend = RNA_boolean_get(op->ptr, "extend"); - - view3d_operator_needs_opengl(C); - - Base *base = NULL; - bone = get_nearest_bone(C, event->mval, !extend, &base); - - if (!bone) - return OPERATOR_CANCELLED; - - /* Select parents */ - for (curBone = bone; curBone; curBone = next) { - /* ignore bone if cannot be selected */ - if ((curBone->flag & BONE_UNSELECTABLE) == 0) { - if (extend) - curBone->flag &= ~BONE_SELECTED; - else - curBone->flag |= BONE_SELECTED; - - if (curBone->flag & BONE_CONNECTED) - next = curBone->parent; - else - next = NULL; - } - else - next = NULL; - } - - /* Select children */ - for (curBone = bone->childbase.first; curBone; curBone = next) - selectconnected_posebonechildren(base->object, curBone, extend); - - ED_pose_bone_select_tag_update(base->object); - - return OPERATOR_FINISHED; + Bone *bone, *curBone, *next = NULL; + const bool extend = RNA_boolean_get(op->ptr, "extend"); + + view3d_operator_needs_opengl(C); + + Base *base = NULL; + bone = get_nearest_bone(C, event->mval, !extend, &base); + + if (!bone) + return OPERATOR_CANCELLED; + + /* Select parents */ + for (curBone = bone; curBone; curBone = next) { + /* ignore bone if cannot be selected */ + if ((curBone->flag & BONE_UNSELECTABLE) == 0) { + if (extend) + curBone->flag &= ~BONE_SELECTED; + else + curBone->flag |= BONE_SELECTED; + + if (curBone->flag & BONE_CONNECTED) + next = curBone->parent; + else + next = NULL; + } + else + next = NULL; + } + + /* Select children */ + for (curBone = bone->childbase.first; curBone; curBone = next) + selectconnected_posebonechildren(base->object, curBone, extend); + + ED_pose_bone_select_tag_update(base->object); + + return OPERATOR_FINISHED; } static bool pose_select_linked_poll(bContext *C) { - return (ED_operator_view3d_active(C) && ED_operator_posemode(C)); + return (ED_operator_view3d_active(C) && ED_operator_posemode(C)); } void POSE_OT_select_linked(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"; - - /* callbacks */ - /* leave 'exec' unset */ - ot->invoke = pose_select_connected_invoke; - ot->poll = pose_select_linked_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* props */ - RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend selection instead of deselecting everything first"); + /* identifiers */ + ot->name = "Select Connected"; + ot->idname = "POSE_OT_select_linked"; + ot->description = "Select bones related to selected ones by parent/child relationships"; + + /* callbacks */ + /* leave 'exec' unset */ + ot->invoke = pose_select_connected_invoke; + ot->poll = pose_select_linked_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* props */ + RNA_def_boolean(ot->srna, + "extend", + false, + "Extend", + "Extend selection instead of deselecting everything first"); } /* -------------------------------------- */ static int pose_de_select_all_exec(bContext *C, wmOperator *op) { - int action = RNA_enum_get(op->ptr, "action"); - - Scene *scene = CTX_data_scene(C); - int multipaint = scene->toolsettings->multipaint; - - if (action == SEL_TOGGLE) { - action = CTX_DATA_COUNT(C, selected_pose_bones) ? SEL_DESELECT : SEL_SELECT; - } - - Object *ob_prev = NULL; - - /* 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); - - if (ob_prev != ob) { - /* weightpaint or mask modifiers need depsgraph updates */ - if (multipaint || (arm->flag & ARM_HAS_VIZ_DEPS)) { - DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); - } - /* need to tag armature for cow updates, or else selection doesn't update */ - DEG_id_tag_update(&arm->id, ID_RECALC_COPY_ON_WRITE); - ob_prev = ob; - } - } - CTX_DATA_END; - - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, NULL); - - return OPERATOR_FINISHED; + int action = RNA_enum_get(op->ptr, "action"); + + Scene *scene = CTX_data_scene(C); + int multipaint = scene->toolsettings->multipaint; + + if (action == SEL_TOGGLE) { + action = CTX_DATA_COUNT(C, selected_pose_bones) ? SEL_DESELECT : SEL_SELECT; + } + + Object *ob_prev = NULL; + + /* 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); + + if (ob_prev != ob) { + /* weightpaint or mask modifiers need depsgraph updates */ + if (multipaint || (arm->flag & ARM_HAS_VIZ_DEPS)) { + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); + } + /* need to tag armature for cow updates, or else selection doesn't update */ + DEG_id_tag_update(&arm->id, ID_RECALC_COPY_ON_WRITE); + ob_prev = ob; + } + } + CTX_DATA_END; + + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, NULL); + + return OPERATOR_FINISHED; } void POSE_OT_select_all(wmOperatorType *ot) { - /* identifiers */ - ot->name = "(De)select All"; - ot->idname = "POSE_OT_select_all"; - ot->description = "Toggle selection status of all bones"; + /* identifiers */ + ot->name = "(De)select All"; + ot->idname = "POSE_OT_select_all"; + ot->description = "Toggle selection status of all bones"; - /* api callbacks */ - ot->exec = pose_de_select_all_exec; - ot->poll = ED_operator_posemode; + /* api callbacks */ + ot->exec = pose_de_select_all_exec; + ot->poll = ED_operator_posemode; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - WM_operator_properties_select_all(ot); + WM_operator_properties_select_all(ot); } /* -------------------------------------- */ static int pose_select_parent_exec(bContext *C, wmOperator *UNUSED(op)) { - Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); - bArmature *arm = (bArmature *)ob->data; - bPoseChannel *pchan, *parent; - - /* Determine if there is an active bone */ - pchan = CTX_data_active_pose_bone(C); - if (pchan) { - parent = pchan->parent; - if ((parent) && !(parent->bone->flag & (BONE_HIDDEN_P | BONE_UNSELECTABLE))) { - parent->bone->flag |= BONE_SELECTED; - arm->act_bone = parent->bone; - } - else { - return OPERATOR_CANCELLED; - } - } - else { - return OPERATOR_CANCELLED; - } - - ED_pose_bone_select_tag_update(ob); - return OPERATOR_FINISHED; + Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); + bArmature *arm = (bArmature *)ob->data; + bPoseChannel *pchan, *parent; + + /* Determine if there is an active bone */ + pchan = CTX_data_active_pose_bone(C); + if (pchan) { + parent = pchan->parent; + if ((parent) && !(parent->bone->flag & (BONE_HIDDEN_P | BONE_UNSELECTABLE))) { + parent->bone->flag |= BONE_SELECTED; + arm->act_bone = parent->bone; + } + else { + return OPERATOR_CANCELLED; + } + } + else { + return OPERATOR_CANCELLED; + } + + ED_pose_bone_select_tag_update(ob); + return OPERATOR_FINISHED; } void POSE_OT_select_parent(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Select Parent Bone"; - ot->idname = "POSE_OT_select_parent"; - ot->description = "Select bones that are parents of the currently selected bones"; + /* identifiers */ + ot->name = "Select Parent Bone"; + ot->idname = "POSE_OT_select_parent"; + ot->description = "Select bones that are parents of the currently selected bones"; - /* api callbacks */ - ot->exec = pose_select_parent_exec; - ot->poll = ED_operator_posemode; + /* api callbacks */ + ot->exec = pose_select_parent_exec; + ot->poll = ED_operator_posemode; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } /* -------------------------------------- */ static int pose_select_constraint_target_exec(bContext *C, wmOperator *UNUSED(op)) { - bConstraint *con; - int found = 0; - - CTX_DATA_BEGIN (C, bPoseChannel *, pchan, visible_pose_bones) - { - if (pchan->bone->flag & BONE_SELECTED) { - for (con = pchan->constraints.first; con; con = con->next) { - const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con); - ListBase targets = {NULL, NULL}; - bConstraintTarget *ct; - - if (cti && cti->get_constraint_targets) { - cti->get_constraint_targets(con, &targets); - - for (ct = targets.first; ct; ct = ct->next) { - Object *ob = ct->tar; - - /* Any armature that is also in pose mode should be selected. */ - if ((ct->subtarget[0] != '\0') && - (ob != NULL) && - (ob->type == OB_ARMATURE) && - (ob->mode == OB_MODE_POSE)) - { - bPoseChannel *pchanc = BKE_pose_channel_find_name(ob->pose, ct->subtarget); - if ((pchanc) && !(pchanc->bone->flag & BONE_UNSELECTABLE)) { - pchanc->bone->flag |= BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL; - ED_pose_bone_select_tag_update(ob); - found = 1; - } - } - } - - if (cti->flush_constraint_targets) - cti->flush_constraint_targets(con, &targets, 1); - } - } - } - } - CTX_DATA_END; - - if (!found) - return OPERATOR_CANCELLED; - - return OPERATOR_FINISHED; + bConstraint *con; + int found = 0; + + CTX_DATA_BEGIN (C, bPoseChannel *, pchan, visible_pose_bones) { + if (pchan->bone->flag & BONE_SELECTED) { + for (con = pchan->constraints.first; con; con = con->next) { + const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con); + ListBase targets = {NULL, NULL}; + bConstraintTarget *ct; + + if (cti && cti->get_constraint_targets) { + cti->get_constraint_targets(con, &targets); + + for (ct = targets.first; ct; ct = ct->next) { + Object *ob = ct->tar; + + /* Any armature that is also in pose mode should be selected. */ + if ((ct->subtarget[0] != '\0') && (ob != NULL) && (ob->type == OB_ARMATURE) && + (ob->mode == OB_MODE_POSE)) { + bPoseChannel *pchanc = BKE_pose_channel_find_name(ob->pose, ct->subtarget); + if ((pchanc) && !(pchanc->bone->flag & BONE_UNSELECTABLE)) { + pchanc->bone->flag |= BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL; + ED_pose_bone_select_tag_update(ob); + found = 1; + } + } + } + + if (cti->flush_constraint_targets) + cti->flush_constraint_targets(con, &targets, 1); + } + } + } + } + CTX_DATA_END; + + if (!found) + return OPERATOR_CANCELLED; + + return OPERATOR_FINISHED; } void POSE_OT_select_constraint_target(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Select Constraint Target"; - ot->idname = "POSE_OT_select_constraint_target"; - ot->description = "Select bones used as targets for the currently selected bones"; + /* identifiers */ + ot->name = "Select Constraint Target"; + ot->idname = "POSE_OT_select_constraint_target"; + ot->description = "Select bones used as targets for the currently selected bones"; - /* api callbacks */ - ot->exec = pose_select_constraint_target_exec; - ot->poll = ED_operator_posemode; + /* api callbacks */ + ot->exec = pose_select_constraint_target_exec; + ot->poll = ED_operator_posemode; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } /* -------------------------------------- */ @@ -578,444 +593,457 @@ void POSE_OT_select_constraint_target(wmOperatorType *ot) * selected we then keep the non-active objects untouched (selected/unselected). */ static int pose_select_hierarchy_exec(bContext *C, wmOperator *op) { - Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); - bArmature *arm = ob->data; - bPoseChannel *pchan_act; - int direction = RNA_enum_get(op->ptr, "direction"); - const bool add_to_sel = RNA_boolean_get(op->ptr, "extend"); - bool changed = false; - - pchan_act = BKE_pose_channel_active(ob); - if (pchan_act == NULL) { - return OPERATOR_CANCELLED; - } - - if (direction == BONE_SELECT_PARENT) { - if (pchan_act->parent) { - Bone *bone_parent; - bone_parent = pchan_act->parent->bone; - - if (PBONE_SELECTABLE(arm, bone_parent)) { - if (!add_to_sel) { - pchan_act->bone->flag &= ~BONE_SELECTED; - } - bone_parent->flag |= BONE_SELECTED; - arm->act_bone = bone_parent; - - changed = true; - } - } - } - else { /* direction == BONE_SELECT_CHILD */ - bPoseChannel *pchan_iter; - Bone *bone_child = NULL; - int pass; - - /* first pass, only connected bones (the logical direct child) */ - for (pass = 0; pass < 2 && (bone_child == NULL); pass++) { - for (pchan_iter = ob->pose->chanbase.first; pchan_iter; pchan_iter = pchan_iter->next) { - /* possible we have multiple children, some invisible */ - if (PBONE_SELECTABLE(arm, pchan_iter->bone)) { - if (pchan_iter->parent == pchan_act) { - if ((pass == 1) || (pchan_iter->bone->flag & BONE_CONNECTED)) { - bone_child = pchan_iter->bone; - break; - } - } - } - } - } - - if (bone_child) { - arm->act_bone = bone_child; - - if (!add_to_sel) { - pchan_act->bone->flag &= ~BONE_SELECTED; - } - bone_child->flag |= BONE_SELECTED; - - changed = true; - } - } - - if (changed == false) { - return OPERATOR_CANCELLED; - } - - ED_pose_bone_select_tag_update(ob); - - return OPERATOR_FINISHED; + Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); + bArmature *arm = ob->data; + bPoseChannel *pchan_act; + int direction = RNA_enum_get(op->ptr, "direction"); + const bool add_to_sel = RNA_boolean_get(op->ptr, "extend"); + bool changed = false; + + pchan_act = BKE_pose_channel_active(ob); + if (pchan_act == NULL) { + return OPERATOR_CANCELLED; + } + + if (direction == BONE_SELECT_PARENT) { + if (pchan_act->parent) { + Bone *bone_parent; + bone_parent = pchan_act->parent->bone; + + if (PBONE_SELECTABLE(arm, bone_parent)) { + if (!add_to_sel) { + pchan_act->bone->flag &= ~BONE_SELECTED; + } + bone_parent->flag |= BONE_SELECTED; + arm->act_bone = bone_parent; + + changed = true; + } + } + } + else { /* direction == BONE_SELECT_CHILD */ + bPoseChannel *pchan_iter; + Bone *bone_child = NULL; + int pass; + + /* first pass, only connected bones (the logical direct child) */ + for (pass = 0; pass < 2 && (bone_child == NULL); pass++) { + for (pchan_iter = ob->pose->chanbase.first; pchan_iter; pchan_iter = pchan_iter->next) { + /* possible we have multiple children, some invisible */ + if (PBONE_SELECTABLE(arm, pchan_iter->bone)) { + if (pchan_iter->parent == pchan_act) { + if ((pass == 1) || (pchan_iter->bone->flag & BONE_CONNECTED)) { + bone_child = pchan_iter->bone; + break; + } + } + } + } + } + + if (bone_child) { + arm->act_bone = bone_child; + + if (!add_to_sel) { + pchan_act->bone->flag &= ~BONE_SELECTED; + } + bone_child->flag |= BONE_SELECTED; + + changed = true; + } + } + + if (changed == false) { + return OPERATOR_CANCELLED; + } + + ED_pose_bone_select_tag_update(ob); + + return OPERATOR_FINISHED; } void POSE_OT_select_hierarchy(wmOperatorType *ot) { - static const EnumPropertyItem direction_items[] = { - {BONE_SELECT_PARENT, "PARENT", 0, "Select Parent", ""}, - {BONE_SELECT_CHILD, "CHILD", 0, "Select Child", ""}, - {0, NULL, 0, NULL, NULL}, - }; - - /* identifiers */ - ot->name = "Select Hierarchy"; - ot->idname = "POSE_OT_select_hierarchy"; - ot->description = "Select immediate parent/children of selected bones"; - - /* api callbacks */ - ot->exec = pose_select_hierarchy_exec; - ot->poll = ED_operator_posemode; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* props */ - ot->prop = RNA_def_enum(ot->srna, "direction", direction_items, BONE_SELECT_PARENT, "Direction", ""); - RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend the selection"); + static const EnumPropertyItem direction_items[] = { + {BONE_SELECT_PARENT, "PARENT", 0, "Select Parent", ""}, + {BONE_SELECT_CHILD, "CHILD", 0, "Select Child", ""}, + {0, NULL, 0, NULL, NULL}, + }; + + /* identifiers */ + ot->name = "Select Hierarchy"; + ot->idname = "POSE_OT_select_hierarchy"; + ot->description = "Select immediate parent/children of selected bones"; + + /* api callbacks */ + ot->exec = pose_select_hierarchy_exec; + ot->poll = ED_operator_posemode; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* props */ + ot->prop = RNA_def_enum( + ot->srna, "direction", direction_items, BONE_SELECT_PARENT, "Direction", ""); + RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend the selection"); } /* -------------------------------------- */ /* modes for select same */ typedef enum ePose_SelectSame_Mode { - POSE_SEL_SAME_LAYER = 0, - POSE_SEL_SAME_GROUP = 1, - POSE_SEL_SAME_KEYINGSET = 2, + POSE_SEL_SAME_LAYER = 0, + POSE_SEL_SAME_GROUP = 1, + POSE_SEL_SAME_KEYINGSET = 2, } ePose_SelectSame_Mode; static bool pose_select_same_group(bContext *C, bool extend) { - ViewLayer *view_layer = CTX_data_view_layer(C); - bool *group_flags_array; - bool *group_flags = NULL; - int groups_len = 0; - bool changed = false, tagged = false; - Object *ob_prev = NULL; - uint ob_index; - - uint objects_len = 0; - Object **objects = BKE_view_layer_array_from_objects_in_mode_unique_data(view_layer, CTX_wm_view3d(C), &objects_len, OB_MODE_POSE); - for (ob_index = 0; ob_index < objects_len; ob_index++) { - Object *ob = BKE_object_pose_armature_get(objects[ob_index]); - bArmature *arm = (ob) ? ob->data : NULL; - bPose *pose = (ob) ? ob->pose : NULL; - - /* Sanity checks. */ - if (ELEM(NULL, ob, pose, arm)) { - continue; - } - - ob->id.tag &= ~LIB_TAG_DOIT; - groups_len = MAX2(groups_len, BLI_listbase_count(&pose->agroups)); - } - - /* Nothing to do here. */ - if (groups_len == 0) { - MEM_freeN(objects); - return false; - } - - /* alloc a small array to keep track of the groups to use - * - each cell stores on/off state for whether group should be used - * - size is (groups_len + 1), since (index = 0) is used for no-group - */ - groups_len++; - group_flags_array = MEM_callocN(objects_len * groups_len * sizeof(bool), "pose_select_same_group"); - - group_flags = NULL; - ob_index = -1; - ob_prev = NULL; - CTX_DATA_BEGIN_WITH_ID (C, bPoseChannel *, pchan, visible_pose_bones, Object, *ob) - { - if (ob != ob_prev) { - ob_index++; - group_flags = group_flags_array + (ob_index * groups_len); - ob_prev = ob; - } - - /* keep track of group as group to use later? */ - if (pchan->bone->flag & BONE_SELECTED) { - group_flags[pchan->agrp_index] = true; - tagged = true; - } - - /* deselect all bones before selecting new ones? */ - if ((extend == false) && (pchan->bone->flag & BONE_UNSELECTABLE) == 0) { - pchan->bone->flag &= ~BONE_SELECTED; - } - } - CTX_DATA_END; - - /* small optimization: only loop through bones a second time if there are any groups tagged */ - if (tagged) { - group_flags = NULL; - ob_index = -1; - ob_prev = NULL; - /* only if group matches (and is not selected or current bone) */ - CTX_DATA_BEGIN_WITH_ID (C, bPoseChannel *, pchan, visible_pose_bones, Object *, ob) - { - if (ob != ob_prev) { - ob_index++; - group_flags = group_flags_array + (ob_index * groups_len); - ob_prev = ob; - } - - if ((pchan->bone->flag & BONE_UNSELECTABLE) == 0) { - /* check if the group used by this bone is counted */ - if (group_flags[pchan->agrp_index]) { - pchan->bone->flag |= BONE_SELECTED; - ob->id.tag |= LIB_TAG_DOIT; - } - } - } - CTX_DATA_END; - } - - for (ob_index = 0; ob_index < objects_len; ob_index++) { - Object *ob = objects[ob_index]; - if (ob->id.tag & LIB_TAG_DOIT) { - ED_pose_bone_select_tag_update(ob); - changed = true; - } - } - - /* Cleanup. */ - MEM_freeN(group_flags_array); - MEM_freeN(objects); - - return changed; + ViewLayer *view_layer = CTX_data_view_layer(C); + bool *group_flags_array; + bool *group_flags = NULL; + int groups_len = 0; + bool changed = false, tagged = false; + Object *ob_prev = NULL; + uint ob_index; + + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_mode_unique_data( + view_layer, CTX_wm_view3d(C), &objects_len, OB_MODE_POSE); + for (ob_index = 0; ob_index < objects_len; ob_index++) { + Object *ob = BKE_object_pose_armature_get(objects[ob_index]); + bArmature *arm = (ob) ? ob->data : NULL; + bPose *pose = (ob) ? ob->pose : NULL; + + /* Sanity checks. */ + if (ELEM(NULL, ob, pose, arm)) { + continue; + } + + ob->id.tag &= ~LIB_TAG_DOIT; + groups_len = MAX2(groups_len, BLI_listbase_count(&pose->agroups)); + } + + /* Nothing to do here. */ + if (groups_len == 0) { + MEM_freeN(objects); + return false; + } + + /* alloc a small array to keep track of the groups to use + * - each cell stores on/off state for whether group should be used + * - size is (groups_len + 1), since (index = 0) is used for no-group + */ + groups_len++; + group_flags_array = MEM_callocN(objects_len * groups_len * sizeof(bool), + "pose_select_same_group"); + + group_flags = NULL; + ob_index = -1; + ob_prev = NULL; + CTX_DATA_BEGIN_WITH_ID(C, bPoseChannel *, pchan, visible_pose_bones, Object, *ob) + { + if (ob != ob_prev) { + ob_index++; + group_flags = group_flags_array + (ob_index * groups_len); + ob_prev = ob; + } + + /* keep track of group as group to use later? */ + if (pchan->bone->flag & BONE_SELECTED) { + group_flags[pchan->agrp_index] = true; + tagged = true; + } + + /* deselect all bones before selecting new ones? */ + if ((extend == false) && (pchan->bone->flag & BONE_UNSELECTABLE) == 0) { + pchan->bone->flag &= ~BONE_SELECTED; + } + } + CTX_DATA_END; + + /* small optimization: only loop through bones a second time if there are any groups tagged */ + if (tagged) { + group_flags = NULL; + ob_index = -1; + ob_prev = NULL; + /* only if group matches (and is not selected or current bone) */ + CTX_DATA_BEGIN_WITH_ID(C, bPoseChannel *, pchan, visible_pose_bones, Object *, ob) + { + if (ob != ob_prev) { + ob_index++; + group_flags = group_flags_array + (ob_index * groups_len); + ob_prev = ob; + } + + if ((pchan->bone->flag & BONE_UNSELECTABLE) == 0) { + /* check if the group used by this bone is counted */ + if (group_flags[pchan->agrp_index]) { + pchan->bone->flag |= BONE_SELECTED; + ob->id.tag |= LIB_TAG_DOIT; + } + } + } + CTX_DATA_END; + } + + for (ob_index = 0; ob_index < objects_len; ob_index++) { + Object *ob = objects[ob_index]; + if (ob->id.tag & LIB_TAG_DOIT) { + ED_pose_bone_select_tag_update(ob); + changed = true; + } + } + + /* Cleanup. */ + MEM_freeN(group_flags_array); + MEM_freeN(objects); + + return changed; } static bool pose_select_same_layer(bContext *C, bool extend) { - ViewLayer *view_layer = CTX_data_view_layer(C); - int *layers_array, *layers = NULL; - Object *ob_prev = NULL; - uint ob_index; - bool changed = false; - - uint objects_len = 0; - Object **objects = BKE_view_layer_array_from_objects_in_mode_unique_data(view_layer, CTX_wm_view3d(C), &objects_len, OB_MODE_POSE); - for (ob_index = 0; ob_index < objects_len; ob_index++) { - Object *ob = objects[ob_index]; - ob->id.tag &= ~LIB_TAG_DOIT; - } - - layers_array = MEM_callocN(objects_len * sizeof(*layers_array), "pose_select_same_layer"); - - /* Figure out what bones are selected. */ - layers = NULL; - ob_prev = NULL; - ob_index = -1; - CTX_DATA_BEGIN_WITH_ID (C, bPoseChannel *, pchan, visible_pose_bones, Object *, ob) - { - if (ob != ob_prev) { - layers = &layers_array[++ob_index]; - ob_prev = ob; - } - - /* Keep track of layers to use later? */ - if (pchan->bone->flag & BONE_SELECTED) - *layers |= pchan->bone->layer; - - /* Deselect all bones before selecting new ones? */ - if ((extend == false) && (pchan->bone->flag & BONE_UNSELECTABLE) == 0) - pchan->bone->flag &= ~BONE_SELECTED; - } - CTX_DATA_END; - - bool any_layer = false; - for (ob_index = 0; ob_index < objects_len; ob_index++) { - if (layers_array[ob_index]) { - any_layer = true; - break; - } - } - - if (!any_layer) { - goto cleanup; - } - - /* Select bones that are on same layers as layers flag. */ - ob_prev = NULL; - ob_index = -1; - CTX_DATA_BEGIN_WITH_ID (C, bPoseChannel *, pchan, visible_pose_bones, Object *, ob) - { - if (ob != ob_prev) { - layers = &layers_array[++ob_index]; - ob_prev = ob; - } - - /* if bone is on a suitable layer, and the bone can have its selection changed, select it */ - if ((*layers & pchan->bone->layer) && (pchan->bone->flag & BONE_UNSELECTABLE) == 0) { - pchan->bone->flag |= BONE_SELECTED; - ob->id.tag |= LIB_TAG_DOIT; - } - } - CTX_DATA_END; - - for (ob_index = 0; ob_index < objects_len; ob_index++) { - Object *ob = objects[ob_index]; - if (ob->id.tag & LIB_TAG_DOIT) { - ED_pose_bone_select_tag_update(ob); - changed = true; - } - } + ViewLayer *view_layer = CTX_data_view_layer(C); + int *layers_array, *layers = NULL; + Object *ob_prev = NULL; + uint ob_index; + bool changed = false; + + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_mode_unique_data( + view_layer, CTX_wm_view3d(C), &objects_len, OB_MODE_POSE); + for (ob_index = 0; ob_index < objects_len; ob_index++) { + Object *ob = objects[ob_index]; + ob->id.tag &= ~LIB_TAG_DOIT; + } + + layers_array = MEM_callocN(objects_len * sizeof(*layers_array), "pose_select_same_layer"); + + /* Figure out what bones are selected. */ + layers = NULL; + ob_prev = NULL; + ob_index = -1; + CTX_DATA_BEGIN_WITH_ID(C, bPoseChannel *, pchan, visible_pose_bones, Object *, ob) + { + if (ob != ob_prev) { + layers = &layers_array[++ob_index]; + ob_prev = ob; + } + + /* Keep track of layers to use later? */ + if (pchan->bone->flag & BONE_SELECTED) + *layers |= pchan->bone->layer; + + /* Deselect all bones before selecting new ones? */ + if ((extend == false) && (pchan->bone->flag & BONE_UNSELECTABLE) == 0) + pchan->bone->flag &= ~BONE_SELECTED; + } + CTX_DATA_END; + + bool any_layer = false; + for (ob_index = 0; ob_index < objects_len; ob_index++) { + if (layers_array[ob_index]) { + any_layer = true; + break; + } + } + + if (!any_layer) { + goto cleanup; + } + + /* Select bones that are on same layers as layers flag. */ + ob_prev = NULL; + ob_index = -1; + CTX_DATA_BEGIN_WITH_ID(C, bPoseChannel *, pchan, visible_pose_bones, Object *, ob) + { + if (ob != ob_prev) { + layers = &layers_array[++ob_index]; + ob_prev = ob; + } + + /* if bone is on a suitable layer, and the bone can have its selection changed, select it */ + if ((*layers & pchan->bone->layer) && (pchan->bone->flag & BONE_UNSELECTABLE) == 0) { + pchan->bone->flag |= BONE_SELECTED; + ob->id.tag |= LIB_TAG_DOIT; + } + } + CTX_DATA_END; + + for (ob_index = 0; ob_index < objects_len; ob_index++) { + Object *ob = objects[ob_index]; + if (ob->id.tag & LIB_TAG_DOIT) { + ED_pose_bone_select_tag_update(ob); + changed = true; + } + } cleanup: - /* Cleanup. */ - MEM_freeN(layers_array); - MEM_freeN(objects); + /* Cleanup. */ + MEM_freeN(layers_array); + MEM_freeN(objects); - return changed; + return changed; } static bool pose_select_same_keyingset(bContext *C, ReportList *reports, bool extend) { - ViewLayer *view_layer = CTX_data_view_layer(C); - bool changed_multi = false; - KeyingSet *ks = ANIM_scene_get_active_keyingset(CTX_data_scene(C)); - KS_Path *ksp; - - /* sanity checks: validate Keying Set and object */ - if (ks == NULL) { - BKE_report(reports, RPT_ERROR, "No active Keying Set to use"); - return false; - } - else if (ANIM_validate_keyingset(C, NULL, ks) != 0) { - if (ks->paths.first == NULL) { - if ((ks->flag & KEYINGSET_ABSOLUTE) == 0) { - BKE_report(reports, RPT_ERROR, - "Use another Keying Set, as the active one depends on the currently " - "selected items or cannot find any targets due to unsuitable context"); - } - else { - BKE_report(reports, RPT_ERROR, "Keying Set does not contain any paths"); - } - } - return false; - } - - /* if not extending selection, deselect all selected first */ - if (extend == false) { - CTX_DATA_BEGIN (C, bPoseChannel *, pchan, visible_pose_bones) - { - if ((pchan->bone->flag & BONE_UNSELECTABLE) == 0) - pchan->bone->flag &= ~BONE_SELECTED; - } - CTX_DATA_END; - } - - uint objects_len = 0; - Object **objects = BKE_view_layer_array_from_objects_in_mode_unique_data(view_layer, CTX_wm_view3d(C), &objects_len, OB_MODE_POSE); - for (uint ob_index = 0; ob_index < objects_len; ob_index++) { - Object *ob = BKE_object_pose_armature_get(objects[ob_index]); - bArmature *arm = (ob) ? ob->data : NULL; - bPose *pose = (ob) ? ob->pose : NULL; - bool changed = false; - - /* Sanity checks. */ - if (ELEM(NULL, ob, pose, arm)) { - continue; - } - - /* iterate over elements in the Keying Set, setting selection depending on whether - * that bone is visible or not... - */ - for (ksp = ks->paths.first; ksp; ksp = ksp->next) { - /* only items related to this object will be relevant */ - if ((ksp->id == &ob->id) && (ksp->rna_path != NULL)) { - if (strstr(ksp->rna_path, "bones")) { - char *boneName = BLI_str_quoted_substrN(ksp->rna_path, "bones["); - - if (boneName) { - bPoseChannel *pchan = BKE_pose_channel_find_name(pose, boneName); - - if (pchan) { - /* select if bone is visible and can be affected */ - if (PBONE_SELECTABLE(arm, pchan->bone)) { - pchan->bone->flag |= BONE_SELECTED; - changed = true; - } - } - - /* free temp memory */ - MEM_freeN(boneName); - } - } - } - } - - if (changed || !extend) { - ED_pose_bone_select_tag_update(ob); - changed_multi = true; - } - } - MEM_freeN(objects); - - return changed_multi; + ViewLayer *view_layer = CTX_data_view_layer(C); + bool changed_multi = false; + KeyingSet *ks = ANIM_scene_get_active_keyingset(CTX_data_scene(C)); + KS_Path *ksp; + + /* sanity checks: validate Keying Set and object */ + if (ks == NULL) { + BKE_report(reports, RPT_ERROR, "No active Keying Set to use"); + return false; + } + else if (ANIM_validate_keyingset(C, NULL, ks) != 0) { + if (ks->paths.first == NULL) { + if ((ks->flag & KEYINGSET_ABSOLUTE) == 0) { + BKE_report(reports, + RPT_ERROR, + "Use another Keying Set, as the active one depends on the currently " + "selected items or cannot find any targets due to unsuitable context"); + } + else { + BKE_report(reports, RPT_ERROR, "Keying Set does not contain any paths"); + } + } + return false; + } + + /* if not extending selection, deselect all selected first */ + if (extend == false) { + CTX_DATA_BEGIN (C, bPoseChannel *, pchan, visible_pose_bones) { + if ((pchan->bone->flag & BONE_UNSELECTABLE) == 0) + pchan->bone->flag &= ~BONE_SELECTED; + } + CTX_DATA_END; + } + + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_mode_unique_data( + view_layer, CTX_wm_view3d(C), &objects_len, OB_MODE_POSE); + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *ob = BKE_object_pose_armature_get(objects[ob_index]); + bArmature *arm = (ob) ? ob->data : NULL; + bPose *pose = (ob) ? ob->pose : NULL; + bool changed = false; + + /* Sanity checks. */ + if (ELEM(NULL, ob, pose, arm)) { + continue; + } + + /* iterate over elements in the Keying Set, setting selection depending on whether + * that bone is visible or not... + */ + for (ksp = ks->paths.first; ksp; ksp = ksp->next) { + /* only items related to this object will be relevant */ + if ((ksp->id == &ob->id) && (ksp->rna_path != NULL)) { + if (strstr(ksp->rna_path, "bones")) { + char *boneName = BLI_str_quoted_substrN(ksp->rna_path, "bones["); + + if (boneName) { + bPoseChannel *pchan = BKE_pose_channel_find_name(pose, boneName); + + if (pchan) { + /* select if bone is visible and can be affected */ + if (PBONE_SELECTABLE(arm, pchan->bone)) { + pchan->bone->flag |= BONE_SELECTED; + changed = true; + } + } + + /* free temp memory */ + MEM_freeN(boneName); + } + } + } + } + + if (changed || !extend) { + ED_pose_bone_select_tag_update(ob); + changed_multi = true; + } + } + MEM_freeN(objects); + + return changed_multi; } static int pose_select_grouped_exec(bContext *C, wmOperator *op) { - Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); - const ePose_SelectSame_Mode type = RNA_enum_get(op->ptr, "type"); - const bool extend = RNA_boolean_get(op->ptr, "extend"); - bool changed = false; - - /* sanity check */ - if (ob->pose == NULL) - return OPERATOR_CANCELLED; - - /* selection types */ - switch (type) { - case POSE_SEL_SAME_LAYER: /* layer */ - changed = pose_select_same_layer(C, extend); - break; - - case POSE_SEL_SAME_GROUP: /* group */ - changed = pose_select_same_group(C, extend); - break; - - case POSE_SEL_SAME_KEYINGSET: /* Keying Set */ - changed = pose_select_same_keyingset(C, op->reports, extend); - break; - - default: - printf("pose_select_grouped() - Unknown selection type %u\n", type); - break; - } - - /* report done status */ - if (changed) - return OPERATOR_FINISHED; - else - return OPERATOR_CANCELLED; + Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); + const ePose_SelectSame_Mode type = RNA_enum_get(op->ptr, "type"); + const bool extend = RNA_boolean_get(op->ptr, "extend"); + bool changed = false; + + /* sanity check */ + if (ob->pose == NULL) + return OPERATOR_CANCELLED; + + /* selection types */ + switch (type) { + case POSE_SEL_SAME_LAYER: /* layer */ + changed = pose_select_same_layer(C, extend); + break; + + case POSE_SEL_SAME_GROUP: /* group */ + changed = pose_select_same_group(C, extend); + break; + + case POSE_SEL_SAME_KEYINGSET: /* Keying Set */ + changed = pose_select_same_keyingset(C, op->reports, extend); + break; + + default: + printf("pose_select_grouped() - Unknown selection type %u\n", type); + break; + } + + /* report done status */ + if (changed) + return OPERATOR_FINISHED; + else + return OPERATOR_CANCELLED; } void POSE_OT_select_grouped(wmOperatorType *ot) { - static const EnumPropertyItem prop_select_grouped_types[] = { - {POSE_SEL_SAME_LAYER, "LAYER", 0, "Layer", "Shared layers"}, - {POSE_SEL_SAME_GROUP, "GROUP", 0, "Group", "Shared group"}, - {POSE_SEL_SAME_KEYINGSET, "KEYINGSET", 0, "Keying Set", "All bones affected by active Keying Set"}, - {0, NULL, 0, NULL, NULL}, - }; - - /* identifiers */ - ot->name = "Select Grouped"; - ot->description = "Select all visible bones grouped by similar properties"; - ot->idname = "POSE_OT_select_grouped"; - - /* api callbacks */ - ot->invoke = WM_menu_invoke; - ot->exec = pose_select_grouped_exec; - ot->poll = ED_operator_posemode; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend selection instead of deselecting everything first"); - ot->prop = RNA_def_enum(ot->srna, "type", prop_select_grouped_types, 0, "Type", ""); + static const EnumPropertyItem prop_select_grouped_types[] = { + {POSE_SEL_SAME_LAYER, "LAYER", 0, "Layer", "Shared layers"}, + {POSE_SEL_SAME_GROUP, "GROUP", 0, "Group", "Shared group"}, + {POSE_SEL_SAME_KEYINGSET, + "KEYINGSET", + 0, + "Keying Set", + "All bones affected by active Keying Set"}, + {0, NULL, 0, NULL, NULL}, + }; + + /* identifiers */ + ot->name = "Select Grouped"; + ot->description = "Select all visible bones grouped by similar properties"; + ot->idname = "POSE_OT_select_grouped"; + + /* api callbacks */ + ot->invoke = WM_menu_invoke; + ot->exec = pose_select_grouped_exec; + ot->poll = ED_operator_posemode; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + RNA_def_boolean(ot->srna, + "extend", + false, + "Extend", + "Extend selection instead of deselecting everything first"); + ot->prop = RNA_def_enum(ot->srna, "type", prop_select_grouped_types, 0, "Type", ""); } /* -------------------------------------- */ @@ -1025,85 +1053,87 @@ void POSE_OT_select_grouped(wmOperatorType *ot) */ static int pose_select_mirror_exec(bContext *C, wmOperator *op) { - ViewLayer *view_layer = CTX_data_view_layer(C); - Object *ob_active = CTX_data_active_object(C); - - const bool is_weight_paint = (ob_active->mode & OB_MODE_WEIGHT_PAINT) != 0; - const bool active_only = RNA_boolean_get(op->ptr, "only_active"); - const bool extend = RNA_boolean_get(op->ptr, "extend"); - - uint objects_len = 0; - Object **objects = BKE_view_layer_array_from_objects_in_mode_unique_data(view_layer, CTX_wm_view3d(C), &objects_len, OB_MODE_POSE); - for (uint ob_index = 0; ob_index < objects_len; ob_index++) { - Object *ob = objects[ob_index]; - bArmature *arm = ob->data; - bPoseChannel *pchan, *pchan_mirror_act = NULL; - - for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { - const int flag = (pchan->bone->flag & BONE_SELECTED); - PBONE_PREV_FLAG_SET(pchan, flag); - } - - for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { - if (PBONE_SELECTABLE(arm, pchan->bone)) { - bPoseChannel *pchan_mirror; - int flag_new = extend ? PBONE_PREV_FLAG_GET(pchan) : 0; - - if ((pchan_mirror = BKE_pose_channel_get_mirrored(ob->pose, pchan->name)) && - (PBONE_VISIBLE(arm, pchan_mirror->bone))) - { - const int flag_mirror = PBONE_PREV_FLAG_GET(pchan_mirror); - flag_new |= flag_mirror; - - if (pchan->bone == arm->act_bone) { - pchan_mirror_act = pchan_mirror; - } - - /* Skip all but the active or its mirror. */ - if (active_only && !ELEM(arm->act_bone, pchan->bone, pchan_mirror->bone)) { - continue; - } - } - - pchan->bone->flag = (pchan->bone->flag & ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL)) | flag_new; - } - } - - if (pchan_mirror_act) { - arm->act_bone = pchan_mirror_act->bone; - - /* In weightpaint we select the associated vertex group too. */ - if (is_weight_paint) { - ED_vgroup_select_by_name(ob, pchan_mirror_act->name); - DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); - } - } - - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); - - /* Need to tag armature for cow updates, or else selection doesn't update. */ - DEG_id_tag_update(&arm->id, ID_RECALC_COPY_ON_WRITE); - } - MEM_freeN(objects); - - return OPERATOR_FINISHED; + ViewLayer *view_layer = CTX_data_view_layer(C); + Object *ob_active = CTX_data_active_object(C); + + const bool is_weight_paint = (ob_active->mode & OB_MODE_WEIGHT_PAINT) != 0; + const bool active_only = RNA_boolean_get(op->ptr, "only_active"); + const bool extend = RNA_boolean_get(op->ptr, "extend"); + + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_mode_unique_data( + view_layer, CTX_wm_view3d(C), &objects_len, OB_MODE_POSE); + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *ob = objects[ob_index]; + bArmature *arm = ob->data; + bPoseChannel *pchan, *pchan_mirror_act = NULL; + + for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { + const int flag = (pchan->bone->flag & BONE_SELECTED); + PBONE_PREV_FLAG_SET(pchan, flag); + } + + for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { + if (PBONE_SELECTABLE(arm, pchan->bone)) { + bPoseChannel *pchan_mirror; + int flag_new = extend ? PBONE_PREV_FLAG_GET(pchan) : 0; + + if ((pchan_mirror = BKE_pose_channel_get_mirrored(ob->pose, pchan->name)) && + (PBONE_VISIBLE(arm, pchan_mirror->bone))) { + const int flag_mirror = PBONE_PREV_FLAG_GET(pchan_mirror); + flag_new |= flag_mirror; + + if (pchan->bone == arm->act_bone) { + pchan_mirror_act = pchan_mirror; + } + + /* Skip all but the active or its mirror. */ + if (active_only && !ELEM(arm->act_bone, pchan->bone, pchan_mirror->bone)) { + continue; + } + } + + pchan->bone->flag = (pchan->bone->flag & ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL)) | + flag_new; + } + } + + if (pchan_mirror_act) { + arm->act_bone = pchan_mirror_act->bone; + + /* In weightpaint we select the associated vertex group too. */ + if (is_weight_paint) { + ED_vgroup_select_by_name(ob, pchan_mirror_act->name); + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); + } + } + + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); + + /* Need to tag armature for cow updates, or else selection doesn't update. */ + DEG_id_tag_update(&arm->id, ID_RECALC_COPY_ON_WRITE); + } + MEM_freeN(objects); + + return OPERATOR_FINISHED; } void POSE_OT_select_mirror(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Flip Active/Selected Bone"; - ot->idname = "POSE_OT_select_mirror"; - ot->description = "Mirror the bone selection"; - - /* api callbacks */ - ot->exec = pose_select_mirror_exec; - ot->poll = ED_operator_posemode; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - RNA_def_boolean(ot->srna, "only_active", false, "Active Only", "Only operate on the active bone"); - RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend the selection"); + /* identifiers */ + ot->name = "Flip Active/Selected Bone"; + ot->idname = "POSE_OT_select_mirror"; + ot->description = "Mirror the bone selection"; + + /* api callbacks */ + ot->exec = pose_select_mirror_exec; + ot->poll = ED_operator_posemode; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + RNA_def_boolean( + ot->srna, "only_active", false, "Active Only", "Only operate on the active bone"); + RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend the selection"); } diff --git a/source/blender/editors/armature/pose_slide.c b/source/blender/editors/armature/pose_slide.c index 90de331b3e0..cb9cfa64181 100644 --- a/source/blender/editors/armature/pose_slide.c +++ b/source/blender/editors/armature/pose_slide.c @@ -81,102 +81,104 @@ /* Temporary data shared between these operators */ typedef struct tPoseSlideOp { - /** current scene */ - Scene *scene; - /** area that we're operating in (needed for modal()) */ - ScrArea *sa; - /** region that we're operating in (needed for modal()) */ - ARegion *ar; - /** len of the PoseSlideObject array. */ - uint objects_len; - - /** links between posechannels and f-curves for all the pose objects. */ - ListBase pfLinks; - /** binary tree for quicker searching for keyframes (when applicable) */ - DLRBT_Tree keys; - - /** current frame number - global time */ - int cframe; - - /** frame before current frame (blend-from) - global time */ - int prevFrame; - /** frame after current frame (blend-to) - global time */ - int nextFrame; - - /** sliding mode (ePoseSlide_Modes) */ - short mode; - /** unused for now, but can later get used for storing runtime settings.... */ - short flag; - - /** which transforms/channels are affected (ePoseSlide_Channels) */ - short channels; - /** axis-limits for transforms (ePoseSlide_AxisLock) */ - short axislock; - - /** 0-1 value for determining the influence of whatever is relevant */ - float percentage; - - /** numeric input */ - NumInput num; - - struct tPoseSlideObject *ob_data_array; + /** current scene */ + Scene *scene; + /** area that we're operating in (needed for modal()) */ + ScrArea *sa; + /** region that we're operating in (needed for modal()) */ + ARegion *ar; + /** len of the PoseSlideObject array. */ + uint objects_len; + + /** links between posechannels and f-curves for all the pose objects. */ + ListBase pfLinks; + /** binary tree for quicker searching for keyframes (when applicable) */ + DLRBT_Tree keys; + + /** current frame number - global time */ + int cframe; + + /** frame before current frame (blend-from) - global time */ + int prevFrame; + /** frame after current frame (blend-to) - global time */ + int nextFrame; + + /** sliding mode (ePoseSlide_Modes) */ + short mode; + /** unused for now, but can later get used for storing runtime settings.... */ + short flag; + + /** which transforms/channels are affected (ePoseSlide_Channels) */ + short channels; + /** axis-limits for transforms (ePoseSlide_AxisLock) */ + short axislock; + + /** 0-1 value for determining the influence of whatever is relevant */ + float percentage; + + /** numeric input */ + NumInput num; + + struct tPoseSlideObject *ob_data_array; } tPoseSlideOp; typedef struct tPoseSlideObject { - Object *ob; /* active object that Pose Info comes from */ - float prevFrameF; /* prevFrame, but in local action time (for F-Curve lookups to work) */ - float nextFrameF; /* nextFrame, but in local action time (for F-Curve lookups to work) */ - bool valid; + Object *ob; /* active object that Pose Info comes from */ + float prevFrameF; /* prevFrame, but in local action time (for F-Curve lookups to work) */ + float nextFrameF; /* nextFrame, but in local action time (for F-Curve lookups to work) */ + bool valid; } tPoseSlideObject; /* Pose Sliding Modes */ typedef enum ePoseSlide_Modes { - POSESLIDE_PUSH = 0, /* exaggerate the pose... */ - POSESLIDE_RELAX, /* soften the pose... */ - POSESLIDE_BREAKDOWN, /* slide between the endpoint poses, finding a 'soft' spot */ + POSESLIDE_PUSH = 0, /* exaggerate the pose... */ + POSESLIDE_RELAX, /* soften the pose... */ + POSESLIDE_BREAKDOWN, /* slide between the endpoint poses, finding a 'soft' spot */ } ePoseSlide_Modes; - /* Transforms/Channels to Affect */ typedef enum ePoseSlide_Channels { - PS_TFM_ALL = 0, /* All transforms and properties */ + PS_TFM_ALL = 0, /* All transforms and properties */ - PS_TFM_LOC, /* Loc/Rot/Scale */ - PS_TFM_ROT, - PS_TFM_SIZE, + PS_TFM_LOC, /* Loc/Rot/Scale */ + PS_TFM_ROT, + PS_TFM_SIZE, - PS_TFM_BBONE_SHAPE, /* Bendy Bones */ + PS_TFM_BBONE_SHAPE, /* Bendy Bones */ - PS_TFM_PROPS, /* Custom Properties */ + PS_TFM_PROPS, /* Custom Properties */ } ePoseSlide_Channels; /* Property enum for ePoseSlide_Channels */ static const EnumPropertyItem prop_channels_types[] = { - {PS_TFM_ALL, "ALL", 0, "All Properties", - "All properties, including transforms, bendy bone shape, and custom properties"}, - {PS_TFM_LOC, "LOC", 0, "Location", "Location only"}, - {PS_TFM_ROT, "ROT", 0, "Rotation", "Rotation only"}, - {PS_TFM_SIZE, "SIZE", 0, "Scale", "Scale only"}, - {PS_TFM_BBONE_SHAPE, "BBONE", 0, "Bendy Bone", "Bendy Bone shape properties"}, - {PS_TFM_PROPS, "CUSTOM", 0, "Custom Properties", "Custom properties"}, - {0, NULL, 0, NULL, NULL}, + {PS_TFM_ALL, + "ALL", + 0, + "All Properties", + "All properties, including transforms, bendy bone shape, and custom properties"}, + {PS_TFM_LOC, "LOC", 0, "Location", "Location only"}, + {PS_TFM_ROT, "ROT", 0, "Rotation", "Rotation only"}, + {PS_TFM_SIZE, "SIZE", 0, "Scale", "Scale only"}, + {PS_TFM_BBONE_SHAPE, "BBONE", 0, "Bendy Bone", "Bendy Bone shape properties"}, + {PS_TFM_PROPS, "CUSTOM", 0, "Custom Properties", "Custom properties"}, + {0, NULL, 0, NULL, NULL}, }; /* Axis Locks */ typedef enum ePoseSlide_AxisLock { - PS_LOCK_X = (1 << 0), - PS_LOCK_Y = (1 << 1), - PS_LOCK_Z = (1 << 2), + PS_LOCK_X = (1 << 0), + PS_LOCK_Y = (1 << 1), + PS_LOCK_Z = (1 << 2), } ePoseSlide_AxisLock; /* Property enum for ePoseSlide_AxisLock */ static const EnumPropertyItem prop_axis_lock_types[] = { - {0, "FREE", 0, "Free", "All axes are affected"}, - {PS_LOCK_X, "X", 0, "X", "Only X-axis transforms are affected"}, - {PS_LOCK_Y, "Y", 0, "Y", "Only Y-axis transforms are affected"}, - {PS_LOCK_Z, "Z", 0, "Z", "Only Z-axis transforms are affected"}, - /* TODO: Combinations? */ - {0, NULL, 0, NULL, NULL}, + {0, "FREE", 0, "Free", "All axes are affected"}, + {PS_LOCK_X, "X", 0, "X", "Only X-axis transforms are affected"}, + {PS_LOCK_Y, "Y", 0, "Y", "Only Y-axis transforms are affected"}, + {PS_LOCK_Z, "Z", 0, "Z", "Only Z-axis transforms are affected"}, + /* TODO: Combinations? */ + {0, NULL, 0, NULL, NULL}, }; /* ------------------------------------ */ @@ -184,99 +186,100 @@ static const EnumPropertyItem prop_axis_lock_types[] = { /* operator init */ static int pose_slide_init(bContext *C, wmOperator *op, ePoseSlide_Modes mode) { - tPoseSlideOp *pso; - - /* init slide-op data */ - pso = op->customdata = MEM_callocN(sizeof(tPoseSlideOp), "tPoseSlideOp"); - - /* get info from context */ - pso->scene = CTX_data_scene(C); - pso->sa = CTX_wm_area(C); /* only really needed when doing modal() */ - pso->ar = CTX_wm_region(C); /* only really needed when doing modal() */ - - pso->cframe = pso->scene->r.cfra; - pso->mode = mode; - - /* set range info from property values - these may get overridden for the invoke() */ - pso->percentage = RNA_float_get(op->ptr, "percentage"); - pso->prevFrame = RNA_int_get(op->ptr, "prev_frame"); - pso->nextFrame = RNA_int_get(op->ptr, "next_frame"); - - /* get the set of properties/axes that can be operated on */ - pso->channels = RNA_enum_get(op->ptr, "channels"); - pso->axislock = RNA_enum_get(op->ptr, "axis_lock"); - - /* for each Pose-Channel which gets affected, get the F-Curves for that channel - * and set the relevant transform flags... */ - poseAnim_mapping_get(C, &pso->pfLinks); - - Object **objects = BKE_view_layer_array_from_objects_in_mode_unique_data(CTX_data_view_layer(C), - CTX_wm_view3d(C), - &pso->objects_len, - OB_MODE_POSE); - pso->ob_data_array = MEM_callocN(pso->objects_len * sizeof(tPoseSlideObject), "pose slide objects data"); - - for (uint ob_index = 0; ob_index < pso->objects_len; ob_index++) { - tPoseSlideObject *ob_data = &pso->ob_data_array[ob_index]; - Object *ob_iter = poseAnim_object_get(objects[ob_index]); - - /* Ensure validity of the settings from the context. */ - if (ob_iter == NULL) { - continue; - } - - ob_data->ob = ob_iter; - ob_data->valid = true; - - /* apply NLA mapping corrections so the frame lookups work */ - ob_data->prevFrameF = BKE_nla_tweakedit_remap(ob_data->ob->adt, pso->prevFrame, NLATIME_CONVERT_UNMAP); - ob_data->nextFrameF = BKE_nla_tweakedit_remap(ob_data->ob->adt, pso->nextFrame, NLATIME_CONVERT_UNMAP); - - /* set depsgraph flags */ - /* make sure the lock is set OK, unlock can be accidentally saved? */ - ob_data->ob->pose->flag |= POSE_LOCKED; - ob_data->ob->pose->flag &= ~POSE_DO_UNLOCK; - } - MEM_freeN(objects); - - /* do basic initialize of RB-BST used for finding keyframes, but leave the filling of it up - * to the caller of this (usually only invoke() will do it, to make things more efficient). - */ - BLI_dlrbTree_init(&pso->keys); - - /* initialise numeric input */ - initNumInput(&pso->num); - pso->num.idx_max = 0; /* one axis */ - pso->num.val_flag[0] |= NUM_NO_NEGATIVE; - pso->num.unit_type[0] = B_UNIT_NONE; /* percentages don't have any units... */ - - /* return status is whether we've got all the data we were requested to get */ - return 1; + tPoseSlideOp *pso; + + /* init slide-op data */ + pso = op->customdata = MEM_callocN(sizeof(tPoseSlideOp), "tPoseSlideOp"); + + /* get info from context */ + pso->scene = CTX_data_scene(C); + pso->sa = CTX_wm_area(C); /* only really needed when doing modal() */ + pso->ar = CTX_wm_region(C); /* only really needed when doing modal() */ + + pso->cframe = pso->scene->r.cfra; + pso->mode = mode; + + /* set range info from property values - these may get overridden for the invoke() */ + pso->percentage = RNA_float_get(op->ptr, "percentage"); + pso->prevFrame = RNA_int_get(op->ptr, "prev_frame"); + pso->nextFrame = RNA_int_get(op->ptr, "next_frame"); + + /* get the set of properties/axes that can be operated on */ + pso->channels = RNA_enum_get(op->ptr, "channels"); + pso->axislock = RNA_enum_get(op->ptr, "axis_lock"); + + /* for each Pose-Channel which gets affected, get the F-Curves for that channel + * and set the relevant transform flags... */ + poseAnim_mapping_get(C, &pso->pfLinks); + + Object **objects = BKE_view_layer_array_from_objects_in_mode_unique_data( + CTX_data_view_layer(C), CTX_wm_view3d(C), &pso->objects_len, OB_MODE_POSE); + pso->ob_data_array = MEM_callocN(pso->objects_len * sizeof(tPoseSlideObject), + "pose slide objects data"); + + for (uint ob_index = 0; ob_index < pso->objects_len; ob_index++) { + tPoseSlideObject *ob_data = &pso->ob_data_array[ob_index]; + Object *ob_iter = poseAnim_object_get(objects[ob_index]); + + /* Ensure validity of the settings from the context. */ + if (ob_iter == NULL) { + continue; + } + + ob_data->ob = ob_iter; + ob_data->valid = true; + + /* apply NLA mapping corrections so the frame lookups work */ + ob_data->prevFrameF = BKE_nla_tweakedit_remap( + ob_data->ob->adt, pso->prevFrame, NLATIME_CONVERT_UNMAP); + ob_data->nextFrameF = BKE_nla_tweakedit_remap( + ob_data->ob->adt, pso->nextFrame, NLATIME_CONVERT_UNMAP); + + /* set depsgraph flags */ + /* make sure the lock is set OK, unlock can be accidentally saved? */ + ob_data->ob->pose->flag |= POSE_LOCKED; + ob_data->ob->pose->flag &= ~POSE_DO_UNLOCK; + } + MEM_freeN(objects); + + /* do basic initialize of RB-BST used for finding keyframes, but leave the filling of it up + * to the caller of this (usually only invoke() will do it, to make things more efficient). + */ + BLI_dlrbTree_init(&pso->keys); + + /* initialise numeric input */ + initNumInput(&pso->num); + pso->num.idx_max = 0; /* one axis */ + pso->num.val_flag[0] |= NUM_NO_NEGATIVE; + pso->num.unit_type[0] = B_UNIT_NONE; /* percentages don't have any units... */ + + /* return status is whether we've got all the data we were requested to get */ + return 1; } /* exiting the operator - free data */ static void pose_slide_exit(wmOperator *op) { - tPoseSlideOp *pso = op->customdata; + tPoseSlideOp *pso = op->customdata; - /* if data exists, clear its data and exit */ - if (pso) { - /* free the temp pchan links and their data */ - poseAnim_mapping_free(&pso->pfLinks); + /* if data exists, clear its data and exit */ + if (pso) { + /* free the temp pchan links and their data */ + poseAnim_mapping_free(&pso->pfLinks); - /* free RB-BST for keyframes (if it contained data) */ - BLI_dlrbTree_free(&pso->keys); + /* free RB-BST for keyframes (if it contained data) */ + BLI_dlrbTree_free(&pso->keys); - if (pso->ob_data_array != NULL) { - MEM_freeN(pso->ob_data_array); - } + if (pso->ob_data_array != NULL) { + MEM_freeN(pso->ob_data_array); + } - /* free data itself */ - MEM_freeN(pso); - } + /* free data itself */ + MEM_freeN(pso); + } - /* cleanup */ - op->customdata = NULL; + /* cleanup */ + op->customdata = NULL; } /* ------------------------------------ */ @@ -284,438 +287,437 @@ static void pose_slide_exit(wmOperator *op) /* helper for apply() / reset() - refresh the data */ static void pose_slide_refresh(bContext *C, tPoseSlideOp *pso) { - /* wrapper around the generic version, allowing us to add some custom stuff later still */ - for (uint ob_index = 0; ob_index < pso->objects_len; ob_index++) { - tPoseSlideObject *ob_data = &pso->ob_data_array[ob_index]; - if (ob_data->valid) { - poseAnim_mapping_refresh(C, pso->scene, ob_data->ob); - } - } + /* wrapper around the generic version, allowing us to add some custom stuff later still */ + for (uint ob_index = 0; ob_index < pso->objects_len; ob_index++) { + tPoseSlideObject *ob_data = &pso->ob_data_array[ob_index]; + if (ob_data->valid) { + poseAnim_mapping_refresh(C, pso->scene, ob_data->ob); + } + } } /** * Although this lookup is not ideal, we won't be dealing with a lot of objects at a given time. * But if it comes to that we can instead store prev/next frame in the #tPChanFCurveLink. */ -static bool pose_frame_range_from_object_get(tPoseSlideOp *pso, Object *ob, float *prevFrameF, float *nextFrameF) +static bool pose_frame_range_from_object_get(tPoseSlideOp *pso, + Object *ob, + float *prevFrameF, + float *nextFrameF) { - for (uint ob_index = 0; ob_index < pso->objects_len; ob_index++) { - tPoseSlideObject *ob_data = &pso->ob_data_array[ob_index]; - Object *ob_iter = ob_data->ob; - - if (ob_iter == ob) { - *prevFrameF = ob_data->prevFrameF; - *nextFrameF = ob_data->nextFrameF; - return true; - } - } - *prevFrameF = *nextFrameF = 0.0f; - return false; + for (uint ob_index = 0; ob_index < pso->objects_len; ob_index++) { + tPoseSlideObject *ob_data = &pso->ob_data_array[ob_index]; + Object *ob_iter = ob_data->ob; + + if (ob_iter == ob) { + *prevFrameF = ob_data->prevFrameF; + *nextFrameF = ob_data->nextFrameF; + return true; + } + } + *prevFrameF = *nextFrameF = 0.0f; + return false; } /* helper for apply() - perform sliding for some value */ -static void pose_slide_apply_val( - tPoseSlideOp *pso, - FCurve *fcu, - Object *ob, - float *val) +static void pose_slide_apply_val(tPoseSlideOp *pso, FCurve *fcu, Object *ob, float *val) { - float prevFrameF, nextFrameF; - float cframe = (float)pso->cframe; - float sVal, eVal; - float w1, w2; - - pose_frame_range_from_object_get(pso, ob, &prevFrameF, &nextFrameF); - - /* get keyframe values for endpoint poses to blend with */ - /* previous/start */ - sVal = evaluate_fcurve(fcu, prevFrameF); - /* next/end */ - eVal = evaluate_fcurve(fcu, nextFrameF); - - /* if both values are equal, don't do anything */ - if (IS_EQF(sVal, eVal)) { - (*val) = sVal; - return; - } - - /* calculate the relative weights of the endpoints */ - if (pso->mode == POSESLIDE_BREAKDOWN) { - /* get weights from the percentage control */ - w1 = pso->percentage; /* this must come second */ - w2 = 1.0f - w1; /* this must come first */ - } - else { - /* - these weights are derived from the relative distance of these - * poses from the current frame - * - they then get normalized so that they only sum up to 1 - */ - float wtot; - - w1 = cframe - (float)pso->prevFrame; - w2 = (float)pso->nextFrame - cframe; - - wtot = w1 + w2; - w1 = (w1 / wtot); - w2 = (w2 / wtot); - } - - /* depending on the mode, calculate the new value - * - in all of these, the start+end values are multiplied by w2 and w1 (respectively), - * since multiplication in another order would decrease the value the current frame is closer to - */ - switch (pso->mode) { - case POSESLIDE_PUSH: /* make the current pose more pronounced */ - { - /* perform a weighted average here, favoring the middle pose - * - numerator should be larger than denominator to 'expand' the result - * - perform this weighting a number of times given by the percentage... - */ - /* TODO: maybe a sensitivity ctrl on top of this is needed */ - int iters = (int)ceil(10.0f * pso->percentage); - - while (iters-- > 0) { - (*val) = (-((sVal * w2) + (eVal * w1)) + ((*val) * 6.0f) ) / 5.0f; - } - break; - } - case POSESLIDE_RELAX: /* make the current pose more like its surrounding ones */ - { - /* perform a weighted average here, favoring the middle pose - * - numerator should be smaller than denominator to 'relax' the result - * - perform this weighting a number of times given by the percentage... - */ - /* TODO: maybe a sensitivity ctrl on top of this is needed */ - int iters = (int)ceil(10.0f * pso->percentage); - - while (iters-- > 0) { - (*val) = ( ((sVal * w2) + (eVal * w1)) + ((*val) * 5.0f) ) / 6.0f; - } - break; - } - case POSESLIDE_BREAKDOWN: /* make the current pose slide around between the endpoints */ - { - /* perform simple linear interpolation - coefficient for start must come from pso->percentage... */ - /* TODO: make this use some kind of spline interpolation instead? */ - (*val) = ((sVal * w2) + (eVal * w1)); - break; - } - } + float prevFrameF, nextFrameF; + float cframe = (float)pso->cframe; + float sVal, eVal; + float w1, w2; + + pose_frame_range_from_object_get(pso, ob, &prevFrameF, &nextFrameF); + + /* get keyframe values for endpoint poses to blend with */ + /* previous/start */ + sVal = evaluate_fcurve(fcu, prevFrameF); + /* next/end */ + eVal = evaluate_fcurve(fcu, nextFrameF); + + /* if both values are equal, don't do anything */ + if (IS_EQF(sVal, eVal)) { + (*val) = sVal; + return; + } + + /* calculate the relative weights of the endpoints */ + if (pso->mode == POSESLIDE_BREAKDOWN) { + /* get weights from the percentage control */ + w1 = pso->percentage; /* this must come second */ + w2 = 1.0f - w1; /* this must come first */ + } + else { + /* - these weights are derived from the relative distance of these + * poses from the current frame + * - they then get normalized so that they only sum up to 1 + */ + float wtot; + + w1 = cframe - (float)pso->prevFrame; + w2 = (float)pso->nextFrame - cframe; + + wtot = w1 + w2; + w1 = (w1 / wtot); + w2 = (w2 / wtot); + } + + /* depending on the mode, calculate the new value + * - in all of these, the start+end values are multiplied by w2 and w1 (respectively), + * since multiplication in another order would decrease the value the current frame is closer to + */ + switch (pso->mode) { + case POSESLIDE_PUSH: /* make the current pose more pronounced */ + { + /* perform a weighted average here, favoring the middle pose + * - numerator should be larger than denominator to 'expand' the result + * - perform this weighting a number of times given by the percentage... + */ + /* TODO: maybe a sensitivity ctrl on top of this is needed */ + int iters = (int)ceil(10.0f * pso->percentage); + + while (iters-- > 0) { + (*val) = (-((sVal * w2) + (eVal * w1)) + ((*val) * 6.0f)) / 5.0f; + } + break; + } + case POSESLIDE_RELAX: /* make the current pose more like its surrounding ones */ + { + /* perform a weighted average here, favoring the middle pose + * - numerator should be smaller than denominator to 'relax' the result + * - perform this weighting a number of times given by the percentage... + */ + /* TODO: maybe a sensitivity ctrl on top of this is needed */ + int iters = (int)ceil(10.0f * pso->percentage); + + while (iters-- > 0) { + (*val) = (((sVal * w2) + (eVal * w1)) + ((*val) * 5.0f)) / 6.0f; + } + break; + } + case POSESLIDE_BREAKDOWN: /* make the current pose slide around between the endpoints */ + { + /* perform simple linear interpolation - coefficient for start must come from pso->percentage... */ + /* TODO: make this use some kind of spline interpolation instead? */ + (*val) = ((sVal * w2) + (eVal * w1)); + break; + } + } } /* helper for apply() - perform sliding for some 3-element vector */ -static void pose_slide_apply_vec3(tPoseSlideOp *pso, tPChanFCurveLink *pfl, float vec[3], const char propName[]) +static void pose_slide_apply_vec3(tPoseSlideOp *pso, + tPChanFCurveLink *pfl, + float vec[3], + const char propName[]) { - LinkData *ld = NULL; - char *path = NULL; - - /* get the path to use... */ - path = BLI_sprintfN("%s.%s", pfl->pchan_path, propName); - - /* using this path, find each matching F-Curve for the variables we're interested in */ - while ( (ld = poseAnim_mapping_getNextFCurve(&pfl->fcurves, ld, path)) ) { - FCurve *fcu = (FCurve *)ld->data; - const int idx = fcu->array_index; - const int lock = pso->axislock; - - /* check if this F-Curve is ok given the current axis locks */ - BLI_assert(fcu->array_index < 3); - - if ((lock == 0) || - ((lock & PS_LOCK_X) && (idx == 0)) || - ((lock & PS_LOCK_Y) && (idx == 1)) || - ((lock & PS_LOCK_Z) && (idx == 2))) - { - /* just work on these channels one by one... there's no interaction between values */ - pose_slide_apply_val(pso, fcu, pfl->ob, &vec[fcu->array_index]); - } - } - - /* free the temp path we got */ - MEM_freeN(path); + LinkData *ld = NULL; + char *path = NULL; + + /* get the path to use... */ + path = BLI_sprintfN("%s.%s", pfl->pchan_path, propName); + + /* using this path, find each matching F-Curve for the variables we're interested in */ + while ((ld = poseAnim_mapping_getNextFCurve(&pfl->fcurves, ld, path))) { + FCurve *fcu = (FCurve *)ld->data; + const int idx = fcu->array_index; + const int lock = pso->axislock; + + /* check if this F-Curve is ok given the current axis locks */ + BLI_assert(fcu->array_index < 3); + + if ((lock == 0) || ((lock & PS_LOCK_X) && (idx == 0)) || ((lock & PS_LOCK_Y) && (idx == 1)) || + ((lock & PS_LOCK_Z) && (idx == 2))) { + /* just work on these channels one by one... there's no interaction between values */ + pose_slide_apply_val(pso, fcu, pfl->ob, &vec[fcu->array_index]); + } + } + + /* free the temp path we got */ + MEM_freeN(path); } /* helper for apply() - perform sliding for custom properties or bbone properties */ -static void pose_slide_apply_props(tPoseSlideOp *pso, tPChanFCurveLink *pfl, const char prop_prefix[]) +static void pose_slide_apply_props(tPoseSlideOp *pso, + tPChanFCurveLink *pfl, + const char prop_prefix[]) { - PointerRNA ptr = {{NULL}}; - LinkData *ld; - int len = strlen(pfl->pchan_path); - - /* setup pointer RNA for resolving paths */ - RNA_pointer_create(NULL, &RNA_PoseBone, pfl->pchan, &ptr); - - /* - custom properties are just denoted using ["..."][etc.] after the end of the base path, - * so just check for opening pair after the end of the path - * - bbone properties are similar, but they always start with a prefix "bbone_*", - * so a similar method should work here for those too - */ - for (ld = pfl->fcurves.first; ld; ld = ld->next) { - FCurve *fcu = (FCurve *)ld->data; - const char *bPtr, *pPtr; - - if (fcu->rna_path == NULL) - continue; - - /* do we have a match? - * - bPtr is the RNA Path with the standard part chopped off - * - pPtr is the chunk of the path which is left over - */ - bPtr = strstr(fcu->rna_path, pfl->pchan_path) + len; - pPtr = strstr(bPtr, prop_prefix); - - if (pPtr) { - /* use RNA to try and get a handle on this property, then, assuming that it is just - * numerical, try and grab the value as a float for temp editing before setting back - */ - PropertyRNA *prop = RNA_struct_find_property(&ptr, pPtr); - - if (prop) { - switch (RNA_property_type(prop)) { - /* continuous values that can be smoothly interpolated... */ - case PROP_FLOAT: - { - float tval = RNA_property_float_get(&ptr, prop); - pose_slide_apply_val(pso, fcu, pfl->ob, &tval); - RNA_property_float_set(&ptr, prop, tval); - break; - } - case PROP_INT: - { - float tval = (float)RNA_property_int_get(&ptr, prop); - pose_slide_apply_val(pso, fcu, pfl->ob, &tval); - RNA_property_int_set(&ptr, prop, (int)tval); - break; - } - - /* values which can only take discrete values */ - case PROP_BOOLEAN: - { - float tval = (float)RNA_property_boolean_get(&ptr, prop); - pose_slide_apply_val(pso, fcu, pfl->ob, &tval); - RNA_property_boolean_set(&ptr, prop, (int)tval); // XXX: do we need threshold clamping here? - break; - } - case PROP_ENUM: - { - /* don't handle this case - these don't usually represent interchangeable - * set of values which should be interpolated between - */ - break; - } - - default: - /* cannot handle */ - //printf("Cannot Pose Slide non-numerical property\n"); - break; - } - } - } - } + PointerRNA ptr = {{NULL}}; + LinkData *ld; + int len = strlen(pfl->pchan_path); + + /* setup pointer RNA for resolving paths */ + RNA_pointer_create(NULL, &RNA_PoseBone, pfl->pchan, &ptr); + + /* - custom properties are just denoted using ["..."][etc.] after the end of the base path, + * so just check for opening pair after the end of the path + * - bbone properties are similar, but they always start with a prefix "bbone_*", + * so a similar method should work here for those too + */ + for (ld = pfl->fcurves.first; ld; ld = ld->next) { + FCurve *fcu = (FCurve *)ld->data; + const char *bPtr, *pPtr; + + if (fcu->rna_path == NULL) + continue; + + /* do we have a match? + * - bPtr is the RNA Path with the standard part chopped off + * - pPtr is the chunk of the path which is left over + */ + bPtr = strstr(fcu->rna_path, pfl->pchan_path) + len; + pPtr = strstr(bPtr, prop_prefix); + + if (pPtr) { + /* use RNA to try and get a handle on this property, then, assuming that it is just + * numerical, try and grab the value as a float for temp editing before setting back + */ + PropertyRNA *prop = RNA_struct_find_property(&ptr, pPtr); + + if (prop) { + switch (RNA_property_type(prop)) { + /* continuous values that can be smoothly interpolated... */ + case PROP_FLOAT: { + float tval = RNA_property_float_get(&ptr, prop); + pose_slide_apply_val(pso, fcu, pfl->ob, &tval); + RNA_property_float_set(&ptr, prop, tval); + break; + } + case PROP_INT: { + float tval = (float)RNA_property_int_get(&ptr, prop); + pose_slide_apply_val(pso, fcu, pfl->ob, &tval); + RNA_property_int_set(&ptr, prop, (int)tval); + break; + } + + /* values which can only take discrete values */ + case PROP_BOOLEAN: { + float tval = (float)RNA_property_boolean_get(&ptr, prop); + pose_slide_apply_val(pso, fcu, pfl->ob, &tval); + RNA_property_boolean_set( + &ptr, prop, (int)tval); // XXX: do we need threshold clamping here? + break; + } + case PROP_ENUM: { + /* don't handle this case - these don't usually represent interchangeable + * set of values which should be interpolated between + */ + break; + } + + default: + /* cannot handle */ + //printf("Cannot Pose Slide non-numerical property\n"); + break; + } + } + } + } } /* helper for apply() - perform sliding for quaternion rotations (using quat blending) */ static void pose_slide_apply_quat(tPoseSlideOp *pso, tPChanFCurveLink *pfl) { - FCurve *fcu_w = NULL, *fcu_x = NULL, *fcu_y = NULL, *fcu_z = NULL; - bPoseChannel *pchan = pfl->pchan; - LinkData *ld = NULL; - char *path = NULL; - float cframe; - float prevFrameF, nextFrameF; - - if (!pose_frame_range_from_object_get(pso, pfl->ob, &prevFrameF, &nextFrameF)) { - BLI_assert(!"Invalid pfl data"); - return; - } - - /* get the path to use - this should be quaternion rotations only (needs care) */ - path = BLI_sprintfN("%s.%s", pfl->pchan_path, "rotation_quaternion"); - - /* get the current frame number */ - cframe = (float)pso->cframe; - - /* using this path, find each matching F-Curve for the variables we're interested in */ - while ( (ld = poseAnim_mapping_getNextFCurve(&pfl->fcurves, ld, path)) ) { - FCurve *fcu = (FCurve *)ld->data; - - /* assign this F-Curve to one of the relevant pointers... */ - switch (fcu->array_index) { - case 3: /* z */ - fcu_z = fcu; - break; - case 2: /* y */ - fcu_y = fcu; - break; - case 1: /* x */ - fcu_x = fcu; - break; - case 0: /* w */ - fcu_w = fcu; - break; - } - } - - /* 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 interpol 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]; - - /* calculate the delta transform from the previous to the current */ - /* TODO: investigate ways to favour one transform more? */ - sub_qt_qtqt(quat_diff, quat_curr, quat_prev); - - /* 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_final); - } - 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); - - copy_qt_qt(quat_final, quat_curr); - - /* 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)); - - normalize_qt_qt(quat_final_prev, quat_final); - - /* tricky interpolations - blending between original and new */ - interp_qt_qtqt(quat_final, quat_final_prev, quat_interp, 1.0f / 6.0f); - } - } - - /* Apply final to the pose bone, keeping compatible for similar keyframe positions. */ - quat_to_compatible_quat(pchan->quat, quat_final, quat_curr_orig); - } - - /* free the path now */ - MEM_freeN(path); + FCurve *fcu_w = NULL, *fcu_x = NULL, *fcu_y = NULL, *fcu_z = NULL; + bPoseChannel *pchan = pfl->pchan; + LinkData *ld = NULL; + char *path = NULL; + float cframe; + float prevFrameF, nextFrameF; + + if (!pose_frame_range_from_object_get(pso, pfl->ob, &prevFrameF, &nextFrameF)) { + BLI_assert(!"Invalid pfl data"); + return; + } + + /* get the path to use - this should be quaternion rotations only (needs care) */ + path = BLI_sprintfN("%s.%s", pfl->pchan_path, "rotation_quaternion"); + + /* get the current frame number */ + cframe = (float)pso->cframe; + + /* using this path, find each matching F-Curve for the variables we're interested in */ + while ((ld = poseAnim_mapping_getNextFCurve(&pfl->fcurves, ld, path))) { + FCurve *fcu = (FCurve *)ld->data; + + /* assign this F-Curve to one of the relevant pointers... */ + switch (fcu->array_index) { + case 3: /* z */ + fcu_z = fcu; + break; + case 2: /* y */ + fcu_y = fcu; + break; + case 1: /* x */ + fcu_x = fcu; + break; + case 0: /* w */ + fcu_w = fcu; + break; + } + } + + /* 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 interpol 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]; + + /* calculate the delta transform from the previous to the current */ + /* TODO: investigate ways to favour one transform more? */ + sub_qt_qtqt(quat_diff, quat_curr, quat_prev); + + /* 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_final); + } + 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); + + copy_qt_qt(quat_final, quat_curr); + + /* 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)); + + normalize_qt_qt(quat_final_prev, quat_final); + + /* tricky interpolations - blending between original and new */ + interp_qt_qtqt(quat_final, quat_final_prev, quat_interp, 1.0f / 6.0f); + } + } + + /* Apply final to the pose bone, keeping compatible for similar keyframe positions. */ + quat_to_compatible_quat(pchan->quat, quat_final, quat_curr_orig); + } + + /* free the path now */ + MEM_freeN(path); } /* apply() - perform the pose sliding based on weighting various poses */ static void pose_slide_apply(bContext *C, tPoseSlideOp *pso) { - tPChanFCurveLink *pfl; - - /* sanitise the frame ranges */ - if (pso->prevFrame == pso->nextFrame) { - /* move out one step either side */ - pso->prevFrame--; - pso->nextFrame++; - - for (uint ob_index = 0; ob_index < pso->objects_len; ob_index++) { - tPoseSlideObject *ob_data = &pso->ob_data_array[ob_index]; - - if (!ob_data->valid) { - continue; - } - - /* apply NLA mapping corrections so the frame lookups work */ - ob_data->prevFrameF = BKE_nla_tweakedit_remap(ob_data->ob->adt, - pso->prevFrame, - NLATIME_CONVERT_UNMAP); - ob_data->nextFrameF = BKE_nla_tweakedit_remap(ob_data->ob->adt, - pso->nextFrame, - NLATIME_CONVERT_UNMAP); - } - } - - /* for each link, handle each set of transforms */ - for (pfl = pso->pfLinks.first; pfl; pfl = pfl->next) { - /* valid transforms for each PoseChannel should have been noted already - * - sliding the pose should be a straightforward exercise for location+rotation, - * but rotations get more complicated since we may want to use quaternion blending - * for quaternions instead... - */ - bPoseChannel *pchan = pfl->pchan; - - if (ELEM(pso->channels, PS_TFM_ALL, PS_TFM_LOC) && (pchan->flag & POSE_LOC)) { - /* calculate these for the 'location' vector, and use location curves */ - pose_slide_apply_vec3(pso, pfl, pchan->loc, "location"); - } - - if (ELEM(pso->channels, PS_TFM_ALL, PS_TFM_SIZE) && (pchan->flag & POSE_SIZE)) { - /* calculate these for the 'scale' vector, and use scale curves */ - pose_slide_apply_vec3(pso, pfl, pchan->size, "scale"); - } - - if (ELEM(pso->channels, PS_TFM_ALL, PS_TFM_ROT) && (pchan->flag & POSE_ROT)) { - /* everything depends on the rotation mode */ - if (pchan->rotmode > 0) { - /* eulers - so calculate these for the 'eul' vector, and use euler_rotation curves */ - pose_slide_apply_vec3(pso, pfl, pchan->eul, "rotation_euler"); - } - else if (pchan->rotmode == ROT_MODE_AXISANGLE) { - /* TODO: need to figure out how to do this! */ - } - else { - /* quaternions - use quaternion blending */ - pose_slide_apply_quat(pso, pfl); - } - } - - if (ELEM(pso->channels, PS_TFM_ALL, PS_TFM_BBONE_SHAPE) && (pchan->flag & POSE_BBONE_SHAPE)) { - /* bbone properties - they all start a "bbone_" prefix */ - pose_slide_apply_props(pso, pfl, "bbone_"); - } - - if (ELEM(pso->channels, PS_TFM_ALL, PS_TFM_PROPS) && (pfl->oldprops)) { - /* not strictly a transform, but custom properties contribute to the pose produced in many rigs - * (e.g. the facial rigs used in Sintel) - */ - pose_slide_apply_props(pso, pfl, "[\""); /* dummy " for texteditor bugs */ - } - } - - /* depsgraph updates + redraws */ - pose_slide_refresh(C, pso); + tPChanFCurveLink *pfl; + + /* sanitise the frame ranges */ + if (pso->prevFrame == pso->nextFrame) { + /* move out one step either side */ + pso->prevFrame--; + pso->nextFrame++; + + for (uint ob_index = 0; ob_index < pso->objects_len; ob_index++) { + tPoseSlideObject *ob_data = &pso->ob_data_array[ob_index]; + + if (!ob_data->valid) { + continue; + } + + /* apply NLA mapping corrections so the frame lookups work */ + ob_data->prevFrameF = BKE_nla_tweakedit_remap( + ob_data->ob->adt, pso->prevFrame, NLATIME_CONVERT_UNMAP); + ob_data->nextFrameF = BKE_nla_tweakedit_remap( + ob_data->ob->adt, pso->nextFrame, NLATIME_CONVERT_UNMAP); + } + } + + /* for each link, handle each set of transforms */ + for (pfl = pso->pfLinks.first; pfl; pfl = pfl->next) { + /* valid transforms for each PoseChannel should have been noted already + * - sliding the pose should be a straightforward exercise for location+rotation, + * but rotations get more complicated since we may want to use quaternion blending + * for quaternions instead... + */ + bPoseChannel *pchan = pfl->pchan; + + if (ELEM(pso->channels, PS_TFM_ALL, PS_TFM_LOC) && (pchan->flag & POSE_LOC)) { + /* calculate these for the 'location' vector, and use location curves */ + pose_slide_apply_vec3(pso, pfl, pchan->loc, "location"); + } + + if (ELEM(pso->channels, PS_TFM_ALL, PS_TFM_SIZE) && (pchan->flag & POSE_SIZE)) { + /* calculate these for the 'scale' vector, and use scale curves */ + pose_slide_apply_vec3(pso, pfl, pchan->size, "scale"); + } + + if (ELEM(pso->channels, PS_TFM_ALL, PS_TFM_ROT) && (pchan->flag & POSE_ROT)) { + /* everything depends on the rotation mode */ + if (pchan->rotmode > 0) { + /* eulers - so calculate these for the 'eul' vector, and use euler_rotation curves */ + pose_slide_apply_vec3(pso, pfl, pchan->eul, "rotation_euler"); + } + else if (pchan->rotmode == ROT_MODE_AXISANGLE) { + /* TODO: need to figure out how to do this! */ + } + else { + /* quaternions - use quaternion blending */ + pose_slide_apply_quat(pso, pfl); + } + } + + if (ELEM(pso->channels, PS_TFM_ALL, PS_TFM_BBONE_SHAPE) && (pchan->flag & POSE_BBONE_SHAPE)) { + /* bbone properties - they all start a "bbone_" prefix */ + pose_slide_apply_props(pso, pfl, "bbone_"); + } + + if (ELEM(pso->channels, PS_TFM_ALL, PS_TFM_PROPS) && (pfl->oldprops)) { + /* not strictly a transform, but custom properties contribute to the pose produced in many rigs + * (e.g. the facial rigs used in Sintel) + */ + pose_slide_apply_props(pso, pfl, "[\""); /* dummy " for texteditor bugs */ + } + } + + /* depsgraph updates + redraws */ + pose_slide_refresh(C, pso); } /* perform auto-key-framing after changes were made + confirmed */ static void pose_slide_autoKeyframe(bContext *C, tPoseSlideOp *pso) { - /* wrapper around the generic call */ - poseAnim_mapping_autoKeyframe(C, pso->scene, &pso->pfLinks, (float)pso->cframe); + /* wrapper around the generic call */ + poseAnim_mapping_autoKeyframe(C, pso->scene, &pso->pfLinks, (float)pso->cframe); } /* reset changes made to current pose */ static void pose_slide_reset(tPoseSlideOp *pso) { - /* wrapper around the generic call, so that custom stuff can be added later */ - poseAnim_mapping_reset(&pso->pfLinks); + /* wrapper around the generic call, so that custom stuff can be added later */ + poseAnim_mapping_reset(&pso->pfLinks); } /* ------------------------------------ */ @@ -724,425 +726,472 @@ static void pose_slide_reset(tPoseSlideOp *pso) // TODO: Include hints about locks here... static void pose_slide_draw_status(tPoseSlideOp *pso) { - char status_str[UI_MAX_DRAW_STR]; - char limits_str[UI_MAX_DRAW_STR]; - char axis_str[50]; - char mode_str[32]; - - switch (pso->mode) { - case POSESLIDE_PUSH: - strcpy(mode_str, "Push Pose"); - break; - case POSESLIDE_RELAX: - strcpy(mode_str, "Relax Pose"); - break; - case POSESLIDE_BREAKDOWN: - strcpy(mode_str, "Breakdown"); - break; - - default: - /* unknown */ - strcpy(mode_str, "Sliding-Tool"); - break; - } - - switch (pso->axislock) { - case PS_LOCK_X: - BLI_strncpy(axis_str, "[X]/Y/Z axis only (X to clear)", sizeof(axis_str)); - break; - case PS_LOCK_Y: - BLI_strncpy(axis_str, "X/[Y]/Z axis only (Y to clear)", sizeof(axis_str)); - break; - case PS_LOCK_Z: - BLI_strncpy(axis_str, "X/Y/[Z] axis only (Z to clear)", sizeof(axis_str)); - break; - - default: - if (ELEM(pso->channels, PS_TFM_LOC, PS_TFM_ROT, PS_TFM_SIZE)) { - BLI_strncpy(axis_str, "X/Y/Z = Axis Constraint", sizeof(axis_str)); - } - else { - axis_str[0] = '\0'; - } - break; - } - - switch (pso->channels) { - case PS_TFM_LOC: - BLI_snprintf(limits_str, sizeof(limits_str), "[G]/R/S/B/C - Location only (G to clear) | %s", axis_str); - break; - case PS_TFM_ROT: - BLI_snprintf(limits_str, sizeof(limits_str), "G/[R]/S/B/C - Rotation only (R to clear) | %s", axis_str); - break; - case PS_TFM_SIZE: - BLI_snprintf(limits_str, sizeof(limits_str), "G/R/[S]/B/C - Scale only (S to clear) | %s", axis_str); - break; - case PS_TFM_BBONE_SHAPE: - BLI_strncpy(limits_str, "G/R/S/[B]/C - Bendy Bone properties only (B to clear) | %s", sizeof(limits_str)); - break; - case PS_TFM_PROPS: - BLI_strncpy(limits_str, "G/R/S/B/[C] - Custom Properties only (C to clear) | %s", sizeof(limits_str)); - break; - default: - BLI_strncpy(limits_str, "G/R/S/B/C - Limit to Transform/Property Set", sizeof(limits_str)); - break; - } - - if (hasNumInput(&pso->num)) { - Scene *scene = pso->scene; - char str_offs[NUM_STR_REP_LEN]; - - outputNumInput(&pso->num, str_offs, &scene->unit); - - BLI_snprintf(status_str, sizeof(status_str), "%s: %s | %s", mode_str, str_offs, limits_str); - } - else { - BLI_snprintf(status_str, sizeof(status_str), "%s: %d %% | %s", mode_str, (int)(pso->percentage * 100.0f), limits_str); - } - - ED_area_status_text(pso->sa, status_str); + char status_str[UI_MAX_DRAW_STR]; + char limits_str[UI_MAX_DRAW_STR]; + char axis_str[50]; + char mode_str[32]; + + switch (pso->mode) { + case POSESLIDE_PUSH: + strcpy(mode_str, "Push Pose"); + break; + case POSESLIDE_RELAX: + strcpy(mode_str, "Relax Pose"); + break; + case POSESLIDE_BREAKDOWN: + strcpy(mode_str, "Breakdown"); + break; + + default: + /* unknown */ + strcpy(mode_str, "Sliding-Tool"); + break; + } + + switch (pso->axislock) { + case PS_LOCK_X: + BLI_strncpy(axis_str, "[X]/Y/Z axis only (X to clear)", sizeof(axis_str)); + break; + case PS_LOCK_Y: + BLI_strncpy(axis_str, "X/[Y]/Z axis only (Y to clear)", sizeof(axis_str)); + break; + case PS_LOCK_Z: + BLI_strncpy(axis_str, "X/Y/[Z] axis only (Z to clear)", sizeof(axis_str)); + break; + + default: + if (ELEM(pso->channels, PS_TFM_LOC, PS_TFM_ROT, PS_TFM_SIZE)) { + BLI_strncpy(axis_str, "X/Y/Z = Axis Constraint", sizeof(axis_str)); + } + else { + axis_str[0] = '\0'; + } + break; + } + + switch (pso->channels) { + case PS_TFM_LOC: + BLI_snprintf(limits_str, + sizeof(limits_str), + "[G]/R/S/B/C - Location only (G to clear) | %s", + axis_str); + break; + case PS_TFM_ROT: + BLI_snprintf(limits_str, + sizeof(limits_str), + "G/[R]/S/B/C - Rotation only (R to clear) | %s", + axis_str); + break; + case PS_TFM_SIZE: + BLI_snprintf( + limits_str, sizeof(limits_str), "G/R/[S]/B/C - Scale only (S to clear) | %s", axis_str); + break; + case PS_TFM_BBONE_SHAPE: + BLI_strncpy(limits_str, + "G/R/S/[B]/C - Bendy Bone properties only (B to clear) | %s", + sizeof(limits_str)); + break; + case PS_TFM_PROPS: + BLI_strncpy(limits_str, + "G/R/S/B/[C] - Custom Properties only (C to clear) | %s", + sizeof(limits_str)); + break; + default: + BLI_strncpy(limits_str, "G/R/S/B/C - Limit to Transform/Property Set", sizeof(limits_str)); + break; + } + + if (hasNumInput(&pso->num)) { + Scene *scene = pso->scene; + char str_offs[NUM_STR_REP_LEN]; + + outputNumInput(&pso->num, str_offs, &scene->unit); + + BLI_snprintf( + status_str, sizeof(status_str), "%s: %s | %s", mode_str, str_offs, limits_str); + } + else { + BLI_snprintf(status_str, + sizeof(status_str), + "%s: %d %% | %s", + mode_str, + (int)(pso->percentage * 100.0f), + limits_str); + } + + ED_area_status_text(pso->sa, status_str); } /* common code for invoke() methods */ static int pose_slide_invoke_common(bContext *C, wmOperator *op, tPoseSlideOp *pso) { - tPChanFCurveLink *pfl; - wmWindow *win = CTX_wm_window(C); - - /* for each link, add all its keyframes to the search tree */ - for (pfl = pso->pfLinks.first; pfl; pfl = pfl->next) { - LinkData *ld; - - /* do this for each F-Curve */ - for (ld = pfl->fcurves.first; ld; ld = ld->next) { - FCurve *fcu = (FCurve *)ld->data; - fcurve_to_keylist(pfl->ob->adt, fcu, &pso->keys, 0); - } - } - - /* cancel if no keyframes found... */ - if (pso->keys.root) { - ActKeyColumn *ak; - float cframe = (float)pso->cframe; - - /* firstly, check if the current frame is a keyframe... */ - ak = (ActKeyColumn *)BLI_dlrbTree_search_exact(&pso->keys, compare_ak_cfraPtr, &cframe); - - if (ak == NULL) { - /* current frame is not a keyframe, so search */ - ActKeyColumn *pk = (ActKeyColumn *)BLI_dlrbTree_search_prev(&pso->keys, compare_ak_cfraPtr, &cframe); - ActKeyColumn *nk = (ActKeyColumn *)BLI_dlrbTree_search_next(&pso->keys, compare_ak_cfraPtr, &cframe); - - /* new set the frames */ - /* prev frame */ - pso->prevFrame = (pk) ? (pk->cfra) : (pso->cframe - 1); - RNA_int_set(op->ptr, "prev_frame", pso->prevFrame); - /* next frame */ - pso->nextFrame = (nk) ? (nk->cfra) : (pso->cframe + 1); - RNA_int_set(op->ptr, "next_frame", pso->nextFrame); - } - else { - /* current frame itself is a keyframe, so just take keyframes on either side */ - /* prev frame */ - pso->prevFrame = (ak->prev) ? (ak->prev->cfra) : (pso->cframe - 1); - RNA_int_set(op->ptr, "prev_frame", pso->prevFrame); - /* next frame */ - pso->nextFrame = (ak->next) ? (ak->next->cfra) : (pso->cframe + 1); - RNA_int_set(op->ptr, "next_frame", pso->nextFrame); - } - - /* apply NLA mapping corrections so the frame lookups work */ - for (uint ob_index = 0; ob_index < pso->objects_len; ob_index++) { - tPoseSlideObject *ob_data = &pso->ob_data_array[ob_index]; - if (ob_data->valid) { - ob_data->prevFrameF = BKE_nla_tweakedit_remap(ob_data->ob->adt, - pso->prevFrame, - NLATIME_CONVERT_UNMAP); - ob_data->nextFrameF = BKE_nla_tweakedit_remap(ob_data->ob->adt, - pso->nextFrame, - NLATIME_CONVERT_UNMAP); - } - } - } - else { - BKE_report(op->reports, RPT_ERROR, "No keyframes to slide between"); - pose_slide_exit(op); - return OPERATOR_CANCELLED; - } - - /* initial apply for operator... */ - /* TODO: need to calculate percentage for initial round too... */ - pose_slide_apply(C, pso); - - /* depsgraph updates + redraws */ - pose_slide_refresh(C, pso); - - /* set cursor to indicate modal */ - WM_cursor_modal_set(win, BC_EW_SCROLLCURSOR); - - /* header print */ - pose_slide_draw_status(pso); - - /* add a modal handler for this operator */ - WM_event_add_modal_handler(C, op); - return OPERATOR_RUNNING_MODAL; + tPChanFCurveLink *pfl; + wmWindow *win = CTX_wm_window(C); + + /* for each link, add all its keyframes to the search tree */ + for (pfl = pso->pfLinks.first; pfl; pfl = pfl->next) { + LinkData *ld; + + /* do this for each F-Curve */ + for (ld = pfl->fcurves.first; ld; ld = ld->next) { + FCurve *fcu = (FCurve *)ld->data; + fcurve_to_keylist(pfl->ob->adt, fcu, &pso->keys, 0); + } + } + + /* cancel if no keyframes found... */ + if (pso->keys.root) { + ActKeyColumn *ak; + float cframe = (float)pso->cframe; + + /* firstly, check if the current frame is a keyframe... */ + ak = (ActKeyColumn *)BLI_dlrbTree_search_exact(&pso->keys, compare_ak_cfraPtr, &cframe); + + if (ak == NULL) { + /* current frame is not a keyframe, so search */ + ActKeyColumn *pk = (ActKeyColumn *)BLI_dlrbTree_search_prev( + &pso->keys, compare_ak_cfraPtr, &cframe); + ActKeyColumn *nk = (ActKeyColumn *)BLI_dlrbTree_search_next( + &pso->keys, compare_ak_cfraPtr, &cframe); + + /* new set the frames */ + /* prev frame */ + pso->prevFrame = (pk) ? (pk->cfra) : (pso->cframe - 1); + RNA_int_set(op->ptr, "prev_frame", pso->prevFrame); + /* next frame */ + pso->nextFrame = (nk) ? (nk->cfra) : (pso->cframe + 1); + RNA_int_set(op->ptr, "next_frame", pso->nextFrame); + } + else { + /* current frame itself is a keyframe, so just take keyframes on either side */ + /* prev frame */ + pso->prevFrame = (ak->prev) ? (ak->prev->cfra) : (pso->cframe - 1); + RNA_int_set(op->ptr, "prev_frame", pso->prevFrame); + /* next frame */ + pso->nextFrame = (ak->next) ? (ak->next->cfra) : (pso->cframe + 1); + RNA_int_set(op->ptr, "next_frame", pso->nextFrame); + } + + /* apply NLA mapping corrections so the frame lookups work */ + for (uint ob_index = 0; ob_index < pso->objects_len; ob_index++) { + tPoseSlideObject *ob_data = &pso->ob_data_array[ob_index]; + if (ob_data->valid) { + ob_data->prevFrameF = BKE_nla_tweakedit_remap( + ob_data->ob->adt, pso->prevFrame, NLATIME_CONVERT_UNMAP); + ob_data->nextFrameF = BKE_nla_tweakedit_remap( + ob_data->ob->adt, pso->nextFrame, NLATIME_CONVERT_UNMAP); + } + } + } + else { + BKE_report(op->reports, RPT_ERROR, "No keyframes to slide between"); + pose_slide_exit(op); + return OPERATOR_CANCELLED; + } + + /* initial apply for operator... */ + /* TODO: need to calculate percentage for initial round too... */ + pose_slide_apply(C, pso); + + /* depsgraph updates + redraws */ + pose_slide_refresh(C, pso); + + /* set cursor to indicate modal */ + WM_cursor_modal_set(win, BC_EW_SCROLLCURSOR); + + /* header print */ + pose_slide_draw_status(pso); + + /* add a modal handler for this operator */ + WM_event_add_modal_handler(C, op); + return OPERATOR_RUNNING_MODAL; } /* calculate percentage based on position of mouse (we only use x-axis for now. * since this is more convenient for users to do), and store new percentage value */ -static void pose_slide_mouse_update_percentage(tPoseSlideOp *pso, wmOperator *op, const wmEvent *event) +static void pose_slide_mouse_update_percentage(tPoseSlideOp *pso, + wmOperator *op, + const wmEvent *event) { - pso->percentage = (event->x - pso->ar->winrct.xmin) / ((float)pso->ar->winx); - RNA_float_set(op->ptr, "percentage", pso->percentage); + pso->percentage = (event->x - pso->ar->winrct.xmin) / ((float)pso->ar->winx); + RNA_float_set(op->ptr, "percentage", pso->percentage); } /* handle an event to toggle channels mode */ -static void pose_slide_toggle_channels_mode(wmOperator *op, tPoseSlideOp *pso, ePoseSlide_Channels channel) +static void pose_slide_toggle_channels_mode(wmOperator *op, + tPoseSlideOp *pso, + ePoseSlide_Channels channel) { - /* Turn channel on or off? */ - if (pso->channels == channel) { - /* Already limiting to transform only, so pressing this again turns it off */ - pso->channels = PS_TFM_ALL; - } - else { - /* Only this set of channels */ - pso->channels = channel; - } - RNA_enum_set(op->ptr, "channels", pso->channels); - - - /* Reset axis limits too for good measure */ - pso->axislock = 0; - RNA_enum_set(op->ptr, "axis_lock", pso->axislock); + /* Turn channel on or off? */ + if (pso->channels == channel) { + /* Already limiting to transform only, so pressing this again turns it off */ + pso->channels = PS_TFM_ALL; + } + else { + /* Only this set of channels */ + pso->channels = channel; + } + RNA_enum_set(op->ptr, "channels", pso->channels); + + /* Reset axis limits too for good measure */ + pso->axislock = 0; + RNA_enum_set(op->ptr, "axis_lock", pso->axislock); } /* handle an event to toggle axis locks - returns whether any change in state is needed */ -static bool pose_slide_toggle_axis_locks(wmOperator *op, tPoseSlideOp *pso, ePoseSlide_AxisLock axis) +static bool pose_slide_toggle_axis_locks(wmOperator *op, + tPoseSlideOp *pso, + ePoseSlide_AxisLock axis) { - /* Axis can only be set when a transform is set - it doesn't make sense otherwise */ - if (ELEM(pso->channels, PS_TFM_ALL, PS_TFM_BBONE_SHAPE, PS_TFM_PROPS)) { - pso->axislock = 0; - RNA_enum_set(op->ptr, "axis_lock", pso->axislock); - return false; - } - - /* Turn on or off? */ - if (pso->axislock == axis) { - /* Already limiting on this axis, so turn off */ - pso->axislock = 0; - } - else { - /* Only this axis */ - pso->axislock = axis; - } - RNA_enum_set(op->ptr, "axis_lock", pso->axislock); - - /* Setting changed, so pose update is needed */ - return true; + /* Axis can only be set when a transform is set - it doesn't make sense otherwise */ + if (ELEM(pso->channels, PS_TFM_ALL, PS_TFM_BBONE_SHAPE, PS_TFM_PROPS)) { + pso->axislock = 0; + RNA_enum_set(op->ptr, "axis_lock", pso->axislock); + return false; + } + + /* Turn on or off? */ + if (pso->axislock == axis) { + /* Already limiting on this axis, so turn off */ + pso->axislock = 0; + } + else { + /* Only this axis */ + pso->axislock = axis; + } + RNA_enum_set(op->ptr, "axis_lock", pso->axislock); + + /* Setting changed, so pose update is needed */ + return true; } /* common code for modal() */ static int pose_slide_modal(bContext *C, wmOperator *op, const wmEvent *event) { - tPoseSlideOp *pso = op->customdata; - wmWindow *win = CTX_wm_window(C); - bool do_pose_update = false; - - const bool has_numinput = hasNumInput(&pso->num); - - switch (event->type) { - case LEFTMOUSE: /* confirm */ - case RETKEY: - case PADENTER: - { - /* return to normal cursor and header status */ - ED_area_status_text(pso->sa, NULL); - WM_cursor_modal_restore(win); - - /* insert keyframes as required... */ - pose_slide_autoKeyframe(C, pso); - pose_slide_exit(op); - - /* done! */ - return OPERATOR_FINISHED; - } - - case ESCKEY: /* cancel */ - case RIGHTMOUSE: - { - /* return to normal cursor and header status */ - ED_area_status_text(pso->sa, NULL); - WM_cursor_modal_restore(win); - - /* reset transforms back to original state */ - pose_slide_reset(pso); - - /* depsgraph updates + redraws */ - pose_slide_refresh(C, pso); - - /* clean up temp data */ - pose_slide_exit(op); - - /* canceled! */ - return OPERATOR_CANCELLED; - } - - /* Percentage Chane... */ - case MOUSEMOVE: /* calculate new position */ - { - /* only handle mousemove if not doing numinput */ - if (has_numinput == false) { - /* update percentage based on position of mouse */ - pose_slide_mouse_update_percentage(pso, op, event); - - /* update pose to reflect the new values (see below) */ - do_pose_update = true; - } - break; - } - default: - { - if ((event->val == KM_PRESS) && handleNumInput(C, &pso->num, event)) { - float value; - - /* Grab percentage from numeric input, and store this new value for redo - * NOTE: users see ints, while internally we use a 0-1 float - */ - value = pso->percentage * 100.0f; - applyNumInput(&pso->num, &value); - - pso->percentage = value / 100.0f; - CLAMP(pso->percentage, 0.0f, 1.0f); - RNA_float_set(op->ptr, "percentage", pso->percentage); - - /* Update pose to reflect the new values (see below) */ - do_pose_update = true; - break; - } - else if (event->val == KM_PRESS) { - switch (event->type) { - /* Transform Channel Limits */ - /* XXX: Replace these hardcoded hotkeys with a modalmap that can be customised */ - case GKEY: /* Location */ - { - pose_slide_toggle_channels_mode(op, pso, PS_TFM_LOC); - do_pose_update = true; - break; - } - case RKEY: /* Rotation */ - { - pose_slide_toggle_channels_mode(op, pso, PS_TFM_ROT); - do_pose_update = true; - break; - } - case SKEY: /* Scale */ - { - pose_slide_toggle_channels_mode(op, pso, PS_TFM_SIZE); - do_pose_update = true; - break; - } - case BKEY: /* Bendy Bones */ - { - pose_slide_toggle_channels_mode(op, pso, PS_TFM_BBONE_SHAPE); - do_pose_update = true; - break; - } - case CKEY: /* Custom Properties */ - { - pose_slide_toggle_channels_mode(op, pso, PS_TFM_PROPS); - do_pose_update = true; - break; - } - - - /* Axis Locks */ - /* XXX: Hardcoded... */ - case XKEY: - { - if (pose_slide_toggle_axis_locks(op, pso, PS_LOCK_X)) { - do_pose_update = true; - } - break; - } - case YKEY: - { - if (pose_slide_toggle_axis_locks(op, pso, PS_LOCK_Y)) { - do_pose_update = true; - } - break; - } - case ZKEY: - { - if (pose_slide_toggle_axis_locks(op, pso, PS_LOCK_Z)) { - do_pose_update = true; - } - break; - } - - - default: /* Some other unhandled key... */ - break; - } - } - else { - /* unhandled event - maybe it was some view manip? */ - /* allow to pass through */ - return OPERATOR_RUNNING_MODAL | OPERATOR_PASS_THROUGH; - } - } - } - - - /* perform pose updates - in response to some user action (e.g. pressing a key or moving the mouse) */ - if (do_pose_update) { - /* update percentage indicator in header */ - pose_slide_draw_status(pso); - - /* reset transforms (to avoid accumulation errors) */ - pose_slide_reset(pso); - - /* apply... */ - pose_slide_apply(C, pso); - } - - /* still running... */ - return OPERATOR_RUNNING_MODAL; + tPoseSlideOp *pso = op->customdata; + wmWindow *win = CTX_wm_window(C); + bool do_pose_update = false; + + const bool has_numinput = hasNumInput(&pso->num); + + switch (event->type) { + case LEFTMOUSE: /* confirm */ + case RETKEY: + case PADENTER: { + /* return to normal cursor and header status */ + ED_area_status_text(pso->sa, NULL); + WM_cursor_modal_restore(win); + + /* insert keyframes as required... */ + pose_slide_autoKeyframe(C, pso); + pose_slide_exit(op); + + /* done! */ + return OPERATOR_FINISHED; + } + + case ESCKEY: /* cancel */ + case RIGHTMOUSE: { + /* return to normal cursor and header status */ + ED_area_status_text(pso->sa, NULL); + WM_cursor_modal_restore(win); + + /* reset transforms back to original state */ + pose_slide_reset(pso); + + /* depsgraph updates + redraws */ + pose_slide_refresh(C, pso); + + /* clean up temp data */ + pose_slide_exit(op); + + /* canceled! */ + return OPERATOR_CANCELLED; + } + + /* Percentage Chane... */ + case MOUSEMOVE: /* calculate new position */ + { + /* only handle mousemove if not doing numinput */ + if (has_numinput == false) { + /* update percentage based on position of mouse */ + pose_slide_mouse_update_percentage(pso, op, event); + + /* update pose to reflect the new values (see below) */ + do_pose_update = true; + } + break; + } + default: { + if ((event->val == KM_PRESS) && handleNumInput(C, &pso->num, event)) { + float value; + + /* Grab percentage from numeric input, and store this new value for redo + * NOTE: users see ints, while internally we use a 0-1 float + */ + value = pso->percentage * 100.0f; + applyNumInput(&pso->num, &value); + + pso->percentage = value / 100.0f; + CLAMP(pso->percentage, 0.0f, 1.0f); + RNA_float_set(op->ptr, "percentage", pso->percentage); + + /* Update pose to reflect the new values (see below) */ + do_pose_update = true; + break; + } + else if (event->val == KM_PRESS) { + switch (event->type) { + /* Transform Channel Limits */ + /* XXX: Replace these hardcoded hotkeys with a modalmap that can be customised */ + case GKEY: /* Location */ + { + pose_slide_toggle_channels_mode(op, pso, PS_TFM_LOC); + do_pose_update = true; + break; + } + case RKEY: /* Rotation */ + { + pose_slide_toggle_channels_mode(op, pso, PS_TFM_ROT); + do_pose_update = true; + break; + } + case SKEY: /* Scale */ + { + pose_slide_toggle_channels_mode(op, pso, PS_TFM_SIZE); + do_pose_update = true; + break; + } + case BKEY: /* Bendy Bones */ + { + pose_slide_toggle_channels_mode(op, pso, PS_TFM_BBONE_SHAPE); + do_pose_update = true; + break; + } + case CKEY: /* Custom Properties */ + { + pose_slide_toggle_channels_mode(op, pso, PS_TFM_PROPS); + do_pose_update = true; + break; + } + + /* Axis Locks */ + /* XXX: Hardcoded... */ + case XKEY: { + if (pose_slide_toggle_axis_locks(op, pso, PS_LOCK_X)) { + do_pose_update = true; + } + break; + } + case YKEY: { + if (pose_slide_toggle_axis_locks(op, pso, PS_LOCK_Y)) { + do_pose_update = true; + } + break; + } + case ZKEY: { + if (pose_slide_toggle_axis_locks(op, pso, PS_LOCK_Z)) { + do_pose_update = true; + } + break; + } + + default: /* Some other unhandled key... */ + break; + } + } + else { + /* unhandled event - maybe it was some view manip? */ + /* allow to pass through */ + return OPERATOR_RUNNING_MODAL | OPERATOR_PASS_THROUGH; + } + } + } + + /* perform pose updates - in response to some user action (e.g. pressing a key or moving the mouse) */ + if (do_pose_update) { + /* update percentage indicator in header */ + pose_slide_draw_status(pso); + + /* reset transforms (to avoid accumulation errors) */ + pose_slide_reset(pso); + + /* apply... */ + pose_slide_apply(C, pso); + } + + /* still running... */ + return OPERATOR_RUNNING_MODAL; } /* common code for cancel() */ static void pose_slide_cancel(bContext *UNUSED(C), wmOperator *op) { - /* cleanup and done */ - pose_slide_exit(op); + /* cleanup and done */ + pose_slide_exit(op); } /* common code for exec() methods */ static int pose_slide_exec_common(bContext *C, wmOperator *op, tPoseSlideOp *pso) { - /* settings should have been set up ok for applying, so just apply! */ - pose_slide_apply(C, pso); + /* settings should have been set up ok for applying, so just apply! */ + pose_slide_apply(C, pso); - /* insert keyframes if needed */ - pose_slide_autoKeyframe(C, pso); + /* insert keyframes if needed */ + pose_slide_autoKeyframe(C, pso); - /* cleanup and done */ - pose_slide_exit(op); + /* cleanup and done */ + pose_slide_exit(op); - return OPERATOR_FINISHED; + return OPERATOR_FINISHED; } /* common code for defining RNA properties */ /* TODO: Skip save on these? */ 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.3, 0.7); - - 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"); + RNA_def_float_percentage(ot->srna, + "percentage", + 0.5f, + 0.0f, + 1.0f, + "Percentage", + "Weighting factor for which keyframe is favored more", + 0.3, + 0.7); + + 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"); } /* ------------------------------------ */ @@ -1150,59 +1199,59 @@ static void pose_slide_opdef_properties(wmOperatorType *ot) /* invoke() - for 'push' mode */ static int pose_slide_push_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - tPoseSlideOp *pso; + tPoseSlideOp *pso; - /* initialize data */ - if (pose_slide_init(C, op, POSESLIDE_PUSH) == 0) { - pose_slide_exit(op); - return OPERATOR_CANCELLED; - } - else - pso = op->customdata; + /* initialize data */ + if (pose_slide_init(C, op, POSESLIDE_PUSH) == 0) { + pose_slide_exit(op); + return OPERATOR_CANCELLED; + } + else + pso = op->customdata; - /* initialise percentage so that it won't pop on first mouse move */ - pose_slide_mouse_update_percentage(pso, op, event); + /* initialise percentage so that it won't pop on first mouse move */ + pose_slide_mouse_update_percentage(pso, op, event); - /* do common setup work */ - return pose_slide_invoke_common(C, op, pso); + /* do common setup work */ + return pose_slide_invoke_common(C, op, pso); } /* exec() - for push */ static int pose_slide_push_exec(bContext *C, wmOperator *op) { - tPoseSlideOp *pso; - - /* initialize data (from RNA-props) */ - if (pose_slide_init(C, op, POSESLIDE_PUSH) == 0) { - pose_slide_exit(op); - return OPERATOR_CANCELLED; - } - else - pso = op->customdata; - - /* do common exec work */ - return pose_slide_exec_common(C, op, pso); + tPoseSlideOp *pso; + + /* initialize data (from RNA-props) */ + if (pose_slide_init(C, op, POSESLIDE_PUSH) == 0) { + pose_slide_exit(op); + return OPERATOR_CANCELLED; + } + else + pso = op->customdata; + + /* do common exec work */ + return pose_slide_exec_common(C, op, pso); } void POSE_OT_push(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Push Pose"; - ot->idname = "POSE_OT_push"; - ot->description = "Exaggerate the current pose"; - - /* callbacks */ - ot->exec = pose_slide_push_exec; - ot->invoke = pose_slide_push_invoke; - ot->modal = pose_slide_modal; - ot->cancel = pose_slide_cancel; - ot->poll = ED_operator_posemode; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING; - - /* Properties */ - pose_slide_opdef_properties(ot); + /* identifiers */ + ot->name = "Push Pose"; + ot->idname = "POSE_OT_push"; + ot->description = "Exaggerate the current pose"; + + /* callbacks */ + ot->exec = pose_slide_push_exec; + ot->invoke = pose_slide_push_invoke; + ot->modal = pose_slide_modal; + ot->cancel = pose_slide_cancel; + ot->poll = ED_operator_posemode; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING; + + /* Properties */ + pose_slide_opdef_properties(ot); } /* ........................ */ @@ -1210,59 +1259,59 @@ void POSE_OT_push(wmOperatorType *ot) /* invoke() - for 'relax' mode */ static int pose_slide_relax_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - tPoseSlideOp *pso; + tPoseSlideOp *pso; - /* initialize data */ - if (pose_slide_init(C, op, POSESLIDE_RELAX) == 0) { - pose_slide_exit(op); - return OPERATOR_CANCELLED; - } - else - pso = op->customdata; + /* initialize data */ + if (pose_slide_init(C, op, POSESLIDE_RELAX) == 0) { + pose_slide_exit(op); + return OPERATOR_CANCELLED; + } + else + pso = op->customdata; - /* initialise percentage so that it won't pop on first mouse move */ - pose_slide_mouse_update_percentage(pso, op, event); + /* initialise percentage so that it won't pop on first mouse move */ + pose_slide_mouse_update_percentage(pso, op, event); - /* do common setup work */ - return pose_slide_invoke_common(C, op, pso); + /* do common setup work */ + return pose_slide_invoke_common(C, op, pso); } /* exec() - for relax */ static int pose_slide_relax_exec(bContext *C, wmOperator *op) { - tPoseSlideOp *pso; - - /* initialize data (from RNA-props) */ - if (pose_slide_init(C, op, POSESLIDE_RELAX) == 0) { - pose_slide_exit(op); - return OPERATOR_CANCELLED; - } - else - pso = op->customdata; - - /* do common exec work */ - return pose_slide_exec_common(C, op, pso); + tPoseSlideOp *pso; + + /* initialize data (from RNA-props) */ + if (pose_slide_init(C, op, POSESLIDE_RELAX) == 0) { + pose_slide_exit(op); + return OPERATOR_CANCELLED; + } + else + pso = op->customdata; + + /* do common exec work */ + return pose_slide_exec_common(C, op, pso); } void POSE_OT_relax(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Relax Pose"; - ot->idname = "POSE_OT_relax"; - ot->description = "Make the current pose more similar to its surrounding ones"; - - /* callbacks */ - ot->exec = pose_slide_relax_exec; - ot->invoke = pose_slide_relax_invoke; - ot->modal = pose_slide_modal; - ot->cancel = pose_slide_cancel; - ot->poll = ED_operator_posemode; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING; - - /* Properties */ - pose_slide_opdef_properties(ot); + /* identifiers */ + ot->name = "Relax Pose"; + ot->idname = "POSE_OT_relax"; + ot->description = "Make the current pose more similar to its surrounding ones"; + + /* callbacks */ + ot->exec = pose_slide_relax_exec; + ot->invoke = pose_slide_relax_invoke; + ot->modal = pose_slide_modal; + ot->cancel = pose_slide_cancel; + ot->poll = ED_operator_posemode; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING; + + /* Properties */ + pose_slide_opdef_properties(ot); } /* ........................ */ @@ -1270,59 +1319,59 @@ void POSE_OT_relax(wmOperatorType *ot) /* invoke() - for 'breakdown' mode */ static int pose_slide_breakdown_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - tPoseSlideOp *pso; + tPoseSlideOp *pso; - /* initialize data */ - if (pose_slide_init(C, op, POSESLIDE_BREAKDOWN) == 0) { - pose_slide_exit(op); - return OPERATOR_CANCELLED; - } - else - pso = op->customdata; + /* initialize data */ + if (pose_slide_init(C, op, POSESLIDE_BREAKDOWN) == 0) { + pose_slide_exit(op); + return OPERATOR_CANCELLED; + } + else + pso = op->customdata; - /* initialise percentage so that it won't pop on first mouse move */ - pose_slide_mouse_update_percentage(pso, op, event); + /* initialise percentage so that it won't pop on first mouse move */ + pose_slide_mouse_update_percentage(pso, op, event); - /* do common setup work */ - return pose_slide_invoke_common(C, op, pso); + /* do common setup work */ + return pose_slide_invoke_common(C, op, pso); } /* exec() - for breakdown */ static int pose_slide_breakdown_exec(bContext *C, wmOperator *op) { - tPoseSlideOp *pso; - - /* initialize data (from RNA-props) */ - if (pose_slide_init(C, op, POSESLIDE_BREAKDOWN) == 0) { - pose_slide_exit(op); - return OPERATOR_CANCELLED; - } - else - pso = op->customdata; - - /* do common exec work */ - return pose_slide_exec_common(C, op, pso); + tPoseSlideOp *pso; + + /* initialize data (from RNA-props) */ + if (pose_slide_init(C, op, POSESLIDE_BREAKDOWN) == 0) { + pose_slide_exit(op); + return OPERATOR_CANCELLED; + } + else + pso = op->customdata; + + /* do common exec work */ + return pose_slide_exec_common(C, op, pso); } void POSE_OT_breakdown(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Pose Breakdowner"; - ot->idname = "POSE_OT_breakdown"; - ot->description = "Create a suitable breakdown pose on the current frame"; - - /* callbacks */ - ot->exec = pose_slide_breakdown_exec; - ot->invoke = pose_slide_breakdown_invoke; - ot->modal = pose_slide_modal; - ot->cancel = pose_slide_cancel; - ot->poll = ED_operator_posemode; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING; - - /* Properties */ - pose_slide_opdef_properties(ot); + /* identifiers */ + ot->name = "Pose Breakdowner"; + ot->idname = "POSE_OT_breakdown"; + ot->description = "Create a suitable breakdown pose on the current frame"; + + /* callbacks */ + ot->exec = pose_slide_breakdown_exec; + ot->invoke = pose_slide_breakdown_invoke; + ot->modal = pose_slide_modal; + ot->cancel = pose_slide_cancel; + ot->poll = ED_operator_posemode; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING; + + /* Properties */ + pose_slide_opdef_properties(ot); } /* **************************************************** */ @@ -1330,30 +1379,30 @@ void POSE_OT_breakdown(wmOperatorType *ot) /* "termination conditions" - i.e. when we stop */ typedef enum ePosePropagate_Termination { - /* stop after the current hold ends */ - POSE_PROPAGATE_SMART_HOLDS = 0, - /* only do on the last keyframe */ - POSE_PROPAGATE_LAST_KEY, - /* stop after the next keyframe */ - POSE_PROPAGATE_NEXT_KEY, - /* stop after the specified frame */ - POSE_PROPAGATE_BEFORE_FRAME, - /* stop when we run out of keyframes */ - POSE_PROPAGATE_BEFORE_END, - - /* only do on keyframes that are selected */ - POSE_PROPAGATE_SELECTED_KEYS, - /* only do on the frames where markers are selected */ - POSE_PROPAGATE_SELECTED_MARKERS, + /* stop after the current hold ends */ + POSE_PROPAGATE_SMART_HOLDS = 0, + /* only do on the last keyframe */ + POSE_PROPAGATE_LAST_KEY, + /* stop after the next keyframe */ + POSE_PROPAGATE_NEXT_KEY, + /* stop after the specified frame */ + POSE_PROPAGATE_BEFORE_FRAME, + /* stop when we run out of keyframes */ + POSE_PROPAGATE_BEFORE_END, + + /* only do on keyframes that are selected */ + POSE_PROPAGATE_SELECTED_KEYS, + /* only do on the frames where markers are selected */ + POSE_PROPAGATE_SELECTED_MARKERS, } ePosePropagate_Termination; /* termination data needed for some modes - assumes only one of these entries will be needed at a time */ typedef union tPosePropagate_ModeData { - /* smart holds + before frame: frame number to stop on */ - float end_frame; + /* smart holds + before frame: frame number to stop on */ + float end_frame; - /* selected markers: listbase for CfraElem's marking these frames */ - ListBase sel_markers; + /* selected markers: listbase for CfraElem's marking these frames */ + ListBase sel_markers; } tPosePropagate_ModeData; /* --------------------------------- */ @@ -1365,333 +1414,371 @@ typedef union tPosePropagate_ModeData { */ static float pose_propagate_get_boneHoldEndFrame(tPChanFCurveLink *pfl, float startFrame) { - DLRBT_Tree keys; - - Object *ob = pfl->ob; - AnimData *adt = ob->adt; - LinkData *ld; - float endFrame = startFrame; - - /* set up optimized data-structures for searching for relevant keyframes + holds */ - BLI_dlrbTree_init(&keys); - - for (ld = pfl->fcurves.first; ld; ld = ld->next) { - FCurve *fcu = (FCurve *)ld->data; - fcurve_to_keylist(adt, fcu, &keys, 0); - } - - /* find the long keyframe (i.e. hold), and hence obtain the endFrame value - * - the best case would be one that starts on the frame itself - */ - ActKeyColumn *ab = (ActKeyColumn *)BLI_dlrbTree_search_exact(&keys, compare_ak_cfraPtr, &startFrame); - - /* There are only two cases for no-exact match: - * 1) the current frame is just before another key but not on a key itself - * 2) the current frame is on a key, but that key doesn't link to the next - * - * If we've got the first case, then we can search for another block, - * otherwise forget it, as we'd be overwriting some valid data. - */ - if (ab == NULL) { - /* we've got case 1, so try the one after */ - ab = (ActKeyColumn *)BLI_dlrbTree_search_next(&keys, compare_ak_cfraPtr, &startFrame); - - if ((actkeyblock_get_valid_hold(ab) & ACTKEYBLOCK_FLAG_STATIC_HOLD) == 0) { - /* try the block before this frame then as last resort */ - ab = (ActKeyColumn *)BLI_dlrbTree_search_prev(&keys, compare_ak_cfraPtr, &startFrame); - } - } - - /* whatever happens, stop searching now... */ - if ((actkeyblock_get_valid_hold(ab) & ACTKEYBLOCK_FLAG_STATIC_HOLD) == 0) { - /* restrict range to just the frame itself - * i.e. everything is in motion, so no holds to safely overwrite - */ - ab = NULL; - } - - /* check if we can go any further than we've already gone */ - if (ab) { - /* go to next if it is also valid and meets "extension" criteria */ - while (ab->next) { - ActKeyColumn *abn = ab->next; - - /* must be valid */ - if ((actkeyblock_get_valid_hold(abn) & ACTKEYBLOCK_FLAG_STATIC_HOLD) == 0) { - break; - } - /* should have the same number of curves */ - if (ab->totblock != abn->totblock) { - break; - } - - /* we can extend the bounds to the end of this "next" block now */ - ab = abn; - } - - /* end frame can now take the value of the end of the block */ - endFrame = ab->next->cfra; - } - - /* free temp memory */ - BLI_dlrbTree_free(&keys); - - /* return the end frame we've found */ - return endFrame; + DLRBT_Tree keys; + + Object *ob = pfl->ob; + AnimData *adt = ob->adt; + LinkData *ld; + float endFrame = startFrame; + + /* set up optimized data-structures for searching for relevant keyframes + holds */ + BLI_dlrbTree_init(&keys); + + for (ld = pfl->fcurves.first; ld; ld = ld->next) { + FCurve *fcu = (FCurve *)ld->data; + fcurve_to_keylist(adt, fcu, &keys, 0); + } + + /* find the long keyframe (i.e. hold), and hence obtain the endFrame value + * - the best case would be one that starts on the frame itself + */ + ActKeyColumn *ab = (ActKeyColumn *)BLI_dlrbTree_search_exact( + &keys, compare_ak_cfraPtr, &startFrame); + + /* There are only two cases for no-exact match: + * 1) the current frame is just before another key but not on a key itself + * 2) the current frame is on a key, but that key doesn't link to the next + * + * If we've got the first case, then we can search for another block, + * otherwise forget it, as we'd be overwriting some valid data. + */ + if (ab == NULL) { + /* we've got case 1, so try the one after */ + ab = (ActKeyColumn *)BLI_dlrbTree_search_next(&keys, compare_ak_cfraPtr, &startFrame); + + if ((actkeyblock_get_valid_hold(ab) & ACTKEYBLOCK_FLAG_STATIC_HOLD) == 0) { + /* try the block before this frame then as last resort */ + ab = (ActKeyColumn *)BLI_dlrbTree_search_prev(&keys, compare_ak_cfraPtr, &startFrame); + } + } + + /* whatever happens, stop searching now... */ + if ((actkeyblock_get_valid_hold(ab) & ACTKEYBLOCK_FLAG_STATIC_HOLD) == 0) { + /* restrict range to just the frame itself + * i.e. everything is in motion, so no holds to safely overwrite + */ + ab = NULL; + } + + /* check if we can go any further than we've already gone */ + if (ab) { + /* go to next if it is also valid and meets "extension" criteria */ + while (ab->next) { + ActKeyColumn *abn = ab->next; + + /* must be valid */ + if ((actkeyblock_get_valid_hold(abn) & ACTKEYBLOCK_FLAG_STATIC_HOLD) == 0) { + break; + } + /* should have the same number of curves */ + if (ab->totblock != abn->totblock) { + break; + } + + /* we can extend the bounds to the end of this "next" block now */ + ab = abn; + } + + /* end frame can now take the value of the end of the block */ + endFrame = ab->next->cfra; + } + + /* free temp memory */ + BLI_dlrbTree_free(&keys); + + /* return the end frame we've found */ + return endFrame; } /* get reference value from F-Curve using RNA */ static bool pose_propagate_get_refVal(Object *ob, FCurve *fcu, float *value) { - PointerRNA id_ptr, ptr; - PropertyRNA *prop; - bool found = false; - - /* base pointer is always the object -> id_ptr */ - RNA_id_pointer_create(&ob->id, &id_ptr); - - /* resolve the property... */ - if (RNA_path_resolve_property(&id_ptr, fcu->rna_path, &ptr, &prop)) { - if (RNA_property_array_check(prop)) { - /* array */ - if (fcu->array_index < RNA_property_array_length(&ptr, prop)) { - found = true; - switch (RNA_property_type(prop)) { - case PROP_BOOLEAN: - *value = (float)RNA_property_boolean_get_index(&ptr, prop, fcu->array_index); - break; - case PROP_INT: - *value = (float)RNA_property_int_get_index(&ptr, prop, fcu->array_index); - break; - case PROP_FLOAT: - *value = RNA_property_float_get_index(&ptr, prop, fcu->array_index); - break; - default: - found = false; - break; - } - } - } - else { - /* not an array */ - found = true; - switch (RNA_property_type(prop)) { - case PROP_BOOLEAN: - *value = (float)RNA_property_boolean_get(&ptr, prop); - break; - case PROP_INT: - *value = (float)RNA_property_int_get(&ptr, prop); - break; - case PROP_ENUM: - *value = (float)RNA_property_enum_get(&ptr, prop); - break; - case PROP_FLOAT: - *value = RNA_property_float_get(&ptr, prop); - break; - default: - found = false; - break; - } - } - } - - return found; + PointerRNA id_ptr, ptr; + PropertyRNA *prop; + bool found = false; + + /* base pointer is always the object -> id_ptr */ + RNA_id_pointer_create(&ob->id, &id_ptr); + + /* resolve the property... */ + if (RNA_path_resolve_property(&id_ptr, fcu->rna_path, &ptr, &prop)) { + if (RNA_property_array_check(prop)) { + /* array */ + if (fcu->array_index < RNA_property_array_length(&ptr, prop)) { + found = true; + switch (RNA_property_type(prop)) { + case PROP_BOOLEAN: + *value = (float)RNA_property_boolean_get_index(&ptr, prop, fcu->array_index); + break; + case PROP_INT: + *value = (float)RNA_property_int_get_index(&ptr, prop, fcu->array_index); + break; + case PROP_FLOAT: + *value = RNA_property_float_get_index(&ptr, prop, fcu->array_index); + break; + default: + found = false; + break; + } + } + } + else { + /* not an array */ + found = true; + switch (RNA_property_type(prop)) { + case PROP_BOOLEAN: + *value = (float)RNA_property_boolean_get(&ptr, prop); + break; + case PROP_INT: + *value = (float)RNA_property_int_get(&ptr, prop); + break; + case PROP_ENUM: + *value = (float)RNA_property_enum_get(&ptr, prop); + break; + case PROP_FLOAT: + *value = RNA_property_float_get(&ptr, prop); + break; + default: + found = false; + break; + } + } + } + + return found; } /* propagate just works along each F-Curve in turn */ -static void pose_propagate_fcurve(wmOperator *op, Object *ob, FCurve *fcu, - float startFrame, tPosePropagate_ModeData modeData) +static void pose_propagate_fcurve( + wmOperator *op, Object *ob, FCurve *fcu, float startFrame, tPosePropagate_ModeData modeData) { - const int mode = RNA_enum_get(op->ptr, "mode"); - - BezTriple *bezt; - float refVal = 0.0f; - bool keyExists; - int i, match; - short first = 1; - - /* skip if no keyframes to edit */ - if ((fcu->bezt == NULL) || (fcu->totvert < 2)) - return; - - /* find the reference value from bones directly, which means that the user - * doesn't need to firstly keyframe the pose (though this doesn't mean that - * they can't either) - */ - if (!pose_propagate_get_refVal(ob, fcu, &refVal)) - return; - - /* find the first keyframe to start propagating from - * - if there's a keyframe on the current frame, we probably want to save this value there too - * since it may be as of yet unkeyed - * - if starting before the starting frame, don't touch the key, as it may have had some valid - * values - * - if only doing selected keyframes, start from the first one - */ - if (mode != POSE_PROPAGATE_SELECTED_KEYS) { - match = binarysearch_bezt_index(fcu->bezt, startFrame, fcu->totvert, &keyExists); - - if (fcu->bezt[match].vec[1][0] < startFrame) - i = match + 1; - else - i = match; - } - else { - /* selected - start from first keyframe */ - i = 0; - } - - for (bezt = &fcu->bezt[i]; i < fcu->totvert; i++, bezt++) { - /* additional termination conditions based on the operator 'mode' property go here... */ - if (ELEM(mode, POSE_PROPAGATE_BEFORE_FRAME, POSE_PROPAGATE_SMART_HOLDS)) { - /* stop if keyframe is outside the accepted range */ - if (bezt->vec[1][0] > modeData.end_frame) - break; - } - else if (mode == POSE_PROPAGATE_NEXT_KEY) { - /* stop after the first keyframe has been processed */ - if (first == 0) - break; - } - else if (mode == POSE_PROPAGATE_LAST_KEY) { - /* only affect this frame if it will be the last one */ - if (i != (fcu->totvert - 1)) - continue; - } - else if (mode == POSE_PROPAGATE_SELECTED_MARKERS) { - /* only allow if there's a marker on this frame */ - CfraElem *ce = NULL; - - /* stop on matching marker if there is one */ - for (ce = modeData.sel_markers.first; ce; ce = ce->next) { - if (ce->cfra == round_fl_to_int(bezt->vec[1][0])) - break; - } - - /* skip this keyframe if no marker */ - if (ce == NULL) - continue; - } - else if (mode == POSE_PROPAGATE_SELECTED_KEYS) { - /* only allow if this keyframe is already selected - skip otherwise */ - if (BEZT_ISSEL_ANY(bezt) == 0) - continue; - } - - /* just flatten handles, since values will now be the same either side... */ - /* TODO: perhaps a fade-out modulation of the value is required here (optional once again)? */ - bezt->vec[0][1] = bezt->vec[1][1] = bezt->vec[2][1] = refVal; - - /* select keyframe to indicate that it's been changed */ - bezt->f2 |= SELECT; - first = 0; - } + const int mode = RNA_enum_get(op->ptr, "mode"); + + BezTriple *bezt; + float refVal = 0.0f; + bool keyExists; + int i, match; + short first = 1; + + /* skip if no keyframes to edit */ + if ((fcu->bezt == NULL) || (fcu->totvert < 2)) + return; + + /* find the reference value from bones directly, which means that the user + * doesn't need to firstly keyframe the pose (though this doesn't mean that + * they can't either) + */ + if (!pose_propagate_get_refVal(ob, fcu, &refVal)) + return; + + /* find the first keyframe to start propagating from + * - if there's a keyframe on the current frame, we probably want to save this value there too + * since it may be as of yet unkeyed + * - if starting before the starting frame, don't touch the key, as it may have had some valid + * values + * - if only doing selected keyframes, start from the first one + */ + if (mode != POSE_PROPAGATE_SELECTED_KEYS) { + match = binarysearch_bezt_index(fcu->bezt, startFrame, fcu->totvert, &keyExists); + + if (fcu->bezt[match].vec[1][0] < startFrame) + i = match + 1; + else + i = match; + } + else { + /* selected - start from first keyframe */ + i = 0; + } + + for (bezt = &fcu->bezt[i]; i < fcu->totvert; i++, bezt++) { + /* additional termination conditions based on the operator 'mode' property go here... */ + if (ELEM(mode, POSE_PROPAGATE_BEFORE_FRAME, POSE_PROPAGATE_SMART_HOLDS)) { + /* stop if keyframe is outside the accepted range */ + if (bezt->vec[1][0] > modeData.end_frame) + break; + } + else if (mode == POSE_PROPAGATE_NEXT_KEY) { + /* stop after the first keyframe has been processed */ + if (first == 0) + break; + } + else if (mode == POSE_PROPAGATE_LAST_KEY) { + /* only affect this frame if it will be the last one */ + if (i != (fcu->totvert - 1)) + continue; + } + else if (mode == POSE_PROPAGATE_SELECTED_MARKERS) { + /* only allow if there's a marker on this frame */ + CfraElem *ce = NULL; + + /* stop on matching marker if there is one */ + for (ce = modeData.sel_markers.first; ce; ce = ce->next) { + if (ce->cfra == round_fl_to_int(bezt->vec[1][0])) + break; + } + + /* skip this keyframe if no marker */ + if (ce == NULL) + continue; + } + else if (mode == POSE_PROPAGATE_SELECTED_KEYS) { + /* only allow if this keyframe is already selected - skip otherwise */ + if (BEZT_ISSEL_ANY(bezt) == 0) + continue; + } + + /* just flatten handles, since values will now be the same either side... */ + /* TODO: perhaps a fade-out modulation of the value is required here (optional once again)? */ + bezt->vec[0][1] = bezt->vec[1][1] = bezt->vec[2][1] = refVal; + + /* select keyframe to indicate that it's been changed */ + bezt->f2 |= SELECT; + first = 0; + } } /* --------------------------------- */ static int pose_propagate_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); - ViewLayer *view_layer = CTX_data_view_layer(C); - View3D *v3d = CTX_wm_view3d(C); - - ListBase pflinks = {NULL, NULL}; - tPChanFCurveLink *pfl; - - tPosePropagate_ModeData modeData; - const int mode = RNA_enum_get(op->ptr, "mode"); - - /* isolate F-Curves related to the selected bones */ - poseAnim_mapping_get(C, &pflinks); - - if (BLI_listbase_is_empty(&pflinks)) { - /* There is a change the reason the list is empty is that there is no valid object to propagate poses for. - * This is very unlikely though, so we focus on the most likely issue. */ - BKE_report(op->reports, RPT_ERROR, "No keyframed poses to propagate to"); - return OPERATOR_CANCELLED; - } - - /* mode-specific data preprocessing (requiring no access to curves) */ - if (mode == POSE_PROPAGATE_SELECTED_MARKERS) { - /* get a list of selected markers */ - ED_markers_make_cfra_list(&scene->markers, &modeData.sel_markers, SELECT); - } - else { - /* assume everything else wants endFrame */ - modeData.end_frame = RNA_float_get(op->ptr, "end_frame"); - } - - /* for each bone, perform the copying required */ - for (pfl = pflinks.first; pfl; pfl = pfl->next) { - LinkData *ld; - - /* mode-specific data preprocessing (requiring access to all curves) */ - if (mode == POSE_PROPAGATE_SMART_HOLDS) { - /* we store in endFrame the end frame of the "long keyframe" (i.e. a held value) starting - * from the keyframe that occurs after the current frame - */ - modeData.end_frame = pose_propagate_get_boneHoldEndFrame(pfl, (float)CFRA); - } - - /* go through propagating pose to keyframes, curve by curve */ - for (ld = pfl->fcurves.first; ld; ld = ld->next) { - pose_propagate_fcurve(op, pfl->ob, (FCurve *)ld->data, (float)CFRA, modeData); - } - } - - /* free temp data */ - poseAnim_mapping_free(&pflinks); - - if (mode == POSE_PROPAGATE_SELECTED_MARKERS) - BLI_freelistN(&modeData.sel_markers); - - /* updates + notifiers */ - FOREACH_OBJECT_IN_MODE_BEGIN(view_layer, v3d, OB_ARMATURE, OB_MODE_POSE, ob) { - poseAnim_mapping_refresh(C, scene, ob); - } FOREACH_OBJECT_IN_MODE_END; - - return OPERATOR_FINISHED; + Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + View3D *v3d = CTX_wm_view3d(C); + + ListBase pflinks = {NULL, NULL}; + tPChanFCurveLink *pfl; + + tPosePropagate_ModeData modeData; + const int mode = RNA_enum_get(op->ptr, "mode"); + + /* isolate F-Curves related to the selected bones */ + poseAnim_mapping_get(C, &pflinks); + + if (BLI_listbase_is_empty(&pflinks)) { + /* There is a change the reason the list is empty is that there is no valid object to propagate poses for. + * This is very unlikely though, so we focus on the most likely issue. */ + BKE_report(op->reports, RPT_ERROR, "No keyframed poses to propagate to"); + return OPERATOR_CANCELLED; + } + + /* mode-specific data preprocessing (requiring no access to curves) */ + if (mode == POSE_PROPAGATE_SELECTED_MARKERS) { + /* get a list of selected markers */ + ED_markers_make_cfra_list(&scene->markers, &modeData.sel_markers, SELECT); + } + else { + /* assume everything else wants endFrame */ + modeData.end_frame = RNA_float_get(op->ptr, "end_frame"); + } + + /* for each bone, perform the copying required */ + for (pfl = pflinks.first; pfl; pfl = pfl->next) { + LinkData *ld; + + /* mode-specific data preprocessing (requiring access to all curves) */ + if (mode == POSE_PROPAGATE_SMART_HOLDS) { + /* we store in endFrame the end frame of the "long keyframe" (i.e. a held value) starting + * from the keyframe that occurs after the current frame + */ + modeData.end_frame = pose_propagate_get_boneHoldEndFrame(pfl, (float)CFRA); + } + + /* go through propagating pose to keyframes, curve by curve */ + for (ld = pfl->fcurves.first; ld; ld = ld->next) { + pose_propagate_fcurve(op, pfl->ob, (FCurve *)ld->data, (float)CFRA, modeData); + } + } + + /* free temp data */ + poseAnim_mapping_free(&pflinks); + + if (mode == POSE_PROPAGATE_SELECTED_MARKERS) + BLI_freelistN(&modeData.sel_markers); + + /* updates + notifiers */ + FOREACH_OBJECT_IN_MODE_BEGIN (view_layer, v3d, OB_ARMATURE, OB_MODE_POSE, ob) { + poseAnim_mapping_refresh(C, scene, ob); + } + FOREACH_OBJECT_IN_MODE_END; + + return OPERATOR_FINISHED; } /* --------------------------------- */ void POSE_OT_propagate(wmOperatorType *ot) { - static const EnumPropertyItem terminate_items[] = { - {POSE_PROPAGATE_SMART_HOLDS, "WHILE_HELD", 0, "While Held", - "Propagate pose to all keyframes after current frame that don't change (Default behavior)"}, - {POSE_PROPAGATE_NEXT_KEY, "NEXT_KEY", 0, "To Next Keyframe", - "Propagate pose to first keyframe following the current frame only"}, - {POSE_PROPAGATE_LAST_KEY, "LAST_KEY", 0, "To Last Keyframe", - "Propagate pose to the last keyframe only (i.e. making action cyclic)"}, - {POSE_PROPAGATE_BEFORE_FRAME, "BEFORE_FRAME", 0, "Before Frame", - "Propagate pose to all keyframes between current frame and 'Frame' property"}, - {POSE_PROPAGATE_BEFORE_END, "BEFORE_END", 0, "Before Last Keyframe", - "Propagate pose to all keyframes from current frame until no more are found"}, - {POSE_PROPAGATE_SELECTED_KEYS, "SELECTED_KEYS", 0, "On Selected Keyframes", - "Propagate pose to all selected keyframes"}, - {POSE_PROPAGATE_SELECTED_MARKERS, "SELECTED_MARKERS", 0, "On Selected Markers", - "Propagate pose to all keyframes occurring on frames with Scene Markers after the current frame"}, - {0, NULL, 0, NULL, NULL}, - }; - - /* identifiers */ - ot->name = "Propagate Pose"; - ot->idname = "POSE_OT_propagate"; - ot->description = "Copy selected aspects of the current pose to subsequent poses already keyframed"; - - /* callbacks */ - ot->exec = pose_propagate_exec; - ot->poll = ED_operator_posemode; /* XXX: needs selected bones! */ - - /* flag */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - /* TODO: add "fade out" control for tapering off amount of propagation as time goes by? */ - ot->prop = RNA_def_enum(ot->srna, "mode", terminate_items, POSE_PROPAGATE_SMART_HOLDS, "Terminate Mode", "Method used to determine when to stop propagating pose to keyframes"); - RNA_def_float(ot->srna, "end_frame", 250.0, FLT_MIN, FLT_MAX, "End Frame", "Frame to stop propagating frames to (for 'Before Frame' mode)", 1.0, 250.0); + static const EnumPropertyItem terminate_items[] = { + {POSE_PROPAGATE_SMART_HOLDS, + "WHILE_HELD", + 0, + "While Held", + "Propagate pose to all keyframes after current frame that don't change (Default behavior)"}, + {POSE_PROPAGATE_NEXT_KEY, + "NEXT_KEY", + 0, + "To Next Keyframe", + "Propagate pose to first keyframe following the current frame only"}, + {POSE_PROPAGATE_LAST_KEY, + "LAST_KEY", + 0, + "To Last Keyframe", + "Propagate pose to the last keyframe only (i.e. making action cyclic)"}, + {POSE_PROPAGATE_BEFORE_FRAME, + "BEFORE_FRAME", + 0, + "Before Frame", + "Propagate pose to all keyframes between current frame and 'Frame' property"}, + {POSE_PROPAGATE_BEFORE_END, + "BEFORE_END", + 0, + "Before Last Keyframe", + "Propagate pose to all keyframes from current frame until no more are found"}, + {POSE_PROPAGATE_SELECTED_KEYS, + "SELECTED_KEYS", + 0, + "On Selected Keyframes", + "Propagate pose to all selected keyframes"}, + {POSE_PROPAGATE_SELECTED_MARKERS, + "SELECTED_MARKERS", + 0, + "On Selected Markers", + "Propagate pose to all keyframes occurring on frames with Scene Markers after the current " + "frame"}, + {0, NULL, 0, NULL, NULL}, + }; + + /* identifiers */ + ot->name = "Propagate Pose"; + ot->idname = "POSE_OT_propagate"; + ot->description = + "Copy selected aspects of the current pose to subsequent poses already keyframed"; + + /* callbacks */ + ot->exec = pose_propagate_exec; + ot->poll = ED_operator_posemode; /* XXX: needs selected bones! */ + + /* flag */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + /* TODO: add "fade out" control for tapering off amount of propagation as time goes by? */ + ot->prop = RNA_def_enum(ot->srna, + "mode", + terminate_items, + POSE_PROPAGATE_SMART_HOLDS, + "Terminate Mode", + "Method used to determine when to stop propagating pose to keyframes"); + RNA_def_float(ot->srna, + "end_frame", + 250.0, + FLT_MIN, + FLT_MAX, + "End Frame", + "Frame to stop propagating frames to (for 'Before Frame' mode)", + 1.0, + 250.0); } /* **************************************************** */ diff --git a/source/blender/editors/armature/pose_transform.c b/source/blender/editors/armature/pose_transform.c index cbb6d63aefe..3a4413f8c1d 100644 --- a/source/blender/editors/armature/pose_transform.c +++ b/source/blender/editors/armature/pose_transform.c @@ -61,7 +61,6 @@ #include "armature_intern.h" - /* ********************************************** */ /* Pose Apply */ @@ -69,216 +68,214 @@ * that are bone-parented to armature */ static void applyarmature_fix_boneparents(const bContext *C, Scene *scene, Object *armob) { - Depsgraph *depsgraph = CTX_data_depsgraph(C); - Main *bmain = CTX_data_main(C); - Object workob, *ob; - - /* go through all objects in database */ - for (ob = bmain->objects.first; ob; ob = ob->id.next) { - /* if parent is bone in this armature, apply corrections */ - if ((ob->parent == armob) && (ob->partype == PARBONE)) { - /* apply current transform from parent (not yet destroyed), - * then calculate new parent inverse matrix - */ - BKE_object_apply_mat4(ob, ob->obmat, false, false); - - BKE_object_workob_calc_parent(depsgraph, scene, ob, &workob); - invert_m4_m4(ob->parentinv, workob.obmat); - } - } + Depsgraph *depsgraph = CTX_data_depsgraph(C); + Main *bmain = CTX_data_main(C); + Object workob, *ob; + + /* go through all objects in database */ + for (ob = bmain->objects.first; ob; ob = ob->id.next) { + /* if parent is bone in this armature, apply corrections */ + if ((ob->parent == armob) && (ob->partype == PARBONE)) { + /* apply current transform from parent (not yet destroyed), + * then calculate new parent inverse matrix + */ + BKE_object_apply_mat4(ob, ob->obmat, false, false); + + BKE_object_workob_calc_parent(depsgraph, scene, ob, &workob); + invert_m4_m4(ob->parentinv, workob.obmat); + } + } } /* set the current pose as the restpose */ static int apply_armature_pose2bones_exec(bContext *C, wmOperator *op) { - Main *bmain = CTX_data_main(C); - Depsgraph *depsgraph = CTX_data_depsgraph(C); - Scene *scene = CTX_data_scene(C); - // must be active object, not edit-object - Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); - const Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob); - bArmature *arm = BKE_armature_from_object(ob); - bPose *pose; - bPoseChannel *pchan; - EditBone *curbone; - - /* don't check if editmode (should be done by caller) */ - if (ob->type != OB_ARMATURE) - return OPERATOR_CANCELLED; - if (BKE_object_obdata_is_libdata(ob)) { - BKE_report(op->reports, RPT_ERROR, "Cannot apply pose to lib-linked armature"); - return OPERATOR_CANCELLED; - } - - /* helpful warnings... */ - /* TODO: add warnings to be careful about actions, applying deforms first, etc. */ - if (ob->adt && ob->adt->action) - BKE_report(op->reports, RPT_WARNING, - "Actions on this armature will be destroyed by this new rest pose as the " - "transforms stored are relative to the old rest pose"); - - /* Get editbones of active armature to alter */ - ED_armature_to_edit(arm); - - /* get pose of active object and move it out of posemode */ - pose = ob->pose; - - for (pchan = pose->chanbase.first; pchan; pchan = pchan->next) { - const bPoseChannel *pchan_eval = BKE_pose_channel_find_name(ob_eval->pose, pchan->name); - curbone = ED_armature_ebone_find_name(arm->edbo, pchan->name); - - /* simply copy the head/tail values from pchan over to curbone */ - copy_v3_v3(curbone->head, pchan_eval->pose_head); - copy_v3_v3(curbone->tail, pchan_eval->pose_tail); - - /* fix roll: - * 1. find auto-calculated roll value for this bone now - * 2. remove this from the 'visual' y-rotation - */ - { - float premat[3][3], imat[3][3], pmat[3][3], tmat[3][3]; - float delta[3], eul[3]; - - /* obtain new auto y-rotation */ - sub_v3_v3v3(delta, curbone->tail, curbone->head); - vec_roll_to_mat3(delta, 0.0f, premat); - invert_m3_m3(imat, premat); - - /* get pchan 'visual' matrix */ - copy_m3_m4(pmat, pchan_eval->pose_mat); - - /* remove auto from visual and get euler rotation */ - mul_m3_m3m3(tmat, imat, pmat); - mat3_to_eul(eul, tmat); - - /* just use this euler-y as new roll value */ - curbone->roll = eul[1]; - } - - /* combine pose and rest values for bendy bone settings, - * then clear the pchan values (so we don't get a double-up) - */ - if (pchan->bone->segments > 1) { - /* combine rest/pose values */ - curbone->curveInX += pchan_eval->curveInX; - curbone->curveInY += pchan_eval->curveInY; - curbone->curveOutX += pchan_eval->curveOutX; - curbone->curveOutY += pchan_eval->curveOutY; - curbone->roll1 += pchan_eval->roll1; - curbone->roll2 += pchan_eval->roll2; - curbone->ease1 += pchan_eval->ease1; - curbone->ease2 += pchan_eval->ease2; - curbone->scaleIn += pchan_eval->scaleIn; - curbone->scaleOut += pchan_eval->scaleOut; - - /* reset pose values */ - pchan->curveInX = pchan->curveOutX = 0.0f; - pchan->curveInY = pchan->curveOutY = 0.0f; - pchan->roll1 = pchan->roll2 = 0.0f; - pchan->ease1 = pchan->ease2 = 0.0f; - pchan->scaleIn = pchan->scaleOut = 1.0f; - } - - /* clear transform values for pchan */ - zero_v3(pchan->loc); - zero_v3(pchan->eul); - unit_qt(pchan->quat); - unit_axis_angle(pchan->rotAxis, &pchan->rotAngle); - pchan->size[0] = pchan->size[1] = pchan->size[2] = 1.0f; - - /* set anim lock */ - curbone->flag |= BONE_UNKEYED; - } - - /* convert editbones back to bones, and then free the edit-data */ - ED_armature_from_edit(bmain, arm); - ED_armature_edit_free(arm); - - /* flush positions of posebones */ - BKE_pose_where_is(depsgraph, scene, ob); - - /* fix parenting of objects which are bone-parented */ - applyarmature_fix_boneparents(C, scene, ob); - - /* note, notifier might evolve */ - WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); - DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE); - - return OPERATOR_FINISHED; + Main *bmain = CTX_data_main(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + Scene *scene = CTX_data_scene(C); + // must be active object, not edit-object + Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); + const Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob); + bArmature *arm = BKE_armature_from_object(ob); + bPose *pose; + bPoseChannel *pchan; + EditBone *curbone; + + /* don't check if editmode (should be done by caller) */ + if (ob->type != OB_ARMATURE) + return OPERATOR_CANCELLED; + if (BKE_object_obdata_is_libdata(ob)) { + BKE_report(op->reports, RPT_ERROR, "Cannot apply pose to lib-linked armature"); + return OPERATOR_CANCELLED; + } + + /* helpful warnings... */ + /* TODO: add warnings to be careful about actions, applying deforms first, etc. */ + if (ob->adt && ob->adt->action) + BKE_report(op->reports, + RPT_WARNING, + "Actions on this armature will be destroyed by this new rest pose as the " + "transforms stored are relative to the old rest pose"); + + /* Get editbones of active armature to alter */ + ED_armature_to_edit(arm); + + /* get pose of active object and move it out of posemode */ + pose = ob->pose; + + for (pchan = pose->chanbase.first; pchan; pchan = pchan->next) { + const bPoseChannel *pchan_eval = BKE_pose_channel_find_name(ob_eval->pose, pchan->name); + curbone = ED_armature_ebone_find_name(arm->edbo, pchan->name); + + /* simply copy the head/tail values from pchan over to curbone */ + copy_v3_v3(curbone->head, pchan_eval->pose_head); + copy_v3_v3(curbone->tail, pchan_eval->pose_tail); + + /* fix roll: + * 1. find auto-calculated roll value for this bone now + * 2. remove this from the 'visual' y-rotation + */ + { + float premat[3][3], imat[3][3], pmat[3][3], tmat[3][3]; + float delta[3], eul[3]; + + /* obtain new auto y-rotation */ + sub_v3_v3v3(delta, curbone->tail, curbone->head); + vec_roll_to_mat3(delta, 0.0f, premat); + invert_m3_m3(imat, premat); + + /* get pchan 'visual' matrix */ + copy_m3_m4(pmat, pchan_eval->pose_mat); + + /* remove auto from visual and get euler rotation */ + mul_m3_m3m3(tmat, imat, pmat); + mat3_to_eul(eul, tmat); + + /* just use this euler-y as new roll value */ + curbone->roll = eul[1]; + } + + /* combine pose and rest values for bendy bone settings, + * then clear the pchan values (so we don't get a double-up) + */ + if (pchan->bone->segments > 1) { + /* combine rest/pose values */ + curbone->curveInX += pchan_eval->curveInX; + curbone->curveInY += pchan_eval->curveInY; + curbone->curveOutX += pchan_eval->curveOutX; + curbone->curveOutY += pchan_eval->curveOutY; + curbone->roll1 += pchan_eval->roll1; + curbone->roll2 += pchan_eval->roll2; + curbone->ease1 += pchan_eval->ease1; + curbone->ease2 += pchan_eval->ease2; + curbone->scaleIn += pchan_eval->scaleIn; + curbone->scaleOut += pchan_eval->scaleOut; + + /* reset pose values */ + pchan->curveInX = pchan->curveOutX = 0.0f; + pchan->curveInY = pchan->curveOutY = 0.0f; + pchan->roll1 = pchan->roll2 = 0.0f; + pchan->ease1 = pchan->ease2 = 0.0f; + pchan->scaleIn = pchan->scaleOut = 1.0f; + } + + /* clear transform values for pchan */ + zero_v3(pchan->loc); + zero_v3(pchan->eul); + unit_qt(pchan->quat); + unit_axis_angle(pchan->rotAxis, &pchan->rotAngle); + pchan->size[0] = pchan->size[1] = pchan->size[2] = 1.0f; + + /* set anim lock */ + curbone->flag |= BONE_UNKEYED; + } + + /* convert editbones back to bones, and then free the edit-data */ + ED_armature_from_edit(bmain, arm); + ED_armature_edit_free(arm); + + /* flush positions of posebones */ + BKE_pose_where_is(depsgraph, scene, ob); + + /* fix parenting of objects which are bone-parented */ + applyarmature_fix_boneparents(C, scene, ob); + + /* note, notifier might evolve */ + WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); + DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE); + + return OPERATOR_FINISHED; } void POSE_OT_armature_apply(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Apply Pose as Rest Pose"; - ot->idname = "POSE_OT_armature_apply"; - ot->description = "Apply the current pose as the new rest pose"; + /* identifiers */ + ot->name = "Apply Pose as Rest Pose"; + ot->idname = "POSE_OT_armature_apply"; + ot->description = "Apply the current pose as the new rest pose"; - /* callbacks */ - ot->exec = apply_armature_pose2bones_exec; - ot->poll = ED_operator_posemode; + /* callbacks */ + ot->exec = apply_armature_pose2bones_exec; + ot->poll = ED_operator_posemode; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } - /* set the current pose as the restpose */ static int pose_visual_transform_apply_exec(bContext *C, wmOperator *UNUSED(op)) { - ViewLayer *view_layer = CTX_data_view_layer(C); - View3D *v3d = CTX_wm_view3d(C); - Depsgraph *depsgraph = CTX_data_depsgraph(C); - - FOREACH_OBJECT_IN_MODE_BEGIN(view_layer, v3d, OB_ARMATURE, OB_MODE_POSE, ob) - { - /* loop over all selected pchans - * - * TODO, loop over children before parents if multiple bones - * at once are to be predictable*/ - FOREACH_PCHAN_SELECTED_IN_OBJECT_BEGIN (ob, pchan) - { - const Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob); - bPoseChannel *pchan_eval = BKE_pose_channel_find_name(ob_eval->pose, pchan->name); - float delta_mat[4][4]; - - /* chan_mat already contains the delta transform from rest pose to pose-mode pose - * as that is baked into there so that B-Bones will work. Once we've set this as the - * new raw-transform components, don't recalc the poses yet, otherwise IK result will - * change, thus changing the result we may be trying to record. - */ - /* XXX For some reason, we can't use pchan->chan_mat here, gives odd rotation/offset (see T38251). - * Using pchan->pose_mat and bringing it back in bone space seems to work as expected! - */ - BKE_armature_mat_pose_to_bone(pchan_eval, pchan_eval->pose_mat, delta_mat); - - BKE_pchan_apply_mat4(pchan, delta_mat, true); - } - FOREACH_PCHAN_SELECTED_IN_OBJECT_END; - - DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); - - /* note, notifier might evolve */ - WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); - } - FOREACH_OBJECT_IN_MODE_END; - - return OPERATOR_FINISHED; + ViewLayer *view_layer = CTX_data_view_layer(C); + View3D *v3d = CTX_wm_view3d(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + + FOREACH_OBJECT_IN_MODE_BEGIN (view_layer, v3d, OB_ARMATURE, OB_MODE_POSE, ob) { + /* loop over all selected pchans + * + * TODO, loop over children before parents if multiple bones + * at once are to be predictable*/ + FOREACH_PCHAN_SELECTED_IN_OBJECT_BEGIN (ob, pchan) { + const Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob); + bPoseChannel *pchan_eval = BKE_pose_channel_find_name(ob_eval->pose, pchan->name); + float delta_mat[4][4]; + + /* chan_mat already contains the delta transform from rest pose to pose-mode pose + * as that is baked into there so that B-Bones will work. Once we've set this as the + * new raw-transform components, don't recalc the poses yet, otherwise IK result will + * change, thus changing the result we may be trying to record. + */ + /* XXX For some reason, we can't use pchan->chan_mat here, gives odd rotation/offset (see T38251). + * Using pchan->pose_mat and bringing it back in bone space seems to work as expected! + */ + BKE_armature_mat_pose_to_bone(pchan_eval, pchan_eval->pose_mat, delta_mat); + + BKE_pchan_apply_mat4(pchan, delta_mat, true); + } + FOREACH_PCHAN_SELECTED_IN_OBJECT_END; + + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); + + /* note, notifier might evolve */ + WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); + } + FOREACH_OBJECT_IN_MODE_END; + + return OPERATOR_FINISHED; } void POSE_OT_visual_transform_apply(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Apply Visual Transform to Pose"; - ot->idname = "POSE_OT_visual_transform_apply"; - ot->description = "Apply final constrained position of pose bones to their transform"; + /* identifiers */ + ot->name = "Apply Visual Transform to Pose"; + ot->idname = "POSE_OT_visual_transform_apply"; + ot->description = "Apply final constrained position of pose bones to their transform"; - /* callbacks */ - ot->exec = pose_visual_transform_apply_exec; - ot->poll = ED_operator_posemode; + /* callbacks */ + ot->exec = pose_visual_transform_apply_exec; + ot->poll = ED_operator_posemode; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } /* ********************************************** */ @@ -289,18 +286,18 @@ void POSE_OT_visual_transform_apply(wmOperatorType *ot) */ static void set_pose_keys(Object *ob) { - bArmature *arm = ob->data; - bPoseChannel *chan; - - if (ob->pose) { - for (chan = ob->pose->chanbase.first; chan; chan = chan->next) { - Bone *bone = chan->bone; - if ((bone) && (bone->flag & BONE_SELECTED) && (arm->layer & bone->layer)) - chan->flag |= POSE_KEY; - else - chan->flag &= ~POSE_KEY; - } - } + bArmature *arm = ob->data; + bPoseChannel *chan; + + if (ob->pose) { + for (chan = ob->pose->chanbase.first; chan; chan = chan->next) { + Bone *bone = chan->bone; + if ((bone) && (bone->flag & BONE_SELECTED) && (arm->layer & bone->layer)) + chan->flag |= POSE_KEY; + else + chan->flag &= ~POSE_KEY; + } + } } /** @@ -312,304 +309,315 @@ static void set_pose_keys(Object *ob) * \param flip: Flip on x-axis * \return Whether the bone that we pasted to if we succeeded */ -static bPoseChannel *pose_bone_do_paste(Object *ob, bPoseChannel *chan, const bool selOnly, const bool flip) +static bPoseChannel *pose_bone_do_paste(Object *ob, + bPoseChannel *chan, + const bool selOnly, + const bool flip) { - bPoseChannel *pchan; - char name[MAXBONENAME]; - short paste_ok; - - /* get the name - if flipping, we must flip this first */ - if (flip) - BLI_string_flip_side_name(name, chan->name, false, sizeof(name)); - else - BLI_strncpy(name, chan->name, sizeof(name)); - - /* only copy when: - * 1) channel exists - poses are not meant to add random channels to anymore - * 2) if selection-masking is on, channel is selected - only selected bones get pasted on, allowing making both sides symmetrical - */ - pchan = BKE_pose_channel_find_name(ob->pose, name); - - if (selOnly) - paste_ok = ((pchan) && (pchan->bone->flag & BONE_SELECTED)); - else - paste_ok = (pchan != NULL); - - /* continue? */ - if (paste_ok) { - /* only loc rot size - * - only copies transform info for the pose - */ - copy_v3_v3(pchan->loc, chan->loc); - copy_v3_v3(pchan->size, chan->size); - pchan->flag = chan->flag; - - /* check if rotation modes are compatible (i.e. do they need any conversions) */ - if (pchan->rotmode == chan->rotmode) { - /* copy the type of rotation in use */ - if (pchan->rotmode > 0) { - copy_v3_v3(pchan->eul, chan->eul); - } - else if (pchan->rotmode == ROT_MODE_AXISANGLE) { - copy_v3_v3(pchan->rotAxis, chan->rotAxis); - pchan->rotAngle = chan->rotAngle; - } - else { - copy_qt_qt(pchan->quat, chan->quat); - } - } - else if (pchan->rotmode > 0) { - /* quat/axis-angle to euler */ - if (chan->rotmode == ROT_MODE_AXISANGLE) - axis_angle_to_eulO(pchan->eul, pchan->rotmode, chan->rotAxis, chan->rotAngle); - else - quat_to_eulO(pchan->eul, pchan->rotmode, chan->quat); - } - else if (pchan->rotmode == ROT_MODE_AXISANGLE) { - /* quat/euler to axis angle */ - if (chan->rotmode > 0) - eulO_to_axis_angle(pchan->rotAxis, &pchan->rotAngle, chan->eul, chan->rotmode); - else - quat_to_axis_angle(pchan->rotAxis, &pchan->rotAngle, chan->quat); - } - else { - /* euler/axis-angle to quat */ - if (chan->rotmode > 0) - eulO_to_quat(pchan->quat, chan->eul, chan->rotmode); - else - axis_angle_to_quat(pchan->quat, chan->rotAxis, pchan->rotAngle); - } - - /* B-Bone posing options should also be included... */ - pchan->curveInX = chan->curveInX; - pchan->curveInY = chan->curveInY; - pchan->curveOutX = chan->curveOutX; - pchan->curveOutY = chan->curveOutY; - - pchan->roll1 = chan->roll1; - pchan->roll2 = chan->roll2; - pchan->ease1 = chan->ease1; - pchan->ease2 = chan->ease2; - pchan->scaleIn = chan->scaleIn; - pchan->scaleOut = chan->scaleOut; - - /* paste flipped pose? */ - if (flip) { - pchan->loc[0] *= -1; - - pchan->curveInX *= -1; - pchan->curveOutX *= -1; - pchan->roll1 *= -1; // XXX? - pchan->roll2 *= -1; // XXX? - - /* has to be done as eulers... */ - if (pchan->rotmode > 0) { - pchan->eul[1] *= -1; - pchan->eul[2] *= -1; - } - else if (pchan->rotmode == ROT_MODE_AXISANGLE) { - float eul[3]; - - axis_angle_to_eulO(eul, EULER_ORDER_DEFAULT, pchan->rotAxis, pchan->rotAngle); - eul[1] *= -1; - eul[2] *= -1; - eulO_to_axis_angle(pchan->rotAxis, &pchan->rotAngle, eul, EULER_ORDER_DEFAULT); - } - else { - float eul[3]; - - normalize_qt(pchan->quat); - quat_to_eul(eul, pchan->quat); - eul[1] *= -1; - eul[2] *= -1; - eul_to_quat(pchan->quat, eul); - } - } - - /* ID properties */ - if (chan->prop) { - if (pchan->prop) { - /* if we have existing properties on a bone, just copy over the values of - * matching properties (i.e. ones which will have some impact) on to the - * target instead of just blinding replacing all [ - */ - IDP_SyncGroupValues(pchan->prop, chan->prop); - } - else { - /* no existing properties, so assume that we want copies too? */ - pchan->prop = IDP_CopyProperty(chan->prop); - } - } - } - - /* return whether paste went ahead */ - return pchan; + bPoseChannel *pchan; + char name[MAXBONENAME]; + short paste_ok; + + /* get the name - if flipping, we must flip this first */ + if (flip) + BLI_string_flip_side_name(name, chan->name, false, sizeof(name)); + else + BLI_strncpy(name, chan->name, sizeof(name)); + + /* only copy when: + * 1) channel exists - poses are not meant to add random channels to anymore + * 2) if selection-masking is on, channel is selected - only selected bones get pasted on, allowing making both sides symmetrical + */ + pchan = BKE_pose_channel_find_name(ob->pose, name); + + if (selOnly) + paste_ok = ((pchan) && (pchan->bone->flag & BONE_SELECTED)); + else + paste_ok = (pchan != NULL); + + /* continue? */ + if (paste_ok) { + /* only loc rot size + * - only copies transform info for the pose + */ + copy_v3_v3(pchan->loc, chan->loc); + copy_v3_v3(pchan->size, chan->size); + pchan->flag = chan->flag; + + /* check if rotation modes are compatible (i.e. do they need any conversions) */ + if (pchan->rotmode == chan->rotmode) { + /* copy the type of rotation in use */ + if (pchan->rotmode > 0) { + copy_v3_v3(pchan->eul, chan->eul); + } + else if (pchan->rotmode == ROT_MODE_AXISANGLE) { + copy_v3_v3(pchan->rotAxis, chan->rotAxis); + pchan->rotAngle = chan->rotAngle; + } + else { + copy_qt_qt(pchan->quat, chan->quat); + } + } + else if (pchan->rotmode > 0) { + /* quat/axis-angle to euler */ + if (chan->rotmode == ROT_MODE_AXISANGLE) + axis_angle_to_eulO(pchan->eul, pchan->rotmode, chan->rotAxis, chan->rotAngle); + else + quat_to_eulO(pchan->eul, pchan->rotmode, chan->quat); + } + else if (pchan->rotmode == ROT_MODE_AXISANGLE) { + /* quat/euler to axis angle */ + if (chan->rotmode > 0) + eulO_to_axis_angle(pchan->rotAxis, &pchan->rotAngle, chan->eul, chan->rotmode); + else + quat_to_axis_angle(pchan->rotAxis, &pchan->rotAngle, chan->quat); + } + else { + /* euler/axis-angle to quat */ + if (chan->rotmode > 0) + eulO_to_quat(pchan->quat, chan->eul, chan->rotmode); + else + axis_angle_to_quat(pchan->quat, chan->rotAxis, pchan->rotAngle); + } + + /* B-Bone posing options should also be included... */ + pchan->curveInX = chan->curveInX; + pchan->curveInY = chan->curveInY; + pchan->curveOutX = chan->curveOutX; + pchan->curveOutY = chan->curveOutY; + + pchan->roll1 = chan->roll1; + pchan->roll2 = chan->roll2; + pchan->ease1 = chan->ease1; + pchan->ease2 = chan->ease2; + pchan->scaleIn = chan->scaleIn; + pchan->scaleOut = chan->scaleOut; + + /* paste flipped pose? */ + if (flip) { + pchan->loc[0] *= -1; + + pchan->curveInX *= -1; + pchan->curveOutX *= -1; + pchan->roll1 *= -1; // XXX? + pchan->roll2 *= -1; // XXX? + + /* has to be done as eulers... */ + if (pchan->rotmode > 0) { + pchan->eul[1] *= -1; + pchan->eul[2] *= -1; + } + else if (pchan->rotmode == ROT_MODE_AXISANGLE) { + float eul[3]; + + axis_angle_to_eulO(eul, EULER_ORDER_DEFAULT, pchan->rotAxis, pchan->rotAngle); + eul[1] *= -1; + eul[2] *= -1; + eulO_to_axis_angle(pchan->rotAxis, &pchan->rotAngle, eul, EULER_ORDER_DEFAULT); + } + else { + float eul[3]; + + normalize_qt(pchan->quat); + quat_to_eul(eul, pchan->quat); + eul[1] *= -1; + eul[2] *= -1; + eul_to_quat(pchan->quat, eul); + } + } + + /* ID properties */ + if (chan->prop) { + if (pchan->prop) { + /* if we have existing properties on a bone, just copy over the values of + * matching properties (i.e. ones which will have some impact) on to the + * target instead of just blinding replacing all [ + */ + IDP_SyncGroupValues(pchan->prop, chan->prop); + } + else { + /* no existing properties, so assume that we want copies too? */ + pchan->prop = IDP_CopyProperty(chan->prop); + } + } + } + + /* return whether paste went ahead */ + return pchan; } /* ---- */ static int pose_copy_exec(bContext *C, wmOperator *op) { - Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); - char str[FILE_MAX]; - /* Sanity checking. */ - if (ELEM(NULL, ob, ob->pose)) { - BKE_report(op->reports, RPT_ERROR, "No pose to copy"); - return OPERATOR_CANCELLED; - } - /* Sets chan->flag to POSE_KEY if bone selected. */ - set_pose_keys(ob); - /* Construct a local bmain and only put object and it's data into it, - * o this way we don't expand any other objects into the copy buffer - * file. - * - * TODO(sergey): Find an easier way to tell copy buffer to only store - * data we are actually interested in. Maybe pass it a flag to skip - * any datablock expansion? - */ - Main *temp_bmain = BKE_main_new(); - Object ob_copy = *ob; - bArmature arm_copy = *((bArmature *)ob->data); - ob_copy.data = &arm_copy; - BLI_addtail(&temp_bmain->objects, &ob_copy); - BLI_addtail(&temp_bmain->armatures, &arm_copy); - /* begin copy buffer on a temp bmain. */ - BKE_copybuffer_begin(temp_bmain); - /* Store the whole object to the copy buffer because pose can't be - * existing on it's own. - */ - BKE_copybuffer_tag_ID(&ob_copy.id); - BLI_make_file_string("/", str, BKE_tempdir_base(), "copybuffer_pose.blend"); - BKE_copybuffer_save(temp_bmain, str, op->reports); - /* We clear the lists so no datablocks gets freed, - * This is required because objects in temp bmain shares same pointers - * as the real ones. - */ - BLI_listbase_clear(&temp_bmain->objects); - BLI_listbase_clear(&temp_bmain->armatures); - BKE_main_free(temp_bmain); - /* We are all done! */ - BKE_report(op->reports, RPT_INFO, "Copied pose to buffer"); - return OPERATOR_FINISHED; + Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); + char str[FILE_MAX]; + /* Sanity checking. */ + if (ELEM(NULL, ob, ob->pose)) { + BKE_report(op->reports, RPT_ERROR, "No pose to copy"); + return OPERATOR_CANCELLED; + } + /* Sets chan->flag to POSE_KEY if bone selected. */ + set_pose_keys(ob); + /* Construct a local bmain and only put object and it's data into it, + * o this way we don't expand any other objects into the copy buffer + * file. + * + * TODO(sergey): Find an easier way to tell copy buffer to only store + * data we are actually interested in. Maybe pass it a flag to skip + * any datablock expansion? + */ + Main *temp_bmain = BKE_main_new(); + Object ob_copy = *ob; + bArmature arm_copy = *((bArmature *)ob->data); + ob_copy.data = &arm_copy; + BLI_addtail(&temp_bmain->objects, &ob_copy); + BLI_addtail(&temp_bmain->armatures, &arm_copy); + /* begin copy buffer on a temp bmain. */ + BKE_copybuffer_begin(temp_bmain); + /* Store the whole object to the copy buffer because pose can't be + * existing on it's own. + */ + BKE_copybuffer_tag_ID(&ob_copy.id); + BLI_make_file_string("/", str, BKE_tempdir_base(), "copybuffer_pose.blend"); + BKE_copybuffer_save(temp_bmain, str, op->reports); + /* We clear the lists so no datablocks gets freed, + * This is required because objects in temp bmain shares same pointers + * as the real ones. + */ + BLI_listbase_clear(&temp_bmain->objects); + BLI_listbase_clear(&temp_bmain->armatures); + BKE_main_free(temp_bmain); + /* We are all done! */ + BKE_report(op->reports, RPT_INFO, "Copied pose to buffer"); + return OPERATOR_FINISHED; } void POSE_OT_copy(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Copy Pose"; - ot->idname = "POSE_OT_copy"; - ot->description = "Copies the current pose of the selected bones to copy/paste buffer"; + /* identifiers */ + ot->name = "Copy Pose"; + ot->idname = "POSE_OT_copy"; + ot->description = "Copies the current pose of the selected bones to copy/paste buffer"; - /* api callbacks */ - ot->exec = pose_copy_exec; - ot->poll = ED_operator_posemode; + /* api callbacks */ + ot->exec = pose_copy_exec; + ot->poll = ED_operator_posemode; - /* flag */ - ot->flag = OPTYPE_REGISTER; + /* flag */ + ot->flag = OPTYPE_REGISTER; } /* ---- */ static int pose_paste_exec(bContext *C, wmOperator *op) { - Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); - Scene *scene = CTX_data_scene(C); - bPoseChannel *chan; - const bool flip = RNA_boolean_get(op->ptr, "flipped"); - bool selOnly = RNA_boolean_get(op->ptr, "selected_mask"); - - /* Get KeyingSet to use. */ - KeyingSet *ks = ANIM_get_keyingset_for_autokeying(scene, ANIM_KS_WHOLE_CHARACTER_ID); - - /* Sanity checks. */ - if (ELEM(NULL, ob, ob->pose)) { - return OPERATOR_CANCELLED; - } - - /* Read copy buffer .blend file. */ - char str[FILE_MAX]; - Main *tmp_bmain = BKE_main_new(); - BLI_make_file_string("/", str, BKE_tempdir_base(), "copybuffer_pose.blend"); - if (!BKE_copybuffer_read(tmp_bmain, str, op->reports, FILTER_ID_OB)) { - BKE_report(op->reports, RPT_ERROR, "Copy buffer is empty"); - BKE_main_free(tmp_bmain); - return OPERATOR_CANCELLED; - } - /* Make sure data from this file is usable for pose paste. */ - if (BLI_listbase_count_at_most(&tmp_bmain->objects, 2) != 1) { - BKE_report(op->reports, RPT_ERROR, "Copy buffer is not from pose mode"); - BKE_main_free(tmp_bmain); - return OPERATOR_CANCELLED; - } - - Object *object_from = tmp_bmain->objects.first; - bPose *pose_from = object_from->pose; - if (pose_from == NULL) { - BKE_report(op->reports, RPT_ERROR, "Copy buffer has no pose"); - BKE_main_free(tmp_bmain); - return OPERATOR_CANCELLED; - } - - /* If selOnly option is enabled, if user hasn't selected any bones, - * just go back to default behavior to be more in line with other - * pose tools. - */ - if (selOnly) { - if (CTX_DATA_COUNT(C, selected_pose_bones) == 0) { - selOnly = false; - } - } - - /* Safely merge all of the channels in the buffer pose into any - * existing pose. - */ - for (chan = pose_from->chanbase.first; chan; chan = chan->next) { - if (chan->flag & POSE_KEY) { - /* Try to perform paste on this bone. */ - bPoseChannel *pchan = pose_bone_do_paste(ob, chan, selOnly, flip); - if (pchan != NULL) { - /* Keyframing tagging for successful paste, */ - ED_autokeyframe_pchan(C, scene, ob, pchan, ks); - } - } - } - BKE_main_free(tmp_bmain); - - /* Update event for pose and deformation children. */ - DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); - - /* Recalculate paths if any of the bones have paths... */ - if ((ob->pose->avs.path_bakeflag & MOTIONPATH_BAKE_HAS_PATHS)) { - ED_pose_recalculate_paths(C, scene, ob, false); - } - - /* Notifiers for updates, */ - WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); - - return OPERATOR_FINISHED; + Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); + Scene *scene = CTX_data_scene(C); + bPoseChannel *chan; + const bool flip = RNA_boolean_get(op->ptr, "flipped"); + bool selOnly = RNA_boolean_get(op->ptr, "selected_mask"); + + /* Get KeyingSet to use. */ + KeyingSet *ks = ANIM_get_keyingset_for_autokeying(scene, ANIM_KS_WHOLE_CHARACTER_ID); + + /* Sanity checks. */ + if (ELEM(NULL, ob, ob->pose)) { + return OPERATOR_CANCELLED; + } + + /* Read copy buffer .blend file. */ + char str[FILE_MAX]; + Main *tmp_bmain = BKE_main_new(); + BLI_make_file_string("/", str, BKE_tempdir_base(), "copybuffer_pose.blend"); + if (!BKE_copybuffer_read(tmp_bmain, str, op->reports, FILTER_ID_OB)) { + BKE_report(op->reports, RPT_ERROR, "Copy buffer is empty"); + BKE_main_free(tmp_bmain); + return OPERATOR_CANCELLED; + } + /* Make sure data from this file is usable for pose paste. */ + if (BLI_listbase_count_at_most(&tmp_bmain->objects, 2) != 1) { + BKE_report(op->reports, RPT_ERROR, "Copy buffer is not from pose mode"); + BKE_main_free(tmp_bmain); + return OPERATOR_CANCELLED; + } + + Object *object_from = tmp_bmain->objects.first; + bPose *pose_from = object_from->pose; + if (pose_from == NULL) { + BKE_report(op->reports, RPT_ERROR, "Copy buffer has no pose"); + BKE_main_free(tmp_bmain); + return OPERATOR_CANCELLED; + } + + /* If selOnly option is enabled, if user hasn't selected any bones, + * just go back to default behavior to be more in line with other + * pose tools. + */ + if (selOnly) { + if (CTX_DATA_COUNT(C, selected_pose_bones) == 0) { + selOnly = false; + } + } + + /* Safely merge all of the channels in the buffer pose into any + * existing pose. + */ + for (chan = pose_from->chanbase.first; chan; chan = chan->next) { + if (chan->flag & POSE_KEY) { + /* Try to perform paste on this bone. */ + bPoseChannel *pchan = pose_bone_do_paste(ob, chan, selOnly, flip); + if (pchan != NULL) { + /* Keyframing tagging for successful paste, */ + ED_autokeyframe_pchan(C, scene, ob, pchan, ks); + } + } + } + BKE_main_free(tmp_bmain); + + /* Update event for pose and deformation children. */ + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); + + /* Recalculate paths if any of the bones have paths... */ + if ((ob->pose->avs.path_bakeflag & MOTIONPATH_BAKE_HAS_PATHS)) { + ED_pose_recalculate_paths(C, scene, ob, false); + } + + /* Notifiers for updates, */ + WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); + + return OPERATOR_FINISHED; } void POSE_OT_paste(wmOperatorType *ot) { - PropertyRNA *prop; - - /* identifiers */ - ot->name = "Paste Pose"; - ot->idname = "POSE_OT_paste"; - ot->description = "Paste the stored pose on to the current pose"; - - /* api callbacks */ - ot->exec = pose_paste_exec; - ot->poll = ED_operator_posemode; - - /* flag */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - prop = RNA_def_boolean(ot->srna, "flipped", false, "Flipped on X-Axis", "Paste the stored pose flipped on to current pose"); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); - - RNA_def_boolean(ot->srna, "selected_mask", false, "On Selected Only", "Only paste the stored pose on to selected bones in the current pose"); + PropertyRNA *prop; + + /* identifiers */ + ot->name = "Paste Pose"; + ot->idname = "POSE_OT_paste"; + ot->description = "Paste the stored pose on to the current pose"; + + /* api callbacks */ + ot->exec = pose_paste_exec; + ot->poll = ED_operator_posemode; + + /* flag */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + prop = RNA_def_boolean(ot->srna, + "flipped", + false, + "Flipped on X-Axis", + "Paste the stored pose flipped on to current pose"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + + RNA_def_boolean(ot->srna, + "selected_mask", + false, + "On Selected Only", + "Only paste the stored pose on to selected bones in the current pose"); } /* ********************************************** */ @@ -618,313 +626,317 @@ void POSE_OT_paste(wmOperatorType *ot) /* clear scale of pose-channel */ static void pchan_clear_scale(bPoseChannel *pchan) { - if ((pchan->protectflag & OB_LOCK_SCALEX) == 0) - pchan->size[0] = 1.0f; - if ((pchan->protectflag & OB_LOCK_SCALEY) == 0) - pchan->size[1] = 1.0f; - if ((pchan->protectflag & OB_LOCK_SCALEZ) == 0) - pchan->size[2] = 1.0f; - - pchan->ease1 = 0.0f; - pchan->ease2 = 0.0f; - pchan->scaleIn = 1.0f; - pchan->scaleOut = 1.0f; + if ((pchan->protectflag & OB_LOCK_SCALEX) == 0) + pchan->size[0] = 1.0f; + if ((pchan->protectflag & OB_LOCK_SCALEY) == 0) + pchan->size[1] = 1.0f; + if ((pchan->protectflag & OB_LOCK_SCALEZ) == 0) + pchan->size[2] = 1.0f; + + pchan->ease1 = 0.0f; + pchan->ease2 = 0.0f; + pchan->scaleIn = 1.0f; + pchan->scaleOut = 1.0f; } /* clear location of pose-channel */ static void pchan_clear_loc(bPoseChannel *pchan) { - if ((pchan->protectflag & OB_LOCK_LOCX) == 0) - pchan->loc[0] = 0.0f; - if ((pchan->protectflag & OB_LOCK_LOCY) == 0) - pchan->loc[1] = 0.0f; - if ((pchan->protectflag & OB_LOCK_LOCZ) == 0) - pchan->loc[2] = 0.0f; + if ((pchan->protectflag & OB_LOCK_LOCX) == 0) + pchan->loc[0] = 0.0f; + if ((pchan->protectflag & OB_LOCK_LOCY) == 0) + pchan->loc[1] = 0.0f; + if ((pchan->protectflag & OB_LOCK_LOCZ) == 0) + pchan->loc[2] = 0.0f; } /* clear rotation of pose-channel */ static void pchan_clear_rot(bPoseChannel *pchan) { - if (pchan->protectflag & (OB_LOCK_ROTX | OB_LOCK_ROTY | OB_LOCK_ROTZ | OB_LOCK_ROTW)) { - /* check if convert to eulers for locking... */ - if (pchan->protectflag & OB_LOCK_ROT4D) { - /* perform clamping on a component by component basis */ - if (pchan->rotmode == ROT_MODE_AXISANGLE) { - if ((pchan->protectflag & OB_LOCK_ROTW) == 0) - pchan->rotAngle = 0.0f; - if ((pchan->protectflag & OB_LOCK_ROTX) == 0) - pchan->rotAxis[0] = 0.0f; - if ((pchan->protectflag & OB_LOCK_ROTY) == 0) - pchan->rotAxis[1] = 0.0f; - if ((pchan->protectflag & OB_LOCK_ROTZ) == 0) - pchan->rotAxis[2] = 0.0f; - - /* check validity of axis - axis should never be 0,0,0 (if so, then we make it rotate about y) */ - if (IS_EQF(pchan->rotAxis[0], pchan->rotAxis[1]) && IS_EQF(pchan->rotAxis[1], pchan->rotAxis[2])) - pchan->rotAxis[1] = 1.0f; - } - else if (pchan->rotmode == ROT_MODE_QUAT) { - if ((pchan->protectflag & OB_LOCK_ROTW) == 0) - pchan->quat[0] = 1.0f; - if ((pchan->protectflag & OB_LOCK_ROTX) == 0) - pchan->quat[1] = 0.0f; - if ((pchan->protectflag & OB_LOCK_ROTY) == 0) - pchan->quat[2] = 0.0f; - if ((pchan->protectflag & OB_LOCK_ROTZ) == 0) - pchan->quat[3] = 0.0f; - } - else { - /* the flag may have been set for the other modes, so just ignore the extra flag... */ - if ((pchan->protectflag & OB_LOCK_ROTX) == 0) - pchan->eul[0] = 0.0f; - if ((pchan->protectflag & OB_LOCK_ROTY) == 0) - pchan->eul[1] = 0.0f; - if ((pchan->protectflag & OB_LOCK_ROTZ) == 0) - pchan->eul[2] = 0.0f; - } - } - else { - /* perform clamping using euler form (3-components) */ - float eul[3], oldeul[3], quat1[4] = {0}; - float qlen = 0.0f; - - if (pchan->rotmode == ROT_MODE_QUAT) { - qlen = normalize_qt_qt(quat1, pchan->quat); - quat_to_eul(oldeul, quat1); - } - else if (pchan->rotmode == ROT_MODE_AXISANGLE) { - axis_angle_to_eulO(oldeul, EULER_ORDER_DEFAULT, pchan->rotAxis, pchan->rotAngle); - } - else { - copy_v3_v3(oldeul, pchan->eul); - } - - eul[0] = eul[1] = eul[2] = 0.0f; - - if (pchan->protectflag & OB_LOCK_ROTX) - eul[0] = oldeul[0]; - if (pchan->protectflag & OB_LOCK_ROTY) - eul[1] = oldeul[1]; - if (pchan->protectflag & OB_LOCK_ROTZ) - eul[2] = oldeul[2]; - - if (pchan->rotmode == ROT_MODE_QUAT) { - eul_to_quat(pchan->quat, eul); - - /* restore original quat size */ - mul_qt_fl(pchan->quat, qlen); - - /* quaternions flip w sign to accumulate rotations correctly */ - if ((quat1[0] < 0.0f && pchan->quat[0] > 0.0f) || (quat1[0] > 0.0f && pchan->quat[0] < 0.0f)) { - mul_qt_fl(pchan->quat, -1.0f); - } - } - else if (pchan->rotmode == ROT_MODE_AXISANGLE) { - eulO_to_axis_angle(pchan->rotAxis, &pchan->rotAngle, eul, EULER_ORDER_DEFAULT); - } - else { - copy_v3_v3(pchan->eul, eul); - } - } - } /* Duplicated in source/blender/editors/object/object_transform.c */ - else { - if (pchan->rotmode == ROT_MODE_QUAT) { - unit_qt(pchan->quat); - } - else if (pchan->rotmode == ROT_MODE_AXISANGLE) { - /* by default, make rotation of 0 radians around y-axis (roll) */ - unit_axis_angle(pchan->rotAxis, &pchan->rotAngle); - } - else { - zero_v3(pchan->eul); - } - } - - /* Clear also Bendy Bone stuff - Roll is obvious, - * but Curve X/Y stuff is also kindof rotational in nature... */ - pchan->roll1 = 0.0f; - pchan->roll2 = 0.0f; - - pchan->curveInX = 0.0f; - pchan->curveInY = 0.0f; - pchan->curveOutX = 0.0f; - pchan->curveOutY = 0.0f; + if (pchan->protectflag & (OB_LOCK_ROTX | OB_LOCK_ROTY | OB_LOCK_ROTZ | OB_LOCK_ROTW)) { + /* check if convert to eulers for locking... */ + if (pchan->protectflag & OB_LOCK_ROT4D) { + /* perform clamping on a component by component basis */ + if (pchan->rotmode == ROT_MODE_AXISANGLE) { + if ((pchan->protectflag & OB_LOCK_ROTW) == 0) + pchan->rotAngle = 0.0f; + if ((pchan->protectflag & OB_LOCK_ROTX) == 0) + pchan->rotAxis[0] = 0.0f; + if ((pchan->protectflag & OB_LOCK_ROTY) == 0) + pchan->rotAxis[1] = 0.0f; + if ((pchan->protectflag & OB_LOCK_ROTZ) == 0) + pchan->rotAxis[2] = 0.0f; + + /* check validity of axis - axis should never be 0,0,0 (if so, then we make it rotate about y) */ + if (IS_EQF(pchan->rotAxis[0], pchan->rotAxis[1]) && + IS_EQF(pchan->rotAxis[1], pchan->rotAxis[2])) + pchan->rotAxis[1] = 1.0f; + } + else if (pchan->rotmode == ROT_MODE_QUAT) { + if ((pchan->protectflag & OB_LOCK_ROTW) == 0) + pchan->quat[0] = 1.0f; + if ((pchan->protectflag & OB_LOCK_ROTX) == 0) + pchan->quat[1] = 0.0f; + if ((pchan->protectflag & OB_LOCK_ROTY) == 0) + pchan->quat[2] = 0.0f; + if ((pchan->protectflag & OB_LOCK_ROTZ) == 0) + pchan->quat[3] = 0.0f; + } + else { + /* the flag may have been set for the other modes, so just ignore the extra flag... */ + if ((pchan->protectflag & OB_LOCK_ROTX) == 0) + pchan->eul[0] = 0.0f; + if ((pchan->protectflag & OB_LOCK_ROTY) == 0) + pchan->eul[1] = 0.0f; + if ((pchan->protectflag & OB_LOCK_ROTZ) == 0) + pchan->eul[2] = 0.0f; + } + } + else { + /* perform clamping using euler form (3-components) */ + float eul[3], oldeul[3], quat1[4] = {0}; + float qlen = 0.0f; + + if (pchan->rotmode == ROT_MODE_QUAT) { + qlen = normalize_qt_qt(quat1, pchan->quat); + quat_to_eul(oldeul, quat1); + } + else if (pchan->rotmode == ROT_MODE_AXISANGLE) { + axis_angle_to_eulO(oldeul, EULER_ORDER_DEFAULT, pchan->rotAxis, pchan->rotAngle); + } + else { + copy_v3_v3(oldeul, pchan->eul); + } + + eul[0] = eul[1] = eul[2] = 0.0f; + + if (pchan->protectflag & OB_LOCK_ROTX) + eul[0] = oldeul[0]; + if (pchan->protectflag & OB_LOCK_ROTY) + eul[1] = oldeul[1]; + if (pchan->protectflag & OB_LOCK_ROTZ) + eul[2] = oldeul[2]; + + if (pchan->rotmode == ROT_MODE_QUAT) { + eul_to_quat(pchan->quat, eul); + + /* restore original quat size */ + mul_qt_fl(pchan->quat, qlen); + + /* quaternions flip w sign to accumulate rotations correctly */ + if ((quat1[0] < 0.0f && pchan->quat[0] > 0.0f) || + (quat1[0] > 0.0f && pchan->quat[0] < 0.0f)) { + mul_qt_fl(pchan->quat, -1.0f); + } + } + else if (pchan->rotmode == ROT_MODE_AXISANGLE) { + eulO_to_axis_angle(pchan->rotAxis, &pchan->rotAngle, eul, EULER_ORDER_DEFAULT); + } + else { + copy_v3_v3(pchan->eul, eul); + } + } + } /* Duplicated in source/blender/editors/object/object_transform.c */ + else { + if (pchan->rotmode == ROT_MODE_QUAT) { + unit_qt(pchan->quat); + } + else if (pchan->rotmode == ROT_MODE_AXISANGLE) { + /* by default, make rotation of 0 radians around y-axis (roll) */ + unit_axis_angle(pchan->rotAxis, &pchan->rotAngle); + } + else { + zero_v3(pchan->eul); + } + } + + /* Clear also Bendy Bone stuff - Roll is obvious, + * but Curve X/Y stuff is also kindof rotational in nature... */ + pchan->roll1 = 0.0f; + pchan->roll2 = 0.0f; + + pchan->curveInX = 0.0f; + pchan->curveInY = 0.0f; + pchan->curveOutX = 0.0f; + pchan->curveOutY = 0.0f; } /* clear loc/rot/scale of pose-channel */ static void pchan_clear_transforms(bPoseChannel *pchan) { - pchan_clear_loc(pchan); - pchan_clear_rot(pchan); - pchan_clear_scale(pchan); + pchan_clear_loc(pchan); + pchan_clear_rot(pchan); + pchan_clear_scale(pchan); } /* --------------- */ /* generic exec for clear-pose operators */ -static int pose_clear_transform_generic_exec(bContext *C, wmOperator *op, - void (*clear_func)(bPoseChannel *), const char default_ksName[]) +static int pose_clear_transform_generic_exec(bContext *C, + wmOperator *op, + void (*clear_func)(bPoseChannel *), + const char default_ksName[]) { - Scene *scene = CTX_data_scene(C); - bool changed_multi = false; - - /* sanity checks */ - if (ELEM(NULL, clear_func, default_ksName)) { - BKE_report(op->reports, RPT_ERROR, "Programming error: missing clear transform function or keying set name"); - return OPERATOR_CANCELLED; - } - - /* only clear relevant transforms for selected bones */ - ViewLayer *view_layer = CTX_data_view_layer(C); - View3D *v3d = CTX_wm_view3d(C); - FOREACH_OBJECT_IN_MODE_BEGIN (view_layer, v3d, OB_ARMATURE, OB_MODE_POSE, ob_iter) - { - Object *ob_eval = DEG_get_evaluated_object(CTX_data_depsgraph(C), ob_iter); // XXX: UGLY HACK (for autokey + clear transforms) - ListBase dsources = {NULL, NULL}; - bool changed = false; - - FOREACH_PCHAN_SELECTED_IN_OBJECT_BEGIN (ob_iter, pchan) - { - /* run provided clearing function */ - clear_func(pchan); - changed = true; - - /* do auto-keyframing as appropriate */ - if (autokeyframe_cfra_can_key(scene, &ob_iter->id)) { - /* clear any unkeyed tags */ - if (pchan->bone) { - pchan->bone->flag &= ~BONE_UNKEYED; - } - /* tag for autokeying later */ - ANIM_relative_keyingset_add_source(&dsources, &ob_iter->id, &RNA_PoseBone, pchan); - -#if 1 /* XXX: Ugly Hack - Run clearing function on evaluated copy of pchan */ - bPoseChannel *pchan_eval = BKE_pose_channel_find_name(ob_eval->pose, pchan->name); - clear_func(pchan_eval); + Scene *scene = CTX_data_scene(C); + bool changed_multi = false; + + /* sanity checks */ + if (ELEM(NULL, clear_func, default_ksName)) { + BKE_report(op->reports, + RPT_ERROR, + "Programming error: missing clear transform function or keying set name"); + return OPERATOR_CANCELLED; + } + + /* only clear relevant transforms for selected bones */ + ViewLayer *view_layer = CTX_data_view_layer(C); + View3D *v3d = CTX_wm_view3d(C); + FOREACH_OBJECT_IN_MODE_BEGIN (view_layer, v3d, OB_ARMATURE, OB_MODE_POSE, ob_iter) { + Object *ob_eval = DEG_get_evaluated_object( + CTX_data_depsgraph(C), ob_iter); // XXX: UGLY HACK (for autokey + clear transforms) + ListBase dsources = {NULL, NULL}; + bool changed = false; + + FOREACH_PCHAN_SELECTED_IN_OBJECT_BEGIN (ob_iter, pchan) { + /* run provided clearing function */ + clear_func(pchan); + changed = true; + + /* do auto-keyframing as appropriate */ + if (autokeyframe_cfra_can_key(scene, &ob_iter->id)) { + /* clear any unkeyed tags */ + if (pchan->bone) { + pchan->bone->flag &= ~BONE_UNKEYED; + } + /* tag for autokeying later */ + ANIM_relative_keyingset_add_source(&dsources, &ob_iter->id, &RNA_PoseBone, pchan); + +#if 1 /* XXX: Ugly Hack - Run clearing function on evaluated copy of pchan */ + bPoseChannel *pchan_eval = BKE_pose_channel_find_name(ob_eval->pose, pchan->name); + clear_func(pchan_eval); #endif - } - else { - /* add unkeyed tags */ - if (pchan->bone) { - pchan->bone->flag |= BONE_UNKEYED; - } - } - } - FOREACH_PCHAN_SELECTED_IN_OBJECT_END; - - if (changed) { - changed_multi = true; - - /* perform autokeying on the bones if needed */ - if (!BLI_listbase_is_empty(&dsources)) { - /* get KeyingSet to use */ - KeyingSet *ks = ANIM_get_keyingset_for_autokeying(scene, default_ksName); - - /* insert keyframes */ - ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, (float)CFRA); - - /* now recalculate paths */ - if ((ob_iter->pose->avs.path_bakeflag & MOTIONPATH_BAKE_HAS_PATHS)) { - ED_pose_recalculate_paths(C, scene, ob_iter, false); - } - - BLI_freelistN(&dsources); - } - - DEG_id_tag_update(&ob_iter->id, ID_RECALC_GEOMETRY); - - /* note, notifier might evolve */ - WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, ob_iter); - } - } - FOREACH_OBJECT_IN_MODE_END; - - return changed_multi ? OPERATOR_FINISHED : OPERATOR_CANCELLED; + } + else { + /* add unkeyed tags */ + if (pchan->bone) { + pchan->bone->flag |= BONE_UNKEYED; + } + } + } + FOREACH_PCHAN_SELECTED_IN_OBJECT_END; + + if (changed) { + changed_multi = true; + + /* perform autokeying on the bones if needed */ + if (!BLI_listbase_is_empty(&dsources)) { + /* get KeyingSet to use */ + KeyingSet *ks = ANIM_get_keyingset_for_autokeying(scene, default_ksName); + + /* insert keyframes */ + ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, (float)CFRA); + + /* now recalculate paths */ + if ((ob_iter->pose->avs.path_bakeflag & MOTIONPATH_BAKE_HAS_PATHS)) { + ED_pose_recalculate_paths(C, scene, ob_iter, false); + } + + BLI_freelistN(&dsources); + } + + DEG_id_tag_update(&ob_iter->id, ID_RECALC_GEOMETRY); + + /* note, notifier might evolve */ + WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, ob_iter); + } + } + FOREACH_OBJECT_IN_MODE_END; + + return changed_multi ? OPERATOR_FINISHED : OPERATOR_CANCELLED; } /* --------------- */ static int pose_clear_scale_exec(bContext *C, wmOperator *op) { - return pose_clear_transform_generic_exec(C, op, pchan_clear_scale, ANIM_KS_SCALING_ID); + return pose_clear_transform_generic_exec(C, op, pchan_clear_scale, ANIM_KS_SCALING_ID); } void POSE_OT_scale_clear(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Clear Pose Scale"; - ot->idname = "POSE_OT_scale_clear"; - ot->description = "Reset scaling of selected bones to their default values"; + /* identifiers */ + ot->name = "Clear Pose Scale"; + ot->idname = "POSE_OT_scale_clear"; + ot->description = "Reset scaling of selected bones to their default values"; - /* api callbacks */ - ot->exec = pose_clear_scale_exec; - ot->poll = ED_operator_posemode; + /* api callbacks */ + ot->exec = pose_clear_scale_exec; + ot->poll = ED_operator_posemode; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } - static int pose_clear_rot_exec(bContext *C, wmOperator *op) { - return pose_clear_transform_generic_exec(C, op, pchan_clear_rot, ANIM_KS_ROTATION_ID); + return pose_clear_transform_generic_exec(C, op, pchan_clear_rot, ANIM_KS_ROTATION_ID); } void POSE_OT_rot_clear(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Clear Pose Rotation"; - ot->idname = "POSE_OT_rot_clear"; - ot->description = "Reset rotations of selected bones to their default values"; + /* identifiers */ + ot->name = "Clear Pose Rotation"; + ot->idname = "POSE_OT_rot_clear"; + ot->description = "Reset rotations of selected bones to their default values"; - /* api callbacks */ - ot->exec = pose_clear_rot_exec; - ot->poll = ED_operator_posemode; + /* api callbacks */ + ot->exec = pose_clear_rot_exec; + ot->poll = ED_operator_posemode; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } - static int pose_clear_loc_exec(bContext *C, wmOperator *op) { - return pose_clear_transform_generic_exec(C, op, pchan_clear_loc, ANIM_KS_LOCATION_ID); + return pose_clear_transform_generic_exec(C, op, pchan_clear_loc, ANIM_KS_LOCATION_ID); } void POSE_OT_loc_clear(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Clear Pose Location"; - ot->idname = "POSE_OT_loc_clear"; - ot->description = "Reset locations of selected bones to their default values"; + /* identifiers */ + ot->name = "Clear Pose Location"; + ot->idname = "POSE_OT_loc_clear"; + ot->description = "Reset locations of selected bones to their default values"; - /* api callbacks */ - ot->exec = pose_clear_loc_exec; - ot->poll = ED_operator_posemode; + /* api callbacks */ + ot->exec = pose_clear_loc_exec; + ot->poll = ED_operator_posemode; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } - static int pose_clear_transforms_exec(bContext *C, wmOperator *op) { - return pose_clear_transform_generic_exec(C, op, pchan_clear_transforms, ANIM_KS_LOC_ROT_SCALE_ID); + return pose_clear_transform_generic_exec( + C, op, pchan_clear_transforms, ANIM_KS_LOC_ROT_SCALE_ID); } void POSE_OT_transforms_clear(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Clear Pose Transforms"; - ot->idname = "POSE_OT_transforms_clear"; - ot->description = "Reset location, rotation, and scaling of selected bones to their default values"; - - /* api callbacks */ - ot->exec = pose_clear_transforms_exec; - ot->poll = ED_operator_posemode; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* identifiers */ + ot->name = "Clear Pose Transforms"; + ot->idname = "POSE_OT_transforms_clear"; + ot->description = + "Reset location, rotation, and scaling of selected bones to their default values"; + + /* api callbacks */ + ot->exec = pose_clear_transforms_exec; + ot->poll = ED_operator_posemode; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } /* ********************************************** */ @@ -932,80 +944,79 @@ void POSE_OT_transforms_clear(wmOperatorType *ot) static int pose_clear_user_transforms_exec(bContext *C, wmOperator *op) { - ViewLayer *view_layer = CTX_data_view_layer(C); - View3D *v3d = CTX_wm_view3d(C); - Scene *scene = CTX_data_scene(C); - float cframe = (float)CFRA; - const bool only_select = RNA_boolean_get(op->ptr, "only_selected"); - - FOREACH_OBJECT_IN_MODE_BEGIN (view_layer, v3d, OB_ARMATURE, OB_MODE_POSE, ob) - { - if ((ob->adt) && (ob->adt->action)) { - /* XXX: this is just like this to avoid contaminating anything else; - * just pose values should change, so this should be fine - */ - bPose *dummyPose = NULL; - Object workob = {{NULL}}; - bPoseChannel *pchan; - - /* execute animation step for current frame using a dummy copy of the pose */ - BKE_pose_copy_data(&dummyPose, ob->pose, 0); - - BLI_strncpy(workob.id.name, "OB<ClearTfmWorkOb>", sizeof(workob.id.name)); - workob.type = OB_ARMATURE; - workob.data = ob->data; - workob.adt = ob->adt; - workob.pose = dummyPose; - - BKE_animsys_evaluate_animdata(NULL, scene, &workob.id, workob.adt, cframe, ADT_RECALC_ANIM); - - /* copy back values, but on selected bones only */ - for (pchan = dummyPose->chanbase.first; pchan; pchan = pchan->next) { - pose_bone_do_paste(ob, pchan, only_select, 0); - } - - /* free temp data - free manually as was copied without constraints */ - for (pchan = dummyPose->chanbase.first; pchan; pchan = pchan->next) { - if (pchan->prop) { - IDP_FreeProperty(pchan->prop); - MEM_freeN(pchan->prop); - } - } - - /* was copied without constraints */ - BLI_freelistN(&dummyPose->chanbase); - MEM_freeN(dummyPose); - } - else { - /* no animation, so just reset whole pose to rest pose - * (cannot just restore for selected though) - */ - BKE_pose_rest(ob->pose); - } - - /* notifiers and updates */ - DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, ob); - } - FOREACH_OBJECT_IN_MODE_END; - - return OPERATOR_FINISHED; + ViewLayer *view_layer = CTX_data_view_layer(C); + View3D *v3d = CTX_wm_view3d(C); + Scene *scene = CTX_data_scene(C); + float cframe = (float)CFRA; + const bool only_select = RNA_boolean_get(op->ptr, "only_selected"); + + FOREACH_OBJECT_IN_MODE_BEGIN (view_layer, v3d, OB_ARMATURE, OB_MODE_POSE, ob) { + if ((ob->adt) && (ob->adt->action)) { + /* XXX: this is just like this to avoid contaminating anything else; + * just pose values should change, so this should be fine + */ + bPose *dummyPose = NULL; + Object workob = {{NULL}}; + bPoseChannel *pchan; + + /* execute animation step for current frame using a dummy copy of the pose */ + BKE_pose_copy_data(&dummyPose, ob->pose, 0); + + BLI_strncpy(workob.id.name, "OB<ClearTfmWorkOb>", sizeof(workob.id.name)); + workob.type = OB_ARMATURE; + workob.data = ob->data; + workob.adt = ob->adt; + workob.pose = dummyPose; + + BKE_animsys_evaluate_animdata(NULL, scene, &workob.id, workob.adt, cframe, ADT_RECALC_ANIM); + + /* copy back values, but on selected bones only */ + for (pchan = dummyPose->chanbase.first; pchan; pchan = pchan->next) { + pose_bone_do_paste(ob, pchan, only_select, 0); + } + + /* free temp data - free manually as was copied without constraints */ + for (pchan = dummyPose->chanbase.first; pchan; pchan = pchan->next) { + if (pchan->prop) { + IDP_FreeProperty(pchan->prop); + MEM_freeN(pchan->prop); + } + } + + /* was copied without constraints */ + BLI_freelistN(&dummyPose->chanbase); + MEM_freeN(dummyPose); + } + else { + /* no animation, so just reset whole pose to rest pose + * (cannot just restore for selected though) + */ + BKE_pose_rest(ob->pose); + } + + /* notifiers and updates */ + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, ob); + } + FOREACH_OBJECT_IN_MODE_END; + + return OPERATOR_FINISHED; } void POSE_OT_user_transforms_clear(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Clear User Transforms"; - ot->idname = "POSE_OT_user_transforms_clear"; - ot->description = "Reset pose on selected bones to keyframed state"; + /* identifiers */ + ot->name = "Clear User Transforms"; + ot->idname = "POSE_OT_user_transforms_clear"; + ot->description = "Reset pose on selected bones to keyframed state"; - /* callbacks */ - ot->exec = pose_clear_user_transforms_exec; - ot->poll = ED_operator_posemode; + /* callbacks */ + ot->exec = pose_clear_user_transforms_exec; + ot->poll = ED_operator_posemode; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - /* properties */ - RNA_def_boolean(ot->srna, "only_selected", true, "Only Selected", "Only visible/selected bones"); + /* properties */ + RNA_def_boolean(ot->srna, "only_selected", true, "Only Selected", "Only visible/selected bones"); } diff --git a/source/blender/editors/armature/pose_utils.c b/source/blender/editors/armature/pose_utils.c index 25ddcee52cf..d8793bc2a1e 100644 --- a/source/blender/editors/armature/pose_utils.c +++ b/source/blender/editors/armature/pose_utils.c @@ -63,64 +63,67 @@ /* FCurves <-> PoseChannels Links */ /* helper for poseAnim_mapping_get() -> get the relevant F-Curves per PoseChannel */ -static void fcurves_to_pchan_links_get(ListBase *pfLinks, Object *ob, bAction *act, bPoseChannel *pchan) +static void fcurves_to_pchan_links_get(ListBase *pfLinks, + Object *ob, + bAction *act, + bPoseChannel *pchan) { - ListBase curves = {NULL, NULL}; - int transFlags = action_get_item_transforms(act, ob, pchan, &curves); - - pchan->flag &= ~(POSE_LOC | POSE_ROT | POSE_SIZE | POSE_BBONE_SHAPE); - - /* check if any transforms found... */ - if (transFlags) { - /* make new linkage data */ - tPChanFCurveLink *pfl = MEM_callocN(sizeof(tPChanFCurveLink), "tPChanFCurveLink"); - PointerRNA ptr; - - pfl->ob = ob; - pfl->fcurves = curves; - pfl->pchan = pchan; - - /* get the RNA path to this pchan - this needs to be freed! */ - RNA_pointer_create((ID *)ob, &RNA_PoseBone, pchan, &ptr); - pfl->pchan_path = RNA_path_from_ID_to_struct(&ptr); - - /* add linkage data to operator data */ - BLI_addtail(pfLinks, pfl); - - /* set pchan's transform flags */ - if (transFlags & ACT_TRANS_LOC) - pchan->flag |= POSE_LOC; - if (transFlags & ACT_TRANS_ROT) - pchan->flag |= POSE_ROT; - if (transFlags & ACT_TRANS_SCALE) - pchan->flag |= POSE_SIZE; - if (transFlags & ACT_TRANS_BBONE) - pchan->flag |= POSE_BBONE_SHAPE; - - /* store current transforms */ - copy_v3_v3(pfl->oldloc, pchan->loc); - copy_v3_v3(pfl->oldrot, pchan->eul); - copy_v3_v3(pfl->oldscale, pchan->size); - copy_qt_qt(pfl->oldquat, pchan->quat); - copy_v3_v3(pfl->oldaxis, pchan->rotAxis); - pfl->oldangle = pchan->rotAngle; - - /* store current bbone values */ - pfl->roll1 = pchan->roll1; - pfl->roll2 = pchan->roll2; - pfl->curveInX = pchan->curveInX; - pfl->curveInY = pchan->curveInY; - pfl->curveOutX = pchan->curveOutX; - pfl->curveOutY = pchan->curveOutY; - pfl->ease1 = pchan->ease1; - pfl->ease2 = pchan->ease2; - pfl->scaleIn = pchan->scaleIn; - pfl->scaleOut = pchan->scaleOut; - - /* make copy of custom properties */ - if (pchan->prop && (transFlags & ACT_TRANS_PROP)) - pfl->oldprops = IDP_CopyProperty(pchan->prop); - } + ListBase curves = {NULL, NULL}; + int transFlags = action_get_item_transforms(act, ob, pchan, &curves); + + pchan->flag &= ~(POSE_LOC | POSE_ROT | POSE_SIZE | POSE_BBONE_SHAPE); + + /* check if any transforms found... */ + if (transFlags) { + /* make new linkage data */ + tPChanFCurveLink *pfl = MEM_callocN(sizeof(tPChanFCurveLink), "tPChanFCurveLink"); + PointerRNA ptr; + + pfl->ob = ob; + pfl->fcurves = curves; + pfl->pchan = pchan; + + /* get the RNA path to this pchan - this needs to be freed! */ + RNA_pointer_create((ID *)ob, &RNA_PoseBone, pchan, &ptr); + pfl->pchan_path = RNA_path_from_ID_to_struct(&ptr); + + /* add linkage data to operator data */ + BLI_addtail(pfLinks, pfl); + + /* set pchan's transform flags */ + if (transFlags & ACT_TRANS_LOC) + pchan->flag |= POSE_LOC; + if (transFlags & ACT_TRANS_ROT) + pchan->flag |= POSE_ROT; + if (transFlags & ACT_TRANS_SCALE) + pchan->flag |= POSE_SIZE; + if (transFlags & ACT_TRANS_BBONE) + pchan->flag |= POSE_BBONE_SHAPE; + + /* store current transforms */ + copy_v3_v3(pfl->oldloc, pchan->loc); + copy_v3_v3(pfl->oldrot, pchan->eul); + copy_v3_v3(pfl->oldscale, pchan->size); + copy_qt_qt(pfl->oldquat, pchan->quat); + copy_v3_v3(pfl->oldaxis, pchan->rotAxis); + pfl->oldangle = pchan->rotAngle; + + /* store current bbone values */ + pfl->roll1 = pchan->roll1; + pfl->roll2 = pchan->roll2; + pfl->curveInX = pchan->curveInX; + pfl->curveInY = pchan->curveInY; + pfl->curveOutX = pchan->curveOutX; + pfl->curveOutY = pchan->curveOutY; + pfl->ease1 = pchan->ease1; + pfl->ease2 = pchan->ease2; + pfl->scaleIn = pchan->scaleIn; + pfl->scaleOut = pchan->scaleOut; + + /* make copy of custom properties */ + if (pchan->prop && (transFlags & ACT_TRANS_PROP)) + pfl->oldprops = IDP_CopyProperty(pchan->prop); + } } /** @@ -128,96 +131,85 @@ static void fcurves_to_pchan_links_get(ListBase *pfLinks, Object *ob, bAction *a */ Object *poseAnim_object_get(Object *ob_) { - Object *ob = BKE_object_pose_armature_get(ob_); - if (!ELEM(NULL, - ob, - ob->data, - ob->adt, - ob->adt->action)) - { - return ob; - } - return NULL; + Object *ob = BKE_object_pose_armature_get(ob_); + if (!ELEM(NULL, ob, ob->data, ob->adt, ob->adt->action)) { + return ob; + } + return NULL; } /* get sets of F-Curves providing transforms for the bones in the Pose */ void poseAnim_mapping_get(bContext *C, ListBase *pfLinks) { - /* for each Pose-Channel which gets affected, get the F-Curves for that channel - * and set the relevant transform flags... - */ - Object *prev_ob, *ob_pose_armature; - - prev_ob = NULL; - ob_pose_armature = NULL; - CTX_DATA_BEGIN_WITH_ID (C, bPoseChannel *, pchan, selected_pose_bones, Object *, ob) - { - if (ob != prev_ob) { - prev_ob = ob; - ob_pose_armature = poseAnim_object_get(ob); - } - - if (ob_pose_armature == NULL) { - continue; - } - - fcurves_to_pchan_links_get(pfLinks, - ob_pose_armature, - ob_pose_armature->adt->action, - pchan); - } - CTX_DATA_END; - - /* if no PoseChannels were found, try a second pass, doing visible ones instead - * i.e. if nothing selected, do whole pose - */ - if (BLI_listbase_is_empty(pfLinks)) { - prev_ob = NULL; - ob_pose_armature = NULL; - CTX_DATA_BEGIN_WITH_ID(C, bPoseChannel *, pchan, visible_pose_bones, Object *, ob) - { - if (ob != prev_ob) { - prev_ob = ob; - ob_pose_armature = poseAnim_object_get(ob); - } - - if (ob_pose_armature == NULL) { - continue; - } - - fcurves_to_pchan_links_get(pfLinks, - ob_pose_armature, - ob_pose_armature->adt->action, - pchan); - } - CTX_DATA_END; - } + /* for each Pose-Channel which gets affected, get the F-Curves for that channel + * and set the relevant transform flags... + */ + Object *prev_ob, *ob_pose_armature; + + prev_ob = NULL; + ob_pose_armature = NULL; + CTX_DATA_BEGIN_WITH_ID(C, bPoseChannel *, pchan, selected_pose_bones, Object *, ob) + { + if (ob != prev_ob) { + prev_ob = ob; + ob_pose_armature = poseAnim_object_get(ob); + } + + if (ob_pose_armature == NULL) { + continue; + } + + fcurves_to_pchan_links_get(pfLinks, ob_pose_armature, ob_pose_armature->adt->action, pchan); + } + CTX_DATA_END; + + /* if no PoseChannels were found, try a second pass, doing visible ones instead + * i.e. if nothing selected, do whole pose + */ + if (BLI_listbase_is_empty(pfLinks)) { + prev_ob = NULL; + ob_pose_armature = NULL; + CTX_DATA_BEGIN_WITH_ID(C, bPoseChannel *, pchan, visible_pose_bones, Object *, ob) + { + if (ob != prev_ob) { + prev_ob = ob; + ob_pose_armature = poseAnim_object_get(ob); + } + + if (ob_pose_armature == NULL) { + continue; + } + + fcurves_to_pchan_links_get(pfLinks, ob_pose_armature, ob_pose_armature->adt->action, pchan); + } + CTX_DATA_END; + } } /* free F-Curve <-> PoseChannel links */ void poseAnim_mapping_free(ListBase *pfLinks) { - tPChanFCurveLink *pfl, *pfln = NULL; + tPChanFCurveLink *pfl, *pfln = NULL; - /* free the temp pchan links and their data */ - for (pfl = pfLinks->first; pfl; pfl = pfln) { - pfln = pfl->next; + /* free the temp pchan links and their data */ + for (pfl = pfLinks->first; pfl; pfl = pfln) { + pfln = pfl->next; - /* free custom properties */ - if (pfl->oldprops) { - IDP_FreeProperty(pfl->oldprops); - MEM_freeN(pfl->oldprops); - } + /* free custom properties */ + if (pfl->oldprops) { + IDP_FreeProperty(pfl->oldprops); + MEM_freeN(pfl->oldprops); + } - /* free list of F-Curve reference links */ - BLI_freelistN(&pfl->fcurves); + /* free list of F-Curve reference links */ + BLI_freelistN(&pfl->fcurves); - /* free pchan RNA Path */ - MEM_freeN(pfl->pchan_path); + /* free pchan RNA Path */ + MEM_freeN(pfl->pchan_path); - /* free link itself */ - BLI_freelinkN(pfLinks, pfl); - } + /* free link itself */ + BLI_freelinkN(pfLinks, pfl); + } } /* ------------------------- */ @@ -225,125 +217,127 @@ void poseAnim_mapping_free(ListBase *pfLinks) /* helper for apply() / reset() - refresh the data */ void poseAnim_mapping_refresh(bContext *C, Scene *scene, Object *ob) { - Depsgraph *depsgraph = CTX_data_depsgraph(C); - bArmature *arm = (bArmature *)ob->data; - - /* old optimize trick... this enforces to bypass the depgraph - * - note: code copied from transform_generics.c -> recalcData() - */ - /* FIXME: shouldn't this use the builtin stuff? */ - if ((arm->flag & ARM_DELAYDEFORM) == 0) - DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); /* sets recalc flags */ - else - BKE_pose_where_is(depsgraph, scene, ob); - - /* otherwise animation doesn't get updated */ - DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE); - WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + bArmature *arm = (bArmature *)ob->data; + + /* old optimize trick... this enforces to bypass the depgraph + * - note: code copied from transform_generics.c -> recalcData() + */ + /* FIXME: shouldn't this use the builtin stuff? */ + if ((arm->flag & ARM_DELAYDEFORM) == 0) + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); /* sets recalc flags */ + else + BKE_pose_where_is(depsgraph, scene, ob); + + /* otherwise animation doesn't get updated */ + DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE); + WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); } /* reset changes made to current pose */ void poseAnim_mapping_reset(ListBase *pfLinks) { - tPChanFCurveLink *pfl; - - /* iterate over each pose-channel affected, restoring all channels to their original values */ - for (pfl = pfLinks->first; pfl; pfl = pfl->next) { - bPoseChannel *pchan = pfl->pchan; - - /* just copy all the values over regardless of whether they changed or not */ - copy_v3_v3(pchan->loc, pfl->oldloc); - copy_v3_v3(pchan->eul, pfl->oldrot); - copy_v3_v3(pchan->size, pfl->oldscale); - copy_qt_qt(pchan->quat, pfl->oldquat); - copy_v3_v3(pchan->rotAxis, pfl->oldaxis); - pchan->rotAngle = pfl->oldangle; - - /* store current bbone values */ - pchan->roll1 = pfl->roll1; - pchan->roll2 = pfl->roll2; - pchan->curveInX = pfl->curveInX; - pchan->curveInY = pfl->curveInY; - pchan->curveOutX = pfl->curveOutX; - pchan->curveOutY = pfl->curveOutY; - pchan->ease1 = pfl->ease1; - pchan->ease2 = pfl->ease2; - pchan->scaleIn = pfl->scaleIn; - pchan->scaleOut = pfl->scaleOut; - - /* just overwrite values of properties from the stored copies (there should be some) */ - if (pfl->oldprops) - IDP_SyncGroupValues(pfl->pchan->prop, pfl->oldprops); - } + tPChanFCurveLink *pfl; + + /* iterate over each pose-channel affected, restoring all channels to their original values */ + for (pfl = pfLinks->first; pfl; pfl = pfl->next) { + bPoseChannel *pchan = pfl->pchan; + + /* just copy all the values over regardless of whether they changed or not */ + copy_v3_v3(pchan->loc, pfl->oldloc); + copy_v3_v3(pchan->eul, pfl->oldrot); + copy_v3_v3(pchan->size, pfl->oldscale); + copy_qt_qt(pchan->quat, pfl->oldquat); + copy_v3_v3(pchan->rotAxis, pfl->oldaxis); + pchan->rotAngle = pfl->oldangle; + + /* store current bbone values */ + pchan->roll1 = pfl->roll1; + pchan->roll2 = pfl->roll2; + pchan->curveInX = pfl->curveInX; + pchan->curveInY = pfl->curveInY; + pchan->curveOutX = pfl->curveOutX; + pchan->curveOutY = pfl->curveOutY; + pchan->ease1 = pfl->ease1; + pchan->ease2 = pfl->ease2; + pchan->scaleIn = pfl->scaleIn; + pchan->scaleOut = pfl->scaleOut; + + /* just overwrite values of properties from the stored copies (there should be some) */ + if (pfl->oldprops) + IDP_SyncGroupValues(pfl->pchan->prop, pfl->oldprops); + } } /* perform auto-key-framing after changes were made + confirmed */ void poseAnim_mapping_autoKeyframe(bContext *C, Scene *scene, ListBase *pfLinks, float cframe) { - ViewLayer *view_layer = CTX_data_view_layer(C); - View3D *v3d = CTX_wm_view3d(C); - bool skip = true; - - FOREACH_OBJECT_IN_MODE_BEGIN(view_layer, v3d, OB_ARMATURE, OB_MODE_POSE, ob) { - ob->id.tag &= ~LIB_TAG_DOIT; - ob = poseAnim_object_get(ob); - - /* Ensure validity of the settings from the context. */ - if (ob == NULL) { - continue; - } - - if (autokeyframe_cfra_can_key(scene, &ob->id)) { - ob->id.tag |= LIB_TAG_DOIT; - skip = false; - } - } FOREACH_OBJECT_IN_MODE_END; - - if (skip) { - return; - } - - /* Insert keyframes as necessary if auto-key-framing. */ - KeyingSet *ks = ANIM_get_keyingset_for_autokeying(scene, ANIM_KS_WHOLE_CHARACTER_ID); - ListBase dsources = {NULL, NULL}; - tPChanFCurveLink *pfl; - - /* iterate over each pose-channel affected, tagging bones to be keyed */ - /* XXX: here we already have the information about what transforms exist, though - * it might be easier to just overwrite all using normal mechanisms - */ - for (pfl = pfLinks->first; pfl; pfl = pfl->next) { - bPoseChannel *pchan = pfl->pchan; - - if ((pfl->ob->id.tag & LIB_TAG_DOIT) == 0) { - continue; - } - - /* add datasource override for the PoseChannel, to be used later */ - ANIM_relative_keyingset_add_source(&dsources, &pfl->ob->id, &RNA_PoseBone, pchan); - - /* clear any unkeyed tags */ - if (pchan->bone) { - pchan->bone->flag &= ~BONE_UNKEYED; - } - } - - /* insert keyframes for all relevant bones in one go */ - ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, cframe); - BLI_freelistN(&dsources); - - /* do the bone paths - * - only do this if keyframes should have been added - * - do not calculate unless there are paths already to update... - */ - FOREACH_OBJECT_IN_MODE_BEGIN(view_layer, v3d, OB_ARMATURE, OB_MODE_POSE, ob) { - if (ob->id.tag & LIB_TAG_DOIT) { - if (ob->pose->avs.path_bakeflag & MOTIONPATH_BAKE_HAS_PATHS) { - //ED_pose_clear_paths(C, ob); // XXX for now, don't need to clear - ED_pose_recalculate_paths(C, scene, ob, false); - } - } - } FOREACH_OBJECT_IN_MODE_END; + ViewLayer *view_layer = CTX_data_view_layer(C); + View3D *v3d = CTX_wm_view3d(C); + bool skip = true; + + FOREACH_OBJECT_IN_MODE_BEGIN (view_layer, v3d, OB_ARMATURE, OB_MODE_POSE, ob) { + ob->id.tag &= ~LIB_TAG_DOIT; + ob = poseAnim_object_get(ob); + + /* Ensure validity of the settings from the context. */ + if (ob == NULL) { + continue; + } + + if (autokeyframe_cfra_can_key(scene, &ob->id)) { + ob->id.tag |= LIB_TAG_DOIT; + skip = false; + } + } + FOREACH_OBJECT_IN_MODE_END; + + if (skip) { + return; + } + + /* Insert keyframes as necessary if auto-key-framing. */ + KeyingSet *ks = ANIM_get_keyingset_for_autokeying(scene, ANIM_KS_WHOLE_CHARACTER_ID); + ListBase dsources = {NULL, NULL}; + tPChanFCurveLink *pfl; + + /* iterate over each pose-channel affected, tagging bones to be keyed */ + /* XXX: here we already have the information about what transforms exist, though + * it might be easier to just overwrite all using normal mechanisms + */ + for (pfl = pfLinks->first; pfl; pfl = pfl->next) { + bPoseChannel *pchan = pfl->pchan; + + if ((pfl->ob->id.tag & LIB_TAG_DOIT) == 0) { + continue; + } + + /* add datasource override for the PoseChannel, to be used later */ + ANIM_relative_keyingset_add_source(&dsources, &pfl->ob->id, &RNA_PoseBone, pchan); + + /* clear any unkeyed tags */ + if (pchan->bone) { + pchan->bone->flag &= ~BONE_UNKEYED; + } + } + + /* insert keyframes for all relevant bones in one go */ + ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, cframe); + BLI_freelistN(&dsources); + + /* do the bone paths + * - only do this if keyframes should have been added + * - do not calculate unless there are paths already to update... + */ + FOREACH_OBJECT_IN_MODE_BEGIN (view_layer, v3d, OB_ARMATURE, OB_MODE_POSE, ob) { + if (ob->id.tag & LIB_TAG_DOIT) { + if (ob->pose->avs.path_bakeflag & MOTIONPATH_BAKE_HAS_PATHS) { + //ED_pose_clear_paths(C, ob); // XXX for now, don't need to clear + ED_pose_recalculate_paths(C, scene, ob, false); + } + } + } + FOREACH_OBJECT_IN_MODE_END; } /* ------------------------- */ @@ -353,20 +347,20 @@ void poseAnim_mapping_autoKeyframe(bContext *C, Scene *scene, ListBase *pfLinks, */ LinkData *poseAnim_mapping_getNextFCurve(ListBase *fcuLinks, LinkData *prev, const char *path) { - LinkData *first = (prev) ? prev->next : (fcuLinks) ? fcuLinks->first : NULL; - LinkData *ld; + LinkData *first = (prev) ? prev->next : (fcuLinks) ? fcuLinks->first : NULL; + LinkData *ld; - /* check each link to see if the linked F-Curve has a matching path */ - for (ld = first; ld; ld = ld->next) { - FCurve *fcu = (FCurve *)ld->data; + /* check each link to see if the linked F-Curve has a matching path */ + for (ld = first; ld; ld = ld->next) { + FCurve *fcu = (FCurve *)ld->data; - /* check if paths match */ - if (STREQ(path, fcu->rna_path)) - return ld; - } + /* check if paths match */ + if (STREQ(path, fcu->rna_path)) + return ld; + } - /* none found */ - return NULL; + /* none found */ + return NULL; } /* *********************************************** */ |