From 1f54b62d6bf07fab4262fe6a02810caa15b9f17e Mon Sep 17 00:00:00 2001 From: Nathan Letwory Date: Tue, 21 Jun 2011 17:02:44 +0000 Subject: Tag 2.58 release --- rigify/CREDITS | 17 + rigify/README | 252 ++++++++ rigify/__init__.py | 166 ++++++ rigify/generate.py | 362 ++++++++++++ rigify/metarig_menu.py | 58 ++ rigify/metarigs/__init__.py | 0 rigify/metarigs/human.py | 1149 +++++++++++++++++++++++++++++++++++++ rigify/rig_ui_template.py | 570 ++++++++++++++++++ rigify/rigs/__init__.py | 0 rigify/rigs/basic/__init__.py | 0 rigify/rigs/basic/copy.py | 142 +++++ rigify/rigs/basic/copy_chain.py | 210 +++++++ rigify/rigs/biped/__init__.py | 0 rigify/rigs/biped/arm/__init__.py | 235 ++++++++ rigify/rigs/biped/arm/deform.py | 230 ++++++++ rigify/rigs/biped/arm/fk.py | 217 +++++++ rigify/rigs/biped/arm/ik.py | 339 +++++++++++ rigify/rigs/biped/leg/__init__.py | 272 +++++++++ rigify/rigs/biped/leg/deform.py | 263 +++++++++ rigify/rigs/biped/leg/fk.py | 255 ++++++++ rigify/rigs/biped/leg/ik.py | 608 ++++++++++++++++++++ rigify/rigs/finger.py | 412 +++++++++++++ rigify/rigs/misc/__init__.py | 0 rigify/rigs/misc/delta.py | 161 ++++++ rigify/rigs/neck_short.py | 392 +++++++++++++ rigify/rigs/palm.py | 273 +++++++++ rigify/rigs/spine.py | 617 ++++++++++++++++++++ rigify/ui.py | 300 ++++++++++ rigify/utils.py | 527 +++++++++++++++++ 29 files changed, 8027 insertions(+) create mode 100644 rigify/CREDITS create mode 100644 rigify/README create mode 100644 rigify/__init__.py create mode 100644 rigify/generate.py create mode 100644 rigify/metarig_menu.py create mode 100644 rigify/metarigs/__init__.py create mode 100644 rigify/metarigs/human.py create mode 100644 rigify/rig_ui_template.py create mode 100644 rigify/rigs/__init__.py create mode 100644 rigify/rigs/basic/__init__.py create mode 100644 rigify/rigs/basic/copy.py create mode 100644 rigify/rigs/basic/copy_chain.py create mode 100644 rigify/rigs/biped/__init__.py create mode 100644 rigify/rigs/biped/arm/__init__.py create mode 100644 rigify/rigs/biped/arm/deform.py create mode 100644 rigify/rigs/biped/arm/fk.py create mode 100644 rigify/rigs/biped/arm/ik.py create mode 100644 rigify/rigs/biped/leg/__init__.py create mode 100644 rigify/rigs/biped/leg/deform.py create mode 100644 rigify/rigs/biped/leg/fk.py create mode 100644 rigify/rigs/biped/leg/ik.py create mode 100644 rigify/rigs/finger.py create mode 100644 rigify/rigs/misc/__init__.py create mode 100644 rigify/rigs/misc/delta.py create mode 100644 rigify/rigs/neck_short.py create mode 100644 rigify/rigs/palm.py create mode 100644 rigify/rigs/spine.py create mode 100644 rigify/ui.py create mode 100644 rigify/utils.py (limited to 'rigify') diff --git a/rigify/CREDITS b/rigify/CREDITS new file mode 100644 index 00000000..dd517fb1 --- /dev/null +++ b/rigify/CREDITS @@ -0,0 +1,17 @@ +A big thank you to all the people listed here for supporting Rigify. + +Original prototyping and development, and Python API support: +- Campbell Barton + +General financial support: +- Benjamin Tolputt +- Nesterenko Viktoriya +- Jeff Hogan + +IK/FK snapping financial support: +- Benjamin Tolputt +- Nesterenko Viktoriya +- Leslie Chih +- Isaac Ah-Loe +- Casey "TheLorax" Jones + diff --git a/rigify/README b/rigify/README new file mode 100644 index 00000000..790942f9 --- /dev/null +++ b/rigify/README @@ -0,0 +1,252 @@ +INTRODUCTION +------------ +Rigify is an auto-rigging system based on a "building blocks" paradigm. The +user can create a rig by putting together any combination of rig types, in any +configuration that they want. + +A rig type is something like "biped arm" or "spine" or "finger". + +The input to the Rigify system is something called a "metarig". It is an +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 +to indicate the rig type. + +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 +will then generate a fully-featured biped arm rig in the same position and +proportions as the 3-bone chain. + +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 +any of those spine bones. Then the rig that Rigify generates would be a +spine rig with an arm rig attached to it. + + +THE GUTS OF RIGIFY, SUMMARIZED +------------------------------ +The concept behind rigify is fairly simple. It recieves an armature as input +with some of the bones tagged as being certain rig types (arm, leg, etc.) + +When Rigify recieves that armature as input, the first thing it does is +duplicate the armature. From here on out, the original armature is totally +ignored. Only the duplicate is used. And this duplicate armature object will +become the generated rig. + +Rigify next prepends "ORG-" to all of the bones. These are the "original" +bones of the metarig, and they are used as the glue between rig types, as I +will explain later. + +Rigify then generates the rig in two passes. The first pass is the +"information gathering" stage. + +The information gathering stage doesn't modify the armature at all. It simply +gathers information about it. Or, rather, it lets the rig types gather +information about 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 +python object (rig types will be explained further down) for that rig type, +and executes the resulting python object's information gathering code. + +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 +their own bit of the rig. + +The next stage is the rig generation stage. This part is pretty simple. All +Rigify does is it loops over all of the rig-type python objects that it created +in the previous stage (also in root-most to leaf-most order), and executes +their rig-generate code. All of the actual rig generation happens in the +rig-type python objects. + +And that's pretty much it. As you can see, most of the important code is +actually in the rig types themselves, not in Rigify. Rigify is pretty sparse +when it comes right down to it. + +There is one final stage to rig generation. Rigify checks all of the bones +for "DEF-", "MCH-", and "ORG-" prefixes, and moves those bones to their own +layers. It also sets all of the "DEF-" bones to deform, and sets all other +bones to _not_ deform. And finally, it looks for any bone that does not have +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() + +__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 +object from the Rig class, executing this method. +In addition to the default "self" parameter, __init__() needs to take the +armature object, the name of the bone that was tagged, and a parameters object. + +A proper rig-type __init__() will look like this: + + def __init__(self, obj, bone_name, params): + # 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: + + def __init__(self, obj, bone_name, params): + self.obj = obj + self.org_bone = bone_name + +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 +parent of the tagged bone is important to the rig type: + + def __init__(self, obj, bone_name, params): + self.obj = obj + self.org_bone = bone_name + self.org_parent = obj.data.bones[bone_name].parent.name + +It is important that you store the _names_ of the bones, and not direct +references. Due to the inner workings of Blender's armature system, direct +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_ +modify the armature in any way. This way all of the rig type's info-gathering +methods can execute on a clean armature. Many rig types depend on traversing +parent-child relationships to figure out what bones are relevant to them, for +example. + + +Next is the generate() method. This is the method that Rigify calls to +actually generate the rig. It takes the form: + + def generate(self): + # code goes here + +It doesn't take any parameters beyond "self". So you have to store any +information you need with the __init__() method. + +generate() pretty much has free reign to do whatever it wants, with the exception +of two simple rules: +1. Other than the "ORG-" bones, do not touch anything that is not created by +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 +and add constraints. Do not rename them, do not remove children or +constraints, and especially do not change their parents. (Adding constraints +and adding children are encouraged, though. ;-)) This is because the "ORG-" +bones are the glue that holds everything together, and changing them beyond +adding children/constraints ruins the glue, so to speak. + +In short: with the exception of adding children/constraints to "ORG-" +bones, only mess with things that you yourself create. + +It is also generally a good idea (though not strictly required) that the rig +type add constraints to the "ORG-" bones it was generated from so that the +"ORG-" bones move with the animation controls. +For example, if I make a simple arm rig type, the controls that the animator +uses should also move the "ORG-" bones. That way, any other rig-types that are +children of those "ORG-" bones will move along with them. For example, any +fingers on the end of the arm. + +Also, any bones that the animator should not directly animate with should have +their names prefixed with "DEF-" or "MCH-". The former if it is a bone that +is intended to deform the mesh, the latter if it is not. +It should be obvious, then, that a bone cannot be both an animation control and +a deforming bone in Rigify. This is on purpose. + +Also note that there are convenience functions in utils.py for prepending +"DEF-" and "MCH-" to bone names: deformer() and mch() +There is also a convenience function for stripping "ORG-" from a bone name: +strip_org() +Which is useful for removing "ORG-" from bones you create by duplicating +the "ORG-" bones. +I recommend you use these functions instead of manually adding/stripping +these prefixes. That way if the prefixes are changed, it can be changed in +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 +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 +what they are for: + + +RIG PARAMETERS +-------------- +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: +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.") + +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: + + @classmethod + def parameters_ui(self, layout, obj, bone): + params = obj.pose.bones[bone].rigify_parameters[0] + r = layout.row() + r.prop(params, "toggle_param") + + +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. + +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): + bpy.ops.object.mode_set(mode='EDIT') + arm = obj.data + + bone = arm.edit_bones.new('Bone') + bone.head[:] = 0.0000, 0.0000, 0.0000 + bone.tail[:] = 0.0000, 0.0000, 1.0000 + bone.roll = 0.0000 + bone.use_connect = False + + bpy.ops.object.mode_set(mode='OBJECT') + pbone = obj.pose.bones[bone] + pbone.rigify_type = 'copy' + 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() + + +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 +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: + +return ["my python code"] + +Otherwise it won't work. + diff --git a/rigify/__init__.py b/rigify/__init__.py new file mode 100644 index 00000000..1de834aa --- /dev/null +++ b/rigify/__init__.py @@ -0,0 +1,166 @@ +#====================== 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 ======================== + +bl_info = { + "name": "Rigify", + "author": "Nathan Vegdahl", + "blender": (2, 5, 7), + "api": 35622, + "location": "View3D > Add > Armature", + "description": "Adds various Rig Templates", + "location": "Armature properties", + "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", + "category": "Rigging"} + + +if "bpy" in locals(): + import imp + imp.reload(generate) + imp.reload(ui) + imp.reload(utils) + imp.reload(metarig_menu) +else: + from . import generate, ui, utils, metarig_menu + +import bpy +import bpy_types +import os + + +def get_rig_list(path): + """ Recursively searches for rig types, and returns a list. + """ + rigs = [] + MODULE_DIR = os.path.dirname(__file__) + RIG_DIR_ABS = os.path.join(MODULE_DIR, utils.RIG_DIR) + SEARCH_DIR_ABS = os.path.join(RIG_DIR_ABS, path) + files = os.listdir(SEARCH_DIR_ABS) + files.sort() + + for f in files: + is_dir = os.path.isdir(os.path.join(SEARCH_DIR_ABS, f)) # Whether the file is a directory + if f[0] in (".", "_"): + pass + elif f.count(".") >= 2 or (is_dir and "." in f): + print("Warning: %r, filename contains a '.', skipping" % os.path.join(SEARCH_DIR_ABS, f)) + else: + if is_dir: + # Check directories + module_name = os.path.join(path, f).replace(os.sep, ".") + try: + rig = utils.get_rig_type(module_name) + except ImportError as e: + print("Rigify: " + str(e)) + else: + # Check if it's a rig itself + if not hasattr(rig, "Rig"): + # Check for sub-rigs + ls = get_rig_list(os.path.join(path, f, "")) # "" adds a final slash + rigs.extend(["%s.%s" % (f, l) for l in ls]) + else: + rigs += [f] + + elif f.endswith(".py"): + # Check straight-up python files + t = f[:-3] + module_name = os.path.join(path, t).replace(os.sep, ".") + try: + utils.get_rig_type(module_name).Rig + except (ImportError, AttributeError): + pass + else: + rigs += [t] + rigs.sort() + return rigs + + +rig_list = get_rig_list("") + + +collection_list = [] +for r in rig_list: + a = r.split(".") + if len(a) >= 2 and a[0] not in collection_list: + collection_list += [a[0]] + + +col_enum_list = [("All", "All", ""), ("None", "None", "")] +for c in collection_list: + col_enum_list += [(c, c, "")] + + +class RigifyName(bpy.types.PropertyGroup): + name = bpy.props.StringProperty() + + +class RigifyParameters(bpy.types.PropertyGroup): + name = bpy.props.StringProperty() + + +class RigifyArmatureLayer(bpy.types.PropertyGroup): + name = bpy.props.StringProperty(name="Layer Name", default=" ") + row = bpy.props.IntProperty(name="Layer Row", default=1, min=1, max=32) + + +##### REGISTER ##### + +def register(): + ui.register() + metarig_menu.register() + + bpy.utils.register_class(RigifyName) + bpy.utils.register_class(RigifyParameters) + bpy.utils.register_class(RigifyArmatureLayer) + + bpy.types.PoseBone.rigify_type = bpy.props.StringProperty(name="Rigify Type", description="Rig type for this bone.") + bpy.types.PoseBone.rigify_parameters = bpy.props.CollectionProperty(type=RigifyParameters) + + bpy.types.Armature.rigify_layers = bpy.props.CollectionProperty(type=RigifyArmatureLayer) + + IDStore = bpy.types.WindowManager + IDStore.rigify_collection = bpy.props.EnumProperty(items=col_enum_list, default="All", name="Rigify Active Collection", description="The selected rig collection") + IDStore.rigify_types = bpy.props.CollectionProperty(type=RigifyName) + IDStore.rigify_active_type = bpy.props.IntProperty(name="Rigify Active Type", description="The selected rig type.") + + # Add rig parameters + for rig in rig_list: + r = utils.get_rig_type(rig).Rig + try: + r.add_parameters(RigifyParameters) + except AttributeError: + pass + + +def unregister(): + del bpy.types.PoseBone.rigify_type + del bpy.types.PoseBone.rigify_parameters + + IDStore = bpy.types.WindowManager + del IDStore.rigify_collection + del IDStore.rigify_types + del IDStore.rigify_active_type + + bpy.utils.unregister_class(RigifyName) + bpy.utils.unregister_class(RigifyParameters) + bpy.utils.unregister_class(RigifyArmatureLayer) + + metarig_menu.unregister() + ui.unregister() diff --git a/rigify/generate.py b/rigify/generate.py new file mode 100644 index 00000000..96a6d68d --- /dev/null +++ b/rigify/generate.py @@ -0,0 +1,362 @@ +#====================== 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 ======================== + +import bpy +import time +import traceback +import sys +from rna_prop_ui import rna_idprop_ui_prop_get +from rigify.utils import MetarigError, new_bone, get_rig_type +from rigify.utils import ORG_PREFIX, MCH_PREFIX, DEF_PREFIX, WGT_PREFIX, ROOT_NAME, make_original_name +from rigify.utils import RIG_DIR +from rigify.utils import create_root_widget +from rigify.utils import random_id +from rigify.rig_ui_template import UI_SLIDERS, layers_ui, UI_REGISTER +from rigify import rigs + +RIG_MODULE = "rigs" +ORG_LAYER = [n == 31 for n in range(0, 32)] # Armature layer that original bones should be moved to. +MCH_LAYER = [n == 30 for n in range(0, 32)] # Armature layer that mechanism bones should be moved to. +DEF_LAYER = [n == 29 for n in range(0, 32)] # Armature layer that deformation bones should be moved to. +ROOT_LAYER = [n == 28 for n in range(0, 32)] # Armature layer that root bone should be moved to. + + +class Timer: + def __init__(self): + self.timez = time.time() + + def tick(self, string): + t = time.time() + print(string + "%.3f" % (t - self.timez)) + self.timez = t + + +# TODO: generalize to take a group as input instead of an armature. +def generate_rig(context, metarig): + """ Generates a rig from a metarig. + + """ + t = Timer() + + # Random string with time appended so that + # different rigs don't collide id's + rig_id = random_id(16) + + # Initial configuration + mode_orig = context.mode + rest_backup = metarig.data.pose_position + metarig.data.pose_position = 'REST' + + bpy.ops.object.mode_set(mode='OBJECT') + + scene = context.scene + + #------------------------------------------ + # Create/find the rig object and set it up + + # Check if the generated rig already exists, so we can + # regenerate in the same object. If not, create a new + # object to generate the rig in. + print("Fetch rig.") + try: + name = metarig["rig_object_name"] + except KeyError: + name = "rig" + + try: + obj = scene.objects[name] + except KeyError: + obj = bpy.data.objects.new(name, bpy.data.armatures.new(name)) + obj.draw_type = 'WIRE' + scene.objects.link(obj) + + obj.data.pose_position = 'POSE' + + # Get rid of anim data in case the rig already existed + print("Clear rig animation data.") + obj.animation_data_clear() + + # Select generated rig object + metarig.select = False + obj.select = True + scene.objects.active = obj + + # Remove all bones from the generated rig armature. + bpy.ops.object.mode_set(mode='EDIT') + for bone in obj.data.edit_bones: + obj.data.edit_bones.remove(bone) + bpy.ops.object.mode_set(mode='OBJECT') + + # Create temporary duplicates for merging + temp_rig_1 = metarig.copy() + temp_rig_1.data = metarig.data.copy() + scene.objects.link(temp_rig_1) + + temp_rig_2 = metarig.copy() + temp_rig_2.data = obj.data + scene.objects.link(temp_rig_2) + + # Select the temp rigs for merging + for objt in scene.objects: + objt.select = False # deselect all objects + temp_rig_1.select = True + temp_rig_2.select = True + scene.objects.active = temp_rig_2 + + # Merge the temporary rigs + bpy.ops.object.join() + + # Delete the second temp rig + bpy.ops.object.delete() + + # Select the generated rig + for objt in scene.objects: + objt.select = False # deselect all objects + obj.select = True + scene.objects.active = obj + + # Copy over the pose_bone properties + for bone in metarig.pose.bones: + bone_gen = obj.pose.bones[bone.name] + + # Rotation mode and transform locks + bone_gen.rotation_mode = bone.rotation_mode + bone_gen.lock_rotation = tuple(bone.lock_rotation) + bone_gen.lock_rotation_w = bone.lock_rotation_w + bone_gen.lock_rotations_4d = bone.lock_rotations_4d + bone_gen.lock_location = tuple(bone.lock_location) + bone_gen.lock_scale = tuple(bone.lock_scale) + + # Custom properties + for prop in bone.keys(): + bone_gen[prop] = bone[prop] + + # Copy over bone properties + for bone in metarig.data.bones: + bone_gen = obj.data.bones[bone.name] + + # B-bone stuff + bone_gen.bbone_segments = bone.bbone_segments + bone_gen.bbone_in = bone.bbone_in + bone_gen.bbone_out = bone.bbone_out + + t.tick("Duplicate rig: ") + #---------------------------------- + # Make a list of the original bones so we can keep track of them. + original_bones = [bone.name for bone in obj.data.bones] + + # Add the ORG_PREFIX to the original bones. + bpy.ops.object.mode_set(mode='OBJECT') + for i in range(0, len(original_bones)): + obj.data.bones[original_bones[i]].name = make_original_name(original_bones[i]) + original_bones[i] = make_original_name(original_bones[i]) + + # Create a sorted list of the original bones, sorted in the order we're + # going to traverse them for rigging. + # (root-most -> leaf-most, alphabetical) + bones_sorted = [] + for name in original_bones: + bones_sorted += [name] + bones_sorted.sort() # first sort by names + bones_sorted.sort(key=lambda bone: len(obj.pose.bones[bone].parent_recursive)) # then parents before children + + t.tick("Make list of org bones: ") + #---------------------------------- + # Create the root bone. + bpy.ops.object.mode_set(mode='EDIT') + root_bone = new_bone(obj, ROOT_NAME) + obj.data.edit_bones[root_bone].head = (0, 0, 0) + obj.data.edit_bones[root_bone].tail = (0, 1, 0) + obj.data.edit_bones[root_bone].roll = 0 + bpy.ops.object.mode_set(mode='OBJECT') + obj.data.bones[root_bone].layers = ROOT_LAYER + # Put the rig_name in the armature custom properties + rna_idprop_ui_prop_get(obj.data, "rig_id", create=True) + obj.data["rig_id"] = rig_id + + t.tick("Create root bone: ") + #---------------------------------- + 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) + t.tick("Initialize rigs: ") + + # Generate all the rigs. + ui_scripts = [] + for rig in rigs: + # Go into editmode in the rig armature + bpy.ops.object.mode_set(mode='OBJECT') + context.scene.objects.active = obj + obj.select = True + bpy.ops.object.mode_set(mode='EDIT') + scripts = rig.generate() + if scripts != None: + ui_scripts += [scripts[0]] + t.tick("Generate rigs: ") + except Exception as e: + # Cleanup if something goes wrong + print("Rigify: failed to generate rig.") + metarig.data.pose_position = rest_backup + obj.data.pose_position = 'POSE' + bpy.ops.object.mode_set(mode='OBJECT') + + # Continue the exception + raise e + + #---------------------------------- + bpy.ops.object.mode_set(mode='OBJECT') + + # Get a list of all the bones in the armature + bones = [bone.name for bone in obj.data.bones] + + # Parent any free-floating bones to the root. + bpy.ops.object.mode_set(mode='EDIT') + for bone in bones: + if obj.data.edit_bones[bone].parent is None: + obj.data.edit_bones[bone].use_connect = False + obj.data.edit_bones[bone].parent = obj.data.edit_bones[root_bone] + bpy.ops.object.mode_set(mode='OBJECT') + + # Every bone that has a name starting with "DEF-" make deforming. All the + # others make non-deforming. + for bone in bones: + if obj.data.bones[bone].name.startswith(DEF_PREFIX): + obj.data.bones[bone].use_deform = True + else: + obj.data.bones[bone].use_deform = False + + # Move all the original bones to their layer. + for bone in original_bones: + obj.data.bones[bone].layers = ORG_LAYER + + # Move all the bones with names starting with "MCH-" to their layer. + for bone in bones: + if obj.data.bones[bone].name.startswith(MCH_PREFIX): + obj.data.bones[bone].layers = MCH_LAYER + + # Move all the bones with names starting with "DEF-" to their layer. + for bone in bones: + if obj.data.bones[bone].name.startswith(DEF_PREFIX): + obj.data.bones[bone].layers = DEF_LAYER + + # Create root bone widget + create_root_widget(obj, "root") + + # Assign shapes to bones + # Object's with name WGT- get used as that bone's shape. + for bone in bones: + wgt_name = (WGT_PREFIX + obj.data.bones[bone].name)[:21] # Object names are limited to 21 characters... arg + if wgt_name in context.scene.objects: + # Weird temp thing because it won't let me index by object name + for ob in context.scene.objects: + if ob.name == wgt_name: + obj.pose.bones[bone].custom_shape = ob + break + # This is what it should do: + # obj.pose.bones[bone].custom_shape = context.scene.objects[wgt_name] + # Reveal all the layers with control bones on them + vis_layers = [False for n in range(0, 32)] + for bone in bones: + for i in range(0, 32): + vis_layers[i] = vis_layers[i] or obj.data.bones[bone].layers[i] + for i in range(0, 32): + vis_layers[i] = vis_layers[i] and not (ORG_LAYER[i] or MCH_LAYER[i] or DEF_LAYER[i]) + obj.data.layers = vis_layers + + # Ensure the collection of layer names exists + for i in range(1 + len(metarig.data.rigify_layers), 29): + layer = metarig.data.rigify_layers.add() + + # Create list of layer name/row pairs + layer_layout = [] + for l in metarig.data.rigify_layers: + layer_layout += [(l.name, l.row)] + + # Generate the UI script + if "rig_ui.py" in bpy.data.texts: + script = bpy.data.texts["rig_ui.py"] + script.clear() + else: + script = bpy.data.texts.new("rig_ui.py") + script.write(UI_SLIDERS % rig_id) + for s in ui_scripts: + script.write("\n " + s.replace("\n", "\n ") + "\n") + script.write(layers_ui(vis_layers, layer_layout)) + script.write(UI_REGISTER) + script.use_module = True + + # Run UI script + exec(script.as_string(), {}) + + t.tick("The rest: ") + #---------------------------------- + # Deconfigure + bpy.ops.object.mode_set(mode='OBJECT') + metarig.data.pose_position = rest_backup + obj.data.pose_position = 'POSE' + + +def get_bone_rigs(obj, bone_name, halt_on_missing=False): + """ Fetch all the rigs specified on a bone. + """ + rigs = [] + rig_type = obj.pose.bones[bone_name].rigify_type + rig_type = rig_type.replace(" ", "") + + if rig_type == "": + pass + else: + # Gather parameters + try: + params = obj.pose.bones[bone_name].rigify_parameters[0] + except (KeyError, IndexError): + params = None + + # Get the rig + try: + rig = get_rig_type(rig_type).Rig(obj, bone_name, params) + except ImportError: + message = "Rig Type Missing: python module for type '%s' not found (bone: %s)" % (t, bone_name) + if halt_on_missing: + raise MetarigError(message) + else: + print(message) + print('print_exc():') + traceback.print_exc(file=sys.stdout) + else: + rigs += [rig] + return rigs + + +def param_matches_type(param_name, rig_type): + """ Returns True if the parameter name is consistent with the rig type. + """ + if param_name.rsplit(".", 1)[0] == rig_type: + return True + else: + return False + + +def param_name(param_name, rig_type): + """ Get the actual parameter name, sans-rig-type. + """ + return param_name[len(rig_type) + 1:] diff --git a/rigify/metarig_menu.py b/rigify/metarig_menu.py new file mode 100644 index 00000000..56b82370 --- /dev/null +++ b/rigify/metarig_menu.py @@ -0,0 +1,58 @@ +# ##### 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 ##### + +import bpy +import mathutils +from rigify.metarigs import human +from math import cos, sin, pi + + +class AddHuman(bpy.types.Operator): + '''Add an advanced human metarig base''' + bl_idname = "object.armature_human_advanced_add" + bl_label = "Add Humanoid (advanced metarig)" + bl_options = {'REGISTER', 'UNDO'} + + def execute(self, context): + bpy.ops.object.armature_add() + obj = context.active_object + mode_orig = obj.mode + bpy.ops.object.mode_set(mode='EDIT') # grr, remove bone + bones = context.active_object.data.edit_bones + bones.remove(bones[0]) + human.create(obj) + bpy.ops.object.mode_set(mode=mode_orig) + return {'FINISHED'} + + +# Add to a menu +menu_func = (lambda self, context: self.layout.operator(AddHuman.bl_idname, + icon='OUTLINER_OB_ARMATURE', text="Human (Meta-Rig)")) + + +def register(): + bpy.utils.register_class(AddHuman) + + bpy.types.INFO_MT_armature_add.append(menu_func) + + +def unregister(): + bpy.utils.unregister_class(AddHuman) + + bpy.types.INFO_MT_armature_add.remove(menu_func) + diff --git a/rigify/metarigs/__init__.py b/rigify/metarigs/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/rigify/metarigs/human.py b/rigify/metarigs/human.py new file mode 100644 index 00000000..cfc9f038 --- /dev/null +++ b/rigify/metarigs/human.py @@ -0,0 +1,1149 @@ +# ##### 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 ##### + +import bpy + + +def create(obj): + # generated by rigify.utils.write_metarig + bpy.ops.object.mode_set(mode='EDIT') + arm = obj.data + + 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[4].name = "Fingers" + arm.rigify_layers[4].row = 3 + arm.rigify_layers[5].name = "(Tweak)" + arm.rigify_layers[5].row = 3 + arm.rigify_layers[6].name = "Arm.L (FK)" + 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 + + bones = {} + + bone = arm.edit_bones.new('hips') + bone.head[:] = 0.0000, 0.0552, 1.0099 + bone.tail[:] = 0.0000, 0.0172, 1.1837 + bone.roll = 0.0000 + bone.use_connect = False + bones['hips'] = bone.name + bone = arm.edit_bones.new('spine') + bone.head[:] = 0.0000, 0.0172, 1.1837 + bone.tail[:] = 0.0000, 0.0004, 1.3418 + bone.roll = 0.0000 + bone.use_connect = True + bone.parent = arm.edit_bones[bones['hips']] + bones['spine'] = bone.name + bone = arm.edit_bones.new('thigh.L') + bone.head[:] = 0.0980, 0.0124, 1.0720 + bone.tail[:] = 0.0980, -0.0286, 0.5372 + bone.roll = 0.0000 + bone.use_connect = False + bone.parent = arm.edit_bones[bones['hips']] + bones['thigh.L'] = bone.name + bone = arm.edit_bones.new('thigh.R') + bone.head[:] = -0.0980, 0.0124, 1.0720 + bone.tail[:] = -0.0980, -0.0286, 0.5372 + bone.roll = 0.0000 + bone.use_connect = False + bone.parent = arm.edit_bones[bones['hips']] + bones['thigh.R'] = bone.name + bone = arm.edit_bones.new('ribs') + bone.head[:] = 0.0000, 0.0004, 1.3418 + bone.tail[:] = 0.0000, 0.0114, 1.6582 + bone.roll = 0.0000 + bone.use_connect = True + bone.parent = arm.edit_bones[bones['spine']] + bones['ribs'] = bone.name + bone = arm.edit_bones.new('shin.L') + bone.head[:] = 0.0980, -0.0286, 0.5372 + bone.tail[:] = 0.0980, 0.0162, 0.0852 + bone.roll = 0.0000 + bone.use_connect = True + bone.parent = arm.edit_bones[bones['thigh.L']] + bones['shin.L'] = bone.name + bone = arm.edit_bones.new('shin.R') + bone.head[:] = -0.0980, -0.0286, 0.5372 + bone.tail[:] = -0.0980, 0.0162, 0.0852 + bone.roll = 0.0000 + bone.use_connect = True + bone.parent = arm.edit_bones[bones['thigh.R']] + bones['shin.R'] = bone.name + bone = arm.edit_bones.new('neck') + bone.head[:] = 0.0000, 0.0114, 1.6582 + bone.tail[:] = 0.0000, -0.0247, 1.7813 + bone.roll = 0.0000 + bone.use_connect = False + bone.parent = arm.edit_bones[bones['ribs']] + bones['neck'] = bone.name + bone = arm.edit_bones.new('shoulder.L') + bone.head[:] = 0.0183, -0.0684, 1.6051 + bone.tail[:] = 0.1694, 0.0205, 1.6050 + bone.roll = 0.0004 + bone.use_connect = False + bone.parent = arm.edit_bones[bones['ribs']] + bones['shoulder.L'] = bone.name + bone = arm.edit_bones.new('shoulder.R') + bone.head[:] = -0.0183, -0.0684, 1.6051 + bone.tail[:] = -0.1694, 0.0205, 1.6050 + bone.roll = -0.0004 + bone.use_connect = False + bone.parent = arm.edit_bones[bones['ribs']] + bones['shoulder.R'] = bone.name + bone = arm.edit_bones.new('foot.L') + bone.head[:] = 0.0980, 0.0162, 0.0852 + bone.tail[:] = 0.0980, -0.0934, 0.0167 + bone.roll = 0.0000 + bone.use_connect = True + bone.parent = arm.edit_bones[bones['shin.L']] + bones['foot.L'] = bone.name + bone = arm.edit_bones.new('heel.L') + bone.head[:] = 0.0980, 0.0162, 0.0852 + bone.tail[:] = 0.0980, 0.0882, -0.0000 + bone.roll = -3.1416 + 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 + bone.roll = -0.0000 + bone.use_connect = True + bone.parent = arm.edit_bones[bones['shin.R']] + bones['foot.R'] = bone.name + bone = arm.edit_bones.new('heel.R') + bone.head[:] = -0.0980, 0.0162, 0.0852 + bone.tail[:] = -0.0980, 0.0882, -0.0000 + bone.roll = 3.1416 + 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 + bone.roll = 0.0000 + bone.use_connect = True + bone.parent = arm.edit_bones[bones['neck']] + bones['head'] = bone.name + bone = arm.edit_bones.new('upper_arm.L') + bone.head[:] = 0.1953, 0.0267, 1.5846 + bone.tail[:] = 0.4424, 0.0885, 1.4491 + bone.roll = 2.0691 + bone.use_connect = False + bone.parent = arm.edit_bones[bones['shoulder.L']] + bones['upper_arm.L'] = bone.name + bone = arm.edit_bones.new('upper_arm.R') + bone.head[:] = -0.1953, 0.0267, 1.5846 + bone.tail[:] = -0.4424, 0.0885, 1.4491 + bone.roll = -2.0691 + bone.use_connect = False + bone.parent = arm.edit_bones[bones['shoulder.R']] + bones['upper_arm.R'] = bone.name + bone = arm.edit_bones.new('toe.L') + bone.head[:] = 0.0980, -0.0934, 0.0167 + bone.tail[:] = 0.0980, -0.1606, 0.0167 + bone.roll = -0.0000 + bone.use_connect = True + bone.parent = arm.edit_bones[bones['foot.L']] + bones['toe.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 + bone.roll = 0.0000 + bone.use_connect = True + bone.parent = arm.edit_bones[bones['foot.R']] + bones['toe.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 + bone.roll = 2.1459 + bone.use_connect = True + bone.parent = arm.edit_bones[bones['upper_arm.L']] + bones['forearm.L'] = bone.name + bone = arm.edit_bones.new('forearm.R') + bone.head[:] = -0.4424, 0.0885, 1.4491 + bone.tail[:] = -0.6594, 0.0492, 1.3061 + bone.roll = -2.1459 + bone.use_connect = True + bone.parent = arm.edit_bones[bones['upper_arm.R']] + bones['forearm.R'] = bone.name + bone = arm.edit_bones.new('hand.L') + bone.head[:] = 0.6594, 0.0492, 1.3061 + bone.tail[:] = 0.7234, 0.0412, 1.2585 + bone.roll = -2.4946 + bone.use_connect = True + bone.parent = arm.edit_bones[bones['forearm.L']] + bones['hand.L'] = bone.name + bone = arm.edit_bones.new('hand.R') + bone.head[:] = -0.6594, 0.0492, 1.3061 + bone.tail[:] = -0.7234, 0.0412, 1.2585 + bone.roll = 2.4946 + bone.use_connect = True + bone.parent = arm.edit_bones[bones['forearm.R']] + bones['hand.R'] = bone.name + bone = arm.edit_bones.new('palm.01.L') + bone.head[:] = 0.6921, 0.0224, 1.2882 + bone.tail[:] = 0.7464, 0.0051, 1.2482 + bone.roll = -2.4928 + bone.use_connect = False + bone.parent = arm.edit_bones[bones['hand.L']] + bones['palm.01.L'] = bone.name + bone = arm.edit_bones.new('palm.02.L') + bone.head[:] = 0.6970, 0.0389, 1.2877 + bone.tail[:] = 0.7518, 0.0277, 1.2487 + bone.roll = -2.5274 + bone.use_connect = False + bone.parent = arm.edit_bones[bones['hand.L']] + bones['palm.02.L'] = bone.name + bone = arm.edit_bones.new('palm.03.L') + bone.head[:] = 0.6963, 0.0545, 1.2874 + bone.tail[:] = 0.7540, 0.0521, 1.2482 + bone.roll = -2.5843 + bone.use_connect = False + bone.parent = arm.edit_bones[bones['hand.L']] + bones['palm.03.L'] = bone.name + bone = arm.edit_bones.new('palm.04.L') + bone.head[:] = 0.6929, 0.0696, 1.2871 + bone.tail[:] = 0.7528, 0.0763, 1.2428 + bone.roll = -2.5155 + bone.use_connect = False + bone.parent = arm.edit_bones[bones['hand.L']] + bones['palm.04.L'] = bone.name + bone = arm.edit_bones.new('palm.01.R') + bone.head[:] = -0.6921, 0.0224, 1.2882 + bone.tail[:] = -0.7464, 0.0051, 1.2482 + bone.roll = 2.4928 + bone.use_connect = False + bone.parent = arm.edit_bones[bones['hand.R']] + bones['palm.01.R'] = bone.name + bone = arm.edit_bones.new('palm.02.R') + bone.head[:] = -0.6970, 0.0389, 1.2877 + bone.tail[:] = -0.7518, 0.0277, 1.2487 + bone.roll = 2.5274 + bone.use_connect = False + bone.parent = arm.edit_bones[bones['hand.R']] + bones['palm.02.R'] = bone.name + bone = arm.edit_bones.new('palm.03.R') + bone.head[:] = -0.6963, 0.0544, 1.2874 + bone.tail[:] = -0.7540, 0.0521, 1.2482 + bone.roll = 2.5843 + bone.use_connect = False + bone.parent = arm.edit_bones[bones['hand.R']] + bones['palm.03.R'] = bone.name + bone = arm.edit_bones.new('palm.04.R') + bone.head[:] = -0.6929, 0.0696, 1.2871 + bone.tail[:] = -0.7528, 0.0763, 1.2428 + bone.roll = 2.5155 + bone.use_connect = False + bone.parent = arm.edit_bones[bones['hand.R']] + bones['palm.04.R'] = bone.name + bone = arm.edit_bones.new('finger_index.01.L') + bone.head[:] = 0.7464, 0.0051, 1.2482 + bone.tail[:] = 0.7718, 0.0013, 1.2112 + bone.roll = -2.0315 + bone.use_connect = True + bone.parent = arm.edit_bones[bones['palm.01.L']] + bones['finger_index.01.L'] = bone.name + bone = arm.edit_bones.new('thumb.01.L') + bone.head[:] = 0.6705, 0.0214, 1.2738 + bone.tail[:] = 0.6857, 0.0015, 1.2404 + bone.roll = -0.1587 + bone.use_connect = False + bone.parent = arm.edit_bones[bones['palm.01.L']] + bones['thumb.01.L'] = bone.name + bone = arm.edit_bones.new('finger_middle.01.L') + bone.head[:] = 0.7518, 0.0277, 1.2487 + bone.tail[:] = 0.7762, 0.0234, 1.2058 + bone.roll = -2.0067 + bone.use_connect = True + bone.parent = arm.edit_bones[bones['palm.02.L']] + bones['finger_middle.01.L'] = bone.name + bone = arm.edit_bones.new('finger_ring.01.L') + bone.head[:] = 0.7540, 0.0521, 1.2482 + bone.tail[:] = 0.7715, 0.0499, 1.2070 + bone.roll = -2.0082 + bone.use_connect = True + bone.parent = arm.edit_bones[bones['palm.03.L']] + bones['finger_ring.01.L'] = bone.name + bone = arm.edit_bones.new('finger_pinky.01.L') + bone.head[:] = 0.7528, 0.0763, 1.2428 + bone.tail[:] = 0.7589, 0.0765, 1.2156 + bone.roll = -1.9749 + bone.use_connect = True + bone.parent = arm.edit_bones[bones['palm.04.L']] + bones['finger_pinky.01.L'] = bone.name + bone = arm.edit_bones.new('finger_index.01.R') + bone.head[:] = -0.7464, 0.0051, 1.2482 + bone.tail[:] = -0.7718, 0.0012, 1.2112 + bone.roll = 2.0315 + bone.use_connect = True + bone.parent = arm.edit_bones[bones['palm.01.R']] + bones['finger_index.01.R'] = bone.name + bone = arm.edit_bones.new('thumb.01.R') + bone.head[:] = -0.6705, 0.0214, 1.2738 + bone.tail[:] = -0.6857, 0.0015, 1.2404 + bone.roll = 0.1587 + bone.use_connect = False + bone.parent = arm.edit_bones[bones['palm.01.R']] + bones['thumb.01.R'] = bone.name + bone = arm.edit_bones.new('finger_middle.01.R') + bone.head[:] = -0.7518, 0.0277, 1.2487 + bone.tail[:] = -0.7762, 0.0233, 1.2058 + bone.roll = 2.0067 + bone.use_connect = True + bone.parent = arm.edit_bones[bones['palm.02.R']] + bones['finger_middle.01.R'] = bone.name + bone = arm.edit_bones.new('finger_ring.01.R') + bone.head[:] = -0.7540, 0.0521, 1.2482 + bone.tail[:] = -0.7715, 0.0499, 1.2070 + bone.roll = 2.0082 + bone.use_connect = True + bone.parent = arm.edit_bones[bones['palm.03.R']] + bones['finger_ring.01.R'] = bone.name + bone = arm.edit_bones.new('finger_pinky.01.R') + bone.head[:] = -0.7528, 0.0763, 1.2428 + bone.tail[:] = -0.7589, 0.0765, 1.2156 + bone.roll = 1.9749 + bone.use_connect = True + bone.parent = arm.edit_bones[bones['palm.04.R']] + bones['finger_pinky.01.R'] = bone.name + bone = arm.edit_bones.new('finger_index.02.L') + bone.head[:] = 0.7718, 0.0013, 1.2112 + bone.tail[:] = 0.7840, -0.0003, 1.1858 + bone.roll = -1.8799 + bone.use_connect = True + bone.parent = arm.edit_bones[bones['finger_index.01.L']] + bones['finger_index.02.L'] = bone.name + bone = arm.edit_bones.new('thumb.02.L') + bone.head[:] = 0.6857, 0.0015, 1.2404 + bone.tail[:] = 0.7056, -0.0057, 1.2145 + bone.roll = -0.4798 + bone.use_connect = True + bone.parent = arm.edit_bones[bones['thumb.01.L']] + bones['thumb.02.L'] = bone.name + bone = arm.edit_bones.new('finger_middle.02.L') + bone.head[:] = 0.7762, 0.0234, 1.2058 + bone.tail[:] = 0.7851, 0.0218, 1.1749 + bone.roll = -1.8283 + bone.use_connect = True + bone.parent = arm.edit_bones[bones['finger_middle.01.L']] + bones['finger_middle.02.L'] = bone.name + bone = arm.edit_bones.new('finger_ring.02.L') + bone.head[:] = 0.7715, 0.0499, 1.2070 + bone.tail[:] = 0.7794, 0.0494, 1.1762 + bone.roll = -1.8946 + bone.use_connect = True + bone.parent = arm.edit_bones[bones['finger_ring.01.L']] + bones['finger_ring.02.L'] = bone.name + bone = arm.edit_bones.new('finger_pinky.02.L') + bone.head[:] = 0.7589, 0.0765, 1.2156 + bone.tail[:] = 0.7618, 0.0770, 1.1932 + bone.roll = -1.9059 + bone.use_connect = True + bone.parent = arm.edit_bones[bones['finger_pinky.01.L']] + bones['finger_pinky.02.L'] = bone.name + bone = arm.edit_bones.new('finger_index.02.R') + bone.head[:] = -0.7718, 0.0012, 1.2112 + bone.tail[:] = -0.7840, -0.0003, 1.1858 + bone.roll = 1.8799 + bone.use_connect = True + bone.parent = arm.edit_bones[bones['finger_index.01.R']] + bones['finger_index.02.R'] = bone.name + bone = arm.edit_bones.new('thumb.02.R') + bone.head[:] = -0.6857, 0.0015, 1.2404 + bone.tail[:] = -0.7056, -0.0057, 1.2145 + bone.roll = 0.4798 + bone.use_connect = True + bone.parent = arm.edit_bones[bones['thumb.01.R']] + bones['thumb.02.R'] = bone.name + bone = arm.edit_bones.new('finger_middle.02.R') + bone.head[:] = -0.7762, 0.0233, 1.2058 + bone.tail[:] = -0.7851, 0.0218, 1.1749 + bone.roll = 1.8283 + bone.use_connect = True + bone.parent = arm.edit_bones[bones['finger_middle.01.R']] + bones['finger_middle.02.R'] = bone.name + bone = arm.edit_bones.new('finger_ring.02.R') + bone.head[:] = -0.7715, 0.0499, 1.2070 + bone.tail[:] = -0.7794, 0.0494, 1.1762 + bone.roll = 1.8946 + bone.use_connect = True + bone.parent = arm.edit_bones[bones['finger_ring.01.R']] + bones['finger_ring.02.R'] = bone.name + bone = arm.edit_bones.new('finger_pinky.02.R') + bone.head[:] = -0.7589, 0.0765, 1.2156 + bone.tail[:] = -0.7618, 0.0770, 1.1932 + bone.roll = 1.9059 + bone.use_connect = True + bone.parent = arm.edit_bones[bones['finger_pinky.01.R']] + bones['finger_pinky.02.R'] = bone.name + bone = arm.edit_bones.new('finger_index.03.L') + bone.head[:] = 0.7840, -0.0003, 1.1858 + bone.tail[:] = 0.7892, 0.0006, 1.1636 + bone.roll = -1.6760 + bone.use_connect = True + bone.parent = arm.edit_bones[bones['finger_index.02.L']] + bones['finger_index.03.L'] = bone.name + bone = arm.edit_bones.new('thumb.03.L') + bone.head[:] = 0.7056, -0.0057, 1.2145 + bone.tail[:] = 0.7194, -0.0098, 1.1995 + bone.roll = -0.5826 + bone.use_connect = True + bone.parent = arm.edit_bones[bones['thumb.02.L']] + bones['thumb.03.L'] = bone.name + bone = arm.edit_bones.new('finger_middle.03.L') + bone.head[:] = 0.7851, 0.0218, 1.1749 + bone.tail[:] = 0.7888, 0.0216, 1.1525 + bone.roll = -1.7483 + bone.use_connect = True + bone.parent = arm.edit_bones[bones['finger_middle.02.L']] + bones['finger_middle.03.L'] = bone.name + bone = arm.edit_bones.new('finger_ring.03.L') + bone.head[:] = 0.7794, 0.0494, 1.1762 + bone.tail[:] = 0.7781, 0.0498, 1.1577 + bone.roll = -1.6582 + bone.use_connect = True + bone.parent = arm.edit_bones[bones['finger_ring.02.L']] + bones['finger_ring.03.L'] = bone.name + bone = arm.edit_bones.new('finger_pinky.03.L') + bone.head[:] = 0.7618, 0.0770, 1.1932 + bone.tail[:] = 0.7611, 0.0772, 1.1782 + bone.roll = -1.7639 + bone.use_connect = True + bone.parent = arm.edit_bones[bones['finger_pinky.02.L']] + bones['finger_pinky.03.L'] = bone.name + bone = arm.edit_bones.new('finger_index.03.R') + bone.head[:] = -0.7840, -0.0003, 1.1858 + bone.tail[:] = -0.7892, 0.0006, 1.1636 + bone.roll = 1.6760 + bone.use_connect = True + bone.parent = arm.edit_bones[bones['finger_index.02.R']] + bones['finger_index.03.R'] = bone.name + bone = arm.edit_bones.new('thumb.03.R') + bone.head[:] = -0.7056, -0.0057, 1.2145 + bone.tail[:] = -0.7194, -0.0098, 1.1995 + bone.roll = 0.5826 + bone.use_connect = True + bone.parent = arm.edit_bones[bones['thumb.02.R']] + bones['thumb.03.R'] = bone.name + bone = arm.edit_bones.new('finger_middle.03.R') + bone.head[:] = -0.7851, 0.0218, 1.1749 + bone.tail[:] = -0.7888, 0.0216, 1.1525 + bone.roll = 1.7483 + bone.use_connect = True + bone.parent = arm.edit_bones[bones['finger_middle.02.R']] + bones['finger_middle.03.R'] = bone.name + bone = arm.edit_bones.new('finger_ring.03.R') + bone.head[:] = -0.7794, 0.0494, 1.1762 + bone.tail[:] = -0.7781, 0.0498, 1.1577 + bone.roll = 1.6582 + bone.use_connect = True + bone.parent = arm.edit_bones[bones['finger_ring.02.R']] + bones['finger_ring.03.R'] = bone.name + bone = arm.edit_bones.new('finger_pinky.03.R') + bone.head[:] = -0.7618, 0.0770, 1.1932 + bone.tail[:] = -0.7611, 0.0772, 1.1782 + bone.roll = 1.7639 + bone.use_connect = True + bone.parent = arm.edit_bones[bones['finger_pinky.02.R']] + bones['finger_pinky.03.R'] = bone.name + + bpy.ops.object.mode_set(mode='OBJECT') + pbone = obj.pose.bones[bones['hips']] + pbone.rigify_type = 'spine' + pbone.rigify_parameters.add() + pbone.rigify_parameters[0].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 = obj.pose.bones[bones['spine']] + 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 = [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['thigh.L']] + pbone.rigify_type = 'biped.leg' + pbone.lock_location = (True, True, True) + 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.rigify_parameters.add() + try: + pbone.rigify_parameters[0].separate_ik_layers = True + except AttributeError: + pass + try: + pbone.rigify_parameters[0].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] + except AttributeError: + pass + pbone = obj.pose.bones[bones['thigh.R']] + pbone.rigify_type = 'biped.leg' + pbone.lock_location = (True, True, True) + 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.rigify_parameters.add() + try: + pbone.rigify_parameters[0].separate_ik_layers = True + except AttributeError: + pass + try: + pbone.rigify_parameters[0].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 + pbone = obj.pose.bones[bones['ribs']] + 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 = [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['shin.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 = obj.pose.bones[bones['shin.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 = obj.pose.bones[bones['neck']] + pbone.rigify_type = 'neck_short' + pbone.lock_location = (True, True, True) + 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, 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.L']] + pbone.rigify_type = 'basic.copy' + pbone.lock_location = (True, True, True) + pbone.lock_rotation = (False, True, False) + 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.rigify_parameters.add() + pbone = obj.pose.bones[bones['shoulder.R']] + pbone.rigify_type = 'basic.copy' + pbone.lock_location = (True, True, True) + pbone.lock_rotation = (False, True, False) + 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.rigify_parameters.add() + pbone = obj.pose.bones[bones['foot.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 = obj.pose.bones[bones['heel.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 = 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 = obj.pose.bones[bones['foot.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 = obj.pose.bones[bones['heel.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 = 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 = obj.pose.bones[bones['head']] + 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, 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['upper_arm.L']] + pbone.rigify_type = 'biped.arm' + pbone.lock_location = (True, True, True) + 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, 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] + pbone.rigify_parameters.add() + try: + pbone.rigify_parameters[0].separate_ik_layers = True + except AttributeError: + pass + try: + pbone.rigify_parameters[0].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 + pbone = obj.pose.bones[bones['upper_arm.R']] + pbone.rigify_type = 'biped.arm' + pbone.lock_location = (True, True, True) + 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, 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.rigify_parameters.add() + try: + pbone.rigify_parameters[0].separate_ik_layers = True + except AttributeError: + pass + try: + pbone.rigify_parameters[0].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] + except AttributeError: + pass + pbone = obj.pose.bones[bones['toe.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 = 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, 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['forearm.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, 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] + pbone = obj.pose.bones[bones['forearm.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, 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 = obj.pose.bones[bones['hand.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, 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] + pbone = obj.pose.bones[bones['hand.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, 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 = obj.pose.bones[bones['palm.01.L']] + pbone.rigify_type = 'palm' + pbone.lock_location = (True, True, True) + pbone.lock_rotation = (False, True, True) + pbone.lock_rotation_w = False + pbone.lock_scale = (False, False, False) + pbone.rotation_mode = 'YXZ' + 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] + pbone.rigify_parameters.add() + pbone = obj.pose.bones[bones['palm.02.L']] + pbone.rigify_type = '' + pbone.lock_location = (True, True, True) + pbone.lock_rotation = (False, True, True) + pbone.lock_rotation_w = False + pbone.lock_scale = (False, False, False) + pbone.rotation_mode = 'YXZ' + 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] + pbone = obj.pose.bones[bones['palm.03.L']] + pbone.rigify_type = '' + pbone.lock_location = (True, True, True) + pbone.lock_rotation = (False, True, True) + pbone.lock_rotation_w = False + pbone.lock_scale = (False, False, False) + pbone.rotation_mode = 'YXZ' + 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] + pbone = obj.pose.bones[bones['palm.04.L']] + pbone.rigify_type = '' + pbone.lock_location = (True, True, True) + pbone.lock_rotation = (False, True, True) + pbone.lock_rotation_w = False + pbone.lock_scale = (False, False, False) + pbone.rotation_mode = 'YXZ' + 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] + pbone = obj.pose.bones[bones['palm.01.R']] + pbone.rigify_type = 'palm' + pbone.lock_location = (False, False, False) + pbone.lock_rotation = (False, True, True) + pbone.lock_rotation_w = False + pbone.lock_scale = (False, False, False) + pbone.rotation_mode = 'YXZ' + 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] + pbone.rigify_parameters.add() + pbone = obj.pose.bones[bones['palm.02.R']] + pbone.rigify_type = '' + pbone.lock_location = (False, False, False) + pbone.lock_rotation = (False, True, True) + pbone.lock_rotation_w = False + pbone.lock_scale = (False, False, False) + pbone.rotation_mode = 'YXZ' + 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] + pbone = obj.pose.bones[bones['palm.03.R']] + pbone.rigify_type = '' + pbone.lock_location = (False, False, False) + pbone.lock_rotation = (False, True, True) + pbone.lock_rotation_w = False + pbone.lock_scale = (False, False, False) + pbone.rotation_mode = 'YXZ' + 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] + pbone = obj.pose.bones[bones['palm.04.R']] + pbone.rigify_type = '' + pbone.lock_location = (False, False, False) + pbone.lock_rotation = (False, True, True) + pbone.lock_rotation_w = False + pbone.lock_scale = (False, False, False) + pbone.rotation_mode = 'YXZ' + 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] + pbone = obj.pose.bones[bones['finger_index.01.L']] + pbone.rigify_type = 'finger' + pbone.lock_location = (True, True, True) + 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, 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] + pbone.rigify_parameters.add() + try: + pbone.rigify_parameters[0].separate_extra_layers = True + except AttributeError: + pass + try: + pbone.rigify_parameters[0].extra_layers = [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, False, False] + except AttributeError: + pass + pbone = obj.pose.bones[bones['thumb.01.L']] + pbone.rigify_type = 'finger' + pbone.lock_location = (True, True, True) + 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, 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] + pbone.rigify_parameters.add() + try: + pbone.rigify_parameters[0].extra_layers = [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, False, False] + except AttributeError: + pass + try: + pbone.rigify_parameters[0].separate_extra_layers = True + except AttributeError: + pass + pbone = obj.pose.bones[bones['finger_middle.01.L']] + pbone.rigify_type = 'finger' + pbone.lock_location = (True, True, True) + 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, 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] + pbone.rigify_parameters.add() + try: + pbone.rigify_parameters[0].separate_extra_layers = True + except AttributeError: + pass + try: + pbone.rigify_parameters[0].extra_layers = [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, False, False] + except AttributeError: + pass + pbone = obj.pose.bones[bones['finger_ring.01.L']] + pbone.rigify_type = 'finger' + pbone.lock_location = (True, True, True) + 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, 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] + pbone.rigify_parameters.add() + try: + pbone.rigify_parameters[0].separate_extra_layers = True + except AttributeError: + pass + try: + pbone.rigify_parameters[0].extra_layers = [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, False, False] + except AttributeError: + pass + pbone = obj.pose.bones[bones['finger_pinky.01.L']] + pbone.rigify_type = 'finger' + pbone.lock_location = (True, True, True) + 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, 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] + pbone.rigify_parameters.add() + try: + pbone.rigify_parameters[0].separate_extra_layers = True + except AttributeError: + pass + try: + pbone.rigify_parameters[0].extra_layers = [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, False, False] + except AttributeError: + pass + pbone = obj.pose.bones[bones['finger_index.01.R']] + pbone.rigify_type = 'finger' + pbone.lock_location = (True, True, True) + 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, 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] + pbone.rigify_parameters.add() + try: + pbone.rigify_parameters[0].extra_layers = [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, False, False] + except AttributeError: + pass + try: + pbone.rigify_parameters[0].separate_extra_layers = True + except AttributeError: + pass + pbone = obj.pose.bones[bones['thumb.01.R']] + pbone.rigify_type = 'finger' + pbone.lock_location = (True, True, True) + 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, 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] + pbone.rigify_parameters.add() + try: + pbone.rigify_parameters[0].separate_extra_layers = True + except AttributeError: + pass + try: + pbone.rigify_parameters[0].extra_layers = [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, False, False] + except AttributeError: + pass + pbone = obj.pose.bones[bones['finger_middle.01.R']] + pbone.rigify_type = 'finger' + pbone.lock_location = (True, True, True) + 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, 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] + pbone.rigify_parameters.add() + try: + pbone.rigify_parameters[0].separate_extra_layers = True + except AttributeError: + pass + try: + pbone.rigify_parameters[0].extra_layers = [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, False, False] + except AttributeError: + pass + pbone = obj.pose.bones[bones['finger_ring.01.R']] + pbone.rigify_type = 'finger' + pbone.lock_location = (True, True, True) + 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, 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] + pbone.rigify_parameters.add() + try: + pbone.rigify_parameters[0].separate_extra_layers = True + except AttributeError: + pass + try: + pbone.rigify_parameters[0].extra_layers = [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, False, False] + except AttributeError: + pass + pbone = obj.pose.bones[bones['finger_pinky.01.R']] + pbone.rigify_type = 'finger' + pbone.lock_location = (True, True, True) + 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, 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] + pbone.rigify_parameters.add() + try: + pbone.rigify_parameters[0].separate_extra_layers = True + except AttributeError: + pass + try: + pbone.rigify_parameters[0].extra_layers = [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, False, False] + except AttributeError: + pass + pbone = obj.pose.bones[bones['finger_index.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, 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] + pbone = obj.pose.bones[bones['thumb.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, 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] + pbone = obj.pose.bones[bones['finger_middle.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, 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] + pbone = obj.pose.bones[bones['finger_ring.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, 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] + pbone = obj.pose.bones[bones['finger_pinky.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, 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] + pbone = obj.pose.bones[bones['finger_index.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, 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] + pbone = obj.pose.bones[bones['thumb.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, 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] + pbone = obj.pose.bones[bones['finger_middle.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, 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] + pbone = obj.pose.bones[bones['finger_ring.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, 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] + pbone = obj.pose.bones[bones['finger_pinky.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, 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] + pbone = obj.pose.bones[bones['finger_index.03.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, 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] + pbone = obj.pose.bones[bones['thumb.03.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, 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] + pbone = obj.pose.bones[bones['finger_middle.03.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, 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] + pbone = obj.pose.bones[bones['finger_ring.03.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, 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] + pbone = obj.pose.bones[bones['finger_pinky.03.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, 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] + pbone = obj.pose.bones[bones['finger_index.03.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, 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] + pbone = obj.pose.bones[bones['thumb.03.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, 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] + pbone = obj.pose.bones[bones['finger_middle.03.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, 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] + pbone = obj.pose.bones[bones['finger_ring.03.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, 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] + pbone = obj.pose.bones[bones['finger_pinky.03.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, 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] + + bpy.ops.object.mode_set(mode='EDIT') + for bone in arm.edit_bones: + bone.select = False + bone.select_head = False + bone.select_tail = False + for b in bones: + bone = arm.edit_bones[bones[b]] + bone.select = True + bone.select_head = True + 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)] + diff --git a/rigify/rig_ui_template.py b/rigify/rig_ui_template.py new file mode 100644 index 00000000..a54ab175 --- /dev/null +++ b/rigify/rig_ui_template.py @@ -0,0 +1,570 @@ +#====================== 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 ======================== + +UI_SLIDERS = ''' +import bpy +from mathutils import Matrix, Vector +from math import acos + +rig_id = "%s" + + +######################################### +## "Visual Transform" helper functions ## +######################################### + +def get_pose_matrix_in_other_space(mat, pose_bone): + """ Returns the transform matrix relative to pose_bone's current + transform space. In other words, presuming that mat is in + armature space, slapping the returned matrix onto pose_bone + should give it the armature-space transforms of mat. + TODO: try to handle cases with axis-scaled parents better. + """ + rest = pose_bone.bone.matrix_local.copy() + rest_inv = rest.inverted() + if pose_bone.parent: + par_mat = pose_bone.parent.matrix.copy() + par_inv = par_mat.inverted() + par_rest = pose_bone.parent.bone.matrix_local.copy() + else: + par_mat = Matrix() + par_inv = Matrix() + par_rest = Matrix() + + # Get matrix in bone's current transform space + smat = rest_inv * (par_rest * (par_inv * mat)) + + # Compensate for non-local location + #if not pose_bone.bone.use_local_location: + # loc = smat.to_translation() * (par_rest.inverted() * rest).to_quaternion() + # smat[3][0] = loc[0] + # smat[3][1] = loc[1] + # smat[3][2] = loc[2] + + return smat + + +def get_local_pose_matrix(pose_bone): + """ Returns the local transform matrix of the given pose bone. + """ + return get_pose_matrix_in_other_space(pose_bone.matrix, pose_bone) + + +def set_pose_translation(pose_bone, mat): + """ Sets the pose bone's translation to the same translation as the given matrix. + Matrix should be given in bone's local space. + """ + if pose_bone.bone.use_local_location == True: + pose_bone.location = mat.to_translation() + else: + loc = mat.to_translation() + + rest = pose_bone.bone.matrix_local.copy() + if pose_bone.bone.parent: + par_rest = pose_bone.bone.parent.matrix_local.copy() + else: + par_rest = Matrix() + + q = (par_rest.inverted() * rest).to_quaternion() + pose_bone.location = loc * q + + +def set_pose_rotation(pose_bone, mat): + """ Sets the pose bone's rotation to the same rotation as the given matrix. + Matrix should be given in bone's local space. + """ + q = mat.to_quaternion() + + if pose_bone.rotation_mode == 'QUATERNION': + pose_bone.rotation_quaternion = q + elif pose_bone.rotation_mode == 'AXIS_ANGLE': + pose_bone.rotation_axis_angle[0] = q.angle + pose_bone.rotation_axis_angle[1] = q.axis[0] + pose_bone.rotation_axis_angle[2] = q.axis[1] + pose_bone.rotation_axis_angle[3] = q.axis[2] + else: + pose_bone.rotation_euler = q.to_euler(pose_bone.rotation_mode) + + +def set_pose_scale(pose_bone, mat): + """ Sets the pose bone's scale to the same scale as the given matrix. + Matrix should be given in bone's local space. + """ + pose_bone.scale = mat.to_scale() + + +def match_pose_translation(pose_bone, target_bone): + """ Matches pose_bone's visual translation to target_bone's visual + translation. + This function assumes you are in pose mode on the relevant armature. + """ + mat = get_pose_matrix_in_other_space(target_bone.matrix, pose_bone) + set_pose_translation(pose_bone, mat) + bpy.ops.object.mode_set(mode='OBJECT') + bpy.ops.object.mode_set(mode='POSE') + + +def match_pose_rotation(pose_bone, target_bone): + """ Matches pose_bone's visual rotation to target_bone's visual + rotation. + This function assumes you are in pose mode on the relevant armature. + """ + mat = get_pose_matrix_in_other_space(target_bone.matrix, pose_bone) + set_pose_rotation(pose_bone, mat) + bpy.ops.object.mode_set(mode='OBJECT') + bpy.ops.object.mode_set(mode='POSE') + + +def match_pose_scale(pose_bone, target_bone): + """ Matches pose_bone's visual scale to target_bone's visual + scale. + This function assumes you are in pose mode on the relevant armature. + """ + mat = get_pose_matrix_in_other_space(target_bone.matrix, pose_bone) + set_pose_scale(pose_bone, mat) + bpy.ops.object.mode_set(mode='OBJECT') + bpy.ops.object.mode_set(mode='POSE') + + +############################## +## IK/FK snapping functions ## +############################## + +def match_pole_target(ik_first, ik_last, pole, match_bone, length): + """ Places an IK chain's pole target to match ik_first's + transforms to match_bone. All bones should be given as pose bones. + You need to be in pose mode on the relevant armature object. + ik_first: first bone in the IK chain + ik_last: last bone in the IK chain + pole: pole target bone for the IK chain + match_bone: bone to match ik_first to (probably first bone in a matching FK chain) + length: distance pole target should be placed from the chain center + """ + a = ik_first.matrix.to_translation() + b = ik_last.matrix.to_translation() + ik_last.vector + + # Vector from the head of ik_first to the + # tip of ik_last + ikv = b - a + + # Create a vector that is not aligned with ikv. + # It doesn't matter what vector. Just any vector + # that's guaranteed to not be pointing in the same + # direction. In this case, we create a unit vector + # on the axis of the smallest component of ikv. + if abs(ikv[0]) < abs(ikv[1]) and abs(ikv[0]) < abs(ikv[2]): + v = Vector((1,0,0)) + elif abs(ikv[1]) < abs(ikv[2]): + v = Vector((0,1,0)) + else: + v = Vector((0,0,1)) + + # Get a vector perpendicular to ikv + pv = v.cross(ikv).normalized() * length + + def set_pole(pvi): + """ Set pole target's position based on a vector + from the arm center line. + """ + # Translate pvi into armature space + ploc = a + (ikv/2) + pvi + + # Set pole target to location + mat = get_pose_matrix_in_other_space(Matrix.Translation(ploc), pole) + set_pose_translation(pole, mat) + + bpy.ops.object.mode_set(mode='OBJECT') + bpy.ops.object.mode_set(mode='POSE') + + set_pole(pv) + + # Get the rotation difference between ik_first and match_bone + q1 = ik_first.matrix.to_quaternion() + q2 = match_bone.matrix.to_quaternion() + angle = acos(min(1,max(-1,q1.dot(q2)))) * 2 + + # Compensate for the rotation difference + if angle > 0.0001: + pv *= Matrix.Rotation(angle, 4, ikv).to_quaternion() + set_pole(pv) + + # Get rotation difference again, to see if we + # compensated in the right direction + q1 = ik_first.matrix.to_quaternion() + q2 = match_bone.matrix.to_quaternion() + angle2 = acos(min(1,max(-1,q1.dot(q2)))) * 2 + if angle2 > 0.0001: + # Compensate in the other direction + pv *= Matrix.Rotation((angle*(-2)), 4, ikv).to_quaternion() + set_pole(pv) + + +def fk2ik_arm(obj, fk, ik): + """ Matches the fk bones in an arm rig to the ik bones. + obj: armature object + fk: list of fk bone names + ik: list of ik bone names + """ + uarm = obj.pose.bones[fk[0]] + farm = obj.pose.bones[fk[1]] + hand = obj.pose.bones[fk[2]] + uarmi = obj.pose.bones[ik[0]] + farmi = obj.pose.bones[ik[1]] + handi = obj.pose.bones[ik[2]] + + # Upper arm position + match_pose_rotation(uarm, uarmi) + match_pose_scale(uarm, uarmi) + + # Forearm position + match_pose_rotation(farm, farmi) + match_pose_scale(farm, farmi) + + # Hand position + match_pose_rotation(hand, handi) + match_pose_scale(hand, handi) + + +def ik2fk_arm(obj, fk, ik): + """ Matches the ik bones in an arm rig to the fk bones. + obj: armature object + fk: list of fk bone names + ik: list of ik bone names + """ + uarm = obj.pose.bones[fk[0]] + farm = obj.pose.bones[fk[1]] + hand = obj.pose.bones[fk[2]] + uarmi = obj.pose.bones[ik[0]] + farmi = obj.pose.bones[ik[1]] + handi = obj.pose.bones[ik[2]] + pole = obj.pose.bones[ik[3]] + + # Hand position + match_pose_translation(handi, hand) + match_pose_rotation(handi, hand) + match_pose_scale(handi, hand) + + # Pole target position + match_pole_target(uarmi, farmi, pole, uarm, (uarmi.length + farmi.length)) + + +def fk2ik_leg(obj, fk, ik): + """ Matches the fk bones in a leg rig to the ik bones. + obj: armature object + fk: list of fk bone names + ik: list of ik bone names + """ + thigh = obj.pose.bones[fk[0]] + shin = obj.pose.bones[fk[1]] + foot = obj.pose.bones[fk[2]] + mfoot = obj.pose.bones[fk[3]] + thighi = obj.pose.bones[ik[0]] + shini = obj.pose.bones[ik[1]] + mfooti = obj.pose.bones[ik[2]] + + # Thigh position + match_pose_rotation(thigh, thighi) + match_pose_scale(thigh, thighi) + + # Shin position + match_pose_rotation(shin, shini) + match_pose_scale(shin, shini) + + # Foot position + mat = mfoot.bone.matrix_local.inverted() * foot.bone.matrix_local + footmat = get_pose_matrix_in_other_space(mfooti.matrix, foot) * mat + set_pose_rotation(foot, footmat) + set_pose_scale(foot, footmat) + bpy.ops.object.mode_set(mode='OBJECT') + bpy.ops.object.mode_set(mode='POSE') + + +def ik2fk_leg(obj, fk, ik): + """ Matches the ik bones in a leg rig to the fk bones. + obj: armature object + fk: list of fk bone names + ik: list of ik bone names + """ + thigh = obj.pose.bones[fk[0]] + shin = obj.pose.bones[fk[1]] + mfoot = obj.pose.bones[fk[2]] + thighi = obj.pose.bones[ik[0]] + shini = obj.pose.bones[ik[1]] + footi = obj.pose.bones[ik[2]] + footroll = obj.pose.bones[ik[3]] + pole = obj.pose.bones[ik[4]] + mfooti = obj.pose.bones[ik[5]] + + # Clear footroll + set_pose_rotation(footroll, Matrix()) + + # Foot position + mat = mfooti.bone.matrix_local.inverted() * footi.bone.matrix_local + footmat = get_pose_matrix_in_other_space(mfoot.matrix, footi) * mat + set_pose_translation(footi, footmat) + set_pose_rotation(footi, footmat) + set_pose_scale(footi, footmat) + bpy.ops.object.mode_set(mode='OBJECT') + bpy.ops.object.mode_set(mode='POSE') + + # Pole target position + match_pole_target(thighi, shini, pole, thigh, (thighi.length + shini.length)) + + +############################## +## IK/FK snapping operators ## +############################## + +class Rigify_Arm_FK2IK(bpy.types.Operator): + """ Snaps an FK arm to an IK arm. + """ + bl_idname = "pose.rigify_arm_fk2ik_" + rig_id + bl_label = "Rigify Snap FK arm to IK" + bl_options = {'UNDO'} + + uarm_fk = bpy.props.StringProperty(name="Upper Arm FK Name") + farm_fk = bpy.props.StringProperty(name="Forerm FK Name") + hand_fk = bpy.props.StringProperty(name="Hand FK Name") + + uarm_ik = bpy.props.StringProperty(name="Upper Arm IK Name") + farm_ik = bpy.props.StringProperty(name="Forearm IK Name") + hand_ik = bpy.props.StringProperty(name="Hand IK Name") + + @classmethod + def poll(cls, context): + return (context.active_object != None and context.mode == 'POSE') + + def execute(self, context): + use_global_undo = context.user_preferences.edit.use_global_undo + context.user_preferences.edit.use_global_undo = False + try: + fk2ik_arm(context.active_object, fk=[self.uarm_fk, self.farm_fk, self.hand_fk], ik=[self.uarm_ik, self.farm_ik, self.hand_ik]) + finally: + context.user_preferences.edit.use_global_undo = use_global_undo + return {'FINISHED'} + + +class Rigify_Arm_IK2FK(bpy.types.Operator): + """ Snaps an IK arm to an FK arm. + """ + bl_idname = "pose.rigify_arm_ik2fk_" + rig_id + bl_label = "Rigify Snap IK arm to FK" + bl_options = {'UNDO'} + + uarm_fk = bpy.props.StringProperty(name="Upper Arm FK Name") + farm_fk = bpy.props.StringProperty(name="Forerm FK Name") + hand_fk = bpy.props.StringProperty(name="Hand FK Name") + + uarm_ik = bpy.props.StringProperty(name="Upper Arm IK Name") + farm_ik = bpy.props.StringProperty(name="Forearm IK Name") + hand_ik = bpy.props.StringProperty(name="Hand IK Name") + pole = bpy.props.StringProperty(name="Pole IK Name") + + @classmethod + def poll(cls, context): + return (context.active_object != None and context.mode == 'POSE') + + def execute(self, context): + use_global_undo = context.user_preferences.edit.use_global_undo + context.user_preferences.edit.use_global_undo = False + try: + ik2fk_arm(context.active_object, fk=[self.uarm_fk, self.farm_fk, self.hand_fk], ik=[self.uarm_ik, self.farm_ik, self.hand_ik, self.pole]) + finally: + context.user_preferences.edit.use_global_undo = use_global_undo + return {'FINISHED'} + + +class Rigify_Leg_FK2IK(bpy.types.Operator): + """ Snaps an FK leg to an IK leg. + """ + bl_idname = "pose.rigify_leg_fk2ik_" + rig_id + bl_label = "Rigify Snap FK leg to IK" + bl_options = {'UNDO'} + + thigh_fk = bpy.props.StringProperty(name="Thigh FK Name") + shin_fk = bpy.props.StringProperty(name="Shin FK Name") + foot_fk = bpy.props.StringProperty(name="Foot FK Name") + mfoot_fk = bpy.props.StringProperty(name="MFoot FK Name") + + thigh_ik = bpy.props.StringProperty(name="Thigh IK Name") + shin_ik = bpy.props.StringProperty(name="Shin IK Name") + mfoot_ik = bpy.props.StringProperty(name="MFoot IK Name") + + @classmethod + def poll(cls, context): + return (context.active_object != None and context.mode == 'POSE') + + def execute(self, context): + 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]) + finally: + context.user_preferences.edit.use_global_undo = use_global_undo + return {'FINISHED'} + + +class Rigify_Leg_IK2FK(bpy.types.Operator): + """ Snaps an IK leg to an FK leg. + """ + bl_idname = "pose.rigify_leg_ik2fk_" + rig_id + bl_label = "Rigify Snap IK leg to FK" + bl_options = {'UNDO'} + + thigh_fk = bpy.props.StringProperty(name="Thigh FK Name") + shin_fk = bpy.props.StringProperty(name="Shin FK Name") + mfoot_fk = bpy.props.StringProperty(name="MFoot FK Name") + + 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") + footroll = bpy.props.StringProperty(name="Foot Roll Name") + pole = bpy.props.StringProperty(name="Pole IK Name") + mfoot_ik = bpy.props.StringProperty(name="MFoot IK Name") + + @classmethod + def poll(cls, context): + return (context.active_object != None and context.mode == 'POSE') + + def execute(self, context): + use_global_undo = context.user_preferences.edit.use_global_undo + context.user_preferences.edit.use_global_undo = False + try: + ik2fk_leg(context.active_object, fk=[self.thigh_fk, self.shin_fk, self.mfoot_fk], ik=[self.thigh_ik, self.shin_ik, self.foot_ik, self.footroll, self.pole, self.mfoot_ik]) + finally: + context.user_preferences.edit.use_global_undo = use_global_undo + return {'FINISHED'} + + +################### +## Rig UI Panels ## +################### + +class RigUI(bpy.types.Panel): + bl_space_type = 'VIEW_3D' + bl_region_type = 'UI' + bl_label = "Rig Main Properties" + bl_idname = rig_id + "_PT_rig_ui" + + @classmethod + def poll(self, context): + if context.mode != 'POSE': + return False + try: + return (context.active_object.data.get("rig_id") == rig_id) + except (AttributeError, KeyError, TypeError): + return False + + def draw(self, context): + layout = self.layout + pose_bones = context.active_object.pose.bones + try: + selected_bones = [bone.name for bone in context.selected_pose_bones] + selected_bones += [context.active_pose_bone.name] + except (AttributeError, TypeError): + return + + def is_selected(names): + # Returns whether any of the named bones are selected. + if type(names) == list: + for name in names: + if name in selected_bones: + return True + elif names in selected_bones: + return True + return False + + +''' + + +def layers_ui(layers, layout): + """ Turn a list of booleans + a list of names into a layer UI. + """ + + code = ''' +class RigLayers(bpy.types.Panel): + bl_space_type = 'VIEW_3D' + bl_region_type = 'UI' + bl_label = "Rig Layers" + bl_idname = rig_id + "_PT_rig_layers" + + @classmethod + def poll(self, context): + try: + return (context.active_object.data.get("rig_id") == rig_id) + except (AttributeError, KeyError, TypeError): + return False + + def draw(self, context): + layout = self.layout + col = layout.column() +''' + rows = {} + for i in range(28): + if layers[i]: + if layout[i][1] not in rows: + rows[layout[i][1]] = [] + rows[layout[i][1]] += [(layout[i][0], i)] + + keys = list(rows.keys()) + keys.sort() + + for key in keys: + code += "\n row = col.row()\n" + i = 0 + for l in rows[key]: + if i > 3: + code += "\n row = col.row()\n" + i = 0 + code += " row.prop(context.active_object.data, 'layers', index=%s, toggle=True, text='%s')\n" % (str(l[1]), l[0]) + i += 1 + + # Root layer + code += "\n row = col.row()" + code += "\n row.separator()" + code += "\n row = col.row()" + code += "\n row.separator()\n" + code += "\n row = col.row()\n" + code += " row.prop(context.active_object.data, 'layers', index=28, toggle=True, text='Root')\n" + + + return code + + +UI_REGISTER = ''' + +def register(): + bpy.utils.register_class(Rigify_Arm_FK2IK) + bpy.utils.register_class(Rigify_Arm_IK2FK) + bpy.utils.register_class(Rigify_Leg_FK2IK) + bpy.utils.register_class(Rigify_Leg_IK2FK) + bpy.utils.register_class(RigUI) + bpy.utils.register_class(RigLayers) + +def unregister(): + bpy.utils.unregister_class(Rigify_Arm_FK2IK) + bpy.utils.unregister_class(Rigify_Arm_IK2FK) + bpy.utils.unregister_class(Rigify_Leg_FK2IK) + bpy.utils.unregister_class(Rigify_Leg_IK2FK) + bpy.utils.unregister_class(RigUI) + bpy.utils.unregister_class(RigLayers) + +register() +''' + diff --git a/rigify/rigs/__init__.py b/rigify/rigs/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/rigify/rigs/basic/__init__.py b/rigify/rigs/basic/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/rigify/rigs/basic/copy.py b/rigify/rigs/basic/copy.py new file mode 100644 index 00000000..22beedf1 --- /dev/null +++ b/rigify/rigs/basic/copy.py @@ -0,0 +1,142 @@ +#====================== 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 ======================== + +import bpy +from rigify.utils import MetarigError +from rigify.utils import copy_bone +from rigify.utils import strip_org, make_deformer_name +from rigify.utils import create_bone_widget + + +class Rig: + """ A "copy" rig. All it does is duplicate the original bone and + constrain it. + This is a control and deformation rig. + + """ + def __init__(self, obj, bone, params): + """ Gather and validate data about the rig. + """ + self.obj = obj + self.org_bone = bone + self.org_name = strip_org(bone) + self.params = params + self.make_control = params.make_control + self.make_deform = params.make_deform + + 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') + + # Make a control bone (copy of original). + if self.make_control: + bone = copy_bone(self.obj, self.org_bone, self.org_name) + + # Make a deformation bone (copy of original, child of original). + if self.make_deform: + def_bone = copy_bone(self.obj, self.org_bone, make_deformer_name(self.org_name)) + + # Get edit bones + eb = self.obj.data.edit_bones + if self.make_control: + bone_e = eb[bone] + if self.make_deform: + def_bone_e = eb[def_bone] + + # Parent + if self.make_deform: + def_bone_e.use_connect = False + def_bone_e.parent = eb[self.org_bone] + + bpy.ops.object.mode_set(mode='OBJECT') + pb = self.obj.pose.bones + + if self.make_control: + # Constrain the original bone. + con = pb[self.org_bone].constraints.new('COPY_TRANSFORMS') + con.name = "copy_transforms" + con.target = self.obj + con.subtarget = bone + + # Create control widget + create_bone_widget(self.obj, bone) + + @classmethod + def add_parameters(self, group): + """ Add the parameters of this rig type to the + RigifyParameters PropertyGroup + """ + group.make_control = bpy.props.BoolProperty(name="Control", default=True, description="Create a control bone for the copy.") + group.make_deform = bpy.props.BoolProperty(name="Deform", default=True, description="Create a deform bone for the copy.") + + + @classmethod + def parameters_ui(self, layout, obj, bone): + """ Create the ui for the rig parameters. + """ + params = obj.pose.bones[bone].rigify_parameters[0] + + r = layout.row() + r.prop(params, "make_control") + r = layout.row() + r.prop(params, "make_deform") + + @classmethod + def create_sample(self, obj): + """ Create a sample metarig for this rig type. + + """ + # generated by rigify.utils.write_metarig + bpy.ops.object.mode_set(mode='EDIT') + arm = obj.data + + bones = {} + + bone = arm.edit_bones.new('Bone') + bone.head[:] = 0.0000, 0.0000, 0.0000 + bone.tail[:] = 0.0000, 0.0000, 0.2000 + bone.roll = 0.0000 + bone.use_connect = False + bones['Bone'] = bone.name + + bpy.ops.object.mode_set(mode='OBJECT') + pbone = obj.pose.bones[bones['Bone']] + pbone.rigify_type = 'simple.bone' + 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.rigify_parameters.add() + + bpy.ops.object.mode_set(mode='EDIT') + for bone in arm.edit_bones: + bone.select = False + bone.select_head = False + bone.select_tail = False + for b in bones: + bone = arm.edit_bones[bones[b]] + bone.select = True + bone.select_head = True + bone.select_tail = True + arm.edit_bones.active = bone + diff --git a/rigify/rigs/basic/copy_chain.py b/rigify/rigs/basic/copy_chain.py new file mode 100644 index 00000000..4910ef82 --- /dev/null +++ b/rigify/rigs/basic/copy_chain.py @@ -0,0 +1,210 @@ +#====================== 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 ======================== + +import bpy +from rigify.utils import MetarigError +from rigify.utils import copy_bone +from rigify.utils import connected_children_names +from rigify.utils import strip_org, make_deformer_name +from rigify.utils import create_bone_widget + + +class Rig: + """ A "copy_chain" rig. All it does is duplicate the original bone chain + and constrain it. + This is a control and deformation rig. + + """ + def __init__(self, obj, bone_name, params): + """ Gather and validate data about the rig. + """ + self.obj = obj + self.org_bones = [bone_name] + connected_children_names(obj, bone_name) + self.params = params + self.make_controls = params.make_controls + self.make_deforms = params.make_deforms + + if len(self.org_bones) <= 1: + raise MetarigError("RIGIFY ERROR: Bone '%s': input to rig type must be a chain of 2 or more bones." % (strip_org(bone))) + + 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 the deformation and control bone chains. + # Just copies of the original chain. + def_chain = [] + ctrl_chain = [] + for i in range(len(self.org_bones)): + name = self.org_bones[i] + + # Control bone + if self.make_controls: + # Copy + ctrl_bone = copy_bone(self.obj, name) + eb = self.obj.data.edit_bones + ctrl_bone_e = eb[ctrl_bone] + # Name + ctrl_bone_e.name = strip_org(name) + # Parenting + if i == 0: + # First bone + ctrl_bone_e.parent = eb[self.org_bones[0]].parent + else: + # The rest + ctrl_bone_e.parent = eb[ctrl_chain[-1]] + # Add to list + ctrl_chain += [ctrl_bone_e.name] + else: + ctrl_chain += [None] + + # Deformation bone + if self.make_deforms: + # Copy + def_bone = copy_bone(self.obj, name) + eb = self.obj.data.edit_bones + def_bone_e = eb[def_bone] + # Name + def_bone_e.name = make_deformer_name(strip_org(name)) + # Parenting + if i == 0: + # First bone + def_bone_e.parent = eb[self.org_bones[0]].parent + else: + # The rest + def_bone_e.parent = eb[def_chain[-1]] + # Add to list + def_chain += [def_bone_e.name] + else: + def_chain += [None] + + bpy.ops.object.mode_set(mode='OBJECT') + pb = self.obj.pose.bones + + # Constraints for org and def + for org, ctrl, defrm in zip(self.org_bones, ctrl_chain, def_chain): + if self.make_controls: + con = pb[org].constraints.new('COPY_TRANSFORMS') + con.name = "copy_transforms" + con.target = self.obj + con.subtarget = ctrl + + if self.make_deforms: + con = pb[defrm].constraints.new('COPY_TRANSFORMS') + con.name = "copy_transforms" + con.target = self.obj + con.subtarget = org + + # Create control widgets + if self.make_controls: + for bone in ctrl_chain: + create_bone_widget(self.obj, bone) + + @classmethod + def add_parameters(self, group): + """ Add the parameters of this rig type to the + RigifyParameters PropertyGroup + """ + group.make_controls = bpy.props.BoolProperty(name="Controls", default=True, description="Create control bones for the copy.") + group.make_deforms = bpy.props.BoolProperty(name="Deform", default=True, description="Create deform bones for the copy.") + + + @classmethod + def parameters_ui(self, layout, obj, bone): + """ Create the ui for the rig parameters. + """ + params = obj.pose.bones[bone].rigify_parameters[0] + + r = layout.row() + r.prop(params, "make_controls") + r = layout.row() + r.prop(params, "make_deforms") + + @classmethod + def create_sample(self, obj): + """ Create a sample metarig for this rig type. + + """ + # generated by rigify.utils.write_metarig + bpy.ops.object.mode_set(mode='EDIT') + arm = obj.data + + bones = {} + + bone = arm.edit_bones.new('bone.01') + bone.head[:] = 0.0000, 0.0000, 0.0000 + bone.tail[:] = 0.0000, 0.0000, 0.3333 + bone.roll = 0.0000 + bone.use_connect = False + bones['bone.01'] = bone.name + bone = arm.edit_bones.new('bone.02') + bone.head[:] = 0.0000, 0.0000, 0.3333 + bone.tail[:] = 0.0000, 0.0000, 0.6667 + bone.roll = 3.1416 + bone.use_connect = True + bone.parent = arm.edit_bones[bones['bone.01']] + bones['bone.02'] = bone.name + bone = arm.edit_bones.new('bone.03') + bone.head[:] = 0.0000, 0.0000, 0.6667 + bone.tail[:] = 0.0000, 0.0000, 1.0000 + bone.roll = 3.1416 + bone.use_connect = True + bone.parent = arm.edit_bones[bones['bone.02']] + bones['bone.03'] = bone.name + + bpy.ops.object.mode_set(mode='OBJECT') + pbone = obj.pose.bones[bones['bone.01']] + pbone.rigify_type = 'basic.copy_chain' + 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.rigify_parameters.add() + pbone = obj.pose.bones[bones['bone.02']] + 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 = obj.pose.bones[bones['bone.03']] + 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' + + bpy.ops.object.mode_set(mode='EDIT') + for bone in arm.edit_bones: + bone.select = False + bone.select_head = False + bone.select_tail = False + for b in bones: + bone = arm.edit_bones[bones[b]] + bone.select = True + bone.select_head = True + bone.select_tail = True + arm.edit_bones.active = bone + + diff --git a/rigify/rigs/biped/__init__.py b/rigify/rigs/biped/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/rigify/rigs/biped/arm/__init__.py b/rigify/rigs/biped/arm/__init__.py new file mode 100644 index 00000000..93e757f4 --- /dev/null +++ b/rigify/rigs/biped/arm/__init__.py @@ -0,0 +1,235 @@ +#====================== 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 ======================== + +import bpy +import imp +from . import fk, ik, deform +from rigify.utils import MetarigError, get_layers + +imp.reload(fk) +imp.reload(ik) +imp.reload(deform) + +script = """ +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): + p = layout.operator("pose.rigify_arm_fk2ik_" + rig_id, text="Snap FK->IK (" + fk_arm[0] + ")") + p.uarm_fk = fk_arm[0] + p.farm_fk = fk_arm[1] + p.hand_fk = fk_arm[2] + p.uarm_ik = ik_arm[0] + p.farm_ik = ik_arm[1] + p.hand_ik = ik_arm[2] + p = layout.operator("pose.rigify_arm_ik2fk_" + rig_id, text="Snap IK->FK (" + fk_arm[0] + ")") + p.uarm_fk = fk_arm[0] + p.farm_fk = fk_arm[1] + p.hand_fk = fk_arm[2] + p.uarm_ik = ik_arm[0] + p.farm_ik = ik_arm[1] + p.hand_ik = ik_arm[2] + p.pole = ik_arm[3] + +""" + + +class Rig: + """ An arm rig, with IK/FK switching and hinge switch. + + """ + 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 names of bones that will be needed. + Do NOT change any data in the scene. This is a gathering phase only. + + """ + # Gather deform rig + self.deform_rig = deform.Rig(obj, bone, params) + + # Gather FK rig + self.fk_rig = fk.Rig(obj, bone, params) + + # Gather IK rig + self.ik_rig = ik.Rig(obj, bone, params, ikfk_switch=True) + + 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. + + """ + 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])] + + @classmethod + def add_parameters(self, group): + """ Add the parameters of this rig type to the + RigifyParameters PropertyGroup + + """ + 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.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.ik_layers = bpy.props.BoolVectorProperty(size=32, description="Layers for the ik controls to be on.") + + group.use_upper_arm_twist = bpy.props.BoolProperty(name="Upper Arm Twist", default=True, description="Generate the dual-bone twist setup for the upper arm.") + group.use_forearm_twist = bpy.props.BoolProperty(name="Forearm Twist", default=True, description="Generate the dual-bone twist setup for the forearm.") + + @classmethod + def parameters_ui(self, layout, obj, bone): + """ Create the ui for the rig parameters. + + """ + params = obj.pose.bones[bone].rigify_parameters[0] + + r = layout.row() + r.prop(params, "separate_ik_layers") + + r = layout.row() + r.active = params.separate_ik_layers + + col = r.column(align=True) + row = col.row(align=True) + row.prop(params, "ik_layers", index=0, toggle=True, text="") + row.prop(params, "ik_layers", index=1, toggle=True, text="") + row.prop(params, "ik_layers", index=2, toggle=True, text="") + row.prop(params, "ik_layers", index=3, toggle=True, text="") + row.prop(params, "ik_layers", index=4, toggle=True, text="") + row.prop(params, "ik_layers", index=5, toggle=True, text="") + row.prop(params, "ik_layers", index=6, toggle=True, text="") + row.prop(params, "ik_layers", index=7, toggle=True, text="") + row = col.row(align=True) + row.prop(params, "ik_layers", index=16, toggle=True, text="") + row.prop(params, "ik_layers", index=17, toggle=True, text="") + row.prop(params, "ik_layers", index=18, toggle=True, text="") + row.prop(params, "ik_layers", index=19, toggle=True, text="") + row.prop(params, "ik_layers", index=20, toggle=True, text="") + row.prop(params, "ik_layers", index=21, toggle=True, text="") + row.prop(params, "ik_layers", index=22, toggle=True, text="") + row.prop(params, "ik_layers", index=23, toggle=True, text="") + + col = r.column(align=True) + row = col.row(align=True) + row.prop(params, "ik_layers", index=8, toggle=True, text="") + row.prop(params, "ik_layers", index=9, toggle=True, text="") + row.prop(params, "ik_layers", index=10, toggle=True, text="") + row.prop(params, "ik_layers", index=11, toggle=True, text="") + row.prop(params, "ik_layers", index=12, toggle=True, text="") + row.prop(params, "ik_layers", index=13, toggle=True, text="") + row.prop(params, "ik_layers", index=14, toggle=True, text="") + row.prop(params, "ik_layers", index=15, toggle=True, text="") + row = col.row(align=True) + row.prop(params, "ik_layers", index=24, toggle=True, text="") + row.prop(params, "ik_layers", index=25, toggle=True, text="") + row.prop(params, "ik_layers", index=26, toggle=True, text="") + row.prop(params, "ik_layers", index=27, toggle=True, text="") + row.prop(params, "ik_layers", index=28, toggle=True, text="") + row.prop(params, "ik_layers", index=29, toggle=True, text="") + 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") + + @classmethod + def create_sample(self, obj): + # generated by rigify.utils.write_meta_rig + bpy.ops.object.mode_set(mode='EDIT') + arm = obj.data + + bones = {} + + bone = arm.edit_bones.new('upper_arm') + bone.head[:] = 0.0000, 0.0000, 0.0000 + bone.tail[:] = 0.3000, 0.0300, 0.0000 + bone.roll = 1.5708 + bone.use_connect = False + bones['upper_arm'] = bone.name + bone = arm.edit_bones.new('forearm') + bone.head[:] = 0.3000, 0.0300, 0.0000 + bone.tail[:] = 0.6000, 0.0000, 0.0000 + bone.roll = 1.5708 + bone.use_connect = True + bone.parent = arm.edit_bones[bones['upper_arm']] + bones['forearm'] = bone.name + bone = arm.edit_bones.new('hand') + bone.head[:] = 0.6000, 0.0000, 0.0000 + bone.tail[:] = 0.7000, 0.0000, 0.0000 + bone.roll = 3.1416 + bone.use_connect = True + bone.parent = arm.edit_bones[bones['forearm']] + bones['hand'] = bone.name + + bpy.ops.object.mode_set(mode='OBJECT') + pbone = obj.pose.bones[bones['upper_arm']] + pbone.rigify_type = 'biped.arm' + pbone.lock_location = (True, True, True) + pbone.lock_rotation = (False, False, False) + pbone.lock_rotation_w = False + pbone.lock_scale = (False, False, False) + pbone.rotation_mode = 'QUATERNION' + pbone.rigify_parameters.add() + pbone = obj.pose.bones[bones['forearm']] + 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 = obj.pose.bones[bones['hand']] + 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' + + bpy.ops.object.mode_set(mode='EDIT') + for bone in arm.edit_bones: + bone.select = False + bone.select_head = False + bone.select_tail = False + for b in bones: + bone = arm.edit_bones[bones[b]] + bone.select = True + bone.select_head = True + bone.select_tail = True + arm.edit_bones.active = bone + diff --git a/rigify/rigs/biped/arm/deform.py b/rigify/rigs/biped/arm/deform.py new file mode 100644 index 00000000..2a7b3109 --- /dev/null +++ b/rigify/rigs/biped/arm/deform.py @@ -0,0 +1,230 @@ +#====================== 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 ======================== + +import bpy +from math import acos, degrees +from mathutils import Vector, Matrix +from rigify.utils import MetarigError +from rigify.utils import copy_bone, flip_bone, put_bone +from rigify.utils import connected_children_names +from rigify.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 = x1 * rot_mat + 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 = bone1_e.x_axis * rot_mat + check = x2 * x3 + + # If not, reverse + if check < 0.9999: + bone1_e.roll = -roll + + +class Rig: + """ An FK arm rig, with hinge switch. + + """ + 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 + + # 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 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"))) + else: + uarm = copy_bone(self.obj, self.org_bones[0], make_deformer_name(strip_org(self.org_bones[0]))) + + # 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] + + 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] + + # 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 + diff --git a/rigify/rigs/biped/arm/fk.py b/rigify/rigs/biped/arm/fk.py new file mode 100644 index 00000000..2c634b86 --- /dev/null +++ b/rigify/rigs/biped/arm/fk.py @@ -0,0 +1,217 @@ +#====================== 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 ======================== + +import bpy +import math +from rigify.utils import MetarigError +from rigify.utils import copy_bone, flip_bone, put_bone +from rigify.utils import connected_children_names +from rigify.utils import strip_org, make_mechanism_name, make_deformer_name +from rigify.utils import get_layers +from rigify.utils import create_widget, create_limb_widget +from rna_prop_ui import rna_idprop_ui_prop_get + + +class Rig: + """ An FK arm rig, with hinge switch. + + """ + 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 + + # 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 + + # Get the rig parameters + if "layers" in params: + self.layers = get_layers(params["layers"]) + else: + self.layers = None + + self.primary_rotation_axis = params.primary_rotation_axis + + 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 the control bones + uarm = copy_bone(self.obj, self.org_bones[0], strip_org(self.org_bones[0])) + farm = copy_bone(self.obj, self.org_bones[1], strip_org(self.org_bones[1])) + hand = copy_bone(self.obj, self.org_bones[2], 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(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] + 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) + + 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)] + 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 [uarm, farm, hand] + diff --git a/rigify/rigs/biped/arm/ik.py b/rigify/rigs/biped/arm/ik.py new file mode 100644 index 00000000..0ecf70e7 --- /dev/null +++ b/rigify/rigs/biped/arm/ik.py @@ -0,0 +1,339 @@ +#====================== 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 ======================== + +import bpy +from mathutils import Vector +from math import pi, acos +from rigify.utils import MetarigError +from rigify.utils import copy_bone +from rigify.utils import connected_children_names +from rigify.utils import strip_org, make_mechanism_name, insert_before_lr +from rigify.utils import get_layers +from rigify.utils import create_widget, create_line_widget, create_sphere_widget +from rna_prop_ui import rna_idprop_ui_prop_get + + +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 + + +class Rig: + """ An IK arm rig, with an optional ik/fk switch. + + """ + def __init__(self, obj, bone, params, ikfk_switch=False): + """ 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. + + ikfk_switch: if True, create an ik/fk switch slider + """ + 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) + else: + self.layers = None + + self.bend_hint = params.bend_hint + + self.primary_rotation_axis = params.primary_rotation_axis + + 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 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 = copy_bone(self.obj, self.org_bones[0], strip_org(insert_before_lr(self.org_bones[0], "_pole"))) + + 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"))) + + # 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] + 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 == 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 == 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 == 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 == 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) + + 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)] + 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 [uarm, farm, hand, pole] + diff --git a/rigify/rigs/biped/leg/__init__.py b/rigify/rigs/biped/leg/__init__.py new file mode 100644 index 00000000..8f9286c7 --- /dev/null +++ b/rigify/rigs/biped/leg/__init__.py @@ -0,0 +1,272 @@ +#====================== 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 ======================== + +import bpy +import imp +from . import fk, ik, deform +from rigify.utils import MetarigError, get_layers + +imp.reload(fk) +imp.reload(ik) +imp.reload(deform) + +script = """ +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] + p.foot_fk = fk_leg[2] + p.mfoot_fk = fk_leg[3] + p.thigh_ik = ik_leg[0] + p.shin_ik = ik_leg[1] + 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] + p.shin_fk = fk_leg[1] + 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.pole = ik_leg[3] + p.footroll = ik_leg[4] + p.mfoot_ik = ik_leg[5] +""" + + +class Rig: + """ A leg rig, with IK/FK switching, a hinge switch, and foot roll. + + """ + 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 names of bones that will be needed. + Do NOT change any data in the scene. This is a gathering phase only. + + """ + # Gather deform rig + self.deform_rig = deform.Rig(obj, bone, params) + + # Gather FK rig + self.fk_rig = fk.Rig(obj, bone, params) + + # Gather IK rig + self.ik_rig = ik.Rig(obj, bone, params, ikfk_switch=True) + + 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. + + """ + 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])] + + @classmethod + def add_parameters(self, group): + """ Add the parameters of this rig type to the + RigifyParameters PropertyGroup + + """ + 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.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.ik_layers = bpy.props.BoolVectorProperty(size=32, description="Layers for the ik controls to be on.") + + group.use_thigh_twist = bpy.props.BoolProperty(name="Thigh Twist", default=True, description="Generate the dual-bone twist setup for the thigh.") + group.use_shin_twist = bpy.props.BoolProperty(name="Shin Twist", default=True, description="Generate the dual-bone twist setup for the shin.") + + @classmethod + def parameters_ui(self, layout, obj, bone): + """ Create the ui for the rig parameters. + + """ + params = obj.pose.bones[bone].rigify_parameters[0] + + r = layout.row() + r.prop(params, "separate_ik_layers") + + r = layout.row() + r.active = params.separate_ik_layers + + col = r.column(align=True) + row = col.row(align=True) + row.prop(params, "ik_layers", index=0, toggle=True, text="") + row.prop(params, "ik_layers", index=1, toggle=True, text="") + row.prop(params, "ik_layers", index=2, toggle=True, text="") + row.prop(params, "ik_layers", index=3, toggle=True, text="") + row.prop(params, "ik_layers", index=4, toggle=True, text="") + row.prop(params, "ik_layers", index=5, toggle=True, text="") + row.prop(params, "ik_layers", index=6, toggle=True, text="") + row.prop(params, "ik_layers", index=7, toggle=True, text="") + row = col.row(align=True) + row.prop(params, "ik_layers", index=16, toggle=True, text="") + row.prop(params, "ik_layers", index=17, toggle=True, text="") + row.prop(params, "ik_layers", index=18, toggle=True, text="") + row.prop(params, "ik_layers", index=19, toggle=True, text="") + row.prop(params, "ik_layers", index=20, toggle=True, text="") + row.prop(params, "ik_layers", index=21, toggle=True, text="") + row.prop(params, "ik_layers", index=22, toggle=True, text="") + row.prop(params, "ik_layers", index=23, toggle=True, text="") + + col = r.column(align=True) + row = col.row(align=True) + row.prop(params, "ik_layers", index=8, toggle=True, text="") + row.prop(params, "ik_layers", index=9, toggle=True, text="") + row.prop(params, "ik_layers", index=10, toggle=True, text="") + row.prop(params, "ik_layers", index=11, toggle=True, text="") + row.prop(params, "ik_layers", index=12, toggle=True, text="") + row.prop(params, "ik_layers", index=13, toggle=True, text="") + row.prop(params, "ik_layers", index=14, toggle=True, text="") + row.prop(params, "ik_layers", index=15, toggle=True, text="") + row = col.row(align=True) + row.prop(params, "ik_layers", index=24, toggle=True, text="") + row.prop(params, "ik_layers", index=25, toggle=True, text="") + row.prop(params, "ik_layers", index=26, toggle=True, text="") + row.prop(params, "ik_layers", index=27, toggle=True, text="") + row.prop(params, "ik_layers", index=28, toggle=True, text="") + row.prop(params, "ik_layers", index=29, toggle=True, text="") + 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") + + @classmethod + def create_sample(self, obj): + # generated by rigify.utils.write_meta_rig + bpy.ops.object.mode_set(mode='EDIT') + arm = obj.data + + bones = {} + + bone = arm.edit_bones.new('thigh') + bone.head[:] = -0.0000, 0.0000, 1.0000 + bone.tail[:] = -0.0000, -0.0500, 0.5000 + bone.roll = -0.0000 + bone.use_connect = False + bones['thigh'] = bone.name + bone = arm.edit_bones.new('shin') + bone.head[:] = -0.0000, -0.0500, 0.5000 + bone.tail[:] = -0.0000, 0.0000, 0.1000 + bone.roll = -0.0000 + bone.use_connect = True + bone.parent = arm.edit_bones[bones['thigh']] + bones['shin'] = bone.name + bone = arm.edit_bones.new('foot') + bone.head[:] = -0.0000, 0.0000, 0.1000 + bone.tail[:] = 0.0000, -0.1200, 0.0300 + bone.roll = 0.0000 + bone.use_connect = True + bone.parent = arm.edit_bones[bones['shin']] + bones['foot'] = bone.name + bone = arm.edit_bones.new('heel') + bone.head[:] = -0.0000, 0.0000, 0.1000 + bone.tail[:] = -0.0000, 0.0600, 0.0000 + bone.roll = -0.0000 + bone.use_connect = True + bone.parent = arm.edit_bones[bones['shin']] + bones['heel'] = bone.name + bone = arm.edit_bones.new('heel.02') + bone.head[:] = -0.0500, -0.0200, 0.0000 + bone.tail[:] = 0.0500, -0.0200, 0.0000 + bone.roll = 0.0000 + bone.use_connect = False + bone.parent = arm.edit_bones[bones['heel']] + bones['heel.02'] = bone.name + bone = arm.edit_bones.new('toe') + bone.head[:] = 0.0000, -0.1200, 0.0300 + bone.tail[:] = 0.0000, -0.2000, 0.0300 + bone.roll = 3.1416 + bone.use_connect = True + bone.parent = arm.edit_bones[bones['foot']] + bones['toe'] = bone.name + + bpy.ops.object.mode_set(mode='OBJECT') + pbone = obj.pose.bones[bones['thigh']] + pbone.rigify_type = 'biped.leg' + pbone.lock_location = (True, True, True) + pbone.lock_rotation = (False, False, False) + pbone.lock_rotation_w = False + pbone.lock_scale = (False, False, False) + pbone.rotation_mode = 'QUATERNION' + pbone.rigify_parameters.add() + pbone = obj.pose.bones[bones['shin']] + 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 = obj.pose.bones[bones['foot']] + 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 = obj.pose.bones[bones['heel']] + 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 = obj.pose.bones[bones['toe']] + 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' + + bpy.ops.object.mode_set(mode='EDIT') + for bone in arm.edit_bones: + bone.select = False + bone.select_head = False + bone.select_tail = False + for b in bones: + bone = arm.edit_bones[bones[b]] + bone.select = True + bone.select_head = True + bone.select_tail = True + arm.edit_bones.active = bone + diff --git a/rigify/rigs/biped/leg/deform.py b/rigify/rigs/biped/leg/deform.py new file mode 100644 index 00000000..bb6b6f39 --- /dev/null +++ b/rigify/rigs/biped/leg/deform.py @@ -0,0 +1,263 @@ +#====================== 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 ======================== + +import bpy +from math import acos, degrees +from mathutils import Vector, Matrix +from rigify.utils import MetarigError +from rigify.utils import copy_bone, flip_bone, put_bone +from rigify.utils import connected_children_names, has_connected_children +from rigify.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 = x1 * rot_mat + 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 = bone1_e.x_axis * rot_mat + check = x2 * x3 + + # If not, reverse + if check < 0.9999: + bone1_e.roll = -roll + + +class Rig: + """ A leg deform-bone setup. + + """ + 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 + + # Get the chain of 2 connected bones + leg_bones = [bone] + connected_children_names(self.obj, bone)[:2] + + if len(leg_bones) != 2: + raise MetarigError("RIGIFY ERROR: Bone '%s': incorrect bone configuration for rig type." % (strip_org(bone))) + + # Get the foot and heel + foot = None + heel = None + for b in self.obj.data.bones[leg_bones[1]].children: + if b.use_connect == True: + if len(b.children) >= 1 and has_connected_children(b): + foot = b.name + else: + heel = b.name + + if foot is None or heel is None: + raise MetarigError("RIGIFY ERROR: Bone '%s': incorrect bone configuration for rig type." % (strip_org(bone))) + + # Get the toe + toe = None + for b in self.obj.data.bones[foot].children: + if b.use_connect == True: + toe = b.name + + if toe is None: + raise MetarigError("RIGIFY ERROR: Bone '%s': incorrect bone configuration for rig type." % (strip_org(bone))) + + 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"))) + else: + thigh = copy_bone(self.obj, self.org_bones[0], make_deformer_name(strip_org(self.org_bones[0]))) + + # 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]))) + + # Create foot bone + foot = copy_bone(self.obj, self.org_bones[2], make_deformer_name(strip_org(self.org_bones[2]))) + + # Create toe bone + 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 + + 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] + + # 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 diff --git a/rigify/rigs/biped/leg/fk.py b/rigify/rigs/biped/leg/fk.py new file mode 100644 index 00000000..f2ce6653 --- /dev/null +++ b/rigify/rigs/biped/leg/fk.py @@ -0,0 +1,255 @@ +#====================== 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 ======================== + +import bpy +import math +from mathutils import Vector +from rigify.utils import MetarigError +from rigify.utils import copy_bone, flip_bone, put_bone +from rigify.utils import connected_children_names, has_connected_children +from rigify.utils import strip_org, make_mechanism_name, make_deformer_name +from rigify.utils import get_layers +from rigify.utils import create_widget, create_limb_widget +from rna_prop_ui import rna_idprop_ui_prop_get + + +class Rig: + """ An FK leg rig, with hinge switch. + + """ + 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 + + # Get the chain of 2 connected bones + leg_bones = [bone] + connected_children_names(self.obj, bone)[:2] + + if len(leg_bones) != 2: + raise MetarigError("RIGIFY ERROR: Bone '%s': incorrect bone configuration for rig type." % (strip_org(bone))) + + # Get the foot and heel + foot = None + heel = None + for b in self.obj.data.bones[leg_bones[1]].children: + if b.use_connect == True: + if len(b.children) >= 1 and has_connected_children(b): + foot = b.name + else: + heel = b.name + + if foot is None or heel is None: + raise MetarigError("RIGIFY ERROR: Bone '%s': incorrect bone configuration for rig type." % (strip_org(bone))) + + # Get the toe + toe = None + for b in self.obj.data.bones[foot].children: + if b.use_connect == True: + toe = b.name + + # Get the toe + if toe is None: + raise MetarigError("RIGIFY ERROR: Bone '%s': incorrect bone configuration for rig type." % (strip_org(bone))) + + self.org_bones = leg_bones + [foot, toe, heel] + + # 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 + + # Get rig parameters + if "layers" in params: + self.layers = get_layers(params["layers"]) + else: + self.layers = None + + self.primary_rotation_axis = params.primary_rotation_axis + + 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 the control bones + thigh = copy_bone(self.obj, self.org_bones[0], strip_org(self.org_bones[0])) + shin = copy_bone(self.obj, self.org_bones[1], strip_org(self.org_bones[1])) + foot = copy_bone(self.obj, self.org_bones[2], strip_org(self.org_bones[2])) + + # 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 + 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] + 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) + + 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)] + 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 [thigh, shin, foot, foot_mch] + diff --git a/rigify/rigs/biped/leg/ik.py b/rigify/rigs/biped/leg/ik.py new file mode 100644 index 00000000..54869b8e --- /dev/null +++ b/rigify/rigs/biped/leg/ik.py @@ -0,0 +1,608 @@ +#====================== 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 ======================== + +import bpy +from mathutils import Vector +from math import pi, acos +from rigify.utils import MetarigError +from rigify.utils import copy_bone, flip_bone, put_bone +from rigify.utils import connected_children_names, has_connected_children +from rigify.utils import strip_org, make_mechanism_name, insert_before_lr +from rigify.utils import get_layers +from rigify.utils import create_widget, create_line_widget, create_sphere_widget, create_circle_widget +from rna_prop_ui import rna_idprop_ui_prop_get + + +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 + + +class Rig: + """ An IK leg rig, with an optional ik/fk switch. + + """ + def __init__(self, obj, bone, params, ikfk_switch=False): + """ 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 + self.switch = ikfk_switch + + # Get the chain of 2 connected bones + leg_bones = [bone] + connected_children_names(self.obj, bone)[:2] + + if len(leg_bones) != 2: + raise MetarigError("RIGIFY ERROR: Bone '%s': incorrect bone configuration for rig type." % (strip_org(bone))) + + # Get the foot and heel + foot = None + heel = None + rocker = None + for b in self.obj.data.bones[leg_bones[1]].children: + if b.use_connect == True: + if len(b.children) >= 1 and has_connected_children(b): + foot = b.name + else: + heel = b.name + if len(b.children) > 0: + rocker = b.children[0].name + + if foot is None or heel is None: + print("blah") + raise MetarigError("RIGIFY ERROR: Bone '%s': incorrect bone configuration for rig type." % (strip_org(bone))) + + # Get the toe + toe = None + for b in self.obj.data.bones[foot].children: + if b.use_connect == True: + toe = b.name + + # Get toe + if toe is None: + raise MetarigError("RIGIFY ERROR: Bone '%s': incorrect bone configuration for rig type." % (strip_org(bone))) + + self.org_bones = leg_bones + [foot, toe, heel, rocker] + + # Get rig parameters + if params.separate_ik_layers: + self.layers = list(params.ik_layers) + else: + self.layers = None + + self.bend_hint = params.bend_hint + + self.primary_rotation_axis = params.primary_rotation_axis + + 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') + + 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 = copy_bone(self.obj, self.org_bones[0], strip_org(insert_before_lr(self.org_bones[0], "_pole"))) + + 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"))) + toe_parent_socket2 = copy_bone(self.obj, self.org_bones[2], make_mechanism_name(strip_org(self.org_bones[3] + ".socket2"))) + + foot_roll = copy_bone(self.obj, self.org_bones[4], strip_org(insert_before_lr(self.org_bones[2], "_roll"))) + roll1 = copy_bone(self.obj, self.org_bones[4], make_mechanism_name(strip_org(self.org_bones[2] + ".roll.01"))) + roll2 = copy_bone(self.obj, self.org_bones[4], make_mechanism_name(strip_org(self.org_bones[2] + ".roll.02"))) + + if make_rocker: + 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"))) + + # 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] + toe_e = eb[toe] + toe_parent_e = eb[toe_parent] + toe_parent_socket1_e = eb[toe_parent_socket1] + toe_parent_socket2_e = eb[toe_parent_socket2] + foot_roll_e = eb[foot_roll] + roll1_e = eb[roll1] + roll2_e = eb[roll2] + 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 + toe_parent_socket1_e.use_connect = False + toe_parent_socket1_e.parent = roll1_e + toe_parent_socket2_e.use_connect = False + toe_parent_socket2_e.parent = eb[self.org_bones[2]] + + foot_roll_e.use_connect = False + foot_roll_e.parent = foot_e + + roll1_e.use_connect = False + roll1_e.parent = foot_e + + 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 + + roll1_e.parent = rocker2_e + 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) + toe_parent_socket2_e.head = Vector(org_foot_e.tail) + toe_parent_socket1_e.tail = Vector(org_foot_e.tail) + (Vector((0, 0, 1)) * foot_e.length / 2) + toe_parent_socket2_e.tail = Vector(org_foot_e.tail) + (Vector((0, 0, 1)) * foot_e.length / 3) + toe_parent_socket2_e.roll = toe_parent_socket1_e.roll + + tail = Vector(roll1_e.tail) + roll1_e.tail = Vector(org_foot_e.tail) + roll1_e.tail = Vector(org_foot_e.tail) + roll1_e.head = tail + roll2_e.head = Vector(org_foot_e.tail) + foot_roll_e.head = Vector(org_foot_e.tail) + put_bone(self.obj, foot_roll, roll1_e.head) + 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) + 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: + flip_bone(self.obj, rocker2) + 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] + 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] + if make_rocker: + rocker1_p = pb[rocker1] + rocker2_p = pb[rocker2] + toe_p = pb[toe] + 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' + if make_rocker: + foot_roll_p.lock_rotation = False, False, True + else: + foot_roll_p.lock_rotation = False, True, True + foot_roll_p.lock_location = True, True, True + foot_roll_p.lock_scale = True, True, True + + # roll and rocker bones set to euler rotation + roll1_p.rotation_mode = 'XYZ' + roll2_p.rotation_mode = 'XYZ' + if make_rocker: + 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 == 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" + con.target = self.obj + con.subtarget = toe_parent_socket2 + + con = toe_parent_socket1_p.constraints.new('COPY_SCALE') + con.name = "copy_scale" + con.target = self.obj + con.subtarget = toe_parent_socket2 + + con = toe_parent_socket1_p.constraints.new('COPY_TRANSFORMS') # drive with IK switch + con.name = "fk" + con.target = self.obj + con.subtarget = toe_parent_socket2 + + 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"]' + mod = fcurve.modifiers[0] + mod.poly_order = 1 + mod.coefficients[0] = 1.0 + mod.coefficients[1] = -1.0 + + # Foot roll drivers + fcurve = roll1_p.driver_add("rotation_euler", 0) + driver = fcurve.driver + var = driver.variables.new() + driver.type = 'SCRIPTED' + driver.expression = "min(0,var)" + var.name = "var" + var.targets[0].id_type = 'OBJECT' + var.targets[0].id = self.obj + var.targets[0].data_path = foot_roll_p.path_from_id() + '.rotation_euler[0]' + + fcurve = roll2_p.driver_add("rotation_euler", 0) + driver = fcurve.driver + var = driver.variables.new() + driver.type = 'SCRIPTED' + driver.expression = "max(0,var)" + var.name = "var" + var.targets[0].id_type = 'OBJECT' + var.targets[0].id = self.obj + var.targets[0].data_path = foot_roll_p.path_from_id() + '.rotation_euler[0]' + + if make_rocker: + fcurve = rocker1_p.driver_add("rotation_euler", 0) + driver = fcurve.driver + var = driver.variables.new() + driver.type = 'SCRIPTED' + driver.expression = "max(0,-var)" + var.name = "var" + var.targets[0].id_type = 'OBJECT' + var.targets[0].id = self.obj + var.targets[0].data_path = foot_roll_p.path_from_id() + '.rotation_euler[1]' + + fcurve = rocker2_p.driver_add("rotation_euler", 0) + driver = fcurve.driver + var = driver.variables.new() + driver.type = 'SCRIPTED' + driver.expression = "max(0,var)" + var.name = "var" + var.targets[0].id_type = 'OBJECT' + 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 == 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 == 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 == 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[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) + 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 + + ob = create_widget(self.obj, foot_roll) + 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)] + mesh = ob.data + mesh.from_pydata(verts, edges, []) + mesh.update() + + mod = ob.modifiers.new("subsurf", 'SUBSURF') + mod.levels = 2 + + return [thigh, shin, foot, pole, foot_roll, foot_ik_target] + diff --git a/rigify/rigs/finger.py b/rigify/rigs/finger.py new file mode 100644 index 00000000..0bcea44b --- /dev/null +++ b/rigify/rigs/finger.py @@ -0,0 +1,412 @@ +#====================== 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 ======================== + +import bpy +from mathutils import Vector +from rigify.utils import MetarigError +from rigify.utils import copy_bone +from rigify.utils import connected_children_names +from rigify.utils import strip_org, make_mechanism_name, make_deformer_name +from rigify.utils import get_layers +from rigify.utils import create_widget, create_line_widget, create_limb_widget +from rna_prop_ui import rna_idprop_ui_prop_get +import re + + +class Rig: + """ A finger rig. It takes a single chain of bones. + This is a control and deformation rig. + """ + def __init__(self, obj, bone, params): + """ Gather and validate data about the rig. + """ + self.obj = obj + self.org_bones = [bone] + connected_children_names(obj, bone) + self.params = params + + if len(self.org_bones) <= 1: + raise MetarigError("RIGIFY ERROR: Bone '%s': input to rig type must be a chain of 2 or more bones." % (strip_org(bone))) + + # Get user-specified layers, if they exist + if params.separate_extra_layers: + self.ex_layers = list(params.extra_layers) + else: + self.ex_layers = None + + # Get other rig parameters + self.primary_rotation_axis = params.primary_rotation_axis + self.use_digit_twist = params.use_digit_twist + + def deform(self): + """ Generate the deformation rig. + Just a copy of the original bones, except the first digit which is a twist bone. + """ + bpy.ops.object.mode_set(mode='EDIT') + + # Create the bones + # First bone is a twist bone + if self.use_digit_twist: + b1a = copy_bone(self.obj, self.org_bones[0], make_deformer_name(strip_org(self.org_bones[0] + ".01"))) + b1b = copy_bone(self.obj, self.org_bones[0], make_deformer_name(strip_org(self.org_bones[0] + ".02"))) + b1tip = copy_bone(self.obj, self.org_bones[0], make_mechanism_name(strip_org(self.org_bones[0] + ".tip"))) + else: + b1 = copy_bone(self.obj, self.org_bones[0], make_deformer_name(strip_org(self.org_bones[0]))) + + # The rest are normal + bones = [] + for bone in self.org_bones[1:]: + bones += [copy_bone(self.obj, bone, make_deformer_name(strip_org(bone)))] + + # Position bones + eb = self.obj.data.edit_bones + if self.use_digit_twist: + b1a_e = eb[b1a] + b1b_e = eb[b1b] + b1tip_e = eb[b1tip] + + b1tip_e.use_connect = False + b1tip_e.tail += Vector((0.1, 0, 0)) + b1tip_e.head = b1b_e.tail + b1tip_e.length = b1a_e.length / 4 + + center = (b1a_e.head + b1a_e.tail) / 2 + b1a_e.tail = center + b1b_e.use_connect = False + b1b_e.head = center + + # Parenting + if self.use_digit_twist: + b1b_e.parent = eb[self.org_bones[0]] + b1tip_e.parent = eb[self.org_bones[0]] + else: + eb[b1].use_connect = False + eb[b1].parent = eb[self.org_bones[0]] + + for (ba, bb) in zip(bones, self.org_bones[1:]): + eb[ba].use_connect = False + eb[ba].parent = eb[bb] + + # Constraints + if self.use_digit_twist: + bpy.ops.object.mode_set(mode='OBJECT') + pb = self.obj.pose.bones + + b1a_p = pb[b1a] + + con = b1a_p.constraints.new('COPY_LOCATION') + con.name = "copy_location" + con.target = self.obj + con.subtarget = self.org_bones[0] + + con = b1a_p.constraints.new('COPY_SCALE') + con.name = "copy_scale" + con.target = self.obj + con.subtarget = self.org_bones[0] + + con = b1a_p.constraints.new('DAMPED_TRACK') + con.name = "track_to" + con.target = self.obj + con.subtarget = b1tip + + def control(self): + """ Generate the control rig. + """ + bpy.ops.object.mode_set(mode='EDIT') + + # Figure out the name for the control bone (remove the last .##) + ctrl_name = re.sub("([0-9]+\.)", "", strip_org(self.org_bones[0])[::-1], count=1)[::-1] + + # Create the bones + ctrl = copy_bone(self.obj, self.org_bones[0], ctrl_name) + + helpers = [] + bones = [] + for bone in self.org_bones: + bones += [copy_bone(self.obj, bone, strip_org(bone))] + helpers += [copy_bone(self.obj, bone, make_mechanism_name(strip_org(bone)))] + + # Position bones + eb = self.obj.data.edit_bones + + length = 0.0 + for bone in helpers: + length += eb[bone].length + eb[bone].length /= 2 + + eb[ctrl].length = length * 1.5 + + # Parent bones + prev = eb[self.org_bones[0]].parent + for (b, h) in zip(bones, helpers): + b_e = eb[b] + h_e = eb[h] + b_e.use_connect = False + h_e.use_connect = False + + b_e.parent = h_e + h_e.parent = prev + + prev = b_e + + # Transform locks and rotation mode + bpy.ops.object.mode_set(mode='OBJECT') + pb = self.obj.pose.bones + + for bone in bones[1:]: + pb[bone].lock_location = True, True, True + + if pb[self.org_bones[0]].bone.use_connect == True: + pb[bones[0]].lock_location = True, True, True + + pb[ctrl].lock_scale = True, False, True + + for bone in helpers: + pb[bone].rotation_mode = 'XYZ' + + # Drivers + i = 1 + val = 1.2 / (len(self.org_bones) - 1) + for bone in helpers: + # Add custom prop + prop_name = "bend_%02d" % i + prop = rna_idprop_ui_prop_get(pb[ctrl], prop_name, create=True) + prop["min"] = 0.0 + prop["max"] = 1.0 + prop["soft_min"] = 0.0 + prop["soft_max"] = 1.0 + if i == 1: + pb[ctrl][prop_name] = 0.0 + else: + pb[ctrl][prop_name] = val + + # Add driver + if 'X' in self.primary_rotation_axis: + fcurve = pb[bone].driver_add("rotation_euler", 0) + elif 'Y' in self.primary_rotation_axis: + fcurve = pb[bone].driver_add("rotation_euler", 1) + else: + fcurve = pb[bone].driver_add("rotation_euler", 2) + + driver = fcurve.driver + driver.type = 'SCRIPTED' + + var = driver.variables.new() + var.name = "ctrl_y" + var.targets[0].id_type = 'OBJECT' + var.targets[0].id = self.obj + var.targets[0].data_path = pb[ctrl].path_from_id() + '.scale[1]' + + var = driver.variables.new() + var.name = "bend" + var.targets[0].id_type = 'OBJECT' + var.targets[0].id = self.obj + var.targets[0].data_path = pb[ctrl].path_from_id() + '["' + prop_name + '"]' + + if '-' in self.primary_rotation_axis: + driver.expression = "-(1.0-ctrl_y) * bend * 3.14159 * 2" + else: + driver.expression = "(1.0-ctrl_y) * bend * 3.14159 * 2" + + i += 1 + + # Constraints + con = pb[helpers[0]].constraints.new('COPY_LOCATION') + con.name = "copy_location" + con.target = self.obj + con.subtarget = ctrl + + con = pb[helpers[0]].constraints.new('COPY_ROTATION') + con.name = "copy_rotation" + con.target = self.obj + con.subtarget = ctrl + + # Constrain org bones to the control bones + for (bone, org) in zip(bones, self.org_bones): + con = pb[org].constraints.new('COPY_TRANSFORMS') + con.name = "copy_transforms" + con.target = self.obj + con.subtarget = bone + + # Set layers for extra control bones + if self.ex_layers: + for bone in bones: + pb[bone].bone.layers = self.ex_layers + + # Create control widgets + w = create_widget(self.obj, ctrl) + if w != None: + mesh = w.data + verts = [(0, 0, 0), (0, 1, 0), (0.05, 1, 0), (0.05, 1.1, 0), (-0.05, 1.1, 0), (-0.05, 1, 0)] + if 'Z' in self.primary_rotation_axis: + # Flip x/z coordinates + temp = [] + for v in verts: + temp += [(v[2], v[1], v[0])] + verts = temp + edges = [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 1)] + mesh.from_pydata(verts, edges, []) + mesh.update() + + for bone in bones: + create_limb_widget(self.obj, bone) + + 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. + """ + self.deform() + self.control() + + @classmethod + def add_parameters(self, group): + """ Add the parameters of this rig type to the + RigifyParameters PropertyGroup + """ + 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.separate_extra_layers = bpy.props.BoolProperty(name="Separate Secondary Control Layers:", default=False, description="Enable putting the secondary controls on a separate layer from the primary controls.") + group.extra_layers = bpy.props.BoolVectorProperty(size=32, description="Layers for the secondary controls to be on.") + + group.use_digit_twist = bpy.props.BoolProperty(name="Digit Twist", default=True, description="Generate the dual-bone twist setup for the first finger digit.") + + @classmethod + def parameters_ui(self, layout, obj, bone): + """ Create the ui for the rig parameters. + """ + params = obj.pose.bones[bone].rigify_parameters[0] + + r = layout.row() + r.prop(params, "separate_extra_layers") + + r = layout.row() + r.active = params.separate_extra_layers + + col = r.column(align=True) + row = col.row(align=True) + row.prop(params, "extra_layers", index=0, toggle=True, text="") + row.prop(params, "extra_layers", index=1, toggle=True, text="") + row.prop(params, "extra_layers", index=2, toggle=True, text="") + row.prop(params, "extra_layers", index=3, toggle=True, text="") + row.prop(params, "extra_layers", index=4, toggle=True, text="") + row.prop(params, "extra_layers", index=5, toggle=True, text="") + row.prop(params, "extra_layers", index=6, toggle=True, text="") + row.prop(params, "extra_layers", index=7, toggle=True, text="") + row = col.row(align=True) + row.prop(params, "extra_layers", index=16, toggle=True, text="") + row.prop(params, "extra_layers", index=17, toggle=True, text="") + row.prop(params, "extra_layers", index=18, toggle=True, text="") + row.prop(params, "extra_layers", index=19, toggle=True, text="") + row.prop(params, "extra_layers", index=20, toggle=True, text="") + row.prop(params, "extra_layers", index=21, toggle=True, text="") + row.prop(params, "extra_layers", index=22, toggle=True, text="") + row.prop(params, "extra_layers", index=23, toggle=True, text="") + + col = r.column(align=True) + row = col.row(align=True) + row.prop(params, "ik_layers", index=8, toggle=True, text="") + row.prop(params, "ik_layers", index=9, toggle=True, text="") + row.prop(params, "ik_layers", index=10, toggle=True, text="") + row.prop(params, "ik_layers", index=11, toggle=True, text="") + row.prop(params, "ik_layers", index=12, toggle=True, text="") + row.prop(params, "ik_layers", index=13, toggle=True, text="") + row.prop(params, "ik_layers", index=14, toggle=True, text="") + row.prop(params, "ik_layers", index=15, toggle=True, text="") + row = col.row(align=True) + row.prop(params, "ik_layers", index=24, toggle=True, text="") + row.prop(params, "ik_layers", index=25, toggle=True, text="") + row.prop(params, "ik_layers", index=26, toggle=True, text="") + row.prop(params, "ik_layers", index=27, toggle=True, text="") + row.prop(params, "ik_layers", index=28, toggle=True, text="") + row.prop(params, "ik_layers", index=29, toggle=True, text="") + 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="Bend rotation axis:") + r.prop(params, "primary_rotation_axis", text="") + + col = layout.column() + col.prop(params, "use_digit_twist") + + @classmethod + def create_sample(self, obj): + # generated by rigify.utils.write_metarig + bpy.ops.object.mode_set(mode='EDIT') + arm = obj.data + + bones = {} + + bone = arm.edit_bones.new('finger.01') + bone.head[:] = 0.0000, 0.0000, 0.0000 + bone.tail[:] = 0.2529, 0.0000, 0.0000 + bone.roll = 3.1416 + bone.use_connect = False + bones['finger.01'] = bone.name + bone = arm.edit_bones.new('finger.02') + bone.head[:] = 0.2529, 0.0000, 0.0000 + bone.tail[:] = 0.4024, 0.0000, -0.0264 + bone.roll = -2.9671 + bone.use_connect = True + bone.parent = arm.edit_bones[bones['finger.01']] + bones['finger.02'] = bone.name + bone = arm.edit_bones.new('finger.03') + bone.head[:] = 0.4024, 0.0000, -0.0264 + bone.tail[:] = 0.4975, -0.0000, -0.0610 + bone.roll = -2.7925 + bone.use_connect = True + bone.parent = arm.edit_bones[bones['finger.02']] + bones['finger.03'] = bone.name + + bpy.ops.object.mode_set(mode='OBJECT') + pbone = obj.pose.bones[bones['finger.01']] + pbone.rigify_type = 'finger' + pbone.lock_location = (True, True, True) + pbone.lock_rotation = (False, False, False) + pbone.lock_rotation_w = False + pbone.lock_scale = (False, False, False) + pbone.rotation_mode = 'YZX' + pbone.rigify_parameters.add() + pbone = obj.pose.bones[bones['finger.02']] + 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 = 'YZX' + pbone = obj.pose.bones[bones['finger.03']] + 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 = 'YZX' + + bpy.ops.object.mode_set(mode='EDIT') + for bone in arm.edit_bones: + bone.select = False + bone.select_head = False + bone.select_tail = False + for b in bones: + bone = arm.edit_bones[bones[b]] + bone.select = True + bone.select_head = True + bone.select_tail = True + arm.edit_bones.active = bone + diff --git a/rigify/rigs/misc/__init__.py b/rigify/rigs/misc/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/rigify/rigs/misc/delta.py b/rigify/rigs/misc/delta.py new file mode 100644 index 00000000..dc8edd41 --- /dev/null +++ b/rigify/rigs/misc/delta.py @@ -0,0 +1,161 @@ +#====================== 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 ======================== + +import bpy +from math import acos +from rigify.utils import MetarigError +from rigify.utils import copy_bone +from rigify.utils import org_name, make_mechanism_name + + +class Rig: + """ A delta rig. + Creates a setup that will place its child at its position in pose mode, + but will not modifying its child's position in edit mode. + This is a mechanism-only rig (no control or deformation bones). + + """ + 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. + Do NOT change any data in the scene. This is a gathering phase only. + + """ + bb = obj.data.bones + + if bb[bone].children is None: + raise MetarigError("RIGIFY ERROR: bone '%s': rig type requires one child." % org_name(bone.name)) + if bb[bone].use_connect == True: + raise MetarigError("RIGIFY ERROR: bone '%s': rig type cannot be connected to parent." % org_name(bone.name)) + + self.obj = obj + self.org_bones = {"delta": bone, "child": bb[bone].children[0].name} + self.org_names = [org_name(bone), org_name(bb[bone].children[0].name)] + + 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') + eb = self.obj.data.edit_bones + + org_delta = self.org_bones["delta"] + org_delta_e = eb[self.org_bones["delta"]] + org_child = self.org_bones["child"] + org_child_e = eb[self.org_bones["child"]] + + # Calculate the matrix for achieving the delta + child_mat = org_delta_e.matrix.invert() * org_child_e.matrix + mat = org_delta_e.matrix * child_mat.invert() + + # Create the delta bones. + delta_e = eb[copy_bone(self.obj, self.org_bones["delta"])] + delta_e.name = make_mechanism_name(self.org_names[0]) + delta = delta_e.name + + # Set the delta to the matrix's transforms + set_mat(self.obj, delta, mat) + + bpy.ops.object.mode_set(mode='OBJECT') + + # Constrain org_delta to delta + con = self.obj.pose.bones[org_delta].constraints.new('COPY_TRANSFORMS') + con.name = "delta" + con.target = self.obj + con.subtarget = delta + + @classmethod + def create_sample(self, obj): + # generated by rigify.utils.write_metarig + bpy.ops.object.mode_set(mode='EDIT') + arm = obj.data + + bones = {} + + bone = arm.edit_bones.new('delta') + bone.head[:] = 0.0000, -0.1198, 0.1253 + bone.tail[:] = -0.0000, -0.2483, 0.2785 + bone.roll = -0.0000 + bone.use_connect = False + bones['delta'] = bone.name + bone = arm.edit_bones.new('Bone') + bone.head[:] = -0.0000, 0.0000, 0.0000 + bone.tail[:] = -0.0000, 0.0000, 0.2000 + bone.roll = 0.0000 + bone.use_connect = False + bone.parent = arm.edit_bones[bones['delta']] + bones['Bone'] = bone.name + + bpy.ops.object.mode_set(mode='OBJECT') + pbone = obj.pose.bones[bones['delta']] + pbone.rigify_type = 'misc.delta' + 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.rigify_parameters.add() + pbone = obj.pose.bones[bones['Bone']] + 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' + + bpy.ops.object.mode_set(mode='EDIT') + for bone in arm.edit_bones: + bone.select = False + bone.select_head = False + bone.select_tail = False + for b in bones: + bone = arm.edit_bones[bones[b]] + bone.select = True + bone.select_head = True + 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. + """ + a = obj.data.edit_bones[bone_name] + + a.head = (0, 0, 0) + a.tail = (0, 1, 0) + + a.transform(matrix) + + d = acos(a.matrix.to_quaternion().dot(matrix.to_quaternion())) * 2.0 + + roll_1 = a.roll + d + roll_2 = a.roll - d + + a.roll = roll_1 + d1 = a.matrix.to_quaternion().dot(matrix.to_quaternion()) + a.roll = roll_2 + d2 = a.matrix.to_quaternion().dot(matrix.to_quaternion()) + + if d1 > d2: + a.roll = roll_1 + else: + a.roll = roll_2 + diff --git a/rigify/rigs/neck_short.py b/rigify/rigs/neck_short.py new file mode 100644 index 00000000..58191e6a --- /dev/null +++ b/rigify/rigs/neck_short.py @@ -0,0 +1,392 @@ +#====================== 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 ======================== + +import bpy +from mathutils import Vector +from rigify.utils import MetarigError +from rigify.utils import copy_bone, new_bone, flip_bone, put_bone +from rigify.utils import connected_children_names +from rigify.utils import strip_org, make_mechanism_name, make_deformer_name +from rigify.utils import obj_to_bone, create_circle_widget +from rna_prop_ui import rna_idprop_ui_prop_get + +script1 = """ +head_neck = ["%s", "%s"] +""" + +script2 = """ +if is_selected(head_neck[0]): + layout.prop(pose_bones[head_neck[0]], '["isolate"]', text="Isolate (" + head_neck[0] + ")", slider=True) +""" + +script3 = """ +if is_selected(head_neck): + layout.prop(pose_bones[head_neck[0]], '["neck_follow"]', text="Neck Follow Head (" + head_neck[0] + ")", slider=True) +""" + + +class Rig: + """ A "neck" rig. It turns a chain of bones into a rig with two controls: + One for the head, and one for the neck. + + """ + def __init__(self, obj, bone_name, params): + """ Gather and validate data about the rig. + + """ + self.obj = obj + self.org_bones = [bone_name] + connected_children_names(obj, bone_name) + self.params = params + + if len(self.org_bones) <= 1: + raise MetarigError("RIGIFY ERROR: Bone '%s': input to rig type must be a chain of 2 or more bones." % (strip_org(bone))) + + self.isolate = False + if self.obj.data.bones[bone_name].parent: + self.isolate = True + + def gen_deform(self): + """ Generate the deformation rig. + + """ + for name in self.org_bones: + bpy.ops.object.mode_set(mode='EDIT') + eb = self.obj.data.edit_bones + + # Create deform bone + bone_e = eb[copy_bone(self.obj, name)] + + # Change its name + bone_e.name = make_deformer_name(strip_org(name)) + bone_name = bone_e.name + + # Leave edit mode + bpy.ops.object.mode_set(mode='OBJECT') + + # Get the pose bone + bone = self.obj.pose.bones[bone_name] + + # Constrain to the original bone + con = bone.constraints.new('COPY_TRANSFORMS') + con.name = "copy_transforms" + con.target = self.obj + con.subtarget = name + + def gen_control(self): + """ Generate the control rig. + + """ + #--------------------------------- + # Create the neck and head controls + bpy.ops.object.mode_set(mode='EDIT') + + # Create bones + neck_ctrl = copy_bone(self.obj, self.org_bones[0], strip_org(self.org_bones[0])) + neck_follow = copy_bone(self.obj, self.org_bones[-1], make_mechanism_name(strip_org(self.org_bones[0] + ".follow"))) + neck_child = new_bone(self.obj, make_mechanism_name(strip_org(self.org_bones[0] + ".child"))) + + head_ctrl = copy_bone(self.obj, self.org_bones[-1], strip_org(self.org_bones[-1])) + head_mch = new_bone(self.obj, make_mechanism_name(strip_org(self.org_bones[-1]))) + if self.isolate: + head_socket1 = copy_bone(self.obj, self.org_bones[-1], make_mechanism_name(strip_org(self.org_bones[-1] + ".socket1"))) + head_socket2 = copy_bone(self.obj, self.org_bones[-1], make_mechanism_name(strip_org(self.org_bones[-1] + ".socket2"))) + + # Create neck chain bones + neck = [] + helpers = [] + for name in self.org_bones: + neck += [copy_bone(self.obj, name, make_mechanism_name(strip_org(name)))] + helpers += [copy_bone(self.obj, neck_child, make_mechanism_name(strip_org(name + ".02")))] + + # Fetch edit bones + eb = self.obj.data.edit_bones + + neck_ctrl_e = eb[neck_ctrl] + neck_follow_e = eb[neck_follow] + neck_child_e = eb[neck_child] + head_ctrl_e = eb[head_ctrl] + head_mch_e = eb[head_mch] + if self.isolate: + head_socket1_e = eb[head_socket1] + head_socket2_e = eb[head_socket2] + + # Parenting + head_ctrl_e.use_connect = False + head_ctrl_e.parent = neck_ctrl_e.parent + head_mch_e.use_connect = False + head_mch_e.parent = head_ctrl_e + + if self.isolate: + head_socket1_e.use_connect = False + head_socket1_e.parent = neck_ctrl_e.parent + + head_socket2_e.use_connect = False + head_socket2_e.parent = None + + head_ctrl_e.parent = head_socket2_e + + for (name1, name2) in zip(neck, helpers): + eb[name1].use_connect = False + eb[name1].parent = eb[name2] + eb[name2].use_connect = False + eb[name2].parent = neck_ctrl_e.parent + + neck_follow_e.use_connect = False + neck_follow_e.parent = neck_ctrl_e.parent + neck_child_e.use_connect = False + neck_child_e.parent = neck_ctrl_e + neck_ctrl_e.parent = neck_follow_e + + # Position + put_bone(self.obj, neck_follow, neck_ctrl_e.head) + put_bone(self.obj, neck_child, neck_ctrl_e.head) + put_bone(self.obj, head_ctrl, neck_ctrl_e.head) + put_bone(self.obj, head_mch, neck_ctrl_e.head) + head_mch_e.length = head_ctrl_e.length / 2 + neck_child_e.length = neck_ctrl_e.length / 2 + + if self.isolate: + put_bone(self.obj, head_socket1, neck_ctrl_e.head) + head_mch_e.length /= 2 + + put_bone(self.obj, head_socket2, neck_ctrl_e.head) + head_mch_e.length /= 3 + + for (name1, name2) in zip(neck, helpers): + put_bone(self.obj, name2, eb[name1].head) + eb[name2].length = eb[name1].length / 2 + + # Switch to object mode + bpy.ops.object.mode_set(mode='OBJECT') + pb = self.obj.pose.bones + neck_ctrl_p = pb[neck_ctrl] + neck_follow_p = pb[neck_follow] + neck_child_p = pb[neck_child] + head_ctrl_p = pb[head_ctrl] + if self.isolate: + head_socket1_p = pb[head_socket1] + head_socket2_p = pb[head_socket2] + + # Custom bone appearance + neck_ctrl_p.custom_shape_transform = pb[self.org_bones[(len(self.org_bones) - 1) // 2]] + head_ctrl_p.custom_shape_transform = pb[self.org_bones[-1]] + + # Custom properties + prop = rna_idprop_ui_prop_get(head_ctrl_p, "inf_extent", create=True) + head_ctrl_p["inf_extent"] = 0.5 + prop["min"] = 0.0 + prop["max"] = 1.0 + prop["soft_min"] = 0.0 + prop["soft_max"] = 1.0 + + prop = rna_idprop_ui_prop_get(head_ctrl_p, "neck_follow", create=True) + head_ctrl_p["neck_follow"] = 1.0 + prop["min"] = 0.0 + prop["max"] = 2.0 + prop["soft_min"] = 0.0 + prop["soft_max"] = 1.0 + + if self.isolate: + prop = rna_idprop_ui_prop_get(head_ctrl_p, "isolate", create=True) + head_ctrl_p["isolate"] = 0.0 + prop["min"] = 0.0 + prop["max"] = 1.0 + prop["soft_min"] = 0.0 + prop["soft_max"] = 1.0 + + # Constraints + + # Neck follow + con = neck_follow_p.constraints.new('COPY_ROTATION') + con.name = "copy_rotation" + con.target = self.obj + con.subtarget = head_ctrl + + fcurve = con.driver_add("influence") + driver = fcurve.driver + var = driver.variables.new() + driver.type = 'SCRIPTED' + var.name = "follow" + var.targets[0].id_type = 'OBJECT' + var.targets[0].id = self.obj + var.targets[0].data_path = head_ctrl_p.path_from_id() + '["neck_follow"]' + driver.expression = "follow / 2" + + # Isolate + if self.isolate: + con = head_socket2_p.constraints.new('COPY_LOCATION') + con.name = "copy_location" + con.target = self.obj + con.subtarget = head_socket1 + + con = head_socket2_p.constraints.new('COPY_TRANSFORMS') + con.name = "copy_transforms" + con.target = self.obj + con.subtarget = head_socket1 + + fcurve = con.driver_add("influence") + driver = fcurve.driver + var = driver.variables.new() + driver.type = 'SCRIPTED' + var.name = "isolate" + var.targets[0].id_type = 'OBJECT' + var.targets[0].id = self.obj + var.targets[0].data_path = head_ctrl_p.path_from_id() + '["isolate"]' + driver.expression = "1.0 - isolate" + + # Neck chain + first = True + prev = None + i = 0 + l = len(neck) + for (name1, name2, org_name) in zip(neck, helpers, self.org_bones): + con = pb[org_name].constraints.new('COPY_TRANSFORMS') + con.name = "copy_transforms" + con.target = self.obj + con.subtarget = name1 + + n_con = pb[name2].constraints.new('COPY_TRANSFORMS') + n_con.name = "neck" + n_con.target = self.obj + n_con.subtarget = neck_child + + h_con = pb[name2].constraints.new('COPY_TRANSFORMS') + h_con.name = "head" + h_con.target = self.obj + h_con.subtarget = head_mch + + con = pb[name2].constraints.new('COPY_LOCATION') + con.name = "anchor" + con.target = self.obj + if first: + con.subtarget = neck_ctrl + else: + con.subtarget = prev + con.head_tail = 1.0 + + # Drivers + n = (i + 1) / l + + # Neck influence + fcurve = n_con.driver_add("influence") + driver = fcurve.driver + var = driver.variables.new() + driver.type = 'SCRIPTED' + var.name = "ext" + var.targets[0].id_type = 'OBJECT' + var.targets[0].id = self.obj + var.targets[0].data_path = head_ctrl_p.path_from_id() + '["inf_extent"]' + driver.expression = "1.0 if (%.4f > (1.0-ext) or (1.0-ext) == 0.0) else (%.4f / (1.0-ext))" % (n, n) + + # Head influence + if (i + 1) == l: + h_con.influence = 1.0 + else: + fcurve = h_con.driver_add("influence") + driver = fcurve.driver + var = driver.variables.new() + driver.type = 'SCRIPTED' + var.name = "ext" + var.targets[0].id_type = 'OBJECT' + var.targets[0].id = self.obj + var.targets[0].data_path = head_ctrl_p.path_from_id() + '["inf_extent"]' + driver.expression = "0.0 if (%.4f <= (1.0-ext)) else ((%.4f - (1.0-ext)) / ext)" % (n, n) + + first = False + prev = name1 + i += 1 + + # Create control widgets + w1 = create_circle_widget(self.obj, neck_ctrl, radius=1.0, head_tail=0.5) + w2 = create_circle_widget(self.obj, head_ctrl, radius=1.0, head_tail=0.5) + + if w1 != None: + obj_to_bone(w1, self.obj, self.org_bones[(len(self.org_bones) - 1) // 2]) + if w2 != None: + obj_to_bone(w2, self.obj, self.org_bones[-1]) + + # Return control bones + return (head_ctrl, neck_ctrl) + + 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. + + """ + self.gen_deform() + (head, neck) = self.gen_control() + + script = script1 % (head, neck) + if self.isolate: + script += script2 + script += script3 + + return [script] + + @classmethod + def create_sample(self, obj): + # generated by rigify.utils.write_metarig + bpy.ops.object.mode_set(mode='EDIT') + arm = obj.data + + bones = {} + + bone = arm.edit_bones.new('neck') + bone.head[:] = 0.0000, 0.0000, 0.0000 + bone.tail[:] = 0.0000, -0.0500, 0.1500 + bone.roll = 0.0000 + bone.use_connect = False + bones['neck'] = bone.name + bone = arm.edit_bones.new('head') + bone.head[:] = 0.0000, -0.0500, 0.1500 + bone.tail[:] = 0.0000, -0.0500, 0.4000 + bone.roll = 3.1416 + bone.use_connect = True + bone.parent = arm.edit_bones[bones['neck']] + bones['head'] = bone.name + + bpy.ops.object.mode_set(mode='OBJECT') + pbone = obj.pose.bones[bones['neck']] + pbone.rigify_type = 'neck_short' + pbone.lock_location = (True, True, True) + pbone.lock_rotation = (False, False, False) + pbone.lock_rotation_w = False + pbone.lock_scale = (False, False, False) + pbone.rotation_mode = 'QUATERNION' + pbone.rigify_parameters.add() + pbone = obj.pose.bones[bones['head']] + 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' + + bpy.ops.object.mode_set(mode='EDIT') + for bone in arm.edit_bones: + bone.select = False + bone.select_head = False + bone.select_tail = False + for b in bones: + bone = arm.edit_bones[bones[b]] + bone.select = True + bone.select_head = True + bone.select_tail = True + arm.edit_bones.active = bone + diff --git a/rigify/rigs/palm.py b/rigify/rigs/palm.py new file mode 100644 index 00000000..d1a3cf61 --- /dev/null +++ b/rigify/rigs/palm.py @@ -0,0 +1,273 @@ +#====================== 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 ======================== + +import bpy +from math import sin, cos, pi +from rigify.utils import MetarigError +from rigify.utils import copy_bone +from rigify.utils import strip_org, deformer, mch +from rigify.utils import create_widget +import re + + +def bone_siblings(obj, bone): + """ Returns a list of the siblings of the given bone. + This requires that the bones has a parent. + + """ + parent = obj.data.bones[bone].parent + + if parent is None: + return [] + + bones = [] + + for b in parent.children: + if b.name != bone: + bones += [b.name] + + return bones + + +def bone_distance(obj, bone1, bone2): + """ Returns the distance between two bones. + + """ + vec = obj.data.bones[bone1].head - obj.data.bones[bone2].head + return vec.length + + +class Rig: + """ A "palm" rig. A set of sibling bones that bend with each other. + This is a control and deformation rig. + + """ + def __init__(self, obj, bone, params): + """ Gather and validate data about the rig. + """ + self.obj = obj + self.params = params + + siblings = bone_siblings(obj, bone) + + if len(siblings) == 0: + raise MetarigError("RIGIFY ERROR: Bone '%s': must have a parent and at least one sibling." % (strip_org(bone))) + + # Sort list by name and distance + siblings.sort() + siblings.sort(key=lambda b: bone_distance(obj, bone, b)) + + self.org_bones = [bone] + siblings + + # Get rig parameters + self.palm_rotation_axis = params.palm_rotation_axis + + 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') + + # Figure out the name for the control bone (remove the last .##) + last_bone = self.org_bones[-1:][0] + ctrl_name = re.sub("([0-9]+\.)", "", strip_org(last_bone)[::-1], count=1)[::-1] + + # Make control bone + ctrl = copy_bone(self.obj, last_bone, ctrl_name) + + # Make deformation bones + def_bones = [] + for bone in self.org_bones: + b = copy_bone(self.obj, bone, deformer(strip_org(bone))) + def_bones += [b] + + # Parenting + eb = self.obj.data.edit_bones + + for d, b in zip(def_bones, self.org_bones): + eb[d].use_connect = False + eb[d].parent = eb[b] + + # Constraints + bpy.ops.object.mode_set(mode='OBJECT') + pb = self.obj.pose.bones + + i = 0 + div = len(self.org_bones) - 1 + for b in self.org_bones: + con = pb[b].constraints.new('COPY_TRANSFORMS') + con.name = "copy_transforms" + con.target = self.obj + con.subtarget = ctrl + con.target_space = 'LOCAL' + con.owner_space = 'LOCAL' + con.influence = i / div + + con = pb[b].constraints.new('COPY_ROTATION') + con.name = "copy_rotation" + con.target = self.obj + con.subtarget = ctrl + con.target_space = 'LOCAL' + con.owner_space = 'LOCAL' + if 'X' in self.palm_rotation_axis: + con.invert_x = True + con.use_x = True + con.use_z = False + else: + con.invert_z = True + con.use_x = False + con.use_z = True + con.use_y = False + + con.influence = (i / div) - (1 - cos((i * pi / 2) / div)) + + i += 1 + + # Create control widget + w = create_widget(self.obj, ctrl) + if w != None: + mesh = w.data + verts = [(0.15780271589756012, 2.086162567138672e-07, -0.30000004172325134), (0.15780259668827057, 1.0, -0.2000001072883606), (-0.15780280530452728, 0.9999999403953552, -0.20000004768371582), (-0.15780259668827057, -2.086162567138672e-07, -0.29999998211860657), (-0.15780256688594818, -2.7089754439657554e-07, 0.30000004172325134), (-0.1578027755022049, 0.9999998807907104, 0.19999995827674866), (0.15780262649059296, 0.9999999403953552, 0.19999989867210388), (0.1578027456998825, 1.4633496903115883e-07, 0.29999998211860657), (0.15780268609523773, 0.2500001788139343, -0.27500003576278687), (-0.15780264139175415, 0.24999985098838806, -0.2749999761581421), (0.15780262649059296, 0.7500000596046448, -0.22500008344650269), (-0.1578027606010437, 0.7499998807907104, -0.2250000238418579), (0.15780265629291534, 0.75, 0.22499991953372955), (0.15780271589756012, 0.2500000596046448, 0.2749999761581421), (-0.15780261158943176, 0.2499997615814209, 0.27500003576278687), (-0.1578027307987213, 0.7499998807907104, 0.22499997913837433)] + if 'Z' in self.palm_rotation_axis: + # Flip x/z coordinates + temp = [] + for v in verts: + temp += [(v[2], v[1], v[0])] + verts = temp + edges = [(1, 2), (0, 3), (4, 7), (5, 6), (8, 0), (9, 3), (10, 1), (11, 2), (12, 6), (13, 7), (4, 14), (15, 5), (10, 8), (11, 9), (15, 14), (12, 13)] + mesh.from_pydata(verts, edges, []) + mesh.update() + + mod = w.modifiers.new("subsurf", 'SUBSURF') + mod.levels = 2 + + @classmethod + def add_parameters(self, group): + """ Add the parameters of this rig type to the + RigifyParameters PropertyGroup + + """ + items = [('X', 'X', ''), ('Z', 'Z', '')] + group.palm_rotation_axis = bpy.props.EnumProperty(items=items, name="Palm Rotation Axis", default='X') + + @classmethod + def parameters_ui(self, layout, obj, bone): + """ Create the ui for the rig parameters. + + """ + params = obj.pose.bones[bone].rigify_parameters[0] + + r = layout.row() + r.label(text="Primary rotation axis:") + r.prop(params, "palm_rotation_axis", text="") + + @classmethod + def create_sample(self, obj): + # generated by rigify.utils.write_metarig + bpy.ops.object.mode_set(mode='EDIT') + arm = obj.data + + bones = {} + + bone = arm.edit_bones.new('palm.parent') + bone.head[:] = 0.0000, 0.0000, 0.0000 + bone.tail[:] = 0.0577, 0.0000, -0.0000 + bone.roll = 3.1416 + bone.use_connect = False + bones['palm.parent'] = bone.name + bone = arm.edit_bones.new('palm.04') + bone.head[:] = 0.0577, 0.0315, -0.0000 + bone.tail[:] = 0.1627, 0.0315, -0.0000 + bone.roll = 3.1416 + bone.use_connect = False + bone.parent = arm.edit_bones[bones['palm.parent']] + bones['palm.04'] = bone.name + bone = arm.edit_bones.new('palm.03') + bone.head[:] = 0.0577, 0.0105, -0.0000 + bone.tail[:] = 0.1627, 0.0105, -0.0000 + bone.roll = 3.1416 + bone.use_connect = False + bone.parent = arm.edit_bones[bones['palm.parent']] + bones['palm.03'] = bone.name + bone = arm.edit_bones.new('palm.02') + bone.head[:] = 0.0577, -0.0105, -0.0000 + bone.tail[:] = 0.1627, -0.0105, -0.0000 + bone.roll = 3.1416 + bone.use_connect = False + bone.parent = arm.edit_bones[bones['palm.parent']] + bones['palm.02'] = bone.name + bone = arm.edit_bones.new('palm.01') + bone.head[:] = 0.0577, -0.0315, -0.0000 + bone.tail[:] = 0.1627, -0.0315, -0.0000 + bone.roll = 3.1416 + bone.use_connect = False + bone.parent = arm.edit_bones[bones['palm.parent']] + bones['palm.01'] = bone.name + + bpy.ops.object.mode_set(mode='OBJECT') + pbone = obj.pose.bones[bones['palm.parent']] + 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 = obj.pose.bones[bones['palm.04']] + pbone.rigify_type = '' + pbone.lock_location = (False, False, False) + pbone.lock_rotation = (False, True, True) + pbone.lock_rotation_w = False + pbone.lock_scale = (False, False, False) + pbone.rotation_mode = 'YXZ' + pbone = obj.pose.bones[bones['palm.03']] + pbone.rigify_type = '' + pbone.lock_location = (False, False, False) + pbone.lock_rotation = (False, True, True) + pbone.lock_rotation_w = False + pbone.lock_scale = (False, False, False) + pbone.rotation_mode = 'YXZ' + pbone = obj.pose.bones[bones['palm.02']] + pbone.rigify_type = '' + pbone.lock_location = (False, False, False) + pbone.lock_rotation = (False, True, True) + pbone.lock_rotation_w = False + pbone.lock_scale = (False, False, False) + pbone.rotation_mode = 'YXZ' + pbone = obj.pose.bones[bones['palm.01']] + pbone.rigify_type = 'palm' + pbone.lock_location = (False, False, False) + pbone.lock_rotation = (False, True, True) + pbone.lock_rotation_w = False + pbone.lock_scale = (False, False, False) + pbone.rotation_mode = 'YXZ' + pbone.rigify_parameters.add() + + bpy.ops.object.mode_set(mode='EDIT') + for bone in arm.edit_bones: + bone.select = False + bone.select_head = False + bone.select_tail = False + for b in bones: + bone = arm.edit_bones[bones[b]] + bone.select = True + bone.select_head = True + bone.select_tail = True + arm.edit_bones.active = bone + diff --git a/rigify/rigs/spine.py b/rigify/rigs/spine.py new file mode 100644 index 00000000..86d9ba6c --- /dev/null +++ b/rigify/rigs/spine.py @@ -0,0 +1,617 @@ +#====================== 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 ======================== + +""" TODO: + - Add parameters for bone transform alphas. +""" + +from math import floor + +import bpy +from mathutils import Vector +from rigify.utils import MetarigError +from rigify.utils import copy_bone, new_bone, flip_bone, put_bone +from rigify.utils import connected_children_names +from rigify.utils import strip_org, make_mechanism_name, make_deformer_name +from rigify.utils import obj_to_bone, create_circle_widget, create_compass_widget +from rna_prop_ui import rna_idprop_ui_prop_get + + +script = """ +main = "%s" +spine = [%s] +if is_selected([main]+ spine): + layout.prop(pose_bones[main], '["pivot_slide"]', text="Pivot Slide (" + main + ")", slider=True) + +for name in spine[1:-1]: + if is_selected(name): + layout.prop(pose_bones[name], '["auto_rotate"]', text="Auto Rotate (" + name + ")", slider=True) +""" + + +class Rig: + """ A "spine" rig. It turns a chain of bones into a rig with two controls: + One for the hips, and one for the rib cage. + + """ + def __init__(self, obj, bone_name, params): + """ Gather and validate data about the rig. + + """ + self.obj = obj + self.org_bones = [bone_name] + connected_children_names(obj, bone_name) + self.params = params + + # Collect control bone indices + self.control_indices = [0, len(self.org_bones) - 1] + temp = self.params.chain_bone_controls.split(",") + for i in temp: + try: + j = int(i) - 1 + except ValueError: + pass + else: + if (j > 0) and (j < len(self.org_bones)) and (j not in self.control_indices): + self.control_indices += [j] + self.control_indices.sort() + + self.pivot_rest = self.params.rest_pivot_slide + self.pivot_rest = max(self.pivot_rest, 1.0/len(self.org_bones)) + self.pivot_rest = min(self.pivot_rest, 1.0-(1.0/len(self.org_bones))) + + if len(self.org_bones) <= 1: + raise MetarigError("RIGIFY ERROR: Bone '%s': input to rig type must be a chain of 2 or more bones." % (strip_org(bone))) + + def gen_deform(self): + """ Generate the deformation rig. + + """ + for name in self.org_bones: + bpy.ops.object.mode_set(mode='EDIT') + eb = self.obj.data.edit_bones + + # Create deform bone + bone_e = eb[copy_bone(self.obj, name)] + + # Change its name + bone_e.name = make_deformer_name(strip_org(name)) + bone_name = bone_e.name + + # Leave edit mode + bpy.ops.object.mode_set(mode='OBJECT') + + # Get the pose bone + bone = self.obj.pose.bones[bone_name] + + # Constrain to the original bone + con = bone.constraints.new('COPY_TRANSFORMS') + con.name = "copy_transforms" + con.target = self.obj + con.subtarget = name + + def gen_control(self): + """ Generate the control rig. + + """ + bpy.ops.object.mode_set(mode='EDIT') + eb = self.obj.data.edit_bones + #------------------------- + # Get rest slide position + a = self.pivot_rest * len(self.org_bones) + i = floor(a) + a -= i + if i == len(self.org_bones): + i -= 1 + a = 1.0 + + pivot_rest_pos = eb[self.org_bones[i]].head.copy() + pivot_rest_pos += eb[self.org_bones[i]].vector * a + + #---------------------- + # Create controls + + # Create control bones + controls = [] + for i in self.control_indices: + name = copy_bone(self.obj, self.org_bones[i], strip_org(self.org_bones[i])) + controls += [name] + + # Create control parents + control_parents = [] + for i in self.control_indices[1:-1]: + name = new_bone(self.obj, make_mechanism_name("par_" + strip_org(self.org_bones[i]))) + control_parents += [name] + + # Create sub-control bones + subcontrols = [] + for i in self.control_indices: + name = new_bone(self.obj, make_mechanism_name("sub_" + strip_org(self.org_bones[i]))) + subcontrols += [name] + + # Create main control bone + main_control = new_bone(self.obj, self.params.spine_main_control_name) + + # Create main control WGT bones + main_wgt1 = new_bone(self.obj, make_mechanism_name(self.params.spine_main_control_name + ".01")) + main_wgt2 = new_bone(self.obj, make_mechanism_name(self.params.spine_main_control_name + ".02")) + + eb = self.obj.data.edit_bones + + # Parent the main control + eb[main_control].use_connect = False + eb[main_control].parent = eb[self.org_bones[0]].parent + + # Parent the main WGTs + eb[main_wgt1].use_connect = False + eb[main_wgt1].parent = eb[main_control] + eb[main_wgt2].use_connect = False + eb[main_wgt2].parent = eb[main_wgt1] + + # Parent the controls and sub-controls + for name, subname in zip(controls, subcontrols): + eb[name].use_connect = False + eb[name].parent = eb[main_control] + eb[subname].use_connect = False + eb[subname].parent = eb[name] + + # Parent the control parents + for name, par_name in zip(controls[1:-1], control_parents): + eb[par_name].use_connect = False + eb[par_name].parent = eb[main_control] + eb[name].parent = eb[par_name] + + # Position the main bone + put_bone(self.obj, main_control, pivot_rest_pos) + eb[main_control].length = sum([eb[b].length for b in self.org_bones]) / 2 + + # Position the main WGTs + eb[main_wgt1].tail = (0.0, 0.0, sum([eb[b].length for b in self.org_bones]) / 4) + eb[main_wgt2].length = sum([eb[b].length for b in self.org_bones]) / 4 + put_bone(self.obj, main_wgt1, pivot_rest_pos) + put_bone(self.obj, main_wgt2, pivot_rest_pos) + + # 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) + eb[subname].length = eb[name].length / 3 + + # Position the control parents + for name, par_name in zip(controls[1:-1], control_parents): + put_bone(self.obj, par_name, pivot_rest_pos) + eb[par_name].length = eb[name].length / 2 + + #----------------------------------------- + # Control bone constraints and properties + bpy.ops.object.mode_set(mode='OBJECT') + pb = self.obj.pose.bones + + # Lock control locations + for name in controls: + bone = pb[name] + bone.lock_location = True, True, True + + # Main control doesn't use local location + pb[main_control].bone.use_local_location = False + + # Intermediate controls follow hips and spine + for name, par_name, i in zip(controls[1:-1], control_parents, self.control_indices[1:-1]): + bone = pb[par_name] + + # Custom bend_alpha property + prop = rna_idprop_ui_prop_get(pb[name], "bend_alpha", create=True) + pb[name]["bend_alpha"] = i / (len(self.org_bones) - 1) # set bend alpha + prop["min"] = 0.0 + prop["max"] = 1.0 + prop["soft_min"] = 0.0 + prop["soft_max"] = 1.0 + + # Custom auto_rotate + prop = rna_idprop_ui_prop_get(pb[name], "auto_rotate", create=True) + pb[name]["auto_rotate"] = 1.0 + prop["min"] = 0.0 + prop["max"] = 1.0 + prop["soft_min"] = 0.0 + prop["soft_max"] = 1.0 + + # Constraints + con1 = bone.constraints.new('COPY_TRANSFORMS') + con1.name = "copy_transforms" + con1.target = self.obj + con1.subtarget = subcontrols[0] + + con2 = bone.constraints.new('COPY_TRANSFORMS') + con2.name = "copy_transforms" + con2.target = self.obj + con2.subtarget = subcontrols[-1] + + # Drivers + fcurve = con1.driver_add("influence") + driver = fcurve.driver + driver.type = 'AVERAGE' + var = driver.variables.new() + var.name = "auto" + var.targets[0].id_type = 'OBJECT' + var.targets[0].id = self.obj + var.targets[0].data_path = pb[name].path_from_id() + '["auto_rotate"]' + + fcurve = con2.driver_add("influence") + driver = fcurve.driver + driver.type = 'SCRIPTED' + driver.expression = "alpha * auto" + var = driver.variables.new() + var.name = "alpha" + var.targets[0].id_type = 'OBJECT' + var.targets[0].id = self.obj + var.targets[0].data_path = pb[name].path_from_id() + '["bend_alpha"]' + var = driver.variables.new() + var.name = "auto" + var.targets[0].id_type = 'OBJECT' + var.targets[0].id = self.obj + var.targets[0].data_path = pb[name].path_from_id() + '["auto_rotate"]' + + #------------------------- + # Create flex spine chain + bpy.ops.object.mode_set(mode='EDIT') + flex_bones = [] + flex_subs = [] + prev_bone = None + for b in self.org_bones: + # Create bones + bone = copy_bone(self.obj, b, make_mechanism_name(strip_org(b) + ".flex")) + sub = new_bone(self.obj, make_mechanism_name(strip_org(b) + ".flex_s")) + flex_bones += [bone] + flex_subs += [sub] + + eb = self.obj.data.edit_bones + bone_e = eb[bone] + sub_e = eb[sub] + + # Parenting + bone_e.use_connect = False + sub_e.use_connect = False + if prev_bone is None: + sub_e.parent = eb[controls[0]] + else: + sub_e.parent = eb[prev_bone] + bone_e.parent = sub_e + + # Position + put_bone(self.obj, sub, bone_e.head) + sub_e.length = bone_e.length / 4 + if prev_bone is not None: + sub_e.use_connect = True + + prev_bone = bone + + #---------------------------- + # Create reverse spine chain + + # Create bones/parenting/positioning + bpy.ops.object.mode_set(mode='EDIT') + rev_bones = [] + prev_bone = None + for b in zip(flex_bones, self.org_bones): + # Create bones + bone = copy_bone(self.obj, b[1], make_mechanism_name(strip_org(b[1]) + ".reverse")) + rev_bones += [bone] + eb = self.obj.data.edit_bones + bone_e = eb[bone] + + # Parenting + bone_e.use_connect = False + bone_e.parent = eb[b[0]] + + # Position + flip_bone(self.obj, bone) + bone_e.tail = Vector(eb[b[0]].head) + #bone_e.head = Vector(eb[b[0]].tail) + if prev_bone is None: + put_bone(self.obj, bone, pivot_rest_pos) + else: + put_bone(self.obj, bone, eb[prev_bone].tail) + + prev_bone = bone + + # Constraints + bpy.ops.object.mode_set(mode='OBJECT') + pb = self.obj.pose.bones + prev_bone = None + for bone in rev_bones: + bone_p = pb[bone] + + con = bone_p.constraints.new('COPY_LOCATION') + con.name = "copy_location" + con.target = self.obj + if prev_bone is None: + con.subtarget = main_control + else: + con.subtarget = prev_bone + con.head_tail = 1.0 + prev_bone = bone + + #---------------------------------------- + # Constrain original bones to flex spine + bpy.ops.object.mode_set(mode='OBJECT') + pb = self.obj.pose.bones + + for obone, fbone in zip(self.org_bones, flex_bones): + con = pb[obone].constraints.new('COPY_TRANSFORMS') + con.name = "copy_transforms" + con.target = self.obj + con.subtarget = fbone + + #--------------------------- + # Create pivot slide system + pb = self.obj.pose.bones + bone_p = pb[self.org_bones[0]] + main_control_p = pb[main_control] + + # Custom pivot_slide property + prop = rna_idprop_ui_prop_get(main_control_p, "pivot_slide", create=True) + main_control_p["pivot_slide"] = self.pivot_rest + prop["min"] = 0.0 + prop["max"] = 1.0 + prop["soft_min"] = 1.0 / len(self.org_bones) + prop["soft_max"] = 1.0 - (1.0 / len(self.org_bones)) + + # Anchor constraints + con = bone_p.constraints.new('COPY_LOCATION') + con.name = "copy_location" + con.target = self.obj + con.subtarget = rev_bones[0] + + con = pb[main_wgt1].constraints.new('COPY_ROTATION') + con.name = "copy_rotation" + con.target = self.obj + con.subtarget = rev_bones[0] + + # Slide constraints + i = 1 + tot = len(rev_bones) + for rb in rev_bones: + con = bone_p.constraints.new('COPY_LOCATION') + con.name = "slide." + str(i) + con.target = self.obj + con.subtarget = rb + con.head_tail = 1.0 + + # Driver + fcurve = con.driver_add("influence") + driver = fcurve.driver + var = driver.variables.new() + driver.type = 'AVERAGE' + var.name = "slide" + var.targets[0].id_type = 'OBJECT' + var.targets[0].id = self.obj + var.targets[0].data_path = main_control_p.path_from_id() + '["pivot_slide"]' + mod = fcurve.modifiers[0] + mod.poly_order = 1 + mod.coefficients[0] = 1 - i + mod.coefficients[1] = tot + + # Main WGT + con = pb[main_wgt1].constraints.new('COPY_ROTATION') + con.name = "slide." + str(i) + con.target = self.obj + con.subtarget = rb + + # Driver + fcurve = con.driver_add("influence") + driver = fcurve.driver + var = driver.variables.new() + driver.type = 'AVERAGE' + var.name = "slide" + var.targets[0].id_type = 'OBJECT' + var.targets[0].id = self.obj + var.targets[0].data_path = main_control_p.path_from_id() + '["pivot_slide"]' + mod = fcurve.modifiers[0] + mod.poly_order = 1 + mod.coefficients[0] = 1.5 - i + mod.coefficients[1] = tot + + i += 1 + + #---------------------------------- + # Constrain flex spine to controls + bpy.ops.object.mode_set(mode='OBJECT') + pb = self.obj.pose.bones + + # Constrain the bones that correspond exactly to the controls + for i, name in zip(self.control_indices, subcontrols): + con = pb[flex_subs[i]].constraints.new('COPY_TRANSFORMS') + con.name = "copy_transforms" + con.target = self.obj + con.subtarget = name + + # Constrain the bones in-between the controls + for i, j, name1, name2 in zip(self.control_indices, self.control_indices[1:], subcontrols, subcontrols[1:]): + if (i + 1) < j: + for n in range(i + 1, j): + bone = pb[flex_subs[n]] + # Custom bend_alpha property + prop = rna_idprop_ui_prop_get(bone, "bend_alpha", create=True) + bone["bend_alpha"] = (n - i) / (j - i) # set bend alpha + prop["min"] = 0.0 + prop["max"] = 1.0 + prop["soft_min"] = 0.0 + prop["soft_max"] = 1.0 + + con = bone.constraints.new('COPY_TRANSFORMS') + con.name = "copy_transforms" + con.target = self.obj + con.subtarget = name1 + + con = bone.constraints.new('COPY_TRANSFORMS') + con.name = "copy_transforms" + con.target = self.obj + con.subtarget = name2 + + # Driver + fcurve = con.driver_add("influence") + driver = fcurve.driver + var = driver.variables.new() + driver.type = 'AVERAGE' + var.name = "alpha" + var.targets[0].id_type = 'OBJECT' + var.targets[0].id = self.obj + var.targets[0].data_path = bone.path_from_id() + '["bend_alpha"]' + + #------------- + # Final stuff + bpy.ops.object.mode_set(mode='OBJECT') + pb = self.obj.pose.bones + + # Control appearance + # Main + pb[main_control].custom_shape_transform = pb[main_wgt2] + w = create_compass_widget(self.obj, main_control) + if w != None: + obj_to_bone(w, self.obj, main_wgt2) + + # 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) + if w != None: + obj_to_bone(w, self.obj, 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) + if w != None: + obj_to_bone(w, self.obj, 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) + if w != None: + obj_to_bone(w, self.obj, self.org_bones[-1]) + + # Layers + pb[main_control].bone.layers = pb[self.org_bones[0]].bone.layers + + return [main_control] + controls + + 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. + + """ + self.gen_deform() + controls = self.gen_control() + + controls_string = ", ".join(["'" + x + "'" for x in controls[1:]]) + return [script % (controls[0], controls_string)] + + @classmethod + def add_parameters(self, group): + """ Add the parameters of this rig type to the + RigifyParameters PropertyGroup + """ + group.spine_main_control_name = bpy.props.StringProperty(name="Main control name", default="torso", description="Name that the main control bone should be given.") + group.rest_pivot_slide = bpy.props.FloatProperty(name="Rest Pivot Slide", default=0.0, min=0.0, max=1.0, soft_min=0.0, soft_max=1.0, description="The pivot slide value in the rest pose.") + group.chain_bone_controls = bpy.props.StringProperty(name="Control bone list", default="", description="Define which bones have controls.") + + + @classmethod + def parameters_ui(self, layout, obj, bone): + """ Create the ui for the rig parameters. + """ + params = obj.pose.bones[bone].rigify_parameters[0] + + r = layout.row() + r.prop(params, "spine_main_control_name") + + r = layout.row() + r.prop(params, "rest_pivot_slide", slider=True) + + r = layout.row() + r.prop(params, "chain_bone_controls") + + @classmethod + def create_sample(self, obj): + # generated by rigify.utils.write_metarig + bpy.ops.object.mode_set(mode='EDIT') + arm = obj.data + + bones = {} + + bone = arm.edit_bones.new('hips') + bone.head[:] = 0.0000, 0.0000, 0.0000 + bone.tail[:] = -0.0000, -0.0590, 0.2804 + bone.roll = -0.0000 + bone.use_connect = False + bones['hips'] = bone.name + bone = arm.edit_bones.new('spine') + bone.head[:] = -0.0000, -0.0590, 0.2804 + bone.tail[:] = 0.0000, 0.0291, 0.5324 + bone.roll = 0.0000 + bone.use_connect = True + bone.parent = arm.edit_bones[bones['hips']] + bones['spine'] = bone.name + bone = arm.edit_bones.new('ribs') + bone.head[:] = 0.0000, 0.0291, 0.5324 + bone.tail[:] = -0.0000, 0.0000, 1.0000 + bone.roll = -0.0000 + bone.use_connect = True + bone.parent = arm.edit_bones[bones['spine']] + bones['ribs'] = bone.name + + bpy.ops.object.mode_set(mode='OBJECT') + pbone = obj.pose.bones[bones['hips']] + pbone.rigify_type = 'spine' + 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 = obj.pose.bones[bones['spine']] + 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 = obj.pose.bones[bones['ribs']] + 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 = obj.pose.bones[bones['hips']] + pbone['rigify_type'] = 'spine' + pbone.rigify_parameters.add() + pbone.rigify_parameters[0].chain_bone_controls = "1, 2, 3" + + bpy.ops.object.mode_set(mode='EDIT') + for bone in arm.edit_bones: + bone.select = False + bone.select_head = False + bone.select_tail = False + for b in bones: + bone = arm.edit_bones[bones[b]] + bone.select = True + bone.select_head = True + bone.select_tail = True + arm.edit_bones.active = bone diff --git a/rigify/ui.py b/rigify/ui.py new file mode 100644 index 00000000..3a2e89e3 --- /dev/null +++ b/rigify/ui.py @@ -0,0 +1,300 @@ +#====================== 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 ======================== + +import bpy +from bpy.props import * +import rigify +from rigify.utils import get_rig_type +from rigify import generate +from rna_prop_ui import rna_idprop_ui_prop_get + + +class DATA_PT_rigify_buttons(bpy.types.Panel): + bl_label = "Rigify Buttons" + bl_space_type = 'PROPERTIES' + bl_region_type = 'WINDOW' + bl_context = "data" + #bl_options = {'DEFAULT_OPEN'} + + @classmethod + def poll(cls, context): + if not context.armature: + return False + #obj = context.object + #if obj: + # return (obj.mode in ('POSE', 'OBJECT', 'EDIT')) + #return False + return True + + def draw(self, context): + C = context + layout = self.layout + obj = context.object + id_store = C.window_manager + + if obj.mode in ('POSE', 'OBJECT'): + row = layout.row() + row.operator("pose.rigify_generate", text="Generate") + elif obj.mode == 'EDIT': + # Build types list + collection_name = str(id_store.rigify_collection).replace(" ", "") + + for i in range(0, len(id_store.rigify_types)): + id_store.rigify_types.remove(0) + + for r in rigify.rig_list: + collection = r.split('.')[0] + if collection_name == "All": + a = id_store.rigify_types.add() + a.name = r + elif r.startswith(collection_name + '.'): + a = id_store.rigify_types.add() + a.name = r + elif collection_name == "None" and len(r.split('.')) == 1: + a = id_store.rigify_types.add() + a.name = r + + ## Rig collection field + #row = layout.row() + #row.prop(id_store, 'rigify_collection', text="Category") + + # Rig type list + row = layout.row() + row.template_list(id_store, "rigify_types", id_store, 'rigify_active_type') + row = layout.row() + op = row.operator("armature.metarig_sample_add", text="Add sample") + op.metarig_type = id_store.rigify_types[id_store.rigify_active_type].name + + +class DATA_PT_rigify_layer_names(bpy.types.Panel): + bl_label = "Rigify Layer Names" + bl_space_type = 'PROPERTIES' + bl_region_type = 'WINDOW' + bl_context = "data" + bl_options = {'DEFAULT_CLOSED'} + + @classmethod + def poll(cls, context): + if not context.armature: + return False + return True + + def draw(self, context): + C = context + layout = self.layout + obj = context.object + + # Ensure that the layers exist + for i in range(1 + len(obj.data.rigify_layers), 29): + layer = obj.data.rigify_layers.add() + + # UI + for i in range(28): + if (i % 16) == 0: + col = layout.column() + if i == 0: + col.label(text="Top Row:") + else: + col.label(text="Bottom Row:") + if (i % 8) == 0: + col = layout.column(align=True) + row = col.row() + row.prop(obj.data, "layers", index=i, text="", toggle=True) + split = row.split(percentage=0.8) + split.prop(obj.data.rigify_layers[i], "name", text="Layer %d" % (i + 1)) + split.prop(obj.data.rigify_layers[i], "row", text="") + #split.prop(obj.data.rigify_layers[i], "column", text="") + + +class BONE_PT_rigify_buttons(bpy.types.Panel): + bl_label = "Rigify Type" + bl_space_type = 'PROPERTIES' + bl_region_type = 'WINDOW' + bl_context = "bone" + #bl_options = {'DEFAULT_OPEN'} + + @classmethod + def poll(cls, context): + if not context.armature or not context.active_pose_bone: + return False + obj = context.object + if obj: + return (obj.mode in ('POSE')) + return False + + def draw(self, context): + C = context + id_store = C.window_manager + bone = context.active_pose_bone + collection_name = str(id_store.rigify_collection).replace(" ", "") + rig_name = str(context.active_pose_bone.rigify_type).replace(" ", "") + + layout = self.layout + + # Build types list + for i in range(0, len(id_store.rigify_types)): + id_store.rigify_types.remove(0) + + for r in rigify.rig_list: + collection = r.split('.')[0] + if collection_name == "All": + a = id_store.rigify_types.add() + a.name = r + elif r.startswith(collection_name + '.'): + a = id_store.rigify_types.add() + a.name = r + elif collection_name == "None" and len(r.split('.')) == 1: + a = id_store.rigify_types.add() + a.name = r + + # Rig type field + row = layout.row() + row.prop_search(bone, "rigify_type", id_store, "rigify_types", text="Rig type:") + + # Rig type parameters / Rig type non-exist alert + if rig_name != "": + if len(bone.rigify_parameters) < 1: + bone.rigify_parameters.add() + + try: + rig = get_rig_type(rig_name) + rig.Rig + except (ImportError, AttributeError): + row = layout.row() + box = row.box() + box.label(text="ALERT: type \"%s\" does not exist!" % rig_name) + else: + try: + rig.Rig.parameters_ui + except AttributeError: + pass + else: + col = layout.column() + col.label(text="Options:") + box = layout.box() + + rig.Rig.parameters_ui(box, C.active_object, bone.name) + + +#class INFO_MT_armature_metarig_add(bpy.types.Menu): +# bl_idname = "INFO_MT_armature_metarig_add" +# bl_label = "Meta-Rig" + +# def draw(self, context): + #import rigify + + #layout = self.layout + #layout.operator_context = 'INVOKE_REGION_WIN' + + #for submodule_type in rigify.get_submodule_types(): + # text = bpy.path.display_name(submodule_type) + # layout.operator("pose.metarig_sample_add", text=text, icon='OUTLINER_OB_ARMATURE').metarig_type = submodule_type + + +def rigify_report_exception(operator, exception): + import traceback + import sys + import os + # find the module name where the error happened + # hint, this is the metarig type! + exceptionType, exceptionValue, exceptionTraceback = sys.exc_info() + fn = traceback.extract_tb(exceptionTraceback)[-1][0] + fn = os.path.basename(fn) + fn = os.path.splitext(fn)[0] + message = [] + if fn.startswith("__"): + message.append("Incorrect armature...") + else: + message.append("Incorrect armature for type '%s'" % fn) + message.append(exception.message) + + message.reverse() # XXX - stupid! menu's are upside down! + + operator.report(set(['INFO']), '\n'.join(message)) + + +class Generate(bpy.types.Operator): + '''Generates a rig from the active metarig armature''' + + bl_idname = "pose.rigify_generate" + bl_label = "Rigify Generate Rig" + bl_options = {'UNDO'} + + def execute(self, context): + import imp + imp.reload(generate) + + use_global_undo = context.user_preferences.edit.use_global_undo + context.user_preferences.edit.use_global_undo = False + try: + generate.generate_rig(context, context.object) + except rigify.utils.MetarigError as rig_exception: + rigify_report_exception(self, rig_exception) + finally: + context.user_preferences.edit.use_global_undo = use_global_undo + + return {'FINISHED'} + + +class Sample(bpy.types.Operator): + '''Create a sample metarig to be modified before generating the final rig.''' + + bl_idname = "armature.metarig_sample_add" + bl_label = "Add a sample metarig for a rig type" + bl_options = {'UNDO'} + + metarig_type = StringProperty(name="Type", description="Name of the rig type to generate a sample of", maxlen=128, default="") + + def execute(self, context): + if context.mode == 'EDIT_ARMATURE' and self.metarig_type != "": + use_global_undo = context.user_preferences.edit.use_global_undo + context.user_preferences.edit.use_global_undo = False + try: + rig = get_rig_type(self.metarig_type).Rig + create_sample = rig.create_sample + except (ImportError, AttributeError): + print("Rigify: rig type has no sample.") + else: + create_sample(context.active_object) + finally: + context.user_preferences.edit.use_global_undo = use_global_undo + bpy.ops.object.mode_set(mode='EDIT') + + return {'FINISHED'} + + +#menu_func = (lambda self, context: self.layout.menu("INFO_MT_armature_metarig_add", icon='OUTLINER_OB_ARMATURE')) + +#from bl_ui import space_info # ensure the menu is loaded first + +def register(): + bpy.utils.register_class(DATA_PT_rigify_layer_names) + bpy.utils.register_class(DATA_PT_rigify_buttons) + bpy.utils.register_class(BONE_PT_rigify_buttons) + bpy.utils.register_class(Generate) + bpy.utils.register_class(Sample) + + #space_info.INFO_MT_armature_add.append(ui.menu_func) + + +def unregister(): + bpy.utils.unregister_class(DATA_PT_rigify_layer_names) + bpy.utils.unregister_class(DATA_PT_rigify_buttons) + bpy.utils.unregister_class(BONE_PT_rigify_buttons) + bpy.utils.unregister_class(Generate) + bpy.utils.unregister_class(Sample) diff --git a/rigify/utils.py b/rigify/utils.py new file mode 100644 index 00000000..c9771222 --- /dev/null +++ b/rigify/utils.py @@ -0,0 +1,527 @@ +#====================== 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 ======================== + +import bpy +import imp +import random +import time +from mathutils import Vector +from math import ceil, floor +from rna_prop_ui import rna_idprop_ui_prop_get + +RIG_DIR = "rigs" # Name of the directory where rig types are kept + +ORG_PREFIX = "ORG-" # Prefix of original bones. +MCH_PREFIX = "MCH-" # Prefix of mechanism bones. +DEF_PREFIX = "DEF-" # Prefix of deformation bones. +WGT_PREFIX = "WGT-" # Prefix for widget objects +ROOT_NAME = "root" # Name of the root bone. + +WGT_LAYERS = [x == 19 for x in range(0, 20)] # Widgets go on the last scene layer. + +MODULE_NAME = "rigify" # Windows/Mac blender is weird, so __package__ doesn't work + + +#======================================================================= +# Error handling +#======================================================================= +class MetarigError(Exception): + """ Exception raised for errors. + """ + def __init__(self, message): + self.message = message + + def __str__(self): + return repr(self.message) + + +#======================================================================= +# Name manipulation +#======================================================================= +def org_name(name): + """ Returns the name with ORG_PREFIX stripped from it. + """ + if name.startswith(ORG_PREFIX): + return name[len(ORG_PREFIX):] + else: + return name + + +def strip_org(name): + """ Returns the name with ORG_PREFIX stripped from it. + """ + if name.startswith(ORG_PREFIX): + return name[len(ORG_PREFIX):] + else: + return name +org_name = strip_org + + +def org(name): + """ Prepends the ORG_PREFIX to a name if it doesn't already have + it, and returns it. + """ + if name.startswith(ORG_PREFIX): + return name + else: + return ORG_PREFIX + name +make_original_name = org + + +def mch(name): + """ Prepends the MCH_PREFIX to a name if it doesn't already have + it, and returns it. + """ + if name.startswith(MCH_PREFIX): + return name + else: + return MCH_PREFIX + name +make_mechanism_name = mch + + +def deformer(name): + """ Prepends the DEF_PREFIX to a name if it doesn't already have + it, and returns it. + """ + if name.startswith(DEF_PREFIX): + return name + else: + return DEF_PREFIX + name +make_deformer_name = deformer + + +def insert_before_lr(name, text): + if name[-1] in ['l', 'L', 'r', 'R'] and name[-2] in ['.', '-', '_']: + return name[:-2] + text + name[-2:] + else: + return name + text + + +#======================= +# Bone manipulation +#======================= +def new_bone(obj, bone_name): + """ Adds a new bone to the given armature object. + Returns the resulting bone's name. + """ + if obj == bpy.context.active_object and bpy.context.mode == 'EDIT_ARMATURE': + edit_bone = obj.data.edit_bones.new(bone_name) + name = edit_bone.name + edit_bone.head = (0, 0, 0) + edit_bone.tail = (0, 1, 0) + edit_bone.roll = 0 + bpy.ops.object.mode_set(mode='OBJECT') + bpy.ops.object.mode_set(mode='EDIT') + return name + else: + raise MetarigError("Can't add new bone '%s' outside of edit mode." % bone_name) + + +def copy_bone(obj, bone_name, assign_name=''): + """ Makes a copy of the given bone in the given armature object. + Returns the resulting bone's name. + """ + if bone_name not in obj.data.bones: + raise MetarigError("copy_bone(): bone '%s' not found, cannot copy it." % bone_name) + + if obj == bpy.context.active_object and bpy.context.mode == 'EDIT_ARMATURE': + if assign_name == '': + assign_name = bone_name + # Copy the edit bone + edit_bone_1 = obj.data.edit_bones[bone_name] + edit_bone_2 = obj.data.edit_bones.new(assign_name) + bone_name_1 = bone_name + bone_name_2 = edit_bone_2.name + + edit_bone_2.parent = edit_bone_1.parent + edit_bone_2.use_connect = edit_bone_1.use_connect + + # Copy edit bone attributes + edit_bone_2.layers = list(edit_bone_1.layers) + + edit_bone_2.head = Vector(edit_bone_1.head) + edit_bone_2.tail = Vector(edit_bone_1.tail) + edit_bone_2.roll = edit_bone_1.roll + + edit_bone_2.use_inherit_rotation = edit_bone_1.use_inherit_rotation + edit_bone_2.use_inherit_scale = edit_bone_1.use_inherit_scale + edit_bone_2.use_local_location = edit_bone_1.use_local_location + + edit_bone_2.use_deform = edit_bone_1.use_deform + edit_bone_2.bbone_segments = edit_bone_1.bbone_segments + edit_bone_2.bbone_in = edit_bone_1.bbone_in + edit_bone_2.bbone_out = edit_bone_1.bbone_out + + bpy.ops.object.mode_set(mode='OBJECT') + + # Get the pose bones + pose_bone_1 = obj.pose.bones[bone_name_1] + pose_bone_2 = obj.pose.bones[bone_name_2] + + # Copy pose bone attributes + pose_bone_2.rotation_mode = pose_bone_1.rotation_mode + pose_bone_2.rotation_axis_angle = tuple(pose_bone_1.rotation_axis_angle) + pose_bone_2.rotation_euler = tuple(pose_bone_1.rotation_euler) + pose_bone_2.rotation_quaternion = tuple(pose_bone_1.rotation_quaternion) + + pose_bone_2.lock_location = tuple(pose_bone_1.lock_location) + pose_bone_2.lock_scale = tuple(pose_bone_1.lock_scale) + pose_bone_2.lock_rotation = tuple(pose_bone_1.lock_rotation) + pose_bone_2.lock_rotation_w = pose_bone_1.lock_rotation_w + pose_bone_2.lock_rotations_4d = pose_bone_1.lock_rotations_4d + + bpy.ops.object.mode_set(mode='EDIT') + + return bone_name_2 + else: + raise MetarigError("Cannot copy bones outside of edit mode.") + + +def flip_bone(obj, bone_name): + """ Flips an edit bone. + """ + if bone_name not in obj.data.bones: + raise MetarigError("flip_bone(): bone '%s' not found, cannot copy it." % bone_name) + + if obj == bpy.context.active_object and bpy.context.mode == 'EDIT_ARMATURE': + bone = obj.data.edit_bones[bone_name] + head = Vector(bone.head) + tail = Vector(bone.tail) + bone.tail = head + tail + bone.head = tail + bone.tail = head + else: + raise MetarigError("Cannot flip bones outside of edit mode.") + + +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) + + if obj == bpy.context.active_object and bpy.context.mode == 'EDIT_ARMATURE': + bone = obj.data.edit_bones[bone_name] + + delta = pos - bone.head + bone.translate(delta) + else: + raise MetarigError("Cannot 'put' bones outside of edit mode.") + + +#============================================= +# Widget creation +#============================================= + +def obj_to_bone(obj, rig, bone_name): + """ Places an object at the location/rotation/scale of the given bone. + """ + if bpy.context.mode == 'EDIT_ARMATURE': + raise MetarigError("obj_to_bone(): does not work while in edit mode.") + + bone = rig.data.bones[bone_name] + + mat = rig.matrix_world * bone.matrix_local + + obj.location = mat.to_translation() + + obj.rotation_mode = 'XYZ' + obj.rotation_euler = mat.to_euler() + + scl = mat.to_scale() + scl_avg = (scl[0] + scl[1] + scl[2]) / 3 + obj.scale = (bone.length * scl_avg), (bone.length * scl_avg), (bone.length * scl_avg) + + +def create_widget(rig, bone_name): + """ Creates an empty widget object for a bone, and returns the object. + """ + obj_name = WGT_PREFIX + bone_name + scene = bpy.context.scene + # Check if it already exists + if obj_name in scene.objects: + return None + else: + mesh = bpy.data.meshes.new(obj_name) + obj = bpy.data.objects.new(obj_name, mesh) + scene.objects.link(obj) + + obj_to_bone(obj, rig, bone_name) + obj.layers = WGT_LAYERS + + return obj + + +# Common Widgets + +def create_line_widget(rig, bone_name): + """ Creates a basic line widget, a line that spans the length of the bone. + """ + obj = create_widget(rig, bone_name) + if obj != None: + mesh = obj.data + mesh.from_pydata([(0, 0, 0), (0, 1, 0)], [(0, 1)], []) + mesh.update() + + +def create_circle_widget(rig, bone_name, radius=1.0, head_tail=0.0, with_line=False): + """ Creates a basic circle widget, a circle around the y-axis. + radius: the radius of the circle + head_tail: where along the length of the bone the circle is (0.0=head, 1.0=tail) + """ + obj = create_widget(rig, bone_name) + if obj != None: + v = [(0.7071068286895752, 2.980232238769531e-07, -0.7071065306663513), (0.8314696550369263, 2.980232238769531e-07, -0.5555699467658997), (0.9238795042037964, 2.682209014892578e-07, -0.3826831877231598), (0.9807852506637573, 2.5331974029541016e-07, -0.19509011507034302), (1.0, 2.365559055306221e-07, 1.6105803979371558e-07), (0.9807853698730469, 2.2351741790771484e-07, 0.19509044289588928), (0.9238796234130859, 2.086162567138672e-07, 0.38268351554870605), (0.8314696550369263, 1.7881393432617188e-07, 0.5555704236030579), (0.7071068286895752, 1.7881393432617188e-07, 0.7071070075035095), (0.5555702447891235, 1.7881393432617188e-07, 0.8314698934555054), (0.38268327713012695, 1.7881393432617188e-07, 0.923879861831665), (0.19509008526802063, 1.7881393432617188e-07, 0.9807855486869812), (-3.2584136988589307e-07, 1.1920928955078125e-07, 1.000000238418579), (-0.19509072601795197, 1.7881393432617188e-07, 0.9807854294776917), (-0.3826838731765747, 1.7881393432617188e-07, 0.9238795638084412), (-0.5555707216262817, 1.7881393432617188e-07, 0.8314695358276367), (-0.7071071863174438, 1.7881393432617188e-07, 0.7071065902709961), (-0.8314700126647949, 1.7881393432617188e-07, 0.5555698871612549), (-0.923879861831665, 2.086162567138672e-07, 0.3826829195022583), (-0.9807853698730469, 2.2351741790771484e-07, 0.1950896978378296), (-1.0, 2.365559907957504e-07, -7.290432222362142e-07), (-0.9807850122451782, 2.5331974029541016e-07, -0.195091113448143), (-0.9238790273666382, 2.682209014892578e-07, -0.38268423080444336), (-0.831468939781189, 2.980232238769531e-07, -0.5555710196495056), (-0.7071058750152588, 2.980232238769531e-07, -0.707107424736023), (-0.555569052696228, 2.980232238769531e-07, -0.8314701318740845), (-0.38268208503723145, 2.980232238769531e-07, -0.923879861831665), (-0.19508881866931915, 2.980232238769531e-07, -0.9807853102684021), (1.6053570561780361e-06, 2.980232238769531e-07, -0.9999997615814209), (0.19509197771549225, 2.980232238769531e-07, -0.9807847142219543), (0.3826850652694702, 2.980232238769531e-07, -0.9238786101341248), (0.5555717945098877, 2.980232238769531e-07, -0.8314683437347412)] + verts = [(a[0] * radius, head_tail, a[2] * radius) for a in v] + if with_line: + edges = [(28, 12), (0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 7), (7, 8), (8, 9), (9, 10), (10, 11), (11, 12), (12, 13), (13, 14), (14, 15), (15, 16), (16, 17), (17, 18), (18, 19), (19, 20), (20, 21), (21, 22), (22, 23), (23, 24), (24, 25), (25, 26), (26, 27), (27, 28), (28, 29), (29, 30), (30, 31), (0, 31)] + else: + edges = [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 7), (7, 8), (8, 9), (9, 10), (10, 11), (11, 12), (12, 13), (13, 14), (14, 15), (15, 16), (16, 17), (17, 18), (18, 19), (19, 20), (20, 21), (21, 22), (22, 23), (23, 24), (24, 25), (25, 26), (26, 27), (27, 28), (28, 29), (29, 30), (30, 31), (0, 31)] + mesh = obj.data + mesh.from_pydata(verts, edges, []) + mesh.update() + return obj + else: + return None + + +def create_sphere_widget(rig, bone_name): + """ Creates a basic sphere widget, three pependicular overlapping circles. + """ + obj = create_widget(rig, bone_name) + if obj != None: + verts = [(0.3535533845424652, 0.3535533845424652, 0.0), (0.4619397521018982, 0.19134171307086945, 0.0), (0.5, -2.1855694143368964e-08, 0.0), (0.4619397521018982, -0.19134175777435303, 0.0), (0.3535533845424652, -0.3535533845424652, 0.0), (0.19134174287319183, -0.4619397521018982, 0.0), (7.549790126404332e-08, -0.5, 0.0), (-0.1913416087627411, -0.46193981170654297, 0.0), (-0.35355329513549805, -0.35355350375175476, 0.0), (-0.4619397521018982, -0.19134178757667542, 0.0), (-0.5, 5.962440319251527e-09, 0.0), (-0.4619397222995758, 0.1913418024778366, 0.0), (-0.35355326533317566, 0.35355350375175476, 0.0), (-0.19134148955345154, 0.46193987131118774, 0.0), (3.2584136988589307e-07, 0.5, 0.0), (0.1913420855998993, 0.46193960309028625, 0.0), (7.450580596923828e-08, 0.46193960309028625, 0.19134199619293213), (5.9254205098113744e-08, 0.5, 2.323586443253589e-07), (4.470348358154297e-08, 0.46193987131118774, -0.1913415789604187), (2.9802322387695312e-08, 0.35355350375175476, -0.3535533547401428), (2.9802322387695312e-08, 0.19134178757667542, -0.46193981170654297), (5.960464477539063e-08, -1.1151834122813398e-08, -0.5000000596046448), (5.960464477539063e-08, -0.1913418024778366, -0.46193984150886536), (5.960464477539063e-08, -0.35355350375175476, -0.3535533845424652), (7.450580596923828e-08, -0.46193981170654297, -0.19134166836738586), (9.348272556053416e-08, -0.5, 1.624372103492533e-08), (1.043081283569336e-07, -0.4619397521018982, 0.19134168326854706), (1.1920928955078125e-07, -0.3535533845424652, 0.35355329513549805), (1.1920928955078125e-07, -0.19134174287319183, 0.46193966269493103), (1.1920928955078125e-07, -4.7414250303745575e-09, 0.49999991059303284), (1.1920928955078125e-07, 0.19134172797203064, 0.46193966269493103), (8.940696716308594e-08, 0.3535533845424652, 0.35355329513549805), (0.3535534739494324, 0.0, 0.35355329513549805), (0.1913418173789978, -2.9802322387695312e-08, 0.46193966269493103), (8.303572940349113e-08, -5.005858838558197e-08, 0.49999991059303284), (-0.19134165346622467, -5.960464477539063e-08, 0.46193966269493103), (-0.35355329513549805, -8.940696716308594e-08, 0.35355329513549805), (-0.46193963289260864, -5.960464477539063e-08, 0.19134168326854706), (-0.49999991059303284, -5.960464477539063e-08, 1.624372103492533e-08), (-0.4619397521018982, -2.9802322387695312e-08, -0.19134166836738586), (-0.3535534143447876, -2.9802322387695312e-08, -0.3535533845424652), (-0.19134171307086945, 0.0, -0.46193984150886536), (7.662531942287387e-08, 9.546055501630235e-09, -0.5000000596046448), (0.19134187698364258, 5.960464477539063e-08, -0.46193981170654297), (0.3535535931587219, 5.960464477539063e-08, -0.3535533547401428), (0.4619399905204773, 5.960464477539063e-08, -0.1913415789604187), (0.5000000596046448, 5.960464477539063e-08, 2.323586443253589e-07), (0.4619396924972534, 2.9802322387695312e-08, 0.19134199619293213)] + edges = [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 7), (7, 8), (8, 9), (9, 10), (10, 11), (11, 12), (12, 13), (13, 14), (14, 15), (0, 15), (16, 31), (16, 17), (17, 18), (18, 19), (19, 20), (20, 21), (21, 22), (22, 23), (23, 24), (24, 25), (25, 26), (26, 27), (27, 28), (28, 29), (29, 30), (30, 31), (32, 33), (33, 34), (34, 35), (35, 36), (36, 37), (37, 38), (38, 39), (39, 40), (40, 41), (41, 42), (42, 43), (43, 44), (44, 45), (45, 46), (46, 47), (32, 47)] + mesh = obj.data + mesh.from_pydata(verts, edges, []) + mesh.update() + + +def create_limb_widget(rig, bone_name): + """ Creates a basic limb widget, a line that spans the length of the + bone, with a circle around the center. + """ + obj = create_widget(rig, bone_name) + if obj != None: + verts = [(-1.1920928955078125e-07, 1.7881393432617188e-07, 0.0), (3.5762786865234375e-07, 1.0000004768371582, 0.0), (0.1767769455909729, 0.5000001192092896, 0.17677664756774902), (0.20786768198013306, 0.5000001192092896, 0.1388925313949585), (0.23097014427185059, 0.5000001192092896, 0.09567084908485413), (0.24519658088684082, 0.5000001192092896, 0.048772573471069336), (0.2500002384185791, 0.5000001192092896, -2.545945676502015e-09), (0.24519658088684082, 0.5000001192092896, -0.048772573471069336), (0.23097014427185059, 0.5000001192092896, -0.09567084908485413), (0.20786768198013306, 0.5000001192092896, -0.13889259099960327), (0.1767769455909729, 0.5000001192092896, -0.1767767071723938), (0.13889282941818237, 0.5000001192092896, -0.20786744356155396), (0.09567105770111084, 0.5000001192092896, -0.23096990585327148), (0.04877278208732605, 0.5000001192092896, -0.24519634246826172), (1.7279069197684294e-07, 0.5000000596046448, -0.25), (-0.0487724244594574, 0.5000001192092896, -0.24519634246826172), (-0.09567070007324219, 0.5000001192092896, -0.2309698462486267), (-0.13889241218566895, 0.5000001192092896, -0.20786738395690918), (-0.17677652835845947, 0.5000001192092896, -0.17677664756774902), (-0.20786726474761963, 0.5000001192092896, -0.13889244198799133), (-0.23096972703933716, 0.5000001192092896, -0.09567070007324219), (-0.24519610404968262, 0.5000001192092896, -0.04877239465713501), (-0.2499997615814209, 0.5000001192092896, 2.1997936983098043e-07), (-0.24519598484039307, 0.5000001192092896, 0.04877282679080963), (-0.23096948862075806, 0.5000001192092896, 0.09567108750343323), (-0.20786696672439575, 0.5000001192092896, 0.1388927698135376), (-0.1767762303352356, 0.5000001192092896, 0.17677688598632812), (-0.13889199495315552, 0.5000001192092896, 0.2078675627708435), (-0.09567028284072876, 0.5000001192092896, 0.23097002506256104), (-0.048771947622299194, 0.5000001192092896, 0.24519634246826172), (6.555903269145347e-07, 0.5000001192092896, 0.25), (0.04877324402332306, 0.5000001192092896, 0.24519622325897217), (0.09567153453826904, 0.5000001192092896, 0.23096966743469238), (0.13889318704605103, 0.5000001192092896, 0.20786714553833008)] + edges = [(0, 1), (2, 3), (4, 3), (5, 4), (5, 6), (6, 7), (8, 7), (8, 9), (10, 9), (10, 11), (11, 12), (13, 12), (14, 13), (14, 15), (16, 15), (16, 17), (17, 18), (19, 18), (19, 20), (21, 20), (21, 22), (22, 23), (24, 23), (25, 24), (25, 26), (27, 26), (27, 28), (29, 28), (29, 30), (30, 31), (32, 31), (32, 33), (2, 33)] + mesh = obj.data + mesh.from_pydata(verts, edges, []) + mesh.update() + + +def create_bone_widget(rig, bone_name): + """ Creates a basic bone widget, a simple obolisk-esk shape. + """ + obj = create_widget(rig, bone_name) + if obj != None: + verts = [(0.04, 1.0, -0.04), (0.1, 0.0, -0.1), (-0.1, 0.0, -0.1), (-0.04, 1.0, -0.04), (0.04, 1.0, 0.04), (0.1, 0.0, 0.1), (-0.1, 0.0, 0.1), (-0.04, 1.0, 0.04)] + edges = [(1, 2), (0, 1), (0, 3), (2, 3), (4, 5), (5, 6), (6, 7), (4, 7), (1, 5), (0, 4), (2, 6), (3, 7)] + mesh = obj.data + mesh.from_pydata(verts, edges, []) + mesh.update() + + +def create_compass_widget(rig, bone_name): + """ Creates a compass-shaped widget. + """ + obj = create_widget(rig, bone_name) + if obj != None: + verts = [(0.0, 1.2000000476837158, 0.0), (0.19509032368659973, 0.9807852506637573, 0.0), (0.3826834559440613, 0.9238795042037964, 0.0), (0.5555702447891235, 0.8314695954322815, 0.0), (0.7071067690849304, 0.7071067690849304, 0.0), (0.8314696550369263, 0.5555701851844788, 0.0), (0.9238795042037964, 0.3826834261417389, 0.0), (0.9807852506637573, 0.19509035348892212, 0.0), (1.2000000476837158, 7.549790126404332e-08, 0.0), (0.9807853102684021, -0.19509020447731018, 0.0), (0.9238795638084412, -0.38268327713012695, 0.0), (0.8314696550369263, -0.5555701851844788, 0.0), (0.7071067690849304, -0.7071067690849304, 0.0), (0.5555701851844788, -0.8314696550369263, 0.0), (0.38268327713012695, -0.9238796234130859, 0.0), (0.19509008526802063, -0.9807853102684021, 0.0), (-3.2584136988589307e-07, -1.2999999523162842, 0.0), (-0.19509072601795197, -0.9807851910591125, 0.0), (-0.3826838731765747, -0.9238793253898621, 0.0), (-0.5555707216262817, -0.8314692974090576, 0.0), (-0.7071072459220886, -0.707106351852417, 0.0), (-0.8314700126647949, -0.5555696487426758, 0.0), (-0.923879861831665, -0.3826826810836792, 0.0), (-0.9807854294776917, -0.1950894594192505, 0.0), (-1.2000000476837158, 9.655991561885457e-07, 0.0), (-0.980785071849823, 0.1950913518667221, 0.0), (-0.923879086971283, 0.38268446922302246, 0.0), (-0.831468939781189, 0.5555712580680847, 0.0), (-0.7071058750152588, 0.707107663154602, 0.0), (-0.5555691123008728, 0.8314703702926636, 0.0), (-0.38268208503723145, 0.9238801002502441, 0.0), (-0.19508881866931915, 0.9807855486869812, 0.0)] + edges = [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 7), (7, 8), (8, 9), (9, 10), (10, 11), (11, 12), (12, 13), (13, 14), (14, 15), (15, 16), (16, 17), (17, 18), (18, 19), (19, 20), (20, 21), (21, 22), (22, 23), (23, 24), (24, 25), (25, 26), (26, 27), (27, 28), (28, 29), (29, 30), (30, 31), (0, 31)] + mesh = obj.data + mesh.from_pydata(verts, edges, []) + mesh.update() + +def create_root_widget(rig, bone_name): + """ Creates a widget for the root bone. + """ + obj = create_widget(rig, bone_name) + if obj != None: + verts = [(0.7071067690849304, 0.7071067690849304, 0.0), (0.7071067690849304, -0.7071067690849304, 0.0), (-0.7071067690849304, 0.7071067690849304, 0.0), (-0.7071067690849304, -0.7071067690849304, 0.0), (0.8314696550369263, 0.5555701851844788, 0.0), (0.8314696550369263, -0.5555701851844788, 0.0), (-0.8314696550369263, 0.5555701851844788, 0.0), (-0.8314696550369263, -0.5555701851844788, 0.0), (0.9238795042037964, 0.3826834261417389, 0.0), (0.9238795042037964, -0.3826834261417389, 0.0), (-0.9238795042037964, 0.3826834261417389, 0.0), (-0.9238795042037964, -0.3826834261417389, 0.0), (0.9807852506637573, 0.19509035348892212, 0.0), (0.9807852506637573, -0.19509035348892212, 0.0), (-0.9807852506637573, 0.19509035348892212, 0.0), (-0.9807852506637573, -0.19509035348892212, 0.0), (0.19509197771549225, 0.9807849526405334, 0.0), (0.19509197771549225, -0.9807849526405334, 0.0), (-0.19509197771549225, 0.9807849526405334, 0.0), (-0.19509197771549225, -0.9807849526405334, 0.0), (0.3826850652694702, 0.9238788485527039, 0.0), (0.3826850652694702, -0.9238788485527039, 0.0), (-0.3826850652694702, 0.9238788485527039, 0.0), (-0.3826850652694702, -0.9238788485527039, 0.0), (0.5555717945098877, 0.8314685821533203, 0.0), (0.5555717945098877, -0.8314685821533203, 0.0), (-0.5555717945098877, 0.8314685821533203, 0.0), (-0.5555717945098877, -0.8314685821533203, 0.0), (0.19509197771549225, 1.2807848453521729, 0.0), (0.19509197771549225, -1.2807848453521729, 0.0), (-0.19509197771549225, 1.2807848453521729, 0.0), (-0.19509197771549225, -1.2807848453521729, 0.0), (1.280785322189331, 0.19509035348892212, 0.0), (1.280785322189331, -0.19509035348892212, 0.0), (-1.280785322189331, 0.19509035348892212, 0.0), (-1.280785322189331, -0.19509035348892212, 0.0), (0.3950919806957245, 1.2807848453521729, 0.0), (0.3950919806957245, -1.2807848453521729, 0.0), (-0.3950919806957245, 1.2807848453521729, 0.0), (-0.3950919806957245, -1.2807848453521729, 0.0), (1.280785322189331, 0.39509034156799316, 0.0), (1.280785322189331, -0.39509034156799316, 0.0), (-1.280785322189331, 0.39509034156799316, 0.0), (-1.280785322189331, -0.39509034156799316, 0.0), (0.0, 1.5807849168777466, 0.0), (0.0, -1.5807849168777466, 0.0), (1.5807852745056152, 0.0, 0.0), (-1.5807852745056152, 0.0, 0.0)] + edges = [(0, 4), (1, 5), (2, 6), (3, 7), (4, 8), (5, 9), (6, 10), (7, 11), (8, 12), (9, 13), (10, 14), (11, 15), (16, 20), (17, 21), (18, 22), (19, 23), (20, 24), (21, 25), (22, 26), (23, 27), (0, 24), (1, 25), (2, 26), (3, 27), (16, 28), (17, 29), (18, 30), (19, 31), (12, 32), (13, 33), (14, 34), (15, 35), (28, 36), (29, 37), (30, 38), (31, 39), (32, 40), (33, 41), (34, 42), (35, 43), (36, 44), (37, 45), (38, 44), (39, 45), (40, 46), (41, 46), (42, 47), (43, 47)] + mesh = obj.data + mesh.from_pydata(verts, edges, []) + mesh.update() + + +#============================================= +# Misc +#============================================= + + +def get_rig_type(rig_type): + """ Fetches a rig module by name, and returns it. + """ + #print("%s.%s.%s" % (__package__,RIG_DIR,rig_type)) + submod = __import__(name="%s.%s.%s" % (MODULE_NAME, RIG_DIR, rig_type), fromlist=[rig_type]) + imp.reload(submod) + return submod + + +def connected_children_names(obj, bone_name): + """ Returns a list of bone names (in order) of the bones that form a single + connected chain starting with the given bone as a parent. + If there is a connected branch, the list stops there. + """ + bone = obj.data.bones[bone_name] + names = [] + + while True: + connects = 0 + con_name = "" + + for child in bone.children: + if child.use_connect: + connects += 1 + con_name = child.name + + if connects == 1: + names += [con_name] + bone = obj.data.bones[con_name] + else: + break + + return names + + +def has_connected_children(bone): + """ Returns true/false whether a bone has connected children or not. + """ + t = False + for b in bone.children: + t = t or b.use_connect + return t + + +def get_layers(layers): + """ Does it's best to exctract a set of layers from any data thrown at it. + """ + if type(layers) == int: + return [x == layers for x in range(0, 32)] + elif type(layers) == str: + s = layers.split(",") + l = [] + for i in s: + try: + l += [int(float(i))] + except ValueError: + pass + return [x in l for x in range(0, 32)] + elif type(layers) == tuple or type(layers) == list: + return [x in layers for x in range(0, 32)] + else: + try: + list(layers) + except TypeError: + pass + else: + return [x in layers for x in range(0, 32)] + + +def write_metarig(obj, layers=False, func_name="create_sample"): + ''' + Write a metarig as a python script, this rig is to have all info needed for + generating the real rig with rigify. + ''' + code = [] + + code.append("def %s(obj):" % func_name) + code.append(" # generated by rigify.utils.write_metarig") + bpy.ops.object.mode_set(mode='EDIT') + code.append(" bpy.ops.object.mode_set(mode='EDIT')") + code.append(" arm = obj.data") + + arm = obj.data + # write parents first + bones = [(len(bone.parent_recursive), bone.name) for bone in arm.edit_bones] + bones.sort(key=lambda item: item[0]) + bones = [item[1] for item in bones] + + code.append("\n bones = {}\n") + + for bone_name in bones: + bone = arm.edit_bones[bone_name] + code.append(" bone = arm.edit_bones.new('%s')" % bone.name) + code.append(" bone.head[:] = %.4f, %.4f, %.4f" % bone.head.to_tuple(4)) + code.append(" bone.tail[:] = %.4f, %.4f, %.4f" % bone.tail.to_tuple(4)) + code.append(" bone.roll = %.4f" % bone.roll) + code.append(" bone.use_connect = %s" % str(bone.use_connect)) + if bone.parent: + code.append(" bone.parent = arm.edit_bones[bones['%s']]" % bone.parent.name) + code.append(" bones['%s'] = bone.name" % bone.name) + + bpy.ops.object.mode_set(mode='OBJECT') + code.append("") + code.append(" bpy.ops.object.mode_set(mode='OBJECT')") + + # 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['%s']]" % bone_name) + code.append(" pbone.rigify_type = '%s'" % pbone.rigify_type) + code.append(" pbone.lock_location = %s" % str(tuple(pbone.lock_location))) + code.append(" pbone.lock_rotation = %s" % str(tuple(pbone.lock_rotation))) + code.append(" pbone.lock_rotation_w = %s" % str(pbone.lock_rotation_w)) + code.append(" pbone.lock_scale = %s" % str(tuple(pbone.lock_scale))) + code.append(" pbone.rotation_mode = '%s'" % str(pbone.rotation_mode)) + if layers: + code.append(" pbone.bone.layers = %s" % str(list(pbone.bone.layers))) + # Rig type parameters + if len(pbone.rigify_parameters) > 0: + code.append(" pbone.rigify_parameters.add()") + for param_name in pbone.rigify_parameters[0].keys(): + param = getattr(pbone.rigify_parameters[0], param_name) + if str(type(param)) == "": + param = list(param) + code.append(" try:") + code.append(" pbone.rigify_parameters[0].%s = %s" % (param_name, str(param))) + code.append(" except AttributeError:") + code.append(" pass") + + code.append("\n bpy.ops.object.mode_set(mode='EDIT')") + code.append(" for bone in arm.edit_bones:") + code.append(" bone.select = False") + code.append(" bone.select_head = False") + code.append(" bone.select_tail = False") + + code.append(" for b in bones:") + code.append(" bone = arm.edit_bones[bones[b]]") + code.append(" bone.select = True") + code.append(" bone.select_head = True") + code.append(" bone.select_tail = True") + code.append(" arm.edit_bones.active = bone") + + return "\n".join(code) + + +def random_id(length = 8): + """ Generates a random alphanumeric id string. + """ + tlength = int(length / 2) + rlength = int(length / 2) + int(length % 2) + + chars = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'] + text = "" + for i in range(0, rlength): + text += random.choice(chars) + text += str(hex(int(time.time())))[2:][-tlength:].rjust(tlength, '0')[::-1] + return text + -- cgit v1.2.3