Skip to content
Snippets Groups Projects
io_import_scene_mhx.py 130 KiB
Newer Older
    restoreSnapProp(rig, prop, old, context)
Thomas Larsson's avatar
Thomas Larsson committed
    if not rig["MhaLegIkToAnkle" + suffix]:
        matchPoseReverse(footFk, footRev, auto)
        matchPoseReverse(toeFk, toeRev, auto)
    scn = context.scene
    prop,old,suffix = setSnapProp(rig, data, 0.0, context, True)
    auto = scn.tool_settings.use_keyframe_insert_auto
    print("Snap IK Leg%s" % suffix)
Thomas Larsson's avatar
Thomas Larsson committed
    snapIk,cnsIk = getSnapBones(rig, "LegIK", suffix)
    (uplegIk, lolegIk, kneePt, ankleIk, legIk, footRev, toeRev, mBall, mToe, mHeel) = snapIk
Thomas Larsson's avatar
Thomas Larsson committed
    snapFk,cnsFk = getSnapBones(rig, "LegFK", suffix)
    (uplegFk, lolegFk, footFk, toeFk) = snapFk
Thomas Larsson's avatar
Thomas Larsson committed
    muteConstraints(cnsIk, True)
    legIkToAnkle = rig["MhaLegIkToAnkle" + suffix]
    if legIkToAnkle:
        matchPoseTranslation(ankleIk, footFk, auto)
    #matchPoseTranslation(legIk, legFk, auto)
    #matchPoseRotation(legIk, legFk, auto)
    matchIkLeg(legIk, toeFk, mBall, mToe, mHeel, auto)
    matchPoseReverse(toeRev, toeFk, auto)
    matchPoseReverse(footRev, footFk, auto)

    matchPoleTarget(kneePt, uplegFk, lolegFk, auto)

    #matchPoseRotation(uplegIk, uplegFk, auto)
    #matchPoseRotation(lolegIk, lolegFk, auto)
    if not legIkToAnkle:
        matchPoseTranslation(ankleIk, footFk, auto)
    restoreSnapProp(rig, prop, old, context)
    #muteConstraints(cnsIk, False)
SnapBonesAlpha8 = {
    "Arm"   : ["upper_arm", "forearm", "hand"],
    "ArmFK" : ["upper_arm.fk", "forearm.fk", "hand.fk"],
    "ArmIK" : ["upper_arm.ik", "forearm.ik", None, "elbow.pt.ik", "hand.ik"],
    "Leg"   : ["thigh", "shin", "foot", "toe"],
    "LegFK" : ["thigh.fk", "shin.fk", "foot.fk", "toe.fk"],
    "LegIK" : ["thigh.ik", "shin.ik", "knee.pt.ik", "ankle.ik", "foot.ik", "foot.rev", "toe.rev", "ball.marker", "toe.marker", "heel.marker"],
    if pb is not None:
        raise NameError("MakeHuman alpha 7 not supported after Blender 2.68")

    try:
        rig.pose.bones["thigh.fk.L"]
        names = SnapBonesAlpha8[key]
        suffix = '.' + suffix[1:]
    except KeyError:
        names = None

    if not names:
        raise NameError("Not an mhx armature")
Thomas Larsson's avatar
Thomas Larsson committed
    constraints = []
            try:
                pb = rig.pose.bones[name+suffix]
            except KeyError:
                pb = None
            if pb is not None:
                for cns in pb.constraints:
                    if cns.type == 'LIMIT_ROTATION' and not cns.mute:
                        constraints.append(cns)
Thomas Larsson's avatar
Thomas Larsson committed
    return tuple(pbones),constraints


def muteConstraints(constraints, value):
    for cns in constraints:
        cns.mute = value
class VIEW3D_OT_MhxSnapFk2IkButton(bpy.types.Operator):
    bl_idname = "mhx.snap_fk_ik"
    bl_label = "Snap FK"
    bl_options = {'UNDO'}

    def execute(self, context):
        bpy.ops.object.mode_set(mode='POSE')
        rig = context.object
Thomas Larsson's avatar
Thomas Larsson committed
        if rig.MhxSnapExact:
            rig["MhaRotationLimits"] = 0.0
        if self.data[:6] == "MhaArm":
            snapFkArm(context, self.data)
        elif self.data[:6] == "MhaLeg":
            snapFkLeg(context, self.data)
class VIEW3D_OT_MhxSnapIk2FkButton(bpy.types.Operator):
    bl_idname = "mhx.snap_ik_fk"
    bl_label = "Snap IK"
    bl_options = {'UNDO'}

    def execute(self, context):
        bpy.ops.object.mode_set(mode='POSE')
        rig = context.object
Thomas Larsson's avatar
Thomas Larsson committed
        if rig.MhxSnapExact:
            rig["MhaRotationLimits"] = 0.0
        if self.data[:6] == "MhaArm":
            snapIkArm(context, self.data)
        elif self.data[:6] == "MhaLeg":
            snapIkLeg(context, self.data)
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), prop[-2:])
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
class VIEW3D_OT_MhxToggleFkIkButton(bpy.types.Operator):
    bl_idname = "mhx.toggle_fk_ik"
    bl_label = "FK - IK"
    bl_options = {'UNDO'}

    def execute(self, context):
        words = self.toggle.split()
        rig = context.object
        prop = words[0]
        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')

        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")
Thomas Larsson's avatar
Thomas Larsson committed
        layout.label("IK Influence")
        row = layout.row()
        row.label("Arm")
        row.prop(rig, '["MhaArmIk_L"]', text="")
        row.prop(rig, '["MhaArmIk_R"]', text="")
        row = layout.row()
        row.label("Leg")
        row.prop(rig, '["MhaLegIk_L"]', text="")
        row.prop(rig, '["MhaLegIk_R"]', text="")
        try:
            ok = (rig["MhxVersion"] >= 12)
        except:
            ok = False
        if not ok:
            layout.label("Snapping only works with MHX version 1.12 and later.")
            return
Thomas Larsson's avatar
Thomas Larsson committed
        layout.separator()
        layout.label("Snapping")
        row = layout.row()
        row.label("Rotation Limits")
        row.prop(rig, '["MhaRotationLimits"]', text="")
        row.prop(rig, "MhxSnapExact", text="Exact Snapping")
        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"
    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


###################################################################################
###################################################################################
#
#    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 == 'MHX')
        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
            layout.prop(ob, '["%s"]' % 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, '["%s"]' % (prop+"_L"), text=prop[3:])
            row.prop(ob, '["%s"]' % (prop+"_R"), text=prop[3:])
        if faceProps:
            layout.separator()
            layout.label("Face shapes")
                layout.prop(ob, '["%s"]' % 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, '["%s"]' % (prop+"_L"), text=prop[3:])
                row.prop(ob, '["%s"]' % (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:
                layout.prop(ob, '["%s"]' % 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)
                    prop[driver.array_index] = value
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:
                defNewProp(prop, "Bool", "default=False")
                addHider(ob, "hide", rig, prop)
                addHider(ob, "hide_render", rig, prop)
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")
        return{'FINISHED'}

###################################################################################
###################################################################################

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):
        return (context.object and context.object.MhxRig == 'MHX')

    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]:

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
        return{'FINISHED'}

###################################################################################
###################################################################################
    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)
    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

###################################################################################
###################################################################################
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.MhAlpha8 = BoolProperty(default=True)
    bpy.types.Object.MhxMesh = BoolProperty(default=False)
    bpy.types.Object.MhxRig = StringProperty(default="")
    bpy.types.Object.MhxVisemeSet = StringProperty(default="")
    bpy.types.Object.MhxRigify = BoolProperty(default=False)
Thomas Larsson's avatar
Thomas Larsson committed
    bpy.types.Object.MhxSnapExact = 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()