Skip to content
Snippets Groups Projects
io_import_scene_mhx.py 133 KiB
Newer Older

def ik2fkArm(context, suffix):
    rig = context.object
    scn = context.scene
    auto = scn.tool_settings.use_keyframe_insert_auto
    print("Snap IK Arm%s" % suffix)
    (uparmIk, loarmIk, elbow, elbowPt, wrist) = getSnapBones(rig, "ArmIK", suffix)
    (uparmFk, loarmFk, elbowPtFk, handFk) = getSnapBones(rig, "ArmFK", suffix)
    #rig["MhaElbowFollowsShoulder" + suffix] = False
    #rig["MhaElbowFollowsWrist" + suffix] = False
    
    matchPoseTranslation(wrist, handFk, auto)
    matchPoseRotation(wrist, handFk, auto)  
    matchPoseTranslation(elbow, elbowPtFk, auto)
    matchPoseTranslation(elbowPt, elbowPtFk, auto)
    setInverse(rig, elbowPt)
    auto = context.scene.tool_settings.use_keyframe_insert_auto
    print("Snap FK Leg%s" % suffix)
    (uplegIk, lolegIk, kneePt, ankleIk, legIk, legFk, footIk, toeIk) = getSnapBones(rig, "LegIK", suffix)
    (uplegFk, lolegFk, kneePtFk, footFk, toeFk) = getSnapBones(rig, "LegFK", suffix)
    matchPoseRotation(uplegFk, uplegFk, auto)
    matchPoseScale(uplegFk, uplegFk, auto)
    matchPoseRotation(lolegFk, lolegFk, auto)
    matchPoseScale(lolegFk, lolegFk, auto)
def ik2fkLeg(context, suffix):
    rig = context.object
    scn = context.scene
    auto = scn.tool_settings.use_keyframe_insert_auto
    print("Snap IK Leg%s" % suffix)
    (uplegIk, lolegIk, kneePt, ankleIk, legIk, legFk, footIk, toeIk) = getSnapBones(rig, "LegIK", suffix)
    (uplegFk, lolegFk, kneePtFk, footFk, toeFk) = getSnapBones(rig, "LegFK", suffix)

    #rig["MhaKneeFollowsHip" + suffix] = False
    #rig["MhaKneeFollowsFoot" + suffix] = False
    legIkToAnkle = rig["MhaLegIkToAnkle" + suffix]
    if legIkToAnkle:
        matchPoseTranslation(ankleIk, footFk, auto)
    matchPoseTranslation(legIk, legFk, auto)
    matchPoseRotation(legIk, legFk, auto)  
    matchPoseReverse(toeIk, toeFk, auto)
    matchPoseReverse(footIk, footFk, auto)
    setInverse(rig, ankleIk)
    matchPoseTranslation(kneePt, kneePtFk, auto)
    setInverse(rig, kneePt)
    if not legIkToAnkle:
        matchPoseTranslation(ankleIk, footFk, auto)
    return
   
#
#   setInverse(rig, pb):
#

def setInverse(rig, pb):
    rig.data.bones.active = pb.bone
    pb.bone.select = True
    bpy.ops.object.mode_set(mode='OBJECT')
    bpy.ops.object.mode_set(mode='POSE')
    for cns in pb.constraints:
        if cns.type == 'CHILD_OF':
            bpy.ops.constraint.childof_set_inverse(constraint=cns.name, owner='BONE')
    bpy.ops.object.mode_set(mode='OBJECT')
    bpy.ops.object.mode_set(mode='POSE')
    return


def clearInverse(rig, pb):
    rig.data.bones.active = pb.bone
    pb.bone.select = True
    bpy.ops.object.mode_set(mode='OBJECT')
    bpy.ops.object.mode_set(mode='POSE')
    for cns in pb.constraints:
        if cns.type == 'CHILD_OF':
            bpy.ops.constraint.childof_clear_inverse(constraint=cns.name, owner='BONE')
    bpy.ops.object.mode_set(mode='OBJECT')
    bpy.ops.object.mode_set(mode='POSE')
    return

def fixAnkle(rig, suffix, scn):
    layers = list(rig.data.layers)
        rig.data.layers = 32*[True]
        setInverse(rig, rig.pose.bones["Ankle" + suffix])
        scn.frame_current = scn.frame_current
    finally:
        rig.data.layers = layers
    return

def clearAnkle(rig, suffix, scn):
    layers = list(rig.data.layers)
    try:
        rig.data.layers = 32*[True]
        clearInverse(rig, rig.pose.bones["Ankle" + suffix])
        scn.frame_current = scn.frame_current
    finally:
        rig.data.layers = layers

class VIEW3D_OT_FixAnkleButton(bpy.types.Operator):
    bl_idname = "mhx.fix_ankle"
    bl_label = "Fix ankle"
    bl_description = "Set inverse for ankle Child-of constraints"
    bl_options = {'UNDO'}
    suffix = StringProperty()

    def execute(self, context):
        fixAnkle(context.object, self.suffix, context.scene)
        return{'FINISHED'}    


class VIEW3D_OT_ClearAnkleButton(bpy.types.Operator):
    bl_idname = "mhx.clear_ankle"
    bl_label = "Clear ankle"
    bl_description = "Clear inverse for ankle Child-of constraints"
    bl_options = {'UNDO'}
    suffix = StringProperty()

    def execute(self, context):
        clearAnkle(context.object, self.suffix, context.scene)
        return{'FINISHED'}    
    "ArmFK" : ["UpArm", "LoArm", "ElbowPTFK", "Hand"],
    "ArmIK" : ["UpArmIK", "LoArmIK", "Elbow", "ElbowPT", "Wrist"],
    "LegFK" : ["UpLeg", "LoLeg", "KneePTFK", "Foot", "Toe"],
    "LegIK" : ["UpLegIK", "LoLegIK", "KneePT", "Ankle", "LegIK", "LegFK", "FootRev", "ToeRev"],
def getSnapBones(rig, key, suffix):
    names = SnapBones[key]
    pbones = []
    for name in names:
        pb = rig.pose.bones[name+suffix]
        pbones.append(pb)
    return tuple(pbones)

class VIEW3D_OT_MhxSnapFk2IkButton(bpy.types.Operator):
    bl_idname = "mhx.snap_fk_ik"
    bl_label = "Snap FK"
    bl_options = {'UNDO'}
    data = StringProperty()    

    def execute(self, context):
        bpy.ops.object.mode_set(mode='POSE')
        rig = context.object
        (prop, old) = setSnapProp(rig, self.data, 1.0, context, False)
            fk2ikArm(context, prop[-2:])
            fk2ikLeg(context, prop[-2:])
        restoreSnapProp(rig, prop, old, context)
class VIEW3D_OT_MhxSnapIk2FkButton(bpy.types.Operator):
    bl_idname = "mhx.snap_ik_fk"
    bl_label = "Snap IK"
    bl_options = {'UNDO'}
    data = StringProperty()    

    def execute(self, context):
        bpy.ops.object.mode_set(mode='POSE')
        rig = context.object
        (prop, old) = setSnapProp(rig, self.data, 0.0, context, True)
            ik2fkArm(context, prop[-2:])
            ik2fkLeg(context, prop[-2:])
        restoreSnapProp(rig, prop, old, context)
def setSnapProp(rig, data, value, context, isIk):
    words = data.split()
    prop = words[0]
    oldValue = rig[prop]
    rig[prop] = value
    ik = int(words[1])
    fk = int(words[2])
    extra = int(words[3])
    oldIk = rig.data.layers[ik]
    oldFk = rig.data.layers[fk]
    oldExtra = rig.data.layers[extra]
    rig.data.layers[ik] = True
    rig.data.layers[fk] = True
    rig.data.layers[extra] = True
    updatePose(context)
    if isIk:
        oldValue = 1.0
        oldIk = True
        oldFk = False
    else:
        oldValue = 0.0
        oldIk = False
        oldFk = True
        oldExtra = False
    return (prop, (oldValue, ik, fk, extra, oldIk, oldFk, oldExtra))
    
def restoreSnapProp(rig, prop, old, context):
    updatePose(context)
    (oldValue, ik, fk, extra, oldIk, oldFk, oldExtra) = old
    rig[prop] = oldValue
    rig.data.layers[ik] = oldIk
    rig.data.layers[fk] = oldFk
    rig.data.layers[extra] = oldExtra
    return

class VIEW3D_OT_MhxToggleFkIkButton(bpy.types.Operator):
    bl_idname = "mhx.toggle_fk_ik"
    bl_label = "FK - IK"
    bl_options = {'UNDO'}
    toggle = StringProperty()    

    def execute(self, context):
        words = self.toggle.split()
        rig = context.object
        prop = words[0]
        value = float(words[1]) 
        onLayer = int(words[2])
        offLayer = int(words[3])
        rig.data.layers[onLayer] = True
        rig.data.layers[offLayer] = False
        rig[prop] = value
        # Don't do autokey - confusing.
        #if context.tool_settings.use_keyframe_insert_auto:
        #    rig.keyframe_insert('["%s"]' % prop, frame=scn.frame_current)        
        updatePose(context)
    bl_label = "MHX FK/IK Switch"
    bl_space_type = "VIEW_3D"
    bl_region_type = "UI"
    #bl_options = {'DEFAULT_CLOSED'}
        return (context.object and context.object.MhxRig == 'MHX')
        rig = context.object
        layout = self.layout        

        row = layout.row()
        row.label("")
        row.label("Left")
        row.label("Right")
        layout.label("FK/IK switch")
        self.toggleButton(row, rig, "MhaArmIk_L", " 3", " 2")
        self.toggleButton(row, rig, "MhaArmIk_R", " 19", " 18")
        self.toggleButton(row, rig, "MhaLegIk_L", " 5", " 4")
        self.toggleButton(row, rig, "MhaLegIk_R", " 21", " 20")
        try:
            ok = (rig["MhxVersion"] >= 12)
        except:
            ok = False
        if not ok:
            layout.label("Snapping only works with MHX version 1.12 and later.")
            return
        
        layout.label("Snap Arm bones")
        row.label("FK Arm")
        row.operator("mhx.snap_fk_ik", text="Snap L FK Arm").data = "MhaArmIk_L 2 3 12"
        row.operator("mhx.snap_fk_ik", text="Snap R FK Arm").data = "MhaArmIk_R 18 19 28"
        row.label("IK Arm")
        row.operator("mhx.snap_ik_fk", text="Snap L IK Arm").data = "MhaArmIk_L 2 3 12"
        row.operator("mhx.snap_ik_fk", text="Snap R IK Arm").data = "MhaArmIk_R 18 19 28"
        layout.label("Snap Leg bones")
        row.label("FK Leg")
        row.operator("mhx.snap_fk_ik", text="Snap L FK Leg").data = "MhaLegIk_L 4 5 12"
        row.operator("mhx.snap_fk_ik", text="Snap R FK Leg").data = "MhaLegIk_R 20 21 28"
        row = layout.row()
        row.label("IK Leg")
        row.operator("mhx.snap_ik_fk", text="Snap L IK Leg").data = "MhaLegIk_L 4 5 12"
        row.operator("mhx.snap_ik_fk", text="Snap R IK Leg").data = "MhaLegIk_R 20 21 28"
        row = layout.row()
        row.label("Ankle")
        row.operator("mhx.fix_ankle", text="Fix L Ankle").suffix = "_L"
        row.operator("mhx.fix_ankle", text="Fix R Ankle").suffix = "_R"
        row.label("")
        row.operator("mhx.clear_ankle", text="Clear L Ankle").suffix = "_L"
        row.operator("mhx.clear_ankle", text="Clear R Ankle").suffix = "_R"
    def toggleButton(self, row, rig, prop, fk, ik):
        if rig[prop] > 0.5:
            row.operator("mhx.toggle_fk_ik", text="IK").toggle = prop + " 0" + fk + ik
        else:
            row.operator("mhx.toggle_fk_ik", text="FK").toggle = prop + " 1" + ik + fk
            

###################################################################################    
#
#    Posing panel
#
###################################################################################          
#
#    class MhxDriversPanel(bpy.types.Panel):
#

class MhxDriversPanel(bpy.types.Panel):
    bl_label = "MHX Drivers"
    bl_space_type = "VIEW_3D"
    bl_region_type = "UI"
    bl_options = {'DEFAULT_CLOSED'}
    
    @classmethod
    def poll(cls, context):
        return (context.object and context.object.MhxRig)
        plist = list(context.object.keys())
        plist.sort()
        for prop in plist:
            if prop[0:3] == 'Mha':
                if prop[-2:] == '_L':
                elif prop[-2:] != '_R':
            elif prop[0:3] == 'Mhf':
                if prop[-2:] == '_L':
                    lrFaceProps.append(prop[:-2])
                elif prop[-2:] != '_R':
                    faceProps.append(prop)
        ob = context.object
        layout = self.layout
        for prop in props:
            layout.prop(ob, prop, text=prop[3:])

        layout.separator()
        row = layout.row()
        row.label("Left")
        row.label("Right")
        for prop in lrProps:
            row = layout.row()
            row.prop(ob, prop+"_L", text=prop[3:])
            row.prop(ob, prop+"_R", text=prop[3:])

        if faceProps:
            layout.separator()
            layout.label("Face shapes")
                layout.prop(ob, prop, text=prop[3:])

            layout.separator()
            row = layout.row()
            row.label("Left")
            row.label("Right")
            for prop in lrFaceProps:
                row = layout.row()
                row.prop(ob, prop+"_L", text=prop[3:])
                row.prop(ob, prop+"_R", text=prop[3:])
###################################################################################    
#
#    Visibility panel
#
###################################################################################          
#
#    class MhxVisibilityPanel(bpy.types.Panel):
#

class MhxVisibilityPanel(bpy.types.Panel):
    bl_label = "MHX Visibility"
    bl_space_type = "VIEW_3D"
    bl_region_type = "UI"
    bl_options = {'DEFAULT_CLOSED'}
    
    @classmethod
    def poll(cls, context):
        return (context.object and context.object.MhxRig)

    def draw(self, context):
        ob = context.object
        props = list(ob.keys())
        props.sort()
        for prop in props:
            if prop[0:3] == "Mhh": 
                layout.prop(ob, prop, text="Hide %s" % prop[3:])
        layout.separator()
        layout.operator("mhx.update_textures")
        layout.separator()
        layout.operator("mhx.add_hiders")
        layout.operator("mhx.remove_hiders")
class VIEW3D_OT_MhxUpdateTexturesButton(bpy.types.Operator):
    bl_idname = "mhx.update_textures"
    bl_label = "Update"
    bl_options = {'UNDO'}

    def execute(self, context):
        scn = context.scene
        for mat in bpy.data.materials:
            if mat.animation_data:
                try:
                    mat["MhxDriven"]
                except:
                    continue
                for driver in mat.animation_data.drivers:
                    prop = mat.path_resolve(driver.data_path)
                    value = driver.evaluate(scn.frame_current)
                    #print("Update %s[%d] = %s" % (driver.data_path, driver.array_index, value))
                    prop[driver.array_index] = value
        return{'FINISHED'}    
        
class VIEW3D_OT_MhxAddHidersButton(bpy.types.Operator):
    bl_idname = "mhx.add_hiders"
    bl_label = "Add Hide Property"
    bl_options = {'UNDO'}

    def execute(self, context):
        rig = context.object
        for ob in context.scene.objects:
            if ob.select and ob != rig:
                rig[prop] = False        
                addHider(ob, "hide", rig, prop)
                addHider(ob, "hide_render", rig, prop)
        return{'FINISHED'}    
                
def addHider(ob, attr, rig, prop):
    fcu = ob.driver_add(attr)
    drv = fcu.driver
    drv.type = 'SCRIPTED'
    drv.expression = "x"
    drv.show_debug_info = True
    var = drv.variables.new()
    var.name = "x"
    targ = var.targets[0]
    targ.id = rig
    targ.data_path = '["%s"]' % prop
    return

class VIEW3D_OT_MhxRemoveHidersButton(bpy.types.Operator):
    bl_idname = "mhx.remove_hiders"
    bl_label = "Remove Hide Property"
    bl_options = {'UNDO'}
    def execute(self, context):
        rig = context.object
        for ob in context.scene.objects:
            if ob.select and ob != rig:
                ob.driver_remove("hide")
                ob.driver_remove("hide_render")
###################################################################################    
#
#    Layers panel
#
###################################################################################    

MhxLayers = [
    (( 0,    'Root', 'MhxRoot'),
     ( 8,    'Face', 'MhxFace')),
    (( 9,    'Tweak', 'MhxTweak'),
     (10,    'Head', 'MhxHead')),
    (( 1,    'FK Spine', 'MhxFKSpine'),
     (17,    'IK Spine', 'MhxIKSpine')),
    ((13,    'Inv FK Spine', 'MhxInvFKSpine'),
    ('Left', 'Right'),
    (( 2,    'IK Arm', 'MhxIKArm'),
     (18,    'IK Arm', 'MhxIKArm')),
    (( 3,    'FK Arm', 'MhxFKArm'),
     (19,    'FK Arm', 'MhxFKArm')),
    (( 4,    'IK Leg', 'MhxIKLeg'),
     (20,    'IK Leg', 'MhxIKLeg')),
    (( 5,    'FK Leg', 'MhxFKLeg'),
     (21,    'FK Leg', 'MhxFKLeg')),
    ((12,    'Extra', 'MhxExtra'),
     (28,    'Extra', 'MhxExtra')),
    (( 6,    'Fingers', 'MhxFingers'),
     (22,    'Fingers', 'MhxFingers')),
    (( 7,    'Links', 'MhxLinks'),
     (23,    'Links', 'MhxLinks')),
    ((11,    'Palm', 'MhxPalm'),
     (27,    'Palm', 'MhxPalm')),
]

#
#    class MhxLayersPanel(bpy.types.Panel):
#

class MhxLayersPanel(bpy.types.Panel):
    bl_label = "MHX Layers"
    bl_space_type = "VIEW_3D"
    bl_region_type = "UI"
    #bl_options = {'DEFAULT_CLOSED'}
    
    @classmethod
    def poll(cls, context):
        ob = context.object
        if (ob and ob.MhxRig == 'MHX'):
            return True
        return False

    def draw(self, context):
        layout = self.layout
        layout.operator("mhx.pose_enable_all_layers")
        layout.operator("mhx.pose_disable_all_layers")
        amt = context.object.data
        for (left,right) in MhxLayers:
            row = layout.row()
            if type(left) == str:
                row.label(left)
                row.label(right)
            else:
                for (n, name, prop) in [left,right]:
                    row.prop(amt, "layers", index=n, toggle=True, text=name)
        return

class VIEW3D_OT_MhxEnableAllLayersButton(bpy.types.Operator):
    bl_idname = "mhx.pose_enable_all_layers"
    bl_label = "Enable all layers"
    bl_options = {'UNDO'}

    def execute(self, context):
        for (left,right) in MhxLayers:
            if type(left) != str:
                for (n, name, prop) in [left,right]:
                    rig.data.layers[n] = True
        return{'FINISHED'}    

class VIEW3D_OT_MhxDisableAllLayersButton(bpy.types.Operator):
    bl_idname = "mhx.pose_disable_all_layers"
    bl_label = "Disable all layers"
    bl_options = {'UNDO'}
        layers = 32*[False]
        pb = context.active_pose_bone
        if pb:
            for n in range(32):
                if pb.bone.layers[n]:
                    layers[n] = True
                    break
        else:
            layers[0] = True
        rig.data.layers = layers            
        return{'FINISHED'}    
                
###################################################################################    
#
#    Common functions
#
###################################################################################    
#
def pollMhx(ob):        
    if not ob:
        return False
    elif ob.type == 'ARMATURE':
        return ob.MhxRig
    elif ob.type == 'MESH':
        par = ob.parent
        return (par and (par.type == 'ARMATURE') and par.MhxRig)
    else:
        return False

    if ob.type == 'ARMATURE':
        for mesh in ob.children:
            if mesh.MhxMesh and ob.MhxRig:
                return (ob, mesh)
        return (ob, None)                
    elif ob.type == 'MESH':
        par = ob.parent
        if (par and par.type == 'ARMATURE' and par.MhxRig):
            if ob.MhxMesh:
                return (par, ob)
            else:
                return (par, None)
        else:
    
        
#
#    setInterpolation(rig):
#

def setInterpolation(rig):
    if not rig.animation_data:
        return
    act = rig.animation_data.action
    if not act:
        return
    for fcu in act.fcurves:
        for pt in fcu.keyframe_points:
            pt.interpolation = 'LINEAR'
        fcu.extrapolation = 'CONSTANT'
    return
    
###################################################################################    
#
#    initialize and register
#
###################################################################################    

def menu_func(self, context):
    self.layout.operator(ImportMhx.bl_idname, text="MakeHuman (.mhx)...")
Brendon Murphy's avatar
Brendon Murphy committed
def register():
    bpy.types.Object.MhxMesh = BoolProperty(default=False)
    bpy.types.Object.MhxRig = StringProperty(default="")
    bpy.types.Object.MhxRigify = BoolProperty(default=False)
    bpy.types.Object.MhxShapekeyDrivers = BoolProperty(default=True)
    bpy.types.Object.MhxStrength = FloatProperty(
        name = "Expression strength",
        description = "Multiply expression with this factor",
        default=1.0, min=-1.0, max=2.0
        )
Luca Bonavita's avatar
Luca Bonavita committed
    bpy.types.INFO_MT_file_import.append(menu_func)
Brendon Murphy's avatar
Brendon Murphy committed
def unregister():
Luca Bonavita's avatar
Luca Bonavita committed
    try:
Luca Bonavita's avatar
Luca Bonavita committed
    except:
        pass
    try:
        bpy.types.INFO_MT_file_import.remove(menu_func)
    except:
        pass

if __name__ == "__main__":
    unregister()
Luca Bonavita's avatar
Luca Bonavita committed
    register()