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'
58 TOON_TEXTURE_OBJECT='ToonTextures'
61 ###############################################################################
63 ###############################################################################
69 from meshio import pmd, englishmap
72 return sys.version_info[0]<3
77 from Blender import Mathutils
83 def createPmdMaterial(m, index):
84 material=Blender.Material.New()
86 material.setDiffuseShader(Blender.Material.Shaders.DIFFUSE_TOON)
87 material.setRGBCol([m.diffuse.r, m.diffuse.g, m.diffuse.b])
88 material.setAlpha(m.diffuse.a)
90 material.setSpecShader(Blender.Material.Shaders.SPEC_TOON)
91 material.setSpec(m.shinness*0.1)
92 material.setSpecCol([m.specular.r, m.specular.g, m.specular.b])
94 material.setMirCol([m.ambient.r, m.ambient.g, m.ambient.b])
96 material.enableSSS=True if m.flag==1 else False
98 material.name="m_%02d" % index
101 def poseBoneLimit(n, b):
104 if n.startswith("knee_"):
109 b.limitMax=[180, 0, 0]
110 elif n.startswith("ankle_"):
113 def setSphereMap(material, index, blend_type='MULTIPLY'):
114 slot=material.textures[index]
115 slot.mapto=Blender.Texture.MapTo.NOR
116 slot.mapping=Blender.Texture.Mappings.SPHERE
117 if blend_type=='MULTIPLY':
118 slot.blendmode=Blender.Texture.BlendModes.MULTIPLY
119 elif blend_type=='ADD':
120 slot.blendmode=Blender.Texture.BlendModes.ADD
125 from bpy.props import *
133 def createPmdMaterial(m, index):
134 material = bpy.data.materials.new("Material")
136 material.diffuse_shader='FRESNEL'
137 material.diffuse_color=([m.diffuse.r, m.diffuse.g, m.diffuse.b])
138 material.alpha=m.diffuse.a
140 material.specular_shader='TOON'
141 material.specular_color=([m.specular.r, m.specular.g, m.specular.b])
142 material.specular_toon_size=int(m.shinness)
144 material.mirror_color=([m.ambient.r, m.ambient.g, m.ambient.b])
146 material.subsurface_scattering.enabled=True if m.flag==1 else False
148 material.name="m_%02d" % index
149 material.preview_render_type='FLAT'
150 material.transparency=True
153 def poseBoneLimit(n, b):
156 if n.startswith("knee_"):
163 elif n.startswith("ankle_"):
167 def setSphereMap(material, index, blend_type='MULTIPLY'):
168 slot=material.texture_slots[index]
169 slot.texture_coordinates='NORMAL'
170 slot.mapping='SPHERE'
171 slot.blend_type=blend_type
174 ###############################################################################
176 return bl.createVector(v.x, v.y, v.z)
179 def convert_coord(pos):
181 Left handed y-up to Right handed z-up
183 return (pos.x, pos.z, pos.y)
186 def to_radian(degree):
187 return math.pi * degree / 180
190 def get_bone_name(l, index):
192 return l.bones[0].getName()
194 if index < len(l.bones):
195 name=englishmap.getEnglishBoneName(l.bones[index].getName())
198 return l.bones[index].getName()
199 print('invalid bone index', index)
200 return l.bones[0].getName()
203 def get_group_name(g):
204 group_name=englishmap.getEnglishBoneGroupName(g.getName().strip())
206 group_name=g.getName().strip()
210 def __importToonTextures(io, tex_dir):
211 mesh, meshObject=bl.mesh.create(TOON_TEXTURE_OBJECT)
212 material=bl.material.create(TOON_TEXTURE_OBJECT)
213 bl.mesh.addMaterial(mesh, material)
215 t=io.getToonTexture(i)
216 path=os.path.join(tex_dir, t.getName())
217 texture, image=bl.texture.create(path)
218 bl.material.addTexture(material, texture, False)
219 return meshObject, material
222 def __importShape(obj, l, vertex_map):
223 if len(l.morph_list)==0:
227 bl.object.pinShape(obj, True)
231 for s in l.morph_list:
235 # create vertex group
236 bl.object.addVertexGroup(obj, MMD_SHAPE_GROUP_NAME)
241 bl.object.assignVertexGroup(
242 obj, MMD_SHAPE_GROUP_NAME, vertex_map[i], 0)
248 baseShapeBlock=bl.object.addShapeKey(obj, BASE_SHAPE_NAME)
250 mesh=bl.object.getData(obj)
254 for s in l.morph_list:
259 name=englishmap.getEnglishSkinName(s.getName())
265 for index, offset in zip(s.indices, s.pos_list):
267 vertex_index=vertex_map[base.indices[index]]
268 v=mesh.verts[vertex_index].co
269 offset=convert_coord(offset)
273 except IndexError as msg:
275 print(index, len(base.indices), len(vertex_map))
276 print(len(mesh.verts))
277 print(base.indices[index])
281 #print 'this mesh not has shape vertices'
284 # create shapekey block
285 new_shape_key=bl.object.addShapeKey(obj, name)
287 # copy vertex to shape key
291 for mv, v in zip(mesh.verts, baseShapeBlock.getData()):
299 new_shape_key=bl.object.addShapeKey(obj, name)
301 for index, offset in zip(s.indices, s.pos_list):
303 vertex_index=vertex_map[base.indices[index]]
304 bl.shapekey.assign(new_shape_key, vertex_index,
305 mesh.verts[vertex_index].co+
306 bl.createVector(*convert_coord(offset)))
307 except IndexError as msg:
309 print(index, len(base.indices), len(vertex_map))
310 print(len(mesh.verts))
311 print(base.indices[index])
315 #print 'this mesh not has shape vertices'
319 bl.object.setActivateShapeKey(obj, 0)
322 def __build(armature, b, p, parent):
323 name=englishmap.getEnglishBoneName(b.getName())
327 bone=bl.armature.createBone(armature, name)
329 if parent and (b.tail_index==0 or b.type==6 or b.type==7 or b.type==9):
331 bone.head = bl.createVector(*convert_coord(b.pos))
332 bone.tail=bone.head+bl.createVector(0, 1, 0)
334 if bone.name=="center_t":
335 # センターボーンは(0, 1, 0)の方向を向いていないと具合が悪い
336 parent.tail=parent.head+bl.createVector(0, 1, 0)
337 bone.head=parent.tail
338 bone.tail=bone.head+bl.createVector(0, 1, 0)
340 if parent.tail==bone.head:
343 print('diffurence with parent.tail and head', name)
346 bl.bone.setConnected(bone)
348 bl.bone.setLayerMask(bone, [0, 1])
351 bone.head = bl.createVector(*convert_coord(b.pos))
352 bone.tail = bl.createVector(*convert_coord(b.tail))
355 if parent.tail==bone.head:
356 bl.bone.setConnected(bone)
358 if bone.head==bone.tail:
359 bone.tail=bone.head+bl.createVector(0, 1, 0)
362 __build(armature, c, b, bone)
365 def __importArmature(l):
366 armature, armature_object=bl.armature.create()
369 bl.armature.makeEditable(armature_object)
372 __build(armature, b, None, None)
373 bl.armature.update(armature)
377 pose = bl.object.getPose(armature_object)
379 target=l.bones[ik.target]
380 name = englishmap.getEnglishBoneName(target.getName())
382 name=target.getName()
383 p_bone = pose.bones[name]
385 print('not found', name)
387 if len(ik.children) >= 16:
388 print('over MAX_CHAINLEN', ik, len(ik.children))
390 effector_name=englishmap.getEnglishBoneName(
391 l.bones[ik.index].getName())
392 if not effector_name:
393 effector_name=l.bones[ik.index].getName()
395 constraint=bl.armature.createIkConstraint(armature_object,
396 p_bone, effector_name, ik)
398 bl.armature.makeEditable(armature_object)
399 bl.armature.update(armature)
406 for i, g in enumerate(l.bone_group_list):
407 name=get_group_name(g)
408 bl.object.createBoneGroup(armature_object, name, "THEME%02d" % (i+1))
410 # assign bone to group
411 for b_index, g_index in l.bone_display_list:
414 bone_name=englishmap.getEnglishBoneName(b.getName())
416 bone_name=b.getName()
418 g=l.bone_group_list[g_index-1]
419 group_name=get_group_name(g)
422 pose.bones[bone_name].bone_group=pose.bone_groups[group_name]
426 return armature_object
429 def __import16MaerialAndMesh(meshObject, l,
430 material_order, face_map, tex_dir, toon_material):
432 mesh=bl.object.getData(meshObject)
433 ############################################################
435 ############################################################
436 bl.progress_print('create materials')
442 for material_index in material_order:
444 m=l.materials[material_index]
445 mesh_material_map[material_index]=index
449 material=createPmdMaterial(m, material_index)
452 texture_name=m.getTexture()
454 for i, t in enumerate(texture_name.split('*')):
456 texture=textureMap[t]
458 path=os.path.join(tex_dir, t)
459 texture, image=bl.texture.create(path)
460 textureMap[texture_name]=texture
461 imageMap[material_index]=image
462 texture_index=bl.material.addTexture(material, texture)
463 if t.endswith('sph'):
465 setSphereMap(material, texture_index)
466 elif t.endswith('spa'):
468 setSphereMap(material, texture_index, 'ADD')
471 toon_index=bl.material.addTexture(
473 bl.material.getTexture(
475 0 if m.toon_index==0xFF else m.toon_index
479 bl.mesh.addMaterial(mesh, material)
483 ############################################################
485 ############################################################
486 bl.progress_print('create vertices')
489 for v in l.each_vertex():
490 vertices.append(convert_coord(v.pos))
492 ############################################################
494 ############################################################
495 bl.progress_print('create faces')
498 mesh_face_materials=[]
501 for material_index in material_order:
502 face_offset=face_map[material_index]
503 m=l.materials[material_index]
504 material_faces=l.indices[face_offset:face_offset+m.vertex_count]
506 def degenerate(i0, i1, i2):
510 return i0==i1 or i1==i2 or i2==i0
512 for j in xrange(0, len(material_faces), 3):
514 i1=material_faces[j+1]
515 i2=material_faces[j+2]
517 triangle=[i2, i1, i0]
518 if degenerate(*triangle):
520 mesh_face_indices.append(triangle[0:3])
521 mesh_face_materials.append(material_index)
522 used_vertices.add(i0)
523 used_vertices.add(i1)
524 used_vertices.add(i2)
526 ############################################################
527 # create vertices & faces
528 ############################################################
529 bl.mesh.addGeometry(mesh, vertices, mesh_face_indices)
531 ############################################################
533 ############################################################
534 # create vertex group
536 for v in l.each_vertex():
537 vertex_groups[v.bone0]=True
538 vertex_groups[v.bone1]=True
539 for i in vertex_groups.keys():
540 bl.object.addVertexGroup(meshObject, get_bone_name(l, i))
543 bl.mesh.useVertexUV(mesh)
544 for i, v, mvert in zip(xrange(len(l.vertices)),
545 l.each_vertex(), mesh.verts):
547 bl.vertex.setNormal(mvert, convert_coord(v.normal))
549 w1=float(v.weight0)/100.0
551 bl.object.assignVertexGroup(meshObject, get_bone_name(l, v.bone0),
553 bl.object.assignVertexGroup(meshObject, get_bone_name(l, v.bone1),
556 ############################################################
558 ############################################################
561 for i, (face, material_index) in enumerate(
562 zip(mesh.faces, mesh_face_materials)):
564 index=mesh_material_map[material_index]
565 except KeyError as message:
566 print(message, mesh_material_map, m)
568 bl.face.setMaterial(face, index)
569 material=mesh.materials[index]
571 if bl.material.hasTexture(material):
572 uv_array=[l.getUV(i) for i in bl.face.getIndices(face)]
573 bl.mesh.setFaceUV(mesh, i, face,
575 [(uv.x, 1.0-uv.y) for uv in uv_array],
576 imageMap.get(index, None))
583 ############################################################
584 # clean up not used vertices
585 ############################################################
586 bl.progress_print('clean up vertices not used')
589 for i, v in enumerate(l.each_vertex()):
590 if i in used_vertices:
591 vertex_map[i]=len(vertex_map)
593 remove_vertices.append(i)
595 bl.mesh.vertsDelete(mesh, remove_vertices)
597 bl.progress_print('%s created' % mesh.name)
601 def __importMaterialAndMesh(io, tex_dir, toon_material):
603 @param l[in] mmd.PMDLoader
606 ############################################################
607 # shpaeキーで使われるマテリアル優先的に前に並べる
608 ############################################################
609 # shapeキーで使われる頂点インデックスを集める
610 shape_key_used_vertices=set()
611 if len(io.morph_list)>0:
614 for s in io.morph_list:
621 for index in base.indices:
622 shape_key_used_vertices.add(index)
624 # マテリアルに含まれる頂点がshape_keyに含まれるか否か?
625 def isMaterialUsedInShape(offset, m):
626 for i in xrange(offset, offset+m.vertex_count):
627 if io.indices[i] in shape_key_used_vertices:
630 material_with_shape=set()
632 # 各マテリアルの開始頂点インデックスを記録する
635 for i, m in enumerate(io.materials):
636 face_map[i]=face_count
637 if isMaterialUsedInShape(face_count, m):
638 material_with_shape.add(i)
639 face_count+=m.vertex_count
641 # shapeキーで使われる頂点のあるマテリアル
642 material_with_shape=list(material_with_shape)
643 material_with_shape.sort()
645 # shapeキーに使われていないマテリアル
646 material_without_shape=[]
647 for i in range(len(io.materials)):
648 if not i in material_with_shape:
649 material_without_shape.append(i)
652 def __splitList(l, length):
653 for i in range(0, len(l), length):
656 def __importMeshAndShape(material16, name):
657 mesh, meshObject=bl.mesh.create(name)
660 bl.object.deselectAll()
661 bl.object.activate(meshObject)
663 # shapeキーで使われる順に並べなおしたマテリアル16個分の
665 vertex_map=__import16MaerialAndMesh(
666 meshObject, io, material16, face_map, tex_dir, toon_material)
669 __importShape(meshObject, io, vertex_map)
674 mesh_objects=[__importMeshAndShape(material16, 'with_shape')
675 for material16 in __splitList(material_with_shape, 16)]
677 mesh_objects+=[__importMeshAndShape(material16, 'mesh')
678 for material16 in __splitList(material_without_shape, 16)]
683 def __importConstraints(io):
686 print("create constraint")
687 container=bl.object.createEmpty('Constraints')
689 True, False, False, False, False, False, False, False,
690 False, False, False, False, False, False, False, False,
691 False, False, False, False, False, False, False, False,
692 False, False, False, False, False, False, False, False,
694 material=bl.material.create('constraint')
695 material.diffuse_color=(1, 0, 0)
697 for i, c in enumerate(io.constraints):
698 bpy.ops.mesh.primitive_uv_sphere_add(
702 location=(c.pos.x, c.pos.z, c.pos.y),
705 meshObject=bl.object.getActive()
706 constraintMeshes.append(meshObject)
707 mesh=bl.object.getData(meshObject)
708 bl.mesh.addMaterial(mesh, material)
709 meshObject.name='c_%d' % i
710 #meshObject.draw_transparent=True
711 #meshObject.draw_wire=True
712 meshObject.max_draw_type='SOLID'
714 meshObject.rotation_euler=(-rot.x, -rot.z, -rot.y)
716 meshObject[CONSTRAINT_NAME]=c.getName()
717 meshObject[CONSTRAINT_A]=io.rigidbodies[c.rigidA].getName()
718 meshObject[CONSTRAINT_B]=io.rigidbodies[c.rigidB].getName()
719 meshObject[CONSTRAINT_POS_MIN]=VtoV(c.constraintPosMin)
720 meshObject[CONSTRAINT_POS_MAX]=VtoV(c.constraintPosMax)
721 meshObject[CONSTRAINT_ROT_MIN]=VtoV(c.constraintRotMin)
722 meshObject[CONSTRAINT_ROT_MAX]=VtoV(c.constraintRotMax)
723 meshObject[CONSTRAINT_SPRING_POS]=VtoV(c.springPos)
724 meshObject[CONSTRAINT_SPRING_ROT]=VtoV(c.springRot)
726 for meshObject in reversed(constraintMeshes):
727 bl.object.makeParent(container, meshObject)
732 def __importRigidBodies(io):
735 print("create rigid bodies")
737 container=bl.object.createEmpty('RigidBodies')
739 True, False, False, False, False, False, False, False,
740 False, False, False, False, False, False, False, False,
741 False, False, False, False, False, False, False, False,
742 False, False, False, False, False, False, False, False,
744 material=bl.material.create('rigidBody')
746 for rigid in io.rigidbodies:
747 if rigid.boneIndex==0xFFFF:
751 bone=io.bones[rigid.boneIndex]
752 pos=bone.pos+rigid.position
754 if rigid.shapeType==pmd.SHAPE_SPHERE:
755 bpy.ops.mesh.primitive_ico_sphere_add(
756 location=(pos.x, pos.z, pos.y),
759 bpy.ops.transform.resize(
760 value=(rigid.w, rigid.w, rigid.w))
761 elif rigid.shapeType==pmd.SHAPE_BOX:
762 bpy.ops.mesh.primitive_cube_add(
763 location=(pos.x, pos.z, pos.y),
766 bpy.ops.transform.resize(
767 value=(rigid.w, rigid.d, rigid.h))
768 elif rigid.shapeType==pmd.SHAPE_CAPSULE:
769 bpy.ops.mesh.primitive_tube_add(
770 location=(pos.x, pos.z, pos.y),
773 bpy.ops.transform.resize(
774 value=(rigid.w, rigid.w, rigid.h))
778 meshObject=bl.object.getActive()
779 mesh=bl.object.getData(meshObject)
780 rigidMeshes.append(meshObject)
781 bl.mesh.addMaterial(mesh, material)
782 meshObject.name=rigid.getName()
783 #meshObject.draw_transparent=True
784 #meshObject.draw_wire=True
785 meshObject.max_draw_type='WIRE'
787 meshObject.rotation_euler=(-rot.x, -rot.z, -rot.y)
790 meshObject[RIGID_SHAPE_TYPE]=rigid.shapeType
791 meshObject[RIGID_PROCESS_TYPE]=rigid.processType
793 bone_name = englishmap.getEnglishBoneName(bone.getName())
795 bone_name=bone.getName()
796 meshObject[RIGID_BONE_NAME]=bone_name
798 meshObject[RIGID_GROUP]=rigid.group
799 meshObject[RIGID_INTERSECTION_GROUP]=rigid.target
800 meshObject[RIGID_WEIGHT]=rigid.weight
801 meshObject[RIGID_LINEAR_DAMPING]=rigid.linearDamping
802 meshObject[RIGID_ANGULAR_DAMPING]=rigid.angularDamping
803 meshObject[RIGID_RESTITUTION]=rigid.restitution
804 meshObject[RIGID_FRICTION]=rigid.friction
806 for meshObject in reversed(rigidMeshes):
807 bl.object.makeParent(container, meshObject)
812 def _execute(filename):
814 load pmd file to context.
818 bl.progress_set('load %s' % filename, 0.0)
821 if not io.read(filename):
822 bl.message("fail to load %s" % filename)
824 bl.progress_set('loaded %s' % filename, 0.1)
827 model_name=io.getEnglishName()
828 if len(model_name)==0:
829 model_name=io.getName()
830 root=bl.object.createEmpty(model_name)
833 tex_dir=os.path.dirname(filename)
834 toonTextures, toonMaterial=__importToonTextures(io, tex_dir)
835 bl.object.makeParent(root, toonTextures)
838 mesh_objects=__importMaterialAndMesh(io, tex_dir, toonMaterial)
839 for o in mesh_objects:
840 bl.object.makeParent(root, o)
843 armature_object=__importArmature(io)
845 bl.object.makeParent(root, armature_object)
846 armature = bl.object.getData(armature_object)
848 # add armature modifier
849 for o in mesh_objects:
850 bl.modifier.addArmature(o, armature_object)
853 for n, b in bl.object.getPose(armature_object).bones.items():
856 # import rigid bodies
857 rigidBodies=__importRigidBodies(io)
859 bl.object.makeParent(root, rigidBodies)
862 constraints=__importConstraints(io)
864 bl.object.makeParent(root, constraints)
866 bl.object.activate(root)
871 def execute_24(filename):
872 bl.initialize('pmd_import', bpy.data.scenes.active)
873 _execute(filename.decode(bl.INTERNAL_ENCODING))
876 Blender.Window.FileSelector(
879 Blender.sys.makename(ext='.pmd'))
883 class IMPORT_OT_pmd(bpy.types.Operator):
884 bl_idname = "import_scene.pmd"
885 bl_label = 'Import PMD'
887 # List of operator properties, the attributes will be assigned
888 # to the class instance from the operator settings before calling.
890 path = StringProperty(
892 description="File path used for importing the PMD file",
893 maxlen= 1024, default= "")
894 filename = StringProperty(
896 description="Name of the file.")
897 directory = StringProperty(
899 description="Directory of the file.")
901 def execute(self, context):
902 bl.initialize('pmd_import', context.scene)
903 _execute(self.properties.path)
907 def invoke(self, context, event):
909 wm.add_fileselect(self)
910 return 'RUNNING_MODAL'
913 def menu_func(self, context):
914 self.layout.operator(IMPORT_OT_pmd.bl_idname,
915 text="MikuMikuDance model (.pmd)")
918 bpy.types.register(IMPORT_OT_pmd)
919 bpy.types.INFO_MT_file_import.append(menu_func)
922 bpy.types.unregister(IMPORT_OT_pmd)
923 bpy.types.INFO_MT_file_import.remove(menu_func)
925 if __name__=="__main__":