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
28 MHX (MakeHuman eXchange format) importer for Blender 2.6x.
30 This script should be distributed with Blender.
31 If not, place it in the .blender/scripts/addons dir
32 Activate the script in the "Addons" tab (user preferences).
33 Access from the File > Import menu.
35 Alternatively, run the script in the script editor (Alt-P), and access from the File > Import menu
39 'name': 'Import: MakeHuman (.mhx)',
40 'author': 'Thomas Larsson',
42 "blender": (2, 68, 0),
43 'location': "File > Import > MakeHuman (.mhx)",
44 'description': 'Import files in the MakeHuman eXchange format (.mhx)',
46 'wiki_url': 'http://www.makehuman.org/documentation',
47 'tracker_url': 'https://developer.blender.org/T21872',
48 'category': 'Import-Export'}
55 majorVersion
= MAJOR_VERSION
56 minorVersion
= MINOR_VERSION
67 from mathutils
import Vector
, Matrix
, Quaternion
68 from bpy
.props
import *
73 theDir
= "~/makehuman/exports"
83 warnedTextureDir
= False
98 T_EnforceVersion
= 0x01
106 T_ShapeDrivers
= 0x80
109 T_Shape
= T_Shapekeys
120 DefaultToggle
= ( T_EnforceVersion
+ T_Mesh
+ T_Armature
+
121 T_Shapekeys
+ T_ShapeDrivers
+ T_Proxy
+ T_Clothes
+ T_Rigify
)
123 toggle
= DefaultToggle
124 toggleSettings
= toggle
128 # mhxEval(expr) - an attempt at a reasonably safe eval.
129 # Note that expr never contains any whitespace due to the behavior
130 # of the mhx tokenizer.
133 def mhxEval(expr
, locls
={}):
137 'theScale' : theScale
,
139 'T_EnforceVersion' : T_EnforceVersion
,
140 'T_Clothes' : T_Clothes
,
141 'T_HardParents' : T_HardParents
,
142 'T_CrashSafe' : T_CrashSafe
,
143 'T_Diamond' : T_Diamond
,
144 'T_Replace' : T_Replace
,
145 'T_Shapekeys' : T_Shapekeys
,
146 'T_ShapeDrivers' : T_ShapeDrivers
,
150 'T_Armature' : T_Armature
,
153 'T_Rigify' : T_Rigify
,
157 return eval(expr
, globls
, locls
)
163 def initLoadedData():
180 'MaterialTextureSlot' : {},
190 'MeshTextureFaceLayer' : {},
191 'MeshColorLayer' : {},
194 'ParticleSystem' : {},
196 'ObjectConstraints' : {},
197 'ObjectModifiers' : {},
202 def reinitGlobalData():
205 'MeshTextureFaceLayer', 'MeshColorLayer', 'VertexGroup', 'ShapeKey',
206 'ParticleSystem', 'ObjectConstraints', 'ObjectModifiers', 'MaterialSlot']:
211 'Object' : 'objects',
213 'Lattice' : 'lattices',
218 'Armature' : 'armatures',
220 'BoneGroup' : 'bone_groups',
222 'PoseBone' : 'pose_bones',
223 'Material' : 'materials',
224 'Texture' : 'textures',
226 'Camera' : 'cameras',
232 # readMhxFile(filePath):
235 def readMhxFile(filePath
):
236 global nErrors
, theScale
, theArmature
, defaultScale
, One
237 global toggle
, warnedVersion
, theMessage
, alpha7
, theDir
239 defaultScale
= theScale
243 warnedVersion
= False
247 theDir
= os
.path
.dirname(filePath
)
248 fileName
= os
.path
.expanduser(filePath
)
249 _
,ext
= os
.path
.splitext(fileName
)
250 if ext
.lower() != ".mhx":
251 print("Error: Not a mhx file: %s" % fileName
.encode('utf-8', 'strict'))
253 print( "Opening MHX file %s " % fileName
.encode('utf-8', 'strict') )
254 print("Toggle %x" % toggle
)
257 # ignore = False # UNUSED
266 file= open(fileName
, "rU")
267 print( "Tokenizing" )
271 lineSplit
= line
.split()
273 if len(lineSplit
) == 0:
275 elif lineSplit
[0][0] == '#':
276 if lineSplit
[0] == '#if':
277 if comment
== nesting
:
279 res
= mhxEval(lineSplit
[1])
285 elif lineSplit
[0] == '#else':
286 if comment
== nesting
-1:
288 elif comment
== nesting
:
290 elif lineSplit
[0] == '#endif':
291 if comment
== nesting
:
294 elif comment
< nesting
:
296 elif lineSplit
[0] == 'end':
304 print( "Tokenizer error at or before line %d.\nThe mhx file has been corrupted.\nTry to export it again from MakeHuman." % lineNo
)
307 elif lineSplit
[-1] == ';':
308 if lineSplit
[0] == '\\':
310 tokens
.append([key
,lineSplit
[2:-1],[]])
313 tokens
.append([key
,lineSplit
[1:-1],[]])
316 tokens
.append([key
,lineSplit
[1:],[]])
323 MyError("Tokenizer error (%d).\nThe mhx file has been corrupted.\nTry to export it again from MakeHuman." % level
)
328 scn
.objects
.active
= theArmature
329 bpy
.ops
.object.mode_set(mode
='OBJECT')
330 bpy
.ops
.object.select_all(action
='DESELECT')
331 theArmature
.select
= True
332 bpy
.ops
.object.mode_set(mode
='POSE')
333 theArmature
.MhAlpha8
= not alpha7
334 #bpy.ops.wm.properties_edit(data_path="object", property="MhxRig", value=theArmature["MhxRig"])
337 msg
= "File %s loaded in %g s" % (fileName
, time2
-time1
)
339 msg
+= " but there where %d errors. " % (nErrors
)
344 # getObject(name, var):
347 def getObject(name
, var
):
349 return loadedData
['Object'][name
]
351 raise MhxError("Bug: object %s not found" % ob
)
354 # checkMhxVersion(major, minor):
357 def checkMhxVersion(major
, minor
):
359 print("MHX", (major
,minor
), (MAJOR_VERSION
, MINOR_VERSION
), warnedVersion
)
360 if major
!= MAJOR_VERSION
or minor
< FROM_VERSION
:
365 "Wrong MHX version\n" +
366 "Expected MHX %d.%02d but the loaded file " % (MAJOR_VERSION
, MINOR_VERSION
) +
367 "has version MHX %d.%02d\n" % (major
, minor
))
368 if minor
< FROM_VERSION
:
370 "You can disable this error message by deselecting the \n" +
371 "Enforce version option when importing. Better:\n" +
372 "Export the MHX file again with an updated version of MakeHuman.\n" +
373 "The most up-to-date version of MakeHuman is the nightly build.\n")
376 "Download the most recent Blender build from www.graphicall.org. \n" +
377 "The most up-to-date version of the import script is distributed\n" +
378 "with Blender. It can also be downloaded from MakeHuman. \n" +
379 "It is located in the importers/mhx/blender25x \n" +
380 "folder and is called import_scene_mhx.py. \n")
381 if (toggle
& T_EnforceVersion
or minor
> MINOR_VERSION
):
394 def printMHXVersionInfo(versionStr
, performVersionCheck
= False):
396 val
= versionStr
.split()
398 majorVersion
= int(val
[0])
399 minorVersion
= int(val
[1])
401 for debugVal
in val
[2:]:
402 debugVal
= debugVal
.replace("_"," ")
403 dKey
, dVal
= debugVal
.split(':')
404 versionInfo
[ dKey
.strip() ] = dVal
.strip()
406 if 'MHXImporter' in versionInfo
:
407 print("MHX importer version: ", versionInfo
["MHXImporter"])
408 if performVersionCheck
:
409 checkMhxVersion(majorVersion
, minorVersion
)
411 print("MHX: %s.%s" % (majorVersion
, minorVersion
))
413 for (key
, value
) in versionInfo
.items():
414 if key
== "MHXImporter":
416 print("%s: %s" % (key
, value
))
419 global MHX249
, ifResult
, theScale
, defaultScale
, One
420 global majorVersion
, minorVersion
423 for (key
, val
, sub
) in tokens
:
426 importerVerStr
= "MHXImporter:_%s" % (bl_info
["version"])
427 versionInfoStr
= " ".join(val
+ [importerVerStr
])
429 printMHXVersionInfo(versionInfoStr
, performVersionCheck
= True)
430 elif key
== 'MHX249':
431 MHX249
= mhxEval(val
[0])
432 print("Blender 2.49 compatibility mode is %s\n" % MHX249
)
436 msg
= concatList(val
)
439 msg
= concatList(val
)
442 msg
= concatList(val
)
444 elif key
== 'NoScale':
448 theScale
= defaultScale
450 elif key
== "Object":
451 parseObject(val
, sub
, versionInfoStr
)
454 data
= parseMesh(val
, sub
)
455 elif key
== "Armature":
456 data
= parseArmature(val
, sub
)
458 data
= parsePose(val
, sub
)
459 elif key
== "Action":
460 data
= parseAction(val
, sub
)
461 elif key
== "Material":
462 data
= parseMaterial(val
, sub
)
463 elif key
== "Texture":
464 data
= parseTexture(val
, sub
)
466 data
= parseImage(val
, sub
)
468 data
= parseCurve(val
, sub
)
469 elif key
== "TextCurve":
470 data
= parseTextCurve(val
, sub
)
471 elif key
== "Lattice":
472 data
= parseLattice(val
, sub
)
474 data
= parseGroup(val
, sub
)
476 data
= parseLamp(val
, sub
)
478 data
= parseWorld(val
, sub
)
480 data
= parseScene(val
, sub
)
481 elif key
== "DefineProperty":
482 parseDefineProperty(val
, sub
)
483 elif key
== "Process":
484 parseProcess(val
, sub
)
485 elif key
== "PostProcess":
488 elif key
== "CorrectRig":
490 elif key
== "Rigify":
491 if toggle
& T_Rigify
:
492 rigifyMhx(bpy
.context
)
493 elif key
== 'AnimationData':
495 ob
= loadedData
['Object'][val
[0]]
499 bpy
.context
.scene
.objects
.active
= ob
500 parseAnimationData(ob
, val
, sub
)
501 elif key
== 'MaterialAnimationData':
503 ob
= loadedData
['Object'][val
[0]]
507 bpy
.context
.scene
.objects
.active
= ob
508 mat
= ob
.data
.materials
[int(val
[2])]
509 parseAnimationData(mat
, val
, sub
)
510 elif key
== 'ShapeKeys':
512 ob
= loadedData
['Object'][val
[0]]
514 MyError("ShapeKeys object %s does not exist" % val
[0])
516 bpy
.context
.scene
.objects
.active
= ob
517 parseShapeKeys(ob
, ob
.data
, val
, sub
)
519 data
= parseDefaultType(key
, val
, sub
)
523 # parseDefaultType(typ, args, tokens):
526 def parseDefaultType(typ
, args
, tokens
):
529 expr
= "bpy.data.%s.new('%s')" % (Plural
[typ
], name
)
532 bpyType
= typ
.capitalize()
533 loadedData
[bpyType
][name
] = data
537 for (key
, val
, sub
) in tokens
:
538 defaultKey(key
, val
, sub
, data
)
545 def concatList(elts
):
548 string
+= " %s" % elt
552 # parseAction(args, tokens):
553 # parseFCurve(fcu, args, tokens):
554 # parseKeyFramePoint(pt, args, tokens):
557 def parseAction(args
, tokens
):
562 ob
= bpy
.context
.object
563 bpy
.ops
.object.mode_set(mode
='POSE')
564 if ob
.animation_data
:
565 ob
.animation_data
.action
= None
567 for (key
, val
, sub
) in tokens
:
569 prepareActionFCurve(ob
, created
, val
, sub
)
571 act
= ob
.animation_data
.action
572 loadedData
['Action'][name
] = act
574 print("Ignoring action %s" % name
)
577 print("Action", name
, act
, ob
)
579 for (key
, val
, sub
) in tokens
:
581 fcu
= parseActionFCurve(act
, ob
, val
, sub
)
583 defaultKey(key
, val
, sub
, act
)
584 ob
.animation_data
.action
= None
585 bpy
.ops
.object.mode_set(mode
='OBJECT')
588 def prepareActionFCurve(ob
, created
, args
, tokens
):
591 (expr
, channel
) = channelFromDataPath(dataPath
, index
)
593 if channel
in created
[expr
]:
596 created
[expr
].append(channel
)
598 created
[expr
] = [channel
]
601 for (key
, val
, sub
) in tokens
:
603 times
.append(int(val
[0]))
608 print("Ignoring illegal expression: %s" % expr
)
613 #bpy.context.scene.current_frame = t
614 bpy
.ops
.anim
.change_frame(frame
= t
)
616 data
.keyframe_insert(channel
)
620 #print("failed", data, expr, channel)
622 print("Mismatch", n
, len(times
), expr
, channel
)
625 def channelFromDataPath(dataPath
, index
):
626 words
= dataPath
.split(']')
631 elif len(words
) == 2:
632 # pose.bones["tongue"].location
633 expr
= "ob.%s]" % (words
[0])
634 cwords
= words
[1].split('.')
636 elif len(words
) == 3:
637 # pose.bones["brow.R"]["mad"]
638 expr
= "ob.%s]" % (words
[0])
639 cwords
= words
[1].split('"')
641 return (expr
, channel
)
643 def parseActionFCurve(act
, ob
, args
, tokens
):
646 (expr
, channel
) = channelFromDataPath(dataPath
, index
)
650 for fcu
in act
.fcurves
:
651 (expr1
, channel1
) = channelFromDataPath(fcu
.data_path
, fcu
.array_index
)
652 if expr1
== expr
and channel1
== channel
and fcu
.array_index
== index
:
659 for (key
, val
, sub
) in tokens
:
662 pt
= fcu
.keyframe_points
[n
]
663 pt
.interpolation
= 'LINEAR'
664 pt
= parseKeyFramePoint(pt
, val
, sub
)
669 #MyError("kp", fcu, n, len(fcu.keyframe_points), val)
671 defaultKey(key
, val
, sub
, fcu
)
674 def parseKeyFramePoint(pt
, args
, tokens
):
675 pt
.co
= (float(args
[0]), float(args
[1]))
677 pt
.handle1
= (float(args
[2]), float(args
[3]))
678 pt
.handle2
= (float(args
[3]), float(args
[5]))
682 # parseAnimationData(rna, args, tokens):
683 # parseDriver(drv, args, tokens):
684 # parseDriverVariable(var, args, tokens):
687 def parseAnimationData(rna
, args
, tokens
):
688 if not mhxEval(args
[1]):
690 if rna
.animation_data
is None:
691 rna
.animation_data_create()
692 adata
= rna
.animation_data
693 for (key
, val
, sub
) in tokens
:
695 fcu
= parseAnimDataFCurve(adata
, rna
, val
, sub
)
697 defaultKey(key
, val
, sub
, adata
)
700 def parseAnimDataFCurve(adata
, rna
, args
, tokens
):
706 for (key
, val
, sub
) in tokens
:
708 fcu
= parseDriver(adata
, dataPath
, index
, rna
, val
, sub
)
709 fmod
= fcu
.modifiers
[0]
710 fcu
.modifiers
.remove(fmod
)
711 elif key
== 'FModifier':
712 parseFModifier(fcu
, val
, sub
)
714 pt
= fcu
.keyframe_points
.insert(n
, 0)
715 pt
.interpolation
= 'LINEAR'
716 pt
= parseKeyFramePoint(pt
, val
, sub
)
719 defaultKey(key
, val
, sub
, fcu
)
723 fcurve = con.driver_add("influence", 0)
724 driver = fcurve.driver
725 driver.type = 'AVERAGE'
727 def parseDriver(adata
, dataPath
, index
, rna
, args
, tokens
):
728 if dataPath
[-1] == ']':
729 words
= dataPath
.split(']')
730 expr
= "rna." + words
[0] + ']'
731 pwords
= words
[1].split('"')
736 words
= dataPath
.split('.')
739 for n
in range(len(words
)-1):
740 expr
+= "." + words
[n
]
741 expr
+= ".driver_add('%s', index)" % channel
743 fcu
= mhxEval(expr
, locals())
746 for (key
, val
, sub
) in tokens
:
747 if key
== 'DriverVariable':
748 var
= parseDriverVariable(drv
, rna
, val
, sub
)
750 defaultKey(key
, val
, sub
, drv
)
753 def parseDriverVariable(drv
, rna
, args
, tokens
):
754 var
= drv
.variables
.new()
758 for (key
, val
, sub
) in tokens
:
760 parseDriverTarget(var
, nTarget
, rna
, val
, sub
)
763 defaultKey(key
, val
, sub
, var
)
766 def parseFModifier(fcu
, args
, tokens
):
767 fmod
= fcu
.modifiers
.new(args
[0])
768 #fmod = fcu.modifiers[0]
769 for (key
, val
, sub
) in tokens
:
770 defaultKey(key
, val
, sub
, fmod
)
774 var = driver.variables.new()
775 var.name = target_bone
776 var.targets[0].id_type = 'OBJECT'
777 var.targets[0].id = obj
778 var.targets[0].rna_path = driver_path
780 def parseDriverTarget(var
, nTarget
, rna
, args
, tokens
):
781 targ
= var
.targets
[nTarget
]
783 #targ.id_type = args[1]
784 dtype
= args
[1].capitalize()
786 targ
.id = loadedData
[dtype
][name
]
787 for (key
, val
, sub
) in tokens
:
788 if key
== 'data_path':
789 words
= val
[0].split('"')
791 targ
.data_path
= propNames(words
[1])[1]
793 targ
.data_path
= propNames(val
)[1]
795 defaultKey(key
, val
, sub
, targ
)
800 # parseMaterial(args, ext, tokens):
801 # parseMTex(mat, args, tokens):
802 # parseTexture(args, tokens):
805 def parseMaterial(args
, tokens
):
807 mat
= bpy
.data
.materials
.new(name
)
810 loadedData
['Material'][name
] = mat
811 for (key
, val
, sub
) in tokens
:
813 parseMTex(mat
, val
, sub
)
815 parseRamp(mat
, val
, sub
)
816 elif key
== 'RaytraceTransparency':
817 parseDefault(mat
.raytrace_transparency
, sub
, {}, [])
819 parseDefault(mat
.halo
, sub
, {}, [])
821 parseDefault(mat
.subsurface_scattering
, sub
, {}, [])
822 elif key
== 'Strand':
823 parseDefault(mat
.strand
, sub
, {}, [])
824 elif key
== 'NodeTree':
826 parseNodeTree(mat
.node_tree
, val
, sub
)
827 elif key
== 'AnimationData':
828 parseAnimationData(mat
, val
, sub
)
830 exclude
= ['specular_intensity', 'tangent_shading']
831 defaultKey(key
, val
, sub
, mat
)
835 def parseMTex(mat
, args
, tokens
):
840 tex
= loadedData
['Texture'][texname
]
841 mtex
= mat
.texture_slots
.add()
842 mtex
.texture_coords
= texco
845 for (key
, val
, sub
) in tokens
:
846 defaultKey(key
, val
, sub
, mtex
)
850 def parseTexture(args
, tokens
):
852 print( "Parsing texture %s" % args
)
854 tex
= bpy
.data
.textures
.new(name
=name
, type=args
[1])
855 loadedData
['Texture'][name
] = tex
857 for (key
, val
, sub
) in tokens
:
861 img
= loadedData
['Image'][imgName
]
864 msg
= "Unable to load image '%s'" % val
[0]
866 parseRamp(tex
, val
, sub
)
867 elif key
== 'NodeTree':
869 parseNodeTree(tex
.node_tree
, val
, sub
)
871 defaultKey(key
, val
, sub
, tex
, ['use_nodes', 'use_textures', 'contrast', 'use_alpha'])
875 def parseRamp(data
, args
, tokens
):
876 setattr(data
, "use_%s" % args
[0], True)
877 ramp
= getattr(data
, args
[0])
880 for (key
, val
, sub
) in tokens
:
882 elts
[n
].color
= mhxEval(val
[0], locals())
883 elts
[n
].position
= mhxEval(val
[1], locals())
886 defaultKey(key
, val
, sub
, tex
, ['use_nodes', 'use_textures', 'contrast'])
888 def parseSSS(mat
, args
, tokens
):
889 sss
= mat
.subsurface_scattering
890 for (key
, val
, sub
) in tokens
:
891 defaultKey(key
, val
, sub
, sss
)
893 def parseStrand(mat
, args
, tokens
):
895 for (key
, val
, sub
) in tokens
:
896 defaultKey(key
, val
, sub
, strand
)
899 # parseNodeTree(tree, args, tokens):
900 # parseNode(node, args, tokens):
901 # parseSocket(socket, args, tokens):
904 def parseNodeTree(tree
, args
, tokens
):
906 print("Tree", tree
, args
)
907 print(list(tree
.nodes
))
909 for (key
, val
, sub
) in tokens
:
911 parseNodes(tree
.nodes
, val
, sub
)
913 defaultKey(key
, val
, sub
, tree
)
915 def parseNodes(nodes
, args
, tokens
):
916 print("Nodes", nodes
, args
)
919 for (key
, val
, sub
) in tokens
:
921 parseSocket(node
.inputs
, val
, sub
)
922 elif key
== 'Outputs':
923 parseSocket(node
.outputs
, val
, sub
)
925 defaultKey(key
, val
, sub
, node
)
927 def parseNode(node
, args
, tokens
):
928 print("Node", node
, args
)
929 print(list(node
.inputs
), list(node
.outputs
))
931 for (key
, val
, sub
) in tokens
:
933 parseSocket(node
.inputs
, val
, sub
)
934 elif key
== 'Outputs':
935 parseSocket(node
.outputs
, val
, sub
)
937 defaultKey(key
, val
, sub
, node
)
939 def parseSocket(socket
, args
, tokens
):
940 print("Socket", socket
, args
)
941 socket
.name
= args
[0]
942 for (key
, val
, sub
) in tokens
:
944 parseNode(tree
.nodes
, val
, sub
)
946 defaultKey(key
, val
, sub
, tree
)
951 # loadImage(filepath):
952 # parseImage(args, tokens):
955 def loadImage(relFilepath
):
956 filepath
= os
.path
.normpath(os
.path
.join(theDir
, relFilepath
))
957 print( "Loading %s" % filepath
.encode('utf-8','strict'))
958 if os
.path
.isfile(filepath
):
959 #print( "Found file %s." % filepath.encode('utf-8','strict') )
961 img
= bpy
.data
.images
.load(filepath
)
964 print( "Cannot read image" )
967 print( "No such file: %s" % filepath
.encode('utf-8','strict') )
971 def parseImage(args
, tokens
):
974 for (key
, val
, sub
) in tokens
:
975 if key
== 'Filename':
977 for n
in range(1,len(val
)):
978 filename
+= " " + val
[n
]
979 img
= loadImage(filename
)
984 defaultKey(key
, val
, sub
, img
, ['depth', 'dirty', 'has_data', 'size', 'type', 'use_premultiply'])
985 print ("Image %s" % img
)
986 loadedData
['Image'][imgName
] = img
990 # parseObject(args, tokens):
991 # createObject(type, name, data, datName):
992 # setObjectAndData(args, typ):
995 def parseObject(args
, tokens
, versionInfoStr
=""):
997 print( "Parsing object %s" % args
)
1003 ob
= bpy
.data
.objects
.new(name
, None)
1004 loadedData
['Object'][name
] = ob
1005 linkObject(ob
, None)
1008 data
= loadedData
[typ
.capitalize()][datName
]
1010 MyError("Failed to find data: %s %s %s" % (name
, typ
, datName
))
1014 ob
= loadedData
['Object'][name
]
1015 bpy
.context
.scene
.objects
.active
= ob
1016 #print("Found data", ob)
1021 ob
= createObject(typ
, name
, data
, datName
)
1022 linkObject(ob
, data
)
1024 for (key
, val
, sub
) in tokens
:
1025 if key
== 'Modifier':
1026 parseModifier(ob
, val
, sub
)
1027 elif key
== 'Constraint':
1028 parseConstraint(ob
.constraints
, None, val
, sub
)
1029 elif key
== 'AnimationData':
1030 parseAnimationData(ob
, val
, sub
)
1031 elif key
== 'ParticleSystem':
1032 parseParticleSystem(ob
, val
, sub
)
1033 elif key
== 'FieldSettings':
1034 parseDefault(ob
.field
, sub
, {}, [])
1036 defaultKey(key
, val
, sub
, ob
, ['type', 'data'])
1039 print('============= updating version string %s' % versionInfoStr
)
1040 ob
.MhxVersionStr
= versionInfoStr
1042 print('============= not updating version str')
1044 if bpy
.context
.object == ob
:
1045 if ob
.type == 'MESH':
1046 bpy
.ops
.object.shade_smooth()
1048 print("Context", ob
, bpy
.context
.object, bpy
.context
.scene
.objects
.active
)
1051 def createObject(typ
, name
, data
, datName
):
1052 # print( "Creating object %s %s %s" % (typ, name, data) )
1053 ob
= bpy
.data
.objects
.new(name
, data
)
1055 loadedData
[typ
.capitalize()][datName
] = data
1056 loadedData
['Object'][name
] = ob
1059 def linkObject(ob
, data
):
1060 #print("Data", data, ob.data)
1061 if data
and ob
.data
is None:
1063 scn
= bpy
.context
.scene
1064 scn
.objects
.link(ob
)
1065 scn
.objects
.active
= ob
1066 #print("Linked object", ob)
1067 #print("Scene", scn)
1068 #print("Active", scn.objects.active)
1069 #print("Context", bpy.context.object)
1072 def setObjectAndData(args
, typ
):
1075 #bpy.ops.object.add(type=typ)
1076 ob
= bpy
.context
.object
1078 ob
.data
.name
= datName
1079 loadedData
[typ
][datName
] = ob
.data
1080 loadedData
['Object'][obName
] = ob
1085 # parseModifier(ob, args, tokens):
1089 def parseModifier(ob
, args
, tokens
):
1092 if typ
== 'PARTICLE_SYSTEM':
1094 mod
= ob
.modifiers
.new(name
, typ
)
1095 for (key
, val
, sub
) in tokens
:
1096 if key
== 'HookAssignNth':
1097 if val
[0] == 'CURVE':
1098 hookAssignNth(mod
, int(val
[1]), True, ob
.data
.splines
[0].points
)
1099 elif val
[0] == 'LATTICE':
1100 hookAssignNth(mod
, int(val
[1]), False, ob
.data
.points
)
1101 elif val
[0] == 'MESH':
1102 hookAssignNth(mod
, int(val
[1]), True, ob
.data
.vertices
)
1104 MyError("Unknown hook %s" % val
)
1106 defaultKey(key
, val
, sub
, mod
)
1109 def hookAssignNth(mod
, n
, select
, points
):
1113 points
[n
].select
= True
1116 sel
.append(pt
.select
)
1117 #print(mod, sel, n, points)
1119 bpy
.ops
.object.mode_set(mode
='EDIT')
1120 bpy
.ops
.object.hook_reset(modifier
=mod
.name
)
1121 bpy
.ops
.object.hook_select(modifier
=mod
.name
)
1122 bpy
.ops
.object.hook_assign(modifier
=mod
.name
)
1123 bpy
.ops
.object.mode_set(mode
='OBJECT')
1127 # parseParticleSystem(ob, args, tokens):
1128 # parseParticles(particles, args, tokens):
1129 # parseParticle(par, args, tokens):
1132 def parseParticleSystem(ob
, args
, tokens
):
1133 print(ob
, bpy
.context
.object)
1134 pss
= ob
.particle_systems
1135 print(pss
, pss
.values())
1138 #psys = pss.new(name, typ)
1139 bpy
.ops
.object.particle_system_add()
1140 print(pss
, pss
.values())
1143 psys
.settings
.type = typ
1144 loadedData
['ParticleSystem'][name
] = psys
1147 for (key
, val
, sub
) in tokens
:
1148 if key
== 'Particles':
1149 parseParticles(psys
, val
, sub
)
1151 defaultKey(key
, val
, sub
, psys
)
1154 def parseParticles(psys
, args
, tokens
):
1155 particles
= psys
.particles
1156 bpy
.ops
.particle
.particle_edit_toggle()
1158 for (key
, val
, sub
) in tokens
:
1159 if key
== 'Particle':
1160 parseParticle(particles
[n
], val
, sub
)
1163 for par
in particles
:
1164 defaultKey(key
, val
, sub
, par
)
1165 bpy
.ops
.particle
.particle_edit_toggle()
1168 def parseParticle(par
, args
, tokens
):
1170 for (key
, val
, sub
) in tokens
:
1173 h
.location
= mhxEval(val
[0], locals())
1174 h
.time
= int(val
[1])
1175 h
.weight
= float(val
[2])
1177 elif key
== 'location':
1178 par
.location
= mhxEval(val
[0], locals())
1182 # unpackList(list_of_tuples):
1185 def unpackList(list_of_tuples
):
1187 for t
in list_of_tuples
:
1194 # parseMesh (args, tokens):
1197 def parseMesh (args
, tokens
):
1200 print( "Parsing mesh %s" % args
)
1204 me
= bpy
.data
.meshes
.new(mename
)
1205 ob
= createObject('MESH', obname
, me
, mename
)
1213 for (key
, val
, sub
) in tokens
:
1215 verts
= parseVerts(sub
)
1216 elif key
== 'Edges':
1217 edges
= parseEdges(sub
)
1218 elif key
== 'Faces':
1219 faces
= parseFaces(sub
)
1222 me
.from_pydata(verts
, [], faces
)
1224 me
.from_pydata(verts
, edges
, [])
1237 for (key
, val
, sub
) in tokens
:
1238 if key
== 'Verts' or key
== 'Edges' or key
== 'Faces':
1240 elif key
== 'MeshTextureFaceLayer':
1242 parseUvTextureBMesh(val
, sub
, me
)
1244 parseUvTextureNoBMesh(val
, sub
, me
)
1245 elif key
== 'MeshColorLayer':
1246 parseVertColorLayer(val
, sub
, me
)
1247 elif key
== 'VertexGroup':
1248 parseVertexGroup(ob
, me
, val
, sub
)
1249 elif key
== 'ShapeKeys':
1250 parseShapeKeys(ob
, me
, val
, sub
)
1251 elif key
== 'Material':
1253 mat
= loadedData
['Material'][val
[0]]
1257 me
.materials
.append(mat
)
1259 defaultKey(key
, val
, sub
, me
)
1261 for (key
, val
, sub
) in tokens
:
1264 parseFaces2BMesh(sub
, me
)
1266 parseFaces2NoBMesh(sub
, me
)
1270 # parseVerts(tokens):
1271 # parseEdges(tokens):
1272 # parseFaces(tokens):
1273 # parseFaces2(tokens, me):
1276 def parseVerts(tokens
):
1278 for (key
, val
, sub
) in tokens
:
1280 verts
.append( (theScale
*float(val
[0]), theScale
*float(val
[1]), theScale
*float(val
[2])) )
1283 def parseEdges(tokens
):
1285 for (key
, val
, sub
) in tokens
:
1287 edges
.append((int(val
[0]), int(val
[1])))
1290 def parseFaces(tokens
):
1292 for (key
, val
, sub
) in tokens
:
1295 face
= [int(val
[0]), int(val
[1]), int(val
[2])]
1297 face
= [int(val
[0]), int(val
[1]), int(val
[2]), int(val
[3])]
1301 def parseFaces2BMesh(tokens
, me
):
1303 for (key
, val
, sub
) in tokens
:
1306 f
.material_index
= int(val
[0])
1307 f
.use_smooth
= int(val
[1])
1313 for i
in range(npts
):
1315 f
.material_index
= mn
1322 f
.material_index
= mn
1323 elif key
== 'ftall':
1325 smooth
= int(val
[1])
1326 for f
in me
.polygons
:
1327 f
.material_index
= mat
1328 f
.use_smooth
= smooth
1331 def parseFaces2NoBMesh(tokens
, me
):
1333 for (key
, val
, sub
) in tokens
:
1336 f
.material_index
= int(val
[0])
1337 f
.use_smooth
= int(val
[1])
1343 for i
in range(npts
):
1345 f
.material_index
= mn
1352 f
.material_index
= mn
1353 elif key
== 'ftall':
1355 smooth
= int(val
[1])
1357 f
.material_index
= mat
1358 f
.use_smooth
= smooth
1363 # parseUvTexture(args, tokens, me,):
1364 # parseUvTexData(args, tokens, uvdata):
1367 def parseUvTextureBMesh(args
, tokens
, me
):
1369 bpy
.ops
.mesh
.uv_texture_add()
1370 uvtex
= me
.uv_textures
[-1]
1372 uvloop
= me
.uv_layers
[-1]
1373 loadedData
['MeshTextureFaceLayer'][name
] = uvloop
1374 for (key
, val
, sub
) in tokens
:
1376 parseUvTexDataBMesh(val
, sub
, uvloop
.data
)
1378 defaultKey(key
, val
, sub
, uvtex
)
1381 def parseUvTexDataBMesh(args
, tokens
, data
):
1383 for (key
, val
, sub
) in tokens
:
1385 data
[n
].uv
= (float(val
[0]), float(val
[1]))
1387 data
[n
].uv
= (float(val
[2]), float(val
[3]))
1389 data
[n
].uv
= (float(val
[4]), float(val
[5]))
1392 data
[n
].uv
= (float(val
[6]), float(val
[7]))
1396 def parseUvTextureNoBMesh(args
, tokens
, me
):
1398 uvtex
= me
.uv_textures
.new(name
= name
)
1399 loadedData
['MeshTextureFaceLayer'][name
] = uvtex
1400 for (key
, val
, sub
) in tokens
:
1402 parseUvTexDataNoBMesh(val
, sub
, uvtex
.data
)
1404 defaultKey(key
, val
, sub
, uvtex
)
1407 def parseUvTexDataNoBMesh(args
, tokens
, data
):
1409 for (key
, val
, sub
) in tokens
:
1411 data
[n
].uv1
= (float(val
[0]), float(val
[1]))
1412 data
[n
].uv2
= (float(val
[2]), float(val
[3]))
1413 data
[n
].uv3
= (float(val
[4]), float(val
[5]))
1415 data
[n
].uv4
= (float(val
[6]), float(val
[7]))
1420 # parseVertColorLayer(args, tokens, me):
1421 # parseVertColorData(args, tokens, data):
1424 def parseVertColorLayer(args
, tokens
, me
):
1426 print("VertColorLayer", name
)
1427 vcol
= me
.vertex_colors
.new(name
)
1428 loadedData
['MeshColorLayer'][name
] = vcol
1429 for (key
, val
, sub
) in tokens
:
1431 parseVertColorData(val
, sub
, vcol
.data
)
1433 defaultKey(key
, val
, sub
, vcol
)
1436 def parseVertColorData(args
, tokens
, data
):
1438 for (key
, val
, sub
) in tokens
:
1440 data
[n
].color1
= mhxEval(val
[0])
1441 data
[n
].color2
= mhxEval(val
[1])
1442 data
[n
].color3
= mhxEval(val
[2])
1443 data
[n
].color4
= mhxEval(val
[3])
1449 # parseVertexGroup(ob, me, args, tokens):
1452 def parseVertexGroup(ob
, me
, args
, tokens
):
1455 print( "Parsing vertgroup %s" % args
)
1458 res
= mhxEval(args
[1])
1464 if (toggle
& T_Armature
) or (grpName
in ['Eye_L', 'Eye_R', 'Gums', 'Head', 'Jaw', 'Left', 'Middle', 'Right', 'Scalp']):
1466 group
= loadedData
['VertexGroup'][grpName
]
1468 group
= ob
.vertex_groups
.new(grpName
)
1469 loadedData
['VertexGroup'][grpName
] = group
1470 for (key
, val
, sub
) in tokens
:
1472 group
.add( [int(val
[0])], float(val
[1]), 'REPLACE' )
1477 # parseShapeKeys(ob, me, args, tokens):
1478 # parseShapeKey(ob, me, args, tokens):
1479 # addShapeKey(ob, name, vgroup, tokens):
1484 if (toggle
& T_Shapekeys
) and (name
== 'Basis'):
1487 return (toggle
& T_Face
)
1490 def parseShapeKeys(ob
, me
, args
, tokens
):
1491 for (key
, val
, sub
) in tokens
:
1492 if key
== 'ShapeKey':
1493 parseShapeKey(ob
, me
, val
, sub
)
1494 elif key
== 'AnimationData':
1496 parseAnimationData(me
.shape_keys
, val
, sub
)
1497 elif key
== 'Expression':
1498 prop
= "Mhe" + val
[0].capitalize()
1499 parseUnits(prop
, ob
, sub
)
1500 elif key
== 'Viseme':
1501 name
= val
[0].upper()
1502 if name
in ["REST", "ETC"]:
1503 name
= name
.capitalize()
1505 parseUnits(prop
, ob
, sub
)
1506 ob
.active_shape_key_index
= 0
1509 def parseUnits(prop
, ob
, sub
):
1512 unit
= words
[0].replace("-","_")
1514 string
+= "%s:%s;" % (unit
, value
)
1519 def parseShapeKey(ob
, me
, args
, tokens
):
1521 print( "Parsing ob %s shape %s" % (bpy
.context
.object, args
[0] ))
1524 if invalid(args
[2]):
1527 if lr
== 'Sym': # or toggle & T_Symm:
1528 addShapeKey(ob
, name
, None, tokens
)
1530 addShapeKey(ob
, name
+'_L', 'Left', tokens
)
1531 addShapeKey(ob
, name
+'_R', 'Right', tokens
)
1533 MyError("ShapeKey L/R %s" % lr
)
1537 def addShapeKey(ob
, name
, vgroup
, tokens
):
1538 skey
= ob
.shape_key_add(name
=name
, from_mix
=False)
1540 skey
.relative_key
= loadedData
['ShapeKey']['Basis']
1543 skey
.vertex_group
= vgroup
1544 loadedData
['ShapeKey'][name
] = skey
1546 for (key
, val
, sub
) in tokens
:
1549 pt
= skey
.data
[index
].co
1550 pt
[0] += theScale
*float(val
[1])
1551 pt
[1] += theScale
*float(val
[2])
1552 pt
[2] += theScale
*float(val
[3])
1554 defaultKey(key
, val
, sub
, skey
)
1560 # parseArmature (obName, args, tokens)
1563 def parseArmature (args
, tokens
):
1564 global toggle
, theArmature
1566 print( "Parsing armature %s" % args
)
1572 amt
= bpy
.data
.armatures
.new(amtname
)
1573 ob
= createObject('ARMATURE', obname
, amt
, amtname
)
1577 bpy
.ops
.object.mode_set(mode
='OBJECT')
1578 bpy
.ops
.object.mode_set(mode
='EDIT')
1582 for (key
, val
, sub
) in tokens
:
1585 if not invalid(val
[1]):
1586 bone
= amt
.edit_bones
.new(bname
)
1587 parseBone(bone
, amt
, sub
, heads
, tails
)
1588 loadedData
['Bone'][bname
] = bone
1589 elif key
== 'RecalcRoll':
1591 for bone
in amt
.edit_bones
:
1593 blist
= mhxEval(val
[0])
1595 bone
= amt
.edit_bones
[name
]
1597 bpy
.ops
.armature
.calculate_roll(type='Z')
1598 for bone
in amt
.edit_bones
:
1599 rolls
[bone
.name
] = bone
.roll
1600 bpy
.ops
.object.mode_set(mode
='OBJECT')
1601 for bone
in amt
.bones
:
1602 bone
['Roll'] = rolls
[bone
.name
]
1603 bpy
.ops
.object.mode_set(mode
='EDIT')
1605 defaultKey(key
, val
, sub
, amt
, ['MetaRig'])
1606 bpy
.ops
.object.mode_set(mode
='OBJECT')
1611 # parseBone(bone, amt, tokens, heads, tails):
1614 def parseBone(bone
, amt
, tokens
, heads
, tails
):
1615 for (key
, val
, sub
) in tokens
:
1617 bone
.head
= (theScale
*float(val
[0]), theScale
*float(val
[1]), theScale
*float(val
[2]))
1619 bone
.tail
= (theScale
*float(val
[0]), theScale
*float(val
[1]), theScale
*float(val
[2]))
1620 #elif key == 'restrict_select':
1622 elif key
== 'hide' and val
[0] == 'True':
1625 defaultKey(key
, val
, sub
, bone
)
1629 # parsePose (args, tokens):
1632 def parsePose (args
, tokens
):
1634 ob
= loadedData
['Object'][name
]
1635 bpy
.context
.scene
.objects
.active
= ob
1636 bpy
.ops
.object.mode_set(mode
='POSE')
1637 pbones
= ob
.pose
.bones
1639 for (key
, val
, sub
) in tokens
:
1640 if key
== 'Posebone':
1641 parsePoseBone(pbones
, ob
, val
, sub
)
1642 elif key
== 'BoneGroup':
1643 parseBoneGroup(ob
.pose
, nGrps
, val
, sub
)
1645 elif key
== 'SetProp':
1648 value
= mhxEval(val
[2])
1652 defaultKey(key
, val
, sub
, ob
.pose
)
1653 bpy
.ops
.object.mode_set(mode
='OBJECT')
1658 # parsePoseBone(pbones, args, tokens):
1659 # parseArray(data, exts, args):
1662 def parseBoneGroup(pose
, nGrps
, args
, tokens
):
1664 print( "Parsing bonegroup %s" % args
)
1666 bpy
.ops
.pose
.group_add()
1667 bg
= pose
.bone_groups
.active
1668 loadedData
['BoneGroup'][name
] = bg
1669 for (key
, val
, sub
) in tokens
:
1670 defaultKey(key
, val
, sub
, bg
)
1673 def parsePoseBone(pbones
, ob
, args
, tokens
):
1674 if invalid(args
[1]):
1679 amt
.bones
.active
= pb
.bone
1681 for (key
, val
, sub
) in tokens
:
1682 if key
== 'Constraint':
1683 amt
.bones
.active
= pb
.bone
1684 cns
= parseConstraint(pb
.constraints
, pb
, val
, sub
)
1685 elif key
== 'bpyops':
1686 amt
.bones
.active
= pb
.bone
1687 expr
= "bpy.ops.%s" % val
[0]
1688 raise MhxError("MHX bug: Must not exec %s" % expr
)
1689 elif key
== 'ik_dof':
1690 parseArray(pb
, ["ik_dof_x", "ik_dof_y", "ik_dof_z"], val
)
1691 elif key
== 'ik_limit':
1692 parseArray(pb
, ["ik_limit_x", "ik_limit_y", "ik_limit_z"], val
)
1693 elif key
== 'ik_max':
1694 parseArray(pb
, ["ik_max_x", "ik_max_y", "ik_max_z"], val
)
1695 elif key
== 'ik_min':
1696 parseArray(pb
, ["ik_min_x", "ik_min_y", "ik_min_z"], val
)
1697 elif key
== 'ik_stiffness':
1698 parseArray(pb
, ["ik_stiffness_x", "ik_stiffness_y", "ik_stiffness_z"], val
)
1700 #bpy.ops.object.mode_set(mode='OBJECT')
1701 amt
.bones
[name
].hide
= mhxEval(val
[0])
1702 #bpy.ops.object.mode_set(mode='POSE')
1705 defaultKey(key
, val
, sub
, pb
)
1708 def parseArray(data
, exts
, args
):
1711 setattr(data
, ext
, mhxEval(args
[n
]))
1716 # parseConstraint(constraints, pb, args, tokens)
1719 def parseConstraint(constraints
, pb
, args
, tokens
):
1720 if invalid(args
[2]):
1722 if (toggle
&T_Opcns
and pb
):
1724 aob
= bpy
.context
.object
1729 print("pose", apose
)
1730 abone
= aamt
.bones
.active
1731 print("bone", abone
)
1732 print('Num cns before', len(list(constraints
)))
1733 bpy
.ops
.pose
.constraint_add(type=args
[1])
1734 cns
= constraints
.active
1735 print('and after', pb
, cns
, len(list(constraints
)))
1737 cns
= constraints
.new(args
[1])
1740 for (key
,val
,sub
) in tokens
:
1742 parseArray(cns
, ["invert_x", "invert_y", "invert_z"], val
)
1744 parseArray(cns
, ["use_x", "use_y", "use_z"], val
)
1745 elif key
== 'pos_lock':
1746 parseArray(cns
, ["lock_location_x", "lock_location_y", "lock_location_z"], val
)
1747 elif key
== 'rot_lock':
1748 parseArray(cns
, ["lock_rotation_x", "lock_rotation_y", "lock_rotation_z"], val
)
1750 defaultKey(key
, val
, sub
, cns
, ["use_target"])
1753 #print("cns %s done" % cns.name)
1759 # parseCurve (args, tokens):
1760 # parseSpline(cu, args, tokens):
1761 # parseBezier(spline, n, args, tokens):
1764 def parseCurve (args
, tokens
):
1766 print( "Parsing curve %s" % args
)
1767 bpy
.ops
.object.add(type='CURVE')
1768 cu
= setObjectAndData(args
, 'Curve')
1770 for (key
, val
, sub
) in tokens
:
1772 parseSpline(cu
, val
, sub
)
1774 defaultKey(key
, val
, sub
, cu
)
1777 def parseTextCurve (args
, tokens
):
1779 print( "Parsing text curve %s" % args
)
1780 bpy
.ops
.object.text_add()
1781 txt
= setObjectAndData(args
, 'Text')
1783 for (key
, val
, sub
) in tokens
:
1785 parseSpline(txt
, val
, sub
)
1786 elif key
== 'BodyFormat':
1787 parseCollection(txt
.body_format
, sub
, [])
1788 elif key
== 'EditFormat':
1789 parseDefault(txt
.edit_format
, sub
, {}, [])
1791 parseDefault(txt
.font
, sub
, {}, [])
1792 elif key
== 'TextBox':
1793 parseCollection(txt
.body_format
, sub
, [])
1795 defaultKey(key
, val
, sub
, txt
)
1799 def parseSpline(cu
, args
, tokens
):
1801 spline
= cu
.splines
.new(typ
)
1802 nPointsU
= int(args
[1])
1803 nPointsV
= int(args
[2])
1804 #spline.point_count_u = nPointsU
1805 #spline.point_count_v = nPointsV
1806 if typ
== 'BEZIER' or typ
== 'BSPLINE':
1807 spline
.bezier_points
.add(nPointsU
)
1809 spline
.points
.add(nPointsU
)
1812 for (key
, val
, sub
) in tokens
:
1814 parseBezier(spline
.bezier_points
[n
], val
, sub
)
1817 parsePoint(spline
.points
[n
], val
, sub
)
1820 defaultKey(key
, val
, sub
, spline
)
1823 def parseBezier(bez
, args
, tokens
):
1824 bez
.co
= mhxEval(args
[0])
1825 bez
.co
= theScale
*bez
.co
1826 bez
.handle1
= mhxEval(args
[1])
1827 bez
.handle1_type
= args
[2]
1828 bez
.handle2
= mhxEval(args
[3])
1829 bez
.handle2_type
= args
[4]
1832 def parsePoint(pt
, args
, tokens
):
1833 pt
.co
= mhxEval(args
[0])
1834 pt
.co
= theScale
*pt
.co
1839 # parseLattice (args, tokens):
1842 def parseLattice (args
, tokens
):
1844 print( "Parsing lattice %s" % args
)
1845 bpy
.ops
.object.add(type='LATTICE')
1846 lat
= setObjectAndData(args
, 'Lattice')
1847 for (key
, val
, sub
) in tokens
:
1849 parseLatticePoints(val
, sub
, lat
.points
)
1851 defaultKey(key
, val
, sub
, lat
)
1854 def parseLatticePoints(args
, tokens
, points
):
1856 for (key
, val
, sub
) in tokens
:
1858 v
= points
[n
].co_deform
1859 v
.x
= theScale
*float(val
[0])
1860 v
.y
= theScale
*float(val
[1])
1861 v
.z
= theScale
*float(val
[2])
1866 # parseLamp (args, tokens):
1867 # parseFalloffCurve(focu, args, tokens):
1870 def parseLamp (args
, tokens
):
1872 print( "Parsing lamp %s" % args
)
1873 bpy
.ops
.object.add(type='LAMP')
1874 lamp
= setObjectAndData(args
, 'Lamp')
1875 for (key
, val
, sub
) in tokens
:
1876 if key
== 'FalloffCurve':
1877 parseFalloffCurve(lamp
.falloff_curve
, val
, sub
)
1879 defaultKey(key
, val
, sub
, lamp
)
1882 def parseFalloffCurve(focu
, args
, tokens
):
1886 # parseGroup (args, tokens):
1887 # parseGroupObjects(args, tokens, grp):
1890 def parseGroup (args
, tokens
):
1892 print( "Parsing group %s" % args
)
1895 grp
= bpy
.data
.groups
.new(grpName
)
1896 loadedData
['Group'][grpName
] = grp
1897 for (key
, val
, sub
) in tokens
:
1898 if key
== 'Objects':
1899 parseGroupObjects(val
, sub
, grp
)
1901 defaultKey(key
, val
, sub
, grp
)
1904 def parseGroupObjects(args
, tokens
, grp
):
1906 for (key
, val
, sub
) in tokens
:
1909 ob
= loadedData
['Object'][val
[0]]
1910 grp
.objects
.link(ob
)
1914 print(ob
, ob
.type, rig
, ob
.parent
)
1915 if ob
.type == 'ARMATURE':
1917 elif ob
.type == 'EMPTY' and rig
and not ob
.parent
:
1923 # parseWorld (args, tokens):
1926 def parseWorld (args
, tokens
):
1928 print( "Parsing world %s" % args
)
1929 world
= bpy
.context
.scene
.world
1930 for (key
, val
, sub
) in tokens
:
1931 if key
== 'Lighting':
1932 parseDefault(world
.lighting
, sub
, {}, [])
1934 parseDefault(world
.mist
, sub
, {}, [])
1935 elif key
== 'Stars':
1936 parseDefault(world
.stars
, sub
, {}, [])
1938 defaultKey(key
, val
, sub
, world
)
1942 # parseScene (args, tokens):
1943 # parseRenderSettings(render, args, tokens):
1944 # parseToolSettings(tool, args, tokens):
1947 def parseScene (args
, tokens
):
1949 print( "Parsing scene %s" % args
)
1950 scn
= bpy
.context
.scene
1951 for (key
, val
, sub
) in tokens
:
1952 if key
== 'NodeTree':
1953 scn
.use_nodes
= True
1954 parseNodeTree(scn
, val
, sub
)
1955 elif key
== 'GameData':
1956 parseDefault(scn
.game_data
, sub
, {}, [])
1957 elif key
== 'KeyingSet':
1959 #parseDefault(scn.keying_sets, sub, {}, [])
1960 elif key
== 'ObjectBase':
1962 #parseDefault(scn.bases, sub, {}, [])
1963 elif key
== 'RenderSettings':
1964 parseRenderSettings(scn
.render
, sub
, [])
1965 elif key
== 'ToolSettings':
1966 subkeys
= {'ImagePaint' : "image_paint",
1967 'Sculpt' : "sculpt",
1968 'VertexPaint' : "vertex_paint",
1969 'WeightPaint' : "weight_paint" }
1970 parseDefault(scn
.tool_settings
, sub
, subkeys
, [])
1971 elif key
== 'UnitSettings':
1972 parseDefault(scn
.unit_settings
, sub
, {}, [])
1974 defaultKey(key
, val
, sub
, scn
)
1977 def parseRenderSettings(render
, args
, tokens
):
1979 print( "Parsing RenderSettings %s" % args
)
1980 for (key
, val
, sub
) in tokens
:
1983 #parseDefault(scn.layers, sub, [])
1985 defaultKey(key
, val
, sub
, render
)
1989 # parseDefineProperty(args, tokens):
1992 def parseDefineProperty(args
, tokens
):
1993 prop
= "%sProperty" % (args
[1])
1995 for option
in args
[2:]:
1996 prop
+= "%s %s" % (c
, option
)
1999 setattr(bpy
.types
.Object
, args
[0], prop
)
2006 def correctRig(args
):
2008 print("CorrectRig %s" % human
)
2010 ob
= loadedData
['Object'][human
]
2013 ob
.MhxShapekeyDrivers
= (toggle
&T_Shapekeys
!= 0 and toggle
&T_ShapeDrivers
!= 0)
2014 bpy
.context
.scene
.objects
.active
= ob
2015 bpy
.ops
.object.mode_set(mode
='POSE')
2018 for pb
in ob
.pose
.bones
:
2019 pb
.bone
.select
= False
2020 for cns
in pb
.constraints
:
2021 if cns
.type == 'CHILD_OF':
2022 cnslist
.append((pb
, cns
, cns
.influence
))
2025 for (pb
, cns
, inf
) in cnslist
:
2026 amt
.bones
.active
= pb
.bone
2028 #print("Childof %s %s %s %.2f" % (amt.name, pb.name, cns.name, inf))
2029 bpy
.ops
.constraint
.childof_clear_inverse(constraint
=cns
.name
, owner
='BONE')
2030 bpy
.ops
.constraint
.childof_set_inverse(constraint
=cns
.name
, owner
='BONE')
2033 for (pb
, cns
, inf
) in cnslist
:
2042 def postProcess(args
):
2044 print("Postprocess %s" % human
)
2046 ob
= loadedData
['Object'][human
]
2049 if toggle
& T_Diamond
== 0 and ob
:
2054 # deleteDiamonds(ob)
2055 # Delete joint diamonds in main mesh
2056 # Invisio = material # 1
2059 def deleteDiamonds(ob
):
2060 bpy
.context
.scene
.objects
.active
= ob
2061 if not bpy
.context
.object:
2063 print("Delete helper geometry in %s" % bpy
.context
.object)
2064 bpy
.ops
.object.mode_set(mode
='EDIT')
2065 bpy
.ops
.mesh
.select_all(action
='DESELECT')
2066 bpy
.ops
.object.mode_set(mode
='OBJECT')
2069 for mn
,mat
in enumerate(me
.materials
):
2070 if "Invis" in mat
.name
:
2074 print("WARNING: Nu Invisio material found. Cannot delete helper geometry")
2076 for f
in me
.polygons
:
2077 if f
.material_index
>= invisioNum
:
2078 for vn
in f
.vertices
:
2079 me
.vertices
[vn
].select
= True
2082 if f
.material_index
>= invisioNum
:
2083 for vn
in f
.vertices
:
2084 me
.vertices
[vn
].select
= True
2085 if BMeshAware
and toggle
&T_CrashSafe
:
2086 theMessage
= "\n *** WARNING ***\nHelper deletion turned off due to Blender crash.\nHelpers can be deleted by deleting all selected vertices in Edit mode\n **********\n"
2089 bpy
.ops
.object.mode_set(mode
='EDIT')
2091 bpy
.ops
.mesh
.delete(type='VERT')
2092 print("Verts deleted")
2093 bpy
.ops
.object.mode_set(mode
='OBJECT')
2094 print("Back to object mode")
2098 # defaultKey(ext, args, tokens, data, exclude):
2103 def propNames(string
):
2105 #string = string.encode('utf-8', 'strict')
2107 # Alpha 7 compatibility
2108 if string
[0:2] == "&_":
2109 string
= "Mhf"+string
[2:]
2111 elif string
[0] == "&":
2112 string
= "Mha"+string
[1:]
2114 elif string
[0] == "*":
2115 string
= "Mhs"+string
[1:]
2117 elif len(string
) > 4 and string
[0:4] == "Hide":
2118 string
= "Mhh"+string
[4:]
2121 if string
[0] == "_":
2123 elif (len(string
) > 3 and
2124 string
[0:3] in ["Mha", "Mhf", "Mhs", "Mhh", "Mhv", "Mhc"]):
2125 name
= string
.replace("-","_")
2126 return name
, '["%s"]' % name
2128 return string
, '["%s"]' % string
2131 def defProp(args
, var
):
2133 name
= propNames(args
[1])[0]
2135 rest
= 'description="%s"' % args
[3].replace("_", " ")
2137 rest
+= ", " + args
[4]
2143 def defNewProp(name
, proptype
, rest
):
2144 prop
= "%sProperty(%s)" % (proptype
, rest
)
2145 setattr(bpy
.types
.Object
, name
, eval(prop
)) # safe: only called from this file
2148 def setProperty(args
, var
):
2151 name
= propNames(args
[0])[0]
2152 value
= mhxEval(args
[1])
2156 tip
= 'description="%s"' % args
[2].replace("_", " ")
2157 theProperty
= (name
, tip
, value
)
2160 def setPropKeys(args
):
2162 if theProperty
is None:
2164 (name
, tip
, value
) = theProperty
2165 if len(args
) >= 2 and not isinstance(value
, bool):
2166 if "BOOLEAN" in args
[1]:
2169 tip
= tip
+ "," + args
[1].replace(":", "=").replace('"', " ")
2170 #expr = "bpy.types.Object.%s = %sProperty(%s)" % (name, proptype, tip)
2171 if isinstance(value
, bool):
2172 prop
= BoolProperty(tip
)
2173 elif isinstance(value
, int):
2174 prop
= IntProperty(tip
)
2175 elif isinstance(value
, float):
2176 prop
= FloatProperty(tip
)
2177 elif isinstance(value
, string
):
2178 prop
= StringProperty(tip
)
2179 setattr(bpy
.types
.Object
, name
, prop
)
2183 def defaultKey(ext
, args
, tokens
, var
, exclude
=[]):
2184 if ext
== 'Property':
2185 return setProperty(args
, var
)
2186 elif ext
== 'PropKeys':
2187 return setPropKeys(args
)
2188 elif ext
== 'DefProp':
2189 return defProp(args
, var
)
2192 expr
= "bpy.ops.%s" % args
[0]
2194 raise MhxError("MHX bug: calling %s" % expr
)
2198 nvar
= getattr(var
, ext
)
2201 MyError("Key length 0: %s" % ext
)
2204 if rnaType
== 'Refer':
2207 setattr(var
, ext
, loadedData
[typ
][name
])
2210 elif rnaType
== 'Struct' or rnaType
== 'Define':
2211 raise MhxError("Struct/Define!")
2215 data
= getattr(var
, ext
)
2218 # print("Old structrna", nvar, data)
2227 rna
= mhxEval(var
, locals())
2228 data
= mhxEval(creator
)
2231 # print("New struct", nvar, typ, data)
2233 if rnaType
== 'Define':
2234 loadedData
[typ
][name
] = data
2237 for (key
, val
, sub
) in tokens
:
2238 defaultKey(key
, val
, sub
, data
)
2241 elif rnaType
== 'PropertyRNA':
2242 raise MhxError("PropertyRNA!")
2243 #print("PropertyRNA ", ext, var)
2244 for (key
, val
, sub
) in tokens
:
2245 defaultKey(ext
, val
, sub
, nvar
, [])
2248 elif rnaType
== 'Array':
2249 for n
in range(1, len(args
)):
2250 nvar
[n
-1] = mhxEval(args
[n
], locals())
2252 nvar
[0] = mhxEval(args
[1], locals())
2255 elif rnaType
== 'List':
2256 raise MhxError("List!")
2258 for (key
, val
, sub
) in tokens
:
2259 elt
= mhxEval(val
[1], locals())
2261 setattr(var
, ext
, data
)
2264 elif rnaType
== 'Matrix':
2265 raise MhxError("Matrix!")
2268 for (key
, val
, sub
) in tokens
:
2271 nvar
[i
][j
] = float(val
[j
])
2277 data
= loadedData
[rnaType
][args
[1]]
2278 raise MhxError("From loaded %s %s!" % (rnaType
, args
[1]))
2281 data
= mhxEval(rnaType
, locals())
2282 setattr(var
, ext
, data
)
2289 def pushOnTodoList(var
, expr
):
2290 print("Unrecognized expression", expr
)
2292 print(dir(mhxEval(var
)))
2294 "Unrecognized expression %s.\n" % expr
+
2295 "This can mean that Blender's python API has changed\n" +
2296 "since the MHX file was exported. Try to export again\n" +
2297 "from an up-to-date MakeHuman nightly build.\n" +
2298 "Alternatively, your Blender version may be obsolete.\n" +
2299 "Download an up-to-date version from www.graphicall.org")
2303 # parseBoolArray(mask):
2306 def parseBoolArray(mask
):
2315 # parseMatrix(args, tokens)
2318 def parseMatrix(args
, tokens
):
2319 matrix
= mathutils
.Matrix()
2321 for (key
, val
, sub
) in tokens
:
2323 matrix
[i
][0] = float(val
[0])
2324 matrix
[i
][1] = float(val
[1])
2325 matrix
[i
][2] = float(val
[2])
2326 matrix
[i
][3] = float(val
[3])
2331 # parseDefault(data, tokens, subkeys, exclude):
2334 def parseDefault(data
, tokens
, subkeys
, exclude
):
2335 for (key
, val
, sub
) in tokens
:
2336 if key
in subkeys
.keys():
2337 for (key2
, val2
, sub2
) in sub
:
2338 ndata
= getattr(data
, subkeys
[key
])
2339 defaultKey(key2
, val2
, sub2
, ndata
)
2341 defaultKey(key
, val
, sub
, data
, exclude
)
2343 def parseCollection(data
, tokens
, exclude
):
2352 # extractBpyType(data):
2355 def extractBpyType(data):
2356 typeSplit = str(type(data)).split("'")
2357 if typeSplit[0] != '<class ':
2359 classSplit = typeSplit[1].split(".")
2360 if classSplit[0] == 'bpy' and classSplit[1] == 'types':
2361 return classSplit[2]
2362 elif classSplit[0] == 'bpy_types':
2363 return classSplit[1]
2372 if string == 'True':
2374 elif string == 'False':
2377 MyError("Bool %s?" % string)
2380 # invalid(condition):
2383 def invalid(condition
):
2384 global rigLeg
, rigArm
, toggle
2386 res
= mhxEval(condition
)
2387 #print("%s = %s" % (condition, res))
2390 #print("%s invalid!" % condition)
2396 # clearScene(context):
2401 scn
= bpy
.context
.scene
2402 for n
in range(len(scn
.layers
)):
2403 scn
.layers
[n
] = True
2405 print("clearScene %s %s" % (toggle
& T_Replace
, scn
))
2406 if not toggle
& T_Replace
:
2409 for ob
in scn
.objects
:
2410 if ob
.type in ['MESH', 'ARMATURE', 'EMPTY', 'CURVE', 'LATTICE']:
2411 scn
.objects
.active
= ob
2412 ob
.name
= "#" + ob
.name
2414 bpy
.ops
.object.mode_set(mode
='OBJECT')
2417 scn
.objects
.unlink(ob
)
2420 for grp
in bpy
.data
.groups
:
2421 grp
.name
= "#" + grp
.name
2427 # args = sceneLayers sceneHideLayers boneLayers boneHideLayers or nothing
2430 def hideLayers(args
):
2432 sceneLayers
= int(args
[2], 16)
2433 sceneHideLayers
= int(args
[3], 16)
2434 boneLayers
= int(args
[4], 16)
2435 # boneHideLayers = int(args[5], 16)
2438 sceneLayers
= 0x00ff
2443 scn
= bpy
.context
.scene
2447 scn
.layers
[n
] = True if sceneLayers
& mask
else False
2448 if sceneHideLayers
& mask
:
2449 hidelayers
.append(n
)
2452 for ob
in scn
.objects
:
2453 for n
in hidelayers
:
2456 ob
.hide_render
= True
2461 ob
= loadedData
['Object'][human
]
2468 ob
.data
.layers
[n
] = True if boneLayers
& mask
else False
2469 if boneHideLayers
& mask
:
2470 hidelayers
.append(n
)
2473 for b
in ob
.data
.bones
:
2474 for n
in hidelayers
:
2486 ConfigFile
= '~/mhx_import.cfg'
2490 global toggle
, toggleSettings
, theScale
2491 path
= os
.path
.realpath(os
.path
.expanduser(ConfigFile
))
2493 fp
= open(path
, 'rU')
2494 print('Storing defaults')
2496 print('Cannot open "%s" for reading' % path
)
2500 words
= line
.split()
2503 toggle
= int(words
[0],16)
2504 theScale
= float(words
[1])
2506 print('Configuration file "%s" is corrupt' % path
)
2508 toggleSettings
= toggle
2511 def writeDefaults():
2512 global toggleSettings
, theScale
2513 path
= os
.path
.realpath(os
.path
.expanduser(ConfigFile
))
2515 fp
= open(path
, 'w')
2516 print('Storing defaults')
2518 print('Cannot open "%s" for writing' % path
)
2520 fp
.write("%x %f Graphicall" % (toggleSettings
, theScale
))
2524 ###################################################################################
2526 # Postprocessing of rigify rig
2528 # rigifyMhx(context):
2530 ###################################################################################
2533 def __init__(self
, eb
):
2535 self
.realname
= None
2536 self
.realname1
= None
2537 self
.realname2
= None
2541 self
.head
= eb
.head
.copy()
2542 self
.tail
= eb
.tail
.copy()
2544 self
.deform
= eb
.use_deform
2547 self
.connect
= False
2548 self
.original
= False
2549 self
.extra
= (eb
.name
in ["spine-1"])
2552 return ("<RigifyBone %s %s %s>" % (self
.name
, self
.realname
, self
.realname1
))
2555 def rigifyMhx(context
):
2557 from collections
import OrderedDict
2559 print("Modifying MHX rig to Rigify")
2562 if ob
.type == 'ARMATURE':
2564 elif ob
.type == 'MESH':
2568 if not(rig
and rig
.type == 'ARMATURE'):
2569 raise NameError("Rigify: %s is neither an armature nor has armature parent" % ob
)
2570 rig
.MhxRigify
= True
2571 scn
.objects
.active
= rig
2574 for grp
in bpy
.data
.groups
:
2575 if rig
.name
in grp
.objects
:
2578 print("Group: %s" % group
)
2580 # Setup info about MHX bones
2581 bones
= OrderedDict()
2582 bpy
.ops
.object.mode_set(mode
='EDIT')
2583 for eb
in rig
.data
.edit_bones
:
2584 bone
= bones
[eb
.name
] = RigifyBone(eb
)
2586 bone
.parent
= eb
.parent
.name
2587 bones
[bone
.parent
].child
= eb
.name
2588 bpy
.ops
.object.mode_set(mode
='OBJECT')
2592 bpy
.ops
.object.armature_human_metarig_add()
2593 except AttributeError:
2594 raise MyError("The Rigify add-on is not enabled. It is found under rigging.")
2595 bpy
.ops
.object.location_clear()
2596 bpy
.ops
.object.rotation_clear()
2597 bpy
.ops
.object.scale_clear()
2598 bpy
.ops
.transform
.resize(value
=(100, 100, 100))
2599 bpy
.ops
.object.transform_apply(location
=False, rotation
=False, scale
=True)
2601 # Fit metarig to default MHX rig
2602 meta
= context
.object
2603 bpy
.ops
.object.mode_set(mode
='EDIT')
2605 for bone
in bones
.values():
2607 eb
= meta
.data
.edit_bones
[bone
.name
]
2614 bone
.original
= True
2616 extra
.append(bone
.name
)
2617 bone
.original
= True
2618 eb
= meta
.data
.edit_bones
.new(bone
.name
)
2619 eb
.use_connect
= False
2620 eb
.head
= bones
[bone
.parent
].tail
2621 eb
.tail
= bones
[bone
.child
].head
2623 parent
= meta
.data
.edit_bones
[bone
.parent
]
2624 child
= meta
.data
.edit_bones
[bone
.child
]
2626 child
.head
= bones
[bone
.child
].head
2627 parent
.tail
= bones
[bone
.parent
].tail
2629 eb
.use_connect
= True
2631 # Add rigify properties to extra bones
2632 bpy
.ops
.object.mode_set(mode
='OBJECT')
2634 pb
= meta
.pose
.bones
[bname
]
2635 pb
["rigify_type"] = ""
2637 # Generate rigify rig
2638 bpy
.ops
.pose
.rigify_generate()
2639 gen
= context
.object
2640 print("Generated", gen
)
2641 scn
.objects
.unlink(meta
)
2644 for bone
in bones
.values():
2646 setBoneName(bone
, gen
)
2648 # Add extra bone to generated rig
2649 bpy
.ops
.object.mode_set(mode
='EDIT')
2652 for bone
in bones
.values():
2653 if not bone
.original
:
2655 bone
.realname
= "DEF-" + bone
.name
2657 bone
.realname
= "MCH-" + bone
.name
2658 eb
= gen
.data
.edit_bones
.new(bone
.realname
)
2662 eb
.use_deform
= bone
.deform
2664 parent
= bones
[bone
.parent
]
2666 eb
.parent
= gen
.data
.edit_bones
[parent
.realname
]
2667 elif parent
.realname1
:
2668 eb
.parent
= gen
.data
.edit_bones
[parent
.realname1
]
2671 eb
.use_connect
= (eb
.parent
!= None and eb
.parent
.tail
== eb
.head
)
2674 bpy
.ops
.object.mode_set(mode
='OBJECT')
2675 for bone
in bones
.values():
2676 if not bone
.original
:
2677 pb
= gen
.pose
.bones
[bone
.realname
]
2678 db
= rig
.pose
.bones
[bone
.name
]
2679 pb
.rotation_mode
= db
.rotation_mode
2680 for cns1
in db
.constraints
:
2681 cns2
= pb
.constraints
.new(cns1
.type)
2682 fixConstraint(cns1
, cns2
, gen
, bones
)
2684 # Add MHX properties
2685 for key
in rig
.keys():
2688 # Copy MHX bone drivers
2689 if rig
.animation_data
:
2690 for fcu1
in rig
.animation_data
.drivers
:
2691 rna
,channel
= fcu1
.data_path
.rsplit(".", 1)
2692 pb
= mhxEval("gen.%s" % rna
)
2693 fcu2
= pb
.driver_add(channel
, fcu1
.array_index
)
2694 copyDriver(fcu1
, fcu2
, gen
)
2696 # Copy MHX morph drivers and change armature modifier
2697 for ob
in rig
.children
:
2698 if ob
.type == 'MESH':
2701 if ob
.data
.animation_data
:
2702 for fcu
in ob
.data
.animation_data
.drivers
:
2703 print(ob
, fcu
.data_path
)
2704 changeDriverTarget(fcu
, gen
)
2706 if ob
.data
.shape_keys
and ob
.data
.shape_keys
.animation_data
:
2707 for fcu
in ob
.data
.shape_keys
.animation_data
.drivers
:
2708 print(skey
, fcu
.data_path
)
2709 changeDriverTarget(fcu
, gen
)
2711 for mod
in ob
.modifiers
:
2712 if mod
.type == 'ARMATURE' and mod
.object == rig
:
2716 group
.objects
.link(gen
)
2718 # Parent widgets under empty
2719 empty
= bpy
.data
.objects
.new("Widgets", None)
2720 scn
.objects
.link(empty
)
2721 empty
.layers
= 20*[False]
2722 empty
.layers
[19] = True
2724 for ob
in scn
.objects
:
2725 if ob
.type == 'MESH' and ob
.name
[0:4] == "WGT-" and not ob
.parent
:
2727 grp
.objects
.link(ob
)
2730 gen
.show_x_ray
= True
2731 gen
.data
.draw_type
= 'STICK'
2732 gen
.MhxRigify
= False
2734 scn
.objects
.unlink(rig
)
2737 bpy
.ops
.object.mode_set(mode
='POSE')
2739 print("MHX rig %s successfully rigified" % name
)
2743 def setBoneName(bone
, gen
):
2744 fkname
= bone
.name
.replace(".", ".fk.")
2746 gen
.data
.bones
[fkname
]
2747 bone
.fkname
= fkname
2748 bone
.ikname
= fkname
.replace(".fk.", ".ik")
2752 defname
= "DEF-" + bone
.name
2754 gen
.data
.bones
[defname
]
2755 bone
.realname
= defname
2760 defname1
= "DEF-" + bone
.name
+ ".01"
2762 gen
.data
.bones
[defname1
]
2763 bone
.realname1
= defname1
2764 bone
.realname2
= defname1
.replace(".01.", ".02.")
2769 defname1
= "DEF-" + bone
.name
.replace(".", ".01.")
2771 gen
.data
.bones
[defname1
]
2772 bone
.realname1
= defname1
2773 bone
.realname2
= defname1
.replace(".01.", ".02")
2779 gen
.data
.edit_bones
[bone
.name
]
2780 bone
.realname
= bone
.name
2785 def fixConstraint(cns1
, cns2
, gen
, bones
):
2786 for key
in dir(cns1
):
2787 if ((key
[0] != "_") and
2788 (key
not in ["bl_rna", "type", "rna_type", "is_valid", "error_location", "error_rotation"])):
2789 expr
= ("cns2.%s = cns1.%s" % (key
, key
))
2790 setattr(cns2
, key
, getattr(cns1
, key
))
2794 if cns1
.type == 'STRETCH_TO':
2795 bone
= bones
[cns1
.subtarget
]
2797 cns2
.subtarget
= bone
.realname
2798 cns2
.head_tail
= cns1
.head_tail
2799 elif not bone
.realname1
:
2802 elif cns1
.head_tail
< 0.5:
2803 cns2
.subtarget
= bone
.realname1
2804 cns2
.head_tail
= 2*cns1
.head_tail
2806 cns2
.subtarget
= bone
.realname2
2807 cns2
.head_tail
= 2*cns1
.head_tail
-1
2809 elif cns1
.type == 'TRANSFORM':
2810 bone
= bones
[cns1
.subtarget
]
2812 cns2
.subtarget
= bone
.fkname
2814 cns2
.subtarget
= bone
.ikname
2816 cns2
.subtarget
= bone
.realname
2819 def copyDriver(fcu1
, fcu2
, id):
2823 for var1
in drv1
.variables
:
2824 var2
= drv2
.variables
.new()
2825 var2
.name
= var1
.name
2826 var2
.type = var1
.type
2827 targ1
= var1
.targets
[0]
2828 targ2
= var2
.targets
[0]
2830 targ2
.data_path
= targ1
.data_path
2832 drv2
.type = drv1
.type
2833 drv2
.expression
= drv1
.expression
2834 drv2
.show_debug_info
= drv1
.show_debug_info
2837 def changeDriverTarget(fcu
, id):
2838 for var
in fcu
.driver
.variables
:
2839 targ
= var
.targets
[0]
2844 # class OBJECT_OT_RigifyMhxButton(bpy.types.Operator):
2847 class OBJECT_OT_RigifyMhxButton(bpy
.types
.Operator
):
2848 bl_idname
= "mhxrig.rigify_mhx"
2849 bl_label
= "Rigify MHX rig"
2850 bl_options
= {'UNDO'}
2852 def execute(self
, context
):
2857 # class RigifyMhxPanel(bpy.types.Panel):
2860 class RigifyMhxPanel(bpy
.types
.Panel
):
2861 bl_label
= "Rigify MHX"
2862 bl_space_type
= "VIEW_3D"
2863 bl_region_type
= "UI"
2866 def poll(cls
, context
):
2867 return (context
.object and context
.object.MhxRigify
)
2869 def draw(self
, context
):
2870 self
.layout
.operator("mhxrig.rigify_mhx")
2873 ###################################################################################
2877 ###################################################################################
2880 from bpy
.props
import StringProperty
, FloatProperty
, EnumProperty
, BoolProperty
2882 class ErrorOperator(bpy
.types
.Operator
):
2883 bl_idname
= "mhx.error"
2884 bl_label
= "Error when loading MHX file"
2886 def execute(self
, context
):
2887 return {'RUNNING_MODAL'}
2889 def invoke(self
, context
, event
):
2890 global theErrorLines
2892 for line
in theErrorLines
:
2893 if len(line
) > maxlen
:
2896 height
= 20+5*len(theErrorLines
)
2897 #self.report({'INFO'}, theMessage)
2898 wm
= context
.window_manager
2899 return wm
.invoke_props_dialog(self
, width
=width
, height
=height
)
2901 def draw(self
, context
):
2902 global theErrorLines
2903 for line
in theErrorLines
:
2904 self
.layout
.label(line
)
2906 def MyError(message
):
2907 global theMessage
, theErrorLines
, theErrorStatus
2908 theMessage
= message
2909 theErrorLines
= message
.split('\n')
2910 theErrorStatus
= True
2911 bpy
.ops
.mhx
.error('INVOKE_DEFAULT')
2912 raise MhxError(theMessage
)
2914 class MhxError(Exception):
2915 def __init__(self
, value
):
2918 return repr(self
.value
)
2920 class SuccessOperator(bpy
.types
.Operator
):
2921 bl_idname
= "mhx.success"
2922 bl_label
= "MHX file successfully loaded:"
2923 message
= StringProperty()
2925 def execute(self
, context
):
2926 return {'RUNNING_MODAL'}
2928 def invoke(self
, context
, event
):
2929 wm
= context
.window_manager
2930 return wm
.invoke_props_dialog(self
)
2932 def draw(self
, context
):
2933 self
.layout
.label(self
.message
+ theMessage
)
2935 ###################################################################################
2939 ###################################################################################
2941 from bpy_extras
.io_utils
import ImportHelper
, ExportHelper
2944 ("enforce", "Enforce version", "Only accept MHX files of correct version", T_EnforceVersion
),
2945 #("crash_safe", "Crash-safe", "Disable features that have caused Blender crashes", T_CrashSafe),
2946 ("mesh", "Mesh", "Use main mesh", T_Mesh
),
2947 ("proxy", "Proxies", "Use proxies", T_Proxy
),
2948 #("armature", "Armature", "Use armature", T_Armature),
2949 #("replace", "Replace scene", "Replace scene", T_Replace),
2950 ("cage", "Cage", "Load mesh deform cage", T_Cage
),
2951 ("clothes", "Clothes", "Include clothes", T_Clothes
),
2952 ("shapekeys", "Shapekeys", "Include shapekeys", T_Shapekeys
),
2953 ("shapedrivers", "Shapekey drivers", "Include shapekey drivers", T_ShapeDrivers
),
2954 #("symm", "Symmetric shapes", "Keep shapekeys symmetric", T_Symm),
2955 ("diamond", "Helper geometry", "Keep helper geometry", T_Diamond
),
2956 ("rigify", "Rigify", "Create rigify control rig", T_Rigify
),
2959 class ImportMhx(bpy
.types
.Operator
, ImportHelper
):
2960 """Import from MHX file format (.mhx)"""
2961 bl_idname
= "import_scene.makehuman_mhx"
2962 bl_description
= 'Import from MHX file format (.mhx)'
2963 bl_label
= "Import MHX"
2964 bl_space_type
= "PROPERTIES"
2965 bl_region_type
= "WINDOW"
2966 bl_options
= {'UNDO'}
2968 filename_ext
= ".mhx"
2969 filter_glob
= StringProperty(default
="*.mhx", options
={'HIDDEN'})
2970 filepath
= StringProperty(subtype
='FILE_PATH')
2972 scale
= FloatProperty(name
="Scale", description
="Default meter, decimeter = 1.0", default
= theScale
)
2973 advanced
= BoolProperty(name
="Advanced settings", description
="Use advanced import settings", default
=False)
2974 for (prop
, name
, desc
, flag
) in MhxBoolProps
:
2975 expr
= '%s = BoolProperty(name="%s", description="%s", default=(toggleSettings&%s != 0))' % (prop
, name
, desc
, flag
)
2976 exec(expr
) # Trusted source: this file.
2979 def draw(self
, context
):
2980 layout
= self
.layout
2981 layout
.prop(self
, "scale")
2982 layout
.prop(self
, "advanced")
2984 for (prop
, name
, desc
, flag
) in MhxBoolProps
:
2985 layout
.prop(self
, prop
)
2988 def execute(self
, context
):
2989 global toggle
, toggleSettings
, theScale
, MhxBoolProps
2990 if not self
.advanced
:
2991 toggle
= DefaultToggle
2994 for (prop
, name
, desc
, flag
) in MhxBoolProps
:
2995 expr
= '(%s if self.%s else 0)' % (flag
, prop
)
2996 toggle |
= eval(expr
) # trusted source: this file
2997 toggleSettings
= toggle
2998 print("execute flags %x" % toggle
)
2999 theScale
= self
.scale
3001 #filepathname = self.filepath.encode('utf-8', 'strict')
3003 if not context
.user_preferences
.system
.use_scripts_auto_execute
:
3004 MyError("Auto Run Python Scripts must be turned on.\nIt is found under\n File > User Preferences > File")
3005 readMhxFile(self
.filepath
)
3006 #bpy.ops.mhx.success('INVOKE_DEFAULT', message = self.filepath)
3008 print("Error when loading MHX file %s:\n" % self
.filepath
+ theMessage
)
3012 self
.advanced
= False
3016 def invoke(self
, context
, event
):
3017 global toggle
, theScale
, MhxBoolProps
3019 self
.scale
= theScale
3020 for (prop
, name
, desc
, flag
) in MhxBoolProps
:
3021 setattr(self
, prop
, mhxEval('(toggle&%s != 0)' % flag
))
3022 context
.window_manager
.fileselect_add(self
)
3023 return {'RUNNING_MODAL'}
3026 ###################################################################################
3030 ###################################################################################
3033 (( 0, 'Root', 'MhxRoot'),
3034 ( 1, 'Spine', 'MhxFKSpine')),
3035 ((10, 'Head', 'MhxHead'),
3036 ( 8, 'Face', 'MhxFace')),
3037 (( 9, 'Tweak', 'MhxTweak'),
3038 (16, 'Clothes', 'MhxClothes')),
3039 #((17, 'IK Spine', 'MhxIKSpine'),
3040 #((13, 'Inv FK Spine', 'MhxInvFKSpine')),
3042 (( 2, 'IK Arm', 'MhxIKArm'),
3043 (18, 'IK Arm', 'MhxIKArm')),
3044 (( 3, 'FK Arm', 'MhxFKArm'),
3045 (19, 'FK Arm', 'MhxFKArm')),
3046 (( 4, 'IK Leg', 'MhxIKLeg'),
3047 (20, 'IK Leg', 'MhxIKLeg')),
3048 (( 5, 'FK Leg', 'MhxFKLeg'),
3049 (21, 'FK Leg', 'MhxFKLeg')),
3050 ((12, 'Extra', 'MhxExtra'),
3051 (28, 'Extra', 'MhxExtra')),
3052 (( 6, 'Fingers', 'MhxFingers'),
3053 (22, 'Fingers', 'MhxFingers')),
3054 (( 7, 'Links', 'MhxLinks'),
3055 (23, 'Links', 'MhxLinks')),
3056 ((11, 'Palm', 'MhxPalm'),
3057 (27, 'Palm', 'MhxPalm')),
3061 (( 1, 'Spine', 'MhxFKSpine'),
3062 ( 10, 'Head', 'MhxHead')),
3063 (( 9, 'Tweak', 'MhxTweak'),
3064 ( 8, 'Face', 'MhxFace')),
3066 (( 3, 'Arm', 'MhxFKArm'),
3067 (19, 'Arm', 'MhxFKArm')),
3068 (( 5, 'Leg', 'MhxFKLeg'),
3069 (21, 'Leg', 'MhxFKLeg')),
3070 (( 7, 'Fingers', 'MhxLinks'),
3071 (23, 'Fingers', 'MhxLinks')),
3072 ((11, 'Palm', 'MhxPalm'),
3073 (27, 'Palm', 'MhxPalm')),
3078 # class MhxMainPanel(bpy.types.Panel):
3081 class MhxMainPanel(bpy
.types
.Panel
):
3082 bl_label
= "MHX Main v %s" % bl_info
["version"]
3083 bl_space_type
= "VIEW_3D"
3084 bl_region_type
= "UI"
3085 #bl_options = {'DEFAULT_CLOSED'}
3088 def poll(cls
, context
):
3089 return (context
.object and context
.object.MhxRig
)
3091 def draw(self
, context
):
3092 layout
= self
.layout
3093 layout
.label("Layers")
3094 layout
.operator("mhx.pose_enable_all_layers")
3095 layout
.operator("mhx.pose_disable_all_layers")
3097 rig
= context
.object
3098 if rig
.MhxRig
== 'MHX':
3101 layers
= OtherLayers
3103 for (left
,right
) in layers
:
3105 if type(left
) == str:
3109 for (n
, name
, prop
) in [left
,right
]:
3110 row
.prop(rig
.data
, "layers", index
=n
, toggle
=True, text
=name
)
3113 layout
.label("Export/Import MHP")
3114 layout
.operator("mhx.saveas_mhp")
3115 layout
.operator("mhx.load_mhp")
3118 class VIEW3D_OT_MhxEnableAllLayersButton(bpy
.types
.Operator
):
3119 bl_idname
= "mhx.pose_enable_all_layers"
3120 bl_label
= "Enable all layers"
3121 bl_options
= {'UNDO'}
3123 def execute(self
, context
):
3124 rig
,mesh
= getMhxRigMesh(context
.object)
3125 for (left
,right
) in MhxLayers
:
3126 if type(left
) != str:
3127 for (n
, name
, prop
) in [left
,right
]:
3128 rig
.data
.layers
[n
] = True
3132 class VIEW3D_OT_MhxDisableAllLayersButton(bpy
.types
.Operator
):
3133 bl_idname
= "mhx.pose_disable_all_layers"
3134 bl_label
= "Disable all layers"
3135 bl_options
= {'UNDO'}
3137 def execute(self
, context
):
3138 rig
,mesh
= getMhxRigMesh(context
.object)
3140 pb
= context
.active_pose_bone
3143 if pb
.bone
.layers
[n
]:
3149 rig
.data
.layers
= layers
3154 def saveMhpFile(rig
, scn
, filepath
):
3156 for pb
in rig
.pose
.bones
:
3157 if pb
.parent
is None:
3160 (pname
, ext
) = os
.path
.splitext(filepath
)
3161 mhppath
= pname
+ ".mhp"
3163 fp
= open(mhppath
, "w", encoding
="utf-8", newline
="\n")
3165 writeMhpBones(fp
, root
, None)
3167 print("Mhp file %s saved" % mhppath
)
3170 def writeMhpBones(fp
, pb
, log
):
3171 if not isMuscleBone(pb
):
3174 mat
= b
.matrix_local
.inverted() * b
.parent
.matrix_local
* pb
.parent
.matrix
.inverted() * pb
.matrix
3176 mat
= pb
.matrix
.copy()
3177 maty
= mat
[1].copy()
3178 matz
= mat
[2].copy()
3182 diff
= mat
- Matrix()
3185 if abs(diff
[i
].length
) > 5e-3:
3190 fp
.write("%s\tmatrix" % pb
.name
)
3193 fp
.write("\t%.4f\t%.4f\t%.4f\t%.4f" % (row
[0], row
[1], row
[2], row
[3]))
3196 for child
in pb
.children
:
3197 writeMhpBones(fp
, child
, log
)
3200 def isMuscleBone(pb
):
3201 layers
= pb
.bone
.layers
3202 if (layers
[14] or layers
[15] or layers
[30] or layers
[31]):
3204 for cns
in pb
.constraints
:
3205 if (cns
.type == 'STRETCH_TO' or
3206 cns
.type == 'TRANSFORM' or
3207 cns
.type == 'TRACK_TO' or
3209 cns
.type[0:5] == 'COPY_'):
3214 def loadMhpFile(rig
, scn
, filepath
):
3216 for pb
in rig
.pose
.bones
:
3217 pb
.matrix_basis
= unit
3219 (pname
, ext
) = os
.path
.splitext(filepath
)
3220 mhppath
= pname
+ ".mhp"
3222 fp
= open(mhppath
, "rU")
3224 words
= line
.split()
3229 pb
= rig
.pose
.bones
[words
[0]]
3231 print("Warning: Did not find bone %s" % words
[0])
3234 if isMuscleBone(pb
):
3236 elif words
[1] == "quat":
3237 q
= Quaternion((float(words
[2]), float(words
[3]), float(words
[4]), float(words
[5])))
3238 mat
= q
.to_matrix().to_4x4()
3239 pb
.matrix_basis
= mat
3240 elif words
[1] == "gquat":
3241 q
= Quaternion((float(words
[2]), float(words
[3]), float(words
[4]), float(words
[5])))
3242 mat
= q
.to_matrix().to_4x4()
3243 maty
= mat
[1].copy()
3244 matz
= mat
[2].copy()
3247 pb
.matrix_basis
= pb
.bone
.matrix_local
.inverted() * mat
3248 elif words
[1] == "matrix":
3252 rows
.append((float(words
[n
]), float(words
[n
+1]), float(words
[n
+2]), float(words
[n
+3])))
3256 pb
.matrix_basis
= mat
3258 maty
= mat
[1].copy()
3259 matz
= mat
[2].copy()
3262 pb
.matrix_basis
= pb
.bone
.matrix_local
.inverted() * mat
3263 elif words
[1] == "scale":
3266 print("WARNING: Unknown line in mcp file:\n%s" % line
)
3268 print("Mhp file %s loaded" % mhppath
)
3271 class VIEW3D_OT_LoadMhpButton(bpy
.types
.Operator
):
3272 bl_idname
= "mhx.load_mhp"
3273 bl_label
= "Load MHP File"
3274 bl_description
= "Load a pose in MHP format"
3275 bl_options
= {'UNDO'}
3277 filename_ext
= ".mhp"
3278 filter_glob
= StringProperty(default
="*.mhp", options
={'HIDDEN'})
3279 filepath
= bpy
.props
.StringProperty(
3281 description
="File path used for mhp file",
3282 maxlen
= 1024, default
= "")
3284 def execute(self
, context
):
3285 loadMhpFile(context
.object, context
.scene
, self
.properties
.filepath
)
3288 def invoke(self
, context
, event
):
3289 context
.window_manager
.fileselect_add(self
)
3290 return {'RUNNING_MODAL'}
3293 class VIEW3D_OT_SaveasMhpFileButton(bpy
.types
.Operator
, ExportHelper
):
3294 bl_idname
= "mhx.saveas_mhp"
3295 bl_label
= "Save MHP File"
3296 bl_description
= "Save current pose in MHP format"
3297 bl_options
= {'UNDO'}
3299 filename_ext
= ".mhp"
3300 filter_glob
= StringProperty(default
="*.mhp", options
={'HIDDEN'})
3301 filepath
= bpy
.props
.StringProperty(
3303 description
="File path used for mhp file",
3304 maxlen
= 1024, default
= "")
3306 def execute(self
, context
):
3307 saveMhpFile(context
.object, context
.scene
, self
.properties
.filepath
)
3310 def invoke(self
, context
, event
):
3311 context
.window_manager
.fileselect_add(self
)
3312 return {'RUNNING_MODAL'}
3314 ###################################################################################
3318 ###################################################################################
3324 bodyLanguageVisemes
= ({
3326 ('MouthWidth_L', 0),
3327 ('MouthWidth_R', 0),
3328 ('MouthNarrow_L', 0),
3329 ('MouthNarrow_R', 0),
3331 ('UpLipsMidHeight', 0),
3332 ('LoLipsMidHeight', 0),
3335 ('TongueBackHeight', 0),
3336 ('TongueHeight', 0),
3339 ('MouthWidth_L', 0),
3340 ('MouthWidth_R', 0),
3341 ('MouthNarrow_L', 0),
3342 ('MouthNarrow_R', 0),
3344 ('UpLipsMidHeight', 0),
3345 ('LoLipsMidHeight', 0),
3348 ('TongueBackHeight', 0),
3349 ('TongueHeight', 0),
3352 ('MouthWidth_L', 0),
3353 ('MouthWidth_R', 0),
3354 ('MouthNarrow_L', 0),
3355 ('MouthNarrow_R', 0),
3357 ('UpLipsMidHeight', 0),
3358 ('LoLipsMidHeight', 0),
3361 ('TongueBackHeight', 0),
3362 ('TongueHeight', 0),
3365 ('MouthWidth_L', 0),
3366 ('MouthWidth_R', 0),
3367 ('MouthNarrow_L', 1.0),
3368 ('MouthNarrow_R', 1.0),
3370 ('UpLipsMidHeight', 0),
3371 ('LoLipsMidHeight', 0),
3374 ('TongueBackHeight', 0),
3375 ('TongueHeight', 0),
3378 ('MouthWidth_L', 0),
3379 ('MouthWidth_R', 0),
3380 ('MouthNarrow_L', 0.9),
3381 ('MouthNarrow_R', 0.9),
3383 ('UpLipsMidHeight', 0),
3384 ('LoLipsMidHeight', 0),
3387 ('TongueBackHeight', 0),
3388 ('TongueHeight', 0),
3391 ('MouthWidth_L', 0),
3392 ('MouthWidth_R', 0),
3393 ('MouthNarrow_L', 0.5),
3394 ('MouthNarrow_R', 0.5),
3396 ('UpLipsMidHeight', 0.2),
3397 ('LoLipsMidHeight', -0.2),
3400 ('TongueBackHeight', 0),
3401 ('TongueHeight', 0),
3404 ('MouthWidth_L', 0.2),
3405 ('MouthWidth_R', 0.2),
3406 ('MouthNarrow_L', 0),
3407 ('MouthNarrow_R', 0),
3409 ('UpLipsMidHeight', 0),
3410 ('LoLipsMidHeight', 0.3),
3413 ('TongueBackHeight', 0),
3414 ('TongueHeight', 0),
3417 ('MouthWidth_L', 0),
3418 ('MouthWidth_R', 0),
3419 ('MouthNarrow_L', 0),
3420 ('MouthNarrow_R', 0),
3422 ('UpLipsMidHeight', 0.5),
3423 ('LoLipsMidHeight', -0.7),
3426 ('TongueBackHeight', 0),
3427 ('TongueHeight', 0),
3430 ('MouthWidth_L', 0.8),
3431 ('MouthWidth_R', 0.8),
3432 ('MouthNarrow_L', 0),
3433 ('MouthNarrow_R', 0),
3435 ('UpLipsMidHeight', 1.0),
3436 ('LoLipsMidHeight', 0),
3439 ('TongueBackHeight', 0),
3440 ('TongueHeight', 0),
3443 ('MouthWidth_L', 0.2),
3444 ('MouthWidth_R', 0.2),
3445 ('MouthNarrow_L', 0),
3446 ('MouthNarrow_R', 0),
3448 ('UpLipsMidHeight', 0.6),
3449 ('LoLipsMidHeight', -0.6),
3452 ('TongueBackHeight', 0),
3453 ('TongueHeight', 0),
3456 ('MouthWidth_L', 0),
3457 ('MouthWidth_R', 0),
3458 ('MouthNarrow_L', 0),
3459 ('MouthNarrow_R', 0),
3461 ('UpLipsMidHeight', 0.4),
3462 ('LoLipsMidHeight', 0),
3465 ('TongueBackHeight', 0),
3466 ('TongueHeight', 0),
3469 ('MouthWidth_L', 0),
3470 ('MouthWidth_R', 0),
3471 ('MouthNarrow_L', 0),
3472 ('MouthNarrow_R', 0),
3474 ('UpLipsMidHeight', 0.5),
3475 ('LoLipsMidHeight', -0.6),
3477 ('MouthOpen', 0.25),
3478 ('TongueBackHeight', 0),
3479 ('TongueHeight', 0),
3482 ('MouthWidth_L', 0),
3483 ('MouthWidth_R', 0),
3484 ('MouthNarrow_L', 0),
3485 ('MouthNarrow_R', 0),
3487 ('UpLipsMidHeight', 0),
3488 ('LoLipsMidHeight', 0),
3491 ('TongueBackHeight', 1.0),
3492 ('TongueHeight', 1.0)
3495 ('MouthWidth_L', 0),
3496 ('MouthWidth_R', 0),
3497 ('MouthNarrow_L', 0),
3498 ('MouthNarrow_R', 0),
3500 ('UpLipsMidHeight', 0.5),
3501 ('LoLipsMidHeight', -0.5),
3503 ('MouthOpen', -0.2),
3504 ('TongueBackHeight', 1.0),
3505 ('TongueHeight', 1.0),
3508 ('MouthWidth_L', 0),
3509 ('MouthWidth_R', 0),
3510 ('MouthNarrow_L', 0),
3511 ('MouthNarrow_R', 0),
3513 ('UpLipsMidHeight', 0.5),
3514 ('LoLipsMidHeight', -0.5),
3516 ('MouthOpen', -0.2),
3517 ('TongueBackHeight', 1.0),
3518 ('TongueHeight', 0),
3536 VisemePanelBones
= {
3537 'MouthOpen' : ('PJaw', (0,0.25)),
3538 'UpLipsMidHeight' : ('PUpLipMid', (0,-0.25)),
3539 'LoLipsMidHeight' : ('PLoLipMid', (0,-0.25)),
3540 'LoLipsIn': ('PLoLipMid', (-0.25,0)),
3541 'MouthWidth_L' : ('PMouth_L', (0.25,0)),
3542 'MouthWidth_R' : ('PMouth_R', (-0.25,0)),
3543 'MouthNarrow_L' : ('PMouth_L', (-0.25,0)),
3544 'MouthNarrow_R' : ('PMouth_R', (0.25,0)),
3545 'LipsPart' : ('PMouthMid', (0, -0.25)),
3546 'TongueBackHeight': ('PTongue', (-0.25, 0)),
3547 'TongueHeight' : ('PTongue', (0, -0.25)),
3549 'UpLidUp_L' : ('PUpLid_L', (0,1.0)),
3550 'UpLidUp_R' : ('PUpLid_R', (0,1.0)),
3551 'LoLidDown_L' : ('PLoLid_L', (0,-1.0)),
3552 'LoLidDown_R' : ('PLoLid_R', (0,-1.0)),
3556 ('Rest', 'Etc', 'AH'),
3564 # makeVisemes(ob, scn):
3565 # class VIEW3D_OT_MhxMakeVisemesButton(bpy.types.Operator):
3568 def makeVisemes(ob
, scn
):
3570 if ob
.type != 'MESH':
3571 print("Active object %s is not a mesh" % ob
)
3573 if not ob
.data
.shape_keys
:
3574 print("%s has no shapekeys" % ob
)
3577 ob
.data
.shape_keys
.key_blocks
["VIS_Rest"]
3578 print("Visemes already created")
3583 ob
.data
.shape_keys
.key_blocks
["MouthOpen"]
3585 print("Mesh does not have face shapes")
3588 verts
= ob
.data
.vertices
3589 for (vis
,shapes
) in bodyLanguageVisemes
.items():
3590 if vis
in ['Blink', 'Unblink']:
3592 vkey
= ob
.shape_key_add(name
="VIS_%s" % vis
)
3594 for n
,v
in enumerate(verts
):
3595 vkey
.data
[n
].co
= v
.co
3596 for (name
,value
) in shapes
:
3597 if name
[-2:] == "_R":
3599 skey
= ob
.data
.shape_keys
.key_blocks
[name
]
3601 for n
,v
in enumerate(verts
):
3602 vkey
.data
[n
].co
+= factor
*(skey
.data
[n
].co
- v
.co
)
3603 print("Visemes made")
3606 class VIEW3D_OT_MhxMakeVisemesButton(bpy
.types
.Operator
):
3607 bl_idname
= "mhx.make_visemes"
3608 bl_label
= "Generate viseme shapekeys"
3609 bl_options
= {'UNDO'}
3611 def execute(self
, context
):
3612 makeVisemes(context
.object, context
.scene
)
3620 MohoVisemes
= dict({
3633 MagpieVisemes
= dict({
3646 # setViseme(context, vis, setKey, frame):
3647 # setBoneLocation(context, pbone, loc, mirror, setKey, frame):
3648 # class VIEW3D_OT_MhxVisemeButton(bpy.types.Operator):
3651 def getVisemeSet(context
, rig
):
3652 if rig
.MhxVisemeSet
== "StopStaring":
3653 return stopStaringVisemes
3654 elif rig
.MhxVisemeSet
== "BodyLanguage":
3655 return bodyLanguageVisemes
3657 raise MhxError("Unknown viseme set %s" % visset
)
3660 def setVisemeAlpha7(context
, vis
, visemes
, setKey
, frame
):
3661 (rig
, mesh
) = getMhxRigMesh(context
.object)
3666 if rig
.MhxShapekeyDrivers
:
3668 scale
*= rig
.pose
.bones
['PFace'].bone
.length
3673 shapekeys
= mesh
.data
.shape_keys
.key_blocks
3675 for (skey
, value
) in visemes
[vis
]:
3677 (b
, (x
,z
)) = VisemePanelBones
[skey
]
3678 loc
= mathutils
.Vector((float(x
*value
),0,float(z
*value
)))
3679 pb
= rig
.pose
.bones
[b
]
3680 pb
.location
= loc
*scale
3681 if setKey
or context
.tool_settings
.use_keyframe_insert_auto
:
3683 pb
.keyframe_insert('location', index
=n
, frame
=frame
, group
=pb
.name
)
3690 rig
[skey
] = value
*scale
3691 if setKey
or context
.tool_settings
.use_keyframe_insert_auto
:
3692 rig
.keyframe_insert('["%s"]' % skey
, frame
=frame
, group
="Visemes")
3695 shapekeys
[skey
].value
= value
*scale
3698 if setKey
or context
.tool_settings
.use_keyframe_insert_auto
:
3699 shapekeys
[skey
].keyframe_insert("value", frame
=frame
)
3704 class VIEW3D_OT_MhxVisemeButton(bpy
.types
.Operator
):
3705 bl_idname
= 'mhx.pose_viseme'
3707 bl_options
= {'UNDO'}
3708 viseme
= StringProperty()
3710 def invoke(self
, context
, event
):
3711 (rig
, mesh
) = getMhxRigMesh(context
.object)
3712 visemes
= getVisemeSet(context
, rig
)
3713 setVisemeAlpha7(context
, self
.viseme
, visemes
, False, context
.scene
.frame_current
)
3717 def readLipsync(context
, filepath
, offs
, struct
):
3718 (rig
, mesh
) = getMhxRigMesh(context
.object)
3719 if rig
.MhxVisemeSet
:
3720 visemes
= getVisemeSet(context
, rig
)
3722 props
= getProps(rig
, "Mhv")
3726 dummy
,units
= getUnitsFromString("x;"+rig
[prop
])
3727 visemes
[prop
] = units
3728 props
= getProps(rig
, "Mhsmouth")
3729 auto
= context
.tool_settings
.use_keyframe_insert_auto
3731 factor
= rig
.MhxStrength
3732 shapekeys
= getMhmShapekeys(rig
, mesh
)
3733 context
.scene
.objects
.active
= rig
3734 bpy
.ops
.object.mode_set(mode
='POSE')
3736 fp
= open(filepath
, "rU")
3742 vis
= struct
[words
[1]]
3743 frame
= int(words
[0])+offs
3744 if rig
.MhxVisemeSet
:
3745 setVisemeAlpha7(context
, vis
, visemes
, True, frame
)
3747 setMhmProps(rig
, shapekeys
, "Mhsmouth", visemes
["Mhv"+vis
], factor
, auto
, frame
)
3750 #setInterpolation(rig)
3752 print("Lipsync file %s loaded" % filepath
)
3755 class VIEW3D_OT_MhxMohoButton(bpy
.types
.Operator
, ImportHelper
):
3756 bl_idname
= "mhx.pose_load_moho"
3757 bl_label
= "Load Moho (.dat)"
3758 bl_options
= {'UNDO'}
3760 filename_ext
= ".dat"
3761 filter_glob
= StringProperty(default
="*.dat", options
={'HIDDEN'})
3762 filepath
= StringProperty(subtype
='FILE_PATH')
3764 def execute(self
, context
):
3765 readLipsync(context
, self
.properties
.filepath
, context
.scene
.frame_start
- 1, MohoVisemes
)
3768 def invoke(self
, context
, event
):
3769 context
.window_manager
.fileselect_add(self
)
3770 return {'RUNNING_MODAL'}
3773 class MhxLipsyncPanel(bpy
.types
.Panel
):
3774 bl_label
= "MHX Lipsync"
3775 bl_space_type
= "VIEW_3D"
3776 bl_region_type
= "UI"
3777 bl_options
= {'DEFAULT_CLOSED'}
3780 def poll(cls
, context
):
3781 return hasProps(context
.object, "Mhv")
3783 def draw(self
, context
):
3784 rig
,mesh
= getMhxRigMesh(context
.object)
3786 layout
.label("No MHX rig found")
3788 layout
= self
.layout
3790 if not rig
.MhxVisemeSet
:
3791 visemes
= getProps(rig
, "Mhv")
3793 layout
.label("No visemes found")
3796 layout
.operator("mhx.pose_reset_expressions", text
="Reset visemes").prefix
="Mhsmouth"
3797 layout
.operator("mhx.pose_key_expressions", text
="Key visemes").prefix
="Mhsmouth"
3798 layout
.prop(rig
, "MhxStrength")
3801 for prop
in visemes
:
3805 row
.operator("mhx.pose_mhm", text
=prop
[3:]).data
="Mhsmouth;"+rig
[prop
]
3812 row
.operator("mhx.pose_mhm", text
="Blink").data
="Mhsmouth;eye_left_closure:1;eye_right_closure:1"
3813 row
.operator("mhx.pose_mhm", text
="Unblink").data
="Mhsmouth;eye_left_closure:0;eye_right_closure:0"
3815 for (vis1
, vis2
, vis3
) in VisemeList
:
3817 row
.operator("mhx.pose_viseme", text
=vis1
).viseme
= vis1
3818 row
.operator("mhx.pose_viseme", text
=vis2
).viseme
= vis2
3819 row
.operator("mhx.pose_viseme", text
=vis3
).viseme
= vis3
3822 row
.operator("mhx.pose_viseme", text
="Blink").viseme
= 'Blink'
3823 row
.operator("mhx.pose_viseme", text
="Unblink").viseme
= 'Unblink'
3825 layout
.operator("mhx.make_visemes")
3829 row
.operator("mhx.pose_load_moho")
3830 #layout.operator("mhx.update")
3833 # updatePose(context):
3834 # class VIEW3D_OT_MhxUpdateButton(bpy.types.Operator):
3837 def updatePose(context
):
3839 scn
.frame_current
= scn
.frame_current
3840 bpy
.ops
.object.posemode_toggle()
3841 bpy
.ops
.object.posemode_toggle()
3844 class VIEW3D_OT_MhxUpdateButton(bpy
.types
.Operator
):
3845 bl_idname
= "mhx.update"
3848 def execute(self
, context
):
3853 ###################################################################################
3857 ###################################################################################
3859 class VIEW3D_OT_MhxResetExpressionsButton(bpy
.types
.Operator
):
3860 bl_idname
= "mhx.pose_reset_expressions"
3861 bl_label
= "Reset expressions"
3862 bl_options
= {'UNDO'}
3863 prefix
= StringProperty()
3865 def execute(self
, context
):
3866 rig
,mesh
= getMhxRigMesh(context
.object)
3867 shapekeys
= getMhmShapekeys(rig
, mesh
)
3868 clearMhmProps(rig
, shapekeys
, self
.prefix
, context
.tool_settings
.use_keyframe_insert_auto
, context
.scene
.frame_current
)
3873 class VIEW3D_OT_MhxKeyExpressionsButton(bpy
.types
.Operator
):
3874 bl_idname
= "mhx.pose_key_expressions"
3875 bl_label
= "Key expressions"
3876 bl_options
= {'UNDO'}
3877 prefix
= StringProperty()
3879 def execute(self
, context
):
3880 rig
,mesh
= getMhxRigMesh(context
.object)
3881 props
= getProps(rig
, self
.prefix
)
3882 frame
= context
.scene
.frame_current
3884 rig
.keyframe_insert('["%s"]'%prop
, frame
=frame
)
3889 class VIEW3D_OT_MhxPinExpressionButton(bpy
.types
.Operator
):
3890 bl_idname
= "mhx.pose_pin_expression"
3892 bl_options
= {'UNDO'}
3893 data
= StringProperty()
3895 def execute(self
, context
):
3896 rig
,mesh
= getMhxRigMesh(context
.object)
3897 words
= self
.data
.split(";")
3899 expression
= words
[1]
3901 props
= getProps(rig
, prefix
)
3902 if context
.tool_settings
.use_keyframe_insert_auto
:
3903 frame
= context
.scene
.frame_current
3906 if prop
== expression
:
3910 if abs(rig
[prop
] - old
) > 1e-3:
3911 rig
.keyframe_insert('["%s"]'%prop
, frame
=frame
)
3914 if prop
== expression
:
3922 def getMhmShapekeys(rig
, mesh
):
3923 if rig
.MhxShapekeyDrivers
:
3926 return mesh
.data
.shape_keys
.key_blocks
3929 def setMhmProps(rig
, shapekeys
, prefix
, units
, factor
, auto
, frame
):
3930 clearMhmProps(rig
, shapekeys
, prefix
, auto
, frame
)
3931 for (prop
, value
) in units
:
3933 skey
= prop
[3:].replace("_","-")
3934 shapekeys
[skey
].value
= factor
*value
3936 shapekeys
[skey
].keyframe_insert("value", frame
=frame
)
3938 rig
[prop
] = factor
*value
3940 rig
.keyframe_insert('["%s"]'%prop
, frame
=frame
)
3943 def clearMhmProps(rig
, shapekeys
, prefix
, auto
, frame
):
3944 props
= getProps(rig
, prefix
)
3947 skey
= prop
[3:].replace("_","-")
3948 shapekeys
[skey
].value
= 0.0
3950 shapekeys
[skey
].keyframe_insert("value", frame
=frame
)
3954 rig
.keyframe_insert('["%s"]'%prop
, frame
=frame
)
3957 def getUnitsFromString(string
):
3958 words
= string
.split(";")
3961 for word
in words
[1:]:
3964 unit
= word
.split(":")
3965 prop
= "Mhs" + unit
[0]
3966 value
= float(unit
[1])
3967 units
.append((prop
, value
))
3971 class VIEW3D_OT_MhxMhmButton(bpy
.types
.Operator
):
3972 bl_idname
= "mhx.pose_mhm"
3974 bl_options
= {'UNDO'}
3975 data
= StringProperty()
3977 def execute(self
, context
):
3978 rig
,mesh
= getMhxRigMesh(context
.object)
3979 auto
= context
.tool_settings
.use_keyframe_insert_auto
3980 frame
= context
.scene
.frame_current
3981 shapekeys
= getMhmShapekeys(rig
, mesh
)
3982 prefix
,units
= getUnitsFromString(self
.data
)
3983 setMhmProps(rig
, shapekeys
, prefix
, units
, rig
.MhxStrength
, auto
, frame
)
3988 def getProps(rig
, prefix
):
3990 for prop
in rig
.keys():
3991 if prop
.startswith(prefix
):
3997 def hasProps(ob
, prefix
):
4000 if ob
.type == 'MESH' and ob
.parent
:
4002 elif ob
.type == 'ARMATURE':
4006 for prop
in rig
.keys():
4007 if prop
.startswith(prefix
):
4012 class MhxExpressionsPanel(bpy
.types
.Panel
):
4013 bl_label
= "MHX Expressions"
4014 bl_space_type
= "VIEW_3D"
4015 bl_region_type
= "UI"
4016 bl_options
= {'DEFAULT_CLOSED'}
4019 def poll(cls
, context
):
4020 return hasProps(context
.object, "Mhe")
4022 def draw(self
, context
):
4023 layout
= self
.layout
4024 rig
,mesh
= getMhxRigMesh(context
.object)
4026 layout
.label("No MHX rig found")
4028 exprs
= getProps(rig
, "Mhe")
4030 layout
.label("No expressions found")
4033 layout
.operator("mhx.pose_reset_expressions").prefix
="Mhs"
4034 layout
.operator("mhx.pose_key_expressions").prefix
="Mhs"
4035 layout
.prop(rig
, "MhxStrength")
4038 layout
.operator("mhx.pose_mhm", text
=prop
[3:]).data
="Mhs;"+rig
[prop
]
4041 def drawShapePanel(self
, context
, prefix
, name
):
4042 layout
= self
.layout
4043 rig
,mesh
= getMhxRigMesh(context
.object)
4045 print("No MHX rig found")
4047 if not rig
.MhxShapekeyDrivers
:
4048 layout
.label("No shapekey drivers.")
4049 layout
.label("Set %s values in mesh context instead" % name
)
4051 props
= getProps(rig
, prefix
)
4053 layout
.label("No %ss found" % name
)
4056 layout
.operator("mhx.pose_reset_expressions", text
="Reset %ss" % name
).prefix
=prefix
4057 layout
.operator("mhx.pose_key_expressions", text
="Key %ss" % name
).prefix
=prefix
4058 #layout.operator("mhx.update")
4062 row
= layout
.split(0.85)
4063 row
.prop(rig
, '["%s"]' % prop
, text
=prop
[3:])
4064 row
.operator("mhx.pose_pin_expression", text
="", icon
='UNPINNED').data
= (prefix
+ ";" + prop
)
4068 class MhxExpressionUnitsPanel(bpy
.types
.Panel
):
4069 bl_label
= "MHX Expression Tuning"
4070 bl_space_type
= "VIEW_3D"
4071 bl_region_type
= "UI"
4072 bl_options
= {'DEFAULT_CLOSED'}
4075 def poll(cls
, context
):
4076 return hasProps(context
.object, "Mhs")
4078 def draw(self
, context
):
4079 drawShapePanel(self
, context
, "Mhs", "expression")
4082 class MhxCustomShapePanel(bpy
.types
.Panel
):
4083 bl_label
= "MHX Custom Shapes"
4084 bl_space_type
= "VIEW_3D"
4085 bl_region_type
= "UI"
4086 bl_options
= {'DEFAULT_CLOSED'}
4089 def poll(cls
, context
):
4090 return hasProps(context
.object, "Mhc")
4092 def draw(self
, context
):
4093 drawShapePanel(self
, context
, "Mhc", "custom shape")
4096 #########################################
4100 #########################################
4102 def getPoseMatrix(gmat
, pb
):
4103 restInv
= pb
.bone
.matrix_local
.inverted()
4105 parInv
= pb
.parent
.matrix
.inverted()
4106 parRest
= pb
.parent
.bone
.matrix_local
4107 return restInv
* (parRest
* (parInv
* gmat
))
4109 return restInv
* gmat
4112 def getGlobalMatrix(mat
, pb
):
4113 gmat
= pb
.bone
.matrix_local
* mat
4115 parMat
= pb
.parent
.matrix
4116 parRest
= pb
.parent
.bone
.matrix_local
4117 return parMat
* (parRest
.inverted() * gmat
)
4122 def matchPoseTranslation(pb
, src
, auto
):
4123 pmat
= getPoseMatrix(src
.matrix
, pb
)
4124 insertLocation(pb
, pmat
, auto
)
4127 def insertLocation(pb
, mat
, auto
):
4128 pb
.location
= mat
.to_translation()
4130 pb
.keyframe_insert("location", group
=pb
.name
)
4131 bpy
.ops
.object.mode_set(mode
='OBJECT')
4132 bpy
.ops
.object.mode_set(mode
='POSE')
4135 def matchPoseRotation(pb
, src
, auto
):
4136 pmat
= getPoseMatrix(src
.matrix
, pb
)
4137 insertRotation(pb
, pmat
, auto
)
4140 def printMatrix(string
,mat
):
4143 print(" %.4g %.4g %.4g %.4g" % tuple(mat
[i
]))
4146 def insertRotation(pb
, mat
, auto
):
4147 q
= mat
.to_quaternion()
4148 if pb
.rotation_mode
== 'QUATERNION':
4149 pb
.rotation_quaternion
= q
4151 pb
.keyframe_insert("rotation_quaternion", group
=pb
.name
)
4153 pb
.rotation_euler
= q
.to_euler(pb
.rotation_mode
)
4155 pb
.keyframe_insert("rotation_euler", group
=pb
.name
)
4156 bpy
.ops
.object.mode_set(mode
='OBJECT')
4157 bpy
.ops
.object.mode_set(mode
='POSE')
4160 def matchIkLeg(legIk
, toeFk
, mBall
, mToe
, mHeel
, auto
):
4161 rmat
= toeFk
.matrix
.to_3x3()
4162 tHead
= Vector(toeFk
.matrix
.col
[3][:3])
4164 tail
= tHead
+ ty
* toeFk
.bone
.length
4167 zBall
= mBall
.matrix
.col
[3][2]
4168 except AttributeError:
4170 zToe
= mToe
.matrix
.col
[3][2]
4171 zHeel
= mHeel
.matrix
.col
[3][2]
4173 x
= Vector(rmat
.col
[0])
4174 y
= Vector(rmat
.col
[1])
4175 z
= Vector(rmat
.col
[2])
4177 if zHeel
> zBall
and zHeel
> zToe
:
4178 # 1. foot.ik is flat
4179 if abs(y
[2]) > abs(z
[2]):
4183 # 2. foot.ik starts at heel
4184 hHead
= Vector(mHeel
.matrix
.col
[3][:3])
4191 head
= tail
- y
* legIk
.bone
.length
4198 gmat
.col
[3][:3] = head
4199 pmat
= getPoseMatrix(gmat
, legIk
)
4201 insertLocation(legIk
, pmat
, auto
)
4202 insertRotation(legIk
, pmat
, auto
)
4205 def matchPoleTarget(pb
, above
, below
, auto
):
4206 x
= Vector(above
.matrix
.col
[1][:3])
4207 y
= Vector(below
.matrix
.col
[1][:3])
4208 p0
= Vector(below
.matrix
.col
[3][:3])
4210 if abs(n
.length
) > 1e-4:
4214 p
= p0
+ z
/z
.length
*3.0
4217 gmat
= Matrix
.Translation(p
)
4218 pmat
= getPoseMatrix(gmat
, pb
)
4219 insertLocation(pb
, pmat
, auto
)
4222 def matchPoseReverse(pb
, src
, auto
):
4224 tail
= gmat
.col
[3] + src
.length
* gmat
.col
[1]
4225 rmat
= Matrix((gmat
.col
[0], -gmat
.col
[1], -gmat
.col
[2], tail
))
4227 pmat
= getPoseMatrix(rmat
, pb
)
4228 pb
.matrix_basis
= pmat
4229 insertRotation(pb
, pmat
, auto
)
4232 def matchPoseScale(pb
, src
, auto
):
4233 pmat
= getPoseMatrix(src
.matrix
, pb
)
4234 pb
.scale
= pmat
.to_scale()
4236 pb
.keyframe_insert("scale", group
=pb
.name
)
4237 bpy
.ops
.object.mode_set(mode
='OBJECT')
4238 bpy
.ops
.object.mode_set(mode
='POSE')
4241 def snapFkArm(context
, data
):
4242 rig
= context
.object
4243 prop
,old
,suffix
= setSnapProp(rig
, data
, 1.0, context
, False)
4244 auto
= context
.scene
.tool_settings
.use_keyframe_insert_auto
4246 print("Snap FK Arm%s" % suffix
)
4247 snapFk
,cnsFk
= getSnapBones(rig
, "ArmFK", suffix
)
4248 (uparmFk
, loarmFk
, handFk
) = snapFk
4249 muteConstraints(cnsFk
, True)
4250 snapIk
,cnsIk
= getSnapBones(rig
, "ArmIK", suffix
)
4251 (uparmIk
, loarmIk
, elbow
, elbowPt
, handIk
) = snapIk
4253 matchPoseRotation(uparmFk
, uparmIk
, auto
)
4254 matchPoseScale(uparmFk
, uparmIk
, auto
)
4256 matchPoseRotation(loarmFk
, loarmIk
, auto
)
4257 matchPoseScale(loarmFk
, loarmIk
, auto
)
4259 restoreSnapProp(rig
, prop
, old
, context
)
4262 matchHand
= rig
["MhaHandFollowsWrist" + suffix
]
4266 matchPoseRotation(handFk
, handIk
, auto
)
4267 matchPoseScale(handFk
, handIk
, auto
)
4269 #muteConstraints(cnsFk, False)
4273 def snapIkArm(context
, data
):
4274 rig
= context
.object
4275 prop
,old
,suffix
= setSnapProp(rig
, data
, 0.0, context
, True)
4276 auto
= context
.scene
.tool_settings
.use_keyframe_insert_auto
4278 print("Snap IK Arm%s" % suffix
)
4279 snapIk
,cnsIk
= getSnapBones(rig
, "ArmIK", suffix
)
4280 (uparmIk
, loarmIk
, elbow
, elbowPt
, handIk
) = snapIk
4281 snapFk
,cnsFk
= getSnapBones(rig
, "ArmFK", suffix
)
4282 (uparmFk
, loarmFk
, handFk
) = snapFk
4283 muteConstraints(cnsIk
, True)
4285 matchPoseTranslation(handIk
, handFk
, auto
)
4286 matchPoseRotation(handIk
, handFk
, auto
)
4288 matchPoleTarget(elbowPt
, uparmFk
, loarmFk
, auto
)
4290 #matchPoseRotation(uparmIk, uparmFk, auto)
4291 #matchPoseRotation(loarmIk, loarmFk, auto)
4293 restoreSnapProp(rig
, prop
, old
, context
)
4294 #muteConstraints(cnsIk, False)
4298 def snapFkLeg(context
, data
):
4299 rig
= context
.object
4300 prop
,old
,suffix
= setSnapProp(rig
, data
, 1.0, context
, False)
4301 auto
= context
.scene
.tool_settings
.use_keyframe_insert_auto
4303 print("Snap FK Leg%s" % suffix
)
4304 snap
,_
= getSnapBones(rig
, "Leg", suffix
)
4305 (upleg
, loleg
, foot
, toe
) = snap
4306 snapIk
,cnsIk
= getSnapBones(rig
, "LegIK", suffix
)
4307 (uplegIk
, lolegIk
, kneePt
, ankleIk
, legIk
, footRev
, toeRev
, mBall
, mToe
, mHeel
) = snapIk
4308 snapFk
,cnsFk
= getSnapBones(rig
, "LegFK", suffix
)
4309 (uplegFk
, lolegFk
, footFk
, toeFk
) = snapFk
4310 muteConstraints(cnsFk
, True)
4312 matchPoseRotation(uplegFk
, uplegIk
, auto
)
4313 matchPoseScale(uplegFk
, uplegIk
, auto
)
4315 matchPoseRotation(lolegFk
, lolegIk
, auto
)
4316 matchPoseScale(lolegFk
, lolegIk
, auto
)
4318 restoreSnapProp(rig
, prop
, old
, context
)
4320 if not rig
["MhaLegIkToAnkle" + suffix
]:
4321 matchPoseReverse(footFk
, footRev
, auto
)
4322 matchPoseReverse(toeFk
, toeRev
, auto
)
4324 #muteConstraints(cnsFk, False)
4328 def snapIkLeg(context
, data
):
4329 rig
= context
.object
4331 prop
,old
,suffix
= setSnapProp(rig
, data
, 0.0, context
, True)
4332 auto
= scn
.tool_settings
.use_keyframe_insert_auto
4334 print("Snap IK Leg%s" % suffix
)
4335 snapIk
,cnsIk
= getSnapBones(rig
, "LegIK", suffix
)
4336 (uplegIk
, lolegIk
, kneePt
, ankleIk
, legIk
, footRev
, toeRev
, mBall
, mToe
, mHeel
) = snapIk
4337 snapFk
,cnsFk
= getSnapBones(rig
, "LegFK", suffix
)
4338 (uplegFk
, lolegFk
, footFk
, toeFk
) = snapFk
4339 muteConstraints(cnsIk
, True)
4341 legIkToAnkle
= rig
["MhaLegIkToAnkle" + suffix
]
4343 matchPoseTranslation(ankleIk
, footFk
, auto
)
4345 #matchPoseTranslation(legIk, legFk, auto)
4346 #matchPoseRotation(legIk, legFk, auto)
4347 matchIkLeg(legIk
, toeFk
, mBall
, mToe
, mHeel
, auto
)
4349 matchPoseReverse(toeRev
, toeFk
, auto
)
4350 matchPoseReverse(footRev
, footFk
, auto
)
4352 matchPoleTarget(kneePt
, uplegFk
, lolegFk
, auto
)
4354 #matchPoseRotation(uplegIk, uplegFk, auto)
4355 #matchPoseRotation(lolegIk, lolegFk, auto)
4357 if not legIkToAnkle
:
4358 matchPoseTranslation(ankleIk
, footFk
, auto
)
4360 restoreSnapProp(rig
, prop
, old
, context
)
4361 #muteConstraints(cnsIk, False)
4366 "Arm" : ["upper_arm", "forearm", "hand"],
4367 "ArmFK" : ["upper_arm.fk", "forearm.fk", "hand.fk"],
4368 "ArmIK" : ["upper_arm.ik", "forearm.ik", None, "elbow.pt.ik", "hand.ik"],
4369 "Leg" : ["thigh", "shin", "foot", "toe"],
4370 "LegFK" : ["thigh.fk", "shin.fk", "foot.fk", "toe.fk"],
4371 "LegIK" : ["thigh.ik", "shin.ik", "knee.pt.ik", "ankle.ik", "foot.ik", "foot.rev", "toe.rev", "ball.marker", "toe.marker", "heel.marker"],
4375 def getSnapBones(rig
, key
, suffix
):
4377 pb
= rig
.pose
.bones
["UpLeg_L"]
4382 raise NameError("MakeHuman alpha 7 not supported after Blender 2.68")
4385 rig
.pose
.bones
["thigh.fk.L"]
4386 names
= SnapBonesAlpha8
[key
]
4387 suffix
= '.' + suffix
[1:]
4392 raise NameError("Not an mhx armature")
4399 pb
= rig
.pose
.bones
[name
+suffix
]
4404 for cns
in pb
.constraints
:
4405 if cns
.type == 'LIMIT_ROTATION' and not cns
.mute
:
4406 constraints
.append(cns
)
4409 return tuple(pbones
),constraints
4412 def muteConstraints(constraints
, value
):
4413 for cns
in constraints
:
4417 class VIEW3D_OT_MhxSnapFk2IkButton(bpy
.types
.Operator
):
4418 bl_idname
= "mhx.snap_fk_ik"
4419 bl_label
= "Snap FK"
4420 bl_options
= {'UNDO'}
4421 data
= StringProperty()
4423 def execute(self
, context
):
4424 bpy
.ops
.object.mode_set(mode
='POSE')
4425 rig
= context
.object
4426 if rig
.MhxSnapExact
:
4427 rig
["MhaRotationLimits"] = 0.0
4428 if self
.data
[:6] == "MhaArm":
4429 snapFkArm(context
, self
.data
)
4430 elif self
.data
[:6] == "MhaLeg":
4431 snapFkLeg(context
, self
.data
)
4435 class VIEW3D_OT_MhxSnapIk2FkButton(bpy
.types
.Operator
):
4436 bl_idname
= "mhx.snap_ik_fk"
4437 bl_label
= "Snap IK"
4438 bl_options
= {'UNDO'}
4439 data
= StringProperty()
4441 def execute(self
, context
):
4442 bpy
.ops
.object.mode_set(mode
='POSE')
4443 rig
= context
.object
4444 if rig
.MhxSnapExact
:
4445 rig
["MhaRotationLimits"] = 0.0
4446 if self
.data
[:6] == "MhaArm":
4447 snapIkArm(context
, self
.data
)
4448 elif self
.data
[:6] == "MhaLeg":
4449 snapIkLeg(context
, self
.data
)
4453 def setSnapProp(rig
, data
, value
, context
, isIk
):
4454 words
= data
.split()
4456 oldValue
= rig
[prop
]
4460 extra
= int(words
[3])
4461 oldIk
= rig
.data
.layers
[ik
]
4462 oldFk
= rig
.data
.layers
[fk
]
4463 oldExtra
= rig
.data
.layers
[extra
]
4464 rig
.data
.layers
[ik
] = True
4465 rig
.data
.layers
[fk
] = True
4466 rig
.data
.layers
[extra
] = True
4477 return (prop
, (oldValue
, ik
, fk
, extra
, oldIk
, oldFk
, oldExtra
), prop
[-2:])
4480 def restoreSnapProp(rig
, prop
, old
, context
):
4482 (oldValue
, ik
, fk
, extra
, oldIk
, oldFk
, oldExtra
) = old
4483 rig
[prop
] = oldValue
4484 rig
.data
.layers
[ik
] = oldIk
4485 rig
.data
.layers
[fk
] = oldFk
4486 rig
.data
.layers
[extra
] = oldExtra
4491 class VIEW3D_OT_MhxToggleFkIkButton(bpy
.types
.Operator
):
4492 bl_idname
= "mhx.toggle_fk_ik"
4493 bl_label
= "FK - IK"
4494 bl_options
= {'UNDO'}
4495 toggle
= StringProperty()
4497 def execute(self
, context
):
4498 words
= self
.toggle
.split()
4499 rig
= context
.object
4501 value
= float(words
[1])
4502 onLayer
= int(words
[2])
4503 offLayer
= int(words
[3])
4504 rig
.data
.layers
[onLayer
] = True
4505 rig
.data
.layers
[offLayer
] = False
4507 # Don't do autokey - confusing.
4508 #if context.tool_settings.use_keyframe_insert_auto:
4509 # rig.keyframe_insert('["%s"]' % prop, frame=scn.frame_current)
4514 # MHX FK/IK Switch panel
4517 class MhxFKIKPanel(bpy
.types
.Panel
):
4518 bl_label
= "MHX FK/IK Switch"
4519 bl_space_type
= "VIEW_3D"
4520 bl_region_type
= "UI"
4521 #bl_options = {'DEFAULT_CLOSED'}
4524 def poll(cls
, context
):
4525 return (context
.object and context
.object.MhxRig
== 'MHX')
4527 def draw(self
, context
):
4528 rig
= context
.object
4529 layout
= self
.layout
4536 layout
.label("FK/IK switch")
4539 self
.toggleButton(row
, rig
, "MhaArmIk_L", " 3", " 2")
4540 self
.toggleButton(row
, rig
, "MhaArmIk_R", " 19", " 18")
4543 self
.toggleButton(row
, rig
, "MhaLegIk_L", " 5", " 4")
4544 self
.toggleButton(row
, rig
, "MhaLegIk_R", " 21", " 20")
4546 layout
.label("IK Influence")
4549 row
.prop(rig
, '["MhaArmIk_L"]', text
="")
4550 row
.prop(rig
, '["MhaArmIk_R"]', text
="")
4553 row
.prop(rig
, '["MhaLegIk_L"]', text
="")
4554 row
.prop(rig
, '["MhaLegIk_R"]', text
="")
4557 ok
= (rig
["MhxVersion"] >= 12)
4561 layout
.label("Snapping only works with MHX version 1.12 and later.")
4565 layout
.label("Snapping")
4567 row
.label("Rotation Limits")
4568 row
.prop(rig
, '["MhaRotationLimits"]', text
="")
4569 row
.prop(rig
, "MhxSnapExact", text
="Exact Snapping")
4571 layout
.label("Snap Arm bones")
4574 row
.operator("mhx.snap_fk_ik", text
="Snap L FK Arm").data
= "MhaArmIk_L 2 3 12"
4575 row
.operator("mhx.snap_fk_ik", text
="Snap R FK Arm").data
= "MhaArmIk_R 18 19 28"
4578 row
.operator("mhx.snap_ik_fk", text
="Snap L IK Arm").data
= "MhaArmIk_L 2 3 12"
4579 row
.operator("mhx.snap_ik_fk", text
="Snap R IK Arm").data
= "MhaArmIk_R 18 19 28"
4581 layout
.label("Snap Leg bones")
4584 row
.operator("mhx.snap_fk_ik", text
="Snap L FK Leg").data
= "MhaLegIk_L 4 5 12"
4585 row
.operator("mhx.snap_fk_ik", text
="Snap R FK Leg").data
= "MhaLegIk_R 20 21 28"
4588 row
.operator("mhx.snap_ik_fk", text
="Snap L IK Leg").data
= "MhaLegIk_L 4 5 12"
4589 row
.operator("mhx.snap_ik_fk", text
="Snap R IK Leg").data
= "MhaLegIk_R 20 21 28"
4592 def toggleButton(self
, row
, rig
, prop
, fk
, ik
):
4594 row
.operator("mhx.toggle_fk_ik", text
="IK").toggle
= prop
+ " 0" + fk
+ ik
4596 row
.operator("mhx.toggle_fk_ik", text
="FK").toggle
= prop
+ " 1" + ik
+ fk
4599 ###################################################################################
4603 ###################################################################################
4605 # class MhxDriversPanel(bpy.types.Panel):
4608 class MhxDriversPanel(bpy
.types
.Panel
):
4609 bl_label
= "MHX Drivers"
4610 bl_space_type
= "VIEW_3D"
4611 bl_region_type
= "UI"
4612 bl_options
= {'DEFAULT_CLOSED'}
4615 def poll(cls
, context
):
4616 return (context
.object and context
.object.MhxRig
== 'MHX')
4618 def draw(self
, context
):
4623 plist
= list(context
.object.keys())
4626 if prop
[0:3] == 'Mha':
4627 if prop
[-2:] == '_L':
4628 lrProps
.append(prop
[:-2])
4629 elif prop
[-2:] != '_R':
4631 elif prop
[0:3] == 'Mhf':
4632 if prop
[-2:] == '_L':
4633 lrFaceProps
.append(prop
[:-2])
4634 elif prop
[-2:] != '_R':
4635 faceProps
.append(prop
)
4638 layout
= self
.layout
4640 layout
.prop(ob
, '["%s"]' % prop
, text
=prop
[3:])
4646 for prop
in lrProps
:
4648 row
.prop(ob
, '["%s"]' % (prop
+"_L"), text
=prop
[3:])
4649 row
.prop(ob
, '["%s"]' % (prop
+"_R"), text
=prop
[3:])
4653 layout
.label("Face shapes")
4654 for prop
in faceProps
:
4655 layout
.prop(ob
, '["%s"]' % prop
, text
=prop
[3:])
4661 for prop
in lrFaceProps
:
4663 row
.prop(ob
, '["%s"]' % (prop
+"_L"), text
=prop
[3:])
4664 row
.prop(ob
, '["%s"]' % (prop
+"_R"), text
=prop
[3:])
4668 ###################################################################################
4672 ###################################################################################
4674 # class MhxVisibilityPanel(bpy.types.Panel):
4677 class MhxVisibilityPanel(bpy
.types
.Panel
):
4678 bl_label
= "MHX Visibility"
4679 bl_space_type
= "VIEW_3D"
4680 bl_region_type
= "UI"
4681 bl_options
= {'DEFAULT_CLOSED'}
4684 def poll(cls
, context
):
4685 return (context
.object and context
.object.MhxRig
)
4687 def draw(self
, context
):
4689 layout
= self
.layout
4690 props
= list(ob
.keys())
4693 if prop
[0:3] == "Mhh":
4694 layout
.prop(ob
, '["%s"]' % prop
, text
="Hide %s" % prop
[3:])
4696 layout
.operator("mhx.update_textures")
4698 layout
.operator("mhx.add_hiders")
4699 layout
.operator("mhx.remove_hiders")
4702 class VIEW3D_OT_MhxUpdateTexturesButton(bpy
.types
.Operator
):
4703 bl_idname
= "mhx.update_textures"
4705 bl_options
= {'UNDO'}
4707 def execute(self
, context
):
4709 for mat
in bpy
.data
.materials
:
4710 if mat
.animation_data
:
4715 for driver
in mat
.animation_data
.drivers
:
4716 prop
= mat
.path_resolve(driver
.data_path
)
4717 value
= driver
.evaluate(scn
.frame_current
)
4718 prop
[driver
.array_index
] = value
4721 class VIEW3D_OT_MhxAddHidersButton(bpy
.types
.Operator
):
4722 bl_idname
= "mhx.add_hiders"
4723 bl_label
= "Add Hide Property"
4724 bl_options
= {'UNDO'}
4726 def execute(self
, context
):
4727 rig
= context
.object
4728 for ob
in context
.scene
.objects
:
4729 if ob
.select
and ob
!= rig
:
4730 prop
= "Mhh%s" % ob
.name
4731 defNewProp(prop
, "Bool", "default=False")
4733 addHider(ob
, "hide", rig
, prop
)
4734 addHider(ob
, "hide_render", rig
, prop
)
4737 def addHider(ob
, attr
, rig
, prop
):
4738 fcu
= ob
.driver_add(attr
)
4740 drv
.type = 'SCRIPTED'
4741 drv
.expression
= "x"
4742 drv
.show_debug_info
= True
4743 var
= drv
.variables
.new()
4745 targ
= var
.targets
[0]
4747 targ
.data_path
= '["%s"]' % prop
4750 class VIEW3D_OT_MhxRemoveHidersButton(bpy
.types
.Operator
):
4751 bl_idname
= "mhx.remove_hiders"
4752 bl_label
= "Remove Hide Property"
4753 bl_options
= {'UNDO'}
4755 def execute(self
, context
):
4756 rig
= context
.object
4757 for ob
in context
.scene
.objects
:
4758 if ob
.select
and ob
!= rig
:
4759 ob
.driver_remove("hide")
4760 ob
.driver_remove("hide_render")
4761 del rig
["Mhh%s" % ob
.name
]
4764 ###################################################################################
4768 ###################################################################################
4770 # getMhxRigMesh(ob):
4776 elif ob
.type == 'ARMATURE':
4778 elif ob
.type == 'MESH':
4780 return (par
and (par
.type == 'ARMATURE') and par
.MhxRig
)
4784 def getMhxRigMesh(ob
):
4785 if ob
.type == 'ARMATURE':
4786 for mesh
in ob
.children
:
4787 if mesh
.MhxMesh
and ob
.MhxRig
:
4790 elif ob
.type == 'MESH':
4792 if (par
and par
.type == 'ARMATURE' and par
.MhxRig
):
4803 # setInterpolation(rig):
4806 def setInterpolation(rig
):
4807 if not rig
.animation_data
:
4809 act
= rig
.animation_data
.action
4812 for fcu
in act
.fcurves
:
4813 for pt
in fcu
.keyframe_points
:
4814 pt
.interpolation
= 'LINEAR'
4815 fcu
.extrapolation
= 'CONSTANT'
4818 ###################################################################################
4820 # initialize and register
4822 ###################################################################################
4824 def menu_func(self
, context
):
4825 self
.layout
.operator(ImportMhx
.bl_idname
, text
="MakeHuman (.mhx)...")
4828 bpy
.types
.Object
.MhxVersionStr
= StringProperty(name
="Version", default
="", maxlen
=128)
4829 bpy
.types
.Object
.MhAlpha8
= BoolProperty(default
=True)
4830 bpy
.types
.Object
.MhxMesh
= BoolProperty(default
=False)
4831 bpy
.types
.Object
.MhxRig
= StringProperty(default
="")
4832 bpy
.types
.Object
.MhxVisemeSet
= StringProperty(default
="")
4833 bpy
.types
.Object
.MhxRigify
= BoolProperty(default
=False)
4834 bpy
.types
.Object
.MhxSnapExact
= BoolProperty(default
=False)
4835 bpy
.types
.Object
.MhxShapekeyDrivers
= BoolProperty(default
=True)
4836 bpy
.types
.Object
.MhxStrength
= FloatProperty(
4837 name
= "Expression strength",
4838 description
= "Multiply expression with this factor",
4839 default
=1.0, min=-1.0, max=2.0
4841 bpy
.utils
.register_module(__name__
)
4842 bpy
.types
.INFO_MT_file_import
.append(menu_func
)
4846 bpy
.utils
.unregister_module(__name__
)
4850 bpy
.types
.INFO_MT_file_import
.remove(menu_func
)
4854 if __name__
== "__main__":