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.
30 MMD_SHAPE_GROUP_NAME='_MMD_SHAPE'
31 BASE_SHAPE_NAME='Basis'
32 RIGID_SHAPE_TYPE='rigid_shape_type'
33 RIGID_PROCESS_TYPE='rigid_process_type'
34 RIGID_BONE_NAME='rigid_bone_name'
35 CONSTRAINT_A='constraint_a'
36 CONSTRAINT_B='constraint_b'
38 ###############################################################################
40 ###############################################################################
47 from meshio import pmd, englishmap
50 return sys.version_info[0]<3
55 from Blender import Mathutils
63 from bpy.props import *
71 ###############################################################################
73 ###############################################################################
74 def progress_start(base):
76 progressBar=bl.ProgressBar(base)
78 def progress_finish():
82 def progress_print(message, progress=0.05):
84 progressBar.advance(message, progress)
86 def progress_set(message, progress):
88 progressBar.set(message, progress)
91 ###############################################################################
92 def convert_coord(pos):
94 Left handed y-up to Right handed z-up
96 return (pos.x, pos.z, pos.y)
100 return (uv.x, 1.0 - uv.y)
103 def to_radian(degree):
104 return math.pi * degree / 180
107 def get_bone_name(l, index):
108 name=englishmap.getEnglishBoneName(l.bones[index].getName())
109 return name if name else l.bones[index].getName()
111 def __importShape(obj, l, vertex_map):
112 if len(l.morph_list)==0:
116 bl.objectPinShape(obj)
120 for s in l.morph_list:
124 # create vertex group
125 bl.meshAddVertexGroup(obj, MMD_SHAPE_GROUP_NAME)
130 bl.meshAssignVertexGroup(
131 obj, MMD_SHAPE_GROUP_NAME, vertex_map[i], 0)
137 baseShapeBlock=bl.objectAddShapeKey(obj, BASE_SHAPE_NAME)
139 mesh=bl.objectGetData(obj)
143 for s in l.morph_list:
148 name=englishmap.getEnglishSkinName(s.getName())
154 for index, offset in zip(s.indices, s.pos_list):
156 vertex_index=vertex_map[base.indices[index]]
157 v=mesh.verts[vertex_index].co
158 offset=convert_coord(offset)
162 except IndexError as msg:
164 print(index, len(base.indices), len(vertex_map))
165 print(len(mesh.verts))
166 print(base.indices[index])
170 #print 'this mesh not has shape vertices'
173 # create shapekey block
174 new_shape_key=bl.objectAddShapeKey(obj, name)
176 # copy vertex to shape key
180 for mv, v in zip(mesh.verts, baseShapeBlock.getData()):
188 new_shape_key=bl.objectAddShapeKey(obj, name)
190 for index, offset in zip(s.indices, s.pos_list):
192 vertex_index=vertex_map[base.indices[index]]
193 bl.shapeKeyAssign(new_shape_key, vertex_index,
194 mesh.verts[vertex_index].co+
195 bl.createVector(*convert_coord(offset)))
196 except IndexError as msg:
198 print(index, len(base.indices), len(vertex_map))
199 print(len(mesh.verts))
200 print(base.indices[index])
204 #print 'this mesh not has shape vertices'
208 bl.objectActivateShapeKey(obj, 0)
211 def __build(armature, b, p, parent):
212 name=englishmap.getEnglishBoneName(b.getName())
216 bone=bl.createArmatureBone(armature, name)
220 assert(b.type==6 or b.type==7)
221 bone.head = bl.createVector(*convert_coord(b.pos))
222 bone.tail=bone.head+bl.createVector(0, 1, 0)
225 if bone.name=="center_t":
226 # センターボーンは(0, 1, 0)の方向を向いていないと具合が悪い
227 parent.tail=parent.head+bl.createVector(0, 1, 0)
228 bone.head=parent.tail
229 bone.tail=bone.head+bl.createVector(0, 1, 0)
231 assert(parent.tail==bone.head)
232 bl.boneSetConnected(bone)
234 bl.boneLayerMask(bone, [0, 1])
236 bone.head = bl.createVector(*convert_coord(b.pos))
237 bone.tail = bl.createVector(*convert_coord(b.tail))
240 if parent.tail==bone.head:
241 bl.boneSetConnected(bone)
243 if bone.head==bone.tail:
244 bone.tail=bone.head+bl.createVector(0, 1, 0)
247 __build(armature, c, b, bone)
250 def __importArmature(scene, l):
252 armature, armature_object=bl.createArmature(scene)
253 bl.armatureMakeEditable(scene, armature_object)
256 __build(armature, b, None, None)
257 bl.armatureUpdate(armature)
261 pose = bl.objectGetPose(armature_object)
263 target=l.bones[ik.target]
264 name = englishmap.getEnglishBoneName(target.getName())
266 name=target.getName()
267 p_bone = pose.bones[name]
269 print('not found', name)
271 if len(ik.children) >= 16:
272 print('over MAX_CHAINLEN', ik, len(ik.children))
274 effector_name=englishmap.getEnglishBoneName(
275 l.bones[ik.index].getName())
276 if not effector_name:
277 effector_name=l.bones[ik.index].getName()
279 constraint=bl.createIkConstraint(armature_object,
280 p_bone, effector_name, ik)
282 bl.armatureMakeEditable(scene, armature_object)
283 bl.armatureUpdate(armature)
286 return armature_object
289 def __import16MaerialAndMesh(meshObject, l,
290 material_order, face_map, tex_dir):
292 mesh=bl.objectGetData(meshObject)
293 ############################################################
295 ############################################################
296 progress_print('create materials')
302 for material_index in material_order:
304 m=l.materials[material_index]
305 mesh_material_map[material_index]=index
309 material=bl.createPmdMaterial(m)
311 texture_name=m.getTexture()
313 if texture_name in textureMap:
314 texture=textureMap[texture_name]
317 texture, image=bl.createTexture(
318 os.path.join(tex_dir, texture_name))
319 textureMap[texture_name]=texture
320 imageMap[material_index]=image
323 bl.materialAddTexture(material, texture)
324 bl.meshAddMaterial(mesh, material)
327 ############################################################
329 ############################################################
330 progress_print('create vertices')
334 for v in l.each_vertex():
335 vertices.append(convert_coord(v.pos))
337 for v in l.each_vertex():
338 vertices.extend(convert_coord(v.pos))
340 ############################################################
342 ############################################################
343 progress_print('create faces')
346 mesh_face_materials=[]
349 for material_index in material_order:
350 face_offset=face_map[material_index]
351 m=l.materials[material_index]
352 material_faces=l.indices[face_offset:face_offset+m.vertex_count]
354 def degenerate(i0, i1, i2):
358 return i0==i1 or i1==i2 or i2==i0
360 for j in xrange(0, len(material_faces), 3):
362 i1=material_faces[j+1]
363 i2=material_faces[j+2]
365 triangle=[i2, i0, i1]
367 triangle=[i0, i1, i2]
368 if degenerate(*triangle):
371 mesh_face_indices.append(triangle[0:3])
373 mesh_face_indices.extend(
374 [triangle[0], triangle[1], triangle[2], 0])
375 mesh_face_materials.append(material_index)
376 used_vertices.add(i0)
377 used_vertices.add(i1)
378 used_vertices.add(i2)
380 ############################################################
381 # create vertices & faces
382 ############################################################
383 bl.meshCreateVerteicesAndFaces(mesh, vertices, mesh_face_indices)
385 ############################################################
387 ############################################################
388 # create vertex group
390 for v in l.each_vertex():
391 vertex_groups[v.bone0]=True
392 vertex_groups[v.bone1]=True
393 for i in vertex_groups.keys():
394 bl.meshAddVertexGroup(meshObject, get_bone_name(l, i))
397 bl.meshUseVertexUv(mesh)
398 for i, v, mvert in zip(xrange(len(l.vertices)),
399 l.each_vertex(), mesh.verts):
401 bl.vertexSetNormal(mvert, convert_coord(v.normal))
402 bl.vertexSetUv(mvert, convert_uv(v.uv))
404 w1=float(v.weight0)/100.0
406 bl.meshAssignVertexGroup(meshObject, get_bone_name(l, v.bone0),
408 bl.meshAssignVertexGroup(meshObject, get_bone_name(l, v.bone1),
411 ############################################################
413 ############################################################
418 for face, material_index in zip(mesh.faces, mesh_face_materials):
420 index=mesh_material_map[material_index]
421 except KeyError as message:
422 print(message, mesh_material_map, m)
425 material=mesh.materials[index]
426 texture=material.getTextures()[0]
429 face.image=texture.tex.image
430 texture.tex.imageFlags|=Blender.Texture.ImageFlags.USEALPHA
432 face.verts[0].uvco, face.verts[1].uvco, face.verts[2].uvco]
438 for face, uv_face, material_index in zip(mesh.faces,
439 mesh.uv_textures[0].data,
443 index=mesh_material_map[material_index]
444 except KeyError as message:
445 print(message, mesh_material_map, m)
447 face.material_index=index
448 material=mesh.materials[index]
450 if material.texture_slots[0]:
451 uv=l.getUV(face.verts[0])
452 uv_face.uv1=[uv.x, 1.0-uv.y]
454 uv=l.getUV(face.verts[1])
455 uv_face.uv2=[uv.x, 1.0-uv.y]
457 uv=l.getUV(face.verts[2])
458 uv_face.uv3=[uv.x, 1.0-uv.y]
459 if face.material_index in imageMap:
460 uv_face.image=imageMap[face.material_index]
467 ############################################################
468 # clean up not used vertices
469 ############################################################
470 progress_print('clean up vertices not used')
473 for i, v in enumerate(l.each_vertex()):
474 if i in used_vertices:
475 vertex_map[i]=len(vertex_map)
477 remove_vertices.append(i)
479 bl.meshVertsDelete(mesh, remove_vertices)
481 progress_print('%s created' % mesh.name)
485 def __importMesh(scene, io, tex_dir):
487 @param l[in] mmd.PMDLoader
490 ############################################################
491 # shpaeキーで使われるマテリアル優先的に前に並べる
492 ############################################################
493 # shapeキーで使われる頂点インデックスを集める
494 shape_key_used_vertices=set()
495 if len(io.morph_list)>0:
498 for s in io.morph_list:
505 for index in base.indices:
506 shape_key_used_vertices.add(index)
508 # マテリアルに含まれる頂点がshape_keyに含まれるか否か?
509 def isMaterialUsedInShape(offset, m):
510 for i in xrange(offset, offset+m.vertex_count):
511 if io.indices[i] in shape_key_used_vertices:
514 # shapeキーで使われるマテリアルを記録する
515 shape_key_materials=set()
516 # 各マテリアルの開始頂点インデックスを記録する
519 for i, m in enumerate(io.materials):
520 face_map[i]=face_count
521 if isMaterialUsedInShape(face_count, m):
522 shape_key_materials.add(i)
523 face_count+=m.vertex_count
526 material_order=list(shape_key_materials)
528 # shapeキーに使われていないマテリアルを後ろに追加
529 for i in range(len(io.materials)):
530 if not i in material_order:
531 material_order.append(i)
533 # マテリアル16個ごとに分割したメッシュを作成する
536 while material_offset<len(io.materials):
537 mesh, meshObject=bl.createMesh(scene, 'mesh')
539 #meshObject.layers = [1]
540 mesh_objects.append(meshObject)
543 bl.objectDeselectAll()
544 bl.objectActivate(scene, meshObject)
546 # shapeキーで使われる順に並べなおしたマテリアル16個分の
548 vertex_map=__import16MaerialAndMesh(
550 material_order[material_offset:material_offset+16],
557 __importShape(meshObject, io, vertex_map)
568 def __importConstraints(scene, io):
571 print("create constrains")
572 container=bl.createEmptyObject(scene, 'Constraints')
574 True, False, False, False, False, False, False, False,
575 False, False, False, False, False, False, False, False,
576 False, False, False, False, False, False, False, False,
577 False, False, False, False, False, False, False, False,
579 material=bl.createMaterial('constraint')
580 material.diffuse_color=(1, 0, 0)
582 for c in io.constraints:
583 bpy.ops.mesh.primitive_uv_sphere_add(
587 location=(c.pos.x, c.pos.z, c.pos.y),
590 meshObject=scene.objects.active
591 constraintMeshes.append(meshObject)
592 mesh=bl.objectGetData(meshObject)
593 bl.meshAddMaterial(mesh, material)
594 meshObject.name='c'+c.getName()
595 #meshObject.draw_transparent=True
596 #meshObject.draw_wire=True
597 meshObject.max_draw_type='SOLID'
599 meshObject.rotation_euler=(-rot.x, -rot.z, -rot.y)
601 meshObject[CONSTRAINT_A]=io.rigidbodies[c.rigidA].getName()
602 meshObject[CONSTRAINT_B]=io.rigidbodies[c.rigidB].getName()
604 for meshObject in reversed(constraintMeshes):
605 bl.objectMakeParent(container, meshObject)
610 def __importRigidBodies(scene, io):
613 print("create rigid bodies")
615 container=bl.createEmptyObject(scene, 'RigidBodies')
617 True, False, False, False, False, False, False, False,
618 False, False, False, False, False, False, False, False,
619 False, False, False, False, False, False, False, False,
620 False, False, False, False, False, False, False, False,
622 material=bl.createMaterial('rigidBody')
624 for rigid in io.rigidbodies:
625 if rigid.boneIndex==0xFFFF:
629 bone=io.bones[rigid.boneIndex]
630 pos=bone.pos+rigid.position
632 if rigid.shapeType==pmd.SHAPE_SPHERE:
633 bpy.ops.mesh.primitive_ico_sphere_add(
634 location=(pos.x, pos.z, pos.y),
637 bpy.ops.transform.resize(
638 value=(rigid.w, rigid.w, rigid.w))
639 elif rigid.shapeType==pmd.SHAPE_BOX:
640 bpy.ops.mesh.primitive_cube_add(
641 location=(pos.x, pos.z, pos.y),
644 bpy.ops.transform.resize(
645 value=(rigid.w, rigid.d, rigid.h))
646 elif rigid.shapeType==pmd.SHAPE_CAPSULE:
647 bpy.ops.mesh.primitive_tube_add(
648 location=(pos.x, pos.z, pos.y),
651 bpy.ops.transform.resize(
652 value=(rigid.w, rigid.w, rigid.h))
656 meshObject=scene.objects.active
657 mesh=bl.objectGetData(meshObject)
658 rigidMeshes.append(meshObject)
659 bl.meshAddMaterial(mesh, material)
660 meshObject.name=rigid.getName()
661 #meshObject.draw_transparent=True
662 #meshObject.draw_wire=True
663 meshObject.max_draw_type='WIRE'
665 meshObject.rotation_euler=(-rot.x, -rot.z, -rot.y)
668 meshObject[RIGID_SHAPE_TYPE]=rigid.shapeType
669 meshObject[RIGID_PROCESS_TYPE]=rigid.processType
671 bone_name = englishmap.getEnglishBoneName(bone.getName())
673 bone_name=bone.getName()
674 meshObject[RIGID_BONE_NAME]=bone_name
676 for meshObject in reversed(rigidMeshes):
677 bl.objectMakeParent(container, meshObject)
682 def __execute(filename, scene):
684 load pmd file to context.
687 progress_set('load %s' % filename, 0.0)
690 if not io.read(filename):
691 print("fail to load %s" % filename)
693 progress_set('loaded %s' % filename, 0.1)
696 model_name=io.getEnglishName()
697 if len(model_name)==0:
698 model_name=io.getName()
699 root=bl.createEmptyObject(scene, model_name)
702 mesh_objects=__importMesh(scene, io, os.path.dirname(filename))
703 for o in mesh_objects:
704 bl.objectMakeParent(root, o)
707 armature_object=__importArmature(scene, io)
709 bl.objectMakeParent(root, armature_object)
710 armature = bl.objectGetData(armature_object)
712 # add armature modifier
713 for o in mesh_objects:
714 bl.objectAddArmatureModifier(o, armature_object)
717 for n, b in bl.objectGetPose(armature_object).bones.items():
718 bl.poseBoneLimit(n, b)
720 # import rigid bodies
721 rigidBodies=__importRigidBodies(scene, io)
723 bl.objectMakeParent(root, rigidBodies)
726 constraints=__importConstraints(scene, io)
728 bl.objectMakeParent(root, constraints)
731 bl.objectSelect(root)
732 for o in mesh_objects:
734 bl.objectSelect(armature_object)
739 def execute_24(filename):
743 filename=filename.decode(bl.INTERNAL_ENCODING)
744 print(bl.INTERNAL_ENCODING, bl.FS_ENCODING)
747 mode_edit = Blender.Window.EditMode()
749 Blender.Window.EditMode(0)
751 progress_start('pmd_import')
752 scene = bpy.data.scenes.active
753 __execute(filename, scene)
759 Blender.Window.EditMode(1)
760 Blender.Window.RedrawAll()
762 Blender.Window.FileSelector(
765 Blender.sys.makename(ext='.pmd'))
769 def execute_25(*args):
770 progress_start('pmd_import')
775 class IMPORT_OT_pmd(bpy.types.Operator):
776 bl_idname = "import_scene.pmd"
777 bl_label = 'Import PMD'
779 # List of operator properties, the attributes will be assigned
780 # to the class instance from the operator settings before calling.
782 path = StringProperty(
784 description="File path used for importing the PMD file",
785 maxlen= 1024, default= "")
786 filename = StringProperty(
788 description="Name of the file.")
789 directory = StringProperty(
791 description="Directory of the file.")
793 def execute(self, context):
794 execute_25(self.properties.path, context.scene)
797 def invoke(self, context, event):
799 wm.add_fileselect(self)
800 return 'RUNNING_MODAL'
803 def menu_func(self, context):
804 self.layout.operator(IMPORT_OT_pmd.bl_idname,
805 text="MikuMikuDance model (.pmd)")
808 bpy.types.register(IMPORT_OT_pmd)
809 bpy.types.INFO_MT_file_import.append(menu_func)
812 bpy.types.unregister(IMPORT_OT_pmd)
813 bpy.types.INFO_MT_file_import.remove(menu_func)
815 if __name__=="__main__":