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