6 def __init__(self, size):
8 self.slots = [-1]*self.size
11 hit = v.index in self.slots
13 self.slots.remove(v.index)
14 self.slots.append(v.index)
19 def fetch_strip(self, strip):
26 def test_strip(self, strip):
28 for i in range(len(strip)):
31 if strip[i].index in self.slots[i:]:
38 self.show_progress = True
39 self.use_strips = True
40 self.use_degen_tris = False
41 self.max_strip_len = 1024
42 self.optimize_cache = True
45 self.material_tex = False
47 def stripify(self, mesh, progress=None):
56 if self.optimize_cache:
57 cache = VertexCache(self.cache_size)
64 # No current island; find any unused face to start from
75 # Find all faces connected to the first one
80 for n in face.get_neighbors():
85 face_neighbors = [f.get_neighbors() for f in island]
87 # Unflag the island for the next phase
91 # Find an unused face with as few unused neighbors as possible, but
92 # at least one. This heuristic gives a preference to faces in corners
93 # or along borders of a non-closed island.
96 for i, f in enumerate(island):
100 score = sum(not n.flag for n in face_neighbors[i])
101 if score>0 and score<best:
106 # Create a strip starting from the face. This will flag the faces.
107 strip = mesh.create_strip(face, self.max_strip_len)
109 island_strips.append(strip)
113 # Couldn't find a candidate face for starting a strip, so we're
114 # done with this island
118 # Find the strip that benefits the most from the current
119 # contents of the vertex cache
121 for i in range(len(island_strips)):
122 hits = cache.test_strip(island_strips[i])
127 strip = island_strips.pop(best)
131 cache.fetch_strip(strip)
133 faces_done += len(island)
135 progress.set_progress(float(faces_done)/len(mesh.faces))
137 # Collect any faces that weren't used in strips
138 loose += [f for f in island if not f.flag]
146 cache = VertexCache(self.cache_size)
149 if self.use_degen_tris and strips:
154 # Generate glue elements, ensuring that the next strip begins at
156 glue = [big_strip[-1], s[0]]
162 total_hits += cache.fetch_strip(glue)
166 total_hits += cache.fetch_strip(s)
169 # Add loose faces to the end. This wastes space, using five
170 # elements per triangle and six elements per quad.
172 order = (-1, -2, 0, 1)
174 order = (0, 1, -1, -2)
175 vertices = [f.vertices[i] for i in order[:len(f.vertices)]]
178 glue = [big_strip[-1], vertices[0]]
181 total_hits += cache.fetch_strip(glue)
183 big_strip += vertices
185 total_hits += cache.fetch_strip(vertices)
192 def export(self, context, out_file, objs=None, progress=None):
194 objs = [(o, mathutils.Matrix()) for o in objs]
198 objs = [(o, mathutils.Matrix()) for o in context.selected_objects]
205 children.append((c, m*c.matrix_local))
209 objs = [(context.active_object, mathutils.Matrix())]
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)
231 if o.data.winding_test:
234 self.material_tex = True
235 bmesh = o.to_mesh(context.scene, True, "PREVIEW")
236 bmeshes.append(bmesh)
245 progress.set_task("Smoothing", 0.05, 0.35)
246 if mesh.smoothing=="NONE":
248 mesh.split_smooth(progress)
250 if mesh.smoothing!="BLENDER":
251 mesh.compute_normals()
253 if mesh.vertex_groups:
254 mesh.sort_vertex_groups(mesh.max_groups_per_vertex)
256 # Create a mapping from vertex group indices to bone indices
257 first_obj = objs[0][0]
258 group_index_map = dict((i, i) for i in range(len(first_obj.vertex_groups)))
259 if first_obj.parent and first_obj.parent.type=="ARMATURE":
260 armature = first_obj.parent.data
261 bone_indices = dict((armature.bones[i].name, i) for i in range(len(armature.bones)))
262 for g in first_obj.vertex_groups:
263 if g.name in bone_indices:
264 group_index_map[g.index] = bone_indices[g.name]
266 if self.material_tex and mesh.materials:
267 mesh.generate_material_uv()
271 if mesh.uv_layers and (mesh.use_uv!="NONE" or self.material_tex):
272 # Figure out which UV layers to export
273 if mesh.use_uv=="ALL":
274 texunits = range(len(mesh.uv_layers))
275 elif self.material_tex:
276 # The material UV layer is always the last one
277 texunits = [len(mesh.uv_layers)-1]
280 for i, u in enumerate(mesh.uv_layers):
284 texunits = [(i, mesh.uv_layers[i]) for i in texunits]
285 texunits = [u for u in texunits if not u[1].hidden]
288 # TBN coordinates must be generated before vertices are split by any other layer
289 uv_names = [u.name for i, u in texunits]
290 if mesh.tbn_uvtex in uv_names:
291 tbn_index = uv_names.index(mesh.tbn_uvtex)
292 unit = texunits[tbn_index]
293 del texunits[tbn_index]
294 texunits.insert(0, unit)
296 for i, u in texunits:
298 progress.set_task("Splitting UVs", 0.35+0.3*i/len(texunits), 0.35+0.3*(i+1)/len(texunits))
299 mesh.split_uv(i, progress)
300 if mesh.tbn_vecs and u.name==mesh.tbn_uvtex:
310 progress.set_task("Creating strips", 0.65, 0.95)
311 strips, loose = self.stripify(mesh, progress)
314 progress.set_task("Writing file", 0.95, 1.0)
316 from .outfile import open_output
317 out_file = open_output(out_file)
321 for i, u in texunits:
322 size = str(len(mesh.vertices[0].uvs[i]))
323 if u.unit==0 or force_unit0:
324 fmt.append("TEXCOORD"+size)
326 fmt.append("TEXCOORD%s_%d"%(size, u.unit))
328 fmt += ["TANGENT3", "BINORMAL3"]
329 if mesh.vertex_groups:
330 fmt.append("ATTRIB%d_5"%(mesh.max_groups_per_vertex*2))
331 fmt.append("VERTEX3")
332 out_file.begin("vertices", *fmt)
338 for v in mesh.vertices:
340 out_file.write("normal3", *v.normal)
342 for i, u in texunits:
343 if v.uvs[i]!=uvs.get(i):
344 size = str(len(v.uvs[i]))
345 if u.unit==0 or force_unit0:
346 out_file.write("texcoord"+size, *v.uvs[i])
348 out_file.write("multitexcoord"+size, u.unit, *v.uvs[i])
352 out_file.write("tangent3", *v.tan)
355 out_file.write("binormal3", *v.bino)
357 if mesh.vertex_groups:
358 group_attr = [(group_index_map[g.group], g.weight*v.group_weight_scale) for g in v.groups[:mesh.max_groups_per_vertex]]
359 while len(group_attr)<mesh.max_groups_per_vertex:
360 group_attr.append((0, 0.0))
361 group_attr = list(itertools.chain(*group_attr))
362 if group_attr!=group:
363 out_file.write("attrib%d"%len(group_attr), 5, *group_attr)
365 out_file.write("vertex3", *v.co)
368 out_file.begin("batch", "TRIANGLE_STRIP")
372 indices.append(v.index)
374 out_file.write("indices", *indices)
377 out_file.write("indices", *indices)
381 out_file.begin("batch", "TRIANGLES")
383 for i in range(2, len(f.vertices)):
384 out_file.write("indices", f.vertices[0].index, f.vertices[i-1].index, f.vertices[i].index)
387 if mesh.use_lines and mesh.lines:
388 out_file.begin("batch", "LINES")
390 out_file.write("indices", l.vertices[0].index, l.vertices[1].index)
394 out_file.write("winding", "COUNTERCLOCKWISE")
397 progress.set_task("Done", 1.0, 1.0)
400 bpy.data.meshes.remove(m)