OSDN Git Service

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