]> git.tdb.fi Git - libs/gl.git/blob - mesh_export.py
Try to generate longer strips
[libs/gl.git] / mesh_export.py
1 #!BPY
2 # $Id$
3
4 """
5 Name: 'MSP GL Mesh (.mesh)...'
6 Blender: 244
7 Group: 'Export'
8 """
9
10 import sys
11 import math
12 import bpy
13 import Blender
14
15 class Edge:
16         def __init__(self, me):
17                 if me.__class__==Edge:
18                         self._medge=me._medge
19                         self.smooth=me.smooth
20                 else:
21                         self._medge=me
22                         self.smooth=False
23                 self.faces=[]
24
25         def __getattr__(self, attr):
26                 return getattr(self._medge, attr)
27
28         def check_smooth(self, limit):
29                 if len(self.faces)!=2:
30                         return
31
32                 d=Blender.Mathutils.DotVecs(self.faces[0].no, self.faces[1].no)
33                 if (d>limit and self.faces[0].smooth and self.faces[1].smooth) or d>0.999:
34                         self.smooth=1
35
36         def other_face(self, f):
37                 if f.index==self.faces[0].index:
38                         if len(self.faces)>=2:
39                                 return self.faces[1]
40                         else:
41                                 return None
42                 else:
43                         return self.faces[0]
44
45
46 class Vertex:
47         def __init__(self, mv):
48                 if mv.__class__==Vertex:
49                         self._mvert=mv._mvert
50                         self.uv=mv.uv
51                 else:
52                         self._mvert=mv
53                         self.uv=None
54                 self.orig_index=self._mvert.index
55
56         def __getattr__(self, attr):
57                 return getattr(self._mvert, attr)
58
59
60 class Face:
61         def __init__(self, mf):
62                 self._mface=mf
63                 self.smooth_group=None
64                 self.edges=[]
65                 self.verts=[v for v in mf.verts]
66                 self.flag=False
67
68         def __getattr__(self, attr):
69                 return getattr(self._mface, attr)
70
71         def get_vertices_from(self, *vt):
72                 indices=[u.index for u in vt]
73                 flags=[(v.index in indices) for v in self.verts]
74                 l=len(self.verts)
75                 for i in range(l):
76                         if flags[i] and not flags[(i+l-1)%l]:
77                                 return self.verts[i:]+self.verts[:i]
78
79         def get_edge(self, v1, v2):     
80                 i1=v1.index
81                 i2=v2.index
82                 for e in self.edges:
83                         if (e.v1.index==i1 or e.v1.index==i2) and (e.v2.index==i1 or e.v2.index==i2):
84                                 return e
85
86
87 class SmoothGroup:
88         def __init__(self, index):
89                 self.index=index
90                 self.faces=[]
91                 self.verts=[]
92
93         def find_vertices(self):
94                 vert_map={}
95                 for f in self.faces:
96                         for i in range(len(f.verts)):
97                                 v=f.verts[i]
98                                 if v.index not in vert_map:
99                                         vt=Vertex(v)
100                                         vt.index=len(self.verts)
101                                         self.verts.append(vt)
102
103                                         vert_map[v.index]=vt
104                                         vt.no=Blender.Mathutils.Vector(f.no)
105                                 else:
106                                         vt=vert_map[v.index]
107                                         vt.no+=f.no
108                                 f.verts[i]=vt
109
110                 for v in self.verts:
111                         v.no.normalize()
112
113         def separate_uv(self):
114                 copies={}
115                 for f in self.faces:
116                         for i in range(len(f.verts)):
117                                 v=f.verts[i]
118                                 if not v.uv:
119                                         v.uv=f.uv[i]
120                                 elif f.uv[i]!=v.uv:
121                                         if v.index not in copies:
122                                                 copies[v.index]=[]
123
124                                         vt=None
125                                         for w in copies[v.index]:
126                                                 if w.uv==f.uv[i]:
127                                                         vt=w
128                                                         break
129
130                                         if not vt:
131                                                 vt=Vertex(v)
132                                                 vt.index=len(self.verts)
133                                                 vt.uv=f.uv[i]
134                                                 self.verts.append(vt)
135                                                 copies[v.index].append(vt)
136
137                                         f.verts[i]=vt
138
139         def create_edges(self):
140                 edge_map={}
141                 for f in self.faces:
142                         vert_map=dict([(v.orig_index, v) for v in f.verts])
143                         for i in range(len(f.edges)):
144                                 v1=vert_map[f.edges[i].v1.index]
145                                 v2=vert_map[f.edges[i].v2.index]
146                                 key=tuple(sorted((v1.index, v2.index)))
147
148                                 if key in edge_map:
149                                         e=edge_map[key]
150                                 else:
151                                         e=Edge(f.edges[i])
152                                         edge_map[key]=e
153                                         e.v1=v1
154                                         e.v2=v2
155                                         e.key=key
156
157                                 f.edges[i]=e
158                                 e.faces.append(f)
159
160
161 class Exporter:
162         def __init__(self, fn):
163                 self.filename=fn
164                 if fn==None:
165                         self.out_file=sys.stdout
166                 else:
167                         self.out_file=file(fn, "w")
168                 self.use_strips=True
169                 self.use_degen_tris=True
170                 self.debug=False
171                 self.strip_debug=False
172
173         def find_smooth_group(self, face, sg):
174                 face.smooth_group=sg
175                 sg.faces.append(face)
176                 queue=[face]
177                 while queue:
178                         cur=queue.pop(0)
179                         for e in cur.edges:
180                                 if e.smooth:
181                                         other=e.other_face(cur)
182                                         if other and not other.smooth_group:
183                                                 other.smooth_group=sg
184                                                 sg.faces.append(other)
185                                                 queue.append(other)
186
187         def create_strip(self, face):
188                 edge=None
189                 for e in face.edges:
190                         other=e.other_face(face)
191                         if other and other.smooth_group.index==face.smooth_group.index and not other.flag:
192                                 edge=e
193                                 break
194
195                 if not edge:
196                         return None
197
198                 if self.strip_debug:
199                         print "Starting strip from %s, edge %s"%([v.index for v in face.verts], (edge.v1.index, edge.v2.index))
200
201                 verts=face.get_vertices_from(edge.v1, edge.v2)
202                 if len(verts)==3:
203                         result=[verts[-1], verts[0]]
204                 else:
205                         result=[verts[-2], verts[-1]]
206
207                 while 1:
208                         verts=face.get_vertices_from(*result[-2:])
209                         k=len(result)%2
210                         if self.strip_debug:
211                                 print "  Adding %s"%[v.index for v in verts]
212
213                         face.flag=True
214                         if len(verts)==4 and not k:
215                                 result.append(verts[3])
216                         result.append(verts[2])
217                         if len(verts)==4 and k:
218                                 result.append(verts[3])
219
220                         edge=face.get_edge(*result[-2:])
221
222                         if self.strip_debug:
223                                 print "  Next edge is %s"%((edge.v1.index, edge.v2.index), )
224
225                         next=edge.other_face(face)
226                         if not next or next.smooth_group.index!=face.smooth_group.index or next.flag:
227                                 break
228                         face=next
229
230                 if self.strip_debug:
231                         print "  %s"%[v.index for v in result]
232
233                 return result
234
235         def get_locality(self, strip):
236                 total=0
237                 for i in range(1, len(strip)):
238                         if strip[i].index!=strip[i-1].index:
239                                 total+=1.0/(abs(strip[i].index-strip[i-1].index))
240                 return total/len(strip)
241
242         def export(self):
243                 scene=bpy.data.scenes.active
244
245                 obj=scene.objects.active
246                 if obj.getType()!="Mesh":
247                         raise Exception, "Can only export Mesh data"
248
249                 mesh=obj.getData(mesh=True)
250
251                 faces=[Face(f) for f in mesh.faces]
252
253                 edges=dict([(e.key, Edge(e)) for e in mesh.edges])
254                 for f in faces:
255                         for e in f.edge_keys:
256                                 edges[e].faces.append(f)
257                                 f.edges.append(edges[e])
258
259                 smooth_limit=math.cos(mesh.degr*math.pi/180)
260                 for e in edges.itervalues():
261                         e.check_smooth(smooth_limit)
262
263                 if self.debug:
264                         ntris=sum([len(f.verts)-2 for f in faces])
265                         print "%d faces (%d triangles), %d edges"%(len(faces), ntris, len(edges))
266
267                 smooth_groups=[]
268                 for f in faces:
269                         if not f.smooth_group:
270                                 sg=SmoothGroup(len(smooth_groups))
271                                 smooth_groups.append(sg)
272                                 self.find_smooth_group(f, sg)
273
274                 for sg in smooth_groups:
275                         sg.find_vertices()
276                         if mesh.faceUV:
277                                 sg.separate_uv()
278                         sg.create_edges()
279
280                 verts=[]
281                 for sg in smooth_groups:
282                         for v in sg.verts:
283                                 v.index=len(verts)
284                                 verts.append(v)
285
286                 if self.debug:
287                         print "%d smooth groups:"%len(smooth_groups)
288                         for i in range(len(smooth_groups)):
289                                 sg=smooth_groups[i]
290                                 print "  %d: %d faces, %d vertices"%(i, len(sg.faces), len(sg.verts))
291                         print "%d vertices total"%len(verts)
292
293                 strips=[]
294                 if self.use_strips:
295                         while 1:
296                                 best=5
297                                 face=None
298                                 for f in faces:
299                                         if f.flag:
300                                                 continue
301                                         score=0
302                                         for e in f.edges:
303                                                 other=e.other_face(f)
304                                                 if other and other.smooth_group.index==f.smooth_group.index and not other.flag:
305                                                         score+=1
306                                         if score>0 and score<best:
307                                                 face=f
308                                                 best=score
309                                 if not face:
310                                         break
311                                 strip=self.create_strip(face)
312                                 if strip:
313                                         strips.append(strip)
314
315                         if self.debug:
316                                 print "%d strips:"%len(strips)
317                                 for i in range(len(strips)):
318                                         print "  %d: %d indices"%(i, len(strips[i]))
319                                 print "%d loose faces"%len([f for f in faces if not f.flag])
320                                 nind=sum([len(s) for s in strips])+sum([len(f.verts) for f in faces if not f.flag])
321                                 print "%d indices total"%nind
322
323                         if self.use_degen_tris:
324                                 big_strip=[]
325                                 while strips:
326                                         strip=None
327                                         if big_strip:
328                                                 for i in range(len(strips)):
329                                                         if strips[i][0].index==big_strip[-1].index:
330                                                                 strip=strips.pop(i)
331                                                                 break
332                                         if not strip:
333                                                 strip=strips.pop(0)
334                                         if big_strip:
335                                                 if strip[0].index!=big_strip[-1].index:
336                                                         big_strip+=[big_strip[-1]]
337                                                 big_strip+=[strip[0]]
338                                         big_strip+=strip
339
340                                 for f in faces:
341                                         if not f.flag:
342                                                 if big_strip:
343                                                         big_strip+=[big_strip[-1], f.verts[0]]
344                                                 big_strip+=[f.verts[i] for i in (0, 1, -1)]
345                                                 if len(f.verts)==4:
346                                                         big_strip.append(f.verts[-2])
347                                                 f.flag=True
348
349                                 strips=[big_strip]
350                                 
351                                 if self.debug:
352                                         nind=len(big_strip)
353                                         print "Big strip has %d indices"%len(big_strip)
354
355                 if self.debug:
356                         print "%.2f vertices per triangle"%(float(nind)/ntris)
357                         print "Locality before optimization: "+" ".join(["%.3f"%self.get_locality(s) for s in strips])
358
359                 used=[False]*len(verts)
360                 i=0
361                 for s in strips:
362                         for v in s:
363                                 if not used[v.index]:
364                                         used[v.index]=True
365                                         v.index=i
366                                         i+=1
367
368                 verts.sort(cmp=lambda x,y: cmp(x.index, y.index))
369
370                 if self.debug:
371                         print "Locality after optimization: "+" ".join(["%.3f"%self.get_locality(s) for s in strips])
372
373                 self.out_file.write("vertices NORMAL3")
374                 if mesh.faceUV:
375                         self.out_file.write("_TEXCOORD2")
376                 self.out_file.write("_VERTEX3\n{\n")
377                 norm=None
378                 uv=None
379                 for v in verts:
380                         if v.no!=norm:
381                                 self.out_file.write("\tnormal3 %f %f %f;\n"%tuple(v.no))
382                                 norm=v.no
383                         if v.uv!=uv:
384                                 self.out_file.write("\ttexcoord2 %f %f;\n"%tuple(v.uv))
385                                 uv=v.uv
386                         self.out_file.write("\tvertex3 %f %f %f;\n"%tuple(v.co))
387                 self.out_file.write("};\n")
388                 for s in strips:
389                         self.out_file.write("batch TRIANGLE_STRIP\n{\n\tindices")
390                         n=0
391                         for v in s:
392                                 self.out_file.write(" %u"%v.index)
393                                 n+=1;
394                                 if n%32==0:
395                                         self.out_file.write(";\n\tindices")
396                         self.out_file.write(";\n};\n")
397
398                 first=True
399                 for f in faces:
400                         if not f.flag:
401                                 if first:
402                                         self.out_file.write("batch TRIANGLES\n{\n")
403                                         first=False
404                                 for i in range(2, len(f.verts)):
405                                         self.out_file.write("\tindices %u %u %u;\n"%(f.verts[0].index, f.verts[i-1].index, f.verts[i].index))
406                 if not first:
407                         self.out_file.write("};\n")
408
409
410 class FrontEnd:
411         def run(self):
412                 #self.export(None)
413                 Blender.Window.FileSelector(self.export, "Export MSP GL mesh", Blender.sys.makename(ext='.mesh'))
414
415         def export(self, fn):
416                 exp=Exporter(fn)
417                 #exp.use_degen_tris=False
418                 #exp.debug=True
419                 exp.export()
420
421
422 if __name__=="__main__":
423         fe=FrontEnd()
424         fe.run()