Skip to content
Snippets Groups Projects
io_import_scene_mhx.py 134 KiB
Newer Older
# ##### BEGIN GPL LICENSE BLOCK #####
#
#  This program is free software; you can redistribute it and/or
#  modify it under the terms of the GNU General Public License
#  as published by the Free Software Foundation; either version 2
#  of the License, or (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software Foundation,
#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
Brendon Murphy's avatar
Brendon Murphy committed

# Project Name:        MakeHuman
# Product Home Page:   http://www.makehuman.org/
# Code Home Page:      http://code.google.com/p/makehuman/
# Authors:             Thomas Larsson
# Script copyright (C) MakeHuman Team 2001-2013
# Coding Standards: See http://www.makehuman.org/node/165
Brendon Murphy's avatar
Brendon Murphy committed

Brendon Murphy's avatar
Brendon Murphy committed
Abstract
MHX (MakeHuman eXchange format) importer for Blender 2.6x.

This script should be distributed with Blender.
If not, place it in the .blender/scripts/addons dir
Activate the script in the "Addons" tab (user preferences).
Access from the File > Import menu.
Brendon Murphy's avatar
Brendon Murphy committed

Alternatively, run the script in the script editor (Alt-P), and access from the File > Import menu
Brendon Murphy's avatar
Brendon Murphy committed
"""

bl_info = {
Luca Bonavita's avatar
Luca Bonavita committed
    'name': 'Import: MakeHuman (.mhx)',
    'author': 'Thomas Larsson',
    'location': "File > Import > MakeHuman (.mhx)",
Luca Bonavita's avatar
Luca Bonavita committed
    'description': 'Import files in the MakeHuman eXchange format (.mhx)',
    'warning': '',
    'wiki_url': 'http://www.makehuman.org/documentation',
Luca Bonavita's avatar
Luca Bonavita committed
    'tracker_url': 'https://projects.blender.org/tracker/index.php?'\
        'func=detail&aid=21872',
Luca Bonavita's avatar
Luca Bonavita committed
    'category': 'Import-Export'}

majorVersion = MAJOR_VERSION
minorVersion = MINOR_VERSION
Brendon Murphy's avatar
Brendon Murphy committed
#
#
#

import bpy
import os
import time
from mathutils import Vector, Matrix, Quaternion
from bpy.props import *
Brendon Murphy's avatar
Brendon Murphy committed

MHX249 = False
Blender24 = False
Blender25 = True
theDir = "~/makehuman/exports"
Brendon Murphy's avatar
Brendon Murphy committed

#
#
#

theScale = 1.0
Brendon Murphy's avatar
Brendon Murphy committed
useMesh = 1
verbosity = 2
warnedTextureDir = False
warnedVersion = False

true = True
false = False
Epsilon = 1e-6
nErrors = 0
theTempDatum = None
theMessage = ""
theMhxFile = ""
Luca Bonavita's avatar
Luca Bonavita committed
#    toggle flags
T_EnforceVersion = 0x01
T_Clothes = 0x02
Brendon Murphy's avatar
Brendon Murphy committed
T_Replace = 0x20
T_Shapekeys = 0x40
T_ShapeDrivers = 0x80

T_Face = T_Shapekeys
T_Shape = T_Shapekeys
Brendon Murphy's avatar
Brendon Murphy committed
T_Mesh = 0x100
T_Armature = 0x200
T_Proxy = 0x400
Brendon Murphy's avatar
Brendon Murphy committed

T_Rigify = 0x1000
Brendon Murphy's avatar
Brendon Murphy committed
T_Symm = 0x4000

DefaultToggle = ( T_EnforceVersion + T_Mesh + T_Armature +
    T_Shapekeys + T_ShapeDrivers + T_Proxy + T_Clothes + T_Rigify )
toggle = DefaultToggle
toggleSettings = toggle
#   mhxEval(expr) - an attempt at a reasonably safe eval.
#   Note that expr never contains any whitespace due to the behavior
#   of the mhx tokenizer.
#

def mhxEval(expr, locls={}):
    globls = {
        '__builtins__' : {},
        'toggle' : toggle,
        'theScale' : theScale,
        'T_EnforceVersion' : T_EnforceVersion,
        'T_Clothes' : T_Clothes,
        'T_HardParents' : T_HardParents,
        'T_CrashSafe' : T_CrashSafe,
        'T_Diamond' : T_Diamond,
        'T_Replace' : T_Replace,
        'T_Shapekeys' : T_Shapekeys,
        'T_ShapeDrivers' : T_ShapeDrivers,
        'T_Face' : T_Face,
        'T_Shape' : T_Shape,
        'T_Mesh' : T_Mesh,
        'T_Armature' : T_Armature,
        'T_Proxy' : T_Proxy,
        'T_Cage' : T_Cage,
        'T_Rigify' : T_Rigify,
        'T_Opcns' : T_Opcns,
        'T_Symm' : T_Symm,
    }
    return eval(expr, globls, locls)
Luca Bonavita's avatar
Luca Bonavita committed
#    Dictionaries
def initLoadedData():
    global loadedData

    loadedData = {
Luca Bonavita's avatar
Luca Bonavita committed
    'NONE' : {},

    'Object' : {},
    'Mesh' : {},
    'Armature' : {},
    'Lamp' : {},
    'Camera' : {},
    'Lattice' : {},
    'Curve' : {},
    'Text' : {},

    'Material' : {},
    'Image' : {},
    'MaterialTextureSlot' : {},
    'Texture' : {},
Luca Bonavita's avatar
Luca Bonavita committed
    'Bone' : {},
    'BoneGroup' : {},
    'Rigify' : {},

    'Action' : {},
    'Group' : {},

    'MeshTextureFaceLayer' : {},
    'MeshColorLayer' : {},
    'VertexGroup' : {},
    'ShapeKey' : {},
    'ParticleSystem' : {},

    'ObjectConstraints' : {},
    'ObjectModifiers' : {},
    'MaterialSlot' : {},
def reinitGlobalData():
    global loadedData
    for key in [
        'MeshTextureFaceLayer', 'MeshColorLayer', 'VertexGroup', 'ShapeKey',
        'ParticleSystem', 'ObjectConstraints', 'ObjectModifiers', 'MaterialSlot']:
        loadedData[key] = {}
    return
Brendon Murphy's avatar
Brendon Murphy committed

Plural = {
Luca Bonavita's avatar
Luca Bonavita committed
    'Object' : 'objects',
    'Mesh' : 'meshes',
    'Lattice' : 'lattices',
    'Curve' : 'curves',
    'Text' : 'texts',
    'Group' : 'groups',
    'Empty' : 'empties',
    'Armature' : 'armatures',
    'Bone' : 'bones',
    'BoneGroup' : 'bone_groups',
    'Pose' : 'poses',
    'PoseBone' : 'pose_bones',
    'Material' : 'materials',
    'Texture' : 'textures',
    'Image' : 'images',
    'Camera' : 'cameras',
    'Lamp' : 'lamps',
    'World' : 'worlds',
    global nErrors, theScale, theArmature, defaultScale, One
    global toggle, warnedVersion, theMessage, alpha7, theDir
Luca Bonavita's avatar
Luca Bonavita committed

Luca Bonavita's avatar
Luca Bonavita committed
    One = 1.0/theScale
    theArmature = None
Luca Bonavita's avatar
Luca Bonavita committed

    theDir = os.path.dirname(filePath)
Luca Bonavita's avatar
Luca Bonavita committed
    fileName = os.path.expanduser(filePath)
    _,ext = os.path.splitext(fileName)
Luca Bonavita's avatar
Luca Bonavita committed
    if ext.lower() != ".mhx":
        print("Error: Not a mhx file: %s" % fileName.encode('utf-8', 'strict'))
Luca Bonavita's avatar
Luca Bonavita committed
        return
    print( "Opening MHX file %s " % fileName.encode('utf-8', 'strict') )
    print("Toggle %x" % toggle)
Luca Bonavita's avatar
Luca Bonavita committed
    time1 = time.clock()

Campbell Barton's avatar
Campbell Barton committed
    # ignore = False  # UNUSED
Luca Bonavita's avatar
Luca Bonavita committed
    stack = []
    tokens = []
    key = "toplevel"
    level = 0
    nErrors = 0
    comment = 0
    nesting = 0

    file= open(fileName, "rU")
    print( "Tokenizing" )
    lineNo = 0
Luca Bonavita's avatar
Luca Bonavita committed
        # print(line)
        lineSplit= line.split()
        lineNo += 1
        if len(lineSplit) == 0:
            pass
        elif lineSplit[0][0] == '#':
            if lineSplit[0] == '#if':
                if comment == nesting:
                    try:
                        res = mhxEval(lineSplit[1])
Luca Bonavita's avatar
Luca Bonavita committed
                    except:
                        res = False
                    if res:
                        comment += 1
                nesting += 1
            elif lineSplit[0] == '#else':
                if comment == nesting-1:
                    comment += 1
                elif comment == nesting:
                    comment -= 1
            elif lineSplit[0] == '#endif':
                if comment == nesting:
                    comment -= 1
                nesting -= 1
        elif comment < nesting:
            pass
        elif lineSplit[0] == 'end':
            try:
                sub = tokens
                tokens = stack.pop()
                if tokens:
                    tokens[-1][2] = sub
                level -= 1
            except:
                print( "Tokenizer error at or before line %d.\nThe mhx file has been corrupted.\nTry to export it again from MakeHuman." % lineNo )
Luca Bonavita's avatar
Luca Bonavita committed
                print( line )
Campbell Barton's avatar
Campbell Barton committed
                stack.pop()
Luca Bonavita's avatar
Luca Bonavita committed
        elif lineSplit[-1] == ';':
            if lineSplit[0] == '\\':
                key = lineSplit[1]
                tokens.append([key,lineSplit[2:-1],[]])
            else:
                key = lineSplit[0]
                tokens.append([key,lineSplit[1:-1],[]])
        else:
            key = lineSplit[0]
            tokens.append([key,lineSplit[1:],[]])
            stack.append(tokens)
            level += 1
            tokens = []
    file.close()

    if level != 0:
        MyError("Tokenizer error (%d).\nThe mhx file has been corrupted.\nTry to export it again from MakeHuman." % level)
    scn = clearScene()
Luca Bonavita's avatar
Luca Bonavita committed
    print( "Parsing" )
    parse(tokens)
    bpy.ops.object.mode_set(mode='OBJECT')
    bpy.ops.object.select_all(action='DESELECT')
    theArmature.select = True
    bpy.ops.object.mode_set(mode='POSE')
    theArmature.MhAlpha8 = not alpha7
    #bpy.ops.wm.properties_edit(data_path="object", property="MhxRig", value=theArmature["MhxRig"])
Luca Bonavita's avatar
Luca Bonavita committed
    time2 = time.clock()
    msg = "File %s loaded in %g s" % (fileName, time2-time1)
    if nErrors:
        msg += " but there where %d errors. " % (nErrors)
    print(msg)
    return

#
#    getObject(name, var):
def getObject(name, var):
Luca Bonavita's avatar
Luca Bonavita committed
    try:
        return loadedData['Object'][name]
Luca Bonavita's avatar
Luca Bonavita committed
    except:
        raise MhxError("Bug: object %s not found" % ob)
Luca Bonavita's avatar
Luca Bonavita committed
#    checkMhxVersion(major, minor):
#

def checkMhxVersion(major, minor):
Luca Bonavita's avatar
Luca Bonavita committed
    global warnedVersion
    print("MHX", (major,minor), (MAJOR_VERSION, MINOR_VERSION), warnedVersion)
    if  major != MAJOR_VERSION or minor < FROM_VERSION:
Luca Bonavita's avatar
Luca Bonavita committed
        if warnedVersion:
            return
        else:
            msg = (
"Expected MHX %d.%02d but the loaded file " % (MAJOR_VERSION, MINOR_VERSION) +
"has version MHX %d.%02d\n" % (major, minor))
"You can disable this error message by deselecting the \n" +
"Enforce version option when importing. Better:\n" +
"Export the MHX file again with an updated version of MakeHuman.\n" +
"The most up-to-date version of MakeHuman is the nightly build.\n")
            else:
                msg += (
"Download the most recent Blender build from www.graphicall.org. \n" +
"The most up-to-date version of the import script is distributed\n" +
"with Blender. It can also be downloaded from MakeHuman. \n" +
"It is located in the importers/mhx/blender25x \n" +
"folder and is called import_scene_mhx.py. \n")
        if (toggle & T_EnforceVersion or minor > MINOR_VERSION):
            MyError(msg)
Luca Bonavita's avatar
Luca Bonavita committed
        else:
            print(msg)
            warnedVersion = True
    return
Luca Bonavita's avatar
Luca Bonavita committed
#    parse(tokens):
Brendon Murphy's avatar
Brendon Murphy committed
#

ifResult = False

def parse(tokens):
Luca Bonavita's avatar
Luca Bonavita committed
    global MHX249, ifResult, theScale, defaultScale, One
    global majorVersion, minorVersion

    for (key, val, sub) in tokens:
Luca Bonavita's avatar
Luca Bonavita committed
        data = None
        if key == 'MHX':
            print("MHX importer version: ", bl_info["version"])
            majorVersion = int(val[0])
            minorVersion = int(val[1])
            checkMhxVersion(majorVersion, minorVersion)
            for string in val[2:]:
                print(string.replace("_"," "))
Luca Bonavita's avatar
Luca Bonavita committed
        elif key == 'MHX249':
            MHX249 = mhxEval(val[0])
Luca Bonavita's avatar
Luca Bonavita committed
            print("Blender 2.49 compatibility mode is %s\n" % MHX249)
        elif MHX249:
            pass
        elif key == 'print':
            msg = concatList(val)
            print(msg)
        elif key == 'warn':
            msg = concatList(val)
            print(msg)
        elif key == 'error':
            msg = concatList(val)
Luca Bonavita's avatar
Luca Bonavita committed
        elif key == 'NoScale':
Luca Bonavita's avatar
Luca Bonavita committed
                theScale = 1.0
            else:
Luca Bonavita's avatar
Luca Bonavita committed
            One = 1.0/theScale
        elif key == "Object":
            parseObject(val, sub)
        elif key == "Mesh":
Luca Bonavita's avatar
Luca Bonavita committed
            data = parseMesh(val, sub)
        elif key == "Armature":
            data = parseArmature(val, sub)
        elif key == "Pose":
            data = parsePose(val, sub)
        elif key == "Action":
            data = parseAction(val, sub)
        elif key == "Material":
            data = parseMaterial(val, sub)
        elif key == "Texture":
            data = parseTexture(val, sub)
        elif key == "Image":
            data = parseImage(val, sub)
        elif key == "Curve":
            data = parseCurve(val, sub)
        elif key == "TextCurve":
            data = parseTextCurve(val, sub)
        elif key == "Lattice":
            data = parseLattice(val, sub)
        elif key == "Group":
            data = parseGroup(val, sub)
        elif key == "Lamp":
            data = parseLamp(val, sub)
        elif key == "World":
            data = parseWorld(val, sub)
        elif key == "Scene":
            data = parseScene(val, sub)
        elif key == "DefineProperty":
            parseDefineProperty(val, sub)
Luca Bonavita's avatar
Luca Bonavita committed
        elif key == "Process":
            parseProcess(val, sub)
        elif key == "PostProcess":
            postProcess(val)
            hideLayers(val)
        elif key == "CorrectRig":
            correctRig(val)
        elif key == "Rigify":
            if toggle & T_Rigify:
Luca Bonavita's avatar
Luca Bonavita committed
        elif key == 'AnimationData':
            try:
                ob = loadedData['Object'][val[0]]
            except:
                ob = None
            if ob:
                bpy.context.scene.objects.active = ob
                parseAnimationData(ob, val, sub)
        elif key == 'MaterialAnimationData':
            try:
                ob = loadedData['Object'][val[0]]
            except:
                ob = None
            if ob:
                bpy.context.scene.objects.active = ob
                mat = ob.data.materials[int(val[2])]
                parseAnimationData(mat, val, sub)
        elif key == 'ShapeKeys':
            try:
                ob = loadedData['Object'][val[0]]
            except:
                MyError("ShapeKeys object %s does not exist" % val[0])
Luca Bonavita's avatar
Luca Bonavita committed
            if ob:
                bpy.context.scene.objects.active = ob
Luca Bonavita's avatar
Luca Bonavita committed
        else:
Luca Bonavita's avatar
Luca Bonavita committed

#
#    parseDefaultType(typ, args, tokens):
Brendon Murphy's avatar
Brendon Murphy committed
#

def parseDefaultType(typ, args, tokens):
Luca Bonavita's avatar
Luca Bonavita committed
    name = args[0]
    data = None
    expr = "bpy.data.%s.new('%s')" % (Plural[typ], name)
Brendon Murphy's avatar
Brendon Murphy committed

Luca Bonavita's avatar
Luca Bonavita committed
    bpyType = typ.capitalize()
    loadedData[bpyType][name] = data
Luca Bonavita's avatar
Luca Bonavita committed
        return None
Brendon Murphy's avatar
Brendon Murphy committed

Luca Bonavita's avatar
Luca Bonavita committed
    for (key, val, sub) in tokens:
        defaultKey(key, val, sub, data)
Luca Bonavita's avatar
Luca Bonavita committed
    return data
Brendon Murphy's avatar
Brendon Murphy committed
#
Luca Bonavita's avatar
Luca Bonavita committed
#    concatList(elts)
Brendon Murphy's avatar
Brendon Murphy committed
#

def concatList(elts):
Luca Bonavita's avatar
Luca Bonavita committed
    string = ""
    for elt in elts:
        string += " %s" % elt
    return string
Luca Bonavita's avatar
Luca Bonavita committed
#    parseAction(args, tokens):
#    parseFCurve(fcu, args, tokens):
#    parseKeyFramePoint(pt, args, tokens):
Brendon Murphy's avatar
Brendon Murphy committed
#

def parseAction(args, tokens):
Luca Bonavita's avatar
Luca Bonavita committed
    name = args[0]
    if invalid(args[1]):
        return

    ob = bpy.context.object
    bpy.ops.object.mode_set(mode='POSE')
    if ob.animation_data:
        ob.animation_data.action = None
    created = {}
    for (key, val, sub) in tokens:
        if key == 'FCurve':
            prepareActionFCurve(ob, created, val, sub)
Luca Bonavita's avatar
Luca Bonavita committed
    act = ob.animation_data.action
    loadedData['Action'][name] = act
Luca Bonavita's avatar
Luca Bonavita committed
        print("Ignoring action %s" % name)
        return act
    act.name = name
    print("Action", name, act, ob)
Luca Bonavita's avatar
Luca Bonavita committed
    for (key, val, sub) in tokens:
        if key == 'FCurve':
            fcu = parseActionFCurve(act, ob, val, sub)
        else:
            defaultKey(key, val, sub, act)
Luca Bonavita's avatar
Luca Bonavita committed
    ob.animation_data.action = None
    bpy.ops.object.mode_set(mode='OBJECT')
    return act

def prepareActionFCurve(ob, created, args, tokens):
Luca Bonavita's avatar
Luca Bonavita committed
    dataPath = args[0]
    index = args[1]
    (expr, channel) = channelFromDataPath(dataPath, index)
    try:
        if channel in created[expr]:
            return
        else:
            created[expr].append(channel)
    except:
        created[expr] = [channel]

    times = []
    for (key, val, sub) in tokens:
        if key == 'kp':
            times.append(int(val[0]))

    try:
Luca Bonavita's avatar
Luca Bonavita committed
    except:
        print("Ignoring illegal expression: %s" % expr)
        return

    n = 0
    for t in times:
        #bpy.context.scene.current_frame = t
        bpy.ops.anim.change_frame(frame = t)
        try:
            data.keyframe_insert(channel)
            n += 1
        except:
            pass
            #print("failed", data, expr, channel)
    if n != len(times):
        print("Mismatch", n, len(times), expr, channel)
    return
Brendon Murphy's avatar
Brendon Murphy committed

def channelFromDataPath(dataPath, index):
Luca Bonavita's avatar
Luca Bonavita committed
    words = dataPath.split(']')
    if len(words) == 1:
        # location
        expr = "ob"
        channel = dataPath
    elif len(words) == 2:
        # pose.bones["tongue"].location
        expr = "ob.%s]" % (words[0])
        cwords = words[1].split('.')
        channel = cwords[1]
    elif len(words) == 3:
        # pose.bones["brow.R"]["mad"]
        expr = "ob.%s]" % (words[0])
        cwords = words[1].split('"')
        channel = cwords[1]
    return (expr, channel)
Brendon Murphy's avatar
Brendon Murphy committed

def parseActionFCurve(act, ob, args, tokens):
Luca Bonavita's avatar
Luca Bonavita committed
    dataPath = args[0]
    index = args[1]
    (expr, channel) = channelFromDataPath(dataPath, index)
    index = int(args[1])

    success = False
    for fcu in act.fcurves:
        (expr1, channel1) = channelFromDataPath(fcu.data_path, fcu.array_index)
        if expr1 == expr and channel1 == channel and fcu.array_index == index:
            success = True
            break
    if not success:
        return None

    n = 0
    for (key, val, sub) in tokens:
        if key == 'kp':
            try:
                pt = fcu.keyframe_points[n]
                pt.interpolation = 'LINEAR'
                pt = parseKeyFramePoint(pt, val, sub)
                n += 1
            except:
                pass
                #print(tokens)
                #MyError("kp", fcu, n, len(fcu.keyframe_points), val)
Luca Bonavita's avatar
Luca Bonavita committed
        else:
            defaultKey(key, val, sub, fcu)
Luca Bonavita's avatar
Luca Bonavita committed
    return fcu
Brendon Murphy's avatar
Brendon Murphy committed

def parseKeyFramePoint(pt, args, tokens):
Luca Bonavita's avatar
Luca Bonavita committed
    pt.co = (float(args[0]), float(args[1]))
    if len(args) > 2:
        pt.handle1 = (float(args[2]), float(args[3]))
        pt.handle2 = (float(args[3]), float(args[5]))
    return pt
Luca Bonavita's avatar
Luca Bonavita committed
#    parseAnimationData(rna, args, tokens):
#    parseDriver(drv, args, tokens):
#    parseDriverVariable(var, args, tokens):
#

def parseAnimationData(rna, args, tokens):
    if not mhxEval(args[1]):
Luca Bonavita's avatar
Luca Bonavita committed
        return
Luca Bonavita's avatar
Luca Bonavita committed
        rna.animation_data_create()
    adata = rna.animation_data
    for (key, val, sub) in tokens:
        if key == 'FCurve':
            fcu = parseAnimDataFCurve(adata, rna, val, sub)
Luca Bonavita's avatar
Luca Bonavita committed
        else:
            defaultKey(key, val, sub, adata)
Luca Bonavita's avatar
Luca Bonavita committed
    return adata
Brendon Murphy's avatar
Brendon Murphy committed

def parseAnimDataFCurve(adata, rna, args, tokens):
Luca Bonavita's avatar
Luca Bonavita committed
    if invalid(args[2]):
        return
    dataPath = args[0]
    index = int(args[1])
    n = 1
    for (key, val, sub) in tokens:
        if key == 'Driver':
            fcu = parseDriver(adata, dataPath, index, rna, val, sub)
            fmod = fcu.modifiers[0]
            fcu.modifiers.remove(fmod)
        elif key == 'FModifier':
            parseFModifier(fcu, val, sub)
        elif key == 'kp':
            pt = fcu.keyframe_points.insert(n, 0)
Luca Bonavita's avatar
Luca Bonavita committed
            pt.interpolation = 'LINEAR'
            pt = parseKeyFramePoint(pt, val, sub)
            n += 1
        else:
            defaultKey(key, val, sub, fcu)
Luca Bonavita's avatar
Luca Bonavita committed
    return fcu
Brendon Murphy's avatar
Brendon Murphy committed

"""
Luca Bonavita's avatar
Luca Bonavita committed
        fcurve = con.driver_add("influence", 0)
        driver = fcurve.driver
        driver.type = 'AVERAGE'
Brendon Murphy's avatar
Brendon Murphy committed
"""
def parseDriver(adata, dataPath, index, rna, args, tokens):
Luca Bonavita's avatar
Luca Bonavita committed
    if dataPath[-1] == ']':
        words = dataPath.split(']')
        expr = "rna." + words[0] + ']'
        pwords = words[1].split('"')
        prop = pwords[1]
Luca Bonavita's avatar
Luca Bonavita committed
        return None
    else:
        words = dataPath.split('.')
        channel = words[-1]
        expr = "rna"
        for n in range(len(words)-1):
            expr += "." + words[n]
        expr += ".driver_add('%s', index)" % channel
    fcu = mhxEval(expr, locals())
Luca Bonavita's avatar
Luca Bonavita committed
    drv = fcu.driver
    drv.type = args[0]
    for (key, val, sub) in tokens:
        if key == 'DriverVariable':
            var = parseDriverVariable(drv, rna, val, sub)
        else:
            defaultKey(key, val, sub, drv)
Luca Bonavita's avatar
Luca Bonavita committed
    return fcu
Brendon Murphy's avatar
Brendon Murphy committed

def parseDriverVariable(drv, rna, args, tokens):
Luca Bonavita's avatar
Luca Bonavita committed
    var = drv.variables.new()
    var.name = args[0]
    var.type = args[1]
    nTarget = 0
    for (key, val, sub) in tokens:
        if key == 'Target':
            parseDriverTarget(var, nTarget, rna, val, sub)
            nTarget += 1
        else:
            defaultKey(key, val, sub, var)
Luca Bonavita's avatar
Luca Bonavita committed
    return var
Brendon Murphy's avatar
Brendon Murphy committed

def parseFModifier(fcu, args, tokens):
Luca Bonavita's avatar
Luca Bonavita committed
    fmod = fcu.modifiers.new(args[0])
    #fmod = fcu.modifiers[0]
    for (key, val, sub) in tokens:
        defaultKey(key, val, sub, fmod)
Luca Bonavita's avatar
Luca Bonavita committed
    return fmod
Brendon Murphy's avatar
Brendon Murphy committed

"""
Luca Bonavita's avatar
Luca Bonavita committed
        var = driver.variables.new()
        var.name = target_bone
        var.targets[0].id_type = 'OBJECT'
        var.targets[0].id = obj
        var.targets[0].rna_path = driver_path
Brendon Murphy's avatar
Brendon Murphy committed
"""
def parseDriverTarget(var, nTarget, rna, args, tokens):
Luca Bonavita's avatar
Luca Bonavita committed
    targ = var.targets[nTarget]
    name = args[0]
    #targ.id_type = args[1]
    dtype = args[1].capitalize()
    dtype = 'Object'
    targ.id = loadedData[dtype][name]
Luca Bonavita's avatar
Luca Bonavita committed
    for (key, val, sub) in tokens:
        if key == 'data_path':
            words = val[0].split('"')
            if len(words) > 1:
                targ.data_path = propNames(words[1])[1]
            else:
                targ.data_path = propNames(val)[1]
        else:
            defaultKey(key, val, sub, targ)
Luca Bonavita's avatar
Luca Bonavita committed
    return targ
Brendon Murphy's avatar
Brendon Murphy committed

Brendon Murphy's avatar
Brendon Murphy committed
#
Luca Bonavita's avatar
Luca Bonavita committed
#    parseMaterial(args, ext, tokens):
#    parseMTex(mat, args, tokens):
#    parseTexture(args, tokens):
Brendon Murphy's avatar
Brendon Murphy committed
#

def parseMaterial(args, tokens):
Luca Bonavita's avatar
Luca Bonavita committed
    name = args[0]
    mat = bpy.data.materials.new(name)
Luca Bonavita's avatar
Luca Bonavita committed
        return None
    loadedData['Material'][name] = mat
    for (key, val, sub) in tokens:
        if key == 'MTex':
            parseMTex(mat, val, sub)
        elif key == 'Ramp':
            parseRamp(mat, val, sub)
        elif key == 'RaytraceTransparency':
            parseDefault(mat.raytrace_transparency, sub, {}, [])
        elif key == 'Halo':
            parseDefault(mat.halo, sub, {}, [])
        elif key == 'SSS':
            parseDefault(mat.subsurface_scattering, sub, {}, [])
        elif key == 'Strand':
            parseDefault(mat.strand, sub, {}, [])
        elif key == 'NodeTree':
            mat.use_nodes = True
            parseNodeTree(mat.node_tree, val, sub)
        elif key == 'AnimationData':
            parseAnimationData(mat, val, sub)
Luca Bonavita's avatar
Luca Bonavita committed
        else:
            exclude = ['specular_intensity', 'tangent_shading']
            defaultKey(key, val, sub, mat)
Luca Bonavita's avatar
Luca Bonavita committed
    return mat
Brendon Murphy's avatar
Brendon Murphy committed

def parseMTex(mat, args, tokens):
Luca Bonavita's avatar
Luca Bonavita committed
    index = int(args[0])
    texname = args[1]
    texco = args[2]
    mapto = args[3]
    tex = loadedData['Texture'][texname]
    mtex = mat.texture_slots.add()
    mtex.texture_coords = texco
    mtex.texture = tex
Brendon Murphy's avatar
Brendon Murphy committed

Luca Bonavita's avatar
Luca Bonavita committed
    for (key, val, sub) in tokens:
        defaultKey(key, val, sub, mtex)
Brendon Murphy's avatar
Brendon Murphy committed

Luca Bonavita's avatar
Luca Bonavita committed
    return mtex
Brendon Murphy's avatar
Brendon Murphy committed

def parseTexture(args, tokens):
Luca Bonavita's avatar
Luca Bonavita committed
    if verbosity > 2:
        print( "Parsing texture %s" % args )
    name = args[0]
    tex = bpy.data.textures.new(name=name, type=args[1])
    loadedData['Texture'][name] = tex
Luca Bonavita's avatar
Luca Bonavita committed
    for (key, val, sub) in tokens:
        if key == 'Image':
            try:
                imgName = val[0]
                img = loadedData['Image'][imgName]
                tex.image = img
            except:
                msg = "Unable to load image '%s'" % val[0]
        elif key == 'Ramp':
            parseRamp(tex, val, sub)
        elif key == 'NodeTree':
            tex.use_nodes = True
            parseNodeTree(tex.node_tree, val, sub)
        else:
            defaultKey(key, val, sub, tex, ['use_nodes', 'use_textures', 'contrast', 'use_alpha'])
Luca Bonavita's avatar
Luca Bonavita committed

    return tex
Brendon Murphy's avatar
Brendon Murphy committed

def parseRamp(data, args, tokens):
    setattr(data, "use_%s" % args[0], True)
    ramp = getattr(data, args[0])
Luca Bonavita's avatar
Luca Bonavita committed
    elts = ramp.elements
    n = 0
    for (key, val, sub) in tokens:
        if key == 'Element':
            elts[n].color = mhxEval(val[0], locals())
            elts[n].position = mhxEval(val[1], locals())
Luca Bonavita's avatar
Luca Bonavita committed
            n += 1
        else:
            defaultKey(key, val, sub, tex, ['use_nodes', 'use_textures', 'contrast'])
Brendon Murphy's avatar
Brendon Murphy committed
def parseSSS(mat, args, tokens):
Luca Bonavita's avatar
Luca Bonavita committed
    sss = mat.subsurface_scattering
    for (key, val, sub) in tokens:
        defaultKey(key, val, sub, sss)
Brendon Murphy's avatar
Brendon Murphy committed

def parseStrand(mat, args, tokens):
Luca Bonavita's avatar
Luca Bonavita committed
    strand = mat.strand
    for (key, val, sub) in tokens:
        defaultKey(key, val, sub, strand)
Luca Bonavita's avatar
Luca Bonavita committed
#    parseNodeTree(tree, args, tokens):
#    parseNode(node, args, tokens):
#    parseSocket(socket, args, tokens):
#

def parseNodeTree(tree, args, tokens):
Luca Bonavita's avatar
Luca Bonavita committed
    return
    print("Tree", tree, args)
    print(list(tree.nodes))
    tree.name = args[0]
    for (key, val, sub) in tokens:
        if key == 'Node':
            parseNodes(tree.nodes, val, sub)
        else:
            defaultKey(key, val, sub, tree)

def parseNodes(nodes, args, tokens):
Luca Bonavita's avatar
Luca Bonavita committed
    print("Nodes", nodes, args)
    print(list(nodes))
    node.name = args[0]
    for (key, val, sub) in tokens:
        if key == 'Inputs':
            parseSocket(node.inputs, val, sub)
        elif key == 'Outputs':
            parseSocket(node.outputs, val, sub)
        else:
            defaultKey(key, val, sub, node)

def parseNode(node, args, tokens):
Luca Bonavita's avatar
Luca Bonavita committed
    print("Node", node, args)
    print(list(node.inputs), list(node.outputs))
    node.name = args[0]
    for (key, val, sub) in tokens:
        if key == 'Inputs':
            parseSocket(node.inputs, val, sub)
        elif key == 'Outputs':
            parseSocket(node.outputs, val, sub)
        else:
            defaultKey(key, val, sub, node)

def parseSocket(socket, args, tokens):
Luca Bonavita's avatar
Luca Bonavita committed
    print("Socket", socket, args)
    socket.name = args[0]
    for (key, val, sub) in tokens:
        if key == 'Node':
            parseNode(tree.nodes, val, sub)
        else:
            defaultKey(key, val, sub, tree)
Luca Bonavita's avatar
Luca Bonavita committed
#    loadImage(filepath):
#    parseImage(args, tokens):
def loadImage(relFilepath):
    filepath = os.path.normpath(os.path.join(theDir, relFilepath))
    print( "Loading %s" % filepath.encode('utf-8','strict'))
    if os.path.isfile(filepath):
        #print( "Found file %s." % filepath.encode('utf-8','strict') )
Luca Bonavita's avatar
Luca Bonavita committed
        try:
            img = bpy.data.images.load(filepath)
Luca Bonavita's avatar
Luca Bonavita committed
            return img
        except:
            print( "Cannot read image" )
            return None
    else:
        print( "No such file: %s" % filepath.encode('utf-8','strict') )
Luca Bonavita's avatar
Luca Bonavita committed
        return None
Brendon Murphy's avatar
Brendon Murphy committed

Brendon Murphy's avatar
Brendon Murphy committed
def parseImage(args, tokens):
Luca Bonavita's avatar
Luca Bonavita committed
    imgName = args[0]
    img = None
    for (key, val, sub) in tokens:
        if key == 'Filename':
            filename = val[0]
            for n in range(1,len(val)):
                filename += " " + val[n]
            img = loadImage(filename)
Luca Bonavita's avatar
Luca Bonavita committed
                return None
            img.name = imgName
        else:
            defaultKey(key, val, sub, img, ['depth', 'dirty', 'has_data', 'size', 'type', 'use_premultiply'])
Luca Bonavita's avatar
Luca Bonavita committed
    print ("Image %s" % img )
    loadedData['Image'][imgName] = img
    return img

#
#    parseObject(args, tokens):
#    createObject(type, name, data, datName):
#    setObjectAndData(args, typ):
#
Brendon Murphy's avatar
Brendon Murphy committed
def parseObject(args, tokens):
Luca Bonavita's avatar
Luca Bonavita committed
    if verbosity > 2:
        print( "Parsing object %s" % args )
    name = args[0]
    typ = args[1]
    datName = args[2]

    if typ == 'EMPTY':
        ob = bpy.data.objects.new(name, None)
        loadedData['Object'][name] = ob
        linkObject(ob, None)
    else:
        try:
            data = loadedData[typ.capitalize()][datName]
Luca Bonavita's avatar
Luca Bonavita committed
        except:
            MyError("Failed to find data: %s %s %s" % (name, typ, datName))
Luca Bonavita's avatar
Luca Bonavita committed
            return

    try:
        ob = loadedData['Object'][name]
        bpy.context.scene.objects.active = ob
        #print("Found data", ob)
    except:
        ob = None

Luca Bonavita's avatar
Luca Bonavita committed
        ob = createObject(typ, name, data, datName)
        linkObject(ob, data)