forked from taniwha/io_object_mu
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathexport_mu.py
356 lines (329 loc) · 13 KB
/
export_mu.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
# vim:ts=4:et
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
# <pep8 compliant>
import bpy, bmesh
from bpy_extras.object_utils import object_data_add
from mathutils import Vector,Matrix,Quaternion
from pprint import pprint
from .mu import MuEnum, Mu, MuColliderMesh, MuColliderSphere, MuColliderCapsule
from .mu import MuObject, MuTransform, MuMesh, MuTagLayer, MuRenderer
from .mu import MuColliderBox, MuColliderWheel, MuMaterial, MuTexture, MuMatTex
from .mu import MuSpring, MuFriction
from .shader import make_shader
from . import properties
def strip_nnn(name):
ind = name.rfind(".")
if ind < 0 or len(name) - ind != 4:
return name
if not name[ind+1:].isdigit():
return name
return name[:ind]
def make_transform(obj):
transform = MuTransform()
transform.name = strip_nnn(obj.name)
transform.localPosition = obj.location
transform.localRotation = obj.rotation_quaternion
transform.localScale = obj.scale
return transform
def split_face(mesh, index):
face = mesh.polygons[index]
s, e = face.loop_start, face.loop_start + face.loop_total
uv = mesh.uv_layers.active.data[s:e]
uv = list(map(lambda a: a.uv, uv))
fv = list(face.vertices)
tris = []
for i in range(1, len(fv) - 1):
tri = ((fv[0], tuple(uv[0])),
(fv[i], tuple(uv[i])),
(fv[i+1], tuple(uv[i+1])))
tris.append(tri)
return tris
def build_submeshes(mesh):
submeshes = []
submesh = []
for i in range(len(mesh.polygons)):
submesh.append(i)
submeshes.append(submesh)
return submeshes
def make_tris(mesh, submeshes):
for sm in submeshes:
i = 0
while i < len(sm):
tris = split_face(mesh, sm[i])
sm[i:i+1] = tris
i += len(tris)
return submeshes
def make_verts(mesh, submeshes):
verts = []
normals = []
uvs = []
for sm in submeshes:
vuvdict = {}
for i, ft in enumerate(sm):
tv = []
for vuv in ft:
if vuv not in vuvdict:
vuvdict[vuv] = len(verts)
mv = mesh.vertices[vuv[0]]
verts.append(tuple(mv.co))
normals.append(tuple(mv.normal))
uvs.append(vuv[1])
tv.append(vuvdict[vuv])
sm[i] = tv
return verts, uvs, normals
def make_tangents(verts, uvs, normals, submeshes):
sdir = [Vector()] * len(verts)
tdir = [Vector()] * len(verts)
tangents = []
for sm in submeshes:
for tri in sm:
v1 = Vector(verts[tri[0]])
v2 = Vector(verts[tri[1]])
v3 = Vector(verts[tri[2]])
w1 = uvs[tri[0]]
w2 = uvs[tri[1]]
w3 = uvs[tri[2]]
u1 = v2 - v1
u2 = v3 - v1
s1 = w2[0] - w1[0]
s2 = w3[0] - w1[0]
t1 = w2[1] - w1[1]
t2 = w3[1] - w1[1]
r = s1 * t2 - s2 * t1
if r * r < 1e-6:
continue
sd = (t2 * u1 - t1 * u2) / r
td = (s1 * u2 - s2 * u1) / r
sdir[tri[0]] += sd
sdir[tri[1]] += sd
sdir[tri[2]] += sd
tdir[tri[0]] += td
tdir[tri[1]] += td
tdir[tri[2]] += td
for i, n in enumerate(normals):
t = sdir[i]
t -= t.dot(n) * Vector(n)
t.normalize()
hand = t.dot(tdir[i]) < 0 and -1.0 or 1.0
tangents.append(tuple(t) + (hand,))
return tangents
def make_mesh(mu, obj):
mesh = obj.to_mesh(bpy.context.scene, True, 'PREVIEW')
submeshes = build_submeshes(mesh)
submeshes = make_tris(mesh, submeshes)
mumesh = MuMesh()
vun = make_verts(mesh, submeshes)
mumesh.verts, mumesh.uvs, mumesh.normals = vun
mumesh.uv2s = mumesh.uvs#FIXME
mumesh.submeshes = submeshes
if True or len(mesh.materials):
mumesh.tangents = make_tangents(mumesh.verts, mumesh.uvs,
mumesh.normals, mumesh.submeshes)
return mumesh
def make_spring(spr):
spring = MuSpring()
spring.spring = spr.spring
spring.damper = spr.damper
spring.targetPosition = spr.targetPosition
return spring
def make_friction(fric):
friction = MuFriction()
friction.extremumSlip = fric.extremumSlip
friction.extremumValue = fric.extremumValue
friction.asymptoteSlip = fric.asymptoteSlip
friction.asymptoteValue = fric.asymptoteValue
friction.stiffness = fric.stiffness
return friction
def make_collider(mu, obj):
if (obj.muproperties.collider == 'MU_COL_MESH' and obj.data
and type (obj.data) == bpy.types.Mesh):
col = MuColliderMesh(True)
col.isTrigger = obj.muproperties.isTrigger
col.convex = True #FIXME calculate
col.mesh = make_mesh (mu, obj)
elif obj.muproperties.collider == 'MU_COL_SPHERE':
col = MuColliderSphere(True)
col.isTrigger = obj.muproperties.isTrigger
col.radius = obj.muproperties.radius
col.center = obj.muproperties.center
elif obj.muproperties.collider == 'MU_COL_CAPSULE':
col = MuColliderCapsule(True)
col.isTrigger = obj.muproperties.isTrigger
col.radius = obj.muproperties.radius
col.height = obj.muproperties.height
col.direction = obj.muproperties.direction
if type(col.direction) is not int:
col.direction = properties.dir_map[col.direction]
col.center = obj.muproperties.center
elif obj.muproperties.collider == 'MU_COL_BOX':
col = MuColliderBox(True)
col.isTrigger = obj.muproperties.isTrigger
col.size = obj.muproperties.size
col.center = obj.muproperties.center
elif obj.muproperties.collider == 'MU_COL_WHEEL':
col = MuColliderWheel()
col.isTrigger = obj.muproperties.isTrigger
col.mass = obj.muproperties.mass
col.radius = obj.muproperties.radius
col.suspensionDistance = obj.muproperties.suspensionDistance
col.center = obj.muproperties.center
col.suspensionSpring = make_spring(obj.muproperties.suspensionSpring)
col.forwardFriction = make_friction(obj.muproperties.forwardFriction)
col.sidewaysFriction = make_friction(obj.muproperties.sideFriction)
return col
def make_tag_and_layer(obj):
tl = MuTagLayer()
tl.tag = obj.muproperties.tag
tl.layer = obj.muproperties.layer
return tl
def make_texture(mu, tex, type):
if tex.tex not in mu.textures:
mutex = MuTexture()
mutex.name = tex.tex
mutex.type = type
mutex.index = len(mu.textures)
mu.textures[tex.tex] = mutex
mattex = MuMatTex()
mattex.index = mu.textures[tex.tex].index
mattex.scale = tex.scale
mattex.offset = tex.offset
return mattex
def make_material(mu, mat):
material = MuMaterial()
material.name = mat.name
material.index = len(mu.materials)
matprops = mat.mumatprop
material.type = MuEnum.ShaderNames.index(matprops.shader)
if matprops.shader == 'KSP/Specular':
material.mainTex = make_texture(mu, matprops.mainTex, 0)
material.specColor = matprops.specColor
material.shininess = matprops.shininess
elif matprops.shader == 'KSP/Bumped':
material.mainTex = make_texture(mu, matprops.mainTex, 0)
material.bumpMap = make_texture(mu, matprops.bumpMap, 1)
elif matprops.shader == 'KSP/Bumped Specular':
material.mainTex = make_texture(mu, matprops.mainTex, 0)
material.bumpMap = make_texture(mu, matprops.bumpMap, 1)
material.specColor = matprops.specColor
material.shininess = matprops.shininess
elif matprops.shader == 'KSP/Emissive/Diffuse':
material.mainTex = make_texture(mu, matprops.mainTex, 0)
material.emissive = make_texture(mu, matprops.emissive, 0)
material.emissiveColor = matprops.emissiveColor
elif matprops.shader == 'KSP/Emissive/Specular':
material.mainTex = make_texture(mu, matprops.mainTex, 0)
material.specColor = matprops.specColor
material.shininess = matprops.shininess
material.emissive = make_texture(mu, matprops.emissive, 0)
material.emissiveColor = matprops.emissiveColor
elif matprops.shader == 'KSP/Emissive/Bumped Specular':
material.mainTex = make_texture(mu, matprops.mainTex, 0)
material.bumpMap = make_texture(mu, matprops.bumpMap, 1)
material.specColor = matprops.specColor
material.shininess = matprops.shininess
material.emissive = make_texture(mu, matprops.emissive, 0)
material.emissiveColor = matprops.emissiveColor
elif matprops.shader == 'KSP/Alpha/Cutoff':
material.mainTex = make_texture(mu, matprops.mainTex, 0)
material.cutoff = matprops.cutoff
elif matprops.shader == 'KSP/Alpha/Cutoff Bumped':
material.mainTex = make_texture(mu, matprops.mainTex, 0)
material.bumpMap = make_texture(mu, matprops.bumpMap, 1)
material.cutoff = matprops.cutoff
elif matprops.shader == 'KSP/Alpha/Translucent':
material.mainTex = make_texture(mu, matprops.mainTex, 0)
elif matprops.shader == 'KSP/Alpha/Translucent Specular':
material.mainTex = make_texture(mu, matprops.mainTex, 0)
material.gloss = matprops.gloss
material.specColor = matprops.specColor
material.shininess = matprops.shininess
elif matprops.shader == 'KSP/Alpha/Unlit Transparent':
material.mainTex = make_texture(mu, matprops.mainTex, 0)
material.color = matprops.color
elif matprops.shader == 'KSP/Unlit':
material.mainTex = make_texture(mu, matprops.mainTex, 0)
material.color = matprops.color
elif matprops.shader == 'KSP/Diffuse':
material.mainTex = make_texture(mu, matprops.mainTex, 0)
return material
def make_renderer(mu, mesh):
rend = MuRenderer()
#FIXME shadows
rend.materials = []
for mat in mesh.materials:
if mat.mumatprop.shader:
if mat.name not in mu.materials:
mu.materials[mat.name] = make_material(mu, mat)
rend.materials.append(mu.materials[mat.name].index)
return rend
def make_light(mu, light):
mulight = MuLight()
mulight.type = ('SPOT', 'SUN', 'POINT', 'AREA').index(light.type)
mulight.color = tuple(light.color) + (1.0,)
mulight.range = light.distance
mulight.intensity = light.energy
mulight.spotAngle = 0.0
mulight.cullingMask = properties.GetPropMask(obj.muproperties.cullingMask)
if light.type == 'SPOT':
mulight.spotAngle = light.spot_size * 180 / pi
return mulight
def make_obj(mu, obj):
muobj = MuObject()
muobj.transform = make_transform (obj)
muobj.tag_and_layer = make_tag_and_layer(obj)
if obj.muproperties.collider and obj.muproperties.collider != 'MU_COL_NONE':
# colliders are children of the object representing the transform so
# they are never exported directly.
pass
elif obj.data:
if type(obj.data) == bpy.types.Mesh:
muobj.shared_mesh = make_mesh(mu, obj)
muobj.renderer = make_renderer(mu, obj.data)
elif type(obj.data) in [bpy.types.PointLamp,
bpy.types.SunLamp,
bpy.types.SpotLamp,
bpy.types.HemiLamp,
bpy.types.AreaLamp]:
muobj.light = make_light(mu, obj.data)
# Blender points spotlights along local -Z, unity along local +Z
# which is Blender's +Y, so rotate -90 degrees around local X to
# go from Blender to Unity
rot = Quaternion((0.5**0.5,-0.5**0.5,0,0))
muobj.transform = rot * muobj.transform
for o in obj.children:
muprops = o.muproperties
if muprops.collider and muprops.collider != 'MU_COL_NONE':
muobj.collider = make_collider(mu, o)
continue
if (o.data and type(o.data) != bpy.types.Mesh):
continue
muobj.children.append(make_obj(mu, o))
return muobj
def export_mu(operator, context, filepath):
obj = context.active_object
mu = Mu()
mu.materials = {}
mu.textures = {}
mu.obj = make_obj(mu, obj)
mu.materials = list(mu.materials.values())
mu.materials.sort(key=lambda x: x.index)
mu.textures = list(mu.textures.values())
mu.textures.sort(key=lambda x: x.index)
mu.write(filepath)
return {'FINISHED'}