OSDN Git Service

15efe5c2bc46b6cac7e3bcf50f4ba05b6c80b851
[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.7"
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 """
21 import Blender
22 from Blender import Mathutils
23 import bpy
24
25 import os
26 import sys
27 import re
28
29 # extension
30 from meshio import pmd, englishmap
31
32
33 FS_ENCODING=sys.getfilesystemencoding()
34 if os.path.exists(os.path.dirname(sys.argv[0])+"/utf8"):
35     INTERNAL_ENCODING='utf-8'
36 else:
37     INTERNAL_ENCODING=FS_ENCODING
38
39
40 class ProgressBar(object):
41     def __init__(self, base):
42         print "#### %s ####" % base
43         self.base=base
44         self.start=Blender.sys.time() 
45         self.set('<start>', 0)
46
47     def advance(self, message, progress):
48         self.progress+=float(progress)
49         self._print(message)
50
51     def set(self, message, progress):
52         self.progress=float(progress)
53         self._print(message)
54
55     def _print(self, message):
56         print message
57         message="%s: %s" % (self.base, message)
58         if message.__class__ is unicode:
59             message=message.encode(INTERNAL_ENCODING)
60         Blender.Window.DrawProgressBar(self.progress, message)
61
62     def finish(self):
63         self.progress=1.0
64         message='finished in %.2f sec' % (Blender.sys.time()-self.start)
65         self.set(message, 1.0)
66
67 def progress_start(base):
68     global progressBar
69     progressBar=ProgressBar(base)
70
71 def progress_finish():
72     global progressBar
73     progressBar.finish()
74
75 def progress_print(message, progress=0.05):
76     global progressBar
77     progressBar.advance(message, progress)
78
79 def progress_set(message, progress):
80     global progressBar
81     progressBar.set(message, progress)
82
83
84 def convert_coord(pos):
85     """
86     Left handed y-up to Right handed z-up
87     """
88     return (pos.x, pos.z, pos.y)
89
90
91 def convert_uv(uv):
92     return (uv.x, 1.0 - uv.y)
93
94
95 def get_bone_name(l, index):
96     name=englishmap.getEnglishBoneName(l.bones[index].getName())
97     return name if name else l.bones[index].getName().encode(INTERNAL_ENCODING)
98
99
100 def createMaterial():
101     """
102     create default materil 
103     """
104     material=Blender.Material.New()
105     material.setDiffuseShader(Blender.Material.Shaders.DIFFUSE_TOON)
106     material.setRef(1)
107     material.diffuseSize = 3.14/2
108     material.setDiffuseSmooth(0)
109     material.setSpecShader(Blender.Material.Shaders.SPEC_TOON)
110     material.setSpecSize(0)
111     material.setSpec(0)
112     return material
113
114
115 def importMesh(scene, l, tex_dir):
116     """
117     @param l[in] mmd.PMDLoader
118     @param filename[in]
119     """
120
121     ############################################################
122     # shpaeキーで使われるマテリアル優先的に前に並べる
123     ############################################################
124     # shapeキーで使われる頂点インデックスを集める
125     shape_key_used_vertices=set()
126     if len(l.morph_list)>0:
127         # base 
128         base=None
129         for s in l.morph_list:
130             if s.type!=0:
131                 continue
132             base=s
133             break
134         assert(base)
135
136         for index in base.indices:
137             shape_key_used_vertices.add(index)
138
139     # マテリアルに含まれる頂点がshape_keyに含まれるか否か?
140     def isMaterialUsedInShape(offset, m):
141         for i in xrange(offset, offset+m.vertex_count): 
142             if l.indices[i] in shape_key_used_vertices:
143                 return True
144
145     # shapeキーで使われるマテリアルを記録する
146     shape_key_materials=set()
147     # 各マテリアルの開始頂点インデックスを記録する
148     face_map={}
149     face_count=0
150     for i, m in enumerate(l.materials):
151         face_map[i]=face_count
152         if isMaterialUsedInShape(face_count, m):
153             shape_key_materials.add(i)
154         face_count+=m.vertex_count
155
156     # shapeキーで使われるマテリアルを前に並べるインデックスマップを作る
157     material_map={}
158     used_index=0
159     not_used_index=len(shape_key_materials)
160     for i, m in enumerate(l.materials):
161         if i in shape_key_materials:
162             material_map[i]=used_index
163             used_index+=1
164         else:
165             material_map[i]=not_used_index
166             not_used_index+=1
167
168     # マテリアル16個ごとに分割したメッシュを作成する
169     material_index=0
170     mesh_objects=[]
171     while material_index<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_index, material_map, face_map, tex_dir)
185
186         # crete shape key
187         importShape(mesh, l, vertex_map)
188
189         mesh.update()
190         material_index+=16
191
192     return mesh_objects
193
194
195 def import16MaerialAndMesh(mesh, l, 
196         material_offset, material_map, face_map, tex_dir):
197     ############################################################
198     # material
199     ############################################################
200     progress_print('create materials')
201     mesh_material_map={}
202     materials=[]
203     index=0
204     for i in xrange(material_offset, material_offset+16):
205         try:
206             material_index=material_map[i]
207             m=l.materials[material_index]
208             mesh_material_map[material_index]=index
209         except KeyError:
210             break
211
212         material=createMaterial()
213         material.setRGBCol([m.diffuse.r, m.diffuse.g, m.diffuse.b])
214         material.setAlpha(m.diffuse.a)
215         material.setHardness(int(m.shinness))
216         material.setSpecCol([m.specular.r, m.specular.g, m.specular.b])
217         material.setMirCol([m.ambient.r, m.ambient.g, m.ambient.b])
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     for i in xrange(material_offset, material_offset+16):
273         try:
274             material_index=material_map[i]
275         except KeyError:
276             break
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             mesh_face_indices.append([i0, i1, i2])
285             mesh_face_materials.append(material_index)
286             used_vertices.add(i0)
287             used_vertices.add(i1)
288             used_vertices.add(i2)
289     mesh.faces.extend(mesh_face_indices, ignoreDups=True)
290     assert(len(mesh.faces)==len(mesh_face_indices))
291
292     # face params
293     used_map={}
294     mesh.addUVLayer('NewUV')
295     for face, material_index in zip(mesh.faces, mesh_face_materials):
296         try:
297             index=mesh_material_map[material_index]
298         except KeyError, message:
299             print message, mesh_material_map, m
300             assert(False)
301         face.mat=index
302         material=mesh.materials[index]
303         texture=material.getTextures()[0]
304         used_map[index]=True
305         if texture:
306             face.image=texture.tex.image
307             texture.tex.imageFlags|=Blender.Texture.ImageFlags.USEALPHA
308             face.uv=[face.verts[0].uvco, face.verts[1].uvco, face.verts[2].uvco]
309         # set smooth
310         face.smooth = 1
311     # flip
312     mesh.flipNormals()
313
314     ############################################################
315     # clean up not used vertices
316     ############################################################
317     progress_print('clean up vertices not used')
318     remove_vertices=[]
319     vertex_map={}
320     for i, v in enumerate(l.each_vertex()):
321         if i in used_vertices:
322             vertex_map[i]=len(vertex_map)
323         else:
324             remove_vertices.append(i)
325     mesh.verts.delete(remove_vertices)
326
327     progress_print('%s created' % mesh.name)
328     return vertex_map
329
330
331 def build_bone(armature, b, parent=None):
332     if b.tail_index==0:
333         return
334
335     bone = Blender.Armature.Editbone()
336     name=englishmap.getEnglishBoneName(b.getName())
337     bone.name = name if name else b.getName().encode(
338             INTERNAL_ENCODING)
339     armature.bones[bone.name] = bone
340     if parent:
341         bone.head = Mathutils.Vector(*convert_coord(b.pos))
342         bone.parent=parent
343         options=[]
344         if parent.tail==bone.head:
345             options.append(Blender.Armature.CONNECTED)
346         bone.options=options
347         bone.tail = Mathutils.Vector(*convert_coord(b.tail))
348         if bone.head==bone.tail:
349             bone.tail=bone.head-Mathutils.Vector(0, 1, 0)
350     elif b.__class__ is pmd.BONE_IK:
351         bone.head = Mathutils.Vector(*convert_coord(b.pos))
352         bone.tail = Mathutils.Vector(*convert_coord(b.tail))
353     else:
354         # center
355         tail=Mathutils.Vector(*convert_coord(b.pos))
356         bone.tail = tail
357         bone.head = tail-Mathutils.Vector(0, 1, 0)
358
359     for child in b.children:
360         build_bone(armature, child, bone)
361
362
363 def importArmature(scene, l):
364     # create armature
365     armature = Blender.Armature.New()
366     # link to object
367     armature_object = scene.objects.new(armature)
368     # create action
369     act = Blender.Armature.NLA.NewAction()
370     act.setActive(armature_object)
371     # set XRAY
372     armature_object.drawMode = (
373             armature_object.drawMode | Blender.Object.DrawModes.XRAY)
374     # armature settings
375     armature.drawType = Blender.Armature.OCTAHEDRON
376     armature.envelopes = False
377     armature.vertexGroups = True
378     armature.mirrorEdit = True
379
380     # create armature
381     armature.makeEditable()
382     for b in l.bones:
383         if not b.parent:
384             build_bone(armature, b)
385     armature.update()
386
387     ############################################################
388     # IK
389     ############################################################
390     pose = armature_object.getPose()
391     cSetting = Blender.Constraint.Settings
392     for ik in l.ik_list:
393         # IKtarget->parent(=IK).name
394         target=l.bones[ik.target]
395         parent=l.bones[target.parent_index]
396         name = englishmap.getEnglishBoneName(parent.getName())
397         p_bone = pose.bones[name]
398         if not p_bone:
399             print 'not found', name
400             continue
401         if len(ik.children) >= 16:
402             print 'over MAX_CHAINLEN', ik, len(ik.children)
403             continue
404         ik_const = p_bone.constraints.append(Blender.Constraint.Type.IKSOLVER)
405         ik_const[cSetting.CHAINLEN] = len(ik.children)
406         ik_const[cSetting.TARGET] = armature_object
407         ik_const[cSetting.BONE] = englishmap.getEnglishBoneName(
408                 l.bones[ik.index].getName())
409         lrot_const = p_bone.constraints.append(Blender.Constraint.Type.LIMITROT)
410         lrot_const.influence = ik.weight
411         lrot_const[cSetting.OWNERSPACE] = cSetting.SPACE_LOCAL
412         lrot_const[cSetting.LIMIT] = (cSetting.LIMIT_XROT | cSetting.LIMIT_ZROT)
413         lrot_const[cSetting.XMIN] = ik.iterations
414         lrot_const[cSetting.XMAX] = 180
415         lrot_const[cSetting.ZMIN] = 180 - ik.iterations
416         lrot_const[cSetting.ZMAX] = 0
417
418     armature.makeEditable()
419     armature.update()
420
421     return armature_object
422     
423
424 def importShape(mesh, l, vertex_map):
425     if len(l.morph_list)==0:
426         return
427     # base 
428     base=None
429     for s in l.morph_list:
430         if s.type!=0:
431             continue
432         base=s
433         break
434     assert(base)
435     mesh.insertKey()
436     baseblock=mesh.key.blocks[-1]
437     ipo=Blender.Ipo.New('Key', 'pmd')
438     mesh.key.ipo=ipo
439
440     # each skin
441     for s in l.morph_list:
442         if s.name==base.name:
443             continue
444         name=englishmap.getEnglishSkinName(s.getName())
445         if not name:
446             name=s.getName().encode(INTERNAL_ENCODING)
447         for index, offset in zip(s.indices, s.pos_list):
448             try:
449                 vertex_index=vertex_map[base.indices[index]]
450                 v=mesh.verts[vertex_index].co
451                 offset=convert_coord(offset)
452                 v[0]+=offset[0]
453                 v[1]+=offset[1]
454                 v[2]+=offset[2]
455             except IndexError, msg:
456                 print IndexError, msg
457                 print index, len(base.indices)
458                 print vertex_index, len(mesh.verts)
459                 print base.indices[index]
460                 break
461             except KeyError:
462                 #print 'this mesh not has shape vertices'
463                 break
464         
465         # set shapekey block
466         mesh.update()
467         mesh.insertKey()
468         mesh.key.blocks[-1].name=name
469
470         # set ipo curve
471         icu=ipo.addCurve(name)
472         icu.interpolation = Blender.IpoCurve.InterpTypes.LINEAR
473         icu.append( (0.0, 0.0) )
474         
475         # restore
476         for mv, v in zip(mesh.verts, baseblock.getData()):
477             mv.co[0] = v[0]
478             mv.co[1] = v[1]
479             mv.co[2] = v[2]
480         mesh.update()
481
482
483 def run(filename):
484     """
485     @param filename
486     """
487     filename=filename.decode(INTERNAL_ENCODING)
488     tex_dir=os.path.dirname(filename)
489
490     # progress
491     progress_start('pmd_import')
492     print(INTERNAL_ENCODING, FS_ENCODING)
493
494     # load pmd
495     progress_set('load %s' % filename, 0.0)
496
497     import locale
498     locale.setlocale(locale.LC_ALL, '')
499
500     l=pmd.IO()
501     if not l.read(filename):
502         print "fail to load %s" % filename
503         return
504     progress_set('loaded %s' % filename, 0.1)
505
506     # set object mode
507     mode_edit = Blender.Window.EditMode() 
508     if mode_edit: 
509         Blender.Window.EditMode(0)
510         
511     scene = bpy.data.scenes.active
512
513     # import objects container
514     root=scene.objects.new("Empty")
515     root.setName(
516             l.english_model_name if len(l.english_model_name)>0 else l.getName().encode(INTERNAL_ENCODING))
517
518     # import mesh
519     mesh_objects=importMesh(scene, l, tex_dir)
520     root.makeParent(mesh_objects)
521
522     # import armature
523     armature_object=importArmature(scene, l)
524     if armature_object:
525         armature = armature_object.getData()
526         armature.drawNames=True
527         root.makeParent([armature_object])
528
529         # add armature modifier
530         for o in mesh_objects:
531             mod=o.modifiers.append(Blender.Modifier.Types.ARMATURE)
532             mod[Blender.Modifier.Settings.OBJECT] = armature_object
533             mod[Blender.Modifier.Settings.ENVELOPES] = False
534             o.makeDisplayList()
535  
536     # redraw
537     scene.update(0)
538
539     # restore edit mode
540     if mode_edit: 
541         Blender.Window.EditMode(1)
542
543     progress_finish()
544     Blender.Window.RedrawAll()
545
546
547 if __name__=="__main__":
548     Blender.Window.FileSelector(
549             run, 
550             'Import PMD file', 
551             Blender.sys.makename(ext='.pmd'))
552