4 Name: 'MikuMikuDance model (.pmd)...'
7 Tooltip: 'Import PMD file for MikuMikuDance.'
9 __author__= ["ousttrue"]
14 0.2: 20091209 implement IK.
15 0.3: 20091210 implement morph target.
16 0.4: 20100305 use english name.
17 0.5: 20100408 cleanup not used vertices.
18 0.6: 20100416 fix fornt face. texture load fail safe. add progress.
19 0.7: 20100506 C extension.
20 0.8: 20100521 add shape_key group.
23 from Blender import Mathutils
31 from meshio import pmd, englishmap
34 FS_ENCODING=sys.getfilesystemencoding()
35 if os.path.exists(os.path.dirname(sys.argv[0])+"/utf8"):
36 INTERNAL_ENCODING='utf-8'
38 INTERNAL_ENCODING=FS_ENCODING
41 MMD_SHAPE_GROUP_NAME='_MMD_SHAPE'
44 class ProgressBar(object):
45 def __init__(self, base):
46 print "#### %s ####" % base
48 self.start=Blender.sys.time()
49 self.set('<start>', 0)
51 def advance(self, message, progress):
52 self.progress+=float(progress)
55 def set(self, message, progress):
56 self.progress=float(progress)
59 def _print(self, message):
61 message="%s: %s" % (self.base, message)
62 if message.__class__ is unicode:
63 message=message.encode(INTERNAL_ENCODING)
64 Blender.Window.DrawProgressBar(self.progress, message)
68 message='finished in %.2f sec' % (Blender.sys.time()-self.start)
69 self.set(message, 1.0)
71 def progress_start(base):
73 progressBar=ProgressBar(base)
75 def progress_finish():
79 def progress_print(message, progress=0.05):
81 progressBar.advance(message, progress)
83 def progress_set(message, progress):
85 progressBar.set(message, progress)
88 def convert_coord(pos):
90 Left handed y-up to Right handed z-up
92 return (pos.x, pos.z, pos.y)
96 return (uv.x, 1.0 - uv.y)
99 def get_bone_name(l, index):
100 name=englishmap.getEnglishBoneName(l.bones[index].getName())
101 return name if name else l.bones[index].getName().encode(INTERNAL_ENCODING)
104 def createMaterial():
106 create default materil
108 material=Blender.Material.New()
109 material.setDiffuseShader(Blender.Material.Shaders.DIFFUSE_TOON)
111 material.diffuseSize = 3.14/2
112 material.setDiffuseSmooth(0)
113 material.setSpecShader(Blender.Material.Shaders.SPEC_TOON)
114 material.setSpecSize(0)
119 def importMesh(scene, l, tex_dir):
121 @param l[in] mmd.PMDLoader
125 ############################################################
126 # shpaeキーで使われるマテリアル優先的に前に並べる
127 ############################################################
128 # shapeキーで使われる頂点インデックスを集める
129 shape_key_used_vertices=set()
130 if len(l.morph_list)>0:
133 for s in l.morph_list:
140 for index in base.indices:
141 shape_key_used_vertices.add(index)
143 # マテリアルに含まれる頂点がshape_keyに含まれるか否か?
144 def isMaterialUsedInShape(offset, m):
145 for i in xrange(offset, offset+m.vertex_count):
146 if l.indices[i] in shape_key_used_vertices:
149 # shapeキーで使われるマテリアルを記録する
150 shape_key_materials=set()
151 # 各マテリアルの開始頂点インデックスを記録する
154 for i, m in enumerate(l.materials):
155 face_map[i]=face_count
156 if isMaterialUsedInShape(face_count, m):
157 shape_key_materials.add(i)
158 face_count+=m.vertex_count
161 material_order=list(shape_key_materials)
163 # shapeキーに使われていないマテリアルを後ろに追加
164 for i in range(len(l.materials)):
165 if not i in material_order:
166 material_order.append(i)
168 # マテリアル16個ごとに分割したメッシュを作成する
171 while material_offset<len(l.materials):
173 mesh = Blender.Mesh.New()
176 obj = scene.objects.new(mesh)
178 mesh_objects.append(obj)
180 # shapeキーで使われる順に並べなおしたマテリアル16個分の
182 vertex_map=import16MaerialAndMesh(
184 material_order[material_offset:material_offset+16],
188 importShape(obj, l, vertex_map)
196 def import16MaerialAndMesh(mesh, l, material_order, face_map, tex_dir):
197 ############################################################
199 ############################################################
200 progress_print('create materials')
204 for material_index in material_order:
206 m=l.materials[material_index]
207 mesh_material_map[material_index]=index
211 material=createMaterial()
212 material.setRGBCol([m.diffuse.r, m.diffuse.g, m.diffuse.b])
213 material.setAlpha(m.diffuse.a)
214 material.setSpec(m.shinness*0.1)
215 material.setSpecCol([m.specular.r, m.specular.g, m.specular.b])
216 material.setMirCol([m.ambient.r, m.ambient.g, m.ambient.b])
217 material.enableSSS=True if m.flag==1 else False
219 if m.getTexture()!='':
220 tex_file=re.compile('\*.*.spa$').sub('', m.getTexture())
221 tex_path = os.path.join(tex_dir, tex_file).encode(
223 tex = Blender.Texture.New()
226 tex.image = Blender.Image.Load(tex_path)
227 material.setTexture(0, tex)
228 material.getTextures()[0].texco = Blender.Texture.TexCo.UV
230 print material.name, "fail to load", tex_path
231 materials.append(material)
232 # lookup table for assign
234 mesh.materials=materials
236 ############################################################
238 ############################################################
239 progress_print('create vertices')
243 for v in l.each_vertex():
244 vertices.append(convert_coord(v.pos))
245 vertex_groups[v.bone0]=True
246 vertex_groups[v.bone1]=True
247 mesh.verts.extend(vertices)
249 # create vertex group
250 for i in vertex_groups.keys():
251 mesh.addVertGroup(get_bone_name(l, i))
254 for i, v, mvert in zip(xrange(len(l.vertices)), l.each_vertex(), mesh.verts):
255 mvert.no=Mathutils.Vector(*convert_coord(v.normal))
256 mvert.uvco=convert_uv(v.uv)
257 w1=float(v.weight0)/100.0
259 mesh.assignVertsToGroup(get_bone_name(l, v.bone0), [i], w1,
260 Blender.Mesh.AssignModes.ADD)
261 mesh.assignVertsToGroup(get_bone_name(l, v.bone1), [i], w2,
262 Blender.Mesh.AssignModes.ADD)
264 ############################################################
266 ############################################################
267 progress_print('create faces')
270 mesh_face_materials=[]
273 def degenerate(i0, i1, i2):
274 return i0==i1 or i1==i2 or i2==i0
276 for material_index in material_order:
277 face_offset=face_map[material_index]
278 m=l.materials[material_index]
279 material_faces=l.indices[face_offset:face_offset+m.vertex_count]
280 for j in xrange(0, len(material_faces), 3):
282 i1=material_faces[j+1]
283 i2=material_faces[j+2]
284 triangle=[i0, i1, i2]
285 if degenerate(*triangle):
287 mesh_face_indices.append(triangle)
288 mesh_face_materials.append(material_index)
289 used_vertices.add(i0)
290 used_vertices.add(i1)
291 used_vertices.add(i2)
293 mesh.faces.extend(mesh_face_indices, ignoreDups=True)
297 mesh.addUVLayer('NewUV')
298 for face, material_index in zip(mesh.faces, mesh_face_materials):
300 index=mesh_material_map[material_index]
301 except KeyError, message:
302 print message, mesh_material_map, m
305 material=mesh.materials[index]
306 texture=material.getTextures()[0]
309 face.image=texture.tex.image
310 texture.tex.imageFlags|=Blender.Texture.ImageFlags.USEALPHA
311 face.uv=[face.verts[0].uvco, face.verts[1].uvco, face.verts[2].uvco]
317 ############################################################
318 # clean up not used vertices
319 ############################################################
320 progress_print('clean up vertices not used')
323 for i, v in enumerate(l.each_vertex()):
324 if i in used_vertices:
325 vertex_map[i]=len(vertex_map)
327 remove_vertices.append(i)
328 mesh.verts.delete(remove_vertices)
330 progress_print('%s created' % mesh.name)
334 def validParent(parent):
337 if parent.name.startswith("arm twist"):
339 if parent.name.startswith("wrist twist"):
344 class Builder(object):
348 def build(self, armature, bones):
351 self.__build(armature, b, None, None)
353 def __build(self, armature, b, p, parent):
354 name=englishmap.getEnglishBoneName(b.getName())
356 name=b.getName().encode(INTERNAL_ENCODING)
362 bone=Blender.Armature.Editbone()
364 armature.bones[name] = bone
366 if bone.name=='center':
368 pos=Mathutils.Vector(*convert_coord(b.pos))
370 bone.head = pos-Mathutils.Vector(0, 0.01, 0)
372 bone.head = Mathutils.Vector(*convert_coord(b.pos))
373 bone.tail = Mathutils.Vector(*convert_coord(b.tail))
375 if validParent(parent):
377 if parent.tail==bone.head:
379 bone.options=[Blender.Armature.CONNECTED]
381 if bone.head==bone.tail:
382 bone.tail=bone.head-Mathutils.Vector(0, 1, 0)
385 self.__build(armature, c, b, bone)
387 def connect(self, armature):
389 for key, value in self.boneMap.items():
390 if index==value.index:
393 for k, b in self.boneMap.items():
396 bone=armature.bones[k]
397 key=getKey(b.tail_index)
401 tail=armature.bones[key]
404 tail.options=[Blender.Armature.CONNECTED]
408 def importArmature(scene, l):
410 armature = Blender.Armature.New()
412 armature_object = scene.objects.new(armature)
414 act = Blender.Armature.NLA.NewAction()
415 act.setActive(armature_object)
417 armature_object.drawMode = (
418 armature_object.drawMode | Blender.Object.DrawModes.XRAY)
420 armature.drawType = Blender.Armature.OCTAHEDRON
421 armature.envelopes = False
422 armature.vertexGroups = True
423 armature.mirrorEdit = True
426 armature.makeEditable()
429 builder.build(armature, l.bones)
430 builder.connect(armature)
434 ############################################################
436 ############################################################
437 pose = armature_object.getPose()
438 cSetting = Blender.Constraint.Settings
440 # IKtarget->parent(=IK).name
441 target=l.bones[ik.target]
442 parent=l.bones[target.parent_index]
443 name = englishmap.getEnglishBoneName(parent.getName())
444 p_bone = pose.bones[name]
446 print 'not found', name
448 if len(ik.children) >= 16:
449 print 'over MAX_CHAINLEN', ik, len(ik.children)
452 ik_const = p_bone.constraints.append(Blender.Constraint.Type.IKSOLVER)
453 ik_const[cSetting.CHAINLEN] = len(ik.children)
454 ik_const[cSetting.TARGET] = armature_object
456 effector_name=englishmap.getEnglishBoneName(
457 l.bones[ik.index].getName())
458 if not effector_name:
459 effector_name=l.bones[ik.index].getName()
461 ik_const[cSetting.BONE] = effector_name
462 ik_const[cSetting.ITERATIONS]=ik.iterations
463 ik_const.influence = ik.weight
465 #lrot_const = p_bone.constraints.append(Blender.Constraint.Type.LIMITROT)
466 #lrot_const[cSetting.OWNERSPACE] = cSetting.SPACE_LOCAL
467 #lrot_const[cSetting.LIMIT] = (cSetting.LIMIT_XROT | cSetting.LIMIT_ZROT)
468 #lrot_const[cSetting.XMIN] = 0.1
469 #lrot_const[cSetting.XMAX] = 180
470 #lrot_const[cSetting.ZMIN] = 180 - 0.1
471 #lrot_const[cSetting.ZMAX] = 0
473 armature.makeEditable()
476 return armature_object
479 def importShape(obj, l, vertex_map):
480 if len(l.morph_list)==0:
483 mesh=obj.getData(mesh=True)
487 for s in l.morph_list:
491 # create vertex group
492 mesh.addVertGroup(MMD_SHAPE_GROUP_NAME)
498 indices.append(vertex_map[i])
499 mesh.assignVertsToGroup(MMD_SHAPE_GROUP_NAME, indices, 0,
500 Blender.Mesh.AssignModes.ADD)
506 assert(len(mesh.key.blocks)==1)
508 baseShapeBlock=mesh.key.blocks[baseShapeIndex]
509 baseShapeBlock.name='Basis'
510 obj.activeShape=baseShapeIndex
517 for s in l.morph_list:
518 if s.name==base.name:
521 for index, offset in zip(s.indices, s.pos_list):
523 vertex_index=vertex_map[base.indices[index]]
524 v=mesh.verts[vertex_index].co
525 offset=convert_coord(offset)
529 except IndexError, msg:
531 print index, len(base.indices), len(vertex_map)
532 print len(mesh.verts)
533 print base.indices[index]
537 #print 'this mesh not has shape vertices'
541 name=englishmap.getEnglishSkinName(s.getName())
543 name=s.getName().encode(INTERNAL_ENCODING)
546 # create shapekey block
548 shapeIndex=len(mesh.key.blocks)-1
549 keyBlock=mesh.key.blocks[shapeIndex]
552 # copy vertex to shape key
556 for mv, v in zip(mesh.verts, baseShapeBlock.getData()):
563 obj.activeShape=baseShapeIndex
569 filename=filename.decode(INTERNAL_ENCODING)
570 tex_dir=os.path.dirname(filename)
573 progress_start('pmd_import')
574 print(INTERNAL_ENCODING, FS_ENCODING)
577 progress_set('load %s' % filename, 0.0)
580 locale.setlocale(locale.LC_ALL, '')
583 if not l.read(filename):
584 print "fail to load %s" % filename
586 progress_set('loaded %s' % filename, 0.1)
589 mode_edit = Blender.Window.EditMode()
591 Blender.Window.EditMode(0)
593 scene = bpy.data.scenes.active
595 # import objects container
596 root=scene.objects.new("Empty")
598 l.english_name if len(l.english_name)>0 else l.getName().encode(INTERNAL_ENCODING))
601 mesh_objects=importMesh(scene, l, tex_dir)
602 root.makeParent(mesh_objects)
605 armature_object=importArmature(scene, l)
607 armature = armature_object.getData()
608 armature.drawNames=True
609 root.makeParent([armature_object])
611 # add armature modifier
612 for o in mesh_objects:
613 mod=o.modifiers.append(Blender.Modifier.Types.ARMATURE)
614 mod[Blender.Modifier.Settings.OBJECT] = armature_object
615 mod[Blender.Modifier.Settings.ENVELOPES] = False
618 ############################################################
620 ############################################################
621 for n, b in armature_object.getPose().bones.items():
622 if n.startswith("knee_"):
627 b.limitMax=[180, 0, 0]
634 Blender.Window.EditMode(1)
637 Blender.Window.RedrawAll()
640 if __name__=="__main__":
641 Blender.Window.FileSelector(
644 Blender.sys.makename(ext='.pmd'))