OSDN Git Service

408e84bdad6d8ba8ec86cc80a3beb5cc254b439c
[meshio/meshio.git] / swig / blender / 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__= "1.1"
11 __url__=()
12 __bpydoc__="""
13 pmd Importer
14
15 This script imports a pmd into Blender for editing.
16
17 0.1: 20091126: first implement.
18 0.2: 20091209: implement IK.
19 0.3: 20091210: implement morph target.
20 0.4: 20100305: use english name.
21 0.5: 20100408: cleanup not used vertices.
22 0.6: 20100416: fix fornt face. texture load fail safe. add progress.
23 0.7: 20100506: C extension.
24 0.8: 20100521: add shape_key group.
25 1.0: 20100530: add invisilbe bone tail(armature layer 2).
26 1.1: 20100608: integrate 2.4 and 2.5.
27 """
28
29 MMD_SHAPE_GROUP_NAME='_MMD_SHAPE'
30 BASE_SHAPE_NAME='Basis'
31
32
33 ###############################################################################
34 # import
35 ###############################################################################
36 import os
37 import sys
38 import re
39 import math
40
41 # C extension
42 from meshio import pmd, englishmap
43
44 def isBlender24():
45     return sys.version_info[0]<3
46
47 if isBlender24():
48     # for 2.4
49     import Blender
50     from Blender import Mathutils
51     import bpy
52
53     # wrapper
54     import bl24 as bl
55 else:
56     # for 2.5
57     import bpy
58     from bpy.props import *
59     import mathutils
60
61     # wrapper
62     import bl25 as bl
63
64     xrange=range
65
66 ###############################################################################
67 # progress bar
68 ###############################################################################
69 def progress_start(base):
70     global progressBar
71     progressBar=bl.ProgressBar(base)
72
73 def progress_finish():
74     global progressBar
75     progressBar.finish()
76
77 def progress_print(message, progress=0.05):
78     global progressBar
79     progressBar.advance(message, progress)
80
81 def progress_set(message, progress):
82     global progressBar
83     progressBar.set(message, progress)
84
85
86 ###############################################################################
87 def convert_coord(pos):
88     """
89     Left handed y-up to Right handed z-up
90     """
91     return (pos.x, pos.z, pos.y)
92
93
94 def convert_uv(uv):
95     return (uv.x, 1.0 - uv.y)
96
97
98 def to_radian(degree):
99     return math.pi * degree / 180
100
101
102 def get_bone_name(l, index):
103     name=englishmap.getEnglishBoneName(l.bones[index].getName())
104     return name if name else l.bones[index].getName()
105
106 def __importShape(obj, l, vertex_map):
107     if len(l.morph_list)==0:
108         return
109
110     # set shape_key pin
111     bl.objectPinShape(obj)
112
113     # find base 
114     base=None
115     for s in l.morph_list:
116         if s.type==0:
117             base=s
118
119             # create vertex group
120             bl.meshAddVertexGroup(obj, MMD_SHAPE_GROUP_NAME)
121             hasShape=False
122             for i in s.indices:
123                 if i in vertex_map:
124                     hasShape=True
125                     bl.meshAssignVertexGroup(
126                             obj, MMD_SHAPE_GROUP_NAME, vertex_map[i], 0)
127             if not hasShape:
128                 return
129     assert(base)
130
131     # create base key
132     baseShapeBlock=bl.objectAddShapeKey(obj, BASE_SHAPE_NAME)
133     # mesh
134     mesh=bl.objectGetData(obj)
135     mesh.update()
136
137     # each skin
138     for s in l.morph_list:
139         if s.type==0:
140             continue
141
142         # name
143         name=englishmap.getEnglishSkinName(s.getName())
144         if not name:
145             name=s.getName()
146
147         if isBlender24():
148             # 24
149             for index, offset in zip(s.indices, s.pos_list):
150                 try:
151                     vertex_index=vertex_map[base.indices[index]]
152                     v=mesh.verts[vertex_index].co
153                     offset=convert_coord(offset)
154                     v[0]+=offset[0]
155                     v[1]+=offset[1]
156                     v[2]+=offset[2]
157                 except IndexError as msg:
158                     print(msg)
159                     print(index, len(base.indices), len(vertex_map))
160                     print(len(mesh.verts))
161                     print(base.indices[index])
162                     print(vertex_index)
163                     break
164                 except KeyError:
165                     #print 'this mesh not has shape vertices'
166                     break
167
168             # create shapekey block
169             new_shape_key=bl.objectAddShapeKey(obj, name)
170
171             # copy vertex to shape key
172             mesh.update()
173             
174             # restore
175             for mv, v in zip(mesh.verts, baseShapeBlock.getData()):
176                 mv.co[0] = v[0]
177                 mv.co[1] = v[1]
178                 mv.co[2] = v[2]
179             mesh.update()
180
181         else:
182             # 25
183             new_shape_key=bl.objectAddShapeKey(obj, name)
184
185             for index, offset in zip(s.indices, s.pos_list):
186                 try:
187                     vertex_index=base.indices[index]
188                     bl.shapeKeyAssign(new_shape_key, vertex_index,
189                             mesh.verts[vertex_index].co+
190                             bl.createVector(*convert_coord(offset)))
191                 except IndexError as msg:
192                     print(msg)
193                     print(index, len(base.indices), len(vertex_map))
194                     print(len(mesh.verts))
195                     print(base.indices[index])
196                     print(vertex_index)
197                     break
198                 except KeyError:
199                     #print 'this mesh not has shape vertices'
200                     break
201
202     # select base shape
203     bl.objectActivateShapeKey(obj, 0)
204
205
206 def __build(armature, b, p, parent):
207     name=englishmap.getEnglishBoneName(b.getName())
208     if not name:
209         name=b.getName().encode(bl.INTERNAL_ENCODING)
210
211     bone=bl.createArmatureBone(armature, name)
212
213     if b.tail_index==0:
214         # 先端
215         assert(b.type==6 or b.type==7)
216         bone.head = bl.createVector(*convert_coord(b.pos))
217         bone.tail=bone.head+bl.createVector(0, 1, 0)
218         assert(parent)
219         bone.parent=parent
220         if bone.name=="center_t":
221             # センターボーンは(0, 1, 0)の方向を向いていないと具合が悪い
222             parent.tail=parent.head+bl.createVector(0, 1, 0)
223             bone.head=parent.tail
224             bone.tail=bone.head+bl.createVector(0, 1, 0)
225         else:
226             assert(parent.tail==bone.head)
227         bl.boneSetConnected(bone)
228         # armature layer 2
229         bl.boneLayerMask(bone, [0, 1])
230     else:
231         bone.head = bl.createVector(*convert_coord(b.pos))
232         bone.tail = bl.createVector(*convert_coord(b.tail))
233         if parent:
234             bone.parent=parent
235             if parent.tail==bone.head:
236                 bl.boneSetConnected(bone)
237
238     if bone.head==bone.tail:
239         bone.tail=bone.head+bl.createVector(0, 1, 0)
240
241     for c in b.children:
242         __build(armature, c, b, bone)
243
244
245 def __importArmature(scene, l):
246     # build bone
247     armature, armature_object=bl.createArmature(scene)
248     bl.armatureMakeEditable(scene, armature_object)
249     for b in l.bones:
250         if not b.parent:
251             __build(armature, b, None, None)
252     bl.armatureUpdate(armature)
253     bl.exitEditMode()
254
255     # IK constraint
256     pose = bl.objectGetPose(armature_object)
257     for ik in l.ik_list:
258         target=l.bones[ik.target]
259         name = englishmap.getEnglishBoneName(target.getName())
260         p_bone = pose.bones[name]
261         if not p_bone:
262             print('not found', name)
263             continue
264         if len(ik.children) >= 16:
265             print('over MAX_CHAINLEN', ik, len(ik.children))
266             continue
267         effector_name=englishmap.getEnglishBoneName(
268                 l.bones[ik.index].getName())
269         if not effector_name:
270             effector_name=l.bones[ik.index].getName()
271
272         constraint=bl.createIkConstraint(armature_object, 
273                 p_bone, effector_name, ik)
274
275     bl.armatureMakeEditable(scene, armature_object)
276     bl.armatureUpdate(armature)
277     bl.exitEditMode()
278
279     return armature_object
280         
281
282 def __import16MaerialAndMesh(meshObject, l, 
283         material_order, face_map, tex_dir):
284
285     mesh=bl.objectGetData(meshObject)
286     ############################################################
287     # material
288     ############################################################
289     progress_print('create materials')
290     mesh_material_map={}
291     textureMap={}
292     imageMap={}
293     index=0
294
295     for material_index in material_order:
296         try:
297             m=l.materials[material_index]
298             mesh_material_map[material_index]=index
299         except KeyError:
300             break
301
302         material=bl.createPmdMaterial(m)
303
304         texture_name=m.getTexture()
305         #tex_file=re.compile('\*.*.spa$').sub('', m.getTexture())
306         #tex_path = os.path.join(tex_dir, tex_file).encode(
307         #        bl.INTERNAL_ENCODING)
308         if texture_name!='':
309             if texture_name in textureMap:
310                 texture=textureMap[texture_name]
311             else:
312                 try:
313                     texture, image=bl.createTexture(
314                             os.path.join(tex_dir, texture_name))
315                     textureMap[texture_name]=texture
316                     imageMap[material_index]=image
317                 except:
318                     continue
319             bl.materialAddTexture(material, texture)
320         bl.meshAddMaterial(mesh, material)
321         index+=1
322
323     ############################################################
324     # vertex
325     ############################################################
326     progress_print('create vertices')
327     # create vertices
328     vertices=[]
329     if isBlender24():
330         for v in l.each_vertex():
331             vertices.append(convert_coord(v.pos))
332     else:
333         for v in l.each_vertex():
334             vertices.extend(convert_coord(v.pos))
335
336     ############################################################
337     # face
338     ############################################################
339     progress_print('create faces')
340     # create faces
341     mesh_face_indices=[]
342     mesh_face_materials=[]
343     used_vertices=set()
344
345     for material_index in material_order:
346         face_offset=face_map[material_index]
347         m=l.materials[material_index]
348         material_faces=l.indices[face_offset:face_offset+m.vertex_count]
349
350         def degenerate(i0, i1, i2):
351             """
352             縮退しているか?
353             """
354             return i0==i1 or i1==i2 or i2==i0
355
356         for j in xrange(0, len(material_faces), 3):
357             i0=material_faces[j]
358             i1=material_faces[j+1]
359             i2=material_faces[j+2]
360             if i2==0:
361                 triangle=[i2, i0, i1]
362             else:
363                 triangle=[i0, i1, i2]
364             if degenerate(*triangle):
365                 continue
366             if isBlender24():
367                 mesh_face_indices.append(triangle[0:3])
368             else:
369                 mesh_face_indices.extend(
370                         [triangle[0], triangle[1], triangle[2], 0])
371             mesh_face_materials.append(material_index)
372             used_vertices.add(i0)
373             used_vertices.add(i1)
374             used_vertices.add(i2)
375
376     ############################################################
377     # create vertices & faces
378     ############################################################
379     bl.meshCreateVerteicesAndFaces(mesh, vertices, mesh_face_indices)
380
381     ############################################################
382     # vertex bone weight
383     ############################################################
384     # create vertex group
385     vertex_groups={}
386     for v in l.each_vertex():
387         vertex_groups[v.bone0]=True
388         vertex_groups[v.bone1]=True
389     for i in vertex_groups.keys():
390         bl.meshAddVertexGroup(meshObject, get_bone_name(l, i))
391
392     # vertex params
393     bl.meshUseVertexUv(mesh)
394     for i, v, mvert in zip(xrange(len(l.vertices)), 
395         l.each_vertex(), mesh.verts):
396         # normal, uv
397         bl.vertexSetNormal(mvert, convert_coord(v.normal))
398         bl.vertexSetUv(mvert, convert_uv(v.uv))
399         # bone weight
400         w1=float(v.weight0)/100.0
401         w2=1.0-w1
402         bl.meshAssignVertexGroup(meshObject, get_bone_name(l, v.bone0),
403             i,  w1)
404         bl.meshAssignVertexGroup(meshObject, get_bone_name(l, v.bone1),
405             i,  w2)
406
407     ############################################################
408     # face params
409     ############################################################
410     used_map={}
411     bl.meshAddUV(mesh)
412
413     if isBlender24():
414         for face, material_index in zip(mesh.faces, mesh_face_materials):
415             try:
416                 index=mesh_material_map[material_index]
417             except KeyError as message:
418                 print(message, mesh_material_map, m)
419                 assert(False)
420             face.mat=index
421             material=mesh.materials[index]
422             texture=material.getTextures()[0]
423             used_map[index]=True
424             if texture:
425                 face.image=texture.tex.image
426                 texture.tex.imageFlags|=Blender.Texture.ImageFlags.USEALPHA
427                 face.uv=[
428                     face.verts[0].uvco, face.verts[1].uvco, face.verts[2].uvco]
429             # set smooth
430             face.smooth = 1
431         # flip
432         mesh.flipNormals()
433     else:
434         for face, uv_face, material_index in zip(mesh.faces, 
435                 mesh.uv_textures[0].data,
436                 mesh_face_materials,
437                 ):
438             try:
439                 index=mesh_material_map[material_index]
440             except KeyError as message:
441                 print(message, mesh_material_map, m)
442                 assert(False)
443             face.material_index=index
444             material=mesh.materials[index]
445             used_map[index]=True
446             if material.texture_slots[0]:
447                 uv=l.getUV(face.verts[0])
448                 uv_face.uv1=[uv.x, 1.0-uv.y]
449
450                 uv=l.getUV(face.verts[1])
451                 uv_face.uv2=[uv.x, 1.0-uv.y]
452
453                 uv=l.getUV(face.verts[2])
454                 uv_face.uv3=[uv.x, 1.0-uv.y]
455             if face.material_index in imageMap:
456                 uv_face.image=imageMap[face.material_index]
457                 uv_face.tex=True
458
459             # set smooth
460             face.smooth = 1
461     mesh.update()
462
463     ############################################################
464     # clean up not used vertices
465     ############################################################
466     progress_print('clean up vertices not used')
467     remove_vertices=[]
468     vertex_map={}
469     for i, v in enumerate(l.each_vertex()):
470         if i in used_vertices:
471             vertex_map[i]=len(vertex_map)
472         else:
473             remove_vertices.append(i)
474
475     bl.meshVertsDelete(mesh, remove_vertices)
476
477     progress_print('%s created' % mesh.name)
478     return vertex_map
479
480
481 def __importMesh(scene, io, tex_dir):
482     """
483     @param l[in] mmd.PMDLoader
484     @param filename[in]
485     """
486     ############################################################
487     # shpaeキーで使われるマテリアル優先的に前に並べる
488     ############################################################
489     # shapeキーで使われる頂点インデックスを集める
490     shape_key_used_vertices=set()
491     if len(io.morph_list)>0:
492         # base 
493         base=None
494         for s in io.morph_list:
495             if s.type!=0:
496                 continue
497             base=s
498             break
499         assert(base)
500
501         for index in base.indices:
502             shape_key_used_vertices.add(index)
503
504     # マテリアルに含まれる頂点がshape_keyに含まれるか否か?
505     def isMaterialUsedInShape(offset, m):
506         for i in xrange(offset, offset+m.vertex_count): 
507             if io.indices[i] in shape_key_used_vertices:
508                 return True
509
510     # shapeキーで使われるマテリアルを記録する
511     shape_key_materials=set()
512     # 各マテリアルの開始頂点インデックスを記録する
513     face_map={}
514     face_count=0
515     for i, m in enumerate(io.materials):
516         face_map[i]=face_count
517         if isMaterialUsedInShape(face_count, m):
518             shape_key_materials.add(i)
519         face_count+=m.vertex_count
520
521     # list化
522     material_order=list(shape_key_materials)
523
524     # shapeキーに使われていないマテリアルを後ろに追加
525     for i in range(len(io.materials)):
526         if not i in material_order:
527             material_order.append(i)
528
529     # マテリアル16個ごとに分割したメッシュを作成する
530     material_offset=0
531     mesh_objects=[]
532     while material_offset<len(io.materials):
533         mesh, meshObject=bl.createMesh(scene, 'mesh')
534         # create object
535         #meshObject.layers = [1]
536         mesh_objects.append(meshObject)
537
538         # activate object
539         bl.objectDeselectAll()
540         bl.objectActivate(scene, meshObject)
541
542         # shapeキーで使われる順に並べなおしたマテリアル16個分の
543         # メッシュを作成する
544         vertex_map=__import16MaerialAndMesh(
545                 meshObject, io, 
546                 material_order[material_offset:material_offset+16], 
547                 face_map, tex_dir)
548
549         # enter Edit Mode
550         bl.enterEditMode()
551
552         # crete shape key
553         __importShape(meshObject, io, vertex_map)
554
555         # exit Edit Mode
556         bl.exitEditMode()
557
558         mesh.update()
559         material_offset+=16
560
561     return mesh_objects
562
563
564 def __execute(filename, scene):
565     """
566     load pmd file to context.
567     """
568     # load pmd
569     progress_set('load %s' % filename, 0.0)
570
571     io=pmd.IO()
572     if not io.read(filename):
573         print("fail to load %s" % filename)
574         return
575     progress_set('loaded %s' % filename, 0.1)
576
577     # create root object
578     root=bl.createEmptyObject(scene, 
579             io.english_name if len(io.english_name)>0 else io.getName().encode(bl.INTERNAL_ENCODING))
580
581     # import mesh
582     mesh_objects=__importMesh(scene, io, os.path.dirname(filename))
583     for o in mesh_objects:
584         bl.objectMakeParent(root, o)
585
586     # import armature
587     armature_object=__importArmature(scene, io)
588     if armature_object:
589         bl.objectMakeParent(root, armature_object)
590         armature = bl.objectGetData(armature_object) 
591
592         # add armature modifier
593         for o in mesh_objects:
594             bl.objectAddArmatureModifier(o, armature_object)
595
596         # Limitation
597         for n, b in bl.objectGetPose(armature_object).bones.items():
598             bl.poseBoneLimit(n, b)
599
600     # select objects
601     bl.objectSelect(root)
602     for o in mesh_objects:
603         bl.objectSelect(o)
604     bl.objectSelect(armature_object)
605  
606
607 if isBlender24():
608     # for 2.4
609     def execute_24(filename):
610         """
611         @param filename
612         """
613         filename=filename.decode(bl.INTERNAL_ENCODING)
614         print(bl.INTERNAL_ENCODING, bl.FS_ENCODING)
615
616         # set object mode
617         mode_edit = Blender.Window.EditMode() 
618         if mode_edit: 
619             Blender.Window.EditMode(0)
620             
621         progress_start('pmd_import')
622         scene = bpy.data.scenes.active
623         __execute(filename, scene)
624         scene.update(0)
625         progress_finish()
626
627         # restore edit mode
628         if mode_edit: 
629             Blender.Window.EditMode(1)
630         Blender.Window.RedrawAll()
631
632     Blender.Window.FileSelector(
633             execute_24, 
634             'Import PMD file', 
635             Blender.sys.makename(ext='.pmd'))
636
637 else:
638     # for 2.5
639     def execute_25(*args):
640         progress_start('pmd_import')
641         __execute(*args)
642         progress_finish()
643
644     # import operator
645     class IMPORT_OT_pmd(bpy.types.Operator):
646         bl_idname = "import_scene.pmd"
647         bl_label = 'Import PMD'
648
649         # List of operator properties, the attributes will be assigned
650         # to the class instance from the operator settings before calling.
651
652         path = StringProperty(
653                 name="File Path", 
654                 description="File path used for importing the PMD file", 
655                 maxlen= 1024, default= "")
656         filename = StringProperty(
657                 name="File Name", 
658                 description="Name of the file.")
659         directory = StringProperty(
660                 name="Directory", 
661                 description="Directory of the file.")
662
663         def execute(self, context):
664             execute_25(self.properties.path, context.scene)
665             return 'FINISHED'
666
667         def invoke(self, context, event):
668             wm = context.manager
669             wm.add_fileselect(self)
670             return 'RUNNING_MODAL'
671
672     # register menu
673     def menu_func(self, context): 
674         self.layout.operator(IMPORT_OT_pmd.bl_idname, 
675                 text="MikuMikuDance model (.pmd)")
676
677     def register():
678         bpy.types.register(IMPORT_OT_pmd)
679         bpy.types.INFO_MT_file_import.append(menu_func)
680
681     def unregister():
682         bpy.types.unregister(IMPORT_OT_pmd)
683         bpy.types.INFO_MT_file_import.remove(menu_func)
684
685     if __name__=="__main__":
686         register()
687