OSDN Git Service

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