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