X-Git-Url: http://git.tdb.fi/?a=blobdiff_plain;f=mesh_export.py;h=4c18bb4870e72122f7879cda206f9b50210fd3cb;hb=40020924ebc8b2a82b6f6cea3dc04561121cebfd;hp=d63206dd4184eb9bdd0abc2f156062dd779ab7bf;hpb=18afcc17fc2c4d722504f2ad006d5066c720279c;p=libs%2Fgl.git diff --git a/mesh_export.py b/mesh_export.py index d63206dd..4c18bb48 100644 --- a/mesh_export.py +++ b/mesh_export.py @@ -12,15 +12,21 @@ import math import bpy import Blender +def make_edge_key(i1, i2): + return (min(i1, i2), max(i1, i2)) + + class Edge: def __init__(self, me): if me.__class__==Edge: - self._medge=me._medge - self.smooth=me.smooth + self._medge = me._medge + self.v1 = me.v1 + self.v2 = me.v2 + self.smooth = me.smooth else: - self._medge=me - self.smooth=False - self.faces=[] + self._medge = me + self.smooth = False + self.faces = [] def __getattr__(self, attr): return getattr(self._medge, attr) @@ -29,9 +35,9 @@ class Edge: if len(self.faces)!=2: return - d=Blender.Mathutils.DotVecs(self.faces[0].no, self.faces[1].no) + d = Blender.Mathutils.DotVecs(self.faces[0].no, self.faces[1].no) if (d>limit and self.faces[0].smooth and self.faces[1].smooth) or d>0.999: - self.smooth=1 + self.smooth = True def other_face(self, f): if f.index==self.faces[0].index: @@ -46,378 +52,634 @@ class Edge: class Vertex: def __init__(self, mv): if mv.__class__==Vertex: - self._mvert=mv._mvert - self.uv=mv.uv + self._mvert = mv._mvert + self.no = mv.no + self.uv = mv.uv else: - self._mvert=mv - self.uv=None - self.orig_index=self._mvert.index + self._mvert = mv + self.uv = None + self.flag = False + self.faces = [] + self.tan = None + self.bino = None def __getattr__(self, attr): return getattr(self._mvert, attr) + def __cmp__(self, other): + if other is None: + return 1 + return cmp(self.index, other.index) + + def __str__(self): + return ""%(self.index, self.co[0], self.co[1], self.co[2], self.no[0], self.no[1], self.no[2]) + + __repr__ = __str__ + class Face: def __init__(self, mf): - self._mface=mf - self.smooth_group=None - self.edges=[] - self.verts=[v for v in mf.verts] - self.flag=False + self._mface = mf + self.edges = [] + self.verts = [v for v in mf.verts] + self.flag = False def __getattr__(self, attr): return getattr(self._mface, attr) - def get_vertices_from(self, reverse, *vt): - verts=self.verts[:] - if reverse: - verts.reverse() - indices=[u.index for u in vt] - flags=[(v.index in indices) for v in verts] - l=len(verts) + def __cmp__(self, other): + if other is None: + return 1 + return cmp(self.index, other.index) + + def __str__(self): + return ""%(self.index, " ".join([str(v.index) for v in self.verts])) + + __repr__ = __str__ + + def pivot_vertices(self, *vt): + flags = [(v in vt) for v in self.verts] + l = len(self.verts) for i in range(l): if flags[i] and not flags[(i+l-1)%l]: - return verts[i:]+verts[:i] + return self.verts[i:]+self.verts[:i] def get_edge(self, v1, v2): - i1=v1.index - i2=v2.index + key = make_edge_key(v1.index, v2.index) for e in self.edges: - if (e.v1.index==i1 or e.v1.index==i2) and (e.v2.index==i1 or e.v2.index==i2): + if e.key==key: return e + raise KeyError, "No edge %s"%(key,) -class SmoothGroup: - def __init__(self, index): - self.index=index - self.faces=[] - self.verts=[] +class Line: + def __init__(self, e): + self.edge = e + self.verts = [e.v1, e.v2] + self.flag = False - def find_vertices(self): - vert_map={} - for f in self.faces: - for i in range(len(f.verts)): - v=f.verts[i] - if v.index not in vert_map: - vt=Vertex(v) - vt.index=len(self.verts) - self.verts.append(vt) - - vert_map[v.index]=vt - vt.no=Blender.Mathutils.Vector(f.no) - else: - vt=vert_map[v.index] - vt.no+=f.no - f.verts[i]=vt + def __str__(self): + return ""%(self.verts[0].index, self.verts[1].index) + + __repr__ = __str__ - for v in self.verts: - v.no.normalize() - def separate_uv(self): - copies={} +class Mesh: + def __init__(self, m): + self._mesh = m + self.verts = [Vertex(v) for v in m.verts] + self.faces = [Face(f) for f in m.faces] + for f in self.faces: for i in range(len(f.verts)): - v=f.verts[i] - if not v.uv: - v.uv=f.uv[i] - elif f.uv[i]!=v.uv: - if v.index not in copies: - copies[v.index]=[] - - vt=None - for w in copies[v.index]: - if w.uv==f.uv[i]: - vt=w - break - - if not vt: - vt=Vertex(v) - vt.index=len(self.verts) - vt.uv=f.uv[i] - self.verts.append(vt) - copies[v.index].append(vt) - - f.verts[i]=vt - - def create_edges(self): - edge_map={} - for f in self.faces: - vert_map=dict([(v.orig_index, v) for v in f.verts]) - for i in range(len(f.edges)): - v1=vert_map[f.edges[i].v1.index] - v2=vert_map[f.edges[i].v2.index] - key=tuple(sorted((v1.index, v2.index))) - - if key in edge_map: - e=edge_map[key] - else: - e=Edge(f.edges[i]) - edge_map[key]=e - e.v1=v1 - e.v2=v2 - e.key=key + f.verts[i] = self.verts[f.verts[i].index] + f.verts[i].faces.append(f) - f.edges[i]=e - e.faces.append(f) + self.edges = dict([(e.key, Edge(e)) for e in m.edges]) + for f in self.faces: + for k in f.edge_keys: + e = self.edges[k] + e.faces.append(self.faces[f.index]) + f.edges.append(e) + self.lines = [Line(e) for e in self.edges.itervalues() if not e.faces] -class Exporter: - def __init__(self, fn): - self.filename=fn - if fn==None: - self.out_file=sys.stdout + if m.mode&Blender.Mesh.Modes.AUTOSMOOTH: + smooth_limit = math.cos(m.degr*math.pi/180) else: - self.out_file=file(fn, "w") - self.use_strips=True - self.use_degen_tris=True - self.debug=False - self.strip_debug=False - - def find_smooth_group(self, face, sg): - face.smooth_group=sg - sg.faces.append(face) - queue=[face] - while queue: - cur=queue.pop(0) - for e in cur.edges: + smooth_limit = -1 + + for e in self.edges.itervalues(): + e.v1 = self.verts[e.v1.index] + e.v2 = self.verts[e.v2.index] + e.check_smooth(smooth_limit) + + def __getattr__(self, attr): + return getattr(self._mesh, attr) + + def splice(self, other): + offset = len(self.verts) + for v in other.verts: + v.index += offset + self.verts.append(v) + + offset = len(self.faces) + for f in other.faces: + f.index += offset + self.faces.append(f) + + for e in other.edges.itervalues(): + e.key = make_edge_key(e.v1.index, e.v2.index) + self.edges[e.key] = e + + self.lines += other.lines + + def split_vertices(self, find_group_func, debug): + groups = [] + for v in self.verts: + for f in v.faces: + f.flag = False + + vg = [] + for f in v.faces: + if not f.flag: + vg.append(find_group_func(v, f)) + + groups.append(vg) + + for i in range(len(self.verts)): + if len(groups[i])==1: + continue + + if debug: + print "Vertex %s has %d groups"%(self.verts[i], len(groups[i])) + + for g in groups[i][1:]: + v = Vertex(self.verts[i]) + v.index = len(self.verts) + self.verts.append(v) + + if debug: + print " -> %d %s"%(v.index, [f.index for f in g]) + + for f in g: + for j in range(len(f.edges)): + e = f.edges[j] + + if e.v1!=self.verts[i] and e.v2!=self.verts[i]: + continue + + if debug: + print " Splitting edge %s with faces %s"%(e.key, e.faces) + + if e.other_face(f) not in g and len(e.faces)>=2: + k = e.faces.index(f) + e.faces.remove(f) + e = Edge(e) + f.edges[j] = e + e.faces.append(f) + else: + del self.edges[e.key] + + if e.v1==self.verts[i]: + e.v1 = v + elif e.v2==self.verts[i]: + e.v2 = v + + e.key = make_edge_key(e.v1.index, e.v2.index) + self.edges[e.key] = e + + self.verts[i].faces.remove(f) + f.verts[f.verts.index(self.verts[i])] = v + v.faces.append(f) + + def split_smooth(self, debug = False): + self.split_vertices(self.find_smooth_group, debug) + + def split_uv(self, debug = False): + self.split_vertices(self.find_uv_group, debug) + + def find_smooth_group(self, vert, face): + face.flag = True + queue = [face] + + for f in queue: + for e in f.edges: + other = e.other_face(f) + #if not other or other.index not in face_indices: + if other not in vert.faces: + continue + if e.smooth: - other=e.other_face(cur) - if other and not other.smooth_group: - other.smooth_group=sg - sg.faces.append(other) + if not other.flag: + other.flag = True queue.append(other) - def create_strip(self, face, reverse): - edge=None + return queue + + def find_uv_group(self, vert, face): + uv = face.uv[face.verts.index(vert)] + face.flag = True + group = [face] + for f in vert.faces: + if not f.flag and f.uv[f.verts.index(vert)]==uv: + f.flag = True + group.append(f) + return group + + def compute_normals(self): + for v in self.verts: + if v.faces: + v.no = Blender.Mathutils.Vector() + for f in v.faces: + v.no += f.no + v.no.normalize() + else: + # XXX Should use edges to compute normal + v.no = Blender.Mathutils.Vector(0, 0, 1) + + def compute_uv(self): + for v in self.verts: + if v.faces: + v.uv = v.faces[0].uv[v.faces[0].verts.index(v)] + + def compute_tbn(self): + for v in self.verts: + v.tan = Blender.Mathutils.Vector() + v.bino = Blender.Mathutils.Vector() + for f in v.faces: + fverts = f.pivot_vertices(False, v) + v1 = fverts[1] + v2 = fverts[-1] + du1 = v1.uv[0]-v.uv[0] + du2 = v2.uv[0]-v.uv[0] + dv1 = v1.uv[1]-v.uv[1] + dv2 = v2.uv[1]-v.uv[1] + div = du1*dv2-du2*dv1 + edge1 = fverts[1].co-fverts[0].co + edge2 = fverts[-1].co-fverts[0].co + v.tan += (edge1*dv2-edge2*dv1)/div + v.bino += (edge2*du1-edge1*du2)/div + v.tan.normalize() + v.bino.normalize() + + def create_strip(self, face, max_len, debug): + edge = None for e in face.edges: - other=e.other_face(face) - if other and other.smooth_group.index==face.smooth_group.index and not other.flag: - edge=e + other = e.other_face(face) + if other and not other.flag: + edge = e break if not edge: return None - if self.strip_debug: - print "Starting strip from %s, edge %s, reverse=%s"%([v.index for v in face.verts], (edge.v1.index, edge.v2.index), reverse) + if debug: + print "Starting strip from %s, edge %s"%([v.index for v in face.verts], (edge.v1.index, edge.v2.index)) - verts=face.get_vertices_from(reverse, edge.v1, edge.v2) + verts = face.pivot_vertices(edge.v1, edge.v2) if len(verts)==3: - result=[verts[-1], verts[0]] + result = [verts[-1], verts[0]] else: - result=[verts[-2], verts[-1]] + result = [verts[-2], verts[-1]] while 1: - verts=face.get_vertices_from(reverse, *result[-2:]) - k=len(result)%2 - if self.strip_debug: - print " Adding %s"%[v.index for v in verts] + if debug: + print " Adding %s"%face + + verts = face.pivot_vertices(*result[-2:]) + k = len(result)%2 - face.flag=True + face.flag = True if len(verts)==4 and not k: result.append(verts[3]) result.append(verts[2]) if len(verts)==4 and k: result.append(verts[3]) - edge=face.get_edge(*result[-2:]) + if len(result)>=max_len: + if debug: + print " Max length exceeded" + break - if self.strip_debug: - print " Next edge is %s"%((edge.v1.index, edge.v2.index), ) + edge = face.get_edge(*result[-2:]) - next=edge.other_face(face) - if not next or next.smooth_group.index!=face.smooth_group.index or next.flag: + if debug: + print " Next edge is %s"%(edge.key, ) + + next = edge.other_face(face) + if not next or next.flag: break - face=next + face = next - if self.strip_debug: + if debug: print " %s"%[v.index for v in result] return result - def get_locality(self, strip): - total=0 - for i in range(1, len(strip)): - if strip[i].index!=strip[i-1].index: - total+=1.0/(abs(strip[i].index-strip[i-1].index)) - return total/len(strip) - def export(self): - scene=bpy.data.scenes.active +class VertexCache: + def __init__(self, size): + self.size = size + self.slots = [-1]*self.size + + def fetch(self, v): + hit = v.index in self.slots + if hit: + self.slots.remove(v.index) + self.slots.append(v.index) + if not hit: + del self.slots[0] + return hit + + def fetch_strip(self, strip): + hits = 0 + for v in strip: + if self.fetch(v): + hits += 1 + return hits + + def test_strip(self, strip): + hits = 0 + for i in range(len(strip)): + if i>=self.size: + break + if strip[i].index in self.slots[i:]: + hits += 1 + return hits - obj=scene.objects.active - if obj.getType()!="Mesh": - raise Exception, "Can only export Mesh data" - mesh=obj.getData(mesh=True) +class Exporter: + def __init__(self, fn): + self.filename = fn + if fn==None: + self.out_file = sys.stdout + else: + self.out_file = file(fn, "w") + self.use_strips = True + self.use_degen_tris = True + self.max_strip_len = 1024 + self.optimize_cache = False + self.cache_size = 64 + self.export_lines = True + self.tbn_vecs = False + self.compound = False + self.debug = False + self.strip_debug = False + self.split_debug = False + + def stripify(self, mesh): + for f in mesh.faces: + f.flag = False + + strips = [] - faces=[Face(f) for f in mesh.faces] + while 1: + best = 5 + face = None + for f in mesh.faces: + if f.flag: + continue + score = 0 + for e in f.edges: + other = e.other_face(f) + if other and not other.flag: + score += 1 + if score>0 and scorebest_hits: + best = i + best_hits = hits + + s = strips[best] + + if big_strip: + glue = [big_strip[-1], s[0]] + if len(big_strip)%2: + glue += [s[0]] + + big_strip += glue + if cache: + total_hits += cache.fetch_strip(glue) + + big_strip += s + if cache: + total_hits += cache.fetch_strip(s) + + del strips[best] + + for f in loose: + if len(big_strip)%2: + order = (-1, -2, 0, 1) + else: + order = (0, 1, -1, -2) + verts = [f.verts[i] for i in order[:len(f.verts)]] + if big_strip: + glue = [big_strip[-1], verts[0]] + big_strip += glue + if cache: + total_hits += cache.fetch_strip(glue) + big_strip += verts + if cache: + total_hits += cache.fetch_strip(verts) + + strips = [big_strip] + loose = [] + + if self.debug: + nind = len(big_strip) + print "Big strip has %d indices"%nind + if self.optimize_cache: + print "%d cache hits"%total_hits if self.debug: - print "%d smooth groups:"%len(smooth_groups) - for i in range(len(smooth_groups)): - sg=smooth_groups[i] - print " %d: %d faces, %d vertices"%(i, len(sg.faces), len(sg.verts)) - print "%d vertices total"%len(verts) + ntris = sum([len(f.verts)-2 for f in mesh.faces]) + print "%.2f indices per triangle"%(float(nind)/max(ntris, 1)) - strips=[] - if self.use_strips: - while 1: - best=5 - face=None - for f in faces: - if f.flag: - continue - score=0 - for e in f.edges: - other=e.other_face(f) - if other and other.smooth_group.index==f.smooth_group.index and not other.flag: - score+=1 - if score>0 and score