]> git.tdb.fi Git - libs/gl.git/blob - mesh_export.py
Add class Technique to share passes between Objects
[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 def make_edge_key(i1, i2):
16         return (min(i1, i2), max(i1, i2))
17
18
19 class Edge:
20         def __init__(self, me):
21                 if me.__class__==Edge:
22                         self._medge=me._medge
23                         self.smooth=me.smooth
24                 else:
25                         self._medge=me
26                         self.smooth=False
27                 self.faces=[]
28
29         def __getattr__(self, attr):
30                 return getattr(self._medge, attr)
31
32         def __cmp__(self, other):
33                 return self is other
34
35         def check_smooth(self, limit):
36                 if len(self.faces)!=2:
37                         return
38
39                 d=Blender.Mathutils.DotVecs(self.faces[0].no, self.faces[1].no)
40                 if (d>limit and self.faces[0].smooth and self.faces[1].smooth) or d>0.999:
41                         self.smooth=True
42
43         def other_face(self, f):
44                 if f.index==self.faces[0].index:
45                         if len(self.faces)>=2:
46                                 return self.faces[1]
47                         else:
48                                 return None
49                 else:
50                         return self.faces[0]
51
52
53 class Vertex:
54         def __init__(self, mv):
55                 if mv.__class__==Vertex:
56                         self._mvert=mv._mvert
57                         self.uv=mv.uv
58                 else:
59                         self._mvert=mv
60                         self.uv=None
61                 self.orig_index=self._mvert.index
62                 self.flag=False
63                 self.faces=[]
64
65         def __getattr__(self, attr):
66                 return getattr(self._mvert, attr)
67
68         def __cmp__(self, other):
69                 return cmp(self.index, other.index)
70
71         def __str__(self):
72                 return "<Vert %d (%.4f, %.4f, %.4f) (%.4f, %.4f, %.4f)>"%(self.index, self.co[0], self.co[1], self.co[2], self.no[0], self.no[1], self.no[2])
73         
74         __repr__=__str__
75
76
77 class Face:
78         def __init__(self, mf):
79                 self._mface=mf
80                 #self.smooth_group=None
81                 self.edges=[]
82                 self.verts=[v for v in mf.verts]
83                 self.flag=False
84
85         def __getattr__(self, attr):
86                 return getattr(self._mface, attr)
87
88         def __cmp__(self, other):
89                 return cmp(self.index, other.index)
90
91         def __str__(self):
92                 return "<Face %d (%s)>"%(self.index, " ".join([str(v.index) for v in self.verts]))
93         
94         __repr__=__str__
95
96         def get_vertices_from(self, reverse, *vt):
97                 verts=self.verts[:]
98                 if reverse:
99                         verts.reverse()
100                 indices=[u.index for u in vt]
101                 flags=[(v.index in indices) for v in verts]
102                 l=len(verts)
103                 for i in range(l):
104                         if flags[i] and not flags[(i+l-1)%l]:
105                                 return verts[i:]+verts[:i]
106
107         def get_edge(self, v1, v2):     
108                 key=make_edge_key(v1.index, v2.index)
109                 for e in self.edges:
110                         if e.key==key:
111                                 return e
112
113
114 class SmoothGroup:
115         def __init__(self, index):
116                 self.index=index
117                 self.faces=[]
118                 self.verts=[]
119
120         def find_vertices(self):
121                 vert_map={}
122                 for f in self.faces:
123                         for i in range(len(f.verts)):
124                                 v=f.verts[i]
125                                 if v.index not in vert_map:
126                                         vt=Vertex(v)
127                                         vt.index=len(self.verts)
128                                         self.verts.append(vt)
129
130                                         vert_map[v.index]=vt
131                                         vt.no=Blender.Mathutils.Vector(f.no)
132                                 else:
133                                         vt=vert_map[v.index]
134                                         vt.no+=f.no
135                                 f.verts[i]=vt
136
137                 for v in self.verts:
138                         v.no.normalize()
139
140         def separate_uv(self):
141                 copies={}
142                 for f in self.faces:
143                         for i in range(len(f.verts)):
144                                 v=f.verts[i]
145                                 if not v.uv:
146                                         v.uv=f.uv[i]
147                                 elif f.uv[i]!=v.uv:
148                                         if v.index not in copies:
149                                                 copies[v.index]=[]
150
151                                         vt=None
152                                         for w in copies[v.index]:
153                                                 if w.uv==f.uv[i]:
154                                                         vt=w
155                                                         break
156
157                                         if not vt:
158                                                 vt=Vertex(v)
159                                                 vt.index=len(self.verts)
160                                                 vt.uv=f.uv[i]
161                                                 self.verts.append(vt)
162                                                 copies[v.index].append(vt)
163
164                                         f.verts[i]=vt
165
166         def create_edges(self):
167                 edge_map={}
168                 for f in self.faces:
169                         vert_map=dict([(v.orig_index, v) for v in f.verts])
170                         for i in range(len(f.edges)):
171                                 v1=vert_map[f.edges[i].v1.index]
172                                 v2=vert_map[f.edges[i].v2.index]
173                                 key=tuple(sorted((v1.index, v2.index)))
174
175                                 if key in edge_map:
176                                         e=edge_map[key]
177                                 else:
178                                         e=Edge(f.edges[i])
179                                         edge_map[key]=e
180                                         e.v1=v1
181                                         e.v2=v2
182                                         e.key=key
183
184                                 f.edges[i]=e
185                                 e.faces.append(f)
186
187
188 class Mesh:
189         def __init__(self, m):
190                 self._mesh=m
191                 self.verts=[Vertex(v) for v in m.verts]
192                 self.faces=[Face(f) for f in m.faces]
193
194                 for f in self.faces:
195                         for i in range(len(f.verts)):
196                                 f.verts[i]=self.verts[f.verts[i].index]
197                                 f.verts[i].faces.append(f)
198
199                 self.edges=dict([(e.key, Edge(e)) for e in m.edges])
200                 for f in self.faces:
201                         for k in f.edge_keys:
202                                 e=self.edges[k]
203                                 e.faces.append(self.faces[f.index])
204                                 f.edges.append(e)
205
206                 smooth_limit=math.cos(m.degr*math.pi/180)
207                 for e in self.edges.itervalues():
208                         e.v1=self.verts[e.v1.index]
209                         e.v2=self.verts[e.v2.index]
210                         e.check_smooth(smooth_limit)
211
212         def __getattr__(self, attr):
213                 return getattr(self._mesh, attr)
214
215         def split_vertices(self, debug=False):
216                 groups=[]
217                 for v in self.verts:
218                         for f in v.faces:
219                                 f.flag=False
220
221                         vg=[]
222                         for f in v.faces:
223                                 if not f.flag:
224                                         vg.append(self.find_group(v, f))
225
226                         groups.append(vg)
227
228                 for i in range(len(self.verts)):
229                         if len(groups[i])==1:
230                                 continue
231
232                         if debug:
233                                 print "Vertex %s has %d groups"%(self.verts[i], len(groups[i]))
234
235                         for g in groups[i][1:]:
236                                 v=Vertex(self.verts[i])
237                                 v.index=len(self.verts)
238                                 self.verts.append(v)
239
240                                 if debug:
241                                         print "  -> %d"%v.index
242
243                                 for f in g:
244                                         for j in range(len(f.edges)):
245                                                 e=f.edges[j]
246
247                                                 if e.v1!=self.verts[i] and e.v2!=self.verts[i]:
248                                                         continue
249
250                                                 if debug:
251                                                         print "  Splitting edge %s with faces %s"%(e.key, e.faces)
252
253                                                 old=e
254                                                 if not e.smooth:
255                                                         if len(e.faces)>=2:
256                                                                 k=e.faces.index(f)
257                                                                 e.faces.remove(f)
258                                                                 e=Edge(e)
259                                                                 f.edges[j]=e
260                                                                 e.faces.append(f)
261                                                         else:
262                                                                 del self.edges[e.key]
263
264                                                 if e.v1==self.verts[i]:
265                                                         e.v1=v
266                                                 elif e.v2==self.verts[i]:
267                                                         e.v2=v
268
269                                                 e.key=make_edge_key(e.v1.index, e.v2.index)
270                                                 if not e.smooth:
271                                                         self.edges[e.key]=e
272
273                                         self.verts[i].faces.remove(f)
274                                         f.verts[f.verts.index(self.verts[i])]=v
275                                         v.faces.append(f)
276
277         def find_group(self, vert, face):
278                 face_indices=[f.index for f in vert.faces]
279
280                 face.flag=True
281                 queue=[face]
282
283                 for f in queue:
284                         for e in f.edges:
285                                 other=e.other_face(f)
286                                 if not other or other.index not in face_indices:
287                                         continue
288
289                                 if e.smooth:
290                                         if not other.flag:
291                                                 other.flag=True
292                                                 queue.append(other)
293
294                 return queue
295
296         def compute_normals(self):
297                 for v in self.verts:
298                         if not v.faces:
299                                 print "WTF?  Vertex %s without faces?"%v
300                         v.no=Blender.Mathutils.Vector()
301                         for f in v.faces:
302                                 v.no+=f.no
303                         v.no.normalize()
304
305         def create_strip(self, face, reverse, debug):
306                 edge=None
307                 for e in face.edges:
308                         other=e.other_face(face)
309                         if other and not other.flag:
310                                 edge=e
311                                 break
312
313                 if not edge:
314                         return None
315
316                 if debug:
317                         print "Starting strip from %s, edge %s, reverse=%s"%([v.index for v in face.verts], (edge.v1.index, edge.v2.index), reverse)
318
319                 verts=face.get_vertices_from(reverse, edge.v1, edge.v2)
320                 if len(verts)==3:
321                         result=[verts[-1], verts[0]]
322                 else:
323                         result=[verts[-2], verts[-1]]
324
325                 while 1:
326                         verts=face.get_vertices_from(reverse, *result[-2:])
327                         k=len(result)%2
328                         if debug:
329                                 print "  Adding %s"%face
330
331                         face.flag=True
332                         if len(verts)==4 and not k:
333                                 result.append(verts[3])
334                         result.append(verts[2])
335                         if len(verts)==4 and k:
336                                 result.append(verts[3])
337
338                         edge=face.get_edge(*result[-2:])
339
340                         if debug:
341                                 print "  Next edge is %s"%(edge.key, )
342
343                         next=edge.other_face(face)
344                         if not next or next.flag:
345                                 break
346                         face=next
347
348                 if debug:
349                         print "  %s"%[v.index for v in result]
350
351                 return result
352
353
354 class Exporter:
355         def __init__(self, fn):
356                 self.filename=fn
357                 if fn==None:
358                         self.out_file=sys.stdout
359                 else:
360                         self.out_file=file(fn, "w")
361                 self.use_strips=True
362                 self.use_degen_tris=True
363                 self.optimize_locality=True
364                 self.debug=False
365                 self.strip_debug=False
366                 self.smooth_debug=False
367
368         """def find_smooth_group(self, face, sg):
369                 face.smooth_group=sg
370                 sg.faces.append(face)
371                 queue=[face]
372                 while queue:
373                         cur=queue.pop(0)
374                         for e in cur.edges:
375                                 if e.smooth:
376                                         other=e.other_face(cur)
377                                         if other and not other.smooth_group:
378                                                 other.smooth_group=sg
379                                                 sg.faces.append(other)
380                                                 queue.append(other)"""
381
382         """def create_strip(self, face, reverse):
383                 edge=None
384                 for e in face.edges:
385                         other=e.other_face(face)
386                         if other and "other.smooth_group.index==face.smooth_group.index" and not other.flag:
387                                 edge=e
388                                 break
389
390                 if not edge:
391                         return None
392
393                 if self.strip_debug:
394                         print "Starting strip from %s, edge %s, reverse=%s"%([v.index for v in face.verts], edge.key, reverse)
395
396                 verts=face.get_vertices_from(reverse, edge.v1, edge.v2)
397                 if len(verts)==3:
398                         result=[verts[-1], verts[0]]
399                 else:
400                         result=[verts[-2], verts[-1]]
401
402                 while 1:
403                         verts=face.get_vertices_from(reverse, *result[-2:])
404                         k=len(result)%2
405                         if self.strip_debug:
406                                 print "  Adding %s"%[v.index for v in verts]
407
408                         face.flag=True
409                         if len(verts)==4 and not k:
410                                 result.append(verts[3])
411                         result.append(verts[2])
412                         if len(verts)==4 and k:
413                                 result.append(verts[3])
414
415                         edge=face.get_edge(*result[-2:])
416
417                         if self.strip_debug:
418                                 print "  Next edge is %s"%((edge.v1.index, edge.v2.index), )
419
420                         next=edge.other_face(face)
421                         if not next or next.flag:
422                                 break
423                         face=next
424
425                 if self.strip_debug:
426                         print "  %s"%[v.index for v in result]
427
428                 return result"""
429
430         def get_locality(self, strip):
431                 total=0
432                 for i in range(1, len(strip)):
433                         if strip[i].index!=strip[i-1].index:
434                                 total+=1.0/(abs(strip[i].index-strip[i-1].index))
435                 return total/len(strip)
436
437         def get_followers(self, strip):
438                 result={}
439                 for i in range(len(strip)-1):
440                         v=strip[i]
441                         n=strip[i+1]
442                         if v.index!=n.index:
443                                 if v.index not in result:
444                                         result[v.index]={}
445                                 if n.index not in result[v.index]:
446                                         result[v.index][n.index]=1
447                                 else:
448                                         result[v.index][n.index]+=1
449                 return result
450
451         def export(self):
452                 scene=bpy.data.scenes.active
453
454                 obj=scene.objects.active
455                 if obj.getType()!="Mesh":
456                         raise Exception, "Can only export Mesh data"
457
458                 mesh=Mesh(obj.getData(mesh=True))
459
460                 if self.debug:
461                         ntris=sum([len(f.verts)-2 for f in mesh.faces])
462                         print "Starting with %d vertices, %d faces (%d triangles) and %d edges"%(len(mesh.verts), len(mesh.faces), ntris, len(mesh.edges))
463
464                 mesh.split_vertices(self.smooth_debug)
465
466                 if self.debug:
467                         ntris=sum([len(f.verts)-2 for f in mesh.faces])
468                         print "After splitting %d vertices, %d faces (%d triangles) and %d edges"%(len(mesh.verts), len(mesh.faces), ntris, len(mesh.edges))
469
470                 mesh.compute_normals()
471
472                 #return
473
474                 """faces=[Face(f) for f in mesh.faces]
475
476                 edges=dict([(e.key, Edge(e)) for e in mesh.edges])
477                 for f in faces:
478                         for e in f.edge_keys:
479                                 edges[e].faces.append(f)
480                                 f.edges.append(edges[e])
481
482                 smooth_limit=math.cos(mesh.degr*math.pi/180)
483                 for e in edges.itervalues():
484                         e.check_smooth(smooth_limit)
485
486                 if self.debug:
487                         ntris=sum([len(f.verts)-2 for f in faces])
488                         print "%d faces (%d triangles), %d edges"%(len(faces), ntris, len(edges))
489
490                 smooth_groups=[]
491                 for f in faces:
492                         if not f.smooth_group:
493                                 sg=SmoothGroup(len(smooth_groups))
494                                 smooth_groups.append(sg)
495                                 self.find_smooth_group(f, sg)
496
497                 for sg in smooth_groups:
498                         sg.find_vertices()
499                         if mesh.faceUV:
500                                 sg.separate_uv()
501                         sg.create_edges()
502
503                 verts=[]
504                 for sg in smooth_groups:
505                         for v in sg.verts:
506                                 v.index=len(verts)
507                                 verts.append(v)
508
509                 if self.debug:
510                         print "%d smooth groups:"%len(smooth_groups)
511                         for i in range(len(smooth_groups)):
512                                 sg=smooth_groups[i]
513                                 print "  %d: %d faces, %d vertices"%(i, len(sg.faces), len(sg.verts))
514                         print "%d vertices total"%len(verts)"""
515
516                 strips=[]
517                 if self.use_strips:
518                         for f in mesh.faces:
519                                 f.flag=False
520
521                         while 1:
522                                 best=5
523                                 face=None
524                                 for f in mesh.faces:
525                                         if f.flag:
526                                                 continue
527                                         score=0
528                                         for e in f.edges:
529                                                 other=e.other_face(f)
530                                                 if other and not other.flag:
531                                                         score+=1
532                                         if score>0 and score<best:
533                                                 face=f
534                                                 best=score
535
536                                 if not face:
537                                         break
538
539                                 strip=mesh.create_strip(face, self.use_degen_tris and sum([len(s) for s in strips])%2, self.strip_debug)
540                                 if strip:
541                                         strips.append(strip)
542
543                         if self.debug:
544                                 print "%d strips:"%len(strips)
545                                 for i in range(len(strips)):
546                                         print "  %d: %d indices"%(i, len(strips[i]))
547                                 print "%d loose faces"%len([f for f in mesh.faces if not f.flag])
548                                 nind=sum([len(s) for s in strips])+sum([len(f.verts) for f in mesh.faces if not f.flag])
549                                 print "%d indices total"%nind
550
551                         if self.use_degen_tris:
552                                 big_strip=[]
553                                 for s in strips:
554                                         if big_strip:
555                                                 big_strip+=[big_strip[-1], s[0]]
556                                         big_strip+=s
557
558                                 for f in mesh.faces:
559                                         if not f.flag:
560                                                 if len(big_strip)%2:
561                                                         order=(-1, -2, 0, 1)
562                                                 else:
563                                                         order=(0, 1, -1, -2)
564                                                 if big_strip:
565                                                         big_strip+=[big_strip[-1], f.verts[order[0]]]
566                                                 big_strip+=[f.verts[i] for i in order[:len(f.verts)]]
567                                                 f.flag=True
568
569                                 strips=[big_strip]
570                                 
571                                 if self.debug:
572                                         nind=len(big_strip)
573                                         print "Big strip has %d indices"%len(big_strip)
574
575                 if self.debug:
576                         print "%.2f indices per triangle"%(float(nind)/ntris)
577                         print "Locality before optimization: "+" ".join(["%.3f"%self.get_locality(s) for s in strips])
578
579                 if self.optimize_locality and self.use_strips:
580                         followers={}
581                         for s in strips:
582                                 followers.update(self.get_followers(s))
583
584                         verts2=[]
585                         vert=strips[0][0]
586                         while 1:
587                                 vert.flag=True
588                                 verts2.append(vert)
589
590                                 next=None
591                                 if vert.index in followers:
592                                         flw=followers[vert.index]
593                                         best=0
594                                         for n in flw:
595                                                 if flw[n]>best and not mesh.verts[n].flag:
596                                                         next=mesh.verts[n]
597                                                         best=flw[n]+0.9/abs(vert.index-n)
598
599                                 if not next:
600                                         for v in mesh.verts:
601                                                 if not v.flag:
602                                                         next=v
603                                                         break
604                                         if not next:
605                                                 break
606
607                                 vert=next
608
609                         mesh.verts=verts2
610
611                         for i in range(len(mesh.verts)):
612                                 mesh.verts[i].index=i
613
614                         if self.debug:
615                                 print "Locality after optimization: "+" ".join(["%.3f"%self.get_locality(s) for s in strips])
616
617                 self.out_file.write("vertices NORMAL3")
618                 if mesh.faceUV:
619                         self.out_file.write("_TEXCOORD2")
620                 self.out_file.write("_VERTEX3\n{\n")
621                 norm=None
622                 uv=None
623                 for v in mesh.verts:
624                         if v.no!=norm:
625                                 self.out_file.write("\tnormal3 %f %f %f;\n"%tuple(v.no))
626                                 norm=v.no
627                         if v.uv!=uv:
628                                 self.out_file.write("\ttexcoord2 %f %f;\n"%tuple(v.uv))
629                                 uv=v.uv
630                         self.out_file.write("\tvertex3 %f %f %f;\n"%tuple(v.co))
631                 self.out_file.write("};\n")
632                 for s in strips:
633                         self.out_file.write("batch TRIANGLE_STRIP\n{\n\tindices")
634                         n=0
635                         for v in s:
636                                 self.out_file.write(" %u"%v.index)
637                                 n+=1;
638                                 if n%32==0:
639                                         self.out_file.write(";\n\tindices")
640                         self.out_file.write(";\n};\n")
641
642                 first=True
643                 for f in mesh.faces:
644                         if not f.flag:
645                                 if first:
646                                         self.out_file.write("batch TRIANGLES\n{\n")
647                                         first=False
648                                 for i in range(2, len(f.verts)):
649                                         self.out_file.write("\tindices %u %u %u;\n"%(f.verts[0].index, f.verts[i-1].index, f.verts[i].index))
650                 if not first:
651                         self.out_file.write("};\n")
652
653
654 class FrontEnd:
655         def run(self):
656                 self.use_strips=Blender.Draw.Create(True)
657                 self.use_degen_tris=Blender.Draw.Create(True)
658                 self.optimize_locality=Blender.Draw.Create(True)
659                 self.debug=Blender.Draw.Create(False)
660                 self.strip_debug=Blender.Draw.Create(False)
661                 self.smooth_debug=Blender.Draw.Create(False)
662                 ret=Blender.Draw.PupBlock("Export MSP GL mesh",
663                         [("Use strips", self.use_strips, "Generage OpenGL triangle strips"),
664                                 ("Use degen tris", self.use_degen_tris, "Use degenerate triangles to combine triangle strips"),
665                                 ("Optimize locality", self.optimize_locality),
666                                 ("Debugging options"),
667                                 ("Debug", self.debug),
668                                 ("Debug strips", self.strip_debug),
669                                 ("Debug smoothing", self.smooth_debug)])
670                 if ret:
671                         Blender.Window.FileSelector(self.export, "Export MSP GL mesh", Blender.sys.makename(ext='.mesh'))
672
673         def draw(self):
674                 pass
675
676         def export(self, fn):
677                 exp=Exporter(fn)
678                 exp.use_strips=self.use_strips.val
679                 exp.use_degen_tris=self.use_degen_tris.val
680                 exp.optimize_locality=self.optimize_locality.val
681                 exp.debug=self.debug.val
682                 exp.strip_debug=self.strip_debug.val
683                 exp.smooth_debug=self.smooth_debug.val
684                 exp.export()
685
686
687 if __name__=="__main__":
688         fe=FrontEnd()
689         fe.run()