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.
31 MMD_SHAPE_GROUP_NAME='_MMD_SHAPE'
32 BASE_SHAPE_NAME='Basis'
33 RIGID_SHAPE_TYPE='rigid_shape_type'
34 RIGID_PROCESS_TYPE='rigid_process_type'
35 RIGID_BONE_NAME='rigid_bone_name'
36 #RIGID_LOCATION='rigid_loation'
37 RIGID_GROUP='ribid_group'
38 RIGID_INTERSECTION_GROUP='rigid_intersection_group'
39 RIGID_WEIGHT='rigid_weight'
40 RIGID_LINEAR_DAMPING='rigid_linear_damping'
41 RIGID_ANGULAR_DAMPING='rigid_angular_damping'
42 RIGID_RESTITUTION='rigid_restitution'
43 RIGID_FRICTION='rigid_friction'
44 CONSTRAINT_A='const_a'
45 CONSTRAINT_B='const_b'
46 CONSTRAINT_POS_MIN='const_pos_min'
47 CONSTRAINT_POS_MAX='const_pos_max'
48 CONSTRAINT_ROT_MIN='const_rot_min'
49 CONSTRAINT_ROT_MAX='const_rot_max'
50 CONSTRAINT_SPRING_POS='const_spring_pos'
51 CONSTRAINT_SPRING_ROT='const_spring_rot'
54 ###############################################################################
56 ###############################################################################
62 from meshio import pmd, englishmap
65 return sys.version_info[0]<3
70 from Blender import Mathutils
78 from bpy.props import *
86 ###############################################################################
87 def convert_coord(pos):
89 Left handed y-up to Right handed z-up
91 return (pos.x, pos.z, pos.y)
95 return (uv.x, 1.0 - uv.y)
98 def to_radian(degree):
99 return math.pi * degree / 180
102 def get_bone_name(l, index):
104 return l.bones[0].getName()
106 if index < len(l.bones):
107 name=englishmap.getEnglishBoneName(l.bones[index].getName())
110 return l.bones[index].getName()
111 print('invalid bone index', index)
112 return l.bones[0].getName()
114 def __importShape(obj, l, vertex_map):
115 if len(l.morph_list)==0:
119 bl.objectPinShape(obj)
123 for s in l.morph_list:
127 # create vertex group
128 bl.meshAddVertexGroup(obj, MMD_SHAPE_GROUP_NAME)
133 bl.meshAssignVertexGroup(
134 obj, MMD_SHAPE_GROUP_NAME, vertex_map[i], 0)
140 baseShapeBlock=bl.objectAddShapeKey(obj, BASE_SHAPE_NAME)
142 mesh=bl.objectGetData(obj)
146 for s in l.morph_list:
151 name=englishmap.getEnglishSkinName(s.getName())
157 for index, offset in zip(s.indices, s.pos_list):
159 vertex_index=vertex_map[base.indices[index]]
160 v=mesh.verts[vertex_index].co
161 offset=convert_coord(offset)
165 except IndexError as msg:
167 print(index, len(base.indices), len(vertex_map))
168 print(len(mesh.verts))
169 print(base.indices[index])
173 #print 'this mesh not has shape vertices'
176 # create shapekey block
177 new_shape_key=bl.objectAddShapeKey(obj, name)
179 # copy vertex to shape key
183 for mv, v in zip(mesh.verts, baseShapeBlock.getData()):
191 new_shape_key=bl.objectAddShapeKey(obj, name)
193 for index, offset in zip(s.indices, s.pos_list):
195 vertex_index=vertex_map[base.indices[index]]
196 bl.shapeKeyAssign(new_shape_key, vertex_index,
197 mesh.verts[vertex_index].co+
198 bl.createVector(*convert_coord(offset)))
199 except IndexError as msg:
201 print(index, len(base.indices), len(vertex_map))
202 print(len(mesh.verts))
203 print(base.indices[index])
207 #print 'this mesh not has shape vertices'
211 bl.objectActivateShapeKey(obj, 0)
214 def __build(armature, b, p, parent):
215 name=englishmap.getEnglishBoneName(b.getName())
219 bone=bl.createArmatureBone(armature, name)
221 if parent and (b.tail_index==0 or b.type==6 or b.type==7 or b.type==9):
222 bone.head = bl.createVector(*convert_coord(b.pos))
223 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 if parent.tail==bone.head:
234 print('diffurence with parent.tail and head', name)
236 bl.boneSetConnected(bone)
238 bl.boneLayerMask(bone, [0, 1])
241 bone.head = bl.createVector(*convert_coord(b.pos))
242 bone.tail = bl.createVector(*convert_coord(b.tail))
245 if parent.tail==bone.head:
246 bl.boneSetConnected(bone)
248 if bone.head==bone.tail:
249 bone.tail=bone.head+bl.createVector(0, 1, 0)
252 __build(armature, c, b, bone)
255 def __importArmature(scene, l):
257 armature, armature_object=bl.createArmature(scene)
258 bl.armatureMakeEditable(scene, armature_object)
261 __build(armature, b, None, None)
262 bl.armatureUpdate(armature)
266 pose = bl.objectGetPose(armature_object)
268 target=l.bones[ik.target]
269 name = englishmap.getEnglishBoneName(target.getName())
271 name=target.getName()
272 p_bone = pose.bones[name]
274 print('not found', name)
276 if len(ik.children) >= 16:
277 print('over MAX_CHAINLEN', ik, len(ik.children))
279 effector_name=englishmap.getEnglishBoneName(
280 l.bones[ik.index].getName())
281 if not effector_name:
282 effector_name=l.bones[ik.index].getName()
284 constraint=bl.createIkConstraint(armature_object,
285 p_bone, effector_name, ik)
287 bl.armatureMakeEditable(scene, armature_object)
288 bl.armatureUpdate(armature)
291 return armature_object
294 def __import16MaerialAndMesh(meshObject, l,
295 material_order, face_map, tex_dir):
297 mesh=bl.objectGetData(meshObject)
298 ############################################################
300 ############################################################
301 bl.progress_print('create materials')
307 for material_index in material_order:
309 m=l.materials[material_index]
310 mesh_material_map[material_index]=index
314 material=bl.createPmdMaterial(m)
316 texture_name=m.getTexture()
318 if texture_name in textureMap:
319 texture=textureMap[texture_name]
322 texture, image=bl.createTexture(
323 os.path.join(tex_dir, texture_name))
324 textureMap[texture_name]=texture
325 imageMap[material_index]=image
328 bl.materialAddTexture(material, texture)
329 bl.meshAddMaterial(mesh, material)
332 ############################################################
334 ############################################################
335 bl.progress_print('create vertices')
339 for v in l.each_vertex():
340 vertices.append(convert_coord(v.pos))
342 for v in l.each_vertex():
343 vertices.extend(convert_coord(v.pos))
345 ############################################################
347 ############################################################
348 bl.progress_print('create faces')
351 mesh_face_materials=[]
354 for material_index in material_order:
355 face_offset=face_map[material_index]
356 m=l.materials[material_index]
357 material_faces=l.indices[face_offset:face_offset+m.vertex_count]
359 def degenerate(i0, i1, i2):
363 return i0==i1 or i1==i2 or i2==i0
365 for j in xrange(0, len(material_faces), 3):
367 i1=material_faces[j+1]
368 i2=material_faces[j+2]
370 triangle=[i2, i0, i1]
372 triangle=[i0, i1, i2]
373 if degenerate(*triangle):
376 mesh_face_indices.append(triangle[0:3])
378 mesh_face_indices.extend(
379 [triangle[0], triangle[1], triangle[2], 0])
380 mesh_face_materials.append(material_index)
381 used_vertices.add(i0)
382 used_vertices.add(i1)
383 used_vertices.add(i2)
385 ############################################################
386 # create vertices & faces
387 ############################################################
388 bl.meshCreateVerteicesAndFaces(mesh, vertices, mesh_face_indices)
390 ############################################################
392 ############################################################
393 # create vertex group
395 for v in l.each_vertex():
396 vertex_groups[v.bone0]=True
397 vertex_groups[v.bone1]=True
398 for i in vertex_groups.keys():
399 bl.meshAddVertexGroup(meshObject, get_bone_name(l, i))
402 bl.meshUseVertexUv(mesh)
403 for i, v, mvert in zip(xrange(len(l.vertices)),
404 l.each_vertex(), mesh.verts):
406 bl.vertexSetNormal(mvert, convert_coord(v.normal))
407 bl.vertexSetUv(mvert, convert_uv(v.uv))
409 w1=float(v.weight0)/100.0
411 bl.meshAssignVertexGroup(meshObject, get_bone_name(l, v.bone0),
413 bl.meshAssignVertexGroup(meshObject, get_bone_name(l, v.bone1),
416 ############################################################
418 ############################################################
423 for face, material_index in zip(mesh.faces, mesh_face_materials):
425 index=mesh_material_map[material_index]
426 except KeyError as message:
427 print(message, mesh_material_map, m)
430 material=mesh.materials[index]
431 texture=material.getTextures()[0]
434 face.image=texture.tex.image
435 texture.tex.imageFlags|=Blender.Texture.ImageFlags.USEALPHA
437 face.verts[0].uvco, face.verts[1].uvco, face.verts[2].uvco]
443 for face, uv_face, material_index in zip(mesh.faces,
444 mesh.uv_textures[0].data,
448 index=mesh_material_map[material_index]
449 except KeyError as message:
450 print(message, mesh_material_map, m)
452 face.material_index=index
453 material=mesh.materials[index]
455 if material.texture_slots[0]:
456 uv=l.getUV(face.verts[0])
457 uv_face.uv1=[uv.x, 1.0-uv.y]
459 uv=l.getUV(face.verts[1])
460 uv_face.uv2=[uv.x, 1.0-uv.y]
462 uv=l.getUV(face.verts[2])
463 uv_face.uv3=[uv.x, 1.0-uv.y]
464 if face.material_index in imageMap:
465 uv_face.image=imageMap[face.material_index]
472 ############################################################
473 # clean up not used vertices
474 ############################################################
475 bl.progress_print('clean up vertices not used')
478 for i, v in enumerate(l.each_vertex()):
479 if i in used_vertices:
480 vertex_map[i]=len(vertex_map)
482 remove_vertices.append(i)
484 bl.meshVertsDelete(mesh, remove_vertices)
486 bl.progress_print('%s created' % mesh.name)
490 def __importMesh(scene, io, tex_dir):
492 @param l[in] mmd.PMDLoader
495 ############################################################
496 # shpaeキーで使われるマテリアル優先的に前に並べる
497 ############################################################
498 # shapeキーで使われる頂点インデックスを集める
499 shape_key_used_vertices=set()
500 if len(io.morph_list)>0:
503 for s in io.morph_list:
510 for index in base.indices:
511 shape_key_used_vertices.add(index)
513 # マテリアルに含まれる頂点がshape_keyに含まれるか否か?
514 def isMaterialUsedInShape(offset, m):
515 for i in xrange(offset, offset+m.vertex_count):
516 if io.indices[i] in shape_key_used_vertices:
519 # shapeキーで使われるマテリアルを記録する
520 shape_key_materials=set()
521 # 各マテリアルの開始頂点インデックスを記録する
524 for i, m in enumerate(io.materials):
525 face_map[i]=face_count
526 if isMaterialUsedInShape(face_count, m):
527 shape_key_materials.add(i)
528 face_count+=m.vertex_count
531 material_order=list(shape_key_materials)
533 # shapeキーに使われていないマテリアルを後ろに追加
534 for i in range(len(io.materials)):
535 if not i in material_order:
536 material_order.append(i)
538 # マテリアル16個ごとに分割したメッシュを作成する
541 while material_offset<len(io.materials):
542 mesh, meshObject=bl.createMesh(scene, 'mesh')
544 mesh_objects.append(meshObject)
547 bl.objectDeselectAll()
548 bl.objectActivate(scene, meshObject)
550 # shapeキーで使われる順に並べなおしたマテリアル16個分の
552 vertex_map=__import16MaerialAndMesh(
554 material_order[material_offset:material_offset+16],
561 __importShape(meshObject, io, vertex_map)
572 def __importConstraints(scene, io):
575 print("create constrains")
576 container=bl.createEmptyObject(scene, 'Constraints')
578 True, False, False, False, False, False, False, False,
579 False, False, False, False, False, False, False, False,
580 False, False, False, False, False, False, False, False,
581 False, False, False, False, False, False, False, False,
583 material=bl.createMaterial('constraint')
584 material.diffuse_color=(1, 0, 0)
586 for c in io.constraints:
587 bpy.ops.mesh.primitive_uv_sphere_add(
591 location=(c.pos.x, c.pos.z, c.pos.y),
594 meshObject=scene.objects.active
595 constraintMeshes.append(meshObject)
596 mesh=bl.objectGetData(meshObject)
597 bl.meshAddMaterial(mesh, material)
598 meshObject.name='c'+c.getName()
599 #meshObject.draw_transparent=True
600 #meshObject.draw_wire=True
601 meshObject.max_draw_type='SOLID'
603 meshObject.rotation_euler=(-rot.x, -rot.z, -rot.y)
605 meshObject[CONSTRAINT_A]=io.rigidbodies[c.rigidA].getName()
606 meshObject[CONSTRAINT_B]=io.rigidbodies[c.rigidB].getName()
607 meshObject[CONSTRAINT_POS_MIN]=bl.VtoV(c.constraintPosMin)
608 meshObject[CONSTRAINT_POS_MAX]=bl.VtoV(c.constraintPosMax)
609 meshObject[CONSTRAINT_ROT_MIN]=bl.VtoV(c.constraintRotMin)
610 meshObject[CONSTRAINT_ROT_MAX]=bl.VtoV(c.constraintRotMax)
611 meshObject[CONSTRAINT_SPRING_POS]=bl.VtoV(c.springPos)
612 meshObject[CONSTRAINT_SPRING_ROT]=bl.VtoV(c.springRot)
614 for meshObject in reversed(constraintMeshes):
615 bl.objectMakeParent(container, meshObject)
620 def __importRigidBodies(scene, io):
623 print("create rigid bodies")
625 container=bl.createEmptyObject(scene, 'RigidBodies')
627 True, False, False, False, False, False, False, False,
628 False, False, False, False, False, False, False, False,
629 False, False, False, False, False, False, False, False,
630 False, False, False, False, False, False, False, False,
632 material=bl.createMaterial('rigidBody')
634 for rigid in io.rigidbodies:
635 if rigid.boneIndex==0xFFFF:
639 bone=io.bones[rigid.boneIndex]
640 pos=bone.pos+rigid.position
642 if rigid.shapeType==pmd.SHAPE_SPHERE:
643 bpy.ops.mesh.primitive_ico_sphere_add(
644 location=(pos.x, pos.z, pos.y),
647 bpy.ops.transform.resize(
648 value=(rigid.w, rigid.w, rigid.w))
649 elif rigid.shapeType==pmd.SHAPE_BOX:
650 bpy.ops.mesh.primitive_cube_add(
651 location=(pos.x, pos.z, pos.y),
654 bpy.ops.transform.resize(
655 value=(rigid.w, rigid.d, rigid.h))
656 elif rigid.shapeType==pmd.SHAPE_CAPSULE:
657 bpy.ops.mesh.primitive_tube_add(
658 location=(pos.x, pos.z, pos.y),
661 bpy.ops.transform.resize(
662 value=(rigid.w, rigid.w, rigid.h))
666 meshObject=scene.objects.active
667 mesh=bl.objectGetData(meshObject)
668 rigidMeshes.append(meshObject)
669 bl.meshAddMaterial(mesh, material)
670 meshObject.name=rigid.getName()
671 #meshObject.draw_transparent=True
672 #meshObject.draw_wire=True
673 meshObject.max_draw_type='WIRE'
675 meshObject.rotation_euler=(-rot.x, -rot.z, -rot.y)
678 meshObject[RIGID_SHAPE_TYPE]=rigid.shapeType
679 meshObject[RIGID_PROCESS_TYPE]=rigid.processType
681 bone_name = englishmap.getEnglishBoneName(bone.getName())
683 bone_name=bone.getName()
684 meshObject[RIGID_BONE_NAME]=bone_name
686 #meshObject[RIGID_LOCATION]=bl.VtoV(rigid.location)
687 meshObject[RIGID_GROUP]=rigid.group
688 meshObject[RIGID_INTERSECTION_GROUP]=rigid.target
689 meshObject[RIGID_WEIGHT]=rigid.weight
690 meshObject[RIGID_LINEAR_DAMPING]=rigid.linearDamping
691 meshObject[RIGID_ANGULAR_DAMPING]=rigid.angularDamping
692 meshObject[RIGID_RESTITUTION]=rigid.restitution
693 meshObject[RIGID_FRICTION]=rigid.friction
695 for meshObject in reversed(rigidMeshes):
696 bl.objectMakeParent(container, meshObject)
701 def __execute(filename, scene):
703 load pmd file to context.
705 bl.progress_start('pmd_import')
708 bl.progress_set('load %s' % filename, 0.0)
711 if not io.read(filename):
712 print("fail to load %s" % filename)
714 bl.progress_set('loaded %s' % filename, 0.1)
717 model_name=io.getEnglishName()
718 if len(model_name)==0:
719 model_name=io.getName()
720 root=bl.createEmptyObject(scene, model_name)
723 mesh_objects=__importMesh(scene, io, os.path.dirname(filename))
724 for o in mesh_objects:
725 bl.objectMakeParent(root, o)
728 armature_object=__importArmature(scene, io)
730 bl.objectMakeParent(root, armature_object)
731 armature = bl.objectGetData(armature_object)
733 # add armature modifier
734 for o in mesh_objects:
735 bl.objectAddArmatureModifier(o, armature_object)
738 for n, b in bl.objectGetPose(armature_object).bones.items():
739 bl.poseBoneLimit(n, b)
741 # import rigid bodies
742 rigidBodies=__importRigidBodies(scene, io)
744 bl.objectMakeParent(root, rigidBodies)
747 constraints=__importConstraints(scene, io)
749 bl.objectMakeParent(root, constraints)
751 bl.objectActivate(scene, root)
756 def execute_24(filename):
760 filename=filename.decode(bl.INTERNAL_ENCODING)
761 print(bl.INTERNAL_ENCODING, bl.FS_ENCODING)
764 mode_edit = Blender.Window.EditMode()
766 Blender.Window.EditMode(0)
768 scene = bpy.data.scenes.active
769 __execute(filename, scene)
774 Blender.Window.EditMode(1)
775 Blender.Window.RedrawAll()
777 Blender.Window.FileSelector(
780 Blender.sys.makename(ext='.pmd'))
784 def execute_25(*args):
788 class IMPORT_OT_pmd(bpy.types.Operator):
789 bl_idname = "import_scene.pmd"
790 bl_label = 'Import PMD'
792 # List of operator properties, the attributes will be assigned
793 # to the class instance from the operator settings before calling.
795 path = StringProperty(
797 description="File path used for importing the PMD file",
798 maxlen= 1024, default= "")
799 filename = StringProperty(
801 description="Name of the file.")
802 directory = StringProperty(
804 description="Directory of the file.")
806 def execute(self, context):
807 execute_25(self.properties.path, context.scene)
810 def invoke(self, context, event):
812 wm.add_fileselect(self)
813 return 'RUNNING_MODAL'
816 def menu_func(self, context):
817 self.layout.operator(IMPORT_OT_pmd.bl_idname,
818 text="MikuMikuDance model (.pmd)")
821 bpy.types.register(IMPORT_OT_pmd)
822 bpy.types.INFO_MT_file_import.append(menu_func)
825 bpy.types.unregister(IMPORT_OT_pmd)
826 bpy.types.INFO_MT_file_import.remove(menu_func)
828 if __name__=="__main__":