OSDN Git Service

terminate string with '\n'.
[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.setHardness(int(m.shinness))
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         # set texture
218         if m.getTexture()!='':
219             tex_file=re.compile('\*.*.spa$').sub('', m.getTexture())
220             tex_path = os.path.join(tex_dir, tex_file).encode(
221                     INTERNAL_ENCODING)
222             tex = Blender.Texture.New()
223             tex.setType("Image")
224             try:
225                 tex.image = Blender.Image.Load(tex_path)
226                 material.setTexture(0, tex)
227                 material.getTextures()[0].texco = Blender.Texture.TexCo.UV
228             except IOError:
229                 print material.name, "fail to load", tex_path
230         materials.append(material)
231         # lookup table for assign
232         index+=1
233     mesh.materials=materials
234
235     ############################################################
236     # vertex
237     ############################################################
238     progress_print('create vertices')
239     # create vertices
240     vertex_groups={}
241     vertices=[]
242     for v in l.each_vertex():
243         vertices.append(convert_coord(v.pos))
244         vertex_groups[v.bone0]=True
245         vertex_groups[v.bone1]=True
246     mesh.verts.extend(vertices)
247
248     # create vertex group
249     for i in vertex_groups.keys():
250         mesh.addVertGroup(get_bone_name(l, i))
251
252     # vertex params
253     for i, v, mvert in zip(xrange(len(l.vertices)), l.each_vertex(), mesh.verts):
254         mvert.no=Mathutils.Vector(*convert_coord(v.normal))
255         mvert.uvco=convert_uv(v.uv)
256         w1=float(v.weight0)/100.0
257         w2=1.0-w1
258         mesh.assignVertsToGroup(get_bone_name(l, v.bone0), [i], w1, 
259                 Blender.Mesh.AssignModes.ADD)
260         mesh.assignVertsToGroup(get_bone_name(l, v.bone1), [i], w2, 
261                 Blender.Mesh.AssignModes.ADD)    
262
263     ############################################################
264     # face
265     ############################################################
266     progress_print('create faces')
267     # create faces
268     mesh_face_indices=[]
269     mesh_face_materials=[]
270     used_vertices=set()
271     for material_index in material_order:
272         face_offset=face_map[material_index]
273         m=l.materials[material_index]
274         material_faces=l.indices[face_offset:face_offset+m.vertex_count]
275         for j in xrange(0, len(material_faces), 3):
276             i0=material_faces[j]
277             i1=material_faces[j+1]
278             i2=material_faces[j+2]
279             mesh_face_indices.append([i0, i1, i2])
280             mesh_face_materials.append(material_index)
281             used_vertices.add(i0)
282             used_vertices.add(i1)
283             used_vertices.add(i2)
284     mesh.faces.extend(mesh_face_indices, ignoreDups=True)
285     assert(len(mesh.faces)==len(mesh_face_indices))
286
287     # face params
288     used_map={}
289     mesh.addUVLayer('NewUV')
290     for face, material_index in zip(mesh.faces, mesh_face_materials):
291         try:
292             index=mesh_material_map[material_index]
293         except KeyError, message:
294             print message, mesh_material_map, m
295             assert(False)
296         face.mat=index
297         material=mesh.materials[index]
298         texture=material.getTextures()[0]
299         used_map[index]=True
300         if texture:
301             face.image=texture.tex.image
302             texture.tex.imageFlags|=Blender.Texture.ImageFlags.USEALPHA
303             face.uv=[face.verts[0].uvco, face.verts[1].uvco, face.verts[2].uvco]
304         # set smooth
305         face.smooth = 1
306     # flip
307     mesh.flipNormals()
308
309     ############################################################
310     # clean up not used vertices
311     ############################################################
312     progress_print('clean up vertices not used')
313     remove_vertices=[]
314     vertex_map={}
315     for i, v in enumerate(l.each_vertex()):
316         if i in used_vertices:
317             vertex_map[i]=len(vertex_map)
318         else:
319             remove_vertices.append(i)
320     mesh.verts.delete(remove_vertices)
321
322     progress_print('%s created' % mesh.name)
323     return vertex_map
324
325
326 def build_bone(armature, b, parent=None):
327     if b.tail_index==0:
328         return
329
330     bone = Blender.Armature.Editbone()
331     name=englishmap.getEnglishBoneName(b.getName())
332     bone.name = name if name else b.getName().encode(
333             INTERNAL_ENCODING)
334     armature.bones[bone.name] = bone
335     if parent:
336         bone.head = Mathutils.Vector(*convert_coord(b.pos))
337         bone.parent=parent
338         options=[]
339         if parent.tail==bone.head:
340             options.append(Blender.Armature.CONNECTED)
341         bone.options=options
342         bone.tail = Mathutils.Vector(*convert_coord(b.tail))
343         if bone.head==bone.tail:
344             bone.tail=bone.head-Mathutils.Vector(0, 1, 0)
345     elif b.__class__ is pmd.BONE_IK:
346         bone.head = Mathutils.Vector(*convert_coord(b.pos))
347         bone.tail = Mathutils.Vector(*convert_coord(b.tail))
348     else:
349         # center
350         tail=Mathutils.Vector(*convert_coord(b.pos))
351         bone.tail = tail
352         bone.head = tail-Mathutils.Vector(0, 1, 0)
353
354     for child in b.children:
355         build_bone(armature, child, bone)
356
357
358 def importArmature(scene, l):
359     # create armature
360     armature = Blender.Armature.New()
361     # link to object
362     armature_object = scene.objects.new(armature)
363     # create action
364     act = Blender.Armature.NLA.NewAction()
365     act.setActive(armature_object)
366     # set XRAY
367     armature_object.drawMode = (
368             armature_object.drawMode | Blender.Object.DrawModes.XRAY)
369     # armature settings
370     armature.drawType = Blender.Armature.OCTAHEDRON
371     armature.envelopes = False
372     armature.vertexGroups = True
373     armature.mirrorEdit = True
374
375     # create armature
376     armature.makeEditable()
377     for b in l.bones:
378         if not b.parent:
379             build_bone(armature, b)
380     armature.update()
381
382     ############################################################
383     # IK
384     ############################################################
385     pose = armature_object.getPose()
386     cSetting = Blender.Constraint.Settings
387     for ik in l.ik_list:
388         # IKtarget->parent(=IK).name
389         target=l.bones[ik.target]
390         parent=l.bones[target.parent_index]
391         name = englishmap.getEnglishBoneName(parent.getName())
392         p_bone = pose.bones[name]
393         if not p_bone:
394             print 'not found', name
395             continue
396         if len(ik.children) >= 16:
397             print 'over MAX_CHAINLEN', ik, len(ik.children)
398             continue
399         # IK solver
400         ik_const = p_bone.constraints.append(Blender.Constraint.Type.IKSOLVER)
401         ik_const[cSetting.CHAINLEN] = len(ik.children)
402         ik_const[cSetting.TARGET] = armature_object
403         ik_const[cSetting.BONE] = englishmap.getEnglishBoneName(
404                 l.bones[ik.index].getName())
405         ik_const[cSetting.ITERATIONS]=ik.iterations
406         ik_const.influence = ik.weight
407         # Limit ROT
408         lrot_const = p_bone.constraints.append(Blender.Constraint.Type.LIMITROT)
409         lrot_const[cSetting.OWNERSPACE] = cSetting.SPACE_LOCAL
410         lrot_const[cSetting.LIMIT] = (cSetting.LIMIT_XROT | cSetting.LIMIT_ZROT)
411         lrot_const[cSetting.XMIN] = 0.1
412         lrot_const[cSetting.XMAX] = 180
413         lrot_const[cSetting.ZMIN] = 180 - 0.1
414         lrot_const[cSetting.ZMAX] = 0
415
416     armature.makeEditable()
417     armature.update()
418
419     return armature_object
420     
421
422 def importShape(obj, l, vertex_map):
423     if len(l.morph_list)==0:
424         return
425     obj.pinShape=True
426     mesh=obj.getData(mesh=True)
427
428     # find base 
429     base=None
430     for s in l.morph_list:
431         if s.type==0:
432             base=s
433
434             # create vertex group
435             mesh.addVertGroup(MMD_SHAPE_GROUP_NAME)
436             indices=[]
437             hasShape=False
438             for i in s.indices:
439                 if i in vertex_map:
440                     hasShape=True
441                     indices.append(vertex_map[i])
442             mesh.assignVertsToGroup(MMD_SHAPE_GROUP_NAME, indices, 0, 
443                     Blender.Mesh.AssignModes.ADD)
444             if not hasShape:
445                 return
446
447             # create base key
448             mesh.insertKey()
449             assert(len(mesh.key.blocks)==1)
450             baseShapeIndex=0
451             baseShapeBlock=mesh.key.blocks[baseShapeIndex]
452             baseShapeBlock.name='Basis'
453             obj.activeShape=baseShapeIndex
454             mesh.update()
455             break
456
457     assert(base)
458
459     # each skin
460     for s in l.morph_list:
461         if s.name==base.name:
462             continue
463
464         for index, offset in zip(s.indices, s.pos_list):
465             try:
466                 vertex_index=vertex_map[base.indices[index]]
467                 v=mesh.verts[vertex_index].co
468                 offset=convert_coord(offset)
469                 v[0]+=offset[0]
470                 v[1]+=offset[1]
471                 v[2]+=offset[2]
472             except IndexError, msg:
473                 print msg
474                 print index, len(base.indices), len(vertex_map)
475                 print len(mesh.verts)
476                 print base.indices[index]
477                 print vertex_index
478                 break
479             except KeyError:
480                 #print 'this mesh not has shape vertices'
481                 break
482
483         # get skin name
484         name=englishmap.getEnglishSkinName(s.getName())
485         if not name:
486             name=s.getName().encode(INTERNAL_ENCODING)
487             print(name)
488
489         # create shapekey block
490         mesh.insertKey()
491         shapeIndex=len(mesh.key.blocks)-1
492         keyBlock=mesh.key.blocks[shapeIndex]
493         keyBlock.name=name
494
495         # copy vertex to shape key
496         mesh.update()
497         
498         # restore
499         for mv, v in zip(mesh.verts, baseShapeBlock.getData()):
500             mv.co[0] = v[0]
501             mv.co[1] = v[1]
502             mv.co[2] = v[2]
503         mesh.update()
504
505     # select base shape
506     obj.activeShape=baseShapeIndex
507
508 def run(filename):
509     """
510     @param filename
511     """
512     filename=filename.decode(INTERNAL_ENCODING)
513     tex_dir=os.path.dirname(filename)
514
515     # progress
516     progress_start('pmd_import')
517     print(INTERNAL_ENCODING, FS_ENCODING)
518
519     # load pmd
520     progress_set('load %s' % filename, 0.0)
521
522     import locale
523     locale.setlocale(locale.LC_ALL, '')
524
525     l=pmd.IO()
526     if not l.read(filename):
527         print "fail to load %s" % filename
528         return
529     progress_set('loaded %s' % filename, 0.1)
530
531     # set object mode
532     mode_edit = Blender.Window.EditMode() 
533     if mode_edit: 
534         Blender.Window.EditMode(0)
535         
536     scene = bpy.data.scenes.active
537
538     # import objects container
539     root=scene.objects.new("Empty")
540     root.setName(
541             l.english_model_name if len(l.english_model_name)>0 else l.getName().encode(INTERNAL_ENCODING))
542
543     # import mesh
544     mesh_objects=importMesh(scene, l, tex_dir)
545     root.makeParent(mesh_objects)
546
547     # import armature
548     armature_object=importArmature(scene, l)
549     if armature_object:
550         armature = armature_object.getData()
551         armature.drawNames=True
552         root.makeParent([armature_object])
553
554         # add armature modifier
555         for o in mesh_objects:
556             mod=o.modifiers.append(Blender.Modifier.Types.ARMATURE)
557             mod[Blender.Modifier.Settings.OBJECT] = armature_object
558             mod[Blender.Modifier.Settings.ENVELOPES] = False
559             o.makeDisplayList()
560  
561     # redraw
562     scene.update(0)
563
564     # restore edit mode
565     if mode_edit: 
566         Blender.Window.EditMode(1)
567
568     progress_finish()
569     Blender.Window.RedrawAll()
570
571
572 if __name__=="__main__":
573     Blender.Window.FileSelector(
574             run, 
575             'Import PMD file', 
576             Blender.sys.makename(ext='.pmd'))
577