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