4 Name: 'MikuMikuDance model (.pmd)...'
7 Tooltip: 'Export PMD file for MikuMikuDance.'
9 __author__= ["ousttrue"]
13 0.1: 20100318 first implementation.
20 if os.path.exists(os.path.dirname(sys.argv[0])+"/utf8"):
23 FS_ENCODING=sys.getfilesystemencoding()
29 ###############################################################################
30 # outlineの木構造からエクスポートするオブジェクトを選び出す。
31 # (Blender.Objectがparentしか持たず下っていくことができないため
33 ###############################################################################
35 __slots__=['name', 'children']
36 def __init__(self, name="__scene__"):
40 def addChild(self, child):
41 self.children.append(child)
43 def printTree(self, level=0):
44 print " " * (level*2) + self.name
45 for child in self.children:
46 child.printTree(level+1)
49 class OutlineTree(object):
50 __slots__=["root", "dict"]
55 def addObject(self, object):
57 if object.name in self.dict:
60 parent=object.getParent()
61 node=Node(object.name)
63 if not parent.name in self.dict:
64 addNode(self.dict, parent)
65 self.dict[parent.name].addChild(node)
67 self.root.addChild(node)
68 self.dict[object.name]=node
72 return self.dict[name]
77 ###############################################################################
78 # Blenderのメッシュをワンスキンメッシュ化する
79 ###############################################################################
80 def near(x, y, EPSILON=1e-5):
82 return d>=-EPSILON and d<=EPSILON
84 class VertexKey(object):
90 'nx', 'ny', 'nz', # 法線
94 def __init__(self, x, y, z, nx, ny, nz, u, v):
105 return int((self.x+self.y+self.z+self.nx+self.ny+self.nz+self.u+self.v)*100)
107 def __eq__(self, rhs):
108 return near(self.x, rhs.x) and near(self.y, rhs.y) and near(self.z, rhs.z) and near(self.nx, rhs.nx) and near(self.ny, rhs.ny) and near(self.nz, rhs.nz) and near(self.u, rhs.u) and near(self.v, rhs.v)
111 class VertexArray(object):
116 # マテリアル毎に分割したインデックス配列
130 return "<VertexArray %d vertices, %d indexArrays>" % (
131 len(self.vertices), len(self.indexArrays))
133 def getIndex(self, pos, normal, uv, b0, b1, weight0):
138 pos[0], pos[1], pos[2],
139 normal[0], normal[1], normal[2],
141 if not key in self.vertexMap:
142 self.vertexMap[key]=len(self.vertices)
143 self.vertices.append((pos.x, pos.y, pos.z))
144 self.normals.append((normal.x, normal.y, normal.z))
145 self.uvs.append((uv.x, uv.y))
149 self.weight.append(weight0)
150 return self.vertexMap[key]
152 def addTriangle(self,
159 weight0, weight1, weight2
161 if not material in self.indexArrays:
162 self.indexArrays[material]=[]
164 index0=self.getIndex(pos0, n0, uv0, b0_0, b1_0, weight0)
165 index1=self.getIndex(pos1, n1, uv1, b0_1, b1_1, weight1)
166 index2=self.getIndex(pos2, n2, uv2, b0_2, b1_2, weight2)
168 self.indexArrays[material]+=[index0, index1, index2]
171 class OneSkinMesh(object):
172 __slots__=['armatureObj', 'vertexArray']
174 self.armatureObj=None
175 self.vertexArray=VertexArray()
177 def create(self, node):
178 obj=Blender.Object.Get(node.name)
184 for child in node.children:
187 def addMesh(self, obj):
188 if obj.restrictDisplay:
191 if len(obj.getData(mesh=True).verts)==0:
194 print("export", obj.name)
197 for m in obj.modifiers:
198 if m.name=="Armature":
199 armatureObj=m[Blender.Modifier.Settings.OBJECT]
200 if not self.armatureObj:
201 self.armatureObj=armatureObj
202 elif self.armatureObj!=armatureObj:
203 print "warning! found multiple armature. ignored.", armatureObj.name
206 mesh=obj.getData(mesh=True)
209 for name in mesh.getVertGroupNames():
210 for i, w in mesh.getVertsFromGroup(name, 1):
213 if i in secondWeightMap:
215 if w<secondWeightMap[i]:
219 secondWeightMap[i]=(name, w)
222 weightMap[i]=(name, w)
224 if w>weightMap[i][1]:
226 secondWeightMap[i]=weightMap[i]
227 weightMap[i]=(name, w)
229 secondWeightMap[i]=(name, w)
231 weightMap[i]=(name, w)
234 for i in xrange(len(mesh.verts)):
235 #for i, name_weight in weightMap.items():
236 if i in secondWeightMap:
237 secondWeightMap[i]=(secondWeightMap[i][0], 1.0-weightMap[i][1])
239 weightMap[i]=(weightMap[i][0], 1.0)
240 secondWeightMap[i]=("", 0)
242 print "no weight vertex"
244 secondWeightMap[i]=("", 0)
247 ############################################################
250 ############################################################
251 mesh=Blender.Mesh.New()
252 # not applied modifiers
253 mesh.getFromObject(obj.name, 1)
254 # apply object transform
255 mesh.transform(obj.getMatrix())
256 if len(mesh.verts)==0:
259 for face in mesh.faces:
260 faceVertexCount=len(face.v)
261 material=mesh.materials[face.mat]
262 if faceVertexCount==3:
267 self.vertexArray.addTriangle(
271 face.uv[0], face.uv[1], face.uv[2],
272 weightMap[v0.index][0],
273 weightMap[v1.index][0],
274 weightMap[v2.index][0],
275 secondWeightMap[v0.index][0],
276 secondWeightMap[v1.index][0],
277 secondWeightMap[v2.index][0],
278 weightMap[v0.index][1],
279 weightMap[v1.index][1],
280 weightMap[v2.index][1]
282 elif faceVertexCount==4:
288 self.vertexArray.addTriangle(
292 face.uv[0], face.uv[1], face.uv[2],
293 weightMap[v0.index][0],
294 weightMap[v1.index][0],
295 weightMap[v2.index][0],
296 secondWeightMap[v0.index][0],
297 secondWeightMap[v1.index][0],
298 secondWeightMap[v2.index][0],
299 weightMap[v0.index][1],
300 weightMap[v1.index][1],
301 weightMap[v2.index][1]
303 self.vertexArray.addTriangle(
307 face.uv[2], face.uv[3], face.uv[0],
308 weightMap[v2.index][0],
309 weightMap[v3.index][0],
310 weightMap[v0.index][0],
311 secondWeightMap[v2.index][0],
312 secondWeightMap[v3.index][0],
313 secondWeightMap[v0.index][0],
314 weightMap[v2.index][1],
315 weightMap[v3.index][1],
316 weightMap[v0.index][1]
320 class BoneBuilder(object):
321 __slots__=['bones', 'boneMap']
326 def build(self, armatureObj):
328 armature=armatureObj.getData()
329 for b in armature.bones.values():
332 bone=mmd.createBone(b.name, 0)
334 self.getBone(bone, b)
336 def getBone(self, parent, b):
337 if len(b.children)==0:
340 for i, c in enumerate(b.children):
341 bone=mmd.createBone(c.name, 0)
344 bone.parentIndex=parent.index
346 parent.tailIndex=bone.index
347 pos=c.head['ARMATURESPACE']
348 # convert to right-handed z-up to len-handed y-up
349 bone.pos=(pos.x, pos.z, pos.y)
350 self.getBone(bone, c)
352 def addBone(self, bone):
353 bone.index=len(self.bones)
354 self.bones.append(bone)
355 self.boneMap[bone.name]=bone.index
358 ###############################################################################
360 ###############################################################################
361 def export_pmd(filename):
362 filename=filename.decode(FS_ENCODING)
363 if not filename.lower().endswith(FILE_EXT):
365 print "## pmd exporter ##", filename
367 Blender.Window.WaitCursor(1)
368 t = Blender.sys.time()
371 scene=Blender.Scene.GetCurrent()
373 for object in scene.objects:
374 tree.addObject(object)
378 mesh.create(tree.get(scene.objects.active.name))
382 pmd.model_name="blender export"
383 pmd.comment="blender export"
386 builder=BoneBuilder()
387 builder.build(mesh.armatureObj)
388 for b in builder.bones:
392 vArray=mesh.vertexArray
393 for pos, normal, uv, b0, b1, weight in zip(
394 vArray.vertices, vArray.normals, vArray.uvs,
395 vArray.b0, vArray.b1, vArray.weight):
396 # convert right-handed z-up to left-handed y-up
398 pos[0], pos[2], pos[1],
399 normal[0], normal[2], normal[1],
401 builder.boneMap[b0] if b0 in builder.boneMap else 0,
402 builder.boneMap[b1] if b1 in builder.boneMap else 0,
404 0 # edge flag, 0: enable edge, 1: not edge
406 pmd.vertices.append(v)
408 for m, indices in vArray.indexArrays.items():
409 material=Blender.Material.Get(m)
411 material=mmd.Material(
412 material.R, material.G, material.B, material.alpha,
413 material.spec, material.specR, material.specG, material.specB,
415 #material.amb, material.amb, material.amb
417 material.vertex_count=len(indices)
418 pmd.materials.append(material)
420 for i in xrange(0, len(indices), 3):
421 #pmd.faces.append(mmd.Face(indices[i], indices[i+1], indices[i+2]))
422 pmd.faces.append(mmd.Face(indices[i+2], indices[i+1], indices[i]))
425 if not pmd.write(filename):
429 print 'My Script finished in %.2f seconds' % (Blender.sys.time()-t)
431 Blender.Window.WaitCursor(0)
435 if __name__=="__main__":
436 Blender.Window.FileSelector(
438 'Export Metasequoia PMD',
439 Blender.sys.makename(ext=FILE_EXT))