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)
323 bl.bone.setConnected(bone)
325 bl.bone.setLayerMask(bone, [0, 1])
328 bone.head = bl.createVector(*convert_coord(b.pos))
329 bone.tail = bl.createVector(*convert_coord(b.tail))
332 if parent.tail==bone.head:
333 bl.bone.setConnected(bone)
335 if bone.head==bone.tail:
336 bone.tail=bone.head+bl.createVector(0, 1, 0)
339 __build(armature, c, b, bone)
342 def __importArmature(scene, l):
343 armature, armature_object=bl.armature.create()
346 bl.armature.makeEditable(armature_object)
349 __build(armature, b, None, None)
350 bl.armature.update(armature)
354 pose = bl.object.getPose(armature_object)
356 target=l.bones[ik.target]
357 name = englishmap.getEnglishBoneName(target.getName())
359 name=target.getName()
360 p_bone = pose.bones[name]
362 print('not found', name)
364 if len(ik.children) >= 16:
365 print('over MAX_CHAINLEN', ik, len(ik.children))
367 effector_name=englishmap.getEnglishBoneName(
368 l.bones[ik.index].getName())
369 if not effector_name:
370 effector_name=l.bones[ik.index].getName()
372 constraint=bl.armature.createIkConstraint(armature_object,
373 p_bone, effector_name, ik)
375 bl.armature.makeEditable(armature_object)
376 bl.armature.update(armature)
383 for i, g in enumerate(l.bone_group_list):
384 name=englishmap.getEnglishBoneGroupName(g.getName().strip())
387 bl.object.createBoneGroup(armature_object, name, "THEME%02d" % (i+1))
389 # assign bone to group
390 for b_index, g_index in l.bone_display_list:
393 bone_name=englishmap.getEnglishBoneName(b.getName())
395 bone_name=b.getName()
397 g=l.bone_group_list[g_index-1]
398 group_name=englishmap.getEnglishBoneGroupName(g.getName().strip())
400 group_name=g.getName()
403 pose.bones[bone_name].bone_group=pose.bone_groups[group_name]
407 return armature_object
410 def __import16MaerialAndMesh(meshObject, l,
411 material_order, face_map, tex_dir):
413 mesh=bl.object.getData(meshObject)
414 ############################################################
416 ############################################################
417 bl.progress_print('create materials')
423 for material_index in material_order:
425 m=l.materials[material_index]
426 mesh_material_map[material_index]=index
430 material=createPmdMaterial(m, material_index)
432 texture_name=m.getTexture()
434 for i, t in enumerate(texture_name.split('*')):
436 texture=textureMap[t]
438 path=os.path.join(tex_dir, t)
439 texture, image=bl.texture.create(path)
440 textureMap[texture_name]=texture
441 imageMap[material_index]=image
442 bl.material.addTexture(material, texture)
443 if t.endswith('sph'):
445 setSphereMap(material, i)
446 elif t.endswith('spa'):
448 setSphereMap(material, i, 'ADD')
450 bl.mesh.addMaterial(mesh, material)
453 ############################################################
455 ############################################################
456 bl.progress_print('create vertices')
459 for v in l.each_vertex():
460 vertices.append(convert_coord(v.pos))
462 ############################################################
464 ############################################################
465 bl.progress_print('create faces')
468 mesh_face_materials=[]
471 for material_index in material_order:
472 face_offset=face_map[material_index]
473 m=l.materials[material_index]
474 material_faces=l.indices[face_offset:face_offset+m.vertex_count]
476 def degenerate(i0, i1, i2):
480 return i0==i1 or i1==i2 or i2==i0
482 for j in xrange(0, len(material_faces), 3):
484 i1=material_faces[j+1]
485 i2=material_faces[j+2]
487 triangle=[i2, i1, i0]
488 if degenerate(*triangle):
490 mesh_face_indices.append(triangle[0:3])
491 mesh_face_materials.append(material_index)
492 used_vertices.add(i0)
493 used_vertices.add(i1)
494 used_vertices.add(i2)
496 ############################################################
497 # create vertices & faces
498 ############################################################
499 bl.mesh.addGeometry(mesh, vertices, mesh_face_indices)
501 ############################################################
503 ############################################################
504 # create vertex group
506 for v in l.each_vertex():
507 vertex_groups[v.bone0]=True
508 vertex_groups[v.bone1]=True
509 for i in vertex_groups.keys():
510 bl.object.addVertexGroup(meshObject, get_bone_name(l, i))
513 bl.mesh.useVertexUV(mesh)
514 for i, v, mvert in zip(xrange(len(l.vertices)),
515 l.each_vertex(), mesh.verts):
517 bl.vertex.setNormal(mvert, convert_coord(v.normal))
519 w1=float(v.weight0)/100.0
521 bl.object.assignVertexGroup(meshObject, get_bone_name(l, v.bone0),
523 bl.object.assignVertexGroup(meshObject, get_bone_name(l, v.bone1),
526 ############################################################
528 ############################################################
531 for i, (face, material_index) in enumerate(
532 zip(mesh.faces, mesh_face_materials)):
534 index=mesh_material_map[material_index]
535 except KeyError as message:
536 print(message, mesh_material_map, m)
538 bl.face.setMaterial(face, index)
539 material=mesh.materials[index]
541 if bl.material.hasTexture(material):
542 uv_array=[l.getUV(i) for i in bl.face.getIndices(face)]
543 bl.mesh.setFaceUV(mesh, i, face,
545 [(uv.x, 1.0-uv.y) for uv in uv_array],
546 imageMap.get(index, None))
553 ############################################################
554 # clean up not used vertices
555 ############################################################
556 bl.progress_print('clean up vertices not used')
559 for i, v in enumerate(l.each_vertex()):
560 if i in used_vertices:
561 vertex_map[i]=len(vertex_map)
563 remove_vertices.append(i)
565 bl.mesh.vertsDelete(mesh, remove_vertices)
567 bl.progress_print('%s created' % mesh.name)
571 def __importMesh(scene, io, tex_dir):
573 @param l[in] mmd.PMDLoader
576 ############################################################
577 # shpaeキーで使われるマテリアル優先的に前に並べる
578 ############################################################
579 # shapeキーで使われる頂点インデックスを集める
580 shape_key_used_vertices=set()
581 if len(io.morph_list)>0:
584 for s in io.morph_list:
591 for index in base.indices:
592 shape_key_used_vertices.add(index)
594 # マテリアルに含まれる頂点がshape_keyに含まれるか否か?
595 def isMaterialUsedInShape(offset, m):
596 for i in xrange(offset, offset+m.vertex_count):
597 if io.indices[i] in shape_key_used_vertices:
600 material_with_shape=set()
602 # 各マテリアルの開始頂点インデックスを記録する
605 for i, m in enumerate(io.materials):
606 face_map[i]=face_count
607 if isMaterialUsedInShape(face_count, m):
608 material_with_shape.add(i)
609 face_count+=m.vertex_count
611 # shapeキーで使われる頂点のあるマテリアル
612 material_with_shape=list(material_with_shape)
613 material_with_shape.sort()
615 # shapeキーに使われていないマテリアル
616 material_without_shape=[]
617 for i in range(len(io.materials)):
618 if not i in material_with_shape:
619 material_without_shape.append(i)
622 def __splitList(l, length):
623 for i in range(0, len(l), length):
626 def __importMeshAndShape(material16, name):
627 mesh, meshObject=bl.mesh.create(name)
630 bl.object.deselectAll()
631 bl.object.activate(meshObject)
633 # shapeキーで使われる順に並べなおしたマテリアル16個分の
635 vertex_map=__import16MaerialAndMesh(
636 meshObject, io, material16, face_map, tex_dir)
639 __importShape(meshObject, io, vertex_map)
644 mesh_objects=[__importMeshAndShape(material16, 'with_shape')
645 for material16 in __splitList(material_with_shape, 16)]
647 mesh_objects+=[__importMeshAndShape(material16, 'mesh')
648 for material16 in __splitList(material_without_shape, 16)]
653 def __importConstraints(scene, io):
656 print("create constraint")
657 container=bl.object.createEmpty('Constraints')
659 True, 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,
662 False, False, False, False, False, False, False, False,
664 material=bl.material.create('constraint')
665 material.diffuse_color=(1, 0, 0)
667 for i, c in enumerate(io.constraints):
668 bpy.ops.mesh.primitive_uv_sphere_add(
672 location=(c.pos.x, c.pos.z, c.pos.y),
675 meshObject=scene.objects.active
676 constraintMeshes.append(meshObject)
677 mesh=bl.object.getData(meshObject)
678 bl.mesh.addMaterial(mesh, material)
679 meshObject.name='c_%d' % i
680 #meshObject.draw_transparent=True
681 #meshObject.draw_wire=True
682 meshObject.max_draw_type='SOLID'
684 meshObject.rotation_euler=(-rot.x, -rot.z, -rot.y)
686 meshObject[CONSTRAINT_NAME]=c.getName()
687 meshObject[CONSTRAINT_A]=io.rigidbodies[c.rigidA].getName()
688 meshObject[CONSTRAINT_B]=io.rigidbodies[c.rigidB].getName()
689 meshObject[CONSTRAINT_POS_MIN]=VtoV(c.constraintPosMin)
690 meshObject[CONSTRAINT_POS_MAX]=VtoV(c.constraintPosMax)
691 meshObject[CONSTRAINT_ROT_MIN]=VtoV(c.constraintRotMin)
692 meshObject[CONSTRAINT_ROT_MAX]=VtoV(c.constraintRotMax)
693 meshObject[CONSTRAINT_SPRING_POS]=VtoV(c.springPos)
694 meshObject[CONSTRAINT_SPRING_ROT]=VtoV(c.springRot)
696 for meshObject in reversed(constraintMeshes):
697 bl.object.makeParent(container, meshObject)
702 def __importRigidBodies(scene, io):
705 print("create rigid bodies")
707 container=bl.object.createEmpty('RigidBodies')
709 True, 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,
712 False, False, False, False, False, False, False, False,
714 material=bl.material.create('rigidBody')
716 for rigid in io.rigidbodies:
717 if rigid.boneIndex==0xFFFF:
721 bone=io.bones[rigid.boneIndex]
722 pos=bone.pos+rigid.position
724 if rigid.shapeType==pmd.SHAPE_SPHERE:
725 bpy.ops.mesh.primitive_ico_sphere_add(
726 location=(pos.x, pos.z, pos.y),
729 bpy.ops.transform.resize(
730 value=(rigid.w, rigid.w, rigid.w))
731 elif rigid.shapeType==pmd.SHAPE_BOX:
732 bpy.ops.mesh.primitive_cube_add(
733 location=(pos.x, pos.z, pos.y),
736 bpy.ops.transform.resize(
737 value=(rigid.w, rigid.d, rigid.h))
738 elif rigid.shapeType==pmd.SHAPE_CAPSULE:
739 bpy.ops.mesh.primitive_tube_add(
740 location=(pos.x, pos.z, pos.y),
743 bpy.ops.transform.resize(
744 value=(rigid.w, rigid.w, rigid.h))
748 meshObject=scene.objects.active
749 mesh=bl.object.getData(meshObject)
750 rigidMeshes.append(meshObject)
751 bl.mesh.addMaterial(mesh, material)
752 meshObject.name=rigid.getName()
753 #meshObject.draw_transparent=True
754 #meshObject.draw_wire=True
755 meshObject.max_draw_type='WIRE'
757 meshObject.rotation_euler=(-rot.x, -rot.z, -rot.y)
760 meshObject[RIGID_SHAPE_TYPE]=rigid.shapeType
761 meshObject[RIGID_PROCESS_TYPE]=rigid.processType
763 bone_name = englishmap.getEnglishBoneName(bone.getName())
765 bone_name=bone.getName()
766 meshObject[RIGID_BONE_NAME]=bone_name
768 meshObject[RIGID_GROUP]=rigid.group
769 meshObject[RIGID_INTERSECTION_GROUP]=rigid.target
770 meshObject[RIGID_WEIGHT]=rigid.weight
771 meshObject[RIGID_LINEAR_DAMPING]=rigid.linearDamping
772 meshObject[RIGID_ANGULAR_DAMPING]=rigid.angularDamping
773 meshObject[RIGID_RESTITUTION]=rigid.restitution
774 meshObject[RIGID_FRICTION]=rigid.friction
776 for meshObject in reversed(rigidMeshes):
777 bl.object.makeParent(container, meshObject)
782 def __execute(filename, scene):
784 load pmd file to context.
788 bl.progress_set('load %s' % filename, 0.0)
791 if not io.read(filename):
792 bl.message("fail to load %s" % filename)
794 bl.progress_set('loaded %s' % filename, 0.1)
797 model_name=io.getEnglishName()
798 if len(model_name)==0:
799 model_name=io.getName()
800 root=bl.object.createEmpty(model_name)
803 mesh_objects=__importMesh(scene, io, os.path.dirname(filename))
804 for o in mesh_objects:
805 bl.object.makeParent(root, o)
808 armature_object=__importArmature(scene, io)
810 bl.object.makeParent(root, armature_object)
811 armature = bl.object.getData(armature_object)
813 # add armature modifier
814 for o in mesh_objects:
815 bl.modifier.addArmature(o, armature_object)
818 for n, b in bl.object.getPose(armature_object).bones.items():
821 # import rigid bodies
822 rigidBodies=__importRigidBodies(scene, io)
824 bl.object.makeParent(root, rigidBodies)
827 constraints=__importConstraints(scene, io)
829 bl.object.makeParent(root, constraints)
831 bl.object.activate(root)
836 def execute_24(filename):
837 scene=bpy.data.scenes.active
838 bl.initialize('pmd_import', scene)
840 filename.decode(bl.INTERNAL_ENCODING),
844 Blender.Window.FileSelector(
847 Blender.sys.makename(ext='.pmd'))
851 def execute_25(filename, scene):
852 bl.initialize('pmd_import', scene)
853 __execute(filename, scene)
857 class IMPORT_OT_pmd(bpy.types.Operator):
858 bl_idname = "import_scene.pmd"
859 bl_label = 'Import PMD'
861 # List of operator properties, the attributes will be assigned
862 # to the class instance from the operator settings before calling.
864 path = StringProperty(
866 description="File path used for importing the PMD file",
867 maxlen= 1024, default= "")
868 filename = StringProperty(
870 description="Name of the file.")
871 directory = StringProperty(
873 description="Directory of the file.")
875 def execute(self, context):
876 execute_25(self.properties.path, context.scene)
879 def invoke(self, context, event):
881 wm.add_fileselect(self)
882 return 'RUNNING_MODAL'
885 def menu_func(self, context):
886 self.layout.operator(IMPORT_OT_pmd.bl_idname,
887 text="MikuMikuDance model (.pmd)")
890 bpy.types.register(IMPORT_OT_pmd)
891 bpy.types.INFO_MT_file_import.append(menu_func)
894 bpy.types.unregister(IMPORT_OT_pmd)
895 bpy.types.INFO_MT_file_import.remove(menu_func)
897 if __name__=="__main__":