removed slow list handeling
authorCampbell Barton <ideasman42@gmail.com>
Tue, 10 Jan 2006 17:40:50 +0000 (17:40 +0000)
committerCampbell Barton <ideasman42@gmail.com>
Tue, 10 Jan 2006 17:40:50 +0000 (17:40 +0000)
faster vert comparisons
replaced spaces for tabs
removed unneeded getData() calls
general cleanup.

release/scripts/flt_export.py

index b5ab09255b23b9a32ef1419658a45b7aa67951d6..b5c5797fa42efe53d99836418b8805d7050f31e6 100755 (executable)
@@ -57,707 +57,667 @@ What's Not Handled:<br>
 """
 
 import Blender
-import math
 from flt_filewalker import FltOut
 
 class ExporterOptions:
-    def __init__(self):
-        self.defaults = { 'Diffuse Color To OpenFlight Material': False,
-                          'Diffuse Color To OpenFlight Face': True}
-        
-        d = Blender.Registry.GetKey('flt_export', True)
-        
-        if d == None or d.keys() != self.defaults.keys():
-            d = self.defaults
-            Blender.Registry.SetKey('flt_export', d, True)
-        
-        self.verbose = 1
-        self.tolerance = 0.001
-        self.use_mat_color = d['Diffuse Color To OpenFlight Material']
-        self.use_face_color = d['Diffuse Color To OpenFlight Face']
-        
+       def __init__(self):
+               self.defaults = { 'Diffuse Color To OpenFlight Material': False,
+                                                 'Diffuse Color To OpenFlight Face': True}
+               
+               d = Blender.Registry.GetKey('flt_export', True)
+               
+               if d == None or d.keys() != self.defaults.keys():
+                       d = self.defaults
+                       Blender.Registry.SetKey('flt_export', d, True)
+               
+               self.verbose = 1
+               self.tolerance = 0.001
+               self.use_mat_color = d['Diffuse Color To OpenFlight Material']
+               self.use_face_color = d['Diffuse Color To OpenFlight Face']
+               
 options = ExporterOptions()
 
-def not_equal_float(f1, f2):
-    return math.fabs(f1 - f2) > options.tolerance
+FLOAT_TOLERANCE = options.tolerance
 
 identity_matrix = [[1.0, 0.0, 0.0, 0.0], [0.0, 1.0, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0], [0.0, 0.0, 0.0, 1.0]]
 
 def is_identity(m):
-    for i in range(4):
-        for j in range(4):
-            if not_equal_float(m[i][j], identity_matrix[i][j]):
-                return False
-    return True
+       for i in xrange(4):
+               for j in xrange(4):
+                       if abs(m[i][j] - identity_matrix[i][j]) > FLOAT_TOLERANCE:
+                               return False
+       return True
 
 class MaterialDesc:
-    def __init__(self):
-        self.name = 'Blender'
+       def __init__(self):
+               self.name = 'Blender'
 
-        # Colors, List of 3 floats.
-        self.diffuse = [1.0, 1.0, 1.0]
-        self.specular = [1.0, 1.0, 1.0]
+               # Colors, List of 3 floats.
+               self.diffuse = [1.0, 1.0, 1.0]
+               self.specular = [1.0, 1.0, 1.0]
 
-        # Scalars
-        self.ambient = 0.1 # [0.0, 1.0]
-        self.emissive = 0.0 # [0.0, 1.0]
-        self.shininess = 32.0 # Range is [0.0, 128.0]
-        self.alpha = 1.0 # Range is [0.0, 1.0]
+               # Scalars
+               self.ambient = 0.1 # [0.0, 1.0]
+               self.emissive = 0.0 # [0.0, 1.0]
+               self.shininess = 32.0 # Range is [0.0, 128.0]
+               self.alpha = 1.0 # Range is [0.0, 1.0]
 
 class VertexDesc:
-    def __init__(self):
-        self.x = 0.0
-        self.y = 0.0
-        self.z = 0.0
-        self.nx = 0.0
-        self.ny = 0.0
-        self.nz = 0.0
-        self.u = 0.0
-        self.v = 0.0
+       def __init__(self, co=None, no=None, uv=None):
+               if co: self.x, self.y, self.z = tuple(co)
+               else: self.x = self.y = self.z = 0.0
+               if no: self.nx, self.ny, self.nz = tuple(no)
+               else: self.nx = self.ny = self.nz = 0.0
+               if uv: self.u, self.v = tuple(uv)
+               else: self.u = self.v = 0.0
 
 class GlobalResourceRepository:
-    def new_face_name(self):
-        n =  'f' + str(self.face_name)
-        self.face_name += 1
-        return n
-
-    def vertex_count(self):
-        return len(self.vertex_lst)
-
-    def request_vertex_desc(self, i):
-        return self.vertex_lst[i]
-
-    def request_vertex_index(self, desc):
-        match = None
-        for i in range(len(self.vertex_lst)):
-            if not_equal_float(self.vertex_lst[i].x, desc.x):
-                continue
-            if not_equal_float(self.vertex_lst[i].y, desc.y):
-                continue
-            if not_equal_float(self.vertex_lst[i].z, desc.z):
-                continue
-            if not_equal_float(self.vertex_lst[i].nx, desc.nx):
-                continue
-            if not_equal_float(self.vertex_lst[i].ny, desc.ny):
-                continue
-            if not_equal_float(self.vertex_lst[i].nz, desc.nz):
-                continue
-            if not_equal_float(self.vertex_lst[i].u, desc.u):
-                continue
-            if not_equal_float(self.vertex_lst[i].v, desc.v):
-                continue
-
-            match = i
-            break
-
-        if match != None:
-            return match
-        else:
-            self.vertex_lst.append(desc)
-            return len(self.vertex_lst) - 1
-
-    def request_texture_index(self, filename):
-        match = None
-        for i in range(len(self.texture_lst)):
-            if self.texture_lst[i] != filename:
-                continue
-            match = i
-            break
-        if match != None:
-            return match
-        else:
-            self.texture_lst.append(filename)
-            return len(self.texture_lst) - 1
-
-    def request_texture_filename(self, index):
-        return self.texture_lst[index]
-
-    def texture_count(self):
-        return len(self.texture_lst)
-
-    def request_material_index(self, desc):
-        match = None
-        for i in range(len(self.material_lst)):
-            if self.material_lst[i].diffuse != desc.diffuse:
-                continue
-            if self.material_lst[i].specular != desc.specular:
-                continue
-            if self.material_lst[i].ambient != desc.ambient:
-                continue
-            if self.material_lst[i].emissive != desc.emissive:
-                continue
-            if self.material_lst[i].shininess != desc.shininess:
-                continue
-            if self.material_lst[i].alpha != desc.alpha:
-                continue
-            match = i
-            break
-
-        if match != None:
-            return i
-        else:
-            self.material_lst.append(desc)
-            return len(self.material_lst) - 1
-
-    def request_material_desc(self, index):
-        return self.material_lst[index]
-
-    def material_count(self):
-        return len(self.material_lst)
-
-    # Returns not actual index but one that includes intensity information.
-    # color_index = 127*intensity + 128*actual_index
-    def request_color_index(self, col):
-        r = col[0]
-        g = col[1]
-        b = col[2]
-        m = max(r, g, b)
-        if m > 0.0:
-            intensity = m / 1.0
-            r = int(round(r/m * 255.0))
-            g = int(round(g/m * 255.0))
-            b = int(round(b/m * 255.0))
-            brightest = [r, g, b]
-        else:
-            brightest = [255, 255, 255]
-            intensity = 0.0
-
-        match = None
-        for i in range(len(self.color_lst)):
-            if self.color_lst[i] != brightest:
-                continue
-
-            match = i
-            break
-
-        if match != None:
-            index = match
-        else:
-            length = len(self.color_lst)
-            if length <= 1024:
-                self.color_lst.append(brightest)
-                index = length
-            else:
-                if options.verbose >= 1:
-                    print 'Warning: Exceeded max color limit.'
-                index = 0
-
-        color_index = int(round(127.0*intensity)) + 128*index
-        return color_index
-
-    # Returns color from actual index.
-    def request_max_color(self, index):
-        return self.color_lst[index]
-
-    def color_count(self):
-        return len(self.color_lst)
-
-    def __init__(self):
-        self.vertex_lst = []
-        self.texture_lst = []
-        self.material_lst = []
-        self.color_lst = [[255, 255, 255]]
-        self.face_name = 0
+       def new_face_name(self):
+               self.face_name += 1
+               return 'f%i' % (self.face_name-1)
+
+       def vertex_count(self):
+               return len(self.vertex_lst)
+
+       def request_vertex_desc(self, i):
+               return self.vertex_lst[i]
+
+       def request_vertex_index(self, desc):
+               match = None
+               for i, v in enumerate(self.vertex_lst):
+                       if\
+                       abs(v.x - desc.x) > FLOAT_TOLERANCE or\
+                       abs(v.y - desc.y) > FLOAT_TOLERANCE or\
+                       abs(v.z - desc.z) > FLOAT_TOLERANCE or\
+                       abs(v.nx - desc.nx) > FLOAT_TOLERANCE or\
+                       abs(v.ny - desc.ny) > FLOAT_TOLERANCE or\
+                       abs(v.nz - desc.nz) > FLOAT_TOLERANCE or\
+                       abs(v.u - desc.u) > FLOAT_TOLERANCE or\
+                       abs(v.v - desc.v) > FLOAT_TOLERANCE:
+                               pass
+                       else:
+                               match = i
+                               break
+
+               if match != None:
+                       return match
+               else:
+                       self.vertex_lst.append(desc)
+                       return len(self.vertex_lst) - 1
+
+       def request_texture_index(self, filename):
+               match = None
+               for i in xrange(len(self.texture_lst)):
+                       if self.texture_lst[i] != filename:
+                               continue
+                       match = i
+                       break
+               if match != None:
+                       return match
+               else:
+                       self.texture_lst.append(filename)
+                       return len(self.texture_lst) - 1
+
+       def request_texture_filename(self, index):
+               return self.texture_lst[index]
+
+       def texture_count(self):
+               return len(self.texture_lst)
+
+       def request_material_index(self, desc):
+               match = None
+               for i in xrange(len(self.material_lst)):
+                       if self.material_lst[i].diffuse != desc.diffuse:
+                               continue
+                       if self.material_lst[i].specular != desc.specular:
+                               continue
+                       if self.material_lst[i].ambient != desc.ambient:
+                               continue
+                       if self.material_lst[i].emissive != desc.emissive:
+                               continue
+                       if self.material_lst[i].shininess != desc.shininess:
+                               continue
+                       if self.material_lst[i].alpha != desc.alpha:
+                               continue
+                       match = i
+                       break
+
+               if match != None:
+                       return i
+               else:
+                       self.material_lst.append(desc)
+                       return len(self.material_lst) - 1
+
+       def request_material_desc(self, index):
+               return self.material_lst[index]
+
+       def material_count(self):
+               return len(self.material_lst)
+
+       # Returns not actual index but one that includes intensity information.
+       # color_index = 127*intensity + 128*actual_index
+       def request_color_index(self, col):
+               r,g,b = tuple(col)
+               m = max(r, g, b)
+               if m > 0.0:
+                       intensity = m / 1.0
+                       r = int(round(r/m * 255.0))
+                       g = int(round(g/m * 255.0))
+                       b = int(round(b/m * 255.0))
+                       brightest = [r, g, b]
+               else:
+                       brightest = [255, 255, 255]
+                       intensity = 0.0
+
+               match = None
+               for i in xrange(len(self.color_lst)):
+                       if self.color_lst[i] != brightest:
+                               continue
+
+                       match = i
+                       break
+
+               if match != None:
+                       index = match
+               else:
+                       length = len(self.color_lst)
+                       if length <= 1024:
+                               self.color_lst.append(brightest)
+                               index = length
+                       else:
+                               if options.verbose >= 1:
+                                       print 'Warning: Exceeded max color limit.'
+                               index = 0
+
+               color_index = int(round(127.0*intensity)) + 128*index
+               return color_index
+
+       # Returns color from actual index.
+       def request_max_color(self, index):
+               return self.color_lst[index]
+
+       def color_count(self):
+               return len(self.color_lst)
+
+       def __init__(self):
+               self.vertex_lst = []
+               self.texture_lst = []
+               self.material_lst = []
+               self.color_lst = [[255, 255, 255]]
+               self.face_name = 0
 
 class Node:
-    # Gathers info from blender needed for export.
-    # The =[0] is a trick to emulate c-like static function variables
-    # that are persistant between calls.
-    def blender_export(self, level=[0]):
-        if self.object:
-            if options.verbose >= 2:
-                for j in range(level[0]):
-                    print '  ',
-                print self.name, type(self.object.getData())
-
-        level[0] += 1
-        
-        for child in self.children:
-            child.blender_export()
-            
-        level[0] -= 1
-
-    # Exports this node's info to file.
-    def write(self):
-        pass
-
-    def write_matrix(self):
-        if self.matrix and not is_identity(self.matrix):
-            self.header.fw.write_short(49)   # Matrix opcode
-            self.header.fw.write_ushort(68)  # Length of record
-            for i in range(4):
-                for j in range(4):
-                    self.header.fw.write_float(self.matrix[i][j])
-
-    def write_push(self):
-        self.header.fw.write_short(10)
-        self.header.fw.write_ushort(4)
-
-    def write_pop(self):
-        self.header.fw.write_short(11)
-        self.header.fw.write_ushort(4)
-
-    def write_longid(self, name):
-        length = len(name)
-        if length >= 8:
-            self.header.fw.write_short(33)              # Long ID opcode
-            self.header.fw.write_ushort(length+5)       # Length of record
-            self.header.fw.write_string(name, length+1) # name + zero terminator
-
-    # Initialization sets up basic tree structure.
-    def __init__(self, parent, header, object, object_lst):
-        self.header = header
-        self.object = object
-        if object:
-            self.name = self.object.getName()
-            self.matrix = self.object.getMatrix('localspace')
-        else:
-            self.name = 'no name'
-            self.matrix = None
-
-        self.children = []
-        self.parent = parent
-        if parent:
-            parent.children.append(self)
-
-        left_over = object_lst[:]
-        self.child_objects = []
-
-        # Add children to child list and remove from left_over list.
-        for obj in object_lst:
-            if obj.getParent() == object:
-                self.child_objects.append(obj)
-                left_over.remove(obj)
-
-        # Spawn children.
-        self.has_object_child = False # For Database class.
-        for child in self.child_objects:
-            dat = child.getData()
-            t = type(dat)
-
-            if dat == None:
-                BlenderEmpty(self, header, child, left_over)
-            if t == Blender.Types.NMeshType:
-                BlenderMesh(self, header, child, left_over)
-                self.has_object_child = True
+       # Gathers info from blender needed for export.
+       # The =[0] is a trick to emulate c-like static function variables
+       # that are persistant between calls.
+       def blender_export(self, level=[0]):
+               if self.object:
+                       if options.verbose >= 2:
+                               print '\t' * level[0], self.name, self.object.getType()
+
+               level[0] += 1
+               
+               for child in self.children:
+                       child.blender_export()
+                       
+               level[0] -= 1
+
+       # Exports this node's info to file.
+       def write(self):
+               pass
+
+       def write_matrix(self):
+               if self.matrix and not is_identity(self.matrix):
+                       self.header.fw.write_short(49)   # Matrix opcode
+                       self.header.fw.write_ushort(68)  # Length of record
+                       for i in xrange(4):
+                               for j in xrange(4):
+                                       self.header.fw.write_float(self.matrix[i][j])
+
+       def write_push(self):
+               self.header.fw.write_short(10)
+               self.header.fw.write_ushort(4)
+
+       def write_pop(self):
+               self.header.fw.write_short(11)
+               self.header.fw.write_ushort(4)
+
+       def write_longid(self, name):
+               length = len(name)
+               if length >= 8:
+                       self.header.fw.write_short(33)              # Long ID opcode
+                       self.header.fw.write_ushort(length+5)       # Length of record
+                       self.header.fw.write_string(name, length+1) # name + zero terminator
+
+       # Initialization sets up basic tree structure.
+       def __init__(self, parent, header, object, object_lst):
+               self.header = header
+               self.object = object
+               if object:
+                       self.name = self.object.getName()
+                       self.matrix = self.object.getMatrix('localspace')
+               else:
+                       self.name = 'no name'
+                       self.matrix = None
+
+               self.children = []
+               self.parent = parent
+               if parent:
+                       parent.children.append(self)
+
+               left_over = object_lst[:]
+               self.child_objects = []
+
+               # Add children to child list and remove from left_over list.
+               
+               # Pop is faster then remove
+               i = len(object_lst)
+               while i:
+                       i-=1
+                       if object_lst[i].parent == object:
+                               self.child_objects.append(left_over.pop(i))
+                       
+               # Spawn children.
+               self.has_object_child = False # For Database class.
+               for child in self.child_objects:                        
+                       if child.getType() == 'Mesh':
+                               BlenderMesh(self, header, child, left_over)
+                               self.has_object_child = True
+                       else: # Treat all non meshes as emptys
+                               BlenderEmpty(self, header, child, left_over)
 
 class FaceDesc:
-    def __init__(self):
-        self.vertex_index_lst = []
-        self.texture_index = -1
-        self.material_index = -1
-        self.color_index = 127
-    
+       def __init__(self):
+               self.vertex_index_lst = []
+               self.texture_index = -1
+               self.material_index = -1
+               self.color_index = 127
+       
 class BlenderMesh(Node):
-    def blender_export(self):
-        Node.blender_export(self)
-
-        mesh = self.object.getData()
-
-        # Gather materials and textures.
-        tex_index_lst = []
-        mat_index_lst = []
-        color_index_lst = []
-        materials = mesh.getMaterials()
-        
-        if materials == []:
-            materials = [Blender.Material.New()]
-        
-        for mat in materials:
-            # Gather Color.
-            if options.use_face_color:
-                color_index_lst.append(self.header.GRR.request_color_index(mat.getRGBCol()))
-            else:
-                color_index_lst.append(127) # white
-            # Gather Texture.
-            mtex_lst = mat.getTextures()
-
-            index = -1
-            mtex = mtex_lst[0] # Not doing multi-texturing at the moment.
-            if mtex != None:
-                tex = mtex_lst[0].tex
-                if tex != None:
-                    image = tex.getImage()
-                    if image != None:
-                        filename = image.getFilename()
-                        index = self.header.GRR.request_texture_index(filename)
-
-            tex_index_lst.append(index)
-
-            # Gather Material
-            mat_desc = MaterialDesc()
-            mat_desc.name = mat.getName()
-            mat_desc.alpha = mat.getAlpha()
-            mat_desc.shininess = mat.getSpec() * 64.0   # 2.0 => 128.0
-            if options.use_mat_color:
-                mat_desc.diffuse = mat.getRGBCol()
-            else:
-                mat_desc.diffuse = [1.0, 1.0, 1.0]
-
-            mat_desc.specular = mat.getSpecCol()
-            amb = mat.getAmb()
-            mat_desc.ambient = [amb, amb, amb]
-            emit = mat.getEmit()
-            mat_desc.emissive = [emit, emit, emit]
-
-            mat_index_lst.append(self.header.GRR.request_material_index(mat_desc))
-
-        # Faces described as lists of indices into the GRR's vertex_lst.
-        for face in mesh.faces:
-            # Create vertex description list for each face.
-            vertex_lst = []
-            for vertex in face.v:
-                vert_desc = VertexDesc()
-                vert_desc.x = vertex.co[0]
-                vert_desc.y = vertex.co[1]
-                vert_desc.z = vertex.co[2]
-                vert_desc.nx = vertex.no[0]
-                vert_desc.ny = vertex.no[1]
-                vert_desc.nz = vertex.no[2]
-                vertex_lst.append(vert_desc)
-
-            for j in range( min(len(face.uv),len(vertex_lst)) ):
-                vertex_lst[j].u = face.uv[j][0]
-                vertex_lst[j].v = face.uv[j][1]
-
-            index_lst = []
-            for vert_desc in vertex_lst:
-                index_lst.append(self.header.GRR.request_vertex_index(vert_desc))
-            
-            face_desc = FaceDesc()
-            face_desc.vertex_index_lst = index_lst
-            
-            if face.materialIndex < len(materials):
-                face_desc.color_index = color_index_lst[face.materialIndex]
-                face_desc.texture_index = tex_index_lst[face.materialIndex]
-                face_desc.material_index = mat_index_lst[face.materialIndex]
-            else:
-                if options.verbose >=1:
-                    print 'Warning: Missing material for material index. Materials will not be imported correctly. Fix by deleting abandoned material indices in Blender.'
-
-            self.face_lst.append(face_desc)
-            
-            # Export double sided face as 2 faces with opposite orientations.
-            if mesh.hasFaceUV() and face.mode & Blender.NMesh.FaceModes['TWOSIDE']:
-                # Create vertex description list for each face.
-                vertex_lst = []
-                for vertex in face.v:
-                    vert_desc = VertexDesc()
-                    vert_desc.x = vertex.co[0]
-                    vert_desc.y = vertex.co[1]
-                    vert_desc.z = vertex.co[2]
-                    vert_desc.nx = -vertex.no[0]
-                    vert_desc.ny = -vertex.no[1]
-                    vert_desc.nz = -vertex.no[2]
-                    vertex_lst.append(vert_desc)
-    
-                for j in range( min(len(face.uv),len(vertex_lst)) ):
-                    vertex_lst[j].u = face.uv[j][0]
-                    vertex_lst[j].v = face.uv[j][1]
-    
-                vertex_lst.reverse()
-                
-                index_lst = []
-                for vert_desc in vertex_lst:
-                    index_lst.append(self.header.GRR.request_vertex_index(vert_desc))
-                
-                face_desc = FaceDesc()
-                face_desc.vertex_index_lst = index_lst
-                if face.materialIndex < len(materials):
-                    face_desc.color_index = color_index_lst[face.materialIndex]
-                    face_desc.texture_index = tex_index_lst[face.materialIndex]
-                    face_desc.material_index = mat_index_lst[face.materialIndex]
-                else:
-                    if options.verbose >=1:
-                        print 'Error: No material for material index. Delete abandoned material indices in Blender.'
-    
-                self.face_lst.append(face_desc)
-
-    def write_faces(self):
-        for face_desc in self.face_lst:
-            face_name = self.header.GRR.new_face_name()
-            
-            self.header.fw.write_short(5)                                   # Face opcode
-            self.header.fw.write_ushort(80)                                 # Length of record
-            self.header.fw.write_string(face_name, 8)                       # ASCII ID
-            self.header.fw.write_int(-1)                                    # IR color code
-            self.header.fw.write_short(0)                                   # Relative priority
-            self.header.fw.write_char(0)                                    # Draw type
-            self.header.fw.write_char(0)                                    # Draw textured white.
-            self.header.fw.write_ushort(0)                                  # Color name index
-            self.header.fw.write_ushort(0)                                  # Alt color name index
-            self.header.fw.write_char(0)                                    # Reserved
-            self.header.fw.write_char(1)                                    # Template
-            self.header.fw.write_short(-1)                                  # Detail tex pat index
-            self.header.fw.write_short(face_desc.texture_index)             # Tex pattern index
-            self.header.fw.write_short(face_desc.material_index)            # material index
-            self.header.fw.write_short(0)                                   # SMC code
-            self.header.fw.write_short(0)                                   # Feature code
-            self.header.fw.write_int(0)                                     # IR material code
-            self.header.fw.write_ushort(0)                                  # transparency 0 = opaque
-            self.header.fw.write_uchar(0)                                   # LOD generation control
-            self.header.fw.write_uchar(0)                                   # line style index
-            self.header.fw.write_int(0x00000000)                            # Flags
-            self.header.fw.write_uchar(2)                                   # Light mode
-            self.header.fw.pad(7)                                           # Reserved
-            self.header.fw.write_uint(-1)                                   # Packed color
-            self.header.fw.write_uint(-1)                                   # Packed alt color
-            self.header.fw.write_short(-1)                                  # Tex map index
-            self.header.fw.write_short(0)                                   # Reserved
-            self.header.fw.write_uint(face_desc.color_index)                # Color index
-            self.header.fw.write_uint(127)                                  # Alt color index
-            self.header.fw.write_short(0)                                   # Reserved
-            self.header.fw.write_short(-1)                                  # Shader index
-
-            self.write_longid(face_name)
-
-            self.write_push()
-
-            # Vertex list record
-            self.header.fw.write_short(72)                        # Vertex list opcode
-            num_verts = len(face_desc.vertex_index_lst)
-            self.header.fw.write_ushort(4*num_verts+4)            # Length of record
-
-            for vert_index in face_desc.vertex_index_lst:
-                # Offset into vertex palette
-                self.header.fw.write_int(vert_index*64+8)
-
-            self.write_pop()
-
-    def write(self):
-        if self.open_flight_type == 'Object':
-            self.header.fw.write_short(4)               # Object opcode
-            self.header.fw.write_ushort(28)             # Length of record
-            self.header.fw.write_string(self.name, 8)   # ASCII ID
-            self.header.fw.pad(16)
-    
-            self.write_longid(self.name)
-            
-            self.write_matrix()
-            
-            if self.face_lst != []:
-                self.write_push()
-        
-                self.write_faces()
-        
-                self.write_pop()
-        else:
-            self.header.fw.write_short(2)               # Group opcode
-            self.header.fw.write_ushort(44)             # Length of record
-            self.header.fw.write_string(self.name, 8)   # ASCII ID
-            self.header.fw.pad(32)
-    
-            self.write_longid(self.name)
-            
-            # Because a group can contain faces as well as children.
-            self.write_push() 
-            
-            self.write_faces()
-            
-            for child in self.children:
-                child.write()
-    
-            self.write_pop()
-            
-
-    def __init__(self, parent, header, object, object_lst):
-        Node.__init__(self, parent, header, object, object_lst)
-        self.face_lst = []
-        
-        if self.children == []:
-            self.open_flight_type = 'Object'
-        else:
-            self.open_flight_type= 'Group'
+       def blender_export(self):
+               Node.blender_export(self)
+
+               mesh = self.object.getData()
+               mesh_hasuv = mesh.hasFaceUV()
+               # Gather materials and textures.
+               tex_index_lst = []
+               mat_index_lst = []
+               color_index_lst = []
+               materials = mesh.getMaterials()
+               
+               if not materials:
+                       materials = [Blender.Material.New()]
+               
+               for mat in materials:
+                       # Gather Color.
+                       if options.use_face_color:
+                               color_index_lst.append(self.header.GRR.request_color_index(mat.getRGBCol()))
+                       else:
+                               color_index_lst.append(127) # white
+                       # Gather Texture.
+                       mtex_lst = mat.getTextures()
+
+                       index = -1
+                       mtex = mtex_lst[0] # Not doing multi-texturing at the moment.
+                       if mtex != None:
+                               tex = mtex_lst[0].tex
+                               if tex != None:
+                                       image = tex.getImage()
+                                       if image != None:
+                                               filename = image.getFilename()
+                                               index = self.header.GRR.request_texture_index(filename)
+
+                       tex_index_lst.append(index)
+
+                       # Gather Material
+                       mat_desc = MaterialDesc()
+                       mat_desc.name = mat.getName()
+                       mat_desc.alpha = mat.getAlpha()
+                       mat_desc.shininess = mat.getSpec() * 64.0   # 2.0 => 128.0
+                       if options.use_mat_color:
+                               mat_desc.diffuse = mat.getRGBCol()
+                       else:
+                               mat_desc.diffuse = [1.0, 1.0, 1.0]
+
+                       mat_desc.specular = mat.getSpecCol()
+                       amb = mat.getAmb()
+                       mat_desc.ambient = [amb, amb, amb]
+                       emit = mat.getEmit()
+                       mat_desc.emissive = [emit, emit, emit]
+
+                       mat_index_lst.append(self.header.GRR.request_material_index(mat_desc))
+
+               # Faces described as lists of indices into the GRR's vertex_lst.
+               for face in mesh.faces:
+                       
+                       face_v = face.v # Faster access
+                       
+                       # Create vertex description list for each face.
+                       if mesh_hasuv:
+                               vertex_lst = [VertexDesc(v.co, v.no, face.uv[i]) for i, v in enumerate(face_v)]
+                       else:
+                               vertex_lst = [VertexDesc(v.co, v.no) for i, v in enumerate(face_v)]
+                       
+                       index_lst = []
+                       for vert_desc in vertex_lst:
+                               index_lst.append(self.header.GRR.request_vertex_index(vert_desc))
+                       
+                       face_desc = FaceDesc()
+                       face_desc.vertex_index_lst = index_lst
+                       
+                       if face.materialIndex < len(materials):
+                               face_desc.color_index    = color_index_lst[face.materialIndex]
+                               face_desc.texture_index  = tex_index_lst[face.materialIndex]
+                               face_desc.material_index = mat_index_lst[face.materialIndex]
+                       else:
+                               if options.verbose >=1:
+                                       print 'Warning: Missing material for material index. Materials will not be imported correctly. Fix by deleting abandoned material indices in Blender.'
+
+                       self.face_lst.append(face_desc)
+                       
+                       # Export double sided face as 2 faces with opposite orientations.
+                       if mesh_hasuv and face.mode & Blender.NMesh.FaceModes['TWOSIDE']:
+                               # Create vertex description list for each face. they have a face mode, so we know they have a UV too.
+                               vertex_lst = [VertexDesc(v.co, -v.no, face.uv[i]) for i, v in enumerate(face_v)]
+                               vertex_lst.reverse() # Reversing flips the face.
+                               
+                               index_lst = []
+                               for vert_desc in vertex_lst:
+                                       index_lst.append(self.header.GRR.request_vertex_index(vert_desc))
+                               
+                               face_desc = FaceDesc()
+                               face_desc.vertex_index_lst = index_lst
+                               if face.materialIndex < len(materials):
+                                       face_desc.color_index = color_index_lst[face.materialIndex]
+                                       face_desc.texture_index = tex_index_lst[face.materialIndex]
+                                       face_desc.material_index = mat_index_lst[face.materialIndex]
+                               else:
+                                       if options.verbose >=1:
+                                               print 'Error: No material for material index. Delete abandoned material indices in Blender.'
+       
+                               self.face_lst.append(face_desc)
+
+       def write_faces(self):
+               for face_desc in self.face_lst:
+                       face_name = self.header.GRR.new_face_name()
+                       
+                       self.header.fw.write_short(5)                                   # Face opcode
+                       self.header.fw.write_ushort(80)                                 # Length of record
+                       self.header.fw.write_string(face_name, 8)                       # ASCII ID
+                       self.header.fw.write_int(-1)                                    # IR color code
+                       self.header.fw.write_short(0)                                   # Relative priority
+                       self.header.fw.write_char(0)                                    # Draw type
+                       self.header.fw.write_char(0)                                    # Draw textured white.
+                       self.header.fw.write_ushort(0)                                  # Color name index
+                       self.header.fw.write_ushort(0)                                  # Alt color name index
+                       self.header.fw.write_char(0)                                    # Reserved
+                       self.header.fw.write_char(1)                                    # Template
+                       self.header.fw.write_short(-1)                                  # Detail tex pat index
+                       self.header.fw.write_short(face_desc.texture_index)             # Tex pattern index
+                       self.header.fw.write_short(face_desc.material_index)            # material index
+                       self.header.fw.write_short(0)                                   # SMC code
+                       self.header.fw.write_short(0)                                   # Feature code
+                       self.header.fw.write_int(0)                                     # IR material code
+                       self.header.fw.write_ushort(0)                                  # transparency 0 = opaque
+                       self.header.fw.write_uchar(0)                                   # LOD generation control
+                       self.header.fw.write_uchar(0)                                   # line style index
+                       self.header.fw.write_int(0x00000000)                            # Flags
+                       self.header.fw.write_uchar(2)                                   # Light mode
+                       self.header.fw.pad(7)                                           # Reserved
+                       self.header.fw.write_uint(-1)                                   # Packed color
+                       self.header.fw.write_uint(-1)                                   # Packed alt color
+                       self.header.fw.write_short(-1)                                  # Tex map index
+                       self.header.fw.write_short(0)                                   # Reserved
+                       self.header.fw.write_uint(face_desc.color_index)                # Color index
+                       self.header.fw.write_uint(127)                                  # Alt color index
+                       self.header.fw.write_short(0)                                   # Reserved
+                       self.header.fw.write_short(-1)                                  # Shader index
+
+                       self.write_longid(face_name)
+
+                       self.write_push()
+
+                       # Vertex list record
+                       self.header.fw.write_short(72)                        # Vertex list opcode
+                       num_verts = len(face_desc.vertex_index_lst)
+                       self.header.fw.write_ushort(4*num_verts+4)            # Length of record
+
+                       for vert_index in face_desc.vertex_index_lst:
+                               # Offset into vertex palette
+                               self.header.fw.write_int(vert_index*64+8)
+
+                       self.write_pop()
+
+       def write(self):
+               if self.open_flight_type == 'Object':
+                       self.header.fw.write_short(4)               # Object opcode
+                       self.header.fw.write_ushort(28)             # Length of record
+                       self.header.fw.write_string(self.name, 8)   # ASCII ID
+                       self.header.fw.pad(16)
+       
+                       self.write_longid(self.name)
+                       
+                       self.write_matrix()
+                       
+                       if self.face_lst != []:
+                               self.write_push()
+               
+                               self.write_faces()
+               
+                               self.write_pop()
+               else:
+                       self.header.fw.write_short(2)               # Group opcode
+                       self.header.fw.write_ushort(44)             # Length of record
+                       self.header.fw.write_string(self.name, 8)   # ASCII ID
+                       self.header.fw.pad(32)
+       
+                       self.write_longid(self.name)
+                       
+                       # Because a group can contain faces as well as children.
+                       self.write_push() 
+                       
+                       self.write_faces()
+                       
+                       for child in self.children:
+                               child.write()
+                       
+                       self.write_pop()
+
+       def __init__(self, parent, header, object, object_lst):
+               Node.__init__(self, parent, header, object, object_lst)
+               self.face_lst = []
+               
+               if self.children:
+                       self.open_flight_type= 'Group'
+               else: # Empty list.
+                       self.open_flight_type = 'Object'
+                       
 
 class BlenderEmpty(Node):
-    def write(self):
-        self.header.fw.write_short(2)               # Group opcode
-        self.header.fw.write_ushort(44)             # Length of record
-        self.header.fw.write_string(self.name, 8)   # ASCII ID
-        self.header.fw.pad(32)
-
-        self.write_longid(self.name)
-        
-        self.write_matrix()
-        
-        if self.children != []:
-            self.write_push()
-    
-            for child in self.children:
-                child.write()
-                
-            self.write_pop()
+       def write(self):
+               self.header.fw.write_short(2)               # Group opcode
+               self.header.fw.write_ushort(44)             # Length of record
+               self.header.fw.write_string(self.name, 8)   # ASCII ID
+               self.header.fw.pad(32)
+
+               self.write_longid(self.name)
+               
+               self.write_matrix()
+               
+               if self.children: # != []
+                       self.write_push()
+       
+                       for child in self.children:
+                               child.write()
+                               
+                       self.write_pop()
 
 class Database(Node):
-    def write_header(self):
-        if options.verbose >= 2:
-            print 'Writing header.'
-        self.fw.write_short(1)          # Header opcode
-        self.fw.write_ushort(324)       # Length of record
-        self.fw.write_string('db', 8)   # ASCII ID
-        self.fw.write_int(1600)         # Revision Number
-        self.fw.pad(44)
-        self.fw.write_short(1)          # Unit multiplier.
-        self.fw.write_char(0)           # Units, 0 = meters
-        self.fw.write_char(0)           # texwhite on new faces 0 = false
-        self.fw.write_uint(0x80000000)  # misc flags set to saving vertex normals
-        self.fw.pad(24)
-        self.fw.write_int(0)            # projection type, 0 = flat earth
-        self.fw.pad(30)
-        self.fw.write_short(1)          # double precision
-        self.fw.pad(140)
-        self.fw.write_int(0)            # ellipsoid model, 0 = WSG 1984
-        self.fw.pad(52)
-
-    def write_vert_pal(self):
-        if options.verbose >= 2:
-            print 'Writing vertex palette.'
-        # Write record for vertex palette
-        self.fw.write_short(67)                             # Vertex palette opcode.
-        self.fw.write_short(8)                              # Length of record
-        self.fw.write_int(self.GRR.vertex_count() * 64 + 8) # Length of everything.
-
-        # Write records for individual vertices.
-        for i in range(self.GRR.vertex_count()):
-            desc = self.GRR.request_vertex_desc(i)
-            self.fw.write_short(70)                         # Vertex with color normal and uv opcode.
-            self.fw.write_ushort(64)                        # Length of record
-            self.fw.write_ushort(0)                         # Color name index
-            self.fw.write_short(0x2000)                     # Flags set to no color
-            self.fw.write_double(desc.x)
-            self.fw.write_double(desc.y)
-            self.fw.write_double(desc.z)
-            self.fw.write_float(desc.nx)
-            self.fw.write_float(desc.ny)
-            self.fw.write_float(desc.nz)
-            self.fw.write_float(desc.u)
-            self.fw.write_float(desc.v)
-            self.fw.pad(12)
-
-    def write_tex_pal(self):
-        if options.verbose >= 2:
-            print 'Writing texture palette.'
-        # Write record for texture palette
-        for i in range(self.GRR.texture_count()):
-            self.fw.write_short(64)                                         # Texture palette opcode.
-            self.fw.write_short(216)                                        # Length of record
-            self.fw.write_string(self.GRR.request_texture_filename(i), 200) # Filename
-            self.fw.write_int(i)                                            # Texture index
-            self.fw.write_int(0)                                            # X
-            self.fw.write_int(0)                                            # Y
-
-    def write_mat_pal(self):
-        if options.verbose >= 2:
-            print 'Writing material palette.'
-        for i in range(self.GRR.material_count()):
-            desc = self.GRR.request_material_desc(i)
-            self.fw.write_short(113)                # Material palette opcode.
-            self.fw.write_short(84)                 # Length of record
-            self.fw.write_int(i)                    # Material index
-            self.fw.write_string(desc.name, 12)     # Material name
-            self.fw.write_uint(0x80000000)          # Flags
-            self.fw.write_float(desc.ambient[0])    # Ambient color.
-            self.fw.write_float(desc.ambient[1])    # Ambient color.
-            self.fw.write_float(desc.ambient[2])    # Ambient color.
-            self.fw.write_float(desc.diffuse[0])    # Diffuse color.
-            self.fw.write_float(desc.diffuse[1])    # Diffuse color.
-            self.fw.write_float(desc.diffuse[2])    # Diffuse color.
-            self.fw.write_float(desc.specular[0])   # Specular color.
-            self.fw.write_float(desc.specular[1])   # Specular color.
-            self.fw.write_float(desc.specular[2])   # Specular color.
-            self.fw.write_float(desc.emissive[0])   # Emissive color.
-            self.fw.write_float(desc.emissive[1])   # Emissive color.
-            self.fw.write_float(desc.emissive[2])   # Emissive color.
-            self.fw.write_float(desc.shininess)
-            self.fw.write_float(desc.alpha)
-            self.fw.write_int(0)                    # Reserved
-
-    def write_col_pal(self):
-        if options.verbose >= 2:
-            print 'Writing color palette.'
-        self.fw.write_short(32)                     # Color palette opcode.
-        self.fw.write_short(4228)                   # Length of record
-        self.fw.pad(128)
-        count = self.GRR.color_count()
-        for i in range(count):
-            col = self.GRR.request_max_color(i)
-            self.fw.write_uchar(255)                  # alpha
-            self.fw.write_uchar(col[2])               # b
-            self.fw.write_uchar(col[1])               # g
-            self.fw.write_uchar(col[0])               # r
-        self.fw.pad(max(4096-count*4, 0))
-
-    def write(self):
-        self.write_header()
-        self.write_vert_pal()
-        self.write_tex_pal()
-        self.write_mat_pal()
-        self.write_col_pal()
-
-        # Wrap everything in a group if it has an object child.
-        if self.has_object_child:
-            self.header.fw.write_short(2)          # Group opcode
-            self.header.fw.write_ushort(44)        # Length of record
-            self.header.fw.write_string('g1', 8)   # ASCII ID
-            self.header.fw.pad(32)
-        
-        self.write_push()
-
-        for child in self.children:
-            child.write()
-
-        self.write_pop()
-
-    def __init__(self, scene, fw):
-        self.fw = fw
-        self.scene = scene
-        self.all_objects = scene.getChildren()
-        self.GRR = GlobalResourceRepository()
-
-        Node.__init__(self, None, self, None, self.all_objects)
+       def write_header(self):
+               if options.verbose >= 2:
+                       print 'Writing header.'
+               self.fw.write_short(1)          # Header opcode
+               self.fw.write_ushort(324)       # Length of record
+               self.fw.write_string('db', 8)   # ASCII ID
+               self.fw.write_int(1600)         # Revision Number
+               self.fw.pad(44)
+               self.fw.write_short(1)          # Unit multiplier.
+               self.fw.write_char(0)           # Units, 0 = meters
+               self.fw.write_char(0)           # texwhite on new faces 0 = false
+               self.fw.write_uint(0x80000000)  # misc flags set to saving vertex normals
+               self.fw.pad(24)
+               self.fw.write_int(0)            # projection type, 0 = flat earth
+               self.fw.pad(30)
+               self.fw.write_short(1)          # double precision
+               self.fw.pad(140)
+               self.fw.write_int(0)            # ellipsoid model, 0 = WSG 1984
+               self.fw.pad(52)
+
+       def write_vert_pal(self):
+               if options.verbose >= 2:
+                       print 'Writing vertex palette.'
+               # Write record for vertex palette
+               self.fw.write_short(67)                             # Vertex palette opcode.
+               self.fw.write_short(8)                              # Length of record
+               self.fw.write_int(self.GRR.vertex_count() * 64 + 8) # Length of everything.
+
+               # Write records for individual vertices.
+               for i in xrange(self.GRR.vertex_count()):
+                       desc = self.GRR.request_vertex_desc(i)
+                       self.fw.write_short(70)                         # Vertex with color normal and uv opcode.
+                       self.fw.write_ushort(64)                        # Length of record
+                       self.fw.write_ushort(0)                         # Color name index
+                       self.fw.write_short(0x2000)                     # Flags set to no color
+                       self.fw.write_double(desc.x)
+                       self.fw.write_double(desc.y)
+                       self.fw.write_double(desc.z)
+                       self.fw.write_float(desc.nx)
+                       self.fw.write_float(desc.ny)
+                       self.fw.write_float(desc.nz)
+                       self.fw.write_float(desc.u)
+                       self.fw.write_float(desc.v)
+                       self.fw.pad(12)
+
+       def write_tex_pal(self):
+               if options.verbose >= 2:
+                       print 'Writing texture palette.'
+               # Write record for texture palette
+               for i in xrange(self.GRR.texture_count()):
+                       self.fw.write_short(64)                                         # Texture palette opcode.
+                       self.fw.write_short(216)                                        # Length of record
+                       self.fw.write_string(self.GRR.request_texture_filename(i), 200) # Filename
+                       self.fw.write_int(i)                                            # Texture index
+                       self.fw.write_int(0)                                            # X
+                       self.fw.write_int(0)                                            # Y
+
+       def write_mat_pal(self):
+               if options.verbose >= 2:
+                       print 'Writing material palette.'
+               for i in xrange(self.GRR.material_count()):
+                       desc = self.GRR.request_material_desc(i)
+                       self.fw.write_short(113)                # Material palette opcode.
+                       self.fw.write_short(84)                 # Length of record
+                       self.fw.write_int(i)                    # Material index
+                       self.fw.write_string(desc.name, 12)     # Material name
+                       self.fw.write_uint(0x80000000)          # Flags
+                       self.fw.write_float(desc.ambient[0])    # Ambient color.
+                       self.fw.write_float(desc.ambient[1])    # Ambient color.
+                       self.fw.write_float(desc.ambient[2])    # Ambient color.
+                       self.fw.write_float(desc.diffuse[0])    # Diffuse color.
+                       self.fw.write_float(desc.diffuse[1])    # Diffuse color.
+                       self.fw.write_float(desc.diffuse[2])    # Diffuse color.
+                       self.fw.write_float(desc.specular[0])   # Specular color.
+                       self.fw.write_float(desc.specular[1])   # Specular color.
+                       self.fw.write_float(desc.specular[2])   # Specular color.
+                       self.fw.write_float(desc.emissive[0])   # Emissive color.
+                       self.fw.write_float(desc.emissive[1])   # Emissive color.
+                       self.fw.write_float(desc.emissive[2])   # Emissive color.
+                       self.fw.write_float(desc.shininess)
+                       self.fw.write_float(desc.alpha)
+                       self.fw.write_int(0)                    # Reserved
+
+       def write_col_pal(self):
+               if options.verbose >= 2:
+                       print 'Writing color palette.'
+               self.fw.write_short(32)                     # Color palette opcode.
+               self.fw.write_short(4228)                   # Length of record
+               self.fw.pad(128)
+               count = self.GRR.color_count()
+               for i in xrange(count):
+                       col = self.GRR.request_max_color(i)
+                       self.fw.write_uchar(255)                  # alpha
+                       self.fw.write_uchar(col[2])               # b
+                       self.fw.write_uchar(col[1])               # g
+                       self.fw.write_uchar(col[0])               # r
+               self.fw.pad(max(4096-count*4, 0))
+
+       def write(self):
+               self.write_header()
+               self.write_vert_pal()
+               self.write_tex_pal()
+               self.write_mat_pal()
+               self.write_col_pal()
+
+               # Wrap everything in a group if it has an object child.
+               if self.has_object_child:
+                       self.header.fw.write_short(2)          # Group opcode
+                       self.header.fw.write_ushort(44)        # Length of record
+                       self.header.fw.write_string('g1', 8)   # ASCII ID
+                       self.header.fw.pad(32)
+               
+               self.write_push()
+
+               for child in self.children:
+                       child.write()
+
+               self.write_pop()
+
+       def __init__(self, scene, fw):
+               self.fw = fw
+               self.scene = scene
+               self.all_objects = scene.getChildren()
+               self.GRR = GlobalResourceRepository()
+
+               Node.__init__(self, None, self, None, self.all_objects)
 
 def fs_callback(filename):
-    Blender.Window.WaitCursor(True)
-    
-    if Blender.sys.exists(filename):
-        r = Blender.Draw.PupMenu('Overwrite ' + filename + '?%t|Yes|No')
-        if r != 1:
-            if options.verbose >= 1:
-                print 'Export cancelled.'
-            return
-    
-    fw = FltOut(filename)
-
-    db = Database(Blender.Scene.GetCurrent(), fw)
-    
-    if options.verbose >= 1:
-        print
-        print 'Pass 1: Exporting from Blender.'
-        print
-    
-    db.blender_export()
-    
-    if options.verbose >= 1:
-        print
-        print 'Pass 2: Writing', filename
-        print
-        
-    db.write()
-
-    fw.close_file()
-    if options.verbose >= 1:
-        print
-        print 'Done.'
-        
-    Blender.Window.WaitCursor(False)
-   
+       Blender.Window.WaitCursor(True)
+       
+       if Blender.sys.exists(filename):
+               r = Blender.Draw.PupMenu('Overwrite ' + filename + '?%t|Yes|No')
+               if r != 1:
+                       if options.verbose >= 1:
+                               print 'Export cancelled.'
+                       return
+       
+       time1 = Blender.sys.time() # Start timing
+       
+       fw = FltOut(filename)
+
+       db = Database(Blender.Scene.GetCurrent(), fw)
+       
+       if options.verbose >= 1:
+               print 'Pass 1: Exporting from Blender.\n'
+       
+       db.blender_export()
+       
+       if options.verbose >= 1:
+               print 'Pass 2: Writing %s\n' % filename
+               
+       db.write()
+
+       fw.close_file()
+       if options.verbose >= 1:
+               print 'Done in %.4f sec.\n' % (Blender.sys.time() - time1)
+               
+       Blender.Window.WaitCursor(False)
+
 if options.verbose >= 1:
-    print
-    print 'OpenFlight Exporter'
-    print 'Version:', __version__
-    print 'Author: Greg MacDonald'
-    print __url__[2]
-    print
-    
+       print '\nOpenFlight Exporter'
+       print 'Version:', __version__
+       print 'Author: Greg MacDonald'
+       print __url__[2]
+       print
+       
 fname = Blender.sys.makename(ext=".flt")
 Blender.Window.FileSelector(fs_callback, "Export OpenFlight v16.0", fname)