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