OSDN Git Service

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