Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/rigify
diff options
context:
space:
mode:
authorNathan Vegdahl <cessen@cessen.com>2013-03-01 04:17:14 +0400
committerNathan Vegdahl <cessen@cessen.com>2013-03-01 04:17:14 +0400
commit3e316aace81f6305f61e9ac1d87ebc1717079429 (patch)
tree1ca9d980b3ed359d67534f7bd7baa9f376d2b3ad /rigify
parent38cc068b90491ddd4bb2d8dc5fefa251cfd64ba4 (diff)
Rigify: significant upgrade to arm and leg rigs, and misc changes/bugfixes.
Arm/leg rig upgrades: - Arms and legs no longer scale with their parents, which was problematic when e.g. the torso of a character did squash-and-stretch causing the arms/legs to distort and shear. - Squash-and-stretch for both FK and IK rigs. - Rubber hose controls. Misc changes/bugfixes: - Rigify now locks all pose transforms for non-control bones automatically. - The README file now correctly reflects the new rig-type API. - Scrubbed the code for unused variables and imports. - PEP8 cleanups.
Diffstat (limited to 'rigify')
-rw-r--r--rigify/CREDITS1
-rw-r--r--rigify/README60
-rw-r--r--rigify/__init__.py11
-rw-r--r--rigify/generate.py11
-rw-r--r--rigify/metarigs/human.py214
-rw-r--r--rigify/rig_lists.py2
-rw-r--r--rigify/rig_ui_template.py26
-rw-r--r--rigify/rigs/biped/arm/__init__.py116
-rw-r--r--rigify/rigs/biped/arm/deform.py203
-rw-r--r--rigify/rigs/biped/arm/fk.py166
-rw-r--r--rigify/rigs/biped/arm/ik.py290
-rw-r--r--rigify/rigs/biped/leg/__init__.py119
-rw-r--r--rigify/rigs/biped/leg/deform.py218
-rw-r--r--rigify/rigs/biped/leg/fk.py162
-rw-r--r--rigify/rigs/biped/leg/ik.py325
-rw-r--r--rigify/rigs/biped/limb_common.py1139
-rw-r--r--rigify/rigs/misc/delta.py2
-rw-r--r--rigify/rigs/neck_short.py6
-rw-r--r--rigify/rigs/spine.py11
-rw-r--r--rigify/utils.py194
20 files changed, 1852 insertions, 1424 deletions
diff --git a/rigify/CREDITS b/rigify/CREDITS
index dd517fb1..2501e152 100644
--- a/rigify/CREDITS
+++ b/rigify/CREDITS
@@ -7,6 +7,7 @@ General financial support:
- Benjamin Tolputt
- Nesterenko Viktoriya
- Jeff Hogan
+- Pitchi Poy productions
IK/FK snapping financial support:
- Benjamin Tolputt
diff --git a/rigify/README b/rigify/README
index 790942f9..7b12589b 100644
--- a/rigify/README
+++ b/rigify/README
@@ -70,8 +70,9 @@ a parent, and sets the root bone (which Rigify creates) as their parent.
THE GUTS OF A RIG TYPE, BASIC
-----------------------------
-A rig type is simply a python module containing a class named "Rig". The Rig
-class is only required to have two methods: __init__() and generate()
+A rig type is simply a python module containing a class named "Rig", and some
+optional module functions. The Rig class has only two methods:
+__init__() and generate()
__init__() is the "information gathering" code for the rig type. When Rigify
loops through the bones and finds a tagged bone, it will create a python
@@ -85,7 +86,7 @@ A proper rig-type __init__() will look like this:
# code goes here
At the bare minimum, you are going to want to store the object and bone name
-in the rig type object for later reference in the generate method. So:
+in the rig type object for later reference in the generate() method. So:
def __init__(self, obj, bone_name, params):
self.obj = obj
@@ -163,17 +164,11 @@ one place (those functions) and all the rig types will still work.
THE GUTS OF A RIG TYPE, ADVANCED
--------------------------------
If you look at any of the rig types included with Rigify, you'll note that they
-have several more methods than just __init__() and generate().
-THESE ADDITIONAL METHODS ARE _NOT_ REQUIRED for a rig type to function. But
+have several functions outside of the Rig class.
+THESE ADDITIONAL FUNCTIONS ARE _NOT_ REQUIRED for a rig type to function. But
they can add some nifty functionality to your rig.
-Not all of the additional methods you see in the included rig types have any
-special purpose for Rigify, however. For example, I often create separate
-methods for generating the deformation and control rigs, and then call them
-both from the main generate() method. But that is just for organization, and
-has nothing to do with Rigify itself.
-
-Here are the additional methods relevant to Rigify, with brief decriptions of
+Here are the additional functions relevant to Rigify, with brief decriptions of
what they are for:
@@ -183,25 +178,21 @@ For many rig types, it is handy for the user to be able to tweak how they are
generated. For example, the included biped arm rig allows the user to specify
the axis of rotation for the elbow.
-There are two methods necessary to give a rig type user-tweakable parameters,
-both of which must be class methods:
+There are two functions necessary to give a rig type user-tweakable parameters:
add_parameters()
parameters_ui()
add_parameters() takes an IDPropertyGroup as input, and adds its parameters
to that group as RNA properties. For example:
- @classmethod
- def add_parameters(self, group):
- group.toggle_param = bpy.props.BoolProperty(name="Test toggle:", default=False, description="Just a test, not really used for anything.")
+ def add_parameters(params):
+ params.toggle_param = bpy.props.BoolProperty(name="Test toggle:", default=False, description="Just a test, not really used for anything.")
-parameter_ui() recieves a Blender UILayout object, the metarig object, and the
-tagged bone name. It creates a GUI in the UILayout for the user to tweak the
-parameters. For example:
+parameters_ui() recieves a Blender UILayout object and an IDPropertyGroup
+containing the parameters added by add_parameters(). It creates a GUI in the
+UILayout for the user to tweak those parameters. For example:
- @classmethod
- def parameters_ui(self, layout, obj, bone):
- params = obj.pose.bones[bone].rigify_parameters[0]
+ def parameters_ui(layout, params):
r = layout.row()
r.prop(params, "toggle_param")
@@ -209,14 +200,12 @@ parameters. For example:
SAMPLE METARIG
--------------
It is a good idea for all rig types to have a sample metarig that the user can
-add to their own metarig. This is what the create_sample() method is for.
-Like the parameter methods above, create_sample() must be a class method.
+add to their own metarig. This is what the create_sample() function is for.
create_sample() takes the current armature object as input, and adds the bones
for its rig-type's metarig. For example:
- @classmethod
- def create_sample(self, obj):
+ def create_sample(obj):
bpy.ops.object.mode_set(mode='EDIT')
arm = obj.data
@@ -232,21 +221,24 @@ for its rig-type's metarig. For example:
pbone.rigify_parameters.add()
Obviously, this isn't something that you generally want to hand-code,
-especially with more complex samples. There is a function in utils.py
-that will generate the code for create_sample() for you, based on a selected
-armature. The function is called write_metarig()
+especially with more complex samples. When in edit-mode on an armature,
+there is a "Rigify Dev Tools" panel in the View3d tools panel containing a
+button labeled "Encode Sample to Python". This button will generate the python
+code for create_sample() from the armature you are editing. The generated code
+appears in a text block called "metarig_sample.py"
GENERATING A PYTHON UI
----------------------
-The generate() method can also, optionally, return python code as a string.
-This python code is added to the "rig properties" panel that gets
+The generate() method can also, optionally, return python code as a single
+string. This python code is added to the "rig properties" panel that gets
auto-generated along with the rig. This is useful for exposing things like
IK/FK switches in a nice way to the animator.
-The string must be returned in a list:
+The string must be returned in a list, e.g.:
return ["my python code"]
-Otherwise it won't work.
+The reason it needs to be put in a list is to leave room for expanding the API
+in the future, for returning additional information.
diff --git a/rigify/__init__.py b/rigify/__init__.py
index 38f0c911..2d968422 100644
--- a/rigify/__init__.py
+++ b/rigify/__init__.py
@@ -20,15 +20,14 @@
bl_info = {
"name": "Rigify",
+ "version": (0, 4),
"author": "Nathan Vegdahl",
- "blender": (2, 57, 0),
- "location": "View3D > Add > Armature",
- "description": "Adds various Rig Templates",
- "location": "Armature properties",
+ "blender": (2, 66, 0),
+ "description": "Automatic rigging from building-block components",
+ "location": "Armature properties, Bone properties, View3d tools panel, Armature Add menu",
"wiki_url": "http://wiki.blender.org/index.php/Extensions:2.5/Py/"
"Scripts/Rigging/Rigify",
- "tracker_url": "http://projects.blender.org/tracker/index.php?"
- "func=detail&aid=25546",
+ "tracker_url": "http://github.com/cessen/rigify/issues",
"category": "Rigging"}
diff --git a/rigify/generate.py b/rigify/generate.py
index 69c8863b..5e9feafc 100644
--- a/rigify/generate.py
+++ b/rigify/generate.py
@@ -265,7 +265,6 @@ def generate_rig(context, metarig):
try:
# Collect/initialize all the rigs.
rigs = []
- deformation_rigs = []
for bone in bones_sorted:
bpy.ops.object.mode_set(mode='EDIT')
rigs += get_bone_rigs(obj, bone)
@@ -307,6 +306,16 @@ def generate_rig(context, metarig):
obj.data.edit_bones[bone].parent = obj.data.edit_bones[root_bone]
bpy.ops.object.mode_set(mode='OBJECT')
+ # Lock transforms on all non-control bones
+ r = re.compile("[A-Z][A-Z][A-Z]-")
+ for bone in bones:
+ if r.match(bone):
+ pb = obj.pose.bones[bone]
+ pb.lock_location = (True, True, True)
+ pb.lock_rotation = (True, True, True)
+ pb.lock_rotation_w = True
+ pb.lock_scale = (True, True, True)
+
# Every bone that has a name starting with "DEF-" make deforming. All the
# others make non-deforming.
for bone in bones:
diff --git a/rigify/metarigs/human.py b/rigify/metarigs/human.py
index 3eafc988..b1675e32 100644
--- a/rigify/metarigs/human.py
+++ b/rigify/metarigs/human.py
@@ -29,10 +29,14 @@ def create(obj):
for i in range(28):
arm.rigify_layers.add()
- arm.rigify_layers[0].name = "Torso"
- arm.rigify_layers[0].row = 2
- arm.rigify_layers[2].name = "Head"
- arm.rigify_layers[2].row = 1
+ arm.rigify_layers[0].name = "head"
+ arm.rigify_layers[0].row = 1
+ arm.rigify_layers[1].name = " "
+ arm.rigify_layers[1].row = 1
+ arm.rigify_layers[2].name = "Torso"
+ arm.rigify_layers[2].row = 2
+ arm.rigify_layers[3].name = " "
+ arm.rigify_layers[3].row = 1
arm.rigify_layers[4].name = "Fingers"
arm.rigify_layers[4].row = 3
arm.rigify_layers[5].name = "(Tweak)"
@@ -41,18 +45,46 @@ def create(obj):
arm.rigify_layers[6].row = 4
arm.rigify_layers[7].name = "Arm.L (IK)"
arm.rigify_layers[7].row = 5
- arm.rigify_layers[8].name = "Arm.R (FK)"
- arm.rigify_layers[8].row = 4
- arm.rigify_layers[9].name = "Arm.R (IK)"
- arm.rigify_layers[9].row = 5
- arm.rigify_layers[10].name = "Leg.L (FK)"
- arm.rigify_layers[10].row = 6
- arm.rigify_layers[11].name = "Leg.L (IK)"
- arm.rigify_layers[11].row = 7
- arm.rigify_layers[12].name = "Leg.R (FK)"
- arm.rigify_layers[12].row = 6
- arm.rigify_layers[13].name = "Leg.R (IK)"
- arm.rigify_layers[13].row = 7
+ arm.rigify_layers[8].name = "Arm.L (Tweak)"
+ arm.rigify_layers[8].row = 6
+ arm.rigify_layers[9].name = "Arm.R (FK)"
+ arm.rigify_layers[9].row = 4
+ arm.rigify_layers[10].name = "Arm.R (IK)"
+ arm.rigify_layers[10].row = 5
+ arm.rigify_layers[11].name = "Arm.R (Tweak)"
+ arm.rigify_layers[11].row = 6
+ arm.rigify_layers[12].name = "Leg.L (FK)"
+ arm.rigify_layers[12].row = 7
+ arm.rigify_layers[13].name = "Leg.L (IK)"
+ arm.rigify_layers[13].row = 8
+ arm.rigify_layers[14].name = "Leg.L (Tweak)"
+ arm.rigify_layers[14].row = 9
+ arm.rigify_layers[15].name = "Leg.R (FK)"
+ arm.rigify_layers[15].row = 7
+ arm.rigify_layers[16].name = "Leg.R (IK)"
+ arm.rigify_layers[16].row = 8
+ arm.rigify_layers[17].name = "Leg.R (Tweak)"
+ arm.rigify_layers[17].row = 9
+ arm.rigify_layers[18].name = " "
+ arm.rigify_layers[18].row = 1
+ arm.rigify_layers[19].name = " "
+ arm.rigify_layers[19].row = 1
+ arm.rigify_layers[20].name = " "
+ arm.rigify_layers[20].row = 1
+ arm.rigify_layers[21].name = " "
+ arm.rigify_layers[21].row = 1
+ arm.rigify_layers[22].name = " "
+ arm.rigify_layers[22].row = 1
+ arm.rigify_layers[23].name = " "
+ arm.rigify_layers[23].row = 1
+ arm.rigify_layers[24].name = " "
+ arm.rigify_layers[24].row = 1
+ arm.rigify_layers[25].name = " "
+ arm.rigify_layers[25].row = 1
+ arm.rigify_layers[26].name = " "
+ arm.rigify_layers[26].row = 1
+ arm.rigify_layers[27].name = " "
+ arm.rigify_layers[27].row = 1
bones = {}
@@ -139,13 +171,6 @@ def create(obj):
bone.use_connect = True
bone.parent = arm.edit_bones[bones['shin.L']]
bones['heel.L'] = bone.name
- bone = arm.edit_bones.new('heel.02.L')
- bone.head[:] = 0.0600, 0.0000, 0.0000
- bone.tail[:] = 0.1400, 0.0000, 0.0000
- bone.roll = 0.0000
- bone.use_connect = False
- bone.parent = arm.edit_bones[bones['heel.L']]
- bones['heel.02.L'] = bone.name
bone = arm.edit_bones.new('foot.R')
bone.head[:] = -0.0980, 0.0162, 0.0852
bone.tail[:] = -0.0980, -0.0934, 0.0167
@@ -160,13 +185,6 @@ def create(obj):
bone.use_connect = True
bone.parent = arm.edit_bones[bones['shin.R']]
bones['heel.R'] = bone.name
- bone = arm.edit_bones.new('heel.02.R')
- bone.head[:] = -0.0600, 0.0000, 0.0000
- bone.tail[:] = -0.1400, 0.0000, 0.0000
- bone.roll = 0.0000
- bone.use_connect = False
- bone.parent = arm.edit_bones[bones['heel.R']]
- bones['heel.02.R'] = bone.name
bone = arm.edit_bones.new('head')
bone.head[:] = 0.0000, -0.0247, 1.7813
bone.tail[:] = 0.0000, -0.0247, 1.9347
@@ -195,6 +213,13 @@ def create(obj):
bone.use_connect = True
bone.parent = arm.edit_bones[bones['foot.L']]
bones['toe.L'] = bone.name
+ bone = arm.edit_bones.new('heel.02.L')
+ bone.head[:] = 0.0600, 0.0000, 0.0000
+ bone.tail[:] = 0.1400, 0.0000, 0.0000
+ bone.roll = 0.0000
+ bone.use_connect = False
+ bone.parent = arm.edit_bones[bones['heel.L']]
+ bones['heel.02.L'] = bone.name
bone = arm.edit_bones.new('toe.R')
bone.head[:] = -0.0980, -0.0934, 0.0167
bone.tail[:] = -0.0980, -0.1606, 0.0167
@@ -202,6 +227,13 @@ def create(obj):
bone.use_connect = True
bone.parent = arm.edit_bones[bones['foot.R']]
bones['toe.R'] = bone.name
+ bone = arm.edit_bones.new('heel.02.R')
+ bone.head[:] = -0.0600, 0.0000, 0.0000
+ bone.tail[:] = -0.1400, 0.0000, 0.0000
+ bone.roll = 0.0000
+ bone.use_connect = False
+ bone.parent = arm.edit_bones[bones['heel.R']]
+ bones['heel.02.R'] = bone.name
bone = arm.edit_bones.new('forearm.L')
bone.head[:] = 0.4424, 0.0885, 1.4491
bone.tail[:] = 0.6594, 0.0492, 1.3061
@@ -500,13 +532,16 @@ def create(obj):
bpy.ops.object.mode_set(mode='OBJECT')
pbone = obj.pose.bones[bones['hips']]
pbone.rigify_type = 'spine'
- pbone.rigify_parameters.chain_bone_controls = "1, 2, 3"
pbone.lock_location = (False, False, False)
pbone.lock_rotation = (False, False, False)
pbone.lock_rotation_w = False
pbone.lock_scale = (False, False, False)
pbone.rotation_mode = 'QUATERNION'
- pbone.bone.layers = [True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
+ pbone.bone.layers = [False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
+ try:
+ pbone.rigify_parameters.chain_bone_controls = "1, 2, 3"
+ except AttributeError:
+ pass
pbone = obj.pose.bones[bones['spine']]
pbone.rigify_type = ''
pbone.lock_location = (False, False, False)
@@ -514,7 +549,7 @@ def create(obj):
pbone.lock_rotation_w = False
pbone.lock_scale = (False, False, False)
pbone.rotation_mode = 'QUATERNION'
- pbone.bone.layers = [True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
+ pbone.bone.layers = [False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
pbone = obj.pose.bones[bones['thigh.L']]
pbone.rigify_type = 'biped.leg'
pbone.lock_location = (True, True, True)
@@ -522,13 +557,21 @@ def create(obj):
pbone.lock_rotation_w = False
pbone.lock_scale = (False, False, False)
pbone.rotation_mode = 'QUATERNION'
- pbone.bone.layers = [False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
+ pbone.bone.layers = [False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
try:
pbone.rigify_parameters.separate_ik_layers = True
except AttributeError:
pass
try:
- pbone.rigify_parameters.ik_layers = [False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
+ pbone.rigify_parameters.ik_layers = [False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
+ except AttributeError:
+ pass
+ try:
+ pbone.rigify_parameters.separate_hose_layers = True
+ except AttributeError:
+ pass
+ try:
+ pbone.rigify_parameters.hose_layers = [False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
except AttributeError:
pass
pbone = obj.pose.bones[bones['thigh.R']]
@@ -538,13 +581,21 @@ def create(obj):
pbone.lock_rotation_w = False
pbone.lock_scale = (False, False, False)
pbone.rotation_mode = 'QUATERNION'
- pbone.bone.layers = [False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
+ pbone.bone.layers = [False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
try:
pbone.rigify_parameters.separate_ik_layers = True
except AttributeError:
pass
try:
- pbone.rigify_parameters.ik_layers = [False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
+ pbone.rigify_parameters.ik_layers = [False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
+ except AttributeError:
+ pass
+ try:
+ pbone.rigify_parameters.separate_hose_layers = True
+ except AttributeError:
+ pass
+ try:
+ pbone.rigify_parameters.hose_layers = [False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
except AttributeError:
pass
pbone = obj.pose.bones[bones['chest']]
@@ -554,7 +605,7 @@ def create(obj):
pbone.lock_rotation_w = False
pbone.lock_scale = (False, False, False)
pbone.rotation_mode = 'QUATERNION'
- pbone.bone.layers = [True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
+ pbone.bone.layers = [False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
pbone = obj.pose.bones[bones['shin.L']]
pbone.rigify_type = ''
pbone.lock_location = (False, False, False)
@@ -562,7 +613,7 @@ def create(obj):
pbone.lock_rotation_w = False
pbone.lock_scale = (False, False, False)
pbone.rotation_mode = 'QUATERNION'
- pbone.bone.layers = [False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
+ pbone.bone.layers = [False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
pbone = obj.pose.bones[bones['shin.R']]
pbone.rigify_type = ''
pbone.lock_location = (False, False, False)
@@ -570,7 +621,7 @@ def create(obj):
pbone.lock_rotation_w = False
pbone.lock_scale = (False, False, False)
pbone.rotation_mode = 'QUATERNION'
- pbone.bone.layers = [False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
+ pbone.bone.layers = [False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
pbone = obj.pose.bones[bones['neck']]
pbone.rigify_type = 'neck_short'
pbone.lock_location = (True, True, True)
@@ -578,7 +629,7 @@ def create(obj):
pbone.lock_rotation_w = False
pbone.lock_scale = (False, False, False)
pbone.rotation_mode = 'QUATERNION'
- pbone.bone.layers = [False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
+ pbone.bone.layers = [True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
pbone = obj.pose.bones[bones['shoulder.L']]
pbone.rigify_type = 'basic.copy'
pbone.lock_location = (True, True, True)
@@ -586,7 +637,7 @@ def create(obj):
pbone.lock_rotation_w = False
pbone.lock_scale = (False, False, False)
pbone.rotation_mode = 'YXZ'
- pbone.bone.layers = [True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
+ pbone.bone.layers = [False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
pbone = obj.pose.bones[bones['shoulder.R']]
pbone.rigify_type = 'basic.copy'
pbone.lock_location = (True, True, True)
@@ -594,7 +645,7 @@ def create(obj):
pbone.lock_rotation_w = False
pbone.lock_scale = (False, False, False)
pbone.rotation_mode = 'YXZ'
- pbone.bone.layers = [True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
+ pbone.bone.layers = [False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
pbone = obj.pose.bones[bones['foot.L']]
pbone.rigify_type = ''
pbone.lock_location = (False, False, False)
@@ -602,7 +653,7 @@ def create(obj):
pbone.lock_rotation_w = False
pbone.lock_scale = (False, False, False)
pbone.rotation_mode = 'QUATERNION'
- pbone.bone.layers = [False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
+ pbone.bone.layers = [False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
pbone = obj.pose.bones[bones['heel.L']]
pbone.rigify_type = ''
pbone.lock_location = (False, False, False)
@@ -610,15 +661,7 @@ def create(obj):
pbone.lock_rotation_w = False
pbone.lock_scale = (False, False, False)
pbone.rotation_mode = 'QUATERNION'
- pbone.bone.layers = [False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
- pbone = obj.pose.bones[bones['heel.02.L']]
- pbone.rigify_type = ''
- pbone.lock_location = (False, False, False)
- pbone.lock_rotation = (False, False, False)
- pbone.lock_rotation_w = False
- pbone.lock_scale = (False, False, False)
- pbone.rotation_mode = 'QUATERNION'
- pbone.bone.layers = [False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
+ pbone.bone.layers = [False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
pbone = obj.pose.bones[bones['foot.R']]
pbone.rigify_type = ''
pbone.lock_location = (False, False, False)
@@ -626,7 +669,7 @@ def create(obj):
pbone.lock_rotation_w = False
pbone.lock_scale = (False, False, False)
pbone.rotation_mode = 'QUATERNION'
- pbone.bone.layers = [False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
+ pbone.bone.layers = [False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
pbone = obj.pose.bones[bones['heel.R']]
pbone.rigify_type = ''
pbone.lock_location = (False, False, False)
@@ -634,15 +677,7 @@ def create(obj):
pbone.lock_rotation_w = False
pbone.lock_scale = (False, False, False)
pbone.rotation_mode = 'QUATERNION'
- pbone.bone.layers = [False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
- pbone = obj.pose.bones[bones['heel.02.R']]
- pbone.rigify_type = ''
- pbone.lock_location = (False, False, False)
- pbone.lock_rotation = (False, False, False)
- pbone.lock_rotation_w = False
- pbone.lock_scale = (False, False, False)
- pbone.rotation_mode = 'QUATERNION'
- pbone.bone.layers = [False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
+ pbone.bone.layers = [False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
pbone = obj.pose.bones[bones['head']]
pbone.rigify_type = ''
pbone.lock_location = (False, False, False)
@@ -650,7 +685,7 @@ def create(obj):
pbone.lock_rotation_w = False
pbone.lock_scale = (False, False, False)
pbone.rotation_mode = 'QUATERNION'
- pbone.bone.layers = [False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
+ pbone.bone.layers = [True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
pbone = obj.pose.bones[bones['upper_arm.L']]
pbone.rigify_type = 'biped.arm'
pbone.lock_location = (True, True, True)
@@ -667,6 +702,14 @@ def create(obj):
pbone.rigify_parameters.ik_layers = [False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
except AttributeError:
pass
+ try:
+ pbone.rigify_parameters.separate_hose_layers = True
+ except AttributeError:
+ pass
+ try:
+ pbone.rigify_parameters.hose_layers = [False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
+ except AttributeError:
+ pass
pbone = obj.pose.bones[bones['upper_arm.R']]
pbone.rigify_type = 'biped.arm'
pbone.lock_location = (True, True, True)
@@ -674,13 +717,21 @@ def create(obj):
pbone.lock_rotation_w = False
pbone.lock_scale = (False, False, False)
pbone.rotation_mode = 'QUATERNION'
- pbone.bone.layers = [False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
+ pbone.bone.layers = [False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
try:
pbone.rigify_parameters.separate_ik_layers = True
except AttributeError:
pass
try:
- pbone.rigify_parameters.ik_layers = [False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
+ pbone.rigify_parameters.ik_layers = [False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
+ except AttributeError:
+ pass
+ try:
+ pbone.rigify_parameters.separate_hose_layers = True
+ except AttributeError:
+ pass
+ try:
+ pbone.rigify_parameters.hose_layers = [False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
except AttributeError:
pass
pbone = obj.pose.bones[bones['toe.L']]
@@ -690,8 +741,8 @@ def create(obj):
pbone.lock_rotation_w = False
pbone.lock_scale = (False, False, False)
pbone.rotation_mode = 'QUATERNION'
- pbone.bone.layers = [False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
- pbone = obj.pose.bones[bones['toe.R']]
+ pbone.bone.layers = [False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
+ pbone = obj.pose.bones[bones['heel.02.L']]
pbone.rigify_type = ''
pbone.lock_location = (False, False, False)
pbone.lock_rotation = (False, False, False)
@@ -699,6 +750,22 @@ def create(obj):
pbone.lock_scale = (False, False, False)
pbone.rotation_mode = 'QUATERNION'
pbone.bone.layers = [False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
+ pbone = obj.pose.bones[bones['toe.R']]
+ pbone.rigify_type = ''
+ pbone.lock_location = (False, False, False)
+ pbone.lock_rotation = (False, False, False)
+ pbone.lock_rotation_w = False
+ pbone.lock_scale = (False, False, False)
+ pbone.rotation_mode = 'QUATERNION'
+ pbone.bone.layers = [False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
+ pbone = obj.pose.bones[bones['heel.02.R']]
+ pbone.rigify_type = ''
+ pbone.lock_location = (False, False, False)
+ pbone.lock_rotation = (False, False, False)
+ pbone.lock_rotation_w = False
+ pbone.lock_scale = (False, False, False)
+ pbone.rotation_mode = 'QUATERNION'
+ pbone.bone.layers = [False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
pbone = obj.pose.bones[bones['forearm.L']]
pbone.rigify_type = ''
pbone.lock_location = (False, False, False)
@@ -714,7 +781,7 @@ def create(obj):
pbone.lock_rotation_w = False
pbone.lock_scale = (False, False, False)
pbone.rotation_mode = 'QUATERNION'
- pbone.bone.layers = [False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
+ pbone.bone.layers = [False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
pbone = obj.pose.bones[bones['hand.L']]
pbone.rigify_type = ''
pbone.lock_location = (False, False, False)
@@ -730,7 +797,7 @@ def create(obj):
pbone.lock_rotation_w = False
pbone.lock_scale = (False, False, False)
pbone.rotation_mode = 'QUATERNION'
- pbone.bone.layers = [False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
+ pbone.bone.layers = [False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
pbone = obj.pose.bones[bones['palm.01.L']]
pbone.rigify_type = 'palm'
pbone.lock_location = (True, True, True)
@@ -851,7 +918,6 @@ def create(obj):
pbone.lock_scale = (False, False, False)
pbone.rotation_mode = 'QUATERNION'
pbone.bone.layers = [False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
-
try:
pbone.rigify_parameters.separate_extra_layers = True
except AttributeError:
@@ -868,7 +934,6 @@ def create(obj):
pbone.lock_scale = (False, False, False)
pbone.rotation_mode = 'QUATERNION'
pbone.bone.layers = [False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
-
try:
pbone.rigify_parameters.separate_extra_layers = True
except AttributeError:
@@ -1130,4 +1195,7 @@ def create(obj):
bone.select_tail = True
arm.edit_bones.active = bone
- arm.layers = [(x in [0, 2, 4, 6, 8, 10, 12]) for x in range(0, 32)]
+ arm.layers = [(x in [0, 2, 4, 6, 9, 12, 15]) for x in range(32)]
+
+if __name__ == "__main__":
+ create(bpy.context.active_object)
diff --git a/rigify/rig_lists.py b/rigify/rig_lists.py
index 66f0a33e..8de8528e 100644
--- a/rigify/rig_lists.py
+++ b/rigify/rig_lists.py
@@ -17,8 +17,6 @@
#======================= END GPL LICENSE BLOCK ========================
import os
-import traceback
-import logging
from . import utils
diff --git a/rigify/rig_ui_template.py b/rigify/rig_ui_template.py
index 89a4e544..9e755bb5 100644
--- a/rigify/rig_ui_template.py
+++ b/rigify/rig_ui_template.py
@@ -245,6 +245,13 @@ def fk2ik_arm(obj, fk, ik):
farmi = obj.pose.bones[ik[1]]
handi = obj.pose.bones[ik[2]]
+ # Stretch
+ if handi['auto_stretch'] == 0.0:
+ uarm['stretch_length'] = handi['stretch_length']
+ else:
+ diff = (uarmi.vector.length + farmi.vector.length) / (uarm.vector.length + farm.vector.length)
+ uarm['stretch_length'] *= diff
+
# Upper arm position
match_pose_rotation(uarm, uarmi)
match_pose_scale(uarm, uarmi)
@@ -272,6 +279,9 @@ def ik2fk_arm(obj, fk, ik):
handi = obj.pose.bones[ik[2]]
pole = obj.pose.bones[ik[3]]
+ # Stretch
+ handi['stretch_length'] = uarm['stretch_length']
+
# Hand position
match_pose_translation(handi, hand)
match_pose_rotation(handi, hand)
@@ -293,7 +303,15 @@ def fk2ik_leg(obj, fk, ik):
mfoot = obj.pose.bones[fk[3]]
thighi = obj.pose.bones[ik[0]]
shini = obj.pose.bones[ik[1]]
- mfooti = obj.pose.bones[ik[2]]
+ footi = obj.pose.bones[ik[2]]
+ mfooti = obj.pose.bones[ik[3]]
+
+ # Stretch
+ if footi['auto_stretch'] == 0.0:
+ thigh['stretch_length'] = footi['stretch_length']
+ else:
+ diff = (thighi.vector.length + shini.vector.length) / (thigh.vector.length + shin.vector.length)
+ thigh['stretch_length'] *= diff
# Thigh position
match_pose_rotation(thigh, thighi)
@@ -328,6 +346,9 @@ def ik2fk_leg(obj, fk, ik):
pole = obj.pose.bones[ik[4]]
mfooti = obj.pose.bones[ik[5]]
+ # Stretch
+ footi['stretch_length'] = thigh['stretch_length']
+
# Clear footroll
set_pose_rotation(footroll, Matrix())
@@ -421,6 +442,7 @@ class Rigify_Leg_FK2IK(bpy.types.Operator):
thigh_ik = bpy.props.StringProperty(name="Thigh IK Name")
shin_ik = bpy.props.StringProperty(name="Shin IK Name")
+ foot_ik = bpy.props.StringProperty(name="Foot IK Name")
mfoot_ik = bpy.props.StringProperty(name="MFoot IK Name")
@classmethod
@@ -431,7 +453,7 @@ class Rigify_Leg_FK2IK(bpy.types.Operator):
use_global_undo = context.user_preferences.edit.use_global_undo
context.user_preferences.edit.use_global_undo = False
try:
- fk2ik_leg(context.active_object, fk=[self.thigh_fk, self.shin_fk, self.foot_fk, self.mfoot_fk], ik=[self.thigh_ik, self.shin_ik, self.mfoot_ik])
+ fk2ik_leg(context.active_object, fk=[self.thigh_fk, self.shin_fk, self.foot_fk, self.mfoot_fk], ik=[self.thigh_ik, self.shin_ik, self.foot_ik, self.mfoot_ik])
finally:
context.user_preferences.edit.use_global_undo = use_global_undo
return {'FINISHED'}
diff --git a/rigify/rigs/biped/arm/__init__.py b/rigify/rigs/biped/arm/__init__.py
index 81b5535c..3235645e 100644
--- a/rigify/rigs/biped/arm/__init__.py
+++ b/rigify/rigs/biped/arm/__init__.py
@@ -31,13 +31,6 @@ fk_arm = ["%s", "%s", "%s"]
ik_arm = ["%s", "%s", "%s", "%s"]
if is_selected(fk_arm+ik_arm):
layout.prop(pose_bones[ik_arm[2]], '["ikfk_switch"]', text="FK / IK (" + ik_arm[2] + ")", slider=True)
-if is_selected(fk_arm):
- try:
- pose_bones[fk_arm[0]]["isolate"]
- layout.prop(pose_bones[fk_arm[0]], '["isolate"]', text="Isolate Rotation (" + fk_arm[0] + ")", slider=True)
- except KeyError:
- pass
-if is_selected(fk_arm+ik_arm):
props = layout.operator("pose.rigify_arm_fk2ik_" + rig_id, text="Snap FK->IK (" + fk_arm[0] + ")")
props.uarm_fk = fk_arm[0]
props.farm_fk = fk_arm[1]
@@ -53,7 +46,27 @@ if is_selected(fk_arm+ik_arm):
props.farm_ik = ik_arm[1]
props.hand_ik = ik_arm[2]
props.pole = ik_arm[3]
+if is_selected(fk_arm):
+ try:
+ pose_bones[fk_arm[0]]["isolate"]
+ layout.prop(pose_bones[fk_arm[0]], '["isolate"]', text="Isolate Rotation (" + fk_arm[0] + ")", slider=True)
+ except KeyError:
+ pass
+ layout.prop(pose_bones[fk_arm[0]], '["stretch_length"]', text="Length FK (" + fk_arm[0] + ")", slider=True)
+if is_selected(ik_arm):
+ layout.prop(pose_bones[ik_arm[2]], '["stretch_length"]', text="Length IK (" + ik_arm[2] + ")", slider=True)
+ layout.prop(pose_bones[ik_arm[2]], '["auto_stretch"]', text="Auto-Stretch IK (" + ik_arm[2] + ")", slider=True)
+"""
+hose_script = """
+hose_arm = ["%s", "%s", "%s"]
+if is_selected(hose_arm):
+ layout.prop(pose_bones[hose_arm[1]], '["smooth_bend"]', text="Smooth Elbow (" + hose_arm[1] + ")", slider=True)
+"""
+
+end_script = """
+if is_selected(fk_arm+ik_arm):
+ layout.separator()
"""
@@ -68,6 +81,9 @@ class Rig:
Do NOT change any data in the scene. This is a gathering phase only.
"""
+ self.obj = obj
+ self.params = params
+
# Gather deform rig
self.deform_rig = deform.Rig(obj, bone, params)
@@ -83,10 +99,14 @@ class Rig:
The main armature should be selected and active before this is called.
"""
- self.deform_rig.generate()
+ hose_controls = self.deform_rig.generate()
fk_controls = self.fk_rig.generate()
ik_controls = self.ik_rig.generate()
- return [script % (fk_controls[0], fk_controls[1], fk_controls[2], ik_controls[0], ik_controls[1], ik_controls[2], ik_controls[3])]
+ ui_script = script % (fk_controls[0], fk_controls[1], fk_controls[2], ik_controls[0], ik_controls[1], ik_controls[2], ik_controls[3])
+ if self.params.use_complex_arm:
+ ui_script += hose_script % (hose_controls[0], hose_controls[1], hose_controls[2])
+ ui_script += end_script
+ return [ui_script]
def add_parameters(params):
@@ -94,25 +114,37 @@ def add_parameters(params):
RigifyParameters PropertyGroup
"""
+ params.use_complex_arm = bpy.props.BoolProperty(name="Complex Arm Rig", default=True, description="Generate the full, complex arm rig with twist bones and rubber-hose controls")
+ params.bend_hint = bpy.props.BoolProperty(name="Bend Hint", default=True, description="Give IK chain a hint about which way to bend. Useful for perfectly straight chains")
+
items = [('X', 'X', ''), ('Y', 'Y', ''), ('Z', 'Z', ''), ('-X', '-X', ''), ('-Y', '-Y', ''), ('-Z', '-Z', '')]
params.primary_rotation_axis = bpy.props.EnumProperty(items=items, name="Primary Rotation Axis", default='X')
- params.bend_hint = bpy.props.BoolProperty(name="Bend Hint", default=True, description="Give IK chain a hint about which way to bend. Useful for perfectly straight chains")
- params.elbow_target_base_name = bpy.props.StringProperty(name="Elbow Target Name", default="elbow_target", description="Base name for the generated elbow target")
+ params.elbow_base_name = bpy.props.StringProperty(name="Elbow Name", default="elbow", description="Base name for the generated elbow-related controls")
params.separate_ik_layers = bpy.props.BoolProperty(name="Separate IK Control Layers:", default=False, description="Enable putting the ik controls on a separate layer from the fk controls")
params.ik_layers = bpy.props.BoolVectorProperty(size=32, description="Layers for the ik controls to be on")
- params.use_upper_arm_twist = bpy.props.BoolProperty(name="Upper Arm Twist", default=True, description="Generate the dual-bone twist setup for the upper arm")
- params.use_forearm_twist = bpy.props.BoolProperty(name="Forearm Twist", default=True, description="Generate the dual-bone twist setup for the forearm")
+ params.separate_hose_layers = bpy.props.BoolProperty(name="Separate Rubber-hose Control Layers:", default=False, description="Enable putting the rubber-hose controls on a separate layer from the other controls")
+ params.hose_layers = bpy.props.BoolVectorProperty(size=32, description="Layers for the rubber-hose controls to be on")
def parameters_ui(layout, params):
""" Create the ui for the rig parameters.
"""
+ col = layout.column()
+ col.prop(params, "use_complex_arm")
+
+ r = layout.row()
+ r.label(text="Elbow rotation axis:")
+ r.prop(params, "primary_rotation_axis", text="")
+
r = layout.row()
- r.prop(params, "elbow_target_base_name")
+ r.prop(params, "elbow_base_name")
+
+ r = layout.row()
+ r.prop(params, "bend_hint")
r = layout.row()
r.prop(params, "separate_ik_layers")
@@ -160,16 +192,52 @@ def parameters_ui(layout, params):
row.prop(params, "ik_layers", index=30, toggle=True, text="")
row.prop(params, "ik_layers", index=31, toggle=True, text="")
- r = layout.row()
- r.label(text="Elbow rotation axis:")
- r.prop(params, "primary_rotation_axis", text="")
-
- r = layout.row()
- r.prop(params, "bend_hint")
-
- col = layout.column()
- col.prop(params, "use_upper_arm_twist")
- col.prop(params, "use_forearm_twist")
+ if params.use_complex_arm:
+ r = layout.row()
+ r.prop(params, "separate_hose_layers")
+
+ r = layout.row()
+ r.active = params.separate_hose_layers
+
+ col = r.column(align=True)
+ row = col.row(align=True)
+ row.prop(params, "hose_layers", index=0, toggle=True, text="")
+ row.prop(params, "hose_layers", index=1, toggle=True, text="")
+ row.prop(params, "hose_layers", index=2, toggle=True, text="")
+ row.prop(params, "hose_layers", index=3, toggle=True, text="")
+ row.prop(params, "hose_layers", index=4, toggle=True, text="")
+ row.prop(params, "hose_layers", index=5, toggle=True, text="")
+ row.prop(params, "hose_layers", index=6, toggle=True, text="")
+ row.prop(params, "hose_layers", index=7, toggle=True, text="")
+ row = col.row(align=True)
+ row.prop(params, "hose_layers", index=16, toggle=True, text="")
+ row.prop(params, "hose_layers", index=17, toggle=True, text="")
+ row.prop(params, "hose_layers", index=18, toggle=True, text="")
+ row.prop(params, "hose_layers", index=19, toggle=True, text="")
+ row.prop(params, "hose_layers", index=20, toggle=True, text="")
+ row.prop(params, "hose_layers", index=21, toggle=True, text="")
+ row.prop(params, "hose_layers", index=22, toggle=True, text="")
+ row.prop(params, "hose_layers", index=23, toggle=True, text="")
+
+ col = r.column(align=True)
+ row = col.row(align=True)
+ row.prop(params, "hose_layers", index=8, toggle=True, text="")
+ row.prop(params, "hose_layers", index=9, toggle=True, text="")
+ row.prop(params, "hose_layers", index=10, toggle=True, text="")
+ row.prop(params, "hose_layers", index=11, toggle=True, text="")
+ row.prop(params, "hose_layers", index=12, toggle=True, text="")
+ row.prop(params, "hose_layers", index=13, toggle=True, text="")
+ row.prop(params, "hose_layers", index=14, toggle=True, text="")
+ row.prop(params, "hose_layers", index=15, toggle=True, text="")
+ row = col.row(align=True)
+ row.prop(params, "hose_layers", index=24, toggle=True, text="")
+ row.prop(params, "hose_layers", index=25, toggle=True, text="")
+ row.prop(params, "hose_layers", index=26, toggle=True, text="")
+ row.prop(params, "hose_layers", index=27, toggle=True, text="")
+ row.prop(params, "hose_layers", index=28, toggle=True, text="")
+ row.prop(params, "hose_layers", index=29, toggle=True, text="")
+ row.prop(params, "hose_layers", index=30, toggle=True, text="")
+ row.prop(params, "hose_layers", index=31, toggle=True, text="")
def create_sample(obj):
diff --git a/rigify/rigs/biped/arm/deform.py b/rigify/rigs/biped/arm/deform.py
index ad6d8634..a5444207 100644
--- a/rigify/rigs/biped/arm/deform.py
+++ b/rigify/rigs/biped/arm/deform.py
@@ -18,58 +18,13 @@
# <pep8 compliant>
-from math import acos
-
import bpy
-from mathutils import Vector, Matrix
+
+from .. import limb_common
from ....utils import MetarigError
-from ....utils import copy_bone, put_bone
from ....utils import connected_children_names
-from ....utils import strip_org, make_mechanism_name, make_deformer_name
-
-
-def align_roll(obj, bone1, bone2):
- bone1_e = obj.data.edit_bones[bone1]
- bone2_e = obj.data.edit_bones[bone2]
-
- bone1_e.roll = 0.0
-
- # Get the directions the bones are pointing in, as vectors
- y1 = bone1_e.y_axis
- x1 = bone1_e.x_axis
- y2 = bone2_e.y_axis
- x2 = bone2_e.x_axis
-
- # Get the shortest axis to rotate bone1 on to point in the same direction as bone2
- axis = y1.cross(y2)
- axis.normalize()
-
- # Angle to rotate on that shortest axis
- angle = y1.angle(y2)
-
- # Create rotation matrix to make bone1 point in the same direction as bone2
- rot_mat = Matrix.Rotation(angle, 3, axis)
-
- # Roll factor
- x3 = rot_mat * x1
- dot = x2 * x3
- if dot > 1.0:
- dot = 1.0
- elif dot < -1.0:
- dot = -1.0
- roll = acos(dot)
-
- # Set the roll
- bone1_e.roll = roll
-
- # Check if we rolled in the right direction
- x3 = rot_mat * bone1_e.x_axis
- check = x2 * x3
-
- # If not, reverse
- if check < 0.9999:
- bone1_e.roll = -roll
+from ....utils import strip_org
class Rig:
@@ -77,13 +32,6 @@ class Rig:
"""
def __init__(self, obj, bone, params):
- """ Gather and validate data about the rig.
- Store any data or references to data that will be needed later on.
- In particular, store references to bones that will be needed, and
- store names of bones that will be needed.
- Do NOT change any data in the scene. This is a gathering phase only.
-
- """
self.obj = obj
self.params = params
@@ -94,140 +42,17 @@ class Rig:
raise MetarigError("RIGIFY ERROR: Bone '%s': input to rig type must be a chain of 3 bones" % (strip_org(bone)))
# Get rig parameters
- self.use_upper_arm_twist = params.use_upper_arm_twist
- self.use_forearm_twist = params.use_forearm_twist
-
- def generate(self):
- """ Generate the rig.
- Do NOT modify any of the original bones, except for adding constraints.
- The main armature should be selected and active before this is called.
-
- """
- bpy.ops.object.mode_set(mode='EDIT')
-
- # Create upper arm bones
- if self.use_upper_arm_twist:
- uarm1 = copy_bone(self.obj, self.org_bones[0], make_deformer_name(strip_org(self.org_bones[0] + ".01")))
- uarm2 = copy_bone(self.obj, self.org_bones[0], make_deformer_name(strip_org(self.org_bones[0] + ".02")))
- utip = copy_bone(self.obj, self.org_bones[0], make_mechanism_name(strip_org(self.org_bones[0] + ".tip")))
+ if params.separate_hose_layers:
+ layers = list(params.hose_layers)
else:
- uarm = copy_bone(self.obj, self.org_bones[0], make_deformer_name(strip_org(self.org_bones[0])))
+ layers = None
+ use_complex_rig = params.use_complex_arm
+ elbow_base_name = params.elbow_base_name
+ primary_rotation_axis = params.primary_rotation_axis
- # Create forearm bones
- if self.use_forearm_twist:
- farm1 = copy_bone(self.obj, self.org_bones[1], make_deformer_name(strip_org(self.org_bones[1] + ".01")))
- farm2 = copy_bone(self.obj, self.org_bones[1], make_deformer_name(strip_org(self.org_bones[1] + ".02")))
- ftip = copy_bone(self.obj, self.org_bones[1], make_mechanism_name(strip_org(self.org_bones[1] + ".tip")))
- else:
- farm = copy_bone(self.obj, self.org_bones[1], make_deformer_name(strip_org(self.org_bones[1])))
-
- # Create hand bone
- hand = copy_bone(self.obj, self.org_bones[2], make_deformer_name(strip_org(self.org_bones[2])))
-
- # Get edit bones
- eb = self.obj.data.edit_bones
-
- org_uarm_e = eb[self.org_bones[0]]
- if self.use_upper_arm_twist:
- uarm1_e = eb[uarm1]
- uarm2_e = eb[uarm2]
- utip_e = eb[utip]
- else:
- uarm_e = eb[uarm]
+ # Based on common limb
+ self.rubber_hose_limb = limb_common.RubberHoseLimb(obj, self.org_bones[0], self.org_bones[1], self.org_bones[2], use_complex_rig, elbow_base_name, primary_rotation_axis, layers)
- org_farm_e = eb[self.org_bones[1]]
- if self.use_forearm_twist:
- farm1_e = eb[farm1]
- farm2_e = eb[farm2]
- ftip_e = eb[ftip]
- else:
- farm_e = eb[farm]
-
- org_hand_e = eb[self.org_bones[2]]
- hand_e = eb[hand]
-
- # Parent and position upper arm bones
- if self.use_upper_arm_twist:
- uarm1_e.use_connect = False
- uarm2_e.use_connect = False
- utip_e.use_connect = False
-
- uarm1_e.parent = org_uarm_e.parent
- uarm2_e.parent = org_uarm_e
- utip_e.parent = org_uarm_e
-
- center = Vector((org_uarm_e.head + org_uarm_e.tail) / 2)
-
- uarm1_e.tail = center
- uarm2_e.head = center
- put_bone(self.obj, utip, org_uarm_e.tail)
- utip_e.length = org_uarm_e.length / 8
- else:
- uarm_e.use_connect = False
- uarm_e.parent = org_uarm_e
-
- # Parent and position forearm bones
- if self.use_forearm_twist:
- farm1_e.use_connect = False
- farm2_e.use_connect = False
- ftip_e.use_connect = False
-
- farm1_e.parent = org_farm_e
- farm2_e.parent = org_farm_e
- ftip_e.parent = org_farm_e
-
- center = Vector((org_farm_e.head + org_farm_e.tail) / 2)
-
- farm1_e.tail = center
- farm2_e.head = center
- put_bone(self.obj, ftip, org_farm_e.tail)
- ftip_e.length = org_farm_e.length / 8
-
- # Align roll of farm2 with hand
- align_roll(self.obj, farm2, hand)
- else:
- farm_e.use_connect = False
- farm_e.parent = org_farm_e
-
- # Parent hand
- hand_e.use_connect = False
- hand_e.parent = org_hand_e
-
- # Object mode, get pose bones
- bpy.ops.object.mode_set(mode='OBJECT')
- pb = self.obj.pose.bones
-
- if self.use_upper_arm_twist:
- uarm1_p = pb[uarm1]
- if self.use_forearm_twist:
- farm2_p = pb[farm2]
- # hand_p = pb[hand] # UNUSED
-
- # Upper arm constraints
- if self.use_upper_arm_twist:
- con = uarm1_p.constraints.new('COPY_LOCATION')
- con.name = "copy_location"
- con.target = self.obj
- con.subtarget = self.org_bones[0]
-
- con = uarm1_p.constraints.new('COPY_SCALE')
- con.name = "copy_scale"
- con.target = self.obj
- con.subtarget = self.org_bones[0]
-
- con = uarm1_p.constraints.new('DAMPED_TRACK')
- con.name = "track_to"
- con.target = self.obj
- con.subtarget = utip
-
- # Forearm constraints
- if self.use_forearm_twist:
- con = farm2_p.constraints.new('COPY_ROTATION')
- con.name = "copy_rotation"
- con.target = self.obj
- con.subtarget = hand
-
- con = farm2_p.constraints.new('DAMPED_TRACK')
- con.name = "track_to"
- con.target = self.obj
- con.subtarget = ftip
+ def generate(self):
+ bone_list = self.rubber_hose_limb.generate()
+ return bone_list
diff --git a/rigify/rigs/biped/arm/fk.py b/rigify/rigs/biped/arm/fk.py
index 54b1a459..740b3277 100644
--- a/rigify/rigs/biped/arm/fk.py
+++ b/rigify/rigs/biped/arm/fk.py
@@ -19,14 +19,14 @@
# <pep8 compliant>
import bpy
-from rna_prop_ui import rna_idprop_ui_prop_get
+
+from .. import limb_common
from ....utils import MetarigError
-from ....utils import copy_bone
from ....utils import connected_children_names
-from ....utils import strip_org, make_mechanism_name, insert_before_lr
+from ....utils import create_widget
+from ....utils import strip_org
from ....utils import get_layers
-from ....utils import create_widget, create_limb_widget
class Rig:
@@ -42,27 +42,23 @@ class Rig:
"""
self.obj = obj
- self.params = params
# Get the chain of 3 connected bones
self.org_bones = [bone] + connected_children_names(self.obj, bone)[:2]
if len(self.org_bones) != 3:
- raise MetarigError("RIGIFY ERROR: Bone '%s': input to rig type must be a chain of 3 bones" % (strip_org(bone)))
-
- # Get (optional) parent
- if self.obj.data.bones[bone].parent is None:
- self.org_parent = None
- else:
- self.org_parent = self.obj.data.bones[bone].parent.name
+ raise MetarigError("RIGIFY ERROR: Bone '%s': input to rig type must be a chain of at least 3 bones" % (strip_org(bone)))
- # Get the rig parameters
+ # Get params
if "layers" in params:
- self.layers = get_layers(params["layers"])
+ layers = get_layers(params["layers"])
else:
- self.layers = None
+ layers = None
- self.primary_rotation_axis = params.primary_rotation_axis
+ primary_rotation_axis = params.primary_rotation_axis
+
+ # Arm is based on common limb
+ self.fk_limb = limb_common.FKLimb(obj, self.org_bones[0], self.org_bones[1], self.org_bones[2], primary_rotation_axis, layers)
def generate(self):
""" Generate the rig.
@@ -70,140 +66,12 @@ class Rig:
The main armature should be selected and active before this is called.
"""
- bpy.ops.object.mode_set(mode='EDIT')
-
- # Create the control bones
- uarm = copy_bone(self.obj, self.org_bones[0], strip_org(insert_before_lr(self.org_bones[0], ".fk")))
- farm = copy_bone(self.obj, self.org_bones[1], strip_org(insert_before_lr(self.org_bones[1], ".fk")))
- hand = copy_bone(self.obj, self.org_bones[2], strip_org(insert_before_lr(self.org_bones[2], ".fk")))
-
- # Create the hinge bones
- if self.org_parent != None:
- hinge = copy_bone(self.obj, self.org_parent, make_mechanism_name(uarm + ".hinge"))
- socket1 = copy_bone(self.obj, uarm, make_mechanism_name(uarm + ".socket1"))
- socket2 = copy_bone(self.obj, uarm, make_mechanism_name(uarm + ".socket2"))
-
- # Get edit bones
- eb = self.obj.data.edit_bones
-
- uarm_e = eb[uarm]
- farm_e = eb[farm]
- hand_e = eb[hand]
-
- if self.org_parent != None:
- hinge_e = eb[hinge]
- socket1_e = eb[socket1]
- socket2_e = eb[socket2]
-
- # Parenting
- farm_e.parent = uarm_e
- hand_e.parent = farm_e
-
- if self.org_parent != None:
- hinge_e.use_connect = False
- socket1_e.use_connect = False
- socket2_e.use_connect = False
-
- uarm_e.parent = hinge_e
- hinge_e.parent = socket2_e
- socket2_e.parent = None
-
- # Positioning
- if self.org_parent != None:
- center = (hinge_e.head + hinge_e.tail) / 2
- hinge_e.head = center
- socket1_e.length /= 4
- socket2_e.length /= 3
-
- # Object mode, get pose bones
- bpy.ops.object.mode_set(mode='OBJECT')
- pb = self.obj.pose.bones
-
- uarm_p = pb[uarm]
- farm_p = pb[farm]
- hand_p = pb[hand]
- if self.org_parent != None:
- hinge_p = pb[hinge]
-
- if self.org_parent != None:
- # socket1_p = pb[socket1] # UNUSED
- socket2_p = pb[socket2]
-
- # Set the elbow to only bend on the x-axis.
- farm_p.rotation_mode = 'XYZ'
- if 'X' in self.primary_rotation_axis:
- farm_p.lock_rotation = (False, True, True)
- elif 'Y' in self.primary_rotation_axis:
- farm_p.lock_rotation = (True, False, True)
- else:
- farm_p.lock_rotation = (True, True, False)
-
- # Hinge transforms are locked, for auto-ik
- if self.org_parent != None:
- hinge_p.lock_location = True, True, True
- hinge_p.lock_rotation = True, True, True
- hinge_p.lock_rotation_w = True
- hinge_p.lock_scale = True, True, True
-
- # Set up custom properties
- if self.org_parent != None:
- prop = rna_idprop_ui_prop_get(uarm_p, "isolate", create=True)
- uarm_p["isolate"] = 0.0
- prop["soft_min"] = prop["min"] = 0.0
- prop["soft_max"] = prop["max"] = 1.0
-
- # Hinge constraints / drivers
- if self.org_parent != None:
- con = socket2_p.constraints.new('COPY_LOCATION')
- con.name = "copy_location"
- con.target = self.obj
- con.subtarget = socket1
-
- con = socket2_p.constraints.new('COPY_TRANSFORMS')
- con.name = "isolate_off"
- con.target = self.obj
- con.subtarget = socket1
-
- # Driver
- fcurve = con.driver_add("influence")
- driver = fcurve.driver
- var = driver.variables.new()
- driver.type = 'AVERAGE'
- var.name = "var"
- var.targets[0].id_type = 'OBJECT'
- var.targets[0].id = self.obj
- var.targets[0].data_path = uarm_p.path_from_id() + '["isolate"]'
- mod = fcurve.modifiers[0]
- mod.poly_order = 1
- mod.coefficients[0] = 1.0
- mod.coefficients[1] = -1.0
-
- # Constrain org bones to controls
- con = pb[self.org_bones[0]].constraints.new('COPY_TRANSFORMS')
- con.name = "fk"
- con.target = self.obj
- con.subtarget = uarm
-
- con = pb[self.org_bones[1]].constraints.new('COPY_TRANSFORMS')
- con.name = "fk"
- con.target = self.obj
- con.subtarget = farm
-
- con = pb[self.org_bones[2]].constraints.new('COPY_TRANSFORMS')
- con.name = "fk"
- con.target = self.obj
- con.subtarget = hand
-
- # Set layers if specified
- if self.layers:
- uarm_p.bone.layers = self.layers
- farm_p.bone.layers = self.layers
- hand_p.bone.layers = self.layers
-
- # Create control widgets
- create_limb_widget(self.obj, uarm)
- create_limb_widget(self.obj, farm)
+ bone_list = self.fk_limb.generate()
+ uarm = bone_list[0]
+ farm = bone_list[1]
+ hand = bone_list[2]
+ # Create hand widget
ob = create_widget(self.obj, hand)
if ob != None:
verts = [(0.7, 1.5, 0.0), (0.7, -0.25, 0.0), (-0.7, -0.25, 0.0), (-0.7, 1.5, 0.0), (0.7, 0.723, 0.0), (-0.7, 0.723, 0.0), (0.7, 0.0, 0.0), (-0.7, 0.0, 0.0)]
diff --git a/rigify/rigs/biped/arm/ik.py b/rigify/rigs/biped/arm/ik.py
index 8f288601..d5c9509f 100644
--- a/rigify/rigs/biped/arm/ik.py
+++ b/rigify/rigs/biped/arm/ik.py
@@ -18,44 +18,14 @@
# <pep8 compliant>
-from math import pi, acos
-
import bpy
-from rna_prop_ui import rna_idprop_ui_prop_get
-from mathutils import Vector
+
+from .. import limb_common
from ....utils import MetarigError
-from ....utils import copy_bone
from ....utils import connected_children_names
-from ....utils import strip_org, make_mechanism_name, insert_before_lr
-from ....utils import create_widget, create_line_widget, create_sphere_widget
-
-
-def angle_on_plane(plane, vec1, vec2):
- """ Return the angle between two vectors projected onto a plane.
- """
- plane.normalize()
- vec1 = vec1 - (plane * (vec1.dot(plane)))
- vec2 = vec2 - (plane * (vec2.dot(plane)))
- vec1.normalize()
- vec2.normalize()
-
- # Determine the angle
- angle = acos(max(-1.0, min(1.0, vec1.dot(vec2))))
-
- if angle < 0.00001: # close enough to zero that sign doesn't matter
- return angle
-
- # Determine the sign of the angle
- vec3 = vec2.cross(vec1)
- vec3.normalize()
- sign = vec3.dot(plane)
- if sign >= 0:
- sign = 1
- else:
- sign = -1
-
- return angle * sign
+from ....utils import strip_org
+from ....utils import create_widget
class Rig:
@@ -73,23 +43,23 @@ class Rig:
"""
self.obj = obj
self.params = params
- self.switch = ikfk_switch
# Get the chain of 3 connected bones
self.org_bones = [bone] + connected_children_names(self.obj, bone)[:2]
-
if len(self.org_bones) != 3:
raise MetarigError("RIGIFY ERROR: Bone '%s': input to rig type must be a chain of 3 bones" % (strip_org(bone)))
# Get the rig parameters
if params.separate_ik_layers:
- self.layers = list(params.ik_layers)
+ layers = list(params.ik_layers)
else:
- self.layers = None
-
- self.bend_hint = params.bend_hint
+ layers = None
+ bend_hint = params.bend_hint
+ primary_rotation_axis = params.primary_rotation_axis
+ pole_target_base_name = self.params.elbow_base_name + "_target"
- self.primary_rotation_axis = params.primary_rotation_axis
+ # Arm is based on common limb
+ self.ik_limb = limb_common.IKLimb(obj, self.org_bones[0], self.org_bones[1], self.org_bones[2], pole_target_base_name, primary_rotation_axis, bend_hint, layers, ikfk_switch)
def generate(self):
""" Generate the rig.
@@ -97,236 +67,14 @@ class Rig:
The main armature should be selected and active before this is called.
"""
- bpy.ops.object.mode_set(mode='EDIT')
-
- # Create the bones
- uarm = copy_bone(self.obj, self.org_bones[0], make_mechanism_name(strip_org(insert_before_lr(self.org_bones[0], ".ik"))))
- farm = copy_bone(self.obj, self.org_bones[1], make_mechanism_name(strip_org(insert_before_lr(self.org_bones[1], ".ik"))))
-
- hand = copy_bone(self.obj, self.org_bones[2], strip_org(insert_before_lr(self.org_bones[2], ".ik")))
- pole_target_name = self.params.elbow_target_base_name + "." + insert_before_lr(self.org_bones[0], ".ik").split(".", 1)[1]
- pole = copy_bone(self.obj, self.org_bones[0], pole_target_name)
-
- vishand = copy_bone(self.obj, self.org_bones[2], "VIS-" + strip_org(insert_before_lr(self.org_bones[2], ".ik")))
- vispole = copy_bone(self.obj, self.org_bones[1], "VIS-" + strip_org(insert_before_lr(self.org_bones[0], "_pole.ik")))
-
- # Get edit bones
- eb = self.obj.data.edit_bones
-
- uarm_e = eb[uarm]
- farm_e = eb[farm]
- hand_e = eb[hand]
- pole_e = eb[pole]
- vishand_e = eb[vishand]
- vispole_e = eb[vispole]
-
- # Parenting
- farm_e.parent = uarm_e
-
- hand_e.use_connect = False
- hand_e.parent = None
-
- pole_e.use_connect = False
-
- vishand_e.use_connect = False
- vishand_e.parent = None
-
- vispole_e.use_connect = False
- vispole_e.parent = None
-
- # Misc
- hand_e.use_local_location = False
-
- vishand_e.hide_select = True
- vispole_e.hide_select = True
-
- # Positioning
- v1 = farm_e.tail - uarm_e.head
- if 'X' in self.primary_rotation_axis or 'Y' in self.primary_rotation_axis:
- v2 = v1.cross(farm_e.x_axis)
- if (v2 * farm_e.z_axis) > 0.0:
- v2 *= -1.0
- else:
- v2 = v1.cross(farm_e.z_axis)
- if (v2 * farm_e.x_axis) < 0.0:
- v2 *= -1.0
- v2.normalize()
- v2 *= v1.length
-
- if '-' in self.primary_rotation_axis:
- v2 *= -1
-
- pole_e.head = farm_e.head + v2
- pole_e.tail = pole_e.head + (Vector((0, 1, 0)) * (v1.length / 8))
- pole_e.roll = 0.0
-
- vishand_e.tail = vishand_e.head + Vector((0, 0, v1.length / 32))
- vispole_e.tail = vispole_e.head + Vector((0, 0, v1.length / 32))
-
- # Determine the pole offset value
- plane = (farm_e.tail - uarm_e.head).normalized()
- vec1 = uarm_e.x_axis.normalized()
- vec2 = (pole_e.head - uarm_e.head).normalized()
- pole_offset = angle_on_plane(plane, vec1, vec2)
-
- # Object mode, get pose bones
- bpy.ops.object.mode_set(mode='OBJECT')
- pb = self.obj.pose.bones
-
- # uarm_p = pb[uarm] # UNUSED
- farm_p = pb[farm]
- hand_p = pb[hand]
- pole_p = pb[pole]
- vishand_p = pb[vishand]
- vispole_p = pb[vispole]
-
- # Set the elbow to only bend on the primary axis
- if 'X' in self.primary_rotation_axis:
- farm_p.lock_ik_y = True
- farm_p.lock_ik_z = True
- elif 'Y' in self.primary_rotation_axis:
- farm_p.lock_ik_x = True
- farm_p.lock_ik_z = True
- else:
- farm_p.lock_ik_x = True
- farm_p.lock_ik_y = True
-
- # Pole target only translates
- pole_p.lock_location = False, False, False
- pole_p.lock_rotation = True, True, True
- pole_p.lock_rotation_w = True
- pole_p.lock_scale = True, True, True
-
- # Set up custom properties
- if self.switch is True:
- prop = rna_idprop_ui_prop_get(hand_p, "ikfk_switch", create=True)
- hand_p["ikfk_switch"] = 0.0
- prop["soft_min"] = prop["min"] = 0.0
- prop["soft_max"] = prop["max"] = 1.0
-
- # Bend direction hint
- if self.bend_hint:
- con = farm_p.constraints.new('LIMIT_ROTATION')
- con.name = "bend_hint"
- con.owner_space = 'LOCAL'
- if self.primary_rotation_axis == 'X':
- con.use_limit_x = True
- con.min_x = pi / 10
- con.max_x = pi / 10
- elif self.primary_rotation_axis == '-X':
- con.use_limit_x = True
- con.min_x = -pi / 10
- con.max_x = -pi / 10
- elif self.primary_rotation_axis == 'Y':
- con.use_limit_y = True
- con.min_y = pi / 10
- con.max_y = pi / 10
- elif self.primary_rotation_axis == '-Y':
- con.use_limit_y = True
- con.min_y = -pi / 10
- con.max_y = -pi / 10
- elif self.primary_rotation_axis == 'Z':
- con.use_limit_z = True
- con.min_z = pi / 10
- con.max_z = pi / 10
- elif self.primary_rotation_axis == '-Z':
- con.use_limit_z = True
- con.min_z = -pi / 10
- con.max_z = -pi / 10
-
- # IK Constraint
- con = farm_p.constraints.new('IK')
- con.name = "ik"
- con.target = self.obj
- con.subtarget = hand
- con.pole_target = self.obj
- con.pole_subtarget = pole
- con.pole_angle = pole_offset
- con.chain_count = 2
-
- # Constrain org bones to controls
- con = pb[self.org_bones[0]].constraints.new('COPY_TRANSFORMS')
- con.name = "ik"
- con.target = self.obj
- con.subtarget = uarm
- if self.switch is True:
- # IK/FK switch driver
- fcurve = con.driver_add("influence")
- driver = fcurve.driver
- var = driver.variables.new()
- driver.type = 'AVERAGE'
- var.name = "var"
- var.targets[0].id_type = 'OBJECT'
- var.targets[0].id = self.obj
- var.targets[0].data_path = hand_p.path_from_id() + '["ikfk_switch"]'
-
- con = pb[self.org_bones[1]].constraints.new('COPY_TRANSFORMS')
- con.name = "ik"
- con.target = self.obj
- con.subtarget = farm
- if self.switch is True:
- # IK/FK switch driver
- fcurve = con.driver_add("influence")
- driver = fcurve.driver
- var = driver.variables.new()
- driver.type = 'AVERAGE'
- var.name = "var"
- var.targets[0].id_type = 'OBJECT'
- var.targets[0].id = self.obj
- var.targets[0].data_path = hand_p.path_from_id() + '["ikfk_switch"]'
-
- con = pb[self.org_bones[2]].constraints.new('COPY_TRANSFORMS')
- con.name = "ik"
- con.target = self.obj
- con.subtarget = hand
- if self.switch is True:
- # IK/FK switch driver
- fcurve = con.driver_add("influence")
- driver = fcurve.driver
- var = driver.variables.new()
- driver.type = 'AVERAGE'
- var.name = "var"
- var.targets[0].id_type = 'OBJECT'
- var.targets[0].id = self.obj
- var.targets[0].data_path = hand_p.path_from_id() + '["ikfk_switch"]'
-
- # VIS hand constraints
- con = vishand_p.constraints.new('COPY_LOCATION')
- con.name = "copy_loc"
- con.target = self.obj
- con.subtarget = self.org_bones[2]
-
- con = vishand_p.constraints.new('STRETCH_TO')
- con.name = "stretch_to"
- con.target = self.obj
- con.subtarget = hand
- con.volume = 'NO_VOLUME'
- con.rest_length = vishand_p.length
-
- # VIS pole constraints
- con = vispole_p.constraints.new('COPY_LOCATION')
- con.name = "copy_loc"
- con.target = self.obj
- con.subtarget = self.org_bones[1]
-
- con = vispole_p.constraints.new('STRETCH_TO')
- con.name = "stretch_to"
- con.target = self.obj
- con.subtarget = pole
- con.volume = 'NO_VOLUME'
- con.rest_length = vispole_p.length
-
- # Set layers if specified
- if self.layers:
- hand_p.bone.layers = self.layers
- pole_p.bone.layers = self.layers
- vishand_p.bone.layers = self.layers
- vispole_p.bone.layers = self.layers
-
- # Create widgets
- create_line_widget(self.obj, vispole)
- create_line_widget(self.obj, vishand)
- create_sphere_widget(self.obj, pole)
+ bone_list = self.ik_limb.generate()
+ uarm = bone_list[0]
+ farm = bone_list[1]
+ hand = bone_list[2]
+ # hand_mch = bone_list[3]
+ pole = bone_list[4]
+ # vispole = bone_list[5]
+ # vishand = bone_list[6]
ob = create_widget(self.obj, hand)
if ob != None:
diff --git a/rigify/rigs/biped/leg/__init__.py b/rigify/rigs/biped/leg/__init__.py
index 36619c98..0a9f0ac7 100644
--- a/rigify/rigs/biped/leg/__init__.py
+++ b/rigify/rigs/biped/leg/__init__.py
@@ -31,13 +31,6 @@ fk_leg = ["%s", "%s", "%s", "%s"]
ik_leg = ["%s", "%s", "%s", "%s", "%s", "%s"]
if is_selected(fk_leg+ik_leg):
layout.prop(pose_bones[ik_leg[2]], '["ikfk_switch"]', text="FK / IK (" + ik_leg[2] + ")", slider=True)
-if is_selected(fk_leg):
- try:
- pose_bones[fk_leg[0]]["isolate"]
- layout.prop(pose_bones[fk_leg[0]], '["isolate"]', text="Isolate Rotation (" + fk_leg[0] + ")", slider=True)
- except KeyError:
- pass
-if is_selected(fk_leg+ik_leg):
p = layout.operator("pose.rigify_leg_fk2ik_" + rig_id, text="Snap FK->IK (" + fk_leg[0] + ")")
p.thigh_fk = fk_leg[0]
p.shin_fk = fk_leg[1]
@@ -45,6 +38,7 @@ if is_selected(fk_leg+ik_leg):
p.mfoot_fk = fk_leg[3]
p.thigh_ik = ik_leg[0]
p.shin_ik = ik_leg[1]
+ p.foot_ik = ik_leg[2]
p.mfoot_ik = ik_leg[5]
p = layout.operator("pose.rigify_leg_ik2fk_" + rig_id, text="Snap IK->FK (" + fk_leg[0] + ")")
p.thigh_fk = fk_leg[0]
@@ -56,6 +50,27 @@ if is_selected(fk_leg+ik_leg):
p.pole = ik_leg[3]
p.footroll = ik_leg[4]
p.mfoot_ik = ik_leg[5]
+if is_selected(fk_leg):
+ try:
+ pose_bones[fk_leg[0]]["isolate"]
+ layout.prop(pose_bones[fk_leg[0]], '["isolate"]', text="Isolate Rotation (" + fk_leg[0] + ")", slider=True)
+ except KeyError:
+ pass
+ layout.prop(pose_bones[fk_leg[0]], '["stretch_length"]', text="Length FK (" + fk_leg[0] + ")", slider=True)
+if is_selected(ik_leg):
+ layout.prop(pose_bones[ik_leg[2]], '["stretch_length"]', text="Length IK (" + ik_leg[2] + ")", slider=True)
+ layout.prop(pose_bones[ik_leg[2]], '["auto_stretch"]', text="Auto-Stretch IK (" + ik_leg[2] + ")", slider=True)
+"""
+
+hose_script = """
+hose_leg = ["%s", "%s", "%s"]
+if is_selected(hose_leg):
+ layout.prop(pose_bones[hose_leg[1]], '["smooth_bend"]', text="Smooth Knee (" + hose_leg[1] + ")", slider=True)
+"""
+
+end_script = """
+if is_selected(fk_leg+ik_leg):
+ layout.separator()
"""
@@ -70,6 +85,9 @@ class Rig:
Do NOT change any data in the scene. This is a gathering phase only.
"""
+ self.obj = obj
+ self.params = params
+
# Gather deform rig
self.deform_rig = deform.Rig(obj, bone, params)
@@ -85,10 +103,14 @@ class Rig:
The main armature should be selected and active before this is called.
"""
- self.deform_rig.generate()
+ hose_controls = self.deform_rig.generate()
fk_controls = self.fk_rig.generate()
ik_controls = self.ik_rig.generate()
- return [script % (fk_controls[0], fk_controls[1], fk_controls[2], fk_controls[3], ik_controls[0], ik_controls[1], ik_controls[2], ik_controls[3], ik_controls[4], ik_controls[5])]
+ ui_script = script % (fk_controls[0], fk_controls[1], fk_controls[2], fk_controls[3], ik_controls[0], ik_controls[1], ik_controls[2], ik_controls[3], ik_controls[4], ik_controls[5])
+ if self.params.use_complex_leg:
+ ui_script += hose_script % (hose_controls[0], hose_controls[1], hose_controls[2])
+ ui_script += end_script
+ return [ui_script]
def add_parameters(params):
@@ -96,26 +118,37 @@ def add_parameters(params):
RigifyParameters PropertyGroup
"""
+ params.use_complex_leg = bpy.props.BoolProperty(name="Complex Leg Rig", default=True, description="Generate the full, complex leg rig with twist bones and rubber-hose controls")
+ params.bend_hint = bpy.props.BoolProperty(name="Bend Hint", default=True, description="Give IK chain a hint about which way to bend (useful for perfectly straight chains)")
+
items = [('X', 'X', ''), ('Y', 'Y', ''), ('Z', 'Z', ''), ('-X', '-X', ''), ('-Y', '-Y', ''), ('-Z', '-Z', '')]
params.primary_rotation_axis = bpy.props.EnumProperty(items=items, name="Primary Rotation Axis", default='X')
- params.bend_hint = bpy.props.BoolProperty(name="Bend Hint", default=True, description="Give IK chain a hint about which way to bend (useful for perfectly straight chains)")
- params.knee_target_base_name = bpy.props.StringProperty(name="Knee Target Name", default="knee_target", description="Base name for the generated knee target")
-
+ params.knee_base_name = bpy.props.StringProperty(name="Knee Name", default="knee", description="Base name for the generated knee-related controls")
params.separate_ik_layers = bpy.props.BoolProperty(name="Separate IK Control Layers:", default=False, description="Enable putting the ik controls on a separate layer from the fk controls")
params.ik_layers = bpy.props.BoolVectorProperty(size=32, description="Layers for the ik controls to be on")
- params.use_thigh_twist = bpy.props.BoolProperty(name="Thigh Twist", default=True, description="Generate the dual-bone twist setup for the thigh")
- params.use_shin_twist = bpy.props.BoolProperty(name="Shin Twist", default=True, description="Generate the dual-bone twist setup for the shin")
+ params.separate_hose_layers = bpy.props.BoolProperty(name="Separate Rubber-hose Control Layers:", default=False, description="Enable putting the rubber-hose controls on a separate layer from the other controls")
+ params.hose_layers = bpy.props.BoolVectorProperty(size=32, description="Layers for the rubber-hose controls to be on")
def parameters_ui(layout, params):
""" Create the ui for the rig parameters.
"""
+ col = layout.column()
+ col.prop(params, "use_complex_leg")
+
+ r = layout.row()
+ r.label(text="Knee rotation axis:")
+ r.prop(params, "primary_rotation_axis", text="")
+
+ r = layout.row()
+ r.prop(params, "knee_base_name")
+
r = layout.row()
- r.prop(params, "knee_target_base_name")
+ r.prop(params, "bend_hint")
r = layout.row()
r.prop(params, "separate_ik_layers")
@@ -163,16 +196,52 @@ def parameters_ui(layout, params):
row.prop(params, "ik_layers", index=30, toggle=True, text="")
row.prop(params, "ik_layers", index=31, toggle=True, text="")
- r = layout.row()
- r.label(text="Knee rotation axis:")
- r.prop(params, "primary_rotation_axis", text="")
-
- r = layout.row()
- r.prop(params, "bend_hint")
-
- col = layout.column()
- col.prop(params, "use_thigh_twist")
- col.prop(params, "use_shin_twist")
+ if params.use_complex_leg:
+ r = layout.row()
+ r.prop(params, "separate_hose_layers")
+
+ r = layout.row()
+ r.active = params.separate_hose_layers
+
+ col = r.column(align=True)
+ row = col.row(align=True)
+ row.prop(params, "hose_layers", index=0, toggle=True, text="")
+ row.prop(params, "hose_layers", index=1, toggle=True, text="")
+ row.prop(params, "hose_layers", index=2, toggle=True, text="")
+ row.prop(params, "hose_layers", index=3, toggle=True, text="")
+ row.prop(params, "hose_layers", index=4, toggle=True, text="")
+ row.prop(params, "hose_layers", index=5, toggle=True, text="")
+ row.prop(params, "hose_layers", index=6, toggle=True, text="")
+ row.prop(params, "hose_layers", index=7, toggle=True, text="")
+ row = col.row(align=True)
+ row.prop(params, "hose_layers", index=16, toggle=True, text="")
+ row.prop(params, "hose_layers", index=17, toggle=True, text="")
+ row.prop(params, "hose_layers", index=18, toggle=True, text="")
+ row.prop(params, "hose_layers", index=19, toggle=True, text="")
+ row.prop(params, "hose_layers", index=20, toggle=True, text="")
+ row.prop(params, "hose_layers", index=21, toggle=True, text="")
+ row.prop(params, "hose_layers", index=22, toggle=True, text="")
+ row.prop(params, "hose_layers", index=23, toggle=True, text="")
+
+ col = r.column(align=True)
+ row = col.row(align=True)
+ row.prop(params, "hose_layers", index=8, toggle=True, text="")
+ row.prop(params, "hose_layers", index=9, toggle=True, text="")
+ row.prop(params, "hose_layers", index=10, toggle=True, text="")
+ row.prop(params, "hose_layers", index=11, toggle=True, text="")
+ row.prop(params, "hose_layers", index=12, toggle=True, text="")
+ row.prop(params, "hose_layers", index=13, toggle=True, text="")
+ row.prop(params, "hose_layers", index=14, toggle=True, text="")
+ row.prop(params, "hose_layers", index=15, toggle=True, text="")
+ row = col.row(align=True)
+ row.prop(params, "hose_layers", index=24, toggle=True, text="")
+ row.prop(params, "hose_layers", index=25, toggle=True, text="")
+ row.prop(params, "hose_layers", index=26, toggle=True, text="")
+ row.prop(params, "hose_layers", index=27, toggle=True, text="")
+ row.prop(params, "hose_layers", index=28, toggle=True, text="")
+ row.prop(params, "hose_layers", index=29, toggle=True, text="")
+ row.prop(params, "hose_layers", index=30, toggle=True, text="")
+ row.prop(params, "hose_layers", index=31, toggle=True, text="")
def create_sample(obj):
diff --git a/rigify/rigs/biped/leg/deform.py b/rigify/rigs/biped/leg/deform.py
index 0d0bb2a7..cde73250 100644
--- a/rigify/rigs/biped/leg/deform.py
+++ b/rigify/rigs/biped/leg/deform.py
@@ -18,58 +18,14 @@
# <pep8 compliant>
-from math import acos
-
import bpy
-from mathutils import Vector, Matrix
+
+from .. import limb_common
from ....utils import MetarigError
-from ....utils import copy_bone, put_bone
+from ....utils import copy_bone
from ....utils import connected_children_names, has_connected_children
-from ....utils import strip_org, make_mechanism_name, make_deformer_name
-
-
-def align_roll(obj, bone1, bone2):
- bone1_e = obj.data.edit_bones[bone1]
- bone2_e = obj.data.edit_bones[bone2]
-
- bone1_e.roll = 0.0
-
- # Get the directions the bones are pointing in, as vectors
- y1 = bone1_e.y_axis
- x1 = bone1_e.x_axis
- y2 = bone2_e.y_axis
- x2 = bone2_e.x_axis
-
- # Get the shortest axis to rotate bone1 on to point in the same direction as bone2
- axis = y1.cross(y2)
- axis.normalize()
-
- # Angle to rotate on that shortest axis
- angle = y1.angle(y2)
-
- # Create rotation matrix to make bone1 point in the same direction as bone2
- rot_mat = Matrix.Rotation(angle, 3, axis)
-
- # Roll factor
- x3 = rot_mat * x1
- dot = x2 * x3
- if dot > 1.0:
- dot = 1.0
- elif dot < -1.0:
- dot = -1.0
- roll = acos(dot)
-
- # Set the roll
- bone1_e.roll = roll
-
- # Check if we rolled in the right direction
- x3 = rot_mat * bone1_e.x_axis
- check = x2 * x3
-
- # If not, reverse
- if check < 0.9999:
- bone1_e.roll = -roll
+from ....utils import strip_org, make_deformer_name
class Rig:
@@ -77,13 +33,6 @@ class Rig:
"""
def __init__(self, obj, bone, params):
- """ Gather and validate data about the rig.
- Store any data or references to data that will be needed later on.
- In particular, store references to bones that will be needed, and
- store names of bones that will be needed.
- Do NOT change any data in the scene. This is a gathering phase only.
-
- """
self.obj = obj
self.params = params
@@ -104,9 +53,9 @@ class Rig:
heel = b.name
if foot is None:
- raise MetarigError("RIGIFY ERROR: Bone '%s': incorrect bone configuration for rig type -- could not find foot bone (that is, a bone with >1 children connected) attached to bone '%s'" % (strip_org(bone), strip_org(shin)))
+ raise MetarigError("RIGIFY ERROR: Bone '%s': incorrect bone configuration for rig type -- could not find foot bone (that is, a bone with >1 children connected) attached to bone '%s'" % (strip_org(bone), strip_org(leg_bones[1])))
if heel is None:
- raise MetarigError("RIGIFY ERROR: Bone '%s': incorrect bone configuration for rig type -- could not find heel bone (that is, a bone with no childrenconnected) attached to bone '%s'" % (strip_org(bone), strip_org(shin)))
+ raise MetarigError("RIGIFY ERROR: Bone '%s': incorrect bone configuration for rig type -- could not find heel bone (that is, a bone with no children connected) attached to bone '%s'" % (strip_org(bone), strip_org(leg_bones[1])))
# Get the toe
toe = None
for b in self.obj.data.bones[foot].children:
@@ -119,150 +68,25 @@ class Rig:
self.org_bones = leg_bones + [foot, toe, heel]
# Get rig parameters
- self.use_thigh_twist = params.use_thigh_twist
- self.use_shin_twist = params.use_shin_twist
-
- def generate(self):
- """ Generate the rig.
- Do NOT modify any of the original bones, except for adding constraints.
- The main armature should be selected and active before this is called.
-
- """
- bpy.ops.object.mode_set(mode='EDIT')
-
- # Create upper arm bones
- if self.use_thigh_twist:
- thigh1 = copy_bone(self.obj, self.org_bones[0], make_deformer_name(strip_org(self.org_bones[0] + ".01")))
- thigh2 = copy_bone(self.obj, self.org_bones[0], make_deformer_name(strip_org(self.org_bones[0] + ".02")))
- utip = copy_bone(self.obj, self.org_bones[0], make_mechanism_name(strip_org(self.org_bones[0] + ".tip")))
+ if params.separate_hose_layers:
+ layers = list(params.hose_layers)
else:
- thigh = copy_bone(self.obj, self.org_bones[0], make_deformer_name(strip_org(self.org_bones[0])))
+ layers = None
+ use_complex_rig = params.use_complex_leg
+ knee_base_name = params.knee_base_name
+ primary_rotation_axis = params.primary_rotation_axis
- # Create forearm bones
- if self.use_shin_twist:
- shin1 = copy_bone(self.obj, self.org_bones[1], make_deformer_name(strip_org(self.org_bones[1] + ".01")))
- shin2 = copy_bone(self.obj, self.org_bones[1], make_deformer_name(strip_org(self.org_bones[1] + ".02")))
- stip = copy_bone(self.obj, self.org_bones[1], make_mechanism_name(strip_org(self.org_bones[1] + ".tip")))
- else:
- shin = copy_bone(self.obj, self.org_bones[1], make_deformer_name(strip_org(self.org_bones[1])))
+ # Based on common limb
+ self.rubber_hose_limb = limb_common.RubberHoseLimb(obj, self.org_bones[0], self.org_bones[1], self.org_bones[2], use_complex_rig, knee_base_name, primary_rotation_axis, layers)
- # Create foot bone
- foot = copy_bone(self.obj, self.org_bones[2], make_deformer_name(strip_org(self.org_bones[2])))
+ def generate(self):
+ bone_list = self.rubber_hose_limb.generate()
- # Create toe bone
+ # Set up toe
+ bpy.ops.object.mode_set(mode='EDIT')
toe = copy_bone(self.obj, self.org_bones[3], make_deformer_name(strip_org(self.org_bones[3])))
-
- # Get edit bones
eb = self.obj.data.edit_bones
+ eb[toe].use_connect = False
+ eb[toe].parent = eb[self.org_bones[3]]
- org_thigh_e = eb[self.org_bones[0]]
- if self.use_thigh_twist:
- thigh1_e = eb[thigh1]
- thigh2_e = eb[thigh2]
- utip_e = eb[utip]
- else:
- thigh_e = eb[thigh]
-
- org_shin_e = eb[self.org_bones[1]]
- if self.use_shin_twist:
- shin1_e = eb[shin1]
- shin2_e = eb[shin2]
- stip_e = eb[stip]
- else:
- shin_e = eb[shin]
-
- org_foot_e = eb[self.org_bones[2]]
- foot_e = eb[foot]
-
- org_toe_e = eb[self.org_bones[3]]
- toe_e = eb[toe]
-
- # Parent and position thigh bones
- if self.use_thigh_twist:
- thigh1_e.use_connect = False
- thigh2_e.use_connect = False
- utip_e.use_connect = False
-
- thigh1_e.parent = org_thigh_e.parent
- thigh2_e.parent = org_thigh_e
- utip_e.parent = org_thigh_e
-
- center = Vector((org_thigh_e.head + org_thigh_e.tail) / 2)
-
- thigh1_e.tail = center
- thigh2_e.head = center
- put_bone(self.obj, utip, org_thigh_e.tail)
- utip_e.length = org_thigh_e.length / 8
- else:
- thigh_e.use_connect = False
- thigh_e.parent = org_thigh_e
-
- # Parent and position shin bones
- if self.use_shin_twist:
- shin1_e.use_connect = False
- shin2_e.use_connect = False
- stip_e.use_connect = False
-
- shin1_e.parent = org_shin_e
- shin2_e.parent = org_shin_e
- stip_e.parent = org_shin_e
-
- center = Vector((org_shin_e.head + org_shin_e.tail) / 2)
-
- shin1_e.tail = center
- shin2_e.head = center
- put_bone(self.obj, stip, org_shin_e.tail)
- stip_e.length = org_shin_e.length / 8
-
- # Align roll of shin2 with foot
- align_roll(self.obj, shin2, foot)
- else:
- shin_e.use_connect = False
- shin_e.parent = org_shin_e
-
- # Parent foot
- foot_e.use_connect = False
- foot_e.parent = org_foot_e
-
- # Parent toe
- toe_e.use_connect = False
- toe_e.parent = org_toe_e
-
- # Object mode, get pose bones
- bpy.ops.object.mode_set(mode='OBJECT')
- pb = self.obj.pose.bones
-
- if self.use_thigh_twist:
- thigh1_p = pb[thigh1]
- if self.use_shin_twist:
- shin2_p = pb[shin2]
- # foot_p = pb[foot] # UNUSED
-
- # Thigh constraints
- if self.use_thigh_twist:
- con = thigh1_p.constraints.new('COPY_LOCATION')
- con.name = "copy_location"
- con.target = self.obj
- con.subtarget = self.org_bones[0]
-
- con = thigh1_p.constraints.new('COPY_SCALE')
- con.name = "copy_scale"
- con.target = self.obj
- con.subtarget = self.org_bones[0]
-
- con = thigh1_p.constraints.new('DAMPED_TRACK')
- con.name = "track_to"
- con.target = self.obj
- con.subtarget = utip
-
- # Shin constraints
- if self.use_shin_twist:
- con = shin2_p.constraints.new('COPY_ROTATION')
- con.name = "copy_rotation"
- con.target = self.obj
- con.subtarget = foot
-
- con = shin2_p.constraints.new('DAMPED_TRACK')
- con.name = "track_to"
- con.target = self.obj
- con.subtarget = stip
+ return bone_list
diff --git a/rigify/rigs/biped/leg/fk.py b/rigify/rigs/biped/leg/fk.py
index 4e224ceb..743e3a19 100644
--- a/rigify/rigs/biped/leg/fk.py
+++ b/rigify/rigs/biped/leg/fk.py
@@ -19,15 +19,15 @@
# <pep8 compliant>
import bpy
-from rna_prop_ui import rna_idprop_ui_prop_get
from mathutils import Vector
+from .. import limb_common
+
from ....utils import MetarigError
-from ....utils import copy_bone
from ....utils import connected_children_names, has_connected_children
-from ....utils import strip_org, make_mechanism_name, insert_before_lr
+from ....utils import strip_org
from ....utils import get_layers
-from ....utils import create_widget, create_limb_widget
+from ....utils import create_widget
class Rig:
@@ -84,11 +84,14 @@ class Rig:
# Get rig parameters
if "layers" in params:
- self.layers = get_layers(params["layers"])
+ layers = get_layers(params["layers"])
else:
- self.layers = None
+ layers = None
+
+ primary_rotation_axis = params.primary_rotation_axis
- self.primary_rotation_axis = params.primary_rotation_axis
+ # Leg is based on common limb
+ self.fk_limb = limb_common.FKLimb(obj, self.org_bones[0], self.org_bones[1], self.org_bones[2], primary_rotation_axis, layers)
def generate(self):
""" Generate the rig.
@@ -96,152 +99,23 @@ class Rig:
The main armature should be selected and active before this is called.
"""
- bpy.ops.object.mode_set(mode='EDIT')
-
- # Create the control bones
- thigh = copy_bone(self.obj, self.org_bones[0], strip_org(insert_before_lr(self.org_bones[0], ".fk")))
- shin = copy_bone(self.obj, self.org_bones[1], strip_org(insert_before_lr(self.org_bones[1], ".fk")))
- foot = copy_bone(self.obj, self.org_bones[2], strip_org(insert_before_lr(self.org_bones[2], ".fk")))
+ ctrl_bones = self.fk_limb.generate()
+ thigh = ctrl_bones[0]
+ shin = ctrl_bones[1]
+ foot = ctrl_bones[2]
+ foot_mch = ctrl_bones[3]
- # Create the foot mechanism bone
- foot_mch = copy_bone(self.obj, self.org_bones[2], make_mechanism_name(strip_org(self.org_bones[2])))
-
- # Create the hinge bones
- if self.org_parent != None:
- hinge = copy_bone(self.obj, self.org_parent, make_mechanism_name(thigh + ".hinge"))
- socket1 = copy_bone(self.obj, thigh, make_mechanism_name(thigh + ".socket1"))
- socket2 = copy_bone(self.obj, thigh, make_mechanism_name(thigh + ".socket2"))
-
- # Get edit bones
+ # Position foot control
+ bpy.ops.object.mode_set(mode='EDIT')
eb = self.obj.data.edit_bones
-
- thigh_e = eb[thigh]
- shin_e = eb[shin]
foot_e = eb[foot]
- foot_mch_e = eb[foot_mch]
-
- if self.org_parent != None:
- hinge_e = eb[hinge]
- socket1_e = eb[socket1]
- socket2_e = eb[socket2]
-
- # Parenting
- shin_e.parent = thigh_e
- foot_e.parent = shin_e
-
- foot_mch_e.use_connect = False
- foot_mch_e.parent = foot_e
-
- if self.org_parent != None:
- hinge_e.use_connect = False
- socket1_e.use_connect = False
- socket2_e.use_connect = False
-
- thigh_e.parent = hinge_e
- hinge_e.parent = socket2_e
- socket2_e.parent = None
-
- # Positioning
vec = Vector(eb[self.org_bones[3]].vector)
vec.normalize()
foot_e.tail = foot_e.head + (vec * foot_e.length)
foot_e.roll = eb[self.org_bones[3]].roll
-
- if self.org_parent != None:
- center = (hinge_e.head + hinge_e.tail) / 2
- hinge_e.head = center
- socket1_e.length /= 4
- socket2_e.length /= 3
-
- # Object mode, get pose bones
bpy.ops.object.mode_set(mode='OBJECT')
- pb = self.obj.pose.bones
-
- thigh_p = pb[thigh]
- shin_p = pb[shin]
- foot_p = pb[foot]
- if self.org_parent != None:
- hinge_p = pb[hinge]
-
- if self.org_parent != None:
- # socket1_p = pb[socket1] # UNUSED
- socket2_p = pb[socket2]
-
- # Set the knee to only bend on the x-axis.
- shin_p.rotation_mode = 'XYZ'
- if 'X' in self.primary_rotation_axis:
- shin_p.lock_rotation = (False, True, True)
- elif 'Y' in self.primary_rotation_axis:
- shin_p.lock_rotation = (True, False, True)
- else:
- shin_p.lock_rotation = (True, True, False)
-
- # Hinge transforms are locked, for auto-ik
- if self.org_parent != None:
- hinge_p.lock_location = True, True, True
- hinge_p.lock_rotation = True, True, True
- hinge_p.lock_rotation_w = True
- hinge_p.lock_scale = True, True, True
-
- # Set up custom properties
- if self.org_parent != None:
- prop = rna_idprop_ui_prop_get(thigh_p, "isolate", create=True)
- thigh_p["isolate"] = 0.0
- prop["soft_min"] = prop["min"] = 0.0
- prop["soft_max"] = prop["max"] = 1.0
-
- # Hinge constraints / drivers
- if self.org_parent != None:
- con = socket2_p.constraints.new('COPY_LOCATION')
- con.name = "copy_location"
- con.target = self.obj
- con.subtarget = socket1
-
- con = socket2_p.constraints.new('COPY_TRANSFORMS')
- con.name = "isolate_off"
- con.target = self.obj
- con.subtarget = socket1
-
- # Driver
- fcurve = con.driver_add("influence")
- driver = fcurve.driver
- var = driver.variables.new()
- driver.type = 'AVERAGE'
- var.name = "var"
- var.targets[0].id_type = 'OBJECT'
- var.targets[0].id = self.obj
- var.targets[0].data_path = thigh_p.path_from_id() + '["isolate"]'
- mod = fcurve.modifiers[0]
- mod.poly_order = 1
- mod.coefficients[0] = 1.0
- mod.coefficients[1] = -1.0
-
- # Constrain org bones to controls
- con = pb[self.org_bones[0]].constraints.new('COPY_TRANSFORMS')
- con.name = "fk"
- con.target = self.obj
- con.subtarget = thigh
-
- con = pb[self.org_bones[1]].constraints.new('COPY_TRANSFORMS')
- con.name = "fk"
- con.target = self.obj
- con.subtarget = shin
-
- con = pb[self.org_bones[2]].constraints.new('COPY_TRANSFORMS')
- con.name = "fk"
- con.target = self.obj
- con.subtarget = foot_mch
-
- # Set layers if specified
- if self.layers:
- thigh_p.bone.layers = self.layers
- shin_p.bone.layers = self.layers
- foot_p.bone.layers = self.layers
-
- # Create control widgets
- create_limb_widget(self.obj, thigh)
- create_limb_widget(self.obj, shin)
+ # Create foot widget
ob = create_widget(self.obj, foot)
if ob != None:
verts = [(0.7, 1.5, 0.0), (0.7, -0.25, 0.0), (-0.7, -0.25, 0.0), (-0.7, 1.5, 0.0), (0.7, 0.723, 0.0), (-0.7, 0.723, 0.0), (0.7, 0.0, 0.0), (-0.7, 0.0, 0.0)]
diff --git a/rigify/rigs/biped/leg/ik.py b/rigify/rigs/biped/leg/ik.py
index 52eae332..aea8502b 100644
--- a/rigify/rigs/biped/leg/ik.py
+++ b/rigify/rigs/biped/leg/ik.py
@@ -18,67 +18,17 @@
# <pep8 compliant>
-from math import pi, acos
-
import bpy
-from rna_prop_ui import rna_idprop_ui_prop_get
from mathutils import Vector
+from .. import limb_common
+
from ....utils import MetarigError
+from ....utils import align_bone_x_axis
from ....utils import copy_bone, flip_bone, put_bone
from ....utils import connected_children_names, has_connected_children
from ....utils import strip_org, make_mechanism_name, insert_before_lr
-from ....utils import create_widget, create_line_widget, create_sphere_widget, create_circle_widget
-
-
-def align_x_axis(obj, bone, vec):
- """ Aligns the x-axis of a bone to the given vector. This only works if it
- can be an exact match.
- Must be in edit mode.
-
- """
- vec.normalize()
- bone_e = obj.data.edit_bones[bone]
- dot = max(-1.0, min(1.0, bone_e.x_axis.dot(vec)))
- angle = acos(dot)
-
- bone_e.roll += angle
-
- dot1 = bone_e.x_axis.dot(vec)
-
- bone_e.roll -= angle * 2
-
- dot2 = bone_e.x_axis.dot(vec)
-
- if dot1 > dot2:
- bone_e.roll += angle * 2
-
-
-def angle_on_plane(plane, vec1, vec2):
- """ Return the angle between two vectors projected onto a plane.
- """
- plane.normalize()
- vec1 = vec1 - (plane * (vec1.dot(plane)))
- vec2 = vec2 - (plane * (vec2.dot(plane)))
- vec1.normalize()
- vec2.normalize()
-
- # Determine the angle
- angle = acos(max(-1.0, min(1.0, vec1.dot(vec2))))
-
- if angle < 0.00001: # close enough to zero that sign doesn't matter
- return angle
-
- # Determine the sign of the angle
- vec3 = vec2.cross(vec1)
- vec3.normalize()
- sign = vec3.dot(plane)
- if sign >= 0:
- sign = 1
- else:
- sign = -1
-
- return angle * sign
+from ....utils import create_widget, create_circle_widget
class Rig:
@@ -136,32 +86,35 @@ class Rig:
self.layers = list(params.ik_layers)
else:
self.layers = None
+ bend_hint = params.bend_hint
+ primary_rotation_axis = params.primary_rotation_axis
+ pole_target_base_name = self.params.knee_base_name + "_target"
- self.bend_hint = params.bend_hint
-
- self.primary_rotation_axis = params.primary_rotation_axis
+ # Leg is based on common limb
+ self.ik_limb = limb_common.IKLimb(obj, self.org_bones[0], self.org_bones[1], self.org_bones[2], pole_target_base_name, primary_rotation_axis, bend_hint, self.layers, ikfk_switch)
def generate(self):
""" Generate the rig.
Do NOT modify any of the original bones, except for adding constraints.
The main armature should be selected and active before this is called.
-
"""
+ # Generate base IK limb
+ bone_list = self.ik_limb.generate()
+ thigh = bone_list[0]
+ shin = bone_list[1]
+ foot = bone_list[2]
+ foot_mch = bone_list[3]
+ pole = bone_list[4]
+ # vispole = bone_list[5]
+ # visfoot = bone_list[6]
+
+ # Build IK foot rig
bpy.ops.object.mode_set(mode='EDIT')
-
make_rocker = False
if self.org_bones[5] is not None:
make_rocker = True
# Create the bones
- thigh = copy_bone(self.obj, self.org_bones[0], make_mechanism_name(strip_org(insert_before_lr(self.org_bones[0], ".ik"))))
- shin = copy_bone(self.obj, self.org_bones[1], make_mechanism_name(strip_org(insert_before_lr(self.org_bones[1], ".ik"))))
-
- foot = copy_bone(self.obj, self.org_bones[2], strip_org(insert_before_lr(self.org_bones[2], ".ik")))
- foot_ik_target = copy_bone(self.obj, self.org_bones[2], make_mechanism_name(strip_org(insert_before_lr(self.org_bones[2], "_ik_target"))))
- pole_target_name = self.params.knee_target_base_name + "." + insert_before_lr(self.org_bones[0], ".ik").split(".", 1)[1]
- pole = copy_bone(self.obj, self.org_bones[0], pole_target_name)
-
toe = copy_bone(self.obj, self.org_bones[3], strip_org(self.org_bones[3]))
toe_parent = copy_bone(self.obj, self.org_bones[2], make_mechanism_name(strip_org(self.org_bones[3] + ".parent")))
toe_parent_socket1 = copy_bone(self.obj, self.org_bones[2], make_mechanism_name(strip_org(self.org_bones[3] + ".socket1")))
@@ -175,18 +128,12 @@ class Rig:
rocker1 = copy_bone(self.obj, self.org_bones[5], make_mechanism_name(strip_org(self.org_bones[2] + ".rocker.01")))
rocker2 = copy_bone(self.obj, self.org_bones[5], make_mechanism_name(strip_org(self.org_bones[2] + ".rocker.02")))
- visfoot = copy_bone(self.obj, self.org_bones[2], "VIS-" + strip_org(insert_before_lr(self.org_bones[2], ".ik")))
- vispole = copy_bone(self.obj, self.org_bones[1], "VIS-" + strip_org(insert_before_lr(self.org_bones[0], "_pole.ik")))
-
# Get edit bones
eb = self.obj.data.edit_bones
org_foot_e = eb[self.org_bones[2]]
- thigh_e = eb[thigh]
- shin_e = eb[shin]
foot_e = eb[foot]
- foot_ik_target_e = eb[foot_ik_target]
- pole_e = eb[pole]
+ foot_ik_target_e = eb[foot_mch]
toe_e = eb[toe]
toe_parent_e = eb[toe_parent]
toe_parent_socket1_e = eb[toe_parent_socket1]
@@ -197,20 +144,11 @@ class Rig:
if make_rocker:
rocker1_e = eb[rocker1]
rocker2_e = eb[rocker2]
- visfoot_e = eb[visfoot]
- vispole_e = eb[vispole]
# Parenting
- shin_e.parent = thigh_e
-
- foot_e.use_connect = False
- foot_e.parent = None
foot_ik_target_e.use_connect = False
foot_ik_target_e.parent = roll2_e
- pole_e.use_connect = False
- pole_e.parent = foot_e
-
toe_e.parent = toe_parent_e
toe_parent_e.use_connect = False
toe_parent_e.parent = toe_parent_socket1_e
@@ -228,12 +166,6 @@ class Rig:
roll2_e.use_connect = False
roll2_e.parent = roll1_e
- visfoot_e.use_connect = False
- visfoot_e.parent = None
-
- vispole_e.use_connect = False
- vispole_e.parent = None
-
if make_rocker:
rocker1_e.use_connect = False
rocker2_e.use_connect = False
@@ -242,38 +174,12 @@ class Rig:
rocker2_e.parent = rocker1_e
rocker1_e.parent = foot_e
- # Misc
- foot_e.use_local_location = False
-
- visfoot_e.hide_select = True
- vispole_e.hide_select = True
-
# Positioning
vec = Vector(toe_e.vector)
vec.normalize()
foot_e.tail = foot_e.head + (vec * foot_e.length)
foot_e.roll = toe_e.roll
- v1 = shin_e.tail - thigh_e.head
-
- if 'X' in self.primary_rotation_axis or 'Y' in self.primary_rotation_axis:
- v2 = v1.cross(shin_e.x_axis)
- if (v2 * shin_e.z_axis) > 0.0:
- v2 *= -1.0
- else:
- v2 = v1.cross(shin_e.z_axis)
- if (v2 * shin_e.x_axis) < 0.0:
- v2 *= -1.0
- v2.normalize()
- v2 *= v1.length
-
- if '-' in self.primary_rotation_axis:
- v2 *= -1
-
- pole_e.head = shin_e.head + v2
- pole_e.tail = pole_e.head + (Vector((0, 1, 0)) * (v1.length / 8))
- pole_e.roll = 0.0
-
flip_bone(self.obj, toe_parent_socket1)
flip_bone(self.obj, toe_parent_socket2)
toe_parent_socket1_e.head = Vector(org_foot_e.tail)
@@ -292,13 +198,10 @@ class Rig:
foot_roll_e.length /= 2
roll_axis = roll1_e.vector.cross(org_foot_e.vector)
- align_x_axis(self.obj, roll1, roll_axis)
- align_x_axis(self.obj, roll2, roll_axis)
+ align_bone_x_axis(self.obj, roll1, roll_axis)
+ align_bone_x_axis(self.obj, roll2, roll_axis)
foot_roll_e.roll = roll2_e.roll
- visfoot_e.tail = visfoot_e.head + Vector((0, 0, v1.length / 32))
- vispole_e.tail = vispole_e.head + Vector((0, 0, v1.length / 32))
-
if make_rocker:
d = toe_e.y_axis.dot(rocker1_e.x_axis)
if d >= 0.0:
@@ -306,30 +209,11 @@ class Rig:
else:
flip_bone(self.obj, rocker1)
- # Weird alignment issues. Fix.
- toe_parent_e.head = Vector(org_foot_e.head)
- toe_parent_e.tail = Vector(org_foot_e.tail)
- toe_parent_e.roll = org_foot_e.roll
-
- foot_e.head = Vector(org_foot_e.head)
-
- foot_ik_target_e.head = Vector(org_foot_e.head)
- foot_ik_target_e.tail = Vector(org_foot_e.tail)
-
- # Determine the pole offset value
- plane = (shin_e.tail - thigh_e.head).normalized()
- vec1 = thigh_e.x_axis.normalized()
- vec2 = (pole_e.head - thigh_e.head).normalized()
- pole_offset = angle_on_plane(plane, vec1, vec2)
-
# Object mode, get pose bones
bpy.ops.object.mode_set(mode='OBJECT')
pb = self.obj.pose.bones
- # thigh_p = pb[thigh] # UNUSED
- shin_p = pb[shin]
foot_p = pb[foot]
- pole_p = pb[pole]
foot_roll_p = pb[foot_roll]
roll1_p = pb[roll1]
roll2_p = pb[roll2]
@@ -337,21 +221,8 @@ class Rig:
rocker1_p = pb[rocker1]
rocker2_p = pb[rocker2]
toe_p = pb[toe]
- toe_parent_p = pb[toe_parent]
+ # toe_parent_p = pb[toe_parent]
toe_parent_socket1_p = pb[toe_parent_socket1]
- visfoot_p = pb[visfoot]
- vispole_p = pb[vispole]
-
- # Set the knee to only bend on the primary axis.
- if 'X' in self.primary_rotation_axis:
- shin_p.lock_ik_y = True
- shin_p.lock_ik_z = True
- elif 'Y' in self.primary_rotation_axis:
- shin_p.lock_ik_x = True
- shin_p.lock_ik_z = True
- else:
- shin_p.lock_ik_x = True
- shin_p.lock_ik_y = True
# Foot roll control only rotates on x-axis, or x and y if rocker.
foot_roll_p.rotation_mode = 'XYZ'
@@ -369,59 +240,6 @@ class Rig:
rocker1_p.rotation_mode = 'XYZ'
rocker2_p.rotation_mode = 'XYZ'
- # Pole target only translates
- pole_p.lock_location = False, False, False
- pole_p.lock_rotation = True, True, True
- pole_p.lock_rotation_w = True
- pole_p.lock_scale = True, True, True
-
- # Set up custom properties
- if self.switch is True:
- prop = rna_idprop_ui_prop_get(foot_p, "ikfk_switch", create=True)
- foot_p["ikfk_switch"] = 0.0
- prop["soft_min"] = prop["min"] = 0.0
- prop["soft_max"] = prop["max"] = 1.0
-
- # Bend direction hint
- if self.bend_hint:
- con = shin_p.constraints.new('LIMIT_ROTATION')
- con.name = "bend_hint"
- con.owner_space = 'LOCAL'
- if self.primary_rotation_axis == 'X':
- con.use_limit_x = True
- con.min_x = pi / 10
- con.max_x = pi / 10
- elif self.primary_rotation_axis == '-X':
- con.use_limit_x = True
- con.min_x = -pi / 10
- con.max_x = -pi / 10
- elif self.primary_rotation_axis == 'Y':
- con.use_limit_y = True
- con.min_y = pi / 10
- con.max_y = pi / 10
- elif self.primary_rotation_axis == '-Y':
- con.use_limit_y = True
- con.min_y = -pi / 10
- con.max_y = -pi / 10
- elif self.primary_rotation_axis == 'Z':
- con.use_limit_z = True
- con.min_z = pi / 10
- con.max_z = pi / 10
- elif self.primary_rotation_axis == '-Z':
- con.use_limit_z = True
- con.min_z = -pi / 10
- con.max_z = -pi / 10
-
- # IK Constraint
- con = shin_p.constraints.new('IK')
- con.name = "ik"
- con.target = self.obj
- con.subtarget = foot_ik_target
- con.pole_target = self.obj
- con.pole_subtarget = pole
- con.pole_angle = pole_offset
- con.chain_count = 2
-
# toe_parent constraint
con = toe_parent_socket1_p.constraints.new('COPY_LOCATION')
con.name = "copy_location"
@@ -493,103 +311,24 @@ class Rig:
var.targets[0].id = self.obj
var.targets[0].data_path = foot_roll_p.path_from_id() + '.rotation_euler[1]'
- # Constrain org bones to controls
- con = pb[self.org_bones[0]].constraints.new('COPY_TRANSFORMS')
- con.name = "ik"
- con.target = self.obj
- con.subtarget = thigh
- if self.switch is True:
- # IK/FK switch driver
- fcurve = con.driver_add("influence")
- driver = fcurve.driver
- var = driver.variables.new()
- driver.type = 'AVERAGE'
- var.name = "var"
- var.targets[0].id_type = 'OBJECT'
- var.targets[0].id = self.obj
- var.targets[0].data_path = foot_p.path_from_id() + '["ikfk_switch"]'
-
- con = pb[self.org_bones[1]].constraints.new('COPY_TRANSFORMS')
- con.name = "ik"
- con.target = self.obj
- con.subtarget = shin
- if self.switch is True:
- # IK/FK switch driver
- fcurve = con.driver_add("influence")
- driver = fcurve.driver
- var = driver.variables.new()
- driver.type = 'AVERAGE'
- var.name = "var"
- var.targets[0].id_type = 'OBJECT'
- var.targets[0].id = self.obj
- var.targets[0].data_path = foot_p.path_from_id() + '["ikfk_switch"]'
-
- con = pb[self.org_bones[2]].constraints.new('COPY_TRANSFORMS')
- con.name = "ik"
- con.target = self.obj
- con.subtarget = foot_ik_target
- if self.switch is True:
- # IK/FK switch driver
- fcurve = con.driver_add("influence")
- driver = fcurve.driver
- var = driver.variables.new()
- driver.type = 'AVERAGE'
- var.name = "var"
- var.targets[0].id_type = 'OBJECT'
- var.targets[0].id = self.obj
- var.targets[0].data_path = foot_p.path_from_id() + '["ikfk_switch"]'
-
+ # Constrain toe bone to toe control
con = pb[self.org_bones[3]].constraints.new('COPY_TRANSFORMS')
con.name = "copy_transforms"
con.target = self.obj
con.subtarget = toe
- # VIS foot constraints
- con = visfoot_p.constraints.new('COPY_LOCATION')
- con.name = "copy_loc"
- con.target = self.obj
- con.subtarget = self.org_bones[2]
-
- con = visfoot_p.constraints.new('STRETCH_TO')
- con.name = "stretch_to"
- con.target = self.obj
- con.subtarget = foot
- con.volume = 'NO_VOLUME'
- con.rest_length = visfoot_p.length
-
- # VIS pole constraints
- con = vispole_p.constraints.new('COPY_LOCATION')
- con.name = "copy_loc"
- con.target = self.obj
- con.subtarget = self.org_bones[1]
-
- con = vispole_p.constraints.new('STRETCH_TO')
- con.name = "stretch_to"
- con.target = self.obj
- con.subtarget = pole
- con.volume = 'NO_VOLUME'
- con.rest_length = vispole_p.length
-
# Set layers if specified
if self.layers:
- foot_p.bone.layers = self.layers
- pole_p.bone.layers = self.layers
foot_roll_p.bone.layers = self.layers
- visfoot_p.bone.layers = self.layers
- vispole_p.bone.layers = self.layers
-
toe_p.bone.layers = [(i[0] or i[1]) for i in zip(toe_p.bone.layers, self.layers)] # Both FK and IK layers
# Create widgets
- create_line_widget(self.obj, vispole)
- create_line_widget(self.obj, visfoot)
- create_sphere_widget(self.obj, pole)
create_circle_widget(self.obj, toe, radius=0.7, head_tail=0.5)
- ob = create_widget(self.obj, foot)
+ ob = create_widget(self.obj, foot_roll)
if ob != None:
- verts = [(0.7, 1.5, 0.0), (0.7, -0.25, 0.0), (-0.7, -0.25, 0.0), (-0.7, 1.5, 0.0), (0.7, 0.723, 0.0), (-0.7, 0.723, 0.0), (0.7, 0.0, 0.0), (-0.7, 0.0, 0.0)]
- edges = [(1, 2), (0, 3), (0, 4), (3, 5), (4, 6), (1, 6), (5, 7), (2, 7)]
+ verts = [(0.3999999761581421, 0.766044557094574, 0.6427875757217407), (0.17668449878692627, 3.823702598992895e-08, 3.2084670920085046e-08), (-0.17668461799621582, 9.874240447516058e-08, 8.285470443070153e-08), (-0.39999961853027344, 0.7660449147224426, 0.6427879333496094), (0.3562471270561218, 0.6159579753875732, 0.5168500542640686), (-0.35624682903289795, 0.6159582138061523, 0.5168502926826477), (0.20492683351039886, 0.09688037633895874, 0.0812922865152359), (-0.20492687821388245, 0.0968804731965065, 0.08129236847162247)]
+ edges = [(1, 2), (0, 3), (0, 4), (3, 5), (1, 6), (4, 6), (2, 7), (5, 7)]
mesh = ob.data
mesh.from_pydata(verts, edges, [])
mesh.update()
@@ -597,10 +336,10 @@ class Rig:
mod = ob.modifiers.new("subsurf", 'SUBSURF')
mod.levels = 2
- ob = create_widget(self.obj, foot_roll)
+ ob = create_widget(self.obj, foot)
if ob != None:
- verts = [(0.3999999761581421, 0.766044557094574, 0.6427875757217407), (0.17668449878692627, 3.823702598992895e-08, 3.2084670920085046e-08), (-0.17668461799621582, 9.874240447516058e-08, 8.285470443070153e-08), (-0.39999961853027344, 0.7660449147224426, 0.6427879333496094), (0.3562471270561218, 0.6159579753875732, 0.5168500542640686), (-0.35624682903289795, 0.6159582138061523, 0.5168502926826477), (0.20492683351039886, 0.09688037633895874, 0.0812922865152359), (-0.20492687821388245, 0.0968804731965065, 0.08129236847162247)]
- edges = [(1, 2), (0, 3), (0, 4), (3, 5), (1, 6), (4, 6), (2, 7), (5, 7)]
+ verts = [(0.7, 1.5, 0.0), (0.7, -0.25, 0.0), (-0.7, -0.25, 0.0), (-0.7, 1.5, 0.0), (0.7, 0.723, 0.0), (-0.7, 0.723, 0.0), (0.7, 0.0, 0.0), (-0.7, 0.0, 0.0)]
+ edges = [(1, 2), (0, 3), (0, 4), (3, 5), (4, 6), (1, 6), (5, 7), (2, 7)]
mesh = ob.data
mesh.from_pydata(verts, edges, [])
mesh.update()
@@ -608,4 +347,4 @@ class Rig:
mod = ob.modifiers.new("subsurf", 'SUBSURF')
mod.levels = 2
- return [thigh, shin, foot, pole, foot_roll, foot_ik_target]
+ return [thigh, shin, foot, pole, foot_roll, foot_mch]
diff --git a/rigify/rigs/biped/limb_common.py b/rigify/rigs/biped/limb_common.py
new file mode 100644
index 00000000..ec29a510
--- /dev/null
+++ b/rigify/rigs/biped/limb_common.py
@@ -0,0 +1,1139 @@
+#====================== 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 ========================
+
+from math import pi
+
+import bpy
+from rna_prop_ui import rna_idprop_ui_prop_get
+from mathutils import Vector
+
+from ...utils import angle_on_plane, align_bone_roll, align_bone_z_axis
+from ...utils import new_bone, copy_bone, put_bone, make_nonscaling_child
+from ...utils import strip_org, make_mechanism_name, make_deformer_name, insert_before_lr
+from ...utils import create_widget, create_limb_widget, create_line_widget, create_sphere_widget
+
+
+class FKLimb:
+ def __init__(self, obj, bone1, bone2, bone3, primary_rotation_axis, layers):
+ self.obj = obj
+
+ self.org_bones = [bone1, bone2, bone3]
+
+ # Get (optional) parent
+ if self.obj.data.bones[bone1].parent is None:
+ self.org_parent = None
+ else:
+ self.org_parent = self.obj.data.bones[bone1].parent.name
+
+ # Get the rig parameters
+ self.layers = layers
+ self.primary_rotation_axis = primary_rotation_axis
+
+ def generate(self):
+ bpy.ops.object.mode_set(mode='EDIT')
+
+ # Create non-scaling parent bone
+ if self.org_parent != None:
+ loc = Vector(self.obj.data.edit_bones[self.org_bones[0]].head)
+ parent = make_nonscaling_child(self.obj, self.org_parent, loc, "_fk")
+ else:
+ parent = None
+
+ # Create the control bones
+ ulimb = copy_bone(self.obj, self.org_bones[0], strip_org(insert_before_lr(self.org_bones[0], ".fk")))
+ flimb = copy_bone(self.obj, self.org_bones[1], strip_org(insert_before_lr(self.org_bones[1], ".fk")))
+ elimb = copy_bone(self.obj, self.org_bones[2], strip_org(insert_before_lr(self.org_bones[2], ".fk")))
+
+ # Create the anti-stretch bones
+ fantistr = copy_bone(self.obj, self.org_bones[0], make_mechanism_name(strip_org(insert_before_lr(self.org_bones[0], "_antistr.fk"))))
+ eantistr = copy_bone(self.obj, self.org_bones[1], make_mechanism_name(strip_org(insert_before_lr(self.org_bones[1], "_antistr.fk"))))
+
+ # Create the end-limb mechanism bone
+ elimb_mch = copy_bone(self.obj, self.org_bones[2], make_mechanism_name(strip_org(self.org_bones[2])))
+
+ # Create the hinge bones
+ if parent != None:
+ socket1 = copy_bone(self.obj, ulimb, make_mechanism_name(ulimb + ".socket1"))
+ socket2 = copy_bone(self.obj, ulimb, make_mechanism_name(ulimb + ".socket2"))
+
+ # Get edit bones
+ eb = self.obj.data.edit_bones
+
+ ulimb_e = eb[ulimb]
+ flimb_e = eb[flimb]
+ elimb_e = eb[elimb]
+
+ fantistr_e = eb[fantistr]
+ eantistr_e = eb[eantistr]
+
+ elimb_mch_e = eb[elimb_mch]
+
+ if parent != None:
+ socket1_e = eb[socket1]
+ socket2_e = eb[socket2]
+
+ # Parenting
+ elimb_mch_e.use_connect = False
+ elimb_mch_e.parent = elimb_e
+
+ elimb_e.use_connect = False
+ elimb_e.parent = eantistr_e
+
+ eantistr_e.use_connect = False
+ eantistr_e.parent = flimb_e
+
+ flimb_e.use_connect = False
+ flimb_e.parent = fantistr_e
+
+ fantistr_e.use_connect = False
+ fantistr_e.parent = ulimb_e
+
+ if parent != None:
+ socket1_e.use_connect = False
+ socket1_e.parent = eb[parent]
+
+ socket2_e.use_connect = False
+ socket2_e.parent = None
+
+ ulimb_e.use_connect = False
+ ulimb_e.parent = socket2_e
+
+ # Positioning
+ fantistr_e.length /= 8
+ put_bone(self.obj, fantistr, Vector(ulimb_e.tail))
+ eantistr_e.length /= 8
+ put_bone(self.obj, eantistr, Vector(flimb_e.tail))
+
+ if parent != None:
+ socket1_e.length /= 4
+ socket2_e.length /= 3
+
+ # Object mode, get pose bones
+ bpy.ops.object.mode_set(mode='OBJECT')
+ pb = self.obj.pose.bones
+
+ ulimb_p = pb[ulimb]
+ flimb_p = pb[flimb]
+ elimb_p = pb[elimb]
+
+ fantistr_p = pb[fantistr]
+ eantistr_p = pb[eantistr]
+
+ if parent != None:
+ socket2_p = pb[socket2]
+
+ # Lock axes
+ ulimb_p.lock_location = (True, True, True)
+ flimb_p.lock_location = (True, True, True)
+ elimb_p.lock_location = (True, True, True)
+
+ # Set the elbow to only bend on the x-axis.
+ flimb_p.rotation_mode = 'XYZ'
+ if 'X' in self.primary_rotation_axis:
+ flimb_p.lock_rotation = (False, True, True)
+ elif 'Y' in self.primary_rotation_axis:
+ flimb_p.lock_rotation = (True, False, True)
+ else:
+ flimb_p.lock_rotation = (True, True, False)
+
+ # Set up custom properties
+ if parent != None:
+ prop = rna_idprop_ui_prop_get(ulimb_p, "isolate", create=True)
+ ulimb_p["isolate"] = 0.0
+ prop["soft_min"] = prop["min"] = 0.0
+ prop["soft_max"] = prop["max"] = 1.0
+
+ prop = rna_idprop_ui_prop_get(ulimb_p, "stretch_length", create=True)
+ ulimb_p["stretch_length"] = 1.0
+ prop["min"] = 0.05
+ prop["max"] = 20.0
+ prop["soft_min"] = 0.25
+ prop["soft_max"] = 4.0
+
+ # Stretch drivers
+ def add_stretch_drivers(pose_bone):
+ driver = pose_bone.driver_add("scale", 1).driver
+ var = driver.variables.new()
+ var.name = "stretch_length"
+ var.targets[0].id_type = 'OBJECT'
+ var.targets[0].id = self.obj
+ var.targets[0].data_path = ulimb_p.path_from_id() + '["stretch_length"]'
+ driver.type = 'SCRIPTED'
+ driver.expression = "stretch_length"
+
+ driver = pose_bone.driver_add("scale", 0).driver
+ var = driver.variables.new()
+ var.name = "stretch_length"
+ var.targets[0].id_type = 'OBJECT'
+ var.targets[0].id = self.obj
+ var.targets[0].data_path = ulimb_p.path_from_id() + '["stretch_length"]'
+ driver.type = 'SCRIPTED'
+ driver.expression = "1/sqrt(stretch_length)"
+
+ driver = pose_bone.driver_add("scale", 2).driver
+ var = driver.variables.new()
+ var.name = "stretch_length"
+ var.targets[0].id_type = 'OBJECT'
+ var.targets[0].id = self.obj
+ var.targets[0].data_path = ulimb_p.path_from_id() + '["stretch_length"]'
+ driver.type = 'SCRIPTED'
+ driver.expression = "1/sqrt(stretch_length)"
+
+ def add_antistretch_drivers(pose_bone):
+ driver = pose_bone.driver_add("scale", 1).driver
+ var = driver.variables.new()
+ var.name = "stretch_length"
+ var.targets[0].id_type = 'OBJECT'
+ var.targets[0].id = self.obj
+ var.targets[0].data_path = ulimb_p.path_from_id() + '["stretch_length"]'
+ driver.type = 'SCRIPTED'
+ driver.expression = "1/stretch_length"
+
+ driver = pose_bone.driver_add("scale", 0).driver
+ var = driver.variables.new()
+ var.name = "stretch_length"
+ var.targets[0].id_type = 'OBJECT'
+ var.targets[0].id = self.obj
+ var.targets[0].data_path = ulimb_p.path_from_id() + '["stretch_length"]'
+ driver.type = 'SCRIPTED'
+ driver.expression = "sqrt(stretch_length)"
+
+ driver = pose_bone.driver_add("scale", 2).driver
+ var = driver.variables.new()
+ var.name = "stretch_length"
+ var.targets[0].id_type = 'OBJECT'
+ var.targets[0].id = self.obj
+ var.targets[0].data_path = ulimb_p.path_from_id() + '["stretch_length"]'
+ driver.type = 'SCRIPTED'
+ driver.expression = "sqrt(stretch_length)"
+
+ add_stretch_drivers(ulimb_p)
+ add_stretch_drivers(flimb_p)
+ add_antistretch_drivers(fantistr_p)
+ add_antistretch_drivers(eantistr_p)
+
+ # Hinge constraints / drivers
+ if parent != None:
+ con = socket2_p.constraints.new('COPY_LOCATION')
+ con.name = "copy_location"
+ con.target = self.obj
+ con.subtarget = socket1
+
+ con = socket2_p.constraints.new('COPY_TRANSFORMS')
+ con.name = "isolate_off"
+ con.target = self.obj
+ con.subtarget = socket1
+
+ # Driver
+ fcurve = con.driver_add("influence")
+ driver = fcurve.driver
+ var = driver.variables.new()
+ driver.type = 'AVERAGE'
+ var.name = "var"
+ var.targets[0].id_type = 'OBJECT'
+ var.targets[0].id = self.obj
+ var.targets[0].data_path = ulimb_p.path_from_id() + '["isolate"]'
+ mod = fcurve.modifiers[0]
+ mod.poly_order = 1
+ mod.coefficients[0] = 1.0
+ mod.coefficients[1] = -1.0
+
+ # Constrain org bones to controls
+ con = pb[self.org_bones[0]].constraints.new('COPY_TRANSFORMS')
+ con.name = "fk"
+ con.target = self.obj
+ con.subtarget = ulimb
+
+ con = pb[self.org_bones[1]].constraints.new('COPY_TRANSFORMS')
+ con.name = "fk"
+ con.target = self.obj
+ con.subtarget = flimb
+
+ con = pb[self.org_bones[2]].constraints.new('COPY_TRANSFORMS')
+ con.name = "fk"
+ con.target = self.obj
+ con.subtarget = elimb_mch
+
+ # Set layers if specified
+ if self.layers:
+ ulimb_p.bone.layers = self.layers
+ flimb_p.bone.layers = self.layers
+ elimb_p.bone.layers = self.layers
+
+ # Create control widgets
+ create_limb_widget(self.obj, ulimb)
+ create_limb_widget(self.obj, flimb)
+
+ ob = create_widget(self.obj, elimb)
+ if ob != None:
+ verts = [(0.7, 1.5, 0.0), (0.7, -0.25, 0.0), (-0.7, -0.25, 0.0), (-0.7, 1.5, 0.0), (0.7, 0.723, 0.0), (-0.7, 0.723, 0.0), (0.7, 0.0, 0.0), (-0.7, 0.0, 0.0)]
+ edges = [(1, 2), (0, 3), (0, 4), (3, 5), (4, 6), (1, 6), (5, 7), (2, 7)]
+ mesh = ob.data
+ mesh.from_pydata(verts, edges, [])
+ mesh.update()
+
+ mod = ob.modifiers.new("subsurf", 'SUBSURF')
+ mod.levels = 2
+
+ return [ulimb, flimb, elimb, elimb_mch]
+
+
+class IKLimb:
+ """ An IK limb rig, with an optional ik/fk switch.
+
+ """
+ def __init__(self, obj, bone1, bone2, bone3, pole_target_base_name, primary_rotation_axis, bend_hint, layers, ikfk_switch=False):
+ self.obj = obj
+ self.switch = ikfk_switch
+
+ # Get the chain of 3 connected bones
+ self.org_bones = [bone1, bone2, bone3]
+
+ # Get (optional) parent
+ if self.obj.data.bones[bone1].parent is None:
+ self.org_parent = None
+ else:
+ self.org_parent = self.obj.data.bones[bone1].parent.name
+
+ # Get the rig parameters
+ self.pole_target_base_name = pole_target_base_name
+ self.layers = layers
+ self.bend_hint = bend_hint
+ self.primary_rotation_axis = primary_rotation_axis
+
+ def generate(self):
+ bpy.ops.object.mode_set(mode='EDIT')
+
+ # Create non-scaling parent bone
+ if self.org_parent != None:
+ loc = Vector(self.obj.data.edit_bones[self.org_bones[0]].head)
+ parent = make_nonscaling_child(self.obj, self.org_parent, loc, "_ik")
+ else:
+ parent = None
+
+ # Create the bones
+ ulimb = copy_bone(self.obj, self.org_bones[0], make_mechanism_name(strip_org(insert_before_lr(self.org_bones[0], ".ik"))))
+ flimb = copy_bone(self.obj, self.org_bones[1], make_mechanism_name(strip_org(insert_before_lr(self.org_bones[1], ".ik"))))
+ elimb = copy_bone(self.obj, self.org_bones[2], strip_org(insert_before_lr(self.org_bones[2], ".ik")))
+ elimb_mch = copy_bone(self.obj, self.org_bones[2], make_mechanism_name(strip_org(self.org_bones[2])))
+
+ ulimb_nostr = copy_bone(self.obj, self.org_bones[0], make_mechanism_name(strip_org(insert_before_lr(self.org_bones[0], ".nostr.ik"))))
+ flimb_nostr = copy_bone(self.obj, self.org_bones[1], make_mechanism_name(strip_org(insert_before_lr(self.org_bones[1], ".nostr.ik"))))
+
+ ulimb_str = copy_bone(self.obj, self.org_bones[0], make_mechanism_name(strip_org(insert_before_lr(self.org_bones[0], ".stretch.ik"))))
+ flimb_str = copy_bone(self.obj, self.org_bones[1], make_mechanism_name(strip_org(insert_before_lr(self.org_bones[1], ".stretch.ik"))))
+
+ pole_target_name = self.pole_target_base_name + "." + insert_before_lr(self.org_bones[0], ".ik").split(".", 1)[1]
+ pole = copy_bone(self.obj, self.org_bones[0], pole_target_name)
+
+ viselimb = copy_bone(self.obj, self.org_bones[2], "VIS-" + strip_org(insert_before_lr(self.org_bones[2], ".ik")))
+ vispole = copy_bone(self.obj, self.org_bones[1], "VIS-" + strip_org(insert_before_lr(self.org_bones[0], "_pole.ik")))
+
+ # Get edit bones
+ eb = self.obj.data.edit_bones
+
+ if parent != None:
+ parent_e = eb[parent]
+ ulimb_e = eb[ulimb]
+ flimb_e = eb[flimb]
+ elimb_e = eb[elimb]
+ elimb_mch_e = eb[elimb_mch]
+ ulimb_nostr_e = eb[ulimb_nostr]
+ flimb_nostr_e = eb[flimb_nostr]
+ ulimb_str_e = eb[ulimb_str]
+ flimb_str_e = eb[flimb_str]
+ pole_e = eb[pole]
+ viselimb_e = eb[viselimb]
+ vispole_e = eb[vispole]
+
+ # Parenting
+ ulimb_e.use_connect = False
+ ulimb_nostr_e.use_connect = False
+ if parent != None:
+ ulimb_e.parent = parent_e
+ ulimb_nostr_e.parent = parent_e
+
+ flimb_e.parent = ulimb_e
+ flimb_nostr_e.parent = ulimb_nostr_e
+
+ elimb_e.use_connect = False
+ elimb_e.parent = None
+
+ elimb_mch_e.use_connect = False
+ elimb_mch_e.parent = elimb_e
+
+ ulimb_str_e.use_connect = False
+ ulimb_str_e.parent = ulimb_e.parent
+
+ flimb_str_e.use_connect = False
+ flimb_str_e.parent = ulimb_e.parent
+
+ pole_e.use_connect = False
+ if parent != None:
+ pole_e.parent = parent_e
+
+ viselimb_e.use_connect = False
+ viselimb_e.parent = None
+
+ vispole_e.use_connect = False
+ vispole_e.parent = None
+
+ # Misc
+ elimb_e.use_local_location = False
+
+ viselimb_e.hide_select = True
+ vispole_e.hide_select = True
+
+ # Positioning
+ v1 = flimb_e.tail - ulimb_e.head
+ if 'X' in self.primary_rotation_axis or 'Y' in self.primary_rotation_axis:
+ v2 = v1.cross(flimb_e.x_axis)
+ if (v2 * flimb_e.z_axis) > 0.0:
+ v2 *= -1.0
+ else:
+ v2 = v1.cross(flimb_e.z_axis)
+ if (v2 * flimb_e.x_axis) < 0.0:
+ v2 *= -1.0
+ v2.normalize()
+ v2 *= v1.length
+
+ if '-' in self.primary_rotation_axis:
+ v2 *= -1
+
+ pole_e.head = flimb_e.head + v2
+ pole_e.tail = pole_e.head + (Vector((0, 1, 0)) * (v1.length / 8))
+ pole_e.roll = 0.0
+
+ viselimb_e.tail = viselimb_e.head + Vector((0, 0, v1.length / 32))
+ vispole_e.tail = vispole_e.head + Vector((0, 0, v1.length / 32))
+
+ # Determine the pole offset value
+ plane = (flimb_e.tail - ulimb_e.head).normalized()
+ vec1 = ulimb_e.x_axis.normalized()
+ vec2 = (pole_e.head - ulimb_e.head).normalized()
+ pole_offset = angle_on_plane(plane, vec1, vec2)
+
+ # Object mode, get pose bones
+ bpy.ops.object.mode_set(mode='OBJECT')
+ pb = self.obj.pose.bones
+
+ ulimb_p = pb[ulimb]
+ flimb_p = pb[flimb]
+ elimb_p = pb[elimb]
+ ulimb_nostr_p = pb[ulimb_nostr]
+ flimb_nostr_p = pb[flimb_nostr]
+ ulimb_str_p = pb[ulimb_str]
+ flimb_str_p = pb[flimb_str]
+ pole_p = pb[pole]
+ viselimb_p = pb[viselimb]
+ vispole_p = pb[vispole]
+
+ # Set the elbow to only bend on the primary axis
+ if 'X' in self.primary_rotation_axis:
+ flimb_p.lock_ik_y = True
+ flimb_p.lock_ik_z = True
+ flimb_nostr_p.lock_ik_y = True
+ flimb_nostr_p.lock_ik_z = True
+ elif 'Y' in self.primary_rotation_axis:
+ flimb_p.lock_ik_x = True
+ flimb_p.lock_ik_z = True
+ flimb_nostr_p.lock_ik_x = True
+ flimb_nostr_p.lock_ik_z = True
+ else:
+ flimb_p.lock_ik_x = True
+ flimb_p.lock_ik_y = True
+ flimb_nostr_p.lock_ik_x = True
+ flimb_nostr_p.lock_ik_y = True
+
+ # Limb stretches
+ ulimb_nostr_p.ik_stretch = 0.0
+ flimb_nostr_p.ik_stretch = 0.0
+
+ # This next bit is weird. The values calculated cause
+ # ulimb and flimb to preserve their relative lengths
+ # while stretching.
+ l1 = ulimb_p.length
+ l2 = flimb_p.length
+ if l1 < l2:
+ ulimb_p.ik_stretch = (l1 ** (1 / 3)) / (l2 ** (1 / 3))
+ flimb_p.ik_stretch = 1.0
+ else:
+ ulimb_p.ik_stretch = 1.0
+ flimb_p.ik_stretch = (l2 ** (1 / 3)) / (l1 ** (1 / 3))
+
+ # Pole target only translates
+ pole_p.lock_location = False, False, False
+ pole_p.lock_rotation = True, True, True
+ pole_p.lock_rotation_w = True
+ pole_p.lock_scale = True, True, True
+
+ # Set up custom properties
+ if self.switch is True:
+ prop = rna_idprop_ui_prop_get(elimb_p, "ikfk_switch", create=True)
+ elimb_p["ikfk_switch"] = 0.0
+ prop["soft_min"] = prop["min"] = 0.0
+ prop["soft_max"] = prop["max"] = 1.0
+
+ prop = rna_idprop_ui_prop_get(elimb_p, "stretch_length", create=True)
+ elimb_p["stretch_length"] = 1.0
+ prop["min"] = 0.05
+ prop["max"] = 20.0
+ prop["soft_min"] = 0.25
+ prop["soft_max"] = 4.0
+
+ prop = rna_idprop_ui_prop_get(elimb_p, "auto_stretch", create=True)
+ elimb_p["auto_stretch"] = 1.0
+ prop["soft_min"] = prop["min"] = 0.0
+ prop["soft_max"] = prop["max"] = 1.0
+
+ # Stretch parameter drivers
+ def add_stretch_drivers(pose_bone):
+ driver = pose_bone.driver_add("scale", 1).driver
+ var = driver.variables.new()
+ var.name = "stretch_length"
+ var.targets[0].id_type = 'OBJECT'
+ var.targets[0].id = self.obj
+ var.targets[0].data_path = elimb_p.path_from_id() + '["stretch_length"]'
+ driver.type = 'SCRIPTED'
+ driver.expression = "stretch_length"
+
+ driver = pose_bone.driver_add("scale", 0).driver
+ var = driver.variables.new()
+ var.name = "stretch_length"
+ var.targets[0].id_type = 'OBJECT'
+ var.targets[0].id = self.obj
+ var.targets[0].data_path = elimb_p.path_from_id() + '["stretch_length"]'
+ driver.type = 'SCRIPTED'
+ driver.expression = "stretch_length"
+
+ driver = pose_bone.driver_add("scale", 2).driver
+ var = driver.variables.new()
+ var.name = "stretch_length"
+ var.targets[0].id_type = 'OBJECT'
+ var.targets[0].id = self.obj
+ var.targets[0].data_path = elimb_p.path_from_id() + '["stretch_length"]'
+ driver.type = 'SCRIPTED'
+ driver.expression = "stretch_length"
+ add_stretch_drivers(ulimb_nostr_p)
+
+ # Bend direction hint
+ def add_bend_hint(pose_bone, axis):
+ con = pose_bone.constraints.new('LIMIT_ROTATION')
+ con.name = "bend_hint"
+ con.owner_space = 'LOCAL'
+ if axis == 'X':
+ con.use_limit_x = True
+ con.min_x = pi / 10
+ con.max_x = pi / 10
+ elif axis == '-X':
+ con.use_limit_x = True
+ con.min_x = -pi / 10
+ con.max_x = -pi / 10
+ elif axis == 'Y':
+ con.use_limit_y = True
+ con.min_y = pi / 10
+ con.max_y = pi / 10
+ elif axis == '-Y':
+ con.use_limit_y = True
+ con.min_y = -pi / 10
+ con.max_y = -pi / 10
+ elif axis == 'Z':
+ con.use_limit_z = True
+ con.min_z = pi / 10
+ con.max_z = pi / 10
+ elif axis == '-Z':
+ con.use_limit_z = True
+ con.min_z = -pi / 10
+ con.max_z = -pi / 10
+ if self.bend_hint:
+ add_bend_hint(flimb_p, self.primary_rotation_axis)
+ add_bend_hint(flimb_nostr_p, self.primary_rotation_axis)
+
+ # Constrain normal IK chain to no-stretch IK chain
+ con = ulimb_p.constraints.new('COPY_TRANSFORMS')
+ con.name = "pre_stretch"
+ con.target = self.obj
+ con.subtarget = ulimb_nostr
+
+ con = flimb_p.constraints.new('COPY_TRANSFORMS')
+ con.name = "pre_stretch"
+ con.target = self.obj
+ con.subtarget = flimb_nostr
+
+ # IK Constraints
+ con = flimb_nostr_p.constraints.new('IK')
+ con.name = "ik"
+ con.target = self.obj
+ con.subtarget = elimb_mch
+ con.pole_target = self.obj
+ con.pole_subtarget = pole
+ con.pole_angle = pole_offset
+ con.chain_count = 2
+
+ con = flimb_p.constraints.new('IK')
+ con.name = "ik"
+ con.target = self.obj
+ con.subtarget = elimb_mch
+ con.chain_count = 2
+
+ # Driver to enable/disable auto stretching IK chain
+ fcurve = con.driver_add("influence")
+ driver = fcurve.driver
+ var = driver.variables.new()
+ driver.type = 'AVERAGE'
+ var.name = "var"
+ var.targets[0].id_type = 'OBJECT'
+ var.targets[0].id = self.obj
+ var.targets[0].data_path = elimb_p.path_from_id() + '["auto_stretch"]'
+
+ # Stretch bone constraints
+ con = ulimb_str_p.constraints.new('COPY_TRANSFORMS')
+ con.name = "anchor"
+ con.target = self.obj
+ con.subtarget = ulimb
+ con = ulimb_str_p.constraints.new('MAINTAIN_VOLUME')
+ con.name = "stretch"
+ con.owner_space = 'LOCAL'
+
+ con = flimb_str_p.constraints.new('COPY_TRANSFORMS')
+ con.name = "anchor"
+ con.target = self.obj
+ con.subtarget = flimb
+ con = flimb_str_p.constraints.new('MAINTAIN_VOLUME')
+ con.name = "stretch"
+ con.owner_space = 'LOCAL'
+
+ # Constrain org bones
+ con = pb[self.org_bones[0]].constraints.new('COPY_TRANSFORMS')
+ con.name = "ik"
+ con.target = self.obj
+ con.subtarget = ulimb_str
+ if self.switch is True:
+ # IK/FK switch driver
+ fcurve = con.driver_add("influence")
+ driver = fcurve.driver
+ var = driver.variables.new()
+ driver.type = 'AVERAGE'
+ var.name = "var"
+ var.targets[0].id_type = 'OBJECT'
+ var.targets[0].id = self.obj
+ var.targets[0].data_path = elimb_p.path_from_id() + '["ikfk_switch"]'
+
+ con = pb[self.org_bones[1]].constraints.new('COPY_TRANSFORMS')
+ con.name = "ik"
+ con.target = self.obj
+ con.subtarget = flimb_str
+ if self.switch is True:
+ # IK/FK switch driver
+ fcurve = con.driver_add("influence")
+ driver = fcurve.driver
+ var = driver.variables.new()
+ driver.type = 'AVERAGE'
+ var.name = "var"
+ var.targets[0].id_type = 'OBJECT'
+ var.targets[0].id = self.obj
+ var.targets[0].data_path = elimb_p.path_from_id() + '["ikfk_switch"]'
+
+ con = pb[self.org_bones[2]].constraints.new('COPY_TRANSFORMS')
+ con.name = "ik"
+ con.target = self.obj
+ con.subtarget = elimb_mch
+ if self.switch is True:
+ # IK/FK switch driver
+ fcurve = con.driver_add("influence")
+ driver = fcurve.driver
+ var = driver.variables.new()
+ driver.type = 'AVERAGE'
+ var.name = "var"
+ var.targets[0].id_type = 'OBJECT'
+ var.targets[0].id = self.obj
+ var.targets[0].data_path = elimb_p.path_from_id() + '["ikfk_switch"]'
+
+ # VIS limb-end constraints
+ con = viselimb_p.constraints.new('COPY_LOCATION')
+ con.name = "copy_loc"
+ con.target = self.obj
+ con.subtarget = self.org_bones[2]
+
+ con = viselimb_p.constraints.new('STRETCH_TO')
+ con.name = "stretch_to"
+ con.target = self.obj
+ con.subtarget = elimb
+ con.volume = 'NO_VOLUME'
+ con.rest_length = viselimb_p.length
+
+ # VIS pole constraints
+ con = vispole_p.constraints.new('COPY_LOCATION')
+ con.name = "copy_loc"
+ con.target = self.obj
+ con.subtarget = self.org_bones[1]
+
+ con = vispole_p.constraints.new('STRETCH_TO')
+ con.name = "stretch_to"
+ con.target = self.obj
+ con.subtarget = pole
+ con.volume = 'NO_VOLUME'
+ con.rest_length = vispole_p.length
+
+ # Set layers if specified
+ if self.layers:
+ elimb_p.bone.layers = self.layers
+ pole_p.bone.layers = self.layers
+ viselimb_p.bone.layers = self.layers
+ vispole_p.bone.layers = self.layers
+
+ # Create widgets
+ create_line_widget(self.obj, vispole)
+ create_line_widget(self.obj, viselimb)
+ create_sphere_widget(self.obj, pole)
+
+ ob = create_widget(self.obj, elimb)
+ if ob != None:
+ verts = [(0.7, 1.5, 0.0), (0.7, -0.25, 0.0), (-0.7, -0.25, 0.0), (-0.7, 1.5, 0.0), (0.7, 0.723, 0.0), (-0.7, 0.723, 0.0), (0.7, 0.0, 0.0), (-0.7, 0.0, 0.0)]
+ edges = [(1, 2), (0, 3), (0, 4), (3, 5), (4, 6), (1, 6), (5, 7), (2, 7)]
+ mesh = ob.data
+ mesh.from_pydata(verts, edges, [])
+ mesh.update()
+
+ mod = ob.modifiers.new("subsurf", 'SUBSURF')
+ mod.levels = 2
+
+ return [ulimb, flimb, elimb, elimb_mch, pole, vispole, viselimb]
+
+
+class RubberHoseLimb:
+ def __init__(self, obj, bone1, bone2, bone3, use_complex_limb, junc_base_name, primary_rotation_axis, layers):
+ self.obj = obj
+
+ # Get the chain of 3 connected bones
+ self.org_bones = [bone1, bone2, bone3]
+
+ # Get (optional) parent
+ if self.obj.data.bones[bone1].parent is None:
+ self.org_parent = None
+ else:
+ self.org_parent = self.obj.data.bones[bone1].parent.name
+
+ # Get rig parameters
+ self.layers = layers
+ self.primary_rotation_axis = primary_rotation_axis
+ self.use_complex_limb = use_complex_limb
+ self.junc_base_name = junc_base_name
+
+ def generate(self):
+ bpy.ops.object.mode_set(mode='EDIT')
+
+ # Create non-scaling parent bone
+ if self.org_parent != None:
+ loc = Vector(self.obj.data.edit_bones[self.org_bones[0]].head)
+ parent = make_nonscaling_child(self.obj, self.org_parent, loc, "_rh")
+ else:
+ parent = None
+
+ if not self.use_complex_limb:
+ # Simple rig
+
+ # Create bones
+ ulimb = copy_bone(self.obj, self.org_bones[0], make_deformer_name(strip_org(self.org_bones[0])))
+ flimb = copy_bone(self.obj, self.org_bones[1], make_deformer_name(strip_org(self.org_bones[1])))
+ elimb = copy_bone(self.obj, self.org_bones[2], make_deformer_name(strip_org(self.org_bones[2])))
+
+ # Get edit bones
+ eb = self.obj.data.edit_bones
+
+ ulimb_e = eb[ulimb]
+ flimb_e = eb[flimb]
+ elimb_e = eb[elimb]
+
+ # Parenting
+ elimb_e.parent = flimb_e
+ elimb_e.use_connect = True
+
+ flimb_e.parent = ulimb_e
+ flimb_e.use_connect = True
+
+ if parent != None:
+ elimb_e.use_connect = False
+ ulimb_e.parent = eb[parent]
+
+ # Object mode, get pose bones
+ bpy.ops.object.mode_set(mode='OBJECT')
+ pb = self.obj.pose.bones
+
+ ulimb_p = pb[ulimb]
+ flimb_p = pb[flimb]
+ elimb_p = pb[elimb]
+
+ # Constrain def bones to org bones
+ con = ulimb_p.constraints.new('COPY_TRANSFORMS')
+ con.name = "def"
+ con.target = self.obj
+ con.subtarget = self.org_bones[0]
+
+ con = flimb_p.constraints.new('COPY_TRANSFORMS')
+ con.name = "def"
+ con.target = self.obj
+ con.subtarget = self.org_bones[1]
+
+ con = elimb_p.constraints.new('COPY_TRANSFORMS')
+ con.name = "def"
+ con.target = self.obj
+ con.subtarget = self.org_bones[2]
+
+ return []
+ else:
+ # Complex rig
+
+ # Get the .R or .L off the end of the upper limb name if it exists
+ lr = self.org_bones[0].split(".", 1)
+ if len(lr) == 1:
+ lr = ""
+ else:
+ lr = lr[1]
+
+ # Create bones
+ ulimb1 = copy_bone(self.obj, self.org_bones[0], make_deformer_name(strip_org(insert_before_lr(self.org_bones[0], ".01"))))
+ ulimb2 = copy_bone(self.obj, self.org_bones[0], make_deformer_name(strip_org(insert_before_lr(self.org_bones[0], ".02"))))
+ flimb1 = copy_bone(self.obj, self.org_bones[1], make_deformer_name(strip_org(insert_before_lr(self.org_bones[1], ".01"))))
+ flimb2 = copy_bone(self.obj, self.org_bones[1], make_deformer_name(strip_org(insert_before_lr(self.org_bones[1], ".02"))))
+ elimb = copy_bone(self.obj, self.org_bones[2], make_deformer_name(strip_org(self.org_bones[2])))
+
+ junc = copy_bone(self.obj, self.org_bones[1], make_mechanism_name(strip_org(insert_before_lr(self.org_bones[1], ".junc"))))
+
+ uhose = new_bone(self.obj, strip_org(insert_before_lr(self.org_bones[0], "_hose")))
+ jhose = new_bone(self.obj, self.junc_base_name + "_hose." + lr)
+ fhose = new_bone(self.obj, strip_org(insert_before_lr(self.org_bones[1], "_hose")))
+
+ uhose_par = copy_bone(self.obj, self.org_bones[0], make_mechanism_name(strip_org(insert_before_lr(uhose, "_parent"))))
+ jhose_par = copy_bone(self.obj, junc, make_mechanism_name(strip_org(insert_before_lr(jhose, "_parent"))))
+ fhose_par = copy_bone(self.obj, self.org_bones[1], make_mechanism_name(strip_org(insert_before_lr(fhose, "_parent"))))
+
+ # Get edit bones
+ eb = self.obj.data.edit_bones
+
+ if parent != None:
+ parent_e = eb[parent]
+ else:
+ parent_e = None
+
+ ulimb1_e = eb[ulimb1]
+ ulimb2_e = eb[ulimb2]
+ flimb1_e = eb[flimb1]
+ flimb2_e = eb[flimb2]
+ elimb_e = eb[elimb]
+
+ junc_e = eb[junc]
+
+ uhose_e = eb[uhose]
+ jhose_e = eb[jhose]
+ fhose_e = eb[fhose]
+
+ uhose_par_e = eb[uhose_par]
+ jhose_par_e = eb[jhose_par]
+ fhose_par_e = eb[fhose_par]
+
+ # Parenting
+ if parent != None:
+ ulimb1_e.use_connect = False
+ ulimb1_e.parent = parent_e
+
+ ulimb2_e.use_connect = False
+ ulimb2_e.parent = eb[self.org_bones[0]]
+
+ flimb1_e.use_connect = True
+ flimb1_e.parent = ulimb2_e
+
+ flimb2_e.use_connect = False
+ flimb2_e.parent = eb[self.org_bones[1]]
+
+ elimb_e.use_connect = False
+ elimb_e.parent = eb[self.org_bones[2]]
+
+ junc_e.use_connect = False
+ junc_e.parent = eb[self.org_bones[0]]
+
+ uhose_e.use_connect = False
+ uhose_e.parent = uhose_par_e
+
+ jhose_e.use_connect = False
+ jhose_e.parent = jhose_par_e
+
+ fhose_e.use_connect = False
+ fhose_e.parent = fhose_par_e
+
+ uhose_par_e.use_connect = False
+ uhose_par_e.parent = parent_e
+
+ jhose_par_e.use_connect = False
+ jhose_par_e.parent = parent_e
+
+ fhose_par_e.use_connect = False
+ fhose_par_e.parent = parent_e
+
+ # Positioning
+ ulimb1_e.length *= 0.5
+ ulimb2_e.head = Vector(ulimb1_e.tail)
+ flimb1_e.length *= 0.5
+ flimb2_e.head = Vector(flimb1_e.tail)
+ align_bone_roll(self.obj, flimb2, elimb)
+
+ junc_e.length *= 0.2
+
+ uhose_par_e.length *= 0.25
+ jhose_par_e.length *= 0.15
+ fhose_par_e.length *= 0.25
+ put_bone(self.obj, uhose_par, Vector(ulimb1_e.tail))
+ put_bone(self.obj, jhose_par, Vector(ulimb2_e.tail))
+ put_bone(self.obj, fhose_par, Vector(flimb1_e.tail))
+
+ put_bone(self.obj, uhose, Vector(ulimb1_e.tail))
+ put_bone(self.obj, jhose, Vector(ulimb2_e.tail))
+ put_bone(self.obj, fhose, Vector(flimb1_e.tail))
+
+ if 'X' in self.primary_rotation_axis:
+ upoint = Vector(ulimb1_e.z_axis)
+ fpoint = Vector(flimb1_e.z_axis)
+ elif 'Z' in self.primary_rotation_axis:
+ upoint = Vector(ulimb1_e.x_axis)
+ fpoint = Vector(flimb1_e.x_axis)
+ else: # Y
+ upoint = Vector(ulimb1_e.z_axis)
+ fpoint = Vector(flimb1_e.z_axis)
+
+ if '-' not in self.primary_rotation_axis:
+ upoint *= -1
+ fpoint *= -1
+
+ if 'Y' in self.primary_rotation_axis:
+ uside = Vector(ulimb1_e.x_axis)
+ fside = Vector(flimb1_e.x_axis)
+ else:
+ uside = Vector(ulimb1_e.y_axis) * -1
+ fside = Vector(flimb1_e.y_axis) * -1
+
+ uhose_e.tail = uhose_e.head + upoint
+ jhose_e.tail = fhose_e.head + upoint + fpoint
+ fhose_e.tail = fhose_e.head + fpoint
+
+ align_bone_z_axis(self.obj, uhose, uside)
+ align_bone_z_axis(self.obj, jhose, uside + fside)
+ align_bone_z_axis(self.obj, fhose, fside)
+
+ l = 0.125 * (ulimb1_e.length + ulimb2_e.length + flimb1_e.length + flimb2_e.length)
+ uhose_e.length = l
+ jhose_e.length = l
+ fhose_e.length = l
+
+ # Object mode, get pose bones
+ bpy.ops.object.mode_set(mode='OBJECT')
+ pb = self.obj.pose.bones
+
+ ulimb1_p = pb[ulimb1]
+ ulimb2_p = pb[ulimb2]
+ flimb1_p = pb[flimb1]
+ flimb2_p = pb[flimb2]
+ elimb_p = pb[elimb]
+
+ junc_p = pb[junc]
+
+ uhose_p = pb[uhose]
+ jhose_p = pb[jhose]
+ fhose_p = pb[fhose]
+
+ uhose_par_p = pb[uhose_par]
+ jhose_par_p = pb[jhose_par]
+ fhose_par_p = pb[fhose_par]
+
+ # Lock axes
+ uhose_p.lock_rotation = (True, True, True)
+ uhose_p.lock_rotation_w = True
+ uhose_p.lock_scale = (True, True, True)
+
+ jhose_p.lock_rotation = (True, True, True)
+ jhose_p.lock_rotation_w = True
+ jhose_p.lock_scale = (True, True, True)
+
+ fhose_p.lock_rotation = (True, True, True)
+ fhose_p.lock_rotation_w = True
+ fhose_p.lock_scale = (True, True, True)
+
+ # B-bone settings
+ ulimb2_p.bone.bbone_segments = 16
+ ulimb2_p.bone.bbone_in = 0.0
+ ulimb2_p.bone.bbone_out = 1.0
+
+ flimb1_p.bone.bbone_segments = 16
+ flimb1_p.bone.bbone_in = 1.0
+ flimb1_p.bone.bbone_out = 0.0
+
+ # Custom properties
+ prop = rna_idprop_ui_prop_get(jhose_p, "smooth_bend", create=True)
+ jhose_p["smooth_bend"] = 0.0
+ prop["soft_min"] = prop["min"] = 0.0
+ prop["soft_max"] = prop["max"] = 1.0
+
+ # Drivers
+ fcurve = ulimb2_p.bone.driver_add("bbone_out")
+ driver = fcurve.driver
+ var = driver.variables.new()
+ driver.type = 'AVERAGE'
+ var.name = "var"
+ var.targets[0].id_type = 'OBJECT'
+ var.targets[0].id = self.obj
+ var.targets[0].data_path = jhose_p.path_from_id() + '["smooth_bend"]'
+
+ fcurve = flimb1_p.bone.driver_add("bbone_in")
+ driver = fcurve.driver
+ var = driver.variables.new()
+ driver.type = 'AVERAGE'
+ var.name = "var"
+ var.targets[0].id_type = 'OBJECT'
+ var.targets[0].id = self.obj
+ var.targets[0].data_path = jhose_p.path_from_id() + '["smooth_bend"]'
+
+ # Constraints
+ con = ulimb1_p.constraints.new('COPY_SCALE')
+ con.name = "anchor"
+ con.target = self.obj
+ con.subtarget = self.org_bones[0]
+ con = ulimb1_p.constraints.new('DAMPED_TRACK')
+ con.name = "track"
+ con.target = self.obj
+ con.subtarget = uhose
+ con = ulimb1_p.constraints.new('STRETCH_TO')
+ con.name = "track"
+ con.target = self.obj
+ con.subtarget = uhose
+ con.volume = 'NO_VOLUME'
+
+ con = ulimb2_p.constraints.new('COPY_LOCATION')
+ con.name = "anchor"
+ con.target = self.obj
+ con.subtarget = uhose
+ con = ulimb2_p.constraints.new('DAMPED_TRACK')
+ con.name = "track"
+ con.target = self.obj
+ con.subtarget = jhose
+ con = ulimb2_p.constraints.new('STRETCH_TO')
+ con.name = "track"
+ con.target = self.obj
+ con.subtarget = jhose
+ con.volume = 'NO_VOLUME'
+
+ con = flimb1_p.constraints.new('COPY_TRANSFORMS')
+ con.name = "anchor"
+ con.target = self.obj
+ con.subtarget = self.org_bones[1]
+ con = flimb1_p.constraints.new('COPY_LOCATION')
+ con.name = "anchor"
+ con.target = self.obj
+ con.subtarget = jhose
+ con = flimb1_p.constraints.new('DAMPED_TRACK')
+ con.name = "track"
+ con.target = self.obj
+ con.subtarget = fhose
+ con = flimb1_p.constraints.new('STRETCH_TO')
+ con.name = "track"
+ con.target = self.obj
+ con.subtarget = fhose
+ con.volume = 'NO_VOLUME'
+
+ con = flimb2_p.constraints.new('COPY_LOCATION')
+ con.name = "anchor"
+ con.target = self.obj
+ con.subtarget = fhose
+ con = flimb2_p.constraints.new('COPY_ROTATION')
+ con.name = "twist"
+ con.target = self.obj
+ con.subtarget = elimb
+ con = flimb2_p.constraints.new('DAMPED_TRACK')
+ con.name = "track"
+ con.target = self.obj
+ con.subtarget = self.org_bones[2]
+ con = flimb2_p.constraints.new('STRETCH_TO')
+ con.name = "track"
+ con.target = self.obj
+ con.subtarget = self.org_bones[2]
+ con.volume = 'NO_VOLUME'
+
+ con = junc_p.constraints.new('COPY_TRANSFORMS')
+ con.name = "bend"
+ con.target = self.obj
+ con.subtarget = self.org_bones[1]
+ con.influence = 0.5
+
+ con = uhose_par_p.constraints.new('COPY_ROTATION')
+ con.name = "follow"
+ con.target = self.obj
+ con.subtarget = self.org_bones[0]
+ con.influence = 1.0
+ con = uhose_par_p.constraints.new('COPY_LOCATION')
+ con.name = "anchor"
+ con.target = self.obj
+ con.subtarget = self.org_bones[0]
+ con.influence = 1.0
+ con = uhose_par_p.constraints.new('COPY_LOCATION')
+ con.name = "anchor"
+ con.target = self.obj
+ con.subtarget = jhose
+ con.influence = 0.5
+
+ con = jhose_par_p.constraints.new('COPY_ROTATION')
+ con.name = "follow"
+ con.target = self.obj
+ con.subtarget = junc
+ con.influence = 1.0
+ con = jhose_par_p.constraints.new('COPY_LOCATION')
+ con.name = "anchor"
+ con.target = self.obj
+ con.subtarget = junc
+ con.influence = 1.0
+
+ con = fhose_par_p.constraints.new('COPY_ROTATION')
+ con.name = "follow"
+ con.target = self.obj
+ con.subtarget = self.org_bones[1]
+ con.influence = 1.0
+ con = fhose_par_p.constraints.new('COPY_LOCATION')
+ con.name = "anchor"
+ con.target = self.obj
+ con.subtarget = jhose
+ con.influence = 1.0
+ con = fhose_par_p.constraints.new('COPY_LOCATION')
+ con.name = "anchor"
+ con.target = self.obj
+ con.subtarget = self.org_bones[2]
+ con.influence = 0.5
+
+ # Layers
+ if self.layers:
+ uhose_p.bone.layers = self.layers
+ jhose_p.bone.layers = self.layers
+ fhose_p.bone.layers = self.layers
+ else:
+ layers = list(pb[self.org_bones[0]].bone.layers)
+ uhose_p.bone.layers = layers
+ jhose_p.bone.layers = layers
+ fhose_p.bone.layers = layers
+
+ # Create widgets
+ create_sphere_widget(self.obj, uhose)
+ create_sphere_widget(self.obj, jhose)
+ create_sphere_widget(self.obj, fhose)
+
+ return [uhose, jhose, fhose]
diff --git a/rigify/rigs/misc/delta.py b/rigify/rigs/misc/delta.py
index a005fb61..84f3612b 100644
--- a/rigify/rigs/misc/delta.py
+++ b/rigify/rigs/misc/delta.py
@@ -90,7 +90,6 @@ if False:
con.target = self.obj
con.subtarget = delta
-
def create_sample(obj):
# generated by rigify.utils.write_metarig
bpy.ops.object.mode_set(mode='EDIT')
@@ -140,7 +139,6 @@ if False:
bone.select_tail = True
arm.edit_bones.active = bone
-
def set_mat(obj, bone_name, matrix):
""" Sets the bone to have the given transform matrix.
"""
diff --git a/rigify/rigs/neck_short.py b/rigify/rigs/neck_short.py
index 9c24832a..63174d40 100644
--- a/rigify/rigs/neck_short.py
+++ b/rigify/rigs/neck_short.py
@@ -25,7 +25,7 @@ from ..utils import MetarigError
from ..utils import copy_bone, new_bone, put_bone
from ..utils import connected_children_names
from ..utils import strip_org, make_mechanism_name, make_deformer_name
-from ..utils import obj_to_bone, create_circle_widget
+from ..utils import create_circle_widget
script1 = """
@@ -315,8 +315,8 @@ class Rig:
i += 1
# Create control widgets
- w1 = create_circle_widget(self.obj, neck_ctrl, radius=1.0, head_tail=0.5, bone_transform_name=self.org_bones[(len(self.org_bones) - 1) // 2])
- w2 = create_circle_widget(self.obj, head_ctrl, radius=1.0, head_tail=0.5, bone_transform_name=self.org_bones[-1])
+ create_circle_widget(self.obj, neck_ctrl, radius=1.0, head_tail=0.5, bone_transform_name=self.org_bones[(len(self.org_bones) - 1) // 2])
+ create_circle_widget(self.obj, head_ctrl, radius=1.0, head_tail=0.5, bone_transform_name=self.org_bones[-1])
# Return control bones
return (head_ctrl, neck_ctrl)
diff --git a/rigify/rigs/spine.py b/rigify/rigs/spine.py
index 0983e83e..680a4ceb 100644
--- a/rigify/rigs/spine.py
+++ b/rigify/rigs/spine.py
@@ -33,7 +33,7 @@ from ..utils import MetarigError
from ..utils import copy_bone, new_bone, flip_bone, put_bone
from ..utils import connected_children_names
from ..utils import strip_org, make_mechanism_name, make_deformer_name
-from ..utils import obj_to_bone, create_circle_widget, create_cube_widget
+from ..utils import create_circle_widget, create_cube_widget
script = """
main = "%s"
@@ -174,7 +174,6 @@ class Rig:
eb[main_control].length = sum([eb[b].length for b in self.org_bones]) / 2
# Position the controls and sub-controls
- pos = eb[controls[0]].head.copy()
for name, subname in zip(controls, subcontrols):
put_bone(self.obj, name, pivot_rest_pos)
put_bone(self.obj, subname, pivot_rest_pos)
@@ -443,23 +442,23 @@ class Rig:
# Control appearance
# Main
- w = create_cube_widget(self.obj, main_control)
+ create_cube_widget(self.obj, main_control)
# Spines
for name, i in zip(controls[1:-1], self.control_indices[1:-1]):
pb[name].custom_shape_transform = pb[self.org_bones[i]]
# Create control widgets
- w = create_circle_widget(self.obj, name, radius=1.0, head_tail=0.5, with_line=True, bone_transform_name=self.org_bones[i])
+ create_circle_widget(self.obj, name, radius=1.0, head_tail=0.5, with_line=True, bone_transform_name=self.org_bones[i])
# Hips
pb[controls[0]].custom_shape_transform = pb[self.org_bones[0]]
# Create control widgets
- w = create_circle_widget(self.obj, controls[0], radius=1.0, head_tail=0.5, with_line=True, bone_transform_name=self.org_bones[0])
+ create_circle_widget(self.obj, controls[0], radius=1.0, head_tail=0.5, with_line=True, bone_transform_name=self.org_bones[0])
# Ribs
pb[controls[-1]].custom_shape_transform = pb[self.org_bones[-1]]
# Create control widgets
- w = create_circle_widget(self.obj, controls[-1], radius=1.0, head_tail=0.5, with_line=True, bone_transform_name=self.org_bones[-1])
+ create_circle_widget(self.obj, controls[-1], radius=1.0, head_tail=0.5, with_line=True, bone_transform_name=self.org_bones[-1])
# Layers
pb[main_control].bone.layers = pb[self.org_bones[0]].bone.layers
diff --git a/rigify/utils.py b/rigify/utils.py
index ec4c9e76..0b27bc21 100644
--- a/rigify/utils.py
+++ b/rigify/utils.py
@@ -21,9 +21,10 @@
import bpy
import imp
import importlib
+import math
import random
import time
-from mathutils import Vector
+from mathutils import Vector, Matrix
from rna_prop_ui import rna_idprop_ui_prop_get
RIG_DIR = "rigs" # Name of the directory where rig types are kept
@@ -227,7 +228,7 @@ def put_bone(obj, bone_name, pos):
""" Places a bone at the given position.
"""
if bone_name not in obj.data.bones:
- raise MetarigError("put_bone(): bone '%s' not found, cannot copy it" % bone_name)
+ raise MetarigError("put_bone(): bone '%s' not found, cannot move it" % bone_name)
if obj == bpy.context.active_object and bpy.context.mode == 'EDIT_ARMATURE':
bone = obj.data.edit_bones[bone_name]
@@ -238,6 +239,68 @@ def put_bone(obj, bone_name, pos):
raise MetarigError("Cannot 'put' bones outside of edit mode")
+def make_nonscaling_child(obj, bone_name, location, child_name_postfix=""):
+ """ Takes the named bone and creates a non-scaling child of it at
+ the given location. The returned bone (returned by name) is not
+ a true child, but behaves like one sans inheriting scaling.
+
+ It is intended as an intermediate construction to prevent rig types
+ from scaling with their parents. The named bone is assumed to be
+ an ORG bone.
+ """
+ if bone_name not in obj.data.bones:
+ raise MetarigError("make_nonscaling_child(): bone '%s' not found, cannot copy it" % bone_name)
+
+ if obj == bpy.context.active_object and bpy.context.mode == 'EDIT_ARMATURE':
+ # Create desired names for bones
+ name1 = make_mechanism_name(strip_org(insert_before_lr(bone_name, child_name_postfix + "_ns_ch")))
+ name2 = make_mechanism_name(strip_org(insert_before_lr(bone_name, child_name_postfix + "_ns_intr")))
+
+ # Create bones
+ child = copy_bone(obj, bone_name, name1)
+ intermediate_parent = copy_bone(obj, bone_name, name2)
+
+ # Get edit bones
+ eb = obj.data.edit_bones
+ child_e = eb[child]
+ intrpar_e = eb[intermediate_parent]
+
+ # Parenting
+ child_e.use_connect = False
+ child_e.parent = None
+
+ intrpar_e.use_connect = False
+ intrpar_e.parent = eb[bone_name]
+
+ # Positioning
+ child_e.length *= 0.5
+ intrpar_e.length *= 0.25
+
+ put_bone(obj, child, location)
+ put_bone(obj, intermediate_parent, location)
+
+ # Object mode
+ bpy.ops.object.mode_set(mode='OBJECT')
+ pb = obj.pose.bones
+
+ # Add constraints
+ con = pb[child].constraints.new('COPY_LOCATION')
+ con.name = "parent_loc"
+ con.target = obj
+ con.subtarget = intermediate_parent
+
+ con = pb[child].constraints.new('COPY_ROTATION')
+ con.name = "parent_loc"
+ con.target = obj
+ con.subtarget = intermediate_parent
+
+ bpy.ops.object.mode_set(mode='EDIT')
+
+ return child
+ else:
+ raise MetarigError("Cannot make nonscaling child outside of edit mode")
+
+
#=============================================
# Widget creation
#=============================================
@@ -406,6 +469,132 @@ def create_root_widget(rig, bone_name, bone_transform_name=None):
#=============================================
+# Math
+#=============================================
+
+def angle_on_plane(plane, vec1, vec2):
+ """ Return the angle between two vectors projected onto a plane.
+ """
+ plane.normalize()
+ vec1 = vec1 - (plane * (vec1.dot(plane)))
+ vec2 = vec2 - (plane * (vec2.dot(plane)))
+ vec1.normalize()
+ vec2.normalize()
+
+ # Determine the angle
+ angle = math.acos(max(-1.0, min(1.0, vec1.dot(vec2))))
+
+ if angle < 0.00001: # close enough to zero that sign doesn't matter
+ return angle
+
+ # Determine the sign of the angle
+ vec3 = vec2.cross(vec1)
+ vec3.normalize()
+ sign = vec3.dot(plane)
+ if sign >= 0:
+ sign = 1
+ else:
+ sign = -1
+
+ return angle * sign
+
+
+def align_bone_roll(obj, bone1, bone2):
+ """ Aligns the roll of two bones.
+ """
+ bone1_e = obj.data.edit_bones[bone1]
+ bone2_e = obj.data.edit_bones[bone2]
+
+ bone1_e.roll = 0.0
+
+ # Get the directions the bones are pointing in, as vectors
+ y1 = bone1_e.y_axis
+ x1 = bone1_e.x_axis
+ y2 = bone2_e.y_axis
+ x2 = bone2_e.x_axis
+
+ # Get the shortest axis to rotate bone1 on to point in the same direction as bone2
+ axis = y1.cross(y2)
+ axis.normalize()
+
+ # Angle to rotate on that shortest axis
+ angle = y1.angle(y2)
+
+ # Create rotation matrix to make bone1 point in the same direction as bone2
+ rot_mat = Matrix.Rotation(angle, 3, axis)
+
+ # Roll factor
+ x3 = rot_mat * x1
+ dot = x2 * x3
+ if dot > 1.0:
+ dot = 1.0
+ elif dot < -1.0:
+ dot = -1.0
+ roll = math.acos(dot)
+
+ # Set the roll
+ bone1_e.roll = roll
+
+ # Check if we rolled in the right direction
+ x3 = rot_mat * bone1_e.x_axis
+ check = x2 * x3
+
+ # If not, reverse
+ if check < 0.9999:
+ bone1_e.roll = -roll
+
+
+def align_bone_x_axis(obj, bone, vec):
+ """ Rolls the bone to align its x-axis as closely as possible to
+ the given vector.
+ Must be in edit mode.
+ """
+ bone_e = obj.data.edit_bones[bone]
+
+ vec = vec.cross(bone_e.y_axis)
+ vec.normalize()
+
+ dot = max(-1.0, min(1.0, bone_e.z_axis.dot(vec)))
+ angle = math.acos(dot)
+
+ bone_e.roll += angle
+
+ dot1 = bone_e.z_axis.dot(vec)
+
+ bone_e.roll -= angle * 2
+
+ dot2 = bone_e.z_axis.dot(vec)
+
+ if dot1 > dot2:
+ bone_e.roll += angle * 2
+
+
+def align_bone_z_axis(obj, bone, vec):
+ """ Rolls the bone to align its z-axis as closely as possible to
+ the given vector.
+ Must be in edit mode.
+ """
+ bone_e = obj.data.edit_bones[bone]
+
+ vec = bone_e.y_axis.cross(vec)
+ vec.normalize()
+
+ dot = max(-1.0, min(1.0, bone_e.x_axis.dot(vec)))
+ angle = math.acos(dot)
+
+ bone_e.roll += angle
+
+ dot1 = bone_e.x_axis.dot(vec)
+
+ bone_e.roll -= angle * 2
+
+ dot2 = bone_e.x_axis.dot(vec)
+
+ if dot1 > dot2:
+ bone_e.roll += angle * 2
+
+
+#=============================================
# Misc
#=============================================
@@ -555,7 +744,6 @@ def write_metarig(obj, layers=False, func_name="create"):
# Rig type and other pose properties
for bone_name in bones:
pbone = obj.pose.bones[bone_name]
- pbone_written = False
code.append(" pbone = obj.pose.bones[bones[%r]]" % bone_name)
code.append(" pbone.rigify_type = %r" % pbone.rigify_type)