Browse Source

*** empty log message ***

David Rose 24 years ago
parent
commit
1892ffa1d0

+ 8 - 3
panda/src/egg/eggData.cxx

@@ -32,9 +32,14 @@ TypeHandle EggData::_type_handle;
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool EggData::
 bool EggData::
 resolve_egg_filename(Filename &egg_filename, const DSearchPath &searchpath) {
 resolve_egg_filename(Filename &egg_filename, const DSearchPath &searchpath) {
-  egg_filename.resolve_filename(searchpath, "egg");
-  egg_filename.resolve_filename(get_egg_path(), "egg");
-  egg_filename.resolve_filename(get_model_path(), "egg");
+  if (egg_filename.is_fully_qualified() && egg_filename.exists()) {
+    return true;
+  }
+
+  egg_filename.resolve_filename(searchpath, "egg") ||
+    egg_filename.resolve_filename(get_egg_path(), "egg") ||
+    egg_filename.resolve_filename(get_model_path(), "egg");
+
   return egg_filename.exists();
   return egg_filename.exists();
 }
 }
 
 

+ 6 - 28
panda/src/putil/bamReader.cxx

@@ -45,7 +45,8 @@ BamReader::
 //     Function: BamReader::init
 //     Function: BamReader::init
 //       Access: Public
 //       Access: Public
 //  Description: Initializes the BamReader prior to reading any
 //  Description: Initializes the BamReader prior to reading any
-//               objects from its source.
+//               objects from its source.  This includes reading the
+//               Bam header.
 //
 //
 //               This returns true if the BamReader successfully
 //               This returns true if the BamReader successfully
 //               initialized, false otherwise.
 //               initialized, false otherwise.
@@ -277,7 +278,7 @@ TypeHandle BamReader::
 read_handle(DatagramIterator &scan) {
 read_handle(DatagramIterator &scan) {
   // We encode TypeHandles within the Bam file by writing a unique
   // We encode TypeHandles within the Bam file by writing a unique
   // index number for each one to the file.  When we write a
   // index number for each one to the file.  When we write a
-  // particular TypeHandle for the first type, we assign it a new
+  // particular TypeHandle for the first time, we assign it a new
   // index number and then immediately follow it by its definition;
   // index number and then immediately follow it by its definition;
   // when we write the same TypeHandle on subsequent times we only
   // when we write the same TypeHandle on subsequent times we only
   // write the index number.
   // write the index number.
@@ -291,15 +292,6 @@ read_handle(DatagramIterator &scan) {
   
   
   if (id == 0) {
   if (id == 0) {
     // Index number 0 is always, by convention, TypeHandle::none().
     // Index number 0 is always, by convention, TypeHandle::none().
-
-    // This indicates an object that should have already been read in,
-    // so return TypeHandle::none() to indicate this.
-#ifndef NDEBUG
-    if (bam_cat.is_spam()) {
-      bam_cat.spam()
-	<< "Read TypeHandle::none().\n";
-    }
-#endif
     return TypeHandle::none();
     return TypeHandle::none();
   }
   }
 
 
@@ -309,13 +301,6 @@ read_handle(DatagramIterator &scan) {
     // no type definition following the id.  Simply return the
     // no type definition following the id.  Simply return the
     // TypeHandle we previously associated with the id.
     // TypeHandle we previously associated with the id.
     TypeHandle type = (*mi).second;
     TypeHandle type = (*mi).second;
-
-#ifndef NDEBUG
-    if (bam_cat.is_spam()) {
-      bam_cat.spam()
-	<< "Read TypeHandle for " << type << ".\n";
-    }
-#endif
     return type;
     return type;
   }
   }
 
 
@@ -510,8 +495,8 @@ get_pta(DatagramIterator &scan) {
     return (void *)NULL;
     return (void *)NULL;
   }
   }
 
 
-  PTAMap::iterator pi = _ptamap.find(id);
-  if (pi == _ptamap.end()) {
+  PTAMap::iterator pi = _pta_map.find(id);
+  if (pi == _pta_map.end()) {
     // This is the first time we've encountered this particular ID,
     // This is the first time we've encountered this particular ID,
     // meaning we need to read the data now and register it.
     // meaning we need to read the data now and register it.
     _pta_id = id;
     _pta_id = id;
@@ -537,7 +522,7 @@ get_pta(DatagramIterator &scan) {
 void BamReader::
 void BamReader::
 register_pta(void *ptr) {
 register_pta(void *ptr) {
   if (_pta_id != -1) {
   if (_pta_id != -1) {
-    bool inserted = _ptamap.insert(PTAMap::value_type(_pta_id, ptr)).second;
+    bool inserted = _pta_map.insert(PTAMap::value_type(_pta_id, ptr)).second;
     _pta_id = -1;
     _pta_id = -1;
     nassertv(inserted);
     nassertv(inserted);
   }
   }
@@ -640,13 +625,6 @@ p_read_object() {
     }
     }
   }
   }
 
 
-#ifndef NDEBUG
-  if (bam_cat.is_spam()) {
-    bam_cat.spam()
-      << "Emptying queue.\n";
-  }
-#endif
-
   return object_id;
   return object_id;
 }
 }
 
 

+ 4 - 3
panda/src/putil/bamReader.h

@@ -18,7 +18,7 @@
 #include <algorithm>
 #include <algorithm>
 
 
 
 
-//Useful define for reading pta's
+// A handy macro for reading PointerToArrays.
 #define READ_PTA(Manager, source, Read_func, array)   \
 #define READ_PTA(Manager, source, Read_func, array)   \
 {                                                     \
 {                                                     \
   void *t;                                            \
   void *t;                                            \
@@ -44,7 +44,8 @@
 //               that inherits, directly or indirectly, from
 //               that inherits, directly or indirectly, from
 //               TypedWriteable.  The objects may include pointers to
 //               TypedWriteable.  The objects may include pointers to
 //               other objects within the Bam file; the BamReader
 //               other objects within the Bam file; the BamReader
-//               automatically manages these and restores the pointers
+//               automatically manages these (with help from code
+//               within each class) and restores the pointers
 //               correctly.
 //               correctly.
 //
 //
 //               This is the abstract interface and does not
 //               This is the abstract interface and does not
@@ -148,7 +149,7 @@ private:
   // These are used by get_pta() and register_pta() to unify multiple
   // These are used by get_pta() and register_pta() to unify multiple
   // references to the same PointerToArray.
   // references to the same PointerToArray.
   typedef map<int, void *> PTAMap;
   typedef map<int, void *> PTAMap;
-  PTAMap _ptamap;
+  PTAMap _pta_map;
   int _pta_id;
   int _pta_id;
 
 
   int _file_major, _file_minor;
   int _file_major, _file_minor;

+ 0 - 14
panda/src/putil/bamWriter.I

@@ -1,17 +1,3 @@
 // Filename: bamWriter.I
 // Filename: bamWriter.I
 // Created by:  jason (08Jun00)
 // Created by:  jason (08Jun00)
 //
 //
-
-////////////////////////////////////////////////////////////////////
-//     Function: BamWriter::Constructor
-//       Access: Public
-//  Description: 
-////////////////////////////////////////////////////////////////////
-INLINE BamWriter::
-BamWriter(DatagramSink *sink) : 
-     //_current needs to start from 1, as a value of 0
-     //for a unique object ID will be used to indicate a null pointer
-     _target(sink), _writing(false), _current(1),
-     _current_pta(1), _emptying(false)
-{
-}

+ 250 - 192
panda/src/putil/bamWriter.cxx

@@ -10,59 +10,43 @@
 #include "bamWriter.h"
 #include "bamWriter.h"
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: BamWriter::Destructor
+//     Function: BamWriter::Constructor
 //       Access: Public
 //       Access: Public
 //  Description: 
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-BamWriter::~BamWriter(void)
+BamWriter::
+BamWriter(DatagramSink *sink) : 
+  _target(sink)
 {
 {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: BamWriter::write_handle
+//     Function: BamWriter::Destructor
 //       Access: Public
 //       Access: Public
-//  Description: Writes a type handle into the file.  If the handle 
-//               has already been encountered then a number 
-//               identifying that handle is written instead.  
+//  Description: 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-void BamWriter::
-write_handle(Datagram &packet, TypeHandle type)
-{
-  //No class should have a type handle index of 0
-  nassertv(type.get_index() != 0);
-
-  packet.add_uint16(type.get_index());
-  if (_type_map.find(type.get_index()) == _type_map.end())
-  {
-    // This is the first time this TypeHandle has been written, so
-    // also write out its name.
-    _type_map.insert(type.get_index()); 
-    packet.add_string(type.get_name());
-
-    // We also need to write the derivation of the TypeHandle, in case
-    // the program reading this file later has never heard of this
-    // type before.
-    int num_parent_classes = type.get_num_parent_classes();
-    nassertv(num_parent_classes <= 255);  // Good grief!
-    packet.add_uint8(num_parent_classes);
-    for (int i = 0; i < num_parent_classes; i++) {
-      write_handle(packet, type.get_parent_class(i));
-    }
-  }
+BamWriter::
+~BamWriter() {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: BamWriter::init
 //     Function: BamWriter::init
 //       Access: Public
 //       Access: Public
-//  Description: This function initializes the BamWriter, setting 
-//               up whatever needs to be set up before the BamWriter
-//               is ready to write out objects.  It returns true if
-//               successful, false on failure.
+//  Description: Initializes the BamWriter prior to writing any
+//               objects to its output stream.  This includes writing
+//               out the Bam header.
+//
+//               This returns true if the BamWriter successfully
+//               initialized, false otherwise.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool BamWriter::
 bool BamWriter::
-init(void)
-{
-  //Write out the current major and minor BAM file version numbers
+init() {
+  // Initialize the next object and PTA ID's.  These start counting at
+  // 1, since 0 is reserved for NULL.
+  _next_object_id = 1;
+  _next_pta_id = 1;
+
+  // Write out the current major and minor BAM file version numbers.
   Datagram header;
   Datagram header;
 
 
   header.add_uint16(_bam_major_ver);
   header.add_uint16(_bam_major_ver);
@@ -79,205 +63,279 @@ init(void)
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: BamWriter::write_object
 //     Function: BamWriter::write_object
 //       Access: Public
 //       Access: Public
-//  Description: Main function for performing the processing of 
-//               writing an object to some Binary target.  Combined
-//               with write_pointer this function can correctly handle
-//               and break circular references, while still writing out
-//               the necessary information for re-constructing those
-//               references when the objects are read back in.
+//  Description: Writes a single object to the Bam file, so that the
+//               BamReader::read_object() can later correctly restore
+//               the object and all its pointers.
+//
+//               This implicitly also writes any additional objects
+//               this object references (if they haven't already been
+//               written), so that pointers may be fully resolved.
+//
+//               This may be called repeatedly to write a sequence of
+//               objects to the Bam file, but typically (especially
+//               for scene graph files, indicated with the .bam
+//               extension), only one object is written directly from
+//               the Bam file: the root of the scene graph.  The
+//               remaining objects will all be written recursively by
+//               the first object.
 //
 //
 //               Returns true if the object is successfully written,
 //               Returns true if the object is successfully written,
 //               false otherwise.
 //               false otherwise.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool BamWriter::
 bool BamWriter::
-write_object(TypedWriteable* obj) 
-{
-  Datagram objData;
-
-  nassertr(obj != TypedWriteable::Null, false);
-
-  //No object should ever be written out that is not
-  //registered as a child of TypedWriteable.  The 
-  //only way this can get in here and have that true is
-  //if someone forgot to set the classes init_type correctly.
-  //So nassert on that to make it easy for them to track
-  //down the error, because that will be an error, but one
-  //that won't show up on the writing, it will show up in
-  //reading, so could be potentially difficult to track down
-  nassertr(obj->is_of_type(TypedWriteable::get_class_type()), false);
-
-  //Need to keep track of the state of any objects written
-  //or queued.  So every time write_object is called,
-  //check to see if has been encountered before, if not
-  //insert it into a map with a Key of the pointer to 
-  //the object and the value being a class that contains
-  //the unigue object ID for that object and a flag of
-  //whether it has been written yet or no   
-  if (_statemap.find(obj) == _statemap.end())
-  {
-   //If the object is not already in the map, then
-    //add it and assign it and unique object number
-    _statemap[obj].objId = _current;
-    _current++;
-    //We are making the assumption that there will never
-    //be more than the max of an unsigned short in objects
-    //in one file, but just in case, this nassert will make
-    //it easy to find that possible error
-    nassertr(_current != 0, false);
-  }
+write_object(TypedWriteable *object) {
+  nassertr(_object_queue.empty(), false);
 
 
-  bool okflag = true;
+  int object_id = enqueue_object(object);
+  nassertr(object_id != 0, false);
 
 
-  if (_writing) 
-  {
-    enqueue(obj);
-  }
-  else 
-  {
-    _writing = true;
-
-    if (!_statemap[obj].written)
-    {
-      //Write the type handle of the object
-      write_handle(objData, obj->get_type());
-      //Write the unique ID of the object into the datagram
-      //so that when it is read back, and there is a back
-      //reference to this object, we know what indices correspond
-      //to what objects
-      objData.add_uint16(_statemap[obj].objId);
-      obj->write_datagram(this, objData);
-      _statemap[obj].written = true;
-    }
-    else
-    {
-      //If it is in the map, then we don't want to try
-      //and write it again, so write the unique ID of the
-      //object into the datagram so that when we read in 
-      //later, we can resolve back references.  Write
-      //0 for the type handle to identify back references
-      objData.add_uint16(0);
-      objData.add_uint16(_statemap[obj].objId);
+  // Now we write out all the objects in the queue, in order.  The
+  // first one on the queue will, of course, be this object we just
+  // queued up, but each object we write may append more to the queue.
+  while (!_object_queue.empty()) {
+    object = _object_queue.front();
+    _object_queue.pop_front();
+
+    // Look up the object in the map.  It had better be there!
+    StateMap::iterator si = _state_map.find(object);
+    nassertr(si != _state_map.end(), false);
+
+    int object_id = (*si).second._object_id;
+    bool already_written = (*si).second._written;
+
+    Datagram dg;
+
+    if (!already_written) {
+      // The first time we write a particular object, we do so by
+      // writing its TypeHandle (which had better not be
+      // TypeHandle::none(), since that's our code for a
+      // previously-written object), followed by the object ID number,
+      // followed by the object definition.
+
+      TypeHandle type = object->get_type();
+      nassertr(type != TypeHandle::none(), false);
+
+      write_handle(dg, type);
+      dg.add_uint16(object_id);
+
+      object->write_datagram(this, dg);
+      (*si).second._written = true;
+
+    } else {
+      // On subsequent times when we write a particular object, we
+      // write simply TypeHandle::none(), followed by the object ID.
+      // The occurrence of TypeHandle::none() is an indicator to the
+      // BamReader that this is a previously-written object.
+
+      write_handle(dg, TypeHandle::none());
+      dg.add_uint16(object_id);
     }
     }
 
 
-    if (!_target->put_datagram(objData)) {
+    if (!_target->put_datagram(dg)) {
       util_cat.error() 
       util_cat.error() 
 	<< "Unable to write datagram to file.\n";
 	<< "Unable to write datagram to file.\n";
-      okflag = false;
-    }
-    _writing = false;
-    if (!_emptying)
-    {
-      if (!empty_queue()) {
-	okflag = false;
-      }
+      return false;
     }
     }
   }
   }
 
 
-  return okflag;
+  return true;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: BamWriter::write_pointer
 //     Function: BamWriter::write_pointer
 //       Access: Public
 //       Access: Public
-//  Description: Utility function to be called by the objects writing
-//               themselves to a Datagram.  Basically amounts to a
-//               request to BamWriter to queue an object to be written
-//               and to write into the Datagram the necessary information
-//               to reference that object
+//  Description: The interface for writing a pointer to another object
+//               to a Bam file.  This is intended to be called by the
+//               various objects that write themselves to the Bam
+//               file, within the write_datagram() method.
+//
+//               This writes the pointer out in such a way that the
+//               BamReader will be able to restore the pointer later.
+//               If the pointer is to an object that has not yet
+//               itself been written to the Bam file, that object will
+//               automatically be written.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void BamWriter::
 void BamWriter::
-write_pointer(Datagram &packet , TypedWriteable *dest)
-{
-  //Write a zero for the object ID if the pointer is null
-  if (dest == TypedWriteable::Null)
-  {
+write_pointer(Datagram &packet, TypedWriteable *object) {
+  // If the pointer is NULL, we always simply write a zero for an
+  // object ID and leave it at that.
+  if (object == (TypedWriteable *)NULL) {
     packet.add_uint16(0);
     packet.add_uint16(0);
-  }
-  else
-  {
-    if (_statemap.find(dest) == _statemap.end())
-    {
-      write_object(dest);
+
+  } else {
+    StateMap::iterator si = _state_map.find(object);
+    if (si == _state_map.end()) {
+      // We have not written this pointer out yet.  This means we must
+      // queue the object definition up for later.
+      int object_id = enqueue_object(object);
+      packet.add_uint16(object_id);
+
+    } else {
+      // We have already assigned this pointer an ID; thus, we can
+      // simply write out the ID.
+      packet.add_uint16((*si).second._object_id);
     }
     }
-    
-    packet.add_uint16(_statemap[dest].objId);
   }
   }
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: BamWriter::register_pta
 //     Function: BamWriter::register_pta
 //       Access: Public
 //       Access: Public
-//  Description: Utility function to be called by the objects writing
-//               themselves to a Datagram. Registers a PTA to be written
-//               into the Datagram, and allows for shared references
-//               to be captured in the datagram
+//  Description: Prepares to write a PointerToArray to the Bam file,
+//               unifying references to the same pointer across the
+//               Bam file.
+//
+//               The writing object should call this prior to writing
+//               out a PointerToArray.  It will return true if the
+//               same pointer has been previously, in which case the
+//               writing object need do nothing further; or it will
+//               return false if this particular pointer has not yet
+//               been written, in which case the writing object must
+//               then write out the contents of the array.
+//
+//               Also see the WRITE_PTA() macro, which consolidates
+//               the work that must be done to write a PTA.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool BamWriter::
 bool BamWriter::
-register_pta(Datagram &packet, void *ptr)
-{
-  if (ptr != (void*)NULL)
-  {
-    if (_ptamap.find(ptr) == _ptamap.end())
-    {
-      _ptamap[ptr] = _current_pta;
-      packet.add_uint16(_current_pta);
-      _current_pta++;
-      return false;
-    }
-
-    packet.add_uint16(_ptamap[ptr]);
-  }
-  else
-  {
-    //A zero for the PTA ID indicates a NULL ptr
+register_pta(Datagram &packet, void *ptr) {
+  if (ptr == (void *)NULL) {
+    // A zero for the PTA ID indicates a NULL pointer.  This is a
+    // special case.
     packet.add_uint16(0);
     packet.add_uint16(0);
+
+    // We return false to indicate the user must now write out the
+    // "definition" of the NULL pointer.  This is necessary because of
+    // a quirk in the BamReader's design, which forces callers to read
+    // the definition of every NULL pointer.  Presumably, the caller
+    // will be able to write the definition in a concise way that will
+    // clearly indicate a NULL pointer; in the case of a
+    // PointerToArray, this will generally be simply a zero element
+    // count.
     return false;
     return false;
   }  
   }  
 
 
-  return true;
+  PTAMap::iterator pi = _pta_map.find(ptr);
+  if (pi == _pta_map.end()) {
+    // We have not encountered this pointer before.
+    int pta_id = _next_pta_id;
+    _next_pta_id++;
+    
+    // Make sure our PTA ID will fit within the PN_uint16 we have
+    // allocated for it.
+    nassertr(pta_id <= 65535, 0);
+    
+    bool inserted = _pta_map.insert(PTAMap::value_type(ptr, pta_id)).second;
+    nassertr(inserted, false);
+    
+    packet.add_uint16(pta_id);
+    
+    // Return false to indicate the caller must now write out the
+    // array definition.
+    return false;
+    
+  } else {
+    // We have encountered this pointer before.
+    int pta_id = (*pi).second;
+    packet.add_uint16(pta_id);
+    
+    // Return true to indicate the caller need do nothing further.
+    return true;
+  }
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: BamWriter::enqueue
-//       Access: Private
-//  Description: Queue an object to be written
+//     Function: BamWriter::write_handle
+//       Access: Public
+//  Description: Writes a TypeHandle to the file in such a way that
+//               the BamReader can read the same TypeHandle later via
+//               read_handle().
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void BamWriter::
 void BamWriter::
-enqueue(TypedWriteable *obj)
-{
-  _deferred.push_back(obj);
+write_handle(Datagram &packet, TypeHandle type) {
+  // We encode TypeHandles within the Bam file by writing a unique
+  // index number for each one to the file.  When we write a
+  // particular TypeHandle for the first time, we assign it a new
+  // index number and then immediately follow it by its definition;
+  // when we write the same TypeHandle on subsequent times we only
+  // write the index number.
+
+  // The unique number we choose is actually the internal index number
+  // of the TypeHandle.  Why not?
+  int index = type.get_index();
+
+  // Also make sure the index number fits within a PN_uint16.
+  nassertv(index <= 65535);
+
+  packet.add_uint16(index);
+
+  if (index != 0) {
+    bool inserted = _types_written.insert(index).second;
+
+    if (inserted) {
+      // This is the first time this TypeHandle has been written, so
+      // also write out its definition.
+      packet.add_string(type.get_name());
+      
+      // We also need to write the derivation of the TypeHandle, in case
+      // the program reading this file later has never heard of this
+      // type before.
+      int num_parent_classes = type.get_num_parent_classes();
+      nassertv(num_parent_classes <= 255);  // Good grief!
+      packet.add_uint8(num_parent_classes);
+      for (int i = 0; i < num_parent_classes; i++) {
+	write_handle(packet, type.get_parent_class(i));
+      }
+    }
+  }
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: BamWriter::empty_queue
+//     Function: BamWriter::enqueue_object
 //       Access: Private
 //       Access: Private
-//  Description: For each object in the queue call write_object on it.
-//               Returns true of all write_object calls returned true,
-//               false otherwise.
+//  Description: Assigns an object ID to the object and queues it up
+//               for later writing to the Bam file.  
+//
+//               The return value is the object ID, or 0 if there is
+//               an error.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-bool BamWriter::
-empty_queue(void)
-{
-  _emptying = true;
-  bool okflag = true;
-  while(!_deferred.empty())
-  {
-    if (!write_object(_deferred.front())) {
-      okflag = false;
-    }
-    _deferred.pop_front();
+int BamWriter::
+enqueue_object(TypedWriteable *object) {
+  Datagram dg;
+
+  nassertr(object != TypedWriteable::Null, 0);
+
+  // No object should ever be written out that is not registered as a
+  // child of TypedWriteable.  The only way this can happen is if
+  // someone failed to initialize their type correctly in init_type().
+  nassertr(object->is_of_type(TypedWriteable::get_class_type()), 0);
+
+  // We need to assign a unique index number to every object we write
+  // out.  Has this object been assigned a number yet?
+  int object_id;
+  bool already_written;
+
+  StateMap::iterator si = _state_map.find(object);
+  if (si == _state_map.end()) {
+    // No, it hasn't, so assign it the next number in sequence
+    // arbitrarily.
+    object_id = _next_object_id;
+    already_written = false;
+    
+    // Make sure our object ID will fit within the PN_uint16 we have
+    // allocated for it.
+    nassertr(object_id <= 65535, 0);
+
+    bool inserted = 
+      _state_map.insert(StateMap::value_type(object, StoreState(_next_object_id))).second;
+    nassertr(inserted, false);
+    _next_object_id++;
+
+  } else {
+    // Yes, it has; get the object ID.
+    object_id = (*si).second._object_id;
+    already_written = (*si).second._written;
   }
   }
-  _emptying = false;
-  return okflag;
-}
-
-
-
-
-
-
-
-
-
-
 
 
+  _object_queue.push_back(object);
+  return object_id;
+}

+ 75 - 42
panda/src/putil/bamWriter.h

@@ -12,6 +12,7 @@
 #include "datagramSink.h"
 #include "datagramSink.h"
 #include <deque>
 #include <deque>
 
 
+// A handy macro for writing PointerToArrays.
 #define WRITE_PTA(Manager, dest, Write_func, array)  \
 #define WRITE_PTA(Manager, dest, Write_func, array)  \
   if (!Manager->register_pta(dest, array.p()))       \
   if (!Manager->register_pta(dest, array.p()))       \
   {                                                  \
   {                                                  \
@@ -21,60 +22,92 @@
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 // 	 Class : BamWriter
 // 	 Class : BamWriter
-// Description : This class manages all aspects of writing data 
-//               structures to some Binary form.  It writes to a 
-//               a DatagramSink which is an abstraction that could
-//               be a file, the net, etc...   The two basic functions
-//               used are write_object and write_pointer.  write_object
-//               is called to write any TypedWriteable object out.  
-//               BamWriter actually asks the object passed to write
-//               itself out to a datagram, and then it writes that
-//               datagram to the DatagramSink.   write_pointer is called
-//               by the objects themselves to resolve writing out 
-//               pointers to other objects.  BamWriter will handle all
-//               circular references correctly
+// Description : This is the fundamental interface for writing binary
+//               objects to a Bam file, to be extracted later by a
+//               BamReader.
+//
+//               A Bam file can be thought of as a linear collection
+//               of objects.  Each object is an instance of a class
+//               that inherits, directly or indirectly, from
+//               TypedWriteable.  The objects may include pointers to
+//               other objects; the BamWriter automatically manages
+//               these (with help from code within each class) and
+//               writes all referenced objects to the file in such a
+//               way that the pointers may be correctly restored
+//               later.
+//
+//               This is the abstract interface and does not
+//               specifically deal with disk files, but rather with a
+//               DatagramSink of some kind, which simply accepts a
+//               linear stream of Datagrams.  It is probably written
+//               to a disk file, but it might conceivably be streamed
+//               directly to a network or some such nonsense.
+//
+//               Bam files are most often used to store scene graphs
+//               or subgraphs, and by convention they are given
+//               filenames ending in the extension ".bam" when they
+//               are used for this purpose.  However, a Bam file may
+//               store any arbitrary list of TypedWriteable objects;
+//               in this more general usage, they are given filenames
+//               ending in ".boo" to differentiate them from the more
+//               common scene graph files.
+//
+//               See also BamFile, which defines a higher-level
+//               interface to read and write Bam files on disk.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 class EXPCL_PANDA BamWriter{
 class EXPCL_PANDA BamWriter{
 public:
 public:
-  INLINE BamWriter(DatagramSink *sink);
-  ~BamWriter(void);
+  BamWriter(DatagramSink *sink);
+  ~BamWriter();
+
+  // The primary interface for a caller.
   
   
-  bool init(void);
+  bool init();
   bool write_object(TypedWriteable *obj);
   bool write_object(TypedWriteable *obj);
+
+public:
+  // Functions to support classes that write themselves to the Bam.
+
   void write_pointer(Datagram &packet, TypedWriteable *dest);
   void write_pointer(Datagram &packet, TypedWriteable *dest);
-  
-  //This function is provided for writing out shared pointers
-  //to PTA's.  You should pass in a pointer to the PTA you want
-  //to write.  If BamWriter has not already been asked to register
-  //this pointer, it will register it, write the appropriate info
-  //into the Datagram and return false.  If it has, it will write
-  //the reference into the Datagram and return true.  If false is
-  //returned, then the class registering the PTA should write out
-  //the info into the Datagram itself
-  bool register_pta(Datagram &packet, void* ptr);
-   
+  bool register_pta(Datagram &packet, void *ptr);
   void write_handle(Datagram &packet, TypeHandle type);
   void write_handle(Datagram &packet, TypeHandle type);
+
 private:
 private:
-  class storeState {
+  int enqueue_object(TypedWriteable *object);
+
+
+  // This is the set of all TypeHandles already written.
+  set<int> _types_written;
+
+  // This keeps track of all of the objects we have written out
+  // already (or are about to write out), and associates a unique
+  // object ID number to each one.
+  class StoreState {
   public:
   public:
-    PN_uint16 objId;
-    bool written;
+    int _object_id;
+    bool _written;
     
     
-    storeState(void) : objId(0), written(false) {}
-    ~storeState(void) {}
+    StoreState(int object_id) : _object_id(object_id), _written(false) {}
   };
   };
-  
-  void enqueue(TypedWriteable *obj);
-  bool empty_queue(void);
-  
+  typedef map<TypedWriteable *, StoreState> StateMap;
+  StateMap _state_map;
+
+  // This is the next object ID that will be assigned to a new object.
+  int _next_object_id;
+
+  // This is the queue of objects that need to be written when the
+  // current object is finished.
+  typedef deque<TypedWriteable *> ObjectQueue;
+  ObjectQueue _object_queue;
+
+  // These are used by register_pta() to unify multiple references to
+  // the same PointerToArray.
+  typedef map<void *, int> PTAMap;
+  PTAMap _pta_map;
+  int _next_pta_id;
+
+  // The destination to write all the output to.
   DatagramSink *_target;
   DatagramSink *_target;
-  bool _writing;
-  deque<TypedWriteable*> _deferred;
-  set<int> _type_map;
-  map<TypedWriteable*, storeState> _statemap;
-  map<void*, int> _ptamap;
-  PN_uint16 _current, _current_pta;
-  bool _emptying;
 };
 };
 
 
 #include "bamWriter.I"
 #include "bamWriter.I"