diff options
-rw-r--r-- | release/scripts/startup/bl_operators/__init__.py | 1 | ||||
-rw-r--r-- | release/scripts/startup/bl_operators/constraint.py | 76 | ||||
-rw-r--r-- | release/scripts/startup/bl_ui/properties_constraint.py | 40 | ||||
-rw-r--r-- | source/blender/blenkernel/BKE_constraint.h | 4 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/constraint.c | 248 | ||||
-rw-r--r-- | source/blender/blenlib/intern/math_rotation.c | 17 | ||||
-rw-r--r-- | source/blender/blenloader/intern/readfile.c | 8 | ||||
-rw-r--r-- | source/blender/blenloader/intern/writefile.c | 12 | ||||
-rw-r--r-- | source/blender/depsgraph/intern/builder/deg_builder_relations.cc | 2 | ||||
-rw-r--r-- | source/blender/editors/animation/keyframing.c | 1 | ||||
-rw-r--r-- | source/blender/editors/object/object_constraint.c | 21 | ||||
-rw-r--r-- | source/blender/editors/transform/transform_conversions.c | 1 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_constraint_types.h | 17 | ||||
-rw-r--r-- | source/blender/makesrna/intern/rna_constraint.c | 227 |
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); |