Skip to content
Snippets Groups Projects
Commit 83840e57 authored by Nathan Vegdahl's avatar Nathan Vegdahl
Browse files

Rigify: added bend hinting to the arm and leg rigs.

This allows IK arms and legs to still function properly even when they
start with an entirely straight chain.
parent 6f3d2c49
No related branches found
No related tags found
No related merge requests found
...@@ -6,6 +6,7 @@ Original prototyping and development, and Python API support: ...@@ -6,6 +6,7 @@ Original prototyping and development, and Python API support:
General financial support: General financial support:
- Benjamin Tolputt - Benjamin Tolputt
- Nesterenko Viktoriya - Nesterenko Viktoriya
- Jeff Hogan
IK/FK snapping financial support: IK/FK snapping financial support:
- Benjamin Tolputt - Benjamin Tolputt
......
...@@ -11,14 +11,14 @@ armature that contains data about how to construct the rig. In particular, it ...@@ -11,14 +11,14 @@ armature that contains data about how to construct the rig. In particular, it
contains bones in the basic configuration of the rig, with some bones tagged contains bones in the basic configuration of the rig, with some bones tagged
to indicate the rig type. to indicate the rig type.
For example, a metaig might contain a chain of three bones, the root-most of For example, a metarig might contain a chain of three bones, the root-most of
which is tagged as being a biped arm. When given as input to Rigify, Rigify which is tagged as being a biped arm. When given as input to Rigify, Rigify
will then generate a fully-featured biped arm rig in the same position and will then generate a fully-featured biped arm rig in the same position and
proportions as the 3-bone chain. proportions as the 3-bone chain.
One could also have another chain of bones, the root-most of which is tagged as One could also have another chain of bones, the root-most of which is tagged as
being a spine. And the root-most bone of the arm chain could be the child of being a spine. And the root-most bone of the arm chain could be the child of
any of those spine bones. Then the rig that Rigify would generate would be a any of those spine bones. Then the rig that Rigify generates would be a
spine rig with an arm rig attached to it. spine rig with an arm rig attached to it.
...@@ -45,7 +45,7 @@ information about it. ...@@ -45,7 +45,7 @@ information about it.
It traverses the bones in a root-most to leaf-most order, and whenever it It traverses the bones in a root-most to leaf-most order, and whenever it
stumbles upon a bone that has a rig type tagged on it, it creates a rig-type stumbles upon a bone that has a rig type tagged on it, it creates a rig-type
python object (rig types will be explained further down) for that rig type, python object (rig types will be explained further down) for that rig type,
and executes the resulting object's information gathering code. and executes the resulting python object's information gathering code.
At the end of the information gathering stage, Rigify has a collection of At the end of the information gathering stage, Rigify has a collection of
python objects, each of which know all the information they need to generate python objects, each of which know all the information they need to generate
...@@ -77,33 +77,33 @@ __init__() is the "information gathering" code for the rig type. When Rigify ...@@ -77,33 +77,33 @@ __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 loops through the bones and finds a tagged bone, it will create a python
object from the Rig class, executing this method. object from the Rig class, executing this method.
In addition to the default "self" parameter, __init__() needs to take the In addition to the default "self" parameter, __init__() needs to take the
armature object, the name of the bone that was tagged, and the bone's rig type armature object, the name of the bone that was tagged, and a parameters object.
parameters object.
A proper rig-type __init__() will look like this: A proper rig-type __init__() will look like this:
def __init__(self, obj, bone, params): def __init__(self, obj, bone_name, params):
# code goes here # code goes here
At the bare minimum, you are going to want to store the object and bone name 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, params): def __init__(self, obj, bone_name, params):
self.obj = obj self.obj = obj
self.org_bone = bone self.org_bone = bone_name
Most rig types involve more than just that one bone, though, so you will also Most rig types involve more than just that one bone, though, so you will also
want to store the names of any other relevant bones. For example, maybe the want to store the names of any other relevant bones. For example, maybe the
parent of the tagged bone: parent of the tagged bone is important to the rig type:
def __init__(self, obj, bone, params): def __init__(self, obj, bone_name, params):
self.obj = obj self.obj = obj
self.org_bone = bone self.org_bone = bone_name
self.org_parent = obj.data.bones[bone].parent.name self.org_parent = obj.data.bones[bone_name].parent.name
It is important that store the _names_ of the bones, and not direct references. It is important that you store the _names_ of the bones, and not direct
Due to how the armature system in Blender works, when flipping in and out of references. Due to the inner workings of Blender's armature system, direct
edit mode, pose-bone and edit-bone references get lost. (Arg...) edit-bone and pose-bone references are lost when flipping in and out of
armature edit mode. (Arg...)
Remember that it is critical that the information-gathering method does _not_ Remember that it is critical that the information-gathering method does _not_
modify the armature in any way. This way all of the rig type's info-gathering modify the armature in any way. This way all of the rig type's info-gathering
...@@ -118,13 +118,13 @@ actually generate the rig. It takes the form: ...@@ -118,13 +118,13 @@ actually generate the rig. It takes the form:
def generate(self): def generate(self):
# code goes here # code goes here
It doesn't take any parameters beyond "self". So you really need to store any It doesn't take any parameters beyond "self". So you have to store any
information you need with the __init__() method. information you need with the __init__() method.
Generate pretty much has free reign to do whatever it wants, with the exception generate() pretty much has free reign to do whatever it wants, with the exception
of two simple rules: of two simple rules:
1. Other than the "ORG-" bones, do not touch anything that is not created by 1. Other than the "ORG-" bones, do not touch anything that is not created by
this method (this prevents rig types from messing each other up). the rig type (this prevents rig types from messing each other up).
2. Even with "ORG-" bones, the only thing you are allowed to do is add children 2. Even with "ORG-" bones, the only thing you are allowed to do is add children
and add constraints. Do not rename them, do not remove children or and add constraints. Do not rename them, do not remove children or
constraints, and especially do not change their parents. (Adding constraints constraints, and especially do not change their parents. (Adding constraints
......
...@@ -96,6 +96,8 @@ class Rig: ...@@ -96,6 +96,8 @@ class Rig:
items = [('X', 'X', ''), ('Y', 'Y', ''), ('Z', 'Z', ''), ('-X', '-X', ''), ('-Y', '-Y', ''), ('-Z', '-Z', '')] items = [('X', 'X', ''), ('Y', 'Y', ''), ('Z', 'Z', ''), ('-X', '-X', ''), ('-Y', '-Y', ''), ('-Z', '-Z', '')]
group.primary_rotation_axis = bpy.props.EnumProperty(items=items, name="Primary Rotation Axis", default='X') group.primary_rotation_axis = bpy.props.EnumProperty(items=items, name="Primary Rotation Axis", default='X')
group.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.")
group.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.") group.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.")
group.ik_layers = bpy.props.BoolVectorProperty(size=32, description="Layers for the ik controls to be on.") group.ik_layers = bpy.props.BoolVectorProperty(size=32, description="Layers for the ik controls to be on.")
...@@ -159,6 +161,9 @@ class Rig: ...@@ -159,6 +161,9 @@ class Rig:
r.label(text="Elbow rotation axis:") r.label(text="Elbow rotation axis:")
r.prop(params, "primary_rotation_axis", text="") r.prop(params, "primary_rotation_axis", text="")
r = layout.row()
r.prop(params, "bend_hint")
col = layout.column() col = layout.column()
col.prop(params, "use_upper_arm_twist") col.prop(params, "use_upper_arm_twist")
col.prop(params, "use_forearm_twist") col.prop(params, "use_forearm_twist")
......
...@@ -84,6 +84,8 @@ class Rig: ...@@ -84,6 +84,8 @@ class Rig:
else: else:
self.layers = None self.layers = None
self.bend_hint = params.bend_hint
self.primary_rotation_axis = params.primary_rotation_axis self.primary_rotation_axis = params.primary_rotation_axis
def generate(self): def generate(self):
...@@ -198,6 +200,36 @@ class Rig: ...@@ -198,6 +200,36 @@ class Rig:
prop["soft_min"] = prop["min"] = 0.0 prop["soft_min"] = prop["min"] = 0.0
prop["soft_max"] = prop["max"] = 1.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 # IK Constraint
con = farm_p.constraints.new('IK') con = farm_p.constraints.new('IK')
con.name = "ik" con.name = "ik"
......
...@@ -98,6 +98,8 @@ class Rig: ...@@ -98,6 +98,8 @@ class Rig:
items = [('X', 'X', ''), ('Y', 'Y', ''), ('Z', 'Z', ''), ('-X', '-X', ''), ('-Y', '-Y', ''), ('-Z', '-Z', '')] items = [('X', 'X', ''), ('Y', 'Y', ''), ('Z', 'Z', ''), ('-X', '-X', ''), ('-Y', '-Y', ''), ('-Z', '-Z', '')]
group.primary_rotation_axis = bpy.props.EnumProperty(items=items, name="Primary Rotation Axis", default='X') group.primary_rotation_axis = bpy.props.EnumProperty(items=items, name="Primary Rotation Axis", default='X')
group.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.")
group.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.") group.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.")
group.ik_layers = bpy.props.BoolVectorProperty(size=32, description="Layers for the ik controls to be on.") group.ik_layers = bpy.props.BoolVectorProperty(size=32, description="Layers for the ik controls to be on.")
...@@ -161,6 +163,9 @@ class Rig: ...@@ -161,6 +163,9 @@ class Rig:
r.label(text="Knee rotation axis:") r.label(text="Knee rotation axis:")
r.prop(params, "primary_rotation_axis", text="") r.prop(params, "primary_rotation_axis", text="")
r = layout.row()
r.prop(params, "bend_hint")
col = layout.column() col = layout.column()
col.prop(params, "use_thigh_twist") col.prop(params, "use_thigh_twist")
col.prop(params, "use_shin_twist") col.prop(params, "use_shin_twist")
......
...@@ -134,6 +134,8 @@ class Rig: ...@@ -134,6 +134,8 @@ class Rig:
else: else:
self.layers = None self.layers = None
self.bend_hint = params.bend_hint
self.primary_rotation_axis = params.primary_rotation_axis self.primary_rotation_axis = params.primary_rotation_axis
def generate(self): def generate(self):
...@@ -376,6 +378,36 @@ class Rig: ...@@ -376,6 +378,36 @@ class Rig:
prop["soft_min"] = prop["min"] = 0.0 prop["soft_min"] = prop["min"] = 0.0
prop["soft_max"] = prop["max"] = 1.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 # IK Constraint
con = shin_p.constraints.new('IK') con = shin_p.constraints.new('IK')
con.name = "ik" con.name = "ik"
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment