blendfile: Python modules shouldn't set their own log level.
[blender-addons.git] / io_import_scene_mhx.py
blob764c36336a863a5418a3d5fcc64529917d5e3e6d
1 # ##### BEGIN GPL LICENSE BLOCK #####
3 # This program is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU General Public License
5 # as published by the Free Software Foundation; either version 2
6 # of the License, or (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
12 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software Foundation,
15 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 # ##### END GPL LICENSE BLOCK #####
19 # Project Name: MakeHuman
20 # Product Home Page: http://www.makehuman.org/
21 # Code Home Page: http://code.google.com/p/makehuman/
22 # Authors: Thomas Larsson
23 # Script copyright (C) MakeHuman Team 2001-2013
24 # Coding Standards: See http://www.makehuman.org/node/165
26 """
27 Abstract
28 MHX (MakeHuman eXchange format) importer for Blender 2.6x.
30 This script should be distributed with Blender.
31 If not, place it in the .blender/scripts/addons dir
32 Activate the script in the "Addons" tab (user preferences).
33 Access from the File > Import menu.
35 Alternatively, run the script in the script editor (Alt-P), and access from the File > Import menu
36 """
38 bl_info = {
39 "name": "Import: MakeHuman (.mhx)",
40 "author": "Thomas Larsson",
41 "version": "1.16.12",
42 "blender": (2, 68, 0),
43 "location": "File > Import > MakeHuman (.mhx)",
44 "description": "Import files in the MakeHuman eXchange format (.mhx)",
45 "warning": "",
46 "wiki_url": "http://www.makehuman.org/documentation",
47 "category": "Import-Export",
50 MAJOR_VERSION = 1
51 MINOR_VERSION = 16
52 FROM_VERSION = 13
53 SUB_VERSION = 12
55 majorVersion = MAJOR_VERSION
56 minorVersion = MINOR_VERSION
62 import bpy
63 import os
64 import time
65 import math
66 import mathutils
67 from mathutils import Vector, Matrix, Quaternion
68 from bpy.props import *
70 MHX249 = False
71 Blender24 = False
72 Blender25 = True
73 theDir = "~/makehuman/exports"
79 theScale = 1.0
80 One = 1.0/theScale
81 useMesh = 1
82 verbosity = 2
83 warnedTextureDir = False
84 warnedVersion = False
86 true = True
87 false = False
88 Epsilon = 1e-6
89 nErrors = 0
90 theTempDatum = None
91 theMessage = ""
92 theMhxFile = ""
95 # toggle flags
98 T_EnforceVersion = 0x01
99 T_Clothes = 0x02
100 T_HardParents = 0x0
101 T_CrashSafe = 0x0
103 T_Diamond = 0x10
104 T_Replace = 0x20
105 T_Shapekeys = 0x40
106 T_ShapeDrivers = 0x80
108 T_Face = T_Shapekeys
109 T_Shape = T_Shapekeys
111 T_Mesh = 0x100
112 T_Armature = 0x200
113 T_Proxy = 0x400
114 T_Cage = 0x800
116 T_Rigify = 0x1000
117 T_Opcns = 0x2000
118 T_Symm = 0x4000
120 DefaultToggle = ( T_EnforceVersion + T_Mesh + T_Armature +
121 T_Shapekeys + T_ShapeDrivers + T_Proxy + T_Clothes + T_Rigify )
123 toggle = DefaultToggle
124 toggleSettings = toggle
125 loadedData = None
128 # mhxEval(expr) - an attempt at a reasonably safe eval.
129 # Note that expr never contains any whitespace due to the behavior
130 # of the mhx tokenizer.
133 def mhxEval(expr, locls={}):
134 globls = {
135 '__builtins__' : {},
136 'toggle' : toggle,
137 'theScale' : theScale,
138 'One' : One,
139 'T_EnforceVersion' : T_EnforceVersion,
140 'T_Clothes' : T_Clothes,
141 'T_HardParents' : T_HardParents,
142 'T_CrashSafe' : T_CrashSafe,
143 'T_Diamond' : T_Diamond,
144 'T_Replace' : T_Replace,
145 'T_Shapekeys' : T_Shapekeys,
146 'T_ShapeDrivers' : T_ShapeDrivers,
147 'T_Face' : T_Face,
148 'T_Shape' : T_Shape,
149 'T_Mesh' : T_Mesh,
150 'T_Armature' : T_Armature,
151 'T_Proxy' : T_Proxy,
152 'T_Cage' : T_Cage,
153 'T_Rigify' : T_Rigify,
154 'T_Opcns' : T_Opcns,
155 'T_Symm' : T_Symm,
157 return eval(expr, globls, locls)
160 # Dictionaries
163 def initLoadedData():
164 global loadedData
166 loadedData = {
167 'NONE' : {},
169 'Object' : {},
170 'Mesh' : {},
171 'Armature' : {},
172 'Lamp' : {},
173 'Camera' : {},
174 'Lattice' : {},
175 'Curve' : {},
176 'Text' : {},
178 'Material' : {},
179 'Image' : {},
180 'MaterialTextureSlot' : {},
181 'Texture' : {},
183 'Bone' : {},
184 'BoneGroup' : {},
185 'Rigify' : {},
187 'Action' : {},
188 'Group' : {},
190 'MeshTextureFaceLayer' : {},
191 'MeshColorLayer' : {},
192 'VertexGroup' : {},
193 'ShapeKey' : {},
194 'ParticleSystem' : {},
196 'ObjectConstraints' : {},
197 'ObjectModifiers' : {},
198 'MaterialSlot' : {},
200 return
202 def reinitGlobalData():
203 global loadedData
204 for key in [
205 'MeshTextureFaceLayer', 'MeshColorLayer', 'VertexGroup', 'ShapeKey',
206 'ParticleSystem', 'ObjectConstraints', 'ObjectModifiers', 'MaterialSlot']:
207 loadedData[key] = {}
208 return
210 Plural = {
211 'Object' : 'objects',
212 'Mesh' : 'meshes',
213 'Lattice' : 'lattices',
214 'Curve' : 'curves',
215 'Text' : 'texts',
216 'Group' : 'groups',
217 'Empty' : 'empties',
218 'Armature' : 'armatures',
219 'Bone' : 'bones',
220 'BoneGroup' : 'bone_groups',
221 'Pose' : 'poses',
222 'PoseBone' : 'pose_bones',
223 'Material' : 'materials',
224 'Texture' : 'textures',
225 'Image' : 'images',
226 'Camera' : 'cameras',
227 'Lamp' : 'lamps',
228 'World' : 'worlds',
232 # readMhxFile(filePath):
235 def readMhxFile(filePath):
236 global nErrors, theScale, theArmature, defaultScale, One
237 global toggle, warnedVersion, theMessage, alpha7, theDir
239 defaultScale = theScale
240 One = 1.0/theScale
241 theArmature = None
242 alpha7 = False
243 warnedVersion = False
244 initLoadedData()
245 theMessage = ""
247 theDir = os.path.dirname(filePath)
248 fileName = os.path.expanduser(filePath)
249 _,ext = os.path.splitext(fileName)
250 if ext.lower() != ".mhx":
251 print("Error: Not a mhx file: %s" % fileName.encode('utf-8', 'strict'))
252 return
253 print( "Opening MHX file %s " % fileName.encode('utf-8', 'strict') )
254 print("Toggle %x" % toggle)
255 time1 = time.clock()
257 # ignore = False # UNUSED
258 stack = []
259 tokens = []
260 key = "toplevel"
261 level = 0
262 nErrors = 0
263 comment = 0
264 nesting = 0
266 file= open(fileName, "rU")
267 print( "Tokenizing" )
268 lineNo = 0
269 for line in file:
270 # print(line)
271 lineSplit= line.split()
272 lineNo += 1
273 if len(lineSplit) == 0:
274 pass
275 elif lineSplit[0][0] == '#':
276 if lineSplit[0] == '#if':
277 if comment == nesting:
278 try:
279 res = mhxEval(lineSplit[1])
280 except:
281 res = False
282 if res:
283 comment += 1
284 nesting += 1
285 elif lineSplit[0] == '#else':
286 if comment == nesting-1:
287 comment += 1
288 elif comment == nesting:
289 comment -= 1
290 elif lineSplit[0] == '#endif':
291 if comment == nesting:
292 comment -= 1
293 nesting -= 1
294 elif comment < nesting:
295 pass
296 elif lineSplit[0] == 'end':
297 try:
298 sub = tokens
299 tokens = stack.pop()
300 if tokens:
301 tokens[-1][2] = sub
302 level -= 1
303 except:
304 print( "Tokenizer error at or before line %d.\nThe mhx file has been corrupted.\nTry to export it again from MakeHuman." % lineNo )
305 print( line )
306 stack.pop()
307 elif lineSplit[-1] == ';':
308 if lineSplit[0] == '\\':
309 key = lineSplit[1]
310 tokens.append([key,lineSplit[2:-1],[]])
311 else:
312 key = lineSplit[0]
313 tokens.append([key,lineSplit[1:-1],[]])
314 else:
315 key = lineSplit[0]
316 tokens.append([key,lineSplit[1:],[]])
317 stack.append(tokens)
318 level += 1
319 tokens = []
320 file.close()
322 if level != 0:
323 MyError("Tokenizer error (%d).\nThe mhx file has been corrupted.\nTry to export it again from MakeHuman." % level)
324 scn = clearScene()
325 print( "Parsing" )
326 parse(tokens)
328 scn.objects.active = theArmature
329 bpy.ops.object.mode_set(mode='OBJECT')
330 bpy.ops.object.select_all(action='DESELECT')
331 theArmature.select = True
332 bpy.ops.object.mode_set(mode='POSE')
333 theArmature.MhAlpha8 = not alpha7
334 #bpy.ops.wm.properties_edit(data_path="object", property="MhxRig", value=theArmature["MhxRig"])
336 time2 = time.clock()
337 msg = "File %s loaded in %g s" % (fileName, time2-time1)
338 if nErrors:
339 msg += " but there where %d errors. " % (nErrors)
340 print(msg)
341 return
344 # getObject(name, var):
347 def getObject(name, var):
348 try:
349 return loadedData['Object'][name]
350 except:
351 raise MhxError("Bug: object %s not found" % ob)
354 # checkMhxVersion(major, minor):
357 def checkMhxVersion(major, minor):
358 global warnedVersion
359 print("MHX", (major,minor), (MAJOR_VERSION, MINOR_VERSION), warnedVersion)
360 if major != MAJOR_VERSION or minor < FROM_VERSION:
361 if warnedVersion:
362 return
363 else:
364 msg = (
365 "Wrong MHX version\n" +
366 "Expected MHX %d.%02d but the loaded file " % (MAJOR_VERSION, MINOR_VERSION) +
367 "has version MHX %d.%02d\n" % (major, minor))
368 if minor < FROM_VERSION:
369 msg += (
370 "You can disable this error message by deselecting the \n" +
371 "Enforce version option when importing. Better:\n" +
372 "Export the MHX file again with an updated version of MakeHuman.\n" +
373 "The most up-to-date version of MakeHuman is the nightly build.\n")
374 else:
375 msg += (
376 "Download the most recent Blender build from www.graphicall.org. \n" +
377 "The most up-to-date version of the import script is distributed\n" +
378 "with Blender. It can also be downloaded from MakeHuman. \n" +
379 "It is located in the importers/mhx/blender25x \n" +
380 "folder and is called import_scene_mhx.py. \n")
381 if (toggle & T_EnforceVersion or minor > MINOR_VERSION):
382 MyError(msg)
383 else:
384 print(msg)
385 warnedVersion = True
386 return
389 # parse(tokens):
392 ifResult = False
394 def printMHXVersionInfo(versionStr, performVersionCheck = False):
395 versionInfo = dict()
396 val = versionStr.split()
398 majorVersion = int(val[0])
399 minorVersion = int(val[1])
401 for debugVal in val[2:]:
402 debugVal = debugVal.replace("_"," ")
403 dKey, dVal = debugVal.split(':')
404 versionInfo[ dKey.strip() ] = dVal.strip()
406 if 'MHXImporter' in versionInfo:
407 print("MHX importer version: ", versionInfo["MHXImporter"])
408 if performVersionCheck:
409 checkMhxVersion(majorVersion, minorVersion)
410 else:
411 print("MHX: %s.%s" % (majorVersion, minorVersion))
413 for (key, value) in versionInfo.items():
414 if key == "MHXImporter":
415 continue
416 print("%s: %s" % (key, value))
418 def parse(tokens):
419 global MHX249, ifResult, theScale, defaultScale, One
420 global majorVersion, minorVersion
421 versionInfoStr = ""
423 for (key, val, sub) in tokens:
424 data = None
425 if key == 'MHX':
426 importerVerStr = "MHXImporter:_%s" % (bl_info["version"])
427 versionInfoStr = " ".join(val + [importerVerStr])
429 printMHXVersionInfo(versionInfoStr, performVersionCheck = True)
430 elif key == 'MHX249':
431 MHX249 = mhxEval(val[0])
432 print("Blender 2.49 compatibility mode is %s\n" % MHX249)
433 elif MHX249:
434 pass
435 elif key == 'print':
436 msg = concatList(val)
437 print(msg)
438 elif key == 'warn':
439 msg = concatList(val)
440 print(msg)
441 elif key == 'error':
442 msg = concatList(val)
443 MyError(msg)
444 elif key == 'NoScale':
445 if mhxEval(val[0]):
446 theScale = 1.0
447 else:
448 theScale = defaultScale
449 One = 1.0/theScale
450 elif key == "Object":
451 parseObject(val, sub, versionInfoStr)
452 elif key == "Mesh":
453 reinitGlobalData()
454 data = parseMesh(val, sub)
455 elif key == "Armature":
456 data = parseArmature(val, sub)
457 elif key == "Pose":
458 data = parsePose(val, sub)
459 elif key == "Action":
460 data = parseAction(val, sub)
461 elif key == "Material":
462 data = parseMaterial(val, sub)
463 elif key == "Texture":
464 data = parseTexture(val, sub)
465 elif key == "Image":
466 data = parseImage(val, sub)
467 elif key == "Curve":
468 data = parseCurve(val, sub)
469 elif key == "TextCurve":
470 data = parseTextCurve(val, sub)
471 elif key == "Lattice":
472 data = parseLattice(val, sub)
473 elif key == "Group":
474 data = parseGroup(val, sub)
475 elif key == "Lamp":
476 data = parseLamp(val, sub)
477 elif key == "World":
478 data = parseWorld(val, sub)
479 elif key == "Scene":
480 data = parseScene(val, sub)
481 elif key == "DefineProperty":
482 parseDefineProperty(val, sub)
483 elif key == "Process":
484 parseProcess(val, sub)
485 elif key == "PostProcess":
486 postProcess(val)
487 hideLayers(val)
488 elif key == "CorrectRig":
489 correctRig(val)
490 elif key == "Rigify":
491 if toggle & T_Rigify:
492 rigifyMhx(bpy.context)
493 elif key == 'AnimationData':
494 try:
495 ob = loadedData['Object'][val[0]]
496 except:
497 ob = None
498 if ob:
499 bpy.context.scene.objects.active = ob
500 parseAnimationData(ob, val, sub)
501 elif key == 'MaterialAnimationData':
502 try:
503 ob = loadedData['Object'][val[0]]
504 except:
505 ob = None
506 if ob:
507 bpy.context.scene.objects.active = ob
508 mat = ob.data.materials[int(val[2])]
509 parseAnimationData(mat, val, sub)
510 elif key == 'ShapeKeys':
511 try:
512 ob = loadedData['Object'][val[0]]
513 except:
514 MyError("ShapeKeys object %s does not exist" % val[0])
515 if ob:
516 bpy.context.scene.objects.active = ob
517 parseShapeKeys(ob, ob.data, val, sub)
518 else:
519 data = parseDefaultType(key, val, sub)
523 # parseDefaultType(typ, args, tokens):
526 def parseDefaultType(typ, args, tokens):
527 name = args[0]
528 data = None
529 expr = "bpy.data.%s.new('%s')" % (Plural[typ], name)
530 data = mhxEval(expr)
532 bpyType = typ.capitalize()
533 loadedData[bpyType][name] = data
534 if data is None:
535 return None
537 for (key, val, sub) in tokens:
538 defaultKey(key, val, sub, data)
539 return data
542 # concatList(elts)
545 def concatList(elts):
546 string = ""
547 for elt in elts:
548 string += " %s" % elt
549 return string
552 # parseAction(args, tokens):
553 # parseFCurve(fcu, args, tokens):
554 # parseKeyFramePoint(pt, args, tokens):
557 def parseAction(args, tokens):
558 name = args[0]
559 if invalid(args[1]):
560 return
562 ob = bpy.context.object
563 bpy.ops.object.mode_set(mode='POSE')
564 if ob.animation_data:
565 ob.animation_data.action = None
566 created = {}
567 for (key, val, sub) in tokens:
568 if key == 'FCurve':
569 prepareActionFCurve(ob, created, val, sub)
571 act = ob.animation_data.action
572 loadedData['Action'][name] = act
573 if act is None:
574 print("Ignoring action %s" % name)
575 return act
576 act.name = name
577 print("Action", name, act, ob)
579 for (key, val, sub) in tokens:
580 if key == 'FCurve':
581 fcu = parseActionFCurve(act, ob, val, sub)
582 else:
583 defaultKey(key, val, sub, act)
584 ob.animation_data.action = None
585 bpy.ops.object.mode_set(mode='OBJECT')
586 return act
588 def prepareActionFCurve(ob, created, args, tokens):
589 dataPath = args[0]
590 index = args[1]
591 (expr, channel) = channelFromDataPath(dataPath, index)
592 try:
593 if channel in created[expr]:
594 return
595 else:
596 created[expr].append(channel)
597 except:
598 created[expr] = [channel]
600 times = []
601 for (key, val, sub) in tokens:
602 if key == 'kp':
603 times.append(int(val[0]))
605 try:
606 data = mhxEval(expr)
607 except:
608 print("Ignoring illegal expression: %s" % expr)
609 return
611 n = 0
612 for t in times:
613 #bpy.context.scene.current_frame = t
614 bpy.ops.anim.change_frame(frame = t)
615 try:
616 data.keyframe_insert(channel)
617 n += 1
618 except:
619 pass
620 #print("failed", data, expr, channel)
621 if n != len(times):
622 print("Mismatch", n, len(times), expr, channel)
623 return
625 def channelFromDataPath(dataPath, index):
626 words = dataPath.split(']')
627 if len(words) == 1:
628 # location
629 expr = "ob"
630 channel = dataPath
631 elif len(words) == 2:
632 # pose.bones["tongue"].location
633 expr = "ob.%s]" % (words[0])
634 cwords = words[1].split('.')
635 channel = cwords[1]
636 elif len(words) == 3:
637 # pose.bones["brow.R"]["mad"]
638 expr = "ob.%s]" % (words[0])
639 cwords = words[1].split('"')
640 channel = cwords[1]
641 return (expr, channel)
643 def parseActionFCurve(act, ob, args, tokens):
644 dataPath = args[0]
645 index = args[1]
646 (expr, channel) = channelFromDataPath(dataPath, index)
647 index = int(args[1])
649 success = False
650 for fcu in act.fcurves:
651 (expr1, channel1) = channelFromDataPath(fcu.data_path, fcu.array_index)
652 if expr1 == expr and channel1 == channel and fcu.array_index == index:
653 success = True
654 break
655 if not success:
656 return None
658 n = 0
659 for (key, val, sub) in tokens:
660 if key == 'kp':
661 try:
662 pt = fcu.keyframe_points[n]
663 pt.interpolation = 'LINEAR'
664 pt = parseKeyFramePoint(pt, val, sub)
665 n += 1
666 except:
667 pass
668 #print(tokens)
669 #MyError("kp", fcu, n, len(fcu.keyframe_points), val)
670 else:
671 defaultKey(key, val, sub, fcu)
672 return fcu
674 def parseKeyFramePoint(pt, args, tokens):
675 pt.co = (float(args[0]), float(args[1]))
676 if len(args) > 2:
677 pt.handle1 = (float(args[2]), float(args[3]))
678 pt.handle2 = (float(args[3]), float(args[5]))
679 return pt
682 # parseAnimationData(rna, args, tokens):
683 # parseDriver(drv, args, tokens):
684 # parseDriverVariable(var, args, tokens):
687 def parseAnimationData(rna, args, tokens):
688 if not mhxEval(args[1]):
689 return
690 if rna.animation_data is None:
691 rna.animation_data_create()
692 adata = rna.animation_data
693 for (key, val, sub) in tokens:
694 if key == 'FCurve':
695 fcu = parseAnimDataFCurve(adata, rna, val, sub)
696 else:
697 defaultKey(key, val, sub, adata)
698 return adata
700 def parseAnimDataFCurve(adata, rna, args, tokens):
701 if invalid(args[2]):
702 return
703 dataPath = args[0]
704 index = int(args[1])
705 n = 1
706 for (key, val, sub) in tokens:
707 if key == 'Driver':
708 fcu = parseDriver(adata, dataPath, index, rna, val, sub)
709 fmod = fcu.modifiers[0]
710 fcu.modifiers.remove(fmod)
711 elif key == 'FModifier':
712 parseFModifier(fcu, val, sub)
713 elif key == 'kp':
714 pt = fcu.keyframe_points.insert(n, 0)
715 pt.interpolation = 'LINEAR'
716 pt = parseKeyFramePoint(pt, val, sub)
717 n += 1
718 else:
719 defaultKey(key, val, sub, fcu)
720 return fcu
723 fcurve = con.driver_add("influence", 0)
724 driver = fcurve.driver
725 driver.type = 'AVERAGE'
727 def parseDriver(adata, dataPath, index, rna, args, tokens):
728 if dataPath[-1] == ']':
729 words = dataPath.split(']')
730 expr = "rna." + words[0] + ']'
731 pwords = words[1].split('"')
732 prop = pwords[1]
733 bone = mhxEval(expr)
734 return None
735 else:
736 words = dataPath.split('.')
737 channel = words[-1]
738 expr = "rna"
739 for n in range(len(words)-1):
740 expr += "." + words[n]
741 expr += ".driver_add('%s', index)" % channel
743 fcu = mhxEval(expr, locals())
744 drv = fcu.driver
745 drv.type = args[0]
746 for (key, val, sub) in tokens:
747 if key == 'DriverVariable':
748 var = parseDriverVariable(drv, rna, val, sub)
749 else:
750 defaultKey(key, val, sub, drv)
751 return fcu
753 def parseDriverVariable(drv, rna, args, tokens):
754 var = drv.variables.new()
755 var.name = args[0]
756 var.type = args[1]
757 nTarget = 0
758 for (key, val, sub) in tokens:
759 if key == 'Target':
760 parseDriverTarget(var, nTarget, rna, val, sub)
761 nTarget += 1
762 else:
763 defaultKey(key, val, sub, var)
764 return var
766 def parseFModifier(fcu, args, tokens):
767 fmod = fcu.modifiers.new(args[0])
768 #fmod = fcu.modifiers[0]
769 for (key, val, sub) in tokens:
770 defaultKey(key, val, sub, fmod)
771 return fmod
774 var = driver.variables.new()
775 var.name = target_bone
776 var.targets[0].id_type = 'OBJECT'
777 var.targets[0].id = obj
778 var.targets[0].rna_path = driver_path
780 def parseDriverTarget(var, nTarget, rna, args, tokens):
781 targ = var.targets[nTarget]
782 name = args[0]
783 #targ.id_type = args[1]
784 dtype = args[1].capitalize()
785 dtype = 'Object'
786 targ.id = loadedData[dtype][name]
787 for (key, val, sub) in tokens:
788 if key == 'data_path':
789 words = val[0].split('"')
790 if len(words) > 1:
791 targ.data_path = propNames(words[1])[1]
792 else:
793 targ.data_path = propNames(val)[1]
794 else:
795 defaultKey(key, val, sub, targ)
796 return targ
800 # parseMaterial(args, ext, tokens):
801 # parseMTex(mat, args, tokens):
802 # parseTexture(args, tokens):
805 def parseMaterial(args, tokens):
806 name = args[0]
807 mat = bpy.data.materials.new(name)
808 if mat is None:
809 return None
810 loadedData['Material'][name] = mat
811 for (key, val, sub) in tokens:
812 if key == 'MTex':
813 parseMTex(mat, val, sub)
814 elif key == 'Ramp':
815 parseRamp(mat, val, sub)
816 elif key == 'RaytraceTransparency':
817 parseDefault(mat.raytrace_transparency, sub, {}, [])
818 elif key == 'Halo':
819 parseDefault(mat.halo, sub, {}, [])
820 elif key == 'SSS':
821 parseDefault(mat.subsurface_scattering, sub, {}, [])
822 elif key == 'Strand':
823 parseDefault(mat.strand, sub, {}, [])
824 elif key == 'NodeTree':
825 mat.use_nodes = True
826 parseNodeTree(mat.node_tree, val, sub)
827 elif key == 'AnimationData':
828 parseAnimationData(mat, val, sub)
829 else:
830 exclude = ['specular_intensity', 'tangent_shading']
831 defaultKey(key, val, sub, mat)
833 return mat
835 def parseMTex(mat, args, tokens):
836 index = int(args[0])
837 texname = args[1]
838 texco = args[2]
839 mapto = args[3]
840 tex = loadedData['Texture'][texname]
841 mtex = mat.texture_slots.add()
842 mtex.texture_coords = texco
843 mtex.texture = tex
845 for (key, val, sub) in tokens:
846 defaultKey(key, val, sub, mtex)
848 return mtex
850 def parseTexture(args, tokens):
851 if verbosity > 2:
852 print( "Parsing texture %s" % args )
853 name = args[0]
854 tex = bpy.data.textures.new(name=name, type=args[1])
855 loadedData['Texture'][name] = tex
857 for (key, val, sub) in tokens:
858 if key == 'Image':
859 try:
860 imgName = val[0]
861 img = loadedData['Image'][imgName]
862 tex.image = img
863 except:
864 msg = "Unable to load image '%s'" % val[0]
865 elif key == 'Ramp':
866 parseRamp(tex, val, sub)
867 elif key == 'NodeTree':
868 tex.use_nodes = True
869 parseNodeTree(tex.node_tree, val, sub)
870 else:
871 defaultKey(key, val, sub, tex, ['use_nodes', 'use_textures', 'contrast', 'use_alpha'])
873 return tex
875 def parseRamp(data, args, tokens):
876 setattr(data, "use_%s" % args[0], True)
877 ramp = getattr(data, args[0])
878 elts = ramp.elements
879 n = 0
880 for (key, val, sub) in tokens:
881 if key == 'Element':
882 elts[n].color = mhxEval(val[0], locals())
883 elts[n].position = mhxEval(val[1], locals())
884 n += 1
885 else:
886 defaultKey(key, val, sub, tex, ['use_nodes', 'use_textures', 'contrast'])
888 def parseSSS(mat, args, tokens):
889 sss = mat.subsurface_scattering
890 for (key, val, sub) in tokens:
891 defaultKey(key, val, sub, sss)
893 def parseStrand(mat, args, tokens):
894 strand = mat.strand
895 for (key, val, sub) in tokens:
896 defaultKey(key, val, sub, strand)
899 # parseNodeTree(tree, args, tokens):
900 # parseNode(node, args, tokens):
901 # parseSocket(socket, args, tokens):
904 def parseNodeTree(tree, args, tokens):
905 return
906 print("Tree", tree, args)
907 print(list(tree.nodes))
908 tree.name = args[0]
909 for (key, val, sub) in tokens:
910 if key == 'Node':
911 parseNodes(tree.nodes, val, sub)
912 else:
913 defaultKey(key, val, sub, tree)
915 def parseNodes(nodes, args, tokens):
916 print("Nodes", nodes, args)
917 print(list(nodes))
918 node.name = args[0]
919 for (key, val, sub) in tokens:
920 if key == 'Inputs':
921 parseSocket(node.inputs, val, sub)
922 elif key == 'Outputs':
923 parseSocket(node.outputs, val, sub)
924 else:
925 defaultKey(key, val, sub, node)
927 def parseNode(node, args, tokens):
928 print("Node", node, args)
929 print(list(node.inputs), list(node.outputs))
930 node.name = args[0]
931 for (key, val, sub) in tokens:
932 if key == 'Inputs':
933 parseSocket(node.inputs, val, sub)
934 elif key == 'Outputs':
935 parseSocket(node.outputs, val, sub)
936 else:
937 defaultKey(key, val, sub, node)
939 def parseSocket(socket, args, tokens):
940 print("Socket", socket, args)
941 socket.name = args[0]
942 for (key, val, sub) in tokens:
943 if key == 'Node':
944 parseNode(tree.nodes, val, sub)
945 else:
946 defaultKey(key, val, sub, tree)
951 # loadImage(filepath):
952 # parseImage(args, tokens):
955 def loadImage(relFilepath):
956 filepath = os.path.normpath(os.path.join(theDir, relFilepath))
957 print( "Loading %s" % filepath.encode('utf-8','strict'))
958 if os.path.isfile(filepath):
959 #print( "Found file %s." % filepath.encode('utf-8','strict') )
960 try:
961 img = bpy.data.images.load(filepath)
962 return img
963 except:
964 print( "Cannot read image" )
965 return None
966 else:
967 print( "No such file: %s" % filepath.encode('utf-8','strict') )
968 return None
971 def parseImage(args, tokens):
972 imgName = args[0]
973 img = None
974 for (key, val, sub) in tokens:
975 if key == 'Filename':
976 filename = val[0]
977 for n in range(1,len(val)):
978 filename += " " + val[n]
979 img = loadImage(filename)
980 if img is None:
981 return None
982 img.name = imgName
983 else:
984 defaultKey(key, val, sub, img, ['depth', 'dirty', 'has_data', 'size', 'type', 'use_premultiply'])
985 print ("Image %s" % img )
986 loadedData['Image'][imgName] = img
987 return img
990 # parseObject(args, tokens):
991 # createObject(type, name, data, datName):
992 # setObjectAndData(args, typ):
995 def parseObject(args, tokens, versionInfoStr=""):
996 if verbosity > 2:
997 print( "Parsing object %s" % args )
998 name = args[0]
999 typ = args[1]
1000 datName = args[2]
1002 if typ == 'EMPTY':
1003 ob = bpy.data.objects.new(name, None)
1004 loadedData['Object'][name] = ob
1005 linkObject(ob, None)
1006 else:
1007 try:
1008 data = loadedData[typ.capitalize()][datName]
1009 except:
1010 MyError("Failed to find data: %s %s %s" % (name, typ, datName))
1011 return
1013 try:
1014 ob = loadedData['Object'][name]
1015 bpy.context.scene.objects.active = ob
1016 #print("Found data", ob)
1017 except:
1018 ob = None
1020 if ob is None:
1021 ob = createObject(typ, name, data, datName)
1022 linkObject(ob, data)
1024 for (key, val, sub) in tokens:
1025 if key == 'Modifier':
1026 parseModifier(ob, val, sub)
1027 elif key == 'Constraint':
1028 parseConstraint(ob.constraints, None, val, sub)
1029 elif key == 'AnimationData':
1030 parseAnimationData(ob, val, sub)
1031 elif key == 'ParticleSystem':
1032 parseParticleSystem(ob, val, sub)
1033 elif key == 'FieldSettings':
1034 parseDefault(ob.field, sub, {}, [])
1035 else:
1036 defaultKey(key, val, sub, ob, ['type', 'data'])
1038 if versionInfoStr:
1039 print('============= updating version string %s' % versionInfoStr)
1040 ob.MhxVersionStr = versionInfoStr
1041 else:
1042 print('============= not updating version str')
1044 if bpy.context.object == ob:
1045 if ob.type == 'MESH':
1046 bpy.ops.object.shade_smooth()
1047 else:
1048 print("Context", ob, bpy.context.object, bpy.context.scene.objects.active)
1049 return
1051 def createObject(typ, name, data, datName):
1052 # print( "Creating object %s %s %s" % (typ, name, data) )
1053 ob = bpy.data.objects.new(name, data)
1054 if data:
1055 loadedData[typ.capitalize()][datName] = data
1056 loadedData['Object'][name] = ob
1057 return ob
1059 def linkObject(ob, data):
1060 #print("Data", data, ob.data)
1061 if data and ob.data is None:
1062 ob.data = data
1063 scn = bpy.context.scene
1064 scn.objects.link(ob)
1065 scn.objects.active = ob
1066 #print("Linked object", ob)
1067 #print("Scene", scn)
1068 #print("Active", scn.objects.active)
1069 #print("Context", bpy.context.object)
1070 return ob
1072 def setObjectAndData(args, typ):
1073 datName = args[0]
1074 obName = args[1]
1075 #bpy.ops.object.add(type=typ)
1076 ob = bpy.context.object
1077 ob.name = obName
1078 ob.data.name = datName
1079 loadedData[typ][datName] = ob.data
1080 loadedData['Object'][obName] = ob
1081 return ob.data
1085 # parseModifier(ob, args, tokens):
1089 def parseModifier(ob, args, tokens):
1090 name = args[0]
1091 typ = args[1]
1092 if typ == 'PARTICLE_SYSTEM':
1093 return None
1094 mod = ob.modifiers.new(name, typ)
1095 for (key, val, sub) in tokens:
1096 if key == 'HookAssignNth':
1097 if val[0] == 'CURVE':
1098 hookAssignNth(mod, int(val[1]), True, ob.data.splines[0].points)
1099 elif val[0] == 'LATTICE':
1100 hookAssignNth(mod, int(val[1]), False, ob.data.points)
1101 elif val[0] == 'MESH':
1102 hookAssignNth(mod, int(val[1]), True, ob.data.vertices)
1103 else:
1104 MyError("Unknown hook %s" % val)
1105 else:
1106 defaultKey(key, val, sub, mod)
1107 return mod
1109 def hookAssignNth(mod, n, select, points):
1110 if select:
1111 for pt in points:
1112 pt.select = False
1113 points[n].select = True
1114 sel = []
1115 for pt in points:
1116 sel.append(pt.select)
1117 #print(mod, sel, n, points)
1119 bpy.ops.object.mode_set(mode='EDIT')
1120 bpy.ops.object.hook_reset(modifier=mod.name)
1121 bpy.ops.object.hook_select(modifier=mod.name)
1122 bpy.ops.object.hook_assign(modifier=mod.name)
1123 bpy.ops.object.mode_set(mode='OBJECT')
1124 return
1127 # parseParticleSystem(ob, args, tokens):
1128 # parseParticles(particles, args, tokens):
1129 # parseParticle(par, args, tokens):
1132 def parseParticleSystem(ob, args, tokens):
1133 print(ob, bpy.context.object)
1134 pss = ob.particle_systems
1135 print(pss, pss.values())
1136 name = args[0]
1137 typ = args[1]
1138 #psys = pss.new(name, typ)
1139 bpy.ops.object.particle_system_add()
1140 print(pss, pss.values())
1141 psys = pss[-1]
1142 psys.name = name
1143 psys.settings.type = typ
1144 loadedData['ParticleSystem'][name] = psys
1145 print("Psys", psys)
1147 for (key, val, sub) in tokens:
1148 if key == 'Particles':
1149 parseParticles(psys, val, sub)
1150 else:
1151 defaultKey(key, val, sub, psys)
1152 return psys
1154 def parseParticles(psys, args, tokens):
1155 particles = psys.particles
1156 bpy.ops.particle.particle_edit_toggle()
1157 n = 0
1158 for (key, val, sub) in tokens:
1159 if key == 'Particle':
1160 parseParticle(particles[n], val, sub)
1161 n += 1
1162 else:
1163 for par in particles:
1164 defaultKey(key, val, sub, par)
1165 bpy.ops.particle.particle_edit_toggle()
1166 return particles
1168 def parseParticle(par, args, tokens):
1169 n = 0
1170 for (key, val, sub) in tokens:
1171 if key == 'h':
1172 h = par.hair[n]
1173 h.location = mhxEval(val[0], locals())
1174 h.time = int(val[1])
1175 h.weight = float(val[2])
1176 n += 1
1177 elif key == 'location':
1178 par.location = mhxEval(val[0], locals())
1179 return
1182 # unpackList(list_of_tuples):
1185 def unpackList(list_of_tuples):
1186 l = []
1187 for t in list_of_tuples:
1188 l.extend(t)
1189 return l
1194 # parseMesh (args, tokens):
1197 def parseMesh (args, tokens):
1198 global BMeshAware
1199 if verbosity > 2:
1200 print( "Parsing mesh %s" % args )
1202 mename = args[0]
1203 obname = args[1]
1204 me = bpy.data.meshes.new(mename)
1205 ob = createObject('MESH', obname, me, mename)
1207 verts = []
1208 edges = []
1209 faces = []
1210 vertsTex = []
1211 texFaces = []
1213 for (key, val, sub) in tokens:
1214 if key == 'Verts':
1215 verts = parseVerts(sub)
1216 elif key == 'Edges':
1217 edges = parseEdges(sub)
1218 elif key == 'Faces':
1219 faces = parseFaces(sub)
1221 if faces:
1222 me.from_pydata(verts, [], faces)
1223 else:
1224 me.from_pydata(verts, edges, [])
1225 me.update()
1226 linkObject(ob, me)
1228 if faces:
1229 try:
1230 me.polygons
1231 BMeshAware = True
1232 except:
1233 BMeshAware = False
1235 mats = []
1236 nuvlayers = 0
1237 for (key, val, sub) in tokens:
1238 if key == 'Verts' or key == 'Edges' or key == 'Faces':
1239 pass
1240 elif key == 'MeshTextureFaceLayer':
1241 if BMeshAware:
1242 parseUvTextureBMesh(val, sub, me)
1243 else:
1244 parseUvTextureNoBMesh(val, sub, me)
1245 elif key == 'MeshColorLayer':
1246 parseVertColorLayer(val, sub, me)
1247 elif key == 'VertexGroup':
1248 parseVertexGroup(ob, me, val, sub)
1249 elif key == 'ShapeKeys':
1250 parseShapeKeys(ob, me, val, sub)
1251 elif key == 'Material':
1252 try:
1253 mat = loadedData['Material'][val[0]]
1254 except:
1255 mat = None
1256 if mat:
1257 me.materials.append(mat)
1258 else:
1259 defaultKey(key, val, sub, me)
1261 for (key, val, sub) in tokens:
1262 if key == 'Faces':
1263 if BMeshAware:
1264 parseFaces2BMesh(sub, me)
1265 else:
1266 parseFaces2NoBMesh(sub, me)
1267 return me
1270 # parseVerts(tokens):
1271 # parseEdges(tokens):
1272 # parseFaces(tokens):
1273 # parseFaces2(tokens, me):
1276 def parseVerts(tokens):
1277 verts = []
1278 for (key, val, sub) in tokens:
1279 if key == 'v':
1280 verts.append( (theScale*float(val[0]), theScale*float(val[1]), theScale*float(val[2])) )
1281 return verts
1283 def parseEdges(tokens):
1284 edges = []
1285 for (key, val, sub) in tokens:
1286 if key == 'e':
1287 edges.append((int(val[0]), int(val[1])))
1288 return edges
1290 def parseFaces(tokens):
1291 faces = []
1292 for (key, val, sub) in tokens:
1293 if key == 'f':
1294 if len(val) == 3:
1295 face = [int(val[0]), int(val[1]), int(val[2])]
1296 elif len(val) == 4:
1297 face = [int(val[0]), int(val[1]), int(val[2]), int(val[3])]
1298 faces.append(face)
1299 return faces
1301 def parseFaces2BMesh(tokens, me):
1302 n = 0
1303 for (key, val, sub) in tokens:
1304 if key == 'ft':
1305 f = me.polygons[n]
1306 f.material_index = int(val[0])
1307 f.use_smooth = int(val[1])
1308 n += 1
1309 elif key == 'ftn':
1310 mn = int(val[1])
1311 us = int(val[2])
1312 npts = int(val[0])
1313 for i in range(npts):
1314 f = me.polygons[n]
1315 f.material_index = mn
1316 f.use_smooth = us
1317 n += 1
1318 elif key == 'mn':
1319 fn = int(val[0])
1320 mn = int(val[1])
1321 f = me.polygons[fn]
1322 f.material_index = mn
1323 elif key == 'ftall':
1324 mat = int(val[0])
1325 smooth = int(val[1])
1326 for f in me.polygons:
1327 f.material_index = mat
1328 f.use_smooth = smooth
1329 return
1331 def parseFaces2NoBMesh(tokens, me):
1332 n = 0
1333 for (key, val, sub) in tokens:
1334 if key == 'ft':
1335 f = me.faces[n]
1336 f.material_index = int(val[0])
1337 f.use_smooth = int(val[1])
1338 n += 1
1339 elif key == 'ftn':
1340 mn = int(val[1])
1341 us = int(val[2])
1342 npts = int(val[0])
1343 for i in range(npts):
1344 f = me.faces[n]
1345 f.material_index = mn
1346 f.use_smooth = us
1347 n += 1
1348 elif key == 'mn':
1349 fn = int(val[0])
1350 mn = int(val[1])
1351 f = me.faces[fn]
1352 f.material_index = mn
1353 elif key == 'ftall':
1354 mat = int(val[0])
1355 smooth = int(val[1])
1356 for f in me.faces:
1357 f.material_index = mat
1358 f.use_smooth = smooth
1359 return
1363 # parseUvTexture(args, tokens, me,):
1364 # parseUvTexData(args, tokens, uvdata):
1367 def parseUvTextureBMesh(args, tokens, me):
1368 name = args[0]
1369 bpy.ops.mesh.uv_texture_add()
1370 uvtex = me.uv_textures[-1]
1371 uvtex.name = name
1372 uvloop = me.uv_layers[-1]
1373 loadedData['MeshTextureFaceLayer'][name] = uvloop
1374 for (key, val, sub) in tokens:
1375 if key == 'Data':
1376 parseUvTexDataBMesh(val, sub, uvloop.data)
1377 else:
1378 defaultKey(key, val, sub, uvtex)
1379 return
1381 def parseUvTexDataBMesh(args, tokens, data):
1382 n = 0
1383 for (key, val, sub) in tokens:
1384 if key == 'vt':
1385 data[n].uv = (float(val[0]), float(val[1]))
1386 n += 1
1387 data[n].uv = (float(val[2]), float(val[3]))
1388 n += 1
1389 data[n].uv = (float(val[4]), float(val[5]))
1390 n += 1
1391 if len(val) > 6:
1392 data[n].uv = (float(val[6]), float(val[7]))
1393 n += 1
1394 return
1396 def parseUvTextureNoBMesh(args, tokens, me):
1397 name = args[0]
1398 uvtex = me.uv_textures.new(name = name)
1399 loadedData['MeshTextureFaceLayer'][name] = uvtex
1400 for (key, val, sub) in tokens:
1401 if key == 'Data':
1402 parseUvTexDataNoBMesh(val, sub, uvtex.data)
1403 else:
1404 defaultKey(key, val, sub, uvtex)
1405 return
1407 def parseUvTexDataNoBMesh(args, tokens, data):
1408 n = 0
1409 for (key, val, sub) in tokens:
1410 if key == 'vt':
1411 data[n].uv1 = (float(val[0]), float(val[1]))
1412 data[n].uv2 = (float(val[2]), float(val[3]))
1413 data[n].uv3 = (float(val[4]), float(val[5]))
1414 if len(val) > 6:
1415 data[n].uv4 = (float(val[6]), float(val[7]))
1416 n += 1
1417 return
1420 # parseVertColorLayer(args, tokens, me):
1421 # parseVertColorData(args, tokens, data):
1424 def parseVertColorLayer(args, tokens, me):
1425 name = args[0]
1426 print("VertColorLayer", name)
1427 vcol = me.vertex_colors.new(name)
1428 loadedData['MeshColorLayer'][name] = vcol
1429 for (key, val, sub) in tokens:
1430 if key == 'Data':
1431 parseVertColorData(val, sub, vcol.data)
1432 else:
1433 defaultKey(key, val, sub, vcol)
1434 return
1436 def parseVertColorData(args, tokens, data):
1437 n = 0
1438 for (key, val, sub) in tokens:
1439 if key == 'cv':
1440 data[n].color1 = mhxEval(val[0])
1441 data[n].color2 = mhxEval(val[1])
1442 data[n].color3 = mhxEval(val[2])
1443 data[n].color4 = mhxEval(val[3])
1444 n += 1
1445 return
1449 # parseVertexGroup(ob, me, args, tokens):
1452 def parseVertexGroup(ob, me, args, tokens):
1453 global toggle
1454 if verbosity > 2:
1455 print( "Parsing vertgroup %s" % args )
1456 grpName = args[0]
1457 try:
1458 res = mhxEval(args[1])
1459 except:
1460 res = True
1461 if not res:
1462 return
1464 if (toggle & T_Armature) or (grpName in ['Eye_L', 'Eye_R', 'Gums', 'Head', 'Jaw', 'Left', 'Middle', 'Right', 'Scalp']):
1465 try:
1466 group = loadedData['VertexGroup'][grpName]
1467 except KeyError:
1468 group = ob.vertex_groups.new(grpName)
1469 loadedData['VertexGroup'][grpName] = group
1470 for (key, val, sub) in tokens:
1471 if key == 'wv':
1472 group.add( [int(val[0])], float(val[1]), 'REPLACE' )
1473 return
1477 # parseShapeKeys(ob, me, args, tokens):
1478 # parseShapeKey(ob, me, args, tokens):
1479 # addShapeKey(ob, name, vgroup, tokens):
1480 # doShape(name):
1483 def doShape(name):
1484 if (toggle & T_Shapekeys) and (name == 'Basis'):
1485 return True
1486 else:
1487 return (toggle & T_Face)
1490 def parseShapeKeys(ob, me, args, tokens):
1491 for (key, val, sub) in tokens:
1492 if key == 'ShapeKey':
1493 parseShapeKey(ob, me, val, sub)
1494 elif key == 'AnimationData':
1495 if me.shape_keys:
1496 parseAnimationData(me.shape_keys, val, sub)
1497 elif key == 'Expression':
1498 prop = "Mhe" + val[0].capitalize()
1499 parseUnits(prop, ob, sub)
1500 elif key == 'Viseme':
1501 name = val[0].upper()
1502 if name in ["REST", "ETC"]:
1503 name = name.capitalize()
1504 prop = "Mhv" + name
1505 parseUnits(prop, ob, sub)
1506 ob.active_shape_key_index = 0
1509 def parseUnits(prop, ob, sub):
1510 string = ""
1511 for words in sub:
1512 unit = words[0].replace("-","_")
1513 value = words[1][0]
1514 string += "%s:%s;" % (unit, value)
1515 rig = ob.parent
1516 rig[prop] = string
1519 def parseShapeKey(ob, me, args, tokens):
1520 if verbosity > 2:
1521 print( "Parsing ob %s shape %s" % (bpy.context.object, args[0] ))
1522 name = args[0]
1523 lr = args[1]
1524 if invalid(args[2]):
1525 return
1527 if lr == 'Sym': # or toggle & T_Symm:
1528 addShapeKey(ob, name, None, tokens)
1529 elif lr == 'LR':
1530 addShapeKey(ob, name+'_L', 'Left', tokens)
1531 addShapeKey(ob, name+'_R', 'Right', tokens)
1532 else:
1533 MyError("ShapeKey L/R %s" % lr)
1534 return
1537 def addShapeKey(ob, name, vgroup, tokens):
1538 skey = ob.shape_key_add(name=name, from_mix=False)
1539 if name != 'Basis':
1540 skey.relative_key = loadedData['ShapeKey']['Basis']
1541 skey.name = name
1542 if vgroup:
1543 skey.vertex_group = vgroup
1544 loadedData['ShapeKey'][name] = skey
1546 for (key, val, sub) in tokens:
1547 if key == 'sv':
1548 index = int(val[0])
1549 pt = skey.data[index].co
1550 pt[0] += theScale*float(val[1])
1551 pt[1] += theScale*float(val[2])
1552 pt[2] += theScale*float(val[3])
1553 else:
1554 defaultKey(key, val, sub, skey)
1556 return
1560 # parseArmature (obName, args, tokens)
1563 def parseArmature (args, tokens):
1564 global toggle, theArmature
1565 if verbosity > 2:
1566 print( "Parsing armature %s" % args )
1568 amtname = args[0]
1569 obname = args[1]
1570 mode = args[2]
1572 amt = bpy.data.armatures.new(amtname)
1573 ob = createObject('ARMATURE', obname, amt, amtname)
1574 linkObject(ob, amt)
1575 theArmature = ob
1577 bpy.ops.object.mode_set(mode='OBJECT')
1578 bpy.ops.object.mode_set(mode='EDIT')
1580 heads = {}
1581 tails = {}
1582 for (key, val, sub) in tokens:
1583 if key == 'Bone':
1584 bname = val[0]
1585 if not invalid(val[1]):
1586 bone = amt.edit_bones.new(bname)
1587 parseBone(bone, amt, sub, heads, tails)
1588 loadedData['Bone'][bname] = bone
1589 elif key == 'RecalcRoll':
1590 rolls = {}
1591 for bone in amt.edit_bones:
1592 bone.select = False
1593 blist = mhxEval(val[0])
1594 for name in blist:
1595 bone = amt.edit_bones[name]
1596 bone.select = True
1597 bpy.ops.armature.calculate_roll(type='Z')
1598 for bone in amt.edit_bones:
1599 rolls[bone.name] = bone.roll
1600 bpy.ops.object.mode_set(mode='OBJECT')
1601 for bone in amt.bones:
1602 bone['Roll'] = rolls[bone.name]
1603 bpy.ops.object.mode_set(mode='EDIT')
1604 else:
1605 defaultKey(key, val, sub, amt, ['MetaRig'])
1606 bpy.ops.object.mode_set(mode='OBJECT')
1608 return amt
1611 # parseBone(bone, amt, tokens, heads, tails):
1614 def parseBone(bone, amt, tokens, heads, tails):
1615 for (key, val, sub) in tokens:
1616 if key == "head":
1617 bone.head = (theScale*float(val[0]), theScale*float(val[1]), theScale*float(val[2]))
1618 elif key == "tail":
1619 bone.tail = (theScale*float(val[0]), theScale*float(val[1]), theScale*float(val[2]))
1620 #elif key == 'restrict_select':
1621 # pass
1622 elif key == 'hide' and val[0] == 'True':
1623 name = bone.name
1624 else:
1625 defaultKey(key, val, sub, bone)
1626 return bone
1629 # parsePose (args, tokens):
1632 def parsePose (args, tokens):
1633 name = args[0]
1634 ob = loadedData['Object'][name]
1635 bpy.context.scene.objects.active = ob
1636 bpy.ops.object.mode_set(mode='POSE')
1637 pbones = ob.pose.bones
1638 nGrps = 0
1639 for (key, val, sub) in tokens:
1640 if key == 'Posebone':
1641 parsePoseBone(pbones, ob, val, sub)
1642 elif key == 'BoneGroup':
1643 parseBoneGroup(ob.pose, nGrps, val, sub)
1644 nGrps += 1
1645 elif key == 'SetProp':
1646 bone = val[0]
1647 prop = val[1]
1648 value = mhxEval(val[2])
1649 pb = pbones[bone]
1650 pb[prop] = value
1651 else:
1652 defaultKey(key, val, sub, ob.pose)
1653 bpy.ops.object.mode_set(mode='OBJECT')
1654 return ob
1658 # parsePoseBone(pbones, args, tokens):
1659 # parseArray(data, exts, args):
1662 def parseBoneGroup(pose, nGrps, args, tokens):
1663 if verbosity > 2:
1664 print( "Parsing bonegroup %s" % args )
1665 name = args[0]
1666 bpy.ops.pose.group_add()
1667 bg = pose.bone_groups.active
1668 loadedData['BoneGroup'][name] = bg
1669 for (key, val, sub) in tokens:
1670 defaultKey(key, val, sub, bg)
1671 return
1673 def parsePoseBone(pbones, ob, args, tokens):
1674 if invalid(args[1]):
1675 return
1676 name = args[0]
1677 pb = pbones[name]
1678 amt = ob.data
1679 amt.bones.active = pb.bone
1681 for (key, val, sub) in tokens:
1682 if key == 'Constraint':
1683 amt.bones.active = pb.bone
1684 cns = parseConstraint(pb.constraints, pb, val, sub)
1685 elif key == 'bpyops':
1686 amt.bones.active = pb.bone
1687 expr = "bpy.ops.%s" % val[0]
1688 raise MhxError("MHX bug: Must not exec %s" % expr)
1689 elif key == 'ik_dof':
1690 parseArray(pb, ["ik_dof_x", "ik_dof_y", "ik_dof_z"], val)
1691 elif key == 'ik_limit':
1692 parseArray(pb, ["ik_limit_x", "ik_limit_y", "ik_limit_z"], val)
1693 elif key == 'ik_max':
1694 parseArray(pb, ["ik_max_x", "ik_max_y", "ik_max_z"], val)
1695 elif key == 'ik_min':
1696 parseArray(pb, ["ik_min_x", "ik_min_y", "ik_min_z"], val)
1697 elif key == 'ik_stiffness':
1698 parseArray(pb, ["ik_stiffness_x", "ik_stiffness_y", "ik_stiffness_z"], val)
1699 elif key == 'hide':
1700 #bpy.ops.object.mode_set(mode='OBJECT')
1701 amt.bones[name].hide = mhxEval(val[0])
1702 #bpy.ops.object.mode_set(mode='POSE')
1704 else:
1705 defaultKey(key, val, sub, pb)
1706 return
1708 def parseArray(data, exts, args):
1709 n = 1
1710 for ext in exts:
1711 setattr(data, ext, mhxEval(args[n]))
1712 n += 1
1713 return
1716 # parseConstraint(constraints, pb, args, tokens)
1719 def parseConstraint(constraints, pb, args, tokens):
1720 if invalid(args[2]):
1721 return None
1722 if (toggle&T_Opcns and pb):
1723 print("Active")
1724 aob = bpy.context.object
1725 print("ob", aob)
1726 aamt = aob.data
1727 print("amt", aamt)
1728 apose = aob.pose
1729 print("pose", apose)
1730 abone = aamt.bones.active
1731 print("bone", abone)
1732 print('Num cns before', len(list(constraints)))
1733 bpy.ops.pose.constraint_add(type=args[1])
1734 cns = constraints.active
1735 print('and after', pb, cns, len(list(constraints)))
1736 else:
1737 cns = constraints.new(args[1])
1739 cns.name = args[0]
1740 for (key,val,sub) in tokens:
1741 if key == 'invert':
1742 parseArray(cns, ["invert_x", "invert_y", "invert_z"], val)
1743 elif key == 'use':
1744 parseArray(cns, ["use_x", "use_y", "use_z"], val)
1745 elif key == 'pos_lock':
1746 parseArray(cns, ["lock_location_x", "lock_location_y", "lock_location_z"], val)
1747 elif key == 'rot_lock':
1748 parseArray(cns, ["lock_rotation_x", "lock_rotation_y", "lock_rotation_z"], val)
1749 else:
1750 defaultKey(key, val, sub, cns, ["use_target"])
1753 #print("cns %s done" % cns.name)
1754 return cns
1759 # parseCurve (args, tokens):
1760 # parseSpline(cu, args, tokens):
1761 # parseBezier(spline, n, args, tokens):
1764 def parseCurve (args, tokens):
1765 if verbosity > 2:
1766 print( "Parsing curve %s" % args )
1767 bpy.ops.object.add(type='CURVE')
1768 cu = setObjectAndData(args, 'Curve')
1770 for (key, val, sub) in tokens:
1771 if key == 'Spline':
1772 parseSpline(cu, val, sub)
1773 else:
1774 defaultKey(key, val, sub, cu)
1775 return
1777 def parseTextCurve (args, tokens):
1778 if verbosity > 2:
1779 print( "Parsing text curve %s" % args )
1780 bpy.ops.object.text_add()
1781 txt = setObjectAndData(args, 'Text')
1783 for (key, val, sub) in tokens:
1784 if key == 'Spline':
1785 parseSpline(txt, val, sub)
1786 elif key == 'BodyFormat':
1787 parseCollection(txt.body_format, sub, [])
1788 elif key == 'EditFormat':
1789 parseDefault(txt.edit_format, sub, {}, [])
1790 elif key == 'Font':
1791 parseDefault(txt.font, sub, {}, [])
1792 elif key == 'TextBox':
1793 parseCollection(txt.body_format, sub, [])
1794 else:
1795 defaultKey(key, val, sub, txt)
1796 return
1799 def parseSpline(cu, args, tokens):
1800 typ = args[0]
1801 spline = cu.splines.new(typ)
1802 nPointsU = int(args[1])
1803 nPointsV = int(args[2])
1804 #spline.point_count_u = nPointsU
1805 #spline.point_count_v = nPointsV
1806 if typ == 'BEZIER' or typ == 'BSPLINE':
1807 spline.bezier_points.add(nPointsU)
1808 else:
1809 spline.points.add(nPointsU)
1811 n = 0
1812 for (key, val, sub) in tokens:
1813 if key == 'bz':
1814 parseBezier(spline.bezier_points[n], val, sub)
1815 n += 1
1816 elif key == 'pt':
1817 parsePoint(spline.points[n], val, sub)
1818 n += 1
1819 else:
1820 defaultKey(key, val, sub, spline)
1821 return
1823 def parseBezier(bez, args, tokens):
1824 bez.co = mhxEval(args[0])
1825 bez.co = theScale*bez.co
1826 bez.handle1 = mhxEval(args[1])
1827 bez.handle1_type = args[2]
1828 bez.handle2 = mhxEval(args[3])
1829 bez.handle2_type = args[4]
1830 return
1832 def parsePoint(pt, args, tokens):
1833 pt.co = mhxEval(args[0])
1834 pt.co = theScale*pt.co
1835 print(" pt", pt.co)
1836 return
1839 # parseLattice (args, tokens):
1842 def parseLattice (args, tokens):
1843 if verbosity > 2:
1844 print( "Parsing lattice %s" % args )
1845 bpy.ops.object.add(type='LATTICE')
1846 lat = setObjectAndData(args, 'Lattice')
1847 for (key, val, sub) in tokens:
1848 if key == 'Points':
1849 parseLatticePoints(val, sub, lat.points)
1850 else:
1851 defaultKey(key, val, sub, lat)
1852 return
1854 def parseLatticePoints(args, tokens, points):
1855 n = 0
1856 for (key, val, sub) in tokens:
1857 if key == 'pt':
1858 v = points[n].co_deform
1859 v.x = theScale*float(val[0])
1860 v.y = theScale*float(val[1])
1861 v.z = theScale*float(val[2])
1862 n += 1
1863 return
1866 # parseLamp (args, tokens):
1867 # parseFalloffCurve(focu, args, tokens):
1870 def parseLamp (args, tokens):
1871 if verbosity > 2:
1872 print( "Parsing lamp %s" % args )
1873 bpy.ops.object.add(type='LAMP')
1874 lamp = setObjectAndData(args, 'Lamp')
1875 for (key, val, sub) in tokens:
1876 if key == 'FalloffCurve':
1877 parseFalloffCurve(lamp.falloff_curve, val, sub)
1878 else:
1879 defaultKey(key, val, sub, lamp)
1880 return
1882 def parseFalloffCurve(focu, args, tokens):
1883 return
1886 # parseGroup (args, tokens):
1887 # parseGroupObjects(args, tokens, grp):
1890 def parseGroup (args, tokens):
1891 if verbosity > 2:
1892 print( "Parsing group %s" % args )
1894 grpName = args[0]
1895 grp = bpy.data.groups.new(grpName)
1896 loadedData['Group'][grpName] = grp
1897 for (key, val, sub) in tokens:
1898 if key == 'Objects':
1899 parseGroupObjects(val, sub, grp)
1900 else:
1901 defaultKey(key, val, sub, grp)
1902 return
1904 def parseGroupObjects(args, tokens, grp):
1905 rig = None
1906 for (key, val, sub) in tokens:
1907 if key == 'ob':
1908 try:
1909 ob = loadedData['Object'][val[0]]
1910 grp.objects.link(ob)
1911 except:
1912 ob = None
1913 if ob:
1914 print(ob, ob.type, rig, ob.parent)
1915 if ob.type == 'ARMATURE':
1916 rig = ob
1917 elif ob.type == 'EMPTY' and rig and not ob.parent:
1918 ob.parent = rig
1919 print("SSS")
1920 return
1923 # parseWorld (args, tokens):
1926 def parseWorld (args, tokens):
1927 if verbosity > 2:
1928 print( "Parsing world %s" % args )
1929 world = bpy.context.scene.world
1930 for (key, val, sub) in tokens:
1931 if key == 'Lighting':
1932 parseDefault(world.lighting, sub, {}, [])
1933 elif key == 'Mist':
1934 parseDefault(world.mist, sub, {}, [])
1935 elif key == 'Stars':
1936 parseDefault(world.stars, sub, {}, [])
1937 else:
1938 defaultKey(key, val, sub, world)
1939 return
1942 # parseScene (args, tokens):
1943 # parseRenderSettings(render, args, tokens):
1944 # parseToolSettings(tool, args, tokens):
1947 def parseScene (args, tokens):
1948 if verbosity > 2:
1949 print( "Parsing scene %s" % args )
1950 scn = bpy.context.scene
1951 for (key, val, sub) in tokens:
1952 if key == 'NodeTree':
1953 scn.use_nodes = True
1954 parseNodeTree(scn, val, sub)
1955 elif key == 'GameData':
1956 parseDefault(scn.game_data, sub, {}, [])
1957 elif key == 'KeyingSet':
1958 pass
1959 #parseDefault(scn.keying_sets, sub, {}, [])
1960 elif key == 'ObjectBase':
1961 pass
1962 #parseDefault(scn.bases, sub, {}, [])
1963 elif key == 'RenderSettings':
1964 parseRenderSettings(scn.render, sub, [])
1965 elif key == 'ToolSettings':
1966 subkeys = {'ImagePaint' : "image_paint",
1967 'Sculpt' : "sculpt",
1968 'VertexPaint' : "vertex_paint",
1969 'WeightPaint' : "weight_paint" }
1970 parseDefault(scn.tool_settings, sub, subkeys, [])
1971 elif key == 'UnitSettings':
1972 parseDefault(scn.unit_settings, sub, {}, [])
1973 else:
1974 defaultKey(key, val, sub, scn)
1975 return
1977 def parseRenderSettings(render, args, tokens):
1978 if verbosity > 2:
1979 print( "Parsing RenderSettings %s" % args )
1980 for (key, val, sub) in tokens:
1981 if key == 'Layer':
1982 pass
1983 #parseDefault(scn.layers, sub, [])
1984 else:
1985 defaultKey(key, val, sub, render)
1986 return
1989 # parseDefineProperty(args, tokens):
1992 def parseDefineProperty(args, tokens):
1993 prop = "%sProperty" % (args[1])
1994 c = '('
1995 for option in args[2:]:
1996 prop += "%s %s" % (c, option)
1997 c = ','
1998 prop += ')'
1999 setattr(bpy.types.Object, args[0], prop)
2000 return
2003 # correctRig(args):
2006 def correctRig(args):
2007 human = args[0]
2008 print("CorrectRig %s" % human)
2009 try:
2010 ob = loadedData['Object'][human]
2011 except:
2012 return
2013 ob.MhxShapekeyDrivers = (toggle&T_Shapekeys != 0 and toggle&T_ShapeDrivers != 0)
2014 bpy.context.scene.objects.active = ob
2015 bpy.ops.object.mode_set(mode='POSE')
2016 amt = ob.data
2017 cnslist = []
2018 for pb in ob.pose.bones:
2019 pb.bone.select = False
2020 for cns in pb.constraints:
2021 if cns.type == 'CHILD_OF':
2022 cnslist.append((pb, cns, cns.influence))
2023 cns.influence = 0
2025 for (pb, cns, inf) in cnslist:
2026 amt.bones.active = pb.bone
2027 cns.influence = 1
2028 #print("Childof %s %s %s %.2f" % (amt.name, pb.name, cns.name, inf))
2029 bpy.ops.constraint.childof_clear_inverse(constraint=cns.name, owner='BONE')
2030 bpy.ops.constraint.childof_set_inverse(constraint=cns.name, owner='BONE')
2031 cns.influence = 0
2033 for (pb, cns, inf) in cnslist:
2034 cns.influence = inf
2035 return
2039 # postProcess(args)
2042 def postProcess(args):
2043 human = args[0]
2044 print("Postprocess %s" % human)
2045 try:
2046 ob = loadedData['Object'][human]
2047 except:
2048 ob = None
2049 if toggle & T_Diamond == 0 and ob:
2050 deleteDiamonds(ob)
2051 return
2054 # deleteDiamonds(ob)
2055 # Delete joint diamonds in main mesh
2056 # Invisio = material # 1
2059 def deleteDiamonds(ob):
2060 bpy.context.scene.objects.active = ob
2061 if not bpy.context.object:
2062 return
2063 print("Delete helper geometry in %s" % bpy.context.object)
2064 bpy.ops.object.mode_set(mode='EDIT')
2065 bpy.ops.mesh.select_all(action='DESELECT')
2066 bpy.ops.object.mode_set(mode='OBJECT')
2067 me = ob.data
2068 invisioNum = -1
2069 for mn,mat in enumerate(me.materials):
2070 if "Invis" in mat.name:
2071 invisioNum = mn
2072 break
2073 if invisioNum < 0:
2074 print("WARNING: Nu Invisio material found. Cannot delete helper geometry")
2075 elif BMeshAware:
2076 for f in me.polygons:
2077 if f.material_index >= invisioNum:
2078 for vn in f.vertices:
2079 me.vertices[vn].select = True
2080 else:
2081 for f in me.faces:
2082 if f.material_index >= invisioNum:
2083 for vn in f.vertices:
2084 me.vertices[vn].select = True
2085 if BMeshAware and toggle&T_CrashSafe:
2086 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"
2087 print(theMessage)
2088 else:
2089 bpy.ops.object.mode_set(mode='EDIT')
2090 print("Do delete")
2091 bpy.ops.mesh.delete(type='VERT')
2092 print("Verts deleted")
2093 bpy.ops.object.mode_set(mode='OBJECT')
2094 print("Back to object mode")
2095 return
2098 # defaultKey(ext, args, tokens, data, exclude):
2101 theProperty = None
2103 def propNames(string):
2104 global alpha7
2105 #string = string.encode('utf-8', 'strict')
2107 # Alpha 7 compatibility
2108 if string[0:2] == "&_":
2109 string = "Mhf"+string[2:]
2110 alpha7 = True
2111 elif string[0] == "&":
2112 string = "Mha"+string[1:]
2113 alpha7 = True
2114 elif string[0] == "*":
2115 string = "Mhs"+string[1:]
2116 alpha7 = True
2117 elif len(string) > 4 and string[0:4] == "Hide":
2118 string = "Mhh"+string[4:]
2119 alpha7 = True
2121 if string[0] == "_":
2122 return None,None
2123 elif (len(string) > 3 and
2124 string[0:3] in ["Mha", "Mhf", "Mhs", "Mhh", "Mhv", "Mhc"]):
2125 name = string.replace("-","_")
2126 return name, '["%s"]' % name
2127 else:
2128 return string, '["%s"]' % string
2131 def defProp(args, var):
2132 proptype = args[0]
2133 name = propNames(args[1])[0]
2134 value = args[2]
2135 rest = 'description="%s"' % args[3].replace("_", " ")
2136 if len(args) > 4:
2137 rest += ", " + args[4]
2139 if name:
2140 var[name] = value
2143 def defNewProp(name, proptype, rest):
2144 prop = "%sProperty(%s)" % (proptype, rest)
2145 setattr(bpy.types.Object, name, eval(prop)) # safe: only called from this file
2148 def setProperty(args, var):
2149 global theProperty
2150 tip = ""
2151 name = propNames(args[0])[0]
2152 value = mhxEval(args[1])
2153 if name:
2154 var[name] = value
2155 if len(args) > 2:
2156 tip = 'description="%s"' % args[2].replace("_", " ")
2157 theProperty = (name, tip, value)
2160 def setPropKeys(args):
2161 global theProperty
2162 if theProperty is None:
2163 return
2164 (name, tip, value) = theProperty
2165 if len(args) >= 2 and not isinstance(value, bool):
2166 if "BOOLEAN" in args[1]:
2167 value = bool(value)
2168 else:
2169 tip = tip + "," + args[1].replace(":", "=").replace('"', " ")
2170 #expr = "bpy.types.Object.%s = %sProperty(%s)" % (name, proptype, tip)
2171 if isinstance(value, bool):
2172 prop = BoolProperty(tip)
2173 elif isinstance(value, int):
2174 prop = IntProperty(tip)
2175 elif isinstance(value, float):
2176 prop = FloatProperty(tip)
2177 elif isinstance(value, string):
2178 prop = StringProperty(tip)
2179 setattr(bpy.types.Object, name, prop)
2180 theProperty = None
2183 def defaultKey(ext, args, tokens, var, exclude=[]):
2184 if ext == 'Property':
2185 return setProperty(args, var)
2186 elif ext == 'PropKeys':
2187 return setPropKeys(args)
2188 elif ext == 'DefProp':
2189 return defProp(args, var)
2191 if ext == 'bpyops':
2192 expr = "bpy.ops.%s" % args[0]
2193 print(expr)
2194 raise MhxError("MHX bug: calling %s" % expr)
2196 if ext in exclude:
2197 return
2198 nvar = getattr(var, ext)
2200 if len(args) == 0:
2201 MyError("Key length 0: %s" % ext)
2203 rnaType = args[0]
2204 if rnaType == 'Refer':
2205 typ = args[1]
2206 name = args[2]
2207 setattr(var, ext, loadedData[typ][name])
2208 return
2210 elif rnaType == 'Struct' or rnaType == 'Define':
2211 raise MhxError("Struct/Define!")
2212 typ = args[1]
2213 name = args[2]
2214 try:
2215 data = getattr(var, ext)
2216 except:
2217 data = None
2218 # print("Old structrna", nvar, data)
2220 if data is None:
2221 try:
2222 creator = args[3]
2223 except:
2224 creator = None
2226 try:
2227 rna = mhxEval(var, locals())
2228 data = mhxEval(creator)
2229 except:
2230 data = None
2231 # print("New struct", nvar, typ, data)
2233 if rnaType == 'Define':
2234 loadedData[typ][name] = data
2236 if data:
2237 for (key, val, sub) in tokens:
2238 defaultKey(key, val, sub, data)
2239 return
2241 elif rnaType == 'PropertyRNA':
2242 raise MhxError("PropertyRNA!")
2243 #print("PropertyRNA ", ext, var)
2244 for (key, val, sub) in tokens:
2245 defaultKey(ext, val, sub, nvar, [])
2246 return
2248 elif rnaType == 'Array':
2249 for n in range(1, len(args)):
2250 nvar[n-1] = mhxEval(args[n], locals())
2251 if len(args) > 0:
2252 nvar[0] = mhxEval(args[1], locals())
2253 return
2255 elif rnaType == 'List':
2256 raise MhxError("List!")
2257 data = []
2258 for (key, val, sub) in tokens:
2259 elt = mhxEval(val[1], locals())
2260 data.append(elt)
2261 setattr(var, ext, data)
2262 return
2264 elif rnaType == 'Matrix':
2265 raise MhxError("Matrix!")
2266 i = 0
2267 n = len(tokens)
2268 for (key, val, sub) in tokens:
2269 if key == 'row':
2270 for j in range(n):
2271 nvar[i][j] = float(val[j])
2272 i += 1
2273 return
2275 else:
2276 try:
2277 data = loadedData[rnaType][args[1]]
2278 raise MhxError("From loaded %s %s!" % (rnaType, args[1]))
2279 except KeyError:
2280 pass
2281 data = mhxEval(rnaType, locals())
2282 setattr(var, ext, data)
2289 def pushOnTodoList(var, expr):
2290 print("Unrecognized expression", expr)
2291 return
2292 print(dir(mhxEval(var)))
2293 MyError(
2294 "Unrecognized expression %s.\n" % expr +
2295 "This can mean that Blender's python API has changed\n" +
2296 "since the MHX file was exported. Try to export again\n" +
2297 "from an up-to-date MakeHuman nightly build.\n" +
2298 "Alternatively, your Blender version may be obsolete.\n" +
2299 "Download an up-to-date version from www.graphicall.org")
2303 # parseBoolArray(mask):
2306 def parseBoolArray(mask):
2307 list = []
2308 for c in mask:
2309 if c == '0':
2310 list.append(False)
2311 else:
2312 list.append(True)
2313 return list
2315 # parseMatrix(args, tokens)
2318 def parseMatrix(args, tokens):
2319 matrix = mathutils.Matrix()
2320 i = 0
2321 for (key, val, sub) in tokens:
2322 if key == 'row':
2323 matrix[i][0] = float(val[0])
2324 matrix[i][1] = float(val[1])
2325 matrix[i][2] = float(val[2])
2326 matrix[i][3] = float(val[3])
2327 i += 1
2328 return matrix
2331 # parseDefault(data, tokens, subkeys, exclude):
2334 def parseDefault(data, tokens, subkeys, exclude):
2335 for (key, val, sub) in tokens:
2336 if key in subkeys.keys():
2337 for (key2, val2, sub2) in sub:
2338 ndata = getattr(data, subkeys[key])
2339 defaultKey(key2, val2, sub2, ndata)
2340 else:
2341 defaultKey(key, val, sub, data, exclude)
2343 def parseCollection(data, tokens, exclude):
2344 return
2348 # Utilities
2352 # extractBpyType(data):
2355 def extractBpyType(data):
2356 typeSplit = str(type(data)).split("'")
2357 if typeSplit[0] != '<class ':
2358 return None
2359 classSplit = typeSplit[1].split(".")
2360 if classSplit[0] == 'bpy' and classSplit[1] == 'types':
2361 return classSplit[2]
2362 elif classSplit[0] == 'bpy_types':
2363 return classSplit[1]
2364 else:
2365 return None
2368 # Bool(string):
2371 def Bool(string):
2372 if string == 'True':
2373 return True
2374 elif string == 'False':
2375 return False
2376 else:
2377 MyError("Bool %s?" % string)
2380 # invalid(condition):
2383 def invalid(condition):
2384 global rigLeg, rigArm, toggle
2385 try:
2386 res = mhxEval(condition)
2387 #print("%s = %s" % (condition, res))
2388 return not res
2389 except:
2390 #print("%s invalid!" % condition)
2391 return True
2396 # clearScene(context):
2399 def clearScene():
2400 global toggle
2401 scn = bpy.context.scene
2402 for n in range(len(scn.layers)):
2403 scn.layers[n] = True
2404 return scn
2405 print("clearScene %s %s" % (toggle & T_Replace, scn))
2406 if not toggle & T_Replace:
2407 return scn
2409 for ob in scn.objects:
2410 if ob.type in ['MESH', 'ARMATURE', 'EMPTY', 'CURVE', 'LATTICE']:
2411 scn.objects.active = ob
2412 ob.name = "#" + ob.name
2413 try:
2414 bpy.ops.object.mode_set(mode='OBJECT')
2415 except:
2416 pass
2417 scn.objects.unlink(ob)
2418 del ob
2420 for grp in bpy.data.groups:
2421 grp.name = "#" + grp.name
2422 #print(scn.objects)
2423 return scn
2426 # hideLayers(args):
2427 # args = sceneLayers sceneHideLayers boneLayers boneHideLayers or nothing
2430 def hideLayers(args):
2431 if len(args) > 1:
2432 sceneLayers = int(args[2], 16)
2433 sceneHideLayers = int(args[3], 16)
2434 boneLayers = int(args[4], 16)
2435 # boneHideLayers = int(args[5], 16)
2436 boneHideLayers = 0
2437 else:
2438 sceneLayers = 0x00ff
2439 sceneHideLayers = 0
2440 boneLayers = 0
2441 boneHideLayers = 0
2443 scn = bpy.context.scene
2444 mask = 1
2445 hidelayers = []
2446 for n in range(20):
2447 scn.layers[n] = True if sceneLayers & mask else False
2448 if sceneHideLayers & mask:
2449 hidelayers.append(n)
2450 mask = mask << 1
2452 for ob in scn.objects:
2453 for n in hidelayers:
2454 if ob.layers[n]:
2455 ob.hide = True
2456 ob.hide_render = True
2458 if boneLayers:
2459 human = args[1]
2460 try:
2461 ob = loadedData['Object'][human]
2462 except:
2463 return
2465 mask = 1
2466 hidelayers = []
2467 for n in range(32):
2468 ob.data.layers[n] = True if boneLayers & mask else False
2469 if boneHideLayers & mask:
2470 hidelayers.append(n)
2471 mask = mask << 1
2473 for b in ob.data.bones:
2474 for n in hidelayers:
2475 if b.layers[n]:
2476 b.hide = True
2478 return
2482 # readDefaults():
2483 # writeDefaults():
2486 ConfigFile = '~/mhx_import.cfg'
2489 def readDefaults():
2490 global toggle, toggleSettings, theScale
2491 path = os.path.realpath(os.path.expanduser(ConfigFile))
2492 try:
2493 fp = open(path, 'rU')
2494 print('Storing defaults')
2495 except:
2496 print('Cannot open "%s" for reading' % path)
2497 return
2498 bver = ''
2499 for line in fp:
2500 words = line.split()
2501 if len(words) >= 3:
2502 try:
2503 toggle = int(words[0],16)
2504 theScale = float(words[1])
2505 except:
2506 print('Configuration file "%s" is corrupt' % path)
2507 fp.close()
2508 toggleSettings = toggle
2509 return
2511 def writeDefaults():
2512 global toggleSettings, theScale
2513 path = os.path.realpath(os.path.expanduser(ConfigFile))
2514 try:
2515 fp = open(path, 'w')
2516 print('Storing defaults')
2517 except:
2518 print('Cannot open "%s" for writing' % path)
2519 return
2520 fp.write("%x %f Graphicall" % (toggleSettings, theScale))
2521 fp.close()
2522 return
2524 ###################################################################################
2526 # Postprocessing of rigify rig
2528 # rigifyMhx(context):
2530 ###################################################################################
2532 class RigifyBone:
2533 def __init__(self, eb):
2534 self.name = eb.name
2535 self.realname = None
2536 self.realname1 = None
2537 self.realname2 = None
2538 self.fkname = None
2539 self.ikname = None
2541 self.head = eb.head.copy()
2542 self.tail = eb.tail.copy()
2543 self.roll = eb.roll
2544 self.deform = eb.use_deform
2545 self.parent = None
2546 self.child = None
2547 self.connect = False
2548 self.original = False
2549 self.extra = (eb.name in ["spine-1"])
2551 def __repr__(self):
2552 return ("<RigifyBone %s %s %s>" % (self.name, self.realname, self.realname1))
2555 def rigifyMhx(context):
2556 global theArmature
2557 from collections import OrderedDict
2559 print("Modifying MHX rig to Rigify")
2560 scn = context.scene
2561 ob = context.object
2562 if ob.type == 'ARMATURE':
2563 rig = ob
2564 elif ob.type == 'MESH':
2565 rig = ob.parent
2566 else:
2567 rig = None
2568 if not(rig and rig.type == 'ARMATURE'):
2569 raise NameError("Rigify: %s is neither an armature nor has armature parent" % ob)
2570 rig.MhxRigify = True
2571 scn.objects.active = rig
2573 group = None
2574 for grp in bpy.data.groups:
2575 if rig.name in grp.objects:
2576 group = grp
2577 break
2578 print("Group: %s" % group)
2580 # Setup info about MHX bones
2581 bones = OrderedDict()
2582 bpy.ops.object.mode_set(mode='EDIT')
2583 for eb in rig.data.edit_bones:
2584 bone = bones[eb.name] = RigifyBone(eb)
2585 if eb.parent:
2586 bone.parent = eb.parent.name
2587 bones[bone.parent].child = eb.name
2588 bpy.ops.object.mode_set(mode='OBJECT')
2590 # Create metarig
2591 try:
2592 bpy.ops.object.armature_human_metarig_add()
2593 except AttributeError:
2594 raise MyError("The Rigify add-on is not enabled. It is found under rigging.")
2595 bpy.ops.object.location_clear()
2596 bpy.ops.object.rotation_clear()
2597 bpy.ops.object.scale_clear()
2598 bpy.ops.transform.resize(value=(100, 100, 100))
2599 bpy.ops.object.transform_apply(location=False, rotation=False, scale=True)
2601 # Fit metarig to default MHX rig
2602 meta = context.object
2603 bpy.ops.object.mode_set(mode='EDIT')
2604 extra = []
2605 for bone in bones.values():
2606 try:
2607 eb = meta.data.edit_bones[bone.name]
2608 except KeyError:
2609 eb = None
2610 if eb:
2611 eb.head = bone.head
2612 eb.tail = bone.tail
2613 eb.roll = bone.roll
2614 bone.original = True
2615 elif bone.extra:
2616 extra.append(bone.name)
2617 bone.original = True
2618 eb = meta.data.edit_bones.new(bone.name)
2619 eb.use_connect = False
2620 eb.head = bones[bone.parent].tail
2621 eb.tail = bones[bone.child].head
2622 eb.roll = bone.roll
2623 parent = meta.data.edit_bones[bone.parent]
2624 child = meta.data.edit_bones[bone.child]
2625 child.parent = eb
2626 child.head = bones[bone.child].head
2627 parent.tail = bones[bone.parent].tail
2628 eb.parent = parent
2629 eb.use_connect = True
2631 # Add rigify properties to extra bones
2632 bpy.ops.object.mode_set(mode='OBJECT')
2633 for bname in extra:
2634 pb = meta.pose.bones[bname]
2635 pb["rigify_type"] = ""
2637 # Generate rigify rig
2638 bpy.ops.pose.rigify_generate()
2639 gen = context.object
2640 print("Generated", gen)
2641 scn.objects.unlink(meta)
2642 del meta
2644 for bone in bones.values():
2645 if bone.original:
2646 setBoneName(bone, gen)
2648 # Add extra bone to generated rig
2649 bpy.ops.object.mode_set(mode='EDIT')
2650 layers = 32*[False]
2651 layers[1] = True
2652 for bone in bones.values():
2653 if not bone.original:
2654 if bone.deform:
2655 bone.realname = "DEF-" + bone.name
2656 else:
2657 bone.realname = "MCH-" + bone.name
2658 eb = gen.data.edit_bones.new(bone.realname)
2659 eb.head = bone.head
2660 eb.tail = bone.tail
2661 eb.roll = bone.roll
2662 eb.use_deform = bone.deform
2663 if bone.parent:
2664 parent = bones[bone.parent]
2665 if parent.realname:
2666 eb.parent = gen.data.edit_bones[parent.realname]
2667 elif parent.realname1:
2668 eb.parent = gen.data.edit_bones[parent.realname1]
2669 else:
2670 print(bone)
2671 eb.use_connect = (eb.parent != None and eb.parent.tail == eb.head)
2672 eb.layers = layers
2674 bpy.ops.object.mode_set(mode='OBJECT')
2675 for bone in bones.values():
2676 if not bone.original:
2677 pb = gen.pose.bones[bone.realname]
2678 db = rig.pose.bones[bone.name]
2679 pb.rotation_mode = db.rotation_mode
2680 for cns1 in db.constraints:
2681 cns2 = pb.constraints.new(cns1.type)
2682 fixConstraint(cns1, cns2, gen, bones)
2684 # Add MHX properties
2685 for key in rig.keys():
2686 gen[key] = rig[key]
2688 # Copy MHX bone drivers
2689 if rig.animation_data:
2690 for fcu1 in rig.animation_data.drivers:
2691 rna,channel = fcu1.data_path.rsplit(".", 1)
2692 pb = mhxEval("gen.%s" % rna)
2693 fcu2 = pb.driver_add(channel, fcu1.array_index)
2694 copyDriver(fcu1, fcu2, gen)
2696 # Copy MHX morph drivers and change armature modifier
2697 for ob in rig.children:
2698 if ob.type == 'MESH':
2699 ob.parent = gen
2701 if ob.data.animation_data:
2702 for fcu in ob.data.animation_data.drivers:
2703 print(ob, fcu.data_path)
2704 changeDriverTarget(fcu, gen)
2706 if ob.data.shape_keys and ob.data.shape_keys.animation_data:
2707 for fcu in ob.data.shape_keys.animation_data.drivers:
2708 print(skey, fcu.data_path)
2709 changeDriverTarget(fcu, gen)
2711 for mod in ob.modifiers:
2712 if mod.type == 'ARMATURE' and mod.object == rig:
2713 mod.object = gen
2715 if group:
2716 group.objects.link(gen)
2718 # Parent widgets under empty
2719 empty = bpy.data.objects.new("Widgets", None)
2720 scn.objects.link(empty)
2721 empty.layers = 20*[False]
2722 empty.layers[19] = True
2723 empty.parent = gen
2724 for ob in scn.objects:
2725 if ob.type == 'MESH' and ob.name[0:4] == "WGT-" and not ob.parent:
2726 ob.parent = empty
2727 grp.objects.link(ob)
2729 #Clean up
2730 gen.show_x_ray = True
2731 gen.data.draw_type = 'STICK'
2732 gen.MhxRigify = False
2733 name = rig.name
2734 scn.objects.unlink(rig)
2735 del rig
2736 gen.name = name
2737 bpy.ops.object.mode_set(mode='POSE')
2738 theArmature = gen
2739 print("MHX rig %s successfully rigified" % name)
2743 def setBoneName(bone, gen):
2744 fkname = bone.name.replace(".", ".fk.")
2745 try:
2746 gen.data.bones[fkname]
2747 bone.fkname = fkname
2748 bone.ikname = fkname.replace(".fk.", ".ik")
2749 except KeyError:
2750 pass
2752 defname = "DEF-" + bone.name
2753 try:
2754 gen.data.bones[defname]
2755 bone.realname = defname
2756 return
2757 except KeyError:
2758 pass
2760 defname1 = "DEF-" + bone.name + ".01"
2761 try:
2762 gen.data.bones[defname1]
2763 bone.realname1 = defname1
2764 bone.realname2 = defname1.replace(".01.", ".02.")
2765 return
2766 except KeyError:
2767 pass
2769 defname1 = "DEF-" + bone.name.replace(".", ".01.")
2770 try:
2771 gen.data.bones[defname1]
2772 bone.realname1 = defname1
2773 bone.realname2 = defname1.replace(".01.", ".02")
2774 return
2775 except KeyError:
2776 pass
2778 try:
2779 gen.data.edit_bones[bone.name]
2780 bone.realname = bone.name
2781 except KeyError:
2782 pass
2785 def fixConstraint(cns1, cns2, gen, bones):
2786 for key in dir(cns1):
2787 if ((key[0] != "_") and
2788 (key not in ["bl_rna", "type", "rna_type", "is_valid", "error_location", "error_rotation"])):
2789 expr = ("cns2.%s = cns1.%s" % (key, key))
2790 setattr(cns2, key, getattr(cns1, key))
2792 cns2.target = gen
2794 if cns1.type == 'STRETCH_TO':
2795 bone = bones[cns1.subtarget]
2796 if bone.realname:
2797 cns2.subtarget = bone.realname
2798 cns2.head_tail = cns1.head_tail
2799 elif not bone.realname1:
2800 print(bone)
2801 halt
2802 elif cns1.head_tail < 0.5:
2803 cns2.subtarget = bone.realname1
2804 cns2.head_tail = 2*cns1.head_tail
2805 else:
2806 cns2.subtarget = bone.realname2
2807 cns2.head_tail = 2*cns1.head_tail-1
2809 elif cns1.type == 'TRANSFORM':
2810 bone = bones[cns1.subtarget]
2811 if bone.fkname:
2812 cns2.subtarget = bone.fkname
2813 elif bone.ikname:
2814 cns2.subtarget = bone.ikname
2815 else:
2816 cns2.subtarget = bone.realname
2819 def copyDriver(fcu1, fcu2, id):
2820 drv1 = fcu1.driver
2821 drv2 = fcu2.driver
2823 for var1 in drv1.variables:
2824 var2 = drv2.variables.new()
2825 var2.name = var1.name
2826 var2.type = var1.type
2827 targ1 = var1.targets[0]
2828 targ2 = var2.targets[0]
2829 targ2.id = id
2830 targ2.data_path = targ1.data_path
2832 drv2.type = drv1.type
2833 drv2.expression = drv1.expression
2834 drv2.show_debug_info = drv1.show_debug_info
2837 def changeDriverTarget(fcu, id):
2838 for var in fcu.driver.variables:
2839 targ = var.targets[0]
2840 targ.id = id
2844 # class OBJECT_OT_RigifyMhxButton(bpy.types.Operator):
2847 class OBJECT_OT_RigifyMhxButton(bpy.types.Operator):
2848 bl_idname = "mhxrig.rigify_mhx"
2849 bl_label = "Rigify MHX rig"
2850 bl_options = {'UNDO'}
2852 def execute(self, context):
2853 rigifyMhx(context)
2854 return{'FINISHED'}
2857 # class RigifyMhxPanel(bpy.types.Panel):
2860 class RigifyMhxPanel(bpy.types.Panel):
2861 bl_label = "Rigify MHX"
2862 bl_space_type = "VIEW_3D"
2863 bl_region_type = "UI"
2865 @classmethod
2866 def poll(cls, context):
2867 return (context.object and context.object.MhxRigify)
2869 def draw(self, context):
2870 self.layout.operator("mhxrig.rigify_mhx")
2871 return
2873 ###################################################################################
2875 # Error popup
2877 ###################################################################################
2879 DEBUG = False
2880 from bpy.props import StringProperty, FloatProperty, EnumProperty, BoolProperty
2882 class ErrorOperator(bpy.types.Operator):
2883 bl_idname = "mhx.error"
2884 bl_label = "Error when loading MHX file"
2886 def execute(self, context):
2887 return {'RUNNING_MODAL'}
2889 def invoke(self, context, event):
2890 global theErrorLines
2891 maxlen = 0
2892 for line in theErrorLines:
2893 if len(line) > maxlen:
2894 maxlen = len(line)
2895 width = 20+5*maxlen
2896 height = 20+5*len(theErrorLines)
2897 #self.report({'INFO'}, theMessage)
2898 wm = context.window_manager
2899 return wm.invoke_props_dialog(self, width=width, height=height)
2901 def draw(self, context):
2902 global theErrorLines
2903 for line in theErrorLines:
2904 self.layout.label(line)
2906 def MyError(message):
2907 global theMessage, theErrorLines, theErrorStatus
2908 theMessage = message
2909 theErrorLines = message.split('\n')
2910 theErrorStatus = True
2911 bpy.ops.mhx.error('INVOKE_DEFAULT')
2912 raise MhxError(theMessage)
2914 class MhxError(Exception):
2915 def __init__(self, value):
2916 self.value = value
2917 def __str__(self):
2918 return repr(self.value)
2920 class SuccessOperator(bpy.types.Operator):
2921 bl_idname = "mhx.success"
2922 bl_label = "MHX file successfully loaded:"
2923 message = StringProperty()
2925 def execute(self, context):
2926 return {'RUNNING_MODAL'}
2928 def invoke(self, context, event):
2929 wm = context.window_manager
2930 return wm.invoke_props_dialog(self)
2932 def draw(self, context):
2933 self.layout.label(self.message + theMessage)
2935 ###################################################################################
2937 # User interface
2939 ###################################################################################
2941 from bpy_extras.io_utils import ImportHelper, ExportHelper
2943 MhxBoolProps = [
2944 ("enforce", "Enforce version", "Only accept MHX files of correct version", T_EnforceVersion),
2945 #("crash_safe", "Crash-safe", "Disable features that have caused Blender crashes", T_CrashSafe),
2946 ("mesh", "Mesh", "Use main mesh", T_Mesh),
2947 ("proxy", "Proxies", "Use proxies", T_Proxy),
2948 #("armature", "Armature", "Use armature", T_Armature),
2949 #("replace", "Replace scene", "Replace scene", T_Replace),
2950 ("cage", "Cage", "Load mesh deform cage", T_Cage),
2951 ("clothes", "Clothes", "Include clothes", T_Clothes),
2952 ("shapekeys", "Shapekeys", "Include shapekeys", T_Shapekeys),
2953 ("shapedrivers", "Shapekey drivers", "Include shapekey drivers", T_ShapeDrivers),
2954 #("symm", "Symmetric shapes", "Keep shapekeys symmetric", T_Symm),
2955 ("diamond", "Helper geometry", "Keep helper geometry", T_Diamond),
2956 ("rigify", "Rigify", "Create rigify control rig", T_Rigify),
2959 class ImportMhx(bpy.types.Operator, ImportHelper):
2960 """Import from MHX file format (.mhx)"""
2961 bl_idname = "import_scene.makehuman_mhx"
2962 bl_description = 'Import from MHX file format (.mhx)'
2963 bl_label = "Import MHX"
2964 bl_space_type = "PROPERTIES"
2965 bl_region_type = "WINDOW"
2966 bl_options = {'UNDO'}
2968 filename_ext = ".mhx"
2969 filter_glob = StringProperty(default="*.mhx", options={'HIDDEN'})
2970 filepath = StringProperty(subtype='FILE_PATH')
2972 scale = FloatProperty(name="Scale", description="Default meter, decimeter = 1.0", default = theScale)
2973 advanced = BoolProperty(name="Advanced settings", description="Use advanced import settings", default=False)
2974 for (prop, name, desc, flag) in MhxBoolProps:
2975 expr = '%s = BoolProperty(name="%s", description="%s", default=(toggleSettings&%s != 0))' % (prop, name, desc, flag)
2976 exec(expr) # Trusted source: this file.
2979 def draw(self, context):
2980 layout = self.layout
2981 layout.prop(self, "scale")
2982 layout.prop(self, "advanced")
2983 if self.advanced:
2984 for (prop, name, desc, flag) in MhxBoolProps:
2985 layout.prop(self, prop)
2988 def execute(self, context):
2989 global toggle, toggleSettings, theScale, MhxBoolProps
2990 if not self.advanced:
2991 toggle = DefaultToggle
2992 else:
2993 toggle = T_Armature
2994 for (prop, name, desc, flag) in MhxBoolProps:
2995 expr = '(%s if self.%s else 0)' % (flag, prop)
2996 toggle |= eval(expr) # trusted source: this file
2997 toggleSettings = toggle
2998 print("execute flags %x" % toggle)
2999 theScale = self.scale
3001 #filepathname = self.filepath.encode('utf-8', 'strict')
3002 try:
3003 if not context.user_preferences.system.use_scripts_auto_execute:
3004 MyError("Auto Run Python Scripts must be turned on.\nIt is found under\n File > User Preferences > File")
3005 readMhxFile(self.filepath)
3006 #bpy.ops.mhx.success('INVOKE_DEFAULT', message = self.filepath)
3007 except MhxError:
3008 print("Error when loading MHX file %s:\n" % self.filepath + theMessage)
3010 if self.advanced:
3011 writeDefaults()
3012 self.advanced = False
3013 return {'FINISHED'}
3016 def invoke(self, context, event):
3017 global toggle, theScale, MhxBoolProps
3018 readDefaults()
3019 self.scale = theScale
3020 for (prop, name, desc, flag) in MhxBoolProps:
3021 setattr(self, prop, mhxEval('(toggle&%s != 0)' % flag))
3022 context.window_manager.fileselect_add(self)
3023 return {'RUNNING_MODAL'}
3026 ###################################################################################
3028 # Main panel
3030 ###################################################################################
3032 MhxLayers = [
3033 (( 0, 'Root', 'MhxRoot'),
3034 ( 1, 'Spine', 'MhxFKSpine')),
3035 ((10, 'Head', 'MhxHead'),
3036 ( 8, 'Face', 'MhxFace')),
3037 (( 9, 'Tweak', 'MhxTweak'),
3038 (16, 'Clothes', 'MhxClothes')),
3039 #((17, 'IK Spine', 'MhxIKSpine'),
3040 #((13, 'Inv FK Spine', 'MhxInvFKSpine')),
3041 ('Left', 'Right'),
3042 (( 2, 'IK Arm', 'MhxIKArm'),
3043 (18, 'IK Arm', 'MhxIKArm')),
3044 (( 3, 'FK Arm', 'MhxFKArm'),
3045 (19, 'FK Arm', 'MhxFKArm')),
3046 (( 4, 'IK Leg', 'MhxIKLeg'),
3047 (20, 'IK Leg', 'MhxIKLeg')),
3048 (( 5, 'FK Leg', 'MhxFKLeg'),
3049 (21, 'FK Leg', 'MhxFKLeg')),
3050 ((12, 'Extra', 'MhxExtra'),
3051 (28, 'Extra', 'MhxExtra')),
3052 (( 6, 'Fingers', 'MhxFingers'),
3053 (22, 'Fingers', 'MhxFingers')),
3054 (( 7, 'Links', 'MhxLinks'),
3055 (23, 'Links', 'MhxLinks')),
3056 ((11, 'Palm', 'MhxPalm'),
3057 (27, 'Palm', 'MhxPalm')),
3060 OtherLayers = [
3061 (( 1, 'Spine', 'MhxFKSpine'),
3062 ( 10, 'Head', 'MhxHead')),
3063 (( 9, 'Tweak', 'MhxTweak'),
3064 ( 8, 'Face', 'MhxFace')),
3065 ('Left', 'Right'),
3066 (( 3, 'Arm', 'MhxFKArm'),
3067 (19, 'Arm', 'MhxFKArm')),
3068 (( 5, 'Leg', 'MhxFKLeg'),
3069 (21, 'Leg', 'MhxFKLeg')),
3070 (( 7, 'Fingers', 'MhxLinks'),
3071 (23, 'Fingers', 'MhxLinks')),
3072 ((11, 'Palm', 'MhxPalm'),
3073 (27, 'Palm', 'MhxPalm')),
3078 # class MhxMainPanel(bpy.types.Panel):
3081 class MhxMainPanel(bpy.types.Panel):
3082 bl_label = "MHX Main v %s" % bl_info["version"]
3083 bl_space_type = "VIEW_3D"
3084 bl_region_type = "UI"
3085 #bl_options = {'DEFAULT_CLOSED'}
3087 @classmethod
3088 def poll(cls, context):
3089 return (context.object and context.object.MhxRig)
3091 def draw(self, context):
3092 layout = self.layout
3093 layout.label("Layers")
3094 layout.operator("mhx.pose_enable_all_layers")
3095 layout.operator("mhx.pose_disable_all_layers")
3097 rig = context.object
3098 if rig.MhxRig == 'MHX':
3099 layers = MhxLayers
3100 else:
3101 layers = OtherLayers
3103 for (left,right) in layers:
3104 row = layout.row()
3105 if type(left) == str:
3106 row.label(left)
3107 row.label(right)
3108 else:
3109 for (n, name, prop) in [left,right]:
3110 row.prop(rig.data, "layers", index=n, toggle=True, text=name)
3112 layout.separator()
3113 layout.label("Export/Import MHP")
3114 layout.operator("mhx.saveas_mhp")
3115 layout.operator("mhx.load_mhp")
3118 class VIEW3D_OT_MhxEnableAllLayersButton(bpy.types.Operator):
3119 bl_idname = "mhx.pose_enable_all_layers"
3120 bl_label = "Enable all layers"
3121 bl_options = {'UNDO'}
3123 def execute(self, context):
3124 rig,mesh = getMhxRigMesh(context.object)
3125 for (left,right) in MhxLayers:
3126 if type(left) != str:
3127 for (n, name, prop) in [left,right]:
3128 rig.data.layers[n] = True
3129 return{'FINISHED'}
3132 class VIEW3D_OT_MhxDisableAllLayersButton(bpy.types.Operator):
3133 bl_idname = "mhx.pose_disable_all_layers"
3134 bl_label = "Disable all layers"
3135 bl_options = {'UNDO'}
3137 def execute(self, context):
3138 rig,mesh = getMhxRigMesh(context.object)
3139 layers = 32*[False]
3140 pb = context.active_pose_bone
3141 if pb:
3142 for n in range(32):
3143 if pb.bone.layers[n]:
3144 layers[n] = True
3145 break
3146 else:
3147 layers[0] = True
3148 if rig:
3149 rig.data.layers = layers
3150 return{'FINISHED'}
3154 def saveMhpFile(rig, scn, filepath):
3155 roots = []
3156 for pb in rig.pose.bones:
3157 if pb.parent is None:
3158 roots.append(pb)
3160 (pname, ext) = os.path.splitext(filepath)
3161 mhppath = pname + ".mhp"
3163 fp = open(mhppath, "w", encoding="utf-8", newline="\n")
3164 for root in roots:
3165 writeMhpBones(fp, root, None)
3166 fp.close()
3167 print("Mhp file %s saved" % mhppath)
3170 def writeMhpBones(fp, pb, log):
3171 if not isMuscleBone(pb):
3172 b = pb.bone
3173 if pb.parent:
3174 mat = b.matrix_local.inverted() * b.parent.matrix_local * pb.parent.matrix.inverted() * pb.matrix
3175 else:
3176 mat = pb.matrix.copy()
3177 maty = mat[1].copy()
3178 matz = mat[2].copy()
3179 mat[1] = matz
3180 mat[2] = -maty
3182 diff = mat - Matrix()
3183 nonzero = False
3184 for i in range(4):
3185 if abs(diff[i].length) > 5e-3:
3186 nonzero = True
3187 break
3189 if nonzero:
3190 fp.write("%s\tmatrix" % pb.name)
3191 for i in range(4):
3192 row = mat[i]
3193 fp.write("\t%.4f\t%.4f\t%.4f\t%.4f" % (row[0], row[1], row[2], row[3]))
3194 fp.write("\n")
3196 for child in pb.children:
3197 writeMhpBones(fp, child, log)
3200 def isMuscleBone(pb):
3201 layers = pb.bone.layers
3202 if (layers[14] or layers[15] or layers[30] or layers[31]):
3203 return True
3204 for cns in pb.constraints:
3205 if (cns.type == 'STRETCH_TO' or
3206 cns.type == 'TRANSFORM' or
3207 cns.type == 'TRACK_TO' or
3208 cns.type == 'IK' or
3209 cns.type[0:5] == 'COPY_'):
3210 return True
3211 return False
3214 def loadMhpFile(rig, scn, filepath):
3215 unit = Matrix()
3216 for pb in rig.pose.bones:
3217 pb.matrix_basis = unit
3219 (pname, ext) = os.path.splitext(filepath)
3220 mhppath = pname + ".mhp"
3222 fp = open(mhppath, "rU")
3223 for line in fp:
3224 words = line.split()
3225 if len(words) < 4:
3226 continue
3228 try:
3229 pb = rig.pose.bones[words[0]]
3230 except KeyError:
3231 print("Warning: Did not find bone %s" % words[0])
3232 continue
3234 if isMuscleBone(pb):
3235 pass
3236 elif words[1] == "quat":
3237 q = Quaternion((float(words[2]), float(words[3]), float(words[4]), float(words[5])))
3238 mat = q.to_matrix().to_4x4()
3239 pb.matrix_basis = mat
3240 elif words[1] == "gquat":
3241 q = Quaternion((float(words[2]), float(words[3]), float(words[4]), float(words[5])))
3242 mat = q.to_matrix().to_4x4()
3243 maty = mat[1].copy()
3244 matz = mat[2].copy()
3245 mat[1] = -matz
3246 mat[2] = maty
3247 pb.matrix_basis = pb.bone.matrix_local.inverted() * mat
3248 elif words[1] == "matrix":
3249 rows = []
3250 n = 2
3251 for i in range(4):
3252 rows.append((float(words[n]), float(words[n+1]), float(words[n+2]), float(words[n+3])))
3253 n += 4
3254 mat = Matrix(rows)
3255 if pb.parent:
3256 pb.matrix_basis = mat
3257 else:
3258 maty = mat[1].copy()
3259 matz = mat[2].copy()
3260 mat[1] = -matz
3261 mat[2] = maty
3262 pb.matrix_basis = pb.bone.matrix_local.inverted() * mat
3263 elif words[1] == "scale":
3264 pass
3265 else:
3266 print("WARNING: Unknown line in mcp file:\n%s" % line)
3267 fp.close()
3268 print("Mhp file %s loaded" % mhppath)
3271 class VIEW3D_OT_LoadMhpButton(bpy.types.Operator):
3272 bl_idname = "mhx.load_mhp"
3273 bl_label = "Load MHP File"
3274 bl_description = "Load a pose in MHP format"
3275 bl_options = {'UNDO'}
3277 filename_ext = ".mhp"
3278 filter_glob = StringProperty(default="*.mhp", options={'HIDDEN'})
3279 filepath = bpy.props.StringProperty(
3280 name="File Path",
3281 description="File path used for mhp file",
3282 maxlen= 1024, default= "")
3284 def execute(self, context):
3285 loadMhpFile(context.object, context.scene, self.properties.filepath)
3286 return {'FINISHED'}
3288 def invoke(self, context, event):
3289 context.window_manager.fileselect_add(self)
3290 return {'RUNNING_MODAL'}
3293 class VIEW3D_OT_SaveasMhpFileButton(bpy.types.Operator, ExportHelper):
3294 bl_idname = "mhx.saveas_mhp"
3295 bl_label = "Save MHP File"
3296 bl_description = "Save current pose in MHP format"
3297 bl_options = {'UNDO'}
3299 filename_ext = ".mhp"
3300 filter_glob = StringProperty(default="*.mhp", options={'HIDDEN'})
3301 filepath = bpy.props.StringProperty(
3302 name="File Path",
3303 description="File path used for mhp file",
3304 maxlen= 1024, default= "")
3306 def execute(self, context):
3307 saveMhpFile(context.object, context.scene, self.properties.filepath)
3308 return {'FINISHED'}
3310 def invoke(self, context, event):
3311 context.window_manager.fileselect_add(self)
3312 return {'RUNNING_MODAL'}
3314 ###################################################################################
3316 # Lipsync panel
3318 ###################################################################################
3321 # visemes
3324 bodyLanguageVisemes = ({
3325 'Rest' : [
3326 ('MouthWidth_L', 0),
3327 ('MouthWidth_R', 0),
3328 ('MouthNarrow_L', 0),
3329 ('MouthNarrow_R', 0),
3330 ('LipsPart', 0.6),
3331 ('UpLipsMidHeight', 0),
3332 ('LoLipsMidHeight', 0),
3333 ('LoLipsIn', 0),
3334 ('MouthOpen', 0),
3335 ('TongueBackHeight', 0),
3336 ('TongueHeight', 0),
3338 'Etc' : [
3339 ('MouthWidth_L', 0),
3340 ('MouthWidth_R', 0),
3341 ('MouthNarrow_L', 0),
3342 ('MouthNarrow_R', 0),
3343 ('LipsPart', 0.4),
3344 ('UpLipsMidHeight', 0),
3345 ('LoLipsMidHeight', 0),
3346 ('LoLipsIn', 0),
3347 ('MouthOpen', 0),
3348 ('TongueBackHeight', 0),
3349 ('TongueHeight', 0),
3351 'MBP' : [
3352 ('MouthWidth_L', 0),
3353 ('MouthWidth_R', 0),
3354 ('MouthNarrow_L', 0),
3355 ('MouthNarrow_R', 0),
3356 ('LipsPart', 0),
3357 ('UpLipsMidHeight', 0),
3358 ('LoLipsMidHeight', 0),
3359 ('LoLipsIn', 0),
3360 ('MouthOpen', 0),
3361 ('TongueBackHeight', 0),
3362 ('TongueHeight', 0),
3364 'OO' : [
3365 ('MouthWidth_L', 0),
3366 ('MouthWidth_R', 0),
3367 ('MouthNarrow_L', 1.0),
3368 ('MouthNarrow_R', 1.0),
3369 ('LipsPart', 0),
3370 ('UpLipsMidHeight', 0),
3371 ('LoLipsMidHeight', 0),
3372 ('LoLipsIn', 0),
3373 ('MouthOpen', 0.4),
3374 ('TongueBackHeight', 0),
3375 ('TongueHeight', 0),
3377 'O' : [
3378 ('MouthWidth_L', 0),
3379 ('MouthWidth_R', 0),
3380 ('MouthNarrow_L', 0.9),
3381 ('MouthNarrow_R', 0.9),
3382 ('LipsPart', 0),
3383 ('UpLipsMidHeight', 0),
3384 ('LoLipsMidHeight', 0),
3385 ('LoLipsIn', 0),
3386 ('MouthOpen', 0.8),
3387 ('TongueBackHeight', 0),
3388 ('TongueHeight', 0),
3390 'R' : [
3391 ('MouthWidth_L', 0),
3392 ('MouthWidth_R', 0),
3393 ('MouthNarrow_L', 0.5),
3394 ('MouthNarrow_R', 0.5),
3395 ('LipsPart', 0),
3396 ('UpLipsMidHeight', 0.2),
3397 ('LoLipsMidHeight', -0.2),
3398 ('LoLipsIn', 0),
3399 ('MouthOpen', 0),
3400 ('TongueBackHeight', 0),
3401 ('TongueHeight', 0),
3403 'FV' : [
3404 ('MouthWidth_L', 0.2),
3405 ('MouthWidth_R', 0.2),
3406 ('MouthNarrow_L', 0),
3407 ('MouthNarrow_R', 0),
3408 ('LipsPart', 1.0),
3409 ('UpLipsMidHeight', 0),
3410 ('LoLipsMidHeight', 0.3),
3411 ('LoLipsIn', 0.6),
3412 ('MouthOpen', 0),
3413 ('TongueBackHeight', 0),
3414 ('TongueHeight', 0),
3416 'S' : [
3417 ('MouthWidth_L', 0),
3418 ('MouthWidth_R', 0),
3419 ('MouthNarrow_L', 0),
3420 ('MouthNarrow_R', 0),
3421 ('LipsPart', 0),
3422 ('UpLipsMidHeight', 0.5),
3423 ('LoLipsMidHeight', -0.7),
3424 ('LoLipsIn', 0),
3425 ('MouthOpen', 0),
3426 ('TongueBackHeight', 0),
3427 ('TongueHeight', 0),
3429 'SH' : [
3430 ('MouthWidth_L', 0.8),
3431 ('MouthWidth_R', 0.8),
3432 ('MouthNarrow_L', 0),
3433 ('MouthNarrow_R', 0),
3434 ('LipsPart', 0),
3435 ('UpLipsMidHeight', 1.0),
3436 ('LoLipsMidHeight', 0),
3437 ('LoLipsIn', 0),
3438 ('MouthOpen', 0),
3439 ('TongueBackHeight', 0),
3440 ('TongueHeight', 0),
3442 'EE' : [
3443 ('MouthWidth_L', 0.2),
3444 ('MouthWidth_R', 0.2),
3445 ('MouthNarrow_L', 0),
3446 ('MouthNarrow_R', 0),
3447 ('LipsPart', 0),
3448 ('UpLipsMidHeight', 0.6),
3449 ('LoLipsMidHeight', -0.6),
3450 ('LoLipsIn', 0),
3451 ('MouthOpen', 0.5),
3452 ('TongueBackHeight', 0),
3453 ('TongueHeight', 0),
3455 'AH' : [
3456 ('MouthWidth_L', 0),
3457 ('MouthWidth_R', 0),
3458 ('MouthNarrow_L', 0),
3459 ('MouthNarrow_R', 0),
3460 ('LipsPart', 0),
3461 ('UpLipsMidHeight', 0.4),
3462 ('LoLipsMidHeight', 0),
3463 ('LoLipsIn', 0),
3464 ('MouthOpen', 0.7),
3465 ('TongueBackHeight', 0),
3466 ('TongueHeight', 0),
3468 'EH' : [
3469 ('MouthWidth_L', 0),
3470 ('MouthWidth_R', 0),
3471 ('MouthNarrow_L', 0),
3472 ('MouthNarrow_R', 0),
3473 ('LipsPart', 0),
3474 ('UpLipsMidHeight', 0.5),
3475 ('LoLipsMidHeight', -0.6),
3476 ('LoLipsIn', 0),
3477 ('MouthOpen', 0.25),
3478 ('TongueBackHeight', 0),
3479 ('TongueHeight', 0),
3481 'TH' : [
3482 ('MouthWidth_L', 0),
3483 ('MouthWidth_R', 0),
3484 ('MouthNarrow_L', 0),
3485 ('MouthNarrow_R', 0),
3486 ('LipsPart', 0),
3487 ('UpLipsMidHeight', 0),
3488 ('LoLipsMidHeight', 0),
3489 ('LoLipsIn', 0),
3490 ('MouthOpen', 0.2),
3491 ('TongueBackHeight', 1.0),
3492 ('TongueHeight', 1.0)
3494 'L' : [
3495 ('MouthWidth_L', 0),
3496 ('MouthWidth_R', 0),
3497 ('MouthNarrow_L', 0),
3498 ('MouthNarrow_R', 0),
3499 ('LipsPart', 0),
3500 ('UpLipsMidHeight', 0.5),
3501 ('LoLipsMidHeight', -0.5),
3502 ('LoLipsIn', 0),
3503 ('MouthOpen', -0.2),
3504 ('TongueBackHeight', 1.0),
3505 ('TongueHeight', 1.0),
3507 'G' : [
3508 ('MouthWidth_L', 0),
3509 ('MouthWidth_R', 0),
3510 ('MouthNarrow_L', 0),
3511 ('MouthNarrow_R', 0),
3512 ('LipsPart', 0),
3513 ('UpLipsMidHeight', 0.5),
3514 ('LoLipsMidHeight', -0.5),
3515 ('LoLipsIn', 0),
3516 ('MouthOpen', -0.2),
3517 ('TongueBackHeight', 1.0),
3518 ('TongueHeight', 0),
3521 'Blink' : [
3522 ('UpLidUp_L', 1),
3523 ('UpLidUp_R', 1),
3524 ('LoLidDown_L', 1),
3525 ('LoLidDown_R', 1)
3528 'Unblink' : [
3529 ('UpLidUp_L', 0),
3530 ('UpLidUp_R', 0),
3531 ('LoLidDown_L', 0),
3532 ('LoLidDown_R', 0)
3536 VisemePanelBones = {
3537 'MouthOpen' : ('PJaw', (0,0.25)),
3538 'UpLipsMidHeight' : ('PUpLipMid', (0,-0.25)),
3539 'LoLipsMidHeight' : ('PLoLipMid', (0,-0.25)),
3540 'LoLipsIn': ('PLoLipMid', (-0.25,0)),
3541 'MouthWidth_L' : ('PMouth_L', (0.25,0)),
3542 'MouthWidth_R' : ('PMouth_R', (-0.25,0)),
3543 'MouthNarrow_L' : ('PMouth_L', (-0.25,0)),
3544 'MouthNarrow_R' : ('PMouth_R', (0.25,0)),
3545 'LipsPart' : ('PMouthMid', (0, -0.25)),
3546 'TongueBackHeight': ('PTongue', (-0.25, 0)),
3547 'TongueHeight' : ('PTongue', (0, -0.25)),
3549 'UpLidUp_L' : ('PUpLid_L', (0,1.0)),
3550 'UpLidUp_R' : ('PUpLid_R', (0,1.0)),
3551 'LoLidDown_L' : ('PLoLid_L', (0,-1.0)),
3552 'LoLidDown_R' : ('PLoLid_R', (0,-1.0)),
3555 VisemeList = [
3556 ('Rest', 'Etc', 'AH'),
3557 ('MBP', 'OO', 'O'),
3558 ('R', 'FV', 'S'),
3559 ('SH', 'EE', 'EH'),
3560 ('TH', 'L', 'G')
3564 # makeVisemes(ob, scn):
3565 # class VIEW3D_OT_MhxMakeVisemesButton(bpy.types.Operator):
3568 def makeVisemes(ob, scn):
3569 rig = ob.parent
3570 if ob.type != 'MESH':
3571 print("Active object %s is not a mesh" % ob)
3572 return
3573 if not ob.data.shape_keys:
3574 print("%s has no shapekeys" % ob)
3575 return
3576 try:
3577 ob.data.shape_keys.key_blocks["VIS_Rest"]
3578 print("Visemes already created")
3579 return
3580 except KeyError:
3581 pass
3582 try:
3583 ob.data.shape_keys.key_blocks["MouthOpen"]
3584 except KeyError:
3585 print("Mesh does not have face shapes")
3586 return
3588 verts = ob.data.vertices
3589 for (vis,shapes) in bodyLanguageVisemes.items():
3590 if vis in ['Blink', 'Unblink']:
3591 continue
3592 vkey = ob.shape_key_add(name="VIS_%s" % vis)
3593 print(vkey.name)
3594 for n,v in enumerate(verts):
3595 vkey.data[n].co = v.co
3596 for (name,value) in shapes:
3597 if name[-2:] == "_R":
3598 continue
3599 skey = ob.data.shape_keys.key_blocks[name]
3600 factor = 0.75*value
3601 for n,v in enumerate(verts):
3602 vkey.data[n].co += factor*(skey.data[n].co - v.co)
3603 print("Visemes made")
3604 return
3606 class VIEW3D_OT_MhxMakeVisemesButton(bpy.types.Operator):
3607 bl_idname = "mhx.make_visemes"
3608 bl_label = "Generate viseme shapekeys"
3609 bl_options = {'UNDO'}
3611 def execute(self, context):
3612 makeVisemes(context.object, context.scene)
3613 return{'FINISHED'}
3616 # mohoVisemes
3617 # magpieVisemes
3620 MohoVisemes = dict({
3621 "rest" : "Rest",
3622 "etc" : "Etc",
3623 "AI" : "AH",
3624 "O" : "O",
3625 "U" : "OO",
3626 "WQ" : "AH",
3627 "L" : "L",
3628 "E" : "EH",
3629 "MBP" : "MBP",
3630 "FV" : "FV",
3633 MagpieVisemes = dict({
3634 "CONS" : "Etc",
3635 "AI" : "AH",
3636 "E" : "EH",
3637 "O" : "O",
3638 "UW" : "AH",
3639 "MBP" : "MBP",
3640 "L" : "L",
3641 "FV" : "FV",
3642 "Sh" : "SH",
3646 # setViseme(context, vis, setKey, frame):
3647 # setBoneLocation(context, pbone, loc, mirror, setKey, frame):
3648 # class VIEW3D_OT_MhxVisemeButton(bpy.types.Operator):
3651 def getVisemeSet(context, rig):
3652 if rig.MhxVisemeSet == "StopStaring":
3653 return stopStaringVisemes
3654 elif rig.MhxVisemeSet == "BodyLanguage":
3655 return bodyLanguageVisemes
3656 else:
3657 raise MhxError("Unknown viseme set %s" % visset)
3660 def setVisemeAlpha7(context, vis, visemes, setKey, frame):
3661 (rig, mesh) = getMhxRigMesh(context.object)
3662 isPanel = False
3663 isProp = False
3664 shapekeys = None
3665 scale = 0.75
3666 if rig.MhxShapekeyDrivers:
3667 try:
3668 scale *= rig.pose.bones['PFace'].bone.length
3669 isPanel = True
3670 except:
3671 isProp = True
3672 elif mesh:
3673 shapekeys = mesh.data.shape_keys.key_blocks
3675 for (skey, value) in visemes[vis]:
3676 if isPanel:
3677 (b, (x,z)) = VisemePanelBones[skey]
3678 loc = mathutils.Vector((float(x*value),0,float(z*value)))
3679 pb = rig.pose.bones[b]
3680 pb.location = loc*scale
3681 if setKey or context.tool_settings.use_keyframe_insert_auto:
3682 for n in range(3):
3683 pb.keyframe_insert('location', index=n, frame=frame, group=pb.name)
3684 elif isProp:
3685 skey = 'Mhf' + skey
3686 try:
3687 prop = rig[skey]
3688 except:
3689 continue
3690 rig[skey] = value*scale
3691 if setKey or context.tool_settings.use_keyframe_insert_auto:
3692 rig.keyframe_insert('["%s"]' % skey, frame=frame, group="Visemes")
3693 elif shapekeys:
3694 try:
3695 shapekeys[skey].value = value*scale
3696 except:
3697 continue
3698 if setKey or context.tool_settings.use_keyframe_insert_auto:
3699 shapekeys[skey].keyframe_insert("value", frame=frame)
3700 updatePose(context)
3701 return
3704 class VIEW3D_OT_MhxVisemeButton(bpy.types.Operator):
3705 bl_idname = 'mhx.pose_viseme'
3706 bl_label = 'Viseme'
3707 bl_options = {'UNDO'}
3708 viseme = StringProperty()
3710 def invoke(self, context, event):
3711 (rig, mesh) = getMhxRigMesh(context.object)
3712 visemes = getVisemeSet(context, rig)
3713 setVisemeAlpha7(context, self.viseme, visemes, False, context.scene.frame_current)
3714 return{'FINISHED'}
3717 def readLipsync(context, filepath, offs, struct):
3718 (rig, mesh) = getMhxRigMesh(context.object)
3719 if rig.MhxVisemeSet:
3720 visemes = getVisemeSet(context, rig)
3721 else:
3722 props = getProps(rig, "Mhv")
3723 visemes = {}
3724 oldKeys = []
3725 for prop in props:
3726 dummy,units = getUnitsFromString("x;"+rig[prop])
3727 visemes[prop] = units
3728 props = getProps(rig, "Mhsmouth")
3729 auto = context.tool_settings.use_keyframe_insert_auto
3730 auto = True
3731 factor = rig.MhxStrength
3732 shapekeys = getMhmShapekeys(rig, mesh)
3733 context.scene.objects.active = rig
3734 bpy.ops.object.mode_set(mode='POSE')
3736 fp = open(filepath, "rU")
3737 for line in fp:
3738 words= line.split()
3739 if len(words) < 2:
3740 continue
3741 else:
3742 vis = struct[words[1]]
3743 frame = int(words[0])+offs
3744 if rig.MhxVisemeSet:
3745 setVisemeAlpha7(context, vis, visemes, True, frame)
3746 else:
3747 setMhmProps(rig, shapekeys, "Mhsmouth", visemes["Mhv"+vis], factor, auto, frame)
3748 fp.close()
3750 #setInterpolation(rig)
3751 updatePose(context)
3752 print("Lipsync file %s loaded" % filepath)
3755 class VIEW3D_OT_MhxMohoButton(bpy.types.Operator, ImportHelper):
3756 bl_idname = "mhx.pose_load_moho"
3757 bl_label = "Load Moho (.dat)"
3758 bl_options = {'UNDO'}
3760 filename_ext = ".dat"
3761 filter_glob = StringProperty(default="*.dat", options={'HIDDEN'})
3762 filepath = StringProperty(subtype='FILE_PATH')
3764 def execute(self, context):
3765 readLipsync(context, self.properties.filepath, context.scene.frame_start - 1, MohoVisemes)
3766 return{'FINISHED'}
3768 def invoke(self, context, event):
3769 context.window_manager.fileselect_add(self)
3770 return {'RUNNING_MODAL'}
3773 class MhxLipsyncPanel(bpy.types.Panel):
3774 bl_label = "MHX Lipsync"
3775 bl_space_type = "VIEW_3D"
3776 bl_region_type = "UI"
3777 bl_options = {'DEFAULT_CLOSED'}
3779 @classmethod
3780 def poll(cls, context):
3781 return hasProps(context.object, "Mhv")
3783 def draw(self, context):
3784 rig,mesh = getMhxRigMesh(context.object)
3785 if not rig:
3786 layout.label("No MHX rig found")
3787 return
3788 layout = self.layout
3790 if not rig.MhxVisemeSet:
3791 visemes = getProps(rig, "Mhv")
3792 if not visemes:
3793 layout.label("No visemes found")
3794 return
3796 layout.operator("mhx.pose_reset_expressions", text="Reset visemes").prefix="Mhsmouth"
3797 layout.operator("mhx.pose_key_expressions", text="Key visemes").prefix="Mhsmouth"
3798 layout.prop(rig, "MhxStrength")
3799 layout.separator()
3800 n = 0
3801 for prop in visemes:
3802 if n % 3 == 0:
3803 row = layout.row()
3804 n = 0
3805 row.operator("mhx.pose_mhm", text=prop[3:]).data="Mhsmouth;"+rig[prop]
3806 n += 1
3807 while n % 3 != 0:
3808 row.label("")
3809 n += 1
3810 layout.separator()
3811 row = layout.row()
3812 row.operator("mhx.pose_mhm", text="Blink").data="Mhsmouth;eye_left_closure:1;eye_right_closure:1"
3813 row.operator("mhx.pose_mhm", text="Unblink").data="Mhsmouth;eye_left_closure:0;eye_right_closure:0"
3814 else:
3815 for (vis1, vis2, vis3) in VisemeList:
3816 row = layout.row()
3817 row.operator("mhx.pose_viseme", text=vis1).viseme = vis1
3818 row.operator("mhx.pose_viseme", text=vis2).viseme = vis2
3819 row.operator("mhx.pose_viseme", text=vis3).viseme = vis3
3820 layout.separator()
3821 row = layout.row()
3822 row.operator("mhx.pose_viseme", text="Blink").viseme = 'Blink'
3823 row.operator("mhx.pose_viseme", text="Unblink").viseme = 'Unblink'
3824 layout.separator()
3825 layout.operator("mhx.make_visemes")
3827 layout.separator()
3828 row = layout.row()
3829 row.operator("mhx.pose_load_moho")
3830 #layout.operator("mhx.update")
3833 # updatePose(context):
3834 # class VIEW3D_OT_MhxUpdateButton(bpy.types.Operator):
3837 def updatePose(context):
3838 scn = context.scene
3839 scn.frame_current = scn.frame_current
3840 bpy.ops.object.posemode_toggle()
3841 bpy.ops.object.posemode_toggle()
3842 return
3844 class VIEW3D_OT_MhxUpdateButton(bpy.types.Operator):
3845 bl_idname = "mhx.update"
3846 bl_label = "Update"
3848 def execute(self, context):
3849 updatePose(context)
3850 return{'FINISHED'}
3853 ###################################################################################
3855 # Expression panels
3857 ###################################################################################
3859 class VIEW3D_OT_MhxResetExpressionsButton(bpy.types.Operator):
3860 bl_idname = "mhx.pose_reset_expressions"
3861 bl_label = "Reset expressions"
3862 bl_options = {'UNDO'}
3863 prefix = StringProperty()
3865 def execute(self, context):
3866 rig,mesh = getMhxRigMesh(context.object)
3867 shapekeys = getMhmShapekeys(rig, mesh)
3868 clearMhmProps(rig, shapekeys, self.prefix, context.tool_settings.use_keyframe_insert_auto, context.scene.frame_current)
3869 updatePose(context)
3870 return{'FINISHED'}
3873 class VIEW3D_OT_MhxKeyExpressionsButton(bpy.types.Operator):
3874 bl_idname = "mhx.pose_key_expressions"
3875 bl_label = "Key expressions"
3876 bl_options = {'UNDO'}
3877 prefix = StringProperty()
3879 def execute(self, context):
3880 rig,mesh = getMhxRigMesh(context.object)
3881 props = getProps(rig, self.prefix)
3882 frame = context.scene.frame_current
3883 for prop in props:
3884 rig.keyframe_insert('["%s"]'%prop, frame=frame)
3885 updatePose(context)
3886 return{'FINISHED'}
3889 class VIEW3D_OT_MhxPinExpressionButton(bpy.types.Operator):
3890 bl_idname = "mhx.pose_pin_expression"
3891 bl_label = "Pin"
3892 bl_options = {'UNDO'}
3893 data = StringProperty()
3895 def execute(self, context):
3896 rig,mesh = getMhxRigMesh(context.object)
3897 words = self.data.split(";")
3898 prefix = words[0]
3899 expression = words[1]
3901 props = getProps(rig, prefix)
3902 if context.tool_settings.use_keyframe_insert_auto:
3903 frame = context.scene.frame_current
3904 for prop in props:
3905 old = rig[prop]
3906 if prop == expression:
3907 rig[prop] = 1.0
3908 else:
3909 rig[prop] = 0.0
3910 if abs(rig[prop] - old) > 1e-3:
3911 rig.keyframe_insert('["%s"]'%prop, frame=frame)
3912 else:
3913 for prop in props:
3914 if prop == expression:
3915 rig[prop] = 1.0
3916 else:
3917 rig[prop] = 0.0
3918 updatePose(context)
3919 return{'FINISHED'}
3922 def getMhmShapekeys(rig, mesh):
3923 if rig.MhxShapekeyDrivers:
3924 return None
3925 else:
3926 return mesh.data.shape_keys.key_blocks
3929 def setMhmProps(rig, shapekeys, prefix, units, factor, auto, frame):
3930 clearMhmProps(rig, shapekeys, prefix, auto, frame)
3931 for (prop, value) in units:
3932 if shapekeys:
3933 skey = prop[3:].replace("_","-")
3934 shapekeys[skey].value = factor*value
3935 if auto:
3936 shapekeys[skey].keyframe_insert("value", frame=frame)
3937 else:
3938 rig[prop] = factor*value
3939 if auto:
3940 rig.keyframe_insert('["%s"]'%prop, frame=frame)
3943 def clearMhmProps(rig, shapekeys, prefix, auto, frame):
3944 props = getProps(rig, prefix)
3945 for prop in props:
3946 if shapekeys:
3947 skey = prop[3:].replace("_","-")
3948 shapekeys[skey].value = 0.0
3949 if auto:
3950 shapekeys[skey].keyframe_insert("value", frame=frame)
3951 else:
3952 rig[prop] = 0.0
3953 if auto:
3954 rig.keyframe_insert('["%s"]'%prop, frame=frame)
3957 def getUnitsFromString(string):
3958 words = string.split(";")
3959 prefix = words[0]
3960 units = []
3961 for word in words[1:]:
3962 if word == "":
3963 continue
3964 unit = word.split(":")
3965 prop = "Mhs" + unit[0]
3966 value = float(unit[1])
3967 units.append((prop, value))
3968 return prefix,units
3971 class VIEW3D_OT_MhxMhmButton(bpy.types.Operator):
3972 bl_idname = "mhx.pose_mhm"
3973 bl_label = "Mhm"
3974 bl_options = {'UNDO'}
3975 data = StringProperty()
3977 def execute(self, context):
3978 rig,mesh = getMhxRigMesh(context.object)
3979 auto = context.tool_settings.use_keyframe_insert_auto
3980 frame = context.scene.frame_current
3981 shapekeys = getMhmShapekeys(rig, mesh)
3982 prefix,units = getUnitsFromString(self.data)
3983 setMhmProps(rig, shapekeys, prefix, units, rig.MhxStrength, auto, frame)
3984 updatePose(context)
3985 return{'FINISHED'}
3988 def getProps(rig, prefix):
3989 props = []
3990 for prop in rig.keys():
3991 if prop.startswith(prefix):
3992 props.append(prop)
3993 props.sort()
3994 return props
3997 def hasProps(ob, prefix):
3998 if ob is None:
3999 return False
4000 if ob.type == 'MESH' and ob.parent:
4001 rig = ob.parent
4002 elif ob.type == 'ARMATURE':
4003 rig = ob
4004 else:
4005 return False
4006 for prop in rig.keys():
4007 if prop.startswith(prefix):
4008 return True
4009 return False
4012 class MhxExpressionsPanel(bpy.types.Panel):
4013 bl_label = "MHX Expressions"
4014 bl_space_type = "VIEW_3D"
4015 bl_region_type = "UI"
4016 bl_options = {'DEFAULT_CLOSED'}
4018 @classmethod
4019 def poll(cls, context):
4020 return hasProps(context.object, "Mhe")
4022 def draw(self, context):
4023 layout = self.layout
4024 rig,mesh = getMhxRigMesh(context.object)
4025 if not rig:
4026 layout.label("No MHX rig found")
4027 return
4028 exprs = getProps(rig, "Mhe")
4029 if not exprs:
4030 layout.label("No expressions found")
4031 return
4033 layout.operator("mhx.pose_reset_expressions").prefix="Mhs"
4034 layout.operator("mhx.pose_key_expressions").prefix="Mhs"
4035 layout.prop(rig, "MhxStrength")
4036 layout.separator()
4037 for prop in exprs:
4038 layout.operator("mhx.pose_mhm", text=prop[3:]).data="Mhs;"+rig[prop]
4041 def drawShapePanel(self, context, prefix, name):
4042 layout = self.layout
4043 rig,mesh = getMhxRigMesh(context.object)
4044 if not rig:
4045 print("No MHX rig found")
4046 return
4047 if not rig.MhxShapekeyDrivers:
4048 layout.label("No shapekey drivers.")
4049 layout.label("Set %s values in mesh context instead" % name)
4050 return
4051 props = getProps(rig, prefix)
4052 if not props:
4053 layout.label("No %ss found" % name)
4054 return
4056 layout.operator("mhx.pose_reset_expressions", text="Reset %ss" % name).prefix=prefix
4057 layout.operator("mhx.pose_key_expressions", text="Key %ss" % name).prefix=prefix
4058 #layout.operator("mhx.update")
4060 layout.separator()
4061 for prop in props:
4062 row = layout.split(0.85)
4063 row.prop(rig, '["%s"]' % prop, text=prop[3:])
4064 row.operator("mhx.pose_pin_expression", text="", icon='UNPINNED').data = (prefix + ";" + prop)
4065 return
4068 class MhxExpressionUnitsPanel(bpy.types.Panel):
4069 bl_label = "MHX Expression Tuning"
4070 bl_space_type = "VIEW_3D"
4071 bl_region_type = "UI"
4072 bl_options = {'DEFAULT_CLOSED'}
4074 @classmethod
4075 def poll(cls, context):
4076 return hasProps(context.object, "Mhs")
4078 def draw(self, context):
4079 drawShapePanel(self, context, "Mhs", "expression")
4082 class MhxCustomShapePanel(bpy.types.Panel):
4083 bl_label = "MHX Custom Shapes"
4084 bl_space_type = "VIEW_3D"
4085 bl_region_type = "UI"
4086 bl_options = {'DEFAULT_CLOSED'}
4088 @classmethod
4089 def poll(cls, context):
4090 return hasProps(context.object, "Mhc")
4092 def draw(self, context):
4093 drawShapePanel(self, context, "Mhc", "custom shape")
4096 #########################################
4098 # FK-IK snapping.
4100 #########################################
4102 def getPoseMatrix(gmat, pb):
4103 restInv = pb.bone.matrix_local.inverted()
4104 if pb.parent:
4105 parInv = pb.parent.matrix.inverted()
4106 parRest = pb.parent.bone.matrix_local
4107 return restInv * (parRest * (parInv * gmat))
4108 else:
4109 return restInv * gmat
4112 def getGlobalMatrix(mat, pb):
4113 gmat = pb.bone.matrix_local * mat
4114 if pb.parent:
4115 parMat = pb.parent.matrix
4116 parRest = pb.parent.bone.matrix_local
4117 return parMat * (parRest.inverted() * gmat)
4118 else:
4119 return gmat
4122 def matchPoseTranslation(pb, src, auto):
4123 pmat = getPoseMatrix(src.matrix, pb)
4124 insertLocation(pb, pmat, auto)
4127 def insertLocation(pb, mat, auto):
4128 pb.location = mat.to_translation()
4129 if auto:
4130 pb.keyframe_insert("location", group=pb.name)
4131 bpy.ops.object.mode_set(mode='OBJECT')
4132 bpy.ops.object.mode_set(mode='POSE')
4135 def matchPoseRotation(pb, src, auto):
4136 pmat = getPoseMatrix(src.matrix, pb)
4137 insertRotation(pb, pmat, auto)
4140 def printMatrix(string,mat):
4141 print(string)
4142 for i in range(4):
4143 print(" %.4g %.4g %.4g %.4g" % tuple(mat[i]))
4146 def insertRotation(pb, mat, auto):
4147 q = mat.to_quaternion()
4148 if pb.rotation_mode == 'QUATERNION':
4149 pb.rotation_quaternion = q
4150 if auto:
4151 pb.keyframe_insert("rotation_quaternion", group=pb.name)
4152 else:
4153 pb.rotation_euler = q.to_euler(pb.rotation_mode)
4154 if auto:
4155 pb.keyframe_insert("rotation_euler", group=pb.name)
4156 bpy.ops.object.mode_set(mode='OBJECT')
4157 bpy.ops.object.mode_set(mode='POSE')
4160 def matchIkLeg(legIk, toeFk, mBall, mToe, mHeel, auto):
4161 rmat = toeFk.matrix.to_3x3()
4162 tHead = Vector(toeFk.matrix.col[3][:3])
4163 ty = rmat.col[1]
4164 tail = tHead + ty * toeFk.bone.length
4166 try:
4167 zBall = mBall.matrix.col[3][2]
4168 except AttributeError:
4169 return
4170 zToe = mToe.matrix.col[3][2]
4171 zHeel = mHeel.matrix.col[3][2]
4173 x = Vector(rmat.col[0])
4174 y = Vector(rmat.col[1])
4175 z = Vector(rmat.col[2])
4177 if zHeel > zBall and zHeel > zToe:
4178 # 1. foot.ik is flat
4179 if abs(y[2]) > abs(z[2]):
4180 y = -z
4181 y[2] = 0
4182 else:
4183 # 2. foot.ik starts at heel
4184 hHead = Vector(mHeel.matrix.col[3][:3])
4185 y = tail - hHead
4187 y.normalize()
4188 x -= x.dot(y)*y
4189 x.normalize()
4190 z = x.cross(y)
4191 head = tail - y * legIk.bone.length
4193 # Create matrix
4194 gmat = Matrix()
4195 gmat.col[0][:3] = x
4196 gmat.col[1][:3] = y
4197 gmat.col[2][:3] = z
4198 gmat.col[3][:3] = head
4199 pmat = getPoseMatrix(gmat, legIk)
4201 insertLocation(legIk, pmat, auto)
4202 insertRotation(legIk, pmat, auto)
4205 def matchPoleTarget(pb, above, below, auto):
4206 x = Vector(above.matrix.col[1][:3])
4207 y = Vector(below.matrix.col[1][:3])
4208 p0 = Vector(below.matrix.col[3][:3])
4209 n = x.cross(y)
4210 if abs(n.length) > 1e-4:
4211 z = x - y
4212 n = n/n.length
4213 z -= z.dot(n)*n
4214 p = p0 + z/z.length*3.0
4215 else:
4216 p = p0
4217 gmat = Matrix.Translation(p)
4218 pmat = getPoseMatrix(gmat, pb)
4219 insertLocation(pb, pmat, auto)
4222 def matchPoseReverse(pb, src, auto):
4223 gmat = src.matrix
4224 tail = gmat.col[3] + src.length * gmat.col[1]
4225 rmat = Matrix((gmat.col[0], -gmat.col[1], -gmat.col[2], tail))
4226 rmat.transpose()
4227 pmat = getPoseMatrix(rmat, pb)
4228 pb.matrix_basis = pmat
4229 insertRotation(pb, pmat, auto)
4232 def matchPoseScale(pb, src, auto):
4233 pmat = getPoseMatrix(src.matrix, pb)
4234 pb.scale = pmat.to_scale()
4235 if auto:
4236 pb.keyframe_insert("scale", group=pb.name)
4237 bpy.ops.object.mode_set(mode='OBJECT')
4238 bpy.ops.object.mode_set(mode='POSE')
4241 def snapFkArm(context, data):
4242 rig = context.object
4243 prop,old,suffix = setSnapProp(rig, data, 1.0, context, False)
4244 auto = context.scene.tool_settings.use_keyframe_insert_auto
4246 print("Snap FK Arm%s" % suffix)
4247 snapFk,cnsFk = getSnapBones(rig, "ArmFK", suffix)
4248 (uparmFk, loarmFk, handFk) = snapFk
4249 muteConstraints(cnsFk, True)
4250 snapIk,cnsIk = getSnapBones(rig, "ArmIK", suffix)
4251 (uparmIk, loarmIk, elbow, elbowPt, handIk) = snapIk
4253 matchPoseRotation(uparmFk, uparmIk, auto)
4254 matchPoseScale(uparmFk, uparmIk, auto)
4256 matchPoseRotation(loarmFk, loarmIk, auto)
4257 matchPoseScale(loarmFk, loarmIk, auto)
4259 restoreSnapProp(rig, prop, old, context)
4261 try:
4262 matchHand = rig["MhaHandFollowsWrist" + suffix]
4263 except KeyError:
4264 matchHand = True
4265 if matchHand:
4266 matchPoseRotation(handFk, handIk, auto)
4267 matchPoseScale(handFk, handIk, auto)
4269 #muteConstraints(cnsFk, False)
4270 return
4273 def snapIkArm(context, data):
4274 rig = context.object
4275 prop,old,suffix = setSnapProp(rig, data, 0.0, context, True)
4276 auto = context.scene.tool_settings.use_keyframe_insert_auto
4278 print("Snap IK Arm%s" % suffix)
4279 snapIk,cnsIk = getSnapBones(rig, "ArmIK", suffix)
4280 (uparmIk, loarmIk, elbow, elbowPt, handIk) = snapIk
4281 snapFk,cnsFk = getSnapBones(rig, "ArmFK", suffix)
4282 (uparmFk, loarmFk, handFk) = snapFk
4283 muteConstraints(cnsIk, True)
4285 matchPoseTranslation(handIk, handFk, auto)
4286 matchPoseRotation(handIk, handFk, auto)
4288 matchPoleTarget(elbowPt, uparmFk, loarmFk, auto)
4290 #matchPoseRotation(uparmIk, uparmFk, auto)
4291 #matchPoseRotation(loarmIk, loarmFk, auto)
4293 restoreSnapProp(rig, prop, old, context)
4294 #muteConstraints(cnsIk, False)
4295 return
4298 def snapFkLeg(context, data):
4299 rig = context.object
4300 prop,old,suffix = setSnapProp(rig, data, 1.0, context, False)
4301 auto = context.scene.tool_settings.use_keyframe_insert_auto
4303 print("Snap FK Leg%s" % suffix)
4304 snap,_ = getSnapBones(rig, "Leg", suffix)
4305 (upleg, loleg, foot, toe) = snap
4306 snapIk,cnsIk = getSnapBones(rig, "LegIK", suffix)
4307 (uplegIk, lolegIk, kneePt, ankleIk, legIk, footRev, toeRev, mBall, mToe, mHeel) = snapIk
4308 snapFk,cnsFk = getSnapBones(rig, "LegFK", suffix)
4309 (uplegFk, lolegFk, footFk, toeFk) = snapFk
4310 muteConstraints(cnsFk, True)
4312 matchPoseRotation(uplegFk, uplegIk, auto)
4313 matchPoseScale(uplegFk, uplegIk, auto)
4315 matchPoseRotation(lolegFk, lolegIk, auto)
4316 matchPoseScale(lolegFk, lolegIk, auto)
4318 restoreSnapProp(rig, prop, old, context)
4320 if not rig["MhaLegIkToAnkle" + suffix]:
4321 matchPoseReverse(footFk, footRev, auto)
4322 matchPoseReverse(toeFk, toeRev, auto)
4324 #muteConstraints(cnsFk, False)
4325 return
4328 def snapIkLeg(context, data):
4329 rig = context.object
4330 scn = context.scene
4331 prop,old,suffix = setSnapProp(rig, data, 0.0, context, True)
4332 auto = scn.tool_settings.use_keyframe_insert_auto
4334 print("Snap IK Leg%s" % suffix)
4335 snapIk,cnsIk = getSnapBones(rig, "LegIK", suffix)
4336 (uplegIk, lolegIk, kneePt, ankleIk, legIk, footRev, toeRev, mBall, mToe, mHeel) = snapIk
4337 snapFk,cnsFk = getSnapBones(rig, "LegFK", suffix)
4338 (uplegFk, lolegFk, footFk, toeFk) = snapFk
4339 muteConstraints(cnsIk, True)
4341 legIkToAnkle = rig["MhaLegIkToAnkle" + suffix]
4342 if legIkToAnkle:
4343 matchPoseTranslation(ankleIk, footFk, auto)
4345 #matchPoseTranslation(legIk, legFk, auto)
4346 #matchPoseRotation(legIk, legFk, auto)
4347 matchIkLeg(legIk, toeFk, mBall, mToe, mHeel, auto)
4349 matchPoseReverse(toeRev, toeFk, auto)
4350 matchPoseReverse(footRev, footFk, auto)
4352 matchPoleTarget(kneePt, uplegFk, lolegFk, auto)
4354 #matchPoseRotation(uplegIk, uplegFk, auto)
4355 #matchPoseRotation(lolegIk, lolegFk, auto)
4357 if not legIkToAnkle:
4358 matchPoseTranslation(ankleIk, footFk, auto)
4360 restoreSnapProp(rig, prop, old, context)
4361 #muteConstraints(cnsIk, False)
4362 return
4365 SnapBonesAlpha8 = {
4366 "Arm" : ["upper_arm", "forearm", "hand"],
4367 "ArmFK" : ["upper_arm.fk", "forearm.fk", "hand.fk"],
4368 "ArmIK" : ["upper_arm.ik", "forearm.ik", None, "elbow.pt.ik", "hand.ik"],
4369 "Leg" : ["thigh", "shin", "foot", "toe"],
4370 "LegFK" : ["thigh.fk", "shin.fk", "foot.fk", "toe.fk"],
4371 "LegIK" : ["thigh.ik", "shin.ik", "knee.pt.ik", "ankle.ik", "foot.ik", "foot.rev", "toe.rev", "ball.marker", "toe.marker", "heel.marker"],
4375 def getSnapBones(rig, key, suffix):
4376 try:
4377 pb = rig.pose.bones["UpLeg_L"]
4378 except KeyError:
4379 pb = None
4381 if pb is not None:
4382 raise NameError("MakeHuman alpha 7 not supported after Blender 2.68")
4384 try:
4385 rig.pose.bones["thigh.fk.L"]
4386 names = SnapBonesAlpha8[key]
4387 suffix = '.' + suffix[1:]
4388 except KeyError:
4389 names = None
4391 if not names:
4392 raise NameError("Not an mhx armature")
4394 pbones = []
4395 constraints = []
4396 for name in names:
4397 if name:
4398 try:
4399 pb = rig.pose.bones[name+suffix]
4400 except KeyError:
4401 pb = None
4402 pbones.append(pb)
4403 if pb is not None:
4404 for cns in pb.constraints:
4405 if cns.type == 'LIMIT_ROTATION' and not cns.mute:
4406 constraints.append(cns)
4407 else:
4408 pbones.append(None)
4409 return tuple(pbones),constraints
4412 def muteConstraints(constraints, value):
4413 for cns in constraints:
4414 cns.mute = value
4417 class VIEW3D_OT_MhxSnapFk2IkButton(bpy.types.Operator):
4418 bl_idname = "mhx.snap_fk_ik"
4419 bl_label = "Snap FK"
4420 bl_options = {'UNDO'}
4421 data = StringProperty()
4423 def execute(self, context):
4424 bpy.ops.object.mode_set(mode='POSE')
4425 rig = context.object
4426 if rig.MhxSnapExact:
4427 rig["MhaRotationLimits"] = 0.0
4428 if self.data[:6] == "MhaArm":
4429 snapFkArm(context, self.data)
4430 elif self.data[:6] == "MhaLeg":
4431 snapFkLeg(context, self.data)
4432 return{'FINISHED'}
4435 class VIEW3D_OT_MhxSnapIk2FkButton(bpy.types.Operator):
4436 bl_idname = "mhx.snap_ik_fk"
4437 bl_label = "Snap IK"
4438 bl_options = {'UNDO'}
4439 data = StringProperty()
4441 def execute(self, context):
4442 bpy.ops.object.mode_set(mode='POSE')
4443 rig = context.object
4444 if rig.MhxSnapExact:
4445 rig["MhaRotationLimits"] = 0.0
4446 if self.data[:6] == "MhaArm":
4447 snapIkArm(context, self.data)
4448 elif self.data[:6] == "MhaLeg":
4449 snapIkLeg(context, self.data)
4450 return{'FINISHED'}
4453 def setSnapProp(rig, data, value, context, isIk):
4454 words = data.split()
4455 prop = words[0]
4456 oldValue = rig[prop]
4457 rig[prop] = value
4458 ik = int(words[1])
4459 fk = int(words[2])
4460 extra = int(words[3])
4461 oldIk = rig.data.layers[ik]
4462 oldFk = rig.data.layers[fk]
4463 oldExtra = rig.data.layers[extra]
4464 rig.data.layers[ik] = True
4465 rig.data.layers[fk] = True
4466 rig.data.layers[extra] = True
4467 updatePose(context)
4468 if isIk:
4469 oldValue = 1.0
4470 oldIk = True
4471 oldFk = False
4472 else:
4473 oldValue = 0.0
4474 oldIk = False
4475 oldFk = True
4476 oldExtra = False
4477 return (prop, (oldValue, ik, fk, extra, oldIk, oldFk, oldExtra), prop[-2:])
4480 def restoreSnapProp(rig, prop, old, context):
4481 updatePose(context)
4482 (oldValue, ik, fk, extra, oldIk, oldFk, oldExtra) = old
4483 rig[prop] = oldValue
4484 rig.data.layers[ik] = oldIk
4485 rig.data.layers[fk] = oldFk
4486 rig.data.layers[extra] = oldExtra
4487 updatePose(context)
4488 return
4491 class VIEW3D_OT_MhxToggleFkIkButton(bpy.types.Operator):
4492 bl_idname = "mhx.toggle_fk_ik"
4493 bl_label = "FK - IK"
4494 bl_options = {'UNDO'}
4495 toggle = StringProperty()
4497 def execute(self, context):
4498 words = self.toggle.split()
4499 rig = context.object
4500 prop = words[0]
4501 value = float(words[1])
4502 onLayer = int(words[2])
4503 offLayer = int(words[3])
4504 rig.data.layers[onLayer] = True
4505 rig.data.layers[offLayer] = False
4506 rig[prop] = value
4507 # Don't do autokey - confusing.
4508 #if context.tool_settings.use_keyframe_insert_auto:
4509 # rig.keyframe_insert('["%s"]' % prop, frame=scn.frame_current)
4510 updatePose(context)
4511 return{'FINISHED'}
4514 # MHX FK/IK Switch panel
4517 class MhxFKIKPanel(bpy.types.Panel):
4518 bl_label = "MHX FK/IK Switch"
4519 bl_space_type = "VIEW_3D"
4520 bl_region_type = "UI"
4521 #bl_options = {'DEFAULT_CLOSED'}
4523 @classmethod
4524 def poll(cls, context):
4525 return (context.object and context.object.MhxRig == 'MHX')
4527 def draw(self, context):
4528 rig = context.object
4529 layout = self.layout
4531 row = layout.row()
4532 row.label("")
4533 row.label("Left")
4534 row.label("Right")
4536 layout.label("FK/IK switch")
4537 row = layout.row()
4538 row.label("Arm")
4539 self.toggleButton(row, rig, "MhaArmIk_L", " 3", " 2")
4540 self.toggleButton(row, rig, "MhaArmIk_R", " 19", " 18")
4541 row = layout.row()
4542 row.label("Leg")
4543 self.toggleButton(row, rig, "MhaLegIk_L", " 5", " 4")
4544 self.toggleButton(row, rig, "MhaLegIk_R", " 21", " 20")
4546 layout.label("IK Influence")
4547 row = layout.row()
4548 row.label("Arm")
4549 row.prop(rig, '["MhaArmIk_L"]', text="")
4550 row.prop(rig, '["MhaArmIk_R"]', text="")
4551 row = layout.row()
4552 row.label("Leg")
4553 row.prop(rig, '["MhaLegIk_L"]', text="")
4554 row.prop(rig, '["MhaLegIk_R"]', text="")
4556 try:
4557 ok = (rig["MhxVersion"] >= 12)
4558 except:
4559 ok = False
4560 if not ok:
4561 layout.label("Snapping only works with MHX version 1.12 and later.")
4562 return
4564 layout.separator()
4565 layout.label("Snapping")
4566 row = layout.row()
4567 row.label("Rotation Limits")
4568 row.prop(rig, '["MhaRotationLimits"]', text="")
4569 row.prop(rig, "MhxSnapExact", text="Exact Snapping")
4571 layout.label("Snap Arm bones")
4572 row = layout.row()
4573 row.label("FK Arm")
4574 row.operator("mhx.snap_fk_ik", text="Snap L FK Arm").data = "MhaArmIk_L 2 3 12"
4575 row.operator("mhx.snap_fk_ik", text="Snap R FK Arm").data = "MhaArmIk_R 18 19 28"
4576 row = layout.row()
4577 row.label("IK Arm")
4578 row.operator("mhx.snap_ik_fk", text="Snap L IK Arm").data = "MhaArmIk_L 2 3 12"
4579 row.operator("mhx.snap_ik_fk", text="Snap R IK Arm").data = "MhaArmIk_R 18 19 28"
4581 layout.label("Snap Leg bones")
4582 row = layout.row()
4583 row.label("FK Leg")
4584 row.operator("mhx.snap_fk_ik", text="Snap L FK Leg").data = "MhaLegIk_L 4 5 12"
4585 row.operator("mhx.snap_fk_ik", text="Snap R FK Leg").data = "MhaLegIk_R 20 21 28"
4586 row = layout.row()
4587 row.label("IK Leg")
4588 row.operator("mhx.snap_ik_fk", text="Snap L IK Leg").data = "MhaLegIk_L 4 5 12"
4589 row.operator("mhx.snap_ik_fk", text="Snap R IK Leg").data = "MhaLegIk_R 20 21 28"
4592 def toggleButton(self, row, rig, prop, fk, ik):
4593 if rig[prop] > 0.5:
4594 row.operator("mhx.toggle_fk_ik", text="IK").toggle = prop + " 0" + fk + ik
4595 else:
4596 row.operator("mhx.toggle_fk_ik", text="FK").toggle = prop + " 1" + ik + fk
4599 ###################################################################################
4601 # Posing panel
4603 ###################################################################################
4605 # class MhxDriversPanel(bpy.types.Panel):
4608 class MhxDriversPanel(bpy.types.Panel):
4609 bl_label = "MHX Drivers"
4610 bl_space_type = "VIEW_3D"
4611 bl_region_type = "UI"
4612 bl_options = {'DEFAULT_CLOSED'}
4614 @classmethod
4615 def poll(cls, context):
4616 return (context.object and context.object.MhxRig == 'MHX')
4618 def draw(self, context):
4619 lrProps = []
4620 props = []
4621 lrFaceProps = []
4622 faceProps = []
4623 plist = list(context.object.keys())
4624 plist.sort()
4625 for prop in plist:
4626 if prop[0:3] == 'Mha':
4627 if prop[-2:] == '_L':
4628 lrProps.append(prop[:-2])
4629 elif prop[-2:] != '_R':
4630 props.append(prop)
4631 elif prop[0:3] == 'Mhf':
4632 if prop[-2:] == '_L':
4633 lrFaceProps.append(prop[:-2])
4634 elif prop[-2:] != '_R':
4635 faceProps.append(prop)
4637 ob = context.object
4638 layout = self.layout
4639 for prop in props:
4640 layout.prop(ob, '["%s"]' % prop, text=prop[3:])
4642 layout.separator()
4643 row = layout.row()
4644 row.label("Left")
4645 row.label("Right")
4646 for prop in lrProps:
4647 row = layout.row()
4648 row.prop(ob, '["%s"]' % (prop+"_L"), text=prop[3:])
4649 row.prop(ob, '["%s"]' % (prop+"_R"), text=prop[3:])
4651 if faceProps:
4652 layout.separator()
4653 layout.label("Face shapes")
4654 for prop in faceProps:
4655 layout.prop(ob, '["%s"]' % prop, text=prop[3:])
4657 layout.separator()
4658 row = layout.row()
4659 row.label("Left")
4660 row.label("Right")
4661 for prop in lrFaceProps:
4662 row = layout.row()
4663 row.prop(ob, '["%s"]' % (prop+"_L"), text=prop[3:])
4664 row.prop(ob, '["%s"]' % (prop+"_R"), text=prop[3:])
4666 return
4668 ###################################################################################
4670 # Visibility panel
4672 ###################################################################################
4674 # class MhxVisibilityPanel(bpy.types.Panel):
4677 class MhxVisibilityPanel(bpy.types.Panel):
4678 bl_label = "MHX Visibility"
4679 bl_space_type = "VIEW_3D"
4680 bl_region_type = "UI"
4681 bl_options = {'DEFAULT_CLOSED'}
4683 @classmethod
4684 def poll(cls, context):
4685 return (context.object and context.object.MhxRig)
4687 def draw(self, context):
4688 ob = context.object
4689 layout = self.layout
4690 props = list(ob.keys())
4691 props.sort()
4692 for prop in props:
4693 if prop[0:3] == "Mhh":
4694 layout.prop(ob, '["%s"]' % prop, text="Hide %s" % prop[3:])
4695 layout.separator()
4696 layout.operator("mhx.update_textures")
4697 layout.separator()
4698 layout.operator("mhx.add_hiders")
4699 layout.operator("mhx.remove_hiders")
4700 return
4702 class VIEW3D_OT_MhxUpdateTexturesButton(bpy.types.Operator):
4703 bl_idname = "mhx.update_textures"
4704 bl_label = "Update"
4705 bl_options = {'UNDO'}
4707 def execute(self, context):
4708 scn = context.scene
4709 for mat in bpy.data.materials:
4710 if mat.animation_data:
4711 try:
4712 mat["MhxDriven"]
4713 except:
4714 continue
4715 for driver in mat.animation_data.drivers:
4716 prop = mat.path_resolve(driver.data_path)
4717 value = driver.evaluate(scn.frame_current)
4718 prop[driver.array_index] = value
4719 return{'FINISHED'}
4721 class VIEW3D_OT_MhxAddHidersButton(bpy.types.Operator):
4722 bl_idname = "mhx.add_hiders"
4723 bl_label = "Add Hide Property"
4724 bl_options = {'UNDO'}
4726 def execute(self, context):
4727 rig = context.object
4728 for ob in context.scene.objects:
4729 if ob.select and ob != rig:
4730 prop = "Mhh%s" % ob.name
4731 defNewProp(prop, "Bool", "default=False")
4732 rig[prop] = False
4733 addHider(ob, "hide", rig, prop)
4734 addHider(ob, "hide_render", rig, prop)
4735 return{'FINISHED'}
4737 def addHider(ob, attr, rig, prop):
4738 fcu = ob.driver_add(attr)
4739 drv = fcu.driver
4740 drv.type = 'SCRIPTED'
4741 drv.expression = "x"
4742 drv.show_debug_info = True
4743 var = drv.variables.new()
4744 var.name = "x"
4745 targ = var.targets[0]
4746 targ.id = rig
4747 targ.data_path = '["%s"]' % prop
4748 return
4750 class VIEW3D_OT_MhxRemoveHidersButton(bpy.types.Operator):
4751 bl_idname = "mhx.remove_hiders"
4752 bl_label = "Remove Hide Property"
4753 bl_options = {'UNDO'}
4755 def execute(self, context):
4756 rig = context.object
4757 for ob in context.scene.objects:
4758 if ob.select and ob != rig:
4759 ob.driver_remove("hide")
4760 ob.driver_remove("hide_render")
4761 del rig["Mhh%s" % ob.name]
4762 return{'FINISHED'}
4764 ###################################################################################
4766 # Common functions
4768 ###################################################################################
4770 # getMhxRigMesh(ob):
4773 def pollMhx(ob):
4774 if not ob:
4775 return False
4776 elif ob.type == 'ARMATURE':
4777 return ob.MhxRig
4778 elif ob.type == 'MESH':
4779 par = ob.parent
4780 return (par and (par.type == 'ARMATURE') and par.MhxRig)
4781 else:
4782 return False
4784 def getMhxRigMesh(ob):
4785 if ob.type == 'ARMATURE':
4786 for mesh in ob.children:
4787 if mesh.MhxMesh and ob.MhxRig:
4788 return (ob, mesh)
4789 return (ob, None)
4790 elif ob.type == 'MESH':
4791 par = ob.parent
4792 if (par and par.type == 'ARMATURE' and par.MhxRig):
4793 if ob.MhxMesh:
4794 return (par, ob)
4795 else:
4796 return (par, None)
4797 else:
4798 return (None, None)
4799 return (None, None)
4803 # setInterpolation(rig):
4806 def setInterpolation(rig):
4807 if not rig.animation_data:
4808 return
4809 act = rig.animation_data.action
4810 if not act:
4811 return
4812 for fcu in act.fcurves:
4813 for pt in fcu.keyframe_points:
4814 pt.interpolation = 'LINEAR'
4815 fcu.extrapolation = 'CONSTANT'
4816 return
4818 ###################################################################################
4820 # initialize and register
4822 ###################################################################################
4824 def menu_func(self, context):
4825 self.layout.operator(ImportMhx.bl_idname, text="MakeHuman (.mhx)...")
4827 def register():
4828 bpy.types.Object.MhxVersionStr = StringProperty(name="Version", default="", maxlen=128)
4829 bpy.types.Object.MhAlpha8 = BoolProperty(default=True)
4830 bpy.types.Object.MhxMesh = BoolProperty(default=False)
4831 bpy.types.Object.MhxRig = StringProperty(default="")
4832 bpy.types.Object.MhxVisemeSet = StringProperty(default="")
4833 bpy.types.Object.MhxRigify = BoolProperty(default=False)
4834 bpy.types.Object.MhxSnapExact = BoolProperty(default=False)
4835 bpy.types.Object.MhxShapekeyDrivers = BoolProperty(default=True)
4836 bpy.types.Object.MhxStrength = FloatProperty(
4837 name = "Expression strength",
4838 description = "Multiply expression with this factor",
4839 default=1.0, min=-1.0, max=2.0
4841 bpy.utils.register_module(__name__)
4842 bpy.types.INFO_MT_file_import.append(menu_func)
4844 def unregister():
4845 try:
4846 bpy.utils.unregister_module(__name__)
4847 except:
4848 pass
4849 try:
4850 bpy.types.INFO_MT_file_import.remove(menu_func)
4851 except:
4852 pass
4854 if __name__ == "__main__":
4855 unregister()
4856 register()