Skip to content
Snippets Groups Projects
io_import_scene_mhx.py 134 KiB
Newer Older
        drawShapePanel(self, context, "Mhs", "expression")


class MhxCustomShapePanel(bpy.types.Panel):
    bl_label = "MHX Custom Shapes"
    bl_space_type = "VIEW_3D"
    bl_region_type = "UI"
    bl_options = {'DEFAULT_CLOSED'}

    def draw(self, context):
        drawShapePanel(self, context, "Mhc", "custom shape")
    restInv = pb.bone.matrix_local.inverted()
        parInv = pb.parent.matrix.inverted()
        parRest = pb.parent.bone.matrix_local
        return restInv * (parRest * (parInv * gmat))
def getGlobalMatrix(mat, pb):
    gmat = pb.bone.matrix_local * mat
    if pb.parent:
        parMat = pb.parent.matrix
        parRest = pb.parent.bone.matrix_local
        return parMat * (parRest.inverted() * gmat)
    else:
        return gmat
def matchPoseTranslation(pb, src, auto):
    pmat = getPoseMatrix(src.matrix, pb)
    insertLocation(pb, pmat, auto)
    pb.location = mat.to_translation()
    if auto:
        pb.keyframe_insert("location", group=pb.name)
    bpy.ops.object.mode_set(mode='OBJECT')
    bpy.ops.object.mode_set(mode='POSE')
def matchPoseRotation(pb, src, auto):
    pmat = getPoseMatrix(src.matrix, pb)
    insertRotation(pb, pmat, auto)


def printMatrix(string,mat):
    print(string)
    for i in range(4):
        print("    %.4g %.4g %.4g %.4g" % tuple(mat[i]))
    q = mat.to_quaternion()
    if pb.rotation_mode == 'QUATERNION':
        pb.rotation_quaternion = q
        if auto:
            pb.keyframe_insert("rotation_quaternion", group=pb.name)
    else:
        pb.rotation_euler = q.to_euler(pb.rotation_mode)
        if auto:
            pb.keyframe_insert("rotation_euler", group=pb.name)
    bpy.ops.object.mode_set(mode='OBJECT')
    bpy.ops.object.mode_set(mode='POSE')


def matchIkLeg(legIk, toeFk, mBall, mToe, mHeel, auto):
    rmat = toeFk.matrix.to_3x3()
    tHead = Vector(toeFk.matrix.col[3][:3])
    ty = rmat.col[1]
    tail = tHead + ty * toeFk.bone.length

    try:
        zBall = mBall.matrix.col[3][2]
    except AttributeError:
        return
    zToe = mToe.matrix.col[3][2]
    zHeel = mHeel.matrix.col[3][2]

    x = Vector(rmat.col[0])
    y = Vector(rmat.col[1])
    z = Vector(rmat.col[2])

    if zHeel > zBall and zHeel > zToe:
        # 1. foot.ik is flat
        if abs(y[2]) > abs(z[2]):
            y = -z
        y[2] = 0
    else:
        # 2. foot.ik starts at heel
        hHead = Vector(mHeel.matrix.col[3][:3])
        y = tail - hHead

    y.normalize()
    x -= x.dot(y)*y
    x.normalize()
    z = x.cross(y)
    head = tail - y * legIk.bone.length

    # Create matrix
    gmat = Matrix()
    gmat.col[0][:3] = x
    gmat.col[1][:3] = y
    gmat.col[2][:3] = z
    gmat.col[3][:3] = head
    pmat = getPoseMatrix(gmat, legIk)

    insertLocation(legIk, pmat, auto)
    insertRotation(legIk, pmat, auto)


def matchPoleTarget(pb, above, below, auto):
    x = Vector(above.matrix.col[1][:3])
    y = Vector(below.matrix.col[1][:3])
    p0 = Vector(below.matrix.col[3][:3])
    n = x.cross(y)
    if abs(n.length) > 1e-4:
        z = x - y
        n = n/n.length
        z -= z.dot(n)*n
        p = p0 + z/z.length*3.0
    else:
        p = p0
    gmat = Matrix.Translation(p)
    pmat = getPoseMatrix(gmat, pb)
    insertLocation(pb, pmat, auto)


def matchPoseReverse(pb, src, auto):
    gmat = src.matrix
    tail = gmat.col[3] + src.length * gmat.col[1]
    rmat = Matrix((gmat.col[0], -gmat.col[1], -gmat.col[2], tail))
    rmat.transpose()
    pmat = getPoseMatrix(rmat, pb)
    pb.matrix_basis = pmat
    insertRotation(pb, pmat, auto)


def matchPoseScale(pb, src, auto):
    pmat = getPoseMatrix(src.matrix, pb)
    pb.scale = pmat.to_scale()
    if auto:
        pb.keyframe_insert("scale", group=pb.name)
    bpy.ops.object.mode_set(mode='OBJECT')
    bpy.ops.object.mode_set(mode='POSE')
    prop,old,suffix = setSnapProp(rig, data, 1.0, context, False)
    auto = context.scene.tool_settings.use_keyframe_insert_auto
    print("Snap FK Arm%s" % suffix)
Thomas Larsson's avatar
Thomas Larsson committed
    snapFk,cnsFk = getSnapBones(rig, "ArmFK", suffix)
Thomas Larsson's avatar
Thomas Larsson committed
    muteConstraints(cnsFk, True)
    snapIk,cnsIk = getSnapBones(rig, "ArmIK", suffix)
    (uparmIk, loarmIk, elbow, elbowPt, handIk) = snapIk
    matchPoseRotation(uparmFk, uparmIk, auto)
    matchPoseScale(uparmFk, uparmIk, auto)
    matchPoseRotation(loarmFk, loarmIk, auto)
    matchPoseScale(loarmFk, loarmIk, auto)

    restoreSnapProp(rig, prop, old, context)
    try:
        matchHand = rig["MhaHandFollowsWrist" + suffix]
    except KeyError:
        matchHand = True
    if matchHand:
        matchPoseRotation(handFk, handIk, auto)
        matchPoseScale(handFk, handIk, auto)
    prop,old,suffix = setSnapProp(rig, data, 0.0, context, True)
    auto = context.scene.tool_settings.use_keyframe_insert_auto

    print("Snap IK Arm%s" % suffix)
Thomas Larsson's avatar
Thomas Larsson committed
    snapIk,cnsIk = getSnapBones(rig, "ArmIK", suffix)
    (uparmIk, loarmIk, elbow, elbowPt, handIk) = snapIk
Thomas Larsson's avatar
Thomas Larsson committed
    snapFk,cnsFk = getSnapBones(rig, "ArmFK", suffix)
Thomas Larsson's avatar
Thomas Larsson committed
    muteConstraints(cnsIk, True)
    matchPoseTranslation(handIk, handFk, auto)
    matchPoseRotation(handIk, handFk, auto)

    matchPoleTarget(elbowPt, uparmFk, loarmFk, auto)

    #matchPoseRotation(uparmIk, uparmFk, auto)
    #matchPoseRotation(loarmIk, loarmFk, auto)

    restoreSnapProp(rig, prop, old, context)
    #muteConstraints(cnsIk, False)
    prop,old,suffix = setSnapProp(rig, data, 1.0, context, False)
    auto = context.scene.tool_settings.use_keyframe_insert_auto
    print("Snap FK Leg%s" % suffix)
    snap,_ = getSnapBones(rig, "Leg", suffix)
    (upleg, loleg, foot, toe) = snap
    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(cnsFk, True)
    matchPoseRotation(uplegFk, uplegIk, auto)
    matchPoseScale(uplegFk, uplegIk, auto)

    matchPoseRotation(lolegFk, lolegIk, auto)
    matchPoseScale(lolegFk, lolegIk, auto)
    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'}

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