Просмотр исходного кода

new pgraph bam rules, cdata copy constructors

David Rose 24 лет назад
Родитель
Сommit
9145e25ad4
69 измененных файлов с 1403 добавлено и 750 удалено
  1. 1 1
      panda/src/chan/animBundleNode.cxx
  2. 2 2
      panda/src/chan/animGroup.cxx
  3. 1 1
      panda/src/chan/partBundleNode.cxx
  4. 1 1
      panda/src/chan/partGroup.cxx
  5. 1 1
      panda/src/chan/qpanimBundleNode.cxx
  6. 1 1
      panda/src/chan/qppartBundleNode.cxx
  7. 2 2
      panda/src/char/character.cxx
  8. 2 2
      panda/src/char/characterJoint.cxx
  9. 2 2
      panda/src/char/qpcharacter.cxx
  10. 1 1
      panda/src/collide/collisionNode.cxx
  11. 6 1
      panda/src/display/graphicsWindow.cxx
  12. 4 4
      panda/src/effects/lensFlareNode.cxx
  13. 1 1
      panda/src/gobj/geomSprite.cxx
  14. 39 39
      panda/src/graph/boundedObject.I
  15. 35 13
      panda/src/graph/boundedObject.cxx
  16. 4 2
      panda/src/graph/boundedObject.h
  17. 2 2
      panda/src/graph/node.cxx
  18. 3 3
      panda/src/graph/nodeRelation.cxx
  19. 1 1
      panda/src/parametrics/piecewiseCurve.cxx
  20. 1 1
      panda/src/pgraph/materialAttrib.cxx
  21. 4 2
      panda/src/pgraph/pandaNode.I
  22. 332 265
      panda/src/pgraph/pandaNode.cxx
  23. 8 4
      panda/src/pgraph/pandaNode.h
  24. 1 13
      panda/src/pgraph/qpcamera.cxx
  25. 2 2
      panda/src/pgraph/qpcamera.h
  26. 67 69
      panda/src/pgraph/qpgeomNode.cxx
  27. 5 4
      panda/src/pgraph/qpgeomNode.h
  28. 1 12
      panda/src/pgraph/qplensNode.I
  29. 2 3
      panda/src/pgraph/qplensNode.h
  30. 12 0
      panda/src/pgraph/qplodNode.I
  31. 17 28
      panda/src/pgraph/qplodNode.cxx
  32. 4 3
      panda/src/pgraph/qplodNode.h
  33. 5 5
      panda/src/pgraph/qpnodePathComponent.I
  34. 34 34
      panda/src/pgraph/qpsequenceNode.cxx
  35. 2 1
      panda/src/pgraph/qpsequenceNode.h
  36. 1 1
      panda/src/pgraph/renderState.cxx
  37. 1 12
      panda/src/pgraph/selectiveChildNode.I
  38. 6 2
      panda/src/pgraph/selectiveChildNode.h
  39. 1 1
      panda/src/pgraph/textureAttrib.cxx
  40. 205 91
      panda/src/putil/bamReader.cxx
  41. 21 7
      panda/src/putil/bamReader.h
  42. 30 0
      panda/src/putil/bamWriter.cxx
  43. 8 2
      panda/src/putil/bamWriter.h
  44. 35 0
      panda/src/putil/cycleData.cxx
  45. 10 0
      panda/src/putil/cycleData.h
  46. 68 2
      panda/src/putil/cycleDataReader.I
  47. 5 0
      panda/src/putil/cycleDataReader.h
  48. 46 7
      panda/src/putil/cycleDataWriter.I
  49. 4 1
      panda/src/putil/cycleDataWriter.h
  50. 102 0
      panda/src/putil/pipelineCycler.I
  51. 5 0
      panda/src/putil/pipelineCycler.h
  52. 202 20
      panda/src/putil/pipelineCyclerBase.I
  53. 0 37
      panda/src/putil/pipelineCyclerBase.cxx
  54. 8 2
      panda/src/putil/pipelineCyclerBase.h
  55. 6 6
      panda/src/putil/test_bam.cxx
  56. 1 1
      panda/src/sgattrib/materialTransition.cxx
  57. 1 1
      panda/src/sgattrib/textureTransition.cxx
  58. 1 1
      panda/src/sgraph/geomNode.cxx
  59. 2 2
      pandatool/src/egg-palettize/eggFile.cxx
  60. 2 2
      pandatool/src/egg-palettize/paletteGroup.cxx
  61. 1 1
      pandatool/src/egg-palettize/paletteGroups.cxx
  62. 2 2
      pandatool/src/egg-palettize/paletteImage.cxx
  63. 2 2
      pandatool/src/egg-palettize/palettePage.cxx
  64. 7 7
      pandatool/src/egg-palettize/palettizer.cxx
  65. 1 1
      pandatool/src/egg-palettize/sourceTextureImage.cxx
  66. 3 3
      pandatool/src/egg-palettize/textureImage.cxx
  67. 5 5
      pandatool/src/egg-palettize/texturePlacement.cxx
  68. 2 2
      pandatool/src/egg-palettize/textureProperties.cxx
  69. 3 3
      pandatool/src/egg-palettize/textureReference.cxx

+ 1 - 1
panda/src/chan/animBundleNode.cxx

@@ -64,7 +64,7 @@ void AnimBundleNode::
 fillin(DatagramIterator& scan, BamReader* manager)
 {
   NamedNode::fillin(scan, manager);
-  manager->read_pointer(scan, this);
+  manager->read_pointer(scan);
 }
 
 ////////////////////////////////////////////////////////////////////

+ 2 - 2
panda/src/chan/animGroup.cxx

@@ -205,11 +205,11 @@ void AnimGroup::
 fillin(DatagramIterator& scan, BamReader* manager)
 {
   set_name(scan.get_string());
-  manager->read_pointer(scan, this);
+  manager->read_pointer(scan);
   _num_children = scan.get_uint16();
   for(int i = 0; i < _num_children; i++)
   {
-    manager->read_pointer(scan, this);
+    manager->read_pointer(scan);
   }
 }
 

+ 1 - 1
panda/src/chan/partBundleNode.cxx

@@ -65,7 +65,7 @@ void PartBundleNode::
 fillin(DatagramIterator& scan, BamReader* manager)
 {
   NamedNode::fillin(scan, manager);
-  manager->read_pointer(scan, this);
+  manager->read_pointer(scan);
 }
 
 ////////////////////////////////////////////////////////////////////

+ 1 - 1
panda/src/chan/partGroup.cxx

@@ -497,7 +497,7 @@ fillin(DatagramIterator& scan, BamReader* manager)
   _num_children = scan.get_uint16();
   for(i = 0; i < _num_children; i++)
   {
-    manager->read_pointer(scan, this);
+    manager->read_pointer(scan);
   }
 }
 

+ 1 - 1
panda/src/chan/qpanimBundleNode.cxx

@@ -106,5 +106,5 @@ make_from_bam(const FactoryParams &params) {
 void qpAnimBundleNode::
 fillin(DatagramIterator &scan, BamReader* manager) {
   PandaNode::fillin(scan, manager);
-  manager->read_pointer(scan, this);
+  manager->read_pointer(scan);
 }

+ 1 - 1
panda/src/chan/qppartBundleNode.cxx

@@ -76,5 +76,5 @@ complete_pointers(TypedWritable **p_list, BamReader* manager) {
 void qpPartBundleNode::
 fillin(DatagramIterator &scan, BamReader* manager) {
   PandaNode::fillin(scan, manager);
-  manager->read_pointer(scan, this);
+  manager->read_pointer(scan);
 }

+ 2 - 2
panda/src/char/character.cxx

@@ -426,7 +426,7 @@ fillin(DatagramIterator& scan, BamReader* manager)
 {
   PartBundleNode::fillin(scan, manager);
   _cv.fillin(scan, manager);
-  manager->read_pointer(scan, this);
+  manager->read_pointer(scan);
 
   // Read the number of parts to expect in the _parts list, and then
   // fill the array up with NULLs.  We'll fill in the actual values in
@@ -435,7 +435,7 @@ fillin(DatagramIterator& scan, BamReader* manager)
   _parts.clear();
   _parts.reserve(num_parts);
   for (int i = 0; i < num_parts; i++) {
-    manager->read_pointer(scan, this);
+    manager->read_pointer(scan);
     _parts.push_back((PartGroup *)NULL);
   }
 

+ 2 - 2
panda/src/char/characterJoint.cxx

@@ -339,13 +339,13 @@ fillin(DatagramIterator& scan, BamReader* manager)
   _num_net_arcs = scan.get_uint16();
   for(i = 0; i < _num_net_arcs; i++)
   {
-    manager->read_pointer(scan, this);
+    manager->read_pointer(scan);
   }
 
   _num_local_arcs = scan.get_uint16();
   for(i = 0; i < _num_local_arcs; i++)
   {
-    manager->read_pointer(scan, this);
+    manager->read_pointer(scan);
   }
 
   _initial_net_transform_inverse.read_datagram(scan);

+ 2 - 2
panda/src/char/qpcharacter.cxx

@@ -515,7 +515,7 @@ void qpCharacter::
 fillin(DatagramIterator &scan, BamReader *manager) {
   qpPartBundleNode::fillin(scan, manager);
   _cv.fillin(scan, manager);
-  manager->read_pointer(scan, this);
+  manager->read_pointer(scan);
 
   // Read the number of parts to expect in the _parts list, and then
   // fill the array up with NULLs.  We'll fill in the actual values in
@@ -524,7 +524,7 @@ fillin(DatagramIterator &scan, BamReader *manager) {
   _parts.clear();
   _parts.reserve(num_parts);
   for (int i = 0; i < num_parts; i++) {
-    manager->read_pointer(scan, this);
+    manager->read_pointer(scan);
     _parts.push_back((PartGroup *)NULL);
   }
 

+ 1 - 1
panda/src/collide/collisionNode.cxx

@@ -286,7 +286,7 @@ fillin(DatagramIterator& scan, BamReader* manager)
   _solids.reserve(num_solids);
 
   for(i = 0; i < num_solids; i++) {
-    manager->read_pointer(scan, this);
+    manager->read_pointer(scan);
     _solids.push_back((CollisionSolid *)NULL);
   }
 

+ 6 - 1
panda/src/display/graphicsWindow.cxx

@@ -34,7 +34,12 @@ TypeHandle GraphicsWindow::WindowPipe::_type_handle;
 
 GraphicsWindow::WindowFactory *GraphicsWindow::_factory = NULL;
 
-#if defined(DO_PSTATS) && !defined(CPPPARSER)
+#ifndef CPPPARSER
+// We must compile these lines, even if DO_PSTATS is not defined,
+// because the symbols for them are declared in the header file.
+// Otherwise they will be undefined symbols at link time.  However,
+// there's no runtime overhead to speak of for declaring these, so
+// there's no harm in compiling them all the time.
 PStatCollector GraphicsWindow::_app_pcollector("App");
 PStatCollector GraphicsWindow::_show_code_pcollector("App:Show code");
 PStatCollector GraphicsWindow::_swap_pcollector("Swap buffers");

+ 4 - 4
panda/src/effects/lensFlareNode.cxx

@@ -449,14 +449,14 @@ fillin(DatagramIterator &scan, BamReader *manager)
   _num_flares = scan.get_uint16();
   for(i = 0; i < _num_flares; i++)
   {
-    manager->read_pointer(scan, this);
+    manager->read_pointer(scan);
   }
-  manager->read_pointer(scan, this);
+  manager->read_pointer(scan);
 
   _num_arcs = scan.get_uint16();
   for(i = 0; i < _num_arcs; i++)
   {
-    manager->read_pointer(scan, this);
+    manager->read_pointer(scan);
   }
 
   size = scan.get_uint16();
@@ -496,7 +496,7 @@ fillin(DatagramIterator &scan, BamReader *manager)
   _blind_fall_off = scan.get_float32();
   _flare_fall_off = scan.get_float32();
 
-  manager->read_pointer(scan, this);
+  manager->read_pointer(scan);
 }
 
 ////////////////////////////////////////////////////////////////////

+ 1 - 1
panda/src/gobj/geomSprite.cxx

@@ -133,7 +133,7 @@ fillin(DatagramIterator& scan, BamReader* manager) {
   _x_bind_type = (GeomBindType) scan.get_uint8();
   _y_bind_type = (GeomBindType) scan.get_uint8();
   _alpha_disable = (scan.get_uint8() !=0);
-  manager->read_pointer(scan, this);
+  manager->read_pointer(scan);
 }
 
 ////////////////////////////////////////////////////////////////////

+ 39 - 39
panda/src/graph/boundedObject.I

@@ -29,6 +29,31 @@ CData() {
   _flags = F_bound_stale;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: BoundedObject::CData::Copy Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE_GRAPH BoundedObject::CData::
+CData(const BoundedObject::CData &copy) :
+  _flags(copy._flags),
+  _bound_type(copy._bound_type),
+  _bound(copy._bound)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BoundedObject::CData::Copy Assignment Operator
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE_GRAPH void BoundedObject::CData::
+operator = (const BoundedObject::CData &copy) {
+  _flags = copy._flags;
+  _bound_type = copy._bound_type;
+  _bound = copy._bound;
+}
+
 
 ////////////////////////////////////////////////////////////////////
 //     Function: BoundedObject::Constructor
@@ -45,12 +70,9 @@ BoundedObject() {
 //  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE_GRAPH BoundedObject::
-BoundedObject(const BoundedObject &copy) {
-  CDWriter cdata(_cycler);
-  CDReader copy_cdata(copy._cycler);
-  cdata->_flags = copy_cdata->_flags;
-  cdata->_bound_type = copy_cdata->_bound_type;
-  cdata->_bound = copy_cdata->_bound;
+BoundedObject(const BoundedObject &copy) :
+  _cycler(copy._cycler)
+{
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -60,11 +82,7 @@ BoundedObject(const BoundedObject &copy) {
 ////////////////////////////////////////////////////////////////////
 INLINE_GRAPH void BoundedObject::
 operator = (const BoundedObject &copy) {
-  CDWriter cdata(_cycler);
-  CDReader copy_cdata(copy._cycler);
-  cdata->_flags = copy_cdata->_flags;
-  cdata->_bound_type = copy_cdata->_bound_type;
-  cdata->_bound = copy_cdata->_bound;
+  _cycler = copy._cycler;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -77,8 +95,8 @@ operator = (const BoundedObject &copy) {
 INLINE_GRAPH void BoundedObject::
 set_bound(BoundedObject::BoundingVolumeType type) {
   nassertv(type != BVT_static);
-  CDWriter cdata(_cycler);
   mark_bound_stale();
+  CDWriter cdata(_cycler);
   cdata->_bound_type = type;
 }
 
@@ -91,35 +109,13 @@ set_bound(BoundedObject::BoundingVolumeType type) {
 ////////////////////////////////////////////////////////////////////
 INLINE_GRAPH void BoundedObject::
 set_bound(const BoundingVolume &bound) {
-  CDWriter cdata(_cycler);
   mark_bound_stale();
+  CDWriter cdata(_cycler);
   cdata->_bound_type = BVT_static;
   cdata->_flags &= ~F_bound_stale;  
   cdata->_bound = bound.make_copy();
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: BoundedObject::get_bound
-//       Access: Published
-//  Description: Returns the current bounding volume on this node,
-//               possibly forcing a recompute.  A node's bounding
-//               volume encloses only the node itself, irrespective of
-//               the nodes above or below it in the graph.  This is
-//               different from the bounding volumes on the arcs,
-//               which enclose all geometry below them.
-////////////////////////////////////////////////////////////////////
-INLINE_GRAPH const BoundingVolume &BoundedObject::
-get_bound() const {
-  CDReader cdata(_cycler);
-  if (cdata->_bound_type == BVT_static) {
-    CDWriter cdata_w(((BoundedObject *)this)->_cycler);
-    cdata_w->_flags &= ~F_bound_stale;
-  } else if (is_bound_stale() || cdata->_bound == (BoundingVolume *)NULL) {
-    ((BoundedObject *)this)->recompute_bound();
-  }
-  return *cdata->_bound;
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: BoundedObject::mark_bound_stale
 //       Access: Published
@@ -135,8 +131,10 @@ mark_bound_stale() {
   if (is_bound_stale()) {
     return false;
   }
-  CDWriter cdata(_cycler);
-  cdata->_flags |= F_bound_stale;
+  {
+    CDWriter cdata(_cycler);
+    cdata->_flags |= F_bound_stale;
+  }
   propagate_stale_bound();
 
   return true;
@@ -151,8 +149,10 @@ mark_bound_stale() {
 ////////////////////////////////////////////////////////////////////
 INLINE_GRAPH void BoundedObject::
 force_bound_stale() {
-  CDWriter cdata(_cycler);
-  cdata->_flags |= F_bound_stale;
+  {
+    CDWriter cdata(_cycler);
+    cdata->_flags |= F_bound_stale;
+  }
   propagate_stale_bound();
 }
 

+ 35 - 13
panda/src/graph/boundedObject.cxx

@@ -23,19 +23,6 @@
 
 TypeHandle BoundedObject::_type_handle;
 
-////////////////////////////////////////////////////////////////////
-//     Function: BoundedObject::CData::Copy Constructor
-//       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-BoundedObject::CData::
-CData(const BoundedObject::CData &copy) :
-  _flags(copy._flags),
-  _bound_type(copy._bound_type),
-  _bound(copy._bound)
-{
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: BoundedObject::Destructor
 //       Access: Public, Virtual
@@ -45,6 +32,41 @@ BoundedObject::
 ~BoundedObject() {
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: BoundedObject::get_bound
+//       Access: Published
+//  Description: Returns the current bounding volume on this node,
+//               possibly forcing a recompute.  A node's bounding
+//               volume encloses only the node itself, irrespective of
+//               the nodes above or below it in the graph.  This is
+//               different from the bounding volumes on the arcs,
+//               which enclose all geometry below them.
+////////////////////////////////////////////////////////////////////
+INLINE_GRAPH const BoundingVolume &BoundedObject::
+get_bound() const {
+  {
+    CDReader cdata(_cycler);
+    if (cdata->_bound_type == BVT_static) {
+      CDWriter cdata_w(((BoundedObject *)this)->_cycler, cdata);
+      cdata_w->_flags &= ~F_bound_stale;
+      return *cdata_w->_bound;
+    }
+    
+    if (!is_bound_stale() && cdata->_bound != (BoundingVolume *)NULL) {
+      return *cdata->_bound;
+    }
+
+    // We need to recompute the bounding volume.  First, we need to
+    // release the old CDReader, so we can get a CDReader in
+    // recompute_bound().
+  }
+
+  // Now it's safe to recompute the bounds.
+  ((BoundedObject *)this)->recompute_bound();
+  CDReader cdata(_cycler);
+  return *cdata->_bound;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: BoundedObject::CData::make_copy
 //       Access: Public, Virtual

+ 4 - 2
panda/src/graph/boundedObject.h

@@ -52,7 +52,7 @@ PUBLISHED:
 
   INLINE_GRAPH void set_bound(BoundingVolumeType type);
   INLINE_GRAPH void set_bound(const BoundingVolume &volume);
-  INLINE_GRAPH const BoundingVolume &get_bound() const;
+  const BoundingVolume &get_bound() const;
 
   INLINE_GRAPH bool mark_bound_stale();
   INLINE_GRAPH void force_bound_stale();
@@ -78,7 +78,9 @@ private:
   class EXPCL_PANDA CData : public CycleData {
   public:
     INLINE_GRAPH CData();
-    CData(const CData &copy);
+    INLINE_GRAPH CData(const CData &copy);
+    INLINE_GRAPH void operator = (const CData &copy);
+
     virtual CycleData *make_copy() const;
 
     int _flags;

+ 2 - 2
panda/src/graph/node.cxx

@@ -636,7 +636,7 @@ fillin(DatagramIterator &scan, BamReader *manager) {
     while (num_types > 0) {
       int num_arcs = scan.get_uint16();
       while (num_arcs > 0) {
-        manager->read_pointer(scan, this);
+        manager->read_pointer(scan);
         num_arcs--;
       }
       num_types--;
@@ -666,7 +666,7 @@ fillin(DatagramIterator &scan, BamReader *manager) {
         DownRelationPointers &drp = _connections[i].get_down();
         int num_arcs = scan.get_uint16();
         while (num_arcs > 0) {
-          manager->read_pointer(scan, this);
+          manager->read_pointer(scan);
           drp.push_back((NodeRelation *)NULL);
           num_arcs--;
         }

+ 3 - 3
panda/src/graph/nodeRelation.cxx

@@ -984,9 +984,9 @@ fillin(DatagramIterator& scan, BamReader* manager)
 {
   _graph_type = manager->read_handle(scan);
   //Read in my parent
-  manager->read_pointer(scan, this);
+  manager->read_pointer(scan);
   //Read in my child
-  manager->read_pointer(scan, this);
+  manager->read_pointer(scan);
   //Get my sort relation
   _sort = scan.get_uint16();
 
@@ -994,7 +994,7 @@ fillin(DatagramIterator& scan, BamReader* manager)
   _num_transitions = scan.get_uint16();
   for(int i = 0; i < _num_transitions; i++)
   {
-    manager->read_pointer(scan, this);
+    manager->read_pointer(scan);
   }
 }
 

+ 1 - 1
panda/src/parametrics/piecewiseCurve.cxx

@@ -610,7 +610,7 @@ fillin(DatagramIterator &scan, BamReader *manager) {
   size_t i;
   for (i = 0; i < num_segs; i++) {
     Curveseg seg;
-    manager->read_pointer(scan, this);
+    manager->read_pointer(scan);
     seg._curve = (ParametricCurve *)NULL;
     seg._tend = scan.get_float64();
     _segs.push_back(seg);

+ 1 - 1
panda/src/pgraph/materialAttrib.cxx

@@ -192,5 +192,5 @@ fillin(DatagramIterator &scan, BamReader *manager) {
   RenderAttrib::fillin(scan, manager);
 
   // Read the _material pointer.
-  manager->read_pointer(scan, this);
+  manager->read_pointer(scan);
 }

+ 4 - 2
panda/src/pgraph/pandaNode.I

@@ -353,8 +353,10 @@ clear_state() {
 ////////////////////////////////////////////////////////////////////
 INLINE void PandaNode::
 set_transform(const TransformState *transform) {
-  CDWriter cdata(_cycler);
-  cdata->_transform = transform;
+  {
+    CDWriter cdata(_cycler);
+    cdata->_transform = transform;
+  }
   mark_bound_stale();
 }
 

+ 332 - 265
panda/src/pgraph/pandaNode.cxx

@@ -53,6 +53,122 @@ make_copy() const {
   return new CData(*this);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNode::CData::write_datagram
+//       Access: Public, Virtual
+//  Description: Writes the contents of this object to the datagram
+//               for shipping out to a Bam file.
+////////////////////////////////////////////////////////////////////
+void PandaNode::CData::
+write_datagram(BamWriter *manager, Datagram &dg) const {
+  manager->write_pointer(dg, _state);
+  manager->write_pointer(dg, _transform);
+
+  // When we write a PandaNode, we write out its complete list of
+  // child node pointers, but we only write out the parent node
+  // pointers that have already been added to the bam file by a
+  // previous write operation.  This is a bit of trickery that allows
+  // us to write out just a subgraph (instead of the complete graph)
+  // when we write out an arbitrary node in the graph, yet also allows
+  // us to keep nodes completely in sync when we use the bam format
+  // for streaming scene graph operations over the network.
+
+  int num_parents = 0;
+  Up::const_iterator ui;
+  for (ui = _up.begin(); ui != _up.end(); ++ui) {
+    PandaNode *parent_node = (*ui).get_parent();
+    if (manager->has_object(parent_node)) {
+      num_parents++;
+    }
+  }
+  nassertv(num_parents == (int)(PN_uint16)num_parents);
+  dg.add_uint16(num_parents);
+  for (ui = _up.begin(); ui != _up.end(); ++ui) {
+    PandaNode *parent_node = (*ui).get_parent();
+    if (manager->has_object(parent_node)) {
+      manager->write_pointer(dg, parent_node);
+    }
+  }
+
+  int num_children = _down.size();
+  nassertv(num_children == (int)(PN_uint16)num_children);
+  dg.add_uint16(num_children);
+
+  // **** We should smarten up the writing of the sort number--most of
+  // the time these will all be zero.
+  Down::const_iterator ci;
+  for (ci = _down.begin(); ci != _down.end(); ++ci) {
+    PandaNode *child_node = (*ci).get_child();
+    int sort = (*ci).get_sort();
+    manager->write_pointer(dg, child_node);
+    dg.add_int32(sort);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNode::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 PandaNode::CData::
+complete_pointers(TypedWritable **p_list, BamReader *manager) {
+  int pi = CycleData::complete_pointers(p_list, manager);
+
+  // Get the state and transform pointers.
+  _state = DCAST(RenderState, p_list[pi++]);
+  _transform = DCAST(TransformState, p_list[pi++]);
+
+  // Get the parent pointers.
+  Up::iterator ui;
+  for (ui = _up.begin(); ui != _up.end(); ++ui) {
+    PT(PandaNode) parent_node = DCAST(PandaNode, p_list[pi++]);
+    (*ui) = UpConnection(parent_node);
+  }
+
+  // Get the child pointers.
+  Down::iterator di;
+  for (di = _down.begin(); di != _down.end(); ++di) {
+    int sort = (*di).get_sort();
+    PT(PandaNode) child_node = DCAST(PandaNode, p_list[pi++]);
+    (*di) = DownConnection(child_node, sort);
+  }
+
+  return pi;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNode::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 PandaNode.
+////////////////////////////////////////////////////////////////////
+void PandaNode::CData::
+fillin(DatagramIterator &scan, BamReader *manager) {
+  // Read the state and transform pointers.
+  manager->read_pointer(scan);
+  manager->read_pointer(scan);
+
+  int num_parents = scan.get_uint16();
+  // Read the list of parent nodes.  Push back a NULL for each one.
+  _up.reserve(num_parents);
+  for (int i = 0; i < num_parents; i++) {
+    manager->read_pointer(scan);
+    _up.push_back(UpConnection(NULL));
+  }
+
+  int num_children = scan.get_uint16();
+  // Read the list of child nodes.  Push back a NULL for each one.
+  _down.reserve(num_children);
+  for (int i = 0; i < num_children; i++) {
+    manager->read_pointer(scan);
+    int sort = scan.get_int32();
+    _down.push_back(DownConnection(NULL, sort));
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaNode::Constructor
 //       Access: Published
@@ -73,16 +189,22 @@ PandaNode::
 ~PandaNode() {
   // We shouldn't have any parents left by the time we destruct, or
   // there's a refcount fault somewhere.
-  CDReader cdata(_cycler);
-  nassertv(cdata->_up.empty());
+#ifndef NDEBUG
+  {
+    CDReader cdata(_cycler);
+    nassertv(cdata->_up.empty());
+  }
+#endif  // NDEBUG
 
   remove_all_children();
 }
 
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaNode::Copy Constructor
-//       Access: Public
-//  Description:
+//       Access: Protected
+//  Description: Do not call the copy constructor directly; instead,
+//               use make_copy() or copy_subgraph() to make a copy of
+//               a node.
 ////////////////////////////////////////////////////////////////////
 PandaNode::
 PandaNode(const PandaNode &copy) :
@@ -101,29 +223,24 @@ PandaNode(const PandaNode &copy) :
 
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaNode::Copy Assignment Operator
-//       Access: Public
-//  Description:
+//       Access: Private
+//  Description: Do not call the copy assignment operator at all.  Use
+//               make_copy() or copy_subgraph() to make a copy of a
+//               node.
 ////////////////////////////////////////////////////////////////////
 void PandaNode::
 operator = (const PandaNode &copy) {
-  TypedWritable::operator = (copy);
-  Namable::operator = (copy);
-  ReferenceCount::operator = (copy);
-
-  // Copy the other node's state.
-  CDReader copy_cdata(copy._cycler);
-  CDWriter cdata(_cycler);
-  cdata->_state = copy_cdata->_state;
-  cdata->_transform = copy_cdata->_transform;
+  nassertv(false);
 }
 
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaNode::make_copy
 //       Access: Public, Virtual
-//  Description: Returns a newly-allocated PandaNode that is a shallow copy
-//               of this one.  It will be a different pointer, but its
-//               internal data may or may not be shared with that of
-//               the original PandaNode.  No children will be copied.
+//  Description: Returns a newly-allocated PandaNode that is a shallow
+//               copy of this one.  It will be a different pointer,
+//               but its internal data may or may not be shared with
+//               that of the original PandaNode.  No children will be
+//               copied.
 ////////////////////////////////////////////////////////////////////
 PandaNode *PandaNode::
 make_copy() const {
@@ -361,29 +478,30 @@ find_child(PandaNode *node) const {
 void PandaNode::
 add_child(PandaNode *child_node, int sort) {
   // Ensure the child_node is not deleted while we do this.
-  PT(PandaNode) keep_child = child_node;
-  remove_child(child_node);
-  CDWriter cdata(_cycler);
-  CDWriter cdata_child(child_node->_cycler);
-  
-  cdata->_down.insert(DownConnection(child_node, sort));
-  cdata_child->_up.insert(UpConnection(this));
-
-  // We also have to adjust any qpNodePathComponents the child might
-  // have that reference the child as a top node.  Any other
-  // components we can leave alone, because we are making a new
-  // instance of the child.
-  Chains::iterator ci;
-  for (ci = cdata_child->_chains.begin();
-       ci != cdata_child->_chains.end();
-       ++ci) {
-    if ((*ci)->is_top_node()) {
-      (*ci)->set_next(get_generic_component());
+  {
+    PT(PandaNode) keep_child = child_node;
+    remove_child(child_node);
+    CDWriter cdata(_cycler);
+    CDWriter cdata_child(child_node->_cycler);
+    
+    cdata->_down.insert(DownConnection(child_node, sort));
+    cdata_child->_up.insert(UpConnection(this));
+    
+    // We also have to adjust any qpNodePathComponents the child might
+    // have that reference the child as a top node.  Any other
+    // components we can leave alone, because we are making a new
+    // instance of the child.
+    Chains::iterator ci;
+    for (ci = cdata_child->_chains.begin();
+	 ci != cdata_child->_chains.end();
+	 ++ci) {
+      if ((*ci)->is_top_node()) {
+	(*ci)->set_next(get_generic_component());
+      }
     }
+    child_node->fix_chain_lengths(cdata_child);
   }
 
-  child_node->fix_chain_lengths();
-
   // Mark the bounding volumes stale.
   force_bound_stale();
 }
@@ -395,45 +513,47 @@ add_child(PandaNode *child_node, int sort) {
 ////////////////////////////////////////////////////////////////////
 void PandaNode::
 remove_child(int n) {
-  CDWriter cdata(_cycler);
-  nassertv(n >= 0 && n < (int)cdata->_down.size());
-
-  PT(PandaNode) child_node = cdata->_down[n].get_child();
-  CDWriter cdata_child(child_node->_cycler);
-
-  cdata->_down.erase(cdata->_down.begin() + n);
-  int num_erased = cdata_child->_up.erase(UpConnection(this));
-  nassertv(num_erased == 1);
-
-  // Now sever any qpNodePathComponents on the child that reference
-  // this node.  If we have multiple of these, we have to collapse
-  // them together.
-  qpNodePathComponent *collapsed = (qpNodePathComponent *)NULL;
-  Chains::iterator ci;
-  ci = cdata_child->_chains.begin();
-  while (ci != cdata_child->_chains.end()) {
-    Chains::iterator cnext = ci;
-    ++cnext;
-    if (!(*ci)->is_top_node() && (*ci)->get_next()->get_node() == this) {
-      if (collapsed == (qpNodePathComponent *)NULL) {
-        (*ci)->set_top_node();
-        collapsed = (*ci);
-      } else {
-        // This is a different component that used to reference a
-        // different instance, but now it's all just the same topnode.
-        // We have to collapse this and the previous one together.
-        // However, there might be some qpNodePaths out there that
-        // still keep a pointer to this one, so we can't remove it
-        // altogether.
-        (*ci)->collapse_with(collapsed);
-        cdata_child->_chains.erase(ci);
+  {
+    CDWriter cdata(_cycler);
+    nassertv(n >= 0 && n < (int)cdata->_down.size());
+    
+    PT(PandaNode) child_node = cdata->_down[n].get_child();
+    CDWriter cdata_child(child_node->_cycler);
+    
+    cdata->_down.erase(cdata->_down.begin() + n);
+    int num_erased = cdata_child->_up.erase(UpConnection(this));
+    nassertv(num_erased == 1);
+    
+    // Now sever any qpNodePathComponents on the child that reference
+    // this node.  If we have multiple of these, we have to collapse
+    // them together.
+    qpNodePathComponent *collapsed = (qpNodePathComponent *)NULL;
+    Chains::iterator ci;
+    ci = cdata_child->_chains.begin();
+    while (ci != cdata_child->_chains.end()) {
+      Chains::iterator cnext = ci;
+      ++cnext;
+      if (!(*ci)->is_top_node() && (*ci)->get_next()->get_node() == this) {
+	if (collapsed == (qpNodePathComponent *)NULL) {
+	  (*ci)->set_top_node();
+	  collapsed = (*ci);
+	} else {
+	  // This is a different component that used to reference a
+	  // different instance, but now it's all just the same topnode.
+	  // We have to collapse this and the previous one together.
+	  // However, there might be some qpNodePaths out there that
+	  // still keep a pointer to this one, so we can't remove it
+	  // altogether.
+	  (*ci)->collapse_with(collapsed);
+	  cdata_child->_chains.erase(ci);
+	}
       }
+      ci = cnext;
     }
-    ci = cnext;
+    
+    child_node->fix_chain_lengths(cdata_child);
   }
 
-  child_node->fix_chain_lengths();
-
   // Mark the bounding volumes stale.
   force_bound_stale();
 }
@@ -448,56 +568,58 @@ remove_child(int n) {
 bool PandaNode::
 remove_child(PandaNode *child_node) {
   // Ensure the child_node is not deleted while we do this.
-  PT(PandaNode) keep_child = child_node;
-  CDWriter cdata_child(child_node->_cycler);
-
-  // First, look for and remove this node from the child's parent
-  // list.
-  int num_erased = cdata_child->_up.erase(UpConnection(this));
-  if (num_erased == 0) {
-    // No such node; it wasn't our child to begin with.
-    return false;
-  }
-
-  // Now sever any qpNodePathComponents on the child that reference
-  // this node.  If we have multiple of these, we have to collapse
-  // them together (see above).
-  qpNodePathComponent *collapsed = (qpNodePathComponent *)NULL;
-  Chains::iterator ci;
-  ci = cdata_child->_chains.begin();
-  while (ci != cdata_child->_chains.end()) {
-    Chains::iterator cnext = ci;
-    ++cnext;
-    if (!(*ci)->is_top_node() && (*ci)->get_next()->get_node() == this) {
-      if (collapsed == (qpNodePathComponent *)NULL) {
-        (*ci)->set_top_node();
-        collapsed = (*ci);
-      } else {
-        (*ci)->collapse_with(collapsed);
-        cdata_child->_chains.erase(ci);
+  {
+    PT(PandaNode) keep_child = child_node;
+    
+    CDWriter cdata(_cycler);
+    CDWriter cdata_child(child_node->_cycler);
+    
+    // First, look for and remove this node from the child's parent
+    // list.
+    int num_erased = cdata_child->_up.erase(UpConnection(this));
+    if (num_erased == 0) {
+      // No such node; it wasn't our child to begin with.
+      return false;
+    }
+    
+    // Now sever any qpNodePathComponents on the child that reference
+    // this node.  If we have multiple of these, we have to collapse
+    // them together (see above).
+    qpNodePathComponent *collapsed = (qpNodePathComponent *)NULL;
+    Chains::iterator ci;
+    ci = cdata_child->_chains.begin();
+    while (ci != cdata_child->_chains.end()) {
+      Chains::iterator cnext = ci;
+      ++cnext;
+      if (!(*ci)->is_top_node() && (*ci)->get_next()->get_node() == this) {
+	if (collapsed == (qpNodePathComponent *)NULL) {
+	  (*ci)->set_top_node();
+	  collapsed = (*ci);
+	} else {
+	  (*ci)->collapse_with(collapsed);
+	  cdata_child->_chains.erase(ci);
+	}
+      }
+      ci = cnext;
+    }
+    
+    child_node->fix_chain_lengths(cdata_child);
+    
+    // Now, look for and remove the child node from our down list.
+    Down::iterator di;
+    bool found = false;
+    for (di = cdata->_down.begin(); di != cdata->_down.end() && !found; ++di) {
+      if ((*di).get_child() == child_node) {
+	cdata->_down.erase(di);
+	found = true;
       }
     }
-    ci = cnext;
-  }
 
-  child_node->fix_chain_lengths();
+    nassertr(found, false);
+  }
 
   // Mark the bounding volumes stale.
   force_bound_stale();
-
-  CDWriter cdata(_cycler);
-
-  // Now, look for and remove the child node from our down list.
-  Down::iterator di;
-  for (di = cdata->_down.begin(); di != cdata->_down.end(); ++di) {
-    if ((*di).get_child() == child_node) {
-      cdata->_down.erase(di);
-      return true;
-    }
-  }
-
-  // We shouldn't get here unless there was a parent-child mismatch.
-  nassertr(false, false);
   return true;
 }
 
@@ -508,35 +630,37 @@ remove_child(PandaNode *child_node) {
 ////////////////////////////////////////////////////////////////////
 void PandaNode::
 remove_all_children() {
-  CDWriter cdata(_cycler);
-  Down::iterator ci;
-  for (ci = cdata->_down.begin(); ci != cdata->_down.end(); ++ci) {
-    PT(PandaNode) child_node = (*ci).get_child();
-    CDWriter cdata_child(child_node->_cycler);
-    cdata_child->_up.erase(UpConnection(this));
-
-    // Now sever any qpNodePathComponents on the child that reference
-    // this node.  If we have multiple of these, we have to collapse
-    // them together (see above).
-    qpNodePathComponent *collapsed = (qpNodePathComponent *)NULL;
-    Chains::iterator ci;
-    ci = cdata_child->_chains.begin();
-    while (ci != cdata_child->_chains.end()) {
-      Chains::iterator cnext = ci;
-      ++cnext;
-      if (!(*ci)->is_top_node() && (*ci)->get_next()->get_node() == this) {
-        if (collapsed == (qpNodePathComponent *)NULL) {
-          (*ci)->set_top_node();
-          collapsed = (*ci);
-        } else {
-          (*ci)->collapse_with(collapsed);
-          cdata_child->_chains.erase(ci);
+  {
+    CDWriter cdata(_cycler);
+    Down::iterator ci;
+    for (ci = cdata->_down.begin(); ci != cdata->_down.end(); ++ci) {
+      PT(PandaNode) child_node = (*ci).get_child();
+      CDWriter cdata_child(child_node->_cycler);
+      cdata_child->_up.erase(UpConnection(this));
+      
+      // Now sever any qpNodePathComponents on the child that reference
+      // this node.  If we have multiple of these, we have to collapse
+      // them together (see above).
+      qpNodePathComponent *collapsed = (qpNodePathComponent *)NULL;
+      Chains::iterator ci;
+      ci = cdata_child->_chains.begin();
+      while (ci != cdata_child->_chains.end()) {
+        Chains::iterator cnext = ci;
+        ++cnext;
+        if (!(*ci)->is_top_node() && (*ci)->get_next()->get_node() == this) {
+          if (collapsed == (qpNodePathComponent *)NULL) {
+            (*ci)->set_top_node();
+            collapsed = (*ci);
+          } else {
+            (*ci)->collapse_with(collapsed);
+            cdata_child->_chains.erase(ci);
+          }
         }
+        ci = cnext;
       }
-      ci = cnext;
+      
+      child_node->fix_chain_lengths(cdata_child);
     }
-
-    child_node->fix_chain_lengths();
   }
 
   // Mark the bounding volumes stale.
@@ -600,8 +724,8 @@ propagate_stale_bound() {
   CDWriter cdata(_cycler);
   Up::const_iterator ui;
   for (ui = cdata->_up.begin(); ui != cdata->_up.end(); ++ui) {
-    PandaNode *parent = (*ui).get_parent();
-    parent->mark_bound_stale();
+    PandaNode *parent_node = (*ui).get_parent();
+    parent_node->mark_bound_stale();
   }
 }
 
@@ -718,57 +842,58 @@ void PandaNode::
 detach(qpNodePathComponent *child) {
   nassertv(child != (qpNodePathComponent *)NULL);
   nassertv(!child->is_top_node());
+
   PandaNode *child_node = child->get_node();
   PandaNode *parent_node = child->get_next()->get_node();
-
-  // Break the qpNodePathComponent connection.
-  child->set_top_node();
-
-  CDWriter cdata_child(child_node->_cycler);
-
-  // Any other components in the same child_node that previously
-  // referenced the same parent has now become invalid and must be
-  // collapsed into this one and removed from the chains set.
-  Chains::iterator ci;
-  ci = cdata_child->_chains.begin();
-  while (ci != cdata_child->_chains.end()) {
-    Chains::iterator cnext = ci;
-    ++cnext;
-    if ((*ci) != child && !(*ci)->is_top_node() && 
-        (*ci)->get_next()->get_node() == parent_node) {
-      (*ci)->collapse_with(child);
-      cdata_child->_chains.erase(ci);
+  
+  {
+    // Break the qpNodePathComponent connection.
+    child->set_top_node();
+    
+    CDWriter cdata_child(child_node->_cycler);
+    CDWriter cdata_parent(parent_node->_cycler);
+    
+    // Any other components in the same child_node that previously
+    // referenced the same parent has now become invalid and must be
+    // collapsed into this one and removed from the chains set.
+    Chains::iterator ci;
+    ci = cdata_child->_chains.begin();
+    while (ci != cdata_child->_chains.end()) {
+      Chains::iterator cnext = ci;
+      ++cnext;
+      if ((*ci) != child && !(*ci)->is_top_node() && 
+	  (*ci)->get_next()->get_node() == parent_node) {
+	(*ci)->collapse_with(child);
+	cdata_child->_chains.erase(ci);
+      }
+      ci = cnext;
     }
-    ci = cnext;
+    
+    // Now look for the child and break the actual connection.
+    
+    // First, look for and remove the parent node from the child's up
+    // list.
+    int num_erased = cdata_child->_up.erase(UpConnection(parent_node));
+    nassertv(num_erased == 1);
+    
+    child_node->fix_chain_lengths(cdata_child);
+    
+    // Now, look for and remove the child node from the parent's down list.
+    Down::iterator di;
+    bool found = false;
+    for (di = cdata_parent->_down.begin(); 
+	 di != cdata_parent->_down.end() && !found; 
+	 ++di) {
+      if ((*di).get_child() == child_node) {
+	cdata_parent->_down.erase(di);
+	found = true;
+      }
+    }
+    nassertv(found);
   }
 
-  child_node->fix_chain_lengths();
-
   // Mark the bounding volumes stale.
   parent_node->force_bound_stale();
-
-  // Now look for the child and break the actual connection.
-
-  // First, look for and remove the parent node from the child's up
-  // list.
-  int num_erased = cdata_child->_up.erase(UpConnection(parent_node));
-  nassertv(num_erased == 1);
-
-  CDWriter cdata_parent(parent_node->_cycler);
-
-  // Now, look for and remove the child node from the parent's down list.
-  Down::iterator di;
-  for (di = cdata_parent->_down.begin(); 
-       di != cdata_parent->_down.end(); 
-       ++di) {
-    if ((*di).get_child() == child_node) {
-      cdata_parent->_down.erase(di);
-      return;
-    }
-  }
-
-  // We shouldn't get here unless there was a parent-child mismatch.
-  nassertv(false);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -791,15 +916,17 @@ reparent(qpNodePathComponent *new_parent, qpNodePathComponent *child, int sort)
   PandaNode *child_node = child->get_node();
   PandaNode *parent_node = new_parent->get_node();
 
-  // Now reattach at the indicated sort position.
-  CDWriter cdata_parent(parent_node->_cycler);
-  CDWriter cdata_child(child_node->_cycler);
-  
-  cdata_parent->_down.insert(DownConnection(child_node, sort));
-  cdata_child->_up.insert(UpConnection(parent_node));
-
-  cdata_child->_chains.insert(child);
-  child_node->fix_chain_lengths();
+  {
+    // Now reattach at the indicated sort position.
+    CDWriter cdata_parent(parent_node->_cycler);
+    CDWriter cdata_child(child_node->_cycler);
+    
+    cdata_parent->_down.insert(DownConnection(child_node, sort));
+    cdata_child->_up.insert(UpConnection(parent_node));
+    
+    cdata_child->_chains.insert(child);
+    child_node->fix_chain_lengths(cdata_child);
+  }
 
   // Mark the bounding volumes stale.
   parent_node->force_bound_stale();
@@ -947,8 +1074,7 @@ delete_component(qpNodePathComponent *component) {
 //               these up.
 ////////////////////////////////////////////////////////////////////
 void PandaNode::
-fix_chain_lengths() {
-  CDReader cdata(_cycler);
+fix_chain_lengths(const CData *cdata) {
   bool any_wrong = false;
 
   Chains::const_iterator ci;
@@ -964,7 +1090,9 @@ fix_chain_lengths() {
   if (any_wrong) {
     Down::const_iterator di;
     for (di = cdata->_down.begin(); di != cdata->_down.end(); ++di) {
-      (*di).get_child()->fix_chain_lengths();
+      PandaNode *child_node = (*di).get_child();
+      CDReader cdata_child(child_node->_cycler);
+      child_node->fix_chain_lengths(cdata_child);
     }
   }
 }
@@ -1012,57 +1140,9 @@ register_with_read_factory() {
 ////////////////////////////////////////////////////////////////////
 void PandaNode::
 write_datagram(BamWriter *manager, Datagram &dg) {
-  CDReader cdata(_cycler);
-
   dg.add_string(get_name());
-  manager->write_pointer(dg, cdata->_state);
-  manager->write_pointer(dg, cdata->_transform);
-
-  // When we write a PandaNode, we write out its list of child nodes,
-  // but not its parent nodes--so that writing out a node implicitly
-  // writes out all of the nodes in the subgraph below, but not the
-  // nodes above as well.
 
-  int num_children = cdata->_down.size();
-  nassertv(num_children == (int)(PN_uint16)num_children);
-  dg.add_uint16(num_children);
-
-  // **** We should smarten up the writing of the sort number--most of
-  // the time these will all be zero.
-  Down::const_iterator ci;
-  for (ci = cdata->_down.begin(); ci != cdata->_down.end(); ++ci) {
-    PandaNode *child = (*ci).get_child();
-    int sort = (*ci).get_sort();
-    manager->write_pointer(dg, child);
-    dg.add_int32(sort);
-  }
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PandaNode::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 PandaNode::
-complete_pointers(TypedWritable **p_list, BamReader *manager) {
-  CDWriter cdata(_cycler);
-  int pi = TypedWritable::complete_pointers(p_list, manager);
-
-  // Get the state and transform pointers.
-  cdata->_state = DCAST(RenderState, p_list[pi++]);
-  cdata->_transform = DCAST(TransformState, p_list[pi++]);
-
-  // Get the child pointers.
-  Down::iterator ci;
-  for (ci = cdata->_down.begin(); ci != cdata->_down.end(); ++ci) {
-    int sort = (*ci).get_sort();
-    PT(PandaNode) node = DCAST(PandaNode, p_list[pi++]);
-    (*ci) = DownConnection(node, sort);
-  }
-
-  return pi;
+  manager->write_cdata(dg, _cycler);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -1094,23 +1174,10 @@ make_from_bam(const FactoryParams &params) {
 ////////////////////////////////////////////////////////////////////
 void PandaNode::
 fillin(DatagramIterator &scan, BamReader *manager) {
-  CDWriter cdata(_cycler);
-
   TypedWritable::fillin(scan, manager);
 
   string name = scan.get_string();
   set_name(name);
 
-  // Read the state and transform pointers.
-  manager->read_pointer(scan, this);
-  manager->read_pointer(scan, this);
-
-  int num_children = scan.get_uint16();
-  // Read the list of child nodes.  Push back a NULL for each one.
-  cdata->_down.reserve(num_children);
-  for (int i = 0; i < num_children; i++) {
-    manager->read_pointer(scan, this);
-    int sort = scan.get_int32();
-    cdata->_down.push_back(DownConnection(NULL, sort));
-  }
+  manager->read_cdata(scan, _cycler);
 }

+ 8 - 4
panda/src/pgraph/pandaNode.h

@@ -53,10 +53,12 @@ PUBLISHED:
   PandaNode(const string &name);
   virtual ~PandaNode();
 
-public:
+protected:
   PandaNode(const PandaNode &copy);
+private:
   void operator = (const PandaNode &copy);
 
+public:
   virtual PandaNode *make_copy() const;
   PandaNode *copy_subgraph() const;
 
@@ -161,7 +163,8 @@ private:
   static PT(qpNodePathComponent) get_top_component(PandaNode *child);
   PT(qpNodePathComponent) get_generic_component();
   void delete_component(qpNodePathComponent *component);
-  void fix_chain_lengths();
+  class CData;
+  void fix_chain_lengths(const CData *cdata);
   void r_list_descendants(ostream &out, int indent_level) const;
 
 private:
@@ -207,6 +210,9 @@ private:
     INLINE CData();
     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);
 
     Down _down;
     Up _up;
@@ -242,8 +248,6 @@ public:
 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);

+ 1 - 13
panda/src/pgraph/qpcamera.cxx

@@ -37,7 +37,7 @@ qpCamera(const string &name) :
 
 ////////////////////////////////////////////////////////////////////
 //     Function: qpCamera::Copy Constructor
-//       Access: Public
+//       Access: Protected
 //  Description:
 ////////////////////////////////////////////////////////////////////
 qpCamera::
@@ -48,18 +48,6 @@ qpCamera(const qpCamera &copy) :
 {
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: qpCamera::Copy Assignment Operator
-//       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-void qpCamera::
-operator = (const qpCamera &copy) {
-  qpLensNode::operator = (copy);
-  _active = copy._active;
-  _scene = copy._scene;
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: qpCamera::Destructor
 //       Access: Public, Virtual

+ 2 - 2
panda/src/pgraph/qpcamera.h

@@ -36,9 +36,9 @@ class EXPCL_PANDA qpCamera : public qpLensNode {
 PUBLISHED:
   qpCamera(const string &name);
 
-public:
+protected:
   qpCamera(const qpCamera &copy);
-  void operator = (const qpCamera &copy);
+public:
   virtual ~qpCamera();
 
   virtual PandaNode *make_copy() const;

+ 67 - 69
panda/src/pgraph/qpgeomNode.cxx

@@ -46,6 +46,67 @@ make_copy() const {
   return new CData(*this);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomNode::CData::write_datagram
+//       Access: Public, Virtual
+//  Description: Writes the contents of this object to the datagram
+//               for shipping out to a Bam file.
+////////////////////////////////////////////////////////////////////
+void qpGeomNode::CData::
+write_datagram(BamWriter *manager, Datagram &dg) const {
+  int num_geoms = _geoms.size();
+  nassertv(num_geoms == (int)(PN_uint16)num_geoms);
+  dg.add_uint16(num_geoms);
+  
+  Geoms::const_iterator gi;
+  for (gi = _geoms.begin(); gi != _geoms.end(); ++gi) {
+    const GeomEntry &entry = (*gi);
+    manager->write_pointer(dg, entry._geom);
+    manager->write_pointer(dg, entry._state);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomNode::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 qpGeomNode::CData::
+complete_pointers(TypedWritable **p_list, BamReader *manager) {
+  int pi = CycleData::complete_pointers(p_list, manager);
+
+  // Get the geom and state pointers.
+  Geoms::iterator gi;
+  for (gi = _geoms.begin(); gi != _geoms.end(); ++gi) {
+    GeomEntry &entry = (*gi);
+    entry._geom = DCAST(Geom, p_list[pi++]);
+    entry._state = DCAST(RenderState, p_list[pi++]);
+  }
+
+  return pi;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomNode::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 qpGeomNode.
+////////////////////////////////////////////////////////////////////
+void qpGeomNode::CData::
+fillin(DatagramIterator &scan, BamReader *manager) {
+  int num_geoms = scan.get_uint16();
+  // Read the list of geoms and states.  Push back a NULL for each one.
+  _geoms.reserve(num_geoms);
+  for (int i = 0; i < num_geoms; i++) {
+    manager->read_pointer(scan);
+    manager->read_pointer(scan);
+    _geoms.push_back(GeomEntry(NULL, NULL));
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeomNode::Constructor
 //       Access: Published
@@ -59,37 +120,19 @@ qpGeomNode(const string &name) :
 
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeomNode::Copy Constructor
-//       Access: Published
+//       Access: Protected
 //  Description:
 ////////////////////////////////////////////////////////////////////
 qpGeomNode::
 qpGeomNode(const qpGeomNode &copy) :
-  PandaNode(copy)
+  PandaNode(copy),
+  _cycler(copy._cycler)
 {
-  // Copy the other node's _geoms.
-  CDReader copy_cdata(copy._cycler);
-  CDWriter cdata(_cycler);
-  cdata->_geoms = copy_cdata->_geoms;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: qpGeomNode::Copy Assignment Operator
-//       Access: Published
-//  Description:
-////////////////////////////////////////////////////////////////////
-void qpGeomNode::
-operator = (const qpGeomNode &copy) {
-  PandaNode::operator = (copy);
-
-  // Copy the other node's _geoms.
-  CDReader copy_cdata(copy._cycler);
-  CDWriter cdata(_cycler);
-  cdata->_geoms = copy_cdata->_geoms;
 }
 
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeomNode::Destructor
-//       Access: Published, Virtual
+//       Access: Public, Virtual
 //  Description:
 ////////////////////////////////////////////////////////////////////
 qpGeomNode::
@@ -212,42 +255,7 @@ register_with_read_factory() {
 void qpGeomNode::
 write_datagram(BamWriter *manager, Datagram &dg) {
   PandaNode::write_datagram(manager, dg);
-
-  CDReader cdata(_cycler);
-
-  int num_geoms = cdata->_geoms.size();
-  nassertv(num_geoms == (int)(PN_uint16)num_geoms);
-  dg.add_uint16(num_geoms);
-  
-  Geoms::const_iterator gi;
-  for (gi = cdata->_geoms.begin(); gi != cdata->_geoms.end(); ++gi) {
-    const GeomEntry &entry = (*gi);
-    manager->write_pointer(dg, entry._geom);
-    manager->write_pointer(dg, entry._state);
-  }
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: qpGeomNode::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 qpGeomNode::
-complete_pointers(TypedWritable **p_list, BamReader *manager) {
-  CDWriter cdata(_cycler);
-  int pi = PandaNode::complete_pointers(p_list, manager);
-
-  // Get the geom and state pointers.
-  Geoms::iterator gi;
-  for (gi = cdata->_geoms.begin(); gi != cdata->_geoms.end(); ++gi) {
-    GeomEntry &entry = (*gi);
-    entry._geom = DCAST(Geom, p_list[pi++]);
-    entry._state = DCAST(RenderState, p_list[pi++]);
-  }
-
-  return pi;
+  manager->write_cdata(dg, _cycler);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -279,16 +287,6 @@ make_from_bam(const FactoryParams &params) {
 ////////////////////////////////////////////////////////////////////
 void qpGeomNode::
 fillin(DatagramIterator &scan, BamReader *manager) {
-  CDWriter cdata(_cycler);
-
   PandaNode::fillin(scan, manager);
-
-  int num_geoms = scan.get_uint16();
-  // Read the list of geoms and states.  Push back a NULL for each one.
-  cdata->_geoms.reserve(num_geoms);
-  for (int i = 0; i < num_geoms; i++) {
-    manager->read_pointer(scan, this);
-    manager->read_pointer(scan, this);
-    cdata->_geoms.push_back(GeomEntry(NULL, NULL));
-  }
+  manager->read_cdata(scan, _cycler);
 }

+ 5 - 4
panda/src/pgraph/qpgeomNode.h

@@ -39,9 +39,9 @@ class EXPCL_PANDA qpGeomNode : public PandaNode {
 PUBLISHED:
   qpGeomNode(const string &name);
 
-public:
+protected:
   qpGeomNode(const qpGeomNode &copy);
-  void operator = (const qpGeomNode &copy);
+public:
   virtual ~qpGeomNode();
 
 PUBLISHED:
@@ -80,6 +80,9 @@ private:
     INLINE CData();
     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);
 
     Geoms _geoms;
   };
@@ -91,8 +94,6 @@ 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);

+ 1 - 12
panda/src/pgraph/qplensNode.I

@@ -30,7 +30,7 @@ qpLensNode(const string &name) :
 
 ////////////////////////////////////////////////////////////////////
 //     Function: qpLensNode::Copy Constructor
-//       Access: Public
+//       Access: Protected
 //  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE qpLensNode::
@@ -40,17 +40,6 @@ qpLensNode(const qpLensNode &copy) :
 {
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: qpLensNode::Copy Assignment Operator
-//       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-INLINE void qpLensNode::
-operator = (const qpLensNode &copy) {
-  PandaNode::operator = (copy);
-  _lens = copy._lens;
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: copy_lens
 //       Access: Public

+ 2 - 3
panda/src/pgraph/qplensNode.h

@@ -36,10 +36,9 @@ class EXPCL_PANDA qpLensNode : public PandaNode {
 PUBLISHED:
   INLINE qpLensNode(const string &name);
 
-public:
+protected:
   INLINE qpLensNode(const qpLensNode &copy);
-  INLINE void operator = (const qpLensNode &copy);
-
+public:
   virtual void output(ostream &out) const;
   virtual void write(ostream &out, int indent_level = 0) const;
 

+ 12 - 0
panda/src/pgraph/qplodNode.I

@@ -47,6 +47,18 @@ qpLODNode(const string &name) :
 {
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: qpLODNode::Copy Constructor
+//       Access: Protected
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE qpLODNode::
+qpLODNode(const qpLODNode &copy) :
+  SelectiveChildNode(copy),
+  _cycler(copy._cycler)
+{
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: qpLODNode::add_switch
 //       Access: Published

+ 17 - 28
panda/src/pgraph/qplodNode.cxx

@@ -32,33 +32,26 @@ make_copy() const {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: qpLODNode::Copy Constructor
-//       Access: Public
-//  Description:
+//     Function: qpLODNode::CData::write_datagram
+//       Access: Public, Virtual
+//  Description: Writes the contents of this object to the datagram
+//               for shipping out to a Bam file.
 ////////////////////////////////////////////////////////////////////
-qpLODNode::
-qpLODNode(const qpLODNode &copy) :
-  SelectiveChildNode(copy)
-{
-  CDWriter cdata(_cycler);
-  CDReader cdata_copy(copy._cycler);
-
-  cdata->_lod = cdata_copy->_lod;
+void qpLODNode::CData::
+write_datagram(BamWriter *manager, Datagram &dg) const {
+  _lod.write_datagram(dg);
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: qpLODNode::Copy Assignment Operator
-//       Access: Public
-//  Description:
+//     Function: qpLODNode::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 qpLODNode.
 ////////////////////////////////////////////////////////////////////
-void qpLODNode::
-operator = (const qpLODNode &copy) {
-  SelectiveChildNode::operator = (copy);
-
-  CDWriter cdata(_cycler);
-  CDReader cdata_copy(copy._cycler);
-
-  cdata->_lod = cdata_copy->_lod;
+void qpLODNode::CData::
+fillin(DatagramIterator &scan, BamReader *manager) {
+  _lod.read_datagram(scan);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -177,9 +170,7 @@ register_with_read_factory() {
 void qpLODNode::
 write_datagram(BamWriter *manager, Datagram &dg) {
   SelectiveChildNode::write_datagram(manager, dg);
-
-  CDReader cdata(_cycler);
-  cdata->_lod.write_datagram(dg);
+  manager->write_cdata(dg, _cycler);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -212,7 +203,5 @@ make_from_bam(const FactoryParams &params) {
 void qpLODNode::
 fillin(DatagramIterator &scan, BamReader *manager) {
   SelectiveChildNode::fillin(scan, manager);
-
-  CDWriter cdata(_cycler);
-  cdata->_lod.read_datagram(scan);
+  manager->read_cdata(scan, _cycler);
 }

+ 4 - 3
panda/src/pgraph/qplodNode.h

@@ -36,10 +36,9 @@ class EXPCL_PANDA qpLODNode : public SelectiveChildNode {
 PUBLISHED:
   INLINE qpLODNode(const string &name);
 
-public:
+protected:
   INLINE qpLODNode(const qpLODNode &copy);
-  INLINE void operator = (const qpLODNode &copy);
-
+public:
   virtual PandaNode *make_copy() const;
   virtual void xform(const LMatrix4f &mat);
   virtual bool has_cull_callback() const;
@@ -78,6 +77,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);
 
     LOD _lod;
   };

+ 5 - 5
panda/src/pgraph/qpnodePathComponent.I

@@ -136,8 +136,8 @@ is_collapsed() const {
 ////////////////////////////////////////////////////////////////////
 INLINE int qpNodePathComponent::
 get_length() const {
-  CDReader cdata(_cycler);
   nassertr(!is_collapsed(), 0);
+  CDReader cdata(_cycler);
   return cdata->_length;
 }
 
@@ -150,8 +150,8 @@ get_length() const {
 ////////////////////////////////////////////////////////////////////
 INLINE qpNodePathComponent *qpNodePathComponent::
 get_collapsed() const {
-  CDReader cdata(_cycler);
   nassertr(is_collapsed(), (qpNodePathComponent *)NULL);
+  CDReader cdata(_cycler);
   return cdata->_next;
 }
 
@@ -162,9 +162,9 @@ get_collapsed() const {
 ////////////////////////////////////////////////////////////////////
 INLINE void qpNodePathComponent::
 set_next(qpNodePathComponent *next) {
-  CDWriter cdata(_cycler);
   nassertv(!is_collapsed());
   nassertv(next != (qpNodePathComponent *)NULL);
+  CDWriter cdata(_cycler);
   cdata->_next = next;
 }
 
@@ -176,8 +176,8 @@ set_next(qpNodePathComponent *next) {
 ////////////////////////////////////////////////////////////////////
 INLINE void qpNodePathComponent::
 set_top_node() {
-  CDWriter cdata(_cycler);
   nassertv(!is_collapsed());
+  CDWriter cdata(_cycler);
   cdata->_next = (qpNodePathComponent *)NULL;
 }
 
@@ -192,9 +192,9 @@ set_top_node() {
 ////////////////////////////////////////////////////////////////////
 INLINE void qpNodePathComponent::
 collapse_with(qpNodePathComponent *next) {
-  CDWriter cdata(_cycler);
   nassertv(!is_collapsed());
   nassertv(next != (qpNodePathComponent *)NULL);
+  CDWriter cdata(_cycler);
   cdata->_next = next;
   cdata->_length = 0;
 

+ 34 - 34
panda/src/pgraph/qpsequenceNode.cxx

@@ -32,37 +32,46 @@ make_copy() const {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: qpSequenceNode::Copy Constructor
-//       Access: Public
-//  Description:
+//     Function: qpSequenceNode::CData::write_datagram
+//       Access: Public, Virtual
+//  Description: Writes the contents of this object to the datagram
+//               for shipping out to a Bam file.
 ////////////////////////////////////////////////////////////////////
-qpSequenceNode::
-qpSequenceNode(const qpSequenceNode &copy) :
-  SelectiveChildNode(copy)
-{
-  CDWriter cdata(_cycler);
-  CDReader cdata_copy(copy._cycler);
+void qpSequenceNode::CData::
+write_datagram(BamWriter *manager, Datagram &dg) const {
+  dg.add_float32(_cycle_rate);
 
-  cdata->_cycle_rate = cdata_copy->_cycle_rate;
-  cdata->_start_time = cdata_copy->_start_time;
-  cdata->_frame_offset = cdata_copy->_frame_offset;
+  float now = ClockObject::get_global_clock()->get_frame_time();
+  float frame = (now - _start_time) * _cycle_rate + _frame_offset;
+  dg.add_float32(frame);
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: qpSequenceNode::Copy Assignment Operator
-//       Access: Public
-//  Description:
+//     Function: qpSequenceNode::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 qpSequenceNode.
 ////////////////////////////////////////////////////////////////////
-void qpSequenceNode::
-operator = (const qpSequenceNode &copy) {
-  SelectiveChildNode::operator = (copy);
+void qpSequenceNode::CData::
+fillin(DatagramIterator &scan, BamReader *manager) {
+  _cycle_rate = scan.get_float32();
+  _frame_offset = scan.get_float32();
 
-  CDWriter cdata(_cycler);
-  CDReader cdata_copy(copy._cycler);
+  float now = ClockObject::get_global_clock()->get_frame_time();
+  _start_time = now;
+}
 
-  cdata->_cycle_rate = cdata_copy->_cycle_rate;
-  cdata->_start_time = cdata_copy->_start_time;
-  cdata->_frame_offset = cdata_copy->_frame_offset;
+////////////////////////////////////////////////////////////////////
+//     Function: qpSequenceNode::Copy Constructor
+//       Access: Protected
+//  Description:
+////////////////////////////////////////////////////////////////////
+qpSequenceNode::
+qpSequenceNode(const qpSequenceNode &copy) :
+  SelectiveChildNode(copy),
+  _cycler(copy._cycler)
+{
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -137,10 +146,7 @@ register_with_read_factory() {
 void qpSequenceNode::
 write_datagram(BamWriter *manager, Datagram &dg) {
   SelectiveChildNode::write_datagram(manager, dg);
-
-  CDReader cdata(_cycler);
-  dg.add_float32(cdata->_cycle_rate);
-  dg.add_float32(calc_frame());
+  manager->write_cdata(dg, _cycler);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -173,11 +179,5 @@ make_from_bam(const FactoryParams &params) {
 void qpSequenceNode::
 fillin(DatagramIterator &scan, BamReader *manager) {
   SelectiveChildNode::fillin(scan, manager);
-
-  CDWriter cdata(_cycler);
-  cdata->_cycle_rate = scan.get_float32();
-  cdata->_frame_offset = scan.get_float32();
-
-  float now = ClockObject::get_global_clock()->get_frame_time();
-  cdata->_start_time = now;
+  manager->read_cdata(scan, _cycler);
 }

+ 2 - 1
panda/src/pgraph/qpsequenceNode.h

@@ -35,7 +35,6 @@ PUBLISHED:
 
 public:
   qpSequenceNode(const qpSequenceNode &copy);
-  void operator = (const qpSequenceNode &copy);
 
   virtual PandaNode *make_copy() const;
 
@@ -57,6 +56,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);
 
     float _cycle_rate;
     float _frame_offset;

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

@@ -1080,7 +1080,7 @@ fillin(DatagramIterator &scan, BamReader *manager) {
   // the actual list of pointers later in complete_pointers().
   _attributes.reserve(num_attribs);
   for (int i = 0; i < num_attribs; i++) {
-    manager->read_pointer(scan, this);
+    manager->read_pointer(scan);
     int override = scan.get_int32();
     _attributes.push_back(Attribute(override));
   }

+ 1 - 12
panda/src/pgraph/selectiveChildNode.I

@@ -31,7 +31,7 @@ SelectiveChildNode(const string &name) :
 
 ////////////////////////////////////////////////////////////////////
 //     Function: SelectiveChildNode::Copy Constructor
-//       Access: Public
+//       Access: Protected
 //  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE SelectiveChildNode::
@@ -41,17 +41,6 @@ SelectiveChildNode(const SelectiveChildNode &copy) :
 {
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: SelectiveChildNode::Copy Assignment Operator
-//       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-INLINE void SelectiveChildNode::
-operator = (const SelectiveChildNode &copy) {
-  PandaNode::operator = (copy);
-  _selected_child = copy._selected_child;
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: SelectiveChildNode::select_child
 //       Access: Protected

+ 6 - 2
panda/src/pgraph/selectiveChildNode.h

@@ -32,10 +32,10 @@ class EXPCL_PANDA SelectiveChildNode : public PandaNode {
 PUBLISHED:
   INLINE SelectiveChildNode(const string &name);
 
-public:
+protected:
   INLINE SelectiveChildNode(const SelectiveChildNode &copy);
-  INLINE void operator = (const SelectiveChildNode &copy);
 
+public:
   virtual bool has_selective_visibility() const;
   virtual int get_first_visible_child() const;
   virtual int get_next_visible_child(int n) const;
@@ -44,6 +44,10 @@ protected:
   INLINE void select_child(int n);
 
 private:
+  // Not sure if this should be cycled or not.  It's not exactly
+  // thread-safe not to cycle it, but it doesn't really need the full
+  // pipeline control.  It's probably a problem in the non-thread-safe
+  // design; need to rethink the design a bit.
   int _selected_child;
 
 public:

+ 1 - 1
panda/src/pgraph/textureAttrib.cxx

@@ -192,5 +192,5 @@ fillin(DatagramIterator &scan, BamReader *manager) {
   RenderAttrib::fillin(scan, manager);
 
   // Read the _texture pointer.
-  manager->read_pointer(scan, this);
+  manager->read_pointer(scan);
 }

+ 205 - 91
panda/src/putil/bamReader.cxx

@@ -16,13 +16,14 @@
 //
 ////////////////////////////////////////////////////////////////////
 
-#include <pandabase.h>
-#include <notify.h>
+#include "pandabase.h"
+#include "notify.h"
 
 #include "bam.h"
 #include "bamReader.h"
-#include <datagramIterator.h>
+#include "datagramIterator.h"
 #include "config_util.h"
+#include "pipelineCyclerBase.h"
 
 WritableFactory *BamReader::_factory = (WritableFactory*)0L;
 BamReader *const BamReader::Null = (BamReader*)0L;
@@ -43,6 +44,7 @@ BamReader(DatagramGenerator *generator)
 {
   _num_extra_objects = 0;
   _now_creating = _created_objs.end();
+  _reading_cycler = (PipelineCyclerBase *)NULL;
   _pta_id = -1;
 }
 
@@ -225,71 +227,25 @@ resolve() {
     any_completed_this_pass = false;
 
     // Walk through all the objects that still have outstanding pointers.
-    Requests::iterator ri;
-    ri = _deferred_pointers.begin();
-    while (ri != _deferred_pointers.end()) {
-      int object_id = (*ri).first;
-      const vector_int &children = (*ri).second;
-      
+    ObjectPointers::iterator oi;
+    oi = _object_pointers.begin();
+    while (oi != _object_pointers.end()) {
+      int object_id = (*oi).first;
+      const vector_int &pointer_ids = (*oi).second;
+
       CreatedObjs::iterator ci = _created_objs.find(object_id);
       nassertr(ci != _created_objs.end(), false);
       CreatedObj &created_obj = (*ci).second;
       
       TypedWritable *object_ptr = created_obj._ptr;
-      
-      // Now make sure we have all of the pointers this object is
-      // waiting for.  If any of the pointers has not yet been read
-      // in, we can't resolve this object--we can't do anything for a
-      // given object until we have *all* outstanding pointers for
-      // that object.
-
-      bool is_complete = true;
-      vector_typedWritable references;
-      
-      vector_int::const_iterator pi;
-      for (pi = children.begin(); pi != children.end() && is_complete; ++pi) {
-        int child_id = (*pi);
-        
-        if (child_id == 0) {
-          // A NULL pointer is a NULL pointer.
-          references.push_back((TypedWritable *)NULL);
-          
-        } else {
-          // See if we have the pointer available now.
-          CreatedObjs::const_iterator oi = _created_objs.find(child_id);
-          if (oi == _created_objs.end()) {
-            // No, too bad.
-            is_complete = false;
-            
-          } else {
-            const CreatedObj &child_obj = (*oi).second;
-            if (child_obj._change_this != NULL) {
-              // It's been created, but the pointer might still change.
-              is_complete = false;
-              
-            } else {
-              // Yes, it's ready.
-              references.push_back(child_obj._ptr);
-            }
-          }
-        }
-      }
-      
-      if (is_complete) {
-        // Okay, here's the complete list of pointers for you!
-        int num_completed = object_ptr->complete_pointers(&references[0], this);
-        if (num_completed != (int)references.size()) {
-          bam_cat.warning()
-            << object_ptr->get_type() << " completed " << num_completed
-            << " of " << references.size() << " pointers.\n";
-        }
-        
+
+      if (resolve_object_pointers(object_ptr, pointer_ids)) {
         // Now remove this object from the list of things that need
         // completion.  We have to be a bit careful when deleting things
         // from the STL container while we are traversing it.
-        Requests::iterator old = ri;
-        ++ri;
-        _deferred_pointers.erase(old);
+        ObjectPointers::iterator old = oi;
+        ++oi;
+        _object_pointers.erase(old);
         
         // Does the pointer need to change?
         if (created_obj._change_this != NULL) {
@@ -300,12 +256,36 @@ resolve() {
         
       } else {
         // Couldn't complete this object yet; it'll wait for next time.
-        ++ri;
+        ++oi;
         all_completed = false;
       }
     }
   } while (!all_completed && any_completed_this_pass);
 
+  // Also do the PipelineCycler objects.  We only need to try these
+  // once, since they don't depend on each other.
+
+  CyclerPointers::iterator ci;
+  ci = _cycler_pointers.begin();
+  while (ci != _cycler_pointers.end()) {
+    PipelineCyclerBase *cycler = (*ci).first;
+    const vector_int &pointer_ids = (*ci).second;
+    
+    if (resolve_cycler_pointers(cycler, pointer_ids)) {
+      // Now remove this cycler from the list of things that need
+      // completion.  We have to be a bit careful when deleting things
+      // from the STL container while we are traversing it.
+      CyclerPointers::iterator old = ci;
+      ++ci;
+      _cycler_pointers.erase(old);
+      
+    } else {
+      // Couldn't complete this cycler yet; it'll wait for next time.
+      ++ci;
+      all_completed = false;
+    }
+  }
+
   if (all_completed) {
     finalize();
   } else {
@@ -314,11 +294,11 @@ resolve() {
     // which some objects might legitimately be uncompleted after
     // calling resolve(), but for now we expect resolve() to always
     // succeed.
-    Requests::const_iterator ri;
-    for (ri = _deferred_pointers.begin(); 
-         ri != _deferred_pointers.end();
-         ++ri) {
-      int object_id = (*ri).first;
+    ObjectPointers::const_iterator oi;
+    for (oi = _object_pointers.begin(); 
+         oi != _object_pointers.end();
+         ++oi) {
+      int object_id = (*oi).first;
       CreatedObjs::iterator ci = _created_objs.find(object_id);
       nassertr(ci != _created_objs.end(), false);
       CreatedObj &created_obj = (*ci).second;
@@ -435,32 +415,20 @@ read_handle(DatagramIterator &scan) {
 //               object properly.
 ////////////////////////////////////////////////////////////////////
 void BamReader::
-read_pointer(DatagramIterator &scan, TypedWritable *for_whom) {
+read_pointer(DatagramIterator &scan) {
   nassertv(_now_creating != _created_objs.end());
   int requestor_id = (*_now_creating).first;
 
-  /*
-    On reflection, we'll let this go undetected for now.  Maybe we
-    should remove the this pointer from read_pointer() altogether.
+  // Read the object ID, and associate it with the requesting object.
+  int object_id = scan.get_uint16();
 
-#ifndef NDEBUG
-  // A bit of sanity checking here: we look up the object ID, and
-  // assign the "this" pointer into the record if it's not there
-  // already.  Then we can verify the "this" pointer later.
-  CreatedObj &created_obj = (*_now_creating).second;
-  if (created_obj._ptr == (TypedWritable *)NULL) {
-    created_obj._ptr = for_whom;
+  if (_reading_cycler == (PipelineCyclerBase *)NULL) {
+    // This is not being read within a read_cdata() call.
+    _object_pointers[requestor_id].push_back(object_id);
   } else {
-    // We've previously assigned this pointer, and we should have
-    // assigned it to the same this pointer we have now.
-    nassertv(created_obj._ptr == for_whom);
+    // This *is* being read within a read_cdata() call.
+    _cycler_pointers[_reading_cycler].push_back(object_id);
   }
-#endif  // NDEBUG
-  */
-
-  // Read the object ID, and associate it with the requesting object.
-  int object_id = scan.get_uint16();
-  _deferred_pointers[requestor_id].push_back(object_id);
 
   // If the object ID is zero (which indicates a NULL pointer), we
   // don't have to do anything else.
@@ -482,9 +450,9 @@ read_pointer(DatagramIterator &scan, TypedWritable *for_whom) {
 //               read_pointer() count times.
 ////////////////////////////////////////////////////////////////////
 void BamReader::
-read_pointers(DatagramIterator &scan, TypedWritable *for_whom, int count) {
+read_pointers(DatagramIterator &scan, int count) {
   for (int i = 0; i < count; i++) {
-    read_pointer(scan, for_whom);
+    read_pointer(scan);
   }
 }
 
@@ -501,6 +469,25 @@ skip_pointer(DatagramIterator &scan) {
   scan.get_uint16();
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: BamReader::read_cdata
+//       Access: Public
+//  Description: Reads in the indicated CycleData object.  This should
+//               be used by classes that store some or all of their
+//               data within a CycleData subclass, in support of
+//               pipelining.  This will call the virtual
+//               CycleData::fillin() method to do the actual reading.
+////////////////////////////////////////////////////////////////////
+void BamReader::
+read_cdata(DatagramIterator &scan, PipelineCyclerBase &cycler) {
+  PipelineCyclerBase *old_cycler = _reading_cycler;
+  _reading_cycler = &cycler;
+  CycleData *cdata = cycler.write();
+  cdata->fillin(scan, this);
+  cycler.release_write(cdata);
+  _reading_cycler = old_cycler;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: BamReader::register_finalize
 //       Access: Public
@@ -739,11 +726,11 @@ p_read_object() {
     if (created_obj._change_this != NULL) {
       // If the pointer is scheduled to change after
       // complete_pointers(), but we have no entry in
-      // _deferred_pointers for this object (and hence no plan to call
+      // _object_pointers for this object (and hence no plan to call
       // complete_pointers()), then just change the pointer
       // immediately.
-      Requests::const_iterator ri = _deferred_pointers.find(object_id);
-      if (ri == _deferred_pointers.end()) {
+      ObjectPointers::const_iterator ri = _object_pointers.find(object_id);
+      if (ri == _object_pointers.end()) {
         object = created_obj._change_this(object, this);
         created_obj._ptr = object;
         created_obj._change_this = NULL;
@@ -772,6 +759,133 @@ p_read_object() {
   return object_id;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: BamReader::resolve_object_pointers
+//       Access: Private
+//  Description: Checks whether all of the pointers a particular
+//               object is waiting for have been filled in yet.  If
+//               they have, calls complete_pointers() on the object
+//               and returns true; otherwise, returns false.
+////////////////////////////////////////////////////////////////////
+bool BamReader::
+resolve_object_pointers(TypedWritable *object, const vector_int &pointer_ids) {
+  // Now make sure we have all of the pointers this object is
+  // waiting for.  If any of the pointers has not yet been read
+  // in, we can't resolve this object--we can't do anything for a
+  // given object until we have *all* outstanding pointers for
+  // that object.
+  
+  bool is_complete = true;
+  vector_typedWritable references;
+  
+  vector_int::const_iterator pi;
+  for (pi = pointer_ids.begin(); pi != pointer_ids.end() && is_complete; ++pi) {
+    int child_id = (*pi);
+    
+    if (child_id == 0) {
+      // A NULL pointer is a NULL pointer.
+      references.push_back((TypedWritable *)NULL);
+      
+    } else {
+      // See if we have the pointer available now.
+      CreatedObjs::const_iterator oi = _created_objs.find(child_id);
+      if (oi == _created_objs.end()) {
+	// No, too bad.
+	is_complete = false;
+	
+      } else {
+	const CreatedObj &child_obj = (*oi).second;
+	if (child_obj._change_this != NULL) {
+	  // It's been created, but the pointer might still change.
+	  is_complete = false;
+	  
+	} else {
+	  // Yes, it's ready.
+	  references.push_back(child_obj._ptr);
+	}
+      }
+    }
+  }
+      
+  if (is_complete) {
+    // Okay, here's the complete list of pointers for you!
+    int num_completed = object->complete_pointers(&references[0], this);
+    if (num_completed != (int)references.size()) {
+      bam_cat.warning()
+	<< object->get_type() << " completed " << num_completed
+	<< " of " << references.size() << " pointers.\n";
+    }
+    return true;
+  }
+
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BamReader::resolve_cycler_pointers
+//       Access: Private
+//  Description: Checks whether all of the pointers a particular
+//               PipelineCycler is waiting for have been filled in
+//               yet.  If they have, calls complete_pointers() on the
+//               cycler and returns true; otherwise, returns false.
+////////////////////////////////////////////////////////////////////
+bool BamReader::
+resolve_cycler_pointers(PipelineCyclerBase *cycler,
+			const vector_int &pointer_ids) {
+  // Now make sure we have all of the pointers this cycler is
+  // waiting for.  If any of the pointers has not yet been read
+  // in, we can't resolve this cycler--we can't do anything for a
+  // given cycler until we have *all* outstanding pointers for
+  // that cycler.
+  
+  bool is_complete = true;
+  vector_typedWritable references;
+  
+  vector_int::const_iterator pi;
+  for (pi = pointer_ids.begin(); pi != pointer_ids.end() && is_complete; ++pi) {
+    int child_id = (*pi);
+    
+    if (child_id == 0) {
+      // A NULL pointer is a NULL pointer.
+      references.push_back((TypedWritable *)NULL);
+      
+    } else {
+      // See if we have the pointer available now.
+      CreatedObjs::const_iterator oi = _created_objs.find(child_id);
+      if (oi == _created_objs.end()) {
+	// No, too bad.
+	is_complete = false;
+	
+      } else {
+	const CreatedObj &child_obj = (*oi).second;
+	if (child_obj._change_this != NULL) {
+	  // It's been created, but the pointer might still change.
+	  is_complete = false;
+	  
+	} else {
+	  // Yes, it's ready.
+	  references.push_back(child_obj._ptr);
+	}
+      }
+    }
+  }
+      
+  if (is_complete) {
+    // Okay, here's the complete list of pointers for you!
+    CycleData *cdata = cycler->write();
+    int num_completed = cdata->complete_pointers(&references[0], this);
+    cycler->release_write(cdata);
+    if (num_completed != (int)references.size()) {
+      bam_cat.warning()
+	<< "CycleData object completed " << num_completed
+	<< " of " << references.size() << " pointers.\n";
+    }
+    return true;
+  }
+
+  return false;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: BamReader::finalize
 //       Access: Private

+ 21 - 7
panda/src/putil/bamReader.h

@@ -19,8 +19,8 @@
 #ifndef __BAM_READER_
 #define __BAM_READER_
 
-#include <pandabase.h>
-#include <notify.h>
+#include "pandabase.h"
+#include "notify.h"
 
 #include "typedWritable.h"
 #include "datagramGenerator.h"
@@ -33,6 +33,8 @@
 
 #include <algorithm>
 
+class PipelineCyclerBase;
+
 
 // A handy macro for reading PointerToArrays.
 #define READ_PTA(Manager, source, Read_func, array)   \
@@ -108,10 +110,12 @@ public:
 public:
   // Functions to support classes that read themselves from the Bam.
 
-  void read_pointer(DatagramIterator &scan, TypedWritable *for_whom);
-  void read_pointers(DatagramIterator &scan, TypedWritable *for_whom, int count);
+  void read_pointer(DatagramIterator &scan);
+  void read_pointers(DatagramIterator &scan, int count);
   void skip_pointer(DatagramIterator &scan);
 
+  void read_cdata(DatagramIterator &scan, PipelineCyclerBase &cycler);
+
   void register_finalize(TypedWritable *whom);
 
   typedef TypedWritable *(*ChangeThisFunc)(TypedWritable *object, BamReader *manager);
@@ -132,6 +136,8 @@ private:
 
 private:
   int p_read_object();
+  bool resolve_object_pointers(TypedWritable *object, const vector_int &pointer_ids);
+  bool resolve_cycler_pointers(PipelineCyclerBase *cycler, const vector_int &pointer_ids);
   void finalize();
 
 private:
@@ -158,12 +164,20 @@ private:
   // during recursion.  We need this so we can associate
   // read_pointer() calls with the proper objects.
   CreatedObjs::iterator _now_creating;
+  // This is the pointer to the current PipelineCycler we are reading,
+  // if we are within a read_cdata() call.
+  PipelineCyclerBase *_reading_cycler;
 
   // 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.
-  typedef pmap<int, vector_int> Requests;
-  Requests _deferred_pointers;
+  // in the order in which read_pointer() was called, so that we may
+  // call the appropriate complete_pointers() later.
+  typedef pmap<int, vector_int> ObjectPointers;
+  ObjectPointers _object_pointers;
+
+  // Ditto, for the PiplineCycler objects.
+  typedef pmap<PipelineCyclerBase *, vector_int> CyclerPointers;
+  CyclerPointers _cycler_pointers;
 
   // This is the number of extra objects that must still be read (and
   // saved in the _created_objs map) before returning from

+ 30 - 0
panda/src/putil/bamWriter.cxx

@@ -162,6 +162,19 @@ write_object(const TypedWritable *object) {
   return true;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: BamWriter::has_object
+//       Access: Public
+//  Description: Returns true if the object has previously been
+//               written (or at least requested to be written) to the
+//               bam file, or false if we've never heard of it before.
+////////////////////////////////////////////////////////////////////
+bool BamWriter::
+has_object(const TypedWritable *object) const {
+  StateMap::const_iterator si = _state_map.find(object);
+  return (si != _state_map.end());
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: BamWriter::write_pointer
 //       Access: Public
@@ -199,6 +212,23 @@ write_pointer(Datagram &packet, const TypedWritable *object) {
   }
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: BamWriter::write_cdata
+//       Access: Public
+//  Description: Writes out the indicated CycleData object.  This
+//               should be used by classes that store some or all of
+//               their data within a CycleData subclass, in support of
+//               pipelining.  This will call the virtual
+//               CycleData::write_datagram() method to do the actual
+//               writing.
+////////////////////////////////////////////////////////////////////
+void BamWriter::
+write_cdata(Datagram &packet, const PipelineCyclerBase &cycler) {
+  const CycleData *cdata = cycler.read();
+  cdata->write_datagram(this, packet);
+  cycler.release_read(cdata);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: BamWriter::register_pta
 //       Access: Public

+ 8 - 2
panda/src/putil/bamWriter.h

@@ -19,13 +19,15 @@
 #ifndef __BAM_WRITER_
 #define __BAM_WRITER_
 
-#include <pandabase.h>
-#include <notify.h>
+#include "pandabase.h"
+#include "notify.h"
 
 #include "typedWritable.h"
 #include "datagramSink.h"
 #include "pdeque.h"
 
+class PipelineCyclerBase;
+
 // A handy macro for writing PointerToArrays.
 #define WRITE_PTA(Manager, dest, Write_func, array)  \
   if (!Manager->register_pta(dest, array.p()))       \
@@ -78,14 +80,18 @@ public:
 
   bool init();
   bool write_object(const TypedWritable *obj);
+  bool has_object(const TypedWritable *obj) const;
 
 public:
   // Functions to support classes that write themselves to the Bam.
 
   void write_pointer(Datagram &packet, const TypedWritable *dest);
+  void write_cdata(Datagram &packet, const PipelineCyclerBase &cycler);
   bool register_pta(Datagram &packet, const void *ptr);
   void write_handle(Datagram &packet, TypeHandle type);
 
+  
+
 private:
   int enqueue_object(const TypedWritable *object);
 

+ 35 - 0
panda/src/putil/cycleData.cxx

@@ -27,3 +27,38 @@
 CycleData::
 ~CycleData() {
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleData::write_datagram
+//       Access: Public, Virtual
+//  Description: Writes the contents of this object to the datagram
+//               for shipping out to a Bam file.
+////////////////////////////////////////////////////////////////////
+void CycleData::
+write_datagram(BamWriter *, Datagram &) const {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleData::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 CycleData::
+complete_pointers(TypedWritable **, BamReader *) {
+  return 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleData::fillin
+//       Access: Public, Virtual
+//  Description: This internal function is intended to be called by
+//               each class's make_from_bam() method to read in all of
+//               the relevant data from the BamFile for the new
+//               object.
+////////////////////////////////////////////////////////////////////
+void CycleData::
+fillin(DatagramIterator &, BamReader *) {
+}
+

+ 10 - 0
panda/src/putil/cycleData.h

@@ -23,6 +23,12 @@
 
 #include "referenceCount.h"
 
+class BamWriter;
+class BamReader;
+class TypedWritable;
+class Datagram;
+class DatagramIterator;
+
 ////////////////////////////////////////////////////////////////////
 //       Class : CycleData
 // Description : A single page of data maintained by a PipelineCycler.
@@ -51,6 +57,10 @@ public:
   virtual ~CycleData();
 
   virtual CycleData *make_copy() const=0;
+
+  virtual void write_datagram(BamWriter *, Datagram &) const;
+  virtual int complete_pointers(TypedWritable **p_list, BamReader *manager);
+  virtual void fillin(DatagramIterator &scan, BamReader *manager);
 };
 
 #include "cycleData.I"

+ 68 - 2
panda/src/putil/cycleDataReader.I

@@ -28,6 +28,7 @@ CycleDataReader(const PipelineCycler<CycleDataType> &cycler) :
   _cycler(cycler)
 {
   _pointer = _cycler.read();
+  _write_pointer = (CycleDataType *)NULL;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -39,8 +40,13 @@ template<class CycleDataType>
 INLINE CycleDataReader<CycleDataType>::
 CycleDataReader(const CycleDataReader<CycleDataType> &copy) :
   _cycler(copy._cycler),
-  _pointer(copy._pointer)
+  _pointer(copy._pointer),
+  _write_pointer(copy._write_pointer)
 {
+  nassertv(_pointer != (const CycleDataType *)NULL);
+  // We cannot copy-construct a CycleDataReader that has elevated
+  // itself to a write pointer.
+  nassertv(_write_pointer == (const CycleDataType *)NULL);
   _cycler.increment_read(_pointer);
 }
 
@@ -52,7 +58,14 @@ CycleDataReader(const CycleDataReader<CycleDataType> &copy) :
 template<class CycleDataType>
 INLINE CycleDataReader<CycleDataType>::
 ~CycleDataReader() {
-  _cycler.release_read(_pointer);
+  if (_write_pointer != (CycleDataType *)NULL) {
+    // If the _write_pointer is non-NULL, then someone called
+    // elevate_to_write() at some point, and we now actually hold a
+    // write pointer, not a read pointer.
+    ((PipelineCycler<CycleDataType> &)_cycler).release_write(_write_pointer);
+  } else if (_pointer != (CycleDataType *)NULL) {
+    _cycler.release_read(_pointer);
+  }
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -64,5 +77,58 @@ INLINE CycleDataReader<CycleDataType>::
 template<class CycleDataType>
 INLINE const CycleDataType *CycleDataReader<CycleDataType>::
 operator -> () const {
+  nassertr(_pointer != (const CycleDataType *)NULL, _cycler.cheat());
   return _pointer;
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataReader::Typecast pointer
+//       Access: Public
+//  Description: This allows the CycleDataReader to be passed to any
+//               function that expects a const CycleDataType pointer.
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataReader<CycleDataType>::
+operator const CycleDataType * () const {
+  nassertr(_pointer != (const CycleDataType *)NULL, _cycler.cheat());
+  return _pointer;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataReader::take_pointer
+//       Access: Public
+//  Description: This is intended to be called only from
+//               CycleDataWriter when it elevates the pointer from
+//               read to write status.  This function returns the
+//               reader's pointer and relinquishes ownership of the
+//               pointer, rendering the reader invalid for future
+//               reads.
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE const CycleDataType *CycleDataReader<CycleDataType>::
+take_pointer() {
+  const CycleDataType *pointer = _pointer;
+  _pointer = (CycleDataType *)NULL;
+  _write_pointer = (CycleDataType *)NULL;
+  nassertr(_pointer != (const CycleDataType *)NULL, _cycler.cheat());
+  return pointer;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataReader::elevate_to_write
+//       Access: Public
+//  Description: Call this to permanently elevate the readable pointer
+//               to a writable pointer.  This returns a writable
+//               pointer; subsequent calls to the same function will
+//               trivially return the same writable pointer.
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataType *CycleDataReader<CycleDataType>::
+elevate_to_write(PipelineCycler<CycleDataType> &cycler) {
+  nassertr(&cycler = &_cycler, cycler.cheat());
+  if (_write_pointer == (CycleDataType *)NULL) {
+    _write_pointer = cycler.elevate_read(_pointer);
+    _pointer = _write_pointer;
+  }
+  return _write_pointer;
+}

+ 5 - 0
panda/src/putil/cycleDataReader.h

@@ -45,10 +45,15 @@ public:
   INLINE ~CycleDataReader();
 
   INLINE const CycleDataType *operator -> () const;
+  INLINE operator const CycleDataType * () const;
+
+  INLINE const CycleDataType *take_pointer();
+  INLINE CycleDataType *elevate_to_write(PipelineCycler<CycleDataType> &cycler);
 
 private:
   const PipelineCycler<CycleDataType> &_cycler;
   const CycleDataType *_pointer;
+  CycleDataType *_write_pointer;
 };
 
 #include "cycleDataReader.I"

+ 46 - 7
panda/src/putil/cycleDataWriter.I

@@ -31,17 +31,39 @@ CycleDataWriter(PipelineCycler<CycleDataType> &cycler) :
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: CycleDataWriter::Copy Constructor
+//     Function: CycleDataWriter::Constructor
 //       Access: Public
-//  Description:
+//  Description: This is a lot like a copy constructor, in that the
+//               new CycleDataWriter object gets a handle to the same
+//               pointer held by the old CycleDataWriter object.
+//               However, since only one write pointer may be active
+//               at a time, this invalidates the old object.
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataWriter<CycleDataType>::
+CycleDataWriter(PipelineCycler<CycleDataType> &cycler, 
+                CycleDataWriter<CycleDataType> &take_from) :
+  _cycler(cycler),
+  _pointer(take_from._pointer)
+{
+  take_from._pointer = (CycleDataType *)NULL;
+  nassertv(_pointer != (CycleDataType *)NULL);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataWriter::Constructor
+//       Access: Public
+//  Description: This flavor of the constructor elevates the pointer
+//               from the CycleDataReader from a read to a write
+//               pointer (and invalidates the reader).
 ////////////////////////////////////////////////////////////////////
 template<class CycleDataType>
 INLINE CycleDataWriter<CycleDataType>::
-CycleDataWriter(const CycleDataWriter<CycleDataType> &copy) :
-  _cycler(copy._cycler),
-  _pointer(copy._pointer)
+CycleDataWriter(PipelineCycler<CycleDataType> &cycler,
+                CycleDataReader<CycleDataType> &take_from) :
+  _cycler(cycler)
 {
-  _cycler.increment_write(_pointer);
+  _pointer = _cycler.elevate_read(take_from.take_pointer());
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -52,7 +74,9 @@ CycleDataWriter(const CycleDataWriter<CycleDataType> &copy) :
 template<class CycleDataType>
 INLINE CycleDataWriter<CycleDataType>::
 ~CycleDataWriter() {
-  _cycler.release_write(_pointer);
+  if (_pointer != (CycleDataType *)NULL) {
+    _cycler.release_write(_pointer);
+  }
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -64,6 +88,7 @@ INLINE CycleDataWriter<CycleDataType>::
 template<class CycleDataType>
 INLINE CycleDataType *CycleDataWriter<CycleDataType>::
 operator -> () {
+  nassertr(_pointer != (CycleDataType *)NULL, _cycler.cheat());
   return _pointer;
 }
 
@@ -76,5 +101,19 @@ operator -> () {
 template<class CycleDataType>
 INLINE const CycleDataType *CycleDataWriter<CycleDataType>::
 operator -> () const {
+  nassertr(_pointer != (CycleDataType *)NULL, _cycler.cheat());
+  return _pointer;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataWriter::Typecast pointer
+//       Access: Public
+//  Description: This allows the CycleDataWriter to be passed to any
+//               function that expects a CycleDataType pointer.
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataWriter<CycleDataType>::
+operator CycleDataType * () {
+  nassertr(_pointer != (CycleDataType *)NULL, _cycler.cheat());
   return _pointer;
 }

+ 4 - 1
panda/src/putil/cycleDataWriter.h

@@ -40,13 +40,16 @@ template<class CycleDataType>
 class CycleDataWriter {
 public:
   INLINE CycleDataWriter(PipelineCycler<CycleDataType> &cycler);
-  INLINE CycleDataWriter(const CycleDataWriter<CycleDataType> &copy);
+  INLINE CycleDataWriter(PipelineCycler<CycleDataType> &cycler, CycleDataWriter<CycleDataType> &take_from);
+  INLINE CycleDataWriter(PipelineCycler<CycleDataType> &cycler, CycleDataReader<CycleDataType> &take_from);
 
   INLINE ~CycleDataWriter();
 
   INLINE CycleDataType *operator -> ();
   INLINE const CycleDataType *operator -> () const;
 
+  INLINE operator CycleDataType * ();
+
 private:
   PipelineCycler<CycleDataType> &_cycler;
   CycleDataType *_pointer;

+ 102 - 0
panda/src/putil/pipelineCycler.I

@@ -33,6 +33,29 @@ PipelineCycler(Pipeline *pipeline) :
 {
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCycler::Copy Constructor (sanity-check)
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE PipelineCycler<CycleDataType>::
+PipelineCycler(const PipelineCycler<CycleDataType> &copy) :
+  PipelineCyclerBase(copy)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCycler::Copy Assignment (sanity-check)
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE void PipelineCycler<CycleDataType>::
+operator = (const PipelineCycler<CycleDataType> &copy) {
+  PipelineCyclerBase::operator = (copy);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PipelineCycler::read (sanity-check)
 //       Access: Public
@@ -55,6 +78,17 @@ write() {
   return (CycleDataType *)PipelineCyclerBase::write();
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCycler::elevate_read (sanity-check)
+//       Access: Public
+//  Description: See PipelineCyclerBase::elevate_read().
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataType *PipelineCycler<CycleDataType>::
+elevate_read(const CycleDataType *pointer) {
+  return (CycleDataType *)PipelineCyclerBase::elevate_read(pointer);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PipelineCycler::write_stage (sanity-check)
 //       Access: Public
@@ -66,6 +100,22 @@ write_stage(int n) {
   return (CycleDataType *)PipelineCyclerBase::write_stage(n);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCycler::cheat (sanity-check)
+//       Access: Public
+//  Description: Returns a pointer without counting it.  This is only
+//               intended for use as the return value for certain
+//               nassertr() functions, so the application can recover
+//               after a failure to manage the read and write pointers
+//               correctly.  You should never call this function
+//               directly.
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataType *PipelineCycler<CycleDataType>::
+cheat() const {
+  return (CycleDataType *)PipelineCyclerBase::cheat();
+}
+
 #else  // !DO_PIPELINING
 // The following implementations are provided for when pipelining is
 // not compiled in.  They are trivial functions that do as little as
@@ -83,6 +133,31 @@ PipelineCycler(Pipeline *pipeline) :
 {
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCycler::Copy Constructor (trivial)
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE PipelineCycler<CycleDataType>::
+PipelineCycler(const PipelineCycler<CycleDataType> &copy) :
+  PipelineCyclerBase(copy),
+  _data(copy._data)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCycler::Copy Assignment (trivial)
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE void PipelineCycler<CycleDataType>::
+operator = (const PipelineCycler<CycleDataType> &copy) {
+  PipelineCyclerBase::operator = (copy);
+  _data = copy._data;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PipelineCycler::read (trivial)
 //       Access: Public
@@ -105,6 +180,17 @@ write() {
   return &_data;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCycler::elevate_read (trivial)
+//       Access: Public
+//  Description: See PipelineCyclerBase::elevate_read().
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataType *PipelineCycler<CycleDataType>::
+elevate_read(const CycleDataType *) {
+  return &_data;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PipelineCycler::write_stage (trivial)
 //       Access: Public
@@ -116,5 +202,21 @@ write_stage(int) {
   return &_data;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCycler::cheat (trivial)
+//       Access: Public
+//  Description: Returns a pointer without counting it.  This is only
+//               intended for use as the return value for certain
+//               nassertr() functions, so the application can recover
+//               after a failure to manage the read and write pointers
+//               correctly.  You should never call this function
+//               directly.
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataType *PipelineCycler<CycleDataType>::
+cheat() const {
+  return &_data;
+}
+
 
 #endif   // DO_PIPELINING

+ 5 - 0
panda/src/putil/pipelineCycler.h

@@ -53,11 +53,16 @@ template<class CycleDataType>
 class PipelineCycler : public PipelineCyclerBase {
 public:
   INLINE PipelineCycler(Pipeline *pipeline = NULL);
+  INLINE PipelineCycler(const PipelineCycler<CycleDataType> &copy);
+  INLINE void operator = (const PipelineCycler<CycleDataType> &copy);
 
   INLINE const CycleDataType *read() const;
   INLINE CycleDataType *write();
+  INLINE CycleDataType *elevate_read(const CycleDataType *pointer);
   INLINE CycleDataType *write_stage(int n);
 
+  INLINE CycleDataType *cheat() const;
+
 #ifndef DO_PIPELINING
 private:
   // If we are *not* compiling in support for pipelining, we just

+ 202 - 20
panda/src/putil/pipelineCyclerBase.I

@@ -21,6 +21,61 @@
 // The following implementations are to support compiled-in pipeline
 // sanity checks.
 
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerBase::Constructor (sanity-check)
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE PipelineCyclerBase::
+PipelineCyclerBase(CycleData *initial_data, Pipeline *pipeline) :
+  _data(initial_data),
+  _pipeline(pipeline),
+  _read_count(0),
+  _write_count(0),
+  _stage_count(0)
+{
+  if (_pipeline == (Pipeline *)NULL) {
+    _pipeline = Pipeline::get_render_pipeline();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerBase::Copy Constructor (sanity-check)
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE PipelineCyclerBase::
+PipelineCyclerBase(const PipelineCyclerBase &copy) :
+  _data(copy._data->make_copy()),
+  _pipeline(copy._pipeline),
+  _read_count(0),
+  _write_count(0),
+  _stage_count(0)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerBase::Copy Assignment (sanity-check)
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE void PipelineCyclerBase::
+operator = (const PipelineCyclerBase &copy) {
+  nassertv(_read_count == 0 && _write_count == 0 && _stage_count == 0);
+  _data = copy._data->make_copy();
+  _pipeline = copy._pipeline;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerBase::Destructor (sanity-check)
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE PipelineCyclerBase::
+~PipelineCyclerBase() {
+  nassertv(_read_count == 0 && _write_count == 0 && _stage_count == 0);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PipelineCyclerBase::read (sanity-check)
 //       Access: Public
@@ -28,12 +83,16 @@
 //               data for the current stage of the pipeline as seen by
 //               this thread.  This pointer should eventually be
 //               released by calling release_read().
+//
+//               There should be no outstanding write pointers on the
+//               data when this function is called.
 ////////////////////////////////////////////////////////////////////
 INLINE const CycleData *PipelineCyclerBase::
 read() const {
   // This function isn't truly const, but it doesn't change the data
   // in any meaningful way, so we pretend it is.
   ((PipelineCyclerBase *)this)->_read_count++;
+  nassertr(_write_count == 0, _data);
   return _data;
 }
 
@@ -78,25 +137,33 @@ release_read(const CycleData *pointer) const {
 //               the data will be propagate to all later stages of the
 //               pipeline.  This pointer should eventually be released
 //               by calling release_write().
+//
+//               There may only be one outstanding write pointer on a
+//               given stage at a time, and if there is a write
+//               pointer there may be no read pointers on the same
+//               stage (but see elevate_read).
 ////////////////////////////////////////////////////////////////////
 INLINE CycleData *PipelineCyclerBase::
 write() {
   _write_count++;
+  nassertr(_read_count == 0, _data);
+  nassertr(_write_count == 1, _data);
   return _data;
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: PipelineCyclerBase::increment_write (sanity-check)
+//     Function: PipelineCyclerBase::elevate_read (sanity-check)
 //       Access: Public
-//  Description: Increments the count on a pointer previously
-//               retrieved by write(); now the pointer will need to be
-//               released twice.
+//  Description: Elevates a currently-held read pointer into a write
+//               pointer.  This may or may not change the value of the
+//               pointer.  It is only valid to do this if this is the
+//               only currently-outstanding read pointer on the
+//               current stage.
 ////////////////////////////////////////////////////////////////////
-INLINE void PipelineCyclerBase::
-increment_write(CycleData *pointer) {
-  nassertv(pointer == _data);
-  nassertv(_write_count > 0);
-  _write_count++;
+INLINE CycleData *PipelineCyclerBase::
+elevate_read(const CycleData *pointer) {
+  release_read(pointer);
+  return write();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -166,13 +233,43 @@ release_write_stage(int n, CycleData *pointer) {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: PipelineCyclerBase::Destructor (sanity-check)
+//     Function: PipelineCyclerBase::cheat (sanity-check)
 //       Access: Public
-//  Description: 
+//  Description: Returns a pointer without counting it.  This is only
+//               intended for use as the return value for certain
+//               nassertr() functions, so the application can recover
+//               after a failure to manage the read and write pointers
+//               correctly.  You should never call this function
+//               directly.
 ////////////////////////////////////////////////////////////////////
-INLINE PipelineCyclerBase::
-~PipelineCyclerBase() {
-  nassertv(_read_count == 0 && _write_count == 0 && _stage_count == 0);
+INLINE CycleData *PipelineCyclerBase::
+cheat() const {
+  return _data;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerBase::get_read_count (sanity-check)
+//       Access: Public
+//  Description: Returns the number of handles currently outstanding
+//               to read the current stage of the data.  This should
+//               only be used for debugging purposes.
+////////////////////////////////////////////////////////////////////
+INLINE int PipelineCyclerBase::
+get_read_count() const {
+  return _read_count;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerBase::get_write_count (sanity-check)
+//       Access: Public
+//  Description: Returns the number of handles currently outstanding
+//               to read the current stage of the data.  This will
+//               normally only be either 0 or 1.  This should only be
+//               used for debugging purposes.
+////////////////////////////////////////////////////////////////////
+INLINE int PipelineCyclerBase::
+get_write_count() const {
+  return _write_count;
 }
 
 #else  // !DO_PIPELINING
@@ -180,6 +277,43 @@ INLINE PipelineCyclerBase::
 // not compiled in.  They are trivial functions that do as little as
 // possible.
 
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerBase::Constructor (trivial)
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE PipelineCyclerBase::
+PipelineCyclerBase(CycleData *initial_data, Pipeline *) {
+  // In the trivial implementation, a derived class (the
+  // PipelineCycler template class) stores the CycleData object
+  // directly within itself, and since we have no data members or
+  // virtual functions, we get away with assuming the pointer is the
+  // same as the 'this' pointer.
+
+  // If this turns out not to be true on a particular platform, we
+  // will have to store the pointer in this class, for a little bit of
+  // extra overhead.
+  nassertv(initial_data == (CycleData *)this);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerBase::Copy Constructor (trivial)
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE PipelineCyclerBase::
+PipelineCyclerBase(const PipelineCyclerBase &) {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerBase::Copy Assignment (trivial)
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE void PipelineCyclerBase::
+operator = (const PipelineCyclerBase &) {
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PipelineCyclerBase::Destructor (trivial)
 //       Access: Public
@@ -233,6 +367,11 @@ release_read(const CycleData *) const {
 //               the data will be propagate to all later stages of the
 //               pipeline.  This pointer should eventually be released
 //               by calling release_write().
+//
+//               There may only be one outstanding write pointer on a
+//               given stage at a time, and if there is a write
+//               pointer there may be no read pointers on the same
+//               stage (but see elevate_read).
 ////////////////////////////////////////////////////////////////////
 INLINE CycleData *PipelineCyclerBase::
 write() {
@@ -240,14 +379,17 @@ write() {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: PipelineCyclerBase::increment_write (trivial)
+//     Function: PipelineCyclerBase::elevate_read (trivial)
 //       Access: Public
-//  Description: Increments the count on a pointer previously
-//               retrieved by write(); now the pointer will need to be
-//               released twice.
+//  Description: Elevates a currently-held read pointer into a write
+//               pointer.  This may or may not change the value of the
+//               pointer.  It is only valid to do this if this is the
+//               only currently-outstanding read pointer on the
+//               current stage.
 ////////////////////////////////////////////////////////////////////
-INLINE void PipelineCyclerBase::
-increment_write(CycleData *) {
+INLINE CycleData *PipelineCyclerBase::
+elevate_read(const CycleData *) {
+  return (CycleData *)this;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -307,5 +449,45 @@ INLINE void PipelineCyclerBase::
 release_write_stage(int, CycleData *) {
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerBase::cheat (trivial)
+//       Access: Public
+//  Description: Returns a pointer without counting it.  This is only
+//               intended for use as the return value for certain
+//               nassertr() functions, so the application can recover
+//               after a failure to manage the read and write pointers
+//               correctly.  You should never call this function
+//               directly.
+////////////////////////////////////////////////////////////////////
+INLINE CycleData *PipelineCyclerBase::
+cheat() const {
+  return (CycleData *)this;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerBase::get_read_count (trivial)
+//       Access: Public
+//  Description: Returns the number of handles currently outstanding
+//               to read the current stage of the data.  This should
+//               only be used for debugging purposes.
+////////////////////////////////////////////////////////////////////
+INLINE int PipelineCyclerBase::
+get_read_count() const {
+  return 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerBase::get_write_count (trivial)
+//       Access: Public
+//  Description: Returns the number of handles currently outstanding
+//               to read the current stage of the data.  This will
+//               normally only be either 0 or 1.  This should only be
+//               used for debugging purposes.
+////////////////////////////////////////////////////////////////////
+INLINE int PipelineCyclerBase::
+get_write_count() const {
+  return 0;
+}
+
 
 #endif   // DO_PIPELINING

+ 0 - 37
panda/src/putil/pipelineCyclerBase.cxx

@@ -23,47 +23,10 @@
 // The following implementations are to support compiled-in pipeline
 // sanity checks.
 
-////////////////////////////////////////////////////////////////////
-//     Function: PipelineCyclerBase::Constructor (sanity-check)
-//       Access: Public
-//  Description: 
-////////////////////////////////////////////////////////////////////
-PipelineCyclerBase::
-PipelineCyclerBase(CycleData *initial_data, Pipeline *pipeline) :
-  _data(initial_data),
-  _pipeline(pipeline),
-  _read_count(0),
-  _write_count(0),
-  _stage_count(0)
-{
-  if (_pipeline == (Pipeline *)NULL) {
-    _pipeline = Pipeline::get_render_pipeline();
-  }
-}
-
 
 #else  // !DO_PIPELINING
 // The following implementations are provided for when pipelining is
 // not compiled in.  They are trivial functions that do as little as
 // possible.
 
-////////////////////////////////////////////////////////////////////
-//     Function: PipelineCyclerBase::Constructor (trivial)
-//       Access: Public
-//  Description: 
-////////////////////////////////////////////////////////////////////
-PipelineCyclerBase::
-PipelineCyclerBase(CycleData *initial_data, Pipeline *) {
-  // In the trivial implementation, a derived class (the
-  // PipelineCycler template class) stores the CycleData object
-  // directly within itself, and since we have no data members or
-  // virtual functions, we get away with assuming the pointer is the
-  // same as the 'this' pointer.
-
-  // If this turns out not to be true on a particular platform, we
-  // will have to store the pointer in this class, for a little bit of
-  // extra overhead.
-  nassertv(initial_data == (CycleData *)this);
-}
-
 #endif

+ 8 - 2
panda/src/putil/pipelineCyclerBase.h

@@ -32,7 +32,9 @@
 ////////////////////////////////////////////////////////////////////
 class EXPCL_PANDA PipelineCyclerBase {
 public:
-  PipelineCyclerBase(CycleData *initial_data, Pipeline *pipeline = NULL);
+  INLINE PipelineCyclerBase(CycleData *initial_data, Pipeline *pipeline = NULL);
+  INLINE PipelineCyclerBase(const PipelineCyclerBase &copy);
+  INLINE void operator = (const PipelineCyclerBase &copy);
   INLINE ~PipelineCyclerBase();
 
   INLINE const CycleData *read() const;
@@ -40,7 +42,7 @@ public:
   INLINE void release_read(const CycleData *pointer) const;
 
   INLINE CycleData *write();
-  INLINE void increment_write(CycleData *pointer);
+  INLINE CycleData *elevate_read(const CycleData *pointer);
   INLINE void release_write(CycleData *pointer);
 
   INLINE int get_num_stages();
@@ -48,6 +50,10 @@ public:
   INLINE CycleData *write_stage(int n);
   INLINE void release_write_stage(int n, CycleData *pointer);
 
+  INLINE CycleData *cheat() const;
+  INLINE int get_read_count() const;
+  INLINE int get_write_count() const;
+
 #ifdef DO_PIPELINING
   // This private data is only stored here if we have pipelining
   // compiled in.  Actually, this particular data is only used for

+ 6 - 6
panda/src/putil/test_bam.cxx

@@ -77,8 +77,8 @@ fillin(Person* me, DatagramIterator& scan, BamReader* manager)
 {
   _name = scan.get_string();
   myGender = scan.get_uint8();
-  manager->read_pointer(scan, this);
-  manager->read_pointer(scan, this);
+  manager->read_pointer(scan);
+  manager->read_pointer(scan);
 }
 
 int Person::
@@ -122,8 +122,8 @@ void Parent::
 fillin(Parent* me, DatagramIterator& scan, BamReader* manager)
 {
   Person::fillin(me, scan, manager);
-  manager->read_pointer(scan, this);
-  manager->read_pointer(scan, this);
+  manager->read_pointer(scan);
+  manager->read_pointer(scan);
 }
 
 int Parent::
@@ -182,8 +182,8 @@ void Child::
 fillin(Child* me, DatagramIterator& scan, BamReader* manager)
 {
   Person::fillin(me, scan, manager);
-  manager->read_pointer(scan, this);
-  manager->read_pointer(scan, this);
+  manager->read_pointer(scan);
+  manager->read_pointer(scan);
 }
 
 int Child::

+ 1 - 1
panda/src/sgattrib/materialTransition.cxx

@@ -189,5 +189,5 @@ make_MaterialTransition(const FactoryParams &params) {
 void MaterialTransition::
 fillin(DatagramIterator& scan, BamReader* manager) {
   OnOffTransition::fillin(scan, manager);
-  manager->read_pointer(scan, this);
+  manager->read_pointer(scan);
 }

+ 1 - 1
panda/src/sgattrib/textureTransition.cxx

@@ -190,5 +190,5 @@ make_TextureTransition(const FactoryParams &params) {
 void TextureTransition::
 fillin(DatagramIterator& scan, BamReader* manager) {
   OnOffTransition::fillin(scan, manager);
-  manager->read_pointer(scan, this);
+  manager->read_pointer(scan);
 }

+ 1 - 1
panda/src/sgraph/geomNode.cxx

@@ -507,7 +507,7 @@ fillin(DatagramIterator& scan, BamReader* manager)
   _num_geoms = scan.get_uint16();
   for(int i = 0; i < _num_geoms; i++)
   {
-    manager->read_pointer(scan, this);
+    manager->read_pointer(scan);
   }
 }
 

+ 2 - 2
pandatool/src/egg-palettize/eggFile.cxx

@@ -628,10 +628,10 @@ fillin(DatagramIterator &scan, BamReader *manager) {
   _dest_filename = FilenameUnifier::get_bam_filename(scan.get_string());
 
   _num_textures = scan.get_uint32();
-  manager->read_pointers(scan, this, _num_textures);
+  manager->read_pointers(scan, _num_textures);
 
   _explicitly_assigned_groups.fillin(scan, manager);
-  manager->read_pointer(scan, this);  // _default_group
+  manager->read_pointer(scan);  // _default_group
 
   _is_surprise = scan.get_bool();
   _is_stale = scan.get_bool();

+ 2 - 2
pandatool/src/egg-palettize/paletteGroup.cxx

@@ -725,8 +725,8 @@ fillin(DatagramIterator &scan, BamReader *manager) {
   }
 
   _num_placements = scan.get_uint32();
-  manager->read_pointers(scan, this, _num_placements);
+  manager->read_pointers(scan, _num_placements);
 
   _num_pages = scan.get_uint32();
-  manager->read_pointers(scan, this, _num_pages);
+  manager->read_pointers(scan, _num_pages);
 }

+ 1 - 1
pandatool/src/egg-palettize/paletteGroups.cxx

@@ -338,5 +338,5 @@ void PaletteGroups::
 fillin(DatagramIterator &scan, BamReader *manager) {
   TypedWritable::fillin(scan, manager);
   _num_groups = scan.get_int32();
-  manager->read_pointers(scan, this, _num_groups);
+  manager->read_pointers(scan, _num_groups);
 }

+ 2 - 2
pandatool/src/egg-palettize/paletteImage.cxx

@@ -849,9 +849,9 @@ fillin(DatagramIterator &scan, BamReader *manager) {
   }
 
   _num_placements = scan.get_uint32();
-  manager->read_pointers(scan, this, _num_placements);
+  manager->read_pointers(scan, _num_placements);
 
-  manager->read_pointer(scan, this);  // _page
+  manager->read_pointer(scan);  // _page
 
   _index = scan.get_uint32();
   _basename = scan.get_string();

+ 2 - 2
pandatool/src/egg-palettize/palettePage.cxx

@@ -347,9 +347,9 @@ fillin(DatagramIterator &scan, BamReader *manager) {
   TypedWritable::fillin(scan, manager);
   set_name(scan.get_string());
 
-  manager->read_pointer(scan, this);  // _group
+  manager->read_pointer(scan);  // _group
   _properties.fillin(scan, manager);
 
   _num_images = scan.get_uint32();
-  manager->read_pointers(scan, this, _num_images);
+  manager->read_pointers(scan, _num_images);
 }

+ 7 - 7
pandatool/src/egg-palettize/palettizer.cxx

@@ -1018,18 +1018,18 @@ fillin(DatagramIterator &scan, BamReader *manager) {
     _remap_char_uv = (RemapUV)scan.get_int32();
   }
 
-  manager->read_pointer(scan, this);  // _color_type
-  manager->read_pointer(scan, this);  // _alpha_type
-  manager->read_pointer(scan, this);  // _shadow_color_type
-  manager->read_pointer(scan, this);  // _shadow_alpha_type
+  manager->read_pointer(scan);  // _color_type
+  manager->read_pointer(scan);  // _alpha_type
+  manager->read_pointer(scan);  // _shadow_color_type
+  manager->read_pointer(scan);  // _shadow_alpha_type
 
   _num_egg_files = scan.get_int32();
-  manager->read_pointers(scan, this, _num_egg_files);
+  manager->read_pointers(scan, _num_egg_files);
 
   _num_groups = scan.get_int32();
-  manager->read_pointers(scan, this, _num_groups);
+  manager->read_pointers(scan, _num_groups);
 
   _num_textures = scan.get_int32();
-  manager->read_pointers(scan, this, _num_textures);
+  manager->read_pointers(scan, _num_textures);
 }
 

+ 1 - 1
pandatool/src/egg-palettize/sourceTextureImage.cxx

@@ -237,5 +237,5 @@ make_SourceTextureImage(const FactoryParams &params) {
 void SourceTextureImage::
 fillin(DatagramIterator &scan, BamReader *manager) {
   ImageFile::fillin(scan, manager);
-  manager->read_pointer(scan, this); // _texture
+  manager->read_pointer(scan); // _texture
 }

+ 3 - 3
pandatool/src/egg-palettize/textureImage.cxx

@@ -1245,10 +1245,10 @@ fillin(DatagramIterator &scan, BamReader *manager) {
   _actual_assigned_groups.fillin(scan, manager);
 
   _num_placement = scan.get_uint32();
-  manager->read_pointers(scan, this, _num_placement * 2);
+  manager->read_pointers(scan, _num_placement * 2);
 
   _num_sources = scan.get_uint32();
-  manager->read_pointers(scan, this, _num_sources);
+  manager->read_pointers(scan, _num_sources);
   _num_dests = scan.get_uint32();
-  manager->read_pointers(scan, this, _num_dests);
+  manager->read_pointers(scan, _num_dests);
 }

+ 5 - 5
pandatool/src/egg-palettize/texturePlacement.cxx

@@ -1029,12 +1029,12 @@ void TexturePlacement::
 fillin(DatagramIterator &scan, BamReader *manager) {
   TypedWritable::fillin(scan, manager);
 
-  manager->read_pointer(scan, this);  // _texture
-  manager->read_pointer(scan, this);  // _group
-  manager->read_pointer(scan, this);  // _image
+  manager->read_pointer(scan);  // _texture
+  manager->read_pointer(scan);  // _group
+  manager->read_pointer(scan);  // _image
 
   if (Palettizer::_read_pi_version >= 2) {
-    manager->read_pointer(scan, this);  // _dest
+    manager->read_pointer(scan);  // _dest
   }
 
   _has_uvs = scan.get_bool();
@@ -1046,7 +1046,7 @@ fillin(DatagramIterator &scan, BamReader *manager) {
   _omit_reason = (OmitReason)scan.get_int32();
 
   _num_references = scan.get_int32();
-  manager->read_pointers(scan, this, _num_references);
+  manager->read_pointers(scan, _num_references);
 }
 
 

+ 2 - 2
pandatool/src/egg-palettize/textureProperties.cxx

@@ -681,6 +681,6 @@ fillin(DatagramIterator &scan, BamReader *manager) {
   }
   _minfilter = (EggTexture::FilterType)scan.get_int32();
   _magfilter = (EggTexture::FilterType)scan.get_int32();
-  manager->read_pointer(scan, this);  // _color_type
-  manager->read_pointer(scan, this);  // _alpha_type
+  manager->read_pointer(scan);  // _color_type
+  manager->read_pointer(scan);  // _alpha_type
 }

+ 3 - 3
pandatool/src/egg-palettize/textureReference.cxx

@@ -773,13 +773,13 @@ make_TextureReference(const FactoryParams &params) {
 void TextureReference::
 fillin(DatagramIterator &scan, BamReader *manager) {
   TypedWritable::fillin(scan, manager);
-  manager->read_pointer(scan, this);  // _egg_file
+  manager->read_pointer(scan);  // _egg_file
 
   _tex_mat.read_datagram(scan);
   _inv_tex_mat.read_datagram(scan);
 
-  manager->read_pointer(scan, this);  // _source_texture
-  manager->read_pointer(scan, this);  // _placement
+  manager->read_pointer(scan);  // _source_texture
+  manager->read_pointer(scan);  // _placement
 
   _uses_alpha = scan.get_bool();
   _any_uvs = scan.get_bool();