blender/release/scripts/flt_export.py
2008-08-09 10:12:59 +00:00

1697 lines
51 KiB
Python

#!BPY
""" Registration info for Blender menus:
Name: 'OpenFlight (.flt)...'
Blender: 245
Group: 'Export'
Tip: 'Export to OpenFlight v16.0 (.flt)'
"""
__author__ = "Greg MacDonald, Geoffrey Bantle"
__version__ = "2.0 11/21/07"
__url__ = ("blender", "blenderartists.org", "Author's homepage, http://sourceforge.net/projects/blight/")
__bpydoc__ = """\
This script exports v16.0 OpenFlight files. OpenFlight is a
registered trademark of MultiGen-Paradigm, Inc.
Feature overview and more availible at:
http://wiki.blender.org/index.php/Scripts/Manual/Export/openflight_flt
"""
# flt_export.py is an OpenFlight exporter 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
from Blender import Modifier
import os.path
import flt_properties
import flt_defaultp as defaultp
from flt_filewalker import FltOut
from flt_filewalker import FileFinder
from flt_properties import *
import shutil
import trace
import sys
FF = FileFinder()
records = process_recordDefs()
class ExporterOptions:
def read_state(self):
reg = Blender.Registry.GetKey('flt_export',1)
if reg:
for key in self.state:
if reg.has_key(key):
self.state[key] = reg[key]
def write_state(self):
d = dict()
for key in self.state:
d[key] = self.state[key]
Blender.Registry.SetKey('flt_export', d, 1)
def __init__(self):
self.verbose = 1
self.tolerance = 0.001
self.writevcol = True
self.state = {'export_shading' : 0,
'shading_default' : 45,
'basepath' : os.path.dirname(Blender.Get('filename')),
'scale': 1.0,
'doxrefs' : 1,
'attrib' : 0,
'copytex' : 0,
'transform' : 0,
'xapp' : 1}
#default externals path
if(os.path.exists(os.path.join(self.state['basepath'],'externals'))):
self.state['externalspath'] = os.path.join(self.state['basepath'],'externals')
else:
self.state['externalspath'] = self.state['basepath']
if(os.path.exists(os.path.join(self.state['basepath'],'textures'))):
self.state['texturespath'] = os.path.join(self.state['basepath'],'textures')
else:
self.state['texturespath'] = self.state['basepath']
self.state['xappath'] = ''
self.read_state() #read from registry
options = ExporterOptions()
tex_files = dict() #a list of (possibly) modified texture path names
tex_layers = ['Layer0', 'Layer1', 'Layer2', 'Layer3', 'Layer4', 'Layer5', 'Layer6', 'Layer7']
mask = 2147483648
mtexmasks = []
for i in xrange(7):
mtexmasks.append(mask)
mask = mask / 2
FLOAT_TOLERANCE = options.tolerance
#need to move all this stuff to flt_properties.py.
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]]
alltypes = [2,4,14,11,73,63,111]
childtypes = {
2 : [111,2,73,4,14,63],
4 : [111],
73 : [111,2,73,4,14,63],
63 : [],
14 : [111,2,73,4,14,63],
111 : []
}
recordlen = {
2: 44,
4: 28,
73: 80,
63: 216,
14: 384,
111: 156
}
def is_identity(m):
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'
# 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]
class VertexDesc:
def __init__(self, co=None, no=None, uv=None, fltindex=None,cindex=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
if cindex: self.cindex = cindex
else: self.cindex = 127
self.fltindex = fltindex
self.accum = 0
class shadowVert:
def __init__(self,bvert,object,world,normal):
global options
self.co = Blender.Mathutils.Vector(bvert.co[0],bvert.co[1],bvert.co[2])
#if world:
# vec = self.co
# vec = Blender.Mathutils.Vector(vec[0] * options.scale, vec[1] * options.scale, vec[2] * options.scale) #scale
# self.co = Blender.Mathutils.TranslationMatrix(vec) * (self.co * object.getMatrix('worldspace'))
if normal:
#if world:
# self.no = Blender.Mathutils.Vector(normal * object.getMatrix('worldspace')).normalize()
#else:
self.no = Blender.Mathutils.Vector(normal[0],normal[1],normal[2])
else:
#if world:
#self.no = Blender.Mathutils.Vector(bvert.no * object.getMatrix('worldspace')).normalize()
#else:
self.no = Blender.Mathutils.Vector(bvert.no[0],bvert.no[1],bvert.no[2])
#do scaling factor
#if options.scale != 1.0:
#self.co[0] = self.co[0] * options.scale
#self.co[1] = self.co[1] * options.scale
#self.co[2] = self.co[2] * options.scale
self.index = bvert.index
class GlobalResourceRepository:
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, object, mesh, face, vfindex, uvok,cindex):
flatShadeNorm = None
vno = None
if type(face) is list:
vertex = face[vfindex]
elif str(type(face)) == "<type " + "'Blender MVert'>":
vertex = face
vno = Blender.Mathutils.Vector(0.0,0.0,1.0)
elif str(type(face)) == "<type " + "'Blender MEdge'>":
if vfindex == 1:
vertex = face.v1
elif vfindex == 2:
vertex = face.v2
elif str(type(face)) == "<type " + "'Blender MFace'>":
if not face.smooth:
flatShadeNorm = face.no
vertex = face.v[vfindex]
else:
return None
if not self.namehash.has_key(object.name):
self.namehash[object.name] = dict()
indexhash = self.namehash[object.name]
#export in global space? THIS HAS BEEN MADE REDUNDANT... REMOVE ME
if not options.state['transform']:
vertex = shadowVert(vertex,object,True,flatShadeNorm)
else:
vertex = shadowVert(vertex,object,False,flatShadeNorm)
if vno:
vertex.no = vno
#Check to see if this vertex has been visited before. If not, add
if not indexhash.has_key(vertex.index):
if uvok:
newvdesc = VertexDesc(vertex.co, vertex.no, face.uv[vfindex], self.nextvindex,cindex=cindex)
else:
newvdesc = VertexDesc(co=vertex.co, no=vertex.no,fltindex=self.nextvindex,cindex=cindex)
indexhash[vertex.index] = [newvdesc]
self.vertex_lst.append(newvdesc)
self.nextvindex = self.nextvindex + 1
return newvdesc.fltindex
else:
desclist = indexhash[vertex.index]
if uvok:
faceu = face.uv[vfindex][0]
facev = face.uv[vfindex][1]
else:
faceu = 0.0
facev = 0.0
for vdesc in desclist:
if\
abs(vdesc.x - vertex.co[0]) > FLOAT_TOLERANCE or\
abs(vdesc.y - vertex.co[1]) > FLOAT_TOLERANCE or\
abs(vdesc.z - vertex.co[2]) > FLOAT_TOLERANCE or\
abs(vdesc.nx - vertex.no[0]) > FLOAT_TOLERANCE or\
abs(vdesc.ny - vertex.no[1]) > FLOAT_TOLERANCE or\
abs(vdesc.nz - vertex.no[2]) > FLOAT_TOLERANCE or\
vdesc.cindex != cindex or\
abs(vdesc.u - faceu) > FLOAT_TOLERANCE or\
abs(vdesc.v - facev) > FLOAT_TOLERANCE:
pass
else:
return vdesc.fltindex
#if we get this far, we didnt find a match. Add a new one and return
if uvok:
newvdesc = VertexDesc(vertex.co, vertex.no, face.uv[vfindex], self.nextvindex,cindex=cindex)
else:
newvdesc = VertexDesc(co=vertex.co, no=vertex.no,fltindex=self.nextvindex,cindex=cindex)
indexhash[vertex.index].append(newvdesc)
self.vertex_lst.append(newvdesc)
self.nextvindex = self.nextvindex + 1
return newvdesc.fltindex
def request_texture_index(self, image):
match = None
for i in xrange(len(self.texture_lst)):
if self.texture_lst[i] != image:
continue
match = i
break
if match != None:
return match
else:
self.texture_lst.append(image)
return len(self.texture_lst) - 1
def request_texture_filename(self, index):
return Blender.sys.expandpath(self.texture_lst[index].getFilename())
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):
#Vertex handling
self.vertex_lst = []
self.nextvindex = 0
self.namehash = dict()
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:
print '\t' * level[0], self.name, self.object.type
level[0] += 1
self.children.reverse()
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_push_extension(self):
self.header.fw.write_short(21)
self.header.fw.write_ushort(24)
self.header.fw.pad(18)
self.header.fw.write_ushort(0)
def write_pop_extension(self):
self.header.fw.write_short(22)
self.header.fw.write_ushort(24)
self.header.fw.pad(18)
self.header.fw.write_ushort(0)
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
def write_comment(self,comment):
length = len(comment)
if length >= 65535:
comment = comment[:65530]
length = len(comment)
pad = (length % 4) - 1
if pad < 0:
pad = None
reclength = length + 5
else:
reclength = length + 5 + pad
self.header.fw.write_short(31) # Comment Opcode
self.header.fw.write_ushort(reclength) # Length of record is 4 + comment length + null terminator + pad
self.header.fw.write_string(comment,length+1) # comment + zero terminator
if pad:
self.header.fw.pad(pad) # pad to multiple of 4 bytes
# Initialization sets up basic tree structure.
def __init__(self, parent, header, object,props):
global options
self.header = header
self.object = object
if object:
self.name = self.object.name
if not options.state['transform']:
oloc = Blender.Mathutils.Vector(object.getLocation('worldspace'))
vec = Blender.Mathutils.Vector(oloc[0] * options.state['scale'], oloc[1] * options.state['scale'], oloc[2] * options.state['scale']) #scale
self.matrix = self.object.getMatrix('worldspace') * Blender.Mathutils.TranslationMatrix(vec - oloc)
else:
self.matrix = self.object.getMatrix('localspace') #do matrix mult here.
self.props = props
self.child_objects = self.header.parenthash[object.name]
else:
self.name = 'no name'
self.matrix = None
self.props = None
self.child_objects = self.header.child_objects
self.children = []
self.parent = parent
if parent:
parent.children.append(self)
# Spawn children.
for child in self.child_objects:
if(not child.restrictDisplay):
childprops = None
ftype = None
if not child.properties.has_key('FLT'):
if child.type == 'Empty':
if child.DupGroup:
childprops = FLTXRef.copy()
ftype = 63
else:
childprops = FLTGroup.copy()
ftype = 2
elif child.type == 'Mesh':
if self.header.childhash[child.name] or not child.parent:
childprops = FLTGroup.copy()
ftype = 2
else:
childprops = FLTObject.copy()
ftype = 4
else:
childprops = dict()
for prop in child.properties['FLT']:
childprops[prop] = child.properties['FLT'][prop]
ftype = child.properties['FLT']['type']
if ftype in self.childtypes and ftype in alltypes:
Newnode = FLTNode(self,header,child,childprops,ftype)
if child.type == 'Mesh':
self.header.mnodes.append(Newnode)
class FaceDesc:
def __init__(self):
self.vertex_index_lst = []
self.mface = None
self.texture_index = 65535
self.material_index = 65535
self.color_index = 127
self.renderstyle = 0
self.twoside = 0
self.name = None #uses next FLT name if not set... fix resolution of conflicts!
self.billboard = 0
#Multi-Tex info. Dosn't include first UV Layer!
self.uvlayer = list() #list of list of tuples for UV coordinates.
self.images = list() #list of texture indices for seperate UV layers
self.mtex = list()
self.subface = None #can either be 'Push' or 'Pop'
def edge_get_othervert(vert, edge):
if edge.v1 == vert:
return edge.v2
elif edge.v2 == vert:
return edge.v1
return None
class FLTNode(Node):
def walkLoop(self, targetvert, startvert, startedge, edgelist, visited, vedges, closeloop):
loop = [targetvert]
curvert = startvert
curedge = startedge
visited[curedge] = True
found = False
while not found:
loop.append(curvert)
disk = vedges[curvert.index]
if not closeloop:
if len(disk) == 1:
visited[curedge] = True
break
else:
if len(disk) < 2: #what?
visited[curedge] = True
return None
if disk[0] == curedge:
curedge = disk[1]
else:
curedge = disk[0]
if curedge.v1.index == curvert.index:
curvert = curedge.v2
else:
curvert = curedge.v1
visited[curedge] = True
if(curvert == targetvert):
found = True
return loop
def buildVertFaces(self,vertuse):
for vert in self.exportmesh.verts:
if vertuse[vert.index][0] == False and vertuse[vert.index][1] == 0:
face_desc = FaceDesc()
face_desc.vertex_index_lst.append(self.header.GRR.request_vertex_index(self.object, self.exportmesh, vert, 0,0,0))
face_desc.renderstyle = 3
face_desc.color_index = 227
self.face_lst.append(face_desc)
def buildEdgeFaces(self,vertuse):
for edge in self.exportmesh.edges:
v1 = vertuse[edge.v1.index]
v2 = vertuse[edge.v2.index]
if v1[0] == False and v2[0] == False:
if v1[1] == 1 and v2[1] == 1:
face_desc = FaceDesc()
face_desc.vertex_index_lst.append(self.header.GRR.request_vertex_index(self.object, self.exportmesh, edge, 1, 0,0))
face_desc.vertex_index_lst.append(self.header.GRR.request_vertex_index(self.object, self.exportmesh, edge, 2, 0,0))
face_desc.renderstyle = 3
face_desc.color_index = 227
self.face_lst.append(face_desc)
def vertwalk(self, startvert, loop, disk, visited):
visited[startvert] = True
for edge in disk[startvert]:
othervert = edge_get_othervert(startvert, edge)
if not visited[othervert]:
loop.append(othervert)
self.vertwalk(othervert,loop,disk,visited)
def buildOpenFacesNew(self, vertuse):
wireverts = list()
wiredges = list()
visited = dict()
disk = dict()
loops = list()
for edge in self.exportmesh.edges:
v1 = vertuse[edge.v1.index]
v2 = vertuse[edge.v2.index]
if v1[0] == False and v2[0] == False:
if v1[1] < 3 and v2[1] < 3:
wireverts.append(edge.v1)
wireverts.append(edge.v2)
wiredges.append(edge)
#build disk data
for vert in wireverts:
visited[vert] = False
disk[vert] = list()
for edge in wiredges:
disk[edge.v1].append(edge)
disk[edge.v2].append(edge)
#first pass: do open faces
for vert in wireverts:
if not visited[vert] and vertuse[vert.index][1] == 1:
loop = list()
done = 0
startvert = vert
while not done:
done = 1
visited[startvert] = True
loop.append(startvert)
for edge in disk[startvert]:
othervert = edge_get_othervert(startvert, edge)
if not visited[othervert]:
done = 0
startvert = othervert
break
if len(loop) > 2: loops.append( ('Open', loop) )
for vert in wireverts:
if not visited[vert]:
loop = list()
done = 0
startvert = vert
while not done:
done = 1
visited[startvert] = True
loop.append(startvert)
for edge in disk[startvert]:
othervert = edge_get_othervert(startvert,edge)
if not visited[othervert]:
done = 0
startvert = othervert
break
if len(loop) > 2: loops.append( ('closed', loop) )
#now go through the loops and append.
for l in loops:
(ftype, loop) = l
face_desc = FaceDesc()
for i,vert in enumerate(loop):
face_desc.vertex_index_lst.append(self.header.GRR.request_vertex_index(self.object,self.exportmesh,loop,i,0,0))
if ftype == 'closed':
face_desc.renderstyle = 2
else:
face_desc.renderstyle = 3
face_desc.color_index = 227
self.face_lst.append(face_desc)
def sortFLTFaces(self,a,b):
aindex = a.getProperty("FLT_ORIGINDEX")
bindex = b.getProperty("FLT_ORIGINDEX")
if aindex > bindex:
return 1
elif aindex < bindex:
return -1
return 0
def buildNormFaces(self):
global options
meshlayers = self.exportmesh.getUVLayerNames()
oldlayer = self.exportmesh.activeUVLayer
uvok = 0
subfaceok = 0
subfacelevel = 0
#special case
if self.exportmesh.faceUV and len(meshlayers) == 1:
uvok = 1
elif self.exportmesh.faceUV and tex_layers[0] in meshlayers:
self.exportmesh.activeUVLayer = tex_layers[0]
uvok = 1
#Sort faces according to the subfaces/FLT indices
if "FLT_ORIGINDEX" in self.exportmesh.faces.properties and "FLT_SFLEVEL" in self.exportmesh.faces.properties:
exportfaces = list()
for face in self.exportmesh.faces:
exportfaces.append(face)
exportfaces.sort(self.sortFLTFaces)
subfaceok = 1
else:
exportfaces = self.exportmesh.faces
# Faces described as lists of indices into the GRR's vertex_lst.
for face in exportfaces:
descs = list()
#first we export the face as normal
index_lst = []
face_v = face.verts
for i, v in enumerate(face_v):
index_lst.append(self.header.GRR.request_vertex_index(self.object,self.exportmesh,face,i,uvok,0))
face_desc = FaceDesc()
face_desc.vertex_index_lst = index_lst
face_desc.mface = face
descs.append(face_desc)
#deal with subfaces
if subfaceok:
fsflevel = face.getProperty("FLT_SFLEVEL")
for face_desc in descs:
if fsflevel > subfacelevel:
face_desc.subface = 'Push'
subfacelevel = fsflevel
elif fsflevel < subfacelevel:
face_desc.subface = 'Pop'
subfacelevel = fsflevel
if uvok and (face.mode & Blender.Mesh.FaceModes.TWOSIDE):
face_desc.renderstyle = 1
for face_desc in descs:
if "FLT_COL" in self.exportmesh.faces.properties:
color_index = face.getProperty("FLT_COL")
# if(color_index < 127):
# color_index = 127 #sanity check for face color indices
if(color_index == 0):
color_index = 127
face_desc.color_index = color_index
else:
face_desc.color_index = 127
if "FLT_ID" in self.exportmesh.faces.properties:
face_desc.name = face.getProperty("FLT_ID") #need better solution than this.
if uvok and face.mode & Blender.Mesh.FaceModes["BILLBOARD"]:
face_desc.billboard = 1
self.face_lst.append(face_desc)
if uvok:
self.exportmesh.activeUVLayer = oldlayer
def buildTexData(self):
meshlayers = self.exportmesh.getUVLayerNames()
oldlayer = self.exportmesh.activeUVLayer
uvok = 0
if self.exportmesh.faceUV and len(meshlayers) == 1:
uvok = 1
if self.exportmesh.faceUV and tex_layers[0] in meshlayers:
self.exportmesh.activeUVLayer = tex_layers[0]
uvok = 1
if uvok:
#do base layer. UVs have been stored on vertices directly already.
for i, face in enumerate(self.face_lst):
if face.mface:
mface = face.mface
image = mface.image
if image != None and mface.mode & Blender.Mesh.FaceModes["TEX"]:
index = self.header.GRR.request_texture_index(image)
else:
index = -1
face.texture_index = index
for i, face in enumerate(self.face_lst):
if face.mface:
mface_v = face.mface.v
for v in mface_v:
face.uvlayer.append([])
for layername in tex_layers[1:]:
if layername in meshlayers:
self.exportmesh.activeUVLayer=layername
for i, face in enumerate(self.face_lst):
if face.mface:
face.mtex.append(layername)
mface = face.mface
mface_v = mface.v
image = mface.image
if image != None and mface.mode & Blender.Mesh.FaceModes["TEX"]:
index = self.header.GRR.request_texture_index(image)
face.images.append(index)
else:
face.images.append(-1)
for j, v in enumerate(mface_v):
face.uvlayer[j].append(tuple(mface.uv[j]))
if uvok:
self.exportmesh.activeUVLayer = oldlayer
def blender_export(self):
global options
Node.blender_export(self)
if self.opcode == 111:
self.exportmesh = Blender.Mesh.New()
self.exportmesh.getFromObject(self.object.name)
for vert in self.exportmesh.verts:
if not options.state['transform']:
vec = vert.co
vec = Blender.Mathutils.Vector(vec[0] * options.state['scale'], vec[1] * options.state['scale'], vec[2] * options.state['scale']) #scale
vert.co = Blender.Mathutils.TranslationMatrix(vec) * (vert.co * self.object.getMatrix('worldspace'))
if options.state['scale'] != 1.0:
vert.co = vert.co * options.state['scale']
if("FLT_VCOL") in self.mesh.verts.properties:
for v in self.exportmesh.verts:
self.vert_lst.append(self.header.GRR.request_vertex_index(self.object,self.exportmesh,v,0,0,v.getProperty("FLT_VCOL")))
else:
for v in self.mesh.verts:
self.vert_lst.append(self.header.GRR.request_vertex_index(self.object,self.mesh,v,0,0,127))
elif self.mesh:
orig_mesh = self.object.getData(mesh=True)
self.exportmesh = Blender.Mesh.New()
default = None
if options.state['export_shading']:
mods = self.object.modifiers
hasedsplit = False
for mod in mods:
if mod.type == Blender.Modifier.Types.EDGESPLIT:
hasedsplit = True
break
if not hasedsplit:
default = mods.append(Modifier.Types.EDGESPLIT)
default[Modifier.Settings.EDGESPLIT_ANGLE] = options.state['shading_default']
default[Modifier.Settings.EDGESPLIT_FROM_ANGLE] = True
default[Modifier.Settings.EDGESPLIT_FROM_SHARP] = False
self.object.makeDisplayList()
self.exportmesh.getFromObject(self.object.name)
#recalculate vertex positions
for vert in self.exportmesh.verts:
if not options.state['transform']:
vec = vert.co
vec = Blender.Mathutils.Vector(vec[0] * options.state['scale'], vec[1] * options.state['scale'], vec[2] * options.state['scale']) #scale
vert.co = Blender.Mathutils.TranslationMatrix(vec) * (vert.co * self.object.getMatrix('worldspace'))
if options.state['scale'] != 1.0:
vert.co = vert.co * options.state['scale']
flipped = self.object.getMatrix('worldspace').determinant()
if not options.state['transform']:
self.exportmesh.calcNormals()
if default:
#remove modifier from list
mods.remove(default)
self.object.makeDisplayList()
#build some adjacency data
vertuse = list()
wiredges = list()
openends = list()
for v in self.exportmesh.verts:
vertuse.append([False,0])
#build face incidence data
for face in self.exportmesh.faces:
for i, v in enumerate(face.verts):
vertuse[v.index][0] = True
for edge in self.exportmesh.edges: #count valance
vertuse[edge.v1.index][1] = vertuse[edge.v1.index][1] + 1
vertuse[edge.v2.index][1] = vertuse[edge.v2.index][1] + 1
#create all face types
self.buildVertFaces(vertuse)
self.buildEdgeFaces(vertuse)
self.buildOpenFacesNew(vertuse)
self.buildNormFaces()
self.buildTexData()
if not options.state['transform']:
if flipped < 0:
for vdesc in self.header.GRR.vertex_lst:
vdesc.accum = 0
for face in self.face_lst:
face.vertex_index_lst.reverse()
for vert in face.vertex_index_lst:
self.header.GRR.vertex_lst[vert].accum = 1
for vdesc in self.header.GRR.vertex_lst:
if vdesc.accum:
vdesc.nx = vdesc.nx * -1
vdesc.ny = vdesc.ny * -1
vdesc.nz = vdesc.nz * -1
def write_faces(self):
sublevel = 0
for face_desc in self.face_lst:
if face_desc.name:
face_name = face_desc.name
else:
face_name = self.header.GRR.new_face_name()
#grab the alpha value.
alpha = 0
if face_desc.texture_index > -1:
try:
typestring = os.path.splitext(self.header.GRR.texture_lst[face_desc.texture_index].getFilename())[1]
if typestring == '.inta' or typestring == '.rgba':
alpha = 1
except:
pass
if not alpha:
for index in face_desc.images:
try:
typestring = os.path.splitext(self.header.GRR.texture_lst[index].getFilename())[1]
if typestring == '.inta' or typestring == '.rgba':
alpha = 1
except:
pass
if face_desc.billboard:
alpha = 2
if face_desc.subface:
if face_desc.subface == 'Push':
self.header.fw.write_short(19)
self.header.fw.write_ushort(4)
sublevel += 1
else:
self.header.fw.write_short(20)
self.header.fw.write_ushort(4)
sublevel -= 1
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(face_desc.renderstyle) # 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(alpha) # Template
self.header.fw.write_short(-1) # Detail tex pat index
if face_desc.texture_index == -1:
self.header.fw.write_ushort(65535)
else:
self.header.fw.write_ushort(face_desc.texture_index) # Tex pattern index
if face_desc.material_index == -1:
self.header.fw.write_ushort(65535)
else:
self.header.fw.write_ushort(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(0) # Flags
self.header.fw.write_uchar(2) # Light mode
#self.header.fw.write_uchar(3) # Light mode
self.header.fw.pad(7) # Reserved
self.header.fw.write_uint(0) # Packed color
self.header.fw.write_uint(0) # 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)
#Write Multitexture field if appropriate
mtex = len(face_desc.mtex)
if mtex:
uvmask = 0
for layername in face_desc.mtex:
mask = mtexmasks[tex_layers.index(layername)-1]
uvmask |= mask
self.header.fw.write_ushort(52) # MultiTexture Opcode
self.header.fw.write_ushort(8 + (mtex * 8)) # Length
self.header.fw.write_uint(uvmask) # UV mask
for i in xrange(mtex):
if face_desc.images[i] == -1:
self.header.fw.write_ushort(65535)
else:
self.header.fw.write_ushort(face_desc.images[i]) # Tex pattern index
self.header.fw.write_ushort(0) # Tex effect
self.header.fw.write_ushort(0) # Tex Mapping index
self.header.fw.write_ushort(0) # Tex data. User defined
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)
#UV list record
if mtex:
#length = 8 + (numverts * multitex * 8)
self.header.fw.write_ushort(53) # UV List Ocode
self.header.fw.write_ushort(8 + (num_verts*mtex*8)) # Record Length
self.header.fw.write_uint(uvmask) # UV mask
for i, vert_index in enumerate(face_desc.vertex_index_lst):
for uv in face_desc.uvlayer[i]:
self.header.fw.write_float(uv[0]) #U coordinate
self.header.fw.write_float(uv[1]) #V coordinate
self.write_pop()
#clean up faces at the end of meshes....
if sublevel:
self.header.fw.write_short(20)
self.header.fw.write_ushort(4)
def write_lps(self):
# Vertex list record
self.write_push()
self.header.fw.write_short(72) # Vertex list opcode
num_verts = len(self.vert_lst)
self.header.fw.write_ushort(4*num_verts+4) # Length of record
for vert_index in self.vert_lst:
# Offset into vertex palette
self.header.fw.write_int(vert_index*64+8)
self.write_pop()
def write(self):
self.header.fw.write_short(self.opcode)
self.header.fw.write_ushort(recordlen[self.opcode])
exportdict = FLT_Records[self.opcode].copy()
if self.object:
self.props['3t8!id'] = self.object.name[:7]
for key in exportdict.keys():
if self.props.has_key(key):
exportdict[key] = self.props[key]
if self.opcode == 63 and options.state['externalspath']:
try:
exportdict['3t200!filename'] = os.path.join(options.state['externalspath'],self.object.DupGroup.name+'.flt').replace("\\", "/")
self.header.xrefnames.append(self.object.DupGroup.name)
except:
pass
for key in records[self.opcode]:
(ftype,length,propname) = records[self.opcode][key]
write_prop(self.header.fw,ftype,exportdict[propname],length)
if self.props.has_key('comment'):
self.write_comment(self.props['comment'])
if self.object and self.object.properties.has_key('FLT') and self.object.properties['FLT'].has_key('EXT'):
datalen = len(self.object.properties['FLT']['EXT']['data'])
self.write_push_extension()
self.header.fw.write_short(100)
self.header.fw.write_ushort(24 + datalen)
for key in records[100]:
(ftype,length,propname) = records[100][key]
write_prop(self.header.fw,ftype,self.object.properties['FLT']['EXT'][propname],length)
#write extension data
for i in xrange(datalen):
self.header.fw.write_uchar(struct.unpack('>B', struct.pack('>B', self.object.properties['FLT']['EXT']['data'][i]))[0])
self.write_pop_extension()
self.write_longid(self.name) #fix this!
if options.state['transform'] or self.opcode == 63:
#writing transform matrix....
self.write_matrix()
if self.opcode == 111:
self.write_lps()
elif self.face_lst != [] or self.children:
self.write_push()
if self.face_lst != []:
#self.write_push()
self.write_faces()
#self.write_pop()
if self.children:
#self.write_push()
for child in self.children:
child.write()
#self.write_pop()
self.write_pop()
def __init__(self, parent, header, object,props,ftype):
self.opcode = ftype #both these next two lines need to be in the node class....
self.childtypes = childtypes[self.opcode]
Node.__init__(self, parent, header, object,props)
self.face_lst = []
self.vert_lst = [] #for light points.
self.mesh = None
self.uvlayer = 0
self.flipx = False
self.flipy = False
self.flipz = False
if self.object.type == 'Mesh':
self.mesh = self.object.getData(mesh=True)
if(self.mesh.faceUV):
self.uvLayer = len(self.mesh.getUVLayerNames())
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.write_int(100) # database origin type
self.fw.pad(88)
try:
self.fw.write_double(self.header.scene.properties['FLT']['origin lat']) #database origin lattitude
except:
self.fw.write_double(0)
try:
self.fw.write_double(self.header.scene.properties['FLT']['origin lon']) #database origin longitude
except:
self.fw.write_double(0)
self.fw.pad(32)
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(1 << 14) # Frozen Normal
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(4)
self.fw.write_uint(desc.cindex)
self.fw.pad(4)
def write_tex_pal(self):
if options.verbose >= 2:
print 'Writing texture palette.'
# Write record for texture palette
for i, img in enumerate(self.GRR.texture_lst):
filename = tex_files[img.name]
self.fw.write_short(64) # Texture palette opcode.
self.fw.write_short(216) # Length of record
self.fw.write_string(filename, 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)
try:
cpalette = self.scene.properties['FLT']['Color Palette']
except:
cpalette = defaultp.pal
count = len(cpalette)
for i in xrange(count):
color = struct.unpack('>BBBB',struct.pack('>i',cpalette[i]))
self.fw.write_uchar(color[3]) # alpha
self.fw.write_uchar(color[2]) # b
self.fw.write_uchar(color[1]) # g
self.fw.write_uchar(color[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()
self.write_push()
for child in self.children:
child.write()
self.write_pop()
def export_textures(self,texturepath):
for i in xrange(self.GRR.texture_count()):
texture = self.GRR.texture_lst[i]
if options.state['copytex']:
filename = os.path.normpath(os.path.join(options.state['texturespath'], os.path.basename(self.GRR.request_texture_filename(i))))
else:
filename = os.path.normpath(self.GRR.request_texture_filename(i))
tex_files[texture.name] = filename
def blender_export(self):
Node.blender_export(self)
self.export_textures(self)
return self.xrefnames
def __init__(self, scene, fw):
self.fw = fw
self.opcode = 1
self.childtypes = [73,14,2,63]
self.scene = scene
self.childhash = dict()
self.parenthash = dict()
self.child_objects = list()
self.mnodes = list()
self.xrefnames = list()
for i in self.scene.objects:
self.parenthash[i.name] = list()
self.childhash[i.name] = False
for i in self.scene.objects:
if i.parent:
self.childhash[i.parent.name] = True
self.parenthash[i.parent.name].append(i)
else:
self.child_objects.append(i)
self.GRR = GlobalResourceRepository()
Node.__init__(self, None, self, None,None)
def write_attribute_files():
for imgname in tex_files:
blentex = Blender.Image.Get(imgname)
exportdict = FLT_Records['Image'].copy()
if blentex.properties.has_key('FLT'):
for key in exportdict.keys():
if blentex.properties.has_key(key):
exportdict[key] = blentex.properties['FLT'][key]
# ClampX/Y override
if blentex.clampX:
exportdict['11i!WrapU'] = 1
if blentex.clampY:
exportdict['12i!WrapV'] = 1
exportdict['16i!Enviorment'] = 0
# File type
typecode = 0
try:
typestring = os.path.splitext(blentex.getFilename())[1]
if typestring == '.rgba':
typecode = 5
elif typestring == '.rgb':
typecode = 4
elif typestring == '.inta':
typecode = 3
elif typestring == '.int':
typecode = 2
except:
pass
exportdict['7i!File Format'] = typecode
fw = FltOut(tex_files[imgname] + '.attr')
size = blentex.getSize()
fw.write_int(size[0])
fw.write_int(size[1])
for key in records['Image']:
(ftype,length,propname) = records['Image'][key]
write_prop(fw,ftype,exportdict[propname],length)
fw.close_file()
#globals used by the scene export function
exportlevel = None
xrefsdone = None
def dbexport_internal(scene):
global exportlevel
global xrefsdone
global options
if exportlevel == 0 or not options.state['externalspath']:
fname = os.path.join(options.state['basepath'],scene.name + '.flt')
else:
fname = os.path.join(options.state['externalspath'],scene.name + '.flt')
fw = FltOut(fname)
db = Database(scene,fw)
if options.verbose >= 1:
print 'Pass 1: Exporting ', scene.name,'.flt from Blender.\n'
xreflist = db.blender_export()
if options.verbose >= 1:
print 'Pass 2: Writing %s\n' % fname
db.write()
fw.close_file()
if options.state['doxrefs']:
for xname in xreflist:
try:
xrefscene = Blender.Scene.Get(xname)
except:
xrefscene = None
if xrefscene and xname not in xrefsdone:
xrefsdone.append(xname)
exportlevel+=1
dbexport_internal(xrefscene)
exportlevel-=1
return fname
#main database export function
def dbexport():
global exportlevel
global xrefsdone
exportlevel = 0
xrefsdone = list()
Blender.Window.WaitCursor(True)
time1 = Blender.sys.time() # Start timing
if options.verbose >= 1:
print '\nOpenFlight Exporter'
print 'Version:', __version__
print 'Author: Greg MacDonald, Geoffrey Bantle'
print __url__[2]
print
fname = dbexport_internal(Blender.Scene.GetCurrent())
if options.verbose >=1:
print 'Done in %.4f sec.\n' % (Blender.sys.time() - time1)
Blender.Window.WaitCursor(False)
#optional: Copy textures
if options.state['copytex']:
for imgname in tex_files:
#Check to see if texture exists in target directory
if not os.path.exists(tex_files[imgname]):
#Get original Blender file name
origpath = Blender.sys.expandpath(Blender.Image.Get(imgname).getFilename())
#copy original to new
if os.path.exists(origpath):
shutil.copyfile(origpath,tex_files[imgname])
#optional: Write attribute files
if options.state['attrib']:
write_attribute_files()
if options.state['xapp']:
cmd= options.state['xappath'] + " " + fname
status = os.system(cmd)
#Begin UI code
FLTExport = None
FLTClose = None
FLTLabel = None
FLTBaseLabel = None
FLTTextureLabel = None
FLTXRefLabel = None
FLTBaseString = None
FLTTextureString = None
FLTXRefString = None
FLTBasePath = None
FLTTexturePath = None
FLTXRefPath = None
FLTShadeExport = None
FLTShadeDefault = None
FLTCopyTex = None
FLTDoXRef = None
FLTGlobal = None
FLTScale = None
FLTXAPP = None
FLTXAPPath = None
FLTXAPPString = None
FLTXAPPLabel = None
FLTXAPPChooser = None
FLTAttrib = None
FLTWarn = None
def setshadingangle(ID,val):
global options
options.state['shading_default'] = val
def setBpath(fname):
global options
options.state['basepath'] = os.path.dirname(fname)
#update xref and textures path too....
if(os.path.exists(os.path.join(options.state['basepath'],'externals'))):
options.state['externalspath'] = os.path.join(options.state['basepath'],'externals')
if(os.path.exists(os.path.join(options.state['basepath'],'textures'))):
options.state['texturespath'] = os.path.join(options.state['basepath'],'textures')
def setexportscale(ID,val):
global options
options.state['scale'] = val
def setTpath(fname):
global options
options.state['texturespath'] = os.path.dirname(fname)
def setXpath(fname):
global options
options.state['externalspath'] = os.path.dirname(fname)
def setXApath(fname):
global options
options.state['xappath'] = fname
def event(evt, val):
x = 1
def but_event(evt):
global options
global FLTExport
global FLTClose
global FLTLabel
global FLTBaseLabel
global FLTTextureLabel
global FLTXRefLabel
global FLTBaseString
global FLTTextureString
global FLTXRefString
global FLTBasePath
global FLTTexturePath
global FLTXRefPath
global FLTShadeExport
global FLTShadeDefault
global FLTCopyTex
global FLTDoXRef
global FLTGlobal
global FLTScale
global FLTXAPP
global FLTXAPPath
global FLTXAPPString
global FLTXAPPLabel
global FLTXAPPChooser
global FLTAttrib
global FLTWarn
#choose base path for export
if evt == 4:
Blender.Window.FileSelector(setBpath, "DB Root", options.state['basepath'])
#choose XREF path
if evt == 6:
Blender.Window.FileSelector(setXpath,"DB Externals",options.state['externalspath'])
#choose texture path
if evt == 8:
Blender.Window.FileSelector(setTpath,"DB Textures",options.state['texturespath'])
#export shading toggle
if evt == 9:
options.state['export_shading'] = FLTShadeExport.val
#export Textures
if evt == 11:
options.state['copytex']= FLTCopyTex.val
#export XRefs
if evt == 13:
options.state['doxrefs'] = FLTDoXRef.val
#export Transforms
if evt == 12:
options.state['transform'] = FLTGlobal.val
if evt == 14:
options.state['xapp'] = FLTXAPP.val
if evt == 16:
Blender.Window.FileSelector(setXApath,"External Application",options.state['xappath'])
if evt == 20:
options.state['attrib'] = FLTAttrib.val
#Export DB
if evt == 1:
try:
dbexport()
except Exception, inst:
import traceback
FLTWarn = Draw.PupBlock("Export Error", ["See console for output!"])
traceback.print_exception(sys.exc_type, sys.exc_value, sys.exc_traceback)
#exit
if evt == 2:
Draw.Exit()
options.write_state()
from Blender.BGL import *
from Blender import Draw
def gui():
global options
global FLTExport
global FLTClose
global FLTLabel
global FLTBaseLabel
global FLTTextureLabel
global FLTXRefLabel
global FLTBaseString
global FLTTextureString
global FLTXRefString
global FLTBasePath
global FLTTexturePath
global FLTXRefPath
global FLTShadeExport
global FLTShadeDefault
global FLTCopyTex
global FLTDoXRef
global FLTGlobal
global FLTScale
global FLTXAPP
global FLTXAPPath
global FLTXAPPString
global FLTXAPPLabel
global FLTXAPPChooser
global FLTAttrib
glClearColor(0.880,0.890,0.730,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]
#draw from top to bottom....
cx = 50
#Draw Title Bar...
#glRasterPos2d(cx, curRect[3]-100)
#FLTLabel = Draw.Text("FLT Exporter V2.0",'large')
cy = height - 80
FLTBaseLabel = Draw.Label("Base Path:",cx,cy,100,20)
FLTBaseString = Draw.String("",3,cx+100,cy,300,20,options.state['basepath'],255,"Folder to export to")
FLTBaseChooser = Draw.PushButton("...",4,cx+400,cy,20,20,"Choose Folder")
cy = cy-40
#externals path
FLTXRefLabel = Draw.Label("XRefs:",cx,cy,100,20)
FLTXRefString = Draw.String("",5,cx+100,cy,300,20,options.state['externalspath'],255,"Folder for external references")
FLTXRefChooser = Draw.PushButton("...",6,cx+400,cy,20,20,"Choose Folder")
cy = cy-40
#Textures path
FLTTextureLabel = Draw.Label("Textures:",cx,cy,100,20)
FLTTextureString = Draw.String("",7,cx+100,cy,300,20,options.state['texturespath'],255,"Folder for texture files")
FLTTextureChooser = Draw.PushButton("...",8,cx+400,cy,20,20,"Choose Folder")
cy=cy-40
#External application path
FLTXAPPLabel = Draw.Label("XApp:",cx,cy,100,20)
FLTXAPPString = Draw.String("",15,cx+100,cy,300,20,options.state['xappath'],255,"External application to launch when done")
FLTXAPPChooser = Draw.PushButton("...",16,cx+400, cy,20,20,"Choose Folder")
cy = cy-60
#Shading Options
FLTShadeExport = Draw.Toggle("Default Shading",9,cx,cy,100,20,options.state['export_shading'],"Turn on export of custom shading")
FLTShadDefault = Draw.Number("",10,cx + 120,cy,100,20,options.state['shading_default'],0.0,180.0,"Default shading angle for objects with no custom shading assigned",setshadingangle)
cy = cy-40
FLTScale = Draw.Number("Export Scale",14,cx,cy,220,20,options.state['scale'],0.0,100.0,"Export scaling factor",setexportscale)
cy = cy-40
#misc Options
FLTCopyTex = Draw.Toggle("Copy Textures",11,cx,cy,220,20,options.state['copytex'],"Copy textures to folder indicated above")
cy = cy-40
FLTGlobal = Draw.Toggle("Export Transforms",12,cx,cy,220,20,options.state['transform'],"If unchecked, Global coordinates are used (recommended)")
cy = cy-40
FLTDoXRef = Draw.Toggle("Export XRefs", 13,cx,cy,220,20,options.state['doxrefs'],"Export External references (only those below current scene!)")
cy = cy-40
FLTXAPP = Draw.Toggle("Launch External App", 14, cx,cy,220,20,options.state['xapp'],"Launch External Application on export")
cy = cy-40
FLTAttrib = Draw.Toggle("Write Attribute Files", 20, cx, cy, 220,20,options.state['attrib'], "Write Texture Attribute files")
#FLTXAPPATH = Draw.String("",15,cx,cy,300,20,options.xappath,255,"External application path")
#Draw export/close buttons
FLTExport = Draw.PushButton("Export",1,cx,20,100,20,"Export to FLT")
FLTClose = Draw.PushButton("Close", 2, cx+120,20,100,20,"Close window")
Draw.Register(gui,event,but_event)