OSDN Git Service

add pymeshio.
[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__= "2.0"
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 1.2 20100616: implement rigid body.
28 1.3 20100619: fix for various models.
29 1.4 20100623: fix constraint name.
30 1.5 20100626: refactoring.
31 1.6 20100629: sphere map.
32 1.7 20100703: implement bone group.
33 1.8 20100710: implement toon texture.
34 1.9 20100718: keep model name, comment.
35 2.0 20100724: update for Blender2.53.
36 2.1 20100731: add full python module.
37 """
38 bl_addon_info = {
39         'category': 'Import/Export',
40         'name': 'Import: MikuMikuDance Model Format (.pmd)',
41         'author': 'ousttrue',
42         'version': '2.0',
43         'blender': (2, 5, 3),
44         'location': 'File > Import',
45         'description': 'Import from the MikuMikuDance Model Format (.pmd)',
46         'warning': '', # used for warning icon and text in addons panel
47         'wiki_url': 'http://sourceforge.jp/projects/meshio/wiki/FrontPage',
48         }
49
50 MMD_SHAPE_GROUP_NAME='_MMD_SHAPE'
51 MMD_MB_NAME='mb_name'
52 MMD_MB_COMMENT='mb_comment'
53 MMD_COMMENT='comment'
54 BASE_SHAPE_NAME='Basis'
55 RIGID_NAME='rigid_name'
56 RIGID_SHAPE_TYPE='rigid_shape_type'
57 RIGID_PROCESS_TYPE='rigid_process_type'
58 RIGID_BONE_NAME='rigid_bone_name'
59 #RIGID_LOCATION='rigid_loation'
60 RIGID_GROUP='ribid_group'
61 RIGID_INTERSECTION_GROUP='rigid_intersection_group'
62 RIGID_WEIGHT='rigid_weight'
63 RIGID_LINEAR_DAMPING='rigid_linear_damping'
64 RIGID_ANGULAR_DAMPING='rigid_angular_damping'
65 RIGID_RESTITUTION='rigid_restitution'
66 RIGID_FRICTION='rigid_friction'
67 CONSTRAINT_NAME='constraint_name'
68 CONSTRAINT_A='const_a'
69 CONSTRAINT_B='const_b'
70 CONSTRAINT_POS_MIN='const_pos_min'
71 CONSTRAINT_POS_MAX='const_pos_max'
72 CONSTRAINT_ROT_MIN='const_rot_min'
73 CONSTRAINT_ROT_MAX='const_rot_max'
74 CONSTRAINT_SPRING_POS='const_spring_pos'
75 CONSTRAINT_SPRING_ROT='const_spring_rot'
76 TOON_TEXTURE_OBJECT='ToonTextures'
77
78
79 ###############################################################################
80 # import
81 ###############################################################################
82 import os
83 import sys
84 import math
85
86 try:
87     # C extension
88     from meshio import pmd, englishmap
89 except ImportError:
90     # full python
91     from pymeshio import englishmap
92     from pymeshio import mmd as pmd
93     pmd.IO=pmd.PMDLoader
94
95 def isBlender24():
96     return sys.version_info[0]<3
97
98 if isBlender24():
99     # for 2.4
100     import Blender
101     from Blender import Mathutils
102     import bpy
103
104     # wrapper
105     import bl24 as bl
106
107     def createPmdMaterial(m, index):
108         material=Blender.Material.New()
109         # fresnelが無いw
110         material.setDiffuseShader(Blender.Material.Shaders.DIFFUSE_TOON)
111         material.setRGBCol([m.diffuse.r, m.diffuse.g, m.diffuse.b])
112         material.setAlpha(m.diffuse.a)
113         # specular
114         material.setSpecShader(Blender.Material.Shaders.SPEC_TOON)
115         material.setSpec(m.shinness*0.1)
116         material.setSpecCol([m.specular.r, m.specular.g, m.specular.b])
117         # ambient
118         material.setMirCol([m.ambient.r, m.ambient.g, m.ambient.b])
119         # flag
120         material.enableSSS=True if m.flag==1 else False
121         # name
122         material.name="m_%02d" % index
123         return material
124
125     def poseBoneLimit(n, b):
126         if n.endswith("_t"):
127             return
128         if n.startswith("knee_"):
129             b.lockYRot=True
130             b.lockZRot=True
131             b.limitX=True
132             b.limitMin=[0, 0, 0]
133             b.limitMax=[180, 0, 0]
134         elif n.startswith("ankle_"):
135             b.lockYRot=True
136
137     def setSphereMap(material, index, blend_type='MULTIPLY'):
138         slot=material.textures[index]
139         slot.mapto=Blender.Texture.MapTo.NOR
140         slot.mapping=Blender.Texture.Mappings.SPHERE
141         if blend_type=='MULTIPLY':
142             slot.blendmode=Blender.Texture.BlendModes.MULTIPLY
143         elif blend_type=='ADD':
144             slot.blendmode=Blender.Texture.BlendModes.ADD
145
146 else:
147     # for 2.5
148     import bpy
149     import mathutils
150
151     # wrapper
152     import bl25 as bl
153
154     xrange=range
155
156     def createPmdMaterial(m, index):
157         material = bpy.data.materials.new("Material")
158         # diffuse
159         material.diffuse_shader='FRESNEL'
160         material.diffuse_color=([m.diffuse.r, m.diffuse.g, m.diffuse.b])
161         material.alpha=m.diffuse.a
162         # specular
163         material.specular_shader='TOON'
164         material.specular_color=([m.specular.r, m.specular.g, m.specular.b])
165         material.specular_toon_size=int(m.shinness)
166         # ambient
167         material.mirror_color=([m.ambient.r, m.ambient.g, m.ambient.b])
168         # flag
169         material.subsurface_scattering.enabled=True if m.flag==1 else False
170         # other
171         material.name="m_%02d" % index
172         material.preview_render_type='FLAT'
173         material.transparency=True
174         return material
175
176     def poseBoneLimit(n, b):
177         if n.endswith("_t"):
178             return
179         if n.startswith("knee_"):
180             b.ik_dof_y=False
181             b.ik_dof_z=False
182             b.ik_dof_x=True
183             b.ik_limit_x=True
184             b.ik_min_x=0
185             b.ik_max_x=180
186         elif n.startswith("ankle_"):
187             #b.ik_dof_y=False
188             pass
189
190     def setSphereMap(material, index, blend_type='MULTIPLY'):
191         slot=material.texture_slots[index]
192         slot.texture_coordinates='NORMAL'
193         slot.mapping='SPHERE'
194         slot.blend_type=blend_type
195
196
197 ###############################################################################
198 def VtoV(v):
199     return bl.createVector(v.x, v.y, v.z)
200
201
202 def convert_coord(pos):
203     """
204     Left handed y-up to Right handed z-up
205     """
206     return (pos.x, pos.z, pos.y)
207
208
209 def to_radian(degree):
210     return math.pi * degree / 180
211
212
213 def get_bone_name(l, index):
214     if index==0xFFFF:
215         return l.bones[0].getName()
216
217     if index < len(l.bones):
218         name=englishmap.getEnglishBoneName(l.bones[index].getName())
219         if name:
220             return name
221         return l.bones[index].getName()
222     print('invalid bone index', index)
223     return l.bones[0].getName()
224
225
226 def get_group_name(g):
227     group_name=englishmap.getEnglishBoneGroupName(g.getName().strip())
228     if not group_name:
229         group_name=g.getName().strip()
230     return group_name
231
232
233 def __importToonTextures(io, tex_dir):
234     mesh, meshObject=bl.mesh.create(TOON_TEXTURE_OBJECT)
235     material=bl.material.create(TOON_TEXTURE_OBJECT)
236     bl.mesh.addMaterial(mesh, material)
237     for i in range(10):
238         t=io.getToonTexture(i)
239         path=os.path.join(tex_dir, t.getName())
240         texture, image=bl.texture.create(path)
241         bl.material.addTexture(material, texture, False)
242     return meshObject, material
243
244
245 def __importShape(obj, l, vertex_map):
246     if len(l.morph_list)==0:
247         return
248
249     # set shape_key pin
250     bl.object.pinShape(obj, True)
251
252     # find base 
253     base=None
254     for s in l.morph_list:
255         if s.type==0:
256             base=s
257
258             # create vertex group
259             bl.object.addVertexGroup(obj, MMD_SHAPE_GROUP_NAME)
260             hasShape=False
261             for i in s.indices:
262                 if i in vertex_map:
263                     hasShape=True
264                     bl.object.assignVertexGroup(
265                             obj, MMD_SHAPE_GROUP_NAME, vertex_map[i], 0)
266             if not hasShape:
267                 return
268     assert(base)
269
270     # create base key
271     baseShapeBlock=bl.object.addShapeKey(obj, BASE_SHAPE_NAME)
272     # mesh
273     mesh=bl.object.getData(obj)
274     mesh.update()
275
276     # each skin
277     for s in l.morph_list:
278         if s.type==0:
279             continue
280
281         # name
282         name=englishmap.getEnglishSkinName(s.getName())
283         if not name:
284             name=s.getName()
285
286         if isBlender24():
287             # 24
288             for index, offset in zip(s.indices, s.pos_list):
289                 try:
290                     vertex_index=vertex_map[base.indices[index]]
291                     v=mesh.verts[vertex_index].co
292                     offset=convert_coord(offset)
293                     v[0]+=offset[0]
294                     v[1]+=offset[1]
295                     v[2]+=offset[2]
296                 except IndexError as msg:
297                     print(msg)
298                     print(index, len(base.indices), len(vertex_map))
299                     print(len(mesh.verts))
300                     print(base.indices[index])
301                     print(vertex_index)
302                     break
303                 except KeyError:
304                     #print 'this mesh not has shape vertices'
305                     break
306
307             # create shapekey block
308             new_shape_key=bl.object.addShapeKey(obj, name)
309
310             # copy vertex to shape key
311             mesh.update()
312             
313             # restore
314             for mv, v in zip(mesh.verts, baseShapeBlock.getData()):
315                 mv.co[0] = v[0]
316                 mv.co[1] = v[1]
317                 mv.co[2] = v[2]
318             mesh.update()
319
320         else:
321             # 25
322             new_shape_key=bl.object.addShapeKey(obj, name)
323
324             for index, offset in zip(s.indices, s.pos_list):
325                 try:
326                     vertex_index=vertex_map[base.indices[index]]
327                     bl.shapekey.assign(new_shape_key, vertex_index,
328                             mesh.verts[vertex_index].co+
329                             bl.createVector(*convert_coord(offset)))
330                 except IndexError as msg:
331                     print(msg)
332                     print(index, len(base.indices), len(vertex_map))
333                     print(len(mesh.verts))
334                     print(base.indices[index])
335                     print(vertex_index)
336                     break
337                 except KeyError:
338                     #print 'this mesh not has shape vertices'
339                     break
340
341     # select base shape
342     bl.object.setActivateShapeKey(obj, 0)
343
344
345 def __build(armature, b, p, parent):
346     name=englishmap.getEnglishBoneName(b.getName())
347     if not name:
348         name=b.getName()
349
350     bone=bl.armature.createBone(armature, name)
351
352     if parent and (b.tail_index==0 or b.type==6 or b.type==7 or b.type==9):
353         # 先端ボーン
354         bone.head = bl.createVector(*convert_coord(b.pos))
355         bone.tail=bone.head+bl.createVector(0, 1, 0)
356         bone.parent=parent
357         if bone.name=="center_t":
358             # センターボーンは(0, 1, 0)の方向を向いていないと具合が悪い
359             parent.tail=parent.head+bl.createVector(0, 1, 0)
360             bone.head=parent.tail
361             bone.tail=bone.head+bl.createVector(0, 1, 0)
362         else:
363             if parent.tail==bone.head:
364                 pass
365             else:
366                 print('diffurence with parent.tail and head', name)
367
368         if b.type!=9:
369             bl.bone.setConnected(bone)
370         # armature layer 2
371         bl.bone.setLayerMask(bone, [0, 1])
372     else:
373         # 通常ボーン
374         bone.head = bl.createVector(*convert_coord(b.pos))
375         bone.tail = bl.createVector(*convert_coord(b.tail))
376         if parent:
377             bone.parent=parent
378             if parent.tail==bone.head:
379                 bl.bone.setConnected(bone)
380
381     if bone.head==bone.tail:
382         bone.tail=bone.head+bl.createVector(0, 1, 0)
383
384     for c in b.children:
385         __build(armature, c, b, bone)
386
387
388 def __importArmature(l):
389     armature, armature_object=bl.armature.create()
390
391     # build bone
392     bl.armature.makeEditable(armature_object)
393     for b in l.bones:
394         if not b.parent:
395             __build(armature, b, None, None)
396     bl.armature.update(armature)
397     bl.enterObjectMode()
398
399     # IK constraint
400     pose = bl.object.getPose(armature_object)
401     for ik in l.ik_list:
402         target=l.bones[ik.target]
403         name = englishmap.getEnglishBoneName(target.getName())
404         if not name:
405             name=target.getName()
406         p_bone = pose.bones[name]
407         if not p_bone:
408             print('not found', name)
409             continue
410         if len(ik.children) >= 16:
411             print('over MAX_CHAINLEN', ik, len(ik.children))
412             continue
413         effector_name=englishmap.getEnglishBoneName(
414                 l.bones[ik.index].getName())
415         if not effector_name:
416             effector_name=l.bones[ik.index].getName()
417
418         constraint=bl.armature.createIkConstraint(armature_object, 
419                 p_bone, effector_name, ik)
420
421     bl.armature.makeEditable(armature_object)
422     bl.armature.update(armature)
423     bl.enterObjectMode()
424
425     if isBlender24():
426         pass
427     else:
428         # create bone group
429         for i, g in enumerate(l.bone_group_list):
430             name=get_group_name(g)
431             bl.object.createBoneGroup(armature_object, name, "THEME%02d" % (i+1))
432
433         # assign bone to group
434         for b_index, g_index in l.bone_display_list:
435             # bone
436             b=l.bones[b_index]
437             bone_name=englishmap.getEnglishBoneName(b.getName())
438             if not bone_name:
439                 bone_name=b.getName()
440             # group
441             g=l.bone_group_list[g_index-1]
442             group_name=get_group_name(g)
443
444             # assign
445             pose.bones[bone_name].bone_group=pose.bone_groups[group_name]
446
447         bl.enterObjectMode()
448
449     return armature_object
450         
451
452 def __import16MaerialAndMesh(meshObject, l, 
453         material_order, face_map, tex_dir, toon_material):
454
455     mesh=bl.object.getData(meshObject)
456     ############################################################
457     # material
458     ############################################################
459     bl.progress_print('create materials')
460     mesh_material_map={}
461     textureMap={}
462     imageMap={}
463     index=0
464
465     for material_index in material_order:
466         try:
467             m=l.materials[material_index]
468             mesh_material_map[material_index]=index
469         except KeyError:
470             break
471
472         material=createPmdMaterial(m, material_index)
473
474         # main texture
475         texture_name=m.getTexture()
476         if texture_name!='':
477             for i, t in enumerate(texture_name.split('*')):
478                 if t in textureMap:
479                     texture=textureMap[t]
480                 else:
481                     path=os.path.join(tex_dir, t)
482                     texture, image=bl.texture.create(path)
483                     textureMap[texture_name]=texture
484                     imageMap[material_index]=image
485                 texture_index=bl.material.addTexture(material, texture)
486                 if t.endswith('sph'):
487                     # sphere map
488                     setSphereMap(material, texture_index)
489                 elif t.endswith('spa'):
490                     # sphere map
491                     setSphereMap(material, texture_index, 'ADD')
492
493         # toon texture
494         toon_index=bl.material.addTexture(
495                 material, 
496                 bl.material.getTexture(
497                     toon_material, 
498                     0 if m.toon_index==0xFF else m.toon_index
499                     ),
500                 False)
501
502         bl.mesh.addMaterial(mesh, material)
503
504         index+=1
505
506     ############################################################
507     # vertex
508     ############################################################
509     bl.progress_print('create vertices')
510     # create vertices
511     vertices=[]
512     for v in l.each_vertex():
513         vertices.append(convert_coord(v.pos))
514
515     ############################################################
516     # face
517     ############################################################
518     bl.progress_print('create faces')
519     # create faces
520     mesh_face_indices=[]
521     mesh_face_materials=[]
522     used_vertices=set()
523
524     for material_index in material_order:
525         face_offset=face_map[material_index]
526         m=l.materials[material_index]
527         material_faces=l.indices[face_offset:face_offset+m.vertex_count]
528
529         def degenerate(i0, i1, i2):
530             """
531             縮退しているか?
532             """
533             return i0==i1 or i1==i2 or i2==i0
534
535         for j in xrange(0, len(material_faces), 3):
536             i0=material_faces[j]
537             i1=material_faces[j+1]
538             i2=material_faces[j+2]
539             # flip
540             triangle=[i2, i1, i0]
541             if degenerate(*triangle):
542                 continue
543             mesh_face_indices.append(triangle[0:3])
544             mesh_face_materials.append(material_index)
545             used_vertices.add(i0)
546             used_vertices.add(i1)
547             used_vertices.add(i2)
548
549     ############################################################
550     # create vertices & faces
551     ############################################################
552     bl.mesh.addGeometry(mesh, vertices, mesh_face_indices)
553
554     ############################################################
555     # vertex bone weight
556     ############################################################
557     # create vertex group
558     vertex_groups={}
559     for v in l.each_vertex():
560         vertex_groups[v.bone0]=True
561         vertex_groups[v.bone1]=True
562     for i in vertex_groups.keys():
563         bl.object.addVertexGroup(meshObject, get_bone_name(l, i))
564
565     # vertex params
566     bl.mesh.useVertexUV(mesh)
567     for i, v, mvert in zip(xrange(len(l.vertices)), 
568         l.each_vertex(), mesh.verts):
569         # normal, uv
570         bl.vertex.setNormal(mvert, convert_coord(v.normal))
571         # bone weight
572         w1=float(v.weight0)/100.0
573         w2=1.0-w1
574         bl.object.assignVertexGroup(meshObject, get_bone_name(l, v.bone0),
575             i,  w1)
576         bl.object.assignVertexGroup(meshObject, get_bone_name(l, v.bone1),
577             i,  w2)
578
579     ############################################################
580     # face params
581     ############################################################
582     used_map={}
583     bl.mesh.addUV(mesh)
584     for i, (face, material_index) in enumerate(
585             zip(mesh.faces, mesh_face_materials)):
586         try:
587             index=mesh_material_map[material_index]
588         except KeyError as message:
589             print(message, mesh_material_map, m)
590             assert(False)
591         bl.face.setMaterial(face, index)
592         material=mesh.materials[index]
593         used_map[index]=True
594         if bl.material.hasTexture(material):
595             uv_array=[l.getUV(i) for i in bl.face.getIndices(face)]
596             bl.mesh.setFaceUV(mesh, i, face, 
597                     # fix uv
598                     [(uv.x, 1.0-uv.y) for uv in uv_array], 
599                     imageMap.get(index, None))
600
601         # set smooth
602         face.smooth = 1
603
604     mesh.update()
605
606     ############################################################
607     # clean up not used vertices
608     ############################################################
609     bl.progress_print('clean up vertices not used')
610     remove_vertices=[]
611     vertex_map={}
612     for i, v in enumerate(l.each_vertex()):
613         if i in used_vertices:
614             vertex_map[i]=len(vertex_map)
615         else:
616             remove_vertices.append(i)
617
618     bl.mesh.vertsDelete(mesh, remove_vertices)
619
620     bl.progress_print('%s created' % mesh.name)
621     return vertex_map
622
623
624 def __importMaterialAndMesh(io, tex_dir, toon_material):
625     """
626     @param l[in] mmd.PMDLoader
627     @param filename[in]
628     """
629     ############################################################
630     # shpaeキーで使われるマテリアル優先的に前に並べる
631     ############################################################
632     # shapeキーで使われる頂点インデックスを集める
633     shape_key_used_vertices=set()
634     if len(io.morph_list)>0:
635         # base 
636         base=None
637         for s in io.morph_list:
638             if s.type!=0:
639                 continue
640             base=s
641             break
642         assert(base)
643
644         for index in base.indices:
645             shape_key_used_vertices.add(index)
646
647     # マテリアルに含まれる頂点がshape_keyに含まれるか否か?
648     def isMaterialUsedInShape(offset, m):
649         for i in xrange(offset, offset+m.vertex_count): 
650             if io.indices[i] in shape_key_used_vertices:
651                 return True
652
653     material_with_shape=set()
654
655     # 各マテリアルの開始頂点インデックスを記録する
656     face_map={}
657     face_count=0
658     for i, m in enumerate(io.materials):
659         face_map[i]=face_count
660         if isMaterialUsedInShape(face_count, m):
661             material_with_shape.add(i)
662         face_count+=m.vertex_count
663
664     # shapeキーで使われる頂点のあるマテリアル
665     material_with_shape=list(material_with_shape)
666     material_with_shape.sort()
667
668     # shapeキーに使われていないマテリアル
669     material_without_shape=[]
670     for i in range(len(io.materials)):
671         if not i in material_with_shape:
672             material_without_shape.append(i)
673
674     # メッシュの生成
675     def __splitList(l, length):
676         for i in range(0, len(l), length):
677             yield l[i:i+length]
678
679     def __importMeshAndShape(material16, name):
680         mesh, meshObject=bl.mesh.create(name)
681
682         # activate object
683         bl.object.deselectAll()
684         bl.object.activate(meshObject)
685
686         # shapeキーで使われる順に並べなおしたマテリアル16個分の
687         # メッシュを作成する
688         vertex_map=__import16MaerialAndMesh(
689                 meshObject, io, material16, face_map, tex_dir, toon_material)
690
691         # crete shape key
692         __importShape(meshObject, io, vertex_map)
693
694         mesh.update()
695         return meshObject
696
697     mesh_objects=[__importMeshAndShape(material16, 'with_shape')
698         for material16 in __splitList(material_with_shape, 16)]
699     
700     mesh_objects+=[__importMeshAndShape(material16, 'mesh')
701         for material16 in __splitList(material_without_shape, 16)]
702  
703     return mesh_objects
704
705
706 def __importConstraints(io):
707     if isBlender24():
708         return
709     print("create constraint")
710     container=bl.object.createEmpty('Constraints')
711     layer=[
712         True, False, False, False, False, False, False, False, False, False,
713         False, False, False, False, False, False, False, False, False, False,
714             ]
715     material=bl.material.create('constraint')
716     material.diffuse_color=(1, 0, 0)
717     constraintMeshes=[]
718     for i, c in enumerate(io.constraints):
719         bpy.ops.mesh.primitive_uv_sphere_add(
720                 segments=8,
721                 rings=4,
722                 size=0.1,
723                 location=(c.pos.x, c.pos.z, c.pos.y),
724                 layer=layer
725                 )
726         meshObject=bl.object.getActive()
727         constraintMeshes.append(meshObject)
728         mesh=bl.object.getData(meshObject)
729         bl.mesh.addMaterial(mesh, material)
730         meshObject.name='c_%d' % i
731         #meshObject.draw_transparent=True
732         #meshObject.draw_wire=True
733         meshObject.max_draw_type='SOLID'
734         rot=c.rot
735         meshObject.rotation_euler=(-rot.x, -rot.z, -rot.y)
736
737         meshObject[CONSTRAINT_NAME]=c.getName()
738         meshObject[CONSTRAINT_A]=io.rigidbodies[c.rigidA].getName()
739         meshObject[CONSTRAINT_B]=io.rigidbodies[c.rigidB].getName()
740         meshObject[CONSTRAINT_POS_MIN]=VtoV(c.constraintPosMin)
741         meshObject[CONSTRAINT_POS_MAX]=VtoV(c.constraintPosMax)
742         meshObject[CONSTRAINT_ROT_MIN]=VtoV(c.constraintRotMin)
743         meshObject[CONSTRAINT_ROT_MAX]=VtoV(c.constraintRotMax)
744         meshObject[CONSTRAINT_SPRING_POS]=VtoV(c.springPos)
745         meshObject[CONSTRAINT_SPRING_ROT]=VtoV(c.springRot)
746
747     for meshObject in reversed(constraintMeshes):
748         bl.object.makeParent(container, meshObject)
749
750     return container
751
752
753 def __importRigidBodies(io):
754     if isBlender24():
755         return
756     print("create rigid bodies")
757
758     container=bl.object.createEmpty('RigidBodies')
759     layer=[
760         True, False, False, False, False, False, False, False, False, False,
761         False, False, False, False, False, False, False, False, False, False,
762             ]
763     material=bl.material.create('rigidBody')
764     rigidMeshes=[]
765     for i, rigid in enumerate(io.rigidbodies):
766         if rigid.boneIndex==0xFFFF:
767             # no reference bone
768             bone=io.bones[0]
769         else:
770             bone=io.bones[rigid.boneIndex]
771         pos=bone.pos+rigid.position
772
773         if rigid.shapeType==pmd.SHAPE_SPHERE:
774             bpy.ops.mesh.primitive_ico_sphere_add(
775                     location=(pos.x, pos.z, pos.y),
776                     layer=layer
777                     )
778             bpy.ops.transform.resize(
779                     value=(rigid.w, rigid.w, rigid.w))
780         elif rigid.shapeType==pmd.SHAPE_BOX:
781             bpy.ops.mesh.primitive_cube_add(
782                     location=(pos.x, pos.z, pos.y),
783                     layer=layer
784                     )
785             bpy.ops.transform.resize(
786                     value=(rigid.w, rigid.d, rigid.h))
787         elif rigid.shapeType==pmd.SHAPE_CAPSULE:
788             bpy.ops.mesh.primitive_tube_add(
789                     location=(pos.x, pos.z, pos.y),
790                     layer=layer
791                     )
792             bpy.ops.transform.resize(
793                     value=(rigid.w, rigid.w, rigid.h))
794         else:
795             assert(False)
796
797         meshObject=bl.object.getActive()
798         mesh=bl.object.getData(meshObject)
799         rigidMeshes.append(meshObject)
800         bl.mesh.addMaterial(mesh, material)
801         meshObject.name='r_%d' % i
802         meshObject[RIGID_NAME]=rigid.getName()
803         #meshObject.draw_transparent=True
804         #meshObject.draw_wire=True
805         meshObject.max_draw_type='WIRE'
806         rot=rigid.rotation
807         meshObject.rotation_euler=(-rot.x, -rot.z, -rot.y)
808
809         # custom properties
810         meshObject[RIGID_SHAPE_TYPE]=rigid.shapeType
811         meshObject[RIGID_PROCESS_TYPE]=rigid.processType
812
813         bone_name = englishmap.getEnglishBoneName(bone.getName())
814         if not bone_name:
815             bone_name=bone.getName()
816         meshObject[RIGID_BONE_NAME]=bone_name
817
818         meshObject[RIGID_GROUP]=rigid.group
819         meshObject[RIGID_INTERSECTION_GROUP]=rigid.target
820         meshObject[RIGID_WEIGHT]=rigid.weight
821         meshObject[RIGID_LINEAR_DAMPING]=rigid.linearDamping
822         meshObject[RIGID_ANGULAR_DAMPING]=rigid.angularDamping
823         meshObject[RIGID_RESTITUTION]=rigid.restitution
824         meshObject[RIGID_FRICTION]=rigid.friction
825
826     for meshObject in reversed(rigidMeshes):
827         bl.object.makeParent(container, meshObject)
828
829     return container
830
831
832 def _execute(filename):
833     """
834     load pmd file to context.
835     """
836            
837     # load pmd
838     bl.progress_set('load %s' % filename, 0.0)
839
840     io=pmd.IO()
841     if not io.read(filename):
842         bl.message("fail to load %s" % filename)
843         return
844     bl.progress_set('loaded %s' % filename, 0.1)
845
846     # create root object
847     model_name=io.getEnglishName()
848     if len(model_name)==0:
849         model_name=io.getName()
850     root=bl.object.createEmpty(model_name)
851     root[MMD_MB_NAME]=io.getName()
852     root[MMD_MB_COMMENT]=io.getComment()
853     root[MMD_COMMENT]=io.getEnglishComment()
854
855     # toon textures
856     tex_dir=os.path.dirname(filename)
857     toonTextures, toonMaterial=__importToonTextures(io, tex_dir)
858     bl.object.makeParent(root, toonTextures)
859
860     # import mesh
861     mesh_objects=__importMaterialAndMesh(io, tex_dir, toonMaterial)
862     for o in mesh_objects:
863         bl.object.makeParent(root, o)
864
865     # import armature
866     armature_object=__importArmature(io)
867     if armature_object:
868         bl.object.makeParent(root, armature_object)
869         armature = bl.object.getData(armature_object) 
870
871         # add armature modifier
872         for o in mesh_objects:
873             bl.modifier.addArmature(o, armature_object)
874
875         # Limitation
876         for n, b in bl.object.getPose(armature_object).bones.items():
877             poseBoneLimit(n, b)
878
879     # import rigid bodies
880     rigidBodies=__importRigidBodies(io)
881     if rigidBodies:
882         bl.object.makeParent(root, rigidBodies)
883
884     # import constraints
885     constraints=__importConstraints(io)
886     if constraints:
887         bl.object.makeParent(root, constraints)
888
889     bl.object.activate(root)
890
891
892 if isBlender24():
893     # for 2.4
894     def execute_24(filename):
895         bl.initialize('pmd_import', bpy.data.scenes.active)
896         _execute(filename.decode(bl.INTERNAL_ENCODING))
897         bl.finalize()
898
899     Blender.Window.FileSelector(
900             execute_24, 
901             'Import PMD file', 
902             Blender.sys.makename(ext='.pmd'))
903
904 else:
905     # import operator
906     class IMPORT_OT_pmd(bpy.types.Operator):
907         bl_idname = "import_scene.pmd"
908         bl_label = 'Import PMD'
909
910         # List of operator properties, the attributes will be assigned
911         # to the class instance from the operator settings before calling.
912         filepath = bpy.props.StringProperty()
913         filename = bpy.props.StringProperty()
914         directory = bpy.props.StringProperty()
915
916         def execute(self, context):
917             bl.initialize('pmd_import', context.scene)
918             _execute(self.properties.filepath)
919             bl.finalize()
920             return 'FINISHED'
921
922         def invoke(self, context, event):
923             wm = context.manager
924             wm.add_fileselect(self)
925             return 'RUNNING_MODAL'
926
927     # register menu
928     def menu_func(self, context): 
929         self.layout.operator(IMPORT_OT_pmd.bl_idname, 
930                 text="MikuMikuDance model (.pmd)",
931                 icon='PLUGIN'
932                 )
933
934     def register():
935         bpy.types.register(IMPORT_OT_pmd)
936         bpy.types.INFO_MT_file_import.append(menu_func)
937
938     def unregister():
939         bpy.types.unregister(IMPORT_OT_pmd)
940         bpy.types.INFO_MT_file_import.remove(menu_func)
941
942     if __name__=="__main__":
943         register()
944