From e7b4d36fd6a57fb88fdca861dc251a6a8bdf6355 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 14 Dec 2009 20:56:19 +0000 Subject: [PATCH] * new metarig type for the durian dragon leg (original rig by Cessen) * option to roll the delta of the arm rig. * fix to copy metarig type * renamed EditBone.align() --> EditBone.align_roll() * Added EditBone.align_orientation(other) * Added bone.vector: same as (bone.tail - bone.head) --- release/scripts/modules/bpy_types.py | 15 +- .../modules/rigify/arm_biped_generic.py | 3 + release/scripts/modules/rigify/copy.py | 10 +- .../modules/rigify/leg_biped_generic.py | 21 +- .../modules/rigify/leg_quadruped_generic.py | 225 ++++++++++++++++++ release/scripts/modules/rigify/neck_flex.py | 2 +- .../modules/rigify/spine_pivot_flex.py | 4 +- release/scripts/modules/rigify_utils.py | 11 + release/scripts/ui/properties_data_bone.py | 1 + .../ui/properties_object_constraint.py | 1 + release/test/pep8.py | 6 +- .../makesrna/intern/rna_armature_api.c | 4 +- 12 files changed, 275 insertions(+), 28 deletions(-) create mode 100644 release/scripts/modules/rigify/leg_quadruped_generic.py diff --git a/release/scripts/modules/bpy_types.py b/release/scripts/modules/bpy_types.py index 239990e2bd5..69618d77ecb 100644 --- a/release/scripts/modules/bpy_types.py +++ b/release/scripts/modules/bpy_types.py @@ -94,13 +94,17 @@ class _GenericBone: @property def length(self): - return (self.head - self.tail).length + return self.vector.length @length.setter def length(self, value): """The distance from head to tail""" self.tail = self.head + ((self.tail - self.head).normalize() * value) + @property + def vector(self): + return (self.tail - self.head) + @property def children(self): return [child for child in self._other_bones if child.parent == self] @@ -172,6 +176,15 @@ class Bone(StructRNA, _GenericBone): class EditBone(StructRNA, _GenericBone): __slots__ = () + def align_orientation(self, other): + ''' + Align this bone to another by moving its tail and settings its roll + the length of the other bone is not used. + ''' + vec = other.vector.normalize() * self.length + self.tail = self.head + vec + self.roll = other.roll + def ord_ind(i1, i2): if i1 < i2: diff --git a/release/scripts/modules/rigify/arm_biped_generic.py b/release/scripts/modules/rigify/arm_biped_generic.py index 1c4c6fd2a6b..dc45ccf8ae8 100644 --- a/release/scripts/modules/rigify/arm_biped_generic.py +++ b/release/scripts/modules/rigify/arm_biped_generic.py @@ -19,6 +19,7 @@ # import bpy +from math import radians from rigify import RigifyError, get_layer_dict from rigify_utils import bone_class_instance, copy_bone_simple, add_pole_target_bone, add_stretch_to, blend_bone_list, get_side_name, get_base_name from rna_prop_ui import rna_idprop_ui_prop_get @@ -213,6 +214,8 @@ def fk(obj, definitions, base_names, options): ex.hand_delta = ex.hand_delta_e.name ex.hand_delta_e.length *= 0.5 ex.hand_delta_e.connected = False + if "hand_roll" in options: + ex.hand_delta_e.roll += radians(options["hand_roll"]) fk_chain.hand_e.connected = False fk_chain.hand_e.parent = ex.hand_delta_e diff --git a/release/scripts/modules/rigify/copy.py b/release/scripts/modules/rigify/copy.py index d052ce6f32b..11c16d2d197 100644 --- a/release/scripts/modules/rigify/copy.py +++ b/release/scripts/modules/rigify/copy.py @@ -53,25 +53,25 @@ def main(obj, bone_definition, base_names, options): cp = bone_class_instance(obj, ["cpy"]) cp.cpy_e = copy_bone_simple(arm, mt.cpy, base_names[mt.cpy], parent=True) cp.cpy = cp.cpy_e.name - + bpy.ops.object.mode_set(mode='OBJECT') cp.update() mt.update() if not cp.cpy_b.connected: - con = cp.cpy_p.constraints.new('COPY_LOCATION') + con = mt.cpy_p.constraints.new('COPY_LOCATION') con.target = obj - con.subtarget = mt.cpy + con.subtarget = cp.cpy - con = cp.cpy_p.constraints.new('COPY_ROTATION') + con = mt.cpy_p.constraints.new('COPY_ROTATION') con.target = obj con.subtarget = cp.cpy con = mt.cpy_p.constraints.new('COPY_SCALE') con.target = obj con.subtarget = cp.cpy - + # Rotation mode and axis locks cp.cpy_p.rotation_mode = mt.cpy_p.rotation_mode cp.cpy_p.lock_location = tuple(mt.cpy_p.lock_location) diff --git a/release/scripts/modules/rigify/leg_biped_generic.py b/release/scripts/modules/rigify/leg_biped_generic.py index 2c5481be1b7..2826a6b7b29 100644 --- a/release/scripts/modules/rigify/leg_biped_generic.py +++ b/release/scripts/modules/rigify/leg_biped_generic.py @@ -128,22 +128,16 @@ def metarig_definition(obj, orig_bone_name): def ik(obj, bone_definition, base_names, options): arm = obj.data - # setup the existing bones + # setup the existing bones, use names from METARIG_NAMES mt_chain = bone_class_instance(obj, ["thigh", "shin", "foot", "toe"]) mt = bone_class_instance(obj, ["hips", "heel"]) + + mt.attr_initialize(METARIG_NAMES, bone_definition) + mt_chain.attr_initialize(METARIG_NAMES, bone_definition) + # children of ik_foot ik = bone_class_instance(obj, ["foot", "foot_roll", "foot_roll_01", "foot_roll_02", "knee_target"]) - # XXX - duplicate below - for bone_class in (mt, mt_chain): - for attr in bone_class.attr_names: - i = METARIG_NAMES.index(attr) - ebone = arm.edit_bones[bone_definition[i]] - setattr(bone_class, attr, ebone.name) - bone_class.update() - # XXX - end dupe - - # Make a new chain ik_chain = mt_chain.copy(to_fmt="MCH-%s", base_names=base_names) @@ -165,7 +159,7 @@ def ik(obj, bone_definition, base_names, options): # foot roll: heel pointing backwards, half length ik.foot_roll_e = copy_bone_simple(arm, mt.heel, base_foot_name + "_roll" + get_side_name(base_names[mt_chain.foot])) ik.foot_roll = ik.foot_roll_e.name - ik.foot_roll_e.tail = ik.foot_roll_e.head + (ik.foot_roll_e.head - ik.foot_roll_e.tail) / 2.0 + ik.foot_roll_e.tail = ik.foot_roll_e.head + ik.foot_roll_e.vector / 2.0 ik.foot_roll_e.parent = ik.foot_e # heel is disconnected # heel pointing forwards to the toe base, parent of the following 2 bones @@ -208,7 +202,7 @@ def ik(obj, bone_definition, base_names, options): # roll the bone to point up... could also point in the same direction as ik.foot_roll # ik.foot_roll_02_e.matrix * Vector(0.0, 0.0, 1.0) # ACK!, no rest matrix in editmode - ik.foot_roll_01_e.align((0.0, 0.0, -1.0)) + ik.foot_roll_01_e.align_roll((0.0, 0.0, -1.0)) bpy.ops.object.mode_set(mode='OBJECT') @@ -355,7 +349,6 @@ def fk(obj, bone_definition, base_names, options): mod.coefficients[1] = -1.0 - # last step setup layers layers = get_layer_dict(options) lay = layers["fk"] diff --git a/release/scripts/modules/rigify/leg_quadruped_generic.py b/release/scripts/modules/rigify/leg_quadruped_generic.py new file mode 100644 index 00000000000..4ad90b959e1 --- /dev/null +++ b/release/scripts/modules/rigify/leg_quadruped_generic.py @@ -0,0 +1,225 @@ +# ##### 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# + +import bpy +from rigify import RigifyError, get_layer_dict +from rigify_utils import bone_class_instance, copy_bone_simple, blend_bone_list, get_side_name, get_base_name, add_pole_target_bone +from rna_prop_ui import rna_idprop_ui_prop_get +from Mathutils import Vector + +METARIG_NAMES = "hips", "thigh", "shin", "foot", "toe" + + +def metarig_template(): + # generated by rigify.write_meta_rig + bpy.ops.object.mode_set(mode='EDIT') + obj = bpy.context.active_object + arm = obj.data + bone = arm.edit_bones.new('body') + bone.head[:] = -0.3000, -1.0000, 0.0000 + bone.tail[:] = -0.3000, -1.0000, 1.0000 + bone.roll = 0.0000 + bone.connected = False + bone = arm.edit_bones.new('thigh') + bone.head[:] = 0.0000, 0.0000, -0.0000 + bone.tail[:] = 0.3351, -0.8690, -1.3903 + bone.roll = -0.4656 + bone.connected = False + bone.parent = arm.edit_bones['body'] + bone = arm.edit_bones.new('shin') + bone.head[:] = 0.3351, -0.8690, -1.3903 + bone.tail[:] = 0.2943, -0.0179, -2.4026 + bone.roll = -0.2024 + bone.connected = True + bone.parent = arm.edit_bones['thigh'] + bone = arm.edit_bones.new('foot') + bone.head[:] = 0.2943, -0.0179, -2.4026 + bone.tail[:] = 0.3830, -0.1995, -3.1531 + bone.roll = -0.3766 + bone.connected = True + bone.parent = arm.edit_bones['shin'] + bone = arm.edit_bones.new('toe') + bone.head[:] = 0.3830, -0.1995, -3.1531 + bone.tail[:] = 0.4724, -0.5126, -3.1531 + bone.roll = 0.0000 + bone.connected = True + bone.parent = arm.edit_bones['foot'] + + bpy.ops.object.mode_set(mode='OBJECT') + pbone = obj.pose.bones['thigh'] + pbone['type'] = 'leg_quadruped_generic' + + +def metarig_definition(obj, orig_bone_name): + ''' + The bone given is the first in a chain + Expects a chain of at least 3 children. + eg. + thigh -> shin -> foot -> [toe, heel] + ''' + + bone_definition = [] + + orig_bone = obj.data.bones[orig_bone_name] + orig_bone_parent = orig_bone.parent + + if orig_bone_parent is None: + raise RigifyError("expected the thigh bone to have a parent hip bone") + + bone_definition.append(orig_bone_parent.name) + bone_definition.append(orig_bone.name) + + + bone = orig_bone + chain = 0 + while chain < 3: # first 2 bones only have 1 child + children = bone.children + + if len(children) != 1: + raise RigifyError("expected the thigh bone to have 3 children without a fork") + bone = children[0] + bone_definition.append(bone.name) # shin, foot + chain += 1 + + if len(bone_definition) != len(METARIG_NAMES): + raise RigifyError("internal problem, expected %d bones" % len(METARIG_NAMES)) + + return bone_definition + + +def ik(obj, bone_definition, base_names, options): + arm = obj.data + + # setup the existing bones, use names from METARIG_NAMES + mt = bone_class_instance(obj, ["hips"]) + mt_chain = bone_class_instance(obj, ["thigh", "shin", "foot", "toe"]) + + mt.attr_initialize(METARIG_NAMES, bone_definition) + mt_chain.attr_initialize(METARIG_NAMES, bone_definition) + + ik_chain = mt_chain.copy(to_fmt="%s", base_names=base_names) + + ik_chain.thigh_e.connected = False + ik_chain.thigh_e.parent = mt.hips_e + + ik_chain.foot_e.parent = None + ik_chain.rename("foot", ik_chain.foot + "_ik") + + # keep the foot_ik as the parent + ik_chain.toe_e.connected = False + + # must be after disconnecting the toe + ik_chain.foot_e.align_orientation(mt_chain.toe_e) + + # children of ik_foot + ik = bone_class_instance(obj, ["foot", "foot_roll", "foot_roll_01", "foot_roll_02", "knee_target", "foot_target"]) + + ik.knee_target = add_pole_target_bone(obj, mt_chain.shin, "knee_target") #XXX - pick a better name + ik.update() + ik.knee_target_e.parent = mt.hips_e + + # foot roll is an interesting one! + # plot a vector from the toe bones head, bactwards to the length of the foot + # then align it with the foot but reverse direction. + ik.foot_roll_e = copy_bone_simple(arm, mt_chain.toe, base_names[mt_chain.foot] + "_roll") + ik.foot_roll = ik.foot_roll_e.name + ik.foot_roll_e.parent = ik_chain.foot_e + ik.foot_roll_e.translate( - (mt_chain.toe_e.vector.normalize() * mt_chain.foot_e.length)) + ik.foot_roll_e.align_orientation(mt_chain.foot_e) + ik.foot_roll_e.tail = ik.foot_roll_e.head - ik.foot_roll_e.vector # flip + ik.foot_roll_e.align_roll(mt_chain.foot_e.matrix.rotationPart() * Vector(0.0, 0.0, -1.0)) + + # MCH-foot + ik.foot_roll_01_e = copy_bone_simple(arm, mt_chain.foot, "MCH-" + base_names[mt_chain.foot]) + ik.foot_roll_01 = ik.foot_roll_01_e.name + ik.foot_roll_01_e.parent = ik_chain.foot_e + ik.foot_roll_01_e.head, ik.foot_roll_01_e.tail = mt_chain.foot_e.tail, mt_chain.foot_e.head + ik.foot_roll_01_e.roll = ik.foot_roll_e.roll + + # ik_target, child of MCH-foot + ik.foot_target_e = copy_bone_simple(arm, mt_chain.foot, base_names[mt_chain.foot] + "_ik_target") + ik.foot_target = ik.foot_target_e.name + ik.foot_target_e.parent = ik.foot_roll_01_e + ik.foot_target_e.align_orientation(ik_chain.foot_e) + ik.foot_target_e.length = ik_chain.foot_e.length / 2.0 + ik.foot_target_e.connected = True + + # MCH-foot.02 child of MCH-foot + ik.foot_roll_02_e = copy_bone_simple(arm, mt_chain.foot, "MCH-%s_02" % base_names[mt_chain.foot]) + ik.foot_roll_02 = ik.foot_roll_02_e.name + ik.foot_roll_02_e.parent = ik.foot_roll_01_e + + + bpy.ops.object.mode_set(mode='OBJECT') + + mt.update() + mt_chain.update() + ik.update() + ik_chain.update() + + # simple constraining of orig bones + con = mt_chain.thigh_p.constraints.new('COPY_ROTATION') + con.target = obj + con.subtarget = ik_chain.thigh + + con = mt_chain.shin_p.constraints.new('COPY_ROTATION') + con.target = obj + con.subtarget = ik_chain.shin + + con = mt_chain.foot_p.constraints.new('COPY_ROTATION') + con.target = obj + con.subtarget = ik.foot_roll_02 + + con = mt_chain.toe_p.constraints.new('COPY_ROTATION') + con.target = obj + con.subtarget = ik_chain.toe + + # others... + con = ik.foot_roll_01_p.constraints.new('COPY_ROTATION') + con.target = obj + con.subtarget = ik.foot_roll + + + # IK + con = ik_chain.shin_p.constraints.new('IK') + con.chain_length = 2 + con.iterations = 500 + con.pole_angle = -90.0 # XXX - in deg! + con.use_tail = True + con.use_stretch = True + con.use_target = True + con.use_rotation = False + con.weight = 1.0 + + con.target = obj + con.subtarget = ik.foot_target + + con.pole_target = obj + con.pole_subtarget = ik.knee_target + + + bpy.ops.object.mode_set(mode='EDIT') + + return None, ik_chain.thigh, ik_chain.shin, ik_chain.foot, ik_chain.toe + + +def main(obj, bone_definition, base_names, options): + bones_ik = ik(obj, bone_definition, base_names, options) + return bones_ik diff --git a/release/scripts/modules/rigify/neck_flex.py b/release/scripts/modules/rigify/neck_flex.py index 137fb626bcf..be548249433 100644 --- a/release/scripts/modules/rigify/neck_flex.py +++ b/release/scripts/modules/rigify/neck_flex.py @@ -167,7 +167,7 @@ def main(obj, bone_definition, base_names, options): # dont store parent names, re-reference as each chain bones parent. neck_e_parent = arm.edit_bones.new("MCH-rot_%s" % base_names[getattr(mt_chain, attr)]) neck_e_parent.head = neck_e.head - neck_e_parent.tail = neck_e.head + ((mt.head_e.tail - mt.head_e.head).normalize() * neck_chain_segment_length / 2.0) + neck_e_parent.tail = neck_e.head + (mt.head_e.vector.normalize() * neck_chain_segment_length / 2.0) neck_e_parent.roll = mt.head_e.roll orig_parent = neck_e.parent diff --git a/release/scripts/modules/rigify/spine_pivot_flex.py b/release/scripts/modules/rigify/spine_pivot_flex.py index ca954eeef4f..848f54ef4e8 100644 --- a/release/scripts/modules/rigify/spine_pivot_flex.py +++ b/release/scripts/modules/rigify/spine_pivot_flex.py @@ -244,7 +244,7 @@ def main(obj, bone_definition, base_names, options): # dont store parent names, re-reference as each chain bones parent. spine_e_parent = arm.edit_bones.new("MCH-rot_%s" % child_name_orig) spine_e_parent.head = spine_e.head - spine_e_parent.tail = spine_e.head + ((mt.ribcage_e.tail - mt.ribcage_e.head).normalize() * spine_chain_segment_length / 2.0) + spine_e_parent.tail = spine_e.head + (mt.ribcage_e.vector.normalize() * spine_chain_segment_length / 2.0) spine_e_parent.roll = mt.ribcage_e.roll @@ -508,7 +508,7 @@ def main(obj, bone_definition, base_names, options): lay = layers["main"] for attr in df.attr_names: getattr(df, attr + "_b").layer = lay - for attr in rv_chain .attr_names: + for attr in rv_chain.attr_names: getattr(rv_chain , attr + "_b").layer = lay # no support for blending chains diff --git a/release/scripts/modules/rigify_utils.py b/release/scripts/modules/rigify_utils.py index f4694023f12..371792a3633 100644 --- a/release/scripts/modules/rigify_utils.py +++ b/release/scripts/modules/rigify_utils.py @@ -353,6 +353,7 @@ def bone_class_instance(obj, slots, name="BoneContainer"): class_dict = { \ "obj": obj, \ "attr_names": attr_names, \ + "attr_initialize": _bone_class_instance_attr_initialize, \ "update": _bone_class_instance_update, \ "rename": _bone_class_instance_rename, \ "names": _bone_class_instance_names, \ @@ -380,6 +381,16 @@ def auto_class_instance(slots, name="ContainerClass", class_dict=None): return auto_class(slots, name, class_dict)() +def _bone_class_instance_attr_initialize(self, attr_names, bone_names): + ''' Initializes attributes, both lists must be aligned + ''' + for attr in self.attr_names: + i = attr_names.index(attr) + setattr(self, attr, bone_names[i]) + + self.update() + + def _bone_class_instance_update(self): ''' Re-Assigns bones from the blender data ''' diff --git a/release/scripts/ui/properties_data_bone.py b/release/scripts/ui/properties_data_bone.py index 121fc3a5db3..99561a026e3 100644 --- a/release/scripts/ui/properties_data_bone.py +++ b/release/scripts/ui/properties_data_bone.py @@ -226,6 +226,7 @@ class BONE_PT_display(BoneButtonsPanel): col.label(text="Custom Shape:") col.prop(pchan, "custom_shape", text="") + class BONE_PT_inverse_kinematics(BoneButtonsPanel): bl_label = "Inverse Kinematics" bl_default_closed = True diff --git a/release/scripts/ui/properties_object_constraint.py b/release/scripts/ui/properties_object_constraint.py index dd874bf52a0..12a12ff40dc 100644 --- a/release/scripts/ui/properties_object_constraint.py +++ b/release/scripts/ui/properties_object_constraint.py @@ -735,6 +735,7 @@ class OBJECT_PT_constraints(ConstraintButtonsPanel): for con in ob.constraints: self.draw_constraint(context, con) + class BONE_PT_constraints(ConstraintButtonsPanel): bl_label = "Bone Constraints" bl_context = "bone_constraint" diff --git a/release/test/pep8.py b/release/test/pep8.py index 0c0c013ad84..68d09c2ee76 100644 --- a/release/test/pep8.py +++ b/release/test/pep8.py @@ -29,9 +29,9 @@ import os # sudo pip install pep8 # # in debian install pylint pyflakes pep8 with apt-get/aptitude/etc -# +# # on *nix run -# python release/test/pep8.py > tmp.err 2>&1 +# python release/test/pep8.py > pep8_error.txt 2>&1 # how many lines to read into the file, pep8 comment # should be directly after the licence header, ~20 in most cases @@ -67,7 +67,7 @@ def main(): if pep8_type: # so we can batch them for each tool. - files.append((f, pep8_type)) + files.append((os.path.abspath(f), pep8_type)) else: if not [None for prefix in SKIP_PREFIX if f.startswith(prefix)]: files_skip.append(f) diff --git a/source/blender/makesrna/intern/rna_armature_api.c b/source/blender/makesrna/intern/rna_armature_api.c index 31bd7275487..22bd7313166 100644 --- a/source/blender/makesrna/intern/rna_armature_api.c +++ b/source/blender/makesrna/intern/rna_armature_api.c @@ -40,7 +40,7 @@ #include "ED_armature.h" -void rna_EditBone_align(EditBone *ebo, float *no) +void rna_EditBone_align_roll(EditBone *ebo, float *no) { if(!is_zero_v3(no)) { float normal[3]; @@ -57,7 +57,7 @@ void RNA_api_armature_edit_bone(StructRNA *srna) FunctionRNA *func; PropertyRNA *parm; - func= RNA_def_function(srna, "align", "rna_EditBone_align"); + func= RNA_def_function(srna, "align_roll", "rna_EditBone_align_roll"); RNA_def_function_ui_description(func, "Align the bone to a localspace vector."); parm= RNA_def_float_vector(func, "vector", 3, NULL, -FLT_MAX, FLT_MAX, "Vector", "", -FLT_MAX, FLT_MAX); RNA_def_property_flag(parm, PROP_REQUIRED);