]> git.tdb.fi Git - libs/gl.git/blob - blender/io_mspgl/export_mesh.py
3f0771fe426b60ad1a59d4fbeb47f85016c5a686
[libs/gl.git] / blender / io_mspgl / export_mesh.py
1 import bpy
2 from .outfile import OutFile
3
4 class VertexCache:
5         def __init__(self, size):
6                 self.size = size
7                 self.slots = [-1]*self.size
8
9         def fetch(self, v):
10                 hit = v.index in self.slots
11                 if hit:
12                         self.slots.remove(v.index)
13                 self.slots.append(v.index)
14                 if not hit:
15                         del self.slots[0]
16                 return hit
17
18         def fetch_strip(self, strip):
19                 hits = 0
20                 for v in strip:
21                         if self.fetch(v):
22                                 hits += 1
23                 return hits
24
25         def test_strip(self, strip):
26                 hits = 0
27                 for i in range(len(strip)):
28                         if i>=self.size:
29                                 break
30                         if strip[i].index in self.slots[i:]:
31                                 hits += 1
32                 return hits
33
34
35 class MeshExporter:
36         def __init__(self):
37                 self.use_strips = True
38                 self.use_degen_tris = True
39                 self.max_strip_len = 1024
40                 self.optimize_cache = False
41                 self.cache_size = 64
42                 self.export_lines = True
43                 self.export_uv = "UNIT0"
44                 self.tbn_vecs = False
45                 self.tbn_uvtex = ""
46                 self.compound = False
47                 self.object = False
48                 self.material_tex = False
49                 self.textures = "REF"
50                 self.smoothing = "MSPGL"
51
52         def stripify(self, mesh, progress = None):
53                 for f in mesh.faces:
54                         f.flag = False
55
56                 faces_done = 0
57                 strips = []
58                 loose = []
59
60                 cache = None
61                 if self.optimize_cache:
62                         cache = VertexCache(self.cache_size)
63
64                 island = []
65                 island_strips = []
66                 while 1:
67                         if not island:
68                                 # No current island; find any unused face to start from
69                                 queue = []
70                                 for f in mesh.faces:
71                                         if not f.flag:
72                                                 f.flag = True
73                                                 queue.append(f)
74                                                 break
75
76                                 if not queue:
77                                         break
78
79                                 # Find all faces connected to the first one
80                                 while queue:
81                                         face = queue.pop(0)
82                                         island.append(face)
83
84                                         for n in f.get_neighbors():
85                                                 if not n.flag:
86                                                         n.flag = True
87                                                         queue.append(n)
88
89                                 # Unflag the island for the next phase
90                                 for f in island:
91                                         f.flag = False
92
93                         # Find an unused face with as few unused neighbors as possible, but
94                         # at least one.  This heuristic gives a preference to faces in corners
95                         # or along borders of a non-closed island.
96                         best = 5
97                         face = None
98                         for f in island:
99                                 if f.flag:
100                                         continue
101
102                                 score = sum(not n.flag for n in f.get_neighbors())
103                                 if score>0 and score<best:
104                                         face = f
105                                         best = score
106
107                         if face:
108                                 # Create a strip starting from the face.  This will flag the faces.
109                                 strip = mesh.create_strip(face, self.max_strip_len)
110                                 if strip:
111                                         island_strips.append(strip)
112                                 else:
113                                         face.flag = True
114                         else:
115                                 # Couldn't find a candidate face for starting a strip, so we're
116                                 # done with this island
117                                 while island_strips:
118                                         best = 0
119                                         if cache:
120                                                 # Find the strip that benefits the most from the current
121                                                 # contents of the vertex cache
122                                                 best_hits = 0
123                                                 for i in range(len(island_strips)):
124                                                         hits = cache.test_strip(island_strips[i])
125                                                         if hits>best_hits:
126                                                                 best = i
127                                                                 best_hits = hits
128
129                                         strip = island_strips.pop(best)
130                                         strips.append(strip)
131
132                                         if cache:
133                                                 cache.fetch_strip(strip)
134
135                                 faces_done += len(island)
136                                 if progress:
137                                         progress.set_progress(float(faces_done)/len(mesh.faces))
138
139                                 # Collect any faces that weren't used in strips
140                                 loose += [f for f in island if not f.flag]
141                                 for f in island:
142                                         f.flag = True
143
144                                 island = []
145                                 island_strips = []
146
147                 if cache:
148                         cache = VertexCache(self.cache_size)
149                         total_hits = 0
150
151                 if self.use_degen_tris and strips:
152                         big_strip = []
153
154                         for s in strips:
155                                 if big_strip:
156                                         # Generate glue elements, ensuring that the next strip begins at
157                                         # an even position
158                                         glue = [big_strip[-1], s[0]]
159                                         if len(big_strip)%2:
160                                                 glue += [s[0]]
161
162                                         big_strip += glue
163                                         if cache:
164                                                 total_hits += cache.fetch_strip(glue)
165
166                                 big_strip += s
167                                 if cache:
168                                         total_hits += cache.fetch_strip(s)
169
170                         for f in loose:
171                                 # Add loose faces to the end.  This wastes space, using five
172                                 # elements per triangle and six elements per quad.
173                                 if len(big_strip)%2:
174                                         order = (-1, -2, 0, 1)
175                                 else:
176                                         order = (0, 1, -1, -2)
177                                 vertices = [f.vertices[i] for i in order[:len(f.vertices)]]
178
179                                 if big_strip:
180                                         glue = [big_strip[-1], vertices[0]]
181                                         big_strip += glue
182                                         if cache:
183                                                 total_hits += cache.fetch_strip(glue)
184
185                                 big_strip += vertices
186                                 if cache:
187                                         total_hits += cache.fetch_strip(vertices)
188
189                         strips = [big_strip]
190                         loose = []
191
192                 return strips, loose
193
194         def export(self, context, fn):
195                 if self.compound:
196                         objs = context.selected_objects
197                 else:
198                         objs = [context.active_object]
199
200                 if not objs:
201                         raise Exception("Nothing to export")
202                 for o in objs:
203                         if o.type!="MESH":
204                                 raise Exception("Can only export Mesh data")
205
206                 from .mesh import Mesh
207                 from .util import Progress
208
209                 progress = Progress()
210                 progress.set_task("Preparing", 0.0, 0.0)
211
212                 mesh = None
213                 bmeshes = []
214                 for o in objs:
215                         bmesh = o.to_mesh(context.scene, True, "PREVIEW")
216                         bmeshes.append(bmesh)
217                         if not mesh:
218                                 mesh = Mesh(bmesh)
219                         else:
220                                 mesh.splice(Mesh(bmesh))
221
222                 progress.set_task("Smoothing", 0.05, 0.35)
223                 if self.smoothing=="NONE":
224                         mesh.flatten_faces()
225                 mesh.split_smooth(progress)
226
227                 if self.smoothing!="BLENDER":
228                         mesh.compute_normals()
229
230                 if self.material_tex and mesh.materials:
231                         mesh.generate_material_uv()
232
233                 texunits = []
234                 if mesh.uv_layers and self.export_uv!="NONE":
235                         # Figure out which UV layers to export
236                         if self.export_uv=="UNIT0":
237                                 if mesh.uv_layers[0].unit==0:
238                                         texunits = [0]
239                         else:
240                                 texunits = range(len(mesh.uv_layers))
241                         texunits = [(i, mesh.uv_layers[i]) for i in texunits]
242                         texunits = [u for u in texunits if not u[1].hidden]
243
244                         if self.tbn_vecs:
245                                 # TBN coordinates must be generated before vertices are split by any other layer
246                                 uv_names = [u.name for i, u in texunits]
247                                 if self.tbn_uvtex in uv_names:
248                                         tbn_index = uv_names.index(self.tbn_uvtex)
249                                         unit = texunits[tbn_index]
250                                         del texunits[tbn_index]
251                                         texunits.insert(0, unit)
252
253                         for i, u in texunits:
254                                 progress.set_task("Splitting UVs", 0.35+0.3*i/len(texunits), 0.35+0.3*(i+1)/len(texunits))
255                                 mesh.split_uv(i, progress)
256                                 if self.tbn_vecs and u.name==self.tbn_uvtex:
257                                         mesh.compute_uv()
258                                         mesh.compute_tbn(i)
259
260                         mesh.compute_uv()
261
262                 strips = []
263                 loose = mesh.faces
264                 if self.use_strips:
265                         progress.set_task("Creating strips", 0.65, 0.95)
266                         strips, loose = self.stripify(mesh, progress)
267
268                 progress.set_task("Writing file", 0.95, 1.0)
269
270                 out_file = OutFile(fn)
271                 if self.object:
272                         out_file.begin("mesh")
273
274                 fmt = "NORMAL3"
275                 if texunits:
276                         for i, u in texunits:
277                                 if u.unit==0:
278                                         fmt += "_TEXCOORD2"
279                                 else:
280                                         fmt += "_TEXCOORD2%d"%u.unit
281                         if self.tbn_vecs:
282                                 fmt += "_ATTRIB33_ATTRIB34"
283                 fmt += "_VERTEX3"
284                 out_file.begin("vertices", fmt)
285                 normal = None
286                 uvs = [None]*len(texunits)
287                 tan = None
288                 bino = None
289                 for v in mesh.vertices:
290                         if v.normal!=normal:
291                                 out_file.write("normal3", *v.normal)
292                                 normal = v.normal
293                         for i, u in texunits:
294                                 if v.uvs[i]!=uvs[i]:
295                                         if u.unit==0:
296                                                 out_file.write("texcoord2", *v.uvs[i])
297                                         else:
298                                                 out_file.write("multitexcoord2", u.unit, *v.uvs[i])
299                                         uvs[i] = v.uvs[i]
300                         if self.tbn_vecs:
301                                 if v.tan!=tan:
302                                         out_file.write("attrib3", 3, *v.tan)
303                                         tan = v.tan
304                                 if v.bino!=bino:
305                                         out_file.write("attrib3", 4, *v.bino)
306                                         bino = v.bino
307                         out_file.write("vertex3", *v.co)
308                 out_file.end()
309                 for s in strips:
310                         out_file.begin("batch", "TRIANGLE_STRIP")
311                         indices = []
312                         n = 0
313                         for v in s:
314                                 indices.append(v.index)
315                                 if len(indices)>=32:
316                                         out_file.write("indices", *indices)
317                                         indices = []
318                         if indices:
319                                 out_file.write("indices", *indices)
320                         out_file.end()
321
322                 if loose:
323                         out_file.begin("batch", "TRIANGLES")
324                         for f in loose:
325                                 for i in range(2, len(f.vertices)):
326                                         out_file.write("indices", f.vertices[0].index, f.vertices[i-1].index, f.vertices[i].index)
327                         out_file.end()
328
329                 if self.export_lines and mesh.lines:
330                         out_file.write("batch", "LINES")
331                         for l in mesh.lines:
332                                 out_file.write("indices", l.vertices[0].index, l.vertices[1].index)
333                         out_file.end()
334
335                 if self.object:
336                         out_file.end()
337                         out_file.begin("technique")
338                         out_file.begin("pass", '""')
339                         if mesh.materials:
340                                 if self.material_tex:
341                                         out_file.begin("material")
342                                         out_file.write("diffuse", 1.0, 1.0, 1.0, 1.0)
343                                         out_file.end()
344                                         index = 0
345                                         for u in mesh.uv_layers:
346                                                 if u.name=="material_tex":
347                                                         index = u.unit
348                                         out_file.begin("texunit", index)
349                                         out_file.begin("texture2d")
350                                         out_file.write("min_filter", "NEAREST")
351                                         out_file.write("mag_filter", "NEAREST")
352                                         out_file.write("storage", "RGB", len(mesh.materials), 1)
353                                         texdata = '"'
354                                         for m in mesh.materials:
355                                                 color = [int(c*255) for c in m.diffuse_color]
356                                                 texdata += "\\x%02X\\x%02X\\x%02X"%tuple(color)
357                                         texdata += '"'
358                                         out_file.write("raw_data", texdata)
359                                         out_file.end()
360                                         out_file.end()
361                                 else:
362                                         mat = mesh.materials[0]
363                                         out_file.begin("material")
364                                         diff = mat.diffuse_color
365                                         out_file.write("diffuse", diff.r, diff.g, diff.b, 1.0)
366                                         amb = diff*mat.ambient
367                                         out_file.write("ambient", amb.r, amb.g, amb.b, 1.0)
368                                         spec = mat.specular_color*mat.specular_intensity
369                                         out_file.write("specular", spec.r, spec.g, spec.b, 1.0)
370                                         out_file.write("shininess", mat.specular_hardness);
371                                         out_file.end()
372
373                                 if self.textures!="NONE":
374                                         for slot in mesh.materials[0].texture_slots:
375                                                 if not slot:
376                                                         continue
377
378                                                 tex = slot.texture
379                                                 if tex.type!="IMAGE":
380                                                         continue
381
382                                                 if slot.uv_layer:
383                                                         for u in mesh.uv_layers:
384                                                                 if u.name==slot.uv_layer:
385                                                                         index = u.unit
386                                                 else:
387                                                         index = mesh.uv_layers[0].unit
388
389                                                 out_file.begin("texunit", index)
390                                                 if self.textures=="INLINE":
391                                                         out_file.begin("texture2d")
392                                                         out_file.write("min_filter", "LINEAR")
393                                                         out_file.write("storage", "RGBA", tex.image.size[0], tex.image.size[1])
394                                                         texdata = '"'
395                                                         for p in tex.image.pixels:
396                                                                 texdata += "\\x%02X"%int(p*255)
397                                                         texdata += '"'
398                                                         out_file.write("raw_data", texdata)
399                                                         out_file.end()
400                                                 else:
401                                                         out_file.write("texture", '"%s"'%tex.image.name)
402                                                 out_file.end()
403
404                         out_file.end()
405                         out_file.end()
406
407                 progress.set_task("Done", 1.0, 1.0)
408
409                 for m in bmeshes:
410                         bpy.data.meshes.remove(m)