OSDN Git Service

fix blender plugin packaging
[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 exporter
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()
56
57     o=self.root.o
58     englishName=o.name
59     name=o[bl.MMD_MB_NAME] if bl.MMD_MB_NAME in o else 'Blenderエクスポート'
60     comment=o[bl.MMD_MB_COMMENT] if bl.MMD_MB_COMMENT in o else 'Blnderエクスポート\n'
61     englishComment=o[bl.MMD_COMMENT] if bl.MMD_COMMENT in o else 'blender export\n'
62
63     model.name=name.encode('cp932')
64     model.english_name=englishName.encode('cp932')
65     model.comment=comment.encode('cp932')
66     model.english_comment=englishComment.encode('cp932')
67
68     # 頂点
69     model.vertices=[pmd.Vertex(
70         # convert right-handed z-up to left-handed y-up
71         common.Vector3(pos[0], pos[2], pos[1]), 
72         # convert right-handed z-up to left-handed y-up
73         common.Vector3(attribute.nx, attribute.nz, attribute.ny),
74         # reverse vertical
75         common.Vector2(attribute.u, 1.0-attribute.v),
76         self.skeleton.indexByName(b0),
77         self.skeleton.indexByName(b1),
78         int(100*weight),
79         # edge flag, 0: enable edge, 1: not edge
80         0 
81         )
82         for pos, attribute, b0, b1, weight in self.oneSkinMesh.vertexArray.zip()]
83
84     # 面とマテリアル
85     vertexCount=self.oneSkinMesh.getVertexCount()
86     for material_name, indices in self.oneSkinMesh.vertexArray.each():
87         #print('material:', material_name)
88         try:
89             m=bl.material.get(material_name)
90         except KeyError as e:
91             m=DefaultMatrial()
92         def get_texture_name(texture):
93             pos=texture.replace("\\", "/").rfind("/")
94             if pos==-1:
95                 return texture
96             else:
97                 return texture[pos+1:]
98         textures=[get_texture_name(path)
99             for path in bl.material.eachEnalbeTexturePath(m)]
100         print(textures)
101         # マテリアル
102         model.materials.append(pmd.Material(
103                 # diffuse_color
104                 common.RGB(m.diffuse_color[0], m.diffuse_color[1], m.diffuse_color[2]),
105                 m.alpha,
106                 # specular_factor
107                 0 if m.specular_toon_size<1e-5 else m.specular_hardness*10,
108                 # specular_color
109                 common.RGB(m.specular_color[0], m.specular_color[1], m.specular_color[2]),
110                 # ambient_color
111                 common.RGB(m.mirror_color[0], m.mirror_color[1], m.mirror_color[2]),
112                 # flag
113                 1 if m.subsurface_scattering.use else 0,
114                 # toon
115                 0,
116                 # vertex_count
117                 len(indices),
118                 # texture
119                 ('*'.join(textures) if len(textures)>0 else "").encode('cp932')
120                 ))
121         # 面
122         for i in indices:
123             assert(i<vertexCount)
124         for i in range(0, len(indices), 3):
125             # reverse triangle
126             model.indices.append(indices[i])
127             model.indices.append(indices[i+1])
128             model.indices.append(indices[i+2])
129
130     # bones
131     boneNameMap={}
132     for i, b in enumerate(self.skeleton.bones):
133
134         # name
135         boneNameMap[b.name]=i
136         v=englishmap.getUnicodeBoneName(b.name)
137         if not v:
138             v=[b.name, b.name]
139         assert(v)
140         bone=pmd.Bone(v[1].encode('cp932'))
141
142         # english name
143         bone_english_name=toCP932(b.name)
144         if len(bone_english_name)>=20:
145             print(bone_english_name)
146             #assert(len(bone_english_name)<20)
147         bone.english_name=bone_english_name
148
149         if len(v)>=3:
150             # has type
151             if v[2]==5:
152                 b.ik_index=self.skeleton.indexByName('eyes')
153             bone.type=v[2]
154         else:
155             bone.type=b.type
156
157         bone.parent_index=b.parent_index
158         bone.tail_index=b.tail_index
159         bone.ik_index=b.ik_index
160
161         # convert right-handed z-up to left-handed y-up
162         bone.pos.x=b.pos[0] if not near(b.pos[0], 0) else 0
163         bone.pos.y=b.pos[2] if not near(b.pos[2], 0) else 0
164         bone.pos.z=b.pos[1] if not near(b.pos[1], 0) else 0
165         
166         model.bones.append(bone)
167
168     # IK
169     for ik in self.skeleton.ik_list:
170         solver=pmd.IK()
171         solver.index=self.skeleton.getIndex(ik.target)
172         solver.target=self.skeleton.getIndex(ik.effector)
173         solver.length=ik.length
174         b=self.skeleton.bones[ik.effector.parent_index]
175         for i in range(solver.length):
176             solver.children.append(self.skeleton.getIndex(b))
177             b=self.skeleton.bones[b.parent_index]
178         solver.iterations=ik.iterations
179         solver.weight=ik.weight
180         model.ik_list.append(solver)
181
182     # 表情
183     for i, m in enumerate(self.oneSkinMesh.morphList):
184         v=englishmap.getUnicodeSkinName(m.name)
185         if not v:
186             v=[m.name, m.name, 0]
187         assert(v)
188         # morph
189         morph=pmd.Morph(v[1].encode("cp932"))
190         morph.english_name=m.name.encode("cp932")
191         m.type=v[2]
192         morph.type=v[2]
193         for index, offset in m.offsets:
194             # convert right-handed z-up to left-handed y-up
195             morph.append(index, offset[0], offset[2], offset[1])
196         morph.vertex_count=len(m.offsets)
197
198     # 表情枠
199     # type==0はbase
200     for i, m in enumerate(self.oneSkinMesh.morphList):
201         if m.type==3:
202             model.morph_indices.append(i)
203     for i, m in enumerate(self.oneSkinMesh.morphList):
204         if m.type==2:
205             model.morph_indices.append(i)
206     for i, m in enumerate(self.oneSkinMesh.morphList):
207         if m.type==1:
208             model.morph_indices.append(i)
209     for i, m in enumerate(self.oneSkinMesh.morphList):
210         if m.type==4:
211             model.morph_indices.append(i)
212
213     # ボーングループ
214     for g in self.skeleton.bone_groups:
215         name=englishmap.getUnicodeBoneGroupName(g[0])
216         if not name:
217             name=g[0]
218         englishName=g[0]
219
220         model.bone_group_list.append(pmd.BoneGroup(
221                 (name+'\n').encode('cp932'),
222                 (englishName+'\n').encode('cp932')
223                 ))
224
225     # ボーングループメンバー
226     for i, b in enumerate(self.skeleton.bones):
227         if i==0:
228            continue
229         if b.type in [6, 7]:
230            continue
231         model.bone_display_list.append((i, self.skeleton.getBoneGroup(b)))
232
233     # toon
234     toonMeshObject=None
235     for o in bl.object.each():
236         try:
237             if o.name.startswith(bl.TOON_TEXTURE_OBJECT):
238                 toonMeshObject=o
239         except:
240             p(o.name)
241         break
242     if toonMeshObject:
243         toonMesh=bl.object.getData(toonMeshObject)
244         toonMaterial=bl.mesh.getMaterial(toonMesh, 0)
245         for i in range(10):
246             t=bl.material.getTexture(toonMaterial, i)
247             if t:
248                 model.toon_textures[i]=("%s" % t.name).encode('cp932')
249             else:
250                 model.toon_textures[i]=("toon%02d.bmp" % (i+1)).encode('cp932')
251     else:
252         for i in range(10):
253             model.toon_textures[i]=("toon%02d.bmp" % (i+1)).encode('cp932')
254
255     # rigid body
256     rigidNameMap={}
257     for i, obj in enumerate(self.oneSkinMesh.rigidbodies):
258         name=obj[bl.RIGID_NAME] if bl.RIGID_NAME in obj else obj.name
259         print(name)
260         rigidNameMap[name]=i
261         boneIndex=boneNameMap[obj[bl.RIGID_BONE_NAME]]
262         if boneIndex==0:
263             boneIndex=-1
264             bone=self.skeleton.bones[0]
265         else:
266             bone=self.skeleton.bones[boneIndex]
267         if obj[bl.RIGID_SHAPE_TYPE]==0:
268             shape_type=pmd.SHAPE_SPHERE
269             shape_size=common.Vector3(obj.scale[0], 0, 0)
270         elif obj[bl.RIGID_SHAPE_TYPE]==1:
271             shape_type=pmd.SHAPE_BOX
272             shape_size=common.Vector3(obj.scale[0], obj.scale[1], obj.scale[2])
273         elif obj[bl.RIGID_SHAPE_TYPE]==2:
274             shape_type=pmd.SHAPE_CAPSULE
275             shape_size=common.Vector3(obj.scale[0], obj.scale[2], 0)
276         rigidBody=pmd.RigidBody(
277                 name.encode('cp932'), 
278                 collision_group=obj[bl.RIGID_GROUP],
279                 no_collision_group=obj[bl.RIGID_INTERSECTION_GROUP],
280                 bone_index=boneIndex,
281                 shape_position=common.Vector3(
282                     obj.location.x-bone.pos[0],
283                     obj.location.z-bone.pos[2],
284                     obj.location.y-bone.pos[1]),
285                 shape_rotation=common.Vector3(
286                     -obj.rotation_euler[0],
287                     -obj.rotation_euler[2],
288                     -obj.rotation_euler[1]),
289                 shape_type=shape_type,
290                 shape_size=shape_size,
291                 mass=obj[bl.RIGID_WEIGHT],
292                 linear_damping=obj[bl.RIGID_LINEAR_DAMPING],
293                 angular_damping=obj[bl.RIGID_ANGULAR_DAMPING],
294                 restitution=obj[bl.RIGID_RESTITUTION],
295                 friction=obj[bl.RIGID_FRICTION],
296                 mode=obj[bl.RIGID_PROCESS_TYPE]
297                 )
298         model.rigidbodies.append(rigidBody)
299
300     # constraint
301     model.joints=[pmd.Joint(
302         name=obj[bl.CONSTRAINT_NAME].encode('cp932'),
303         rigidbody_index_a=rigidNameMap[obj[bl.CONSTRAINT_A]],
304         rigidbody_index_b=rigidNameMap[obj[bl.CONSTRAINT_B]],
305         position=common.Vector3(
306             obj.location[0], 
307             obj.location[2], 
308             obj.location[1]),
309         rotation=common.Vector3(
310             -obj.rotation_euler[0], 
311             -obj.rotation_euler[2], 
312             -obj.rotation_euler[1]),
313         translation_limit_min=common.Vector3(
314             obj[bl.CONSTRAINT_POS_MIN][0],
315             obj[bl.CONSTRAINT_POS_MIN][1],
316             obj[bl.CONSTRAINT_POS_MIN][2]
317             ),
318         translation_limit_max=common.Vector3(
319             obj[bl.CONSTRAINT_POS_MAX][0],
320             obj[bl.CONSTRAINT_POS_MAX][1],
321             obj[bl.CONSTRAINT_POS_MAX][2]
322             ),
323         rotation_limit_min=common.Vector3(
324             obj[bl.CONSTRAINT_ROT_MIN][0],
325             obj[bl.CONSTRAINT_ROT_MIN][1],
326             obj[bl.CONSTRAINT_ROT_MIN][2]),
327         rotation_limit_max=common.Vector3(
328             obj[bl.CONSTRAINT_ROT_MAX][0],
329             obj[bl.CONSTRAINT_ROT_MAX][1],
330             obj[bl.CONSTRAINT_ROT_MAX][2]),
331         spring_constant_translation=common.Vector3(
332             obj[bl.CONSTRAINT_SPRING_POS][0],
333             obj[bl.CONSTRAINT_SPRING_POS][1],
334             obj[bl.CONSTRAINT_SPRING_POS][2]),
335         spring_constant_rotation=common.Vector3(
336             obj[bl.CONSTRAINT_SPRING_ROT][0],
337             obj[bl.CONSTRAINT_SPRING_ROT][1],
338             obj[bl.CONSTRAINT_SPRING_ROT][2])
339         )
340         for obj in self.oneSkinMesh.constraints]
341
342     bl.message('write: %s' % path)
343     with io.open(path, 'wb') as f:
344         return writer.write(f, model)
345
346
347 def _execute(filepath=''):
348     active=bl.object.getActive()
349     if not active:
350         print("abort. no active object.")
351         return
352
353     ex=exporter.Exporter()
354     ex.setup()
355     print(ex)
356
357     write(ex, filepath)
358     bl.object.activate(active)
359     return {'FINISHED'}
360