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)
450 toon_index=bl.material.addTexture(
452 bl.material.getTexture(
454 0 if m.toon_index==0xFF else m.toon_index
458 texture_name=m.getTexture()
460 for i, t in enumerate(texture_name.split('*')):
462 texture=textureMap[t]
464 path=os.path.join(tex_dir, t)
465 texture, image=bl.texture.create(path)
466 textureMap[texture_name]=texture
467 imageMap[material_index]=image
468 texture_index=bl.material.addTexture(material, texture)
469 if t.endswith('sph'):
471 setSphereMap(material, texture_index)
472 elif t.endswith('spa'):
474 setSphereMap(material, texture_index, 'ADD')
476 bl.mesh.addMaterial(mesh, material)
479 ############################################################
481 ############################################################
482 bl.progress_print('create vertices')
485 for v in l.each_vertex():
486 vertices.append(convert_coord(v.pos))
488 ############################################################
490 ############################################################
491 bl.progress_print('create faces')
494 mesh_face_materials=[]
497 for material_index in material_order:
498 face_offset=face_map[material_index]
499 m=l.materials[material_index]
500 material_faces=l.indices[face_offset:face_offset+m.vertex_count]
502 def degenerate(i0, i1, i2):
506 return i0==i1 or i1==i2 or i2==i0
508 for j in xrange(0, len(material_faces), 3):
510 i1=material_faces[j+1]
511 i2=material_faces[j+2]
513 triangle=[i2, i1, i0]
514 if degenerate(*triangle):
516 mesh_face_indices.append(triangle[0:3])
517 mesh_face_materials.append(material_index)
518 used_vertices.add(i0)
519 used_vertices.add(i1)
520 used_vertices.add(i2)
522 ############################################################
523 # create vertices & faces
524 ############################################################
525 bl.mesh.addGeometry(mesh, vertices, mesh_face_indices)
527 ############################################################
529 ############################################################
530 # create vertex group
532 for v in l.each_vertex():
533 vertex_groups[v.bone0]=True
534 vertex_groups[v.bone1]=True
535 for i in vertex_groups.keys():
536 bl.object.addVertexGroup(meshObject, get_bone_name(l, i))
539 bl.mesh.useVertexUV(mesh)
540 for i, v, mvert in zip(xrange(len(l.vertices)),
541 l.each_vertex(), mesh.verts):
543 bl.vertex.setNormal(mvert, convert_coord(v.normal))
545 w1=float(v.weight0)/100.0
547 bl.object.assignVertexGroup(meshObject, get_bone_name(l, v.bone0),
549 bl.object.assignVertexGroup(meshObject, get_bone_name(l, v.bone1),
552 ############################################################
554 ############################################################
557 for i, (face, material_index) in enumerate(
558 zip(mesh.faces, mesh_face_materials)):
560 index=mesh_material_map[material_index]
561 except KeyError as message:
562 print(message, mesh_material_map, m)
564 bl.face.setMaterial(face, index)
565 material=mesh.materials[index]
567 if bl.material.hasTexture(material):
568 uv_array=[l.getUV(i) for i in bl.face.getIndices(face)]
569 bl.mesh.setFaceUV(mesh, i, face,
571 [(uv.x, 1.0-uv.y) for uv in uv_array],
572 imageMap.get(index, None))
579 ############################################################
580 # clean up not used vertices
581 ############################################################
582 bl.progress_print('clean up vertices not used')
585 for i, v in enumerate(l.each_vertex()):
586 if i in used_vertices:
587 vertex_map[i]=len(vertex_map)
589 remove_vertices.append(i)
591 bl.mesh.vertsDelete(mesh, remove_vertices)
593 bl.progress_print('%s created' % mesh.name)
597 def __importMaterialAndMesh(io, tex_dir, toon_material):
599 @param l[in] mmd.PMDLoader
602 ############################################################
603 # shpaeキーで使われるマテリアル優先的に前に並べる
604 ############################################################
605 # shapeキーで使われる頂点インデックスを集める
606 shape_key_used_vertices=set()
607 if len(io.morph_list)>0:
610 for s in io.morph_list:
617 for index in base.indices:
618 shape_key_used_vertices.add(index)
620 # マテリアルに含まれる頂点がshape_keyに含まれるか否か?
621 def isMaterialUsedInShape(offset, m):
622 for i in xrange(offset, offset+m.vertex_count):
623 if io.indices[i] in shape_key_used_vertices:
626 material_with_shape=set()
628 # 各マテリアルの開始頂点インデックスを記録する
631 for i, m in enumerate(io.materials):
632 face_map[i]=face_count
633 if isMaterialUsedInShape(face_count, m):
634 material_with_shape.add(i)
635 face_count+=m.vertex_count
637 # shapeキーで使われる頂点のあるマテリアル
638 material_with_shape=list(material_with_shape)
639 material_with_shape.sort()
641 # shapeキーに使われていないマテリアル
642 material_without_shape=[]
643 for i in range(len(io.materials)):
644 if not i in material_with_shape:
645 material_without_shape.append(i)
648 def __splitList(l, length):
649 for i in range(0, len(l), length):
652 def __importMeshAndShape(material16, name):
653 mesh, meshObject=bl.mesh.create(name)
656 bl.object.deselectAll()
657 bl.object.activate(meshObject)
659 # shapeキーで使われる順に並べなおしたマテリアル16個分の
661 vertex_map=__import16MaerialAndMesh(
662 meshObject, io, material16, face_map, tex_dir, toon_material)
665 __importShape(meshObject, io, vertex_map)
670 mesh_objects=[__importMeshAndShape(material16, 'with_shape')
671 for material16 in __splitList(material_with_shape, 16)]
673 mesh_objects+=[__importMeshAndShape(material16, 'mesh')
674 for material16 in __splitList(material_without_shape, 16)]
679 def __importConstraints(io):
682 print("create constraint")
683 container=bl.object.createEmpty('Constraints')
685 True, 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,
688 False, False, False, False, False, False, False, False,
690 material=bl.material.create('constraint')
691 material.diffuse_color=(1, 0, 0)
693 for i, c in enumerate(io.constraints):
694 bpy.ops.mesh.primitive_uv_sphere_add(
698 location=(c.pos.x, c.pos.z, c.pos.y),
701 meshObject=bl.object.getActive()
702 constraintMeshes.append(meshObject)
703 mesh=bl.object.getData(meshObject)
704 bl.mesh.addMaterial(mesh, material)
705 meshObject.name='c_%d' % i
706 #meshObject.draw_transparent=True
707 #meshObject.draw_wire=True
708 meshObject.max_draw_type='SOLID'
710 meshObject.rotation_euler=(-rot.x, -rot.z, -rot.y)
712 meshObject[CONSTRAINT_NAME]=c.getName()
713 meshObject[CONSTRAINT_A]=io.rigidbodies[c.rigidA].getName()
714 meshObject[CONSTRAINT_B]=io.rigidbodies[c.rigidB].getName()
715 meshObject[CONSTRAINT_POS_MIN]=VtoV(c.constraintPosMin)
716 meshObject[CONSTRAINT_POS_MAX]=VtoV(c.constraintPosMax)
717 meshObject[CONSTRAINT_ROT_MIN]=VtoV(c.constraintRotMin)
718 meshObject[CONSTRAINT_ROT_MAX]=VtoV(c.constraintRotMax)
719 meshObject[CONSTRAINT_SPRING_POS]=VtoV(c.springPos)
720 meshObject[CONSTRAINT_SPRING_ROT]=VtoV(c.springRot)
722 for meshObject in reversed(constraintMeshes):
723 bl.object.makeParent(container, meshObject)
728 def __importRigidBodies(io):
731 print("create rigid bodies")
733 container=bl.object.createEmpty('RigidBodies')
735 True, 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,
738 False, False, False, False, False, False, False, False,
740 material=bl.material.create('rigidBody')
742 for rigid in io.rigidbodies:
743 if rigid.boneIndex==0xFFFF:
747 bone=io.bones[rigid.boneIndex]
748 pos=bone.pos+rigid.position
750 if rigid.shapeType==pmd.SHAPE_SPHERE:
751 bpy.ops.mesh.primitive_ico_sphere_add(
752 location=(pos.x, pos.z, pos.y),
755 bpy.ops.transform.resize(
756 value=(rigid.w, rigid.w, rigid.w))
757 elif rigid.shapeType==pmd.SHAPE_BOX:
758 bpy.ops.mesh.primitive_cube_add(
759 location=(pos.x, pos.z, pos.y),
762 bpy.ops.transform.resize(
763 value=(rigid.w, rigid.d, rigid.h))
764 elif rigid.shapeType==pmd.SHAPE_CAPSULE:
765 bpy.ops.mesh.primitive_tube_add(
766 location=(pos.x, pos.z, pos.y),
769 bpy.ops.transform.resize(
770 value=(rigid.w, rigid.w, rigid.h))
774 meshObject=bl.object.getActive()
775 mesh=bl.object.getData(meshObject)
776 rigidMeshes.append(meshObject)
777 bl.mesh.addMaterial(mesh, material)
778 meshObject.name=rigid.getName()
779 #meshObject.draw_transparent=True
780 #meshObject.draw_wire=True
781 meshObject.max_draw_type='WIRE'
783 meshObject.rotation_euler=(-rot.x, -rot.z, -rot.y)
786 meshObject[RIGID_SHAPE_TYPE]=rigid.shapeType
787 meshObject[RIGID_PROCESS_TYPE]=rigid.processType
789 bone_name = englishmap.getEnglishBoneName(bone.getName())
791 bone_name=bone.getName()
792 meshObject[RIGID_BONE_NAME]=bone_name
794 meshObject[RIGID_GROUP]=rigid.group
795 meshObject[RIGID_INTERSECTION_GROUP]=rigid.target
796 meshObject[RIGID_WEIGHT]=rigid.weight
797 meshObject[RIGID_LINEAR_DAMPING]=rigid.linearDamping
798 meshObject[RIGID_ANGULAR_DAMPING]=rigid.angularDamping
799 meshObject[RIGID_RESTITUTION]=rigid.restitution
800 meshObject[RIGID_FRICTION]=rigid.friction
802 for meshObject in reversed(rigidMeshes):
803 bl.object.makeParent(container, meshObject)
808 def _execute(filename):
810 load pmd file to context.
814 bl.progress_set('load %s' % filename, 0.0)
817 if not io.read(filename):
818 bl.message("fail to load %s" % filename)
820 bl.progress_set('loaded %s' % filename, 0.1)
823 model_name=io.getEnglishName()
824 if len(model_name)==0:
825 model_name=io.getName()
826 root=bl.object.createEmpty(model_name)
829 tex_dir=os.path.dirname(filename)
830 toonTextures, toonMaterial=__importToonTextures(io, tex_dir)
831 bl.object.makeParent(root, toonTextures)
834 mesh_objects=__importMaterialAndMesh(io, tex_dir, toonMaterial)
835 for o in mesh_objects:
836 bl.object.makeParent(root, o)
839 armature_object=__importArmature(io)
841 bl.object.makeParent(root, armature_object)
842 armature = bl.object.getData(armature_object)
844 # add armature modifier
845 for o in mesh_objects:
846 bl.modifier.addArmature(o, armature_object)
849 for n, b in bl.object.getPose(armature_object).bones.items():
852 # import rigid bodies
853 rigidBodies=__importRigidBodies(io)
855 bl.object.makeParent(root, rigidBodies)
858 constraints=__importConstraints(io)
860 bl.object.makeParent(root, constraints)
862 bl.object.activate(root)
867 def execute_24(filename):
868 bl.initialize('pmd_import', bpy.data.scenes.active)
869 _execute(filename.decode(bl.INTERNAL_ENCODING))
872 Blender.Window.FileSelector(
875 Blender.sys.makename(ext='.pmd'))
879 class IMPORT_OT_pmd(bpy.types.Operator):
880 bl_idname = "import_scene.pmd"
881 bl_label = 'Import PMD'
883 # List of operator properties, the attributes will be assigned
884 # to the class instance from the operator settings before calling.
886 path = StringProperty(
888 description="File path used for importing the PMD file",
889 maxlen= 1024, default= "")
890 filename = StringProperty(
892 description="Name of the file.")
893 directory = StringProperty(
895 description="Directory of the file.")
897 def execute(self, context):
898 bl.initialize('pmd_import', context.scene)
899 _execute(self.properties.path)
903 def invoke(self, context, event):
905 wm.add_fileselect(self)
906 return 'RUNNING_MODAL'
909 def menu_func(self, context):
910 self.layout.operator(IMPORT_OT_pmd.bl_idname,
911 text="MikuMikuDance model (.pmd)")
914 bpy.types.register(IMPORT_OT_pmd)
915 bpy.types.INFO_MT_file_import.append(menu_func)
918 bpy.types.unregister(IMPORT_OT_pmd)
919 bpy.types.INFO_MT_file_import.remove(menu_func)
921 if __name__=="__main__":