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
diff options
context:
space:
mode:
authorTamito Kajiyama <rd6t-kjym@asahi-net.or.jp>2013-03-03 19:07:49 +0400
committerTamito Kajiyama <rd6t-kjym@asahi-net.or.jp>2013-03-03 19:07:49 +0400
commit66a2b848972d47a033f617a40f9a9044756d1d32 (patch)
treebf30f5bc13e9c0b4f52aacead676a2c07cbb331a /source/blender/editors
parent0c5dfc8a639b04e1767852121b9dee7cd5bb050f (diff)
parent4774357b594c5cd5a38b21df131b539a197c101b (diff)
Merged changes in the trunk up to revision 54992.
Resolved conflicts: release/scripts/startup/bl_ui/space_view3d.py
Diffstat (limited to 'source/blender/editors')
-rw-r--r--source/blender/editors/animation/anim_channels_defines.c16
-rw-r--r--source/blender/editors/animation/fmodifier_ui.c4
-rw-r--r--source/blender/editors/armature/CMakeLists.txt19
-rw-r--r--source/blender/editors/armature/armature_add.c849
-rw-r--r--source/blender/editors/armature/armature_edit.c1245
-rw-r--r--source/blender/editors/armature/armature_intern.h38
-rw-r--r--source/blender/editors/armature/armature_naming.c368
-rw-r--r--source/blender/editors/armature/armature_ops.c8
-rw-r--r--source/blender/editors/armature/armature_relations.c774
-rw-r--r--source/blender/editors/armature/armature_select.c940
-rw-r--r--source/blender/editors/armature/armature_skinning.c441
-rw-r--r--source/blender/editors/armature/armature_utils.c671
-rw-r--r--source/blender/editors/armature/editarmature.c6197
-rw-r--r--source/blender/editors/armature/editarmature_generate.c11
-rw-r--r--source/blender/editors/armature/editarmature_retarget.c13
-rw-r--r--source/blender/editors/armature/editarmature_sketch.c9
-rw-r--r--source/blender/editors/armature/meshlaplacian.c8
-rw-r--r--source/blender/editors/armature/pose_edit.c1180
-rw-r--r--source/blender/editors/armature/pose_group.c523
-rw-r--r--source/blender/editors/armature/pose_lib.c (renamed from source/blender/editors/armature/poselib.c)10
-rw-r--r--source/blender/editors/armature/pose_select.c853
-rw-r--r--source/blender/editors/armature/pose_slide.c (renamed from source/blender/editors/armature/poseSlide.c)12
-rw-r--r--source/blender/editors/armature/pose_transform.c879
-rw-r--r--source/blender/editors/armature/pose_utils.c (renamed from source/blender/editors/armature/poseUtils.c)13
-rw-r--r--source/blender/editors/armature/poseobject.c2364
-rw-r--r--source/blender/editors/armature/reeb.c15
-rw-r--r--source/blender/editors/curve/CMakeLists.txt5
-rw-r--r--source/blender/editors/curve/SConscript9
-rw-r--r--source/blender/editors/curve/editcurve.c8
-rw-r--r--source/blender/editors/gpencil/gpencil_paint.c3
-rw-r--r--source/blender/editors/include/ED_armature.h2
-rw-r--r--source/blender/editors/include/ED_view3d.h14
-rw-r--r--source/blender/editors/include/UI_interface.h1
-rw-r--r--source/blender/editors/interface/interface.c75
-rw-r--r--source/blender/editors/interface/interface_handlers.c292
-rw-r--r--source/blender/editors/interface/interface_intern.h11
-rw-r--r--source/blender/editors/interface/interface_layout.c4
-rw-r--r--source/blender/editors/interface/interface_ops.c218
-rw-r--r--source/blender/editors/interface/interface_templates.c3
-rw-r--r--source/blender/editors/interface/interface_widgets.c2
-rw-r--r--source/blender/editors/interface/view2d_ops.c7
-rw-r--r--source/blender/editors/io/CMakeLists.txt4
-rw-r--r--source/blender/editors/io/SConscript3
-rw-r--r--source/blender/editors/io/io_collada.c11
-rw-r--r--source/blender/editors/mesh/editface.c14
-rw-r--r--source/blender/editors/mesh/editmesh_select.c46
-rw-r--r--source/blender/editors/mesh/editmesh_tools.c3
-rw-r--r--source/blender/editors/mesh/meshtools.c6
-rw-r--r--source/blender/editors/object/object_add.c85
-rw-r--r--source/blender/editors/object/object_constraint.c12
-rw-r--r--source/blender/editors/object/object_intern.h1
-rw-r--r--source/blender/editors/object/object_ops.c1
-rw-r--r--source/blender/editors/object/object_relations.c2
-rw-r--r--source/blender/editors/object/object_select.c4
-rw-r--r--source/blender/editors/object/object_transform.c2
-rw-r--r--source/blender/editors/object/object_vgroup.c8
-rw-r--r--source/blender/editors/physics/physics_pointcache.c26
-rw-r--r--source/blender/editors/screen/area.c8
-rw-r--r--source/blender/editors/screen/screen_ops.c10
-rw-r--r--source/blender/editors/screen/screendump.c16
-rw-r--r--source/blender/editors/sculpt_paint/paint_intern.h2
-rw-r--r--source/blender/editors/sculpt_paint/paint_mask.c6
-rw-r--r--source/blender/editors/sculpt_paint/paint_ops.c8
-rw-r--r--source/blender/editors/sculpt_paint/paint_utils.c44
-rw-r--r--source/blender/editors/sculpt_paint/paint_vertex.c3
-rw-r--r--source/blender/editors/sculpt_paint/sculpt.c6
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_undo.c2
-rw-r--r--source/blender/editors/space_buttons/buttons_texture.c4
-rw-r--r--source/blender/editors/space_clip/clip_buttons.c2
-rw-r--r--source/blender/editors/space_clip/tracking_ops.c2
-rw-r--r--source/blender/editors/space_file/file_ops.c9
-rw-r--r--source/blender/editors/space_file/fsmenu.h2
-rw-r--r--source/blender/editors/space_graph/graph_buttons.c39
-rw-r--r--source/blender/editors/space_image/image_buttons.c22
-rw-r--r--source/blender/editors/space_image/image_intern.h2
-rw-r--r--source/blender/editors/space_image/image_ops.c8
-rw-r--r--source/blender/editors/space_image/space_image.c2
-rw-r--r--source/blender/editors/space_info/info_ops.c10
-rw-r--r--source/blender/editors/space_info/info_stats.c18
-rw-r--r--source/blender/editors/space_nla/nla_buttons.c12
-rw-r--r--source/blender/editors/space_nla/nla_edit.c4
-rw-r--r--source/blender/editors/space_node/drawnode.c7
-rw-r--r--source/blender/editors/space_node/node_add.c1
-rw-r--r--source/blender/editors/space_node/node_group.c6
-rw-r--r--source/blender/editors/space_outliner/outliner_tools.c2
-rw-r--r--source/blender/editors/space_sequencer/sequencer_add.c71
-rw-r--r--source/blender/editors/space_sequencer/sequencer_draw.c9
-rw-r--r--source/blender/editors/space_sequencer/space_sequencer.c2
-rw-r--r--source/blender/editors/space_text/text_format.c2
-rw-r--r--source/blender/editors/space_text/text_header.c25
-rw-r--r--source/blender/editors/space_text/text_ops.c26
-rw-r--r--source/blender/editors/space_view3d/drawobject.c17
-rw-r--r--source/blender/editors/space_view3d/space_view3d.c31
-rw-r--r--source/blender/editors/space_view3d/view3d_edit.c324
-rw-r--r--source/blender/editors/space_view3d/view3d_header.c17
-rw-r--r--source/blender/editors/space_view3d/view3d_intern.h4
-rw-r--r--source/blender/editors/space_view3d/view3d_project.c36
-rw-r--r--source/blender/editors/space_view3d/view3d_select.c6
-rw-r--r--source/blender/editors/transform/transform.c27
-rw-r--r--source/blender/editors/transform/transform.h3
-rw-r--r--source/blender/editors/transform/transform_conversions.c2
-rw-r--r--source/blender/editors/transform/transform_manipulator.c310
-rw-r--r--source/blender/editors/transform/transform_ops.c4
-rw-r--r--source/blender/editors/util/ed_util.c22
-rw-r--r--source/blender/editors/util/undo.c4
-rw-r--r--source/blender/editors/uvedit/uvedit_ops.c24
106 files changed, 9930 insertions, 9635 deletions
diff --git a/source/blender/editors/animation/anim_channels_defines.c b/source/blender/editors/animation/anim_channels_defines.c
index bc7b62df185..533420ad641 100644
--- a/source/blender/editors/animation/anim_channels_defines.c
+++ b/source/blender/editors/animation/anim_channels_defines.c
@@ -36,6 +36,8 @@
#include "BLI_math.h"
#include "BLI_utildefines.h"
+#include "BLF_translation.h"
+
#include "DNA_anim_types.h"
#include "DNA_armature_types.h"
#include "DNA_camera_types.h"
@@ -3341,21 +3343,21 @@ static void draw_setting_widget(bAnimContext *ac, bAnimListElem *ale, bAnimChann
icon = ICON_VISIBLE_IPO_OFF;
if (ale->type == ANIMTYPE_FCURVE)
- tooltip = "Channel is visible in Graph Editor for editing";
+ tooltip = TIP_("Channel is visible in Graph Editor for editing");
else
- tooltip = "Channel(s) are visible in Graph Editor for editing";
+ tooltip = TIP_("Channels are visible in Graph Editor for editing");
break;
case ACHANNEL_SETTING_EXPAND: /* expanded triangle */
//icon = ((enabled)? ICON_TRIA_DOWN : ICON_TRIA_RIGHT);
icon = ICON_TRIA_RIGHT;
- tooltip = "Make channels grouped under this channel visible";
+ tooltip = TIP_("Make channels grouped under this channel visible");
break;
case ACHANNEL_SETTING_SOLO: /* NLA Tracks only */
//icon = ((enabled)? ICON_LAYER_ACTIVE : ICON_LAYER_USED);
icon = ICON_LAYER_USED;
- tooltip = "NLA Track is the only one evaluated for the AnimData block it belongs to";
+ tooltip = TIP_("NLA Track is the only one evaluated for the AnimData block it belongs to");
break;
/* --- */
@@ -3364,7 +3366,7 @@ static void draw_setting_widget(bAnimContext *ac, bAnimListElem *ale, bAnimChann
// TODO: what about when there's no protect needed?
//icon = ((enabled)? ICON_LOCKED : ICON_UNLOCKED);
icon = ICON_UNLOCKED;
- tooltip = "Editability of keyframes for this channel";
+ tooltip = TIP_("Editability of keyframes for this channel");
break;
case ACHANNEL_SETTING_MUTE: /* muted speaker */
@@ -3372,9 +3374,9 @@ static void draw_setting_widget(bAnimContext *ac, bAnimListElem *ale, bAnimChann
icon = ICON_MUTE_IPO_OFF;
if (ale->type == ALE_FCURVE)
- tooltip = "Does F-Curve contribute to result";
+ tooltip = TIP_("Does F-Curve contribute to result");
else
- tooltip = "Do channels contribute to result";
+ tooltip = TIP_("Do channels contribute to result");
break;
default:
diff --git a/source/blender/editors/animation/fmodifier_ui.c b/source/blender/editors/animation/fmodifier_ui.c
index cc2366affe6..dc0fba0930f 100644
--- a/source/blender/editors/animation/fmodifier_ui.c
+++ b/source/blender/editors/animation/fmodifier_ui.c
@@ -584,9 +584,9 @@ void ANIM_uiTemplate_fmodifier_draw(uiLayout *layout, ID *id, ListBase *modifier
/* name */
if (fmi)
- uiItemL(sub, fmi->name, ICON_NONE);
+ uiItemL(sub, IFACE_(fmi->name), ICON_NONE);
else
- uiItemL(sub, "<Unknown Modifier>", ICON_NONE);
+ uiItemL(sub, IFACE_("<Unknown Modifier>"), ICON_NONE);
/* right-align ------------------------------------------- */
sub = uiLayoutRow(row, TRUE);
diff --git a/source/blender/editors/armature/CMakeLists.txt b/source/blender/editors/armature/CMakeLists.txt
index 2bd9956ef5a..d9555108733 100644
--- a/source/blender/editors/armature/CMakeLists.txt
+++ b/source/blender/editors/armature/CMakeLists.txt
@@ -36,16 +36,25 @@ 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_skinning.c
+ armature_utils.c
editarmature_generate.c
editarmature_retarget.c
editarmature_sketch.c
meshlaplacian.c
- poseSlide.c
- poseUtils.c
- poselib.c
- poseobject.c
+ pose_edit.c
+ pose_lib.c
+ pose_group.c
+ pose_select.c
+ pose_slide.c
+ pose_transform.c
+ pose_utils.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..531c0551c87
--- /dev/null
+++ b/source/blender/editors/armature/armature_add.c
@@ -0,0 +1,849 @@
+/*
+ * ***** 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 "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 "BKE_action.h"
+#include "BKE_constraint.h"
+#include "BKE_context.h"
+#include "BKE_idprop.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "ED_armature.h"
+#include "ED_screen.h"
+#include "ED_view3d.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..12602c10955
--- /dev/null
+++ b/source/blender/editors/armature/armature_edit.c
@@ -0,0 +1,1245 @@
+/*
+ * ***** 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 <assert.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 "BKE_action.h"
+#include "BKE_armature.h"
+#include "BKE_constraint.h"
+#include "BKE_context.h"
+#include "BKE_global.h"
+#include "BKE_report.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "ED_armature.h"
+#include "ED_screen.h"
+#include "ED_view3d.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 ED_armature_origin_set(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;
+}
+
+
+typedef enum eCalcRollTypes {
+ CALC_ROLL_X = 0,
+ CALC_ROLL_Y = 1,
+ CALC_ROLL_Z = 2,
+
+ CALC_ROLL_ACTIVE = 5,
+ CALC_ROLL_VIEW = 6,
+ CALC_ROLL_CURSOR = 7
+} eCalcRollTypes;
+
+static EnumPropertyItem prop_calc_roll_types[] = {
+ {CALC_ROLL_X, "X", 0, "X Axis", ""},
+ {CALC_ROLL_Y, "Y", 0, "Y Axis", ""},
+ {CALC_ROLL_Z, "Z", 0, "Z Axis", ""},
+ {CALC_ROLL_ACTIVE, "ACTIVE", 0, "Active Bone", ""},
+ {CALC_ROLL_VIEW, "VIEW", 0, "View Axis", ""},
+ {CALC_ROLL_CURSOR, "CURSOR", 0, "Cursor", ""},
+ {0, NULL, 0, NULL, NULL}
+};
+
+
+static int armature_calc_roll_exec(bContext *C, wmOperator *op)
+{
+ 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 == CALC_ROLL_CURSOR) { /* 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 == CALC_ROLL_VIEW) { /* 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 == CALC_ROLL_ACTIVE) {
+ 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);
+}
+
+/* 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);
+ }
+ }
+}
+
+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);
+}
+
+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;
+
+}
diff --git a/source/blender/editors/armature/armature_intern.h b/source/blender/editors/armature/armature_intern.h
index ddf66a6169b..bfebc68ea46 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);
@@ -144,7 +146,7 @@ void SKETCH_OT_select(struct wmOperatorType *ot);
/* ******************************************************* */
/* Pose Tool Utilities (for PoseLib, Pose Sliding, etc.) */
-/* poseUtils.c */
+/* pose_utils.c */
/* Temporary data linking PoseChannels with the F-Curves they affect */
typedef struct tPChanFCurveLink {
@@ -178,7 +180,7 @@ LinkData *poseAnim_mapping_getNextFCurve(ListBase *fcuLinks, LinkData *prev, con
/* ******************************************************* */
/* PoseLib */
-/* poselib.c */
+/* pose_lib.c */
void POSELIB_OT_new(struct wmOperatorType *ot);
void POSELIB_OT_unlink(struct wmOperatorType *ot);
@@ -194,7 +196,7 @@ void POSELIB_OT_apply_pose(struct wmOperatorType *ot);
/* ******************************************************* */
/* Pose Sliding Tools */
-/* poseSlide.c */
+/* pose_slide.c */
void POSE_OT_push(struct wmOperatorType *ot);
void POSE_OT_relax(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..6417a795712
--- /dev/null
+++ b/source/blender/editors/armature/armature_naming.c
@@ -0,0 +1,368 @@
+/*
+ * ***** 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 <string.h>
+
+#include "DNA_armature_types.h"
+#include "DNA_constraint_types.h"
+#include "DNA_object_types.h"
+
+#include "BLI_blenlib.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_main.h"
+#include "BKE_modifier.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "ED_armature.h"
+#include "ED_screen.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_ops.c b/source/blender/editors/armature/armature_ops.c
index 7c5b75e56ae..f87d4bc4d47 100644
--- a/source/blender/editors/armature/armature_ops.c
+++ b/source/blender/editors/armature/armature_ops.c
@@ -28,15 +28,7 @@
* \ingroup edarmature
*/
-
-#include <stdlib.h>
-#include <math.h>
-
-#include "BLO_sys_types.h"
-
#include "BLI_math.h"
-#include "BLI_blenlib.h"
-
#include "RNA_access.h"
diff --git a/source/blender/editors/armature/armature_relations.c b/source/blender/editors/armature/armature_relations.c
new file mode 100644
index 00000000000..c64bb5c4683
--- /dev/null
+++ b/source/blender/editors/armature/armature_relations.c
@@ -0,0 +1,774 @@
+/*
+ * ***** 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 "DNA_armature_types.h"
+#include "DNA_constraint_types.h"
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+
+#include "BLI_blenlib.h"
+#include "BLI_math.h"
+
+#include "BLF_translation.h"
+
+#include "BKE_action.h"
+#include "BKE_armature.h"
+#include "BKE_constraint.h"
+#include "BKE_context.h"
+#include "BKE_depsgraph.h"
+#include "BKE_global.h"
+#include "BKE_main.h"
+#include "BKE_report.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 "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..b7a436cc209
--- /dev/null
+++ b/source/blender/editors/armature/armature_select.c
@@ -0,0 +1,940 @@
+/*
+ * ***** 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 "DNA_armature_types.h"
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+
+#include "BLI_blenlib.h"
+#include "BLI_math.h"
+
+#include "BKE_context.h"
+#include "BKE_deform.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_screen.h"
+#include "ED_view3d.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)
+{
+ bArmature *arm = (bArmature *)vc->obedit->data;
+ EditBone *ebone_next_act = arm->act_edbone;
+
+ EditBone *ebone;
+ rcti rect;
+ unsigned int buffer[MAXPICKBUF];
+ unsigned int hitresult, besthitresult = BONESEL_NOSEL;
+ int i, mindep = 4;
+ short hits;
+
+ glInitNames();
+
+ /* find the bone after the current active bone, so as to bump up its chances in selection.
+ * this way overlapping bones will cycle selection state as with objects. */
+ if (ebone_next_act &&
+ EBONE_VISIBLE(arm, ebone_next_act) &&
+ ebone_next_act->flag & (BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL))
+ {
+ ebone_next_act = ebone_next_act->next ? ebone_next_act->next : arm->edbo->first;
+ }
+ else {
+ ebone_next_act = NULL;
+ }
+
+ 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 (ebone == ebone_next_act) {
+ dep -= 1;
+ }
+
+ 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..2e43c388fa8
--- /dev/null
+++ b/source/blender/editors/armature/armature_skinning.c
@@ -0,0 +1,441 @@
+/*
+ * ***** 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 "DNA_mesh_types.h"
+#include "DNA_armature_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 "BKE_action.h"
+#include "BKE_armature.h"
+#include "BKE_deform.h"
+#include "BKE_report.h"
+#include "BKE_subsurf.h"
+#include "BKE_modifier.h"
+
+#include "ED_armature.h"
+#include "ED_mesh.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..05f48ad73f4
--- /dev/null
+++ b/source/blender/editors/armature/armature_utils.c
@@ -0,0 +1,671 @@
+/*
+ * ***** 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/armature_utils.c
+ * \ingroup edarmature
+ */
+
+#include "DNA_armature_types.h"
+#include "DNA_object_types.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_blenlib.h"
+#include "BLI_math.h"
+
+#include "BKE_armature.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 "ED_armature.h"
+#include "ED_util.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 2eac6ba87ee..00000000000
--- a/source/blender/editors/armature/editarmature.c
+++ /dev/null
@@ -1,6197 +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 "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, "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/editarmature_generate.c b/source/blender/editors/armature/editarmature_generate.c
index 979c352c4b2..89772d38e8f 100644
--- a/source/blender/editors/armature/editarmature_generate.c
+++ b/source/blender/editors/armature/editarmature_generate.c
@@ -30,24 +30,13 @@
* \ingroup edarmature
*/
-
-#include <string.h>
-#include <math.h>
-#include <float.h>
-
-
#include "DNA_scene_types.h"
#include "DNA_armature_types.h"
-#include "BLI_blenlib.h"
#include "BLI_math.h"
#include "BLI_graph.h"
-#include "BLI_utildefines.h"
-
-
#include "ED_armature.h"
-#include "armature_intern.h"
#include "BIF_generate.h"
void setBoneRollFromNormal(EditBone *bone, const float no[3], float UNUSED(invmat[4][4]), float tmat[3][3])
diff --git a/source/blender/editors/armature/editarmature_retarget.c b/source/blender/editors/armature/editarmature_retarget.c
index 3e34a4c6808..62cce6b2c1e 100644
--- a/source/blender/editors/armature/editarmature_retarget.c
+++ b/source/blender/editors/armature/editarmature_retarget.c
@@ -25,12 +25,6 @@
* \ingroup edarmature
*/
-#include <ctype.h>
-#include <stdlib.h>
-#include <string.h>
-#include <math.h>
-#include <float.h>
-
#include "MEM_guardedalloc.h"
#include "PIL_time.h"
@@ -42,11 +36,6 @@
#include "BLI_blenlib.h"
#include "BLI_math.h"
-#include "BLI_utildefines.h"
-#include "BLI_ghash.h"
-#include "BLI_graph.h"
-#include "BLI_rand.h"
-#include "BLI_threads.h"
#include "BKE_constraint.h"
#include "BKE_armature.h"
@@ -57,8 +46,6 @@
#include "BIF_retarget.h"
-#include "reeb.h" /* FIX ME */
-
#include "armature_intern.h"
/************ RIG RETARGET DATA STRUCTURES ***************/
diff --git a/source/blender/editors/armature/editarmature_sketch.c b/source/blender/editors/armature/editarmature_sketch.c
index b61aa86a52f..ec96c574f75 100644
--- a/source/blender/editors/armature/editarmature_sketch.c
+++ b/source/blender/editors/armature/editarmature_sketch.c
@@ -22,10 +22,6 @@
* \ingroup edarmature
*/
-#include <string.h>
-#include <math.h>
-#include <float.h>
-
#include "MEM_guardedalloc.h"
#include "DNA_object_types.h"
@@ -37,9 +33,6 @@
#include "BLI_blenlib.h"
#include "BLI_math.h"
-#include "BLI_utildefines.h"
-#include "BLI_graph.h"
-#include "BLI_ghash.h"
#include "BLF_translation.h"
@@ -60,8 +53,6 @@
#include "WM_api.h"
#include "WM_types.h"
-#include "reeb.h"
-
typedef int (*GestureDetectFct)(bContext *, SK_Gesture *, SK_Sketch *);
typedef void (*GestureApplyFct)(bContext *, SK_Gesture *, SK_Sketch *);
diff --git a/source/blender/editors/armature/meshlaplacian.c b/source/blender/editors/armature/meshlaplacian.c
index a7e84d590b5..3ac514f1465 100644
--- a/source/blender/editors/armature/meshlaplacian.c
+++ b/source/blender/editors/armature/meshlaplacian.c
@@ -30,18 +30,12 @@
* \ingroup edarmature
*/
-
-#include <math.h>
-#include <string.h>
-
#include "MEM_guardedalloc.h"
#include "DNA_object_types.h"
#include "DNA_mesh_types.h"
-#include "DNA_meshdata_types.h"
#include "DNA_scene_types.h"
-#include "BLI_utildefines.h"
#include "BLI_math.h"
#include "BLI_edgehash.h"
#include "BLI_memarena.h"
@@ -59,8 +53,6 @@
#include "ONL_opennl.h"
-#include "BLO_sys_types.h" // for intptr_t support
-
#include "ED_mesh.h"
#include "ED_armature.h"
diff --git a/source/blender/editors/armature/pose_edit.c b/source/blender/editors/armature/pose_edit.c
new file mode 100644
index 00000000000..3d1d5d2f6ba
--- /dev/null
+++ b/source/blender/editors/armature/pose_edit.c
@@ -0,0 +1,1180 @@
+/*
+ * ***** 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/pose_edit.c
+ * \ingroup edarmature
+ */
+
+#include "BLI_math.h"
+#include "BLI_blenlib.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_anim.h"
+#include "BKE_action.h"
+#include "BKE_armature.h"
+#include "BKE_context.h"
+#include "BKE_deform.h"
+#include "BKE_depsgraph.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_screen.h"
+#include "ED_object.h"
+
+#include "UI_interface.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..06a88f013d7
--- /dev/null
+++ b/source/blender/editors/armature/pose_group.c
@@ -0,0 +1,523 @@
+/*
+ * ***** 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 <string.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_blenlib.h"
+
+#include "DNA_armature_types.h"
+#include "DNA_object_types.h"
+
+#include "BKE_action.h"
+#include "BKE_context.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "ED_armature.h"
+#include "ED_screen.h"
+
+#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/poselib.c b/source/blender/editors/armature/pose_lib.c
index 48c0a4a38c3..457874c7ae6 100644
--- a/source/blender/editors/armature/poselib.c
+++ b/source/blender/editors/armature/pose_lib.c
@@ -23,25 +23,17 @@
* ***** END GPL LICENSE BLOCK *****
*/
-/** \file blender/editors/armature/poselib.c
+/** \file blender/editors/armature/pose_lib.c
* \ingroup edarmature
*/
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <stddef.h>
#include <string.h>
#include <math.h>
-#include <float.h>
#include "MEM_guardedalloc.h"
-#include "BLI_math.h"
#include "BLI_blenlib.h"
-#include "BLI_dynstr.h"
#include "BLI_dlrbTree.h"
-#include "BLI_utildefines.h"
#include "BLF_translation.h"
diff --git a/source/blender/editors/armature/pose_select.c b/source/blender/editors/armature/pose_select.c
new file mode 100644
index 00000000000..186c7a1fe89
--- /dev/null
+++ b/source/blender/editors/armature/pose_select.c
@@ -0,0 +1,853 @@
+/*
+ * ***** 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 <string.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 "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_object.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_view3d.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) {
+ 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/poseSlide.c b/source/blender/editors/armature/pose_slide.c
index 3fd65de6c04..fee37a9c0fb 100644
--- a/source/blender/editors/armature/poseSlide.c
+++ b/source/blender/editors/armature/pose_slide.c
@@ -23,25 +23,15 @@
* ***** END GPL LICENSE BLOCK *****
*/
-/** \file blender/editors/armature/poseSlide.c
+/** \file blender/editors/armature/pose_slide.c
* \ingroup edarmature
*/
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <stddef.h>
-#include <string.h>
-#include <math.h>
-#include <float.h>
-
#include "MEM_guardedalloc.h"
#include "BLI_math.h"
#include "BLI_blenlib.h"
-#include "BLI_dynstr.h"
#include "BLI_dlrbTree.h"
-#include "BLI_utildefines.h"
#include "DNA_anim_types.h"
#include "DNA_armature_types.h"
diff --git a/source/blender/editors/armature/pose_transform.c b/source/blender/editors/armature/pose_transform.c
new file mode 100644
index 00000000000..2525641e58c
--- /dev/null
+++ b/source/blender/editors/armature/pose_transform.c
@@ -0,0 +1,879 @@
+/*
+ * ***** 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_transform.c
+ * \ingroup edarmature
+ */
+
+#include "DNA_anim_types.h"
+#include "DNA_armature_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 "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_global.h"
+#include "BKE_idprop.h"
+#include "BKE_main.h"
+#include "BKE_object.h"
+#include "BKE_report.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_screen.h"
+#include "ED_util.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/poseUtils.c b/source/blender/editors/armature/pose_utils.c
index f3c32399ad6..a5e51ccf32a 100644
--- a/source/blender/editors/armature/poseUtils.c
+++ b/source/blender/editors/armature/pose_utils.c
@@ -23,25 +23,14 @@
* ***** END GPL LICENSE BLOCK *****
*/
-/** \file blender/editors/armature/poseUtils.c
+/** \file blender/editors/armature/pose_utils.c
* \ingroup edarmature
*/
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <stddef.h>
-#include <string.h>
-#include <math.h>
-#include <float.h>
-
#include "MEM_guardedalloc.h"
#include "BLI_math.h"
#include "BLI_blenlib.h"
-#include "BLI_dynstr.h"
-#include "BLI_dlrbTree.h"
-#include "BLI_utildefines.h"
#include "DNA_anim_types.h"
#include "DNA_armature_types.h"
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");
-}
-
diff --git a/source/blender/editors/armature/reeb.c b/source/blender/editors/armature/reeb.c
index 1b5c550df11..454b0f0bceb 100644
--- a/source/blender/editors/armature/reeb.c
+++ b/source/blender/editors/armature/reeb.c
@@ -24,29 +24,14 @@
* \ingroup edarmature
*/
-#include <math.h>
-#include <string.h> /* for memcpy */
-#include <stdio.h>
-#include <stdlib.h> /* for qsort */
-#include <float.h>
-
-#include "DNA_scene_types.h"
-#include "DNA_object_types.h"
-
#include "MEM_guardedalloc.h"
#include "BKE_context.h"
#include "BLI_blenlib.h"
#include "BLI_math.h"
-#include "BLI_utildefines.h"
#include "BLI_edgehash.h"
#include "BLI_ghash.h"
-#include "BLI_heap.h"
-
-#include "BKE_mesh.h"
-
-#include "ONL_opennl.h"
#include "reeb.h"
diff --git a/source/blender/editors/curve/CMakeLists.txt b/source/blender/editors/curve/CMakeLists.txt
index 25df17d232a..c38ded49830 100644
--- a/source/blender/editors/curve/CMakeLists.txt
+++ b/source/blender/editors/curve/CMakeLists.txt
@@ -20,6 +20,7 @@
set(INC
../include
+ ../../blenfont
../../blenkernel
../../blenlib
../../blenloader
@@ -42,4 +43,8 @@ set(SRC
curve_intern.h
)
+if(WITH_INTERNATIONAL)
+ add_definitions(-DWITH_INTERNATIONAL)
+endif()
+
blender_add_lib(bf_editor_curve "${SRC}" "${INC}" "${INC_SYS}")
diff --git a/source/blender/editors/curve/SConscript b/source/blender/editors/curve/SConscript
index abefd3c6dd6..21c6a3732fa 100644
--- a/source/blender/editors/curve/SConscript
+++ b/source/blender/editors/curve/SConscript
@@ -29,9 +29,14 @@ Import ('env')
sources = env.Glob('*.c')
-incs = '../include ../../blenlib ../../blenkernel ../../makesdna ../../imbuf'
+defs = []
+
+incs = '../include ../../blenfont ../../blenlib ../../blenkernel ../../makesdna ../../imbuf'
incs += ' ../../windowmanager #/intern/guardedalloc #/extern/glew/include'
incs += ' ../../bmesh ../../gpu ../../blenloader'
incs += ' ../../makesrna ../../render/extern/include #/intern/elbeem/extern'
-env.BlenderLib ( 'bf_editors_curve', sources, Split(incs), [], libtype=['core'], priority=[45] )
+if env['WITH_BF_INTERNATIONAL']:
+ defs.append('WITH_INTERNATIONAL')
+
+env.BlenderLib ( 'bf_editors_curve', sources, Split(incs), defs, libtype=['core'], priority=[45] )
diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c
index b5aa55dbda9..d01e5c5d9bf 100644
--- a/source/blender/editors/curve/editcurve.c
+++ b/source/blender/editors/curve/editcurve.c
@@ -53,6 +53,8 @@
#include "BLI_utildefines.h"
#include "BLI_ghash.h"
+#include "BLF_translation.h"
+
#include "BKE_context.h"
#include "BKE_curve.h"
#include "BKE_depsgraph.h"
@@ -4925,7 +4927,7 @@ static int toggle_cyclic_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(eve
for (nu = editnurb->first; nu; nu = nu->next) {
if (nu->pntsu > 1 || nu->pntsv > 1) {
if (nu->type == CU_NURBS) {
- pup = uiPupMenuBegin(C, "Direction", ICON_NONE);
+ pup = uiPupMenuBegin(C, IFACE_("Direction"), ICON_NONE);
layout = uiPupMenuLayout(pup);
uiItemsEnumO(layout, op->type->idname, "direction");
uiPupMenuEnd(C, pup);
@@ -6051,14 +6053,14 @@ static int delete_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
uiLayout *layout;
if (obedit->type == OB_SURF) {
- pup = uiPupMenuBegin(C, "Delete", ICON_NONE);
+ pup = uiPupMenuBegin(C, IFACE_("Delete"), ICON_NONE);
layout = uiPupMenuLayout(pup);
uiItemEnumO_ptr(layout, op->type, NULL, 0, "type", 0);
uiItemEnumO_ptr(layout, op->type, NULL, 0, "type", 2);
uiPupMenuEnd(C, pup);
}
else {
- pup = uiPupMenuBegin(C, "Delete", ICON_NONE);
+ pup = uiPupMenuBegin(C, IFACE_("Delete"), ICON_NONE);
layout = uiPupMenuLayout(pup);
uiItemsEnumO(layout, op->type->idname, "type");
uiPupMenuEnd(C, pup);
diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c
index 8cd2bd861bc..46815450bf2 100644
--- a/source/blender/editors/gpencil/gpencil_paint.c
+++ b/source/blender/editors/gpencil/gpencil_paint.c
@@ -931,7 +931,8 @@ static void gp_stroke_eraser_dostroke(tGPsdata *p,
/* check that point segment of the boundbox of the eraser stroke */
if (((!ELEM(V2D_IS_CLIPPED, x0, y0)) && BLI_rcti_isect_pt(rect, x0, y0)) ||
- ((!ELEM(V2D_IS_CLIPPED, x1, y1)) && BLI_rcti_isect_pt(rect, x1, y1))) {
+ ((!ELEM(V2D_IS_CLIPPED, x1, y1)) && BLI_rcti_isect_pt(rect, x1, y1)))
+ {
/* check if point segment of stroke had anything to do with
* eraser region (either within stroke painted, or on its lines)
* - this assumes that linewidth is irrelevant
diff --git a/source/blender/editors/include/ED_armature.h b/source/blender/editors/include/ED_armature.h
index 4db79df033e..30ac360cab6 100644
--- a/source/blender/editors/include/ED_armature.h
+++ b/source/blender/editors/include/ED_armature.h
@@ -137,7 +137,7 @@ struct EditBone *ED_armature_edit_bone_add(struct bArmature *arm, const char *na
void ED_armature_edit_bone_remove(struct bArmature *arm, EditBone *exBone);
void transform_armature_mirror_update(struct Object *obedit);
-void docenter_armature(struct Scene *scene, struct Object *ob, float cursor[3], int centermode, int around);
+void ED_armature_origin_set(struct Scene *scene, struct Object *ob, float cursor[3], int centermode, int around);
void ED_armature_apply_transform(struct Object *ob, float mat[4][4]);
diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h
index 46f4515e0ca..d89d66dd62c 100644
--- a/source/blender/editors/include/ED_view3d.h
+++ b/source/blender/editors/include/ED_view3d.h
@@ -105,9 +105,10 @@ void ED_view3d_depth_tag_update(struct RegionView3D *rv3d);
typedef enum {
V3D_PROJ_RET_OK = 0,
V3D_PROJ_RET_CLIP_NEAR = 1, /* can't avoid this when in perspective mode, (can't avoid) */
- V3D_PROJ_RET_CLIP_BB = 2, /* bounding box clip - RV3D_CLIPPING */
- V3D_PROJ_RET_CLIP_WIN = 3, /* outside window bounds */
- V3D_PROJ_RET_OVERFLOW = 4 /* outside range (mainly for short), (can't avoid) */
+ V3D_PROJ_RET_CLIP_ZERO = 2, /* so close to zero we can't apply a perspective matrix usefully */
+ V3D_PROJ_RET_CLIP_BB = 3, /* bounding box clip - RV3D_CLIPPING */
+ V3D_PROJ_RET_CLIP_WIN = 4, /* outside window bounds */
+ V3D_PROJ_RET_OVERFLOW = 5 /* outside range (mainly for short), (can't avoid) */
} eV3DProjStatus;
/* some clipping tests are optional */
@@ -116,10 +117,13 @@ typedef enum {
V3D_PROJ_TEST_CLIP_BB = (1 << 0),
V3D_PROJ_TEST_CLIP_WIN = (1 << 1),
V3D_PROJ_TEST_CLIP_NEAR = (1 << 2),
+ V3D_PROJ_TEST_CLIP_ZERO = (1 << 3)
} eV3DProjTest;
-#define V3D_PROJ_TEST_CLIP_DEFAULT (V3D_PROJ_TEST_CLIP_BB | V3D_PROJ_TEST_CLIP_WIN | V3D_PROJ_TEST_CLIP_NEAR)
-#define V3D_PROJ_TEST_ALL (V3D_PROJ_TEST_CLIP_BB | V3D_PROJ_TEST_CLIP_WIN | V3D_PROJ_TEST_CLIP_NEAR)
+#define V3D_PROJ_TEST_CLIP_DEFAULT \
+ (V3D_PROJ_TEST_CLIP_BB | V3D_PROJ_TEST_CLIP_WIN | V3D_PROJ_TEST_CLIP_NEAR)
+#define V3D_PROJ_TEST_ALL \
+ (V3D_PROJ_TEST_CLIP_BB | V3D_PROJ_TEST_CLIP_WIN | V3D_PROJ_TEST_CLIP_NEAR | V3D_PROJ_TEST_CLIP_ZERO)
/* view3d_iterators.c */
diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h
index 78689c078c6..3c8a9a87fbe 100644
--- a/source/blender/editors/include/UI_interface.h
+++ b/source/blender/editors/include/UI_interface.h
@@ -183,6 +183,7 @@ typedef struct uiLayout uiLayout;
/* scale fixed button widths by this to account for DPI */
#define UI_DPI_FAC ((U.pixelsize * (float)U.dpi) / 72.0f)
+#define UI_DPI_WINDOW_FAC (((float)U.dpi) / 72.0f)
/* 16 to copy ICON_DEFAULT_HEIGHT */
#define UI_DPI_ICON_SIZE ((float)16 * UI_DPI_FAC)
diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c
index 37249896e34..2c17a629ed4 100644
--- a/source/blender/editors/interface/interface.c
+++ b/source/blender/editors/interface/interface.c
@@ -1097,34 +1097,29 @@ void uiDrawBlock(const bContext *C, uiBlock *block)
/* ************* EVENTS ************* */
-static void ui_is_but_sel(uiBut *but, double *value)
+int ui_is_but_push_ex(uiBut *but, double *value)
{
- short is_push = 0; /* (0 == UNSELECT), (1 == SELECT), (2 == DO-NOHING) */
- short is_true = TRUE;
-
- if (ELEM3(but->type, TOGN, ICONTOGN, OPTIONN)) {
- is_true = FALSE;
- }
+ int is_push = false; /* (0 == UNSELECT), (1 == SELECT), (-1 == DO-NOHING) */
if (but->bit) {
+ const bool state = ELEM3(but->type, TOGN, ICONTOGN, OPTIONN) ? false : true;
int lvalue;
UI_GET_BUT_VALUE_INIT(but, *value);
lvalue = (int)*value;
if (UI_BITBUT_TEST(lvalue, (but->bitnr))) {
- is_push = is_true;
+ is_push = state;
}
else {
- is_push = !is_true;
+ is_push = !state;
}
}
else {
switch (but->type) {
case BUT:
- is_push = 2;
- break;
case HOTKEYEVT:
case KEYEVT:
- is_push = 2;
+ case COLOR:
+ is_push = -1;
break;
case TOGBUT:
case TOG:
@@ -1134,42 +1129,48 @@ static void ui_is_but_sel(uiBut *but, double *value)
case ICONTOG:
case OPTION:
UI_GET_BUT_VALUE_INIT(but, *value);
- if (*value != (double)but->hardmin) is_push = 1;
+ if (*value != (double)but->hardmin) is_push = true;
break;
case ICONTOGN:
case TOGN:
case OPTIONN:
UI_GET_BUT_VALUE_INIT(but, *value);
- if (*value == 0.0) is_push = 1;
+ if (*value == 0.0) is_push = true;
break;
case ROW:
case LISTROW:
UI_GET_BUT_VALUE_INIT(but, *value);
/* support for rna enum buts */
if (but->rnaprop && (RNA_property_flag(but->rnaprop) & PROP_ENUM_FLAG)) {
- if ((int)*value & (int)but->hardmax) is_push = 1;
+ if ((int)*value & (int)but->hardmax) is_push = true;
}
else {
- if (*value == (double)but->hardmax) is_push = 1;
+ if (*value == (double)but->hardmax) is_push = true;
}
break;
- case COLOR:
- is_push = 2;
- break;
default:
- is_push = 2;
+ is_push = -1;
break;
}
}
-
- if (is_push == 2) {
- /* pass */
- }
- else if (is_push == 1) {
- but->flag |= UI_SELECT;
- }
- else {
- but->flag &= ~UI_SELECT;
+
+ return is_push;
+}
+int ui_is_but_push(uiBut *but)
+{
+ double value = UI_BUT_VALUE_UNSET;
+ return ui_is_but_push_ex(but, &value);
+}
+
+static void ui_check_but_select(uiBut *but, double *value)
+{
+ switch (ui_is_but_push_ex(but, value)) {
+ case true:
+ but->flag |= UI_SELECT;
+ break;
+ case false:
+ but->flag &= ~UI_SELECT;
+ break;
}
}
@@ -1620,8 +1621,7 @@ void ui_set_but_val(uiBut *but, double value)
value = *((float *)but->poin) = (float)value;
}
- /* update select flag */
- ui_is_but_sel(but, &value);
+ ui_check_but_select(but, &value);
}
int ui_get_but_string_max_length(uiBut *but)
@@ -2256,7 +2256,7 @@ void ui_check_but(uiBut *but)
double value = UI_BUT_VALUE_UNSET;
// float okwidth; // UNUSED
- ui_is_but_sel(but, &value);
+ ui_check_but_select(but, &value);
/* only update soft range while not editing */
if (but->rnaprop && !(but->editval || but->editstr || but->editvec)) {
@@ -3929,15 +3929,20 @@ void uiButGetStrInfo(bContext *C, uiBut *but, ...)
}
}
else if (type == BUT_GET_RNA_LABEL_CONTEXT) {
+ const char *_tmp = NULL;
if (but->rnaprop)
- tmp = BLI_strdup(RNA_property_translation_context(but->rnaprop));
+ _tmp = RNA_property_translation_context(but->rnaprop);
else if (but->optype)
- tmp = BLI_strdup(RNA_struct_translation_context(but->optype->srna));
+ _tmp = RNA_struct_translation_context(but->optype->srna);
else if (ELEM(but->type, MENU, PULLDOWN)) {
MenuType *mt = uiButGetMenuType(but);
if (mt)
- tmp = BLI_strdup(RNA_struct_translation_context(mt->ext.srna));
+ _tmp = RNA_struct_translation_context(mt->ext.srna);
+ }
+ if (!_tmp) { /* _tmp == BLF_I18NCONTEXT_DEFAULT */
+ _tmp = BLF_I18NCONTEXT_DEFAULT_BPY;
}
+ tmp = BLI_strdup(_tmp);
}
else if (ELEM3(type, BUT_GET_RNAENUM_IDENTIFIER, BUT_GET_RNAENUM_LABEL, BUT_GET_RNAENUM_TIP)) {
PointerRNA *ptr = NULL;
diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c
index 5cff61f4239..e0d6c293be5 100644
--- a/source/blender/editors/interface/interface_handlers.c
+++ b/source/blender/editors/interface/interface_handlers.c
@@ -81,6 +81,8 @@
/* place the mouse at the scaled down location when un-grabbing */
#define USE_CONT_MOUSE_CORRECT
+/* support dragging toggle buttons */
+#define USE_DRAG_TOGGLE
/* proto */
static void ui_add_smart_controller(bContext *C, uiBut *from, uiBut *to);
@@ -151,6 +153,7 @@ typedef struct uiHandleButtonData {
int maxlen, selextend, selstartx;
/* number editing / dragging */
+ /* coords are Window/uiBlock relative (depends on the button) */
int draglastx, draglasty;
int dragstartx, dragstarty;
int dragchange, draglock, dragsel;
@@ -406,9 +409,9 @@ static void ui_apply_but_func(bContext *C, uiBut *but)
}
}
-static void ui_apply_autokey_undo(bContext *C, uiBut *but)
+/* typically call ui_apply_undo(), ui_apply_autokey() */
+static void ui_apply_undo(uiBut *but)
{
- Scene *scene = CTX_data_scene(C);
uiAfterFunc *after;
if (but->flag & UI_BUT_UNDO) {
@@ -430,6 +433,11 @@ static void ui_apply_autokey_undo(bContext *C, uiBut *but)
BLI_strncpy(after->undostr, str, sizeof(after->undostr));
BLI_addtail(&UIAfterFuncs, after);
}
+}
+
+static void ui_apply_autokey(bContext *C, uiBut *but)
+{
+ Scene *scene = CTX_data_scene(C);
/* try autokey */
ui_but_anim_autokey(C, but, scene, scene->r.cfra);
@@ -732,6 +740,153 @@ static void ui_apply_but_CHARTAB(bContext *C, uiBut *but, uiHandleButtonData *da
/* ****************** drag drop code *********************** */
+#ifdef USE_DRAG_TOGGLE
+
+typedef struct uiDragToggleHandle {
+ /* init */
+ bool is_set;
+ float but_cent_start[2];
+ eButType but_type_start;
+
+ bool xy_lock[2];
+ int xy_last[2];
+} uiDragToggleHandle;
+
+static bool ui_drag_toggle_set_xy_xy(bContext *C, ARegion *ar, const bool is_set, const eButType but_type_start,
+ const int xy_src[2], const int xy_dst[2])
+{
+ bool change = false;
+ uiBlock *block;
+
+ for (block = ar->uiblocks.first; block; block = block->next) {
+ uiBut *but;
+
+ float xy_a_block[2] = {UNPACK2(xy_src)};
+ float xy_b_block[2] = {UNPACK2(xy_dst)};
+
+ ui_window_to_block_fl(ar, block, &xy_a_block[0], &xy_a_block[1]);
+ ui_window_to_block_fl(ar, block, &xy_b_block[0], &xy_b_block[1]);
+
+ for (but = block->buttons.first; but; but = but->next) {
+ if (ui_is_but_interactive(but)) {
+ if (BLI_rctf_isect_segment(&but->rect, xy_a_block, xy_b_block)) {
+
+ /* execute the button */
+ if (ui_is_but_bool(but) && but->type == but_type_start) {
+ /* is it pressed? */
+ bool is_set_but = ui_is_but_push(but);
+ BLI_assert(ui_is_but_bool(but) == true);
+ if (is_set_but != is_set) {
+ uiButExecute(C, but);
+ change = true;
+ }
+ }
+ /* done */
+
+ }
+ }
+ }
+ }
+
+ return change;
+}
+
+static void ui_drag_toggle_set(bContext *C, uiDragToggleHandle *drag_info, const int xy_input[2])
+{
+ ARegion *ar = CTX_wm_region(C);
+ bool do_draw = false;
+ int xy[2];
+
+ /**
+ * Initialize Locking:
+ *
+ * Check if we need to initialize the lock axis by finding if the first
+ * button we mouse over is X or Y aligned, then lock the mouse to that axis after.
+ */
+ if (drag_info->xy_lock[0] == false && drag_info->xy_lock[1] == false) {
+ ARegion *ar = CTX_wm_region(C);
+
+ /* first store the buttons original coords */
+ uiBut *but = ui_but_find_mouse_over(ar, xy_input[0], xy_input[1]);
+ if (but) {
+ const float but_cent_new[2] = {BLI_rctf_cent_x(&but->rect),
+ BLI_rctf_cent_y(&but->rect)};
+
+ /* check if this is a different button, chances are high the button wont move about :) */
+ if (len_manhattan_v2v2(drag_info->but_cent_start, but_cent_new) > 1.0f) {
+ if (fabsf(drag_info->but_cent_start[0] - but_cent_new[0]) <
+ fabsf(drag_info->but_cent_start[1] - but_cent_new[1]))
+ {
+ drag_info->xy_lock[0] = true;
+ }
+ else {
+ drag_info->xy_lock[1] = true;
+ }
+ }
+ }
+ }
+ /* done with axis locking */
+
+
+ xy[0] = (drag_info->xy_lock[0] == false) ? xy_input[0] : drag_info->xy_last[0];
+ xy[1] = (drag_info->xy_lock[1] == false) ? xy_input[1] : drag_info->xy_last[1];
+
+
+ /* touch all buttons between last mouse coord and this one */
+ do_draw = ui_drag_toggle_set_xy_xy(C, ar, drag_info->is_set, drag_info->but_type_start, drag_info->xy_last, xy);
+
+ if (do_draw) {
+ ED_region_tag_redraw(ar);
+ }
+
+ copy_v2_v2_int(drag_info->xy_last, xy);
+}
+
+static void ui_handler_region_drag_toggle_remove(bContext *UNUSED(C), void *userdata)
+{
+ uiDragToggleHandle *drag_info = userdata;
+ MEM_freeN(drag_info);
+}
+
+static int ui_handler_region_drag_toggle(bContext *C, const wmEvent *event, void *userdata)
+{
+ uiDragToggleHandle *drag_info = userdata;
+ bool done = false;
+
+ switch (event->type) {
+ case LEFTMOUSE:
+ {
+ if (event->val != KM_PRESS) {
+ done = true;
+ }
+ break;
+ }
+ case MOUSEMOVE:
+ {
+ ui_drag_toggle_set(C, drag_info, &event->x);
+ break;
+ }
+ }
+
+ if (done) {
+ wmWindow *win = CTX_wm_window(C);
+ WM_event_remove_ui_handler(&win->modalhandlers,
+ ui_handler_region_drag_toggle,
+ ui_handler_region_drag_toggle_remove,
+ drag_info, false);
+ ui_handler_region_drag_toggle_remove(C, drag_info);
+
+ WM_event_add_mousemove(C);
+ return WM_UI_HANDLER_BREAK;
+ }
+ else {
+ return WM_UI_HANDLER_CONTINUE;
+ }
+}
+
+#endif /* USE_DRAG_TOGGLE */
+
+
static int ui_but_mouse_inside_icon(uiBut *but, ARegion *ar, const wmEvent *event)
{
rcti rect;
@@ -765,18 +920,24 @@ static int ui_but_start_drag(bContext *C, uiBut *but, uiHandleButtonData *data,
button_activate_state(C, but, BUTTON_STATE_EXIT);
data->cancel = TRUE;
-
+#ifdef USE_DRAG_TOGGLE
if (ui_is_but_bool(but)) {
- const bool is_set = (ui_get_but_val(but) != 0.0);
- PointerRNA ptr;
- WM_operator_properties_create(&ptr, "UI_OT_drag_toggle");
- RNA_boolean_set(&ptr, "state", !is_set);
- RNA_int_set(&ptr, "last_x", data->dragstartx);
- RNA_int_set(&ptr, "last_y", data->dragstarty);
- WM_operator_name_call(C, "UI_OT_drag_toggle", WM_OP_INVOKE_DEFAULT, &ptr);
- WM_operator_properties_free(&ptr);
+ uiDragToggleHandle *drag_info = MEM_callocN(sizeof(*drag_info), __func__);
+
+ drag_info->is_set = ui_is_but_push(but);
+ drag_info->but_cent_start[0] = BLI_rctf_cent_x(&but->rect);
+ drag_info->but_cent_start[1] = BLI_rctf_cent_y(&but->rect);
+ drag_info->but_type_start = but->type;
+ copy_v2_v2_int(drag_info->xy_last, &event->x);
+
+ WM_event_add_ui_handler(C, &data->window->modalhandlers,
+ ui_handler_region_drag_toggle,
+ ui_handler_region_drag_toggle_remove,
+ drag_info);
}
- else {
+ else
+#endif
+ {
wmDrag *drag;
drag = WM_event_start_drag(C, but->icon, but->dragtype, but->dragpoin, ui_get_but_val(but));
@@ -2485,25 +2646,32 @@ static int ui_do_but_SEARCH_UNLINK(bContext *C, uiBlock *block, uiBut *but, uiHa
static int ui_do_but_TOG(bContext *C, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
{
+#ifdef USE_DRAG_TOGGLE
if (data->state == BUTTON_STATE_HIGHLIGHT) {
if (event->type == LEFTMOUSE && event->val == KM_PRESS && ui_is_but_bool(but)) {
+ data->togdual = event->ctrl;
+ data->togonly = !event->shift;
+ ui_apply_button(C, but->block, but, data, true);
button_activate_state(C, but, BUTTON_STATE_WAIT_DRAG);
data->dragstartx = event->x;
data->dragstarty = event->y;
- return WM_UI_HANDLER_CONTINUE;
+ return WM_UI_HANDLER_BREAK;
}
-
+ }
+ else if (data->state == BUTTON_STATE_WAIT_DRAG) {
+ /* note: the 'BUTTON_STATE_WAIT_DRAG' part of 'ui_do_but_EXIT' could be refactored into its own function */
+ data->applied = false;
+ return ui_do_but_EXIT(C, but, data, event);
+ }
+#endif
+ if (data->state == BUTTON_STATE_HIGHLIGHT) {
if (ELEM3(event->type, LEFTMOUSE, PADENTER, RETKEY) && event->val == KM_PRESS) {
data->togdual = event->ctrl;
data->togonly = !event->shift;
button_activate_state(C, but, BUTTON_STATE_EXIT);
- return WM_UI_HANDLER_CONTINUE;
+ return WM_UI_HANDLER_BREAK;
}
}
- else if (data->state == BUTTON_STATE_WAIT_DRAG) {
- /* note: the 'BUTTON_STATE_WAIT_DRAG' part of 'ui_do_but_EXIT' could be refactored into its own function */
- return ui_do_but_EXIT(C, but, data, event);
- }
return WM_UI_HANDLER_CONTINUE;
}
@@ -2524,13 +2692,15 @@ static int ui_do_but_EXIT(bContext *C, uiBut *but, uiHandleButtonData *data, con
return WM_UI_HANDLER_CONTINUE;
}
}
+#ifdef USE_DRAG_TOGGLE
if (event->type == LEFTMOUSE && ui_is_but_bool(but)) {
button_activate_state(C, but, BUTTON_STATE_WAIT_DRAG);
data->dragstartx = event->x;
data->dragstarty = event->y;
return WM_UI_HANDLER_CONTINUE;
}
-
+#endif
+
if (ELEM3(event->type, LEFTMOUSE, PADENTER, RETKEY) && event->val == KM_PRESS) {
int ret = WM_UI_HANDLER_BREAK;
/* XXX (a bit ugly) Special case handling for filebrowser drag button */
@@ -3246,13 +3416,14 @@ static int ui_do_but_BLOCK(bContext *C, uiBut *but, uiHandleButtonData *data, co
return WM_UI_HANDLER_BREAK;
}
}
+#ifdef USE_DRAG_TOGGLE
if (event->type == LEFTMOUSE && ui_is_but_bool(but)) {
button_activate_state(C, but, BUTTON_STATE_WAIT_DRAG);
data->dragstartx = event->x;
data->dragstarty = event->y;
return WM_UI_HANDLER_BREAK;
}
-
+#endif
/* regular open menu */
if (ELEM3(event->type, LEFTMOUSE, PADENTER, RETKEY) && event->val == KM_PRESS) {
button_activate_state(C, but, BUTTON_STATE_MENU_OPEN);
@@ -4253,7 +4424,7 @@ static int ui_numedit_but_HISTOGRAM(uiBut *but, uiHandleButtonData *data, int mx
if (in_scope_resize_zone(but, data->dragstartx, data->dragstarty)) {
/* resize histogram widget itself */
- hist->height = (BLI_rctf_size_y(&but->rect) + (data->dragstarty - my))/UI_DPI_FAC;
+ hist->height = (BLI_rctf_size_y(&but->rect) + (data->dragstarty - my)) / UI_DPI_FAC;
}
else {
/* scale histogram values (dy / 10 for better control) */
@@ -4337,7 +4508,7 @@ static int ui_numedit_but_WAVEFORM(uiBut *but, uiHandleButtonData *data, int mx,
if (in_scope_resize_zone(but, data->dragstartx, data->dragstarty)) {
/* resize waveform widget itself */
- scopes->wavefrm_height = (BLI_rctf_size_y(&but->rect) + (data->dragstarty - my))/UI_DPI_FAC;
+ scopes->wavefrm_height = (BLI_rctf_size_y(&but->rect) + (data->dragstarty - my)) / UI_DPI_FAC;
}
else {
/* scale waveform values */
@@ -4419,7 +4590,7 @@ static int ui_numedit_but_VECTORSCOPE(uiBut *but, uiHandleButtonData *data, int
if (in_scope_resize_zone(but, data->dragstartx, data->dragstarty)) {
/* resize vectorscope widget itself */
- scopes->vecscope_height = (BLI_rctf_size_y(&but->rect) + (data->dragstarty - my))/UI_DPI_FAC;
+ scopes->vecscope_height = (BLI_rctf_size_y(&but->rect) + (data->dragstarty - my)) / UI_DPI_FAC;
}
data->draglastx = mx;
@@ -4622,7 +4793,7 @@ static int ui_numedit_but_TRACKPREVIEW(bContext *C, uiBut *but, uiHandleButtonDa
if (in_scope_resize_zone(but, data->dragstartx, data->dragstarty)) {
/* resize preview widget itself */
- scopes->track_preview_height = (BLI_rctf_size_y(&but->rect) + (data->dragstarty - my))/UI_DPI_FAC;
+ scopes->track_preview_height = (BLI_rctf_size_y(&but->rect) + (data->dragstarty - my)) / UI_DPI_FAC;
}
else {
if (!scopes->track_locked) {
@@ -5452,6 +5623,24 @@ static int ui_mouse_inside_button(ARegion *ar, uiBut *but, int x, int y)
return 1;
}
+/**
+ * Can we mouse over the button or is it hidden/disabled/layout.
+ */
+bool ui_is_but_interactive(uiBut *but)
+{
+ /* note, LABEL is included for highlights, this allows drags */
+ if (but->type == LABEL && but->dragpoin == NULL)
+ return false;
+ if (ELEM3(but->type, ROUNDBOX, SEPR, LISTBOX))
+ return false;
+ if (but->flag & UI_HIDDEN)
+ return false;
+ if (but->flag & UI_SCROLLED)
+ return false;
+
+ return true;
+}
+
uiBut *ui_but_find_mouse_over(ARegion *ar, int x, int y)
{
uiBlock *block;
@@ -5469,17 +5658,11 @@ uiBut *ui_but_find_mouse_over(ARegion *ar, int x, int y)
ui_window_to_block(ar, block, &mx, &my);
for (but = block->buttons.first; but; but = but->next) {
- /* note, LABEL is included for highlights, this allows drags */
- if (but->type == LABEL && but->dragpoin == NULL)
- continue;
- if (ELEM3(but->type, ROUNDBOX, SEPR, LISTBOX))
- continue;
- if (but->flag & UI_HIDDEN)
- continue;
- if (but->flag & UI_SCROLLED)
- continue;
- if (ui_but_contains_pt(but, mx, my))
- butover = but;
+ if (ui_is_but_interactive(but)) {
+ if (ui_but_contains_pt(but, mx, my)) {
+ butover = but;
+ }
+ }
}
/* CLIP_EVENTS prevents the event from reaching other blocks */
@@ -5795,7 +5978,8 @@ static void button_activate_exit(bContext *C, uiBut *but, uiHandleButtonData *da
if (!onfree && !data->cancel) {
/* autokey & undo push */
- ui_apply_autokey_undo(C, but);
+ ui_apply_undo(but);
+ ui_apply_autokey(C, but);
/* popup menu memory */
if (block->flag & UI_BLOCK_POPUP_MEMORY)
@@ -5820,12 +6004,13 @@ static void button_activate_exit(bContext *C, uiBut *but, uiHandleButtonData *da
/* redraw (data is but->active!) */
ED_region_tag_redraw(data->region);
-
+
/* clean up button */
if (but->active) {
MEM_freeN(but->active);
but->active = NULL;
}
+
but->flag &= ~(UI_ACTIVE | UI_SELECT);
but->flag |= UI_BUT_LAST_ACTIVE;
if (!onfree)
@@ -6084,6 +6269,7 @@ void ui_button_execute_do(struct bContext *C, struct ARegion *ar, uiBut *but)
data->region = ar;
ui_apply_button(C, but->block, but, data, true);
/* use onfree event so undo is handled by caller and apply is already done above */
+ ui_apply_autokey(C, but);
button_activate_exit((bContext *)C, but, data, false, true);
but->active = active_back;
}
@@ -6107,14 +6293,12 @@ static void ui_handle_button_activate(bContext *C, ARegion *ar, uiBut *but, uiBu
static int ui_handle_button_event(bContext *C, const wmEvent *event, uiBut *but)
{
- uiHandleButtonData *data;
+ uiHandleButtonData *data = but->active;
+ const uiButtonActivateType state_orig = data->state;
uiBlock *block;
ARegion *ar;
- uiBut *postbut;
- uiButtonActivateType posttype;
int retval;
- data = but->active;
block = but->block;
ar = data->region;
@@ -6266,14 +6450,28 @@ static int ui_handle_button_event(bContext *C, const wmEvent *event, uiBut *but)
}
if (data->state == BUTTON_STATE_EXIT) {
- postbut = data->postbut;
- posttype = data->posttype;
+ uiBut *post_but = data->postbut;
+ uiButtonActivateType post_type = data->posttype;
- button_activate_exit(C, but, data, (postbut == NULL), false);
+ button_activate_exit(C, but, data, (post_but == NULL), false);
/* for jumping to the next button with tab while text editing */
- if (postbut)
- button_activate_init(C, ar, postbut, posttype);
+ if (post_but) {
+ button_activate_init(C, ar, post_but, post_type);
+ }
+ else {
+ /* XXX issue is because WM_event_add_mousemove(C) is a bad hack and not reliable,
+ *if that gets coded better this bypass can go away too.
+ *
+ * This is needed to make sure if a button was active,
+ * it stays active while the mouse is over it.
+ * This avoids adding mousemoves, see: [#33466] */
+ if (ELEM(state_orig, BUTTON_ACTIVATE, BUTTON_ACTIVATE_OVER)) {
+ if (ui_but_find_mouse_over(ar, event->x, event->y) == but) {
+ button_activate_init(C, ar, but, state_orig);
+ }
+ }
+ }
}
return retval;
diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h
index f0e59f1f935..9093e8f8d30 100644
--- a/source/blender/editors/interface/interface_intern.h
+++ b/source/blender/editors/interface/interface_intern.h
@@ -171,8 +171,8 @@ struct uiBut {
char *str;
char strdata[UI_MAX_NAME_STR];
char drawstr[UI_MAX_DRAW_STR];
-
- rctf rect;
+
+ rctf rect; /* block relative coords */
char *poin;
float hardmin, hardmax, softmin, softmax;
@@ -225,7 +225,7 @@ struct uiBut {
void *rename_orig;
uiLink *link;
- short linkto[2];
+ short linkto[2]; /* region relative coords */
const char *tip, *lockstr;
@@ -410,6 +410,11 @@ extern int ui_is_but_bool(uiBut *but);
extern int ui_is_but_unit(uiBut *but);
extern int ui_is_but_rna_valid(uiBut *but);
extern int ui_is_but_utf8(uiBut *but);
+extern bool ui_is_but_interactive(uiBut *but);
+
+extern int ui_is_but_push_ex(uiBut *but, double *value);
+extern int ui_is_but_push(uiBut *but);
+
extern void ui_bounds_block(uiBlock *block);
extern void ui_block_translate(uiBlock *block, int x, int y);
diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c
index c69f53a53d2..1e95b4df762 100644
--- a/source/blender/editors/interface/interface_layout.c
+++ b/source/blender/editors/interface/interface_layout.c
@@ -2263,6 +2263,7 @@ uiLayout *uiLayoutRow(uiLayout *layout, int align)
litem->enabled = 1;
litem->context = layout->context;
litem->space = (align) ? 0 : layout->root->style->buttonspacex;
+ litem->redalert = layout->redalert;
litem->w = layout->w;
BLI_addtail(&layout->items, litem);
@@ -2283,6 +2284,7 @@ uiLayout *uiLayoutColumn(uiLayout *layout, int align)
litem->enabled = 1;
litem->context = layout->context;
litem->space = (litem->align) ? 0 : layout->root->style->buttonspacey;
+ litem->redalert = layout->redalert;
litem->w = layout->w;
BLI_addtail(&layout->items, litem);
@@ -2303,6 +2305,7 @@ uiLayout *uiLayoutColumnFlow(uiLayout *layout, int number, int align)
flow->litem.enabled = 1;
flow->litem.context = layout->context;
flow->litem.space = (flow->litem.align) ? 0 : layout->root->style->columnspace;
+ flow->litem.redalert = layout->redalert;
flow->litem.w = layout->w;
flow->number = number;
BLI_addtail(&layout->items, flow);
@@ -2323,6 +2326,7 @@ static uiLayoutItemBx *ui_layout_box(uiLayout *layout, int type)
box->litem.enabled = 1;
box->litem.context = layout->context;
box->litem.space = layout->root->style->columnspace;
+ box->litem.redalert = layout->redalert;
box->litem.w = layout->w;
BLI_addtail(&layout->items, box);
diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c
index d48d699d881..3298859ee0c 100644
--- a/source/blender/editors/interface/interface_ops.c
+++ b/source/blender/editors/interface/interface_ops.c
@@ -1071,222 +1071,6 @@ static void UI_OT_reloadtranslation(wmOperatorType *ot)
ot->exec = reloadtranslation_exec;
}
-
-/* -------------------------------------------------------------------- */
-/* Toggle Drag Operator */
-
-typedef struct DragOpInfo {
- bool xy_lock[2];
- float but_cent_start[2];
- eButType but_type_start;
-} DragOpInfo;
-
-typedef struct DragOpPlotData {
- bContext *C;
- ARegion *ar;
- bool is_set;
- eButType but_type_start;
- bool do_draw;
- const uiBut *but_prev;
-} DragOpPlotData;
-
-static const uiBut *ui_but_set_xy(bContext *C, ARegion *ar, const bool is_set, const eButType but_type_start,
- const int xy[2], const uiBut *but_prev)
-{
- uiBut *but = ui_but_find_mouse_over(ar, xy[0], xy[1]);
-
- if (but_prev == but) {
- return but_prev;
- }
-
- if (but && ui_is_but_bool(but) && but->type == but_type_start) {
- /* is it pressed? */
- bool is_set_but = (ui_get_but_val(but) != 0.0);
- BLI_assert(ui_is_but_bool(but) == true);
- if (is_set_but != is_set) {
- uiButExecute(C, but);
- return but;
- }
- }
-
- return but_prev;
-}
-
-static int ui_but_set_cb(int x, int y, void *data_v)
-{
- DragOpPlotData *data = data_v;
- int xy[2] = {x, y};
- data->but_prev = ui_but_set_xy(data->C, data->ar, data->is_set, data->but_type_start, xy, data->but_prev);
- return 1; /* keep going */
-}
-
-/* operates on buttons between 2 mouse-points */
-static bool ui_but_set_xy_xy(bContext *C, ARegion *ar, const bool is_set, const eButType but_type_start,
- const int xy_src[2], const int xy_dst[2])
-{
- DragOpPlotData data;
- data.C = C;
- data.ar = ar;
- data.is_set = is_set;
- data.but_type_start = but_type_start;
- data.do_draw = false;
- data.but_prev = NULL;
-
-
- /* prevent dragging too fast loosing buttons */
- plot_line_v2v2i(xy_src, xy_dst, ui_but_set_cb, &data);
-
- return data.do_draw;
-}
-
-static void ui_drag_but_set(bContext *C, wmOperator *op, const int xy_input[2])
-{
- ARegion *ar = CTX_wm_region(C);
- DragOpInfo *drag_info = op->customdata;
- bool do_draw = false;
-
- const bool is_set = RNA_boolean_get(op->ptr, "state");
- const int xy_last[2] = {RNA_int_get(op->ptr, "last_x"),
- RNA_int_get(op->ptr, "last_y")};
-
- int xy[2];
-
- /**
- * Initialize Locking:
- *
- * Check if we need to initialize the lock axis by finding if the first
- * button we mouse over is X or Y aligned, then lock the mouse to that axis after.
- */
- if (drag_info->xy_lock[0] == false && drag_info->xy_lock[1] == false) {
- ARegion *ar = CTX_wm_region(C);
-
- /* first store the buttons original coords */
- uiBut *but = ui_but_find_mouse_over(ar, xy_input[0], xy_input[1]);
- if (but) {
- const float but_cent_new[2] = {BLI_rctf_cent_x(&but->rect),
- BLI_rctf_cent_y(&but->rect)};
-
- /* check if this is a different button, chances are high the button wont move about :) */
- if (len_manhattan_v2v2(drag_info->but_cent_start, but_cent_new) > 1.0f) {
- if (fabsf(drag_info->but_cent_start[0] - but_cent_new[0]) <
- fabsf(drag_info->but_cent_start[1] - but_cent_new[1]))
- {
- drag_info->xy_lock[0] = true;
- }
- else {
- drag_info->xy_lock[1] = true;
- }
- }
- }
- }
- /* done with axis locking */
-
-
- xy[0] = (drag_info->xy_lock[0] == false) ? xy_input[0] : xy_last[0];
- xy[1] = (drag_info->xy_lock[1] == false) ? xy_input[1] : xy_last[1];
-
-
- /* touch all buttons between last mouse coord and this one */
- do_draw = ui_but_set_xy_xy(C, ar, is_set, drag_info->but_type_start, xy_last, xy);
-
- if (do_draw) {
- ED_region_tag_redraw(ar);
- }
-
- RNA_int_set(op->ptr, "last_x", xy[0]);
- RNA_int_set(op->ptr, "last_y", xy[1]);
-}
-
-static int ui_drag_toggle_invoke(bContext *C, wmOperator *op, wmEvent *event)
-{
- int xy_last[2] = {RNA_int_get(op->ptr, "last_x"),
- RNA_int_get(op->ptr, "last_y")};
-
- float but_cent_start[2];
- eButType but_type_start;
- DragOpInfo *drag_info;
-
- {
- /* find the button where we started dragging */
- ARegion *ar = CTX_wm_region(C);
- uiBut *but = ui_but_find_mouse_over(ar, xy_last[0], xy_last[1]);
- if (but) {
- but_cent_start[0] = BLI_rctf_cent_x(&but->rect);
- but_cent_start[1] = BLI_rctf_cent_y(&but->rect);
- but_type_start = but->type;
- }
- else {
- return OPERATOR_CANCELLED;
- }
- }
-
- drag_info = op->customdata = MEM_callocN(sizeof(DragOpInfo), __func__);
- copy_v2_v2(drag_info->but_cent_start, but_cent_start);
- drag_info->but_type_start = but_type_start;
-
- /* set the initial button */
- ui_drag_but_set(C, op, xy_last);
- ui_drag_but_set(C, op, &event->x);
-
- WM_event_add_modal_handler(C, op);
- return OPERATOR_RUNNING_MODAL;
-}
-
-static int ui_drag_toggle_modal(bContext *C, wmOperator *op, wmEvent *event)
-{
- bool done = false;
-
- switch (event->type) {
- case LEFTMOUSE:
- {
- if (event->val != KM_PRESS) {
- done = true;
- }
- break;
- }
- case MOUSEMOVE:
- {
- ui_drag_but_set(C, op, &event->x);
- break;
- }
- }
-
- if (done) {
- MEM_freeN(op->customdata);
- return OPERATOR_FINISHED;
- }
- else {
- return OPERATOR_RUNNING_MODAL;
- }
-}
-
-static int ui_drag_toggle_cancel(bContext *UNUSED(C), wmOperator *op)
-{
- MEM_freeN(op->customdata);
- return OPERATOR_CANCELLED;
-}
-
-static void UI_OT_drag_toggle(wmOperatorType *ot)
-{
- /* identifiers */
- ot->name = "Button Drag Toggle";
- ot->description = "";
- ot->idname = "UI_OT_drag_toggle";
-
- /* api callbacks */
- ot->invoke = ui_drag_toggle_invoke;
- ot->modal = ui_drag_toggle_modal;
- ot->cancel = ui_drag_toggle_cancel;
-
- /* flags */
- ot->flag = OPTYPE_UNDO | OPTYPE_INTERNAL;
-
- /* properties */
- RNA_def_boolean(ot->srna, "state", true, "State", "");
- RNA_def_int(ot->srna, "last_x", 0, 0, INT_MAX, "X", "", 0, INT_MAX);
- RNA_def_int(ot->srna, "last_y", 0, 0, INT_MAX, "Y", "", 0, INT_MAX);
-}
-
/* ********************************************************* */
/* Registration */
@@ -1304,6 +1088,4 @@ void UI_buttons_operatortypes(void)
WM_operatortype_append(UI_OT_edittranslation_init);
#endif
WM_operatortype_append(UI_OT_reloadtranslation);
- WM_operatortype_append(UI_OT_drag_toggle);
}
-
diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c
index 900ba7de559..83540cef9fc 100644
--- a/source/blender/editors/interface/interface_templates.c
+++ b/source/blender/editors/interface/interface_templates.c
@@ -2594,7 +2594,8 @@ void uiTemplateList(uiLayout *layout, bContext *C, const char *listtype_name, co
/* if list length changes and active is out of view, scroll to it */
if ((ui_list->list_last_len != len) &&
- (activei < ui_list->list_scroll || activei >= ui_list->list_scroll + items)) {
+ (activei < ui_list->list_scroll || activei >= ui_list->list_scroll + items))
+ {
ui_list->list_scroll = activei;
}
diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c
index 68df8e29957..dd7b1d0ee06 100644
--- a/source/blender/editors/interface/interface_widgets.c
+++ b/source/blender/editors/interface/interface_widgets.c
@@ -1855,7 +1855,7 @@ static void widget_softshadow(const rcti *rect, int roundboxalign, const float r
widget_verts_to_quad_strip(&wtb, totvert, quad_strip);
glVertexPointer(2, GL_FLOAT, 0, quad_strip);
- glDrawArrays(GL_QUAD_STRIP, 0, totvert * 2 + 2);
+ glDrawArrays(GL_QUAD_STRIP, 0, totvert * 2); /* add + 2 for getting a complete soft rect. Now it skips top edge to allow transparent menus */
}
glDisableClientState(GL_VERTEX_ARRAY);
diff --git a/source/blender/editors/interface/view2d_ops.c b/source/blender/editors/interface/view2d_ops.c
index 7f1140501b5..daa78957231 100644
--- a/source/blender/editors/interface/view2d_ops.c
+++ b/source/blender/editors/interface/view2d_ops.c
@@ -1034,10 +1034,11 @@ static int view_zoomdrag_modal(bContext *C, wmOperator *op, wmEvent *event)
}
else {
/* 'continuous' or 'dolly' */
- float fac, zoomfac = 0.001f * v2d->maxzoom;
+ float fac, zoomfac = 0.01f;
/* some view2d's (graph) don't have min/max zoom, or extreme ones */
- CLAMP (zoomfac, 0.001f, 0.01f);
+ if (v2d->maxzoom > 0.0f)
+ zoomfac = CLAMPIS(0.001f * v2d->maxzoom, 0.001f, 0.01f);
/* x-axis transform */
fac = zoomfac * (event->x - vzd->lastx);
@@ -1116,7 +1117,7 @@ static void VIEW2D_OT_zoom(wmOperatorType *ot)
ot->poll = view_zoom_poll;
/* operator is repeatable */
- // ot->flag = OPTYPE_BLOCKING;
+ ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_POINTER;
/* rna - must keep these in sync with the other operators */
RNA_def_float(ot->srna, "deltax", 0, -FLT_MAX, FLT_MAX, "Delta X", "", -FLT_MAX, FLT_MAX);
diff --git a/source/blender/editors/io/CMakeLists.txt b/source/blender/editors/io/CMakeLists.txt
index 7db23041c88..d4ae5b8b29b 100644
--- a/source/blender/editors/io/CMakeLists.txt
+++ b/source/blender/editors/io/CMakeLists.txt
@@ -47,4 +47,8 @@ if(WITH_OPENCOLLADA)
add_definitions(-DWITH_COLLADA)
endif()
+if(WITH_INTERNATIONAL)
+ add_definitions(-DWITH_INTERNATIONAL)
+endif()
+
blender_add_lib(bf_editor_io "${SRC}" "${INC}" "${INC_SYS}")
diff --git a/source/blender/editors/io/SConscript b/source/blender/editors/io/SConscript
index cef73f33ddd..b7449ccad79 100644
--- a/source/blender/editors/io/SConscript
+++ b/source/blender/editors/io/SConscript
@@ -36,4 +36,7 @@ incs += '../../makesdna ../../makesrna ../../windowmanager ../../collada'
if env['WITH_BF_COLLADA']:
defs += ['WITH_COLLADA']
+if env['WITH_BF_INTERNATIONAL']:
+ defs += ['WITH_INTERNATIONAL']
+
env.BlenderLib ( 'bf_editor_io', sources, Split(incs), defines=defs, libtype=['core','player'], priority=[330,210] )
diff --git a/source/blender/editors/io/io_collada.c b/source/blender/editors/io/io_collada.c
index 7703a8638c9..4908c101a7c 100644
--- a/source/blender/editors/io/io_collada.c
+++ b/source/blender/editors/io/io_collada.c
@@ -92,6 +92,7 @@ static int wm_collada_export_exec(bContext *C, wmOperator *op)
int use_texture_copies;
int active_uv_only;
+ int triangulate;
int use_object_instantiation;
int sort_by_name;
int second_life;
@@ -118,6 +119,7 @@ static int wm_collada_export_exec(bContext *C, wmOperator *op)
use_texture_copies = RNA_boolean_get(op->ptr, "use_texture_copies");
active_uv_only = RNA_boolean_get(op->ptr, "active_uv_only");
+ triangulate = RNA_boolean_get(op->ptr, "triangulate");
use_object_instantiation = RNA_boolean_get(op->ptr, "use_object_instantiation");
sort_by_name = RNA_boolean_get(op->ptr, "sort_by_name");
second_life = RNA_boolean_get(op->ptr, "second_life");
@@ -140,9 +142,11 @@ static int wm_collada_export_exec(bContext *C, wmOperator *op)
include_material_textures,
use_texture_copies,
+ triangulate,
use_object_instantiation,
sort_by_name,
- second_life)) {
+ second_life))
+ {
return OPERATOR_FINISHED;
}
else {
@@ -217,6 +221,8 @@ static void uiCollada_exportSettings(uiLayout *layout, PointerRNA *imfptr)
uiItemL(row, IFACE_("Collada Options:"), ICON_MODIFIER);
row = uiLayoutRow(box, FALSE);
+ uiItemR(row, imfptr, "triangulate", 0, NULL, ICON_NONE);
+ row = uiLayoutRow(box, FALSE);
uiItemR(row, imfptr, "use_object_instantiation", 0, NULL, ICON_NONE);
row = uiLayoutRow(box, FALSE);
uiItemR(row, imfptr, "sort_by_name", 0, NULL, ICON_NONE);
@@ -293,6 +299,9 @@ void WM_OT_collada_export(wmOperatorType *ot)
"Copy textures to same folder where the .dae file is exported");
+ RNA_def_boolean(ot->srna, "triangulate", 1, "Triangulate",
+ "Export Polygons (Quads & NGons) as Triangles");
+
RNA_def_boolean(ot->srna, "use_object_instantiation", 1, "Use Object Instances",
"Instantiate multiple Objects from same Data");
diff --git a/source/blender/editors/mesh/editface.c b/source/blender/editors/mesh/editface.c
index 7ac27e038a4..7ddf2b54a88 100644
--- a/source/blender/editors/mesh/editface.c
+++ b/source/blender/editors/mesh/editface.c
@@ -181,11 +181,17 @@ void paintface_reveal(Object *ob)
static void hash_add_face(EdgeHash *ehash, MPoly *mp, MLoop *mloop)
{
- MLoop *ml;
- int i;
+ MLoop *ml, *ml_next;
+ int i = mp->totloop;
+
+ ml_next = mloop; /* first loop */
+ ml = &ml_next[i - 1]; /* last loop */
+
+ while (i-- != 0) {
+ BLI_edgehash_insert(ehash, ml->v, ml_next->v, NULL);
- for (i = 0, ml = mloop; i < mp->totloop; i++, ml++) {
- BLI_edgehash_insert(ehash, ml->v, ME_POLY_LOOP_NEXT(mloop, mp, i)->v, NULL);
+ ml = ml_next;
+ ml_next++;
}
}
diff --git a/source/blender/editors/mesh/editmesh_select.c b/source/blender/editors/mesh/editmesh_select.c
index 57f116693f8..51161970c0b 100644
--- a/source/blender/editors/mesh/editmesh_select.c
+++ b/source/blender/editors/mesh/editmesh_select.c
@@ -933,9 +933,13 @@ static int edbm_select_mode_exec(bContext *C, wmOperator *op)
static int edbm_select_mode_invoke(bContext *C, wmOperator *op, wmEvent *event)
{
- // RNA_enum_set(op->ptr, "type"); /* type must be set already */
- RNA_boolean_set(op->ptr, "use_extend", event->shift);
- RNA_boolean_set(op->ptr, "use_expand", event->ctrl);
+ /* detecting these options based on shift/ctrl here is weak, but it's done
+ * to make this work when clicking buttons or menus */
+ if (!RNA_struct_property_is_set(op->ptr, "use_extend"))
+ RNA_boolean_set(op->ptr, "use_extend", event->shift);
+ if (!RNA_struct_property_is_set(op->ptr, "use_expand"))
+ RNA_boolean_set(op->ptr, "use_expand", event->ctrl);
+
return edbm_select_mode_exec(C, op);
}
@@ -1358,6 +1362,22 @@ static int edgetag_context_check(Scene *scene, BMesh *bm, BMEdge *e)
return 0;
}
+static void edgetag_ensure_cd_flag(Scene *scene, Mesh *me)
+{
+ BMesh *bm = me->edit_btmesh->bm;
+
+ switch (scene->toolsettings->edge_mode) {
+ case EDGE_MODE_TAG_CREASE:
+ BM_mesh_cd_flag_ensure(bm, me, ME_CDFLAG_EDGE_CREASE);
+ break;
+ case EDGE_MODE_TAG_BEVEL:
+ BM_mesh_cd_flag_ensure(bm, me, ME_CDFLAG_EDGE_BWEIGHT);
+ break;
+ default:
+ break;
+ }
+}
+
static int edgetag_shortest_path(Scene *scene, BMesh *bm, BMEdge *e_src, BMEdge *e_dst)
{
/* BM_ELEM_TAG flag is used to store visited edges */
@@ -1371,16 +1391,7 @@ static int edgetag_shortest_path(Scene *scene, BMesh *bm, BMEdge *e_src, BMEdge
/* note, would pass BM_EDGE except we are looping over all edges anyway */
BM_mesh_elem_index_ensure(bm, BM_VERT /* | BM_EDGE */);
- switch (scene->toolsettings->edge_mode) {
- case EDGE_MODE_TAG_CREASE:
- BM_mesh_cd_flag_ensure(bm, BKE_mesh_from_object(OBACT), ME_CDFLAG_EDGE_CREASE);
- break;
- case EDGE_MODE_TAG_BEVEL:
- BM_mesh_cd_flag_ensure(bm, BKE_mesh_from_object(OBACT), ME_CDFLAG_EDGE_BWEIGHT);
- break;
- default:
- break;
- }
+ edgetag_ensure_cd_flag(scene, OBACT->data);
BM_ITER_MESH_INDEX (e, &eiter, bm, BM_EDGES_OF_MESH, i) {
if (BM_elem_flag_test(e, BM_ELEM_HIDDEN) == FALSE) {
@@ -1470,7 +1481,7 @@ static int mouse_mesh_shortest_path_edge(ViewContext *vc)
e_dst = EDBM_edge_find_nearest(vc, &dist);
if (e_dst) {
Mesh *me = vc->obedit->data;
- int path = 0;
+ bool is_path = false;
if (em->bm->selected.last) {
BMEditSelection *ese = em->bm->selected.last;
@@ -1481,13 +1492,14 @@ static int mouse_mesh_shortest_path_edge(ViewContext *vc)
if (e_act != e_dst) {
if (edgetag_shortest_path(vc->scene, em->bm, e_act, e_dst)) {
BM_select_history_remove(em->bm, e_act);
- path = 1;
+ is_path = true;
}
}
}
}
- if (path == 0) {
+ if (is_path == false) {
int act = (edgetag_context_check(vc->scene, em->bm, e_dst) == 0);
+ edgetag_ensure_cd_flag(vc->scene, vc->obedit->data);
edgetag_context_set(em->bm, vc->scene, e_dst, act); /* switch the edge option */
}
@@ -2963,7 +2975,7 @@ void MESH_OT_select_random(wmOperatorType *ot)
/* props */
RNA_def_float_percentage(ot->srna, "percent", 50.f, 0.0f, 100.0f,
"Percent", "Percentage of elements to select randomly", 0.f, 100.0f);
- RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend the selection");
+ RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend the selection");
}
static int edbm_select_next_loop_exec(bContext *C, wmOperator *UNUSED(op))
diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c
index dcb25423b80..92e1dbf433f 100644
--- a/source/blender/editors/mesh/editmesh_tools.c
+++ b/source/blender/editors/mesh/editmesh_tools.c
@@ -3321,7 +3321,8 @@ static int edbm_fill_exec(bContext *C, wmOperator *op)
if (!EDBM_op_init(em, &bmop, op,
"triangle_fill edges=%he use_beauty=%b",
- BM_ELEM_SELECT, use_beauty)) {
+ BM_ELEM_SELECT, use_beauty))
+ {
return OPERATOR_CANCELLED;
}
diff --git a/source/blender/editors/mesh/meshtools.c b/source/blender/editors/mesh/meshtools.c
index f983a43f573..378f6374336 100644
--- a/source/blender/editors/mesh/meshtools.c
+++ b/source/blender/editors/mesh/meshtools.c
@@ -149,7 +149,7 @@ int join_mesh_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- /* remove tessface to ensure we don't old references to invalid faces */
+ /* remove tessface to ensure we don't hold references to invalid faces */
BKE_mesh_tessface_clear(me);
/* new material indices and material array */
@@ -309,6 +309,10 @@ int join_mesh_exec(bContext *C, wmOperator *op)
me = base->object->data;
if (me->totvert) {
+
+ /* merge customdata flag */
+ ((Mesh *)ob->data)->cd_flag |= me->cd_flag;
+
/* standard data */
CustomData_merge(&me->vdata, &vdata, CD_MASK_MESH, CD_DEFAULT, totvert);
CustomData_copy_data(&me->vdata, &vdata, 0, vertofs, me->totvert);
diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c
index bc577f6ee29..997cbb71683 100644
--- a/source/blender/editors/object/object_add.c
+++ b/source/blender/editors/object/object_add.c
@@ -67,6 +67,7 @@
#include "BKE_displist.h"
#include "BKE_effect.h"
#include "BKE_group.h"
+#include "BKE_image.h"
#include "BKE_lamp.h"
#include "BKE_lattice.h"
#include "BKE_library.h"
@@ -735,6 +736,83 @@ void OBJECT_OT_empty_add(wmOperatorType *ot)
ED_object_add_generic_props(ot, FALSE);
}
+static int empty_drop_named_image_invoke(bContext *C, wmOperator *op, wmEvent *event)
+{
+ Base *base = NULL;
+ Image *ima = NULL;
+ Object *ob = NULL;
+
+ /* check image input variables */
+ if (RNA_struct_property_is_set(op->ptr, "filepath")) {
+ char path[FILE_MAX];
+
+ RNA_string_get(op->ptr, "filepath", path);
+ ima = BKE_image_load_exists(path);
+ }
+ else if (RNA_struct_property_is_set(op->ptr, "name")) {
+ char name[MAX_ID_NAME - 2];
+
+ RNA_string_get(op->ptr, "name", name);
+ ima = (Image *)BKE_libblock_find_name(ID_IM, name);
+ }
+
+ if (ima == NULL) {
+ BKE_report(op->reports, RPT_ERROR, "Not an image");
+ return OPERATOR_CANCELLED;
+ }
+
+ base = ED_view3d_give_base_under_cursor(C, event->mval);
+
+ /* if empty under cursor, then set object */
+ if (base && base->object->type == OB_EMPTY) {
+ ob = base->object;
+ WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, CTX_data_scene(C));
+ }
+ else {
+ /* add new empty */
+ unsigned int layer;
+ float rot[3];
+
+ if (!ED_object_add_generic_get_opts(C, op, NULL, rot, NULL, &layer, NULL))
+ return OPERATOR_CANCELLED;
+
+ ob = ED_object_add_type(C, OB_EMPTY, NULL, rot, FALSE, layer);
+
+ /* add under the mouse */
+ ED_object_location_from_view(C, ob->loc);
+ ED_view3d_cursor3d_position(C, ob->loc, event->mval);
+ }
+
+ ob->empty_drawtype = OB_EMPTY_IMAGE;
+ ob->data = ima;
+
+ return OPERATOR_FINISHED;
+}
+
+void OBJECT_OT_drop_named_image(wmOperatorType *ot)
+{
+ PropertyRNA *prop;
+
+ /* identifiers */
+ ot->name = "Add Empty Image/Drop Image To Empty";
+ ot->description = "Add an empty image type to scene with data";
+ ot->idname = "OBJECT_OT_drop_named_image";
+
+ /* api callbacks */
+ ot->invoke = empty_drop_named_image_invoke;
+ ot->poll = ED_operator_objectmode;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* properties */
+ prop = RNA_def_string(ot->srna, "filepath", "", FILE_MAX, "Filepath", "Path to image file");
+ RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
+ prop = RNA_def_string(ot->srna, "name", "", MAX_ID_NAME - 2, "Name", "Image name to assign");
+ RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
+ ED_object_add_generic_props(ot, FALSE);
+}
+
/********************* Add Lamp Operator ********************/
static const char *get_lamp_defname(int type)
@@ -2034,12 +2112,17 @@ static int add_named_exec(bContext *C, wmOperator *op)
}
basen->lay = basen->object->lay = scene->lay;
+ basen->object->restrictflag &= ~OB_RESTRICT_VIEW;
if (event) {
+ ARegion *ar = CTX_wm_region(C);
+ const int mval[2] = {event->x - ar->winrct.xmin,
+ event->y - ar->winrct.ymin};
ED_object_location_from_view(C, basen->object->loc);
- ED_view3d_cursor3d_position(C, basen->object->loc, event->mval);
+ ED_view3d_cursor3d_position(C, basen->object->loc, mval);
}
+ ED_base_object_select(basen, BA_SELECT);
ED_base_object_activate(C, basen);
copy_object_set_idnew(C, dupflag);
diff --git a/source/blender/editors/object/object_constraint.c b/source/blender/editors/object/object_constraint.c
index 84b989e2f59..1bed7d7dd11 100644
--- a/source/blender/editors/object/object_constraint.c
+++ b/source/blender/editors/object/object_constraint.c
@@ -40,6 +40,8 @@
#include "BLI_dynstr.h"
#include "BLI_utildefines.h"
+#include "BLF_translation.h"
+
#include "DNA_anim_types.h"
#include "DNA_constraint_types.h"
#include "DNA_curve_types.h"
@@ -1873,7 +1875,7 @@ static int pose_ik_add_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(evt))
}
/* prepare popup menu to choose targetting options */
- pup = uiPupMenuBegin(C, "Add IK", ICON_NONE);
+ pup = uiPupMenuBegin(C, IFACE_("Add IK"), ICON_NONE);
layout = uiPupMenuLayout(pup);
/* the type of targets we'll set determines the menu entries to show... */
@@ -1882,14 +1884,14 @@ static int pose_ik_add_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(evt))
* - the only thing that matters is that we want a target...
*/
if (tar_pchan)
- uiItemBooleanO(layout, "To Active Bone", ICON_NONE, "POSE_OT_ik_add", "with_targets", 1);
+ uiItemBooleanO(layout, IFACE_("To Active Bone"), ICON_NONE, "POSE_OT_ik_add", "with_targets", 1);
else
- uiItemBooleanO(layout, "To Active Object", ICON_NONE, "POSE_OT_ik_add", "with_targets", 1);
+ uiItemBooleanO(layout, IFACE_("To Active Object"), ICON_NONE, "POSE_OT_ik_add", "with_targets", 1);
}
else {
/* we have a choice of adding to a new empty, or not setting any target (targetless IK) */
- uiItemBooleanO(layout, "To New Empty Object", ICON_NONE, "POSE_OT_ik_add", "with_targets", 1);
- uiItemBooleanO(layout, "Without Targets", ICON_NONE, "POSE_OT_ik_add", "with_targets", 0);
+ uiItemBooleanO(layout, IFACE_("To New Empty Object"), ICON_NONE, "POSE_OT_ik_add", "with_targets", 1);
+ uiItemBooleanO(layout, IFACE_("Without Targets"), ICON_NONE, "POSE_OT_ik_add", "with_targets", 0);
}
/* finish building the menu, and process it (should result in calling self again) */
diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h
index 4523ac098d6..a302aa65fd0 100644
--- a/source/blender/editors/object/object_intern.h
+++ b/source/blender/editors/object/object_intern.h
@@ -113,6 +113,7 @@ void OBJECT_OT_metaball_add(struct wmOperatorType *ot);
void OBJECT_OT_text_add(struct wmOperatorType *ot);
void OBJECT_OT_armature_add(struct wmOperatorType *ot);
void OBJECT_OT_empty_add(struct wmOperatorType *ot);
+void OBJECT_OT_drop_named_image(struct wmOperatorType *ot);
void OBJECT_OT_lamp_add(struct wmOperatorType *ot);
void OBJECT_OT_effector_add(struct wmOperatorType *ot);
void OBJECT_OT_camera_add(struct wmOperatorType *ot);
diff --git a/source/blender/editors/object/object_ops.c b/source/blender/editors/object/object_ops.c
index bf408d6dbe4..e5aaaffade8 100644
--- a/source/blender/editors/object/object_ops.c
+++ b/source/blender/editors/object/object_ops.c
@@ -114,6 +114,7 @@ void ED_operatortypes_object(void)
WM_operatortype_append(OBJECT_OT_text_add);
WM_operatortype_append(OBJECT_OT_armature_add);
WM_operatortype_append(OBJECT_OT_empty_add);
+ WM_operatortype_append(OBJECT_OT_drop_named_image);
WM_operatortype_append(OBJECT_OT_lamp_add);
WM_operatortype_append(OBJECT_OT_camera_add);
WM_operatortype_append(OBJECT_OT_speaker_add);
diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c
index c5e8310e9fb..f9dde043607 100644
--- a/source/blender/editors/object/object_relations.c
+++ b/source/blender/editors/object/object_relations.c
@@ -304,7 +304,7 @@ static int make_proxy_invoke(bContext *C, wmOperator *op, wmEvent *evt)
}
else if (ob->id.lib) {
- uiPopupMenu *pup = uiPupMenuBegin(C, "OK?", ICON_QUESTION);
+ uiPopupMenu *pup = uiPupMenuBegin(C, IFACE_("OK?"), ICON_QUESTION);
uiLayout *layout = uiPupMenuLayout(pup);
/* create operator menu item with relevant properties filled in */
diff --git a/source/blender/editors/object/object_select.c b/source/blender/editors/object/object_select.c
index 775a2897513..baa0199baf7 100644
--- a/source/blender/editors/object/object_select.c
+++ b/source/blender/editors/object/object_select.c
@@ -50,6 +50,8 @@
#include "BLI_string.h"
#include "BLI_utildefines.h"
+#include "BLF_translation.h"
+
#include "BKE_context.h"
#include "BKE_group.h"
#include "BKE_main.h"
@@ -606,7 +608,7 @@ static short select_grouped_group(bContext *C, Object *ob) /* Select objects in
}
/* build the menu. */
- pup = uiPupMenuBegin(C, "Select Group", ICON_NONE);
+ pup = uiPupMenuBegin(C, IFACE_("Select Group"), ICON_NONE);
layout = uiPupMenuLayout(pup);
for (i = 0; i < group_count; i++) {
diff --git a/source/blender/editors/object/object_transform.c b/source/blender/editors/object/object_transform.c
index 01dcac2d1b4..3fa6c301fcb 100644
--- a/source/blender/editors/object/object_transform.c
+++ b/source/blender/editors/object/object_transform.c
@@ -871,7 +871,7 @@ static int object_origin_set_exec(bContext *C, wmOperator *op)
/* Function to recenter armatures in editarmature.c
* Bone + object locations are handled there.
*/
- docenter_armature(scene, ob, cursor, centermode, around);
+ ED_armature_origin_set(scene, ob, cursor, centermode, around);
tot_change++;
arm->id.flag |= LIB_DOIT;
diff --git a/source/blender/editors/object/object_vgroup.c b/source/blender/editors/object/object_vgroup.c
index 87ff42ef4db..ef882b2486b 100644
--- a/source/blender/editors/object/object_vgroup.c
+++ b/source/blender/editors/object/object_vgroup.c
@@ -558,8 +558,10 @@ static int ed_vgroup_transfer_weight(Object *ob_dst, Object *ob_src, bDeformGrou
}
/* Loop through the vertices.*/
- for (i = 0, dv_src = dv_array_src, dv_dst = dv_array_dst; i < me_dst->totvert;
- i++, dv_dst++, dv_src++, mv_src++, mv_dst++) {
+ for (i = 0, dv_src = dv_array_src, dv_dst = dv_array_dst;
+ i < me_dst->totvert;
+ i++, dv_dst++, dv_src++, mv_src++, mv_dst++)
+ {
if (*dv_dst == NULL) {
continue;
@@ -2598,7 +2600,7 @@ static int vertex_group_poll(bContext *C)
return (ob && !ob->id.lib && OB_TYPE_SUPPORT_VGROUP(ob->type) && data && !data->lib);
}
-static int UNUSED_FUNCTION(vertex_group_poll_edit) (bContext * C)
+static int UNUSED_FUNCTION(vertex_group_poll_edit) (bContext *C)
{
Object *ob = ED_object_context(C);
ID *data = (ob) ? ob->data : NULL;
diff --git a/source/blender/editors/physics/physics_pointcache.c b/source/blender/editors/physics/physics_pointcache.c
index bbce94b6215..b9742c9968f 100644
--- a/source/blender/editors/physics/physics_pointcache.c
+++ b/source/blender/editors/physics/physics_pointcache.c
@@ -90,6 +90,20 @@ static void bake_console_progress_end(void *UNUSED(arg))
printf("\rbake: done!\n");
}
+static void ptcache_free_bake(PointCache *cache)
+{
+ if (cache->edit) {
+ if (!cache->edit->edited || 1) {// XXX okee("Lose changes done in particle mode?")) {
+ PE_free_ptcache_edit(cache->edit);
+ cache->edit = NULL;
+ cache->flag &= ~PTCACHE_BAKED;
+ }
+ }
+ else {
+ cache->flag &= ~PTCACHE_BAKED;
+ }
+}
+
static int ptcache_bake_all_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
@@ -139,7 +153,7 @@ static int ptcache_free_bake_all_exec(bContext *C, wmOperator *UNUSED(op))
BKE_ptcache_ids_from_object(&pidlist, base->object, scene, MAX_DUPLI_RECUR);
for (pid=pidlist.first; pid; pid=pid->next) {
- pid->cache->flag &= ~PTCACHE_BAKED;
+ ptcache_free_bake(pid->cache);
}
BLI_freelistN(&pidlist);
@@ -241,15 +255,7 @@ static int ptcache_free_bake_exec(bContext *C, wmOperator *UNUSED(op))
PointCache *cache= ptr.data;
Object *ob= ptr.id.data;
- if (cache->edit) {
- if (!cache->edit->edited || 1) {// XXX okee("Lose changes done in particle mode?")) {
- PE_free_ptcache_edit(cache->edit);
- cache->edit = NULL;
- cache->flag &= ~PTCACHE_BAKED;
- }
- }
- else
- cache->flag &= ~PTCACHE_BAKED;
+ ptcache_free_bake(cache);
WM_event_add_notifier(C, NC_OBJECT|ND_POINTCACHE, ob);
diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c
index 46f726e45c6..1b7cd4a6d20 100644
--- a/source/blender/editors/screen/area.c
+++ b/source/blender/editors/screen/area.c
@@ -1586,14 +1586,14 @@ int ED_area_header_standardbuttons(const bContext *C, uiBlock *block, int yco)
ICON_DISCLOSURE_TRI_RIGHT,
xco, yco, U.widget_unit, U.widget_unit * 0.9f,
&(sa->flag), 0, 0, 0, 0,
- "Show pulldown menus");
+ TIP_("Show pulldown menus"));
}
else {
but = uiDefIconButBitS(block, TOG, HEADER_NO_PULLDOWN, 0,
ICON_DISCLOSURE_TRI_DOWN,
xco, yco, U.widget_unit, U.widget_unit * 0.9f,
&(sa->flag), 0, 0, 0, 0,
- "Hide pulldown menus");
+ TIP_("Hide pulldown menus"));
}
uiButClearFlag(but, UI_BUT_UNDO); /* skip undo on screen buttons */
@@ -1967,11 +1967,11 @@ void ED_region_visible_rect(ARegion *ar, rcti *rect)
if (BLI_rcti_isect(rect, &arn->winrct, NULL)) {
/* overlap left, also check 1 pixel offset (2 regions on one side) */
- if ( ABS(rect->xmin - arn->winrct.xmin) < 2)
+ if (ABS(rect->xmin - arn->winrct.xmin) < 2)
rect->xmin = arn->winrct.xmax;
/* overlap right */
- if ( ABS(rect->xmax - arn->winrct.xmax) < 2)
+ if (ABS(rect->xmax - arn->winrct.xmax) < 2)
rect->xmax = arn->winrct.xmin;
}
}
diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c
index 12a7a33c893..fcd0968d52f 100644
--- a/source/blender/editors/screen/screen_ops.c
+++ b/source/blender/editors/screen/screen_ops.c
@@ -206,7 +206,7 @@ int ED_operator_animview_active(bContext *C)
return TRUE;
}
- CTX_wm_operator_poll_msg_set(C, "expected an timeline/animation area to be active");
+ CTX_wm_operator_poll_msg_set(C, "expected a timeline/animation area to be active");
return 0;
}
@@ -3427,8 +3427,8 @@ static int userpref_show_invoke(bContext *C, wmOperator *UNUSED(op), wmEvent *ev
rcti rect;
int sizex, sizey;
- sizex = 800;
- sizey = 480;
+ sizex = 800 * UI_DPI_WINDOW_FAC;
+ sizey = 480 * UI_DPI_WINDOW_FAC;
/* some magic to calculate postition */
/* pixelsize: mouse coords are in U.pixelsize units :/ */
@@ -3447,8 +3447,8 @@ static int userpref_show_invoke(bContext *C, wmOperator *UNUSED(op), wmEvent *ev
static void SCREEN_OT_userpref_show(struct wmOperatorType *ot)
{
/* identifiers */
- ot->name = "Show/Hide User Preferences";
- ot->description = "Show/hide user preferences";
+ ot->name = "Show User Preferences";
+ ot->description = "Show user preferences";
ot->idname = "SCREEN_OT_userpref_show";
/* api callbacks */
diff --git a/source/blender/editors/screen/screendump.c b/source/blender/editors/screen/screendump.c
index 1f7fee313b3..9cde62e8302 100644
--- a/source/blender/editors/screen/screendump.c
+++ b/source/blender/editors/screen/screendump.c
@@ -449,13 +449,21 @@ static void screenshot_endjob(void *sjv)
static int screencast_exec(bContext *C, wmOperator *op)
{
+ wmWindowManager *wm = CTX_wm_manager(C);
+ wmWindow *win = CTX_wm_window(C);
bScreen *screen = CTX_wm_screen(C);
- wmJob *wm_job = WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C), screen, "Screencast", 0, WM_JOB_TYPE_SCREENCAST);
- ScreenshotJob *sj = MEM_callocN(sizeof(ScreenshotJob), "screenshot job");
+ wmJob *wm_job;
+ ScreenshotJob *sj;
+ /* if called again, stop the running job */
+ if (WM_jobs_test(wm, screen, WM_JOB_TYPE_SCREENCAST))
+ WM_jobs_stop(wm, screen, screenshot_startjob);
+
+ wm_job = WM_jobs_get(wm, win, screen, "Screencast", 0, WM_JOB_TYPE_SCREENCAST);
+ sj = MEM_callocN(sizeof(ScreenshotJob), "screenshot job");
+
/* setup sj */
if (RNA_boolean_get(op->ptr, "full")) {
- wmWindow *win = CTX_wm_window(C);
sj->x = 0;
sj->y = 0;
sj->dumpsx = WM_window_pixels_x(win);
@@ -470,7 +478,7 @@ static int screencast_exec(bContext *C, wmOperator *op)
}
sj->bmain = CTX_data_main(C);
sj->scene = CTX_data_scene(C);
- sj->wm = CTX_wm_manager(C);
+ sj->wm = wm;
BKE_reports_init(&sj->reports, RPT_PRINT);
diff --git a/source/blender/editors/sculpt_paint/paint_intern.h b/source/blender/editors/sculpt_paint/paint_intern.h
index faa9ce00da8..e7c3ddd071b 100644
--- a/source/blender/editors/sculpt_paint/paint_intern.h
+++ b/source/blender/editors/sculpt_paint/paint_intern.h
@@ -151,12 +151,10 @@ void BRUSH_OT_curve_preset(struct wmOperatorType *ot);
void PAINT_OT_face_select_linked(struct wmOperatorType *ot);
void PAINT_OT_face_select_linked_pick(struct wmOperatorType *ot);
void PAINT_OT_face_select_all(struct wmOperatorType *ot);
-void PAINT_OT_face_select_inverse(struct wmOperatorType *ot);
void PAINT_OT_face_select_hide(struct wmOperatorType *ot);
void PAINT_OT_face_select_reveal(struct wmOperatorType *ot);
void PAINT_OT_vert_select_all(struct wmOperatorType *ot);
-void PAINT_OT_vert_select_inverse(struct wmOperatorType *ot);
int vert_paint_poll(struct bContext *C);
int mask_paint_poll(struct bContext *C);
diff --git a/source/blender/editors/sculpt_paint/paint_mask.c b/source/blender/editors/sculpt_paint/paint_mask.c
index 3cf67667f39..dffb8c39bf2 100644
--- a/source/blender/editors/sculpt_paint/paint_mask.c
+++ b/source/blender/editors/sculpt_paint/paint_mask.c
@@ -127,13 +127,14 @@ static int mask_flood_fill_exec(bContext *C, wmOperator *op)
void PAINT_OT_mask_flood_fill(struct wmOperatorType *ot)
{
static EnumPropertyItem mode_items[] = {
- {PAINT_MASK_FLOOD_VALUE, "VALUE", 0, "Value", "Set mask to the level specified by the \"value\" property"},
+ {PAINT_MASK_FLOOD_VALUE, "VALUE", 0, "Value", "Set mask to the level specified by the 'value' property"},
{PAINT_MASK_INVERT, "INVERT", 0, "Invert", "Invert the mask"},
{0}};
/* identifiers */
ot->name = "Mask Flood Fill";
ot->idname = "PAINT_OT_mask_flood_fill";
+ ot->description = "Fill the whole mask with a given value, or invert its values";
/* api callbacks */
ot->exec = mask_flood_fill_exec;
@@ -143,5 +144,6 @@ void PAINT_OT_mask_flood_fill(struct wmOperatorType *ot)
/* rna */
RNA_def_enum(ot->srna, "mode", mode_items, PAINT_MASK_FLOOD_VALUE, "Mode", NULL);
- RNA_def_float(ot->srna, "value", 0, 0, 1, "Value", "Mask level to use when mode is \"Value\"; zero means no masking and one is fully masked", 0, 1);
+ RNA_def_float(ot->srna, "value", 0, 0, 1, "Value",
+ "Mask level to use when mode is 'Value'; zero means no masking and one is fully masked", 0, 1);
}
diff --git a/source/blender/editors/sculpt_paint/paint_ops.c b/source/blender/editors/sculpt_paint/paint_ops.c
index ba33ad22f92..7385c2f0cf1 100644
--- a/source/blender/editors/sculpt_paint/paint_ops.c
+++ b/source/blender/editors/sculpt_paint/paint_ops.c
@@ -485,7 +485,6 @@ void ED_operatortypes_paint(void)
/* vertex selection */
WM_operatortype_append(PAINT_OT_vert_select_all);
- WM_operatortype_append(PAINT_OT_vert_select_inverse);
/* vertex */
WM_operatortype_append(PAINT_OT_vertex_paint_toggle);
@@ -496,7 +495,6 @@ void ED_operatortypes_paint(void)
WM_operatortype_append(PAINT_OT_face_select_linked);
WM_operatortype_append(PAINT_OT_face_select_linked_pick);
WM_operatortype_append(PAINT_OT_face_select_all);
- WM_operatortype_append(PAINT_OT_face_select_inverse);
WM_operatortype_append(PAINT_OT_face_select_hide);
WM_operatortype_append(PAINT_OT_face_select_reveal);
@@ -748,7 +746,8 @@ void ED_keymap_paint(wmKeyConfig *keyconf)
keymap = WM_keymap_find(keyconf, "Weight Paint Vertex Selection", 0, 0);
keymap->poll = vert_paint_poll;
WM_keymap_add_item(keymap, "PAINT_OT_vert_select_all", AKEY, KM_PRESS, 0, 0);
- WM_keymap_add_item(keymap, "PAINT_OT_vert_select_inverse", IKEY, KM_PRESS, KM_CTRL, 0);
+ kmi = WM_keymap_add_item(keymap, "PAINT_OT_vert_select_all", IKEY, KM_PRESS, KM_CTRL, 0);
+ RNA_enum_set(kmi->ptr, "action", SEL_INVERT);
WM_keymap_add_item(keymap, "VIEW3D_OT_select_border", BKEY, KM_PRESS, 0, 0);
kmi = WM_keymap_add_item(keymap, "VIEW3D_OT_select_lasso", EVT_TWEAK_A, KM_ANY, KM_CTRL, 0);
RNA_boolean_set(kmi->ptr, "deselect", FALSE);
@@ -777,7 +776,8 @@ void ED_keymap_paint(wmKeyConfig *keyconf)
keymap->poll = facemask_paint_poll;
WM_keymap_add_item(keymap, "PAINT_OT_face_select_all", AKEY, KM_PRESS, 0, 0);
- WM_keymap_add_item(keymap, "PAINT_OT_face_select_inverse", IKEY, KM_PRESS, KM_CTRL, 0);
+ kmi = WM_keymap_add_item(keymap, "PAINT_OT_face_select_all", IKEY, KM_PRESS, KM_CTRL, 0);
+ RNA_enum_set(kmi->ptr, "action", SEL_INVERT);
kmi = WM_keymap_add_item(keymap, "PAINT_OT_face_select_hide", HKEY, KM_PRESS, 0, 0);
RNA_boolean_set(kmi->ptr, "unselected", FALSE);
kmi = WM_keymap_add_item(keymap, "PAINT_OT_face_select_hide", HKEY, KM_PRESS, KM_SHIFT, 0);
diff --git a/source/blender/editors/sculpt_paint/paint_utils.c b/source/blender/editors/sculpt_paint/paint_utils.c
index 4f156276aac..dc3f310b405 100644
--- a/source/blender/editors/sculpt_paint/paint_utils.c
+++ b/source/blender/editors/sculpt_paint/paint_utils.c
@@ -448,7 +448,7 @@ static int face_select_all_exec(bContext *C, wmOperator *op)
void PAINT_OT_face_select_all(wmOperatorType *ot)
{
- ot->name = "Face Selection";
+ ot->name = "(De)select All";
ot->description = "Change selection for all faces";
ot->idname = "PAINT_OT_face_select_all";
@@ -472,7 +472,7 @@ static int vert_select_all_exec(bContext *C, wmOperator *op)
void PAINT_OT_vert_select_all(wmOperatorType *ot)
{
- ot->name = "Vertex Selection";
+ ot->name = "(De)select All";
ot->description = "Change selection for all vertices";
ot->idname = "PAINT_OT_vert_select_all";
@@ -484,46 +484,6 @@ void PAINT_OT_vert_select_all(wmOperatorType *ot)
WM_operator_properties_select_all(ot);
}
-static int vert_select_inverse_exec(bContext *C, wmOperator *UNUSED(op))
-{
- Object *ob = CTX_data_active_object(C);
- paintvert_deselect_all_visible(ob, SEL_INVERT, TRUE);
- ED_region_tag_redraw(CTX_wm_region(C));
- return OPERATOR_FINISHED;
-}
-
-void PAINT_OT_vert_select_inverse(wmOperatorType *ot)
-{
- ot->name = "Vertex Select Invert";
- ot->description = "Invert selection of vertices";
- ot->idname = "PAINT_OT_vert_select_inverse";
-
- ot->exec = vert_select_inverse_exec;
- ot->poll = vert_paint_poll;
-
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-}
-static int face_select_inverse_exec(bContext *C, wmOperator *UNUSED(op))
-{
- Object *ob = CTX_data_active_object(C);
- paintface_deselect_all_visible(ob, SEL_INVERT, TRUE);
- ED_region_tag_redraw(CTX_wm_region(C));
- return OPERATOR_FINISHED;
-}
-
-
-void PAINT_OT_face_select_inverse(wmOperatorType *ot)
-{
- ot->name = "Face Select Invert";
- ot->description = "Invert selection of faces";
- ot->idname = "PAINT_OT_face_select_inverse";
-
- ot->exec = face_select_inverse_exec;
- ot->poll = facemask_paint_poll;
-
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-}
-
static int face_select_hide_exec(bContext *C, wmOperator *op)
{
const int unselected = RNA_boolean_get(op->ptr, "unselected");
diff --git a/source/blender/editors/sculpt_paint/paint_vertex.c b/source/blender/editors/sculpt_paint/paint_vertex.c
index 8ff2a68013c..0277e1e11dc 100644
--- a/source/blender/editors/sculpt_paint/paint_vertex.c
+++ b/source/blender/editors/sculpt_paint/paint_vertex.c
@@ -913,7 +913,8 @@ static float calc_vp_alpha_dl(VPaint *vp, ViewContext *vc,
BLI_INLINE float wval_blend(const float weight, const float paintval, const float alpha)
{
- return (paintval * alpha) + (weight * (1.0f - alpha));
+ const float talpha = min_ff(alpha, 1.0f); /* blending with values over 1 doesn't make sense */
+ return (paintval * talpha) + (weight * (1.0f - talpha));
}
BLI_INLINE float wval_add(const float weight, const float paintval, const float alpha)
{
diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c
index 342671698d2..90e7645032b 100644
--- a/source/blender/editors/sculpt_paint/sculpt.c
+++ b/source/blender/editors/sculpt_paint/sculpt.c
@@ -2281,7 +2281,7 @@ static void calc_flatten_center(Sculpt *sd, Object *ob, PBVHNode **nodes, int to
unode = sculpt_undo_push_node(ob, nodes[n], SCULPT_UNDO_COORDS);
sculpt_brush_test_init(ss, &test);
- if (ss->cache->original) {
+ if (ss->cache->original && unode->co) {
BKE_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE)
{
if (sculpt_brush_test_fast(&test, unode->co[vd.i])) {
@@ -3742,7 +3742,8 @@ static void sculpt_update_cache_invariants(bContext *C, Sculpt *sd, SculptSessio
if (brush->sculpt_tool == SCULPT_TOOL_LAYER) {
/* not supported yet for multires or dynamic topology */
if (!ss->multires && !ss->bm && !ss->layer_co &&
- (brush->flag & BRUSH_PERSISTENT)) {
+ (brush->flag & BRUSH_PERSISTENT))
+ {
if (!ss->layer_co)
ss->layer_co = MEM_mallocN(sizeof(float) * 3 * ss->totvert,
"sculpt mesh vertices copy");
@@ -4784,6 +4785,7 @@ static void SCULPT_OT_symmetrize(wmOperatorType *ot)
/* identifiers */
ot->name = "Symmetrize";
ot->idname = "SCULPT_OT_symmetrize";
+ ot->description = "Symmetrize the topology modifications";
/* api callbacks */
ot->exec = sculpt_symmetrize_exec;
diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.c b/source/blender/editors/sculpt_paint/sculpt_undo.c
index c828e8c8651..406756f4197 100644
--- a/source/blender/editors/sculpt_paint/sculpt_undo.c
+++ b/source/blender/editors/sculpt_paint/sculpt_undo.c
@@ -567,6 +567,8 @@ static SculptUndoNode *sculpt_undo_alloc_node(Object *ob, PBVHNode *node,
unode->totvert = totvert;
}
+ else
+ maxgrid = 0;
/* we will use this while sculpting, is mapalloc slow to access then? */
diff --git a/source/blender/editors/space_buttons/buttons_texture.c b/source/blender/editors/space_buttons/buttons_texture.c
index 935b15f3cd8..16d194d0929 100644
--- a/source/blender/editors/space_buttons/buttons_texture.c
+++ b/source/blender/editors/space_buttons/buttons_texture.c
@@ -37,6 +37,8 @@
#include "BLI_string.h"
#include "BLI_utildefines.h"
+#include "BLF_translation.h"
+
#include "DNA_brush_types.h"
#include "DNA_ID.h"
#include "DNA_lamp_types.h"
@@ -397,7 +399,7 @@ void uiTemplateTextureUser(uiLayout *layout, bContext *C)
user = ct->user;
if (!user) {
- uiItemL(layout, "No textures in context.", ICON_NONE);
+ uiItemL(layout, IFACE_("No textures in context"), ICON_NONE);
return;
}
diff --git a/source/blender/editors/space_clip/clip_buttons.c b/source/blender/editors/space_clip/clip_buttons.c
index b2d31ececa5..9409ce42d3a 100644
--- a/source/blender/editors/space_clip/clip_buttons.c
+++ b/source/blender/editors/space_clip/clip_buttons.c
@@ -407,7 +407,7 @@ void uiTemplateMarker(uiLayout *layout, PointerRNA *ptr, const char *propname, P
if (track->flag & TRACK_LOCKED) {
uiLayoutSetActive(layout, FALSE);
block = uiLayoutAbsoluteBlock(layout);
- uiDefBut(block, LABEL, 0, IFACE_("Track is locked"), 0, 0, UI_UNIT_X*15.0f, UI_UNIT_Y, NULL, 0, 0, 0, 0, "");
+ uiDefBut(block, LABEL, 0, IFACE_("Track is locked"), 0, 0, UI_UNIT_X * 15.0f, UI_UNIT_Y, NULL, 0, 0, 0, 0, "");
return;
}
diff --git a/source/blender/editors/space_clip/tracking_ops.c b/source/blender/editors/space_clip/tracking_ops.c
index 77ed197c1d7..ac7ebd9ddac 100644
--- a/source/blender/editors/space_clip/tracking_ops.c
+++ b/source/blender/editors/space_clip/tracking_ops.c
@@ -2892,7 +2892,7 @@ void CLIP_OT_set_solver_keyframe(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
- RNA_def_enum(ot->srna, "keyframe", keyframe_items, 0, "Keyframe", "keyframe to set");
+ RNA_def_enum(ot->srna, "keyframe", keyframe_items, 0, "Keyframe", "Keyframe to set");
}
/********************** track copy color operator *********************/
diff --git a/source/blender/editors/space_file/file_ops.c b/source/blender/editors/space_file/file_ops.c
index 9349abb4d8b..79a578a86d1 100644
--- a/source/blender/editors/space_file/file_ops.c
+++ b/source/blender/editors/space_file/file_ops.c
@@ -1178,11 +1178,10 @@ static void file_expand_directory(bContext *C)
get_default_root(sfile->params->dir);
}
/* change "C:" --> "C:\", [#28102] */
- else if ( (isalpha(sfile->params->dir[0]) &&
- (sfile->params->dir[1] == ':')) &&
- (sfile->params->dir[2] == '\0')
-
- ) {
+ else if ((isalpha(sfile->params->dir[0]) &&
+ (sfile->params->dir[1] == ':')) &&
+ (sfile->params->dir[2] == '\0'))
+ {
sfile->params->dir[2] = '\\';
sfile->params->dir[3] = '\0';
}
diff --git a/source/blender/editors/space_file/fsmenu.h b/source/blender/editors/space_file/fsmenu.h
index 1b69eb09fce..eea7e0e3837 100644
--- a/source/blender/editors/space_file/fsmenu.h
+++ b/source/blender/editors/space_file/fsmenu.h
@@ -64,7 +64,7 @@ char *fsmenu_get_entry(struct FSMenu *fsmenu, FSMenuCategory category, int index
/** Inserts a new fsmenu entry with the given \a path.
* Duplicate entries are not added.
- * \param sorted Should entry be inserted in sorted order?
+ * \param flag Options for inserting the entry.
*/
void fsmenu_insert_entry(struct FSMenu *fsmenu, FSMenuCategory category, const char *path, const FSMenuInsert flag);
diff --git a/source/blender/editors/space_graph/graph_buttons.c b/source/blender/editors/space_graph/graph_buttons.c
index ab16a9d55e6..f1501857b13 100644
--- a/source/blender/editors/space_graph/graph_buttons.c
+++ b/source/blender/editors/space_graph/graph_buttons.c
@@ -461,6 +461,7 @@ static void graph_panel_driverVar__singleProp(uiLayout *layout, ID *id, DriverVa
/* Target ID */
row = uiLayoutRow(layout, FALSE);
+ uiLayoutSetRedAlert(row, ((dtar->flag & DTAR_FLAG_INVALID) && !dtar->id));
uiTemplateAnyID(row, &dtar_ptr, "id", "id_type", IFACE_("Prop:"));
/* Target Property */
@@ -470,8 +471,9 @@ static void graph_panel_driverVar__singleProp(uiLayout *layout, ID *id, DriverVa
/* get pointer for resolving the property selected */
RNA_id_pointer_create(dtar->id, &root_ptr);
- col = uiLayoutColumn(layout, TRUE);
/* rna path */
+ col = uiLayoutColumn(layout, TRUE);
+ uiLayoutSetRedAlert(col, (dtar->flag & DTAR_FLAG_INVALID));
uiTemplatePathBuilder(col, &dtar_ptr, "data_path", &root_ptr, IFACE_("Path"));
}
}
@@ -492,21 +494,23 @@ static void graph_panel_driverVar__rotDiff(uiLayout *layout, ID *id, DriverVar *
/* Bone 1 */
col = uiLayoutColumn(layout, TRUE);
+ uiLayoutSetRedAlert(col, (dtar->flag & DTAR_FLAG_INVALID)); /* XXX: per field... */
uiTemplateAnyID(col, &dtar_ptr, "id", "id_type", IFACE_("Bone 1:"));
-
+
if (dtar->id && ob1->pose) {
PointerRNA tar_ptr;
-
+
RNA_pointer_create(dtar->id, &RNA_Pose, ob1->pose, &tar_ptr);
uiItemPointerR(col, &dtar_ptr, "bone_target", &tar_ptr, "bones", "", ICON_BONE_DATA);
}
col = uiLayoutColumn(layout, TRUE);
+ uiLayoutSetRedAlert(col, (dtar2->flag & DTAR_FLAG_INVALID)); /* XXX: per field... */
uiTemplateAnyID(col, &dtar2_ptr, "id", "id_type", IFACE_("Bone 2:"));
if (dtar2->id && ob2->pose) {
PointerRNA tar_ptr;
-
+
RNA_pointer_create(dtar2->id, &RNA_Pose, ob2->pose, &tar_ptr);
uiItemPointerR(col, &dtar2_ptr, "bone_target", &tar_ptr, "bones", "", ICON_BONE_DATA);
}
@@ -515,7 +519,7 @@ static void graph_panel_driverVar__rotDiff(uiLayout *layout, ID *id, DriverVar *
/* settings for 'location difference' driver variable type */
static void graph_panel_driverVar__locDiff(uiLayout *layout, ID *id, DriverVar *dvar)
{
- DriverTarget *dtar = &dvar->targets[0];
+ DriverTarget *dtar = &dvar->targets[0];
DriverTarget *dtar2 = &dvar->targets[1];
Object *ob1 = (Object *)dtar->id;
Object *ob2 = (Object *)dtar2->id;
@@ -523,32 +527,36 @@ static void graph_panel_driverVar__locDiff(uiLayout *layout, ID *id, DriverVar *
uiLayout *col;
/* initialize RNA pointer to the target */
- RNA_pointer_create(id, &RNA_DriverTarget, dtar, &dtar_ptr);
+ RNA_pointer_create(id, &RNA_DriverTarget, dtar, &dtar_ptr);
RNA_pointer_create(id, &RNA_DriverTarget, dtar2, &dtar2_ptr);
/* Bone 1 */
col = uiLayoutColumn(layout, TRUE);
+ uiLayoutSetRedAlert(col, (dtar->flag & DTAR_FLAG_INVALID)); /* XXX: per field... */
uiTemplateAnyID(col, &dtar_ptr, "id", "id_type", IFACE_("Ob/Bone 1:"));
if (dtar->id && ob1->pose) {
PointerRNA tar_ptr;
-
+
RNA_pointer_create(dtar->id, &RNA_Pose, ob1->pose, &tar_ptr);
uiItemPointerR(col, &dtar_ptr, "bone_target", &tar_ptr, "bones", "", ICON_BONE_DATA);
}
-
+
+ uiLayoutSetRedAlert(col, false); /* we can clear it again now - it's only needed when creating the ID/Bone fields */
uiItemR(col, &dtar_ptr, "transform_space", 0, NULL, ICON_NONE);
col = uiLayoutColumn(layout, TRUE);
+ uiLayoutSetRedAlert(col, (dtar2->flag & DTAR_FLAG_INVALID)); /* XXX: per field... */
uiTemplateAnyID(col, &dtar2_ptr, "id", "id_type", IFACE_("Ob/Bone 2:"));
if (dtar2->id && ob2->pose) {
PointerRNA tar_ptr;
-
+
RNA_pointer_create(dtar2->id, &RNA_Pose, ob2->pose, &tar_ptr);
uiItemPointerR(col, &dtar2_ptr, "bone_target", &tar_ptr, "bones", "", ICON_BONE_DATA);
}
+ uiLayoutSetRedAlert(col, false); /* we can clear it again now - it's only needed when creating the ID/Bone fields */
uiItemR(col, &dtar2_ptr, "transform_space", 0, NULL, ICON_NONE);
}
@@ -565,11 +573,12 @@ static void graph_panel_driverVar__transChan(uiLayout *layout, ID *id, DriverVar
/* properties */
col = uiLayoutColumn(layout, TRUE);
+ uiLayoutSetRedAlert(col, (dtar->flag & DTAR_FLAG_INVALID)); /* XXX: per field... */
uiTemplateAnyID(col, &dtar_ptr, "id", "id_type", IFACE_("Ob/Bone:"));
if (dtar->id && ob->pose) {
PointerRNA tar_ptr;
-
+
RNA_pointer_create(dtar->id, &RNA_Pose, ob->pose, &tar_ptr);
uiItemPointerR(col, &dtar_ptr, "bone_target", &tar_ptr, "bones", "", ICON_BONE_DATA);
}
@@ -623,7 +632,7 @@ static void graph_panel_drivers(const bContext *C, Panel *pa)
if (driver->type == DRIVER_TYPE_PYTHON) {
/* expression */
uiItemR(col, &driver_ptr, "expression", 0, IFACE_("Expr"), ICON_NONE);
-
+
/* errors? */
if (driver->flag & DRIVER_FLAG_INVALID)
uiItemL(col, IFACE_("ERROR: Invalid Python expression"), ICON_ERROR);
@@ -668,19 +677,19 @@ static void graph_panel_drivers(const bContext *C, Panel *pa)
box = uiLayoutBox(col);
/* first row context info for driver */
RNA_pointer_create(ale->id, &RNA_DriverVariable, dvar, &dvar_ptr);
-
+
row = uiLayoutRow(box, FALSE);
block = uiLayoutGetBlock(row);
/* variable name */
uiItemR(row, &dvar_ptr, "name", 0, "", ICON_NONE);
-
+
/* remove button */
uiBlockSetEmboss(block, UI_EMBOSSN);
but = uiDefIconBut(block, BUT, B_IPO_DEPCHANGE, ICON_X, 290, 0, UI_UNIT_X, UI_UNIT_Y,
NULL, 0.0, 0.0, 0.0, 0.0, IFACE_("Delete target variable"));
uiButSetFunc(but, driver_delete_var_cb, driver, dvar);
uiBlockSetEmboss(block, UI_EMBOSS);
-
+
/* variable type */
row = uiLayoutRow(box, FALSE);
uiItemR(row, &dvar_ptr, "type", 0, "", ICON_NONE);
@@ -721,7 +730,7 @@ static void graph_panel_drivers(const bContext *C, Panel *pa)
else {
BLI_snprintf(valBuf, sizeof(valBuf), "%.3f", dvar->curval);
}
-
+
uiItemL(row, valBuf, ICON_NONE);
}
}
diff --git a/source/blender/editors/space_image/image_buttons.c b/source/blender/editors/space_image/image_buttons.c
index 01364fcacf0..6237958e723 100644
--- a/source/blender/editors/space_image/image_buttons.c
+++ b/source/blender/editors/space_image/image_buttons.c
@@ -82,36 +82,36 @@ static void image_info(Scene *scene, ImageUser *iuser, Image *ima, ImBuf *ibuf,
if (ima == NULL) return;
if (ibuf == NULL) {
- ofs += sprintf(str, "%s", IFACE_("Can't Load Image"));
+ ofs += sprintf(str, IFACE_("Can't Load Image"));
}
else {
if (ima->source == IMA_SRC_MOVIE) {
- ofs += sprintf(str, "%s", IFACE_("Movie"));
+ ofs += sprintf(str, IFACE_("Movie"));
if (ima->anim)
- ofs += sprintf(str + ofs, IFACE_("%d frs"), IMB_anim_get_duration(ima->anim, IMB_TC_RECORD_RUN));
+ ofs += sprintf(str + ofs, IFACE_(" %d frs"), IMB_anim_get_duration(ima->anim, IMB_TC_RECORD_RUN));
}
else
- ofs += sprintf(str, "%s", IFACE_("Image"));
+ ofs += sprintf(str, IFACE_("Image"));
- ofs += sprintf(str + ofs, ": %s %d x %d,", IFACE_("size"), ibuf->x, ibuf->y);
+ ofs += sprintf(str + ofs, IFACE_(": size %d x %d,"), ibuf->x, ibuf->y);
if (ibuf->rect_float) {
if (ibuf->channels != 4) {
- ofs += sprintf(str + ofs, "%d %s", ibuf->channels, IFACE_("float channel(s)"));
+ ofs += sprintf(str + ofs, IFACE_("%d float channel(s)"), ibuf->channels);
}
else if (ibuf->planes == R_IMF_PLANES_RGBA)
- ofs += sprintf(str + ofs, "%s", IFACE_(" RGBA float"));
+ ofs += sprintf(str + ofs, IFACE_(" RGBA float"));
else
- ofs += sprintf(str + ofs, "%s", IFACE_(" RGB float"));
+ ofs += sprintf(str + ofs, IFACE_(" RGB float"));
}
else {
if (ibuf->planes == R_IMF_PLANES_RGBA)
- ofs += sprintf(str + ofs, "%s", IFACE_(" RGBA byte"));
+ ofs += sprintf(str + ofs, IFACE_(" RGBA byte"));
else
- ofs += sprintf(str + ofs, "%s", IFACE_(" RGB byte"));
+ ofs += sprintf(str + ofs, IFACE_(" RGB byte"));
}
if (ibuf->zbuf || ibuf->zbuf_float)
- ofs += sprintf(str + ofs, "%s", IFACE_(" + Z"));
+ ofs += sprintf(str + ofs, IFACE_(" + Z"));
if (ima->source == IMA_SRC_SEQUENCE) {
char *file = BLI_last_slash(ibuf->name);
diff --git a/source/blender/editors/space_image/image_intern.h b/source/blender/editors/space_image/image_intern.h
index f86e59c41a8..5184b1e1a73 100644
--- a/source/blender/editors/space_image/image_intern.h
+++ b/source/blender/editors/space_image/image_intern.h
@@ -87,8 +87,6 @@ void IMAGE_OT_sample(struct wmOperatorType *ot);
void IMAGE_OT_sample_line(struct wmOperatorType *ot);
void IMAGE_OT_curves_point_set(struct wmOperatorType *ot);
-void IMAGE_OT_record_composite(struct wmOperatorType *ot);
-
/* image_panels.c */
struct ImageUser *ntree_get_active_iuser(struct bNodeTree *ntree);
void image_buttons_register(struct ARegionType *art);
diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c
index dc834d88323..b913b3528ac 100644
--- a/source/blender/editors/space_image/image_ops.c
+++ b/source/blender/editors/space_image/image_ops.c
@@ -1959,9 +1959,10 @@ static int image_pack_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event)
ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL);
if (!as_png && (ibuf && (ibuf->userflags & IB_BITMAPDIRTY))) {
- pup = uiPupMenuBegin(C, "OK", ICON_QUESTION);
+ pup = uiPupMenuBegin(C, IFACE_("OK"), ICON_QUESTION);
layout = uiPupMenuLayout(pup);
- uiItemBooleanO(layout, "Can't pack edited image from disk. Pack as internal PNG?", ICON_NONE, op->idname, "as_png", 1);
+ uiItemBooleanO(layout, IFACE_("Can't pack edited image from disk, pack as internal PNG?"), ICON_NONE,
+ op->idname, "as_png", 1);
uiPupMenuEnd(C, pup);
BKE_image_release_ibuf(ima, ibuf, NULL);
@@ -2438,6 +2439,7 @@ void IMAGE_OT_curves_point_set(wmOperatorType *ot)
RNA_def_enum(ot->srna, "point", point_items, 0, "Point", "Set black point or white point for curves");
}
+#if 0 /* Not ported to 2.5x yet */
/******************** record composite operator *********************/
typedef struct RecordCompositeData {
@@ -2589,6 +2591,8 @@ void IMAGE_OT_record_composite(wmOperatorType *ot)
ot->poll = space_image_buffer_exists_poll;
}
+#endif
+
/********************* cycle render slot operator *********************/
static int image_cycle_render_slot_poll(bContext *C)
diff --git a/source/blender/editors/space_image/space_image.c b/source/blender/editors/space_image/space_image.c
index ca8270ba0f5..d82a46e9578 100644
--- a/source/blender/editors/space_image/space_image.c
+++ b/source/blender/editors/space_image/space_image.c
@@ -253,8 +253,6 @@ static void image_operatortypes(void)
WM_operatortype_append(IMAGE_OT_sample_line);
WM_operatortype_append(IMAGE_OT_curves_point_set);
- WM_operatortype_append(IMAGE_OT_record_composite);
-
WM_operatortype_append(IMAGE_OT_properties);
WM_operatortype_append(IMAGE_OT_scopes);
}
diff --git a/source/blender/editors/space_info/info_ops.c b/source/blender/editors/space_info/info_ops.c
index 22668a3de3a..04f6a5152e6 100644
--- a/source/blender/editors/space_info/info_ops.c
+++ b/source/blender/editors/space_info/info_ops.c
@@ -40,9 +40,11 @@
#include "BLI_blenlib.h"
#include "BLI_math.h"
-#include "BKE_bpath.h"
#include "BLI_utildefines.h"
+#include "BLF_translation.h"
+
+#include "BKE_bpath.h"
#include "BKE_context.h"
#include "BKE_global.h"
#include "BKE_image.h"
@@ -218,9 +220,9 @@ static int unpack_all_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event)
}
if (count == 1)
- strcpy(title, "Unpack 1 file");
+ strcpy(title, IFACE_("Unpack 1 File"));
else
- BLI_snprintf(title, sizeof(title), "Unpack %d files", count);
+ BLI_snprintf(title, sizeof(title), IFACE_("Unpack %d Files"), count);
pup = uiPupMenuBegin(C, title, ICON_NONE);
layout = uiPupMenuLayout(pup);
@@ -291,7 +293,7 @@ static int unpack_item_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event
uiPopupMenu *pup;
uiLayout *layout;
- pup = uiPupMenuBegin(C, "Unpack", ICON_NONE);
+ pup = uiPupMenuBegin(C, IFACE_("Unpack"), ICON_NONE);
layout = uiPupMenuLayout(pup);
uiLayoutSetOperatorContext(layout, WM_OP_EXEC_DEFAULT);
diff --git a/source/blender/editors/space_info/info_stats.c b/source/blender/editors/space_info/info_stats.c
index 5a8a77f7350..976769752f9 100644
--- a/source/blender/editors/space_info/info_stats.c
+++ b/source/blender/editors/space_info/info_stats.c
@@ -367,9 +367,9 @@ static void stats_string(Scene *scene)
mmap_in_use = MEM_get_mapped_memory_in_use();
/* get memory statistics */
- s = memstr + sprintf(memstr, " | Mem:%.2fM", (double)((mem_in_use - mmap_in_use) >> 10) / 1024.0);
+ s = memstr + sprintf(memstr, IFACE_(" | Mem:%.2fM"), (double)((mem_in_use - mmap_in_use) >> 10) / 1024.0);
if (mmap_in_use)
- sprintf(s, " (%.2fM)", (double)((mmap_in_use) >> 10) / 1024.0);
+ sprintf(s, IFACE_(" (%.2fM)"), (double)((mmap_in_use) >> 10) / 1024.0);
s = stats->infostr;
@@ -377,32 +377,32 @@ static void stats_string(Scene *scene)
if (scene->obedit) {
if (BKE_keyblock_from_object(scene->obedit))
- s += sprintf(s, "(Key) ");
+ s += sprintf(s, IFACE_("(Key) "));
if (scene->obedit->type == OB_MESH) {
- s += sprintf(s, "Verts:%d/%d | Edges:%d/%d | Faces:%d/%d | Tris:%d",
+ s += sprintf(s, IFACE_("Verts:%d/%d | Edges:%d/%d | Faces:%d/%d | Tris:%d"),
stats->totvertsel, stats->totvert, stats->totedgesel, stats->totedge, stats->totfacesel,
stats->totface, stats->tottri);
}
else if (scene->obedit->type == OB_ARMATURE) {
- s += sprintf(s, "Verts:%d/%d | Bones:%d/%d", stats->totvertsel, stats->totvert, stats->totbonesel,
+ s += sprintf(s, IFACE_("Verts:%d/%d | Bones:%d/%d"), stats->totvertsel, stats->totvert, stats->totbonesel,
stats->totbone);
}
else {
- s += sprintf(s, "Verts:%d/%d", stats->totvertsel, stats->totvert);
+ s += sprintf(s, IFACE_("Verts:%d/%d"), stats->totvertsel, stats->totvert);
}
strcat(s, memstr);
}
else if (ob && (ob->mode & OB_MODE_POSE)) {
- s += sprintf(s, "Bones:%d/%d %s",
+ s += sprintf(s, IFACE_("Bones:%d/%d %s"),
stats->totbonesel, stats->totbone, memstr);
}
else if (stats_is_object_dynamic_topology_sculpt(ob)) {
- s += sprintf(s, "Verts:%d | Tris:%d", stats->totvert, stats->tottri);
+ s += sprintf(s, IFACE_("Verts:%d | Tris:%d"), stats->totvert, stats->tottri);
}
else {
- s += sprintf(s, "Verts:%d | Faces:%d | Tris:%d | Objects:%d/%d | Lamps:%d/%d%s",
+ s += sprintf(s, IFACE_("Verts:%d | Faces:%d | Tris:%d | Objects:%d/%d | Lamps:%d/%d%s"),
stats->totvert, stats->totface, stats->tottri, stats->totobjsel, stats->totobj, stats->totlampsel,
stats->totlamp, memstr);
}
diff --git a/source/blender/editors/space_nla/nla_buttons.c b/source/blender/editors/space_nla/nla_buttons.c
index e2eb1f030b3..31c773e613e 100644
--- a/source/blender/editors/space_nla/nla_buttons.c
+++ b/source/blender/editors/space_nla/nla_buttons.c
@@ -311,7 +311,7 @@ static void nla_panel_properties(const bContext *C, Panel *pa)
/* strip extents */
column = uiLayoutColumn(layout, TRUE);
- uiItemL(column, "Strip Extents:", ICON_NONE);
+ uiItemL(column, IFACE_("Strip Extents:"), ICON_NONE);
uiItemR(column, &strip_ptr, "frame_start", 0, NULL, ICON_NONE);
uiItemR(column, &strip_ptr, "frame_end", 0, NULL, ICON_NONE);
@@ -346,7 +346,7 @@ static void nla_panel_properties(const bContext *C, Panel *pa)
/* settings */
column = uiLayoutColumn(layout, TRUE);
uiLayoutSetActive(column, !(RNA_boolean_get(&strip_ptr, "use_animated_influence") || RNA_boolean_get(&strip_ptr, "use_animated_time")));
- uiItemL(column, "Playback Settings:", ICON_NONE);
+ uiItemL(column, IFACE_("Playback Settings:"), ICON_NONE);
uiItemR(column, &strip_ptr, "mute", 0, NULL, ICON_NONE);
uiItemR(column, &strip_ptr, "use_reverse", 0, NULL, ICON_NONE);
}
@@ -376,15 +376,15 @@ static void nla_panel_actclip(const bContext *C, Panel *pa)
/* action extents */
// XXX custom names were used here (to avoid the prefixes)... probably not necessary in future?
column = uiLayoutColumn(layout, TRUE);
- uiItemL(column, "Action Extents:", ICON_NONE);
- uiItemR(column, &strip_ptr, "action_frame_start", 0, "Start Frame", ICON_NONE);
- uiItemR(column, &strip_ptr, "action_frame_end", 0, "End Frame", ICON_NONE);
+ uiItemL(column, IFACE_("Action Extents:"), ICON_NONE);
+ uiItemR(column, &strip_ptr, "action_frame_start", 0, IFACE_("Start Frame"), ICON_NONE);
+ uiItemR(column, &strip_ptr, "action_frame_end", 0, IFACE_("End Frame"), ICON_NONE);
uiItemO(column, NULL, ICON_NONE, "NLA_OT_action_sync_length");
/* action usage */
column = uiLayoutColumn(layout, TRUE);
uiLayoutSetActive(column, RNA_boolean_get(&strip_ptr, "use_animated_time") == FALSE);
- uiItemL(column, "Playback Settings:", ICON_NONE);
+ uiItemL(column, IFACE_("Playback Settings:"), ICON_NONE);
uiItemR(column, &strip_ptr, "scale", 0, NULL, ICON_NONE);
uiItemR(column, &strip_ptr, "repeat", 0, NULL, ICON_NONE);
}
diff --git a/source/blender/editors/space_nla/nla_edit.c b/source/blender/editors/space_nla/nla_edit.c
index 3740c3fae5e..484eb47fa8e 100644
--- a/source/blender/editors/space_nla/nla_edit.c
+++ b/source/blender/editors/space_nla/nla_edit.c
@@ -44,6 +44,8 @@
#include "BLI_rand.h"
#include "BLI_utildefines.h"
+#include "BLF_translation.h"
+
#include "BKE_action.h"
#include "BKE_fcurve.h"
#include "BKE_nla.h"
@@ -1951,7 +1953,7 @@ static int nla_fmodifier_add_invoke(bContext *C, wmOperator *UNUSED(op), wmEvent
uiLayout *layout;
int i;
- pup = uiPupMenuBegin(C, "Add F-Modifier", ICON_NONE);
+ pup = uiPupMenuBegin(C, IFACE_("Add F-Modifier"), ICON_NONE);
layout = uiPupMenuLayout(pup);
/* start from 1 to skip the 'Invalid' modifier type */
diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.c
index b211bca4c0a..8a2e03f2660 100644
--- a/source/blender/editors/space_node/drawnode.c
+++ b/source/blender/editors/space_node/drawnode.c
@@ -1131,8 +1131,8 @@ static void node_draw_reroute(const bContext *C, ARegion *ar, SpaceNode *UNUSED(
/* skip if out of view */
if (node->totr.xmax < ar->v2d.cur.xmin || node->totr.xmin > ar->v2d.cur.xmax ||
- node->totr.ymax < ar->v2d.cur.ymin || node->totr.ymin > ar->v2d.cur.ymax) {
-
+ node->totr.ymax < ar->v2d.cur.ymin || node->totr.ymin > ar->v2d.cur.ymax)
+ {
uiEndBlock(C, node->block);
node->block = NULL;
return;
@@ -1608,12 +1608,15 @@ static void node_composit_buts_image_details(uiLayout *layout, bContext *C, Poin
node_composit_buts_image(layout, C, ptr);
+ uiItemR(layout, ptr, "use_straight_alpha_output", 0, NULL, 0);
+
if (!node->id)
return;
imaptr = RNA_pointer_get(ptr, "image");
uiTemplateColorspaceSettings(layout, &imaptr, "colorspace_settings");
+ uiItemR(layout, &imaptr, "alpha_mode", 0, NULL, 0);
}
static void node_composit_buts_renderlayers(uiLayout *layout, bContext *C, PointerRNA *ptr)
diff --git a/source/blender/editors/space_node/node_add.c b/source/blender/editors/space_node/node_add.c
index 22631568d03..4943bb45113 100644
--- a/source/blender/editors/space_node/node_add.c
+++ b/source/blender/editors/space_node/node_add.c
@@ -307,6 +307,7 @@ void NODE_OT_add_reroute(wmOperatorType *ot)
ot->name = "Add Reroute";
ot->idname = "NODE_OT_add_reroute";
+ ot->description = "Add a reroute node";
ot->invoke = WM_gesture_lines_invoke;
ot->modal = WM_gesture_lines_modal;
diff --git a/source/blender/editors/space_node/node_group.c b/source/blender/editors/space_node/node_group.c
index 943f12c4c54..7b7b98f132c 100644
--- a/source/blender/editors/space_node/node_group.c
+++ b/source/blender/editors/space_node/node_group.c
@@ -42,6 +42,8 @@
#include "BLI_rect.h"
#include "BLI_math.h"
+#include "BLF_translation.h"
+
#include "BKE_action.h"
#include "BKE_animsys.h"
#include "BKE_context.h"
@@ -789,7 +791,7 @@ static int node_group_separate_exec(bContext *C, wmOperator *op)
static int node_group_separate_invoke(bContext *C, wmOperator *UNUSED(op), wmEvent *UNUSED(event))
{
- uiPopupMenu *pup = uiPupMenuBegin(C, "Separate", ICON_NONE);
+ uiPopupMenu *pup = uiPupMenuBegin(C, CTX_IFACE_(BLF_I18NCONTEXT_OPERATOR_DEFAULT, "Separate"), ICON_NONE);
uiLayout *layout = uiPupMenuLayout(pup);
uiLayoutSetOperatorContext(layout, WM_OP_EXEC_DEFAULT);
@@ -1148,7 +1150,7 @@ static int node_group_make_invoke(bContext *C, wmOperator *UNUSED(op), wmEvent *
{
SpaceNode *snode = CTX_wm_space_node(C);
bNode *act = nodeGetActive(snode->edittree);
- uiPopupMenu *pup = uiPupMenuBegin(C, "Make Group", ICON_NONE);
+ uiPopupMenu *pup = uiPupMenuBegin(C, CTX_IFACE_(BLF_I18NCONTEXT_OPERATOR_DEFAULT, "Make Group"), ICON_NONE);
uiLayout *layout = uiPupMenuLayout(pup);
uiLayoutSetOperatorContext(layout, WM_OP_EXEC_DEFAULT);
diff --git a/source/blender/editors/space_outliner/outliner_tools.c b/source/blender/editors/space_outliner/outliner_tools.c
index 303782f3fbd..cd04c8c6bd1 100644
--- a/source/blender/editors/space_outliner/outliner_tools.c
+++ b/source/blender/editors/space_outliner/outliner_tools.c
@@ -389,7 +389,7 @@ static void group_linkobs2scene_cb(bContext *UNUSED(C), Scene *scene, TreeElemen
/* link to scene */
base = MEM_callocN(sizeof(Base), "add_base");
BLI_addhead(&scene->base, base);
- base->lay = (1 << 20) - 1; /*v3d->lay;*/ /* would be nice to use the 3d layer but the include's not here */
+ base->lay = gob->ob->lay;
gob->ob->flag |= SELECT;
base->flag = gob->ob->flag;
base->object = gob->ob;
diff --git a/source/blender/editors/space_sequencer/sequencer_add.c b/source/blender/editors/space_sequencer/sequencer_add.c
index 29a6a1f6d50..78a76532487 100644
--- a/source/blender/editors/space_sequencer/sequencer_add.c
+++ b/source/blender/editors/space_sequencer/sequencer_add.c
@@ -126,23 +126,50 @@ static void sequencer_generic_invoke_path__internal(bContext *C, wmOperator *op,
}
}
-static void sequencer_generic_invoke_xy__internal(bContext *C, wmOperator *op, wmEvent *event, int flag)
+static int sequencer_generic_invoke_xy_guess_channel(bContext *C, int type)
{
- View2D *v2d = UI_view2d_fromcontext(C);
-
- float mval_v2d[2];
+ Sequence *tgt = NULL;
+ Sequence *seq;
+ Scene *scene = CTX_data_scene(C);
+ Editing *ed = BKE_sequencer_editing_get(scene, TRUE);
+ int cfra = (int) CFRA;
+ int proximity = INT_MAX;
+
+ if (!ed || !ed->seqbasep) {
+ return 1;
+ }
+
+ for (seq = ed->seqbasep->first; seq; seq = seq->next) {
+ if ((type == -1 || seq->type == type) &&
+ (seq->enddisp < cfra) &&
+ (cfra - seq->enddisp < proximity))
+ {
+ tgt = seq;
+ proximity = cfra - seq->enddisp;
+ }
+ }
- UI_view2d_region_to_view(v2d, event->mval[0], event->mval[1], &mval_v2d[0], &mval_v2d[1]);
+ if (tgt) {
+ return tgt->machine;
+ }
+ return 1;
+}
+static void sequencer_generic_invoke_xy__internal(bContext *C, wmOperator *op, int flag, int type)
+{
+ Scene *scene = CTX_data_scene(C);
+
+ int cfra = (int) CFRA;
+
/* effect strips don't need a channel initialized from the mouse */
if (!(flag & SEQPROP_NOCHAN)) {
- RNA_int_set(op->ptr, "channel", (int)mval_v2d[1] + 0.5f);
+ RNA_int_set(op->ptr, "channel", sequencer_generic_invoke_xy_guess_channel(C, type));
}
- RNA_int_set(op->ptr, "frame_start", (int)mval_v2d[0]);
+ RNA_int_set(op->ptr, "frame_start", cfra);
if ((flag & SEQPROP_ENDFRAME) && RNA_struct_property_is_set(op->ptr, "frame_end") == 0)
- RNA_int_set(op->ptr, "frame_end", (int)mval_v2d[0] + 25); // XXX arbitary but ok for now.
+ RNA_int_set(op->ptr, "frame_end", cfra + 25); // XXX arbitary but ok for now.
if (!(flag & SEQPROP_NOPATHS)) {
sequencer_generic_invoke_path__internal(C, op, "filepath");
@@ -277,7 +304,7 @@ static int sequencer_add_scene_strip_invoke(bContext *C, wmOperator *op, wmEvent
if (!RNA_struct_property_is_set(op->ptr, "scene"))
return WM_enum_search_invoke(C, op, event);
- sequencer_generic_invoke_xy__internal(C, op, event, 0);
+ sequencer_generic_invoke_xy__internal(C, op, 0, SEQ_TYPE_SCENE);
return sequencer_add_scene_strip_exec(C, op);
// needs a menu
// return WM_menu_invoke(C, op, event);
@@ -375,7 +402,7 @@ static int sequencer_add_movieclip_strip_invoke(bContext *C, wmOperator *op, wmE
if (!RNA_struct_property_is_set(op->ptr, "clip"))
return WM_enum_search_invoke(C, op, event);
- sequencer_generic_invoke_xy__internal(C, op, event, 0);
+ sequencer_generic_invoke_xy__internal(C, op, 0, SEQ_TYPE_MOVIECLIP);
return sequencer_add_movieclip_strip_exec(C, op);
// needs a menu
// return WM_menu_invoke(C, op, event);
@@ -472,7 +499,7 @@ static int sequencer_add_mask_strip_invoke(bContext *C, wmOperator *op, wmEvent
if (!RNA_struct_property_is_set(op->ptr, "mask"))
return WM_enum_search_invoke(C, op, event);
- sequencer_generic_invoke_xy__internal(C, op, event, 0);
+ sequencer_generic_invoke_xy__internal(C, op, 0, SEQ_TYPE_MASK);
return sequencer_add_mask_strip_exec(C, op);
// needs a menu
// return WM_menu_invoke(C, op, event);
@@ -575,7 +602,7 @@ static int sequencer_add_movie_strip_exec(bContext *C, wmOperator *op)
}
-static int sequencer_add_movie_strip_invoke(bContext *C, wmOperator *op, wmEvent *event)
+static int sequencer_add_movie_strip_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
{
if (!ED_operator_sequencer_active(C)) {
@@ -587,11 +614,11 @@ static int sequencer_add_movie_strip_invoke(bContext *C, wmOperator *op, wmEvent
if ((RNA_struct_property_is_set(op->ptr, "files") && RNA_collection_length(op->ptr, "files")) ||
RNA_struct_property_is_set(op->ptr, "filepath"))
{
- sequencer_generic_invoke_xy__internal(C, op, event, SEQPROP_NOPATHS);
+ sequencer_generic_invoke_xy__internal(C, op, SEQPROP_NOPATHS, SEQ_TYPE_MOVIE);
return sequencer_add_movie_strip_exec(C, op);
}
- sequencer_generic_invoke_xy__internal(C, op, event, 0);
+ sequencer_generic_invoke_xy__internal(C, op, 0, SEQ_TYPE_MOVIE);
WM_event_add_fileselect(C, op);
return OPERATOR_RUNNING_MODAL;
@@ -630,7 +657,7 @@ static int sequencer_add_sound_strip_exec(bContext *C, wmOperator *op)
return sequencer_add_generic_strip_exec(C, op, BKE_sequencer_add_sound_strip);
}
-static int sequencer_add_sound_strip_invoke(bContext *C, wmOperator *op, wmEvent *event)
+static int sequencer_add_sound_strip_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
{
if (!ED_operator_sequencer_active(C)) {
@@ -642,11 +669,11 @@ static int sequencer_add_sound_strip_invoke(bContext *C, wmOperator *op, wmEvent
if ((RNA_struct_property_is_set(op->ptr, "files") && RNA_collection_length(op->ptr, "files")) ||
RNA_struct_property_is_set(op->ptr, "filepath"))
{
- sequencer_generic_invoke_xy__internal(C, op, event, SEQPROP_NOPATHS);
+ sequencer_generic_invoke_xy__internal(C, op, SEQPROP_NOPATHS, SEQ_TYPE_SOUND_RAM);
return sequencer_add_sound_strip_exec(C, op);
}
- sequencer_generic_invoke_xy__internal(C, op, event, 0);
+ sequencer_generic_invoke_xy__internal(C, op, 0, SEQ_TYPE_SOUND_RAM);
WM_event_add_fileselect(C, op);
return OPERATOR_RUNNING_MODAL;
@@ -742,7 +769,7 @@ static int sequencer_add_image_strip_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
-static int sequencer_add_image_strip_invoke(bContext *C, wmOperator *op, wmEvent *event)
+static int sequencer_add_image_strip_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
{
if (!ED_operator_sequencer_active(C)) {
@@ -753,11 +780,11 @@ static int sequencer_add_image_strip_invoke(bContext *C, wmOperator *op, wmEvent
/* drag drop has set the names */
if (RNA_struct_property_is_set(op->ptr, "files") && RNA_collection_length(op->ptr, "files")) {
- sequencer_generic_invoke_xy__internal(C, op, event, SEQPROP_ENDFRAME | SEQPROP_NOPATHS);
+ sequencer_generic_invoke_xy__internal(C, op, SEQPROP_ENDFRAME | SEQPROP_NOPATHS, SEQ_TYPE_IMAGE);
return sequencer_add_image_strip_exec(C, op);
}
- sequencer_generic_invoke_xy__internal(C, op, event, SEQPROP_ENDFRAME);
+ sequencer_generic_invoke_xy__internal(C, op, SEQPROP_ENDFRAME, SEQ_TYPE_IMAGE);
WM_event_add_fileselect(C, op);
return OPERATOR_RUNNING_MODAL;
@@ -894,7 +921,7 @@ static int sequencer_add_effect_strip_exec(bContext *C, wmOperator *op)
/* add color */
-static int sequencer_add_effect_strip_invoke(bContext *C, wmOperator *op, wmEvent *event)
+static int sequencer_add_effect_strip_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
{
short is_type_set = RNA_struct_property_is_set(op->ptr, "type");
int type = -1;
@@ -917,7 +944,7 @@ static int sequencer_add_effect_strip_invoke(bContext *C, wmOperator *op, wmEven
}
}
- sequencer_generic_invoke_xy__internal(C, op, event, prop_flag);
+ sequencer_generic_invoke_xy__internal(C, op, prop_flag, type);
return sequencer_add_effect_strip_exec(C, op);
}
diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c
index 249ba986fd3..396878cbfeb 100644
--- a/source/blender/editors/space_sequencer/sequencer_draw.c
+++ b/source/blender/editors/space_sequencer/sequencer_draw.c
@@ -1059,8 +1059,10 @@ void draw_image_seq(const bContext *C, Scene *scene, ARegion *ar, SpaceSeq *sseq
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, ibuf->x, ibuf->y, 0, GL_RGBA, GL_UNSIGNED_BYTE, display_buffer);
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ if (sseq->flag & SEQ_USE_ALPHA) {
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ }
glBegin(GL_QUADS);
@@ -1093,7 +1095,8 @@ void draw_image_seq(const bContext *C, Scene *scene, ARegion *ar, SpaceSeq *sseq
glEnd();
glBindTexture(GL_TEXTURE_2D, last_texid);
glDisable(GL_TEXTURE_2D);
- glDisable(GL_BLEND);
+ if (sseq->flag & SEQ_USE_ALPHA)
+ glDisable(GL_BLEND);
glDeleteTextures(1, &texid);
if (sseq->mainb == SEQ_DRAW_IMG_IMBUF) {
diff --git a/source/blender/editors/space_sequencer/space_sequencer.c b/source/blender/editors/space_sequencer/space_sequencer.c
index d541e1d6c07..4c6b909882c 100644
--- a/source/blender/editors/space_sequencer/space_sequencer.c
+++ b/source/blender/editors/space_sequencer/space_sequencer.c
@@ -120,7 +120,7 @@ static SpaceLink *sequencer_new(const bContext *C)
sseq->chanshown = 0;
sseq->view = SEQ_VIEW_SEQUENCE;
sseq->mainb = SEQ_DRAW_IMG_IMBUF;
- sseq->flag = SEQ_SHOW_GPENCIL;
+ sseq->flag = SEQ_SHOW_GPENCIL | SEQ_USE_ALPHA;
/* header */
ar = MEM_callocN(sizeof(ARegion), "header for sequencer");
diff --git a/source/blender/editors/space_text/text_format.c b/source/blender/editors/space_text/text_format.c
index a8c2de193c4..3c7897200ed 100644
--- a/source/blender/editors/space_text/text_format.c
+++ b/source/blender/editors/space_text/text_format.c
@@ -157,7 +157,7 @@ void text_format_fill(const char **str_p, char **fmt_p, const char type, const i
*fmt++ = type;
str += size;
- i += size;
+ i += 1;
}
str--;
diff --git a/source/blender/editors/space_text/text_header.c b/source/blender/editors/space_text/text_header.c
index 52253b2d5d2..605a08e587a 100644
--- a/source/blender/editors/space_text/text_header.c
+++ b/source/blender/editors/space_text/text_header.c
@@ -42,6 +42,8 @@
#include "BLI_blenlib.h"
+#include "BLF_translation.h"
+
#include "BKE_context.h"
#include "BKE_screen.h"
@@ -120,7 +122,7 @@ void TEXT_OT_properties(wmOperatorType *ot)
uiPopupMenu *pup;
if (text) {
- pup = uiPupMenuBegin(C, "Text", ICON_NONE);
+ pup = uiPupMenuBegin(C, IFACE_("Text"), ICON_NONE);
if (txt_has_sel(text)) {
uiItemO(layout, NULL, ICON_NONE, "TEXT_OT_cut");
uiItemO(layout, NULL, ICON_NONE, "TEXT_OT_copy");
@@ -134,7 +136,7 @@ void TEXT_OT_properties(wmOperatorType *ot)
uiPupMenuEnd(C, pup);
}
else {
- pup = uiPupMenuBegin(C, "File", ICON_NONE);
+ pup = uiPupMenuBegin(C, IFACE_("File"), ICON_NONE);
uiItemO(layout, NULL, ICON_NONE, "TEXT_OT_new");
uiItemO(layout, NULL, ICON_NONE, "TEXT_OT_open");
uiPupMenuEnd(C, pup);
@@ -146,7 +148,7 @@ void TEXT_OT_properties(wmOperatorType *ot)
uiPopupMenu *pup;
- pup = uiPupMenuBegin(C, "Edit", ICON_NONE);
+ pup = uiPupMenuBegin(C, IFACE_("Edit"), ICON_NONE);
uiItemO(layout, NULL, ICON_NONE, "TEXT_OT_cut");
uiItemO(layout, NULL, ICON_NONE, "TEXT_OT_copy");
uiItemO(layout, NULL, ICON_NONE, "TEXT_OT_paste");
@@ -159,7 +161,7 @@ void TEXT_OT_properties(wmOperatorType *ot)
uiPopupMenu *pup;
if (text) {
- pup = uiPupMenuBegin(C, "Text", ICON_NONE);
+ pup = uiPupMenuBegin(C, IFACE_("Text"), ICON_NONE);
uiItemO(layout, NULL, ICON_NONE, "TEXT_OT_new");
uiItemO(layout, NULL, ICON_NONE, "TEXT_OT_open");
uiItemO(layout, NULL, ICON_NONE, "TEXT_OT_save");
@@ -168,7 +170,7 @@ void TEXT_OT_properties(wmOperatorType *ot)
uiPupMenuEnd(C, pup);
}
else {
- pup = uiPupMenuBegin(C, "File", ICON_NONE);
+ pup = uiPupMenuBegin(C, IFACE_("File"), ICON_NONE);
uiItemO(layout, NULL, ICON_NONE, "TEXT_OT_new");
uiItemO(layout, NULL, ICON_NONE, "TEXT_OT_open");
uiPupMenuEnd(C, pup);
@@ -180,11 +182,14 @@ void TEXT_OT_properties(wmOperatorType *ot)
uiPopupMenu *pup;
- pup = uiPupMenuBegin(C, "Text", ICON_NONE);
- uiItemEnumO(layout, "TEXT_OT_move", "Top of File", 0, "type", FILE_TOP);
- uiItemEnumO(layout, "TEXT_OT_move", "Bottom of File", 0, "type", FILE_BOTTOM);
- uiItemEnumO(layout, "TEXT_OT_move", "Page Up", 0, "type", PREV_PAGE);
- uiItemEnumO(layout, "TEXT_OT_move", "Page Down", 0, "type", NEXT_PAGE);
+ pup = uiPupMenuBegin(C, IFACE_("Text"), ICON_NONE);
+ uiItemEnumO(layout, "TEXT_OT_move", CTX_IFACE_(BLF_I18NCONTEXT_OPERATOR_DEFAULT, "Top of File"),
+ 0, "type", FILE_TOP);
+ uiItemEnumO(layout, "TEXT_OT_move", CTX_IFACE_(BLF_I18NCONTEXT_OPERATOR_DEFAULT, "Bottom of File"),
+ 0, "type", FILE_BOTTOM);
+ uiItemEnumO(layout, "TEXT_OT_move", CTX_IFACE_(BLF_I18NCONTEXT_OPERATOR_DEFAULT, "Page Up"), 0, "type", PREV_PAGE);
+ uiItemEnumO(layout, "TEXT_OT_move", CTX_IFACE_(BLF_I18NCONTEXT_OPERATOR_DEFAULT, "Page Down"),
+ 0, "type", NEXT_PAGE);
uiPupMenuEnd(C, pup);
}
#endif
diff --git a/source/blender/editors/space_text/text_ops.c b/source/blender/editors/space_text/text_ops.c
index 9b5d453d8a3..b45961bff11 100644
--- a/source/blender/editors/space_text/text_ops.c
+++ b/source/blender/editors/space_text/text_ops.c
@@ -3110,27 +3110,31 @@ static int text_resolve_conflict_invoke(bContext *C, wmOperator *op, wmEvent *UN
case 1:
if (text->flags & TXT_ISDIRTY) {
/* modified locally and externally, ahhh. offer more possibilites. */
- pup = uiPupMenuBegin(C, "File Modified Outside and Inside Blender", ICON_NONE);
+ pup = uiPupMenuBegin(C, IFACE_("File Modified Outside and Inside Blender"), ICON_NONE);
layout = uiPupMenuLayout(pup);
- uiItemEnumO_ptr(layout, op->type, "Reload from disk (ignore local changes)", 0, "resolution", RESOLVE_RELOAD);
- uiItemEnumO_ptr(layout, op->type, "Save to disk (ignore outside changes)", 0, "resolution", RESOLVE_SAVE);
- uiItemEnumO_ptr(layout, op->type, "Make text internal (separate copy)", 0, "resolution", RESOLVE_MAKE_INTERNAL);
+ uiItemEnumO_ptr(layout, op->type, IFACE_("Reload from disk (ignore local changes)"),
+ 0, "resolution", RESOLVE_RELOAD);
+ uiItemEnumO_ptr(layout, op->type, IFACE_("Save to disk (ignore outside changes)"),
+ 0, "resolution", RESOLVE_SAVE);
+ uiItemEnumO_ptr(layout, op->type, IFACE_("Make text internal (separate copy)"),
+ 0, "resolution", RESOLVE_MAKE_INTERNAL);
uiPupMenuEnd(C, pup);
}
else {
- pup = uiPupMenuBegin(C, "File Modified Outside Blender", ICON_NONE);
+ pup = uiPupMenuBegin(C, IFACE_("File Modified Outside Blender"), ICON_NONE);
layout = uiPupMenuLayout(pup);
- uiItemEnumO_ptr(layout, op->type, "Reload from disk", 0, "resolution", RESOLVE_RELOAD);
- uiItemEnumO_ptr(layout, op->type, "Make text internal (separate copy)", 0, "resolution", RESOLVE_MAKE_INTERNAL);
- uiItemEnumO_ptr(layout, op->type, "Ignore", 0, "resolution", RESOLVE_IGNORE);
+ uiItemEnumO_ptr(layout, op->type, IFACE_("Reload from disk"), 0, "resolution", RESOLVE_RELOAD);
+ uiItemEnumO_ptr(layout, op->type, IFACE_("Make text internal (separate copy)"),
+ 0, "resolution", RESOLVE_MAKE_INTERNAL);
+ uiItemEnumO_ptr(layout, op->type, IFACE_("Ignore"), 0, "resolution", RESOLVE_IGNORE);
uiPupMenuEnd(C, pup);
}
break;
case 2:
- pup = uiPupMenuBegin(C, "File Deleted Outside Blender", ICON_NONE);
+ pup = uiPupMenuBegin(C, IFACE_("File Deleted Outside Blender"), ICON_NONE);
layout = uiPupMenuLayout(pup);
- uiItemEnumO_ptr(layout, op->type, "Make text internal", 0, "resolution", RESOLVE_MAKE_INTERNAL);
- uiItemEnumO_ptr(layout, op->type, "Recreate file", 0, "resolution", RESOLVE_SAVE);
+ uiItemEnumO_ptr(layout, op->type, IFACE_("Make text internal"), 0, "resolution", RESOLVE_MAKE_INTERNAL);
+ uiItemEnumO_ptr(layout, op->type, IFACE_("Recreate file"), 0, "resolution", RESOLVE_SAVE);
uiPupMenuEnd(C, pup);
break;
}
diff --git a/source/blender/editors/space_view3d/drawobject.c b/source/blender/editors/space_view3d/drawobject.c
index fc8d5d26455..32d961ab64c 100644
--- a/source/blender/editors/space_view3d/drawobject.c
+++ b/source/blender/editors/space_view3d/drawobject.c
@@ -623,16 +623,17 @@ static void draw_empty_image(Object *ob, const short dflag, const unsigned char
if ((dflag & DRAW_CONSTCOLOR) == 0) {
glColor3ubv(ob_wire_col);
-
- /* Calculate the outline vertex positions */
- glBegin(GL_LINE_LOOP);
- glVertex2f(ofs_x, ofs_y);
- glVertex2f(ofs_x + ima_x, ofs_y);
- glVertex2f(ofs_x + ima_x, ofs_y + ima_y);
- glVertex2f(ofs_x, ofs_y + ima_y);
- glEnd();
}
+ /* Calculate the outline vertex positions */
+ glBegin(GL_LINE_LOOP);
+ glVertex2f(ofs_x, ofs_y);
+ glVertex2f(ofs_x + ima_x, ofs_y);
+ glVertex2f(ofs_x + ima_x, ofs_y + ima_y);
+ glVertex2f(ofs_x, ofs_y + ima_y);
+ glEnd();
+
+
/* Reset GL settings */
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c
index 658196a1bd4..57755231240 100644
--- a/source/blender/editors/space_view3d/space_view3d.c
+++ b/source/blender/editors/space_view3d/space_view3d.c
@@ -513,20 +513,34 @@ static int view3d_ima_drop_poll(bContext *UNUSED(C), wmDrag *drag, wmEvent *UNUS
return 0;
}
-
static int view3d_ima_bg_drop_poll(bContext *C, wmDrag *drag, wmEvent *event)
{
- if (ED_view3d_give_base_under_cursor(C, event->mval) ) {
- return 0;
+ if (event->ctrl)
+ return false;
+
+ if (!ED_view3d_give_base_under_cursor(C, event->mval)) {
+ return view3d_ima_drop_poll(C, drag, event);
}
- return view3d_ima_drop_poll(C, drag, event);
+ return 0;
}
-static int view3d_ima_ob_drop_poll(bContext *C, wmDrag *drag, wmEvent *event)
+static int view3d_ima_empty_drop_poll(bContext *C, wmDrag *drag, wmEvent *event)
{
- if (ED_view3d_give_base_under_cursor(C, event->mval) ) {
+ Base *base = ED_view3d_give_base_under_cursor(C, event->mval);
+
+ /* either holding and ctrl and no object, or dropping to empty */
+ if ((event->ctrl && !base) || (base && base->object->type == OB_EMPTY))
+ return view3d_ima_drop_poll(C, drag, event);
+
+ return 0;
+}
+
+static int view3d_ima_mesh_drop_poll(bContext *C, wmDrag *drag, wmEvent *event)
+{
+ Base *base = ED_view3d_give_base_under_cursor(C, event->mval);
+
+ if (base && base->object->type == OB_MESH)
return view3d_ima_drop_poll(C, drag, event);
- }
return 0;
}
@@ -570,7 +584,8 @@ static void view3d_dropboxes(void)
WM_dropbox_add(lb, "OBJECT_OT_add_named", view3d_ob_drop_poll, view3d_ob_drop_copy);
WM_dropbox_add(lb, "OBJECT_OT_drop_named_material", view3d_mat_drop_poll, view3d_id_drop_copy);
- WM_dropbox_add(lb, "MESH_OT_drop_named_image", view3d_ima_ob_drop_poll, view3d_id_path_drop_copy);
+ WM_dropbox_add(lb, "MESH_OT_drop_named_image", view3d_ima_mesh_drop_poll, view3d_id_path_drop_copy);
+ WM_dropbox_add(lb, "OBJECT_OT_drop_named_image", view3d_ima_empty_drop_poll, view3d_id_path_drop_copy);
WM_dropbox_add(lb, "VIEW3D_OT_background_image_add", view3d_ima_bg_drop_poll, view3d_id_path_drop_copy);
WM_dropbox_add(lb, "OBJECT_OT_group_instance_add", view3d_group_drop_poll, view3d_group_drop_copy);
}
diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c
index 30ed7a57f9d..5f54f0dcfc8 100644
--- a/source/blender/editors/space_view3d/view3d_edit.c
+++ b/source/blender/editors/space_view3d/view3d_edit.c
@@ -996,12 +996,12 @@ void VIEW3D_OT_rotate(wmOperatorType *ot)
/* NDOF utility functions
* (should these functions live in this file?)
*/
-float ndof_to_axis_angle(struct wmNDOFMotionData *ndof, float axis[3])
+float ndof_to_axis_angle(const struct wmNDOFMotionData *ndof, float axis[3])
{
return ndof->dt * normalize_v3_v3(axis, ndof->rvec);
}
-void ndof_to_quat(struct wmNDOFMotionData *ndof, float q[4])
+void ndof_to_quat(const struct wmNDOFMotionData *ndof, float q[4])
{
float axis[3];
float angle;
@@ -1010,6 +1010,84 @@ void ndof_to_quat(struct wmNDOFMotionData *ndof, float q[4])
axis_angle_to_quat(q, axis, angle);
}
+static void view3d_ndof_orbit(const struct wmNDOFMotionData *ndof, RegionView3D *rv3d, const float view_inv[4],
+ const float rot_sensitivity, const float dt,
+ /* optional, can be NULL*/
+ ViewOpsData *vod)
+{
+ if (U.ndof_flag & NDOF_TURNTABLE) {
+
+ /* turntable view code by John Aughey, adapted for 3D mouse by [mce] */
+ float angle, rot[4];
+ float xvec[3] = {1, 0, 0};
+
+ /* Determine the direction of the x vector (for rotating up and down) */
+ mul_qt_v3(view_inv, xvec);
+
+ /* Perform the up/down rotation */
+ angle = rot_sensitivity * dt * ndof->rx;
+ if (U.ndof_flag & NDOF_TILT_INVERT_AXIS)
+ angle = -angle;
+ rot[0] = cosf(angle);
+ mul_v3_v3fl(rot + 1, xvec, sin(angle));
+ mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, rot);
+
+ /* Perform the orbital rotation */
+ angle = rot_sensitivity * dt * ndof->ry;
+ if (U.ndof_flag & NDOF_ROTATE_INVERT_AXIS)
+ angle = -angle;
+
+ /* update the onscreen doo-dad */
+ rv3d->rot_angle = angle;
+ rv3d->rot_axis[0] = 0;
+ rv3d->rot_axis[1] = 0;
+ rv3d->rot_axis[2] = 1;
+
+ rot[0] = cosf(angle);
+ rot[1] = rot[2] = 0.0;
+ rot[3] = sinf(angle);
+ mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, rot);
+
+ }
+ else {
+ float rot[4];
+ float axis[3];
+ float angle = rot_sensitivity * ndof_to_axis_angle(ndof, axis);
+
+ if (U.ndof_flag & NDOF_ROLL_INVERT_AXIS) axis[2] = -axis[2];
+ if (U.ndof_flag & NDOF_TILT_INVERT_AXIS) axis[0] = -axis[0];
+ if (U.ndof_flag & NDOF_ROTATE_INVERT_AXIS) axis[1] = -axis[1];
+
+
+ /* transform rotation axis from view to world coordinates */
+ mul_qt_v3(view_inv, axis);
+
+ /* update the onscreen doo-dad */
+ rv3d->rot_angle = angle;
+ copy_v3_v3(rv3d->rot_axis, axis);
+
+ axis_angle_to_quat(rot, axis, angle);
+
+ /* apply rotation */
+ mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, rot);
+ }
+
+ /* rotate around custom center */
+ if (vod && vod->use_dyn_ofs) {
+ float q1[4];
+
+ /* compute the post multiplication quat, to rotate the offset correctly */
+ conjugate_qt_qt(q1, vod->oldquat);
+ mul_qt_qtqt(q1, q1, rv3d->viewquat);
+
+ conjugate_qt(q1); /* conj == inv for unit quat */
+ copy_v3_v3(rv3d->ofs, vod->ofs);
+ sub_v3_v3(rv3d->ofs, vod->dyn_ofs);
+ mul_qt_v3(q1, rv3d->ofs);
+ add_v3_v3(rv3d->ofs, vod->dyn_ofs);
+ }
+}
+
/* -- "orbit" navigation (trackball/turntable)
* -- zooming
* -- panning in rotationally-locked views
@@ -1065,86 +1143,8 @@ static int ndof_orbit_invoke(bContext *C, wmOperator *op, wmEvent *event)
}
if (has_rotation) {
-
rv3d->view = RV3D_VIEW_USER;
-
- if (U.ndof_flag & NDOF_TURNTABLE) {
-
- /* turntable view code by John Aughey, adapted for 3D mouse by [mce] */
- float angle, rot[4];
- float xvec[3] = {1, 0, 0};
-
- /* Determine the direction of the x vector (for rotating up and down) */
- mul_qt_v3(view_inv, xvec);
-
- /* Perform the up/down rotation */
- angle = rot_sensitivity * dt * ndof->rx;
- if (U.ndof_flag & NDOF_TILT_INVERT_AXIS)
- angle = -angle;
- rot[0] = cos(angle);
- mul_v3_v3fl(rot + 1, xvec, sin(angle));
- mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, rot);
-
- /* Perform the orbital rotation */
- angle = rot_sensitivity * dt * ndof->ry;
- if (U.ndof_flag & NDOF_ROTATE_INVERT_AXIS)
- angle = -angle;
-
- /* update the onscreen doo-dad */
- rv3d->rot_angle = angle;
- rv3d->rot_axis[0] = 0;
- rv3d->rot_axis[1] = 0;
- rv3d->rot_axis[2] = 1;
-
- rot[0] = cos(angle);
- rot[1] = rot[2] = 0.0;
- rot[3] = sin(angle);
- mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, rot);
-
- }
- else {
- float rot[4];
- float axis[3];
- float angle = rot_sensitivity * ndof_to_axis_angle(ndof, axis);
-
- if (U.ndof_flag & NDOF_ROLL_INVERT_AXIS)
- axis[2] = -axis[2];
-
- if (U.ndof_flag & NDOF_TILT_INVERT_AXIS)
- axis[0] = -axis[0];
-
- if (U.ndof_flag & NDOF_ROTATE_INVERT_AXIS)
- axis[1] = -axis[1];
-
-
- /* transform rotation axis from view to world coordinates */
- mul_qt_v3(view_inv, axis);
-
- /* update the onscreen doo-dad */
- rv3d->rot_angle = angle;
- copy_v3_v3(rv3d->rot_axis, axis);
-
- axis_angle_to_quat(rot, axis, angle);
-
- /* apply rotation */
- mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, rot);
-
- }
-
- /* rotate around custom center */
- if (vod && vod->use_dyn_ofs) {
- float q1[4];
-
- /* compute the post multiplication quat, to rotate the offset correctly */
- conjugate_qt_qt(q1, vod->oldquat);
- mul_qt_qtqt(q1, q1, rv3d->viewquat);
-
- conjugate_qt(q1); /* conj == inv for unit quat */
- copy_v3_v3(rv3d->ofs, vod->ofs);
- sub_v3_v3(rv3d->ofs, vod->dyn_ofs);
- mul_qt_v3(q1, rv3d->ofs);
- add_v3_v3(rv3d->ofs, vod->dyn_ofs);
- }
+ view3d_ndof_orbit(ndof, rv3d, view_inv, rot_sensitivity, dt, vod);
}
}
@@ -1239,86 +1239,8 @@ static int ndof_orbit_zoom_invoke(bContext *C, wmOperator *op, wmEvent *event)
}
if (has_rotation) {
-
rv3d->view = RV3D_VIEW_USER;
-
- if (U.ndof_flag & NDOF_TURNTABLE) {
-
- /* turntable view code by John Aughey, adapted for 3D mouse by [mce] */
- float angle, rot[4];
- float xvec[3] = {1, 0, 0};
-
- /* Determine the direction of the x vector (for rotating up and down) */
- mul_qt_v3(view_inv, xvec);
-
- /* Perform the up/down rotation */
- angle = rot_sensitivity * dt * ndof->rx;
- if (U.ndof_flag & NDOF_TILT_INVERT_AXIS)
- angle = -angle;
- rot[0] = cos(angle);
- mul_v3_v3fl(rot + 1, xvec, sin(angle));
- mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, rot);
-
- /* Perform the orbital rotation */
- angle = rot_sensitivity * dt * ndof->ry;
- if (U.ndof_flag & NDOF_ROTATE_INVERT_AXIS)
- angle = -angle;
-
- /* update the onscreen doo-dad */
- rv3d->rot_angle = angle;
- rv3d->rot_axis[0] = 0;
- rv3d->rot_axis[1] = 0;
- rv3d->rot_axis[2] = 1;
-
- rot[0] = cos(angle);
- rot[1] = rot[2] = 0.0;
- rot[3] = sin(angle);
- mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, rot);
-
- }
- else {
- float rot[4];
- float axis[3];
- float angle = rot_sensitivity * ndof_to_axis_angle(ndof, axis);
-
- if (U.ndof_flag & NDOF_ROLL_INVERT_AXIS)
- axis[2] = -axis[2];
-
- if (U.ndof_flag & NDOF_TILT_INVERT_AXIS)
- axis[0] = -axis[0];
-
- if (U.ndof_flag & NDOF_ROTATE_INVERT_AXIS)
- axis[1] = -axis[1];
-
-
- /* transform rotation axis from view to world coordinates */
- mul_qt_v3(view_inv, axis);
-
- /* update the onscreen doo-dad */
- rv3d->rot_angle = angle;
- copy_v3_v3(rv3d->rot_axis, axis);
-
- axis_angle_to_quat(rot, axis, angle);
-
- /* apply rotation */
- mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, rot);
-
- }
-
- /* rotate around custom center */
- if (vod && vod->use_dyn_ofs) {
- float q1[4];
-
- /* compute the post multiplication quat, to rotate the offset correctly */
- conjugate_qt_qt(q1, vod->oldquat);
- mul_qt_qtqt(q1, q1, rv3d->viewquat);
-
- conjugate_qt(q1); /* conj == inv for unit quat */
- copy_v3_v3(rv3d->ofs, vod->ofs);
- sub_v3_v3(rv3d->ofs, vod->dyn_ofs);
- mul_qt_v3(q1, rv3d->ofs);
- add_v3_v3(rv3d->ofs, vod->dyn_ofs);
- }
+ view3d_ndof_orbit(ndof, rv3d, view_inv, rot_sensitivity, dt, vod);
}
}
@@ -1502,84 +1424,8 @@ static int ndof_all_invoke(bContext *C, wmOperator *op, wmEvent *event)
/* move center of view opposite of hand motion (this is camera mode, not object mode) */
sub_v3_v3(rv3d->ofs, pan_vec);
-
- if (U.ndof_flag & NDOF_TURNTABLE) {
- /* turntable view code by John Aughey, adapted for 3D mouse by [mce] */
- float angle, rot[4];
- float xvec[3] = {1, 0, 0};
-
- /* Determine the direction of the x vector (for rotating up and down) */
- mul_qt_v3(view_inv, xvec);
-
- /* Perform the up/down rotation */
- angle = rot_sensitivity * dt * ndof->rx;
- if (U.ndof_flag & NDOF_TILT_INVERT_AXIS)
- angle = -angle;
- rot[0] = cos(angle);
- mul_v3_v3fl(rot + 1, xvec, sin(angle));
- mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, rot);
-
- /* Perform the orbital rotation */
- angle = rot_sensitivity * dt * ndof->ry;
- if (U.ndof_flag & NDOF_ROTATE_INVERT_AXIS)
- angle = -angle;
-
- /* update the onscreen doo-dad */
- rv3d->rot_angle = angle;
- rv3d->rot_axis[0] = 0;
- rv3d->rot_axis[1] = 0;
- rv3d->rot_axis[2] = 1;
-
- rot[0] = cos(angle);
- rot[1] = rot[2] = 0.0;
- rot[3] = sin(angle);
- mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, rot);
-
- }
- else {
-
- float rot[4];
- float axis[3];
- float angle = rot_sensitivity * ndof_to_axis_angle(ndof, axis);
-
- if (U.ndof_flag & NDOF_ROLL_INVERT_AXIS)
- axis[2] = -axis[2];
-
- if (U.ndof_flag & NDOF_TILT_INVERT_AXIS)
- axis[0] = -axis[0];
-
- if (U.ndof_flag & NDOF_ROTATE_INVERT_AXIS)
- axis[1] = -axis[1];
-
- /* transform rotation axis from view to world coordinates */
- mul_qt_v3(view_inv, axis);
-
- /* update the onscreen doo-dad */
- rv3d->rot_angle = angle;
- copy_v3_v3(rv3d->rot_axis, axis);
-
- axis_angle_to_quat(rot, axis, angle);
-
- /* apply rotation */
- mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, rot);
-
- }
-
- /* rotate around custom center */
- if (vod->use_dyn_ofs) {
- float q1[4];
-
- /* compute the post multiplication quat, to rotate the offset correctly */
- conjugate_qt_qt(q1, vod->oldquat);
- mul_qt_qtqt(q1, q1, rv3d->viewquat);
-
- conjugate_qt(q1); /* conj == inv for unit quat */
- copy_v3_v3(rv3d->ofs, vod->ofs);
- sub_v3_v3(rv3d->ofs, vod->dyn_ofs);
- mul_qt_v3(q1, rv3d->ofs);
- add_v3_v3(rv3d->ofs, vod->dyn_ofs);
- }
+ view3d_ndof_orbit(ndof, rv3d, view_inv, rot_sensitivity, dt, vod);
}
viewops_data_free(C, op);
@@ -3699,7 +3545,9 @@ static int background_image_add_invoke(bContext *C, wmOperator *op, wmEvent *UNU
void VIEW3D_OT_background_image_add(wmOperatorType *ot)
{
/* identifiers */
- ot->name = "Add Background Image";
+ /* note: having key shortcut here is bad practice,
+ * but for now keep because this displays when dragging an image over the 3D viewport */
+ ot->name = "Add Background Image (Ctrl for Empty Object)";
ot->description = "Add a new background image";
ot->idname = "VIEW3D_OT_background_image_add";
diff --git a/source/blender/editors/space_view3d/view3d_header.c b/source/blender/editors/space_view3d/view3d_header.c
index 2b30e4e6b81..e078fa8eda1 100644
--- a/source/blender/editors/space_view3d/view3d_header.c
+++ b/source/blender/editors/space_view3d/view3d_header.c
@@ -400,13 +400,13 @@ void uiTemplateEditModeSelection(uiLayout *layout, struct bContext *C)
block = uiLayoutGetBlock(row);
uiDefIconButBitS(block, TOG, SCE_SELECT_VERTEX, B_SEL_VERT, ICON_VERTEXSEL,
0, 0, UI_UNIT_X, UI_UNIT_Y, &em->selectmode, 1.0, 0.0, 0, 0,
- "Vertex select - Shift-Click for multiple modes");
+ TIP_("Vertex select - Shift-Click for multiple modes"));
uiDefIconButBitS(block, TOG, SCE_SELECT_EDGE, B_SEL_EDGE, ICON_EDGESEL,
0, 0, UI_UNIT_X, UI_UNIT_Y, &em->selectmode, 1.0, 0.0, 0, 0,
- "Edge select - Shift-Click for multiple modes, Ctrl-Click expands selection");
+ TIP_("Edge select - Shift-Click for multiple modes, Ctrl-Click expands selection"));
uiDefIconButBitS(block, TOG, SCE_SELECT_FACE, B_SEL_FACE, ICON_FACESEL,
0, 0, UI_UNIT_X, UI_UNIT_Y, &em->selectmode, 1.0, 0.0, 0, 0,
- "Face select - Shift-Click for multiple modes, Ctrl-Click expands selection");
+ TIP_("Face select - Shift-Click for multiple modes, Ctrl-Click expands selection"));
}
}
@@ -453,6 +453,13 @@ void uiTemplateHeader3D(uiLayout *layout, struct bContext *C)
uiItemR(layout, &v3dptr, "viewport_shade", UI_ITEM_R_ICON_ONLY, "", ICON_NONE);
if (obedit == NULL && is_paint) {
+
+ if (ob->mode & OB_MODE_WEIGHT_PAINT) {
+ /* Only for Weight Paint. makes no sense in other paint modes. */
+ row = uiLayoutRow(layout, TRUE);
+ uiItemR(row, &v3dptr, "pivot_point", UI_ITEM_R_ICON_ONLY, "", ICON_NONE);
+ }
+
/* Manipulators aren't used in paint modes */
if (!ELEM(ob->mode, OB_MODE_SCULPT, OB_MODE_PARTICLE_EDIT)) {
/* masks aren't used for sculpt and particle painting */
@@ -476,7 +483,9 @@ void uiTemplateHeader3D(uiLayout *layout, struct bContext *C)
uiItemR(row, &v3dptr, "pivot_point", UI_ITEM_R_ICON_ONLY, "", ICON_NONE);
/* pose/object only however we want to allow in weight paint mode too
- * so don't be totally strict and just check not-editmode for now */
+ * so don't be totally strict and just check not-editmode for now
+ * XXX We never get here when we are in Weight Paint mode
+ */
if (obedit == NULL) {
uiItemR(row, &v3dptr, "use_pivot_point_align", UI_ITEM_R_ICON_ONLY, "", ICON_NONE);
}
diff --git a/source/blender/editors/space_view3d/view3d_intern.h b/source/blender/editors/space_view3d/view3d_intern.h
index c8f0fe44433..d4cfa9076a6 100644
--- a/source/blender/editors/space_view3d/view3d_intern.h
+++ b/source/blender/editors/space_view3d/view3d_intern.h
@@ -101,8 +101,8 @@ void VIEW3D_OT_clear_render_border(struct wmOperatorType *ot);
void VIEW3D_OT_zoom_border(struct wmOperatorType *ot);
void view3d_boxview_copy(ScrArea *sa, ARegion *ar);
-void ndof_to_quat(struct wmNDOFMotionData *ndof, float q[4]);
-float ndof_to_axis_angle(struct wmNDOFMotionData *ndof, float axis[3]);
+void ndof_to_quat(const struct wmNDOFMotionData *ndof, float q[4]);
+float ndof_to_axis_angle(const struct wmNDOFMotionData *ndof, float axis[3]);
/* view3d_fly.c */
void view3d_keymap(struct wmKeyConfig *keyconf);
diff --git a/source/blender/editors/space_view3d/view3d_project.c b/source/blender/editors/space_view3d/view3d_project.c
index c428cb02236..bc1656ff7c6 100644
--- a/source/blender/editors/space_view3d/view3d_project.c
+++ b/source/blender/editors/space_view3d/view3d_project.c
@@ -43,6 +43,7 @@
#include "ED_view3d.h" /* own include */
#define BL_NEAR_CLIP 0.001
+#define BL_ZERO_CLIP 0.001
/* Non Clipping Projection Functions
* ********************************* */
@@ -134,18 +135,25 @@ static eV3DProjStatus ed_view3d_project__internal(ARegion *ar,
vec4[3] = 1.0;
mul_m4_v4(perspmat, vec4);
- if (((flag & V3D_PROJ_TEST_CLIP_NEAR) == 0) || (vec4[3] > (float)BL_NEAR_CLIP)) {
- const float scalar = (vec4[3] != 0.0f) ? (1.0f / vec4[3]): 0.0f;
- const float fx = ((float)ar->winx / 2.0f) * (1.0f + (vec4[0] * scalar));
- if (((flag & V3D_PROJ_TEST_CLIP_WIN) == 0) || (fx > 0.0f && fx < (float)ar->winx)) {
- const float fy = ((float)ar->winy / 2.0f) * (1.0f + (vec4[1] * scalar));
- if (((flag & V3D_PROJ_TEST_CLIP_WIN) == 0) || (fy > 0.0f && fy < (float)ar->winy)) {
- r_co[0] = floorf(fx);
- r_co[1] = floorf(fy);
-
- /* check if the point is behind the view, we need to flip in this case */
- if (UNLIKELY((flag & V3D_PROJ_TEST_CLIP_NEAR) == 0) && (vec4[3] < 0.0f)) {
- negate_v2(r_co);
+
+
+ if (((flag & V3D_PROJ_TEST_CLIP_ZERO) == 0) || (fabsf(vec4[3]) > (float)BL_ZERO_CLIP)) {
+ if (((flag & V3D_PROJ_TEST_CLIP_NEAR) == 0) || (vec4[3] > (float)BL_NEAR_CLIP)) {
+ const float scalar = (vec4[3] != 0.0f) ? (1.0f / vec4[3]): 0.0f;
+ const float fx = ((float)ar->winx / 2.0f) * (1.0f + (vec4[0] * scalar));
+ if (((flag & V3D_PROJ_TEST_CLIP_WIN) == 0) || (fx > 0.0f && fx < (float)ar->winx)) {
+ const float fy = ((float)ar->winy / 2.0f) * (1.0f + (vec4[1] * scalar));
+ if (((flag & V3D_PROJ_TEST_CLIP_WIN) == 0) || (fy > 0.0f && fy < (float)ar->winy)) {
+ r_co[0] = floorf(fx);
+ r_co[1] = floorf(fy);
+
+ /* check if the point is behind the view, we need to flip in this case */
+ if (UNLIKELY((flag & V3D_PROJ_TEST_CLIP_NEAR) == 0) && (vec4[3] < 0.0f)) {
+ negate_v2(r_co);
+ }
+ }
+ else {
+ return V3D_PROJ_RET_CLIP_WIN;
}
}
else {
@@ -153,11 +161,11 @@ static eV3DProjStatus ed_view3d_project__internal(ARegion *ar,
}
}
else {
- return V3D_PROJ_RET_CLIP_WIN;
+ return V3D_PROJ_RET_CLIP_NEAR;
}
}
else {
- return V3D_PROJ_RET_CLIP_NEAR;
+ return V3D_PROJ_RET_CLIP_ZERO;
}
return V3D_PROJ_RET_OK;
diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c
index dac887f7f13..24260898066 100644
--- a/source/blender/editors/space_view3d/view3d_select.c
+++ b/source/blender/editors/space_view3d/view3d_select.c
@@ -617,7 +617,8 @@ static void do_lasso_select_lattice__doSelect(void *userData, BPoint *bp, const
LassoSelectUserData *data = userData;
if (BLI_rctf_isect_pt_v(data->rect_fl, screen_co) &&
- BLI_lasso_is_point_inside(data->mcords, data->moves, screen_co[0], screen_co[1], IS_CLIPPED)) {
+ BLI_lasso_is_point_inside(data->mcords, data->moves, screen_co[0], screen_co[1], IS_CLIPPED))
+ {
bp->f1 = data->select ? (bp->f1 | SELECT) : (bp->f1 & ~SELECT);
}
}
@@ -717,7 +718,8 @@ static void do_lasso_select_mball__doSelectElem(void *userData, struct MetaElem
LassoSelectUserData *data = userData;
if (BLI_rctf_isect_pt_v(data->rect_fl, screen_co) &&
- BLI_lasso_is_point_inside(data->mcords, data->moves, screen_co[0], screen_co[1], INT_MAX)) {
+ BLI_lasso_is_point_inside(data->mcords, data->moves, screen_co[0], screen_co[1], INT_MAX))
+ {
if (data->select) ml->flag |= SELECT;
else ml->flag &= ~SELECT;
data->is_change = TRUE;
diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c
index ea82ebb9234..c8874d13cac 100644
--- a/source/blender/editors/transform/transform.c
+++ b/source/blender/editors/transform/transform.c
@@ -239,11 +239,11 @@ void convertViewVec(TransInfo *t, float r_vec[3], int dx, int dy)
}
}
-void projectIntView(TransInfo *t, const float vec[3], int adr[2])
+void projectIntViewEx(TransInfo *t, const float vec[3], int adr[2], const eV3DProjTest flag)
{
if (t->spacetype == SPACE_VIEW3D) {
if (t->ar->regiontype == RGN_TYPE_WINDOW) {
- if (ED_view3d_project_int_global(t->ar, vec, adr, V3D_PROJ_TEST_NOP) != V3D_PROJ_RET_OK) {
+ if (ED_view3d_project_int_global(t->ar, vec, adr, flag) != V3D_PROJ_RET_OK) {
adr[0] = (int)2140000000.0f; /* this is what was done in 2.64, perhaps we can be smarter? */
adr[1] = (int)2140000000.0f;
}
@@ -361,15 +361,19 @@ void projectIntView(TransInfo *t, const float vec[3], int adr[2])
UI_view2d_to_region_no_clip((View2D *)t->view, vec[0], vec[1], adr, adr + 1);
}
}
+void projectIntView(TransInfo *t, const float vec[3], int adr[2])
+{
+ projectIntViewEx(t, vec, adr, V3D_PROJ_TEST_NOP);
+}
-void projectFloatView(TransInfo *t, const float vec[3], float adr[2])
+void projectFloatViewEx(TransInfo *t, const float vec[3], float adr[2], const eV3DProjTest flag)
{
switch (t->spacetype) {
case SPACE_VIEW3D:
{
if (t->ar->regiontype == RGN_TYPE_WINDOW) {
/* allow points behind the view [#33643] */
- if (ED_view3d_project_float_global(t->ar, vec, adr, V3D_PROJ_TEST_NOP) != V3D_PROJ_RET_OK) {
+ if (ED_view3d_project_float_global(t->ar, vec, adr, flag) != V3D_PROJ_RET_OK) {
/* XXX, 2.64 and prior did this, weak! */
adr[0] = t->ar->winx / 2.0f;
adr[1] = t->ar->winy / 2.0f;
@@ -393,6 +397,10 @@ void projectFloatView(TransInfo *t, const float vec[3], float adr[2])
zero_v2(adr);
}
+void projectFloatView(TransInfo *t, const float vec[3], float adr[2])
+{
+ projectFloatViewEx(t, vec, adr, V3D_PROJ_TEST_NOP);
+}
void applyAspectRatio(TransInfo *t, float vec[2])
{
@@ -1543,7 +1551,7 @@ static void drawHelpline(bContext *UNUSED(C), int x, int y, void *customdata)
if (ob) mul_m4_v3(ob->obmat, vecrot);
}
- projectFloatView(t, vecrot, cent); // no overflow in extreme cases
+ projectFloatViewEx(t, vecrot, cent, V3D_PROJ_TEST_CLIP_ZERO);
glPushMatrix();
@@ -5285,6 +5293,7 @@ static int createEdgeSlideVerts(TransInfo *t)
TransDataEdgeSlideVert *sv;
/* XXX, 'sv' will initialize multiple times, this is suspicious. see [#34024] */
+ BLI_assert(BLI_smallhash_haskey(&table, (uintptr_t)v) != false);
sv = sv_array + GET_INT_FROM_POINTER(BLI_smallhash_lookup(&table, (uintptr_t)v));
sv->v = v;
sv->origvert = *v;
@@ -5307,6 +5316,7 @@ static int createEdgeSlideVerts(TransInfo *t)
e1 = e;
e = get_other_edge(v, e);
if (!e) {
+ BLI_assert(BLI_smallhash_haskey(&table, (uintptr_t)v) != false);
sv = sv_array + GET_INT_FROM_POINTER(BLI_smallhash_lookup(&table, (uintptr_t)v));
sv->v = v;
sv->origvert = *v;
@@ -5331,6 +5341,12 @@ static int createEdgeSlideVerts(TransInfo *t)
l1 = get_next_loop(v, l1, e1, e, vec);
l2 = l2 ? get_next_loop(v, l2, e1, e, vec2) : NULL;
+ if (UNLIKELY(l1 == NULL && l2 != NULL)) {
+ l1 = l2;
+ l2 = NULL;
+ swap_v3_v3(vec, vec2);
+ }
+
BM_elem_flag_disable(v, BM_ELEM_TAG);
BM_elem_flag_disable(v2, BM_ELEM_TAG);
} while (e != v_first->e && l1);
@@ -5372,6 +5388,7 @@ static int createEdgeSlideVerts(TransInfo *t)
continue;
}
+ BLI_assert(BLI_smallhash_haskey(&table, (uintptr_t)v) != false);
j = GET_INT_FROM_POINTER(BLI_smallhash_lookup(&table, (uintptr_t)v));
if (sv_array[j].down) {
diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h
index 9c57c74a26d..dbf56f658ac 100644
--- a/source/blender/editors/transform/transform.h
+++ b/source/blender/editors/transform/transform.h
@@ -35,6 +35,7 @@
#include "ED_transform.h"
#include "ED_numinput.h"
+#include "ED_view3d.h"
#include "DNA_listBase.h"
@@ -481,7 +482,9 @@ int transformEnd(struct bContext *C, TransInfo *t);
void setTransformViewMatrices(TransInfo *t);
void convertViewVec(TransInfo *t, float r_vec[3], int dx, int dy);
+void projectIntViewEx(TransInfo *t, const float vec[3], int adr[2], const eV3DProjTest flag);
void projectIntView(TransInfo *t, const float vec[3], int adr[2]);
+void projectFloatViewEx(TransInfo *t, const float vec[3], float adr[2], const eV3DProjTest flag);
void projectFloatView(TransInfo *t, const float vec[3], float adr[2]);
void applyAspectRatio(TransInfo *t, float *vec);
diff --git a/source/blender/editors/transform/transform_conversions.c b/source/blender/editors/transform/transform_conversions.c
index 5be06188e4e..57816e7546c 100644
--- a/source/blender/editors/transform/transform_conversions.c
+++ b/source/blender/editors/transform/transform_conversions.c
@@ -4560,7 +4560,7 @@ static void ObjectToTransData(TransInfo *t, TransData *td, Object *ob)
short constinv;
short skip_invert = 0;
- if (ob->rigidbody_object) {
+ if (t->mode != TFM_DUMMY && ob->rigidbody_object) {
float rot[3][3], scale[3];
/* save original object transform */
diff --git a/source/blender/editors/transform/transform_manipulator.c b/source/blender/editors/transform/transform_manipulator.c
index cd6035a232b..54fb567b8a2 100644
--- a/source/blender/editors/transform/transform_manipulator.c
+++ b/source/blender/editors/transform/transform_manipulator.c
@@ -841,37 +841,89 @@ static void manipulator_setcolor(View3D *v3d, char axis, int colcode, unsigned c
glColor4ubv(col);
}
-/* viewmatrix should have been set OK, also no shademode! */
-static void draw_manipulator_axes(View3D *v3d, RegionView3D *rv3d, int colcode, int flagx, int flagy, int flagz)
+static void axis_sort_v3(const float axis_values[3], int r_axis_order[3])
{
+ float v[3];
+ copy_v3_v3(v, axis_values);
- /* axes */
- if (flagx) {
- manipulator_setcolor(v3d, 'X', colcode, axisBlendAngle(rv3d->twangle[0]));
- if (flagx & MAN_SCALE_X) glLoadName(MAN_SCALE_X);
- else if (flagx & MAN_TRANS_X) glLoadName(MAN_TRANS_X);
- glBegin(GL_LINES);
- glVertex3f(0.2f, 0.0f, 0.0f);
- glVertex3f(1.0f, 0.0f, 0.0f);
- glEnd();
+#define SWAP_AXIS(a, b) { \
+ SWAP(float, v[a], v[b]); \
+ SWAP(int, r_axis_order[a], r_axis_order[b]); \
+} (void)0
+
+ if (v[0] < v[1]) {
+ if (v[2] < v[0]) { SWAP_AXIS(0, 2); }
+ }
+ else {
+ if (v[1] < v[2]) { SWAP_AXIS(0, 1); }
+ else { SWAP_AXIS(0, 2); }
}
- if (flagy) {
- if (flagy & MAN_SCALE_Y) glLoadName(MAN_SCALE_Y);
- else if (flagy & MAN_TRANS_Y) glLoadName(MAN_TRANS_Y);
- manipulator_setcolor(v3d, 'Y', colcode, axisBlendAngle(rv3d->twangle[1]));
- glBegin(GL_LINES);
- glVertex3f(0.0f, 0.2f, 0.0f);
- glVertex3f(0.0f, 1.0f, 0.0f);
- glEnd();
+ if (v[2] < v[1]) { SWAP_AXIS(1, 2); }
+
+#undef SWAP_AXIS
+}
+static void manipulator_axis_order(RegionView3D *rv3d, int r_axis_order[3])
+{
+ float axis_values[3];
+ float vec[3];
+
+ ED_view3d_global_to_vector(rv3d, rv3d->twmat[3], vec);
+
+ axis_values[0] = -dot_v3v3(rv3d->twmat[0], vec);
+ axis_values[1] = -dot_v3v3(rv3d->twmat[1], vec);
+ axis_values[2] = -dot_v3v3(rv3d->twmat[2], vec);
+
+ axis_sort_v3(axis_values, r_axis_order);
+}
+
+/* viewmatrix should have been set OK, also no shademode! */
+static void draw_manipulator_axes_single(View3D *v3d, RegionView3D *rv3d, int colcode,
+ int flagx, int flagy, int flagz, int axis)
+{
+ switch (axis) {
+ case 0:
+ /* axes */
+ if (flagx) {
+ manipulator_setcolor(v3d, 'X', colcode, axisBlendAngle(rv3d->twangle[0]));
+ if (flagx & MAN_SCALE_X) glLoadName(MAN_SCALE_X);
+ else if (flagx & MAN_TRANS_X) glLoadName(MAN_TRANS_X);
+ glBegin(GL_LINES);
+ glVertex3f(0.2f, 0.0f, 0.0f);
+ glVertex3f(1.0f, 0.0f, 0.0f);
+ glEnd();
+ }
+ break;
+ case 1:
+ if (flagy) {
+ if (flagy & MAN_SCALE_Y) glLoadName(MAN_SCALE_Y);
+ else if (flagy & MAN_TRANS_Y) glLoadName(MAN_TRANS_Y);
+ manipulator_setcolor(v3d, 'Y', colcode, axisBlendAngle(rv3d->twangle[1]));
+ glBegin(GL_LINES);
+ glVertex3f(0.0f, 0.2f, 0.0f);
+ glVertex3f(0.0f, 1.0f, 0.0f);
+ glEnd();
+ }
+ break;
+ case 2:
+ if (flagz) {
+ if (flagz & MAN_SCALE_Z) glLoadName(MAN_SCALE_Z);
+ else if (flagz & MAN_TRANS_Z) glLoadName(MAN_TRANS_Z);
+ manipulator_setcolor(v3d, 'Z', colcode, axisBlendAngle(rv3d->twangle[2]));
+ glBegin(GL_LINES);
+ glVertex3f(0.0f, 0.0f, 0.2f);
+ glVertex3f(0.0f, 0.0f, 1.0f);
+ glEnd();
+ }
+ break;
}
- if (flagz) {
- if (flagz & MAN_SCALE_Z) glLoadName(MAN_SCALE_Z);
- else if (flagz & MAN_TRANS_Z) glLoadName(MAN_TRANS_Z);
- manipulator_setcolor(v3d, 'Z', colcode, axisBlendAngle(rv3d->twangle[2]));
- glBegin(GL_LINES);
- glVertex3f(0.0f, 0.0f, 0.2f);
- glVertex3f(0.0f, 0.0f, 1.0f);
- glEnd();
+}
+static void draw_manipulator_axes(View3D *v3d, RegionView3D *rv3d, int colcode,
+ int flagx, int flagy, int flagz,
+ const int axis_order[3])
+{
+ int i;
+ for (i = 0; i < 3; i++) {
+ draw_manipulator_axes_single(v3d, rv3d, colcode, flagx, flagy, flagz, axis_order[i]);
}
}
@@ -1214,10 +1266,14 @@ static void draw_manipulator_scale(View3D *v3d, RegionView3D *rv3d, int moving,
{
float cywid = 0.25f * 0.01f * (float)U.tw_handlesize;
float cusize = cywid * 0.75f, dz;
+ int axis_order[3] = {2, 0, 1};
+ int i;
/* when called while moving in mixed mode, do not draw when... */
if ((drawflags & MAN_SCALE_C) == 0) return;
+ manipulator_axis_order(rv3d, axis_order);
+
glDisable(GL_DEPTH_TEST);
/* not in combo mode */
@@ -1255,28 +1311,41 @@ static void draw_manipulator_scale(View3D *v3d, RegionView3D *rv3d, int moving,
/* axis */
/* in combo mode, this is always drawn as first type */
- draw_manipulator_axes(v3d, rv3d, colcode, drawflags & MAN_SCALE_X, drawflags & MAN_SCALE_Y, drawflags & MAN_SCALE_Z);
-
- /* Z cube */
- glTranslatef(0.0, 0.0, dz);
- if (drawflags & MAN_SCALE_Z) {
- if (G.f & G_PICKSEL) glLoadName(MAN_SCALE_Z);
- manipulator_setcolor(v3d, 'Z', colcode, axisBlendAngle(rv3d->twangle[2]));
- drawsolidcube(cusize);
- }
- /* X cube */
- glTranslatef(dz, 0.0, -dz);
- if (drawflags & MAN_SCALE_X) {
- if (G.f & G_PICKSEL) glLoadName(MAN_SCALE_X);
- manipulator_setcolor(v3d, 'X', colcode, axisBlendAngle(rv3d->twangle[0]));
- drawsolidcube(cusize);
- }
- /* Y cube */
- glTranslatef(-dz, dz, 0.0);
- if (drawflags & MAN_SCALE_Y) {
- if (G.f & G_PICKSEL) glLoadName(MAN_SCALE_Y);
- manipulator_setcolor(v3d, 'Y', colcode, axisBlendAngle(rv3d->twangle[1]));
- drawsolidcube(cusize);
+ draw_manipulator_axes(v3d, rv3d, colcode,
+ drawflags & MAN_SCALE_X, drawflags & MAN_SCALE_Y, drawflags & MAN_SCALE_Z,
+ axis_order);
+
+
+ for (i = 0; i < 3; i++) {
+ switch (axis_order[i]) {
+ case 0: /* X cube */
+ if (drawflags & MAN_SCALE_X) {
+ glTranslatef(dz, 0.0, 0.0);
+ if (G.f & G_PICKSEL) glLoadName(MAN_SCALE_X);
+ manipulator_setcolor(v3d, 'X', colcode, axisBlendAngle(rv3d->twangle[0]));
+ drawsolidcube(cusize);
+ glTranslatef(-dz, 0.0, 0.0);
+ }
+ break;
+ case 1: /* Y cube */
+ if (drawflags & MAN_SCALE_Y) {
+ glTranslatef(0.0, dz, 0.0);
+ if (G.f & G_PICKSEL) glLoadName(MAN_SCALE_Y);
+ manipulator_setcolor(v3d, 'Y', colcode, axisBlendAngle(rv3d->twangle[1]));
+ drawsolidcube(cusize);
+ glTranslatef(0.0, -dz, 0.0);
+ }
+ break;
+ case 2: /* Z cube */
+ if (drawflags & MAN_SCALE_Z) {
+ glTranslatef(0.0, 0.0, dz);
+ if (G.f & G_PICKSEL) glLoadName(MAN_SCALE_Z);
+ manipulator_setcolor(v3d, 'Z', colcode, axisBlendAngle(rv3d->twangle[2]));
+ drawsolidcube(cusize);
+ glTranslatef(0.0, 0.0, -dz);
+ }
+ break;
+ }
}
/* if shiftkey, center point as last, for selectbuffer order */
@@ -1333,16 +1402,17 @@ static void draw_manipulator_translate(View3D *v3d, RegionView3D *rv3d, int UNUS
float cywid = 0.25f * cylen, dz, size;
float unitmat[4][4];
int shift = 0; // XXX
+ int axis_order[3] = {0, 1, 2};
+ int i;
/* when called while moving in mixed mode, do not draw when... */
if ((drawflags & MAN_TRANS_C) == 0) return;
+ manipulator_axis_order(rv3d, axis_order);
+
// XXX if (moving) glTranslatef(t->vec[0], t->vec[1], t->vec[2]);
glDisable(GL_DEPTH_TEST);
- qobj = gluNewQuadric();
- gluQuadricDrawStyle(qobj, GLU_FILL);
-
/* center circle, do not add to selection when shift is pressed (planar constraint) */
if ((G.f & G_PICKSEL) && shift == 0) glLoadName(MAN_TRANS_C);
@@ -1360,8 +1430,11 @@ static void draw_manipulator_translate(View3D *v3d, RegionView3D *rv3d, int UNUS
glLoadName(-1);
// translate drawn as last, only axis when no combo with scale, or for ghosting
- if ((combo & V3D_MANIP_SCALE) == 0 || colcode == MAN_GHOST)
- draw_manipulator_axes(v3d, rv3d, colcode, drawflags & MAN_TRANS_X, drawflags & MAN_TRANS_Y, drawflags & MAN_TRANS_Z);
+ if ((combo & V3D_MANIP_SCALE) == 0 || colcode == MAN_GHOST) {
+ draw_manipulator_axes(v3d, rv3d, colcode,
+ drawflags & MAN_TRANS_X, drawflags & MAN_TRANS_Y, drawflags & MAN_TRANS_Z,
+ axis_order);
+ }
/* offset in combo mode, for rotate a bit more */
@@ -1369,29 +1442,43 @@ static void draw_manipulator_translate(View3D *v3d, RegionView3D *rv3d, int UNUS
else if (combo & (V3D_MANIP_SCALE)) dz = 1.0f + 0.5f * cylen;
else dz = 1.0f;
- /* Z Cone */
- glTranslatef(0.0, 0.0, dz);
- if (drawflags & MAN_TRANS_Z) {
- if (G.f & G_PICKSEL) glLoadName(MAN_TRANS_Z);
- manipulator_setcolor(v3d, 'Z', colcode, axisBlendAngle(rv3d->twangle[2]));
- draw_cone(qobj, cylen, cywid);
- }
- /* X Cone */
- glTranslatef(dz, 0.0, -dz);
- if (drawflags & MAN_TRANS_X) {
- if (G.f & G_PICKSEL) glLoadName(MAN_TRANS_X);
- glRotatef(90.0, 0.0, 1.0, 0.0);
- manipulator_setcolor(v3d, 'X', colcode, axisBlendAngle(rv3d->twangle[0]));
- draw_cone(qobj, cylen, cywid);
- glRotatef(-90.0, 0.0, 1.0, 0.0);
- }
- /* Y Cone */
- glTranslatef(-dz, dz, 0.0);
- if (drawflags & MAN_TRANS_Y) {
- if (G.f & G_PICKSEL) glLoadName(MAN_TRANS_Y);
- glRotatef(-90.0, 1.0, 0.0, 0.0);
- manipulator_setcolor(v3d, 'Y', colcode, axisBlendAngle(rv3d->twangle[1]));
- draw_cone(qobj, cylen, cywid);
+ qobj = gluNewQuadric();
+ gluQuadricDrawStyle(qobj, GLU_FILL);
+
+ for (i = 0; i < 3; i++) {
+ switch (axis_order[i]) {
+ case 0: /* Z Cone */
+ if (drawflags & MAN_TRANS_Z) {
+ glTranslatef(0.0, 0.0, dz);
+ if (G.f & G_PICKSEL) glLoadName(MAN_TRANS_Z);
+ manipulator_setcolor(v3d, 'Z', colcode, axisBlendAngle(rv3d->twangle[2]));
+ draw_cone(qobj, cylen, cywid);
+ glTranslatef(0.0, 0.0, -dz);
+ }
+ break;
+ case 1: /* X Cone */
+ if (drawflags & MAN_TRANS_X) {
+ glTranslatef(dz, 0.0, 0.0);
+ if (G.f & G_PICKSEL) glLoadName(MAN_TRANS_X);
+ glRotatef(90.0, 0.0, 1.0, 0.0);
+ manipulator_setcolor(v3d, 'X', colcode, axisBlendAngle(rv3d->twangle[0]));
+ draw_cone(qobj, cylen, cywid);
+ glRotatef(-90.0, 0.0, 1.0, 0.0);
+ glTranslatef(-dz, 0.0, 0.0);
+ }
+ break;
+ case 2: /* Y Cone */
+ if (drawflags & MAN_TRANS_Y) {
+ glTranslatef(0.0, dz, 0.0);
+ if (G.f & G_PICKSEL) glLoadName(MAN_TRANS_Y);
+ glRotatef(-90.0, 1.0, 0.0, 0.0);
+ manipulator_setcolor(v3d, 'Y', colcode, axisBlendAngle(rv3d->twangle[1]));
+ draw_cone(qobj, cylen, cywid);
+ glRotatef(90.0, 1.0, 0.0, 0.0);
+ glTranslatef(0.0, -dz, 0.0);
+ }
+ break;
+ }
}
gluDeleteQuadric(qobj);
@@ -1407,10 +1494,13 @@ static void draw_manipulator_rotate_cyl(View3D *v3d, RegionView3D *rv3d, int mov
float size;
float cylen = 0.01f * (float)U.tw_handlesize;
float cywid = 0.25f * cylen;
-
+ int axis_order[3] = {2, 0, 1};
+ int i;
/* when called while moving in mixed mode, do not draw when... */
if ((drawflags & MAN_ROT_C) == 0) return;
+ manipulator_axis_order(rv3d, axis_order);
+
/* prepare for screen aligned draw */
glPushMatrix();
size = screen_aligned(rv3d, rv3d->twmat);
@@ -1461,36 +1551,50 @@ static void draw_manipulator_rotate_cyl(View3D *v3d, RegionView3D *rv3d, int mov
if ((G.f & G_PICKSEL) == 0) {
// only draw axis when combo didn't draw scale axes
- if ((combo & V3D_MANIP_SCALE) == 0)
- draw_manipulator_axes(v3d, rv3d, colcode, drawflags & MAN_ROT_X, drawflags & MAN_ROT_Y, drawflags & MAN_ROT_Z);
+ if ((combo & V3D_MANIP_SCALE) == 0) {
+ draw_manipulator_axes(v3d, rv3d, colcode,
+ drawflags & MAN_ROT_X, drawflags & MAN_ROT_Y, drawflags & MAN_ROT_Z,
+ axis_order);
+ }
/* only has to be set when not in picking */
gluQuadricDrawStyle(qobj, GLU_FILL);
}
- /* Z cyl */
- glTranslatef(0.0, 0.0, 1.0);
- if (drawflags & MAN_ROT_Z) {
- if (G.f & G_PICKSEL) glLoadName(MAN_ROT_Z);
- manipulator_setcolor(v3d, 'Z', colcode, 255);
- draw_cylinder(qobj, cylen, cywid);
- }
- /* X cyl */
- glTranslatef(1.0, 0.0, -1.0);
- if (drawflags & MAN_ROT_X) {
- if (G.f & G_PICKSEL) glLoadName(MAN_ROT_X);
- glRotatef(90.0, 0.0, 1.0, 0.0);
- manipulator_setcolor(v3d, 'X', colcode, 255);
- draw_cylinder(qobj, cylen, cywid);
- glRotatef(-90.0, 0.0, 1.0, 0.0);
- }
- /* Y cylinder */
- glTranslatef(-1.0, 1.0, 0.0);
- if (drawflags & MAN_ROT_Y) {
- if (G.f & G_PICKSEL) glLoadName(MAN_ROT_Y);
- glRotatef(-90.0, 1.0, 0.0, 0.0);
- manipulator_setcolor(v3d, 'Y', colcode, 255);
- draw_cylinder(qobj, cylen, cywid);
+ for (i = 0; i < 3; i++) {
+ switch (axis_order[i]) {
+ case 0: /* X cylinder */
+ if (drawflags & MAN_ROT_X) {
+ glTranslatef(1.0, 0.0, 0.0);
+ if (G.f & G_PICKSEL) glLoadName(MAN_ROT_X);
+ glRotatef(90.0, 0.0, 1.0, 0.0);
+ manipulator_setcolor(v3d, 'X', colcode, 255);
+ draw_cylinder(qobj, cylen, cywid);
+ glRotatef(-90.0, 0.0, 1.0, 0.0);
+ glTranslatef(-1.0, 0.0, 0.0);
+ }
+ break;
+ case 1: /* Y cylinder */
+ if (drawflags & MAN_ROT_Y) {
+ glTranslatef(0.0, 1.0, 0.0);
+ if (G.f & G_PICKSEL) glLoadName(MAN_ROT_Y);
+ glRotatef(-90.0, 1.0, 0.0, 0.0);
+ manipulator_setcolor(v3d, 'Y', colcode, 255);
+ draw_cylinder(qobj, cylen, cywid);
+ glRotatef(90.0, 1.0, 0.0, 0.0);
+ glTranslatef(0.0, -1.0, 0.0);
+ }
+ break;
+ case 2: /* Z cylinder */
+ if (drawflags & MAN_ROT_Z) {
+ glTranslatef(0.0, 0.0, 1.0);
+ if (G.f & G_PICKSEL) glLoadName(MAN_ROT_Z);
+ manipulator_setcolor(v3d, 'Z', colcode, 255);
+ draw_cylinder(qobj, cylen, cywid);
+ glTranslatef(0.0, 0.0, -1.0);
+ }
+ break;
+ }
}
/* restore */
diff --git a/source/blender/editors/transform/transform_ops.c b/source/blender/editors/transform/transform_ops.c
index 32dc31954f2..0392c0f47a2 100644
--- a/source/blender/editors/transform/transform_ops.c
+++ b/source/blender/editors/transform/transform_ops.c
@@ -37,6 +37,8 @@
#include "BLI_math.h"
#include "BLI_utildefines.h"
+#include "BLF_translation.h"
+
#include "BKE_context.h"
#include "BKE_global.h"
#include "BKE_armature.h"
@@ -169,7 +171,7 @@ static int select_orientation_invoke(bContext *C, wmOperator *UNUSED(op), wmEven
uiPopupMenu *pup;
uiLayout *layout;
- pup = uiPupMenuBegin(C, "Orientation", ICON_NONE);
+ pup = uiPupMenuBegin(C, IFACE_("Orientation"), ICON_NONE);
layout = uiPupMenuLayout(pup);
uiItemsEnumO(layout, "TRANSFORM_OT_select_orientation", "orientation");
uiPupMenuEnd(C, pup);
diff --git a/source/blender/editors/util/ed_util.c b/source/blender/editors/util/ed_util.c
index 23d6b0a075e..f0f31b1e793 100644
--- a/source/blender/editors/util/ed_util.c
+++ b/source/blender/editors/util/ed_util.c
@@ -42,6 +42,8 @@
#include "BLI_blenlib.h"
+#include "BLF_translation.h"
+
#include "BKE_context.h"
#include "BKE_global.h"
#include "BKE_main.h"
@@ -174,10 +176,10 @@ void unpack_menu(bContext *C, const char *opname, const char *id_name, const cha
char line[FILE_MAX + 100];
wmOperatorType *ot = WM_operatortype_find(opname, 1);
- pup = uiPupMenuBegin(C, "Unpack file", ICON_NONE);
+ pup = uiPupMenuBegin(C, IFACE_("Unpack File"), ICON_NONE);
layout = uiPupMenuLayout(pup);
- strcpy(line, "Remove Pack");
+ strcpy(line, IFACE_("Remove Pack"));
props_ptr = uiItemFullO_ptr(layout, ot, line, ICON_NONE, NULL, WM_OP_EXEC_DEFAULT, UI_ITEM_O_RETURN_PROPS);
RNA_enum_set(&props_ptr, "method", PF_REMOVE);
RNA_string_set(&props_ptr, "id", id_name);
@@ -191,14 +193,14 @@ void unpack_menu(bContext *C, const char *opname, const char *id_name, const cha
if (strcmp(abs_name, local_name) != 0) {
switch (checkPackedFile(local_name, pf)) {
case PF_NOFILE:
- BLI_snprintf(line, sizeof(line), "Create %s", local_name);
+ BLI_snprintf(line, sizeof(line), IFACE_("Create %s"), local_name);
props_ptr = uiItemFullO_ptr(layout, ot, line, ICON_NONE, NULL, WM_OP_EXEC_DEFAULT, UI_ITEM_O_RETURN_PROPS);
RNA_enum_set(&props_ptr, "method", PF_WRITE_LOCAL);
RNA_string_set(&props_ptr, "id", id_name);
break;
case PF_EQUAL:
- BLI_snprintf(line, sizeof(line), "Use %s (identical)", local_name);
+ BLI_snprintf(line, sizeof(line), IFACE_("Use %s (identical)"), local_name);
//uiItemEnumO_ptr(layout, ot, line, 0, "method", PF_USE_LOCAL);
props_ptr = uiItemFullO_ptr(layout, ot, line, ICON_NONE, NULL, WM_OP_EXEC_DEFAULT, UI_ITEM_O_RETURN_PROPS);
RNA_enum_set(&props_ptr, "method", PF_USE_LOCAL);
@@ -206,13 +208,13 @@ void unpack_menu(bContext *C, const char *opname, const char *id_name, const cha
break;
case PF_DIFFERS:
- BLI_snprintf(line, sizeof(line), "Use %s (differs)", local_name);
+ BLI_snprintf(line, sizeof(line), IFACE_("Use %s (differs)"), local_name);
//uiItemEnumO_ptr(layout, ot, line, 0, "method", PF_USE_LOCAL);
props_ptr = uiItemFullO_ptr(layout, ot, line, ICON_NONE, NULL, WM_OP_EXEC_DEFAULT, UI_ITEM_O_RETURN_PROPS);
RNA_enum_set(&props_ptr, "method", PF_USE_LOCAL);
RNA_string_set(&props_ptr, "id", id_name);
- BLI_snprintf(line, sizeof(line), "Overwrite %s", local_name);
+ BLI_snprintf(line, sizeof(line), IFACE_("Overwrite %s"), local_name);
//uiItemEnumO_ptr(layout, ot, line, 0, "method", PF_WRITE_LOCAL);
props_ptr = uiItemFullO_ptr(layout, ot, line, ICON_NONE, NULL, WM_OP_EXEC_DEFAULT, UI_ITEM_O_RETURN_PROPS);
RNA_enum_set(&props_ptr, "method", PF_WRITE_LOCAL);
@@ -224,27 +226,27 @@ void unpack_menu(bContext *C, const char *opname, const char *id_name, const cha
switch (checkPackedFile(abs_name, pf)) {
case PF_NOFILE:
- BLI_snprintf(line, sizeof(line), "Create %s", abs_name);
+ BLI_snprintf(line, sizeof(line), IFACE_("Create %s"), abs_name);
//uiItemEnumO_ptr(layout, ot, line, 0, "method", PF_WRITE_ORIGINAL);
props_ptr = uiItemFullO_ptr(layout, ot, line, ICON_NONE, NULL, WM_OP_EXEC_DEFAULT, UI_ITEM_O_RETURN_PROPS);
RNA_enum_set(&props_ptr, "method", PF_WRITE_ORIGINAL);
RNA_string_set(&props_ptr, "id", id_name);
break;
case PF_EQUAL:
- BLI_snprintf(line, sizeof(line), "Use %s (identical)", abs_name);
+ BLI_snprintf(line, sizeof(line), IFACE_("Use %s (identical)"), abs_name);
//uiItemEnumO_ptr(layout, ot, line, 0, "method", PF_USE_ORIGINAL);
props_ptr = uiItemFullO_ptr(layout, ot, line, ICON_NONE, NULL, WM_OP_EXEC_DEFAULT, UI_ITEM_O_RETURN_PROPS);
RNA_enum_set(&props_ptr, "method", PF_USE_ORIGINAL);
RNA_string_set(&props_ptr, "id", id_name);
break;
case PF_DIFFERS:
- BLI_snprintf(line, sizeof(line), "Use %s (differs)", abs_name);
+ BLI_snprintf(line, sizeof(line), IFACE_("Use %s (differs)"), abs_name);
//uiItemEnumO_ptr(layout, ot, line, 0, "method", PF_USE_ORIGINAL);
props_ptr = uiItemFullO_ptr(layout, ot, line, ICON_NONE, NULL, WM_OP_EXEC_DEFAULT, UI_ITEM_O_RETURN_PROPS);
RNA_enum_set(&props_ptr, "method", PF_USE_ORIGINAL);
RNA_string_set(&props_ptr, "id", id_name);
- BLI_snprintf(line, sizeof(line), "Overwrite %s", abs_name);
+ BLI_snprintf(line, sizeof(line), IFACE_("Overwrite %s"), abs_name);
//uiItemEnumO_ptr(layout, ot, line, 0, "method", PF_WRITE_ORIGINAL);
props_ptr = uiItemFullO_ptr(layout, ot, line, ICON_NONE, NULL, WM_OP_EXEC_DEFAULT, UI_ITEM_O_RETURN_PROPS);
RNA_enum_set(&props_ptr, "method", PF_WRITE_ORIGINAL);
diff --git a/source/blender/editors/util/undo.c b/source/blender/editors/util/undo.c
index 8a0ef06ef12..ac2ee21d09d 100644
--- a/source/blender/editors/util/undo.c
+++ b/source/blender/editors/util/undo.c
@@ -83,8 +83,8 @@ void ED_undo_push(bContext *C, const char *str)
Object *obact = CTX_data_active_object(C);
if (G.debug & G_DEBUG)
- printf("undo push %s\n", str);
-
+ printf("%s: %s\n", __func__, str);
+
if (obedit) {
if (U.undosteps == 0) return;
diff --git a/source/blender/editors/uvedit/uvedit_ops.c b/source/blender/editors/uvedit/uvedit_ops.c
index a17d3c20c1c..fbb6250c05b 100644
--- a/source/blender/editors/uvedit/uvedit_ops.c
+++ b/source/blender/editors/uvedit/uvedit_ops.c
@@ -113,7 +113,7 @@ static int ED_operator_uvedit_can_uv_sculpt(struct bContext *C)
return ED_space_image_show_uvedit(sima, obedit) && !(toolsettings->use_uv_sculpt);
}
-static int UNUSED_FUNCTION(ED_operator_uvmap_mesh) (bContext * C)
+static int UNUSED_FUNCTION(ED_operator_uvmap_mesh) (bContext *C)
{
Object *ob = CTX_data_active_object(C);
@@ -1582,7 +1582,7 @@ static void UV_OT_align(wmOperatorType *ot)
/* api callbacks */
ot->exec = align_exec;
- ot->poll = ED_operator_image_active; /* requires space image */;
+ ot->poll = ED_operator_uvedit; /* requires space image */;
/* properties */
RNA_def_enum(ot->srna, "axis", axis_items, 'a', "Axis", "Axis to align UV locations on");
@@ -2258,7 +2258,7 @@ static void UV_OT_select(wmOperatorType *ot)
/* api callbacks */
ot->exec = select_exec;
ot->invoke = select_invoke;
- ot->poll = ED_operator_image_active; /* requires space image */;
+ ot->poll = ED_operator_uvedit; /* requires space image */;
/* properties */
RNA_def_boolean(ot->srna, "extend", 0,
@@ -2303,7 +2303,7 @@ static void UV_OT_select_loop(wmOperatorType *ot)
/* api callbacks */
ot->exec = select_loop_exec;
ot->invoke = select_loop_invoke;
- ot->poll = ED_operator_image_active; /* requires space image */;
+ ot->poll = ED_operator_uvedit; /* requires space image */;
/* properties */
RNA_def_boolean(ot->srna, "extend", 0,
@@ -2377,7 +2377,7 @@ static void UV_OT_select_linked(wmOperatorType *ot)
/* api callbacks */
ot->exec = select_linked_exec;
- ot->poll = ED_operator_image_active; /* requires space image */
+ ot->poll = ED_operator_uvedit; /* requires space image */
/* properties */
RNA_def_boolean(ot->srna, "extend", 0,
@@ -2405,7 +2405,7 @@ static void UV_OT_select_linked_pick(wmOperatorType *ot)
/* api callbacks */
ot->invoke = select_linked_pick_invoke;
ot->exec = select_linked_pick_exec;
- ot->poll = ED_operator_image_active; /* requires space image */;
+ ot->poll = ED_operator_uvedit; /* requires space image */;
/* properties */
RNA_def_boolean(ot->srna, "extend", 0,
@@ -2842,7 +2842,7 @@ static void UV_OT_select_border(wmOperatorType *ot)
ot->invoke = WM_border_select_invoke;
ot->exec = border_select_exec;
ot->modal = WM_border_select_modal;
- ot->poll = ED_operator_image_active; /* requires space image */;
+ ot->poll = ED_operator_uvedit; /* requires space image */;
ot->cancel = WM_border_select_cancel;
/* flags */
@@ -2966,7 +2966,7 @@ static void UV_OT_circle_select(wmOperatorType *ot)
ot->invoke = WM_gesture_circle_invoke;
ot->modal = WM_gesture_circle_modal;
ot->exec = circle_select_exec;
- ot->poll = ED_operator_image_active; /* requires space image */;
+ ot->poll = ED_operator_uvedit; /* requires space image */;
ot->cancel = WM_gesture_circle_cancel;
/* flags */
@@ -3095,7 +3095,7 @@ static void UV_OT_select_lasso(wmOperatorType *ot)
ot->invoke = WM_gesture_lasso_invoke;
ot->modal = WM_gesture_lasso_modal;
ot->exec = uv_lasso_select_exec;
- ot->poll = ED_operator_image_active;
+ ot->poll = ED_operator_uvedit;
ot->cancel = WM_gesture_lasso_cancel;
/* flags */
@@ -3170,7 +3170,7 @@ static void UV_OT_snap_cursor(wmOperatorType *ot)
/* api callbacks */
ot->exec = snap_cursor_exec;
- ot->poll = ED_operator_image_active; /* requires space image */;
+ ot->poll = ED_operator_uvedit; /* requires space image */;
/* properties */
RNA_def_enum(ot->srna, "target", target_items, 0, "Target", "Target to snap the selected UVs to");
@@ -3350,7 +3350,7 @@ static void UV_OT_snap_selected(wmOperatorType *ot)
/* api callbacks */
ot->exec = snap_selection_exec;
- ot->poll = ED_operator_image_active; /* requires space image */;
+ ot->poll = ED_operator_uvedit; /* requires space image */;
/* properties */
RNA_def_enum(ot->srna, "target", target_items, 0, "Target", "Target to snap the selected UVs to");
@@ -3769,7 +3769,7 @@ static void UV_OT_cursor_set(wmOperatorType *ot)
/* api callbacks */
ot->exec = set_2d_cursor_exec;
ot->invoke = set_2d_cursor_invoke;
- ot->poll = ED_operator_image_active; /* requires space image */;
+ ot->poll = ED_operator_uvedit; /* requires space image */;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;