3ba118c0d184149dfd2b70198e7f53f61c1b3f37
[blender.git] / release / scripts / flt_import.py
1 #!BPY
2 """ Registration info for Blender menus:
3 Name: 'OpenFlight (.flt)...'
4 Blender: 245
5 Group: 'Import'
6 Tip: 'Import OpenFlight (.flt)'
7 """
8
9 __author__ = "Greg MacDonald, Campbell Barton, Geoffrey Bantle"
10 __version__ = "2.0 11/21/07"
11 __url__ = ("blender", "elysiun", "Author's homepage, http://sourceforge.net/projects/blight/")
12 __bpydoc__ = """\
13 This script imports OpenFlight files into Blender. 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/Import/openflight_flt
18
19 Note: This file is a grab-bag of old and new code. It needs some cleanup still.
20 """
21
22 # flt_import.py is an OpenFlight importer for blender.
23 # Copyright (C) 2005 Greg MacDonald, 2007  Blender Foundation
24 #
25 # This program is free software; you can redistribute it and/or
26 # modify it under the terms of the GNU General Public License
27 # as published by the Free Software Foundation; either version 2
28 # of the License, or (at your option) any later version.
29 #
30 # This program is distributed in the hope that it will be useful,
31 # but WITHOUT ANY WARRANTY; without even the implied warranty of
32 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
33 # GNU General Public License for more details.
34 #
35 # You should have received a copy of the GNU General Public License
36 # along with this program; if not, write to the Free Software
37 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
38
39 import Blender
40 import os
41 import BPyMesh
42 import BPyImage
43 import flt_filewalker 
44 import flt_properties
45 reload(flt_properties)
46 from flt_properties import *
47
48 #Globals. Should Clean these up and minimize their usage.
49
50 typecodes = ['c','C','s','S','i','I','f','d','t']
51 records = dict()
52
53 FLTBaseLabel = None
54 FLTBaseString = None
55 FLTBaseChooser = None
56 FLTExport = None
57 FLTClose = None
58 FLTDoXRef = None
59 FLTScale = None
60 FLTShadeImport = None
61 FLTAttrib = None
62
63 Vector= Blender.Mathutils.Vector
64 FLOAT_TOLERANCE = 0.01
65
66 FF = flt_filewalker.FileFinder()
67 current_layer = 0x01
68
69 global_prefs = dict()
70 global_prefs['verbose']= 4
71 global_prefs['get_texture'] = True
72 global_prefs['get_diffuse'] = True
73 global_prefs['get_specular'] = False
74 global_prefs['get_emissive'] = False
75 global_prefs['get_alpha'] = True
76 global_prefs['get_ambient'] = False
77 global_prefs['get_shininess'] = True
78 global_prefs['color_from_face'] = True
79 global_prefs['fltfile']= ''
80 global_prefs['smoothshading'] = 1
81 global_prefs['doxrefs'] = 1
82 global_prefs['scale'] = 1.0
83 global_prefs['attrib'] = 0
84 msg_once = False
85
86 throw_back_opcodes = [2, 73, 4, 11, 96, 14, 91, 98, 63,111] # Opcodes that indicate its time to return control to parent.
87 do_not_report_opcodes = [76, 78, 79, 80, 81, 82, 94, 83, 33, 112, 100, 101, 102, 97, 31, 103, 104, 117, 118, 120, 121, 124, 125]
88
89 #Process FLT record definitions
90 for record in FLT_Records:
91         props = dict()
92         for prop in FLT_Records[record]:
93                 position = ''
94                 slice = 0
95                 (format,name) = prop.split('!')
96                 for i in format:
97                         if i not in typecodes:
98                                 position = position + i
99                                 slice = slice + 1
100                         else:
101                                 break
102                 type = format[slice:]
103                 length = type[1:] 
104                 if len(length) == 0:
105                         length = 1
106                 else:
107                         type = type[0]
108                         length = int(length)
109                 
110                 props[int(position)] = (type,length,prop)
111         records[record] = props
112
113 def col_to_gray(c):
114         return 0.3*c[0] + 0.59*c[1] + 0.11*c[2]
115 class MaterialDesc:
116         # Was going to use int(f*1000.0) instead of round(f,3), but for some reason
117         # round produces better results, as in less dups.
118         def make_key(self):
119                 key = list()
120                 if global_prefs['get_texture']:
121                         if self.tex0:
122                                 key.append(self.tex0.getName())
123                         else:
124                                 key.append(None)
125                 
126                 if global_prefs['get_alpha']:
127                         key.append(round(self.alpha, 3))
128                 else:
129                         key.append(None)
130                         
131                 if global_prefs['get_shininess']:
132                         key.append(round(self.shininess, 3))
133                 else:
134                         key.append(None)
135                 
136                 if global_prefs['get_emissive']:
137                         key.append(round(self.emissive, 3))
138                 else:
139                         key.append(None)
140                 
141                 if global_prefs['get_ambient']:
142                         key.append(round(self.ambient, 3))
143                 else:
144                         key.append(None)
145                 
146                 if global_prefs['get_specular']:
147                         for n in self.specular:
148                                 key.append(round(n, 3))
149                 else:
150                         key.extend([None, None, None])
151                 
152                 if global_prefs['get_diffuse']:
153                         for n in self.diffuse:
154                                 key.append(round(n, 3))
155                 else:
156                         key.extend([None, None, None])
157                 
158 #        key.extend(self.face_props.values())
159                 
160                 return tuple(key)
161
162         def __init__(self):
163                 self.name = 'Material'
164                 # Colors, List of 3 floats.
165                 self.diffuse = [1.0, 1.0, 1.0]
166                 self.specular = [1.0, 1.0, 1.0]
167
168                 # Scalars
169                 self.ambient = 0.0 # [0.0, 1.0]
170                 self.emissive = 0.0 # [0.0, 1.0]
171                 self.shininess = 0.5 # Range is [0.0, 2.0]
172                 self.alpha = 1.0 # Range is [0.0, 1.0]
173
174                 self.tex0 = None
175                 
176                 # OpenFlight Face attributes
177                 self.face_props = dict.fromkeys(['comment', 'ir color', 'priority', 
178                                                         'draw type', 'texture white', 'template billboard',
179                                                         'smc', 'fid', 'ir material', 'lod generation control',
180                                                         'flags', 'light mode'])
181
182 class VertexDesc:
183         def make_key(self):
184                 return round(self.x, 6), round(self.y, 6), round(self.z, 6)
185                 
186         def __init__(self):
187                 
188                 # Assign later, save memory, all verts have a loc
189                 self.x = 0.0
190                 self.y = 0.0
191                 self.z = 0.0
192                 
193                 
194                 self.nx = 0.0
195                 self.ny = 0.0
196                 self.nz = 0.0
197                 
198                 self.uv= Vector(0,0)
199                 self.cindex = 127 #default/lowest
200                 self.cnorm = False        
201
202 class LightPointAppDesc:
203         def make_key(self):
204                 d = dict(self.props)
205                 del d['id']
206                 del d['type']
207                 
208                 if d['directionality'] != 0: # not omni
209                         d['nx'] = 0.0
210                         d['ny'] = 0.0
211                         d['nz'] = 0.0
212                 
213                 return tuple(d.values())
214                 
215         def __init__(self):
216                 self.props = dict()
217                 self.props.update({'type': 'LPA'})
218                 self.props.update({'id': 'ap'})
219                 # Attribs not found in inline lightpoint.
220                 self.props.update({'visibility range': 0.0})
221                 self.props.update({'fade range ratio': 0.0})
222                 self.props.update({'fade in duration': 0.0})
223                 self.props.update({'fade out duration': 0.0})
224                 self.props.update({'LOD range ratio': 0.0})
225                 self.props.update({'LOD scale': 0.0})
226
227 class GlobalResourceRepository:
228         def request_lightpoint_app(self, desc, scene):
229                 match = self.light_point_app.get(desc.make_key())
230                 
231                 if match:
232                         return match.getName()
233                 else:
234                         # Create empty and fill with properties.
235                         name = desc.props['type'] + ': ' + desc.props['id']
236                         object = Blender.Object.New('Empty', name)
237                         scene.objects.link(object)
238                         object.Layers= current_layer
239                         object.sel= 1
240                         
241                         # Attach properties
242                         for name, value in desc.props.iteritems():
243                                 object.addProperty(name, value)
244                         
245                         self.light_point_app.update({desc.make_key(): object})
246                         
247                         return object.getName()
248         
249         # Dont use request_vert - faster to make it from the vector direct.
250         """
251         def request_vert(self, desc):
252                 match = self.vert_dict.get(desc.make_key())
253
254                 if match:
255                         return match
256                 else:
257                         vert = Blender.Mathutils.Vector(desc.x, desc.y, desc.z)
258                         ''' IGNORE_NORMALS
259                         vert.no[0] = desc.nx
260                         vert.no[1] = desc.ny
261                         vert.no[2] = desc.nz
262                         '''
263                         self.vert_dict.update({desc.make_key(): vert})
264                         return vert
265         """
266         def request_mat(self, mat_desc):
267                 match = self.mat_dict.get(mat_desc.make_key())
268                 if match: return match
269                 
270                 mat = Blender.Material.New(mat_desc.name)
271
272                 if mat_desc.tex0 != None:
273                         mat.setTexture(0, mat_desc.tex0, Blender.Texture.TexCo.UV)
274
275                 mat.setAlpha(mat_desc.alpha)
276                 mat.setSpec(mat_desc.shininess)
277                 mat.setHardness(255)
278                 mat.setEmit(mat_desc.emissive)
279                 mat.setAmb(mat_desc.ambient)
280                 mat.setSpecCol(mat_desc.specular)
281                 mat.setRGBCol(mat_desc.diffuse)
282                 
283                 # Create a text object to store openflight face attribs until
284                 # user properties can be set on materials.
285 #        t = Blender.Text.New('FACE: ' + mat.getName())
286 #
287 #        for name, value in mat_desc.face_props.items():
288 #            t.write(name + '\n' + str(value) + '\n\n')    
289                                 
290                 self.mat_dict.update({mat_desc.make_key(): mat})
291
292                 return mat
293                 
294         def request_image(self, filename_with_path):
295                 if not global_prefs['get_texture']: return None
296                 return BPyImage.comprehensiveImageLoad(filename_with_path, global_prefs['fltfile']) # Use join in case of spaces 
297                 
298         def request_texture(self, image):
299                 if not global_prefs['get_texture']:
300                         return None
301
302                 tex = self.tex_dict.get(image.filename)
303                 if tex: return tex
304                 
305                 tex = Blender.Texture.New(Blender.sys.basename(image.filename))
306                 tex.setImage(image)
307                 tex.setType('Image')
308                 self.tex_dict.update({image.filename: tex})
309                 return tex
310                 
311         def __init__(self):
312                 
313                 #list of scenes xrefs belong to.
314                 self.xrefs = dict()
315                 # material
316                 self.mat_dict = dict()
317                 mat_lst = Blender.Material.Get()
318                 for mat in mat_lst:
319                         mat_desc = MaterialDesc()
320                         mapto_lst = mat.getTextures()
321                         if mapto_lst[0]:
322                                 mat_desc.tex0 = mapto_lst[0].tex
323                         else:
324                                 mat_desc.tex0 = None
325                         mat_desc.alpha = mat.getAlpha()
326                         mat_desc.shininess = mat.getSpec()
327                         mat_desc.emissive = mat.getEmit()
328                         mat_desc.ambient = mat.getAmb()
329                         mat_desc.specular = mat.getSpecCol()
330                         mat_desc.diffuse = mat.getRGBCol()
331                         
332                         self.mat_dict.update({mat_desc.make_key(): mat})
333                         
334                 # texture
335                 self.tex_dict = dict()
336                 tex_lst = Blender.Texture.Get()
337                 
338                 for tex in tex_lst:
339                         img = tex.getImage()
340                         # Only interested in textures with images.
341                         if img:
342                                 self.tex_dict.update({img.filename: tex})
343                         
344                 # vertex
345                 # self.vert_dict = dict()
346                 
347                 # light point
348                 self.light_point_app = dict()
349                 
350 class Handler:
351         def in_throw_back_lst(self, opcode):
352                 return opcode in self.throw_back_lst
353                 
354         def handle(self, opcode):
355                 return self.handler[opcode]()
356         
357         def handles(self, opcode):
358                 return opcode in self.handler.iterkeys()
359         
360         def throws_back_all_unhandled(self):
361                 return self.throw_back_unhandled
362                 
363         def set_throw_back_lst(self, a):
364                 self.throw_back_lst = a
365                 
366         def set_throw_back_all_unhandled(self):
367                 self.throw_back_unhandled = True
368                 
369         def set_only_throw_back_specified(self):
370                 self.throw_back_unhandled = False
371                 
372         def set_handler(self, d):
373                 self.handler = d
374                 
375         def __init__(self):
376                 # Dictionary of opcodes to handler methods.
377                 self.handler = dict()
378                 # Send all opcodes not handled to the parent node.
379                 self.throw_back_unhandled = False
380                 # If throw_back_unhandled is False then only throw back
381                 # if the opcodes in throw_back are encountered.
382                 self.throw_back_lst = list()
383                 
384 class Node:
385         def blender_import(self):
386                 if self.opcode in opcode_name and global_prefs['verbose'] >= 2:
387                         for i in xrange(self.get_level()):
388                                 print ' ',
389                         print opcode_name[self.opcode],
390                         print '-', self.props['id'],
391                         print '-', self.props['comment'],
392
393                         print
394                 
395                 for child in self.children:
396                         child.blender_import()
397                         
398 # Import comment.
399 #        if self.props['comment'] != '':
400 #            name = 'COMMENT: ' + self.props['id']
401 #            t = Blender.Text.New(name)
402 #            t.write(self.props['comment'])
403 #            self.props['comment'] = name
404                 
405         # Always ignore extensions and anything in between them.
406         def parse_push_extension(self):
407                 self.saved_handler = self.active_handler
408                 self.active_handler = self.extension_handler
409                 return True
410         
411         def parse_pop_extension(self):
412                 self.active_handler = self.saved_handler
413                 return True
414         
415         def parse_push(self):
416                 self.header.fw.up_level()
417                 # Ignore unknown children.
418                 self.ignore_unhandled = True
419                 # Don't do child records that might overwrite parent info. ex: longid
420                 self.active_handler = self.child_handler
421                 return True
422                 
423         def parse_pop(self):
424                 self.header.fw.down_level()
425                 
426                 if self.header.fw.get_level() == self.level:
427                         return False
428                 
429                 return True
430         
431         def parse(self):
432                 while self.header.fw.begin_record():
433                         opcode = self.header.fw.get_opcode()
434
435                         # Print out info on opcode and tree level.
436                         if global_prefs['verbose'] >= 3:
437                                 p = ''
438                                 for i in xrange(self.header.fw.get_level()):
439                                         p = p + '  '
440                                 if opcode in opcode_name:
441                                         p = p + opcode_name[opcode]
442                                 else:
443                                         if global_prefs['verbose'] >= 1:
444                                                 print 'undocumented opcode', opcode
445                                         continue
446                                                         
447                         if self.global_handler.handles(opcode):
448                                 if global_prefs['verbose'] >= 3:
449                                         print p + ' handled globally'
450                                 if self.global_handler.handle(opcode) == False:
451                                         break
452                                         
453                         elif self.active_handler.handles(opcode):
454                                 if global_prefs['verbose'] >= 4:
455                                         print p + ' handled'
456                                 if self.active_handler.handle(opcode) == False:
457                                         break
458                                         
459                         else:
460                                 if self.active_handler.throws_back_all_unhandled():
461                                         if global_prefs['verbose'] >= 3:
462                                                 print p + ' handled elsewhere'              
463                                         self.header.fw.repeat_record()
464                                         break
465
466                                 elif self.active_handler.in_throw_back_lst(opcode):
467                                         if global_prefs['verbose'] >= 3:
468                                                 print p + ' handled elsewhere'              
469                                         self.header.fw.repeat_record()
470                                         break
471
472                                 else:
473                                         if global_prefs['verbose'] >= 3:
474                                                 print p + ' ignored'
475                                         elif global_prefs['verbose'] >= 1 and not opcode in do_not_report_opcodes and opcode in opcode_name:   
476                                                 print 'not handled'
477                                         
478         def get_level(self):
479                 return self.level
480                 
481         def parse_long_id(self):
482                 self.props['id'] = self.header.fw.read_string(self.header.fw.get_length()-4)
483                 return True
484
485         def parse_comment(self):
486                 self.props['comment'] = self.header.fw.read_string(self.header.fw.get_length()-4)
487                 return True
488         
489         def parse_record(self):
490                 self.props['type'] = self.opcode
491                 props = records[self.opcode]
492                 propkeys = props.keys()
493                 propkeys.sort()
494                 for position in propkeys:
495                         (type,length,name) = props[position]
496                         self.props[name] = read_prop(self.header.fw,type,length)
497                 try: #remove me!
498                         self.props['id'] = self.props['3t8!id']
499                 except:
500                         pass
501         def __init__(self, parent, header):
502                 self.root_handler = Handler()
503                 self.child_handler = Handler()
504                 self.extension_handler = Handler()
505                 self.global_handler = Handler()
506                 
507                 self.global_handler.set_handler({21: self.parse_push_extension})
508                 self.active_handler = self.root_handler
509                 
510                 # used by parse_*_extension
511                 self.extension_handler.set_handler({22: self.parse_pop_extension})
512                 self.saved_handler = None
513                 
514                 self.header = header
515                 self.children = list()
516
517                 self.parent = parent
518
519                 if parent:
520                         parent.children.append(self)
521
522                 self.level = self.header.fw.get_level()
523                 self.opcode = self.header.fw.get_opcode()
524
525                 self.props = {'id': 'unnamed', 'comment': '', 'type': 'untyped'}
526
527 class VertexPalette(Node):
528         def __init__(self, parent):
529                 Node.__init__(self, parent, parent.header)
530                 self.root_handler.set_handler({68: self.parse_vertex_c,
531                                                                            69: self.parse_vertex_cn,
532                                                                            70: self.parse_vertex_cnuv,
533                                                                            71: self.parse_vertex_cuv})
534                 self.root_handler.set_throw_back_all_unhandled()
535
536                 self.vert_desc_lst = list()
537                 self.blender_verts = list()
538                 self.offset = 8
539                 # Used to create a map from byte offset to vertex index.
540                 self.index = dict()
541         
542         
543         def blender_import(self):
544                 self.blender_verts.extend([Vector(vert_desc.x, vert_desc.y, vert_desc.z) for vert_desc in self.vert_desc_lst ])
545
546         def parse_vertex_common(self):
547                 # Add this vertex to an offset to index dictionary.
548                 #self.index_lst.append( (self.offset, self.next_index) )
549                 self.index[self.offset]= len(self.index)
550                 
551                 # Get ready for next record.
552                 self.offset += self.header.fw.get_length()
553
554                 v = VertexDesc()
555
556                 self.header.fw.read_ahead(2)
557                 v.flags = self.header.fw.read_short()
558
559                 v.x = self.header.fw.read_double()
560                 v.y = self.header.fw.read_double()
561                 v.z = self.header.fw.read_double()
562
563                 return v
564
565         def parse_vertex_post_common(self, v):
566                 #if not v.flags & 0x2000: # 0x2000 = no color
567                         #if v.flags & 0x1000: # 0x1000 = packed color
568                         #       v.a = self.header.fw.read_uchar()
569                         #       v.b = self.header.fw.read_uchar()
570                         #       v.g = self.header.fw.read_uchar()
571                         #       v.r = self.header.fw.read_uchar()
572                         #else:
573                 self.header.fw.read_ahead(4) #skip packed color
574                 v.cindex = self.header.fw.read_uint()
575                 self.vert_desc_lst.append(v)
576                 return True
577
578         def parse_vertex_c(self):
579                 v = self.parse_vertex_common()
580
581                 self.parse_vertex_post_common(v)
582                 
583                 return True
584
585         def parse_vertex_cn(self):
586                 v = self.parse_vertex_common()
587                 v.cnorm = True
588                 v.nx = self.header.fw.read_float()
589                 v.ny = self.header.fw.read_float()
590                 v.nz = self.header.fw.read_float()
591                 
592                 self.parse_vertex_post_common(v)
593                 
594                 return True
595
596         def parse_vertex_cuv(self):
597                 v = self.parse_vertex_common()
598
599                 v.uv[:] = self.header.fw.read_float(), self.header.fw.read_float()
600
601                 self.parse_vertex_post_common(v)
602                 
603                 return True
604
605         def parse_vertex_cnuv(self):
606                 v = self.parse_vertex_common()
607                 v.cnorm = True
608                 v.nx = self.header.fw.read_float()
609                 v.ny = self.header.fw.read_float()
610                 v.nz = self.header.fw.read_float()
611                 
612                 v.uv[:] = self.header.fw.read_float(), self.header.fw.read_float()
613
614                 self.parse_vertex_post_common(v)
615                 
616                 return True
617
618         def parse(self): # Run once per import
619                 Node.parse(self)
620
621 class InterNode(Node):
622         def __init__(self):
623                 self.object = None
624                 self.mesh = None
625                 self.hasMesh = False
626                 self.faceLs= []
627                 self.matrix = None
628                 self.vis = True
629                 self.hasmtex = False
630                 self.uvlayers = dict()
631                 self.blayernames = dict()
632                 self.subfacelevel = 0
633                 
634                 mask = 2147483648
635                 for i in xrange(7):
636                         self.uvlayers[mask] = False
637                         mask = mask / 2
638                 
639
640         def blender_import_my_faces(self):
641
642                 # Add the verts onto the mesh
643                 blender_verts= self.header.vert_pal.blender_verts
644                 vert_desc_lst= self.header.vert_pal.vert_desc_lst
645                 
646                 vert_list= [ i for flt_face in self.faceLs for i in flt_face.indices] #splitting faces apart. Is this a good thing?
647                 face_edges= []
648                 face_verts= []
649                 self.mesh.verts.extend([blender_verts[i] for i in vert_list])
650                 
651                 new_faces= []
652                 new_faces_props= []
653                 ngon= BPyMesh.ngon
654                 vert_index= 1
655                 
656                 #add vertex color layer for baked face colors.
657                 self.mesh.addColorLayer("FLT_Fcol")
658                 self.mesh.activeColorLayer = "FLT_Fcol"
659                 
660                 FLT_OrigIndex = 0
661                 for flt_face in self.faceLs:
662                         if flt_face.tex_index != -1:
663                                 try:
664                                         image= self.header.tex_pal[flt_face.tex_index][1]
665                                 except KeyError:
666                                         image= None
667                         else:
668                                 image= None
669                         face_len= len(flt_face.indices)
670                         
671                         #create dummy uvert dicts
672                         if len(flt_face.uverts) == 0:
673                                 for i in xrange(face_len):
674                                         flt_face.uverts.append(dict())
675                         #May need to patch up MTex info
676                         if self.hasmtex:
677                                 #For every layer in mesh, there should be corresponding layer in the face
678                                 for mask in self.uvlayers.keys():
679                                         if self.uvlayers[mask]:
680                                                 if not flt_face.uvlayers.has_key(mask): #Does the face have this layer?
681                                                         #Create Layer info for this face
682                                                         flt_face.uvlayers[mask] = dict()
683                                                         flt_face.uvlayers[mask]['texture index'] = -1
684                                                         flt_face.uvlayers[mask]['texture enviorment'] = 3
685                                                         flt_face.uvlayers[mask]['texture mapping'] = 0
686                                                         flt_face.uvlayers[mask]['texture data'] = 0
687                                                         
688                                                         #now go through and create dummy uvs for this layer
689                                                         for uvert in flt_face.uverts:
690                                                                         uv = Vector(0.0,0.0)
691                                                                         uvert[mask] = uv
692
693                         # Get the indicies in reference to the mesh.
694                         uvs= [vert_desc_lst[j].uv for j in flt_face.indices]
695                         if face_len == 1:
696                                 pass
697                         elif face_len == 2:
698                                 face_edges.append((vert_index, vert_index+1))
699                         elif flt_face.props['draw type'] == 2 or flt_face.props['draw type'] == 3:
700                                 i = 0
701                                 while i < (face_len-1):
702                                         face_edges.append((vert_index + i, vert_index + i + 1))
703                                         i = i + 1
704                                 if flt_face.props['draw type'] == 2:
705                                         face_edges.append((vert_index + i,vert_index)) 
706                         elif face_len == 3 or face_len == 4: # tri or quad
707                                 #if face_len == 1:
708                                 #       pass
709                                 #if face_len == 2:
710                                 #       face_edges.append((vert_index, vert_index+1))
711                                 new_faces.append( [i+vert_index for i in xrange(face_len)] )
712                                 new_faces_props.append((None, image, uvs, flt_face.uverts, flt_face.uvlayers, flt_face.color_index, flt_face.props,FLT_OrigIndex,0, flt_face.subfacelevel))
713                         
714                         else: # fgon
715                                 mesh_face_indicies = [i+vert_index for i in xrange(face_len)]
716                                 tri_ngons= ngon(self.mesh, mesh_face_indicies)
717                                 new_faces.extend([ [mesh_face_indicies[t] for t in tri] for tri in tri_ngons])
718                                 new_faces_props.extend( [ (None, image, (uvs[tri[0]], uvs[tri[1]], uvs[tri[2]]), [flt_face.uverts[tri[0]], flt_face.uverts[tri[1]], flt_face.uverts[tri[2]]], flt_face.uvlayers, flt_face.color_index, flt_face.props,FLT_OrigIndex,1, flt_face.subfacelevel) for tri in tri_ngons ])
719                         
720                         vert_index+= face_len
721                         FLT_OrigIndex+=1
722                 
723                 self.mesh.faces.extend(new_faces)
724                 self.mesh.edges.extend(face_edges)
725                 
726                 #add in the FLT_ORIGINDEX layer
727                 if len(self.mesh.faces):
728                         try:    self.mesh.faceUV= True
729                         except: pass
730                 
731                         if self.mesh.faceUV == True:
732                                 self.mesh.renameUVLayer(self.mesh.activeUVLayer, 'Layer0')
733                 
734                         #create name layer for faces
735                         self.mesh.faces.addPropertyLayer("FLT_ID",Blender.Mesh.PropertyTypes["STRING"])
736                         #create layer for face color indices
737                         self.mesh.faces.addPropertyLayer("FLT_COL",Blender.Mesh.PropertyTypes["INT"])
738                         #create index layer for faces. This is needed by both FGONs and subfaces
739                         self.mesh.faces.addPropertyLayer("FLT_ORIGINDEX",Blender.Mesh.PropertyTypes["INT"])
740                         #create temporary FGON flag layer. Delete after remove doubles
741                         self.mesh.faces.addPropertyLayer("FLT_FGON",Blender.Mesh.PropertyTypes["INT"])
742                         self.mesh.faces.addPropertyLayer("FLT_SFLEVEL", Blender.Mesh.PropertyTypes["INT"])
743                         
744                         for i, f in enumerate(self.mesh.faces):
745                                 props = new_faces_props[i]
746                                 if props[6]['template billboard'] > 0:
747                                         f.transp |= Blender.Mesh.FaceTranspModes["ALPHA"]
748                                         if props[6]['template billboard'] == 2:
749                                                 f.mode |=  Blender.Mesh.FaceModes["BILLBOARD"]
750                                         f.mode |= Blender.Mesh.FaceModes["LIGHT"]
751                                 
752                                 #f.mat = props[0]
753                                 f.image = props[1]
754                                 f.uv = props[2]
755                                 #set vertex colors
756                                 color = self.header.get_color(props[5])
757                                 if not color:
758                                         color = [255,255,255,255]
759                                 for mcol in f.col:
760                                         mcol.a = color[3]
761                                         mcol.r = color[0]
762                                         mcol.g = color[1]
763                                         mcol.b = color[2]
764                                 
765                                 f.setProperty("FLT_SFLEVEL", props[9])
766                                 f.setProperty("FLT_ORIGINDEX",i)
767                                 f.setProperty("FLT_ID",props[6]['id'])
768                                 #if props[5] > 13199:
769                                 #       print "Warning, invalid color index read in! Using default!"
770                                 #       f.setProperty("FLT_COL",127)
771                                 #else:
772                                 if(1):                  #uh oh....
773                                         value = struct.unpack('>i',struct.pack('>I',props[5]))[0]
774                                         f.setProperty("FLT_COL",value)
775                                 
776                                 #if props[8]: 
777                                 #       f.setProperty("FLT_FGON",1)
778                                 #else:
779                                 #       f.setProperty("FLT_FGON",0)
780                         
781                         
782                         #Create multitex layers, if present.
783                         actuvlayer = self.mesh.activeUVLayer
784                         if(self.hasmtex):
785                                 #For every multi-tex layer, we have to add a new UV layer to the mesh
786                                 for i,mask in enumerate(reversed(sorted(self.uvlayers))):
787                                         if self.uvlayers[mask]:
788                                                 self.blayernames[mask] = "Layer" + str(i+1)
789                                                 self.mesh.addUVLayer(self.blayernames[mask])
790                                 
791                                 #Cycle through availible multi-tex layers and add face UVS
792                                 for mask in self.uvlayers:
793                                         if self.uvlayers[mask]:
794                                                 self.mesh.activeUVLayer = self.blayernames[mask]
795                                                 for j, f in enumerate(self.mesh.faces):
796                                                         f.transp |= Blender.Mesh.FaceTranspModes["ALPHA"]
797                                                         f.mode |= Blender.Mesh.FaceModes["LIGHT"]
798                                                         props = new_faces_props[j]
799                                                         uvlayers = props[4]
800                                                         if uvlayers.has_key(mask): #redundant
801                                                                 uverts = props[3]
802                                                                 for k, uv in enumerate(f.uv):
803                                                                         uv[0] = uverts[k][mask][0]
804                                                                         uv[1] = uverts[k][mask][1]
805                                 
806                                                                 uvlayer = uvlayers[mask]
807                                                                 tex_index = uvlayer['texture index']
808                                                                 if tex_index != -1:
809                                                                         try:
810                                                                                 f.image = self.header.tex_pal[tex_index][1]
811                                                                         except KeyError:
812                                                                                 f.image = None
813                                                                         
814                         if global_prefs['smoothshading'] == True and len(self.mesh.faces):
815                                 #We need to store per-face vertex normals in the faces as UV layers and delete them later.
816                                 self.mesh.addUVLayer("FLTNorm1")
817                                 self.mesh.addUVLayer("FLTNorm2")
818                                 self.mesh.activeUVLayer = "FLTNorm1"
819                                 for f in self.mesh.faces:
820                                         f.smooth = 1
821                                         #grab the X and Y components of normal and store them in UV 
822                                         for i, uv in enumerate(f.uv):
823                                                 vert = f.v[i].index
824                                                 vert_desc = vert_desc_lst[vert_list[vert-1]]
825                                                 if vert_desc.cnorm:
826                                                         uv[0] = vert_desc.nx
827                                                         uv[1] = vert_desc.ny
828                                                 else:
829                                                         uv[0] = 0.0
830                                                         uv[1] = 0.0
831                                 
832                                 #Now go through and populate the second UV Layer with the z component
833                                 self.mesh.activeUVLayer = "FLTNorm2"
834                                 for f in self.mesh.faces:
835                                         for i, uv in enumerate(f.uv):
836                                                 vert = f.v[i].index
837                                                 vert_desc = vert_desc_lst[vert_list[vert-1]]
838                                                 if vert_desc.cnorm:
839                                                         uv[0] = vert_desc.nz
840                                                         uv[1] = 0.0
841                                                 else:
842                                                         uv[0] = 0.0
843                                                         uv[1] = 0.0
844                         
845                                 
846                                 
847                         #Finally, go through, remove dummy vertex, remove doubles and add edgesplit modifier.
848                         Blender.Mesh.Mode(Blender.Mesh.SelectModes['VERTEX'])
849                         self.mesh.verts.delete(0) # remove the dummy vert
850                         self.mesh.sel= 1
851                         self.header.scene.update(1) #slow!
852                         self.mesh.remDoubles(0.0001)
853                         
854                         edgeHash = dict()
855
856                         for edge in self.mesh.edges:
857                                 edgeHash[edge.key] = edge.index
858
859
860                         if global_prefs['smoothshading'] == True and len(self.mesh.faces):
861                                 
862                                 #rip out the custom vertex normals from the mesh and place them in a face aligned list. Easier to compare this way.
863                                 facenorms = []
864                                 self.mesh.activeUVLayer = "FLTNorm1"
865                                 for face in self.mesh.faces:
866                                         facenorm = []
867                                         for uv in face.uv:
868                                                 facenorm.append(Vector(uv[0],uv[1],0.0))
869                                         facenorms.append(facenorm)
870                                 self.mesh.activeUVLayer = "FLTNorm2"
871                                 for i, face in enumerate(self.mesh.faces):
872                                         facenorm = facenorms[i]
873                                         for j, uv in enumerate(face.uv):
874                                                 facenorm[j][2] = uv[0]
875                                 self.mesh.removeUVLayer("FLTNorm1")
876                                 self.mesh.removeUVLayer("FLTNorm2")
877
878                                 #find hard edges
879                                 #store edge data for lookup by faces
880                                 #edgeHash = dict()
881                                 #for edge in self.mesh.edges:
882                                 #       edgeHash[edge.key] = edge.index
883
884                                 edgeNormHash = dict()
885                                 #make sure to align the edgenormals to key value!
886                                 for i, face in enumerate(self.mesh.faces):
887                                         
888                                         facenorm = facenorms[i]
889                                         faceEdges = []
890                                         faceEdges.append((face.v[0].index,face.v[1].index,facenorm[0],facenorm[1],face.edge_keys[0]))
891                                         faceEdges.append((face.v[1].index,face.v[2].index,facenorm[1],facenorm[2],face.edge_keys[1]))
892                                         if len(face.v) == 3:
893                                                 faceEdges.append((face.v[2].index,face.v[0].index,facenorm[2],facenorm[0],face.edge_keys[2]))
894                                         elif len(face.v) == 4:
895                                                 faceEdges.append((face.v[2].index,face.v[3].index,facenorm[2],facenorm[3],face.edge_keys[2]))
896                                                 faceEdges.append((face.v[3].index,face.v[0].index,facenorm[3],facenorm[0],face.edge_keys[3]))
897                                         
898                                         #check to see if edgeNormal has been placed in the edgeNormHash yet
899                                         #this is a redundant test, and should be optimized to not be called as often as it is.
900                                         for j, faceEdge in enumerate(faceEdges):
901                                                 #the value we are looking for is (faceEdge[2],faceEdge[3])
902                                                 hashvalue = (faceEdge[2],faceEdge[3])
903                                                 if (faceEdge[0],faceEdge[1]) != faceEdge[4]:
904                                                         hashvalue = (hashvalue[1],hashvalue[0])
905                                                         assert (faceEdge[1],faceEdge[0]) == faceEdge[4]
906                                                 if edgeNormHash.has_key(faceEdge[4]):
907                                                         #compare value in the hash, if different, mark as sharp
908                                                         edgeNorm = edgeNormHash[faceEdge[4]]
909                                                         if\
910                                                         abs(hashvalue[0][0] - edgeNorm[0][0]) > FLOAT_TOLERANCE or\
911                                                         abs(hashvalue[0][1] - edgeNorm[0][1]) > FLOAT_TOLERANCE or\
912                                                         abs(hashvalue[0][2] - edgeNorm[0][2]) > FLOAT_TOLERANCE or\
913                                                         abs(hashvalue[1][0] - edgeNorm[1][0]) > FLOAT_TOLERANCE or\
914                                                         abs(hashvalue[1][1] - edgeNorm[1][1]) > FLOAT_TOLERANCE or\
915                                                         abs(hashvalue[1][2] - edgeNorm[1][2]) > FLOAT_TOLERANCE:
916                                                                 edge = self.mesh.edges[edgeHash[faceEdge[4]]]
917                                                                 edge.flag |= Blender.Mesh.EdgeFlags.SHARP
918                                                                 
919                                                 else:
920                                                         edgeNormHash[faceEdge[4]] = hashvalue
921                                 
922                                 #add in edgesplit modifier
923                                 mod = self.object.modifiers.append(Blender.Modifier.Types.EDGESPLIT)
924                                 mod[Blender.Modifier.Settings.EDGESPLIT_FROM_SHARP] = True
925                                 mod[Blender.Modifier.Settings.EDGESPLIT_FROM_ANGLE] = False
926
927                         if(actuvlayer):
928                                 self.mesh.activeUVLayer = actuvlayer
929                 
930         def blender_import(self):
931                 if self.vis and self.parent:
932                         self.vis = self.parent.vis
933                 name = self.props['id']
934                 
935                 if self.hasMesh:
936                         self.mesh = Blender.Mesh.New()
937                         self.mesh.name = 'FLT_FaceList'
938                         self.mesh.fakeUser = True
939                         self.mesh.verts.extend( Vector()) #DUMMYVERT
940                         self.object = self.header.scene.objects.new(self.mesh)
941                 else:
942                         self.object = self.header.scene.objects.new('Empty')
943
944                 self.object.name = name
945                 self.header.group.objects.link(self.object)
946
947                 #id props import
948                 self.object.properties['FLT'] = dict()
949                 for key in self.props:
950                         try:
951                                 self.object.properties['FLT'][key] = self.props[key]
952                         except: #horrible...
953                                 pass
954                 
955                 if self.parent and self.parent.object and (self.header.scene == self.parent.header.scene):
956                                 self.parent.object.makeParent([self.object])
957
958                 if self.vis == False:
959                         self.object.restrictDisplay = True
960                         self.object.restrictRender = True
961                 
962                 else: #check for LOD children and set the proper flags
963                         lodlist = list()
964                         for child in self.children:
965                                 if child.props.has_key('type') and child.props['type'] == 73:
966                                         lodlist.append(child)
967                 
968                         def LODmin(a,b):
969                                 if a.props['5d!switch in'] < b.props['5d!switch in']:
970                                         return a 
971                                 return b
972                 
973                         min= None
974                         if len(lodlist) > 1:
975                                 for lod in lodlist:
976                                         lod.vis = False
977                                 min = lodlist[0]
978                                 for i in xrange(len(lodlist)):
979                                         min= LODmin(min,lodlist[i])
980                                 min.vis = True
981                                 
982                 if self.matrix:
983                         self.object.setMatrix(self.matrix)
984                         
985                 Node.blender_import(self) # Attach faces to self.faceLs
986                 
987                 if self.hasMesh:
988                         # Add all my faces into the mesh at once
989                         self.blender_import_my_faces()
990                         
991         def parse_face(self):
992                 child = Face(self, self.subfacelevel)
993                 child.parse()
994                 return True
995
996         def parse_group(self):
997                 child = Group(self)
998                 child.parse()
999                 return True
1000
1001         def move_to_next_layer(self):
1002                 global current_layer
1003                 current_layer = current_layer << 1
1004                 if current_layer > 0x80000:
1005                         current_layer = 1
1006
1007         def parse_lod(self):
1008                 child = LOD(self)
1009                 child.parse()
1010                 return True
1011
1012         def parse_unhandled(self):
1013                 child = Unhandled(self)
1014                 child.parse()
1015                 return True
1016
1017         def parse_object(self):
1018                 child = Object(self)
1019                 child.parse()
1020                 return True
1021         
1022         def parse_xref(self):
1023                 child = XRef(self)
1024                 child.parse()
1025                 return True
1026
1027         def parse_dof(self):
1028                 child = DOF(self)
1029                 child.parse()
1030                 return True
1031
1032         def parse_indexed_light_point(self):
1033                 child = IndexedLightPoint(self)
1034                 child.parse()
1035                 return True
1036                 
1037         def parse_inline_light_point(self):
1038                 child = InlineLightPoint(self)
1039                 child.parse()
1040                 return True
1041                 
1042         def parse_matrix(self):
1043                 m = list()
1044                 for i in xrange(4):
1045                         m.append([])
1046                         for j in xrange(4):
1047                                 f = self.header.fw.read_float()
1048                                 m[i].append(f)
1049                 self.matrix = Blender.Mathutils.Matrix(m[0], m[1], m[2], m[3])
1050                 
1051         def parse_subpush(self):
1052                 self.parse_push()
1053                 self.subfacelevel+= 1
1054                 return True
1055         def  parse_subpop(self):
1056                 self.parse_pop()
1057                 self.subfacelevel -= 1
1058                 return True
1059
1060                 
1061                 
1062 class Face(Node):
1063         def __init__(self, parent,subfacelevel):
1064                 Node.__init__(self, parent, parent.header)
1065                 self.root_handler.set_handler({31: self.parse_comment,
1066                                                                            10: self.parse_push,
1067                                                                            52: self.parse_multitex})
1068                 self.root_handler.set_throw_back_lst(throw_back_opcodes)
1069                 
1070                 self.child_handler.set_handler({72: self.parse_vertex_list,
1071                                                                                 10: self.parse_push,
1072                                                                                 11: self.parse_pop,
1073                                                                                 53: self.parse_uvlist})
1074                 
1075                 if parent:
1076                         parent.hasMesh = True
1077
1078                 self.subfacelevel = subfacelevel
1079                 self.indices =  list()  # face verts here
1080                 self.uvlayers = dict()  # MultiTexture layers keyed to layer bitmask.
1081                 self.uverts = list()    # Vertex aligned list of dictionaries keyed to layer bitmask.
1082                 self.uvmask = 0                 # Bitfield read from MTex record
1083                 
1084                 self.comment = ''
1085                 self.props = dict()             
1086                 self.props['id'] = self.header.fw.read_string(8)
1087                 # Load face.
1088                 self.props['ir color'] = self.header.fw.read_int()
1089                 self.props['priority'] = self.header.fw.read_short()
1090                 self.props['draw type'] = self.header.fw.read_char()
1091                 self.props['texture white'] = self.header.fw.read_char()
1092                 self.header.fw.read_ahead(4) # color name indices
1093                 self.header.fw.read_ahead(1) # reserved
1094                 self.props['template billboard'] = self.header.fw.read_uchar()
1095                 self.detail_tex_index = self.header.fw.read_short()
1096                 self.tex_index = self.header.fw.read_short()
1097                 self.mat_index = self.header.fw.read_short()
1098                 self.props['smc'] = self.header.fw.read_short()
1099                 self.props['fid'] = self.header.fw.read_short()
1100                 self.props['ir material'] = self.header.fw.read_int()
1101                 self.alpha = 1.0 - float(self.header.fw.read_ushort()) / 65535.0
1102                 self.props['lod generation control'] = self.header.fw.read_uchar()
1103                 self.header.fw.read_ahead(1) # line style index
1104                 self.props['flags'] = self.header.fw.read_int()
1105                 self.props['light mode'] = self.header.fw.read_uchar()
1106                 self.header.fw.read_ahead(7)
1107                 a = self.header.fw.read_uchar()
1108                 b = self.header.fw.read_uchar()
1109                 g = self.header.fw.read_uchar()
1110                 r = self.header.fw.read_uchar()
1111                 self.packed_color = [r, g, b, a]
1112                 a = self.header.fw.read_uchar()
1113                 b = self.header.fw.read_uchar()
1114                 g = self.header.fw.read_uchar()
1115                 r = self.header.fw.read_uchar()
1116                 self.alt_packed_color = [r, g, b, a]
1117                 self.tex_map_index = self.header.fw.read_short()
1118                 self.header.fw.read_ahead(2)
1119                 self.color_index = self.header.fw.read_uint()
1120                 self.alt_color_index = self.header.fw.read_uint()
1121                 #self.header.fw.read_ahead(2)
1122                 #self.shader_index = self.header.fw.read_short()
1123
1124         def parse_comment(self):
1125                 self.comment = self.header.fw.read_string(self.header.fw.get_length()-4)
1126                 return True
1127                 
1128         def blender_import(self):
1129                 vert_count = len(self.indices)
1130                 if vert_count < 1:
1131                         if global_prefs['verbose'] >= 2:
1132                                 print 'Warning: Ignoring face with no vertices.'
1133                         return
1134                 
1135                 # Assign material and image
1136                 
1137                 self.parent.faceLs.append(self)
1138                 #need to store comment in mesh prop layer!
1139                 
1140                 # Store comment info in parent.
1141                 #if self.comment != '':
1142                 #       if self.parent.props['comment'] != '':
1143                 #               self.parent.props['comment'] += '\n\nFrom Face:\n' + self.comment
1144                 #       else:
1145                 #               self.parent.props['comment'] = self.comment
1146                 
1147                 if self.uvlayers:
1148                         #Make sure that the mesh knows about the layers that this face uses
1149                         self.parent.hasmtex = True
1150                         for mask in self.uvlayers.keys():
1151                                 self.parent.uvlayers[mask] = True
1152                         
1153         def parse_vertex_list(self):
1154                 length = self.header.fw.get_length()
1155                 fw = self.header.fw
1156                 vert_pal = self.header.vert_pal
1157
1158                 count = (length-4)/4
1159                 
1160                 # If this ever fails the chunk below does error checking
1161                 self.indices= [vert_pal.index[fw.read_int()] for i in xrange(count)]
1162                 '''
1163                 for i in xrange(count):
1164                         byte_offset = fw.read_int()
1165                         if byte_offset in vert_pal.index:
1166                                 index = vert_pal.index[byte_offset]
1167                                 self.indices.append(index)
1168                         elif global_prefs['verbose'] >= 1:
1169                                 print 'Warning: Unable to map byte offset %s' + \
1170                                           ' to vertex index.' % byte_offset
1171                 '''
1172                 return True
1173         
1174         def parse_multitex(self):
1175                 #Parse  MultiTex Record.
1176                 length = self.header.fw.get_length()
1177                 fw = self.header.fw
1178                 #num layers == (length - 8) / 4
1179                 uvmask = fw.read_uint()
1180                 mask = 2147483648
1181                 for i in xrange(7):
1182                         if mask & uvmask:
1183                                 uvlayer = dict()
1184                                 self.uvlayers[mask] = uvlayer
1185                         mask = mask / 2
1186                 
1187                 #read in record for each individual layer.
1188                 for key in reversed(sorted(self.uvlayers)):
1189                         uvlayer = self.uvlayers[key]
1190                         uvlayer['texture index'] = fw.read_ushort()
1191                         uvlayer['texture enviorment'] = fw.read_ushort()
1192                         uvlayer['texture mapping'] = fw.read_ushort()
1193                         uvlayer['texture data'] = fw.read_ushort()
1194                 
1195                         self.uvmask = uvmask
1196                 
1197         def parse_uvlist(self):
1198                 #for each uvlayer, add uv vertices
1199                 length = self.header.fw.get_length()
1200                 fw = self.header.fw
1201                 uvmask = fw.read_uint()
1202                 if uvmask != self.uvmask: #This should never happen!
1203                         fw.read_ahead(self.length -  4) #potentially unnessecary?
1204                 else:   
1205                         #need to store in uvverts dictionary for each vertex.
1206                         totverts = len(self.indices)
1207                         for i in xrange(totverts):
1208                                 uvert = dict()
1209                                 for key in reversed(sorted(self.uvlayers)):
1210                                         uv = Vector(0.0,0.0)
1211                                         uv[0] = fw.read_float()
1212                                         uv[1] = fw.read_float()
1213                                         uvert[key] = uv
1214                                 self.uverts.append(uvert)
1215                                 
1216 class Object(InterNode):
1217         def __init__(self, parent):
1218                 Node.__init__(self, parent, parent.header)
1219                 InterNode.__init__(self)
1220                 
1221                 self.root_handler.set_handler({33: self.parse_long_id,
1222                                                                            31: self.parse_comment,
1223                                                                            10: self.parse_push,
1224                                                                            49: self.parse_matrix})
1225                 self.root_handler.set_throw_back_lst(throw_back_opcodes)
1226                 
1227                 self.child_handler.set_handler({5: self.parse_face,
1228                                                                                 19: self.parse_subpush,
1229                                                                                 20: self.parse_subpop,
1230                                                                                 111: self.parse_inline_light_point,
1231                                                                                 10: self.parse_push,
1232                                                                                 11: self.parse_pop})
1233
1234                 self.props = dict()             
1235                 self.props['comment'] = ''
1236                 self.parse_record()
1237
1238 class Group(InterNode):
1239         def __init__(self, parent):
1240                 Node.__init__(self, parent, parent.header)
1241                 InterNode.__init__(self)
1242                 
1243                 self.root_handler.set_handler({33: self.parse_long_id,
1244                                                                            31: self.parse_comment,
1245                                                                            10: self.parse_push,
1246                                                                            49: self.parse_matrix})
1247                 self.root_handler.set_throw_back_lst(throw_back_opcodes)
1248                 
1249                 self.child_handler.set_handler({5: self.parse_face,
1250                                                                                 19: self.parse_subpush,
1251                                                                                 20: self.parse_subpop,
1252                                                                                 111: self.parse_inline_light_point,
1253                                                                                 2: self.parse_group,
1254                                                                                 73: self.parse_lod,
1255                                                                                 4: self.parse_object,
1256                                                                                 10: self.parse_push,
1257                                                                                 11: self.parse_pop,
1258                                                                                 96: self.parse_unhandled,
1259                                                                                 14: self.parse_dof,
1260                                                                                 91: self.parse_unhandled,
1261                                                                                 98: self.parse_unhandled,
1262                                                                                 63: self.parse_xref})
1263                 self.props = dict.fromkeys(['type', 'id', 'comment', 'priority', 'flags', 'special1',
1264                                                                         'special2', 'significance', 'layer code', 'loop count',
1265                                                                         'loop duration', 'last frame duration'])
1266                 
1267                 self.props['comment'] = ''
1268                 self.parse_record()
1269                 
1270                 #self.props['type'] = str(self.opcode) + ':' + opcode_name[self.opcode]
1271                 #props = records[self.opcode]
1272                 #propkeys = props.keys()
1273                 #propkeys.sort()
1274                 #for position in propkeys:
1275                 #       (type,length,name) = props[position]
1276                 #       self.props[name] = read_prop(self.header.fw,type,length)
1277                 #self.props['id'] = self.props['3t8!id']
1278
1279 class DOF(InterNode):
1280         def blender_import(self):
1281                 InterNode.blender_import(self)
1282
1283         def __init__(self, parent):
1284                 Node.__init__(self, parent, parent.header)
1285                 InterNode.__init__(self)
1286                 
1287                 self.root_handler.set_handler({33: self.parse_long_id,
1288                                                                            31: self.parse_comment,
1289                                                                            10: self.parse_push,
1290                                                                            49: self.parse_matrix})
1291                 self.root_handler.set_throw_back_lst(throw_back_opcodes)
1292                 
1293                 self.child_handler.set_handler({#130: self.parse_indexed_light_point,
1294                                                                                 111: self.parse_inline_light_point,
1295                                                                                 2: self.parse_group,
1296                                                                                 73: self.parse_lod,
1297                                                                                 4: self.parse_object,
1298                                                                                 10: self.parse_push,
1299                                                                                 11: self.parse_pop,
1300                                                                                 96: self.parse_unhandled,
1301                                                                                 14: self.parse_dof,
1302                                                                                 91: self.parse_unhandled,
1303                                                                                 98: self.parse_unhandled,
1304                                                                                 63: self.parse_xref})
1305                 self.props = dict()             
1306                 self.props['comment'] = ''
1307                 self.parse_record()
1308
1309
1310 class XRef(InterNode):
1311         def parse(self):
1312                 if self.xref:
1313                         self.xref.parse()
1314                 Node.parse(self)
1315
1316         def __init__(self, parent):
1317                 Node.__init__(self, parent, parent.header)
1318                 InterNode.__init__(self)
1319                 
1320                 self.root_handler.set_handler({49: self.parse_matrix})
1321                 self.root_handler.set_throw_back_lst(throw_back_opcodes)
1322                 
1323                 self.props = dict()             
1324                 self.props['comment'] = ''
1325                 self.parse_record()
1326
1327                 xref_filename = self.props['3t200!filename']
1328                 self.props['id'] = 'X: ' + Blender.sys.splitext(Blender.sys.basename(xref_filename))[0] #this is really wrong as well....
1329                 
1330                 if global_prefs['doxrefs'] and os.path.exists(xref_filename) and not self.header.grr.xrefs.has_key(xref_filename):
1331                         self.xref = Database(xref_filename, self.header.grr, self)
1332                         self.header.grr.xrefs[xref_filename] = self.xref
1333                 else:
1334                         self.xref = None
1335                 
1336
1337         def blender_import(self):
1338                 #name = self.props['type'] + ': ' + self.props['id']
1339                 name = self.props['id']
1340                 self.object = self.header.scene.objects.new('Empty')
1341                 self.object.name = name
1342                 self.object.enableDupGroup = True
1343                 self.header.group.objects.link(self.object)
1344                 
1345                 #for broken links its ok to leave this empty! they purely for visual purposes anyway.....
1346                 try:
1347                         self.object.DupGroup = self.header.grr.xrefs[self.props['3t200!filename']].group
1348                 except:
1349                         pass
1350                         
1351                 if self.parent and self.parent.object:
1352                         self.parent.object.makeParent([self.object])
1353
1354                 #id props import
1355                 self.object.properties['FLT'] = dict()
1356                 for key in self.props:
1357                         try:
1358                                 self.object.properties['FLT'][key] = self.props[key]
1359                         except: #horrible...
1360                                 pass
1361
1362                 self.object.Layer = current_layer
1363                 self.object.sel = 1
1364                 if self.matrix:
1365                         self.object.setMatrix(self.matrix)
1366                 Node.blender_import(self)
1367                 
1368                 
1369 class LOD(InterNode):
1370         def blender_import(self):
1371                 #self.move_to_next_layer()
1372                 InterNode.blender_import(self)
1373                 #self.object.properties['FLT'] = self.props.copy()
1374                 
1375         def __init__(self, parent):
1376                 Node.__init__(self, parent, parent.header)
1377                 InterNode.__init__(self)
1378
1379                 self.root_handler.set_handler({33: self.parse_long_id,
1380                                                                            31: self.parse_comment,
1381                                                                            10: self.parse_push,
1382                                                                            49: self.parse_matrix})
1383                 self.root_handler.set_throw_back_lst(throw_back_opcodes)
1384                 
1385                 self.child_handler.set_handler({2: self.parse_group,
1386                                                                                 111: self.parse_inline_light_point,
1387                                                                                 73: self.parse_lod,
1388                                                                                 4: self.parse_object,
1389                                                                                 10: self.parse_push,
1390                                                                                 11: self.parse_pop,
1391                                                                                 96: self.parse_unhandled, # switch
1392                                                                                 14: self.parse_dof, # DOF
1393                                                                                 91: self.parse_unhandled, # sound
1394                                                                                 98: self.parse_unhandled, # clip
1395                                                                                 63: self.parse_xref})
1396
1397                 self.props = dict()             
1398                 self.props['comment'] = ''
1399                 self.parse_record()
1400
1401 class InlineLightPoint(InterNode):
1402         def __init__(self, parent):
1403                 Node.__init__(self, parent, parent.header)
1404                 InterNode.__init__(self)
1405                 self.root_handler.set_handler({33: self.parse_long_id,
1406                                                                            31: self.parse_comment,
1407                                                                            10: self.parse_push,
1408                                                                            49: self.parse_matrix})
1409                 self.root_handler.set_throw_back_lst(throw_back_opcodes)
1410                 
1411                 self.child_handler.set_handler({72: self.parse_vertex_list,
1412                                                                                 10: self.parse_push,
1413                                                                                 11: self.parse_pop})
1414
1415                 self.indices = list()
1416                 self.props = dict()             
1417                 self.props['comment'] = ''
1418                 self.parse_record()
1419
1420                 
1421         def blender_import(self):
1422                 
1423
1424                 name = self.props['id']
1425                 self.mesh= Blender.Mesh.New()
1426                 self.mesh.name = 'FLT_LP'
1427                 self.object = self.header.scene.objects.new(self.mesh)
1428                 self.object.name = name
1429                 #self.mesh.verts.extend(Vector() ) # DUMMYVERT
1430                 self.object.Layer = current_layer
1431                 self.object.sel= 1
1432         
1433                 self.object.properties['FLT'] = dict()
1434                 for key in self.props:
1435                         try:
1436                                 self.object.properties['FLT'][key] = self.props[key]
1437                         except: #horrible...
1438                                 pass
1439
1440                 if self.parent and self.parent.object and self.header.scene == self.parent.header.scene:
1441                         self.parent.object.makeParent([self.object])
1442
1443                 if self.matrix:
1444                         self.object.setMatrix(self.matrix)
1445
1446                 self.mesh.verts.extend([self.header.vert_pal.blender_verts[i] for i in self.indices])
1447                 
1448                 #add color index information.
1449                 self.mesh.verts.addPropertyLayer("FLT_VCOL",Blender.Mesh.PropertyTypes["INT"])
1450                 for i, vindex in enumerate(self.indices):
1451                         vdesc = self.header.vert_pal.vert_desc_lst[vindex]
1452                         v = self.mesh.verts[i]
1453                         v.setProperty("FLT_VCOL",vdesc.cindex)
1454                 #for i, v in enumerate(self.mesh.verts):
1455                 #       vdesc = self.header.vert_pal.vert_desc_lst[i]
1456                 #       v.setProperty("FLT_VCOL",vdesc.cindex)
1457                 self.mesh.update()
1458                                 
1459         def parse_vertex_list(self):
1460                 length = self.header.fw.get_length()
1461                 fw = self.header.fw
1462                 vert_pal = self.header.vert_pal
1463
1464                 count = (length-4)/4
1465                 
1466                 # If this ever fails the chunk below does error checking
1467                 self.indices= [vert_pal.index[fw.read_int()] for i in xrange(count)]
1468                 
1469                 '''
1470                 for i in xrange(count):
1471                         byte_offset = fw.read_int()
1472                         if byte_offset in vert_pal.index:
1473                                 index = vert_pal.index[byte_offset]
1474                                 self.indices.append(index)
1475                         elif global_prefs['verbose'] >= 1:
1476                                 print 'Warning: Unable to map byte offset %s' + \
1477                                           ' to vertex index.' % byte_offset
1478                 '''
1479                 
1480                 return True
1481                 
1482
1483
1484 class IndexedLightPoint(InterNode):
1485         # return dictionary: lp_app name => index list
1486         def group_points(self, props):
1487                 
1488                 name_to_indices = {}
1489                 
1490                 for i in self.indices:
1491                         vert_desc = self.header.vert_pal.vert_desc_lst[i]
1492                         app_desc = LightPointAppDesc()
1493                         app_desc.props.update(props)
1494                         # add vertex normal and color
1495                         app_desc.props.update({'nx': vert_desc.nx})
1496                         app_desc.props.update({'ny': vert_desc.ny})
1497                         app_desc.props.update({'nz': vert_desc.nz})
1498                         
1499                         app_desc.props.update({'r': vert_desc.r})
1500                         app_desc.props.update({'g': vert_desc.g})
1501                         app_desc.props.update({'b': vert_desc.b})
1502                         app_desc.props.update({'a': vert_desc.a})
1503                         
1504                         app_name = self.header.grr.request_lightpoint_app(app_desc, self.header.scene)
1505
1506                         if name_to_indices.get(app_name):
1507                                 name_to_indices[app_name].append(i)
1508                         else:
1509                                 name_to_indices.update({app_name: [i]})
1510                         
1511                 return name_to_indices
1512                 
1513         def blender_import(self):
1514                 name = self.props['type'] + ': ' + self.props['id']
1515                 
1516                 name_to_indices = self.group_points(self.header.lightpoint_appearance_pal[self.index])
1517                 
1518                 for app_name, indices in name_to_indices.iteritems():        
1519                         self.object = Blender.Object.New('Mesh', name)
1520                         self.mesh= Blender.Mesh.New()
1521                         self.mesh.verts.extend( Vector() ) # DUMMYVERT
1522                         self.object.link(self.mesh)
1523                         
1524                         if self.parent:
1525                                 self.parent.object.makeParent([self.object])
1526                                 
1527                         for i in indices:
1528                                 vert = self.header.vert_pal.blender_verts[i]
1529                                 self.mesh.verts.append(vert)
1530                         
1531                         self.header.scene.objects.link(self.object)
1532         
1533                         self.object.Layer = current_layer
1534                         
1535                         if self.matrix:
1536                                 self.object.setMatrix(self.matrix)
1537                                 
1538                         # Import comment.
1539                         if self.props['comment'] != '':
1540                                 name = 'COMMENT: ' + self.props['id']
1541                                 t = Blender.Text.New(name)
1542                                 t.write(self.props['comment'])
1543                                 self.props['comment'] = name
1544                                 
1545                         # Attach properties.
1546                         self.props.update({'appearance': app_name})
1547                         for name, value in self.props.iteritems():
1548                                 self.object.addProperty(name, value)
1549                         
1550                         self.mesh.update()
1551                         
1552         def parse_vertex_list(self):
1553                 length = self.header.fw.get_length()
1554                 fw = self.header.fw
1555                 vert_pal = self.header.vert_pal
1556
1557                 count = (length-4)/4
1558                 
1559                 # If this ever fails the chunk below does error checking
1560                 self.indices= [vert_pal.index[fw.read_int()] for i in xrange(count)]
1561                 
1562                 '''
1563                 for i in xrange(count):
1564                         byte_offset = fw.read_int()
1565                         if byte_offset in vert_pal.index:
1566                                 index = vert_pal.index[byte_offset]
1567                                 self.indices.append(index)
1568                         elif global_prefs['verbose'] >= 1:
1569                                 print 'Warning: Unable to map byte offset %s' + \
1570                                           ' to vertex index.' % byte_offset
1571                 '''
1572                 return True
1573                 
1574         def __init__(self, parent):
1575                 Node.__init__(self, parent, parent.header)
1576                 InterNode.__init__(self)
1577                 self.root_handler.set_handler({33: self.parse_long_id,
1578                                                                            31: self.parse_comment,
1579                                                                            10: self.parse_push,
1580                                                                            49: self.parse_matrix})
1581                 self.root_handler.set_throw_back_lst(throw_back_opcodes)
1582                 
1583                 self.child_handler.set_handler({72: self.parse_vertex_list,
1584                                                                                 10: self.parse_push,
1585                                                                                 11: self.parse_pop})
1586
1587                 self.indices = list()
1588                 
1589                 self.props = dict.fromkeys(['id', 'type', 'comment', 'draw order', 'appearance'])
1590                 self.props['comment'] = ''
1591                 self.props['type'] = 'Light Point'
1592                 self.props['id'] = self.header.fw.read_string(8)
1593                 self.index = self.header.fw.read_int()
1594                 self.header.fw.read_ahead(4) # animation index
1595                 self.props['draw order'] = self.header.fw.read_int()        
1596
1597 class Unhandled(InterNode):
1598         def __init__(self, parent):
1599                 Node.__init__(self, parent, parent.header)
1600                 InterNode.__init__(self)
1601                 
1602                 self.root_handler.set_handler({33: self.parse_long_id,
1603                                                                            31: self.parse_comment,
1604                                                                            10: self.parse_push,
1605                                                                            49: self.parse_matrix})
1606                 self.root_handler.set_throw_back_lst(throw_back_opcodes)
1607                 
1608                 self.child_handler.set_handler({2: self.parse_group,
1609                                                                                 73: self.parse_lod,
1610                                                                                 4: self.parse_object,
1611                                                                                 10: self.parse_push,
1612                                                                                 11: self.parse_pop,
1613                                                                                 96: self.parse_unhandled, # switch
1614                                                                                 14: self.parse_dof, # DOF
1615                                                                                 91: self.parse_unhandled, # sound
1616                                                                                 98: self.parse_unhandled, # clip
1617                                                                                 63: self.parse_xref})
1618
1619                 self.props['id'] = self.header.fw.read_string(8)
1620
1621 class Database(InterNode):
1622         def blender_import(self):
1623                 for key in self.tex_pal.keys():
1624                         path_filename= FF.find(self.tex_pal[key][0])
1625                         if path_filename != None:
1626                                 img = self.grr.request_image(path_filename)
1627                                 if img:
1628                                         self.tex_pal[key][1] = img
1629                         elif global_prefs['verbose'] >= 1:
1630                                 print 'Warning: Unable to find', self.tex_pal[key][0]
1631                 
1632                 self.scene.properties['FLT'] = dict()
1633                 for key in self.props:
1634                         try:
1635                                 self.scene.properties['FLT'][key] = self.props[key]
1636                         except: #horrible...
1637                                 pass
1638                 
1639                 self.scene.properties['FLT']['Main'] = 0
1640                 self.scene.properties['FLT']['Filename'] = self.bname
1641                 
1642                 #import color palette
1643                 carray = list()
1644                 for color in self.col_pal:
1645                         carray.append(struct.unpack('>i',struct.pack('>BBBB',color[0],color[1],color[2],color[3]))[0])
1646                 self.scene.properties['FLT']['Color Palette'] = carray
1647                 Node.blender_import(self)
1648
1649         def parse_appearance_palette(self):
1650                 props = dict()
1651                 self.fw.read_ahead(4) # reserved
1652                 props.update({'id': self.fw.read_string(256)})
1653                 index = self.fw.read_int()
1654                 props.update({'smc': self.fw.read_short()})
1655                 props.update({'fid': self.fw.read_short()})
1656                 props.update({'back color: a': self.fw.read_uchar()})
1657                 props.update({'back color: b': self.fw.read_uchar()})
1658                 props.update({'back color: g': self.fw.read_uchar()})
1659                 props.update({'back color: r': self.fw.read_uchar()})
1660                 props.update({'display mode': self.fw.read_int()})
1661                 props.update({'intensity': self.fw.read_float()})
1662                 props.update({'back intensity': self.fw.read_float()})
1663                 props.update({'minimum defocus': self.fw.read_float()})
1664                 props.update({'maximum defocus': self.fw.read_float()})
1665                 props.update({'fading mode': self.fw.read_int()})
1666                 props.update({'fog punch mode': self.fw.read_int()})
1667                 props.update({'directional mode': self.fw.read_int()})
1668                 props.update({'range mode': self.fw.read_int()})
1669                 props.update({'min pixel size': self.fw.read_float()})
1670                 props.update({'max pixel size': self.fw.read_float()})
1671                 props.update({'actual size': self.fw.read_float()})
1672                 props.update({'trans falloff pixel size': self.fw.read_float()})
1673                 props.update({'trans falloff exponent': self.fw.read_float()})
1674                 props.update({'trans falloff scalar': self.fw.read_float()})
1675                 props.update({'trans falloff clamp': self.fw.read_float()})
1676                 props.update({'fog scalar': self.fw.read_float()})
1677                 props.update({'fog intensity': self.fw.read_float()})
1678                 props.update({'size threshold': self.fw.read_float()})
1679                 props.update({'directionality': self.fw.read_int()})
1680                 props.update({'horizontal lobe angle': self.fw.read_float()})
1681                 props.update({'vertical lobe angle': self.fw.read_float()})
1682                 props.update({'lobe roll angle': self.fw.read_float()})
1683                 props.update({'dir falloff exponent': self.fw.read_float()})
1684                 props.update({'dir ambient intensity': self.fw.read_float()})
1685                 props.update({'significance': self.fw.read_float()})
1686                 props.update({'flags': self.fw.read_int()})
1687                 props.update({'visibility range': self.fw.read_float()})
1688                 props.update({'fade range ratio': self.fw.read_float()})
1689                 props.update({'fade in duration': self.fw.read_float()})
1690                 props.update({'fade out duration': self.fw.read_float()})
1691                 props.update({'LOD range ratio': self.fw.read_float()})
1692                 props.update({'LOD scale': self.fw.read_float()})
1693                 
1694                 self.lightpoint_appearance_pal.update({index: props})
1695                 
1696         def parse_header(self):
1697                 self.props['type'] = 'Header'
1698                 self.props['comment'] = ''
1699                 self.props['id'] = self.fw.read_string(8)
1700                 self.props['version'] = self.fw.read_int()
1701                 self.fw.read_ahead(46)
1702                 self.props['units'] = self.fw.read_char()
1703                 self.props['set white'] = bool(self.fw.read_char())
1704                 self.props['flags'] = self.fw.read_int()
1705                 self.fw.read_ahead(24)
1706                 self.props['projection type'] = self.fw.read_int()
1707                 self.fw.read_ahead(36)
1708                 self.props['sw x'] = self.fw.read_double()
1709                 self.props['sw y'] = self.fw.read_double()
1710                 self.props['dx'] = self.fw.read_double()
1711                 self.props['dy'] = self.fw.read_double()
1712                 self.fw.read_ahead(24)
1713                 self.props['sw lat'] = self.fw.read_double()
1714                 self.props['sw lon'] = self.fw.read_double()
1715                 self.props['ne lat'] = self.fw.read_double()
1716                 self.props['ne lon'] = self.fw.read_double()
1717                 self.props['origin lat'] = self.fw.read_double()
1718                 self.props['origin lon'] = self.fw.read_double()
1719                 self.props['lambert lat1'] = self.fw.read_double()
1720                 self.props['lambert lat2'] = self.fw.read_double()
1721                 self.fw.read_ahead(16)
1722                 self.props['ellipsoid model'] = self.fw.read_int()
1723                 self.fw.read_ahead(4)
1724                 self.props['utm zone'] = self.fw.read_short()
1725                 self.fw.read_ahead(6)
1726                 self.props['dz'] = self.fw.read_double()
1727                 self.props['radius'] = self.fw.read_double()
1728                 self.fw.read_ahead(8)
1729                 self.props['major axis'] = self.fw.read_double()
1730                 self.props['minor axis'] = self.fw.read_double()
1731                 
1732                 if global_prefs['verbose'] >= 1:
1733                         print 'OpenFlight Version:', float(self.props['version']) / 100.0
1734                         print
1735                         
1736                 return True
1737
1738         def parse_mat_palette(self):
1739                 mat_desc = MaterialDesc()
1740                 index = self.fw.read_int()
1741
1742                 name = self.fw.read_string(12)
1743                 if len(mat_desc.name) > 0:
1744                         mat_desc.name = name
1745
1746                 flag = self.fw.read_int()
1747                 # skip material if not used
1748                 if not flag & 0x80000000:
1749                         return True
1750
1751                 ambient_col = [self.fw.read_float(), self.fw.read_float(), self.fw.read_float()]
1752                 mat_desc.diffuse = [self.fw.read_float(), self.fw.read_float(), self.fw.read_float()]
1753                 mat_desc.specular = [self.fw.read_float(), self.fw.read_float(), self.fw.read_float()]
1754                 emissive_col = [self.fw.read_float(), self.fw.read_float(), self.fw.read_float()]
1755
1756                 mat_desc.shininess = self.fw.read_float() / 64.0 # [0.0, 128.0] => [0.0, 2.0]
1757                 mat_desc.alpha = self.fw.read_float()
1758
1759                 # Convert ambient and emissive colors into intensitities.
1760                 mat_desc.ambient = col_to_gray(ambient_col)
1761                 mat_desc.emissive = col_to_gray(emissive_col)
1762
1763                 self.mat_desc_pal_lst.append( (index, mat_desc) )
1764                 
1765                 return True
1766         
1767         def get_color(self, color_index):
1768                 color = None
1769                 index = color_index / 128
1770                 intensity = float(color_index - 128.0 * index) / 127.0
1771                 
1772                 if index >= 0 and index <= 1023:
1773                         brightest = self.col_pal[index]
1774                         r = int(brightest[0] * intensity)
1775                         g = int(brightest[1] * intensity)
1776                         b = int(brightest[2] * intensity)
1777                         a = int(brightest[3])
1778                         
1779                         color = [r, g, b, a]
1780                 
1781                 return color
1782         
1783         def parse_color_palette(self):
1784                 self.header.fw.read_ahead(128)
1785                 for i in xrange(1024):
1786                         a = self.header.fw.read_uchar()
1787                         b = self.header.fw.read_uchar()
1788                         g = self.header.fw.read_uchar()
1789                         r = self.header.fw.read_uchar()
1790                         self.col_pal.append((r, g, b, a))
1791                 return True
1792                 
1793         def parse_vertex_palette(self):
1794                 self.vert_pal = VertexPalette(self)
1795                 self.vert_pal.parse()
1796                 return True
1797                 
1798         def parse_texture_palette(self):
1799                 name = self.fw.read_string(200)
1800                 index = self.fw.read_int()
1801                 self.tex_pal[index]= [name, None]
1802                 return True
1803         
1804         def read_attribute_files(self):
1805                 for tex in self.tex_pal.keys():
1806                         [name,image] = self.tex_pal[tex]
1807                         basename = os.path.basename(name)
1808                         if(image):
1809                                 basename = basename + ".attr"
1810                                 dirname = os.path.dirname(Blender.sys.expandpath(image.getFilename())) #can't rely on original info stored in pallette since it might be relative link
1811                                 newpath = os.path.join(dirname, basename)
1812                                 if os.path.exists(newpath) and not image.properties.has_key('FLT'):
1813                                         fw = flt_filewalker.FltIn(newpath)
1814                                         fw.read_ahead(8) #We dont care what the attribute file says about x/y dimensions
1815                                         image.properties['FLT']={}
1816                                         
1817                                         #need to steal code from parse records....
1818                                         props = records['Image']
1819                                         propkeys = props.keys()
1820                                         propkeys.sort()
1821                                         for position in propkeys:
1822                                                 (type,length,name) = props[position]
1823                                                 image.properties['FLT'][name] = read_prop(fw,type,length)
1824                                         fw.close_file()
1825                                         
1826                                         #copy clamp settings
1827                                         wrap = image.properties['FLT']['10i!Wrap']
1828                                         wrapu = image.properties['FLT']['11i!WrapU']
1829                                         wrapv = image.properties['FLT']['12i!WrapV']
1830                                         
1831                                         if wrapu == 3 or wrapv == 3:
1832                                                 wrapuv = (wrap,wrap)
1833                                         else:
1834                                                 wrapuv = (wrapu, wrapv)
1835                                         image.clampX = wrapuv[0]
1836                                         image.clampY = wrapuv[1]
1837                                         
1838                                 elif not os.path.exists(newpath):
1839                                         print "Cannot read attribute file:" + newpath
1840                                         
1841         def __init__(self, filename, grr, parent=None):
1842                 if global_prefs['verbose'] >= 1:
1843                         print 'Parsing:', filename
1844                         print
1845                 
1846                 self.fw = flt_filewalker.FltIn(filename)
1847                 self.filename = filename
1848                 self.bname = os.path.splitext(os.path.basename(filename))[0]
1849                 self.grr = grr
1850                 
1851                 Node.__init__(self, parent, self)
1852                 InterNode.__init__(self)
1853                 
1854                 self.root_handler.set_handler({1: self.parse_header,
1855                                                                            67: self.parse_vertex_palette,
1856                                                                            33: self.parse_long_id,
1857                                                                            31: self.parse_comment,
1858                                                                            64: self.parse_texture_palette,
1859                                                                            32: self.parse_color_palette,
1860                                                                            113: self.parse_mat_palette,
1861                                                                            128: self.parse_appearance_palette,
1862                                                                            10: self.parse_push})
1863                 if parent:
1864                         self.root_handler.set_throw_back_lst(throw_back_opcodes)
1865
1866                 self.child_handler.set_handler({#130: self.parse_indexed_light_point,
1867                                                                                 111: self.parse_inline_light_point,
1868                                                                                 2: self.parse_group,
1869                                                                                 73: self.parse_lod,
1870                                                                                 4: self.parse_object,
1871                                                                                 10: self.parse_push,
1872                                                                                 11: self.parse_pop,
1873                                                                                 96: self.parse_unhandled,
1874                                                                                 14: self.parse_dof,
1875                                                                                 91: self.parse_unhandled,
1876                                                                                 98: self.parse_unhandled,
1877                                                                                 63: self.parse_xref})
1878                 
1879                 self.scene = Blender.Scene.New(self.bname)
1880                 self.group = Blender.Group.New(self.bname)
1881
1882                 self.vert_pal = None
1883                 self.lightpoint_appearance_pal = dict()
1884                 self.tex_pal = dict()
1885                 #self.tex_pal_lst = list()
1886                 #self.bl_tex_pal = dict()
1887                 self.col_pal = list()
1888                 self.mat_desc_pal_lst = list()
1889                 self.mat_desc_pal = dict()
1890                 self.props = dict.fromkeys(['id', 'type', 'comment', 'version', 'units', 'set white',
1891                         'flags', 'projection type', 'sw x', 'sw y', 'dx', 'dy', 'dz', 'sw lat',
1892                         'sw lon', 'ne lat', 'ne lon', 'origin lat', 'origin lon', 'lambert lat1',
1893                         'lambert lat2', 'ellipsoid model', 'utm zone', 'radius', 'major axis', 'minor axis'])
1894
1895
1896 def clearparent(root,childhash):
1897         for child in childhash[root]:
1898                 clearparent(child,childhash)
1899         root.clrParent(2,0)
1900
1901 def fixscale(root,childhash):   
1902         for child in childhash[root]:
1903                 fixscale(child,childhash)
1904         location = Blender.Mathutils.Vector(root.getLocation('worldspace'))
1905         if location[0] != 0.0 and location[1] != 0.0 and location[2] != 0.0:
1906                 #direction = Blender.Mathutils.Vector(0-location[0],0-location[1],0-location[2]) #reverse vector
1907                 smat = Blender.Mathutils.ScaleMatrix(global_prefs['scale'],4)
1908                 root.setLocation(location * smat)
1909         #if its a mesh, we need to scale all of its vertices too
1910         if root.type == 'Mesh':
1911                 smat = Blender.Mathutils.ScaleMatrix(global_prefs['scale'],4)
1912                 rmesh = root.getData(mesh=True)
1913                 for v in rmesh.verts:
1914                         v.co = v.co * smat
1915         
1916         
1917 def reparent(root,childhash,sce):
1918         for child in childhash[root]:
1919                 reparent(child,childhash,sce)
1920         
1921         root.makeParent(childhash[root])
1922         sce.update(1)
1923         
1924 def update_scene(root,sdone):
1925         for object in root.objects:
1926                 if object.DupGroup:
1927                         try:
1928                                 child = Blender.Scene.Get(object.DupGroup.name)
1929                         except:
1930                                 child = None
1931                         if child and child not in sdone:
1932                                 update_scene(child,sdone)
1933         root.makeCurrent()
1934         #create a list of children for each object
1935         childhash = dict()
1936         for object in root.objects:
1937                 childhash[object] = list()
1938                 
1939         for object in root.objects:
1940                 if object.parent:
1941                         childhash[object.parent].append(object)
1942         
1943         for object in root.objects:
1944                 if not object.parent:
1945                         #recursivley go through and clear all the children of their transformation, starting at deepest level first.
1946                         clearparent(object,childhash)
1947                         #now fix the location of everything
1948                         fixscale(object,childhash)
1949                         #now fix the parenting
1950                         reparent(object,childhash,root)
1951         
1952         for object in root.objects:
1953                 object.makeDisplayList()
1954         root.update(1)
1955         sdone.append(root)
1956
1957
1958 def select_file(filename, grr):
1959         if not Blender.sys.exists(filename):
1960                 msg = 'Error: File ' + filename + ' does not exist.'
1961                 Blender.Draw.PupMenu(msg)
1962                 return
1963         
1964         if not filename.lower().endswith('.flt'):
1965                 msg = 'Error: Not a flight file.'
1966                 Blender.Draw.PupMenu(msg)
1967                 print msg
1968                 print
1969                 return
1970         
1971         global_prefs['fltfile']= filename
1972         global_prefs['verbose']= 1
1973         global_prefs['get_texture'] = True
1974         global_prefs['get_diffuse'] = True
1975         global_prefs['get_specular'] = False
1976         global_prefs['get_emissive'] = False
1977         global_prefs['get_alpha'] = True
1978         global_prefs['get_ambient'] = False
1979         global_prefs['get_shininess'] = True
1980         global_prefs['color_from_face'] = True
1981         global_prefs['log to blender'] = True
1982         
1983         
1984         
1985         Blender.Window.WaitCursor(True)
1986         Blender.Window.EditMode(0)
1987         
1988         
1989         FF.add_file_to_search_path(filename)
1990         
1991         if global_prefs['verbose'] >= 1:
1992                 print 'Pass 1: Loading.'
1993                 print
1994
1995         load_time = Blender.sys.time()    
1996         db = Database(filename,grr)
1997         db.parse()
1998         load_time = Blender.sys.time() - load_time
1999
2000         if global_prefs['verbose'] >= 1:
2001                 print
2002                 print 'Pass 2: Importing to Blender.'
2003                 print
2004
2005         import_time = Blender.sys.time()
2006         db.blender_import()
2007         
2008         if global_prefs['attrib']:
2009                 print "reading attribute files"
2010                 db.read_attribute_files()
2011         
2012         Blender.Window.ViewLayer(range(1,21))
2013         
2014         update_scene(db.scene,[])
2015         import_time = Blender.sys.time() - import_time
2016         if global_prefs['verbose'] >= 1:
2017                 print 'Done.'
2018                 print
2019                 print 'Time to parse file: %.3f seconds' % load_time
2020                 print 'Time to import to blender: %.3f seconds' % import_time
2021                 print 'Total time: %.3f seconds' % (load_time + import_time)
2022         
2023         Blender.Window.WaitCursor(False)
2024
2025 def setimportscale(ID,val):
2026         global global_prefs
2027         global_prefs['scale'] = val
2028 def setBpath(fname):
2029         global_prefs['fltfile'] = fname
2030
2031 def event(evt,val):
2032         pass
2033 def but_event(evt):
2034         
2035         global FLTBaseLabel
2036         global FLTBaseString
2037         global FLTBaseChooser
2038
2039         global FLTExport
2040         global FLTClose
2041         
2042         global FLTDoXRef
2043         global FLTShadeImport
2044         global FLTAttrib
2045         
2046         #Import DB
2047         if evt == 1:
2048                 if global_prefs['verbose'] >= 1:
2049                         print
2050                         print 'OpenFlight Importer'
2051                         print 'Version:', __version__
2052                         print 'Author: Greg MacDonald, Campbell Barton, Geoffrey Bantle'
2053                         print __url__[2]
2054                         print
2055                 
2056                 GRR = GlobalResourceRepository()
2057                 select_file(global_prefs['fltfile'], GRR)
2058         #choose base path for export
2059         if evt == 4:
2060                 Blender.Window.FileSelector(setBpath, "DB Root", global_prefs['fltfile'])
2061         #Import custom shading?
2062         if evt == 9:
2063                 global_prefs['smoothshading'] = FLTShadeImport.val
2064         #Import Image attribute files
2065         if evt == 10:
2066                 global_prefs['attrib'] = FLTAttrib.val
2067         #export XRefs
2068         if evt == 13:
2069                 global_prefs['doxrefs'] = FLTDoXRef.val
2070         
2071         if evt == 2:
2072                 Draw.Exit()
2073         
2074
2075         
2076
2077 from Blender.BGL import *
2078 from Blender import Draw
2079 def gui():
2080         
2081         global FLTBaseLabel
2082         global FLTBaseString
2083         global FLTBaseChooser
2084
2085         global FLTExport
2086         global FLTClose
2087         
2088         global FLTDoXRef
2089         global FLTShadeImport
2090         
2091         global FLTAttrib
2092         
2093         
2094         glClearColor(0.772,0.832,0.847,1.0)
2095         glClear(GL_COLOR_BUFFER_BIT)
2096         
2097         areas = Blender.Window.GetScreenInfo()
2098         curarea = Blender.Window.GetAreaID()
2099         curRect = None
2100         
2101         for area in areas:
2102                 if area['id'] == curarea:
2103                         curRect = area['vertices']
2104                         break
2105         
2106         width = curRect[2] - curRect[0]
2107         height = curRect[3] - curRect[1]
2108         cx = 50
2109         cy = height - 80
2110
2111         FLTBaseLabel = Draw.Label("Base file:",cx,cy,100,20)
2112         FLTBaseString = Draw.String("",3,cx+100,cy,300,20,global_prefs['fltfile'],255,"Root DB file")
2113         FLTBaseChooser = Draw.PushButton("...",4,cx+400,cy,20,20,"Choose Folder")
2114         
2115         cy = cy-40
2116         FLTScale = Draw.Number("Import Scale",14,cx,cy,220,20,global_prefs['scale'],0.0,100.0,"Export scaleing factor",setimportscale)
2117         
2118         cy = cy-40
2119         FLTDoXRef = Draw.Toggle("Import XRefs", 13,cx,cy,220,20,global_prefs['doxrefs'],"Import External references")
2120         
2121         cy = cy-40
2122         FLTShadeImport = Draw.Toggle("Import Custom Shading",9,cx,cy,220,20,global_prefs['smoothshading'],"Import custom shading via edgesplit modifiers")
2123         
2124         cy = cy-40
2125         FLTAttrib = Draw.Toggle("Import Attribute Files", 10,cx,cy,220,20,global_prefs['attrib'],"Import Image Attribute files")
2126         
2127         cy = cy - 40
2128         FLTExport = Draw.PushButton("Import",1,cx,20,100,20,"Import FLT Database")
2129         FLTClose = Draw.PushButton("Close",2,cx+120,20,100,20,"Close Window")
2130
2131         
2132         
2133 Draw.Register(gui,event,but_event)