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
89 print('use meshio C module')
92 from pymeshio import englishmap
93 from pymeshio import mmd as pmd
97 return sys.version_info[0]<3
102 from Blender import Mathutils
108 def createPmdMaterial(m, index):
109 material=Blender.Material.New()
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)
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])
119 material.setMirCol([m.ambient.r, m.ambient.g, m.ambient.b])
121 material.enableSSS=True if m.flag==1 else False
123 material.name="m_%02d" % index
126 def poseBoneLimit(n, b):
129 if n.startswith("knee_"):
134 b.limitMax=[180, 0, 0]
135 elif n.startswith("ankle_"):
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
157 def createPmdMaterial(m, index):
158 material = bpy.data.materials.new("Material")
160 material.diffuse_shader='FRESNEL'
161 material.diffuse_color=([m.diffuse.r, m.diffuse.g, m.diffuse.b])
162 material.alpha=m.diffuse.a
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)
168 material.mirror_color=([m.ambient.r, m.ambient.g, m.ambient.b])
170 material.subsurface_scattering.enabled=True if m.flag==1 else False
172 material.name="m_%02d" % index
173 material.preview_render_type='FLAT'
174 material.transparency=True
177 def poseBoneLimit(n, b):
180 if n.startswith("knee_"):
187 elif n.startswith("ankle_"):
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
198 ###############################################################################
200 return bl.createVector(v.x, v.y, v.z)
203 def convert_coord(pos):
205 Left handed y-up to Right handed z-up
207 return (pos.x, pos.z, pos.y)
210 def to_radian(degree):
211 return math.pi * degree / 180
214 def get_bone_name(l, index):
216 return l.bones[0].getName()
218 if index < len(l.bones):
219 name=englishmap.getEnglishBoneName(l.bones[index].getName())
222 return l.bones[index].getName()
223 print('invalid bone index', index)
224 return l.bones[0].getName()
227 def get_group_name(g):
228 group_name=englishmap.getEnglishBoneGroupName(g.getName().strip())
230 group_name=g.getName().strip()
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)
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
246 def __importShape(obj, l, vertex_map):
247 if len(l.morph_list)==0:
251 bl.object.pinShape(obj, True)
255 for s in l.morph_list:
259 # create vertex group
260 bl.object.addVertexGroup(obj, MMD_SHAPE_GROUP_NAME)
265 bl.object.assignVertexGroup(
266 obj, MMD_SHAPE_GROUP_NAME, vertex_map[i], 0)
272 baseShapeBlock=bl.object.addShapeKey(obj, BASE_SHAPE_NAME)
274 mesh=bl.object.getData(obj)
278 for s in l.morph_list:
283 name=englishmap.getEnglishSkinName(s.getName())
289 for index, offset in zip(s.indices, s.pos_list):
291 vertex_index=vertex_map[base.indices[index]]
292 v=mesh.verts[vertex_index].co
293 offset=convert_coord(offset)
297 except IndexError as msg:
299 print(index, len(base.indices), len(vertex_map))
300 print(len(mesh.verts))
301 print(base.indices[index])
305 #print 'this mesh not has shape vertices'
308 # create shapekey block
309 new_shape_key=bl.object.addShapeKey(obj, name)
311 # copy vertex to shape key
315 for mv, v in zip(mesh.verts, baseShapeBlock.getData()):
323 new_shape_key=bl.object.addShapeKey(obj, name)
325 for index, offset in zip(s.indices, s.pos_list):
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:
333 print(index, len(base.indices), len(vertex_map))
334 print(len(mesh.verts))
335 print(base.indices[index])
339 #print 'this mesh not has shape vertices'
343 bl.object.setActivateShapeKey(obj, 0)
346 def __build(armature, b, p, parent):
347 name=englishmap.getEnglishBoneName(b.getName())
351 bone=bl.armature.createBone(armature, name)
353 if parent and (b.tail_index==0 or b.type==6 or b.type==7 or b.type==9):
355 bone.head = bl.createVector(*convert_coord(b.pos))
356 bone.tail=bone.head+bl.createVector(0, 1, 0)
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)
364 if parent.tail==bone.head:
367 print('diffurence with parent.tail and head', name)
370 bl.bone.setConnected(bone)
372 bl.bone.setLayerMask(bone, [0, 1])
375 bone.head = bl.createVector(*convert_coord(b.pos))
376 bone.tail = bl.createVector(*convert_coord(b.tail))
379 if parent.tail==bone.head:
380 bl.bone.setConnected(bone)
382 if bone.head==bone.tail:
383 bone.tail=bone.head+bl.createVector(0, 1, 0)
386 __build(armature, c, b, bone)
389 def __importArmature(l):
390 armature, armature_object=bl.armature.create()
393 bl.armature.makeEditable(armature_object)
396 __build(armature, b, None, None)
397 bl.armature.update(armature)
401 pose = bl.object.getPose(armature_object)
403 target=l.bones[ik.target]
404 name = englishmap.getEnglishBoneName(target.getName())
406 name=target.getName()
407 p_bone = pose.bones[name]
409 print('not found', name)
411 if len(ik.children) >= 16:
412 print('over MAX_CHAINLEN', ik, len(ik.children))
414 effector_name=englishmap.getEnglishBoneName(
415 l.bones[ik.index].getName())
416 if not effector_name:
417 effector_name=l.bones[ik.index].getName()
419 constraint=bl.armature.createIkConstraint(armature_object,
420 p_bone, effector_name, ik)
422 bl.armature.makeEditable(armature_object)
423 bl.armature.update(armature)
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))
434 # assign bone to group
435 for b_index, g_index in l.bone_display_list:
438 bone_name=englishmap.getEnglishBoneName(b.getName())
440 bone_name=b.getName()
442 g=l.bone_group_list[g_index-1]
443 group_name=get_group_name(g)
446 pose.bones[bone_name].bone_group=pose.bone_groups[group_name]
450 return armature_object
453 def __import16MaerialAndMesh(meshObject, l,
454 material_order, face_map, tex_dir, toon_material):
456 mesh=bl.object.getData(meshObject)
457 ############################################################
459 ############################################################
460 bl.progress_print('create materials')
466 for material_index in material_order:
468 m=l.materials[material_index]
469 mesh_material_map[material_index]=index
473 material=createPmdMaterial(m, material_index)
476 texture_name=m.getTexture()
478 for i, t in enumerate(texture_name.split('*')):
480 texture=textureMap[t]
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'):
489 setSphereMap(material, texture_index)
490 elif t.endswith('spa'):
492 setSphereMap(material, texture_index, 'ADD')
495 toon_index=bl.material.addTexture(
497 bl.material.getTexture(
499 0 if m.toon_index==0xFF else m.toon_index
503 bl.mesh.addMaterial(mesh, material)
507 ############################################################
509 ############################################################
510 bl.progress_print('create vertices')
513 for v in l.each_vertex():
514 vertices.append(convert_coord(v.pos))
516 ############################################################
518 ############################################################
519 bl.progress_print('create faces')
522 mesh_face_materials=[]
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]
530 def degenerate(i0, i1, i2):
534 return i0==i1 or i1==i2 or i2==i0
536 for j in xrange(0, len(material_faces), 3):
538 i1=material_faces[j+1]
539 i2=material_faces[j+2]
541 triangle=[i2, i1, i0]
542 if degenerate(*triangle):
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)
550 ############################################################
551 # create vertices & faces
552 ############################################################
553 bl.mesh.addGeometry(mesh, vertices, mesh_face_indices)
555 ############################################################
557 ############################################################
558 # create vertex group
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))
567 bl.mesh.useVertexUV(mesh)
568 for i, v, mvert in zip(xrange(len(l.vertices)),
569 l.each_vertex(), mesh.verts):
571 bl.vertex.setNormal(mvert, convert_coord(v.normal))
573 w1=float(v.weight0)/100.0
575 bl.object.assignVertexGroup(meshObject, get_bone_name(l, v.bone0),
577 bl.object.assignVertexGroup(meshObject, get_bone_name(l, v.bone1),
580 ############################################################
582 ############################################################
585 for i, (face, material_index) in enumerate(
586 zip(mesh.faces, mesh_face_materials)):
588 index=mesh_material_map[material_index]
589 except KeyError as message:
590 print(message, mesh_material_map, m)
592 bl.face.setMaterial(face, index)
593 material=mesh.materials[index]
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,
599 [(uv.x, 1.0-uv.y) for uv in uv_array],
600 imageMap.get(index, None))
607 ############################################################
608 # clean up not used vertices
609 ############################################################
610 bl.progress_print('clean up vertices not used')
613 for i, v in enumerate(l.each_vertex()):
614 if i in used_vertices:
615 vertex_map[i]=len(vertex_map)
617 remove_vertices.append(i)
619 bl.mesh.vertsDelete(mesh, remove_vertices)
621 bl.progress_print('%s created' % mesh.name)
625 def __importMaterialAndMesh(io, tex_dir, toon_material):
627 @param l[in] mmd.PMDLoader
630 ############################################################
631 # shpaeキーで使われるマテリアル優先的に前に並べる
632 ############################################################
633 # shapeキーで使われる頂点インデックスを集める
634 shape_key_used_vertices=set()
635 if len(io.morph_list)>0:
638 for s in io.morph_list:
645 for index in base.indices:
646 shape_key_used_vertices.add(index)
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:
654 material_with_shape=set()
656 # 各マテリアルの開始頂点インデックスを記録する
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
665 # shapeキーで使われる頂点のあるマテリアル
666 material_with_shape=list(material_with_shape)
667 material_with_shape.sort()
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)
676 def __splitList(l, length):
677 for i in range(0, len(l), length):
680 def __importMeshAndShape(material16, name):
681 mesh, meshObject=bl.mesh.create(name)
684 bl.object.deselectAll()
685 bl.object.activate(meshObject)
687 # shapeキーで使われる順に並べなおしたマテリアル16個分の
689 vertex_map=__import16MaerialAndMesh(
690 meshObject, io, material16, face_map, tex_dir, toon_material)
693 __importShape(meshObject, io, vertex_map)
698 mesh_objects=[__importMeshAndShape(material16, 'with_shape')
699 for material16 in __splitList(material_with_shape, 16)]
701 mesh_objects+=[__importMeshAndShape(material16, 'mesh')
702 for material16 in __splitList(material_without_shape, 16)]
707 def __importConstraints(io):
710 print("create constraint")
711 container=bl.object.createEmpty('Constraints')
713 True, False, False, False, False, False, False, False, False, False,
714 False, False, False, False, False, False, False, False, False, False,
716 material=bl.material.create('constraint')
717 material.diffuse_color=(1, 0, 0)
719 for i, c in enumerate(io.constraints):
720 bpy.ops.mesh.primitive_uv_sphere_add(
724 location=(c.pos.x, c.pos.z, c.pos.y),
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'
736 meshObject.rotation_euler=(-rot.x, -rot.z, -rot.y)
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)
748 for meshObject in reversed(constraintMeshes):
749 bl.object.makeParent(container, meshObject)
754 def __importRigidBodies(io):
757 print("create rigid bodies")
759 container=bl.object.createEmpty('RigidBodies')
761 True, False, False, False, False, False, False, False, False, False,
762 False, False, False, False, False, False, False, False, False, False,
764 material=bl.material.create('rigidBody')
766 for i, rigid in enumerate(io.rigidbodies):
767 if rigid.boneIndex==0xFFFF:
771 bone=io.bones[rigid.boneIndex]
772 pos=bone.pos+rigid.position
774 if rigid.shapeType==pmd.SHAPE_SPHERE:
775 bpy.ops.mesh.primitive_ico_sphere_add(
776 location=(pos.x, pos.z, pos.y),
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),
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),
793 bpy.ops.transform.resize(
794 value=(rigid.w, rigid.w, rigid.h))
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'
808 meshObject.rotation_euler=(-rot.x, -rot.z, -rot.y)
811 meshObject[RIGID_SHAPE_TYPE]=rigid.shapeType
812 meshObject[RIGID_PROCESS_TYPE]=rigid.processType
814 bone_name = englishmap.getEnglishBoneName(bone.getName())
816 bone_name=bone.getName()
817 meshObject[RIGID_BONE_NAME]=bone_name
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
827 for meshObject in reversed(rigidMeshes):
828 bl.object.makeParent(container, meshObject)
833 def _execute(filename):
835 load pmd file to context.
839 bl.progress_set('load %s' % filename, 0.0)
842 if not io.read(filename):
843 bl.message("fail to load %s" % filename)
845 bl.progress_set('loaded', 0.1)
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()
857 tex_dir=os.path.dirname(filename)
858 toonTextures, toonMaterial=__importToonTextures(io, tex_dir)
859 bl.object.makeParent(root, toonTextures)
862 mesh_objects=__importMaterialAndMesh(io, tex_dir, toonMaterial)
863 for o in mesh_objects:
864 bl.object.makeParent(root, o)
867 armature_object=__importArmature(io)
869 bl.object.makeParent(root, armature_object)
870 armature = bl.object.getData(armature_object)
872 # add armature modifier
873 for o in mesh_objects:
874 bl.modifier.addArmature(o, armature_object)
877 for n, b in bl.object.getPose(armature_object).bones.items():
880 # import rigid bodies
881 rigidBodies=__importRigidBodies(io)
883 bl.object.makeParent(root, rigidBodies)
886 constraints=__importConstraints(io)
888 bl.object.makeParent(root, constraints)
890 bl.object.activate(root)
895 def execute_24(filename):
896 bl.initialize('pmd_import', bpy.data.scenes.active)
897 _execute(filename.decode(bl.INTERNAL_ENCODING))
900 Blender.Window.FileSelector(
903 Blender.sys.makename(ext='.pmd'))
907 class IMPORT_OT_pmd(bpy.types.Operator):
908 bl_idname = "import_scene.pmd"
909 bl_label = 'Import PMD'
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()
917 def execute(self, context):
918 bl.initialize('pmd_import', context.scene)
919 _execute(self.properties.filepath)
923 def invoke(self, context, event):
925 wm.add_fileselect(self)
926 return 'RUNNING_MODAL'
929 def menu_func(self, context):
930 self.layout.operator(IMPORT_OT_pmd.bl_idname,
931 text="MikuMikuDance model (.pmd)",
936 bpy.types.register(IMPORT_OT_pmd)
937 bpy.types.INFO_MT_file_import.append(menu_func)
940 bpy.types.unregister(IMPORT_OT_pmd)
941 bpy.types.INFO_MT_file_import.remove(menu_func)
943 if __name__=="__main__":