mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-04 02:42:49 -04:00
1672 lines
56 KiB
C++
1672 lines
56 KiB
C++
// Filename: fltHeader.cxx
|
|
// Created by: drose (24Aug00)
|
|
//
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
#include "fltHeader.h"
|
|
#include "fltRecordReader.h"
|
|
#include "fltRecordWriter.h"
|
|
#include "fltUnsupportedRecord.h"
|
|
#include "config_flt.h"
|
|
|
|
#include <assert.h>
|
|
|
|
TypeHandle FltHeader::_type_handle;
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::Constructor
|
|
// Access: Public
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
FltHeader::
|
|
FltHeader() : FltBeadID(this) {
|
|
_format_revision_level = 1520;
|
|
_edit_revision_level = 1520;
|
|
_next_group_id = 1;
|
|
_next_lod_id = 1;
|
|
_next_object_id = 1;
|
|
_next_face_id = 1;
|
|
_unit_multiplier = 1;
|
|
_vertex_units = U_feet;
|
|
_texwhite_new = false;
|
|
_flags = 0;
|
|
_projection_type = PT_flat_earth;
|
|
_next_dof_id = 1;
|
|
_vertex_storage_type = VTS_double;
|
|
_database_origin = DO_open_flight;
|
|
_sw_x = 0.0;
|
|
_sw_y = 0.0;
|
|
_delta_x = 0.0;
|
|
_delta_y = 0.0;
|
|
_next_sound_id = 1;
|
|
_next_path_id = 1;
|
|
_next_clip_id = 1;
|
|
_next_text_id = 1;
|
|
_next_bsp_id = 1;
|
|
_next_switch_id = 1;
|
|
_sw_lat = 0.0;
|
|
_sw_long = 0.0;
|
|
_ne_lat = 0.0;
|
|
_ne_long = 0.0;
|
|
_origin_lat = 0.0;
|
|
_origin_long = 0.0;
|
|
_lambert_upper_lat = 0.0;
|
|
_lambert_lower_lat = 0.0;
|
|
_next_light_id = 1;
|
|
_next_road_id = 1;
|
|
_next_cat_id = 1;
|
|
_earth_model = EM_wgs84;
|
|
|
|
_vertex_lookups_stale = false;
|
|
_current_vertex_offset = 0;
|
|
_got_color_palette = false;
|
|
_got_eyepoint_trackplane_palette = false;
|
|
|
|
_auto_attr_update = AU_if_missing;
|
|
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::read_flt
|
|
// Access: Public
|
|
// Description: Opens the indicated filename for reading and attempts
|
|
// to read the complete Flt file. Returns FE_ok on
|
|
// success, otherwise on failure.
|
|
////////////////////////////////////////////////////////////////////
|
|
FltError FltHeader::
|
|
read_flt(Filename filename) {
|
|
filename.set_binary();
|
|
|
|
ifstream in;
|
|
if (!filename.open_read(in)) {
|
|
assert(!flt_error_abort);
|
|
return FE_could_not_open;
|
|
}
|
|
|
|
// By default, the filename's directory is added to the texture and
|
|
// model search path.
|
|
string dirname = filename.get_dirname();
|
|
if (!dirname.empty()) {
|
|
_texture_path.prepend_directory(dirname);
|
|
_model_path.prepend_directory(dirname);
|
|
}
|
|
|
|
return read_flt(in);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::read_flt
|
|
// Access: Public
|
|
// Description: Attempts to read a complete Flt file from the
|
|
// already-opened stream. Returns FE_ok on success,
|
|
// otherwise on failure.
|
|
////////////////////////////////////////////////////////////////////
|
|
FltError FltHeader::
|
|
read_flt(istream &in) {
|
|
FltRecordReader reader(in);
|
|
FltError result = reader.advance();
|
|
if (result == FE_end_of_file) {
|
|
assert(!flt_error_abort);
|
|
return FE_empty_file;
|
|
} else if (result != FE_ok) {
|
|
return result;
|
|
}
|
|
|
|
result = read_record_and_children(reader);
|
|
if (result != FE_ok) {
|
|
return result;
|
|
}
|
|
|
|
if (!reader.eof()) {
|
|
assert(!flt_error_abort);
|
|
return FE_extra_data;
|
|
}
|
|
|
|
return FE_ok;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::write_flt
|
|
// Access: Public
|
|
// Description: Opens the indicated filename for writing and attempts
|
|
// to write the complete Flt file. Returns FE_ok on
|
|
// success, otherwise on failure.
|
|
////////////////////////////////////////////////////////////////////
|
|
FltError FltHeader::
|
|
write_flt(Filename filename) {
|
|
filename.set_binary();
|
|
|
|
ofstream out;
|
|
if (!filename.open_write(out)) {
|
|
assert(!flt_error_abort);
|
|
return FE_could_not_open;
|
|
}
|
|
|
|
return write_flt(out);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::write_flt
|
|
// Access: Public
|
|
// Description: Attempts to write a complete Flt file to the
|
|
// already-opened stream. Returns FE_ok on success,
|
|
// otherwise on failure.
|
|
////////////////////////////////////////////////////////////////////
|
|
FltError FltHeader::
|
|
write_flt(ostream &out) {
|
|
FltRecordWriter writer(out);
|
|
FltError result = write_record_and_children(writer);
|
|
|
|
if (out.fail()) {
|
|
assert(!flt_error_abort);
|
|
return FE_write_error;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::set_auto_attr_update
|
|
// Access: Public
|
|
// Description: Controls whether texture .attr files are written
|
|
// automatically when write_flt() is called. There are
|
|
// three possibilities:
|
|
//
|
|
// AU_none: the .attr files are not written
|
|
// automatically; they must be written explicitly via a
|
|
// call to FltTexture::write_attr_data() if you want
|
|
// them to be written.
|
|
//
|
|
// AU_if_missing: the .attr files are written only if
|
|
// they do not already exist. This will not update any
|
|
// .attr files, even if the data is changed.
|
|
//
|
|
// AU_always: the .attr files are always rewritten, even
|
|
// if they already exist and even if the data has not
|
|
// changed.
|
|
//
|
|
// The default is AU_if_missing.
|
|
////////////////////////////////////////////////////////////////////
|
|
void FltHeader::
|
|
set_auto_attr_update(FltHeader::AttrUpdate attr) {
|
|
_auto_attr_update = attr;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::get_auto_attr_update
|
|
// Access: Public
|
|
// Description: Returns the current setting of the auto_attr_update
|
|
// flag. See sett_auto_attr_update().
|
|
////////////////////////////////////////////////////////////////////
|
|
FltHeader::AttrUpdate FltHeader::
|
|
get_auto_attr_update() const {
|
|
return _auto_attr_update;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::get_flt_version
|
|
// Access: Public
|
|
// Description: Returns the version number of the flt file as
|
|
// reported in the header.
|
|
////////////////////////////////////////////////////////////////////
|
|
double FltHeader::
|
|
get_flt_version() const {
|
|
if (_format_revision_level < 1420) {
|
|
return _format_revision_level;
|
|
} else {
|
|
return _format_revision_level / 100.0;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::min_flt_version
|
|
// Access: Public, Static
|
|
// Description: Returns the earliest flt version number that this
|
|
// codebase supports. Earlier versions will probably
|
|
// not work.
|
|
////////////////////////////////////////////////////////////////////
|
|
double FltHeader::
|
|
min_flt_version() {
|
|
return 15.2;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::max_flt_version
|
|
// Access: Public, Static
|
|
// Description: Returns the latest flt version number that this
|
|
// codebase is known to support. Later versions might
|
|
// work, but then again they may not.
|
|
////////////////////////////////////////////////////////////////////
|
|
double FltHeader::
|
|
max_flt_version() {
|
|
return 15.2;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::check_version
|
|
// Access: Public
|
|
// Description: Verifies that the version number read from the header
|
|
// is an understand version number, and prints a warning
|
|
// to the user if this is not so--the reading may or may
|
|
// not succeed. Returns true if the version number is
|
|
// acceptable (and no warning is printed), or false if
|
|
// it is questionable (and a warning is printed).
|
|
////////////////////////////////////////////////////////////////////
|
|
bool FltHeader::
|
|
check_version() const {
|
|
double version = get_flt_version();
|
|
|
|
if (version < min_flt_version()) {
|
|
nout << "Warning! The version number of this file appears to be "
|
|
<< version << ", which is older than " << min_flt_version()
|
|
<< ", the oldest OpenFlight version understood by this program. "
|
|
"It is unlikely that this program will be able to read the file "
|
|
"correctly.\n";
|
|
return false;
|
|
}
|
|
|
|
if (version > max_flt_version()) {
|
|
nout << "Warning! The version number of this file appears to be "
|
|
<< version << ", which is newer than " << max_flt_version()
|
|
<< ", the newest OpenFlight version understood by this program. "
|
|
"Chances are good that the program will still be able to read it "
|
|
"correctly, but any features in the file that are specific to "
|
|
"the latest version of OpenFlight will not be understood.\n";
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::has_instance
|
|
// Access: Public
|
|
// Description: Returns true if a instance subtree with the given
|
|
// index has been defined.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool FltHeader::
|
|
has_instance(int instance_index) const {
|
|
return (_instances.count(instance_index) != 0);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::get_instance
|
|
// Access: Public
|
|
// Description: Returns the instance subtree associated with the
|
|
// given index, or NULL if there is no such instance.
|
|
////////////////////////////////////////////////////////////////////
|
|
FltInstanceDefinition *FltHeader::
|
|
get_instance(int instance_index) const {
|
|
Instances::const_iterator mi;
|
|
mi = _instances.find(instance_index);
|
|
if (mi != _instances.end()) {
|
|
return (*mi).second;
|
|
}
|
|
return (FltInstanceDefinition *)NULL;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::clear_instances
|
|
// Access: Public
|
|
// Description: Removes all instance subtrees from the instance pool.
|
|
////////////////////////////////////////////////////////////////////
|
|
void FltHeader::
|
|
clear_instances() {
|
|
_instances.clear();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::add_instance
|
|
// Access: Public
|
|
// Description: Defines a new instance subtree. This subtree is not
|
|
// itself part of the hierarchy; it marks geometry that
|
|
// may be instanced to various beads elsewhere in the
|
|
// hierarchy by creating a corresponding FltInstanceRef
|
|
// bead.
|
|
////////////////////////////////////////////////////////////////////
|
|
void FltHeader::
|
|
add_instance(FltInstanceDefinition *instance) {
|
|
_instances[instance->_instance_index] = instance;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::remove_instance
|
|
// Access: Public
|
|
// Description: Removes a particular instance subtree from the pool,
|
|
// if it exists.
|
|
////////////////////////////////////////////////////////////////////
|
|
void FltHeader::
|
|
remove_instance(int instance_index) {
|
|
_instances.erase(instance_index);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::get_num_vertices
|
|
// Access: Public
|
|
// Description: Returns the number of vertices in the vertex palette.
|
|
////////////////////////////////////////////////////////////////////
|
|
int FltHeader::
|
|
get_num_vertices() const {
|
|
return _vertices.size();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::get_vertex
|
|
// Access: Public
|
|
// Description: Returns the nth vertex of the vertex palette.
|
|
////////////////////////////////////////////////////////////////////
|
|
FltVertex *FltHeader::
|
|
get_vertex(int n) const {
|
|
nassertr(n >= 0 && n < (int)_vertices.size(), 0);
|
|
return _vertices[n];
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::clear_vertices
|
|
// Access: Public
|
|
// Description: Removes all vertices from the vertex palette.
|
|
////////////////////////////////////////////////////////////////////
|
|
void FltHeader::
|
|
clear_vertices() {
|
|
_vertices.clear();
|
|
_unique_vertices.clear();
|
|
_vertices_by_offset.clear();
|
|
_offsets_by_vertex.clear();
|
|
_vertex_lookups_stale = false;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::add_vertex
|
|
// Access: Public
|
|
// Description: Adds a new vertex to the end of the vertex palette.
|
|
// If this particular vertex was already present in the
|
|
// palette, does nothing.
|
|
////////////////////////////////////////////////////////////////////
|
|
void FltHeader::
|
|
add_vertex(FltVertex *vertex) {
|
|
bool inserted = _unique_vertices.insert(vertex).second;
|
|
if (inserted) {
|
|
_vertices.push_back(vertex);
|
|
}
|
|
_vertex_lookups_stale = true;
|
|
nassertv(_unique_vertices.size() == _vertices.size());
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::get_vertex_by_offset
|
|
// Access: Public
|
|
// Description: Returns the particular vertex pointer associated with
|
|
// the given byte offset into the vertex palette. If
|
|
// there is no such vertex in the palette, this
|
|
// generates an error message and returns NULL.
|
|
////////////////////////////////////////////////////////////////////
|
|
FltVertex *FltHeader::
|
|
get_vertex_by_offset(int offset) {
|
|
if (_vertex_lookups_stale) {
|
|
update_vertex_lookups();
|
|
}
|
|
|
|
VerticesByOffset::const_iterator vi;
|
|
vi = _vertices_by_offset.find(offset);
|
|
if (vi == _vertices_by_offset.end()) {
|
|
nout << "No vertex with offset " << offset << "\n";
|
|
return (FltVertex *)NULL;
|
|
}
|
|
return (*vi).second;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::get_offset_by_vertex
|
|
// Access: Public
|
|
// Description: Returns the byte offset into the vertex palette
|
|
// associated with the given vertex pointer. If there
|
|
// is no such vertex in the palette, this generates an
|
|
// error message and returns 0.
|
|
////////////////////////////////////////////////////////////////////
|
|
int FltHeader::
|
|
get_offset_by_vertex(FltVertex *vertex) {
|
|
if (_vertex_lookups_stale) {
|
|
update_vertex_lookups();
|
|
}
|
|
|
|
OffsetsByVertex::const_iterator vi;
|
|
vi = _offsets_by_vertex.find(vertex);
|
|
if (vi == _offsets_by_vertex.end()) {
|
|
nout << "Vertex does not appear in palette.\n";
|
|
return 0;
|
|
}
|
|
return (*vi).second;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::get_num_colors
|
|
// Access: Public
|
|
// Description: Returns the total number of different colors in the
|
|
// color palette. This includes all different colors,
|
|
// and represents the complete range of alloable color
|
|
// indices. This is different from the actual number of
|
|
// color entries as read directly from the color
|
|
// palette, since each color entry defines a number of
|
|
// different intensity levels--the value returned by
|
|
// get_num_colors() is equal to get_num_color_entries()
|
|
// * get_num_color_shades().
|
|
////////////////////////////////////////////////////////////////////
|
|
int FltHeader::
|
|
get_num_colors() const {
|
|
return _colors.size() * get_num_color_shades();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::get_color
|
|
// Access: Public
|
|
// Description: Returns the four-component color corresponding to the
|
|
// given color index. Each component will be in the
|
|
// range [0, 1].
|
|
////////////////////////////////////////////////////////////////////
|
|
Colorf FltHeader::
|
|
get_color(int color_index) const {
|
|
nassertr(color_index >= 0 && color_index < get_num_colors(),
|
|
Colorf(0.0, 0.0, 0.0, 0.0));
|
|
int num_color_shades = get_num_color_shades();
|
|
|
|
int index = (color_index / num_color_shades);
|
|
int level = (color_index % num_color_shades);
|
|
nassertr(index >= 0 && index < (int)_colors.size(),
|
|
Colorf(0.0, 0.0, 0.0, 0.0));
|
|
|
|
Colorf color = _colors[index].get_color();
|
|
return color * ((double)level / (double)(num_color_shades - 1));
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::get_rgb
|
|
// Access: Public
|
|
// Description: Returns the three-component color corresponding to
|
|
// the given color index, ignoring the alpha component.
|
|
// Each component will be in the range [0, 1].
|
|
////////////////////////////////////////////////////////////////////
|
|
RGBColorf FltHeader::
|
|
get_rgb(int color_index) const {
|
|
nassertr(color_index >= 0 && color_index < get_num_colors(),
|
|
RGBColorf(0.0, 0.0, 0.0));
|
|
int num_color_shades = get_num_color_shades();
|
|
|
|
int index = (color_index / num_color_shades);
|
|
int level = (color_index % num_color_shades);
|
|
nassertr(index >= 0 && index < (int)_colors.size(),
|
|
RGBColorf(0.0, 0.0, 0.0));
|
|
|
|
RGBColorf color = _colors[index].get_rgb();
|
|
return color * ((double)level / (double)(num_color_shades - 1));
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::has_color_name
|
|
// Access: Public
|
|
// Description: Returns true if the given color is named, false
|
|
// otherwise.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool FltHeader::
|
|
has_color_name(int color_index) const {
|
|
return (_color_names.count(color_index) != 0);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::get_color_name
|
|
// Access: Public
|
|
// Description: Returns the name associated with the given color, if
|
|
// any.
|
|
////////////////////////////////////////////////////////////////////
|
|
string FltHeader::
|
|
get_color_name(int color_index) const {
|
|
ColorNames::const_iterator ni;
|
|
ni = _color_names.find(color_index);
|
|
if (ni != _color_names.end()) {
|
|
return (*ni).second;
|
|
}
|
|
return string();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::get_closest_color
|
|
// Access: Public
|
|
// Description: Returns the color index of the nearest color in the
|
|
// palette that matches the given four-component color,
|
|
// including alpha.
|
|
////////////////////////////////////////////////////////////////////
|
|
int FltHeader::
|
|
get_closest_color(Colorf color) const {
|
|
// Since the colortable stores the brightest colors, with
|
|
// num_color_shades scaled versions of each color implicitly
|
|
// available, we really only care about the relative brightnesses of
|
|
// the various components. Normalize the color in terms of the
|
|
// largest of these.
|
|
|
|
double scale = 1.0;
|
|
|
|
if (color[0] == 0.0 && color[1] == 0.0 && color[2] == 0.0 && color[3] == 0.0) {
|
|
// Oh, this is invisible black.
|
|
scale = 0.0;
|
|
color.set(1.0, 1.0, 1.0, 1.0);
|
|
|
|
} else {
|
|
if (color[0] >= color[1] && color[0] >= color[2] && color[0] >= color[3]) {
|
|
// color[0] is largest.
|
|
scale = color[0];
|
|
|
|
} else if (color[1] >= color[2] && color[1] >= color[3]) {
|
|
// color[1] is largest.
|
|
scale = color[1];
|
|
|
|
} else if (color[2] >= color[3]) {
|
|
// color[2] is largest.
|
|
scale = color[2];
|
|
|
|
} else {
|
|
// color[3] is largest.
|
|
scale = color[3];
|
|
}
|
|
color /= scale;
|
|
}
|
|
|
|
// Now search for the best match.
|
|
float best_dist = 5.0; // Greater than 4.
|
|
int best_i = -1;
|
|
|
|
int num_color_entries = get_num_color_entries();
|
|
for (int i = 0; i < num_color_entries; i++) {
|
|
Colorf consider = _colors[i].get_color();
|
|
float dist2 = dot(consider - color, consider - color);
|
|
nassertr(dist2 < 5.0, 0);
|
|
|
|
if (dist2 < best_dist) {
|
|
best_dist = dist2;
|
|
best_i = i;
|
|
}
|
|
}
|
|
nassertr(best_i >= 0, 0);
|
|
|
|
int num_color_shades = get_num_color_shades();
|
|
int shade_index = (int)floor((num_color_shades-1) * scale + 0.5);
|
|
|
|
return (best_i * num_color_shades) + shade_index;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::get_closest_color
|
|
// Access: Public
|
|
// Description: Returns the color index of the nearest color in the
|
|
// palette that matches the given three-component color,
|
|
// ignoring alpha.
|
|
////////////////////////////////////////////////////////////////////
|
|
int FltHeader::
|
|
get_closest_rgb(RGBColorf color) const {
|
|
// Since the colortable stores the brightest colors, with
|
|
// num_color_shades scaled versions of each color implicitly
|
|
// available, we really only care about the relative brightnesses of
|
|
// the various components. Normalize the color in terms of the
|
|
// largest of these.
|
|
|
|
double scale = 1.0;
|
|
|
|
if (color[0] == 0.0 && color[1] == 0.0 && color[2] == 0.0) {
|
|
// Oh, this is black.
|
|
scale = 0.0;
|
|
color.set(1.0, 1.0, 1.0);
|
|
|
|
} else {
|
|
if (color[0] >= color[1] && color[0] >= color[2]) {
|
|
// color[0] is largest.
|
|
scale = color[0];
|
|
|
|
} else if (color[1] >= color[2]) {
|
|
// color[1] is largest.
|
|
scale = color[1];
|
|
|
|
} else {
|
|
// color[2] is largest.
|
|
scale = color[2];
|
|
}
|
|
color /= scale;
|
|
}
|
|
|
|
// Now search for the best match.
|
|
float best_dist = 5.0; // Greater than 4.
|
|
int best_i = -1;
|
|
|
|
int num_color_entries = get_num_color_entries();
|
|
for (int i = 0; i < num_color_entries; i++) {
|
|
RGBColorf consider = _colors[i].get_rgb();
|
|
float dist2 = dot(consider - color, consider - color);
|
|
nassertr(dist2 < 5.0, 0);
|
|
|
|
if (dist2 < best_dist) {
|
|
best_dist = dist2;
|
|
best_i = i;
|
|
}
|
|
}
|
|
nassertr(best_i >= 0, 0);
|
|
|
|
int num_color_shades = get_num_color_shades();
|
|
int shade_index = (int)floor((num_color_shades-1) * scale + 0.5);
|
|
|
|
return (best_i * num_color_shades) + shade_index;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::get_num_color_entries
|
|
// Access: Public
|
|
// Description: Returns the number of actual entries in the color
|
|
// palette. This is based on the version of the flt
|
|
// file, and is usually either 512 or 1024.
|
|
////////////////////////////////////////////////////////////////////
|
|
int FltHeader::
|
|
get_num_color_entries() const {
|
|
return _colors.size();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::get_num_color_shades
|
|
// Access: Public
|
|
// Description: Returns the number of shades of brightness of each
|
|
// entry in the color palette. This is a fixed property
|
|
// of MultiGen files: each entry in the palette actually
|
|
// represents a range of this many colors.
|
|
////////////////////////////////////////////////////////////////////
|
|
int FltHeader::
|
|
get_num_color_shades() const {
|
|
return 128;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::get_color
|
|
// Access: Public
|
|
// Description: Decodes a MultiGen color, as stored on a face or
|
|
// vertex, into an actual four-component Colorf.
|
|
// Normally you need not call this directly; there are
|
|
// color accessors defined on faces and vertices that do
|
|
// this.
|
|
////////////////////////////////////////////////////////////////////
|
|
Colorf FltHeader::
|
|
get_color(int color_index, bool use_packed_color,
|
|
const FltPackedColor &packed_color,
|
|
int transparency) {
|
|
if (!use_packed_color) {
|
|
return get_color(color_index);
|
|
}
|
|
|
|
Colorf color;
|
|
color[0] = packed_color._r / 255.0;
|
|
color[1] = packed_color._g / 255.0;
|
|
color[2] = packed_color._b / 255.0;
|
|
// MultiGen doesn't yet use the A component of RGBA.
|
|
//color[3] = packed_color._a / 255.0;
|
|
color[3] = 1.0 - (transparency / 65535.0);
|
|
return color;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::get_color
|
|
// Access: Public
|
|
// Description: Decodes a MultiGen color, as stored on a face or
|
|
// vertex, into an actual three-component RGBColorf.
|
|
// Normally you need not call this directly; there are
|
|
// color accessors defined on faces and vertices that do
|
|
// this.
|
|
////////////////////////////////////////////////////////////////////
|
|
RGBColorf FltHeader::
|
|
get_rgb(int color_index, bool use_packed_color,
|
|
const FltPackedColor &packed_color) {
|
|
if (!use_packed_color) {
|
|
return get_rgb(color_index);
|
|
}
|
|
|
|
RGBColorf color;
|
|
color[0] = packed_color._r / 255.0;
|
|
color[1] = packed_color._g / 255.0;
|
|
color[2] = packed_color._b / 255.0;
|
|
return color;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::has_material
|
|
// Access: Public
|
|
// Description: Returns true if a material with the given index has
|
|
// been defined.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool FltHeader::
|
|
has_material(int material_index) const {
|
|
return (_materials.count(material_index) != 0);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::get_material
|
|
// Access: Public
|
|
// Description: Returns the material associated with the given index,
|
|
// or NULL if there is no such material.
|
|
////////////////////////////////////////////////////////////////////
|
|
FltMaterial *FltHeader::
|
|
get_material(int material_index) const {
|
|
Materials::const_iterator mi;
|
|
mi = _materials.find(material_index);
|
|
if (mi != _materials.end()) {
|
|
return (*mi).second;
|
|
}
|
|
return (FltMaterial *)NULL;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::clear_materials
|
|
// Access: Public
|
|
// Description: Removes all materials from the palette.
|
|
////////////////////////////////////////////////////////////////////
|
|
void FltHeader::
|
|
clear_materials() {
|
|
_materials.clear();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::add_material
|
|
// Access: Public
|
|
// Description: Defines a new material. The material is added in the
|
|
// position indicated by the material's index number.
|
|
// If there is already a material defined for that index
|
|
// number, it is replaced.
|
|
////////////////////////////////////////////////////////////////////
|
|
void FltHeader::
|
|
add_material(FltMaterial *material) {
|
|
_materials[material->_material_index] = material;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::remove_material
|
|
// Access: Public
|
|
// Description: Removes a particular material from the material
|
|
// palette, if it exists.
|
|
////////////////////////////////////////////////////////////////////
|
|
void FltHeader::
|
|
remove_material(int material_index) {
|
|
_materials.erase(material_index);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::has_texture
|
|
// Access: Public
|
|
// Description: Returns true if a texture with the given index has
|
|
// been defined.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool FltHeader::
|
|
has_texture(int texture_index) const {
|
|
return (_textures.count(texture_index) != 0);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::get_texture
|
|
// Access: Public
|
|
// Description: Returns the texture associated with the given index,
|
|
// or NULL if there is no such texture.
|
|
////////////////////////////////////////////////////////////////////
|
|
FltTexture *FltHeader::
|
|
get_texture(int texture_index) const {
|
|
Textures::const_iterator mi;
|
|
mi = _textures.find(texture_index);
|
|
if (mi != _textures.end()) {
|
|
return (*mi).second;
|
|
}
|
|
return (FltTexture *)NULL;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::clear_textures
|
|
// Access: Public
|
|
// Description: Removes all textures from the palette.
|
|
////////////////////////////////////////////////////////////////////
|
|
void FltHeader::
|
|
clear_textures() {
|
|
_textures.clear();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::add_texture
|
|
// Access: Public
|
|
// Description: Defines a new texture. The texture is added in the
|
|
// position indicated by the texture's index number.
|
|
// If there is already a texture defined for that index
|
|
// number, it is replaced.
|
|
////////////////////////////////////////////////////////////////////
|
|
void FltHeader::
|
|
add_texture(FltTexture *texture) {
|
|
_textures[texture->_pattern_index] = texture;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::remove_texture
|
|
// Access: Public
|
|
// Description: Removes a particular texture from the texture
|
|
// palette, if it exists.
|
|
////////////////////////////////////////////////////////////////////
|
|
void FltHeader::
|
|
remove_texture(int texture_index) {
|
|
_textures.erase(texture_index);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::set_texture_path
|
|
// Access: Public
|
|
// Description: Sets the search path that relative texture filenames
|
|
// will be looked for along.
|
|
////////////////////////////////////////////////////////////////////
|
|
void FltHeader::
|
|
set_texture_path(const DSearchPath &path) {
|
|
_texture_path = path;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::update_texture_path
|
|
// Access: Public
|
|
// Description: Returns a non-const reference to the texture search
|
|
// path, so that it may be appended to or otherwise
|
|
// modified.
|
|
////////////////////////////////////////////////////////////////////
|
|
DSearchPath &FltHeader::
|
|
update_texture_path() {
|
|
return _texture_path;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::get_texture_path
|
|
// Access: Public
|
|
// Description: Returns the search path for looking up texture
|
|
// filenames.
|
|
////////////////////////////////////////////////////////////////////
|
|
const DSearchPath &FltHeader::
|
|
get_texture_path() const {
|
|
return _texture_path;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::set_model_path
|
|
// Access: Public
|
|
// Description: Sets the search path that relative external
|
|
// references will be looked for along.
|
|
////////////////////////////////////////////////////////////////////
|
|
void FltHeader::
|
|
set_model_path(const DSearchPath &path) {
|
|
_model_path = path;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::update_model_path
|
|
// Access: Public
|
|
// Description: Returns a non-const reference to the model search
|
|
// path, so that it may be appended to or otherwise
|
|
// modified.
|
|
////////////////////////////////////////////////////////////////////
|
|
DSearchPath &FltHeader::
|
|
update_model_path() {
|
|
return _model_path;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::get_model_path
|
|
// Access: Public
|
|
// Description: Returns the search path for looking up external
|
|
// references.
|
|
////////////////////////////////////////////////////////////////////
|
|
const DSearchPath &FltHeader::
|
|
get_model_path() const {
|
|
return _model_path;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::has_light_source
|
|
// Access: Public
|
|
// Description: Returns true if a light source with the given index
|
|
// has been defined.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool FltHeader::
|
|
has_light_source(int light_index) const {
|
|
return (_light_sources.count(light_index) != 0);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::get_light_source
|
|
// Access: Public
|
|
// Description: Returns the light source associated with the given
|
|
// index, or NULL if there is no such light source.
|
|
////////////////////////////////////////////////////////////////////
|
|
FltLightSourceDefinition *FltHeader::
|
|
get_light_source(int light_index) const {
|
|
LightSources::const_iterator li;
|
|
li = _light_sources.find(light_index);
|
|
if (li != _light_sources.end()) {
|
|
return (*li).second;
|
|
}
|
|
return (FltLightSourceDefinition *)NULL;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::clear_light_sources
|
|
// Access: Public
|
|
// Description: Removes all light sources from the palette.
|
|
////////////////////////////////////////////////////////////////////
|
|
void FltHeader::
|
|
clear_light_sources() {
|
|
_light_sources.clear();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::add_light_source
|
|
// Access: Public
|
|
// Description: Defines a new light source. The light source is
|
|
// added in the position indicated by its light index
|
|
// number. If there is already a light source defined
|
|
// for that index number, it is replaced.
|
|
////////////////////////////////////////////////////////////////////
|
|
void FltHeader::
|
|
add_light_source(FltLightSourceDefinition *light_source) {
|
|
_light_sources[light_source->_light_index] = light_source;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::remove_light_source
|
|
// Access: Public
|
|
// Description: Removes a particular light source from the light
|
|
// source palette, if it exists.
|
|
////////////////////////////////////////////////////////////////////
|
|
void FltHeader::
|
|
remove_light_source(int light_index) {
|
|
_light_sources.erase(light_index);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::got_eyepoint_trackplane_palette
|
|
// Access: Public
|
|
// Description: Returns true if we have read an eyepoint/trackplane
|
|
// palette, and at least some of the eyepoints and
|
|
// trackplanes are therefore expected to be meaningful.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool FltHeader::
|
|
got_eyepoint_trackplane_palette() const {
|
|
return _got_eyepoint_trackplane_palette;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::set_eyepoint_trackplane_palette
|
|
// Access: Public
|
|
// Description: Sets the state of the eyepoint/trackplane palette
|
|
// flag. When this is false, the palette is believed to
|
|
// be meaningless, and will not be written; when it is
|
|
// true, the palette is believed to contain at least
|
|
// some meaningful data, and will be written.
|
|
////////////////////////////////////////////////////////////////////
|
|
void FltHeader::
|
|
set_eyepoint_trackplane_palette(bool flag) {
|
|
_got_eyepoint_trackplane_palette = flag;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::get_num_eyepoints
|
|
// Access: Public
|
|
// Description: Returns the number of eyepoints in the
|
|
// eyepoint/trackplane palette. This is presently fixed
|
|
// at 10, according to the MultiGen specs.
|
|
////////////////////////////////////////////////////////////////////
|
|
int FltHeader::
|
|
get_num_eyepoints() const {
|
|
return 10;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::get_eyepoint
|
|
// Access: Public
|
|
// Description: Returns the nth eyepoint in the eyepoint/trackplane
|
|
// palette.
|
|
////////////////////////////////////////////////////////////////////
|
|
FltEyepoint *FltHeader::
|
|
get_eyepoint(int n) {
|
|
nassertr(n >= 0 && n < get_num_eyepoints(), (FltEyepoint *)NULL);
|
|
return &_eyepoints[n];
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::get_num_trackplanes
|
|
// Access: Public
|
|
// Description: Returns the number of trackplanes in the
|
|
// eyepoint/trackplane palette. This is presently fixed
|
|
// at 10, according to the MultiGen specs.
|
|
////////////////////////////////////////////////////////////////////
|
|
int FltHeader::
|
|
get_num_trackplanes() const {
|
|
return 10;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::get_trackplane
|
|
// Access: Public
|
|
// Description: Returns the nth trackplane in the eyepoint/trackplane
|
|
// palette.
|
|
////////////////////////////////////////////////////////////////////
|
|
FltTrackplane *FltHeader::
|
|
get_trackplane(int n) {
|
|
nassertr(n >= 0 && n < get_num_trackplanes(), (FltTrackplane *)NULL);
|
|
return &_trackplanes[n];
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::update_vertex_lookups
|
|
// Access: Public
|
|
// Description: Recomputes the offsets_by_vertex and
|
|
// vertices_by_offset tables. This reflects the flt
|
|
// file as it will be written out, but not necessarily
|
|
// as it was read in.
|
|
//
|
|
// The return value is the total length of the vertex
|
|
// palette, including the header record.
|
|
////////////////////////////////////////////////////////////////////
|
|
int FltHeader::
|
|
update_vertex_lookups() {
|
|
// We start with the length of the vertex palette record itself.
|
|
int offset = 8;
|
|
|
|
Vertices::const_iterator vi;
|
|
for (vi = _vertices.begin(); vi != _vertices.end(); ++vi) {
|
|
FltVertex *vertex = (*vi);
|
|
|
|
_offsets_by_vertex[vertex] = offset;
|
|
_vertices_by_offset[offset] = vertex;
|
|
offset += vertex->get_record_length();
|
|
}
|
|
|
|
_vertex_lookups_stale = false;
|
|
|
|
return offset;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::extract_record
|
|
// Access: Protected, Virtual
|
|
// Description: Fills in the information in this bead based on the
|
|
// information given in the indicated datagram, whose
|
|
// opcode has already been read. Returns true on
|
|
// success, false if the datagram is invalid.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool FltHeader::
|
|
extract_record(FltRecordReader &reader) {
|
|
if (!FltBeadID::extract_record(reader)) {
|
|
return false;
|
|
}
|
|
|
|
nassertr(reader.get_opcode() == FO_header, false);
|
|
DatagramIterator &iterator = reader.get_iterator();
|
|
|
|
_format_revision_level = iterator.get_be_int32();
|
|
_edit_revision_level = iterator.get_be_int32();
|
|
_last_revision = iterator.get_fixed_string(32);
|
|
_next_group_id = iterator.get_be_int16();
|
|
_next_lod_id = iterator.get_be_int16();
|
|
_next_object_id = iterator.get_be_int16();
|
|
_next_face_id = iterator.get_be_int16();
|
|
_unit_multiplier = iterator.get_be_int16();
|
|
_vertex_units = (Units)iterator.get_int8();
|
|
_texwhite_new = (iterator.get_int8() != 0);
|
|
_flags = iterator.get_be_uint32();
|
|
iterator.skip_bytes(24);
|
|
_projection_type = (ProjectionType)iterator.get_be_int32();
|
|
iterator.skip_bytes(28);
|
|
_next_dof_id = iterator.get_be_int16();
|
|
_vertex_storage_type = (VertexStorageType)iterator.get_be_int16();
|
|
_database_origin = (DatabaseOrigin)iterator.get_be_int32();
|
|
_sw_x = iterator.get_be_float64();
|
|
_sw_y = iterator.get_be_float64();
|
|
_delta_x = iterator.get_be_float64();
|
|
_delta_y = iterator.get_be_float64();
|
|
_next_sound_id = iterator.get_be_int16();
|
|
_next_path_id = iterator.get_be_int16();
|
|
iterator.skip_bytes(8);
|
|
_next_clip_id = iterator.get_be_int16();
|
|
_next_text_id = iterator.get_be_int16();
|
|
_next_bsp_id = iterator.get_be_int16();
|
|
_next_switch_id = iterator.get_be_int16();
|
|
iterator.skip_bytes(4);
|
|
_sw_lat = iterator.get_be_float64();
|
|
_sw_long = iterator.get_be_float64();
|
|
_ne_lat = iterator.get_be_float64();
|
|
_ne_long = iterator.get_be_float64();
|
|
_origin_lat = iterator.get_be_float64();
|
|
_origin_long = iterator.get_be_float64();
|
|
_lambert_upper_lat = iterator.get_be_float64();
|
|
_lambert_lower_lat = iterator.get_be_float64();
|
|
_next_light_id = iterator.get_be_int16();
|
|
iterator.skip_bytes(2);
|
|
_next_road_id = iterator.get_be_int16();
|
|
_next_cat_id = iterator.get_be_int16();
|
|
iterator.skip_bytes(2 + 2 + 2 + 2);
|
|
_earth_model = (EarthModel)iterator.get_be_int32();
|
|
|
|
// Undocumented additional padding.
|
|
iterator.skip_bytes(4);
|
|
|
|
// nassertr(iterator.get_remaining_size() == 0, true);
|
|
return true;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::extract_ancillary
|
|
// Access: Protected, Virtual
|
|
// Description: Checks whether the given bead, which follows this
|
|
// bead sequentially in the file, is an ancillary record
|
|
// of this bead. If it is, extracts the relevant
|
|
// information and returns true; otherwise, leaves it
|
|
// alone and returns false.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool FltHeader::
|
|
extract_ancillary(FltRecordReader &reader) {
|
|
switch (reader.get_opcode()) {
|
|
case FO_vertex_palette:
|
|
// We're about to begin the vertex palette!
|
|
clear_vertices();
|
|
_current_vertex_offset = reader.get_record_length();
|
|
return true;
|
|
|
|
case FO_vertex_c:
|
|
case FO_vertex_cn:
|
|
case FO_vertex_cnu:
|
|
case FO_vertex_cu:
|
|
// Here's a new vertex for the palette.
|
|
return extract_vertex(reader);
|
|
|
|
case FO_color_palette:
|
|
return extract_color_palette(reader);
|
|
|
|
case FO_15_material:
|
|
return extract_material(reader);
|
|
|
|
case FO_texture:
|
|
return extract_texture(reader);
|
|
|
|
case FO_texture_map_palette:
|
|
return extract_texture_map(reader);
|
|
|
|
case FO_light_definition:
|
|
return extract_light_source(reader);
|
|
|
|
case FO_eyepoint_palette:
|
|
return extract_eyepoint_palette(reader);
|
|
|
|
default:
|
|
return FltBeadID::extract_ancillary(reader);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::build_record
|
|
// Access: Protected, Virtual
|
|
// Description: Fills up the current record on the FltRecordWriter with
|
|
// data for this record, but does not advance the
|
|
// writer. Returns true on success, false if there is
|
|
// some error.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool FltHeader::
|
|
build_record(FltRecordWriter &writer) const {
|
|
if (!FltBeadID::build_record(writer)) {
|
|
return false;
|
|
}
|
|
|
|
writer.set_opcode(FO_header);
|
|
Datagram &datagram = writer.update_datagram();
|
|
|
|
datagram.add_be_int32(_format_revision_level);
|
|
datagram.add_be_int32(_edit_revision_level);
|
|
datagram.add_fixed_string(_last_revision, 32);
|
|
datagram.add_be_int16(_next_group_id);
|
|
datagram.add_be_int16(_next_lod_id);
|
|
datagram.add_be_int16(_next_object_id);
|
|
datagram.add_be_int16(_next_face_id);
|
|
datagram.add_be_int16(_unit_multiplier);
|
|
datagram.add_int8(_vertex_units);
|
|
datagram.add_int8(_texwhite_new);
|
|
datagram.add_be_uint32(_flags);
|
|
datagram.pad_bytes(24);
|
|
datagram.add_be_int32(_projection_type);
|
|
datagram.pad_bytes(28);
|
|
datagram.add_be_int16(_next_dof_id);
|
|
datagram.add_be_int16(_vertex_storage_type);
|
|
datagram.add_be_int32(_database_origin);
|
|
datagram.add_be_float64(_sw_x);
|
|
datagram.add_be_float64(_sw_y);
|
|
datagram.add_be_float64(_delta_x);
|
|
datagram.add_be_float64(_delta_y);
|
|
datagram.add_be_int16(_next_sound_id);
|
|
datagram.add_be_int16(_next_path_id);
|
|
datagram.pad_bytes(8);
|
|
datagram.add_be_int16(_next_clip_id);
|
|
datagram.add_be_int16(_next_text_id);
|
|
datagram.add_be_int16(_next_bsp_id);
|
|
datagram.add_be_int16(_next_switch_id);
|
|
datagram.pad_bytes(4);
|
|
datagram.add_be_float64(_sw_lat);
|
|
datagram.add_be_float64(_sw_long);
|
|
datagram.add_be_float64(_ne_lat);
|
|
datagram.add_be_float64(_ne_long);
|
|
datagram.add_be_float64(_origin_lat);
|
|
datagram.add_be_float64(_origin_long);
|
|
datagram.add_be_float64(_lambert_upper_lat);
|
|
datagram.add_be_float64(_lambert_lower_lat);
|
|
datagram.add_be_int16(_next_light_id);
|
|
datagram.pad_bytes(2);
|
|
datagram.add_be_int16(_next_road_id);
|
|
datagram.add_be_int16(_next_cat_id);
|
|
datagram.pad_bytes(2 + 2 + 2 + 2);
|
|
datagram.add_be_int32(_earth_model);
|
|
|
|
// Undocumented additional padding.
|
|
datagram.pad_bytes(4);
|
|
|
|
return true;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::write_ancillary
|
|
// Access: Protected, Virtual
|
|
// Description: Writes whatever ancillary records are required for
|
|
// this bead. Returns FE_ok on success, or something
|
|
// else on error.
|
|
////////////////////////////////////////////////////////////////////
|
|
FltError FltHeader::
|
|
write_ancillary(FltRecordWriter &writer) const {
|
|
FltError result;
|
|
|
|
result = write_color_palette(writer);
|
|
if (result != FE_ok) {
|
|
return result;
|
|
}
|
|
|
|
result = write_material_palette(writer);
|
|
if (result != FE_ok) {
|
|
return result;
|
|
}
|
|
|
|
result = write_texture_palette(writer);
|
|
if (result != FE_ok) {
|
|
return result;
|
|
}
|
|
|
|
result = write_light_source_palette(writer);
|
|
if (result != FE_ok) {
|
|
return result;
|
|
}
|
|
|
|
result = write_eyepoint_palette(writer);
|
|
if (result != FE_ok) {
|
|
return result;
|
|
}
|
|
|
|
result = write_vertex_palette(writer);
|
|
if (result != FE_ok) {
|
|
return result;
|
|
}
|
|
|
|
return FltBeadID::write_ancillary(writer);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::extract_vertex
|
|
// Access: Private
|
|
// Description: Reads a single vertex ancillary record. It is
|
|
// assumed that all the vertex records will immediately
|
|
// follow the vertex palette record.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool FltHeader::
|
|
extract_vertex(FltRecordReader &reader) {
|
|
FltVertex *vertex = new FltVertex(this);
|
|
if (!vertex->extract_record(reader)) {
|
|
return false;
|
|
}
|
|
_vertices.push_back(vertex);
|
|
_unique_vertices.insert(vertex);
|
|
_offsets_by_vertex[vertex] = _current_vertex_offset;
|
|
_vertices_by_offset[_current_vertex_offset] = vertex;
|
|
_current_vertex_offset += reader.get_record_length();
|
|
|
|
// _vertex_lookups_stale remains false.
|
|
|
|
return true;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::extract_color_palette
|
|
// Access: Private
|
|
// Description: Reads the color palette.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool FltHeader::
|
|
extract_color_palette(FltRecordReader &reader) {
|
|
nassertr(reader.get_opcode() == FO_color_palette, false);
|
|
DatagramIterator &iterator = reader.get_iterator();
|
|
|
|
if (_got_color_palette) {
|
|
nout << "Warning: multiple color palettes found.\n";
|
|
}
|
|
_got_color_palette = true;
|
|
|
|
static const int expected_color_entries = 1024;
|
|
|
|
iterator.skip_bytes(128);
|
|
_colors.clear();
|
|
for (int i = 0; i < expected_color_entries; i++) {
|
|
if (iterator.get_remaining_size() == 0) {
|
|
// An early end to the palette is acceptable.
|
|
return true;
|
|
}
|
|
FltPackedColor color;
|
|
if (!color.extract_record(reader)) {
|
|
return false;
|
|
}
|
|
_colors.push_back(color);
|
|
}
|
|
|
|
// Now pull out the color names.
|
|
while (iterator.get_remaining_size() > 0) {
|
|
int entry_length = iterator.get_be_uint16();
|
|
iterator.skip_bytes(2);
|
|
int color_index = iterator.get_be_int16();
|
|
iterator.skip_bytes(2);
|
|
|
|
int name_length = entry_length - 8;
|
|
nassertr(color_index >= 0 && color_index < (int)_colors.size(), false);
|
|
_color_names[color_index] = iterator.get_fixed_string(name_length);
|
|
}
|
|
|
|
nassertr(iterator.get_remaining_size() == 0, true);
|
|
return true;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::extract_material
|
|
// Access: Private
|
|
// Description: Reads a single material ancillary record.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool FltHeader::
|
|
extract_material(FltRecordReader &reader) {
|
|
FltMaterial *material = new FltMaterial(this);
|
|
if (!material->extract_record(reader)) {
|
|
return false;
|
|
}
|
|
add_material(material);
|
|
|
|
return true;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::extract_texture
|
|
// Access: Private
|
|
// Description: Reads a single texture ancillary record.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool FltHeader::
|
|
extract_texture(FltRecordReader &reader) {
|
|
FltTexture *texture = new FltTexture(this);
|
|
if (!texture->extract_record(reader)) {
|
|
return false;
|
|
}
|
|
add_texture(texture);
|
|
|
|
return true;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::extract_texture_map
|
|
// Access: Private
|
|
// Description: Reads the a single texture mapping ancillary record.
|
|
// This describes a kind of texture mapping in the
|
|
// texture mapping palette.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool FltHeader::
|
|
extract_texture_map(FltRecordReader &reader) {
|
|
// At the moment, we ignore this, since it's not needed for
|
|
// meaningful extraction of data: we can get this information from
|
|
// the UV's for a particular model. We just add an
|
|
// UnsupportedRecord for it.
|
|
FltUnsupportedRecord *rec = new FltUnsupportedRecord(this);
|
|
if (!rec->extract_record(reader)) {
|
|
return false;
|
|
}
|
|
add_ancillary(rec);
|
|
|
|
return true;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::extract_light_source
|
|
// Access: Private
|
|
// Description: Reads a single light source ancillary record.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool FltHeader::
|
|
extract_light_source(FltRecordReader &reader) {
|
|
FltLightSourceDefinition *light_source = new FltLightSourceDefinition(this);
|
|
if (!light_source->extract_record(reader)) {
|
|
return false;
|
|
}
|
|
add_light_source(light_source);
|
|
|
|
return true;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::extract_eyepoint_palette
|
|
// Access: Private
|
|
// Description: Reads the eyepoint/trackplane palette.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool FltHeader::
|
|
extract_eyepoint_palette(FltRecordReader &reader) {
|
|
nassertr(reader.get_opcode() == FO_eyepoint_palette, false);
|
|
DatagramIterator &iterator = reader.get_iterator();
|
|
|
|
iterator.skip_bytes(4);
|
|
|
|
int i;
|
|
int num_eyepoints = get_num_eyepoints();
|
|
for (i = 0; i < num_eyepoints; i++) {
|
|
if (!_eyepoints[i].extract_record(reader)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
int num_trackplanes = get_num_trackplanes();
|
|
for (i = 0; i < num_trackplanes; i++) {
|
|
if (!_trackplanes[i].extract_record(reader)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
_got_eyepoint_trackplane_palette = true;
|
|
nassertr(iterator.get_remaining_size() == 0, true);
|
|
return true;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::write_vertex_palette
|
|
// Access: Private
|
|
// Description: Writes out the vertex palette with all of its
|
|
// vertices.
|
|
////////////////////////////////////////////////////////////////////
|
|
FltError FltHeader::
|
|
write_vertex_palette(FltRecordWriter &writer) const {
|
|
FltError result;
|
|
|
|
int vertex_palette_length =
|
|
((FltHeader *)this)->update_vertex_lookups();
|
|
Datagram vertex_palette;
|
|
vertex_palette.add_be_int32(vertex_palette_length);
|
|
result = writer.write_record(FO_vertex_palette, vertex_palette);
|
|
if (result != FE_ok) {
|
|
return result;
|
|
}
|
|
// Now write out each vertex in the palette.
|
|
Vertices::const_iterator vi;
|
|
for (vi = _vertices.begin(); vi != _vertices.end(); ++vi) {
|
|
FltVertex *vertex = (*vi);
|
|
vertex->build_record(writer);
|
|
result = writer.advance();
|
|
if (result != FE_ok) {
|
|
return result;
|
|
}
|
|
}
|
|
|
|
return FE_ok;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::write_color_palette
|
|
// Access: Private
|
|
// Description: Writes out the color palette.
|
|
////////////////////////////////////////////////////////////////////
|
|
FltError FltHeader::
|
|
write_color_palette(FltRecordWriter &writer) const {
|
|
writer.set_opcode(FO_color_palette);
|
|
Datagram &datagram = writer.update_datagram();
|
|
|
|
datagram.pad_bytes(128);
|
|
|
|
// How many colors should we write?
|
|
int num_colors = 1024;
|
|
|
|
Colors::const_iterator ci;
|
|
for (ci = _colors.begin(); num_colors > 0 && ci != _colors.end(); ++ci) {
|
|
if (!(*ci).build_record(writer)) {
|
|
assert(!flt_error_abort);
|
|
return FE_invalid_record;
|
|
}
|
|
num_colors--;
|
|
}
|
|
|
|
// Now we might need to pad the record to fill up the required
|
|
// number of colors.
|
|
if (num_colors > 0) {
|
|
FltPackedColor empty;
|
|
while (num_colors > 0) {
|
|
if (!empty.build_record(writer)) {
|
|
assert(!flt_error_abort);
|
|
return FE_invalid_record;
|
|
}
|
|
num_colors--;
|
|
}
|
|
}
|
|
|
|
// Now append all the names at the end.
|
|
ColorNames::const_iterator ni;
|
|
for (ni = _color_names.begin(); ni != _color_names.end(); ++ni) {
|
|
string name = (*ni).second.substr(0, 80);
|
|
int entry_length = name.length() + 8;
|
|
datagram.add_be_uint16(entry_length);
|
|
datagram.pad_bytes(2);
|
|
datagram.add_be_uint16((*ni).first);
|
|
datagram.pad_bytes(2);
|
|
datagram.add_fixed_string(name, name.length());
|
|
}
|
|
|
|
return writer.advance();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::write_material_palette
|
|
// Access: Private
|
|
// Description: Writes out the material palette.
|
|
////////////////////////////////////////////////////////////////////
|
|
FltError FltHeader::
|
|
write_material_palette(FltRecordWriter &writer) const {
|
|
FltError result;
|
|
|
|
Materials::const_iterator mi;
|
|
for (mi = _materials.begin(); mi != _materials.end(); ++mi) {
|
|
FltMaterial *material = (*mi).second;
|
|
material->build_record(writer);
|
|
result = writer.advance();
|
|
if (result != FE_ok) {
|
|
return result;
|
|
}
|
|
}
|
|
|
|
return FE_ok;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::write_texture_palette
|
|
// Access: Private
|
|
// Description: Writes out the texture palette.
|
|
////////////////////////////////////////////////////////////////////
|
|
FltError FltHeader::
|
|
write_texture_palette(FltRecordWriter &writer) const {
|
|
FltError result;
|
|
|
|
Textures::const_iterator ti;
|
|
for (ti = _textures.begin(); ti != _textures.end(); ++ti) {
|
|
FltTexture *texture = (*ti).second;
|
|
texture->build_record(writer);
|
|
result = writer.advance();
|
|
if (result != FE_ok) {
|
|
return result;
|
|
}
|
|
}
|
|
|
|
return FE_ok;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::write_light_source_palette
|
|
// Access: Private
|
|
// Description: Writes out the light source palette.
|
|
////////////////////////////////////////////////////////////////////
|
|
FltError FltHeader::
|
|
write_light_source_palette(FltRecordWriter &writer) const {
|
|
FltError result;
|
|
|
|
LightSources::const_iterator li;
|
|
for (li = _light_sources.begin(); li != _light_sources.end(); ++li) {
|
|
FltLightSourceDefinition *light_source = (*li).second;
|
|
light_source->build_record(writer);
|
|
result = writer.advance();
|
|
if (result != FE_ok) {
|
|
return result;
|
|
}
|
|
}
|
|
|
|
return FE_ok;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: FltHeader::write_eyepoint_palette
|
|
// Access: Private
|
|
// Description: Writes out the eyepoint/trackplane palette, if we
|
|
// have one.
|
|
////////////////////////////////////////////////////////////////////
|
|
FltError FltHeader::
|
|
write_eyepoint_palette(FltRecordWriter &writer) const {
|
|
if (!_got_eyepoint_trackplane_palette) {
|
|
return FE_ok;
|
|
}
|
|
|
|
writer.set_opcode(FO_eyepoint_palette);
|
|
Datagram &datagram = writer.update_datagram();
|
|
datagram.pad_bytes(4);
|
|
|
|
int i;
|
|
int num_eyepoints = get_num_eyepoints();
|
|
for (i = 0; i < num_eyepoints; i++) {
|
|
if (!_eyepoints[i].build_record(writer)) {
|
|
assert(!flt_error_abort);
|
|
return FE_bad_data;
|
|
}
|
|
}
|
|
|
|
int num_trackplanes = get_num_trackplanes();
|
|
for (i = 0; i < num_trackplanes; i++) {
|
|
if (!_trackplanes[i].build_record(writer)) {
|
|
assert(!flt_error_abort);
|
|
return FE_bad_data;
|
|
}
|
|
}
|
|
|
|
return writer.advance();
|
|
}
|