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:
-rw-r--r--release/scripts/startup/bl_operators/__init__.py1
-rw-r--r--release/scripts/startup/bl_operators/constraint.py76
-rw-r--r--release/scripts/startup/bl_ui/properties_constraint.py40
-rw-r--r--source/blender/blenkernel/BKE_constraint.h4
-rw-r--r--source/blender/blenkernel/intern/constraint.c248
-rw-r--r--source/blender/blenlib/intern/math_rotation.c17
-rw-r--r--source/blender/blenloader/intern/readfile.c8
-rw-r--r--source/blender/blenloader/intern/writefile.c12
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_relations.cc2
-rw-r--r--source/blender/editors/animation/keyframing.c1
-rw-r--r--source/blender/editors/object/object_constraint.c21
-rw-r--r--source/blender/editors/transform/transform_conversions.c1
-rw-r--r--source/blender/makesdna/DNA_constraint_types.h17
-rw-r--r--source/blender/makesrna/intern/rna_constraint.c227
14 files changed, 663 insertions, 12 deletions
diff --git a/release/scripts/startup/bl_operators/__init__.py b/release/scripts/startup/bl_operators/__init__.py
index de538634595..4d9038684d1 100644
--- a/release/scripts/startup/bl_operators/__init__.py
+++ b/release/scripts/startup/bl_operators/__init__.py
@@ -29,6 +29,7 @@ _modules = [
"anim",
"clip",
"console",
+ "constraint",
"file",
"image",
"mask",
diff --git a/release/scripts/startup/bl_operators/constraint.py b/release/scripts/startup/bl_operators/constraint.py
new file mode 100644
index 00000000000..cf70022ed48
--- /dev/null
+++ b/release/scripts/startup/bl_operators/constraint.py
@@ -0,0 +1,76 @@
+# ##### 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.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+# <pep8-80 compliant>
+
+import bpy
+from bpy.types import (
+ Operator,
+)
+from bpy.props import (
+ IntProperty,
+)
+
+
+class CONSTRAINT_OT_add_target(Operator):
+ """Add a target to the constraint"""
+ bl_idname = "constraint.add_target"
+ bl_label = "Add Target"
+ bl_options = {'UNDO', 'INTERNAL'}
+
+ def execute(self, context):
+ context.constraint.targets.new()
+ return {'FINISHED'}
+
+
+class CONSTRAINT_OT_remove_target(Operator):
+ """Remove the target from the constraint"""
+ bl_idname = "constraint.remove_target"
+ bl_label = "Remove Target"
+ bl_options = {'UNDO', 'INTERNAL'}
+
+ index = IntProperty()
+
+ def execute(self, context):
+ tgts = context.constraint.targets
+ tgts.remove(tgts[self.index])
+ return {'FINISHED'}
+
+
+class CONSTRAINT_OT_normalize_target_weights(Operator):
+ """Normalize weights of all target bones"""
+ bl_idname = "constraint.normalize_target_weights"
+ bl_label = "Normalize Weights"
+ bl_options = {'UNDO', 'INTERNAL'}
+
+ def execute(self, context):
+ tgts = context.constraint.targets
+ total = sum(t.weight for t in tgts)
+
+ if total > 0:
+ for t in tgts:
+ t.weight = t.weight / total
+
+ return {'FINISHED'}
+
+
+classes = (
+ CONSTRAINT_OT_add_target,
+ CONSTRAINT_OT_remove_target,
+ CONSTRAINT_OT_normalize_target_weights,
+)
diff --git a/release/scripts/startup/bl_ui/properties_constraint.py b/release/scripts/startup/bl_ui/properties_constraint.py
index 30822fbe9a1..b1c0217f9c9 100644
--- a/release/scripts/startup/bl_ui/properties_constraint.py
+++ b/release/scripts/startup/bl_ui/properties_constraint.py
@@ -919,6 +919,46 @@ class ConstraintButtonsPanel:
def SCRIPT(self, context, layout, con):
layout.label(text="Blender 2.6 doesn't support python constraints yet")
+ def ARMATURE(self, context, layout, con):
+ topcol = layout.column()
+ topcol.use_property_split = True
+ topcol.operator("constraint.add_target", text="Add Target Bone")
+
+ if not con.targets:
+ box = topcol.box()
+ box.label(text="No target bones were added", icon="ERROR")
+
+ for i, tgt in enumerate(con.targets):
+ box = topcol.box()
+
+ has_target = tgt.target is not None
+
+ header = box.row()
+ header.use_property_split = False
+
+ split = header.split(factor=0.45, align=True)
+ split.prop(tgt, "target", text="")
+
+ row = split.row(align=True)
+ row.active = has_target
+ if has_target:
+ row.prop_search(tgt, "subtarget", tgt.target.data, "bones", text="")
+ else:
+ row.prop(tgt, "subtarget", text="", icon="BONE_DATA")
+
+ header.operator("constraint.remove_target", icon="REMOVE", text="").index = i
+
+ col = box.column()
+ col.active = has_target and tgt.subtarget != ""
+ col.prop(tgt, "weight", slider=True)
+
+ topcol.operator("constraint.normalize_target_weights")
+ topcol.prop(con, "use_deform_preserve_volume")
+ topcol.prop(con, "use_bone_envelopes")
+
+ if context.pose_bone:
+ topcol.prop(con, "use_current_location")
+
class OBJECT_PT_constraints(ConstraintButtonsPanel, Panel):
bl_label = "Object Constraints"
diff --git a/source/blender/blenkernel/BKE_constraint.h b/source/blender/blenkernel/BKE_constraint.h
index cfc7d8e6065..e7672001a15 100644
--- a/source/blender/blenkernel/BKE_constraint.h
+++ b/source/blender/blenkernel/BKE_constraint.h
@@ -131,11 +131,15 @@ void BKE_constraints_id_loop(struct ListBase *list, ConstraintIDFunc func, void
void BKE_constraint_free_data(struct bConstraint *con);
void BKE_constraint_free_data_ex(struct bConstraint *con, bool do_id_user);
+bool BKE_constraint_target_uses_bbone(struct bConstraint *con, struct bConstraintTarget *ct);
+
/* Constraint API function prototypes */
struct bConstraint *BKE_constraints_active_get(struct ListBase *list);
void BKE_constraints_active_set(ListBase *list, struct bConstraint *con);
struct bConstraint *BKE_constraints_find_name(struct ListBase *list, const char *name);
+struct bConstraint *BKE_constraint_find_from_target(struct Object *ob, struct bConstraintTarget *tgt);
+
struct bConstraint *BKE_constraint_add_for_object(struct Object *ob, const char *name, short type);
struct bConstraint *BKE_constraint_add_for_pose(struct Object *ob, struct bPoseChannel *pchan, const char *name, short type);
diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c
index 2ba19b8c7de..41b07d73dc9 100644
--- a/source/blender/blenkernel/intern/constraint.c
+++ b/source/blender/blenkernel/intern/constraint.c
@@ -2098,6 +2098,206 @@ static bConstraintTypeInfo CTI_PYTHON = {
pycon_evaluate /* evaluate */
};
+/* ----------- Armature Constraint -------------- */
+
+static void armdef_free(bConstraint *con)
+{
+ bArmatureConstraint *data = con->data;
+
+ /* Target list. */
+ BLI_freelistN(&data->targets);
+}
+
+static void armdef_copy(bConstraint *con, bConstraint *srccon)
+{
+ bArmatureConstraint *pcon = (bArmatureConstraint *)con->data;
+ bArmatureConstraint *opcon = (bArmatureConstraint *)srccon->data;
+
+ BLI_duplicatelist(&pcon->targets, &opcon->targets);
+}
+
+static int armdef_get_tars(bConstraint *con, ListBase *list)
+{
+ if (con && list) {
+ bArmatureConstraint *data = con->data;
+
+ *list = data->targets;
+
+ return BLI_listbase_count(&data->targets);
+ }
+
+ return 0;
+}
+
+static void armdef_id_looper(bConstraint *con, ConstraintIDFunc func, void *userdata)
+{
+ bArmatureConstraint *data = con->data;
+ bConstraintTarget *ct;
+
+ /* Target list. */
+ for (ct = data->targets.first; ct; ct = ct->next) {
+ func(con, (ID **)&ct->tar, false, userdata);
+ }
+}
+
+/* Compute the world space pose matrix of the target bone. */
+static void armdef_get_tarmat(struct Depsgraph *UNUSED(depsgraph),
+ bConstraint *UNUSED(con), bConstraintOb *UNUSED(cob),
+ bConstraintTarget *ct, float UNUSED(ctime))
+{
+ if (ct != NULL) {
+ if (ct->tar && ct->tar->type == OB_ARMATURE) {
+ bPoseChannel *pchan = BKE_pose_channel_find_name(ct->tar->pose, ct->subtarget);
+
+ if (pchan != NULL) {
+ mul_m4_m4m4(ct->matrix, ct->tar->obmat, pchan->pose_mat);
+ return;
+ }
+ }
+
+ unit_m4(ct->matrix);
+ }
+}
+
+/* Compute and accumulate transformation for a single target bone. */
+static void armdef_accumulate_bone(bConstraintTarget *ct, bPoseChannel *pchan, const float wco[3], bool force_envelope, float *r_totweight, float r_sum_mat[4][4], DualQuat *r_sum_dq)
+{
+ float mat[4][4], iobmat[4][4], iamat[4][4], basemat[4][4], co[3];
+ Bone *bone = pchan->bone;
+ float weight = ct->weight;
+
+ /* Our object's location in target pose space. */
+ invert_m4_m4(iobmat, ct->tar->obmat);
+ mul_v3_m4v3(co, iobmat, wco);
+
+ /* Inverted rest pose matrix: bone->chan_mat may not be final yet. */
+ invert_m4_m4(iamat, bone->arm_mat);
+
+ /* Multiply by the envelope weight when appropriate. */
+ if (force_envelope || (bone->flag & BONE_MULT_VG_ENV)) {
+ weight *= distfactor_to_bone(co, bone->arm_head, bone->arm_tail,
+ bone->rad_head, bone->rad_tail, bone->dist);
+ }
+
+ /* Find the correct bone transform matrix in world space. */
+ if (bone->segments > 1) {
+ /* The target is a B-Bone:
+ * FIRST: find the segment (see b_bone_deform in armature.c)
+ * Need to transform co back to bonespace, only need y. */
+ float y = iamat[0][1] * co[0] + iamat[1][1] * co[1] + iamat[2][1] * co[2] + iamat[3][1];
+
+ float segment = bone->length / ((float)bone->segments);
+ int a = (int)(y / segment);
+
+ CLAMP(a, 0, bone->segments - 1);
+
+ /* SECOND: compute the matrix (see pchan_b_bone_defmats in armature.c) */
+ Mat4 b_bone[MAX_BBONE_SUBDIV], b_bone_rest[MAX_BBONE_SUBDIV];
+ float irmat[4][4];
+
+ b_bone_spline_setup(pchan, false, b_bone);
+ b_bone_spline_setup(pchan, true, b_bone_rest);
+
+ invert_m4_m4(irmat, b_bone_rest[a].mat);
+ mul_m4_series(mat, ct->matrix, b_bone[a].mat, irmat, iamat, iobmat);
+ }
+ else {
+ /* Simple bone. */
+ mul_m4_series(mat, ct->matrix, iamat, iobmat);
+ }
+
+ /* Accumulate the transformation. */
+ *r_totweight += weight;
+
+ if (r_sum_dq != NULL) {
+ DualQuat tmpdq;
+
+ mul_m4_series(basemat, ct->tar->obmat, bone->arm_mat, iobmat);
+
+ mat4_to_dquat(&tmpdq, basemat, mat);
+ add_weighted_dq_dq(r_sum_dq, &tmpdq, weight);
+ }
+ else {
+ mul_m4_fl(mat, weight);
+ add_m4_m4m4(r_sum_mat, r_sum_mat, mat);
+ }
+}
+
+static void armdef_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *targets)
+{
+ bArmatureConstraint *data = con->data;
+
+ float sum_mat[4][4], input_co[3];
+ DualQuat sum_dq;
+ float weight = 0.0f;
+
+ /* Prepare for blending. */
+ zero_m4(sum_mat);
+ memset(&sum_dq, 0, sizeof(sum_dq));
+
+ DualQuat *pdq = (data->flag & CONSTRAINT_ARMATURE_QUATERNION) ? &sum_dq : NULL;
+ bool use_envelopes = (data->flag & CONSTRAINT_ARMATURE_ENVELOPE) != 0;
+
+ if (cob->pchan && cob->pchan->bone && !(data->flag & CONSTRAINT_ARMATURE_CUR_LOCATION)) {
+ /* For constraints on bones, use the rest position to bind b-bone segments
+ * and envelopes, to allow safely changing the bone location as if parented. */
+ copy_v3_v3(input_co, cob->pchan->bone->arm_head);
+ mul_m4_v3(cob->ob->obmat, input_co);
+ }
+ else {
+ copy_v3_v3(input_co, cob->matrix[3]);
+ }
+
+ /* Process all targets. */
+ for (bConstraintTarget *ct = targets->first; ct; ct = ct->next) {
+ if (ct->weight <= 0.0f) {
+ continue;
+ }
+
+ /* Lookup the bone and abort if failed. */
+ if (!VALID_CONS_TARGET(ct) || ct->tar->type != OB_ARMATURE) {
+ return;
+ }
+
+ bPoseChannel *pchan = BKE_pose_channel_find_name(ct->tar->pose, ct->subtarget);
+
+ if (pchan == NULL || pchan->bone == NULL) {
+ return;
+ }
+
+ armdef_accumulate_bone(ct, pchan, input_co, use_envelopes, &weight, sum_mat, pdq);
+ }
+
+ /* Compute the final transform. */
+ if (weight > 0.0f) {
+ if (pdq != NULL) {
+ normalize_dq(pdq, weight);
+ dquat_to_mat4(sum_mat, pdq);
+ }
+ else {
+ mul_m4_fl(sum_mat, 1.0f / weight);
+ }
+
+ /* Apply the transform to the result matrix. */
+ mul_m4_m4m4(cob->matrix, sum_mat, cob->matrix);
+ }
+}
+
+static bConstraintTypeInfo CTI_ARMATURE = {
+ CONSTRAINT_TYPE_ARMATURE, /* type */
+ sizeof(bArmatureConstraint), /* size */
+ "Armature", /* name */
+ "bArmatureConstraint", /* struct name */
+ armdef_free, /* free data */
+ armdef_id_looper, /* id looper */
+ armdef_copy, /* copy data */
+ NULL, /* new data */
+ armdef_get_tars, /* get constraint targets */
+ NULL, /* flush constraint targets */
+ armdef_get_tarmat, /* get target matrix */
+ armdef_evaluate /* evaluate */
+};
+
/* -------- Action Constraint ----------- */
static void actcon_new_data(void *cdata)
@@ -4469,6 +4669,7 @@ static void constraints_init_typeinfo(void)
constraintsTypeInfo[27] = &CTI_CAMERASOLVER; /* Camera Solver Constraint */
constraintsTypeInfo[28] = &CTI_OBJECTSOLVER; /* Object Solver Constraint */
constraintsTypeInfo[29] = &CTI_TRANSFORM_CACHE; /* Transform Cache Constraint */
+ constraintsTypeInfo[30] = &CTI_ARMATURE; /* Armature Constraint */
}
/* This function should be used for getting the appropriate type-info when only
@@ -4684,6 +4885,11 @@ static bConstraint *add_new_constraint(Object *ob, bPoseChannel *pchan, const ch
return con;
}
+bool BKE_constraint_target_uses_bbone(struct bConstraint *con, struct bConstraintTarget *UNUSED(ct))
+{
+ return (con->flag & CONSTRAINT_BBONE_SHAPE) || (con->type == CONSTRAINT_TYPE_ARMATURE);
+}
+
/* ......... */
/* Add new constraint for the given bone */
@@ -4829,6 +5035,48 @@ void BKE_constraints_active_set(ListBase *list, bConstraint *con)
}
}
+static bConstraint *constraint_list_find_from_target(ListBase *constraints, bConstraintTarget *tgt)
+{
+ for (bConstraint *con = constraints->first; con; con = con->next) {
+ ListBase *targets = NULL;
+
+ if (con->type == CONSTRAINT_TYPE_PYTHON) {
+ targets = &((bPythonConstraint*)con->data)->targets;
+ }
+ else if (con->type == CONSTRAINT_TYPE_ARMATURE) {
+ targets = &((bArmatureConstraint*)con->data)->targets;
+ }
+
+ if (targets && BLI_findindex(targets, tgt) != -1) {
+ return con;
+ }
+ }
+
+ return NULL;
+}
+
+/* Finds the constraint that owns the given target within the object. */
+bConstraint *BKE_constraint_find_from_target(Object *ob, bConstraintTarget *tgt)
+{
+ bConstraint *result = constraint_list_find_from_target(&ob->constraints, tgt);
+
+ if (result != NULL) {
+ return result;
+ }
+
+ if (ob->pose != NULL) {
+ for (bPoseChannel *pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
+ result = constraint_list_find_from_target(&pchan->constraints, tgt);
+
+ if (result != NULL) {
+ return result;
+ }
+ }
+ }
+
+ return NULL;
+}
+
/* -------- Constraints and Proxies ------- */
/* Rescue all constraints tagged as being CONSTRAINT_PROXY_LOCAL (i.e. added to bone that's proxy-synced in this file) */
diff --git a/source/blender/blenlib/intern/math_rotation.c b/source/blender/blenlib/intern/math_rotation.c
index 29e7cf32ddc..17b395036a3 100644
--- a/source/blender/blenlib/intern/math_rotation.c
+++ b/source/blender/blenlib/intern/math_rotation.c
@@ -1872,19 +1872,24 @@ void dquat_to_mat4(float mat[4][4], const DualQuat *dq)
/* normalize */
len = sqrtf(dot_qtqt(q0, q0));
- if (len != 0.0f)
- mul_qt_fl(q0, 1.0f / len);
+ if (len != 0.0f) {
+ len = 1.0f / len;
+ }
+ mul_qt_fl(q0, len);
/* rotation */
quat_to_mat4(mat, q0);
/* translation */
t = dq->trans;
- mat[3][0] = 2.0f * (-t[0] * q0[1] + t[1] * q0[0] - t[2] * q0[3] + t[3] * q0[2]);
- mat[3][1] = 2.0f * (-t[0] * q0[2] + t[1] * q0[3] + t[2] * q0[0] - t[3] * q0[1]);
- mat[3][2] = 2.0f * (-t[0] * q0[3] - t[1] * q0[2] + t[2] * q0[1] + t[3] * q0[0]);
+ mat[3][0] = 2.0f * (-t[0] * q0[1] + t[1] * q0[0] - t[2] * q0[3] + t[3] * q0[2]) * len;
+ mat[3][1] = 2.0f * (-t[0] * q0[2] + t[1] * q0[3] + t[2] * q0[0] - t[3] * q0[1]) * len;
+ mat[3][2] = 2.0f * (-t[0] * q0[3] - t[1] * q0[2] + t[2] * q0[1] + t[3] * q0[0]) * len;
- /* note: this does not handle scaling */
+ /* scaling */
+ if (dq->scale_weight) {
+ mul_m4_m4m4(mat, mat, dq->scale);
+ }
}
void add_weighted_dq_dq(DualQuat *dqsum, const DualQuat *dq, float weight)
diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c
index 4a2a0b3efe2..530a4c8dfb8 100644
--- a/source/blender/blenloader/intern/readfile.c
+++ b/source/blender/blenloader/intern/readfile.c
@@ -3501,6 +3501,14 @@ static void direct_link_constraints(FileData *fd, ListBase *lb)
IDP_DirectLinkGroup_OrFree(&data->prop, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
break;
}
+ case CONSTRAINT_TYPE_ARMATURE:
+ {
+ bArmatureConstraint *data= con->data;
+
+ link_list(fd, &data->targets);
+
+ break;
+ }
case CONSTRAINT_TYPE_SPLINEIK:
{
bSplineIKConstraint *data= con->data;
diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c
index c3693de4866..919c6d05740 100644
--- a/source/blender/blenloader/intern/writefile.c
+++ b/source/blender/blenloader/intern/writefile.c
@@ -1536,6 +1536,18 @@ static void write_constraints(WriteData *wd, ListBase *conlist)
break;
}
+ case CONSTRAINT_TYPE_ARMATURE:
+ {
+ bArmatureConstraint *data = con->data;
+ bConstraintTarget *ct;
+
+ /* write targets */
+ for (ct = data->targets.first; ct; ct = ct->next) {
+ writestruct(wd, DATA, bConstraintTarget, 1, ct);
+ }
+
+ break;
+ }
case CONSTRAINT_TYPE_SPLINEIK:
{
bSplineIKConstraint *data = con->data;
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
index 960e49416c1..122f4f7d317 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
@@ -965,7 +965,7 @@ void DepsgraphRelationBuilder::build_constraints(ID *id,
opcode);
add_relation(target_key, constraint_op_key, cti->name);
/* if needs bbone shape, also reference handles */
- if (con->flag & CONSTRAINT_BBONE_SHAPE) {
+ if (BKE_constraint_target_uses_bbone(con, ct)) {
bPoseChannel *pchan = BKE_pose_channel_find_name(ct->tar->pose, ct->subtarget);
/* actually a bbone */
if (pchan && pchan->bone && pchan->bone->segments > 1) {
diff --git a/source/blender/editors/animation/keyframing.c b/source/blender/editors/animation/keyframing.c
index 39b1bed3ac1..863dd4efa82 100644
--- a/source/blender/editors/animation/keyframing.c
+++ b/source/blender/editors/animation/keyframing.c
@@ -810,6 +810,7 @@ static bool visualkey_can_use(PointerRNA *ptr, PropertyRNA *prop)
switch (con->type) {
/* multi-transform constraints */
case CONSTRAINT_TYPE_CHILDOF:
+ case CONSTRAINT_TYPE_ARMATURE:
return true;
case CONSTRAINT_TYPE_TRANSFORM:
case CONSTRAINT_TYPE_TRANSLIKE:
diff --git a/source/blender/editors/object/object_constraint.c b/source/blender/editors/object/object_constraint.c
index 4081c005132..212bf0ba018 100644
--- a/source/blender/editors/object/object_constraint.c
+++ b/source/blender/editors/object/object_constraint.c
@@ -430,6 +430,11 @@ static void test_constraint(Main *bmain, Object *owner, bPoseChannel *pchan, bCo
if (check_targets && cti && cti->get_constraint_targets) {
cti->get_constraint_targets(con, &targets);
+ /* constraints with empty target list that actually require targets */
+ if (!targets.first && ELEM(con->type, CONSTRAINT_TYPE_ARMATURE)) {
+ con->flag |= CONSTRAINT_DISABLE;
+ }
+
/* disable and clear constraints targets that are incorrect */
for (ct = targets.first; ct; ct = ct->next) {
/* general validity checks (for those constraints that need this) */
@@ -473,6 +478,18 @@ static void test_constraint(Main *bmain, Object *owner, bPoseChannel *pchan, bCo
}
}
}
+ else if (con->type == CONSTRAINT_TYPE_ARMATURE) {
+ if (ct->tar) {
+ if (ct->tar->type != OB_ARMATURE) {
+ ct->tar = NULL;
+ con->flag |= CONSTRAINT_DISABLE;
+ }
+ else if (!BKE_armature_find_bone_name(BKE_armature_from_object(ct->tar), ct->subtarget)) {
+ /* bone must exist in armature... */
+ con->flag |= CONSTRAINT_DISABLE;
+ }
+ }
+ }
}
/* free any temporary targets */
@@ -1245,7 +1262,9 @@ void ED_object_constraint_tag_update(Main *bmain, Object *ob, bConstraint *con)
BKE_pose_tag_update_constraint_flags(ob->pose);
}
- object_test_constraint(bmain, ob, con);
+ if (con) {
+ object_test_constraint(bmain, ob, con);
+ }
if (ob->type == OB_ARMATURE)
DEG_id_tag_update(&ob->id, OB_RECALC_DATA | OB_RECALC_OB);
diff --git a/source/blender/editors/transform/transform_conversions.c b/source/blender/editors/transform/transform_conversions.c
index d9857b50ec0..c0d4db0cc97 100644
--- a/source/blender/editors/transform/transform_conversions.c
+++ b/source/blender/editors/transform/transform_conversions.c
@@ -5599,6 +5599,7 @@ static bool constraints_list_needinv(TransInfo *t, ListBase *list)
if (ELEM(con->type,
CONSTRAINT_TYPE_FOLLOWPATH,
CONSTRAINT_TYPE_CLAMPTO,
+ CONSTRAINT_TYPE_ARMATURE,
CONSTRAINT_TYPE_OBJECTSOLVER,
CONSTRAINT_TYPE_FOLLOWTRACK))
{
diff --git a/source/blender/makesdna/DNA_constraint_types.h b/source/blender/makesdna/DNA_constraint_types.h
index fd98774e948..15555a2bd50 100644
--- a/source/blender/makesdna/DNA_constraint_types.h
+++ b/source/blender/makesdna/DNA_constraint_types.h
@@ -94,6 +94,8 @@ typedef struct bConstraintTarget {
short flag; /* runtime settings (for editor, etc.) */
short type; /* type of target (eConstraintObType) */
short rotOrder; /* rotation order for target (as defined in BLI_math.h) */
+ float weight; /* weight for armature deform */
+ char pad[4];
} bConstraintTarget;
/* bConstraintTarget -> flag */
@@ -180,6 +182,13 @@ typedef struct bSplineIKConstraint {
float bulge_smooth;
} bSplineIKConstraint;
+/* Armature Constraint */
+typedef struct bArmatureConstraint {
+ int flag; /* general settings/state indicators accessed by bitmapping */
+ char pad[4];
+
+ ListBase targets; /* a list of targets that this constraint has (bConstraintTarget-s) */
+} bArmatureConstraint;
/* Single-target subobject constraints --------------------- */
@@ -504,6 +513,7 @@ typedef enum eBConstraint_Types {
CONSTRAINT_TYPE_CAMERASOLVER = 27, /* Camera Solver Constraint */
CONSTRAINT_TYPE_OBJECTSOLVER = 28, /* Object Solver Constraint */
CONSTRAINT_TYPE_TRANSFORM_CACHE = 29, /* Transform Cache Constraint */
+ CONSTRAINT_TYPE_ARMATURE = 30, /* Armature Deform Constraint */
/* NOTE: no constraints are allowed to be added after this */
NUM_CONSTRAINT_TYPES
@@ -747,6 +757,13 @@ typedef enum eSplineIK_XZScaleModes {
CONSTRAINT_SPLINEIK_XZS_VOLUMETRIC = 3
} eSplineIK_XZScaleModes;
+/* bArmatureConstraint -> flag */
+typedef enum eArmature_Flags {
+ CONSTRAINT_ARMATURE_QUATERNION = (1<<0), /* use dual quaternion blending */
+ CONSTRAINT_ARMATURE_ENVELOPE = (1<<1), /* use envelopes */
+ CONSTRAINT_ARMATURE_CUR_LOCATION = (1<<2), /* use current bone location */
+} eArmature_Flags;
+
/* MinMax (floor) flags */
typedef enum eFloor_Flags {
MINMAX_STICKY = (1<<0),
diff --git a/source/blender/makesrna/intern/rna_constraint.c b/source/blender/makesrna/intern/rna_constraint.c
index 51d4d93586c..e2d4093b753 100644
--- a/source/blender/makesrna/intern/rna_constraint.c
+++ b/source/blender/makesrna/intern/rna_constraint.c
@@ -27,6 +27,9 @@
#include <stdlib.h>
#include "BLI_math.h"
+#include "BLI_listbase.h"
+
+#include "MEM_guardedalloc.h"
#include "BLT_translation.h"
@@ -106,6 +109,8 @@ const EnumPropertyItem rna_enum_constraint_type_items[] = {
"Custom constraint(s) written in Python (Not yet implemented)"}, */
{CONSTRAINT_TYPE_SHRINKWRAP, "SHRINKWRAP", ICON_CONSTRAINT, "Shrinkwrap",
"Restrict movements to surface of target mesh"},
+ {CONSTRAINT_TYPE_ARMATURE, "ARMATURE", ICON_CONSTRAINT, "Armature",
+ "Apply weight-blended transformation from multiple bones like the Armature modifier"},
{0, NULL, 0, NULL, NULL}
};
@@ -192,6 +197,8 @@ static StructRNA *rna_ConstraintType_refine(struct PointerRNA *ptr)
return &RNA_MaintainVolumeConstraint;
case CONSTRAINT_TYPE_PYTHON:
return &RNA_PythonConstraint;
+ case CONSTRAINT_TYPE_ARMATURE:
+ return &RNA_ArmatureConstraint;
case CONSTRAINT_TYPE_ACTION:
return &RNA_ActionConstraint;
case CONSTRAINT_TYPE_LOCKTRACK:
@@ -235,6 +242,17 @@ static StructRNA *rna_ConstraintType_refine(struct PointerRNA *ptr)
}
}
+static void rna_ConstraintTargetBone_target_set(PointerRNA *ptr, PointerRNA value)
+{
+ bConstraintTarget *tgt = (bConstraintTarget *)ptr->data;
+ Object *ob = value.data;
+
+ if (!ob || ob->type == OB_ARMATURE) {
+ id_lib_extern((ID *)ob);
+ tgt->tar = ob;
+ }
+}
+
static void rna_Constraint_name_set(PointerRNA *ptr, const char *value)
{
bConstraint *con = ptr->data;
@@ -260,10 +278,8 @@ static void rna_Constraint_name_set(PointerRNA *ptr, const char *value)
BKE_animdata_fix_paths_rename_all(NULL, "constraints", oldname, con->name);
}
-static char *rna_Constraint_path(PointerRNA *ptr)
+static char *rna_Constraint_do_compute_path(Object *ob, bConstraint *con)
{
- Object *ob = ptr->id.data;
- bConstraint *con = ptr->data;
bPoseChannel *pchan;
ListBase *lb = get_constraint_lb(ob, con, &pchan);
@@ -285,6 +301,55 @@ static char *rna_Constraint_path(PointerRNA *ptr)
}
}
+static char *rna_Constraint_path(PointerRNA *ptr)
+{
+ Object *ob = ptr->id.data;
+ bConstraint *con = ptr->data;
+
+ return rna_Constraint_do_compute_path(ob, con);
+}
+
+static bConstraint* rna_constraint_from_target(PointerRNA *ptr)
+{
+ Object *ob = ptr->id.data;
+ bConstraintTarget *tgt = ptr->data;
+
+ return BKE_constraint_find_from_target(ob, tgt);
+}
+
+static char *rna_ConstraintTarget_path(PointerRNA *ptr)
+{
+ Object *ob = ptr->id.data;
+ bConstraintTarget *tgt = ptr->data;
+ bConstraint *con = rna_constraint_from_target(ptr);
+ int index = -1;
+
+ if (con != NULL) {
+ if (con->type == CONSTRAINT_TYPE_ARMATURE) {
+ bArmatureConstraint *acon = (bArmatureConstraint*)con->data;
+ index = BLI_findindex(&acon->targets, tgt);
+ }
+ else if (con->type == CONSTRAINT_TYPE_PYTHON) {
+ bPythonConstraint *pcon = (bPythonConstraint*)con->data;
+ index = BLI_findindex(&pcon->targets, tgt);
+ }
+ }
+
+ if (index >= 0) {
+ char *con_path = rna_Constraint_do_compute_path(ob, con);
+ char *result = BLI_sprintfN("%s.targets[%d]", con_path, index);
+
+ MEM_freeN(con_path);
+ return result;
+ }
+ else {
+ printf("%s: internal error, constraint '%s' of object '%s' does not contain the target\n",
+ __func__, con->name, ob->id.name);
+ }
+
+ return NULL;
+}
+
static void rna_Constraint_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr)
{
ED_object_constraint_tag_update(bmain, ptr->id.data, ptr->data);
@@ -295,6 +360,16 @@ static void rna_Constraint_dependency_update(Main *bmain, Scene *UNUSED(scene),
ED_object_constraint_dependency_tag_update(bmain, ptr->id.data, ptr->data);
}
+static void rna_ConstraintTarget_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr)
+{
+ ED_object_constraint_tag_update(bmain, ptr->id.data, rna_constraint_from_target(ptr));
+}
+
+static void rna_ConstraintTarget_dependency_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr)
+{
+ ED_object_constraint_dependency_tag_update(bmain, ptr->id.data, rna_constraint_from_target(ptr));
+}
+
static void rna_Constraint_influence_update(Main *bmain, Scene *scene, PointerRNA *ptr)
{
Object *ob = ptr->id.data;
@@ -360,6 +435,42 @@ static const EnumPropertyItem *rna_Constraint_target_space_itemf(bContext *UNUSE
return space_object_items;
}
+static bConstraintTarget *rna_ArmatureConstraint_target_new(ID *id, bConstraint *con, Main *bmain)
+{
+ bArmatureConstraint *acon = (bArmatureConstraint*)con->data;
+ bConstraintTarget *tgt = MEM_callocN(sizeof(bConstraintTarget), "Constraint Target");
+
+ tgt->weight = 1.0f;
+ BLI_addtail(&acon->targets, tgt);
+
+ ED_object_constraint_dependency_tag_update(bmain, (Object*)id, con);
+ return tgt;
+}
+
+static void rna_ArmatureConstraint_target_remove(ID *id, bConstraint *con, Main *bmain, ReportList *reports, PointerRNA *target_ptr)
+{
+ bArmatureConstraint *acon = (bArmatureConstraint*)con->data;
+ bConstraintTarget *tgt = target_ptr->data;
+
+ if (BLI_findindex(&acon->targets, tgt) < 0) {
+ BKE_reportf(reports, RPT_ERROR, "Target is not in the constraint target list");
+ return;
+ }
+
+ BLI_freelinkN(&acon->targets, tgt);
+
+ ED_object_constraint_dependency_tag_update(bmain, (Object*)id, con);
+}
+
+static void rna_ArmatureConstraint_target_clear(ID *id, bConstraint *con, Main *bmain)
+{
+ bArmatureConstraint *acon = (bArmatureConstraint*)con->data;
+
+ BLI_freelistN(&acon->targets);
+
+ ED_object_constraint_dependency_tag_update(bmain, (Object*)id, con);
+}
+
static void rna_ActionConstraint_minmax_range(PointerRNA *ptr, float *min, float *max,
float *UNUSED(softmin), float *UNUSED(softmax))
{
@@ -564,16 +675,58 @@ static void rna_def_constraint_target_common(StructRNA *srna)
static void rna_def_constrainttarget(BlenderRNA *brna)
{
StructRNA *srna;
+ PropertyRNA *prop;
srna = RNA_def_struct(brna, "ConstraintTarget", NULL);
RNA_def_struct_ui_text(srna, "Constraint Target", "Target object for multi-target constraints");
+ RNA_def_struct_path_func(srna, "rna_ConstraintTarget_path");
RNA_def_struct_sdna(srna, "bConstraintTarget");
- rna_def_constraint_target_common(srna);
+ prop = RNA_def_property(srna, "target", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "tar");
+ RNA_def_property_ui_text(prop, "Target", "Target object");
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_STATIC);
+ RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_ConstraintTarget_dependency_update");
+
+ prop = RNA_def_property(srna, "subtarget", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_sdna(prop, NULL, "subtarget");
+ RNA_def_property_ui_text(prop, "Sub-Target", "Armature bone, mesh or lattice vertex group, ...");
+ RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_ConstraintTarget_dependency_update");
/* space, flag and type still to do */
}
+static void rna_def_constrainttarget_bone(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "ConstraintTargetBone", NULL);
+ RNA_def_struct_ui_text(srna, "Constraint Target Bone", "Target bone for multi-target constraints");
+ RNA_def_struct_path_func(srna, "rna_ConstraintTarget_path");
+ RNA_def_struct_sdna(srna, "bConstraintTarget");
+
+ prop = RNA_def_property(srna, "target", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "tar");
+ RNA_def_property_ui_text(prop, "Target", "Target armature");
+ RNA_def_property_pointer_funcs(prop, NULL, "rna_ConstraintTargetBone_target_set", NULL, "rna_Armature_object_poll");
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_STATIC);
+ RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_ConstraintTarget_dependency_update");
+
+ prop = RNA_def_property(srna, "subtarget", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_sdna(prop, NULL, "subtarget");
+ RNA_def_property_ui_text(prop, "Sub-Target", "Target armature bone");
+ RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_ConstraintTarget_dependency_update");
+
+ prop = RNA_def_property(srna, "weight", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "weight");
+ RNA_def_property_range(prop, 0.0f, 1.0f);
+ RNA_def_property_ui_text(prop, "Blend Weight", "Blending weight of this bone");
+ RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_ConstraintTarget_update");
+}
+
static void rna_def_constraint_childof(BlenderRNA *brna)
{
StructRNA *srna;
@@ -673,6 +826,70 @@ static void rna_def_constraint_python(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Script Error", "The linked Python script has thrown an error");
}
+
+static void rna_def_constraint_armature_deform_targets(BlenderRNA *brna, PropertyRNA *cprop)
+{
+ StructRNA *srna;
+ FunctionRNA *func;
+ PropertyRNA *parm;
+
+ RNA_def_property_srna(cprop, "ArmatureConstraintTargets");
+ srna = RNA_def_struct(brna, "ArmatureConstraintTargets", NULL);
+ RNA_def_struct_sdna(srna, "bConstraint");
+ RNA_def_struct_ui_text(srna, "Armature Deform Constraint Targets", "Collection of target bones and weights");
+
+ func = RNA_def_function(srna, "new", "rna_ArmatureConstraint_target_new");
+ RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_MAIN);
+ RNA_def_function_ui_description(func, "Add a new target to the constraint");
+ parm = RNA_def_pointer(func, "target", "ConstraintTargetBone", "", "New target bone");
+ RNA_def_function_return(func, parm);
+
+ func = RNA_def_function(srna, "remove", "rna_ArmatureConstraint_target_remove");
+ RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_MAIN | FUNC_USE_REPORTS);
+ RNA_def_function_ui_description(func, "Delete target from the constraint");
+ parm = RNA_def_pointer(func, "target", "ConstraintTargetBone", "", "Target to remove");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR);
+ RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, 0);
+
+ func = RNA_def_function(srna, "clear", "rna_ArmatureConstraint_target_clear");
+ RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_MAIN);
+ RNA_def_function_ui_description(func, "Delete all targets from object");
+}
+
+static void rna_def_constraint_armature_deform(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "ArmatureConstraint", "Constraint");
+ RNA_def_struct_ui_text(srna, "Armature Constraint", "Applies transformations done by the Armature modifier");
+ RNA_def_struct_sdna_from(srna, "bArmatureConstraint", "data");
+
+ prop = RNA_def_property(srna, "targets", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_collection_sdna(prop, NULL, "targets", NULL);
+ RNA_def_property_struct_type(prop, "ConstraintTargetBone");
+ RNA_def_property_ui_text(prop, "Targets", "Target Bones");
+ rna_def_constraint_armature_deform_targets(brna, prop);
+
+ prop = RNA_def_property(srna, "use_deform_preserve_volume", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", CONSTRAINT_ARMATURE_QUATERNION);
+ RNA_def_property_ui_text(prop, "Preserve Volume", "Deform rotation interpolation with quaternions");
+ RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update");
+
+ prop = RNA_def_property(srna, "use_bone_envelopes", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", CONSTRAINT_ARMATURE_ENVELOPE);
+ RNA_def_property_ui_text(prop, "Use Envelopes",
+ "Multiply weights by envelope for all bones, instead of acting like Vertex Group based blending. "
+ "The specified weights are still used, and only the listed bones are considered");
+ RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update");
+
+ prop = RNA_def_property(srna, "use_current_location", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", CONSTRAINT_ARMATURE_CUR_LOCATION);
+ RNA_def_property_ui_text(prop, "Use Current Location",
+ "Use the current bone location for envelopes and choosing B-Bone segments instead of rest position");
+ RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update");
+}
+
static void rna_def_constraint_kinematic(BlenderRNA *brna)
{
StructRNA *srna;
@@ -2465,9 +2682,11 @@ void RNA_def_constraint(BlenderRNA *brna)
/* pointers */
rna_def_constrainttarget(brna);
+ rna_def_constrainttarget_bone(brna);
rna_def_constraint_childof(brna);
rna_def_constraint_python(brna);
+ rna_def_constraint_armature_deform(brna);
rna_def_constraint_stretch_to(brna);
rna_def_constraint_follow_path(brna);
rna_def_constraint_locked_track(brna);