4 Name: 'MikuMikuDance model (.pmd)...'
7 Tooltip: 'Export PMD file for MikuMikuDance.'
9 __author__= ["ousttrue"]
15 This script exports a pmd model.
17 20100318: first implementation.
18 20100519: refactoring. use C extension.
19 20100530: implement basic features.
20 20100612: integrate 2.4 and 2.5.
21 20100616: implement rigid body.
22 20100619: fix rigid body, bone weight.
23 20100626: refactoring.
25 20100710: toon texture & bone group.
26 20100711: separate vertex with normal or uv.
27 20100724: update for Blender2.53.
28 20100731: add full python module.
29 20101005: update for Blender2.54.
30 20101228: update for Blender2.55.
31 20110429: update for Blender2.57b.
32 20110522: implement RigidBody and Constraint.
33 20111002: update for pymeshio-2.1.0
38 from . import oneskinmesh
39 from .pymeshio import common
40 from .pymeshio import pmd
41 from .pymeshio import englishmap
42 from .pymeshio.pmd import writer
45 def near(x, y, EPSILON=1e-5):
47 return d>=-EPSILON and d<=EPSILON
51 return s.encode('cp932')
54 def write(self, path):
56 model.name=self.name.encode('cp932')
57 model.comment=self.comment.encode('cp932')
60 model.vertices=[pmd.Vertex(
61 # convert right-handed z-up to left-handed y-up
62 common.Vector3(pos[0], pos[2], pos[1]),
63 # convert right-handed z-up to left-handed y-up
64 common.Vector3(attribute.nx, attribute.nz, attribute.ny),
66 common.Vector2(attribute.u, 1.0-attribute.v),
67 self.skeleton.indexByName(b0),
68 self.skeleton.indexByName(b1),
70 # edge flag, 0: enable edge, 1: not edge
73 for pos, attribute, b0, b1, weight in self.oneSkinMesh.vertexArray.zip()]
76 vertexCount=self.oneSkinMesh.getVertexCount()
77 for material_name, indices in self.oneSkinMesh.vertexArray.each():
78 #print('material:', material_name)
80 m=bl.material.get(material_name)
83 def get_texture_name(texture):
84 pos=texture.replace("\\", "/").rfind("/")
88 return texture[pos+1:]
89 textures=[get_texture_name(path)
90 for path in bl.material.eachEnalbeTexturePath(m)]
93 model.materials.append(pmd.Material(
95 common.RGB(m.diffuse_color[0], m.diffuse_color[1], m.diffuse_color[2]),
98 0 if m.specular_toon_size<1e-5 else m.specular_hardness*10,
100 common.RGB(m.specular_color[0], m.specular_color[1], m.specular_color[2]),
102 common.RGB(m.mirror_color[0], m.mirror_color[1], m.mirror_color[2]),
104 1 if m.subsurface_scattering.use else 0,
110 ('*'.join(textures) if len(textures)>0 else "").encode('cp932')
114 assert(i<vertexCount)
115 for i in range(0, len(indices), 3):
117 model.indices.append(indices[i])
118 model.indices.append(indices[i+1])
119 model.indices.append(indices[i+2])
123 for i, b in enumerate(self.skeleton.bones):
126 boneNameMap[b.name]=i
127 v=englishmap.getUnicodeBoneName(b.name)
131 bone=pmd.Bone(v[1].encode('cp932'))
134 bone_english_name=toCP932(b.name)
135 if len(bone_english_name)>=20:
136 print(bone_english_name)
137 #assert(len(bone_english_name)<20)
138 bone.english_name=bone_english_name
143 b.ik_index=self.skeleton.indexByName('eyes')
148 bone.parent_index=b.parent_index
149 bone.tail_index=b.tail_index
150 bone.ik_index=b.ik_index
152 # convert right-handed z-up to left-handed y-up
153 bone.pos.x=b.pos[0] if not near(b.pos[0], 0) else 0
154 bone.pos.y=b.pos[2] if not near(b.pos[2], 0) else 0
155 bone.pos.z=b.pos[1] if not near(b.pos[1], 0) else 0
157 model.bones.append(bone)
160 for ik in self.skeleton.ik_list:
162 solver.index=self.skeleton.getIndex(ik.target)
163 solver.target=self.skeleton.getIndex(ik.effector)
164 solver.length=ik.length
165 b=self.skeleton.bones[ik.effector.parent_index]
166 for i in range(solver.length):
167 solver.children.append(self.skeleton.getIndex(b))
168 b=self.skeleton.bones[b.parent_index]
169 solver.iterations=ik.iterations
170 solver.weight=ik.weight
171 model.ik_list.append(solver)
174 for i, m in enumerate(self.oneSkinMesh.morphList):
175 v=englishmap.getUnicodeSkinName(m.name)
177 v=[m.name, m.name, 0]
180 morph=pmd.Morph(v[1].encode("cp932"))
181 morph.english_name=m.name.encode("cp932")
184 for index, offset in m.offsets:
185 # convert right-handed z-up to left-handed y-up
186 morph.append(index, offset[0], offset[2], offset[1])
187 morph.vertex_count=len(m.offsets)
191 for i, m in enumerate(self.oneSkinMesh.morphList):
193 model.morph_indices.append(i)
194 for i, m in enumerate(self.oneSkinMesh.morphList):
196 model.morph_indices.append(i)
197 for i, m in enumerate(self.oneSkinMesh.morphList):
199 model.morph_indices.append(i)
200 for i, m in enumerate(self.oneSkinMesh.morphList):
202 model.morph_indices.append(i)
205 for g in self.skeleton.bone_groups:
206 name=englishmap.getUnicodeBoneGroupName(g[0])
211 model.bone_group_list.append(pmd.BoneGroup(
212 (name+'\n').encode('cp932'),
213 (englishName+'\n').encode('cp932')
217 for i, b in enumerate(self.skeleton.bones):
222 model.bone_display_list.append((i, self.skeleton.getBoneGroup(b)))
225 model.english_name=self.englishName.encode('cp932')
226 model.english_comment=self.englishComment.encode('cp932')
230 for o in bl.object.each():
232 if o.name.startswith(bl.TOON_TEXTURE_OBJECT):
238 toonMesh=bl.object.getData(toonMeshObject)
239 toonMaterial=bl.mesh.getMaterial(toonMesh, 0)
241 t=bl.material.getTexture(toonMaterial, i)
243 model.toon_textures[i]=("%s" % t.name).encode('cp932')
245 model.toon_textures[i]=("toon%02d.bmp" % (i+1)).encode('cp932')
248 model.toon_textures[i]=("toon%02d.bmp" % (i+1)).encode('cp932')
252 for i, obj in enumerate(self.oneSkinMesh.rigidbodies):
253 name=obj[bl.RIGID_NAME] if bl.RIGID_NAME in obj else obj.name
256 boneIndex=boneNameMap[obj[bl.RIGID_BONE_NAME]]
259 bone=self.skeleton.bones[0]
261 bone=self.skeleton.bones[boneIndex]
262 if obj[bl.RIGID_SHAPE_TYPE]==0:
263 shape_type=pmd.SHAPE_SPHERE
264 shape_size=common.Vector3(obj.scale[0], 0, 0)
265 elif obj[bl.RIGID_SHAPE_TYPE]==1:
266 shape_type=pmd.SHAPE_BOX
267 shape_size=common.Vector3(obj.scale[0], obj.scale[1], obj.scale[2])
268 elif obj[bl.RIGID_SHAPE_TYPE]==2:
269 shape_type=pmd.SHAPE_CAPSULE
270 shape_size=common.Vector3(obj.scale[0], obj.scale[2], 0)
271 rigidBody=pmd.RigidBody(
272 name.encode('cp932'),
273 collision_group=obj[bl.RIGID_GROUP],
274 no_collision_group=obj[bl.RIGID_INTERSECTION_GROUP],
275 bone_index=boneIndex,
276 shape_position=common.Vector3(
277 obj.location.x-bone.pos[0],
278 obj.location.z-bone.pos[2],
279 obj.location.y-bone.pos[1]),
280 shape_rotation=common.Vector3(
281 -obj.rotation_euler[0],
282 -obj.rotation_euler[2],
283 -obj.rotation_euler[1]),
284 shape_type=shape_type,
285 shape_size=shape_size,
286 mass=obj[bl.RIGID_WEIGHT],
287 linear_damping=obj[bl.RIGID_LINEAR_DAMPING],
288 angular_damping=obj[bl.RIGID_ANGULAR_DAMPING],
289 restitution=obj[bl.RIGID_RESTITUTION],
290 friction=obj[bl.RIGID_FRICTION],
291 mode=obj[bl.RIGID_PROCESS_TYPE]
293 model.rigidbodies.append(rigidBody)
296 model.joints=[pmd.Joint(
297 name=obj[bl.CONSTRAINT_NAME].encode('cp932'),
298 rigidbody_index_a=rigidNameMap[obj[bl.CONSTRAINT_A]],
299 rigidbody_index_b=rigidNameMap[obj[bl.CONSTRAINT_B]],
300 position=common.Vector3(
304 rotation=common.Vector3(
305 -obj.rotation_euler[0],
306 -obj.rotation_euler[2],
307 -obj.rotation_euler[1]),
308 translation_limit_min=common.Vector3(
309 obj[bl.CONSTRAINT_POS_MIN][0],
310 obj[bl.CONSTRAINT_POS_MIN][1],
311 obj[bl.CONSTRAINT_POS_MIN][2]
313 translation_limit_max=common.Vector3(
314 obj[bl.CONSTRAINT_POS_MAX][0],
315 obj[bl.CONSTRAINT_POS_MAX][1],
316 obj[bl.CONSTRAINT_POS_MAX][2]
318 rotation_limit_min=common.Vector3(
319 obj[bl.CONSTRAINT_ROT_MIN][0],
320 obj[bl.CONSTRAINT_ROT_MIN][1],
321 obj[bl.CONSTRAINT_ROT_MIN][2]),
322 rotation_limit_max=common.Vector3(
323 obj[bl.CONSTRAINT_ROT_MAX][0],
324 obj[bl.CONSTRAINT_ROT_MAX][1],
325 obj[bl.CONSTRAINT_ROT_MAX][2]),
326 spring_constant_translation=common.Vector3(
327 obj[bl.CONSTRAINT_SPRING_POS][0],
328 obj[bl.CONSTRAINT_SPRING_POS][1],
329 obj[bl.CONSTRAINT_SPRING_POS][2]),
330 spring_constant_rotation=common.Vector3(
331 obj[bl.CONSTRAINT_SPRING_ROT][0],
332 obj[bl.CONSTRAINT_SPRING_ROT][1],
333 obj[bl.CONSTRAINT_SPRING_ROT][2])
335 for obj in self.oneSkinMesh.constraints]
338 bl.message('write: %s' % path)
339 return writer.write(io.open(path, 'wb'), model)
342 def _execute(filepath=''):
343 active=bl.object.getActive()
345 print("abort. no active object.")
348 exporter=oneskinmesh.Exporter()
352 write(exporter, filepath)
353 bl.object.activate(active)