# ##### 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 ##### # 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 """ Abstract MHX (MakeHuman eXchange format) importer for Blender 2.5x. Version 1.14.0 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. Alternatively, run the script in the script editor (Alt-P), and access from the File > Import menu """ bl_info = { 'name': 'Import: MakeHuman (.mhx)', 'author': 'Thomas Larsson', 'version': (1, 14, 1), "blender": (2, 6, 4), 'location': "File > Import > MakeHuman (.mhx)", 'description': 'Import files in the MakeHuman eXchange format (.mhx)', 'warning': '', 'wiki_url': 'http://sites.google.com/site/makehumandocs/blender-export-and-mhx', 'tracker_url': 'https://projects.blender.org/tracker/index.php?'\ 'func=detail&aid=21872', 'category': 'Import-Export'} MAJOR_VERSION = 1 MINOR_VERSION = 14 FROM_VERSION = 13 SUB_VERSION = 1 # # # import bpy import os import time import math import mathutils from mathutils import Vector, Matrix from bpy.props import * MHX249 = False Blender24 = False Blender25 = True TexDir = "~/makehuman/exports" # # # theScale = 1.0 One = 1.0/theScale useMesh = 1 verbosity = 2 warnedTextureDir = False warnedVersion = False true = True false = False Epsilon = 1e-6 nErrors = 0 theTempDatum = None theMessage = "" theMhxFile = "" todo = [] # # toggle flags # T_EnforceVersion = 0x01 T_Clothes = 0x02 T_HardParents = 0x0 T_CrashSafe = 0x0 T_Diamond = 0x10 T_Replace = 0x20 T_Shapekeys = 0x40 T_ShapeDrivers = 0x80 T_Face = T_Shapekeys T_Shape = T_Shapekeys T_Mesh = 0x100 T_Armature = 0x200 T_Proxy = 0x400 T_Cage = 0x800 T_Rigify = 0x1000 T_Opcns = 0x2000 T_Symm = 0x4000 DefaultToggle = ( T_EnforceVersion + T_Mesh + T_Armature + T_Shapekeys + T_ShapeDrivers + T_Proxy + T_Clothes + T_Rigify ) toggle = DefaultToggle toggleSettings = toggle # # Dictionaries # def initLoadedData(): global loadedData loadedData = { '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 Plural = { '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', } # # readMhxFile(filePath): # def readMhxFile(filePath): global todo, nErrors, theScale, theArmature, defaultScale, One global toggle, warnedVersion, theMessage, alpha7 defaultScale = theScale One = 1.0/theScale theArmature = None alpha7 = False warnedVersion = False initLoadedData() theMessage = "" 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() # ignore = False # UNUSED 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" % lineNo ) print( line ) stack.pop() 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) scn = 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 print( msg ) nErrors += 1 #MyError(msg) scn.objects.active = theArmature theArmature.MhAlpha8 = not alpha7 #bpy.ops.wm.properties_edit(data_path="object", property="MhxRig", value=theArmature["MhxRig"]) 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): # def getObject(name, var, glbals, lcals): try: ob = loadedData['Object'][name] except: if name != "None": pushOnTodoList(None, "ob = loadedData['Object'][name]" % globals(), locals()) ob = None return ob # # checkMhxVersion(major, minor): # def checkMhxVersion(major, minor): global warnedVersion print("MHX", (major,minor), (MAJOR_VERSION, MINOR_VERSION), warnedVersion) if major != MAJOR_VERSION or minor < FROM_VERSION: if warnedVersion: return else: msg = ( "Wrong MHX version\n" + "Expected MHX %d.%02d but the loaded file " % (MAJOR_VERSION, MINOR_VERSION) + "has version MHX %d.%02d\n" % (major, minor)) if minor < FROM_VERSION: msg += ( "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) else: print(msg) warnedVersion = True return # # parse(tokens): # ifResult = False def parse(tokens): global MHX249, ifResult, theScale, defaultScale, One for (key, val, sub) in tokens: print("Parse %s" % key) 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) 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": reinitGlobalData() 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) 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: rigifyMhx(bpy.context, val[0]) 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]) if ob: bpy.context.scene.objects.active = ob parseShapeKeys(ob, ob.data, val, sub) else: data = parseDefaultType(key, val, sub) if data and key != 'Mesh': print( data ) return # # parseDefaultType(typ, args, tokens): # def parseDefaultType(typ, args, tokens): global todo name = args[0] data = None expr = "bpy.data.%s.new('%s')" % (Plural[typ], name) # print(expr) data = eval(expr) # print(" ok", data) bpyType = typ.capitalize() print(bpyType, name, data) loadedData[bpyType][name] = data if data is None: return None for (key, val, sub) in tokens: #print("%s %s" % (key, val)) defaultKey(key, val, sub, 'data', [], globals(), locals()) print("Done ", data) return data # # concatList(elts) # def concatList(elts): string = "" for elt in elts: string += " %s" % elt return string # # parseAction(args, tokens): # parseFCurve(fcu, args, tokens): # parseKeyFramePoint(pt, args, tokens): # def parseAction(args, tokens): 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 if act is None: 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 def channelFromDataPath(dataPath, index): 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) def parseActionFCurve(act, ob, args, tokens): 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) else: defaultKey(key, val, sub, 'fcu', [], globals(), locals()) return fcu def parseKeyFramePoint(pt, args, tokens): 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 # # parseAnimationData(rna, args, tokens): # parseDriver(drv, args, tokens): # parseDriverVariable(var, args, tokens): # def parseAnimationData(rna, args, tokens): if not eval(args[1]): return print("Parse Animation data") if rna.animation_data is None: rna.animation_data_create() adata = rna.animation_data for (key, val, sub) in tokens: if key == 'FCurve': fcu = parseAnimDataFCurve(adata, rna, val, sub) else: defaultKey(key, val, sub, 'adata', [], globals(), locals()) print(adata) return adata def parseAnimDataFCurve(adata, rna, args, tokens): 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) pt.interpolation = 'LINEAR' pt = parseKeyFramePoint(pt, val, sub) n += 1 else: defaultKey(key, val, sub, 'fcu', [], globals(), locals()) return fcu """ fcurve = con.driver_add("influence", 0) driver = fcurve.driver driver.type = 'AVERAGE' """ def parseDriver(adata, dataPath, index, rna, args, tokens): if dataPath[-1] == ']': words = dataPath.split(']') expr = "rna." + words[0] + ']' pwords = words[1].split('"') prop = pwords[1] #print("prop", expr, prop) 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]) drv.type = args[0] #print(" ->", drv.type) 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 def parseDriverVariable(drv, rna, args, tokens): var = drv.variables.new() var.name = args[0] #print(" Var type", var, args[1]) var.type = args[1] #print(" ->", var.type) 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 def parseFModifier(fcu, args, tokens): 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 """ 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 """ def parseDriverTarget(var, nTarget, rna, args, tokens): targ = var.targets[nTarget] name = args[0] #targ.id_type = args[1] dtype = args[1].capitalize() dtype = 'Object' targ.id = loadedData[dtype][name] #print(" ->", targ.id) 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()) return targ # # parseMaterial(args, ext, tokens): # parseMTex(mat, args, tokens): # parseTexture(args, tokens): # def parseMaterial(args, tokens): global todo name = args[0] mat = bpy.data.materials.new(name) if mat is None: 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) else: exclude = ['specular_intensity', 'tangent_shading'] defaultKey(key, val, sub, 'mat', [], globals(), locals()) return mat def parseMTex(mat, args, tokens): 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 for (key, val, sub) in tokens: defaultKey(key, val, sub, "mtex", [], globals(), locals()) return mtex def parseTexture(args, tokens): 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 def parseRamp(data, args, tokens): 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()) def parseSSS(mat, args, tokens): sss = mat.subsurface_scattering for (key, val, sub) in tokens: defaultKey(key, val, sub, "sss", [], globals(), locals()) def parseStrand(mat, args, tokens): strand = mat.strand for (key, val, sub) in tokens: defaultKey(key, val, sub, "strand", [], globals(), locals()) # # parseNodeTree(tree, args, tokens): # parseNode(node, args, tokens): # parseSocket(socket, args, tokens): # def parseNodeTree(tree, args, tokens): 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): 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): 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): 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()) # # doLoadImage(filepath): # loadImage(filepath): # parseImage(args, tokens): # 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 def loadImage(filepath): global TexDir, warnedTextureDir, loadedData texDir = os.path.expanduser(TexDir) path1 = os.path.expanduser(filepath) file1 = os.path.realpath(path1) (path, filename) = os.path.split(file1) (name, ext) = os.path.splitext(filename) print( "Loading ", filepath, " = ", filename ) # img = doLoadImage(texDir+"/"+name+".png") # if img: # return img img = doLoadImage(texDir+"/"+filename) if img: return img # img = doLoadImage(path+"/"+name+".png") # if img: # return img img = doLoadImage(path+"/"+filename) if img: return img if warnedTextureDir: return None warnedTextureDir = True return None TexDir = Draw.PupStrInput("TexDir? ", path, 100) texDir = os.path.expanduser(TexDir) img = doLoadImage(texDir+"/"+name+".png") if img: return img img = doLoadImage(TexDir+"/"+filename) return img def parseImage(args, tokens): 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) if img is None: return None img.name = imgName else: defaultKey(key, val, sub, "img", ['depth', 'dirty', 'has_data', 'size', 'type'], globals(), locals()) print ("Image %s" % img ) loadedData['Image'][imgName] = img return img # # parseObject(args, tokens): # createObject(type, name, data, datName): # setObjectAndData(args, typ): # def parseObject(args, tokens): 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)) return try: ob = loadedData['Object'][name] bpy.context.scene.objects.active = ob #print("Found data", ob) except: ob = None if ob is None: print("Create", name, data, datName) ob = createObject(typ, name, data, datName) print("created", ob) 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) 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()) if bpy.context.object == ob: if ob.type == 'MESH': print("Smooth shade", ob) bpy.ops.object.shade_smooth() else: print("Context", ob, bpy.context.object, bpy.context.scene.objects.active) return def createObject(typ, name, data, datName): # print( "Creating object %s %s %s" % (typ, name, data) ) ob = bpy.data.objects.new(name, data) if data: loadedData[typ.capitalize()][datName] = data loadedData['Object'][name] = ob return ob def linkObject(ob, data): #print("Data", data, ob.data) if data and ob.data is None: ob.data = data print("Data linked", ob, ob.data) scn = bpy.context.scene scn.objects.link(ob) scn.objects.active = ob #print("Linked object", ob) #print("Scene", scn) #print("Active", scn.objects.active) #print("Context", bpy.context.object) return ob def setObjectAndData(args, typ): datName = args[0] obName = args[1] #bpy.ops.object.add(type=typ) ob = bpy.context.object ob.name = obName ob.data.name = datName loadedData[typ][datName] = ob.data loadedData['Object'][obName] = ob return ob.data # # parseModifier(ob, args, tokens): # def parseModifier(ob, args, tokens): name = args[0] typ = args[1] if typ == 'PARTICLE_SYSTEM': return None mod = ob.modifiers.new(name, typ) for (key, val, sub) in tokens: if key == 'HookAssignNth': if val[0] == 'CURVE': hookAssignNth(mod, int(val[1]), True, ob.data.splines[0].points) elif val[0] == 'LATTICE': hookAssignNth(mod, int(val[1]), False, ob.data.points) elif val[0] == 'MESH': hookAssignNth(mod, int(val[1]), True, ob.data.vertices) else: MyError("Unknown hook %s" % val) else: defaultKey(key, val, sub, 'mod', [], globals(), locals()) return mod def hookAssignNth(mod, n, select, points): if select: for pt in points: pt.select = False points[n].select = True sel = [] for pt in points: sel.append(pt.select) #print(mod, sel, n, points) bpy.ops.object.mode_set(mode='EDIT') bpy.ops.object.hook_reset(modifier=mod.name) bpy.ops.object.hook_select(modifier=mod.name) bpy.ops.object.hook_assign(modifier=mod.name) bpy.ops.object.mode_set(mode='OBJECT') return # # parseParticleSystem(ob, args, tokens): # parseParticles(particles, args, tokens): # parseParticle(par, args, tokens): # def parseParticleSystem(ob, args, tokens): print(ob, bpy.context.object) pss = ob.particle_systems print(pss, pss.values()) name = args[0] typ = args[1] #psys = pss.new(name, typ) bpy.ops.object.particle_system_add() print(pss, pss.values()) psys = pss[-1] psys.name = name psys.settings.type = typ loadedData['ParticleSystem'][name] = psys print("Psys", psys) for (key, val, sub) in tokens: if key == 'Particles': parseParticles(psys, val, sub) else: defaultKey(key, val, sub, 'psys', [], globals(), locals()) return psys def parseParticles(psys, args, tokens): particles = psys.particles bpy.ops.particle.particle_edit_toggle() n = 0 for (key, val, sub) in tokens: if key == 'Particle': parseParticle(particles[n], val, sub) n += 1 else: for par in particles: defaultKey(key, val, sub, 'par', [], globals(), locals()) bpy.ops.particle.particle_edit_toggle() return particles def parseParticle(par, args, tokens): n = 0 for (key, val, sub) in tokens: if key == 'h': h = par.hair[n] h.location = eval(val[0]) h.time = int(val[1]) h.weight = float(val[2]) n += 1 elif key == 'location': par.location = eval(val[0]) return # # unpackList(list_of_tuples): # def unpackList(list_of_tuples): l = [] for t in list_of_tuples: l.extend(t) return l # # parseMesh (args, tokens): # def parseMesh (args, tokens): global todo, BMeshAware if verbosity > 2: print( "Parsing mesh %s" % args ) mename = args[0] obname = args[1] me = bpy.data.meshes.new(mename) ob = createObject('MESH', obname, me, mename) verts = [] edges = [] faces = [] vertsTex = [] texFaces = [] for (key, val, sub) in tokens: if key == 'Verts': verts = parseVerts(sub) elif key == 'Edges': edges = parseEdges(sub) elif key == 'Faces': faces = parseFaces(sub) if faces: me.from_pydata(verts, [], faces) else: me.from_pydata(verts, edges, []) me.update() linkObject(ob, me) if faces: try: me.polygons BMeshAware = True print("Using BMesh") except: BMeshAware = False print("Not using BMesh") mats = [] nuvlayers = 0 for (key, val, sub) in tokens: if key == 'Verts' or key == 'Edges' or key == 'Faces': pass elif key == 'MeshTextureFaceLayer': if BMeshAware: parseUvTextureBMesh(val, sub, me) else: parseUvTextureNoBMesh(val, sub, me) elif key == 'MeshColorLayer': parseVertColorLayer(val, sub, me) elif key == 'VertexGroup': parseVertexGroup(ob, me, val, sub) elif key == 'ShapeKeys': parseShapeKeys(ob, me, val, sub) elif key == 'Material': try: mat = loadedData['Material'][val[0]] except: mat = None if mat: me.materials.append(mat) else: defaultKey(key, val, sub, "me", [], globals(), locals()) for (key, val, sub) in tokens: if key == 'Faces': if BMeshAware: parseFaces2BMesh(sub, me) else: parseFaces2NoBMesh(sub, me) print(me) return me # # parseVerts(tokens): # parseEdges(tokens): # parseFaces(tokens): # parseFaces2(tokens, me): # def parseVerts(tokens): verts = [] for (key, val, sub) in tokens: if key == 'v': verts.append( (theScale*float(val[0]), theScale*float(val[1]), theScale*float(val[2])) ) return verts def parseEdges(tokens): edges = [] for (key, val, sub) in tokens: if key == 'e': edges.append((int(val[0]), int(val[1]))) return edges def parseFaces(tokens): faces = [] for (key, val, sub) in tokens: if key == 'f': if len(val) == 3: face = [int(val[0]), int(val[1]), int(val[2])] elif len(val) == 4: face = [int(val[0]), int(val[1]), int(val[2]), int(val[3])] faces.append(face) return faces def parseFaces2BMesh(tokens, me): n = 0 for (key, val, sub) in tokens: if key == 'ft': f = me.polygons[n] f.material_index = int(val[0]) f.use_smooth = int(val[1]) n += 1 elif key == 'ftn': mn = int(val[1]) us = int(val[2]) npts = int(val[0]) for i in range(npts): f = me.polygons[n] f.material_index = mn f.use_smooth = us n += 1 elif key == 'mn': fn = int(val[0]) mn = int(val[1]) f = me.polygons[fn] f.material_index = mn elif key == 'ftall': mat = int(val[0]) smooth = int(val[1]) for f in me.polygons: f.material_index = mat f.use_smooth = smooth return def parseFaces2NoBMesh(tokens, me): n = 0 for (key, val, sub) in tokens: if key == 'ft': f = me.faces[n] f.material_index = int(val[0]) f.use_smooth = int(val[1]) n += 1 elif key == 'ftn': mn = int(val[1]) us = int(val[2]) npts = int(val[0]) for i in range(npts): f = me.faces[n] f.material_index = mn f.use_smooth = us n += 1 elif key == 'mn': fn = int(val[0]) mn = int(val[1]) f = me.faces[fn] f.material_index = mn elif key == 'ftall': mat = int(val[0]) smooth = int(val[1]) for f in me.faces: f.material_index = mat f.use_smooth = smooth return # # parseUvTexture(args, tokens, me,): # parseUvTexData(args, tokens, uvdata): # def parseUvTextureBMesh(args, tokens, me): name = args[0] bpy.ops.mesh.uv_texture_add() uvtex = me.uv_textures[-1] uvtex.name = name uvloop = me.uv_layers[-1] loadedData['MeshTextureFaceLayer'][name] = uvloop for (key, val, sub) in tokens: if key == 'Data': parseUvTexDataBMesh(val, sub, uvloop.data) else: defaultKey(key, val, sub, "uvtex", [], globals(), locals()) return def parseUvTexDataBMesh(args, tokens, data): n = 0 for (key, val, sub) in tokens: if key == 'vt': data[n].uv = (float(val[0]), float(val[1])) n += 1 data[n].uv = (float(val[2]), float(val[3])) n += 1 data[n].uv = (float(val[4]), float(val[5])) n += 1 if len(val) > 6: data[n].uv = (float(val[6]), float(val[7])) n += 1 return def parseUvTextureNoBMesh(args, tokens, me): name = args[0] uvtex = me.uv_textures.new(name = name) loadedData['MeshTextureFaceLayer'][name] = uvtex for (key, val, sub) in tokens: if key == 'Data': parseUvTexDataNoBMesh(val, sub, uvtex.data) else: defaultKey(key, val, sub, "uvtex", [], globals(), locals()) return def parseUvTexDataNoBMesh(args, tokens, data): n = 0 for (key, val, sub) in tokens: if key == 'vt': data[n].uv1 = (float(val[0]), float(val[1])) data[n].uv2 = (float(val[2]), float(val[3])) data[n].uv3 = (float(val[4]), float(val[5])) if len(val) > 6: data[n].uv4 = (float(val[6]), float(val[7])) n += 1 return # # parseVertColorLayer(args, tokens, me): # parseVertColorData(args, tokens, data): # def parseVertColorLayer(args, tokens, me): name = args[0] print("VertColorLayer", name) vcol = me.vertex_colors.new(name) loadedData['MeshColorLayer'][name] = vcol for (key, val, sub) in tokens: if key == 'Data': parseVertColorData(val, sub, vcol.data) else: defaultKey(key, val, sub, "vcol", [], globals(), locals()) return def parseVertColorData(args, tokens, data): n = 0 for (key, val, sub) in tokens: if key == 'cv': data[n].color1 = eval(val[0]) data[n].color2 = eval(val[1]) data[n].color3 = eval(val[2]) data[n].color4 = eval(val[3]) n += 1 return # # parseVertexGroup(ob, me, args, tokens): # def parseVertexGroup(ob, me, args, tokens): global toggle if verbosity > 2: print( "Parsing vertgroup %s" % args ) grpName = args[0] try: res = eval(args[1]) except: res = True if not res: return if (toggle & T_Armature) or (grpName in ['Eye_L', 'Eye_R', 'Gums', 'Head', 'Jaw', 'Left', 'Middle', 'Right', 'Scalp']): try: group = loadedData['VertexGroup'][grpName] except KeyError: group = ob.vertex_groups.new(grpName) loadedData['VertexGroup'][grpName] = group for (key, val, sub) in tokens: if key == 'wv': group.add( [int(val[0])], float(val[1]), 'REPLACE' ) return # # parseShapeKeys(ob, me, args, tokens): # parseShapeKey(ob, me, args, tokens): # addShapeKey(ob, name, vgroup, tokens): # doShape(name): # def doShape(name): if (toggle & T_Shapekeys) and (name == 'Basis'): return True else: return (toggle & T_Face) def parseShapeKeys(ob, me, args, tokens): for (key, val, sub) in tokens: if key == 'ShapeKey': parseShapeKey(ob, me, val, sub) elif key == 'AnimationData': if me.shape_keys: parseAnimationData(me.shape_keys, val, sub) elif key == 'Expression': prop = "Mhe" + val[0].capitalize() parseUnits(prop, ob, sub) elif key == 'Viseme': name = val[0].upper() if name in ["REST", "ETC"]: name = name.capitalize() prop = "Mhv" + name parseUnits(prop, ob, sub) ob.active_shape_key_index = 0 print("Shapekeys parsed") return def parseUnits(prop, ob, sub): string = "" for words in sub: unit = words[0].replace("-","_") value = words[1][0] string += "%s:%s;" % (unit, value) rig = ob.parent rig[prop] = string def parseShapeKey(ob, me, args, tokens): if verbosity > 2: print( "Parsing ob %s shape %s" % (bpy.context.object, args[0] )) name = args[0] lr = args[1] if invalid(args[2]): return if lr == 'Sym': # or toggle & T_Symm: addShapeKey(ob, name, None, tokens) elif lr == 'LR': addShapeKey(ob, name+'_L', 'Left', tokens) addShapeKey(ob, name+'_R', 'Right', tokens) else: MyError("ShapeKey L/R %s" % lr) return def addShapeKey(ob, name, vgroup, tokens): skey = ob.shape_key_add(name=name, from_mix=False) if name != 'Basis': skey.relative_key = loadedData['ShapeKey']['Basis'] skey.name = name if vgroup: skey.vertex_group = vgroup loadedData['ShapeKey'][name] = skey for (key, val, sub) in tokens: if key == 'sv': index = int(val[0]) pt = skey.data[index].co pt[0] += theScale*float(val[1]) pt[1] += theScale*float(val[2]) pt[2] += theScale*float(val[3]) else: defaultKey(key, val, sub, "skey", [], globals(), locals()) return # # parseArmature (obName, args, tokens) # def parseArmature (args, tokens): global toggle, theArmature if verbosity > 2: print( "Parsing armature %s" % args ) amtname = args[0] obname = args[1] mode = args[2] amt = bpy.data.armatures.new(amtname) ob = createObject('ARMATURE', obname, amt, amtname) linkObject(ob, amt) theArmature = ob bpy.ops.object.mode_set(mode='OBJECT') bpy.ops.object.mode_set(mode='EDIT') heads = {} tails = {} for (key, val, sub) in tokens: if key == 'Bone': bname = val[0] if not invalid(val[1]): bone = amt.edit_bones.new(bname) parseBone(bone, amt, sub, heads, tails) loadedData['Bone'][bname] = bone elif key == 'RecalcRoll': rolls = {} for bone in amt.edit_bones: bone.select = False blist = eval(val[0]) for name in blist: bone = amt.edit_bones[name] bone.select = True bpy.ops.armature.calculate_roll(type='Z') for bone in amt.edit_bones: rolls[bone.name] = bone.roll bpy.ops.object.mode_set(mode='OBJECT') for bone in amt.bones: bone['Roll'] = rolls[bone.name] bpy.ops.object.mode_set(mode='EDIT') else: defaultKey(key, val, sub, "amt", ['MetaRig'], globals(), locals()) bpy.ops.object.mode_set(mode='OBJECT') return amt # # parseBone(bone, amt, tokens, heads, tails): # def parseBone(bone, amt, tokens, heads, tails): global todo for (key, val, sub) in tokens: if key == "head": bone.head = (theScale*float(val[0]), theScale*float(val[1]), theScale*float(val[2])) elif key == "tail": bone.tail = (theScale*float(val[0]), theScale*float(val[1]), theScale*float(val[2])) #elif key == 'restrict_select': # pass elif key == 'hide' and val[0] == 'True': name = bone.name ''' #bpy.ops.object.mode_set(mode='OBJECT') pbone = amt.bones[name] pbone.hide = True print("Hide", pbone, pbone.hide) #bpy.ops.object.mode_set(mode='EDIT') ''' else: defaultKey(key, val, sub, "bone", [], globals(), locals()) return bone # # parsePose (args, tokens): # def parsePose (args, tokens): global todo name = args[0] ob = loadedData['Object'][name] bpy.context.scene.objects.active = ob bpy.ops.object.mode_set(mode='POSE') pbones = ob.pose.bones nGrps = 0 for (key, val, sub) in tokens: if key == 'Posebone': parsePoseBone(pbones, ob, val, sub) elif key == 'BoneGroup': parseBoneGroup(ob.pose, nGrps, val, sub) nGrps += 1 elif key == 'SetProp': bone = val[0] prop = val[1] value = eval(val[2]) pb = pbones[bone] print("Setting", pb, prop, val) pb[prop] = value print("Prop set", pb[prop]) else: defaultKey(key, val, sub, "ob.pose", [], globals(), locals()) bpy.ops.object.mode_set(mode='OBJECT') return ob # # parsePoseBone(pbones, args, tokens): # parseArray(data, exts, args): # def parseBoneGroup(pose, nGrps, args, tokens): global todo if verbosity > 2: print( "Parsing bonegroup %s" % args ) name = args[0] bpy.ops.pose.group_add() bg = pose.bone_groups.active loadedData['BoneGroup'][name] = bg for (key, val, sub) in tokens: defaultKey(key, val, sub, "bg", [], globals(), locals()) return def parsePoseBone(pbones, ob, args, tokens): global todo if invalid(args[1]): return name = args[0] pb = pbones[name] amt = ob.data amt.bones.active = pb.bone for (key, val, sub) in tokens: if key == 'Constraint': amt.bones.active = pb.bone cns = parseConstraint(pb.constraints, pb, val, sub) elif key == 'bpyops': amt.bones.active = pb.bone expr = "bpy.ops.%s" % val[0] print(expr) exec(expr) elif key == 'ik_dof': parseArray(pb, ["ik_dof_x", "ik_dof_y", "ik_dof_z"], val) elif key == 'ik_limit': parseArray(pb, ["ik_limit_x", "ik_limit_y", "ik_limit_z"], val) elif key == 'ik_max': parseArray(pb, ["ik_max_x", "ik_max_y", "ik_max_z"], val) elif key == 'ik_min': parseArray(pb, ["ik_min_x", "ik_min_y", "ik_min_z"], val) elif key == 'ik_stiffness': parseArray(pb, ["ik_stiffness_x", "ik_stiffness_y", "ik_stiffness_z"], val) elif key == 'hide': #bpy.ops.object.mode_set(mode='OBJECT') amt.bones[name].hide = eval(val[0]) #bpy.ops.object.mode_set(mode='POSE') else: defaultKey(key, val, sub, "pb", [], globals(), locals()) #print("pb %s done" % name) return def parseArray(data, exts, args): n = 1 for ext in exts: expr = "data.%s = %s" % (ext, args[n]) # print(expr) exec(expr) n += 1 return # # parseConstraint(constraints, pb, args, tokens) # def parseConstraint(constraints, pb, args, tokens): if invalid(args[2]): return None if (toggle&T_Opcns and pb): print("Active") aob = bpy.context.object print("ob", aob) aamt = aob.data print("amt", aamt) apose = aob.pose print("pose", apose) abone = aamt.bones.active print("bone", abone) print('Num cns before', len(list(constraints))) bpy.ops.pose.constraint_add(type=args[1]) cns = constraints.active print('and after', pb, cns, len(list(constraints))) else: cns = constraints.new(args[1]) cns.name = args[0] for (key,val,sub) in tokens: if key == 'invert': parseArray(cns, ["invert_x", "invert_y", "invert_z"], val) elif key == 'use': parseArray(cns, ["use_x", "use_y", "use_z"], val) elif key == 'pos_lock': parseArray(cns, ["lock_location_x", "lock_location_y", "lock_location_z"], val) elif key == 'rot_lock': parseArray(cns, ["lock_rotation_x", "lock_rotation_y", "lock_rotation_z"], val) else: defaultKey(key, val, sub, "cns", ["use_target"], globals(), locals()) #print("cns %s done" % cns.name) return cns # # parseCurve (args, tokens): # parseSpline(cu, args, tokens): # parseBezier(spline, n, args, tokens): # def parseCurve (args, tokens): global todo if verbosity > 2: print( "Parsing curve %s" % args ) bpy.ops.object.add(type='CURVE') cu = setObjectAndData(args, 'Curve') for (key, val, sub) in tokens: if key == 'Spline': parseSpline(cu, val, sub) else: defaultKey(key, val, sub, "cu", [], globals(), locals()) return def parseTextCurve (args, tokens): global todo if verbosity > 2: print( "Parsing text curve %s" % args ) bpy.ops.object.text_add() txt = setObjectAndData(args, 'Text') for (key, val, sub) in tokens: if key == 'Spline': parseSpline(txt, val, sub) elif key == 'BodyFormat': parseCollection(txt.body_format, sub, []) elif key == 'EditFormat': parseDefault(txt.edit_format, sub, {}, []) elif key == 'Font': parseDefault(txt.font, sub, {}, []) elif key == 'TextBox': parseCollection(txt.body_format, sub, []) else: defaultKey(key, val, sub, "txt", [], globals(), locals()) return def parseSpline(cu, args, tokens): typ = args[0] spline = cu.splines.new(typ) nPointsU = int(args[1]) nPointsV = int(args[2]) #spline.point_count_u = nPointsU #spline.point_count_v = nPointsV if typ == 'BEZIER' or typ == 'BSPLINE': spline.bezier_points.add(nPointsU) else: spline.points.add(nPointsU) n = 0 for (key, val, sub) in tokens: if key == 'bz': parseBezier(spline.bezier_points[n], val, sub) n += 1 elif key == 'pt': parsePoint(spline.points[n], val, sub) n += 1 else: defaultKey(key, val, sub, "spline", [], globals(), locals()) return def parseBezier(bez, args, tokens): bez.co = eval(args[0]) bez.co = theScale*bez.co bez.handle1 = eval(args[1]) bez.handle1_type = args[2] bez.handle2 = eval(args[3]) bez.handle2_type = args[4] return def parsePoint(pt, args, tokens): pt.co = eval(args[0]) pt.co = theScale*pt.co print(" pt", pt.co) return # # parseLattice (args, tokens): # def parseLattice (args, tokens): global todo if verbosity > 2: print( "Parsing lattice %s" % args ) bpy.ops.object.add(type='LATTICE') lat = setObjectAndData(args, 'Lattice') for (key, val, sub) in tokens: if key == 'Points': parseLatticePoints(val, sub, lat.points) else: defaultKey(key, val, sub, "lat", [], globals(), locals()) return def parseLatticePoints(args, tokens, points): global todo n = 0 for (key, val, sub) in tokens: if key == 'pt': v = points[n].co_deform v.x = theScale*float(val[0]) v.y = theScale*float(val[1]) v.z = theScale*float(val[2]) n += 1 return # # parseLamp (args, tokens): # parseFalloffCurve(focu, args, tokens): # def parseLamp (args, tokens): global todo if verbosity > 2: print( "Parsing lamp %s" % args ) bpy.ops.object.add(type='LAMP') lamp = setObjectAndData(args, 'Lamp') for (key, val, sub) in tokens: if key == 'FalloffCurve': parseFalloffCurve(lamp.falloff_curve, val, sub) else: defaultKey(key, val, sub, "lamp", [], globals(), locals()) return def parseFalloffCurve(focu, args, tokens): return # # parseGroup (args, tokens): # parseGroupObjects(args, tokens, grp): # def parseGroup (args, tokens): global todo if verbosity > 2: print( "Parsing group %s" % args ) grpName = args[0] grp = bpy.data.groups.new(grpName) loadedData['Group'][grpName] = grp for (key, val, sub) in tokens: if key == 'Objects': parseGroupObjects(val, sub, grp) else: defaultKey(key, val, sub, "grp", [], globals(), locals()) return def parseGroupObjects(args, tokens, grp): global todo rig = None for (key, val, sub) in tokens: if key == 'ob': try: ob = loadedData['Object'][val[0]] grp.objects.link(ob) except: ob = None if ob: print(ob, ob.type, rig, ob.parent) if ob.type == 'ARMATURE': rig = ob elif ob.type == 'EMPTY' and rig and not ob.parent: ob.parent = rig print("SSS") return # # parseWorld (args, tokens): # def parseWorld (args, tokens): global todo if verbosity > 2: print( "Parsing world %s" % args ) world = bpy.context.scene.world for (key, val, sub) in tokens: if key == 'Lighting': parseDefault(world.lighting, sub, {}, []) elif key == 'Mist': parseDefault(world.mist, sub, {}, []) elif key == 'Stars': parseDefault(world.stars, sub, {}, []) else: defaultKey(key, val, sub, "world", [], globals(), locals()) return # # parseScene (args, tokens): # parseRenderSettings(render, args, tokens): # parseToolSettings(tool, args, tokens): # def parseScene (args, tokens): global todo if verbosity > 2: print( "Parsing scene %s" % args ) scn = bpy.context.scene for (key, val, sub) in tokens: if key == 'NodeTree': scn.use_nodes = True parseNodeTree(scn, val, sub) elif key == 'GameData': parseDefault(scn.game_data, sub, {}, []) elif key == 'KeyingSet': pass #parseDefault(scn.keying_sets, sub, {}, []) elif key == 'ObjectBase': pass #parseDefault(scn.bases, sub, {}, []) elif key == 'RenderSettings': parseRenderSettings(scn.render, sub, []) elif key == 'ToolSettings': subkeys = {'ImagePaint' : "image_paint", 'Sculpt' : "sculpt", 'VertexPaint' : "vertex_paint", 'WeightPaint' : "weight_paint" } parseDefault(scn.tool_settings, sub, subkeys, []) elif key == 'UnitSettings': parseDefault(scn.unit_settings, sub, {}, []) else: defaultKey(key, val, sub, "scn", [], globals(), locals()) return def parseRenderSettings(render, args, tokens): global todo if verbosity > 2: print( "Parsing RenderSettings %s" % args ) for (key, val, sub) in tokens: if key == 'Layer': pass #parseDefault(scn.layers, sub, []) else: defaultKey(key, val, sub, "render", [], globals(), locals()) return # # parseDefineProperty(args, tokens): # def parseDefineProperty(args, tokens): expr = "bpy.types.Object.%s = %sProperty" % (args[0], args[1]) c = '(' for option in args[2:]: expr += "%s %s" % (c, option) c = ',' expr += ')' #print(expr) exec(expr) #print("Done") return # # correctRig(args): # def correctRig(args): human = args[0] print("CorrectRig %s" % human) try: ob = loadedData['Object'][human] except: return ob.MhxShapekeyDrivers = (toggle&T_Shapekeys and toggle&T_ShapeDrivers) bpy.context.scene.objects.active = ob bpy.ops.object.mode_set(mode='POSE') amt = ob.data cnslist = [] for pb in ob.pose.bones: pb.bone.select = False for cns in pb.constraints: if cns.type == 'CHILD_OF': cnslist.append((pb, cns, cns.influence)) cns.influence = 0 for (pb, cns, inf) in cnslist: amt.bones.active = pb.bone cns.influence = 1 #print("Childof %s %s %s %.2f" % (amt.name, pb.name, cns.name, inf)) bpy.ops.constraint.childof_clear_inverse(constraint=cns.name, owner='BONE') bpy.ops.constraint.childof_set_inverse(constraint=cns.name, owner='BONE') cns.influence = 0 for (pb, cns, inf) in cnslist: cns.influence = inf return # # postProcess(args) # def postProcess(args): human = args[0] print("Postprocess %s" % human) try: ob = loadedData['Object'][human] except: ob = None if toggle & T_Diamond == 0 and ob: deleteDiamonds(ob) return # # deleteDiamonds(ob) # Delete joint diamonds in main mesh # Invisio = material # 1 # def deleteDiamonds(ob): bpy.context.scene.objects.active = ob if not bpy.context.object: return print("Delete helper geometry in %s" % bpy.context.object) bpy.ops.object.mode_set(mode='EDIT') bpy.ops.mesh.select_all(action='DESELECT') bpy.ops.object.mode_set(mode='OBJECT') me = ob.data invisioNum = -1 for mn,mat in enumerate(me.materials): if "Invis" in mat.name: invisioNum = mn break if invisioNum < 0: print("WARNING: Nu Invisio material found. Cannot delete helper geometry") elif BMeshAware: for f in me.polygons: if f.material_index >= invisioNum: for vn in f.vertices: me.vertices[vn].select = True else: for f in me.faces: if f.material_index >= invisioNum: for vn in f.vertices: me.vertices[vn].select = True if BMeshAware and toggle&T_CrashSafe: theMessage = "\n *** WARNING ***\nHelper deletion turned off due to Blender crash.\nHelpers can be deleted by deleting all selected vertices in Edit mode\n **********\n" print(theMessage) else: bpy.ops.object.mode_set(mode='EDIT') print("Do delete") bpy.ops.mesh.delete(type='VERT') print("Verts deleted") bpy.ops.object.mode_set(mode='OBJECT') print("Back to object mode") return # # defaultKey(ext, args, tokens, var, exclude, glbals, lcals): # theProperty = None def propNames(string): global alpha7 # Alpha 7 compatibility if string[0:2] == "&_": string = "Mhf"+string[2:] alpha7 = True elif string[0] == "&": string = "Mha"+string[1:] alpha7 = True elif string[0] == "*": string = "Mhs"+string[1:] alpha7 = True elif string.startswith("Hide"): string = "Mhh"+string[4:] alpha7 = True if string.startswith(("Mha", "Mhf", "Mhs", "Mhh", "Mhv", "Mhc")): name = string.replace("-","_") return name, '["%s"]' % name elif string[0] == "_": return None,None else: return string, '["%s"]' % string def defProp(args, var, glbals, lcals): proptype = args[0] name = propNames(args[1])[0] value = args[2] rest = 'description="%s"' % args[3].replace("_", " ") if len(args) > 4: rest += ", " + args[4] if name: expr = 'bpy.types.Object.%s = %sProperty(%s)' % (name, proptype, rest) print(expr) exec(expr) expr = '%s.%s = %s' % (var, name, value) print(expr) exec(expr, glbals, lcals) def defNewProp(name, proptype, rest): expr = 'bpy.types.Object.%s = %sProperty(%s)' % (name, proptype, rest) print(expr) exec(expr) def setProperty(args, var, glbals, lcals): global theProperty tip = "" name = propNames(args[0])[0] value = args[1] if name: expr = '%s["%s"] = %s' % (var, name, value) exec(expr, glbals, lcals) print(expr) if len(args) > 2: tip = 'description="%s"' % args[2].replace("_", " ") if value in ["True", "False"]: proptype = "Bool" elif value[0] in ["'",'"']: proptype = "String" elif "." in value: proptype = "Float" else: proptype = "Int" theProperty = (name, tip, proptype) #if proptype == "Int": # halt return def defineProperty(args): global theProperty if theProperty is None: return (name, tip, proptype) = theProperty if len(args) >= 2 and proptype != "Bool": if "BOOLEAN" in args[1]: proptype = "Bool" else: tip = tip + "," + args[1].replace(":", "=").replace('"', " ") expr = "bpy.types.Object.%s = %sProperty(%s)" % (name, proptype, tip) print(expr) exec(expr) if proptype == "Int": halt theProperty = None return def defaultKey(ext, args, tokens, var, exclude, glbals, lcals): global todo if ext == 'Property': return setProperty(args, var, glbals, lcals) elif ext == 'PropKeys': return defineProperty(args) elif ext == 'DefProp': return defProp(args, var, glbals, lcals) if ext == 'bpyops': expr = "bpy.ops.%s" % args[0] print(expr) exec(expr) return nvar = "%s.%s" % (var, ext) #print(ext) if ext in exclude: return #print("D", nvar) if len(args) == 0: MyError("Key length 0: %s" % ext) rnaType = args[0] if rnaType == 'Add': print("*** Cannot Add yet ***") return elif rnaType == 'Refer': typ = args[1] name = args[2] data = "loadedData['%s']['%s']" % (typ, name) elif rnaType == 'Struct' or rnaType == 'Define': typ = args[1] name = args[2] try: data = eval(nvar, glbals, lcals) except: data = None # print("Old structrna", nvar, data) if data is None: try: creator = args[3] except: creator = None # print("Creator", creator, eval(var,glbals,lcals)) try: rna = eval(var,glbals,lcals) data = eval(creator) except: data = None # print("New struct", nvar, typ, data) if rnaType == 'Define': loadedData[typ][name] = data if data: for (key, val, sub) in tokens: defaultKey(key, val, sub, "data", [], globals(), locals()) print("Struct done", nvar) return elif rnaType == 'PropertyRNA': MyError("PropertyRNA!") #print("PropertyRNA ", ext, var) for (key, val, sub) in tokens: defaultKey(ext, val, sub, nvar, [], glbals, lcals) return elif rnaType == 'Array': for n in range(1, len(args)): expr = "%s[%d] = %s" % (nvar, n-1, args[n]) exec(expr, glbals, lcals) if len(args) > 0: expr = "%s[0] = %s" % (nvar, args[1]) exec(expr, glbals, lcals) return elif rnaType == 'List': data = [] for (key, val, sub) in tokens: elt = eval(val[1], glbals, lcals) data.append(elt) elif rnaType == 'Matrix': return i = 0 n = len(tokens) for (key, val, sub) in tokens: if key == 'row': for j in range(n): expr = "%s[%d][%d] = %g" % (nvar, i, j, float(val[j])) exec(expr, glbals, lcals) i += 1 return else: try: data = loadedData[rnaType][args[1]] #print("From loaded", rnaType, args[1], data) return data except: data = rnaType #print(var, ext, data) expr = "%s = %s" % (nvar, data) try: exec(expr, glbals, lcals) except: pushOnTodoList(var, expr, glbals, lcals) return # # # def pushOnTodoList(var, expr, glbals, lcals): global todo print("Tdo", var) print(dir(eval(var, glbals, lcals))) MyError( "Unrecognized expression %s.\n" % expr + "This can mean that Blender's python API has changed\n" + "since the MHX file was exported. Try to export again\n" + "from an up-to-date MakeHuman nightly build.\n" + "Alternatively, your Blender version may be obsolete.\n" + "Download an up-to-date version from www.graphicall.org") todo.append((expr, glbals, lcals)) return # # parseBoolArray(mask): # def parseBoolArray(mask): list = [] for c in mask: if c == '0': list.append(False) else: list.append(True) return list # parseMatrix(args, tokens) # def parseMatrix(args, tokens): matrix = mathutils.Matrix() i = 0 for (key, val, sub) in tokens: if key == 'row': matrix[i][0] = float(val[0]) matrix[i][1] = float(val[1]) matrix[i][2] = float(val[2]) matrix[i][3] = float(val[3]) i += 1 return matrix # # parseDefault(data, tokens, subkeys, exclude): # def parseDefault(data, tokens, subkeys, exclude): for (key, val, sub) in tokens: if key in subkeys.keys(): for (key2, val2, sub2) in sub: defaultKey(key2, val2, sub2, "data.%s" % subkeys[key], [], globals(), locals()) else: defaultKey(key, val, sub, "data", exclude, globals(), locals()) def parseCollection(data, tokens, exclude): return # # Utilities # # # extractBpyType(data): # """ def extractBpyType(data): typeSplit = str(type(data)).split("'") if typeSplit[0] != ' 1: sceneLayers = int(args[2], 16) sceneHideLayers = int(args[3], 16) boneLayers = int(args[4], 16) # boneHideLayers = int(args[5], 16) boneHideLayers = 0 else: sceneLayers = 0x00ff sceneHideLayers = 0 boneLayers = 0 boneHideLayers = 0 scn = bpy.context.scene mask = 1 hidelayers = [] for n in range(20): scn.layers[n] = True if sceneLayers & mask else False if sceneHideLayers & mask: hidelayers.append(n) mask = mask << 1 for ob in scn.objects: for n in hidelayers: if ob.layers[n]: ob.hide = True ob.hide_render = True if boneLayers: human = args[1] try: ob = loadedData['Object'][human] except: return mask = 1 hidelayers = [] for n in range(32): ob.data.layers[n] = True if boneLayers & mask else False if boneHideLayers & mask: hidelayers.append(n) mask = mask << 1 for b in ob.data.bones: for n in hidelayers: if b.layers[n]: b.hide = True return # # readDefaults(): # writeDefaults(): # ConfigFile = '~/mhx_import.cfg' def readDefaults(): global toggle, toggleSettings, theScale path = os.path.realpath(os.path.expanduser(ConfigFile)) try: fp = open(path, 'rU') print('Storing defaults') except: print('Cannot open "%s" for reading' % path) return bver = '' for line in fp: words = line.split() if len(words) >= 3: try: toggle = int(words[0],16) theScale = float(words[1]) except: print('Configuration file "%s" is corrupt' % path) fp.close() toggleSettings = toggle return def writeDefaults(): global toggleSettings, theScale path = os.path.realpath(os.path.expanduser(ConfigFile)) try: fp = open(path, 'w') print('Storing defaults') except: print('Cannot open "%s" for writing' % path) return fp.write("%x %f Graphicall" % (toggleSettings, theScale)) fp.close() return ################################################################################### # # Postprocessing of rigify rig # # rigifyMhx(context, name): # ################################################################################### def rigifyMhx(context, name): print("Modifying MHX rig to Rigify") scn = context.scene mhx = loadedData['Object'][name] mhx.MhxRigify = True bpy.context.scene.objects.active = mhx # Delete old widgets """ for ob in scn.objects: if ob.type == 'MESH' and ob.name[0:3] == "WGT": scn.objects.unlink(ob) """ # Save mhx bone locations heads = {} tails = {} rolls = {} parents = {} extras = {} bpy.ops.object.mode_set(mode='EDIT') newParents = { 'head' : 'DEF-head', 'ribs' : 'DEF-ribs', 'upper_arm.L' : 'DEF-upper_arm.L.02', 'thigh.L' : 'DEF-thigh.L.02', 'upper_arm.R' : 'DEF-upper_arm.R.02', 'thigh.R' : 'DEF-thigh.R.02', } for eb in mhx.data.edit_bones: heads[eb.name] = eb.head.copy() tails[eb.name] = eb.tail.copy() rolls[eb.name] = eb.roll if eb.parent: par = eb.parent.name # print(eb.name, par) try: parents[eb.name] = newParents[par] except: parents[eb.name] = par else: parents[eb.name] = None extras[eb.name] = not eb.layers[16] bpy.ops.object.mode_set(mode='OBJECT') # Find corresponding meshes. Can be several (clothes etc.) meshes = [] for ob in scn.objects: for mod in ob.modifiers: if (mod.type == 'ARMATURE' and mod.object == mhx): meshes.append((ob, mod)) if meshes == []: MyError("Did not find matching mesh") # Rename Head vertex group for (mesh, mod) in meshes: try: vg = mesh.vertex_groups['DfmHead'] vg.name = 'DEF-head' except: pass # Change meta bone locations scn.objects.active = None try: bpy.ops.object.armature_human_advanced_add() success = True except: success = False if not success: MyError("Unable to create advanced human. \n" \ "Make sure that the Rigify addon is enabled. \n" \ "It is found under Rigging.") return meta = context.object bpy.ops.object.mode_set(mode='EDIT') for eb in meta.data.edit_bones: eb.head = heads[eb.name] eb.tail = tails[eb.name] eb.roll = rolls[eb.name] extras[eb.name] = False fingerPlanes = [ ('UP-thumb.L', 'thumb.01.L', 'thumb.03.L', ['thumb.02.L']), ('UP-index.L', 'finger_index.01.L', 'finger_index.03.L', ['finger_index.02.L']), ('UP-middle.L', 'finger_middle.01.L', 'finger_middle.03.L', ['finger_middle.02.L']), ('UP-ring.L', 'finger_ring.01.L', 'finger_ring.03.L', ['finger_ring.02.L']), ('UP-pinky.L', 'finger_pinky.01.L', 'finger_pinky.03.L', ['finger_pinky.02.L']), ('UP-thumb.R', 'thumb.01.R', 'thumb.03.R', ['thumb.02.R']), ('UP-index.R', 'finger_index.01.R', 'finger_index.03.R', ['finger_index.02.R']), ('UP-middle.R', 'finger_middle.01.R', 'finger_middle.03.R', ['finger_middle.02.R']), ('UP-ring.R', 'finger_ring.01.R', 'finger_ring.03.R', ['finger_ring.02.R']), ('UP-pinky.R', 'finger_pinky.01.R', 'finger_pinky.03.R', ['finger_pinky.02.R']), ] for (upbone, first, last, middles) in fingerPlanes: extras[upbone] = False #lineateChain(upbone, first, last, middles, 0.01, meta, heads, tails) ikPlanes = [ ('UP-leg.L', 'thigh.L', 'shin.L'), ('UP-arm.L', 'upper_arm.L', 'forearm.L'), ('UP-leg.R', 'thigh.R', 'shin.R'), ('UP-arm.R', 'upper_arm.R', 'forearm.R'), ] for (upbone, first, last) in ikPlanes: extras[upbone] = False lineateChain(upbone, first, last, [], 0.1, meta, heads, tails) bpy.ops.object.mode_set(mode='OBJECT') # Generate rigify rig bpy.ops.pose.rigify_generate() scn.objects.unlink(meta) rigify = context.object rigify.name = name+"Rig" layers = 20*[False] layers[1] = True rigify.layers = layers rigify.show_x_ray = True for (mesh, mod) in meshes: mod.object = rigify grp = loadedData['Group'][name] grp.objects.link(rigify) # Parent widgets under empty empty = bpy.data.objects.new("Widgets", None) scn.objects.link(empty) empty.layers = 20*[False] empty.layers[19] = True empty.parent = rigify for ob in scn.objects: if ob.type == 'MESH' and ob.name[0:4] == "WGT-" and not ob.parent: ob.parent = empty grp.objects.link(ob) elif ob.parent == mhx: ob.parent = rigify # Copy extra bones to rigify rig bpy.ops.object.mode_set(mode='EDIT') for name in heads.keys(): if extras[name]: eb = rigify.data.edit_bones.new(name) eb.head = heads[name] eb.tail = tails[name] eb.roll = rolls[name] for name in heads.keys(): if extras[name] and parents[name]: eb = rigify.data.edit_bones[name] eb.parent = rigify.data.edit_bones[parents[name]] # Copy constraints etc. bpy.ops.object.mode_set(mode='POSE') for name in heads.keys(): if extras[name]: pb1 = mhx.pose.bones[name] pb2 = rigify.pose.bones[name] pb2.custom_shape = pb1.custom_shape pb2.lock_location = pb1.lock_location pb2.lock_rotation = pb1.lock_rotation pb2.lock_scale = pb1.lock_scale b1 = pb1.bone b2 = pb2.bone b2.use_deform = b1.use_deform b2.hide_select = b1.hide_select b2.show_wire = b1.show_wire layers = 32*[False] if b1.layers[8]: layers[28] = True else: layers[29] = True if b1.layers[10]: layers[2] = True b2.layers = layers for cns1 in pb1.constraints: cns2 = copyConstraint(cns1, pb1, pb2, mhx, rigify) if cns2.type == 'CHILD_OF': rigify.data.bones.active = pb2.bone bpy.ops.constraint.childof_set_inverse(constraint=cns2.name, owner='BONE') # Create animation data if mhx.animation_data: for fcu in mhx.animation_data.drivers: rigify.animation_data.drivers.from_existing(src_driver=fcu) fixDrivers(rigify.animation_data, mhx, rigify) for (mesh, mod) in meshes: mesh.parent = rigify skeys = mesh.data.shape_keys if skeys: fixDrivers(skeys.animation_data, mhx, rigify) scn.objects.unlink(mhx) print("Rigify rig complete") return # # lineateChain(upbone, first, last, middles, minDist, rig, heads, tails): # lineate(pt, start, minDist, normal, offVector): # def lineateChain(upbone, first, last, middles, minDist, rig, heads, tails): fb = rig.data.edit_bones[first] lb = rig.data.edit_bones[last] uhead = heads[upbone] utail = tails[upbone] tang = lb.tail - fb.head tangent = tang/tang.length up = (uhead+utail)/2 - fb.head norm = up - tangent*tangent.dot(up) normal = norm/norm.length offVector = tangent.cross(normal) vec = utail - uhead fb.tail = lineate(fb.tail, fb.head, minDist, normal, offVector) lb.head = lineate(lb.head, fb.head, minDist, normal, offVector) for bone in middles: mb = rig.data.edit_bones[bone] mb.head = lineate(mb.head, fb.head, minDist, normal, offVector) mb.tail = lineate(mb.tail, fb.head, minDist, normal, offVector) return def lineate(pt, start, minDist, normal, offVector): diff = pt - start diff = diff - offVector*offVector.dot(diff) dist = diff.dot(normal) if dist < minDist: diff += (minDist - dist)*normal return start + diff # # fixDrivers(adata, mhx, rigify): # def fixDrivers(adata, mhx, rigify): if not adata: return for fcu in adata.drivers: for var in fcu.driver.variables: for targ in var.targets: if targ.id == mhx: targ.id = rigify return # # copyConstraint(cns1, pb1, pb2, mhx, rigify): # def copyConstraint(cns1, pb1, pb2, mhx, rigify): substitute = { 'Head' : 'DEF-head', 'MasterFloor' : 'root', 'upper_arm.L' : 'DEF-upper_arm.L.01', 'upper_arm.R' : 'DEF-upper_arm.R.01', 'thigh.L' : 'DEF-thigh.L.01', 'thigh.R' : 'DEF-thigh.R.01', 'shin.L' : 'DEF-shin.L.01', 'shin.R' : 'DEF-shin.R.01' } cns2 = pb2.constraints.new(cns1.type) for prop in dir(cns1): if prop == 'target': if cns1.target == mhx: cns2.target = rigify else: cns2.target = cns1.target elif prop == 'subtarget': try: cns2.subtarget = substitute[cns1.subtarget] except: cns2.subtarget = cns1.subtarget elif prop[0] != '_': try: expr = "cns2.%s = cns1.%s" % (prop, prop) #print(pb1.name, expr) exec(expr) except: pass return cns2 # # class OBJECT_OT_RigifyMhxButton(bpy.types.Operator): # class OBJECT_OT_RigifyMhxButton(bpy.types.Operator): bl_idname = "mhxrig.rigify_mhx" bl_label = "Rigify MHX rig" bl_options = {'UNDO'} def execute(self, context): rigifyMhx(context, context.object.name) return{'FINISHED'} # # class RigifyMhxPanel(bpy.types.Panel): # class RigifyMhxPanel(bpy.types.Panel): bl_label = "Rigify MHX" bl_space_type = "VIEW_3D" bl_region_type = "UI" @classmethod def poll(cls, context): if context.object: return context.object.MhxRigify return False def draw(self, context): self.layout.operator("mhxrig.rigify_mhx") return ################################################################################### # # Error popup # ################################################################################### DEBUG = False from bpy.props import StringProperty, FloatProperty, EnumProperty, BoolProperty class ErrorOperator(bpy.types.Operator): bl_idname = "mhx.error" bl_label = "Error when loading MHX file" def execute(self, context): return {'RUNNING_MODAL'} def invoke(self, context, event): global theErrorLines maxlen = 0 for line in theErrorLines: if len(line) > maxlen: maxlen = len(line) width = 20+5*maxlen height = 20+5*len(theErrorLines) #self.report({'INFO'}, theMessage) wm = context.window_manager return wm.invoke_props_dialog(self, width=width, height=height) def draw(self, context): global theErrorLines for line in theErrorLines: self.layout.label(line) def MyError(message): global theMessage, theErrorLines, theErrorStatus theMessage = message theErrorLines = message.split('\n') theErrorStatus = True bpy.ops.mhx.error('INVOKE_DEFAULT') raise MhxError(theMessage) class MhxError(Exception): def __init__(self, value): self.value = value def __str__(self): return repr(self.value) class SuccessOperator(bpy.types.Operator): bl_idname = "mhx.success" bl_label = "MHX file successfully loaded:" message = StringProperty() def execute(self, context): return {'RUNNING_MODAL'} def invoke(self, context, event): wm = context.window_manager return wm.invoke_props_dialog(self) def draw(self, context): self.layout.label(self.message + theMessage) ################################################################################### # # User interface # ################################################################################### from bpy_extras.io_utils import ImportHelper MhxBoolProps = [ ("enforce", "Enforce version", "Only accept MHX files of correct version", T_EnforceVersion), #("crash_safe", "Crash-safe", "Disable features that have caused Blender crashes", T_CrashSafe), ("mesh", "Mesh", "Use main mesh", T_Mesh), ("proxy", "Proxies", "Use proxies", T_Proxy), #("armature", "Armature", "Use armature", T_Armature), #("replace", "Replace scene", "Replace scene", T_Replace), ("cage", "Cage", "Load mesh deform cage", T_Cage), ("clothes", "Clothes", "Include clothes", T_Clothes), ("shapekeys", "Shapekeys", "Include shapekeys", T_Shapekeys), ("shapedrivers", "Shapekey drivers", "Include shapekey drivers", T_ShapeDrivers), #("symm", "Symmetric shapes", "Keep shapekeys symmetric", T_Symm), ("diamond", "Helper geometry", "Keep helper geometry", T_Diamond), ("rigify", "Rigify", "Create rigify control rig", T_Rigify), ] class ImportMhx(bpy.types.Operator, ImportHelper): """Import from MHX file format (.mhx)""" bl_idname = "import_scene.makehuman_mhx" bl_description = 'Import from MHX file format (.mhx)' bl_label = "Import MHX" bl_space_type = "PROPERTIES" bl_region_type = "WINDOW" bl_options = {'UNDO'} filename_ext = ".mhx" filter_glob = StringProperty(default="*.mhx", options={'HIDDEN'}) filepath = StringProperty(subtype='FILE_PATH') scale = FloatProperty(name="Scale", description="Default meter, decimeter = 1.0", default = theScale) advanced = BoolProperty(name="Advanced settings", description="Use advanced import settings", default=False) for (prop, name, desc, flag) in MhxBoolProps: expr = '%s = BoolProperty(name="%s", description="%s", default=toggleSettings&%s)' % (prop, name, desc, flag) exec(expr) def draw(self, context): layout = self.layout layout.prop(self, "scale") layout.prop(self, "advanced") if self.advanced: for (prop, name, desc, flag) in MhxBoolProps: layout.prop(self, prop) def execute(self, context): global toggle, toggleSettings, theScale, MhxBoolProps if not self.advanced: toggle = DefaultToggle else: toggle = T_Armature for (prop, name, desc, flag) in MhxBoolProps: expr = '(%s if self.%s else 0)' % (flag, prop) toggle |= eval(expr) toggleSettings = toggle print("execute flags %x" % toggle) theScale = self.scale try: readMhxFile(self.filepath) bpy.ops.mhx.success('INVOKE_DEFAULT', message = self.filepath) except MhxError: print("Error when loading MHX file:\n" + theMessage) if self.advanced: writeDefaults() self.advanced = False return {'FINISHED'} def invoke(self, context, event): global toggle, theScale, MhxBoolProps readDefaults() self.scale = theScale for (prop, name, desc, flag) in MhxBoolProps: expr = 'self.%s = toggle&%s' % (prop, flag) exec(expr) context.window_manager.fileselect_add(self) return {'RUNNING_MODAL'} ################################################################################### # # Lipsync panel # ################################################################################### # # visemes # """ stopStaringVisemes = ({ 'Rest' : [ ('PMouth', (0,0)), ('PUpLip', (0,-0.1)), ('PLoLip', (0,0.1)), ('PJaw', (0,0.05)), ('PTongue', (0,0.0))], 'Etc' : [ ('PMouth', (0,0)), ('PUpLip', (0,-0.1)), ('PLoLip', (0,0.1)), ('PJaw', (0,0.15)), ('PTongue', (0,0.0))], 'MBP' : [('PMouth', (-0.3,0)), ('PUpLip', (0,1)), ('PLoLip', (0,0)), ('PJaw', (0,0.1)), ('PTongue', (0,0.0))], 'OO' : [('PMouth', (-1.5,0)), ('PUpLip', (0,0)), ('PLoLip', (0,0)), ('PJaw', (0,0.2)), ('PTongue', (0,0.0))], 'O' : [('PMouth', (-1.1,0)), ('PUpLip', (0,0)), ('PLoLip', (0,0)), ('PJaw', (0,0.5)), ('PTongue', (0,0.0))], 'R' : [('PMouth', (-0.9,0)), ('PUpLip', (0,-0.2)), ('PLoLip', (0,0.2)), ('PJaw', (0,0.2)), ('PTongue', (0,0.0))], 'FV' : [('PMouth', (0,0)), ('PUpLip', (0,0)), ('PLoLip', (0,-0.8)), ('PJaw', (0,0.1)), ('PTongue', (0,0.0))], 'S' : [('PMouth', (0,0)), ('PUpLip', (0,-0.2)), ('PLoLip', (0,0.2)), ('PJaw', (0,0.05)), ('PTongue', (0,0.0))], 'SH' : [('PMouth', (-0.6,0)), ('PUpLip', (0,-0.5)), ('PLoLip', (0,0.5)), ('PJaw', (0,0)), ('PTongue', (0,0.0))], 'EE' : [('PMouth', (0.3,0)), ('PUpLip', (0,-0.3)), ('PLoLip', (0,0.3)), ('PJaw', (0,0.025)), ('PTongue', (0,0.0))], 'AH' : [('PMouth', (-0.1,0)), ('PUpLip', (0,-0.4)), ('PLoLip', (0,0)), ('PJaw', (0,0.35)), ('PTongue', (0,0.0))], 'EH' : [('PMouth', (0.1,0)), ('PUpLip', (0,-0.2)), ('PLoLip', (0,0.2)), ('PJaw', (0,0.2)), ('PTongue', (0,0.0))], 'TH' : [('PMouth', (0,0)), ('PUpLip', (0,-0.5)), ('PLoLip', (0,0.5)), ('PJaw', (-0.2,0.1)), ('PTongue', (0,-0.6))], 'L' : [('PMouth', (0,0)), ('PUpLip', (0,-0.2)), ('PLoLip', (0,0.2)), ('PJaw', (0.2,0.2)), ('PTongue', (0,-0.8))], 'G' : [('PMouth', (0,0)), ('PUpLip', (0,-0.1)), ('PLoLip', (0,0.1)), ('PJaw', (-0.3,0.1)), ('PTongue', (0,-0.6))], 'Blink' : [('PUpLid', (0,1.0)), ('PLoLid', (0,-1.0))], 'Unblink' : [('PUpLid', (0,0)), ('PLoLid', (0,0))], }) bodyLanguageVisemes = ({ 'Rest' : [ ('MouthWidth_L', 0), ('MouthWidth_R', 0), ('MouthNarrow_L', 0), ('MouthNarrow_R', 0), ('LipsPart', 0.6), ('UpLipsMidHeight', 0), ('LoLipsMidHeight', 0), ('LoLipsIn', 0), ('MouthOpen', 0), ('TongueBackHeight', 0), ('TongueHeight', 0), ], 'Etc' : [ ('MouthWidth_L', 0), ('MouthWidth_R', 0), ('MouthNarrow_L', 0), ('MouthNarrow_R', 0), ('LipsPart', 0.4), ('UpLipsMidHeight', 0), ('LoLipsMidHeight', 0), ('LoLipsIn', 0), ('MouthOpen', 0), ('TongueBackHeight', 0), ('TongueHeight', 0), ], 'MBP' : [ ('MouthWidth_L', 0), ('MouthWidth_R', 0), ('MouthNarrow_L', 0), ('MouthNarrow_R', 0), ('LipsPart', 0), ('UpLipsMidHeight', 0), ('LoLipsMidHeight', 0), ('LoLipsIn', 0), ('MouthOpen', 0), ('TongueBackHeight', 0), ('TongueHeight', 0), ], 'OO' : [ ('MouthWidth_L', 0), ('MouthWidth_R', 0), ('MouthNarrow_L', 1.0), ('MouthNarrow_R', 1.0), ('LipsPart', 0), ('UpLipsMidHeight', 0), ('LoLipsMidHeight', 0), ('LoLipsIn', 0), ('MouthOpen', 0.4), ('TongueBackHeight', 0), ('TongueHeight', 0), ], 'O' : [ ('MouthWidth_L', 0), ('MouthWidth_R', 0), ('MouthNarrow_L', 0.9), ('MouthNarrow_R', 0.9), ('LipsPart', 0), ('UpLipsMidHeight', 0), ('LoLipsMidHeight', 0), ('LoLipsIn', 0), ('MouthOpen', 0.8), ('TongueBackHeight', 0), ('TongueHeight', 0), ], 'R' : [ ('MouthWidth_L', 0), ('MouthWidth_R', 0), ('MouthNarrow_L', 0.5), ('MouthNarrow_R', 0.5), ('LipsPart', 0), ('UpLipsMidHeight', 0.2), ('LoLipsMidHeight', -0.2), ('LoLipsIn', 0), ('MouthOpen', 0), ('TongueBackHeight', 0), ('TongueHeight', 0), ], 'FV' : [ ('MouthWidth_L', 0.2), ('MouthWidth_R', 0.2), ('MouthNarrow_L', 0), ('MouthNarrow_R', 0), ('LipsPart', 1.0), ('UpLipsMidHeight', 0), ('LoLipsMidHeight', 0.3), ('LoLipsIn', 0.6), ('MouthOpen', 0), ('TongueBackHeight', 0), ('TongueHeight', 0), ], 'S' : [ ('MouthWidth_L', 0), ('MouthWidth_R', 0), ('MouthNarrow_L', 0), ('MouthNarrow_R', 0), ('LipsPart', 0), ('UpLipsMidHeight', 0.5), ('LoLipsMidHeight', -0.7), ('LoLipsIn', 0), ('MouthOpen', 0), ('TongueBackHeight', 0), ('TongueHeight', 0), ], 'SH' : [ ('MouthWidth_L', 0.8), ('MouthWidth_R', 0.8), ('MouthNarrow_L', 0), ('MouthNarrow_R', 0), ('LipsPart', 0), ('UpLipsMidHeight', 1.0), ('LoLipsMidHeight', 0), ('LoLipsIn', 0), ('MouthOpen', 0), ('TongueBackHeight', 0), ('TongueHeight', 0), ], 'EE' : [ ('MouthWidth_L', 0.2), ('MouthWidth_R', 0.2), ('MouthNarrow_L', 0), ('MouthNarrow_R', 0), ('LipsPart', 0), ('UpLipsMidHeight', 0.6), ('LoLipsMidHeight', -0.6), ('LoLipsIn', 0), ('MouthOpen', 0.5), ('TongueBackHeight', 0), ('TongueHeight', 0), ], 'AH' : [ ('MouthWidth_L', 0), ('MouthWidth_R', 0), ('MouthNarrow_L', 0), ('MouthNarrow_R', 0), ('LipsPart', 0), ('UpLipsMidHeight', 0.4), ('LoLipsMidHeight', 0), ('LoLipsIn', 0), ('MouthOpen', 0.7), ('TongueBackHeight', 0), ('TongueHeight', 0), ], 'EH' : [ ('MouthWidth_L', 0), ('MouthWidth_R', 0), ('MouthNarrow_L', 0), ('MouthNarrow_R', 0), ('LipsPart', 0), ('UpLipsMidHeight', 0.5), ('LoLipsMidHeight', -0.6), ('LoLipsIn', 0), ('MouthOpen', 0.25), ('TongueBackHeight', 0), ('TongueHeight', 0), ], 'TH' : [ ('MouthWidth_L', 0), ('MouthWidth_R', 0), ('MouthNarrow_L', 0), ('MouthNarrow_R', 0), ('LipsPart', 0), ('UpLipsMidHeight', 0), ('LoLipsMidHeight', 0), ('LoLipsIn', 0), ('MouthOpen', 0.2), ('TongueBackHeight', 1.0), ('TongueHeight', 1.0) ], 'L' : [ ('MouthWidth_L', 0), ('MouthWidth_R', 0), ('MouthNarrow_L', 0), ('MouthNarrow_R', 0), ('LipsPart', 0), ('UpLipsMidHeight', 0.5), ('LoLipsMidHeight', -0.5), ('LoLipsIn', 0), ('MouthOpen', -0.2), ('TongueBackHeight', 1.0), ('TongueHeight', 1.0), ], 'G' : [ ('MouthWidth_L', 0), ('MouthWidth_R', 0), ('MouthNarrow_L', 0), ('MouthNarrow_R', 0), ('LipsPart', 0), ('UpLipsMidHeight', 0.5), ('LoLipsMidHeight', -0.5), ('LoLipsIn', 0), ('MouthOpen', -0.2), ('TongueBackHeight', 1.0), ('TongueHeight', 0), ], 'Blink' : [ ('UpLidUp_L', 1), ('UpLidUp_R', 1), ('LoLidDown_L', 1), ('LoLidDown_R', 1) ], 'Unblink' : [ ('UpLidUp_L', 0), ('UpLidUp_R', 0), ('LoLidDown_L', 0), ('LoLidDown_R', 0) ], }) VisemePanelBones = { 'MouthOpen' : ('PJaw', (0,0.25)), 'UpLipsMidHeight' : ('PUpLipMid', (0,-0.25)), 'LoLipsMidHeight' : ('PLoLipMid', (0,-0.25)), 'LoLipsIn': ('PLoLipMid', (-0.25,0)), 'MouthWidth_L' : ('PMouth_L', (0.25,0)), 'MouthWidth_R' : ('PMouth_R', (-0.25,0)), 'MouthNarrow_L' : ('PMouth_L', (-0.25,0)), 'MouthNarrow_R' : ('PMouth_R', (0.25,0)), 'LipsPart' : ('PMouthMid', (0, -0.25)), 'TongueBackHeight': ('PTongue', (-0.25, 0)), 'TongueHeight' : ('PTongue', (0, -0.25)), 'UpLidUp_L' : ('PUpLid_L', (0,1.0)), 'UpLidUp_R' : ('PUpLid_R', (0,1.0)), 'LoLidDown_L' : ('PLoLid_L', (0,-1.0)), 'LoLidDown_R' : ('PLoLid_R', (0,-1.0)), } VisemeList = [ ('Rest', 'Etc', 'AH'), ('MBP', 'OO', 'O'), ('R', 'FV', 'S'), ('SH', 'EE', 'EH'), ('TH', 'L', 'G') ] # # makeVisemes(ob, scn): # class VIEW3D_OT_MhxMakeVisemesButton(bpy.types.Operator): # def makeVisemes(ob, scn): if ob.type != 'MESH': print("Active object %s is not a mesh" % ob) return if not ob.data.shape_keys: print("%s has no shapekeys" % ob) return try: ob.data.shape_keys.key_blocks["VIS_Rest"] print("Visemes already created") return except: pass verts = ob.data.vertices for (vis,shapes) in bodyLanguageVisemes.items(): if vis in ['Blink', 'Unblink']: continue vkey = ob.shape_key_add(name="VIS_%s" % vis) print(vkey.name) for n,v in enumerate(verts): vkey.data[n].co = v.co for (name,value) in shapes: if name[-2:] == "_R": continue skey = ob.data.shape_keys.key_blocks[name] factor = 0.75*value for n,v in enumerate(verts): vkey.data[n].co += factor*(skey.data[n].co - v.co) print("Visemes made") return class VIEW3D_OT_MhxMakeVisemesButton(bpy.types.Operator): bl_idname = "mhx.make_visemes" bl_label = "Generate viseme shapekeys" bl_options = {'UNDO'} def execute(self, context): makeVisemes(context.object, context.scene) return{'FINISHED'} """ # # mohoVisemes # magpieVisemes # MohoVisemes = dict({ 'rest' : 'Rest', 'etc' : 'Etc', 'AI' : 'AH', 'O' : 'O', 'U' : 'OO', 'WQ' : 'AH', 'L' : 'L', 'E' : 'EH', 'MBP' : 'MBP', 'FV' : 'FV', }) MagpieVisemes = dict({ "CONS" : "Etc", "AI" : 'AH', "E" : "EH", "O" : "O", "UW" : "AH", "MBP" : "MBP", "L" : "L", "FV" : "FV", "Sh" : "SH", }) # # setViseme(context, vis, setKey, frame): # setBoneLocation(context, pbone, loc, mirror, setKey, frame): # class VIEW3D_OT_MhxVisemeButton(bpy.types.Operator): # """ def getVisemeSet(context, rig): try: visset = rig['MhxVisemeSet'] except: return bodyLanguageVisemes if visset == 'StopStaring': return stopStaringVisemes elif visset == 'BodyLanguage': return bodyLanguageVisemes else: raise MhxError("Unknown viseme set %s" % visset) def setVisemeAlpha7(context, vis, visemes, setKey, frame): (rig, mesh) = getMhxRigMesh(context.object) isPanel = False isProp = False shapekeys = None scale = 0.75 if rig.MhxShapekeyDrivers: try: scale *= rig.pose.bones['PFace'].bone.length isPanel = True except: isProp = True elif mesh: shapekeys = mesh.data.shape_keys.key_blocks for (skey, value) in visemes[vis]: if isPanel: (b, (x,z)) = VisemePanelBones[skey] loc = mathutils.Vector((float(x*value),0,float(z*value))) pb = rig.pose.bones[b] pb.location = loc*scale if setKey or context.tool_settings.use_keyframe_insert_auto: for n in range(3): pb.keyframe_insert('location', index=n, frame=frame, group=pb.name) elif isProp: skey = 'Mhf' + skey try: prop = rig[skey] except: continue rig[skey] = value*scale if setKey or context.tool_settings.use_keyframe_insert_auto: rig.keyframe_insert('["%s"]' % skey, frame=frame, group="Visemes") elif shapekeys: try: shapekeys[skey].value = value*scale except: continue if setKey or context.tool_settings.use_keyframe_insert_auto: shapekeys[skey].keyframe_insert("value", frame=frame) updatePose(context) return class VIEW3D_OT_MhxVisemeButton(bpy.types.Operator): bl_idname = 'mhx.pose_viseme' bl_label = 'Viseme' bl_options = {'UNDO'} viseme = StringProperty() def invoke(self, context, event): (rig, mesh) = getMhxRigMesh(context.object) visemes = getVisemeSet(context, rig) setVisemeAlpha7(context, self.viseme, visemes, False, context.scene.frame_current) return{'FINISHED'} """ def readLipsync(context, filepath, offs, struct): (rig, mesh) = getMhxRigMesh(context.object) if rig.MhAlpha8: props = getProps(rig, "Mhv") visemes = {} oldKeys = [] for prop in props: dummy,units = getUnitsFromString("x;"+rig[prop]) visemes[prop] = units props = getProps(rig, "Mhsmouth") auto = context.tool_settings.use_keyframe_insert_auto auto = True factor = rig.MhxStrength shapekeys = getMhmShapekeys(rig, mesh) else: visemes = getVisemeSet(context, rig) context.scene.objects.active = rig bpy.ops.object.mode_set(mode='POSE') fp = open(filepath, "rU") for line in fp: words= line.split() if len(words) < 2: continue else: vis = "Mhv" + struct[words[1]] frame = int(words[0])+offs if rig.MhAlpha8: setMhmProps(rig, shapekeys, "Mhsmouth", visemes[vis], factor, auto, frame) else: setVisemeAlpha7(context, vis, visemes, True, frame) fp.close() #setInterpolation(rig) updatePose(context) print("Lipsync file %s loaded" % filepath) class VIEW3D_OT_MhxMohoButton(bpy.types.Operator, ImportHelper): bl_idname = "mhx.pose_load_moho" bl_label = "Load Moho (.dat)" bl_options = {'UNDO'} filename_ext = ".dat" filter_glob = StringProperty(default="*.dat", options={'HIDDEN'}) filepath = StringProperty(subtype='FILE_PATH') def execute(self, context): readLipsync(context, self.properties.filepath, context.scene.frame_start - 1, MohoVisemes) return{'FINISHED'} def invoke(self, context, event): context.window_manager.fileselect_add(self) return {'RUNNING_MODAL'} class MhxLipsyncPanel(bpy.types.Panel): bl_label = "MHX Lipsync" bl_space_type = "VIEW_3D" bl_region_type = "UI" bl_options = {'DEFAULT_CLOSED'} @classmethod def poll(cls, context): return pollMhx(context.object) def draw(self, context): rig,mesh = getMhxRigMesh(context.object) if not rig: layout.label("No MHX rig found") return layout = self.layout if rig.MhAlpha8: visemes = getProps(rig, "Mhv") if not visemes: layout.label("No visemes found") return layout.operator("mhx.pose_reset_expressions", text="Reset visemes").prefix="Mhsmouth" layout.operator("mhx.pose_key_expressions", text="Key visemes").prefix="Mhsmouth" layout.prop(rig, "MhxStrength") layout.separator() n = 0 for prop in visemes: if n % 3 == 0: row = layout.row() n = 0 row.operator("mhx.pose_mhm", text=prop[3:]).data="Mhsmouth;"+rig[prop] n += 1 while n % 3 != 0: row.label("") n += 1 layout.separator() row = layout.row() row.operator("mhx.pose_mhm", text="Blink").data="Mhsmouth;eye_left_closure:1;eye_right_closure:1" row.operator("mhx.pose_mhm", text="Unblink").data="Mhsmouth;eye_left_closure:0;eye_right_closure:0" else: layout.label("Lipsync disabled for alpha7 mhx files") return for (vis1, vis2, vis3) in VisemeList: row = layout.row() row.operator("mhx.pose_viseme", text=vis1).viseme = vis1 row.operator("mhx.pose_viseme", text=vis2).viseme = vis2 row.operator("mhx.pose_viseme", text=vis3).viseme = vis3 layout.separator() row = layout.row() row.operator("mhx.pose_viseme", text="Blink").viseme = 'Blink' row.operator("mhx.pose_viseme", text="Unblink").viseme = 'Unblink' layout.separator() layout.operator("mhx.make_visemes") layout.separator() row = layout.row() row.operator("mhx.pose_load_moho") #layout.operator("mhx.update") # # updatePose(context): # class VIEW3D_OT_MhxUpdateButton(bpy.types.Operator): # def updatePose(context): scn = context.scene scn.frame_current = scn.frame_current bpy.ops.object.posemode_toggle() bpy.ops.object.posemode_toggle() return class VIEW3D_OT_MhxUpdateButton(bpy.types.Operator): bl_idname = "mhx.update" bl_label = "Update" def execute(self, context): updatePose(context) return{'FINISHED'} ################################################################################### # # Expression panels # ################################################################################### class VIEW3D_OT_MhxResetExpressionsButton(bpy.types.Operator): bl_idname = "mhx.pose_reset_expressions" bl_label = "Reset expressions" bl_options = {'UNDO'} prefix = StringProperty() def execute(self, context): rig,mesh = getMhxRigMesh(context.object) shapekeys = getMhmShapekeys(rig, mesh) clearMhmProps(rig, shapekeys, self.prefix, context.tool_settings.use_keyframe_insert_auto, context.scene.frame_current) updatePose(context) return{'FINISHED'} class VIEW3D_OT_MhxKeyExpressionsButton(bpy.types.Operator): bl_idname = "mhx.pose_key_expressions" bl_label = "Key expressions" bl_options = {'UNDO'} prefix = StringProperty() def execute(self, context): rig,mesh = getMhxRigMesh(context.object) props = getProps(rig, self.prefix) frame = context.scene.frame_current for prop in props: rig.keyframe_insert(prop, frame=frame) updatePose(context) return{'FINISHED'} class VIEW3D_OT_MhxPinExpressionButton(bpy.types.Operator): bl_idname = "mhx.pose_pin_expression" bl_label = "Pin" bl_options = {'UNDO'} data = StringProperty() def execute(self, context): rig,mesh = getMhxRigMesh(context.object) words = self.data.split(";") prefix = words[0] expression = words[1] props = getProps(rig, prefix) if context.tool_settings.use_keyframe_insert_auto: frame = context.scene.frame_current for prop in props: old = rig[prop] if prop == expression: rig[prop] = 1.0 else: rig[prop] = 0.0 if abs(rig[prop] - old) > 1e-3: rig.keyframe_insert(prop, frame=frame) else: for prop in props: if prop == expression: rig[prop] = 1.0 else: rig[prop] = 0.0 updatePose(context) return{'FINISHED'} def getMhmShapekeys(rig, mesh): if rig.MhxShapekeyDrivers: return None else: return mesh.data.shape_keys.key_blocks def setMhmProps(rig, shapekeys, prefix, units, factor, auto, frame): clearMhmProps(rig, shapekeys, prefix, auto, frame) for (prop, value) in units: if shapekeys: skey = prop[3:].replace("_","-") shapekeys[skey].value = factor*value if auto: shapekeys[skey].keyframe_insert("value", frame=frame) else: rig[prop] = factor*value if auto: rig.keyframe_insert(prop, frame=frame) def clearMhmProps(rig, shapekeys, prefix, auto, frame): props = getProps(rig, prefix) for prop in props: if shapekeys: skey = prop[3:].replace("_","-") shapekeys[skey].value = 0.0 if auto: shapekeys[skey].keyframe_insert("value", frame=frame) else: rig[prop] = 0.0 if auto: rig.keyframe_insert(prop, frame=frame) def getUnitsFromString(string): words = string.split(";") prefix = words[0] units = [] for word in words[1:]: if word == "": continue unit = word.split(":") prop = "Mhs" + unit[0] value = float(unit[1]) units.append((prop, value)) return prefix,units class VIEW3D_OT_MhxMhmButton(bpy.types.Operator): bl_idname = "mhx.pose_mhm" bl_label = "Mhm" bl_options = {'UNDO'} data = StringProperty() def execute(self, context): rig,mesh = getMhxRigMesh(context.object) auto = context.tool_settings.use_keyframe_insert_auto frame = context.scene.frame_current shapekeys = getMhmShapekeys(rig, mesh) prefix,units = getUnitsFromString(self.data) setMhmProps(rig, shapekeys, prefix, units, rig.MhxStrength, auto, frame) updatePose(context) return{'FINISHED'} def getProps(rig, prefix): props = [] for prop in rig.keys(): if prop.startswith(prefix): props.append(prop) props.sort() return props class MhxExpressionsPanel(bpy.types.Panel): bl_label = "MHX Expressions" bl_space_type = "VIEW_3D" bl_region_type = "UI" bl_options = {'DEFAULT_CLOSED'} @classmethod def poll(cls, context): return pollMhx(context.object) def draw(self, context): layout = self.layout rig,mesh = getMhxRigMesh(context.object) if not rig: layout.label("No MHX rig found") return exprs = getProps(rig, "Mhe") if not exprs: layout.label("No expressions found") return layout.operator("mhx.pose_reset_expressions").prefix="Mhs" layout.operator("mhx.pose_key_expressions").prefix="Mhs" layout.prop(rig, "MhxStrength") layout.separator() for prop in exprs: layout.operator("mhx.pose_mhm", text=prop[3:]).data="Mhs;"+rig[prop] def drawShapePanel(self, context, prefix, name): layout = self.layout rig,mesh = getMhxRigMesh(context.object) if not rig: print("No MHX rig found") return if not rig.MhxShapekeyDrivers: layout.label("No shapekey drivers.") layout.label("Set %s values in mesh context instead" % name) return props = getProps(rig, prefix) if not props: layout.label("No %ss found" % name) return layout.operator("mhx.pose_reset_expressions", text="Reset %ss" % name).prefix=prefix layout.operator("mhx.pose_key_expressions", text="Reset %ss" % name).prefix=prefix #layout.operator("mhx.update") layout.separator() for prop in props: row = layout.split(0.85) row.prop(rig, '["%s"]' % prop, text=prop[3:]) row.operator("mhx.pose_pin_expression", text="", icon='UNPINNED').data = (prefix + ";" + prop) return class MhxExpressionUnitsPanel(bpy.types.Panel): bl_label = "MHX Expression Units" bl_space_type = "VIEW_3D" bl_region_type = "UI" bl_options = {'DEFAULT_CLOSED'} @classmethod def poll(cls, context): return pollMhx(context.object) def draw(self, context): drawShapePanel(self, context, "Mhs", "expression") class MhxCustomShapePanel(bpy.types.Panel): bl_label = "MHX Custom Shapes" bl_space_type = "VIEW_3D" bl_region_type = "UI" bl_options = {'DEFAULT_CLOSED'} @classmethod def poll(cls, context): return pollMhx(context.object) def draw(self, context): drawShapePanel(self, context, "Mhc", "custom shape") ######################################### # # FK-IK snapping. # ######################################### def getPoseMatrix(mat, pb): restInv = pb.bone.matrix_local.inverted() if pb.parent: parInv = pb.parent.matrix.inverted() parRest = pb.parent.bone.matrix_local return restInv * (parRest * (parInv * mat)) else: return restInv * mat def getGlobalMatrix(mat, pb): gmat = pb.bone.matrix_local * mat if pb.parent: parMat = pb.parent.matrix parRest = pb.parent.bone.matrix_local return parMat * (parRest.inverted() * gmat) else: return gmat def matchPoseTranslation(pb, fkPb, auto): mat = getPoseMatrix(fkPb.matrix, pb) insertLocation(pb, mat, auto) def insertLocation(pb, mat, auto): pb.location = mat.to_translation() if auto: pb.keyframe_insert("location", group=pb.name) bpy.ops.object.mode_set(mode='OBJECT') bpy.ops.object.mode_set(mode='POSE') def matchPoseRotation(pb, fkPb, auto): mat = getPoseMatrix(fkPb.matrix, pb) insertRotation(pb, mat, auto) def insertRotation(pb, mat, auto): q = mat.to_quaternion() if pb.rotation_mode == 'QUATERNION': pb.rotation_quaternion = q if auto: pb.keyframe_insert("rotation_quaternion", group=pb.name) else: pb.rotation_euler = q.to_euler(pb.rotation_mode) if auto: pb.keyframe_insert("rotation_euler", group=pb.name) bpy.ops.object.mode_set(mode='OBJECT') bpy.ops.object.mode_set(mode='POSE') def matchPoseReverse(pb, fkPb, auto): bpy.ops.object.mode_set(mode='OBJECT') bpy.ops.object.mode_set(mode='POSE') gmat = fkPb.matrix * Matrix.Rotation(math.pi, 4, 'Z') offs = pb.bone.length * fkPb.matrix.col[1] gmat[0][3] += offs[0] gmat[1][3] += offs[1] gmat[2][3] += offs[2] mat = getPoseMatrix(gmat, pb) pb.matrix_basis = mat insertLocation(pb, mat, auto) insertRotation(pb, mat, auto) def matchPoseScale(pb, fkPb, auto): mat = getPoseMatrix(fkPb.matrix, pb) pb.scale = mat.to_scale() if auto: pb.keyframe_insert("scale", group=pb.name) bpy.ops.object.mode_set(mode='OBJECT') bpy.ops.object.mode_set(mode='POSE') def fk2ikArm(context, suffix): rig = context.object auto = context.scene.tool_settings.use_keyframe_insert_auto print("Snap FK Arm%s" % suffix) (uparmIk, loarmIk, elbow, elbowPt, wrist) = getSnapBones(rig, "ArmIK", suffix) (uparmFk, loarmFk, elbowPtFk, handFk) = getSnapBones(rig, "ArmFK", suffix) matchPoseRotation(uparmFk, uparmFk, auto) matchPoseScale(uparmFk, uparmFk, auto) matchPoseRotation(loarmFk, loarmFk, auto) matchPoseScale(loarmFk, loarmFk, auto) if rig["MhaHandFollowsWrist" + suffix]: matchPoseRotation(handFk, wrist, auto) matchPoseScale(handFk, wrist, auto) return def ik2fkArm(context, suffix): rig = context.object scn = context.scene auto = scn.tool_settings.use_keyframe_insert_auto print("Snap IK Arm%s" % suffix) (uparmIk, loarmIk, elbow, elbowPt, wrist) = getSnapBones(rig, "ArmIK", suffix) (uparmFk, loarmFk, elbowPtFk, handFk) = getSnapBones(rig, "ArmFK", suffix) #rig["MhaElbowFollowsShoulder" + suffix] = False #rig["MhaElbowFollowsWrist" + suffix] = False matchPoseTranslation(wrist, handFk, auto) matchPoseRotation(wrist, handFk, auto) matchPoseTranslation(elbow, elbowPtFk, auto) matchPoseTranslation(elbowPt, elbowPtFk, auto) setInverse(rig, elbowPt) return def fk2ikLeg(context, suffix): rig = context.object auto = context.scene.tool_settings.use_keyframe_insert_auto print("Snap FK Leg%s" % suffix) (uplegIk, lolegIk, kneePt, ankleIk, legIk, legFk, footIk, toeIk) = getSnapBones(rig, "LegIK", suffix) (uplegFk, lolegFk, kneePtFk, footFk, toeFk) = getSnapBones(rig, "LegFK", suffix) matchPoseRotation(uplegFk, uplegFk, auto) matchPoseScale(uplegFk, uplegFk, auto) matchPoseRotation(lolegFk, lolegFk, auto) matchPoseScale(lolegFk, lolegFk, auto) return def ik2fkLeg(context, suffix): rig = context.object scn = context.scene auto = scn.tool_settings.use_keyframe_insert_auto print("Snap IK Leg%s" % suffix) (uplegIk, lolegIk, kneePt, ankleIk, legIk, legFk, footIk, toeIk) = getSnapBones(rig, "LegIK", suffix) (uplegFk, lolegFk, kneePtFk, footFk, toeFk) = getSnapBones(rig, "LegFK", suffix) #rig["MhaKneeFollowsHip" + suffix] = False #rig["MhaKneeFollowsFoot" + suffix] = False legIkToAnkle = rig["MhaLegIkToAnkle" + suffix] if legIkToAnkle: matchPoseTranslation(ankleIk, footFk, auto) matchPoseTranslation(legIk, legFk, auto) matchPoseRotation(legIk, legFk, auto) matchPoseReverse(toeIk, toeFk, auto) matchPoseReverse(footIk, footFk, auto) setInverse(rig, ankleIk) matchPoseTranslation(kneePt, kneePtFk, auto) setInverse(rig, kneePt) if not legIkToAnkle: matchPoseTranslation(ankleIk, footFk, auto) return # # setInverse(rig, pb): # def setInverse(rig, pb): rig.data.bones.active = pb.bone pb.bone.select = True bpy.ops.object.mode_set(mode='OBJECT') bpy.ops.object.mode_set(mode='POSE') for cns in pb.constraints: if cns.type == 'CHILD_OF': bpy.ops.constraint.childof_set_inverse(constraint=cns.name, owner='BONE') bpy.ops.object.mode_set(mode='OBJECT') bpy.ops.object.mode_set(mode='POSE') return """ def clearInverse(rig, pb): rig.data.bones.active = pb.bone pb.bone.select = True bpy.ops.object.mode_set(mode='OBJECT') bpy.ops.object.mode_set(mode='POSE') for cns in pb.constraints: if cns.type == 'CHILD_OF': bpy.ops.constraint.childof_clear_inverse(constraint=cns.name, owner='BONE') bpy.ops.object.mode_set(mode='OBJECT') bpy.ops.object.mode_set(mode='POSE') return def fixAnkle(rig, suffix, scn): layers = list(rig.data.layers) try: rig.data.layers = 32*[True] setInverse(rig, rig.pose.bones["Ankle" + suffix]) scn.frame_current = scn.frame_current finally: rig.data.layers = layers return def clearAnkle(rig, suffix, scn): layers = list(rig.data.layers) try: rig.data.layers = 32*[True] clearInverse(rig, rig.pose.bones["Ankle" + suffix]) scn.frame_current = scn.frame_current finally: rig.data.layers = layers return class VIEW3D_OT_FixAnkleButton(bpy.types.Operator): bl_idname = "mhx.fix_ankle" bl_label = "Fix ankle" bl_description = "Set inverse for ankle Child-of constraints" bl_options = {'UNDO'} suffix = StringProperty() def execute(self, context): fixAnkle(context.object, self.suffix, context.scene) return{'FINISHED'} class VIEW3D_OT_ClearAnkleButton(bpy.types.Operator): bl_idname = "mhx.clear_ankle" bl_label = "Clear ankle" bl_description = "Clear inverse for ankle Child-of constraints" bl_options = {'UNDO'} suffix = StringProperty() def execute(self, context): clearAnkle(context.object, self.suffix, context.scene) return{'FINISHED'} """ # # # SnapBones = { "ArmFK" : ["UpArm", "LoArm", "ElbowPTFK", "Hand"], "ArmIK" : ["UpArmIK", "LoArmIK", "Elbow", "ElbowPT", "Wrist"], "LegFK" : ["UpLeg", "LoLeg", "KneePTFK", "Foot", "Toe"], "LegIK" : ["UpLegIK", "LoLegIK", "KneePT", "Ankle", "LegIK", "LegFK", "FootRev", "ToeRev"], } def getSnapBones(rig, key, suffix): names = SnapBones[key] pbones = [] for name in names: pb = rig.pose.bones[name+suffix] pbones.append(pb) return tuple(pbones) class VIEW3D_OT_MhxSnapFk2IkButton(bpy.types.Operator): bl_idname = "mhx.snap_fk_ik" bl_label = "Snap FK" bl_options = {'UNDO'} data = StringProperty() def execute(self, context): bpy.ops.object.mode_set(mode='POSE') rig = context.object (prop, old) = setSnapProp(rig, self.data, 1.0, context, False) if prop[:6] == "MhaArm": fk2ikArm(context, prop[-2:]) elif prop[:6] == "MhaLeg": fk2ikLeg(context, prop[-2:]) restoreSnapProp(rig, prop, old, context) return{'FINISHED'} class VIEW3D_OT_MhxSnapIk2FkButton(bpy.types.Operator): bl_idname = "mhx.snap_ik_fk" bl_label = "Snap IK" bl_options = {'UNDO'} data = StringProperty() def execute(self, context): bpy.ops.object.mode_set(mode='POSE') rig = context.object (prop, old) = setSnapProp(rig, self.data, 0.0, context, True) if prop[:6] == "MhaArm": ik2fkArm(context, prop[-2:]) elif prop[:6] == "MhaLeg": ik2fkLeg(context, prop[-2:]) restoreSnapProp(rig, prop, old, context) return{'FINISHED'} def setSnapProp(rig, data, value, context, isIk): words = data.split() prop = words[0] oldValue = rig[prop] rig[prop] = value ik = int(words[1]) fk = int(words[2]) extra = int(words[3]) oldIk = rig.data.layers[ik] oldFk = rig.data.layers[fk] oldExtra = rig.data.layers[extra] rig.data.layers[ik] = True rig.data.layers[fk] = True rig.data.layers[extra] = True updatePose(context) if isIk: oldValue = 1.0 oldIk = True oldFk = False else: oldValue = 0.0 oldIk = False oldFk = True oldExtra = False return (prop, (oldValue, ik, fk, extra, oldIk, oldFk, oldExtra)) def restoreSnapProp(rig, prop, old, context): updatePose(context) (oldValue, ik, fk, extra, oldIk, oldFk, oldExtra) = old rig[prop] = oldValue rig.data.layers[ik] = oldIk rig.data.layers[fk] = oldFk rig.data.layers[extra] = oldExtra return class VIEW3D_OT_MhxToggleFkIkButton(bpy.types.Operator): bl_idname = "mhx.toggle_fk_ik" bl_label = "FK - IK" bl_options = {'UNDO'} toggle = StringProperty() def execute(self, context): words = self.toggle.split() rig = context.object prop = words[0] value = float(words[1]) onLayer = int(words[2]) offLayer = int(words[3]) rig.data.layers[onLayer] = True rig.data.layers[offLayer] = False rig[prop] = value # Don't do autokey - confusing. #if context.tool_settings.use_keyframe_insert_auto: # rig.keyframe_insert('["%s"]' % prop, frame=scn.frame_current) updatePose(context) return{'FINISHED'} # # MHX FK/IK Switch panel # class MhxFKIKPanel(bpy.types.Panel): bl_label = "MHX FK/IK Switch" bl_space_type = "VIEW_3D" bl_region_type = "UI" #bl_options = {'DEFAULT_CLOSED'} @classmethod def poll(cls, context): return (context.object and context.object.MhxRig == 'MHX') def draw(self, context): rig = context.object layout = self.layout row = layout.row() row.label("") row.label("Left") row.label("Right") layout.label("FK/IK switch") row = layout.row() row.label("Arm") self.toggleButton(row, rig, "MhaArmIk_L", " 3", " 2") self.toggleButton(row, rig, "MhaArmIk_R", " 19", " 18") row = layout.row() row.label("Leg") self.toggleButton(row, rig, "MhaLegIk_L", " 5", " 4") self.toggleButton(row, rig, "MhaLegIk_R", " 21", " 20") try: ok = (rig["MhxVersion"] >= 12) except: ok = False if not ok: layout.label("Snapping only works with MHX version 1.12 and later.") return layout.label("Snap Arm bones") row = layout.row() row.label("FK Arm") row.operator("mhx.snap_fk_ik", text="Snap L FK Arm").data = "MhaArmIk_L 2 3 12" row.operator("mhx.snap_fk_ik", text="Snap R FK Arm").data = "MhaArmIk_R 18 19 28" row = layout.row() row.label("IK Arm") row.operator("mhx.snap_ik_fk", text="Snap L IK Arm").data = "MhaArmIk_L 2 3 12" row.operator("mhx.snap_ik_fk", text="Snap R IK Arm").data = "MhaArmIk_R 18 19 28" layout.label("Snap Leg bones") row = layout.row() row.label("FK Leg") row.operator("mhx.snap_fk_ik", text="Snap L FK Leg").data = "MhaLegIk_L 4 5 12" row.operator("mhx.snap_fk_ik", text="Snap R FK Leg").data = "MhaLegIk_R 20 21 28" row = layout.row() row.label("IK Leg") row.operator("mhx.snap_ik_fk", text="Snap L IK Leg").data = "MhaLegIk_L 4 5 12" row.operator("mhx.snap_ik_fk", text="Snap R IK Leg").data = "MhaLegIk_R 20 21 28" """ row = layout.row() row.label("Ankle") row.operator("mhx.fix_ankle", text="Fix L Ankle").suffix = "_L" row.operator("mhx.fix_ankle", text="Fix R Ankle").suffix = "_R" row = layout.row() row.label("") row.operator("mhx.clear_ankle", text="Clear L Ankle").suffix = "_L" row.operator("mhx.clear_ankle", text="Clear R Ankle").suffix = "_R" """ def toggleButton(self, row, rig, prop, fk, ik): if rig[prop] > 0.5: row.operator("mhx.toggle_fk_ik", text="IK").toggle = prop + " 0" + fk + ik else: row.operator("mhx.toggle_fk_ik", text="FK").toggle = prop + " 1" + ik + fk ################################################################################### # # Posing panel # ################################################################################### # # class MhxDriversPanel(bpy.types.Panel): # class MhxDriversPanel(bpy.types.Panel): bl_label = "MHX Drivers" bl_space_type = "VIEW_3D" bl_region_type = "UI" bl_options = {'DEFAULT_CLOSED'} @classmethod def poll(cls, context): return (context.object and context.object.MhxRig) def draw(self, context): lrProps = [] props = [] lrFaceProps = [] faceProps = [] plist = list(context.object.keys()) plist.sort() for prop in plist: if prop[0:3] == 'Mha': if prop[-2:] == '_L': lrProps.append(prop[:-2]) elif prop[-2:] != '_R': props.append(prop) elif prop[0:3] == 'Mhf': if prop[-2:] == '_L': lrFaceProps.append(prop[:-2]) elif prop[-2:] != '_R': faceProps.append(prop) ob = context.object layout = self.layout for prop in props: layout.prop(ob, '["%s"]' % prop, text=prop[3:]) layout.separator() row = layout.row() row.label("Left") row.label("Right") for prop in lrProps: row = layout.row() row.prop(ob, '["%s"]' % (prop+"_L"), text=prop[3:]) row.prop(ob, '["%s"]' % (prop+"_R"), text=prop[3:]) if faceProps: layout.separator() layout.label("Face shapes") for prop in faceProps: layout.prop(ob, '["%s"]' % prop, text=prop[3:]) layout.separator() row = layout.row() row.label("Left") row.label("Right") for prop in lrFaceProps: row = layout.row() row.prop(ob, '["%s"]' % (prop+"_L"), text=prop[3:]) row.prop(ob, '["%s"]' % (prop+"_R"), text=prop[3:]) return ################################################################################### # # Visibility panel # ################################################################################### # # class MhxVisibilityPanel(bpy.types.Panel): # class MhxVisibilityPanel(bpy.types.Panel): bl_label = "MHX Visibility" bl_space_type = "VIEW_3D" bl_region_type = "UI" bl_options = {'DEFAULT_CLOSED'} @classmethod def poll(cls, context): return (context.object and context.object.MhxRig) def draw(self, context): ob = context.object layout = self.layout props = list(ob.keys()) props.sort() for prop in props: if prop[0:3] == "Mhh": layout.prop(ob, '["%s"]' % prop, text="Hide %s" % prop[3:]) layout.separator() layout.operator("mhx.update_textures") layout.separator() layout.operator("mhx.add_hiders") layout.operator("mhx.remove_hiders") return class VIEW3D_OT_MhxUpdateTexturesButton(bpy.types.Operator): bl_idname = "mhx.update_textures" bl_label = "Update" bl_options = {'UNDO'} def execute(self, context): scn = context.scene for mat in bpy.data.materials: if mat.animation_data: try: mat["MhxDriven"] except: continue for driver in mat.animation_data.drivers: prop = mat.path_resolve(driver.data_path) value = driver.evaluate(scn.frame_current) #print("Update %s[%d] = %s" % (driver.data_path, driver.array_index, value)) prop[driver.array_index] = value return{'FINISHED'} class VIEW3D_OT_MhxAddHidersButton(bpy.types.Operator): bl_idname = "mhx.add_hiders" bl_label = "Add Hide Property" bl_options = {'UNDO'} def execute(self, context): rig = context.object for ob in context.scene.objects: if ob.select and ob != rig: prop = "Mhh%s" % ob.name defNewProp(prop, "Bool", "default=False") rig[prop] = False addHider(ob, "hide", rig, prop) addHider(ob, "hide_render", rig, prop) return{'FINISHED'} def addHider(ob, attr, rig, prop): fcu = ob.driver_add(attr) drv = fcu.driver drv.type = 'SCRIPTED' drv.expression = "x" drv.show_debug_info = True var = drv.variables.new() var.name = "x" targ = var.targets[0] targ.id = rig targ.data_path = '["%s"]' % prop return class VIEW3D_OT_MhxRemoveHidersButton(bpy.types.Operator): bl_idname = "mhx.remove_hiders" bl_label = "Remove Hide Property" bl_options = {'UNDO'} def execute(self, context): rig = context.object for ob in context.scene.objects: if ob.select and ob != rig: ob.driver_remove("hide") ob.driver_remove("hide_render") del rig["Mhh%s" % ob.name] return{'FINISHED'} ################################################################################### # # Layers panel # ################################################################################### MhxLayers = [ (( 0, 'Root', 'MhxRoot'), ( 8, 'Face', 'MhxFace')), (( 9, 'Tweak', 'MhxTweak'), (10, 'Head', 'MhxHead')), (( 1, 'FK Spine', 'MhxFKSpine'), (17, 'IK Spine', 'MhxIKSpine')), ((13, 'Inv FK Spine', 'MhxInvFKSpine'), (16, 'Clothes', 'MhxClothes')), ('Left', 'Right'), (( 2, 'IK Arm', 'MhxIKArm'), (18, 'IK Arm', 'MhxIKArm')), (( 3, 'FK Arm', 'MhxFKArm'), (19, 'FK Arm', 'MhxFKArm')), (( 4, 'IK Leg', 'MhxIKLeg'), (20, 'IK Leg', 'MhxIKLeg')), (( 5, 'FK Leg', 'MhxFKLeg'), (21, 'FK Leg', 'MhxFKLeg')), ((12, 'Extra', 'MhxExtra'), (28, 'Extra', 'MhxExtra')), (( 6, 'Fingers', 'MhxFingers'), (22, 'Fingers', 'MhxFingers')), (( 7, 'Links', 'MhxLinks'), (23, 'Links', 'MhxLinks')), ((11, 'Palm', 'MhxPalm'), (27, 'Palm', 'MhxPalm')), ] # # class MhxLayersPanel(bpy.types.Panel): # class MhxLayersPanel(bpy.types.Panel): bl_label = "MHX Layers" bl_space_type = "VIEW_3D" bl_region_type = "UI" #bl_options = {'DEFAULT_CLOSED'} @classmethod def poll(cls, context): ob = context.object if (ob and ob.MhxRig == 'MHX'): return True return False def draw(self, context): layout = self.layout layout.operator("mhx.pose_enable_all_layers") layout.operator("mhx.pose_disable_all_layers") amt = context.object.data for (left,right) in MhxLayers: row = layout.row() if type(left) == str: row.label(left) row.label(right) else: for (n, name, prop) in [left,right]: row.prop(amt, "layers", index=n, toggle=True, text=name) return class VIEW3D_OT_MhxEnableAllLayersButton(bpy.types.Operator): bl_idname = "mhx.pose_enable_all_layers" bl_label = "Enable all layers" bl_options = {'UNDO'} def execute(self, context): rig,mesh = getMhxRigMesh(context.object) for (left,right) in MhxLayers: if type(left) != str: for (n, name, prop) in [left,right]: rig.data.layers[n] = True return{'FINISHED'} class VIEW3D_OT_MhxDisableAllLayersButton(bpy.types.Operator): bl_idname = "mhx.pose_disable_all_layers" bl_label = "Disable all layers" bl_options = {'UNDO'} def execute(self, context): rig,mesh = getMhxRigMesh(context.object) layers = 32*[False] pb = context.active_pose_bone if pb: for n in range(32): if pb.bone.layers[n]: layers[n] = True break else: layers[0] = True rig.data.layers = layers return{'FINISHED'} ################################################################################### # # Common functions # ################################################################################### # # getMhxRigMesh(ob): # def pollMhx(ob): if not ob: return False elif ob.type == 'ARMATURE': return ob.MhxRig elif ob.type == 'MESH': par = ob.parent return (par and (par.type == 'ARMATURE') and par.MhxRig) else: return False def getMhxRigMesh(ob): if ob.type == 'ARMATURE': for mesh in ob.children: if mesh.MhxMesh and ob.MhxRig: return (ob, mesh) return (ob, None) elif ob.type == 'MESH': par = ob.parent if (par and par.type == 'ARMATURE' and par.MhxRig): if ob.MhxMesh: return (par, ob) else: return (par, None) else: return (None, None) return (None, None) # # setInterpolation(rig): # def setInterpolation(rig): if not rig.animation_data: return act = rig.animation_data.action if not act: return for fcu in act.fcurves: for pt in fcu.keyframe_points: pt.interpolation = 'LINEAR' fcu.extrapolation = 'CONSTANT' return ################################################################################### # # initialize and register # ################################################################################### def menu_func(self, context): self.layout.operator(ImportMhx.bl_idname, text="MakeHuman (.mhx)...") def register(): bpy.types.Object.MhAlpha8 = BoolProperty(default=True) bpy.types.Object.MhxMesh = BoolProperty(default=False) bpy.types.Object.MhxRig = StringProperty(default="") bpy.types.Object.MhxRigify = BoolProperty(default=False) bpy.types.Object.MhxShapekeyDrivers = BoolProperty(default=True) bpy.types.Object.MhxStrength = FloatProperty( name = "Expression strength", description = "Multiply expression with this factor", default=1.0, min=-1.0, max=2.0 ) bpy.utils.register_module(__name__) bpy.types.INFO_MT_file_import.append(menu_func) def unregister(): try: bpy.utils.unregister_module(__name__) except: pass try: bpy.types.INFO_MT_file_import.remove(menu_func) except: pass if __name__ == "__main__": unregister() register()