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
450 texture_name=m.getTexture()
452 for i, t in enumerate(texture_name.split('*')):
454 texture=textureMap[t]
456 path=os.path.join(tex_dir, t)
457 texture, image=bl.texture.create(path)
458 textureMap[texture_name]=texture
459 imageMap[material_index]=image
460 texture_index=bl.material.addTexture(material, texture)
461 if t.endswith('sph'):
463 setSphereMap(material, texture_index)
464 elif t.endswith('spa'):
466 setSphereMap(material, texture_index, 'ADD')
469 material=createPmdMaterial(m, material_index)
470 toon_index=bl.material.addTexture(
472 bl.material.getTexture(
474 0 if m.toon_index==0xFF else m.toon_index
478 bl.mesh.addMaterial(mesh, material)
482 ############################################################
484 ############################################################
485 bl.progress_print('create vertices')
488 for v in l.each_vertex():
489 vertices.append(convert_coord(v.pos))
491 ############################################################
493 ############################################################
494 bl.progress_print('create faces')
497 mesh_face_materials=[]
500 for material_index in material_order:
501 face_offset=face_map[material_index]
502 m=l.materials[material_index]
503 material_faces=l.indices[face_offset:face_offset+m.vertex_count]
505 def degenerate(i0, i1, i2):
509 return i0==i1 or i1==i2 or i2==i0
511 for j in xrange(0, len(material_faces), 3):
513 i1=material_faces[j+1]
514 i2=material_faces[j+2]
516 triangle=[i2, i1, i0]
517 if degenerate(*triangle):
519 mesh_face_indices.append(triangle[0:3])
520 mesh_face_materials.append(material_index)
521 used_vertices.add(i0)
522 used_vertices.add(i1)
523 used_vertices.add(i2)
525 ############################################################
526 # create vertices & faces
527 ############################################################
528 bl.mesh.addGeometry(mesh, vertices, mesh_face_indices)
530 ############################################################
532 ############################################################
533 # create vertex group
535 for v in l.each_vertex():
536 vertex_groups[v.bone0]=True
537 vertex_groups[v.bone1]=True
538 for i in vertex_groups.keys():
539 bl.object.addVertexGroup(meshObject, get_bone_name(l, i))
542 bl.mesh.useVertexUV(mesh)
543 for i, v, mvert in zip(xrange(len(l.vertices)),
544 l.each_vertex(), mesh.verts):
546 bl.vertex.setNormal(mvert, convert_coord(v.normal))
548 w1=float(v.weight0)/100.0
550 bl.object.assignVertexGroup(meshObject, get_bone_name(l, v.bone0),
552 bl.object.assignVertexGroup(meshObject, get_bone_name(l, v.bone1),
555 ############################################################
557 ############################################################
560 for i, (face, material_index) in enumerate(
561 zip(mesh.faces, mesh_face_materials)):
563 index=mesh_material_map[material_index]
564 except KeyError as message:
565 print(message, mesh_material_map, m)
567 bl.face.setMaterial(face, index)
568 material=mesh.materials[index]
570 if bl.material.hasTexture(material):
571 uv_array=[l.getUV(i) for i in bl.face.getIndices(face)]
572 bl.mesh.setFaceUV(mesh, i, face,
574 [(uv.x, 1.0-uv.y) for uv in uv_array],
575 imageMap.get(index, None))
582 ############################################################
583 # clean up not used vertices
584 ############################################################
585 bl.progress_print('clean up vertices not used')
588 for i, v in enumerate(l.each_vertex()):
589 if i in used_vertices:
590 vertex_map[i]=len(vertex_map)
592 remove_vertices.append(i)
594 bl.mesh.vertsDelete(mesh, remove_vertices)
596 bl.progress_print('%s created' % mesh.name)
600 def __importMaterialAndMesh(io, tex_dir, toon_material):
602 @param l[in] mmd.PMDLoader
605 ############################################################
606 # shpaeキーで使われるマテリアル優先的に前に並べる
607 ############################################################
608 # shapeキーで使われる頂点インデックスを集める
609 shape_key_used_vertices=set()
610 if len(io.morph_list)>0:
613 for s in io.morph_list:
620 for index in base.indices:
621 shape_key_used_vertices.add(index)
623 # マテリアルに含まれる頂点がshape_keyに含まれるか否か?
624 def isMaterialUsedInShape(offset, m):
625 for i in xrange(offset, offset+m.vertex_count):
626 if io.indices[i] in shape_key_used_vertices:
629 material_with_shape=set()
631 # 各マテリアルの開始頂点インデックスを記録する
634 for i, m in enumerate(io.materials):
635 face_map[i]=face_count
636 if isMaterialUsedInShape(face_count, m):
637 material_with_shape.add(i)
638 face_count+=m.vertex_count
640 # shapeキーで使われる頂点のあるマテリアル
641 material_with_shape=list(material_with_shape)
642 material_with_shape.sort()
644 # shapeキーに使われていないマテリアル
645 material_without_shape=[]
646 for i in range(len(io.materials)):
647 if not i in material_with_shape:
648 material_without_shape.append(i)
651 def __splitList(l, length):
652 for i in range(0, len(l), length):
655 def __importMeshAndShape(material16, name):
656 mesh, meshObject=bl.mesh.create(name)
659 bl.object.deselectAll()
660 bl.object.activate(meshObject)
662 # shapeキーで使われる順に並べなおしたマテリアル16個分の
664 vertex_map=__import16MaerialAndMesh(
665 meshObject, io, material16, face_map, tex_dir, toon_material)
668 __importShape(meshObject, io, vertex_map)
673 mesh_objects=[__importMeshAndShape(material16, 'with_shape')
674 for material16 in __splitList(material_with_shape, 16)]
676 mesh_objects+=[__importMeshAndShape(material16, 'mesh')
677 for material16 in __splitList(material_without_shape, 16)]
682 def __importConstraints(io):
685 print("create constraint")
686 container=bl.object.createEmpty('Constraints')
688 True, False, False, False, False, False, False, False,
689 False, 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,
693 material=bl.material.create('constraint')
694 material.diffuse_color=(1, 0, 0)
696 for i, c in enumerate(io.constraints):
697 bpy.ops.mesh.primitive_uv_sphere_add(
701 location=(c.pos.x, c.pos.z, c.pos.y),
704 meshObject=bl.object.getActive()
705 constraintMeshes.append(meshObject)
706 mesh=bl.object.getData(meshObject)
707 bl.mesh.addMaterial(mesh, material)
708 meshObject.name='c_%d' % i
709 #meshObject.draw_transparent=True
710 #meshObject.draw_wire=True
711 meshObject.max_draw_type='SOLID'
713 meshObject.rotation_euler=(-rot.x, -rot.z, -rot.y)
715 meshObject[CONSTRAINT_NAME]=c.getName()
716 meshObject[CONSTRAINT_A]=io.rigidbodies[c.rigidA].getName()
717 meshObject[CONSTRAINT_B]=io.rigidbodies[c.rigidB].getName()
718 meshObject[CONSTRAINT_POS_MIN]=VtoV(c.constraintPosMin)
719 meshObject[CONSTRAINT_POS_MAX]=VtoV(c.constraintPosMax)
720 meshObject[CONSTRAINT_ROT_MIN]=VtoV(c.constraintRotMin)
721 meshObject[CONSTRAINT_ROT_MAX]=VtoV(c.constraintRotMax)
722 meshObject[CONSTRAINT_SPRING_POS]=VtoV(c.springPos)
723 meshObject[CONSTRAINT_SPRING_ROT]=VtoV(c.springRot)
725 for meshObject in reversed(constraintMeshes):
726 bl.object.makeParent(container, meshObject)
731 def __importRigidBodies(io):
734 print("create rigid bodies")
736 container=bl.object.createEmpty('RigidBodies')
738 True, False, False, False, False, False, False, False,
739 False, 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,
743 material=bl.material.create('rigidBody')
745 for rigid in io.rigidbodies:
746 if rigid.boneIndex==0xFFFF:
750 bone=io.bones[rigid.boneIndex]
751 pos=bone.pos+rigid.position
753 if rigid.shapeType==pmd.SHAPE_SPHERE:
754 bpy.ops.mesh.primitive_ico_sphere_add(
755 location=(pos.x, pos.z, pos.y),
758 bpy.ops.transform.resize(
759 value=(rigid.w, rigid.w, rigid.w))
760 elif rigid.shapeType==pmd.SHAPE_BOX:
761 bpy.ops.mesh.primitive_cube_add(
762 location=(pos.x, pos.z, pos.y),
765 bpy.ops.transform.resize(
766 value=(rigid.w, rigid.d, rigid.h))
767 elif rigid.shapeType==pmd.SHAPE_CAPSULE:
768 bpy.ops.mesh.primitive_tube_add(
769 location=(pos.x, pos.z, pos.y),
772 bpy.ops.transform.resize(
773 value=(rigid.w, rigid.w, rigid.h))
777 meshObject=bl.object.getActive()
778 mesh=bl.object.getData(meshObject)
779 rigidMeshes.append(meshObject)
780 bl.mesh.addMaterial(mesh, material)
781 meshObject.name=rigid.getName()
782 #meshObject.draw_transparent=True
783 #meshObject.draw_wire=True
784 meshObject.max_draw_type='WIRE'
786 meshObject.rotation_euler=(-rot.x, -rot.z, -rot.y)
789 meshObject[RIGID_SHAPE_TYPE]=rigid.shapeType
790 meshObject[RIGID_PROCESS_TYPE]=rigid.processType
792 bone_name = englishmap.getEnglishBoneName(bone.getName())
794 bone_name=bone.getName()
795 meshObject[RIGID_BONE_NAME]=bone_name
797 meshObject[RIGID_GROUP]=rigid.group
798 meshObject[RIGID_INTERSECTION_GROUP]=rigid.target
799 meshObject[RIGID_WEIGHT]=rigid.weight
800 meshObject[RIGID_LINEAR_DAMPING]=rigid.linearDamping
801 meshObject[RIGID_ANGULAR_DAMPING]=rigid.angularDamping
802 meshObject[RIGID_RESTITUTION]=rigid.restitution
803 meshObject[RIGID_FRICTION]=rigid.friction
805 for meshObject in reversed(rigidMeshes):
806 bl.object.makeParent(container, meshObject)
811 def _execute(filename):
813 load pmd file to context.
817 bl.progress_set('load %s' % filename, 0.0)
820 if not io.read(filename):
821 bl.message("fail to load %s" % filename)
823 bl.progress_set('loaded %s' % filename, 0.1)
826 model_name=io.getEnglishName()
827 if len(model_name)==0:
828 model_name=io.getName()
829 root=bl.object.createEmpty(model_name)
832 tex_dir=os.path.dirname(filename)
833 toonTextures, toonMaterial=__importToonTextures(io, tex_dir)
834 bl.object.makeParent(root, toonTextures)
837 mesh_objects=__importMaterialAndMesh(io, tex_dir, toonMaterial)
838 for o in mesh_objects:
839 bl.object.makeParent(root, o)
842 armature_object=__importArmature(io)
844 bl.object.makeParent(root, armature_object)
845 armature = bl.object.getData(armature_object)
847 # add armature modifier
848 for o in mesh_objects:
849 bl.modifier.addArmature(o, armature_object)
852 for n, b in bl.object.getPose(armature_object).bones.items():
855 # import rigid bodies
856 rigidBodies=__importRigidBodies(io)
858 bl.object.makeParent(root, rigidBodies)
861 constraints=__importConstraints(io)
863 bl.object.makeParent(root, constraints)
865 bl.object.activate(root)
870 def execute_24(filename):
871 bl.initialize('pmd_import', bpy.data.scenes.active)
872 _execute(filename.decode(bl.INTERNAL_ENCODING))
875 Blender.Window.FileSelector(
878 Blender.sys.makename(ext='.pmd'))
882 class IMPORT_OT_pmd(bpy.types.Operator):
883 bl_idname = "import_scene.pmd"
884 bl_label = 'Import PMD'
886 # List of operator properties, the attributes will be assigned
887 # to the class instance from the operator settings before calling.
889 path = StringProperty(
891 description="File path used for importing the PMD file",
892 maxlen= 1024, default= "")
893 filename = StringProperty(
895 description="Name of the file.")
896 directory = StringProperty(
898 description="Directory of the file.")
900 def execute(self, context):
901 bl.initialize('pmd_import', context.scene)
902 _execute(self.properties.path)
906 def invoke(self, context, event):
908 wm.add_fileselect(self)
909 return 'RUNNING_MODAL'
912 def menu_func(self, context):
913 self.layout.operator(IMPORT_OT_pmd.bl_idname,
914 text="MikuMikuDance model (.pmd)")
917 bpy.types.register(IMPORT_OT_pmd)
918 bpy.types.INFO_MT_file_import.append(menu_func)
921 bpy.types.unregister(IMPORT_OT_pmd)
922 bpy.types.INFO_MT_file_import.remove(menu_func)
924 if __name__=="__main__":