OSDN Git Service

fix material.
[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 build_bone(armature, b, parent=None):
335     if b.tail_index==0:
336         return
337
338     bone = Blender.Armature.Editbone()
339     name=englishmap.getEnglishBoneName(b.getName())
340     bone.name = name if name else b.getName().encode(
341             INTERNAL_ENCODING)
342     armature.bones[bone.name] = bone
343     if parent:
344         bone.head = Mathutils.Vector(*convert_coord(b.pos))
345         bone.parent=parent
346         options=[]
347         if parent.tail==bone.head:
348             options.append(Blender.Armature.CONNECTED)
349         bone.options=options
350         bone.tail = Mathutils.Vector(*convert_coord(b.tail))
351         if bone.head==bone.tail:
352             bone.tail=bone.head-Mathutils.Vector(0, 1, 0)
353     elif b.__class__ is pmd.BONE_IK:
354         bone.head = Mathutils.Vector(*convert_coord(b.pos))
355         bone.tail = Mathutils.Vector(*convert_coord(b.tail))
356     else:
357         # center
358         tail=Mathutils.Vector(*convert_coord(b.pos))
359         bone.tail = tail
360         bone.head = tail-Mathutils.Vector(0, 1, 0)
361
362     for child in b.children:
363         build_bone(armature, child, bone)
364
365
366 def importArmature(scene, l):
367     # create armature
368     armature = Blender.Armature.New()
369     # link to object
370     armature_object = scene.objects.new(armature)
371     # create action
372     act = Blender.Armature.NLA.NewAction()
373     act.setActive(armature_object)
374     # set XRAY
375     armature_object.drawMode = (
376             armature_object.drawMode | Blender.Object.DrawModes.XRAY)
377     # armature settings
378     armature.drawType = Blender.Armature.OCTAHEDRON
379     armature.envelopes = False
380     armature.vertexGroups = True
381     armature.mirrorEdit = True
382
383     # create armature
384     armature.makeEditable()
385     for b in l.bones:
386         if not b.parent:
387             build_bone(armature, b)
388     armature.update()
389
390     ############################################################
391     # IK
392     ############################################################
393     pose = armature_object.getPose()
394     cSetting = Blender.Constraint.Settings
395     for ik in l.ik_list:
396         # IKtarget->parent(=IK).name
397         target=l.bones[ik.target]
398         parent=l.bones[target.parent_index]
399         name = englishmap.getEnglishBoneName(parent.getName())
400         p_bone = pose.bones[name]
401         if not p_bone:
402             print 'not found', name
403             continue
404         if len(ik.children) >= 16:
405             print 'over MAX_CHAINLEN', ik, len(ik.children)
406             continue
407         # IK solver
408         ik_const = p_bone.constraints.append(Blender.Constraint.Type.IKSOLVER)
409         ik_const[cSetting.CHAINLEN] = len(ik.children)
410         ik_const[cSetting.TARGET] = armature_object
411         ik_const[cSetting.BONE] = englishmap.getEnglishBoneName(
412                 l.bones[ik.index].getName())
413         ik_const[cSetting.ITERATIONS]=ik.iterations
414         ik_const.influence = ik.weight
415         # Limit ROT
416         lrot_const = p_bone.constraints.append(Blender.Constraint.Type.LIMITROT)
417         lrot_const[cSetting.OWNERSPACE] = cSetting.SPACE_LOCAL
418         lrot_const[cSetting.LIMIT] = (cSetting.LIMIT_XROT | cSetting.LIMIT_ZROT)
419         lrot_const[cSetting.XMIN] = 0.1
420         lrot_const[cSetting.XMAX] = 180
421         lrot_const[cSetting.ZMIN] = 180 - 0.1
422         lrot_const[cSetting.ZMAX] = 0
423
424     armature.makeEditable()
425     armature.update()
426
427     return armature_object
428     
429
430 def importShape(obj, l, vertex_map):
431     if len(l.morph_list)==0:
432         return
433     obj.pinShape=True
434     mesh=obj.getData(mesh=True)
435
436     # find base 
437     base=None
438     for s in l.morph_list:
439         if s.type==0:
440             base=s
441
442             # create vertex group
443             mesh.addVertGroup(MMD_SHAPE_GROUP_NAME)
444             indices=[]
445             hasShape=False
446             for i in s.indices:
447                 if i in vertex_map:
448                     hasShape=True
449                     indices.append(vertex_map[i])
450             mesh.assignVertsToGroup(MMD_SHAPE_GROUP_NAME, indices, 0, 
451                     Blender.Mesh.AssignModes.ADD)
452             if not hasShape:
453                 return
454
455             # create base key
456             mesh.insertKey()
457             assert(len(mesh.key.blocks)==1)
458             baseShapeIndex=0
459             baseShapeBlock=mesh.key.blocks[baseShapeIndex]
460             baseShapeBlock.name='Basis'
461             obj.activeShape=baseShapeIndex
462             mesh.update()
463             break
464
465     assert(base)
466
467     # each skin
468     for s in l.morph_list:
469         if s.name==base.name:
470             continue
471
472         for index, offset in zip(s.indices, s.pos_list):
473             try:
474                 vertex_index=vertex_map[base.indices[index]]
475                 v=mesh.verts[vertex_index].co
476                 offset=convert_coord(offset)
477                 v[0]+=offset[0]
478                 v[1]+=offset[1]
479                 v[2]+=offset[2]
480             except IndexError, msg:
481                 print msg
482                 print index, len(base.indices), len(vertex_map)
483                 print len(mesh.verts)
484                 print base.indices[index]
485                 print vertex_index
486                 break
487             except KeyError:
488                 #print 'this mesh not has shape vertices'
489                 break
490
491         # get skin name
492         name=englishmap.getEnglishSkinName(s.getName())
493         if not name:
494             name=s.getName().encode(INTERNAL_ENCODING)
495             print(name)
496
497         # create shapekey block
498         mesh.insertKey()
499         shapeIndex=len(mesh.key.blocks)-1
500         keyBlock=mesh.key.blocks[shapeIndex]
501         keyBlock.name=name
502
503         # copy vertex to shape key
504         mesh.update()
505         
506         # restore
507         for mv, v in zip(mesh.verts, baseShapeBlock.getData()):
508             mv.co[0] = v[0]
509             mv.co[1] = v[1]
510             mv.co[2] = v[2]
511         mesh.update()
512
513     # select base shape
514     obj.activeShape=baseShapeIndex
515
516 def run(filename):
517     """
518     @param filename
519     """
520     filename=filename.decode(INTERNAL_ENCODING)
521     tex_dir=os.path.dirname(filename)
522
523     # progress
524     progress_start('pmd_import')
525     print(INTERNAL_ENCODING, FS_ENCODING)
526
527     # load pmd
528     progress_set('load %s' % filename, 0.0)
529
530     import locale
531     locale.setlocale(locale.LC_ALL, '')
532
533     l=pmd.IO()
534     if not l.read(filename):
535         print "fail to load %s" % filename
536         return
537     progress_set('loaded %s' % filename, 0.1)
538
539     # set object mode
540     mode_edit = Blender.Window.EditMode() 
541     if mode_edit: 
542         Blender.Window.EditMode(0)
543         
544     scene = bpy.data.scenes.active
545
546     # import objects container
547     root=scene.objects.new("Empty")
548     root.setName(
549             l.english_name if len(l.english_name)>0 else l.getName().encode(INTERNAL_ENCODING))
550
551     # import mesh
552     mesh_objects=importMesh(scene, l, tex_dir)
553     root.makeParent(mesh_objects)
554
555     # import armature
556     armature_object=importArmature(scene, l)
557     if armature_object:
558         armature = armature_object.getData()
559         armature.drawNames=True
560         root.makeParent([armature_object])
561
562         # add armature modifier
563         for o in mesh_objects:
564             mod=o.modifiers.append(Blender.Modifier.Types.ARMATURE)
565             mod[Blender.Modifier.Settings.OBJECT] = armature_object
566             mod[Blender.Modifier.Settings.ENVELOPES] = False
567             o.makeDisplayList()
568  
569     # redraw
570     scene.update(0)
571
572     # restore edit mode
573     if mode_edit: 
574         Blender.Window.EditMode(1)
575
576     progress_finish()
577     Blender.Window.RedrawAll()
578
579
580 if __name__=="__main__":
581     Blender.Window.FileSelector(
582             run, 
583             'Import PMD file', 
584             Blender.sys.makename(ext='.pmd'))
585