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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/source
diff options
context:
space:
mode:
authorJoshua Leung <aligorith@gmail.com>2013-02-28 03:34:29 +0400
committerJoshua Leung <aligorith@gmail.com>2013-02-28 03:34:29 +0400
commit28a84a2c3a204bfab21541df0d6a78d718248f42 (patch)
tree1829518e5a0952bb8e52e7380007ed906992290d /source
parent07cd75d7b04836e6d4b19f35f79c9c1283458aaa (diff)
Code Maintenance - Splitting up Armature/Pose Editing Files
This commit splits editarmature.c and poseobject.c into several files, such that certain types of functionality are (mostly) self-contained within particular files (instead of being mixed in with other functionality in a large file). In particular, this was done so that: 1) Armature EditMode tools are now in the armature_*.c files only, and Pose Mode tools in pose_*.c files only. - In one or two cases, this hasn't been possible as the two modes rely on much of the same shared infrastructure. 2) The "clear loc/rot/scale" operators and pose show/hide are no longer housed in editarmature.c 3) Selection operators, Transform operators, structural (add/delete) operators, and supporting utilities for the modes (enter/exit/roll-calculations) are not all interleaved in an ad-hoc manner Notes: * I've tried to ensure that the history of the new files has been maintained by doing svn copy {editarmature.c/poseobject.c} {armature_*.c/pose_*.c} Unfortunately, this means that the diffs are a bit messy. * There should be no functional/logic changes here. Just code moving around and cosmetic comment tweaks where needed. * #includes have largely been untouched for now, but could be cleaned up later * CMake changes untested, but should work in theory.
Diffstat (limited to 'source')
-rw-r--r--source/blender/editors/armature/CMakeLists.txt12
-rw-r--r--source/blender/editors/armature/armature_add.c880
-rw-r--r--source/blender/editors/armature/armature_edit.c1326
-rw-r--r--source/blender/editors/armature/armature_intern.h32
-rw-r--r--source/blender/editors/armature/armature_naming.c395
-rw-r--r--source/blender/editors/armature/armature_relations.c800
-rw-r--r--source/blender/editors/armature/armature_select.c951
-rw-r--r--source/blender/editors/armature/armature_skinning.c480
-rw-r--r--source/blender/editors/armature/armature_utils.c707
-rw-r--r--source/blender/editors/armature/editarmature.c6199
-rw-r--r--source/blender/editors/armature/pose_edit.c1198
-rw-r--r--source/blender/editors/armature/pose_group.c549
-rw-r--r--source/blender/editors/armature/pose_select.c876
-rw-r--r--source/blender/editors/armature/pose_transform.c899
-rw-r--r--source/blender/editors/armature/poseobject.c2364
15 files changed, 9098 insertions, 8570 deletions
diff --git a/source/blender/editors/armature/CMakeLists.txt b/source/blender/editors/armature/CMakeLists.txt
index 2bd9956ef5a..1ce04ddb642 100644
--- a/source/blender/editors/armature/CMakeLists.txt
+++ b/source/blender/editors/armature/CMakeLists.txt
@@ -36,16 +36,24 @@ set(INC_SYS
)
set(SRC
+ armature_add.c
+ armature_edit.c
+ armature_naming.c
armature_ops.c
- editarmature.c
+ armature_relations.c
+ armature_select.c
+ armature_utils.c
editarmature_generate.c
editarmature_retarget.c
editarmature_sketch.c
meshlaplacian.c
+ pose_edit.c
+ pose_group.c
+ pose_select.c
+ pose_transform.c
poseSlide.c
poseUtils.c
poselib.c
- poseobject.c
reeb.c
BIF_generate.h
diff --git a/source/blender/editors/armature/armature_add.c b/source/blender/editors/armature/armature_add.c
new file mode 100644
index 00000000000..b2a5eaec2cf
--- /dev/null
+++ b/source/blender/editors/armature/armature_add.c
@@ -0,0 +1,880 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
+ * All rights reserved.
+ *
+ * Contributor(s): Blender Foundation, 2002-2009 full recode.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ *
+ * Operators and API's for creating bones
+ */
+
+/** \file blender/editors/armature/armature_add.c
+ * \ingroup edarmature
+ */
+
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <math.h>
+#include <float.h>
+
+
+#include "DNA_anim_types.h"
+#include "DNA_armature_types.h"
+#include "DNA_constraint_types.h"
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_modifier_types.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_blenlib.h"
+#include "BLI_math.h"
+#include "BLI_utildefines.h"
+#include "BLI_ghash.h"
+
+#include "BKE_animsys.h"
+#include "BKE_action.h"
+#include "BKE_armature.h"
+#include "BKE_constraint.h"
+#include "BKE_context.h"
+#include "BKE_deform.h"
+#include "BKE_depsgraph.h"
+#include "BKE_DerivedMesh.h"
+#include "BKE_global.h"
+#include "BKE_idprop.h"
+#include "BKE_main.h"
+#include "BKE_object.h"
+#include "BKE_report.h"
+#include "BKE_modifier.h"
+
+#include "BIF_gl.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "ED_armature.h"
+#include "ED_keyframing.h"
+#include "ED_object.h"
+#include "ED_screen.h"
+#include "ED_util.h"
+#include "ED_view3d.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "armature_intern.h"
+
+/* *************** Adding stuff in editmode *************** */
+
+/* default bone add, returns it selected, but without tail set */
+/* XXX should be used everywhere, now it mallocs bones still locally in functions */
+EditBone *ED_armature_edit_bone_add(bArmature *arm, const char *name)
+{
+ EditBone *bone = MEM_callocN(sizeof(EditBone), "eBone");
+
+ BLI_strncpy(bone->name, name, sizeof(bone->name));
+ unique_editbone_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->ease1 = 1.0f;
+ bone->ease2 = 1.0f;
+ bone->rad_head = 0.10f;
+ bone->rad_tail = 0.05f;
+ bone->segments = 1;
+ bone->layer = arm->layer;
+
+ return bone;
+}
+
+/* v3d and rv3d are allowed to be NULL */
+void add_primitive_bone(Scene *scene, View3D *v3d, RegionView3D *rv3d)
+{
+ Object *obedit = scene->obedit; // XXX get from context
+ bArmature *arm = obedit->data;
+ float obmat[3][3], curs[3], viewmat[3][3], totmat[3][3], imat[3][3];
+ EditBone *bone;
+
+ /* Get inverse point for head and orientation for tail */
+ invert_m4_m4(obedit->imat, obedit->obmat);
+ mul_v3_m4v3(curs, obedit->imat, give_cursor(scene, v3d));
+
+ 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);
+
+ ED_armature_deselect_all(obedit, 0);
+
+ /* Create a bone */
+ bone = ED_armature_edit_bone_add(arm, "Bone");
+
+ arm->act_edbone = bone;
+
+ 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
+}
+
+
+/* previously addvert_armature */
+/* the ctrl-click method */
+static int armature_click_extrude_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ View3D *v3d;
+ bArmature *arm;
+ EditBone *ebone, *newbone, *flipbone;
+ float mat[3][3], imat[3][3];
+ const float *curs;
+ int a, to_root = 0;
+ Object *obedit;
+ Scene *scene;
+
+ scene = CTX_data_scene(C);
+ v3d = CTX_wm_view3d(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_deselect_all(obedit, 0);
+
+ /* we re-use code for mirror editing... */
+ flipbone = NULL;
+ if (arm->flag & ARM_MIRROR_EDIT)
+ flipbone = ED_armature_bone_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_edit_bone_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;
+ }
+
+ curs = give_cursor(scene, v3d);
+ copy_v3_v3(newbone->tail, curs);
+ 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_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, wmEvent *event)
+{
+ /* 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 *fp, tvec[3], oldcurs[3], mval_f[2];
+ int retv;
+
+ scene = CTX_data_scene(C);
+ ar = CTX_wm_region(C);
+ v3d = CTX_wm_view3d(C);
+
+ fp = give_cursor(scene, v3d);
+
+ copy_v3_v3(oldcurs, fp);
+
+ VECCOPY2D(mval_f, event->mval);
+ ED_view3d_win_to_3d(ar, fp, mval_f, tvec);
+ copy_v3_v3(fp, tvec);
+
+ /* 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(fp, oldcurs);
+
+ 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";
+
+ /* 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;
+
+ /* 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;
+
+ ebo = ED_armature_edit_bone_add(obedit->data, "Bone");
+
+ copy_v3_v3(ebo->head, head);
+ copy_v3_v3(ebo->tail, tail);
+
+ return ebo;
+}
+
+
+static EditBone *get_named_editbone(ListBase *edbo, char *name)
+{
+ EditBone *eBone;
+
+ if (name) {
+ for (eBone = edbo->first; eBone; eBone = eBone->next) {
+ if (!strcmp(name, eBone->name))
+ return eBone;
+ }
+ }
+
+ return NULL;
+}
+
+/* Call this before doing any duplications
+ * */
+void preEditBoneDuplicate(ListBase *editbones)
+{
+ EditBone *eBone;
+
+ /* clear temp */
+ for (eBone = editbones->first; eBone; eBone = eBone->next) {
+ eBone->temp = 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)
+{
+ /* 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?
+ */
+ bConstraintTypeInfo *cti = BKE_constraint_get_typeinfo(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) {
+ newtarget = (EditBone *) oldtarget->temp;
+ 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);
+}
+
+
+EditBone *duplicateEditBoneObjects(EditBone *curBone, const char *name, ListBase *editbones,
+ Object *src_ob, Object *dst_ob)
+{
+ EditBone *eBone = MEM_mallocN(sizeof(EditBone), "addup_editbone");
+
+ /* Copy data from old bone to new bone */
+ memcpy(eBone, curBone, sizeof(EditBone));
+
+ curBone->temp = eBone;
+ eBone->temp = curBone;
+
+ if (name != NULL) {
+ BLI_strncpy(eBone->name, name, sizeof(eBone->name));
+ }
+
+ unique_editbone_name(editbones, eBone->name, NULL);
+ BLI_addtail(editbones, eBone);
+
+ /* 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;
+
+ 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);
+ }
+ }
+ }
+
+ return eBone;
+}
+
+EditBone *duplicateEditBone(EditBone *curBone, const char *name, ListBase *editbones, Object *ob)
+{
+ return duplicateEditBoneObjects(curBone, name, editbones, ob, ob);
+}
+
+/* previously adduplicate_armature */
+static int armature_duplicate_selected_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ bArmature *arm;
+ EditBone *eBone = NULL;
+ EditBone *curBone;
+ EditBone *firstDup = NULL; /* The beginning of the duplicated bones in the edbo list */
+
+ Object *obedit = CTX_data_edit_object(C);
+ arm = obedit->data;
+
+ /* cancel if nothing selected */
+ if (CTX_DATA_COUNT(C, selected_bones) == 0)
+ return OPERATOR_CANCELLED;
+
+ ED_armature_sync_selection(arm->edbo); // XXX why is this needed?
+
+ preEditBoneDuplicate(arm->edbo);
+
+ /* Select mirrored bones */
+ if (arm->flag & ARM_MIRROR_EDIT) {
+ for (curBone = arm->edbo->first; curBone; curBone = curBone->next) {
+ if (EBONE_VISIBLE(arm, curBone)) {
+ if (curBone->flag & BONE_SELECTED) {
+ eBone = ED_armature_bone_get_mirrored(arm->edbo, curBone);
+ if (eBone)
+ eBone->flag |= BONE_SELECTED;
+ }
+ }
+ }
+ }
+
+
+ /* Find the selected bones and duplicate them as needed */
+ for (curBone = arm->edbo->first; curBone && curBone != firstDup; curBone = curBone->next) {
+ if (EBONE_VISIBLE(arm, curBone)) {
+ if (curBone->flag & BONE_SELECTED) {
+
+ eBone = duplicateEditBone(curBone, curBone->name, arm->edbo, obedit);
+
+ if (!firstDup)
+ firstDup = eBone;
+
+ }
+ }
+ }
+
+ /* Run though the list and fix the pointers */
+ for (curBone = arm->edbo->first; curBone && curBone != firstDup; curBone = curBone->next) {
+ if (EBONE_VISIBLE(arm, curBone)) {
+ if (curBone->flag & BONE_SELECTED) {
+ eBone = (EditBone *) curBone->temp;
+
+ if (!curBone->parent) {
+ /* If this bone has no parent,
+ * Set the duplicate->parent to NULL
+ */
+ eBone->parent = NULL;
+ }
+ else if (curBone->parent->temp) {
+ /* If this bone has a parent that was duplicated,
+ * Set the duplicate->parent to the curBone->parent->temp
+ */
+ eBone->parent = (EditBone *)curBone->parent->temp;
+ }
+ else {
+ /* If this bone has a parent that IS not selected,
+ * Set the duplicate->parent to the curBone->parent
+ */
+ eBone->parent = (EditBone *) curBone->parent;
+ eBone->flag &= ~BONE_CONNECTED;
+ }
+
+ /* Lets try to fix any constraint subtargets that might
+ * have been duplicated
+ */
+ updateDuplicateSubtarget(eBone, arm->edbo, obedit);
+ }
+ }
+ }
+
+ /* correct the active bone */
+ if (arm->act_edbone) {
+ eBone = arm->act_edbone;
+ if (eBone->temp)
+ arm->act_edbone = eBone->temp;
+ }
+
+ /* Deselect the old bones and select the new ones */
+ for (curBone = arm->edbo->first; curBone && curBone != firstDup; curBone = curBone->next) {
+ if (EBONE_VISIBLE(arm, curBone))
+ curBone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
+ }
+
+ ED_armature_validate_active(arm);
+
+ WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit);
+
+ 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;
+}
+
+/* ------------------------------------------ */
+
+/* previously extrude_armature */
+/* context; editmode armature */
+/* if forked && mirror-edit: makes two bones with flipped names */
+static int armature_extrude_exec(bContext *C, wmOperator *op)
+{
+ Object *obedit;
+ bArmature *arm;
+ EditBone *newbone, *ebone, *flipbone, *first = NULL;
+ int a, totbone = 0, do_extrude;
+ int forked = RNA_boolean_get(op->ptr, "forked");
+
+ obedit = CTX_data_edit_object(C);
+ arm = obedit->data;
+
+ /* 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_bone_get_mirrored(arm->edbo, ebone);
+ if (flipbone) {
+ forked = 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))
+ 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;
+
+ newbone->flag = ebone->flag & (BONE_TIPSEL | BONE_RELATIVE_PARENTING); // copies it, in case mirrored bone
+
+ 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->ease1 = ebone->ease1;
+ newbone->ease2 = ebone->ease2;
+ newbone->rad_head = ebone->rad_tail; // don't copy entire bone...
+ newbone->rad_tail = ebone->rad_tail;
+ newbone->segments = 1;
+ newbone->layer = ebone->layer;
+
+ BLI_strncpy(newbone->name, ebone->name, sizeof(newbone->name));
+
+ if (flipbone && forked) { // only set if mirror edit
+ if (strlen(newbone->name) < 30) {
+ if (a == 0) strcat(newbone->name, "_L");
+ else strcat(newbone->name, "_R");
+ }
+ }
+ unique_editbone_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;
+
+ if (totbone == 0) return OPERATOR_CANCELLED;
+
+ /* Transform the endpoints */
+ ED_armature_sync_selection(arm->edbo);
+
+ WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit);
+
+ return OPERATOR_FINISHED;
+}
+
+void ARMATURE_OT_extrude(wmOperatorType *ot)
+{
+ /* 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;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* props */
+ RNA_def_boolean(ot->srna, "forked", 0, "Forked", "");
+}
+
+/* ********************** Bone Add *************************************/
+
+/*op makes a new bone and returns it with its tip selected */
+
+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];
+
+ RNA_string_get(op->ptr, "name", name);
+
+ copy_v3_v3(curs, give_cursor(CTX_data_scene(C), CTX_wm_view3d(C)));
+
+ /* 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);
+
+ copy_m3_m4(viewmat, obedit->obmat);
+ mul_m3_m3m3(totmat, obmat, viewmat);
+ invert_m3_m3(imat, totmat);
+
+ ED_armature_deselect_all(obedit, 0);
+
+ /* Create a bone */
+ bone = ED_armature_edit_bone_add(obedit->data, name);
+
+ 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
+
+ /* note, notifier might evolve */
+ WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit);
+
+ 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;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ RNA_def_string(ot->srna, "name", "Bone", MAXBONENAME, "Name", "Name of the newly created bone");
+
+}
+
+/* ********************** Subdivide *******************************/
+
+/* Subdivide Operators:
+ * This group of operators all use the same 'exec' callback, but they are called
+ * through several different operators - a combined menu (which just calls the exec in the
+ * appropriate ways), and two separate ones.
+ */
+
+static int armature_subdivide_exec(bContext *C, wmOperator *op)
+{
+ Object *obedit = CTX_data_edit_object(C);
+ bArmature *arm = obedit->data;
+ 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(C, EditBone *, ebone, selected_editable_bones)
+ {
+ 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 = 0.5f * (ebone->rad_head + ebone->rad_tail);
+ ebone->rad_tail = newbone->rad_head;
+
+ newbone->flag |= BONE_CONNECTED;
+
+ unique_editbone_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;
+
+ /* 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;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* Properties */
+ prop = RNA_def_int(ot->srna, "number_cuts", 1, 1, INT_MAX, "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
new file mode 100644
index 00000000000..65c3c1c64e2
--- /dev/null
+++ b/source/blender/editors/armature/armature_edit.c
@@ -0,0 +1,1326 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
+ * All rights reserved.
+ *
+ * Contributor(s): Blender Foundation, 2002-2009 full recode.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ *
+ * Armature EditMode tools - transforms, chain based editing, and other settings
+ */
+
+/** \file blender/editors/armature/armature_edit.c
+ * \ingroup edarmature
+ */
+
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <math.h>
+#include <float.h>
+#include <assert.h>
+
+
+#include "DNA_anim_types.h"
+#include "DNA_armature_types.h"
+#include "DNA_constraint_types.h"
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_modifier_types.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_blenlib.h"
+#include "BLI_math.h"
+#include "BLI_utildefines.h"
+#include "BLI_ghash.h"
+
+#include "BKE_animsys.h"
+#include "BKE_action.h"
+#include "BKE_armature.h"
+#include "BKE_constraint.h"
+#include "BKE_context.h"
+#include "BKE_deform.h"
+#include "BKE_depsgraph.h"
+#include "BKE_DerivedMesh.h"
+#include "BKE_global.h"
+#include "BKE_idprop.h"
+#include "BKE_main.h"
+#include "BKE_object.h"
+#include "BKE_report.h"
+#include "BKE_modifier.h"
+
+#include "BIF_gl.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "ED_armature.h"
+#include "ED_keyframing.h"
+#include "ED_object.h"
+#include "ED_screen.h"
+#include "ED_util.h"
+#include "ED_view3d.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "armature_intern.h"
+
+/* ************************** Object Tools Exports ******************************* */
+/* NOTE: these functions are exported to the Object module to be called from the tools there */
+
+void ED_armature_apply_transform(Object *ob, float mat[4][4])
+{
+ EditBone *ebone;
+ bArmature *arm = ob->data;
+ 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);
+
+ /* Put the armature into editmode */
+ ED_armature_to_edit(ob);
+
+ /* Do the rotations */
+ for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
+ float delta[3], tmat[3][3];
+
+ /* find the current bone's roll matrix */
+ sub_v3_v3v3(delta, ebone->tail, ebone->head);
+ vec_roll_to_mat3(delta, ebone->roll, 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 transfiormed roll back */
+ mat3_to_vec_roll(tmat, NULL, &ebone->roll);
+
+ 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;
+ }
+
+ /* Turn the list into an armature */
+ ED_armature_from_edit(ob);
+ ED_armature_edit_free(ob);
+}
+
+/* exported for use in editors/object/ */
+/* 0 == do center, 1 == center new, 2 == center cursor */
+void docenter_armature(Scene *scene, Object *ob, float cursor[3], int centermode, int around)
+{
+ Object *obedit = scene->obedit; // XXX get from context
+ EditBone *ebone;
+ bArmature *arm = ob->data;
+ float cent[3];
+
+ /* Put the armature into editmode */
+ if (ob != obedit) {
+ ED_armature_to_edit(ob);
+ obedit = NULL; /* we cant use this so behave as if there is no obedit */
+ }
+
+ /* 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_CENTROID) {
+ 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 (obedit == NULL) {
+ ED_armature_from_edit(ob);
+ ED_armature_edit_free(ob);
+ }
+
+ /* Adjust object location for new centerpoint */
+ if (centermode && obedit == NULL) {
+ mul_mat3_m4_v3(ob->obmat, cent); /* ommit translation part */
+ add_v3_v3(ob->loc, cent);
+ }
+}
+
+/* ********************************* Roll ******************************* */
+
+/* adjust bone roll to align Z axis with vector
+ * vec is in local space and is normalized
+ */
+float ED_rollBoneToVector(EditBone *bone, const float align_axis[3], const short axis_only)
+{
+ float mat[3][3], nor[3];
+
+ sub_v3_v3v3(nor, bone->tail, bone->head);
+ vec_roll_to_mat3(nor, 0.0f, mat);
+
+ /* check the bone isn't aligned with the axis */
+ if (!is_zero_v3(align_axis) && angle_v3v3(align_axis, mat[2]) > FLT_EPSILON) {
+ float vec[3], align_axis_proj[3], roll;
+
+ /* project the new_up_axis along the normal */
+ project_v3_v3v3(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.0)) {
+ negate_v3(align_axis_proj);
+ }
+ }
+
+ roll = angle_v3v3(align_axis_proj, mat[2]);
+
+ cross_v3_v3v3(vec, mat[2], align_axis_proj);
+
+ if (dot_v3v3(vec, nor) < 0) {
+ roll = -roll;
+ }
+
+ return roll;
+ }
+
+ return 0.0f;
+}
+
+
+static EnumPropertyItem prop_calc_roll_types[] = {
+ {0, "X", 0, "X Axis", ""},
+ {1, "Y", 0, "Y Axis", ""},
+ {2, "Z", 0, "Z Axis", ""},
+ {5, "ACTIVE", 0, "Active Bone", ""},
+ {6, "VIEW", 0, "View Axis", ""},
+ {7, "CURSOR", 0, "Cursor", ""},
+ {0, NULL, 0, NULL, NULL}
+};
+
+
+static int armature_calc_roll_exec(bContext *C, wmOperator *op)
+{
+ Object *ob = CTX_data_edit_object(C);
+ const short type = RNA_enum_get(op->ptr, "type");
+ const short axis_only = RNA_boolean_get(op->ptr, "axis_only");
+ const short axis_flip = RNA_boolean_get(op->ptr, "axis_flip");
+
+ float imat[3][3];
+
+ bArmature *arm = ob->data;
+ EditBone *ebone;
+
+ copy_m3_m4(imat, ob->obmat);
+ invert_m3(imat);
+
+ if (type == 7) { /* Cursor */
+ Scene *scene = CTX_data_scene(C);
+ View3D *v3d = CTX_wm_view3d(C); /* can be NULL */
+ float cursor_local[3];
+ const float *cursor = give_cursor(scene, v3d);
+
+
+ copy_v3_v3(cursor_local, cursor);
+ mul_m3_v3(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);
+ ebone->roll = ED_rollBoneToVector(ebone, cursor_rel, axis_only);
+ }
+ }
+ }
+ else {
+ float vec[3] = {0.0f, 0.0f, 0.0f};
+ if (type == 6) { /* View */
+ RegionView3D *rv3d = CTX_wm_region_view3d(C);
+ if (rv3d == NULL) {
+ BKE_report(op->reports, RPT_ERROR, "No region view3d available");
+ return OPERATOR_CANCELLED;
+ }
+
+ copy_v3_v3(vec, rv3d->viewinv[2]);
+ mul_m3_v3(imat, vec);
+ }
+ else if (type == 5) {
+ float mat[3][3], nor[3];
+ ebone = (EditBone *)arm->act_edbone;
+ if (ebone == NULL) {
+ BKE_report(op->reports, RPT_ERROR, "No active bone set");
+ return OPERATOR_CANCELLED;
+ }
+
+ sub_v3_v3v3(nor, ebone->tail, ebone->head);
+ vec_roll_to_mat3(nor, ebone->roll, mat);
+ copy_v3_v3(vec, mat[2]);
+ }
+ else { /* Axis */
+ assert(type >= 0 && type <= 5);
+ if (type < 3) vec[type] = 1.0f;
+ else vec[type - 2] = -1.0f;
+ mul_m3_v3(imat, 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_rollBoneToVector(ebone, vec, axis_only);
+ }
+ }
+ }
+
+ 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_bone_get_mirrored(arm->edbo, ebone);
+ if (ebone_mirr && (EBONE_VISIBLE(arm, ebone_mirr) && EBONE_EDITABLE(ebone_mirr))) {
+ ebone->roll = -ebone_mirr->roll;
+ }
+ }
+ }
+ }
+
+ /* note, notifier might evolve */
+ WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
+
+ return OPERATOR_FINISHED;
+}
+
+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, 0, "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");
+}
+
+/* ******************************** Chain-Based Tools ********************************* */
+
+/* temporary data-structure for merge/fill bones */
+typedef struct EditBonePoint {
+ 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 */
+
+ 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);
+ }
+}
+
+/* --------------------- */
+
+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);
+ }
+}
+
+/* bone adding between selected joints */
+static int armature_fill_bones_exec(bContext *C, wmOperator *op)
+{
+ Object *obedit = CTX_data_edit_object(C);
+ bArmature *arm = (obedit) ? obedit->data : NULL;
+ Scene *scene = CTX_data_scene(C);
+ View3D *v3d = CTX_wm_view3d(C);
+ ListBase points = {NULL, NULL};
+ int count;
+
+ /* sanity checks */
+ if (ELEM(NULL, obedit, arm))
+ return OPERATOR_CANCELLED;
+
+ /* loop over all bones, and only consider if visible */
+ CTX_DATA_BEGIN(C, EditBone *, ebone, visible_bones)
+ {
+ if (!(ebone->flag & BONE_CONNECTED) && (ebone->flag & BONE_ROOTSEL))
+ fill_add_joint(ebone, 0, &points);
+ if (ebone->flag & BONE_TIPSEL)
+ fill_add_joint(ebone, 1, &points);
+ }
+ 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/hierachy)
+ * 3+) error (a smarter method involving finding chains needs to be worked out
+ */
+ count = BLI_countlist(&points);
+
+ if (count == 0) {
+ BKE_report(op->reports, RPT_ERROR, "No joints selected");
+ return OPERATOR_CANCELLED;
+ }
+ else if (count == 1) {
+ EditBonePoint *ebp;
+ float curs[3];
+
+ /* Get Points - selected joint */
+ ebp = (EditBonePoint *)points.first;
+
+ /* Get points - cursor (tail) */
+ invert_m4_m4(obedit->imat, obedit->obmat);
+ mul_v3_m4v3(curs, obedit->imat, give_cursor(scene, v3d));
+
+ /* Create a bone */
+ /* newbone = */ add_points_bone(obedit, ebp->vec, curs);
+ }
+ else if (count == 2) {
+ EditBonePoint *ebp, *ebp2;
+ float head[3], tail[3];
+ short headtail = 0;
+
+ /* check that the points don't belong to the same bone */
+ ebp = (EditBonePoint *)points.first;
+ ebp2 = ebp->next;
+
+ if ((ebp->head_owner == ebp2->tail_owner) && (ebp->head_owner != NULL)) {
+ BKE_report(op->reports, RPT_ERROR, "Same bone selected...");
+ BLI_freelistN(&points);
+ return OPERATOR_CANCELLED;
+ }
+ if ((ebp->tail_owner == ebp2->head_owner) && (ebp->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->head_owner && ebp2->head_owner) || (ebp->tail_owner && ebp2->tail_owner)) {
+ /* rule: whichever one is closer to 3d-cursor */
+ float curs[3];
+ float vecA[3], vecB[3];
+ float distA, distB;
+
+ /* get cursor location */
+ invert_m4_m4(obedit->imat, obedit->obmat);
+ mul_v3_m4v3(curs, obedit->imat, give_cursor(scene, v3d));
+
+ /* get distances */
+ sub_v3_v3v3(vecA, ebp->vec, curs);
+ sub_v3_v3v3(vecB, ebp2->vec, curs);
+ distA = len_v3(vecA);
+ distB = len_v3(vecB);
+
+ /* compare distances - closer one therefore acts as direction for bone to go */
+ headtail = (distA < distB) ? 2 : 1;
+ }
+ else if (ebp->head_owner) {
+ headtail = 1;
+ }
+ else if (ebp2->head_owner) {
+ headtail = 2;
+ }
+
+ /* assign head/tail combinations */
+ if (headtail == 2) {
+ copy_v3_v3(head, ebp->vec);
+ copy_v3_v3(tail, ebp2->vec);
+ }
+ else if (headtail == 1) {
+ copy_v3_v3(head, ebp2->vec);
+ copy_v3_v3(tail, ebp->vec);
+ }
+
+ /* add new bone and parent it to the appropriate end */
+ if (headtail) {
+ EditBone *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->tail_owner)
+ newbone->parent = ebp->tail_owner;
+ else
+ newbone->parent = ebp->head_owner;
+ }
+ else {
+ /* ebp2 tail or head - tail gets priority */
+ if (ebp2->tail_owner)
+ newbone->parent = ebp2->tail_owner;
+ else
+ newbone->parent = ebp2->head_owner;
+ }
+
+ newbone->flag |= BONE_CONNECTED;
+ }
+ }
+ else {
+ /* FIXME.. figure out a method for multiple bones */
+ BKE_reportf(op->reports, RPT_ERROR, "Too many points selected: %d", count);
+ BLI_freelistN(&points);
+ return OPERATOR_CANCELLED;
+ }
+
+ /* 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";
+
+ /* callbacks */
+ ot->exec = armature_fill_bones_exec;
+ ot->poll = ED_operator_editarmature;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+/* --------------------- */
+
+/* this function merges between two bones, removes them and those in-between,
+ * and adjusts the parent relationships for those in-between
+ */
+static void bones_merge(Object *obedit, EditBone *start, EditBone *end, EditBone *endchild, ListBase *chains)
+{
+ bArmature *arm = obedit->data;
+ EditBone *ebo, *ebone, *newbone;
+ LinkData *chain;
+ float head[3], tail[3];
+
+ /* check if same bone */
+ if (start == end) {
+ if (G.debug & G_DEBUG) {
+ printf("Error: same bone!\n");
+ printf("\tstart = %s, end = %s\n", start->name, end->name);
+ }
+ }
+
+ /* step 1: add a new bone
+ * - head = head/tail of start (default head)
+ * - tail = head/tail of end (default tail)
+ * - parent = parent of start
+ */
+ if ((start->flag & BONE_TIPSEL) && (start->flag & BONE_SELECTED) == 0) {
+ copy_v3_v3(head, start->tail);
+ }
+ else {
+ copy_v3_v3(head, start->head);
+ }
+ if ((end->flag & BONE_ROOTSEL) && (end->flag & BONE_SELECTED) == 0) {
+ copy_v3_v3(tail, end->head);
+ }
+ else {
+ copy_v3_v3(tail, end->tail);
+ }
+ newbone = add_points_bone(obedit, head, tail);
+ newbone->parent = start->parent;
+
+ /* TODO, copy more things to the new bone */
+ newbone->flag = start->flag & (BONE_HINGE | BONE_NO_DEFORM | BONE_NO_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_sync_selection(arm->edbo);
+}
+
+
+static int armature_merge_exec(bContext *C, wmOperator *op)
+{
+ Object *obedit = CTX_data_edit_object(C);
+ bArmature *arm = (obedit) ? obedit->data : NULL;
+ short type = RNA_enum_get(op->ptr, "type");
+
+ /* sanity checks */
+ if (ELEM(NULL, obedit, arm))
+ return OPERATOR_CANCELLED;
+
+ /* 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 (chains.first == NULL) return OPERATOR_CANCELLED;
+
+ /* 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 continous 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_sync_selection(arm->edbo);
+ WM_event_add_notifier(C, NC_OBJECT | ND_POSE, obedit);
+
+ return OPERATOR_FINISHED;
+}
+
+void ARMATURE_OT_merge(wmOperatorType *ot)
+{
+ static 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", "");
+}
+
+/* --------------------- */
+
+/* Switch Direction operator:
+ * Currently, this does not use context loops, as context loops do not make it
+ * easy to retrieve any hierarchical/chain relationships which are necessary for
+ * this to be done easily.
+ */
+
+/* helper to clear BONE_TRANSFORM flags */
+static void armature_clear_swap_done_flags(bArmature *arm)
+{
+ EditBone *ebone;
+
+ for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
+ ebone->flag &= ~BONE_TRANSFORM;
+ }
+}
+
+static int armature_switch_direction_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Object *ob = CTX_data_edit_object(C);
+ bArmature *arm = (bArmature *)ob->data;
+ ListBase chains = {NULL, NULL};
+ LinkData *chain;
+
+ /* get chains of bones (ends on chains) */
+ chains_find_tips(arm->edbo, &chains);
+ if (chains.first == NULL) return OPERATOR_CANCELLED;
+
+ /* ensure that mirror bones will also be operated on */
+ armature_tag_select_mirrored(arm);
+
+ /* clear BONE_TRANSFORM flags
+ * - used to prevent duplicate/cancelling 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(float, ebo->head[0], ebo->tail[0]);
+ SWAP(float, ebo->head[1], ebo->tail[1]);
+ SWAP(float, ebo->head[2], ebo->tail[2]);
+
+ /* 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);
+
+ 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)";
+
+ /* api callbacks */
+ ot->exec = armature_switch_direction_exec;
+ ot->poll = ED_operator_editarmature;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+/* ********************************* Align ******************************* */
+
+/* helper to fix a ebone position if its parent has moved due to alignment*/
+static void fix_connected_bone(EditBone *ebone)
+{
+ float diff[3];
+
+ 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);
+ return;
+}
+
+/* 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);
+ }
+ }
+ return;
+}
+
+static void bone_align_to_bone(ListBase *edbo, EditBone *selbone, EditBone *actbone)
+{
+ float selboneaxis[3], actboneaxis[3], length;
+
+ sub_v3_v3v3(actboneaxis, actbone->tail, actbone->head);
+ normalize_v3(actboneaxis);
+
+ 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;
+
+ /* 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);
+ return;
+}
+
+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;
+
+ /* 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_bone_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,
+ * 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);
+ }
+ }
+ 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;
+ }
+
+
+ /* note, notifier might evolve */
+ WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, 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)";
+
+ /* api callbacks */
+ ot->invoke = WM_operator_confirm;
+ ot->exec = armature_align_bones_exec;
+ ot->poll = ED_operator_editarmature;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+/* ********************************* Delete ******************************* */
+
+/* previously delete_armature */
+/* only editmode! */
+static int armature_delete_selected_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ bArmature *arm;
+ EditBone *curBone, *ebone_next;
+ bConstraint *con;
+ Object *obedit = CTX_data_edit_object(C); // XXX get from context
+ arm = obedit->data;
+
+ /* cancel if nothing selected */
+ if (CTX_DATA_COUNT(C, selected_bones) == 0)
+ return OPERATOR_CANCELLED;
+
+ armature_select_mirrored(arm);
+
+ /* First erase any associated pose channel */
+ if (obedit->pose) {
+ bPoseChannel *pchan, *pchan_next;
+ for (pchan = obedit->pose->chanbase.first; pchan; pchan = pchan_next) {
+ pchan_next = pchan->next;
+ curBone = editbone_name_exists(arm->edbo, pchan->name);
+
+ if (curBone && (curBone->flag & BONE_SELECTED) && (arm->layer & curBone->layer)) {
+ BKE_pose_channel_free(pchan);
+ BKE_pose_channels_hash_free(obedit->pose);
+ BLI_freelinkN(&obedit->pose->chanbase, pchan);
+ }
+ else {
+ for (con = pchan->constraints.first; con; con = con->next) {
+ bConstraintTypeInfo *cti = BKE_constraint_get_typeinfo(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) {
+ if (ct->tar == obedit) {
+ if (ct->subtarget[0]) {
+ curBone = editbone_name_exists(arm->edbo, ct->subtarget);
+ if (curBone && (curBone->flag & BONE_SELECTED) && (arm->layer & curBone->layer)) {
+ con->flag |= CONSTRAINT_DISABLE;
+ ct->subtarget[0] = 0;
+ }
+ }
+ }
+ }
+
+ if (cti->flush_constraint_targets)
+ cti->flush_constraint_targets(con, &targets, 0);
+ }
+ }
+ }
+ }
+ }
+
+
+ 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_edit_bone_remove(arm, curBone);
+ }
+ }
+ }
+
+
+ ED_armature_sync_selection(arm->edbo);
+
+ WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit);
+
+ 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;
+}
+
+/* ********************************* Show/Hide ******************************* */
+
+static int armature_hide_exec(bContext *C, wmOperator *op)
+{
+ Object *obedit = CTX_data_edit_object(C);
+ bArmature *arm = obedit->data;
+ EditBone *ebone;
+ 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;
+
+ for (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;
+ }
+ }
+ }
+ ED_armature_validate_active(arm);
+ ED_armature_sync_selection(arm->edbo);
+
+ WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit);
+
+ return OPERATOR_FINISHED;
+}
+
+void ARMATURE_OT_hide(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Hide Selected Bones";
+ 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;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* props */
+ RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected");
+}
+
+static int armature_reveal_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Object *obedit = CTX_data_edit_object(C);
+ bArmature *arm = obedit->data;
+ EditBone *ebone;
+
+ for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
+ if (arm->layer & ebone->layer) {
+ if (ebone->flag & BONE_HIDDEN_A) {
+ ebone->flag |= (BONE_TIPSEL | BONE_SELECTED | BONE_ROOTSEL);
+ ebone->flag &= ~BONE_HIDDEN_A;
+ }
+ }
+ }
+ ED_armature_validate_active(arm);
+ ED_armature_sync_selection(arm->edbo);
+
+ WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit);
+
+ return OPERATOR_FINISHED;
+}
+
+void ARMATURE_OT_reveal(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Reveal Bones";
+ ot->idname = "ARMATURE_OT_reveal";
+ ot->description = "Unhide all bones that have been tagged to be hidden in Edit Mode";
+
+ /* api callbacks */
+ ot->exec = armature_reveal_exec;
+ ot->poll = ED_operator_editarmature;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+}
+#if 0 // remove this?
+static void hide_selected_armature_bones(Scene *scene)
+{
+ Object *obedit = scene->obedit; // XXX get from context
+ bArmature *arm = obedit->data;
+ EditBone *ebone;
+
+ for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
+ if (EBONE_VISIBLE(arm, ebone)) {
+ if (ebone->flag & BONE_SELECTED) {
+ ebone->flag &= ~(BONE_TIPSEL | BONE_SELECTED | BONE_ROOTSEL);
+ ebone->flag |= BONE_HIDDEN_A;
+ }
+ }
+ }
+ ED_armature_validate_active(arm);
+ ED_armature_sync_selection(arm->edbo);
+}
+
+static void hide_unselected_armature_bones(Scene *scene)
+{
+ Object *obedit = scene->obedit; // XXX get from context
+ bArmature *arm = obedit->data;
+ EditBone *ebone;
+
+ for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
+ bArmature *arm = obedit->data;
+ if (EBONE_VISIBLE(arm, ebone)) {
+ if (ebone->flag & (BONE_TIPSEL | BONE_SELECTED | BONE_ROOTSEL)) {
+ /* pass */
+ }
+ else {
+ ebone->flag |= BONE_HIDDEN_A;
+ }
+ }
+ }
+
+ ED_armature_validate_active(arm);
+ ED_armature_sync_selection(arm->edbo);
+}
+
+void show_all_armature_bones(Scene *scene)
+{
+ Object *obedit = scene->obedit; // XXX get from context
+ bArmature *arm = obedit->data;
+ EditBone *ebone;
+
+ for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
+ if (arm->layer & ebone->layer) {
+ if (ebone->flag & BONE_HIDDEN_A) {
+ ebone->flag |= (BONE_TIPSEL | BONE_SELECTED | BONE_ROOTSEL);
+ ebone->flag &= ~BONE_HIDDEN_A;
+ }
+ }
+ }
+ ED_armature_validate_active(arm);
+ ED_armature_sync_selection(arm->edbo);
+}
+#endif
diff --git a/source/blender/editors/armature/armature_intern.h b/source/blender/editors/armature/armature_intern.h
index ddf66a6169b..02ebbe9d1ec 100644
--- a/source/blender/editors/armature/armature_intern.h
+++ b/source/blender/editors/armature/armature_intern.h
@@ -37,17 +37,19 @@ struct wmOperatorType;
struct bContext;
struct Scene;
struct Object;
+struct Base;
struct bAction;
struct bPoseChannel;
struct bArmature;
struct EditBone;
+struct Bone;
struct ListBase;
struct LinkData;
/* ******************************************************* */
-/* editarmature.c operators */
+/* Armature EditMode Operators */
void ARMATURE_OT_bone_primitive_add(struct wmOperatorType *ot);
void ARMATURE_OT_align(struct wmOperatorType *ot);
@@ -131,7 +133,7 @@ void POSE_OT_armature_layers(struct wmOperatorType *ot);
void POSE_OT_bone_layers(struct wmOperatorType *ot);
/* ******************************************************* */
-/* Etch-A-Ton */
+/* Etch-A-Ton (Skeleton Sketching) Operators */
void SKETCH_OT_gesture(struct wmOperatorType *ot);
void SKETCH_OT_delete(struct wmOperatorType *ot);
@@ -203,7 +205,11 @@ void POSE_OT_breakdown(struct wmOperatorType *ot);
void POSE_OT_propagate(struct wmOperatorType *ot);
/* ******************************************************* */
-/* editarmature.c */
+/* Various Armature Edit/Pose Editing API's */
+
+/* Ideally, many of these defines would not be needed as everything would be strictly self-contained
+ * within each file, but some tools still have a bit of overlap which makes things messy -- Feb 2013
+ */
EditBone *make_boneList(struct ListBase *edbo, struct ListBase *bones, struct EditBone *parent, struct Bone *actBone);
void BIF_sk_selectStroke(struct bContext *C, const int mval[2], short extend);
@@ -213,13 +219,29 @@ void preEditBoneDuplicate(struct ListBase *editbones);
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 */
-
+/* 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);
/* editbones is the source list */
void updateDuplicateSubtargetObjects(struct EditBone *dupBone, struct ListBase *editbones, struct Object *src_ob, struct Object *dst_ob);
+
+EditBone *editbone_name_exists(struct ListBase *edbo, const char *name);
+
+EditBone *add_points_bone(struct Object *obedit, float head[3], float tail[3]);
+void bone_free(struct bArmature *arm, struct EditBone *bone);
+
+void armature_tag_select_mirrored(struct bArmature *arm);
+void armature_select_mirrored(struct bArmature *arm);
+void armature_tag_unselect(struct bArmature *arm);
+
+void *get_nearest_bone(struct bContext *C, short findunsel, int x, int y);
+void *get_bone_from_selectbuffer(struct Scene *scene, struct Base *base, unsigned int *buffer, short hits, short findunsel);
+
+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
new file mode 100644
index 00000000000..db2eba8556d
--- /dev/null
+++ b/source/blender/editors/armature/armature_naming.c
@@ -0,0 +1,395 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
+ * All rights reserved.
+ *
+ * Contributor(s): Blender Foundation, 2002-2009 full recode.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ *
+ * Operators and API's for renaming bones both in and out of Edit Mode
+ */
+
+/** \file blender/editors/armature/armature_naming.c
+ * \ingroup edarmature
+ */
+
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <math.h>
+#include <float.h>
+
+
+#include "DNA_anim_types.h"
+#include "DNA_armature_types.h"
+#include "DNA_constraint_types.h"
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_modifier_types.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_blenlib.h"
+#include "BLI_math.h"
+#include "BLI_utildefines.h"
+#include "BLI_ghash.h"
+
+#include "BKE_animsys.h"
+#include "BKE_action.h"
+#include "BKE_armature.h"
+#include "BKE_constraint.h"
+#include "BKE_context.h"
+#include "BKE_deform.h"
+#include "BKE_depsgraph.h"
+#include "BKE_DerivedMesh.h"
+#include "BKE_global.h"
+#include "BKE_idprop.h"
+#include "BKE_main.h"
+#include "BKE_object.h"
+#include "BKE_report.h"
+#include "BKE_modifier.h"
+
+#include "BIF_gl.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "ED_armature.h"
+#include "ED_keyframing.h"
+#include "ED_object.h"
+#include "ED_screen.h"
+#include "ED_util.h"
+#include "ED_view3d.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "armature_intern.h"
+
+/* This file contains functions/API's for renaming bones and/or working with them */
+
+/* ************************************************** */
+/* EditBone Names */
+
+/* checks if an EditBone with a matching name already, returning the matching bone if it exists */
+EditBone *editbone_name_exists(ListBase *edbo, const char *name)
+{
+ return BLI_findstring(edbo, name, offsetof(EditBone, name));
+}
+
+/* note: there's a unique_bone_name() too! */
+static int editbone_unique_check(void *arg, const char *name)
+{
+ struct {ListBase *lb; void *bone; } *data = arg;
+ EditBone *dupli = editbone_name_exists(data->lb, name);
+ return dupli && dupli != data->bone;
+}
+
+void unique_editbone_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, "Bone", '.', name, sizeof(bone->name));
+}
+
+/* ************************************************** */
+/* Bone Renaming - API */
+
+static int bone_unique_check(void *arg, const char *name)
+{
+ return BKE_armature_find_bone_name((bArmature *)arg, name) != NULL;
+}
+
+static void unique_bone_name(bArmature *arm, char *name)
+{
+ BLI_uniquename_cb(bone_unique_check, (void *)arm, "Bone", '.', name, sizeof(((Bone *)NULL)->name));
+}
+
+/* helper call for armature_bone_rename */
+static void constraint_bone_name_fix(Object *ob, ListBase *conlist, char *oldname, char *newname)
+{
+ bConstraint *curcon;
+ bConstraintTarget *ct;
+
+ for (curcon = conlist->first; curcon; curcon = curcon->next) {
+ bConstraintTypeInfo *cti = BKE_constraint_get_typeinfo(curcon);
+ ListBase targets = {NULL, NULL};
+
+ 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 (!strcmp(ct->subtarget, oldname) )
+ BLI_strncpy(ct->subtarget, newname, MAXBONENAME);
+ }
+ }
+
+ if (cti->flush_constraint_targets)
+ cti->flush_constraint_targets(curcon, &targets, 0);
+ }
+ }
+}
+
+/* called by UI for renaming a bone */
+/* warning: make sure the original bone was not renamed yet! */
+/* seems messy, but thats what you get with not using pointers but channel names :) */
+void ED_armature_bone_rename(bArmature *arm, const char *oldnamep, const char *newnamep)
+{
+ Object *ob;
+ char newname[MAXBONENAME];
+ char oldname[MAXBONENAME];
+
+ /* names better differ! */
+ if (strncmp(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 = editbone_name_exists(arm->edbo, oldname);
+
+ if (eBone) {
+ unique_editbone_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;
+ }
+
+ /* do entire dbase - objects */
+ for (ob = G.main->object.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) {
+ BLI_strncpy(pchan->name, newname, MAXBONENAME);
+
+ if (ob->pose->chanhash) {
+ GHash *gh = ob->pose->chanhash;
+
+ /* remove the old hash entry, and replace with the new name */
+ BLI_ghash_remove(gh, oldname, NULL, NULL);
+ BLI_ghash_insert(gh, pchan->name, pchan);
+ }
+ }
+ }
+
+ /* Update any object constraints to use the new bone name */
+ for (cob = G.main->object.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 (!strcmp(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) {
+ if (md->type == eModifierType_Hook) {
+ HookModifierData *hmd = (HookModifierData *)md;
+
+ /* uses armature, so may use the affected bone name */
+ if (hmd->object && (hmd->object->data == arm)) {
+ if (!strcmp(hmd->subtarget, oldname))
+ BLI_strncpy(hmd->subtarget, newname, MAXBONENAME);
+ }
+ }
+ }
+ }
+
+ /* 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]
+ */
+ {
+
+ BKE_all_animdata_fix_paths_rename(&arm->id, "pose.bones", oldname, newname);
+ }
+
+ /* correct view locking */
+ {
+ bScreen *screen;
+ for (screen = G.main->screen.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 (!strcmp(v3d->ob_centre_bone, oldname)) {
+ BLI_strncpy(v3d->ob_centre_bone, newname, MAXBONENAME);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+/* ************************************************** */
+/* Bone Renaming - EditMode */
+
+static int armature_flip_names_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Object *ob = CTX_data_edit_object(C);
+ bArmature *arm;
+ char newname[MAXBONENAME];
+
+ /* paranoia checks */
+ if (ELEM(NULL, ob, ob->pose))
+ return OPERATOR_CANCELLED;
+ arm = ob->data;
+
+ /* loop through selected bones, auto-naming them */
+ CTX_DATA_BEGIN(C, EditBone *, ebone, selected_editable_bones)
+ {
+ flip_side_name(newname, ebone->name, TRUE); // 1 = do strip off number extensions
+ ED_armature_bone_rename(arm, ebone->name, newname);
+ }
+ CTX_DATA_END;
+
+ /* since we renamed stuff... */
+ DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
+
+ /* note, notifier might evolve */
+ WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
+
+ 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;
+}
+
+
+static int armature_autoside_names_exec(bContext *C, wmOperator *op)
+{
+ Object *ob = CTX_data_edit_object(C);
+ bArmature *arm;
+ char newname[MAXBONENAME];
+ short axis = RNA_enum_get(op->ptr, "type");
+
+ /* paranoia checks */
+ if (ELEM(NULL, ob, ob->pose))
+ return OPERATOR_CANCELLED;
+ arm = ob->data;
+
+ /* loop through selected bones, auto-naming them */
+ CTX_DATA_BEGIN(C, EditBone *, ebone, selected_editable_bones)
+ {
+ BLI_strncpy(newname, ebone->name, sizeof(newname));
+ if (bone_autoside_name(newname, 1, axis, ebone->head[axis], ebone->tail[axis]))
+ ED_armature_bone_rename(arm, ebone->name, newname);
+ }
+ CTX_DATA_END;
+
+ /* since we renamed stuff... */
+ DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
+
+ /* note, notifier might evolve */
+ WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
+
+ return OPERATOR_FINISHED;
+}
+
+void ARMATURE_OT_autoside_names(wmOperatorType *ot)
+{
+ static 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_relations.c b/source/blender/editors/armature/armature_relations.c
new file mode 100644
index 00000000000..52a018be21e
--- /dev/null
+++ b/source/blender/editors/armature/armature_relations.c
@@ -0,0 +1,800 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
+ * All rights reserved.
+ *
+ * Contributor(s): Blender Foundation, 2002-2009 full recode.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ *
+ * Operators for relations between bones and for transferring bones between armature objects
+ */
+
+/** \file blender/editors/armature/armature_relations.c
+ * \ingroup edarmature
+ */
+
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <math.h>
+#include <float.h>
+
+
+#include "DNA_anim_types.h"
+#include "DNA_armature_types.h"
+#include "DNA_constraint_types.h"
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_modifier_types.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_blenlib.h"
+#include "BLI_math.h"
+#include "BLI_utildefines.h"
+#include "BLI_ghash.h"
+
+#include "BLF_translation.h"
+
+#include "BKE_animsys.h"
+#include "BKE_action.h"
+#include "BKE_armature.h"
+#include "BKE_constraint.h"
+#include "BKE_context.h"
+#include "BKE_deform.h"
+#include "BKE_depsgraph.h"
+#include "BKE_DerivedMesh.h"
+#include "BKE_global.h"
+#include "BKE_idprop.h"
+#include "BKE_main.h"
+#include "BKE_object.h"
+#include "BKE_report.h"
+#include "BKE_modifier.h"
+
+#include "BIF_gl.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "ED_armature.h"
+#include "ED_keyframing.h"
+#include "ED_object.h"
+#include "ED_screen.h"
+#include "ED_util.h"
+#include "ED_view3d.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "armature_intern.h"
+
+/* *************************************** Join *************************************** */
+/* NOTE: no operator define here as this is exported to the Object-level operator */
+
+/* Helper function for armature joining - link fixing */
+static void joined_armature_fix_links(Object *tarArm, Object *srcArm, bPoseChannel *pchan, EditBone *curbone)
+{
+ Object *ob;
+ bPose *pose;
+ bPoseChannel *pchant;
+ bConstraint *con;
+
+ /* let's go through all objects in database */
+ for (ob = G.main->object.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) {
+ for (con = pchant->constraints.first; con; con = con->next) {
+ bConstraintTypeInfo *cti = BKE_constraint_get_typeinfo(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 (strcmp(ct->subtarget, pchan->name) == 0) {
+ 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? */
+ if (con->type == CONSTRAINT_TYPE_ACTION) {
+ bActionConstraint *data = con->data; // XXX old animation system
+ bAction *act;
+ bActionChannel *achan;
+
+ if (data->act) {
+ act = data->act;
+
+ for (achan = act->chanbase.first; achan; achan = achan->next) {
+ if (strcmp(achan->name, pchan->name) == 0)
+ BLI_strncpy(achan->name, curbone->name, sizeof(achan->name));
+ }
+ }
+ }
+
+ }
+ }
+ }
+
+ /* fix object-level constraints */
+ if (ob != srcArm) {
+ for (con = ob->constraints.first; con; con = con->next) {
+ bConstraintTypeInfo *cti = BKE_constraint_get_typeinfo(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 (strcmp(ct->subtarget, pchan->name) == 0) {
+ ct->tar = tarArm;
+ BLI_strncpy(ct->subtarget, curbone->name, sizeof(ct->subtarget));
+ }
+ }
+ }
+
+ 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 == srcArm)) {
+ /* Is object parented to a bone of this src armature? */
+ if (ob->partype == PARBONE) {
+ /* bone name in object */
+ if (!strcmp(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 *UNUSED(op))
+{
+ Main *bmain = CTX_data_main(C);
+ Scene *scene = CTX_data_scene(C);
+ Object *ob = CTX_data_active_object(C);
+ bArmature *arm = (ob) ? ob->data : NULL;
+ bPose *pose, *opose;
+ bPoseChannel *pchan, *pchann;
+ EditBone *curbone;
+ float mat[4][4], oimat[4][4];
+
+ /* Ensure we're not in editmode and that the active object is an armature*/
+ if (!ob || ob->type != OB_ARMATURE)
+ return OPERATOR_CANCELLED;
+ if (!arm || arm->edbo)
+ return OPERATOR_CANCELLED;
+
+ /* Get editbones of active armature to add editbones to */
+ ED_armature_to_edit(ob);
+
+ /* get pose of active object and move it out of posemode */
+ pose = ob->pose;
+ ob->mode &= ~OB_MODE_POSE;
+
+ CTX_DATA_BEGIN(C, Base *, base, selected_editable_bases)
+ {
+ if ((base->object->type == OB_ARMATURE) && (base->object != ob)) {
+ bArmature *curarm = base->object->data;
+
+ /* Make a list of editbones in current armature */
+ ED_armature_to_edit(base->object);
+
+ /* Get Pose of current armature */
+ opose = base->object->pose;
+ base->object->mode &= ~OB_MODE_POSE;
+ //BASACT->flag &= ~OB_MODE_POSE;
+
+ /* Find the difference matrix */
+ invert_m4_m4(oimat, ob->obmat);
+ mult_m4_m4m4(mat, oimat, base->object->obmat);
+
+ /* Copy bones and posechannels from the object to the edit armature */
+ for (pchan = opose->chanbase.first; pchan; pchan = pchann) {
+ pchann = pchan->next;
+ curbone = editbone_name_exists(curarm->edbo, pchan->name);
+
+ /* Get new name */
+ unique_editbone_name(arm->edbo, curbone->name, NULL);
+
+ /* Transform the bone */
+ {
+ float premat[4][4];
+ float postmat[4][4];
+ float difmat[4][4];
+ float imat[4][4];
+ float temp[3][3];
+ float delta[3];
+
+ /* Get the premat */
+ sub_v3_v3v3(delta, curbone->tail, curbone->head);
+ vec_roll_to_mat3(delta, curbone->roll, temp);
+
+ unit_m4(premat); /* Mat4MulMat34 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 */
+ sub_v3_v3v3(delta, curbone->tail, curbone->head);
+ vec_roll_to_mat3(delta, curbone->roll, temp);
+ copy_m4_m3(postmat, temp);
+
+ /* Find the roll */
+ invert_m4_m4(imat, premat);
+ mult_m4_m4m4(difmat, imat, postmat);
+
+ curbone->roll -= (float)atan2(difmat[2][0], difmat[2][2]);
+ }
+
+ /* Fix Constraints and Other Links to this Bone and Armature */
+ joined_armature_fix_links(ob, base->object, 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);
+ }
+
+ ED_base_object_free_and_unlink(bmain, scene, base);
+ }
+ }
+ CTX_DATA_END;
+
+ DAG_relations_tag_update(bmain); /* because we removed object(s) */
+
+ ED_armature_from_edit(ob);
+ ED_armature_edit_free(ob);
+
+ WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene);
+
+ return OPERATOR_FINISHED;
+}
+
+/* *********************************** Separate *********************************************** */
+
+/* Helper function for armature separating - link fixing */
+static void separated_armature_fix_links(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 = G.main->object.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) {
+ bConstraintTypeInfo *cti = BKE_constraint_get_typeinfo(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) {
+ bConstraintTypeInfo *cti = BKE_constraint_get_typeinfo(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
+ * sel: remove selected bones from the armature, otherwise the unselected bones are removed
+ * (ob is not in editmode)
+ */
+static void separate_armature_bones(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(ob);
+
+ /* go through pose-channels, checking if a bone should be removed */
+ for (pchan = ob->pose->chanbase.first; pchan; pchan = pchann) {
+ pchann = pchan->next;
+ curbone = editbone_name_exists(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;
+ ebo->temp = NULL; /* this is needed to prevent random crashes with in ED_armature_from_edit */
+ 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;
+ }
+
+ /* 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(ob);
+ ED_armature_edit_free(ob);
+}
+
+/* separate selected bones into their armature */
+static int separate_armature_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Main *bmain = CTX_data_main(C);
+ Scene *scene = CTX_data_scene(C);
+ Object *obedit = CTX_data_edit_object(C);
+ Object *oldob, *newob;
+ Base *oldbase, *newbase;
+
+ /* sanity checks */
+ if (obedit == NULL)
+ return OPERATOR_CANCELLED;
+
+ /* set wait cursor in case this takes a while */
+ WM_cursor_wait(1);
+
+ /* 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 */
+ /* TODO: use context iterators for this? */
+ CTX_DATA_BEGIN(C, Base *, base, visible_bases)
+ {
+ if (base->object == obedit) base->flag |= 1;
+ else base->flag &= ~1;
+ }
+ CTX_DATA_END;
+
+ /* 1) store starting settings and exit editmode */
+ oldob = obedit;
+ oldbase = BASACT;
+ oldob->mode &= ~OB_MODE_POSE;
+ //oldbase->flag &= ~OB_POSEMODE;
+
+ ED_armature_from_edit(obedit);
+ ED_armature_edit_free(obedit);
+
+ /* 2) duplicate base */
+ newbase = ED_object_add_duplicate(bmain, scene, oldbase, USER_DUP_ARM); /* only duplicate linked armature */
+ DAG_relations_tag_update(bmain);
+
+ newob = newbase->object;
+ newbase->flag &= ~SELECT;
+
+
+ /* 3) remove bones that shouldn't still be around on both armatures */
+ separate_armature_bones(oldob, 1);
+ separate_armature_bones(newob, 0);
+
+
+ /* 4) fix links before depsgraph flushes */ // err... or after?
+ separated_armature_fix_links(oldob, newob);
+
+ DAG_id_tag_update(&oldob->id, OB_RECALC_DATA); /* this is the original one */
+ DAG_id_tag_update(&newob->id, OB_RECALC_DATA); /* this is the separated one */
+
+
+ /* 5) restore original conditions */
+ obedit = oldob;
+
+ ED_armature_to_edit(obedit);
+
+ /* note, notifier might evolve */
+ WM_event_add_notifier(C, NC_OBJECT | ND_POSE, obedit);
+
+ /* recalc/redraw + cleanup */
+ WM_cursor_wait(0);
+
+ 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;
+}
+
+/* ******************************************** Parenting ************************************************* */
+
+/* armature parenting options */
+#define ARM_PAR_CONNECT 1
+#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;
+}
+
+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;
+ }
+}
+
+
+static EnumPropertyItem prop_editarm_make_parent_types[] = {
+ {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_bone_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), wmEvent *UNUSED(event))
+{
+ EditBone *actbone = CTX_data_active_bone(C);
+ uiPopupMenu *pup = uiPupMenuBegin(C, CTX_IFACE_(BLF_I18NCONTEXT_OPERATOR_DEFAULT, "Make Parent"), ICON_NONE);
+ uiLayout *layout = uiPupMenuLayout(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;
+
+ 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);
+
+ uiPupMenuEnd(C, pup);
+
+ return OPERATOR_CANCELLED;
+}
+
+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";
+
+ /* 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;
+
+ RNA_def_enum(ot->srna, "type", prop_editarm_make_parent_types, 0, "ParentType", "Type of parenting");
+}
+
+
+
+static EnumPropertyItem prop_editarm_clear_parent_types[] = {
+ {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;
+}
+
+static int armature_parent_clear_exec(bContext *C, wmOperator *op)
+{
+ Object *ob = CTX_data_edit_object(C);
+ bArmature *arm = (bArmature *)ob->data;
+ 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;
+
+ ED_armature_sync_selection(arm->edbo);
+
+ /* note, notifier might evolve */
+ WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob);
+
+ 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");
+}
+
diff --git a/source/blender/editors/armature/armature_select.c b/source/blender/editors/armature/armature_select.c
new file mode 100644
index 00000000000..493a681bb08
--- /dev/null
+++ b/source/blender/editors/armature/armature_select.c
@@ -0,0 +1,951 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
+ * All rights reserved.
+ *
+ * Contributor(s): Blender Foundation, 2002-2009 full recode.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ *
+ * API's and Operators for selecting armature bones in EditMode
+ */
+
+/** \file blender/editors/armature/armature_select.c
+ * \ingroup edarmature
+ */
+
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <math.h>
+#include <float.h>
+#include <assert.h>
+
+
+#include "DNA_anim_types.h"
+#include "DNA_armature_types.h"
+#include "DNA_constraint_types.h"
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_blenlib.h"
+#include "BLI_math.h"
+#include "BLI_utildefines.h"
+#include "BLI_ghash.h"
+
+#include "BKE_animsys.h"
+#include "BKE_action.h"
+#include "BKE_armature.h"
+#include "BKE_constraint.h"
+#include "BKE_context.h"
+#include "BKE_deform.h"
+#include "BKE_depsgraph.h"
+#include "BKE_DerivedMesh.h"
+#include "BKE_global.h"
+#include "BKE_idprop.h"
+#include "BKE_main.h"
+#include "BKE_object.h"
+#include "BKE_report.h"
+
+#include "BIF_gl.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "ED_armature.h"
+#include "ED_object.h"
+#include "ED_screen.h"
+#include "ED_util.h"
+#include "ED_view3d.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "armature_intern.h"
+
+/* **************** PoseMode & EditMode Selection Buffer Queries *************************** */
+
+/* only for opengl selection indices */
+Bone *get_indexed_bone(Object *ob, int index)
+{
+ bPoseChannel *pchan;
+ if (ob->pose == NULL) return NULL;
+ index >>= 16; // bone selection codes use left 2 bytes
+
+ pchan = BLI_findlink(&ob->pose->chanbase, index);
+ return pchan ? pchan->bone : NULL;
+}
+
+/* See if there are any selected bones in this buffer */
+/* only bones from base are checked on */
+void *get_bone_from_selectbuffer(Scene *scene, Base *base, unsigned int *buffer, short hits, short findunsel)
+{
+ Object *obedit = scene->obedit; // XXX get from context
+ Bone *bone;
+ EditBone *ebone;
+ void *firstunSel = NULL, *firstSel = NULL, *data;
+ unsigned int hitresult;
+ short i, takeNext = 0, sel;
+
+ for (i = 0; i < hits; i++) {
+ hitresult = buffer[3 + (i * 4)];
+
+ if (!(hitresult & BONESEL_NOSEL)) { // -1
+ if (hitresult & BONESEL_ANY) { // to avoid including objects in selection
+
+ hitresult &= ~(BONESEL_ANY);
+ /* Determine what the current bone is */
+ if (obedit == NULL || base->object != obedit) {
+ /* no singular posemode, so check for correct object */
+ if (base->selcol == (hitresult & 0xFFFF)) {
+ bone = get_indexed_bone(base->object, hitresult);
+
+ if (findunsel)
+ sel = (bone->flag & BONE_SELECTED);
+ else
+ sel = !(bone->flag & BONE_SELECTED);
+
+ data = bone;
+ }
+ else {
+ data = NULL;
+ sel = 0;
+ }
+ }
+ else {
+ bArmature *arm = obedit->data;
+
+ ebone = BLI_findlink(arm->edbo, hitresult);
+ if (findunsel)
+ sel = (ebone->flag & BONE_SELECTED);
+ else
+ sel = !(ebone->flag & BONE_SELECTED);
+
+ data = ebone;
+ }
+
+ if (data) {
+ if (sel) {
+ if (!firstSel) firstSel = data;
+ takeNext = 1;
+ }
+ else {
+ if (!firstunSel)
+ firstunSel = data;
+ if (takeNext)
+ return data;
+ }
+ }
+ }
+ }
+ }
+
+ if (firstunSel)
+ return firstunSel;
+ else
+ 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, short findunsel, int x, int y)
+{
+ ViewContext vc;
+ rcti rect;
+ unsigned int buffer[MAXPICKBUF];
+ short hits;
+
+ view3d_set_viewcontext(C, &vc);
+
+ // rect.xmin = ... mouseco!
+ rect.xmin = rect.xmax = x;
+ rect.ymin = rect.ymax = y;
+
+ glInitNames();
+ hits = view3d_opengl_select(&vc, buffer, MAXPICKBUF, &rect);
+
+ if (hits > 0)
+ return get_bone_from_selectbuffer(vc.scene, vc.scene->basact, buffer, hits, findunsel);
+
+ return NULL;
+}
+
+/* **************** EditMode stuff ********************** */
+
+/* called in space.c */
+/* previously "selectconnected_armature" */
+static int armature_select_linked_invoke(bContext *C, wmOperator *op, wmEvent *event)
+{
+ bArmature *arm;
+ EditBone *bone, *curBone, *next;
+ int extend = RNA_boolean_get(op->ptr, "extend");
+ Object *obedit = CTX_data_edit_object(C);
+ arm = obedit->data;
+
+ view3d_operator_needs_opengl(C);
+
+ if (extend)
+ bone = get_nearest_bone(C, 0, event->mval[0], event->mval[1]);
+ else
+ bone = get_nearest_bone(C, 1, event->mval[0], event->mval[1]);
+
+ if (!bone)
+ return OPERATOR_CANCELLED;
+
+ /* Select parents */
+ for (curBone = bone; curBone; curBone = next) {
+ if ((curBone->flag & BONE_UNSELECTABLE) == 0) {
+ if (extend) {
+ 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 (extend)
+ 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_sync_selection(arm->edbo);
+
+ WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit);
+
+ return OPERATOR_FINISHED;
+}
+
+static int armature_select_linked_poll(bContext *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";
+
+ /* api callbacks */
+ ot->exec = NULL;
+ ot->invoke = armature_select_linked_invoke;
+ ot->poll = armature_select_linked_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_boolean(ot->srna, "extend", FALSE, "Extend", "Extend selection instead of deselecting everything first");
+}
+
+/* does bones and points */
+/* note that BONE ROOT only gets drawn for root bones (or without IK) */
+static EditBone *get_nearest_editbonepoint(ViewContext *vc, const int mval[2],
+ ListBase *edbo, int findunsel, int *selmask)
+{
+ EditBone *ebone;
+ rcti rect;
+ unsigned int buffer[MAXPICKBUF];
+ unsigned int hitresult, besthitresult = BONESEL_NOSEL;
+ int i, mindep = 4;
+ short hits;
+
+ glInitNames();
+
+ rect.xmin = mval[0] - 5;
+ rect.xmax = mval[0] + 5;
+ rect.ymin = mval[1] - 5;
+ rect.ymax = mval[1] + 5;
+
+ hits = view3d_opengl_select(vc, buffer, MAXPICKBUF, &rect);
+ if (hits == 0) {
+ rect.xmin = mval[0] - 12;
+ rect.xmax = mval[0] + 12;
+ rect.ymin = mval[1] - 12;
+ rect.ymax = mval[1] + 12;
+ hits = view3d_opengl_select(vc, buffer, MAXPICKBUF, &rect);
+ }
+ /* See if there are any selected bones in this group */
+ if (hits > 0) {
+
+ if (hits == 1) {
+ if (!(buffer[3] & BONESEL_NOSEL))
+ besthitresult = buffer[3];
+ }
+ else {
+ for (i = 0; i < hits; i++) {
+ hitresult = buffer[3 + (i * 4)];
+ if (!(hitresult & BONESEL_NOSEL)) {
+ int dep;
+
+ ebone = BLI_findlink(edbo, hitresult & ~BONESEL_ANY);
+
+ /* 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 = 2;
+ }
+ else {
+ /* bone found */
+ if (findunsel) {
+ if ((ebone->flag & BONE_SELECTED) == 0)
+ dep = 2;
+ else
+ dep = 3;
+ }
+ else dep = 3;
+ }
+ if (dep < mindep) {
+ mindep = dep;
+ besthitresult = hitresult;
+ }
+ }
+ }
+ }
+
+ if (!(besthitresult & BONESEL_NOSEL)) {
+
+ ebone = BLI_findlink(edbo, besthitresult & ~BONESEL_ANY);
+
+ *selmask = 0;
+ if (besthitresult & BONESEL_ROOT)
+ *selmask |= BONE_ROOTSEL;
+ if (besthitresult & BONESEL_TIP)
+ *selmask |= BONE_TIPSEL;
+ if (besthitresult & BONESEL_BONE)
+ *selmask |= BONE_SELECTED;
+ return ebone;
+ }
+ }
+ *selmask = 0;
+ return NULL;
+}
+
+
+
+/* toggle==0: deselect
+ * toggle==1: swap (based on test)
+ * toggle==2: swap (no test), CURRENTLY UNUSED
+ */
+void ED_armature_deselect_all(Object *obedit, int toggle)
+{
+ bArmature *arm = obedit->data;
+ EditBone *eBone;
+ int sel = 1;
+
+ if (toggle == 1) {
+ /* Determine if there are any selected bones
+ * and therefore whether we are selecting or deselecting */
+ for (eBone = arm->edbo->first; eBone; eBone = eBone->next) {
+ // if (arm->layer & eBone->layer) {
+ if (eBone->flag & (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL)) {
+ sel = 0;
+ break;
+ }
+ // }
+ }
+ }
+ else sel = toggle;
+
+ /* Set the flags */
+ for (eBone = arm->edbo->first; eBone; eBone = eBone->next) {
+ if (sel == 2) {
+ /* invert selection of bone */
+ if (EBONE_VISIBLE(arm, eBone)) {
+ eBone->flag ^= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
+ if (arm->act_edbone == eBone)
+ arm->act_edbone = NULL;
+ }
+ }
+ else if (sel == 1) {
+ /* select bone */
+ if (EBONE_VISIBLE(arm, eBone)) {
+ eBone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
+ if (eBone->parent)
+ eBone->parent->flag |= (BONE_TIPSEL);
+ }
+ }
+ else {
+ /* deselect bone */
+ eBone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
+ if (arm->act_edbone == eBone)
+ arm->act_edbone = NULL;
+ }
+ }
+
+ ED_armature_sync_selection(arm->edbo);
+}
+
+void ED_armature_deselect_all_visible(Object *obedit)
+{
+ bArmature *arm = obedit->data;
+ EditBone *ebone;
+
+ for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
+ /* first and foremost, bone must be visible and selected */
+ if (EBONE_SELECTABLE(arm, ebone)) {
+ ebone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
+ }
+ }
+
+ ED_armature_sync_selection(arm->edbo);
+}
+
+/* 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);
+ }
+}
+
+/* context: editmode armature in view3d */
+int mouse_armature(bContext *C, const int mval[2], int extend, int deselect, int toggle)
+{
+ Object *obedit = CTX_data_edit_object(C);
+ bArmature *arm = obedit->data;
+ ViewContext vc;
+ EditBone *nearBone = NULL;
+ int selmask;
+
+ view3d_set_viewcontext(C, &vc);
+
+ BIF_sk_selectStroke(C, mval, extend);
+
+ nearBone = get_nearest_editbonepoint(&vc, mval, arm->edbo, 1, &selmask);
+ if (nearBone) {
+
+ if (!extend && !deselect && !toggle)
+ ED_armature_deselect_all(obedit, 0);
+
+ /* 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_sync_selection(arm->edbo);
+
+ if (nearBone) {
+ /* then now check for active status */
+ if (ebone_select_flag(nearBone)) {
+ arm->act_edbone = nearBone;
+ }
+ }
+
+ WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, vc.obedit);
+ return 1;
+ }
+
+ return 0;
+}
+
+
+/* **************** Selections ******************/
+
+static int armature_select_inverse_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ /* Set the flags */
+ CTX_DATA_BEGIN(C, EditBone *, ebone, visible_bones)
+ {
+ /* ignore bone if selection can't change */
+ if ((ebone->flag & BONE_UNSELECTABLE) == 0) {
+ /* select bone */
+ ebone->flag ^= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
+ }
+ }
+ CTX_DATA_END;
+
+ WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void ARMATURE_OT_select_inverse(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Select Inverse";
+ ot->idname = "ARMATURE_OT_select_inverse";
+ ot->description = "Flip the selection status of bones (selected -> unselected, unselected -> selected)";
+
+ /* api callbacks */
+ ot->exec = armature_select_inverse_exec;
+ ot->poll = ED_operator_editarmature;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+}
+static int armature_de_select_all_exec(bContext *C, wmOperator *op)
+{
+ int action = RNA_enum_get(op->ptr, "action");
+
+ if (action == SEL_TOGGLE) {
+ action = SEL_SELECT;
+ /* Determine if there are any selected bones
+ * And therefore whether we are selecting or deselecting */
+ if (CTX_DATA_COUNT(C, selected_bones) > 0)
+ action = SEL_DESELECT;
+ }
+
+ /* Set the flags */
+ CTX_DATA_BEGIN(C, EditBone *, ebone, visible_bones)
+ {
+ /* ignore bone if selection can't change */
+ if ((ebone->flag & BONE_UNSELECTABLE) == 0) {
+ switch (action) {
+ case SEL_SELECT:
+ 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 {
+ 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";
+
+ /* api callbacks */
+ ot->exec = armature_de_select_all_exec;
+ ot->poll = ED_operator_editarmature;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ WM_operator_properties_select_all(ot);
+}
+
+enum {
+ SIMEDBONE_LENGTH = 1,
+ SIMEDBONE_DIRECTION,
+ SIMEDBONE_PREFIX,
+ SIMEDBONE_SUFFIX,
+ SIMEDBONE_LAYER
+};
+
+static EnumPropertyItem prop_similar_types[] = {
+ {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", ""},
+ {0, NULL, 0, NULL, NULL}
+};
+
+/* could be used in more places */
+static void ED_armature_edit_bone_select(EditBone *ebone)
+{
+ BLI_assert((ebone->flag & BONE_UNSELECTABLE) == 0);
+ ebone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
+
+ if ((ebone->flag & BONE_CONNECTED) && (ebone->parent != NULL)) {
+ ebone->parent->flag |= BONE_TIPSEL;
+ }
+}
+
+static void select_similar_length(bArmature *arm, EditBone *ebone_act, const float thresh)
+{
+ EditBone *ebone;
+
+ /* thresh is always relative to current length */
+ const float len_min = ebone_act->length / (1.0f + thresh);
+ const float len_max = ebone_act->length * (1.0f + thresh);
+
+ for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
+ if (EBONE_SELECTABLE(arm, ebone)) {
+ if ((ebone->length >= len_min) &&
+ (ebone->length <= len_max))
+ {
+ ED_armature_edit_bone_select(ebone);
+ }
+ }
+ }
+}
+
+static void select_similar_direction(bArmature *arm, EditBone *ebone_act, const float thresh)
+{
+ EditBone *ebone;
+ float dir_act[3];
+ sub_v3_v3v3(dir_act, ebone_act->head, ebone_act->tail);
+
+ for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
+ if (EBONE_SELECTABLE(arm, ebone)) {
+ float dir[3];
+ sub_v3_v3v3(dir, ebone->head, ebone->tail);
+
+ if (angle_v3v3(dir_act, dir) / (float)M_PI < thresh) {
+ ED_armature_edit_bone_select(ebone);
+ }
+ }
+ }
+}
+
+static void select_similar_layer(bArmature *arm, EditBone *ebone_act)
+{
+ EditBone *ebone;
+
+ for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
+ if (EBONE_SELECTABLE(arm, ebone)) {
+ if (ebone->layer & ebone_act->layer) {
+ ED_armature_edit_bone_select(ebone);
+ }
+ }
+ }
+}
+
+static void select_similar_prefix(bArmature *arm, EditBone *ebone_act)
+{
+ EditBone *ebone;
+
+ char body_tmp[MAX_VGROUP_NAME];
+ char prefix_act[MAX_VGROUP_NAME];
+
+ BKE_deform_split_prefix(ebone_act->name, prefix_act, body_tmp);
+
+ if (prefix_act[0] == '\0')
+ return;
+
+ /* Find matches */
+ for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
+ if (EBONE_SELECTABLE(arm, ebone)) {
+ char prefix_other[MAX_VGROUP_NAME];
+ BKE_deform_split_prefix(ebone->name, prefix_other, body_tmp);
+ if (!strcmp(prefix_act, prefix_other)) {
+ ED_armature_edit_bone_select(ebone);
+ }
+ }
+ }
+}
+
+static void select_similar_suffix(bArmature *arm, EditBone *ebone_act)
+{
+ EditBone *ebone;
+
+ char body_tmp[MAX_VGROUP_NAME];
+ char suffix_act[MAX_VGROUP_NAME];
+
+ BKE_deform_split_suffix(ebone_act->name, body_tmp, suffix_act);
+
+ if (suffix_act[0] == '\0')
+ return;
+
+ /* Find matches */
+ for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
+ if (EBONE_SELECTABLE(arm, ebone)) {
+ char suffix_other[MAX_VGROUP_NAME];
+ BKE_deform_split_suffix(ebone->name, body_tmp, suffix_other);
+ if (!strcmp(suffix_act, suffix_other)) {
+ ED_armature_edit_bone_select(ebone);
+ }
+ }
+ }
+}
+
+static int armature_select_similar_exec(bContext *C, wmOperator *op)
+{
+ Object *obedit = CTX_data_edit_object(C);
+ bArmature *arm = obedit->data;
+ EditBone *ebone_act = CTX_data_active_bone(C);
+
+ /* Get props */
+ int type = RNA_enum_get(op->ptr, "type");
+ float thresh = RNA_float_get(op->ptr, "threshold");
+
+ /* Check for active bone */
+ if (ebone_act == NULL) {
+ BKE_report(op->reports, RPT_ERROR, "Operation requires an active bone");
+ return OPERATOR_CANCELLED;
+ }
+
+ switch (type) {
+ case SIMEDBONE_LENGTH:
+ select_similar_length(arm, ebone_act, thresh);
+ break;
+ case SIMEDBONE_DIRECTION:
+ select_similar_direction(arm, ebone_act, thresh);
+ break;
+ case SIMEDBONE_PREFIX:
+ select_similar_prefix(arm, ebone_act);
+ break;
+ case SIMEDBONE_SUFFIX:
+ select_similar_suffix(arm, ebone_act);
+ break;
+ case SIMEDBONE_LAYER:
+ select_similar_layer(arm, ebone_act);
+ break;
+ }
+
+ WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit);
+
+ 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, 0, "Type", "");
+ RNA_def_float(ot->srna, "threshold", 0.1f, 0.0f, 1.0f, "Threshold", "", 0.0f, 1.0f);
+}
+
+/* ********************* select hierarchy operator ************** */
+
+/* Get the first available child of an editbone */
+static EditBone *editbone_get_child(bArmature *arm, EditBone *pabone, short use_visibility)
+{
+ EditBone *curbone, *chbone = NULL;
+
+ for (curbone = arm->edbo->first; curbone; curbone = curbone->next) {
+ if (curbone->parent == pabone) {
+ if (use_visibility) {
+ if ((arm->layer & curbone->layer) && !(pabone->flag & BONE_HIDDEN_A)) {
+ chbone = curbone;
+ }
+ }
+ else
+ chbone = curbone;
+ }
+ }
+
+ return chbone;
+}
+
+static int armature_select_hierarchy_exec(bContext *C, wmOperator *op)
+{
+ Object *obedit = CTX_data_edit_object(C);
+ Object *ob;
+ bArmature *arm;
+ EditBone *curbone, *pabone, *chbone;
+ int direction = RNA_enum_get(op->ptr, "direction");
+ int add_to_sel = RNA_boolean_get(op->ptr, "extend");
+
+ ob = obedit;
+ arm = (bArmature *)ob->data;
+
+ for (curbone = arm->edbo->first; curbone; curbone = curbone->next) {
+ /* only work on bone if it is visible and its selection can change */
+ if (EBONE_SELECTABLE(arm, curbone)) {
+ if (curbone == arm->act_edbone) {
+ if (direction == BONE_SELECT_PARENT) {
+ if (curbone->parent == NULL) continue;
+ else pabone = curbone->parent;
+
+ if (EBONE_VISIBLE(arm, pabone)) {
+ pabone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
+ arm->act_edbone = pabone;
+ if (pabone->parent) pabone->parent->flag |= BONE_TIPSEL;
+
+ if (!add_to_sel) curbone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
+ break;
+ }
+
+ }
+ else { // BONE_SELECT_CHILD
+ chbone = editbone_get_child(arm, curbone, 1);
+ if (chbone == NULL) continue;
+
+ if (EBONE_SELECTABLE(arm, chbone)) {
+ chbone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
+ arm->act_edbone = chbone;
+
+ if (!add_to_sel) {
+ curbone->flag &= ~(BONE_SELECTED | BONE_ROOTSEL);
+ if (curbone->parent) curbone->parent->flag &= ~BONE_TIPSEL;
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ ED_armature_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 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");
+}
+
diff --git a/source/blender/editors/armature/armature_skinning.c b/source/blender/editors/armature/armature_skinning.c
new file mode 100644
index 00000000000..4b84a707ead
--- /dev/null
+++ b/source/blender/editors/armature/armature_skinning.c
@@ -0,0 +1,480 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
+ * All rights reserved.
+ *
+ * Contributor(s): Blender Foundation, 2002-2009 full recode.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ *
+ * API's for creating vertex groups from bones
+ * - Interfaces with heat weighting in meshlaplacian
+ */
+
+/** \file blender/editors/armature/armature_skinning.c
+ * \ingroup edarmature
+ */
+
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <math.h>
+#include <float.h>
+
+
+#include "DNA_anim_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_armature_types.h"
+#include "DNA_constraint_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_blenlib.h"
+#include "BLI_math.h"
+#include "BLI_utildefines.h"
+#include "BLI_ghash.h"
+
+#include "BKE_animsys.h"
+#include "BKE_action.h"
+#include "BKE_armature.h"
+#include "BKE_constraint.h"
+#include "BKE_context.h"
+#include "BKE_deform.h"
+#include "BKE_depsgraph.h"
+#include "BKE_DerivedMesh.h"
+#include "BKE_global.h"
+#include "BKE_idprop.h"
+#include "BKE_main.h"
+#include "BKE_object.h"
+#include "BKE_report.h"
+#include "BKE_subsurf.h"
+#include "BKE_modifier.h"
+#include "DNA_object_types.h"
+
+#include "BIF_gl.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "ED_armature.h"
+#include "ED_keyframing.h"
+#include "ED_mesh.h"
+#include "ED_object.h"
+#include "ED_screen.h"
+#include "ED_util.h"
+#include "ED_view3d.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "armature_intern.h"
+#include "meshlaplacian.h"
+
+#if 0
+#include "reeb.h"
+#endif
+
+/* ********************************** Bone Skinning *********************************************** */
+
+static int bone_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) 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; } *data = datap;
+
+ if (!(ob->mode & OB_MODE_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 aleady exist the routine exits.
+ */
+ if (!(bone->flag & BONE_NO_DEFORM)) {
+ if (!defgroup_find_name(ob, bone->name)) {
+ ED_vgroup_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; } *data = datap;
+ int wpmode = (ob->mode & OB_MODE_WEIGHT_PAINT);
+ bArmature *arm = data->armob->data;
+
+ if (!wpmode || !(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 (!wpmode || ((arm->layer & bone->layer) && (bone->flag & BONE_SELECTED)))
+ if (!(defgroup = defgroup_find_name(ob, bone->name)))
+ defgroup = ED_vgroup_add_name(ob, bone->name);
+
+ if (data->list != NULL) {
+ hgroup = (bDeformGroup ***) &data->list;
+
+ for (a = 0; a < segments; a++) {
+ **hgroup = defgroup;
+ ++*hgroup;
+ }
+ }
+ return segments;
+ }
+ }
+ return 0;
+}
+
+static void add_vgroups__mapFunc(void *userData, int index, const float co[3],
+ const float UNUSED(no_f[3]), const short UNUSED(no_s[3]))
+{
+ /* DerivedMesh mapFunc for getting final coords in weight paint mode */
+
+ float (*verts)[3] = userData;
+ copy_v3_v3(verts[index], co);
+}
+
+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], int *selected, float scale)
+{
+ /* Create vertex group weights from envelopes */
+
+ Bone *bone;
+ bDeformGroup *dgroup;
+ float distance;
+ int i, iflip, j;
+
+ /* for each vertex in the mesh */
+ for (i = 0; i < mesh->totvert; i++) {
+ iflip = (dgroupflip) ? mesh_get_x_mirror_vert(ob, i) : 0;
+
+ /* 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 >= 0) {
+ 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, Scene *scene, Object *ob, Object *par, int heat, int 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 derivedmesh 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 = NULL;
+ float (*root)[3], (*tip)[3], (*verts)[3];
+ int *selected;
+ int numbones, vertsfilled = 0, i, j, segments = 0;
+ int wpmode = (ob->mode & OB_MODE_WEIGHT_PAINT);
+ struct { Object *armob; void *list; int heat; } looper_data;
+
+ looper_data.armob = par;
+ looper_data.heat = heat;
+ looper_data.list = NULL;
+
+ /* count the number of skinnable bones */
+ numbones = bone_looper(ob, arm->bonebase.first, &looper_data, bone_skinnable_cb);
+
+ if (numbones == 0)
+ return;
+
+ if (ED_vgroup_data_create(ob->data) == FALSE)
+ 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;
+ bbone = b_bone_spline_setup(pchan, 1);
+ }
+ }
+ }
+
+ 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[MAXBONENAME];
+
+ // 0 = don't strip off number extensions
+ flip_side_name(name, dgroup->name, FALSE);
+ dgroupflip[j] = defgroup_find_name(ob, name);
+ }
+ }
+
+ /* 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 derivedmesh */
+ DerivedMesh *dm = mesh_get_derived_final(scene, ob, CD_MASK_BAREMESH);
+
+ if (dm->foreachMappedVert) {
+ dm->foreachMappedVert(dm, add_vgroups__mapFunc, (void *)verts);
+ vertsfilled = 1;
+ }
+
+ dm->release(dm);
+ }
+ 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 */
+ mesh_octree_table(ob, 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 create_vgroups_from_armature(ReportList *reports, Scene *scene, Object *ob, Object *par, int mode, int 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_countlist(&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 (mode == ARM_GROUPS_ENVELOPE || mode == 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, 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
new file mode 100644
index 00000000000..db47fd06f38
--- /dev/null
+++ b/source/blender/editors/armature/armature_utils.c
@@ -0,0 +1,707 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
+ * All rights reserved.
+ *
+ * Contributor(s): Blender Foundation, 2002-2009 full recode.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/editors/armature/editarmature.c
+ * \ingroup edarmature
+ */
+
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <math.h>
+#include <float.h>
+
+
+#include "DNA_anim_types.h"
+#include "DNA_armature_types.h"
+#include "DNA_constraint_types.h"
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_modifier_types.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_blenlib.h"
+#include "BLI_math.h"
+#include "BLI_utildefines.h"
+#include "BLI_ghash.h"
+
+#include "BKE_animsys.h"
+#include "BKE_action.h"
+#include "BKE_armature.h"
+#include "BKE_constraint.h"
+#include "BKE_context.h"
+#include "BKE_deform.h"
+#include "BKE_depsgraph.h"
+#include "BKE_global.h"
+#include "BKE_idprop.h"
+#include "BKE_main.h"
+#include "BKE_object.h"
+#include "BKE_report.h"
+#include "BKE_modifier.h"
+
+#include "BIF_gl.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "ED_armature.h"
+#include "ED_keyframing.h"
+#include "ED_object.h"
+#include "ED_screen.h"
+#include "ED_util.h"
+#include "ED_view3d.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "armature_intern.h"
+
+/* *************************************************************** */
+/* Validation */
+
+/* Sync selection to parent for connected children */
+void ED_armature_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;
+ }
+ }
+}
+
+void ED_armature_validate_active(struct bArmature *arm)
+{
+ EditBone *ebone = arm->act_edbone;
+
+ if (ebone) {
+ if (ebone->flag & BONE_HIDDEN_A)
+ arm->act_edbone = NULL;
+ }
+}
+
+/* *************************************************************** */
+/* Bone Operations */
+
+/* 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 *))
+{
+ /* 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);
+
+ /* 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);
+ }
+
+ return count;
+}
+
+/* *************************************************************** */
+/* Bone Removal */
+
+void bone_free(bArmature *arm, EditBone *bone)
+{
+ if (arm->act_edbone == bone)
+ arm->act_edbone = NULL;
+
+ if (bone->prop) {
+ IDP_FreeProperty(bone->prop);
+ MEM_freeN(bone->prop);
+ }
+
+ BLI_freelinkN(arm->edbo, bone);
+}
+
+void ED_armature_edit_bone_remove(bArmature *arm, EditBone *exBone)
+{
+ 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;
+ curBone->flag &= ~BONE_CONNECTED;
+ }
+ }
+
+ bone_free(arm, exBone);
+}
+
+/* *************************************************************** */
+/* Mirroring */
+
+/* context: editmode armature */
+EditBone *ED_armature_bone_get_mirrored(ListBase *edbo, EditBone *ebo)
+{
+ EditBone *eboflip = NULL;
+ char name[MAXBONENAME];
+
+ if (ebo == NULL)
+ return NULL;
+
+ flip_side_name(name, ebo->name, FALSE);
+
+ for (eboflip = edbo->first; eboflip; eboflip = eboflip->next) {
+ if (ebo != eboflip) {
+ if (!strcmp(name, eboflip->name))
+ break;
+ }
+ }
+
+ return eboflip;
+}
+
+/* ------------------------------------- */
+
+/* helper function for tools to work on mirrored parts.
+ * it leaves mirrored bones selected then too, which is a good indication of what happened */
+void armature_select_mirrored(bArmature *arm)
+{
+ /* 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 & BONE_SELECTED) {
+ ebone_mirr = ED_armature_bone_get_mirrored(arm->edbo, curBone);
+ if (ebone_mirr)
+ ebone_mirr->flag |= 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_bone_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_bone_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;
+
+ for (curBone = arm->edbo->first; curBone; curBone = curBone->next) {
+ if (curBone->flag & BONE_DONE) {
+ curBone->flag &= ~(BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL | BONE_DONE);
+ }
+ }
+}
+
+/* ------------------------------------- */
+
+/* if editbone (partial) selected, copy data */
+/* context; editmode armature, with mirror editing enabled */
+void transform_armature_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_bone_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;
+
+ /* 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;
+
+ /* 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;
+ }
+ }
+ }
+ }
+}
+
+/* *************************************************************** */
+/* Armature EditMode Conversions */
+
+/* converts Bones to EditBone list, used for tools as well */
+EditBone *make_boneList(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");
+
+ /* Copy relevant data from bone to eBone */
+ 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;
+ eBone->flag &= ~BONE_ROOTSEL; /* this is ignored when there is a connected parent, so unset it */
+ }
+ else {
+ eBone->flag |= BONE_ROOTSEL;
+ }
+ }
+ else {
+ /* if the bone is not selected, but connected to its parent
+ * copy the parents tip selection state */
+ if (eBone->parent && (eBone->flag & BONE_CONNECTED)) {
+ /* selecting with the mouse gives this behavior */
+ if (eBone->parent->flag & BONE_TIPSEL) {
+ eBone->flag |= BONE_ROOTSEL;
+ }
+ else {
+ eBone->flag &= ~BONE_ROOTSEL;
+ }
+
+ /* probably not selected but just in case */
+ eBone->flag &= ~BONE_TIPSEL;
+ }
+ }
+
+ 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->ease1 = curBone->ease1;
+ eBone->ease2 = curBone->ease2;
+ eBone->rad_head = curBone->rad_head;
+ eBone->rad_tail = curBone->rad_tail;
+ eBone->segments = curBone->segments;
+ eBone->layer = curBone->layer;
+
+ if (curBone->prop)
+ eBone->prop = IDP_CopyProperty(curBone->prop);
+
+ BLI_addtail(edbo, eBone);
+
+ /* Add children if necessary */
+ if (curBone->childbase.first) {
+ eBoneTest = make_boneList(edbo, &curBone->childbase, eBone, actBone);
+ if (eBoneTest)
+ eBoneAct = eBoneTest;
+ }
+
+ if (curBone == actBone)
+ eBoneAct = eBone;
+ }
+
+ return eBoneAct;
+}
+
+/* nasty stuff for converting roll in editbones into bones */
+/* also sets restposition in armature (arm_mat) */
+static void fix_bonelist_roll(ListBase *bonelist, ListBase *editbonelist)
+{
+ Bone *curBone;
+ EditBone *ebone;
+ float premat[3][3];
+ float postmat[3][3];
+ float difmat[3][3];
+ float imat[3][3];
+ float delta[3];
+
+ for (curBone = bonelist->first; curBone; curBone = curBone->next) {
+ /* sets local matrix and arm_mat (restpos) */
+ BKE_armature_where_is_bone(curBone, curBone->parent);
+
+ /* Find the associated editbone */
+ for (ebone = editbonelist->first; ebone; ebone = ebone->next)
+ if ((Bone *)ebone->temp == curBone)
+ break;
+
+ if (ebone) {
+ /* Get the ebone premat */
+ sub_v3_v3v3(delta, ebone->tail, ebone->head);
+ vec_roll_to_mat3(delta, ebone->roll, premat);
+
+ /* Get the bone postmat */
+ copy_m3_m4(postmat, curBone->arm_mat);
+
+ invert_m3_m3(imat, premat);
+ 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])));
+#endif
+ curBone->roll = (float)-atan2(difmat[2][0], difmat[2][2]);
+
+ /* and set restposition again */
+ BKE_armature_where_is_bone(curBone, curBone->parent);
+ }
+ fix_bonelist_roll(&curBone->childbase, editbonelist);
+ }
+}
+
+/* put EditMode back in Object */
+void ED_armature_from_edit(Object *obedit)
+{
+ bArmature *arm = obedit->data;
+ EditBone *eBone, *neBone;
+ Bone *newBone;
+ Object *obt;
+
+ /* armature bones */
+ BKE_armature_bonelist_free(&arm->bonebase);
+
+ /* remove zero sized bones, this gives instable restposes */
+ for (eBone = arm->edbo->first; eBone; eBone = neBone) {
+ float len = len_v3v3(eBone->head, eBone->tail);
+ neBone = eBone->next;
+ if (len <= 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 = 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 */
+ /* newBone->flag |= BONE_SELECTED; */ /* important, editbones can be active with only 1 point selected */
+ arm->act_edbone = NULL;
+ 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->ease1 = eBone->ease1;
+ newBone->ease2 = eBone->ease2;
+ newBone->rad_head = eBone->rad_head;
+ newBone->rad_tail = eBone->rad_tail;
+ newBone->segments = eBone->segments;
+ newBone->layer = eBone->layer;
+
+ 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 */
+ for (eBone = arm->edbo->first; eBone; eBone = eBone->next) {
+ newBone = (Bone *)eBone->temp;
+ if (eBone->parent) {
+ newBone->parent = (Bone *)eBone->parent->temp;
+ BLI_addtail(&newBone->parent->childbase, newBone);
+
+ {
+ float M_parentRest[3][3];
+ float iM_parentRest[3][3];
+ float delta[3];
+
+ /* Get the parent's matrix (rotation only) */
+ sub_v3_v3v3(delta, eBone->parent->tail, eBone->parent->head);
+ vec_roll_to_mat3(delta, eBone->parent->roll, M_parentRest);
+
+ /* Invert the parent matrix */
+ invert_m3_m3(iM_parentRest, M_parentRest);
+
+ /* Get the new head and tail */
+ sub_v3_v3v3(newBone->head, eBone->head, eBone->parent->tail);
+ sub_v3_v3v3(newBone->tail, eBone->tail, eBone->parent->tail);
+
+ mul_m3_v3(iM_parentRest, newBone->head);
+ mul_m3_v3(iM_parentRest, newBone->tail);
+ }
+ }
+ /* ...otherwise add this bone to the armature's bonebase */
+ else {
+ copy_v3_v3(newBone->head, eBone->head);
+ copy_v3_v3(newBone->tail, eBone->tail);
+ BLI_addtail(&arm->bonebase, newBone);
+ }
+ }
+
+ /* Make a pass through the new armature to fix rolling */
+ /* also builds restposition again (like BKE_armature_where_is) */
+ fix_bonelist_roll(&arm->bonebase, arm->edbo);
+
+ /* so all users of this armature should get rebuilt */
+ for (obt = G.main->object.first; obt; obt = obt->id.next) {
+ if (obt->data == arm)
+ BKE_pose_rebuild(obt, arm);
+ }
+
+ DAG_id_tag_update(&obedit->id, OB_RECALC_DATA);
+}
+
+void ED_armature_edit_free(struct Object *ob)
+{
+ bArmature *arm = ob->data;
+ 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);
+ }
+ }
+
+ BLI_freelistN(arm->edbo);
+ }
+ MEM_freeN(arm->edbo);
+ arm->edbo = NULL;
+ }
+}
+
+/* Put armature in EditMode */
+void ED_armature_to_edit(Object *ob)
+{
+ bArmature *arm = ob->data;
+
+ ED_armature_edit_free(ob);
+ arm->edbo = MEM_callocN(sizeof(ListBase), "edbo armature");
+ arm->act_edbone = make_boneList(arm->edbo, &arm->bonebase, NULL, arm->act_bone);
+ arm->act_bone = NULL;
+
+// BIF_freeTemplates(); /* force template update when entering editmode */
+}
+
+/* *************************************************************** */
+/* Undo for Armature EditMode*/
+
+typedef struct UndoArmature {
+ EditBone *act_edbone;
+ ListBase lb;
+} UndoArmature;
+
+static void undoBones_to_editBones(void *uarmv, void *armv, void *UNUSED(data))
+{
+ UndoArmature *uarm = uarmv;
+ bArmature *arm = armv;
+ EditBone *ebo, *newebo;
+
+ BLI_freelistN(arm->edbo);
+
+ /* copy */
+ for (ebo = uarm->lb.first; ebo; ebo = ebo->next) {
+ newebo = MEM_dupallocN(ebo);
+ ebo->temp = newebo;
+ BLI_addtail(arm->edbo, newebo);
+ }
+
+ /* active bone */
+ if (uarm->act_edbone) {
+ ebo = uarm->act_edbone;
+ arm->act_edbone = ebo->temp;
+ }
+ else
+ arm->act_edbone = NULL;
+
+ /* set pointers */
+ for (newebo = arm->edbo->first; newebo; newebo = newebo->next) {
+ if (newebo->parent) newebo->parent = newebo->parent->temp;
+ }
+ /* be sure they don't hang ever */
+ for (newebo = arm->edbo->first; newebo; newebo = newebo->next) {
+ newebo->temp = NULL;
+ }
+}
+
+static void *editBones_to_undoBones(void *armv, void *UNUSED(obdata))
+{
+ bArmature *arm = armv;
+ UndoArmature *uarm;
+ EditBone *ebo, *newebo;
+
+ uarm = MEM_callocN(sizeof(UndoArmature), "listbase undo");
+
+ /* copy */
+ for (ebo = arm->edbo->first; ebo; ebo = ebo->next) {
+ newebo = MEM_dupallocN(ebo);
+ ebo->temp = newebo;
+ BLI_addtail(&uarm->lb, newebo);
+ }
+
+ /* active bone */
+ if (arm->act_edbone) {
+ ebo = arm->act_edbone;
+ uarm->act_edbone = ebo->temp;
+ }
+
+ /* set pointers */
+ for (newebo = uarm->lb.first; newebo; newebo = newebo->next) {
+ if (newebo->parent) newebo->parent = newebo->parent->temp;
+ }
+
+ return uarm;
+}
+
+static void free_undoBones(void *uarmv)
+{
+ UndoArmature *uarm = uarmv;
+
+ BLI_freelistN(&uarm->lb);
+ MEM_freeN(uarm);
+}
+
+static void *get_armature_edit(bContext *C)
+{
+ Object *obedit = CTX_data_edit_object(C);
+ if (obedit && obedit->type == OB_ARMATURE) {
+ return obedit->data;
+ }
+ return NULL;
+}
+
+/* and this is all the undo system needs to know */
+void undo_push_armature(bContext *C, const char *name)
+{
+ // XXX solve getdata()
+ undo_editmode_push(C, name, get_armature_edit, free_undoBones, undoBones_to_editBones, editBones_to_undoBones, NULL);
+}
diff --git a/source/blender/editors/armature/editarmature.c b/source/blender/editors/armature/editarmature.c
deleted file mode 100644
index 574cf18773b..00000000000
--- a/source/blender/editors/armature/editarmature.c
+++ /dev/null
@@ -1,6199 +0,0 @@
-/*
- * ***** BEGIN GPL LICENSE BLOCK *****
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
- * All rights reserved.
- *
- * Contributor(s): Blender Foundation, 2002-2009 full recode.
- *
- * ***** END GPL LICENSE BLOCK *****
- */
-
-/** \file blender/editors/armature/editarmature.c
- * \ingroup edarmature
- */
-
-
-#include <ctype.h>
-#include <stdlib.h>
-#include <stddef.h>
-#include <string.h>
-#include <math.h>
-#include <float.h>
-#include <assert.h>
-
-
-#include "DNA_anim_types.h"
-#include "DNA_mesh_types.h"
-#include "DNA_armature_types.h"
-#include "DNA_constraint_types.h"
-#include "DNA_meshdata_types.h"
-#include "DNA_object_types.h"
-#include "DNA_scene_types.h"
-
-#include "MEM_guardedalloc.h"
-
-#include "BLI_blenlib.h"
-#include "BLI_math.h"
-#include "BLI_utildefines.h"
-#include "BLI_ghash.h"
-
-#include "BLF_translation.h"
-
-#include "BKE_animsys.h"
-#include "BKE_action.h"
-#include "BKE_armature.h"
-#include "BKE_constraint.h"
-#include "BKE_context.h"
-#include "BKE_deform.h"
-#include "BKE_depsgraph.h"
-#include "BKE_DerivedMesh.h"
-#include "BKE_global.h"
-#include "BKE_idprop.h"
-#include "BKE_main.h"
-#include "BKE_object.h"
-#include "BKE_report.h"
-#include "BKE_subsurf.h"
-#include "BKE_modifier.h"
-#include "DNA_object_types.h"
-
-#include "BIF_gl.h"
-
-#include "RNA_access.h"
-#include "RNA_define.h"
-
-#include "WM_api.h"
-#include "WM_types.h"
-
-#include "ED_armature.h"
-#include "ED_keyframing.h"
-#include "ED_mesh.h"
-#include "ED_object.h"
-#include "ED_screen.h"
-#include "ED_util.h"
-#include "ED_view3d.h"
-
-#include "UI_interface.h"
-#include "UI_resources.h"
-
-#include "armature_intern.h"
-#include "meshlaplacian.h"
-
-#if 0
-#include "reeb.h"
-#endif
-
-/* **************** tools on Editmode Armature **************** */
-
-/* Sync selection to parent for connected children */
-void ED_armature_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;
- }
- }
-}
-
-void ED_armature_validate_active(struct bArmature *arm)
-{
- EditBone *ebone = arm->act_edbone;
-
- if (ebone) {
- if (ebone->flag & BONE_HIDDEN_A)
- arm->act_edbone = NULL;
- }
-}
-
-static void bone_free(bArmature *arm, EditBone *bone)
-{
- if (arm->act_edbone == bone)
- arm->act_edbone = NULL;
-
- if (bone->prop) {
- IDP_FreeProperty(bone->prop);
- MEM_freeN(bone->prop);
- }
-
- BLI_freelinkN(arm->edbo, bone);
-}
-
-void ED_armature_edit_bone_remove(bArmature *arm, EditBone *exBone)
-{
- 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;
- curBone->flag &= ~BONE_CONNECTED;
- }
- }
-
- bone_free(arm, exBone);
-}
-
-/* context: editmode armature */
-EditBone *ED_armature_bone_get_mirrored(ListBase *edbo, EditBone *ebo)
-{
- EditBone *eboflip = NULL;
- char name[MAXBONENAME];
-
- if (ebo == NULL)
- return NULL;
-
- flip_side_name(name, ebo->name, FALSE);
-
- for (eboflip = edbo->first; eboflip; eboflip = eboflip->next) {
- if (ebo != eboflip) {
- if (!strcmp(name, eboflip->name))
- break;
- }
- }
-
- return eboflip;
-}
-
-/* helper function for tools to work on mirrored parts.
- * it leaves mirrored bones selected then too, which is a good indication of what happened */
-static void armature_select_mirrored(bArmature *arm)
-{
- /* 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 & BONE_SELECTED) {
- ebone_mirr = ED_armature_bone_get_mirrored(arm->edbo, curBone);
- if (ebone_mirr)
- ebone_mirr->flag |= BONE_SELECTED;
- }
- }
- }
- }
-
-}
-
-static 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_bone_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_bone_get_mirrored(arm->edbo, curBone);
- curBone->flag |= ebone_mirr->flag & (BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL);
- }
- }
- }
-}
-
-
-/* only works when tagged */
-static void armature_tag_unselect(bArmature *arm)
-{
- 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);
- }
- }
-}
-
-/* converts Bones to EditBone list, used for tools as well */
-EditBone *make_boneList(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");
-
- /* Copy relevant data from bone to eBone */
- 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;
- eBone->flag &= ~BONE_ROOTSEL; /* this is ignored when there is a connected parent, so unset it */
- }
- else {
- eBone->flag |= BONE_ROOTSEL;
- }
- }
- else {
- /* if the bone is not selected, but connected to its parent
- * copy the parents tip selection state */
- if (eBone->parent && (eBone->flag & BONE_CONNECTED)) {
- /* selecting with the mouse gives this behavior */
- if (eBone->parent->flag & BONE_TIPSEL) {
- eBone->flag |= BONE_ROOTSEL;
- }
- else {
- eBone->flag &= ~BONE_ROOTSEL;
- }
-
- /* probably not selected but just in case */
- eBone->flag &= ~BONE_TIPSEL;
- }
- }
-
- 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->ease1 = curBone->ease1;
- eBone->ease2 = curBone->ease2;
- eBone->rad_head = curBone->rad_head;
- eBone->rad_tail = curBone->rad_tail;
- eBone->segments = curBone->segments;
- eBone->layer = curBone->layer;
-
- if (curBone->prop)
- eBone->prop = IDP_CopyProperty(curBone->prop);
-
- BLI_addtail(edbo, eBone);
-
- /* Add children if necessary */
- if (curBone->childbase.first) {
- eBoneTest = make_boneList(edbo, &curBone->childbase, eBone, actBone);
- if (eBoneTest)
- eBoneAct = eBoneTest;
- }
-
- if (curBone == actBone)
- eBoneAct = eBone;
- }
-
- return eBoneAct;
-}
-
-/* nasty stuff for converting roll in editbones into bones */
-/* also sets restposition in armature (arm_mat) */
-static void fix_bonelist_roll(ListBase *bonelist, ListBase *editbonelist)
-{
- Bone *curBone;
- EditBone *ebone;
- float premat[3][3];
- float postmat[3][3];
- float difmat[3][3];
- float imat[3][3];
- float delta[3];
-
- for (curBone = bonelist->first; curBone; curBone = curBone->next) {
- /* sets local matrix and arm_mat (restpos) */
- BKE_armature_where_is_bone(curBone, curBone->parent);
-
- /* Find the associated editbone */
- for (ebone = editbonelist->first; ebone; ebone = ebone->next)
- if ((Bone *)ebone->temp == curBone)
- break;
-
- if (ebone) {
- /* Get the ebone premat */
- sub_v3_v3v3(delta, ebone->tail, ebone->head);
- vec_roll_to_mat3(delta, ebone->roll, premat);
-
- /* Get the bone postmat */
- copy_m3_m4(postmat, curBone->arm_mat);
-
- invert_m3_m3(imat, premat);
- 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])));
-#endif
- curBone->roll = (float)-atan2(difmat[2][0], difmat[2][2]);
-
- /* and set restposition again */
- BKE_armature_where_is_bone(curBone, curBone->parent);
- }
- fix_bonelist_roll(&curBone->childbase, editbonelist);
- }
-}
-
-/* put EditMode back in Object */
-void ED_armature_from_edit(Object *obedit)
-{
- bArmature *arm = obedit->data;
- EditBone *eBone, *neBone;
- Bone *newBone;
- Object *obt;
-
- /* armature bones */
- BKE_armature_bonelist_free(&arm->bonebase);
-
- /* remove zero sized bones, this gives instable restposes */
- for (eBone = arm->edbo->first; eBone; eBone = neBone) {
- float len = len_v3v3(eBone->head, eBone->tail);
- neBone = eBone->next;
- if (len <= 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 = 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 */
- /* newBone->flag |= BONE_SELECTED; */ /* important, editbones can be active with only 1 point selected */
- arm->act_edbone = NULL;
- 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->ease1 = eBone->ease1;
- newBone->ease2 = eBone->ease2;
- newBone->rad_head = eBone->rad_head;
- newBone->rad_tail = eBone->rad_tail;
- newBone->segments = eBone->segments;
- newBone->layer = eBone->layer;
-
- 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 */
- for (eBone = arm->edbo->first; eBone; eBone = eBone->next) {
- newBone = (Bone *)eBone->temp;
- if (eBone->parent) {
- newBone->parent = (Bone *)eBone->parent->temp;
- BLI_addtail(&newBone->parent->childbase, newBone);
-
- {
- float M_parentRest[3][3];
- float iM_parentRest[3][3];
- float delta[3];
-
- /* Get the parent's matrix (rotation only) */
- sub_v3_v3v3(delta, eBone->parent->tail, eBone->parent->head);
- vec_roll_to_mat3(delta, eBone->parent->roll, M_parentRest);
-
- /* Invert the parent matrix */
- invert_m3_m3(iM_parentRest, M_parentRest);
-
- /* Get the new head and tail */
- sub_v3_v3v3(newBone->head, eBone->head, eBone->parent->tail);
- sub_v3_v3v3(newBone->tail, eBone->tail, eBone->parent->tail);
-
- mul_m3_v3(iM_parentRest, newBone->head);
- mul_m3_v3(iM_parentRest, newBone->tail);
- }
- }
- /* ...otherwise add this bone to the armature's bonebase */
- else {
- copy_v3_v3(newBone->head, eBone->head);
- copy_v3_v3(newBone->tail, eBone->tail);
- BLI_addtail(&arm->bonebase, newBone);
- }
- }
-
- /* Make a pass through the new armature to fix rolling */
- /* also builds restposition again (like BKE_armature_where_is) */
- fix_bonelist_roll(&arm->bonebase, arm->edbo);
-
- /* so all users of this armature should get rebuilt */
- for (obt = G.main->object.first; obt; obt = obt->id.next) {
- if (obt->data == arm)
- BKE_pose_rebuild(obt, arm);
- }
-
- DAG_id_tag_update(&obedit->id, OB_RECALC_DATA);
-}
-
-void ED_armature_apply_transform(Object *ob, float mat[4][4])
-{
- EditBone *ebone;
- bArmature *arm = ob->data;
- 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);
-
- /* Put the armature into editmode */
- ED_armature_to_edit(ob);
-
- /* Do the rotations */
- for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
- float delta[3], tmat[3][3];
-
- /* find the current bone's roll matrix */
- sub_v3_v3v3(delta, ebone->tail, ebone->head);
- vec_roll_to_mat3(delta, ebone->roll, 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 transfiormed roll back */
- mat3_to_vec_roll(tmat, NULL, &ebone->roll);
-
- 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;
- }
-
- /* Turn the list into an armature */
- ED_armature_from_edit(ob);
- ED_armature_edit_free(ob);
-}
-
-/* exported for use in editors/object/ */
-/* 0 == do center, 1 == center new, 2 == center cursor */
-void docenter_armature(Scene *scene, Object *ob, float cursor[3], int centermode, int around)
-{
- Object *obedit = scene->obedit; // XXX get from context
- EditBone *ebone;
- bArmature *arm = ob->data;
- float cent[3];
-
- /* Put the armature into editmode */
- if (ob != obedit) {
- ED_armature_to_edit(ob);
- obedit = NULL; /* we cant use this so behave as if there is no obedit */
- }
-
- /* 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_CENTROID) {
- 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 (obedit == NULL) {
- ED_armature_from_edit(ob);
- ED_armature_edit_free(ob);
- }
-
- /* Adjust object location for new centerpoint */
- if (centermode && obedit == NULL) {
- mul_mat3_m4_v3(ob->obmat, cent); /* ommit translation part */
- add_v3_v3(ob->loc, cent);
- }
-}
-
-/* ---------------------- */
-
-/* checks if an EditBone with a matching name already, returning the matching bone if it exists */
-static EditBone *editbone_name_exists(ListBase *edbo, const char *name)
-{
- return BLI_findstring(edbo, name, offsetof(EditBone, name));
-}
-
-/* note: there's a unique_bone_name() too! */
-static int editbone_unique_check(void *arg, const char *name)
-{
- struct {ListBase *lb; void *bone; } *data = arg;
- EditBone *dupli = editbone_name_exists(data->lb, name);
- return dupli && dupli != data->bone;
-}
-
-void unique_editbone_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, "Bone", '.', name, sizeof(bone->name));
-}
-
-/* helper for apply_armature_pose2bones - fixes parenting of objects that are bone-parented to armature */
-static void applyarmature_fix_boneparents(Scene *scene, Object *armob)
-{
- Object workob, *ob;
-
- /* go through all objects in database */
- for (ob = G.main->object.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(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)
-{
- Scene *scene = CTX_data_scene(C);
- Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); // must be active object, not edit-object
- 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"); /* error_libdata(); */
- 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(ob);
-
- /* get pose of active object and move it out of posemode */
- pose = ob->pose;
-
- for (pchan = pose->chanbase.first; pchan; pchan = pchan->next) {
- curbone = editbone_name_exists(arm->edbo, pchan->name);
-
- /* simply copy the head/tail values from pchan over to curbone */
- copy_v3_v3(curbone->head, pchan->pose_head);
- copy_v3_v3(curbone->tail, pchan->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->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];
- }
-
- /* 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(ob);
- ED_armature_edit_free(ob);
-
- /* flush positions of posebones */
- BKE_pose_where_is(scene, ob);
-
- /* fix parenting of objects which are bone-parented */
- applyarmature_fix_boneparents(scene, ob);
-
- /* note, notifier might evolve */
- WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
-
- 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";
-
- /* callbacks */
- ot->exec = apply_armature_pose2bones_exec;
- ot->poll = ED_operator_posemode;
-
- /* 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))
-{
- Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); // must be active object, not edit-object
-
- /* don't check if editmode (should be done by caller) */
- if (ob->type != OB_ARMATURE)
- return OPERATOR_CANCELLED;
-
- /* loop over all selected pchans
- *
- * TODO, loop over children before parents if multiple bones
- * at once are to be predictable*/
- CTX_DATA_BEGIN(C, bPoseChannel *, pchan, selected_pose_bones)
- {
- 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.
- */
- copy_m4_m4(delta_mat, pchan->chan_mat);
- BKE_pchan_apply_mat4(pchan, delta_mat, TRUE);
- }
- CTX_DATA_END;
-
- DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
-
- /* note, notifier might evolve */
- WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
-
- 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";
-
- /* callbacks */
- ot->exec = pose_visual_transform_apply_exec;
- ot->poll = ED_operator_posemode;
-
- /* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-}
-
-/* ---------------------- */
-
-/* Helper function for armature joining - link fixing */
-static void joined_armature_fix_links(Object *tarArm, Object *srcArm, bPoseChannel *pchan, EditBone *curbone)
-{
- Object *ob;
- bPose *pose;
- bPoseChannel *pchant;
- bConstraint *con;
-
- /* let's go through all objects in database */
- for (ob = G.main->object.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) {
- for (con = pchant->constraints.first; con; con = con->next) {
- bConstraintTypeInfo *cti = BKE_constraint_get_typeinfo(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 (strcmp(ct->subtarget, pchan->name) == 0) {
- 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? */
- if (con->type == CONSTRAINT_TYPE_ACTION) {
- bActionConstraint *data = con->data; // XXX old animation system
- bAction *act;
- bActionChannel *achan;
-
- if (data->act) {
- act = data->act;
-
- for (achan = act->chanbase.first; achan; achan = achan->next) {
- if (strcmp(achan->name, pchan->name) == 0)
- BLI_strncpy(achan->name, curbone->name, sizeof(achan->name));
- }
- }
- }
-
- }
- }
- }
-
- /* fix object-level constraints */
- if (ob != srcArm) {
- for (con = ob->constraints.first; con; con = con->next) {
- bConstraintTypeInfo *cti = BKE_constraint_get_typeinfo(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 (strcmp(ct->subtarget, pchan->name) == 0) {
- ct->tar = tarArm;
- BLI_strncpy(ct->subtarget, curbone->name, sizeof(ct->subtarget));
- }
- }
- }
-
- 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 == srcArm)) {
- /* Is object parented to a bone of this src armature? */
- if (ob->partype == PARBONE) {
- /* bone name in object */
- if (!strcmp(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 *UNUSED(op))
-{
- Main *bmain = CTX_data_main(C);
- Scene *scene = CTX_data_scene(C);
- Object *ob = CTX_data_active_object(C);
- bArmature *arm = (ob) ? ob->data : NULL;
- bPose *pose, *opose;
- bPoseChannel *pchan, *pchann;
- EditBone *curbone;
- float mat[4][4], oimat[4][4];
-
- /* Ensure we're not in editmode and that the active object is an armature*/
- if (!ob || ob->type != OB_ARMATURE)
- return OPERATOR_CANCELLED;
- if (!arm || arm->edbo)
- return OPERATOR_CANCELLED;
-
- /* Get editbones of active armature to add editbones to */
- ED_armature_to_edit(ob);
-
- /* get pose of active object and move it out of posemode */
- pose = ob->pose;
- ob->mode &= ~OB_MODE_POSE;
-
- CTX_DATA_BEGIN(C, Base *, base, selected_editable_bases)
- {
- if ((base->object->type == OB_ARMATURE) && (base->object != ob)) {
- bArmature *curarm = base->object->data;
-
- /* Make a list of editbones in current armature */
- ED_armature_to_edit(base->object);
-
- /* Get Pose of current armature */
- opose = base->object->pose;
- base->object->mode &= ~OB_MODE_POSE;
- //BASACT->flag &= ~OB_MODE_POSE;
-
- /* Find the difference matrix */
- invert_m4_m4(oimat, ob->obmat);
- mult_m4_m4m4(mat, oimat, base->object->obmat);
-
- /* Copy bones and posechannels from the object to the edit armature */
- for (pchan = opose->chanbase.first; pchan; pchan = pchann) {
- pchann = pchan->next;
- curbone = editbone_name_exists(curarm->edbo, pchan->name);
-
- /* Get new name */
- unique_editbone_name(arm->edbo, curbone->name, NULL);
-
- /* Transform the bone */
- {
- float premat[4][4];
- float postmat[4][4];
- float difmat[4][4];
- float imat[4][4];
- float temp[3][3];
- float delta[3];
-
- /* Get the premat */
- sub_v3_v3v3(delta, curbone->tail, curbone->head);
- vec_roll_to_mat3(delta, curbone->roll, temp);
-
- unit_m4(premat); /* Mat4MulMat34 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 */
- sub_v3_v3v3(delta, curbone->tail, curbone->head);
- vec_roll_to_mat3(delta, curbone->roll, temp);
- copy_m4_m3(postmat, temp);
-
- /* Find the roll */
- invert_m4_m4(imat, premat);
- mult_m4_m4m4(difmat, imat, postmat);
-
- curbone->roll -= (float)atan2(difmat[2][0], difmat[2][2]);
- }
-
- /* Fix Constraints and Other Links to this Bone and Armature */
- joined_armature_fix_links(ob, base->object, 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);
- }
-
- ED_base_object_free_and_unlink(bmain, scene, base);
- }
- }
- CTX_DATA_END;
-
- DAG_relations_tag_update(bmain); /* because we removed object(s) */
-
- ED_armature_from_edit(ob);
- ED_armature_edit_free(ob);
-
- WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene);
-
- return OPERATOR_FINISHED;
-}
-
-/* ---------------------- */
-
-/* Helper function for armature separating - link fixing */
-static void separated_armature_fix_links(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 = G.main->object.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) {
- bConstraintTypeInfo *cti = BKE_constraint_get_typeinfo(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) {
- bConstraintTypeInfo *cti = BKE_constraint_get_typeinfo(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
- * sel: remove selected bones from the armature, otherwise the unselected bones are removed
- * (ob is not in editmode)
- */
-static void separate_armature_bones(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(ob);
-
- /* go through pose-channels, checking if a bone should be removed */
- for (pchan = ob->pose->chanbase.first; pchan; pchan = pchann) {
- pchann = pchan->next;
- curbone = editbone_name_exists(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;
- ebo->temp = NULL; /* this is needed to prevent random crashes with in ED_armature_from_edit */
- 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;
- }
-
- /* 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(ob);
- ED_armature_edit_free(ob);
-}
-
-/* separate selected bones into their armature */
-static int separate_armature_exec(bContext *C, wmOperator *UNUSED(op))
-{
- Main *bmain = CTX_data_main(C);
- Scene *scene = CTX_data_scene(C);
- Object *obedit = CTX_data_edit_object(C);
- Object *oldob, *newob;
- Base *oldbase, *newbase;
-
- /* sanity checks */
- if (obedit == NULL)
- return OPERATOR_CANCELLED;
-
- /* set wait cursor in case this takes a while */
- WM_cursor_wait(1);
-
- /* 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 */
- /* TODO: use context iterators for this? */
- CTX_DATA_BEGIN(C, Base *, base, visible_bases)
- {
- if (base->object == obedit) base->flag |= 1;
- else base->flag &= ~1;
- }
- CTX_DATA_END;
-
- /* 1) store starting settings and exit editmode */
- oldob = obedit;
- oldbase = BASACT;
- oldob->mode &= ~OB_MODE_POSE;
- //oldbase->flag &= ~OB_POSEMODE;
-
- ED_armature_from_edit(obedit);
- ED_armature_edit_free(obedit);
-
- /* 2) duplicate base */
- newbase = ED_object_add_duplicate(bmain, scene, oldbase, USER_DUP_ARM); /* only duplicate linked armature */
- DAG_relations_tag_update(bmain);
-
- newob = newbase->object;
- newbase->flag &= ~SELECT;
-
-
- /* 3) remove bones that shouldn't still be around on both armatures */
- separate_armature_bones(oldob, 1);
- separate_armature_bones(newob, 0);
-
-
- /* 4) fix links before depsgraph flushes */ // err... or after?
- separated_armature_fix_links(oldob, newob);
-
- DAG_id_tag_update(&oldob->id, OB_RECALC_DATA); /* this is the original one */
- DAG_id_tag_update(&newob->id, OB_RECALC_DATA); /* this is the separated one */
-
-
- /* 5) restore original conditions */
- obedit = oldob;
-
- ED_armature_to_edit(obedit);
-
- /* note, notifier might evolve */
- WM_event_add_notifier(C, NC_OBJECT | ND_POSE, obedit);
-
- /* recalc/redraw + cleanup */
- WM_cursor_wait(0);
-
- 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;
-}
-
-/* **************** END tools on Editmode Armature **************** */
-/* **************** PoseMode & EditMode *************************** */
-
-/* only for opengl selection indices */
-Bone *get_indexed_bone(Object *ob, int index)
-{
- bPoseChannel *pchan;
- if (ob->pose == NULL) return NULL;
- index >>= 16; // bone selection codes use left 2 bytes
-
- pchan = BLI_findlink(&ob->pose->chanbase, index);
- return pchan ? pchan->bone : NULL;
-}
-
-/* See if there are any selected bones in this buffer */
-/* only bones from base are checked on */
-static void *get_bone_from_selectbuffer(Scene *scene, Base *base, unsigned int *buffer, short hits, short findunsel)
-{
- Object *obedit = scene->obedit; // XXX get from context
- Bone *bone;
- EditBone *ebone;
- void *firstunSel = NULL, *firstSel = NULL, *data;
- unsigned int hitresult;
- short i, takeNext = 0, sel;
-
- for (i = 0; i < hits; i++) {
- hitresult = buffer[3 + (i * 4)];
-
- if (!(hitresult & BONESEL_NOSEL)) { // -1
- if (hitresult & BONESEL_ANY) { // to avoid including objects in selection
-
- hitresult &= ~(BONESEL_ANY);
- /* Determine what the current bone is */
- if (obedit == NULL || base->object != obedit) {
- /* no singular posemode, so check for correct object */
- if (base->selcol == (hitresult & 0xFFFF)) {
- bone = get_indexed_bone(base->object, hitresult);
-
- if (findunsel)
- sel = (bone->flag & BONE_SELECTED);
- else
- sel = !(bone->flag & BONE_SELECTED);
-
- data = bone;
- }
- else {
- data = NULL;
- sel = 0;
- }
- }
- else {
- bArmature *arm = obedit->data;
-
- ebone = BLI_findlink(arm->edbo, hitresult);
- if (findunsel)
- sel = (ebone->flag & BONE_SELECTED);
- else
- sel = !(ebone->flag & BONE_SELECTED);
-
- data = ebone;
- }
-
- if (data) {
- if (sel) {
- if (!firstSel) firstSel = data;
- takeNext = 1;
- }
- else {
- if (!firstunSel)
- firstunSel = data;
- if (takeNext)
- return data;
- }
- }
- }
- }
- }
-
- if (firstunSel)
- return firstunSel;
- else
- return firstSel;
-}
-
-
-
-/* used by posemode as well editmode */
-/* only checks scene->basact! */
-/* x and y are mouse coords (area space) */
-static void *get_nearest_bone(bContext *C, short findunsel, int x, int y)
-{
- ViewContext vc;
- rcti rect;
- unsigned int buffer[MAXPICKBUF];
- short hits;
-
- view3d_set_viewcontext(C, &vc);
-
- // rect.xmin = ... mouseco!
- rect.xmin = rect.xmax = x;
- rect.ymin = rect.ymax = y;
-
- glInitNames();
- hits = view3d_opengl_select(&vc, buffer, MAXPICKBUF, &rect);
-
- if (hits > 0)
- return get_bone_from_selectbuffer(vc.scene, vc.scene->basact, buffer, hits, findunsel);
-
- return NULL;
-}
-
-/* Get the first available child of an editbone */
-static EditBone *editbone_get_child(bArmature *arm, EditBone *pabone, short use_visibility)
-{
- EditBone *curbone, *chbone = NULL;
-
- for (curbone = arm->edbo->first; curbone; curbone = curbone->next) {
- if (curbone->parent == pabone) {
- if (use_visibility) {
- if ((arm->layer & curbone->layer) && !(pabone->flag & BONE_HIDDEN_A)) {
- chbone = curbone;
- }
- }
- else
- chbone = curbone;
- }
- }
-
- return chbone;
-}
-
-/* **************** END PoseMode & EditMode *************************** */
-/* **************** Posemode stuff ********************** */
-
-
-static void selectconnected_posebonechildren(Object *ob, Bone *bone, int extend)
-{
- Bone *curBone;
-
- /* stop when unconnected child is encontered, or when unselectable bone is encountered */
- if (!(bone->flag & BONE_CONNECTED) || (bone->flag & BONE_UNSELECTABLE))
- return;
-
- /* XXX old cruft! use notifiers instead */
- //select_actionchannel_by_name (ob->action, bone->name, !(shift));
-
- 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);
-}
-
-/* within active object context */
-/* previously known as "selectconnected_posearmature" */
-static int pose_select_connected_invoke(bContext *C, wmOperator *op, wmEvent *event)
-{
- Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C));
- bArmature *arm = (bArmature *)ob->data;
- Bone *bone, *curBone, *next = NULL;
- int extend = RNA_boolean_get(op->ptr, "extend");
-
- view3d_operator_needs_opengl(C);
-
- if (extend)
- bone = get_nearest_bone(C, 0, event->mval[0], event->mval[1]);
- else
- bone = get_nearest_bone(C, 1, event->mval[0], event->mval[1]);
-
- 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) {
- // XXX old cruft! use notifiers instead
- //select_actionchannel_by_name (ob->action, curBone->name, !(shift));
-
- 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(ob, curBone, extend);
-
- /* updates */
- WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob);
-
- if (arm->flag & ARM_HAS_VIZ_DEPS) {
- /* mask modifier ('armature' mode), etc. */
- DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
- }
-
- return OPERATOR_FINISHED;
-}
-
-static int pose_select_linked_poll(bContext *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";
-
- /* api callbacks */
- ot->exec = NULL;
- 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");
-}
-
-/* **************** END Posemode stuff ********************** */
-/* **************** EditMode stuff ********************** */
-
-/* called in space.c */
-/* previously "selectconnected_armature" */
-static int armature_select_linked_invoke(bContext *C, wmOperator *op, wmEvent *event)
-{
- bArmature *arm;
- EditBone *bone, *curBone, *next;
- int extend = RNA_boolean_get(op->ptr, "extend");
- Object *obedit = CTX_data_edit_object(C);
- arm = obedit->data;
-
- view3d_operator_needs_opengl(C);
-
- if (extend)
- bone = get_nearest_bone(C, 0, event->mval[0], event->mval[1]);
- else
- bone = get_nearest_bone(C, 1, event->mval[0], event->mval[1]);
-
- if (!bone)
- return OPERATOR_CANCELLED;
-
- /* Select parents */
- for (curBone = bone; curBone; curBone = next) {
- if ((curBone->flag & BONE_UNSELECTABLE) == 0) {
- if (extend) {
- 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 (extend)
- 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_sync_selection(arm->edbo);
-
- WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit);
-
- return OPERATOR_FINISHED;
-}
-
-static int armature_select_linked_poll(bContext *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";
-
- /* api callbacks */
- ot->exec = NULL;
- ot->invoke = armature_select_linked_invoke;
- ot->poll = armature_select_linked_poll;
-
- /* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-
- /* properties */
- RNA_def_boolean(ot->srna, "extend", FALSE, "Extend", "Extend selection instead of deselecting everything first");
-}
-
-/* does bones and points */
-/* note that BONE ROOT only gets drawn for root bones (or without IK) */
-static EditBone *get_nearest_editbonepoint(ViewContext *vc, const int mval[2],
- ListBase *edbo, int findunsel, int *selmask)
-{
- EditBone *ebone;
- rcti rect;
- unsigned int buffer[MAXPICKBUF];
- unsigned int hitresult, besthitresult = BONESEL_NOSEL;
- int i, mindep = 4;
- short hits;
-
- glInitNames();
-
- rect.xmin = mval[0] - 5;
- rect.xmax = mval[0] + 5;
- rect.ymin = mval[1] - 5;
- rect.ymax = mval[1] + 5;
-
- hits = view3d_opengl_select(vc, buffer, MAXPICKBUF, &rect);
- if (hits == 0) {
- rect.xmin = mval[0] - 12;
- rect.xmax = mval[0] + 12;
- rect.ymin = mval[1] - 12;
- rect.ymax = mval[1] + 12;
- hits = view3d_opengl_select(vc, buffer, MAXPICKBUF, &rect);
- }
- /* See if there are any selected bones in this group */
- if (hits > 0) {
-
- if (hits == 1) {
- if (!(buffer[3] & BONESEL_NOSEL))
- besthitresult = buffer[3];
- }
- else {
- for (i = 0; i < hits; i++) {
- hitresult = buffer[3 + (i * 4)];
- if (!(hitresult & BONESEL_NOSEL)) {
- int dep;
-
- ebone = BLI_findlink(edbo, hitresult & ~BONESEL_ANY);
-
- /* 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 = 2;
- }
- else {
- /* bone found */
- if (findunsel) {
- if ((ebone->flag & BONE_SELECTED) == 0)
- dep = 2;
- else
- dep = 3;
- }
- else dep = 3;
- }
- if (dep < mindep) {
- mindep = dep;
- besthitresult = hitresult;
- }
- }
- }
- }
-
- if (!(besthitresult & BONESEL_NOSEL)) {
-
- ebone = BLI_findlink(edbo, besthitresult & ~BONESEL_ANY);
-
- *selmask = 0;
- if (besthitresult & BONESEL_ROOT)
- *selmask |= BONE_ROOTSEL;
- if (besthitresult & BONESEL_TIP)
- *selmask |= BONE_TIPSEL;
- if (besthitresult & BONESEL_BONE)
- *selmask |= BONE_SELECTED;
- return ebone;
- }
- }
- *selmask = 0;
- return NULL;
-}
-
-/* previously delete_armature */
-/* only editmode! */
-static int armature_delete_selected_exec(bContext *C, wmOperator *UNUSED(op))
-{
- bArmature *arm;
- EditBone *curBone, *ebone_next;
- bConstraint *con;
- Object *obedit = CTX_data_edit_object(C); // XXX get from context
- arm = obedit->data;
-
- /* cancel if nothing selected */
- if (CTX_DATA_COUNT(C, selected_bones) == 0)
- return OPERATOR_CANCELLED;
-
- armature_select_mirrored(arm);
-
- /* First erase any associated pose channel */
- if (obedit->pose) {
- bPoseChannel *pchan, *pchan_next;
- for (pchan = obedit->pose->chanbase.first; pchan; pchan = pchan_next) {
- pchan_next = pchan->next;
- curBone = editbone_name_exists(arm->edbo, pchan->name);
-
- if (curBone && (curBone->flag & BONE_SELECTED) && (arm->layer & curBone->layer)) {
- BKE_pose_channel_free(pchan);
- BKE_pose_channels_hash_free(obedit->pose);
- BLI_freelinkN(&obedit->pose->chanbase, pchan);
- }
- else {
- for (con = pchan->constraints.first; con; con = con->next) {
- bConstraintTypeInfo *cti = BKE_constraint_get_typeinfo(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) {
- if (ct->tar == obedit) {
- if (ct->subtarget[0]) {
- curBone = editbone_name_exists(arm->edbo, ct->subtarget);
- if (curBone && (curBone->flag & BONE_SELECTED) && (arm->layer & curBone->layer)) {
- con->flag |= CONSTRAINT_DISABLE;
- ct->subtarget[0] = 0;
- }
- }
- }
- }
-
- if (cti->flush_constraint_targets)
- cti->flush_constraint_targets(con, &targets, 0);
- }
- }
- }
- }
- }
-
-
- 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_edit_bone_remove(arm, curBone);
- }
- }
- }
-
-
- ED_armature_sync_selection(arm->edbo);
-
- WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit);
-
- 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;
-}
-
-/* toggle==0: deselect
- * toggle==1: swap (based on test)
- * toggle==2: swap (no test), CURRENTLY UNUSED
- */
-void ED_armature_deselect_all(Object *obedit, int toggle)
-{
- bArmature *arm = obedit->data;
- EditBone *eBone;
- int sel = 1;
-
- if (toggle == 1) {
- /* Determine if there are any selected bones
- * and therefore whether we are selecting or deselecting */
- for (eBone = arm->edbo->first; eBone; eBone = eBone->next) {
- // if (arm->layer & eBone->layer) {
- if (eBone->flag & (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL)) {
- sel = 0;
- break;
- }
- // }
- }
- }
- else sel = toggle;
-
- /* Set the flags */
- for (eBone = arm->edbo->first; eBone; eBone = eBone->next) {
- if (sel == 2) {
- /* invert selection of bone */
- if (EBONE_VISIBLE(arm, eBone)) {
- eBone->flag ^= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
- if (arm->act_edbone == eBone)
- arm->act_edbone = NULL;
- }
- }
- else if (sel == 1) {
- /* select bone */
- if (EBONE_VISIBLE(arm, eBone)) {
- eBone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
- if (eBone->parent)
- eBone->parent->flag |= (BONE_TIPSEL);
- }
- }
- else {
- /* deselect bone */
- eBone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
- if (arm->act_edbone == eBone)
- arm->act_edbone = NULL;
- }
- }
-
- ED_armature_sync_selection(arm->edbo);
-}
-
-void ED_armature_deselect_all_visible(Object *obedit)
-{
- bArmature *arm = obedit->data;
- EditBone *ebone;
-
- for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
- /* first and foremost, bone must be visible and selected */
- if (EBONE_SELECTABLE(arm, ebone)) {
- ebone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
- }
- }
-
- ED_armature_sync_selection(arm->edbo);
-}
-
-/* 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);
- }
-}
-
-/* context: editmode armature in view3d */
-int mouse_armature(bContext *C, const int mval[2], int extend, int deselect, int toggle)
-{
- Object *obedit = CTX_data_edit_object(C);
- bArmature *arm = obedit->data;
- ViewContext vc;
- EditBone *nearBone = NULL;
- int selmask;
-
- view3d_set_viewcontext(C, &vc);
-
- BIF_sk_selectStroke(C, mval, extend);
-
- nearBone = get_nearest_editbonepoint(&vc, mval, arm->edbo, 1, &selmask);
- if (nearBone) {
-
- if (!extend && !deselect && !toggle)
- ED_armature_deselect_all(obedit, 0);
-
- /* 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_sync_selection(arm->edbo);
-
- if (nearBone) {
- /* then now check for active status */
- if (ebone_select_flag(nearBone)) {
- arm->act_edbone = nearBone;
- }
- }
-
- WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, vc.obedit);
- return 1;
- }
-
- return 0;
-}
-
-void ED_armature_edit_free(struct Object *ob)
-{
- bArmature *arm = ob->data;
- 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);
- }
- }
-
- BLI_freelistN(arm->edbo);
- }
- MEM_freeN(arm->edbo);
- arm->edbo = NULL;
- }
-}
-
-/* Put armature in EditMode */
-void ED_armature_to_edit(Object *ob)
-{
- bArmature *arm = ob->data;
-
- ED_armature_edit_free(ob);
- arm->edbo = MEM_callocN(sizeof(ListBase), "edbo armature");
- arm->act_edbone = make_boneList(arm->edbo, &arm->bonebase, NULL, arm->act_bone);
- arm->act_bone = NULL;
-
-// BIF_freeTemplates(); /* force template update when entering editmode */
-}
-
-
-/* adjust bone roll to align Z axis with vector
- * vec is in local space and is normalized
- */
-
-float ED_rollBoneToVector(EditBone *bone, const float align_axis[3], const short axis_only)
-{
- float mat[3][3], nor[3];
-
- sub_v3_v3v3(nor, bone->tail, bone->head);
- vec_roll_to_mat3(nor, 0.0f, mat);
-
- /* check the bone isn't aligned with the axis */
- if (!is_zero_v3(align_axis) && angle_v3v3(align_axis, mat[2]) > FLT_EPSILON) {
- float vec[3], align_axis_proj[3], roll;
-
- /* project the new_up_axis along the normal */
- project_v3_v3v3(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.0)) {
- negate_v3(align_axis_proj);
- }
- }
-
- roll = angle_v3v3(align_axis_proj, mat[2]);
-
- cross_v3_v3v3(vec, mat[2], align_axis_proj);
-
- if (dot_v3v3(vec, nor) < 0) {
- roll = -roll;
- }
-
- return roll;
- }
-
- return 0.0f;
-}
-
-
-static EnumPropertyItem prop_calc_roll_types[] = {
- {0, "X", 0, "X Axis", ""},
- {1, "Y", 0, "Y Axis", ""},
- {2, "Z", 0, "Z Axis", ""},
- {5, "ACTIVE", 0, "Active Bone", ""},
- {6, "VIEW", 0, "View Axis", ""},
- {7, "CURSOR", 0, "Cursor", ""},
- {0, NULL, 0, NULL, NULL}
-};
-
-
-static int armature_calc_roll_exec(bContext *C, wmOperator *op)
-{
- Object *ob = CTX_data_edit_object(C);
- const short type = RNA_enum_get(op->ptr, "type");
- const short axis_only = RNA_boolean_get(op->ptr, "axis_only");
- const short axis_flip = RNA_boolean_get(op->ptr, "axis_flip");
-
- float imat[3][3];
-
- bArmature *arm = ob->data;
- EditBone *ebone;
-
- copy_m3_m4(imat, ob->obmat);
- invert_m3(imat);
-
- if (type == 7) { /* Cursor */
- Scene *scene = CTX_data_scene(C);
- View3D *v3d = CTX_wm_view3d(C); /* can be NULL */
- float cursor_local[3];
- const float *cursor = give_cursor(scene, v3d);
-
-
- copy_v3_v3(cursor_local, cursor);
- mul_m3_v3(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);
- ebone->roll = ED_rollBoneToVector(ebone, cursor_rel, axis_only);
- }
- }
- }
- else {
- float vec[3] = {0.0f, 0.0f, 0.0f};
- if (type == 6) { /* View */
- RegionView3D *rv3d = CTX_wm_region_view3d(C);
- if (rv3d == NULL) {
- BKE_report(op->reports, RPT_ERROR, "No region view3d available");
- return OPERATOR_CANCELLED;
- }
-
- copy_v3_v3(vec, rv3d->viewinv[2]);
- mul_m3_v3(imat, vec);
- }
- else if (type == 5) {
- float mat[3][3], nor[3];
- ebone = (EditBone *)arm->act_edbone;
- if (ebone == NULL) {
- BKE_report(op->reports, RPT_ERROR, "No active bone set");
- return OPERATOR_CANCELLED;
- }
-
- sub_v3_v3v3(nor, ebone->tail, ebone->head);
- vec_roll_to_mat3(nor, ebone->roll, mat);
- copy_v3_v3(vec, mat[2]);
- }
- else { /* Axis */
- assert(type >= 0 && type <= 5);
- if (type < 3) vec[type] = 1.0f;
- else vec[type - 2] = -1.0f;
- mul_m3_v3(imat, 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_rollBoneToVector(ebone, vec, axis_only);
- }
- }
- }
-
- 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_bone_get_mirrored(arm->edbo, ebone);
- if (ebone_mirr && (EBONE_VISIBLE(arm, ebone_mirr) && EBONE_EDITABLE(ebone_mirr))) {
- ebone->roll = -ebone_mirr->roll;
- }
- }
- }
- }
-
- /* note, notifier might evolve */
- WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
-
- return OPERATOR_FINISHED;
-}
-
-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, 0, "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");
-}
-
-/* **************** undo for armatures ************** */
-
-typedef struct UndoArmature {
- EditBone *act_edbone;
- ListBase lb;
-} UndoArmature;
-
-static void undoBones_to_editBones(void *uarmv, void *armv, void *UNUSED(data))
-{
- UndoArmature *uarm = uarmv;
- bArmature *arm = armv;
- EditBone *ebo, *newebo;
-
- BLI_freelistN(arm->edbo);
-
- /* copy */
- for (ebo = uarm->lb.first; ebo; ebo = ebo->next) {
- newebo = MEM_dupallocN(ebo);
- ebo->temp = newebo;
- BLI_addtail(arm->edbo, newebo);
- }
-
- /* active bone */
- if (uarm->act_edbone) {
- ebo = uarm->act_edbone;
- arm->act_edbone = ebo->temp;
- }
- else
- arm->act_edbone = NULL;
-
- /* set pointers */
- for (newebo = arm->edbo->first; newebo; newebo = newebo->next) {
- if (newebo->parent) newebo->parent = newebo->parent->temp;
- }
- /* be sure they don't hang ever */
- for (newebo = arm->edbo->first; newebo; newebo = newebo->next) {
- newebo->temp = NULL;
- }
-}
-
-static void *editBones_to_undoBones(void *armv, void *UNUSED(obdata))
-{
- bArmature *arm = armv;
- UndoArmature *uarm;
- EditBone *ebo, *newebo;
-
- uarm = MEM_callocN(sizeof(UndoArmature), "listbase undo");
-
- /* copy */
- for (ebo = arm->edbo->first; ebo; ebo = ebo->next) {
- newebo = MEM_dupallocN(ebo);
- ebo->temp = newebo;
- BLI_addtail(&uarm->lb, newebo);
- }
-
- /* active bone */
- if (arm->act_edbone) {
- ebo = arm->act_edbone;
- uarm->act_edbone = ebo->temp;
- }
-
- /* set pointers */
- for (newebo = uarm->lb.first; newebo; newebo = newebo->next) {
- if (newebo->parent) newebo->parent = newebo->parent->temp;
- }
-
- return uarm;
-}
-
-static void free_undoBones(void *uarmv)
-{
- UndoArmature *uarm = uarmv;
-
- BLI_freelistN(&uarm->lb);
- MEM_freeN(uarm);
-}
-
-static void *get_armature_edit(bContext *C)
-{
- Object *obedit = CTX_data_edit_object(C);
- if (obedit && obedit->type == OB_ARMATURE) {
- return obedit->data;
- }
- return NULL;
-}
-
-/* and this is all the undo system needs to know */
-void undo_push_armature(bContext *C, const char *name)
-{
- // XXX solve getdata()
- undo_editmode_push(C, name, get_armature_edit, free_undoBones, undoBones_to_editBones, editBones_to_undoBones, NULL);
-}
-
-
-
-/* **************** END EditMode stuff ********************** */
-/* *************** Adding stuff in editmode *************** */
-
-/* default bone add, returns it selected, but without tail set */
-/* XXX should be used everywhere, now it mallocs bones still locally in functions */
-EditBone *ED_armature_edit_bone_add(bArmature *arm, const char *name)
-{
- EditBone *bone = MEM_callocN(sizeof(EditBone), "eBone");
-
- BLI_strncpy(bone->name, name, sizeof(bone->name));
- unique_editbone_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->ease1 = 1.0f;
- bone->ease2 = 1.0f;
- bone->rad_head = 0.10f;
- bone->rad_tail = 0.05f;
- bone->segments = 1;
- bone->layer = arm->layer;
-
- return bone;
-}
-
-/* v3d and rv3d are allowed to be NULL */
-void add_primitive_bone(Scene *scene, View3D *v3d, RegionView3D *rv3d)
-{
- Object *obedit = scene->obedit; // XXX get from context
- bArmature *arm = obedit->data;
- float obmat[3][3], curs[3], viewmat[3][3], totmat[3][3], imat[3][3];
- EditBone *bone;
-
- /* Get inverse point for head and orientation for tail */
- invert_m4_m4(obedit->imat, obedit->obmat);
- mul_v3_m4v3(curs, obedit->imat, give_cursor(scene, v3d));
-
- 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);
-
- ED_armature_deselect_all(obedit, 0);
-
- /* Create a bone */
- bone = ED_armature_edit_bone_add(arm, "Bone");
-
- arm->act_edbone = bone;
-
- 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
-}
-
-
-/* previously addvert_armature */
-/* the ctrl-click method */
-static int armature_click_extrude_exec(bContext *C, wmOperator *UNUSED(op))
-{
- View3D *v3d;
- bArmature *arm;
- EditBone *ebone, *newbone, *flipbone;
- float mat[3][3], imat[3][3];
- const float *curs;
- int a, to_root = 0;
- Object *obedit;
- Scene *scene;
-
- scene = CTX_data_scene(C);
- v3d = CTX_wm_view3d(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_deselect_all(obedit, 0);
-
- /* we re-use code for mirror editing... */
- flipbone = NULL;
- if (arm->flag & ARM_MIRROR_EDIT)
- flipbone = ED_armature_bone_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_edit_bone_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;
- }
-
- curs = give_cursor(scene, v3d);
- copy_v3_v3(newbone->tail, curs);
- 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_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, wmEvent *event)
-{
- /* 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 *fp, tvec[3], oldcurs[3], mval_f[2];
- int retv;
-
- scene = CTX_data_scene(C);
- ar = CTX_wm_region(C);
- v3d = CTX_wm_view3d(C);
-
- fp = give_cursor(scene, v3d);
-
- copy_v3_v3(oldcurs, fp);
-
- VECCOPY2D(mval_f, event->mval);
- ED_view3d_win_to_3d(ar, fp, mval_f, tvec);
- copy_v3_v3(fp, tvec);
-
- /* 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(fp, oldcurs);
-
- 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";
-
- /* 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;
-
- /* props */
-}
-
-/* adds an EditBone between the nominated locations (should be in the right space) */
-static EditBone *add_points_bone(Object *obedit, float head[], float tail[])
-{
- EditBone *ebo;
-
- ebo = ED_armature_edit_bone_add(obedit->data, "Bone");
-
- copy_v3_v3(ebo->head, head);
- copy_v3_v3(ebo->tail, tail);
-
- return ebo;
-}
-
-
-static EditBone *get_named_editbone(ListBase *edbo, char *name)
-{
- EditBone *eBone;
-
- if (name) {
- for (eBone = edbo->first; eBone; eBone = eBone->next) {
- if (!strcmp(name, eBone->name))
- return eBone;
- }
- }
-
- return NULL;
-}
-
-/* Call this before doing any duplications
- * */
-void preEditBoneDuplicate(ListBase *editbones)
-{
- EditBone *eBone;
-
- /* clear temp */
- for (eBone = editbones->first; eBone; eBone = eBone->next) {
- eBone->temp = 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)
-{
- /* 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?
- */
- bConstraintTypeInfo *cti = BKE_constraint_get_typeinfo(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) {
- newtarget = (EditBone *) oldtarget->temp;
- 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);
-}
-
-
-EditBone *duplicateEditBoneObjects(EditBone *curBone, const char *name, ListBase *editbones,
- Object *src_ob, Object *dst_ob)
-{
- EditBone *eBone = MEM_mallocN(sizeof(EditBone), "addup_editbone");
-
- /* Copy data from old bone to new bone */
- memcpy(eBone, curBone, sizeof(EditBone));
-
- curBone->temp = eBone;
- eBone->temp = curBone;
-
- if (name != NULL) {
- BLI_strncpy(eBone->name, name, sizeof(eBone->name));
- }
-
- unique_editbone_name(editbones, eBone->name, NULL);
- BLI_addtail(editbones, eBone);
-
- /* 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;
-
- 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);
- }
- }
- }
-
- return eBone;
-}
-
-EditBone *duplicateEditBone(EditBone *curBone, const char *name, ListBase *editbones, Object *ob)
-{
- return duplicateEditBoneObjects(curBone, name, editbones, ob, ob);
-}
-
-/* previously adduplicate_armature */
-static int armature_duplicate_selected_exec(bContext *C, wmOperator *UNUSED(op))
-{
- bArmature *arm;
- EditBone *eBone = NULL;
- EditBone *curBone;
- EditBone *firstDup = NULL; /* The beginning of the duplicated bones in the edbo list */
-
- Object *obedit = CTX_data_edit_object(C);
- arm = obedit->data;
-
- /* cancel if nothing selected */
- if (CTX_DATA_COUNT(C, selected_bones) == 0)
- return OPERATOR_CANCELLED;
-
- ED_armature_sync_selection(arm->edbo); // XXX why is this needed?
-
- preEditBoneDuplicate(arm->edbo);
-
- /* Select mirrored bones */
- if (arm->flag & ARM_MIRROR_EDIT) {
- for (curBone = arm->edbo->first; curBone; curBone = curBone->next) {
- if (EBONE_VISIBLE(arm, curBone)) {
- if (curBone->flag & BONE_SELECTED) {
- eBone = ED_armature_bone_get_mirrored(arm->edbo, curBone);
- if (eBone)
- eBone->flag |= BONE_SELECTED;
- }
- }
- }
- }
-
-
- /* Find the selected bones and duplicate them as needed */
- for (curBone = arm->edbo->first; curBone && curBone != firstDup; curBone = curBone->next) {
- if (EBONE_VISIBLE(arm, curBone)) {
- if (curBone->flag & BONE_SELECTED) {
-
- eBone = duplicateEditBone(curBone, curBone->name, arm->edbo, obedit);
-
- if (!firstDup)
- firstDup = eBone;
-
- }
- }
- }
-
- /* Run though the list and fix the pointers */
- for (curBone = arm->edbo->first; curBone && curBone != firstDup; curBone = curBone->next) {
- if (EBONE_VISIBLE(arm, curBone)) {
- if (curBone->flag & BONE_SELECTED) {
- eBone = (EditBone *) curBone->temp;
-
- if (!curBone->parent) {
- /* If this bone has no parent,
- * Set the duplicate->parent to NULL
- */
- eBone->parent = NULL;
- }
- else if (curBone->parent->temp) {
- /* If this bone has a parent that was duplicated,
- * Set the duplicate->parent to the curBone->parent->temp
- */
- eBone->parent = (EditBone *)curBone->parent->temp;
- }
- else {
- /* If this bone has a parent that IS not selected,
- * Set the duplicate->parent to the curBone->parent
- */
- eBone->parent = (EditBone *) curBone->parent;
- eBone->flag &= ~BONE_CONNECTED;
- }
-
- /* Lets try to fix any constraint subtargets that might
- * have been duplicated
- */
- updateDuplicateSubtarget(eBone, arm->edbo, obedit);
- }
- }
- }
-
- /* correct the active bone */
- if (arm->act_edbone) {
- eBone = arm->act_edbone;
- if (eBone->temp)
- arm->act_edbone = eBone->temp;
- }
-
- /* Deselect the old bones and select the new ones */
- for (curBone = arm->edbo->first; curBone && curBone != firstDup; curBone = curBone->next) {
- if (EBONE_VISIBLE(arm, curBone))
- curBone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
- }
-
- ED_armature_validate_active(arm);
-
- WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit);
-
- 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;
-}
-
-
-/* *************** END Adding stuff in editmode *************** */
-/* ************** Add/Remove stuff in editmode **************** */
-
-/* temporary data-structure for merge/fill bones */
-typedef struct EditBonePoint {
- 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 */
-
- 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);
- }
-}
-
-/* --------------------- */
-
-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);
- }
-}
-
-/* bone adding between selected joints */
-static int armature_fill_bones_exec(bContext *C, wmOperator *op)
-{
- Object *obedit = CTX_data_edit_object(C);
- bArmature *arm = (obedit) ? obedit->data : NULL;
- Scene *scene = CTX_data_scene(C);
- View3D *v3d = CTX_wm_view3d(C);
- ListBase points = {NULL, NULL};
- int count;
-
- /* sanity checks */
- if (ELEM(NULL, obedit, arm))
- return OPERATOR_CANCELLED;
-
- /* loop over all bones, and only consider if visible */
- CTX_DATA_BEGIN(C, EditBone *, ebone, visible_bones)
- {
- if (!(ebone->flag & BONE_CONNECTED) && (ebone->flag & BONE_ROOTSEL))
- fill_add_joint(ebone, 0, &points);
- if (ebone->flag & BONE_TIPSEL)
- fill_add_joint(ebone, 1, &points);
- }
- 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/hierachy)
- * 3+) error (a smarter method involving finding chains needs to be worked out
- */
- count = BLI_countlist(&points);
-
- if (count == 0) {
- BKE_report(op->reports, RPT_ERROR, "No joints selected");
- return OPERATOR_CANCELLED;
- }
- else if (count == 1) {
- EditBonePoint *ebp;
- float curs[3];
-
- /* Get Points - selected joint */
- ebp = (EditBonePoint *)points.first;
-
- /* Get points - cursor (tail) */
- invert_m4_m4(obedit->imat, obedit->obmat);
- mul_v3_m4v3(curs, obedit->imat, give_cursor(scene, v3d));
-
- /* Create a bone */
- /* newbone = */ add_points_bone(obedit, ebp->vec, curs);
- }
- else if (count == 2) {
- EditBonePoint *ebp, *ebp2;
- float head[3], tail[3];
- short headtail = 0;
-
- /* check that the points don't belong to the same bone */
- ebp = (EditBonePoint *)points.first;
- ebp2 = ebp->next;
-
- if ((ebp->head_owner == ebp2->tail_owner) && (ebp->head_owner != NULL)) {
- BKE_report(op->reports, RPT_ERROR, "Same bone selected...");
- BLI_freelistN(&points);
- return OPERATOR_CANCELLED;
- }
- if ((ebp->tail_owner == ebp2->head_owner) && (ebp->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->head_owner && ebp2->head_owner) || (ebp->tail_owner && ebp2->tail_owner)) {
- /* rule: whichever one is closer to 3d-cursor */
- float curs[3];
- float vecA[3], vecB[3];
- float distA, distB;
-
- /* get cursor location */
- invert_m4_m4(obedit->imat, obedit->obmat);
- mul_v3_m4v3(curs, obedit->imat, give_cursor(scene, v3d));
-
- /* get distances */
- sub_v3_v3v3(vecA, ebp->vec, curs);
- sub_v3_v3v3(vecB, ebp2->vec, curs);
- distA = len_v3(vecA);
- distB = len_v3(vecB);
-
- /* compare distances - closer one therefore acts as direction for bone to go */
- headtail = (distA < distB) ? 2 : 1;
- }
- else if (ebp->head_owner) {
- headtail = 1;
- }
- else if (ebp2->head_owner) {
- headtail = 2;
- }
-
- /* assign head/tail combinations */
- if (headtail == 2) {
- copy_v3_v3(head, ebp->vec);
- copy_v3_v3(tail, ebp2->vec);
- }
- else if (headtail == 1) {
- copy_v3_v3(head, ebp2->vec);
- copy_v3_v3(tail, ebp->vec);
- }
-
- /* add new bone and parent it to the appropriate end */
- if (headtail) {
- EditBone *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->tail_owner)
- newbone->parent = ebp->tail_owner;
- else
- newbone->parent = ebp->head_owner;
- }
- else {
- /* ebp2 tail or head - tail gets priority */
- if (ebp2->tail_owner)
- newbone->parent = ebp2->tail_owner;
- else
- newbone->parent = ebp2->head_owner;
- }
-
- newbone->flag |= BONE_CONNECTED;
- }
- }
- else {
- /* FIXME.. figure out a method for multiple bones */
- BKE_reportf(op->reports, RPT_ERROR, "Too many points selected: %d", count);
- BLI_freelistN(&points);
- return OPERATOR_CANCELLED;
- }
-
- /* 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";
-
- /* callbacks */
- ot->exec = armature_fill_bones_exec;
- ot->poll = ED_operator_editarmature;
-
- /* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-}
-
-/* --------------------- */
-
-/* this function merges between two bones, removes them and those in-between,
- * and adjusts the parent relationships for those in-between
- */
-static void bones_merge(Object *obedit, EditBone *start, EditBone *end, EditBone *endchild, ListBase *chains)
-{
- bArmature *arm = obedit->data;
- EditBone *ebo, *ebone, *newbone;
- LinkData *chain;
- float head[3], tail[3];
-
- /* check if same bone */
- if (start == end) {
- if (G.debug & G_DEBUG) {
- printf("Error: same bone!\n");
- printf("\tstart = %s, end = %s\n", start->name, end->name);
- }
- }
-
- /* step 1: add a new bone
- * - head = head/tail of start (default head)
- * - tail = head/tail of end (default tail)
- * - parent = parent of start
- */
- if ((start->flag & BONE_TIPSEL) && (start->flag & BONE_SELECTED) == 0) {
- copy_v3_v3(head, start->tail);
- }
- else {
- copy_v3_v3(head, start->head);
- }
- if ((end->flag & BONE_ROOTSEL) && (end->flag & BONE_SELECTED) == 0) {
- copy_v3_v3(tail, end->head);
- }
- else {
- copy_v3_v3(tail, end->tail);
- }
- newbone = add_points_bone(obedit, head, tail);
- newbone->parent = start->parent;
-
- /* TODO, copy more things to the new bone */
- newbone->flag = start->flag & (BONE_HINGE | BONE_NO_DEFORM | BONE_NO_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_sync_selection(arm->edbo);
-}
-
-
-static int armature_merge_exec(bContext *C, wmOperator *op)
-{
- Object *obedit = CTX_data_edit_object(C);
- bArmature *arm = (obedit) ? obedit->data : NULL;
- short type = RNA_enum_get(op->ptr, "type");
-
- /* sanity checks */
- if (ELEM(NULL, obedit, arm))
- return OPERATOR_CANCELLED;
-
- /* 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 (chains.first == NULL) return OPERATOR_CANCELLED;
-
- /* 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 continous 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_sync_selection(arm->edbo);
- WM_event_add_notifier(C, NC_OBJECT | ND_POSE, obedit);
-
- return OPERATOR_FINISHED;
-}
-
-void ARMATURE_OT_merge(wmOperatorType *ot)
-{
- static 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", "");
-}
-
-/* ************** END Add/Remove stuff in editmode ************ */
-/* *************** Tools in editmode *********** */
-
-static int armature_hide_exec(bContext *C, wmOperator *op)
-{
- Object *obedit = CTX_data_edit_object(C);
- bArmature *arm = obedit->data;
- EditBone *ebone;
- 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;
-
- for (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;
- }
- }
- }
- ED_armature_validate_active(arm);
- ED_armature_sync_selection(arm->edbo);
-
- WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit);
-
- return OPERATOR_FINISHED;
-}
-
-void ARMATURE_OT_hide(wmOperatorType *ot)
-{
- /* identifiers */
- ot->name = "Hide Selected Bones";
- 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;
-
- /* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-
- /* props */
- RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected");
-}
-
-static int armature_reveal_exec(bContext *C, wmOperator *UNUSED(op))
-{
- Object *obedit = CTX_data_edit_object(C);
- bArmature *arm = obedit->data;
- EditBone *ebone;
-
- for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
- if (arm->layer & ebone->layer) {
- if (ebone->flag & BONE_HIDDEN_A) {
- ebone->flag |= (BONE_TIPSEL | BONE_SELECTED | BONE_ROOTSEL);
- ebone->flag &= ~BONE_HIDDEN_A;
- }
- }
- }
- ED_armature_validate_active(arm);
- ED_armature_sync_selection(arm->edbo);
-
- WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit);
-
- return OPERATOR_FINISHED;
-}
-
-void ARMATURE_OT_reveal(wmOperatorType *ot)
-{
- /* identifiers */
- ot->name = "Reveal Bones";
- ot->idname = "ARMATURE_OT_reveal";
- ot->description = "Unhide all bones that have been tagged to be hidden in Edit Mode";
-
- /* api callbacks */
- ot->exec = armature_reveal_exec;
- ot->poll = ED_operator_editarmature;
-
- /* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-
-}
-#if 0 // remove this?
-static void hide_selected_armature_bones(Scene *scene)
-{
- Object *obedit = scene->obedit; // XXX get from context
- bArmature *arm = obedit->data;
- EditBone *ebone;
-
- for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
- if (EBONE_VISIBLE(arm, ebone)) {
- if (ebone->flag & BONE_SELECTED) {
- ebone->flag &= ~(BONE_TIPSEL | BONE_SELECTED | BONE_ROOTSEL);
- ebone->flag |= BONE_HIDDEN_A;
- }
- }
- }
- ED_armature_validate_active(arm);
- ED_armature_sync_selection(arm->edbo);
-}
-
-static void hide_unselected_armature_bones(Scene *scene)
-{
- Object *obedit = scene->obedit; // XXX get from context
- bArmature *arm = obedit->data;
- EditBone *ebone;
-
- for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
- bArmature *arm = obedit->data;
- if (EBONE_VISIBLE(arm, ebone)) {
- if (ebone->flag & (BONE_TIPSEL | BONE_SELECTED | BONE_ROOTSEL)) {
- /* pass */
- }
- else {
- ebone->flag |= BONE_HIDDEN_A;
- }
- }
- }
-
- ED_armature_validate_active(arm);
- ED_armature_sync_selection(arm->edbo);
-}
-
-void show_all_armature_bones(Scene *scene)
-{
- Object *obedit = scene->obedit; // XXX get from context
- bArmature *arm = obedit->data;
- EditBone *ebone;
-
- for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
- if (arm->layer & ebone->layer) {
- if (ebone->flag & BONE_HIDDEN_A) {
- ebone->flag |= (BONE_TIPSEL | BONE_SELECTED | BONE_ROOTSEL);
- ebone->flag &= ~BONE_HIDDEN_A;
- }
- }
- }
- ED_armature_validate_active(arm);
- ED_armature_sync_selection(arm->edbo);
-}
-#endif
-
-/* previously extrude_armature */
-/* context; editmode armature */
-/* if forked && mirror-edit: makes two bones with flipped names */
-static int armature_extrude_exec(bContext *C, wmOperator *op)
-{
- Object *obedit;
- bArmature *arm;
- EditBone *newbone, *ebone, *flipbone, *first = NULL;
- int a, totbone = 0, do_extrude;
- int forked = RNA_boolean_get(op->ptr, "forked");
-
- obedit = CTX_data_edit_object(C);
- arm = obedit->data;
-
- /* 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_bone_get_mirrored(arm->edbo, ebone);
- if (flipbone) {
- forked = 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))
- 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;
-
- newbone->flag = ebone->flag & (BONE_TIPSEL | BONE_RELATIVE_PARENTING); // copies it, in case mirrored bone
-
- 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->ease1 = ebone->ease1;
- newbone->ease2 = ebone->ease2;
- newbone->rad_head = ebone->rad_tail; // don't copy entire bone...
- newbone->rad_tail = ebone->rad_tail;
- newbone->segments = 1;
- newbone->layer = ebone->layer;
-
- BLI_strncpy(newbone->name, ebone->name, sizeof(newbone->name));
-
- if (flipbone && forked) { // only set if mirror edit
- if (strlen(newbone->name) < 30) {
- if (a == 0) strcat(newbone->name, "_L");
- else strcat(newbone->name, "_R");
- }
- }
- unique_editbone_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;
-
- if (totbone == 0) return OPERATOR_CANCELLED;
-
- /* Transform the endpoints */
- ED_armature_sync_selection(arm->edbo);
-
- WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit);
-
- return OPERATOR_FINISHED;
-}
-
-void ARMATURE_OT_extrude(wmOperatorType *ot)
-{
- /* 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;
-
- /* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-
- /* props */
- RNA_def_boolean(ot->srna, "forked", 0, "Forked", "");
-}
-/* ********************** Bone Add ********************/
-
-/*op makes a new bone and returns it with its tip selected */
-
-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];
-
- RNA_string_get(op->ptr, "name", name);
-
- copy_v3_v3(curs, give_cursor(CTX_data_scene(C), CTX_wm_view3d(C)));
-
- /* 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);
-
- copy_m3_m4(viewmat, obedit->obmat);
- mul_m3_m3m3(totmat, obmat, viewmat);
- invert_m3_m3(imat, totmat);
-
- ED_armature_deselect_all(obedit, 0);
-
- /* Create a bone */
- bone = ED_armature_edit_bone_add(obedit->data, name);
-
- 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
-
- /* note, notifier might evolve */
- WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit);
-
- 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;
-
- /* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-
- RNA_def_string(ot->srna, "name", "Bone", MAXBONENAME, "Name", "Name of the newly created bone");
-
-}
-
-
-/* ----------- */
-
-/* Subdivide Operators:
- * This group of operators all use the same 'exec' callback, but they are called
- * through several different operators - a combined menu (which just calls the exec in the
- * appropriate ways), and two separate ones.
- */
-
-static int armature_subdivide_exec(bContext *C, wmOperator *op)
-{
- Object *obedit = CTX_data_edit_object(C);
- bArmature *arm = obedit->data;
- 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(C, EditBone *, ebone, selected_editable_bones)
- {
- 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 = 0.5f * (ebone->rad_head + ebone->rad_tail);
- ebone->rad_tail = newbone->rad_head;
-
- newbone->flag |= BONE_CONNECTED;
-
- unique_editbone_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;
-
- /* 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;
-
- /* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-
- /* Properties */
- prop = RNA_def_int(ot->srna, "number_cuts", 1, 1, INT_MAX, "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);
-}
-
-/* ----------- */
-
-/* Switch Direction operator:
- * Currently, this does not use context loops, as context loops do not make it
- * easy to retrieve any hierarchical/chain relationships which are necessary for
- * this to be done easily.
- */
-
-/* helper to clear BONE_TRANSFORM flags */
-static void armature_clear_swap_done_flags(bArmature *arm)
-{
- EditBone *ebone;
-
- for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
- ebone->flag &= ~BONE_TRANSFORM;
- }
-}
-
-static int armature_switch_direction_exec(bContext *C, wmOperator *UNUSED(op))
-{
- Object *ob = CTX_data_edit_object(C);
- bArmature *arm = (bArmature *)ob->data;
- ListBase chains = {NULL, NULL};
- LinkData *chain;
-
- /* get chains of bones (ends on chains) */
- chains_find_tips(arm->edbo, &chains);
- if (chains.first == NULL) return OPERATOR_CANCELLED;
-
- /* ensure that mirror bones will also be operated on */
- armature_tag_select_mirrored(arm);
-
- /* clear BONE_TRANSFORM flags
- * - used to prevent duplicate/cancelling 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(float, ebo->head[0], ebo->tail[0]);
- SWAP(float, ebo->head[1], ebo->tail[1]);
- SWAP(float, ebo->head[2], ebo->tail[2]);
-
- /* 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);
-
- 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)";
-
- /* api callbacks */
- ot->exec = armature_switch_direction_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
-
-/* 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;
-}
-
-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;
- }
-}
-
-static EnumPropertyItem prop_editarm_make_parent_types[] = {
- {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_bone_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), wmEvent *UNUSED(event))
-{
- EditBone *actbone = CTX_data_active_bone(C);
- uiPopupMenu *pup = uiPupMenuBegin(C, CTX_IFACE_(BLF_I18NCONTEXT_OPERATOR_DEFAULT, "Make Parent"), ICON_NONE);
- uiLayout *layout = uiPupMenuLayout(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;
-
- 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);
-
- uiPupMenuEnd(C, pup);
-
- return OPERATOR_CANCELLED;
-}
-
-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";
-
- /* 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;
-
- RNA_def_enum(ot->srna, "type", prop_editarm_make_parent_types, 0, "ParentType", "Type of parenting");
-}
-
-static EnumPropertyItem prop_editarm_clear_parent_types[] = {
- {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;
-}
-
-static int armature_parent_clear_exec(bContext *C, wmOperator *op)
-{
- Object *ob = CTX_data_edit_object(C);
- bArmature *arm = (bArmature *)ob->data;
- 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;
-
- ED_armature_sync_selection(arm->edbo);
-
- /* note, notifier might evolve */
- WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob);
-
- 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");
-}
-
-/* **************** Selections ******************/
-
-static int armature_select_inverse_exec(bContext *C, wmOperator *UNUSED(op))
-{
- /* Set the flags */
- CTX_DATA_BEGIN(C, EditBone *, ebone, visible_bones)
- {
- /* ignore bone if selection can't change */
- if ((ebone->flag & BONE_UNSELECTABLE) == 0) {
- /* select bone */
- ebone->flag ^= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
- }
- }
- CTX_DATA_END;
-
- WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, NULL);
-
- return OPERATOR_FINISHED;
-}
-
-void ARMATURE_OT_select_inverse(wmOperatorType *ot)
-{
- /* identifiers */
- ot->name = "Select Inverse";
- ot->idname = "ARMATURE_OT_select_inverse";
- ot->description = "Flip the selection status of bones (selected -> unselected, unselected -> selected)";
-
- /* api callbacks */
- ot->exec = armature_select_inverse_exec;
- ot->poll = ED_operator_editarmature;
-
- /* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-
-}
-static int armature_de_select_all_exec(bContext *C, wmOperator *op)
-{
- int action = RNA_enum_get(op->ptr, "action");
-
- if (action == SEL_TOGGLE) {
- action = SEL_SELECT;
- /* Determine if there are any selected bones
- * And therefore whether we are selecting or deselecting */
- if (CTX_DATA_COUNT(C, selected_bones) > 0)
- action = SEL_DESELECT;
- }
-
- /* Set the flags */
- CTX_DATA_BEGIN(C, EditBone *, ebone, visible_bones)
- {
- /* ignore bone if selection can't change */
- if ((ebone->flag & BONE_UNSELECTABLE) == 0) {
- switch (action) {
- case SEL_SELECT:
- 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 {
- 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";
-
- /* api callbacks */
- ot->exec = armature_de_select_all_exec;
- ot->poll = ED_operator_editarmature;
-
- /* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-
- WM_operator_properties_select_all(ot);
-}
-
-enum {
- SIMEDBONE_LENGTH = 1,
- SIMEDBONE_DIRECTION,
- SIMEDBONE_PREFIX,
- SIMEDBONE_SUFFIX,
- SIMEDBONE_LAYER
-};
-
-static EnumPropertyItem prop_similar_types[] = {
- {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", ""},
- {0, NULL, 0, NULL, NULL}
-};
-
-/* could be used in more places */
-static void ED_armature_edit_bone_select(EditBone *ebone)
-{
- BLI_assert((ebone->flag & BONE_UNSELECTABLE) == 0);
- ebone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
-
- if ((ebone->flag & BONE_CONNECTED) && (ebone->parent != NULL)) {
- ebone->parent->flag |= BONE_TIPSEL;
- }
-}
-
-static void select_similar_length(bArmature *arm, EditBone *ebone_act, const float thresh)
-{
- EditBone *ebone;
-
- /* thresh is always relative to current length */
- const float len_min = ebone_act->length / (1.0f + thresh);
- const float len_max = ebone_act->length * (1.0f + thresh);
-
- for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
- if (EBONE_SELECTABLE(arm, ebone)) {
- if ((ebone->length >= len_min) &&
- (ebone->length <= len_max))
- {
- ED_armature_edit_bone_select(ebone);
- }
- }
- }
-}
-
-static void select_similar_direction(bArmature *arm, EditBone *ebone_act, const float thresh)
-{
- EditBone *ebone;
- float dir_act[3];
- sub_v3_v3v3(dir_act, ebone_act->head, ebone_act->tail);
-
- for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
- if (EBONE_SELECTABLE(arm, ebone)) {
- float dir[3];
- sub_v3_v3v3(dir, ebone->head, ebone->tail);
-
- if (angle_v3v3(dir_act, dir) / (float)M_PI < thresh) {
- ED_armature_edit_bone_select(ebone);
- }
- }
- }
-}
-
-static void select_similar_layer(bArmature *arm, EditBone *ebone_act)
-{
- EditBone *ebone;
-
- for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
- if (EBONE_SELECTABLE(arm, ebone)) {
- if (ebone->layer & ebone_act->layer) {
- ED_armature_edit_bone_select(ebone);
- }
- }
- }
-}
-
-static void select_similar_prefix(bArmature *arm, EditBone *ebone_act)
-{
- EditBone *ebone;
-
- char body_tmp[MAX_VGROUP_NAME];
- char prefix_act[MAX_VGROUP_NAME];
-
- BKE_deform_split_prefix(ebone_act->name, prefix_act, body_tmp);
-
- if (prefix_act[0] == '\0')
- return;
-
- /* Find matches */
- for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
- if (EBONE_SELECTABLE(arm, ebone)) {
- char prefix_other[MAX_VGROUP_NAME];
- BKE_deform_split_prefix(ebone->name, prefix_other, body_tmp);
- if (!strcmp(prefix_act, prefix_other)) {
- ED_armature_edit_bone_select(ebone);
- }
- }
- }
-}
-
-static void select_similar_suffix(bArmature *arm, EditBone *ebone_act)
-{
- EditBone *ebone;
-
- char body_tmp[MAX_VGROUP_NAME];
- char suffix_act[MAX_VGROUP_NAME];
-
- BKE_deform_split_suffix(ebone_act->name, body_tmp, suffix_act);
-
- if (suffix_act[0] == '\0')
- return;
-
- /* Find matches */
- for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
- if (EBONE_SELECTABLE(arm, ebone)) {
- char suffix_other[MAX_VGROUP_NAME];
- BKE_deform_split_suffix(ebone->name, body_tmp, suffix_other);
- if (!strcmp(suffix_act, suffix_other)) {
- ED_armature_edit_bone_select(ebone);
- }
- }
- }
-}
-
-static int armature_select_similar_exec(bContext *C, wmOperator *op)
-{
- Object *obedit = CTX_data_edit_object(C);
- bArmature *arm = obedit->data;
- EditBone *ebone_act = CTX_data_active_bone(C);
-
- /* Get props */
- int type = RNA_enum_get(op->ptr, "type");
- float thresh = RNA_float_get(op->ptr, "threshold");
-
- /* Check for active bone */
- if (ebone_act == NULL) {
- BKE_report(op->reports, RPT_ERROR, "Operation requires an active bone");
- return OPERATOR_CANCELLED;
- }
-
- switch (type) {
- case SIMEDBONE_LENGTH:
- select_similar_length(arm, ebone_act, thresh);
- break;
- case SIMEDBONE_DIRECTION:
- select_similar_direction(arm, ebone_act, thresh);
- break;
- case SIMEDBONE_PREFIX:
- select_similar_prefix(arm, ebone_act);
- break;
- case SIMEDBONE_SUFFIX:
- select_similar_suffix(arm, ebone_act);
- break;
- case SIMEDBONE_LAYER:
- select_similar_layer(arm, ebone_act);
- break;
- }
-
- WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit);
-
- 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, 0, "Type", "");
- RNA_def_float(ot->srna, "threshold", 0.1f, 0.0f, 1.0f, "Threshold", "", 0.0f, 1.0f);
-}
-
-/* ********************* select hierarchy operator ************** */
-
-static int armature_select_hierarchy_exec(bContext *C, wmOperator *op)
-{
- Object *obedit = CTX_data_edit_object(C);
- Object *ob;
- bArmature *arm;
- EditBone *curbone, *pabone, *chbone;
- int direction = RNA_enum_get(op->ptr, "direction");
- int add_to_sel = RNA_boolean_get(op->ptr, "extend");
-
- ob = obedit;
- arm = (bArmature *)ob->data;
-
- for (curbone = arm->edbo->first; curbone; curbone = curbone->next) {
- /* only work on bone if it is visible and its selection can change */
- if (EBONE_SELECTABLE(arm, curbone)) {
- if (curbone == arm->act_edbone) {
- if (direction == BONE_SELECT_PARENT) {
- if (curbone->parent == NULL) continue;
- else pabone = curbone->parent;
-
- if (EBONE_VISIBLE(arm, pabone)) {
- pabone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
- arm->act_edbone = pabone;
- if (pabone->parent) pabone->parent->flag |= BONE_TIPSEL;
-
- if (!add_to_sel) curbone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
- break;
- }
-
- }
- else { // BONE_SELECT_CHILD
- chbone = editbone_get_child(arm, curbone, 1);
- if (chbone == NULL) continue;
-
- if (EBONE_SELECTABLE(arm, chbone)) {
- chbone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
- arm->act_edbone = chbone;
-
- if (!add_to_sel) {
- curbone->flag &= ~(BONE_SELECTED | BONE_ROOTSEL);
- if (curbone->parent) curbone->parent->flag &= ~BONE_TIPSEL;
- }
- break;
- }
- }
- }
- }
- }
-
- ED_armature_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 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");
-}
-
-/* ***************** EditBone Alignment ********************* */
-
-/* helper to fix a ebone position if its parent has moved due to alignment*/
-static void fix_connected_bone(EditBone *ebone)
-{
- float diff[3];
-
- 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);
- return;
-}
-
-/* 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);
- }
- }
- return;
-}
-
-static void bone_align_to_bone(ListBase *edbo, EditBone *selbone, EditBone *actbone)
-{
- float selboneaxis[3], actboneaxis[3], length;
-
- sub_v3_v3v3(actboneaxis, actbone->tail, actbone->head);
- normalize_v3(actboneaxis);
-
- 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;
-
- /* 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);
- return;
-}
-
-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;
-
- /* 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_bone_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,
- * 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);
- }
- }
- 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;
- }
-
-
- /* note, notifier might evolve */
- WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, 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)";
-
- /* api callbacks */
- ot->invoke = WM_operator_confirm;
- ot->exec = armature_align_bones_exec;
- ot->poll = ED_operator_editarmature;
-
- /* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-}
-
-/* ***************** Pose tools ********************* */
-
-/* XXX bone_looper is only to be used when we want to access settings
- * (i.e. editability/visibility/selected) that context doesn't offer */
-static 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;
-
- 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 next bone at this
- * depth of the recursion.
- */
- count += bone_looper(ob, bone->next, data, bone_func);
- }
-
- return count;
-}
-
-/* called from editview.c, for mode-less pose selection */
-/* assumes scene obact and basact is still on old situation */
-int ED_do_pose_selectbuffer(Scene *scene, Base *base, unsigned int *buffer, short hits,
- short extend, short deselect, short toggle)
-{
- Object *ob = base->object;
- Bone *nearBone;
-
- if (!ob || !ob->pose) return 0;
-
- nearBone = get_bone_from_selectbuffer(scene, base, buffer, hits, 1);
-
- /* if the bone cannot be affected, don't do anything */
- if ((nearBone) && !(nearBone->flag & BONE_UNSELECTABLE)) {
- Object *ob_act = OBACT;
- 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 ((!extend && !deselect && !toggle) ||
- ((ob_act && (ob_act != ob) && (ob_act->mode & OB_MODE_WEIGHT_PAINT) == 0)))
- {
- ED_pose_deselectall(ob, 0);
- 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);
- DAG_id_tag_update(&ob_act->id, OB_RECALC_DATA);
- }
- }
- /* 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) {
- DAG_id_tag_update(&ob_act->id, OB_RECALC_DATA);
- }
- }
- }
-
- return nearBone != NULL;
-}
-
-/* test==0: deselect all
- * test==1: swap select (apply to all the opposite of current situation)
- * test==2: only clear active tag
- * test==3: swap select (no test / inverse selection status of all independently)
- */
-void ED_pose_deselectall(Object *ob, int test)
-{
- bArmature *arm = ob->data;
- bPoseChannel *pchan;
- int selectmode = 0;
-
- /* we call this from outliner too */
- if (ob->pose == NULL) {
- return;
- }
-
- /* Determine if we're selecting or deselecting */
- if (test == 1) {
- for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
- if (PBONE_VISIBLE(arm, pchan->bone)) {
- if (pchan->bone->flag & BONE_SELECTED)
- break;
- }
- }
-
- if (pchan == NULL)
- selectmode = 1;
- }
- else if (test == 2)
- selectmode = 2;
-
- /* Set the flags accordingly */
- 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 ((pchan->bone->layer & arm->layer) && !(pchan->bone->flag & (BONE_HIDDEN_P | BONE_UNSELECTABLE))) {
- if (test == 3) {
- pchan->bone->flag ^= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
- }
- else {
- if (selectmode == 0) pchan->bone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
- else if (selectmode == 1) pchan->bone->flag |= BONE_SELECTED;
- }
- }
- }
-}
-
-static int bone_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) 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; } *data = datap;
-
- if (!(ob->mode & OB_MODE_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 aleady exist the routine exits.
- */
- if (!(bone->flag & BONE_NO_DEFORM)) {
- if (!defgroup_find_name(ob, bone->name)) {
- ED_vgroup_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; } *data = datap;
- int wpmode = (ob->mode & OB_MODE_WEIGHT_PAINT);
- bArmature *arm = data->armob->data;
-
- if (!wpmode || !(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 (!wpmode || ((arm->layer & bone->layer) && (bone->flag & BONE_SELECTED)))
- if (!(defgroup = defgroup_find_name(ob, bone->name)))
- defgroup = ED_vgroup_add_name(ob, bone->name);
-
- if (data->list != NULL) {
- hgroup = (bDeformGroup ***) &data->list;
-
- for (a = 0; a < segments; a++) {
- **hgroup = defgroup;
- ++*hgroup;
- }
- }
- return segments;
- }
- }
- return 0;
-}
-
-static void add_vgroups__mapFunc(void *userData, int index, const float co[3],
- const float UNUSED(no_f[3]), const short UNUSED(no_s[3]))
-{
- /* DerivedMesh mapFunc for getting final coords in weight paint mode */
-
- float (*verts)[3] = userData;
- copy_v3_v3(verts[index], co);
-}
-
-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], int *selected, float scale)
-{
- /* Create vertex group weights from envelopes */
-
- Bone *bone;
- bDeformGroup *dgroup;
- float distance;
- int i, iflip, j;
-
- /* for each vertex in the mesh */
- for (i = 0; i < mesh->totvert; i++) {
- iflip = (dgroupflip) ? mesh_get_x_mirror_vert(ob, i) : 0;
-
- /* 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 >= 0) {
- 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, Scene *scene, Object *ob, Object *par, int heat, int 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 derivedmesh 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 = NULL;
- float (*root)[3], (*tip)[3], (*verts)[3];
- int *selected;
- int numbones, vertsfilled = 0, i, j, segments = 0;
- int wpmode = (ob->mode & OB_MODE_WEIGHT_PAINT);
- struct { Object *armob; void *list; int heat; } looper_data;
-
- looper_data.armob = par;
- looper_data.heat = heat;
- looper_data.list = NULL;
-
- /* count the number of skinnable bones */
- numbones = bone_looper(ob, arm->bonebase.first, &looper_data, bone_skinnable_cb);
-
- if (numbones == 0)
- return;
-
- if (ED_vgroup_data_create(ob->data) == FALSE)
- 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;
- bbone = b_bone_spline_setup(pchan, 1);
- }
- }
- }
-
- 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[MAXBONENAME];
-
- // 0 = don't strip off number extensions
- flip_side_name(name, dgroup->name, FALSE);
- dgroupflip[j] = defgroup_find_name(ob, name);
- }
- }
-
- /* 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 derivedmesh */
- DerivedMesh *dm = mesh_get_derived_final(scene, ob, CD_MASK_BAREMESH);
-
- if (dm->foreachMappedVert) {
- dm->foreachMappedVert(dm, add_vgroups__mapFunc, (void *)verts);
- vertsfilled = 1;
- }
-
- dm->release(dm);
- }
- 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 */
- mesh_octree_table(ob, 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 create_vgroups_from_armature(ReportList *reports, Scene *scene, Object *ob, Object *par, int mode, int 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_countlist(&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 (mode == ARM_GROUPS_ENVELOPE || mode == 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, scene, ob, par, (mode == ARM_GROUPS_AUTO), mirror);
- }
-}
-/* ************* Clear Pose *****************************/
-
-/* 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;
-}
-
-/* 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;
-}
-
-/* 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 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);
-}
-
-/* --------------- */
-
-/* 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[])
-{
- Scene *scene = CTX_data_scene(C);
- Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C));
- short autokey = 0;
-
- /* 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 */
- CTX_DATA_BEGIN(C, bPoseChannel *, pchan, selected_pose_bones)
- {
- /* run provided clearing function */
- clear_func(pchan);
-
- /* do auto-keyframing as appropriate */
- if (autokeyframe_cfra_can_key(scene, &ob->id)) {
- /* clear any unkeyed tags */
- if (pchan->bone)
- pchan->bone->flag &= ~BONE_UNKEYED;
-
- /* tag for autokeying later */
- autokey = 1;
- }
- else {
- /* add unkeyed tags */
- if (pchan->bone)
- pchan->bone->flag |= BONE_UNKEYED;
- }
- }
- CTX_DATA_END;
-
- /* perform autokeying on the bones if needed */
- if (autokey) {
- /* get KeyingSet to use */
- KeyingSet *ks = ANIM_get_keyingset_for_autokeying(scene, default_ksName);
-
- /* insert keyframes */
- ANIM_apply_keyingset(C, NULL, NULL, ks, MODIFYKEY_MODE_INSERT, (float)CFRA);
-
- /* now recalculate paths */
- if ((ob->pose->avs.path_bakeflag & MOTIONPATH_BAKE_HAS_PATHS))
- ED_pose_recalculate_paths(scene, ob);
- }
-
- DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
-
- /* note, notifier might evolve */
- WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, ob);
-
- return OPERATOR_FINISHED;
-}
-
-/* --------------- */
-
-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);
-}
-
-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";
-
- /* api callbacks */
- ot->exec = pose_clear_scale_exec;
- ot->poll = ED_operator_posemode;
-
- /* 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);
-}
-
-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";
-
- /* api callbacks */
- ot->exec = pose_clear_rot_exec;
- ot->poll = ED_operator_posemode;
-
- /* 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);
-}
-
-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";
-
- /* api callbacks */
- ot->exec = pose_clear_loc_exec;
- ot->poll = ED_operator_posemode;
-
- /* 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);
-}
-
-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;
-}
-
-/* ***************** selections ********************** */
-
-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);
- Object *ob = ED_object_context(C);
- bArmature *arm = ob->data;
- int multipaint = scene->toolsettings->multipaint;
-
- if (action == SEL_TOGGLE) {
- action = CTX_DATA_COUNT(C, selected_pose_bones) ? SEL_DESELECT : SEL_SELECT;
- }
-
- /* Set the flags */
- CTX_DATA_BEGIN(C, bPoseChannel *, pchan, visible_pose_bones)
- {
- /* select pchan only if selectable, but deselect works always */
- switch (action) {
- case SEL_SELECT:
- if ((pchan->bone->flag & BONE_UNSELECTABLE) == 0)
- 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) == 0) {
- pchan->bone->flag |= BONE_SELECTED;
- }
- break;
- }
- }
- CTX_DATA_END;
-
- WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, NULL);
-
- /* weightpaint or mask modifiers need depsgraph updates */
- if (multipaint || (arm->flag & ARM_HAS_VIZ_DEPS)) {
- DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
- }
-
- 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";
-
- /* api callbacks */
- ot->exec = pose_de_select_all_exec;
- ot->poll = ED_operator_posemode;
-
- /* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-
- 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;
- }
-
- /* updates */
- WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob);
-
- if (arm->flag & ARM_HAS_VIZ_DEPS) {
- /* mask modifier ('armature' mode), etc. */
- DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
- }
-
- 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";
-
- /* api callbacks */
- ot->exec = pose_select_parent_exec;
- ot->poll = ED_operator_posemode;
-
- /* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-
-}
-
-/* ************* hide/unhide pose bones ******************* */
-
-static int hide_selected_pose_bone_cb(Object *ob, Bone *bone, void *UNUSED(ptr))
-{
- bArmature *arm = ob->data;
-
- if (arm->layer & bone->layer) {
- if (bone->flag & BONE_SELECTED) {
- bone->flag |= BONE_HIDDEN_P;
- bone->flag &= ~BONE_SELECTED;
- if (arm->act_bone == bone)
- arm->act_bone = NULL;
- }
- }
- return 0;
-}
-
-static int hide_unselected_pose_bone_cb(Object *ob, Bone *bone, void *UNUSED(ptr))
-{
- bArmature *arm = ob->data;
-
- if (arm->layer & bone->layer) {
- /* hrm... typo here? */
- if ((bone->flag & BONE_SELECTED) == 0) {
- bone->flag |= BONE_HIDDEN_P;
- if (arm->act_bone == bone)
- arm->act_bone = NULL;
- }
- }
- return 0;
-}
-
-/* active object is armature in posemode, poll checked */
-static int pose_hide_exec(bContext *C, wmOperator *op)
-{
- Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C));
- bArmature *arm = ob->data;
-
- if (RNA_boolean_get(op->ptr, "unselected"))
- bone_looper(ob, arm->bonebase.first, NULL, hide_unselected_pose_bone_cb);
- else
- bone_looper(ob, arm->bonebase.first, NULL, hide_selected_pose_bone_cb);
-
- /* note, notifier might evolve */
- WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob);
-
- return OPERATOR_FINISHED;
-}
-
-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";
-
- /* api callbacks */
- ot->exec = pose_hide_exec;
- ot->poll = ED_operator_posemode;
-
- /* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-
- /* props */
- RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "");
-}
-
-static int show_pose_bone_cb(Object *ob, Bone *bone, void *UNUSED(ptr))
-{
- bArmature *arm = ob->data;
-
- if (arm->layer & bone->layer) {
- if (bone->flag & BONE_HIDDEN_P) {
- bone->flag &= ~BONE_HIDDEN_P;
- bone->flag |= BONE_SELECTED;
- }
- }
-
- return 0;
-}
-
-/* active object is armature in posemode, poll checked */
-static int pose_reveal_exec(bContext *C, wmOperator *UNUSED(op))
-{
- Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C));
- bArmature *arm = ob->data;
-
- bone_looper(ob, arm->bonebase.first, NULL, show_pose_bone_cb);
-
- /* note, notifier might evolve */
- WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob);
-
- return OPERATOR_FINISHED;
-}
-
-void POSE_OT_reveal(wmOperatorType *ot)
-{
- /* identifiers */
- ot->name = "Reveal Selected";
- ot->idname = "POSE_OT_reveal";
- ot->description = "Unhide all bones that have been tagged to be hidden in Pose Mode";
-
- /* api callbacks */
- ot->exec = pose_reveal_exec;
- ot->poll = ED_operator_posemode;
-
- /* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-}
-
-/* ************* RENAMING DISASTERS ************ */
-
-static int bone_unique_check(void *arg, const char *name)
-{
- return BKE_armature_find_bone_name((bArmature *)arg, name) != NULL;
-}
-
-static void unique_bone_name(bArmature *arm, char *name)
-{
- BLI_uniquename_cb(bone_unique_check, (void *)arm, "Bone", '.', name, sizeof(((Bone *)NULL)->name));
-}
-
-/* helper call for armature_bone_rename */
-static void constraint_bone_name_fix(Object *ob, ListBase *conlist, char *oldname, char *newname)
-{
- bConstraint *curcon;
- bConstraintTarget *ct;
-
- for (curcon = conlist->first; curcon; curcon = curcon->next) {
- bConstraintTypeInfo *cti = BKE_constraint_get_typeinfo(curcon);
- ListBase targets = {NULL, NULL};
-
- 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 (!strcmp(ct->subtarget, oldname) )
- BLI_strncpy(ct->subtarget, newname, MAXBONENAME);
- }
- }
-
- if (cti->flush_constraint_targets)
- cti->flush_constraint_targets(curcon, &targets, 0);
- }
- }
-}
-
-/* called by UI for renaming a bone */
-/* warning: make sure the original bone was not renamed yet! */
-/* seems messy, but thats what you get with not using pointers but channel names :) */
-void ED_armature_bone_rename(bArmature *arm, const char *oldnamep, const char *newnamep)
-{
- Object *ob;
- char newname[MAXBONENAME];
- char oldname[MAXBONENAME];
-
- /* names better differ! */
- if (strncmp(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 = editbone_name_exists(arm->edbo, oldname);
-
- if (eBone) {
- unique_editbone_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;
- }
-
- /* do entire dbase - objects */
- for (ob = G.main->object.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) {
- BLI_strncpy(pchan->name, newname, MAXBONENAME);
-
- if (ob->pose->chanhash) {
- GHash *gh = ob->pose->chanhash;
-
- /* remove the old hash entry, and replace with the new name */
- BLI_ghash_remove(gh, oldname, NULL, NULL);
- BLI_ghash_insert(gh, pchan->name, pchan);
- }
- }
- }
-
- /* Update any object constraints to use the new bone name */
- for (cob = G.main->object.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 (!strcmp(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) {
- if (md->type == eModifierType_Hook) {
- HookModifierData *hmd = (HookModifierData *)md;
-
- /* uses armature, so may use the affected bone name */
- if (hmd->object && (hmd->object->data == arm)) {
- if (!strcmp(hmd->subtarget, oldname))
- BLI_strncpy(hmd->subtarget, newname, MAXBONENAME);
- }
- }
- }
- }
-
- /* 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]
- */
- {
-
- BKE_all_animdata_fix_paths_rename(&arm->id, "pose.bones", oldname, newname);
- }
-
- /* correct view locking */
- {
- bScreen *screen;
- for (screen = G.main->screen.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 (!strcmp(v3d->ob_centre_bone, oldname)) {
- BLI_strncpy(v3d->ob_centre_bone, newname, MAXBONENAME);
- }
- }
- }
- }
- }
- }
- }
- }
-}
-
-
-static int armature_flip_names_exec(bContext *C, wmOperator *UNUSED(op))
-{
- Object *ob = CTX_data_edit_object(C);
- bArmature *arm;
- char newname[MAXBONENAME];
-
- /* paranoia checks */
- if (ELEM(NULL, ob, ob->pose))
- return OPERATOR_CANCELLED;
- arm = ob->data;
-
- /* loop through selected bones, auto-naming them */
- CTX_DATA_BEGIN(C, EditBone *, ebone, selected_editable_bones)
- {
- flip_side_name(newname, ebone->name, TRUE); // 1 = do strip off number extensions
- ED_armature_bone_rename(arm, ebone->name, newname);
- }
- CTX_DATA_END;
-
- /* since we renamed stuff... */
- DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
-
- /* note, notifier might evolve */
- WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
-
- 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;
-}
-
-
-static int armature_autoside_names_exec(bContext *C, wmOperator *op)
-{
- Object *ob = CTX_data_edit_object(C);
- bArmature *arm;
- char newname[MAXBONENAME];
- short axis = RNA_enum_get(op->ptr, "type");
-
- /* paranoia checks */
- if (ELEM(NULL, ob, ob->pose))
- return OPERATOR_CANCELLED;
- arm = ob->data;
-
- /* loop through selected bones, auto-naming them */
- CTX_DATA_BEGIN(C, EditBone *, ebone, selected_editable_bones)
- {
- BLI_strncpy(newname, ebone->name, sizeof(newname));
- if (bone_autoside_name(newname, 1, axis, ebone->head[axis], ebone->tail[axis]))
- ED_armature_bone_rename(arm, ebone->name, newname);
- }
- CTX_DATA_END;
-
- /* since we renamed stuff... */
- DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
-
- /* note, notifier might evolve */
- WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
-
- return OPERATOR_FINISHED;
-}
-
-void ARMATURE_OT_autoside_names(wmOperatorType *ot)
-{
- static 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");
-}
-
-
-
-/* if editbone (partial) selected, copy data */
-/* context; editmode armature, with mirror editing enabled */
-void transform_armature_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_bone_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;
-
- /* 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;
-
- /* 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;
- }
- }
- }
- }
-}
-
-
-/*****************************************************************************************************/
-/*************************************** SKELETON GENERATOR ******************************************/
-/*****************************************************************************************************/
-
-#if 0
-
-/**************************************** SUBDIVISION ALGOS ******************************************/
-
-EditBone *subdivideByAngle(Scene *scene, Object *obedit, ReebArc *arc, ReebNode *head, ReebNode *tail)
-{
- bArmature *arm = obedit->data;
- EditBone *lastBone = NULL;
-
- if (scene->toolsettings->skgen_options & SKGEN_CUT_ANGLE) {
- ReebArcIterator arc_iter;
- BArcIterator *iter = (BArcIterator *)&arc_iter;
- float *previous = NULL, *current = NULL;
- EditBone *child = NULL;
- EditBone *parent = NULL;
- EditBone *root = NULL;
- float angleLimit = (float)cos(scene->toolsettings->skgen_angle_limit * M_PI / 180.0f);
-
- parent = ED_armature_edit_bone_add(arm, "Bone");
- parent->flag |= BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL;
- copy_v3_v3(parent->head, head->p);
-
- root = parent;
-
- initArcIterator(iter, arc, head);
- IT_next(iter);
- previous = iter->p;
-
- for (IT_next(iter);
- IT_stopped(iter) == 0;
- previous = iter->p, IT_next(iter))
- {
- float vec1[3], vec2[3];
- float len1, len2;
-
- current = iter->p;
-
- sub_v3_v3v3(vec1, previous, parent->head);
- sub_v3_v3v3(vec2, current, previous);
-
- len1 = normalize_v3(vec1);
- len2 = normalize_v3(vec2);
-
- if (len1 > 0.0f && len2 > 0.0f && dot_v3v3(vec1, vec2) < angleLimit) {
- copy_v3_v3(parent->tail, previous);
-
- child = ED_armature_edit_bone_add(arm, "Bone");
- copy_v3_v3(child->head, parent->tail);
- child->parent = parent;
- child->flag |= BONE_CONNECTED | BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL;
-
- parent = child; /* new child is next parent */
- }
- }
- copy_v3_v3(parent->tail, tail->p);
-
- /* If the bone wasn't subdivided, delete it and return NULL
- * to let subsequent subdivision methods do their thing.
- * */
- if (parent == root) {
- if (parent == arm->act_edbone) arm->act_edbone = NULL;
- ED_armature_edit_bone_remove(arm, parent);
- parent = NULL;
- }
-
- lastBone = parent; /* set last bone in the chain */
- }
-
- return lastBone;
-}
-
-EditBone *test_subdivideByCorrelation(Scene *scene, Object *obedit, ReebArc *arc, ReebNode *head, ReebNode *tail)
-{
- EditBone *lastBone = NULL;
-
- if (scene->toolsettings->skgen_options & SKGEN_CUT_CORRELATION) {
- float invmat[4][4] = MAT4_UNITY;
- float tmat[3][3] = MAT3_UNITY;
- ReebArcIterator arc_iter;
- BArcIterator *iter = (BArcIterator *)&arc_iter;
- bArmature *arm = obedit->data;
-
- initArcIterator(iter, arc, head);
-
- lastBone = subdivideArcBy(arm, arm->edbo, iter, invmat, tmat, nextAdaptativeSubdivision);
- }
-
- return lastBone;
-}
-
-float arcLengthRatio(ReebArc *arc)
-{
- float arcLength = 0.0f;
- float embedLength = 0.0f;
- int i;
-
- arcLength = len_v3v3(arc->head->p, arc->tail->p);
-
- if (arc->bcount > 0) {
- /* Add the embedding */
- for (i = 1; i < arc->bcount; i++) {
- embedLength += len_v3v3(arc->buckets[i - 1].p, arc->buckets[i].p);
- }
- /* Add head and tail -> embedding vectors */
- embedLength += len_v3v3(arc->head->p, arc->buckets[0].p);
- embedLength += len_v3v3(arc->tail->p, arc->buckets[arc->bcount - 1].p);
- }
- else {
- embedLength = arcLength;
- }
-
- return embedLength / arcLength;
-}
-
-EditBone *test_subdivideByLength(Scene *scene, Object *obedit, ReebArc *arc, ReebNode *head, ReebNode *tail)
-{
- EditBone *lastBone = NULL;
- if ((scene->toolsettings->skgen_options & SKGEN_CUT_LENGTH) &&
- arcLengthRatio(arc) >= G.scene->toolsettings->skgen_length_ratio)
- {
- float invmat[4][4] = MAT4_UNITY;
- float tmat[3][3] = MAT3_UNITY;
- ReebArcIterator arc_iter;
- BArcIterator *iter = (BArcIterator *)&arc_iter;
- bArmature *arm = obedit->data;
-
- initArcIterator(iter, arc, head);
-
- lastBone = subdivideArcBy(arm, arm->edbo, iter, invmat, tmat, nextLengthSubdivision);
- }
-
- return lastBone;
-}
-
-/***************************************** MAIN ALGORITHM ********************************************/
-
-void generateSkeletonFromReebGraph(Scene *scene, ReebGraph *rg)
-{
- Object *obedit = scene->obedit; // XXX get from context
- GHash *arcBoneMap = NULL;
- ReebArc *arc = NULL;
- ReebNode *node = NULL;
- Object *src = NULL;
- Object *dst = NULL;
-
- src = scene->basact->object;
-
- if (obedit != NULL) {
- ED_armature_from_edit(obedit);
- ED_armature_edit_free(obedit);
- }
-
- dst = BKE_object_add(scene, OB_ARMATURE);
- ED_object_base_init_transform(NULL, scene->basact, NULL, NULL); // XXX NULL is C, loc, rot
- obedit = scene->basact->object;
-
- /* Copy orientation from source */
- copy_v3_v3(dst->loc, src->obmat[3]);
- mat4_to_eul(dst->rot, src->obmat);
- mat4_to_size(dst->size, src->obmat);
-
- BKE_object_where_is_calc(scene, obedit);
-
- ED_armature_to_edit(obedit);
-
- arcBoneMap = BLI_ghash_ptr_new("SkeletonFromReebGraph gh");
-
- BLI_markdownSymmetry((BGraph *)rg, rg->nodes.first, scene->toolsettings->skgen_symmetry_limit);
-
- for (arc = rg->arcs.first; arc; arc = arc->next)
- {
- EditBone *lastBone = NULL;
- ReebNode *head, *tail;
- int i;
-
- /* Find out the direction of the arc through simple heuristics (in order of priority) :
- *
- * 1- Arcs on primary symmetry axis (symmetry == 1) point up (head: high weight -> tail: low weight)
- * 2- Arcs starting on a primary axis point away from it (head: node on primary axis)
- * 3- Arcs point down (head: low weight -> tail: high weight)
- *
- * Finally, the arc direction is stored in its flag: 1 (low -> high), -1 (high -> low)
- */
-
- /* if arc is a symmetry axis, internal bones go up the tree */
- if (arc->symmetry_level == 1 && arc->tail->degree != 1) {
- head = arc->tail;
- tail = arc->head;
-
- arc->flag = -1; /* mark arc direction */
- }
- /* Bones point AWAY from the symmetry axis */
- else if (arc->head->symmetry_level == 1) {
- head = arc->head;
- tail = arc->tail;
-
- arc->flag = 1; /* mark arc direction */
- }
- else if (arc->tail->symmetry_level == 1) {
- head = arc->tail;
- tail = arc->head;
-
- arc->flag = -1; /* mark arc direction */
- }
- /* otherwise, always go from low weight to high weight */
- else {
- head = arc->head;
- tail = arc->tail;
-
- arc->flag = 1; /* mark arc direction */
- }
-
- /* Loop over subdivision methods */
- for (i = 0; lastBone == NULL && i < SKGEN_SUB_TOTAL; i++) {
- switch (scene->toolsettings->skgen_subdivisions[i]) {
- case SKGEN_SUB_LENGTH:
- lastBone = test_subdivideByLength(scene, obedit, arc, head, tail);
- break;
- case SKGEN_SUB_ANGLE:
- lastBone = subdivideByAngle(scene, obedit, arc, head, tail);
- break;
- case SKGEN_SUB_CORRELATION:
- lastBone = test_subdivideByCorrelation(scene, obedit, arc, head, tail);
- break;
- }
- }
-
- if (lastBone == NULL) {
- EditBone *bone;
- bone = ED_armature_edit_bone_add(obedit->data, "Bone");
- bone->flag |= BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL;
-
- copy_v3_v3(bone->head, head->p);
- copy_v3_v3(bone->tail, tail->p);
-
- /* set first and last bone, since there's only one */
- lastBone = bone;
- }
-
- BLI_ghash_insert(arcBoneMap, arc, lastBone);
- }
-
- /* Second pass, setup parent relationship between arcs */
- for (node = rg->nodes.first; node; node = node->next)
- {
- ReebArc *incomingArc = NULL;
- int i;
-
- for (i = 0; i < node->degree; i++) {
- arc = (ReebArc *)node->arcs[i];
-
- /* if arc is incoming into the node */
- if ((arc->head == node && arc->flag == -1) ||
- (arc->tail == node && arc->flag == 1))
- {
- if (incomingArc == NULL) {
- incomingArc = arc;
- /* loop further to make sure there's only one incoming arc */
- }
- else {
- /* skip this node if more than one incomingArc */
- incomingArc = NULL;
- break; /* No need to look further, we are skipping already */
- }
- }
- }
-
- if (incomingArc != NULL) {
- EditBone *parentBone = BLI_ghash_lookup(arcBoneMap, incomingArc);
-
- /* Look for outgoing arcs and parent their bones */
- for (i = 0; i < node->degree; i++)
- {
- arc = node->arcs[i];
-
- /* if arc is outgoing from the node */
- if ((arc->head == node && arc->flag == 1) || (arc->tail == node && arc->flag == -1)) {
- EditBone *childBone = BLI_ghash_lookup(arcBoneMap, arc);
-
- /* find the root bone */
- while (childBone->parent != NULL)
- {
- childBone = childBone->parent;
- }
-
- childBone->parent = parentBone;
- childBone->flag |= BONE_CONNECTED;
- }
- }
- }
- }
-
- BLI_ghash_free(arcBoneMap, NULL, NULL);
-}
-
-void generateSkeleton(Scene *scene)
-{
- ReebGraph *reebg;
-
-// setcursor_space(SPACE_VIEW3D, CURSOR_WAIT);
-
- reebg = BIF_ReebGraphFromEditMesh();
-
- generateSkeletonFromReebGraph(scene, reebg);
-
- REEB_freeGraph(reebg);
-
- //setcursor_space(SPACE_VIEW3D, CURSOR_EDIT);
-}
-
-#endif
diff --git a/source/blender/editors/armature/pose_edit.c b/source/blender/editors/armature/pose_edit.c
new file mode 100644
index 00000000000..f4a5aabf149
--- /dev/null
+++ b/source/blender/editors/armature/pose_edit.c
@@ -0,0 +1,1198 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
+ * All rights reserved.
+ *
+ * Contributor(s): Ton Roosendaal, Blender Foundation '05, full recode.
+ * Joshua Leung
+ * Reevan McKay (original NaN code)
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ *
+ * Pose Mode API's and Operators for Pose Mode armatures
+ */
+
+/** \file blender/editors/armature/poseobject.c
+ * \ingroup edarmature
+ */
+
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_math.h"
+#include "BLI_blenlib.h"
+#include "BLI_dynstr.h"
+#include "BLI_utildefines.h"
+
+#include "DNA_anim_types.h"
+#include "DNA_armature_types.h"
+#include "DNA_constraint_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_object_types.h"
+
+#include "BKE_animsys.h"
+#include "BKE_anim.h"
+#include "BKE_idprop.h"
+#include "BKE_action.h"
+#include "BKE_armature.h"
+#include "BKE_context.h"
+#include "BKE_constraint.h"
+#include "BKE_deform.h"
+#include "BKE_depsgraph.h"
+#include "BKE_fcurve.h"
+#include "BKE_modifier.h"
+#include "BKE_object.h"
+#include "BKE_report.h"
+
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+#include "RNA_enum_types.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "ED_armature.h"
+#include "ED_keyframing.h"
+#include "ED_mesh.h"
+#include "ED_screen.h"
+#include "ED_object.h"
+#include "ED_util.h" /* clipboard */
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "armature_intern.h"
+
+/* 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_BUTS) {
+ 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 */
+void ED_armature_enter_posemode(bContext *C, Base *base)
+{
+ ReportList *reports = CTX_wm_reports(C);
+ Object *ob = base->object;
+
+ if (ob->id.lib) {
+ BKE_report(reports, RPT_WARNING, "Cannot pose libdata");
+ return;
+ }
+
+ switch (ob->type) {
+ case OB_ARMATURE:
+ ob->restore_mode = ob->mode;
+ ob->mode |= OB_MODE_POSE;
+
+ WM_event_add_notifier(C, NC_SCENE | ND_MODE | NS_MODE_POSE, NULL);
+
+ break;
+ default:
+ return;
+ }
+
+ /* XXX: disabled as this would otherwise cause a nasty loop... */
+ //ED_object_toggle_modes(C, ob->mode);
+}
+
+void ED_armature_exit_posemode(bContext *C, Base *base)
+{
+ if (base) {
+ Object *ob = base->object;
+
+ ob->restore_mode = ob->mode;
+ ob->mode &= ~OB_MODE_POSE;
+
+ WM_event_add_notifier(C, NC_SCENE | ND_MODE | NS_MODE_OBJECT, NULL);
+ }
+}
+
+/* if a selected or active bone is protected, throw error (oonly if warn == 1) and return 1 */
+/* only_selected == 1: the active bone is allowed to be protected */
+#if 0 /* UNUSED 2.5 */
+static short 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;
+}
+#endif
+
+/* only for real IK, not for auto-IK */
+static int pose_channel_in_IK_chain(Object *ob, bPoseChannel *pchan, int level)
+{
+ bConstraint *con;
+ Bone *bone;
+
+ /* No need to check if constraint is active (has influence),
+ * since all constraints with CONSTRAINT_IK_AUTO are active */
+ for (con = pchan->constraints.first; con; con = con->next) {
+ if (con->type == CONSTRAINT_TYPE_KINEMATIC) {
+ bKinematicConstraint *data = con->data;
+ if (data->rootbone == 0 || data->rootbone > level) {
+ if ((data->flag & CONSTRAINT_IK_AUTO) == 0)
+ return 1;
+ }
+ }
+ }
+ for (bone = pchan->bone->childbase.first; bone; bone = bone->next) {
+ pchan = BKE_pose_channel_find_name(ob->pose, bone->name);
+ if (pchan && pose_channel_in_IK_chain(ob, pchan, level + 1))
+ return 1;
+ }
+ return 0;
+}
+
+int ED_pose_channel_in_IK_chain(Object *ob, bPoseChannel *pchan)
+{
+ return pose_channel_in_IK_chain(ob, pchan, 0);
+}
+
+/* ********************************************** */
+/* Motion Paths */
+
+/* For the object with pose/action: update paths for those that have got them
+ * This should selectively update paths that exist...
+ *
+ * To be called from various tools that do incremental updates
+ */
+void ED_pose_recalculate_paths(Scene *scene, Object *ob)
+{
+ ListBase targets = {NULL, NULL};
+
+ /* 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 */
+ animviz_calc_motionpaths(scene, &targets);
+ BLI_freelistN(&targets);
+}
+
+
+/* show popup to determine settings */
+static int pose_calculate_paths_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
+{
+ Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C));
+
+ if (ELEM(NULL, ob, ob->pose))
+ return OPERATOR_CANCELLED;
+
+ /* 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_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, 10 * UI_UNIT_X, 10 * UI_UNIT_Y);
+}
+
+/* For the object with pose/action: create path curves for selected bones
+ * This recalculates the WHOLE path within the pchan->pathsf and pchan->pathef range
+ */
+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);
+
+ if (ELEM(NULL, ob, ob->pose))
+ return OPERATOR_CANCELLED;
+
+ /* 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");
+
+ 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)
+ {
+ /* 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;
+
+ /* calculate the bones that now have motionpaths... */
+ /* TODO: only make for the selected bones? */
+ ED_pose_recalculate_paths(scene, ob);
+
+ /* notifiers for updates */
+ WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
+
+ 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", motionpath_bake_location_items, 0,
+ "Bake Location",
+ "Which point on the bones is used when calculating paths");
+}
+
+/* --------- */
+
+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);
+
+ 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(scene, ob);
+
+ /* notifiers for updates */
+ WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
+
+ 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";
+
+ /* api callbakcs */
+ ot->exec = pose_update_paths_exec;
+ ot->poll = ED_operator_posemode_exclusive; /* TODO: this should probably check for active bone and/or existing paths */
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+/* --------- */
+
+/* for the object with pose/action: clear path curves for selected bones only */
+static void ED_pose_clear_paths(Object *ob)
+{
+ bPoseChannel *pchan;
+ short skipped = 0;
+
+ if (ELEM(NULL, ob, ob->pose))
+ return;
+
+ /* free the motionpath blocks, but also take note of whether we skipped some... */
+ for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
+ if (pchan->mpath) {
+ if ((pchan->bone) && (pchan->bone->flag & BONE_SELECTED)) {
+ animviz_free_motionpath(pchan->mpath);
+ pchan->mpath = NULL;
+ }
+ else
+ skipped = 1;
+ }
+ }
+
+ /* if we didn't skip any, we shouldn't have any paths left */
+ if (skipped == 0)
+ ob->pose->avs.path_bakeflag &= ~MOTIONPATH_BAKE_HAS_PATHS;
+}
+
+/* operator callback for this */
+static int pose_clear_paths_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C));
+
+ /* 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);
+
+ /* notifiers for updates */
+ WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
+
+ return OPERATOR_FINISHED;
+}
+
+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 selected bones";
+
+ /* api callbacks */
+ ot->exec = pose_clear_paths_exec;
+ ot->poll = ED_operator_posemode_exclusive;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+/* ********************************************** */
+#if 0 /* UNUSED 2.5 */
+static void pose_copy_menu(Scene *scene)
+{
+ Object *obedit = scene->obedit; // XXX context
+ Object *ob = OBACT;
+ bArmature *arm;
+ bPoseChannel *pchan, *pchanact;
+ short nr = 0;
+ int i = 0;
+
+ /* paranoia checks */
+ if (ELEM(NULL, ob, ob->pose)) return;
+ if ((ob == obedit) || (ob->mode & OB_MODE_POSE) == 0) return;
+
+ pchan = BKE_pose_channel_active(ob);
+
+ if (pchan == NULL) return;
+ pchanact = pchan;
+ arm = ob->data;
+
+ /* if proxy-protected bones selected, some things (such as locks + displays) shouldn't be changeable,
+ * but for constraints (just add local constraints)
+ */
+ if (pose_has_protected_selected(ob, 0)) {
+ i = BLI_countlist(&(pchanact->constraints)); /* if there are 24 or less, allow for the user to select constraints */
+ if (i < 25)
+ nr = pupmenu("Copy Pose Attributes %t|Local Location %x1|Local Rotation %x2|Local Size %x3|%l|Visual Location %x9|Visual Rotation %x10|Visual Size %x11|%l|Constraints (All) %x4|Constraints... %x5");
+ else
+ nr = pupmenu("Copy Pose Attributes %t|Local Location %x1|Local Rotation %x2|Local Size %x3|%l|Visual Location %x9|Visual Rotation %x10|Visual Size %x11|%l|Constraints (All) %x4");
+ }
+ else {
+ i = BLI_countlist(&(pchanact->constraints)); /* if there are 24 or less, allow for the user to select constraints */
+ if (i < 25)
+ nr = pupmenu("Copy Pose Attributes %t|Local Location %x1|Local Rotation %x2|Local Size %x3|%l|Visual Location %x9|Visual Rotation %x10|Visual Size %x11|%l|Constraints (All) %x4|Constraints... %x5|%l|Transform Locks %x6|IK Limits %x7|Bone Shape %x8");
+ else
+ nr = pupmenu("Copy Pose Attributes %t|Local Location %x1|Local Rotation %x2|Local Size %x3|%l|Visual Location %x9|Visual Rotation %x10|Visual Size %x11|%l|Constraints (All) %x4|%l|Transform Locks %x6|IK Limits %x7|Bone Shape %x8");
+ }
+
+ if (nr <= 0)
+ return;
+
+ if (nr != 5) {
+ for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
+ if ((arm->layer & pchan->bone->layer) &&
+ (pchan->bone->flag & BONE_SELECTED) &&
+ (pchan != pchanact) )
+ {
+ switch (nr) {
+ case 1: /* Local Location */
+ copy_v3_v3(pchan->loc, pchanact->loc);
+ break;
+ case 2: /* Local Rotation */
+ copy_qt_qt(pchan->quat, pchanact->quat);
+ copy_v3_v3(pchan->eul, pchanact->eul);
+ break;
+ case 3: /* Local Size */
+ copy_v3_v3(pchan->size, pchanact->size);
+ break;
+ case 4: /* All Constraints */
+ {
+ ListBase tmp_constraints = {NULL, NULL};
+
+ /* copy constraints to tmpbase and apply 'local' tags before
+ * appending to list of constraints for this channel
+ */
+ BKE_copy_constraints(&tmp_constraints, &pchanact->constraints, TRUE);
+ if ((ob->proxy) && (pchan->bone->layer & arm->layer_protected)) {
+ bConstraint *con;
+
+ /* add proxy-local tags */
+ for (con = tmp_constraints.first; con; con = con->next)
+ con->flag |= CONSTRAINT_PROXY_LOCAL;
+ }
+ BLI_movelisttolist(&pchan->constraints, &tmp_constraints);
+
+ /* update flags (need to add here, not just copy) */
+ pchan->constflag |= pchanact->constflag;
+
+ if (ob->pose)
+ ob->pose->flag |= POSE_RECALC;
+ }
+ break;
+ case 6: /* Transform Locks */
+ pchan->protectflag = pchanact->protectflag;
+ break;
+ case 7: /* IK (DOF) settings */
+ {
+ pchan->ikflag = pchanact->ikflag;
+ copy_v3_v3(pchan->limitmin, pchanact->limitmin);
+ copy_v3_v3(pchan->limitmax, pchanact->limitmax);
+ copy_v3_v3(pchan->stiffness, pchanact->stiffness);
+ pchan->ikstretch = pchanact->ikstretch;
+ pchan->ikrotweight = pchanact->ikrotweight;
+ pchan->iklinweight = pchanact->iklinweight;
+ }
+ break;
+ case 8: /* Custom Bone Shape */
+ pchan->custom = pchanact->custom;
+ break;
+ case 9: /* Visual Location */
+ BKE_armature_loc_pose_to_bone(pchan, pchanact->pose_mat[3], pchan->loc);
+ break;
+ case 10: /* Visual Rotation */
+ {
+ float delta_mat[4][4];
+
+ BKE_armature_mat_pose_to_bone(pchan, pchanact->pose_mat, delta_mat);
+
+ if (pchan->rotmode == ROT_MODE_AXISANGLE) {
+ float tmp_quat[4];
+
+ /* need to convert to quat first (in temp var)... */
+ mat4_to_quat(tmp_quat, delta_mat);
+ quat_to_axis_angle(pchan->rotAxis, &pchan->rotAngle, tmp_quat);
+ }
+ else if (pchan->rotmode == ROT_MODE_QUAT)
+ mat4_to_quat(pchan->quat, delta_mat);
+ else
+ mat4_to_eulO(pchan->eul, pchan->rotmode, delta_mat);
+ }
+ break;
+ case 11: /* Visual Size */
+ {
+ float delta_mat[4][4], size[4];
+
+ BKE_armature_mat_pose_to_bone(pchan, pchanact->pose_mat, delta_mat);
+ mat4_to_size(size, delta_mat);
+ copy_v3_v3(pchan->size, size);
+ }
+ }
+ }
+ }
+ }
+ else { /* constraints, optional (note: max we can have is 24 constraints) */
+ bConstraint *con, *con_back;
+ int const_toggle[24] = {0}; /* XXX, initialize as 0 to quiet errors */
+ ListBase const_copy = {NULL, NULL};
+
+ BLI_duplicatelist(&const_copy, &(pchanact->constraints));
+
+ /* build the puplist of constraints */
+ for (con = pchanact->constraints.first, i = 0; con; con = con->next, i++) {
+ const_toggle[i] = 1;
+// add_numbut(i, TOG|INT, con->name, 0, 0, &(const_toggle[i]), "");
+ }
+
+// if (!do_clever_numbuts("Select Constraints", i, REDRAW)) {
+// BLI_freelistN(&const_copy);
+// return;
+// }
+
+ /* now build a new listbase from the options selected */
+ for (i = 0, con = const_copy.first; con; i++) {
+ /* if not selected, free/remove it from the list */
+ if (!const_toggle[i]) {
+ con_back = con->next;
+ BLI_freelinkN(&const_copy, con);
+ con = con_back;
+ }
+ else
+ con = con->next;
+ }
+
+ /* Copy the temo listbase to the selected posebones */
+ for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
+ if ((arm->layer & pchan->bone->layer) &&
+ (pchan->bone->flag & BONE_SELECTED) &&
+ (pchan != pchanact) )
+ {
+ ListBase tmp_constraints = {NULL, NULL};
+
+ /* copy constraints to tmpbase and apply 'local' tags before
+ * appending to list of constraints for this channel
+ */
+ BKE_copy_constraints(&tmp_constraints, &const_copy, TRUE);
+ if ((ob->proxy) && (pchan->bone->layer & arm->layer_protected)) {
+ /* add proxy-local tags */
+ for (con = tmp_constraints.first; con; con = con->next)
+ con->flag |= CONSTRAINT_PROXY_LOCAL;
+ }
+ BLI_movelisttolist(&pchan->constraints, &tmp_constraints);
+
+ /* update flags (need to add here, not just copy) */
+ pchan->constflag |= pchanact->constflag;
+ }
+ }
+ BLI_freelistN(&const_copy);
+ BKE_pose_update_constraint_flags(ob->pose); /* we could work out the flags but its simpler to do this */
+
+ if (ob->pose)
+ ob->pose->flag |= POSE_RECALC;
+ }
+
+ DAG_id_tag_update(&ob->id, OB_RECALC_DATA); // and all its relations
+
+ BIF_undo_push("Copy Pose Attributes");
+
+}
+#endif
+
+/* ********************************************** */
+
+static int pose_flip_names_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C));
+ bArmature *arm;
+
+ /* paranoia checks */
+ if (ELEM(NULL, ob, ob->pose))
+ return OPERATOR_CANCELLED;
+ arm = ob->data;
+
+ /* loop through selected bones, auto-naming them */
+ CTX_DATA_BEGIN (C, bPoseChannel *, pchan, selected_pose_bones)
+ {
+ char newname[MAXBONENAME];
+ flip_side_name(newname, pchan->name, TRUE);
+ ED_armature_bone_rename(arm, pchan->name, newname);
+ }
+ CTX_DATA_END;
+
+ /* since we renamed stuff... */
+ DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
+
+ /* note, notifier might evolve */
+ WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
+
+ 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 the names of selected bones";
+
+ /* api callbacks */
+ ot->exec = pose_flip_names_exec;
+ ot->poll = ED_operator_posemode;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+/* ------------------ */
+
+static int pose_autoside_names_exec(bContext *C, wmOperator *op)
+{
+ Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C));
+ bArmature *arm;
+ char newname[MAXBONENAME];
+ short axis = RNA_enum_get(op->ptr, "axis");
+
+ /* paranoia checks */
+ if (ELEM(NULL, ob, ob->pose))
+ return OPERATOR_CANCELLED;
+ arm = ob->data;
+
+ /* loop through selected bones, auto-naming them */
+ CTX_DATA_BEGIN (C, bPoseChannel *, pchan, selected_pose_bones)
+ {
+ 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(arm, pchan->name, newname);
+ }
+ CTX_DATA_END;
+
+ /* since we renamed stuff... */
+ DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
+
+ /* note, notifier might evolve */
+ WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
+
+ return OPERATOR_FINISHED;
+}
+
+void POSE_OT_autoside_names(wmOperatorType *ot)
+{
+ static 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)
+{
+ Object *ob = CTX_data_active_object(C);
+ int mode = RNA_enum_get(op->ptr, "type");
+
+ /* set rotation mode of selected bones */
+ CTX_DATA_BEGIN (C, bPoseChannel *, pchan, selected_pose_bones)
+ {
+ pchan->rotmode = mode;
+ }
+ CTX_DATA_END;
+
+ /* notifiers and updates */
+ DAG_id_tag_update((ID *)ob, OB_RECALC_DATA);
+ WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, ob);
+
+ 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", posebone_rotmode_items, 0, "Rotation Mode", "");
+}
+
+/* ********************************************** */
+
+/* Show all armature layers */
+static int pose_armature_layers_showall_poll(bContext *C)
+{
+ /* this single operator can be used in posemode OR editmode for armatures */
+ return ED_operator_posemode(C) || ED_operator_editarmature(C);
+}
+
+static int pose_armature_layers_showall_exec(bContext *C, wmOperator *op)
+{
+ Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C));
+ bArmature *arm = (ob) ? ob->data : NULL;
+ PointerRNA ptr;
+ int maxLayers = (RNA_boolean_get(op->ptr, "all")) ? 32 : 16;
+ int layers[32] = {0}; /* hardcoded for now - we can only have 32 armature layers, so this should be fine... */
+ 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);
+
+ /* 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";
+
+ /* callbacks */
+ ot->exec = pose_armature_layers_showall_exec;
+ ot->poll = pose_armature_layers_showall_poll;
+
+ /* 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)");
+}
+
+/* ------------------- */
+
+/* Present a popup to get the layers that should be used */
+static int pose_armature_layers_invoke(bContext *C, wmOperator *op, wmEvent *evt)
+{
+ Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C));
+ bArmature *arm = (ob) ? ob->data : NULL;
+ PointerRNA ptr;
+ int layers[32]; /* hardcoded for now - we can only have 32 armature layers, so this should be fine... */
+
+ /* 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, evt);
+}
+
+/* Set the visible layers for the active armature (edit and pose modes) */
+static int pose_armature_layers_exec(bContext *C, wmOperator *op)
+{
+ Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C));
+ PointerRNA ptr;
+ int layers[32]; /* hardcoded for now - we can only have 32 armature layers, so this should be fine... */
+
+ if (ELEM(NULL, ob, ob->data)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ /* 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 *)ob->data, &ptr);
+ RNA_boolean_set_array(&ptr, "layers", layers);
+
+ /* note, notifier might evolve */
+ WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
+
+ return OPERATOR_FINISHED;
+}
+
+
+void POSE_OT_armature_layers(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Change Armature Layers";
+ ot->idname = "POSE_OT_armature_layers";
+ ot->description = "Change the visible armature layers";
+
+ /* callbacks */
+ ot->invoke = pose_armature_layers_invoke;
+ ot->exec = pose_armature_layers_exec;
+ ot->poll = ED_operator_posemode;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_boolean_layer_member(ot->srna, "layers", 32, NULL, "Layer", "Armature layers to make visible");
+}
+
+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 = pose_armature_layers_invoke;
+ ot->exec = pose_armature_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 to make visible");
+}
+
+/* ------------------- */
+
+/* Present a popup to get the layers that should be used */
+static int pose_bone_layers_invoke(bContext *C, wmOperator *op, wmEvent *evt)
+{
+ int layers[32] = {0}; /* hardcoded for now - we can only have 32 armature layers, so this should be fine... */
+
+ /* 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++) {
+ if (pchan->bone->layer & (1 << 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, evt);
+}
+
+/* Set the visible layers for the active armature (edit and pose modes) */
+static int pose_bone_layers_exec(bContext *C, wmOperator *op)
+{
+ Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C));
+ PointerRNA ptr;
+ int layers[32]; /* hardcoded for now - we can only have 32 armature layers, so this should be fine... */
+
+ if (ob == NULL || ob->data == NULL) {
+ return OPERATOR_CANCELLED;
+ }
+
+ /* 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 (C, bPoseChannel *, pchan, selected_pose_bones)
+ {
+ /* 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);
+ }
+ CTX_DATA_END;
+
+ /* note, notifier might evolve */
+ WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
+
+ 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");
+}
+
+/* ------------------- */
+
+/* Present a popup to get the layers that should be used */
+static int armature_bone_layers_invoke(bContext *C, wmOperator *op, wmEvent *evt)
+{
+ int layers[32] = {0}; /* hardcoded for now - we can only have 32 armature layers, so this should be fine... */
+
+ /* 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 & (1 << 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, evt);
+}
+
+/* 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);
+ bArmature *arm = (ob) ? ob->data : NULL;
+ PointerRNA ptr;
+ int layers[32]; /* hardcoded for now - we can only have 32 armature layers, so this should be fine... */
+
+ /* 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 (C, EditBone *, ebone, selected_editable_bones)
+ {
+ /* 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");
+}
+
+/* ********************************************** */
+/* Show/Hide Bones */
+
+static int hide_selected_pose_bone_cb(Object *ob, Bone *bone, void *UNUSED(ptr))
+{
+ bArmature *arm = ob->data;
+
+ if (arm->layer & bone->layer) {
+ if (bone->flag & BONE_SELECTED) {
+ bone->flag |= BONE_HIDDEN_P;
+ bone->flag &= ~BONE_SELECTED;
+ if (arm->act_bone == bone)
+ arm->act_bone = NULL;
+ }
+ }
+ return 0;
+}
+
+static int hide_unselected_pose_bone_cb(Object *ob, Bone *bone, void *UNUSED(ptr))
+{
+ bArmature *arm = ob->data;
+
+ if (arm->layer & bone->layer) {
+ /* hrm... typo here? */
+ if ((bone->flag & BONE_SELECTED) == 0) {
+ bone->flag |= BONE_HIDDEN_P;
+ if (arm->act_bone == bone)
+ arm->act_bone = NULL;
+ }
+ }
+ return 0;
+}
+
+/* active object is armature in posemode, poll checked */
+static int pose_hide_exec(bContext *C, wmOperator *op)
+{
+ Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C));
+ bArmature *arm = ob->data;
+
+ if (RNA_boolean_get(op->ptr, "unselected"))
+ bone_looper(ob, arm->bonebase.first, NULL, hide_unselected_pose_bone_cb);
+ else
+ bone_looper(ob, arm->bonebase.first, NULL, hide_selected_pose_bone_cb);
+
+ /* note, notifier might evolve */
+ WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob);
+
+ return OPERATOR_FINISHED;
+}
+
+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";
+
+ /* api callbacks */
+ ot->exec = pose_hide_exec;
+ ot->poll = ED_operator_posemode;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* props */
+ RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "");
+}
+
+static int show_pose_bone_cb(Object *ob, Bone *bone, void *UNUSED(ptr))
+{
+ bArmature *arm = ob->data;
+
+ if (arm->layer & bone->layer) {
+ if (bone->flag & BONE_HIDDEN_P) {
+ bone->flag &= ~BONE_HIDDEN_P;
+ bone->flag |= BONE_SELECTED;
+ }
+ }
+
+ return 0;
+}
+
+/* active object is armature in posemode, poll checked */
+static int pose_reveal_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C));
+ bArmature *arm = ob->data;
+
+ bone_looper(ob, arm->bonebase.first, NULL, show_pose_bone_cb);
+
+ /* note, notifier might evolve */
+ WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob);
+
+ return OPERATOR_FINISHED;
+}
+
+void POSE_OT_reveal(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Reveal Selected";
+ ot->idname = "POSE_OT_reveal";
+ ot->description = "Unhide all bones that have been tagged to be hidden in Pose Mode";
+
+ /* api callbacks */
+ ot->exec = pose_reveal_exec;
+ ot->poll = ED_operator_posemode;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+/* ********************************************** */
+/* Flip Quats */
+
+static int pose_flip_quats_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Scene *scene = CTX_data_scene(C);
+ Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C));
+ KeyingSet *ks = ANIM_builtin_keyingset_get_named(NULL, ANIM_KS_LOC_ROT_SCALE_ID);
+
+ /* loop through all selected pchans, flipping and keying (as needed) */
+ CTX_DATA_BEGIN (C, bPoseChannel *, pchan, selected_pose_bones)
+ {
+ /* only if bone is using quaternion rotation */
+ if (pchan->rotmode == ROT_MODE_QUAT) {
+ /* quaternions have 720 degree range */
+ negate_v4(pchan->quat);
+
+ ED_autokeyframe_pchan(C, scene, ob, pchan, ks);
+ }
+ }
+ CTX_DATA_END;
+
+ /* notifiers and updates */
+ DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
+ WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, ob);
+
+ return OPERATOR_FINISHED;
+}
+
+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;
+}
+
diff --git a/source/blender/editors/armature/pose_group.c b/source/blender/editors/armature/pose_group.c
new file mode 100644
index 00000000000..168243c2bb6
--- /dev/null
+++ b/source/blender/editors/armature/pose_group.c
@@ -0,0 +1,549 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2008 Blender Foundation
+ * All rights reserved.
+ *
+ * Contributor(s): Joshua Leung
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ *
+ * Implementation of Bone Groups operators and editing API's
+ */
+
+/** \file blender/editors/armature/pose_group.c
+ * \ingroup edarmature
+ */
+
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_math.h"
+#include "BLI_blenlib.h"
+#include "BLI_dynstr.h"
+#include "BLI_utildefines.h"
+
+#include "DNA_anim_types.h"
+#include "DNA_armature_types.h"
+#include "DNA_constraint_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_object_types.h"
+
+#include "BKE_animsys.h"
+#include "BKE_anim.h"
+#include "BKE_idprop.h"
+#include "BKE_action.h"
+#include "BKE_armature.h"
+#include "BKE_context.h"
+#include "BKE_constraint.h"
+#include "BKE_deform.h"
+#include "BKE_depsgraph.h"
+#include "BKE_fcurve.h"
+#include "BKE_modifier.h"
+#include "BKE_object.h"
+#include "BKE_report.h"
+
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+#include "RNA_enum_types.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "ED_armature.h"
+#include "ED_keyframing.h"
+#include "ED_mesh.h"
+#include "ED_screen.h"
+#include "ED_object.h"
+#include "ED_util.h" /* clipboard */
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "armature_intern.h"
+
+/* ********************************************** */
+/* Bone Groups */
+
+static int pose_group_add_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Object *ob = ED_pose_object_from_context(C);
+
+ /* only continue if there's an object */
+ if (ob == NULL)
+ return OPERATOR_CANCELLED;
+
+ /* for now, just call the API function for this */
+ BKE_pose_add_group(ob);
+
+ /* notifiers for updates */
+ WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
+
+ 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";
+
+ /* api callbacks */
+ ot->exec = pose_group_add_exec;
+ ot->poll = ED_operator_posemode_context;
+
+ /* 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);
+
+ /* only continue if there's an object */
+ if (ob == NULL)
+ return OPERATOR_CANCELLED;
+
+ /* for now, just call the API function for this */
+ BKE_pose_remove_group(ob);
+
+ /* notifiers for updates */
+ WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
+
+ 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";
+
+ /* api callbacks */
+ ot->exec = pose_group_remove_exec;
+ ot->poll = ED_operator_posemode_context;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+/* ------------ */
+
+/* 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, wmEvent *UNUSED(evt))
+{
+ Object *ob = ED_pose_object_from_context(C);
+ bPose *pose;
+
+ 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 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 = uiPupMenuBegin(C, op->type->name, ICON_NONE);
+ layout = uiPupMenuLayout(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) */
+ uiPupMenuEnd(C, pup);
+
+ return OPERATOR_CANCELLED;
+ }
+ 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;
+ short 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);
+
+ /* add selected bones to group then */
+ CTX_DATA_BEGIN (C, bPoseChannel *, pchan, selected_pose_bones)
+ {
+ pchan->agrp_index = pose->active_group;
+ done = TRUE;
+ }
+ CTX_DATA_END;
+
+ /* notifiers for updates */
+ WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
+
+ /* 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";
+
+ /* 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;
+
+ /* 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);
+ short 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 */
+ CTX_DATA_BEGIN (C, bPoseChannel *, pchan, selected_pose_bones)
+ {
+ if (pchan->agrp_index) {
+ pchan->agrp_index = 0;
+ done = TRUE;
+ }
+ }
+ CTX_DATA_END;
+
+ /* notifiers for updates */
+ WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
+
+ /* 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";
+
+ /* api callbacks */
+ ot->exec = pose_group_unassign_exec;
+ ot->poll = ED_operator_posemode_context;
+
+ /* 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");
+ int grpIndexA, grpIndexB;
+
+ 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 */
+ grpIndexA = pose->active_group;
+ if (dir == 1) { /* up */
+ void *prev = grp->prev;
+
+ if (prev == NULL)
+ return OPERATOR_FINISHED;
+
+ BLI_remlink(&pose->agroups, grp);
+ BLI_insertlinkbefore(&pose->agroups, prev, grp);
+
+ grpIndexB = grpIndexA - 1;
+ pose->active_group--;
+ }
+ else { /* down */
+ void *next = grp->next;
+
+ if (next == NULL)
+ return OPERATOR_FINISHED;
+
+ BLI_remlink(&pose->agroups, grp);
+ BLI_insertlinkafter(&pose->agroups, next, grp);
+
+ grpIndexB = grpIndexA + 1;
+ pose->active_group++;
+ }
+
+ /* 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 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, UP or DOWN");
+}
+
+/* bone group sort element */
+typedef struct tSortActionGroup {
+ bActionGroup *agrp;
+ int index;
+} tSortActionGroup;
+
+/* compare bone groups by name */
+static int compare_agroup(const void *sgrp_a_ptr, const void *sgrp_b_ptr)
+{
+ tSortActionGroup *sgrp_a = (tSortActionGroup *)sgrp_a_ptr;
+ tSortActionGroup *sgrp_b = (tSortActionGroup *)sgrp_b_ptr;
+
+ 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_countlist(&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 */
+ pose->agroups.first = pose->agroups.last = NULL;
+ 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);
+
+ 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";
+
+ /* api callbacks */
+ ot->exec = group_sort_exec;
+ ot->poll = ED_operator_posemode_context;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+static void pose_group_select(bContext *C, Object *ob, int select)
+{
+ bPose *pose = ob->pose;
+
+ CTX_DATA_BEGIN (C, bPoseChannel *, pchan, visible_pose_bones)
+ {
+ 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;
+ }
+ }
+ }
+ CTX_DATA_END;
+}
+
+static int pose_group_select_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ 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;
+
+ pose_group_select(C, ob, 1);
+
+ /* notifiers for updates */
+ WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
+
+ 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";
+
+ /* api callbacks */
+ ot->exec = pose_group_select_exec;
+ ot->poll = ED_operator_posemode_context;
+
+ /* 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);
+
+ /* only continue if there's an object, and a pose there too */
+ if (ELEM(NULL, ob, ob->pose))
+ return OPERATOR_CANCELLED;
+
+ pose_group_select(C, ob, 0);
+
+ /* notifiers for updates */
+ WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
+
+ 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";
+
+ /* api callbacks */
+ ot->exec = pose_group_deselect_exec;
+ ot->poll = ED_operator_posemode_context;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+/* ********************************************** */
diff --git a/source/blender/editors/armature/pose_select.c b/source/blender/editors/armature/pose_select.c
new file mode 100644
index 00000000000..16bc8bdfba1
--- /dev/null
+++ b/source/blender/editors/armature/pose_select.c
@@ -0,0 +1,876 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
+ * All rights reserved.
+ *
+ * Contributor(s): Blender Foundation, 2002-2009 full recode.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/editors/armature/pose_select.c
+ * \ingroup edarmature
+ */
+
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <math.h>
+#include <float.h>
+
+
+#include "DNA_anim_types.h"
+#include "DNA_action_types.h"
+#include "DNA_armature_types.h"
+#include "DNA_constraint_types.h"
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_blenlib.h"
+#include "BLI_utildefines.h"
+
+#include "BKE_animsys.h"
+#include "BKE_action.h"
+#include "BKE_armature.h"
+#include "BKE_constraint.h"
+#include "BKE_context.h"
+#include "BKE_deform.h"
+#include "BKE_depsgraph.h"
+#include "BKE_global.h"
+#include "BKE_idprop.h"
+#include "BKE_main.h"
+#include "BKE_object.h"
+#include "BKE_report.h"
+
+#include "BIF_gl.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "ED_armature.h"
+#include "ED_keyframing.h"
+#include "ED_mesh.h"
+#include "ED_object.h"
+#include "ED_screen.h"
+#include "ED_util.h"
+#include "ED_view3d.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "armature_intern.h"
+
+/* ***************** Pose Select Utilities ********************* */
+
+/* called from editview.c, for mode-less pose selection */
+/* assumes scene obact and basact is still on old situation */
+int ED_do_pose_selectbuffer(Scene *scene, Base *base, unsigned int *buffer, short hits,
+ short extend, short deselect, short toggle)
+{
+ Object *ob = base->object;
+ Bone *nearBone;
+
+ if (!ob || !ob->pose) return 0;
+
+ nearBone = get_bone_from_selectbuffer(scene, base, buffer, hits, 1);
+
+ /* if the bone cannot be affected, don't do anything */
+ if ((nearBone) && !(nearBone->flag & BONE_UNSELECTABLE)) {
+ Object *ob_act = OBACT;
+ 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 ((!extend && !deselect && !toggle) ||
+ ((ob_act && (ob_act != ob) && (ob_act->mode & OB_MODE_WEIGHT_PAINT) == 0)))
+ {
+ ED_pose_deselectall(ob, 0);
+ 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);
+ DAG_id_tag_update(&ob_act->id, OB_RECALC_DATA);
+ }
+ }
+ /* 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) {
+ DAG_id_tag_update(&ob_act->id, OB_RECALC_DATA);
+ }
+ }
+ }
+
+ return nearBone != NULL;
+}
+
+/* test==0: deselect all
+ * test==1: swap select (apply to all the opposite of current situation)
+ * test==2: only clear active tag
+ * test==3: swap select (no test / inverse selection status of all independently)
+ */
+void ED_pose_deselectall(Object *ob, int test)
+{
+ bArmature *arm = ob->data;
+ bPoseChannel *pchan;
+ int selectmode = 0;
+
+ /* we call this from outliner too */
+ if (ob->pose == NULL) {
+ return;
+ }
+
+ /* Determine if we're selecting or deselecting */
+ if (test == 1) {
+ for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
+ if (PBONE_VISIBLE(arm, pchan->bone)) {
+ if (pchan->bone->flag & BONE_SELECTED)
+ break;
+ }
+ }
+
+ if (pchan == NULL)
+ selectmode = 1;
+ }
+ else if (test == 2)
+ selectmode = 2;
+
+ /* Set the flags accordingly */
+ 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 ((pchan->bone->layer & arm->layer) && !(pchan->bone->flag & (BONE_HIDDEN_P | BONE_UNSELECTABLE))) {
+ if (test == 3) {
+ pchan->bone->flag ^= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
+ }
+ else {
+ if (selectmode == 0) pchan->bone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
+ else if (selectmode == 1) pchan->bone->flag |= BONE_SELECTED;
+ }
+ }
+ }
+}
+
+/* ***************** Selections ********************** */
+
+static void selectconnected_posebonechildren(Object *ob, Bone *bone, int extend)
+{
+ Bone *curBone;
+
+ /* stop when unconnected child is encontered, or when unselectable bone is encountered */
+ if (!(bone->flag & BONE_CONNECTED) || (bone->flag & BONE_UNSELECTABLE))
+ return;
+
+ /* XXX old cruft! use notifiers instead */
+ //select_actionchannel_by_name (ob->action, bone->name, !(shift));
+
+ 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);
+}
+
+/* within active object context */
+/* previously known as "selectconnected_posearmature" */
+static int pose_select_connected_invoke(bContext *C, wmOperator *op, wmEvent *event)
+{
+ Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C));
+ bArmature *arm = (bArmature *)ob->data;
+ Bone *bone, *curBone, *next = NULL;
+ int extend = RNA_boolean_get(op->ptr, "extend");
+
+ view3d_operator_needs_opengl(C);
+
+ if (extend)
+ bone = get_nearest_bone(C, 0, event->mval[0], event->mval[1]);
+ else
+ bone = get_nearest_bone(C, 1, event->mval[0], event->mval[1]);
+
+ 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) {
+ // XXX old cruft! use notifiers instead
+ //select_actionchannel_by_name (ob->action, curBone->name, !(shift));
+
+ 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(ob, curBone, extend);
+
+ /* updates */
+ WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob);
+
+ if (arm->flag & ARM_HAS_VIZ_DEPS) {
+ /* mask modifier ('armature' mode), etc. */
+ DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+static int pose_select_linked_poll(bContext *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";
+
+ /* api callbacks */
+ ot->exec = NULL;
+ 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);
+ Object *ob = ED_object_context(C);
+ bArmature *arm = ob->data;
+ int multipaint = scene->toolsettings->multipaint;
+
+ if (action == SEL_TOGGLE) {
+ action = CTX_DATA_COUNT(C, selected_pose_bones) ? SEL_DESELECT : SEL_SELECT;
+ }
+
+ /* Set the flags */
+ CTX_DATA_BEGIN(C, bPoseChannel *, pchan, visible_pose_bones)
+ {
+ /* select pchan only if selectable, but deselect works always */
+ switch (action) {
+ case SEL_SELECT:
+ if ((pchan->bone->flag & BONE_UNSELECTABLE) == 0)
+ 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) == 0) {
+ pchan->bone->flag |= BONE_SELECTED;
+ }
+ break;
+ }
+ }
+ CTX_DATA_END;
+
+ WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, NULL);
+
+ /* weightpaint or mask modifiers need depsgraph updates */
+ if (multipaint || (arm->flag & ARM_HAS_VIZ_DEPS)) {
+ DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
+ }
+
+ 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";
+
+ /* api callbacks */
+ ot->exec = pose_de_select_all_exec;
+ ot->poll = ED_operator_posemode;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ 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;
+ }
+
+ /* updates */
+ WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob);
+
+ if (arm->flag & ARM_HAS_VIZ_DEPS) {
+ /* mask modifier ('armature' mode), etc. */
+ DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
+ }
+
+ 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";
+
+ /* api callbacks */
+ ot->exec = pose_select_parent_exec;
+ ot->poll = ED_operator_posemode;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+/* -------------------------------------- */
+
+static int pose_select_constraint_target_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C));
+ bArmature *arm = (bArmature *)ob->data;
+ 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) {
+ bConstraintTypeInfo *cti = BKE_constraint_get_typeinfo(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) {
+ if ((ct->tar == ob) && (ct->subtarget[0])) {
+ 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;
+ found = 1;
+ }
+ }
+ }
+
+ if (cti->flush_constraint_targets)
+ cti->flush_constraint_targets(con, &targets, 1);
+ }
+ }
+ }
+ }
+ CTX_DATA_END;
+
+ if (!found)
+ return OPERATOR_CANCELLED;
+
+ /* updates */
+ WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob);
+
+ if (arm->flag & ARM_HAS_VIZ_DEPS) {
+ /* mask modifier ('armature' mode), etc. */
+ DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
+ }
+
+ 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";
+
+ /* api callbacks */
+ ot->exec = pose_select_constraint_target_exec;
+ ot->poll = ED_operator_posemode;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+/* -------------------------------------- */
+
+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;
+ Bone *curbone, *pabone, *chbone;
+ int direction = RNA_enum_get(op->ptr, "direction");
+ int add_to_sel = RNA_boolean_get(op->ptr, "extend");
+ int found = 0;
+
+ CTX_DATA_BEGIN (C, bPoseChannel *, pchan, visible_pose_bones)
+ {
+ curbone = pchan->bone;
+
+ if ((curbone->flag & BONE_UNSELECTABLE) == 0) {
+ if (curbone == arm->act_bone) {
+ if (direction == BONE_SELECT_PARENT) {
+ if (pchan->parent == NULL) continue;
+ else pabone = pchan->parent->bone;
+
+ if (PBONE_SELECTABLE(arm, pabone)) {
+ if (!add_to_sel) curbone->flag &= ~BONE_SELECTED;
+ pabone->flag |= BONE_SELECTED;
+ arm->act_bone = pabone;
+
+ found = 1;
+ break;
+ }
+ }
+ else { /* direction == BONE_SELECT_CHILD */
+ /* the child member is only assigned to connected bones, see [#30340] */
+#if 0
+ if (pchan->child == NULL) continue;
+ else chbone = pchan->child->bone;
+#else
+ /* instead. find _any_ visible child bone, using the first one is a little arbitrary - campbell */
+ chbone = pchan->child ? pchan->child->bone : NULL;
+ if (chbone == NULL) {
+ bPoseChannel *pchan_child;
+
+ for (pchan_child = ob->pose->chanbase.first; pchan_child; pchan_child = pchan_child->next) {
+ /* possible we have multiple children, some invisible */
+ if (PBONE_SELECTABLE(arm, pchan_child->bone)) {
+ if (pchan_child->parent == pchan) {
+ chbone = pchan_child->bone;
+ break;
+ }
+ }
+ }
+ }
+
+ if (chbone == NULL) continue;
+#endif
+
+ if (PBONE_SELECTABLE(arm, chbone)) {
+ if (!add_to_sel) curbone->flag &= ~BONE_SELECTED;
+ chbone->flag |= BONE_SELECTED;
+ arm->act_bone = chbone;
+
+ found = 1;
+ break;
+ }
+ }
+ }
+ }
+ }
+ CTX_DATA_END;
+
+ if (found == 0)
+ return OPERATOR_CANCELLED;
+
+ /* updates */
+ WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob);
+
+ if (arm->flag & ARM_HAS_VIZ_DEPS) {
+ /* mask modifier ('armature' mode), etc. */
+ DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+void POSE_OT_select_hierarchy(wmOperatorType *ot)
+{
+ static 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 short pose_select_same_group(bContext *C, Object *ob, short extend)
+{
+ bArmature *arm = (ob) ? ob->data : NULL;
+ bPose *pose = (ob) ? ob->pose : NULL;
+ char *group_flags;
+ int numGroups = 0;
+ short changed = 0, tagged = 0;
+
+ /* sanity checks */
+ if (ELEM3(NULL, ob, pose, arm))
+ return 0;
+
+ /* count the number of groups */
+ numGroups = BLI_countlist(&pose->agroups);
+ if (numGroups == 0)
+ return 0;
+
+ /* 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 (numGroups + 1), since (index = 0) is used for no-group
+ */
+ group_flags = MEM_callocN(numGroups + 1, "pose_select_same_group");
+
+ CTX_DATA_BEGIN (C, bPoseChannel *, pchan, visible_pose_bones)
+ {
+ /* keep track of group as group to use later? */
+ if (pchan->bone->flag & BONE_SELECTED) {
+ group_flags[pchan->agrp_index] = 1;
+ tagged = 1;
+ }
+
+ /* deselect all bones before selecting new ones? */
+ if ((extend == 0) && (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) {
+ /* only if group matches (and is not selected or current bone) */
+ CTX_DATA_BEGIN (C, bPoseChannel *, pchan, visible_pose_bones)
+ {
+ 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;
+ changed = 1;
+ }
+ }
+ }
+ CTX_DATA_END;
+ }
+
+ /* free temp info */
+ MEM_freeN(group_flags);
+
+ return changed;
+}
+
+static short pose_select_same_layer(bContext *C, Object *ob, short extend)
+{
+ bPose *pose = (ob) ? ob->pose : NULL;
+ bArmature *arm = (ob) ? ob->data : NULL;
+ short changed = 0;
+ int layers = 0;
+
+ if (ELEM3(NULL, ob, pose, arm))
+ return 0;
+
+ /* figure out what bones are selected */
+ CTX_DATA_BEGIN (C, bPoseChannel *, pchan, visible_pose_bones)
+ {
+ /* 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 == 0) && (pchan->bone->flag & BONE_UNSELECTABLE) == 0)
+ pchan->bone->flag &= ~BONE_SELECTED;
+ }
+ CTX_DATA_END;
+ if (layers == 0)
+ return 0;
+
+ /* select bones that are on same layers as layers flag */
+ CTX_DATA_BEGIN (C, bPoseChannel *, pchan, visible_pose_bones)
+ {
+ /* 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;
+ changed = 1;
+ }
+ }
+ CTX_DATA_END;
+
+ return changed;
+}
+
+static int pose_select_same_keyingset(bContext *C, Object *ob, short extend)
+{
+ KeyingSet *ks = ANIM_scene_get_active_keyingset(CTX_data_scene(C));
+ KS_Path *ksp;
+
+ bArmature *arm = (ob) ? ob->data : NULL;
+ bPose *pose = (ob) ? ob->pose : NULL;
+ short changed = 0;
+
+ /* sanity checks: validate Keying Set and object */
+ if ((ks == NULL) || (ANIM_validate_keyingset(C, NULL, ks) != 0))
+ return 0;
+
+ if (ELEM3(NULL, ob, pose, arm))
+ return 0;
+
+ /* if not extending selection, deselect all selected first */
+ if (extend == 0) {
+ CTX_DATA_BEGIN (C, bPoseChannel *, pchan, visible_pose_bones)
+ {
+ if ((pchan->bone->flag & BONE_UNSELECTABLE) == 0)
+ pchan->bone->flag &= ~BONE_SELECTED;
+ }
+ CTX_DATA_END;
+ }
+
+ /* 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 = 1;
+ }
+ }
+
+ /* free temp memory */
+ MEM_freeN(boneName);
+ }
+ }
+ }
+ }
+
+ return changed;
+}
+
+static int pose_select_grouped_exec(bContext *C, wmOperator *op)
+{
+ Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C));
+ bArmature *arm = (bArmature *)ob->data;
+ short extend = RNA_boolean_get(op->ptr, "extend");
+ short changed = 0;
+
+ /* sanity check */
+ if (ob->pose == NULL)
+ return OPERATOR_CANCELLED;
+
+ /* selection types
+ * NOTE: for the order of these, see the enum in POSE_OT_select_grouped()
+ */
+ switch (RNA_enum_get(op->ptr, "type")) {
+ case 1: /* group */
+ changed = pose_select_same_group(C, ob, extend);
+ break;
+ case 2: /* Keying Set */
+ changed = pose_select_same_keyingset(C, ob, extend);
+ break;
+ default: /* layer */
+ changed = pose_select_same_layer(C, ob, extend);
+ break;
+ }
+
+ /* notifiers for updates */
+ WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
+
+ if (arm->flag & ARM_HAS_VIZ_DEPS) {
+ /* mask modifier ('armature' mode), etc. */
+ DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
+ }
+
+ /* report done status */
+ if (changed)
+ return OPERATOR_FINISHED;
+ else
+ return OPERATOR_CANCELLED;
+}
+
+void POSE_OT_select_grouped(wmOperatorType *ot)
+{
+ static EnumPropertyItem prop_select_grouped_types[] = {
+ {0, "LAYER", 0, "Layer", "Shared layers"},
+ {1, "GROUP", 0, "Group", "Shared group"},
+ {2, "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", "");
+}
+
+/* -------------------------------------- */
+
+/* context active object, or weightpainted object with armature in posemode */
+static int pose_bone_flip_active_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Object *ob_act = CTX_data_active_object(C);
+ Object *ob = BKE_object_pose_armature_get(ob_act);
+
+ if (ob && (ob->mode & OB_MODE_POSE)) {
+ bArmature *arm = ob->data;
+
+ if (arm->act_bone) {
+ bPoseChannel *pchanf;
+ char name[MAXBONENAME];
+ flip_side_name(name, arm->act_bone->name, TRUE);
+
+ pchanf = BKE_pose_channel_find_name(ob->pose, name);
+ if (pchanf && pchanf->bone != arm->act_bone) {
+ arm->act_bone->flag &= ~BONE_SELECTED;
+ pchanf->bone->flag |= BONE_SELECTED;
+
+ arm->act_bone = pchanf->bone;
+
+ /* in weightpaint we select the associated vertex group too */
+ if (ob_act->mode & OB_MODE_WEIGHT_PAINT) {
+ ED_vgroup_select_by_name(ob_act, name);
+ DAG_id_tag_update(&ob_act->id, OB_RECALC_DATA);
+ }
+
+ WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob);
+
+ return OPERATOR_FINISHED;
+ }
+ }
+ }
+
+ return OPERATOR_CANCELLED;
+}
+
+void POSE_OT_select_flip_active(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Flip Selected Active Bone";
+ ot->idname = "POSE_OT_select_flip_active";
+ ot->description = "Activate the bone with a flipped name";
+
+ /* api callbacks */
+ ot->exec = pose_bone_flip_active_exec;
+ ot->poll = ED_operator_posemode;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
diff --git a/source/blender/editors/armature/pose_transform.c b/source/blender/editors/armature/pose_transform.c
new file mode 100644
index 00000000000..e039a307e67
--- /dev/null
+++ b/source/blender/editors/armature/pose_transform.c
@@ -0,0 +1,899 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
+ * All rights reserved.
+ *
+ * Contributor(s): Blender Foundation, 2002-2009 full recode.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/editors/armature/editarmature.c
+ * \ingroup edarmature
+ */
+
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <math.h>
+#include <float.h>
+
+
+#include "DNA_anim_types.h"
+#include "DNA_armature_types.h"
+#include "DNA_constraint_types.h"
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_blenlib.h"
+#include "BLI_math.h"
+#include "BLI_utildefines.h"
+#include "BLI_ghash.h"
+
+#include "BKE_animsys.h"
+#include "BKE_action.h"
+#include "BKE_armature.h"
+#include "BKE_context.h"
+#include "BKE_deform.h"
+#include "BKE_depsgraph.h"
+#include "BKE_DerivedMesh.h"
+#include "BKE_global.h"
+#include "BKE_idprop.h"
+#include "BKE_main.h"
+#include "BKE_object.h"
+#include "BKE_report.h"
+
+#include "BIF_gl.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "ED_armature.h"
+#include "ED_keyframing.h"
+#include "ED_object.h"
+#include "ED_screen.h"
+#include "ED_util.h"
+#include "ED_view3d.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "armature_intern.h"
+
+
+/* ********************************************** */
+/* Pose Apply */
+
+/* helper for apply_armature_pose2bones - fixes parenting of objects that are bone-parented to armature */
+static void applyarmature_fix_boneparents(Scene *scene, Object *armob)
+{
+ Object workob, *ob;
+
+ /* go through all objects in database */
+ for (ob = G.main->object.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(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)
+{
+ Scene *scene = CTX_data_scene(C);
+ Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); // must be active object, not edit-object
+ 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"); /* error_libdata(); */
+ 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(ob);
+
+ /* get pose of active object and move it out of posemode */
+ pose = ob->pose;
+
+ for (pchan = pose->chanbase.first; pchan; pchan = pchan->next) {
+ curbone = editbone_name_exists(arm->edbo, pchan->name);
+
+ /* simply copy the head/tail values from pchan over to curbone */
+ copy_v3_v3(curbone->head, pchan->pose_head);
+ copy_v3_v3(curbone->tail, pchan->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->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];
+ }
+
+ /* 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(ob);
+ ED_armature_edit_free(ob);
+
+ /* flush positions of posebones */
+ BKE_pose_where_is(scene, ob);
+
+ /* fix parenting of objects which are bone-parented */
+ applyarmature_fix_boneparents(scene, ob);
+
+ /* note, notifier might evolve */
+ WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
+
+ 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";
+
+ /* callbacks */
+ ot->exec = apply_armature_pose2bones_exec;
+ ot->poll = ED_operator_posemode;
+
+ /* 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))
+{
+ Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); // must be active object, not edit-object
+
+ /* don't check if editmode (should be done by caller) */
+ if (ob->type != OB_ARMATURE)
+ return OPERATOR_CANCELLED;
+
+ /* loop over all selected pchans
+ *
+ * TODO, loop over children before parents if multiple bones
+ * at once are to be predictable*/
+ CTX_DATA_BEGIN(C, bPoseChannel *, pchan, selected_pose_bones)
+ {
+ 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.
+ */
+ copy_m4_m4(delta_mat, pchan->chan_mat);
+ BKE_pchan_apply_mat4(pchan, delta_mat, TRUE);
+ }
+ CTX_DATA_END;
+
+ DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
+
+ /* note, notifier might evolve */
+ WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
+
+ 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";
+
+ /* callbacks */
+ ot->exec = pose_visual_transform_apply_exec;
+ ot->poll = ED_operator_posemode;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+/* ********************************************** */
+/* Copy/Paste */
+
+/* Global copy/paste buffer for pose - cleared on start/end session + before every copy operation */
+static bPose *g_posebuf = NULL;
+
+void ED_clipboard_posebuf_free(void)
+{
+ if (g_posebuf) {
+ bPoseChannel *pchan;
+
+ for (pchan = g_posebuf->chanbase.first; pchan; pchan = pchan->next) {
+ if (pchan->prop) {
+ IDP_FreeProperty(pchan->prop);
+ MEM_freeN(pchan->prop);
+ }
+ }
+
+ /* was copied without constraints */
+ BLI_freelistN(&g_posebuf->chanbase);
+ MEM_freeN(g_posebuf);
+ }
+
+ g_posebuf = NULL;
+}
+
+/* This function is used to indicate that a bone is selected
+ * and needs to be included in copy buffer (used to be for inserting keys)
+ */
+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;
+ }
+ }
+}
+
+/* perform paste pose, for a single bone
+ * < ob: object where bone to paste to lives
+ * < chan: bone that pose to paste comes from
+ * < selOnly: only paste on selected bones
+ * < flip: flip on x-axis
+ *
+ * > returns: whether the bone that we pasted to if we succeeded
+ */
+static bPoseChannel *pose_bone_do_paste(Object *ob, bPoseChannel *chan, short selOnly, short flip)
+{
+ bPoseChannel *pchan;
+ char name[MAXBONENAME];
+ short paste_ok;
+
+ /* get the name - if flipping, we must flip this first */
+ if (flip)
+ flip_side_name(name, chan->name, 0); /* 0 = don't strip off number extensions */
+ 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);
+ }
+
+ /* paste flipped pose? */
+ if (flip) {
+ pchan->loc[0] *= -1;
+
+ /* 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));
+
+ /* sanity checking */
+ if (ELEM(NULL, ob, ob->pose)) {
+ BKE_report(op->reports, RPT_ERROR, "No pose to copy");
+ return OPERATOR_CANCELLED;
+ }
+
+ /* free existing pose buffer */
+ ED_clipboard_posebuf_free();
+
+ /* sets chan->flag to POSE_KEY if bone selected, then copy those bones to the buffer */
+ set_pose_keys(ob);
+ BKE_pose_copy_data(&g_posebuf, ob->pose, 0);
+
+
+ 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";
+
+ /* api callbacks */
+ ot->exec = pose_copy_exec;
+ ot->poll = ED_operator_posemode;
+
+ /* 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;
+ int flip = RNA_boolean_get(op->ptr, "flipped");
+ int selOnly = RNA_boolean_get(op->ptr, "selected_mask");
+
+ /* get KeyingSet to use */
+ KeyingSet *ks = ANIM_get_keyingset_for_autokeying(scene, ANIM_KS_LOC_ROT_SCALE_ID);
+
+ /* sanity checks */
+ if (ELEM(NULL, ob, ob->pose))
+ return OPERATOR_CANCELLED;
+
+ if (g_posebuf == NULL) {
+ BKE_report(op->reports, RPT_ERROR, "Copy buffer is empty");
+ 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 = 0;
+ }
+
+ /* Safely merge all of the channels in the buffer pose into any existing pose */
+ for (chan = g_posebuf->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) {
+ /* keyframing tagging for successful paste */
+ ED_autokeyframe_pchan(C, scene, ob, pchan, ks);
+ }
+ }
+ }
+
+ /* Update event for pose and deformation children */
+ DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
+
+ /* 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");
+}
+
+/* ********************************************** */
+/* Clear Pose Transforms */
+
+/* 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;
+}
+
+/* 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;
+}
+
+/* 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 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);
+}
+
+/* --------------- */
+
+/* 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[])
+{
+ Scene *scene = CTX_data_scene(C);
+ Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C));
+ short autokey = 0;
+
+ /* 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 */
+ CTX_DATA_BEGIN(C, bPoseChannel *, pchan, selected_pose_bones)
+ {
+ /* run provided clearing function */
+ clear_func(pchan);
+
+ /* do auto-keyframing as appropriate */
+ if (autokeyframe_cfra_can_key(scene, &ob->id)) {
+ /* clear any unkeyed tags */
+ if (pchan->bone)
+ pchan->bone->flag &= ~BONE_UNKEYED;
+
+ /* tag for autokeying later */
+ autokey = 1;
+ }
+ else {
+ /* add unkeyed tags */
+ if (pchan->bone)
+ pchan->bone->flag |= BONE_UNKEYED;
+ }
+ }
+ CTX_DATA_END;
+
+ /* perform autokeying on the bones if needed */
+ if (autokey) {
+ /* get KeyingSet to use */
+ KeyingSet *ks = ANIM_get_keyingset_for_autokeying(scene, default_ksName);
+
+ /* insert keyframes */
+ ANIM_apply_keyingset(C, NULL, NULL, ks, MODIFYKEY_MODE_INSERT, (float)CFRA);
+
+ /* now recalculate paths */
+ if ((ob->pose->avs.path_bakeflag & MOTIONPATH_BAKE_HAS_PATHS))
+ ED_pose_recalculate_paths(scene, ob);
+ }
+
+ DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
+
+ /* note, notifier might evolve */
+ WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, ob);
+
+ return OPERATOR_FINISHED;
+}
+
+/* --------------- */
+
+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);
+}
+
+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";
+
+ /* api callbacks */
+ ot->exec = pose_clear_scale_exec;
+ ot->poll = ED_operator_posemode;
+
+ /* 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);
+}
+
+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";
+
+ /* api callbacks */
+ ot->exec = pose_clear_rot_exec;
+ ot->poll = ED_operator_posemode;
+
+ /* 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);
+}
+
+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";
+
+ /* api callbacks */
+ ot->exec = pose_clear_loc_exec;
+ ot->poll = ED_operator_posemode;
+
+ /* 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);
+}
+
+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;
+}
+
+/* ********************************************** */
+/* Clear User Transforms */
+
+static int pose_clear_user_transforms_exec(bContext *C, wmOperator *op)
+{
+ Scene *scene = CTX_data_scene(C);
+ Object *ob = CTX_data_active_object(C);
+ float cframe = (float)CFRA;
+ const short only_select = RNA_boolean_get(op->ptr, "only_selected");
+
+ 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 = {{0}};
+ 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(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 */
+ DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
+ WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, ob);
+
+ 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";
+
+ /* callbacks */
+ ot->exec = pose_clear_user_transforms_exec;
+ ot->poll = ED_operator_posemode;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_boolean(ot->srna, "only_selected", TRUE, "Only Selected", "Only visible/selected bones");
+}
diff --git a/source/blender/editors/armature/poseobject.c b/source/blender/editors/armature/poseobject.c
deleted file mode 100644
index c7d1e428355..00000000000
--- a/source/blender/editors/armature/poseobject.c
+++ /dev/null
@@ -1,2364 +0,0 @@
-/*
- * ***** BEGIN GPL LICENSE BLOCK *****
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
- * All rights reserved.
- *
- * Contributor(s): Ton Roosendaal, Blender Foundation '05, full recode.
- * Joshua Leung
- *
- * ***** END GPL LICENSE BLOCK *****
- * support for animation modes - Reevan McKay
- */
-
-/** \file blender/editors/armature/poseobject.c
- * \ingroup edarmature
- */
-
-
-#include <stdlib.h>
-#include <stddef.h>
-#include <string.h>
-
-#include "MEM_guardedalloc.h"
-
-#include "BLI_math.h"
-#include "BLI_blenlib.h"
-#include "BLI_dynstr.h"
-#include "BLI_utildefines.h"
-
-#include "DNA_anim_types.h"
-#include "DNA_armature_types.h"
-#include "DNA_constraint_types.h"
-#include "DNA_scene_types.h"
-#include "DNA_object_types.h"
-
-#include "BKE_animsys.h"
-#include "BKE_anim.h"
-#include "BKE_idprop.h"
-#include "BKE_action.h"
-#include "BKE_armature.h"
-#include "BKE_context.h"
-#include "BKE_constraint.h"
-#include "BKE_deform.h"
-#include "BKE_depsgraph.h"
-#include "BKE_fcurve.h"
-#include "BKE_modifier.h"
-#include "BKE_object.h"
-#include "BKE_report.h"
-
-
-#include "RNA_access.h"
-#include "RNA_define.h"
-#include "RNA_enum_types.h"
-
-#include "WM_api.h"
-#include "WM_types.h"
-
-#include "ED_armature.h"
-#include "ED_keyframing.h"
-#include "ED_mesh.h"
-#include "ED_screen.h"
-#include "ED_object.h"
-#include "ED_util.h" /* clipboard */
-
-#include "UI_interface.h"
-#include "UI_resources.h"
-
-#include "armature_intern.h"
-
-/* 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_BUTS) {
- 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 */
-void ED_armature_enter_posemode(bContext *C, Base *base)
-{
- ReportList *reports = CTX_wm_reports(C);
- Object *ob = base->object;
-
- if (ob->id.lib) {
- BKE_report(reports, RPT_WARNING, "Cannot pose libdata");
- return;
- }
-
- switch (ob->type) {
- case OB_ARMATURE:
- ob->restore_mode = ob->mode;
- ob->mode |= OB_MODE_POSE;
-
- WM_event_add_notifier(C, NC_SCENE | ND_MODE | NS_MODE_POSE, NULL);
-
- break;
- default:
- return;
- }
-
- /* XXX: disabled as this would otherwise cause a nasty loop... */
- //ED_object_toggle_modes(C, ob->mode);
-}
-
-void ED_armature_exit_posemode(bContext *C, Base *base)
-{
- if (base) {
- Object *ob = base->object;
-
- ob->restore_mode = ob->mode;
- ob->mode &= ~OB_MODE_POSE;
-
- WM_event_add_notifier(C, NC_SCENE | ND_MODE | NS_MODE_OBJECT, NULL);
- }
-}
-
-/* if a selected or active bone is protected, throw error (oonly if warn == 1) and return 1 */
-/* only_selected == 1: the active bone is allowed to be protected */
-#if 0 /* UNUSED 2.5 */
-static short 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;
-}
-#endif
-
-/* only for real IK, not for auto-IK */
-static int pose_channel_in_IK_chain(Object *ob, bPoseChannel *pchan, int level)
-{
- bConstraint *con;
- Bone *bone;
-
- /* No need to check if constraint is active (has influence),
- * since all constraints with CONSTRAINT_IK_AUTO are active */
- for (con = pchan->constraints.first; con; con = con->next) {
- if (con->type == CONSTRAINT_TYPE_KINEMATIC) {
- bKinematicConstraint *data = con->data;
- if (data->rootbone == 0 || data->rootbone > level) {
- if ((data->flag & CONSTRAINT_IK_AUTO) == 0)
- return 1;
- }
- }
- }
- for (bone = pchan->bone->childbase.first; bone; bone = bone->next) {
- pchan = BKE_pose_channel_find_name(ob->pose, bone->name);
- if (pchan && pose_channel_in_IK_chain(ob, pchan, level + 1))
- return 1;
- }
- return 0;
-}
-
-int ED_pose_channel_in_IK_chain(Object *ob, bPoseChannel *pchan)
-{
- return pose_channel_in_IK_chain(ob, pchan, 0);
-}
-
-/* ********************************************** */
-/* Motion Paths */
-
-/* For the object with pose/action: update paths for those that have got them
- * This should selectively update paths that exist...
- *
- * To be called from various tools that do incremental updates
- */
-void ED_pose_recalculate_paths(Scene *scene, Object *ob)
-{
- ListBase targets = {NULL, NULL};
-
- /* 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 */
- animviz_calc_motionpaths(scene, &targets);
- BLI_freelistN(&targets);
-}
-
-
-/* show popup to determine settings */
-static int pose_calculate_paths_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
-{
- Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C));
-
- if (ELEM(NULL, ob, ob->pose))
- return OPERATOR_CANCELLED;
-
- /* 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_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, 10 * UI_UNIT_X, 10 * UI_UNIT_Y);
-}
-
-/* For the object with pose/action: create path curves for selected bones
- * This recalculates the WHOLE path within the pchan->pathsf and pchan->pathef range
- */
-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);
-
- if (ELEM(NULL, ob, ob->pose))
- return OPERATOR_CANCELLED;
-
- /* 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");
-
- 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)
- {
- /* 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;
-
- /* calculate the bones that now have motionpaths... */
- /* TODO: only make for the selected bones? */
- ED_pose_recalculate_paths(scene, ob);
-
- /* notifiers for updates */
- WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
-
- 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", motionpath_bake_location_items, 0,
- "Bake Location",
- "Which point on the bones is used when calculating paths");
-}
-
-/* --------- */
-
-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);
-
- 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(scene, ob);
-
- /* notifiers for updates */
- WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
-
- 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";
-
- /* api callbakcs */
- ot->exec = pose_update_paths_exec;
- ot->poll = ED_operator_posemode_exclusive; /* TODO: this should probably check for active bone and/or existing paths */
-
- /* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-}
-
-/* --------- */
-
-/* for the object with pose/action: clear path curves for selected bones only */
-static void ED_pose_clear_paths(Object *ob)
-{
- bPoseChannel *pchan;
- short skipped = 0;
-
- if (ELEM(NULL, ob, ob->pose))
- return;
-
- /* free the motionpath blocks, but also take note of whether we skipped some... */
- for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
- if (pchan->mpath) {
- if ((pchan->bone) && (pchan->bone->flag & BONE_SELECTED)) {
- animviz_free_motionpath(pchan->mpath);
- pchan->mpath = NULL;
- }
- else
- skipped = 1;
- }
- }
-
- /* if we didn't skip any, we shouldn't have any paths left */
- if (skipped == 0)
- ob->pose->avs.path_bakeflag &= ~MOTIONPATH_BAKE_HAS_PATHS;
-}
-
-/* operator callback for this */
-static int pose_clear_paths_exec(bContext *C, wmOperator *UNUSED(op))
-{
- Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C));
-
- /* 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);
-
- /* notifiers for updates */
- WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
-
- return OPERATOR_FINISHED;
-}
-
-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 selected bones";
-
- /* api callbacks */
- ot->exec = pose_clear_paths_exec;
- ot->poll = ED_operator_posemode_exclusive;
-
- /* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-}
-
-/* ******************* Select Constraint Target Operator ************* */
-
-static int pose_select_constraint_target_exec(bContext *C, wmOperator *UNUSED(op))
-{
- Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C));
- bArmature *arm = (bArmature *)ob->data;
- 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) {
- bConstraintTypeInfo *cti = BKE_constraint_get_typeinfo(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) {
- if ((ct->tar == ob) && (ct->subtarget[0])) {
- 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;
- found = 1;
- }
- }
- }
-
- if (cti->flush_constraint_targets)
- cti->flush_constraint_targets(con, &targets, 1);
- }
- }
- }
- }
- CTX_DATA_END;
-
- if (!found)
- return OPERATOR_CANCELLED;
-
- /* updates */
- WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob);
-
- if (arm->flag & ARM_HAS_VIZ_DEPS) {
- /* mask modifier ('armature' mode), etc. */
- DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
- }
-
- 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";
-
- /* api callbacks */
- ot->exec = pose_select_constraint_target_exec;
- ot->poll = ED_operator_posemode;
-
- /* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-}
-
-/* ******************* select hierarchy operator ************* */
-
-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;
- Bone *curbone, *pabone, *chbone;
- int direction = RNA_enum_get(op->ptr, "direction");
- int add_to_sel = RNA_boolean_get(op->ptr, "extend");
- int found = 0;
-
- CTX_DATA_BEGIN (C, bPoseChannel *, pchan, visible_pose_bones)
- {
- curbone = pchan->bone;
-
- if ((curbone->flag & BONE_UNSELECTABLE) == 0) {
- if (curbone == arm->act_bone) {
- if (direction == BONE_SELECT_PARENT) {
- if (pchan->parent == NULL) continue;
- else pabone = pchan->parent->bone;
-
- if (PBONE_SELECTABLE(arm, pabone)) {
- if (!add_to_sel) curbone->flag &= ~BONE_SELECTED;
- pabone->flag |= BONE_SELECTED;
- arm->act_bone = pabone;
-
- found = 1;
- break;
- }
- }
- else { /* direction == BONE_SELECT_CHILD */
- /* the child member is only assigned to connected bones, see [#30340] */
-#if 0
- if (pchan->child == NULL) continue;
- else chbone = pchan->child->bone;
-#else
- /* instead. find _any_ visible child bone, using the first one is a little arbitrary - campbell */
- chbone = pchan->child ? pchan->child->bone : NULL;
- if (chbone == NULL) {
- bPoseChannel *pchan_child;
-
- for (pchan_child = ob->pose->chanbase.first; pchan_child; pchan_child = pchan_child->next) {
- /* possible we have multiple children, some invisible */
- if (PBONE_SELECTABLE(arm, pchan_child->bone)) {
- if (pchan_child->parent == pchan) {
- chbone = pchan_child->bone;
- break;
- }
- }
- }
- }
-
- if (chbone == NULL) continue;
-#endif
-
- if (PBONE_SELECTABLE(arm, chbone)) {
- if (!add_to_sel) curbone->flag &= ~BONE_SELECTED;
- chbone->flag |= BONE_SELECTED;
- arm->act_bone = chbone;
-
- found = 1;
- break;
- }
- }
- }
- }
- }
- CTX_DATA_END;
-
- if (found == 0)
- return OPERATOR_CANCELLED;
-
- /* updates */
- WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob);
-
- if (arm->flag & ARM_HAS_VIZ_DEPS) {
- /* mask modifier ('armature' mode), etc. */
- DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
- }
-
- return OPERATOR_FINISHED;
-}
-
-void POSE_OT_select_hierarchy(wmOperatorType *ot)
-{
- static 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");
-}
-
-/* ******************* select grouped operator ************* */
-
-static short pose_select_same_group(bContext *C, Object *ob, short extend)
-{
- bArmature *arm = (ob) ? ob->data : NULL;
- bPose *pose = (ob) ? ob->pose : NULL;
- char *group_flags;
- int numGroups = 0;
- short changed = 0, tagged = 0;
-
- /* sanity checks */
- if (ELEM3(NULL, ob, pose, arm))
- return 0;
-
- /* count the number of groups */
- numGroups = BLI_countlist(&pose->agroups);
- if (numGroups == 0)
- return 0;
-
- /* 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 (numGroups + 1), since (index = 0) is used for no-group
- */
- group_flags = MEM_callocN(numGroups + 1, "pose_select_same_group");
-
- CTX_DATA_BEGIN (C, bPoseChannel *, pchan, visible_pose_bones)
- {
- /* keep track of group as group to use later? */
- if (pchan->bone->flag & BONE_SELECTED) {
- group_flags[pchan->agrp_index] = 1;
- tagged = 1;
- }
-
- /* deselect all bones before selecting new ones? */
- if ((extend == 0) && (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) {
- /* only if group matches (and is not selected or current bone) */
- CTX_DATA_BEGIN (C, bPoseChannel *, pchan, visible_pose_bones)
- {
- 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;
- changed = 1;
- }
- }
- }
- CTX_DATA_END;
- }
-
- /* free temp info */
- MEM_freeN(group_flags);
-
- return changed;
-}
-
-static short pose_select_same_layer(bContext *C, Object *ob, short extend)
-{
- bPose *pose = (ob) ? ob->pose : NULL;
- bArmature *arm = (ob) ? ob->data : NULL;
- short changed = 0;
- int layers = 0;
-
- if (ELEM3(NULL, ob, pose, arm))
- return 0;
-
- /* figure out what bones are selected */
- CTX_DATA_BEGIN (C, bPoseChannel *, pchan, visible_pose_bones)
- {
- /* 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 == 0) && (pchan->bone->flag & BONE_UNSELECTABLE) == 0)
- pchan->bone->flag &= ~BONE_SELECTED;
- }
- CTX_DATA_END;
- if (layers == 0)
- return 0;
-
- /* select bones that are on same layers as layers flag */
- CTX_DATA_BEGIN (C, bPoseChannel *, pchan, visible_pose_bones)
- {
- /* 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;
- changed = 1;
- }
- }
- CTX_DATA_END;
-
- return changed;
-}
-
-static int pose_select_same_keyingset(bContext *C, Object *ob, short extend)
-{
- KeyingSet *ks = ANIM_scene_get_active_keyingset(CTX_data_scene(C));
- KS_Path *ksp;
-
- bArmature *arm = (ob) ? ob->data : NULL;
- bPose *pose = (ob) ? ob->pose : NULL;
- short changed = 0;
-
- /* sanity checks: validate Keying Set and object */
- if ((ks == NULL) || (ANIM_validate_keyingset(C, NULL, ks) != 0))
- return 0;
-
- if (ELEM3(NULL, ob, pose, arm))
- return 0;
-
- /* if not extending selection, deselect all selected first */
- if (extend == 0) {
- CTX_DATA_BEGIN (C, bPoseChannel *, pchan, visible_pose_bones)
- {
- if ((pchan->bone->flag & BONE_UNSELECTABLE) == 0)
- pchan->bone->flag &= ~BONE_SELECTED;
- }
- CTX_DATA_END;
- }
-
- /* 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 = 1;
- }
- }
-
- /* free temp memory */
- MEM_freeN(boneName);
- }
- }
- }
- }
-
- return changed;
-}
-
-static int pose_select_grouped_exec(bContext *C, wmOperator *op)
-{
- Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C));
- bArmature *arm = (bArmature *)ob->data;
- short extend = RNA_boolean_get(op->ptr, "extend");
- short changed = 0;
-
- /* sanity check */
- if (ob->pose == NULL)
- return OPERATOR_CANCELLED;
-
- /* selection types
- * NOTE: for the order of these, see the enum in POSE_OT_select_grouped()
- */
- switch (RNA_enum_get(op->ptr, "type")) {
- case 1: /* group */
- changed = pose_select_same_group(C, ob, extend);
- break;
- case 2: /* Keying Set */
- changed = pose_select_same_keyingset(C, ob, extend);
- break;
- default: /* layer */
- changed = pose_select_same_layer(C, ob, extend);
- break;
- }
-
- /* notifiers for updates */
- WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
-
- if (arm->flag & ARM_HAS_VIZ_DEPS) {
- /* mask modifier ('armature' mode), etc. */
- DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
- }
-
- /* report done status */
- if (changed)
- return OPERATOR_FINISHED;
- else
- return OPERATOR_CANCELLED;
-}
-
-void POSE_OT_select_grouped(wmOperatorType *ot)
-{
- static EnumPropertyItem prop_select_grouped_types[] = {
- {0, "LAYER", 0, "Layer", "Shared layers"},
- {1, "GROUP", 0, "Group", "Shared group"},
- {2, "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", "");
-}
-
-
-/* ********************************************** */
-
-/* context active object, or weightpainted object with armature in posemode */
-static int pose_bone_flip_active_exec(bContext *C, wmOperator *UNUSED(op))
-{
- Object *ob_act = CTX_data_active_object(C);
- Object *ob = BKE_object_pose_armature_get(ob_act);
-
- if (ob && (ob->mode & OB_MODE_POSE)) {
- bArmature *arm = ob->data;
-
- if (arm->act_bone) {
- bPoseChannel *pchanf;
- char name[MAXBONENAME];
- flip_side_name(name, arm->act_bone->name, TRUE);
-
- pchanf = BKE_pose_channel_find_name(ob->pose, name);
- if (pchanf && pchanf->bone != arm->act_bone) {
- arm->act_bone->flag &= ~BONE_SELECTED;
- pchanf->bone->flag |= BONE_SELECTED;
-
- arm->act_bone = pchanf->bone;
-
- /* in weightpaint we select the associated vertex group too */
- if (ob_act->mode & OB_MODE_WEIGHT_PAINT) {
- ED_vgroup_select_by_name(ob_act, name);
- DAG_id_tag_update(&ob_act->id, OB_RECALC_DATA);
- }
-
- WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob);
-
- return OPERATOR_FINISHED;
- }
- }
- }
-
- return OPERATOR_CANCELLED;
-}
-
-void POSE_OT_select_flip_active(wmOperatorType *ot)
-{
- /* identifiers */
- ot->name = "Flip Selected Active Bone";
- ot->idname = "POSE_OT_select_flip_active";
- ot->description = "Activate the bone with a flipped name";
-
- /* api callbacks */
- ot->exec = pose_bone_flip_active_exec;
- ot->poll = ED_operator_posemode;
-
- /* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-}
-
-
-/* ********************************************** */
-#if 0 /* UNUSED 2.5 */
-static void pose_copy_menu(Scene *scene)
-{
- Object *obedit = scene->obedit; // XXX context
- Object *ob = OBACT;
- bArmature *arm;
- bPoseChannel *pchan, *pchanact;
- short nr = 0;
- int i = 0;
-
- /* paranoia checks */
- if (ELEM(NULL, ob, ob->pose)) return;
- if ((ob == obedit) || (ob->mode & OB_MODE_POSE) == 0) return;
-
- pchan = BKE_pose_channel_active(ob);
-
- if (pchan == NULL) return;
- pchanact = pchan;
- arm = ob->data;
-
- /* if proxy-protected bones selected, some things (such as locks + displays) shouldn't be changeable,
- * but for constraints (just add local constraints)
- */
- if (pose_has_protected_selected(ob, 0)) {
- i = BLI_countlist(&(pchanact->constraints)); /* if there are 24 or less, allow for the user to select constraints */
- if (i < 25)
- nr = pupmenu("Copy Pose Attributes %t|Local Location %x1|Local Rotation %x2|Local Size %x3|%l|Visual Location %x9|Visual Rotation %x10|Visual Size %x11|%l|Constraints (All) %x4|Constraints... %x5");
- else
- nr = pupmenu("Copy Pose Attributes %t|Local Location %x1|Local Rotation %x2|Local Size %x3|%l|Visual Location %x9|Visual Rotation %x10|Visual Size %x11|%l|Constraints (All) %x4");
- }
- else {
- i = BLI_countlist(&(pchanact->constraints)); /* if there are 24 or less, allow for the user to select constraints */
- if (i < 25)
- nr = pupmenu("Copy Pose Attributes %t|Local Location %x1|Local Rotation %x2|Local Size %x3|%l|Visual Location %x9|Visual Rotation %x10|Visual Size %x11|%l|Constraints (All) %x4|Constraints... %x5|%l|Transform Locks %x6|IK Limits %x7|Bone Shape %x8");
- else
- nr = pupmenu("Copy Pose Attributes %t|Local Location %x1|Local Rotation %x2|Local Size %x3|%l|Visual Location %x9|Visual Rotation %x10|Visual Size %x11|%l|Constraints (All) %x4|%l|Transform Locks %x6|IK Limits %x7|Bone Shape %x8");
- }
-
- if (nr <= 0)
- return;
-
- if (nr != 5) {
- for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
- if ((arm->layer & pchan->bone->layer) &&
- (pchan->bone->flag & BONE_SELECTED) &&
- (pchan != pchanact) )
- {
- switch (nr) {
- case 1: /* Local Location */
- copy_v3_v3(pchan->loc, pchanact->loc);
- break;
- case 2: /* Local Rotation */
- copy_qt_qt(pchan->quat, pchanact->quat);
- copy_v3_v3(pchan->eul, pchanact->eul);
- break;
- case 3: /* Local Size */
- copy_v3_v3(pchan->size, pchanact->size);
- break;
- case 4: /* All Constraints */
- {
- ListBase tmp_constraints = {NULL, NULL};
-
- /* copy constraints to tmpbase and apply 'local' tags before
- * appending to list of constraints for this channel
- */
- BKE_copy_constraints(&tmp_constraints, &pchanact->constraints, TRUE);
- if ((ob->proxy) && (pchan->bone->layer & arm->layer_protected)) {
- bConstraint *con;
-
- /* add proxy-local tags */
- for (con = tmp_constraints.first; con; con = con->next)
- con->flag |= CONSTRAINT_PROXY_LOCAL;
- }
- BLI_movelisttolist(&pchan->constraints, &tmp_constraints);
-
- /* update flags (need to add here, not just copy) */
- pchan->constflag |= pchanact->constflag;
-
- if (ob->pose)
- ob->pose->flag |= POSE_RECALC;
- }
- break;
- case 6: /* Transform Locks */
- pchan->protectflag = pchanact->protectflag;
- break;
- case 7: /* IK (DOF) settings */
- {
- pchan->ikflag = pchanact->ikflag;
- copy_v3_v3(pchan->limitmin, pchanact->limitmin);
- copy_v3_v3(pchan->limitmax, pchanact->limitmax);
- copy_v3_v3(pchan->stiffness, pchanact->stiffness);
- pchan->ikstretch = pchanact->ikstretch;
- pchan->ikrotweight = pchanact->ikrotweight;
- pchan->iklinweight = pchanact->iklinweight;
- }
- break;
- case 8: /* Custom Bone Shape */
- pchan->custom = pchanact->custom;
- break;
- case 9: /* Visual Location */
- BKE_armature_loc_pose_to_bone(pchan, pchanact->pose_mat[3], pchan->loc);
- break;
- case 10: /* Visual Rotation */
- {
- float delta_mat[4][4];
-
- BKE_armature_mat_pose_to_bone(pchan, pchanact->pose_mat, delta_mat);
-
- if (pchan->rotmode == ROT_MODE_AXISANGLE) {
- float tmp_quat[4];
-
- /* need to convert to quat first (in temp var)... */
- mat4_to_quat(tmp_quat, delta_mat);
- quat_to_axis_angle(pchan->rotAxis, &pchan->rotAngle, tmp_quat);
- }
- else if (pchan->rotmode == ROT_MODE_QUAT)
- mat4_to_quat(pchan->quat, delta_mat);
- else
- mat4_to_eulO(pchan->eul, pchan->rotmode, delta_mat);
- }
- break;
- case 11: /* Visual Size */
- {
- float delta_mat[4][4], size[4];
-
- BKE_armature_mat_pose_to_bone(pchan, pchanact->pose_mat, delta_mat);
- mat4_to_size(size, delta_mat);
- copy_v3_v3(pchan->size, size);
- }
- }
- }
- }
- }
- else { /* constraints, optional (note: max we can have is 24 constraints) */
- bConstraint *con, *con_back;
- int const_toggle[24] = {0}; /* XXX, initialize as 0 to quiet errors */
- ListBase const_copy = {NULL, NULL};
-
- BLI_duplicatelist(&const_copy, &(pchanact->constraints));
-
- /* build the puplist of constraints */
- for (con = pchanact->constraints.first, i = 0; con; con = con->next, i++) {
- const_toggle[i] = 1;
-// add_numbut(i, TOG|INT, con->name, 0, 0, &(const_toggle[i]), "");
- }
-
-// if (!do_clever_numbuts("Select Constraints", i, REDRAW)) {
-// BLI_freelistN(&const_copy);
-// return;
-// }
-
- /* now build a new listbase from the options selected */
- for (i = 0, con = const_copy.first; con; i++) {
- /* if not selected, free/remove it from the list */
- if (!const_toggle[i]) {
- con_back = con->next;
- BLI_freelinkN(&const_copy, con);
- con = con_back;
- }
- else
- con = con->next;
- }
-
- /* Copy the temo listbase to the selected posebones */
- for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
- if ((arm->layer & pchan->bone->layer) &&
- (pchan->bone->flag & BONE_SELECTED) &&
- (pchan != pchanact) )
- {
- ListBase tmp_constraints = {NULL, NULL};
-
- /* copy constraints to tmpbase and apply 'local' tags before
- * appending to list of constraints for this channel
- */
- BKE_copy_constraints(&tmp_constraints, &const_copy, TRUE);
- if ((ob->proxy) && (pchan->bone->layer & arm->layer_protected)) {
- /* add proxy-local tags */
- for (con = tmp_constraints.first; con; con = con->next)
- con->flag |= CONSTRAINT_PROXY_LOCAL;
- }
- BLI_movelisttolist(&pchan->constraints, &tmp_constraints);
-
- /* update flags (need to add here, not just copy) */
- pchan->constflag |= pchanact->constflag;
- }
- }
- BLI_freelistN(&const_copy);
- BKE_pose_update_constraint_flags(ob->pose); /* we could work out the flags but its simpler to do this */
-
- if (ob->pose)
- ob->pose->flag |= POSE_RECALC;
- }
-
- DAG_id_tag_update(&ob->id, OB_RECALC_DATA); // and all its relations
-
- BIF_undo_push("Copy Pose Attributes");
-
-}
-#endif
-
-/* ******************** copy/paste pose ********************** */
-
-/* Global copy/paste buffer for pose - cleared on start/end session + before every copy operation */
-static bPose *g_posebuf = NULL;
-
-void ED_clipboard_posebuf_free(void)
-{
- if (g_posebuf) {
- bPoseChannel *pchan;
-
- for (pchan = g_posebuf->chanbase.first; pchan; pchan = pchan->next) {
- if (pchan->prop) {
- IDP_FreeProperty(pchan->prop);
- MEM_freeN(pchan->prop);
- }
- }
-
- /* was copied without constraints */
- BLI_freelistN(&g_posebuf->chanbase);
- MEM_freeN(g_posebuf);
- }
-
- g_posebuf = NULL;
-}
-
-/* This function is used to indicate that a bone is selected
- * and needs to be included in copy buffer (used to be for inserting keys)
- */
-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;
- }
- }
-}
-
-/* perform paste pose, for a single bone
- * < ob: object where bone to paste to lives
- * < chan: bone that pose to paste comes from
- * < selOnly: only paste on selected bones
- * < flip: flip on x-axis
- *
- * > returns: whether the bone that we pasted to if we succeeded
- */
-static bPoseChannel *pose_bone_do_paste(Object *ob, bPoseChannel *chan, short selOnly, short flip)
-{
- bPoseChannel *pchan;
- char name[MAXBONENAME];
- short paste_ok;
-
- /* get the name - if flipping, we must flip this first */
- if (flip)
- flip_side_name(name, chan->name, 0); /* 0 = don't strip off number extensions */
- 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);
- }
-
- /* paste flipped pose? */
- if (flip) {
- pchan->loc[0] *= -1;
-
- /* 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));
-
- /* sanity checking */
- if (ELEM(NULL, ob, ob->pose)) {
- BKE_report(op->reports, RPT_ERROR, "No pose to copy");
- return OPERATOR_CANCELLED;
- }
-
- /* free existing pose buffer */
- ED_clipboard_posebuf_free();
-
- /* sets chan->flag to POSE_KEY if bone selected, then copy those bones to the buffer */
- set_pose_keys(ob);
- BKE_pose_copy_data(&g_posebuf, ob->pose, 0);
-
-
- 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";
-
- /* api callbacks */
- ot->exec = pose_copy_exec;
- ot->poll = ED_operator_posemode;
-
- /* 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;
- int flip = RNA_boolean_get(op->ptr, "flipped");
- int selOnly = RNA_boolean_get(op->ptr, "selected_mask");
-
- /* get KeyingSet to use */
- KeyingSet *ks = ANIM_get_keyingset_for_autokeying(scene, ANIM_KS_LOC_ROT_SCALE_ID);
-
- /* sanity checks */
- if (ELEM(NULL, ob, ob->pose))
- return OPERATOR_CANCELLED;
-
- if (g_posebuf == NULL) {
- BKE_report(op->reports, RPT_ERROR, "Copy buffer is empty");
- 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 = 0;
- }
-
- /* Safely merge all of the channels in the buffer pose into any existing pose */
- for (chan = g_posebuf->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) {
- /* keyframing tagging for successful paste */
- ED_autokeyframe_pchan(C, scene, ob, pchan, ks);
- }
- }
- }
-
- /* Update event for pose and deformation children */
- DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
-
- /* 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");
-}
-
-/* ********************************************** */
-/* Bone Groups */
-
-static int pose_group_add_exec(bContext *C, wmOperator *UNUSED(op))
-{
- Object *ob = ED_pose_object_from_context(C);
-
- /* only continue if there's an object */
- if (ob == NULL)
- return OPERATOR_CANCELLED;
-
- /* for now, just call the API function for this */
- BKE_pose_add_group(ob);
-
- /* notifiers for updates */
- WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
-
- 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";
-
- /* api callbacks */
- ot->exec = pose_group_add_exec;
- ot->poll = ED_operator_posemode_context;
-
- /* 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);
-
- /* only continue if there's an object */
- if (ob == NULL)
- return OPERATOR_CANCELLED;
-
- /* for now, just call the API function for this */
- BKE_pose_remove_group(ob);
-
- /* notifiers for updates */
- WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
-
- 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";
-
- /* api callbacks */
- ot->exec = pose_group_remove_exec;
- ot->poll = ED_operator_posemode_context;
-
- /* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-}
-
-/* ------------ */
-
-/* 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, wmEvent *UNUSED(evt))
-{
- Object *ob = ED_pose_object_from_context(C);
- bPose *pose;
-
- 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 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 = uiPupMenuBegin(C, op->type->name, ICON_NONE);
- layout = uiPupMenuLayout(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) */
- uiPupMenuEnd(C, pup);
-
- return OPERATOR_CANCELLED;
- }
- 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;
- short 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);
-
- /* add selected bones to group then */
- CTX_DATA_BEGIN (C, bPoseChannel *, pchan, selected_pose_bones)
- {
- pchan->agrp_index = pose->active_group;
- done = TRUE;
- }
- CTX_DATA_END;
-
- /* notifiers for updates */
- WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
-
- /* 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";
-
- /* 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;
-
- /* 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);
- short 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 */
- CTX_DATA_BEGIN (C, bPoseChannel *, pchan, selected_pose_bones)
- {
- if (pchan->agrp_index) {
- pchan->agrp_index = 0;
- done = TRUE;
- }
- }
- CTX_DATA_END;
-
- /* notifiers for updates */
- WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
-
- /* 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";
-
- /* api callbacks */
- ot->exec = pose_group_unassign_exec;
- ot->poll = ED_operator_posemode_context;
-
- /* 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");
- int grpIndexA, grpIndexB;
-
- 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 */
- grpIndexA = pose->active_group;
- if (dir == 1) { /* up */
- void *prev = grp->prev;
-
- if (prev == NULL)
- return OPERATOR_FINISHED;
-
- BLI_remlink(&pose->agroups, grp);
- BLI_insertlinkbefore(&pose->agroups, prev, grp);
-
- grpIndexB = grpIndexA - 1;
- pose->active_group--;
- }
- else { /* down */
- void *next = grp->next;
-
- if (next == NULL)
- return OPERATOR_FINISHED;
-
- BLI_remlink(&pose->agroups, grp);
- BLI_insertlinkafter(&pose->agroups, next, grp);
-
- grpIndexB = grpIndexA + 1;
- pose->active_group++;
- }
-
- /* 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 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, UP or DOWN");
-}
-
-/* bone group sort element */
-typedef struct tSortActionGroup {
- bActionGroup *agrp;
- int index;
-} tSortActionGroup;
-
-/* compare bone groups by name */
-static int compare_agroup(const void *sgrp_a_ptr, const void *sgrp_b_ptr)
-{
- tSortActionGroup *sgrp_a = (tSortActionGroup *)sgrp_a_ptr;
- tSortActionGroup *sgrp_b = (tSortActionGroup *)sgrp_b_ptr;
-
- 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_countlist(&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 */
- pose->agroups.first = pose->agroups.last = NULL;
- 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);
-
- 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";
-
- /* api callbacks */
- ot->exec = group_sort_exec;
- ot->poll = ED_operator_posemode_context;
-
- /* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-}
-
-static void pose_group_select(bContext *C, Object *ob, int select)
-{
- bPose *pose = ob->pose;
-
- CTX_DATA_BEGIN (C, bPoseChannel *, pchan, visible_pose_bones)
- {
- 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;
- }
- }
- }
- CTX_DATA_END;
-}
-
-static int pose_group_select_exec(bContext *C, wmOperator *UNUSED(op))
-{
- 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;
-
- pose_group_select(C, ob, 1);
-
- /* notifiers for updates */
- WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
-
- 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";
-
- /* api callbacks */
- ot->exec = pose_group_select_exec;
- ot->poll = ED_operator_posemode_context;
-
- /* 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);
-
- /* only continue if there's an object, and a pose there too */
- if (ELEM(NULL, ob, ob->pose))
- return OPERATOR_CANCELLED;
-
- pose_group_select(C, ob, 0);
-
- /* notifiers for updates */
- WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
-
- 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";
-
- /* api callbacks */
- ot->exec = pose_group_deselect_exec;
- ot->poll = ED_operator_posemode_context;
-
- /* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-}
-
-/* ********************************************** */
-
-static int pose_flip_names_exec(bContext *C, wmOperator *UNUSED(op))
-{
- Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C));
- bArmature *arm;
-
- /* paranoia checks */
- if (ELEM(NULL, ob, ob->pose))
- return OPERATOR_CANCELLED;
- arm = ob->data;
-
- /* loop through selected bones, auto-naming them */
- CTX_DATA_BEGIN (C, bPoseChannel *, pchan, selected_pose_bones)
- {
- char newname[MAXBONENAME];
- flip_side_name(newname, pchan->name, TRUE);
- ED_armature_bone_rename(arm, pchan->name, newname);
- }
- CTX_DATA_END;
-
- /* since we renamed stuff... */
- DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
-
- /* note, notifier might evolve */
- WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
-
- 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 the names of selected bones";
-
- /* api callbacks */
- ot->exec = pose_flip_names_exec;
- ot->poll = ED_operator_posemode;
-
- /* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-}
-
-/* ------------------ */
-
-static int pose_autoside_names_exec(bContext *C, wmOperator *op)
-{
- Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C));
- bArmature *arm;
- char newname[MAXBONENAME];
- short axis = RNA_enum_get(op->ptr, "axis");
-
- /* paranoia checks */
- if (ELEM(NULL, ob, ob->pose))
- return OPERATOR_CANCELLED;
- arm = ob->data;
-
- /* loop through selected bones, auto-naming them */
- CTX_DATA_BEGIN (C, bPoseChannel *, pchan, selected_pose_bones)
- {
- 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(arm, pchan->name, newname);
- }
- CTX_DATA_END;
-
- /* since we renamed stuff... */
- DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
-
- /* note, notifier might evolve */
- WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
-
- return OPERATOR_FINISHED;
-}
-
-void POSE_OT_autoside_names(wmOperatorType *ot)
-{
- static 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)
-{
- Object *ob = CTX_data_active_object(C);
- int mode = RNA_enum_get(op->ptr, "type");
-
- /* set rotation mode of selected bones */
- CTX_DATA_BEGIN (C, bPoseChannel *, pchan, selected_pose_bones)
- {
- pchan->rotmode = mode;
- }
- CTX_DATA_END;
-
- /* notifiers and updates */
- DAG_id_tag_update((ID *)ob, OB_RECALC_DATA);
- WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, ob);
-
- 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", posebone_rotmode_items, 0, "Rotation Mode", "");
-}
-
-/* ********************************************** */
-
-/* Show all armature layers */
-static int pose_armature_layers_showall_poll(bContext *C)
-{
- /* this single operator can be used in posemode OR editmode for armatures */
- return ED_operator_posemode(C) || ED_operator_editarmature(C);
-}
-
-static int pose_armature_layers_showall_exec(bContext *C, wmOperator *op)
-{
- Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C));
- bArmature *arm = (ob) ? ob->data : NULL;
- PointerRNA ptr;
- int maxLayers = (RNA_boolean_get(op->ptr, "all")) ? 32 : 16;
- int layers[32] = {0}; /* hardcoded for now - we can only have 32 armature layers, so this should be fine... */
- 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);
-
- /* 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";
-
- /* callbacks */
- ot->exec = pose_armature_layers_showall_exec;
- ot->poll = pose_armature_layers_showall_poll;
-
- /* 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)");
-}
-
-/* ------------------- */
-
-/* Present a popup to get the layers that should be used */
-static int pose_armature_layers_invoke(bContext *C, wmOperator *op, wmEvent *evt)
-{
- Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C));
- bArmature *arm = (ob) ? ob->data : NULL;
- PointerRNA ptr;
- int layers[32]; /* hardcoded for now - we can only have 32 armature layers, so this should be fine... */
-
- /* 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, evt);
-}
-
-/* Set the visible layers for the active armature (edit and pose modes) */
-static int pose_armature_layers_exec(bContext *C, wmOperator *op)
-{
- Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C));
- PointerRNA ptr;
- int layers[32]; /* hardcoded for now - we can only have 32 armature layers, so this should be fine... */
-
- if (ELEM(NULL, ob, ob->data)) {
- return OPERATOR_CANCELLED;
- }
-
- /* 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 *)ob->data, &ptr);
- RNA_boolean_set_array(&ptr, "layers", layers);
-
- /* note, notifier might evolve */
- WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
-
- return OPERATOR_FINISHED;
-}
-
-
-void POSE_OT_armature_layers(wmOperatorType *ot)
-{
- /* identifiers */
- ot->name = "Change Armature Layers";
- ot->idname = "POSE_OT_armature_layers";
- ot->description = "Change the visible armature layers";
-
- /* callbacks */
- ot->invoke = pose_armature_layers_invoke;
- ot->exec = pose_armature_layers_exec;
- ot->poll = ED_operator_posemode;
-
- /* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-
- /* properties */
- RNA_def_boolean_layer_member(ot->srna, "layers", 32, NULL, "Layer", "Armature layers to make visible");
-}
-
-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 = pose_armature_layers_invoke;
- ot->exec = pose_armature_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 to make visible");
-}
-
-/* ------------------- */
-
-/* Present a popup to get the layers that should be used */
-static int pose_bone_layers_invoke(bContext *C, wmOperator *op, wmEvent *evt)
-{
- int layers[32] = {0}; /* hardcoded for now - we can only have 32 armature layers, so this should be fine... */
-
- /* 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++) {
- if (pchan->bone->layer & (1 << 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, evt);
-}
-
-/* Set the visible layers for the active armature (edit and pose modes) */
-static int pose_bone_layers_exec(bContext *C, wmOperator *op)
-{
- Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C));
- PointerRNA ptr;
- int layers[32]; /* hardcoded for now - we can only have 32 armature layers, so this should be fine... */
-
- if (ob == NULL || ob->data == NULL) {
- return OPERATOR_CANCELLED;
- }
-
- /* 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 (C, bPoseChannel *, pchan, selected_pose_bones)
- {
- /* 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);
- }
- CTX_DATA_END;
-
- /* note, notifier might evolve */
- WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
-
- 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");
-}
-
-/* ------------------- */
-
-/* Present a popup to get the layers that should be used */
-static int armature_bone_layers_invoke(bContext *C, wmOperator *op, wmEvent *evt)
-{
- int layers[32] = {0}; /* hardcoded for now - we can only have 32 armature layers, so this should be fine... */
-
- /* 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 & (1 << 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, evt);
-}
-
-/* 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);
- bArmature *arm = (ob) ? ob->data : NULL;
- PointerRNA ptr;
- int layers[32]; /* hardcoded for now - we can only have 32 armature layers, so this should be fine... */
-
- /* 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 (C, EditBone *, ebone, selected_editable_bones)
- {
- /* 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");
-}
-
-/* ********************************************** */
-/* Flip Quats */
-
-static int pose_flip_quats_exec(bContext *C, wmOperator *UNUSED(op))
-{
- Scene *scene = CTX_data_scene(C);
- Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C));
- KeyingSet *ks = ANIM_builtin_keyingset_get_named(NULL, ANIM_KS_LOC_ROT_SCALE_ID);
-
- /* loop through all selected pchans, flipping and keying (as needed) */
- CTX_DATA_BEGIN (C, bPoseChannel *, pchan, selected_pose_bones)
- {
- /* only if bone is using quaternion rotation */
- if (pchan->rotmode == ROT_MODE_QUAT) {
- /* quaternions have 720 degree range */
- negate_v4(pchan->quat);
-
- ED_autokeyframe_pchan(C, scene, ob, pchan, ks);
- }
- }
- CTX_DATA_END;
-
- /* notifiers and updates */
- DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
- WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, ob);
-
- return OPERATOR_FINISHED;
-}
-
-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;
-}
-
-/* ********************************************** */
-/* Clear User Transforms */
-
-static int pose_clear_user_transforms_exec(bContext *C, wmOperator *op)
-{
- Scene *scene = CTX_data_scene(C);
- Object *ob = CTX_data_active_object(C);
- float cframe = (float)CFRA;
- const short only_select = RNA_boolean_get(op->ptr, "only_selected");
-
- 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 = {{0}};
- 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(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 */
- DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
- WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, ob);
-
- 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";
-
- /* callbacks */
- ot->exec = pose_clear_user_transforms_exec;
- ot->poll = ED_operator_posemode;
-
- /* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-
- /* properties */
- RNA_def_boolean(ot->srna, "only_selected", TRUE, "Only Selected", "Only visible/selected bones");
-}
-