only apply modifiers if the mesh has modifiers
[blender-addons.git] / io_import_scene_mhx.py
blobdd66531e66540ff0230dee0dd5234924c7c4be7a
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.5x.
29 Version 1.14.0
31 This script should be distributed with Blender.
32 If not, place it in the .blender/scripts/addons dir
33 Activate the script in the "Addons" tab (user preferences).
34 Access from the File > Import menu.
36 Alternatively, run the script in the script editor (Alt-P), and access from the File > Import menu
37 """
39 bl_info = {
40 'name': 'Import: MakeHuman (.mhx)',
41 'author': 'Thomas Larsson',
42 'version': (1, 15, 1),
43 "blender": (2, 65, 0),
44 'location': "File > Import > MakeHuman (.mhx)",
45 'description': 'Import files in the MakeHuman eXchange format (.mhx)',
46 'warning': '',
47 'wiki_url': 'http://sites.google.com/site/makehumandocs/blender-export-and-mhx',
48 'tracker_url': 'https://projects.blender.org/tracker/index.php?'\
49 'func=detail&aid=21872',
50 'category': 'Import-Export'}
52 MAJOR_VERSION = 1
53 MINOR_VERSION = 15
54 FROM_VERSION = 13
55 SUB_VERSION = 1
61 import bpy
62 import os
63 import time
64 import math
65 import mathutils
66 from mathutils import Vector, Matrix
67 from bpy.props import *
69 MHX249 = False
70 Blender24 = False
71 Blender25 = True
72 theDir = "~/makehuman/exports"
78 theScale = 1.0
79 One = 1.0/theScale
80 useMesh = 1
81 verbosity = 2
82 warnedTextureDir = False
83 warnedVersion = False
85 true = True
86 false = False
87 Epsilon = 1e-6
88 nErrors = 0
89 theTempDatum = None
90 theMessage = ""
91 theMhxFile = ""
93 todo = []
96 # toggle flags
99 T_EnforceVersion = 0x01
100 T_Clothes = 0x02
101 T_HardParents = 0x0
102 T_CrashSafe = 0x0
104 T_Diamond = 0x10
105 T_Replace = 0x20
106 T_Shapekeys = 0x40
107 T_ShapeDrivers = 0x80
109 T_Face = T_Shapekeys
110 T_Shape = T_Shapekeys
112 T_Mesh = 0x100
113 T_Armature = 0x200
114 T_Proxy = 0x400
115 T_Cage = 0x800
117 T_Rigify = 0x1000
118 T_Opcns = 0x2000
119 T_Symm = 0x4000
121 DefaultToggle = ( T_EnforceVersion + T_Mesh + T_Armature +
122 T_Shapekeys + T_ShapeDrivers + T_Proxy + T_Clothes + T_Rigify )
124 toggle = DefaultToggle
125 toggleSettings = toggle
128 # Dictionaries
131 def initLoadedData():
132 global loadedData
134 loadedData = {
135 'NONE' : {},
137 'Object' : {},
138 'Mesh' : {},
139 'Armature' : {},
140 'Lamp' : {},
141 'Camera' : {},
142 'Lattice' : {},
143 'Curve' : {},
144 'Text' : {},
146 'Material' : {},
147 'Image' : {},
148 'MaterialTextureSlot' : {},
149 'Texture' : {},
151 'Bone' : {},
152 'BoneGroup' : {},
153 'Rigify' : {},
155 'Action' : {},
156 'Group' : {},
158 'MeshTextureFaceLayer' : {},
159 'MeshColorLayer' : {},
160 'VertexGroup' : {},
161 'ShapeKey' : {},
162 'ParticleSystem' : {},
164 'ObjectConstraints' : {},
165 'ObjectModifiers' : {},
166 'MaterialSlot' : {},
168 return
170 def reinitGlobalData():
171 global loadedData
172 for key in [
173 'MeshTextureFaceLayer', 'MeshColorLayer', 'VertexGroup', 'ShapeKey',
174 'ParticleSystem', 'ObjectConstraints', 'ObjectModifiers', 'MaterialSlot']:
175 loadedData[key] = {}
176 return
178 Plural = {
179 'Object' : 'objects',
180 'Mesh' : 'meshes',
181 'Lattice' : 'lattices',
182 'Curve' : 'curves',
183 'Text' : 'texts',
184 'Group' : 'groups',
185 'Empty' : 'empties',
186 'Armature' : 'armatures',
187 'Bone' : 'bones',
188 'BoneGroup' : 'bone_groups',
189 'Pose' : 'poses',
190 'PoseBone' : 'pose_bones',
191 'Material' : 'materials',
192 'Texture' : 'textures',
193 'Image' : 'images',
194 'Camera' : 'cameras',
195 'Lamp' : 'lamps',
196 'World' : 'worlds',
200 # readMhxFile(filePath):
203 def readMhxFile(filePath):
204 global todo, nErrors, theScale, theArmature, defaultScale, One
205 global toggle, warnedVersion, theMessage, alpha7, theDir
207 defaultScale = theScale
208 One = 1.0/theScale
209 theArmature = None
210 alpha7 = False
211 warnedVersion = False
212 initLoadedData()
213 theMessage = ""
215 theDir = os.path.dirname(filePath)
216 fileName = os.path.expanduser(filePath)
217 _,ext = os.path.splitext(fileName)
218 if ext.lower() != ".mhx":
219 print("Error: Not a mhx file: %s" % fileName.encode('utf-8', 'strict'))
220 return
221 print( "Opening MHX file %s " % fileName.encode('utf-8', 'strict') )
222 print("Toggle %x" % toggle)
223 time1 = time.clock()
225 # ignore = False # UNUSED
226 stack = []
227 tokens = []
228 key = "toplevel"
229 level = 0
230 nErrors = 0
231 comment = 0
232 nesting = 0
234 file= open(fileName, "rU")
235 print( "Tokenizing" )
236 lineNo = 0
237 for line in file:
238 # print(line)
239 lineSplit= line.split()
240 lineNo += 1
241 if len(lineSplit) == 0:
242 pass
243 elif lineSplit[0][0] == '#':
244 if lineSplit[0] == '#if':
245 if comment == nesting:
246 try:
247 res = eval(lineSplit[1])
248 except:
249 res = False
250 if res:
251 comment += 1
252 nesting += 1
253 elif lineSplit[0] == '#else':
254 if comment == nesting-1:
255 comment += 1
256 elif comment == nesting:
257 comment -= 1
258 elif lineSplit[0] == '#endif':
259 if comment == nesting:
260 comment -= 1
261 nesting -= 1
262 elif comment < nesting:
263 pass
264 elif lineSplit[0] == 'end':
265 try:
266 sub = tokens
267 tokens = stack.pop()
268 if tokens:
269 tokens[-1][2] = sub
270 level -= 1
271 except:
272 print( "Tokenizer error at or before line %d.\nThe mhx file has been corrupted.\nTry to export it again from MakeHuman." % lineNo )
273 print( line )
274 stack.pop()
275 elif lineSplit[-1] == ';':
276 if lineSplit[0] == '\\':
277 key = lineSplit[1]
278 tokens.append([key,lineSplit[2:-1],[]])
279 else:
280 key = lineSplit[0]
281 tokens.append([key,lineSplit[1:-1],[]])
282 else:
283 key = lineSplit[0]
284 tokens.append([key,lineSplit[1:],[]])
285 stack.append(tokens)
286 level += 1
287 tokens = []
288 file.close()
290 if level != 0:
291 MyError("Tokenizer error (%d).\nThe mhx file has been corrupted.\nTry to export it again from MakeHuman." % level)
292 scn = clearScene()
293 print( "Parsing" )
294 parse(tokens)
296 for (expr, glbals, lcals) in todo:
297 try:
298 print("Doing %s" % expr)
299 exec(expr, glbals, lcals)
300 except:
301 msg = "Failed: %s\n" % expr
302 print( msg )
303 nErrors += 1
304 #MyError(msg)
306 scn.objects.active = theArmature
307 theArmature.MhAlpha8 = not alpha7
308 #bpy.ops.wm.properties_edit(data_path="object", property="MhxRig", value=theArmature["MhxRig"])
310 time2 = time.clock()
311 msg = "File %s loaded in %g s" % (fileName, time2-time1)
312 if nErrors:
313 msg += " but there where %d errors. " % (nErrors)
314 print(msg)
315 return
318 # getObject(name, var, glbals, lcals):
321 def getObject(name, var, glbals, lcals):
322 try:
323 ob = loadedData['Object'][name]
324 except:
325 if name != "None":
326 pushOnTodoList(None, "ob = loadedData['Object'][name]" % globals(), locals())
327 ob = None
328 return ob
331 # checkMhxVersion(major, minor):
334 def checkMhxVersion(major, minor):
335 global warnedVersion
336 print("MHX", (major,minor), (MAJOR_VERSION, MINOR_VERSION), warnedVersion)
337 if major != MAJOR_VERSION or minor < FROM_VERSION:
338 if warnedVersion:
339 return
340 else:
341 msg = (
342 "Wrong MHX version\n" +
343 "Expected MHX %d.%02d but the loaded file " % (MAJOR_VERSION, MINOR_VERSION) +
344 "has version MHX %d.%02d\n" % (major, minor))
345 if minor < FROM_VERSION:
346 msg += (
347 "You can disable this error message by deselecting the \n" +
348 "Enforce version option when importing. Better:\n" +
349 "Export the MHX file again with an updated version of MakeHuman.\n" +
350 "The most up-to-date version of MakeHuman is the nightly build.\n")
351 else:
352 msg += (
353 "Download the most recent Blender build from www.graphicall.org. \n" +
354 "The most up-to-date version of the import script is distributed\n" +
355 "with Blender. It can also be downloaded from MakeHuman. \n" +
356 "It is located in the importers/mhx/blender25x \n" +
357 "folder and is called import_scene_mhx.py. \n")
358 if (toggle & T_EnforceVersion or minor > MINOR_VERSION):
359 MyError(msg)
360 else:
361 print(msg)
362 warnedVersion = True
363 return
366 # parse(tokens):
369 ifResult = False
371 def parse(tokens):
372 global MHX249, ifResult, theScale, defaultScale, One
374 for (key, val, sub) in tokens:
375 data = None
376 if key == 'MHX':
377 checkMhxVersion(int(val[0]), int(val[1]))
378 elif key == 'MHX249':
379 MHX249 = eval(val[0])
380 print("Blender 2.49 compatibility mode is %s\n" % MHX249)
381 elif MHX249:
382 pass
383 elif key == 'print':
384 msg = concatList(val)
385 print(msg)
386 elif key == 'warn':
387 msg = concatList(val)
388 print(msg)
389 elif key == 'error':
390 msg = concatList(val)
391 MyError(msg)
392 elif key == 'NoScale':
393 if eval(val[0]):
394 theScale = 1.0
395 else:
396 theScale = defaultScale
397 One = 1.0/theScale
398 elif key == "Object":
399 parseObject(val, sub)
400 elif key == "Mesh":
401 reinitGlobalData()
402 data = parseMesh(val, sub)
403 elif key == "Armature":
404 data = parseArmature(val, sub)
405 elif key == "Pose":
406 data = parsePose(val, sub)
407 elif key == "Action":
408 data = parseAction(val, sub)
409 elif key == "Material":
410 data = parseMaterial(val, sub)
411 elif key == "Texture":
412 data = parseTexture(val, sub)
413 elif key == "Image":
414 data = parseImage(val, sub)
415 elif key == "Curve":
416 data = parseCurve(val, sub)
417 elif key == "TextCurve":
418 data = parseTextCurve(val, sub)
419 elif key == "Lattice":
420 data = parseLattice(val, sub)
421 elif key == "Group":
422 data = parseGroup(val, sub)
423 elif key == "Lamp":
424 data = parseLamp(val, sub)
425 elif key == "World":
426 data = parseWorld(val, sub)
427 elif key == "Scene":
428 data = parseScene(val, sub)
429 elif key == "DefineProperty":
430 parseDefineProperty(val, sub)
431 elif key == "Process":
432 parseProcess(val, sub)
433 elif key == "PostProcess":
434 postProcess(val)
435 hideLayers(val)
436 elif key == "CorrectRig":
437 correctRig(val)
438 elif key == "Rigify":
439 if toggle & T_Rigify:
440 rigifyMhx(bpy.context, val[0])
441 elif key == 'AnimationData':
442 try:
443 ob = loadedData['Object'][val[0]]
444 except:
445 ob = None
446 if ob:
447 bpy.context.scene.objects.active = ob
448 parseAnimationData(ob, val, sub)
449 elif key == 'MaterialAnimationData':
450 try:
451 ob = loadedData['Object'][val[0]]
452 except:
453 ob = None
454 if ob:
455 bpy.context.scene.objects.active = ob
456 mat = ob.data.materials[int(val[2])]
457 parseAnimationData(mat, val, sub)
458 elif key == 'ShapeKeys':
459 try:
460 ob = loadedData['Object'][val[0]]
461 except:
462 MyError("ShapeKeys object %s does not exist" % val[0])
463 if ob:
464 bpy.context.scene.objects.active = ob
465 parseShapeKeys(ob, ob.data, val, sub)
466 else:
467 data = parseDefaultType(key, val, sub)
470 # parseDefaultType(typ, args, tokens):
473 def parseDefaultType(typ, args, tokens):
474 global todo
476 name = args[0]
477 data = None
478 expr = "bpy.data.%s.new('%s')" % (Plural[typ], name)
479 data = eval(expr)
481 bpyType = typ.capitalize()
482 loadedData[bpyType][name] = data
483 if data is None:
484 return None
486 for (key, val, sub) in tokens:
487 defaultKey(key, val, sub, 'data', [], globals(), locals())
488 return data
491 # concatList(elts)
494 def concatList(elts):
495 string = ""
496 for elt in elts:
497 string += " %s" % elt
498 return string
501 # parseAction(args, tokens):
502 # parseFCurve(fcu, args, tokens):
503 # parseKeyFramePoint(pt, args, tokens):
506 def parseAction(args, tokens):
507 name = args[0]
508 if invalid(args[1]):
509 return
511 ob = bpy.context.object
512 bpy.ops.object.mode_set(mode='POSE')
513 if ob.animation_data:
514 ob.animation_data.action = None
515 created = {}
516 for (key, val, sub) in tokens:
517 if key == 'FCurve':
518 prepareActionFCurve(ob, created, val, sub)
520 act = ob.animation_data.action
521 loadedData['Action'][name] = act
522 if act is None:
523 print("Ignoring action %s" % name)
524 return act
525 act.name = name
526 print("Action", name, act, ob)
528 for (key, val, sub) in tokens:
529 if key == 'FCurve':
530 fcu = parseActionFCurve(act, ob, val, sub)
531 else:
532 defaultKey(key, val, sub, 'act', [], globals(), locals())
533 ob.animation_data.action = None
534 bpy.ops.object.mode_set(mode='OBJECT')
535 return act
537 def prepareActionFCurve(ob, created, args, tokens):
538 dataPath = args[0]
539 index = args[1]
540 (expr, channel) = channelFromDataPath(dataPath, index)
541 try:
542 if channel in created[expr]:
543 return
544 else:
545 created[expr].append(channel)
546 except:
547 created[expr] = [channel]
549 times = []
550 for (key, val, sub) in tokens:
551 if key == 'kp':
552 times.append(int(val[0]))
554 try:
555 data = eval(expr)
556 except:
557 print("Ignoring illegal expression: %s" % expr)
558 return
560 n = 0
561 for t in times:
562 #bpy.context.scene.current_frame = t
563 bpy.ops.anim.change_frame(frame = t)
564 try:
565 data.keyframe_insert(channel)
566 n += 1
567 except:
568 pass
569 #print("failed", data, expr, channel)
570 if n != len(times):
571 print("Mismatch", n, len(times), expr, channel)
572 return
574 def channelFromDataPath(dataPath, index):
575 words = dataPath.split(']')
576 if len(words) == 1:
577 # location
578 expr = "ob"
579 channel = dataPath
580 elif len(words) == 2:
581 # pose.bones["tongue"].location
582 expr = "ob.%s]" % (words[0])
583 cwords = words[1].split('.')
584 channel = cwords[1]
585 elif len(words) == 3:
586 # pose.bones["brow.R"]["mad"]
587 expr = "ob.%s]" % (words[0])
588 cwords = words[1].split('"')
589 channel = cwords[1]
590 return (expr, channel)
592 def parseActionFCurve(act, ob, args, tokens):
593 dataPath = args[0]
594 index = args[1]
595 (expr, channel) = channelFromDataPath(dataPath, index)
596 index = int(args[1])
598 success = False
599 for fcu in act.fcurves:
600 (expr1, channel1) = channelFromDataPath(fcu.data_path, fcu.array_index)
601 if expr1 == expr and channel1 == channel and fcu.array_index == index:
602 success = True
603 break
604 if not success:
605 return None
607 n = 0
608 for (key, val, sub) in tokens:
609 if key == 'kp':
610 try:
611 pt = fcu.keyframe_points[n]
612 pt.interpolation = 'LINEAR'
613 pt = parseKeyFramePoint(pt, val, sub)
614 n += 1
615 except:
616 pass
617 #print(tokens)
618 #MyError("kp", fcu, n, len(fcu.keyframe_points), val)
619 else:
620 defaultKey(key, val, sub, 'fcu', [], globals(), locals())
621 return fcu
623 def parseKeyFramePoint(pt, args, tokens):
624 pt.co = (float(args[0]), float(args[1]))
625 if len(args) > 2:
626 pt.handle1 = (float(args[2]), float(args[3]))
627 pt.handle2 = (float(args[3]), float(args[5]))
628 return pt
631 # parseAnimationData(rna, args, tokens):
632 # parseDriver(drv, args, tokens):
633 # parseDriverVariable(var, args, tokens):
636 def parseAnimationData(rna, args, tokens):
637 if not eval(args[1]):
638 return
639 if rna.animation_data is None:
640 rna.animation_data_create()
641 adata = rna.animation_data
642 for (key, val, sub) in tokens:
643 if key == 'FCurve':
644 fcu = parseAnimDataFCurve(adata, rna, val, sub)
645 else:
646 defaultKey(key, val, sub, 'adata', [], globals(), locals())
647 return adata
649 def parseAnimDataFCurve(adata, rna, args, tokens):
650 if invalid(args[2]):
651 return
652 dataPath = args[0]
653 index = int(args[1])
654 n = 1
655 for (key, val, sub) in tokens:
656 if key == 'Driver':
657 fcu = parseDriver(adata, dataPath, index, rna, val, sub)
658 fmod = fcu.modifiers[0]
659 fcu.modifiers.remove(fmod)
660 elif key == 'FModifier':
661 parseFModifier(fcu, val, sub)
662 elif key == 'kp':
663 pt = fcu.keyframe_points.insert(n, 0)
664 pt.interpolation = 'LINEAR'
665 pt = parseKeyFramePoint(pt, val, sub)
666 n += 1
667 else:
668 defaultKey(key, val, sub, 'fcu', [], globals(), locals())
669 return fcu
672 fcurve = con.driver_add("influence", 0)
673 driver = fcurve.driver
674 driver.type = 'AVERAGE'
676 def parseDriver(adata, dataPath, index, rna, args, tokens):
677 if dataPath[-1] == ']':
678 words = dataPath.split(']')
679 expr = "rna." + words[0] + ']'
680 pwords = words[1].split('"')
681 prop = pwords[1]
682 bone = eval(expr)
683 return None
684 else:
685 words = dataPath.split('.')
686 channel = words[-1]
687 expr = "rna"
688 for n in range(len(words)-1):
689 expr += "." + words[n]
690 expr += ".driver_add('%s', index)" % channel
692 fcu = eval(expr)
693 drv = fcu.driver
694 drv.type = args[0]
695 for (key, val, sub) in tokens:
696 if key == 'DriverVariable':
697 var = parseDriverVariable(drv, rna, val, sub)
698 else:
699 defaultKey(key, val, sub, 'drv', [], globals(), locals())
700 return fcu
702 def parseDriverVariable(drv, rna, args, tokens):
703 var = drv.variables.new()
704 var.name = args[0]
705 var.type = args[1]
706 nTarget = 0
707 for (key, val, sub) in tokens:
708 if key == 'Target':
709 parseDriverTarget(var, nTarget, rna, val, sub)
710 nTarget += 1
711 else:
712 defaultKey(key, val, sub, 'var', [], globals(), locals())
713 return var
715 def parseFModifier(fcu, args, tokens):
716 fmod = fcu.modifiers.new(args[0])
717 #fmod = fcu.modifiers[0]
718 for (key, val, sub) in tokens:
719 defaultKey(key, val, sub, 'fmod', [], globals(), locals())
720 return fmod
723 var = driver.variables.new()
724 var.name = target_bone
725 var.targets[0].id_type = 'OBJECT'
726 var.targets[0].id = obj
727 var.targets[0].rna_path = driver_path
729 def parseDriverTarget(var, nTarget, rna, args, tokens):
730 targ = var.targets[nTarget]
731 name = args[0]
732 #targ.id_type = args[1]
733 dtype = args[1].capitalize()
734 dtype = 'Object'
735 targ.id = loadedData[dtype][name]
736 for (key, val, sub) in tokens:
737 if key == 'data_path':
738 words = val[0].split('"')
739 if len(words) > 1:
740 targ.data_path = propNames(words[1])[1]
741 else:
742 targ.data_path = propNames(val)[1]
743 else:
744 defaultKey(key, val, sub, 'targ', [], globals(), locals())
745 return targ
749 # parseMaterial(args, ext, tokens):
750 # parseMTex(mat, args, tokens):
751 # parseTexture(args, tokens):
754 def parseMaterial(args, tokens):
755 global todo
756 name = args[0]
757 mat = bpy.data.materials.new(name)
758 if mat is None:
759 return None
760 loadedData['Material'][name] = mat
761 for (key, val, sub) in tokens:
762 if key == 'MTex':
763 parseMTex(mat, val, sub)
764 elif key == 'Ramp':
765 parseRamp(mat, val, sub)
766 elif key == 'RaytraceTransparency':
767 parseDefault(mat.raytrace_transparency, sub, {}, [])
768 elif key == 'Halo':
769 parseDefault(mat.halo, sub, {}, [])
770 elif key == 'SSS':
771 parseDefault(mat.subsurface_scattering, sub, {}, [])
772 elif key == 'Strand':
773 parseDefault(mat.strand, sub, {}, [])
774 elif key == 'NodeTree':
775 mat.use_nodes = True
776 parseNodeTree(mat.node_tree, val, sub)
777 elif key == 'AnimationData':
778 parseAnimationData(mat, val, sub)
779 else:
780 exclude = ['specular_intensity', 'tangent_shading']
781 defaultKey(key, val, sub, 'mat', [], globals(), locals())
783 return mat
785 def parseMTex(mat, args, tokens):
786 global todo
787 index = int(args[0])
788 texname = args[1]
789 texco = args[2]
790 mapto = args[3]
791 tex = loadedData['Texture'][texname]
792 mtex = mat.texture_slots.add()
793 mtex.texture_coords = texco
794 mtex.texture = tex
796 for (key, val, sub) in tokens:
797 defaultKey(key, val, sub, "mtex", [], globals(), locals())
799 return mtex
801 def parseTexture(args, tokens):
802 global todo
803 if verbosity > 2:
804 print( "Parsing texture %s" % args )
805 name = args[0]
806 tex = bpy.data.textures.new(name=name, type=args[1])
807 loadedData['Texture'][name] = tex
809 for (key, val, sub) in tokens:
810 if key == 'Image':
811 try:
812 imgName = val[0]
813 img = loadedData['Image'][imgName]
814 tex.image = img
815 except:
816 msg = "Unable to load image '%s'" % val[0]
817 elif key == 'Ramp':
818 parseRamp(tex, val, sub)
819 elif key == 'NodeTree':
820 tex.use_nodes = True
821 parseNodeTree(tex.node_tree, val, sub)
822 else:
823 defaultKey(key, val, sub, "tex", ['use_nodes', 'use_textures', 'contrast', 'use_alpha'], globals(), locals())
825 return tex
827 def parseRamp(data, args, tokens):
828 nvar = "data.%s" % args[0]
829 use = "data.use_%s = True" % args[0]
830 exec(use)
831 ramp = eval(nvar)
832 elts = ramp.elements
833 n = 0
834 for (key, val, sub) in tokens:
835 # print("Ramp", key, val)
836 if key == 'Element':
837 elts[n].color = eval(val[0])
838 elts[n].position = eval(val[1])
839 n += 1
840 else:
841 defaultKey(key, val, sub, "tex", ['use_nodes', 'use_textures', 'contrast'], globals(), locals())
843 def parseSSS(mat, args, tokens):
844 sss = mat.subsurface_scattering
845 for (key, val, sub) in tokens:
846 defaultKey(key, val, sub, "sss", [], globals(), locals())
848 def parseStrand(mat, args, tokens):
849 strand = mat.strand
850 for (key, val, sub) in tokens:
851 defaultKey(key, val, sub, "strand", [], globals(), locals())
854 # parseNodeTree(tree, args, tokens):
855 # parseNode(node, args, tokens):
856 # parseSocket(socket, args, tokens):
859 def parseNodeTree(tree, args, tokens):
860 return
861 print("Tree", tree, args)
862 print(list(tree.nodes))
863 tree.name = args[0]
864 for (key, val, sub) in tokens:
865 if key == 'Node':
866 parseNodes(tree.nodes, val, sub)
867 else:
868 defaultKey(key, val, sub, "tree", [], globals(), locals())
870 def parseNodes(nodes, args, tokens):
871 print("Nodes", nodes, args)
872 print(list(nodes))
873 node.name = args[0]
874 for (key, val, sub) in tokens:
875 if key == 'Inputs':
876 parseSocket(node.inputs, val, sub)
877 elif key == 'Outputs':
878 parseSocket(node.outputs, val, sub)
879 else:
880 defaultKey(key, val, sub, "node", [], globals(), locals())
882 def parseNode(node, args, tokens):
883 print("Node", node, args)
884 print(list(node.inputs), list(node.outputs))
885 node.name = args[0]
886 for (key, val, sub) in tokens:
887 if key == 'Inputs':
888 parseSocket(node.inputs, val, sub)
889 elif key == 'Outputs':
890 parseSocket(node.outputs, val, sub)
891 else:
892 defaultKey(key, val, sub, "node", [], globals(), locals())
894 def parseSocket(socket, args, tokens):
895 print("Socket", socket, args)
896 socket.name = args[0]
897 for (key, val, sub) in tokens:
898 if key == 'Node':
899 parseNode(tree.nodes, val, sub)
900 else:
901 defaultKey(key, val, sub, "tree", [], globals(), locals())
906 # loadImage(filepath):
907 # parseImage(args, tokens):
910 def loadImage(relFilepath):
911 filepath = os.path.normpath(os.path.join(theDir, relFilepath))
912 print( "Loading %s" % filepath.encode('utf-8','strict'))
913 if os.path.isfile(filepath):
914 #print( "Found file %s." % filepath.encode('utf-8','strict') )
915 try:
916 img = bpy.data.images.load(filepath)
917 return img
918 except:
919 print( "Cannot read image" )
920 return None
921 else:
922 print( "No such file: %s" % filepath.encode('utf-8','strict') )
923 return None
926 def parseImage(args, tokens):
927 global todo
928 imgName = args[0]
929 img = None
930 for (key, val, sub) in tokens:
931 if key == 'Filename':
932 filename = val[0]
933 for n in range(1,len(val)):
934 filename += " " + val[n]
935 img = loadImage(filename)
936 if img is None:
937 return None
938 img.name = imgName
939 else:
940 defaultKey(key, val, sub, "img", ['depth', 'dirty', 'has_data', 'size', 'type', 'use_premultiply'], globals(), locals())
941 print ("Image %s" % img )
942 loadedData['Image'][imgName] = img
943 return img
946 # parseObject(args, tokens):
947 # createObject(type, name, data, datName):
948 # setObjectAndData(args, typ):
951 def parseObject(args, tokens):
952 if verbosity > 2:
953 print( "Parsing object %s" % args )
954 name = args[0]
955 typ = args[1]
956 datName = args[2]
958 if typ == 'EMPTY':
959 ob = bpy.data.objects.new(name, None)
960 loadedData['Object'][name] = ob
961 linkObject(ob, None)
962 else:
963 try:
964 data = loadedData[typ.capitalize()][datName]
965 except:
966 MyError("Failed to find data: %s %s %s" % (name, typ, datName))
967 return
969 try:
970 ob = loadedData['Object'][name]
971 bpy.context.scene.objects.active = ob
972 #print("Found data", ob)
973 except:
974 ob = None
976 if ob is None:
977 ob = createObject(typ, name, data, datName)
978 linkObject(ob, data)
980 for (key, val, sub) in tokens:
981 if key == 'Modifier':
982 parseModifier(ob, val, sub)
983 elif key == 'Constraint':
984 parseConstraint(ob.constraints, None, val, sub)
985 elif key == 'AnimationData':
986 parseAnimationData(ob, val, sub)
987 elif key == 'ParticleSystem':
988 parseParticleSystem(ob, val, sub)
989 elif key == 'FieldSettings':
990 parseDefault(ob.field, sub, {}, [])
991 else:
992 defaultKey(key, val, sub, "ob", ['type', 'data'], globals(), locals())
994 if bpy.context.object == ob:
995 if ob.type == 'MESH':
996 bpy.ops.object.shade_smooth()
997 else:
998 print("Context", ob, bpy.context.object, bpy.context.scene.objects.active)
999 return
1001 def createObject(typ, name, data, datName):
1002 # print( "Creating object %s %s %s" % (typ, name, data) )
1003 ob = bpy.data.objects.new(name, data)
1004 if data:
1005 loadedData[typ.capitalize()][datName] = data
1006 loadedData['Object'][name] = ob
1007 return ob
1009 def linkObject(ob, data):
1010 #print("Data", data, ob.data)
1011 if data and ob.data is None:
1012 ob.data = data
1013 scn = bpy.context.scene
1014 scn.objects.link(ob)
1015 scn.objects.active = ob
1016 #print("Linked object", ob)
1017 #print("Scene", scn)
1018 #print("Active", scn.objects.active)
1019 #print("Context", bpy.context.object)
1020 return ob
1022 def setObjectAndData(args, typ):
1023 datName = args[0]
1024 obName = args[1]
1025 #bpy.ops.object.add(type=typ)
1026 ob = bpy.context.object
1027 ob.name = obName
1028 ob.data.name = datName
1029 loadedData[typ][datName] = ob.data
1030 loadedData['Object'][obName] = ob
1031 return ob.data
1035 # parseModifier(ob, args, tokens):
1039 def parseModifier(ob, args, tokens):
1040 name = args[0]
1041 typ = args[1]
1042 if typ == 'PARTICLE_SYSTEM':
1043 return None
1044 mod = ob.modifiers.new(name, typ)
1045 for (key, val, sub) in tokens:
1046 if key == 'HookAssignNth':
1047 if val[0] == 'CURVE':
1048 hookAssignNth(mod, int(val[1]), True, ob.data.splines[0].points)
1049 elif val[0] == 'LATTICE':
1050 hookAssignNth(mod, int(val[1]), False, ob.data.points)
1051 elif val[0] == 'MESH':
1052 hookAssignNth(mod, int(val[1]), True, ob.data.vertices)
1053 else:
1054 MyError("Unknown hook %s" % val)
1055 else:
1056 defaultKey(key, val, sub, 'mod', [], globals(), locals())
1057 return mod
1059 def hookAssignNth(mod, n, select, points):
1060 if select:
1061 for pt in points:
1062 pt.select = False
1063 points[n].select = True
1064 sel = []
1065 for pt in points:
1066 sel.append(pt.select)
1067 #print(mod, sel, n, points)
1069 bpy.ops.object.mode_set(mode='EDIT')
1070 bpy.ops.object.hook_reset(modifier=mod.name)
1071 bpy.ops.object.hook_select(modifier=mod.name)
1072 bpy.ops.object.hook_assign(modifier=mod.name)
1073 bpy.ops.object.mode_set(mode='OBJECT')
1074 return
1077 # parseParticleSystem(ob, args, tokens):
1078 # parseParticles(particles, args, tokens):
1079 # parseParticle(par, args, tokens):
1082 def parseParticleSystem(ob, args, tokens):
1083 print(ob, bpy.context.object)
1084 pss = ob.particle_systems
1085 print(pss, pss.values())
1086 name = args[0]
1087 typ = args[1]
1088 #psys = pss.new(name, typ)
1089 bpy.ops.object.particle_system_add()
1090 print(pss, pss.values())
1091 psys = pss[-1]
1092 psys.name = name
1093 psys.settings.type = typ
1094 loadedData['ParticleSystem'][name] = psys
1095 print("Psys", psys)
1097 for (key, val, sub) in tokens:
1098 if key == 'Particles':
1099 parseParticles(psys, val, sub)
1100 else:
1101 defaultKey(key, val, sub, 'psys', [], globals(), locals())
1102 return psys
1104 def parseParticles(psys, args, tokens):
1105 particles = psys.particles
1106 bpy.ops.particle.particle_edit_toggle()
1107 n = 0
1108 for (key, val, sub) in tokens:
1109 if key == 'Particle':
1110 parseParticle(particles[n], val, sub)
1111 n += 1
1112 else:
1113 for par in particles:
1114 defaultKey(key, val, sub, 'par', [], globals(), locals())
1115 bpy.ops.particle.particle_edit_toggle()
1116 return particles
1118 def parseParticle(par, args, tokens):
1119 n = 0
1120 for (key, val, sub) in tokens:
1121 if key == 'h':
1122 h = par.hair[n]
1123 h.location = eval(val[0])
1124 h.time = int(val[1])
1125 h.weight = float(val[2])
1126 n += 1
1127 elif key == 'location':
1128 par.location = eval(val[0])
1129 return
1132 # unpackList(list_of_tuples):
1135 def unpackList(list_of_tuples):
1136 l = []
1137 for t in list_of_tuples:
1138 l.extend(t)
1139 return l
1144 # parseMesh (args, tokens):
1147 def parseMesh (args, tokens):
1148 global todo, BMeshAware
1149 if verbosity > 2:
1150 print( "Parsing mesh %s" % args )
1152 mename = args[0]
1153 obname = args[1]
1154 me = bpy.data.meshes.new(mename)
1155 ob = createObject('MESH', obname, me, mename)
1157 verts = []
1158 edges = []
1159 faces = []
1160 vertsTex = []
1161 texFaces = []
1163 for (key, val, sub) in tokens:
1164 if key == 'Verts':
1165 verts = parseVerts(sub)
1166 elif key == 'Edges':
1167 edges = parseEdges(sub)
1168 elif key == 'Faces':
1169 faces = parseFaces(sub)
1171 if faces:
1172 me.from_pydata(verts, [], faces)
1173 else:
1174 me.from_pydata(verts, edges, [])
1175 me.update()
1176 linkObject(ob, me)
1178 if faces:
1179 try:
1180 me.polygons
1181 BMeshAware = True
1182 except:
1183 BMeshAware = False
1185 mats = []
1186 nuvlayers = 0
1187 for (key, val, sub) in tokens:
1188 if key == 'Verts' or key == 'Edges' or key == 'Faces':
1189 pass
1190 elif key == 'MeshTextureFaceLayer':
1191 if BMeshAware:
1192 parseUvTextureBMesh(val, sub, me)
1193 else:
1194 parseUvTextureNoBMesh(val, sub, me)
1195 elif key == 'MeshColorLayer':
1196 parseVertColorLayer(val, sub, me)
1197 elif key == 'VertexGroup':
1198 parseVertexGroup(ob, me, val, sub)
1199 elif key == 'ShapeKeys':
1200 parseShapeKeys(ob, me, val, sub)
1201 elif key == 'Material':
1202 try:
1203 mat = loadedData['Material'][val[0]]
1204 except:
1205 mat = None
1206 if mat:
1207 me.materials.append(mat)
1208 else:
1209 defaultKey(key, val, sub, "me", [], globals(), locals())
1211 for (key, val, sub) in tokens:
1212 if key == 'Faces':
1213 if BMeshAware:
1214 parseFaces2BMesh(sub, me)
1215 else:
1216 parseFaces2NoBMesh(sub, me)
1217 return me
1220 # parseVerts(tokens):
1221 # parseEdges(tokens):
1222 # parseFaces(tokens):
1223 # parseFaces2(tokens, me):
1226 def parseVerts(tokens):
1227 verts = []
1228 for (key, val, sub) in tokens:
1229 if key == 'v':
1230 verts.append( (theScale*float(val[0]), theScale*float(val[1]), theScale*float(val[2])) )
1231 return verts
1233 def parseEdges(tokens):
1234 edges = []
1235 for (key, val, sub) in tokens:
1236 if key == 'e':
1237 edges.append((int(val[0]), int(val[1])))
1238 return edges
1240 def parseFaces(tokens):
1241 faces = []
1242 for (key, val, sub) in tokens:
1243 if key == 'f':
1244 if len(val) == 3:
1245 face = [int(val[0]), int(val[1]), int(val[2])]
1246 elif len(val) == 4:
1247 face = [int(val[0]), int(val[1]), int(val[2]), int(val[3])]
1248 faces.append(face)
1249 return faces
1251 def parseFaces2BMesh(tokens, me):
1252 n = 0
1253 for (key, val, sub) in tokens:
1254 if key == 'ft':
1255 f = me.polygons[n]
1256 f.material_index = int(val[0])
1257 f.use_smooth = int(val[1])
1258 n += 1
1259 elif key == 'ftn':
1260 mn = int(val[1])
1261 us = int(val[2])
1262 npts = int(val[0])
1263 for i in range(npts):
1264 f = me.polygons[n]
1265 f.material_index = mn
1266 f.use_smooth = us
1267 n += 1
1268 elif key == 'mn':
1269 fn = int(val[0])
1270 mn = int(val[1])
1271 f = me.polygons[fn]
1272 f.material_index = mn
1273 elif key == 'ftall':
1274 mat = int(val[0])
1275 smooth = int(val[1])
1276 for f in me.polygons:
1277 f.material_index = mat
1278 f.use_smooth = smooth
1279 return
1281 def parseFaces2NoBMesh(tokens, me):
1282 n = 0
1283 for (key, val, sub) in tokens:
1284 if key == 'ft':
1285 f = me.faces[n]
1286 f.material_index = int(val[0])
1287 f.use_smooth = int(val[1])
1288 n += 1
1289 elif key == 'ftn':
1290 mn = int(val[1])
1291 us = int(val[2])
1292 npts = int(val[0])
1293 for i in range(npts):
1294 f = me.faces[n]
1295 f.material_index = mn
1296 f.use_smooth = us
1297 n += 1
1298 elif key == 'mn':
1299 fn = int(val[0])
1300 mn = int(val[1])
1301 f = me.faces[fn]
1302 f.material_index = mn
1303 elif key == 'ftall':
1304 mat = int(val[0])
1305 smooth = int(val[1])
1306 for f in me.faces:
1307 f.material_index = mat
1308 f.use_smooth = smooth
1309 return
1313 # parseUvTexture(args, tokens, me,):
1314 # parseUvTexData(args, tokens, uvdata):
1317 def parseUvTextureBMesh(args, tokens, me):
1318 name = args[0]
1319 bpy.ops.mesh.uv_texture_add()
1320 uvtex = me.uv_textures[-1]
1321 uvtex.name = name
1322 uvloop = me.uv_layers[-1]
1323 loadedData['MeshTextureFaceLayer'][name] = uvloop
1324 for (key, val, sub) in tokens:
1325 if key == 'Data':
1326 parseUvTexDataBMesh(val, sub, uvloop.data)
1327 else:
1328 defaultKey(key, val, sub, "uvtex", [], globals(), locals())
1329 return
1331 def parseUvTexDataBMesh(args, tokens, data):
1332 n = 0
1333 for (key, val, sub) in tokens:
1334 if key == 'vt':
1335 data[n].uv = (float(val[0]), float(val[1]))
1336 n += 1
1337 data[n].uv = (float(val[2]), float(val[3]))
1338 n += 1
1339 data[n].uv = (float(val[4]), float(val[5]))
1340 n += 1
1341 if len(val) > 6:
1342 data[n].uv = (float(val[6]), float(val[7]))
1343 n += 1
1344 return
1346 def parseUvTextureNoBMesh(args, tokens, me):
1347 name = args[0]
1348 uvtex = me.uv_textures.new(name = name)
1349 loadedData['MeshTextureFaceLayer'][name] = uvtex
1350 for (key, val, sub) in tokens:
1351 if key == 'Data':
1352 parseUvTexDataNoBMesh(val, sub, uvtex.data)
1353 else:
1354 defaultKey(key, val, sub, "uvtex", [], globals(), locals())
1355 return
1357 def parseUvTexDataNoBMesh(args, tokens, data):
1358 n = 0
1359 for (key, val, sub) in tokens:
1360 if key == 'vt':
1361 data[n].uv1 = (float(val[0]), float(val[1]))
1362 data[n].uv2 = (float(val[2]), float(val[3]))
1363 data[n].uv3 = (float(val[4]), float(val[5]))
1364 if len(val) > 6:
1365 data[n].uv4 = (float(val[6]), float(val[7]))
1366 n += 1
1367 return
1370 # parseVertColorLayer(args, tokens, me):
1371 # parseVertColorData(args, tokens, data):
1374 def parseVertColorLayer(args, tokens, me):
1375 name = args[0]
1376 print("VertColorLayer", name)
1377 vcol = me.vertex_colors.new(name)
1378 loadedData['MeshColorLayer'][name] = vcol
1379 for (key, val, sub) in tokens:
1380 if key == 'Data':
1381 parseVertColorData(val, sub, vcol.data)
1382 else:
1383 defaultKey(key, val, sub, "vcol", [], globals(), locals())
1384 return
1386 def parseVertColorData(args, tokens, data):
1387 n = 0
1388 for (key, val, sub) in tokens:
1389 if key == 'cv':
1390 data[n].color1 = eval(val[0])
1391 data[n].color2 = eval(val[1])
1392 data[n].color3 = eval(val[2])
1393 data[n].color4 = eval(val[3])
1394 n += 1
1395 return
1399 # parseVertexGroup(ob, me, args, tokens):
1402 def parseVertexGroup(ob, me, args, tokens):
1403 global toggle
1404 if verbosity > 2:
1405 print( "Parsing vertgroup %s" % args )
1406 grpName = args[0]
1407 try:
1408 res = eval(args[1])
1409 except:
1410 res = True
1411 if not res:
1412 return
1414 if (toggle & T_Armature) or (grpName in ['Eye_L', 'Eye_R', 'Gums', 'Head', 'Jaw', 'Left', 'Middle', 'Right', 'Scalp']):
1415 try:
1416 group = loadedData['VertexGroup'][grpName]
1417 except KeyError:
1418 group = ob.vertex_groups.new(grpName)
1419 loadedData['VertexGroup'][grpName] = group
1420 for (key, val, sub) in tokens:
1421 if key == 'wv':
1422 group.add( [int(val[0])], float(val[1]), 'REPLACE' )
1423 return
1427 # parseShapeKeys(ob, me, args, tokens):
1428 # parseShapeKey(ob, me, args, tokens):
1429 # addShapeKey(ob, name, vgroup, tokens):
1430 # doShape(name):
1433 def doShape(name):
1434 if (toggle & T_Shapekeys) and (name == 'Basis'):
1435 return True
1436 else:
1437 return (toggle & T_Face)
1440 def parseShapeKeys(ob, me, args, tokens):
1441 for (key, val, sub) in tokens:
1442 if key == 'ShapeKey':
1443 parseShapeKey(ob, me, val, sub)
1444 elif key == 'AnimationData':
1445 if me.shape_keys:
1446 parseAnimationData(me.shape_keys, val, sub)
1447 elif key == 'Expression':
1448 prop = "Mhe" + val[0].capitalize()
1449 parseUnits(prop, ob, sub)
1450 elif key == 'Viseme':
1451 name = val[0].upper()
1452 if name in ["REST", "ETC"]:
1453 name = name.capitalize()
1454 prop = "Mhv" + name
1455 parseUnits(prop, ob, sub)
1456 ob.active_shape_key_index = 0
1459 def parseUnits(prop, ob, sub):
1460 string = ""
1461 for words in sub:
1462 unit = words[0].replace("-","_")
1463 value = words[1][0]
1464 string += "%s:%s;" % (unit, value)
1465 rig = ob.parent
1466 rig[prop] = string
1469 def parseShapeKey(ob, me, args, tokens):
1470 if verbosity > 2:
1471 print( "Parsing ob %s shape %s" % (bpy.context.object, args[0] ))
1472 name = args[0]
1473 lr = args[1]
1474 if invalid(args[2]):
1475 return
1477 if lr == 'Sym': # or toggle & T_Symm:
1478 addShapeKey(ob, name, None, tokens)
1479 elif lr == 'LR':
1480 addShapeKey(ob, name+'_L', 'Left', tokens)
1481 addShapeKey(ob, name+'_R', 'Right', tokens)
1482 else:
1483 MyError("ShapeKey L/R %s" % lr)
1484 return
1487 def addShapeKey(ob, name, vgroup, tokens):
1488 skey = ob.shape_key_add(name=name, from_mix=False)
1489 if name != 'Basis':
1490 skey.relative_key = loadedData['ShapeKey']['Basis']
1491 skey.name = name
1492 if vgroup:
1493 skey.vertex_group = vgroup
1494 loadedData['ShapeKey'][name] = skey
1496 for (key, val, sub) in tokens:
1497 if key == 'sv':
1498 index = int(val[0])
1499 pt = skey.data[index].co
1500 pt[0] += theScale*float(val[1])
1501 pt[1] += theScale*float(val[2])
1502 pt[2] += theScale*float(val[3])
1503 else:
1504 defaultKey(key, val, sub, "skey", [], globals(), locals())
1506 return
1510 # parseArmature (obName, args, tokens)
1513 def parseArmature (args, tokens):
1514 global toggle, theArmature
1515 if verbosity > 2:
1516 print( "Parsing armature %s" % args )
1518 amtname = args[0]
1519 obname = args[1]
1520 mode = args[2]
1522 amt = bpy.data.armatures.new(amtname)
1523 ob = createObject('ARMATURE', obname, amt, amtname)
1524 linkObject(ob, amt)
1525 theArmature = ob
1527 bpy.ops.object.mode_set(mode='OBJECT')
1528 bpy.ops.object.mode_set(mode='EDIT')
1530 heads = {}
1531 tails = {}
1532 for (key, val, sub) in tokens:
1533 if key == 'Bone':
1534 bname = val[0]
1535 if not invalid(val[1]):
1536 bone = amt.edit_bones.new(bname)
1537 parseBone(bone, amt, sub, heads, tails)
1538 loadedData['Bone'][bname] = bone
1539 elif key == 'RecalcRoll':
1540 rolls = {}
1541 for bone in amt.edit_bones:
1542 bone.select = False
1543 blist = eval(val[0])
1544 for name in blist:
1545 bone = amt.edit_bones[name]
1546 bone.select = True
1547 bpy.ops.armature.calculate_roll(type='Z')
1548 for bone in amt.edit_bones:
1549 rolls[bone.name] = bone.roll
1550 bpy.ops.object.mode_set(mode='OBJECT')
1551 for bone in amt.bones:
1552 bone['Roll'] = rolls[bone.name]
1553 bpy.ops.object.mode_set(mode='EDIT')
1554 else:
1555 defaultKey(key, val, sub, "amt", ['MetaRig'], globals(), locals())
1556 bpy.ops.object.mode_set(mode='OBJECT')
1558 return amt
1561 # parseBone(bone, amt, tokens, heads, tails):
1564 def parseBone(bone, amt, tokens, heads, tails):
1565 global todo
1567 for (key, val, sub) in tokens:
1568 if key == "head":
1569 bone.head = (theScale*float(val[0]), theScale*float(val[1]), theScale*float(val[2]))
1570 elif key == "tail":
1571 bone.tail = (theScale*float(val[0]), theScale*float(val[1]), theScale*float(val[2]))
1572 #elif key == 'restrict_select':
1573 # pass
1574 elif key == 'hide' and val[0] == 'True':
1575 name = bone.name
1576 else:
1577 defaultKey(key, val, sub, "bone", [], globals(), locals())
1578 return bone
1581 # parsePose (args, tokens):
1584 def parsePose (args, tokens):
1585 global todo
1586 name = args[0]
1587 ob = loadedData['Object'][name]
1588 bpy.context.scene.objects.active = ob
1589 bpy.ops.object.mode_set(mode='POSE')
1590 pbones = ob.pose.bones
1591 nGrps = 0
1592 for (key, val, sub) in tokens:
1593 if key == 'Posebone':
1594 parsePoseBone(pbones, ob, val, sub)
1595 elif key == 'BoneGroup':
1596 parseBoneGroup(ob.pose, nGrps, val, sub)
1597 nGrps += 1
1598 elif key == 'SetProp':
1599 bone = val[0]
1600 prop = val[1]
1601 value = eval(val[2])
1602 pb = pbones[bone]
1603 pb[prop] = value
1604 else:
1605 defaultKey(key, val, sub, "ob.pose", [], globals(), locals())
1606 bpy.ops.object.mode_set(mode='OBJECT')
1607 return ob
1611 # parsePoseBone(pbones, args, tokens):
1612 # parseArray(data, exts, args):
1615 def parseBoneGroup(pose, nGrps, args, tokens):
1616 global todo
1617 if verbosity > 2:
1618 print( "Parsing bonegroup %s" % args )
1619 name = args[0]
1620 bpy.ops.pose.group_add()
1621 bg = pose.bone_groups.active
1622 loadedData['BoneGroup'][name] = bg
1623 for (key, val, sub) in tokens:
1624 defaultKey(key, val, sub, "bg", [], globals(), locals())
1625 return
1627 def parsePoseBone(pbones, ob, args, tokens):
1628 global todo
1629 if invalid(args[1]):
1630 return
1631 name = args[0]
1632 pb = pbones[name]
1633 amt = ob.data
1634 amt.bones.active = pb.bone
1636 for (key, val, sub) in tokens:
1637 if key == 'Constraint':
1638 amt.bones.active = pb.bone
1639 cns = parseConstraint(pb.constraints, pb, val, sub)
1640 elif key == 'bpyops':
1641 amt.bones.active = pb.bone
1642 expr = "bpy.ops.%s" % val[0]
1643 print(expr)
1644 exec(expr)
1645 elif key == 'ik_dof':
1646 parseArray(pb, ["ik_dof_x", "ik_dof_y", "ik_dof_z"], val)
1647 elif key == 'ik_limit':
1648 parseArray(pb, ["ik_limit_x", "ik_limit_y", "ik_limit_z"], val)
1649 elif key == 'ik_max':
1650 parseArray(pb, ["ik_max_x", "ik_max_y", "ik_max_z"], val)
1651 elif key == 'ik_min':
1652 parseArray(pb, ["ik_min_x", "ik_min_y", "ik_min_z"], val)
1653 elif key == 'ik_stiffness':
1654 parseArray(pb, ["ik_stiffness_x", "ik_stiffness_y", "ik_stiffness_z"], val)
1655 elif key == 'hide':
1656 #bpy.ops.object.mode_set(mode='OBJECT')
1657 amt.bones[name].hide = eval(val[0])
1658 #bpy.ops.object.mode_set(mode='POSE')
1660 else:
1661 defaultKey(key, val, sub, "pb", [], globals(), locals())
1662 #print("pb %s done" % name)
1663 return
1665 def parseArray(data, exts, args):
1666 n = 1
1667 for ext in exts:
1668 expr = "data.%s = %s" % (ext, args[n])
1669 # print(expr)
1670 exec(expr)
1671 n += 1
1672 return
1675 # parseConstraint(constraints, pb, args, tokens)
1678 def parseConstraint(constraints, pb, args, tokens):
1679 if invalid(args[2]):
1680 return None
1681 if (toggle&T_Opcns and pb):
1682 print("Active")
1683 aob = bpy.context.object
1684 print("ob", aob)
1685 aamt = aob.data
1686 print("amt", aamt)
1687 apose = aob.pose
1688 print("pose", apose)
1689 abone = aamt.bones.active
1690 print("bone", abone)
1691 print('Num cns before', len(list(constraints)))
1692 bpy.ops.pose.constraint_add(type=args[1])
1693 cns = constraints.active
1694 print('and after', pb, cns, len(list(constraints)))
1695 else:
1696 cns = constraints.new(args[1])
1698 cns.name = args[0]
1699 for (key,val,sub) in tokens:
1700 if key == 'invert':
1701 parseArray(cns, ["invert_x", "invert_y", "invert_z"], val)
1702 elif key == 'use':
1703 parseArray(cns, ["use_x", "use_y", "use_z"], val)
1704 elif key == 'pos_lock':
1705 parseArray(cns, ["lock_location_x", "lock_location_y", "lock_location_z"], val)
1706 elif key == 'rot_lock':
1707 parseArray(cns, ["lock_rotation_x", "lock_rotation_y", "lock_rotation_z"], val)
1708 else:
1709 defaultKey(key, val, sub, "cns", ["use_target"], globals(), locals())
1712 #print("cns %s done" % cns.name)
1713 return cns
1718 # parseCurve (args, tokens):
1719 # parseSpline(cu, args, tokens):
1720 # parseBezier(spline, n, args, tokens):
1723 def parseCurve (args, tokens):
1724 global todo
1725 if verbosity > 2:
1726 print( "Parsing curve %s" % args )
1727 bpy.ops.object.add(type='CURVE')
1728 cu = setObjectAndData(args, 'Curve')
1730 for (key, val, sub) in tokens:
1731 if key == 'Spline':
1732 parseSpline(cu, val, sub)
1733 else:
1734 defaultKey(key, val, sub, "cu", [], globals(), locals())
1735 return
1737 def parseTextCurve (args, tokens):
1738 global todo
1739 if verbosity > 2:
1740 print( "Parsing text curve %s" % args )
1741 bpy.ops.object.text_add()
1742 txt = setObjectAndData(args, 'Text')
1744 for (key, val, sub) in tokens:
1745 if key == 'Spline':
1746 parseSpline(txt, val, sub)
1747 elif key == 'BodyFormat':
1748 parseCollection(txt.body_format, sub, [])
1749 elif key == 'EditFormat':
1750 parseDefault(txt.edit_format, sub, {}, [])
1751 elif key == 'Font':
1752 parseDefault(txt.font, sub, {}, [])
1753 elif key == 'TextBox':
1754 parseCollection(txt.body_format, sub, [])
1755 else:
1756 defaultKey(key, val, sub, "txt", [], globals(), locals())
1757 return
1760 def parseSpline(cu, args, tokens):
1761 typ = args[0]
1762 spline = cu.splines.new(typ)
1763 nPointsU = int(args[1])
1764 nPointsV = int(args[2])
1765 #spline.point_count_u = nPointsU
1766 #spline.point_count_v = nPointsV
1767 if typ == 'BEZIER' or typ == 'BSPLINE':
1768 spline.bezier_points.add(nPointsU)
1769 else:
1770 spline.points.add(nPointsU)
1772 n = 0
1773 for (key, val, sub) in tokens:
1774 if key == 'bz':
1775 parseBezier(spline.bezier_points[n], val, sub)
1776 n += 1
1777 elif key == 'pt':
1778 parsePoint(spline.points[n], val, sub)
1779 n += 1
1780 else:
1781 defaultKey(key, val, sub, "spline", [], globals(), locals())
1782 return
1784 def parseBezier(bez, args, tokens):
1785 bez.co = eval(args[0])
1786 bez.co = theScale*bez.co
1787 bez.handle1 = eval(args[1])
1788 bez.handle1_type = args[2]
1789 bez.handle2 = eval(args[3])
1790 bez.handle2_type = args[4]
1791 return
1793 def parsePoint(pt, args, tokens):
1794 pt.co = eval(args[0])
1795 pt.co = theScale*pt.co
1796 print(" pt", pt.co)
1797 return
1800 # parseLattice (args, tokens):
1803 def parseLattice (args, tokens):
1804 global todo
1805 if verbosity > 2:
1806 print( "Parsing lattice %s" % args )
1807 bpy.ops.object.add(type='LATTICE')
1808 lat = setObjectAndData(args, 'Lattice')
1809 for (key, val, sub) in tokens:
1810 if key == 'Points':
1811 parseLatticePoints(val, sub, lat.points)
1812 else:
1813 defaultKey(key, val, sub, "lat", [], globals(), locals())
1814 return
1816 def parseLatticePoints(args, tokens, points):
1817 global todo
1818 n = 0
1819 for (key, val, sub) in tokens:
1820 if key == 'pt':
1821 v = points[n].co_deform
1822 v.x = theScale*float(val[0])
1823 v.y = theScale*float(val[1])
1824 v.z = theScale*float(val[2])
1825 n += 1
1826 return
1829 # parseLamp (args, tokens):
1830 # parseFalloffCurve(focu, args, tokens):
1833 def parseLamp (args, tokens):
1834 global todo
1835 if verbosity > 2:
1836 print( "Parsing lamp %s" % args )
1837 bpy.ops.object.add(type='LAMP')
1838 lamp = setObjectAndData(args, 'Lamp')
1839 for (key, val, sub) in tokens:
1840 if key == 'FalloffCurve':
1841 parseFalloffCurve(lamp.falloff_curve, val, sub)
1842 else:
1843 defaultKey(key, val, sub, "lamp", [], globals(), locals())
1844 return
1846 def parseFalloffCurve(focu, args, tokens):
1847 return
1850 # parseGroup (args, tokens):
1851 # parseGroupObjects(args, tokens, grp):
1854 def parseGroup (args, tokens):
1855 global todo
1856 if verbosity > 2:
1857 print( "Parsing group %s" % args )
1859 grpName = args[0]
1860 grp = bpy.data.groups.new(grpName)
1861 loadedData['Group'][grpName] = grp
1862 for (key, val, sub) in tokens:
1863 if key == 'Objects':
1864 parseGroupObjects(val, sub, grp)
1865 else:
1866 defaultKey(key, val, sub, "grp", [], globals(), locals())
1867 return
1869 def parseGroupObjects(args, tokens, grp):
1870 global todo
1871 rig = None
1872 for (key, val, sub) in tokens:
1873 if key == 'ob':
1874 try:
1875 ob = loadedData['Object'][val[0]]
1876 grp.objects.link(ob)
1877 except:
1878 ob = None
1879 if ob:
1880 print(ob, ob.type, rig, ob.parent)
1881 if ob.type == 'ARMATURE':
1882 rig = ob
1883 elif ob.type == 'EMPTY' and rig and not ob.parent:
1884 ob.parent = rig
1885 print("SSS")
1886 return
1889 # parseWorld (args, tokens):
1892 def parseWorld (args, tokens):
1893 global todo
1894 if verbosity > 2:
1895 print( "Parsing world %s" % args )
1896 world = bpy.context.scene.world
1897 for (key, val, sub) in tokens:
1898 if key == 'Lighting':
1899 parseDefault(world.lighting, sub, {}, [])
1900 elif key == 'Mist':
1901 parseDefault(world.mist, sub, {}, [])
1902 elif key == 'Stars':
1903 parseDefault(world.stars, sub, {}, [])
1904 else:
1905 defaultKey(key, val, sub, "world", [], globals(), locals())
1906 return
1909 # parseScene (args, tokens):
1910 # parseRenderSettings(render, args, tokens):
1911 # parseToolSettings(tool, args, tokens):
1914 def parseScene (args, tokens):
1915 global todo
1916 if verbosity > 2:
1917 print( "Parsing scene %s" % args )
1918 scn = bpy.context.scene
1919 for (key, val, sub) in tokens:
1920 if key == 'NodeTree':
1921 scn.use_nodes = True
1922 parseNodeTree(scn, val, sub)
1923 elif key == 'GameData':
1924 parseDefault(scn.game_data, sub, {}, [])
1925 elif key == 'KeyingSet':
1926 pass
1927 #parseDefault(scn.keying_sets, sub, {}, [])
1928 elif key == 'ObjectBase':
1929 pass
1930 #parseDefault(scn.bases, sub, {}, [])
1931 elif key == 'RenderSettings':
1932 parseRenderSettings(scn.render, sub, [])
1933 elif key == 'ToolSettings':
1934 subkeys = {'ImagePaint' : "image_paint",
1935 'Sculpt' : "sculpt",
1936 'VertexPaint' : "vertex_paint",
1937 'WeightPaint' : "weight_paint" }
1938 parseDefault(scn.tool_settings, sub, subkeys, [])
1939 elif key == 'UnitSettings':
1940 parseDefault(scn.unit_settings, sub, {}, [])
1941 else:
1942 defaultKey(key, val, sub, "scn", [], globals(), locals())
1943 return
1945 def parseRenderSettings(render, args, tokens):
1946 global todo
1947 if verbosity > 2:
1948 print( "Parsing RenderSettings %s" % args )
1949 for (key, val, sub) in tokens:
1950 if key == 'Layer':
1951 pass
1952 #parseDefault(scn.layers, sub, [])
1953 else:
1954 defaultKey(key, val, sub, "render", [], globals(), locals())
1955 return
1958 # parseDefineProperty(args, tokens):
1961 def parseDefineProperty(args, tokens):
1962 expr = "bpy.types.Object.%s = %sProperty" % (args[0], args[1])
1963 c = '('
1964 for option in args[2:]:
1965 expr += "%s %s" % (c, option)
1966 c = ','
1967 expr += ')'
1968 #print(expr)
1969 exec(expr)
1970 #print("Done")
1971 return
1974 # correctRig(args):
1977 def correctRig(args):
1978 human = args[0]
1979 print("CorrectRig %s" % human)
1980 try:
1981 ob = loadedData['Object'][human]
1982 except:
1983 return
1984 ob.MhxShapekeyDrivers = (toggle&T_Shapekeys != 0 and toggle&T_ShapeDrivers != 0)
1985 bpy.context.scene.objects.active = ob
1986 bpy.ops.object.mode_set(mode='POSE')
1987 amt = ob.data
1988 cnslist = []
1989 for pb in ob.pose.bones:
1990 pb.bone.select = False
1991 for cns in pb.constraints:
1992 if cns.type == 'CHILD_OF':
1993 cnslist.append((pb, cns, cns.influence))
1994 cns.influence = 0
1996 for (pb, cns, inf) in cnslist:
1997 amt.bones.active = pb.bone
1998 cns.influence = 1
1999 #print("Childof %s %s %s %.2f" % (amt.name, pb.name, cns.name, inf))
2000 bpy.ops.constraint.childof_clear_inverse(constraint=cns.name, owner='BONE')
2001 bpy.ops.constraint.childof_set_inverse(constraint=cns.name, owner='BONE')
2002 cns.influence = 0
2004 for (pb, cns, inf) in cnslist:
2005 cns.influence = inf
2006 return
2010 # postProcess(args)
2013 def postProcess(args):
2014 human = args[0]
2015 print("Postprocess %s" % human)
2016 try:
2017 ob = loadedData['Object'][human]
2018 except:
2019 ob = None
2020 if toggle & T_Diamond == 0 and ob:
2021 deleteDiamonds(ob)
2022 return
2025 # deleteDiamonds(ob)
2026 # Delete joint diamonds in main mesh
2027 # Invisio = material # 1
2030 def deleteDiamonds(ob):
2031 bpy.context.scene.objects.active = ob
2032 if not bpy.context.object:
2033 return
2034 print("Delete helper geometry in %s" % bpy.context.object)
2035 bpy.ops.object.mode_set(mode='EDIT')
2036 bpy.ops.mesh.select_all(action='DESELECT')
2037 bpy.ops.object.mode_set(mode='OBJECT')
2038 me = ob.data
2039 invisioNum = -1
2040 for mn,mat in enumerate(me.materials):
2041 if "Invis" in mat.name:
2042 invisioNum = mn
2043 break
2044 if invisioNum < 0:
2045 print("WARNING: Nu Invisio material found. Cannot delete helper geometry")
2046 elif BMeshAware:
2047 for f in me.polygons:
2048 if f.material_index >= invisioNum:
2049 for vn in f.vertices:
2050 me.vertices[vn].select = True
2051 else:
2052 for f in me.faces:
2053 if f.material_index >= invisioNum:
2054 for vn in f.vertices:
2055 me.vertices[vn].select = True
2056 if BMeshAware and toggle&T_CrashSafe:
2057 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"
2058 print(theMessage)
2059 else:
2060 bpy.ops.object.mode_set(mode='EDIT')
2061 print("Do delete")
2062 bpy.ops.mesh.delete(type='VERT')
2063 print("Verts deleted")
2064 bpy.ops.object.mode_set(mode='OBJECT')
2065 print("Back to object mode")
2066 return
2069 # defaultKey(ext, args, tokens, var, exclude, glbals, lcals):
2072 theProperty = None
2074 def propNames(string):
2075 global alpha7
2076 #string = string.encode('utf-8', 'strict')
2078 # Alpha 7 compatibility
2079 if string[0:2] == "&_":
2080 string = "Mhf"+string[2:]
2081 alpha7 = True
2082 elif string[0] == "&":
2083 string = "Mha"+string[1:]
2084 alpha7 = True
2085 elif string[0] == "*":
2086 string = "Mhs"+string[1:]
2087 alpha7 = True
2088 elif len(string) > 4 and string[0:4] == "Hide":
2089 string = "Mhh"+string[4:]
2090 alpha7 = True
2092 if string[0] == "_":
2093 return None,None
2094 elif (len(string) > 3 and
2095 string[0:3] in ["Mha", "Mhf", "Mhs", "Mhh", "Mhv", "Mhc"]):
2096 name = string.replace("-","_")
2097 return name, '["%s"]' % name
2098 else:
2099 return string, '["%s"]' % string
2102 def defProp(args, var, glbals, lcals):
2103 proptype = args[0]
2104 name = propNames(args[1])[0]
2105 value = args[2]
2106 rest = 'description="%s"' % args[3].replace("_", " ")
2107 if len(args) > 4:
2108 rest += ", " + args[4]
2110 if name:
2111 #expr = 'bpy.types.Object.%s = %sProperty(%s)' % (name, proptype, rest)
2112 expr = '%s["%s"] = %s' % (var, name, value)
2113 exec(expr, glbals, lcals)
2116 def defNewProp(name, proptype, rest):
2117 expr = 'bpy.types.Object.%s = %sProperty(%s)' % (name, proptype, rest)
2118 print(expr)
2119 exec(expr)
2122 def setProperty(args, var, glbals, lcals):
2123 global theProperty
2124 tip = ""
2125 name = propNames(args[0])[0]
2126 value = args[1]
2127 if name:
2128 expr = '%s["%s"] = %s' % (var, name, value)
2129 exec(expr, glbals, lcals)
2131 if len(args) > 2:
2132 tip = 'description="%s"' % args[2].replace("_", " ")
2133 if value in ["True", "False"]:
2134 proptype = "Bool"
2135 elif value[0] in ["'",'"']:
2136 proptype = "String"
2137 elif "." in value:
2138 proptype = "Float"
2139 else:
2140 proptype = "Int"
2141 theProperty = (name, tip, proptype)
2144 def defineProperty(args):
2145 global theProperty
2146 if theProperty is None:
2147 return
2148 (name, tip, proptype) = theProperty
2149 if len(args) >= 2 and proptype != "Bool":
2150 if "BOOLEAN" in args[1]:
2151 proptype = "Bool"
2152 else:
2153 tip = tip + "," + args[1].replace(":", "=").replace('"', " ")
2154 expr = "bpy.types.Object.%s = %sProperty(%s)" % (name, proptype, tip)
2155 theProperty = None
2158 def defaultKey(ext, args, tokens, var, exclude, glbals, lcals):
2159 global todo
2161 if ext == 'Property':
2162 return setProperty(args, var, glbals, lcals)
2163 elif ext == 'PropKeys':
2164 return defineProperty(args)
2165 elif ext == 'DefProp':
2166 return defProp(args, var, glbals, lcals)
2168 if ext == 'bpyops':
2169 expr = "bpy.ops.%s" % args[0]
2170 print(expr)
2171 exec(expr)
2172 return
2174 nvar = "%s.%s" % (var, ext)
2175 #print(ext)
2176 if ext in exclude:
2177 return
2178 #print("D", nvar)
2180 if len(args) == 0:
2181 MyError("Key length 0: %s" % ext)
2183 rnaType = args[0]
2184 if rnaType == 'Add':
2185 print("*** Cannot Add yet ***")
2186 return
2188 elif rnaType == 'Refer':
2189 typ = args[1]
2190 name = args[2]
2191 data = "loadedData['%s']['%s']" % (typ, name)
2193 elif rnaType == 'Struct' or rnaType == 'Define':
2194 typ = args[1]
2195 name = args[2]
2196 try:
2197 data = eval(nvar, glbals, lcals)
2198 except:
2199 data = None
2200 # print("Old structrna", nvar, data)
2202 if data is None:
2203 try:
2204 creator = args[3]
2205 except:
2206 creator = None
2207 # print("Creator", creator, eval(var,glbals,lcals))
2209 try:
2210 rna = eval(var,glbals,lcals)
2211 data = eval(creator)
2212 except:
2213 data = None
2214 # print("New struct", nvar, typ, data)
2216 if rnaType == 'Define':
2217 loadedData[typ][name] = data
2219 if data:
2220 for (key, val, sub) in tokens:
2221 defaultKey(key, val, sub, "data", [], globals(), locals())
2223 print("Struct done", nvar)
2224 return
2226 elif rnaType == 'PropertyRNA':
2227 MyError("PropertyRNA!")
2228 #print("PropertyRNA ", ext, var)
2229 for (key, val, sub) in tokens:
2230 defaultKey(ext, val, sub, nvar, [], glbals, lcals)
2231 return
2233 elif rnaType == 'Array':
2234 for n in range(1, len(args)):
2235 expr = "%s[%d] = %s" % (nvar, n-1, args[n])
2236 exec(expr, glbals, lcals)
2237 if len(args) > 0:
2238 expr = "%s[0] = %s" % (nvar, args[1])
2239 exec(expr, glbals, lcals)
2240 return
2242 elif rnaType == 'List':
2243 data = []
2244 for (key, val, sub) in tokens:
2245 elt = eval(val[1], glbals, lcals)
2246 data.append(elt)
2248 elif rnaType == 'Matrix':
2249 return
2250 i = 0
2251 n = len(tokens)
2252 for (key, val, sub) in tokens:
2253 if key == 'row':
2254 for j in range(n):
2255 expr = "%s[%d][%d] = %g" % (nvar, i, j, float(val[j]))
2256 exec(expr, glbals, lcals)
2257 i += 1
2258 return
2260 else:
2261 try:
2262 data = loadedData[rnaType][args[1]]
2263 #print("From loaded", rnaType, args[1], data)
2264 return data
2265 except:
2266 data = rnaType
2268 #print(var, ext, data)
2269 expr = "%s = %s" % (nvar, data)
2270 try:
2271 exec(expr, glbals, lcals)
2272 except:
2273 pushOnTodoList(var, expr, glbals, lcals)
2274 return
2280 def pushOnTodoList(var, expr, glbals, lcals):
2281 global todo
2282 print("Tdo", var)
2283 print(dir(eval(var, glbals, lcals)))
2284 MyError(
2285 "Unrecognized expression %s.\n" % expr +
2286 "This can mean that Blender's python API has changed\n" +
2287 "since the MHX file was exported. Try to export again\n" +
2288 "from an up-to-date MakeHuman nightly build.\n" +
2289 "Alternatively, your Blender version may be obsolete.\n" +
2290 "Download an up-to-date version from www.graphicall.org")
2291 todo.append((expr, glbals, lcals))
2292 return
2296 # parseBoolArray(mask):
2299 def parseBoolArray(mask):
2300 list = []
2301 for c in mask:
2302 if c == '0':
2303 list.append(False)
2304 else:
2305 list.append(True)
2306 return list
2308 # parseMatrix(args, tokens)
2311 def parseMatrix(args, tokens):
2312 matrix = mathutils.Matrix()
2313 i = 0
2314 for (key, val, sub) in tokens:
2315 if key == 'row':
2316 matrix[i][0] = float(val[0])
2317 matrix[i][1] = float(val[1])
2318 matrix[i][2] = float(val[2])
2319 matrix[i][3] = float(val[3])
2320 i += 1
2321 return matrix
2324 # parseDefault(data, tokens, subkeys, exclude):
2327 def parseDefault(data, tokens, subkeys, exclude):
2328 for (key, val, sub) in tokens:
2329 if key in subkeys.keys():
2330 for (key2, val2, sub2) in sub:
2331 defaultKey(key2, val2, sub2, "data.%s" % subkeys[key], [], globals(), locals())
2332 else:
2333 defaultKey(key, val, sub, "data", exclude, globals(), locals())
2335 def parseCollection(data, tokens, exclude):
2336 return
2340 # Utilities
2344 # extractBpyType(data):
2347 def extractBpyType(data):
2348 typeSplit = str(type(data)).split("'")
2349 if typeSplit[0] != '<class ':
2350 return None
2351 classSplit = typeSplit[1].split(".")
2352 if classSplit[0] == 'bpy' and classSplit[1] == 'types':
2353 return classSplit[2]
2354 elif classSplit[0] == 'bpy_types':
2355 return classSplit[1]
2356 else:
2357 return None
2360 # Bool(string):
2363 def Bool(string):
2364 if string == 'True':
2365 return True
2366 elif string == 'False':
2367 return False
2368 else:
2369 MyError("Bool %s?" % string)
2370 """
2372 # invalid(condition):
2375 def invalid(condition):
2376 global rigLeg, rigArm, toggle
2377 res = eval(condition, globals())
2378 try:
2379 res = eval(condition, globals())
2380 #print("%s = %s" % (condition, res))
2381 return not res
2382 except:
2383 #print("%s invalid!" % condition)
2384 return True
2389 # clearScene(context):
2392 def clearScene():
2393 global toggle
2394 scn = bpy.context.scene
2395 for n in range(len(scn.layers)):
2396 scn.layers[n] = True
2397 return scn
2398 print("clearScene %s %s" % (toggle & T_Replace, scn))
2399 if not toggle & T_Replace:
2400 return scn
2402 for ob in scn.objects:
2403 if ob.type in ['MESH', 'ARMATURE', 'EMPTY', 'CURVE', 'LATTICE']:
2404 scn.objects.active = ob
2405 ob.name = "#" + ob.name
2406 try:
2407 bpy.ops.object.mode_set(mode='OBJECT')
2408 except:
2409 pass
2410 scn.objects.unlink(ob)
2411 del ob
2413 for grp in bpy.data.groups:
2414 grp.name = "#" + grp.name
2415 #print(scn.objects)
2416 return scn
2419 # hideLayers(args):
2420 # args = sceneLayers sceneHideLayers boneLayers boneHideLayers or nothing
2423 def hideLayers(args):
2424 if len(args) > 1:
2425 sceneLayers = int(args[2], 16)
2426 sceneHideLayers = int(args[3], 16)
2427 boneLayers = int(args[4], 16)
2428 # boneHideLayers = int(args[5], 16)
2429 boneHideLayers = 0
2430 else:
2431 sceneLayers = 0x00ff
2432 sceneHideLayers = 0
2433 boneLayers = 0
2434 boneHideLayers = 0
2436 scn = bpy.context.scene
2437 mask = 1
2438 hidelayers = []
2439 for n in range(20):
2440 scn.layers[n] = True if sceneLayers & mask else False
2441 if sceneHideLayers & mask:
2442 hidelayers.append(n)
2443 mask = mask << 1
2445 for ob in scn.objects:
2446 for n in hidelayers:
2447 if ob.layers[n]:
2448 ob.hide = True
2449 ob.hide_render = True
2451 if boneLayers:
2452 human = args[1]
2453 try:
2454 ob = loadedData['Object'][human]
2455 except:
2456 return
2458 mask = 1
2459 hidelayers = []
2460 for n in range(32):
2461 ob.data.layers[n] = True if boneLayers & mask else False
2462 if boneHideLayers & mask:
2463 hidelayers.append(n)
2464 mask = mask << 1
2466 for b in ob.data.bones:
2467 for n in hidelayers:
2468 if b.layers[n]:
2469 b.hide = True
2471 return
2475 # readDefaults():
2476 # writeDefaults():
2479 ConfigFile = '~/mhx_import.cfg'
2482 def readDefaults():
2483 global toggle, toggleSettings, theScale
2484 path = os.path.realpath(os.path.expanduser(ConfigFile))
2485 try:
2486 fp = open(path, 'rU')
2487 print('Storing defaults')
2488 except:
2489 print('Cannot open "%s" for reading' % path)
2490 return
2491 bver = ''
2492 for line in fp:
2493 words = line.split()
2494 if len(words) >= 3:
2495 try:
2496 toggle = int(words[0],16)
2497 theScale = float(words[1])
2498 except:
2499 print('Configuration file "%s" is corrupt' % path)
2500 fp.close()
2501 toggleSettings = toggle
2502 return
2504 def writeDefaults():
2505 global toggleSettings, theScale
2506 path = os.path.realpath(os.path.expanduser(ConfigFile))
2507 try:
2508 fp = open(path, 'w')
2509 print('Storing defaults')
2510 except:
2511 print('Cannot open "%s" for writing' % path)
2512 return
2513 fp.write("%x %f Graphicall" % (toggleSettings, theScale))
2514 fp.close()
2515 return
2517 ###################################################################################
2519 # Postprocessing of rigify rig
2521 # rigifyMhx(context, name):
2523 ###################################################################################
2525 def rigifyMhx(context, name):
2526 print("Modifying MHX rig to Rigify")
2527 scn = context.scene
2528 mhx = loadedData['Object'][name]
2529 mhx.MhxRigify = True
2530 bpy.context.scene.objects.active = mhx
2532 # Delete old widgets
2534 for ob in scn.objects:
2535 if ob.type == 'MESH' and ob.name[0:3] == "WGT":
2536 scn.objects.unlink(ob)
2539 # Save mhx bone locations
2540 heads = {}
2541 tails = {}
2542 rolls = {}
2543 parents = {}
2544 extras = {}
2545 bpy.ops.object.mode_set(mode='EDIT')
2547 newParents = {
2548 'head' : 'DEF-head',
2549 'ribs' : 'DEF-ribs',
2550 'upper_arm.L' : 'DEF-upper_arm.L.02',
2551 'thigh.L' : 'DEF-thigh.L.02',
2552 'upper_arm.R' : 'DEF-upper_arm.R.02',
2553 'thigh.R' : 'DEF-thigh.R.02',
2556 for eb in mhx.data.edit_bones:
2557 heads[eb.name] = eb.head.copy()
2558 tails[eb.name] = eb.tail.copy()
2559 rolls[eb.name] = eb.roll
2560 if eb.parent:
2561 par = eb.parent.name
2562 # print(eb.name, par)
2563 try:
2564 parents[eb.name] = newParents[par]
2565 except:
2566 parents[eb.name] = par
2567 else:
2568 parents[eb.name] = None
2569 extras[eb.name] = not eb.layers[16]
2570 bpy.ops.object.mode_set(mode='OBJECT')
2572 # Find corresponding meshes. Can be several (clothes etc.)
2573 meshes = []
2574 for ob in scn.objects:
2575 for mod in ob.modifiers:
2576 if (mod.type == 'ARMATURE' and mod.object == mhx):
2577 meshes.append((ob, mod))
2578 if meshes == []:
2579 MyError("Did not find matching mesh")
2581 # Rename Head vertex group
2582 for (mesh, mod) in meshes:
2583 try:
2584 vg = mesh.vertex_groups['DfmHead']
2585 vg.name = 'DEF-head'
2586 except:
2587 pass
2589 # Change meta bone locations
2590 scn.objects.active = None
2591 try:
2592 bpy.ops.object.armature_human_advanced_add()
2593 success = True
2594 except:
2595 success = False
2596 if not success:
2597 MyError("Unable to create advanced human. \n" \
2598 "Make sure that the Rigify addon is enabled. \n" \
2599 "It is found under Rigging.")
2600 return
2602 meta = context.object
2603 bpy.ops.object.mode_set(mode='EDIT')
2604 for eb in meta.data.edit_bones:
2605 eb.head = heads[eb.name]
2606 eb.tail = tails[eb.name]
2607 eb.roll = rolls[eb.name]
2608 extras[eb.name] = False
2610 fingerPlanes = [
2611 ('UP-thumb.L', 'thumb.01.L', 'thumb.03.L', ['thumb.02.L']),
2612 ('UP-index.L', 'finger_index.01.L', 'finger_index.03.L', ['finger_index.02.L']),
2613 ('UP-middle.L', 'finger_middle.01.L', 'finger_middle.03.L', ['finger_middle.02.L']),
2614 ('UP-ring.L', 'finger_ring.01.L', 'finger_ring.03.L', ['finger_ring.02.L']),
2615 ('UP-pinky.L', 'finger_pinky.01.L', 'finger_pinky.03.L', ['finger_pinky.02.L']),
2616 ('UP-thumb.R', 'thumb.01.R', 'thumb.03.R', ['thumb.02.R']),
2617 ('UP-index.R', 'finger_index.01.R', 'finger_index.03.R', ['finger_index.02.R']),
2618 ('UP-middle.R', 'finger_middle.01.R', 'finger_middle.03.R', ['finger_middle.02.R']),
2619 ('UP-ring.R', 'finger_ring.01.R', 'finger_ring.03.R', ['finger_ring.02.R']),
2620 ('UP-pinky.R', 'finger_pinky.01.R', 'finger_pinky.03.R', ['finger_pinky.02.R']),
2623 for (upbone, first, last, middles) in fingerPlanes:
2624 extras[upbone] = False
2625 #lineateChain(upbone, first, last, middles, 0.01, meta, heads, tails)
2627 ikPlanes = [
2628 ('UP-leg.L', 'thigh.L', 'shin.L'),
2629 ('UP-arm.L', 'upper_arm.L', 'forearm.L'),
2630 ('UP-leg.R', 'thigh.R', 'shin.R'),
2631 ('UP-arm.R', 'upper_arm.R', 'forearm.R'),
2634 for (upbone, first, last) in ikPlanes:
2635 extras[upbone] = False
2636 lineateChain(upbone, first, last, [], 0.1, meta, heads, tails)
2638 bpy.ops.object.mode_set(mode='OBJECT')
2640 # Generate rigify rig
2641 bpy.ops.pose.rigify_generate()
2642 scn.objects.unlink(meta)
2643 rigify = context.object
2644 rigify.name = name+"Rig"
2645 layers = 20*[False]
2646 layers[1] = True
2647 rigify.layers = layers
2648 rigify.show_x_ray = True
2649 for (mesh, mod) in meshes:
2650 mod.object = rigify
2652 grp = loadedData['Group'][name]
2653 grp.objects.link(rigify)
2655 # Parent widgets under empty
2656 empty = bpy.data.objects.new("Widgets", None)
2657 scn.objects.link(empty)
2658 empty.layers = 20*[False]
2659 empty.layers[19] = True
2660 empty.parent = rigify
2661 for ob in scn.objects:
2662 if ob.type == 'MESH' and ob.name[0:4] == "WGT-" and not ob.parent:
2663 ob.parent = empty
2664 grp.objects.link(ob)
2665 elif ob.parent == mhx:
2666 ob.parent = rigify
2668 # Copy extra bones to rigify rig
2669 bpy.ops.object.mode_set(mode='EDIT')
2670 for name in heads.keys():
2671 if extras[name]:
2672 eb = rigify.data.edit_bones.new(name)
2673 eb.head = heads[name]
2674 eb.tail = tails[name]
2675 eb.roll = rolls[name]
2676 for name in heads.keys():
2677 if extras[name] and parents[name]:
2678 eb = rigify.data.edit_bones[name]
2679 eb.parent = rigify.data.edit_bones[parents[name]]
2681 # Copy constraints etc.
2682 bpy.ops.object.mode_set(mode='POSE')
2683 for name in heads.keys():
2684 if extras[name]:
2685 pb1 = mhx.pose.bones[name]
2686 pb2 = rigify.pose.bones[name]
2687 pb2.custom_shape = pb1.custom_shape
2688 pb2.lock_location = pb1.lock_location
2689 pb2.lock_rotation = pb1.lock_rotation
2690 pb2.lock_scale = pb1.lock_scale
2691 b1 = pb1.bone
2692 b2 = pb2.bone
2693 b2.use_deform = b1.use_deform
2694 b2.hide_select = b1.hide_select
2695 b2.show_wire = b1.show_wire
2696 layers = 32*[False]
2697 if b1.layers[8]:
2698 layers[28] = True
2699 else:
2700 layers[29] = True
2701 if b1.layers[10]:
2702 layers[2] = True
2703 b2.layers = layers
2704 for cns1 in pb1.constraints:
2705 cns2 = copyConstraint(cns1, pb1, pb2, mhx, rigify)
2706 if cns2.type == 'CHILD_OF':
2707 rigify.data.bones.active = pb2.bone
2708 bpy.ops.constraint.childof_set_inverse(constraint=cns2.name, owner='BONE')
2710 # Create animation data
2711 if mhx.animation_data:
2712 for fcu in mhx.animation_data.drivers:
2713 rigify.animation_data.drivers.from_existing(src_driver=fcu)
2715 fixDrivers(rigify.animation_data, mhx, rigify)
2716 for (mesh, mod) in meshes:
2717 mesh.parent = rigify
2718 skeys = mesh.data.shape_keys
2719 if skeys:
2720 fixDrivers(skeys.animation_data, mhx, rigify)
2722 scn.objects.unlink(mhx)
2723 print("Rigify rig complete")
2724 return
2727 # lineateChain(upbone, first, last, middles, minDist, rig, heads, tails):
2728 # lineate(pt, start, minDist, normal, offVector):
2731 def lineateChain(upbone, first, last, middles, minDist, rig, heads, tails):
2732 fb = rig.data.edit_bones[first]
2733 lb = rig.data.edit_bones[last]
2734 uhead = heads[upbone]
2735 utail = tails[upbone]
2736 tang = lb.tail - fb.head
2737 tangent = tang/tang.length
2738 up = (uhead+utail)/2 - fb.head
2739 norm = up - tangent*tangent.dot(up)
2740 normal = norm/norm.length
2741 offVector = tangent.cross(normal)
2742 vec = utail - uhead
2743 fb.tail = lineate(fb.tail, fb.head, minDist, normal, offVector)
2744 lb.head = lineate(lb.head, fb.head, minDist, normal, offVector)
2745 for bone in middles:
2746 mb = rig.data.edit_bones[bone]
2747 mb.head = lineate(mb.head, fb.head, minDist, normal, offVector)
2748 mb.tail = lineate(mb.tail, fb.head, minDist, normal, offVector)
2749 return
2751 def lineate(pt, start, minDist, normal, offVector):
2752 diff = pt - start
2753 diff = diff - offVector*offVector.dot(diff)
2754 dist = diff.dot(normal)
2755 if dist < minDist:
2756 diff += (minDist - dist)*normal
2757 return start + diff
2760 # fixDrivers(adata, mhx, rigify):
2763 def fixDrivers(adata, mhx, rigify):
2764 if not adata:
2765 return
2766 for fcu in adata.drivers:
2767 for var in fcu.driver.variables:
2768 for targ in var.targets:
2769 if targ.id == mhx:
2770 targ.id = rigify
2771 return
2774 # copyConstraint(cns1, pb1, pb2, mhx, rigify):
2777 def copyConstraint(cns1, pb1, pb2, mhx, rigify):
2778 substitute = {
2779 'Head' : 'DEF-head',
2780 'MasterFloor' : 'root',
2781 'upper_arm.L' : 'DEF-upper_arm.L.01',
2782 'upper_arm.R' : 'DEF-upper_arm.R.01',
2783 'thigh.L' : 'DEF-thigh.L.01',
2784 'thigh.R' : 'DEF-thigh.R.01',
2785 'shin.L' : 'DEF-shin.L.01',
2786 'shin.R' : 'DEF-shin.R.01'
2789 cns2 = pb2.constraints.new(cns1.type)
2790 for prop in dir(cns1):
2791 if prop == 'target':
2792 if cns1.target == mhx:
2793 cns2.target = rigify
2794 else:
2795 cns2.target = cns1.target
2796 elif prop == 'subtarget':
2797 try:
2798 cns2.subtarget = substitute[cns1.subtarget]
2799 except:
2800 cns2.subtarget = cns1.subtarget
2801 elif prop[0] != '_':
2802 try:
2803 expr = "cns2.%s = cns1.%s" % (prop, prop)
2804 #print(pb1.name, expr)
2805 exec(expr)
2806 except:
2807 pass
2808 return cns2
2811 # class OBJECT_OT_RigifyMhxButton(bpy.types.Operator):
2814 class OBJECT_OT_RigifyMhxButton(bpy.types.Operator):
2815 bl_idname = "mhxrig.rigify_mhx"
2816 bl_label = "Rigify MHX rig"
2817 bl_options = {'UNDO'}
2819 def execute(self, context):
2820 rigifyMhx(context, context.object.name)
2821 return{'FINISHED'}
2824 # class RigifyMhxPanel(bpy.types.Panel):
2827 class RigifyMhxPanel(bpy.types.Panel):
2828 bl_label = "Rigify MHX"
2829 bl_space_type = "VIEW_3D"
2830 bl_region_type = "UI"
2832 @classmethod
2833 def poll(cls, context):
2834 if context.object:
2835 return context.object.MhxRigify
2836 return False
2838 def draw(self, context):
2839 self.layout.operator("mhxrig.rigify_mhx")
2840 return
2842 ###################################################################################
2844 # Error popup
2846 ###################################################################################
2848 DEBUG = False
2849 from bpy.props import StringProperty, FloatProperty, EnumProperty, BoolProperty
2851 class ErrorOperator(bpy.types.Operator):
2852 bl_idname = "mhx.error"
2853 bl_label = "Error when loading MHX file"
2855 def execute(self, context):
2856 return {'RUNNING_MODAL'}
2858 def invoke(self, context, event):
2859 global theErrorLines
2860 maxlen = 0
2861 for line in theErrorLines:
2862 if len(line) > maxlen:
2863 maxlen = len(line)
2864 width = 20+5*maxlen
2865 height = 20+5*len(theErrorLines)
2866 #self.report({'INFO'}, theMessage)
2867 wm = context.window_manager
2868 return wm.invoke_props_dialog(self, width=width, height=height)
2870 def draw(self, context):
2871 global theErrorLines
2872 for line in theErrorLines:
2873 self.layout.label(line)
2875 def MyError(message):
2876 global theMessage, theErrorLines, theErrorStatus
2877 theMessage = message
2878 theErrorLines = message.split('\n')
2879 theErrorStatus = True
2880 bpy.ops.mhx.error('INVOKE_DEFAULT')
2881 raise MhxError(theMessage)
2883 class MhxError(Exception):
2884 def __init__(self, value):
2885 self.value = value
2886 def __str__(self):
2887 return repr(self.value)
2889 class SuccessOperator(bpy.types.Operator):
2890 bl_idname = "mhx.success"
2891 bl_label = "MHX file successfully loaded:"
2892 message = StringProperty()
2894 def execute(self, context):
2895 return {'RUNNING_MODAL'}
2897 def invoke(self, context, event):
2898 wm = context.window_manager
2899 return wm.invoke_props_dialog(self)
2901 def draw(self, context):
2902 self.layout.label(self.message + theMessage)
2904 ###################################################################################
2906 # User interface
2908 ###################################################################################
2910 from bpy_extras.io_utils import ImportHelper
2912 MhxBoolProps = [
2913 ("enforce", "Enforce version", "Only accept MHX files of correct version", T_EnforceVersion),
2914 #("crash_safe", "Crash-safe", "Disable features that have caused Blender crashes", T_CrashSafe),
2915 ("mesh", "Mesh", "Use main mesh", T_Mesh),
2916 ("proxy", "Proxies", "Use proxies", T_Proxy),
2917 #("armature", "Armature", "Use armature", T_Armature),
2918 #("replace", "Replace scene", "Replace scene", T_Replace),
2919 ("cage", "Cage", "Load mesh deform cage", T_Cage),
2920 ("clothes", "Clothes", "Include clothes", T_Clothes),
2921 ("shapekeys", "Shapekeys", "Include shapekeys", T_Shapekeys),
2922 ("shapedrivers", "Shapekey drivers", "Include shapekey drivers", T_ShapeDrivers),
2923 #("symm", "Symmetric shapes", "Keep shapekeys symmetric", T_Symm),
2924 ("diamond", "Helper geometry", "Keep helper geometry", T_Diamond),
2925 ("rigify", "Rigify", "Create rigify control rig", T_Rigify),
2928 class ImportMhx(bpy.types.Operator, ImportHelper):
2929 """Import from MHX file format (.mhx)"""
2930 bl_idname = "import_scene.makehuman_mhx"
2931 bl_description = 'Import from MHX file format (.mhx)'
2932 bl_label = "Import MHX"
2933 bl_space_type = "PROPERTIES"
2934 bl_region_type = "WINDOW"
2935 bl_options = {'UNDO'}
2937 filename_ext = ".mhx"
2938 filter_glob = StringProperty(default="*.mhx", options={'HIDDEN'})
2939 filepath = StringProperty(subtype='FILE_PATH')
2941 scale = FloatProperty(name="Scale", description="Default meter, decimeter = 1.0", default = theScale)
2942 advanced = BoolProperty(name="Advanced settings", description="Use advanced import settings", default=False)
2943 for (prop, name, desc, flag) in MhxBoolProps:
2944 expr = '%s = BoolProperty(name="%s", description="%s", default=(toggleSettings&%s != 0))' % (prop, name, desc, flag)
2945 exec(expr)
2948 def draw(self, context):
2949 layout = self.layout
2950 layout.prop(self, "scale")
2951 layout.prop(self, "advanced")
2952 if self.advanced:
2953 for (prop, name, desc, flag) in MhxBoolProps:
2954 layout.prop(self, prop)
2957 def execute(self, context):
2958 global toggle, toggleSettings, theScale, MhxBoolProps
2959 if not self.advanced:
2960 toggle = DefaultToggle
2961 else:
2962 toggle = T_Armature
2963 for (prop, name, desc, flag) in MhxBoolProps:
2964 expr = '(%s if self.%s else 0)' % (flag, prop)
2965 toggle |= eval(expr)
2966 toggleSettings = toggle
2967 print("execute flags %x" % toggle)
2968 theScale = self.scale
2970 #filepathname = self.filepath.encode('utf-8', 'strict')
2971 try:
2972 readMhxFile(self.filepath)
2973 bpy.ops.mhx.success('INVOKE_DEFAULT', message = self.filepath)
2974 except MhxError:
2975 print("Error when loading MHX file %s:\n" % self.filepath + theMessage)
2977 if self.advanced:
2978 writeDefaults()
2979 self.advanced = False
2980 return {'FINISHED'}
2983 def invoke(self, context, event):
2984 global toggle, theScale, MhxBoolProps
2985 readDefaults()
2986 self.scale = theScale
2987 for (prop, name, desc, flag) in MhxBoolProps:
2988 expr = 'self.%s = (toggle&%s != 0)' % (prop, flag)
2989 exec(expr)
2990 context.window_manager.fileselect_add(self)
2991 return {'RUNNING_MODAL'}
2994 ###################################################################################
2996 # Lipsync panel
2998 ###################################################################################
3001 # visemes
3004 stopStaringVisemes = ({
3005 'Rest' : [
3006 ('PMouth', (0,0)),
3007 ('PUpLip', (0,-0.1)),
3008 ('PLoLip', (0,0.1)),
3009 ('PJaw', (0,0.05)),
3010 ('PTongue', (0,0.0))],
3011 'Etc' : [
3012 ('PMouth', (0,0)),
3013 ('PUpLip', (0,-0.1)),
3014 ('PLoLip', (0,0.1)),
3015 ('PJaw', (0,0.15)),
3016 ('PTongue', (0,0.0))],
3017 'MBP' : [('PMouth', (-0.3,0)),
3018 ('PUpLip', (0,1)),
3019 ('PLoLip', (0,0)),
3020 ('PJaw', (0,0.1)),
3021 ('PTongue', (0,0.0))],
3022 'OO' : [('PMouth', (-1.5,0)),
3023 ('PUpLip', (0,0)),
3024 ('PLoLip', (0,0)),
3025 ('PJaw', (0,0.2)),
3026 ('PTongue', (0,0.0))],
3027 'O' : [('PMouth', (-1.1,0)),
3028 ('PUpLip', (0,0)),
3029 ('PLoLip', (0,0)),
3030 ('PJaw', (0,0.5)),
3031 ('PTongue', (0,0.0))],
3032 'R' : [('PMouth', (-0.9,0)),
3033 ('PUpLip', (0,-0.2)),
3034 ('PLoLip', (0,0.2)),
3035 ('PJaw', (0,0.2)),
3036 ('PTongue', (0,0.0))],
3037 'FV' : [('PMouth', (0,0)),
3038 ('PUpLip', (0,0)),
3039 ('PLoLip', (0,-0.8)),
3040 ('PJaw', (0,0.1)),
3041 ('PTongue', (0,0.0))],
3042 'S' : [('PMouth', (0,0)),
3043 ('PUpLip', (0,-0.2)),
3044 ('PLoLip', (0,0.2)),
3045 ('PJaw', (0,0.05)),
3046 ('PTongue', (0,0.0))],
3047 'SH' : [('PMouth', (-0.6,0)),
3048 ('PUpLip', (0,-0.5)),
3049 ('PLoLip', (0,0.5)),
3050 ('PJaw', (0,0)),
3051 ('PTongue', (0,0.0))],
3052 'EE' : [('PMouth', (0.3,0)),
3053 ('PUpLip', (0,-0.3)),
3054 ('PLoLip', (0,0.3)),
3055 ('PJaw', (0,0.025)),
3056 ('PTongue', (0,0.0))],
3057 'AH' : [('PMouth', (-0.1,0)),
3058 ('PUpLip', (0,-0.4)),
3059 ('PLoLip', (0,0)),
3060 ('PJaw', (0,0.35)),
3061 ('PTongue', (0,0.0))],
3062 'EH' : [('PMouth', (0.1,0)),
3063 ('PUpLip', (0,-0.2)),
3064 ('PLoLip', (0,0.2)),
3065 ('PJaw', (0,0.2)),
3066 ('PTongue', (0,0.0))],
3067 'TH' : [('PMouth', (0,0)),
3068 ('PUpLip', (0,-0.5)),
3069 ('PLoLip', (0,0.5)),
3070 ('PJaw', (-0.2,0.1)),
3071 ('PTongue', (0,-0.6))],
3072 'L' : [('PMouth', (0,0)),
3073 ('PUpLip', (0,-0.2)),
3074 ('PLoLip', (0,0.2)),
3075 ('PJaw', (0.2,0.2)),
3076 ('PTongue', (0,-0.8))],
3077 'G' : [('PMouth', (0,0)),
3078 ('PUpLip', (0,-0.1)),
3079 ('PLoLip', (0,0.1)),
3080 ('PJaw', (-0.3,0.1)),
3081 ('PTongue', (0,-0.6))],
3083 'Blink' : [('PUpLid', (0,1.0)), ('PLoLid', (0,-1.0))],
3084 'Unblink' : [('PUpLid', (0,0)), ('PLoLid', (0,0))],
3087 bodyLanguageVisemes = ({
3088 'Rest' : [
3089 ('MouthWidth_L', 0),
3090 ('MouthWidth_R', 0),
3091 ('MouthNarrow_L', 0),
3092 ('MouthNarrow_R', 0),
3093 ('LipsPart', 0.6),
3094 ('UpLipsMidHeight', 0),
3095 ('LoLipsMidHeight', 0),
3096 ('LoLipsIn', 0),
3097 ('MouthOpen', 0),
3098 ('TongueBackHeight', 0),
3099 ('TongueHeight', 0),
3101 'Etc' : [
3102 ('MouthWidth_L', 0),
3103 ('MouthWidth_R', 0),
3104 ('MouthNarrow_L', 0),
3105 ('MouthNarrow_R', 0),
3106 ('LipsPart', 0.4),
3107 ('UpLipsMidHeight', 0),
3108 ('LoLipsMidHeight', 0),
3109 ('LoLipsIn', 0),
3110 ('MouthOpen', 0),
3111 ('TongueBackHeight', 0),
3112 ('TongueHeight', 0),
3114 'MBP' : [
3115 ('MouthWidth_L', 0),
3116 ('MouthWidth_R', 0),
3117 ('MouthNarrow_L', 0),
3118 ('MouthNarrow_R', 0),
3119 ('LipsPart', 0),
3120 ('UpLipsMidHeight', 0),
3121 ('LoLipsMidHeight', 0),
3122 ('LoLipsIn', 0),
3123 ('MouthOpen', 0),
3124 ('TongueBackHeight', 0),
3125 ('TongueHeight', 0),
3127 'OO' : [
3128 ('MouthWidth_L', 0),
3129 ('MouthWidth_R', 0),
3130 ('MouthNarrow_L', 1.0),
3131 ('MouthNarrow_R', 1.0),
3132 ('LipsPart', 0),
3133 ('UpLipsMidHeight', 0),
3134 ('LoLipsMidHeight', 0),
3135 ('LoLipsIn', 0),
3136 ('MouthOpen', 0.4),
3137 ('TongueBackHeight', 0),
3138 ('TongueHeight', 0),
3140 'O' : [
3141 ('MouthWidth_L', 0),
3142 ('MouthWidth_R', 0),
3143 ('MouthNarrow_L', 0.9),
3144 ('MouthNarrow_R', 0.9),
3145 ('LipsPart', 0),
3146 ('UpLipsMidHeight', 0),
3147 ('LoLipsMidHeight', 0),
3148 ('LoLipsIn', 0),
3149 ('MouthOpen', 0.8),
3150 ('TongueBackHeight', 0),
3151 ('TongueHeight', 0),
3153 'R' : [
3154 ('MouthWidth_L', 0),
3155 ('MouthWidth_R', 0),
3156 ('MouthNarrow_L', 0.5),
3157 ('MouthNarrow_R', 0.5),
3158 ('LipsPart', 0),
3159 ('UpLipsMidHeight', 0.2),
3160 ('LoLipsMidHeight', -0.2),
3161 ('LoLipsIn', 0),
3162 ('MouthOpen', 0),
3163 ('TongueBackHeight', 0),
3164 ('TongueHeight', 0),
3166 'FV' : [
3167 ('MouthWidth_L', 0.2),
3168 ('MouthWidth_R', 0.2),
3169 ('MouthNarrow_L', 0),
3170 ('MouthNarrow_R', 0),
3171 ('LipsPart', 1.0),
3172 ('UpLipsMidHeight', 0),
3173 ('LoLipsMidHeight', 0.3),
3174 ('LoLipsIn', 0.6),
3175 ('MouthOpen', 0),
3176 ('TongueBackHeight', 0),
3177 ('TongueHeight', 0),
3179 'S' : [
3180 ('MouthWidth_L', 0),
3181 ('MouthWidth_R', 0),
3182 ('MouthNarrow_L', 0),
3183 ('MouthNarrow_R', 0),
3184 ('LipsPart', 0),
3185 ('UpLipsMidHeight', 0.5),
3186 ('LoLipsMidHeight', -0.7),
3187 ('LoLipsIn', 0),
3188 ('MouthOpen', 0),
3189 ('TongueBackHeight', 0),
3190 ('TongueHeight', 0),
3192 'SH' : [
3193 ('MouthWidth_L', 0.8),
3194 ('MouthWidth_R', 0.8),
3195 ('MouthNarrow_L', 0),
3196 ('MouthNarrow_R', 0),
3197 ('LipsPart', 0),
3198 ('UpLipsMidHeight', 1.0),
3199 ('LoLipsMidHeight', 0),
3200 ('LoLipsIn', 0),
3201 ('MouthOpen', 0),
3202 ('TongueBackHeight', 0),
3203 ('TongueHeight', 0),
3205 'EE' : [
3206 ('MouthWidth_L', 0.2),
3207 ('MouthWidth_R', 0.2),
3208 ('MouthNarrow_L', 0),
3209 ('MouthNarrow_R', 0),
3210 ('LipsPart', 0),
3211 ('UpLipsMidHeight', 0.6),
3212 ('LoLipsMidHeight', -0.6),
3213 ('LoLipsIn', 0),
3214 ('MouthOpen', 0.5),
3215 ('TongueBackHeight', 0),
3216 ('TongueHeight', 0),
3218 'AH' : [
3219 ('MouthWidth_L', 0),
3220 ('MouthWidth_R', 0),
3221 ('MouthNarrow_L', 0),
3222 ('MouthNarrow_R', 0),
3223 ('LipsPart', 0),
3224 ('UpLipsMidHeight', 0.4),
3225 ('LoLipsMidHeight', 0),
3226 ('LoLipsIn', 0),
3227 ('MouthOpen', 0.7),
3228 ('TongueBackHeight', 0),
3229 ('TongueHeight', 0),
3231 'EH' : [
3232 ('MouthWidth_L', 0),
3233 ('MouthWidth_R', 0),
3234 ('MouthNarrow_L', 0),
3235 ('MouthNarrow_R', 0),
3236 ('LipsPart', 0),
3237 ('UpLipsMidHeight', 0.5),
3238 ('LoLipsMidHeight', -0.6),
3239 ('LoLipsIn', 0),
3240 ('MouthOpen', 0.25),
3241 ('TongueBackHeight', 0),
3242 ('TongueHeight', 0),
3244 'TH' : [
3245 ('MouthWidth_L', 0),
3246 ('MouthWidth_R', 0),
3247 ('MouthNarrow_L', 0),
3248 ('MouthNarrow_R', 0),
3249 ('LipsPart', 0),
3250 ('UpLipsMidHeight', 0),
3251 ('LoLipsMidHeight', 0),
3252 ('LoLipsIn', 0),
3253 ('MouthOpen', 0.2),
3254 ('TongueBackHeight', 1.0),
3255 ('TongueHeight', 1.0)
3257 'L' : [
3258 ('MouthWidth_L', 0),
3259 ('MouthWidth_R', 0),
3260 ('MouthNarrow_L', 0),
3261 ('MouthNarrow_R', 0),
3262 ('LipsPart', 0),
3263 ('UpLipsMidHeight', 0.5),
3264 ('LoLipsMidHeight', -0.5),
3265 ('LoLipsIn', 0),
3266 ('MouthOpen', -0.2),
3267 ('TongueBackHeight', 1.0),
3268 ('TongueHeight', 1.0),
3270 'G' : [
3271 ('MouthWidth_L', 0),
3272 ('MouthWidth_R', 0),
3273 ('MouthNarrow_L', 0),
3274 ('MouthNarrow_R', 0),
3275 ('LipsPart', 0),
3276 ('UpLipsMidHeight', 0.5),
3277 ('LoLipsMidHeight', -0.5),
3278 ('LoLipsIn', 0),
3279 ('MouthOpen', -0.2),
3280 ('TongueBackHeight', 1.0),
3281 ('TongueHeight', 0),
3284 'Blink' : [
3285 ('UpLidUp_L', 1),
3286 ('UpLidUp_R', 1),
3287 ('LoLidDown_L', 1),
3288 ('LoLidDown_R', 1)
3291 'Unblink' : [
3292 ('UpLidUp_L', 0),
3293 ('UpLidUp_R', 0),
3294 ('LoLidDown_L', 0),
3295 ('LoLidDown_R', 0)
3299 VisemePanelBones = {
3300 'MouthOpen' : ('PJaw', (0,0.25)),
3301 'UpLipsMidHeight' : ('PUpLipMid', (0,-0.25)),
3302 'LoLipsMidHeight' : ('PLoLipMid', (0,-0.25)),
3303 'LoLipsIn': ('PLoLipMid', (-0.25,0)),
3304 'MouthWidth_L' : ('PMouth_L', (0.25,0)),
3305 'MouthWidth_R' : ('PMouth_R', (-0.25,0)),
3306 'MouthNarrow_L' : ('PMouth_L', (-0.25,0)),
3307 'MouthNarrow_R' : ('PMouth_R', (0.25,0)),
3308 'LipsPart' : ('PMouthMid', (0, -0.25)),
3309 'TongueBackHeight': ('PTongue', (-0.25, 0)),
3310 'TongueHeight' : ('PTongue', (0, -0.25)),
3312 'UpLidUp_L' : ('PUpLid_L', (0,1.0)),
3313 'UpLidUp_R' : ('PUpLid_R', (0,1.0)),
3314 'LoLidDown_L' : ('PLoLid_L', (0,-1.0)),
3315 'LoLidDown_R' : ('PLoLid_R', (0,-1.0)),
3318 VisemeList = [
3319 ('Rest', 'Etc', 'AH'),
3320 ('MBP', 'OO', 'O'),
3321 ('R', 'FV', 'S'),
3322 ('SH', 'EE', 'EH'),
3323 ('TH', 'L', 'G')
3327 # makeVisemes(ob, scn):
3328 # class VIEW3D_OT_MhxMakeVisemesButton(bpy.types.Operator):
3331 def makeVisemes(ob, scn):
3332 if ob.type != 'MESH':
3333 print("Active object %s is not a mesh" % ob)
3334 return
3335 if not ob.data.shape_keys:
3336 print("%s has no shapekeys" % ob)
3337 return
3338 try:
3339 ob.data.shape_keys.key_blocks["VIS_Rest"]
3340 print("Visemes already created")
3341 return
3342 except:
3343 pass
3345 verts = ob.data.vertices
3346 for (vis,shapes) in bodyLanguageVisemes.items():
3347 if vis in ['Blink', 'Unblink']:
3348 continue
3349 vkey = ob.shape_key_add(name="VIS_%s" % vis)
3350 print(vkey.name)
3351 for n,v in enumerate(verts):
3352 vkey.data[n].co = v.co
3353 for (name,value) in shapes:
3354 if name[-2:] == "_R":
3355 continue
3356 skey = ob.data.shape_keys.key_blocks[name]
3357 factor = 0.75*value
3358 for n,v in enumerate(verts):
3359 vkey.data[n].co += factor*(skey.data[n].co - v.co)
3360 print("Visemes made")
3361 return
3363 class VIEW3D_OT_MhxMakeVisemesButton(bpy.types.Operator):
3364 bl_idname = "mhx.make_visemes"
3365 bl_label = "Generate viseme shapekeys"
3366 bl_options = {'UNDO'}
3368 def execute(self, context):
3369 makeVisemes(context.object, context.scene)
3370 return{'FINISHED'}
3371 """
3373 # mohoVisemes
3374 # magpieVisemes
3377 MohoVisemes = dict({
3378 'rest' : 'Rest',
3379 'etc' : 'Etc',
3380 'AI' : 'AH',
3381 'O' : 'O',
3382 'U' : 'OO',
3383 'WQ' : 'AH',
3384 'L' : 'L',
3385 'E' : 'EH',
3386 'MBP' : 'MBP',
3387 'FV' : 'FV',
3390 MagpieVisemes = dict({
3391 "CONS" : "Etc",
3392 "AI" : 'AH',
3393 "E" : "EH",
3394 "O" : "O",
3395 "UW" : "AH",
3396 "MBP" : "MBP",
3397 "L" : "L",
3398 "FV" : "FV",
3399 "Sh" : "SH",
3403 # setViseme(context, vis, setKey, frame):
3404 # setBoneLocation(context, pbone, loc, mirror, setKey, frame):
3405 # class VIEW3D_OT_MhxVisemeButton(bpy.types.Operator):
3408 def getVisemeSet(context, rig):
3409 try:
3410 visset = rig['MhxVisemeSet']
3411 except:
3412 return bodyLanguageVisemes
3413 if visset == 'StopStaring':
3414 return stopStaringVisemes
3415 elif visset == 'BodyLanguage':
3416 return bodyLanguageVisemes
3417 else:
3418 raise MhxError("Unknown viseme set %s" % visset)
3421 def setVisemeAlpha7(context, vis, visemes, setKey, frame):
3422 (rig, mesh) = getMhxRigMesh(context.object)
3423 isPanel = False
3424 isProp = False
3425 shapekeys = None
3426 scale = 0.75
3427 if rig.MhxShapekeyDrivers:
3428 try:
3429 scale *= rig.pose.bones['PFace'].bone.length
3430 isPanel = True
3431 except:
3432 isProp = True
3433 elif mesh:
3434 shapekeys = mesh.data.shape_keys.key_blocks
3436 for (skey, value) in visemes[vis]:
3437 if isPanel:
3438 (b, (x,z)) = VisemePanelBones[skey]
3439 loc = mathutils.Vector((float(x*value),0,float(z*value)))
3440 pb = rig.pose.bones[b]
3441 pb.location = loc*scale
3442 if setKey or context.tool_settings.use_keyframe_insert_auto:
3443 for n in range(3):
3444 pb.keyframe_insert('location', index=n, frame=frame, group=pb.name)
3445 elif isProp:
3446 skey = 'Mhf' + skey
3447 try:
3448 prop = rig[skey]
3449 except:
3450 continue
3451 rig[skey] = value*scale
3452 if setKey or context.tool_settings.use_keyframe_insert_auto:
3453 rig.keyframe_insert('["%s"]' % skey, frame=frame, group="Visemes")
3454 elif shapekeys:
3455 try:
3456 shapekeys[skey].value = value*scale
3457 except:
3458 continue
3459 if setKey or context.tool_settings.use_keyframe_insert_auto:
3460 shapekeys[skey].keyframe_insert("value", frame=frame)
3461 updatePose(context)
3462 return
3465 class VIEW3D_OT_MhxVisemeButton(bpy.types.Operator):
3466 bl_idname = 'mhx.pose_viseme'
3467 bl_label = 'Viseme'
3468 bl_options = {'UNDO'}
3469 viseme = StringProperty()
3471 def invoke(self, context, event):
3472 (rig, mesh) = getMhxRigMesh(context.object)
3473 visemes = getVisemeSet(context, rig)
3474 setVisemeAlpha7(context, self.viseme, visemes, False, context.scene.frame_current)
3475 return{'FINISHED'}
3480 def readLipsync(context, filepath, offs, struct):
3481 (rig, mesh) = getMhxRigMesh(context.object)
3482 if rig.MhAlpha8:
3483 props = getProps(rig, "Mhv")
3484 visemes = {}
3485 oldKeys = []
3486 for prop in props:
3487 dummy,units = getUnitsFromString("x;"+rig[prop])
3488 visemes[prop] = units
3489 props = getProps(rig, "Mhsmouth")
3490 auto = context.tool_settings.use_keyframe_insert_auto
3491 auto = True
3492 factor = rig.MhxStrength
3493 shapekeys = getMhmShapekeys(rig, mesh)
3494 else:
3495 visemes = getVisemeSet(context, rig)
3496 context.scene.objects.active = rig
3497 bpy.ops.object.mode_set(mode='POSE')
3499 fp = open(filepath, "rU")
3500 for line in fp:
3501 words= line.split()
3502 if len(words) < 2:
3503 continue
3504 else:
3505 vis = "Mhv" + struct[words[1]]
3506 frame = int(words[0])+offs
3507 if rig.MhAlpha8:
3508 setMhmProps(rig, shapekeys, "Mhsmouth", visemes[vis], factor, auto, frame)
3509 else:
3510 setVisemeAlpha7(context, vis, visemes, True, frame)
3511 fp.close()
3513 #setInterpolation(rig)
3514 updatePose(context)
3515 print("Lipsync file %s loaded" % filepath)
3518 class VIEW3D_OT_MhxMohoButton(bpy.types.Operator, ImportHelper):
3519 bl_idname = "mhx.pose_load_moho"
3520 bl_label = "Load Moho (.dat)"
3521 bl_options = {'UNDO'}
3523 filename_ext = ".dat"
3524 filter_glob = StringProperty(default="*.dat", options={'HIDDEN'})
3525 filepath = StringProperty(subtype='FILE_PATH')
3527 def execute(self, context):
3528 readLipsync(context, self.properties.filepath, context.scene.frame_start - 1, MohoVisemes)
3529 return{'FINISHED'}
3531 def invoke(self, context, event):
3532 context.window_manager.fileselect_add(self)
3533 return {'RUNNING_MODAL'}
3536 class MhxLipsyncPanel(bpy.types.Panel):
3537 bl_label = "MHX Lipsync"
3538 bl_space_type = "VIEW_3D"
3539 bl_region_type = "UI"
3540 bl_options = {'DEFAULT_CLOSED'}
3542 @classmethod
3543 def poll(cls, context):
3544 return pollMhx(context.object)
3546 def draw(self, context):
3547 rig,mesh = getMhxRigMesh(context.object)
3548 if not rig:
3549 layout.label("No MHX rig found")
3550 return
3551 layout = self.layout
3553 if rig.MhAlpha8:
3554 visemes = getProps(rig, "Mhv")
3555 if not visemes:
3556 layout.label("No visemes found")
3557 return
3559 layout.operator("mhx.pose_reset_expressions", text="Reset visemes").prefix="Mhsmouth"
3560 layout.operator("mhx.pose_key_expressions", text="Key visemes").prefix="Mhsmouth"
3561 layout.prop(rig, "MhxStrength")
3562 layout.separator()
3563 n = 0
3564 for prop in visemes:
3565 if n % 3 == 0:
3566 row = layout.row()
3567 n = 0
3568 row.operator("mhx.pose_mhm", text=prop[3:]).data="Mhsmouth;"+rig[prop]
3569 n += 1
3570 while n % 3 != 0:
3571 row.label("")
3572 n += 1
3573 layout.separator()
3574 row = layout.row()
3575 row.operator("mhx.pose_mhm", text="Blink").data="Mhsmouth;eye_left_closure:1;eye_right_closure:1"
3576 row.operator("mhx.pose_mhm", text="Unblink").data="Mhsmouth;eye_left_closure:0;eye_right_closure:0"
3577 else:
3578 layout.label("Lipsync disabled for alpha7 mhx files")
3579 return
3580 for (vis1, vis2, vis3) in VisemeList:
3581 row = layout.row()
3582 row.operator("mhx.pose_viseme", text=vis1).viseme = vis1
3583 row.operator("mhx.pose_viseme", text=vis2).viseme = vis2
3584 row.operator("mhx.pose_viseme", text=vis3).viseme = vis3
3585 layout.separator()
3586 row = layout.row()
3587 row.operator("mhx.pose_viseme", text="Blink").viseme = 'Blink'
3588 row.operator("mhx.pose_viseme", text="Unblink").viseme = 'Unblink'
3589 layout.separator()
3590 layout.operator("mhx.make_visemes")
3592 layout.separator()
3593 row = layout.row()
3594 row.operator("mhx.pose_load_moho")
3595 #layout.operator("mhx.update")
3598 # updatePose(context):
3599 # class VIEW3D_OT_MhxUpdateButton(bpy.types.Operator):
3602 def updatePose(context):
3603 scn = context.scene
3604 scn.frame_current = scn.frame_current
3605 bpy.ops.object.posemode_toggle()
3606 bpy.ops.object.posemode_toggle()
3607 return
3609 class VIEW3D_OT_MhxUpdateButton(bpy.types.Operator):
3610 bl_idname = "mhx.update"
3611 bl_label = "Update"
3613 def execute(self, context):
3614 updatePose(context)
3615 return{'FINISHED'}
3618 ###################################################################################
3620 # Expression panels
3622 ###################################################################################
3624 class VIEW3D_OT_MhxResetExpressionsButton(bpy.types.Operator):
3625 bl_idname = "mhx.pose_reset_expressions"
3626 bl_label = "Reset expressions"
3627 bl_options = {'UNDO'}
3628 prefix = StringProperty()
3630 def execute(self, context):
3631 rig,mesh = getMhxRigMesh(context.object)
3632 shapekeys = getMhmShapekeys(rig, mesh)
3633 clearMhmProps(rig, shapekeys, self.prefix, context.tool_settings.use_keyframe_insert_auto, context.scene.frame_current)
3634 updatePose(context)
3635 return{'FINISHED'}
3638 class VIEW3D_OT_MhxKeyExpressionsButton(bpy.types.Operator):
3639 bl_idname = "mhx.pose_key_expressions"
3640 bl_label = "Key expressions"
3641 bl_options = {'UNDO'}
3642 prefix = StringProperty()
3644 def execute(self, context):
3645 rig,mesh = getMhxRigMesh(context.object)
3646 props = getProps(rig, self.prefix)
3647 frame = context.scene.frame_current
3648 for prop in props:
3649 rig.keyframe_insert(prop, frame=frame)
3650 updatePose(context)
3651 return{'FINISHED'}
3654 class VIEW3D_OT_MhxPinExpressionButton(bpy.types.Operator):
3655 bl_idname = "mhx.pose_pin_expression"
3656 bl_label = "Pin"
3657 bl_options = {'UNDO'}
3658 data = StringProperty()
3660 def execute(self, context):
3661 rig,mesh = getMhxRigMesh(context.object)
3662 words = self.data.split(";")
3663 prefix = words[0]
3664 expression = words[1]
3666 props = getProps(rig, prefix)
3667 if context.tool_settings.use_keyframe_insert_auto:
3668 frame = context.scene.frame_current
3669 for prop in props:
3670 old = rig[prop]
3671 if prop == expression:
3672 rig[prop] = 1.0
3673 else:
3674 rig[prop] = 0.0
3675 if abs(rig[prop] - old) > 1e-3:
3676 rig.keyframe_insert(prop, frame=frame)
3677 else:
3678 for prop in props:
3679 if prop == expression:
3680 rig[prop] = 1.0
3681 else:
3682 rig[prop] = 0.0
3683 updatePose(context)
3684 return{'FINISHED'}
3687 def getMhmShapekeys(rig, mesh):
3688 if rig.MhxShapekeyDrivers:
3689 return None
3690 else:
3691 return mesh.data.shape_keys.key_blocks
3694 def setMhmProps(rig, shapekeys, prefix, units, factor, auto, frame):
3695 clearMhmProps(rig, shapekeys, prefix, auto, frame)
3696 for (prop, value) in units:
3697 if shapekeys:
3698 skey = prop[3:].replace("_","-")
3699 shapekeys[skey].value = factor*value
3700 if auto:
3701 shapekeys[skey].keyframe_insert("value", frame=frame)
3702 else:
3703 rig[prop] = factor*value
3704 if auto:
3705 rig.keyframe_insert(prop, frame=frame)
3708 def clearMhmProps(rig, shapekeys, prefix, auto, frame):
3709 props = getProps(rig, prefix)
3710 for prop in props:
3711 if shapekeys:
3712 skey = prop[3:].replace("_","-")
3713 shapekeys[skey].value = 0.0
3714 if auto:
3715 shapekeys[skey].keyframe_insert("value", frame=frame)
3716 else:
3717 rig[prop] = 0.0
3718 if auto:
3719 rig.keyframe_insert(prop, frame=frame)
3722 def getUnitsFromString(string):
3723 words = string.split(";")
3724 prefix = words[0]
3725 units = []
3726 for word in words[1:]:
3727 if word == "":
3728 continue
3729 unit = word.split(":")
3730 prop = "Mhs" + unit[0]
3731 value = float(unit[1])
3732 units.append((prop, value))
3733 return prefix,units
3736 class VIEW3D_OT_MhxMhmButton(bpy.types.Operator):
3737 bl_idname = "mhx.pose_mhm"
3738 bl_label = "Mhm"
3739 bl_options = {'UNDO'}
3740 data = StringProperty()
3742 def execute(self, context):
3743 rig,mesh = getMhxRigMesh(context.object)
3744 auto = context.tool_settings.use_keyframe_insert_auto
3745 frame = context.scene.frame_current
3746 shapekeys = getMhmShapekeys(rig, mesh)
3747 prefix,units = getUnitsFromString(self.data)
3748 setMhmProps(rig, shapekeys, prefix, units, rig.MhxStrength, auto, frame)
3749 updatePose(context)
3750 return{'FINISHED'}
3753 def getProps(rig, prefix):
3754 props = []
3755 for prop in rig.keys():
3756 if prop.startswith(prefix):
3757 props.append(prop)
3758 props.sort()
3759 return props
3762 class MhxExpressionsPanel(bpy.types.Panel):
3763 bl_label = "MHX Expressions"
3764 bl_space_type = "VIEW_3D"
3765 bl_region_type = "UI"
3766 bl_options = {'DEFAULT_CLOSED'}
3768 @classmethod
3769 def poll(cls, context):
3770 return pollMhx(context.object)
3772 def draw(self, context):
3773 layout = self.layout
3774 rig,mesh = getMhxRigMesh(context.object)
3775 if not rig:
3776 layout.label("No MHX rig found")
3777 return
3778 exprs = getProps(rig, "Mhe")
3779 if not exprs:
3780 layout.label("No expressions found")
3781 return
3783 layout.operator("mhx.pose_reset_expressions").prefix="Mhs"
3784 layout.operator("mhx.pose_key_expressions").prefix="Mhs"
3785 layout.prop(rig, "MhxStrength")
3786 layout.separator()
3787 for prop in exprs:
3788 layout.operator("mhx.pose_mhm", text=prop[3:]).data="Mhs;"+rig[prop]
3791 def drawShapePanel(self, context, prefix, name):
3792 layout = self.layout
3793 rig,mesh = getMhxRigMesh(context.object)
3794 if not rig:
3795 print("No MHX rig found")
3796 return
3797 if not rig.MhxShapekeyDrivers:
3798 layout.label("No shapekey drivers.")
3799 layout.label("Set %s values in mesh context instead" % name)
3800 return
3801 props = getProps(rig, prefix)
3802 if not props:
3803 layout.label("No %ss found" % name)
3804 return
3806 layout.operator("mhx.pose_reset_expressions", text="Reset %ss" % name).prefix=prefix
3807 layout.operator("mhx.pose_key_expressions", text="Reset %ss" % name).prefix=prefix
3808 #layout.operator("mhx.update")
3810 layout.separator()
3811 for prop in props:
3812 row = layout.split(0.85)
3813 row.prop(rig, '["%s"]' % prop, text=prop[3:])
3814 row.operator("mhx.pose_pin_expression", text="", icon='UNPINNED').data = (prefix + ";" + prop)
3815 return
3818 class MhxExpressionUnitsPanel(bpy.types.Panel):
3819 bl_label = "MHX Expression Units"
3820 bl_space_type = "VIEW_3D"
3821 bl_region_type = "UI"
3822 bl_options = {'DEFAULT_CLOSED'}
3824 @classmethod
3825 def poll(cls, context):
3826 return pollMhx(context.object)
3828 def draw(self, context):
3829 drawShapePanel(self, context, "Mhs", "expression")
3832 class MhxCustomShapePanel(bpy.types.Panel):
3833 bl_label = "MHX Custom Shapes"
3834 bl_space_type = "VIEW_3D"
3835 bl_region_type = "UI"
3836 bl_options = {'DEFAULT_CLOSED'}
3838 @classmethod
3839 def poll(cls, context):
3840 return pollMhx(context.object)
3842 def draw(self, context):
3843 drawShapePanel(self, context, "Mhc", "custom shape")
3846 #########################################
3848 # FK-IK snapping.
3850 #########################################
3852 def getPoseMatrix(mat, pb):
3853 restInv = pb.bone.matrix_local.inverted()
3854 if pb.parent:
3855 parInv = pb.parent.matrix.inverted()
3856 parRest = pb.parent.bone.matrix_local
3857 return restInv * (parRest * (parInv * mat))
3858 else:
3859 return restInv * mat
3862 def getGlobalMatrix(mat, pb):
3863 gmat = pb.bone.matrix_local * mat
3864 if pb.parent:
3865 parMat = pb.parent.matrix
3866 parRest = pb.parent.bone.matrix_local
3867 return parMat * (parRest.inverted() * gmat)
3868 else:
3869 return gmat
3872 def matchPoseTranslation(pb, fkPb, auto):
3873 mat = getPoseMatrix(fkPb.matrix, pb)
3874 insertLocation(pb, mat, auto)
3877 def insertLocation(pb, mat, auto):
3878 pb.location = mat.to_translation()
3879 if auto:
3880 pb.keyframe_insert("location", group=pb.name)
3881 bpy.ops.object.mode_set(mode='OBJECT')
3882 bpy.ops.object.mode_set(mode='POSE')
3885 def matchPoseRotation(pb, fkPb, auto):
3886 mat = getPoseMatrix(fkPb.matrix, pb)
3887 insertRotation(pb, mat, auto)
3890 def insertRotation(pb, mat, auto):
3891 q = mat.to_quaternion()
3892 if pb.rotation_mode == 'QUATERNION':
3893 pb.rotation_quaternion = q
3894 if auto:
3895 pb.keyframe_insert("rotation_quaternion", group=pb.name)
3896 else:
3897 pb.rotation_euler = q.to_euler(pb.rotation_mode)
3898 if auto:
3899 pb.keyframe_insert("rotation_euler", group=pb.name)
3900 bpy.ops.object.mode_set(mode='OBJECT')
3901 bpy.ops.object.mode_set(mode='POSE')
3904 def matchPoseReverse(pb, fkPb, auto):
3905 bpy.ops.object.mode_set(mode='OBJECT')
3906 bpy.ops.object.mode_set(mode='POSE')
3907 gmat = fkPb.matrix * Matrix.Rotation(math.pi, 4, 'Z')
3908 offs = pb.bone.length * fkPb.matrix.col[1]
3909 gmat[0][3] += offs[0]
3910 gmat[1][3] += offs[1]
3911 gmat[2][3] += offs[2]
3912 mat = getPoseMatrix(gmat, pb)
3913 pb.matrix_basis = mat
3914 insertLocation(pb, mat, auto)
3915 insertRotation(pb, mat, auto)
3918 def matchPoseScale(pb, fkPb, auto):
3919 mat = getPoseMatrix(fkPb.matrix, pb)
3920 pb.scale = mat.to_scale()
3921 if auto:
3922 pb.keyframe_insert("scale", group=pb.name)
3923 bpy.ops.object.mode_set(mode='OBJECT')
3924 bpy.ops.object.mode_set(mode='POSE')
3927 def fk2ikArm(context, suffix):
3928 rig = context.object
3929 auto = context.scene.tool_settings.use_keyframe_insert_auto
3930 print("Snap FK Arm%s" % suffix)
3931 snapIk,cnsIk = getSnapBones(rig, "ArmIK", suffix)
3932 (uparmIk, loarmIk, elbow, elbowPt, wrist) = snapIk
3933 snapFk,cnsFk = getSnapBones(rig, "ArmFK", suffix)
3934 (uparmFk, loarmFk, elbowPtFk, handFk) = snapFk
3935 muteConstraints(cnsFk, True)
3937 matchPoseRotation(uparmFk, uparmFk, auto)
3938 matchPoseScale(uparmFk, uparmFk, auto)
3940 matchPoseRotation(loarmFk, loarmFk, auto)
3941 matchPoseScale(loarmFk, loarmFk, auto)
3943 if rig["MhaHandFollowsWrist" + suffix]:
3944 matchPoseRotation(handFk, wrist, auto)
3945 matchPoseScale(handFk, wrist, auto)
3947 muteConstraints(cnsFk, False)
3948 return
3951 def ik2fkArm(context, suffix):
3952 rig = context.object
3953 scn = context.scene
3954 auto = scn.tool_settings.use_keyframe_insert_auto
3955 print("Snap IK Arm%s" % suffix)
3956 snapIk,cnsIk = getSnapBones(rig, "ArmIK", suffix)
3957 (uparmIk, loarmIk, elbow, elbowPt, wrist) = snapIk
3958 snapFk,cnsFk = getSnapBones(rig, "ArmFK", suffix)
3959 (uparmFk, loarmFk, elbowPtFk, handFk) = snapFk
3960 muteConstraints(cnsIk, True)
3962 #rig["MhaElbowFollowsShoulder" + suffix] = False
3963 #rig["MhaElbowFollowsWrist" + suffix] = False
3965 matchPoseTranslation(wrist, handFk, auto)
3966 matchPoseRotation(wrist, handFk, auto)
3967 matchPoseTranslation(elbow, elbowPtFk, auto)
3968 matchPoseTranslation(elbowPt, elbowPtFk, auto)
3969 setInverse(rig, elbowPt)
3970 muteConstraints(cnsIk, False)
3971 return
3974 def fk2ikLeg(context, suffix):
3975 rig = context.object
3976 auto = context.scene.tool_settings.use_keyframe_insert_auto
3977 print("Snap FK Leg%s" % suffix)
3978 snapIk,cnsIk = getSnapBones(rig, "LegIK", suffix)
3979 (uplegIk, lolegIk, kneePt, ankleIk, legIk, legFk, footIk, toeIk) = snapIk
3980 snapFk,cnsFk = getSnapBones(rig, "LegFK", suffix)
3981 (uplegFk, lolegFk, kneePtFk, footFk, toeFk) = snapFk
3982 muteConstraints(cnsFk, True)
3984 if not rig["MhaLegIkToAnkle" + suffix]:
3985 matchPoseRotation(footFk, footFk, auto)
3986 matchPoseRotation(toeFk, toeFk, auto)
3988 matchPoseRotation(uplegFk, uplegFk, auto)
3989 matchPoseScale(uplegFk, uplegFk, auto)
3991 matchPoseRotation(lolegFk, lolegFk, auto)
3992 matchPoseScale(lolegFk, lolegFk, auto)
3994 muteConstraints(cnsFk, False)
3995 return
3998 def ik2fkLeg(context, suffix):
3999 rig = context.object
4000 scn = context.scene
4001 auto = scn.tool_settings.use_keyframe_insert_auto
4002 print("Snap IK Leg%s" % suffix)
4003 snapIk,cnsIk = getSnapBones(rig, "LegIK", suffix)
4004 (uplegIk, lolegIk, kneePt, ankleIk, legIk, legFk, footIk, toeIk) = snapIk
4005 snapFk,cnsFk = getSnapBones(rig, "LegFK", suffix)
4006 (uplegFk, lolegFk, kneePtFk, footFk, toeFk) = snapFk
4007 muteConstraints(cnsIk, True)
4009 #rig["MhaKneeFollowsHip" + suffix] = False
4010 #rig["MhaKneeFollowsFoot" + suffix] = False
4012 legIkToAnkle = rig["MhaLegIkToAnkle" + suffix]
4013 if legIkToAnkle:
4014 matchPoseTranslation(ankleIk, footFk, auto)
4015 matchPoseTranslation(legIk, legFk, auto)
4016 matchPoseRotation(legIk, legFk, auto)
4017 matchPoseReverse(toeIk, toeFk, auto)
4018 matchPoseReverse(footIk, footFk, auto)
4019 setInverse(rig, ankleIk)
4020 matchPoseTranslation(kneePt, kneePtFk, auto)
4021 setInverse(rig, kneePt)
4022 if not legIkToAnkle:
4023 matchPoseTranslation(ankleIk, footFk, auto)
4025 muteConstraints(cnsIk, False)
4026 return
4030 # setInverse(rig, pb):
4033 def setInverse(rig, pb):
4034 rig.data.bones.active = pb.bone
4035 pb.bone.select = True
4036 bpy.ops.object.mode_set(mode='OBJECT')
4037 bpy.ops.object.mode_set(mode='POSE')
4038 for cns in pb.constraints:
4039 if cns.type == 'CHILD_OF':
4040 bpy.ops.constraint.childof_set_inverse(constraint=cns.name, owner='BONE')
4041 bpy.ops.object.mode_set(mode='OBJECT')
4042 bpy.ops.object.mode_set(mode='POSE')
4043 return
4049 SnapBones = {
4050 "ArmFK" : ["UpArm", "LoArm", "ElbowPTFK", "Hand"],
4051 "ArmIK" : ["UpArmIK", "LoArmIK", "Elbow", "ElbowPT", "Wrist"],
4052 "LegFK" : ["UpLeg", "LoLeg", "KneePTFK", "Foot", "Toe"],
4053 "LegIK" : ["UpLegIK", "LoLegIK", "KneePT", "Ankle", "LegIK", "LegFK", "FootRev", "ToeRev"],
4057 def getSnapBones(rig, key, suffix):
4058 names = SnapBones[key]
4059 pbones = []
4060 constraints = []
4061 for name in names:
4062 pb = rig.pose.bones[name+suffix]
4063 pbones.append(pb)
4064 for cns in pb.constraints:
4065 if cns.type == 'LIMIT_ROTATION' and not cns.mute:
4066 constraints.append(cns)
4067 return tuple(pbones),constraints
4070 def muteConstraints(constraints, value):
4071 for cns in constraints:
4072 cns.mute = value
4075 class VIEW3D_OT_MhxSnapFk2IkButton(bpy.types.Operator):
4076 bl_idname = "mhx.snap_fk_ik"
4077 bl_label = "Snap FK"
4078 bl_options = {'UNDO'}
4079 data = StringProperty()
4081 def execute(self, context):
4082 bpy.ops.object.mode_set(mode='POSE')
4083 rig = context.object
4084 if rig.MhxSnapExact:
4085 rig["MhaRotationLimits"] = 0.0
4086 (prop, old) = setSnapProp(rig, self.data, 1.0, context, False)
4087 if prop[:6] == "MhaArm":
4088 fk2ikArm(context, prop[-2:])
4089 elif prop[:6] == "MhaLeg":
4090 fk2ikLeg(context, prop[-2:])
4091 restoreSnapProp(rig, prop, old, context)
4092 return{'FINISHED'}
4095 class VIEW3D_OT_MhxSnapIk2FkButton(bpy.types.Operator):
4096 bl_idname = "mhx.snap_ik_fk"
4097 bl_label = "Snap IK"
4098 bl_options = {'UNDO'}
4099 data = StringProperty()
4101 def execute(self, context):
4102 bpy.ops.object.mode_set(mode='POSE')
4103 rig = context.object
4104 if rig.MhxSnapExact:
4105 rig["MhaRotationLimits"] = 0.0
4106 (prop, old) = setSnapProp(rig, self.data, 0.0, context, True)
4107 if prop[:6] == "MhaArm":
4108 ik2fkArm(context, prop[-2:])
4109 elif prop[:6] == "MhaLeg":
4110 ik2fkLeg(context, prop[-2:])
4111 restoreSnapProp(rig, prop, old, context)
4112 return{'FINISHED'}
4115 def setSnapProp(rig, data, value, context, isIk):
4116 words = data.split()
4117 prop = words[0]
4118 oldValue = rig[prop]
4119 rig[prop] = value
4120 ik = int(words[1])
4121 fk = int(words[2])
4122 extra = int(words[3])
4123 oldIk = rig.data.layers[ik]
4124 oldFk = rig.data.layers[fk]
4125 oldExtra = rig.data.layers[extra]
4126 rig.data.layers[ik] = True
4127 rig.data.layers[fk] = True
4128 rig.data.layers[extra] = True
4129 updatePose(context)
4130 if isIk:
4131 oldValue = 1.0
4132 oldIk = True
4133 oldFk = False
4134 else:
4135 oldValue = 0.0
4136 oldIk = False
4137 oldFk = True
4138 oldExtra = False
4139 return (prop, (oldValue, ik, fk, extra, oldIk, oldFk, oldExtra))
4142 def restoreSnapProp(rig, prop, old, context):
4143 updatePose(context)
4144 (oldValue, ik, fk, extra, oldIk, oldFk, oldExtra) = old
4145 rig[prop] = oldValue
4146 rig.data.layers[ik] = oldIk
4147 rig.data.layers[fk] = oldFk
4148 rig.data.layers[extra] = oldExtra
4149 return
4152 class VIEW3D_OT_MhxToggleFkIkButton(bpy.types.Operator):
4153 bl_idname = "mhx.toggle_fk_ik"
4154 bl_label = "FK - IK"
4155 bl_options = {'UNDO'}
4156 toggle = StringProperty()
4158 def execute(self, context):
4159 words = self.toggle.split()
4160 rig = context.object
4161 prop = words[0]
4162 value = float(words[1])
4163 onLayer = int(words[2])
4164 offLayer = int(words[3])
4165 rig.data.layers[onLayer] = True
4166 rig.data.layers[offLayer] = False
4167 rig[prop] = value
4168 # Don't do autokey - confusing.
4169 #if context.tool_settings.use_keyframe_insert_auto:
4170 # rig.keyframe_insert('["%s"]' % prop, frame=scn.frame_current)
4171 updatePose(context)
4172 return{'FINISHED'}
4176 # MHX FK/IK Switch panel
4179 class MhxFKIKPanel(bpy.types.Panel):
4180 bl_label = "MHX FK/IK Switch"
4181 bl_space_type = "VIEW_3D"
4182 bl_region_type = "UI"
4183 #bl_options = {'DEFAULT_CLOSED'}
4185 @classmethod
4186 def poll(cls, context):
4187 return (context.object and context.object.MhxRig == 'MHX')
4189 def draw(self, context):
4190 rig = context.object
4191 layout = self.layout
4193 row = layout.row()
4194 row.label("")
4195 row.label("Left")
4196 row.label("Right")
4198 layout.label("FK/IK switch")
4199 row = layout.row()
4200 row.label("Arm")
4201 self.toggleButton(row, rig, "MhaArmIk_L", " 3", " 2")
4202 self.toggleButton(row, rig, "MhaArmIk_R", " 19", " 18")
4203 row = layout.row()
4204 row.label("Leg")
4205 self.toggleButton(row, rig, "MhaLegIk_L", " 5", " 4")
4206 self.toggleButton(row, rig, "MhaLegIk_R", " 21", " 20")
4208 layout.label("IK Influence")
4209 row = layout.row()
4210 row.label("Arm")
4211 row.prop(rig, '["MhaArmIk_L"]', text="")
4212 row.prop(rig, '["MhaArmIk_R"]', text="")
4213 row = layout.row()
4214 row.label("Leg")
4215 row.prop(rig, '["MhaLegIk_L"]', text="")
4216 row.prop(rig, '["MhaLegIk_R"]', text="")
4218 try:
4219 ok = (rig["MhxVersion"] >= 12)
4220 except:
4221 ok = False
4222 if not ok:
4223 layout.label("Snapping only works with MHX version 1.12 and later.")
4224 return
4226 layout.separator()
4227 layout.label("Snapping")
4228 row = layout.row()
4229 row.label("Rotation Limits")
4230 row.prop(rig, '["MhaRotationLimits"]', text="")
4231 row.prop(rig, "MhxSnapExact", text="Exact Snapping")
4233 layout.label("Snap Arm bones")
4234 row = layout.row()
4235 row.label("FK Arm")
4236 row.operator("mhx.snap_fk_ik", text="Snap L FK Arm").data = "MhaArmIk_L 2 3 12"
4237 row.operator("mhx.snap_fk_ik", text="Snap R FK Arm").data = "MhaArmIk_R 18 19 28"
4238 row = layout.row()
4239 row.label("IK Arm")
4240 row.operator("mhx.snap_ik_fk", text="Snap L IK Arm").data = "MhaArmIk_L 2 3 12"
4241 row.operator("mhx.snap_ik_fk", text="Snap R IK Arm").data = "MhaArmIk_R 18 19 28"
4243 layout.label("Snap Leg bones")
4244 row = layout.row()
4245 row.label("FK Leg")
4246 row.operator("mhx.snap_fk_ik", text="Snap L FK Leg").data = "MhaLegIk_L 4 5 12"
4247 row.operator("mhx.snap_fk_ik", text="Snap R FK Leg").data = "MhaLegIk_R 20 21 28"
4248 row = layout.row()
4249 row.label("IK Leg")
4250 row.operator("mhx.snap_ik_fk", text="Snap L IK Leg").data = "MhaLegIk_L 4 5 12"
4251 row.operator("mhx.snap_ik_fk", text="Snap R IK Leg").data = "MhaLegIk_R 20 21 28"
4254 def toggleButton(self, row, rig, prop, fk, ik):
4255 if rig[prop] > 0.5:
4256 row.operator("mhx.toggle_fk_ik", text="IK").toggle = prop + " 0" + fk + ik
4257 else:
4258 row.operator("mhx.toggle_fk_ik", text="FK").toggle = prop + " 1" + ik + fk
4261 ###################################################################################
4263 # Posing panel
4265 ###################################################################################
4267 # class MhxDriversPanel(bpy.types.Panel):
4270 class MhxDriversPanel(bpy.types.Panel):
4271 bl_label = "MHX Drivers"
4272 bl_space_type = "VIEW_3D"
4273 bl_region_type = "UI"
4274 bl_options = {'DEFAULT_CLOSED'}
4276 @classmethod
4277 def poll(cls, context):
4278 return (context.object and context.object.MhxRig)
4280 def draw(self, context):
4281 lrProps = []
4282 props = []
4283 lrFaceProps = []
4284 faceProps = []
4285 plist = list(context.object.keys())
4286 plist.sort()
4287 for prop in plist:
4288 if prop[0:3] == 'Mha':
4289 if prop[-2:] == '_L':
4290 lrProps.append(prop[:-2])
4291 elif prop[-2:] != '_R':
4292 props.append(prop)
4293 elif prop[0:3] == 'Mhf':
4294 if prop[-2:] == '_L':
4295 lrFaceProps.append(prop[:-2])
4296 elif prop[-2:] != '_R':
4297 faceProps.append(prop)
4299 ob = context.object
4300 layout = self.layout
4301 for prop in props:
4302 layout.prop(ob, '["%s"]' % prop, text=prop[3:])
4304 layout.separator()
4305 row = layout.row()
4306 row.label("Left")
4307 row.label("Right")
4308 for prop in lrProps:
4309 row = layout.row()
4310 row.prop(ob, '["%s"]' % (prop+"_L"), text=prop[3:])
4311 row.prop(ob, '["%s"]' % (prop+"_R"), text=prop[3:])
4313 if faceProps:
4314 layout.separator()
4315 layout.label("Face shapes")
4316 for prop in faceProps:
4317 layout.prop(ob, '["%s"]' % prop, text=prop[3:])
4319 layout.separator()
4320 row = layout.row()
4321 row.label("Left")
4322 row.label("Right")
4323 for prop in lrFaceProps:
4324 row = layout.row()
4325 row.prop(ob, '["%s"]' % (prop+"_L"), text=prop[3:])
4326 row.prop(ob, '["%s"]' % (prop+"_R"), text=prop[3:])
4328 return
4330 ###################################################################################
4332 # Visibility panel
4334 ###################################################################################
4336 # class MhxVisibilityPanel(bpy.types.Panel):
4339 class MhxVisibilityPanel(bpy.types.Panel):
4340 bl_label = "MHX Visibility"
4341 bl_space_type = "VIEW_3D"
4342 bl_region_type = "UI"
4343 bl_options = {'DEFAULT_CLOSED'}
4345 @classmethod
4346 def poll(cls, context):
4347 return (context.object and context.object.MhxRig)
4349 def draw(self, context):
4350 ob = context.object
4351 layout = self.layout
4352 props = list(ob.keys())
4353 props.sort()
4354 for prop in props:
4355 if prop[0:3] == "Mhh":
4356 layout.prop(ob, '["%s"]' % prop, text="Hide %s" % prop[3:])
4357 layout.separator()
4358 layout.operator("mhx.update_textures")
4359 layout.separator()
4360 layout.operator("mhx.add_hiders")
4361 layout.operator("mhx.remove_hiders")
4362 return
4364 class VIEW3D_OT_MhxUpdateTexturesButton(bpy.types.Operator):
4365 bl_idname = "mhx.update_textures"
4366 bl_label = "Update"
4367 bl_options = {'UNDO'}
4369 def execute(self, context):
4370 scn = context.scene
4371 for mat in bpy.data.materials:
4372 if mat.animation_data:
4373 try:
4374 mat["MhxDriven"]
4375 except:
4376 continue
4377 for driver in mat.animation_data.drivers:
4378 prop = mat.path_resolve(driver.data_path)
4379 value = driver.evaluate(scn.frame_current)
4380 #print("Update %s[%d] = %s" % (driver.data_path, driver.array_index, value))
4381 prop[driver.array_index] = value
4382 return{'FINISHED'}
4384 class VIEW3D_OT_MhxAddHidersButton(bpy.types.Operator):
4385 bl_idname = "mhx.add_hiders"
4386 bl_label = "Add Hide Property"
4387 bl_options = {'UNDO'}
4389 def execute(self, context):
4390 rig = context.object
4391 for ob in context.scene.objects:
4392 if ob.select and ob != rig:
4393 prop = "Mhh%s" % ob.name
4394 defNewProp(prop, "Bool", "default=False")
4395 rig[prop] = False
4396 addHider(ob, "hide", rig, prop)
4397 addHider(ob, "hide_render", rig, prop)
4398 return{'FINISHED'}
4400 def addHider(ob, attr, rig, prop):
4401 fcu = ob.driver_add(attr)
4402 drv = fcu.driver
4403 drv.type = 'SCRIPTED'
4404 drv.expression = "x"
4405 drv.show_debug_info = True
4406 var = drv.variables.new()
4407 var.name = "x"
4408 targ = var.targets[0]
4409 targ.id = rig
4410 targ.data_path = '["%s"]' % prop
4411 return
4413 class VIEW3D_OT_MhxRemoveHidersButton(bpy.types.Operator):
4414 bl_idname = "mhx.remove_hiders"
4415 bl_label = "Remove Hide Property"
4416 bl_options = {'UNDO'}
4418 def execute(self, context):
4419 rig = context.object
4420 for ob in context.scene.objects:
4421 if ob.select and ob != rig:
4422 ob.driver_remove("hide")
4423 ob.driver_remove("hide_render")
4424 del rig["Mhh%s" % ob.name]
4425 return{'FINISHED'}
4427 ###################################################################################
4429 # Layers panel
4431 ###################################################################################
4433 MhxLayers = [
4434 (( 0, 'Root', 'MhxRoot'),
4435 ( 8, 'Face', 'MhxFace')),
4436 (( 9, 'Tweak', 'MhxTweak'),
4437 (10, 'Head', 'MhxHead')),
4438 (( 1, 'FK Spine', 'MhxFKSpine'),
4439 (17, 'IK Spine', 'MhxIKSpine')),
4440 ((13, 'Inv FK Spine', 'MhxInvFKSpine'),
4441 (16, 'Clothes', 'MhxClothes')),
4442 ('Left', 'Right'),
4443 (( 2, 'IK Arm', 'MhxIKArm'),
4444 (18, 'IK Arm', 'MhxIKArm')),
4445 (( 3, 'FK Arm', 'MhxFKArm'),
4446 (19, 'FK Arm', 'MhxFKArm')),
4447 (( 4, 'IK Leg', 'MhxIKLeg'),
4448 (20, 'IK Leg', 'MhxIKLeg')),
4449 (( 5, 'FK Leg', 'MhxFKLeg'),
4450 (21, 'FK Leg', 'MhxFKLeg')),
4451 ((12, 'Extra', 'MhxExtra'),
4452 (28, 'Extra', 'MhxExtra')),
4453 (( 6, 'Fingers', 'MhxFingers'),
4454 (22, 'Fingers', 'MhxFingers')),
4455 (( 7, 'Links', 'MhxLinks'),
4456 (23, 'Links', 'MhxLinks')),
4457 ((11, 'Palm', 'MhxPalm'),
4458 (27, 'Palm', 'MhxPalm')),
4462 # class MhxLayersPanel(bpy.types.Panel):
4465 class MhxLayersPanel(bpy.types.Panel):
4466 bl_label = "MHX Layers"
4467 bl_space_type = "VIEW_3D"
4468 bl_region_type = "UI"
4469 #bl_options = {'DEFAULT_CLOSED'}
4471 @classmethod
4472 def poll(cls, context):
4473 ob = context.object
4474 if (ob and ob.MhxRig == 'MHX'):
4475 return True
4476 return False
4478 def draw(self, context):
4479 layout = self.layout
4480 layout.operator("mhx.pose_enable_all_layers")
4481 layout.operator("mhx.pose_disable_all_layers")
4482 amt = context.object.data
4483 for (left,right) in MhxLayers:
4484 row = layout.row()
4485 if type(left) == str:
4486 row.label(left)
4487 row.label(right)
4488 else:
4489 for (n, name, prop) in [left,right]:
4490 row.prop(amt, "layers", index=n, toggle=True, text=name)
4491 return
4493 class VIEW3D_OT_MhxEnableAllLayersButton(bpy.types.Operator):
4494 bl_idname = "mhx.pose_enable_all_layers"
4495 bl_label = "Enable all layers"
4496 bl_options = {'UNDO'}
4498 def execute(self, context):
4499 rig,mesh = getMhxRigMesh(context.object)
4500 for (left,right) in MhxLayers:
4501 if type(left) != str:
4502 for (n, name, prop) in [left,right]:
4503 rig.data.layers[n] = True
4504 return{'FINISHED'}
4506 class VIEW3D_OT_MhxDisableAllLayersButton(bpy.types.Operator):
4507 bl_idname = "mhx.pose_disable_all_layers"
4508 bl_label = "Disable all layers"
4509 bl_options = {'UNDO'}
4511 def execute(self, context):
4512 rig,mesh = getMhxRigMesh(context.object)
4513 layers = 32*[False]
4514 pb = context.active_pose_bone
4515 if pb:
4516 for n in range(32):
4517 if pb.bone.layers[n]:
4518 layers[n] = True
4519 break
4520 else:
4521 layers[0] = True
4522 rig.data.layers = layers
4523 return{'FINISHED'}
4525 ###################################################################################
4527 # Common functions
4529 ###################################################################################
4531 # getMhxRigMesh(ob):
4534 def pollMhx(ob):
4535 if not ob:
4536 return False
4537 elif ob.type == 'ARMATURE':
4538 return ob.MhxRig
4539 elif ob.type == 'MESH':
4540 par = ob.parent
4541 return (par and (par.type == 'ARMATURE') and par.MhxRig)
4542 else:
4543 return False
4545 def getMhxRigMesh(ob):
4546 if ob.type == 'ARMATURE':
4547 for mesh in ob.children:
4548 if mesh.MhxMesh and ob.MhxRig:
4549 return (ob, mesh)
4550 return (ob, None)
4551 elif ob.type == 'MESH':
4552 par = ob.parent
4553 if (par and par.type == 'ARMATURE' and par.MhxRig):
4554 if ob.MhxMesh:
4555 return (par, ob)
4556 else:
4557 return (par, None)
4558 else:
4559 return (None, None)
4560 return (None, None)
4564 # setInterpolation(rig):
4567 def setInterpolation(rig):
4568 if not rig.animation_data:
4569 return
4570 act = rig.animation_data.action
4571 if not act:
4572 return
4573 for fcu in act.fcurves:
4574 for pt in fcu.keyframe_points:
4575 pt.interpolation = 'LINEAR'
4576 fcu.extrapolation = 'CONSTANT'
4577 return
4579 ###################################################################################
4581 # initialize and register
4583 ###################################################################################
4585 def menu_func(self, context):
4586 self.layout.operator(ImportMhx.bl_idname, text="MakeHuman (.mhx)...")
4588 def register():
4589 bpy.types.Object.MhAlpha8 = BoolProperty(default=True)
4590 bpy.types.Object.MhxMesh = BoolProperty(default=False)
4591 bpy.types.Object.MhxRig = StringProperty(default="")
4592 bpy.types.Object.MhxRigify = BoolProperty(default=False)
4593 bpy.types.Object.MhxSnapExact = BoolProperty(default=False)
4594 bpy.types.Object.MhxShapekeyDrivers = BoolProperty(default=True)
4595 bpy.types.Object.MhxStrength = FloatProperty(
4596 name = "Expression strength",
4597 description = "Multiply expression with this factor",
4598 default=1.0, min=-1.0, max=2.0
4600 bpy.utils.register_module(__name__)
4601 bpy.types.INFO_MT_file_import.append(menu_func)
4603 def unregister():
4604 try:
4605 bpy.utils.unregister_module(__name__)
4606 except:
4607 pass
4608 try:
4609 bpy.types.INFO_MT_file_import.remove(menu_func)
4610 except:
4611 pass
4613 if __name__ == "__main__":
4614 unregister()
4615 register()