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):
330 bone.head = bl.createVector(*convert_coord(b.pos))
331 bone.tail=bone.head+bl.createVector(0, 1, 0)
333 if bone.name=="center_t":
334 # センターボーンは(0, 1, 0)の方向を向いていないと具合が悪い
335 parent.tail=parent.head+bl.createVector(0, 1, 0)
336 bone.head=parent.tail
337 bone.tail=bone.head+bl.createVector(0, 1, 0)
339 if parent.tail==bone.head:
342 print('diffurence with parent.tail and head', name)
345 bl.bone.setConnected(bone)
347 bl.bone.setLayerMask(bone, [0, 1])
350 bone.head = bl.createVector(*convert_coord(b.pos))
351 bone.tail = bl.createVector(*convert_coord(b.tail))
354 if parent.tail==bone.head:
355 bl.bone.setConnected(bone)
357 if bone.head==bone.tail:
358 bone.tail=bone.head+bl.createVector(0, 1, 0)
361 __build(armature, c, b, bone)
364 def __importArmature(l):
365 armature, armature_object=bl.armature.create()
368 bl.armature.makeEditable(armature_object)
371 __build(armature, b, None, None)
372 bl.armature.update(armature)
376 pose = bl.object.getPose(armature_object)
378 target=l.bones[ik.target]
379 name = englishmap.getEnglishBoneName(target.getName())
381 name=target.getName()
382 p_bone = pose.bones[name]
384 print('not found', name)
386 if len(ik.children) >= 16:
387 print('over MAX_CHAINLEN', ik, len(ik.children))
389 effector_name=englishmap.getEnglishBoneName(
390 l.bones[ik.index].getName())
391 if not effector_name:
392 effector_name=l.bones[ik.index].getName()
394 constraint=bl.armature.createIkConstraint(armature_object,
395 p_bone, effector_name, ik)
397 bl.armature.makeEditable(armature_object)
398 bl.armature.update(armature)
405 for i, g in enumerate(l.bone_group_list):
406 name=get_group_name(g)
407 bl.object.createBoneGroup(armature_object, name, "THEME%02d" % (i+1))
409 # assign bone to group
410 for b_index, g_index in l.bone_display_list:
413 bone_name=englishmap.getEnglishBoneName(b.getName())
415 bone_name=b.getName()
417 g=l.bone_group_list[g_index-1]
418 group_name=get_group_name(g)
421 pose.bones[bone_name].bone_group=pose.bone_groups[group_name]
425 return armature_object
428 def __import16MaerialAndMesh(meshObject, l,
429 material_order, face_map, tex_dir, toon_material):
431 mesh=bl.object.getData(meshObject)
432 ############################################################
434 ############################################################
435 bl.progress_print('create materials')
441 for material_index in material_order:
443 m=l.materials[material_index]
444 mesh_material_map[material_index]=index
448 material=createPmdMaterial(m, material_index)
449 toon_index=bl.material.addTexture(
451 bl.material.getTexture(
453 0 if m.toon_index==0xFF else m.toon_index
457 texture_name=m.getTexture()
459 for i, t in enumerate(texture_name.split('*')):
461 texture=textureMap[t]
463 path=os.path.join(tex_dir, t)
464 texture, image=bl.texture.create(path)
465 textureMap[texture_name]=texture
466 imageMap[material_index]=image
467 texture_index=bl.material.addTexture(material, texture)
468 if t.endswith('sph'):
470 setSphereMap(material, texture_index)
471 elif t.endswith('spa'):
473 setSphereMap(material, texture_index, 'ADD')
475 bl.mesh.addMaterial(mesh, material)
478 ############################################################
480 ############################################################
481 bl.progress_print('create vertices')
484 for v in l.each_vertex():
485 vertices.append(convert_coord(v.pos))
487 ############################################################
489 ############################################################
490 bl.progress_print('create faces')
493 mesh_face_materials=[]
496 for material_index in material_order:
497 face_offset=face_map[material_index]
498 m=l.materials[material_index]
499 material_faces=l.indices[face_offset:face_offset+m.vertex_count]
501 def degenerate(i0, i1, i2):
505 return i0==i1 or i1==i2 or i2==i0
507 for j in xrange(0, len(material_faces), 3):
509 i1=material_faces[j+1]
510 i2=material_faces[j+2]
512 triangle=[i2, i1, i0]
513 if degenerate(*triangle):
515 mesh_face_indices.append(triangle[0:3])
516 mesh_face_materials.append(material_index)
517 used_vertices.add(i0)
518 used_vertices.add(i1)
519 used_vertices.add(i2)
521 ############################################################
522 # create vertices & faces
523 ############################################################
524 bl.mesh.addGeometry(mesh, vertices, mesh_face_indices)
526 ############################################################
528 ############################################################
529 # create vertex group
531 for v in l.each_vertex():
532 vertex_groups[v.bone0]=True
533 vertex_groups[v.bone1]=True
534 for i in vertex_groups.keys():
535 bl.object.addVertexGroup(meshObject, get_bone_name(l, i))
538 bl.mesh.useVertexUV(mesh)
539 for i, v, mvert in zip(xrange(len(l.vertices)),
540 l.each_vertex(), mesh.verts):
542 bl.vertex.setNormal(mvert, convert_coord(v.normal))
544 w1=float(v.weight0)/100.0
546 bl.object.assignVertexGroup(meshObject, get_bone_name(l, v.bone0),
548 bl.object.assignVertexGroup(meshObject, get_bone_name(l, v.bone1),
551 ############################################################
553 ############################################################
556 for i, (face, material_index) in enumerate(
557 zip(mesh.faces, mesh_face_materials)):
559 index=mesh_material_map[material_index]
560 except KeyError as message:
561 print(message, mesh_material_map, m)
563 bl.face.setMaterial(face, index)
564 material=mesh.materials[index]
566 if bl.material.hasTexture(material):
567 uv_array=[l.getUV(i) for i in bl.face.getIndices(face)]
568 bl.mesh.setFaceUV(mesh, i, face,
570 [(uv.x, 1.0-uv.y) for uv in uv_array],
571 imageMap.get(index, None))
578 ############################################################
579 # clean up not used vertices
580 ############################################################
581 bl.progress_print('clean up vertices not used')
584 for i, v in enumerate(l.each_vertex()):
585 if i in used_vertices:
586 vertex_map[i]=len(vertex_map)
588 remove_vertices.append(i)
590 bl.mesh.vertsDelete(mesh, remove_vertices)
592 bl.progress_print('%s created' % mesh.name)
596 def __importMaterialAndMesh(io, tex_dir, toon_material):
598 @param l[in] mmd.PMDLoader
601 ############################################################
602 # shpaeキーで使われるマテリアル優先的に前に並べる
603 ############################################################
604 # shapeキーで使われる頂点インデックスを集める
605 shape_key_used_vertices=set()
606 if len(io.morph_list)>0:
609 for s in io.morph_list:
616 for index in base.indices:
617 shape_key_used_vertices.add(index)
619 # マテリアルに含まれる頂点がshape_keyに含まれるか否か?
620 def isMaterialUsedInShape(offset, m):
621 for i in xrange(offset, offset+m.vertex_count):
622 if io.indices[i] in shape_key_used_vertices:
625 material_with_shape=set()
627 # 各マテリアルの開始頂点インデックスを記録する
630 for i, m in enumerate(io.materials):
631 face_map[i]=face_count
632 if isMaterialUsedInShape(face_count, m):
633 material_with_shape.add(i)
634 face_count+=m.vertex_count
636 # shapeキーで使われる頂点のあるマテリアル
637 material_with_shape=list(material_with_shape)
638 material_with_shape.sort()
640 # shapeキーに使われていないマテリアル
641 material_without_shape=[]
642 for i in range(len(io.materials)):
643 if not i in material_with_shape:
644 material_without_shape.append(i)
647 def __splitList(l, length):
648 for i in range(0, len(l), length):
651 def __importMeshAndShape(material16, name):
652 mesh, meshObject=bl.mesh.create(name)
655 bl.object.deselectAll()
656 bl.object.activate(meshObject)
658 # shapeキーで使われる順に並べなおしたマテリアル16個分の
660 vertex_map=__import16MaerialAndMesh(
661 meshObject, io, material16, face_map, tex_dir, toon_material)
664 __importShape(meshObject, io, vertex_map)
669 mesh_objects=[__importMeshAndShape(material16, 'with_shape')
670 for material16 in __splitList(material_with_shape, 16)]
672 mesh_objects+=[__importMeshAndShape(material16, 'mesh')
673 for material16 in __splitList(material_without_shape, 16)]
678 def __importConstraints(io):
681 print("create constraint")
682 container=bl.object.createEmpty('Constraints')
684 True, 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,
687 False, False, False, False, False, False, False, False,
689 material=bl.material.create('constraint')
690 material.diffuse_color=(1, 0, 0)
692 for i, c in enumerate(io.constraints):
693 bpy.ops.mesh.primitive_uv_sphere_add(
697 location=(c.pos.x, c.pos.z, c.pos.y),
700 meshObject=bl.object.getActive()
701 constraintMeshes.append(meshObject)
702 mesh=bl.object.getData(meshObject)
703 bl.mesh.addMaterial(mesh, material)
704 meshObject.name='c_%d' % i
705 #meshObject.draw_transparent=True
706 #meshObject.draw_wire=True
707 meshObject.max_draw_type='SOLID'
709 meshObject.rotation_euler=(-rot.x, -rot.z, -rot.y)
711 meshObject[CONSTRAINT_NAME]=c.getName()
712 meshObject[CONSTRAINT_A]=io.rigidbodies[c.rigidA].getName()
713 meshObject[CONSTRAINT_B]=io.rigidbodies[c.rigidB].getName()
714 meshObject[CONSTRAINT_POS_MIN]=VtoV(c.constraintPosMin)
715 meshObject[CONSTRAINT_POS_MAX]=VtoV(c.constraintPosMax)
716 meshObject[CONSTRAINT_ROT_MIN]=VtoV(c.constraintRotMin)
717 meshObject[CONSTRAINT_ROT_MAX]=VtoV(c.constraintRotMax)
718 meshObject[CONSTRAINT_SPRING_POS]=VtoV(c.springPos)
719 meshObject[CONSTRAINT_SPRING_ROT]=VtoV(c.springRot)
721 for meshObject in reversed(constraintMeshes):
722 bl.object.makeParent(container, meshObject)
727 def __importRigidBodies(io):
730 print("create rigid bodies")
732 container=bl.object.createEmpty('RigidBodies')
734 True, 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,
737 False, False, False, False, False, False, False, False,
739 material=bl.material.create('rigidBody')
741 for rigid in io.rigidbodies:
742 if rigid.boneIndex==0xFFFF:
746 bone=io.bones[rigid.boneIndex]
747 pos=bone.pos+rigid.position
749 if rigid.shapeType==pmd.SHAPE_SPHERE:
750 bpy.ops.mesh.primitive_ico_sphere_add(
751 location=(pos.x, pos.z, pos.y),
754 bpy.ops.transform.resize(
755 value=(rigid.w, rigid.w, rigid.w))
756 elif rigid.shapeType==pmd.SHAPE_BOX:
757 bpy.ops.mesh.primitive_cube_add(
758 location=(pos.x, pos.z, pos.y),
761 bpy.ops.transform.resize(
762 value=(rigid.w, rigid.d, rigid.h))
763 elif rigid.shapeType==pmd.SHAPE_CAPSULE:
764 bpy.ops.mesh.primitive_tube_add(
765 location=(pos.x, pos.z, pos.y),
768 bpy.ops.transform.resize(
769 value=(rigid.w, rigid.w, rigid.h))
773 meshObject=bl.object.getActive()
774 mesh=bl.object.getData(meshObject)
775 rigidMeshes.append(meshObject)
776 bl.mesh.addMaterial(mesh, material)
777 meshObject.name=rigid.getName()
778 #meshObject.draw_transparent=True
779 #meshObject.draw_wire=True
780 meshObject.max_draw_type='WIRE'
782 meshObject.rotation_euler=(-rot.x, -rot.z, -rot.y)
785 meshObject[RIGID_SHAPE_TYPE]=rigid.shapeType
786 meshObject[RIGID_PROCESS_TYPE]=rigid.processType
788 bone_name = englishmap.getEnglishBoneName(bone.getName())
790 bone_name=bone.getName()
791 meshObject[RIGID_BONE_NAME]=bone_name
793 meshObject[RIGID_GROUP]=rigid.group
794 meshObject[RIGID_INTERSECTION_GROUP]=rigid.target
795 meshObject[RIGID_WEIGHT]=rigid.weight
796 meshObject[RIGID_LINEAR_DAMPING]=rigid.linearDamping
797 meshObject[RIGID_ANGULAR_DAMPING]=rigid.angularDamping
798 meshObject[RIGID_RESTITUTION]=rigid.restitution
799 meshObject[RIGID_FRICTION]=rigid.friction
801 for meshObject in reversed(rigidMeshes):
802 bl.object.makeParent(container, meshObject)
807 def _execute(filename):
809 load pmd file to context.
813 bl.progress_set('load %s' % filename, 0.0)
816 if not io.read(filename):
817 bl.message("fail to load %s" % filename)
819 bl.progress_set('loaded %s' % filename, 0.1)
822 model_name=io.getEnglishName()
823 if len(model_name)==0:
824 model_name=io.getName()
825 root=bl.object.createEmpty(model_name)
828 tex_dir=os.path.dirname(filename)
829 toonTextures, toonMaterial=__importToonTextures(io, tex_dir)
830 bl.object.makeParent(root, toonTextures)
833 mesh_objects=__importMaterialAndMesh(io, tex_dir, toonMaterial)
834 for o in mesh_objects:
835 bl.object.makeParent(root, o)
838 armature_object=__importArmature(io)
840 bl.object.makeParent(root, armature_object)
841 armature = bl.object.getData(armature_object)
843 # add armature modifier
844 for o in mesh_objects:
845 bl.modifier.addArmature(o, armature_object)
848 for n, b in bl.object.getPose(armature_object).bones.items():
851 # import rigid bodies
852 rigidBodies=__importRigidBodies(io)
854 bl.object.makeParent(root, rigidBodies)
857 constraints=__importConstraints(io)
859 bl.object.makeParent(root, constraints)
861 bl.object.activate(root)
866 def execute_24(filename):
867 bl.initialize('pmd_import', bpy.data.scenes.active)
868 _execute(filename.decode(bl.INTERNAL_ENCODING))
871 Blender.Window.FileSelector(
874 Blender.sys.makename(ext='.pmd'))
878 class IMPORT_OT_pmd(bpy.types.Operator):
879 bl_idname = "import_scene.pmd"
880 bl_label = 'Import PMD'
882 # List of operator properties, the attributes will be assigned
883 # to the class instance from the operator settings before calling.
885 path = StringProperty(
887 description="File path used for importing the PMD file",
888 maxlen= 1024, default= "")
889 filename = StringProperty(
891 description="Name of the file.")
892 directory = StringProperty(
894 description="Directory of the file.")
896 def execute(self, context):
897 bl.initialize('pmd_import', context.scene)
898 _execute(self.properties.path)
902 def invoke(self, context, event):
904 wm.add_fileselect(self)
905 return 'RUNNING_MODAL'
908 def menu_func(self, context):
909 self.layout.operator(IMPORT_OT_pmd.bl_idname,
910 text="MikuMikuDance model (.pmd)")
913 bpy.types.register(IMPORT_OT_pmd)
914 bpy.types.INFO_MT_file_import.append(menu_func)
917 bpy.types.unregister(IMPORT_OT_pmd)
918 bpy.types.INFO_MT_file_import.remove(menu_func)
920 if __name__=="__main__":