#!BPY """ Registration info for Blender menus: Name: 'OpenFlight (.flt)...' Blender: 245 Group: 'Import' Tip: 'Import OpenFlight (.flt)' """ __author__ = "Greg MacDonald, Campbell Barton, Geoffrey Bantle" __version__ = "2.0 11/21/07" __url__ = ("blender", "elysiun", "Author's homepage, http://sourceforge.net/projects/blight/") __bpydoc__ = """\ This script imports OpenFlight files into Blender. OpenFlight is a registered trademark of MultiGen-Paradigm, Inc. Feature overview and more availible at: http://wiki.blender.org/index.php/Scripts/Manual/Import/openflight_flt Note: This file is a grab-bag of old and new code. It needs some cleanup still. """ # flt_import.py is an OpenFlight importer for blender. # Copyright (C) 2005 Greg MacDonald, 2007 Blender Foundation # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. import Blender import os import BPyMesh import BPyImage import flt_filewalker import flt_properties reload(flt_properties) from flt_properties import * #Globals. Should Clean these up and minimize their usage. typecodes = ['c','C','s','S','i','I','f','d','t'] records = dict() FLTBaseLabel = None FLTBaseString = None FLTBaseChooser = None FLTExport = None FLTClose = None FLTDoXRef = None FLTScale = None FLTShadeImport = None FLTAttrib = None Vector= Blender.Mathutils.Vector FLOAT_TOLERANCE = 0.01 FF = flt_filewalker.FileFinder() current_layer = 0x01 global_prefs = dict() global_prefs['verbose']= 4 global_prefs['get_texture'] = True global_prefs['get_diffuse'] = True global_prefs['get_specular'] = False global_prefs['get_emissive'] = False global_prefs['get_alpha'] = True global_prefs['get_ambient'] = False global_prefs['get_shininess'] = True global_prefs['color_from_face'] = True global_prefs['fltfile']= '' global_prefs['smoothshading'] = 1 global_prefs['doxrefs'] = 1 global_prefs['scale'] = 1.0 global_prefs['attrib'] = 0 msg_once = False throw_back_opcodes = [2, 73, 4, 11, 96, 14, 91, 98, 63,111] # Opcodes that indicate its time to return control to parent. 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] #Process FLT record definitions for record in FLT_Records: props = dict() for prop in FLT_Records[record]: position = '' slice = 0 (format,name) = prop.split('!') for i in format: if i not in typecodes: position = position + i slice = slice + 1 else: break type = format[slice:] length = type[1:] if len(length) == 0: length = 1 else: type = type[0] length = int(length) props[int(position)] = (type,length,prop) records[record] = props def col_to_gray(c): return 0.3*c[0] + 0.59*c[1] + 0.11*c[2] class MaterialDesc: # Was going to use int(f*1000.0) instead of round(f,3), but for some reason # round produces better results, as in less dups. def make_key(self): key = list() if global_prefs['get_texture']: if self.tex0: key.append(self.tex0.getName()) else: key.append(None) if global_prefs['get_alpha']: key.append(round(self.alpha, 3)) else: key.append(None) if global_prefs['get_shininess']: key.append(round(self.shininess, 3)) else: key.append(None) if global_prefs['get_emissive']: key.append(round(self.emissive, 3)) else: key.append(None) if global_prefs['get_ambient']: key.append(round(self.ambient, 3)) else: key.append(None) if global_prefs['get_specular']: for n in self.specular: key.append(round(n, 3)) else: key.extend([None, None, None]) if global_prefs['get_diffuse']: for n in self.diffuse: key.append(round(n, 3)) else: key.extend([None, None, None]) # key.extend(self.face_props.values()) return tuple(key) def __init__(self): self.name = 'Material' # 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.0 # [0.0, 1.0] self.emissive = 0.0 # [0.0, 1.0] self.shininess = 0.5 # Range is [0.0, 2.0] self.alpha = 1.0 # Range is [0.0, 1.0] self.tex0 = None # OpenFlight Face attributes self.face_props = dict.fromkeys(['comment', 'ir color', 'priority', 'draw type', 'texture white', 'template billboard', 'smc', 'fid', 'ir material', 'lod generation control', 'flags', 'light mode']) class VertexDesc: def make_key(self): return round(self.x, 6), round(self.y, 6), round(self.z, 6) def __init__(self): # Assign later, save memory, all verts have a loc 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.uv= Vector(0,0) self.cindex = 127 #default/lowest self.cnorm = False class LightPointAppDesc: def make_key(self): d = dict(self.props) del d['id'] del d['type'] if d['directionality'] != 0: # not omni d['nx'] = 0.0 d['ny'] = 0.0 d['nz'] = 0.0 return tuple(d.values()) def __init__(self): self.props = dict() self.props.update({'type': 'LPA'}) self.props.update({'id': 'ap'}) # Attribs not found in inline lightpoint. self.props.update({'visibility range': 0.0}) self.props.update({'fade range ratio': 0.0}) self.props.update({'fade in duration': 0.0}) self.props.update({'fade out duration': 0.0}) self.props.update({'LOD range ratio': 0.0}) self.props.update({'LOD scale': 0.0}) class GlobalResourceRepository: def request_lightpoint_app(self, desc, scene): match = self.light_point_app.get(desc.make_key()) if match: return match.getName() else: # Create empty and fill with properties. name = desc.props['type'] + ': ' + desc.props['id'] object = Blender.Object.New('Empty', name) scene.objects.link(object) object.Layers= current_layer object.sel= 1 # Attach properties for name, value in desc.props.iteritems(): object.addProperty(name, value) self.light_point_app.update({desc.make_key(): object}) return object.getName() # Dont use request_vert - faster to make it from the vector direct. """ def request_vert(self, desc): match = self.vert_dict.get(desc.make_key()) if match: return match else: vert = Blender.Mathutils.Vector(desc.x, desc.y, desc.z) ''' IGNORE_NORMALS vert.no[0] = desc.nx vert.no[1] = desc.ny vert.no[2] = desc.nz ''' self.vert_dict.update({desc.make_key(): vert}) return vert """ def request_mat(self, mat_desc): match = self.mat_dict.get(mat_desc.make_key()) if match: return match mat = Blender.Material.New(mat_desc.name) if mat_desc.tex0 != None: mat.setTexture(0, mat_desc.tex0, Blender.Texture.TexCo.UV) mat.setAlpha(mat_desc.alpha) mat.setSpec(mat_desc.shininess) mat.setHardness(255) mat.setEmit(mat_desc.emissive) mat.setAmb(mat_desc.ambient) mat.setSpecCol(mat_desc.specular) mat.setRGBCol(mat_desc.diffuse) # Create a text object to store openflight face attribs until # user properties can be set on materials. # t = Blender.Text.New('FACE: ' + mat.getName()) # # for name, value in mat_desc.face_props.items(): # t.write(name + '\n' + str(value) + '\n\n') self.mat_dict.update({mat_desc.make_key(): mat}) return mat def request_image(self, filename_with_path): if not global_prefs['get_texture']: return None return BPyImage.comprehensiveImageLoad(filename_with_path, global_prefs['fltfile']) # Use join in case of spaces def request_texture(self, image): if not global_prefs['get_texture']: return None tex = self.tex_dict.get(image.filename) if tex: return tex tex = Blender.Texture.New(Blender.sys.basename(image.filename)) tex.setImage(image) tex.setType('Image') self.tex_dict.update({image.filename: tex}) return tex def __init__(self): #list of scenes xrefs belong to. self.xrefs = dict() # material self.mat_dict = dict() mat_lst = Blender.Material.Get() for mat in mat_lst: mat_desc = MaterialDesc() mapto_lst = mat.getTextures() if mapto_lst[0]: mat_desc.tex0 = mapto_lst[0].tex else: mat_desc.tex0 = None mat_desc.alpha = mat.getAlpha() mat_desc.shininess = mat.getSpec() mat_desc.emissive = mat.getEmit() mat_desc.ambient = mat.getAmb() mat_desc.specular = mat.getSpecCol() mat_desc.diffuse = mat.getRGBCol() self.mat_dict.update({mat_desc.make_key(): mat}) # texture self.tex_dict = dict() tex_lst = Blender.Texture.Get() for tex in tex_lst: img = tex.getImage() # Only interested in textures with images. if img: self.tex_dict.update({img.filename: tex}) # vertex # self.vert_dict = dict() # light point self.light_point_app = dict() class Handler: def in_throw_back_lst(self, opcode): return opcode in self.throw_back_lst def handle(self, opcode): return self.handler[opcode]() def handles(self, opcode): return opcode in self.handler.iterkeys() def throws_back_all_unhandled(self): return self.throw_back_unhandled def set_throw_back_lst(self, a): self.throw_back_lst = a def set_throw_back_all_unhandled(self): self.throw_back_unhandled = True def set_only_throw_back_specified(self): self.throw_back_unhandled = False def set_handler(self, d): self.handler = d def __init__(self): # Dictionary of opcodes to handler methods. self.handler = dict() # Send all opcodes not handled to the parent node. self.throw_back_unhandled = False # If throw_back_unhandled is False then only throw back # if the opcodes in throw_back are encountered. self.throw_back_lst = list() class Node: def blender_import(self): if self.opcode in opcode_name and global_prefs['verbose'] >= 2: for i in xrange(self.get_level()): print ' ', print opcode_name[self.opcode], print '-', self.props['id'], print '-', self.props['comment'], print for child in self.children: child.blender_import() # Import comment. # if self.props['comment'] != '': # name = 'COMMENT: ' + self.props['id'] # t = Blender.Text.New(name) # t.write(self.props['comment']) # self.props['comment'] = name # Always ignore extensions and anything in between them. def parse_push_extension(self): self.saved_handler = self.active_handler self.active_handler = self.extension_handler return True def parse_pop_extension(self): self.active_handler = self.saved_handler return True def parse_push(self): self.header.fw.up_level() # Ignore unknown children. self.ignore_unhandled = True # Don't do child records that might overwrite parent info. ex: longid self.active_handler = self.child_handler return True def parse_pop(self): self.header.fw.down_level() if self.header.fw.get_level() == self.level: return False return True def parse(self): while self.header.fw.begin_record(): opcode = self.header.fw.get_opcode() # Print out info on opcode and tree level. if global_prefs['verbose'] >= 3: p = '' for i in xrange(self.header.fw.get_level()): p = p + ' ' if opcode in opcode_name: p = p + opcode_name[opcode] else: if global_prefs['verbose'] >= 1: print 'undocumented opcode', opcode continue if self.global_handler.handles(opcode): if global_prefs['verbose'] >= 3: print p + ' handled globally' if self.global_handler.handle(opcode) == False: break elif self.active_handler.handles(opcode): if global_prefs['verbose'] >= 4: print p + ' handled' if self.active_handler.handle(opcode) == False: break else: if self.active_handler.throws_back_all_unhandled(): if global_prefs['verbose'] >= 3: print p + ' handled elsewhere' self.header.fw.repeat_record() break elif self.active_handler.in_throw_back_lst(opcode): if global_prefs['verbose'] >= 3: print p + ' handled elsewhere' self.header.fw.repeat_record() break else: if global_prefs['verbose'] >= 3: print p + ' ignored' elif global_prefs['verbose'] >= 1 and not opcode in do_not_report_opcodes and opcode in opcode_name: print 'not handled' def get_level(self): return self.level def parse_long_id(self): self.props['id'] = self.header.fw.read_string(self.header.fw.get_length()-4) return True def parse_comment(self): self.props['comment'] = self.header.fw.read_string(self.header.fw.get_length()-4) return True def parse_record(self): self.props['type'] = self.opcode props = records[self.opcode] propkeys = props.keys() propkeys.sort() for position in propkeys: (type,length,name) = props[position] self.props[name] = read_prop(self.header.fw,type,length) try: #remove me! self.props['id'] = self.props['3t8!id'] except: pass def __init__(self, parent, header): self.root_handler = Handler() self.child_handler = Handler() self.extension_handler = Handler() self.global_handler = Handler() self.global_handler.set_handler({21: self.parse_push_extension}) self.active_handler = self.root_handler # used by parse_*_extension self.extension_handler.set_handler({22: self.parse_pop_extension}) self.saved_handler = None self.header = header self.children = list() self.parent = parent if parent: parent.children.append(self) self.level = self.header.fw.get_level() self.opcode = self.header.fw.get_opcode() self.props = {'id': 'unnamed', 'comment': '', 'type': 'untyped'} class VertexPalette(Node): def __init__(self, parent): Node.__init__(self, parent, parent.header) self.root_handler.set_handler({68: self.parse_vertex_c, 69: self.parse_vertex_cn, 70: self.parse_vertex_cnuv, 71: self.parse_vertex_cuv}) self.root_handler.set_throw_back_all_unhandled() self.vert_desc_lst = list() self.blender_verts = list() self.offset = 8 # Used to create a map from byte offset to vertex index. self.index = dict() def blender_import(self): self.blender_verts.extend([Vector(vert_desc.x, vert_desc.y, vert_desc.z) for vert_desc in self.vert_desc_lst ]) def parse_vertex_common(self): # Add this vertex to an offset to index dictionary. #self.index_lst.append( (self.offset, self.next_index) ) self.index[self.offset]= len(self.index) # Get ready for next record. self.offset += self.header.fw.get_length() v = VertexDesc() self.header.fw.read_ahead(2) v.flags = self.header.fw.read_short() v.x = self.header.fw.read_double() v.y = self.header.fw.read_double() v.z = self.header.fw.read_double() return v def parse_vertex_post_common(self, v): #if not v.flags & 0x2000: # 0x2000 = no color #if v.flags & 0x1000: # 0x1000 = packed color # v.a = self.header.fw.read_uchar() # v.b = self.header.fw.read_uchar() # v.g = self.header.fw.read_uchar() # v.r = self.header.fw.read_uchar() #else: self.header.fw.read_ahead(4) #skip packed color v.cindex = self.header.fw.read_uint() self.vert_desc_lst.append(v) return True def parse_vertex_c(self): v = self.parse_vertex_common() self.parse_vertex_post_common(v) return True def parse_vertex_cn(self): v = self.parse_vertex_common() v.cnorm = True v.nx = self.header.fw.read_float() v.ny = self.header.fw.read_float() v.nz = self.header.fw.read_float() self.parse_vertex_post_common(v) return True def parse_vertex_cuv(self): v = self.parse_vertex_common() v.uv[:] = self.header.fw.read_float(), self.header.fw.read_float() self.parse_vertex_post_common(v) return True def parse_vertex_cnuv(self): v = self.parse_vertex_common() v.cnorm = True v.nx = self.header.fw.read_float() v.ny = self.header.fw.read_float() v.nz = self.header.fw.read_float() v.uv[:] = self.header.fw.read_float(), self.header.fw.read_float() self.parse_vertex_post_common(v) return True def parse(self): # Run once per import Node.parse(self) class InterNode(Node): def __init__(self): self.object = None self.mesh = None self.hasMesh = False self.faceLs= [] self.matrix = None self.vis = True self.hasmtex = False self.uvlayers = dict() self.blayernames = dict() self.subfacelevel = 0 mask = 2147483648 for i in xrange(7): self.uvlayers[mask] = False mask = mask / 2 def blender_import_my_faces(self): # Add the verts onto the mesh blender_verts= self.header.vert_pal.blender_verts vert_desc_lst= self.header.vert_pal.vert_desc_lst vert_list= [ i for flt_face in self.faceLs for i in flt_face.indices] #splitting faces apart. Is this a good thing? face_edges= [] face_verts= [] self.mesh.verts.extend([blender_verts[i] for i in vert_list]) new_faces= [] new_faces_props= [] ngon= BPyMesh.ngon vert_index= 1 #add vertex color layer for baked face colors. self.mesh.addColorLayer("FLT_Fcol") self.mesh.activeColorLayer = "FLT_Fcol" FLT_OrigIndex = 0 for flt_face in self.faceLs: if flt_face.tex_index != -1: try: image= self.header.tex_pal[flt_face.tex_index][1] except KeyError: image= None else: image= None face_len= len(flt_face.indices) #create dummy uvert dicts if len(flt_face.uverts) == 0: for i in xrange(face_len): flt_face.uverts.append(dict()) #May need to patch up MTex info if self.hasmtex: #For every layer in mesh, there should be corresponding layer in the face for mask in self.uvlayers.keys(): if self.uvlayers[mask]: if not flt_face.uvlayers.has_key(mask): #Does the face have this layer? #Create Layer info for this face flt_face.uvlayers[mask] = dict() flt_face.uvlayers[mask]['texture index'] = -1 flt_face.uvlayers[mask]['texture enviorment'] = 3 flt_face.uvlayers[mask]['texture mapping'] = 0 flt_face.uvlayers[mask]['texture data'] = 0 #now go through and create dummy uvs for this layer for uvert in flt_face.uverts: uv = Vector(0.0,0.0) uvert[mask] = uv # Get the indicies in reference to the mesh. uvs= [vert_desc_lst[j].uv for j in flt_face.indices] if face_len == 1: pass elif face_len == 2: face_edges.append((vert_index, vert_index+1)) elif flt_face.props['draw type'] == 2 or flt_face.props['draw type'] == 3: i = 0 while i < (face_len-1): face_edges.append((vert_index + i, vert_index + i + 1)) i = i + 1 if flt_face.props['draw type'] == 2: face_edges.append((vert_index + i,vert_index)) elif face_len == 3 or face_len == 4: # tri or quad #if face_len == 1: # pass #if face_len == 2: # face_edges.append((vert_index, vert_index+1)) new_faces.append( [i+vert_index for i in xrange(face_len)] ) 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)) else: # fgon mesh_face_indicies = [i+vert_index for i in xrange(face_len)] tri_ngons= ngon(self.mesh, mesh_face_indicies) new_faces.extend([ [mesh_face_indicies[t] for t in tri] for tri in tri_ngons]) 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 ]) vert_index+= face_len FLT_OrigIndex+=1 self.mesh.faces.extend(new_faces) self.mesh.edges.extend(face_edges) #add in the FLT_ORIGINDEX layer if len(self.mesh.faces): try: self.mesh.faceUV= True except: pass if self.mesh.faceUV == True: self.mesh.renameUVLayer(self.mesh.activeUVLayer, 'Layer0') #create name layer for faces self.mesh.faces.addPropertyLayer("FLT_ID",Blender.Mesh.PropertyTypes["STRING"]) #create layer for face color indices self.mesh.faces.addPropertyLayer("FLT_COL",Blender.Mesh.PropertyTypes["INT"]) #create index layer for faces. This is needed by both FGONs and subfaces self.mesh.faces.addPropertyLayer("FLT_ORIGINDEX",Blender.Mesh.PropertyTypes["INT"]) #create temporary FGON flag layer. Delete after remove doubles self.mesh.faces.addPropertyLayer("FLT_FGON",Blender.Mesh.PropertyTypes["INT"]) self.mesh.faces.addPropertyLayer("FLT_SFLEVEL", Blender.Mesh.PropertyTypes["INT"]) for i, f in enumerate(self.mesh.faces): props = new_faces_props[i] if props[6]['template billboard'] > 0: f.transp |= Blender.Mesh.FaceTranspModes["ALPHA"] if props[6]['template billboard'] == 2: f.mode |= Blender.Mesh.FaceModes["BILLBOARD"] f.mode |= Blender.Mesh.FaceModes["LIGHT"] #f.mat = props[0] f.image = props[1] f.uv = props[2] #set vertex colors color = self.header.get_color(props[5]) if not color: color = [255,255,255,255] for mcol in f.col: mcol.a = color[3] mcol.r = color[0] mcol.g = color[1] mcol.b = color[2] f.setProperty("FLT_SFLEVEL", props[9]) f.setProperty("FLT_ORIGINDEX",i) f.setProperty("FLT_ID",props[6]['id']) #if props[5] > 13199: # print "Warning, invalid color index read in! Using default!" # f.setProperty("FLT_COL",127) #else: if(1): #uh oh.... value = struct.unpack('>i',struct.pack('>I',props[5]))[0] f.setProperty("FLT_COL",value) #if props[8]: # f.setProperty("FLT_FGON",1) #else: # f.setProperty("FLT_FGON",0) #Create multitex layers, if present. actuvlayer = self.mesh.activeUVLayer if(self.hasmtex): #For every multi-tex layer, we have to add a new UV layer to the mesh for i,mask in enumerate(reversed(sorted(self.uvlayers))): if self.uvlayers[mask]: self.blayernames[mask] = "Layer" + str(i+1) self.mesh.addUVLayer(self.blayernames[mask]) #Cycle through availible multi-tex layers and add face UVS for mask in self.uvlayers: if self.uvlayers[mask]: self.mesh.activeUVLayer = self.blayernames[mask] for j, f in enumerate(self.mesh.faces): f.transp |= Blender.Mesh.FaceTranspModes["ALPHA"] f.mode |= Blender.Mesh.FaceModes["LIGHT"] props = new_faces_props[j] uvlayers = props[4] if uvlayers.has_key(mask): #redundant uverts = props[3] for k, uv in enumerate(f.uv): uv[0] = uverts[k][mask][0] uv[1] = uverts[k][mask][1] uvlayer = uvlayers[mask] tex_index = uvlayer['texture index'] if tex_index != -1: try: f.image = self.header.tex_pal[tex_index][1] except KeyError: f.image = None if global_prefs['smoothshading'] == True and len(self.mesh.faces): #We need to store per-face vertex normals in the faces as UV layers and delete them later. self.mesh.addUVLayer("FLTNorm1") self.mesh.addUVLayer("FLTNorm2") self.mesh.activeUVLayer = "FLTNorm1" for f in self.mesh.faces: f.smooth = 1 #grab the X and Y components of normal and store them in UV for i, uv in enumerate(f.uv): vert = f.v[i].index vert_desc = vert_desc_lst[vert_list[vert-1]] if vert_desc.cnorm: uv[0] = vert_desc.nx uv[1] = vert_desc.ny else: uv[0] = 0.0 uv[1] = 0.0 #Now go through and populate the second UV Layer with the z component self.mesh.activeUVLayer = "FLTNorm2" for f in self.mesh.faces: for i, uv in enumerate(f.uv): vert = f.v[i].index vert_desc = vert_desc_lst[vert_list[vert-1]] if vert_desc.cnorm: uv[0] = vert_desc.nz uv[1] = 0.0 else: uv[0] = 0.0 uv[1] = 0.0 #Finally, go through, remove dummy vertex, remove doubles and add edgesplit modifier. Blender.Mesh.Mode(Blender.Mesh.SelectModes['VERTEX']) self.mesh.verts.delete(0) # remove the dummy vert self.mesh.sel= 1 self.header.scene.update(1) #slow! self.mesh.remDoubles(0.0001) edgeHash = dict() for edge in self.mesh.edges: edgeHash[edge.key] = edge.index if global_prefs['smoothshading'] == True and len(self.mesh.faces): #rip out the custom vertex normals from the mesh and place them in a face aligned list. Easier to compare this way. facenorms = [] self.mesh.activeUVLayer = "FLTNorm1" for face in self.mesh.faces: facenorm = [] for uv in face.uv: facenorm.append(Vector(uv[0],uv[1],0.0)) facenorms.append(facenorm) self.mesh.activeUVLayer = "FLTNorm2" for i, face in enumerate(self.mesh.faces): facenorm = facenorms[i] for j, uv in enumerate(face.uv): facenorm[j][2] = uv[0] self.mesh.removeUVLayer("FLTNorm1") self.mesh.removeUVLayer("FLTNorm2") #find hard edges #store edge data for lookup by faces #edgeHash = dict() #for edge in self.mesh.edges: # edgeHash[edge.key] = edge.index edgeNormHash = dict() #make sure to align the edgenormals to key value! for i, face in enumerate(self.mesh.faces): facenorm = facenorms[i] faceEdges = [] faceEdges.append((face.v[0].index,face.v[1].index,facenorm[0],facenorm[1],face.edge_keys[0])) faceEdges.append((face.v[1].index,face.v[2].index,facenorm[1],facenorm[2],face.edge_keys[1])) if len(face.v) == 3: faceEdges.append((face.v[2].index,face.v[0].index,facenorm[2],facenorm[0],face.edge_keys[2])) elif len(face.v) == 4: faceEdges.append((face.v[2].index,face.v[3].index,facenorm[2],facenorm[3],face.edge_keys[2])) faceEdges.append((face.v[3].index,face.v[0].index,facenorm[3],facenorm[0],face.edge_keys[3])) #check to see if edgeNormal has been placed in the edgeNormHash yet #this is a redundant test, and should be optimized to not be called as often as it is. for j, faceEdge in enumerate(faceEdges): #the value we are looking for is (faceEdge[2],faceEdge[3]) hashvalue = (faceEdge[2],faceEdge[3]) if (faceEdge[0],faceEdge[1]) != faceEdge[4]: hashvalue = (hashvalue[1],hashvalue[0]) assert (faceEdge[1],faceEdge[0]) == faceEdge[4] if edgeNormHash.has_key(faceEdge[4]): #compare value in the hash, if different, mark as sharp edgeNorm = edgeNormHash[faceEdge[4]] if\ abs(hashvalue[0][0] - edgeNorm[0][0]) > FLOAT_TOLERANCE or\ abs(hashvalue[0][1] - edgeNorm[0][1]) > FLOAT_TOLERANCE or\ abs(hashvalue[0][2] - edgeNorm[0][2]) > FLOAT_TOLERANCE or\ abs(hashvalue[1][0] - edgeNorm[1][0]) > FLOAT_TOLERANCE or\ abs(hashvalue[1][1] - edgeNorm[1][1]) > FLOAT_TOLERANCE or\ abs(hashvalue[1][2] - edgeNorm[1][2]) > FLOAT_TOLERANCE: edge = self.mesh.edges[edgeHash[faceEdge[4]]] edge.flag |= Blender.Mesh.EdgeFlags.SHARP else: edgeNormHash[faceEdge[4]] = hashvalue #add in edgesplit modifier mod = self.object.modifiers.append(Blender.Modifier.Types.EDGESPLIT) mod[Blender.Modifier.Settings.EDGESPLIT_FROM_SHARP] = True mod[Blender.Modifier.Settings.EDGESPLIT_FROM_ANGLE] = False if(actuvlayer): self.mesh.activeUVLayer = actuvlayer def blender_import(self): if self.vis and self.parent: self.vis = self.parent.vis name = self.props['id'] if self.hasMesh: self.mesh = Blender.Mesh.New() self.mesh.name = 'FLT_FaceList' self.mesh.fakeUser = True self.mesh.verts.extend( Vector()) #DUMMYVERT self.object = self.header.scene.objects.new(self.mesh) else: self.object = self.header.scene.objects.new('Empty') self.object.name = name self.header.group.objects.link(self.object) #id props import self.object.properties['FLT'] = dict() for key in self.props: try: self.object.properties['FLT'][key] = self.props[key] except: #horrible... pass if self.parent and self.parent.object and (self.header.scene == self.parent.header.scene): self.parent.object.makeParent([self.object]) if self.vis == False: self.object.restrictDisplay = True self.object.restrictRender = True else: #check for LOD children and set the proper flags lodlist = list() for child in self.children: if child.props.has_key('type') and child.props['type'] == 73: lodlist.append(child) def LODmin(a,b): if a.props['5d!switch in'] < b.props['5d!switch in']: return a return b min= None if len(lodlist) > 1: for lod in lodlist: lod.vis = False min = lodlist[0] for i in xrange(len(lodlist)): min= LODmin(min,lodlist[i]) min.vis = True if self.matrix: self.object.setMatrix(self.matrix) Node.blender_import(self) # Attach faces to self.faceLs if self.hasMesh: # Add all my faces into the mesh at once self.blender_import_my_faces() def parse_face(self): child = Face(self, self.subfacelevel) child.parse() return True def parse_group(self): child = Group(self) child.parse() return True def move_to_next_layer(self): global current_layer current_layer = current_layer << 1 if current_layer > 0x80000: current_layer = 1 def parse_lod(self): child = LOD(self) child.parse() return True def parse_unhandled(self): child = Unhandled(self) child.parse() return True def parse_object(self): child = Object(self) child.parse() return True def parse_xref(self): child = XRef(self) child.parse() return True def parse_dof(self): child = DOF(self) child.parse() return True def parse_indexed_light_point(self): child = IndexedLightPoint(self) child.parse() return True def parse_inline_light_point(self): child = InlineLightPoint(self) child.parse() return True def parse_matrix(self): m = list() for i in xrange(4): m.append([]) for j in xrange(4): f = self.header.fw.read_float() m[i].append(f) self.matrix = Blender.Mathutils.Matrix(m[0], m[1], m[2], m[3]) def parse_subpush(self): self.parse_push() self.subfacelevel+= 1 return True def parse_subpop(self): self.parse_pop() self.subfacelevel -= 1 return True class Face(Node): def __init__(self, parent,subfacelevel): Node.__init__(self, parent, parent.header) self.root_handler.set_handler({31: self.parse_comment, 10: self.parse_push, 52: self.parse_multitex}) self.root_handler.set_throw_back_lst(throw_back_opcodes) self.child_handler.set_handler({72: self.parse_vertex_list, 10: self.parse_push, 11: self.parse_pop, 53: self.parse_uvlist}) if parent: parent.hasMesh = True self.subfacelevel = subfacelevel self.indices = list() # face verts here self.uvlayers = dict() # MultiTexture layers keyed to layer bitmask. self.uverts = list() # Vertex aligned list of dictionaries keyed to layer bitmask. self.uvmask = 0 # Bitfield read from MTex record self.comment = '' self.props = dict() self.props['id'] = self.header.fw.read_string(8) # Load face. self.props['ir color'] = self.header.fw.read_int() self.props['priority'] = self.header.fw.read_short() self.props['draw type'] = self.header.fw.read_char() self.props['texture white'] = self.header.fw.read_char() self.header.fw.read_ahead(4) # color name indices self.header.fw.read_ahead(1) # reserved self.props['template billboard'] = self.header.fw.read_uchar() self.detail_tex_index = self.header.fw.read_short() self.tex_index = self.header.fw.read_short() self.mat_index = self.header.fw.read_short() self.props['smc'] = self.header.fw.read_short() self.props['fid'] = self.header.fw.read_short() self.props['ir material'] = self.header.fw.read_int() self.alpha = 1.0 - float(self.header.fw.read_ushort()) / 65535.0 self.props['lod generation control'] = self.header.fw.read_uchar() self.header.fw.read_ahead(1) # line style index self.props['flags'] = self.header.fw.read_int() self.props['light mode'] = self.header.fw.read_uchar() self.header.fw.read_ahead(7) a = self.header.fw.read_uchar() b = self.header.fw.read_uchar() g = self.header.fw.read_uchar() r = self.header.fw.read_uchar() self.packed_color = [r, g, b, a] a = self.header.fw.read_uchar() b = self.header.fw.read_uchar() g = self.header.fw.read_uchar() r = self.header.fw.read_uchar() self.alt_packed_color = [r, g, b, a] self.tex_map_index = self.header.fw.read_short() self.header.fw.read_ahead(2) self.color_index = self.header.fw.read_uint() self.alt_color_index = self.header.fw.read_uint() #self.header.fw.read_ahead(2) #self.shader_index = self.header.fw.read_short() def parse_comment(self): self.comment = self.header.fw.read_string(self.header.fw.get_length()-4) return True def blender_import(self): vert_count = len(self.indices) if vert_count < 1: if global_prefs['verbose'] >= 2: print 'Warning: Ignoring face with no vertices.' return # Assign material and image self.parent.faceLs.append(self) #need to store comment in mesh prop layer! # Store comment info in parent. #if self.comment != '': # if self.parent.props['comment'] != '': # self.parent.props['comment'] += '\n\nFrom Face:\n' + self.comment # else: # self.parent.props['comment'] = self.comment if self.uvlayers: #Make sure that the mesh knows about the layers that this face uses self.parent.hasmtex = True for mask in self.uvlayers.keys(): self.parent.uvlayers[mask] = True def parse_vertex_list(self): length = self.header.fw.get_length() fw = self.header.fw vert_pal = self.header.vert_pal count = (length-4)/4 # If this ever fails the chunk below does error checking self.indices= [vert_pal.index[fw.read_int()] for i in xrange(count)] ''' for i in xrange(count): byte_offset = fw.read_int() if byte_offset in vert_pal.index: index = vert_pal.index[byte_offset] self.indices.append(index) elif global_prefs['verbose'] >= 1: print 'Warning: Unable to map byte offset %s' + \ ' to vertex index.' % byte_offset ''' return True def parse_multitex(self): #Parse MultiTex Record. length = self.header.fw.get_length() fw = self.header.fw #num layers == (length - 8) / 4 uvmask = fw.read_uint() mask = 2147483648 for i in xrange(7): if mask & uvmask: uvlayer = dict() self.uvlayers[mask] = uvlayer mask = mask / 2 #read in record for each individual layer. for key in reversed(sorted(self.uvlayers)): uvlayer = self.uvlayers[key] uvlayer['texture index'] = fw.read_ushort() uvlayer['texture enviorment'] = fw.read_ushort() uvlayer['texture mapping'] = fw.read_ushort() uvlayer['texture data'] = fw.read_ushort() self.uvmask = uvmask def parse_uvlist(self): #for each uvlayer, add uv vertices length = self.header.fw.get_length() fw = self.header.fw uvmask = fw.read_uint() if uvmask != self.uvmask: #This should never happen! fw.read_ahead(self.length - 4) #potentially unnessecary? else: #need to store in uvverts dictionary for each vertex. totverts = len(self.indices) for i in xrange(totverts): uvert = dict() for key in reversed(sorted(self.uvlayers)): uv = Vector(0.0,0.0) uv[0] = fw.read_float() uv[1] = fw.read_float() uvert[key] = uv self.uverts.append(uvert) class Object(InterNode): def __init__(self, parent): Node.__init__(self, parent, parent.header) InterNode.__init__(self) self.root_handler.set_handler({33: self.parse_long_id, 31: self.parse_comment, 10: self.parse_push, 49: self.parse_matrix}) self.root_handler.set_throw_back_lst(throw_back_opcodes) self.child_handler.set_handler({5: self.parse_face, 19: self.parse_subpush, 20: self.parse_subpop, 111: self.parse_inline_light_point, 10: self.parse_push, 11: self.parse_pop}) self.props = dict() self.props['comment'] = '' self.parse_record() class Group(InterNode): def __init__(self, parent): Node.__init__(self, parent, parent.header) InterNode.__init__(self) self.root_handler.set_handler({33: self.parse_long_id, 31: self.parse_comment, 10: self.parse_push, 49: self.parse_matrix}) self.root_handler.set_throw_back_lst(throw_back_opcodes) self.child_handler.set_handler({5: self.parse_face, 19: self.parse_subpush, 20: self.parse_subpop, 111: self.parse_inline_light_point, 2: self.parse_group, 73: self.parse_lod, 4: self.parse_object, 10: self.parse_push, 11: self.parse_pop, 96: self.parse_unhandled, 14: self.parse_dof, 91: self.parse_unhandled, 98: self.parse_unhandled, 63: self.parse_xref}) self.props = dict.fromkeys(['type', 'id', 'comment', 'priority', 'flags', 'special1', 'special2', 'significance', 'layer code', 'loop count', 'loop duration', 'last frame duration']) self.props['comment'] = '' self.parse_record() #self.props['type'] = str(self.opcode) + ':' + opcode_name[self.opcode] #props = records[self.opcode] #propkeys = props.keys() #propkeys.sort() #for position in propkeys: # (type,length,name) = props[position] # self.props[name] = read_prop(self.header.fw,type,length) #self.props['id'] = self.props['3t8!id'] class DOF(InterNode): def blender_import(self): InterNode.blender_import(self) def __init__(self, parent): Node.__init__(self, parent, parent.header) InterNode.__init__(self) self.root_handler.set_handler({33: self.parse_long_id, 31: self.parse_comment, 10: self.parse_push, 49: self.parse_matrix}) self.root_handler.set_throw_back_lst(throw_back_opcodes) self.child_handler.set_handler({#130: self.parse_indexed_light_point, 111: self.parse_inline_light_point, 2: self.parse_group, 73: self.parse_lod, 4: self.parse_object, 10: self.parse_push, 11: self.parse_pop, 96: self.parse_unhandled, 14: self.parse_dof, 91: self.parse_unhandled, 98: self.parse_unhandled, 63: self.parse_xref}) self.props = dict() self.props['comment'] = '' self.parse_record() class XRef(InterNode): def parse(self): if self.xref: self.xref.parse() Node.parse(self) def __init__(self, parent): Node.__init__(self, parent, parent.header) InterNode.__init__(self) self.root_handler.set_handler({49: self.parse_matrix}) self.root_handler.set_throw_back_lst(throw_back_opcodes) self.props = dict() self.props['comment'] = '' self.parse_record() xref_filename = self.props['3t200!filename'] self.props['id'] = 'X: ' + Blender.sys.splitext(Blender.sys.basename(xref_filename))[0] #this is really wrong as well.... if global_prefs['doxrefs'] and os.path.exists(xref_filename) and not self.header.grr.xrefs.has_key(xref_filename): self.xref = Database(xref_filename, self.header.grr, self) self.header.grr.xrefs[xref_filename] = self.xref else: self.xref = None def blender_import(self): #name = self.props['type'] + ': ' + self.props['id'] name = self.props['id'] self.object = self.header.scene.objects.new('Empty') self.object.name = name self.object.enableDupGroup = True self.header.group.objects.link(self.object) #for broken links its ok to leave this empty! they purely for visual purposes anyway..... try: self.object.DupGroup = self.header.grr.xrefs[self.props['3t200!filename']].group except: pass if self.parent and self.parent.object: self.parent.object.makeParent([self.object]) #id props import self.object.properties['FLT'] = dict() for key in self.props: try: self.object.properties['FLT'][key] = self.props[key] except: #horrible... pass self.object.Layer = current_layer self.object.sel = 1 if self.matrix: self.object.setMatrix(self.matrix) Node.blender_import(self) class LOD(InterNode): def blender_import(self): #self.move_to_next_layer() InterNode.blender_import(self) #self.object.properties['FLT'] = self.props.copy() def __init__(self, parent): Node.__init__(self, parent, parent.header) InterNode.__init__(self) self.root_handler.set_handler({33: self.parse_long_id, 31: self.parse_comment, 10: self.parse_push, 49: self.parse_matrix}) self.root_handler.set_throw_back_lst(throw_back_opcodes) self.child_handler.set_handler({2: self.parse_group, 111: self.parse_inline_light_point, 73: self.parse_lod, 4: self.parse_object, 10: self.parse_push, 11: self.parse_pop, 96: self.parse_unhandled, # switch 14: self.parse_dof, # DOF 91: self.parse_unhandled, # sound 98: self.parse_unhandled, # clip 63: self.parse_xref}) self.props = dict() self.props['comment'] = '' self.parse_record() class InlineLightPoint(InterNode): def __init__(self, parent): Node.__init__(self, parent, parent.header) InterNode.__init__(self) self.root_handler.set_handler({33: self.parse_long_id, 31: self.parse_comment, 10: self.parse_push, 49: self.parse_matrix}) self.root_handler.set_throw_back_lst(throw_back_opcodes) self.child_handler.set_handler({72: self.parse_vertex_list, 10: self.parse_push, 11: self.parse_pop}) self.indices = list() self.props = dict() self.props['comment'] = '' self.parse_record() def blender_import(self): name = self.props['id'] self.mesh= Blender.Mesh.New() self.mesh.name = 'FLT_LP' self.object = self.header.scene.objects.new(self.mesh) self.object.name = name #self.mesh.verts.extend(Vector() ) # DUMMYVERT self.object.Layer = current_layer self.object.sel= 1 self.object.properties['FLT'] = dict() for key in self.props: try: self.object.properties['FLT'][key] = self.props[key] except: #horrible... pass if self.parent and self.parent.object and self.header.scene == self.parent.header.scene: self.parent.object.makeParent([self.object]) if self.matrix: self.object.setMatrix(self.matrix) self.mesh.verts.extend([self.header.vert_pal.blender_verts[i] for i in self.indices]) #add color index information. self.mesh.verts.addPropertyLayer("FLT_VCOL",Blender.Mesh.PropertyTypes["INT"]) for i, vindex in enumerate(self.indices): vdesc = self.header.vert_pal.vert_desc_lst[vindex] v = self.mesh.verts[i] v.setProperty("FLT_VCOL",vdesc.cindex) #for i, v in enumerate(self.mesh.verts): # vdesc = self.header.vert_pal.vert_desc_lst[i] # v.setProperty("FLT_VCOL",vdesc.cindex) self.mesh.update() def parse_vertex_list(self): length = self.header.fw.get_length() fw = self.header.fw vert_pal = self.header.vert_pal count = (length-4)/4 # If this ever fails the chunk below does error checking self.indices= [vert_pal.index[fw.read_int()] for i in xrange(count)] ''' for i in xrange(count): byte_offset = fw.read_int() if byte_offset in vert_pal.index: index = vert_pal.index[byte_offset] self.indices.append(index) elif global_prefs['verbose'] >= 1: print 'Warning: Unable to map byte offset %s' + \ ' to vertex index.' % byte_offset ''' return True class IndexedLightPoint(InterNode): # return dictionary: lp_app name => index list def group_points(self, props): name_to_indices = {} for i in self.indices: vert_desc = self.header.vert_pal.vert_desc_lst[i] app_desc = LightPointAppDesc() app_desc.props.update(props) # add vertex normal and color app_desc.props.update({'nx': vert_desc.nx}) app_desc.props.update({'ny': vert_desc.ny}) app_desc.props.update({'nz': vert_desc.nz}) app_desc.props.update({'r': vert_desc.r}) app_desc.props.update({'g': vert_desc.g}) app_desc.props.update({'b': vert_desc.b}) app_desc.props.update({'a': vert_desc.a}) app_name = self.header.grr.request_lightpoint_app(app_desc, self.header.scene) if name_to_indices.get(app_name): name_to_indices[app_name].append(i) else: name_to_indices.update({app_name: [i]}) return name_to_indices def blender_import(self): name = self.props['type'] + ': ' + self.props['id'] name_to_indices = self.group_points(self.header.lightpoint_appearance_pal[self.index]) for app_name, indices in name_to_indices.iteritems(): self.object = Blender.Object.New('Mesh', name) self.mesh= Blender.Mesh.New() self.mesh.verts.extend( Vector() ) # DUMMYVERT self.object.link(self.mesh) if self.parent: self.parent.object.makeParent([self.object]) for i in indices: vert = self.header.vert_pal.blender_verts[i] self.mesh.verts.append(vert) self.header.scene.objects.link(self.object) self.object.Layer = current_layer if self.matrix: self.object.setMatrix(self.matrix) # Import comment. if self.props['comment'] != '': name = 'COMMENT: ' + self.props['id'] t = Blender.Text.New(name) t.write(self.props['comment']) self.props['comment'] = name # Attach properties. self.props.update({'appearance': app_name}) for name, value in self.props.iteritems(): self.object.addProperty(name, value) self.mesh.update() def parse_vertex_list(self): length = self.header.fw.get_length() fw = self.header.fw vert_pal = self.header.vert_pal count = (length-4)/4 # If this ever fails the chunk below does error checking self.indices= [vert_pal.index[fw.read_int()] for i in xrange(count)] ''' for i in xrange(count): byte_offset = fw.read_int() if byte_offset in vert_pal.index: index = vert_pal.index[byte_offset] self.indices.append(index) elif global_prefs['verbose'] >= 1: print 'Warning: Unable to map byte offset %s' + \ ' to vertex index.' % byte_offset ''' return True def __init__(self, parent): Node.__init__(self, parent, parent.header) InterNode.__init__(self) self.root_handler.set_handler({33: self.parse_long_id, 31: self.parse_comment, 10: self.parse_push, 49: self.parse_matrix}) self.root_handler.set_throw_back_lst(throw_back_opcodes) self.child_handler.set_handler({72: self.parse_vertex_list, 10: self.parse_push, 11: self.parse_pop}) self.indices = list() self.props = dict.fromkeys(['id', 'type', 'comment', 'draw order', 'appearance']) self.props['comment'] = '' self.props['type'] = 'Light Point' self.props['id'] = self.header.fw.read_string(8) self.index = self.header.fw.read_int() self.header.fw.read_ahead(4) # animation index self.props['draw order'] = self.header.fw.read_int() class Unhandled(InterNode): def __init__(self, parent): Node.__init__(self, parent, parent.header) InterNode.__init__(self) self.root_handler.set_handler({33: self.parse_long_id, 31: self.parse_comment, 10: self.parse_push, 49: self.parse_matrix}) self.root_handler.set_throw_back_lst(throw_back_opcodes) self.child_handler.set_handler({2: self.parse_group, 73: self.parse_lod, 4: self.parse_object, 10: self.parse_push, 11: self.parse_pop, 96: self.parse_unhandled, # switch 14: self.parse_dof, # DOF 91: self.parse_unhandled, # sound 98: self.parse_unhandled, # clip 63: self.parse_xref}) self.props['id'] = self.header.fw.read_string(8) class Database(InterNode): def blender_import(self): for key in self.tex_pal.keys(): path_filename= FF.find(self.tex_pal[key][0]) if path_filename != None: img = self.grr.request_image(path_filename) if img: self.tex_pal[key][1] = img elif global_prefs['verbose'] >= 1: print 'Warning: Unable to find', self.tex_pal[key][0] self.scene.properties['FLT'] = dict() for key in self.props: try: self.scene.properties['FLT'][key] = self.props[key] except: #horrible... pass self.scene.properties['FLT']['Main'] = 0 self.scene.properties['FLT']['Filename'] = self.bname #import color palette carray = list() for color in self.col_pal: carray.append(struct.unpack('>i',struct.pack('>BBBB',color[0],color[1],color[2],color[3]))[0]) self.scene.properties['FLT']['Color Palette'] = carray Node.blender_import(self) def parse_appearance_palette(self): props = dict() self.fw.read_ahead(4) # reserved props.update({'id': self.fw.read_string(256)}) index = self.fw.read_int() props.update({'smc': self.fw.read_short()}) props.update({'fid': self.fw.read_short()}) props.update({'back color: a': self.fw.read_uchar()}) props.update({'back color: b': self.fw.read_uchar()}) props.update({'back color: g': self.fw.read_uchar()}) props.update({'back color: r': self.fw.read_uchar()}) props.update({'display mode': self.fw.read_int()}) props.update({'intensity': self.fw.read_float()}) props.update({'back intensity': self.fw.read_float()}) props.update({'minimum defocus': self.fw.read_float()}) props.update({'maximum defocus': self.fw.read_float()}) props.update({'fading mode': self.fw.read_int()}) props.update({'fog punch mode': self.fw.read_int()}) props.update({'directional mode': self.fw.read_int()}) props.update({'range mode': self.fw.read_int()}) props.update({'min pixel size': self.fw.read_float()}) props.update({'max pixel size': self.fw.read_float()}) props.update({'actual size': self.fw.read_float()}) props.update({'trans falloff pixel size': self.fw.read_float()}) props.update({'trans falloff exponent': self.fw.read_float()}) props.update({'trans falloff scalar': self.fw.read_float()}) props.update({'trans falloff clamp': self.fw.read_float()}) props.update({'fog scalar': self.fw.read_float()}) props.update({'fog intensity': self.fw.read_float()}) props.update({'size threshold': self.fw.read_float()}) props.update({'directionality': self.fw.read_int()}) props.update({'horizontal lobe angle': self.fw.read_float()}) props.update({'vertical lobe angle': self.fw.read_float()}) props.update({'lobe roll angle': self.fw.read_float()}) props.update({'dir falloff exponent': self.fw.read_float()}) props.update({'dir ambient intensity': self.fw.read_float()}) props.update({'significance': self.fw.read_float()}) props.update({'flags': self.fw.read_int()}) props.update({'visibility range': self.fw.read_float()}) props.update({'fade range ratio': self.fw.read_float()}) props.update({'fade in duration': self.fw.read_float()}) props.update({'fade out duration': self.fw.read_float()}) props.update({'LOD range ratio': self.fw.read_float()}) props.update({'LOD scale': self.fw.read_float()}) self.lightpoint_appearance_pal.update({index: props}) def parse_header(self): self.props['type'] = 'Header' self.props['comment'] = '' self.props['id'] = self.fw.read_string(8) self.props['version'] = self.fw.read_int() self.fw.read_ahead(46) self.props['units'] = self.fw.read_char() self.props['set white'] = bool(self.fw.read_char()) self.props['flags'] = self.fw.read_int() self.fw.read_ahead(24) self.props['projection type'] = self.fw.read_int() self.fw.read_ahead(36) self.props['sw x'] = self.fw.read_double() self.props['sw y'] = self.fw.read_double() self.props['dx'] = self.fw.read_double() self.props['dy'] = self.fw.read_double() self.fw.read_ahead(24) self.props['sw lat'] = self.fw.read_double() self.props['sw lon'] = self.fw.read_double() self.props['ne lat'] = self.fw.read_double() self.props['ne lon'] = self.fw.read_double() self.props['origin lat'] = self.fw.read_double() self.props['origin lon'] = self.fw.read_double() self.props['lambert lat1'] = self.fw.read_double() self.props['lambert lat2'] = self.fw.read_double() self.fw.read_ahead(16) self.props['ellipsoid model'] = self.fw.read_int() self.fw.read_ahead(4) self.props['utm zone'] = self.fw.read_short() self.fw.read_ahead(6) self.props['dz'] = self.fw.read_double() self.props['radius'] = self.fw.read_double() self.fw.read_ahead(8) self.props['major axis'] = self.fw.read_double() self.props['minor axis'] = self.fw.read_double() if global_prefs['verbose'] >= 1: print 'OpenFlight Version:', float(self.props['version']) / 100.0 print return True def parse_mat_palette(self): mat_desc = MaterialDesc() index = self.fw.read_int() name = self.fw.read_string(12) if len(mat_desc.name) > 0: mat_desc.name = name flag = self.fw.read_int() # skip material if not used if not flag & 0x80000000: return True ambient_col = [self.fw.read_float(), self.fw.read_float(), self.fw.read_float()] mat_desc.diffuse = [self.fw.read_float(), self.fw.read_float(), self.fw.read_float()] mat_desc.specular = [self.fw.read_float(), self.fw.read_float(), self.fw.read_float()] emissive_col = [self.fw.read_float(), self.fw.read_float(), self.fw.read_float()] mat_desc.shininess = self.fw.read_float() / 64.0 # [0.0, 128.0] => [0.0, 2.0] mat_desc.alpha = self.fw.read_float() # Convert ambient and emissive colors into intensitities. mat_desc.ambient = col_to_gray(ambient_col) mat_desc.emissive = col_to_gray(emissive_col) self.mat_desc_pal_lst.append( (index, mat_desc) ) return True def get_color(self, color_index): color = None index = color_index / 128 intensity = float(color_index - 128.0 * index) / 127.0 if index >= 0 and index <= 1023: brightest = self.col_pal[index] r = int(brightest[0] * intensity) g = int(brightest[1] * intensity) b = int(brightest[2] * intensity) a = int(brightest[3]) color = [r, g, b, a] return color def parse_color_palette(self): self.header.fw.read_ahead(128) for i in xrange(1024): a = self.header.fw.read_uchar() b = self.header.fw.read_uchar() g = self.header.fw.read_uchar() r = self.header.fw.read_uchar() self.col_pal.append((r, g, b, a)) return True def parse_vertex_palette(self): self.vert_pal = VertexPalette(self) self.vert_pal.parse() return True def parse_texture_palette(self): name = self.fw.read_string(200) index = self.fw.read_int() self.tex_pal[index]= [name, None] return True def read_attribute_files(self): for tex in self.tex_pal.keys(): [name,image] = self.tex_pal[tex] basename = os.path.basename(name) if(image): basename = basename + ".attr" dirname = os.path.dirname(Blender.sys.expandpath(image.getFilename())) #can't rely on original info stored in pallette since it might be relative link newpath = os.path.join(dirname, basename) if os.path.exists(newpath) and not image.properties.has_key('FLT'): fw = flt_filewalker.FltIn(newpath) fw.read_ahead(8) #We dont care what the attribute file says about x/y dimensions image.properties['FLT']={} #need to steal code from parse records.... props = records['Image'] propkeys = props.keys() propkeys.sort() for position in propkeys: (type,length,name) = props[position] image.properties['FLT'][name] = read_prop(fw,type,length) fw.close_file() #copy clamp settings wrap = image.properties['FLT']['10i!Wrap'] wrapu = image.properties['FLT']['11i!WrapU'] wrapv = image.properties['FLT']['12i!WrapV'] if wrapu == 3 or wrapv == 3: wrapuv = (wrap,wrap) else: wrapuv = (wrapu, wrapv) image.clampX = wrapuv[0] image.clampY = wrapuv[1] elif not os.path.exists(newpath): print "Cannot read attribute file:" + newpath def __init__(self, filename, grr, parent=None): if global_prefs['verbose'] >= 1: print 'Parsing:', filename print self.fw = flt_filewalker.FltIn(filename) self.filename = filename self.bname = os.path.splitext(os.path.basename(filename))[0] self.grr = grr Node.__init__(self, parent, self) InterNode.__init__(self) self.root_handler.set_handler({1: self.parse_header, 67: self.parse_vertex_palette, 33: self.parse_long_id, 31: self.parse_comment, 64: self.parse_texture_palette, 32: self.parse_color_palette, 113: self.parse_mat_palette, 128: self.parse_appearance_palette, 10: self.parse_push}) if parent: self.root_handler.set_throw_back_lst(throw_back_opcodes) self.child_handler.set_handler({#130: self.parse_indexed_light_point, 111: self.parse_inline_light_point, 2: self.parse_group, 73: self.parse_lod, 4: self.parse_object, 10: self.parse_push, 11: self.parse_pop, 96: self.parse_unhandled, 14: self.parse_dof, 91: self.parse_unhandled, 98: self.parse_unhandled, 63: self.parse_xref}) self.scene = Blender.Scene.New(self.bname) self.group = Blender.Group.New(self.bname) self.vert_pal = None self.lightpoint_appearance_pal = dict() self.tex_pal = dict() #self.tex_pal_lst = list() #self.bl_tex_pal = dict() self.col_pal = list() self.mat_desc_pal_lst = list() self.mat_desc_pal = dict() self.props = dict.fromkeys(['id', 'type', 'comment', 'version', 'units', 'set white', 'flags', 'projection type', 'sw x', 'sw y', 'dx', 'dy', 'dz', 'sw lat', 'sw lon', 'ne lat', 'ne lon', 'origin lat', 'origin lon', 'lambert lat1', 'lambert lat2', 'ellipsoid model', 'utm zone', 'radius', 'major axis', 'minor axis']) def clearparent(root,childhash): for child in childhash[root]: clearparent(child,childhash) root.clrParent(2,0) def fixscale(root,childhash): for child in childhash[root]: fixscale(child,childhash) location = Blender.Mathutils.Vector(root.getLocation('worldspace')) if location[0] != 0.0 and location[1] != 0.0 and location[2] != 0.0: #direction = Blender.Mathutils.Vector(0-location[0],0-location[1],0-location[2]) #reverse vector smat = Blender.Mathutils.ScaleMatrix(global_prefs['scale'],4) root.setLocation(location * smat) #if its a mesh, we need to scale all of its vertices too if root.type == 'Mesh': smat = Blender.Mathutils.ScaleMatrix(global_prefs['scale'],4) rmesh = root.getData(mesh=True) for v in rmesh.verts: v.co = v.co * smat def reparent(root,childhash,sce): for child in childhash[root]: reparent(child,childhash,sce) root.makeParent(childhash[root]) sce.update(1) def update_scene(root,sdone): for object in root.objects: if object.DupGroup: try: child = Blender.Scene.Get(object.DupGroup.name) except: child = None if child and child not in sdone: update_scene(child,sdone) root.makeCurrent() #create a list of children for each object childhash = dict() for object in root.objects: childhash[object] = list() for object in root.objects: if object.parent: childhash[object.parent].append(object) for object in root.objects: if not object.parent: #recursivley go through and clear all the children of their transformation, starting at deepest level first. clearparent(object,childhash) #now fix the location of everything fixscale(object,childhash) #now fix the parenting reparent(object,childhash,root) for object in root.objects: object.makeDisplayList() root.update(1) sdone.append(root) def select_file(filename, grr): if not Blender.sys.exists(filename): msg = 'Error: File ' + filename + ' does not exist.' Blender.Draw.PupMenu(msg) return if not filename.lower().endswith('.flt'): msg = 'Error: Not a flight file.' Blender.Draw.PupMenu(msg) print msg print return global_prefs['fltfile']= filename global_prefs['verbose']= 1 global_prefs['get_texture'] = True global_prefs['get_diffuse'] = True global_prefs['get_specular'] = False global_prefs['get_emissive'] = False global_prefs['get_alpha'] = True global_prefs['get_ambient'] = False global_prefs['get_shininess'] = True global_prefs['color_from_face'] = True global_prefs['log to blender'] = True Blender.Window.WaitCursor(True) Blender.Window.EditMode(0) FF.add_file_to_search_path(filename) if global_prefs['verbose'] >= 1: print 'Pass 1: Loading.' print load_time = Blender.sys.time() db = Database(filename,grr) db.parse() load_time = Blender.sys.time() - load_time if global_prefs['verbose'] >= 1: print print 'Pass 2: Importing to Blender.' print import_time = Blender.sys.time() db.blender_import() if global_prefs['attrib']: print "reading attribute files" db.read_attribute_files() Blender.Window.ViewLayer(range(1,21)) update_scene(db.scene,[]) import_time = Blender.sys.time() - import_time if global_prefs['verbose'] >= 1: print 'Done.' print print 'Time to parse file: %.3f seconds' % load_time print 'Time to import to blender: %.3f seconds' % import_time print 'Total time: %.3f seconds' % (load_time + import_time) Blender.Window.WaitCursor(False) def setimportscale(ID,val): global global_prefs global_prefs['scale'] = val def setBpath(fname): global_prefs['fltfile'] = fname def event(evt,val): pass def but_event(evt): global FLTBaseLabel global FLTBaseString global FLTBaseChooser global FLTExport global FLTClose global FLTDoXRef global FLTShadeImport global FLTAttrib #Import DB if evt == 1: if global_prefs['verbose'] >= 1: print print 'OpenFlight Importer' print 'Version:', __version__ print 'Author: Greg MacDonald, Campbell Barton, Geoffrey Bantle' print __url__[2] print GRR = GlobalResourceRepository() select_file(global_prefs['fltfile'], GRR) #choose base path for export if evt == 4: Blender.Window.FileSelector(setBpath, "DB Root", global_prefs['fltfile']) #Import custom shading? if evt == 9: global_prefs['smoothshading'] = FLTShadeImport.val #Import Image attribute files if evt == 10: global_prefs['attrib'] = FLTAttrib.val #export XRefs if evt == 13: global_prefs['doxrefs'] = FLTDoXRef.val if evt == 2: Draw.Exit() from Blender.BGL import * from Blender import Draw def gui(): global FLTBaseLabel global FLTBaseString global FLTBaseChooser global FLTExport global FLTClose global FLTDoXRef global FLTShadeImport global FLTAttrib glClearColor(0.772,0.832,0.847,1.0) glClear(GL_COLOR_BUFFER_BIT) areas = Blender.Window.GetScreenInfo() curarea = Blender.Window.GetAreaID() curRect = None for area in areas: if area['id'] == curarea: curRect = area['vertices'] break width = curRect[2] - curRect[0] height = curRect[3] - curRect[1] cx = 50 cy = height - 80 FLTBaseLabel = Draw.Label("Base file:",cx,cy,100,20) FLTBaseString = Draw.String("",3,cx+100,cy,300,20,global_prefs['fltfile'],255,"Root DB file") FLTBaseChooser = Draw.PushButton("...",4,cx+400,cy,20,20,"Choose Folder") cy = cy-40 FLTScale = Draw.Number("Import Scale",14,cx,cy,220,20,global_prefs['scale'],0.0,100.0,"Export scaleing factor",setimportscale) cy = cy-40 FLTDoXRef = Draw.Toggle("Import XRefs", 13,cx,cy,220,20,global_prefs['doxrefs'],"Import External references") cy = cy-40 FLTShadeImport = Draw.Toggle("Import Custom Shading",9,cx,cy,220,20,global_prefs['smoothshading'],"Import custom shading via edgesplit modifiers") cy = cy-40 FLTAttrib = Draw.Toggle("Import Attribute Files", 10,cx,cy,220,20,global_prefs['attrib'],"Import Image Attribute files") cy = cy - 40 FLTExport = Draw.PushButton("Import",1,cx,20,100,20,"Import FLT Database") FLTClose = Draw.PushButton("Close",2,cx+120,20,100,20,"Close Window") Draw.Register(gui,event,but_event)