Browse Source

Initial revision

David Rose 25 years ago
parent
commit
67a298793d
100 changed files with 10710 additions and 0 deletions
  1. 18 0
      pandatool/Config.pp
  2. 34 0
      pandatool/Package.pp
  3. 10 0
      pandatool/Sources.pp
  4. 4 0
      pandatool/src/Sources.pp
  5. 32 0
      pandatool/src/bam/Sources.pp
  6. 167 0
      pandatool/src/bam/bamInfo.cxx
  7. 45 0
      pandatool/src/bam/bamInfo.h
  8. 74 0
      pandatool/src/bam/eggToBam.cxx
  9. 24 0
      pandatool/src/bam/eggToBam.h
  10. 1 0
      pandatool/src/configfiles/Sources.pp
  11. 3 0
      pandatool/src/configfiles/pandatool.init
  12. 19 0
      pandatool/src/eggbase/Sources.pp
  13. 93 0
      pandatool/src/eggbase/eggBase.cxx
  14. 41 0
      pandatool/src/eggbase/eggBase.h
  15. 28 0
      pandatool/src/eggbase/eggConverter.cxx
  16. 33 0
      pandatool/src/eggbase/eggConverter.h
  17. 56 0
      pandatool/src/eggbase/eggFilter.cxx
  18. 28 0
      pandatool/src/eggbase/eggFilter.h
  19. 51 0
      pandatool/src/eggbase/eggReader.cxx
  20. 30 0
      pandatool/src/eggbase/eggReader.h
  21. 104 0
      pandatool/src/eggbase/eggToSomething.cxx
  22. 32 0
      pandatool/src/eggbase/eggToSomething.h
  23. 211 0
      pandatool/src/eggbase/eggWriter.cxx
  24. 49 0
      pandatool/src/eggbase/eggWriter.h
  25. 86 0
      pandatool/src/eggbase/somethingToEgg.cxx
  26. 34 0
      pandatool/src/eggbase/somethingToEgg.h
  27. 16 0
      pandatool/src/eggprogs/Sources.pp
  28. 37 0
      pandatool/src/eggprogs/eggTrans.cxx
  29. 27 0
      pandatool/src/eggprogs/eggTrans.h
  30. 60 0
      pandatool/src/flt/Sources.pp
  31. 63 0
      pandatool/src/flt/config_flt.cxx
  32. 11 0
      pandatool/src/flt/config_flt.h
  33. 391 0
      pandatool/src/flt/fltBead.cxx
  34. 83 0
      pandatool/src/flt/fltBead.h
  35. 110 0
      pandatool/src/flt/fltBeadID.cxx
  36. 57 0
      pandatool/src/flt/fltBeadID.h
  37. 47 0
      pandatool/src/flt/fltError.cxx
  38. 32 0
      pandatool/src/flt/fltError.h
  39. 107 0
      pandatool/src/flt/fltExternalReference.cxx
  40. 63 0
      pandatool/src/flt/fltExternalReference.h
  41. 133 0
      pandatool/src/flt/fltEyepoint.cxx
  42. 52 0
      pandatool/src/flt/fltEyepoint.h
  43. 60 0
      pandatool/src/flt/fltFace.I
  44. 250 0
      pandatool/src/flt/fltFace.cxx
  45. 128 0
      pandatool/src/flt/fltFace.h
  46. 84 0
      pandatool/src/flt/fltGroup.cxx
  47. 59 0
      pandatool/src/flt/fltGroup.h
  48. 1556 0
      pandatool/src/flt/fltHeader.cxx
  49. 310 0
      pandatool/src/flt/fltHeader.h
  50. 67 0
      pandatool/src/flt/fltInstanceDefinition.cxx
  51. 56 0
      pandatool/src/flt/fltInstanceDefinition.h
  52. 108 0
      pandatool/src/flt/fltInstanceRef.cxx
  53. 54 0
      pandatool/src/flt/fltInstanceRef.h
  54. 91 0
      pandatool/src/flt/fltLOD.cxx
  55. 59 0
      pandatool/src/flt/fltLOD.h
  56. 129 0
      pandatool/src/flt/fltLightSourceDefinition.cxx
  57. 77 0
      pandatool/src/flt/fltLightSourceDefinition.h
  58. 106 0
      pandatool/src/flt/fltMaterial.cxx
  59. 63 0
      pandatool/src/flt/fltMaterial.h
  60. 76 0
      pandatool/src/flt/fltObject.cxx
  61. 60 0
      pandatool/src/flt/fltObject.h
  62. 253 0
      pandatool/src/flt/fltOpcode.cxx
  63. 101 0
      pandatool/src/flt/fltOpcode.h
  64. 47 0
      pandatool/src/flt/fltPackedColor.I
  65. 52 0
      pandatool/src/flt/fltPackedColor.cxx
  66. 46 0
      pandatool/src/flt/fltPackedColor.h
  67. 10 0
      pandatool/src/flt/fltRecord.I
  68. 648 0
      pandatool/src/flt/fltRecord.cxx
  69. 108 0
      pandatool/src/flt/fltRecord.h
  70. 176 0
      pandatool/src/flt/fltRecordReader.cxx
  71. 58 0
      pandatool/src/flt/fltRecordReader.h
  72. 140 0
      pandatool/src/flt/fltRecordWriter.cxx
  73. 51 0
      pandatool/src/flt/fltRecordWriter.h
  74. 431 0
      pandatool/src/flt/fltTexture.cxx
  75. 232 0
      pandatool/src/flt/fltTexture.h
  76. 95 0
      pandatool/src/flt/fltTrackplane.cxx
  77. 46 0
      pandatool/src/flt/fltTrackplane.h
  78. 92 0
      pandatool/src/flt/fltTransformGeneralMatrix.cxx
  79. 50 0
      pandatool/src/flt/fltTransformGeneralMatrix.h
  80. 213 0
      pandatool/src/flt/fltTransformPut.cxx
  81. 71 0
      pandatool/src/flt/fltTransformPut.h
  82. 29 0
      pandatool/src/flt/fltTransformRecord.cxx
  83. 53 0
      pandatool/src/flt/fltTransformRecord.h
  84. 155 0
      pandatool/src/flt/fltTransformRotateAboutEdge.cxx
  85. 57 0
      pandatool/src/flt/fltTransformRotateAboutEdge.h
  86. 152 0
      pandatool/src/flt/fltTransformRotateAboutPoint.cxx
  87. 57 0
      pandatool/src/flt/fltTransformRotateAboutPoint.h
  88. 230 0
      pandatool/src/flt/fltTransformRotateScale.cxx
  89. 66 0
      pandatool/src/flt/fltTransformRotateScale.h
  90. 133 0
      pandatool/src/flt/fltTransformScale.cxx
  91. 55 0
      pandatool/src/flt/fltTransformScale.h
  92. 132 0
      pandatool/src/flt/fltTransformTranslate.cxx
  93. 55 0
      pandatool/src/flt/fltTransformTranslate.h
  94. 63 0
      pandatool/src/flt/fltUnsupportedRecord.cxx
  95. 53 0
      pandatool/src/flt/fltUnsupportedRecord.h
  96. 16 0
      pandatool/src/flt/fltVertex.I
  97. 221 0
      pandatool/src/flt/fltVertex.cxx
  98. 85 0
      pandatool/src/flt/fltVertex.h
  99. 128 0
      pandatool/src/flt/fltVertexList.cxx
  100. 61 0
      pandatool/src/flt/fltVertexList.h

+ 18 - 0
pandatool/Config.pp

@@ -0,0 +1,18 @@
+//
+// Config.pp
+//
+// This file defines certain configuration variables that are written
+// into the various make scripts.  It is processed by ppremake (along
+// with the Sources.pp files in each of the various directories) to
+// generate build scripts appropriate to each environment.
+//
+// There are not too many variables to declare at this level; most of
+// them are defined in the DTOOL-specific Config.pp.
+
+
+// Where should we find PANDA?  This will come from the environment
+// variable if it is set.
+#if $[eq $[PANDA],]
+  #define PANDA /usr/local/panda
+#endif
+

+ 34 - 0
pandatool/Package.pp

@@ -0,0 +1,34 @@
+//
+// Package.pp
+//
+// This file defines certain configuration variables that are to be
+// written into the various make scripts.  It is processed by ppremake
+// (along with the Sources.pp files in each of the various
+// directories) to generate build scripts appropriate to each
+// environment.
+//
+// This is the package-specific file, which should be at the top of
+// every source hierarchy.  It generally gets the ball rolling, and is
+// responsible for explicitly including all of the relevent Config.pp
+// files.
+
+
+
+// What is the name and version of this source tree?
+#if $[eq $[PACKAGE],]
+  #define PACKAGE pandatool
+  #define VERSION 0.80
+#endif
+
+
+// Pull in the package-level Config file.  This contains a few
+// configuration variables that the user might want to fine-tune.
+#include $[THISDIRPREFIX]Config.pp
+
+
+// Also get the PANDA Package file and everything that includes.
+#if $[eq $[wildcard $[PANDA]],]
+  #error Directory defined by $PANDA not found!  Are you attached properly?
+#endif
+
+#include $[PANDA]/Package.pp

+ 10 - 0
pandatool/Sources.pp

@@ -0,0 +1,10 @@
+// This is the toplevel directory.  It contains configure.in and other
+// stuff.
+
+#define DIR_TYPE toplevel
+
+#define SAMPLE_SOURCE_FILE src/pandatoolbase/pandatoolbase.cxx
+#define REQUIRED_TREES dtool panda
+
+#define EXTRA_DIST \
+    Config.Irix.pp Config.Linux.pp Config.Win32.pp Package.pp

+ 4 - 0
pandatool/src/Sources.pp

@@ -0,0 +1,4 @@
+// This is a group directory: a directory level above a number of
+// source subdirectories.
+
+#define DIR_TYPE group

+ 32 - 0
pandatool/src/bam/Sources.pp

@@ -0,0 +1,32 @@
+#begin bin_target
+  #define TARGET bam-info
+  #define LOCAL_LIBS \
+    eggbase progbase config compiler
+  #define OTHER_LIBS \
+    loader:c egg:c sgraphutil:c sgattrib:c sgraph:c pnmimagetypes:c \
+    graph:c putil:c express:c panda:m interrogatedb:c dtool:m
+  #define UNIX_SYS_LIBS \
+    m
+
+  #define SOURCES \
+    bamInfo.cxx bamInfo.h
+
+  #define INSTALL_HEADERS \
+
+#end bin_target
+
+#begin bin_target
+  #define TARGET egg2bam
+  #define LOCAL_LIBS \
+    eggbase progbase config compiler
+  #define OTHER_LIBS \
+    loader:c egg2sg:c builder:c egg:c pnmimagetypes:c graph:c putil:c \
+    express:c panda:m interrogatedb:c dtool:m
+  #define UNIX_SYS_LIBS \
+    m
+
+  #define SOURCES \
+    eggToBam.cxx eggToBam.h
+
+#end bin_target
+

+ 167 - 0
pandatool/src/bam/bamInfo.cxx

@@ -0,0 +1,167 @@
+// Filename: bamInfo.cxx
+// Created by:  drose (02Jul00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "bamInfo.h"
+
+#include <bamFile.h>
+#include <node.h>
+#include <renderRelation.h>
+
+#include <vector>
+
+////////////////////////////////////////////////////////////////////
+//     Function: BamInfo::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+BamInfo::
+BamInfo() {
+  set_program_description
+    ("This program scans one or more Bam files--Panda's Binary Animation "
+     "and Models native binary format--and describes their contents.");
+
+  clear_runlines();
+  add_runline("[opts] input.bam [input.bam ... ]");
+
+  _num_scene_graphs = 0;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BamInfo::run
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void BamInfo::
+run() {
+  bool okflag = true;
+
+  Filenames::const_iterator fi;
+  for (fi = _filenames.begin(); fi != _filenames.end(); ++fi) {
+    if (!get_info(*fi)) {
+      okflag = false;
+    }
+  }
+
+  if (_num_scene_graphs > 0) {
+    nout << "\rScene graph statistics:\n";
+    _analyzer.write(nout, 2);
+  }
+  nout << "\r";
+
+  if (!okflag) {
+    // Exit with an error if any of the files was unreadable.
+    exit(1);
+  }
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BamInfo::handle_args
+//       Access: Protected, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+bool BamInfo::
+handle_args(ProgramBase::Args &args) {
+  if (args.empty()) {
+    nout << "You must specify the Bam file(s) to read on the command line.\n";
+    return false;
+  }
+
+  ProgramBase::Args::const_iterator ai;
+  for (ai = args.begin(); ai != args.end(); ++ai) {
+    _filenames.push_back(*ai);
+  }
+
+  return true;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BamInfo::get_info
+//       Access: Private
+//  Description: Reads a single Bam file and displays its contents.
+//               Returns true if successful, false on error.
+////////////////////////////////////////////////////////////////////
+bool BamInfo::
+get_info(const Filename &filename) {
+  BamFile bam_file;
+
+  if (!bam_file.open_read(filename)) {
+    nout << "Unable to read.\n";
+    return false;
+  }
+
+  nout << filename << " : Bam version " << bam_file.get_file_major_ver()
+       << "." << bam_file.get_file_minor_ver() << "\n";
+
+  typedef vector<TypedWriteable *> Objects;
+  Objects objects;
+  TypedWriteable *object = bam_file.read_object();
+  while (object != (TypedWriteable *)NULL) {
+    objects.push_back(object);
+    object = bam_file.read_object();
+  }
+  bam_file.resolve();
+  bam_file.close();
+
+  if (objects.size() == 1 && objects[0]->is_of_type(Node::get_class_type())) {
+    describe_scene_graph(DCAST(Node, objects[0]));
+
+  } else {
+    for (int i = 0; i < (int)objects.size(); i++) {
+      describe_general_object(objects[i]);
+    }
+  }
+
+  return true;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BamInfo::describe_scene_graph
+//       Access: Private
+//  Description: Called for Bam files that contain a single scene
+//               graph and no other objects.  This should describe
+//               that scene graph in some meaningful way.
+////////////////////////////////////////////////////////////////////
+void BamInfo::
+describe_scene_graph(Node *node) {
+  // Parent the node to our own scene graph root, so we can (a)
+  // guarantee it won't accidentally be deleted before we're done, (b)
+  // easily determine the bounding volume of the scene, and (c) report
+  // statistics on all the bam file's scene graphs together when we've
+  // finished.
+
+  PT_Node root = new Node;
+  NodeRelation *arc = new RenderRelation(root, node);
+  _num_scene_graphs++;
+
+  int num_nodes = _analyzer._num_nodes;
+  _analyzer.add_node(node);
+  num_nodes = _analyzer._num_nodes - num_nodes;
+
+  nout << "  " << num_nodes << " nodes, bounding volume is "
+       << arc->get_bound() << "\n";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BamInfo::describe_general_object
+//       Access: Private
+//  Description: Called for Bam files that contain multiple objects
+//               which may or may not be scene graph nodes.  This
+//               should describe each object in some meaningful way.
+////////////////////////////////////////////////////////////////////
+void BamInfo::
+describe_general_object(TypedWriteable *object) {
+  nout << "  " << object->get_type() << "\n";
+}
+
+int main(int argc, char *argv[]) {
+  BamInfo prog;
+  prog.parse_command_line(argc, argv);
+  prog.run();
+  return 0;
+}

+ 45 - 0
pandatool/src/bam/bamInfo.h

@@ -0,0 +1,45 @@
+// Filename: bamInfo.h
+// Created by:  drose (02Jul00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef BAMINFO_H
+#define BAMINFO_H
+
+#include <pandatoolbase.h>
+
+#include <eggToSomething.h>
+#include <filename.h>
+#include <pt_Node.h>
+#include <sceneGraphAnalyzer.h>
+
+#include <vector>
+
+class TypedWriteable;
+
+////////////////////////////////////////////////////////////////////
+// 	 Class : BamInfo
+// Description : 
+////////////////////////////////////////////////////////////////////
+class BamInfo : public ProgramBase {
+public:
+  BamInfo();
+
+  void run();
+
+protected:
+  virtual bool handle_args(Args &args);
+
+private:
+  bool get_info(const Filename &filename);
+  void describe_scene_graph(Node *node);
+  void describe_general_object(TypedWriteable *object);
+
+  typedef vector<Filename> Filenames;
+  Filenames _filenames;
+
+  int _num_scene_graphs;
+  SceneGraphAnalyzer _analyzer;
+};
+
+#endif

+ 74 - 0
pandatool/src/bam/eggToBam.cxx

@@ -0,0 +1,74 @@
+// Filename: eggToBam.cxx
+// Created by:  drose (28Jun00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "eggToBam.h"
+
+#include <bamFile.h>
+#include <load_egg_file.h>
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggToBam::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+EggToBam::
+EggToBam() :
+  EggToSomething("Bam", ".bam", true, false)
+{
+  set_program_description
+    ("This program reads Egg files and outputs Bam files, the binary format "
+     "suitable for direct loading of animation and models into Panda.");
+
+  redescribe_option
+    ("cs",
+     "Specify the coordinate system of the resulting " + _format_name +
+     " file.  This may be "
+     "one of 'y-up', 'z-up', 'y-up-left', or 'z-up-left'.  The default "
+     "is z-up.");
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggToBam::run
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void EggToBam::
+run() {
+  if (!_got_coordinate_system) {
+    // If the user didn't specify otherwise, ensure the coordinate
+    // system is Z-up.
+    _data.set_coordinate_system(CS_zup_right);
+  }
+
+  PT_NamedNode root = load_egg_data(_data);
+  if (root == (NamedNode *)NULL) {
+    nout << "Unable to build scene graph from egg file.\n";
+    exit(1);
+  }
+
+  // This should be guaranteed because we pass false to the
+  // constructor, above.
+  nassertv(has_output_filename());
+
+  nout << "Writing " << get_output_filename() << "\n";
+  BamFile bam_file;
+  if (!bam_file.open_write(get_output_filename())) {
+    nout << "Error in writing.\n";
+    exit(1);
+  }
+
+  if (!bam_file.write_object(root)) {
+    nout << "Error in writing.\n";
+    exit(1);
+  }
+}
+
+
+int main(int argc, char *argv[]) {
+  EggToBam prog;
+  prog.parse_command_line(argc, argv);
+  prog.run();
+  return 0;
+}

+ 24 - 0
pandatool/src/bam/eggToBam.h

@@ -0,0 +1,24 @@
+// Filename: eggToBam.h
+// Created by:  drose (28Jun00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef EGGTOBAM_H
+#define EGGTOBAM_H
+
+#include <pandatoolbase.h>
+
+#include <eggToSomething.h>
+
+////////////////////////////////////////////////////////////////////
+// 	 Class : EggToBam
+// Description : 
+////////////////////////////////////////////////////////////////////
+class EggToBam : public EggToSomething {
+public:
+  EggToBam();
+
+  void run();
+};
+
+#endif

+ 1 - 0
pandatool/src/configfiles/Sources.pp

@@ -0,0 +1 @@
+#define INSTALL_DATA pandatool.init

+ 3 - 0
pandatool/src/configfiles/pandatool.init

@@ -0,0 +1,3 @@
+ATTACH panda
+DOCSH if { \which gtkmm-config >& /dev/null } setenv HAVE_GTK yes
+DOSH if which gtkmm-config >/dev/null 2>&1; then HAVE_GTK="yes"; export HAVE_GTK; fi

+ 19 - 0
pandatool/src/eggbase/Sources.pp

@@ -0,0 +1,19 @@
+#begin lib_target
+  #define TARGET eggbase
+  #define LOCAL_LIBS \
+    progbase
+  #define OTHER_LIBS \
+    egg:c panda:m
+
+  #define SOURCES \
+    eggBase.cxx eggBase.h eggConverter.cxx eggConverter.h eggFilter.cxx \
+    eggFilter.h eggReader.cxx eggReader.h eggToSomething.cxx \
+    eggToSomething.h eggWriter.cxx eggWriter.h somethingToEgg.cxx \
+    somethingToEgg.h
+
+  #define INSTALL_HEADERS \
+    eggBase.h eggConverter.h eggFilter.h eggReader.h eggToSomething.h \
+    eggWriter.h somethingToEgg.h
+
+#end lib_target
+

+ 93 - 0
pandatool/src/eggbase/eggBase.cxx

@@ -0,0 +1,93 @@
+// Filename: eggBase.cxx
+// Created by:  drose (14Feb00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "eggBase.h"
+
+#include <eggComment.h>
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggBase::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+EggBase::
+EggBase() {
+  add_option
+    ("cs", "coordinate-system", 80, 
+     "Specify the coordinate system to operate in.  This may be one of "
+     "'y-up', 'z-up', 'y-up-left', or 'z-up-left'.",
+     &EggBase::dispatch_coordinate_system, 
+     &_got_coordinate_system, &_coordinate_system);
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggBase::post_command_line
+//       Access: Protected, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+bool EggBase::
+post_command_line() {
+  if (_got_coordinate_system) {
+    _data.set_coordinate_system(_coordinate_system);
+  }
+  
+  return ProgramBase::post_command_line();
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggBase::append_command_comment
+//       Access: Protected
+//  Description: Inserts a comment into the beginning of the indicated
+//               egg file corresponding to the command line that
+//               invoked this program.
+//
+//               Normally this function is called automatically when
+//               appropriate by EggWriter, and it's not necessary to
+//               call it explicitly.
+////////////////////////////////////////////////////////////////////
+void EggBase::
+append_command_comment(EggData &data) {
+  string comment;
+
+  comment = _program_name;
+  Args::const_iterator ai;
+  for (ai = _program_args.begin(); ai != _program_args.end(); ++ai) {
+    const string &arg = (*ai);
+
+    // First, check to see if the string is shell-acceptable.
+    bool legal = true;
+    string::const_iterator si;
+    for (si = arg.begin(); legal && si != arg.end(); ++si) {
+      switch (*si) {
+      case ' ':
+      case '\n':
+      case '\t':
+      case '*':
+      case '?':
+      case '\\':
+      case '(':
+      case ')':
+      case '|':
+      case '&':
+      case '<':
+      case '>':
+      case '"':
+      case ';':
+      case '$':
+	legal = false;
+      }
+    }
+
+    if (legal) {
+      comment += " " + arg;
+    } else {
+      comment += " '" + arg + "'";
+    }
+  }
+
+  data.insert(_data.begin(), new EggComment("", comment));
+}

+ 41 - 0
pandatool/src/eggbase/eggBase.h

@@ -0,0 +1,41 @@
+// Filename: eggBase.h
+// Created by:  drose (14Feb00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef EGGBASE_H
+#define EGGBASE_H
+
+#include <pandatoolbase.h>
+
+#include <programBase.h>
+#include <coordinateSystem.h>
+#include <eggData.h>
+
+////////////////////////////////////////////////////////////////////
+// 	 Class : EggBase
+// Description : This specialization of ProgramBase is intended for
+//               programs that read and/or write a single egg file.
+//               (See EggMultiBase for programs that operate on
+//               multiple egg files at once.)
+//
+//               This is just a base class; see EggReader, EggWriter,
+//               or EggFilter according to your particular I/O needs.
+////////////////////////////////////////////////////////////////////
+class EggBase : public ProgramBase {
+public:
+  EggBase();
+
+protected:
+  virtual bool post_command_line();
+  void append_command_comment(EggData &_data);
+
+protected:
+  bool _got_coordinate_system;
+  CoordinateSystem _coordinate_system;
+  EggData _data;
+};
+
+#endif
+
+

+ 28 - 0
pandatool/src/eggbase/eggConverter.cxx

@@ -0,0 +1,28 @@
+// Filename: eggConverter.cxx
+// Created by:  drose (15Feb00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "eggConverter.h"
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggConverter::Constructor
+//       Access: Public
+//  Description: The first parameter to the constructor should be the
+//               one-word name of the alien file format that is to be
+//               read or written, for instance "OpenFlight" or
+//               "Alias".  It's just used in printing error messages
+//               and such.  The second parameter is the preferred
+//               extension of files of this form, if any, with a
+//               leading dot.
+////////////////////////////////////////////////////////////////////
+EggConverter::
+EggConverter(const string &format_name, 
+	     const string &preferred_extension,
+	     bool allow_last_param,
+	     bool allow_stdout) : 
+  EggFilter(allow_last_param, allow_stdout),
+  _format_name(format_name),
+  _preferred_extension(preferred_extension)
+{
+}

+ 33 - 0
pandatool/src/eggbase/eggConverter.h

@@ -0,0 +1,33 @@
+// Filename: eggConverter.h
+// Created by:  drose (15Feb00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef EGGCONVERTER_H
+#define EGGCONVERTER_H
+
+#include <pandatoolbase.h>
+
+#include "eggFilter.h"
+
+////////////////////////////////////////////////////////////////////
+// 	 Class : EggConverter
+// Description : This is a general base class for programs that
+//               convert between egg files and some other format.  See
+//               EggToSomething and SomethingToEgg.
+////////////////////////////////////////////////////////////////////
+class EggConverter : public EggFilter {
+public:
+  EggConverter(const string &format_name, 
+	       const string &preferred_extension = string(),
+	       bool allow_last_param = true,
+	       bool allow_stdout = true);
+
+protected:
+  string _format_name;
+  string _preferred_extension;
+};
+
+#endif
+
+

+ 56 - 0
pandatool/src/eggbase/eggFilter.cxx

@@ -0,0 +1,56 @@
+// Filename: eggFilter.cxx
+// Created by:  drose (14Feb00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "eggFilter.h"
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggFilter::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+EggFilter::
+EggFilter(bool allow_last_param, bool allow_stdout) : 
+  EggWriter(allow_last_param, allow_stdout) 
+{
+  clear_runlines();
+  if (allow_last_param) {
+    add_runline("[opts] input.egg output.egg");
+  }
+  add_runline("[opts] -o output.egg input.egg");
+  if (allow_stdout) {
+    add_runline("[opts] input.egg >output.egg");
+  }
+
+  redescribe_option
+    ("cs",
+     "Specify the coordinate system of the resulting egg file.  This may be "
+     " one of 'y-up', 'z-up', 'y-up-left', or 'z-up-left'.  The default "
+     "is the same coordinate system as the input egg file.  If this is "
+     "different from the input egg file, a conversion will be performed.");
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggFilter::handle_args
+//       Access: Protected, Virtual
+//  Description: Does something with the additional arguments on the
+//               command line (after all the -options have been
+//               parsed).  Returns true if the arguments are good,
+//               false otherwise.
+////////////////////////////////////////////////////////////////////
+bool EggFilter::
+handle_args(ProgramBase::Args &args) {
+  if (_allow_last_param && !_got_output_filename && args.size() > 1) {
+    _got_output_filename = true;
+    _output_filename = args.back();
+    args.pop_back();
+
+    if (!verify_output_file_safe()) {
+      return false;
+    }
+  }
+
+  return EggReader::handle_args(args);
+}

+ 28 - 0
pandatool/src/eggbase/eggFilter.h

@@ -0,0 +1,28 @@
+// Filename: eggFilter.h
+// Created by:  drose (14Feb00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef EGGFILTER_H
+#define EGGFILTER_H
+
+#include <pandatoolbase.h>
+
+#include "eggReader.h"
+#include "eggWriter.h"
+
+////////////////////////////////////////////////////////////////////
+// 	 Class : EggFilter
+// Description : This is the base class for a program that reads an
+//               egg file, operates on it, and writes another egg file
+//               out.
+////////////////////////////////////////////////////////////////////
+class EggFilter : public EggReader, public EggWriter {
+public:
+  EggFilter(bool allow_last_param = false, bool allow_stdout = true);
+  virtual bool handle_args(Args &args);
+};
+
+#endif
+
+

+ 51 - 0
pandatool/src/eggbase/eggReader.cxx

@@ -0,0 +1,51 @@
+// Filename: eggReader.cxx
+// Created by:  drose (14Feb00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "eggReader.h"
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggReader::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+EggReader::
+EggReader() {
+  clear_runlines();
+  add_runline("[opts] input.egg");
+
+  redescribe_option
+    ("cs",
+     "Specify the coordinate system to operate in.  This may be "
+     " one of 'y-up', 'z-up', 'y-up-left', or 'z-up-left'.  The default "
+     "is the coordinate system of the input egg file.");
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggReader::handle_args
+//       Access: Protected, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+bool EggReader::
+handle_args(ProgramBase::Args &args) {
+  if (args.empty()) {
+    nout << "You must specify the egg file(s) to read on the command line.\n";
+    return false;
+  }
+
+  // Any separate egg files that are listed on the command line will
+  // get implicitly loaded up into one big egg file.
+
+  Args::const_iterator ai;
+  for (ai = args.begin(); ai != args.end(); ++ai) {
+    if (!_data.read(*ai)) {
+      // Rather than returning false, we simply exit here, so the
+      // ProgramBase won't try to tell the user how to run the program
+      // just because we got a bad egg file.
+      exit(1);
+    }
+  }
+
+  return true;
+}

+ 30 - 0
pandatool/src/eggbase/eggReader.h

@@ -0,0 +1,30 @@
+// Filename: eggReader.h
+// Created by:  drose (14Feb00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef EGGREADER_H
+#define EGGREADER_H
+
+#include <pandatoolbase.h>
+
+#include "eggBase.h"
+
+
+////////////////////////////////////////////////////////////////////
+// 	 Class : EggReader
+// Description : This is the base class for a program that reads egg
+//               files, but doesn't write an egg file.
+////////////////////////////////////////////////////////////////////
+class EggReader : virtual public EggBase {
+public:
+  EggReader();
+
+protected:
+  virtual bool handle_args(Args &args);
+
+};
+
+#endif
+
+

+ 104 - 0
pandatool/src/eggbase/eggToSomething.cxx

@@ -0,0 +1,104 @@
+// Filename: eggToSomething.cxx
+// Created by:  drose (15Feb00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "eggToSomething.h"
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggToSomething::Constructor
+//       Access: Public
+//  Description: The first parameter to the constructor should be the
+//               one-word name of the file format that is to be read,
+//               for instance "OpenFlight" or "Alias".  It's just used
+//               in printing error messages and such.
+////////////////////////////////////////////////////////////////////
+EggToSomething::
+EggToSomething(const string &format_name, 
+	       const string &preferred_extension,
+	       bool allow_last_param, bool allow_stdout) : 
+  EggConverter(format_name, preferred_extension, allow_last_param, 
+	       allow_stdout)
+{
+  clear_runlines();
+  if (_allow_last_param) {
+    add_runline("[opts] input.egg output" + _preferred_extension);
+  }
+  add_runline("[opts] -o output" + _preferred_extension + " input.egg");
+  if (_allow_stdout) {
+    add_runline("[opts] input.egg >output" + _preferred_extension);
+  }
+
+  string o_description;
+
+  if (_allow_stdout) {
+    if (_allow_last_param) {
+      o_description =
+	"Specify the filename to which the resulting " + format_name + 
+	" file will be written.  "
+	"If this option is omitted, the last parameter name is taken to be the "
+	"name of the output file, or standard output is used if there are no "
+	"other parameters.";
+    } else {
+      o_description =
+	"Specify the filename to which the resulting " + format_name +
+	" file will be written.  "
+	"If this option is omitted, the " + format_name + 
+	" file is written to standard output.";
+    }
+  } else {
+    if (_allow_last_param) {
+      o_description =
+	"Specify the filename to which the resulting " + format_name +
+	" file will be written.  "
+	"If this option is omitted, the last parameter name is taken to be the "
+	"name of the output file.";
+    } else {
+      o_description =
+	"Specify the filename to which the resulting " + format_name + 
+	" file will be written.";
+    }
+  }
+
+  redescribe_option("o", o_description);
+
+  redescribe_option
+    ("cs",
+     "Specify the coordinate system of the resulting " + _format_name +
+     " file.  This may be "
+     "one of 'y-up', 'z-up', 'y-up-left', or 'z-up-left'.  The default "
+     "is the same coordinate system as the input egg file.  If this is "
+     "different from the input egg file, a conversion will be performed.");
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggToSomething::handle_args
+//       Access: Protected, Virtual
+//  Description: Does something with the additional arguments on the
+//               command line (after all the -options have been
+//               parsed).  Returns true if the arguments are good,
+//               false otherwise.
+////////////////////////////////////////////////////////////////////
+bool EggToSomething::
+handle_args(ProgramBase::Args &args) {
+  if (_allow_last_param && !_got_output_filename && args.size() > 1) {
+    _got_output_filename = true;
+    _output_filename = args.back();
+    args.pop_back();
+
+    if (!_preferred_extension.empty() && 
+	("." + _output_filename.get_extension()) != _preferred_extension) {
+      nout << "Output filename " << _output_filename 
+	   << " does not end in " << _preferred_extension 
+	   << ".  If this is really what you intended, "
+	"use the -o output_file syntax.\n";
+      return false;
+    }
+
+    if (!verify_output_file_safe()) {
+      return false;
+    }
+  }
+
+  return EggConverter::handle_args(args);
+}

+ 32 - 0
pandatool/src/eggbase/eggToSomething.h

@@ -0,0 +1,32 @@
+// Filename: eggToSomething.h
+// Created by:  drose (15Feb00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef EGGTOSOMETHING_H
+#define EGGTOSOMETHING_H
+
+#include <pandatoolbase.h>
+
+#include "eggConverter.h"
+
+////////////////////////////////////////////////////////////////////
+// 	 Class : EggToSomething
+// Description : This is the general base class for a file-converter
+//               program that reads some model file format and
+//               generates an egg file.
+////////////////////////////////////////////////////////////////////
+class EggToSomething : public EggConverter {
+public:
+  EggToSomething(const string &format_name, 
+		 const string &preferred_extension = string(),
+		 bool allow_last_param = true,
+		 bool allow_stdout = true);
+
+protected:
+  virtual bool handle_args(Args &args);
+};
+
+#endif
+
+

+ 211 - 0
pandatool/src/eggbase/eggWriter.cxx

@@ -0,0 +1,211 @@
+// Filename: eggWriter.cxx
+// Created by:  drose (14Feb00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "eggWriter.h"
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggWriter::Constructor
+//       Access: Public
+//  Description: Egg-writing type programs may specify their output
+//               file using either the last-filename convention, the
+//               -o convention, and/or implicitly writing the result
+//               to standard output.  Not all interfaces are
+//               appropriate for all applications; some may be
+//               confusing or dangerous.
+//
+//               The calling application should pass allow_last_param
+//               true to allow the user to specify the output filename
+//               as the last parameter on the command line (the most
+//               dangerous, but convenient, method), and allow_stdout
+//               true to allow the user to omit the output filename
+//               altogether and have the output implicitly go to
+//               standard output (not terribly dangerous, but
+//               inappropriate when writing binary file formats).
+////////////////////////////////////////////////////////////////////
+EggWriter::
+EggWriter(bool allow_last_param, bool allow_stdout) {
+  _allow_last_param = allow_last_param;
+  _allow_stdout = allow_stdout;
+
+  clear_runlines();
+  if (_allow_last_param) {
+    add_runline("[opts] output.egg");
+  }
+  add_runline("[opts] -o output.egg");
+  if (_allow_stdout) {
+    add_runline("[opts] >output.egg");
+  }
+
+  string o_description;
+
+  if (_allow_stdout) {
+    if (_allow_last_param) {
+      o_description =
+	"Specify the filename to which the resulting egg file will be written.  "
+	"If this option is omitted, the last parameter name is taken to be the "
+	"name of the output file, or standard output is used if there are no "
+	"other parameters.";
+    } else {
+      o_description =
+	"Specify the filename to which the resulting egg file will be written.  "
+	"If this option is omitted, the egg file is written to standard output.";
+    }
+  } else {
+    if (_allow_last_param) {
+      o_description =
+	"Specify the filename to which the resulting egg file will be written.  "
+	"If this option is omitted, the last parameter name is taken to be the "
+	"name of the output file.";
+    } else {
+      o_description =
+	"Specify the filename to which the resulting egg file will be written.";
+    }
+  }
+
+  add_option
+    ("o", "filename", 50, o_description,
+     &EggWriter::dispatch_filename, &_got_output_filename, &_output_filename);
+
+  redescribe_option
+    ("cs",
+     "Specify the coordinate system of the resulting egg file.  This may be "
+     " one of 'y-up', 'z-up', 'y-up-left', or 'z-up-left'.  The default is "
+     "y-up.");
+
+  _output_ptr = (ostream *)NULL;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggWriter::get_output
+//       Access: Public
+//  Description: Returns an output stream that corresponds to the
+//               user's intended egg file output--either stdout, or
+//               the named output file.
+////////////////////////////////////////////////////////////////////
+ostream &EggWriter::
+get_output() {
+  if (_output_ptr == (ostream *)NULL) {
+    if (!_got_output_filename) {
+      // No filename given; use standard output.
+      assert(_allow_stdout);
+      _output_ptr = &cout;
+      
+    } else {
+      // Attempt to open the named file.
+      unlink(_output_filename.c_str());
+      _output_stream.open(_output_filename.c_str(), ios::out, 0666);
+      if (!_output_stream) {
+	nout << "Unable to write to " << _output_filename << "\n";
+	exit(1);
+      }
+      nout << "Writing " << _output_filename << "\n";
+      _output_ptr = &_output_stream;
+    }
+  }
+  return *_output_ptr;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggWriter::has_output_filename
+//       Access: Public
+//  Description: Returns true if the user specified an output
+//               filename, false otherwise (e.g. the output file is
+//               implicitly stdout).
+////////////////////////////////////////////////////////////////////
+bool EggWriter::
+has_output_filename() const {
+  return _got_output_filename;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggWriter::get_output_filename
+//       Access: Public
+//  Description: If has_output_filename() returns true, this is the
+//               filename that the user specified.  Otherwise, it
+//               returns the empty string.
+////////////////////////////////////////////////////////////////////
+Filename EggWriter::
+get_output_filename() const {
+  if (_got_output_filename) {
+    return _output_filename;
+  }
+  return Filename();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggWriter::handle_args
+//       Access: Protected, Virtual
+//  Description: Does something with the additional arguments on the
+//               command line (after all the -options have been
+//               parsed).  Returns true if the arguments are good,
+//               false otherwise.
+////////////////////////////////////////////////////////////////////
+bool EggWriter::
+handle_args(ProgramBase::Args &args) {
+  if (_allow_last_param && !_got_output_filename && !args.empty()) {
+    _got_output_filename = true;
+    _output_filename = args.back();
+    args.pop_back();
+
+    if (!verify_output_file_safe()) {
+      return false;
+    }
+  }
+
+  if (!args.empty()) {
+    nout << "Unexpected arguments on command line:\n";
+    copy(args.begin(), args.end(), ostream_iterator<string>(nout, " "));
+    nout << "\r";
+    return false;
+  }
+
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggWriter::post_command_line
+//       Access: Protected, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+bool EggWriter::
+post_command_line() {
+  if (!_allow_stdout && !_got_output_filename) {
+    nout << "You must specify the filename to write with -o.\n";
+    return false;
+  }
+
+  append_command_comment(_data);
+  
+  return EggBase::post_command_line();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggWriter::verify_output_file_safe
+//       Access: Protected
+//  Description: This is called when the output file is given as the
+//               last parameter on the command line.  Since this is a
+//               fairly dangerous way to specify the output file (it's
+//               easy to accidentally overwrite an input file this
+//               way), the convention is to disallow this syntax if
+//               the output file already exists.
+//
+//               This function will test if the output file exists,
+//               and issue a warning message if it does, returning
+//               false.  If all is well, it will return true.
+////////////////////////////////////////////////////////////////////
+bool EggWriter::
+verify_output_file_safe() const {
+  nassertr(_got_output_filename, false);
+
+  if (_output_filename.exists()) {
+    nout << "The output filename " << _output_filename << " already exists.  "
+      "If you wish to overwrite it, you must use the -o option to specify "
+      "the output filename, instead of simply specifying it as the last "
+      "parameter.\n";
+    return false;
+  }
+  return true;
+}

+ 49 - 0
pandatool/src/eggbase/eggWriter.h

@@ -0,0 +1,49 @@
+// Filename: eggWriter.h
+// Created by:  drose (14Feb00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef EGGWRITER_H
+#define EGGWRITER_H
+
+#include <pandatoolbase.h>
+
+#include "eggBase.h"
+
+#include <filename.h>
+
+#include <fstream.h>
+
+////////////////////////////////////////////////////////////////////
+// 	 Class : EggWriter
+// Description : This is the base class for a program that generates
+//               an egg file output, but doesn't read any for input.
+////////////////////////////////////////////////////////////////////
+class EggWriter : virtual public EggBase {
+public:
+  EggWriter(bool allow_last_param = false, bool allow_stdout = true);
+
+  ostream &get_output();
+  bool has_output_filename() const;
+  Filename get_output_filename() const;
+
+protected:
+  virtual bool handle_args(Args &args);
+  virtual bool post_command_line();
+
+  bool verify_output_file_safe() const;
+
+protected:
+  bool _allow_last_param;
+  bool _allow_stdout;
+  bool _got_output_filename;
+  Filename _output_filename;
+
+private:
+  ofstream _output_stream;
+  ostream *_output_ptr;
+};
+
+#endif
+
+

+ 86 - 0
pandatool/src/eggbase/somethingToEgg.cxx

@@ -0,0 +1,86 @@
+// Filename: somethingToEgg.cxx
+// Created by:  drose (15Feb00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "somethingToEgg.h"
+
+////////////////////////////////////////////////////////////////////
+//     Function: SomethingToEgg::Constructor
+//       Access: Public
+//  Description: The first parameter to the constructor should be the
+//               one-word name of the file format that is to be read,
+//               for instance "OpenFlight" or "Alias".  It's just used
+//               in printing error messages and such.
+////////////////////////////////////////////////////////////////////
+SomethingToEgg::
+SomethingToEgg(const string &format_name, 
+	       const string &preferred_extension,
+	       bool allow_last_param, bool allow_stdout) : 
+  EggConverter(format_name, preferred_extension, allow_last_param, allow_stdout)
+{
+  clear_runlines();
+  if (_allow_last_param) {
+    add_runline("[opts] input" + _preferred_extension + " output.egg");
+  }
+  add_runline("[opts] -o output.egg input" + _preferred_extension);
+  if (_allow_stdout) {
+    add_runline("[opts] input" + _preferred_extension + " >output.egg");
+  }
+
+  redescribe_option
+    ("cs",
+     "Specify the coordinate system of the resulting egg file.  This may be "
+     " one of 'y-up', 'z-up', 'y-up-left', or 'z-up-left'.  The default "
+     "is the same coordinate system as the input " + _format_name +
+     " file, if this can be determined.");
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SomethingToEgg::handle_args
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+bool SomethingToEgg::
+handle_args(Args &args) {
+  if (_allow_last_param && !_got_output_filename && args.size() > 1) {
+    _got_output_filename = true;
+    _output_filename = args.back();
+    args.pop_back();
+
+    if (!(_output_filename.get_extension() == "egg")) {
+      nout << "Output filename " << _output_filename 
+	   << " does not end in .egg.  If this is really what you intended, "
+	"use the -o output_file syntax.\n";
+      return false;
+    }
+
+    if (!verify_output_file_safe()) {
+      return false;
+    }
+  }
+
+  if (args.empty()) {
+    nout << "You must specify the " << _format_name 
+	  << " file to read on the command line.\n";
+    return false;
+  }
+
+  if (args.size() != 1) {
+    nout << "You may only specify one " << _format_name 
+	 << " file to read on the command line.  "
+	 << "You specified: ";
+    copy(args.begin(), args.end(), ostream_iterator<string>(nout, " "));
+    nout << "\n";
+    return false;
+  }
+
+  _input_filename = args[0];
+
+  if (access(_input_filename.c_str(), R_OK) != 0) {
+    nout << "Cannot find input file " << _input_filename << "\n";
+    return false;
+  }
+
+  return true;
+}

+ 34 - 0
pandatool/src/eggbase/somethingToEgg.h

@@ -0,0 +1,34 @@
+// Filename: somethingToEgg.h
+// Created by:  drose (15Feb00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef SOMETHINGTOEGG_H
+#define SOMETHINGTOEGG_H
+
+#include <pandatoolbase.h>
+
+#include "eggConverter.h"
+
+////////////////////////////////////////////////////////////////////
+// 	 Class : SomethingToEgg
+// Description : This is the general base class for a file-converter
+//               program that reads some model file format and
+//               generates an egg file.
+////////////////////////////////////////////////////////////////////
+class SomethingToEgg : public EggConverter {
+public:
+  SomethingToEgg(const string &format_name, 
+		 const string &preferred_extension = string(),
+		 bool allow_last_param = true,
+		 bool allow_stdout = true);
+
+protected:
+  virtual bool handle_args(Args &args);
+
+  Filename _input_filename;
+};
+
+#endif
+
+

+ 16 - 0
pandatool/src/eggprogs/Sources.pp

@@ -0,0 +1,16 @@
+#begin bin_target
+  #define TARGET egg-trans
+  #define LOCAL_LIBS \
+    eggbase progbase config compiler
+  #define OTHER_LIBS \
+    egg:c linmath:c putil:c express:c panda:m
+  #define UNIX_SYS_LIBS \
+    m
+
+  #define SOURCES \
+    eggTrans.cxx eggTrans.h
+
+  #define INSTALL_HEADERS \
+
+#end bin_target
+

+ 37 - 0
pandatool/src/eggprogs/eggTrans.cxx

@@ -0,0 +1,37 @@
+// Filename: eggTrans.cxx
+// Created by:  drose (14Feb00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "eggTrans.h"
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggTrans::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+EggTrans::
+EggTrans() {
+  set_program_description
+    ("This program reads an egg file and writes an essentially equivalent "
+     "egg file to the standard output, or to the file specified with -o.  "
+     "Some simple operations on the egg file are supported."); 
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggTrans::run
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void EggTrans::
+run() {
+  _data.write_egg(get_output());
+}
+
+
+int main(int argc, char *argv[]) {
+  EggTrans prog;
+  prog.parse_command_line(argc, argv);
+  prog.run();
+  return 0;
+}

+ 27 - 0
pandatool/src/eggprogs/eggTrans.h

@@ -0,0 +1,27 @@
+// Filename: eggTrans.h
+// Created by:  drose (14Feb00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef EGGTRANS_H
+#define EGGTRANS_H
+
+#include <pandatoolbase.h>
+
+#include <eggFilter.h>
+
+////////////////////////////////////////////////////////////////////
+// 	 Class : EggTrans
+// Description : A program to read an egg file and write an equivalent
+//               egg file, possibly performing some minor operations
+//               along the way.
+////////////////////////////////////////////////////////////////////
+class EggTrans : public EggFilter {
+public:
+  EggTrans();
+
+  void run();
+};
+
+#endif
+

+ 60 - 0
pandatool/src/flt/Sources.pp

@@ -0,0 +1,60 @@
+#begin lib_target
+  #define TARGET flt
+  #define LOCAL_LIBS \
+    compiler
+  #define OTHER_LIBS \
+    mathutil:c linmath:c putil:c express:c panda:m dtool:m
+  #define UNIX_SYS_LIBS \
+    m
+
+  #define SOURCES \
+    config_flt.cxx config_flt.h fltBead.cxx fltBead.h fltBeadID.cxx \
+    fltBeadID.h fltError.cxx fltError.h fltExternalReference.cxx \
+    fltExternalReference.h fltEyepoint.cxx fltEyepoint.h fltFace.I \
+    fltFace.cxx fltFace.h fltGroup.cxx fltGroup.h fltHeader.cxx \
+    fltHeader.h fltInstanceDefinition.cxx fltInstanceDefinition.h \
+    fltInstanceRef.cxx fltInstanceRef.h fltLOD.cxx fltLOD.h \
+    fltLightSourceDefinition.cxx fltLightSourceDefinition.h \
+    fltMaterial.cxx fltMaterial.h fltObject.cxx fltObject.h \
+    fltOpcode.cxx fltOpcode.h fltPackedColor.I fltPackedColor.cxx \
+    fltPackedColor.h fltRecord.I fltRecord.cxx fltRecord.h \
+    fltRecordReader.cxx fltRecordReader.h fltRecordWriter.cxx \
+    fltRecordWriter.h fltTexture.cxx fltTexture.h fltTrackplane.cxx \
+    fltTrackplane.h fltTransformGeneralMatrix.cxx \
+    fltTransformGeneralMatrix.h fltTransformPut.cxx fltTransformPut.h \
+    fltTransformRecord.cxx fltTransformRecord.h \
+    fltTransformRotateAboutEdge.cxx fltTransformRotateAboutEdge.h \
+    fltTransformRotateAboutPoint.cxx fltTransformRotateAboutPoint.h \
+    fltTransformRotateScale.cxx fltTransformRotateScale.h \
+    fltTransformScale.cxx fltTransformScale.h fltTransformTranslate.cxx \
+    fltTransformTranslate.h fltUnsupportedRecord.cxx \
+    fltUnsupportedRecord.h fltVertex.I fltVertex.cxx fltVertex.h \
+    fltVertexList.cxx fltVertexList.h
+
+  #define INSTALL_HEADERS \
+    fltBead.h fltBeadID.h fltError.h fltExternalReference.h \
+    fltEyepoint.h fltFace.I fltFace.h fltGroup.h fltHeader.h \
+    fltInstanceDefinition.h fltInstanceRef.h fltLOD.h \
+    fltLightSourceDefinition.h fltMaterial.h fltObject.h fltOpcode.h \
+    fltPackedColor.I fltPackedColor.h fltRecord.I fltRecord.h \
+    fltRecordReader.h fltRecordWriter.h fltTexture.h fltTrackplane.h \
+    fltTransformGeneralMatrix.h fltTransformPut.h fltTransformRecord.h \
+    fltTransformRotateAboutEdge.h fltTransformRotateAboutPoint.h \
+    fltTransformRotateScale.h fltTransformScale.h \
+    fltTransformTranslate.h fltUnsupportedRecord.h fltVertex.I \
+    fltVertex.h fltVertexList.h
+
+#end lib_target
+
+#begin test_bin_target
+  #define TARGET test_flt
+  #define LOCAL_LIBS \
+    flt
+  #define OTHER_LIBS \
+    pystub
+
+  #define SOURCES \
+    test_flt.cxx
+
+#end test_bin_target
+

+ 63 - 0
pandatool/src/flt/config_flt.cxx

@@ -0,0 +1,63 @@
+// Filename: config_flt.cxx
+// Created by:  drose (24Aug00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "config_flt.h"
+#include "fltRecord.h"
+#include "fltBead.h"
+#include "fltBeadID.h"
+#include "fltGroup.h"
+#include "fltObject.h"
+#include "fltFace.h"
+#include "fltVertexList.h"
+#include "fltLOD.h"
+#include "fltInstanceDefinition.h"
+#include "fltInstanceRef.h"
+#include "fltHeader.h"
+#include "fltVertex.h"
+#include "fltMaterial.h"
+#include "fltTexture.h"
+#include "fltLightSourceDefinition.h"
+#include "fltUnsupportedRecord.h"
+#include "fltTransformRecord.h"
+#include "fltTransformGeneralMatrix.h"
+#include "fltTransformPut.h"
+#include "fltTransformRotateAboutEdge.h"
+#include "fltTransformRotateAboutPoint.h"
+#include "fltTransformScale.h"
+#include "fltTransformTranslate.h"
+#include "fltTransformRotateScale.h"
+#include "fltExternalReference.h"
+
+#include <dconfig.h>
+
+Configure(config_flt);
+
+ConfigureFn(config_flt) {
+  FltRecord::init_type();
+  FltBead::init_type();
+  FltBeadID::init_type();
+  FltGroup::init_type();
+  FltObject::init_type();
+  FltFace::init_type();
+  FltVertexList::init_type();
+  FltLOD::init_type();
+  FltInstanceDefinition::init_type();
+  FltInstanceRef::init_type();
+  FltHeader::init_type();
+  FltVertex::init_type();
+  FltMaterial::init_type();
+  FltTexture::init_type();
+  FltLightSourceDefinition::init_type();
+  FltUnsupportedRecord::init_type();
+  FltTransformRecord::init_type();
+  FltTransformGeneralMatrix::init_type();
+  FltTransformPut::init_type();
+  FltTransformRotateAboutEdge::init_type();
+  FltTransformRotateAboutPoint::init_type();
+  FltTransformScale::init_type();
+  FltTransformTranslate::init_type();
+  FltTransformRotateScale::init_type();
+  FltExternalReference::init_type();
+}

+ 11 - 0
pandatool/src/flt/config_flt.h

@@ -0,0 +1,11 @@
+// Filename: config_flt.h
+// Created by:  drose (24Aug00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef CONFIG_FLT_H
+#define CONFIG_FLT_H
+
+// No configure variables.
+
+#endif

+ 391 - 0
pandatool/src/flt/fltBead.cxx

@@ -0,0 +1,391 @@
+// Filename: fltBead.cxx
+// Created by:  drose (24Aug00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "fltBead.h"
+#include "fltRecordReader.h"
+#include "fltRecordWriter.h"
+#include "fltTransformGeneralMatrix.h"
+#include "fltTransformPut.h"
+#include "fltTransformRotateAboutEdge.h"
+#include "fltTransformRotateAboutPoint.h"
+#include "fltTransformScale.h"
+#include "fltTransformTranslate.h"
+#include "fltTransformRotateScale.h"
+
+TypeHandle FltBead::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltBead::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+FltBead::
+FltBead(FltHeader *header) : FltRecord(header) {
+  _has_transform = false;
+  _transform = LMatrix4d::ident_mat();
+  _replicate_count = 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltBead::has_transform
+//       Access: Public
+//  Description: Returns true if the bead has been transformed, false
+//               otherwise.  If this returns true, get_transform()
+//               will return the single-precision net transformation,
+//               and get_num_transform_steps() will return nonzero.
+////////////////////////////////////////////////////////////////////
+bool FltBead::
+has_transform() const {
+  return _has_transform;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltBead::get_transform
+//       Access: Public
+//  Description: Returns the single-precision 4x4 matrix that
+//               represents the transform applied to this bead, or the
+//               identity matrix if the bead has not been transformed.
+////////////////////////////////////////////////////////////////////
+const LMatrix4d &FltBead::
+get_transform() const {
+  return _has_transform ? _transform : LMatrix4d::ident_mat();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltBead::set_transform
+//       Access: Public
+//  Description: Replaces the transform matrix on this bead.  This
+//               implicitly removes all of the transform steps added
+//               previously, and replaces them with a single 4x4
+//               general matrix transform step.
+////////////////////////////////////////////////////////////////////
+void FltBead::
+set_transform(const LMatrix4d &mat) {
+  clear_transform();
+  FltTransformGeneralMatrix *step = new FltTransformGeneralMatrix(_header);
+  step->set_matrix(mat);
+  add_transform_step(step);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltBead::clear_transform
+//       Access: Public
+//  Description: Removes any transform matrix and all transform steps
+//               on this bead.
+////////////////////////////////////////////////////////////////////
+void FltBead::
+clear_transform() {
+  _has_transform = false;
+  _transform = LMatrix4d::ident_mat();
+  _transform_steps.clear();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltBead::get_num_transform_steps
+//       Access: Public
+//  Description: Returns the number of individual steps that define
+//               the net transform on this bead as returned by
+//               set_transform().  Each step is a single
+//               transformation; the concatenation of all
+//               transformations will produce the matrix represented
+//               by set_transform().
+////////////////////////////////////////////////////////////////////
+int FltBead::
+get_num_transform_steps() const {
+  return _transform_steps.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltBead::get_transform_step
+//       Access: Public
+//  Description: Returns the nth individual step that defines
+//               the net transform on this bead.  See
+//               get_num_transform_steps().
+////////////////////////////////////////////////////////////////////
+FltTransformRecord *FltBead::
+get_transform_step(int n) {
+  nassertr(n >= 0 && n < (int)_transform_steps.size(), 
+	   (FltTransformRecord *)NULL);
+  return _transform_steps[n];
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltBead::add_transform_step
+//       Access: Public
+//  Description: Applies the indicated transform step to the net
+//               transformation applied to the bead.
+////////////////////////////////////////////////////////////////////
+void FltBead::
+add_transform_step(FltTransformRecord *record) {
+  if (!_has_transform) {
+    _has_transform = true;
+    _transform = record->get_matrix();
+  } else {
+    _transform = record->get_matrix() * _transform;
+  }
+  _transform_steps.push_back(record);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltBead::get_replicate_count
+//       Access: Public
+//  Description: Returns the replicate count of this bead.  If this is
+//               nonzero, it means that the bead is implicitly copied
+//               this number of additional times (for replicate_count
+//               + 1 total copies), applying the transform on this
+//               bead for each copy.  In this case, the transform does
+//               *not* apply to the initial copy of the bead.
+////////////////////////////////////////////////////////////////////
+int FltBead::
+get_replicate_count() const {
+  return _replicate_count;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltBead::set_replicate_count
+//       Access: Public
+//  Description: Changes the replicate count of this bead.  If you are
+//               setting the replicate count to some nonzero number,
+//               you must also set a transform on the bead.  See
+//               set_replicate_count().
+////////////////////////////////////////////////////////////////////
+void FltBead::
+set_replicate_count(int count) {
+  _replicate_count = count;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltBead::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 FltBead::
+extract_record(FltRecordReader &reader) {
+  if (!FltRecord::extract_record(reader)) {
+    return false;
+  }
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltBead::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 FltBead::
+extract_ancillary(FltRecordReader &reader) {
+  FltTransformRecord *step = (FltTransformRecord *)NULL;
+
+  switch (reader.get_opcode()) {
+  case FO_transform_matrix:
+    return extract_transform_matrix(reader);
+
+  case FO_general_matrix:
+    step = new FltTransformGeneralMatrix(_header);
+    break;
+
+  case FO_put:
+    step = new FltTransformPut(_header);
+    break;
+
+  case FO_rotate_about_edge:
+    step = new FltTransformRotateAboutEdge(_header);
+    break;
+
+  case FO_rotate_about_point:
+    step = new FltTransformRotateAboutPoint(_header);
+    break;
+
+  case FO_scale:
+    step = new FltTransformScale(_header);
+    break;
+
+  case FO_translate:
+    step = new FltTransformTranslate(_header);
+    break;
+
+  case FO_rotate_and_scale:
+    step = new FltTransformRotateScale(_header);
+    break;
+
+  case FO_replicate:
+    return extract_replicate_count(reader);
+
+  default:
+    return FltRecord::extract_ancillary(reader);
+  }
+
+  // A transform step.
+  nassertr(step != (FltTransformRecord *)NULL, false);
+  step->extract_record(reader);
+  _transform_steps.push_back(DCAST(FltTransformRecord, step));
+
+  /*
+  cerr << "Added transform step: " << step->get_type() << "\n";
+  cerr << "Net matrix is:\n";
+  _transform.write(cerr, 2);
+  cerr << "Step is:\n";
+  step->get_matrix().write(cerr, 2);
+  */
+
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltBead::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 FltBead::
+build_record(FltRecordWriter &writer) const {
+  if (!FltRecord::build_record(writer)) {
+    return false;
+  }
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltBead::write_ancillary
+//       Access: Protected, Virtual
+//  Description: Writes whatever ancillary records are required for
+//               this record.  Returns FE_ok on success, or something
+//               else if there is some error.
+////////////////////////////////////////////////////////////////////
+FltError FltBead::
+write_ancillary(FltRecordWriter &writer) const {
+  if (_has_transform) {
+    FltError result = write_transform(writer);
+    if (result != FE_ok) {
+      return result;
+    }
+  }
+  if (_replicate_count != 0) {
+    FltError result = write_replicate_count(writer);
+    if (result != FE_ok) {
+      return result;
+    }
+  }
+    
+
+  return FltRecord::write_ancillary(writer);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltBead::extract_transform_matrix
+//       Access: Private
+//  Description: Reads a transform matrix ancillary bead.  This
+//               defines the net transformation that has been applied
+//               to the bead, and precedes the set of individual
+//               transform steps that define how this net transform
+//               was computed.
+////////////////////////////////////////////////////////////////////
+bool FltBead::
+extract_transform_matrix(FltRecordReader &reader) {
+  nassertr(reader.get_opcode() == FO_transform_matrix, false);
+  DatagramIterator &iterator = reader.get_iterator();
+
+  LMatrix4d matrix;
+  for (int r = 0; r < 4; r++) {
+    for (int c = 0; c < 4; c++) {
+      matrix(r, c) = iterator.get_be_float32();
+    }
+  }
+  nassertr(iterator.get_remaining_size() == 0, true);
+
+  _transform_steps.clear();
+  _has_transform = true;
+  _transform = matrix;
+
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltBead::extract_replicate_count
+//       Access: Private
+//  Description: Reads a replicate count ancillary bead.
+////////////////////////////////////////////////////////////////////
+bool FltBead::
+extract_replicate_count(FltRecordReader &reader) {
+  nassertr(reader.get_opcode() == FO_replicate, false);
+  DatagramIterator &iterator = reader.get_iterator();
+
+  _replicate_count = iterator.get_be_int16();
+  iterator.skip_bytes(2);
+
+  nassertr(iterator.get_remaining_size() == 0, true);
+
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltBead::write_transform
+//       Access: Private
+//  Description: Writes out the transformation and all of its defining
+//               steps.
+////////////////////////////////////////////////////////////////////
+FltError FltBead::
+write_transform(FltRecordWriter &writer) const {
+  // First, write out the initial transform indication.
+  writer.set_opcode(FO_transform_matrix);
+  Datagram &datagram = writer.update_datagram();
+
+  for (int r = 0; r < 4; r++) {
+    for (int c = 0; c < 4; c++) {
+      datagram.add_be_float32(_transform(r, c));
+    }
+  }
+  
+  FltError result = writer.advance();
+  if (result != FE_ok) {
+    return result;
+  }
+
+  // Now, write out each of the steps of the transform.
+  Transforms::const_iterator ti;
+  for (ti = _transform_steps.begin(); ti != _transform_steps.end(); ++ti) {
+    if (!(*ti)->build_record(writer)) {
+      return FE_invalid_record;
+    }
+    FltError result = writer.advance();
+    if (result != FE_ok) {
+      return result;
+    }
+  }
+  
+  return FE_ok;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltBead::write_replicate_count
+//       Access: Private
+//  Description: Writes out the replicate count, if needed.
+////////////////////////////////////////////////////////////////////
+FltError FltBead::
+write_replicate_count(FltRecordWriter &writer) const {
+  if (_replicate_count != 0) {
+    writer.set_opcode(FO_replicate);
+    Datagram &datagram = writer.update_datagram();
+
+    datagram.add_be_int16(_replicate_count);
+    datagram.pad_bytes(2);
+  
+    FltError result = writer.advance();
+    if (result != FE_ok) {
+      return result;
+    }
+  }
+
+  return FE_ok;
+}

+ 83 - 0
pandatool/src/flt/fltBead.h

@@ -0,0 +1,83 @@
+// Filename: fltBead.h
+// Created by:  drose (24Aug00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef FLTBEAD_H
+#define FLTBEAD_H
+
+#include <pandatoolbase.h>
+
+#include "fltRecord.h"
+#include "fltTransformRecord.h"
+
+#include <luse.h>
+
+////////////////////////////////////////////////////////////////////
+// 	 Class : FltBead
+// Description : A base class for any of a broad family of flt records
+//               that represent particular beads in the hierarchy.
+//               These are things like group beads and object beads,
+//               as opposed to things like push and pop or comment
+//               records.
+////////////////////////////////////////////////////////////////////
+class FltBead : public FltRecord {
+public:
+  FltBead(FltHeader *header);
+
+  bool has_transform() const;
+  const LMatrix4d &get_transform() const;
+  void set_transform(const LMatrix4d &mat);
+  void clear_transform();
+
+  int get_num_transform_steps() const;
+  FltTransformRecord *get_transform_step(int n);
+  void add_transform_step(FltTransformRecord *record);
+
+  int get_replicate_count() const;
+  void set_replicate_count(int count);
+
+protected:
+  virtual bool extract_record(FltRecordReader &reader);
+  virtual bool extract_ancillary(FltRecordReader &reader);
+
+  virtual bool build_record(FltRecordWriter &writer) const;
+  virtual FltError write_ancillary(FltRecordWriter &writer) const;
+
+private:
+  bool extract_transform_matrix(FltRecordReader &reader);
+  bool extract_replicate_count(FltRecordReader &reader);
+
+  FltError write_transform(FltRecordWriter &writer) const;
+  FltError write_replicate_count(FltRecordWriter &writer) const;
+
+private:
+  bool _has_transform;
+  LMatrix4d _transform;
+
+  typedef vector<PT(FltTransformRecord)> Transforms;
+  Transforms _transform_steps;
+
+  int _replicate_count;
+
+public:
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    FltRecord::init_type();
+    register_type(_type_handle, "FltBead",
+		  FltRecord::get_class_type());
+  }
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#endif
+
+

+ 110 - 0
pandatool/src/flt/fltBeadID.cxx

@@ -0,0 +1,110 @@
+// Filename: fltBeadID.cxx
+// Created by:  drose (24Aug00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "fltBeadID.h"
+#include "fltRecordReader.h"
+#include "fltRecordWriter.h"
+
+TypeHandle FltBeadID::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltBeadID::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+FltBeadID::
+FltBeadID(FltHeader *header) : FltBead(header) {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltBeadID::output
+//       Access: Public
+//  Description: Writes a quick one-line description of the record, but
+//               not its children.  This is a human-readable
+//               description, primarily for debugging; to write a flt
+//               file, use FltHeader::write_flt().
+////////////////////////////////////////////////////////////////////
+void FltBeadID::
+output(ostream &out) const {
+  out << get_type();
+  if (!_id.empty()) {
+    out << " " << _id;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltBeadID::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 FltBeadID::
+extract_record(FltRecordReader &reader) {
+  if (!FltBead::extract_record(reader)) {
+    return false;
+  }
+
+  _id = reader.get_iterator().get_fixed_string(8);
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltBeadID::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 FltBeadID::
+extract_ancillary(FltRecordReader &reader) {
+  if (reader.get_opcode() == FO_long_id) {
+    _id = reader.get_iterator().get_remaining_bytes();
+    return true;
+  }
+
+  return FltBead::extract_ancillary(reader);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltBeadID::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 FltBeadID::
+build_record(FltRecordWriter &writer) const {
+  if (!FltBead::build_record(writer)) {
+    return false;
+  }
+
+  writer.update_datagram().add_fixed_string(_id.substr(0, 7), 8);
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltBeadID::write_ancillary
+//       Access: Protected, Virtual
+//  Description: Writes whatever ancillary records are required for
+//               this record.  Returns FE_ok on success, or something
+//               else if there is some error.
+////////////////////////////////////////////////////////////////////
+FltError FltBeadID::
+write_ancillary(FltRecordWriter &writer) const {
+  if (_id.length() > 7) {
+    Datagram dc(_id);
+    FltError result = writer.write_record(FO_long_id, dc);
+    if (result != FE_ok) {
+      return result;
+    }
+  }
+
+  return FltBead::write_ancillary(writer);
+}

+ 57 - 0
pandatool/src/flt/fltBeadID.h

@@ -0,0 +1,57 @@
+// Filename: fltBeadID.h
+// Created by:  drose (24Aug00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef FLTBEADID_H
+#define FLTBEADID_H
+
+#include <pandatoolbase.h>
+
+#include "fltBead.h"
+
+////////////////////////////////////////////////////////////////////
+// 	 Class : FltBeadID
+// Description : A base class for any of a broad family of flt beads
+//               that include an ID.
+////////////////////////////////////////////////////////////////////
+class FltBeadID : public FltBead {
+public:
+  FltBeadID(FltHeader *header);
+
+  const string &get_id() const;
+  void set_id(const string &id);
+
+  virtual void output(ostream &out) const;
+
+protected:
+  virtual bool extract_record(FltRecordReader &reader);
+  virtual bool extract_ancillary(FltRecordReader &reader);
+
+  virtual bool build_record(FltRecordWriter &writer) const;
+  virtual FltError write_ancillary(FltRecordWriter &writer) const;
+
+private:
+  string _id;
+
+public:
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    FltBead::init_type();
+    register_type(_type_handle, "FltBeadID",
+		  FltBead::get_class_type());
+  }
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#endif
+
+

+ 47 - 0
pandatool/src/flt/fltError.cxx

@@ -0,0 +1,47 @@
+// Filename: fltError.cxx
+// Created by:  drose (24Aug00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "fltError.h"
+
+ostream &
+operator << (ostream &out, FltError error) {
+  switch (error) {
+  case FE_ok:
+    return out << "no error";
+
+  case FE_could_not_open:
+    return out << "could not open file";
+
+  case FE_empty_file:
+    return out << "empty file";
+
+  case FE_end_of_file:
+    return out << "unexpected end of file";
+
+  case FE_read_error:
+    return out << "read error on file";
+
+  case FE_invalid_record:
+    return out << "invalid record";
+
+  case FE_extra_data:
+    return out << "extra data at end of file";
+    
+  case FE_write_error:
+    return out << "write error on file";
+    
+  case FE_bad_data:
+    return out << "bad data";
+    
+  case FE_not_implemented:
+    return out << "not implemented";
+    
+  case FE_internal:
+    return out << "internal error";
+
+  default:
+    return out << "unknown error " << (int)error;
+  }
+}

+ 32 - 0
pandatool/src/flt/fltError.h

@@ -0,0 +1,32 @@
+// Filename: fltError.h
+// Created by:  drose (24Aug00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef FLTERROR_H
+#define FLTERROR_H
+
+#include <pandatoolbase.h>
+
+// Return values for various functions in the flt library.
+enum FltError {
+  FE_ok = 0,
+  FE_could_not_open,
+  FE_empty_file,
+  FE_end_of_file,
+  FE_read_error,
+  FE_invalid_record,
+  FE_extra_data,
+  FE_write_error,
+  FE_bad_data,
+  FE_not_implemented,
+  FE_undefined_instance,
+  FE_internal
+};
+
+ostream &operator << (ostream &out, FltError error);
+
+#endif
+
+
+  

+ 107 - 0
pandatool/src/flt/fltExternalReference.cxx

@@ -0,0 +1,107 @@
+// Filename: fltExternalReference.cxx
+// Created by:  drose (30Aug00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "fltExternalReference.h"
+#include "fltRecordReader.h"
+#include "fltRecordWriter.h"
+
+TypeHandle FltExternalReference::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltExternalReference::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+FltExternalReference::
+FltExternalReference(FltHeader *header) : FltBead(header) {
+  _flags = 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltExternalReference::output
+//       Access: Public
+//  Description: Writes a quick one-line description of the record, but
+//               not its children.  This is a human-readable
+//               description, primarily for debugging; to write a flt
+//               file, use FltHeader::write_flt().
+////////////////////////////////////////////////////////////////////
+void FltExternalReference::
+output(ostream &out) const {
+  out << "External " << _filename;
+  if (!_bead_id.empty()) {
+    out << " (" << _bead_id << ")";
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltExternalReference::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 FltExternalReference::
+extract_record(FltRecordReader &reader) {
+  if (!FltBead::extract_record(reader)) {
+    return false;
+  }
+
+  nassertr(reader.get_opcode() == FO_external_ref, false);
+  DatagramIterator &iterator = reader.get_iterator();
+
+  string name = iterator.get_fixed_string(200);
+  iterator.skip_bytes(1 + 1);
+  iterator.skip_bytes(2);   // Undocumented additional padding.
+  _flags = iterator.get_be_uint32();
+  iterator.skip_bytes(2);
+  iterator.skip_bytes(2);   // Undocumented additional padding.
+
+  _filename = name;
+
+  if (!name.empty() && name[name.length() - 1] == '>') {
+    // Extract out the bead name.
+    size_t open = name.rfind('<');
+    if (open != string::npos) {
+      _filename = name.substr(0, open);
+      _bead_id = name.substr(open + 1, name.length() - open - 2);
+    }
+  }
+
+  nassertr(iterator.get_remaining_size() == 0, true);
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltExternalReference::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 FltExternalReference::
+build_record(FltRecordWriter &writer) const {
+  if (!FltBead::build_record(writer)) {
+    return false;
+  }
+
+  writer.set_opcode(FO_external_ref);
+  Datagram &datagram = writer.update_datagram();
+
+  string name = _filename;
+  if (!_bead_id.empty()) {
+    name += "<" + _bead_id + ">";
+  }
+
+  datagram.add_fixed_string(name.substr(0, 199), 200);
+  datagram.pad_bytes(1 + 1);
+  datagram.pad_bytes(2);   // Undocumented additional padding.
+  datagram.add_be_uint32(_flags);
+  datagram.pad_bytes(2);
+  datagram.pad_bytes(2);   // Undocumented additional padding.
+
+  return true;
+}

+ 63 - 0
pandatool/src/flt/fltExternalReference.h

@@ -0,0 +1,63 @@
+// Filename: fltExternalReference.h
+// Created by:  drose (30Aug00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef FLTEXTERNALREFERENCE_H
+#define FLTEXTERNALREFERENCE_H
+
+#include <pandatoolbase.h>
+
+#include "fltBead.h"
+
+#include <filename.h>
+
+////////////////////////////////////////////////////////////////////
+// 	 Class : FltExternalReference
+// Description : An external reference to another flt file (possibly
+//               to a specific bead within the flt file).
+////////////////////////////////////////////////////////////////////
+class FltExternalReference : public FltBead {
+public:
+  FltExternalReference(FltHeader *header);
+
+  virtual void output(ostream &out) const;
+
+  enum Flags {
+    F_color_palette_override      = 0x80000000,
+    F_material_palette_override   = 0x40000000,
+    F_texture_palette_override    = 0x20000000,
+    F_line_style_palette_override = 0x10000000,
+    F_sound_palette_override      = 0x08000000,
+    F_light_palette_override      = 0x04000000
+  };
+
+  Filename _filename;
+  string _bead_id;
+  int _flags;
+
+protected:
+  virtual bool extract_record(FltRecordReader &reader);
+  virtual bool build_record(FltRecordWriter &writer) const;
+
+public:
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    FltBead::init_type();
+    register_type(_type_handle, "FltExternalReference",
+		  FltBead::get_class_type());
+  }
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#endif
+
+

+ 133 - 0
pandatool/src/flt/fltEyepoint.cxx

@@ -0,0 +1,133 @@
+// Filename: fltEyepoint.cxx
+// Created by:  drose (26Aug00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "fltEyepoint.h"
+#include "fltRecordReader.h"
+#include "fltRecordWriter.h"
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltEyepoint::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+FltEyepoint::
+FltEyepoint() {
+  _rotation_center.set(0.0, 0.0, 0.0);
+  _hpr.set(0.0, 0.0, 0.0);
+  _rotation = LMatrix4f::ident_mat();
+  _fov = 60.0;
+  _scale = 1.0;
+  _near_clip = 0.1;
+  _far_clip = 10000.0;
+  _fly_through = LMatrix4f::ident_mat();
+  _eyepoint.set(0.0, 0.0, 0.0);
+  _fly_through_yaw = 0.0;
+  _fly_through_pitch = 0.0;
+  _eyepoint_direction.set(0.0, 1.0, 0.0);
+  _no_fly_through = true;
+  _ortho_mode = false;
+  _is_valid = true;
+  _image_offset_x = 0;
+  _image_offset_y = 0;
+  _image_zoom = 1;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltEyepoint::extract_record
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+bool FltEyepoint::
+extract_record(FltRecordReader &reader) {
+  DatagramIterator &iterator = reader.get_iterator();
+
+  _rotation_center[0] = iterator.get_be_float64();
+  _rotation_center[1] = iterator.get_be_float64();
+  _rotation_center[2] = iterator.get_be_float64();
+  _hpr[0] = iterator.get_be_float32();
+  _hpr[1] = iterator.get_be_float32();
+  _hpr[2] = iterator.get_be_float32();
+  int r;
+  for (r = 0; r < 4; r++) {
+    for (int c = 0; c < 4; c++) {
+      _rotation(r, c) = iterator.get_be_float32();
+    }
+  }
+  _fov = iterator.get_be_float32();
+  _scale = iterator.get_be_float32();
+  _near_clip = iterator.get_be_float32();
+  _far_clip = iterator.get_be_float32();
+  for (r = 0; r < 4; r++) {
+    for (int c = 0; c < 4; c++) {
+      _fly_through(r, c) = iterator.get_be_float32();
+    }
+  }
+  _eyepoint[0] = iterator.get_be_float32();
+  _eyepoint[1] = iterator.get_be_float32();
+  _eyepoint[2] = iterator.get_be_float32();
+  _fly_through_yaw = iterator.get_be_float32();
+  _fly_through_pitch = iterator.get_be_float32();
+  _eyepoint_direction[0] = iterator.get_be_float32();
+  _eyepoint_direction[1] = iterator.get_be_float32();
+  _eyepoint_direction[2] = iterator.get_be_float32();
+  _no_fly_through = (iterator.get_be_int32() != 0);
+  _ortho_mode = (iterator.get_be_int32() != 0);
+  _is_valid = (iterator.get_be_int32() != 0);
+  _image_offset_x = iterator.get_be_int32();
+  _image_offset_y = iterator.get_be_int32();
+  _image_zoom = iterator.get_be_int32();
+  iterator.skip_bytes(4*9);
+
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltEyepoint::build_record
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+bool FltEyepoint::
+build_record(FltRecordWriter &writer) const {
+  Datagram &datagram = writer.update_datagram();
+
+  datagram.add_be_float64(_rotation_center[0]);
+  datagram.add_be_float64(_rotation_center[1]);
+  datagram.add_be_float64(_rotation_center[2]);
+  datagram.add_be_float32(_hpr[0]);
+  datagram.add_be_float32(_hpr[1]);
+  datagram.add_be_float32(_hpr[2]);
+  int r;
+  for (r = 0; r < 4; r++) {
+    for (int c = 0; c < 4; c++) {
+      datagram.add_be_float32(_rotation(r, c));
+    }
+  }
+  datagram.add_be_float32(_fov);
+  datagram.add_be_float32(_scale);
+  datagram.add_be_float32(_near_clip);
+  datagram.add_be_float32(_far_clip);
+  for (r = 0; r < 4; r++) {
+    for (int c = 0; c < 4; c++) {
+      datagram.add_be_float32(_fly_through(r, c));
+    }
+  }
+  datagram.add_be_float32(_eyepoint[0]);
+  datagram.add_be_float32(_eyepoint[1]);
+  datagram.add_be_float32(_eyepoint[2]);
+  datagram.add_be_float32(_fly_through_yaw);
+  datagram.add_be_float32(_fly_through_pitch);
+  datagram.add_be_float32(_eyepoint_direction[0]);
+  datagram.add_be_float32(_eyepoint_direction[1]);
+  datagram.add_be_float32(_eyepoint_direction[2]);
+  datagram.add_be_int32(_no_fly_through);
+  datagram.add_be_int32(_ortho_mode);
+  datagram.add_be_int32(_is_valid);
+  datagram.add_be_int32(_image_offset_x);
+  datagram.add_be_int32(_image_offset_y);
+  datagram.add_be_int32(_image_zoom);
+  datagram.pad_bytes(4*9);
+
+  return true;
+}

+ 52 - 0
pandatool/src/flt/fltEyepoint.h

@@ -0,0 +1,52 @@
+// Filename: fltEyepoint.h
+// Created by:  drose (26Aug00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef FLTEYEPOINT_H
+#define FLTEYEPOINT_H
+
+#include <pandatoolbase.h>
+
+#include <luse.h>
+
+class FltRecordReader;
+class FltRecordWriter;
+
+////////////////////////////////////////////////////////////////////
+// 	 Class : FltEyepoint
+// Description : A single eyepoint entry in the eyepoint/trackplane
+//               palette.
+////////////////////////////////////////////////////////////////////
+class FltEyepoint {
+public:
+  FltEyepoint();
+
+  bool extract_record(FltRecordReader &reader);
+  bool build_record(FltRecordWriter &writer) const;
+
+public:
+  LPoint3d _rotation_center;
+  LVecBase3f _hpr;
+  LMatrix4f _rotation;
+  float _fov;
+  float _scale;
+  float _near_clip;
+  float _far_clip;
+  LMatrix4f _fly_through;
+  LPoint3f _eyepoint;
+  float _fly_through_yaw;
+  float _fly_through_pitch;
+  LVector3f _eyepoint_direction;
+  bool _no_fly_through;
+  bool _ortho_mode;
+  bool _is_valid;
+  int _image_offset_x;
+  int _image_offset_y;
+  int _image_zoom;
+};
+
+#endif
+
+
+  

+ 60 - 0
pandatool/src/flt/fltFace.I

@@ -0,0 +1,60 @@
+// Filename: fltFace.I
+// Created by:  drose (30Aug00)
+// 
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltFace::has_texture
+//       Access: Public
+//  Description: Returns true if the face has a texture applied, false
+//               otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool FltFace::
+has_texture() const {
+  return (_texture_index >= 0 && _header->has_texture(_texture_index));
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltFace::get_texture
+//       Access: Public
+//  Description: Returns the texture applied to this face, or NULL if
+//               no texture was applied.
+////////////////////////////////////////////////////////////////////
+INLINE FltTexture *FltFace::
+get_texture() const {
+  return _header->get_texture(_texture_index);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltFace::has_material
+//       Access: Public
+//  Description: Returns true if the face has a material applied, false
+//               otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool FltFace::
+has_material() const {
+  return (_material_index >= 0 && _header->has_material(_material_index));
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltFace::get_material
+//       Access: Public
+//  Description: Returns the material applied to this face, or NULL if
+//               no material was applied.
+////////////////////////////////////////////////////////////////////
+INLINE FltMaterial *FltFace::
+get_material() const {
+  return _header->get_material(_material_index);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltFace::has_color
+//       Access: Public
+//  Description: Returns true if the face has a primary color
+//               indicated, false otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool FltFace::
+has_color() const {
+  return ((_flags & F_no_color) == 0) || has_material();
+}

+ 250 - 0
pandatool/src/flt/fltFace.cxx

@@ -0,0 +1,250 @@
+// Filename: fltFace.cxx
+// Created by:  drose (25Aug00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "fltFace.h"
+#include "fltRecordReader.h"
+#include "fltRecordWriter.h"
+#include "fltHeader.h"
+#include "fltMaterial.h"
+
+TypeHandle FltFace::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltFace::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+FltFace::
+FltFace(FltHeader *header) : FltBeadID(header) {
+  _ir_color = 0;
+  _relative_priority = 0;
+  _draw_type = DT_solid_backface;
+  _texwhite = false;
+  _color_name_index = 0;
+  _alt_color_name_index = 0;
+  _billboard_type = BT_none;
+  _detail_texture_index = -1;
+  _texture_index = -1;
+  _material_index = -1;
+  _dfad_material_code = 0;
+  _dfad_feature_id = 0;
+  _ir_material_code = 0;
+  _transparency = 0;
+  _lod_generation_control = 0;
+  _line_style_index = 0;
+  _flags = 0;
+  _light_mode = LM_face_no_normal;
+  _texture_mapping_index = 0;
+  _color_index = 0;
+  _alt_color_index = 0;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltFace::get_color
+//       Access: Public
+//  Description: If has_color() indicates true, returns the primary
+//               color of the face, as a four-component value
+//               (including alpha as the transparency channel).
+////////////////////////////////////////////////////////////////////
+Colorf FltFace::
+get_color() const {
+  nassertr(has_color(), Colorf(0.0, 0.0, 0.0, 0.0));
+
+  if (_texwhite && has_texture()) {
+    // Force this one white.
+    return Colorf(1.0, 1.0, 1.0, 1.0 - (_transparency / 65535.0));
+  }
+
+  if (has_material()) {
+    // If we have a material, that replaces the color.
+    FltMaterial *material = get_material();
+    return Colorf(material->_diffuse[0],
+		  material->_diffuse[1],
+		  material->_diffuse[2],
+		  1.0 - (_transparency / 65535.0));
+  }
+
+  return _header->get_color(_color_index, (_flags & F_packed_color) != 0,
+			    _packed_color, _transparency);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltFace::get_rgb
+//       Access: Public
+//  Description: If has_color() indicates true, returns the primary
+//               color of the face, as a three-component value
+//               ignoring transparency.
+////////////////////////////////////////////////////////////////////
+RGBColorf FltFace::
+get_rgb() const {
+  nassertr(has_color(), RGBColorf(0.0, 0.0, 0.0));
+
+  if (_texwhite && has_texture()) {
+    // Force this one white.
+    return RGBColorf(1.0, 1.0, 1.0);
+  }
+
+  if (has_material()) {
+    // If we have a material, that replaces the color.
+    FltMaterial *material = get_material();
+    return material->_diffuse;
+  }
+
+  return _header->get_rgb(_color_index, (_flags & F_packed_color) != 0,
+			  _packed_color);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltFace::has_alt_color
+//       Access: Public
+//  Description: Returns true if the face has an alternate color
+//               indicated, false otherwise.
+////////////////////////////////////////////////////////////////////
+bool FltFace::
+has_alt_color() const {
+  return (_flags & F_no_alt_color) == 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltFace::get_alt_color
+//       Access: Public
+//  Description: If has_alt_color() indicates true, returns the alternate
+//               color of the face, as a four-component value
+//               (including alpha as the transparency channel).
+////////////////////////////////////////////////////////////////////
+Colorf FltFace::
+get_alt_color() const {
+  nassertr(has_alt_color(), Colorf(0.0, 0.0, 0.0, 0.0));
+
+  return _header->get_color(_alt_color_index, (_flags & F_packed_color) != 0,
+			    _alt_packed_color, _transparency);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltFace::get_alt_rgb
+//       Access: Public
+//  Description: If has_alt_color() indicates true, returns the alternate
+//               color of the face, as a three-component value
+//               ignoring transparency.
+////////////////////////////////////////////////////////////////////
+RGBColorf FltFace::
+get_alt_rgb() const {
+  nassertr(has_alt_color(), RGBColorf(0.0, 0.0, 0.0));
+
+  return _header->get_rgb(_alt_color_index, (_flags & F_packed_color) != 0,
+			  _alt_packed_color);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltFace::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 FltFace::
+extract_record(FltRecordReader &reader) {
+  if (!FltBeadID::extract_record(reader)) {
+    return false;
+  }
+
+  nassertr(reader.get_opcode() == FO_face, false);
+  DatagramIterator &iterator = reader.get_iterator();
+
+  _ir_color = iterator.get_be_int32();
+  _relative_priority = iterator.get_be_int16();
+  _draw_type = (DrawType)iterator.get_int8();
+  _texwhite = (iterator.get_int8() != 0);
+  _color_name_index = iterator.get_be_int16();
+  _alt_color_name_index = iterator.get_be_int16();
+  iterator.skip_bytes(1);
+  _billboard_type = (BillboardType)iterator.get_int8();
+  _detail_texture_index = iterator.get_be_int16();
+  _texture_index = iterator.get_be_int16();
+  _material_index = iterator.get_be_int16();
+  _dfad_material_code = iterator.get_be_int16();
+  _dfad_feature_id = iterator.get_be_int16();
+  _ir_material_code = iterator.get_be_int32();
+  _transparency = iterator.get_be_uint16();
+  _lod_generation_control = iterator.get_uint8();
+  _line_style_index = iterator.get_uint8();
+  _flags = iterator.get_be_uint32();
+  _light_mode = (LightMode)iterator.get_uint8();
+  iterator.skip_bytes(1 + 4);
+  iterator.skip_bytes(2); // Undocumented padding.
+  
+  if (!_packed_color.extract_record(reader)) {
+    return false;
+  }
+  if (!_alt_packed_color.extract_record(reader)) {
+    return false;
+  }
+  
+  _texture_mapping_index = iterator.get_be_int16();
+  iterator.skip_bytes(2);
+  _color_index = iterator.get_be_uint32();
+  _alt_color_index = iterator.get_be_uint32();
+  iterator.skip_bytes(2 + 2);
+
+  nassertr(iterator.get_remaining_size() == 0, true);
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltFace::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 FltFace::
+build_record(FltRecordWriter &writer) const {
+  if (!FltBeadID::build_record(writer)) {
+    return false;
+  }
+
+  writer.set_opcode(FO_face);
+  Datagram &datagram = writer.update_datagram();
+
+  datagram.add_be_int32(_ir_color);
+  datagram.add_be_int16(_relative_priority);
+  datagram.add_int8(_draw_type);
+  datagram.add_int8(_texwhite);
+  datagram.add_be_uint16(_color_name_index);
+  datagram.add_be_uint16(_alt_color_name_index);
+  datagram.pad_bytes(1);
+  datagram.add_int8(_billboard_type);
+  datagram.add_be_int16(_detail_texture_index);
+  datagram.add_be_int16(_texture_index);
+  datagram.add_be_int16(_material_index);
+  datagram.add_be_int16(_dfad_material_code);
+  datagram.add_be_int16(_dfad_feature_id);
+  datagram.add_be_int32(_ir_material_code);
+  datagram.add_be_uint16(_transparency);
+  datagram.add_uint8(_lod_generation_control);
+  datagram.add_uint8(_line_style_index);
+  datagram.add_be_uint32(_flags);
+  datagram.add_uint8(_light_mode);
+  datagram.pad_bytes(1 + 4);
+  datagram.pad_bytes(2); // Undocumented padding.
+
+  if (!_packed_color.build_record(writer)) {
+    return false;
+  }
+  if (!_alt_packed_color.build_record(writer)) {
+    return false;
+  }
+
+  datagram.add_be_int16(_texture_mapping_index);
+  datagram.pad_bytes(2);
+  datagram.add_be_uint32(_color_index);
+  datagram.add_be_uint32(_alt_color_index);
+  datagram.pad_bytes(2 + 2);
+
+  return true;
+}

+ 128 - 0
pandatool/src/flt/fltFace.h

@@ -0,0 +1,128 @@
+// Filename: fltFace.h
+// Created by:  drose (25Aug00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef FLTFACE_H
+#define FLTFACE_H
+
+#include <pandatoolbase.h>
+
+#include "fltBeadID.h"
+#include "fltPackedColor.h"
+#include "fltHeader.h"
+
+#include <luse.h>
+
+class FltTexture;
+class FltMaterial;
+
+////////////////////////////////////////////////////////////////////
+// 	 Class : FltFace
+// Description : A single face bead, e.g. a polygon.
+////////////////////////////////////////////////////////////////////
+class FltFace : public FltBeadID {
+public:
+  FltFace(FltHeader *header);
+
+  enum DrawType {
+    DT_solid_backface      = 0,
+    DT_solid_no_backface   = 1,
+    DT_wireframe           = 2,
+    DT_wireframe_close     = 3,
+    DT_wireframe_highlight = 4,
+    DT_omni_light          = 5,
+    DT_uni_light           = 6,
+    DT_bi_light            = 7
+  };
+
+  enum BillboardType {
+    BT_none                = 0,
+    BT_fixed               = 1,
+    BT_axial               = 2,
+    BT_point               = 4
+  };
+
+  enum Flags {
+    F_terrain              = 0x80000000,
+    F_no_color             = 0x40000000,
+    F_no_alt_color         = 0x20000000,
+    F_packed_color         = 0x10000000,
+    F_terrain_footprint    = 0x08000000,
+    F_hidden               = 0x04000000
+  };
+
+  enum LightMode {
+    LM_face_no_normal      = 0,
+    LM_vertex_no_normal    = 1,
+    LM_face_with_normal    = 2,
+    LM_vertex_with_normal  = 3
+  };
+
+  int _ir_color;
+  int _relative_priority;
+  DrawType _draw_type;
+  bool _texwhite;
+  int _color_name_index;
+  int _alt_color_name_index;
+  BillboardType _billboard_type;
+  int _detail_texture_index;
+  int _texture_index;
+  int _material_index;
+  int _dfad_material_code;
+  int _dfad_feature_id;
+  int _ir_material_code;
+  int _transparency;
+  int _lod_generation_control;
+  int _line_style_index;
+  unsigned int _flags;
+  LightMode _light_mode;
+  FltPackedColor _packed_color;
+  FltPackedColor _alt_packed_color;
+  int _texture_mapping_index;
+  unsigned int _color_index;
+  unsigned int _alt_color_index;
+
+public:
+  INLINE bool has_texture() const;
+  INLINE FltTexture *get_texture() const;
+
+  INLINE bool has_material() const;
+  INLINE FltMaterial *get_material() const;
+
+  INLINE bool has_color() const;
+  Colorf get_color() const;
+  RGBColorf get_rgb() const;
+
+  bool has_alt_color() const;
+  Colorf get_alt_color() const;
+  RGBColorf get_alt_rgb() const;
+
+
+protected:
+  virtual bool extract_record(FltRecordReader &reader);
+  virtual bool build_record(FltRecordWriter &writer) const;
+
+public:
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    FltBeadID::init_type();
+    register_type(_type_handle, "FltFace",
+		  FltBeadID::get_class_type());
+  }
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#include "fltFace.I"
+
+#endif
+
+

+ 84 - 0
pandatool/src/flt/fltGroup.cxx

@@ -0,0 +1,84 @@
+// Filename: fltGroup.cxx
+// Created by:  drose (24Aug00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "fltGroup.h"
+#include "fltRecordReader.h"
+#include "fltRecordWriter.h"
+
+TypeHandle FltGroup::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltGroup::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+FltGroup::
+FltGroup(FltHeader *header) : FltBeadID(header) {
+  _relative_priority = 0;
+  _flags = 0;
+  _special_id1 = 0;
+  _special_id2 = 0;
+  _significance = 0;
+  _layer_id = 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltGroup::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 FltGroup::
+extract_record(FltRecordReader &reader) {
+  if (!FltBeadID::extract_record(reader)) {
+    return false;
+  }
+
+  nassertr(reader.get_opcode() == FO_group, false);
+  DatagramIterator &iterator = reader.get_iterator();
+
+  _relative_priority = iterator.get_be_int16();
+  iterator.skip_bytes(2);
+  _flags = iterator.get_be_uint32();
+  _special_id1 = iterator.get_be_int16();
+  _special_id2 = iterator.get_be_int16();
+  _significance = iterator.get_be_int16();
+  _layer_id = iterator.get_int8();
+  iterator.skip_bytes(5);
+
+  nassertr(iterator.get_remaining_size() == 0, true);
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltGroup::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 FltGroup::
+build_record(FltRecordWriter &writer) const {
+  if (!FltBeadID::build_record(writer)) {
+    return false;
+  }
+
+  writer.set_opcode(FO_group);
+  Datagram &datagram = writer.update_datagram();
+
+  datagram.add_be_int16(_relative_priority);
+  datagram.pad_bytes(2);
+  datagram.add_be_uint32(_flags);
+  datagram.add_be_int16(_special_id1);
+  datagram.add_be_int16(_special_id2);
+  datagram.add_be_int16(_significance);
+  datagram.add_int8(_layer_id);
+  datagram.pad_bytes(5);
+
+  return true;
+}

+ 59 - 0
pandatool/src/flt/fltGroup.h

@@ -0,0 +1,59 @@
+// Filename: fltGroup.h
+// Created by:  drose (24Aug00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef FLTGROUP_H
+#define FLTGROUP_H
+
+#include <pandatoolbase.h>
+
+#include "fltBeadID.h"
+
+////////////////////////////////////////////////////////////////////
+// 	 Class : FltGroup
+// Description : The main grouping bead of the flt file.
+////////////////////////////////////////////////////////////////////
+class FltGroup : public FltBeadID {
+public:
+  FltGroup(FltHeader *header);
+  
+  enum Flags {
+    F_forward_animation   = 0x40000000,
+    F_swing_animation     = 0x20000000,
+    F_bounding_box        = 0x10000000,
+    F_freeze_bounding_box = 0x08000000,
+    F_default_parent      = 0x04000000,
+  };
+
+  int _relative_priority;
+  unsigned int _flags;
+  int _special_id1, _special_id2;
+  int _significance;
+  int _layer_id;
+
+protected:
+  virtual bool extract_record(FltRecordReader &reader);
+  virtual bool build_record(FltRecordWriter &writer) const;
+
+public:
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    FltBeadID::init_type();
+    register_type(_type_handle, "FltGroup",
+		  FltBeadID::get_class_type());
+  }
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#endif
+
+

+ 1556 - 0
pandatool/src/flt/fltHeader.cxx

@@ -0,0 +1,1556 @@
+// Filename: fltHeader.cxx
+// Created by:  drose (24Aug00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "fltHeader.h"
+#include "fltRecordReader.h"
+#include "fltRecordWriter.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_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)) {
+    return FE_could_not_open;
+  }
+
+  // By default, the filename's directory is added to the texture
+  // search path.
+  string dirname = filename.get_dirname();
+  if (!dirname.empty()) {
+    _texture_path.append_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) {
+    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()) {
+    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)) {
+    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()) {
+    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::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::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_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();
+
+  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_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)) {
+      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)) {
+	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_color_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)) {
+      return FE_bad_data;
+    }
+  }
+
+  int num_trackplanes = get_num_trackplanes();
+  for (i = 0; i < num_trackplanes; i++) {
+    if (!_trackplanes[i].build_record(writer)) {
+      return FE_bad_data;
+    }
+  }
+
+  return writer.advance();
+}

+ 310 - 0
pandatool/src/flt/fltHeader.h

@@ -0,0 +1,310 @@
+// Filename: fltHeader.h
+// Created by:  drose (24Aug00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef FLTHEADER_H
+#define FLTHEADER_H
+
+#include <pandatoolbase.h>
+
+#include "fltBeadID.h"
+#include "fltVertex.h"
+#include "fltMaterial.h"
+#include "fltTexture.h"
+#include "fltLightSourceDefinition.h"
+#include "fltEyepoint.h"
+#include "fltTrackplane.h"
+#include "fltInstanceDefinition.h"
+
+#include <filename.h>
+#include <dSearchPath.h>
+
+////////////////////////////////////////////////////////////////////
+// 	 Class : FltHeader
+// Description : This is the first bead in the file, the top of the
+//               bead hierarchy, and the primary interface to reading
+//               and writing a Flt file.  You always read a Flt file
+//               by creating a header and calling read_flt(), which
+//               fills in its children beads automatically; you write
+//               a Flt file by creating a header, adding its children,
+//               and calling write_flt().
+////////////////////////////////////////////////////////////////////
+class FltHeader : public FltBeadID {
+public:
+  FltHeader();
+
+  FltError read_flt(Filename filename);
+  FltError read_flt(istream &in);
+  FltError write_flt(Filename filename);
+  FltError write_flt(ostream &out);
+
+  enum AttrUpdate {
+    AU_none,
+    AU_if_missing,
+    AU_always
+  };
+  
+  void set_auto_attr_update(AttrUpdate attr);
+  AttrUpdate get_auto_attr_update() const;
+
+  enum Units {
+    U_meters               = 0,
+    U_kilometers           = 1,
+    U_feet                 = 4,
+    U_inches               = 5,
+    U_nautical_miles       = 8
+  };
+
+  enum Flags {
+    F_save_vertex_normals  = 0x80000000
+  };
+
+  enum ProjectionType {
+    PT_flat_earth          = 0,
+    PT_trapezoidal         = 1,
+    PT_round_earth         = 2,
+    PT_lambert             = 3,
+    PT_utm                 = 4
+  };
+
+  enum VertexStorageType {
+    VTS_double             = 1
+  };
+
+  enum DatabaseOrigin {
+    DO_open_flight         = 100,
+    DO_dig                 = 200,
+    DO_es_ct6              = 300,
+    DO_psp                 = 400,
+    DO_ge_civ              = 600,
+    DO_es_gdf              = 700,
+  };
+
+  enum EarthModel {
+    EM_wgs84               = 0,
+    EM_wgs72               = 1,
+    EM_bessel              = 2,
+    EM_clarke_1866         = 3,
+    EM_nad27               = 4
+  };
+
+  int _format_revision_level;
+  int _edit_revision_level;
+  string _last_revision;
+  int _next_group_id;
+  int _next_lod_id;
+  int _next_object_id;
+  int _next_face_id;
+  int _unit_multiplier;
+  Units _vertex_units;
+  bool _texwhite_new;
+  unsigned int _flags;
+  ProjectionType _projection_type;
+  int _next_dof_id;
+  VertexStorageType _vertex_storage_type;
+  DatabaseOrigin _database_origin;
+  double _sw_x, _sw_y;
+  double _delta_x, _delta_y;
+  int _next_sound_id;
+  int _next_path_id;
+  int _next_clip_id;
+  int _next_text_id;
+  int _next_bsp_id;
+  int _next_switch_id;
+  double _sw_lat, _sw_long;
+  double _ne_lat, _ne_long;
+  double _origin_lat, _origin_long;
+  double _lambert_upper_lat, _lambert_lower_lat;
+  int _next_light_id;
+  int _next_road_id;
+  int _next_cat_id;
+  EarthModel _earth_model;
+
+public:
+  double get_flt_version() const;
+  static double min_flt_version();
+  static double max_flt_version();
+
+
+  // Accessors into the instance pool.
+  bool has_instance(int instance_index) const;
+  FltInstanceDefinition *get_instance(int instance_index) const;
+  void clear_instances();
+  void add_instance(FltInstanceDefinition *instance);
+  void remove_instance(int instance_index);
+
+
+  // Accessors into the vertex palette.
+  int get_num_vertices() const;
+  FltVertex *get_vertex(int n) const;
+  void clear_vertices();
+  void add_vertex(FltVertex *vertex);
+
+  FltVertex *get_vertex_by_offset(int offset);
+  int get_offset_by_vertex(FltVertex *vertex);
+
+
+  // Accessors into the color palette.  This is read-only; why would
+  // you want to mess with building a new color palette?
+  int get_num_colors() const;
+  Colorf get_color(int color_index) const;
+  RGBColorf get_rgb(int color_index) const;
+  bool has_color_name(int color_index) const;
+  string get_color_name(int color_index) const;
+
+  int get_closest_color(Colorf color) const;
+  int get_closest_rgb(RGBColorf color) const;
+
+  int get_num_color_entries() const;
+  int get_num_color_shades() const;
+
+  // These functions are mainly used behind-the-scenes to decode the
+  // strange forest of color options defined for faces and vertices.
+  Colorf get_color(int color_index, bool use_packed_color,
+		   const FltPackedColor &packed_color, 
+		   int transparency);
+  RGBColorf get_rgb(int color_index, bool use_packed_color,
+		    const FltPackedColor &packed_color);
+
+  // Accessors into the material palette.
+  bool has_material(int material_index) const;
+  FltMaterial *get_material(int material_index) const;
+  void clear_materials();
+  void add_material(FltMaterial *material);
+  void remove_material(int material_index);
+
+
+  // Accessors into the texture palette.
+  bool has_texture(int texture_index) const;
+  FltTexture *get_texture(int texture_index) const;
+  void clear_textures();
+  void add_texture(FltTexture *texture);
+  void remove_texture(int texture_index);
+
+  // Sometimes Flt files store textures as relative pathnames.
+  // Setting this search path helps resolve that tendency.
+  void set_texture_path(const DSearchPath &path);
+  DSearchPath &update_texture_path();
+  const DSearchPath &get_texture_path() const;
+
+
+  // Accessors into the light source palette.
+  bool has_light_source(int light_index) const;
+  FltLightSourceDefinition *get_light_source(int light_index) const;
+  void clear_light_sources();
+  void add_light_source(FltLightSourceDefinition *light_source);
+  void remove_light_source(int light_index);
+
+  
+  // Accessors into the eyepoint/trackplane palette.
+  bool got_eyepoint_trackplane_palette() const;
+  void set_eyepoint_trackplane_palette(bool flag);
+
+  int get_num_eyepoints() const;
+  FltEyepoint *get_eyepoint(int n);
+  int get_num_trackplanes() const;
+  FltTrackplane *get_trackplane(int n);
+
+private:
+  // Instance subtrees.  These are standalone subtrees, which may be
+  // referenced by various points in the hierarchy, stored by instance
+  // ID number.
+  typedef map<int, PT(FltInstanceDefinition)> Instances;
+  Instances _instances;
+
+
+  // Support for the vertex palette.
+  int update_vertex_lookups();
+
+  typedef vector<PT(FltVertex)> Vertices;
+  typedef set<FltVertex *> UniqueVertices;
+
+  typedef map<int, FltVertex *> VerticesByOffset;
+  typedef map<FltVertex *, int> OffsetsByVertex;
+
+  Vertices _vertices;
+  UniqueVertices _unique_vertices;
+  VerticesByOffset _vertices_by_offset;
+  OffsetsByVertex _offsets_by_vertex;
+  
+  bool _vertex_lookups_stale;
+
+  // This is maintained while the header is being read, to map the
+  // vertices to their corresponding offsets in the vertex palette.
+  int _current_vertex_offset;
+
+
+  // Support for the color palette.
+  typedef vector<FltPackedColor> Colors;
+  typedef map<int, string> ColorNames;
+  Colors _colors;
+  ColorNames _color_names;
+
+
+  // Support for the material palette.
+  typedef map<int, PT(FltMaterial)> Materials;
+  Materials _materials;
+
+
+  // Support for the texture palette.
+  AttrUpdate _auto_attr_update;
+  typedef map<int, PT(FltTexture)> Textures;
+  Textures _textures;
+  DSearchPath _texture_path;
+
+
+  // Support for the light source palette.
+  typedef map<int, PT(FltLightSourceDefinition)> LightSources;
+  LightSources _light_sources;
+
+
+  // Support for the eyepoint/trackplane palette.
+  bool _got_eyepoint_trackplane_palette;
+  FltEyepoint _eyepoints[10];
+  FltTrackplane _trackplanes[10];
+
+
+protected:
+  virtual bool extract_record(FltRecordReader &reader);
+  virtual bool extract_ancillary(FltRecordReader &reader);
+
+  virtual bool build_record(FltRecordWriter &writer) const;
+  virtual FltError write_ancillary(FltRecordWriter &writer) const;
+
+private:
+  bool extract_vertex(FltRecordReader &reader);
+  bool extract_color_palette(FltRecordReader &reader);
+  bool extract_material(FltRecordReader &reader);
+  bool extract_texture(FltRecordReader &reader);
+  bool extract_light_source(FltRecordReader &reader);
+  bool extract_eyepoint_palette(FltRecordReader &reader);
+
+  FltError write_vertex_palette(FltRecordWriter &writer) const;
+  FltError write_color_palette(FltRecordWriter &writer) const;
+  FltError write_material_palette(FltRecordWriter &writer) const;
+  FltError write_texture_palette(FltRecordWriter &writer) const;
+  FltError write_light_source_palette(FltRecordWriter &writer) const;
+  FltError write_eyepoint_palette(FltRecordWriter &writer) const;
+
+public:
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    FltBeadID::init_type();
+    register_type(_type_handle, "FltHeader",
+		  FltBeadID::get_class_type());
+  }
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#endif
+
+

+ 67 - 0
pandatool/src/flt/fltInstanceDefinition.cxx

@@ -0,0 +1,67 @@
+// Filename: fltInstanceDefinition.cxx
+// Created by:  drose (30Aug00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "fltInstanceDefinition.h"
+#include "fltRecordReader.h"
+#include "fltRecordWriter.h"
+
+TypeHandle FltInstanceDefinition::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltInstanceDefinition::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+FltInstanceDefinition::
+FltInstanceDefinition(FltHeader *header) : FltBead(header) {
+  _instance_index = 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltInstanceDefinition::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 FltInstanceDefinition::
+extract_record(FltRecordReader &reader) {
+  if (!FltBead::extract_record(reader)) {
+    return false;
+  }
+
+  nassertr(reader.get_opcode() == FO_instance, false);
+  DatagramIterator &iterator = reader.get_iterator();
+
+  iterator.skip_bytes(2);
+  _instance_index = iterator.get_be_int16();
+
+  nassertr(iterator.get_remaining_size() == 0, true);
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltInstanceDefinition::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 FltInstanceDefinition::
+build_record(FltRecordWriter &writer) const {
+  if (!FltBead::build_record(writer)) {
+    return false;
+  }
+
+  writer.set_opcode(FO_instance);
+  Datagram &datagram = writer.update_datagram();
+
+  datagram.pad_bytes(2);
+  datagram.add_be_int16(_instance_index);
+
+  return true;
+}

+ 56 - 0
pandatool/src/flt/fltInstanceDefinition.h

@@ -0,0 +1,56 @@
+// Filename: fltInstanceDefinition.h
+// Created by:  drose (30Aug00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef FLTINSTANCEDEFINITION_H
+#define FLTINSTANCEDEFINITION_H
+
+#include <pandatoolbase.h>
+
+#include "fltBead.h"
+
+////////////////////////////////////////////////////////////////////
+// 	 Class : FltInstanceDefinition
+// Description : This special kind of record marks the top node of an
+//               instance subtree.  This subtree lives outside of the
+//               normal hierarchy, and is MultiGen's way of support
+//               instancing--each instance subtree has a unique index,
+//               which may be referenced in a FltInstanceRef object to
+//               make the instance appear in various places in the
+//               hierarchy.
+////////////////////////////////////////////////////////////////////
+class FltInstanceDefinition : public FltBead {
+public:
+  FltInstanceDefinition(FltHeader *header);
+
+  int _instance_index;
+
+protected:
+  virtual bool extract_record(FltRecordReader &reader);
+  virtual bool build_record(FltRecordWriter &writer) const;
+
+public:
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    FltBead::init_type();
+    register_type(_type_handle, "FltInstanceDefinition",
+		  FltBead::get_class_type());
+  }
+
+private:
+  static TypeHandle _type_handle;
+
+  friend class FltInstanceRef;
+  friend class FltRecordWriter;
+};
+
+#endif
+
+

+ 108 - 0
pandatool/src/flt/fltInstanceRef.cxx

@@ -0,0 +1,108 @@
+// Filename: fltInstanceRef.cxx
+// Created by:  drose (30Aug00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "fltInstanceRef.h"
+#include "fltRecordReader.h"
+#include "fltRecordWriter.h"
+#include "fltInstanceDefinition.h"
+#include "fltHeader.h"
+
+TypeHandle FltInstanceRef::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltInstanceRef::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+FltInstanceRef::
+FltInstanceRef(FltHeader *header) : FltBead(header) {
+  _instance_index = 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltInstanceRef::write
+//       Access: Public
+//  Description: Writes a multiple-line description of the record and
+//               all of its children.  This is a human-readable
+//               description, primarily for debugging; to write a flt
+//               file, use FltHeader::write_flt().
+////////////////////////////////////////////////////////////////////
+void FltInstanceRef::
+write(ostream &out, int indent_level) const {
+  indent(out, indent_level) << "instance";
+  FltInstanceDefinition *def = _header->get_instance(_instance_index);
+  if (def != (FltInstanceDefinition *)NULL) {
+    def->write_children(out, indent_level + 2);
+    indent(out, indent_level) << "}\n";
+  } else {
+    out << "\n";
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltInstanceRef::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 FltInstanceRef::
+extract_record(FltRecordReader &reader) {
+  if (!FltBead::extract_record(reader)) {
+    return false;
+  }
+
+  nassertr(reader.get_opcode() == FO_instance_ref, false);
+  DatagramIterator &iterator = reader.get_iterator();
+
+  iterator.skip_bytes(2);
+  _instance_index = iterator.get_be_int16();
+
+  nassertr(iterator.get_remaining_size() == 0, true);
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltInstanceRef::write_record_and_children
+//       Access: Protected, Virtual
+//  Description: Writes this record out to the flt file, along with all
+//               of its ancillary records and children records.  Returns
+//               FE_ok on success, or something else on error.
+////////////////////////////////////////////////////////////////////
+FltError FltInstanceRef::
+write_record_and_children(FltRecordWriter &writer) const {
+  // First, make sure our instance definition has already been written.
+  FltError result = writer.write_instance_def(_header, _instance_index);
+  if (result != FE_ok) {
+    return result;
+  }
+
+  // Then write out our own record.
+  return FltBead::write_record_and_children(writer);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltInstanceRef::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 FltInstanceRef::
+build_record(FltRecordWriter &writer) const {
+  if (!FltBead::build_record(writer)) {
+    return false;
+  }
+
+  writer.set_opcode(FO_instance_ref);
+  Datagram &datagram = writer.update_datagram();
+
+  datagram.pad_bytes(2);
+  datagram.add_be_int16(_instance_index);
+
+  return true;
+}

+ 54 - 0
pandatool/src/flt/fltInstanceRef.h

@@ -0,0 +1,54 @@
+// Filename: fltInstanceRef.h
+// Created by:  drose (30Aug00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef FLTINSTANCEREF_H
+#define FLTINSTANCEREF_H
+
+#include <pandatoolbase.h>
+
+#include "fltBead.h"
+
+////////////////////////////////////////////////////////////////////
+// 	 Class : FltInstanceRef
+// Description : This bead appears in the hierarchy to refer to a
+//               FltInstanceDefinition node defined elsewhere.  It
+//               indicates that the subtree beginning at the
+//               FltInstanceDefinition should be considered to be
+//               instanced here.
+////////////////////////////////////////////////////////////////////
+class FltInstanceRef : public FltBead {
+public:
+  FltInstanceRef(FltHeader *header);
+
+  int _instance_index;
+
+  virtual void write(ostream &out, int indent_level = 0) const;
+
+protected:
+  virtual bool extract_record(FltRecordReader &reader);
+  virtual FltError write_record_and_children(FltRecordWriter &writer) const;
+  virtual bool build_record(FltRecordWriter &writer) const;
+
+public:
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    FltBead::init_type();
+    register_type(_type_handle, "FltInstanceRef",
+		  FltBead::get_class_type());
+  }
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#endif
+
+

+ 91 - 0
pandatool/src/flt/fltLOD.cxx

@@ -0,0 +1,91 @@
+// Filename: fltLOD.cxx
+// Created by:  drose (25Aug00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "fltLOD.h"
+#include "fltRecordReader.h"
+#include "fltRecordWriter.h"
+
+TypeHandle FltLOD::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltLOD::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+FltLOD::
+FltLOD(FltHeader *header) : FltBeadID(header) {
+  _switch_in = 0.0;
+  _switch_out = 0.0;
+  _special_id1 = 0;
+  _special_id2 = 0;
+  _flags = 0;
+  _center_x = 0.0;
+  _center_y = 0.0;
+  _center_z = 0.0;
+  _transition_range = 0.0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltLOD::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 FltLOD::
+extract_record(FltRecordReader &reader) {
+  if (!FltBeadID::extract_record(reader)) {
+    return false;
+  }
+
+  nassertr(reader.get_opcode() == FO_lod, false);
+  DatagramIterator &iterator = reader.get_iterator();
+
+  iterator.skip_bytes(4);
+  _switch_in = iterator.get_be_float64();
+  _switch_out = iterator.get_be_float64();
+  _special_id1 = iterator.get_be_int16();
+  _special_id2 = iterator.get_be_int16();
+  _flags = iterator.get_be_uint32();
+  _center_x = iterator.get_be_float64();
+  _center_y = iterator.get_be_float64();
+  _center_z = iterator.get_be_float64();
+  _transition_range = iterator.get_be_float64();
+
+  nassertr(iterator.get_remaining_size() == 0, true);
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltLOD::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 FltLOD::
+build_record(FltRecordWriter &writer) const {
+  if (!FltBeadID::build_record(writer)) {
+    return false;
+  }
+
+  writer.set_opcode(FO_lod);
+  Datagram &datagram = writer.update_datagram();
+
+  datagram.pad_bytes(4);
+  datagram.add_be_float64(_switch_in);
+  datagram.add_be_float64(_switch_out);
+  datagram.add_be_int16(_special_id1);
+  datagram.add_be_int16(_special_id2);
+  datagram.add_be_uint32(_flags);
+  datagram.add_be_float64(_center_x);
+  datagram.add_be_float64(_center_y);
+  datagram.add_be_float64(_center_z);
+  datagram.add_be_float64(_transition_range);
+
+  return true;
+}

+ 59 - 0
pandatool/src/flt/fltLOD.h

@@ -0,0 +1,59 @@
+// Filename: fltLOD.h
+// Created by:  drose (25Aug00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef FLTLOD_H
+#define FLTLOD_H
+
+#include <pandatoolbase.h>
+
+#include "fltBeadID.h"
+
+////////////////////////////////////////////////////////////////////
+// 	 Class : FltLOD
+// Description : A Level-of-Detail record.
+////////////////////////////////////////////////////////////////////
+class FltLOD : public FltBeadID {
+public:
+  FltLOD(FltHeader *header);
+
+  enum Flags {
+    F_use_previous_slant  = 0x80000000,
+    F_freeze_center       = 0x20000000
+  };
+
+  double _switch_in;
+  double _switch_out;
+  int _special_id1, _special_id2;
+  unsigned int _flags;
+  double _center_x;
+  double _center_y;
+  double _center_z;
+  double _transition_range;
+
+protected:
+  virtual bool extract_record(FltRecordReader &reader);
+  virtual bool build_record(FltRecordWriter &writer) const;
+
+public:
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    FltBeadID::init_type();
+    register_type(_type_handle, "FltLOD",
+		  FltBeadID::get_class_type());
+  }
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#endif
+
+

+ 129 - 0
pandatool/src/flt/fltLightSourceDefinition.cxx

@@ -0,0 +1,129 @@
+// Filename: fltLightSourceDefinition.cxx
+// Created by:  drose (26Aug00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "fltLightSourceDefinition.h"
+#include "fltRecordReader.h"
+#include "fltRecordWriter.h"
+
+TypeHandle FltLightSourceDefinition::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltLightSourceDefinition::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+FltLightSourceDefinition::
+FltLightSourceDefinition(FltHeader *header) : FltRecord(header) {
+  _light_index = 0;
+  _ambient.set(0.0, 0.0, 0.0, 1.0);
+  _diffuse.set(1.0, 1.0, 1.0, 1.0);
+  _specular.set(0.0, 0.0, 0.0, 1.0);
+  _light_type = LT_infinite;
+  _exponential_dropoff = 1.0;
+  _cutoff_angle = 180.0;
+  _yaw = 0.0;
+  _pitch = 0.0;
+  _constant_coefficient = 0.0;
+  _linear_coefficient = 0.0;
+  _quadratic_coefficient = 1.0;
+  _modeling_light = false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltLightSourceDefinition::extract_record
+//       Access: Protected, Virtual
+//  Description: Fills in the information in this record 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 FltLightSourceDefinition::
+extract_record(FltRecordReader &reader) {
+  if (!FltRecord::extract_record(reader)) {
+    return false;
+  }
+
+  nassertr(reader.get_opcode() == FO_light_definition, false);
+  DatagramIterator &iterator = reader.get_iterator();
+
+  _light_index = iterator.get_be_int32();
+  iterator.skip_bytes(2*4);
+  _light_name = iterator.get_fixed_string(20);
+  iterator.skip_bytes(4);
+  _ambient[0] = iterator.get_be_float32();
+  _ambient[1] = iterator.get_be_float32();
+  _ambient[2] = iterator.get_be_float32();
+  _ambient[3] = iterator.get_be_float32();
+  _diffuse[0] = iterator.get_be_float32();
+  _diffuse[1] = iterator.get_be_float32();
+  _diffuse[2] = iterator.get_be_float32();
+  _diffuse[3] = iterator.get_be_float32();
+  _specular[0] = iterator.get_be_float32();
+  _specular[1] = iterator.get_be_float32();
+  _specular[2] = iterator.get_be_float32();
+  _specular[3] = iterator.get_be_float32();
+  _light_type = (LightType)iterator.get_be_int32();
+  iterator.skip_bytes(4*10);
+  _exponential_dropoff = iterator.get_be_float32();
+  _cutoff_angle = iterator.get_be_float32();
+  _yaw = iterator.get_be_float32();
+  _pitch = iterator.get_be_float32();
+  _constant_coefficient = iterator.get_be_float32();
+  _linear_coefficient = iterator.get_be_float32();
+  _quadratic_coefficient = iterator.get_be_float32();
+  _modeling_light = (iterator.get_be_int32() != 0);
+  iterator.skip_bytes(4*19);
+
+  nassertr(iterator.get_remaining_size() == 0, true);
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltLightSourceDefinition::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 FltLightSourceDefinition::
+build_record(FltRecordWriter &writer) const {
+  if (!FltRecord::build_record(writer)) {
+    return false;
+  }
+  
+  writer.set_opcode(FO_light_definition);
+  Datagram &datagram = writer.update_datagram();
+
+  datagram.add_be_int32(_light_index);
+  datagram.pad_bytes(2*4);
+  datagram.add_fixed_string(_light_name, 20);
+  datagram.pad_bytes(4);
+  datagram.add_be_float32(_ambient[0]);
+  datagram.add_be_float32(_ambient[1]);
+  datagram.add_be_float32(_ambient[2]);
+  datagram.add_be_float32(_ambient[3]);
+  datagram.add_be_float32(_diffuse[0]);
+  datagram.add_be_float32(_diffuse[1]);
+  datagram.add_be_float32(_diffuse[2]);
+  datagram.add_be_float32(_diffuse[3]);
+  datagram.add_be_float32(_specular[0]);
+  datagram.add_be_float32(_specular[1]);
+  datagram.add_be_float32(_specular[2]);
+  datagram.add_be_float32(_specular[3]);
+  datagram.add_be_int32(_light_type);
+  datagram.pad_bytes(4*10);
+  datagram.add_be_float32(_exponential_dropoff);
+  datagram.add_be_float32(_cutoff_angle);
+  datagram.add_be_float32(_yaw);
+  datagram.add_be_float32(_pitch);
+  datagram.add_be_float32(_constant_coefficient);
+  datagram.add_be_float32(_linear_coefficient);
+  datagram.add_be_float32(_quadratic_coefficient);
+  datagram.add_be_int32(_modeling_light);
+  datagram.pad_bytes(4*19);
+  
+  return true;
+}

+ 77 - 0
pandatool/src/flt/fltLightSourceDefinition.h

@@ -0,0 +1,77 @@
+// Filename: fltLightSourceDefinition.h
+// Created by:  drose (26Aug00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef FLTLIGHTSOURCEDEFINITION_H
+#define FLTLIGHTSOURCEDEFINITION_H
+
+#include <pandatoolbase.h>
+
+#include "fltRecord.h"
+
+#include <luse.h>
+
+////////////////////////////////////////////////////////////////////
+// 	 Class : FltLightSourceDefinition
+// Description : Represents a single entry in the light source
+//               palette.  This completely defines the color, etc. of
+//               a single light source, which may be referenced later
+//               by a FltLightSource bead in the hierarchy.
+////////////////////////////////////////////////////////////////////
+class FltLightSourceDefinition : public FltRecord {
+public:
+  FltLightSourceDefinition(FltHeader *header);
+
+  enum LightType {
+    LT_infinite = 0,
+    LT_local    = 1,
+    LT_spot     = 2
+  };
+
+  int _light_index;
+  string _light_name;
+  Colorf _ambient;
+  Colorf _diffuse;
+  Colorf _specular;
+  LightType _light_type;
+  float _exponential_dropoff;
+  float _cutoff_angle;  // in degrees
+
+  // yaw and pitch only for modeling lights, which are positioned at
+  // the eyepoint.
+  float _yaw;
+  float _pitch;
+
+  float _constant_coefficient;
+  float _linear_coefficient;
+  float _quadratic_coefficient;
+  bool _modeling_light;
+
+protected:
+  virtual bool extract_record(FltRecordReader &reader);
+  virtual bool build_record(FltRecordWriter &writer) const;
+
+public:
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    FltRecord::init_type();
+    register_type(_type_handle, "FltLightSourceDefinition",
+		  FltRecord::get_class_type());
+  }
+
+private:
+  static TypeHandle _type_handle;
+
+  friend class FltHeader;
+};
+
+#endif
+
+

+ 106 - 0
pandatool/src/flt/fltMaterial.cxx

@@ -0,0 +1,106 @@
+// Filename: fltMaterial.cxx
+// Created by:  drose (25Aug00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "fltMaterial.h"
+#include "fltRecordReader.h"
+#include "fltRecordWriter.h"
+
+TypeHandle FltMaterial::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltMaterial::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+FltMaterial::
+FltMaterial(FltHeader *header) : FltRecord(header) {
+  _material_index = 0;
+  _flags = 0;
+  _ambient.set(0.0, 0.0, 0.0);
+  _diffuse.set(0.0, 0.0, 0.0);
+  _specular.set(0.0, 0.0, 0.0);
+  _emissive.set(0.0, 0.0, 0.0);
+  _shininess = 0.0;
+  _alpha = 1.0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltMaterial::extract_record
+//       Access: Protected, Virtual
+//  Description: Fills in the information in this record 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 FltMaterial::
+extract_record(FltRecordReader &reader) {
+  if (!FltRecord::extract_record(reader)) {
+    return false;
+  }
+
+  nassertr(reader.get_opcode() == FO_15_material, false);
+  DatagramIterator &iterator = reader.get_iterator();
+
+  _material_index = iterator.get_be_int32();
+  _material_name = iterator.get_fixed_string(12);
+  _flags = iterator.get_be_uint32();
+  _ambient[0] = iterator.get_be_float32();
+  _ambient[1] = iterator.get_be_float32();
+  _ambient[2] = iterator.get_be_float32();
+  _diffuse[0] = iterator.get_be_float32();
+  _diffuse[1] = iterator.get_be_float32();
+  _diffuse[2] = iterator.get_be_float32();
+  _specular[0] = iterator.get_be_float32();
+  _specular[1] = iterator.get_be_float32();
+  _specular[2] = iterator.get_be_float32();
+  _emissive[0] = iterator.get_be_float32();
+  _emissive[1] = iterator.get_be_float32();
+  _emissive[2] = iterator.get_be_float32();
+  _shininess = iterator.get_be_float32();
+  _alpha = iterator.get_be_float32();
+  iterator.skip_bytes(4);
+
+  nassertr(iterator.get_remaining_size() == 0, true);
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltMaterial::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 FltMaterial::
+build_record(FltRecordWriter &writer) const {
+  if (!FltRecord::build_record(writer)) {
+    return false;
+  }
+  
+  writer.set_opcode(FO_15_material);
+  Datagram &datagram = writer.update_datagram();
+
+  datagram.add_be_int32(_material_index);
+  datagram.add_fixed_string(_material_name, 12);
+  datagram.add_be_uint32(_flags);
+  datagram.add_be_float32(_ambient[0]);
+  datagram.add_be_float32(_ambient[1]);
+  datagram.add_be_float32(_ambient[2]);
+  datagram.add_be_float32(_diffuse[0]);
+  datagram.add_be_float32(_diffuse[1]);
+  datagram.add_be_float32(_diffuse[2]);
+  datagram.add_be_float32(_specular[0]);
+  datagram.add_be_float32(_specular[1]);
+  datagram.add_be_float32(_specular[2]);
+  datagram.add_be_float32(_emissive[0]);
+  datagram.add_be_float32(_emissive[1]);
+  datagram.add_be_float32(_emissive[2]);
+  datagram.add_be_float32(_shininess);
+  datagram.add_be_float32(_alpha);
+  datagram.pad_bytes(4);
+
+  return true;
+}

+ 63 - 0
pandatool/src/flt/fltMaterial.h

@@ -0,0 +1,63 @@
+// Filename: fltMaterial.h
+// Created by:  drose (25Aug00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef FLTMATERIAL_H
+#define FLTMATERIAL_H
+
+#include <pandatoolbase.h>
+
+#include "fltRecord.h"
+
+#include <luse.h>
+
+////////////////////////////////////////////////////////////////////
+// 	 Class : FltMaterial
+// Description : Represents a single material in the material palette.
+////////////////////////////////////////////////////////////////////
+class FltMaterial : public FltRecord {
+public:
+  FltMaterial(FltHeader *header);
+
+  enum Flags {
+    F_materials_used    = 0x80000000,
+  };
+
+  int _material_index;
+  string _material_name;
+  unsigned int _flags;
+  RGBColorf _ambient;
+  RGBColorf _diffuse;
+  RGBColorf _specular;
+  RGBColorf _emissive;
+  float _shininess;
+  float _alpha;
+
+protected:
+  virtual bool extract_record(FltRecordReader &reader);
+  virtual bool build_record(FltRecordWriter &writer) const;
+
+public:
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    FltRecord::init_type();
+    register_type(_type_handle, "FltMaterial",
+		  FltRecord::get_class_type());
+  }
+
+private:
+  static TypeHandle _type_handle;
+
+  friend class FltHeader;
+};
+
+#endif
+
+

+ 76 - 0
pandatool/src/flt/fltObject.cxx

@@ -0,0 +1,76 @@
+// Filename: fltObject.cxx
+// Created by:  drose (24Aug00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "fltObject.h"
+#include "fltRecordReader.h"
+#include "fltRecordWriter.h"
+
+TypeHandle FltObject::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltObject::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+FltObject::
+FltObject(FltHeader *header) : FltBeadID(header) {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltObject::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 FltObject::
+extract_record(FltRecordReader &reader) {
+  if (!FltBeadID::extract_record(reader)) {
+    return false;
+  }
+
+  nassertr(reader.get_opcode() == FO_object, false);
+  DatagramIterator &iterator = reader.get_iterator();
+
+  _flags = iterator.get_be_uint32();
+  _relative_priority = iterator.get_be_int16();
+  _transparency = iterator.get_be_int16();
+  _special_id1 = iterator.get_be_int16();
+  _special_id2 = iterator.get_be_int16();
+  _significance = iterator.get_be_int16();
+  iterator.skip_bytes(2);
+
+  nassertr(iterator.get_remaining_size() == 0, true);
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltObject::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 FltObject::
+build_record(FltRecordWriter &writer) const {
+  if (!FltBeadID::build_record(writer)) {
+    return false;
+  }
+
+  writer.set_opcode(FO_object);
+  Datagram &datagram = writer.update_datagram();
+
+  datagram.add_be_uint32(_flags);
+  datagram.add_be_int16(_relative_priority);
+  datagram.add_be_int16(_transparency);
+  datagram.add_be_int16(_special_id1);
+  datagram.add_be_int16(_special_id2);
+  datagram.add_be_int16(_significance);
+  datagram.pad_bytes(2);
+
+  return true;
+}

+ 60 - 0
pandatool/src/flt/fltObject.h

@@ -0,0 +1,60 @@
+// Filename: fltObject.h
+// Created by:  drose (24Aug00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef FLTOBJECT_H
+#define FLTOBJECT_H
+
+#include <pandatoolbase.h>
+
+#include "fltBeadID.h"
+
+////////////////////////////////////////////////////////////////////
+// 	 Class : FltObject
+// Description : The main objecting bead of the flt file.
+////////////////////////////////////////////////////////////////////
+class FltObject : public FltBeadID {
+public:
+  FltObject(FltHeader *header);
+
+  enum Flags {
+    F_no_daylight    = 0x80000000,
+    F_no_dusk        = 0x40000000,
+    F_no_night       = 0x20000000,
+    F_no_illuminate  = 0x10000000,
+    F_flat_shaded    = 0x08000000,
+    F_shadow_object  = 0x04000000,
+  };
+
+  unsigned int _flags;
+  int _relative_priority;
+  int _transparency;
+  int _special_id1, _special_id2;
+  int _significance;
+
+protected:
+  virtual bool extract_record(FltRecordReader &reader);
+  virtual bool build_record(FltRecordWriter &writer) const;
+
+public:
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    FltBeadID::init_type();
+    register_type(_type_handle, "FltObject",
+		  FltBeadID::get_class_type());
+  }
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#endif
+
+

+ 253 - 0
pandatool/src/flt/fltOpcode.cxx

@@ -0,0 +1,253 @@
+// Filename: fltOpcode.cxx
+// Created by:  drose (24Aug00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "fltOpcode.h"
+
+ostream &
+operator << (ostream &out, FltOpcode opcode) {
+  switch (opcode) {
+  case FO_none:
+    return out << "null opcode";
+
+  case FO_header:
+    return out << "header";
+
+  case FO_group:
+    return out << "group";
+
+  case FO_OB_scale:
+  case FO_OB_scale2:
+  case FO_OB_scale3:
+    return out << "(obsolete) scale";
+
+  case FO_object:
+    return out << "object";
+
+  case FO_face:
+    return out << "face";
+
+  case FO_OB_vertex_i:
+    return out << "(obsolete) vertex with ID";
+
+  case FO_OB_short_vertex:
+    return out << "(obsolete) short vertex";
+
+  case FO_OB_vertex_c:
+    return out << "(obsolete) vertex with color";
+
+  case FO_OB_vertex_cn:
+    return out << "(obsolete) vertex with color and normal";
+
+  case FO_push:
+    return out << "push";
+
+  case FO_pop:
+    return out << "pop";
+
+  case FO_OB_translate:
+  case FO_OB_translate2:
+  case FO_OB_translate3:
+    return out << "(obsolete) translate";
+
+  case FO_OB_dof:
+    return out << "(obsolete) degree-of-freedom";
+
+  case FO_dof:
+    return out << "degree-of-freedom";
+
+  case FO_OB_instance_ref:
+    return out << "(obsolete) instance reference";
+
+  case FO_OB_instance:
+    return out << "(obsolete) instance definition";
+
+  case FO_push_face:
+    return out << "push subface";
+
+  case FO_pop_face:
+    return out << "pop subface";
+
+  case FO_comment:
+    return out << "comment";
+
+  case FO_color_palette:
+    return out << "color palette";
+
+  case FO_long_id:
+    return out << "long ID";
+
+  case FO_transform_matrix:
+    return out << "transformation matrix";
+
+  case FO_OB_rotate_point:
+  case FO_OB_rotate_point2:
+    return out << "(obsolete) rotate about point";
+
+  case FO_OB_rotate_edge:
+    return out << "(obsolete) rotate about edge";
+
+  case FO_OB_nu_scale:
+    return out << "(obsolete) non-uniform scale";
+
+  case FO_OB_rotate_to_point:
+    return out << "(obsolete) rotate to point";
+
+  case FO_OB_put:
+    return out << "(obsolete) put";
+
+  case FO_OB_bounding_box:
+    return out << "(obsolete) bounding box";
+
+  case FO_vector:
+    return out << "vector";
+
+  case FO_bsp:
+    return out << "BSP";
+
+  case FO_replicate:
+    return out << "replicate";
+
+  case FO_instance_ref:
+    return out << "instance reference";
+
+  case FO_instance:
+    return out << "instance definition";
+
+  case FO_external_ref:
+    return out << "external reference";
+
+  case FO_texture:
+    return out << "texture";
+
+  case FO_OB_eyepoint_palette:
+    return out << "(obsolete) eyepoint palette";
+
+  case FO_14_material_palette:
+    return out << "v14 material palette";
+
+  case FO_vertex_palette:
+    return out << "vertex palette";
+
+  case FO_vertex_c:
+    return out << "vertex with color";
+
+  case FO_vertex_cn:
+    return out << "vertex with color and normal";
+
+  case FO_vertex_cnu:
+    return out << "vertex with color, normal, and uv";
+
+  case FO_vertex_cu:
+    return out << "vertex with color and uv";
+
+  case FO_vertex_list:
+    return out << "vertex list";
+
+  case FO_lod:
+    return out << "LOD";
+
+  case FO_bounding_box:
+    return out << "bounding box";
+
+  case FO_rotate_about_edge:
+    return out << "rotate about edge";
+
+  case FO_translate:
+    return out << "translate";
+
+  case FO_scale:
+    return out << "scale";
+
+  case FO_rotate_about_point:
+    return out << "rotate about point";
+
+  case FO_rotate_and_scale:
+    return out << "rotate and/or scale";
+
+  case FO_put:
+    return out << "put";
+
+  case FO_eyepoint_palette:
+    return out << "eyepoint palette";
+
+  case FO_road_segment:
+    return out << "road segment";
+
+  case FO_road_zone:
+    return out << "road zone";
+
+  case FO_morph_list:
+    return out << "morph vertex list";
+
+  case FO_behavior_palette:
+    return out << "behavior palette";
+
+  case FO_sound:
+    return out << "sound";
+
+  case FO_road_path:
+    return out << "road path";
+
+  case FO_sound_palette:
+    return out << "sound palette";
+
+  case FO_general_matrix:
+    return out << "general matrix";
+
+  case FO_text:
+    return out << "text";
+
+  case FO_switch:
+    return out << "switch";
+
+  case FO_line_style:
+    return out << "line style";
+
+  case FO_clip_region:
+    return out << "clip region";
+
+  case FO_light_source:
+    return out << "light source";
+
+  case FO_light_definition:
+    return out << "light source definition";
+
+  case FO_bounding_sphere:
+    return out << "bounding sphere";
+
+  case FO_bounding_cylinder:
+    return out << "bounding cylinder";
+
+  case FO_bv_center:
+    return out << "bounding volume center";
+
+  case FO_bv_orientation:
+    return out << "bounding volume orientation";
+
+  case FO_texture_map_palette:
+    return out << "texture mapping palette";
+
+  case FO_15_material:
+    return out << "material";
+
+  case FO_color_name_palette:
+    return out << "color name palette";
+
+  case FO_cat:
+    return out << "continuously adaptive terrain";
+
+  case FO_cat_data:
+    return out << "CAT Data";
+
+  case FO_push_attribute:
+    return out << "push attribute";
+
+  case FO_pop_attribute:
+    return out << "pop attribute";
+
+  default:
+    return out << "unknown opcode " << (int)opcode;
+  }
+}

+ 101 - 0
pandatool/src/flt/fltOpcode.h

@@ -0,0 +1,101 @@
+// Filename: fltOpcode.h
+// Created by:  drose (24Aug00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef FLTOPCODE_H
+#define FLTOPCODE_H
+
+#include <pandatoolbase.h>
+
+// Known opcodes, as of the latest version of flt.
+enum FltOpcode {
+  FO_none                = 0,
+  FO_header              = 1,
+  FO_group               = 2,
+  FO_OB_scale            = 3,    // obsolete
+  FO_object              = 4,
+  FO_face                = 5,
+  FO_OB_vertex_i         = 6,    // obsolete
+  FO_OB_short_vertex     = 7,    // obsolete
+  FO_OB_vertex_c         = 8,    // obsolete
+  FO_OB_vertex_cn        = 9,    // obsolete
+  FO_push                = 10,
+  FO_pop                 = 11,
+  FO_OB_translate        = 12,   // obsolete
+  FO_OB_dof              = 13,   // obsolete
+  FO_dof                 = 14,
+  FO_OB_instance_ref     = 16,   // obsolete
+  FO_OB_instance         = 17,   // obsolete
+  FO_push_face           = 19,
+  FO_pop_face            = 20,
+  FO_comment             = 31,
+  FO_color_palette       = 32,
+  FO_long_id             = 33,
+  FO_OB_translate2       = 40,   // obsolete
+  FO_OB_rotate_point     = 41,   // obsolete
+  FO_OB_rotate_edge      = 42,   // obsolete
+  FO_OB_scale2           = 43,   // obsolete
+  FO_OB_translate3       = 44,   // obsolete
+  FO_OB_nu_scale         = 45,   // obsolete
+  FO_OB_rotate_point2    = 46,   // obsolete
+  FO_OB_rotate_to_point  = 47,   // obsolete
+  FO_OB_put              = 48,   // obsolete
+  FO_transform_matrix    = 49,
+  FO_vector              = 50,
+  FO_OB_bounding_box     = 51,   // obsolete
+  FO_bsp                 = 55,
+  FO_replicate           = 60,
+  FO_instance_ref        = 61,
+  FO_instance            = 62,
+  FO_external_ref        = 63,
+  FO_texture             = 64,
+  FO_OB_eyepoint_palette = 65,   // obsolete
+  FO_14_material_palette = 66,
+  FO_vertex_palette      = 67,
+  FO_vertex_c            = 68,
+  FO_vertex_cn           = 69,
+  FO_vertex_cnu          = 70,
+  FO_vertex_cu           = 71,
+  FO_vertex_list         = 72,
+  FO_lod                 = 73,
+  FO_bounding_box        = 74,
+  FO_rotate_about_edge   = 76,
+  FO_OB_scale3           = 77,   // obsolete
+  FO_translate           = 78,
+  FO_scale               = 79,
+  FO_rotate_about_point  = 80,
+  FO_rotate_and_scale    = 81,
+  FO_put                 = 82,
+  FO_eyepoint_palette    = 83,
+  FO_road_segment        = 87,
+  FO_road_zone           = 88,
+  FO_morph_list          = 89,
+  FO_behavior_palette    = 90,
+  FO_sound               = 91,
+  FO_road_path           = 92,
+  FO_sound_palette       = 93,
+  FO_general_matrix      = 94,
+  FO_text                = 95,
+  FO_switch              = 96,
+  FO_line_style          = 97,
+  FO_clip_region         = 98,
+  FO_light_source        = 101,
+  FO_light_definition    = 102,
+  FO_bounding_sphere     = 105,
+  FO_bounding_cylinder   = 106,
+  FO_bv_center           = 108,
+  FO_bv_orientation      = 109,
+  FO_texture_map_palette = 112,
+  FO_15_material         = 113,
+  FO_color_name_palette  = 114,
+  FO_cat                 = 115,
+  FO_cat_data            = 116,
+  FO_push_attribute      = 122,
+  FO_pop_attribute       = 123,
+};
+
+ostream &operator << (ostream &out, FltOpcode opcode);
+
+#endif
+

+ 47 - 0
pandatool/src/flt/fltPackedColor.I

@@ -0,0 +1,47 @@
+// Filename: fltPackedColor.I
+// Created by:  drose (25Aug00)
+// 
+////////////////////////////////////////////////////////////////////
+
+INLINE ostream &
+operator << (ostream &out, const FltPackedColor &color) {
+  color.output(out);
+  return out;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltPackedColor::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE FltPackedColor::
+FltPackedColor() {
+  _a = 0;
+  _b = 0;
+  _g = 0;
+  _r = 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltPackedColor::get_color
+//       Access: Public
+//  Description: Returns the four-component color as a Colorf, where
+//               each component is in the range [0, 1].
+////////////////////////////////////////////////////////////////////
+INLINE Colorf FltPackedColor::
+get_color() const {
+  return Colorf(_r / 255.0, _g / 255.0, _b / 255.0, _a / 255.0);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltPackedColor::get_rgb
+//       Access: Public
+//  Description: Returns the three-component color as an RGBColorf
+//               (ignoring the alpha component), where each component
+//               is in the range [0, 1].
+////////////////////////////////////////////////////////////////////
+INLINE RGBColorf FltPackedColor::
+get_rgb() const {
+  return RGBColorf(_r / 255.0, _g / 255.0, _b / 255.0);
+}

+ 52 - 0
pandatool/src/flt/fltPackedColor.cxx

@@ -0,0 +1,52 @@
+// Filename: fltPackedColor.cxx
+// Created by:  drose (25Aug00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "fltPackedColor.h"
+#include "fltRecordReader.h"
+#include "fltRecordWriter.h"
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltPackedColor::output
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void FltPackedColor::
+output(ostream &out) const {
+  out << "(" << _r << " " << _g << " " << _b << " " << _a << ")";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltPackedColor::extract_record
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+bool FltPackedColor::
+extract_record(FltRecordReader &reader) {
+  DatagramIterator &iterator = reader.get_iterator();
+
+  _a = iterator.get_uint8();
+  _g = iterator.get_uint8();
+  _b = iterator.get_uint8();
+  _r = iterator.get_uint8();
+
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltPackedColor::build_record
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+bool FltPackedColor::
+build_record(FltRecordWriter &writer) const {
+  Datagram &datagram = writer.update_datagram();
+
+  datagram.add_uint8(_a);
+  datagram.add_uint8(_g);
+  datagram.add_uint8(_b);
+  datagram.add_uint8(_r);
+
+  return true;
+}

+ 46 - 0
pandatool/src/flt/fltPackedColor.h

@@ -0,0 +1,46 @@
+// Filename: fltPackedColor.h
+// Created by:  drose (25Aug00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef FLTPACKEDCOLOR_H
+#define FLTPACKEDCOLOR_H
+
+#include <pandatoolbase.h>
+
+#include <luse.h>
+
+class FltRecordReader;
+class FltRecordWriter;
+
+////////////////////////////////////////////////////////////////////
+// 	 Class : FltPackedColor
+// Description : A packed color record, A, B, G, R.  This appears, for
+//               instance, within a face bead.
+////////////////////////////////////////////////////////////////////
+class FltPackedColor {
+public:
+  INLINE FltPackedColor();
+
+  INLINE Colorf get_color() const;
+  INLINE RGBColorf get_rgb() const;
+
+  void output(ostream &out) const;
+  bool extract_record(FltRecordReader &reader);
+  bool build_record(FltRecordWriter &writer) const;
+
+public:
+  int _a;
+  int _b;
+  int _g;
+  int _r;
+};
+
+INLINE ostream &operator << (ostream &out, const FltPackedColor &color);
+
+#include "fltPackedColor.I"
+
+#endif
+
+
+  

+ 10 - 0
pandatool/src/flt/fltRecord.I

@@ -0,0 +1,10 @@
+// Filename: fltRecord.I
+// Created by:  drose (24Aug00)
+// 
+////////////////////////////////////////////////////////////////////
+
+INLINE ostream &
+operator << (ostream &out, const FltRecord &record) {
+  record.output(out);
+  return out;
+}

+ 648 - 0
pandatool/src/flt/fltRecord.cxx

@@ -0,0 +1,648 @@
+// Filename: fltRecord.cxx
+// Created by:  drose (24Aug00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "fltRecord.h"
+#include "fltRecordReader.h"
+#include "fltRecordWriter.h"
+#include "fltHeader.h"
+#include "fltGroup.h"
+#include "fltObject.h"
+#include "fltFace.h"
+#include "fltVertexList.h"
+#include "fltLOD.h"
+#include "fltInstanceDefinition.h"
+#include "fltInstanceRef.h"
+#include "fltUnsupportedRecord.h"
+#include "fltExternalReference.h"
+
+#include <indent.h>
+
+TypeHandle FltRecord::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltRecord::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+FltRecord::
+FltRecord(FltHeader *header) :
+  _header(header)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltRecord::Destructor
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+FltRecord::
+~FltRecord() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltRecord::get_num_children
+//       Access: Public
+//  Description: Returns the number of child records of this record.  This
+//               reflects the normal scene graph hierarchy.
+////////////////////////////////////////////////////////////////////
+int FltRecord::
+get_num_children() const {
+  return _children.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltRecord::get_child
+//       Access: Public
+//  Description: Returns the nth child of this record.
+////////////////////////////////////////////////////////////////////
+FltRecord *FltRecord::
+get_child(int n) const {
+  nassertr(n >= 0 && n < (int)_children.size(), (FltRecord *)NULL);
+  return _children[n];
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltRecord::clear_children
+//       Access: Public
+//  Description: Removes all children from this record.
+////////////////////////////////////////////////////////////////////
+void FltRecord::
+clear_children() {
+  _children.clear();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltRecord::add_child
+//       Access: Public
+//  Description: Adds a new child to the end of the list of children
+//               for this record.
+////////////////////////////////////////////////////////////////////
+void FltRecord::
+add_child(FltRecord *child) {
+  _children.push_back(child);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltRecord::get_num_subfaces
+//       Access: Public
+//  Description: Returns the number of subface records of this record.
+//               Normally, subfaces will only be present on object
+//               records, although it is logically possible for them to
+//               appear anywhere.
+////////////////////////////////////////////////////////////////////
+int FltRecord::
+get_num_subfaces() const {
+  return _subfaces.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltRecord::get_subface
+//       Access: Public
+//  Description: Returns the nth subface of this record.
+////////////////////////////////////////////////////////////////////
+FltRecord *FltRecord::
+get_subface(int n) const {
+  nassertr(n >= 0 && n < (int)_subfaces.size(), (FltRecord *)NULL);
+  return _subfaces[n];
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltRecord::clear_subfaces
+//       Access: Public
+//  Description: Removes all subfaces from this record.
+////////////////////////////////////////////////////////////////////
+void FltRecord::
+clear_subfaces() {
+  _subfaces.clear();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltRecord::add_subface
+//       Access: Public
+//  Description: Adds a new subface to the end of the list of subfaces
+//               for this record.
+////////////////////////////////////////////////////////////////////
+void FltRecord::
+add_subface(FltRecord *subface) {
+  _subfaces.push_back(subface);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltRecord::get_num_ancillary
+//       Access: Public
+//  Description: Returns the number of unsupported ancillary records
+//               of this record.  These are ancillary records that
+//               appeared following this record in the flt file but that
+//               aren't directly understood by the flt
+//               loader--normally, an ancillary record is examined and
+//               decoded on the spot, and no pointer to it is kept.
+////////////////////////////////////////////////////////////////////
+int FltRecord::
+get_num_ancillary() const {
+  return _ancillary.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltRecord::get_ancillary
+//       Access: Public
+//  Description: Returns the nth unsupported ancillary record of this
+//               record.  See get_num_ancillary().
+////////////////////////////////////////////////////////////////////
+FltRecord *FltRecord::
+get_ancillary(int n) const {
+  nassertr(n >= 0 && n < (int)_ancillary.size(), (FltRecord *)NULL);
+  return _ancillary[n];
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltRecord::clear_ancillary
+//       Access: Public
+//  Description: Removes all unsupported ancillary records from this
+//               record.  See get_num_ancillary().
+////////////////////////////////////////////////////////////////////
+void FltRecord::
+clear_ancillary() {
+  _ancillary.clear();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltRecord::add_ancillary
+//       Access: Public
+//  Description: Adds a new unsupported ancillary record to the end of
+//               the list of ancillary records for this record.  This
+//               record will be written to the flt file following this
+//               record, without attempting to understand what is in it.
+//
+//               Normally, there is no reason to use this function; if
+//               the data stored in the FltRecord requires one or more
+//               ancillary record, the appropriate records will
+//               automatically be generated when the record is written.
+//               This function is only required to output a record
+//               whose type is not supported by the flt loader.  But
+//               it would be better to extend the flt loader to know
+//               about this new kind of data record.
+////////////////////////////////////////////////////////////////////
+void FltRecord::
+add_ancillary(FltRecord *ancillary) {
+  _ancillary.push_back(ancillary);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltRecord::has_comment
+//       Access: Public
+//  Description: Returns true if this record has a nonempty comment,
+//               false otherwise.
+////////////////////////////////////////////////////////////////////
+bool FltRecord::
+has_comment() const {
+  return !_comment.empty();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltRecord::get_comment
+//       Access: Public
+//  Description: Retrieves the comment for this record.
+////////////////////////////////////////////////////////////////////
+const string &FltRecord::
+get_comment() const {
+  return _comment;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltRecord::clear_comment
+//       Access: Public
+//  Description: Removes the comment for this record.
+////////////////////////////////////////////////////////////////////
+void FltRecord::
+clear_comment() {
+  _comment = "";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltRecord::set_comment
+//       Access: Public
+//  Description: Changes the comment for this record.
+////////////////////////////////////////////////////////////////////
+void FltRecord::
+set_comment(const string &comment) {
+  _comment = comment;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltRecord::output
+//       Access: Public
+//  Description: Writes a quick one-line description of the record, but
+//               not its children.  This is a human-readable
+//               description, primarily for debugging; to write a flt
+//               file, use FltHeader::write_flt().
+////////////////////////////////////////////////////////////////////
+void FltRecord::
+output(ostream &out) const {
+  out << get_type();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltRecord::write
+//       Access: Public
+//  Description: Writes a multiple-line description of the record and
+//               all of its children.  This is a human-readable
+//               description, primarily for debugging; to write a flt
+//               file, use FltHeader::write_flt().
+////////////////////////////////////////////////////////////////////
+void FltRecord::
+write(ostream &out, int indent_level) const {
+  indent(out, indent_level) << *this;
+  write_children(out, indent_level);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltRecord::write_children
+//       Access: Protected
+//  Description: Assuming the current write position has been left at
+//               the end of the last line of the record description,
+//               writes out the list of children.
+////////////////////////////////////////////////////////////////////
+void FltRecord::
+write_children(ostream &out, int indent_level) const {
+  if (!_ancillary.empty()) {
+    out << " + " << _ancillary.size() << " ancillary";
+  }
+  if (!_subfaces.empty()) {
+    out << " [";
+    Records::const_iterator ci;
+    for (ci = _subfaces.begin(); ci != _subfaces.end(); ++ci) {
+      out << " " << *(*ci);
+    }
+    out << " ]";
+  }
+  if (!_children.empty()) {
+    out << " {\n";
+    Records::const_iterator ci;
+    for (ci = _children.begin(); ci != _children.end(); ++ci) {
+      (*ci)->write(out, indent_level + 2);
+    }
+    indent(out, indent_level) << "}\n";
+  } else {
+    out << "\n";
+  }
+}
+
+  /*
+  virtual void write(ostream &out) const;
+  virtual void build_record(Datagram &datagram) const;
+  */
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltRecord::is_ancillary
+//       Access: Protected, Static
+//  Description: Returns true if the indicated opcode corresponds to
+//               an ancillary record type, false otherwise.  In
+//               general, this function is used to identify ancillary
+//               records that are not presently supported by the
+//               FltReader; these will be ignored.  Normally,
+//               ancillary records will be detected and processed by
+//               extract_ancillary().
+////////////////////////////////////////////////////////////////////
+bool FltRecord::
+is_ancillary(FltOpcode opcode) {
+  switch (opcode) {
+  case FO_comment:
+  case FO_long_id:
+  case FO_replicate:
+  case FO_road_zone:
+  case FO_transform_matrix:
+  case FO_rotate_about_edge:
+  case FO_translate:
+  case FO_scale:
+  case FO_rotate_about_point:
+  case FO_rotate_and_scale:
+  case FO_put:
+  case FO_general_matrix:
+  case FO_vector:
+  case FO_bounding_box:
+  case FO_bounding_sphere:
+  case FO_bounding_cylinder:
+  case FO_bv_center:
+  case FO_bv_orientation:
+  case FO_vertex_palette:
+  case FO_vertex_c:
+  case FO_vertex_cn:
+  case FO_vertex_cnu:
+  case FO_vertex_cu:
+  case FO_color_palette:
+  case FO_color_name_palette:
+  case FO_15_material:
+  case FO_texture:
+  case FO_eyepoint_palette:
+  case FO_light_definition:
+    return true;
+
+  case FO_header:
+  case FO_group:
+  case FO_object:
+  case FO_face:
+  case FO_dof:
+  case FO_vertex_list:
+  case FO_morph_list:
+  case FO_bsp:
+  case FO_external_ref:
+  case FO_lod:
+  case FO_sound:
+  case FO_light_source:
+  case FO_road_segment:
+  case FO_road_path:
+  case FO_clip_region:
+  case FO_text:
+  case FO_switch:
+    return false;
+
+  case FO_push:
+  case FO_pop:
+  case FO_push_face:
+  case FO_pop_face:
+  case FO_push_attribute:
+  case FO_pop_attribute:
+  case FO_instance:
+  case FO_instance_ref:
+    return false;
+
+  default:
+    nout << "Don't know whether " << opcode << " is ancillary.\n";
+    return false;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltRecord::create_new_record
+//       Access: Protected
+//  Description: Creates a new FltRecord corresponding to the opcode.
+//               If the opcode is unknown, creates a
+//               FltUnsupportedRecord.
+////////////////////////////////////////////////////////////////////
+FltRecord *FltRecord::
+create_new_record(FltOpcode opcode) const {
+  switch (opcode) {
+  case FO_group:
+    return new FltGroup(_header);
+
+  case FO_object:
+    return new FltObject(_header);
+
+  case FO_face:
+    return new FltFace(_header);
+
+  case FO_vertex_list:
+    return new FltVertexList(_header);
+
+  case FO_lod:
+    return new FltLOD(_header);
+
+  case FO_instance:
+    return new FltInstanceDefinition(_header);
+
+  case FO_instance_ref:
+    return new FltInstanceRef(_header);
+
+  case FO_external_ref:
+    return new FltExternalReference(_header);
+
+  default:
+    nout << "Unsupported record " << opcode << "\n";
+    return new FltUnsupportedRecord(_header);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltRecord::read_record_and_children
+//       Access: Protected
+//  Description: Extracts this record information from the current
+//               record presented in the reader, then advances the
+//               reader and continues to read any children, if
+//               present.  On return, the reader is position on the
+//               next sibling record to this record.
+//
+//               Returns FE_ok if successful, otherwise on error.
+////////////////////////////////////////////////////////////////////
+FltError FltRecord::
+read_record_and_children(FltRecordReader &reader) {
+  if (!extract_record(reader)) {
+    nout << "Could not extract record for " << *this << "\n";
+    return FE_invalid_record;
+  }
+  FltError result = reader.advance();
+  if (result == FE_end_of_file) {
+    return FE_ok;
+  } else if (result != FE_ok) {
+    return result;
+  }
+
+  while (true) {
+    if (extract_ancillary(reader)) {
+      // Ok, a known ancillary record.  Fine.
+
+    } else if (reader.get_opcode() == FO_push) {
+      // A push begins a new list of children.
+      result = reader.advance();
+      if (result != FE_ok) {
+	return result;
+      }
+      
+      while (reader.get_opcode() != FO_pop) {
+	PT(FltRecord) child = create_new_record(reader.get_opcode());
+	FltError result = child->read_record_and_children(reader);
+	if (result != FE_ok) {
+	  return result;
+	}
+
+	if (child->is_of_type(FltInstanceDefinition::get_class_type())) {
+	  // A special case for an instance definition.  These
+	  // shouldn't appear in the hierarchy, but should instead be
+	  // added directly to the header.
+	  _header->add_instance(DCAST(FltInstanceDefinition, child));
+
+	} else {
+	  add_child(child);
+	}
+
+	if (reader.eof() || reader.error()) {
+	  return FE_end_of_file;
+	}
+      }
+
+    } else if (reader.get_opcode() == FO_push_face) {
+      // A push subface begins a new list of subfaces.
+      result = reader.advance();
+      if (result != FE_ok) {
+	return result;
+      }
+      
+      while (reader.get_opcode() != FO_pop_face) {
+	PT(FltRecord) subface = create_new_record(reader.get_opcode());
+	FltError result = subface->read_record_and_children(reader);
+	if (result != FE_ok) {
+	  return result;
+	}
+	add_subface(subface);
+	if (reader.eof() || reader.error()) {
+	  return FE_end_of_file;
+	}
+      }
+
+    } else if (is_ancillary(reader.get_opcode())) {
+      // An unsupported ancillary record.  Skip it.
+      PT(FltRecord) ancillary = create_new_record(reader.get_opcode());
+      ancillary->extract_record(reader);
+      _ancillary.push_back(ancillary);
+
+    } else {
+      // None of the above: we're done.
+      return FE_ok;
+    }
+
+    // Skip to the next record.  If that's the end, fine.
+    result = reader.advance();
+    if (result == FE_end_of_file) {
+      return FE_ok;
+    } else if (result != FE_ok) {
+      return result;
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltRecord::extract_record
+//       Access: Protected, Virtual
+//  Description: Fills in the information in this record 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 FltRecord::
+extract_record(FltRecordReader &) {
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltRecord::extract_ancillary
+//       Access: Protected, Virtual
+//  Description: Checks whether the given record, which follows this
+//               record sequentially in the file, is an ancillary record
+//               of this record.  If it is, extracts the relevant
+//               information and returns true; otherwise, leaves it
+//               alone and returns false.
+////////////////////////////////////////////////////////////////////
+bool FltRecord::
+extract_ancillary(FltRecordReader &reader) {
+  if (reader.get_opcode() == FO_comment) {
+    _comment = reader.get_iterator().get_remaining_bytes();
+    return true;
+  }
+
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltRecord::write_record_and_children
+//       Access: Protected, Virtual
+//  Description: Writes this record out to the flt file, along with all
+//               of its ancillary records and children records.  Returns
+//               FE_ok on success, or something else on error.
+////////////////////////////////////////////////////////////////////
+FltError FltRecord::
+write_record_and_children(FltRecordWriter &writer) const {
+  // First, write the record.
+  if (!build_record(writer)) {
+    return FE_bad_data;
+  }
+
+  FltError result = writer.advance();
+  if (result != FE_ok) {
+    return result;
+  }
+
+  // Then the ancillary data.
+  result = write_ancillary(writer);
+  if (result != FE_ok) {
+    return result;
+  }
+  Records::const_iterator ci;
+  for (ci = _ancillary.begin(); ci != _ancillary.end(); ++ci) {
+    if (!(*ci)->build_record(writer)) {
+      return FE_bad_data;
+    }
+    result = writer.advance();
+    if (result != FE_ok) {
+      return result;
+    }
+  }
+
+  // Any subfaces?
+  if (!_subfaces.empty()) {
+    result = writer.write_record(FO_push_face);
+    if (result != FE_ok) {
+      return result;
+    }
+
+    for (ci = _subfaces.begin(); ci != _subfaces.end(); ++ci) {
+      (*ci)->write_record_and_children(writer);
+    }
+
+    result = writer.write_record(FO_pop_face);
+    if (result != FE_ok) {
+      return result;
+    }
+  }
+
+  // Finally, write all the children.
+  if (!_children.empty()) {
+    result = writer.write_record(FO_push);
+    if (result != FE_ok) {
+      return result;
+    }
+
+    for (ci = _children.begin(); ci != _children.end(); ++ci) {
+      (*ci)->write_record_and_children(writer);
+    }
+
+    result = writer.write_record(FO_pop);
+    if (result != FE_ok) {
+      return result;
+    }
+  }
+
+  return FE_ok;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltRecord::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 FltRecord::
+build_record(FltRecordWriter &) const {
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltRecord::write_ancillary
+//       Access: Protected, Virtual
+//  Description: Writes whatever ancillary records are required for
+//               this record.  Returns FE_ok on success, or something
+//               else if there is some error.
+////////////////////////////////////////////////////////////////////
+FltError FltRecord::
+write_ancillary(FltRecordWriter &writer) const {
+  if (!_comment.empty()) {
+    Datagram dc(_comment);
+    FltError result = writer.write_record(FO_comment, dc);
+    if (result != FE_ok) {
+      return result;
+    }
+  }
+  return FE_ok;
+}

+ 108 - 0
pandatool/src/flt/fltRecord.h

@@ -0,0 +1,108 @@
+// filename: fltRecord.h
+// Created by:  drose (24Aug00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef FLTRECORD_H
+#define FLTRECORD_H
+
+#include <pandatoolbase.h>
+
+#include "fltOpcode.h"
+#include "fltError.h"
+
+#include <typeHandle.h>
+#include <typedReferenceCount.h>
+#include <pointerTo.h>
+
+class FltHeader;
+class FltRecordReader;
+class FltRecordWriter;
+
+////////////////////////////////////////////////////////////////////
+// 	 Class : FltRecord
+// Description : The base class for all kinds of records in a MultiGen
+//               OpenFlight file.  A flt file consists of a hierarchy
+//               of "beads" of various kinds, each of which may be
+//               followed by n ancillary records, written sequentially
+//               to the file.
+////////////////////////////////////////////////////////////////////
+class FltRecord : public TypedReferenceCount {
+public:
+  FltRecord(FltHeader *header);
+  virtual ~FltRecord();
+
+  int get_num_children() const;
+  FltRecord *get_child(int n) const;
+  void clear_children();
+  void add_child(FltRecord *child);
+
+  int get_num_subfaces() const;
+  FltRecord *get_subface(int n) const;
+  void clear_subfaces();
+  void add_subface(FltRecord *subface);
+
+  int get_num_ancillary() const;
+  FltRecord *get_ancillary(int n) const;
+  void clear_ancillary();
+  void add_ancillary(FltRecord *ancillary);
+
+  bool has_comment() const;
+  const string &get_comment() const;
+  void clear_comment();
+  void set_comment(const string &comment);
+
+  virtual void output(ostream &out) const;
+  virtual void write(ostream &out, int indent_level = 0) const;
+
+protected:
+  void write_children(ostream &out, int indent_level) const;
+
+  static bool is_ancillary(FltOpcode opcode);
+
+  FltRecord *create_new_record(FltOpcode opcode) const;
+  FltError read_record_and_children(FltRecordReader &reader);
+  virtual bool extract_record(FltRecordReader &reader);
+  virtual bool extract_ancillary(FltRecordReader &reader);
+
+  virtual FltError write_record_and_children(FltRecordWriter &writer) const;
+  virtual bool build_record(FltRecordWriter &writer) const;
+  virtual FltError write_ancillary(FltRecordWriter &writer) const;
+
+protected:
+  FltHeader *_header;
+
+private:
+  typedef vector<PT(FltRecord)> Records;
+  Records _children;
+  Records _subfaces;
+  Records _ancillary;
+
+  string _comment;
+
+
+public:
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    TypedReferenceCount::init_type();
+    register_type(_type_handle, "FltRecord",
+		  TypedReferenceCount::get_class_type());
+  }
+
+private:
+  static TypeHandle _type_handle;
+};
+
+INLINE ostream &operator << (ostream &out, const FltRecord &record);
+
+#include "fltRecord.I"
+
+#endif
+
+

+ 176 - 0
pandatool/src/flt/fltRecordReader.cxx

@@ -0,0 +1,176 @@
+// Filename: fltRecordReader.cxx
+// Created by:  drose (24Aug00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "fltRecordReader.h"
+
+#include <datagramIterator.h>
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltRecordReader::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+FltRecordReader::
+FltRecordReader(istream &in) :
+  _in(in)
+{
+  _opcode = FO_none;
+  _record_length = 0;
+  _iterator = (DatagramIterator *)NULL;
+  _state = S_begin;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltRecordReader::Destructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+FltRecordReader::
+~FltRecordReader() {
+  if (_iterator != (DatagramIterator *)NULL) {
+    delete _iterator;
+    _iterator = (DatagramIterator *)NULL;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltRecordReader::get_opcode
+//       Access: Public
+//  Description: Returns the opcode associated with the current
+//               record.
+////////////////////////////////////////////////////////////////////
+FltOpcode FltRecordReader::
+get_opcode() const {
+  nassertr(_state == S_normal, FO_none);
+  return _opcode;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltRecordReader::get_iterator
+//       Access: Public
+//  Description: Returns an iterator suitable for extracting data from
+//               the current record.
+////////////////////////////////////////////////////////////////////
+DatagramIterator &FltRecordReader::
+get_iterator() {
+  nassertr(_state == S_normal, *_iterator);
+  return *_iterator;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltRecordReader::get_datagram
+//       Access: Public
+//  Description: Returns the datagram representing the entire record,
+//               less the four-byte header.
+////////////////////////////////////////////////////////////////////
+const Datagram &FltRecordReader::
+get_datagram() {
+#ifndef NDEBUG
+  static Datagram bogus_datagram;
+  nassertr(_state == S_normal, bogus_datagram);
+#endif
+  return _iterator->get_datagram();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltRecordReader::get_record_length
+//       Access: Public
+//  Description: Returns the entire length of the record, including
+//               the four-byte header.
+////////////////////////////////////////////////////////////////////
+int FltRecordReader::
+get_record_length() const {
+  return _record_length;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltRecordReader::advance
+//       Access: Public
+//  Description: Extracts the next record from the file.  Returns true
+//               if there is another record, or false if the end of
+//               file has been reached.
+////////////////////////////////////////////////////////////////////
+FltError FltRecordReader::
+advance() {
+  if (_state == S_eof) {
+    return FE_end_of_file;
+  }
+  if (_state == S_error) {
+    return FE_read_error;
+  }
+  if (_iterator != (DatagramIterator *)NULL) {
+    delete _iterator;
+    _iterator = (DatagramIterator *)NULL;
+  }
+
+  // Get the first four bytes of the record.  This will be the opcode
+  // and length.
+  static const int header_size = 4;
+  char bytes[header_size];
+  _in.read(bytes, header_size);
+
+  if (_in.eof()) {
+    _state = S_eof;
+    return FE_end_of_file;
+
+  } else if (_in.fail()) {
+    _state = S_error;
+    return FE_read_error;
+  }
+
+  // Now extract out the opcode and length.
+  Datagram dg(bytes, header_size);
+  DatagramIterator dgi(dg);
+  _opcode = (FltOpcode)dgi.get_be_int16();
+  _record_length = dgi.get_be_uint16();
+
+  //  cerr << "Reading " << _opcode << " of length " << _record_length << "\n";
+
+  // And now read the full record based on the length.
+  int length = _record_length - header_size;
+  char *buffer = new char[length];
+  _in.read(buffer, length);
+  _datagram = Datagram(buffer, length);
+  delete[] buffer;
+
+  if (_in.eof()) {
+    _state = S_eof;
+    return FE_end_of_file;
+  }
+
+  if (_in.fail()) {
+    _state = S_error;
+    return FE_read_error;
+  }
+
+  // Finally, create a new iterator to read this record.
+  _iterator = new DatagramIterator(_datagram);
+  _state = S_normal;
+
+  return FE_ok;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltRecordReader::eof
+//       Access: Public
+//  Description: Returns true if end-of-file has been reached without
+//               error.
+////////////////////////////////////////////////////////////////////
+bool FltRecordReader::
+eof() const {
+  return _state == S_eof;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltRecordReader::error
+//       Access: Public
+//  Description: Returns true if some error has been encountered while
+//               reading (for instance, a truncated file).
+////////////////////////////////////////////////////////////////////
+bool FltRecordReader::
+error() const {
+  return _state == S_error;
+}
+

+ 58 - 0
pandatool/src/flt/fltRecordReader.h

@@ -0,0 +1,58 @@
+// Filename: fltRecordReader.h
+// Created by:  drose (24Aug00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef FLTRECORDREADER_H
+#define FLTRECORDREADER_H
+
+#include <pandatoolbase.h>
+
+#include "fltOpcode.h"
+#include "fltError.h"
+
+#include <datagram.h>
+#include <datagramIterator.h>
+
+////////////////////////////////////////////////////////////////////
+// 	 Class : FltRecordReader
+// Description : This class turns an istream into a sequence of
+//               FltRecords by reading a sequence of Datagrams and
+//               extracting the opcode from each one.  It remembers
+//               where it is in the file and what the current record
+//               is.
+////////////////////////////////////////////////////////////////////
+class FltRecordReader {
+public:
+  FltRecordReader(istream &in);
+  ~FltRecordReader();
+
+  FltOpcode get_opcode() const;
+  DatagramIterator &get_iterator();
+  const Datagram &get_datagram();
+  int get_record_length() const;
+
+  FltError advance();
+
+  bool eof() const;
+  bool error() const;
+
+private:
+  istream &_in;
+  Datagram _datagram;
+  FltOpcode _opcode;
+  int _record_length;
+  DatagramIterator *_iterator;
+
+  enum State {
+    S_begin,
+    S_normal,
+    S_eof,
+    S_error
+  };
+  State _state;
+};
+
+#endif
+
+

+ 140 - 0
pandatool/src/flt/fltRecordWriter.cxx

@@ -0,0 +1,140 @@
+// Filename: fltRecordWriter.cxx
+// Created by:  drose (24Aug00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "fltRecordWriter.h"
+#include "fltInstanceDefinition.h"
+#include "fltHeader.h"
+
+#include <datagram.h>
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltRecordWriter::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+FltRecordWriter::
+FltRecordWriter(ostream &out) :
+  _out(out)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltRecordWriter::Destructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+FltRecordWriter::
+~FltRecordWriter() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltRecordWriter::set_opcode
+//       Access: Public
+//  Description: Sets the opcode associated with the current record.
+////////////////////////////////////////////////////////////////////
+void FltRecordWriter::
+set_opcode(FltOpcode opcode) {
+  _opcode = opcode;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltRecordWriter::set_datagram
+//       Access: Public
+//  Description: Sets the datagram that will be written when advance()
+//               is called.
+////////////////////////////////////////////////////////////////////
+void FltRecordWriter::
+set_datagram(const Datagram &datagram) {
+  _datagram = datagram;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltRecordWriter::update_datagram
+//       Access: Public
+//  Description: Returns a modifiable reference to the datagram
+//               associated with the current record.  This datagram
+//               should then be stuffed with data corresponding to the
+//               data in the record, in preparation for calling
+//               advance() to write the data.
+////////////////////////////////////////////////////////////////////
+Datagram &FltRecordWriter::
+update_datagram() {
+  return _datagram;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltRecordWriter::advance
+//       Access: Public
+//  Description: Writes the current record to the flt file, and resets
+//               the current record to receive new data.  Returns
+//               FE_ok on success, or something else on error.
+////////////////////////////////////////////////////////////////////
+FltError FltRecordWriter::
+advance() {
+  //  cerr << "Writing " << _opcode << " of length " << _datagram.get_length() << "\n";
+
+  // Build a mini-datagram to write the header.
+  static const int header_size = 4;
+
+  Datagram dg;
+  dg.add_be_int16(_opcode);
+  dg.add_be_int16(_datagram.get_length() + header_size);
+
+  nassertr(dg.get_length() == header_size, FE_internal);
+
+  _out.write(dg.get_message().data(), dg.get_length());
+  if (_out.fail()) {
+    return FE_write_error;
+  }
+
+  // Now write the rest of the record.
+  _out.write(_datagram.get_message().data(), _datagram.get_length());
+  if (_out.fail()) {
+    return FE_write_error;
+  }
+
+  _datagram.clear();
+  _opcode = FO_none;
+
+  return FE_ok;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltRecordWriter::write_record
+//       Access: Public
+//  Description: A convenience function to quickly write a simple
+//               record that consists of an opcode and possibly a
+//               datagram.
+////////////////////////////////////////////////////////////////////
+FltError FltRecordWriter::
+write_record(FltOpcode opcode, const Datagram &datagram) {
+  _opcode = opcode;
+  _datagram = datagram;
+  return advance();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltRecordWriter::write_instance_def
+//       Access: Public
+//  Description: Ensures that the given instance definition has
+//               already been written to the file.  If it has not,
+//               writes it now.
+////////////////////////////////////////////////////////////////////
+FltError FltRecordWriter::
+write_instance_def(FltHeader *header, int instance_index) {
+  bool inserted = _instances_written.insert(instance_index).second;
+
+  if (!inserted) {
+    // It's already been written.
+    return FE_ok;
+  }
+
+  FltInstanceDefinition *instance = header->get_instance(instance_index);
+  if (instance == (FltInstanceDefinition *)NULL) {
+    return FE_undefined_instance;
+  }
+
+  return instance->write_record_and_children(*this);
+}

+ 51 - 0
pandatool/src/flt/fltRecordWriter.h

@@ -0,0 +1,51 @@
+// Filename: fltRecordWriter.h
+// Created by:  drose (24Aug00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef FLTRECORDWRITER_H
+#define FLTRECORDWRITER_H
+
+#include <pandatoolbase.h>
+
+#include "fltOpcode.h"
+#include "fltError.h"
+
+#include <datagram.h>
+
+class FltHeader;
+
+////////////////////////////////////////////////////////////////////
+// 	 Class : FltRecordWriter
+// Description : This class writes a sequence of FltRecords to an
+//               ostream, handling opcode and size counts properly.
+////////////////////////////////////////////////////////////////////
+class FltRecordWriter {
+public:
+  FltRecordWriter(ostream &out);
+  ~FltRecordWriter();
+
+  void set_opcode(FltOpcode opcode);
+  const Datagram &get_datagram() const;
+  void set_datagram(const Datagram &datagram);
+  Datagram &update_datagram();
+
+  FltError advance();
+
+  FltError write_record(FltOpcode opcode, 
+			const Datagram &datagram = Datagram());
+
+  FltError write_instance_def(FltHeader *header, int instance_index);
+
+private:
+  ostream &_out;
+  Datagram _datagram;
+  FltOpcode _opcode;
+
+  typedef set<int> Instances;
+  Instances _instances_written;
+};
+
+#endif
+
+

+ 431 - 0
pandatool/src/flt/fltTexture.cxx

@@ -0,0 +1,431 @@
+// Filename: fltTexture.cxx
+// Created by:  drose (25Aug00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "fltTexture.h"
+#include "fltRecordReader.h"
+#include "fltRecordWriter.h"
+#include "fltHeader.h"
+
+TypeHandle FltTexture::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltTexture::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+FltTexture::
+FltTexture(FltHeader *header) : FltRecord(header) {
+  _pattern_index = 0;
+  _x_location = 0;
+  _y_location = 0;
+
+  _num_texels_u = 0;
+  _num_texels_v = 0;
+  _real_world_size_u = 0;
+  _real_world_size_v = 0;
+  _up_vector_x = 0;
+  _up_vector_y = 1;
+  _file_format = FF_none;
+  _min_filter = MN_point;
+  _mag_filter = MG_point;
+  _repeat = RT_repeat;
+  _repeat_u = RT_repeat;
+  _repeat_v = RT_repeat;
+  _modify_flag = 0;
+  _x_pivot_point = 0;
+  _y_pivot_point = 0;
+  _env_type = ET_modulate;
+  _intensity_is_alpha = false;
+  _float_real_world_size_u = 0.0;
+  _float_real_world_size_v = 0.0;
+  _imported_origin_code = 0;
+  _kernel_version = 1520;
+  _internal_format = IF_default;
+  _external_format = EF_default;
+  _use_mipmap_kernel = false;
+  memset(_mipmap_kernel, 0, sizeof(_mipmap_kernel));
+  _use_lod_scale = false;
+  memset(_lod_scale, 0, sizeof(_lod_scale));
+  _clamp = 0.0;
+  _mag_filter_alpha = MG_point;
+  _mag_filter_color = MG_point;
+  _lambert_conic_central_meridian = 0.0;
+  _lambert_conic_upper_latitude = 0.0;
+  _lambert_conic_lower_latitude = 0.0;
+  _use_detail = false;
+  _detail_j = 0;
+  _detail_k = 0;
+  _detail_m = 0;
+  _detail_n = 0;
+  _detail_scramble = 0;
+  _use_tile = false;
+  _tile_lower_left_u = 0.0;
+  _tile_lower_left_v = 0.0;
+  _tile_upper_right_u = 0.0;
+  _tile_upper_right_v = 0.0;
+  _projection = PT_flat_earth;
+  _earth_model = EM_wgs84;
+  _utm_zone = 0;
+  _image_origin = IO_lower_left;
+  _geospecific_points_units = PU_degrees;
+  _geospecific_hemisphere = H_southern;
+  _file_version = 1501;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltTexture::get_texture_filename
+//       Access: Public
+//  Description: Returns the name of the texture image file.  If it
+//               appears to be a relative filename, it will be
+//               converted to the correct full pathname according to
+//               the texture_path specified in the header.
+////////////////////////////////////////////////////////////////////
+Filename FltTexture::
+get_texture_filename() const {
+  Filename file(_filename);
+  file.resolve_filename(_header->get_texture_path());
+  return file;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltTexture::get_attr_filename
+//       Access: Public
+//  Description: Returns the name of the texture's associated .attr
+//               file.  This contains some additional MultiGen
+//               information about the texture parameters.  This is,
+//               of course, just the name of the texture with .attr
+//               appended.
+//
+//               Normally, it won't be necessary to access this file
+//               directly; you can call read_attr_data() or
+//               write_attr_data() to get at the data stored in this
+//               file.  (And read_attr_data() is called automatically
+//               when the Flt file is read in.)
+////////////////////////////////////////////////////////////////////
+Filename FltTexture::
+get_attr_filename() const {
+  string texture_filename = get_texture_filename();
+  return Filename::binary_filename(texture_filename + ".attr");
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltTexture::read_attr_data
+//       Access: Public
+//  Description: Opens up the texture's .attr file and reads its data
+//               into the extra FltTexture fields.  This is normally
+//               performed automatically when the Flt file is read
+//               from disk.
+////////////////////////////////////////////////////////////////////
+FltError FltTexture::
+read_attr_data() {
+  Filename attr_filename = get_attr_filename();
+
+  ifstream attr;
+  if (!attr_filename.open_read(attr)) {
+    return FE_could_not_open;
+  }
+
+  // Determine the file's size so we can read it all into one big
+  // datagram.
+  attr.seekg(0, ios::end);
+  if (attr.fail()) {
+    return FE_read_error;
+  }
+  streampos length = attr.tellg();
+
+  char *buffer = new char[length];
+
+  attr.seekg(0, ios::beg);
+  attr.read(buffer, length);
+  if (attr.fail()) {
+    return FE_read_error;
+  }
+
+  Datagram datagram(buffer, length);
+  delete[] buffer;
+
+  return unpack_attr(datagram);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltTexture::write_attr_data
+//       Access: Public
+//  Description: Writes the texture's .attr file.  This may or may
+//               not be performed automatically, according to the
+//               setting of FltHeader::set_auto_attr_update().
+////////////////////////////////////////////////////////////////////
+FltError FltTexture::
+write_attr_data() const {
+  Datagram datagram;
+  FltError result = pack_attr(datagram);
+  if (result != FE_ok) {
+    return result;
+  }
+
+  Filename attr_filename = get_attr_filename();
+
+  ofstream attr;
+  if (!attr_filename.open_write(attr)) {
+    return FE_could_not_open;
+  }
+
+  attr.write((const char *)datagram.get_data(), datagram.get_length());
+  if (attr.fail()) {
+    return FE_write_error;
+  }
+  return FE_ok;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltTexture::extract_record
+//       Access: Protected, Virtual
+//  Description: Fills in the information in this record 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 FltTexture::
+extract_record(FltRecordReader &reader) {
+  if (!FltRecord::extract_record(reader)) {
+    return false;
+  }
+
+  nassertr(reader.get_opcode() == FO_texture, false);
+  DatagramIterator &iterator = reader.get_iterator();
+
+  _filename = iterator.get_fixed_string(200);
+  _pattern_index = iterator.get_be_int32();
+  _x_location = iterator.get_be_int32();
+  _y_location = iterator.get_be_int32();
+
+  if (read_attr_data() != FE_ok) {
+    nout << "Unable to read attribute file " << get_attr_filename() << "\n";
+  }
+
+  nassertr(iterator.get_remaining_size() == 0, true);
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltTexture::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 FltTexture::
+build_record(FltRecordWriter &writer) const {
+  if (!FltRecord::build_record(writer)) {
+    return false;
+  }
+  
+  writer.set_opcode(FO_texture);
+  Datagram &datagram = writer.update_datagram();
+
+  datagram.add_fixed_string(_filename, 200);
+  datagram.add_be_int32(_pattern_index);
+  datagram.add_be_int32(_x_location);
+  datagram.add_be_int32(_y_location);
+
+  if (_header->get_auto_attr_update() == FltHeader::AU_always ||
+      (_header->get_auto_attr_update() == FltHeader::AU_if_missing &&
+       !get_attr_filename().exists())) {
+    if (write_attr_data() != FE_ok) {
+      nout << "Unable to write attribute file " << get_attr_filename() << "\n";
+    }
+  }
+
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltTexture::unpack_attr
+//       Access: Private
+//  Description: Reads the data from the attribute file.
+////////////////////////////////////////////////////////////////////
+FltError FltTexture::
+unpack_attr(const Datagram &datagram) {
+  DatagramIterator iterator(datagram);
+
+  _num_texels_u = iterator.get_be_int32();
+  _num_texels_v = iterator.get_be_int32();
+  _real_world_size_u = iterator.get_be_int32();
+  _real_world_size_v = iterator.get_be_int32();
+  _up_vector_x = iterator.get_be_int32();
+  _up_vector_y = iterator.get_be_int32();
+  _file_format = (FileFormat)iterator.get_be_int32();
+  _min_filter = (Minification)iterator.get_be_int32();
+  _mag_filter = (Magnification)iterator.get_be_int32();
+  _repeat = (RepeatType)iterator.get_be_int32();
+  _repeat_u = (RepeatType)iterator.get_be_int32();
+  _repeat_v = (RepeatType)iterator.get_be_int32();
+  _modify_flag = iterator.get_be_int32();
+  _x_pivot_point = iterator.get_be_int32();
+  _y_pivot_point = iterator.get_be_int32();
+  _env_type = (EnvironmentType)iterator.get_be_int32();
+  _intensity_is_alpha = (iterator.get_be_int32() != 0);
+  iterator.skip_bytes(4 * 8);
+  iterator.skip_bytes(4);  // Undocumented padding.
+  _float_real_world_size_u = iterator.get_be_float64();
+  _float_real_world_size_v = iterator.get_be_float64();
+  _imported_origin_code = iterator.get_be_int32();
+  _kernel_version = iterator.get_be_int32();
+  _internal_format = (InternalFormat)iterator.get_be_int32();
+  _external_format = (ExternalFormat)iterator.get_be_int32();
+  _use_mipmap_kernel = (iterator.get_be_int32() != 0);
+  int i;
+  for (i = 0; i < 8; i++) {
+    _mipmap_kernel[i] = iterator.get_be_float32();
+  }
+  _use_lod_scale = (iterator.get_be_int32() != 0);
+  for (i = 0; i < 8; i++) {
+    _lod_scale[i]._lod = iterator.get_be_float32();
+    _lod_scale[i]._scale = iterator.get_be_float32();
+  }
+  _clamp = iterator.get_be_float32();
+  _mag_filter_alpha = (Magnification)iterator.get_be_int32();
+  _mag_filter_color = (Magnification)iterator.get_be_int32();
+  iterator.skip_bytes(4 + 4 * 8);
+  _lambert_conic_central_meridian = iterator.get_be_float64();
+  _lambert_conic_upper_latitude = iterator.get_be_float64();
+  _lambert_conic_lower_latitude = iterator.get_be_float64();
+  iterator.skip_bytes(8 + 4 * 5);
+  _use_detail = (iterator.get_be_int32() != 0);
+  _detail_j = iterator.get_be_int32();
+  _detail_k = iterator.get_be_int32();
+  _detail_m = iterator.get_be_int32();
+  _detail_n = iterator.get_be_int32();
+  _detail_scramble = iterator.get_be_int32();
+  _use_tile = (iterator.get_be_int32() != 0);
+  _tile_lower_left_u = iterator.get_be_float32();
+  _tile_lower_left_v = iterator.get_be_float32();
+  _tile_upper_right_u = iterator.get_be_float32();
+  _tile_upper_right_v = iterator.get_be_float32();
+  _projection = (ProjectionType)iterator.get_be_int32();
+  _earth_model = (EarthModel)iterator.get_be_int32();
+  iterator.skip_bytes(4);
+  _utm_zone = iterator.get_be_int32();
+  _image_origin = (ImageOrigin)iterator.get_be_int32();
+  _geospecific_points_units = (PointsUnits)iterator.get_be_int32();
+  _geospecific_hemisphere = (Hemisphere)iterator.get_be_int32();
+  iterator.skip_bytes(4 + 4 + 149 * 4);
+  iterator.skip_bytes(8);  // Undocumented padding.
+  _comment = iterator.get_fixed_string(512);
+  iterator.skip_bytes(13 * 4);
+  iterator.skip_bytes(4);  // Undocumented padding.
+  _file_version = iterator.get_be_int32();
+
+  // Now read the geospecific control points.
+  _geospecific_control_points.clear();
+  int num_points = iterator.get_be_int32();
+  if (num_points > 0) {
+    iterator.skip_bytes(4);
+
+    while (num_points > 0) {
+      GeospecificControlPoint gcp;
+      gcp._uv[0] = iterator.get_be_float64();
+      gcp._uv[1] = iterator.get_be_float64();
+      gcp._real_earth[0] = iterator.get_be_float64();
+      gcp._real_earth[1] = iterator.get_be_float64();
+    }
+  }
+
+  nassertr(iterator.get_remaining_size() == 0, FE_ok);
+  return FE_ok;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltTexture::pack_attr
+//       Access: Private
+//  Description: Packs the attribute data into a big datagram.
+////////////////////////////////////////////////////////////////////
+FltError FltTexture::
+pack_attr(Datagram &datagram) const {
+  datagram.add_be_int32(_num_texels_u);
+  datagram.add_be_int32(_num_texels_v);
+  datagram.add_be_int32(_real_world_size_u);
+  datagram.add_be_int32(_real_world_size_v);
+  datagram.add_be_int32(_up_vector_x);
+  datagram.add_be_int32(_up_vector_y);
+  datagram.add_be_int32(_file_format);
+  datagram.add_be_int32(_min_filter);
+  datagram.add_be_int32(_mag_filter);
+  datagram.add_be_int32(_repeat);
+  datagram.add_be_int32(_repeat_u);
+  datagram.add_be_int32(_repeat_v);
+  datagram.add_be_int32(_modify_flag);
+  datagram.add_be_int32(_x_pivot_point);
+  datagram.add_be_int32(_y_pivot_point);
+  datagram.add_be_int32(_env_type);
+  datagram.add_be_int32(_intensity_is_alpha);
+  datagram.pad_bytes(4 * 8);
+  datagram.pad_bytes(4);  // Undocumented padding.
+  datagram.add_be_float64(_float_real_world_size_u);
+  datagram.add_be_float64(_float_real_world_size_v);
+  datagram.add_be_int32(_imported_origin_code);
+  datagram.add_be_int32(_kernel_version);
+  datagram.add_be_int32(_internal_format);
+  datagram.add_be_int32(_external_format);
+  datagram.add_be_int32(_use_mipmap_kernel);
+  int i;
+  for (i = 0; i < 8; i++) {
+    datagram.add_be_float32(_mipmap_kernel[i]);
+  }
+  datagram.add_be_int32(_use_lod_scale);
+  for (i = 0; i < 8; i++) {
+    datagram.add_be_float32(_lod_scale[i]._lod);
+    datagram.add_be_float32(_lod_scale[i]._scale);
+  }
+  datagram.add_be_float32(_clamp);
+  datagram.add_be_int32(_mag_filter_alpha);
+  datagram.add_be_int32(_mag_filter_color);
+  datagram.pad_bytes(4 + 4 * 8);
+  datagram.add_be_float64(_lambert_conic_central_meridian);
+  datagram.add_be_float64(_lambert_conic_upper_latitude); 
+  datagram.add_be_float64(_lambert_conic_lower_latitude);
+  datagram.pad_bytes(8 + 4 * 5);
+  datagram.add_be_int32(_use_detail);
+  datagram.add_be_int32(_detail_j);  
+  datagram.add_be_int32(_detail_k);  
+  datagram.add_be_int32(_detail_m);  
+  datagram.add_be_int32(_detail_n);
+  datagram.add_be_int32(_detail_scramble);
+  datagram.add_be_int32(_use_tile);
+  datagram.add_be_float32(_tile_lower_left_u);
+  datagram.add_be_float32(_tile_lower_left_v);
+  datagram.add_be_float32(_tile_upper_right_u);
+  datagram.add_be_float32(_tile_upper_right_v);
+  datagram.add_be_int32(_projection);
+  datagram.add_be_int32(_earth_model);
+  datagram.pad_bytes(4);
+  datagram.add_be_int32(_utm_zone);
+  datagram.add_be_int32(_image_origin);
+  datagram.add_be_int32(_geospecific_points_units);
+  datagram.add_be_int32(_geospecific_hemisphere);
+  datagram.pad_bytes(4 + 4 + 149 * 4);
+  datagram.pad_bytes(8);  // Undocumented padding.
+  datagram.add_fixed_string(_comment, 512);
+  datagram.pad_bytes(13 * 4);
+  datagram.pad_bytes(4);  // Undocumented padding.
+  datagram.add_be_int32(_file_version);
+
+  // Now write the geospecific control points.
+  datagram.add_be_int32(_geospecific_control_points.size());
+  if (!_geospecific_control_points.empty()) {
+    datagram.pad_bytes(4);
+    GeospecificControlPoints::const_iterator pi;
+    for (pi = _geospecific_control_points.begin();
+	 pi != _geospecific_control_points.end();
+	 ++pi) {
+      datagram.add_be_float64((*pi)._uv[0]);
+      datagram.add_be_float64((*pi)._uv[1]);
+      datagram.add_be_float64((*pi)._real_earth[0]);
+      datagram.add_be_float64((*pi)._real_earth[1]);
+    }
+  }
+
+  return FE_ok;
+}

+ 232 - 0
pandatool/src/flt/fltTexture.h

@@ -0,0 +1,232 @@
+// Filename: fltTexture.h
+// Created by:  drose (25Aug00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef FLTTEXTURE_H
+#define FLTTEXTURE_H
+
+#include <pandatoolbase.h>
+
+#include "fltRecord.h"
+
+#include <filename.h>
+#include <luse.h>
+
+////////////////////////////////////////////////////////////////////
+// 	 Class : FltTexture
+// Description : Represents a single texture in the texture palette.
+////////////////////////////////////////////////////////////////////
+class FltTexture : public FltRecord {
+public:
+  FltTexture(FltHeader *header);
+
+  string _filename;
+  int _pattern_index;
+  int _x_location;
+  int _y_location;
+
+  Filename get_texture_filename() const;
+  Filename get_attr_filename() const;
+  FltError read_attr_data();
+  FltError write_attr_data() const;
+
+  // The remaining fields are from the attr file.
+  enum FileFormat {
+    FF_none             = -1,
+    FF_att_8_pattern    = 0,
+    FF_att_8_template   = 1,
+    FF_sgi_i            = 2,
+    FF_sgi_ia           = 3,
+    FF_sgi_rgb          = 4,
+    FF_sgi_rgba         = 5
+  };
+
+  enum Minification {
+    MN_point            = 0,
+    MN_bilinear         = 1,
+    MN_OB_mipmap        = 2,  // obsolete
+    MN_mipmap_point     = 3,
+    MN_mipmap_linear    = 4,
+    MN_mipmap_bilinear  = 5,
+    MN_mipmap_trilinear = 6,
+    MN_bicubic          = 8,
+    MN_bilinear_gequal  = 9,
+    MN_bilinear_lequal  = 10,
+    MN_bicubic_gequal   = 11,
+    MN_bicubic_lequal   = 12
+  };
+
+  enum Magnification {
+    MG_point            = 0,
+    MG_bilinear         = 1,
+    MG_bicubic          = 3,
+    MG_sharpen          = 4,
+    MG_add_detail       = 5,
+    MG_modulate_detail  = 6,
+    MG_bilinear_gequal  = 7,
+    MG_bilinear_lequal  = 8,
+    MG_bicubic_gequal   = 9,
+    MG_bicubic_lequal   = 10
+  };
+
+  enum RepeatType {
+    RT_repeat           = 0,
+    RT_clamp            = 1
+  };
+
+  enum EnvironmentType {
+    ET_modulate         = 0,
+    ET_blend            = 1,
+    ET_decal            = 2,
+    ET_color            = 3
+  };
+
+  enum InternalFormat {
+    IF_default          = 0,
+    IF_i_12a_4          = 1,
+    IF_ia_8             = 2,
+    IF_rgb_5            = 3,
+    IF_rgba_4           = 4,
+    IF_ia_12            = 5,
+    IF_rgba_8           = 6,
+    IF_rgba_12          = 7,
+    IF_i_16             = 8,  // shadow mode only
+    IF_rgb_12           = 9
+  };
+
+  enum ExternalFormat {
+    EF_default          = 0,
+    EF_pack_8           = 1,
+    EF_pack_16          = 2
+  };
+
+  enum ProjectionType {
+    PT_flat_earth       = 0,
+    PT_lambert          = 3,
+    PT_utm              = 4,
+    PT_undefined        = 7
+  };
+
+  enum EarthModel {
+    EM_wgs84            = 0,
+    EM_wgs72            = 1,
+    EM_bessel           = 2,
+    EM_clarke_1866      = 3,
+    EM_nad27            = 4
+  };
+
+  enum ImageOrigin {
+    IO_lower_left       = 0,
+    IO_upper_left       = 1
+  };
+
+  enum PointsUnits {
+    PU_degrees          = 0,
+    PU_meters           = 1,
+    PU_pixels           = 2
+  };
+
+  enum Hemisphere {
+    H_southern          = 0,
+    H_northern          = 1,
+  };
+
+  struct LODScale {
+    float _lod;
+    float _scale;
+  };
+
+  struct GeospecificControlPoint {
+    LPoint2d _uv;
+    LPoint2d _real_earth;
+  };
+
+  typedef vector<GeospecificControlPoint> GeospecificControlPoints;
+
+  int _num_texels_u;
+  int _num_texels_v;
+  int _real_world_size_u;
+  int _real_world_size_v;
+  int _up_vector_x;
+  int _up_vector_y;
+  FileFormat _file_format;
+  Minification _min_filter;
+  Magnification _mag_filter;
+  RepeatType _repeat;
+  RepeatType _repeat_u;
+  RepeatType _repeat_v;
+  int _modify_flag;
+  int _x_pivot_point;
+  int _y_pivot_point;
+  EnvironmentType _env_type;
+  bool _intensity_is_alpha; // if true, a one-channel image is actually
+                            // an alpha image, not an intensity image.
+  double _float_real_world_size_u;
+  double _float_real_world_size_v;
+  int _imported_origin_code;
+  int _kernel_version;
+  InternalFormat _internal_format;
+  ExternalFormat _external_format;
+  bool _use_mipmap_kernel;
+  float _mipmap_kernel[8];
+  bool _use_lod_scale;
+  LODScale _lod_scale[8];
+  float _clamp;
+  Magnification _mag_filter_alpha;
+  Magnification _mag_filter_color;
+  double _lambert_conic_central_meridian;
+  double _lambert_conic_upper_latitude;
+  double _lambert_conic_lower_latitude;
+  bool _use_detail;
+  int _detail_j;
+  int _detail_k;
+  int _detail_m;
+  int _detail_n;
+  int _detail_scramble;
+  bool _use_tile;
+  float _tile_lower_left_u;
+  float _tile_lower_left_v;
+  float _tile_upper_right_u;
+  float _tile_upper_right_v;
+  ProjectionType _projection;
+  EarthModel _earth_model;
+  int _utm_zone;
+  ImageOrigin _image_origin;
+  PointsUnits _geospecific_points_units;
+  Hemisphere _geospecific_hemisphere;
+  string _comment;
+  int _file_version;
+  GeospecificControlPoints _geospecific_control_points;
+
+protected:
+  virtual bool extract_record(FltRecordReader &reader);
+  virtual bool build_record(FltRecordWriter &writer) const;
+
+private:
+  FltError unpack_attr(const Datagram &datagram);
+  FltError pack_attr(Datagram &datagram) const;
+
+public:
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    FltRecord::init_type();
+    register_type(_type_handle, "FltTexture",
+		  FltRecord::get_class_type());
+  }
+
+private:
+  static TypeHandle _type_handle;
+
+  friend class FltHeader;
+};
+
+#endif
+
+

+ 95 - 0
pandatool/src/flt/fltTrackplane.cxx

@@ -0,0 +1,95 @@
+// Filename: fltTrackplane.cxx
+// Created by:  drose (26Aug00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "fltTrackplane.h"
+#include "fltRecordReader.h"
+#include "fltRecordWriter.h"
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltTrackplane::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+FltTrackplane::
+FltTrackplane() {
+  _origin.set(0.0, 0.0, 0.0);
+  _alignment.set(0.0, 0.0, 0.0);
+  _plane.set(0.0, 0.0, 1.0);
+  _grid_state = false;
+  _grid_under = false;
+  _grid_angle = 0.0;
+  _grid_spacing_x = 1;
+  _grid_spacing_y = 1;
+  _snap_to_grid = false;
+  _grid_size = 10.0;
+  _grid_spacing_direction = 0;
+  _grid_mask = 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltTrackplane::extract_record
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+bool FltTrackplane::
+extract_record(FltRecordReader &reader) {
+  DatagramIterator &iterator = reader.get_iterator();
+
+  _origin[0] = iterator.get_be_float64();
+  _origin[1] = iterator.get_be_float64();
+  _origin[2] = iterator.get_be_float64();
+  _alignment[0] = iterator.get_be_float64();
+  _alignment[1] = iterator.get_be_float64();
+  _alignment[0] = iterator.get_be_float64();
+  _plane[0] = iterator.get_be_float64();
+  _plane[1] = iterator.get_be_float64();
+  _plane[2] = iterator.get_be_float64();
+  _grid_state = (iterator.get_be_int32() != 0);
+  _grid_under = (iterator.get_be_int32() != 0);
+  _grid_angle = iterator.get_be_float32();
+  iterator.skip_bytes(4);
+  _grid_spacing_x = iterator.get_be_float64();
+  _grid_spacing_y = iterator.get_be_float64();
+  _snap_to_grid = (iterator.get_be_int32() != 0);
+  _grid_size = iterator.get_be_float64();
+  _grid_spacing_direction = iterator.get_be_int32();
+  _grid_mask = iterator.get_be_int32();
+  iterator.skip_bytes(4);
+
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltTrackplane::build_record
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+bool FltTrackplane::
+build_record(FltRecordWriter &writer) const {
+  Datagram &datagram = writer.update_datagram();
+
+  datagram.add_be_float64(_origin[0]);
+  datagram.add_be_float64(_origin[1]);
+  datagram.add_be_float64(_origin[2]);
+  datagram.add_be_float64(_alignment[0]);
+  datagram.add_be_float64(_alignment[1]);
+  datagram.add_be_float64(_alignment[2]);
+  datagram.add_be_float64(_plane[0]);
+  datagram.add_be_float64(_plane[1]);
+  datagram.add_be_float64(_plane[2]);
+  datagram.add_be_int32(_grid_state); 
+  datagram.add_be_int32(_grid_under);
+  datagram.add_be_float32(_grid_angle);
+  datagram.pad_bytes(4);
+  datagram.add_be_float64(_grid_spacing_x);
+  datagram.add_be_float64(_grid_spacing_y);
+  datagram.add_be_int32(_snap_to_grid);
+  datagram.add_be_float64(_grid_size);
+  datagram.add_be_int32(_grid_spacing_direction);
+  datagram.add_be_int32(_grid_mask);
+  datagram.pad_bytes(4);
+
+  return true;
+}

+ 46 - 0
pandatool/src/flt/fltTrackplane.h

@@ -0,0 +1,46 @@
+// Filename: fltTrackplane.h
+// Created by:  drose (26Aug00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef FLTTRACKPLANE_H
+#define FLTTRACKPLANE_H
+
+#include <pandatoolbase.h>
+
+#include <luse.h>
+
+class FltRecordReader;
+class FltRecordWriter;
+
+////////////////////////////////////////////////////////////////////
+// 	 Class : FltTrackplane
+// Description : A single trackplane entry in the eyepoint/trackplane
+//               palette.
+////////////////////////////////////////////////////////////////////
+class FltTrackplane {
+public:
+  FltTrackplane();
+
+  bool extract_record(FltRecordReader &reader);
+  bool build_record(FltRecordWriter &writer) const;
+
+public:
+  LPoint3d _origin;
+  LPoint3d _alignment;
+  LVector3d _plane;
+  bool _grid_state;
+  bool _grid_under;
+  float _grid_angle;
+  double _grid_spacing_x;
+  double _grid_spacing_y;
+  bool _snap_to_grid;
+  double _grid_size;
+  int _grid_spacing_direction;
+  int _grid_mask;
+};
+
+#endif
+
+
+  

+ 92 - 0
pandatool/src/flt/fltTransformGeneralMatrix.cxx

@@ -0,0 +1,92 @@
+// Filename: fltTransformGeneralMatrix.cxx
+// Created by:  drose (24Aug00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "fltTransformGeneralMatrix.h"
+#include "fltRecordReader.h"
+#include "fltRecordWriter.h"
+
+TypeHandle FltTransformGeneralMatrix::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltTransformGeneralMatrix::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+FltTransformGeneralMatrix::
+FltTransformGeneralMatrix(FltHeader *header) : FltTransformRecord(header) {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltTransformGeneralMatrix::set_matrix
+//       Access: Public
+//  Description: Directly sets the general matrix.
+////////////////////////////////////////////////////////////////////
+void FltTransformGeneralMatrix::
+set_matrix(const LMatrix4d &matrix) {
+  _matrix = matrix;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltTransformGeneralMatrix::set_matrix
+//       Access: Public
+//  Description: Directly sets the general matrix.
+////////////////////////////////////////////////////////////////////
+void FltTransformGeneralMatrix::
+set_matrix(const LMatrix4f &matrix) {
+  _matrix = LCAST(double, matrix);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltTransformGeneralMatrix::extract_record
+//       Access: Protected, Virtual
+//  Description: Fills in the information in this record 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 FltTransformGeneralMatrix::
+extract_record(FltRecordReader &reader) {
+  if (!FltTransformRecord::extract_record(reader)) {
+    return false;
+  }
+
+  nassertr(reader.get_opcode() == FO_general_matrix, false);
+  DatagramIterator &iterator = reader.get_iterator();
+
+  for (int r = 0; r < 4; r++) {
+    for (int c = 0; c < 4; c++) {
+      _matrix(r, c) = iterator.get_be_float32();
+    }
+  }
+
+  nassertr(iterator.get_remaining_size() == 0, true);
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltTransformGeneralMatrix::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 FltTransformGeneralMatrix::
+build_record(FltRecordWriter &writer) const {
+  if (!FltTransformRecord::build_record(writer)) {
+    return false;
+  }
+
+  writer.set_opcode(FO_general_matrix);
+  Datagram &datagram = writer.update_datagram();
+
+  for (int r = 0; r < 4; r++) {
+    for (int c = 0; c < 4; c++) {
+      datagram.add_be_float32(_matrix(r, c));
+    }
+  }
+
+  return true;
+}

+ 50 - 0
pandatool/src/flt/fltTransformGeneralMatrix.h

@@ -0,0 +1,50 @@
+// Filename: fltTransformGeneralMatrix.h
+// Created by:  drose (24Aug00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef FLTTRANSFORMGENERALMATRIX_H
+#define FLTTRANSFORMGENERALMATRIX_H
+
+#include <pandatoolbase.h>
+
+#include "fltTransformRecord.h"
+
+////////////////////////////////////////////////////////////////////
+// 	 Class : FltTransformGeneralMatrix
+// Description : A general 4x4 matrix.  This appears in the flt file
+//               when there is no record of the composition of the
+//               transform.
+////////////////////////////////////////////////////////////////////
+class FltTransformGeneralMatrix : public FltTransformRecord {
+public:
+  FltTransformGeneralMatrix(FltHeader *header);
+
+  void set_matrix(const LMatrix4d &matrix);
+  void set_matrix(const LMatrix4f &matrix);
+
+protected:
+  virtual bool extract_record(FltRecordReader &reader);
+  virtual bool build_record(FltRecordWriter &writer) const;
+
+public:
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    FltTransformRecord::init_type();
+    register_type(_type_handle, "FltTransformGeneralMatrix",
+		  FltTransformRecord::get_class_type());
+  }
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#endif
+
+

+ 213 - 0
pandatool/src/flt/fltTransformPut.cxx

@@ -0,0 +1,213 @@
+// Filename: fltTransformPut.cxx
+// Created by:  drose (29Aug00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "fltTransformPut.h"
+#include "fltRecordReader.h"
+#include "fltRecordWriter.h"
+
+#include <look_at.h>
+
+TypeHandle FltTransformPut::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltTransformPut::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+FltTransformPut::
+FltTransformPut(FltHeader *header) : FltTransformRecord(header) {
+  _from_origin.set(0.0, 0.0, 0.0);
+  _from_align.set(1.0, 0.0, 0.0);
+  _from_track.set(1.0, 0.0, 0.0);
+  _to_origin.set(0.0, 0.0, 0.0);
+  _to_align.set(1.0, 0.0, 0.0);
+  _to_track.set(1.0, 0.0, 0.0);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltTransformPut::set
+//       Access: Public
+//  Description: Defines the put explicitly.  The transformation will
+//               map the three "from" points to the corresponding
+//               three "to" points.
+////////////////////////////////////////////////////////////////////
+void FltTransformPut::
+set(const LPoint3d &from_origin, const LPoint3d &from_align,
+    const LPoint3d &from_track,
+    const LPoint3d &to_origin, const LPoint3d &to_align,
+    const LPoint3d &to_track) {
+  _from_origin = from_origin;
+  _from_align = from_align;
+  _from_track = from_track;
+  _to_origin = to_origin;
+  _to_align = to_align;
+  _to_track = to_track;
+
+  recompute_matrix();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltTransformPut::get_from_origin
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+const LPoint3d &FltTransformPut::
+get_from_origin() const {
+  return _from_origin;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltTransformPut::get_from_align
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+const LPoint3d &FltTransformPut::
+get_from_align() const {
+  return _from_align;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltTransformPut::get_from_track
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+const LPoint3d &FltTransformPut::
+get_from_track() const {
+  return _from_track;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltTransformPut::get_to_origin
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+const LPoint3d &FltTransformPut::
+get_to_origin() const {
+  return _to_origin;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltTransformPut::get_to_align
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+const LPoint3d &FltTransformPut::
+get_to_align() const {
+  return _to_align;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltTransformPut::get_to_track
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+const LPoint3d &FltTransformPut::
+get_to_track() const {
+  return _to_track;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltTransformPut::recompute_matrix
+//       Access: Private
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void FltTransformPut::
+recompute_matrix() {
+  LMatrix4d r1, r2;
+  look_at(r1, _from_align - _from_origin, _from_track - _from_origin, CS_zup_right);
+  look_at(r2, _to_align - _to_origin, _to_track - _to_origin, CS_zup_right);
+
+  _matrix = 
+    LMatrix4d::translate_mat(-_from_origin) *
+    invert(r1) *
+    r2 *
+    LMatrix4d::translate_mat(_to_origin);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltTransformPut::extract_record
+//       Access: Protected, Virtual
+//  Description: Fills in the information in this record 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 FltTransformPut::
+extract_record(FltRecordReader &reader) {
+  if (!FltTransformRecord::extract_record(reader)) {
+    return false;
+  }
+
+  nassertr(reader.get_opcode() == FO_put, false);
+  DatagramIterator &iterator = reader.get_iterator();
+
+  iterator.skip_bytes(4);   // Undocumented additional padding.
+
+  _from_origin[0] = iterator.get_be_float64();
+  _from_origin[1] = iterator.get_be_float64();
+  _from_origin[2] = iterator.get_be_float64();
+  _from_align[0] = iterator.get_be_float64();
+  _from_align[1] = iterator.get_be_float64();
+  _from_align[2] = iterator.get_be_float64();
+  _from_track[0] = iterator.get_be_float64();
+  _from_track[1] = iterator.get_be_float64();
+  _from_track[2] = iterator.get_be_float64();
+  _to_origin[0] = iterator.get_be_float64();
+  _to_origin[1] = iterator.get_be_float64();
+  _to_origin[2] = iterator.get_be_float64();
+  _to_align[0] = iterator.get_be_float64();
+  _to_align[1] = iterator.get_be_float64();
+  _to_align[2] = iterator.get_be_float64();
+  _to_track[0] = iterator.get_be_float64();
+  _to_track[1] = iterator.get_be_float64();
+  _to_track[2] = iterator.get_be_float64();
+
+  recompute_matrix();
+
+  nassertr(iterator.get_remaining_size() == 0, true);
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltTransformPut::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 FltTransformPut::
+build_record(FltRecordWriter &writer) const {
+  if (!FltTransformRecord::build_record(writer)) {
+    return false;
+  }
+
+  writer.set_opcode(FO_put);
+  Datagram &datagram = writer.update_datagram();
+
+  datagram.pad_bytes(4);   // Undocumented additional padding.
+
+  datagram.add_be_float64(_from_origin[0]);
+  datagram.add_be_float64(_from_origin[1]);
+  datagram.add_be_float64(_from_origin[2]);
+  datagram.add_be_float64(_from_align[0]);
+  datagram.add_be_float64(_from_align[1]);
+  datagram.add_be_float64(_from_align[2]);
+  datagram.add_be_float64(_from_track[0]);
+  datagram.add_be_float64(_from_track[1]);
+  datagram.add_be_float64(_from_track[2]);
+  datagram.add_be_float64(_to_origin[0]);
+  datagram.add_be_float64(_to_origin[1]);
+  datagram.add_be_float64(_to_origin[2]);
+  datagram.add_be_float64(_to_align[0]);
+  datagram.add_be_float64(_to_align[1]);
+  datagram.add_be_float64(_to_align[2]);
+  datagram.add_be_float64(_to_track[0]);
+  datagram.add_be_float64(_to_track[1]);
+  datagram.add_be_float64(_to_track[2]);
+
+  return true;
+}
+

+ 71 - 0
pandatool/src/flt/fltTransformPut.h

@@ -0,0 +1,71 @@
+// Filename: fltTransformPut.h
+// Created by:  drose (29Aug00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef FLTTRANSFORMPUT_H
+#define FLTTRANSFORMPUT_H
+
+#include <pandatoolbase.h>
+
+#include "fltTransformRecord.h"
+
+////////////////////////////////////////////////////////////////////
+// 	 Class : FltTransformPut
+// Description : A "put", which is a MultiGen concept of defining a
+//               transformation by mapping three arbitrary points to
+//               three new arbitrary points.
+////////////////////////////////////////////////////////////////////
+class FltTransformPut : public FltTransformRecord {
+public:
+  FltTransformPut(FltHeader *header);
+
+  void set(const LPoint3d &from_origin, 
+	   const LPoint3d &from_align,
+	   const LPoint3d &from_track,
+	   const LPoint3d &to_origin,
+	   const LPoint3d &to_align,
+	   const LPoint3d &to_track);
+
+  const LPoint3d &get_from_origin() const;
+  const LPoint3d &get_from_align() const;
+  const LPoint3d &get_from_track() const;
+  const LPoint3d &get_to_origin() const;
+  const LPoint3d &get_to_align() const;
+  const LPoint3d &get_to_track() const;
+
+private:
+  void recompute_matrix();
+
+  LPoint3d _from_origin;
+  LPoint3d _from_align;
+  LPoint3d _from_track;
+  LPoint3d _to_origin;
+  LPoint3d _to_align;
+  LPoint3d _to_track;
+
+protected:
+  virtual bool extract_record(FltRecordReader &reader);
+  virtual bool build_record(FltRecordWriter &writer) const;
+
+public:
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    FltTransformRecord::init_type();
+    register_type(_type_handle, "FltTransformPut",
+		  FltTransformRecord::get_class_type());
+  }
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#endif
+
+

+ 29 - 0
pandatool/src/flt/fltTransformRecord.cxx

@@ -0,0 +1,29 @@
+// Filename: fltTransformRecord.cxx
+// Created by:  drose (24Aug00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "fltTransformRecord.h"
+
+TypeHandle FltTransformRecord::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltTransformRecord::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+FltTransformRecord::
+FltTransformRecord(FltHeader *header) : FltRecord(header) {
+  _matrix = LMatrix4d::ident_mat();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltTransformRecord::get_matrix
+//       Access: Public
+//  Description: Returns the transform matrix represented by this
+//               particular component of the transform.
+////////////////////////////////////////////////////////////////////
+const LMatrix4d &FltTransformRecord::
+get_matrix() const {
+  return _matrix;
+}

+ 53 - 0
pandatool/src/flt/fltTransformRecord.h

@@ -0,0 +1,53 @@
+// Filename: fltTransformRecord.h
+// Created by:  drose (24Aug00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef FLTTRANSFORMRECORD_H
+#define FLTTRANSFORMRECORD_H
+
+#include <pandatoolbase.h>
+
+#include "fltRecord.h"
+
+#include <luse.h>
+
+////////////////////////////////////////////////////////////////////
+// 	 Class : FltTransformRecord
+// Description : A base class for a number of types of ancillary
+//               records that follow beads and indicate some kind of a
+//               transformation.  Pointers of this type are collected
+//               in the FltTransformation class.
+////////////////////////////////////////////////////////////////////
+class FltTransformRecord : public FltRecord {
+public:
+  FltTransformRecord(FltHeader *header);
+
+  const LMatrix4d &get_matrix() const;
+
+protected:
+  LMatrix4d _matrix;
+
+public:
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    FltRecord::init_type();
+    register_type(_type_handle, "FltTransformRecord",
+		  FltRecord::get_class_type());
+  }
+
+private:
+  static TypeHandle _type_handle;
+
+  friend class FltBead;
+};
+
+#endif
+
+

+ 155 - 0
pandatool/src/flt/fltTransformRotateAboutEdge.cxx

@@ -0,0 +1,155 @@
+// Filename: fltTransformRotateAboutEdge.cxx
+// Created by:  drose (30Aug00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "fltTransformRotateAboutEdge.h"
+#include "fltRecordReader.h"
+#include "fltRecordWriter.h"
+
+TypeHandle FltTransformRotateAboutEdge::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltTransformRotateAboutEdge::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+FltTransformRotateAboutEdge::
+FltTransformRotateAboutEdge(FltHeader *header) : FltTransformRecord(header) {
+  _point_a.set(0.0, 0.0, 0.0);
+  _point_b.set(1.0, 0.0, 0.0);
+  _angle = 0.0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltTransformRotateAboutEdge::set
+//       Access: Public
+//  Description: Defines the rotation.  The angle is given in degrees,
+//               counterclockwise about the axis as seen from point a.
+////////////////////////////////////////////////////////////////////
+void FltTransformRotateAboutEdge::
+set(const LPoint3d &point_a, const LPoint3d &point_b, float angle) {
+  _point_a = point_a;
+  _point_b = point_b;
+  _angle = angle;
+
+  recompute_matrix();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltTransformRotateAboutEdge::get_point_a
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+const LPoint3d &FltTransformRotateAboutEdge::
+get_point_a() const {
+  return _point_a;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltTransformRotateAboutEdge::get_point_b
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+const LPoint3d &FltTransformRotateAboutEdge::
+get_point_b() const {
+  return _point_b;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltTransformRotateAboutEdge::get_angle
+//       Access: Public
+//  Description: Returns the angle of rotation, in degrees
+//               counterclockwise about the axis as seen from point a.
+////////////////////////////////////////////////////////////////////
+float FltTransformRotateAboutEdge::
+get_angle() const {
+  return _angle;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltTransformRotateAboutEdge::recompute_matrix
+//       Access: Private
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void FltTransformRotateAboutEdge::
+recompute_matrix() {
+  if (_point_a == _point_b) {
+    // Degenerate case.
+    _matrix = LMatrix4d::ident_mat();
+  } else {
+    LVector3d axis = _point_b - _point_a;
+    _matrix = 
+      LMatrix4d::translate_mat(-_point_a) *
+      LMatrix4d::rotate_mat(_angle, normalize(axis), CS_zup_right) *
+      LMatrix4d::translate_mat(_point_a);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltTransformRotateAboutEdge::extract_record
+//       Access: Protected, Virtual
+//  Description: Fills in the information in this record 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 FltTransformRotateAboutEdge::
+extract_record(FltRecordReader &reader) {
+  if (!FltTransformRecord::extract_record(reader)) {
+    return false;
+  }
+
+  nassertr(reader.get_opcode() == FO_rotate_about_edge, false);
+  DatagramIterator &iterator = reader.get_iterator();
+
+  iterator.skip_bytes(4);   // Undocumented additional padding.
+
+  _point_a[0] = iterator.get_be_float64();
+  _point_a[1] = iterator.get_be_float64();
+  _point_a[2] = iterator.get_be_float64();
+  _point_b[0] = iterator.get_be_float64();
+  _point_b[1] = iterator.get_be_float64();
+  _point_b[2] = iterator.get_be_float64();
+  _angle = iterator.get_be_float32();
+
+  iterator.skip_bytes(4);   // Undocumented additional padding.
+
+  recompute_matrix();
+
+  nassertr(iterator.get_remaining_size() == 0, true);
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltTransformRotateAboutEdge::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 FltTransformRotateAboutEdge::
+build_record(FltRecordWriter &writer) const {
+  if (!FltTransformRecord::build_record(writer)) {
+    return false;
+  }
+
+  writer.set_opcode(FO_rotate_about_edge);
+  Datagram &datagram = writer.update_datagram();
+
+  datagram.pad_bytes(4);   // Undocumented additional padding.
+
+  datagram.add_be_float64(_point_a[0]);
+  datagram.add_be_float64(_point_a[1]);
+  datagram.add_be_float64(_point_a[2]);
+  datagram.add_be_float64(_point_b[0]);
+  datagram.add_be_float64(_point_b[1]);
+  datagram.add_be_float64(_point_b[2]);
+  datagram.add_be_float32(_angle);
+
+  datagram.pad_bytes(4);   // Undocumented additional padding.
+
+  return true;
+}
+

+ 57 - 0
pandatool/src/flt/fltTransformRotateAboutEdge.h

@@ -0,0 +1,57 @@
+// Filename: fltTransformRotateAboutEdge.h
+// Created by:  drose (30Aug00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef FLTTRANSFORMROTATEABOUTEDGE_H
+#define FLTTRANSFORMROTATEABOUTEDGE_H
+
+#include <pandatoolbase.h>
+
+#include "fltTransformRecord.h"
+
+////////////////////////////////////////////////////////////////////
+// 	 Class : FltTransformRotateAboutEdge
+// Description : A transformation that rotates about a particular axis
+//               in space, defined by two endpoints.
+////////////////////////////////////////////////////////////////////
+class FltTransformRotateAboutEdge : public FltTransformRecord {
+public:
+  FltTransformRotateAboutEdge(FltHeader *header);
+
+  void set(const LPoint3d &point_a, const LPoint3d &point_b, float angle);
+
+  const LPoint3d &get_point_a() const;
+  const LPoint3d &get_point_b() const;
+  float get_angle() const;
+
+private:
+  void recompute_matrix();
+
+  LPoint3d _point_a;
+  LPoint3d _point_b;
+  float _angle;
+
+protected:
+  virtual bool extract_record(FltRecordReader &reader);
+  virtual bool build_record(FltRecordWriter &writer) const;
+
+public:
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    FltTransformRecord::init_type();
+    register_type(_type_handle, "FltTransformRotateAboutEdge",
+		  FltTransformRecord::get_class_type());
+  }
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#endif

+ 152 - 0
pandatool/src/flt/fltTransformRotateAboutPoint.cxx

@@ -0,0 +1,152 @@
+// Filename: fltTransformRotateAboutPoint.cxx
+// Created by:  drose (30Aug00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "fltTransformRotateAboutPoint.h"
+#include "fltRecordReader.h"
+#include "fltRecordWriter.h"
+
+TypeHandle FltTransformRotateAboutPoint::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltTransformRotateAboutPoint::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+FltTransformRotateAboutPoint::
+FltTransformRotateAboutPoint(FltHeader *header) : FltTransformRecord(header) {
+  _center.set(0.0, 0.0, 0.0);
+  _axis.set(1.0, 0.0, 0.0);
+  _angle = 0.0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltTransformRotateAboutPoint::set
+//       Access: Public
+//  Description: Defines the rotation.  The angle is given in degrees,
+//               counterclockwise about the axis as seen from point a.
+////////////////////////////////////////////////////////////////////
+void FltTransformRotateAboutPoint::
+set(const LPoint3d &center, const LVector3f &axis, float angle) {
+  _center = center;
+  _axis = axis;
+  _angle = angle;
+
+  recompute_matrix();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltTransformRotateAboutPoint::get_center
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+const LPoint3d &FltTransformRotateAboutPoint::
+get_center() const {
+  return _center;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltTransformRotateAboutPoint::get_axis
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+const LVector3f &FltTransformRotateAboutPoint::
+get_axis() const {
+  return _axis;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltTransformRotateAboutPoint::get_angle
+//       Access: Public
+//  Description: Returns the angle of rotation, in degrees
+//               counterclockwise about the axis.
+////////////////////////////////////////////////////////////////////
+float FltTransformRotateAboutPoint::
+get_angle() const {
+  return _angle;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltTransformRotateAboutPoint::recompute_matrix
+//       Access: Private
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void FltTransformRotateAboutPoint::
+recompute_matrix() {
+  if (_axis == LVector3f::zero()) {
+    // Degenerate case.
+    _matrix = LMatrix4d::ident_mat();
+  } else {
+    LVector3d axis = LCAST(double, axis);
+
+    _matrix = 
+      LMatrix4d::translate_mat(-_center) *
+      LMatrix4d::rotate_mat(_angle, normalize(axis), CS_zup_right) *
+      LMatrix4d::translate_mat(_center);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltTransformRotateAboutPoint::extract_record
+//       Access: Protected, Virtual
+//  Description: Fills in the information in this record 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 FltTransformRotateAboutPoint::
+extract_record(FltRecordReader &reader) {
+  if (!FltTransformRecord::extract_record(reader)) {
+    return false;
+  }
+
+  nassertr(reader.get_opcode() == FO_rotate_about_point, false);
+  DatagramIterator &iterator = reader.get_iterator();
+
+  iterator.skip_bytes(4);   // Undocumented additional padding.
+
+  _center[0] = iterator.get_be_float64();
+  _center[1] = iterator.get_be_float64();
+  _center[2] = iterator.get_be_float64();
+  _axis[0] = iterator.get_be_float32();
+  _axis[1] = iterator.get_be_float32();
+  _axis[2] = iterator.get_be_float32();
+  _angle = iterator.get_be_float32();
+
+  recompute_matrix();
+
+  nassertr(iterator.get_remaining_size() == 0, true);
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltTransformRotateAboutPoint::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 FltTransformRotateAboutPoint::
+build_record(FltRecordWriter &writer) const {
+  if (!FltTransformRecord::build_record(writer)) {
+    return false;
+  }
+
+  writer.set_opcode(FO_rotate_about_point);
+  Datagram &datagram = writer.update_datagram();
+
+  datagram.pad_bytes(4);   // Undocumented additional padding.
+
+  datagram.add_be_float64(_center[0]);
+  datagram.add_be_float64(_center[1]);
+  datagram.add_be_float64(_center[2]);
+  datagram.add_be_float32(_axis[0]);
+  datagram.add_be_float32(_axis[1]);
+  datagram.add_be_float32(_axis[2]);
+  datagram.add_be_float32(_angle);
+
+  return true;
+}
+

+ 57 - 0
pandatool/src/flt/fltTransformRotateAboutPoint.h

@@ -0,0 +1,57 @@
+// Filename: fltTransformRotateAboutPoint.h
+// Created by:  drose (30Aug00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef FLTTRANSFORMROTATEABOUTPOINT_H
+#define FLTTRANSFORMROTATEABOUTPOINT_H
+
+#include <pandatoolbase.h>
+
+#include "fltTransformRecord.h"
+
+////////////////////////////////////////////////////////////////////
+// 	 Class : FltTransformRotateAboutPoint
+// Description : A transformation that rotates about a particular axis
+//               in space, defined by a point and vector.
+////////////////////////////////////////////////////////////////////
+class FltTransformRotateAboutPoint : public FltTransformRecord {
+public:
+  FltTransformRotateAboutPoint(FltHeader *header);
+
+  void set(const LPoint3d &center, const LVector3f &axis, float angle);
+
+  const LPoint3d &get_center() const;
+  const LVector3f &get_axis() const;
+  float get_angle() const;
+
+private:
+  void recompute_matrix();
+
+  LPoint3d _center;
+  LVector3f _axis;
+  float _angle;
+
+protected:
+  virtual bool extract_record(FltRecordReader &reader);
+  virtual bool build_record(FltRecordWriter &writer) const;
+
+public:
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    FltTransformRecord::init_type();
+    register_type(_type_handle, "FltTransformRotateAboutPoint",
+		  FltTransformRecord::get_class_type());
+  }
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#endif

+ 230 - 0
pandatool/src/flt/fltTransformRotateScale.cxx

@@ -0,0 +1,230 @@
+// Filename: fltTransformRotateScale.cxx
+// Created by:  drose (30Aug00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "fltTransformRotateScale.h"
+#include "fltRecordReader.h"
+#include "fltRecordWriter.h"
+
+#include <mathNumbers.h>
+#include <look_at.h>
+
+TypeHandle FltTransformRotateScale::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltTransformRotateScale::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+FltTransformRotateScale::
+FltTransformRotateScale(FltHeader *header) : FltTransformRecord(header) {
+  _center.set(0.0, 0.0, 0.0);
+  _reference_point.set(0.0, 0.0, 0.0);
+  _to_point.set(0.0, 0.0, 0.0);
+  _overall_scale = 1.0;
+  _axis_scale = 1.0;
+  _angle = 0.0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltTransformRotateScale::set
+//       Access: Public
+//  Description: Defines the transform explicitly.  The angle of
+//               rotation is determined by the angle between the
+//               reference point and the to point (relative to the
+//               center), and the scale factor is determined by the
+//               distance between the reference point and the center
+//               point.  If axis_scale is true, the scale is along
+//               reference point axis only; otherwise, it is a uniform
+//               scale.
+////////////////////////////////////////////////////////////////////
+void FltTransformRotateScale::
+set(const LPoint3d &center, const LPoint3d &reference_point,
+    const LPoint3d &to_point, bool axis_scale) {
+  _center = center;
+  _reference_point = reference_point;
+  _to_point = to_point;
+
+  LVector3d v1 = _reference_point - _center;
+  LVector3d v2 = _to_point - _center;
+
+  _angle = 
+    acos(dot(normalize(v1), normalize(v2))) * 180.0 / MathNumbers::pi;
+
+  if (axis_scale) {
+    _axis_scale = length(v1);
+    _overall_scale = 1.0;
+  } else {
+    _overall_scale = length(v1);
+    _axis_scale = 1.0;
+  }
+
+  recompute_matrix();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltTransformRotateScale::get_center
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+const LPoint3d &FltTransformRotateScale::
+get_center() const {
+  return _center;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltTransformRotateScale::get_reference_point
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+const LPoint3d &FltTransformRotateScale::
+get_reference_point() const {
+  return _reference_point;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltTransformRotateScale::get_to_point
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+const LPoint3d &FltTransformRotateScale::
+get_to_point() const {
+  return _to_point;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltTransformRotateScale::get_overall_scale
+//       Access: Public
+//  Description: Returns the overall scale factor.
+////////////////////////////////////////////////////////////////////
+float FltTransformRotateScale::
+get_overall_scale() const {
+  return _overall_scale;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltTransformRotateScale::get_axis_scale
+//       Access: Public
+//  Description: Returns the scale factor in the direction of the
+//               axis.
+////////////////////////////////////////////////////////////////////
+float FltTransformRotateScale::
+get_axis_scale() const {
+  return _axis_scale;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltTransformRotateScale::get_angle
+//       Access: Public
+//  Description: Returns the angle of rotation in degrees.
+////////////////////////////////////////////////////////////////////
+float FltTransformRotateScale::
+get_angle() const {
+  return _angle;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltTransformRotateScale::recompute_matrix
+//       Access: Private
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void FltTransformRotateScale::
+recompute_matrix() {
+  LVector3d v1 = _reference_point - _center;
+  LVector3d v2 = _to_point - _center;
+  LVector3d rotate_axis = normalize(cross(v1, v2));
+
+  // To scale along an axis, we have to do a bit of work.  First
+  // determine the matrices to rotate and unrotate the rotate axis
+  // to the y-forward axis.
+  LMatrix4d r1;
+  look_at(r1, v1, rotate_axis, CS_zup_right);
+  
+  _matrix = 
+    LMatrix4d::translate_mat(-_center) *
+    r1 *
+    LMatrix4d::scale_mat(1.0, _axis_scale, 1.0) *
+    LMatrix4d::scale_mat(_overall_scale) *
+    invert(r1) *
+    LMatrix4d::rotate_mat(_angle, rotate_axis, CS_zup_right) *
+    LMatrix4d::translate_mat(_center);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltTransformRotateScale::extract_record
+//       Access: Protected, Virtual
+//  Description: Fills in the information in this record 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 FltTransformRotateScale::
+extract_record(FltRecordReader &reader) {
+  if (!FltTransformRecord::extract_record(reader)) {
+    return false;
+  }
+
+  nassertr(reader.get_opcode() == FO_rotate_and_scale, false);
+  DatagramIterator &iterator = reader.get_iterator();
+
+  iterator.skip_bytes(4);   // Undocumented additional padding.
+
+  _center[0] = iterator.get_be_float64();
+  _center[1] = iterator.get_be_float64();
+  _center[2] = iterator.get_be_float64();
+  _reference_point[0] = iterator.get_be_float64();
+  _reference_point[1] = iterator.get_be_float64();
+  _reference_point[2] = iterator.get_be_float64();
+  _to_point[0] = iterator.get_be_float64();
+  _to_point[1] = iterator.get_be_float64();
+  _to_point[2] = iterator.get_be_float64();
+  _overall_scale = iterator.get_be_float32();
+  _axis_scale = iterator.get_be_float32();
+  _angle = iterator.get_be_float32();
+
+  iterator.skip_bytes(4);   // Undocumented additional padding.
+
+  recompute_matrix();
+
+  nassertr(iterator.get_remaining_size() == 0, true);
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltTransformRotateScale::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 FltTransformRotateScale::
+build_record(FltRecordWriter &writer) const {
+  if (!FltTransformRecord::build_record(writer)) {
+    return false;
+  }
+
+  writer.set_opcode(FO_put);
+  Datagram &datagram = writer.update_datagram();
+
+  datagram.pad_bytes(4);   // Undocumented additional padding.
+
+  datagram.add_be_float64(_center[0]);
+  datagram.add_be_float64(_center[1]);
+  datagram.add_be_float64(_center[2]);
+  datagram.add_be_float64(_reference_point[0]);
+  datagram.add_be_float64(_reference_point[1]);
+  datagram.add_be_float64(_reference_point[2]);
+  datagram.add_be_float64(_to_point[0]);
+  datagram.add_be_float64(_to_point[1]);
+  datagram.add_be_float64(_to_point[2]);
+  datagram.add_be_float32(_overall_scale);
+  datagram.add_be_float32(_axis_scale);
+  datagram.add_be_float32(_angle);
+
+  datagram.pad_bytes(4);   // Undocumented additional padding.
+
+  return true;
+}
+

+ 66 - 0
pandatool/src/flt/fltTransformRotateScale.h

@@ -0,0 +1,66 @@
+// Filename: fltTransformRotateScale.h
+// Created by:  drose (30Aug00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef FLTTRANSFORMROTATESCALE_H
+#define FLTTRANSFORMROTATESCALE_H
+
+#include <pandatoolbase.h>
+
+#include "fltTransformRecord.h"
+
+////////////////////////////////////////////////////////////////////
+// 	 Class : FltTransformRotateScale
+// Description : A combination rotation and scale.  This is sometimes
+//               called "Rotate To Point" within MultiGen.
+////////////////////////////////////////////////////////////////////
+class FltTransformRotateScale : public FltTransformRecord {
+public:
+  FltTransformRotateScale(FltHeader *header);
+
+  void set(const LPoint3d &center, const LPoint3d &reference_point,
+	   const LPoint3d &to_point, bool axis_scale);
+
+  const LPoint3d &get_center() const;
+  const LPoint3d &get_reference_point() const;
+  const LPoint3d &get_to_point() const;
+  float get_overall_scale() const;
+  float get_axis_scale() const;
+  float get_angle() const;
+
+private:
+  void recompute_matrix();
+
+  LPoint3d _center;
+  LPoint3d _reference_point;
+  LPoint3d _to_point;
+  float _overall_scale;
+  float _axis_scale;
+  float _angle;
+
+protected:
+  virtual bool extract_record(FltRecordReader &reader);
+  virtual bool build_record(FltRecordWriter &writer) const;
+
+public:
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    FltTransformRecord::init_type();
+    register_type(_type_handle, "FltTransformRotateScale",
+		  FltTransformRecord::get_class_type());
+  }
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#endif
+
+

+ 133 - 0
pandatool/src/flt/fltTransformScale.cxx

@@ -0,0 +1,133 @@
+// Filename: fltTransformScale.cxx
+// Created by:  drose (30Aug00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "fltTransformScale.h"
+#include "fltRecordReader.h"
+#include "fltRecordWriter.h"
+
+TypeHandle FltTransformScale::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltTransformScale::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+FltTransformScale::
+FltTransformScale(FltHeader *header) : FltTransformRecord(header) {
+  _center.set(0.0, 0.0, 0.0);
+  _scale.set(1.0, 1.0, 1.0);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltTransformScale::set
+//       Access: Public
+//  Description: Defines the scale.
+////////////////////////////////////////////////////////////////////
+void FltTransformScale::
+set(const LPoint3d &center, const LVecBase3f &scale) {
+  _center = center;
+  _scale = scale;
+
+  recompute_matrix();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltTransformScale::get_center
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+const LPoint3d &FltTransformScale::
+get_center() const {
+  return _center;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltTransformScale::get_scale
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+const LVecBase3f &FltTransformScale::
+get_scale() const {
+  return _scale;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltTransformScale::recompute_matrix
+//       Access: Private
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void FltTransformScale::
+recompute_matrix() {
+  _matrix = 
+    LMatrix4d::translate_mat(-_center) *
+    LMatrix4d::scale_mat(LCAST(double, _scale)) *
+    LMatrix4d::translate_mat(_center);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltTransformScale::extract_record
+//       Access: Protected, Virtual
+//  Description: Fills in the information in this record 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 FltTransformScale::
+extract_record(FltRecordReader &reader) {
+  if (!FltTransformRecord::extract_record(reader)) {
+    return false;
+  }
+
+  nassertr(reader.get_opcode() == FO_scale, false);
+  DatagramIterator &iterator = reader.get_iterator();
+
+  iterator.skip_bytes(4);   // Undocumented additional padding.
+
+  _center[0] = iterator.get_be_float64();
+  _center[1] = iterator.get_be_float64();
+  _center[2] = iterator.get_be_float64();
+  _scale[0] = iterator.get_be_float32();
+  _scale[1] = iterator.get_be_float32();
+  _scale[2] = iterator.get_be_float32();
+
+  iterator.skip_bytes(4);   // Undocumented additional padding.
+
+  recompute_matrix();
+
+  nassertr(iterator.get_remaining_size() == 0, true);
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltTransformScale::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 FltTransformScale::
+build_record(FltRecordWriter &writer) const {
+  if (!FltTransformRecord::build_record(writer)) {
+    return false;
+  }
+
+  writer.set_opcode(FO_scale);
+  Datagram &datagram = writer.update_datagram();
+
+  datagram.pad_bytes(4);   // Undocumented additional padding.
+
+  datagram.add_be_float64(_center[0]);
+  datagram.add_be_float64(_center[1]);
+  datagram.add_be_float64(_center[2]);
+  datagram.add_be_float32(_scale[0]);
+  datagram.add_be_float32(_scale[1]);
+  datagram.add_be_float32(_scale[2]);
+
+  datagram.pad_bytes(4);   // Undocumented additional padding.
+
+  return true;
+}
+

+ 55 - 0
pandatool/src/flt/fltTransformScale.h

@@ -0,0 +1,55 @@
+// Filename: fltTransformScale.h
+// Created by:  drose (30Aug00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef FLTTRANSFORMSCALE_H
+#define FLTTRANSFORMSCALE_H
+
+#include <pandatoolbase.h>
+
+#include "fltTransformRecord.h"
+
+////////////////////////////////////////////////////////////////////
+// 	 Class : FltTransformScale
+// Description : A transformation that applies a (possibly nonuniform)
+//               scale.
+////////////////////////////////////////////////////////////////////
+class FltTransformScale : public FltTransformRecord {
+public:
+  FltTransformScale(FltHeader *header);
+
+  void set(const LPoint3d &center, const LVecBase3f &scale);
+
+  const LPoint3d &get_center() const;
+  const LVecBase3f &get_scale() const;
+
+private:
+  void recompute_matrix();
+
+  LPoint3d _center;
+  LVecBase3f _scale;
+
+protected:
+  virtual bool extract_record(FltRecordReader &reader);
+  virtual bool build_record(FltRecordWriter &writer) const;
+
+public:
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    FltTransformRecord::init_type();
+    register_type(_type_handle, "FltTransformScale",
+		  FltTransformRecord::get_class_type());
+  }
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#endif

+ 132 - 0
pandatool/src/flt/fltTransformTranslate.cxx

@@ -0,0 +1,132 @@
+// Filename: fltTransformTranslate.cxx
+// Created by:  drose (30Aug00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "fltTransformTranslate.h"
+#include "fltRecordReader.h"
+#include "fltRecordWriter.h"
+
+TypeHandle FltTransformTranslate::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltTransformTranslate::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+FltTransformTranslate::
+FltTransformTranslate(FltHeader *header) : FltTransformRecord(header) {
+  _from.set(0.0, 0.0, 0.0);
+  _delta.set(0.0, 0.0, 0.0);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltTransformTranslate::set
+//       Access: Public
+//  Description: Defines the translation.  The "from" point seems to
+//               be pretty much ignored.
+////////////////////////////////////////////////////////////////////
+void FltTransformTranslate::
+set(const LPoint3d &from, const LVector3d &delta) {
+  _from = from;
+  _delta = delta;
+
+  recompute_matrix();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltTransformTranslate::get_from
+//       Access: Public
+//  Description: Returns the reference point of the translation.  This
+//               is largely meaningless.
+////////////////////////////////////////////////////////////////////
+const LPoint3d &FltTransformTranslate::
+get_from() const {
+  return _from;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltTransformTranslate::get_delta
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+const LVector3d &FltTransformTranslate::
+get_delta() const {
+  return _delta;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltTransformTranslate::recompute_matrix
+//       Access: Private
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void FltTransformTranslate::
+recompute_matrix() {
+  _matrix = LMatrix4d::translate_mat(_delta);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltTransformTranslate::extract_record
+//       Access: Protected, Virtual
+//  Description: Fills in the information in this record 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 FltTransformTranslate::
+extract_record(FltRecordReader &reader) {
+  if (!FltTransformRecord::extract_record(reader)) {
+    return false;
+  }
+
+  nassertr(reader.get_opcode() == FO_translate, false);
+  DatagramIterator &iterator = reader.get_iterator();
+
+  iterator.skip_bytes(4);   // Undocumented additional padding.
+
+  _from[0] = iterator.get_be_float64();
+  _from[1] = iterator.get_be_float64();
+  _from[2] = iterator.get_be_float64();
+  _delta[0] = iterator.get_be_float64();
+  _delta[1] = iterator.get_be_float64();
+  _delta[2] = iterator.get_be_float64();
+
+  //  iterator.skip_bytes(4);   // Undocumented additional padding.
+
+  recompute_matrix();
+
+  nassertr(iterator.get_remaining_size() == 0, true);
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltTransformTranslate::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 FltTransformTranslate::
+build_record(FltRecordWriter &writer) const {
+  if (!FltTransformRecord::build_record(writer)) {
+    return false;
+  }
+
+  writer.set_opcode(FO_translate);
+  Datagram &datagram = writer.update_datagram();
+
+  datagram.pad_bytes(4);   // Undocumented additional padding.
+
+  datagram.add_be_float64(_from[0]);
+  datagram.add_be_float64(_from[1]);
+  datagram.add_be_float64(_from[2]);
+  datagram.add_be_float64(_delta[0]);
+  datagram.add_be_float64(_delta[1]);
+  datagram.add_be_float64(_delta[2]);
+
+  //  datagram.pad_bytes(4);   // Undocumented additional padding.
+
+  return true;
+}
+

+ 55 - 0
pandatool/src/flt/fltTransformTranslate.h

@@ -0,0 +1,55 @@
+// Filename: fltTransformTranslate.h
+// Created by:  drose (30Aug00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef FLTTRANSFORMTRANSLATE_H
+#define FLTTRANSFORMTRANSLATE_H
+
+#include <pandatoolbase.h>
+
+#include "fltTransformRecord.h"
+
+////////////////////////////////////////////////////////////////////
+// 	 Class : FltTransformTranslate
+// Description : A transformation that applies a (possibly nonuniform)
+//               scale.
+////////////////////////////////////////////////////////////////////
+class FltTransformTranslate : public FltTransformRecord {
+public:
+  FltTransformTranslate(FltHeader *header);
+
+  void set(const LPoint3d &from, const LVector3d &delta);
+
+  const LPoint3d &get_from() const;
+  const LVector3d &get_delta() const;
+
+private:
+  void recompute_matrix();
+
+  LPoint3d _from;
+  LVector3d _delta;
+
+protected:
+  virtual bool extract_record(FltRecordReader &reader);
+  virtual bool build_record(FltRecordWriter &writer) const;
+
+public:
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    FltTransformRecord::init_type();
+    register_type(_type_handle, "FltTransformTranslate",
+		  FltTransformRecord::get_class_type());
+  }
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#endif

+ 63 - 0
pandatool/src/flt/fltUnsupportedRecord.cxx

@@ -0,0 +1,63 @@
+// Filename: fltUnsupportedRecord.cxx
+// Created by:  drose (24Aug00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "fltUnsupportedRecord.h"
+#include "fltRecordReader.h"
+#include "fltRecordWriter.h"
+
+TypeHandle FltUnsupportedRecord::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltUnsupportedRecord::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+FltUnsupportedRecord::
+FltUnsupportedRecord(FltHeader *header) : FltRecord(header) {
+  _opcode = FO_none;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltUnsupportedRecord::output
+//       Access: Public
+//  Description: Writes a quick one-line description of the bead, but
+//               not its children.  This is a human-readable
+//               description, primarily for debugging; to write a flt
+//               file, use FltHeader::write_flt().
+////////////////////////////////////////////////////////////////////
+void FltUnsupportedRecord::
+output(ostream &out) const {
+  out << "Unsupported(" << _opcode << ")";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltUnsupportedRecord::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 FltUnsupportedRecord::
+extract_record(FltRecordReader &reader) {
+  _opcode = reader.get_opcode();
+  _datagram = reader.get_datagram();
+
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltUnsupportedRecord::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 FltUnsupportedRecord::
+build_record(FltRecordWriter &writer) const {
+  writer.set_opcode(_opcode);
+  writer.set_datagram(_datagram);
+}

+ 53 - 0
pandatool/src/flt/fltUnsupportedRecord.h

@@ -0,0 +1,53 @@
+// Filename: fltUnsupportedRecord.h
+// Created by:  drose (24Aug00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef FLTUNSUPPORTEDRECORD_H
+#define FLTUNSUPPORTEDRECORD_H
+
+#include <pandatoolbase.h>
+
+#include "fltRecord.h"
+
+#include <datagram.h>
+
+////////////////////////////////////////////////////////////////////
+// 	 Class : FltUnsupportedRecord
+// Description : 
+////////////////////////////////////////////////////////////////////
+class FltUnsupportedRecord : public FltRecord {
+public:
+  FltUnsupportedRecord(FltHeader *header);
+
+  virtual void output(ostream &out) const;
+
+protected:
+  virtual bool extract_record(FltRecordReader &reader);
+  virtual bool build_record(FltRecordWriter &writer) const;
+
+private:
+  FltOpcode _opcode;
+  Datagram _datagram;
+
+public:
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    FltRecord::init_type();
+    register_type(_type_handle, "FltUnsupportedRecord",
+		  FltRecord::get_class_type());
+  }
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#endif
+
+

+ 16 - 0
pandatool/src/flt/fltVertex.I

@@ -0,0 +1,16 @@
+// Filename: fltVertex.I
+// Created by:  drose (30Aug00)
+// 
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltVertex::has_color
+//       Access: Public
+//  Description: Returns true if the vertex has a primary color
+//               indicated, false otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool FltVertex::
+has_color() const {
+  return (_flags & F_no_color) == 0;
+}

+ 221 - 0
pandatool/src/flt/fltVertex.cxx

@@ -0,0 +1,221 @@
+// Filename: fltVertex.cxx
+// Created by:  drose (25Aug00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "fltVertex.h"
+#include "fltRecordReader.h"
+#include "fltRecordWriter.h"
+#include "fltHeader.h"
+
+TypeHandle FltVertex::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltVertex::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+FltVertex::
+FltVertex(FltHeader *header) : FltRecord(header) {
+  _color_name_index = 0;
+  _flags = 0;
+  _pos.set(0.0, 0.0, 0.0);
+  _normal.set(0.0, 0.0, 0.0);
+  _uv.set(0.0, 0.0);
+  _color_index = 0;
+
+  _has_normal = false;
+  _has_uv = false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltVertex::get_opcode
+//       Access: Public
+//  Description: Returns the opcode that this record will be written
+//               as.
+////////////////////////////////////////////////////////////////////
+FltOpcode FltVertex::
+get_opcode() const {
+  if (_has_normal) {
+    if (_has_uv) {
+      return FO_vertex_cnu;
+    } else {
+      return FO_vertex_cn;
+    }
+  } else {
+    if (_has_uv) {
+      return FO_vertex_cu;
+    } else {
+      return FO_vertex_c;
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltVertex::get_record_length
+//       Access: Public
+//  Description: Returns the length of this record in bytes as it will
+//               be written to the flt file.
+////////////////////////////////////////////////////////////////////
+int FltVertex::
+get_record_length() const {
+  switch (get_opcode()) {
+  case FO_vertex_c:
+    return 40;
+
+  case FO_vertex_cn:
+    return 52;
+
+  case FO_vertex_cnu:
+    return 60;
+
+  case FO_vertex_cu:
+    return 48;
+
+  default:
+    nassertr(false, 0);
+  }
+
+  return 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltVertex::get_color
+//       Access: Public
+//  Description: If has_color() indicates true, returns the primary
+//               color of the face, as a four-component value.  In the
+//               case of a vertex, the alpha channel will always be
+//               1.0, as MultiGen does not store transparency
+//               per-vertex.
+////////////////////////////////////////////////////////////////////
+Colorf FltVertex::
+get_color() const {
+  nassertr(has_color(), Colorf(0.0, 0.0, 0.0, 0.0));
+
+  return _header->get_color(_color_index, (_flags & F_packed_color) != 0,
+			    _packed_color, 0);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltVertex::get_rgb
+//       Access: Public
+//  Description: If has_color() indicates true, returns the primary
+//               color of the face, as a three-component value
+//               ignoring transparency.
+////////////////////////////////////////////////////////////////////
+RGBColorf FltVertex::
+get_rgb() const {
+  nassertr(has_color(), RGBColorf(0.0, 0.0, 0.0));
+
+  return _header->get_rgb(_color_index, (_flags & F_packed_color) != 0,
+			  _packed_color);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltVertex::extract_record
+//       Access: Protected, Virtual
+//  Description: Fills in the information in this record 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 FltVertex::
+extract_record(FltRecordReader &reader) {
+  if (!FltRecord::extract_record(reader)) {
+    return false;
+  }
+
+  switch (reader.get_opcode()) {
+  case FO_vertex_c:
+    _has_normal = false;
+    _has_uv = false;
+    break;
+
+  case FO_vertex_cn:
+    _has_normal = true;
+    _has_uv = false;
+    break;
+
+  case FO_vertex_cnu:
+    _has_normal = true;
+    _has_uv = true;
+    break;
+
+  case FO_vertex_cu:
+    _has_normal = false;
+    _has_uv = true;
+    break;
+
+  default:
+    nassertr(false, false);
+  }
+
+  DatagramIterator &iterator = reader.get_iterator();
+
+  _color_name_index = iterator.get_be_int16();
+  _flags = iterator.get_be_uint16();
+  _pos[0] = iterator.get_be_float64();
+  _pos[1] = iterator.get_be_float64();
+  _pos[2] = iterator.get_be_float64();
+
+  if (_has_normal) {
+    _normal[0] = iterator.get_be_float32();
+    _normal[1] = iterator.get_be_float32();
+    _normal[2] = iterator.get_be_float32();
+  }
+  if (_has_uv) {
+    _uv[0] = iterator.get_be_float32();
+    _uv[1] = iterator.get_be_float32();
+  }
+
+  if (!_packed_color.extract_record(reader)) {
+    return false;
+  }
+  _color_index = iterator.get_be_uint32();
+
+  nassertr(iterator.get_remaining_size() == 0, true);
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltVertex::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 FltVertex::
+build_record(FltRecordWriter &writer) const {
+  if (!FltRecord::build_record(writer)) {
+    return false;
+  }
+  
+  writer.set_opcode(get_opcode());
+  Datagram &datagram = writer.update_datagram();
+
+  datagram.add_be_int16(_color_name_index);
+  datagram.add_be_uint16(_flags);
+  datagram.add_be_float64(_pos[0]);
+  datagram.add_be_float64(_pos[1]);
+  datagram.add_be_float64(_pos[2]);
+
+  if (_has_normal) {
+    datagram.add_be_float32(_normal[0]);
+    datagram.add_be_float32(_normal[1]);
+    datagram.add_be_float32(_normal[2]);
+  }
+  if (_has_uv) {
+    datagram.add_be_float32(_uv[0]);
+    datagram.add_be_float32(_uv[1]);
+  }
+
+  if (!_packed_color.build_record(writer)) {
+    return false;
+  }
+
+  datagram.add_be_uint32(_color_index);
+
+  nassertr(datagram.get_length() == get_record_length() - 4, true);
+  return true;
+}

+ 85 - 0
pandatool/src/flt/fltVertex.h

@@ -0,0 +1,85 @@
+// Filename: fltVertex.h
+// Created by:  drose (25Aug00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef FLTVERTEX_H
+#define FLTVERTEX_H
+
+#include <pandatoolbase.h>
+
+#include "fltRecord.h"
+#include "fltPackedColor.h"
+
+#include <luse.h>
+
+////////////////////////////////////////////////////////////////////
+// 	 Class : FltVertex
+// Description : Represents a single vertex in the vertex palette.
+//               Flt files index vertices by their byte offset in the
+//               vertex palette; within this library, we map those
+//               byte offsets to pointers automatically.
+//
+//               This may represent a vertex with or without a normal
+//               or texture coordinates.
+////////////////////////////////////////////////////////////////////
+class FltVertex : public FltRecord {
+public:
+  FltVertex(FltHeader *header);
+
+  FltOpcode get_opcode() const;
+  int get_record_length() const;
+
+  enum Flags {
+    F_hard_edge         = 0x80000000,
+    F_normal_frozen     = 0x40000000,
+    F_no_color          = 0x20000000,
+    F_packed_color      = 0x10000000
+  };
+
+  int _color_name_index;
+  unsigned int _flags;
+  LPoint3d _pos;
+  LPoint3f _normal;
+  LPoint2f _uv;
+  FltPackedColor _packed_color;
+  int _color_index;
+
+  bool _has_normal;
+  bool _has_uv;
+
+public:
+  INLINE bool has_color() const;
+  Colorf get_color() const;
+  RGBColorf get_rgb() const;
+
+
+protected:
+  virtual bool extract_record(FltRecordReader &reader);
+  virtual bool build_record(FltRecordWriter &writer) const;
+
+public:
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    FltRecord::init_type();
+    register_type(_type_handle, "FltVertex",
+		  FltRecord::get_class_type());
+  }
+
+private:
+  static TypeHandle _type_handle;
+
+  friend class FltHeader;
+};
+
+#include "fltVertex.I"
+
+#endif
+
+

+ 128 - 0
pandatool/src/flt/fltVertexList.cxx

@@ -0,0 +1,128 @@
+// Filename: fltVertexList.cxx
+// Created by:  drose (25Aug00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "fltVertexList.h"
+#include "fltRecordReader.h"
+#include "fltRecordWriter.h"
+#include "fltHeader.h"
+
+TypeHandle FltVertexList::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltVertexList::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+FltVertexList::
+FltVertexList(FltHeader *header) : FltRecord(header) {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltVertexList::get_num_vertices
+//       Access: Public
+//  Description: Returns the number of vertices in this vertex list.
+////////////////////////////////////////////////////////////////////
+int FltVertexList::
+get_num_vertices() const {
+  return _vertices.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltVertexList::get_vertex
+//       Access: Public
+//  Description: Returns the nth vertex of this vertex list.
+////////////////////////////////////////////////////////////////////
+FltVertex *FltVertexList::
+get_vertex(int n) const {
+  nassertr(n >= 0 && n < (int)_vertices.size(), 0);
+  return _vertices[n];
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltVertexList::clear_vertices
+//       Access: Public
+//  Description: Removes all vertices from this vertex list.
+////////////////////////////////////////////////////////////////////
+void FltVertexList::
+clear_vertices() {
+  _vertices.clear();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltVertexList::add_vertex
+//       Access: Public
+//  Description: Adds a new vertex to the end of the vertex list.
+//               Care must be taken to ensure the vertex is also added
+//               to the vertex palette.
+////////////////////////////////////////////////////////////////////
+void FltVertexList::
+add_vertex(FltVertex *vertex) {
+  _vertices.push_back(vertex);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltVertexList::output
+//       Access: Public
+//  Description: Writes a quick one-line description of the record, but
+//               not its children.  This is a human-readable
+//               description, primarily for debugging; to write a flt
+//               file, use FltHeader::write_flt().
+////////////////////////////////////////////////////////////////////
+void FltVertexList::
+output(ostream &out) const {
+  out << _vertices.size() << " vertices";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltVertexList::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 FltVertexList::
+extract_record(FltRecordReader &reader) {
+  if (!FltRecord::extract_record(reader)) {
+    return false;
+  }
+
+  nassertr(reader.get_opcode() == FO_vertex_list, false);
+  DatagramIterator &iterator = reader.get_iterator();
+
+  _vertices.clear();
+  while (iterator.get_remaining_size() >= 4) {
+    int vertex_offset = iterator.get_be_int32();
+    _vertices.push_back(_header->get_vertex_by_offset(vertex_offset));
+  }
+
+  nassertr(iterator.get_remaining_size() == 0, true);
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltVertexList::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 FltVertexList::
+build_record(FltRecordWriter &writer) const {
+  if (!FltRecord::build_record(writer)) {
+    return false;
+  }
+
+  writer.set_opcode(FO_vertex_list);
+  Datagram &datagram = writer.update_datagram();
+
+  Vertices::const_iterator vi;
+  for (vi = _vertices.begin(); vi != _vertices.end(); ++vi) {
+    datagram.add_be_uint32(_header->get_offset_by_vertex(*vi));
+  }
+
+  return true;
+}

+ 61 - 0
pandatool/src/flt/fltVertexList.h

@@ -0,0 +1,61 @@
+// Filename: fltVertexList.h
+// Created by:  drose (25Aug00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef FLTVERTEXLIST_H
+#define FLTVERTEXLIST_H
+
+#include <pandatoolbase.h>
+
+#include "fltRecord.h"
+#include "fltPackedColor.h"
+#include "fltVertex.h"
+
+#include <pointerTo.h>
+
+////////////////////////////////////////////////////////////////////
+// 	 Class : FltVertexList
+// Description : A list of vertices, typically added as a child of a
+//               face bead.
+////////////////////////////////////////////////////////////////////
+class FltVertexList : public FltRecord {
+public:
+  FltVertexList(FltHeader *header);
+
+  int get_num_vertices() const;
+  FltVertex *get_vertex(int n) const;
+  void clear_vertices();
+  void add_vertex(FltVertex *vertex);
+
+  virtual void output(ostream &out) const;
+
+protected:
+  virtual bool extract_record(FltRecordReader &reader);
+  virtual bool build_record(FltRecordWriter &writer) const;
+
+private:
+  typedef vector<PT(FltVertex)> Vertices;
+  Vertices _vertices;
+
+public:
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    FltRecord::init_type();
+    register_type(_type_handle, "FltVertexList",
+		  FltRecord::get_class_type());
+  }
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#endif
+
+

Some files were not shown because too many files changed in this diff