3 from .outfile import OutFile
9 return 1.055*(l**(1/2.4))-0.055
12 def __init__(self, size):
14 self.slots = [-1]*self.size
17 hit = v.index in self.slots
19 self.slots.remove(v.index)
20 self.slots.append(v.index)
25 def fetch_strip(self, strip):
32 def test_strip(self, strip):
34 for i in range(len(strip)):
37 if strip[i].index in self.slots[i:]:
44 self.use_strips = True
45 self.use_degen_tris = True
46 self.max_strip_len = 1024
47 self.optimize_cache = False
49 self.export_lines = True
50 self.export_uv = "UNIT0"
55 self.material_tex = False
56 self.srgb_colors = True
58 self.smoothing = "MSPGL"
59 self.export_groups = False
62 def stripify(self, mesh, progress = None):
71 if self.optimize_cache:
72 cache = VertexCache(self.cache_size)
78 # No current island; find any unused face to start from
89 # Find all faces connected to the first one
94 for n in f.get_neighbors():
99 # Unflag the island for the next phase
103 # Find an unused face with as few unused neighbors as possible, but
104 # at least one. This heuristic gives a preference to faces in corners
105 # or along borders of a non-closed island.
112 score = sum(not n.flag for n in f.get_neighbors())
113 if score>0 and score<best:
118 # Create a strip starting from the face. This will flag the faces.
119 strip = mesh.create_strip(face, self.max_strip_len)
121 island_strips.append(strip)
125 # Couldn't find a candidate face for starting a strip, so we're
126 # done with this island
130 # Find the strip that benefits the most from the current
131 # contents of the vertex cache
133 for i in range(len(island_strips)):
134 hits = cache.test_strip(island_strips[i])
139 strip = island_strips.pop(best)
143 cache.fetch_strip(strip)
145 faces_done += len(island)
147 progress.set_progress(float(faces_done)/len(mesh.faces))
149 # Collect any faces that weren't used in strips
150 loose += [f for f in island if not f.flag]
158 cache = VertexCache(self.cache_size)
161 if self.use_degen_tris and strips:
166 # Generate glue elements, ensuring that the next strip begins at
168 glue = [big_strip[-1], s[0]]
174 total_hits += cache.fetch_strip(glue)
178 total_hits += cache.fetch_strip(s)
181 # Add loose faces to the end. This wastes space, using five
182 # elements per triangle and six elements per quad.
184 order = (-1, -2, 0, 1)
186 order = (0, 1, -1, -2)
187 vertices = [f.vertices[i] for i in order[:len(f.vertices)]]
190 glue = [big_strip[-1], vertices[0]]
193 total_hits += cache.fetch_strip(glue)
195 big_strip += vertices
197 total_hits += cache.fetch_strip(vertices)
204 def export(self, context, fn):
206 objs = context.selected_objects
208 objs = [context.active_object]
211 raise Exception("Nothing to export")
214 raise Exception("Can only export Mesh data")
216 from .mesh import Mesh
217 from .util import Progress
219 progress = Progress()
220 progress.set_task("Preparing", 0.0, 0.0)
225 bmesh = o.to_mesh(context.scene, True, "PREVIEW")
226 bmeshes.append(bmesh)
230 mesh.splice(Mesh(bmesh))
232 progress.set_task("Smoothing", 0.05, 0.35)
233 if self.smoothing=="NONE":
235 mesh.split_smooth(progress)
237 if self.smoothing!="BLENDER":
238 mesh.compute_normals()
240 if self.export_groups:
241 mesh.sort_vertex_groups(self.max_groups)
243 # Create a mapping from vertex group indices to bone indices
244 group_index_map = dict((i, i) for i in range(len(objs[0].vertex_groups)))
245 if objs[0].parent and objs[0].parent.type=="ARMATURE":
246 armature = objs[0].parent.data
247 bone_indices = dict((armature.bones[i].name, i) for i in range(len(armature.bones)))
248 for g in objs[0].vertex_groups:
249 if g.name in bone_indices:
250 group_index_map[g.index] = bone_indices[g.name]
252 if self.material_tex and mesh.materials:
253 mesh.generate_material_uv()
256 if mesh.uv_layers and self.export_uv!="NONE":
257 # Figure out which UV layers to export
258 if self.export_uv=="UNIT0":
259 if mesh.uv_layers[0].unit==0:
262 texunits = range(len(mesh.uv_layers))
263 texunits = [(i, mesh.uv_layers[i]) for i in texunits]
264 texunits = [u for u in texunits if not u[1].hidden]
267 # TBN coordinates must be generated before vertices are split by any other layer
268 uv_names = [u.name for i, u in texunits]
269 if self.tbn_uvtex in uv_names:
270 tbn_index = uv_names.index(self.tbn_uvtex)
271 unit = texunits[tbn_index]
272 del texunits[tbn_index]
273 texunits.insert(0, unit)
275 for i, u in texunits:
276 progress.set_task("Splitting UVs", 0.35+0.3*i/len(texunits), 0.35+0.3*(i+1)/len(texunits))
277 mesh.split_uv(i, progress)
278 if self.tbn_vecs and u.name==self.tbn_uvtex:
287 progress.set_task("Creating strips", 0.65, 0.95)
288 strips, loose = self.stripify(mesh, progress)
290 progress.set_task("Writing file", 0.95, 1.0)
292 out_file = OutFile(fn)
294 out_file.begin("mesh")
298 for i, u in texunits:
300 fmt.append("TEXCOORD2")
302 fmt.append("TEXCOORD2_%d"%u.unit)
304 fmt += ["TANGENT3", "BINORMAL3"]
305 if self.export_groups:
306 fmt.append("ATTRIB%d_5"%(self.max_groups*2))
307 fmt.append("VERTEX3")
308 out_file.begin("vertices", *fmt)
310 uvs = [None]*len(texunits)
314 for v in mesh.vertices:
316 out_file.write("normal3", *v.normal)
318 for i, u in texunits:
321 out_file.write("texcoord2", *v.uvs[i])
323 out_file.write("multitexcoord2", u.unit, *v.uvs[i])
327 out_file.write("tangent3", *v.tan)
330 out_file.write("binormal3", *v.bino)
332 if self.export_groups:
333 group_attr = [(group_index_map[g.group], g.weight*v.group_weight_scale) for g in v.groups[:self.max_groups]]
334 while len(group_attr)<self.max_groups:
335 group_attr.append((0, 0.0))
336 group_attr = list(itertools.chain(*group_attr))
337 if group_attr!=group:
338 out_file.write("attrib%d"%len(group_attr), 5, *group_attr)
340 out_file.write("vertex3", *v.co)
343 out_file.begin("batch", "TRIANGLE_STRIP")
347 indices.append(v.index)
349 out_file.write("indices", *indices)
352 out_file.write("indices", *indices)
356 out_file.begin("batch", "TRIANGLES")
358 for i in range(2, len(f.vertices)):
359 out_file.write("indices", f.vertices[0].index, f.vertices[i-1].index, f.vertices[i].index)
362 if self.export_lines and mesh.lines:
363 out_file.write("batch", "LINES")
365 out_file.write("indices", l.vertices[0].index, l.vertices[1].index)
370 out_file.begin("technique")
371 out_file.begin("pass", '""')
378 if self.material_tex:
379 out_file.begin("material")
380 out_file.write("diffuse", 1.0, 1.0, 1.0, 1.0)
383 for u in mesh.uv_layers:
384 if u.name=="material_tex":
386 out_file.begin("texunit", index)
387 out_file.begin("texture2d")
388 out_file.write("min_filter", "NEAREST")
389 out_file.write("mag_filter", "NEAREST")
390 out_file.write("storage", "RGB", len(mesh.materials), 1)
392 for m in mesh.materials:
393 color = [int(cm(c)*255) for c in m.diffuse_color]
394 texdata += "\\x%02X\\x%02X\\x%02X"%tuple(color)
396 out_file.write("raw_data", texdata)
400 mat = mesh.materials[0]
401 out_file.begin("material")
402 if any((s and s.use_map_color_diffuse) for s in mat.texture_slots):
403 out_file.write("diffuse", 1.0, 1.0, 1.0, 1.0)
404 amb = cm(mat.ambient)
405 out_file.write("ambient", amb, amb, amb, 1.0)
407 diff = mat.diffuse_color
408 out_file.write("diffuse", cm(diff.r), cm(diff.g), cm(diff.b), 1.0)
409 amb = diff*mat.ambient
410 out_file.write("ambient", cm(amb.r), cm(amb.g), cm(amb.b), 1.0)
411 spec = mat.specular_color*mat.specular_intensity
412 out_file.write("specular", spec.r, spec.g, spec.b, 1.0)
413 out_file.write("shininess", mat.specular_hardness);
416 if self.textures!="NONE":
417 for slot in mesh.materials[0].texture_slots:
422 if tex.type!="IMAGE":
426 for u in mesh.uv_layers:
427 if u.name==slot.uv_layer:
430 index = mesh.uv_layers[0].unit
432 out_file.begin("texunit", index)
433 if self.textures=="INLINE":
434 out_file.begin("texture2d")
435 out_file.write("min_filter", "LINEAR")
436 out_file.write("storage", "RGBA", tex.image.size[0], tex.image.size[1])
438 for p in tex.image.pixels:
439 texdata += "\\x%02X"%int(p*255)
441 out_file.write("raw_data", texdata)
444 out_file.write("texture", '"%s"'%tex.image.name)
450 progress.set_task("Done", 1.0, 1.0)
453 bpy.data.meshes.remove(m)