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.
22 from Blender import Mathutils
29 from meshio import pmd, englishmap
32 if os.path.exists(os.path.dirname(sys.argv[0])+"/utf8"):
35 FS_ENCODING=sys.getfilesystemencoding()
39 def cp932_to_utf8(cp932):
40 return str(cp932).decode('cp932').encode('utf-8')
43 class ProgressBar(object):
44 def __init__(self, base):
45 print "#### %s ####" % base
47 self.start=Blender.sys.time()
48 self.set('<start>', 0)
50 def advance(self, message, progress):
51 self.progress+=float(progress)
54 def set(self, message, progress):
55 self.progress=float(progress)
58 def _print(self, message):
60 message="%s: %s" % (self.base, message)
61 if message.__class__ is unicode:
62 message=message.encode('utf-8')
63 Blender.Window.DrawProgressBar(self.progress, message)
67 message='finished in %.2f sec' % (Blender.sys.time()-self.start)
68 self.set(message, 1.0)
70 def progress_start(base):
72 progressBar=ProgressBar(base)
74 def progress_finish():
78 def progress_print(message, progress=0.05):
80 progressBar.advance(message, progress)
82 def progress_set(message, progress):
84 progressBar.set(message, progress)
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 get_bone_name(l, index):
99 name=englishmap.getEnglishBoneName(l.bones[index].name.decode('cp932'))
100 return name if name else cp932_to_utf8(l.bones[index].name)
103 def createMaterial():
105 create default materil
107 material=Blender.Material.New()
108 material.setDiffuseShader(Blender.Material.Shaders.DIFFUSE_TOON)
110 material.diffuseSize = 3.14/2
111 material.setDiffuseSmooth(0)
112 material.setSpecShader(Blender.Material.Shaders.SPEC_TOON)
113 material.setSpecSize(0)
118 def importMesh(scene, l, tex_dir):
120 @param l[in] mmd.PMDLoader
124 ############################################################
125 # shpaeキーで使われるマテリアル優先的に前に並べる
126 ############################################################
127 # shapeキーで使われる頂点インデックスを集める
128 shape_key_used_vertices=set()
129 if len(l.morph_list)>0:
132 for s in l.morph_list:
139 for index in base.indices:
140 shape_key_used_vertices.add(index)
142 # マテリアルに含まれる頂点がshape_keyに含まれるか否か?
143 def isMaterialUsedInShape(offset, m):
144 for i in xrange(offset, offset+m.vertex_count):
145 if l.indices[i] in shape_key_used_vertices:
148 # shapeキーで使われるマテリアルを記録する
149 shape_key_materials=set()
150 # 各マテリアルの開始頂点インデックスを記録する
153 for i, m in enumerate(l.materials):
154 face_map[i]=face_count
155 if isMaterialUsedInShape(face_count, m):
156 shape_key_materials.add(i)
157 face_count+=m.vertex_count
159 # shapeキーで使われるマテリアルを前に並べるインデックスマップを作る
162 not_used_index=len(shape_key_materials)
163 for i, m in enumerate(l.materials):
164 if i in shape_key_materials:
165 material_map[i]=used_index
168 material_map[i]=not_used_index
171 # マテリアル16個ごとに分割したメッシュを作成する
174 while material_index<len(l.materials):
176 mesh = Blender.Mesh.New()
179 obj = scene.objects.new(mesh)
181 mesh_objects.append(obj)
183 # shapeキーで使われる順に並べなおしたマテリアル16個分の
185 vertex_map=import16MaerialAndMesh(
187 material_index, material_map, face_map, tex_dir)
190 importShape(mesh, l, vertex_map)
198 def import16MaerialAndMesh(mesh, l,
199 material_offset, material_map, face_map, tex_dir):
200 ############################################################
202 ############################################################
203 progress_print('create materials')
207 for i in xrange(material_offset, material_offset+16):
209 material_index=material_map[i]
210 m=l.materials[material_index]
211 mesh_material_map[material_index]=index
215 material=createMaterial()
216 material.setRGBCol([m.diffuse.r, m.diffuse.g, m.diffuse.b])
217 material.setAlpha(m.diffuse.a)
218 material.setHardness(int(m.shinness))
219 material.setSpecCol([m.specular.r, m.specular.g, m.specular.b])
220 material.setMirCol([m.ambient.r, m.ambient.g, m.ambient.b])
223 if not tex_dir.endswith("\\") and not tex_dir.endswith("/"):
225 tex_path = tex_dir+m.texture
226 tex = Blender.Texture.New()
229 tex.image = Blender.Image.Load(cp932_to_utf8(tex_path))
230 material.setTexture(0, tex)
231 material.getTextures()[0].texco = Blender.Texture.TexCo.UV
233 print material.name, "fail to load", tex_path
234 materials.append(material)
235 # lookup table for assign
237 mesh.materials=materials
239 ############################################################
241 ############################################################
242 progress_print('create vertices')
247 vertices.append(convert_coord(v.pos))
248 vertex_groups[v.bone0]=True
249 vertex_groups[v.bone1]=True
250 mesh.verts.extend(vertices)
252 # create vertex group
253 for i in vertex_groups.keys():
254 mesh.addVertGroup(get_bone_name(l, i))
257 for i, v, mvert in zip(xrange(len(l.vertices)), l.vertices, mesh.verts):
258 mvert.no=Mathutils.Vector(*convert_coord(v.normal))
259 mvert.uvco=convert_uv(v.uv)
260 w1=float(v.weight0)/100.0
262 mesh.assignVertsToGroup(get_bone_name(l, v.bone0), [i], w1,
263 Blender.Mesh.AssignModes.ADD)
264 mesh.assignVertsToGroup(get_bone_name(l, v.bone1), [i], w2,
265 Blender.Mesh.AssignModes.ADD)
267 ############################################################
269 ############################################################
270 progress_print('create faces')
273 mesh_face_materials=[]
275 for i in xrange(material_offset, material_offset+16):
277 material_index=material_map[i]
280 face_offset=face_map[material_index]
281 m=l.materials[material_index]
282 material_faces=l.indices[face_offset:face_offset+m.vertex_count]
283 for j in xrange(0, len(material_faces), 3):
285 i1=material_faces[j+1]
286 i2=material_faces[j+2]
287 mesh_face_indices.append([i0, i1, i2])
288 mesh_face_materials.append(material_index)
289 used_vertices.add(i0)
290 used_vertices.add(i1)
291 used_vertices.add(i2)
292 mesh.faces.extend(mesh_face_indices, ignoreDups=True)
293 assert(len(mesh.faces)==len(mesh_face_indices))
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.vertices):
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 build_bone(armature, b, parent=None):
338 bone = Blender.Armature.Editbone()
339 name=englishmap.getEnglishBoneName(b.name.decode('cp932'))
340 bone.name = name if name else cp932_to_utf8(b.name)
341 armature.bones[bone.name] = bone
343 bone.head = Mathutils.Vector(*convert_coord(b.pos))
346 if parent.tail==bone.head:
347 options.append(Blender.Armature.CONNECTED)
349 bone.tail = Mathutils.Vector(*convert_coord(b.tail))
350 if bone.head==bone.tail:
351 bone.tail=bone.head-Mathutils.Vector(0, 1, 0)
352 elif b.__class__ is pmd.BONE_IK:
353 bone.head = Mathutils.Vector(*convert_coord(b.pos))
354 bone.tail = Mathutils.Vector(*convert_coord(b.tail))
357 tail=Mathutils.Vector(*convert_coord(b.pos))
359 bone.head = tail-Mathutils.Vector(0, 1, 0)
361 for child in b.children:
362 build_bone(armature, child, bone)
365 def importArmature(scene, l):
367 armature = Blender.Armature.New()
369 armature_object = scene.objects.new(armature)
371 act = Blender.Armature.NLA.NewAction()
372 act.setActive(armature_object)
374 armature_object.drawMode = (
375 armature_object.drawMode | Blender.Object.DrawModes.XRAY)
377 armature.drawType = Blender.Armature.OCTAHEDRON
378 armature.envelopes = False
379 armature.vertexGroups = True
380 armature.mirrorEdit = True
383 armature.makeEditable()
386 build_bone(armature, b)
389 ############################################################
391 ############################################################
392 pose = armature_object.getPose()
393 cSetting = Blender.Constraint.Settings
395 # IKtarget->parent(=IK).name
396 target=l.bones[ik.target]
397 parent=l.bones[target.parent_index]
398 name = englishmap.getEnglishBoneName(parent.name.decode('cp932'))
399 p_bone = pose.bones[name]
401 print 'not found', name
403 if len(ik.children) >= 16:
404 print 'over MAX_CHAINLEN', ik, len(ik.children)
406 ik_const = p_bone.constraints.append(Blender.Constraint.Type.IKSOLVER)
407 ik_const[cSetting.CHAINLEN] = len(ik.children)
408 ik_const[cSetting.TARGET] = armature_object
409 ik_const[cSetting.BONE] = englishmap.getEnglishBoneName(
410 l.bones[ik.index].name.decode('cp932'))
411 lrot_const = p_bone.constraints.append(Blender.Constraint.Type.LIMITROT)
412 lrot_const.influence = ik.weight
413 lrot_const[cSetting.OWNERSPACE] = cSetting.SPACE_LOCAL
414 lrot_const[cSetting.LIMIT] = (cSetting.LIMIT_XROT | cSetting.LIMIT_ZROT)
415 lrot_const[cSetting.XMIN] = ik.iterations
416 lrot_const[cSetting.XMAX] = 180
417 lrot_const[cSetting.ZMIN] = 180 - ik.iterations
418 lrot_const[cSetting.ZMAX] = 0
420 armature.makeEditable()
423 return armature_object
426 def importShape(mesh, l, vertex_map):
427 if len(l.morph_list)==0:
431 for s in l.morph_list:
438 baseblock=mesh.key.blocks[-1]
439 ipo=Blender.Ipo.New('Key', 'pmd')
443 for s in l.morph_list:
444 if s.name==base.name:
446 name=englishmap.getEnglishSkinName(s.name.decode('cp932'))
448 name=cp932_to_utf8(s.name)
449 for index, offset in zip(s.indices, s.pos_list):
451 vertex_index=vertex_map[base.indices[index]]
452 v=mesh.verts[vertex_index].co
453 offset=convert_coord(offset)
457 except IndexError, msg:
458 print IndexError, msg
459 print index, len(base.indices)
460 print vertex_index, len(mesh.verts)
461 print base.indices[index]
464 #print 'this mesh not has shape vertices'
470 mesh.key.blocks[-1].name=name
473 icu=ipo.addCurve(name)
474 icu.interpolation = Blender.IpoCurve.InterpTypes.LINEAR
475 icu.append( (0.0, 0.0) )
478 for mv, v in zip(mesh.verts, baseblock.getData()):
489 filename=filename.decode(FS_ENCODING)
490 tex_dir=os.path.dirname(filename)
493 progress_start('pmd_import')
496 progress_set('load %s' % filename, 0.0)
499 locale.setlocale(locale.LC_ALL, '')
502 if not l.read(filename.encode('cp932')):
503 print "fail to load %s" % filename
505 progress_set('loaded %s' % filename, 0.1)
508 mode_edit = Blender.Window.EditMode()
510 Blender.Window.EditMode(0)
512 scene = bpy.data.scenes.active
514 # import objects container
515 root=scene.objects.new("Empty")
516 root.setName(l.english_model_name)
519 mesh_objects=importMesh(scene, l, tex_dir)
520 root.makeParent(mesh_objects)
523 armature_object=importArmature(scene, l)
525 armature = armature_object.getData()
526 armature.drawNames=True
527 root.makeParent([armature_object])
529 # add armature modifier
530 for o in mesh_objects:
531 mod=o.modifiers.append(Blender.Modifier.Types.ARMATURE)
532 mod[Blender.Modifier.Settings.OBJECT] = armature_object
533 mod[Blender.Modifier.Settings.ENVELOPES] = False
541 Blender.Window.EditMode(1)
544 Blender.Window.RedrawAll()
547 if __name__=="__main__":
548 Blender.Window.FileSelector(
551 Blender.sys.makename(ext='.pmd'))