-> FLT Export bugfix
[blender.git] / release / scripts / flt_export.py
1 #!BPY
2 """ Registration info for Blender menus:
3 Name: 'OpenFlight (.flt)...'
4 Blender: 245
5 Group: 'Export'
6 Tip: 'Export to OpenFlight v16.0 (.flt)'
7 """
8
9 __author__ = "Greg MacDonald, Geoffrey Bantle"
10 __version__ = "2.0 11/21/07"
11 __url__ = ("blender", "blenderartists.org", "Author's homepage, http://sourceforge.net/projects/blight/")
12 __bpydoc__ = """\
13 This script exports v16.0 OpenFlight files.  OpenFlight is a
14 registered trademark of MultiGen-Paradigm, Inc.
15
16 Feature overview and more availible at:
17 http://wiki.blender.org/index.php/Scripts/Manual/Export/openflight_flt
18 """
19
20 # flt_export.py is an OpenFlight exporter for blender.
21 #
22 # Copyright (C) 2005 Greg MacDonald, 2007 Blender Foundation.
23 #
24 # This program is free software; you can redistribute it and/or
25 # modify it under the terms of the GNU General Public License
26 # as published by the Free Software Foundation; either version 2
27 # of the License, or (at your option) any later version.
28
29 # This program is distributed in the hope that it will be useful,
30 # but WITHOUT ANY WARRANTY; without even the implied warranty of
31 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
32 # GNU General Public License for more details.
33 #
34 # You should have received a copy of the GNU General Public License
35 # along with this program; if not, write to the Free Software
36 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
37
38 import Blender
39 from Blender import Modifier
40 import os.path
41 import flt_properties
42 import flt_defaultp as defaultp
43 from flt_filewalker import FltOut
44 from flt_filewalker import FileFinder
45 from flt_properties import *
46 import shutil
47
48 FF = FileFinder()
49 records = process_recordDefs()
50                 
51 class ExporterOptions:
52         
53         def read_state(self):
54                 reg = Blender.Registry.GetKey('flt_export',1)
55                 if reg:
56                         for key in self.state:
57                                 if reg.has_key(key):
58                                         self.state[key] = reg[key]
59         
60         def write_state(self):
61                 d = dict()
62                 for key in self.state:
63                         d[key] = self.state[key]
64                         Blender.Registry.SetKey('flt_export', d, 1) 
65         def __init__(self):
66                 self.verbose = 1
67                 self.tolerance = 0.001
68                 self.writevcol = True
69                 
70                 self.state = {'export_shading' : 0, 
71                                 'shading_default' : 45, 
72                                 'basepath' : os.path.dirname(Blender.Get('filename')),
73                                 'scale': 1.0,
74                                 'doxrefs' : 1,
75                                 'attrib' : 0,
76                                 'copytex' : 0,
77                                 'transform' : 0,
78                                 'xapp' : 1}
79                                 
80                 #default externals path
81                 if(os.path.exists(os.path.join(self.state['basepath'],'externals'))):
82                         self.state['externalspath'] = os.path.join(self.state['basepath'],'externals')
83                 else:
84                         self.state['externalspath'] = self.state['basepath'] 
85                 
86                 if(os.path.exists(os.path.join(self.state['basepath'],'textures'))):
87                         self.state['texturespath'] = os.path.join(self.state['basepath'],'textures')
88                 else:
89                         self.state['texturespath'] = self.state['basepath']
90
91                 self.state['xappath'] = ''
92                 self.read_state() #read from registry
93                 
94                 
95 options = ExporterOptions()
96 tex_files = dict() #a list of (possibly) modified texture path names
97
98 tex_layers = ['Layer0', 'Layer1', 'Layer2', 'Layer3', 'Layer4', 'Layer5', 'Layer6', 'Layer7']
99 mask = 2147483648
100 mtexmasks = []
101 for i in xrange(7):
102         mtexmasks.append(mask)
103         mask = mask / 2
104
105 FLOAT_TOLERANCE = options.tolerance
106
107 #need to move all this stuff to flt_properties.py.
108 identity_matrix = [[1.0, 0.0, 0.0, 0.0], [0.0, 1.0, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0], [0.0, 0.0, 0.0, 1.0]]
109 alltypes = [2,4,14,11,73,63,111]
110 childtypes = { 
111         2 : [111,2,73,4,14,63],
112         4 : [111],
113         73 : [111,2,73,4,14,63],
114         63 : [],
115         14 : [111,2,73,4,14,63],
116         111 : []
117 }
118 recordlen = {
119         2: 44,
120         4: 28,
121         73: 80,
122         63: 216,
123         14: 384,
124         111: 156
125 }
126
127 def is_identity(m):
128         for i in xrange(4):
129                 for j in xrange(4):
130                         if abs(m[i][j] - identity_matrix[i][j]) > FLOAT_TOLERANCE:
131                                 return False
132         return True
133
134 class MaterialDesc:
135         def __init__(self):
136                 self.name = 'Blender'
137
138                 # Colors, List of 3 floats.
139                 self.diffuse = [1.0, 1.0, 1.0]
140                 self.specular = [1.0, 1.0, 1.0]
141
142                 # Scalars
143                 self.ambient = 0.1 # [0.0, 1.0]
144                 self.emissive = 0.0 # [0.0, 1.0]
145                 self.shininess = 32.0 # Range is [0.0, 128.0]
146                 self.alpha = 1.0 # Range is [0.0, 1.0]
147
148 class VertexDesc:
149         def __init__(self, co=None, no=None, uv=None, fltindex=None,cindex=None):
150                 if co: self.x, self.y, self.z = tuple(co)
151                 else: self.x = self.y = self.z = 0.0
152                 if no: self.nx, self.ny, self.nz = tuple(no)
153                 else: self.nx = self.ny = self.nz = 0.0
154                 if uv: self.u, self.v = tuple(uv)
155                 else: self.u = self.v = 0.0
156                 if cindex: self.cindex = cindex
157                 else: self.cindex = 127
158                 self.fltindex = fltindex
159                 self.accum = 0
160
161 class shadowVert:
162         def __init__(self,bvert,object,world,normal):
163                 global options
164                 
165                 self.co = Blender.Mathutils.Vector(bvert.co[0],bvert.co[1],bvert.co[2])
166                 #if world:
167                 #       vec = self.co
168                 #       vec = Blender.Mathutils.Vector(vec[0] * options.scale, vec[1] * options.scale, vec[2] * options.scale) #scale
169                 #       self.co =  Blender.Mathutils.TranslationMatrix(vec) * (self.co * object.getMatrix('worldspace'))
170                 
171                 if normal:
172                         #if world:
173                         #       self.no = Blender.Mathutils.Vector(normal * object.getMatrix('worldspace')).normalize()
174                         #else:
175                         self.no = Blender.Mathutils.Vector(normal[0],normal[1],normal[2])
176                         
177                 else:
178                         #if world:
179                                 #self.no = Blender.Mathutils.Vector(bvert.no * object.getMatrix('worldspace')).normalize() 
180                         #else:
181                         self.no = Blender.Mathutils.Vector(bvert.no[0],bvert.no[1],bvert.no[2])
182                         
183                 #do scaling factor
184                 #if options.scale != 1.0:
185                         #self.co[0] = self.co[0] * options.scale
186                         #self.co[1] = self.co[1] * options.scale
187                         #self.co[2] = self.co[2] * options.scale
188                         
189                 self.index = bvert.index
190
191 class GlobalResourceRepository:
192         def new_face_name(self):
193                 self.face_name += 1
194                 return 'f%i' % (self.face_name-1)
195
196         def vertex_count(self):
197                 return len(self.vertex_lst)
198
199         def request_vertex_desc(self, i):
200                 return self.vertex_lst[i]
201
202         def request_vertex_index(self, object, mesh, face, vfindex, uvok,cindex):
203
204                 flatShadeNorm = None
205                 vno = None
206
207                 
208                 if type(face) is list:
209                         vertex = face[vfindex]
210                 elif str(type(face)) == "<type " + "'Blender MVert'>": 
211                         vertex = face
212                         vno = Blender.Mathutils.Vector(0.0,0.0,1.0)
213                 elif str(type(face)) == "<type " + "'Blender MEdge'>":
214                         if vfindex == 1:
215                                 vertex = face.v1
216                         elif vfindex == 2:
217                                 vertex = face.v2
218                 elif str(type(face)) == "<type " + "'Blender MFace'>":
219                         if not face.smooth:
220                                 flatShadeNorm = face.no
221                         vertex = face.v[vfindex]
222                 else: 
223                         return None
224                                                 
225                 if not self.namehash.has_key(object.name):
226                         self.namehash[object.name] = dict()
227                 indexhash = self.namehash[object.name]
228                 
229                 #export in global space? THIS HAS BEEN MADE REDUNDANT... REMOVE ME
230                 if not options.state['transform']:
231                         vertex = shadowVert(vertex,object,True,flatShadeNorm)
232                 else:
233                         vertex = shadowVert(vertex,object,False,flatShadeNorm)
234                 
235                 if vno:
236                         vertex.no = vno        
237         
238      
239                 #Check to see if this vertex has been visited before. If not, add
240                 if not indexhash.has_key(vertex.index):
241                         if uvok:
242                                 newvdesc = VertexDesc(vertex.co, vertex.no, face.uv[vfindex], self.nextvindex,cindex=cindex)
243                         else:
244                                 newvdesc = VertexDesc(co=vertex.co, no=vertex.no,fltindex=self.nextvindex,cindex=cindex)
245                         
246                         indexhash[vertex.index] = [newvdesc]
247                         self.vertex_lst.append(newvdesc)
248                         self.nextvindex = self.nextvindex + 1
249                         return newvdesc.fltindex
250                 
251                 else:
252                         desclist = indexhash[vertex.index]
253                         if uvok: 
254                                 faceu = face.uv[vfindex][0]
255                                 facev = face.uv[vfindex][1]
256                         else:
257                                 faceu = 0.0
258                                 facev = 0.0
259                         for vdesc in desclist:
260                                 if\
261                                 abs(vdesc.x - vertex.co[0]) > FLOAT_TOLERANCE or\
262                                 abs(vdesc.y - vertex.co[1]) > FLOAT_TOLERANCE or\
263                                 abs(vdesc.z - vertex.co[2]) > FLOAT_TOLERANCE or\
264                                 abs(vdesc.nx - vertex.no[0]) > FLOAT_TOLERANCE or\
265                                 abs(vdesc.ny - vertex.no[1]) > FLOAT_TOLERANCE or\
266                                 abs(vdesc.nz - vertex.no[2]) > FLOAT_TOLERANCE or\
267                                 vdesc.cindex != cindex or\
268                                 abs(vdesc.u - faceu) > FLOAT_TOLERANCE or\
269                                 abs(vdesc.v - facev) > FLOAT_TOLERANCE:
270                                         pass
271                                 else:
272                                         return vdesc.fltindex
273                                 
274                         #if we get this far, we didnt find a match. Add a new one and return
275                         if uvok:
276                                 newvdesc = VertexDesc(vertex.co, vertex.no, face.uv[vfindex], self.nextvindex,cindex=cindex)
277                         else:
278                                 newvdesc = VertexDesc(co=vertex.co, no=vertex.no,fltindex=self.nextvindex,cindex=cindex)
279                         indexhash[vertex.index].append(newvdesc)
280                         self.vertex_lst.append(newvdesc)
281                         self.nextvindex = self.nextvindex + 1
282                         return newvdesc.fltindex
283                                         
284                         
285         def request_texture_index(self, image):
286                 match = None
287                 for i in xrange(len(self.texture_lst)):
288                         if self.texture_lst[i] != image:
289                                 continue
290                         match = i
291                         break
292                 if match != None:
293                         return match
294                 else:
295                         self.texture_lst.append(image)
296                         return len(self.texture_lst) - 1
297
298         def request_texture_filename(self, index):
299                 return Blender.sys.expandpath(self.texture_lst[index].getFilename())
300
301         def texture_count(self):
302                 return len(self.texture_lst)
303
304         def request_material_index(self, desc):
305                 match = None
306                 for i in xrange(len(self.material_lst)):
307                         if self.material_lst[i].diffuse != desc.diffuse:
308                                 continue
309                         if self.material_lst[i].specular != desc.specular:
310                                 continue
311                         if self.material_lst[i].ambient != desc.ambient:
312                                 continue
313                         if self.material_lst[i].emissive != desc.emissive:
314                                 continue
315                         if self.material_lst[i].shininess != desc.shininess:
316                                 continue
317                         if self.material_lst[i].alpha != desc.alpha:
318                                 continue
319                         match = i
320                         break
321
322                 if match != None:
323                         return i
324                 else:
325                         self.material_lst.append(desc)
326                         return len(self.material_lst) - 1
327
328         def request_material_desc(self, index):
329                 return self.material_lst[index]
330
331         def material_count(self):
332                 return len(self.material_lst)
333
334         # Returns not actual index but one that includes intensity information.
335         # color_index = 127*intensity + 128*actual_index
336         def request_color_index(self, col):
337                 r,g,b = tuple(col)
338                 m = max(r, g, b)
339                 if m > 0.0:
340                         intensity = m / 1.0
341                         r = int(round(r/m * 255.0))
342                         g = int(round(g/m * 255.0))
343                         b = int(round(b/m * 255.0))
344                         brightest = [r, g, b]
345                 else:
346                         brightest = [255, 255, 255]
347                         intensity = 0.0
348
349                 match = None
350                 for i in xrange(len(self.color_lst)):
351                         if self.color_lst[i] != brightest:
352                                 continue
353
354                         match = i
355                         break
356
357                 if match != None:
358                         index = match
359                 else:
360                         length = len(self.color_lst)
361                         if length <= 1024:
362                                 self.color_lst.append(brightest)
363                                 index = length
364                         else:
365                                 if options.verbose >= 1:
366                                         print 'Warning: Exceeded max color limit.'
367                                 index = 0
368
369                 color_index = int(round(127.0*intensity)) + 128*index
370                 return color_index
371
372         # Returns color from actual index.
373         def request_max_color(self, index):
374                 return self.color_lst[index]
375
376         def color_count(self):
377                 return len(self.color_lst)
378
379         def __init__(self):
380                 #Vertex handling
381                 self.vertex_lst = []
382                 self.nextvindex = 0
383                 self.namehash = dict()
384                 
385                 self.texture_lst = []
386                 self.material_lst = []
387                 self.color_lst = [[255, 255, 255]]
388                 self.face_name = 0
389
390 class Node:
391         # Gathers info from blender needed for export.
392         # The =[0] is a trick to emulate c-like static function variables
393         # that are persistant between calls.
394         def blender_export(self, level=[0]):
395                 if self.object:
396                         if options.verbose >= 2:
397                                 print '\t' * level[0], self.name, self.object.type
398                 level[0] += 1
399                 
400                 self.children.reverse()
401                 for child in self.children:
402                         child.blender_export()
403                         
404                 level[0] -= 1
405
406         # Exports this node's info to file.
407         def write(self):
408                 pass
409
410         def write_matrix(self):
411                 if self.matrix and not is_identity(self.matrix):
412                         self.header.fw.write_short(49)   # Matrix opcode
413                         self.header.fw.write_ushort(68)  # Length of record
414                         for i in xrange(4):
415                                 for j in xrange(4):
416                                         self.header.fw.write_float(self.matrix[i][j])
417
418         def write_push(self):
419                 self.header.fw.write_short(10)
420                 self.header.fw.write_ushort(4)
421
422         def write_pop(self):
423                 self.header.fw.write_short(11)
424                 self.header.fw.write_ushort(4)
425                 
426         def write_push_extension(self):
427                 self.header.fw.write_short(21)
428                 self.header.fw.write_ushort(24)
429                 self.header.fw.pad(18)
430                 self.header.fw.write_ushort(0)
431                 
432         def write_pop_extension(self):
433                 self.header.fw.write_short(22)
434                 self.header.fw.write_ushort(24)
435                 self.header.fw.pad(18)
436                 self.header.fw.write_ushort(0)
437                 
438         def write_longid(self, name):
439                 length = len(name)
440                 if length >= 8:
441                         self.header.fw.write_short(33)              # Long ID opcode
442                         self.header.fw.write_ushort(length+5)       # Length of record
443                         self.header.fw.write_string(name, length+1) # name + zero terminator
444                         
445         def write_comment(self,comment):
446                 length = len(comment)
447                 if length >= 65535:
448                         comment = comment[:65530]
449                         length = len(comment)
450                 
451                 pad = (length % 4) - 1
452                 if pad < 0: 
453                         pad = None
454                         reclength = length + 5
455                 else:
456                         reclength = length + 5 + pad
457                 
458                 self.header.fw.write_short(31)                                  # Comment Opcode
459                 self.header.fw.write_ushort(reclength)                  # Length of record is 4 + comment length + null terminator + pad
460                 self.header.fw.write_string(comment,length+1)   # comment + zero terminator
461                 if pad:
462                         self.header.fw.pad(pad)                                         # pad to multiple of 4 bytes
463                 
464         # Initialization sets up basic tree structure.
465         def __init__(self, parent, header, object,props):
466                 global options
467                 
468                 self.header = header
469                 self.object = object
470                 if object:
471                         self.name = self.object.name
472                         if not options.state['transform']:
473                                 oloc = Blender.Mathutils.Vector(object.getLocation('worldspace'))
474                                 vec = Blender.Mathutils.Vector(oloc[0] * options.state['scale'], oloc[1] * options.state['scale'], oloc[2] * options.state['scale']) #scale
475                                 self.matrix =  self.object.getMatrix('worldspace') *  Blender.Mathutils.TranslationMatrix(vec - oloc)                   
476                         else:
477                                 self.matrix = self.object.getMatrix('localspace') #do matrix mult here.
478                         self.props = props
479                         self.child_objects = self.header.parenthash[object.name]
480                 else:
481                         self.name = 'no name'
482                         self.matrix = None
483                         self.props = None
484                         self.child_objects = self.header.child_objects
485                 
486                 self.children = []
487                 self.parent = parent
488                 if parent:
489                         parent.children.append(self)
490                 
491                 # Spawn children.
492                 for child in self.child_objects:                        
493                         if(not child.restrictDisplay):
494                                 childprops = None
495                                 ftype = None
496                                 if not child.properties.has_key('FLT'):
497                                         if child.type == 'Empty':
498                                                 if child.DupGroup:
499                                                         childprops = FLTXRef.copy()
500                                                         ftype = 63
501                                                 else:
502                                                         childprops = FLTGroup.copy()
503                                                         ftype = 2
504                                         elif child.type == 'Mesh':
505                                                 if self.header.childhash[child.name] or not child.parent:
506                                                         childprops = FLTGroup.copy()
507                                                         ftype = 2
508                                                 else:
509                                                         childprops = FLTObject.copy()
510                                                         ftype = 4
511                                                         
512                                 else:
513                                         childprops = dict()
514                                         for prop in child.properties['FLT']:
515                                                 childprops[prop] = child.properties['FLT'][prop]
516                                         ftype = child.properties['FLT']['type']
517                                 
518                                 if ftype in self.childtypes and ftype in alltypes:
519                                         Newnode = FLTNode(self,header,child,childprops,ftype)
520                                         if child.type == 'Mesh':
521                                                 self.header.mnodes.append(Newnode)
522 class FaceDesc:
523         def __init__(self):
524                 self.vertex_index_lst = []
525                 self.mface = None
526                 self.texture_index = -1
527                 self.material_index = -1
528                 self.color_index = 127
529                 self.renderstyle = 0
530                 self.twoside = 0
531                 self.name = None #uses next FLT name if not set... fix resolution of conflicts!
532                 self.billboard = 0
533                 
534                 #Multi-Tex info. Dosn't include first UV Layer!
535                 self.uvlayer = list() #list of list of tuples for UV coordinates.
536                 self.images = list()  #list of texture indices for seperate UV layers
537                 self.mtex = list()
538                 self.subface = None #can either be 'Push' or 'Pop'
539
540 def edge_get_othervert(vert, edge):
541         if edge.v1 == vert:
542                 return edge.v2
543         elif edge.v2 == vert:
544                 return edge.v1
545         return None
546
547 class FLTNode(Node):
548         def walkLoop(self, targetvert, startvert, startedge, edgelist, visited, vedges, closeloop):
549                 loop = [targetvert]
550                 
551                 curvert = startvert
552                 curedge = startedge
553                 visited[curedge] = True
554                 found = False
555                 
556                 while not found:
557                         loop.append(curvert)
558                         disk = vedges[curvert.index]
559                         if not closeloop:
560                                 if len(disk) == 1:
561                                         visited[curedge] = True
562                                         break
563                         else:
564                                 if len(disk) < 2: #what?
565                                         visited[curedge] = True
566                                         return None
567                         
568                         if disk[0] == curedge:
569                                 curedge = disk[1]
570                         else:
571                                 curedge = disk[0]
572                         if curedge.v1.index == curvert.index:
573                                 curvert = curedge.v2
574                         else:
575                                 curvert = curedge.v1
576
577                         visited[curedge] = True
578                         
579                         if(curvert == targetvert):
580                                 found = True
581                 
582                 return loop
583         
584         def buildVertFaces(self,vertuse):
585                 for vert in self.exportmesh.verts:
586                         if vertuse[vert.index][0] == False and vertuse[vert.index][1] == 0:
587                                 face_desc = FaceDesc()
588                                 face_desc.vertex_index_lst.append(self.header.GRR.request_vertex_index(self.object, self.exportmesh, vert, 0,0,0))
589                                 face_desc.renderstyle = 3
590                                 face_desc.color_index = 227
591                                 self.face_lst.append(face_desc)
592
593         def buildEdgeFaces(self,vertuse):
594                 for edge in self.exportmesh.edges:
595                         v1 = vertuse[edge.v1.index]
596                         v2 = vertuse[edge.v2.index]
597                         if v1[0] == False and v2[0] == False:
598                                 if v1[1] == 1 and v2[1] == 1:
599                                         face_desc = FaceDesc()
600                                         face_desc.vertex_index_lst.append(self.header.GRR.request_vertex_index(self.object, self.exportmesh, edge, 1, 0,0))
601                                         face_desc.vertex_index_lst.append(self.header.GRR.request_vertex_index(self.object, self.exportmesh, edge, 2, 0,0))                                     
602                                         face_desc.renderstyle = 3
603                                         face_desc.color_index = 227
604                                         self.face_lst.append(face_desc)
605
606
607         def vertwalk(self, startvert, loop, disk, visited):
608                 visited[startvert] = True
609                 for edge in disk[startvert]:
610                         othervert = edge_get_othervert(startvert, edge)
611                         if not visited[othervert]:
612                                 loop.append(othervert)
613                                 self.vertwalk(othervert,loop,disk,visited)
614
615         def buildOpenFacesNew(self, vertuse):
616                 wireverts = list()
617                 wiredges = list()
618                 visited = dict()
619                 disk = dict()
620                 loops = list()
621                 
622                 for edge in self.exportmesh.edges:
623                         v1 = vertuse[edge.v1.index]
624                         v2 = vertuse[edge.v2.index]
625                         if v1[0] == False and v2[0] == False:
626                                 if v1[1] < 3 and v2[1] < 3:
627                                         wireverts.append(edge.v1)
628                                         wireverts.append(edge.v2)
629                                         wiredges.append(edge)
630                                 
631                 #build disk data
632                 for vert in wireverts:
633                         visited[vert] = False
634                         disk[vert] = list()
635                 for edge in wiredges:
636                         disk[edge.v1].append(edge)
637                         disk[edge.v2].append(edge)
638                 
639                 #first pass: do open faces
640                 for vert in wireverts:
641                         if not visited[vert] and vertuse[vert.index][1] == 1:
642                                 loop = list()
643                                 done = 0
644                                 startvert = vert
645                                 while not done:
646                                         done = 1
647                                         visited[startvert] = True
648                                         loop.append(startvert)
649                                         for edge in disk[startvert]:
650                                                 othervert = edge_get_othervert(startvert, edge)
651                                                 if not visited[othervert]:
652                                                         done = 0
653                                                         startvert = othervert
654                                                         break
655                                 if len(loop) > 2: loops.append( ('Open', loop) )
656                 for vert in wireverts:
657                         if not visited[vert]:
658                                 loop = list()
659                                 done = 0
660                                 startvert = vert
661                                 while not done:
662                                         done = 1
663                                         visited[startvert] = True
664                                         loop.append(startvert)
665                                         for edge in disk[startvert]:
666                                                 othervert = edge_get_othervert(startvert,edge)
667                                                 if not visited[othervert]:
668                                                         done = 0
669                                                         startvert = othervert
670                                                         break
671                                 if len(loop) > 2: loops.append( ('closed', loop) )
672                 
673                 #now go through the loops and append.
674                 for l in loops:
675                         (ftype, loop) = l
676                         face_desc = FaceDesc()
677                         for i,vert in enumerate(loop):
678                                 face_desc.vertex_index_lst.append(self.header.GRR.request_vertex_index(self.object,self.exportmesh,loop,i,0,0))
679                                 if ftype  == 'closed':
680                                         face_desc.renderstyle = 2
681                                 else:
682                                         face_desc.renderstyle = 3
683                                 face_desc.color_index = 227
684                         self.face_lst.append(face_desc)
685
686
687
688         def sortFLTFaces(self,a,b):
689                 aindex = a.getProperty("FLT_ORIGINDEX")
690                 bindex = b.getProperty("FLT_ORIGINDEX")
691                 
692                 if aindex > bindex:
693                         return 1
694                 elif aindex < bindex:
695                         return -1
696                 return 0
697
698         def buildNormFaces(self):
699                 
700                 global options
701                 meshlayers = self.exportmesh.getUVLayerNames()
702                 oldlayer = self.exportmesh.activeUVLayer
703                 uvok = 0
704                 subfaceok = 0
705                 subfacelevel = 0
706                 
707                 #special case
708                 if self.exportmesh.faceUV and len(meshlayers) == 1:
709                         uvok = 1
710                 elif self.exportmesh.faceUV and tex_layers[0] in meshlayers:
711                         self.exportmesh.activeUVLayer = tex_layers[0] 
712                         uvok = 1
713                 
714                 #Sort faces according to the subfaces/FLT indices
715                 if "FLT_ORIGINDEX" in self.exportmesh.faces.properties and "FLT_SFLEVEL" in self.exportmesh.faces.properties:
716                         exportfaces = list()
717                         for face in self.exportmesh.faces:
718                                 exportfaces.append(face)
719                         exportfaces.sort(self.sortFLTFaces)
720                         subfaceok = 1
721                 else:
722                         exportfaces = self.exportmesh.faces
723                         
724                 # Faces described as lists of indices into the GRR's vertex_lst.
725                 for face in exportfaces:
726                         descs = list()
727                         #first we export the face as normal
728                         index_lst = []
729                         face_v = face.verts
730                         for i, v in enumerate(face_v):
731                                 index_lst.append(self.header.GRR.request_vertex_index(self.object,self.exportmesh,face,i,uvok,0))
732                         face_desc = FaceDesc()
733                         face_desc.vertex_index_lst = index_lst
734                         face_desc.mface = face
735                         descs.append(face_desc)
736                         
737                         #deal with subfaces                     
738                         if subfaceok:
739                                 fsflevel = face.getProperty("FLT_SFLEVEL")
740                                 for face_desc in descs:
741                                         if fsflevel > subfacelevel:
742                                                 face_desc.subface = 'Push'
743                                                 subfacelevel = fsflevel
744                                         elif fsflevel < subfacelevel:
745                                                 face_desc.subface = 'Pop'
746                                                 subfacelevel = fsflevel
747                 
748                         
749                         if uvok and (face.mode & Blender.Mesh.FaceModes.TWOSIDE):
750                                 face_desc.renderstyle = 1
751                         for face_desc in descs: 
752                                 if "FLT_COL" in self.exportmesh.faces.properties:
753                                         color_index = face.getProperty("FLT_COL")
754 #                                       if(color_index < 127):
755 #                                               color_index = 127 #sanity check for face color indices
756                                         if(color_index == 0):
757                                                 color_index = 127
758                                         face_desc.color_index = color_index
759                                 else:
760                                         face_desc.color_index = 127
761                                 if "FLT_ID" in self.exportmesh.faces.properties:
762                                         face_desc.name = face.getProperty("FLT_ID") #need better solution than this.
763                                 
764                                 if uvok and face.mode & Blender.Mesh.FaceModes["BILLBOARD"]:
765                                         face_desc.billboard = 1
766                                         
767                                 self.face_lst.append(face_desc)
768                 if uvok:                
769                         self.exportmesh.activeUVLayer = oldlayer
770
771         def buildTexData(self):
772                 
773                 meshlayers = self.exportmesh.getUVLayerNames()
774                 oldlayer = self.exportmesh.activeUVLayer
775                 uvok = 0
776                 
777                 if self.exportmesh.faceUV and len(meshlayers) == 1:
778                         uvok = 1
779                 if self.exportmesh.faceUV and tex_layers[0] in meshlayers:
780                         self.exportmesh.activeUVLayer = tex_layers[0] 
781                         uvok = 1
782                 
783                 if uvok: 
784                         #do base layer. UVs have been stored on vertices directly already.
785                         for i, face in enumerate(self.face_lst):
786                                 if face.mface:
787                                         mface = face.mface
788                                         image = mface.image
789                                         if image != None and mface.mode & Blender.Mesh.FaceModes["TEX"]:
790                                                 index = self.header.GRR.request_texture_index(image)
791                                         else:
792                                                 index = -1
793                                         face.texture_index = index
794
795                         for i, face in enumerate(self.face_lst):
796                                 if face.mface:
797                                         mface_v = face.mface.v
798                                         for v in mface_v:
799                                                 face.uvlayer.append([])
800                         
801                         for layername in tex_layers[1:]:
802                                 if layername in meshlayers:
803                                         self.exportmesh.activeUVLayer=layername
804                                         for i, face in enumerate(self.face_lst):
805                                                 if face.mface:
806
807                                                         face.mtex.append(layername)
808                                                         mface = face.mface
809                                                         mface_v = mface.v
810                                                         image = mface.image
811                                                 
812                                                         if image != None and mface.mode & Blender.Mesh.FaceModes["TEX"]:
813                                                                 index = self.header.GRR.request_texture_index(image)
814                                                                 face.images.append(index)
815                                                         else:
816                                                                 face.images.append(-1)
817
818                                                         for j, v in enumerate(mface_v):
819                                                                 face.uvlayer[j].append(tuple(mface.uv[j]))
820                 if uvok:
821                         self.exportmesh.activeUVLayer = oldlayer
822         def blender_export(self):
823                 global options
824                 Node.blender_export(self)
825                 if self.opcode == 111:
826                         self.exportmesh = Blender.Mesh.New()
827                         self.exportmesh.getFromObject(self.object.name)                 
828
829                         for vert in self.exportmesh.verts:
830                                 if not options.state['transform']:
831                                         vec = vert.co
832                                         vec = Blender.Mathutils.Vector(vec[0] * options.state['scale'], vec[1] * options.state['scale'], vec[2] * options.state['scale']) #scale
833                                         vert.co =  Blender.Mathutils.TranslationMatrix(vec) * (vert.co * self.object.getMatrix('worldspace'))                                           
834                                 
835                                 if options.state['scale'] != 1.0:
836                                         vert.co = vert.co * options.state['scale']
837
838                         if("FLT_VCOL") in self.mesh.verts.properties:
839                                 for v in self.exportmesh.verts:
840                                         self.vert_lst.append(self.header.GRR.request_vertex_index(self.object,self.exportmesh,v,0,0,v.getProperty("FLT_VCOL")))
841                         else:
842                                 for v in self.mesh.verts:
843                                         self.vert_lst.append(self.header.GRR.request_vertex_index(self.object,self.mesh,v,0,0,127))
844                         
845                 
846                 
847                 elif self.mesh:
848                         orig_mesh = self.object.getData(mesh=True)
849                         self.exportmesh = Blender.Mesh.New()
850                         default = None
851
852
853                         if options.state['export_shading']:
854                                 mods = self.object.modifiers
855                                 hasedsplit = False
856                                 for mod in mods:
857                                         if mod.type == Blender.Modifier.Types.EDGESPLIT:
858                                                 hasedsplit = True
859                                                 break
860                                 if not hasedsplit:
861                                         default = mods.append(Modifier.Types.EDGESPLIT)
862                                         default[Modifier.Settings.EDGESPLIT_ANGLE] = options.state['shading_default']
863                                         default[Modifier.Settings.EDGESPLIT_FROM_ANGLE] = True
864                                         default[Modifier.Settings.EDGESPLIT_FROM_SHARP] = False
865                                         self.object.makeDisplayList()
866
867                         self.exportmesh.getFromObject(self.object.name)
868
869                         #recalculate vertex positions
870                         for vert in self.exportmesh.verts:
871                                 if not options.state['transform']:
872                                         vec = vert.co
873                                         vec = Blender.Mathutils.Vector(vec[0] * options.state['scale'], vec[1] * options.state['scale'], vec[2] * options.state['scale']) #scale
874                                         vert.co =  Blender.Mathutils.TranslationMatrix(vec) * (vert.co * self.object.getMatrix('worldspace'))                                           
875                                 
876                                 if options.state['scale'] != 1.0:
877                                         vert.co = vert.co * options.state['scale']                      
878                         
879                         flipped = self.object.getMatrix('worldspace').determinant()
880                         
881                         if not options.state['transform']:
882                                 self.exportmesh.calcNormals()
883                         
884
885                         if default:
886                                 #remove modifier from list
887                                 mods.remove(default)
888                                 self.object.makeDisplayList()
889                                 
890                         #build some adjacency data
891                         vertuse = list()
892                         wiredges = list()
893                         openends = list()
894                         for v in self.exportmesh.verts:
895                                 vertuse.append([False,0])
896                         
897                         #build face incidence data
898                         for face in self.exportmesh.faces:
899                                 for i, v in enumerate(face.verts):
900                                         vertuse[v.index][0] = True
901
902                         for edge in self.exportmesh.edges: #count valance
903                                 vertuse[edge.v1.index][1] = vertuse[edge.v1.index][1] + 1
904                                 vertuse[edge.v2.index][1] = vertuse[edge.v2.index][1] + 1
905
906                         #create all face types
907                         self.buildVertFaces(vertuse)
908                         self.buildEdgeFaces(vertuse)
909                         self.buildOpenFacesNew(vertuse)
910                         self.buildNormFaces()
911                         self.buildTexData()
912                         
913                         if not options.state['transform']:
914                                 if flipped < 0:
915                                         for vdesc in self.header.GRR.vertex_lst:
916                                                 vdesc.accum = 0
917                                         for face in self.face_lst:
918                                                 face.vertex_index_lst.reverse()
919                                                 for vert in face.vertex_index_lst:
920                                                         self.header.GRR.vertex_lst[vert].accum = 1
921                                                         
922                                         for vdesc in self.header.GRR.vertex_lst:
923                                                 if vdesc.accum:
924                                                         vdesc.nx = vdesc.nx * -1
925                                                         vdesc.ny = vdesc.ny * -1
926                                                         vdesc.nz = vdesc.nz * -1
927
928
929         def write_faces(self):
930                 sublevel = 0
931                 for face_desc in self.face_lst:
932                         if face_desc.name:
933                                 face_name = face_desc.name
934                         else:
935                                 face_name = self.header.GRR.new_face_name()
936                         
937                         #grab the alpha value.
938                         alpha = 0
939                         if face_desc.texture_index > -1:
940                                 try:
941                                         typestring = os.path.splitext(self.header.GRR.texture_lst[face_desc.texture_index].getFilename())[1]
942                                         if typestring == '.inta' or typestring == '.rgba':
943                                                 alpha = 1
944                                 except:
945                                         pass
946                                         
947                         if not alpha:
948                                 for index in face_desc.images:
949                                         try:
950                                                 typestring = os.path.splitext(self.header.GRR.texture_lst[index].getFilename())[1]
951                                                 if typestring == '.inta' or typestring == '.rgba':
952                                                         alpha = 1
953                                         except:
954                                                 pass
955                                                 
956                         if face_desc.billboard:
957                                 alpha = 2
958                                 
959                         if face_desc.subface:
960                                 if face_desc.subface == 'Push':
961                                         self.header.fw.write_short(19)
962                                         self.header.fw.write_ushort(4)
963                                         sublevel += 1
964                                 else:
965                                         self.header.fw.write_short(20)
966                                         self.header.fw.write_ushort(4)
967                                         sublevel -= 1
968                         self.header.fw.write_short(5)                                   # Face opcode
969                         self.header.fw.write_ushort(80)                                 # Length of record
970                         self.header.fw.write_string(face_name, 8)                       # ASCII ID
971                         self.header.fw.write_int(-1)                                    # IR color code
972                         self.header.fw.write_short(0)                                                                   # Relative priority
973                         self.header.fw.write_char(face_desc.renderstyle)                # Draw type
974                         self.header.fw.write_char(0)                                    # Draw textured white.
975                         self.header.fw.write_ushort(0)                                  # Color name index
976                         self.header.fw.write_ushort(0)                                  # Alt color name index
977                         self.header.fw.write_char(0)                                    # Reserved
978                         self.header.fw.write_char(alpha)                                    # Template
979                         self.header.fw.write_short(-1)                                  # Detail tex pat index
980                         self.header.fw.write_short(face_desc.texture_index)             # Tex pattern index
981                         self.header.fw.write_short(face_desc.material_index)            # material index
982                         self.header.fw.write_short(0)                                   # SMC code
983                         self.header.fw.write_short(0)                                   # Feature                                       code
984                         self.header.fw.write_int(0)                                     # IR material code
985                         self.header.fw.write_ushort(0)                                  # transparency 0 = opaque
986                         self.header.fw.write_uchar(0)                                   # LOD generation control
987                         self.header.fw.write_uchar(0)                                   # line style index
988                         self.header.fw.write_int(0)                            # Flags
989                         self.header.fw.write_uchar(2)                                   # Light mode
990                         #self.header.fw.write_uchar(3)                                   # Light mode
991
992                         self.header.fw.pad(7)                                           # Reserved
993                         self.header.fw.write_uint(0)                                   # Packed color
994                         self.header.fw.write_uint(0)                                   # Packed alt color
995                         self.header.fw.write_short(-1)                                  # Tex map index
996                         self.header.fw.write_short(0)                                   # Reserved
997                         self.header.fw.write_uint(face_desc.color_index)                # Color index
998                         self.header.fw.write_uint(127)                                  # Alt color index
999                         self.header.fw.write_short(0)                                   # Reserved
1000                         self.header.fw.write_short(-1)                                  # Shader index
1001
1002                         self.write_longid(face_name)
1003                         
1004                         
1005                         #Write Multitexture field if appropriate
1006                         mtex = len(face_desc.mtex)
1007                         if mtex:
1008                                 uvmask = 0
1009                                 for layername in face_desc.mtex:
1010                                         mask = mtexmasks[tex_layers.index(layername)-1]
1011                                         uvmask |= mask
1012                                 self.header.fw.write_ushort(52)                                                                 # MultiTexture Opcode
1013                                 self.header.fw.write_ushort(8 + (mtex * 8))             # Length
1014                                 self.header.fw.write_uint(uvmask)                                                               # UV mask
1015                                 for i in xrange(mtex):
1016                                         self.header.fw.write_ushort(face_desc.images[i])                        # Tex pattern index
1017                                         self.header.fw.write_ushort(0)                                                          # Tex effect
1018                                         self.header.fw.write_ushort(0)                                                          # Tex Mapping index
1019                                         self.header.fw.write_ushort(0)                                                          # Tex data. User defined
1020                         
1021                         self.write_push()
1022
1023                         # Vertex list record
1024                         self.header.fw.write_short(72)                        # Vertex list opcode
1025                         num_verts = len(face_desc.vertex_index_lst)
1026                         self.header.fw.write_ushort(4*num_verts+4)            # Length of record
1027
1028                         for vert_index in face_desc.vertex_index_lst:
1029                                 # Offset into vertex palette
1030                                 self.header.fw.write_int(vert_index*64+8)
1031                         
1032                         #UV list record
1033                         if mtex:
1034                                 #length = 8 + (numverts * multitex * 8)
1035                                 self.header.fw.write_ushort(53)                                                                 # UV List Ocode
1036                                 self.header.fw.write_ushort(8 + (num_verts*mtex*8))                             # Record Length
1037                                 self.header.fw.write_uint(uvmask)                                                               # UV mask
1038                                 for i, vert_index in enumerate(face_desc.vertex_index_lst):
1039                                         for uv in face_desc.uvlayer[i]:
1040                                                 self.header.fw.write_float(uv[0])                                               #U coordinate
1041                                                 self.header.fw.write_float(uv[1])                                               #V coordinate                           
1042                         self.write_pop()
1043                 #clean up faces at the end of meshes....
1044                 if sublevel:
1045                         self.header.fw.write_short(20)
1046                         self.header.fw.write_ushort(4)
1047
1048         def write_lps(self):
1049                 # Vertex list record
1050                 self.write_push()
1051                 self.header.fw.write_short(72)                        # Vertex list opcode
1052                 num_verts = len(self.vert_lst)
1053                 self.header.fw.write_ushort(4*num_verts+4)            # Length of record
1054
1055                 for vert_index in self.vert_lst:
1056                         # Offset into vertex palette
1057                         self.header.fw.write_int(vert_index*64+8)
1058                 self.write_pop()
1059         def write(self):
1060                 self.header.fw.write_short(self.opcode)
1061                 self.header.fw.write_ushort(recordlen[self.opcode])
1062                 exportdict = FLT_Records[self.opcode].copy()
1063                 if self.object:
1064                         self.props['3t8!id'] = self.object.name[:7]
1065                 for key in exportdict.keys():
1066                         if self.props.has_key(key):
1067                                 exportdict[key] = self.props[key]
1068
1069                 if self.opcode == 63 and options.state['externalspath']:
1070                                 try:
1071                                         exportdict['3t200!filename'] = os.path.join(options.state['externalspath'],self.object.DupGroup.name+'.flt')
1072                                         self.header.xrefnames.append(self.object.DupGroup.name)
1073                                 except:
1074                                         pass
1075                 
1076                 for key in records[self.opcode]:
1077                         (ftype,length,propname) = records[self.opcode][key]
1078                         write_prop(self.header.fw,ftype,exportdict[propname],length)
1079                 
1080                 if self.props.has_key('comment'):
1081                         self.write_comment(self.props['comment'])
1082
1083                 if self.object and self.object.properties.has_key('FLT') and self.object.properties['FLT'].has_key('EXT'):
1084                         datalen = len(self.object.properties['FLT']['EXT']['data'])
1085                         self.write_push_extension()
1086                         self.header.fw.write_short(100)
1087                         self.header.fw.write_ushort(24 + datalen)
1088                         for key in records[100]:
1089                                 (ftype,length,propname) = records[100][key]
1090                                 write_prop(self.header.fw,ftype,self.object.properties['FLT']['EXT'][propname],length)
1091                         #write extension data
1092                         for i in xrange(datalen):
1093                                 self.header.fw.write_char(self.object.properties['FLT']['EXT']['data'][i])
1094                         self.write_pop_extension()
1095
1096
1097                 self.write_longid(self.name) #fix this!
1098                 
1099                 if options.state['transform'] or self.opcode == 63:
1100                         #writing transform matrix....
1101                         self.write_matrix()
1102
1103                 if self.opcode == 111:
1104                         self.write_lps()
1105                 elif self.face_lst != [] or self.children:
1106                         self.write_push()
1107                         if self.face_lst != []:
1108                                 #self.write_push()
1109                                 self.write_faces()
1110                                 #self.write_pop()
1111                 
1112                         if self.children:
1113                                 #self.write_push()
1114                                 for child in self.children:
1115                                         child.write()
1116                                 #self.write_pop()
1117                         self.write_pop()
1118                         
1119         def __init__(self, parent, header, object,props,ftype):
1120                 self.opcode = ftype #both these next two lines need to be in the node class....
1121                 self.childtypes = childtypes[self.opcode]
1122                 Node.__init__(self, parent, header, object,props)
1123                 self.face_lst = []
1124                 self.vert_lst = [] #for light points.
1125                 self.mesh = None
1126                 self.uvlayer = 0
1127                 self.flipx = False
1128                 self.flipy = False
1129                 self.flipz = False
1130                 
1131                                 
1132                 if self.object.type == 'Mesh':
1133                         self.mesh = self.object.getData(mesh=True)
1134                         if(self.mesh.faceUV):
1135                                 self.uvLayer = len(self.mesh.getUVLayerNames())
1136
1137 class Database(Node):
1138         def write_header(self):
1139                 if options.verbose >= 2:
1140                         print 'Writing header.'
1141                 self.fw.write_short(1)          # Header opcode
1142                 self.fw.write_ushort(324)       # Length of record
1143                 self.fw.write_string('db', 8)   # ASCII ID
1144                 self.fw.write_int(1600)         # Revision Number
1145                 self.fw.pad(44)
1146                 self.fw.write_short(1)          # Unit multiplier.
1147                 self.fw.write_char(0)           # Units, 0 = meters
1148                 self.fw.write_char(0)           # texwhite on new faces 0 = false
1149                 self.fw.write_uint(0x80000000)  # misc flags set to saving vertex normals
1150                 self.fw.pad(24)
1151                 self.fw.write_int(0)            # projection type, 0 = flat earth
1152                 self.fw.pad(30)
1153                 self.fw.write_short(1)          # double precision
1154                 self.fw.write_int(100)                  # database origin type
1155                 self.fw.pad(88)
1156                 try:
1157                         self.fw.write_double(self.header.scene.properties['FLT']['origin lat']) #database origin lattitude
1158                 except:
1159                         self.fw.write_double(0)
1160                 try:
1161                         self.fw.write_double(self.header.scene.properties['FLT']['origin lon']) #database origin longitude
1162                 except:
1163                         self.fw.write_double(0)
1164                 self.fw.pad(32)
1165                 self.fw.write_int(0)            # ellipsoid model, 0 = WSG 1984
1166                 
1167                 self.fw.pad(52)
1168
1169         def write_vert_pal(self):
1170                 if options.verbose >= 2:
1171                         print 'Writing vertex palette.'
1172                 # Write record for vertex palette
1173                 self.fw.write_short(67)                             # Vertex palette opcode.
1174                 self.fw.write_short(8)                              # Length of record
1175                 self.fw.write_int(self.GRR.vertex_count() * 64 + 8) # Length of everything.
1176                 # Write records for individual vertices.
1177                 for i in xrange(self.GRR.vertex_count()):
1178                         desc = self.GRR.request_vertex_desc(i)
1179                         self.fw.write_short(70)                         # Vertex with color normal and uv opcode.
1180                         self.fw.write_ushort(64)                        # Length of record
1181                         self.fw.write_ushort(0)                                                 # Color name index
1182                         self.fw.write_short(0x20000000)                                 # Flags
1183                         self.fw.write_double(desc.x)
1184                         self.fw.write_double(desc.y)
1185                         self.fw.write_double(desc.z)
1186                         self.fw.write_float(desc.nx)
1187                         self.fw.write_float(desc.ny)
1188                         self.fw.write_float(desc.nz)
1189                         self.fw.write_float(desc.u)
1190                         self.fw.write_float(desc.v)
1191                         self.fw.pad(4)
1192                         self.fw.write_uint(desc.cindex)
1193                         self.fw.pad(4)
1194
1195         def write_tex_pal(self):
1196                 if options.verbose >= 2:
1197                         print 'Writing texture palette.'
1198                 # Write record for texture palette
1199                 for i, img in enumerate(self.GRR.texture_lst):
1200                         filename = tex_files[img.name]
1201                         self.fw.write_short(64)                                         # Texture palette opcode.
1202                         self.fw.write_short(216)                                        # Length of record
1203                         self.fw.write_string(filename, 200) # Filename
1204                         self.fw.write_int(i)                                            # Texture index
1205                         self.fw.write_int(0)                                            # X
1206                         self.fw.write_int(0)                                            # Y
1207
1208         def write_mat_pal(self):
1209                 if options.verbose >= 2:
1210                         print 'Writing material palette.'
1211                 for i in xrange(self.GRR.material_count()):
1212                         desc = self.GRR.request_material_desc(i)
1213                         self.fw.write_short(113)                # Material palette opcode.
1214                         self.fw.write_short(84)                 # Length of record
1215                         self.fw.write_int(i)                    # Material index
1216                         self.fw.write_string(desc.name, 12)     # Material name
1217                         self.fw.write_uint(0x80000000)          # Flags
1218                         self.fw.write_float(desc.ambient[0])    # Ambient color.
1219                         self.fw.write_float(desc.ambient[1])    # Ambient color.
1220                         self.fw.write_float(desc.ambient[2])    # Ambient color.
1221                         self.fw.write_float(desc.diffuse[0])    # Diffuse color.
1222                         self.fw.write_float(desc.diffuse[1])    # Diffuse color.
1223                         self.fw.write_float(desc.diffuse[2])    # Diffuse color.
1224                         self.fw.write_float(desc.specular[0])   # Specular color.
1225                         self.fw.write_float(desc.specular[1])   # Specular color.
1226                         self.fw.write_float(desc.specular[2])   # Specular color.
1227                         self.fw.write_float(desc.emissive[0])   # Emissive color.
1228                         self.fw.write_float(desc.emissive[1])   # Emissive color.
1229                         self.fw.write_float(desc.emissive[2])   # Emissive color.
1230                         self.fw.write_float(desc.shininess)
1231                         self.fw.write_float(desc.alpha)
1232                         self.fw.write_int(0)                    # Reserved
1233
1234         def write_col_pal(self):
1235                 if options.verbose >= 2:
1236                         print 'Writing color palette.'
1237                 self.fw.write_short(32)                     # Color palette opcode.
1238                 self.fw.write_short(4228)                   # Length of record
1239                 self.fw.pad(128)
1240                 try:
1241                         cpalette = self.scene.properties['FLT']['Color Palette']
1242                 except:
1243                         cpalette = defaultp.pal
1244                 count = len(cpalette)
1245                 for i in xrange(count):
1246                         color = struct.unpack('>BBBB',struct.pack('>I',cpalette[i]))
1247                         self.fw.write_uchar(color[3])               # alpha
1248                         self.fw.write_uchar(color[2])               # b
1249                         self.fw.write_uchar(color[1])               # g
1250                         self.fw.write_uchar(color[0])               # r
1251                 self.fw.pad(max(4096-count*4, 0))
1252
1253         def write(self):
1254                 self.write_header()
1255                 self.write_vert_pal()
1256                 self.write_tex_pal()
1257                 self.write_mat_pal()
1258                 self.write_col_pal()
1259
1260                 self.write_push()
1261                 
1262                 for child in self.children:
1263                         child.write()
1264                 self.write_pop()
1265         
1266         def export_textures(self,texturepath):
1267                 for i in xrange(self.GRR.texture_count()):
1268                         texture = self.GRR.texture_lst[i]
1269                         
1270                         if options.state['copytex']:
1271                                 filename = os.path.normpath(os.path.join(options.state['texturespath'], os.path.basename(self.GRR.request_texture_filename(i))))
1272                         else:
1273                                 filename = os.path.normpath(self.GRR.request_texture_filename(i))
1274                         
1275                         tex_files[texture.name] = filename
1276
1277         def blender_export(self):
1278                 Node.blender_export(self)
1279                 self.export_textures(self)
1280                 return self.xrefnames
1281         def __init__(self, scene, fw):
1282                 self.fw = fw
1283                 self.opcode = 1
1284                 self.childtypes = [73,14,2,63]
1285                 self.scene = scene
1286                 self.childhash = dict()
1287                 self.parenthash = dict()
1288                 self.child_objects = list()
1289                 self.mnodes = list()
1290                 self.xrefnames = list()
1291                 for i in self.scene.objects:
1292                         self.parenthash[i.name] = list()
1293                         self.childhash[i.name] = False
1294                 for i in self.scene.objects:
1295                         if i.parent:
1296                                 self.childhash[i.parent.name] = True
1297                                 self.parenthash[i.parent.name].append(i)
1298                         else:
1299                                 self.child_objects.append(i)
1300
1301                 self.GRR = GlobalResourceRepository()
1302                 Node.__init__(self, None, self, None,None)
1303
1304 def write_attribute_files():
1305         for imgname in tex_files:
1306                 blentex = Blender.Image.Get(imgname)
1307                 exportdict = FLT_Records['Image'].copy()
1308                 
1309                 if blentex.properties.has_key('FLT'):
1310                         for key in exportdict.keys():
1311                                 if blentex.properties.has_key(key):
1312                                         exportdict[key] = blentex.properties['FLT'][key]
1313                 
1314                 # ClampX/Y override
1315                 if blentex.clampX:
1316                         exportdict['11i!WrapU'] = 1
1317                 if blentex.clampY:
1318                         exportdict['12i!WrapV'] = 1 
1319                 
1320                 exportdict['16i!Enviorment'] = 0 
1321                 
1322                 # File type
1323                 typecode = 0
1324                 try:
1325                         typestring = os.path.splitext(blentex.getFilename())[1]
1326                         
1327                         if typestring == '.rgba':
1328                                 typecode = 5
1329                         elif typestring == '.rgb':
1330                                 typecode = 4
1331                         elif typestring == '.inta':
1332                                 typecode = 3
1333                         elif typestring == '.int':
1334                                 typecode = 2
1335                 except:
1336                         pass
1337                 
1338                 exportdict['7i!File Format'] = typecode
1339
1340                 fw = FltOut(tex_files[imgname] + '.attr')
1341                 size = blentex.getSize()
1342                 fw.write_int(size[0])
1343                 fw.write_int(size[1])
1344                 for key in records['Image']:
1345                         (ftype,length,propname) = records['Image'][key]
1346                         write_prop(fw,ftype,exportdict[propname],length)
1347                 fw.close_file()
1348
1349 #globals used by the scene export function
1350 exportlevel = None
1351 xrefsdone = None
1352
1353 def dbexport_internal(scene):
1354         global exportlevel
1355         global xrefsdone
1356         global options
1357
1358         if exportlevel == 0 or not options.state['externalspath']:
1359                 fname = os.path.join(options.state['basepath'],scene.name + '.flt')
1360         else:
1361                 fname = os.path.join(options.state['externalspath'],scene.name + '.flt')
1362         
1363         fw = FltOut(fname)
1364         db = Database(scene,fw)
1365         
1366         if options.verbose >= 1:
1367                 print 'Pass 1: Exporting ', scene.name,'.flt from Blender.\n'
1368         
1369         xreflist = db.blender_export()
1370         if options.verbose >= 1:
1371                 print 'Pass 2: Writing %s\n' % fname
1372         db.write()
1373         fw.close_file()
1374         
1375         if options.state['doxrefs']:
1376                 for xname in xreflist:
1377                         try:
1378                                 xrefscene = Blender.Scene.Get(xname)
1379                         except:
1380                                 xrefscene = None
1381                         if xrefscene and xname not in xrefsdone:
1382                                 xrefsdone.append(xname)
1383                                 exportlevel+=1
1384                                 dbexport_internal(xrefscene)
1385                                 exportlevel-=1
1386         return fname
1387 #main database export function
1388 def dbexport():
1389         global exportlevel
1390         global xrefsdone
1391         exportlevel = 0
1392         xrefsdone = list()
1393         
1394         Blender.Window.WaitCursor(True)
1395         time1 = Blender.sys.time() # Start timing
1396         
1397         if options.verbose >= 1:
1398                 print '\nOpenFlight Exporter'
1399                 print 'Version:', __version__
1400                 print 'Author: Greg MacDonald, Geoffrey Bantle'
1401                 print __url__[2]
1402                 print
1403         
1404         fname = dbexport_internal(Blender.Scene.GetCurrent())
1405         if options.verbose >=1:
1406                 print 'Done in %.4f sec.\n' % (Blender.sys.time() - time1)
1407         Blender.Window.WaitCursor(False)
1408         
1409         #optional: Copy textures
1410         if options.state['copytex']:
1411                 for imgname in tex_files:
1412                         #Check to see if texture exists in target directory
1413                         if not os.path.exists(tex_files[imgname]):
1414                                 #Get original Blender file name
1415                                 origpath = Blender.sys.expandpath(Blender.Image.Get(imgname).getFilename())
1416                                 #copy original to new
1417                                 if os.path.exists(origpath):
1418                                         shutil.copyfile(origpath,tex_files[imgname])
1419         
1420         #optional: Write attribute files
1421         if options.state['attrib']:
1422                 write_attribute_files()
1423
1424         if options.state['xapp']:
1425                 cmd= options.state['xappath'] + " " + fname 
1426                 status = os.system(cmd)
1427         
1428
1429 #Begin UI code
1430 FLTExport = None
1431 FLTClose = None
1432 FLTLabel = None
1433
1434 FLTBaseLabel = None
1435 FLTTextureLabel = None
1436 FLTXRefLabel = None
1437
1438 FLTBaseString = None
1439 FLTTextureString = None
1440 FLTXRefString = None
1441
1442 FLTBasePath = None
1443 FLTTexturePath = None
1444 FLTXRefPath = None
1445
1446 FLTShadeExport = None
1447 FLTShadeDefault = None
1448
1449 FLTCopyTex = None
1450 FLTDoXRef = None
1451 FLTGlobal = None
1452
1453 FLTScale = None
1454
1455 FLTXAPP = None
1456 FLTXAPPath = None
1457 FLTXAPPString = None
1458 FLTXAPPLabel = None
1459 FLTXAPPChooser = None
1460
1461 FLTAttrib = None
1462
1463 def setshadingangle(ID,val):
1464         global options
1465         options.state['shading_default'] = val
1466 def setBpath(fname):
1467         global options
1468         options.state['basepath'] = os.path.dirname(fname)
1469         #update xref and textures path too....
1470         if(os.path.exists(os.path.join(options.state['basepath'],'externals'))):
1471                 options.state['externalspath'] = os.path.join(options.state['basepath'],'externals')
1472         if(os.path.exists(os.path.join(options.state['basepath'],'textures'))):
1473                 options.state['texturespath'] = os.path.join(options.state['basepath'],'textures')
1474 def setexportscale(ID,val):
1475         global options
1476         options.state['scale'] = val
1477
1478 def setTpath(fname):
1479         global options
1480         options.state['texturespath'] = os.path.dirname(fname)
1481 def setXpath(fname):
1482         global options
1483         options.state['externalspath'] = os.path.dirname(fname)
1484 def setXApath(fname):
1485         global options
1486         options.state['xappath'] = fname
1487 def event(evt, val):
1488         x = 1
1489 def but_event(evt):
1490         global options
1491         
1492         global FLTExport
1493         global FLTClose 
1494         global FLTLabel
1495         
1496         global FLTBaseLabel
1497         global FLTTextureLabel
1498         global FLTXRefLabel
1499
1500         global FLTBaseString
1501         global FLTTextureString
1502         global FLTXRefString
1503         
1504         global FLTBasePath
1505         global FLTTexturePath
1506         global FLTXRefPath
1507         
1508         global FLTShadeExport
1509         global FLTShadeDefault
1510         
1511         global FLTCopyTex
1512         global FLTDoXRef
1513         global FLTGlobal
1514         
1515         global FLTScale
1516         
1517         
1518         global FLTXAPP
1519         global FLTXAPPath
1520         global FLTXAPPString
1521         global FLTXAPPLabel     
1522         global FLTXAPPChooser
1523
1524         global FLTAttrib
1525         
1526         #choose base path for export
1527         if evt == 4:
1528                 Blender.Window.FileSelector(setBpath, "DB Root", options.state['basepath'])
1529                 
1530         #choose XREF path
1531         if evt == 6:
1532                 Blender.Window.FileSelector(setXpath,"DB Externals",options.state['externalspath'])
1533
1534         #choose texture path
1535         if evt == 8:
1536                 Blender.Window.FileSelector(setTpath,"DB Textures",options.state['texturespath'])
1537
1538         #export shading toggle
1539         if evt == 9:
1540                 options.state['export_shading'] = FLTShadeExport.val
1541         #export Textures
1542         if evt == 11:
1543                 options.state['copytex']= FLTCopyTex.val
1544         #export XRefs
1545         if evt == 13:
1546                 options.state['doxrefs'] = FLTDoXRef.val
1547         #export Transforms
1548         if evt == 12:
1549                 options.state['transform'] = FLTGlobal.val
1550                 
1551         if evt == 14:
1552                 options.state['xapp'] = FLTXAPP.val
1553         if evt == 16:
1554                 Blender.Window.FileSelector(setXApath,"External Application",options.state['xappath'])
1555         if evt == 20:
1556                 options.state['attrib'] = FLTAttrib.val
1557         
1558         #Export DB
1559         if evt == 1:
1560                 dbexport()
1561         
1562         #exit
1563         if evt == 2:
1564                 Draw.Exit()
1565
1566         options.write_state()
1567
1568 from Blender.BGL import *
1569 from Blender import Draw
1570 def gui():
1571         
1572         global options
1573         
1574         global FLTExport
1575         global FLTClose 
1576         global FLTLabel
1577         
1578         global FLTBaseLabel
1579         global FLTTextureLabel
1580         global FLTXRefLabel
1581
1582         global FLTBaseString
1583         global FLTTextureString
1584         global FLTXRefString
1585         
1586         global FLTBasePath
1587         global FLTTexturePath
1588         global FLTXRefPath
1589         
1590         global FLTShadeExport
1591         global FLTShadeDefault
1592         
1593         global FLTCopyTex
1594         global FLTDoXRef
1595         global FLTGlobal
1596         
1597         global FLTScale
1598         
1599         global FLTXAPP
1600         global FLTXAPPath
1601         global FLTXAPPString
1602         global FLTXAPPLabel
1603         global FLTXAPPChooser   
1604         
1605         global FLTAttrib
1606         
1607         glClearColor(0.880,0.890,0.730,1.0 )
1608         glClear(GL_COLOR_BUFFER_BIT)
1609         
1610         areas = Blender.Window.GetScreenInfo()
1611         curarea = Blender.Window.GetAreaID()
1612         curRect = None
1613         
1614         for area in areas:
1615                 if area['id'] == curarea:
1616                         curRect = area['vertices']
1617                         break
1618         
1619         width = curRect[2] - curRect[0]
1620         height = curRect[3] - curRect[1]
1621         #draw from top to bottom....
1622         cx = 50
1623         #Draw Title Bar...
1624         #glRasterPos2d(cx, curRect[3]-100)
1625         #FLTLabel = Draw.Text("FLT Exporter V2.0",'large')
1626         cy = height - 80
1627         
1628         FLTBaseLabel = Draw.Label("Base Path:",cx,cy,100,20)
1629         FLTBaseString = Draw.String("",3,cx+100,cy,300,20,options.state['basepath'],255,"Folder to export to")
1630         FLTBaseChooser = Draw.PushButton("...",4,cx+400,cy,20,20,"Choose Folder")
1631         
1632         cy = cy-40
1633         
1634         #externals path
1635         FLTXRefLabel = Draw.Label("XRefs:",cx,cy,100,20)
1636         FLTXRefString = Draw.String("",5,cx+100,cy,300,20,options.state['externalspath'],255,"Folder for external references")
1637         FLTXRefChooser = Draw.PushButton("...",6,cx+400,cy,20,20,"Choose Folder")
1638         cy = cy-40
1639         #Textures path
1640         FLTTextureLabel = Draw.Label("Textures:",cx,cy,100,20)
1641         FLTTextureString = Draw.String("",7,cx+100,cy,300,20,options.state['texturespath'],255,"Folder for texture files")
1642         FLTTextureChooser = Draw.PushButton("...",8,cx+400,cy,20,20,"Choose Folder")
1643         cy=cy-40
1644         #External application path
1645         FLTXAPPLabel = Draw.Label("XApp:",cx,cy,100,20)
1646         FLTXAPPString = Draw.String("",15,cx+100,cy,300,20,options.state['xappath'],255,"External application to launch when done")
1647         FLTXAPPChooser = Draw.PushButton("...",16,cx+400, cy,20,20,"Choose Folder")
1648         
1649         cy = cy-60
1650         #Shading Options
1651         FLTShadeExport = Draw.Toggle("Default Shading",9,cx,cy,100,20,options.state['export_shading'],"Turn on export of custom shading")
1652         FLTShadDefault = Draw.Number("",10,cx + 120,cy,100,20,options.state['shading_default'],0.0,180.0,"Default shading angle for objects with no custom shading assigned",setshadingangle)
1653         
1654         cy = cy-40
1655         FLTScale = Draw.Number("Export Scale",14,cx,cy,220,20,options.state['scale'],0.0,100.0,"Export scaling factor",setexportscale)
1656         
1657         cy = cy-40
1658         #misc Options
1659         FLTCopyTex = Draw.Toggle("Copy Textures",11,cx,cy,220,20,options.state['copytex'],"Copy textures to folder indicated above")
1660         cy = cy-40
1661         FLTGlobal = Draw.Toggle("Export Transforms",12,cx,cy,220,20,options.state['transform'],"If unchecked, Global coordinates are used (recommended)")
1662         cy = cy-40
1663         FLTDoXRef = Draw.Toggle("Export XRefs", 13,cx,cy,220,20,options.state['doxrefs'],"Export External references (only those below current scene!)")
1664         cy = cy-40
1665         FLTXAPP = Draw.Toggle("Launch External App", 14, cx,cy,220,20,options.state['xapp'],"Launch External Application on export")
1666         cy = cy-40
1667         FLTAttrib = Draw.Toggle("Write Attribute Files", 20, cx, cy, 220,20,options.state['attrib'], "Write Texture Attribute files")
1668         #FLTXAPPATH = Draw.String("",15,cx,cy,300,20,options.xappath,255,"External application path")
1669         
1670
1671         #Draw export/close buttons
1672         FLTExport = Draw.PushButton("Export",1,cx,20,100,20,"Export to FLT")
1673         FLTClose = Draw.PushButton("Close", 2, cx+120,20,100,20,"Close window")
1674         
1675
1676 Draw.Register(gui,event,but_event)