Example #1
0
    def gen_control(self):
        """ Generate the control rig.

        """
        bpy.ops.object.mode_set(mode='EDIT')
        eb = self.obj.data.edit_bones
        #-------------------------
        # Get rest slide position
        a = self.pivot_rest * len(self.org_bones)
        i = floor(a)
        a -= i
        if i == len(self.org_bones):
            i -= 1
            a = 1.0

        pivot_rest_pos = eb[self.org_bones[i]].head.copy()
        pivot_rest_pos += eb[self.org_bones[i]].vector * a

        #----------------------
        # Create controls

        # Create control bones
        controls = []
        for i in self.control_indices:
            name = copy_bone(self.obj, self.org_bones[i],
                             strip_org(self.org_bones[i]))
            controls += [name]

        # Create control parents
        control_parents = []
        for i in self.control_indices[1:-1]:
            name = new_bone(
                self.obj,
                make_mechanism_name("par_" + strip_org(self.org_bones[i])))
            control_parents += [name]

        # Create sub-control bones
        subcontrols = []
        for i in self.control_indices:
            name = new_bone(
                self.obj,
                make_mechanism_name("sub_" + strip_org(self.org_bones[i])))
            subcontrols += [name]

        # Create main control bone
        main_control = new_bone(self.obj, self.params.spine_main_control_name)

        # Create main control WGT bones
        main_wgt1 = new_bone(
            self.obj,
            make_mechanism_name(self.params.spine_main_control_name + ".01"))
        main_wgt2 = new_bone(
            self.obj,
            make_mechanism_name(self.params.spine_main_control_name + ".02"))

        eb = self.obj.data.edit_bones

        # Parent the main control
        eb[main_control].use_connect = False
        eb[main_control].parent = eb[self.org_bones[0]].parent

        # Parent the main WGTs
        eb[main_wgt1].use_connect = False
        eb[main_wgt1].parent = eb[main_control]
        eb[main_wgt2].use_connect = False
        eb[main_wgt2].parent = eb[main_wgt1]

        # Parent the controls and sub-controls
        for name, subname in zip(controls, subcontrols):
            eb[name].use_connect = False
            eb[name].parent = eb[main_control]
            eb[subname].use_connect = False
            eb[subname].parent = eb[name]

        # Parent the control parents
        for name, par_name in zip(controls[1:-1], control_parents):
            eb[par_name].use_connect = False
            eb[par_name].parent = eb[main_control]
            eb[name].parent = eb[par_name]

        # Position the main bone
        put_bone(self.obj, main_control, pivot_rest_pos)
        eb[main_control].length = sum([eb[b].length
                                       for b in self.org_bones]) / 2

        # Position the main WGTs
        eb[main_wgt1].tail = (0.0, 0.0,
                              sum([eb[b].length for b in self.org_bones]) / 4)
        eb[main_wgt2].length = sum([eb[b].length for b in self.org_bones]) / 4
        put_bone(self.obj, main_wgt1, pivot_rest_pos)
        put_bone(self.obj, main_wgt2, pivot_rest_pos)

        # Position the controls and sub-controls
        pos = eb[controls[0]].head.copy()
        for name, subname in zip(controls, subcontrols):
            put_bone(self.obj, name, pivot_rest_pos)
            put_bone(self.obj, subname, pivot_rest_pos)
            eb[subname].length = eb[name].length / 3

        # Position the control parents
        for name, par_name in zip(controls[1:-1], control_parents):
            put_bone(self.obj, par_name, pivot_rest_pos)
            eb[par_name].length = eb[name].length / 2

        #-----------------------------------------
        # Control bone constraints and properties
        bpy.ops.object.mode_set(mode='OBJECT')
        pb = self.obj.pose.bones

        # Lock control locations
        for name in controls:
            bone = pb[name]
            bone.lock_location = True, True, True

        # Main control doesn't use local location
        pb[main_control].bone.use_local_location = False

        # Intermediate controls follow hips and spine
        for name, par_name, i in zip(controls[1:-1], control_parents,
                                     self.control_indices[1:-1]):
            bone = pb[par_name]

            # Custom bend_alpha property
            prop = rna_idprop_ui_prop_get(pb[name], "bend_alpha", create=True)
            pb[name]["bend_alpha"] = i / (len(self.org_bones) - 1
                                          )  # set bend alpha
            prop["min"] = 0.0
            prop["max"] = 1.0
            prop["soft_min"] = 0.0
            prop["soft_max"] = 1.0

            # Custom auto_rotate
            prop = rna_idprop_ui_prop_get(pb[name], "auto_rotate", create=True)
            pb[name]["auto_rotate"] = 1.0
            prop["min"] = 0.0
            prop["max"] = 1.0
            prop["soft_min"] = 0.0
            prop["soft_max"] = 1.0

            # Constraints
            con1 = bone.constraints.new('COPY_TRANSFORMS')
            con1.name = "copy_transforms"
            con1.target = self.obj
            con1.subtarget = subcontrols[0]

            con2 = bone.constraints.new('COPY_TRANSFORMS')
            con2.name = "copy_transforms"
            con2.target = self.obj
            con2.subtarget = subcontrols[-1]

            # Drivers
            fcurve = con1.driver_add("influence")
            driver = fcurve.driver
            driver.type = 'AVERAGE'
            var = driver.variables.new()
            var.name = "auto"
            var.targets[0].id_type = 'OBJECT'
            var.targets[0].id = self.obj
            var.targets[0].data_path = pb[name].path_from_id(
            ) + '["auto_rotate"]'

            fcurve = con2.driver_add("influence")
            driver = fcurve.driver
            driver.type = 'SCRIPTED'
            driver.expression = "alpha * auto"
            var = driver.variables.new()
            var.name = "alpha"
            var.targets[0].id_type = 'OBJECT'
            var.targets[0].id = self.obj
            var.targets[0].data_path = pb[name].path_from_id(
            ) + '["bend_alpha"]'
            var = driver.variables.new()
            var.name = "auto"
            var.targets[0].id_type = 'OBJECT'
            var.targets[0].id = self.obj
            var.targets[0].data_path = pb[name].path_from_id(
            ) + '["auto_rotate"]'

        #-------------------------
        # Create flex spine chain
        bpy.ops.object.mode_set(mode='EDIT')
        flex_bones = []
        flex_subs = []
        prev_bone = None
        for b in self.org_bones:
            # Create bones
            bone = copy_bone(self.obj, b,
                             make_mechanism_name(strip_org(b) + ".flex"))
            sub = new_bone(self.obj,
                           make_mechanism_name(strip_org(b) + ".flex_s"))
            flex_bones += [bone]
            flex_subs += [sub]

            eb = self.obj.data.edit_bones
            bone_e = eb[bone]
            sub_e = eb[sub]

            # Parenting
            bone_e.use_connect = False
            sub_e.use_connect = False
            if prev_bone is None:
                sub_e.parent = eb[controls[0]]
            else:
                sub_e.parent = eb[prev_bone]
            bone_e.parent = sub_e

            # Position
            put_bone(self.obj, sub, bone_e.head)
            sub_e.length = bone_e.length / 4
            if prev_bone is not None:
                sub_e.use_connect = True

            prev_bone = bone

        #----------------------------
        # Create reverse spine chain

        # Create bones/parenting/positioning
        bpy.ops.object.mode_set(mode='EDIT')
        rev_bones = []
        prev_bone = None
        for b in zip(flex_bones, self.org_bones):
            # Create bones
            bone = copy_bone(self.obj, b[1],
                             make_mechanism_name(strip_org(b[1]) + ".reverse"))
            rev_bones += [bone]
            eb = self.obj.data.edit_bones
            bone_e = eb[bone]

            # Parenting
            bone_e.use_connect = False
            bone_e.parent = eb[b[0]]

            # Position
            flip_bone(self.obj, bone)
            bone_e.tail = Vector(eb[b[0]].head)
            #bone_e.head = Vector(eb[b[0]].tail)
            if prev_bone is None:
                put_bone(self.obj, bone, pivot_rest_pos)
            else:
                put_bone(self.obj, bone, eb[prev_bone].tail)

            prev_bone = bone

        # Constraints
        bpy.ops.object.mode_set(mode='OBJECT')
        pb = self.obj.pose.bones
        prev_bone = None
        for bone in rev_bones:
            bone_p = pb[bone]

            con = bone_p.constraints.new('COPY_LOCATION')
            con.name = "copy_location"
            con.target = self.obj
            if prev_bone is None:
                con.subtarget = main_control
            else:
                con.subtarget = prev_bone
                con.head_tail = 1.0
            prev_bone = bone

        #----------------------------------------
        # Constrain original bones to flex spine
        bpy.ops.object.mode_set(mode='OBJECT')
        pb = self.obj.pose.bones

        for obone, fbone in zip(self.org_bones, flex_bones):
            con = pb[obone].constraints.new('COPY_TRANSFORMS')
            con.name = "copy_transforms"
            con.target = self.obj
            con.subtarget = fbone

        #---------------------------
        # Create pivot slide system
        pb = self.obj.pose.bones
        bone_p = pb[self.org_bones[0]]
        main_control_p = pb[main_control]

        # Custom pivot_slide property
        prop = rna_idprop_ui_prop_get(main_control_p,
                                      "pivot_slide",
                                      create=True)
        main_control_p["pivot_slide"] = self.pivot_rest
        prop["min"] = 0.0
        prop["max"] = 1.0
        prop["soft_min"] = 1.0 / len(self.org_bones)
        prop["soft_max"] = 1.0 - (1.0 / len(self.org_bones))

        # Anchor constraints
        con = bone_p.constraints.new('COPY_LOCATION')
        con.name = "copy_location"
        con.target = self.obj
        con.subtarget = rev_bones[0]

        con = pb[main_wgt1].constraints.new('COPY_ROTATION')
        con.name = "copy_rotation"
        con.target = self.obj
        con.subtarget = rev_bones[0]

        # Slide constraints
        i = 1
        tot = len(rev_bones)
        for rb in rev_bones:
            con = bone_p.constraints.new('COPY_LOCATION')
            con.name = "slide." + str(i)
            con.target = self.obj
            con.subtarget = rb
            con.head_tail = 1.0

            # Driver
            fcurve = con.driver_add("influence")
            driver = fcurve.driver
            var = driver.variables.new()
            driver.type = 'AVERAGE'
            var.name = "slide"
            var.targets[0].id_type = 'OBJECT'
            var.targets[0].id = self.obj
            var.targets[0].data_path = main_control_p.path_from_id(
            ) + '["pivot_slide"]'
            mod = fcurve.modifiers[0]
            mod.poly_order = 1
            mod.coefficients[0] = 1 - i
            mod.coefficients[1] = tot

            # Main WGT
            con = pb[main_wgt1].constraints.new('COPY_ROTATION')
            con.name = "slide." + str(i)
            con.target = self.obj
            con.subtarget = rb

            # Driver
            fcurve = con.driver_add("influence")
            driver = fcurve.driver
            var = driver.variables.new()
            driver.type = 'AVERAGE'
            var.name = "slide"
            var.targets[0].id_type = 'OBJECT'
            var.targets[0].id = self.obj
            var.targets[0].data_path = main_control_p.path_from_id(
            ) + '["pivot_slide"]'
            mod = fcurve.modifiers[0]
            mod.poly_order = 1
            mod.coefficients[0] = 1.5 - i
            mod.coefficients[1] = tot

            i += 1

        #----------------------------------
        # Constrain flex spine to controls
        bpy.ops.object.mode_set(mode='OBJECT')
        pb = self.obj.pose.bones

        # Constrain the bones that correspond exactly to the controls
        for i, name in zip(self.control_indices, subcontrols):
            con = pb[flex_subs[i]].constraints.new('COPY_TRANSFORMS')
            con.name = "copy_transforms"
            con.target = self.obj
            con.subtarget = name

        # Constrain the bones in-between the controls
        for i, j, name1, name2 in zip(self.control_indices,
                                      self.control_indices[1:], subcontrols,
                                      subcontrols[1:]):
            if (i + 1) < j:
                for n in range(i + 1, j):
                    bone = pb[flex_subs[n]]
                    # Custom bend_alpha property
                    prop = rna_idprop_ui_prop_get(bone,
                                                  "bend_alpha",
                                                  create=True)
                    bone["bend_alpha"] = (n - i) / (j - i)  # set bend alpha
                    prop["min"] = 0.0
                    prop["max"] = 1.0
                    prop["soft_min"] = 0.0
                    prop["soft_max"] = 1.0

                    con = bone.constraints.new('COPY_TRANSFORMS')
                    con.name = "copy_transforms"
                    con.target = self.obj
                    con.subtarget = name1

                    con = bone.constraints.new('COPY_TRANSFORMS')
                    con.name = "copy_transforms"
                    con.target = self.obj
                    con.subtarget = name2

                    # Driver
                    fcurve = con.driver_add("influence")
                    driver = fcurve.driver
                    var = driver.variables.new()
                    driver.type = 'AVERAGE'
                    var.name = "alpha"
                    var.targets[0].id_type = 'OBJECT'
                    var.targets[0].id = self.obj
                    var.targets[0].data_path = bone.path_from_id(
                    ) + '["bend_alpha"]'

        #-------------
        # Final stuff
        bpy.ops.object.mode_set(mode='OBJECT')
        pb = self.obj.pose.bones

        # Control appearance
        # Main
        pb[main_control].custom_shape_transform = pb[main_wgt2]
        w = create_compass_widget(self.obj, main_control)
        if w != None:
            obj_to_bone(w, self.obj, main_wgt2)

        # Spines
        for name, i in zip(controls[1:-1], self.control_indices[1:-1]):
            pb[name].custom_shape_transform = pb[self.org_bones[i]]
            # Create control widgets
            w = create_circle_widget(self.obj,
                                     name,
                                     radius=1.0,
                                     head_tail=0.5,
                                     with_line=True)
            if w != None:
                obj_to_bone(w, self.obj, self.org_bones[i])
        # Hips
        pb[controls[0]].custom_shape_transform = pb[self.org_bones[0]]
        # Create control widgets
        w = create_circle_widget(self.obj,
                                 controls[0],
                                 radius=1.0,
                                 head_tail=0.5,
                                 with_line=True)
        if w != None:
            obj_to_bone(w, self.obj, self.org_bones[0])

        # Ribs
        pb[controls[-1]].custom_shape_transform = pb[self.org_bones[-1]]
        # Create control widgets
        w = create_circle_widget(self.obj,
                                 controls[-1],
                                 radius=1.0,
                                 head_tail=0.5,
                                 with_line=True)
        if w != None:
            obj_to_bone(w, self.obj, self.org_bones[-1])

        # Layers
        pb[main_control].bone.layers = pb[self.org_bones[0]].bone.layers

        return [main_control] + controls
Example #2
0
def generate_rig(context, metarig):
    """ Generates a rig from a metarig.

    """
    t = Timer()

    # Random string with time appended so that
    # different rigs don't collide id's
    rig_id = random_id(16)

    # Initial configuration
    # mode_orig = context.mode  # UNUSED
    rest_backup = metarig.data.pose_position
    metarig.data.pose_position = 'REST'

    bpy.ops.object.mode_set(mode='OBJECT')

    scene = context.scene

    #------------------------------------------
    # Create/find the rig object and set it up

    # Check if the generated rig already exists, so we can
    # regenerate in the same object.  If not, create a new
    # object to generate the rig in.
    print("Fetch rig.")
    try:
        name = metarig["rig_object_name"]
    except KeyError:
        name = "rig"

    try:
        obj = scene.objects[name]
    except KeyError:
        obj = bpy.data.objects.new(name, bpy.data.armatures.new(name))
        obj.draw_type = 'WIRE'
        scene.objects.link(obj)

    obj.data.pose_position = 'POSE'

    # Get rid of anim data in case the rig already existed
    print("Clear rig animation data.")
    obj.animation_data_clear()

    # Select generated rig object
    metarig.select = False
    obj.select = True
    scene.objects.active = obj

    # Remove all bones from the generated rig armature.
    bpy.ops.object.mode_set(mode='EDIT')
    for bone in obj.data.edit_bones:
        obj.data.edit_bones.remove(bone)
    bpy.ops.object.mode_set(mode='OBJECT')

    # Create temporary duplicates for merging
    temp_rig_1 = metarig.copy()
    temp_rig_1.data = metarig.data.copy()
    scene.objects.link(temp_rig_1)

    temp_rig_2 = metarig.copy()
    temp_rig_2.data = obj.data
    scene.objects.link(temp_rig_2)

    # Select the temp rigs for merging
    for objt in scene.objects:
        objt.select = False  # deselect all objects
    temp_rig_1.select = True
    temp_rig_2.select = True
    scene.objects.active = temp_rig_2

    # Merge the temporary rigs
    bpy.ops.object.join()

    # Delete the second temp rig
    bpy.ops.object.delete()

    # Select the generated rig
    for objt in scene.objects:
        objt.select = False  # deselect all objects
    obj.select = True
    scene.objects.active = obj

    # Copy over bone properties
    for bone in metarig.data.bones:
        bone_gen = obj.data.bones[bone.name]

        # B-bone stuff
        bone_gen.bbone_segments = bone.bbone_segments
        bone_gen.bbone_in = bone.bbone_in
        bone_gen.bbone_out = bone.bbone_out

    # Copy over the pose_bone properties
    for bone in metarig.pose.bones:
        bone_gen = obj.pose.bones[bone.name]

        # Rotation mode and transform locks
        bone_gen.rotation_mode = bone.rotation_mode
        bone_gen.lock_rotation = tuple(bone.lock_rotation)
        bone_gen.lock_rotation_w = bone.lock_rotation_w
        bone_gen.lock_rotations_4d = bone.lock_rotations_4d
        bone_gen.lock_location = tuple(bone.lock_location)
        bone_gen.lock_scale = tuple(bone.lock_scale)

        # Custom properties
        for prop in bone.keys():
            bone_gen[prop] = bone[prop]

        # Constraints
        for con1 in bone.constraints:
            con2 = bone_gen.constraints.new(type=con1.type)
            copy_attributes(con1, con2)

            # Set metarig target to rig target
            if "target" in dir(con2):
                if con2.target == metarig:
                    con2.target = obj

    # Copy drivers
    if metarig.animation_data:
        for d1 in metarig.animation_data.drivers:
            d2 = obj.driver_add(d1.data_path)
            copy_attributes(d1, d2)
            copy_attributes(d1.driver, d2.driver)

            # Remove default modifiers, variables, etc.
            for m in d2.modifiers:
                d2.modifiers.remove(m)
            for v in d2.driver.variables:
                d2.driver.variables.remove(v)

            # Copy modifiers
            for m1 in d1.modifiers:
                m2 = d2.modifiers.new(type=m1.type)
                copy_attributes(m1, m2)

            # Copy variables
            for v1 in d1.driver.variables:
                v2 = d2.driver.variables.new()
                copy_attributes(v1, v2)
                for i in range(len(v1.targets)):
                    copy_attributes(v1.targets[i], v2.targets[i])
                    # Switch metarig targets to rig targets
                    if v2.targets[i].id == metarig:
                        v2.targets[i].id = obj

                    # Mark targets that may need to be altered after rig generation
                    tar = v2.targets[i]
                    # If a custom property
                    if v2.type == 'SINGLE_PROP' \
                    and re.match('^pose.bones\["[^"\]]*"\]\["[^"\]]*"\]$', tar.data_path):
                        tar.data_path = "RIGIFY-" + tar.data_path

            # Copy key frames
            for i in range(len(d1.keyframe_points)):
                d2.keyframe_points.add()
                k1 = d1.keyframe_points[i]
                k2 = d2.keyframe_points[i]
                copy_attributes(k1, k2)

    t.tick("Duplicate rig: ")
    #----------------------------------
    # Make a list of the original bones so we can keep track of them.
    original_bones = [bone.name for bone in obj.data.bones]

    # Add the ORG_PREFIX to the original bones.
    bpy.ops.object.mode_set(mode='OBJECT')
    for i in range(0, len(original_bones)):
        obj.data.bones[original_bones[i]].name = make_original_name(original_bones[i])
        original_bones[i] = make_original_name(original_bones[i])

    # Create a sorted list of the original bones, sorted in the order we're
    # going to traverse them for rigging.
    # (root-most -> leaf-most, alphabetical)
    bones_sorted = []
    for name in original_bones:
        bones_sorted += [name]
    bones_sorted.sort()  # first sort by names
    bones_sorted.sort(key=lambda bone: len(obj.pose.bones[bone].parent_recursive))  # then parents before children

    t.tick("Make list of org bones: ")
    #----------------------------------
    # Create the root bone.
    bpy.ops.object.mode_set(mode='EDIT')
    root_bone = new_bone(obj, ROOT_NAME)
    obj.data.edit_bones[root_bone].head = (0, 0, 0)
    obj.data.edit_bones[root_bone].tail = (0, 1, 0)
    obj.data.edit_bones[root_bone].roll = 0
    bpy.ops.object.mode_set(mode='OBJECT')
    obj.data.bones[root_bone].layers = ROOT_LAYER
    # Put the rig_name in the armature custom properties
    rna_idprop_ui_prop_get(obj.data, "rig_id", create=True)
    obj.data["rig_id"] = rig_id

    t.tick("Create root bone: ")
    #----------------------------------
    try:
        # Collect/initialize all the rigs.
        rigs = []
        deformation_rigs = []
        for bone in bones_sorted:
            bpy.ops.object.mode_set(mode='EDIT')
            rigs += get_bone_rigs(obj, bone)
        t.tick("Initialize rigs: ")

        # Generate all the rigs.
        ui_scripts = []
        for rig in rigs:
            # Go into editmode in the rig armature
            bpy.ops.object.mode_set(mode='OBJECT')
            context.scene.objects.active = obj
            obj.select = True
            bpy.ops.object.mode_set(mode='EDIT')
            scripts = rig.generate()
            if scripts != None:
                ui_scripts += [scripts[0]]
        t.tick("Generate rigs: ")
    except Exception as e:
        # Cleanup if something goes wrong
        print("Rigify: failed to generate rig.")
        metarig.data.pose_position = rest_backup
        obj.data.pose_position = 'POSE'
        bpy.ops.object.mode_set(mode='OBJECT')

        # Continue the exception
        raise e

    #----------------------------------
    bpy.ops.object.mode_set(mode='OBJECT')

    # Get a list of all the bones in the armature
    bones = [bone.name for bone in obj.data.bones]

    # Parent any free-floating bones to the root.
    bpy.ops.object.mode_set(mode='EDIT')
    for bone in bones:
        if obj.data.edit_bones[bone].parent is None:
            obj.data.edit_bones[bone].use_connect = False
            obj.data.edit_bones[bone].parent = obj.data.edit_bones[root_bone]
    bpy.ops.object.mode_set(mode='OBJECT')

    # Every bone that has a name starting with "DEF-" make deforming.  All the
    # others make non-deforming.
    for bone in bones:
        if obj.data.bones[bone].name.startswith(DEF_PREFIX):
            obj.data.bones[bone].use_deform = True
        else:
            obj.data.bones[bone].use_deform = False

    # Alter marked driver targets
    for d in obj.animation_data.drivers:
        for v in d.driver.variables:
            for tar in v.targets:
                if tar.data_path.startswith("RIGIFY-"):
                    temp, bone, prop = tuple([x.strip('"]') for x in tar.data_path.split('["')])
                    if bone in obj.data.bones \
                    and prop in obj.pose.bones[bone].keys():
                        tar.data_path = tar.data_path[7:]
                    else:
                        tar.data_path = 'pose.bones["%s"]["%s"]' % (make_original_name(bone), prop)

    # Move all the original bones to their layer.
    for bone in original_bones:
        obj.data.bones[bone].layers = ORG_LAYER

    # Move all the bones with names starting with "MCH-" to their layer.
    for bone in bones:
        if obj.data.bones[bone].name.startswith(MCH_PREFIX):
            obj.data.bones[bone].layers = MCH_LAYER

    # Move all the bones with names starting with "DEF-" to their layer.
    for bone in bones:
        if obj.data.bones[bone].name.startswith(DEF_PREFIX):
            obj.data.bones[bone].layers = DEF_LAYER

    # Create root bone widget
    create_root_widget(obj, "root")

    # Assign shapes to bones
    # Object's with name WGT-<bone_name> get used as that bone's shape.
    for bone in bones:
        wgt_name = (WGT_PREFIX + obj.data.bones[bone].name)[:21]  # Object names are limited to 21 characters... arg
        if wgt_name in context.scene.objects:
            # Weird temp thing because it won't let me index by object name
            for ob in context.scene.objects:
                if ob.name == wgt_name:
                    obj.pose.bones[bone].custom_shape = ob
                    break
            # This is what it should do:
            # obj.pose.bones[bone].custom_shape = context.scene.objects[wgt_name]
    # Reveal all the layers with control bones on them
    vis_layers = [False for n in range(0, 32)]
    for bone in bones:
        for i in range(0, 32):
            vis_layers[i] = vis_layers[i] or obj.data.bones[bone].layers[i]
    for i in range(0, 32):
        vis_layers[i] = vis_layers[i] and not (ORG_LAYER[i] or MCH_LAYER[i] or DEF_LAYER[i])
    obj.data.layers = vis_layers

    # Ensure the collection of layer names exists
    for i in range(1 + len(metarig.data.rigify_layers), 29):
        metarig.data.rigify_layers.add()

    # Create list of layer name/row pairs
    layer_layout = []
    for l in metarig.data.rigify_layers:
        layer_layout += [(l.name, l.row)]

    # Generate the UI script
    if "rig_ui.py" in bpy.data.texts:
        script = bpy.data.texts["rig_ui.py"]
        script.clear()
    else:
        script = bpy.data.texts.new("rig_ui.py")
    script.write(UI_SLIDERS % rig_id)
    for s in ui_scripts:
        script.write("\n        " + s.replace("\n", "\n        ") + "\n")
    script.write(layers_ui(vis_layers, layer_layout))
    script.write(UI_REGISTER)
    script.use_module = True

    # Run UI script
    exec(script.as_string(), {})

    t.tick("The rest: ")
    #----------------------------------
    # Deconfigure
    bpy.ops.object.mode_set(mode='OBJECT')
    metarig.data.pose_position = rest_backup
    obj.data.pose_position = 'POSE'
    def generate(self):
        bpy.ops.object.mode_set(mode='EDIT')

        eb = self.obj.data.edit_bones

        # Create the control bones
        ulimb_ik = copy_bone(
            self.obj, self.org_bones[0],
            pantin_utils.strip_LR_numbers(strip_org(self.org_bones[0])) +
            '.IK' + self.side_suffix)
        flimb_ik = copy_bone(
            self.obj, self.org_bones[1],
            make_mechanism_name(
                pantin_utils.strip_LR_numbers(strip_org(self.org_bones[1])) +
                '.IK' + self.side_suffix))
        elimb_ik = copy_bone(
            self.obj, self.org_bones[2],
            pantin_utils.strip_LR_numbers(strip_org(self.org_bones[2])) +
            '.IK' + self.side_suffix)

        ulimb_str = copy_bone(
            self.obj, self.org_bones[0],
            make_mechanism_name(
                pantin_utils.strip_LR_numbers(strip_org(self.org_bones[0])) +
                ".stretch.ik" + self.side_suffix))
        flimb_str = copy_bone(
            self.obj, self.org_bones[1],
            make_mechanism_name(
                pantin_utils.strip_LR_numbers(strip_org(self.org_bones[1])) +
                ".stretch.ik" + self.side_suffix))
        elimb_str = copy_bone(
            self.obj, self.org_bones[2],
            make_mechanism_name(
                pantin_utils.strip_LR_numbers(strip_org(self.org_bones[2])) +
                ".stretch.ik" + self.side_suffix))

        joint_str = new_bone(
            self.obj,
            pantin_utils.strip_LR_numbers(strip_org(self.org_bones[1])) +
            '.IK' + self.side_suffix)
        eb[joint_str].head = eb[flimb_str].head
        eb[joint_str].tail = (eb[flimb_str].head + Vector(
            (0, 0, 1)) * eb[flimb_str].length / 2)
        align_bone_x_axis(self.obj, joint_str, Vector((-1, 0, 0)))
        # put_bone(self.obj, joint_str, Vector(eb[flimb_str].head))

        # Pelvis follow
        elimb_ik_root = copy_bone(
            self.obj, self.org_bones[2],
            make_mechanism_name(
                pantin_utils.strip_LR_numbers(strip_org(self.org_bones[2])) +
                ".ik_root" + self.side_suffix))
        elimb_ik_parent = copy_bone(
            self.obj, self.org_bones[2],
            make_mechanism_name(
                pantin_utils.strip_LR_numbers(strip_org(self.org_bones[2])) +
                ".ik_parent" + self.side_suffix))
        elimb_ik_socket = copy_bone(
            self.obj, self.org_bones[2],
            make_mechanism_name(
                pantin_utils.strip_LR_numbers(strip_org(self.org_bones[2])) +
                ".ik_socket" + self.side_suffix))

        # Get edit bones
        ulimb_ik_e = eb[ulimb_ik]
        flimb_ik_e = eb[flimb_ik]
        elimb_ik_e = eb[elimb_ik]

        ulimb_str_e = eb[ulimb_str]
        flimb_str_e = eb[flimb_str]
        elimb_str_e = eb[elimb_str]

        joint_str_e = eb[joint_str]

        elimb_ik_root_e = eb[elimb_ik_root]
        elimb_ik_parent_e = eb[elimb_ik_parent]
        elimb_ik_socket_e = eb[elimb_ik_socket]

        # Parenting

        # Side org chain
        for b, o_b in zip(self.side_org_bones[1:], self.org_bones[1:]):
            eb[b].parent = eb[
                pantin_utils.strip_LR_numbers(eb[o_b].parent.name) +
                self.side_suffix]

        if self.org_parent is not None:
            ulimb_ik_e.use_connect = False
            ulimb_ik_e.parent = eb[self.org_parent]

        flimb_ik_e.use_connect = False
        flimb_ik_e.parent = ulimb_ik_e

        elimb_ik_root_e.use_connect = False
        elimb_ik_root_e.parent = None
        elimb_ik_parent_e.use_connect = False
        elimb_ik_parent_e.parent = eb[self.side_org_bones[0]].parent
        elimb_ik_socket_e.use_connect = False
        elimb_ik_socket_e.parent = None
        elimb_ik_e.use_connect = False
        elimb_ik_e.parent = elimb_ik_socket_e

        if self.org_parent is not None:
            ulimb_str_e.use_connect = False
            ulimb_str_e.parent = eb[self.org_parent]

        flimb_str_e.use_connect = False
        flimb_str_e.parent = joint_str_e

        elimb_str_e.use_connect = True
        elimb_str_e.parent = flimb_ik_e

        joint_str_e.use_connect = False
        joint_str_e.parent = ulimb_ik_e

        # Layers
        joint_str_e.layers = elimb_str_e.layers
        # Object mode, get pose bones
        bpy.ops.object.mode_set(mode='OBJECT')
        pb = self.obj.pose.bones

        ulimb_ik_p = pb[ulimb_ik]
        flimb_ik_p = pb[flimb_ik]
        elimb_ik_p = pb[elimb_ik]

        ulimb_str_p = pb[ulimb_str]
        flimb_str_p = pb[flimb_str]
        elimb_str_p = pb[elimb_str]

        joint_str_p = pb[joint_str]

        joint_str_p.lock_location = (False, False, True)
        joint_str_p.lock_rotation = (True, True, False)
        joint_str_p.lock_rotation_w = False
        joint_str_p.lock_scale = (False, False, False)
        joint_str_p.rotation_mode = 'XZY'

        # Set up custom properties
        prop = rna_idprop_ui_prop_get(elimb_ik_p, "pelvis_follow", create=True)
        elimb_ik_p["pelvis_follow"] = int(self.pelvis_follow)
        prop["soft_min"] = 0
        prop["soft_max"] = 1
        prop["min"] = 0
        prop["max"] = 1

        prop = rna_idprop_ui_prop_get(elimb_ik_p, "IK_FK", create=True)
        elimb_ik_p["IK_FK"] = 0
        prop["soft_min"] = 0
        prop["soft_max"] = 1
        prop["min"] = 0
        prop["max"] = 1

        # Constraints
        # Bend hint, ripped off from Rigify' biped
        con = flimb_ik_p.constraints.new('LIMIT_ROTATION')
        con.name = "bend_hint"
        con.use_limit_z = True
        con.min_z = radians(45)
        con.max_z = radians(45)
        con.owner_space = 'LOCAL'

        con = flimb_ik_p.constraints.new('IK')
        con.name = "ik"
        con.target = self.obj
        con.subtarget = elimb_ik
        con.chain_count = 2

        con = ulimb_str_p.constraints.new('COPY_LOCATION')
        con.name = "anchor"
        con.target = self.obj
        con.subtarget = ulimb_ik
        con.target_space = 'LOCAL'
        con.owner_space = 'LOCAL'

        con = elimb_str_p.constraints.new('COPY_ROTATION')
        con.name = "copy rotation"
        con.target = self.obj
        con.subtarget = elimb_ik
        con.target_space = 'POSE'
        con.owner_space = 'POSE'

        con = ulimb_str_p.constraints.new('STRETCH_TO')
        con.name = "stretch to"
        con.target = self.obj
        con.subtarget = joint_str
        con.volume = 'NO_VOLUME'
        con.rest_length = ulimb_str_p.length
        con.keep_axis = 'PLANE_Z'

        con = flimb_str_p.constraints.new('STRETCH_TO')
        con.name = "stretch to"
        con.target = self.obj
        con.subtarget = elimb_str
        con.volume = 'NO_VOLUME'
        con.rest_length = flimb_str_p.length
        con.keep_axis = 'PLANE_Z'

        # Pelvis follow
        con = pb[elimb_ik_socket].constraints.new('COPY_TRANSFORMS')
        con.name = "copy root"
        con.target = self.obj
        con.subtarget = elimb_ik_root

        con = pb[elimb_ik_socket].constraints.new('COPY_TRANSFORMS')
        con.name = "copy socket"
        con.target = self.obj
        con.subtarget = elimb_ik_parent

        # Drivers
        driver = self.obj.driver_add(con.path_from_id("influence"))
        driver.driver.expression = 'pelvis_follow'
        var = driver.driver.variables.new()

        var.type = 'SINGLE_PROP'
        var.name = 'pelvis_follow'
        var.targets[0].id_type = 'OBJECT'
        var.targets[0].id = self.obj
        var.targets[0].data_path = (pb[elimb_ik].path_from_id() +
                                    '["pelvis_follow"]')

        # IK Limits
        ulimb_ik_p.lock_ik_x = True
        ulimb_ik_p.lock_ik_y = True

        flimb_ik_p.lock_ik_x = True
        flimb_ik_p.lock_ik_y = True
        flimb_ik_p.use_ik_limit_z = True
        flimb_ik_p.ik_min_z = radians(self.ik_limits[0])  # 0.0
        flimb_ik_p.ik_max_z = radians(self.ik_limits[1])  # radians(160.0)

        # Arm ik angle fix
        limb_angle = ulimb_ik_p.vector.xz.angle_signed(flimb_ik_p.vector.xz)
        if self.ik_limits[0] < 0:  # folds counterclockwise (arms)
            # has to be slightly less than the original angle
            flimb_ik_p.ik_max_z = -limb_angle - .02
        else:
            # has to be slightly more than the original angle
            flimb_ik_p.ik_min_z = -limb_angle + .02

        return [
            ulimb_ik, ulimb_str, flimb_ik, flimb_str, joint_str, elimb_ik,
            elimb_str
        ]
Example #4
0
    def gen_control(self):
        """ Generate the control rig.

        """
        #---------------------------------
        # Create the neck and head controls
        bpy.ops.object.mode_set(mode='EDIT')

        # Create bones
        neck_ctrl = copy_bone(self.obj, self.org_bones[0], strip_org(self.org_bones[0]))
        neck_follow = copy_bone(self.obj, self.org_bones[-1], make_mechanism_name(strip_org(self.org_bones[0] + ".follow")))
        neck_child = new_bone(self.obj, make_mechanism_name(strip_org(self.org_bones[0] + ".child")))

        head_ctrl = copy_bone(self.obj, self.org_bones[-1], strip_org(self.org_bones[-1]))
        head_mch = new_bone(self.obj, make_mechanism_name(strip_org(self.org_bones[-1])))
        if self.isolate:
            head_socket1 = copy_bone(self.obj, self.org_bones[-1], make_mechanism_name(strip_org(self.org_bones[-1] + ".socket1")))
            head_socket2 = copy_bone(self.obj, self.org_bones[-1], make_mechanism_name(strip_org(self.org_bones[-1] + ".socket2")))

        # Create neck chain bones
        neck = []
        helpers = []
        for name in self.org_bones:
            neck += [copy_bone(self.obj, name, make_mechanism_name(strip_org(name)))]
            helpers += [copy_bone(self.obj, neck_child, make_mechanism_name(strip_org(name + ".02")))]

        # Fetch edit bones
        eb = self.obj.data.edit_bones

        neck_ctrl_e = eb[neck_ctrl]
        neck_follow_e = eb[neck_follow]
        neck_child_e = eb[neck_child]
        head_ctrl_e = eb[head_ctrl]
        head_mch_e = eb[head_mch]
        if self.isolate:
            head_socket1_e = eb[head_socket1]
            head_socket2_e = eb[head_socket2]

        # Parenting
        head_ctrl_e.use_connect = False
        head_ctrl_e.parent = neck_ctrl_e.parent
        head_mch_e.use_connect = False
        head_mch_e.parent = head_ctrl_e

        if self.isolate:
            head_socket1_e.use_connect = False
            head_socket1_e.parent = neck_ctrl_e.parent

            head_socket2_e.use_connect = False
            head_socket2_e.parent = None

            head_ctrl_e.parent = head_socket2_e

        for (name1, name2) in zip(neck, helpers):
            eb[name1].use_connect = False
            eb[name1].parent = eb[name2]
            eb[name2].use_connect = False
            eb[name2].parent = neck_ctrl_e.parent

        neck_follow_e.use_connect = False
        neck_follow_e.parent = neck_ctrl_e.parent
        neck_child_e.use_connect = False
        neck_child_e.parent = neck_ctrl_e
        neck_ctrl_e.parent = neck_follow_e

        # Position
        put_bone(self.obj, neck_follow, neck_ctrl_e.head)
        put_bone(self.obj, neck_child, neck_ctrl_e.head)
        put_bone(self.obj, head_ctrl, neck_ctrl_e.head)
        put_bone(self.obj, head_mch, neck_ctrl_e.head)
        head_mch_e.length = head_ctrl_e.length / 2
        neck_child_e.length = neck_ctrl_e.length / 2

        if self.isolate:
            put_bone(self.obj, head_socket1, neck_ctrl_e.head)
            head_mch_e.length /= 2

            put_bone(self.obj, head_socket2, neck_ctrl_e.head)
            head_mch_e.length /= 3

        for (name1, name2) in zip(neck, helpers):
            put_bone(self.obj, name2, eb[name1].head)
            eb[name2].length = eb[name1].length / 2

        # Switch to object mode
        bpy.ops.object.mode_set(mode='OBJECT')
        pb = self.obj.pose.bones
        neck_ctrl_p = pb[neck_ctrl]
        neck_follow_p = pb[neck_follow]
        # neck_child_p = pb[neck_child]  # UNUSED
        head_ctrl_p = pb[head_ctrl]
        if self.isolate:
            # head_socket1_p = pb[head_socket1]  # UNUSED
            head_socket2_p = pb[head_socket2]

        # Custom bone appearance
        neck_ctrl_p.custom_shape_transform = pb[self.org_bones[(len(self.org_bones) - 1) // 2]]
        head_ctrl_p.custom_shape_transform = pb[self.org_bones[-1]]

        # Custom properties
        prop = rna_idprop_ui_prop_get(head_ctrl_p, "inf_extent", create=True)
        head_ctrl_p["inf_extent"] = 0.5
        prop["min"] = 0.0
        prop["max"] = 1.0
        prop["soft_min"] = 0.0
        prop["soft_max"] = 1.0

        prop = rna_idprop_ui_prop_get(head_ctrl_p, "neck_follow", create=True)
        head_ctrl_p["neck_follow"] = 1.0
        prop["min"] = 0.0
        prop["max"] = 2.0
        prop["soft_min"] = 0.0
        prop["soft_max"] = 1.0

        if self.isolate:
            prop = rna_idprop_ui_prop_get(head_ctrl_p, "isolate", create=True)
            head_ctrl_p["isolate"] = 0.0
            prop["min"] = 0.0
            prop["max"] = 1.0
            prop["soft_min"] = 0.0
            prop["soft_max"] = 1.0

        # Constraints

        # Neck follow
        con = neck_follow_p.constraints.new('COPY_ROTATION')
        con.name = "copy_rotation"
        con.target = self.obj
        con.subtarget = head_ctrl

        fcurve = con.driver_add("influence")
        driver = fcurve.driver
        var = driver.variables.new()
        driver.type = 'SCRIPTED'
        var.name = "follow"
        var.targets[0].id_type = 'OBJECT'
        var.targets[0].id = self.obj
        var.targets[0].data_path = head_ctrl_p.path_from_id() + '["neck_follow"]'
        driver.expression = "follow / 2"

        # Isolate
        if self.isolate:
            con = head_socket2_p.constraints.new('COPY_LOCATION')
            con.name = "copy_location"
            con.target = self.obj
            con.subtarget = head_socket1

            con = head_socket2_p.constraints.new('COPY_TRANSFORMS')
            con.name = "copy_transforms"
            con.target = self.obj
            con.subtarget = head_socket1

            fcurve = con.driver_add("influence")
            driver = fcurve.driver
            var = driver.variables.new()
            driver.type = 'SCRIPTED'
            var.name = "isolate"
            var.targets[0].id_type = 'OBJECT'
            var.targets[0].id = self.obj
            var.targets[0].data_path = head_ctrl_p.path_from_id() + '["isolate"]'
            driver.expression = "1.0 - isolate"

        # Neck chain
        first = True
        prev = None
        i = 0
        l = len(neck)
        for (name1, name2, org_name) in zip(neck, helpers, self.org_bones):
            con = pb[org_name].constraints.new('COPY_TRANSFORMS')
            con.name = "copy_transforms"
            con.target = self.obj
            con.subtarget = name1

            n_con = pb[name2].constraints.new('COPY_TRANSFORMS')
            n_con.name = "neck"
            n_con.target = self.obj
            n_con.subtarget = neck_child

            h_con = pb[name2].constraints.new('COPY_TRANSFORMS')
            h_con.name = "head"
            h_con.target = self.obj
            h_con.subtarget = head_mch

            con = pb[name2].constraints.new('COPY_LOCATION')
            con.name = "anchor"
            con.target = self.obj
            if first:
                con.subtarget = neck_ctrl
            else:
                con.subtarget = prev
                con.head_tail = 1.0

            # Drivers
            n = (i + 1) / l

            # Neck influence
            fcurve = n_con.driver_add("influence")
            driver = fcurve.driver
            var = driver.variables.new()
            driver.type = 'SCRIPTED'
            var.name = "ext"
            var.targets[0].id_type = 'OBJECT'
            var.targets[0].id = self.obj
            var.targets[0].data_path = head_ctrl_p.path_from_id() + '["inf_extent"]'
            driver.expression = "1.0 if (%.4f > (1.0-ext) or (1.0-ext) == 0.0) else (%.4f / (1.0-ext))" % (n, n)

            # Head influence
            if (i + 1) == l:
                h_con.influence = 1.0
            else:
                fcurve = h_con.driver_add("influence")
                driver = fcurve.driver
                var = driver.variables.new()
                driver.type = 'SCRIPTED'
                var.name = "ext"
                var.targets[0].id_type = 'OBJECT'
                var.targets[0].id = self.obj
                var.targets[0].data_path = head_ctrl_p.path_from_id() + '["inf_extent"]'
                driver.expression = "0.0 if (%.4f <= (1.0-ext)) else ((%.4f - (1.0-ext)) / ext)" % (n, n)

            first = False
            prev = name1
            i += 1

        # Create control widgets
        w1 = create_circle_widget(self.obj, neck_ctrl, radius=1.0, head_tail=0.5)
        w2 = create_circle_widget(self.obj, head_ctrl, radius=1.0, head_tail=0.5)

        if w1 != None:
            obj_to_bone(w1, self.obj, self.org_bones[(len(self.org_bones) - 1) // 2])
        if w2 != None:
            obj_to_bone(w2, self.obj, self.org_bones[-1])

        # Return control bones
        return (head_ctrl, neck_ctrl)
Example #5
0
def generate_rig(context, metarig):
    """ Generates a rig from a metarig.

    """
    t = Timer()

    # Random string with time appended so that
    # different rigs don't collide id's
    rig_id = random_id(16)

    # Initial configuration
    # mode_orig = context.mode  # UNUSED
    rest_backup = metarig.data.pose_position
    metarig.data.pose_position = 'REST'

    bpy.ops.object.mode_set(mode='OBJECT')

    scene = context.scene

    #------------------------------------------
    # Create/find the rig object and set it up

    # Check if the generated rig already exists, so we can
    # regenerate in the same object.  If not, create a new
    # object to generate the rig in.
    print("Fetch rig.")
    try:
        name = metarig["rig_object_name"]
    except KeyError:
        name = "rig"

    try:
        obj = scene.objects[name]
    except KeyError:
        obj = bpy.data.objects.new(name, bpy.data.armatures.new(name))
        obj.draw_type = 'WIRE'
        scene.objects.link(obj)

    obj.data.pose_position = 'POSE'

    # Get rid of anim data in case the rig already existed
    print("Clear rig animation data.")
    obj.animation_data_clear()

    # Select generated rig object
    metarig.select = False
    obj.select = True
    scene.objects.active = obj

    # Remove all bones from the generated rig armature.
    bpy.ops.object.mode_set(mode='EDIT')
    for bone in obj.data.edit_bones:
        obj.data.edit_bones.remove(bone)
    bpy.ops.object.mode_set(mode='OBJECT')

    # Create temporary duplicates for merging
    temp_rig_1 = metarig.copy()
    temp_rig_1.data = metarig.data.copy()
    scene.objects.link(temp_rig_1)

    temp_rig_2 = metarig.copy()
    temp_rig_2.data = obj.data
    scene.objects.link(temp_rig_2)

    # Select the temp rigs for merging
    for objt in scene.objects:
        objt.select = False  # deselect all objects
    temp_rig_1.select = True
    temp_rig_2.select = True
    scene.objects.active = temp_rig_2

    # Merge the temporary rigs
    bpy.ops.object.join()

    # Delete the second temp rig
    bpy.ops.object.delete()

    # Select the generated rig
    for objt in scene.objects:
        objt.select = False  # deselect all objects
    obj.select = True
    scene.objects.active = obj

    # Copy over bone properties
    for bone in metarig.data.bones:
        bone_gen = obj.data.bones[bone.name]

        # B-bone stuff
        bone_gen.bbone_segments = bone.bbone_segments
        bone_gen.bbone_in = bone.bbone_in
        bone_gen.bbone_out = bone.bbone_out

    # Copy over the pose_bone properties
    for bone in metarig.pose.bones:
        bone_gen = obj.pose.bones[bone.name]

        # Rotation mode and transform locks
        bone_gen.rotation_mode = bone.rotation_mode
        bone_gen.lock_rotation = tuple(bone.lock_rotation)
        bone_gen.lock_rotation_w = bone.lock_rotation_w
        bone_gen.lock_rotations_4d = bone.lock_rotations_4d
        bone_gen.lock_location = tuple(bone.lock_location)
        bone_gen.lock_scale = tuple(bone.lock_scale)

        # Custom properties
        for prop in bone.keys():
            bone_gen[prop] = bone[prop]

        # Constraints
        for con1 in bone.constraints:
            con2 = bone_gen.constraints.new(type=con1.type)
            copy_attributes(con1, con2)

            # Set metarig target to rig target
            if "target" in dir(con2):
                if con2.target == metarig:
                    con2.target = obj

    # Copy drivers
    if metarig.animation_data:
        for d1 in metarig.animation_data.drivers:
            d2 = obj.driver_add(d1.data_path)
            copy_attributes(d1, d2)
            copy_attributes(d1.driver, d2.driver)

            # Remove default modifiers, variables, etc.
            for m in d2.modifiers:
                d2.modifiers.remove(m)
            for v in d2.driver.variables:
                d2.driver.variables.remove(v)

            # Copy modifiers
            for m1 in d1.modifiers:
                m2 = d2.modifiers.new(type=m1.type)
                copy_attributes(m1, m2)

            # Copy variables
            for v1 in d1.driver.variables:
                v2 = d2.driver.variables.new()
                copy_attributes(v1, v2)
                for i in range(len(v1.targets)):
                    copy_attributes(v1.targets[i], v2.targets[i])
                    # Switch metarig targets to rig targets
                    if v2.targets[i].id == metarig:
                        v2.targets[i].id = obj

                    # Mark targets that may need to be altered after rig generation
                    tar = v2.targets[i]
                    # If a custom property
                    if v2.type == 'SINGLE_PROP' \
                    and re.match('^pose.bones\["[^"\]]*"\]\["[^"\]]*"\]$', tar.data_path):
                        tar.data_path = "RIGIFY-" + tar.data_path

            # Copy key frames
            for i in range(len(d1.keyframe_points)):
                d2.keyframe_points.add()
                k1 = d1.keyframe_points[i]
                k2 = d2.keyframe_points[i]
                copy_attributes(k1, k2)

    t.tick("Duplicate rig: ")
    #----------------------------------
    # Make a list of the original bones so we can keep track of them.
    original_bones = [bone.name for bone in obj.data.bones]

    # Add the ORG_PREFIX to the original bones.
    bpy.ops.object.mode_set(mode='OBJECT')
    for i in range(0, len(original_bones)):
        obj.data.bones[original_bones[i]].name = make_original_name(
            original_bones[i])
        original_bones[i] = make_original_name(original_bones[i])

    # Create a sorted list of the original bones, sorted in the order we're
    # going to traverse them for rigging.
    # (root-most -> leaf-most, alphabetical)
    bones_sorted = []
    for name in original_bones:
        bones_sorted += [name]
    bones_sorted.sort()  # first sort by names
    bones_sorted.sort(key=lambda bone: len(obj.pose.bones[
        bone].parent_recursive))  # then parents before children

    t.tick("Make list of org bones: ")
    #----------------------------------
    # Create the root bone.
    bpy.ops.object.mode_set(mode='EDIT')
    root_bone = new_bone(obj, ROOT_NAME)
    obj.data.edit_bones[root_bone].head = (0, 0, 0)
    obj.data.edit_bones[root_bone].tail = (0, 1, 0)
    obj.data.edit_bones[root_bone].roll = 0
    bpy.ops.object.mode_set(mode='OBJECT')
    obj.data.bones[root_bone].layers = ROOT_LAYER
    # Put the rig_name in the armature custom properties
    rna_idprop_ui_prop_get(obj.data, "rig_id", create=True)
    obj.data["rig_id"] = rig_id

    t.tick("Create root bone: ")
    #----------------------------------
    try:
        # Collect/initialize all the rigs.
        rigs = []
        deformation_rigs = []
        for bone in bones_sorted:
            bpy.ops.object.mode_set(mode='EDIT')
            rigs += get_bone_rigs(obj, bone)
        t.tick("Initialize rigs: ")

        # Generate all the rigs.
        ui_scripts = []
        for rig in rigs:
            # Go into editmode in the rig armature
            bpy.ops.object.mode_set(mode='OBJECT')
            context.scene.objects.active = obj
            obj.select = True
            bpy.ops.object.mode_set(mode='EDIT')
            scripts = rig.generate()
            if scripts != None:
                ui_scripts += [scripts[0]]
        t.tick("Generate rigs: ")
    except Exception as e:
        # Cleanup if something goes wrong
        print("Rigify: failed to generate rig.")
        metarig.data.pose_position = rest_backup
        obj.data.pose_position = 'POSE'
        bpy.ops.object.mode_set(mode='OBJECT')

        # Continue the exception
        raise e

    #----------------------------------
    bpy.ops.object.mode_set(mode='OBJECT')

    # Get a list of all the bones in the armature
    bones = [bone.name for bone in obj.data.bones]

    # Parent any free-floating bones to the root.
    bpy.ops.object.mode_set(mode='EDIT')
    for bone in bones:
        if obj.data.edit_bones[bone].parent is None:
            obj.data.edit_bones[bone].use_connect = False
            obj.data.edit_bones[bone].parent = obj.data.edit_bones[root_bone]
    bpy.ops.object.mode_set(mode='OBJECT')

    # Every bone that has a name starting with "DEF-" make deforming.  All the
    # others make non-deforming.
    for bone in bones:
        if obj.data.bones[bone].name.startswith(DEF_PREFIX):
            obj.data.bones[bone].use_deform = True
        else:
            obj.data.bones[bone].use_deform = False

    # Alter marked driver targets
    for d in obj.animation_data.drivers:
        for v in d.driver.variables:
            for tar in v.targets:
                if tar.data_path.startswith("RIGIFY-"):
                    temp, bone, prop = tuple(
                        [x.strip('"]') for x in tar.data_path.split('["')])
                    if bone in obj.data.bones \
                    and prop in obj.pose.bones[bone].keys():
                        tar.data_path = tar.data_path[7:]
                    else:
                        tar.data_path = 'pose.bones["%s"]["%s"]' % (
                            make_original_name(bone), prop)

    # Move all the original bones to their layer.
    for bone in original_bones:
        obj.data.bones[bone].layers = ORG_LAYER

    # Move all the bones with names starting with "MCH-" to their layer.
    for bone in bones:
        if obj.data.bones[bone].name.startswith(MCH_PREFIX):
            obj.data.bones[bone].layers = MCH_LAYER

    # Move all the bones with names starting with "DEF-" to their layer.
    for bone in bones:
        if obj.data.bones[bone].name.startswith(DEF_PREFIX):
            obj.data.bones[bone].layers = DEF_LAYER

    # Create root bone widget
    create_root_widget(obj, "root")

    # Assign shapes to bones
    # Object's with name WGT-<bone_name> get used as that bone's shape.
    for bone in bones:
        wgt_name = (WGT_PREFIX + obj.data.bones[bone].name
                    )[:21]  # Object names are limited to 21 characters... arg
        if wgt_name in context.scene.objects:
            # Weird temp thing because it won't let me index by object name
            for ob in context.scene.objects:
                if ob.name == wgt_name:
                    obj.pose.bones[bone].custom_shape = ob
                    break
            # This is what it should do:
            # obj.pose.bones[bone].custom_shape = context.scene.objects[wgt_name]
    # Reveal all the layers with control bones on them
    vis_layers = [False for n in range(0, 32)]
    for bone in bones:
        for i in range(0, 32):
            vis_layers[i] = vis_layers[i] or obj.data.bones[bone].layers[i]
    for i in range(0, 32):
        vis_layers[i] = vis_layers[i] and not (ORG_LAYER[i] or MCH_LAYER[i]
                                               or DEF_LAYER[i])
    obj.data.layers = vis_layers

    # Ensure the collection of layer names exists
    for i in range(1 + len(metarig.data.rigify_layers), 29):
        metarig.data.rigify_layers.add()

    # Create list of layer name/row pairs
    layer_layout = []
    for l in metarig.data.rigify_layers:
        layer_layout += [(l.name, l.row)]

    # Generate the UI script
    if "rig_ui.py" in bpy.data.texts:
        script = bpy.data.texts["rig_ui.py"]
        script.clear()
    else:
        script = bpy.data.texts.new("rig_ui.py")
    script.write(UI_SLIDERS % rig_id)
    for s in ui_scripts:
        script.write("\n        " + s.replace("\n", "\n        ") + "\n")
    script.write(layers_ui(vis_layers, layer_layout))
    script.write(UI_REGISTER)
    script.use_module = True

    # Run UI script
    exec(script.as_string(), {})

    t.tick("The rest: ")
    #----------------------------------
    # Deconfigure
    bpy.ops.object.mode_set(mode='OBJECT')
    metarig.data.pose_position = rest_backup
    obj.data.pose_position = 'POSE'
Example #6
0
    def gen_control(self):
        """ Generate the control rig.

        """
        bpy.ops.object.mode_set(mode='EDIT')
        eb = self.obj.data.edit_bones
        #-------------------------
        # Get rest slide position
        a = self.pivot_rest * len(self.org_bones)
        i = floor(a)
        a -= i
        if i == len(self.org_bones):
            i -= 1
            a = 1.0

        pivot_rest_pos = eb[self.org_bones[i]].head.copy()
        pivot_rest_pos += eb[self.org_bones[i]].vector * a

        #----------------------
        # Create controls

        # Create control bones
        controls = []
        for i in self.control_indices:
            name = copy_bone(self.obj, self.org_bones[i], strip_org(self.org_bones[i]))
            controls += [name]

        # Create control parents
        control_parents = []
        for i in self.control_indices[1:-1]:
            name = new_bone(self.obj, make_mechanism_name("par_" + strip_org(self.org_bones[i])))
            control_parents += [name]

        # Create sub-control bones
        subcontrols = []
        for i in self.control_indices:
            name = new_bone(self.obj, make_mechanism_name("sub_" + strip_org(self.org_bones[i])))
            subcontrols += [name]

        # Create main control bone
        main_control = new_bone(self.obj, self.params.spine_main_control_name)

        # Create main control WGT bones
        main_wgt1 = new_bone(self.obj, make_mechanism_name(self.params.spine_main_control_name + ".01"))
        main_wgt2 = new_bone(self.obj, make_mechanism_name(self.params.spine_main_control_name + ".02"))

        eb = self.obj.data.edit_bones

        # Parent the main control
        eb[main_control].use_connect = False
        eb[main_control].parent = eb[self.org_bones[0]].parent

        # Parent the main WGTs
        eb[main_wgt1].use_connect = False
        eb[main_wgt1].parent = eb[main_control]
        eb[main_wgt2].use_connect = False
        eb[main_wgt2].parent = eb[main_wgt1]

        # Parent the controls and sub-controls
        for name, subname in zip(controls, subcontrols):
            eb[name].use_connect = False
            eb[name].parent = eb[main_control]
            eb[subname].use_connect = False
            eb[subname].parent = eb[name]

        # Parent the control parents
        for name, par_name in zip(controls[1:-1], control_parents):
            eb[par_name].use_connect = False
            eb[par_name].parent = eb[main_control]
            eb[name].parent = eb[par_name]

        # Position the main bone
        put_bone(self.obj, main_control, pivot_rest_pos)
        eb[main_control].length = sum([eb[b].length for b in self.org_bones]) / 2

        # Position the main WGTs
        eb[main_wgt1].tail = (0.0, 0.0, sum([eb[b].length for b in self.org_bones]) / 4)
        eb[main_wgt2].length = sum([eb[b].length for b in self.org_bones]) / 4
        put_bone(self.obj, main_wgt1, pivot_rest_pos)
        put_bone(self.obj, main_wgt2, pivot_rest_pos)

        # Position the controls and sub-controls
        pos = eb[controls[0]].head.copy()
        for name, subname in zip(controls, subcontrols):
            put_bone(self.obj, name, pivot_rest_pos)
            put_bone(self.obj, subname, pivot_rest_pos)
            eb[subname].length = eb[name].length / 3

        # Position the control parents
        for name, par_name in zip(controls[1:-1], control_parents):
            put_bone(self.obj, par_name, pivot_rest_pos)
            eb[par_name].length = eb[name].length / 2

        #-----------------------------------------
        # Control bone constraints and properties
        bpy.ops.object.mode_set(mode='OBJECT')
        pb = self.obj.pose.bones

        # Lock control locations
        for name in controls:
            bone = pb[name]
            bone.lock_location = True, True, True

        # Main control doesn't use local location
        pb[main_control].bone.use_local_location = False

        # Intermediate controls follow hips and spine
        for name, par_name, i in zip(controls[1:-1], control_parents, self.control_indices[1:-1]):
            bone = pb[par_name]

            # Custom bend_alpha property
            prop = rna_idprop_ui_prop_get(pb[name], "bend_alpha", create=True)
            pb[name]["bend_alpha"] = i / (len(self.org_bones) - 1)  # set bend alpha
            prop["min"] = 0.0
            prop["max"] = 1.0
            prop["soft_min"] = 0.0
            prop["soft_max"] = 1.0

            # Custom auto_rotate
            prop = rna_idprop_ui_prop_get(pb[name], "auto_rotate", create=True)
            pb[name]["auto_rotate"] = 1.0
            prop["min"] = 0.0
            prop["max"] = 1.0
            prop["soft_min"] = 0.0
            prop["soft_max"] = 1.0

            # Constraints
            con1 = bone.constraints.new('COPY_TRANSFORMS')
            con1.name = "copy_transforms"
            con1.target = self.obj
            con1.subtarget = subcontrols[0]

            con2 = bone.constraints.new('COPY_TRANSFORMS')
            con2.name = "copy_transforms"
            con2.target = self.obj
            con2.subtarget = subcontrols[-1]

            # Drivers
            fcurve = con1.driver_add("influence")
            driver = fcurve.driver
            driver.type = 'AVERAGE'
            var = driver.variables.new()
            var.name = "auto"
            var.targets[0].id_type = 'OBJECT'
            var.targets[0].id = self.obj
            var.targets[0].data_path = pb[name].path_from_id() + '["auto_rotate"]'

            fcurve = con2.driver_add("influence")
            driver = fcurve.driver
            driver.type = 'SCRIPTED'
            driver.expression = "alpha * auto"
            var = driver.variables.new()
            var.name = "alpha"
            var.targets[0].id_type = 'OBJECT'
            var.targets[0].id = self.obj
            var.targets[0].data_path = pb[name].path_from_id() + '["bend_alpha"]'
            var = driver.variables.new()
            var.name = "auto"
            var.targets[0].id_type = 'OBJECT'
            var.targets[0].id = self.obj
            var.targets[0].data_path = pb[name].path_from_id() + '["auto_rotate"]'

        #-------------------------
        # Create flex spine chain
        bpy.ops.object.mode_set(mode='EDIT')
        flex_bones = []
        flex_subs = []
        prev_bone = None
        for b in self.org_bones:
            # Create bones
            bone = copy_bone(self.obj, b, make_mechanism_name(strip_org(b) + ".flex"))
            sub = new_bone(self.obj, make_mechanism_name(strip_org(b) + ".flex_s"))
            flex_bones += [bone]
            flex_subs += [sub]

            eb = self.obj.data.edit_bones
            bone_e = eb[bone]
            sub_e = eb[sub]

            # Parenting
            bone_e.use_connect = False
            sub_e.use_connect = False
            if prev_bone is None:
                sub_e.parent = eb[controls[0]]
            else:
                sub_e.parent = eb[prev_bone]
            bone_e.parent = sub_e

            # Position
            put_bone(self.obj, sub, bone_e.head)
            sub_e.length = bone_e.length / 4
            if prev_bone is not None:
                sub_e.use_connect = True

            prev_bone = bone

        #----------------------------
        # Create reverse spine chain

        # Create bones/parenting/positioning
        bpy.ops.object.mode_set(mode='EDIT')
        rev_bones = []
        prev_bone = None
        for b in zip(flex_bones, self.org_bones):
            # Create bones
            bone = copy_bone(self.obj, b[1], make_mechanism_name(strip_org(b[1]) + ".reverse"))
            rev_bones += [bone]
            eb = self.obj.data.edit_bones
            bone_e = eb[bone]

            # Parenting
            bone_e.use_connect = False
            bone_e.parent = eb[b[0]]

            # Position
            flip_bone(self.obj, bone)
            bone_e.tail = Vector(eb[b[0]].head)
            #bone_e.head = Vector(eb[b[0]].tail)
            if prev_bone is None:
                put_bone(self.obj, bone, pivot_rest_pos)
            else:
                put_bone(self.obj, bone, eb[prev_bone].tail)

            prev_bone = bone

        # Constraints
        bpy.ops.object.mode_set(mode='OBJECT')
        pb = self.obj.pose.bones
        prev_bone = None
        for bone in rev_bones:
            bone_p = pb[bone]

            con = bone_p.constraints.new('COPY_LOCATION')
            con.name = "copy_location"
            con.target = self.obj
            if prev_bone is None:
                con.subtarget = main_control
            else:
                con.subtarget = prev_bone
                con.head_tail = 1.0
            prev_bone = bone

        #----------------------------------------
        # Constrain original bones to flex spine
        bpy.ops.object.mode_set(mode='OBJECT')
        pb = self.obj.pose.bones

        for obone, fbone in zip(self.org_bones, flex_bones):
            con = pb[obone].constraints.new('COPY_TRANSFORMS')
            con.name = "copy_transforms"
            con.target = self.obj
            con.subtarget = fbone

        #---------------------------
        # Create pivot slide system
        pb = self.obj.pose.bones
        bone_p = pb[self.org_bones[0]]
        main_control_p = pb[main_control]

        # Custom pivot_slide property
        prop = rna_idprop_ui_prop_get(main_control_p, "pivot_slide", create=True)
        main_control_p["pivot_slide"] = self.pivot_rest
        prop["min"] = 0.0
        prop["max"] = 1.0
        prop["soft_min"] = 1.0 / len(self.org_bones)
        prop["soft_max"] = 1.0 - (1.0 / len(self.org_bones))

        # Anchor constraints
        con = bone_p.constraints.new('COPY_LOCATION')
        con.name = "copy_location"
        con.target = self.obj
        con.subtarget = rev_bones[0]

        con = pb[main_wgt1].constraints.new('COPY_ROTATION')
        con.name = "copy_rotation"
        con.target = self.obj
        con.subtarget = rev_bones[0]

        # Slide constraints
        i = 1
        tot = len(rev_bones)
        for rb in rev_bones:
            con = bone_p.constraints.new('COPY_LOCATION')
            con.name = "slide." + str(i)
            con.target = self.obj
            con.subtarget = rb
            con.head_tail = 1.0

            # Driver
            fcurve = con.driver_add("influence")
            driver = fcurve.driver
            var = driver.variables.new()
            driver.type = 'AVERAGE'
            var.name = "slide"
            var.targets[0].id_type = 'OBJECT'
            var.targets[0].id = self.obj
            var.targets[0].data_path = main_control_p.path_from_id() + '["pivot_slide"]'
            mod = fcurve.modifiers[0]
            mod.poly_order = 1
            mod.coefficients[0] = 1 - i
            mod.coefficients[1] = tot

            # Main WGT
            con = pb[main_wgt1].constraints.new('COPY_ROTATION')
            con.name = "slide." + str(i)
            con.target = self.obj
            con.subtarget = rb

            # Driver
            fcurve = con.driver_add("influence")
            driver = fcurve.driver
            var = driver.variables.new()
            driver.type = 'AVERAGE'
            var.name = "slide"
            var.targets[0].id_type = 'OBJECT'
            var.targets[0].id = self.obj
            var.targets[0].data_path = main_control_p.path_from_id() + '["pivot_slide"]'
            mod = fcurve.modifiers[0]
            mod.poly_order = 1
            mod.coefficients[0] = 1.5 - i
            mod.coefficients[1] = tot

            i += 1

        #----------------------------------
        # Constrain flex spine to controls
        bpy.ops.object.mode_set(mode='OBJECT')
        pb = self.obj.pose.bones

        # Constrain the bones that correspond exactly to the controls
        for i, name in zip(self.control_indices, subcontrols):
            con = pb[flex_subs[i]].constraints.new('COPY_TRANSFORMS')
            con.name = "copy_transforms"
            con.target = self.obj
            con.subtarget = name

        # Constrain the bones in-between the controls
        for i, j, name1, name2 in zip(self.control_indices, self.control_indices[1:], subcontrols, subcontrols[1:]):
            if (i + 1) < j:
                for n in range(i + 1, j):
                    bone = pb[flex_subs[n]]
                    # Custom bend_alpha property
                    prop = rna_idprop_ui_prop_get(bone, "bend_alpha", create=True)
                    bone["bend_alpha"] = (n - i) / (j - i)  # set bend alpha
                    prop["min"] = 0.0
                    prop["max"] = 1.0
                    prop["soft_min"] = 0.0
                    prop["soft_max"] = 1.0

                    con = bone.constraints.new('COPY_TRANSFORMS')
                    con.name = "copy_transforms"
                    con.target = self.obj
                    con.subtarget = name1

                    con = bone.constraints.new('COPY_TRANSFORMS')
                    con.name = "copy_transforms"
                    con.target = self.obj
                    con.subtarget = name2

                    # Driver
                    fcurve = con.driver_add("influence")
                    driver = fcurve.driver
                    var = driver.variables.new()
                    driver.type = 'AVERAGE'
                    var.name = "alpha"
                    var.targets[0].id_type = 'OBJECT'
                    var.targets[0].id = self.obj
                    var.targets[0].data_path = bone.path_from_id() + '["bend_alpha"]'

        #-------------
        # Final stuff
        bpy.ops.object.mode_set(mode='OBJECT')
        pb = self.obj.pose.bones

        # Control appearance
        # Main
        pb[main_control].custom_shape_transform = pb[main_wgt2]
        w = create_compass_widget(self.obj, main_control)
        if w != None:
            obj_to_bone(w, self.obj, main_wgt2)

        # Spines
        for name, i in zip(controls[1:-1], self.control_indices[1:-1]):
            pb[name].custom_shape_transform = pb[self.org_bones[i]]
            # Create control widgets
            w = create_circle_widget(self.obj, name, radius=1.0, head_tail=0.5, with_line=True)
            if w != None:
                obj_to_bone(w, self.obj, self.org_bones[i])
        # Hips
        pb[controls[0]].custom_shape_transform = pb[self.org_bones[0]]
        # Create control widgets
        w = create_circle_widget(self.obj, controls[0], radius=1.0, head_tail=0.5, with_line=True)
        if w != None:
            obj_to_bone(w, self.obj, self.org_bones[0])

        # Ribs
        pb[controls[-1]].custom_shape_transform = pb[self.org_bones[-1]]
        # Create control widgets
        w = create_circle_widget(self.obj, controls[-1], radius=1.0, head_tail=0.5, with_line=True)
        if w != None:
            obj_to_bone(w, self.obj, self.org_bones[-1])

        # Layers
        pb[main_control].bone.layers = pb[self.org_bones[0]].bone.layers

        return [main_control] + controls
    def gen_control(self):
        """ Generate the control rig.

        """
        #---------------------------------
        # Create the neck and head controls
        bpy.ops.object.mode_set(mode='EDIT')

        # Create bones
        neck_ctrl = copy_bone(self.obj, self.org_bones[0], strip_org(self.org_bones[0]))
        neck_follow = copy_bone(self.obj, self.org_bones[-1], make_mechanism_name(strip_org(self.org_bones[0] + ".follow")))
        neck_child = new_bone(self.obj, make_mechanism_name(strip_org(self.org_bones[0] + ".child")))

        head_ctrl = copy_bone(self.obj, self.org_bones[-1], strip_org(self.org_bones[-1]))
        head_mch = new_bone(self.obj, make_mechanism_name(strip_org(self.org_bones[-1])))
        if self.isolate:
            head_socket1 = copy_bone(self.obj, self.org_bones[-1], make_mechanism_name(strip_org(self.org_bones[-1] + ".socket1")))
            head_socket2 = copy_bone(self.obj, self.org_bones[-1], make_mechanism_name(strip_org(self.org_bones[-1] + ".socket2")))

        # Create neck chain bones
        neck = []
        helpers = []
        for name in self.org_bones:
            neck += [copy_bone(self.obj, name, make_mechanism_name(strip_org(name)))]
            helpers += [copy_bone(self.obj, neck_child, make_mechanism_name(strip_org(name + ".02")))]

        # Fetch edit bones
        eb = self.obj.data.edit_bones

        neck_ctrl_e = eb[neck_ctrl]
        neck_follow_e = eb[neck_follow]
        neck_child_e = eb[neck_child]
        head_ctrl_e = eb[head_ctrl]
        head_mch_e = eb[head_mch]
        if self.isolate:
            head_socket1_e = eb[head_socket1]
            head_socket2_e = eb[head_socket2]

        # Parenting
        head_ctrl_e.use_connect = False
        head_ctrl_e.parent = neck_ctrl_e.parent
        head_mch_e.use_connect = False
        head_mch_e.parent = head_ctrl_e

        if self.isolate:
            head_socket1_e.use_connect = False
            head_socket1_e.parent = neck_ctrl_e.parent

            head_socket2_e.use_connect = False
            head_socket2_e.parent = None

            head_ctrl_e.parent = head_socket2_e

        for (name1, name2) in zip(neck, helpers):
            eb[name1].use_connect = False
            eb[name1].parent = eb[name2]
            eb[name2].use_connect = False
            eb[name2].parent = neck_ctrl_e.parent

        neck_follow_e.use_connect = False
        neck_follow_e.parent = neck_ctrl_e.parent
        neck_child_e.use_connect = False
        neck_child_e.parent = neck_ctrl_e
        neck_ctrl_e.parent = neck_follow_e

        # Position
        put_bone(self.obj, neck_follow, neck_ctrl_e.head)
        put_bone(self.obj, neck_child, neck_ctrl_e.head)
        put_bone(self.obj, head_ctrl, neck_ctrl_e.head)
        put_bone(self.obj, head_mch, neck_ctrl_e.head)
        head_mch_e.length = head_ctrl_e.length / 2
        neck_child_e.length = neck_ctrl_e.length / 2

        if self.isolate:
            put_bone(self.obj, head_socket1, neck_ctrl_e.head)
            head_mch_e.length /= 2

            put_bone(self.obj, head_socket2, neck_ctrl_e.head)
            head_mch_e.length /= 3

        for (name1, name2) in zip(neck, helpers):
            put_bone(self.obj, name2, eb[name1].head)
            eb[name2].length = eb[name1].length / 2

        # Switch to object mode
        bpy.ops.object.mode_set(mode='OBJECT')
        pb = self.obj.pose.bones
        neck_ctrl_p = pb[neck_ctrl]
        neck_follow_p = pb[neck_follow]
        # neck_child_p = pb[neck_child]  # UNUSED
        head_ctrl_p = pb[head_ctrl]
        if self.isolate:
            # head_socket1_p = pb[head_socket1]  # UNUSED
            head_socket2_p = pb[head_socket2]

        # Custom bone appearance
        neck_ctrl_p.custom_shape_transform = pb[self.org_bones[(len(self.org_bones) - 1) // 2]]
        head_ctrl_p.custom_shape_transform = pb[self.org_bones[-1]]

        # Custom properties
        prop = rna_idprop_ui_prop_get(head_ctrl_p, "inf_extent", create=True)
        head_ctrl_p["inf_extent"] = 0.5
        prop["min"] = 0.0
        prop["max"] = 1.0
        prop["soft_min"] = 0.0
        prop["soft_max"] = 1.0

        prop = rna_idprop_ui_prop_get(head_ctrl_p, "neck_follow", create=True)
        head_ctrl_p["neck_follow"] = 1.0
        prop["min"] = 0.0
        prop["max"] = 2.0
        prop["soft_min"] = 0.0
        prop["soft_max"] = 1.0

        if self.isolate:
            prop = rna_idprop_ui_prop_get(head_ctrl_p, "isolate", create=True)
            head_ctrl_p["isolate"] = 0.0
            prop["min"] = 0.0
            prop["max"] = 1.0
            prop["soft_min"] = 0.0
            prop["soft_max"] = 1.0

        # Constraints

        # Neck follow
        con = neck_follow_p.constraints.new('COPY_ROTATION')
        con.name = "copy_rotation"
        con.target = self.obj
        con.subtarget = head_ctrl

        fcurve = con.driver_add("influence")
        driver = fcurve.driver
        var = driver.variables.new()
        driver.type = 'SCRIPTED'
        var.name = "follow"
        var.targets[0].id_type = 'OBJECT'
        var.targets[0].id = self.obj
        var.targets[0].data_path = head_ctrl_p.path_from_id() + '["neck_follow"]'
        driver.expression = "follow / 2"

        # Isolate
        if self.isolate:
            con = head_socket2_p.constraints.new('COPY_LOCATION')
            con.name = "copy_location"
            con.target = self.obj
            con.subtarget = head_socket1

            con = head_socket2_p.constraints.new('COPY_TRANSFORMS')
            con.name = "copy_transforms"
            con.target = self.obj
            con.subtarget = head_socket1

            fcurve = con.driver_add("influence")
            driver = fcurve.driver
            var = driver.variables.new()
            driver.type = 'SCRIPTED'
            var.name = "isolate"
            var.targets[0].id_type = 'OBJECT'
            var.targets[0].id = self.obj
            var.targets[0].data_path = head_ctrl_p.path_from_id() + '["isolate"]'
            driver.expression = "1.0 - isolate"

        # Neck chain
        first = True
        prev = None
        i = 0
        l = len(neck)
        for (name1, name2, org_name) in zip(neck, helpers, self.org_bones):
            con = pb[org_name].constraints.new('COPY_TRANSFORMS')
            con.name = "copy_transforms"
            con.target = self.obj
            con.subtarget = name1

            n_con = pb[name2].constraints.new('COPY_TRANSFORMS')
            n_con.name = "neck"
            n_con.target = self.obj
            n_con.subtarget = neck_child

            h_con = pb[name2].constraints.new('COPY_TRANSFORMS')
            h_con.name = "head"
            h_con.target = self.obj
            h_con.subtarget = head_mch

            con = pb[name2].constraints.new('COPY_LOCATION')
            con.name = "anchor"
            con.target = self.obj
            if first:
                con.subtarget = neck_ctrl
            else:
                con.subtarget = prev
                con.head_tail = 1.0

            # Drivers
            n = (i + 1) / l

            # Neck influence
            fcurve = n_con.driver_add("influence")
            driver = fcurve.driver
            var = driver.variables.new()
            driver.type = 'SCRIPTED'
            var.name = "ext"
            var.targets[0].id_type = 'OBJECT'
            var.targets[0].id = self.obj
            var.targets[0].data_path = head_ctrl_p.path_from_id() + '["inf_extent"]'
            driver.expression = "1.0 if (%.4f > (1.0-ext) or (1.0-ext) == 0.0) else (%.4f / (1.0-ext))" % (n, n)

            # Head influence
            if (i + 1) == l:
                h_con.influence = 1.0
            else:
                fcurve = h_con.driver_add("influence")
                driver = fcurve.driver
                var = driver.variables.new()
                driver.type = 'SCRIPTED'
                var.name = "ext"
                var.targets[0].id_type = 'OBJECT'
                var.targets[0].id = self.obj
                var.targets[0].data_path = head_ctrl_p.path_from_id() + '["inf_extent"]'
                driver.expression = "0.0 if (%.4f <= (1.0-ext)) else ((%.4f - (1.0-ext)) / ext)" % (n, n)

            first = False
            prev = name1
            i += 1

        # Create control widgets
        w1 = create_circle_widget(self.obj, neck_ctrl, radius=1.0, head_tail=0.5)
        w2 = create_circle_widget(self.obj, head_ctrl, radius=1.0, head_tail=0.5)

        if w1 != None:
            obj_to_bone(w1, self.obj, self.org_bones[(len(self.org_bones) - 1) // 2])
        if w2 != None:
            obj_to_bone(w2, self.obj, self.org_bones[-1])

        # Return control bones
        return (head_ctrl, neck_ctrl)
Example #8
0
    def generate(self):
        bpy.ops.object.mode_set(mode='EDIT')

        ctrl_chain = []

        eb = self.obj.data.edit_bones

        # Control bones
        # Global control
        ctrl_g = copy_bone(self.obj, self.mouth, strip_org(self.mouth))
        ctrl_g_eb = eb[ctrl_g]
        ctrl_chain.append(ctrl_g)
        if self.org_parent is not None:
            ctrl_g_eb.use_connect = False
            ctrl_g_eb.parent = eb[self.org_parent]

        # Right control
        ctrl_r_name = strip_org(self.ur.split('_')[0] + '.R')
        ctrl_r = new_bone(self.obj, ctrl_r_name)
        eb[ctrl_r].head = eb[self.ur].head
        eb[ctrl_r].tail = eb[self.ur].head + Vector(
            (0, 0, 1)) * eb[self.ur].length

        # Left control
        ctrl_l_name = strip_org(self.ul.split('_')[0] + '.L')
        ctrl_l = new_bone(self.obj, ctrl_l_name)
        eb[ctrl_l].head = eb[self.ul].tail
        eb[ctrl_l].tail = eb[self.ul].tail + Vector(
            (0, 0, 1)) * eb[self.ul].length

        # Up control
        ctrl_uc_name = strip_org(self.uc)
        ctrl_uc = new_bone(self.obj, ctrl_uc_name)
        eb[ctrl_uc].head = (eb[self.uc].head + eb[self.uc].tail) / 2
        eb[ctrl_uc].tail = eb[ctrl_uc].head + Vector(
            (0, 0, 1)) * eb[self.uc].length

        # Down control
        ctrl_lc_name = strip_org(self.lc)
        ctrl_lc = new_bone(self.obj, ctrl_lc_name)
        eb[ctrl_lc].head = (eb[self.lc].head + eb[self.lc].tail) / 2
        eb[ctrl_lc].tail = eb[ctrl_lc].head + Vector(
            (0, 0, 1)) * eb[self.lc].length

        for b in [ctrl_r, ctrl_uc, ctrl_lc, ctrl_l]:
            align_bone_x_axis(self.obj, b, Vector((-1, 0, 0)))
            eb[b].layers = eb[self.lc].layers
            eb[b].parent = eb[self.mouth]
            ctrl_chain.append(b)

        bpy.ops.object.mode_set(mode='OBJECT')
        pb = self.obj.pose.bones
        for b in [ctrl_r, ctrl_uc, ctrl_lc, ctrl_l]:
            pb[b].lock_location = (False, False, True)
            pb[b].lock_rotation = (True, True, False)
            pb[b].lock_rotation_w = False
            pb[b].lock_scale = (False, False, False)
            pb[b].rotation_mode = 'XZY'
        bpy.ops.object.mode_set(mode='EDIT')
        eb = self.obj.data.edit_bones

        # Stretch
        # RIGHT
        stretch_r_chain = []
        for bone in [self.ur, self.lr]:
            mch_b = copy_bone(
                self.obj, bone,
                pantin_utils.strip_LR(make_mechanism_name(strip_org(bone))) +
                '.stretch.R')
            mch_eb = eb[mch_b]
            mch_eb.parent = eb[ctrl_r]
            stretch_r_chain.append(mch_b)

        # CENTER
        stretch_c_chain = []
        for bone in [self.uc, self.lc]:
            mch_b = copy_bone(
                self.obj, bone,
                pantin_utils.strip_LR(make_mechanism_name(strip_org(bone))) +
                '.stretch')
            mch_eb = eb[mch_b]
            mch_eb.use_connect = False
            mch_eb.parent = eb[strip_org(bone)]
            stretch_c_chain.append(mch_b)

        #LEFT
        stretch_l_chain = []
        for i, bone in enumerate([self.ul, self.ll]):
            mch_b = copy_bone(
                self.obj, bone,
                pantin_utils.strip_LR(make_mechanism_name(strip_org(bone))) +
                '.stretch.L')
            mch_eb = eb[mch_b]
            mch_eb.parent = eb[stretch_c_chain[i]]
            stretch_l_chain.append(mch_b)

        # Def bones
        Z_index = self.params.Z_index
        for i, b in enumerate([
                self.mouth,
                stretch_l_chain[1],
                stretch_r_chain[1],
                stretch_c_chain[1],
                stretch_l_chain[0],
                stretch_r_chain[0],
                stretch_c_chain[0],
        ]):
            def_bone_name = b.split('.')[0][4:]
            if b[-1] in ('L', 'R'):
                def_bone_name += '.' + b[-1]
            def_bone = pantin_utils.create_deformation(self.obj,
                                                       b,
                                                       member_index=Z_index,
                                                       bone_index=i,
                                                       new_name=def_bone_name)
            if b == stretch_c_chain[1]:
                pantin_utils.create_deformation(
                    self.obj,
                    b,
                    member_index=Z_index,
                    bone_index=i + 1,
                    new_name=strip_org(self.org_bones[0]) + '_int')

        bpy.ops.object.mode_set(mode='OBJECT')
        pb = self.obj.pose.bones

        # Widgets
        wgt_radius = pb[ctrl_uc].length / 3
        for b in [ctrl_r, ctrl_l, ctrl_uc, ctrl_lc]:
            pantin_utils.create_aligned_circle_widget(self.obj,
                                                      b,
                                                      radius=wgt_radius)
        pantin_utils.create_aligned_circle_widget(self.obj,
                                                  ctrl_g,
                                                  radius=pb[ctrl_g].length,
                                                  head_tail=0.0,
                                                  width_ratio=2.0)

        # Constraints
        for src_b, target_b in zip(stretch_r_chain, stretch_c_chain):
            mch_pb = pb[src_b]
            con = mch_pb.constraints.new('STRETCH_TO')
            con.name = "stretch to"
            con.target = self.obj
            con.subtarget = target_b
            con.volume = 'NO_VOLUME'
            con.rest_length = mch_pb.length
            con.keep_axis = 'PLANE_Z'
        for src_b in stretch_l_chain:
            mch_pb = pb[src_b]
            con = mch_pb.constraints.new('STRETCH_TO')
            con.name = "stretch to"
            con.target = self.obj
            con.subtarget = ctrl_l
            con.volume = 'NO_VOLUME'
            con.rest_length = mch_pb.length
            con.keep_axis = 'PLANE_Z'

        for org, ctrl in zip(self.org_bones, [ctrl_g] + stretch_r_chain +
                             stretch_c_chain + stretch_l_chain):
            con = pb[org].constraints.new('COPY_TRANSFORMS')
            con.name = "copy_transforms"
            con.target = self.obj
            con.subtarget = ctrl

        return {
            'imports': UI_IMPORTS,
            'utilities': PANTIN_UTILS,
            'register': PANTIN_REGISTER,
            'register_props': REGISTER_PANTIN_PROPS,
        }
Example #9
0
    def generate(self):
        ui_script = ""
        for s, limb in self.sides.items():
            side_org_bones, ik_limb, fk_limb = limb
            (ulimb_ik, ulimb_str, flimb_ik, flimb_str, joint_str, elimb_ik,
             elimb_str) = ik_limb.generate()

            ulimb_fk, flimb_fk, elimb_fk = fk_limb.generate()

            bpy.ops.object.mode_set(mode='EDIT')
            eb = self.obj.data.edit_bones

            # Foot rig
            foot_fr = copy_bone(
                self.obj, self.org_bones[2],
                pantin_utils.strip_LR_numbers(
                    make_mechanism_name(strip_org(self.org_bones[2]))) +
                '.fr' + s)
            foot_tgt = copy_bone(
                self.obj, self.org_bones[2],
                pantin_utils.strip_LR_numbers(
                    make_mechanism_name(strip_org(self.org_bones[2]))) +
                '.tgt' + s)
            heel_fr = copy_bone(
                self.obj, self.org_bones[3],
                pantin_utils.strip_LR_numbers(
                    make_mechanism_name(strip_org(self.org_bones[3]))) +
                '.fr' + s)
            toe_fr = copy_bone(
                self.obj, self.org_bones[4],
                pantin_utils.strip_LR_numbers(
                    make_mechanism_name(strip_org(self.org_bones[4]))) +
                '.fr' + s)
            toe_ik_ctl = copy_bone(
                self.obj, self.org_bones[4],
                pantin_utils.strip_LR_numbers(strip_org(self.org_bones[4])) +
                '.IK' + s)
            toe_fk_ctl = copy_bone(
                self.obj, self.org_bones[4],
                pantin_utils.strip_LR_numbers(strip_org(self.org_bones[4])) +
                '.FK' + s)
            toe_pos = copy_bone(
                self.obj, self.org_bones[4],
                pantin_utils.strip_LR_numbers(
                    make_mechanism_name(strip_org(self.org_bones[4]))) +
                '.pos' + s)
            roll_fr = new_bone(self.obj, "Foot roll" + s)

            # Position
            eb[roll_fr].head = (eb[elimb_str].head + Vector(
                (-1, 0, 0)) * eb[elimb_str].length * 2)
            eb[roll_fr].tail = (eb[elimb_str].head + Vector(
                (-1, 0, 1)) * eb[elimb_str].length * 2)
            eb[roll_fr].layers = eb[elimb_ik].layers

            # IK foot horizontal and starting at heel
            eb[elimb_ik].head = eb[heel_fr].tail
            eb[elimb_ik].tail.z = eb[heel_fr].tail.z
            align_bone_x_axis(self.obj, elimb_ik, Vector((0, 0, 1)))

            align_bone_x_axis(self.obj, roll_fr, Vector((-1, 0, 0)))
            align_bone_x_axis(self.obj, foot_fr, Vector((-1, 0, 0)))
            align_bone_x_axis(self.obj, heel_fr, Vector((-1, 0, 0)))
            align_bone_x_axis(self.obj, toe_fr, Vector((-1, 0, 0)))

            # Parenting
            eb[foot_fr].parent = None
            eb[heel_fr].parent = None
            eb[toe_fr].parent = None

            flip_bone(self.obj, foot_fr)
            flip_bone(self.obj, heel_fr)
            flip_bone(self.obj, toe_fr)

            eb[foot_fr].use_connect = True
            eb[foot_fr].parent = eb[toe_fr]

            eb[foot_tgt].use_connect = True
            eb[foot_tgt].parent = eb[foot_fr]

            eb[toe_fr].use_connect = False
            eb[toe_fr].parent = eb[heel_fr]

            eb[toe_ik_ctl].use_connect = True
            eb[toe_ik_ctl].parent = eb[toe_fr]

            eb[toe_fk_ctl].use_connect = True
            eb[toe_fk_ctl].parent = eb[elimb_fk]

            eb[toe_pos].use_connect = False
            eb[toe_pos].parent = eb[elimb_str]

            eb[heel_fr].use_connect = False
            eb[heel_fr].parent = eb[elimb_ik]

            eb[roll_fr].use_connect = False
            eb[roll_fr].parent = eb[elimb_ik]

            if self.params.do_stretch:
                eb[elimb_str].use_connect = False

            # Def bones
            if s == '.L':
                Z_index = -self.params.Z_index
            else:
                Z_index = self.params.Z_index

            side_org_bones = side_org_bones[:3] + side_org_bones[
                -1:]  # Ignore heel
            for i, b in enumerate(side_org_bones):
                def_bone_name = pantin_utils.strip_LR_numbers(strip_org(b))
                def_bone = pantin_utils.create_deformation(
                    self.obj,
                    b,
                    member_index=Z_index,
                    bone_index=i,
                    new_name=def_bone_name + s)

            # Set layers if specified
            active_layer = pantin_utils.layers_to_index(eb[ulimb_ik].layers)
            right_offset = self.params.right_offset if self.params.duplicate_lr else 0
            if s == '.R':
                for b in (ulimb_ik, joint_str, elimb_ik, roll_fr, toe_ik_ctl):
                    eb[b].layers = get_layers(active_layer + right_offset)
                for b in (ulimb_fk, flimb_fk, elimb_fk, toe_fk_ctl):
                    eb[b].layers = get_layers(active_layer +
                                              self.params.fk_offset +
                                              right_offset)
            elif s == '.L':
                for b in (ulimb_fk, flimb_fk, elimb_fk, toe_fk_ctl):
                    eb[b].layers = get_layers(active_layer +
                                              self.params.fk_offset)

            bpy.ops.object.mode_set(mode='OBJECT')
            pb = self.obj.pose.bones

            # Bone settings
            pb[roll_fr].rotation_mode = 'XZY'
            pb[roll_fr].lock_location = [True] * 3
            pb[roll_fr].lock_rotation = [True, True, False]
            pb[roll_fr].lock_scale = [True] * 3
            pb[foot_fr].rotation_mode = 'XZY'

            # Widgets
            # IK
            global_scale = self.obj.dimensions[2]
            member_factor = 0.06
            if s == '.R':
                side_factor = 1.2
            else:
                side_factor = 1.0
            widget_size = global_scale * member_factor * side_factor
            pantin_utils.create_aligned_circle_widget(self.obj,
                                                      ulimb_ik,
                                                      number_verts=3,
                                                      radius=widget_size)
            pantin_utils.create_aligned_circle_widget(self.obj,
                                                      joint_str,
                                                      radius=widget_size)

            # FK
            widget_size = 0.5
            for bone in (ulimb_fk, flimb_fk, elimb_fk, toe_fk_ctl):
                pantin_utils.create_capsule_widget(self.obj,
                                                   bone,
                                                   length=widget_size,
                                                   width=widget_size * 0.2,
                                                   head_tail=0.5,
                                                   horizontal=False,
                                                   overshoot=True)

                # Default follow value
                pb[ulimb_fk]["follow"] = 0.0

            # Foot WGT
            down = pb[heel_fr].head.z
            left = pb[heel_fr].head.x - (side_factor - 1) * pb[heel_fr].length
            right = pb[foot_fr].head.x
            up = pb[foot_fr].tail.z + (side_factor - 1) * pb[heel_fr].length

            foot_verts = ((left, down), (left, up),
                          (right, down + (up - down) * 2 / 3), (right, down))
            pantin_utils.create_aligned_polygon_widget(self.obj, elimb_ik,
                                                       foot_verts)

            # Toe WGT
            left = right
            right = pb[toe_fr].head.x + (side_factor - 1) * pb[heel_fr].length
            up = down + (up - down) * 2 / 3

            # TODO investigate why moving an edit bones
            # screws up widgets' matrices (foot_ik has moved)
            toe_verts = ((left, down), (left, up),
                         (right, down + (up - down) * 2 / 3), (right, down))
            pantin_utils.create_aligned_polygon_widget(self.obj, toe_ik_ctl,
                                                       toe_verts)

            pantin_utils.create_aligned_crescent_widget(self.obj,
                                                        roll_fr,
                                                        radius=side_factor *
                                                        pb[roll_fr].length / 2)

            # Constraints
            foot_fr_p = pb[foot_fr]
            heel_fr_p = pb[heel_fr]
            toe_fr_p = pb[toe_fr]
            roll_fr_p = pb[roll_fr]

            hor_vector = Vector((1, 0, 0))
            foot_rotation = (foot_fr_p.vector.rotation_difference(
                hor_vector).to_euler('XZY'))
            toe_rotation = toe_fr_p.vector.rotation_difference(
                hor_vector).to_euler('XZY')

            foot_vertical_rot = foot_rotation[1] - pi / 2
            toe_vertical_rot = toe_rotation[1] - pi / 2

            con = foot_fr_p.constraints.new('TRANSFORM')
            con.name = "roll"
            con.target = self.obj
            con.subtarget = roll_fr
            con.map_from = 'ROTATION'
            con.map_to = 'ROTATION'
            con.from_min_z_rot = radians(0.0)
            con.from_max_z_rot = radians(60.0)
            con.to_min_z_rot = radians(0.0)
            con.to_max_z_rot = foot_vertical_rot
            con.target_space = 'LOCAL'
            con.owner_space = 'LOCAL'

            con = heel_fr_p.constraints.new('TRANSFORM')
            con.name = "roll"
            con.target = self.obj
            con.subtarget = roll_fr
            con.map_from = 'ROTATION'
            con.map_to = 'ROTATION'
            con.from_min_z_rot = radians(-60.0)
            con.from_max_z_rot = 0.0
            con.to_min_z_rot = radians(-60.0)
            con.to_max_z_rot = 0.0
            con.target_space = 'LOCAL'
            con.owner_space = 'LOCAL'

            con = toe_fr_p.constraints.new('TRANSFORM')
            con.name = "roll"
            con.target = self.obj
            con.subtarget = roll_fr
            con.map_from = 'ROTATION'
            con.map_to = 'ROTATION'
            con.from_min_z_rot = radians(60.0)
            con.from_max_z_rot = radians(90.0)
            con.to_min_z_rot = radians(0.0)
            con.to_max_z_rot = toe_vertical_rot
            con.target_space = 'LOCAL'
            con.owner_space = 'LOCAL'

            # Compensate foot for heel
            con = foot_fr_p.constraints.new('TRANSFORM')
            con.name = "roll_compensate"
            con.target = self.obj
            con.subtarget = roll_fr
            con.map_from = 'ROTATION'
            con.map_to = 'ROTATION'
            con.from_min_z_rot = radians(60.0)
            con.from_max_z_rot = radians(90.0)
            con.to_min_z_rot = radians(0.0)
            con.to_max_z_rot = -toe_vertical_rot
            con.target_space = 'LOCAL'
            con.owner_space = 'LOCAL'

            # Roll limits
            con = roll_fr_p.constraints.new('LIMIT_ROTATION')
            con.name = "limit rotation"
            con.use_limit_z = True
            con.min_z = radians(-60.0)
            con.max_z = radians(90.0)
            con.owner_space = 'LOCAL'

            # Edit IK to follow the rolled foot instead of ik control
            con = pb[flimb_ik].constraints["ik"]
            con.subtarget = foot_tgt
            con = pb[elimb_str].constraints["copy rotation"]
            con.subtarget = foot_tgt

            con = pb[toe_pos].constraints.new('COPY_ROTATION')
            con.name = "copy rotation"
            con.target = self.obj
            con.subtarget = toe_ik_ctl
            #con.invert_x = True
            con.target_space = 'POSE'
            con.owner_space = 'POSE'

            # Optional stretch to the foot
            if self.params.do_stretch:
                con1 = pb[flimb_str].constraints.new('STRETCH_TO')
                con1.name = "stretch to foot"
                con1.target = self.obj
                con1.subtarget = foot_tgt
                con1.volume = 'NO_VOLUME'
                con1.rest_length = pb[flimb_str].length
                con1.keep_axis = 'PLANE_Z'

                con2 = pb[elimb_str].constraints.new('COPY_LOCATION')
                con2.name = "copy foot location"
                con2.target = self.obj
                con2.subtarget = foot_tgt
                con2.target_space = 'POSE'
                con2.owner_space = 'POSE'

                # Set up custom properties
                prop = rna_idprop_ui_prop_get(pb[elimb_ik],
                                              "foot_stretch",
                                              create=True)
                pb[elimb_ik]["foot_stretch"] = 1  # int(self.foot_stretch)
                prop["soft_min"] = 0
                prop["soft_max"] = 1
                prop["min"] = 0
                prop["max"] = 1

                # Drivers
                for c in (con1, con2):
                    driver = self.obj.driver_add(c.path_from_id("influence"))
                    driver.driver.expression = 'foot_stretch'
                    var_fs = driver.driver.variables.new()

                    var_fs.type = 'SINGLE_PROP'
                    var_fs.name = 'foot_stretch'
                    var_fs.targets[0].id_type = 'OBJECT'
                    var_fs.targets[0].id = self.obj
                    var_fs.targets[0].data_path = (
                        pb[elimb_ik].path_from_id() + '["foot_stretch"]')

            for org, ctrl in zip(
                    side_org_bones,
                [ulimb_str, flimb_str, elimb_str, toe_ik_ctl]):
                con = pb[org].constraints.new('COPY_TRANSFORMS')
                con.name = "copy_ik"
                con.target = self.obj
                con.subtarget = ctrl

            for org, ctrl in zip(side_org_bones,
                                 [ulimb_fk, flimb_fk, elimb_fk, toe_fk_ctl]):
                con = pb[org].constraints.new('COPY_TRANSFORMS')
                con.name = "copy_fk"
                con.target = self.obj
                con.subtarget = ctrl

                # Drivers
                driver = self.obj.driver_add(con.path_from_id("influence"))
                driver.driver.expression = 'fk'
                var_fk = driver.driver.variables.new()
                var_fk.type = 'SINGLE_PROP'
                var_fk.name = 'fk'
                var_fk.targets[0].id_type = 'OBJECT'
                var_fk.targets[0].id = self.obj
                var_fk.targets[
                    0].data_path = 'pose.bones["{}"]["IK_FK"]'.format(elimb_ik)

            ui_script += UI_PANTIN_LIMB_SCRIPT % (
                '"{}", "{}", "{}", "{}"'.format(
                    ulimb_ik, joint_str, elimb_ik,
                    toe_ik_ctl), '"{}", "{}", "{}", "{}"'.format(
                        ulimb_fk, flimb_fk, elimb_fk, toe_fk_ctl))

            if self.params.do_stretch:
                ui_script += """    layout.prop(pose_bones[ik_leg[2]], \
'["foot_stretch"]', \
text="Foot stretch (" + ik_leg[2] + ")", slider=True)
    """

        return {
            'script': [ui_script],
            'imports': UI_IMPORTS,
            'utilities': PANTIN_UTILS + [UTILITIES_PANTIN_LIMBS],
            'register': PANTIN_REGISTER + REGISTER_PANTIN_LIMBS,
            'register_props': REGISTER_PANTIN_PROPS,
        }
Example #10
0
def generate_rig(context, metarig):
    """ Generates a rig from a metarig.

    """
    t = Timer()
    rig_id = random_string(12)  # Random so that different rigs don't collide id's

    # Initial configuration
    use_global_undo = context.user_preferences.edit.use_global_undo
    context.user_preferences.edit.use_global_undo = False
    mode_orig = context.mode
    rest_backup = metarig.data.pose_position
    metarig.data.pose_position = 'REST'

    bpy.ops.object.mode_set(mode='OBJECT')

    scene = context.scene

    #------------------------------------------
    # Create/find the rig object and set it up

    # Check if the generated rig already exists, so we can
    # regenerate in the same object.  If not, create a new
    # object to generate the rig in.
    print("Fetch rig.")
    try:
        name = metarig["rig_object_name"]
    except KeyError:
        name = "rig"

    try:
        obj = scene.objects[name]
    except KeyError:
        obj = bpy.data.objects.new(name, bpy.data.armatures.new(name))
        obj.draw_type = 'WIRE'
        scene.objects.link(obj)

    obj.data.pose_position = 'POSE'

    # Get rid of anim data in case the rig already existed
    print("Clear rig animation data.")
    obj.animation_data_clear()

    # Select generated rig object
    metarig.select = False
    obj.select = True
    scene.objects.active = obj

    # Remove all bones from the generated rig armature.
    bpy.ops.object.mode_set(mode='EDIT')
    for bone in obj.data.edit_bones:
        obj.data.edit_bones.remove(bone)
    bpy.ops.object.mode_set(mode='OBJECT')

    # Create temporary duplicates for merging
    temp_rig_1 = metarig.copy()
    temp_rig_1.data = metarig.data.copy()
    scene.objects.link(temp_rig_1)

    temp_rig_2 = metarig.copy()
    temp_rig_2.data = obj.data
    scene.objects.link(temp_rig_2)

    # Select the temp rigs for merging
    for objt in scene.objects:
        objt.select = False  # deselect all objects
    temp_rig_1.select = True
    temp_rig_2.select = True
    scene.objects.active = temp_rig_2

    # Merge the temporary rigs
    bpy.ops.object.join()

    # Delete the second temp rig
    bpy.ops.object.delete()

    # Select the generated rig
    for objt in scene.objects:
        objt.select = False  # deselect all objects
    obj.select = True
    scene.objects.active = obj

    # Copy over the pose_bone properties
    for bone in metarig.pose.bones:
        bone_gen = obj.pose.bones[bone.name]

        # Rotation mode and transform locks
        bone_gen.rotation_mode = bone.rotation_mode
        bone_gen.lock_rotation = tuple(bone.lock_rotation)
        bone_gen.lock_rotation_w = bone.lock_rotation_w
        bone_gen.lock_rotations_4d = bone.lock_rotations_4d
        bone_gen.lock_location = tuple(bone.lock_location)
        bone_gen.lock_scale = tuple(bone.lock_scale)

        # Custom properties
        for prop in bone.keys():
            bone_gen[prop] = bone[prop]

    # Copy over bone properties
    for bone in metarig.data.bones:
        bone_gen = obj.data.bones[bone.name]

        # B-bone stuff
        bone_gen.bbone_segments = bone.bbone_segments
        bone_gen.bbone_in = bone.bbone_in
        bone_gen.bbone_out = bone.bbone_out

    t.tick("Duplicate rig: ")
    #----------------------------------
    # Make a list of the original bones so we can keep track of them.
    original_bones = [bone.name for bone in obj.data.bones]

    # Add the ORG_PREFIX to the original bones.
    bpy.ops.object.mode_set(mode='OBJECT')
    for i in range(0, len(original_bones)):
        obj.data.bones[original_bones[i]].name = make_original_name(original_bones[i])
        original_bones[i] = make_original_name(original_bones[i])

    # Create a sorted list of the original bones, sorted in the order we're
    # going to traverse them for rigging.
    # (root-most -> leaf-most, alphabetical)
    bones_sorted = []
    for name in original_bones:
        bones_sorted += [name]
    bones_sorted.sort()  # first sort by names
    bones_sorted.sort(key=lambda bone: len(obj.pose.bones[bone].parent_recursive))  # then parents before children

    t.tick("Make list of org bones: ")
    #----------------------------------
    # Create the root bone.
    bpy.ops.object.mode_set(mode='EDIT')
    root_bone = new_bone(obj, ROOT_NAME)
    obj.data.edit_bones[root_bone].head = (0, 0, 0)
    obj.data.edit_bones[root_bone].tail = (0, 1, 0)
    obj.data.edit_bones[root_bone].roll = 0
    bpy.ops.object.mode_set(mode='OBJECT')
    obj.data.bones[root_bone].layers = ROOT_LAYER
    # Put the rig_name in the armature custom properties
    rna_idprop_ui_prop_get(obj.data, "rig_id", create=True)
    obj.data["rig_id"] = rig_id

    t.tick("Create root bone: ")
    #----------------------------------
    try:
        # Collect/initialize all the rigs.
        rigs = []
        deformation_rigs = []
        for bone in bones_sorted:
            bpy.ops.object.mode_set(mode='EDIT')
            rigs += get_bone_rigs(obj, bone)
        t.tick("Initialize rigs: ")

        # Generate all the rigs.
        ui_scripts = []
        for rig in rigs:
            # Go into editmode in the rig armature
            bpy.ops.object.mode_set(mode='OBJECT')
            context.scene.objects.active = obj
            obj.select = True
            bpy.ops.object.mode_set(mode='EDIT')
            scripts = rig.generate()
            if scripts != None:
                ui_scripts += [scripts[0]]
        t.tick("Generate rigs: ")
    except Exception as e:
        # Cleanup if something goes wrong
        print("Rigify: failed to generate rig.")
        context.user_preferences.edit.use_global_undo = use_global_undo
        metarig.data.pose_position = rest_backup
        obj.data.pose_position = 'POSE'
        bpy.ops.object.mode_set(mode='OBJECT')

        # Continue the exception
        raise e

    #----------------------------------
    bpy.ops.object.mode_set(mode='OBJECT')

    # Get a list of all the bones in the armature
    bones = [bone.name for bone in obj.data.bones]

    # Parent any free-floating bones to the root.
    bpy.ops.object.mode_set(mode='EDIT')
    for bone in bones:
        if obj.data.edit_bones[bone].parent == None:
            obj.data.edit_bones[bone].use_connect = False
            obj.data.edit_bones[bone].parent = obj.data.edit_bones[root_bone]
    bpy.ops.object.mode_set(mode='OBJECT')

    # Every bone that has a name starting with "DEF-" make deforming.  All the
    # others make non-deforming.
    for bone in bones:
        if obj.data.bones[bone].name.startswith(DEF_PREFIX):
            obj.data.bones[bone].use_deform = True
        else:
            obj.data.bones[bone].use_deform = False

    # Move all the original bones to their layer.
    for bone in original_bones:
        obj.data.bones[bone].layers = ORG_LAYER

    # Move all the bones with names starting with "MCH-" to their layer.
    for bone in bones:
        if obj.data.bones[bone].name.startswith(MCH_PREFIX):
            obj.data.bones[bone].layers = MCH_LAYER

    # Move all the bones with names starting with "DEF-" to their layer.
    for bone in bones:
        if obj.data.bones[bone].name.startswith(DEF_PREFIX):
            obj.data.bones[bone].layers = DEF_LAYER

    # Create root bone widget
    create_root_widget(obj, "root")

    # Assign shapes to bones
    # Object's with name WGT-<bone_name> get used as that bone's shape.
    for bone in bones:
        wgt_name = (WGT_PREFIX + obj.data.bones[bone].name)[:21]  # Object names are limited to 21 characters... arg
        if wgt_name in context.scene.objects:
            # Weird temp thing because it won't let me index by object name
            for ob in context.scene.objects:
                if ob.name == wgt_name:
                    obj.pose.bones[bone].custom_shape = ob
                    break
            # This is what it should do:
            # obj.pose.bones[bone].custom_shape = context.scene.objects[wgt_name]
    # Reveal all the layers with control bones on them
    vis_layers = [False for n in range(0, 32)]
    for bone in bones:
        for i in range(0, 32):
            vis_layers[i] = vis_layers[i] or obj.data.bones[bone].layers[i]
    for i in range(0, 32):
        vis_layers[i] = vis_layers[i] and not (ORG_LAYER[i] or MCH_LAYER[i] or DEF_LAYER[i])
    obj.data.layers = vis_layers

    # Generate the UI script
    if "rig_ui.py" in bpy.data.texts:
        script = bpy.data.texts["rig_ui.py"]
        script.clear()
    else:
        script = bpy.data.texts.new("rig_ui.py")
    script.write(UI_SLIDERS % rig_id)
    for s in ui_scripts:
        script.write("\n        " + s.replace("\n", "\n        ") + "\n")
    script.write(layers_ui(vis_layers))
    script.use_module = True

    t.tick("The rest: ")
    #----------------------------------
    # Deconfigure
    bpy.ops.object.mode_set(mode='OBJECT')
    metarig.data.pose_position = rest_backup
    obj.data.pose_position = 'POSE'
    context.user_preferences.edit.use_global_undo = use_global_undo
def generate_rig(context, metarig):
    """ Generates a rig from a metarig.

    """
    t = Timer()
    rig_id = random_string(
        12)  # Random so that different rigs don't collide id's

    # Initial configuration
    use_global_undo = context.user_preferences.edit.use_global_undo
    context.user_preferences.edit.use_global_undo = False
    mode_orig = context.mode
    rest_backup = metarig.data.pose_position
    metarig.data.pose_position = 'REST'

    bpy.ops.object.mode_set(mode='OBJECT')

    scene = context.scene

    #------------------------------------------
    # Create/find the rig object and set it up

    # Check if the generated rig already exists, so we can
    # regenerate in the same object.  If not, create a new
    # object to generate the rig in.
    print("Fetch rig.")
    try:
        name = metarig["rig_object_name"]
    except KeyError:
        name = "rig"

    try:
        obj = scene.objects[name]
    except KeyError:
        obj = bpy.data.objects.new(name, bpy.data.armatures.new(name))
        obj.draw_type = 'WIRE'
        scene.objects.link(obj)

    obj.data.pose_position = 'POSE'

    # Get rid of anim data in case the rig already existed
    print("Clear rig animation data.")
    obj.animation_data_clear()

    # Select generated rig object
    metarig.select = False
    obj.select = True
    scene.objects.active = obj

    # Remove all bones from the generated rig armature.
    bpy.ops.object.mode_set(mode='EDIT')
    for bone in obj.data.edit_bones:
        obj.data.edit_bones.remove(bone)
    bpy.ops.object.mode_set(mode='OBJECT')

    # Create temporary duplicates for merging
    temp_rig_1 = metarig.copy()
    temp_rig_1.data = metarig.data.copy()
    scene.objects.link(temp_rig_1)

    temp_rig_2 = metarig.copy()
    temp_rig_2.data = obj.data
    scene.objects.link(temp_rig_2)

    # Select the temp rigs for merging
    for objt in scene.objects:
        objt.select = False  # deselect all objects
    temp_rig_1.select = True
    temp_rig_2.select = True
    scene.objects.active = temp_rig_2

    # Merge the temporary rigs
    bpy.ops.object.join()

    # Delete the second temp rig
    bpy.ops.object.delete()

    # Select the generated rig
    for objt in scene.objects:
        objt.select = False  # deselect all objects
    obj.select = True
    scene.objects.active = obj

    # Copy over the pose_bone properties
    for bone in metarig.pose.bones:
        bone_gen = obj.pose.bones[bone.name]

        # Rotation mode and transform locks
        bone_gen.rotation_mode = bone.rotation_mode
        bone_gen.lock_rotation = tuple(bone.lock_rotation)
        bone_gen.lock_rotation_w = bone.lock_rotation_w
        bone_gen.lock_rotations_4d = bone.lock_rotations_4d
        bone_gen.lock_location = tuple(bone.lock_location)
        bone_gen.lock_scale = tuple(bone.lock_scale)

        # Custom properties
        for prop in bone.keys():
            bone_gen[prop] = bone[prop]

    # Copy over bone properties
    for bone in metarig.data.bones:
        bone_gen = obj.data.bones[bone.name]

        # B-bone stuff
        bone_gen.bbone_segments = bone.bbone_segments
        bone_gen.bbone_in = bone.bbone_in
        bone_gen.bbone_out = bone.bbone_out

    t.tick("Duplicate rig: ")
    #----------------------------------
    # Make a list of the original bones so we can keep track of them.
    original_bones = [bone.name for bone in obj.data.bones]

    # Add the ORG_PREFIX to the original bones.
    bpy.ops.object.mode_set(mode='OBJECT')
    for i in range(0, len(original_bones)):
        obj.data.bones[original_bones[i]].name = make_original_name(
            original_bones[i])
        original_bones[i] = make_original_name(original_bones[i])

    # Create a sorted list of the original bones, sorted in the order we're
    # going to traverse them for rigging.
    # (root-most -> leaf-most, alphabetical)
    bones_sorted = []
    for name in original_bones:
        bones_sorted += [name]
    bones_sorted.sort()  # first sort by names
    bones_sorted.sort(key=lambda bone: len(obj.pose.bones[
        bone].parent_recursive))  # then parents before children

    t.tick("Make list of org bones: ")
    #----------------------------------
    # Create the root bone.
    bpy.ops.object.mode_set(mode='EDIT')
    root_bone = new_bone(obj, ROOT_NAME)
    obj.data.edit_bones[root_bone].head = (0, 0, 0)
    obj.data.edit_bones[root_bone].tail = (0, 1, 0)
    obj.data.edit_bones[root_bone].roll = 0
    bpy.ops.object.mode_set(mode='OBJECT')
    obj.data.bones[root_bone].layers = ROOT_LAYER
    # Put the rig_name in the armature custom properties
    rna_idprop_ui_prop_get(obj.data, "rig_id", create=True)
    obj.data["rig_id"] = rig_id

    t.tick("Create root bone: ")
    #----------------------------------
    try:
        # Collect/initialize all the rigs.
        rigs = []
        deformation_rigs = []
        for bone in bones_sorted:
            bpy.ops.object.mode_set(mode='EDIT')
            rigs += get_bone_rigs(obj, bone)
        t.tick("Initialize rigs: ")

        # Generate all the rigs.
        ui_scripts = []
        for rig in rigs:
            # Go into editmode in the rig armature
            bpy.ops.object.mode_set(mode='OBJECT')
            context.scene.objects.active = obj
            obj.select = True
            bpy.ops.object.mode_set(mode='EDIT')
            scripts = rig.generate()
            if scripts != None:
                ui_scripts += [scripts[0]]
        t.tick("Generate rigs: ")
    except Exception as e:
        # Cleanup if something goes wrong
        print("Rigify: failed to generate rig.")
        context.user_preferences.edit.use_global_undo = use_global_undo
        metarig.data.pose_position = rest_backup
        obj.data.pose_position = 'POSE'
        bpy.ops.object.mode_set(mode='OBJECT')

        # Continue the exception
        raise e

    #----------------------------------
    bpy.ops.object.mode_set(mode='OBJECT')

    # Get a list of all the bones in the armature
    bones = [bone.name for bone in obj.data.bones]

    # Parent any free-floating bones to the root.
    bpy.ops.object.mode_set(mode='EDIT')
    for bone in bones:
        if obj.data.edit_bones[bone].parent == None:
            obj.data.edit_bones[bone].use_connect = False
            obj.data.edit_bones[bone].parent = obj.data.edit_bones[root_bone]
    bpy.ops.object.mode_set(mode='OBJECT')

    # Every bone that has a name starting with "DEF-" make deforming.  All the
    # others make non-deforming.
    for bone in bones:
        if obj.data.bones[bone].name.startswith(DEF_PREFIX):
            obj.data.bones[bone].use_deform = True
        else:
            obj.data.bones[bone].use_deform = False

    # Move all the original bones to their layer.
    for bone in original_bones:
        obj.data.bones[bone].layers = ORG_LAYER

    # Move all the bones with names starting with "MCH-" to their layer.
    for bone in bones:
        if obj.data.bones[bone].name.startswith(MCH_PREFIX):
            obj.data.bones[bone].layers = MCH_LAYER

    # Move all the bones with names starting with "DEF-" to their layer.
    for bone in bones:
        if obj.data.bones[bone].name.startswith(DEF_PREFIX):
            obj.data.bones[bone].layers = DEF_LAYER

    # Create root bone widget
    create_root_widget(obj, "root")

    # Assign shapes to bones
    # Object's with name WGT-<bone_name> get used as that bone's shape.
    for bone in bones:
        wgt_name = (WGT_PREFIX + obj.data.bones[bone].name
                    )[:21]  # Object names are limited to 21 characters... arg
        if wgt_name in context.scene.objects:
            # Weird temp thing because it won't let me index by object name
            for ob in context.scene.objects:
                if ob.name == wgt_name:
                    obj.pose.bones[bone].custom_shape = ob
                    break
            # This is what it should do:
            # obj.pose.bones[bone].custom_shape = context.scene.objects[wgt_name]
    # Reveal all the layers with control bones on them
    vis_layers = [False for n in range(0, 32)]
    for bone in bones:
        for i in range(0, 32):
            vis_layers[i] = vis_layers[i] or obj.data.bones[bone].layers[i]
    for i in range(0, 32):
        vis_layers[i] = vis_layers[i] and not (ORG_LAYER[i] or MCH_LAYER[i]
                                               or DEF_LAYER[i])
    obj.data.layers = vis_layers

    # Generate the UI script
    if "rig_ui.py" in bpy.data.texts:
        script = bpy.data.texts["rig_ui.py"]
        script.clear()
    else:
        script = bpy.data.texts.new("rig_ui.py")
    script.write(UI_SLIDERS % rig_id)
    for s in ui_scripts:
        script.write("\n        " + s.replace("\n", "\n        ") + "\n")
    script.write(layers_ui(vis_layers))
    script.use_module = True

    t.tick("The rest: ")
    #----------------------------------
    # Deconfigure
    bpy.ops.object.mode_set(mode='OBJECT')
    metarig.data.pose_position = rest_backup
    obj.data.pose_position = 'POSE'
    context.user_preferences.edit.use_global_undo = use_global_undo
Example #12
0
    def generate(self):
        def parent_ctrl(eb, child, parent_to=None):
            """This tries to parent the child to a .L/.R bone."""
            # Get parent if none supplied
            if parent_to is None and eb[child].parent is not None:
                parent_to = eb[child].parent.name
            print(parent_to)

            if parent_to + self.params.object_side in eb:
                eb[child].parent = (eb[parent_to + self.params.object_side])
            elif parent_to in eb:
                eb[child].parent = eb[parent_to]
            elif 'MCH-Flip' in eb:
                eb[child].parent = eb['MCH-Flip']
            else:
                raise Exception(
                    "RIGIFY ERROR: Bone %s does not have a %s side" %
                    (strip_org(eb[b].parent.name), side))

        if self.params.use_parent_Z_index and self.org_parent is not None:
            # Get parent's Z indices
            bpy.ops.object.mode_set(mode='OBJECT')
            pb = self.obj.pose.bones
            def_parent_name = make_deformer_name(strip_org(self.org_parent))
            if (self.params.object_side != ".C"
                    and def_parent_name[-2:] not in ['.L', '.R']):
                def_parent_name += self.params.object_side
            if not def_parent_name in pb:
                raise Exception(
                    "RIGIFY ERROR: Bone %s does not have a %s side" %
                    (strip_org(self.org_parent), self.params.object_side))
            parent_p = pb[def_parent_name]
            member_Z_index = parent_p['member_index']
            bone_Z_index = 0
            for b in pb:
                if b.bone.use_deform and b.name.startswith('DEF-'):
                    if not 'member_index' in b:
                        raise MetarigError(
                            "RIGIFY ERROR: One rig bone with should not be connected. "
                            "Check armature for connected bones.")
                    if (b['member_index'] == member_Z_index
                            and b['bone_index'] > bone_Z_index):
                        bone_Z_index = b['bone_index']
            bone_Z_index += 1

            bpy.ops.object.mode_set(mode='EDIT')
        else:
            member_Z_index = self.params.member_Z_index
            bone_Z_index = self.params.first_bone_Z_index

        eb = self.obj.data.edit_bones

        ctrl_chain = []
        mch_chain = []
        # def_chain = []

        # if self.params.duplicate_lr:
        #     sides = ['.L', '.R']
        # else:
        side = self.params.object_side
        if side == '.C':
            side = ''

        # IK control bone
        if self.params.chain_type == 'IK':
            last_bone = self.org_bones[-1]
            ctrl_bone = new_bone(self.obj, strip_org(last_bone) + ".ik" + side)
            ctrl_bone_e = eb[ctrl_bone]
            ctrl_bone_e.head = eb[last_bone].tail
            ctrl_bone_e.tail = eb[last_bone].tail + Vector((0.3, 0, 0))
            align_bone_z_axis(self.obj, ctrl_bone, Vector((0, 1, 0)))
            ctrl_chain.append(ctrl_bone)
            # ctrl_bone_e.layers = layers

        for i, b in enumerate(self.org_bones):
            # Control bones
            if self.params.chain_type in {'Normal', 'Curve', 'Dynamic'}:
                if side in b:
                    ctrl_bone_name = b
                else:
                    ctrl_bone_name = b + side
                ctrl_bone_name = strip_org(ctrl_bone_name)
                ctrl_bone = copy_bone(self.obj, b, ctrl_bone_name)
                ctrl_bone_e = eb[ctrl_bone]
                ctrl_chain.append(ctrl_bone)

                if self.params.chain_type == 'Curve':
                    ctrl_bone_e.use_connect = False
                    if self.params.curve_parent_to_first:
                        if i > 0:
                            parent_ctrl(eb, ctrl_bone, parent_to=ctrl_chain[0])
                        else:
                            parent_ctrl(eb,
                                        ctrl_bone,
                                        parent_to=self.org_parent)
                    else:
                        parent_ctrl(eb, ctrl_bone, parent_to=self.org_parent)
                    ctrl_bone_e.tail = (ctrl_bone_e.head + Vector(
                        (0, 0, 1)) * ctrl_bone_e.length)
                    align_bone_z_axis(self.obj, ctrl_bone, Vector((0, 1, 0)))

                elif self.params.chain_type == 'Dynamic':
                    # Create an empty object to use slow parent
                    # What follows is quite dirty.
                    # ctrl_bone_e.parent = eb[self.org_parent]
                    parent_ctrl(eb, ctrl_bone)

                    empty_name = self.obj.name + "_" + strip_org(b) + '.dyn'
                    if empty_name in bpy.data.objects:
                        empty_obj = bpy.data.objects[empty_name]
                    else:
                        empty_obj = bpy.data.objects.new(empty_name, None)
                    if not empty_name in bpy.context.scene.objects:
                        bpy.context.scene.objects.link(empty_obj)
                    empty_obj.empty_draw_type = 'SPHERE'
                    empty_obj.empty_draw_size = self.obj.data.bones[
                        b].length / 10
                    empty_obj.hide = True

                    empty_obj.parent = self.obj
                    empty_obj.parent_type = 'BONE'
                    empty_obj.parent_bone = ctrl_bone
                    empty_obj.matrix_local = Matrix()
                    empty_obj.use_slow_parent = True
                    empty_obj.slow_parent_offset = 1.0

            # Mechanism bones
            if self.params.chain_type == 'Curve':
                stretch_bone = copy_bone(
                    self.obj, b,
                    make_mechanism_name(strip_org(b)) + '.stretch' + side)
                stretch_bone_e = eb[stretch_bone]
                stretch_bone_e.use_connect = False
                stretch_bone_e.parent = eb[ctrl_bone]
                mch_chain.append(stretch_bone)

            elif self.params.chain_type == 'IK':
                ik_bone = copy_bone(
                    self.obj, b,
                    make_mechanism_name(strip_org(b)) + '.ik' + side)
                ik_bone_e = eb[ik_bone]
                ik_bone_e.parent = eb[mch_chain[-1] if i > 0 else self.
                                      org_parent]
                mch_chain.append(ik_bone)

            elif self.params.chain_type == 'Dynamic':
                dyn_bone = copy_bone(
                    self.obj, b,
                    make_mechanism_name(strip_org(b)) + '.dyn' + side)
                dyn_bone_e = eb[dyn_bone]
                dyn_bone_e.parent = eb[ctrl_bone]
                mch_chain.append(dyn_bone)

            # Parenting
            # if self.params.chain_type == 'Normal':
            if (i == 0 and self.params.chain_type in ('Normal', 'Dynamic')):
                parent_ctrl(eb, ctrl_bone)

            # Def bones
            def_bone = pantin_utils.create_deformation(self.obj, b,
                                                       member_Z_index,
                                                       bone_Z_index + i, 0.0,
                                                       b + side)

        if self.params.chain_type == 'Curve':
            # Curve end bone
            last_bone = self.org_bones[-1]
            ctrl_bone = new_bone(self.obj,
                                 strip_org(last_bone) + ".end" + side)
            ctrl_bone_e = eb[ctrl_bone]
            last_bone_e = eb[last_bone]

            ctrl_bone_e.use_connect = False
            if self.params.curve_parent_to_first:
                ctrl_bone_e.parent = eb[ctrl_chain[0]]
            else:
                parent_ctrl(eb, ctrl_bone, parent_to=self.org_parent)
            ctrl_bone_e.head = last_bone_e.tail
            ctrl_bone_e.tail = (ctrl_bone_e.head +
                                (last_bone_e.tail - last_bone_e.head))
            align_bone_z_axis(self.obj, ctrl_bone, Vector((0, 1, 0)))
            ctrl_chain.append(ctrl_bone)
            ctrl_bone_e.layers = last_bone_e.layers
            # ctrl_bone_e.layers = layers

        bpy.ops.object.mode_set(mode='OBJECT')
        pb = self.obj.pose.bones

        # Pose bone settings
        if self.params.chain_type in ('IK', 'Curve', 'Dynamic'):
            pbone = pb[ctrl_chain[-1]]
            pbone.rotation_mode = 'XZY'
            pbone.lock_location = (False, False, True)
            pbone.lock_rotation = (True, True, False)
            pbone.lock_rotation_w = False
            pbone.lock_scale = (False, False, False)

        if self.params.chain_type in ('IK', 'Curve', 'Dynamic'):
            # Widgets
            for ctrl_bone in ctrl_chain:
                global_scale = pb[ctrl_bone].length  # self.obj.dimensions[2]
                # member_factor = 0.06
                widget_size = global_scale * 0.3  # * member_factor
                pantin_utils.create_aligned_circle_widget(self.obj,
                                                          ctrl_bone,
                                                          radius=widget_size)

            # Constraints
            for org, mch in zip(self.org_bones, mch_chain):
                con = pb[org].constraints.new('COPY_TRANSFORMS')
                con.name = "copy_transforms"
                con.target = self.obj
                con.subtarget = mch
        else:
            # Widgets
            widget_size = 0.5
            for bone in ctrl_chain:
                pantin_utils.create_capsule_widget(self.obj,
                                                   bone,
                                                   length=widget_size,
                                                   width=widget_size * 0.1,
                                                   head_tail=0.5,
                                                   horizontal=False)

            # Constraints
            for org, ctrl in zip(self.org_bones, ctrl_chain):
                con = pb[org].constraints.new('COPY_TRANSFORMS')
                con.name = "copy_transforms"
                con.target = self.obj
                con.subtarget = ctrl

        ui_script = ""

        if self.params.chain_type == 'Curve':
            for ctrl, mch in zip(ctrl_chain[1:], mch_chain):
                con = pb[mch].constraints.new('STRETCH_TO')
                con.name = "stretch_to"
                con.target = self.obj
                con.subtarget = ctrl
                con.volume = 'NO_VOLUME'
                con.keep_axis = 'PLANE_Z'

        elif self.params.chain_type == 'IK':
            last_bone = mch_chain[-1]
            con = pb[last_bone].constraints.new('IK')
            con.target = self.obj
            con.subtarget = ctrl_bone
            con.chain_count = len(self.org_bones)

            # # Pelvis follow
            # if self.params.do_flip:
            #     pantin_utils.create_ik_child_of(
            #         self.obj, ctrl_bone, self.params.pelvis_name)

        elif self.params.chain_type == 'Dynamic':
            for ctrl, mch in zip(ctrl_chain, mch_chain):
                con = pb[mch].constraints.new('DAMPED_TRACK')
                con.name = "damped_track"
                con.target = empty_obj
                # con.volume = 'NO_VOLUME'
                # con.keep_axis = 'PLANE_Z'
                # con.rest_length = pb[ctrl].length

            ui_script = script % (ctrl, dyn_bone)  # % ctrl_bone, MCH-bone.dyn

        elif self.params.chain_type == 'Def':
            # Modify constraints to add side suffix
            if side in ('.L', '.R'):
                for b in self.org_bones:
                    for con in pb[b].constraints:
                        if (hasattr(con, 'subtarget')
                                and con.subtarget + side in pb):
                            con.subtarget += side

        return {
            'script': [ui_script],
            'imports': UI_IMPORTS,
            'utilities': PANTIN_UTILS,
            'register': PANTIN_REGISTER,
            'register_props': REGISTER_PANTIN_PROPS,
        }