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