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.
34 1.9 20100718: keep model name, comment.
35 2.0 20100724: update for Blender2.53.
36 2.1 20100731: add full python module.
37 2.2 20101005: update for Blender2.54.
38 2.3 20101228: update for Blender2.55.
39 2.4 20110429: update for Blender2.57b.
40 2.5 20110522: implement RigidBody and Constraint.
41 2.6 20110918: update for Blender2.59.
44 'category': 'Import/Export',
45 'name': 'Import: MikuMikuDance Model Format (.pmd)',
49 'location': 'File > Import',
50 'description': 'Import from the MikuMikuDance Model Format (.pmd)',
51 'warning': '', # used for warning icon and text in addons panel
52 'wiki_url': 'http://sourceforge.jp/projects/meshio/wiki/FrontPage',
53 'tracker_url': 'http://sourceforge.jp/ticket/newticket.php?group_id=5081',
57 ###############################################################################
59 ###############################################################################
66 from .meshio import pmd, englishmap
67 print('use meshio C module')
70 from .pymeshio import englishmap
71 #from .pymeshio import mmd as pmd
73 from .pymeshio import pmd
80 from . import bl25 as bl
84 def createPmdMaterial(m, index):
85 material = bpy.data.materials.new("Material")
87 material.diffuse_shader='FRESNEL'
88 material.diffuse_color=([m.diffuse.r, m.diffuse.g, m.diffuse.b])
89 material.alpha=m.diffuse.a
91 material.specular_shader='TOON'
92 material.specular_color=([m.specular.r, m.specular.g, m.specular.b])
93 material.specular_toon_size=int(m.shinness)
95 material.mirror_color=([m.ambient.r, m.ambient.g, m.ambient.b])
97 material.subsurface_scattering.use=True if m.flag==1 else False
99 material.name="m_%02d" % index
100 material.preview_render_type='FLAT'
101 material.use_transparency=True
104 def poseBoneLimit(n, b):
107 if n.startswith("knee_"):
112 b.use_ik_limit_x=True
115 elif n.startswith("ankle_"):
119 def setSphereMap(material, index, blend_type='MULTIPLY'):
120 slot=material.texture_slots[index]
121 slot.texture_coords='NORMAL'
122 slot.mapping='SPHERE'
123 slot.blend_type=blend_type
126 ###############################################################################
128 return bl.createVector(v.x, v.y, v.z)
131 def convert_coord(pos):
133 Left handed y-up to Right handed z-up
135 return (pos.x, pos.z, pos.y)
138 def to_radian(degree):
139 return math.pi * degree / 180
142 def get_bone_name(l, index):
144 return l.bones[0]._name
146 if index < len(l.bones):
147 name=englishmap.getEnglishBoneName(l.bones[index]._name)
150 return l.bones[index]._name
151 print('invalid bone index', index)
152 return l.bones[0]._name
155 def get_group_name(g):
156 group_name=englishmap.getEnglishBoneGroupName(g._name.strip())
158 group_name=g._name.strip()
162 def __importToonTextures(io, tex_dir):
163 mesh, meshObject=bl.mesh.create(bl.TOON_TEXTURE_OBJECT)
164 material=bl.material.create(bl.TOON_TEXTURE_OBJECT)
165 bl.mesh.addMaterial(mesh, material)
166 for toon in (io.toon_textures._toon_textures[i] for i in range(10)):
167 path=os.path.join(tex_dir, toon)
168 texture, image=bl.texture.create(path)
169 bl.material.addTexture(material, texture, False)
170 return meshObject, material
173 def __importShape(obj, l, vertex_map):
174 if len(l.morph_list)==0:
178 bl.object.pinShape(obj, True)
182 for s in l.morph_list:
186 # create vertex group
187 bl.object.addVertexGroup(obj, bl.MMD_SHAPE_GROUP_NAME)
192 bl.object.assignVertexGroup(
193 obj, bl.MMD_SHAPE_GROUP_NAME, vertex_map[i], 0)
199 baseShapeBlock=bl.object.addShapeKey(obj, bl.BASE_SHAPE_NAME)
201 mesh=bl.object.getData(obj)
205 for s in l.morph_list:
210 name=englishmap.getEnglishSkinName(s._name)
215 new_shape_key=bl.object.addShapeKey(obj, name)
217 for index, offset in zip(s.indices, s.pos_list):
219 base_index=base.indices[index]
220 except IndexError as msg:
223 print("invalid index %d/%d" % (index, len(base.indices)))
225 vertex_index=vertex_map[base_index]
226 bl.shapekey.assign(new_shape_key, vertex_index,
227 mesh.vertices[vertex_index].co+
228 bl.createVector(*convert_coord(offset)))
231 bl.object.setActivateShapeKey(obj, 0)
234 def __build(armature, b, p, parent):
235 name=englishmap.getEnglishBoneName(b._name)
239 bone=bl.armature.createBone(armature, name)
241 if parent and (b.tail_index==0 or b.type==6 or b.type==7 or b.type==9):
243 bone.head = bl.createVector(*convert_coord(b.pos))
244 bone.tail=bone.head+bl.createVector(0, 1, 0)
246 if bone.name=="center_t":
247 # センターボーンは(0, 1, 0)の方向を向いていないと具合が悪い
248 parent.tail=parent.head+bl.createVector(0, 1, 0)
249 bone.head=parent.tail
250 bone.tail=bone.head+bl.createVector(0, 1, 0)
252 if parent.tail==bone.head:
255 print('diffurence with parent.tail and head', name)
258 bl.bone.setConnected(bone)
260 bl.bone.setLayerMask(bone, [0, 1])
263 bone.head = bl.createVector(*convert_coord(b.pos))
264 bone.tail = bl.createVector(*convert_coord(b.tail))
267 if parent.tail==bone.head:
268 bl.bone.setConnected(bone)
270 if bone.head==bone.tail:
271 bone.tail=bone.head+bl.createVector(0, 1, 0)
274 __build(armature, c, b, bone)
277 def __importArmature(l):
278 armature, armature_object=bl.armature.create()
281 bl.armature.makeEditable(armature_object)
284 __build(armature, b, None, None)
285 bl.armature.update(armature)
289 pose = bl.object.getPose(armature_object)
291 target=l.bones[ik.target]
292 name = englishmap.getEnglishBoneName(target._name)
295 p_bone = pose.bones[name]
297 print('not found', name)
299 if len(ik.children) >= 16:
300 print('over MAX_CHAINLEN', ik, len(ik.children))
302 effector_name=englishmap.getEnglishBoneName(
303 l.bones[ik.index]._name)
304 if not effector_name:
305 effector_name=l.bones[ik.index]._name
307 constraint=bl.armature.createIkConstraint(armature_object,
308 p_bone, effector_name, ik)
310 bl.armature.makeEditable(armature_object)
311 bl.armature.update(armature)
315 for i, g in enumerate(l.bone_group_list):
316 name=get_group_name(g)
317 bl.object.createBoneGroup(armature_object, name, "THEME%02d" % (i+1))
319 # assign bone to group
320 for b_index, g_index in l.bone_display_list:
323 bone_name=englishmap.getEnglishBoneName(b._name)
327 g=l.bone_group_list[g_index-1]
328 group_name=get_group_name(g)
331 pose.bones[bone_name].bone_group=pose.bone_groups[group_name]
335 return armature_object
338 def __import16MaerialAndMesh(meshObject, l,
339 material_order, face_map, tex_dir, toon_material):
341 mesh=bl.object.getData(meshObject)
342 ############################################################
344 ############################################################
345 bl.progress_print('create materials')
351 for material_index in material_order:
353 m=l.materials[material_index]
354 mesh_material_map[material_index]=index
358 material=createPmdMaterial(m, material_index)
361 texture_name=m._texture
363 for i, t in enumerate(texture_name.split('*')):
365 texture=textureMap[t]
367 path=os.path.join(tex_dir, t)
368 texture, image=bl.texture.create(path)
369 textureMap[texture_name]=texture
370 imageMap[material_index]=image
371 texture_index=bl.material.addTexture(material, texture)
372 if t.endswith('sph'):
374 setSphereMap(material, texture_index)
375 elif t.endswith('spa'):
377 setSphereMap(material, texture_index, 'ADD')
380 toon_index=bl.material.addTexture(
382 bl.material.getTexture(
384 0 if m.toon_index==0xFF else m.toon_index
388 bl.mesh.addMaterial(mesh, material)
392 ############################################################
394 ############################################################
395 bl.progress_print('create vertices')
398 for v in l.each_vertex():
399 vertices.append(convert_coord(v.pos))
401 ############################################################
403 ############################################################
404 bl.progress_print('create faces')
407 mesh_face_materials=[]
410 for material_index in material_order:
411 face_offset=face_map[material_index]
412 m=l.materials[material_index]
413 material_faces=l.indices[face_offset:face_offset+m.vertex_count]
415 def degenerate(i0, i1, i2):
419 return i0==i1 or i1==i2 or i2==i0
421 for j in xrange(0, len(material_faces), 3):
423 i1=material_faces[j+1]
424 i2=material_faces[j+2]
426 triangle=[i2, i1, i0]
427 if degenerate(*triangle):
429 mesh_face_indices.append(triangle[0:3])
430 mesh_face_materials.append(material_index)
431 used_vertices.add(i0)
432 used_vertices.add(i1)
433 used_vertices.add(i2)
435 ############################################################
436 # create vertices & faces
437 ############################################################
438 bl.mesh.addGeometry(mesh, vertices, mesh_face_indices)
440 ############################################################
442 ############################################################
443 # create vertex group
445 for v in l.each_vertex():
446 vertex_groups[v.bone0]=True
447 vertex_groups[v.bone1]=True
448 for i in vertex_groups.keys():
449 bl.object.addVertexGroup(meshObject, get_bone_name(l, i))
452 bl.mesh.useVertexUV(mesh)
453 for i, v, mvert in zip(xrange(len(l.vertices)),
454 l.each_vertex(), mesh.vertices):
456 bl.vertex.setNormal(mvert, convert_coord(v.normal))
458 w1=float(v.weight0)/100.0
460 bl.object.assignVertexGroup(meshObject, get_bone_name(l, v.bone0),
462 bl.object.assignVertexGroup(meshObject, get_bone_name(l, v.bone1),
465 ############################################################
467 ############################################################
470 for i, (face, material_index) in enumerate(
471 zip(mesh.faces, mesh_face_materials)):
473 index=mesh_material_map[material_index]
474 except KeyError as message:
475 print(message, mesh_material_map, m)
477 bl.face.setMaterial(face, index)
478 material=mesh.materials[index]
480 if bl.material.hasTexture(material):
481 uv_array=[l.getUV(i) for i in bl.face.getIndices(face)]
482 bl.mesh.setFaceUV(mesh, i, face,
484 [(uv.x, 1.0-uv.y) for uv in uv_array],
485 imageMap.get(index, None))
488 bl.face.setSmooth(face, True)
492 ############################################################
493 # clean up not used vertices
494 ############################################################
495 bl.progress_print('clean up vertices not used')
498 for i, v in enumerate(l.each_vertex()):
499 if i in used_vertices:
500 vertex_map[i]=len(vertex_map)
502 remove_vertices.append(i)
504 bl.mesh.vertsDelete(mesh, remove_vertices)
506 bl.progress_print('%s created' % mesh.name)
510 def __importMaterialAndMesh(io, tex_dir, toon_material):
512 @param l[in] mmd.PMDLoader
515 ############################################################
516 # shpaeキーで使われるマテリアル優先的に前に並べる
517 ############################################################
518 # shapeキーで使われる頂点インデックスを集める
519 shape_key_used_vertices=set()
520 if len(io.morph_list)>0:
523 for s in io.morph_list:
530 for index in base.indices:
531 shape_key_used_vertices.add(index)
533 # マテリアルに含まれる頂点がshape_keyに含まれるか否か?
534 def isMaterialUsedInShape(offset, m):
535 for i in xrange(offset, offset+m.vertex_count):
536 if io.indices[i] in shape_key_used_vertices:
539 material_with_shape=set()
541 # 各マテリアルの開始頂点インデックスを記録する
544 for i, m in enumerate(io.materials):
545 face_map[i]=face_count
546 if isMaterialUsedInShape(face_count, m):
547 material_with_shape.add(i)
548 face_count+=m.vertex_count
550 # shapeキーで使われる頂点のあるマテリアル
551 material_with_shape=list(material_with_shape)
552 material_with_shape.sort()
554 # shapeキーに使われていないマテリアル
555 material_without_shape=[]
556 for i in range(len(io.materials)):
557 if not i in material_with_shape:
558 material_without_shape.append(i)
561 def __splitList(l, length):
562 for i in range(0, len(l), length):
565 def __importMeshAndShape(material16, name):
566 mesh, meshObject=bl.mesh.create(name)
569 bl.object.deselectAll()
570 bl.object.activate(meshObject)
572 # shapeキーで使われる順に並べなおしたマテリアル16個分の
574 vertex_map=__import16MaerialAndMesh(
575 meshObject, io, material16, face_map, tex_dir, toon_material)
578 __importShape(meshObject, io, vertex_map)
583 mesh_objects=[__importMeshAndShape(material16, 'with_shape')
584 for material16 in __splitList(material_with_shape, 16)]
586 mesh_objects+=[__importMeshAndShape(material16, 'mesh')
587 for material16 in __splitList(material_without_shape, 16)]
592 def __importConstraints(io):
593 print("create constraint")
594 container=bl.object.createEmpty('Constraints')
596 True, False, False, False, False, False, False, False, False, False,
597 False, False, False, False, False, False, False, False, False, False,
599 material=bl.material.create('constraint')
600 material.diffuse_color=(1, 0, 0)
602 for i, c in enumerate(io.constraints):
603 bpy.ops.mesh.primitive_uv_sphere_add(
607 location=(c.pos.x, c.pos.z, c.pos.y),
610 meshObject=bl.object.getActive()
611 constraintMeshes.append(meshObject)
612 mesh=bl.object.getData(meshObject)
613 bl.mesh.addMaterial(mesh, material)
614 meshObject.name='c_%03d' % i
615 #meshObject.draw_transparent=True
616 #meshObject.draw_wire=True
617 meshObject.draw_type='SOLID'
619 meshObject.rotation_euler=(-rot.x, -rot.z, -rot.y)
621 meshObject[bl.CONSTRAINT_NAME]=c._name
622 meshObject[bl.CONSTRAINT_A]=io.rigidbodies[c.rigidA]._name
623 meshObject[bl.CONSTRAINT_B]=io.rigidbodies[c.rigidB]._name
624 meshObject[bl.CONSTRAINT_POS_MIN]=VtoV(c.constraintPosMin)
625 meshObject[bl.CONSTRAINT_POS_MAX]=VtoV(c.constraintPosMax)
626 meshObject[bl.CONSTRAINT_ROT_MIN]=VtoV(c.constraintRotMin)
627 meshObject[bl.CONSTRAINT_ROT_MAX]=VtoV(c.constraintRotMax)
628 meshObject[bl.CONSTRAINT_SPRING_POS]=VtoV(c.springPos)
629 meshObject[bl.CONSTRAINT_SPRING_ROT]=VtoV(c.springRot)
631 for meshObject in reversed(constraintMeshes):
632 bl.object.makeParent(container, meshObject)
637 def __importRigidBodies(io):
638 print("create rigid bodies")
640 container=bl.object.createEmpty('RigidBodies')
642 True, False, False, False, False, False, False, False, False, False,
643 False, False, False, False, False, False, False, False, False, False,
645 material=bl.material.create('rigidBody')
647 for i, rigid in enumerate(io.rigidbodies):
648 if rigid.boneIndex==0xFFFF:
652 bone=io.bones[rigid.boneIndex]
653 pos=bone.pos+rigid.position
655 if rigid.shapeType==pmd.SHAPE_SPHERE:
656 bpy.ops.mesh.primitive_ico_sphere_add(
657 location=(pos.x, pos.z, pos.y),
660 bpy.ops.transform.resize(
661 value=(rigid.w, rigid.w, rigid.w))
662 elif rigid.shapeType==pmd.SHAPE_BOX:
663 bpy.ops.mesh.primitive_cube_add(
664 location=(pos.x, pos.z, pos.y),
667 bpy.ops.transform.resize(
668 value=(rigid.w, rigid.d, rigid.h))
669 elif rigid.shapeType==pmd.SHAPE_CAPSULE:
670 bpy.ops.mesh.primitive_cylinder_add(
671 location=(pos.x, pos.z, pos.y),
674 bpy.ops.transform.resize(
675 value=(rigid.w, rigid.w, rigid.h))
679 meshObject=bl.object.getActive()
680 mesh=bl.object.getData(meshObject)
681 rigidMeshes.append(meshObject)
682 bl.mesh.addMaterial(mesh, material)
683 meshObject.name='r_%03d' % i
684 meshObject[bl.RIGID_NAME]=rigid._name
685 #meshObject.draw_transparent=True
686 #meshObject.draw_wire=True
687 meshObject.draw_type='WIRE'
689 meshObject.rotation_euler=(-rot.x, -rot.z, -rot.y)
692 meshObject[bl.RIGID_SHAPE_TYPE]=rigid.shapeType
693 meshObject[bl.RIGID_PROCESS_TYPE]=rigid.processType
695 bone_name = englishmap.getEnglishBoneName(bone._name)
698 meshObject[bl.RIGID_BONE_NAME]=bone_name
700 meshObject[bl.RIGID_GROUP]=rigid.group
701 meshObject[bl.RIGID_INTERSECTION_GROUP]=rigid.target
702 meshObject[bl.RIGID_WEIGHT]=rigid.weight
703 meshObject[bl.RIGID_LINEAR_DAMPING]=rigid.linearDamping
704 meshObject[bl.RIGID_ANGULAR_DAMPING]=rigid.angularDamping
705 meshObject[bl.RIGID_RESTITUTION]=rigid.restitution
706 meshObject[bl.RIGID_FRICTION]=rigid.friction
708 for meshObject in reversed(rigidMeshes):
709 bl.object.makeParent(container, meshObject)
714 def _execute(filepath=""):
716 load pmd file to context.
720 bl.progress_set('load %s' % filepath, 0.0)
723 if not io.read(filepath):
724 bl.message("fail to load %s" % filepath)
726 bl.progress_set('loaded', 0.1)
729 model_name=io._english_name
730 if len(model_name)==0:
732 root=bl.object.createEmpty(model_name)
733 root[bl.MMD_MB_NAME]=io._name
734 root[bl.MMD_MB_COMMENT]=io._comment
735 root[bl.MMD_COMMENT]=io._english_comment
738 tex_dir=os.path.dirname(filepath)
739 toonTextures, toonMaterial=__importToonTextures(io, tex_dir)
740 bl.object.makeParent(root, toonTextures)
743 mesh_objects=__importMaterialAndMesh(io, tex_dir, toonMaterial)
744 for o in mesh_objects:
745 bl.object.makeParent(root, o)
748 armature_object=__importArmature(io)
750 bl.object.makeParent(root, armature_object)
751 armature = bl.object.getData(armature_object)
753 # add armature modifier
754 for o in mesh_objects:
755 bl.modifier.addArmature(o, armature_object)
758 for n, b in bl.object.getPose(armature_object).bones.items():
761 # import rigid bodies
762 rigidBodies=__importRigidBodies(io)
764 bl.object.makeParent(root, rigidBodies)
767 constraints=__importConstraints(io)
769 bl.object.makeParent(root, constraints)
771 bl.object.activate(root)