OSDN Git Service

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