4 Name: 'MikuMikuDance model (.pmd)...'
7 Tooltip: 'Import PMD file for MikuMikuDance.'
9 __author__= ["ousttrue"]
15 This script imports a pmd into Blender for editing.
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.
38 'category': 'Import/Export',
39 'name': 'Import: MikuMikuDance Model Format (.pmd)',
43 'location': 'File > Import',
44 'description': 'Import from the MikuMikuDance Model Format (.pmd)',
45 'warning': '', # used for warning icon and text in addons panel
46 'wiki_url': 'http://sourceforge.jp/projects/meshio/wiki/FrontPage',
49 MMD_SHAPE_GROUP_NAME='_MMD_SHAPE'
51 MMD_MB_COMMENT='mb_comment'
53 BASE_SHAPE_NAME='Basis'
54 RIGID_NAME='rigid_name'
55 RIGID_SHAPE_TYPE='rigid_shape_type'
56 RIGID_PROCESS_TYPE='rigid_process_type'
57 RIGID_BONE_NAME='rigid_bone_name'
58 #RIGID_LOCATION='rigid_loation'
59 RIGID_GROUP='ribid_group'
60 RIGID_INTERSECTION_GROUP='rigid_intersection_group'
61 RIGID_WEIGHT='rigid_weight'
62 RIGID_LINEAR_DAMPING='rigid_linear_damping'
63 RIGID_ANGULAR_DAMPING='rigid_angular_damping'
64 RIGID_RESTITUTION='rigid_restitution'
65 RIGID_FRICTION='rigid_friction'
66 CONSTRAINT_NAME='constraint_name'
67 CONSTRAINT_A='const_a'
68 CONSTRAINT_B='const_b'
69 CONSTRAINT_POS_MIN='const_pos_min'
70 CONSTRAINT_POS_MAX='const_pos_max'
71 CONSTRAINT_ROT_MIN='const_rot_min'
72 CONSTRAINT_ROT_MAX='const_rot_max'
73 CONSTRAINT_SPRING_POS='const_spring_pos'
74 CONSTRAINT_SPRING_ROT='const_spring_rot'
75 TOON_TEXTURE_OBJECT='ToonTextures'
78 ###############################################################################
80 ###############################################################################
86 from meshio import pmd, englishmap
89 return sys.version_info[0]<3
94 from Blender import Mathutils
100 def createPmdMaterial(m, index):
101 material=Blender.Material.New()
103 material.setDiffuseShader(Blender.Material.Shaders.DIFFUSE_TOON)
104 material.setRGBCol([m.diffuse.r, m.diffuse.g, m.diffuse.b])
105 material.setAlpha(m.diffuse.a)
107 material.setSpecShader(Blender.Material.Shaders.SPEC_TOON)
108 material.setSpec(m.shinness*0.1)
109 material.setSpecCol([m.specular.r, m.specular.g, m.specular.b])
111 material.setMirCol([m.ambient.r, m.ambient.g, m.ambient.b])
113 material.enableSSS=True if m.flag==1 else False
115 material.name="m_%02d" % index
118 def poseBoneLimit(n, b):
121 if n.startswith("knee_"):
126 b.limitMax=[180, 0, 0]
127 elif n.startswith("ankle_"):
130 def setSphereMap(material, index, blend_type='MULTIPLY'):
131 slot=material.textures[index]
132 slot.mapto=Blender.Texture.MapTo.NOR
133 slot.mapping=Blender.Texture.Mappings.SPHERE
134 if blend_type=='MULTIPLY':
135 slot.blendmode=Blender.Texture.BlendModes.MULTIPLY
136 elif blend_type=='ADD':
137 slot.blendmode=Blender.Texture.BlendModes.ADD
149 def createPmdMaterial(m, index):
150 material = bpy.data.materials.new("Material")
152 material.diffuse_shader='FRESNEL'
153 material.diffuse_color=([m.diffuse.r, m.diffuse.g, m.diffuse.b])
154 material.alpha=m.diffuse.a
156 material.specular_shader='TOON'
157 material.specular_color=([m.specular.r, m.specular.g, m.specular.b])
158 material.specular_toon_size=int(m.shinness)
160 material.mirror_color=([m.ambient.r, m.ambient.g, m.ambient.b])
162 material.subsurface_scattering.enabled=True if m.flag==1 else False
164 material.name="m_%02d" % index
165 material.preview_render_type='FLAT'
166 material.transparency=True
169 def poseBoneLimit(n, b):
172 if n.startswith("knee_"):
179 elif n.startswith("ankle_"):
183 def setSphereMap(material, index, blend_type='MULTIPLY'):
184 slot=material.texture_slots[index]
185 slot.texture_coordinates='NORMAL'
186 slot.mapping='SPHERE'
187 slot.blend_type=blend_type
190 ###############################################################################
192 return bl.createVector(v.x, v.y, v.z)
195 def convert_coord(pos):
197 Left handed y-up to Right handed z-up
199 return (pos.x, pos.z, pos.y)
202 def to_radian(degree):
203 return math.pi * degree / 180
206 def get_bone_name(l, index):
208 return l.bones[0].getName()
210 if index < len(l.bones):
211 name=englishmap.getEnglishBoneName(l.bones[index].getName())
214 return l.bones[index].getName()
215 print('invalid bone index', index)
216 return l.bones[0].getName()
219 def get_group_name(g):
220 group_name=englishmap.getEnglishBoneGroupName(g.getName().strip())
222 group_name=g.getName().strip()
226 def __importToonTextures(io, tex_dir):
227 mesh, meshObject=bl.mesh.create(TOON_TEXTURE_OBJECT)
228 material=bl.material.create(TOON_TEXTURE_OBJECT)
229 bl.mesh.addMaterial(mesh, material)
231 t=io.getToonTexture(i)
232 path=os.path.join(tex_dir, t.getName())
233 texture, image=bl.texture.create(path)
234 bl.material.addTexture(material, texture, False)
235 return meshObject, material
238 def __importShape(obj, l, vertex_map):
239 if len(l.morph_list)==0:
243 bl.object.pinShape(obj, True)
247 for s in l.morph_list:
251 # create vertex group
252 bl.object.addVertexGroup(obj, MMD_SHAPE_GROUP_NAME)
257 bl.object.assignVertexGroup(
258 obj, MMD_SHAPE_GROUP_NAME, vertex_map[i], 0)
264 baseShapeBlock=bl.object.addShapeKey(obj, BASE_SHAPE_NAME)
266 mesh=bl.object.getData(obj)
270 for s in l.morph_list:
275 name=englishmap.getEnglishSkinName(s.getName())
281 for index, offset in zip(s.indices, s.pos_list):
283 vertex_index=vertex_map[base.indices[index]]
284 v=mesh.verts[vertex_index].co
285 offset=convert_coord(offset)
289 except IndexError as msg:
291 print(index, len(base.indices), len(vertex_map))
292 print(len(mesh.verts))
293 print(base.indices[index])
297 #print 'this mesh not has shape vertices'
300 # create shapekey block
301 new_shape_key=bl.object.addShapeKey(obj, name)
303 # copy vertex to shape key
307 for mv, v in zip(mesh.verts, baseShapeBlock.getData()):
315 new_shape_key=bl.object.addShapeKey(obj, name)
317 for index, offset in zip(s.indices, s.pos_list):
319 vertex_index=vertex_map[base.indices[index]]
320 bl.shapekey.assign(new_shape_key, vertex_index,
321 mesh.verts[vertex_index].co+
322 bl.createVector(*convert_coord(offset)))
323 except IndexError as msg:
325 print(index, len(base.indices), len(vertex_map))
326 print(len(mesh.verts))
327 print(base.indices[index])
331 #print 'this mesh not has shape vertices'
335 bl.object.setActivateShapeKey(obj, 0)
338 def __build(armature, b, p, parent):
339 name=englishmap.getEnglishBoneName(b.getName())
343 bone=bl.armature.createBone(armature, name)
345 if parent and (b.tail_index==0 or b.type==6 or b.type==7 or b.type==9):
347 bone.head = bl.createVector(*convert_coord(b.pos))
348 bone.tail=bone.head+bl.createVector(0, 1, 0)
350 if bone.name=="center_t":
351 # センターボーンは(0, 1, 0)の方向を向いていないと具合が悪い
352 parent.tail=parent.head+bl.createVector(0, 1, 0)
353 bone.head=parent.tail
354 bone.tail=bone.head+bl.createVector(0, 1, 0)
356 if parent.tail==bone.head:
359 print('diffurence with parent.tail and head', name)
362 bl.bone.setConnected(bone)
364 bl.bone.setLayerMask(bone, [0, 1])
367 bone.head = bl.createVector(*convert_coord(b.pos))
368 bone.tail = bl.createVector(*convert_coord(b.tail))
371 if parent.tail==bone.head:
372 bl.bone.setConnected(bone)
374 if bone.head==bone.tail:
375 bone.tail=bone.head+bl.createVector(0, 1, 0)
378 __build(armature, c, b, bone)
381 def __importArmature(l):
382 armature, armature_object=bl.armature.create()
385 bl.armature.makeEditable(armature_object)
388 __build(armature, b, None, None)
389 bl.armature.update(armature)
393 pose = bl.object.getPose(armature_object)
395 target=l.bones[ik.target]
396 name = englishmap.getEnglishBoneName(target.getName())
398 name=target.getName()
399 p_bone = pose.bones[name]
401 print('not found', name)
403 if len(ik.children) >= 16:
404 print('over MAX_CHAINLEN', ik, len(ik.children))
406 effector_name=englishmap.getEnglishBoneName(
407 l.bones[ik.index].getName())
408 if not effector_name:
409 effector_name=l.bones[ik.index].getName()
411 constraint=bl.armature.createIkConstraint(armature_object,
412 p_bone, effector_name, ik)
414 bl.armature.makeEditable(armature_object)
415 bl.armature.update(armature)
422 for i, g in enumerate(l.bone_group_list):
423 name=get_group_name(g)
424 bl.object.createBoneGroup(armature_object, name, "THEME%02d" % (i+1))
426 # assign bone to group
427 for b_index, g_index in l.bone_display_list:
430 bone_name=englishmap.getEnglishBoneName(b.getName())
432 bone_name=b.getName()
434 g=l.bone_group_list[g_index-1]
435 group_name=get_group_name(g)
438 pose.bones[bone_name].bone_group=pose.bone_groups[group_name]
442 return armature_object
445 def __import16MaerialAndMesh(meshObject, l,
446 material_order, face_map, tex_dir, toon_material):
448 mesh=bl.object.getData(meshObject)
449 ############################################################
451 ############################################################
452 bl.progress_print('create materials')
458 for material_index in material_order:
460 m=l.materials[material_index]
461 mesh_material_map[material_index]=index
465 material=createPmdMaterial(m, material_index)
468 texture_name=m.getTexture()
470 for i, t in enumerate(texture_name.split('*')):
472 texture=textureMap[t]
474 path=os.path.join(tex_dir, t)
475 texture, image=bl.texture.create(path)
476 textureMap[texture_name]=texture
477 imageMap[material_index]=image
478 texture_index=bl.material.addTexture(material, texture)
479 if t.endswith('sph'):
481 setSphereMap(material, texture_index)
482 elif t.endswith('spa'):
484 setSphereMap(material, texture_index, 'ADD')
487 toon_index=bl.material.addTexture(
489 bl.material.getTexture(
491 0 if m.toon_index==0xFF else m.toon_index
495 bl.mesh.addMaterial(mesh, material)
499 ############################################################
501 ############################################################
502 bl.progress_print('create vertices')
505 for v in l.each_vertex():
506 vertices.append(convert_coord(v.pos))
508 ############################################################
510 ############################################################
511 bl.progress_print('create faces')
514 mesh_face_materials=[]
517 for material_index in material_order:
518 face_offset=face_map[material_index]
519 m=l.materials[material_index]
520 material_faces=l.indices[face_offset:face_offset+m.vertex_count]
522 def degenerate(i0, i1, i2):
526 return i0==i1 or i1==i2 or i2==i0
528 for j in xrange(0, len(material_faces), 3):
530 i1=material_faces[j+1]
531 i2=material_faces[j+2]
533 triangle=[i2, i1, i0]
534 if degenerate(*triangle):
536 mesh_face_indices.append(triangle[0:3])
537 mesh_face_materials.append(material_index)
538 used_vertices.add(i0)
539 used_vertices.add(i1)
540 used_vertices.add(i2)
542 ############################################################
543 # create vertices & faces
544 ############################################################
545 bl.mesh.addGeometry(mesh, vertices, mesh_face_indices)
547 ############################################################
549 ############################################################
550 # create vertex group
552 for v in l.each_vertex():
553 vertex_groups[v.bone0]=True
554 vertex_groups[v.bone1]=True
555 for i in vertex_groups.keys():
556 bl.object.addVertexGroup(meshObject, get_bone_name(l, i))
559 bl.mesh.useVertexUV(mesh)
560 for i, v, mvert in zip(xrange(len(l.vertices)),
561 l.each_vertex(), mesh.verts):
563 bl.vertex.setNormal(mvert, convert_coord(v.normal))
565 w1=float(v.weight0)/100.0
567 bl.object.assignVertexGroup(meshObject, get_bone_name(l, v.bone0),
569 bl.object.assignVertexGroup(meshObject, get_bone_name(l, v.bone1),
572 ############################################################
574 ############################################################
577 for i, (face, material_index) in enumerate(
578 zip(mesh.faces, mesh_face_materials)):
580 index=mesh_material_map[material_index]
581 except KeyError as message:
582 print(message, mesh_material_map, m)
584 bl.face.setMaterial(face, index)
585 material=mesh.materials[index]
587 if bl.material.hasTexture(material):
588 uv_array=[l.getUV(i) for i in bl.face.getIndices(face)]
589 bl.mesh.setFaceUV(mesh, i, face,
591 [(uv.x, 1.0-uv.y) for uv in uv_array],
592 imageMap.get(index, None))
599 ############################################################
600 # clean up not used vertices
601 ############################################################
602 bl.progress_print('clean up vertices not used')
605 for i, v in enumerate(l.each_vertex()):
606 if i in used_vertices:
607 vertex_map[i]=len(vertex_map)
609 remove_vertices.append(i)
611 bl.mesh.vertsDelete(mesh, remove_vertices)
613 bl.progress_print('%s created' % mesh.name)
617 def __importMaterialAndMesh(io, tex_dir, toon_material):
619 @param l[in] mmd.PMDLoader
622 ############################################################
623 # shpaeキーで使われるマテリアル優先的に前に並べる
624 ############################################################
625 # shapeキーで使われる頂点インデックスを集める
626 shape_key_used_vertices=set()
627 if len(io.morph_list)>0:
630 for s in io.morph_list:
637 for index in base.indices:
638 shape_key_used_vertices.add(index)
640 # マテリアルに含まれる頂点がshape_keyに含まれるか否か?
641 def isMaterialUsedInShape(offset, m):
642 for i in xrange(offset, offset+m.vertex_count):
643 if io.indices[i] in shape_key_used_vertices:
646 material_with_shape=set()
648 # 各マテリアルの開始頂点インデックスを記録する
651 for i, m in enumerate(io.materials):
652 face_map[i]=face_count
653 if isMaterialUsedInShape(face_count, m):
654 material_with_shape.add(i)
655 face_count+=m.vertex_count
657 # shapeキーで使われる頂点のあるマテリアル
658 material_with_shape=list(material_with_shape)
659 material_with_shape.sort()
661 # shapeキーに使われていないマテリアル
662 material_without_shape=[]
663 for i in range(len(io.materials)):
664 if not i in material_with_shape:
665 material_without_shape.append(i)
668 def __splitList(l, length):
669 for i in range(0, len(l), length):
672 def __importMeshAndShape(material16, name):
673 mesh, meshObject=bl.mesh.create(name)
676 bl.object.deselectAll()
677 bl.object.activate(meshObject)
679 # shapeキーで使われる順に並べなおしたマテリアル16個分の
681 vertex_map=__import16MaerialAndMesh(
682 meshObject, io, material16, face_map, tex_dir, toon_material)
685 __importShape(meshObject, io, vertex_map)
690 mesh_objects=[__importMeshAndShape(material16, 'with_shape')
691 for material16 in __splitList(material_with_shape, 16)]
693 mesh_objects+=[__importMeshAndShape(material16, 'mesh')
694 for material16 in __splitList(material_without_shape, 16)]
699 def __importConstraints(io):
702 print("create constraint")
703 container=bl.object.createEmpty('Constraints')
705 True, False, False, False, False, False, False, False, False, False,
706 False, False, False, False, False, False, False, False, False, False,
708 material=bl.material.create('constraint')
709 material.diffuse_color=(1, 0, 0)
711 for i, c in enumerate(io.constraints):
712 bpy.ops.mesh.primitive_uv_sphere_add(
716 location=(c.pos.x, c.pos.z, c.pos.y),
719 meshObject=bl.object.getActive()
720 constraintMeshes.append(meshObject)
721 mesh=bl.object.getData(meshObject)
722 bl.mesh.addMaterial(mesh, material)
723 meshObject.name='c_%d' % i
724 #meshObject.draw_transparent=True
725 #meshObject.draw_wire=True
726 meshObject.max_draw_type='SOLID'
728 meshObject.rotation_euler=(-rot.x, -rot.z, -rot.y)
730 meshObject[CONSTRAINT_NAME]=c.getName()
731 meshObject[CONSTRAINT_A]=io.rigidbodies[c.rigidA].getName()
732 meshObject[CONSTRAINT_B]=io.rigidbodies[c.rigidB].getName()
733 meshObject[CONSTRAINT_POS_MIN]=VtoV(c.constraintPosMin)
734 meshObject[CONSTRAINT_POS_MAX]=VtoV(c.constraintPosMax)
735 meshObject[CONSTRAINT_ROT_MIN]=VtoV(c.constraintRotMin)
736 meshObject[CONSTRAINT_ROT_MAX]=VtoV(c.constraintRotMax)
737 meshObject[CONSTRAINT_SPRING_POS]=VtoV(c.springPos)
738 meshObject[CONSTRAINT_SPRING_ROT]=VtoV(c.springRot)
740 for meshObject in reversed(constraintMeshes):
741 bl.object.makeParent(container, meshObject)
746 def __importRigidBodies(io):
749 print("create rigid bodies")
751 container=bl.object.createEmpty('RigidBodies')
753 True, False, False, False, False, False, False, False, False, False,
754 False, False, False, False, False, False, False, False, False, False,
756 material=bl.material.create('rigidBody')
758 for i, rigid in enumerate(io.rigidbodies):
759 if rigid.boneIndex==0xFFFF:
763 bone=io.bones[rigid.boneIndex]
764 pos=bone.pos+rigid.position
766 if rigid.shapeType==pmd.SHAPE_SPHERE:
767 bpy.ops.mesh.primitive_ico_sphere_add(
768 location=(pos.x, pos.z, pos.y),
771 bpy.ops.transform.resize(
772 value=(rigid.w, rigid.w, rigid.w))
773 elif rigid.shapeType==pmd.SHAPE_BOX:
774 bpy.ops.mesh.primitive_cube_add(
775 location=(pos.x, pos.z, pos.y),
778 bpy.ops.transform.resize(
779 value=(rigid.w, rigid.d, rigid.h))
780 elif rigid.shapeType==pmd.SHAPE_CAPSULE:
781 bpy.ops.mesh.primitive_tube_add(
782 location=(pos.x, pos.z, pos.y),
785 bpy.ops.transform.resize(
786 value=(rigid.w, rigid.w, rigid.h))
790 meshObject=bl.object.getActive()
791 mesh=bl.object.getData(meshObject)
792 rigidMeshes.append(meshObject)
793 bl.mesh.addMaterial(mesh, material)
794 meshObject.name='r_%d' % i
795 meshObject[RIGID_NAME]=rigid.getName()
796 #meshObject.draw_transparent=True
797 #meshObject.draw_wire=True
798 meshObject.max_draw_type='WIRE'
800 meshObject.rotation_euler=(-rot.x, -rot.z, -rot.y)
803 meshObject[RIGID_SHAPE_TYPE]=rigid.shapeType
804 meshObject[RIGID_PROCESS_TYPE]=rigid.processType
806 bone_name = englishmap.getEnglishBoneName(bone.getName())
808 bone_name=bone.getName()
809 meshObject[RIGID_BONE_NAME]=bone_name
811 meshObject[RIGID_GROUP]=rigid.group
812 meshObject[RIGID_INTERSECTION_GROUP]=rigid.target
813 meshObject[RIGID_WEIGHT]=rigid.weight
814 meshObject[RIGID_LINEAR_DAMPING]=rigid.linearDamping
815 meshObject[RIGID_ANGULAR_DAMPING]=rigid.angularDamping
816 meshObject[RIGID_RESTITUTION]=rigid.restitution
817 meshObject[RIGID_FRICTION]=rigid.friction
819 for meshObject in reversed(rigidMeshes):
820 bl.object.makeParent(container, meshObject)
825 def _execute(filename):
827 load pmd file to context.
831 bl.progress_set('load %s' % filename, 0.0)
834 if not io.read(filename):
835 bl.message("fail to load %s" % filename)
837 bl.progress_set('loaded %s' % filename, 0.1)
840 model_name=io.getEnglishName()
841 if len(model_name)==0:
842 model_name=io.getName()
843 root=bl.object.createEmpty(model_name)
844 root[MMD_MB_NAME]=io.getName()
845 root[MMD_MB_COMMENT]=io.getComment()
846 root[MMD_COMMENT]=io.getEnglishComment()
849 tex_dir=os.path.dirname(filename)
850 toonTextures, toonMaterial=__importToonTextures(io, tex_dir)
851 bl.object.makeParent(root, toonTextures)
854 mesh_objects=__importMaterialAndMesh(io, tex_dir, toonMaterial)
855 for o in mesh_objects:
856 bl.object.makeParent(root, o)
859 armature_object=__importArmature(io)
861 bl.object.makeParent(root, armature_object)
862 armature = bl.object.getData(armature_object)
864 # add armature modifier
865 for o in mesh_objects:
866 bl.modifier.addArmature(o, armature_object)
869 for n, b in bl.object.getPose(armature_object).bones.items():
872 # import rigid bodies
873 rigidBodies=__importRigidBodies(io)
875 bl.object.makeParent(root, rigidBodies)
878 constraints=__importConstraints(io)
880 bl.object.makeParent(root, constraints)
882 bl.object.activate(root)
887 def execute_24(filename):
888 bl.initialize('pmd_import', bpy.data.scenes.active)
889 _execute(filename.decode(bl.INTERNAL_ENCODING))
892 Blender.Window.FileSelector(
895 Blender.sys.makename(ext='.pmd'))
899 class IMPORT_OT_pmd(bpy.types.Operator):
900 bl_idname = "import_scene.pmd"
901 bl_label = 'Import PMD'
903 # List of operator properties, the attributes will be assigned
904 # to the class instance from the operator settings before calling.
905 filepath = bpy.props.StringProperty()
906 filename = bpy.props.StringProperty()
907 directory = bpy.props.StringProperty()
909 def execute(self, context):
910 bl.initialize('pmd_import', context.scene)
911 _execute(self.properties.filepath)
915 def invoke(self, context, event):
917 wm.add_fileselect(self)
918 return 'RUNNING_MODAL'
921 def menu_func(self, context):
922 self.layout.operator(IMPORT_OT_pmd.bl_idname,
923 text="MikuMikuDance model (.pmd)",
928 bpy.types.register(IMPORT_OT_pmd)
929 bpy.types.INFO_MT_file_import.append(menu_func)
932 bpy.types.unregister(IMPORT_OT_pmd)
933 bpy.types.INFO_MT_file_import.remove(menu_func)
935 if __name__=="__main__":