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