OSDN Git Service

refactoring export_pmd
[meshio/pymeshio.git] / blender26-meshio / export_pmd.py
1 #!BPY
2 # coding: utf-8
3 """
4  Name: 'MikuMikuDance model (.pmd)...'
5  Blender: 248
6  Group: 'Export'
7  Tooltip: 'Export PMD file for MikuMikuDance.'
8 """
9 __author__= ["ousttrue"]
10 __version__= "2.5"
11 __url__=()
12 __bpydoc__="""
13 pmd Importer
14
15 This script exports a pmd model.
16
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.
24 20100629: sphere map.
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
34 """
35 import io
36
37 from . import bl
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
43
44
45 def near(x, y, EPSILON=1e-5):
46     d=x-y
47     return d>=-EPSILON and d<=EPSILON
48
49
50 def toCP932(s):
51     return s.encode('cp932')
52
53
54 def write(self, path):
55     model=pmd.Model(1.0)
56     model.name=self.name.encode('cp932')
57     model.comment=self.comment.encode('cp932')
58
59     # 頂点
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),
65         # reverse vertical
66         common.Vector2(attribute.u, 1.0-attribute.v),
67         self.skeleton.indexByName(b0),
68         self.skeleton.indexByName(b1),
69         int(100*weight),
70         # edge flag, 0: enable edge, 1: not edge
71         0 
72         )
73         for pos, attribute, b0, b1, weight in self.oneSkinMesh.vertexArray.zip()]
74
75     # 面とマテリアル
76     vertexCount=self.oneSkinMesh.getVertexCount()
77     for material_name, indices in self.oneSkinMesh.vertexArray.each():
78         #print('material:', material_name)
79         try:
80             m=bl.material.get(material_name)
81         except KeyError as e:
82             m=DefaultMatrial()
83         def get_texture_name(texture):
84             pos=texture.replace("\\", "/").rfind("/")
85             if pos==-1:
86                 return texture
87             else:
88                 return texture[pos+1:]
89         textures=[get_texture_name(path)
90             for path in bl.material.eachEnalbeTexturePath(m)]
91         print(textures)
92         # マテリアル
93         model.materials.append(pmd.Material(
94                 # diffuse_color
95                 common.RGB(m.diffuse_color[0], m.diffuse_color[1], m.diffuse_color[2]),
96                 m.alpha,
97                 # specular_factor
98                 0 if m.specular_toon_size<1e-5 else m.specular_hardness*10,
99                 # specular_color
100                 common.RGB(m.specular_color[0], m.specular_color[1], m.specular_color[2]),
101                 # ambient_color
102                 common.RGB(m.mirror_color[0], m.mirror_color[1], m.mirror_color[2]),
103                 # flag
104                 1 if m.subsurface_scattering.use else 0,
105                 # toon
106                 0,
107                 # vertex_count
108                 len(indices),
109                 # texture
110                 ('*'.join(textures) if len(textures)>0 else "").encode('cp932')
111                 ))
112         # 面
113         for i in indices:
114             assert(i<vertexCount)
115         for i in range(0, len(indices), 3):
116             # reverse triangle
117             model.indices.append(indices[i])
118             model.indices.append(indices[i+1])
119             model.indices.append(indices[i+2])
120
121     # bones
122     boneNameMap={}
123     for i, b in enumerate(self.skeleton.bones):
124
125         # name
126         boneNameMap[b.name]=i
127         v=englishmap.getUnicodeBoneName(b.name)
128         if not v:
129             v=[b.name, b.name]
130         assert(v)
131         bone=pmd.Bone(v[1].encode('cp932'))
132
133         # english name
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
139
140         if len(v)>=3:
141             # has type
142             if v[2]==5:
143                 b.ik_index=self.skeleton.indexByName('eyes')
144             bone.type=v[2]
145         else:
146             bone.type=b.type
147
148         bone.parent_index=b.parent_index
149         bone.tail_index=b.tail_index
150         bone.ik_index=b.ik_index
151
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
156         
157         model.bones.append(bone)
158
159     # IK
160     for ik in self.skeleton.ik_list:
161         solver=pmd.IK()
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)
172
173     # 表情
174     for i, m in enumerate(self.oneSkinMesh.morphList):
175         v=englishmap.getUnicodeSkinName(m.name)
176         if not v:
177             v=[m.name, m.name, 0]
178         assert(v)
179         # morph
180         morph=pmd.Morph(v[1].encode("cp932"))
181         morph.english_name=m.name.encode("cp932")
182         m.type=v[2]
183         morph.type=v[2]
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)
188
189     # 表情枠
190     # type==0はbase
191     for i, m in enumerate(self.oneSkinMesh.morphList):
192         if m.type==3:
193             model.morph_indices.append(i)
194     for i, m in enumerate(self.oneSkinMesh.morphList):
195         if m.type==2:
196             model.morph_indices.append(i)
197     for i, m in enumerate(self.oneSkinMesh.morphList):
198         if m.type==1:
199             model.morph_indices.append(i)
200     for i, m in enumerate(self.oneSkinMesh.morphList):
201         if m.type==4:
202             model.morph_indices.append(i)
203
204     # ボーングループ
205     for g in self.skeleton.bone_groups:
206         name=englishmap.getUnicodeBoneGroupName(g[0])
207         if not name:
208             name=g[0]
209         englishName=g[0]
210
211         model.bone_group_list.append(pmd.BoneGroup(
212                 (name+'\n').encode('cp932'),
213                 (englishName+'\n').encode('cp932')
214                 ))
215
216     # ボーングループメンバー
217     for i, b in enumerate(self.skeleton.bones):
218         if i==0:
219            continue
220         if b.type in [6, 7]:
221            continue
222         model.bone_display_list.append((i, self.skeleton.getBoneGroup(b)))
223
224     # English
225     model.english_name=self.englishName.encode('cp932')
226     model.english_comment=self.englishComment.encode('cp932')
227
228     # toon
229     toonMeshObject=None
230     for o in bl.object.each():
231         try:
232             if o.name.startswith(bl.TOON_TEXTURE_OBJECT):
233                 toonMeshObject=o
234         except:
235             p(o.name)
236         break
237     if toonMeshObject:
238         toonMesh=bl.object.getData(toonMeshObject)
239         toonMaterial=bl.mesh.getMaterial(toonMesh, 0)
240         for i in range(10):
241             t=bl.material.getTexture(toonMaterial, i)
242             if t:
243                 model.toon_textures[i]=("%s" % t.name).encode('cp932')
244             else:
245                 model.toon_textures[i]=("toon%02d.bmp" % (i+1)).encode('cp932')
246     else:
247         for i in range(10):
248             model.toon_textures[i]=("toon%02d.bmp" % (i+1)).encode('cp932')
249
250     # rigid body
251     rigidNameMap={}
252     for i, obj in enumerate(self.oneSkinMesh.rigidbodies):
253         name=obj[bl.RIGID_NAME] if bl.RIGID_NAME in obj else obj.name
254         print(name)
255         rigidNameMap[name]=i
256         boneIndex=boneNameMap[obj[bl.RIGID_BONE_NAME]]
257         if boneIndex==0:
258             boneIndex=-1
259             bone=self.skeleton.bones[0]
260         else:
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]
292                 )
293         model.rigidbodies.append(rigidBody)
294
295     # constraint
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(
301             obj.location[0], 
302             obj.location[2], 
303             obj.location[1]),
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]
312             ),
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]
317             ),
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])
334         )
335         for obj in self.oneSkinMesh.constraints]
336
337     # 書き込み
338     bl.message('write: %s' % path)
339     return writer.write(io.open(path, 'wb'), model)
340
341
342 def _execute(filepath=''):
343     active=bl.object.getActive()
344     if not active:
345         print("abort. no active object.")
346         return
347
348     exporter=oneskinmesh.Exporter()
349     exporter.setup()
350     print(exporter)
351
352     write(exporter, filepath)
353     bl.object.activate(active)
354