Skip to content
Snippets Groups Projects
io_import_scene_mhx.py 103 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-2011
# Coding Standards:    See http://sites.google.com/site/makehumandocs/developers-guide
Brendon Murphy's avatar
Brendon Murphy committed

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

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',
    "blender": (2, 5, 9),
    "api": 40335,
    '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 = 9
SUB_VERSION = 0
BLENDER_VERSION = (2, 59, 2)
Brendon Murphy's avatar
Brendon Murphy committed
#
#
#

import bpy
import os
import time
import mathutils
from bpy.props import *
Brendon Murphy's avatar
Brendon Murphy committed

MHX249 = False
Blender24 = False
Blender25 = True
TexDir = "~/makehuman/exports"

#
#
#

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
T_Stretch = 0x04

T_Diamond = 0x10
Brendon Murphy's avatar
Brendon Murphy committed
T_Replace = 0x20
T_Face = 0x40
T_Shape = 0x80
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

toggle = (T_EnforceVersion + T_Replace + T_Mesh + T_Armature + 
        T_Face + T_Shape + T_Proxy + T_Clothes + T_Rigify)
Brendon Murphy's avatar
Brendon Murphy committed

#
#    Blender versions
#

BLENDER_GRAPHICALL = 0
BLENDER_256a = 1

BlenderVersions = ['Graphicall', 'Blender256a']
theBlenderVersion = BLENDER_GRAPHICALL

Brendon Murphy's avatar
Brendon Murphy committed
#
Luca Bonavita's avatar
Luca Bonavita committed
#    setFlagsAndFloats(rigFlags):
Brendon Murphy's avatar
Brendon Murphy committed
#
Luca Bonavita's avatar
Luca Bonavita committed
#    Global floats
#fFingerPanel = 0.0
#fFingerIK = 0.0
fNoStretch = 0.0
Brendon Murphy's avatar
Brendon Murphy committed

Luca Bonavita's avatar
Luca Bonavita committed
#    rigLeg and rigArm flags
Brendon Murphy's avatar
Brendon Murphy committed
T_Toes = 0x0001
Brendon Murphy's avatar
Brendon Murphy committed

#T_InvFoot = 0x0010
#T_InvFootPT = 0x0020
#T_InvFootNoPT = 0x0040
Brendon Murphy's avatar
Brendon Murphy committed

#T_FingerPanel = 0x100
#T_FingerRot = 0x0200
#T_FingerIK = 0x0400
Brendon Murphy's avatar
Brendon Murphy committed

Brendon Murphy's avatar
Brendon Murphy committed

Luca Bonavita's avatar
Luca Bonavita committed
    '''
    global toggle, rigLeg, rigArm
Brendon Murphy's avatar
Brendon Murphy committed

Luca Bonavita's avatar
Luca Bonavita committed
    (footRig, fingerRig) = rigFlags
    rigLeg = 0
    if footRig == 'Reverse foot': 
        rigLeg |= T_InvFoot
        if toggle & T_PoleTar:
            rigLeg |= T_InvFootPT
        else:
            rigLeg |= T_InvFootNoPT
    elif footRig == 'Gobo': rigLeg |= T_GoboFoot        
Brendon Murphy's avatar
Brendon Murphy committed

Luca Bonavita's avatar
Luca Bonavita committed
    rigArm = 0
    if fingerRig == 'Panel': rigArm |= T_FingerPanel
    elif fingerRig == 'Rotation': rigArm |= T_FingerRot
    elif fingerRig == 'IK': rigArm |= T_FingerIK
Brendon Murphy's avatar
Brendon Murphy committed

Luca Bonavita's avatar
Luca Bonavita committed
    toggle |= T_Panel
    '''
    global fNoStretch
    if toggle&T_Stretch: fNoStretch == 0.0
    else: fNoStretch = 1.0
Brendon Murphy's avatar
Brendon Murphy committed

Luca Bonavita's avatar
Luca Bonavita committed
    return
Luca Bonavita's avatar
Luca Bonavita committed
#    Dictionaries
Brendon Murphy's avatar
Brendon Murphy committed
#

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' : {},
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',
Luca Bonavita's avatar
Luca Bonavita committed
#    checkBlenderVersion()
Luca Bonavita's avatar
Luca Bonavita committed
    print("Found Blender", bpy.app.version)
    (A, B, C) = bpy.app.version
    (a, b, c) = BLENDER_VERSION
    if a <= A: return
    if b <= B: return
    if c <= C: return
    msg = (
"This version of the MHX importer only works with \n" +
"Blender (%d, %d, %d) or later.\n" % (a, b, c) +
"Download a more recent Blender from \n" +
"www.blender.org or www.graphicall.org.\n"
Luca Bonavita's avatar
Luca Bonavita committed
    )
    MyError(msg)
Luca Bonavita's avatar
Luca Bonavita committed
    return
Luca Bonavita's avatar
Luca Bonavita committed
    global todo, nErrors, theScale, defaultScale, One, toggle

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

    fileName = os.path.expanduser(filePath)
    (shortName, ext) = os.path.splitext(fileName)
    if ext.lower() != ".mhx":
        print("Error: Not a mhx file: " + fileName)
        return
    print( "Opening MHX file "+ fileName )
    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

    setFlagsAndFloats()

    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" % lineNo )
                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 out of kilter %d" % level)    
Luca Bonavita's avatar
Luca Bonavita committed
    clearScene()
    print( "Parsing" )
    parse(tokens)
    
    for (expr, glbals, lcals) in todo:
        try:
            print("Doing %s" % expr)
            exec(expr, glbals, lcals)
        except:
            msg = "Failed: \n"+expr
Luca Bonavita's avatar
Luca Bonavita committed
            print( msg )
            nErrors += 1
            #MyError(msg)
Luca Bonavita's avatar
Luca Bonavita committed

    time2 = time.clock()
    print("toggle = %x" % toggle)
    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((major,minor), (MAJOR_VERSION, MINOR_VERSION), warnedVersion)
    if  major != MAJOR_VERSION or minor != MINOR_VERSION:
Luca Bonavita's avatar
Luca Bonavita committed
        if warnedVersion:
            return
        else:
            msg = (
"Expected MHX %d.%d but the loaded file \n" % (MAJOR_VERSION, MINOR_VERSION) +
"has version MHX %d.%d\n" % (major, minor) +
"You can disable this error message by deselecting the \n" +
"Enforce version option when importing. \n" +
"Alternatively, you can try to download the most recent \n" +
"Blender build from www.graphicall.org. \n" +
"The most up-to-date version of the import script is distributed\n" +
"with Blender, but 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"
Luca Bonavita's avatar
Luca Bonavita committed
        if toggle & T_EnforceVersion:
            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":
            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])]
                print("matanim", ob, mat)
                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)                

        if data and key != 'Mesh':
            print( data )
    return

#
#    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)
Luca Bonavita's avatar
Luca Bonavita committed
    data = eval(expr)
Brendon Murphy's avatar
Brendon Murphy committed

Luca Bonavita's avatar
Luca Bonavita committed
    bpyType = typ.capitalize()
    print(bpyType, name, data)
    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:
        #print("%s %s" % (key, val))
        defaultKey(key, val, sub, 'data', [], globals(), locals())
    print("Done ", data)
    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]
    # print(expr, channel, index)
    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
    print("Parse Animation data")
    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())
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':
            if theBlenderVersion >= BLENDER_256a:
                pt = fcu.keyframe_points.add(n, 0)
            else:
                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]
Luca Bonavita's avatar
Luca Bonavita committed
        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
    
    #print("expr", rna, expr)
    fcu = eval(expr)
    drv = fcu.driver
    #print("   Driver type", drv, args[0])
Luca Bonavita's avatar
Luca Bonavita committed
    drv.type = args[0]
Luca Bonavita's avatar
Luca Bonavita committed
    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]
    #print("   Var type", var, args[1])
Luca Bonavita's avatar
Luca Bonavita committed
    var.type = args[1]
Luca Bonavita's avatar
Luca Bonavita committed
    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:
        defaultKey(key, val, sub, 'targ', [], globals(), locals())
    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'], globals(), locals())

    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
#    doLoadImage(filepath):
#    loadImage(filepath):
#    parseImage(args, tokens):
Luca Bonavita's avatar
Luca Bonavita committed
def doLoadImage(filepath):        
    path1 = os.path.expanduser(filepath)
    file1 = os.path.realpath(path1)
    if os.path.isfile(file1):
        print( "Found file "+file1 )
        try:
            img = bpy.data.images.load(file1)
            return img
        except:
            print( "Cannot read image" )
            return None
    else:
        print( "No file "+file1 )
        return None
Brendon Murphy's avatar
Brendon Murphy committed


def loadImage(filepath):
Luca Bonavita's avatar
Luca Bonavita committed
    global TexDir, warnedTextureDir, loadedData

    texDir = os.path.expanduser(TexDir)