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