OSDN Git Service

a13cc4a6f066650d584f4c1ac9449ac6f8f908a2
[meshio/meshio.git] / swig / blender24 / pmd_import.py
1 #!BPY
2 # coding:utf-8
3 """
4  Name: 'MikuMikuDance model (.pmd)...'
5  Blender: 248
6  Group: 'Import'
7  Tooltip: 'Import PMD file for MikuMikuDance.'
8 """
9 __author__= ["ousttrue"]
10 __version__= "0.8"
11 __url__=()
12 __bpydoc__="""
13 0.1: 20091126
14 0.2: 20091209 implement IK.
15 0.3: 20091210 implement morph target.
16 0.4: 20100305 use english name.
17 0.5: 20100408 cleanup not used vertices.
18 0.6: 20100416 fix fornt face. texture load fail safe. add progress.
19 0.7: 20100506 C extension.
20 0.8: 20100521 add shape_key group.
21 """
22 import Blender
23 from Blender import Mathutils
24 import bpy
25
26 import os
27 import sys
28 import re
29
30 # extension
31 from meshio import pmd, englishmap
32
33
34 FS_ENCODING=sys.getfilesystemencoding()
35 if os.path.exists(os.path.dirname(sys.argv[0])+"/utf8"):
36     INTERNAL_ENCODING='utf-8'
37 else:
38     INTERNAL_ENCODING=FS_ENCODING
39
40
41 MMD_SHAPE_GROUP_NAME='_MMD_SHAPE'
42
43
44 class ProgressBar(object):
45     def __init__(self, base):
46         print "#### %s ####" % base
47         self.base=base
48         self.start=Blender.sys.time() 
49         self.set('<start>', 0)
50
51     def advance(self, message, progress):
52         self.progress+=float(progress)
53         self._print(message)
54
55     def set(self, message, progress):
56         self.progress=float(progress)
57         self._print(message)
58
59     def _print(self, message):
60         print message
61         message="%s: %s" % (self.base, message)
62         if message.__class__ is unicode:
63             message=message.encode(INTERNAL_ENCODING)
64         Blender.Window.DrawProgressBar(self.progress, message)
65
66     def finish(self):
67         self.progress=1.0
68         message='finished in %.2f sec' % (Blender.sys.time()-self.start)
69         self.set(message, 1.0)
70
71 def progress_start(base):
72     global progressBar
73     progressBar=ProgressBar(base)
74
75 def progress_finish():
76     global progressBar
77     progressBar.finish()
78
79 def progress_print(message, progress=0.05):
80     global progressBar
81     progressBar.advance(message, progress)
82
83 def progress_set(message, progress):
84     global progressBar
85     progressBar.set(message, progress)
86
87
88 def convert_coord(pos):
89     """
90     Left handed y-up to Right handed z-up
91     """
92     return (pos.x, pos.z, pos.y)
93
94
95 def convert_uv(uv):
96     return (uv.x, 1.0 - uv.y)
97
98
99 def get_bone_name(l, index):
100     name=englishmap.getEnglishBoneName(l.bones[index].getName())
101     return name if name else l.bones[index].getName().encode(INTERNAL_ENCODING)
102
103
104 def createMaterial():
105     """
106     create default materil 
107     """
108     material=Blender.Material.New()
109     material.setDiffuseShader(Blender.Material.Shaders.DIFFUSE_TOON)
110     material.setRef(1)
111     material.diffuseSize = 3.14/2
112     material.setDiffuseSmooth(0)
113     material.setSpecShader(Blender.Material.Shaders.SPEC_TOON)
114     material.setSpecSize(0)
115     material.setSpec(0)
116     return material
117
118
119 def importMesh(scene, l, tex_dir):
120     """
121     @param l[in] mmd.PMDLoader
122     @param filename[in]
123     """
124
125     ############################################################
126     # shpaeキーで使われるマテリアル優先的に前に並べる
127     ############################################################
128     # shapeキーで使われる頂点インデックスを集める
129     shape_key_used_vertices=set()
130     if len(l.morph_list)>0:
131         # base 
132         base=None
133         for s in l.morph_list:
134             if s.type!=0:
135                 continue
136             base=s
137             break
138         assert(base)
139
140         for index in base.indices:
141             shape_key_used_vertices.add(index)
142
143     # マテリアルに含まれる頂点がshape_keyに含まれるか否か?
144     def isMaterialUsedInShape(offset, m):
145         for i in xrange(offset, offset+m.vertex_count): 
146             if l.indices[i] in shape_key_used_vertices:
147                 return True
148
149     # shapeキーで使われるマテリアルを記録する
150     shape_key_materials=set()
151     # 各マテリアルの開始頂点インデックスを記録する
152     face_map={}
153     face_count=0
154     for i, m in enumerate(l.materials):
155         face_map[i]=face_count
156         if isMaterialUsedInShape(face_count, m):
157             shape_key_materials.add(i)
158         face_count+=m.vertex_count
159
160     # list化
161     material_order=list(shape_key_materials)
162
163     # shapeキーに使われていないマテリアルを後ろに追加
164     for i in range(len(l.materials)):
165         if not i in material_order:
166             material_order.append(i)
167
168     # マテリアル16個ごとに分割したメッシュを作成する
169     material_offset=0
170     mesh_objects=[]
171     while material_offset<len(l.materials):
172         # create mesh
173         mesh = Blender.Mesh.New()
174         mesh.vertexUV = 1
175         # create object
176         obj = scene.objects.new(mesh)
177         obj.layers = [1]
178         mesh_objects.append(obj)
179
180         # shapeキーで使われる順に並べなおしたマテリアル16個分の
181         # メッシュを作成する
182         vertex_map=import16MaerialAndMesh(
183                 mesh, l, 
184                 material_order[material_offset:material_offset+16], 
185                 face_map, tex_dir)
186
187         # crete shape key
188         importShape(obj, l, vertex_map)
189
190         mesh.update()
191         material_offset+=16
192
193     return mesh_objects
194
195
196 def import16MaerialAndMesh(mesh, l, material_order, face_map, tex_dir):
197     ############################################################
198     # material
199     ############################################################
200     progress_print('create materials')
201     mesh_material_map={}
202     materials=[]
203     index=0
204     for material_index in material_order:
205         try:
206             m=l.materials[material_index]
207             mesh_material_map[material_index]=index
208         except KeyError:
209             break
210
211         material=createMaterial()
212         material.setRGBCol([m.diffuse.r, m.diffuse.g, m.diffuse.b])
213         material.setAlpha(m.diffuse.a)
214         material.setSpec(m.shinness*0.1)
215         material.setSpecCol([m.specular.r, m.specular.g, m.specular.b])
216         material.setMirCol([m.ambient.r, m.ambient.g, m.ambient.b])
217         material.enableSSS=True if m.flag==1 else False
218         # set texture
219         if m.getTexture()!='':
220             tex_file=re.compile('\*.*.spa$').sub('', m.getTexture())
221             tex_path = os.path.join(tex_dir, tex_file).encode(
222                     INTERNAL_ENCODING)
223             tex = Blender.Texture.New()
224             tex.setType("Image")
225             try:
226                 tex.image = Blender.Image.Load(tex_path)
227                 material.setTexture(0, tex)
228                 material.getTextures()[0].texco = Blender.Texture.TexCo.UV
229             except IOError:
230                 print material.name, "fail to load", tex_path
231         materials.append(material)
232         # lookup table for assign
233         index+=1
234     mesh.materials=materials
235
236     ############################################################
237     # vertex
238     ############################################################
239     progress_print('create vertices')
240     # create vertices
241     vertex_groups={}
242     vertices=[]
243     for v in l.each_vertex():
244         vertices.append(convert_coord(v.pos))
245         vertex_groups[v.bone0]=True
246         vertex_groups[v.bone1]=True
247     mesh.verts.extend(vertices)
248
249     # create vertex group
250     for i in vertex_groups.keys():
251         mesh.addVertGroup(get_bone_name(l, i))
252
253     # vertex params
254     for i, v, mvert in zip(xrange(len(l.vertices)), l.each_vertex(), mesh.verts):
255         mvert.no=Mathutils.Vector(*convert_coord(v.normal))
256         mvert.uvco=convert_uv(v.uv)
257         w1=float(v.weight0)/100.0
258         w2=1.0-w1
259         mesh.assignVertsToGroup(get_bone_name(l, v.bone0), [i], w1, 
260                 Blender.Mesh.AssignModes.ADD)
261         mesh.assignVertsToGroup(get_bone_name(l, v.bone1), [i], w2, 
262                 Blender.Mesh.AssignModes.ADD)    
263
264     ############################################################
265     # face
266     ############################################################
267     progress_print('create faces')
268     # create faces
269     mesh_face_indices=[]
270     mesh_face_materials=[]
271     used_vertices=set()
272
273     def degenerate(i0, i1, i2):
274         return i0==i1 or i1==i2 or i2==i0
275
276     for material_index in material_order:
277         face_offset=face_map[material_index]
278         m=l.materials[material_index]
279         material_faces=l.indices[face_offset:face_offset+m.vertex_count]
280         for j in xrange(0, len(material_faces), 3):
281             i0=material_faces[j]
282             i1=material_faces[j+1]
283             i2=material_faces[j+2]
284             triangle=[i0, i1, i2]
285             if degenerate(*triangle):
286                 continue
287             mesh_face_indices.append(triangle)
288             mesh_face_materials.append(material_index)
289             used_vertices.add(i0)
290             used_vertices.add(i1)
291             used_vertices.add(i2)
292
293     mesh.faces.extend(mesh_face_indices, ignoreDups=True)
294
295     # face params
296     used_map={}
297     mesh.addUVLayer('NewUV')
298     for face, material_index in zip(mesh.faces, mesh_face_materials):
299         try:
300             index=mesh_material_map[material_index]
301         except KeyError, message:
302             print message, mesh_material_map, m
303             assert(False)
304         face.mat=index
305         material=mesh.materials[index]
306         texture=material.getTextures()[0]
307         used_map[index]=True
308         if texture:
309             face.image=texture.tex.image
310             texture.tex.imageFlags|=Blender.Texture.ImageFlags.USEALPHA
311             face.uv=[face.verts[0].uvco, face.verts[1].uvco, face.verts[2].uvco]
312         # set smooth
313         face.smooth = 1
314     # flip
315     mesh.flipNormals()
316
317     ############################################################
318     # clean up not used vertices
319     ############################################################
320     progress_print('clean up vertices not used')
321     remove_vertices=[]
322     vertex_map={}
323     for i, v in enumerate(l.each_vertex()):
324         if i in used_vertices:
325             vertex_map[i]=len(vertex_map)
326         else:
327             remove_vertices.append(i)
328     mesh.verts.delete(remove_vertices)
329
330     progress_print('%s created' % mesh.name)
331     return vertex_map
332
333
334 def validParent(parent):
335     if parent==None:
336         return False
337     if parent.name.startswith("arm twist"):
338         return False
339     if parent.name.startswith("wrist twist"):
340         return False
341     return True
342
343
344 class Builder(object):
345     def __init__(self):
346         self.boneMap={}
347
348     def build(self, armature, bones):
349         for b in bones:
350             if not b.parent:
351                 self.__build(armature, b, None, None)
352
353     def __build(self, armature, b, p, parent):
354         name=englishmap.getEnglishBoneName(b.getName())
355         if not name:
356             name=b.getName().encode(INTERNAL_ENCODING)
357         self.boneMap[name]=b
358
359         if b.tail_index==0:
360             return
361
362         bone=Blender.Armature.Editbone()
363         bone.name=name
364         armature.bones[name] = bone
365
366         if bone.name=='center':
367             # center
368             pos=Mathutils.Vector(*convert_coord(b.pos))
369             bone.tail = pos
370             bone.head = pos-Mathutils.Vector(0, 0.01, 0)
371         else:
372             bone.head = Mathutils.Vector(*convert_coord(b.pos))
373             bone.tail = Mathutils.Vector(*convert_coord(b.tail))
374
375             if validParent(parent):
376                 bone.parent=parent
377                 if parent.tail==bone.head:
378                     bone.parent=parent
379                     bone.options=[Blender.Armature.CONNECTED]
380
381             if bone.head==bone.tail:
382                 bone.tail=bone.head-Mathutils.Vector(0, 1, 0)
383
384         for c in b.children:
385             self.__build(armature, c, b, bone)
386
387     def connect(self, armature):
388         def getKey(index):
389             for key, value in self.boneMap.items():
390                 if index==value.index:
391                     return key
392
393         for k, b in self.boneMap.items():
394             if b.tail_index==0:
395                 continue
396             bone=armature.bones[k]
397             key=getKey(b.tail_index)
398             if not key:
399                 return
400             try:
401                 tail=armature.bones[key]
402                 # connect
403                 tail.parent=bone
404                 tail.options=[Blender.Armature.CONNECTED]
405             except:
406                 pass
407
408 def importArmature(scene, l):
409     # create armature
410     armature = Blender.Armature.New()
411     # link to object
412     armature_object = scene.objects.new(armature)
413     # create action
414     act = Blender.Armature.NLA.NewAction()
415     act.setActive(armature_object)
416     # set XRAY
417     armature_object.drawMode = (
418             armature_object.drawMode | Blender.Object.DrawModes.XRAY)
419     # armature settings
420     armature.drawType = Blender.Armature.OCTAHEDRON
421     armature.envelopes = False
422     armature.vertexGroups = True
423     armature.mirrorEdit = True
424
425     # create armature
426     armature.makeEditable()
427
428     builder=Builder()
429     builder.build(armature, l.bones)
430     builder.connect(armature)
431
432     armature.update()
433
434     ############################################################
435     # IK
436     ############################################################
437     pose = armature_object.getPose()
438     cSetting = Blender.Constraint.Settings
439     for ik in l.ik_list:
440         # IKtarget->parent(=IK).name
441         target=l.bones[ik.target]
442         parent=l.bones[target.parent_index]
443         name = englishmap.getEnglishBoneName(parent.getName())
444         p_bone = pose.bones[name]
445         if not p_bone:
446             print 'not found', name
447             continue
448         if len(ik.children) >= 16:
449             print 'over MAX_CHAINLEN', ik, len(ik.children)
450             continue
451         # IK solver
452         ik_const = p_bone.constraints.append(Blender.Constraint.Type.IKSOLVER)
453         ik_const[cSetting.CHAINLEN] = len(ik.children)
454         ik_const[cSetting.TARGET] = armature_object
455
456         effector_name=englishmap.getEnglishBoneName(
457                 l.bones[ik.index].getName())
458         if not effector_name:
459             effector_name=l.bones[ik.index].getName()
460
461         ik_const[cSetting.BONE] = effector_name
462         ik_const[cSetting.ITERATIONS]=ik.iterations
463         ik_const.influence = ik.weight
464         # Limit ROT
465         #lrot_const = p_bone.constraints.append(Blender.Constraint.Type.LIMITROT)
466         #lrot_const[cSetting.OWNERSPACE] = cSetting.SPACE_LOCAL
467         #lrot_const[cSetting.LIMIT] = (cSetting.LIMIT_XROT | cSetting.LIMIT_ZROT)
468         #lrot_const[cSetting.XMIN] = 0.1
469         #lrot_const[cSetting.XMAX] = 180
470         #lrot_const[cSetting.ZMIN] = 180 - 0.1
471         #lrot_const[cSetting.ZMAX] = 0
472
473     armature.makeEditable()
474     armature.update()
475
476     return armature_object
477     
478
479 def importShape(obj, l, vertex_map):
480     if len(l.morph_list)==0:
481         return
482     obj.pinShape=True
483     mesh=obj.getData(mesh=True)
484
485     # find base 
486     base=None
487     for s in l.morph_list:
488         if s.type==0:
489             base=s
490
491             # create vertex group
492             mesh.addVertGroup(MMD_SHAPE_GROUP_NAME)
493             indices=[]
494             hasShape=False
495             for i in s.indices:
496                 if i in vertex_map:
497                     hasShape=True
498                     indices.append(vertex_map[i])
499             mesh.assignVertsToGroup(MMD_SHAPE_GROUP_NAME, indices, 0, 
500                     Blender.Mesh.AssignModes.ADD)
501             if not hasShape:
502                 return
503
504             # create base key
505             mesh.insertKey()
506             assert(len(mesh.key.blocks)==1)
507             baseShapeIndex=0
508             baseShapeBlock=mesh.key.blocks[baseShapeIndex]
509             baseShapeBlock.name='Basis'
510             obj.activeShape=baseShapeIndex
511             mesh.update()
512             break
513
514     assert(base)
515
516     # each skin
517     for s in l.morph_list:
518         if s.name==base.name:
519             continue
520
521         for index, offset in zip(s.indices, s.pos_list):
522             try:
523                 vertex_index=vertex_map[base.indices[index]]
524                 v=mesh.verts[vertex_index].co
525                 offset=convert_coord(offset)
526                 v[0]+=offset[0]
527                 v[1]+=offset[1]
528                 v[2]+=offset[2]
529             except IndexError, msg:
530                 print msg
531                 print index, len(base.indices), len(vertex_map)
532                 print len(mesh.verts)
533                 print base.indices[index]
534                 print vertex_index
535                 break
536             except KeyError:
537                 #print 'this mesh not has shape vertices'
538                 break
539
540         # get skin name
541         name=englishmap.getEnglishSkinName(s.getName())
542         if not name:
543             name=s.getName().encode(INTERNAL_ENCODING)
544             print(name)
545
546         # create shapekey block
547         mesh.insertKey()
548         shapeIndex=len(mesh.key.blocks)-1
549         keyBlock=mesh.key.blocks[shapeIndex]
550         keyBlock.name=name
551
552         # copy vertex to shape key
553         mesh.update()
554         
555         # restore
556         for mv, v in zip(mesh.verts, baseShapeBlock.getData()):
557             mv.co[0] = v[0]
558             mv.co[1] = v[1]
559             mv.co[2] = v[2]
560         mesh.update()
561
562     # select base shape
563     obj.activeShape=baseShapeIndex
564
565 def run(filename):
566     """
567     @param filename
568     """
569     filename=filename.decode(INTERNAL_ENCODING)
570     tex_dir=os.path.dirname(filename)
571
572     # progress
573     progress_start('pmd_import')
574     print(INTERNAL_ENCODING, FS_ENCODING)
575
576     # load pmd
577     progress_set('load %s' % filename, 0.0)
578
579     import locale
580     locale.setlocale(locale.LC_ALL, '')
581
582     l=pmd.IO()
583     if not l.read(filename):
584         print "fail to load %s" % filename
585         return
586     progress_set('loaded %s' % filename, 0.1)
587
588     # set object mode
589     mode_edit = Blender.Window.EditMode() 
590     if mode_edit: 
591         Blender.Window.EditMode(0)
592         
593     scene = bpy.data.scenes.active
594
595     # import objects container
596     root=scene.objects.new("Empty")
597     root.setName(
598             l.english_name if len(l.english_name)>0 else l.getName().encode(INTERNAL_ENCODING))
599
600     # import mesh
601     mesh_objects=importMesh(scene, l, tex_dir)
602     root.makeParent(mesh_objects)
603
604     # import armature
605     armature_object=importArmature(scene, l)
606     if armature_object:
607         armature = armature_object.getData()
608         armature.drawNames=True
609         root.makeParent([armature_object])
610
611         # add armature modifier
612         for o in mesh_objects:
613             mod=o.modifiers.append(Blender.Modifier.Types.ARMATURE)
614             mod[Blender.Modifier.Settings.OBJECT] = armature_object
615             mod[Blender.Modifier.Settings.ENVELOPES] = False
616             o.makeDisplayList()
617
618         ############################################################
619         # Limitation
620         ############################################################
621         for n, b in armature_object.getPose().bones.items():
622             if n.startswith("knee_"):
623                 b.lockYRot=True
624                 b.lockZRot=True
625                 b.limitX=True
626                 b.limitMin=[0, 0, 0]
627                 b.limitMax=[180, 0, 0]
628
629     # redraw
630     scene.update(0)
631
632     # restore edit mode
633     if mode_edit: 
634         Blender.Window.EditMode(1)
635
636     progress_finish()
637     Blender.Window.RedrawAll()
638
639
640 if __name__=="__main__":
641     Blender.Window.FileSelector(
642             run, 
643             'Import PMD file', 
644             Blender.sys.makename(ext='.pmd'))
645