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.
37 2.2 20101005: update for Blender2.54.
40 'category': 'Import/Export',
41 'name': 'Import: MikuMikuDance Model Format (.pmd)',
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',
52 MMD_SHAPE_GROUP_NAME='_MMD_SHAPE'
54 MMD_MB_COMMENT='mb_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'
81 ###############################################################################
83 ###############################################################################
90 from meshio import pmd, englishmap
91 print('use meshio C module')
94 from pymeshio import englishmap
95 from pymeshio import mmd as pmd
99 return sys.version_info[0]<3
104 from Blender import Mathutils
110 def createPmdMaterial(m, index):
111 material=Blender.Material.New()
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)
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])
121 material.setMirCol([m.ambient.r, m.ambient.g, m.ambient.b])
123 material.enableSSS=True if m.flag==1 else False
125 material.name="m_%02d" % index
128 def poseBoneLimit(n, b):
131 if n.startswith("knee_"):
136 b.limitMax=[180, 0, 0]
137 elif n.startswith("ankle_"):
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
159 def createPmdMaterial(m, index):
160 material = bpy.data.materials.new("Material")
162 material.diffuse_shader='FRESNEL'
163 material.diffuse_color=([m.diffuse.r, m.diffuse.g, m.diffuse.b])
164 material.alpha=m.diffuse.a
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)
170 material.mirror_color=([m.ambient.r, m.ambient.g, m.ambient.b])
172 material.subsurface_scattering.enabled=True if m.flag==1 else False
174 material.name="m_%02d" % index
175 material.preview_render_type='FLAT'
176 material.transparency=True
179 def poseBoneLimit(n, b):
182 if n.startswith("knee_"):
189 elif n.startswith("ankle_"):
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
200 ###############################################################################
202 return bl.createVector(v.x, v.y, v.z)
205 def convert_coord(pos):
207 Left handed y-up to Right handed z-up
209 return (pos.x, pos.z, pos.y)
212 def to_radian(degree):
213 return math.pi * degree / 180
216 def get_bone_name(l, index):
218 return l.bones[0].getName()
220 if index < len(l.bones):
221 name=englishmap.getEnglishBoneName(l.bones[index].getName())
224 return l.bones[index].getName()
225 print('invalid bone index', index)
226 return l.bones[0].getName()
229 def get_group_name(g):
230 group_name=englishmap.getEnglishBoneGroupName(g.getName().strip())
232 group_name=g.getName().strip()
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)
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
248 def __importShape(obj, l, vertex_map):
249 if len(l.morph_list)==0:
253 bl.object.pinShape(obj, True)
257 for s in l.morph_list:
261 # create vertex group
262 bl.object.addVertexGroup(obj, MMD_SHAPE_GROUP_NAME)
267 bl.object.assignVertexGroup(
268 obj, MMD_SHAPE_GROUP_NAME, vertex_map[i], 0)
274 baseShapeBlock=bl.object.addShapeKey(obj, BASE_SHAPE_NAME)
276 mesh=bl.object.getData(obj)
280 for s in l.morph_list:
285 name=englishmap.getEnglishSkinName(s.getName())
291 for index, offset in zip(s.indices, s.pos_list):
293 vertex_index=vertex_map[base.indices[index]]
294 v=mesh.verts[vertex_index].co
295 offset=convert_coord(offset)
299 except IndexError as msg:
301 print(index, len(base.indices), len(vertex_map))
302 print(len(mesh.verts))
303 print(base.indices[index])
307 #print 'this mesh not has shape vertices'
310 # create shapekey block
311 new_shape_key=bl.object.addShapeKey(obj, name)
313 # copy vertex to shape key
317 for mv, v in zip(mesh.verts, baseShapeBlock.getData()):
325 new_shape_key=bl.object.addShapeKey(obj, name)
327 for index, offset in zip(s.indices, s.pos_list):
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:
335 print(index, len(base.indices), len(vertex_map))
336 print(len(mesh.verts))
337 print(base.indices[index])
341 #print 'this mesh not has shape vertices'
345 bl.object.setActivateShapeKey(obj, 0)
348 def __build(armature, b, p, parent):
349 name=englishmap.getEnglishBoneName(b.getName())
353 bone=bl.armature.createBone(armature, name)
355 if parent and (b.tail_index==0 or b.type==6 or b.type==7 or b.type==9):
357 bone.head = bl.createVector(*convert_coord(b.pos))
358 bone.tail=bone.head+bl.createVector(0, 1, 0)
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)
366 if parent.tail==bone.head:
369 print('diffurence with parent.tail and head', name)
372 bl.bone.setConnected(bone)
374 bl.bone.setLayerMask(bone, [0, 1])
377 bone.head = bl.createVector(*convert_coord(b.pos))
378 bone.tail = bl.createVector(*convert_coord(b.tail))
381 if parent.tail==bone.head:
382 bl.bone.setConnected(bone)
384 if bone.head==bone.tail:
385 bone.tail=bone.head+bl.createVector(0, 1, 0)
388 __build(armature, c, b, bone)
391 def __importArmature(l):
392 armature, armature_object=bl.armature.create()
395 bl.armature.makeEditable(armature_object)
398 __build(armature, b, None, None)
399 bl.armature.update(armature)
403 pose = bl.object.getPose(armature_object)
405 target=l.bones[ik.target]
406 name = englishmap.getEnglishBoneName(target.getName())
408 name=target.getName()
409 p_bone = pose.bones[name]
411 print('not found', name)
413 if len(ik.children) >= 16:
414 print('over MAX_CHAINLEN', ik, len(ik.children))
416 effector_name=englishmap.getEnglishBoneName(
417 l.bones[ik.index].getName())
418 if not effector_name:
419 effector_name=l.bones[ik.index].getName()
421 constraint=bl.armature.createIkConstraint(armature_object,
422 p_bone, effector_name, ik)
424 bl.armature.makeEditable(armature_object)
425 bl.armature.update(armature)
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))
436 # assign bone to group
437 for b_index, g_index in l.bone_display_list:
440 bone_name=englishmap.getEnglishBoneName(b.getName())
442 bone_name=b.getName()
444 g=l.bone_group_list[g_index-1]
445 group_name=get_group_name(g)
448 pose.bones[bone_name].bone_group=pose.bone_groups[group_name]
452 return armature_object
455 def __import16MaerialAndMesh(meshObject, l,
456 material_order, face_map, tex_dir, toon_material):
458 mesh=bl.object.getData(meshObject)
459 ############################################################
461 ############################################################
462 bl.progress_print('create materials')
468 for material_index in material_order:
470 m=l.materials[material_index]
471 mesh_material_map[material_index]=index
475 material=createPmdMaterial(m, material_index)
478 texture_name=m.getTexture()
480 for i, t in enumerate(texture_name.split('*')):
482 texture=textureMap[t]
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'):
491 setSphereMap(material, texture_index)
492 elif t.endswith('spa'):
494 setSphereMap(material, texture_index, 'ADD')
497 toon_index=bl.material.addTexture(
499 bl.material.getTexture(
501 0 if m.toon_index==0xFF else m.toon_index
505 bl.mesh.addMaterial(mesh, material)
509 ############################################################
511 ############################################################
512 bl.progress_print('create vertices')
515 for v in l.each_vertex():
516 vertices.append(convert_coord(v.pos))
518 ############################################################
520 ############################################################
521 bl.progress_print('create faces')
524 mesh_face_materials=[]
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]
532 def degenerate(i0, i1, i2):
536 return i0==i1 or i1==i2 or i2==i0
538 for j in xrange(0, len(material_faces), 3):
540 i1=material_faces[j+1]
541 i2=material_faces[j+2]
543 triangle=[i2, i1, i0]
544 if degenerate(*triangle):
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)
552 ############################################################
553 # create vertices & faces
554 ############################################################
555 bl.mesh.addGeometry(mesh, vertices, mesh_face_indices)
557 ############################################################
559 ############################################################
560 # create vertex group
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))
569 bl.mesh.useVertexUV(mesh)
570 for i, v, mvert in zip(xrange(len(l.vertices)),
571 l.each_vertex(), mesh.verts):
573 bl.vertex.setNormal(mvert, convert_coord(v.normal))
575 w1=float(v.weight0)/100.0
577 bl.object.assignVertexGroup(meshObject, get_bone_name(l, v.bone0),
579 bl.object.assignVertexGroup(meshObject, get_bone_name(l, v.bone1),
582 ############################################################
584 ############################################################
587 for i, (face, material_index) in enumerate(
588 zip(mesh.faces, mesh_face_materials)):
590 index=mesh_material_map[material_index]
591 except KeyError as message:
592 print(message, mesh_material_map, m)
594 bl.face.setMaterial(face, index)
595 material=mesh.materials[index]
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,
601 [(uv.x, 1.0-uv.y) for uv in uv_array],
602 imageMap.get(index, None))
609 ############################################################
610 # clean up not used vertices
611 ############################################################
612 bl.progress_print('clean up vertices not used')
615 for i, v in enumerate(l.each_vertex()):
616 if i in used_vertices:
617 vertex_map[i]=len(vertex_map)
619 remove_vertices.append(i)
621 bl.mesh.vertsDelete(mesh, remove_vertices)
623 bl.progress_print('%s created' % mesh.name)
627 def __importMaterialAndMesh(io, tex_dir, toon_material):
629 @param l[in] mmd.PMDLoader
632 ############################################################
633 # shpaeキーで使われるマテリアル優先的に前に並べる
634 ############################################################
635 # shapeキーで使われる頂点インデックスを集める
636 shape_key_used_vertices=set()
637 if len(io.morph_list)>0:
640 for s in io.morph_list:
647 for index in base.indices:
648 shape_key_used_vertices.add(index)
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:
656 material_with_shape=set()
658 # 各マテリアルの開始頂点インデックスを記録する
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
667 # shapeキーで使われる頂点のあるマテリアル
668 material_with_shape=list(material_with_shape)
669 material_with_shape.sort()
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)
678 def __splitList(l, length):
679 for i in range(0, len(l), length):
682 def __importMeshAndShape(material16, name):
683 mesh, meshObject=bl.mesh.create(name)
686 bl.object.deselectAll()
687 bl.object.activate(meshObject)
689 # shapeキーで使われる順に並べなおしたマテリアル16個分の
691 vertex_map=__import16MaerialAndMesh(
692 meshObject, io, material16, face_map, tex_dir, toon_material)
695 __importShape(meshObject, io, vertex_map)
700 mesh_objects=[__importMeshAndShape(material16, 'with_shape')
701 for material16 in __splitList(material_with_shape, 16)]
703 mesh_objects+=[__importMeshAndShape(material16, 'mesh')
704 for material16 in __splitList(material_without_shape, 16)]
709 def __importConstraints(io):
712 print("create constraint")
713 container=bl.object.createEmpty('Constraints')
715 True, False, False, False, False, False, False, False, False, False,
716 False, False, False, False, False, False, False, False, False, False,
718 material=bl.material.create('constraint')
719 material.diffuse_color=(1, 0, 0)
721 for i, c in enumerate(io.constraints):
722 bpy.ops.mesh.primitive_uv_sphere_add(
726 location=(c.pos.x, c.pos.z, c.pos.y),
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'
738 meshObject.rotation_euler=(-rot.x, -rot.z, -rot.y)
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)
750 for meshObject in reversed(constraintMeshes):
751 bl.object.makeParent(container, meshObject)
756 def __importRigidBodies(io):
759 print("create rigid bodies")
761 container=bl.object.createEmpty('RigidBodies')
763 True, False, False, False, False, False, False, False, False, False,
764 False, False, False, False, False, False, False, False, False, False,
766 material=bl.material.create('rigidBody')
768 for i, rigid in enumerate(io.rigidbodies):
769 if rigid.boneIndex==0xFFFF:
773 bone=io.bones[rigid.boneIndex]
774 pos=bone.pos+rigid.position
776 if rigid.shapeType==pmd.SHAPE_SPHERE:
777 bpy.ops.mesh.primitive_ico_sphere_add(
778 location=(pos.x, pos.z, pos.y),
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),
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),
795 bpy.ops.transform.resize(
796 value=(rigid.w, rigid.w, rigid.h))
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'
810 meshObject.rotation_euler=(-rot.x, -rot.z, -rot.y)
813 meshObject[RIGID_SHAPE_TYPE]=rigid.shapeType
814 meshObject[RIGID_PROCESS_TYPE]=rigid.processType
816 bone_name = englishmap.getEnglishBoneName(bone.getName())
818 bone_name=bone.getName()
819 meshObject[RIGID_BONE_NAME]=bone_name
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
829 for meshObject in reversed(rigidMeshes):
830 bl.object.makeParent(container, meshObject)
835 def _execute(filename):
837 load pmd file to context.
841 bl.progress_set('load %s' % filename, 0.0)
844 if not io.read(filename):
845 bl.message("fail to load %s" % filename)
847 bl.progress_set('loaded', 0.1)
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()
859 tex_dir=os.path.dirname(filename)
860 toonTextures, toonMaterial=__importToonTextures(io, tex_dir)
861 bl.object.makeParent(root, toonTextures)
864 mesh_objects=__importMaterialAndMesh(io, tex_dir, toonMaterial)
865 for o in mesh_objects:
866 bl.object.makeParent(root, o)
869 armature_object=__importArmature(io)
871 bl.object.makeParent(root, armature_object)
872 armature = bl.object.getData(armature_object)
874 # add armature modifier
875 for o in mesh_objects:
876 bl.modifier.addArmature(o, armature_object)
879 for n, b in bl.object.getPose(armature_object).bones.items():
882 # import rigid bodies
883 rigidBodies=__importRigidBodies(io)
885 bl.object.makeParent(root, rigidBodies)
888 constraints=__importConstraints(io)
890 bl.object.makeParent(root, constraints)
892 bl.object.activate(root)
897 def execute_24(filename):
898 bl.initialize('pmd_import', bpy.data.scenes.active)
899 _execute(filename.decode(bl.INTERNAL_ENCODING))
902 Blender.Window.FileSelector(
905 Blender.sys.makename(ext='.pmd'))
909 class IMPORT_OT_pmd(bpy.types.Operator):
910 bl_idname = "import_scene.pmd"
911 bl_label = 'Import PMD'
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()
919 def execute(self, context):
920 bl.initialize('pmd_import', context.scene)
921 _execute(self.properties.filepath)
925 def invoke(self, context, event):
926 wm = context.window_manager
927 wm.add_fileselect(self)
928 return 'RUNNING_MODAL'
931 def menu_func(self, context):
932 self.layout.operator(IMPORT_OT_pmd.bl_idname,
933 text="MikuMikuDance model (.pmd)",
938 bpy.types.INFO_MT_file_import.append(menu_func)
941 bpy.types.INFO_MT_file_import.remove(menu_func)
943 if __name__=="__main__":