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.
21 1.0: 20100530 add invisilbe bone tail(armature layer 2).
24 from Blender import Mathutils
32 from meshio import pmd, englishmap
35 FS_ENCODING=sys.getfilesystemencoding()
36 if os.path.exists(os.path.dirname(sys.argv[0])+"/utf8"):
37 INTERNAL_ENCODING='utf-8'
39 INTERNAL_ENCODING=FS_ENCODING
42 MMD_SHAPE_GROUP_NAME='_MMD_SHAPE'
45 ###############################################################################
47 ###############################################################################
48 class ProgressBar(object):
49 def __init__(self, base):
50 print "#### %s ####" % base
52 self.start=Blender.sys.time()
53 self.set('<start>', 0)
55 def advance(self, message, progress):
56 self.progress+=float(progress)
59 def set(self, message, progress):
60 self.progress=float(progress)
63 def _print(self, message):
65 message="%s: %s" % (self.base, message)
66 if message.__class__ is unicode:
67 message=message.encode(FS_ENCODING)
68 Blender.Window.DrawProgressBar(self.progress, message)
72 message='finished in %.2f sec' % (Blender.sys.time()-self.start)
73 self.set(message, 1.0)
75 def progress_start(base):
77 progressBar=ProgressBar(base)
79 def progress_finish():
83 def progress_print(message, progress=0.05):
85 progressBar.advance(message, progress)
87 def progress_set(message, progress):
89 progressBar.set(message, progress)
92 ###############################################################################
94 ###############################################################################
95 def convert_coord(pos):
97 Left handed y-up to Right handed z-up
99 return (pos.x, pos.z, pos.y)
103 return (uv.x, 1.0 - uv.y)
106 def get_bone_name(l, index):
107 name=englishmap.getEnglishBoneName(l.bones[index].getName())
108 return name if name else l.bones[index].getName().encode(INTERNAL_ENCODING)
111 def createMaterial():
113 create default materil
115 material=Blender.Material.New()
116 material.setDiffuseShader(Blender.Material.Shaders.DIFFUSE_TOON)
118 material.diffuseSize = 3.14/2
119 material.setDiffuseSmooth(0)
120 material.setSpecShader(Blender.Material.Shaders.SPEC_TOON)
121 material.setSpecSize(0)
126 def importMesh(scene, l, tex_dir):
128 @param l[in] mmd.PMDLoader
132 ############################################################
133 # shpaeキーで使われるマテリアル優先的に前に並べる
134 ############################################################
135 # shapeキーで使われる頂点インデックスを集める
136 shape_key_used_vertices=set()
137 if len(l.morph_list)>0:
140 for s in l.morph_list:
147 for index in base.indices:
148 shape_key_used_vertices.add(index)
150 # マテリアルに含まれる頂点がshape_keyに含まれるか否か?
151 def isMaterialUsedInShape(offset, m):
152 for i in xrange(offset, offset+m.vertex_count):
153 if l.indices[i] in shape_key_used_vertices:
156 # shapeキーで使われるマテリアルを記録する
157 shape_key_materials=set()
158 # 各マテリアルの開始頂点インデックスを記録する
161 for i, m in enumerate(l.materials):
162 face_map[i]=face_count
163 if isMaterialUsedInShape(face_count, m):
164 shape_key_materials.add(i)
165 face_count+=m.vertex_count
168 material_order=list(shape_key_materials)
170 # shapeキーに使われていないマテリアルを後ろに追加
171 for i in range(len(l.materials)):
172 if not i in material_order:
173 material_order.append(i)
175 # マテリアル16個ごとに分割したメッシュを作成する
178 while material_offset<len(l.materials):
180 mesh = Blender.Mesh.New()
183 obj = scene.objects.new(mesh)
185 mesh_objects.append(obj)
187 # shapeキーで使われる順に並べなおしたマテリアル16個分の
189 vertex_map=import16MaerialAndMesh(
191 material_order[material_offset:material_offset+16],
195 importShape(obj, l, vertex_map)
203 def import16MaerialAndMesh(mesh, l, material_order, face_map, tex_dir):
204 ############################################################
206 ############################################################
207 progress_print('create materials')
211 for material_index in material_order:
213 m=l.materials[material_index]
214 mesh_material_map[material_index]=index
218 material=createMaterial()
219 material.setRGBCol([m.diffuse.r, m.diffuse.g, m.diffuse.b])
220 material.setAlpha(m.diffuse.a)
221 material.setSpec(m.shinness*0.1)
222 material.setSpecCol([m.specular.r, m.specular.g, m.specular.b])
223 material.setMirCol([m.ambient.r, m.ambient.g, m.ambient.b])
224 material.enableSSS=True if m.flag==1 else False
226 if m.getTexture()!='':
227 tex_file=re.compile('\*.*.spa$').sub('', m.getTexture())
228 tex_path = os.path.join(tex_dir, tex_file).encode(
230 tex = Blender.Texture.New()
233 tex.image = Blender.Image.Load(tex_path)
234 material.setTexture(0, tex)
235 material.getTextures()[0].texco = Blender.Texture.TexCo.UV
237 print material.name, "fail to load", tex_path
238 materials.append(material)
239 # lookup table for assign
241 mesh.materials=materials
243 ############################################################
245 ############################################################
246 progress_print('create vertices')
250 for v in l.each_vertex():
251 vertices.append(convert_coord(v.pos))
252 vertex_groups[v.bone0]=True
253 vertex_groups[v.bone1]=True
254 mesh.verts.extend(vertices)
256 # create vertex group
257 for i in vertex_groups.keys():
258 mesh.addVertGroup(get_bone_name(l, i))
261 for i, v, mvert in zip(xrange(len(l.vertices)), l.each_vertex(), mesh.verts):
262 mvert.no=Mathutils.Vector(*convert_coord(v.normal))
263 mvert.uvco=convert_uv(v.uv)
264 w1=float(v.weight0)/100.0
266 mesh.assignVertsToGroup(get_bone_name(l, v.bone0), [i], w1,
267 Blender.Mesh.AssignModes.ADD)
268 mesh.assignVertsToGroup(get_bone_name(l, v.bone1), [i], w2,
269 Blender.Mesh.AssignModes.ADD)
271 ############################################################
273 ############################################################
274 progress_print('create faces')
277 mesh_face_materials=[]
280 def degenerate(i0, i1, i2):
281 return i0==i1 or i1==i2 or i2==i0
283 for material_index in material_order:
284 face_offset=face_map[material_index]
285 m=l.materials[material_index]
286 material_faces=l.indices[face_offset:face_offset+m.vertex_count]
287 for j in xrange(0, len(material_faces), 3):
289 i1=material_faces[j+1]
290 i2=material_faces[j+2]
291 triangle=[i0, i1, i2]
292 if degenerate(*triangle):
294 mesh_face_indices.append(triangle)
295 mesh_face_materials.append(material_index)
296 used_vertices.add(i0)
297 used_vertices.add(i1)
298 used_vertices.add(i2)
300 mesh.faces.extend(mesh_face_indices, ignoreDups=True)
304 mesh.addUVLayer('NewUV')
305 for face, material_index in zip(mesh.faces, mesh_face_materials):
307 index=mesh_material_map[material_index]
308 except KeyError, message:
309 print message, mesh_material_map, m
312 material=mesh.materials[index]
313 texture=material.getTextures()[0]
316 face.image=texture.tex.image
317 texture.tex.imageFlags|=Blender.Texture.ImageFlags.USEALPHA
318 face.uv=[face.verts[0].uvco, face.verts[1].uvco, face.verts[2].uvco]
324 ############################################################
325 # clean up not used vertices
326 ############################################################
327 progress_print('clean up vertices not used')
330 for i, v in enumerate(l.each_vertex()):
331 if i in used_vertices:
332 vertex_map[i]=len(vertex_map)
334 remove_vertices.append(i)
335 mesh.verts.delete(remove_vertices)
337 progress_print('%s created' % mesh.name)
341 class Builder(object):
345 def build(self, armature, bones):
348 self.__build(armature, b, None, None)
351 def __build(self, armature, b, p, parent):
352 name=englishmap.getEnglishBoneName(b.getName())
354 name=b.getName().encode(INTERNAL_ENCODING)
357 bone=Blender.Armature.Editbone()
359 armature.bones[name]=bone
363 assert(b.type==6 or b.type==7)
364 bone.head = Mathutils.Vector(*convert_coord(b.pos))
365 bone.tail=bone.head+Mathutils.Vector(0, 1, 0)
368 if bone.name=="center_t":
369 # センターボーンは(0, 1, 0)の方向を向いていないと具合が悪い
370 parent.tail=parent.head+Mathutils.Vector(0, 1, 0)
371 bone.head=parent.tail
372 bone.tail=bone.head+Mathutils.Vector(0, 1, 0)
374 assert(parent.tail==bone.head)
375 bone.options=[Blender.Armature.CONNECTED]
377 bone.layerMask = (1<<1)
379 bone.head = Mathutils.Vector(*convert_coord(b.pos))
380 bone.tail = Mathutils.Vector(*convert_coord(b.tail))
383 if parent.tail==bone.head:
384 bone.options=[Blender.Armature.CONNECTED]
386 if bone.head==bone.tail:
387 bone.tail=bone.head+Mathutils.Vector(0, 1, 0)
390 self.__build(armature, c, b, bone)
393 def importArmature(scene, l):
395 armature = Blender.Armature.New()
397 armature_object = scene.objects.new(armature)
399 act = Blender.Armature.NLA.NewAction()
400 act.setActive(armature_object)
402 armature_object.drawMode = (
403 armature_object.drawMode | Blender.Object.DrawModes.XRAY)
405 armature.drawType = Blender.Armature.OCTAHEDRON
406 armature.drawNames=True
407 armature.envelopes = False
408 armature.vertexGroups = True
409 armature.mirrorEdit = True
412 armature.makeEditable()
414 ############################################################
416 ############################################################
418 builder.build(armature, l.bones)
420 ############################################################
422 ############################################################
423 pose = armature_object.getPose()
424 cSetting = Blender.Constraint.Settings
426 # IKtarget->parent(=IK).name
427 target=l.bones[ik.target]
428 name = englishmap.getEnglishBoneName(target.getName())
429 p_bone = pose.bones[name]
431 print 'not found', name
433 if len(ik.children) >= 16:
434 print 'over MAX_CHAINLEN', ik, len(ik.children)
437 ik_solver = p_bone.constraints.append(Blender.Constraint.Type.IKSOLVER)
438 ik_solver[cSetting.CHAINLEN]=len(ik.children)
439 ik_solver[cSetting.TARGET]=armature_object
440 ik_solver[cSetting.USETIP]=False
442 effector_name=englishmap.getEnglishBoneName(
443 l.bones[ik.index].getName())
444 if not effector_name:
445 effector_name=l.bones[ik.index].getName()
447 ik_solver[cSetting.BONE]=effector_name
448 #ik_solver.influence=ik.weight
449 # not used. place folder when export.
450 ik_solver[cSetting.ROTWEIGHT]=ik.weight
451 ik_solver[cSetting.ITERATIONS]=ik.iterations * 10
453 armature.makeEditable()
456 return armature_object
459 def importShape(obj, l, vertex_map):
460 if len(l.morph_list)==0:
463 mesh=obj.getData(mesh=True)
467 for s in l.morph_list:
471 # create vertex group
472 mesh.addVertGroup(MMD_SHAPE_GROUP_NAME)
478 indices.append(vertex_map[i])
479 mesh.assignVertsToGroup(MMD_SHAPE_GROUP_NAME, indices, 0,
480 Blender.Mesh.AssignModes.ADD)
486 assert(len(mesh.key.blocks)==1)
488 baseShapeBlock=mesh.key.blocks[baseShapeIndex]
489 baseShapeBlock.name='Basis'
490 obj.activeShape=baseShapeIndex
497 for s in l.morph_list:
498 if s.name==base.name:
501 for index, offset in zip(s.indices, s.pos_list):
503 vertex_index=vertex_map[base.indices[index]]
504 v=mesh.verts[vertex_index].co
505 offset=convert_coord(offset)
509 except IndexError, msg:
511 print index, len(base.indices), len(vertex_map)
512 print len(mesh.verts)
513 print base.indices[index]
517 #print 'this mesh not has shape vertices'
521 name=englishmap.getEnglishSkinName(s.getName())
523 name=s.getName().encode(INTERNAL_ENCODING)
526 # create shapekey block
528 shapeIndex=len(mesh.key.blocks)-1
529 keyBlock=mesh.key.blocks[shapeIndex]
532 # copy vertex to shape key
536 for mv, v in zip(mesh.verts, baseShapeBlock.getData()):
543 obj.activeShape=baseShapeIndex
549 filename=filename.decode(INTERNAL_ENCODING)
550 tex_dir=os.path.dirname(filename)
553 progress_start('pmd_import')
554 print(INTERNAL_ENCODING, FS_ENCODING)
557 progress_set('load %s' % filename, 0.0)
560 if not l.read(filename):
561 print "fail to load %s" % filename
563 progress_set('loaded %s' % filename, 0.1)
566 mode_edit = Blender.Window.EditMode()
568 Blender.Window.EditMode(0)
570 scene = bpy.data.scenes.active
572 # import objects container
573 root=scene.objects.new("Empty")
575 l.english_name if len(l.english_name)>0 else l.getName().encode(INTERNAL_ENCODING))
578 mesh_objects=importMesh(scene, l, tex_dir)
579 root.makeParent(mesh_objects)
582 armature_object=importArmature(scene, l)
584 armature = armature_object.getData()
585 root.makeParent([armature_object])
587 # add armature modifier
588 for o in mesh_objects:
589 mod=o.modifiers.append(Blender.Modifier.Types.ARMATURE)
590 mod[Blender.Modifier.Settings.OBJECT] = armature_object
591 mod[Blender.Modifier.Settings.ENVELOPES] = False
594 ############################################################
596 ############################################################
597 for n, b in armature_object.getPose().bones.items():
601 if n.startswith("knee_"):
606 b.limitMax=[180, 0, 0]
607 elif n.startswith("ankle_"):
615 Blender.Window.EditMode(1)
618 Blender.Window.RedrawAll()
621 if __name__=="__main__":
622 Blender.Window.FileSelector(
625 Blender.sys.makename(ext='.pmd'))