5 def __init__(self, size):
7 self.slots = [-1]*self.size
10 hit = v.index in self.slots
12 self.slots.remove(v.index)
13 self.slots.append(v.index)
18 def fetch_strip(self, strip):
25 def test_strip(self, strip):
27 for i in range(len(strip)):
30 if strip[i].index in self.slots[i:]:
37 self.show_progress = True
38 self.use_strips = True
39 self.use_degen_tris = False
40 self.max_strip_len = 1024
41 self.optimize_cache = True
43 self.export_lines = False
44 self.export_uv = "UNIT0"
48 self.material_tex = False
49 self.smoothing = "MSPGL"
50 self.export_groups = False
53 def stripify(self, mesh, progress=None):
62 if self.optimize_cache:
63 cache = VertexCache(self.cache_size)
69 # No current island; find any unused face to start from
80 # Find all faces connected to the first one
85 for n in f.get_neighbors():
90 # Unflag the island for the next phase
94 # Find an unused face with as few unused neighbors as possible, but
95 # at least one. This heuristic gives a preference to faces in corners
96 # or along borders of a non-closed island.
103 score = sum(not n.flag for n in f.get_neighbors())
104 if score>0 and score<best:
109 # Create a strip starting from the face. This will flag the faces.
110 strip = mesh.create_strip(face, self.max_strip_len)
112 island_strips.append(strip)
116 # Couldn't find a candidate face for starting a strip, so we're
117 # done with this island
121 # Find the strip that benefits the most from the current
122 # contents of the vertex cache
124 for i in range(len(island_strips)):
125 hits = cache.test_strip(island_strips[i])
130 strip = island_strips.pop(best)
134 cache.fetch_strip(strip)
136 faces_done += len(island)
138 progress.set_progress(float(faces_done)/len(mesh.faces))
140 # Collect any faces that weren't used in strips
141 loose += [f for f in island if not f.flag]
149 cache = VertexCache(self.cache_size)
152 if self.use_degen_tris and strips:
157 # Generate glue elements, ensuring that the next strip begins at
159 glue = [big_strip[-1], s[0]]
165 total_hits += cache.fetch_strip(glue)
169 total_hits += cache.fetch_strip(s)
172 # Add loose faces to the end. This wastes space, using five
173 # elements per triangle and six elements per quad.
175 order = (-1, -2, 0, 1)
177 order = (0, 1, -1, -2)
178 vertices = [f.vertices[i] for i in order[:len(f.vertices)]]
181 glue = [big_strip[-1], vertices[0]]
184 total_hits += cache.fetch_strip(glue)
186 big_strip += vertices
188 total_hits += cache.fetch_strip(vertices)
195 def export(self, context, out_file, objs=None, progress=None):
198 objs = context.selected_objects
209 objs = [context.active_object]
212 raise Exception("Nothing to export")
215 raise Exception("Can only export Mesh data")
217 from .mesh import Mesh
218 from .util import Progress
220 if self.show_progress:
222 progress = Progress(context)
223 progress.set_task("Preparing", 0.0, 0.0)
230 bmesh = o.to_mesh(context.scene, True, "PREVIEW")
231 bmeshes.append(bmesh)
235 mesh.splice(Mesh(bmesh))
238 progress.set_task("Smoothing", 0.05, 0.35)
239 if self.smoothing=="NONE":
241 mesh.split_smooth(progress)
243 if self.smoothing!="BLENDER":
244 mesh.compute_normals()
246 if self.export_groups:
247 mesh.sort_vertex_groups(self.max_groups)
249 # Create a mapping from vertex group indices to bone indices
250 group_index_map = dict((i, i) for i in range(len(objs[0].vertex_groups)))
251 if objs[0].parent and objs[0].parent.type=="ARMATURE":
252 armature = objs[0].parent.data
253 bone_indices = dict((armature.bones[i].name, i) for i in range(len(armature.bones)))
254 for g in objs[0].vertex_groups:
255 if g.name in bone_indices:
256 group_index_map[g.index] = bone_indices[g.name]
258 if self.material_tex and mesh.materials:
259 mesh.generate_material_uv()
263 if mesh.uv_layers and (self.export_uv!="NONE" or self.material_tex):
264 # Figure out which UV layers to export
265 if self.export_uv=="ALL":
266 texunits = range(len(mesh.uv_layers))
267 elif self.material_tex:
268 # The material UV layer is always the last one
269 texunits = [len(mesh.uv_layers)-1]
272 for i, u in enumerate(mesh.uv_layers):
276 texunits = [(i, mesh.uv_layers[i]) for i in texunits]
277 texunits = [u for u in texunits if not u[1].hidden]
280 # TBN coordinates must be generated before vertices are split by any other layer
281 uv_names = [u.name for i, u in texunits]
282 if self.tbn_uvtex in uv_names:
283 tbn_index = uv_names.index(self.tbn_uvtex)
284 unit = texunits[tbn_index]
285 del texunits[tbn_index]
286 texunits.insert(0, unit)
288 for i, u in texunits:
290 progress.set_task("Splitting UVs", 0.35+0.3*i/len(texunits), 0.35+0.3*(i+1)/len(texunits))
291 mesh.split_uv(i, progress)
292 if self.tbn_vecs and u.name==self.tbn_uvtex:
302 progress.set_task("Creating strips", 0.65, 0.95)
303 strips, loose = self.stripify(mesh, progress)
306 progress.set_task("Writing file", 0.95, 1.0)
308 from .outfile import open_output
309 out_file = open_output(out_file)
313 for i, u in texunits:
314 if u.unit==0 or force_unit0:
315 fmt.append("TEXCOORD2")
317 fmt.append("TEXCOORD2_%d"%u.unit)
319 fmt += ["TANGENT3", "BINORMAL3"]
320 if self.export_groups:
321 fmt.append("ATTRIB%d_5"%(self.max_groups*2))
322 fmt.append("VERTEX3")
323 out_file.begin("vertices", *fmt)
329 for v in mesh.vertices:
331 out_file.write("normal3", *v.normal)
333 for i, u in texunits:
334 if v.uvs[i]!=uvs.get(i):
335 if u.unit==0 or force_unit0:
336 out_file.write("texcoord2", *v.uvs[i])
338 out_file.write("multitexcoord2", u.unit, *v.uvs[i])
342 out_file.write("tangent3", *v.tan)
345 out_file.write("binormal3", *v.bino)
347 if self.export_groups:
348 group_attr = [(group_index_map[g.group], g.weight*v.group_weight_scale) for g in v.groups[:self.max_groups]]
349 while len(group_attr)<self.max_groups:
350 group_attr.append((0, 0.0))
351 group_attr = list(itertools.chain(*group_attr))
352 if group_attr!=group:
353 out_file.write("attrib%d"%len(group_attr), 5, *group_attr)
355 out_file.write("vertex3", *v.co)
358 out_file.begin("batch", "TRIANGLE_STRIP")
362 indices.append(v.index)
364 out_file.write("indices", *indices)
367 out_file.write("indices", *indices)
371 out_file.begin("batch", "TRIANGLES")
373 for i in range(2, len(f.vertices)):
374 out_file.write("indices", f.vertices[0].index, f.vertices[i-1].index, f.vertices[i].index)
377 if self.export_lines and mesh.lines:
378 out_file.begin("batch", "LINES")
380 out_file.write("indices", l.vertices[0].index, l.vertices[1].index)
384 progress.set_task("Done", 1.0, 1.0)
387 bpy.data.meshes.remove(m)