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.
33 MMD_SHAPE_GROUP_NAME='_MMD_SHAPE'
34 BASE_SHAPE_NAME='Basis'
35 RIGID_SHAPE_TYPE='rigid_shape_type'
36 RIGID_PROCESS_TYPE='rigid_process_type'
37 RIGID_BONE_NAME='rigid_bone_name'
38 #RIGID_LOCATION='rigid_loation'
39 RIGID_GROUP='ribid_group'
40 RIGID_INTERSECTION_GROUP='rigid_intersection_group'
41 RIGID_WEIGHT='rigid_weight'
42 RIGID_LINEAR_DAMPING='rigid_linear_damping'
43 RIGID_ANGULAR_DAMPING='rigid_angular_damping'
44 RIGID_RESTITUTION='rigid_restitution'
45 RIGID_FRICTION='rigid_friction'
46 CONSTRAINT_NAME='constraint_name'
47 CONSTRAINT_A='const_a'
48 CONSTRAINT_B='const_b'
49 CONSTRAINT_POS_MIN='const_pos_min'
50 CONSTRAINT_POS_MAX='const_pos_max'
51 CONSTRAINT_ROT_MIN='const_rot_min'
52 CONSTRAINT_ROT_MAX='const_rot_max'
53 CONSTRAINT_SPRING_POS='const_spring_pos'
54 CONSTRAINT_SPRING_ROT='const_spring_rot'
57 ###############################################################################
59 ###############################################################################
65 from meshio import pmd, englishmap
68 return sys.version_info[0]<3
73 from Blender import Mathutils
79 def createPmdMaterial(m, index):
80 material=Blender.Material.New()
82 #material.diffuseSize = 3.14/2
83 #material.setDiffuseSmooth(0)
84 #material.setSpecSize(0)
87 material.setDiffuseShader(Blender.Material.Shaders.DIFFUSE_TOON)
88 material.setSpecShader(Blender.Material.Shaders.SPEC_TOON)
90 material.setRGBCol([m.diffuse.r, m.diffuse.g, m.diffuse.b])
91 material.setAlpha(m.diffuse.a)
93 material.setSpec(m.shinness*0.1)
94 material.setSpecCol([m.specular.r, m.specular.g, m.specular.b])
96 material.setMirCol([m.ambient.r, m.ambient.g, m.ambient.b])
98 material.enableSSS=True if m.flag==1 else False
100 material.name="m_%02d" % index
103 def poseBoneLimit(n, b):
106 if n.startswith("knee_"):
111 b.limitMax=[180, 0, 0]
112 elif n.startswith("ankle_"):
118 from bpy.props import *
126 def createPmdMaterial(m, index):
127 material = bpy.data.materials.new("Material")
129 material.diffuse_shader='FRESNEL'
130 material.specular_shader='TOON'
132 material.diffuse_color=([m.diffuse.r, m.diffuse.g, m.diffuse.b])
133 material.alpha=m.diffuse.a
135 material.specular_hardness=int(m.shinness)
136 material.specular_color=([m.specular.r, m.specular.g, m.specular.b])
138 material.mirror_color=([m.ambient.r, m.ambient.g, m.ambient.b])
140 material.subsurface_scattering.enabled=True if m.flag==1 else False
142 material.name="m_%02d" % index
145 def poseBoneLimit(n, b):
148 if n.startswith("knee_"):
155 elif n.startswith("ankle_"):
160 ###############################################################################
162 return bl.createVector(v.x, v.y, v.z)
164 def convert_coord(pos):
166 Left handed y-up to Right handed z-up
168 return (pos.x, pos.z, pos.y)
172 return (uv.x, 1.0 - uv.y)
175 def to_radian(degree):
176 return math.pi * degree / 180
179 def get_bone_name(l, index):
181 return l.bones[0].getName()
183 if index < len(l.bones):
184 name=englishmap.getEnglishBoneName(l.bones[index].getName())
187 return l.bones[index].getName()
188 print('invalid bone index', index)
189 return l.bones[0].getName()
191 def __importShape(obj, l, vertex_map):
192 if len(l.morph_list)==0:
196 bl.object.pinShape(obj, True)
200 for s in l.morph_list:
204 # create vertex group
205 bl.object.addVertexGroup(obj, MMD_SHAPE_GROUP_NAME)
210 bl.object.assignVertexGroup(
211 obj, MMD_SHAPE_GROUP_NAME, vertex_map[i], 0)
217 baseShapeBlock=bl.object.addShapeKey(obj, BASE_SHAPE_NAME)
219 mesh=bl.object.getData(obj)
223 for s in l.morph_list:
228 name=englishmap.getEnglishSkinName(s.getName())
234 for index, offset in zip(s.indices, s.pos_list):
236 vertex_index=vertex_map[base.indices[index]]
237 v=mesh.verts[vertex_index].co
238 offset=convert_coord(offset)
242 except IndexError as msg:
244 print(index, len(base.indices), len(vertex_map))
245 print(len(mesh.verts))
246 print(base.indices[index])
250 #print 'this mesh not has shape vertices'
253 # create shapekey block
254 new_shape_key=bl.object.addShapeKey(obj, name)
256 # copy vertex to shape key
260 for mv, v in zip(mesh.verts, baseShapeBlock.getData()):
268 new_shape_key=bl.object.addShapeKey(obj, name)
270 for index, offset in zip(s.indices, s.pos_list):
272 vertex_index=vertex_map[base.indices[index]]
273 bl.shapekey.assign(new_shape_key, vertex_index,
274 mesh.verts[vertex_index].co+
275 bl.createVector(*convert_coord(offset)))
276 except IndexError as msg:
278 print(index, len(base.indices), len(vertex_map))
279 print(len(mesh.verts))
280 print(base.indices[index])
284 #print 'this mesh not has shape vertices'
288 bl.object.setActivateShapeKey(obj, 0)
291 def __build(armature, b, p, parent):
292 name=englishmap.getEnglishBoneName(b.getName())
296 bone=bl.armature.createBone(armature, name)
298 if parent and (b.tail_index==0 or b.type==6 or b.type==7 or b.type==9):
299 bone.head = bl.createVector(*convert_coord(b.pos))
300 bone.tail=bone.head+bl.createVector(0, 1, 0)
302 if bone.name=="center_t":
303 # センターボーンは(0, 1, 0)の方向を向いていないと具合が悪い
304 parent.tail=parent.head+bl.createVector(0, 1, 0)
305 bone.head=parent.tail
306 bone.tail=bone.head+bl.createVector(0, 1, 0)
308 if parent.tail==bone.head:
311 print('diffurence with parent.tail and head', name)
313 bl.bone.setConnected(bone)
315 bl.bone.setLayerMask(bone, [0, 1])
318 bone.head = bl.createVector(*convert_coord(b.pos))
319 bone.tail = bl.createVector(*convert_coord(b.tail))
322 if parent.tail==bone.head:
323 bl.bone.setConnected(bone)
325 if bone.head==bone.tail:
326 bone.tail=bone.head+bl.createVector(0, 1, 0)
329 __build(armature, c, b, bone)
332 def __importArmature(scene, l):
334 armature, armature_object=bl.armature.create()
335 bl.armature.makeEditable(armature_object)
338 __build(armature, b, None, None)
339 bl.armature.update(armature)
343 pose = bl.object.getPose(armature_object)
345 target=l.bones[ik.target]
346 name = englishmap.getEnglishBoneName(target.getName())
348 name=target.getName()
349 p_bone = pose.bones[name]
351 print('not found', name)
353 if len(ik.children) >= 16:
354 print('over MAX_CHAINLEN', ik, len(ik.children))
356 effector_name=englishmap.getEnglishBoneName(
357 l.bones[ik.index].getName())
358 if not effector_name:
359 effector_name=l.bones[ik.index].getName()
361 constraint=bl.armature.createIkConstraint(armature_object,
362 p_bone, effector_name, ik)
364 bl.armature.makeEditable(armature_object)
365 bl.armature.update(armature)
368 return armature_object
371 def __import16MaerialAndMesh(meshObject, l,
372 material_order, face_map, tex_dir):
374 mesh=bl.object.getData(meshObject)
375 ############################################################
377 ############################################################
378 bl.progress_print('create materials')
384 for material_index in material_order:
386 m=l.materials[material_index]
387 mesh_material_map[material_index]=index
391 material=createPmdMaterial(m, material_index)
393 texture_name=m.getTexture()
395 if texture_name in textureMap:
396 texture=textureMap[texture_name]
399 texture, image=bl.texture.create(
400 os.path.join(tex_dir, texture_name))
401 textureMap[texture_name]=texture
402 imageMap[material_index]=image
405 bl.material.addTexture(material, texture)
406 bl.mesh.addMaterial(mesh, material)
409 ############################################################
411 ############################################################
412 bl.progress_print('create vertices')
415 for v in l.each_vertex():
416 vertices.append(convert_coord(v.pos))
418 ############################################################
420 ############################################################
421 bl.progress_print('create faces')
424 mesh_face_materials=[]
427 for material_index in material_order:
428 face_offset=face_map[material_index]
429 m=l.materials[material_index]
430 material_faces=l.indices[face_offset:face_offset+m.vertex_count]
432 def degenerate(i0, i1, i2):
436 return i0==i1 or i1==i2 or i2==i0
438 for j in xrange(0, len(material_faces), 3):
440 i1=material_faces[j+1]
441 i2=material_faces[j+2]
443 triangle=[i2, i1, i0]
444 if degenerate(*triangle):
446 mesh_face_indices.append(triangle[0:3])
447 mesh_face_materials.append(material_index)
448 used_vertices.add(i0)
449 used_vertices.add(i1)
450 used_vertices.add(i2)
452 ############################################################
453 # create vertices & faces
454 ############################################################
455 bl.mesh.addGeometry(mesh, vertices, mesh_face_indices)
457 ############################################################
459 ############################################################
460 # create vertex group
462 for v in l.each_vertex():
463 vertex_groups[v.bone0]=True
464 vertex_groups[v.bone1]=True
465 for i in vertex_groups.keys():
466 bl.object.addVertexGroup(meshObject, get_bone_name(l, i))
469 bl.mesh.useVertexUV(mesh)
470 for i, v, mvert in zip(xrange(len(l.vertices)),
471 l.each_vertex(), mesh.verts):
473 bl.vertex.setNormal(mvert, convert_coord(v.normal))
475 w1=float(v.weight0)/100.0
477 bl.object.assignVertexGroup(meshObject, get_bone_name(l, v.bone0),
479 bl.object.assignVertexGroup(meshObject, get_bone_name(l, v.bone1),
482 ############################################################
484 ############################################################
487 for i, (face, material_index) in enumerate(
488 zip(mesh.faces, mesh_face_materials)):
490 index=mesh_material_map[material_index]
491 except KeyError as message:
492 print(message, mesh_material_map, m)
494 bl.face.setMaterial(face, index)
495 material=mesh.materials[index]
497 if bl.material.hasTexture(material):
498 uv_array=[l.getUV(i) for i in bl.face.getIndices(face)]
499 bl.mesh.setFaceUV(mesh, i, face,
501 [(uv.x, 1.0-uv.y) for uv in uv_array],
502 imageMap.get(index, None))
509 ############################################################
510 # clean up not used vertices
511 ############################################################
512 bl.progress_print('clean up vertices not used')
515 for i, v in enumerate(l.each_vertex()):
516 if i in used_vertices:
517 vertex_map[i]=len(vertex_map)
519 remove_vertices.append(i)
521 bl.mesh.vertsDelete(mesh, remove_vertices)
523 bl.progress_print('%s created' % mesh.name)
527 def __importMesh(scene, io, tex_dir):
529 @param l[in] mmd.PMDLoader
532 ############################################################
533 # shpaeキーで使われるマテリアル優先的に前に並べる
534 ############################################################
535 # shapeキーで使われる頂点インデックスを集める
536 shape_key_used_vertices=set()
537 if len(io.morph_list)>0:
540 for s in io.morph_list:
547 for index in base.indices:
548 shape_key_used_vertices.add(index)
550 # マテリアルに含まれる頂点がshape_keyに含まれるか否か?
551 def isMaterialUsedInShape(offset, m):
552 for i in xrange(offset, offset+m.vertex_count):
553 if io.indices[i] in shape_key_used_vertices:
556 # shapeキーで使われるマテリアルを記録する
557 shape_key_materials=set()
558 # 各マテリアルの開始頂点インデックスを記録する
561 for i, m in enumerate(io.materials):
562 face_map[i]=face_count
563 if isMaterialUsedInShape(face_count, m):
564 shape_key_materials.add(i)
565 face_count+=m.vertex_count
568 material_order=list(shape_key_materials)
570 # shapeキーに使われていないマテリアルを後ろに追加
571 for i in range(len(io.materials)):
572 if not i in material_order:
573 material_order.append(i)
575 # マテリアル16個ごとに分割したメッシュを作成する
578 while material_offset<len(io.materials):
579 mesh, meshObject=bl.mesh.create('mesh')
581 mesh_objects.append(meshObject)
584 bl.object.deselectAll()
585 bl.object.activate(meshObject)
587 # shapeキーで使われる順に並べなおしたマテリアル16個分の
589 vertex_map=__import16MaerialAndMesh(
591 material_order[material_offset:material_offset+16],
595 __importShape(meshObject, io, vertex_map)
603 def __importConstraints(scene, io):
606 print("create constraint")
607 container=bl.object.createEmpty('Constraints')
609 True, False, False, False, False, False, False, False,
610 False, False, False, False, False, False, False, False,
611 False, False, False, False, False, False, False, False,
612 False, False, False, False, False, False, False, False,
614 material=bl.material.create('constraint')
615 material.diffuse_color=(1, 0, 0)
617 for i, c in enumerate(io.constraints):
618 bpy.ops.mesh.primitive_uv_sphere_add(
622 location=(c.pos.x, c.pos.z, c.pos.y),
625 meshObject=scene.objects.active
626 constraintMeshes.append(meshObject)
627 mesh=bl.object.getData(meshObject)
628 bl.mesh.addMaterial(mesh, material)
629 meshObject.name='c_%d' % i
630 #meshObject.draw_transparent=True
631 #meshObject.draw_wire=True
632 meshObject.max_draw_type='SOLID'
634 meshObject.rotation_euler=(-rot.x, -rot.z, -rot.y)
636 meshObject[CONSTRAINT_NAME]=c.getName()
637 meshObject[CONSTRAINT_A]=io.rigidbodies[c.rigidA].getName()
638 meshObject[CONSTRAINT_B]=io.rigidbodies[c.rigidB].getName()
639 meshObject[CONSTRAINT_POS_MIN]=VtoV(c.constraintPosMin)
640 meshObject[CONSTRAINT_POS_MAX]=VtoV(c.constraintPosMax)
641 meshObject[CONSTRAINT_ROT_MIN]=VtoV(c.constraintRotMin)
642 meshObject[CONSTRAINT_ROT_MAX]=VtoV(c.constraintRotMax)
643 meshObject[CONSTRAINT_SPRING_POS]=VtoV(c.springPos)
644 meshObject[CONSTRAINT_SPRING_ROT]=VtoV(c.springRot)
646 for meshObject in reversed(constraintMeshes):
647 bl.object.makeParent(container, meshObject)
652 def __importRigidBodies(scene, io):
655 print("create rigid bodies")
657 container=bl.object.createEmpty('RigidBodies')
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('rigidBody')
666 for rigid in io.rigidbodies:
667 if rigid.boneIndex==0xFFFF:
671 bone=io.bones[rigid.boneIndex]
672 pos=bone.pos+rigid.position
674 if rigid.shapeType==pmd.SHAPE_SPHERE:
675 bpy.ops.mesh.primitive_ico_sphere_add(
676 location=(pos.x, pos.z, pos.y),
679 bpy.ops.transform.resize(
680 value=(rigid.w, rigid.w, rigid.w))
681 elif rigid.shapeType==pmd.SHAPE_BOX:
682 bpy.ops.mesh.primitive_cube_add(
683 location=(pos.x, pos.z, pos.y),
686 bpy.ops.transform.resize(
687 value=(rigid.w, rigid.d, rigid.h))
688 elif rigid.shapeType==pmd.SHAPE_CAPSULE:
689 bpy.ops.mesh.primitive_tube_add(
690 location=(pos.x, pos.z, pos.y),
693 bpy.ops.transform.resize(
694 value=(rigid.w, rigid.w, rigid.h))
698 meshObject=scene.objects.active
699 mesh=bl.object.getData(meshObject)
700 rigidMeshes.append(meshObject)
701 bl.mesh.addMaterial(mesh, material)
702 meshObject.name=rigid.getName()
703 #meshObject.draw_transparent=True
704 #meshObject.draw_wire=True
705 meshObject.max_draw_type='WIRE'
707 meshObject.rotation_euler=(-rot.x, -rot.z, -rot.y)
710 meshObject[RIGID_SHAPE_TYPE]=rigid.shapeType
711 meshObject[RIGID_PROCESS_TYPE]=rigid.processType
713 bone_name = englishmap.getEnglishBoneName(bone.getName())
715 bone_name=bone.getName()
716 meshObject[RIGID_BONE_NAME]=bone_name
718 meshObject[RIGID_GROUP]=rigid.group
719 meshObject[RIGID_INTERSECTION_GROUP]=rigid.target
720 meshObject[RIGID_WEIGHT]=rigid.weight
721 meshObject[RIGID_LINEAR_DAMPING]=rigid.linearDamping
722 meshObject[RIGID_ANGULAR_DAMPING]=rigid.angularDamping
723 meshObject[RIGID_RESTITUTION]=rigid.restitution
724 meshObject[RIGID_FRICTION]=rigid.friction
726 for meshObject in reversed(rigidMeshes):
727 bl.object.makeParent(container, meshObject)
732 def __execute(filename, scene):
734 load pmd file to context.
738 bl.progress_set('load %s' % filename, 0.0)
741 if not io.read(filename):
742 bl.message("fail to load %s" % filename)
744 bl.progress_set('loaded %s' % filename, 0.1)
747 model_name=io.getEnglishName()
748 if len(model_name)==0:
749 model_name=io.getName()
750 root=bl.object.createEmpty(model_name)
753 mesh_objects=__importMesh(scene, io, os.path.dirname(filename))
754 for o in mesh_objects:
755 bl.object.makeParent(root, o)
758 armature_object=__importArmature(scene, io)
760 bl.object.makeParent(root, armature_object)
761 armature = bl.object.getData(armature_object)
763 # add armature modifier
764 for o in mesh_objects:
765 bl.modifier.addArmature(o, armature_object)
768 for n, b in bl.object.getPose(armature_object).bones.items():
771 # import rigid bodies
772 rigidBodies=__importRigidBodies(scene, io)
774 bl.object.makeParent(root, rigidBodies)
777 constraints=__importConstraints(scene, io)
779 bl.object.makeParent(root, constraints)
781 bl.object.activate(root)
786 def execute_24(filename):
787 scene=bpy.data.scenes.active
788 bl.initialize('pmd_import', scene)
790 filename.decode(bl.INTERNAL_ENCODING),
794 Blender.Window.FileSelector(
797 Blender.sys.makename(ext='.pmd'))
801 def execute_25(filename, scene):
802 bl.initialize('pmd_import', scene)
803 __execute(filename, scene)
807 class IMPORT_OT_pmd(bpy.types.Operator):
808 bl_idname = "import_scene.pmd"
809 bl_label = 'Import PMD'
811 # List of operator properties, the attributes will be assigned
812 # to the class instance from the operator settings before calling.
814 path = StringProperty(
816 description="File path used for importing the PMD file",
817 maxlen= 1024, default= "")
818 filename = StringProperty(
820 description="Name of the file.")
821 directory = StringProperty(
823 description="Directory of the file.")
825 def execute(self, context):
826 execute_25(self.properties.path, context.scene)
829 def invoke(self, context, event):
831 wm.add_fileselect(self)
832 return 'RUNNING_MODAL'
835 def menu_func(self, context):
836 self.layout.operator(IMPORT_OT_pmd.bl_idname,
837 text="MikuMikuDance model (.pmd)")
840 bpy.types.register(IMPORT_OT_pmd)
841 bpy.types.INFO_MT_file_import.append(menu_func)
844 bpy.types.unregister(IMPORT_OT_pmd)
845 bpy.types.INFO_MT_file_import.remove(menu_func)
847 if __name__=="__main__":