OSDN Git Service

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