35022a69bbbcd7ce3321bb127228be391d7b4a8a
[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                                 visited[vert] = True
643                                 loop = [vert]
644                                 othervert = edge_get_othervert(vert, disk[vert][0])
645                                 self.vertwalk(othervert, loop, disk, visited)
646                                 if len(loop) > 2: loops.append( ('Open', loop) )
647
648                 for vert in wireverts:
649                         if not visited[vert]:
650                                 visited[vert] = True
651                                 loop = [vert]
652                                 othervert = edge_get_othervert(vert,disk[vert][0])
653                                 self.vertwalk(othervert, loop, disk, visited)
654                                 if len(loop) > 2: loops.append( ('closed', loop) )
655                                 
656                 #now go through the loops and append.
657                 for l in loops:
658                         (ftype, loop) = l
659                         face_desc = FaceDesc()
660                         for i,vert in enumerate(loop):
661                                 face_desc.vertex_index_lst.append(self.header.GRR.request_vertex_index(self.object,self.exportmesh,loop,i,0,0))
662                                 if ftype  == 'closed':
663                                         face_desc.renderstyle = 2
664                                 else:
665                                         face_desc.renderstyle = 3
666                                 face_desc.color_index = 227
667                         self.face_lst.append(face_desc)
668
669         def sortFLTFaces(self,a,b):
670                 aindex = a.getProperty("FLT_ORIGINDEX")
671                 bindex = b.getProperty("FLT_ORIGINDEX")
672                 
673                 if aindex > bindex:
674                         return 1
675                 elif aindex < bindex:
676                         return -1
677                 return 0
678
679         def buildNormFaces(self):
680                 
681                 global options
682                 meshlayers = self.exportmesh.getUVLayerNames()
683                 oldlayer = self.exportmesh.activeUVLayer
684                 uvok = 0
685                 subfaceok = 0
686                 subfacelevel = 0
687                 
688                 #special case
689                 if self.exportmesh.faceUV and len(meshlayers) == 1:
690                         uvok = 1
691                 elif self.exportmesh.faceUV and tex_layers[0] in meshlayers:
692                         self.exportmesh.activeUVLayer = tex_layers[0] 
693                         uvok = 1
694                 
695                 #Sort faces according to the subfaces/FLT indices
696                 if "FLT_ORIGINDEX" in self.exportmesh.faces.properties and "FLT_SFLEVEL" in self.exportmesh.faces.properties:
697                         exportfaces = list()
698                         for face in self.exportmesh.faces:
699                                 exportfaces.append(face)
700                         exportfaces.sort(self.sortFLTFaces)
701                         subfaceok = 1
702                 else:
703                         exportfaces = self.exportmesh.faces
704                         
705                 # Faces described as lists of indices into the GRR's vertex_lst.
706                 for face in exportfaces:
707                         descs = list()
708                         #first we export the face as normal
709                         index_lst = []
710                         face_v = face.verts
711                         for i, v in enumerate(face_v):
712                                 index_lst.append(self.header.GRR.request_vertex_index(self.object,self.exportmesh,face,i,uvok,0))
713                         face_desc = FaceDesc()
714                         face_desc.vertex_index_lst = index_lst
715                         face_desc.mface = face
716                         descs.append(face_desc)
717                         
718                         #deal with subfaces                     
719                         if subfaceok:
720                                 fsflevel = face.getProperty("FLT_SFLEVEL")
721                                 for face_desc in descs:
722                                         if fsflevel > subfacelevel:
723                                                 face_desc.subface = 'Push'
724                                                 subfacelevel = fsflevel
725                                         elif fsflevel < subfacelevel:
726                                                 face_desc.subface = 'Pop'
727                                                 subfacelevel = fsflevel
728                 
729                         
730                         if uvok and (face.mode & Blender.Mesh.FaceModes.TWOSIDE):
731                                 face_desc.renderstyle = 1
732                         for face_desc in descs: 
733                                 if "FLT_COL" in self.exportmesh.faces.properties:
734                                         color_index = face.getProperty("FLT_COL")
735 #                                       if(color_index < 127):
736 #                                               color_index = 127 #sanity check for face color indices
737                                         if(color_index == 0):
738                                                 color_index = 127
739                                         face_desc.color_index = color_index
740                                 else:
741                                         face_desc.color_index = 127
742                                 if "FLT_ID" in self.exportmesh.faces.properties:
743                                         face_desc.name = face.getProperty("FLT_ID") #need better solution than this.
744                                 
745                                 if uvok and face.mode & Blender.Mesh.FaceModes["BILLBOARD"]:
746                                         face_desc.billboard = 1
747                                         
748                                 self.face_lst.append(face_desc)
749                 if uvok:                
750                         self.exportmesh.activeUVLayer = oldlayer
751
752         def buildTexData(self):
753                 
754                 meshlayers = self.exportmesh.getUVLayerNames()
755                 oldlayer = self.exportmesh.activeUVLayer
756                 uvok = 0
757                 
758                 if self.exportmesh.faceUV and len(meshlayers) == 1:
759                         uvok = 1
760                 if self.exportmesh.faceUV and tex_layers[0] in meshlayers:
761                         self.exportmesh.activeUVLayer = tex_layers[0] 
762                         uvok = 1
763                 
764                 if uvok: 
765                         #do base layer. UVs have been stored on vertices directly already.
766                         for i, face in enumerate(self.face_lst):
767                                 if face.mface:
768                                         mface = face.mface
769                                         image = mface.image
770                                         if image != None and mface.mode & Blender.Mesh.FaceModes["TEX"]:
771                                                 index = self.header.GRR.request_texture_index(image)
772                                         else:
773                                                 index = -1
774                                         face.texture_index = index
775
776                         for i, face in enumerate(self.face_lst):
777                                 if face.mface:
778                                         mface_v = face.mface.v
779                                         for v in mface_v:
780                                                 face.uvlayer.append([])
781                         
782                         for layername in tex_layers[1:]:
783                                 if layername in meshlayers:
784                                         self.exportmesh.activeUVLayer=layername
785                                         for i, face in enumerate(self.face_lst):
786                                                 if face.mface:
787
788                                                         face.mtex.append(layername)
789                                                         mface = face.mface
790                                                         mface_v = mface.v
791                                                         image = mface.image
792                                                 
793                                                         if image != None and mface.mode & Blender.Mesh.FaceModes["TEX"]:
794                                                                 index = self.header.GRR.request_texture_index(image)
795                                                                 face.images.append(index)
796                                                         else:
797                                                                 face.images.append(-1)
798
799                                                         for j, v in enumerate(mface_v):
800                                                                 face.uvlayer[j].append(tuple(mface.uv[j]))
801                 if uvok:
802                         self.exportmesh.activeUVLayer = oldlayer
803         def blender_export(self):
804                 global options
805                 Node.blender_export(self)
806                 if self.opcode == 111:
807                         self.exportmesh = Blender.Mesh.New()
808                         self.exportmesh.getFromObject(self.object.name)                 
809
810                         for vert in self.exportmesh.verts:
811                                 if not options.state['transform']:
812                                         vec = vert.co
813                                         vec = Blender.Mathutils.Vector(vec[0] * options.state['scale'], vec[1] * options.state['scale'], vec[2] * options.state['scale']) #scale
814                                         vert.co =  Blender.Mathutils.TranslationMatrix(vec) * (vert.co * self.object.getMatrix('worldspace'))                                           
815                                 
816                                 if options.state['scale'] != 1.0:
817                                         vert.co = vert.co * options.state['scale']
818
819                         if("FLT_VCOL") in self.mesh.verts.properties:
820                                 for v in self.exportmesh.verts:
821                                         self.vert_lst.append(self.header.GRR.request_vertex_index(self.object,self.exportmesh,v,0,0,v.getProperty("FLT_VCOL")))
822                         else:
823                                 for v in self.mesh.verts:
824                                         self.vert_lst.append(self.header.GRR.request_vertex_index(self.object,self.mesh,v,0,0,127))
825                         
826                 
827                 
828                 elif self.mesh:
829                         orig_mesh = self.object.getData(mesh=True)
830                         self.exportmesh = Blender.Mesh.New()
831                         default = None
832
833
834                         if options.state['export_shading']:
835                                 mods = self.object.modifiers
836                                 hasedsplit = False
837                                 for mod in mods:
838                                         if mod.type == Blender.Modifier.Types.EDGESPLIT:
839                                                 hasedsplit = True
840                                                 break
841                                 if not hasedsplit:
842                                         default = mods.append(Modifier.Types.EDGESPLIT)
843                                         default[Modifier.Settings.EDGESPLIT_ANGLE] = options.state['shading_default']
844                                         default[Modifier.Settings.EDGESPLIT_FROM_ANGLE] = True
845                                         default[Modifier.Settings.EDGESPLIT_FROM_SHARP] = False
846                                         self.object.makeDisplayList()
847
848                         self.exportmesh.getFromObject(self.object.name)
849
850                         #recalculate vertex positions
851                         for vert in self.exportmesh.verts:
852                                 if not options.state['transform']:
853                                         vec = vert.co
854                                         vec = Blender.Mathutils.Vector(vec[0] * options.state['scale'], vec[1] * options.state['scale'], vec[2] * options.state['scale']) #scale
855                                         vert.co =  Blender.Mathutils.TranslationMatrix(vec) * (vert.co * self.object.getMatrix('worldspace'))                                           
856                                 
857                                 if options.state['scale'] != 1.0:
858                                         vert.co = vert.co * options.state['scale']                      
859                         
860                         flipped = self.object.getMatrix('worldspace').determinant()
861                         
862                         if not options.state['transform']:
863                                 self.exportmesh.calcNormals()
864                         
865
866                         if default:
867                                 #remove modifier from list
868                                 mods.remove(default)
869                                 self.object.makeDisplayList()
870                                 
871                         #build some adjacency data
872                         vertuse = list()
873                         wiredges = list()
874                         openends = list()
875                         for v in self.exportmesh.verts:
876                                 vertuse.append([False,0])
877                         
878                         #build face incidence data
879                         for face in self.exportmesh.faces:
880                                 for i, v in enumerate(face.verts):
881                                         vertuse[v.index][0] = True
882
883                         for edge in self.exportmesh.edges: #count valance
884                                 vertuse[edge.v1.index][1] = vertuse[edge.v1.index][1] + 1
885                                 vertuse[edge.v2.index][1] = vertuse[edge.v2.index][1] + 1
886
887                         #create all face types
888                         self.buildVertFaces(vertuse)
889                         self.buildEdgeFaces(vertuse)
890                         self.buildOpenFacesNew(vertuse)
891                         self.buildNormFaces()
892                         self.buildTexData()
893                         
894                         if not options.state['transform']:
895                                 if flipped < 0:
896                                         for vdesc in self.header.GRR.vertex_lst:
897                                                 vdesc.accum = 0
898                                         for face in self.face_lst:
899                                                 face.vertex_index_lst.reverse()
900                                                 for vert in face.vertex_index_lst:
901                                                         self.header.GRR.vertex_lst[vert].accum = 1
902                                                         
903                                         for vdesc in self.header.GRR.vertex_lst:
904                                                 if vdesc.accum:
905                                                         vdesc.nx = vdesc.nx * -1
906                                                         vdesc.ny = vdesc.ny * -1
907                                                         vdesc.nz = vdesc.nz * -1
908
909
910         def write_faces(self):
911                 sublevel = 0
912                 for face_desc in self.face_lst:
913                         if face_desc.name:
914                                 face_name = face_desc.name
915                         else:
916                                 face_name = self.header.GRR.new_face_name()
917                         
918                         #grab the alpha value.
919                         alpha = 0
920                         if face_desc.texture_index > -1:
921                                 try:
922                                         typestring = os.path.splitext(self.header.GRR.texture_lst[face_desc.texture_index].getFilename())[1]
923                                         if typestring == '.inta' or typestring == '.rgba':
924                                                 alpha = 1
925                                 except:
926                                         pass
927                                         
928                         if not alpha:
929                                 for index in face_desc.images:
930                                         try:
931                                                 typestring = os.path.splitext(self.header.GRR.texture_lst[index].getFilename())[1]
932                                                 if typestring == '.inta' or typestring == '.rgba':
933                                                         alpha = 1
934                                         except:
935                                                 pass
936                                                 
937                         if face_desc.billboard:
938                                 alpha = 2
939                                 
940                         if face_desc.subface:
941                                 if face_desc.subface == 'Push':
942                                         self.header.fw.write_short(19)
943                                         self.header.fw.write_ushort(4)
944                                         sublevel += 1
945                                 else:
946                                         self.header.fw.write_short(20)
947                                         self.header.fw.write_ushort(4)
948                                         sublevel -= 1
949                         self.header.fw.write_short(5)                                   # Face opcode
950                         self.header.fw.write_ushort(80)                                 # Length of record
951                         self.header.fw.write_string(face_name, 8)                       # ASCII ID
952                         self.header.fw.write_int(-1)                                    # IR color code
953                         self.header.fw.write_short(0)                                                                   # Relative priority
954                         self.header.fw.write_char(face_desc.renderstyle)                # Draw type
955                         self.header.fw.write_char(0)                                    # Draw textured white.
956                         self.header.fw.write_ushort(0)                                  # Color name index
957                         self.header.fw.write_ushort(0)                                  # Alt color name index
958                         self.header.fw.write_char(0)                                    # Reserved
959                         self.header.fw.write_char(alpha)                                    # Template
960                         self.header.fw.write_short(-1)                                  # Detail tex pat index
961                         self.header.fw.write_short(face_desc.texture_index)             # Tex pattern index
962                         self.header.fw.write_short(face_desc.material_index)            # material index
963                         self.header.fw.write_short(0)                                   # SMC code
964                         self.header.fw.write_short(0)                                   # Feature                                       code
965                         self.header.fw.write_int(0)                                     # IR material code
966                         self.header.fw.write_ushort(0)                                  # transparency 0 = opaque
967                         self.header.fw.write_uchar(0)                                   # LOD generation control
968                         self.header.fw.write_uchar(0)                                   # line style index
969                         self.header.fw.write_int(0)                            # Flags
970                         self.header.fw.write_uchar(2)                                   # Light mode
971                         #self.header.fw.write_uchar(3)                                   # Light mode
972
973                         self.header.fw.pad(7)                                           # Reserved
974                         self.header.fw.write_uint(0)                                   # Packed color
975                         self.header.fw.write_uint(0)                                   # Packed alt color
976                         self.header.fw.write_short(-1)                                  # Tex map index
977                         self.header.fw.write_short(0)                                   # Reserved
978                         self.header.fw.write_uint(face_desc.color_index)                # Color index
979                         self.header.fw.write_uint(127)                                  # Alt color index
980                         self.header.fw.write_short(0)                                   # Reserved
981                         self.header.fw.write_short(-1)                                  # Shader index
982
983                         self.write_longid(face_name)
984                         
985                         
986                         #Write Multitexture field if appropriate
987                         mtex = len(face_desc.mtex)
988                         if mtex:
989                                 uvmask = 0
990                                 for layername in face_desc.mtex:
991                                         mask = mtexmasks[tex_layers.index(layername)-1]
992                                         uvmask |= mask
993                                 self.header.fw.write_ushort(52)                                                                 # MultiTexture Opcode
994                                 self.header.fw.write_ushort(8 + (mtex * 8))             # Length
995                                 self.header.fw.write_uint(uvmask)                                                               # UV mask
996                                 for i in xrange(mtex):
997                                         self.header.fw.write_ushort(face_desc.images[i])                        # Tex pattern index
998                                         self.header.fw.write_ushort(0)                                                          # Tex effect
999                                         self.header.fw.write_ushort(0)                                                          # Tex Mapping index
1000                                         self.header.fw.write_ushort(0)                                                          # Tex data. User defined
1001                         
1002                         self.write_push()
1003
1004                         # Vertex list record
1005                         self.header.fw.write_short(72)                        # Vertex list opcode
1006                         num_verts = len(face_desc.vertex_index_lst)
1007                         self.header.fw.write_ushort(4*num_verts+4)            # Length of record
1008
1009                         for vert_index in face_desc.vertex_index_lst:
1010                                 # Offset into vertex palette
1011                                 self.header.fw.write_int(vert_index*64+8)
1012                         
1013                         #UV list record
1014                         if mtex:
1015                                 #length = 8 + (numverts * multitex * 8)
1016                                 self.header.fw.write_ushort(53)                                                                 # UV List Ocode
1017                                 self.header.fw.write_ushort(8 + (num_verts*mtex*8))                             # Record Length
1018                                 self.header.fw.write_uint(uvmask)                                                               # UV mask
1019                                 for i, vert_index in enumerate(face_desc.vertex_index_lst):
1020                                         for uv in face_desc.uvlayer[i]:
1021                                                 self.header.fw.write_float(uv[0])                                               #U coordinate
1022                                                 self.header.fw.write_float(uv[1])                                               #V coordinate                           
1023                         self.write_pop()
1024                 #clean up faces at the end of meshes....
1025                 if sublevel:
1026                         self.header.fw.write_short(20)
1027                         self.header.fw.write_ushort(4)
1028
1029         def write_lps(self):
1030                 # Vertex list record
1031                 self.write_push()
1032                 self.header.fw.write_short(72)                        # Vertex list opcode
1033                 num_verts = len(self.vert_lst)
1034                 self.header.fw.write_ushort(4*num_verts+4)            # Length of record
1035
1036                 for vert_index in self.vert_lst:
1037                         # Offset into vertex palette
1038                         self.header.fw.write_int(vert_index*64+8)
1039                 self.write_pop()
1040         def write(self):
1041                 self.header.fw.write_short(self.opcode)
1042                 self.header.fw.write_ushort(recordlen[self.opcode])
1043                 exportdict = FLT_Records[self.opcode].copy()
1044                 if self.object:
1045                         self.props['3t8!id'] = self.object.name[:7]
1046                 for key in exportdict.keys():
1047                         if self.props.has_key(key):
1048                                 exportdict[key] = self.props[key]
1049
1050                 if self.opcode == 63 and options.state['externalspath']:
1051                                 try:
1052                                         exportdict['3t200!filename'] = os.path.join(options.state['externalspath'],self.object.DupGroup.name+'.flt')
1053                                         self.header.xrefnames.append(self.object.DupGroup.name)
1054                                 except:
1055                                         pass
1056                 
1057                 for key in records[self.opcode]:
1058                         (ftype,length,propname) = records[self.opcode][key]
1059                         write_prop(self.header.fw,ftype,exportdict[propname],length)
1060                 
1061                 if self.props.has_key('comment'):
1062                         self.write_comment(self.props['comment'])
1063
1064                 if self.object and self.object.properties.has_key('FLT') and self.object.properties['FLT'].has_key('EXT'):
1065                         datalen = len(self.object.properties['FLT']['EXT']['data'])
1066                         self.write_push_extension()
1067                         self.header.fw.write_short(100)
1068                         self.header.fw.write_ushort(24 + datalen)
1069                         for key in records[100]:
1070                                 (ftype,length,propname) = records[100][key]
1071                                 write_prop(self.header.fw,ftype,self.object.properties['FLT']['EXT'][propname],length)
1072                         #write extension data
1073                         for i in xrange(datalen):
1074                                 self.header.fw.write_char(self.object.properties['FLT']['EXT']['data'][i])
1075                         self.write_pop_extension()
1076
1077
1078                 self.write_longid(self.name) #fix this!
1079                 
1080                 if options.state['transform'] or self.opcode == 63:
1081                         #writing transform matrix....
1082                         self.write_matrix()
1083
1084                 if self.opcode == 111:
1085                         self.write_lps()
1086                 elif self.face_lst != [] or self.children:
1087                         self.write_push()
1088                         if self.face_lst != []:
1089                                 #self.write_push()
1090                                 self.write_faces()
1091                                 #self.write_pop()
1092                 
1093                         if self.children:
1094                                 #self.write_push()
1095                                 for child in self.children:
1096                                         child.write()
1097                                 #self.write_pop()
1098                         self.write_pop()
1099                         
1100         def __init__(self, parent, header, object,props,ftype):
1101                 self.opcode = ftype #both these next two lines need to be in the node class....
1102                 self.childtypes = childtypes[self.opcode]
1103                 Node.__init__(self, parent, header, object,props)
1104                 self.face_lst = []
1105                 self.vert_lst = [] #for light points.
1106                 self.mesh = None
1107                 self.uvlayer = 0
1108                 self.flipx = False
1109                 self.flipy = False
1110                 self.flipz = False
1111                 
1112                                 
1113                 if self.object.type == 'Mesh':
1114                         self.mesh = self.object.getData(mesh=True)
1115                         if(self.mesh.faceUV):
1116                                 self.uvLayer = len(self.mesh.getUVLayerNames())
1117
1118 class Database(Node):
1119         def write_header(self):
1120                 if options.verbose >= 2:
1121                         print 'Writing header.'
1122                 self.fw.write_short(1)          # Header opcode
1123                 self.fw.write_ushort(324)       # Length of record
1124                 self.fw.write_string('db', 8)   # ASCII ID
1125                 self.fw.write_int(1600)         # Revision Number
1126                 self.fw.pad(44)
1127                 self.fw.write_short(1)          # Unit multiplier.
1128                 self.fw.write_char(0)           # Units, 0 = meters
1129                 self.fw.write_char(0)           # texwhite on new faces 0 = false
1130                 self.fw.write_uint(0x80000000)  # misc flags set to saving vertex normals
1131                 self.fw.pad(24)
1132                 self.fw.write_int(0)            # projection type, 0 = flat earth
1133                 self.fw.pad(30)
1134                 self.fw.write_short(1)          # double precision
1135                 self.fw.write_int(100)                  # database origin type
1136                 self.fw.pad(88)
1137                 try:
1138                         self.fw.write_double(self.header.scene.properties['FLT']['origin lat']) #database origin lattitude
1139                 except:
1140                         self.fw.write_double(0)
1141                 try:
1142                         self.fw.write_double(self.header.scene.properties['FLT']['origin lon']) #database origin longitude
1143                 except:
1144                         self.fw.write_double(0)
1145                 self.fw.pad(32)
1146                 self.fw.write_int(0)            # ellipsoid model, 0 = WSG 1984
1147                 
1148                 self.fw.pad(52)
1149
1150         def write_vert_pal(self):
1151                 if options.verbose >= 2:
1152                         print 'Writing vertex palette.'
1153                 # Write record for vertex palette
1154                 self.fw.write_short(67)                             # Vertex palette opcode.
1155                 self.fw.write_short(8)                              # Length of record
1156                 self.fw.write_int(self.GRR.vertex_count() * 64 + 8) # Length of everything.
1157                 # Write records for individual vertices.
1158                 for i in xrange(self.GRR.vertex_count()):
1159                         desc = self.GRR.request_vertex_desc(i)
1160                         self.fw.write_short(70)                         # Vertex with color normal and uv opcode.
1161                         self.fw.write_ushort(64)                        # Length of record
1162                         self.fw.write_ushort(0)                                                 # Color name index
1163                         self.fw.write_short(0x20000000)                                 # Flags
1164                         self.fw.write_double(desc.x)
1165                         self.fw.write_double(desc.y)
1166                         self.fw.write_double(desc.z)
1167                         self.fw.write_float(desc.nx)
1168                         self.fw.write_float(desc.ny)
1169                         self.fw.write_float(desc.nz)
1170                         self.fw.write_float(desc.u)
1171                         self.fw.write_float(desc.v)
1172                         self.fw.pad(4)
1173                         self.fw.write_uint(desc.cindex)
1174                         self.fw.pad(4)
1175
1176         def write_tex_pal(self):
1177                 if options.verbose >= 2:
1178                         print 'Writing texture palette.'
1179                 # Write record for texture palette
1180                 for i, img in enumerate(self.GRR.texture_lst):
1181                         filename = tex_files[img.name]
1182                         self.fw.write_short(64)                                         # Texture palette opcode.
1183                         self.fw.write_short(216)                                        # Length of record
1184                         self.fw.write_string(filename, 200) # Filename
1185                         self.fw.write_int(i)                                            # Texture index
1186                         self.fw.write_int(0)                                            # X
1187                         self.fw.write_int(0)                                            # Y
1188
1189         def write_mat_pal(self):
1190                 if options.verbose >= 2:
1191                         print 'Writing material palette.'
1192                 for i in xrange(self.GRR.material_count()):
1193                         desc = self.GRR.request_material_desc(i)
1194                         self.fw.write_short(113)                # Material palette opcode.
1195                         self.fw.write_short(84)                 # Length of record
1196                         self.fw.write_int(i)                    # Material index
1197                         self.fw.write_string(desc.name, 12)     # Material name
1198                         self.fw.write_uint(0x80000000)          # Flags
1199                         self.fw.write_float(desc.ambient[0])    # Ambient color.
1200                         self.fw.write_float(desc.ambient[1])    # Ambient color.
1201                         self.fw.write_float(desc.ambient[2])    # Ambient color.
1202                         self.fw.write_float(desc.diffuse[0])    # Diffuse color.
1203                         self.fw.write_float(desc.diffuse[1])    # Diffuse color.
1204                         self.fw.write_float(desc.diffuse[2])    # Diffuse color.
1205                         self.fw.write_float(desc.specular[0])   # Specular color.
1206                         self.fw.write_float(desc.specular[1])   # Specular color.
1207                         self.fw.write_float(desc.specular[2])   # Specular color.
1208                         self.fw.write_float(desc.emissive[0])   # Emissive color.
1209                         self.fw.write_float(desc.emissive[1])   # Emissive color.
1210                         self.fw.write_float(desc.emissive[2])   # Emissive color.
1211                         self.fw.write_float(desc.shininess)
1212                         self.fw.write_float(desc.alpha)
1213                         self.fw.write_int(0)                    # Reserved
1214
1215         def write_col_pal(self):
1216                 if options.verbose >= 2:
1217                         print 'Writing color palette.'
1218                 self.fw.write_short(32)                     # Color palette opcode.
1219                 self.fw.write_short(4228)                   # Length of record
1220                 self.fw.pad(128)
1221                 try:
1222                         cpalette = self.scene.properties['FLT']['Color Palette']
1223                 except:
1224                         cpalette = defaultp.pal
1225                 count = len(cpalette)
1226                 for i in xrange(count):
1227                         color = struct.unpack('>BBBB',struct.pack('>I',cpalette[i]))
1228                         self.fw.write_uchar(color[3])               # alpha
1229                         self.fw.write_uchar(color[2])               # b
1230                         self.fw.write_uchar(color[1])               # g
1231                         self.fw.write_uchar(color[0])               # r
1232                 self.fw.pad(max(4096-count*4, 0))
1233
1234         def write(self):
1235                 self.write_header()
1236                 self.write_vert_pal()
1237                 self.write_tex_pal()
1238                 self.write_mat_pal()
1239                 self.write_col_pal()
1240
1241                 self.write_push()
1242                 
1243                 for child in self.children:
1244                         child.write()
1245                 self.write_pop()
1246         
1247         def export_textures(self,texturepath):
1248                 for i in xrange(self.GRR.texture_count()):
1249                         texture = self.GRR.texture_lst[i]
1250                         
1251                         if options.state['copytex']:
1252                                 filename = os.path.normpath(os.path.join(options.state['texturespath'], os.path.basename(self.GRR.request_texture_filename(i))))
1253                         else:
1254                                 filename = os.path.normpath(self.GRR.request_texture_filename(i))
1255                         
1256                         tex_files[texture.name] = filename
1257
1258         def blender_export(self):
1259                 Node.blender_export(self)
1260                 self.export_textures(self)
1261                 return self.xrefnames
1262         def __init__(self, scene, fw):
1263                 self.fw = fw
1264                 self.opcode = 1
1265                 self.childtypes = [73,14,2,63]
1266                 self.scene = scene
1267                 self.childhash = dict()
1268                 self.parenthash = dict()
1269                 self.child_objects = list()
1270                 self.mnodes = list()
1271                 self.xrefnames = list()
1272                 for i in self.scene.objects:
1273                         self.parenthash[i.name] = list()
1274                         self.childhash[i.name] = False
1275                 for i in self.scene.objects:
1276                         if i.parent:
1277                                 self.childhash[i.parent.name] = True
1278                                 self.parenthash[i.parent.name].append(i)
1279                         else:
1280                                 self.child_objects.append(i)
1281
1282                 self.GRR = GlobalResourceRepository()
1283                 Node.__init__(self, None, self, None,None)
1284
1285 def write_attribute_files():
1286         for imgname in tex_files:
1287                 blentex = Blender.Image.Get(imgname)
1288                 exportdict = FLT_Records['Image'].copy()
1289                 
1290                 if blentex.properties.has_key('FLT'):
1291                         for key in exportdict.keys():
1292                                 if blentex.properties.has_key(key):
1293                                         exportdict[key] = blentex.properties['FLT'][key]
1294                 
1295                 # ClampX/Y override
1296                 if blentex.clampX:
1297                         exportdict['11i!WrapU'] = 1
1298                 if blentex.clampY:
1299                         exportdict['12i!WrapV'] = 1 
1300                 
1301                 exportdict['16i!Enviorment'] = 0 
1302                 
1303                 # File type
1304                 typecode = 0
1305                 try:
1306                         typestring = os.path.splitext(blentex.getFilename())[1]
1307                         
1308                         if typestring == '.rgba':
1309                                 typecode = 5
1310                         elif typestring == '.rgb':
1311                                 typecode = 4
1312                         elif typestring == '.inta':
1313                                 typecode = 3
1314                         elif typestring == '.int':
1315                                 typecode = 2
1316                 except:
1317                         pass
1318                 
1319                 exportdict['7i!File Format'] = typecode
1320
1321                 fw = FltOut(tex_files[imgname] + '.attr')
1322                 size = blentex.getSize()
1323                 fw.write_int(size[0])
1324                 fw.write_int(size[1])
1325                 for key in records['Image']:
1326                         (ftype,length,propname) = records['Image'][key]
1327                         write_prop(fw,ftype,exportdict[propname],length)
1328                 fw.close_file()
1329
1330 #globals used by the scene export function
1331 exportlevel = None
1332 xrefsdone = None
1333
1334 def dbexport_internal(scene):
1335         global exportlevel
1336         global xrefsdone
1337         global options
1338
1339         if exportlevel == 0 or not options.state['externalspath']:
1340                 fname = os.path.join(options.state['basepath'],scene.name + '.flt')
1341         else:
1342                 fname = os.path.join(options.state['externalspath'],scene.name + '.flt')
1343         
1344         fw = FltOut(fname)
1345         db = Database(scene,fw)
1346         
1347         if options.verbose >= 1:
1348                 print 'Pass 1: Exporting ', scene.name,'.flt from Blender.\n'
1349         
1350         xreflist = db.blender_export()
1351         if options.verbose >= 1:
1352                 print 'Pass 2: Writing %s\n' % fname
1353         db.write()
1354         fw.close_file()
1355         
1356         if options.state['doxrefs']:
1357                 for xname in xreflist:
1358                         try:
1359                                 xrefscene = Blender.Scene.Get(xname)
1360                         except:
1361                                 xrefscene = None
1362                         if xrefscene and xname not in xrefsdone:
1363                                 xrefsdone.append(xname)
1364                                 exportlevel+=1
1365                                 dbexport_internal(xrefscene)
1366                                 exportlevel-=1
1367         return fname
1368 #main database export function
1369 def dbexport():
1370         global exportlevel
1371         global xrefsdone
1372         exportlevel = 0
1373         xrefsdone = list()
1374         
1375         Blender.Window.WaitCursor(True)
1376         time1 = Blender.sys.time() # Start timing
1377         
1378         if options.verbose >= 1:
1379                 print '\nOpenFlight Exporter'
1380                 print 'Version:', __version__
1381                 print 'Author: Greg MacDonald, Geoffrey Bantle'
1382                 print __url__[2]
1383                 print
1384         
1385         fname = dbexport_internal(Blender.Scene.GetCurrent())
1386         if options.verbose >=1:
1387                 print 'Done in %.4f sec.\n' % (Blender.sys.time() - time1)
1388         Blender.Window.WaitCursor(False)
1389         
1390         #optional: Copy textures
1391         if options.state['copytex']:
1392                 for imgname in tex_files:
1393                         #Check to see if texture exists in target directory
1394                         if not os.path.exists(tex_files[imgname]):
1395                                 #Get original Blender file name
1396                                 origpath = Blender.sys.expandpath(Blender.Image.Get(imgname).getFilename())
1397                                 #copy original to new
1398                                 if os.path.exists(origpath):
1399                                         shutil.copyfile(origpath,tex_files[imgname])
1400         
1401         #optional: Write attribute files
1402         if options.state['attrib']:
1403                 write_attribute_files()
1404
1405         if options.state['xapp']:
1406                 cmd= options.state['xappath'] + " " + fname 
1407                 status = os.system(cmd)
1408         
1409
1410 #Begin UI code
1411 FLTExport = None
1412 FLTClose = None
1413 FLTLabel = None
1414
1415 FLTBaseLabel = None
1416 FLTTextureLabel = None
1417 FLTXRefLabel = None
1418
1419 FLTBaseString = None
1420 FLTTextureString = None
1421 FLTXRefString = None
1422
1423 FLTBasePath = None
1424 FLTTexturePath = None
1425 FLTXRefPath = None
1426
1427 FLTShadeExport = None
1428 FLTShadeDefault = None
1429
1430 FLTCopyTex = None
1431 FLTDoXRef = None
1432 FLTGlobal = None
1433
1434 FLTScale = None
1435
1436 FLTXAPP = None
1437 FLTXAPPath = None
1438 FLTXAPPString = None
1439 FLTXAPPLabel = None
1440 FLTXAPPChooser = None
1441
1442 FLTAttrib = None
1443
1444 def setshadingangle(ID,val):
1445         global options
1446         options.state['shading_default'] = val
1447 def setBpath(fname):
1448         global options
1449         options.state['basepath'] = os.path.dirname(fname)
1450         #update xref and textures path too....
1451         if(os.path.exists(os.path.join(options.state['basepath'],'externals'))):
1452                 options.state['externalspath'] = os.path.join(options.state['basepath'],'externals')
1453         if(os.path.exists(os.path.join(options.state['basepath'],'textures'))):
1454                 options.state['texturespath'] = os.path.join(options.state['basepath'],'textures')
1455 def setexportscale(ID,val):
1456         global options
1457         options.state['scale'] = val
1458
1459 def setTpath(fname):
1460         global options
1461         options.state['texturespath'] = os.path.dirname(fname)
1462 def setXpath(fname):
1463         global options
1464         options.state['externalspath'] = os.path.dirname(fname)
1465 def setXApath(fname):
1466         global options
1467         options.state['xappath'] = fname
1468 def event(evt, val):
1469         x = 1
1470 def but_event(evt):
1471         global options
1472         
1473         global FLTExport
1474         global FLTClose 
1475         global FLTLabel
1476         
1477         global FLTBaseLabel
1478         global FLTTextureLabel
1479         global FLTXRefLabel
1480
1481         global FLTBaseString
1482         global FLTTextureString
1483         global FLTXRefString
1484         
1485         global FLTBasePath
1486         global FLTTexturePath
1487         global FLTXRefPath
1488         
1489         global FLTShadeExport
1490         global FLTShadeDefault
1491         
1492         global FLTCopyTex
1493         global FLTDoXRef
1494         global FLTGlobal
1495         
1496         global FLTScale
1497         
1498         
1499         global FLTXAPP
1500         global FLTXAPPath
1501         global FLTXAPPString
1502         global FLTXAPPLabel     
1503         global FLTXAPPChooser
1504
1505         global FLTAttrib
1506         
1507         #choose base path for export
1508         if evt == 4:
1509                 Blender.Window.FileSelector(setBpath, "DB Root", options.state['basepath'])
1510                 
1511         #choose XREF path
1512         if evt == 6:
1513                 Blender.Window.FileSelector(setXpath,"DB Externals",options.state['externalspath'])
1514
1515         #choose texture path
1516         if evt == 8:
1517                 Blender.Window.FileSelector(setTpath,"DB Textures",options.state['texturespath'])
1518
1519         #export shading toggle
1520         if evt == 9:
1521                 options.state['export_shading'] = FLTShadeExport.val
1522         #export Textures
1523         if evt == 11:
1524                 options.state['copytex']= FLTCopyTex.val
1525         #export XRefs
1526         if evt == 13:
1527                 options.state['doxrefs'] = FLTDoXRef.val
1528         #export Transforms
1529         if evt == 12:
1530                 options.state['transform'] = FLTGlobal.val
1531                 
1532         if evt == 14:
1533                 options.state['xapp'] = FLTXAPP.val
1534         if evt == 16:
1535                 Blender.Window.FileSelector(setXApath,"External Application",options.state['xappath'])
1536         if evt == 20:
1537                 options.state['attrib'] = FLTAttrib.val
1538         
1539         #Export DB
1540         if evt == 1:
1541                 dbexport()
1542         
1543         #exit
1544         if evt == 2:
1545                 Draw.Exit()
1546
1547         options.write_state()
1548
1549 from Blender.BGL import *
1550 from Blender import Draw
1551 def gui():
1552         
1553         global options
1554         
1555         global FLTExport
1556         global FLTClose 
1557         global FLTLabel
1558         
1559         global FLTBaseLabel
1560         global FLTTextureLabel
1561         global FLTXRefLabel
1562
1563         global FLTBaseString
1564         global FLTTextureString
1565         global FLTXRefString
1566         
1567         global FLTBasePath
1568         global FLTTexturePath
1569         global FLTXRefPath
1570         
1571         global FLTShadeExport
1572         global FLTShadeDefault
1573         
1574         global FLTCopyTex
1575         global FLTDoXRef
1576         global FLTGlobal
1577         
1578         global FLTScale
1579         
1580         global FLTXAPP
1581         global FLTXAPPath
1582         global FLTXAPPString
1583         global FLTXAPPLabel
1584         global FLTXAPPChooser   
1585         
1586         global FLTAttrib
1587         
1588         glClearColor(0.880,0.890,0.730,1.0 )
1589         glClear(GL_COLOR_BUFFER_BIT)
1590         
1591         areas = Blender.Window.GetScreenInfo()
1592         curarea = Blender.Window.GetAreaID()
1593         curRect = None
1594         
1595         for area in areas:
1596                 if area['id'] == curarea:
1597                         curRect = area['vertices']
1598                         break
1599         
1600         width = curRect[2] - curRect[0]
1601         height = curRect[3] - curRect[1]
1602         #draw from top to bottom....
1603         cx = 50
1604         #Draw Title Bar...
1605         #glRasterPos2d(cx, curRect[3]-100)
1606         #FLTLabel = Draw.Text("FLT Exporter V2.0",'large')
1607         cy = height - 80
1608         
1609         FLTBaseLabel = Draw.Label("Base Path:",cx,cy,100,20)
1610         FLTBaseString = Draw.String("",3,cx+100,cy,300,20,options.state['basepath'],255,"Folder to export to")
1611         FLTBaseChooser = Draw.PushButton("...",4,cx+400,cy,20,20,"Choose Folder")
1612         
1613         cy = cy-40
1614         
1615         #externals path
1616         FLTXRefLabel = Draw.Label("XRefs:",cx,cy,100,20)
1617         FLTXRefString = Draw.String("",5,cx+100,cy,300,20,options.state['externalspath'],255,"Folder for external references")
1618         FLTXRefChooser = Draw.PushButton("...",6,cx+400,cy,20,20,"Choose Folder")
1619         cy = cy-40
1620         #Textures path
1621         FLTTextureLabel = Draw.Label("Textures:",cx,cy,100,20)
1622         FLTTextureString = Draw.String("",7,cx+100,cy,300,20,options.state['texturespath'],255,"Folder for texture files")
1623         FLTTextureChooser = Draw.PushButton("...",8,cx+400,cy,20,20,"Choose Folder")
1624         cy=cy-40
1625         #External application path
1626         FLTXAPPLabel = Draw.Label("XApp:",cx,cy,100,20)
1627         FLTXAPPString = Draw.String("",15,cx+100,cy,300,20,options.state['xappath'],255,"External application to launch when done")
1628         FLTXAPPChooser = Draw.PushButton("...",16,cx+400, cy,20,20,"Choose Folder")
1629         
1630         cy = cy-60
1631         #Shading Options
1632         FLTShadeExport = Draw.Toggle("Default Shading",9,cx,cy,100,20,options.state['export_shading'],"Turn on export of custom shading")
1633         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)
1634         
1635         cy = cy-40
1636         FLTScale = Draw.Number("Export Scale",14,cx,cy,220,20,options.state['scale'],0.0,100.0,"Export scaling factor",setexportscale)
1637         
1638         cy = cy-40
1639         #misc Options
1640         FLTCopyTex = Draw.Toggle("Copy Textures",11,cx,cy,220,20,options.state['copytex'],"Copy textures to folder indicated above")
1641         cy = cy-40
1642         FLTGlobal = Draw.Toggle("Export Transforms",12,cx,cy,220,20,options.state['transform'],"If unchecked, Global coordinates are used (recommended)")
1643         cy = cy-40
1644         FLTDoXRef = Draw.Toggle("Export XRefs", 13,cx,cy,220,20,options.state['doxrefs'],"Export External references (only those below current scene!)")
1645         cy = cy-40
1646         FLTXAPP = Draw.Toggle("Launch External App", 14, cx,cy,220,20,options.state['xapp'],"Launch External Application on export")
1647         cy = cy-40
1648         FLTAttrib = Draw.Toggle("Write Attribute Files", 20, cx, cy, 220,20,options.state['attrib'], "Write Texture Attribute files")
1649         #FLTXAPPATH = Draw.String("",15,cx,cy,300,20,options.xappath,255,"External application path")
1650         
1651
1652         #Draw export/close buttons
1653         FLTExport = Draw.PushButton("Export",1,cx,20,100,20,"Export to FLT")
1654         FLTClose = Draw.PushButton("Close", 2, cx+120,20,100,20,"Close window")
1655         
1656
1657 Draw.Register(gui,event,but_event)