Browse Source

qpgeom in bam files

David Rose 21 years ago
parent
commit
61dcffa70c
52 changed files with 775 additions and 239 deletions
  1. 5 5
      panda/src/chan/partBundle.cxx
  2. 1 1
      panda/src/chan/partBundle.h
  3. 6 0
      panda/src/char/characterVertexSlider.cxx
  4. 7 0
      panda/src/char/jointVertexTransform.cxx
  5. 10 3
      panda/src/gobj/internalName.cxx
  6. 1 1
      panda/src/gobj/internalName.h
  7. 30 0
      panda/src/gobj/qpgeom.cxx
  8. 14 13
      panda/src/gobj/qpgeomPrimitive.cxx
  9. 0 1
      panda/src/gobj/qpgeomPrimitive.h
  10. 29 0
      panda/src/gobj/qpgeomVertexAnimationSpec.cxx
  11. 9 0
      panda/src/gobj/qpgeomVertexAnimationSpec.h
  12. 61 16
      panda/src/gobj/qpgeomVertexArrayData.cxx
  13. 4 1
      panda/src/gobj/qpgeomVertexArrayData.h
  14. 59 26
      panda/src/gobj/qpgeomVertexArrayFormat.cxx
  15. 6 7
      panda/src/gobj/qpgeomVertexArrayFormat.h
  16. 28 0
      panda/src/gobj/qpgeomVertexColumn.I
  17. 65 19
      panda/src/gobj/qpgeomVertexColumn.cxx
  18. 21 2
      panda/src/gobj/qpgeomVertexColumn.h
  19. 110 50
      panda/src/gobj/qpgeomVertexData.cxx
  20. 5 4
      panda/src/gobj/qpgeomVertexData.h
  21. 4 4
      panda/src/gobj/qpgeomVertexFormat.I
  22. 32 10
      panda/src/gobj/qpgeomVertexFormat.cxx
  23. 6 6
      panda/src/gobj/qpgeomVertexFormat.h
  24. 2 2
      panda/src/gobj/sliderTable.I
  25. 36 14
      panda/src/gobj/sliderTable.cxx
  26. 6 3
      panda/src/gobj/sliderTable.h
  27. 27 1
      panda/src/gobj/transformBlend.cxx
  28. 44 0
      panda/src/gobj/transformBlendPalette.cxx
  29. 2 0
      panda/src/gobj/transformBlendPalette.h
  30. 2 2
      panda/src/gobj/transformPalette.I
  31. 37 16
      panda/src/gobj/transformPalette.cxx
  32. 2 2
      panda/src/gobj/transformPalette.h
  33. 2 0
      panda/src/gobj/userVertexSlider.cxx
  34. 2 0
      panda/src/gobj/userVertexTransform.cxx
  35. 5 4
      panda/src/pgraph/renderAttrib.cxx
  36. 1 1
      panda/src/pgraph/renderAttrib.h
  37. 5 4
      panda/src/pgraph/renderEffect.cxx
  38. 1 1
      panda/src/pgraph/renderEffect.h
  39. 2 2
      panda/src/pgraph/renderEffects.cxx
  40. 1 1
      panda/src/pgraph/renderEffects.h
  41. 2 2
      panda/src/pgraph/renderState.cxx
  42. 1 1
      panda/src/pgraph/renderState.h
  43. 2 2
      panda/src/pgraph/transformState.cxx
  44. 1 1
      panda/src/pgraph/transformState.h
  45. 67 7
      panda/src/putil/bamReader.cxx
  46. 6 0
      panda/src/putil/bamReader.h
  47. 1 0
      panda/src/putil/cycleDataReader.I
  48. 1 0
      panda/src/putil/cycleDataWriter.I
  49. 1 1
      panda/src/putil/typedWritable.cxx
  50. 1 1
      panda/src/putil/typedWritable.h
  51. 1 1
      pandatool/src/palettizer/paletteGroup.cxx
  52. 1 1
      pandatool/src/palettizer/paletteGroup.h

+ 5 - 5
panda/src/chan/partBundle.cxx

@@ -428,13 +428,13 @@ recompute_net_blend() {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: PartBundle::finalize
-//       Access: Public
-//  Description: Method to ensure that any necessary clean up tasks
-//               that have to be performed by this object are performed
+//       Access: Public, Virtual
+//  Description: Called by the BamReader to perform any final actions
+//               needed for setting up the object after all objects
+//               have been read and all pointers have been completed.
 ////////////////////////////////////////////////////////////////////
 void PartBundle::
-finalize(void)
-{
+finalize(BamReader *) {
   do_update(this, NULL, true, true);
 }
 

+ 1 - 1
panda/src/chan/partBundle.h

@@ -142,7 +142,7 @@ protected:
 
 public:
   static void register_with_read_factory(void);
-  virtual void finalize();
+  virtual void finalize(BamReader *manager);
 
   static TypedWritable *make_PartBundle(const FactoryParams &params);
 

+ 6 - 0
panda/src/char/characterVertexSlider.cxx

@@ -93,6 +93,8 @@ register_with_read_factory() {
 void CharacterVertexSlider::
 write_datagram(BamWriter *manager, Datagram &dg) {
   VertexSlider::write_datagram(manager, dg);
+
+  manager->write_pointer(dg, _char_slider);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -106,6 +108,8 @@ int CharacterVertexSlider::
 complete_pointers(TypedWritable **p_list, BamReader *manager) {
   int pi = VertexSlider::complete_pointers(p_list, manager);
 
+  _char_slider = DCAST(CharacterSlider, p_list[pi++]);    
+
   return pi;
 }
 
@@ -139,4 +143,6 @@ make_from_bam(const FactoryParams &params) {
 void CharacterVertexSlider::
 fillin(DatagramIterator &scan, BamReader *manager) {
   VertexSlider::fillin(scan, manager);
+
+  manager->read_pointer(scan);
 }

+ 7 - 0
panda/src/char/jointVertexTransform.cxx

@@ -154,6 +154,8 @@ register_with_read_factory() {
 void JointVertexTransform::
 write_datagram(BamWriter *manager, Datagram &dg) {
   VertexTransform::write_datagram(manager, dg);
+
+  manager->write_pointer(dg, _joint);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -167,6 +169,8 @@ int JointVertexTransform::
 complete_pointers(TypedWritable **p_list, BamReader *manager) {
   int pi = VertexTransform::complete_pointers(p_list, manager);
 
+  _joint = DCAST(CharacterJoint, p_list[pi++]);    
+
   return pi;
 }
 
@@ -200,4 +204,7 @@ make_from_bam(const FactoryParams &params) {
 void JointVertexTransform::
 fillin(DatagramIterator &scan, BamReader *manager) {
   VertexTransform::fillin(scan, manager);
+
+  manager->read_pointer(scan);
+  _matrix_stale = true;
 }

+ 10 - 3
panda/src/gobj/internalName.cxx

@@ -78,6 +78,8 @@ InternalName::
 ////////////////////////////////////////////////////////////////////
 PT(InternalName) InternalName::
 append(const string &name) {
+  test_ref_count_integrity();
+
   if (name.empty()) {
     return this;
   }
@@ -129,6 +131,8 @@ get_name() const {
 ////////////////////////////////////////////////////////////////////
 int InternalName::
 find_ancestor(const string &basename) const {
+  test_ref_count_integrity();
+
   if (_basename == basename) {
     return 0;
 
@@ -152,6 +156,8 @@ find_ancestor(const string &basename) const {
 ////////////////////////////////////////////////////////////////////
 const InternalName *InternalName::
 get_ancestor(int n) const {
+  test_ref_count_integrity();
+
   if (n == 0) {
     return this;
 
@@ -217,11 +223,12 @@ register_with_read_factory() {
 ////////////////////////////////////////////////////////////////////
 //     Function: InternalName::finalize
 //       Access: Public, Virtual
-//  Description: Method to ensure that any necessary clean up tasks
-//               that have to be performed by this object are performed
+//  Description: Called by the BamReader to perform any final actions
+//               needed for setting up the object after all objects
+//               have been read and all pointers have been completed.
 ////////////////////////////////////////////////////////////////////
 void InternalName::
-finalize() {
+finalize(BamReader *) {
   // Unref the pointer that we explicitly reffed in make_from_bam().
   unref();
 

+ 1 - 1
panda/src/gobj/internalName.h

@@ -107,7 +107,7 @@ public:
   static void register_with_read_factory();
   virtual void write_datagram(BamWriter *manager, Datagram &me);
 
-  virtual void finalize();
+  virtual void finalize(BamReader *manager);
 
 protected:
   static TypedWritable *make_from_bam(const FactoryParams &params);

+ 30 - 0
panda/src/gobj/qpgeom.cxx

@@ -784,6 +784,16 @@ make_copy() const {
 ////////////////////////////////////////////////////////////////////
 void qpGeom::CData::
 write_datagram(BamWriter *manager, Datagram &dg) const {
+  manager->write_pointer(dg, _data);
+
+  dg.add_uint16(_primitives.size());
+  Primitives::const_iterator pi;
+  for (pi = _primitives.begin(); pi != _primitives.end(); ++pi) {
+    manager->write_pointer(dg, *pi);
+  }
+
+  dg.add_uint8(_primitive_type);
+  dg.add_uint16(_point_rendering);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -797,6 +807,13 @@ int qpGeom::CData::
 complete_pointers(TypedWritable **p_list, BamReader *manager) {
   int pi = CycleData::complete_pointers(p_list, manager);
 
+  _data = DCAST(qpGeomVertexData, p_list[pi++]);
+
+  Primitives::iterator pri;
+  for (pri = _primitives.begin(); pri != _primitives.end(); ++pri) {
+    (*pri) = DCAST(qpGeomPrimitive, p_list[pi++]);
+  }
+
   return pi;
 }
 
@@ -809,4 +826,17 @@ complete_pointers(TypedWritable **p_list, BamReader *manager) {
 ////////////////////////////////////////////////////////////////////
 void qpGeom::CData::
 fillin(DatagramIterator &scan, BamReader *manager) {
+  manager->read_pointer(scan);
+
+  int num_primitives = scan.get_uint16();
+  _primitives.reserve(num_primitives);
+  for (int i = 0; i < num_primitives; ++i) {
+    manager->read_pointer(scan);
+    _primitives.push_back(NULL);
+  }
+
+  _primitive_type = (qpGeomPrimitive::PrimitiveType)scan.get_uint8();
+  _point_rendering = scan.get_uint16();
+  _got_usage_hint = false;
+  _modified = qpGeom::get_next_modified();
 }

+ 14 - 13
panda/src/gobj/qpgeomPrimitive.cxx

@@ -1004,6 +1004,8 @@ void qpGeomPrimitive::
 write_datagram(BamWriter *manager, Datagram &dg) {
   TypedWritable::write_datagram(manager, dg);
 
+  dg.add_uint8(_usage_hint);
+
   manager->write_cdata(dg, _cycler);
 }
 
@@ -1018,6 +1020,8 @@ void qpGeomPrimitive::
 fillin(DatagramIterator &scan, BamReader *manager) {
   TypedWritable::fillin(scan, manager);
 
+  _usage_hint = (qpGeomUsageHint::UsageHint)scan.get_uint8();
+
   manager->read_cdata(scan, _cycler);
 }
 
@@ -1079,20 +1083,10 @@ make_copy() const {
 ////////////////////////////////////////////////////////////////////
 void qpGeomPrimitive::CData::
 write_datagram(BamWriter *manager, Datagram &dg) const {
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: qpGeomPrimitive::CData::complete_pointers
-//       Access: Public, Virtual
-//  Description: Receives an array of pointers, one for each time
-//               manager->read_pointer() was called in fillin().
-//               Returns the number of pointers processed.
-////////////////////////////////////////////////////////////////////
-int qpGeomPrimitive::CData::
-complete_pointers(TypedWritable **p_list, BamReader *manager) {
-  int pi = CycleData::complete_pointers(p_list, manager);
+  dg.add_uint8(_shade_model);
 
-  return pi;
+  WRITE_PTA(manager, dg, IPD_ushort::write_datagram, _vertices);
+  WRITE_PTA(manager, dg, IPD_int::write_datagram, _ends);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -1104,4 +1098,11 @@ complete_pointers(TypedWritable **p_list, BamReader *manager) {
 ////////////////////////////////////////////////////////////////////
 void qpGeomPrimitive::CData::
 fillin(DatagramIterator &scan, BamReader *manager) {
+  _shade_model = (ShadeModel)scan.get_uint8();
+  
+  READ_PTA(manager, scan, IPD_ushort::read_datagram, _vertices);
+  READ_PTA(manager, scan, IPD_int::read_datagram, _ends);
+
+  _modified = qpGeom::get_next_modified();
+  _got_minmax = false;
 }

+ 0 - 1
panda/src/gobj/qpgeomPrimitive.h

@@ -228,7 +228,6 @@ private:
     INLINE CData(const CData &copy);
     virtual CycleData *make_copy() const;
     virtual void write_datagram(BamWriter *manager, Datagram &dg) const;
-    virtual int complete_pointers(TypedWritable **plist, BamReader *manager);
     virtual void fillin(DatagramIterator &scan, BamReader *manager);
 
     ShadeModel _shade_model;

+ 29 - 0
panda/src/gobj/qpgeomVertexAnimationSpec.cxx

@@ -17,6 +17,8 @@
 ////////////////////////////////////////////////////////////////////
 
 #include "qpgeomVertexAnimationSpec.h"
+#include "datagram.h"
+#include "datagramIterator.h"
 
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeomVertexAnimationSpec::output
@@ -40,3 +42,30 @@ output(ostream &out) const {
     break;
   }
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexAnimationSpec::write_datagram
+//       Access: Public
+//  Description: Writes the contents of this object to the datagram
+//               for shipping out to a Bam file.
+////////////////////////////////////////////////////////////////////
+void qpGeomVertexAnimationSpec::
+write_datagram(BamWriter *, Datagram &dg) {
+  dg.add_uint8(_animation_type);
+  dg.add_uint16(_num_transforms);
+  dg.add_bool(_indexed_transforms);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexAnimationSpec::fillin
+//       Access: Protected
+//  Description: This internal function is called by make_from_bam to
+//               read in all of the relevant data from the BamFile for
+//               the new qpGeomVertexAnimationSpec.
+////////////////////////////////////////////////////////////////////
+void qpGeomVertexAnimationSpec::
+fillin(DatagramIterator &scan, BamReader *) {
+  _animation_type = (AnimationType)scan.get_uint8();
+  _num_transforms = scan.get_uint16();
+  _indexed_transforms = scan.get_bool();
+}

+ 9 - 0
panda/src/gobj/qpgeomVertexAnimationSpec.h

@@ -21,6 +21,11 @@
 
 #include "pandabase.h"
 
+class BamWriter;
+class BamReader;
+class Datagram;
+class DatagramIterator;
+
 ////////////////////////////////////////////////////////////////////
 //       Class : qpGeomVertexAnimationSpec
 // Description : This object describes how the vertex animation, if
@@ -68,6 +73,10 @@ public:
   INLINE bool operator != (const qpGeomVertexAnimationSpec &other) const;
   INLINE int compare_to(const qpGeomVertexAnimationSpec &other) const;
 
+public:
+  void write_datagram(BamWriter *manager, Datagram &dg);
+  void fillin(DatagramIterator &scan, BamReader *manager);
+
 private:  
   AnimationType _animation_type;
 

+ 61 - 16
panda/src/gobj/qpgeomVertexArrayData.cxx

@@ -317,11 +317,63 @@ register_with_read_factory() {
 ////////////////////////////////////////////////////////////////////
 void qpGeomVertexArrayData::
 write_datagram(BamWriter *manager, Datagram &dg) {
-  TypedWritable::write_datagram(manager, dg);
+  TypedWritableReferenceCount::write_datagram(manager, dg);
+
+  manager->write_pointer(dg, _array_format);
+  dg.add_uint8(_usage_hint);
 
   manager->write_cdata(dg, _cycler);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexArrayData::write_raw_data
+//       Access: Public, Static
+//  Description: Called by CData::write_datagram to write the raw data
+//               of the array to the indicated datagram.
+////////////////////////////////////////////////////////////////////
+void qpGeomVertexArrayData::
+write_raw_data(Datagram &dg, const PTA_uchar &data) {
+  // TODO: account for endianness of host.
+  dg.add_uint32(data.size());
+  dg.append_data(data, data.size());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexArrayData::read_raw_data
+//       Access: Public, Static
+//  Description: Called by CData::fillin to read the raw data
+//               of the array from the indicated datagram.
+////////////////////////////////////////////////////////////////////
+PTA_uchar qpGeomVertexArrayData::
+read_raw_data(DatagramIterator &scan) {
+  // TODO: account for endianness of host.
+
+  size_t size = scan.get_uint32();
+  PTA_uchar data = PTA_uchar::empty_array(size);
+  const unsigned char *source_data = 
+    (const unsigned char *)scan.get_datagram().get_data();
+  memcpy(data, source_data + scan.get_current_index(), size);
+  scan.skip_bytes(size);
+
+  return data;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexArrayData::complete_pointers
+//       Access: Public, Virtual
+//  Description: Receives an array of pointers, one for each time
+//               manager->read_pointer() was called in fillin().
+//               Returns the number of pointers processed.
+////////////////////////////////////////////////////////////////////
+int qpGeomVertexArrayData::
+complete_pointers(TypedWritable **p_list, BamReader *manager) {
+  int pi = TypedWritableReferenceCount::complete_pointers(p_list, manager);
+
+  _array_format = DCAST(qpGeomVertexArrayFormat, p_list[pi++]);
+
+  return pi;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeomVertexArrayData::make_from_bam
 //       Access: Protected, Static
@@ -351,7 +403,10 @@ make_from_bam(const FactoryParams &params) {
 ////////////////////////////////////////////////////////////////////
 void qpGeomVertexArrayData::
 fillin(DatagramIterator &scan, BamReader *manager) {
-  TypedWritable::fillin(scan, manager);
+  TypedWritableReferenceCount::fillin(scan, manager);
+
+  manager->read_pointer(scan);
+  _usage_hint = (qpGeomUsageHint::UsageHint)scan.get_uint8();
 
   manager->read_cdata(scan, _cycler);
 }
@@ -374,20 +429,7 @@ make_copy() const {
 ////////////////////////////////////////////////////////////////////
 void qpGeomVertexArrayData::CData::
 write_datagram(BamWriter *manager, Datagram &dg) const {
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: qpGeomVertexArrayData::CData::complete_pointers
-//       Access: Public, Virtual
-//  Description: Receives an array of pointers, one for each time
-//               manager->read_pointer() was called in fillin().
-//               Returns the number of pointers processed.
-////////////////////////////////////////////////////////////////////
-int qpGeomVertexArrayData::CData::
-complete_pointers(TypedWritable **p_list, BamReader *manager) {
-  int pi = CycleData::complete_pointers(p_list, manager);
-
-  return pi;
+  WRITE_PTA(manager, dg, write_raw_data, _data);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -399,4 +441,7 @@ complete_pointers(TypedWritable **p_list, BamReader *manager) {
 ////////////////////////////////////////////////////////////////////
 void qpGeomVertexArrayData::CData::
 fillin(DatagramIterator &scan, BamReader *manager) {
+  READ_PTA(manager, scan, read_raw_data, _data);
+
+  _modified = qpGeom::get_next_modified();
 }

+ 4 - 1
panda/src/gobj/qpgeomVertexArrayData.h

@@ -115,7 +115,6 @@ private:
     INLINE CData(const CData &copy);
     virtual CycleData *make_copy() const;
     virtual void write_datagram(BamWriter *manager, Datagram &dg) const;
-    virtual int complete_pointers(TypedWritable **plist, BamReader *manager);
     virtual void fillin(DatagramIterator &scan, BamReader *manager);
 
     PTA_uchar _data;
@@ -129,6 +128,9 @@ private:
 public:
   static void register_with_read_factory();
   virtual void write_datagram(BamWriter *manager, Datagram &dg);
+  static void write_raw_data(Datagram &dg, const PTA_uchar &data);
+  static PTA_uchar read_raw_data(DatagramIterator &source);
+  virtual int complete_pointers(TypedWritable **plist, BamReader *manager);
 
 protected:
   static TypedWritable *make_from_bam(const FactoryParams &params);
@@ -152,6 +154,7 @@ private:
   static TypeHandle _type_handle;
 
   friend class qpGeomCacheManager;
+  friend class qpGeomVertexData;
   friend class PreparedGraphicsObjects;
 };
 

+ 59 - 26
panda/src/gobj/qpgeomVertexArrayFormat.cxx

@@ -151,7 +151,7 @@ qpGeomVertexArrayFormat(const qpGeomVertexArrayFormat &copy) :
   _pad_to(copy._pad_to),
   _columns_unsorted(copy._columns_unsorted)
 {
-  DataTypes::const_iterator dti;
+  Columns::const_iterator dti;
   for (dti = copy._columns.begin(); dti != copy._columns.end(); ++dti) {
     add_column(*(*dti));
   }
@@ -172,7 +172,7 @@ operator = (const qpGeomVertexArrayFormat &copy) {
   _columns.clear();
   _columns_by_name.clear();
   _columns_unsorted = false;
-  DataTypes::const_iterator dti;
+  Columns::const_iterator dti;
   for (dti = copy._columns.begin(); dti != copy._columns.end(); ++dti) {
     add_column(*(*dti));
   }
@@ -185,9 +185,9 @@ operator = (const qpGeomVertexArrayFormat &copy) {
 ////////////////////////////////////////////////////////////////////
 qpGeomVertexArrayFormat::
 ~qpGeomVertexArrayFormat() {
-  DataTypes::iterator dti;
-  for (dti = _columns.begin(); dti != _columns.end(); ++dti) {
-    delete (*dti);
+  Columns::iterator ci;
+  for (ci = _columns.begin(); ci != _columns.end(); ++ci) {
+    delete (*ci);
   }
 }
 
@@ -263,7 +263,7 @@ add_column(const qpGeomVertexColumn &column) {
 
   int new_index = (int)_columns.size();
   _columns.push_back(new_column);
-  _columns_by_name.insert(DataTypesByName::value_type(new_column->get_name(), new_column));
+  _columns_by_name.insert(ColumnsByName::value_type(new_column->get_name(), new_column));
 
   return new_index;
 }
@@ -277,16 +277,16 @@ add_column(const qpGeomVertexColumn &column) {
 void qpGeomVertexArrayFormat::
 remove_column(const InternalName *name) {
   nassertv(!_is_registered);
-  DataTypesByName::iterator ni;
+  ColumnsByName::iterator ni;
   ni = _columns_by_name.find(name);
   if (ni != _columns_by_name.end()) {
     qpGeomVertexColumn *column = (*ni).second;
     _columns_by_name.erase(ni);
 
-    DataTypes::iterator dti;
-    dti = find(_columns.begin(), _columns.end(), column);
-    nassertv(dti != _columns.end());
-    _columns.erase(dti);
+    Columns::iterator ci;
+    ci = find(_columns.begin(), _columns.end(), column);
+    nassertv(ci != _columns.end());
+    _columns.erase(ci);
 
     delete column;
 
@@ -326,7 +326,7 @@ clear_columns() {
 ////////////////////////////////////////////////////////////////////
 const qpGeomVertexColumn *qpGeomVertexArrayFormat::
 get_column(const InternalName *name) const {
-  DataTypesByName::const_iterator ni;
+  ColumnsByName::const_iterator ni;
   ni = _columns_by_name.find(name);
   if (ni != _columns_by_name.end()) {
     return (*ni).second;
@@ -344,9 +344,9 @@ get_column(const InternalName *name) const {
 const qpGeomVertexColumn *qpGeomVertexArrayFormat::
 get_column(int start_byte, int num_bytes) const {
   consider_sort_columns();
-  DataTypes::const_iterator dti;
-  for (dti = _columns.begin(); dti != _columns.end(); ++dti) {
-    const qpGeomVertexColumn *column = (*dti);
+  Columns::const_iterator ci;
+  for (ci = _columns.begin(); ci != _columns.end(); ++ci) {
+    const qpGeomVertexColumn *column = (*ci);
     if (column->overlaps_with(start_byte, num_bytes)) {
       return column;
     }
@@ -394,10 +394,10 @@ is_data_subset_of(const qpGeomVertexArrayFormat &other) const {
 ////////////////////////////////////////////////////////////////////
 void qpGeomVertexArrayFormat::
 output(ostream &out) const {
-  DataTypes::const_iterator dti;
+  Columns::const_iterator ci;
   out << "[";
-  for (dti = _columns.begin(); dti != _columns.end(); ++dti) {
-    const qpGeomVertexColumn *column = (*dti);
+  for (ci = _columns.begin(); ci != _columns.end(); ++ci) {
+    const qpGeomVertexColumn *column = (*ci);
     out << " " << *column;
   }
   out << " ]";
@@ -413,9 +413,9 @@ write(ostream &out, int indent_level) const {
   indent(out, indent_level)
     << "Array format (stride = " << get_stride() << "):\n";
   consider_sort_columns();
-  DataTypes::const_iterator dti;
-  for (dti = _columns.begin(); dti != _columns.end(); ++dti) {
-    const qpGeomVertexColumn *column = (*dti);
+  Columns::const_iterator ci;
+  for (ci = _columns.begin(); ci != _columns.end(); ++ci) {
+    const qpGeomVertexColumn *column = (*ci);
     indent(out, indent_level + 2)
       << *column 
       << " " << column->get_numeric_type()
@@ -441,9 +441,9 @@ write_with_data(ostream &out, int indent_level,
     indent(out, indent_level)
       << "vertex index " << i << ":\n";
     reader.set_vertex(i);
-    DataTypes::const_iterator dti;
-    for (dti = _columns.begin(); dti != _columns.end(); ++dti) {
-      const qpGeomVertexColumn *column = (*dti);
+    Columns::const_iterator ci;
+    for (ci = _columns.begin(); ci != _columns.end(); ++ci) {
+      const qpGeomVertexColumn *column = (*ci);
       int num_values = min(column->get_num_values(), 4);
       reader.set_column(array_index, column);
       const LVecBase4f &d = reader.get_data4f();
@@ -532,6 +532,19 @@ register_with_read_factory() {
 void qpGeomVertexArrayFormat::
 write_datagram(BamWriter *manager, Datagram &dg) {
   TypedWritableReferenceCount::write_datagram(manager, dg);
+
+  dg.add_uint16(_stride);
+  dg.add_uint16(_total_bytes);
+  dg.add_uint8(_pad_to);
+
+  consider_sort_columns();
+
+  dg.add_uint16(_columns.size());
+  Columns::iterator ci;
+  for (ci = _columns.begin(); ci != _columns.end(); ++ci) {
+    qpGeomVertexColumn *column = (*ci);
+    column->write_datagram(manager, dg);
+  }
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -545,6 +558,12 @@ int qpGeomVertexArrayFormat::
 complete_pointers(TypedWritable **p_list, BamReader *manager) {
   int pi = TypedWritableReferenceCount::complete_pointers(p_list, manager);
 
+  Columns::iterator ci;
+  for (ci = _columns.begin(); ci != _columns.end(); ++ci) {
+    qpGeomVertexColumn *column = (*ci);
+    pi += column->complete_pointers(p_list + pi, manager);
+  }
+
   return pi;
 }
 
@@ -554,8 +573,8 @@ complete_pointers(TypedWritable **p_list, BamReader *manager) {
 //  Description: This function is called by the BamReader's factory
 //               when a new object of type qpGeomVertexArrayFormat is
 //               encountered in the Bam file.  It should create the
-//               qpGeomVertexArrayFormat and extract its information from
-//               the file.
+//               qpGeomVertexArrayFormat and extract its information
+//               from the file.
 ////////////////////////////////////////////////////////////////////
 TypedWritable *qpGeomVertexArrayFormat::
 make_from_bam(const FactoryParams &params) {
@@ -579,4 +598,18 @@ make_from_bam(const FactoryParams &params) {
 void qpGeomVertexArrayFormat::
 fillin(DatagramIterator &scan, BamReader *manager) {
   TypedWritableReferenceCount::fillin(scan, manager);
+  nassertv(!_is_registered);
+
+  _stride = scan.get_uint16();
+  _total_bytes = scan.get_uint16();
+  _pad_to = scan.get_uint8();
+
+  int num_columns = scan.get_uint16();
+  _columns.reserve(num_columns);
+  for (int i = 0; i < num_columns; ++i) {
+    qpGeomVertexColumn *column = new qpGeomVertexColumn;
+    column->fillin(scan, manager);
+    _columns.push_back(column);
+  }
+  _columns_unsorted = false;
 }

+ 6 - 7
panda/src/gobj/qpgeomVertexArrayFormat.h

@@ -29,6 +29,8 @@ class qpGeomVertexFormat;
 class qpGeomVertexData;
 class InternalName;
 class FactoryParams;
+class BamWriter;
+class BamReader;
 
 ////////////////////////////////////////////////////////////////////
 //       Class : qpGeomVertexArrayFormat
@@ -126,19 +128,16 @@ private:
   void do_register();
 
   bool _is_registered;
-  const qpGeomVertexFormat *_root_format;
-  const qpGeomVertexFormat *_parent_format;
-
   int _stride;
   int _total_bytes;
   int _pad_to;
 
-  typedef pvector<qpGeomVertexColumn *> DataTypes;
-  DataTypes _columns;
+  typedef pvector<qpGeomVertexColumn *> Columns;
+  Columns _columns;
   bool _columns_unsorted;
 
-  typedef pmap<const InternalName *, qpGeomVertexColumn *> DataTypesByName;
-  DataTypesByName _columns_by_name;
+  typedef pmap<const InternalName *, qpGeomVertexColumn *> ColumnsByName;
+  ColumnsByName _columns_by_name;
 
 public:
   static void register_with_read_factory();

+ 28 - 0
panda/src/gobj/qpgeomVertexColumn.I

@@ -17,6 +17,34 @@
 ////////////////////////////////////////////////////////////////////
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexColumn::Default Constructor
+//       Access: Private
+//  Description: Creates an invalid column.  Used on when constructing
+//               from a bam file.
+////////////////////////////////////////////////////////////////////
+INLINE qpGeomVertexColumn::
+qpGeomVertexColumn() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexColumn::Constructor
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE qpGeomVertexColumn::
+qpGeomVertexColumn(const InternalName *name, int num_components,
+                   NumericType numeric_type, Contents contents,
+                   int start) :
+  _name(name),
+  _num_components(num_components),
+  _numeric_type(numeric_type),
+  _contents(contents),
+  _start(start)
+{
+  setup();
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeomVertexColumn::Copy Constructor
 //       Access: Published

+ 65 - 19
panda/src/gobj/qpgeomVertexColumn.cxx

@@ -17,26 +17,32 @@
 ////////////////////////////////////////////////////////////////////
 
 #include "qpgeomVertexColumn.h"
+#include "bamReader.h"
+#include "bamWriter.h"
 
 ////////////////////////////////////////////////////////////////////
-//     Function: qpGeomVertexColumn::Constructor
+//     Function: qpGeomVertexColumn::output
 //       Access: Published
 //  Description: 
 ////////////////////////////////////////////////////////////////////
-qpGeomVertexColumn::
-qpGeomVertexColumn(const InternalName *name, int num_components,
-                   NumericType numeric_type, Contents contents,
-                   int start) :
-  _name(name),
-  _num_components(num_components),
-  _num_values(num_components),
-  _numeric_type(numeric_type),
-  _contents(contents),
-  _start(start)
-{
-  nassertv(num_components > 0 && start >= 0);
+void qpGeomVertexColumn::
+output(ostream &out) const {
+  out << *get_name() << "(" << get_num_components() << ")";
+}
 
-  switch (numeric_type) {
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexColumn::setup
+//       Access: Private
+//  Description: Called once at construction time (or at bam-reading
+//               time) to initialize the internal dependent values.
+////////////////////////////////////////////////////////////////////
+void qpGeomVertexColumn::
+setup() {
+  nassertv(_num_components > 0 && _start >= 0);
+
+  _num_values = _num_components;
+
+  switch (_numeric_type) {
   case NT_uint16:
     _component_bytes = 2;  // sizeof(PN_uint16)
     break;
@@ -60,13 +66,53 @@ qpGeomVertexColumn(const InternalName *name, int num_components,
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: qpGeomVertexColumn::output
-//       Access: Published
-//  Description: 
+//     Function: qpGeomVertexColumn::write_datagram
+//       Access: Public
+//  Description: Writes the contents of this object to the datagram
+//               for shipping out to a Bam file.
 ////////////////////////////////////////////////////////////////////
 void qpGeomVertexColumn::
-output(ostream &out) const {
-  out << *get_name() << "(" << get_num_components() << ")";
+write_datagram(BamWriter *manager, Datagram &dg) {
+  manager->write_pointer(dg, _name);
+  dg.add_uint8(_num_components);
+  dg.add_uint8(_numeric_type);
+  dg.add_uint8(_contents);
+  dg.add_uint16(_start);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexColumn::complete_pointers
+//       Access: Public
+//  Description: Receives an array of pointers, one for each time
+//               manager->read_pointer() was called in fillin().
+//               Returns the number of pointers processed.
+////////////////////////////////////////////////////////////////////
+int qpGeomVertexColumn::
+complete_pointers(TypedWritable **p_list, BamReader *manager) {
+  int pi = 0;
+
+  _name = DCAST(InternalName, p_list[pi++]);
+
+  return pi;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexColumn::fillin
+//       Access: Protected
+//  Description: This internal function is called by make_from_bam to
+//               read in all of the relevant data from the BamFile for
+//               the new qpGeomVertexColumn.
+////////////////////////////////////////////////////////////////////
+void qpGeomVertexColumn::
+fillin(DatagramIterator &scan, BamReader *manager) {
+  manager->read_pointer(scan);
+
+  _num_components = scan.get_uint8();
+  _numeric_type = (NumericType)scan.get_uint8();
+  _contents = (Contents)scan.get_uint8();
+  _start = scan.get_uint16();
+
+  setup();
 }
 
 ostream &

+ 21 - 2
panda/src/gobj/qpgeomVertexColumn.h

@@ -23,6 +23,12 @@
 #include "internalName.h"
 #include "pointerTo.h"
 
+class TypedWritable;
+class BamWriter;
+class BamReader;
+class Datagram;
+class DatagramIterator;
+
 ////////////////////////////////////////////////////////////////////
 //       Class : qpGeomVertexColumn
 // Description : This defines how a single column is interleaved
@@ -53,9 +59,12 @@ PUBLISHED:
     C_morph_delta,  // A delta from some base value, defining a blend shape
   };
 
+private:
+  INLINE qpGeomVertexColumn();
+PUBLISHED:
   INLINE qpGeomVertexColumn(const InternalName *name, int num_components,
-                              NumericType numeric_type, Contents contents,
-                              int start);
+                            NumericType numeric_type, Contents contents,
+                            int start);
   INLINE qpGeomVertexColumn(const qpGeomVertexColumn &copy);
   INLINE void operator = (const qpGeomVertexColumn &copy);
 
@@ -83,6 +92,14 @@ public:
   INLINE bool operator != (const qpGeomVertexColumn &other) const;
   INLINE bool operator < (const qpGeomVertexColumn &other) const;
 
+private:
+  void setup();
+
+public:
+  void write_datagram(BamWriter *manager, Datagram &dg);
+  int complete_pointers(TypedWritable **plist, BamReader *manager);
+  void fillin(DatagramIterator &scan, BamReader *manager);
+
 private:
   CPT(InternalName) _name;
   int _num_components;
@@ -92,6 +109,8 @@ private:
   int _start;
   int _component_bytes;
   int _total_bytes;
+
+  friend class qpGeomVertexArrayFormat;
 };
 
 INLINE ostream &operator << (ostream &out, const qpGeomVertexColumn &obj);

+ 110 - 50
panda/src/gobj/qpgeomVertexData.cxx

@@ -207,30 +207,6 @@ set_array(int i, const qpGeomVertexArrayData *array) {
   cdata->_animated_vertices_modified = UpdateSeq();
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: qpGeomVertexData::modify_transform_palette
-//       Access: Published
-//  Description: Returns a modifiable pointer to the current
-//               TransformPalette on this vertex data, if any, or
-//               NULL if there is not a TransformPalette.  See
-//               get_transform_palette().
-////////////////////////////////////////////////////////////////////
-TransformPalette *qpGeomVertexData::
-modify_transform_palette() {
-  // Perform copy-on-write: if the reference count on the palette is
-  // greater than 1, assume some other GeomVertexData has the same
-  // pointer, so make a copy of it first.
-  CDWriter cdata(_cycler);
-
-  if (cdata->_transform_palette->get_ref_count() > 1) {
-    cdata->_transform_palette = new TransformPalette(*cdata->_transform_palette);
-  }
-  cdata->_modified = qpGeom::get_next_modified();
-  cdata->_animated_vertices_modified = UpdateSeq();
-
-  return cdata->_transform_palette;
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeomVertexData::set_transform_palette
 //       Access: Published
@@ -242,6 +218,8 @@ modify_transform_palette() {
 ////////////////////////////////////////////////////////////////////
 void qpGeomVertexData::
 set_transform_palette(const TransformPalette *palette) {
+  nassertv(palette == (TransformPalette *)NULL || palette->is_registered());
+
   CDWriter cdata(_cycler);
   cdata->_transform_palette = (TransformPalette *)palette;
   cdata->_modified = qpGeom::get_next_modified();
@@ -289,30 +267,6 @@ set_transform_blend_palette(const TransformBlendPalette *palette) {
   cdata->_animated_vertices_modified = UpdateSeq();
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: qpGeomVertexData::modify_slider_table
-//       Access: Published
-//  Description: Returns a modifiable pointer to the current
-//               SliderTable on this vertex data, if any, or
-//               NULL if there is not a SliderTable.  See
-//               get_slider_table().
-////////////////////////////////////////////////////////////////////
-SliderTable *qpGeomVertexData::
-modify_slider_table() {
-  // Perform copy-on-write: if the reference count on the table is
-  // greater than 1, assume some other GeomVertexData has the same
-  // pointer, so make a copy of it first.
-  CDWriter cdata(_cycler);
-
-  if (cdata->_slider_table->get_ref_count() > 1) {
-    cdata->_slider_table = new SliderTable(*cdata->_slider_table);
-  }
-  cdata->_modified = qpGeom::get_next_modified();
-  cdata->_animated_vertices_modified = UpdateSeq();
-
-  return cdata->_slider_table;
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeomVertexData::set_slider_table
 //       Access: Published
@@ -1368,7 +1322,11 @@ register_with_read_factory() {
 ////////////////////////////////////////////////////////////////////
 void qpGeomVertexData::
 write_datagram(BamWriter *manager, Datagram &dg) {
-  TypedWritable::write_datagram(manager, dg);
+  TypedWritableReferenceCount::write_datagram(manager, dg);
+
+  dg.add_string(_name);
+  manager->write_pointer(dg, _format);
+  dg.add_uint8(_usage_hint);
 
   manager->write_cdata(dg, _cycler);
 }
@@ -1389,10 +1347,78 @@ make_from_bam(const FactoryParams &params) {
 
   parse_params(params, scan, manager);
   object->fillin(scan, manager);
+  manager->register_finalize(object);
 
   return object;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexData::complete_pointers
+//       Access: Public, Virtual
+//  Description: Receives an array of pointers, one for each time
+//               manager->read_pointer() was called in fillin().
+//               Returns the number of pointers processed.
+////////////////////////////////////////////////////////////////////
+int qpGeomVertexData::
+complete_pointers(TypedWritable **p_list, BamReader *manager) {
+  int pi = TypedWritableReferenceCount::complete_pointers(p_list, manager);
+
+  _format = DCAST(qpGeomVertexFormat, p_list[pi++]);
+
+  return pi;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexData::finalize
+//       Access: Public, Virtual
+//  Description: Called by the BamReader to perform any final actions
+//               needed for setting up the object after all objects
+//               have been read and all pointers have been completed.
+////////////////////////////////////////////////////////////////////
+void qpGeomVertexData::
+finalize(BamReader *manager) {
+  // Now we need to register the format that we have read from the bam
+  // file (since it doesn't come out of the bam file automatically
+  // registered).  This may change the format's pointer, which we
+  // should then update our own data to reflect.  But since this may
+  // cause the unregistered object to destruct, we have to also tell
+  // the BamReader to return the new object from now on.
+
+  // This extends to the nested array datas, as well as the transform
+  // palette and slider tables, as well.
+
+  CDWriter cdata(_cycler);
+
+  CPT(qpGeomVertexFormat) new_format = 
+    qpGeomVertexFormat::register_format(_format);
+
+  for (size_t i = 0; i < cdata->_arrays.size(); ++i) {
+    CPT(qpGeomVertexArrayFormat) new_array_format = new_format->get_array(i);
+    CPT(qpGeomVertexArrayFormat) old_array_format = _format->get_array(i);
+    nassertv(cdata->_arrays[i]->_array_format == old_array_format);
+
+    manager->change_pointer(old_array_format, new_array_format);
+    cdata->_arrays[i]->_array_format = new_array_format;
+  }
+
+  manager->change_pointer(_format, new_format);
+  _format = new_format;
+
+  if (cdata->_transform_palette != (TransformPalette *)NULL) {
+    CPT(TransformPalette) new_transform_palette = 
+      TransformPalette::register_palette(cdata->_transform_palette);
+    manager->change_pointer(cdata->_transform_palette, new_transform_palette);
+    cdata->_transform_palette = new_transform_palette;
+  }
+
+  if (cdata->_slider_table != (SliderTable *)NULL) {
+    CPT(SliderTable) new_slider_table = 
+      SliderTable::register_table(cdata->_slider_table);
+    manager->change_pointer(cdata->_slider_table, new_slider_table);
+    cdata->_slider_table = new_slider_table;
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeomVertexData::fillin
 //       Access: Protected
@@ -1402,7 +1428,11 @@ make_from_bam(const FactoryParams &params) {
 ////////////////////////////////////////////////////////////////////
 void qpGeomVertexData::
 fillin(DatagramIterator &scan, BamReader *manager) {
-  TypedWritable::fillin(scan, manager);
+  TypedWritableReferenceCount::fillin(scan, manager);
+
+  _name = scan.get_string();
+  manager->read_pointer(scan);
+  _usage_hint = (qpGeomUsageHint::UsageHint)scan.get_uint8();
 
   manager->read_cdata(scan, _cycler);
 }
@@ -1425,6 +1455,15 @@ make_copy() const {
 ////////////////////////////////////////////////////////////////////
 void qpGeomVertexData::CData::
 write_datagram(BamWriter *manager, Datagram &dg) const {
+  dg.add_uint16(_arrays.size());
+  Arrays::const_iterator ai;
+  for (ai = _arrays.begin(); ai != _arrays.end(); ++ai) {
+    manager->write_pointer(dg, *ai);
+  }
+
+  manager->write_pointer(dg, _transform_palette);
+  manager->write_pointer(dg, _transform_blend_palette);
+  manager->write_pointer(dg, _slider_table);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -1438,6 +1477,17 @@ int qpGeomVertexData::CData::
 complete_pointers(TypedWritable **p_list, BamReader *manager) {
   int pi = CycleData::complete_pointers(p_list, manager);
 
+  Arrays::iterator ai;
+  for (ai = _arrays.begin(); ai != _arrays.end(); ++ai) {
+    (*ai) = DCAST(qpGeomVertexArrayData, p_list[pi++]);    
+  }
+
+  _transform_palette = DCAST(TransformPalette, p_list[pi++]);
+  _transform_blend_palette = DCAST(TransformBlendPalette, p_list[pi++]);
+  _slider_table = DCAST(SliderTable, p_list[pi++]);
+
+  _modified = qpGeom::get_next_modified();
+
   return pi;
 }
 
@@ -1450,4 +1500,14 @@ complete_pointers(TypedWritable **p_list, BamReader *manager) {
 ////////////////////////////////////////////////////////////////////
 void qpGeomVertexData::CData::
 fillin(DatagramIterator &scan, BamReader *manager) {
+  size_t num_arrays = scan.get_uint16();
+  _arrays.reserve(num_arrays);
+  for (size_t i = 0; i < num_arrays; ++i) {
+    manager->read_pointer(scan);
+    _arrays.push_back(NULL);
+  }
+
+  manager->read_pointer(scan);
+  manager->read_pointer(scan);
+  manager->read_pointer(scan);
 }

+ 5 - 4
panda/src/gobj/qpgeomVertexData.h

@@ -101,7 +101,6 @@ PUBLISHED:
   void set_array(int i, const qpGeomVertexArrayData *array);
 
   INLINE const TransformPalette *get_transform_palette() const;
-  TransformPalette *modify_transform_palette();
   void set_transform_palette(const TransformPalette *palette);
   INLINE void clear_transform_palette();
 
@@ -111,7 +110,6 @@ PUBLISHED:
   INLINE void clear_transform_blend_palette();
 
   INLINE const SliderTable *get_slider_table() const;
-  SliderTable *modify_slider_table();
   void set_slider_table(const SliderTable *palette);
   INLINE void clear_slider_table();
 
@@ -218,9 +216,9 @@ private:
     virtual void fillin(DatagramIterator &scan, BamReader *manager);
 
     Arrays _arrays;
-    PT(TransformPalette) _transform_palette;
+    CPT(TransformPalette) _transform_palette;
     PT(TransformBlendPalette) _transform_blend_palette;
-    PT(SliderTable) _slider_table;
+    CPT(SliderTable) _slider_table;
     PT(qpGeomVertexData) _animated_vertices;
     UpdateSeq _animated_vertices_modified;
     UpdateSeq _modified;
@@ -247,6 +245,9 @@ private:
 public:
   static void register_with_read_factory();
   virtual void write_datagram(BamWriter *manager, Datagram &dg);
+  virtual int complete_pointers(TypedWritable **plist, BamReader *manager);
+
+  virtual void finalize(BamReader *manager);
 
 protected:
   static TypedWritable *make_from_bam(const FactoryParams &params);

+ 4 - 4
panda/src/gobj/qpgeomVertexFormat.I

@@ -52,8 +52,8 @@ is_registered() const {
 //               this point on.
 ////////////////////////////////////////////////////////////////////
 INLINE CPT(qpGeomVertexFormat) qpGeomVertexFormat::
-register_format(qpGeomVertexFormat *format) {
-  return get_registry()->register_format(format);
+register_format(const qpGeomVertexFormat *format) {
+  return get_registry()->register_format((qpGeomVertexFormat *)format);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -63,8 +63,8 @@ register_format(qpGeomVertexFormat *format) {
 //               one-array vertex format from the array definition.
 ////////////////////////////////////////////////////////////////////
 INLINE CPT(qpGeomVertexFormat) qpGeomVertexFormat::
-register_format(qpGeomVertexArrayFormat *format) {
-  return get_registry()->register_format(format);
+register_format(const qpGeomVertexArrayFormat *format) {
+  return register_format(new qpGeomVertexFormat(format));
 }
 
 ////////////////////////////////////////////////////////////////////

+ 32 - 10
panda/src/gobj/qpgeomVertexFormat.cxx

@@ -44,7 +44,7 @@ qpGeomVertexFormat() :
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 qpGeomVertexFormat::
-qpGeomVertexFormat(qpGeomVertexArrayFormat *array_format) :
+qpGeomVertexFormat(const qpGeomVertexArrayFormat *array_format) :
   _is_registered(false)
 {
   add_array(array_format);
@@ -120,10 +120,10 @@ modify_array(int array) {
 //               registered.
 ////////////////////////////////////////////////////////////////////
 void qpGeomVertexFormat::
-set_array(int array, qpGeomVertexArrayFormat *format) {
+set_array(int array, const qpGeomVertexArrayFormat *format) {
   nassertv(!_is_registered);
   nassertv(array >= 0 && array < (int)_arrays.size());
-  _arrays[array] = format;
+  _arrays[array] = (qpGeomVertexArrayFormat *)format;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -154,11 +154,11 @@ remove_array(int array) {
 //               registered.
 ////////////////////////////////////////////////////////////////////
 int qpGeomVertexFormat::
-add_array(qpGeomVertexArrayFormat *array_format) {
+add_array(const qpGeomVertexArrayFormat *array_format) {
   nassertr(!_is_registered, -1);
 
   int new_array = (int)_arrays.size();
-  _arrays.push_back(array_format);
+  _arrays.push_back((qpGeomVertexArrayFormat *)array_format);
   return new_array;
 }
 
@@ -174,11 +174,11 @@ add_array(qpGeomVertexArrayFormat *array_format) {
 //               registered.
 ////////////////////////////////////////////////////////////////////
 void qpGeomVertexFormat::
-insert_array(int array, qpGeomVertexArrayFormat *array_format) {
+insert_array(int array, const qpGeomVertexArrayFormat *array_format) {
   nassertv(!_is_registered);
   nassertv(array >= 0 && array <= (int)_arrays.size());
 
-  _arrays.insert(_arrays.begin() + array, array_format);
+  _arrays.insert(_arrays.begin() + array, (qpGeomVertexArrayFormat *)array_format);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -516,7 +516,7 @@ do_register() {
       result = _columns_by_name.insert(DataTypesByName::value_type(column->get_name(), DataTypeRecord()));
       if (!result.second) {
         gobj_cat.warning()
-          << "Column " << column->get_name() << " repeated in format.\n";
+          << "Column " << *column->get_name() << " repeated in format.\n";
       } else {
         DataTypeRecord &record = (*result.first).second;
         record._array_index = array;
@@ -627,6 +627,14 @@ register_with_read_factory() {
 void qpGeomVertexFormat::
 write_datagram(BamWriter *manager, Datagram &dg) {
   TypedWritableReferenceCount::write_datagram(manager, dg);
+
+  _animation.write_datagram(manager, dg);
+
+  dg.add_uint16(_arrays.size());
+  Arrays::const_iterator ai;
+  for (ai = _arrays.begin(); ai != _arrays.end(); ++ai) {
+    manager->write_pointer(dg, *ai);
+  }
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -640,6 +648,11 @@ int qpGeomVertexFormat::
 complete_pointers(TypedWritable **p_list, BamReader *manager) {
   int pi = TypedWritableReferenceCount::complete_pointers(p_list, manager);
 
+  Arrays::iterator ai;
+  for (ai = _arrays.begin(); ai != _arrays.end(); ++ai) {
+    (*ai) = DCAST(qpGeomVertexArrayFormat, p_list[pi++]);
+  }
+
   return pi;
 }
 
@@ -649,8 +662,8 @@ complete_pointers(TypedWritable **p_list, BamReader *manager) {
 //  Description: This function is called by the BamReader's factory
 //               when a new object of type qpGeomVertexFormat is
 //               encountered in the Bam file.  It should create the
-//               qpGeomVertexFormat and extract its information from
-//               the file.
+//               qpGeomVertexFormat and extract its information
+//               from the file.
 ////////////////////////////////////////////////////////////////////
 TypedWritable *qpGeomVertexFormat::
 make_from_bam(const FactoryParams &params) {
@@ -674,6 +687,15 @@ make_from_bam(const FactoryParams &params) {
 void qpGeomVertexFormat::
 fillin(DatagramIterator &scan, BamReader *manager) {
   TypedWritableReferenceCount::fillin(scan, manager);
+
+  _animation.fillin(scan, manager);
+
+  int num_arrays = scan.get_uint16();
+  _arrays.reserve(num_arrays);
+  for (int i = 0; i < num_arrays; i++) {
+    manager->read_pointer(scan);
+    _arrays.push_back(NULL);
+  }
 }
 
 ////////////////////////////////////////////////////////////////////

+ 6 - 6
panda/src/gobj/qpgeomVertexFormat.h

@@ -67,14 +67,14 @@ class qpGeomMunger;
 class EXPCL_PANDA qpGeomVertexFormat : public TypedWritableReferenceCount {
 PUBLISHED:
   qpGeomVertexFormat();
-  qpGeomVertexFormat(qpGeomVertexArrayFormat *array_format);
+  qpGeomVertexFormat(const qpGeomVertexArrayFormat *array_format);
   qpGeomVertexFormat(const qpGeomVertexFormat &copy);
   void operator = (const qpGeomVertexFormat &copy);
   virtual ~qpGeomVertexFormat();
 
   INLINE bool is_registered() const;
-  INLINE static CPT(qpGeomVertexFormat) register_format(qpGeomVertexFormat *format);
-  INLINE static CPT(qpGeomVertexFormat) register_format(qpGeomVertexArrayFormat *format);
+  INLINE static CPT(qpGeomVertexFormat) register_format(const qpGeomVertexFormat *format);
+  INLINE static CPT(qpGeomVertexFormat) register_format(const qpGeomVertexArrayFormat *format);
 
   INLINE const qpGeomVertexAnimationSpec &get_animation() const;
   INLINE void set_animation(const qpGeomVertexAnimationSpec &animation);
@@ -82,10 +82,10 @@ PUBLISHED:
   INLINE int get_num_arrays() const;
   INLINE const qpGeomVertexArrayFormat *get_array(int array) const;
   qpGeomVertexArrayFormat *modify_array(int array);
-  void set_array(int array, qpGeomVertexArrayFormat *format);
+  void set_array(int array, const qpGeomVertexArrayFormat *format);
   void remove_array(int array);
-  int add_array(qpGeomVertexArrayFormat *array_format);
-  void insert_array(int array, qpGeomVertexArrayFormat *array_format);
+  int add_array(const qpGeomVertexArrayFormat *array_format);
+  void insert_array(int array, const qpGeomVertexArrayFormat *array_format);
   void clear_arrays();
 
   int get_num_columns() const;

+ 2 - 2
panda/src/gobj/sliderTable.I

@@ -48,7 +48,7 @@ is_registered() const {
 //               this point on.
 ////////////////////////////////////////////////////////////////////
 INLINE CPT(SliderTable) SliderTable::
-register_table(SliderTable *table) {
+register_table(const SliderTable *table) {
   // We don't actually bother adding the table object to a registry.
   // This means there may be multiple copies of identical registered
   // SliderTables.  Big deal.  We can always go back and make a
@@ -57,7 +57,7 @@ register_table(SliderTable *table) {
     return table;
   }
 
-  table->do_register();
+  ((SliderTable *)table)->do_register();
   return table;
 }
 

+ 36 - 14
panda/src/gobj/sliderTable.cxx

@@ -166,9 +166,38 @@ void SliderTable::
 write_datagram(BamWriter *manager, Datagram &dg) {
   TypedWritable::write_datagram(manager, dg);
 
+  dg.add_uint16(_sliders.size());
+  Sliders::const_iterator si;
+  for (si = _sliders.begin(); si != _sliders.end(); ++si) {
+    manager->write_pointer(dg, (*si).first);
+    manager->write_pointer(dg, (*si).second);
+  }
+
   manager->write_cdata(dg, _cycler);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: SliderTable::complete_pointers
+//       Access: Public, Virtual
+//  Description: Receives an array of pointers, one for each time
+//               manager->read_pointer() was called in fillin().
+//               Returns the number of pointers processed.
+////////////////////////////////////////////////////////////////////
+int SliderTable::
+complete_pointers(TypedWritable **p_list, BamReader *manager) {
+  int pi = TypedWritableReferenceCount::complete_pointers(p_list, manager);
+
+  for (size_t i = 0; i < _num_sliders; ++i) {
+    CPT(InternalName) name = DCAST(InternalName, p_list[pi++]);
+    PT(VertexSlider) slider = DCAST(VertexSlider, p_list[pi++]);
+
+    bool inserted = _sliders.insert(Sliders::value_type(name, slider)).second;
+    nassertr(inserted, pi);
+  }
+
+  return pi;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: SliderTable::make_from_bam
 //       Access: Protected, Static
@@ -200,6 +229,12 @@ void SliderTable::
 fillin(DatagramIterator &scan, BamReader *manager) {
   TypedWritable::fillin(scan, manager);
 
+  _num_sliders = scan.get_uint16();
+  for (size_t i = 0; i < _num_sliders; ++i) {
+    manager->read_pointer(scan);
+    manager->read_pointer(scan);
+  }
+
   manager->read_cdata(scan, _cycler);
 }
 
@@ -223,20 +258,6 @@ void SliderTable::CData::
 write_datagram(BamWriter *manager, Datagram &dg) const {
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: SliderTable::CData::complete_pointers
-//       Access: Public, Virtual
-//  Description: Receives an array of pointers, one for each time
-//               manager->read_pointer() was called in fillin().
-//               Returns the number of pointers processed.
-////////////////////////////////////////////////////////////////////
-int SliderTable::CData::
-complete_pointers(TypedWritable **p_list, BamReader *manager) {
-  int pi = CycleData::complete_pointers(p_list, manager);
-
-  return pi;
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: SliderTable::CData::fillin
 //       Access: Public, Virtual
@@ -246,4 +267,5 @@ complete_pointers(TypedWritable **p_list, BamReader *manager) {
 ////////////////////////////////////////////////////////////////////
 void SliderTable::CData::
 fillin(DatagramIterator &scan, BamReader *manager) {
+  _modified = VertexTransform::get_next_modified();
 }

+ 6 - 3
panda/src/gobj/sliderTable.h

@@ -53,7 +53,7 @@ PUBLISHED:
   virtual ~SliderTable();
 
   INLINE bool is_registered() const;
-  INLINE static CPT(SliderTable) register_table(SliderTable *table);
+  INLINE static CPT(SliderTable) register_table(const SliderTable *table);
 
   INLINE const VertexSlider *get_slider(const InternalName *name) const;
   INLINE bool has_slider(const InternalName *name) const;
@@ -73,9 +73,12 @@ private:
 private:
   bool _is_registered;
 
-  typedef pmap< const InternalName *, PT(VertexSlider) > Sliders;
+  typedef pmap< CPT(InternalName), PT(VertexSlider) > Sliders;
   Sliders _sliders;
 
+  // This is only filled in while reading from the bam stream.
+  size_t _num_sliders;
+
   // This is the data that must be cycled between pipeline stages.
   class EXPCL_PANDA CData : public CycleData {
   public:
@@ -83,7 +86,6 @@ private:
     INLINE CData(const CData &copy);
     virtual CycleData *make_copy() const;
     virtual void write_datagram(BamWriter *manager, Datagram &dg) const;
-    virtual int complete_pointers(TypedWritable **plist, BamReader *manager);
     virtual void fillin(DatagramIterator &scan, BamReader *manager);
 
     UpdateSeq _modified;
@@ -96,6 +98,7 @@ private:
 public:
   static void register_with_read_factory();
   virtual void write_datagram(BamWriter *manager, Datagram &dg);
+  virtual int complete_pointers(TypedWritable **plist, BamReader *manager);
 
 protected:
   static TypedWritable *make_from_bam(const FactoryParams &params);

+ 27 - 1
panda/src/gobj/transformBlend.cxx

@@ -251,6 +251,13 @@ clear_result() {
 ////////////////////////////////////////////////////////////////////
 void TransformBlend::
 write_datagram(BamWriter *manager, Datagram &dg) const {
+  dg.add_uint16(_entries.size());
+
+  Entries::const_iterator ei;
+  for (ei = _entries.begin(); ei != _entries.end(); ++ei) {
+    manager->write_pointer(dg, (*ei)._transform);
+    dg.add_float32((*ei)._weight);
+  }
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -262,7 +269,18 @@ write_datagram(BamWriter *manager, Datagram &dg) const {
 ////////////////////////////////////////////////////////////////////
 int TransformBlend::
 complete_pointers(TypedWritable **p_list, BamReader *manager) {
-  return 0;
+  int pi = 0;
+
+  Entries::iterator ei;
+  for (ei = _entries.begin(); ei != _entries.end(); ++ei) {
+    (*ei)._transform = DCAST(VertexTransform, p_list[pi++]);
+  }
+
+  // Now that we have actual pointers, we can sort the list of
+  // entries.
+  _entries.sort();
+
+  return pi;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -274,6 +292,14 @@ complete_pointers(TypedWritable **p_list, BamReader *manager) {
 ////////////////////////////////////////////////////////////////////
 void TransformBlend::
 fillin(DatagramIterator &scan, BamReader *manager) {
+  size_t num_entries = scan.get_uint16();
+  _entries.reserve(num_entries);
+  for (size_t i = 0; i < num_entries; ++i) {
+    TransformEntry entry;
+    manager->read_pointer(scan);
+    entry._weight = scan.get_float32();
+    _entries.push_back(entry);
+  }
 }
 
 ////////////////////////////////////////////////////////////////////

+ 44 - 0
panda/src/gobj/transformBlendPalette.cxx

@@ -223,6 +223,12 @@ void TransformBlendPalette::
 write_datagram(BamWriter *manager, Datagram &dg) {
   TypedWritable::write_datagram(manager, dg);
 
+  dg.add_uint16(_blends.size());
+  Blends::const_iterator bi;
+  for (bi = _blends.begin(); bi != _blends.end(); ++bi) {
+    (*bi).write_datagram(manager, dg);
+  }
+
   manager->write_cdata(dg, _cycler);
 }
 
@@ -236,6 +242,12 @@ write_datagram(BamWriter *manager, Datagram &dg) {
 int TransformBlendPalette::
 complete_pointers(TypedWritable **p_list, BamReader *manager) {
   int pi = TypedWritable::complete_pointers(p_list, manager);
+
+  Blends::iterator bi;
+  for (bi = _blends.begin(); bi != _blends.end(); ++bi) {
+    pi += (*bi).complete_pointers(p_list + pi, manager);
+  }
+
   return pi;
 }
 
@@ -270,6 +282,15 @@ void TransformBlendPalette::
 fillin(DatagramIterator &scan, BamReader *manager) {
   TypedWritable::fillin(scan, manager);
 
+  size_t num_blends = scan.get_uint16();
+  _blends.reserve(num_blends);
+  size_t i;
+  for (i = 0; i < num_blends; ++i) {
+    TransformBlend blend;
+    blend.fillin(scan, manager);
+    _blends.push_back(blend);
+  }
+
   manager->read_cdata(scan, _cycler);
 }
 
@@ -282,3 +303,26 @@ CycleData *TransformBlendPalette::CData::
 make_copy() const {
   return new CData(*this);
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformBlendPalette::CData::write_datagram
+//       Access: Public, Virtual
+//  Description: Writes the contents of this object to the datagram
+//               for shipping out to a Bam file.
+////////////////////////////////////////////////////////////////////
+void TransformBlendPalette::CData::
+write_datagram(BamWriter *manager, Datagram &dg) const {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformBlendPalette::CData::fillin
+//       Access: Public, Virtual
+//  Description: This internal function is called by make_from_bam to
+//               read in all of the relevant data from the BamFile for
+//               the new TransformBlendPalette.
+////////////////////////////////////////////////////////////////////
+void TransformBlendPalette::CData::
+fillin(DatagramIterator &scan, BamReader *manager) {
+  _modified = VertexTransform::get_next_modified();
+  _global_modified = VertexTransform::get_global_modified();
+}

+ 2 - 0
panda/src/gobj/transformBlendPalette.h

@@ -99,6 +99,8 @@ private:
     INLINE CData();
     INLINE CData(const CData &copy);
     virtual CycleData *make_copy() const;
+    virtual void write_datagram(BamWriter *manager, Datagram &dg) const;
+    virtual void fillin(DatagramIterator &scan, BamReader *manager);
 
     UpdateSeq _modified;
     UpdateSeq _global_modified;

+ 2 - 2
panda/src/gobj/transformPalette.I

@@ -48,7 +48,7 @@ is_registered() const {
 //               this point on.
 ////////////////////////////////////////////////////////////////////
 INLINE CPT(TransformPalette) TransformPalette::
-register_palette(TransformPalette *palette) {
+register_palette(const TransformPalette *palette) {
   // We don't actually bother adding the palette object to a registry.
   // This means there may be multiple copies of identical registered
   // TransformPalettes.  Big deal.  We can always go back and make a
@@ -57,7 +57,7 @@ register_palette(TransformPalette *palette) {
     return palette;
   }
 
-  palette->do_register();
+  ((TransformPalette *)palette)->do_register();
   return palette;
 }
 

+ 37 - 16
panda/src/gobj/transformPalette.cxx

@@ -176,11 +176,38 @@ register_with_read_factory() {
 ////////////////////////////////////////////////////////////////////
 void TransformPalette::
 write_datagram(BamWriter *manager, Datagram &dg) {
-  TypedWritable::write_datagram(manager, dg);
+  TypedWritableReferenceCount::write_datagram(manager, dg);
+
+  dg.add_uint16(_transforms.size());
+  for (Transforms::const_iterator ti = _transforms.begin();
+       ti != _transforms.end();
+       ++ti) {
+    manager->write_pointer(dg, *ti);
+  }
 
   manager->write_cdata(dg, _cycler);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: TransformPalette::complete_pointers
+//       Access: Public, Virtual
+//  Description: Receives an array of pointers, one for each time
+//               manager->read_pointer() was called in fillin().
+//               Returns the number of pointers processed.
+////////////////////////////////////////////////////////////////////
+int TransformPalette::
+complete_pointers(TypedWritable **p_list, BamReader *manager) {
+  int pi = TypedWritableReferenceCount::complete_pointers(p_list, manager);
+
+  for (Transforms::iterator ti = _transforms.begin();
+       ti != _transforms.end();
+       ++ti) {
+    (*ti) = DCAST(VertexTransform, p_list[pi++]);
+  }
+
+  return pi;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: TransformPalette::make_from_bam
 //       Access: Protected, Static
@@ -210,7 +237,14 @@ make_from_bam(const FactoryParams &params) {
 ////////////////////////////////////////////////////////////////////
 void TransformPalette::
 fillin(DatagramIterator &scan, BamReader *manager) {
-  TypedWritable::fillin(scan, manager);
+  TypedWritableReferenceCount::fillin(scan, manager);
+
+  size_t num_transforms = scan.get_uint16();
+  _transforms.reserve(num_transforms);
+  for (size_t i = 0; i < num_transforms; ++i) {
+    manager->read_pointer(scan);
+    _transforms.push_back(NULL);
+  }
 
   manager->read_cdata(scan, _cycler);
 }
@@ -235,20 +269,6 @@ void TransformPalette::CData::
 write_datagram(BamWriter *manager, Datagram &dg) const {
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: TransformPalette::CData::complete_pointers
-//       Access: Public, Virtual
-//  Description: Receives an array of pointers, one for each time
-//               manager->read_pointer() was called in fillin().
-//               Returns the number of pointers processed.
-////////////////////////////////////////////////////////////////////
-int TransformPalette::CData::
-complete_pointers(TypedWritable **p_list, BamReader *manager) {
-  int pi = CycleData::complete_pointers(p_list, manager);
-
-  return pi;
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: TransformPalette::CData::fillin
 //       Access: Public, Virtual
@@ -258,4 +278,5 @@ complete_pointers(TypedWritable **p_list, BamReader *manager) {
 ////////////////////////////////////////////////////////////////////
 void TransformPalette::CData::
 fillin(DatagramIterator &scan, BamReader *manager) {
+  _modified = VertexTransform::get_next_modified();
 }

+ 2 - 2
panda/src/gobj/transformPalette.h

@@ -52,7 +52,7 @@ PUBLISHED:
   virtual ~TransformPalette();
 
   INLINE bool is_registered() const;
-  INLINE static CPT(TransformPalette) register_palette(TransformPalette *palette);
+  INLINE static CPT(TransformPalette) register_palette(const TransformPalette *palette);
 
   INLINE int get_num_transforms() const;
   INLINE const VertexTransform *get_transform(int n) const;
@@ -82,7 +82,6 @@ private:
     INLINE CData(const CData &copy);
     virtual CycleData *make_copy() const;
     virtual void write_datagram(BamWriter *manager, Datagram &dg) const;
-    virtual int complete_pointers(TypedWritable **plist, BamReader *manager);
     virtual void fillin(DatagramIterator &scan, BamReader *manager);
 
     UpdateSeq _modified;
@@ -95,6 +94,7 @@ private:
 public:
   static void register_with_read_factory();
   virtual void write_datagram(BamWriter *manager, Datagram &dg);
+  virtual int complete_pointers(TypedWritable **plist, BamReader *manager);
 
 protected:
   static TypedWritable *make_from_bam(const FactoryParams &params);

+ 2 - 0
panda/src/gobj/userVertexSlider.cxx

@@ -131,6 +131,7 @@ fillin(DatagramIterator &scan, BamReader *manager) {
 ////////////////////////////////////////////////////////////////////
 void UserVertexSlider::CData::
 write_datagram(BamWriter *manager, Datagram &dg) const {
+  dg.add_float32(_slider);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -142,4 +143,5 @@ write_datagram(BamWriter *manager, Datagram &dg) const {
 ////////////////////////////////////////////////////////////////////
 void UserVertexSlider::CData::
 fillin(DatagramIterator &scan, BamReader *manager) {
+  _slider = scan.get_float32();
 }

+ 2 - 0
panda/src/gobj/userVertexTransform.cxx

@@ -130,6 +130,7 @@ fillin(DatagramIterator &scan, BamReader *manager) {
 ////////////////////////////////////////////////////////////////////
 void UserVertexTransform::CData::
 write_datagram(BamWriter *manager, Datagram &dg) const {
+  _matrix.write_datagram(dg);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -141,4 +142,5 @@ write_datagram(BamWriter *manager, Datagram &dg) const {
 ////////////////////////////////////////////////////////////////////
 void UserVertexTransform::CData::
 fillin(DatagramIterator &scan, BamReader *manager) {
+  _matrix.read_datagram(scan);
 }

+ 5 - 4
panda/src/pgraph/renderAttrib.cxx

@@ -399,12 +399,13 @@ change_this(TypedWritable *old_ptr, BamReader *manager) {
 ////////////////////////////////////////////////////////////////////
 //     Function: RenderAttrib::finalize
 //       Access: Public, Virtual
-//  Description: Method to ensure that any necessary clean up tasks
-//               that have to be performed by this object are performed
+//  Description: Called by the BamReader to perform any final actions
+//               needed for setting up the object after all objects
+//               have been read and all pointers have been completed.
 ////////////////////////////////////////////////////////////////////
 void RenderAttrib::
-finalize() {
-  // Unref the pointer that we explicitly reffed in make_from_bam().
+finalize(BamReader *) {
+  // Unref the pointer that we explicitly reffed in change_this().
   unref();
 
   // We should never get back to zero after unreffing our own count,

+ 1 - 1
panda/src/pgraph/renderAttrib.h

@@ -112,7 +112,7 @@ private:
 public:
   virtual void write_datagram(BamWriter *manager, Datagram &dg);
   static TypedWritable *change_this(TypedWritable *old_ptr, BamReader *manager);
-  virtual void finalize();
+  virtual void finalize(BamReader *manager);
 
 protected:
   static TypedWritable *new_from_bam(RenderAttrib *attrib, BamReader *manager);

+ 5 - 4
panda/src/pgraph/renderEffect.cxx

@@ -397,12 +397,13 @@ change_this(TypedWritable *old_ptr, BamReader *manager) {
 ////////////////////////////////////////////////////////////////////
 //     Function: RenderEffect::finalize
 //       Access: Public, Virtual
-//  Description: Method to ensure that any necessary clean up tasks
-//               that have to be performed by this object are performed
+//  Description: Called by the BamReader to perform any final actions
+//               needed for setting up the object after all objects
+//               have been read and all pointers have been completed.
 ////////////////////////////////////////////////////////////////////
 void RenderEffect::
-finalize() {
-  // Unref the pointer that we explicitly reffed in make_from_bam().
+finalize(BamReader *) {
+  // Unref the pointer that we explicitly reffed in change_this().
   unref();
 
   // We should never get back to zero after unreffing our own count,

+ 1 - 1
panda/src/pgraph/renderEffect.h

@@ -104,7 +104,7 @@ private:
 public:
   virtual void write_datagram(BamWriter *manager, Datagram &dg);
   static TypedWritable *change_this(TypedWritable *old_ptr, BamReader *manager);
-  virtual void finalize();
+  virtual void finalize(BamReader *manager);
 
 protected:
   static TypedWritable *new_from_bam(RenderEffect *effect, BamReader *manager);

+ 2 - 2
panda/src/pgraph/renderEffects.cxx

@@ -735,8 +735,8 @@ change_this(TypedWritable *old_ptr, BamReader *manager) {
 //               have been read and all pointers have been completed.
 ////////////////////////////////////////////////////////////////////
 void RenderEffects::
-finalize() {
-  // Unref the pointer that we explicitly reffed in make_from_bam().
+finalize(BamReader *) {
+  // Unref the pointer that we explicitly reffed in change_this().
   unref();
 
   // We should never get back to zero after unreffing our own count,

+ 1 - 1
panda/src/pgraph/renderEffects.h

@@ -161,7 +161,7 @@ public:
   virtual void write_datagram(BamWriter *manager, Datagram &dg);
   virtual int complete_pointers(TypedWritable **plist, BamReader *manager);
   static TypedWritable *change_this(TypedWritable *old_ptr, BamReader *manager);
-  virtual void finalize();
+  virtual void finalize(BamReader *manager);
 
 protected:
   static TypedWritable *make_from_bam(const FactoryParams &params);

+ 2 - 2
panda/src/pgraph/renderState.cxx

@@ -1696,8 +1696,8 @@ change_this(TypedWritable *old_ptr, BamReader *manager) {
 //               have been read and all pointers have been completed.
 ////////////////////////////////////////////////////////////////////
 void RenderState::
-finalize() {
-  // Unref the pointer that we explicitly reffed in make_from_bam().
+finalize(BamReader *) {
+  // Unref the pointer that we explicitly reffed in change_this().
   unref();
 
   // We should never get back to zero after unreffing our own count,

+ 1 - 1
panda/src/pgraph/renderState.h

@@ -267,7 +267,7 @@ public:
   virtual void write_datagram(BamWriter *manager, Datagram &dg);
   virtual int complete_pointers(TypedWritable **plist, BamReader *manager);
   static TypedWritable *change_this(TypedWritable *old_ptr, BamReader *manager);
-  virtual void finalize();
+  virtual void finalize(BamReader *manager);
 
 protected:
   static TypedWritable *make_from_bam(const FactoryParams &params);

+ 2 - 2
panda/src/pgraph/transformState.cxx

@@ -1598,8 +1598,8 @@ change_this(TypedWritable *old_ptr, BamReader *manager) {
 //               have been read and all pointers have been completed.
 ////////////////////////////////////////////////////////////////////
 void TransformState::
-finalize() {
-  // Unref the pointer that we explicitly reffed in make_from_bam().
+finalize(BamReader *) {
+  // Unref the pointer that we explicitly reffed in change_this().
   unref();
 
   // We should never get back to zero after unreffing our own count,

+ 1 - 1
panda/src/pgraph/transformState.h

@@ -247,7 +247,7 @@ public:
   static void register_with_read_factory();
   virtual void write_datagram(BamWriter *manager, Datagram &dg);
   static TypedWritable *change_this(TypedWritable *old_ptr, BamReader *manager);
-  virtual void finalize();
+  virtual void finalize(BamReader *manager);
 
 protected:
   static TypedWritable *make_from_bam(const FactoryParams &params);

+ 67 - 7
panda/src/putil/bamReader.cxx

@@ -293,7 +293,19 @@ resolve() {
         
         // Does the pointer need to change?
         if (created_obj._change_this != NULL) {
-          created_obj._ptr = created_obj._change_this(object_ptr, this);
+          TypedWritable *new_ptr = created_obj._change_this(object_ptr, this);
+          if (new_ptr != object_ptr) {
+            // Also update the reverse
+            vector_int &old_refs = _created_objs_by_pointer[object_ptr];
+            vector_int &new_refs = _created_objs_by_pointer[new_ptr];
+            for (vector_int::const_iterator oi = old_refs.begin();
+                 oi != old_refs.end();
+                 ++oi) {
+              new_refs.push_back(*oi);
+            }
+            _created_objs_by_pointer.erase(object_ptr);
+          }
+          created_obj._ptr = new_ptr;
           created_obj._change_this = NULL;
         }
         any_completed_this_pass = true;
@@ -356,6 +368,52 @@ resolve() {
   return all_completed;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: BamReader::change_pointer
+//       Access: Public
+//  Description: Indicates that an object recently read from the bam
+//               stream should be replaced with a new object.  Any
+//               future occurrences of the original object in the
+//               stream will henceforth return the new object instead.
+//
+//               The return value is true if the replacement was
+//               successfully made, or false if the object was not
+//               read from the stream (or if change_pointer had
+//               already been called on it).
+////////////////////////////////////////////////////////////////////
+bool BamReader::
+change_pointer(const TypedWritable *orig_pointer, const TypedWritable *new_pointer) {
+  if (orig_pointer == new_pointer) {
+    return false;
+  }
+  CreatedObjsByPointer::iterator ci = _created_objs_by_pointer.find(orig_pointer);
+  if (ci == _created_objs_by_pointer.end()) {
+    // No record of this object.
+    return false;
+  }
+
+  const vector_int &old_refs = (*ci).second;
+  vector_int &new_refs = _created_objs_by_pointer[new_pointer];
+
+  for (vector_int::const_iterator oi = old_refs.begin(); 
+       oi != old_refs.end();
+       ++oi) {
+    int object_id = (*oi);
+
+    CreatedObjs::iterator ci = _created_objs.find(object_id);
+    nassertr(ci != _created_objs.end(), false);
+    nassertr((*ci).second._ptr == orig_pointer, false);
+
+    (*ci).second._ptr = (TypedWritable *)new_pointer;
+    new_refs.push_back(object_id);
+  }
+
+  _created_objs_by_pointer.erase(ci);
+
+  return true;
+}
+
+
 ////////////////////////////////////////////////////////////////////
 //     Function: BamReader::read_handle
 //       Access: Public
@@ -546,10 +604,9 @@ read_cdata(DatagramIterator &scan, PipelineCyclerBase &cycler) {
 //               all the objects and pointers in the Bam file are
 //               completely read.
 //
-//               This provides a hook for objects (like Characters)
-//               that need to do any additional finalization work
-//               after all of their related pointers are guaranteed to
-//               be filled in.
+//               This provides a hook for objects that need to do any
+//               additional finalization work after all of their
+//               related pointers are guaranteed to be filled in.
 ////////////////////////////////////////////////////////////////////
 void BamReader::
 register_finalize(TypedWritable *whom) {
@@ -613,7 +670,7 @@ finalize_now(TypedWritable *whom) {
   Finalize::iterator fi = _finalize_list.find(whom);
   if (fi != _finalize_list.end()) {
     _finalize_list.erase(fi);
-    whom->finalize();
+    whom->finalize(this);
   }
 }
 
@@ -717,6 +774,7 @@ free_object_ids(DatagramIterator &scan) {
           << " before removing from table.\n";
       }
 
+      _created_objs_by_pointer.erase((*ci).second._ptr);
       _created_objs.erase(ci);
     }
   }
@@ -875,6 +933,8 @@ p_read_object() {
       }
     }
 
+    _created_objs_by_pointer[created_obj._ptr].push_back(object_id);
+
     // Just some sanity checks
     if (object == (TypedWritable *)NULL) {
       if (bam_cat.is_debug()) {
@@ -1059,7 +1119,7 @@ finalize() {
     TypedWritable *object = (*fi);
     nassertv(object != (TypedWritable *)NULL);
     _finalize_list.erase(fi);
-    object->finalize();
+    object->finalize(this);
 
     fi = _finalize_list.begin();
   }

+ 6 - 0
panda/src/putil/bamReader.h

@@ -105,6 +105,8 @@ public:
   INLINE bool is_eof() const;
   bool resolve();
 
+  bool change_pointer(const TypedWritable *orig_pointer, const TypedWritable *new_pointer);
+
   INLINE int get_file_major_ver() const;
   INLINE int get_file_minor_ver() const;
 
@@ -184,6 +186,10 @@ private:
   // if we are within a read_cdata() call.
   PipelineCyclerBase *_reading_cycler;
 
+  // This is the reverse lookup into the above map.
+  typedef phash_map<const TypedWritable *, vector_int, pointer_hash> CreatedObjsByPointer;
+  CreatedObjsByPointer _created_objs_by_pointer;
+
   // This records all the objects that still need their pointers
   // completed, along with the object ID's of the pointers they need,
   // in the order in which read_pointer() was called, so that we may

+ 1 - 0
panda/src/putil/cycleDataReader.I

@@ -32,6 +32,7 @@ CycleDataReader(const PipelineCycler<CycleDataType> &cycler) :
 {
   _pointer = _cycler->read();
   _write_pointer = (CycleDataType *)NULL;
+  nassertv(_pointer != (const CycleDataType *)NULL);
 }
 
 ////////////////////////////////////////////////////////////////////

+ 1 - 0
panda/src/putil/cycleDataWriter.I

@@ -31,6 +31,7 @@ CycleDataWriter(PipelineCycler<CycleDataType> &cycler) :
   _cycler(&cycler)
 {
   _pointer = _cycler->write();
+  nassertv(_pointer != (CycleDataType *)NULL);
 }
 
 ////////////////////////////////////////////////////////////////////

+ 1 - 1
panda/src/putil/typedWritable.cxx

@@ -83,7 +83,7 @@ complete_pointers(TypedWritable **, BamReader *) {
 //               have been read and all pointers have been completed.
 ////////////////////////////////////////////////////////////////////
 void TypedWritable::
-finalize() {
+finalize(BamReader *) {
 }
 
 

+ 1 - 1
panda/src/putil/typedWritable.h

@@ -50,7 +50,7 @@ public:
 
   virtual int complete_pointers(TypedWritable **p_list, BamReader *manager);
 
-  virtual void finalize();
+  virtual void finalize(BamReader *manager);
 
 protected:
   void fillin(DatagramIterator &scan, BamReader *manager);

+ 1 - 1
pandatool/src/palettizer/paletteGroup.cxx

@@ -685,7 +685,7 @@ complete_pointers(TypedWritable **p_list, BamReader *manager) {
 //               other pointers being valid.
 ////////////////////////////////////////////////////////////////////
 void PaletteGroup::
-finalize() {
+finalize(BamReader *) {
   // Now we can copy the pages into the actual map.
   pvector<PalettePage *>::const_iterator pi;
   for (pi = _load_pages.begin(); pi != _load_pages.end(); ++pi) {

+ 1 - 1
pandatool/src/palettizer/paletteGroup.h

@@ -110,7 +110,7 @@ public:
   virtual void write_datagram(BamWriter *writer, Datagram &datagram);
   virtual int complete_pointers(TypedWritable **p_list,
                                 BamReader *manager);
-  virtual void finalize();
+  virtual void finalize(BamReader *manager);
 
 protected:
   static TypedWritable *make_PaletteGroup(const FactoryParams &params);