Skip to content
Snippets Groups Projects
io_import_scene_mhx.py 133 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',
    'version': (1, 15, 4),
    "blender": (2, 67, 0),
    '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://sites.google.com/site/makehumandocs/blender-export-and-mhx',
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'}
MINOR_VERSION = 15
Brendon Murphy's avatar
Brendon Murphy committed
#
#
#

import bpy
import os
import time
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 = ""
Brendon Murphy's avatar
Brendon Murphy committed

todo = []

#
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
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' : {},
    
    'Bone' : {},
    'BoneGroup' : {},
    'Rigify' : {},

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

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

    'ObjectConstraints' : {},
    'ObjectModifiers' : {},
    'MaterialSlot' : {},
    }
    return
    
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 todo, 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
    for line in file: 
        # 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 = eval(lineSplit[1])
                    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)
    
    for (expr, glbals, lcals) in todo:
        try:
            print("Doing %s" % expr)
            exec(expr, glbals, lcals)
        except:
            msg = "Failed: %s\n" % expr
Luca Bonavita's avatar
Luca Bonavita committed
            print( msg )
            nErrors += 1
            #MyError(msg)
Luca Bonavita's avatar
Luca Bonavita committed

    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, glbals, lcals):
Brendon Murphy's avatar
Brendon Murphy committed
#

def getObject(name, var, glbals, lcals):
Luca Bonavita's avatar
Luca Bonavita committed
    try:
        ob = loadedData['Object'][name]
    except:
        if name != "None":
            pushOnTodoList(None, "ob = loadedData['Object'][name]" % globals(), locals())
        ob = None
    return 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
    
    for (key, val, sub) in tokens: 
Luca Bonavita's avatar
Luca Bonavita committed
        data = None
        if key == 'MHX':
            checkMhxVersion(int(val[0]), int(val[1]))
        elif key == 'MHX249':
            MHX249 = eval(val[0])
            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)
            MyError(msg)    
Luca Bonavita's avatar
Luca Bonavita committed
        elif key == 'NoScale':
            if eval(val[0]):
                theScale = 1.0
            else:
                theScale = defaultScale        
            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
                parseShapeKeys(ob, ob.data, val, sub)        
Luca Bonavita's avatar
Luca Bonavita committed
        else:
            data = parseDefaultType(key, val, sub)                

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

def parseDefaultType(typ, args, tokens):
Luca Bonavita's avatar
Luca Bonavita committed
    global todo
Brendon Murphy's avatar
Brendon Murphy committed

Luca Bonavita's avatar
Luca Bonavita committed
    name = args[0]
    data = None
    expr = "bpy.data.%s.new('%s')" % (Plural[typ], name)
    data = eval(expr)
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', [], globals(), locals())
    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)
        
    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)
    
    for (key, val, sub) in tokens:
        if key == 'FCurve':
            fcu = parseActionFCurve(act, ob, val, sub)
        else:
            defaultKey(key, val, sub, 'act', [], globals(), locals())
    ob.animation_data.action = None
    bpy.ops.object.mode_set(mode='OBJECT')
    return act

def prepareActionFCurve(ob, created, args, tokens):            
    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:
        data = eval(expr)
    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', [], globals(), locals())
    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):
Luca Bonavita's avatar
Luca Bonavita committed
    if not eval(args[1]):
        return
    if rna.animation_data is None:    
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', [], globals(), locals())
    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', [], globals(), locals())
    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]
        bone = eval(expr)
        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 = eval(expr)
    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', [], globals(), locals())
    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', [], globals(), locals())
    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', [], globals(), locals())
    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', [], globals(), locals())
Luca Bonavita's avatar
Luca Bonavita committed
    return targ
Brendon Murphy's avatar
Brendon Murphy committed

Luca Bonavita's avatar
Luca Bonavita 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
    global todo
    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', [], globals(), locals())
    
    return mat
Brendon Murphy's avatar
Brendon Murphy committed

def parseMTex(mat, args, tokens):
Luca Bonavita's avatar
Luca Bonavita committed
    global todo
    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", [], globals(), locals())
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
    global todo
    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
    
    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'], globals(), locals())
Luca Bonavita's avatar
Luca Bonavita committed

    return tex
Brendon Murphy's avatar
Brendon Murphy committed

def parseRamp(data, args, tokens):
Luca Bonavita's avatar
Luca Bonavita committed
    nvar = "data.%s" % args[0]
    use = "data.use_%s = True" % args[0]
    exec(use)
    ramp = eval(nvar)
    elts = ramp.elements
    n = 0
    for (key, val, sub) in tokens:
        # print("Ramp", key, val)
        if key == 'Element':
            elts[n].color = eval(val[0])
            elts[n].position = eval(val[1])
            n += 1
        else:
            defaultKey(key, val,  sub, "tex", ['use_nodes', 'use_textures', 'contrast'], globals(), locals())
    
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", [], globals(), locals())
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", [], globals(), locals())
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", [], globals(), locals())

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", [], globals(), locals())

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", [], globals(), locals())

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", [], globals(), locals())
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

Luca Bonavita's avatar
Luca Bonavita committed
    
Brendon Murphy's avatar
Brendon Murphy committed
def parseImage(args, tokens):
Luca Bonavita's avatar
Luca Bonavita committed
    global todo
    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'], globals(), locals())
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]    
        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)

    for (key, val, sub) in tokens:
        if key == 'Modifier':
            parseModifier(ob, val, sub)
        elif key == 'Constraint':
            parseConstraint(ob.constraints, None, val, sub)
Luca Bonavita's avatar
Luca Bonavita committed
        elif key == 'AnimationData':
            parseAnimationData(ob, val, sub)
        elif key == 'ParticleSystem':
            parseParticleSystem(ob, val, sub)
        elif key == 'FieldSettings':
            parseDefault(ob.field, sub, {}, [])
        else:
            defaultKey(key, val, sub, "ob", ['type', 'data'], globals(), locals())
Luca Bonavita's avatar
Luca Bonavita committed
    if bpy.context.object == ob:
        if ob.type == 'MESH':
            bpy.ops.object.shade_smooth()
Luca Bonavita's avatar
Luca Bonavita committed
    else:
        print("Context", ob, bpy.context.object, bpy.context.scene.objects.active)
    return
Brendon Murphy's avatar
Brendon Murphy committed

def createObject(typ, name, data, datName):