forked from bartvdbraak/blender
Added openflight import/export- Blight v1.2
This commit is contained in:
parent
b3eef4f9f1
commit
41a06c1ab5
763
release/scripts/flt_export.py
Executable file
763
release/scripts/flt_export.py
Executable file
@ -0,0 +1,763 @@
|
||||
#!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
|
||||
import math
|
||||
from flt_filewalker import FltOut
|
||||
|
||||
class ExporterOptions:
|
||||
def __init__(self):
|
||||
self.defaults = { 'Diffuse Color To OpenFlight Material': False,
|
||||
'Diffuse Color To OpenFlight Face': True}
|
||||
|
||||
d = Blender.Registry.GetKey('flt_export', True)
|
||||
|
||||
if d == None or d.keys() != self.defaults.keys():
|
||||
d = self.defaults
|
||||
Blender.Registry.SetKey('flt_export', d, True)
|
||||
|
||||
self.verbose = 1
|
||||
self.tolerance = 0.001
|
||||
self.use_mat_color = d['Diffuse Color To OpenFlight Material']
|
||||
self.use_face_color = d['Diffuse Color To OpenFlight Face']
|
||||
|
||||
options = ExporterOptions()
|
||||
|
||||
def not_equal_float(f1, f2):
|
||||
return math.fabs(f1 - f2) > options.tolerance
|
||||
|
||||
identity_matrix = [[1.0, 0.0, 0.0, 0.0], [0.0, 1.0, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0], [0.0, 0.0, 0.0, 1.0]]
|
||||
|
||||
def is_identity(m):
|
||||
for i in range(4):
|
||||
for j in range(4):
|
||||
if not_equal_float(m[i][j], identity_matrix[i][j]):
|
||||
return False
|
||||
return True
|
||||
|
||||
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):
|
||||
self.x = 0.0
|
||||
self.y = 0.0
|
||||
self.z = 0.0
|
||||
self.nx = 0.0
|
||||
self.ny = 0.0
|
||||
self.nz = 0.0
|
||||
self.u = 0.0
|
||||
self.v = 0.0
|
||||
|
||||
class GlobalResourceRepository:
|
||||
def new_face_name(self):
|
||||
n = 'f' + str(self.face_name)
|
||||
self.face_name += 1
|
||||
return n
|
||||
|
||||
def vertex_count(self):
|
||||
return len(self.vertex_lst)
|
||||
|
||||
def request_vertex_desc(self, i):
|
||||
return self.vertex_lst[i]
|
||||
|
||||
def request_vertex_index(self, desc):
|
||||
match = None
|
||||
for i in range(len(self.vertex_lst)):
|
||||
if not_equal_float(self.vertex_lst[i].x, desc.x):
|
||||
continue
|
||||
if not_equal_float(self.vertex_lst[i].y, desc.y):
|
||||
continue
|
||||
if not_equal_float(self.vertex_lst[i].z, desc.z):
|
||||
continue
|
||||
if not_equal_float(self.vertex_lst[i].nx, desc.nx):
|
||||
continue
|
||||
if not_equal_float(self.vertex_lst[i].ny, desc.ny):
|
||||
continue
|
||||
if not_equal_float(self.vertex_lst[i].nz, desc.nz):
|
||||
continue
|
||||
if not_equal_float(self.vertex_lst[i].u, desc.u):
|
||||
continue
|
||||
if not_equal_float(self.vertex_lst[i].v, desc.v):
|
||||
continue
|
||||
|
||||
match = i
|
||||
break
|
||||
|
||||
if match != None:
|
||||
return match
|
||||
else:
|
||||
self.vertex_lst.append(desc)
|
||||
return len(self.vertex_lst) - 1
|
||||
|
||||
def request_texture_index(self, filename):
|
||||
match = None
|
||||
for i in range(len(self.texture_lst)):
|
||||
if self.texture_lst[i] != filename:
|
||||
continue
|
||||
match = i
|
||||
break
|
||||
if match != None:
|
||||
return match
|
||||
else:
|
||||
self.texture_lst.append(filename)
|
||||
return len(self.texture_lst) - 1
|
||||
|
||||
def request_texture_filename(self, index):
|
||||
return self.texture_lst[index]
|
||||
|
||||
def texture_count(self):
|
||||
return len(self.texture_lst)
|
||||
|
||||
def request_material_index(self, desc):
|
||||
match = None
|
||||
for i in range(len(self.material_lst)):
|
||||
if self.material_lst[i].diffuse != desc.diffuse:
|
||||
continue
|
||||
if self.material_lst[i].specular != desc.specular:
|
||||
continue
|
||||
if self.material_lst[i].ambient != desc.ambient:
|
||||
continue
|
||||
if self.material_lst[i].emissive != desc.emissive:
|
||||
continue
|
||||
if self.material_lst[i].shininess != desc.shininess:
|
||||
continue
|
||||
if self.material_lst[i].alpha != desc.alpha:
|
||||
continue
|
||||
match = i
|
||||
break
|
||||
|
||||
if match != None:
|
||||
return i
|
||||
else:
|
||||
self.material_lst.append(desc)
|
||||
return len(self.material_lst) - 1
|
||||
|
||||
def request_material_desc(self, index):
|
||||
return self.material_lst[index]
|
||||
|
||||
def material_count(self):
|
||||
return len(self.material_lst)
|
||||
|
||||
# Returns not actual index but one that includes intensity information.
|
||||
# color_index = 127*intensity + 128*actual_index
|
||||
def request_color_index(self, col):
|
||||
r = col[0]
|
||||
g = col[1]
|
||||
b = col[2]
|
||||
m = max(r, g, b)
|
||||
if m > 0.0:
|
||||
intensity = m / 1.0
|
||||
r = int(round(r/m * 255.0))
|
||||
g = int(round(g/m * 255.0))
|
||||
b = int(round(b/m * 255.0))
|
||||
brightest = [r, g, b]
|
||||
else:
|
||||
brightest = [255, 255, 255]
|
||||
intensity = 0.0
|
||||
|
||||
match = None
|
||||
for i in range(len(self.color_lst)):
|
||||
if self.color_lst[i] != brightest:
|
||||
continue
|
||||
|
||||
match = i
|
||||
break
|
||||
|
||||
if match != None:
|
||||
index = match
|
||||
else:
|
||||
length = len(self.color_lst)
|
||||
if length <= 1024:
|
||||
self.color_lst.append(brightest)
|
||||
index = length
|
||||
else:
|
||||
if options.verbose >= 1:
|
||||
print 'Warning: Exceeded max color limit.'
|
||||
index = 0
|
||||
|
||||
color_index = int(round(127.0*intensity)) + 128*index
|
||||
return color_index
|
||||
|
||||
# Returns color from actual index.
|
||||
def request_max_color(self, index):
|
||||
return self.color_lst[index]
|
||||
|
||||
def color_count(self):
|
||||
return len(self.color_lst)
|
||||
|
||||
def __init__(self):
|
||||
self.vertex_lst = []
|
||||
self.texture_lst = []
|
||||
self.material_lst = []
|
||||
self.color_lst = [[255, 255, 255]]
|
||||
self.face_name = 0
|
||||
|
||||
class Node:
|
||||
# Gathers info from blender needed for export.
|
||||
# The =[0] is a trick to emulate c-like static function variables
|
||||
# that are persistant between calls.
|
||||
def blender_export(self, level=[0]):
|
||||
if self.object:
|
||||
if options.verbose >= 2:
|
||||
for j in range(level[0]):
|
||||
print ' ',
|
||||
print self.name, type(self.object.getData())
|
||||
|
||||
level[0] += 1
|
||||
|
||||
for child in self.children:
|
||||
child.blender_export()
|
||||
|
||||
level[0] -= 1
|
||||
|
||||
# Exports this node's info to file.
|
||||
def write(self):
|
||||
pass
|
||||
|
||||
def write_matrix(self):
|
||||
if self.matrix and not is_identity(self.matrix):
|
||||
self.header.fw.write_short(49) # Matrix opcode
|
||||
self.header.fw.write_ushort(68) # Length of record
|
||||
for i in range(4):
|
||||
for j in range(4):
|
||||
self.header.fw.write_float(self.matrix[i][j])
|
||||
|
||||
def write_push(self):
|
||||
self.header.fw.write_short(10)
|
||||
self.header.fw.write_ushort(4)
|
||||
|
||||
def write_pop(self):
|
||||
self.header.fw.write_short(11)
|
||||
self.header.fw.write_ushort(4)
|
||||
|
||||
def write_longid(self, name):
|
||||
length = len(name)
|
||||
if length >= 8:
|
||||
self.header.fw.write_short(33) # Long ID opcode
|
||||
self.header.fw.write_ushort(length+5) # Length of record
|
||||
self.header.fw.write_string(name, length+1) # name + zero terminator
|
||||
|
||||
# Initialization sets up basic tree structure.
|
||||
def __init__(self, parent, header, object, object_lst):
|
||||
self.header = header
|
||||
self.object = object
|
||||
if object:
|
||||
self.name = self.object.getName()
|
||||
self.matrix = self.object.getMatrix('localspace')
|
||||
else:
|
||||
self.name = 'no name'
|
||||
self.matrix = None
|
||||
|
||||
self.children = []
|
||||
self.parent = parent
|
||||
if parent:
|
||||
parent.children.append(self)
|
||||
|
||||
left_over = object_lst[:]
|
||||
self.child_objects = []
|
||||
|
||||
# Add children to child list and remove from left_over list.
|
||||
for obj in object_lst:
|
||||
if obj.getParent() == object:
|
||||
self.child_objects.append(obj)
|
||||
left_over.remove(obj)
|
||||
|
||||
# Spawn children.
|
||||
self.has_object_child = False # For Database class.
|
||||
for child in self.child_objects:
|
||||
dat = child.getData()
|
||||
t = type(dat)
|
||||
|
||||
if dat == None:
|
||||
BlenderEmpty(self, header, child, left_over)
|
||||
if t == Blender.Types.NMeshType:
|
||||
BlenderMesh(self, header, child, left_over)
|
||||
self.has_object_child = True
|
||||
|
||||
class FaceDesc:
|
||||
def __init__(self):
|
||||
self.vertex_index_lst = []
|
||||
self.texture_index = -1
|
||||
self.material_index = -1
|
||||
self.color_index = 127
|
||||
|
||||
class BlenderMesh(Node):
|
||||
def blender_export(self):
|
||||
Node.blender_export(self)
|
||||
|
||||
mesh = self.object.getData()
|
||||
|
||||
# Gather materials and textures.
|
||||
tex_index_lst = []
|
||||
mat_index_lst = []
|
||||
color_index_lst = []
|
||||
materials = mesh.getMaterials()
|
||||
|
||||
if materials == []:
|
||||
materials = [Blender.Material.New()]
|
||||
|
||||
for mat in materials:
|
||||
# Gather Color.
|
||||
if options.use_face_color:
|
||||
color_index_lst.append(self.header.GRR.request_color_index(mat.getRGBCol()))
|
||||
else:
|
||||
color_index_lst.append(127) # white
|
||||
# Gather Texture.
|
||||
mtex_lst = mat.getTextures()
|
||||
|
||||
index = -1
|
||||
mtex = mtex_lst[0] # Not doing multi-texturing at the moment.
|
||||
if mtex != None:
|
||||
tex = mtex_lst[0].tex
|
||||
if tex != None:
|
||||
image = tex.getImage()
|
||||
if image != None:
|
||||
filename = image.getFilename()
|
||||
index = self.header.GRR.request_texture_index(filename)
|
||||
|
||||
tex_index_lst.append(index)
|
||||
|
||||
# Gather Material
|
||||
mat_desc = MaterialDesc()
|
||||
mat_desc.name = mat.getName()
|
||||
mat_desc.alpha = mat.getAlpha()
|
||||
mat_desc.shininess = mat.getSpec() * 64.0 # 2.0 => 128.0
|
||||
if options.use_mat_color:
|
||||
mat_desc.diffuse = mat.getRGBCol()
|
||||
else:
|
||||
mat_desc.diffuse = [1.0, 1.0, 1.0]
|
||||
|
||||
mat_desc.specular = mat.getSpecCol()
|
||||
amb = mat.getAmb()
|
||||
mat_desc.ambient = [amb, amb, amb]
|
||||
emit = mat.getEmit()
|
||||
mat_desc.emissive = [emit, emit, emit]
|
||||
|
||||
mat_index_lst.append(self.header.GRR.request_material_index(mat_desc))
|
||||
|
||||
# Faces described as lists of indices into the GRR's vertex_lst.
|
||||
for face in mesh.faces:
|
||||
# Create vertex description list for each face.
|
||||
vertex_lst = []
|
||||
for vertex in face.v:
|
||||
vert_desc = VertexDesc()
|
||||
vert_desc.x = vertex.co[0]
|
||||
vert_desc.y = vertex.co[1]
|
||||
vert_desc.z = vertex.co[2]
|
||||
vert_desc.nx = vertex.no[0]
|
||||
vert_desc.ny = vertex.no[1]
|
||||
vert_desc.nz = vertex.no[2]
|
||||
vertex_lst.append(vert_desc)
|
||||
|
||||
for j in range( min(len(face.uv),len(vertex_lst)) ):
|
||||
vertex_lst[j].u = face.uv[j][0]
|
||||
vertex_lst[j].v = face.uv[j][1]
|
||||
|
||||
index_lst = []
|
||||
for vert_desc in vertex_lst:
|
||||
index_lst.append(self.header.GRR.request_vertex_index(vert_desc))
|
||||
|
||||
face_desc = FaceDesc()
|
||||
face_desc.vertex_index_lst = index_lst
|
||||
|
||||
if face.materialIndex < len(materials):
|
||||
face_desc.color_index = color_index_lst[face.materialIndex]
|
||||
face_desc.texture_index = tex_index_lst[face.materialIndex]
|
||||
face_desc.material_index = mat_index_lst[face.materialIndex]
|
||||
else:
|
||||
if options.verbose >=1:
|
||||
print 'Warning: Missing material for material index. Materials will not be imported correctly. Fix by deleting abandoned material indices in Blender.'
|
||||
|
||||
self.face_lst.append(face_desc)
|
||||
|
||||
# Export double sided face as 2 faces with opposite orientations.
|
||||
if mesh.hasFaceUV() and face.mode & Blender.NMesh.FaceModes['TWOSIDE']:
|
||||
# Create vertex description list for each face.
|
||||
vertex_lst = []
|
||||
for vertex in face.v:
|
||||
vert_desc = VertexDesc()
|
||||
vert_desc.x = vertex.co[0]
|
||||
vert_desc.y = vertex.co[1]
|
||||
vert_desc.z = vertex.co[2]
|
||||
vert_desc.nx = -vertex.no[0]
|
||||
vert_desc.ny = -vertex.no[1]
|
||||
vert_desc.nz = -vertex.no[2]
|
||||
vertex_lst.append(vert_desc)
|
||||
|
||||
for j in range( min(len(face.uv),len(vertex_lst)) ):
|
||||
vertex_lst[j].u = face.uv[j][0]
|
||||
vertex_lst[j].v = face.uv[j][1]
|
||||
|
||||
vertex_lst.reverse()
|
||||
|
||||
index_lst = []
|
||||
for vert_desc in vertex_lst:
|
||||
index_lst.append(self.header.GRR.request_vertex_index(vert_desc))
|
||||
|
||||
face_desc = FaceDesc()
|
||||
face_desc.vertex_index_lst = index_lst
|
||||
if face.materialIndex < len(materials):
|
||||
face_desc.color_index = color_index_lst[face.materialIndex]
|
||||
face_desc.texture_index = tex_index_lst[face.materialIndex]
|
||||
face_desc.material_index = mat_index_lst[face.materialIndex]
|
||||
else:
|
||||
if options.verbose >=1:
|
||||
print 'Error: No material for material index. Delete abandoned material indices in Blender.'
|
||||
|
||||
self.face_lst.append(face_desc)
|
||||
|
||||
def write_faces(self):
|
||||
for face_desc in self.face_lst:
|
||||
face_name = self.header.GRR.new_face_name()
|
||||
|
||||
self.header.fw.write_short(5) # Face opcode
|
||||
self.header.fw.write_ushort(80) # Length of record
|
||||
self.header.fw.write_string(face_name, 8) # ASCII ID
|
||||
self.header.fw.write_int(-1) # IR color code
|
||||
self.header.fw.write_short(0) # Relative priority
|
||||
self.header.fw.write_char(0) # Draw type
|
||||
self.header.fw.write_char(0) # Draw textured white.
|
||||
self.header.fw.write_ushort(0) # Color name index
|
||||
self.header.fw.write_ushort(0) # Alt color name index
|
||||
self.header.fw.write_char(0) # Reserved
|
||||
self.header.fw.write_char(1) # Template
|
||||
self.header.fw.write_short(-1) # Detail tex pat index
|
||||
self.header.fw.write_short(face_desc.texture_index) # Tex pattern index
|
||||
self.header.fw.write_short(face_desc.material_index) # material index
|
||||
self.header.fw.write_short(0) # SMC code
|
||||
self.header.fw.write_short(0) # Feature code
|
||||
self.header.fw.write_int(0) # IR material code
|
||||
self.header.fw.write_ushort(0) # transparency 0 = opaque
|
||||
self.header.fw.write_uchar(0) # LOD generation control
|
||||
self.header.fw.write_uchar(0) # line style index
|
||||
self.header.fw.write_int(0x00000000) # Flags
|
||||
self.header.fw.write_uchar(2) # Light mode
|
||||
self.header.fw.pad(7) # Reserved
|
||||
self.header.fw.write_uint(-1) # Packed color
|
||||
self.header.fw.write_uint(-1) # Packed alt color
|
||||
self.header.fw.write_short(-1) # Tex map index
|
||||
self.header.fw.write_short(0) # Reserved
|
||||
self.header.fw.write_uint(face_desc.color_index) # Color index
|
||||
self.header.fw.write_uint(127) # Alt color index
|
||||
self.header.fw.write_short(0) # Reserved
|
||||
self.header.fw.write_short(-1) # Shader index
|
||||
|
||||
self.write_longid(face_name)
|
||||
|
||||
self.write_push()
|
||||
|
||||
# Vertex list record
|
||||
self.header.fw.write_short(72) # Vertex list opcode
|
||||
num_verts = len(face_desc.vertex_index_lst)
|
||||
self.header.fw.write_ushort(4*num_verts+4) # Length of record
|
||||
|
||||
for vert_index in face_desc.vertex_index_lst:
|
||||
# Offset into vertex palette
|
||||
self.header.fw.write_int(vert_index*64+8)
|
||||
|
||||
self.write_pop()
|
||||
|
||||
def write(self):
|
||||
if self.open_flight_type == 'Object':
|
||||
self.header.fw.write_short(4) # Object opcode
|
||||
self.header.fw.write_ushort(28) # Length of record
|
||||
self.header.fw.write_string(self.name, 8) # ASCII ID
|
||||
self.header.fw.pad(16)
|
||||
|
||||
self.write_longid(self.name)
|
||||
|
||||
self.write_matrix()
|
||||
|
||||
if self.face_lst != []:
|
||||
self.write_push()
|
||||
|
||||
self.write_faces()
|
||||
|
||||
self.write_pop()
|
||||
else:
|
||||
self.header.fw.write_short(2) # Group opcode
|
||||
self.header.fw.write_ushort(44) # Length of record
|
||||
self.header.fw.write_string(self.name, 8) # ASCII ID
|
||||
self.header.fw.pad(32)
|
||||
|
||||
self.write_longid(self.name)
|
||||
|
||||
# Because a group can contain faces as well as children.
|
||||
self.write_push()
|
||||
|
||||
self.write_faces()
|
||||
|
||||
for child in self.children:
|
||||
child.write()
|
||||
|
||||
self.write_pop()
|
||||
|
||||
|
||||
def __init__(self, parent, header, object, object_lst):
|
||||
Node.__init__(self, parent, header, object, object_lst)
|
||||
self.face_lst = []
|
||||
|
||||
if self.children == []:
|
||||
self.open_flight_type = 'Object'
|
||||
else:
|
||||
self.open_flight_type= 'Group'
|
||||
|
||||
class BlenderEmpty(Node):
|
||||
def write(self):
|
||||
self.header.fw.write_short(2) # Group opcode
|
||||
self.header.fw.write_ushort(44) # Length of record
|
||||
self.header.fw.write_string(self.name, 8) # ASCII ID
|
||||
self.header.fw.pad(32)
|
||||
|
||||
self.write_longid(self.name)
|
||||
|
||||
self.write_matrix()
|
||||
|
||||
if self.children != []:
|
||||
self.write_push()
|
||||
|
||||
for child in self.children:
|
||||
child.write()
|
||||
|
||||
self.write_pop()
|
||||
|
||||
class Database(Node):
|
||||
def write_header(self):
|
||||
if options.verbose >= 2:
|
||||
print 'Writing header.'
|
||||
self.fw.write_short(1) # Header opcode
|
||||
self.fw.write_ushort(324) # Length of record
|
||||
self.fw.write_string('db', 8) # ASCII ID
|
||||
self.fw.write_int(1600) # Revision Number
|
||||
self.fw.pad(44)
|
||||
self.fw.write_short(1) # Unit multiplier.
|
||||
self.fw.write_char(0) # Units, 0 = meters
|
||||
self.fw.write_char(0) # texwhite on new faces 0 = false
|
||||
self.fw.write_uint(0x80000000) # misc flags set to saving vertex normals
|
||||
self.fw.pad(24)
|
||||
self.fw.write_int(0) # projection type, 0 = flat earth
|
||||
self.fw.pad(30)
|
||||
self.fw.write_short(1) # double precision
|
||||
self.fw.pad(140)
|
||||
self.fw.write_int(0) # ellipsoid model, 0 = WSG 1984
|
||||
self.fw.pad(52)
|
||||
|
||||
def write_vert_pal(self):
|
||||
if options.verbose >= 2:
|
||||
print 'Writing vertex palette.'
|
||||
# Write record for vertex palette
|
||||
self.fw.write_short(67) # Vertex palette opcode.
|
||||
self.fw.write_short(8) # Length of record
|
||||
self.fw.write_int(self.GRR.vertex_count() * 64 + 8) # Length of everything.
|
||||
|
||||
# Write records for individual vertices.
|
||||
for i in range(self.GRR.vertex_count()):
|
||||
desc = self.GRR.request_vertex_desc(i)
|
||||
self.fw.write_short(70) # Vertex with color normal and uv opcode.
|
||||
self.fw.write_ushort(64) # Length of record
|
||||
self.fw.write_ushort(0) # Color name index
|
||||
self.fw.write_short(0x2000) # Flags set to no color
|
||||
self.fw.write_double(desc.x)
|
||||
self.fw.write_double(desc.y)
|
||||
self.fw.write_double(desc.z)
|
||||
self.fw.write_float(desc.nx)
|
||||
self.fw.write_float(desc.ny)
|
||||
self.fw.write_float(desc.nz)
|
||||
self.fw.write_float(desc.u)
|
||||
self.fw.write_float(desc.v)
|
||||
self.fw.pad(12)
|
||||
|
||||
def write_tex_pal(self):
|
||||
if options.verbose >= 2:
|
||||
print 'Writing texture palette.'
|
||||
# Write record for texture palette
|
||||
for i in range(self.GRR.texture_count()):
|
||||
self.fw.write_short(64) # Texture palette opcode.
|
||||
self.fw.write_short(216) # Length of record
|
||||
self.fw.write_string(self.GRR.request_texture_filename(i), 200) # Filename
|
||||
self.fw.write_int(i) # Texture index
|
||||
self.fw.write_int(0) # X
|
||||
self.fw.write_int(0) # Y
|
||||
|
||||
def write_mat_pal(self):
|
||||
if options.verbose >= 2:
|
||||
print 'Writing material palette.'
|
||||
for i in range(self.GRR.material_count()):
|
||||
desc = self.GRR.request_material_desc(i)
|
||||
self.fw.write_short(113) # Material palette opcode.
|
||||
self.fw.write_short(84) # Length of record
|
||||
self.fw.write_int(i) # Material index
|
||||
self.fw.write_string(desc.name, 12) # Material name
|
||||
self.fw.write_uint(0x80000000) # Flags
|
||||
self.fw.write_float(desc.ambient[0]) # Ambient color.
|
||||
self.fw.write_float(desc.ambient[1]) # Ambient color.
|
||||
self.fw.write_float(desc.ambient[2]) # Ambient color.
|
||||
self.fw.write_float(desc.diffuse[0]) # Diffuse color.
|
||||
self.fw.write_float(desc.diffuse[1]) # Diffuse color.
|
||||
self.fw.write_float(desc.diffuse[2]) # Diffuse color.
|
||||
self.fw.write_float(desc.specular[0]) # Specular color.
|
||||
self.fw.write_float(desc.specular[1]) # Specular color.
|
||||
self.fw.write_float(desc.specular[2]) # Specular color.
|
||||
self.fw.write_float(desc.emissive[0]) # Emissive color.
|
||||
self.fw.write_float(desc.emissive[1]) # Emissive color.
|
||||
self.fw.write_float(desc.emissive[2]) # Emissive color.
|
||||
self.fw.write_float(desc.shininess)
|
||||
self.fw.write_float(desc.alpha)
|
||||
self.fw.write_int(0) # Reserved
|
||||
|
||||
def write_col_pal(self):
|
||||
if options.verbose >= 2:
|
||||
print 'Writing color palette.'
|
||||
self.fw.write_short(32) # Color palette opcode.
|
||||
self.fw.write_short(4228) # Length of record
|
||||
self.fw.pad(128)
|
||||
count = self.GRR.color_count()
|
||||
for i in range(count):
|
||||
col = self.GRR.request_max_color(i)
|
||||
self.fw.write_uchar(255) # alpha
|
||||
self.fw.write_uchar(col[2]) # b
|
||||
self.fw.write_uchar(col[1]) # g
|
||||
self.fw.write_uchar(col[0]) # r
|
||||
self.fw.pad(max(4096-count*4, 0))
|
||||
|
||||
def write(self):
|
||||
self.write_header()
|
||||
self.write_vert_pal()
|
||||
self.write_tex_pal()
|
||||
self.write_mat_pal()
|
||||
self.write_col_pal()
|
||||
|
||||
# Wrap everything in a group if it has an object child.
|
||||
if self.has_object_child:
|
||||
self.header.fw.write_short(2) # Group opcode
|
||||
self.header.fw.write_ushort(44) # Length of record
|
||||
self.header.fw.write_string('g1', 8) # ASCII ID
|
||||
self.header.fw.pad(32)
|
||||
|
||||
self.write_push()
|
||||
|
||||
for child in self.children:
|
||||
child.write()
|
||||
|
||||
self.write_pop()
|
||||
|
||||
def __init__(self, scene, fw):
|
||||
self.fw = fw
|
||||
self.scene = scene
|
||||
self.all_objects = scene.getChildren()
|
||||
self.GRR = GlobalResourceRepository()
|
||||
|
||||
Node.__init__(self, None, self, None, self.all_objects)
|
||||
|
||||
def fs_callback(filename):
|
||||
Blender.Window.WaitCursor(True)
|
||||
|
||||
if Blender.sys.exists(filename):
|
||||
r = Blender.Draw.PupMenu('Overwrite ' + filename + '?%t|Yes|No')
|
||||
if r != 1:
|
||||
if options.verbose >= 1:
|
||||
print 'Export cancelled.'
|
||||
return
|
||||
|
||||
fw = FltOut(filename)
|
||||
|
||||
db = Database(Blender.Scene.GetCurrent(), fw)
|
||||
|
||||
if options.verbose >= 1:
|
||||
print
|
||||
print 'Pass 1: Exporting from Blender.'
|
||||
print
|
||||
|
||||
db.blender_export()
|
||||
|
||||
if options.verbose >= 1:
|
||||
print
|
||||
print 'Pass 2: Writing', filename
|
||||
print
|
||||
|
||||
db.write()
|
||||
|
||||
fw.close_file()
|
||||
if options.verbose >= 1:
|
||||
print
|
||||
print 'Done.'
|
||||
|
||||
Blender.Window.WaitCursor(False)
|
||||
|
||||
if options.verbose >= 1:
|
||||
print
|
||||
print 'OpenFlight Exporter'
|
||||
print 'Version:', __version__
|
||||
print 'Author: Greg MacDonald'
|
||||
print __url__[2]
|
||||
print
|
||||
|
||||
fname = Blender.sys.makename(ext=".flt")
|
||||
Blender.Window.FileSelector(fs_callback, "Export OpenFlight v16.0", fname)
|
278
release/scripts/flt_filewalker.py
Normal file
278
release/scripts/flt_filewalker.py
Normal file
@ -0,0 +1,278 @@
|
||||
#!BPY
|
||||
|
||||
# flt_filewalker.py is an utility module for OpenFlight IO scripts 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.
|
||||
|
||||
import Blender
|
||||
from struct import *
|
||||
import re
|
||||
|
||||
class FltIn:
|
||||
def begin_record(self):
|
||||
if self.repeat == True:
|
||||
self.repeat = False
|
||||
else:
|
||||
self.position += self.length
|
||||
try:
|
||||
self.file.seek(self.position)
|
||||
input = self.file.read(4)
|
||||
except:
|
||||
print 'Parse Error!'
|
||||
return False
|
||||
|
||||
if not input:
|
||||
self.close_file()
|
||||
return False
|
||||
|
||||
self.opcode = unpack('>h', input[:2])[0]
|
||||
self.length = unpack('>H', input[-2:])[0]
|
||||
|
||||
self.next_position = self.position + self.length
|
||||
|
||||
return True
|
||||
|
||||
def repeat_record(self):
|
||||
self.repeat = True
|
||||
|
||||
def get_opcode(self):
|
||||
return self.opcode
|
||||
|
||||
def get_level(self):
|
||||
return self.level
|
||||
|
||||
def up_level(self):
|
||||
self.level += 1
|
||||
|
||||
def down_level(self):
|
||||
self.level -= 1
|
||||
|
||||
def read_string(self, length):
|
||||
s = ''
|
||||
if self.file.tell() + length <= self.next_position:
|
||||
start = self.file.tell()
|
||||
for i in range(length):
|
||||
char = self.file.read(1)
|
||||
if char == '\x00':
|
||||
break
|
||||
s = s + char
|
||||
|
||||
self.file.seek(start+length)
|
||||
# else:
|
||||
# print 'Warning: string truncated'
|
||||
|
||||
return s
|
||||
|
||||
def read_int(self):
|
||||
if self.file.tell() + 4 <= self.next_position:
|
||||
return unpack('>i', self.file.read(4))[0]
|
||||
else:
|
||||
#print 'Warning: int truncated'
|
||||
return 0
|
||||
|
||||
def read_uint(self):
|
||||
if self.file.tell() + 4 <= self.next_position:
|
||||
return unpack('>I', self.file.read(4))[0]
|
||||
else:
|
||||
#print 'Warning: uint truncated'
|
||||
return 0
|
||||
|
||||
def read_double(self):
|
||||
if self.file.tell() + 8 <= self.next_position:
|
||||
return unpack('>d', self.file.read(8))[0]
|
||||
else:
|
||||
#print 'Warning: double truncated'
|
||||
return 0.0
|
||||
|
||||
def read_float(self):
|
||||
if self.file.tell() + 4 <= self.next_position:
|
||||
return unpack('>f', self.file.read(4))[0]
|
||||
else:
|
||||
#print 'Warning: float truncated'
|
||||
return 0.0
|
||||
|
||||
def read_ushort(self):
|
||||
if self.file.tell() + 2 <= self.next_position:
|
||||
return unpack('>H', self.file.read(2))[0]
|
||||
else:
|
||||
#print 'Warning: ushort truncated'
|
||||
return 0
|
||||
|
||||
def read_short(self):
|
||||
if self.file.tell() + 2 <= self.next_position:
|
||||
return unpack('>h', self.file.read(2))[0]
|
||||
else:
|
||||
#print 'Warning: short trunated'
|
||||
return 0
|
||||
|
||||
def read_uchar(self):
|
||||
if self.file.tell() + 1 <= self.next_position:
|
||||
return unpack('>B', self.file.read(1))[0]
|
||||
else:
|
||||
#print 'Warning: uchar truncated'
|
||||
return 0
|
||||
|
||||
def read_char(self):
|
||||
if self.file.tell() + 1 <= self.next_position:
|
||||
return unpack('>b', self.file.read(1))[0]
|
||||
else:
|
||||
#print 'Warning: char truncated'
|
||||
return 0
|
||||
|
||||
def read_ahead(self, i):
|
||||
if self.file.tell() + i <= self.next_position:
|
||||
self.file.seek(i, 1)
|
||||
# else:
|
||||
# print 'Warning: attempt to seek past record'
|
||||
|
||||
def get_length(self):
|
||||
return self.length
|
||||
|
||||
def close_file(self):
|
||||
self.file.close()
|
||||
|
||||
def __init__(self, filename):
|
||||
self.file = open(filename, 'rb')
|
||||
self.position = 0
|
||||
self.next_position = 100000
|
||||
self.opcode = 0
|
||||
self.length = 0
|
||||
self.level = 0
|
||||
self.repeat = False # Repeat the last record.
|
||||
|
||||
class FltOut:
|
||||
# Length includes terminating null
|
||||
def write_string(self, string, length):
|
||||
if len(string) > length - 1:
|
||||
str_len = length - 1
|
||||
else:
|
||||
str_len = len(string)
|
||||
|
||||
pad_len = length - str_len
|
||||
|
||||
self.file.write(string[:str_len])
|
||||
|
||||
self.pad(pad_len)
|
||||
|
||||
def write_int(self, a):
|
||||
self.file.write( pack('>i', a) )
|
||||
|
||||
def write_uint(self, a):
|
||||
self.file.write( pack('>I', a) )
|
||||
|
||||
def write_double(self, a):
|
||||
self.file.write( pack('>d', a) )
|
||||
|
||||
def write_float(self, a):
|
||||
self.file.write( pack('>f', a) )
|
||||
|
||||
def write_ushort(self, a):
|
||||
self.file.write( pack('>H', a) )
|
||||
|
||||
def write_short(self, a):
|
||||
self.file.write( pack('>h', a) )
|
||||
|
||||
def write_uchar(self, a):
|
||||
self.file.write( pack('>B', a) )
|
||||
|
||||
def write_char(self, a):
|
||||
self.file.write( pack('>b', a) )
|
||||
|
||||
def pad(self, reps):
|
||||
for i in range(reps):
|
||||
self.file.write('\x00')
|
||||
|
||||
def close_file(self):
|
||||
self.file.close()
|
||||
|
||||
def __init__(self, filename):
|
||||
self.file = open(filename, 'wb')
|
||||
|
||||
class FileFinder:
|
||||
def add_file_to_search_path(self, filename):
|
||||
dir = Blender.sys.dirname(filename)
|
||||
if dir != None and dir != '':
|
||||
self.search_dirs.append(dir)
|
||||
|
||||
def strip_path(self, full_path):
|
||||
# One of my flt files had a windows path with unix seperation. Basename
|
||||
# returned the whole path + filename, which isn't expected. So my
|
||||
# attempt to fix it is to replace all / or \ with the platform specific
|
||||
# dir seperator.
|
||||
#
|
||||
# note: \\\\ is actually just one \ indirected twice, once for python
|
||||
# then again for re.sub
|
||||
if Blender.sys.sep == '\\':
|
||||
full_path = re.sub('/', '\\\\', full_path)
|
||||
elif Blender.sys.sep == '/':
|
||||
full_path = re.sub('\\\\', '/', full_path)
|
||||
|
||||
filename = Blender.sys.basename(full_path)
|
||||
return filename
|
||||
|
||||
def find(self, full_path):
|
||||
if full_path == '':
|
||||
return None
|
||||
|
||||
# Seperate out the path.
|
||||
dirname = Blender.sys.dirname(full_path)
|
||||
|
||||
# Try it first.
|
||||
if Blender.sys.exists(full_path):
|
||||
if not dirname in self.search_dirs:
|
||||
self.search_dirs.append(dirname)
|
||||
return full_path
|
||||
|
||||
# Maybe it's relative.
|
||||
for path in self.search_dirs:
|
||||
rel_full_path = Blender.sys.join(path, full_path)
|
||||
if Blender.sys.exists(rel_full_path):
|
||||
return rel_full_path
|
||||
|
||||
# Search previous directories that have worked.
|
||||
filename = self.strip_path(full_path)
|
||||
for path in self.search_dirs:
|
||||
t = Blender.sys.join(path, filename)
|
||||
if Blender.sys.exists(t):
|
||||
return t
|
||||
|
||||
# Ask user where it is.
|
||||
self.user_input = Blender.Draw.PupStrInput(filename + "? ", '', 100)
|
||||
if self.user_input != None:
|
||||
t = Blender.sys.join(self.user_input, filename)
|
||||
if Blender.sys.exists(t):
|
||||
user_dirname = Blender.sys.dirname(t)
|
||||
if not user_dirname in self.search_dirs:
|
||||
self.search_dirs.append(user_dirname)
|
||||
return t
|
||||
|
||||
# Couldn't find it.
|
||||
return None
|
||||
|
||||
def __init__(self):
|
||||
self.user_input = ''
|
||||
self.current_file = ''
|
||||
self.search_dirs = []
|
||||
|
||||
dir = Blender.Get('texturesdir')
|
||||
if dir != None and dir != '':
|
||||
self.search_dirs.append(dir)
|
||||
|
||||
dir = Blender.sys.dirname(Blender.Get('filename'))
|
||||
if dir != None and dir != '':
|
||||
print dir
|
||||
self.search_dirs.append(dir)
|
||||
|
1803
release/scripts/flt_import.py
Executable file
1803
release/scripts/flt_import.py
Executable file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user