Skip to content
Snippets Groups Projects
fk.py 7.33 KiB
Newer Older
  • Learn to ignore specific revisions
  • #====================== BEGIN GPL LICENSE BLOCK ======================
    #
    #  This program is free software; you can redistribute it and/or
    #  modify it under the terms of the GNU General Public License
    #  as published by the Free Software Foundation; either version 2
    #  of the License, or (at your option) any later version.
    #
    #  This program is distributed in the hope that it will be useful,
    #  but WITHOUT ANY WARRANTY; without even the implied warranty of
    #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    #  GNU General Public License for more details.
    #
    #  You should have received a copy of the GNU General Public License
    #  along with this program; if not, write to the Free Software Foundation,
    #  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
    #
    #======================= END GPL LICENSE BLOCK ========================
    
    import bpy
    import math
    from rigify.utils import MetarigError
    from rigify.utils import copy_bone, flip_bone, put_bone
    from rigify.utils import connected_children_names
    from rigify.utils import strip_org, make_mechanism_name, make_deformer_name
    from rigify.utils import get_layers
    from rigify.utils import create_widget, create_limb_widget
    from rna_prop_ui import rna_idprop_ui_prop_get
    
    
    class Rig:
        """ An FK arm rig, with hinge switch.
    
        """
        def __init__(self, obj, bone, params):
            """ Gather and validate data about the rig.
                Store any data or references to data that will be needed later on.
                In particular, store references to bones that will be needed, and
                store names of bones that will be needed.
                Do NOT change any data in the scene.  This is a gathering phase only.
    
            """
            self.obj = obj
            self.params = params
    
            # Get the chain of 3 connected bones
            self.org_bones = [bone] + connected_children_names(self.obj, bone)[:2]
    
            if len(self.org_bones) != 3:
                raise MetarigError("RIGIFY ERROR: Bone '%s': input to rig type must be a chain of 3 bones." % (strip_org(bone)))
    
            # Get (optional) parent
            if self.obj.data.bones[bone].parent == None:
                self.org_parent = None
            else:
                self.org_parent = self.obj.data.bones[bone].parent.name
    
            # Get the rig parameters
            if "layers" in params:
                self.layers = get_layers(params["layers"])
            else:
                self.layers = None
    
            self.primary_rotation_axis = params.primary_rotation_axis
    
        def generate(self):
            """ Generate the rig.
                Do NOT modify any of the original bones, except for adding constraints.
                The main armature should be selected and active before this is called.
    
            """
            bpy.ops.object.mode_set(mode='EDIT')
    
            # Create the control bones
            uarm = copy_bone(self.obj, self.org_bones[0], strip_org(self.org_bones[0]))
            farm = copy_bone(self.obj, self.org_bones[1], strip_org(self.org_bones[1]))
            hand = copy_bone(self.obj, self.org_bones[2], strip_org(self.org_bones[2]))
    
            # Create the hinge bones
            if self.org_parent != None:
                hinge = copy_bone(self.obj, self.org_parent, make_mechanism_name(uarm + ".hinge"))
                socket1 = copy_bone(self.obj, uarm, make_mechanism_name(uarm + ".socket1"))
                socket2 = copy_bone(self.obj, uarm, make_mechanism_name(uarm + ".socket2"))
    
            # Get edit bones
            eb = self.obj.data.edit_bones
    
            uarm_e = eb[uarm]
            farm_e = eb[farm]
            hand_e = eb[hand]
    
            if self.org_parent != None:
                hinge_e = eb[hinge]
                socket1_e = eb[socket1]
                socket2_e = eb[socket2]
    
            # Parenting
            farm_e.parent = uarm_e
            hand_e.parent = farm_e
    
            if self.org_parent != None:
                hinge_e.use_connect = False
                socket1_e.use_connect = False
                socket2_e.use_connect = False
    
                uarm_e.parent = hinge_e
                hinge_e.parent = socket2_e
                socket2_e.parent = None
    
            # Positioning
            if self.org_parent != None:
                center = (hinge_e.head + hinge_e.tail) / 2
                hinge_e.head = center
                socket1_e.length /= 4
                socket2_e.length /= 3
    
            # Object mode, get pose bones
            bpy.ops.object.mode_set(mode='OBJECT')
            pb = self.obj.pose.bones
    
            uarm_p = pb[uarm]
            farm_p = pb[farm]
            hand_p = pb[hand]
    
            if self.org_parent != None:
                socket1_p = pb[socket1]
                socket2_p = pb[socket2]
    
            # Set the elbow to only bend on the x-axis.
            farm_p.rotation_mode = 'XYZ'
            if 'X' in self.primary_rotation_axis:
                farm_p.lock_rotation = (False, True, True)
            elif 'Y' in self.primary_rotation_axis:
                farm_p.lock_rotation = (True, False, True)
            else:
                farm_p.lock_rotation = (True, True, False)
    
            # Set up custom properties
            if self.org_parent != None:
                prop = rna_idprop_ui_prop_get(uarm_p, "isolate", create=True)
                uarm_p["isolate"] = 0.0
                prop["soft_min"] = prop["min"] = 0.0
                prop["soft_max"] = prop["max"] = 1.0
    
            # Hinge constraints / drivers
            if self.org_parent != None:
                con = socket2_p.constraints.new('COPY_LOCATION')
                con.name = "copy_location"
                con.target = self.obj
                con.subtarget = socket1
    
                con = socket2_p.constraints.new('COPY_TRANSFORMS')
                con.name = "isolate_off"
                con.target = self.obj
                con.subtarget = socket1
    
                # Driver
                fcurve = con.driver_add("influence")
                driver = fcurve.driver
                var = driver.variables.new()
                driver.type = 'AVERAGE'
                var.name = "var"
                var.targets[0].id_type = 'OBJECT'
                var.targets[0].id = self.obj
                var.targets[0].data_path = uarm_p.path_from_id() + '["isolate"]'
                mod = fcurve.modifiers[0]
                mod.poly_order = 1
                mod.coefficients[0] = 1.0
                mod.coefficients[1] = -1.0
    
            # Constrain org bones to controls
            con = pb[self.org_bones[0]].constraints.new('COPY_TRANSFORMS')
            con.name = "fk"
            con.target = self.obj
            con.subtarget = uarm
    
            con = pb[self.org_bones[1]].constraints.new('COPY_TRANSFORMS')
            con.name = "fk"
            con.target = self.obj
            con.subtarget = farm
    
            con = pb[self.org_bones[2]].constraints.new('COPY_TRANSFORMS')
            con.name = "fk"
            con.target = self.obj
            con.subtarget = hand
    
            # Set layers if specified
            if self.layers:
                uarm_p.bone.layers = self.layers
                farm_p.bone.layers = self.layers
                hand_p.bone.layers = self.layers
    
            # Create control widgets
            create_limb_widget(self.obj, uarm)
            create_limb_widget(self.obj, farm)
    
            ob = create_widget(self.obj, hand)
            if ob != None:
                verts = [(0.7, 1.5, 0.0), (0.7, -0.25, 0.0), (-0.7, -0.25, 0.0), (-0.7, 1.5, 0.0), (0.7, 0.723, 0.0), (-0.7, 0.723, 0.0), (0.7, 0.0, 0.0), (-0.7, 0.0, 0.0)]
                edges = [(1, 2), (0, 3), (0, 4), (3, 5), (4, 6), (1, 6), (5, 7), (2, 7)]
                mesh = ob.data
                mesh.from_pydata(verts, edges, [])
    
    
                mod = ob.modifiers.new("subsurf", 'SUBSURF')
                mod.levels = 2
    
            return [uarm, farm, hand]