[Bf-blender-cvs] [798cdaeeb69] blender2.8: Implement an Armature constraint that mimics the modifier.

Alexander Gavrilov noreply at git.blender.org
Tue Nov 6 09:37:40 CET 2018


Commit: 798cdaeeb6927cb9ca42597fa23845eac04c02b2
Author: Alexander Gavrilov
Date:   Sun Jul 15 20:39:02 2018 +0300
Branches: blender2.8
https://developer.blender.org/rB798cdaeeb6927cb9ca42597fa23845eac04c02b2

Implement an Armature constraint that mimics the modifier.

The main use one can imagine for this is adding tweak controls to
parts of a model that are already deformed by multiple other major
bones. It is natural to expect such locations to deform as if the
tweaks aren't there by default; however currently there is no easy
way to make a bone follow multiple other bones.

This adds a new constraint that implements the math behind the Armature
modifier, with support for explicit weights, bone envelopes, and dual
quaternion blending. It can also access bones from multiple armatures
at the same time (mainly because it's easier to code it that way.)

This also fixes dquat_to_mat4, which wasn't used anywhere before.

Differential Revision: https://developer.blender.org/D3664

===================================================================

M	release/scripts/startup/bl_operators/__init__.py
A	release/scripts/startup/bl_operators/constraint.py
M	release/scripts/startup/bl_ui/properties_constraint.py
M	source/blender/blenkernel/BKE_constraint.h
M	source/blender/blenkernel/intern/constraint.c
M	source/blender/blenlib/intern/math_rotation.c
M	source/blender/blenloader/intern/readfile.c
M	source/blender/blenloader/intern/writefile.c
M	source/blender/depsgraph/intern/builder/deg_builder_relations.cc
M	source/blender/editors/animation/keyframing.c
M	source/blender/editors/object/object_constraint.c
M	source/blender/editors/transform/transform_conversions.c
M	source/blender/makesdna/DNA_constraint_types.h
M	source/blender/makesrna/intern/rna_constraint.c

===================================================================

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

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list