-> FLT importer hierarchy transform bugs
[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']
1332                 self.props['id'] = 'X: ' + Blender.sys.splitext(Blender.sys.basename(xref_filename))[0] #this is really wrong as well....
1333                 
1334                 if global_prefs['doxrefs'] and os.path.exists(xref_filename) and not self.header.grr.xrefs.has_key(xref_filename):
1335                         self.xref = Database(xref_filename, self.header.grr, self)
1336                         self.header.grr.xrefs[xref_filename] = self.xref
1337                 else:
1338                         self.xref = None
1339                 
1340
1341         def blender_import(self):
1342                 #name = self.props['type'] + ': ' + self.props['id']
1343                 name = self.props['id']
1344                 self.object = self.header.scene.objects.new('Empty')
1345                 self.object.name = name
1346                 self.object.enableDupGroup = True
1347                 self.header.group.objects.link(self.object)
1348                 
1349                 #for broken links its ok to leave this empty! they purely for visual purposes anyway.....
1350                 try:
1351                         self.object.DupGroup = self.header.grr.xrefs[self.props['3t200!filename']].group
1352                 except:
1353                         pass
1354                         
1355
1356
1357
1358                 if self.parent and self.parent.object:
1359                         self.parent.object.makeParent([self.object],1)
1360
1361                 if self.matrix:
1362                         self.object.setMatrix(self.matrix)
1363
1364
1365                 #id props import
1366                 self.object.properties['FLT'] = dict()
1367                 for key in self.props:
1368                         try:
1369                                 self.object.properties['FLT'][key] = self.props[key]
1370                         except: #horrible...
1371                                 pass
1372
1373                 self.object.Layer = current_layer
1374                 self.object.sel = 1
1375
1376                 Node.blender_import(self)
1377                 
1378                 
1379 class LOD(InterNode):
1380         def blender_import(self):
1381                 #self.move_to_next_layer()
1382                 InterNode.blender_import(self)
1383                 #self.object.properties['FLT'] = self.props.copy()
1384                 
1385         def __init__(self, parent):
1386                 Node.__init__(self, parent, parent.header)
1387                 InterNode.__init__(self)
1388
1389                 self.root_handler.set_handler({33: self.parse_long_id,
1390                                                                            31: self.parse_comment,
1391                                                                            10: self.parse_push,
1392                                                                            49: self.parse_matrix})
1393                 self.root_handler.set_throw_back_lst(throw_back_opcodes)
1394                 
1395                 self.child_handler.set_handler({2: self.parse_group,
1396                                                                                 111: self.parse_inline_light_point,
1397                                                                                 73: self.parse_lod,
1398                                                                                 4: self.parse_object,
1399                                                                                 10: self.parse_push,
1400                                                                                 11: self.parse_pop,
1401                                                                                 96: self.parse_unhandled, # switch
1402                                                                                 14: self.parse_dof, # DOF
1403                                                                                 91: self.parse_unhandled, # sound
1404                                                                                 98: self.parse_unhandled, # clip
1405                                                                                 63: self.parse_xref})
1406
1407                 self.props = dict()             
1408                 self.props['comment'] = ''
1409                 self.parse_record()
1410
1411 class InlineLightPoint(InterNode):
1412         def __init__(self, parent):
1413                 Node.__init__(self, parent, parent.header)
1414                 InterNode.__init__(self)
1415                 self.root_handler.set_handler({33: self.parse_long_id,
1416                                                                            31: self.parse_comment,
1417                                                                            10: self.parse_push,
1418                                                                            49: self.parse_matrix})
1419                 self.root_handler.set_throw_back_lst(throw_back_opcodes)
1420                 
1421                 self.child_handler.set_handler({72: self.parse_vertex_list,
1422                                                                                 10: self.parse_push,
1423                                                                                 11: self.parse_pop})
1424
1425                 self.indices = list()
1426                 self.props = dict()             
1427                 self.props['comment'] = ''
1428                 self.parse_record()
1429
1430                 
1431         def blender_import(self):
1432                 
1433
1434                 name = self.props['id']
1435                 self.mesh= Blender.Mesh.New()
1436                 self.mesh.name = 'FLT_LP'
1437                 self.object = self.header.scene.objects.new(self.mesh)
1438                 self.object.name = name
1439                 #self.mesh.verts.extend(Vector() ) # DUMMYVERT
1440                 self.object.Layer = current_layer
1441                 self.object.sel= 1
1442         
1443                 self.object.properties['FLT'] = dict()
1444                 for key in self.props:
1445                         try:
1446                                 self.object.properties['FLT'][key] = self.props[key]
1447                         except: #horrible...
1448                                 pass
1449
1450                 if self.parent and self.parent.object and self.header.scene == self.parent.header.scene:
1451                         self.parent.object.makeParent([self.object])
1452
1453                 if self.matrix:
1454                         self.object.setMatrix(self.matrix)
1455
1456                 self.mesh.verts.extend([self.header.vert_pal.blender_verts[i] for i in self.indices])
1457                 
1458                 #add color index information.
1459                 self.mesh.verts.addPropertyLayer("FLT_VCOL",Blender.Mesh.PropertyTypes["INT"])
1460                 for i, vindex in enumerate(self.indices):
1461                         vdesc = self.header.vert_pal.vert_desc_lst[vindex]
1462                         v = self.mesh.verts[i]
1463                         v.setProperty("FLT_VCOL",vdesc.cindex)
1464                 #for i, v in enumerate(self.mesh.verts):
1465                 #       vdesc = self.header.vert_pal.vert_desc_lst[i]
1466                 #       v.setProperty("FLT_VCOL",vdesc.cindex)
1467                 self.mesh.update()
1468                                 
1469         def parse_vertex_list(self):
1470                 length = self.header.fw.get_length()
1471                 fw = self.header.fw
1472                 vert_pal = self.header.vert_pal
1473
1474                 count = (length-4)/4
1475                 
1476                 # If this ever fails the chunk below does error checking
1477                 self.indices= [vert_pal.index[fw.read_int()] for i in xrange(count)]
1478                 
1479                 '''
1480                 for i in xrange(count):
1481                         byte_offset = fw.read_int()
1482                         if byte_offset in vert_pal.index:
1483                                 index = vert_pal.index[byte_offset]
1484                                 self.indices.append(index)
1485                         elif global_prefs['verbose'] >= 1:
1486                                 print 'Warning: Unable to map byte offset %s' + \
1487                                           ' to vertex index.' % byte_offset
1488                 '''
1489                 
1490                 return True
1491                 
1492
1493
1494 class IndexedLightPoint(InterNode):
1495         # return dictionary: lp_app name => index list
1496         def group_points(self, props):
1497                 
1498                 name_to_indices = {}
1499                 
1500                 for i in self.indices:
1501                         vert_desc = self.header.vert_pal.vert_desc_lst[i]
1502                         app_desc = LightPointAppDesc()
1503                         app_desc.props.update(props)
1504                         # add vertex normal and color
1505                         app_desc.props.update({'nx': vert_desc.nx})
1506                         app_desc.props.update({'ny': vert_desc.ny})
1507                         app_desc.props.update({'nz': vert_desc.nz})
1508                         
1509                         app_desc.props.update({'r': vert_desc.r})
1510                         app_desc.props.update({'g': vert_desc.g})
1511                         app_desc.props.update({'b': vert_desc.b})
1512                         app_desc.props.update({'a': vert_desc.a})
1513                         
1514                         app_name = self.header.grr.request_lightpoint_app(app_desc, self.header.scene)
1515
1516                         if name_to_indices.get(app_name):
1517                                 name_to_indices[app_name].append(i)
1518                         else:
1519                                 name_to_indices.update({app_name: [i]})
1520                         
1521                 return name_to_indices
1522                 
1523         def blender_import(self):
1524                 name = self.props['type'] + ': ' + self.props['id']
1525                 
1526                 name_to_indices = self.group_points(self.header.lightpoint_appearance_pal[self.index])
1527                 
1528                 for app_name, indices in name_to_indices.iteritems():        
1529                         self.object = Blender.Object.New('Mesh', name)
1530                         self.mesh= Blender.Mesh.New()
1531                         self.mesh.verts.extend( Vector() ) # DUMMYVERT
1532                         self.object.link(self.mesh)
1533                         
1534                         if self.parent:
1535                                 self.parent.object.makeParent([self.object])
1536                                 
1537                         for i in indices:
1538                                 vert = self.header.vert_pal.blender_verts[i]
1539                                 self.mesh.verts.append(vert)
1540                         
1541                         self.header.scene.objects.link(self.object)
1542         
1543                         self.object.Layer = current_layer
1544                         
1545                         if self.matrix:
1546                                 self.object.setMatrix(self.matrix)
1547                                 
1548                         # Import comment.
1549                         if self.props['comment'] != '':
1550                                 name = 'COMMENT: ' + self.props['id']
1551                                 t = Blender.Text.New(name)
1552                                 t.write(self.props['comment'])
1553                                 self.props['comment'] = name
1554                                 
1555                         # Attach properties.
1556                         self.props.update({'appearance': app_name})
1557                         for name, value in self.props.iteritems():
1558                                 self.object.addProperty(name, value)
1559                         
1560                         self.mesh.update()
1561                         
1562         def parse_vertex_list(self):
1563                 length = self.header.fw.get_length()
1564                 fw = self.header.fw
1565                 vert_pal = self.header.vert_pal
1566
1567                 count = (length-4)/4
1568                 
1569                 # If this ever fails the chunk below does error checking
1570                 self.indices= [vert_pal.index[fw.read_int()] for i in xrange(count)]
1571                 
1572                 '''
1573                 for i in xrange(count):
1574                         byte_offset = fw.read_int()
1575                         if byte_offset in vert_pal.index:
1576                                 index = vert_pal.index[byte_offset]
1577                                 self.indices.append(index)
1578                         elif global_prefs['verbose'] >= 1:
1579                                 print 'Warning: Unable to map byte offset %s' + \
1580                                           ' to vertex index.' % byte_offset
1581                 '''
1582                 return True
1583                 
1584         def __init__(self, parent):
1585                 Node.__init__(self, parent, parent.header)
1586                 InterNode.__init__(self)
1587                 self.root_handler.set_handler({33: self.parse_long_id,
1588                                                                            31: self.parse_comment,
1589                                                                            10: self.parse_push,
1590                                                                            49: self.parse_matrix})
1591                 self.root_handler.set_throw_back_lst(throw_back_opcodes)
1592                 
1593                 self.child_handler.set_handler({72: self.parse_vertex_list,
1594                                                                                 10: self.parse_push,
1595                                                                                 11: self.parse_pop})
1596
1597                 self.indices = list()
1598                 
1599                 self.props = dict.fromkeys(['id', 'type', 'comment', 'draw order', 'appearance'])
1600                 self.props['comment'] = ''
1601                 self.props['type'] = 'Light Point'
1602                 self.props['id'] = self.header.fw.read_string(8)
1603                 self.index = self.header.fw.read_int()
1604                 self.header.fw.read_ahead(4) # animation index
1605                 self.props['draw order'] = self.header.fw.read_int()        
1606
1607 class Unhandled(InterNode):
1608         def __init__(self, parent):
1609                 Node.__init__(self, parent, parent.header)
1610                 InterNode.__init__(self)
1611                 
1612                 self.root_handler.set_handler({33: self.parse_long_id,
1613                                                                            31: self.parse_comment,
1614                                                                            10: self.parse_push,
1615                                                                            49: self.parse_matrix})
1616                 self.root_handler.set_throw_back_lst(throw_back_opcodes)
1617                 
1618                 self.child_handler.set_handler({2: self.parse_group,
1619                                                                                 73: self.parse_lod,
1620                                                                                 4: self.parse_object,
1621                                                                                 10: self.parse_push,
1622                                                                                 11: self.parse_pop,
1623                                                                                 96: self.parse_unhandled, # switch
1624                                                                                 14: self.parse_dof, # DOF
1625                                                                                 91: self.parse_unhandled, # sound
1626                                                                                 98: self.parse_unhandled, # clip
1627                                                                                 63: self.parse_xref})
1628
1629                 self.props['id'] = self.header.fw.read_string(8)
1630
1631 class Database(InterNode):
1632         def blender_import(self):
1633                 for key in self.tex_pal.keys():
1634                         path_filename= FF.find(self.tex_pal[key][0])
1635                         if path_filename != None:
1636                                 img = self.grr.request_image(path_filename)
1637                                 if img:
1638                                         self.tex_pal[key][1] = img
1639                         elif global_prefs['verbose'] >= 1:
1640                                 print 'Warning: Unable to find', self.tex_pal[key][0]
1641                 
1642                 self.scene.properties['FLT'] = dict()
1643                 for key in self.props:
1644                         try:
1645                                 self.scene.properties['FLT'][key] = self.props[key]
1646                         except: #horrible...
1647                                 pass
1648                 
1649                 self.scene.properties['FLT']['Main'] = 0
1650                 self.scene.properties['FLT']['Filename'] = self.bname
1651                 
1652                 #import color palette
1653                 carray = list()
1654                 for color in self.col_pal:
1655                         carray.append(struct.unpack('>i',struct.pack('>BBBB',color[0],color[1],color[2],color[3]))[0])
1656                 self.scene.properties['FLT']['Color Palette'] = carray
1657                 Node.blender_import(self)
1658
1659         def parse_appearance_palette(self):
1660                 props = dict()
1661                 self.fw.read_ahead(4) # reserved
1662                 props.update({'id': self.fw.read_string(256)})
1663                 index = self.fw.read_int()
1664                 props.update({'smc': self.fw.read_short()})
1665                 props.update({'fid': self.fw.read_short()})
1666                 props.update({'back color: a': self.fw.read_uchar()})
1667                 props.update({'back color: b': self.fw.read_uchar()})
1668                 props.update({'back color: g': self.fw.read_uchar()})
1669                 props.update({'back color: r': self.fw.read_uchar()})
1670                 props.update({'display mode': self.fw.read_int()})
1671                 props.update({'intensity': self.fw.read_float()})
1672                 props.update({'back intensity': self.fw.read_float()})
1673                 props.update({'minimum defocus': self.fw.read_float()})
1674                 props.update({'maximum defocus': self.fw.read_float()})
1675                 props.update({'fading mode': self.fw.read_int()})
1676                 props.update({'fog punch mode': self.fw.read_int()})
1677                 props.update({'directional mode': self.fw.read_int()})
1678                 props.update({'range mode': self.fw.read_int()})
1679                 props.update({'min pixel size': self.fw.read_float()})
1680                 props.update({'max pixel size': self.fw.read_float()})
1681                 props.update({'actual size': self.fw.read_float()})
1682                 props.update({'trans falloff pixel size': self.fw.read_float()})
1683                 props.update({'trans falloff exponent': self.fw.read_float()})
1684                 props.update({'trans falloff scalar': self.fw.read_float()})
1685                 props.update({'trans falloff clamp': self.fw.read_float()})
1686                 props.update({'fog scalar': self.fw.read_float()})
1687                 props.update({'fog intensity': self.fw.read_float()})
1688                 props.update({'size threshold': self.fw.read_float()})
1689                 props.update({'directionality': self.fw.read_int()})
1690                 props.update({'horizontal lobe angle': self.fw.read_float()})
1691                 props.update({'vertical lobe angle': self.fw.read_float()})
1692                 props.update({'lobe roll angle': self.fw.read_float()})
1693                 props.update({'dir falloff exponent': self.fw.read_float()})
1694                 props.update({'dir ambient intensity': self.fw.read_float()})
1695                 props.update({'significance': self.fw.read_float()})
1696                 props.update({'flags': self.fw.read_int()})
1697                 props.update({'visibility range': self.fw.read_float()})
1698                 props.update({'fade range ratio': self.fw.read_float()})
1699                 props.update({'fade in duration': self.fw.read_float()})
1700                 props.update({'fade out duration': self.fw.read_float()})
1701                 props.update({'LOD range ratio': self.fw.read_float()})
1702                 props.update({'LOD scale': self.fw.read_float()})
1703                 
1704                 self.lightpoint_appearance_pal.update({index: props})
1705                 
1706         def parse_header(self):
1707                 self.props['type'] = 'Header'
1708                 self.props['comment'] = ''
1709                 self.props['id'] = self.fw.read_string(8)
1710                 self.props['version'] = self.fw.read_int()
1711                 self.fw.read_ahead(46)
1712                 self.props['units'] = self.fw.read_char()
1713                 self.props['set white'] = bool(self.fw.read_char())
1714                 self.props['flags'] = self.fw.read_int()
1715                 self.fw.read_ahead(24)
1716                 self.props['projection type'] = self.fw.read_int()
1717                 self.fw.read_ahead(36)
1718                 self.props['sw x'] = self.fw.read_double()
1719                 self.props['sw y'] = self.fw.read_double()
1720                 self.props['dx'] = self.fw.read_double()
1721                 self.props['dy'] = self.fw.read_double()
1722                 self.fw.read_ahead(24)
1723                 self.props['sw lat'] = self.fw.read_double()
1724                 self.props['sw lon'] = self.fw.read_double()
1725                 self.props['ne lat'] = self.fw.read_double()
1726                 self.props['ne lon'] = self.fw.read_double()
1727                 self.props['origin lat'] = self.fw.read_double()
1728                 self.props['origin lon'] = self.fw.read_double()
1729                 self.props['lambert lat1'] = self.fw.read_double()
1730                 self.props['lambert lat2'] = self.fw.read_double()
1731                 self.fw.read_ahead(16)
1732                 self.props['ellipsoid model'] = self.fw.read_int()
1733                 self.fw.read_ahead(4)
1734                 self.props['utm zone'] = self.fw.read_short()
1735                 self.fw.read_ahead(6)
1736                 self.props['dz'] = self.fw.read_double()
1737                 self.props['radius'] = self.fw.read_double()
1738                 self.fw.read_ahead(8)
1739                 self.props['major axis'] = self.fw.read_double()
1740                 self.props['minor axis'] = self.fw.read_double()
1741                 
1742                 if global_prefs['verbose'] >= 1:
1743                         print 'OpenFlight Version:', float(self.props['version']) / 100.0
1744                         print
1745                         
1746                 return True
1747
1748         def parse_mat_palette(self):
1749                 mat_desc = MaterialDesc()
1750                 index = self.fw.read_int()
1751
1752                 name = self.fw.read_string(12)
1753                 if len(mat_desc.name) > 0:
1754                         mat_desc.name = name
1755
1756                 flag = self.fw.read_int()
1757                 # skip material if not used
1758                 if not flag & 0x80000000:
1759                         return True
1760
1761                 ambient_col = [self.fw.read_float(), self.fw.read_float(), self.fw.read_float()]
1762                 mat_desc.diffuse = [self.fw.read_float(), self.fw.read_float(), self.fw.read_float()]
1763                 mat_desc.specular = [self.fw.read_float(), self.fw.read_float(), self.fw.read_float()]
1764                 emissive_col = [self.fw.read_float(), self.fw.read_float(), self.fw.read_float()]
1765
1766                 mat_desc.shininess = self.fw.read_float() / 64.0 # [0.0, 128.0] => [0.0, 2.0]
1767                 mat_desc.alpha = self.fw.read_float()
1768
1769                 # Convert ambient and emissive colors into intensitities.
1770                 mat_desc.ambient = col_to_gray(ambient_col)
1771                 mat_desc.emissive = col_to_gray(emissive_col)
1772
1773                 self.mat_desc_pal_lst.append( (index, mat_desc) )
1774                 
1775                 return True
1776         
1777         def get_color(self, color_index):
1778                 color = None
1779                 index = color_index / 128
1780                 intensity = float(color_index - 128.0 * index) / 127.0
1781                 
1782                 if index >= 0 and index <= 1023:
1783                         brightest = self.col_pal[index]
1784                         r = int(brightest[0] * intensity)
1785                         g = int(brightest[1] * intensity)
1786                         b = int(brightest[2] * intensity)
1787                         a = int(brightest[3])
1788                         
1789                         color = [r, g, b, a]
1790                 
1791                 return color
1792         
1793         def parse_color_palette(self):
1794                 self.header.fw.read_ahead(128)
1795                 for i in xrange(1024):
1796                         a = self.header.fw.read_uchar()
1797                         b = self.header.fw.read_uchar()
1798                         g = self.header.fw.read_uchar()
1799                         r = self.header.fw.read_uchar()
1800                         self.col_pal.append((r, g, b, a))
1801                 return True
1802                 
1803         def parse_vertex_palette(self):
1804                 self.vert_pal = VertexPalette(self)
1805                 self.vert_pal.parse()
1806                 return True
1807                 
1808         def parse_texture_palette(self):
1809                 name = self.fw.read_string(200)
1810                 index = self.fw.read_int()
1811                 self.tex_pal[index]= [name, None]
1812                 return True
1813         
1814         def read_attribute_files(self):
1815                 for tex in self.tex_pal.keys():
1816                         [name,image] = self.tex_pal[tex]
1817                         basename = os.path.basename(name)
1818                         if(image):
1819                                 basename = basename + ".attr"
1820                                 dirname = os.path.dirname(Blender.sys.expandpath(image.getFilename())) #can't rely on original info stored in pallette since it might be relative link
1821                                 newpath = os.path.join(dirname, basename)
1822                                 if os.path.exists(newpath) and not image.properties.has_key('FLT'):
1823                                         fw = flt_filewalker.FltIn(newpath)
1824                                         fw.read_ahead(8) #We dont care what the attribute file says about x/y dimensions
1825                                         image.properties['FLT']={}
1826                                         
1827                                         #need to steal code from parse records....
1828                                         props = records['Image']
1829                                         propkeys = props.keys()
1830                                         propkeys.sort()
1831                                         for position in propkeys:
1832                                                 (type,length,name) = props[position]
1833                                                 image.properties['FLT'][name] = read_prop(fw,type,length)
1834                                         fw.close_file()
1835                                         
1836                                         #copy clamp settings
1837                                         wrap = image.properties['FLT']['10i!Wrap']
1838                                         wrapu = image.properties['FLT']['11i!WrapU']
1839                                         wrapv = image.properties['FLT']['12i!WrapV']
1840                                         
1841                                         if wrapu == 3 or wrapv == 3:
1842                                                 wrapuv = (wrap,wrap)
1843                                         else:
1844                                                 wrapuv = (wrapu, wrapv)
1845                                         image.clampX = wrapuv[0]
1846                                         image.clampY = wrapuv[1]
1847                                         
1848                                 elif not os.path.exists(newpath):
1849                                         print "Cannot read attribute file:" + newpath
1850                                         
1851         def __init__(self, filename, grr, parent=None):
1852                 if global_prefs['verbose'] >= 1:
1853                         print 'Parsing:', filename
1854                         print
1855                 
1856                 self.fw = flt_filewalker.FltIn(filename)
1857                 self.filename = filename
1858                 self.bname = os.path.splitext(os.path.basename(filename))[0]
1859                 self.grr = grr
1860                 
1861                 Node.__init__(self, parent, self)
1862                 InterNode.__init__(self)
1863                 
1864                 self.root_handler.set_handler({1: self.parse_header,
1865                                                                            67: self.parse_vertex_palette,
1866                                                                            33: self.parse_long_id,
1867                                                                            31: self.parse_comment,
1868                                                                            64: self.parse_texture_palette,
1869                                                                            32: self.parse_color_palette,
1870                                                                            113: self.parse_mat_palette,
1871                                                                            128: self.parse_appearance_palette,
1872                                                                            10: self.parse_push})
1873                 if parent:
1874                         self.root_handler.set_throw_back_lst(throw_back_opcodes)
1875
1876                 self.child_handler.set_handler({#130: self.parse_indexed_light_point,
1877                                                                                 111: self.parse_inline_light_point,
1878                                                                                 2: self.parse_group,
1879                                                                                 73: self.parse_lod,
1880                                                                                 4: self.parse_object,
1881                                                                                 10: self.parse_push,
1882                                                                                 11: self.parse_pop,
1883                                                                                 96: self.parse_unhandled,
1884                                                                                 14: self.parse_dof,
1885                                                                                 91: self.parse_unhandled,
1886                                                                                 98: self.parse_unhandled,
1887                                                                                 63: self.parse_xref})
1888                 
1889                 self.scene = Blender.Scene.New(self.bname)
1890                 self.group = Blender.Group.New(self.bname)
1891
1892                 self.vert_pal = None
1893                 self.lightpoint_appearance_pal = dict()
1894                 self.tex_pal = dict()
1895                 #self.tex_pal_lst = list()
1896                 #self.bl_tex_pal = dict()
1897                 self.col_pal = list()
1898                 self.mat_desc_pal_lst = list()
1899                 self.mat_desc_pal = dict()
1900                 self.props = dict.fromkeys(['id', 'type', 'comment', 'version', 'units', 'set white',
1901                         'flags', 'projection type', 'sw x', 'sw y', 'dx', 'dy', 'dz', 'sw lat',
1902                         'sw lon', 'ne lat', 'ne lon', 'origin lat', 'origin lon', 'lambert lat1',
1903                         'lambert lat2', 'ellipsoid model', 'utm zone', 'radius', 'major axis', 'minor axis'])
1904
1905
1906 def clearparent(root,childhash):
1907         for child in childhash[root]:
1908                 clearparent(child,childhash)
1909         root.clrParent(2,0)
1910
1911 def fixscale(root,childhash):   
1912         for child in childhash[root]:
1913                 fixscale(child,childhash)
1914         location = Blender.Mathutils.Vector(root.getLocation('worldspace'))
1915         if location[0] != 0.0 or location[1] != 0.0 or location[2] != 0.0:
1916                 #direction = Blender.Mathutils.Vector(0-location[0],0-location[1],0-location[2]) #reverse vector
1917                 smat = Blender.Mathutils.ScaleMatrix(global_prefs['scale'],4)
1918                 root.setLocation(location * smat)
1919         #if its a mesh, we need to scale all of its vertices too
1920         if root.type == 'Mesh':
1921                 smat = Blender.Mathutils.ScaleMatrix(global_prefs['scale'],4)
1922                 rmesh = root.getData(mesh=True)
1923                 for v in rmesh.verts:
1924                         v.co = v.co * smat
1925         
1926         
1927 def reparent(root,childhash,sce):
1928         for child in childhash[root]:
1929                 reparent(child,childhash,sce)
1930         
1931         root.makeParent(childhash[root])
1932         sce.update(1)
1933         
1934 def update_scene(root,sdone):
1935         for object in root.objects:
1936                 if object.DupGroup:
1937                         try:
1938                                 child = Blender.Scene.Get(object.DupGroup.name)
1939                         except:
1940                                 child = None
1941                         if child and child not in sdone:
1942                                 update_scene(child,sdone)
1943         root.makeCurrent()
1944         #create a list of children for each object
1945         childhash = dict()
1946         for object in root.objects:
1947                 childhash[object] = list()
1948                 
1949         for object in root.objects:
1950                 if object.parent:
1951                         childhash[object.parent].append(object)
1952         
1953         for object in root.objects:
1954                 if not object.parent:
1955                         #recursivley go through and clear all the children of their transformation, starting at deepest level first.
1956                         clearparent(object,childhash)
1957                         #now fix the location of everything
1958                         fixscale(object,childhash)
1959                         #now fix the parenting
1960                         reparent(object,childhash,root)
1961         
1962         for object in root.objects:
1963                 object.makeDisplayList()
1964         root.update(1)
1965         sdone.append(root)
1966
1967
1968 def select_file(filename, grr):
1969         if not Blender.sys.exists(filename):
1970                 msg = 'Error: File ' + filename + ' does not exist.'
1971                 Blender.Draw.PupMenu(msg)
1972                 return
1973         
1974         if not filename.lower().endswith('.flt'):
1975                 msg = 'Error: Not a flight file.'
1976                 Blender.Draw.PupMenu(msg)
1977                 print msg
1978                 print
1979                 return
1980         
1981         global_prefs['fltfile']= filename
1982         global_prefs['verbose']= 1
1983         global_prefs['get_texture'] = True
1984         global_prefs['get_diffuse'] = True
1985         global_prefs['get_specular'] = False
1986         global_prefs['get_emissive'] = False
1987         global_prefs['get_alpha'] = True
1988         global_prefs['get_ambient'] = False
1989         global_prefs['get_shininess'] = True
1990         global_prefs['color_from_face'] = True
1991         global_prefs['log to blender'] = True
1992         
1993         
1994         
1995         Blender.Window.WaitCursor(True)
1996         Blender.Window.EditMode(0)
1997         
1998         
1999         FF.add_file_to_search_path(filename)
2000         
2001         if global_prefs['verbose'] >= 1:
2002                 print 'Pass 1: Loading.'
2003                 print
2004
2005         load_time = Blender.sys.time()    
2006         db = Database(filename,grr)
2007         db.parse()
2008         load_time = Blender.sys.time() - load_time
2009
2010         if global_prefs['verbose'] >= 1:
2011                 print
2012                 print 'Pass 2: Importing to Blender.'
2013                 print
2014
2015         import_time = Blender.sys.time()
2016         db.blender_import()
2017         
2018         if global_prefs['attrib']:
2019                 print "reading attribute files"
2020                 db.read_attribute_files()
2021         
2022         Blender.Window.ViewLayer(range(1,21))
2023         
2024         update_scene(db.scene,[])
2025         import_time = Blender.sys.time() - import_time
2026         if global_prefs['verbose'] >= 1:
2027                 print 'Done.'
2028                 print
2029                 print 'Time to parse file: %.3f seconds' % load_time
2030                 print 'Time to import to blender: %.3f seconds' % import_time
2031                 print 'Total time: %.3f seconds' % (load_time + import_time)
2032         
2033         Blender.Window.WaitCursor(False)
2034
2035 def setimportscale(ID,val):
2036         global global_prefs
2037         global_prefs['scale'] = val
2038 def setBpath(fname):
2039         global_prefs['fltfile'] = fname
2040
2041 def event(evt,val):
2042         pass
2043 def but_event(evt):
2044         
2045         global FLTBaseLabel
2046         global FLTBaseString
2047         global FLTBaseChooser
2048
2049         global FLTExport
2050         global FLTClose
2051         
2052         global FLTDoXRef
2053         global FLTShadeImport
2054         global FLTAttrib
2055         
2056         #Import DB
2057         if evt == 1:
2058                 if global_prefs['verbose'] >= 1:
2059                         print
2060                         print 'OpenFlight Importer'
2061                         print 'Version:', __version__
2062                         print 'Author: Greg MacDonald, Campbell Barton, Geoffrey Bantle'
2063                         print __url__[2]
2064                         print
2065                 
2066                 GRR = GlobalResourceRepository()
2067                 select_file(global_prefs['fltfile'], GRR)
2068         #choose base path for export
2069         if evt == 4:
2070                 Blender.Window.FileSelector(setBpath, "DB Root", global_prefs['fltfile'])
2071         #Import custom shading?
2072         if evt == 9:
2073                 global_prefs['smoothshading'] = FLTShadeImport.val
2074         #Import Image attribute files
2075         if evt == 10:
2076                 global_prefs['attrib'] = FLTAttrib.val
2077         #export XRefs
2078         if evt == 13:
2079                 global_prefs['doxrefs'] = FLTDoXRef.val
2080         
2081         if evt == 2:
2082                 Draw.Exit()
2083         
2084
2085         
2086
2087 from Blender.BGL import *
2088 from Blender import Draw
2089 def gui():
2090         
2091         global FLTBaseLabel
2092         global FLTBaseString
2093         global FLTBaseChooser
2094
2095         global FLTExport
2096         global FLTClose
2097         
2098         global FLTDoXRef
2099         global FLTShadeImport
2100         
2101         global FLTAttrib
2102         
2103         
2104         glClearColor(0.772,0.832,0.847,1.0)
2105         glClear(GL_COLOR_BUFFER_BIT)
2106         
2107         areas = Blender.Window.GetScreenInfo()
2108         curarea = Blender.Window.GetAreaID()
2109         curRect = None
2110         
2111         for area in areas:
2112                 if area['id'] == curarea:
2113                         curRect = area['vertices']
2114                         break
2115         
2116         width = curRect[2] - curRect[0]
2117         height = curRect[3] - curRect[1]
2118         cx = 50
2119         cy = height - 80
2120
2121         FLTBaseLabel = Draw.Label("Base file:",cx,cy,100,20)
2122         FLTBaseString = Draw.String("",3,cx+100,cy,300,20,global_prefs['fltfile'],255,"Root DB file")
2123         FLTBaseChooser = Draw.PushButton("...",4,cx+400,cy,20,20,"Choose Folder")
2124         
2125         cy = cy-40
2126         FLTScale = Draw.Number("Import Scale",14,cx,cy,220,20,global_prefs['scale'],0.0,100.0,"Export scaleing factor",setimportscale)
2127         
2128         cy = cy-40
2129         FLTDoXRef = Draw.Toggle("Import XRefs", 13,cx,cy,220,20,global_prefs['doxrefs'],"Import External references")
2130         
2131         cy = cy-40
2132         FLTShadeImport = Draw.Toggle("Import Custom Shading",9,cx,cy,220,20,global_prefs['smoothshading'],"Import custom shading via edgesplit modifiers")
2133         
2134         cy = cy-40
2135         FLTAttrib = Draw.Toggle("Import Attribute Files", 10,cx,cy,220,20,global_prefs['attrib'],"Import Image Attribute files")
2136         
2137         cy = cy - 40
2138         FLTExport = Draw.PushButton("Import",1,cx,20,100,20,"Import FLT Database")
2139         FLTClose = Draw.PushButton("Close",2,cx+120,20,100,20,"Close Window")
2140
2141         
2142         
2143 Draw.Register(gui,event,but_event)