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.
36 MMD_SHAPE_GROUP_NAME='_MMD_SHAPE'
37 BASE_SHAPE_NAME='Basis'
38 RIGID_SHAPE_TYPE='rigid_shape_type'
39 RIGID_PROCESS_TYPE='rigid_process_type'
40 RIGID_BONE_NAME='rigid_bone_name'
41 #RIGID_LOCATION='rigid_loation'
42 RIGID_GROUP='ribid_group'
43 RIGID_INTERSECTION_GROUP='rigid_intersection_group'
44 RIGID_WEIGHT='rigid_weight'
45 RIGID_LINEAR_DAMPING='rigid_linear_damping'
46 RIGID_ANGULAR_DAMPING='rigid_angular_damping'
47 RIGID_RESTITUTION='rigid_restitution'
48 RIGID_FRICTION='rigid_friction'
49 CONSTRAINT_NAME='constraint_name'
50 CONSTRAINT_A='const_a'
51 CONSTRAINT_B='const_b'
52 CONSTRAINT_POS_MIN='const_pos_min'
53 CONSTRAINT_POS_MAX='const_pos_max'
54 CONSTRAINT_ROT_MIN='const_rot_min'
55 CONSTRAINT_ROT_MAX='const_rot_max'
56 CONSTRAINT_SPRING_POS='const_spring_pos'
57 CONSTRAINT_SPRING_ROT='const_spring_rot'
60 ###############################################################################
62 ###############################################################################
68 from meshio import pmd, englishmap
71 return sys.version_info[0]<3
76 from Blender import Mathutils
82 def createPmdMaterial(m, index):
83 material=Blender.Material.New()
85 material.setDiffuseShader(Blender.Material.Shaders.DIFFUSE_TOON)
86 material.setRGBCol([m.diffuse.r, m.diffuse.g, m.diffuse.b])
87 material.setAlpha(m.diffuse.a)
89 material.setSpecShader(Blender.Material.Shaders.SPEC_TOON)
90 material.setSpec(m.shinness*0.1)
91 material.setSpecCol([m.specular.r, m.specular.g, m.specular.b])
93 material.setMirCol([m.ambient.r, m.ambient.g, m.ambient.b])
95 material.enableSSS=True if m.flag==1 else False
97 material.name="m_%02d" % index
100 def poseBoneLimit(n, b):
103 if n.startswith("knee_"):
108 b.limitMax=[180, 0, 0]
109 elif n.startswith("ankle_"):
112 def setSphereMap(material, index, blend_type='MULTIPLY'):
113 slot=material.textures[index]
114 slot.mapto=Blender.Texture.MapTo.NOR
115 slot.mapping=Blender.Texture.Mappings.SPHERE
116 if blend_type=='MULTIPLY':
117 slot.blendmode=Blender.Texture.BlendModes.MULTIPLY
118 elif blend_type=='ADD':
119 slot.blendmode=Blender.Texture.BlendModes.ADD
124 from bpy.props import *
132 def createPmdMaterial(m, index):
133 material = bpy.data.materials.new("Material")
135 material.diffuse_shader='FRESNEL'
136 material.diffuse_color=([m.diffuse.r, m.diffuse.g, m.diffuse.b])
137 material.alpha=m.diffuse.a
139 material.specular_shader='TOON'
140 material.specular_color=([m.specular.r, m.specular.g, m.specular.b])
141 material.specular_toon_size=int(m.shinness)
143 material.mirror_color=([m.ambient.r, m.ambient.g, m.ambient.b])
145 material.subsurface_scattering.enabled=True if m.flag==1 else False
147 material.name="m_%02d" % index
148 material.preview_render_type='FLAT'
149 material.transparency=True
152 def poseBoneLimit(n, b):
155 if n.startswith("knee_"):
162 elif n.startswith("ankle_"):
166 def setSphereMap(material, index, blend_type='MULTIPLY'):
167 slot=material.texture_slots[index]
168 slot.texture_coordinates='NORMAL'
169 slot.mapping='SPHERE'
170 slot.blend_type=blend_type
173 ###############################################################################
175 return bl.createVector(v.x, v.y, v.z)
178 def convert_coord(pos):
180 Left handed y-up to Right handed z-up
182 return (pos.x, pos.z, pos.y)
185 def to_radian(degree):
186 return math.pi * degree / 180
189 def get_bone_name(l, index):
191 return l.bones[0].getName()
193 if index < len(l.bones):
194 name=englishmap.getEnglishBoneName(l.bones[index].getName())
197 return l.bones[index].getName()
198 print('invalid bone index', index)
199 return l.bones[0].getName()
202 def get_group_name(g):
203 group_name=englishmap.getEnglishBoneGroupName(g.getName().strip())
205 group_name=g.getName().strip()
209 def __importToonTextures(io, tex_dir):
210 mesh, meshObject=bl.mesh.create('ToonTextures')
211 material=bl.material.create('ToonTextures')
212 bl.mesh.addMaterial(mesh, material)
214 t=io.getToonTexture(i)
215 path=os.path.join(tex_dir, t.getName())
216 texture, image=bl.texture.create(path)
217 bl.material.addTexture(material, texture, False)
218 return meshObject, material
221 def __importShape(obj, l, vertex_map):
222 if len(l.morph_list)==0:
226 bl.object.pinShape(obj, True)
230 for s in l.morph_list:
234 # create vertex group
235 bl.object.addVertexGroup(obj, MMD_SHAPE_GROUP_NAME)
240 bl.object.assignVertexGroup(
241 obj, MMD_SHAPE_GROUP_NAME, vertex_map[i], 0)
247 baseShapeBlock=bl.object.addShapeKey(obj, BASE_SHAPE_NAME)
249 mesh=bl.object.getData(obj)
253 for s in l.morph_list:
258 name=englishmap.getEnglishSkinName(s.getName())
264 for index, offset in zip(s.indices, s.pos_list):
266 vertex_index=vertex_map[base.indices[index]]
267 v=mesh.verts[vertex_index].co
268 offset=convert_coord(offset)
272 except IndexError as msg:
274 print(index, len(base.indices), len(vertex_map))
275 print(len(mesh.verts))
276 print(base.indices[index])
280 #print 'this mesh not has shape vertices'
283 # create shapekey block
284 new_shape_key=bl.object.addShapeKey(obj, name)
286 # copy vertex to shape key
290 for mv, v in zip(mesh.verts, baseShapeBlock.getData()):
298 new_shape_key=bl.object.addShapeKey(obj, name)
300 for index, offset in zip(s.indices, s.pos_list):
302 vertex_index=vertex_map[base.indices[index]]
303 bl.shapekey.assign(new_shape_key, vertex_index,
304 mesh.verts[vertex_index].co+
305 bl.createVector(*convert_coord(offset)))
306 except IndexError as msg:
308 print(index, len(base.indices), len(vertex_map))
309 print(len(mesh.verts))
310 print(base.indices[index])
314 #print 'this mesh not has shape vertices'
318 bl.object.setActivateShapeKey(obj, 0)
321 def __build(armature, b, p, parent):
322 name=englishmap.getEnglishBoneName(b.getName())
326 bone=bl.armature.createBone(armature, name)
328 if parent and (b.tail_index==0 or b.type==6 or b.type==7 or b.type==9):
329 bone.head = bl.createVector(*convert_coord(b.pos))
330 bone.tail=bone.head+bl.createVector(0, 1, 0)
332 if bone.name=="center_t":
333 # センターボーンは(0, 1, 0)の方向を向いていないと具合が悪い
334 parent.tail=parent.head+bl.createVector(0, 1, 0)
335 bone.head=parent.tail
336 bone.tail=bone.head+bl.createVector(0, 1, 0)
338 if parent.tail==bone.head:
341 print('diffurence with parent.tail and head', name)
344 bl.bone.setConnected(bone)
346 bl.bone.setLayerMask(bone, [0, 1])
349 bone.head = bl.createVector(*convert_coord(b.pos))
350 bone.tail = bl.createVector(*convert_coord(b.tail))
353 if parent.tail==bone.head:
354 bl.bone.setConnected(bone)
356 if bone.head==bone.tail:
357 bone.tail=bone.head+bl.createVector(0, 1, 0)
360 __build(armature, c, b, bone)
363 def __importArmature(l):
364 armature, armature_object=bl.armature.create()
367 bl.armature.makeEditable(armature_object)
370 __build(armature, b, None, None)
371 bl.armature.update(armature)
375 pose = bl.object.getPose(armature_object)
377 target=l.bones[ik.target]
378 name = englishmap.getEnglishBoneName(target.getName())
380 name=target.getName()
381 p_bone = pose.bones[name]
383 print('not found', name)
385 if len(ik.children) >= 16:
386 print('over MAX_CHAINLEN', ik, len(ik.children))
388 effector_name=englishmap.getEnglishBoneName(
389 l.bones[ik.index].getName())
390 if not effector_name:
391 effector_name=l.bones[ik.index].getName()
393 constraint=bl.armature.createIkConstraint(armature_object,
394 p_bone, effector_name, ik)
396 bl.armature.makeEditable(armature_object)
397 bl.armature.update(armature)
404 for i, g in enumerate(l.bone_group_list):
405 name=get_group_name(g)
406 bl.object.createBoneGroup(armature_object, name, "THEME%02d" % (i+1))
408 # assign bone to group
409 for b_index, g_index in l.bone_display_list:
412 bone_name=englishmap.getEnglishBoneName(b.getName())
414 bone_name=b.getName()
416 g=l.bone_group_list[g_index-1]
417 group_name=get_group_name(g)
420 pose.bones[bone_name].bone_group=pose.bone_groups[group_name]
424 return armature_object
427 def __import16MaerialAndMesh(meshObject, l,
428 material_order, face_map, tex_dir, toon_material):
430 mesh=bl.object.getData(meshObject)
431 ############################################################
433 ############################################################
434 bl.progress_print('create materials')
440 for material_index in material_order:
442 m=l.materials[material_index]
443 mesh_material_map[material_index]=index
447 material=createPmdMaterial(m, material_index)
448 toon_index=bl.material.addTexture(
450 bl.material.getTexture(
452 0 if m.toon_index==0xFF else m.toon_index
456 texture_name=m.getTexture()
458 for i, t in enumerate(texture_name.split('*')):
460 texture=textureMap[t]
462 path=os.path.join(tex_dir, t)
463 texture, image=bl.texture.create(path)
464 textureMap[texture_name]=texture
465 imageMap[material_index]=image
466 texture_index=bl.material.addTexture(material, texture)
467 if t.endswith('sph'):
469 setSphereMap(material, texture_index)
470 elif t.endswith('spa'):
472 setSphereMap(material, texture_index, 'ADD')
474 bl.mesh.addMaterial(mesh, material)
477 ############################################################
479 ############################################################
480 bl.progress_print('create vertices')
483 for v in l.each_vertex():
484 vertices.append(convert_coord(v.pos))
486 ############################################################
488 ############################################################
489 bl.progress_print('create faces')
492 mesh_face_materials=[]
495 for material_index in material_order:
496 face_offset=face_map[material_index]
497 m=l.materials[material_index]
498 material_faces=l.indices[face_offset:face_offset+m.vertex_count]
500 def degenerate(i0, i1, i2):
504 return i0==i1 or i1==i2 or i2==i0
506 for j in xrange(0, len(material_faces), 3):
508 i1=material_faces[j+1]
509 i2=material_faces[j+2]
511 triangle=[i2, i1, i0]
512 if degenerate(*triangle):
514 mesh_face_indices.append(triangle[0:3])
515 mesh_face_materials.append(material_index)
516 used_vertices.add(i0)
517 used_vertices.add(i1)
518 used_vertices.add(i2)
520 ############################################################
521 # create vertices & faces
522 ############################################################
523 bl.mesh.addGeometry(mesh, vertices, mesh_face_indices)
525 ############################################################
527 ############################################################
528 # create vertex group
530 for v in l.each_vertex():
531 vertex_groups[v.bone0]=True
532 vertex_groups[v.bone1]=True
533 for i in vertex_groups.keys():
534 bl.object.addVertexGroup(meshObject, get_bone_name(l, i))
537 bl.mesh.useVertexUV(mesh)
538 for i, v, mvert in zip(xrange(len(l.vertices)),
539 l.each_vertex(), mesh.verts):
541 bl.vertex.setNormal(mvert, convert_coord(v.normal))
543 w1=float(v.weight0)/100.0
545 bl.object.assignVertexGroup(meshObject, get_bone_name(l, v.bone0),
547 bl.object.assignVertexGroup(meshObject, get_bone_name(l, v.bone1),
550 ############################################################
552 ############################################################
555 for i, (face, material_index) in enumerate(
556 zip(mesh.faces, mesh_face_materials)):
558 index=mesh_material_map[material_index]
559 except KeyError as message:
560 print(message, mesh_material_map, m)
562 bl.face.setMaterial(face, index)
563 material=mesh.materials[index]
565 if bl.material.hasTexture(material):
566 uv_array=[l.getUV(i) for i in bl.face.getIndices(face)]
567 bl.mesh.setFaceUV(mesh, i, face,
569 [(uv.x, 1.0-uv.y) for uv in uv_array],
570 imageMap.get(index, None))
577 ############################################################
578 # clean up not used vertices
579 ############################################################
580 bl.progress_print('clean up vertices not used')
583 for i, v in enumerate(l.each_vertex()):
584 if i in used_vertices:
585 vertex_map[i]=len(vertex_map)
587 remove_vertices.append(i)
589 bl.mesh.vertsDelete(mesh, remove_vertices)
591 bl.progress_print('%s created' % mesh.name)
595 def __importMaterialAndMesh(io, tex_dir, toon_material):
597 @param l[in] mmd.PMDLoader
600 ############################################################
601 # shpaeキーで使われるマテリアル優先的に前に並べる
602 ############################################################
603 # shapeキーで使われる頂点インデックスを集める
604 shape_key_used_vertices=set()
605 if len(io.morph_list)>0:
608 for s in io.morph_list:
615 for index in base.indices:
616 shape_key_used_vertices.add(index)
618 # マテリアルに含まれる頂点がshape_keyに含まれるか否か?
619 def isMaterialUsedInShape(offset, m):
620 for i in xrange(offset, offset+m.vertex_count):
621 if io.indices[i] in shape_key_used_vertices:
624 material_with_shape=set()
626 # 各マテリアルの開始頂点インデックスを記録する
629 for i, m in enumerate(io.materials):
630 face_map[i]=face_count
631 if isMaterialUsedInShape(face_count, m):
632 material_with_shape.add(i)
633 face_count+=m.vertex_count
635 # shapeキーで使われる頂点のあるマテリアル
636 material_with_shape=list(material_with_shape)
637 material_with_shape.sort()
639 # shapeキーに使われていないマテリアル
640 material_without_shape=[]
641 for i in range(len(io.materials)):
642 if not i in material_with_shape:
643 material_without_shape.append(i)
646 def __splitList(l, length):
647 for i in range(0, len(l), length):
650 def __importMeshAndShape(material16, name):
651 mesh, meshObject=bl.mesh.create(name)
654 bl.object.deselectAll()
655 bl.object.activate(meshObject)
657 # shapeキーで使われる順に並べなおしたマテリアル16個分の
659 vertex_map=__import16MaerialAndMesh(
660 meshObject, io, material16, face_map, tex_dir, toon_material)
663 __importShape(meshObject, io, vertex_map)
668 mesh_objects=[__importMeshAndShape(material16, 'with_shape')
669 for material16 in __splitList(material_with_shape, 16)]
671 mesh_objects+=[__importMeshAndShape(material16, 'mesh')
672 for material16 in __splitList(material_without_shape, 16)]
677 def __importConstraints(io):
680 print("create constraint")
681 container=bl.object.createEmpty('Constraints')
683 True, False, False, False, False, False, False, False,
684 False, False, False, False, False, False, False, False,
685 False, False, False, False, False, False, False, False,
686 False, False, False, False, False, False, False, False,
688 material=bl.material.create('constraint')
689 material.diffuse_color=(1, 0, 0)
691 for i, c in enumerate(io.constraints):
692 bpy.ops.mesh.primitive_uv_sphere_add(
696 location=(c.pos.x, c.pos.z, c.pos.y),
699 meshObject=bl.object.getActive()
700 constraintMeshes.append(meshObject)
701 mesh=bl.object.getData(meshObject)
702 bl.mesh.addMaterial(mesh, material)
703 meshObject.name='c_%d' % i
704 #meshObject.draw_transparent=True
705 #meshObject.draw_wire=True
706 meshObject.max_draw_type='SOLID'
708 meshObject.rotation_euler=(-rot.x, -rot.z, -rot.y)
710 meshObject[CONSTRAINT_NAME]=c.getName()
711 meshObject[CONSTRAINT_A]=io.rigidbodies[c.rigidA].getName()
712 meshObject[CONSTRAINT_B]=io.rigidbodies[c.rigidB].getName()
713 meshObject[CONSTRAINT_POS_MIN]=VtoV(c.constraintPosMin)
714 meshObject[CONSTRAINT_POS_MAX]=VtoV(c.constraintPosMax)
715 meshObject[CONSTRAINT_ROT_MIN]=VtoV(c.constraintRotMin)
716 meshObject[CONSTRAINT_ROT_MAX]=VtoV(c.constraintRotMax)
717 meshObject[CONSTRAINT_SPRING_POS]=VtoV(c.springPos)
718 meshObject[CONSTRAINT_SPRING_ROT]=VtoV(c.springRot)
720 for meshObject in reversed(constraintMeshes):
721 bl.object.makeParent(container, meshObject)
726 def __importRigidBodies(io):
729 print("create rigid bodies")
731 container=bl.object.createEmpty('RigidBodies')
733 True, False, False, False, False, False, False, False,
734 False, False, False, False, False, False, False, False,
735 False, False, False, False, False, False, False, False,
736 False, False, False, False, False, False, False, False,
738 material=bl.material.create('rigidBody')
740 for rigid in io.rigidbodies:
741 if rigid.boneIndex==0xFFFF:
745 bone=io.bones[rigid.boneIndex]
746 pos=bone.pos+rigid.position
748 if rigid.shapeType==pmd.SHAPE_SPHERE:
749 bpy.ops.mesh.primitive_ico_sphere_add(
750 location=(pos.x, pos.z, pos.y),
753 bpy.ops.transform.resize(
754 value=(rigid.w, rigid.w, rigid.w))
755 elif rigid.shapeType==pmd.SHAPE_BOX:
756 bpy.ops.mesh.primitive_cube_add(
757 location=(pos.x, pos.z, pos.y),
760 bpy.ops.transform.resize(
761 value=(rigid.w, rigid.d, rigid.h))
762 elif rigid.shapeType==pmd.SHAPE_CAPSULE:
763 bpy.ops.mesh.primitive_tube_add(
764 location=(pos.x, pos.z, pos.y),
767 bpy.ops.transform.resize(
768 value=(rigid.w, rigid.w, rigid.h))
772 meshObject=bl.object.getActive()
773 mesh=bl.object.getData(meshObject)
774 rigidMeshes.append(meshObject)
775 bl.mesh.addMaterial(mesh, material)
776 meshObject.name=rigid.getName()
777 #meshObject.draw_transparent=True
778 #meshObject.draw_wire=True
779 meshObject.max_draw_type='WIRE'
781 meshObject.rotation_euler=(-rot.x, -rot.z, -rot.y)
784 meshObject[RIGID_SHAPE_TYPE]=rigid.shapeType
785 meshObject[RIGID_PROCESS_TYPE]=rigid.processType
787 bone_name = englishmap.getEnglishBoneName(bone.getName())
789 bone_name=bone.getName()
790 meshObject[RIGID_BONE_NAME]=bone_name
792 meshObject[RIGID_GROUP]=rigid.group
793 meshObject[RIGID_INTERSECTION_GROUP]=rigid.target
794 meshObject[RIGID_WEIGHT]=rigid.weight
795 meshObject[RIGID_LINEAR_DAMPING]=rigid.linearDamping
796 meshObject[RIGID_ANGULAR_DAMPING]=rigid.angularDamping
797 meshObject[RIGID_RESTITUTION]=rigid.restitution
798 meshObject[RIGID_FRICTION]=rigid.friction
800 for meshObject in reversed(rigidMeshes):
801 bl.object.makeParent(container, meshObject)
806 def _execute(filename):
808 load pmd file to context.
812 bl.progress_set('load %s' % filename, 0.0)
815 if not io.read(filename):
816 bl.message("fail to load %s" % filename)
818 bl.progress_set('loaded %s' % filename, 0.1)
821 model_name=io.getEnglishName()
822 if len(model_name)==0:
823 model_name=io.getName()
824 root=bl.object.createEmpty(model_name)
827 tex_dir=os.path.dirname(filename)
828 toonTextures, toonMaterial=__importToonTextures(io, tex_dir)
829 bl.object.makeParent(root, toonTextures)
832 mesh_objects=__importMaterialAndMesh(io, tex_dir, toonMaterial)
833 for o in mesh_objects:
834 bl.object.makeParent(root, o)
837 armature_object=__importArmature(io)
839 bl.object.makeParent(root, armature_object)
840 armature = bl.object.getData(armature_object)
842 # add armature modifier
843 for o in mesh_objects:
844 bl.modifier.addArmature(o, armature_object)
847 for n, b in bl.object.getPose(armature_object).bones.items():
850 # import rigid bodies
851 rigidBodies=__importRigidBodies(io)
853 bl.object.makeParent(root, rigidBodies)
856 constraints=__importConstraints(io)
858 bl.object.makeParent(root, constraints)
860 bl.object.activate(root)
865 def execute_24(filename):
866 bl.initialize('pmd_import', bpy.data.scenes.active)
867 _execute(filename.decode(bl.INTERNAL_ENCODING))
870 Blender.Window.FileSelector(
873 Blender.sys.makename(ext='.pmd'))
877 class IMPORT_OT_pmd(bpy.types.Operator):
878 bl_idname = "import_scene.pmd"
879 bl_label = 'Import PMD'
881 # List of operator properties, the attributes will be assigned
882 # to the class instance from the operator settings before calling.
884 path = StringProperty(
886 description="File path used for importing the PMD file",
887 maxlen= 1024, default= "")
888 filename = StringProperty(
890 description="Name of the file.")
891 directory = StringProperty(
893 description="Directory of the file.")
895 def execute(self, context):
896 bl.initialize('pmd_import', context.scene)
897 _execute(self.properties.path)
901 def invoke(self, context, event):
903 wm.add_fileselect(self)
904 return 'RUNNING_MODAL'
907 def menu_func(self, context):
908 self.layout.operator(IMPORT_OT_pmd.bl_idname,
909 text="MikuMikuDance model (.pmd)")
912 bpy.types.register(IMPORT_OT_pmd)
913 bpy.types.INFO_MT_file_import.append(menu_func)
916 bpy.types.unregister(IMPORT_OT_pmd)
917 bpy.types.INFO_MT_file_import.remove(menu_func)
919 if __name__=="__main__":