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.
35 MMD_SHAPE_GROUP_NAME='_MMD_SHAPE'
36 BASE_SHAPE_NAME='Basis'
37 RIGID_SHAPE_TYPE='rigid_shape_type'
38 RIGID_PROCESS_TYPE='rigid_process_type'
39 RIGID_BONE_NAME='rigid_bone_name'
40 #RIGID_LOCATION='rigid_loation'
41 RIGID_GROUP='ribid_group'
42 RIGID_INTERSECTION_GROUP='rigid_intersection_group'
43 RIGID_WEIGHT='rigid_weight'
44 RIGID_LINEAR_DAMPING='rigid_linear_damping'
45 RIGID_ANGULAR_DAMPING='rigid_angular_damping'
46 RIGID_RESTITUTION='rigid_restitution'
47 RIGID_FRICTION='rigid_friction'
48 CONSTRAINT_NAME='constraint_name'
49 CONSTRAINT_A='const_a'
50 CONSTRAINT_B='const_b'
51 CONSTRAINT_POS_MIN='const_pos_min'
52 CONSTRAINT_POS_MAX='const_pos_max'
53 CONSTRAINT_ROT_MIN='const_rot_min'
54 CONSTRAINT_ROT_MAX='const_rot_max'
55 CONSTRAINT_SPRING_POS='const_spring_pos'
56 CONSTRAINT_SPRING_ROT='const_spring_rot'
59 ###############################################################################
61 ###############################################################################
67 from meshio import pmd, englishmap
70 return sys.version_info[0]<3
75 from Blender import Mathutils
81 def createPmdMaterial(m, index):
82 material=Blender.Material.New()
84 #material.diffuseSize = 3.14/2
85 #material.setDiffuseSmooth(0)
86 #material.setSpecSize(0)
90 material.setDiffuseShader(Blender.Material.Shaders.DIFFUSE_TOON)
91 material.setRGBCol([m.diffuse.r, m.diffuse.g, m.diffuse.b])
92 material.setAlpha(m.diffuse.a)
94 material.setSpecShader(Blender.Material.Shaders.SPEC_TOON)
95 material.setSpec(m.shinness*0.1)
96 material.setSpecCol([m.specular.r, m.specular.g, m.specular.b])
98 material.setMirCol([m.ambient.r, m.ambient.g, m.ambient.b])
100 material.enableSSS=True if m.flag==1 else False
102 material.name="m_%02d" % index
105 def poseBoneLimit(n, b):
108 if n.startswith("knee_"):
113 b.limitMax=[180, 0, 0]
114 elif n.startswith("ankle_"):
117 def setSphereMap(material, index, blende_type=None):
123 from bpy.props import *
131 def createPmdMaterial(m, index):
132 material = bpy.data.materials.new("Material")
134 material.diffuse_shader='FRESNEL'
135 material.diffuse_color=([m.diffuse.r, m.diffuse.g, m.diffuse.b])
136 material.alpha=m.diffuse.a
138 material.specular_shader='TOON'
139 material.specular_color=([m.specular.r, m.specular.g, m.specular.b])
140 material.specular_toon_size=int(m.shinness)
142 material.mirror_color=([m.ambient.r, m.ambient.g, m.ambient.b])
144 material.subsurface_scattering.enabled=True if m.flag==1 else False
146 material.name="m_%02d" % index
147 material.preview_render_type='FLAT'
148 material.transparency=True
151 def poseBoneLimit(n, b):
154 if n.startswith("knee_"):
161 elif n.startswith("ankle_"):
165 def setSphereMap(material, index, blend_type=None):
166 slot=material.texture_slots[index]
167 slot.texture_coordinates='REFLECTION'
168 slot.mapping='SPHERE'
170 slot.blend_type=blend_type
173 ###############################################################################
175 return bl.createVector(v.x, v.y, v.z)
177 def convert_coord(pos):
179 Left handed y-up to Right handed z-up
181 return (pos.x, pos.z, pos.y)
184 def to_radian(degree):
185 return math.pi * degree / 180
188 def get_bone_name(l, index):
190 return l.bones[0].getName()
192 if index < len(l.bones):
193 name=englishmap.getEnglishBoneName(l.bones[index].getName())
196 return l.bones[index].getName()
197 print('invalid bone index', index)
198 return l.bones[0].getName()
200 def __importShape(obj, l, vertex_map):
201 if len(l.morph_list)==0:
205 bl.object.pinShape(obj, True)
209 for s in l.morph_list:
213 # create vertex group
214 bl.object.addVertexGroup(obj, MMD_SHAPE_GROUP_NAME)
219 bl.object.assignVertexGroup(
220 obj, MMD_SHAPE_GROUP_NAME, vertex_map[i], 0)
226 baseShapeBlock=bl.object.addShapeKey(obj, BASE_SHAPE_NAME)
228 mesh=bl.object.getData(obj)
232 for s in l.morph_list:
237 name=englishmap.getEnglishSkinName(s.getName())
243 for index, offset in zip(s.indices, s.pos_list):
245 vertex_index=vertex_map[base.indices[index]]
246 v=mesh.verts[vertex_index].co
247 offset=convert_coord(offset)
251 except IndexError as msg:
253 print(index, len(base.indices), len(vertex_map))
254 print(len(mesh.verts))
255 print(base.indices[index])
259 #print 'this mesh not has shape vertices'
262 # create shapekey block
263 new_shape_key=bl.object.addShapeKey(obj, name)
265 # copy vertex to shape key
269 for mv, v in zip(mesh.verts, baseShapeBlock.getData()):
277 new_shape_key=bl.object.addShapeKey(obj, name)
279 for index, offset in zip(s.indices, s.pos_list):
281 vertex_index=vertex_map[base.indices[index]]
282 bl.shapekey.assign(new_shape_key, vertex_index,
283 mesh.verts[vertex_index].co+
284 bl.createVector(*convert_coord(offset)))
285 except IndexError as msg:
287 print(index, len(base.indices), len(vertex_map))
288 print(len(mesh.verts))
289 print(base.indices[index])
293 #print 'this mesh not has shape vertices'
297 bl.object.setActivateShapeKey(obj, 0)
300 def __build(armature, b, p, parent):
301 name=englishmap.getEnglishBoneName(b.getName())
305 bone=bl.armature.createBone(armature, name)
307 if parent and (b.tail_index==0 or b.type==6 or b.type==7 or b.type==9):
308 bone.head = bl.createVector(*convert_coord(b.pos))
309 bone.tail=bone.head+bl.createVector(0, 1, 0)
311 if bone.name=="center_t":
312 # センターボーンは(0, 1, 0)の方向を向いていないと具合が悪い
313 parent.tail=parent.head+bl.createVector(0, 1, 0)
314 bone.head=parent.tail
315 bone.tail=bone.head+bl.createVector(0, 1, 0)
317 if parent.tail==bone.head:
320 print('diffurence with parent.tail and head', name)
322 bl.bone.setConnected(bone)
324 bl.bone.setLayerMask(bone, [0, 1])
327 bone.head = bl.createVector(*convert_coord(b.pos))
328 bone.tail = bl.createVector(*convert_coord(b.tail))
331 if parent.tail==bone.head:
332 bl.bone.setConnected(bone)
334 if bone.head==bone.tail:
335 bone.tail=bone.head+bl.createVector(0, 1, 0)
338 __build(armature, c, b, bone)
341 def __importArmature(scene, l):
342 armature, armature_object=bl.armature.create()
345 bl.armature.makeEditable(armature_object)
348 __build(armature, b, None, None)
349 bl.armature.update(armature)
353 pose = bl.object.getPose(armature_object)
355 target=l.bones[ik.target]
356 name = englishmap.getEnglishBoneName(target.getName())
358 name=target.getName()
359 p_bone = pose.bones[name]
361 print('not found', name)
363 if len(ik.children) >= 16:
364 print('over MAX_CHAINLEN', ik, len(ik.children))
366 effector_name=englishmap.getEnglishBoneName(
367 l.bones[ik.index].getName())
368 if not effector_name:
369 effector_name=l.bones[ik.index].getName()
371 constraint=bl.armature.createIkConstraint(armature_object,
372 p_bone, effector_name, ik)
374 bl.armature.makeEditable(armature_object)
375 bl.armature.update(armature)
382 for i, g in enumerate(l.bone_group_list):
383 name=englishmap.getEnglishBoneGroupName(g.getName().strip())
386 bl.object.createBoneGroup(armature_object, name, "THEME%02d" % (i+1))
388 # assign bone to group
389 for b_index, g_index in l.bone_display_list:
392 bone_name=englishmap.getEnglishBoneName(b.getName())
394 bone_name=b.getName()
396 g=l.bone_group_list[g_index-1]
397 group_name=englishmap.getEnglishBoneGroupName(g.getName().strip())
399 group_name=g.getName()
402 pose.bones[bone_name].bone_group=pose.bone_groups[group_name]
406 return armature_object
409 def __import16MaerialAndMesh(meshObject, l,
410 material_order, face_map, tex_dir):
412 mesh=bl.object.getData(meshObject)
413 ############################################################
415 ############################################################
416 bl.progress_print('create materials')
422 for material_index in material_order:
424 m=l.materials[material_index]
425 mesh_material_map[material_index]=index
429 material=createPmdMaterial(m, material_index)
431 texture_name=m.getTexture()
433 for i, t in enumerate(texture_name.split('*')):
435 texture=textureMap[t]
437 path=os.path.join(tex_dir, t)
438 texture, image=bl.texture.create(path)
439 textureMap[texture_name]=texture
440 imageMap[material_index]=image
441 bl.material.addTexture(material, texture)
442 if t.endswith('sph'):
444 setSphereMap(material, i)
445 elif t.endswith('spa'):
447 setSphereMap(material, i, 'ADD')
449 bl.mesh.addMaterial(mesh, material)
452 ############################################################
454 ############################################################
455 bl.progress_print('create vertices')
458 for v in l.each_vertex():
459 vertices.append(convert_coord(v.pos))
461 ############################################################
463 ############################################################
464 bl.progress_print('create faces')
467 mesh_face_materials=[]
470 for material_index in material_order:
471 face_offset=face_map[material_index]
472 m=l.materials[material_index]
473 material_faces=l.indices[face_offset:face_offset+m.vertex_count]
475 def degenerate(i0, i1, i2):
479 return i0==i1 or i1==i2 or i2==i0
481 for j in xrange(0, len(material_faces), 3):
483 i1=material_faces[j+1]
484 i2=material_faces[j+2]
486 triangle=[i2, i1, i0]
487 if degenerate(*triangle):
489 mesh_face_indices.append(triangle[0:3])
490 mesh_face_materials.append(material_index)
491 used_vertices.add(i0)
492 used_vertices.add(i1)
493 used_vertices.add(i2)
495 ############################################################
496 # create vertices & faces
497 ############################################################
498 bl.mesh.addGeometry(mesh, vertices, mesh_face_indices)
500 ############################################################
502 ############################################################
503 # create vertex group
505 for v in l.each_vertex():
506 vertex_groups[v.bone0]=True
507 vertex_groups[v.bone1]=True
508 for i in vertex_groups.keys():
509 bl.object.addVertexGroup(meshObject, get_bone_name(l, i))
512 bl.mesh.useVertexUV(mesh)
513 for i, v, mvert in zip(xrange(len(l.vertices)),
514 l.each_vertex(), mesh.verts):
516 bl.vertex.setNormal(mvert, convert_coord(v.normal))
518 w1=float(v.weight0)/100.0
520 bl.object.assignVertexGroup(meshObject, get_bone_name(l, v.bone0),
522 bl.object.assignVertexGroup(meshObject, get_bone_name(l, v.bone1),
525 ############################################################
527 ############################################################
530 for i, (face, material_index) in enumerate(
531 zip(mesh.faces, mesh_face_materials)):
533 index=mesh_material_map[material_index]
534 except KeyError as message:
535 print(message, mesh_material_map, m)
537 bl.face.setMaterial(face, index)
538 material=mesh.materials[index]
540 if bl.material.hasTexture(material):
541 uv_array=[l.getUV(i) for i in bl.face.getIndices(face)]
542 bl.mesh.setFaceUV(mesh, i, face,
544 [(uv.x, 1.0-uv.y) for uv in uv_array],
545 imageMap.get(index, None))
552 ############################################################
553 # clean up not used vertices
554 ############################################################
555 bl.progress_print('clean up vertices not used')
558 for i, v in enumerate(l.each_vertex()):
559 if i in used_vertices:
560 vertex_map[i]=len(vertex_map)
562 remove_vertices.append(i)
564 bl.mesh.vertsDelete(mesh, remove_vertices)
566 bl.progress_print('%s created' % mesh.name)
570 def __importMesh(scene, io, tex_dir):
572 @param l[in] mmd.PMDLoader
575 ############################################################
576 # shpaeキーで使われるマテリアル優先的に前に並べる
577 ############################################################
578 # shapeキーで使われる頂点インデックスを集める
579 shape_key_used_vertices=set()
580 if len(io.morph_list)>0:
583 for s in io.morph_list:
590 for index in base.indices:
591 shape_key_used_vertices.add(index)
593 # マテリアルに含まれる頂点がshape_keyに含まれるか否か?
594 def isMaterialUsedInShape(offset, m):
595 for i in xrange(offset, offset+m.vertex_count):
596 if io.indices[i] in shape_key_used_vertices:
599 material_with_shape=set()
601 # 各マテリアルの開始頂点インデックスを記録する
604 for i, m in enumerate(io.materials):
605 face_map[i]=face_count
606 if isMaterialUsedInShape(face_count, m):
607 material_with_shape.add(i)
608 face_count+=m.vertex_count
610 # shapeキーで使われる頂点のあるマテリアル
611 material_with_shape=list(material_with_shape)
612 material_with_shape.sort()
614 # shapeキーに使われていないマテリアル
615 material_without_shape=[]
616 for i in range(len(io.materials)):
617 if not i in material_with_shape:
618 material_without_shape.append(i)
621 def __splitList(l, length):
622 for i in range(0, len(l), length):
625 def __importMeshAndShape(material16, name):
626 mesh, meshObject=bl.mesh.create(name)
629 bl.object.deselectAll()
630 bl.object.activate(meshObject)
632 # shapeキーで使われる順に並べなおしたマテリアル16個分の
634 vertex_map=__import16MaerialAndMesh(
635 meshObject, io, material16, face_map, tex_dir)
638 __importShape(meshObject, io, vertex_map)
643 mesh_objects=[__importMeshAndShape(material16, 'with_shape')
644 for material16 in __splitList(material_with_shape, 16)]
646 mesh_objects+=[__importMeshAndShape(material16, 'mesh')
647 for material16 in __splitList(material_without_shape, 16)]
652 def __importConstraints(scene, io):
655 print("create constraint")
656 container=bl.object.createEmpty('Constraints')
658 True, False, False, False, False, False, False, False,
659 False, False, False, False, False, False, False, False,
660 False, False, False, False, False, False, False, False,
661 False, False, False, False, False, False, False, False,
663 material=bl.material.create('constraint')
664 material.diffuse_color=(1, 0, 0)
666 for i, c in enumerate(io.constraints):
667 bpy.ops.mesh.primitive_uv_sphere_add(
671 location=(c.pos.x, c.pos.z, c.pos.y),
674 meshObject=scene.objects.active
675 constraintMeshes.append(meshObject)
676 mesh=bl.object.getData(meshObject)
677 bl.mesh.addMaterial(mesh, material)
678 meshObject.name='c_%d' % i
679 #meshObject.draw_transparent=True
680 #meshObject.draw_wire=True
681 meshObject.max_draw_type='SOLID'
683 meshObject.rotation_euler=(-rot.x, -rot.z, -rot.y)
685 meshObject[CONSTRAINT_NAME]=c.getName()
686 meshObject[CONSTRAINT_A]=io.rigidbodies[c.rigidA].getName()
687 meshObject[CONSTRAINT_B]=io.rigidbodies[c.rigidB].getName()
688 meshObject[CONSTRAINT_POS_MIN]=VtoV(c.constraintPosMin)
689 meshObject[CONSTRAINT_POS_MAX]=VtoV(c.constraintPosMax)
690 meshObject[CONSTRAINT_ROT_MIN]=VtoV(c.constraintRotMin)
691 meshObject[CONSTRAINT_ROT_MAX]=VtoV(c.constraintRotMax)
692 meshObject[CONSTRAINT_SPRING_POS]=VtoV(c.springPos)
693 meshObject[CONSTRAINT_SPRING_ROT]=VtoV(c.springRot)
695 for meshObject in reversed(constraintMeshes):
696 bl.object.makeParent(container, meshObject)
701 def __importRigidBodies(scene, io):
704 print("create rigid bodies")
706 container=bl.object.createEmpty('RigidBodies')
708 True, False, False, False, False, False, False, False,
709 False, False, False, False, False, False, False, False,
710 False, False, False, False, False, False, False, False,
711 False, False, False, False, False, False, False, False,
713 material=bl.material.create('rigidBody')
715 for rigid in io.rigidbodies:
716 if rigid.boneIndex==0xFFFF:
720 bone=io.bones[rigid.boneIndex]
721 pos=bone.pos+rigid.position
723 if rigid.shapeType==pmd.SHAPE_SPHERE:
724 bpy.ops.mesh.primitive_ico_sphere_add(
725 location=(pos.x, pos.z, pos.y),
728 bpy.ops.transform.resize(
729 value=(rigid.w, rigid.w, rigid.w))
730 elif rigid.shapeType==pmd.SHAPE_BOX:
731 bpy.ops.mesh.primitive_cube_add(
732 location=(pos.x, pos.z, pos.y),
735 bpy.ops.transform.resize(
736 value=(rigid.w, rigid.d, rigid.h))
737 elif rigid.shapeType==pmd.SHAPE_CAPSULE:
738 bpy.ops.mesh.primitive_tube_add(
739 location=(pos.x, pos.z, pos.y),
742 bpy.ops.transform.resize(
743 value=(rigid.w, rigid.w, rigid.h))
747 meshObject=scene.objects.active
748 mesh=bl.object.getData(meshObject)
749 rigidMeshes.append(meshObject)
750 bl.mesh.addMaterial(mesh, material)
751 meshObject.name=rigid.getName()
752 #meshObject.draw_transparent=True
753 #meshObject.draw_wire=True
754 meshObject.max_draw_type='WIRE'
756 meshObject.rotation_euler=(-rot.x, -rot.z, -rot.y)
759 meshObject[RIGID_SHAPE_TYPE]=rigid.shapeType
760 meshObject[RIGID_PROCESS_TYPE]=rigid.processType
762 bone_name = englishmap.getEnglishBoneName(bone.getName())
764 bone_name=bone.getName()
765 meshObject[RIGID_BONE_NAME]=bone_name
767 meshObject[RIGID_GROUP]=rigid.group
768 meshObject[RIGID_INTERSECTION_GROUP]=rigid.target
769 meshObject[RIGID_WEIGHT]=rigid.weight
770 meshObject[RIGID_LINEAR_DAMPING]=rigid.linearDamping
771 meshObject[RIGID_ANGULAR_DAMPING]=rigid.angularDamping
772 meshObject[RIGID_RESTITUTION]=rigid.restitution
773 meshObject[RIGID_FRICTION]=rigid.friction
775 for meshObject in reversed(rigidMeshes):
776 bl.object.makeParent(container, meshObject)
781 def __execute(filename, scene):
783 load pmd file to context.
787 bl.progress_set('load %s' % filename, 0.0)
790 if not io.read(filename):
791 bl.message("fail to load %s" % filename)
793 bl.progress_set('loaded %s' % filename, 0.1)
796 model_name=io.getEnglishName()
797 if len(model_name)==0:
798 model_name=io.getName()
799 root=bl.object.createEmpty(model_name)
802 mesh_objects=__importMesh(scene, io, os.path.dirname(filename))
803 for o in mesh_objects:
804 bl.object.makeParent(root, o)
807 armature_object=__importArmature(scene, io)
809 bl.object.makeParent(root, armature_object)
810 armature = bl.object.getData(armature_object)
812 # add armature modifier
813 for o in mesh_objects:
814 bl.modifier.addArmature(o, armature_object)
817 for n, b in bl.object.getPose(armature_object).bones.items():
820 # import rigid bodies
821 rigidBodies=__importRigidBodies(scene, io)
823 bl.object.makeParent(root, rigidBodies)
826 constraints=__importConstraints(scene, io)
828 bl.object.makeParent(root, constraints)
830 bl.object.activate(root)
835 def execute_24(filename):
836 scene=bpy.data.scenes.active
837 bl.initialize('pmd_import', scene)
839 filename.decode(bl.INTERNAL_ENCODING),
843 Blender.Window.FileSelector(
846 Blender.sys.makename(ext='.pmd'))
850 def execute_25(filename, scene):
851 bl.initialize('pmd_import', scene)
852 __execute(filename, scene)
856 class IMPORT_OT_pmd(bpy.types.Operator):
857 bl_idname = "import_scene.pmd"
858 bl_label = 'Import PMD'
860 # List of operator properties, the attributes will be assigned
861 # to the class instance from the operator settings before calling.
863 path = StringProperty(
865 description="File path used for importing the PMD file",
866 maxlen= 1024, default= "")
867 filename = StringProperty(
869 description="Name of the file.")
870 directory = StringProperty(
872 description="Directory of the file.")
874 def execute(self, context):
875 execute_25(self.properties.path, context.scene)
878 def invoke(self, context, event):
880 wm.add_fileselect(self)
881 return 'RUNNING_MODAL'
884 def menu_func(self, context):
885 self.layout.operator(IMPORT_OT_pmd.bl_idname,
886 text="MikuMikuDance model (.pmd)")
889 bpy.types.register(IMPORT_OT_pmd)
890 bpy.types.INFO_MT_file_import.append(menu_func)
893 bpy.types.unregister(IMPORT_OT_pmd)
894 bpy.types.INFO_MT_file_import.remove(menu_func)
896 if __name__=="__main__":