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.
36 2.1 20100731: add full python module.
39 'category': 'Import/Export',
40 'name': 'Import: MikuMikuDance Model Format (.pmd)',
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',
50 MMD_SHAPE_GROUP_NAME='_MMD_SHAPE'
52 MMD_MB_COMMENT='mb_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'
79 ###############################################################################
81 ###############################################################################
88 from meshio import pmd, englishmap
91 from pymeshio import englishmap
92 from pymeshio import mmd as pmd
96 return sys.version_info[0]<3
101 from Blender import Mathutils
107 def createPmdMaterial(m, index):
108 material=Blender.Material.New()
110 material.setDiffuseShader(Blender.Material.Shaders.DIFFUSE_TOON)
111 material.setRGBCol([m.diffuse.r, m.diffuse.g, m.diffuse.b])
112 material.setAlpha(m.diffuse.a)
114 material.setSpecShader(Blender.Material.Shaders.SPEC_TOON)
115 material.setSpec(m.shinness*0.1)
116 material.setSpecCol([m.specular.r, m.specular.g, m.specular.b])
118 material.setMirCol([m.ambient.r, m.ambient.g, m.ambient.b])
120 material.enableSSS=True if m.flag==1 else False
122 material.name="m_%02d" % index
125 def poseBoneLimit(n, b):
128 if n.startswith("knee_"):
133 b.limitMax=[180, 0, 0]
134 elif n.startswith("ankle_"):
137 def setSphereMap(material, index, blend_type='MULTIPLY'):
138 slot=material.textures[index]
139 slot.mapto=Blender.Texture.MapTo.NOR
140 slot.mapping=Blender.Texture.Mappings.SPHERE
141 if blend_type=='MULTIPLY':
142 slot.blendmode=Blender.Texture.BlendModes.MULTIPLY
143 elif blend_type=='ADD':
144 slot.blendmode=Blender.Texture.BlendModes.ADD
156 def createPmdMaterial(m, index):
157 material = bpy.data.materials.new("Material")
159 material.diffuse_shader='FRESNEL'
160 material.diffuse_color=([m.diffuse.r, m.diffuse.g, m.diffuse.b])
161 material.alpha=m.diffuse.a
163 material.specular_shader='TOON'
164 material.specular_color=([m.specular.r, m.specular.g, m.specular.b])
165 material.specular_toon_size=int(m.shinness)
167 material.mirror_color=([m.ambient.r, m.ambient.g, m.ambient.b])
169 material.subsurface_scattering.enabled=True if m.flag==1 else False
171 material.name="m_%02d" % index
172 material.preview_render_type='FLAT'
173 material.transparency=True
176 def poseBoneLimit(n, b):
179 if n.startswith("knee_"):
186 elif n.startswith("ankle_"):
190 def setSphereMap(material, index, blend_type='MULTIPLY'):
191 slot=material.texture_slots[index]
192 slot.texture_coordinates='NORMAL'
193 slot.mapping='SPHERE'
194 slot.blend_type=blend_type
197 ###############################################################################
199 return bl.createVector(v.x, v.y, v.z)
202 def convert_coord(pos):
204 Left handed y-up to Right handed z-up
206 return (pos.x, pos.z, pos.y)
209 def to_radian(degree):
210 return math.pi * degree / 180
213 def get_bone_name(l, index):
215 return l.bones[0].getName()
217 if index < len(l.bones):
218 name=englishmap.getEnglishBoneName(l.bones[index].getName())
221 return l.bones[index].getName()
222 print('invalid bone index', index)
223 return l.bones[0].getName()
226 def get_group_name(g):
227 group_name=englishmap.getEnglishBoneGroupName(g.getName().strip())
229 group_name=g.getName().strip()
233 def __importToonTextures(io, tex_dir):
234 mesh, meshObject=bl.mesh.create(TOON_TEXTURE_OBJECT)
235 material=bl.material.create(TOON_TEXTURE_OBJECT)
236 bl.mesh.addMaterial(mesh, material)
238 t=io.getToonTexture(i)
239 path=os.path.join(tex_dir, t.getName())
240 texture, image=bl.texture.create(path)
241 bl.material.addTexture(material, texture, False)
242 return meshObject, material
245 def __importShape(obj, l, vertex_map):
246 if len(l.morph_list)==0:
250 bl.object.pinShape(obj, True)
254 for s in l.morph_list:
258 # create vertex group
259 bl.object.addVertexGroup(obj, MMD_SHAPE_GROUP_NAME)
264 bl.object.assignVertexGroup(
265 obj, MMD_SHAPE_GROUP_NAME, vertex_map[i], 0)
271 baseShapeBlock=bl.object.addShapeKey(obj, BASE_SHAPE_NAME)
273 mesh=bl.object.getData(obj)
277 for s in l.morph_list:
282 name=englishmap.getEnglishSkinName(s.getName())
288 for index, offset in zip(s.indices, s.pos_list):
290 vertex_index=vertex_map[base.indices[index]]
291 v=mesh.verts[vertex_index].co
292 offset=convert_coord(offset)
296 except IndexError as msg:
298 print(index, len(base.indices), len(vertex_map))
299 print(len(mesh.verts))
300 print(base.indices[index])
304 #print 'this mesh not has shape vertices'
307 # create shapekey block
308 new_shape_key=bl.object.addShapeKey(obj, name)
310 # copy vertex to shape key
314 for mv, v in zip(mesh.verts, baseShapeBlock.getData()):
322 new_shape_key=bl.object.addShapeKey(obj, name)
324 for index, offset in zip(s.indices, s.pos_list):
326 vertex_index=vertex_map[base.indices[index]]
327 bl.shapekey.assign(new_shape_key, vertex_index,
328 mesh.verts[vertex_index].co+
329 bl.createVector(*convert_coord(offset)))
330 except IndexError as msg:
332 print(index, len(base.indices), len(vertex_map))
333 print(len(mesh.verts))
334 print(base.indices[index])
338 #print 'this mesh not has shape vertices'
342 bl.object.setActivateShapeKey(obj, 0)
345 def __build(armature, b, p, parent):
346 name=englishmap.getEnglishBoneName(b.getName())
350 bone=bl.armature.createBone(armature, name)
352 if parent and (b.tail_index==0 or b.type==6 or b.type==7 or b.type==9):
354 bone.head = bl.createVector(*convert_coord(b.pos))
355 bone.tail=bone.head+bl.createVector(0, 1, 0)
357 if bone.name=="center_t":
358 # センターボーンは(0, 1, 0)の方向を向いていないと具合が悪い
359 parent.tail=parent.head+bl.createVector(0, 1, 0)
360 bone.head=parent.tail
361 bone.tail=bone.head+bl.createVector(0, 1, 0)
363 if parent.tail==bone.head:
366 print('diffurence with parent.tail and head', name)
369 bl.bone.setConnected(bone)
371 bl.bone.setLayerMask(bone, [0, 1])
374 bone.head = bl.createVector(*convert_coord(b.pos))
375 bone.tail = bl.createVector(*convert_coord(b.tail))
378 if parent.tail==bone.head:
379 bl.bone.setConnected(bone)
381 if bone.head==bone.tail:
382 bone.tail=bone.head+bl.createVector(0, 1, 0)
385 __build(armature, c, b, bone)
388 def __importArmature(l):
389 armature, armature_object=bl.armature.create()
392 bl.armature.makeEditable(armature_object)
395 __build(armature, b, None, None)
396 bl.armature.update(armature)
400 pose = bl.object.getPose(armature_object)
402 target=l.bones[ik.target]
403 name = englishmap.getEnglishBoneName(target.getName())
405 name=target.getName()
406 p_bone = pose.bones[name]
408 print('not found', name)
410 if len(ik.children) >= 16:
411 print('over MAX_CHAINLEN', ik, len(ik.children))
413 effector_name=englishmap.getEnglishBoneName(
414 l.bones[ik.index].getName())
415 if not effector_name:
416 effector_name=l.bones[ik.index].getName()
418 constraint=bl.armature.createIkConstraint(armature_object,
419 p_bone, effector_name, ik)
421 bl.armature.makeEditable(armature_object)
422 bl.armature.update(armature)
429 for i, g in enumerate(l.bone_group_list):
430 name=get_group_name(g)
431 bl.object.createBoneGroup(armature_object, name, "THEME%02d" % (i+1))
433 # assign bone to group
434 for b_index, g_index in l.bone_display_list:
437 bone_name=englishmap.getEnglishBoneName(b.getName())
439 bone_name=b.getName()
441 g=l.bone_group_list[g_index-1]
442 group_name=get_group_name(g)
445 pose.bones[bone_name].bone_group=pose.bone_groups[group_name]
449 return armature_object
452 def __import16MaerialAndMesh(meshObject, l,
453 material_order, face_map, tex_dir, toon_material):
455 mesh=bl.object.getData(meshObject)
456 ############################################################
458 ############################################################
459 bl.progress_print('create materials')
465 for material_index in material_order:
467 m=l.materials[material_index]
468 mesh_material_map[material_index]=index
472 material=createPmdMaterial(m, material_index)
475 texture_name=m.getTexture()
477 for i, t in enumerate(texture_name.split('*')):
479 texture=textureMap[t]
481 path=os.path.join(tex_dir, t)
482 texture, image=bl.texture.create(path)
483 textureMap[texture_name]=texture
484 imageMap[material_index]=image
485 texture_index=bl.material.addTexture(material, texture)
486 if t.endswith('sph'):
488 setSphereMap(material, texture_index)
489 elif t.endswith('spa'):
491 setSphereMap(material, texture_index, 'ADD')
494 toon_index=bl.material.addTexture(
496 bl.material.getTexture(
498 0 if m.toon_index==0xFF else m.toon_index
502 bl.mesh.addMaterial(mesh, material)
506 ############################################################
508 ############################################################
509 bl.progress_print('create vertices')
512 for v in l.each_vertex():
513 vertices.append(convert_coord(v.pos))
515 ############################################################
517 ############################################################
518 bl.progress_print('create faces')
521 mesh_face_materials=[]
524 for material_index in material_order:
525 face_offset=face_map[material_index]
526 m=l.materials[material_index]
527 material_faces=l.indices[face_offset:face_offset+m.vertex_count]
529 def degenerate(i0, i1, i2):
533 return i0==i1 or i1==i2 or i2==i0
535 for j in xrange(0, len(material_faces), 3):
537 i1=material_faces[j+1]
538 i2=material_faces[j+2]
540 triangle=[i2, i1, i0]
541 if degenerate(*triangle):
543 mesh_face_indices.append(triangle[0:3])
544 mesh_face_materials.append(material_index)
545 used_vertices.add(i0)
546 used_vertices.add(i1)
547 used_vertices.add(i2)
549 ############################################################
550 # create vertices & faces
551 ############################################################
552 bl.mesh.addGeometry(mesh, vertices, mesh_face_indices)
554 ############################################################
556 ############################################################
557 # create vertex group
559 for v in l.each_vertex():
560 vertex_groups[v.bone0]=True
561 vertex_groups[v.bone1]=True
562 for i in vertex_groups.keys():
563 bl.object.addVertexGroup(meshObject, get_bone_name(l, i))
566 bl.mesh.useVertexUV(mesh)
567 for i, v, mvert in zip(xrange(len(l.vertices)),
568 l.each_vertex(), mesh.verts):
570 bl.vertex.setNormal(mvert, convert_coord(v.normal))
572 w1=float(v.weight0)/100.0
574 bl.object.assignVertexGroup(meshObject, get_bone_name(l, v.bone0),
576 bl.object.assignVertexGroup(meshObject, get_bone_name(l, v.bone1),
579 ############################################################
581 ############################################################
584 for i, (face, material_index) in enumerate(
585 zip(mesh.faces, mesh_face_materials)):
587 index=mesh_material_map[material_index]
588 except KeyError as message:
589 print(message, mesh_material_map, m)
591 bl.face.setMaterial(face, index)
592 material=mesh.materials[index]
594 if bl.material.hasTexture(material):
595 uv_array=[l.getUV(i) for i in bl.face.getIndices(face)]
596 bl.mesh.setFaceUV(mesh, i, face,
598 [(uv.x, 1.0-uv.y) for uv in uv_array],
599 imageMap.get(index, None))
606 ############################################################
607 # clean up not used vertices
608 ############################################################
609 bl.progress_print('clean up vertices not used')
612 for i, v in enumerate(l.each_vertex()):
613 if i in used_vertices:
614 vertex_map[i]=len(vertex_map)
616 remove_vertices.append(i)
618 bl.mesh.vertsDelete(mesh, remove_vertices)
620 bl.progress_print('%s created' % mesh.name)
624 def __importMaterialAndMesh(io, tex_dir, toon_material):
626 @param l[in] mmd.PMDLoader
629 ############################################################
630 # shpaeキーで使われるマテリアル優先的に前に並べる
631 ############################################################
632 # shapeキーで使われる頂点インデックスを集める
633 shape_key_used_vertices=set()
634 if len(io.morph_list)>0:
637 for s in io.morph_list:
644 for index in base.indices:
645 shape_key_used_vertices.add(index)
647 # マテリアルに含まれる頂点がshape_keyに含まれるか否か?
648 def isMaterialUsedInShape(offset, m):
649 for i in xrange(offset, offset+m.vertex_count):
650 if io.indices[i] in shape_key_used_vertices:
653 material_with_shape=set()
655 # 各マテリアルの開始頂点インデックスを記録する
658 for i, m in enumerate(io.materials):
659 face_map[i]=face_count
660 if isMaterialUsedInShape(face_count, m):
661 material_with_shape.add(i)
662 face_count+=m.vertex_count
664 # shapeキーで使われる頂点のあるマテリアル
665 material_with_shape=list(material_with_shape)
666 material_with_shape.sort()
668 # shapeキーに使われていないマテリアル
669 material_without_shape=[]
670 for i in range(len(io.materials)):
671 if not i in material_with_shape:
672 material_without_shape.append(i)
675 def __splitList(l, length):
676 for i in range(0, len(l), length):
679 def __importMeshAndShape(material16, name):
680 mesh, meshObject=bl.mesh.create(name)
683 bl.object.deselectAll()
684 bl.object.activate(meshObject)
686 # shapeキーで使われる順に並べなおしたマテリアル16個分の
688 vertex_map=__import16MaerialAndMesh(
689 meshObject, io, material16, face_map, tex_dir, toon_material)
692 __importShape(meshObject, io, vertex_map)
697 mesh_objects=[__importMeshAndShape(material16, 'with_shape')
698 for material16 in __splitList(material_with_shape, 16)]
700 mesh_objects+=[__importMeshAndShape(material16, 'mesh')
701 for material16 in __splitList(material_without_shape, 16)]
706 def __importConstraints(io):
709 print("create constraint")
710 container=bl.object.createEmpty('Constraints')
712 True, False, False, False, False, False, False, False, False, False,
713 False, False, False, False, False, False, False, False, False, False,
715 material=bl.material.create('constraint')
716 material.diffuse_color=(1, 0, 0)
718 for i, c in enumerate(io.constraints):
719 bpy.ops.mesh.primitive_uv_sphere_add(
723 location=(c.pos.x, c.pos.z, c.pos.y),
726 meshObject=bl.object.getActive()
727 constraintMeshes.append(meshObject)
728 mesh=bl.object.getData(meshObject)
729 bl.mesh.addMaterial(mesh, material)
730 meshObject.name='c_%d' % i
731 #meshObject.draw_transparent=True
732 #meshObject.draw_wire=True
733 meshObject.max_draw_type='SOLID'
735 meshObject.rotation_euler=(-rot.x, -rot.z, -rot.y)
737 meshObject[CONSTRAINT_NAME]=c.getName()
738 meshObject[CONSTRAINT_A]=io.rigidbodies[c.rigidA].getName()
739 meshObject[CONSTRAINT_B]=io.rigidbodies[c.rigidB].getName()
740 meshObject[CONSTRAINT_POS_MIN]=VtoV(c.constraintPosMin)
741 meshObject[CONSTRAINT_POS_MAX]=VtoV(c.constraintPosMax)
742 meshObject[CONSTRAINT_ROT_MIN]=VtoV(c.constraintRotMin)
743 meshObject[CONSTRAINT_ROT_MAX]=VtoV(c.constraintRotMax)
744 meshObject[CONSTRAINT_SPRING_POS]=VtoV(c.springPos)
745 meshObject[CONSTRAINT_SPRING_ROT]=VtoV(c.springRot)
747 for meshObject in reversed(constraintMeshes):
748 bl.object.makeParent(container, meshObject)
753 def __importRigidBodies(io):
756 print("create rigid bodies")
758 container=bl.object.createEmpty('RigidBodies')
760 True, False, False, False, False, False, False, False, False, False,
761 False, False, False, False, False, False, False, False, False, False,
763 material=bl.material.create('rigidBody')
765 for i, rigid in enumerate(io.rigidbodies):
766 if rigid.boneIndex==0xFFFF:
770 bone=io.bones[rigid.boneIndex]
771 pos=bone.pos+rigid.position
773 if rigid.shapeType==pmd.SHAPE_SPHERE:
774 bpy.ops.mesh.primitive_ico_sphere_add(
775 location=(pos.x, pos.z, pos.y),
778 bpy.ops.transform.resize(
779 value=(rigid.w, rigid.w, rigid.w))
780 elif rigid.shapeType==pmd.SHAPE_BOX:
781 bpy.ops.mesh.primitive_cube_add(
782 location=(pos.x, pos.z, pos.y),
785 bpy.ops.transform.resize(
786 value=(rigid.w, rigid.d, rigid.h))
787 elif rigid.shapeType==pmd.SHAPE_CAPSULE:
788 bpy.ops.mesh.primitive_tube_add(
789 location=(pos.x, pos.z, pos.y),
792 bpy.ops.transform.resize(
793 value=(rigid.w, rigid.w, rigid.h))
797 meshObject=bl.object.getActive()
798 mesh=bl.object.getData(meshObject)
799 rigidMeshes.append(meshObject)
800 bl.mesh.addMaterial(mesh, material)
801 meshObject.name='r_%d' % i
802 meshObject[RIGID_NAME]=rigid.getName()
803 #meshObject.draw_transparent=True
804 #meshObject.draw_wire=True
805 meshObject.max_draw_type='WIRE'
807 meshObject.rotation_euler=(-rot.x, -rot.z, -rot.y)
810 meshObject[RIGID_SHAPE_TYPE]=rigid.shapeType
811 meshObject[RIGID_PROCESS_TYPE]=rigid.processType
813 bone_name = englishmap.getEnglishBoneName(bone.getName())
815 bone_name=bone.getName()
816 meshObject[RIGID_BONE_NAME]=bone_name
818 meshObject[RIGID_GROUP]=rigid.group
819 meshObject[RIGID_INTERSECTION_GROUP]=rigid.target
820 meshObject[RIGID_WEIGHT]=rigid.weight
821 meshObject[RIGID_LINEAR_DAMPING]=rigid.linearDamping
822 meshObject[RIGID_ANGULAR_DAMPING]=rigid.angularDamping
823 meshObject[RIGID_RESTITUTION]=rigid.restitution
824 meshObject[RIGID_FRICTION]=rigid.friction
826 for meshObject in reversed(rigidMeshes):
827 bl.object.makeParent(container, meshObject)
832 def _execute(filename):
834 load pmd file to context.
838 bl.progress_set('load %s' % filename, 0.0)
841 if not io.read(filename):
842 bl.message("fail to load %s" % filename)
844 bl.progress_set('loaded %s' % filename, 0.1)
847 model_name=io.getEnglishName()
848 if len(model_name)==0:
849 model_name=io.getName()
850 root=bl.object.createEmpty(model_name)
851 root[MMD_MB_NAME]=io.getName()
852 root[MMD_MB_COMMENT]=io.getComment()
853 root[MMD_COMMENT]=io.getEnglishComment()
856 tex_dir=os.path.dirname(filename)
857 toonTextures, toonMaterial=__importToonTextures(io, tex_dir)
858 bl.object.makeParent(root, toonTextures)
861 mesh_objects=__importMaterialAndMesh(io, tex_dir, toonMaterial)
862 for o in mesh_objects:
863 bl.object.makeParent(root, o)
866 armature_object=__importArmature(io)
868 bl.object.makeParent(root, armature_object)
869 armature = bl.object.getData(armature_object)
871 # add armature modifier
872 for o in mesh_objects:
873 bl.modifier.addArmature(o, armature_object)
876 for n, b in bl.object.getPose(armature_object).bones.items():
879 # import rigid bodies
880 rigidBodies=__importRigidBodies(io)
882 bl.object.makeParent(root, rigidBodies)
885 constraints=__importConstraints(io)
887 bl.object.makeParent(root, constraints)
889 bl.object.activate(root)
894 def execute_24(filename):
895 bl.initialize('pmd_import', bpy.data.scenes.active)
896 _execute(filename.decode(bl.INTERNAL_ENCODING))
899 Blender.Window.FileSelector(
902 Blender.sys.makename(ext='.pmd'))
906 class IMPORT_OT_pmd(bpy.types.Operator):
907 bl_idname = "import_scene.pmd"
908 bl_label = 'Import PMD'
910 # List of operator properties, the attributes will be assigned
911 # to the class instance from the operator settings before calling.
912 filepath = bpy.props.StringProperty()
913 filename = bpy.props.StringProperty()
914 directory = bpy.props.StringProperty()
916 def execute(self, context):
917 bl.initialize('pmd_import', context.scene)
918 _execute(self.properties.filepath)
922 def invoke(self, context, event):
924 wm.add_fileselect(self)
925 return 'RUNNING_MODAL'
928 def menu_func(self, context):
929 self.layout.operator(IMPORT_OT_pmd.bl_idname,
930 text="MikuMikuDance model (.pmd)",
935 bpy.types.register(IMPORT_OT_pmd)
936 bpy.types.INFO_MT_file_import.append(menu_func)
939 bpy.types.unregister(IMPORT_OT_pmd)
940 bpy.types.INFO_MT_file_import.remove(menu_func)
942 if __name__=="__main__":