Browse Source

unify PandaNode::CData again; create PandaNodePipelineReader

David Rose 19 years ago
parent
commit
f1038ea8ef

+ 19 - 13
panda/src/pgraph/cullTraverser.cxx

@@ -115,7 +115,7 @@ traverse(const NodePath &root, bool python_cull_control) {
 
 
     CullTraverserData data(root, TransformState::make_identity(),
     CullTraverserData data(root, TransformState::make_identity(),
                            _initial_state, _view_frustum, 
                            _initial_state, _view_frustum, 
-                           _guard_band);
+                           _guard_band, _current_thread);
     
     
     traverse(data);
     traverse(data);
     
     
@@ -133,7 +133,7 @@ traverse(const NodePath &root, bool python_cull_control) {
   } else {
   } else {
     CullTraverserData data(root, TransformState::make_identity(),
     CullTraverserData data(root, TransformState::make_identity(),
                            _initial_state, _view_frustum, 
                            _initial_state, _view_frustum, 
-                           _guard_band);
+                           _guard_band, _current_thread);
     
     
     traverse(data);
     traverse(data);
   }
   }
@@ -153,16 +153,17 @@ traverse(CullTraverserData &data) {
   // optimization, we should tag nodes with these properties as
   // optimization, we should tag nodes with these properties as
   // being "fancy", and skip this processing for non-fancy nodes.
   // being "fancy", and skip this processing for non-fancy nodes.
   
   
-  if (data.is_in_view(_camera_mask, _current_thread)) {
+  if (data.is_in_view(_camera_mask)) {
     if (pgraph_cat.is_spam()) {
     if (pgraph_cat.is_spam()) {
       pgraph_cat.spam() 
       pgraph_cat.spam() 
         << "\n" << data._node_path
         << "\n" << data._node_path
         << " " << data._draw_mask << "\n";
         << " " << data._draw_mask << "\n";
     }
     }
 
 
+    PandaNodePipelineReader *node_reader = data.node_reader();
     PandaNode *node = data.node();
     PandaNode *node = data.node();
 
 
-    const RenderEffects *node_effects = node->get_effects(_current_thread);
+    const RenderEffects *node_effects = node_reader->get_effects();
     if (node_effects->has_show_bounds()) {
     if (node_effects->has_show_bounds()) {
       // If we should show the bounding volume for this node, make it
       // If we should show the bounding volume for this node, make it
       // up now.
       // up now.
@@ -171,7 +172,7 @@ traverse(CullTraverserData &data) {
 
 
     data.apply_transform_and_state(this);
     data.apply_transform_and_state(this);
 
 
-    const FogAttrib *fog = node->get_state(_current_thread)->get_fog();
+    const FogAttrib *fog = node_reader->get_state()->get_fog();
     if (fog != (const FogAttrib *)NULL && fog->get_fog() != (Fog *)NULL) {
     if (fog != (const FogAttrib *)NULL && fog->get_fog() != (Fog *)NULL) {
       // If we just introduced a FogAttrib here, call adjust_to_camera()
       // If we just introduced a FogAttrib here, call adjust_to_camera()
       // now.  This maybe isn't the perfect time to call it, but it's
       // now.  This maybe isn't the perfect time to call it, but it's
@@ -200,11 +201,12 @@ traverse(CullTraverserData &data) {
 void CullTraverser::
 void CullTraverser::
 traverse_below(CullTraverserData &data) {
 traverse_below(CullTraverserData &data) {
   _nodes_pcollector.add_level(1);
   _nodes_pcollector.add_level(1);
+  PandaNodePipelineReader *node_reader = data.node_reader();
   PandaNode *node = data.node();
   PandaNode *node = data.node();
 
 
   bool this_node_hidden = data.is_this_node_hidden(this);
   bool this_node_hidden = data.is_this_node_hidden(this);
 
 
-  const RenderEffects *node_effects = node->get_effects(_current_thread);
+  const RenderEffects *node_effects = node_reader->get_effects();
   bool has_decal = !this_node_hidden && node_effects->has_decal();
   bool has_decal = !this_node_hidden && node_effects->has_decal();
   if (has_decal && !_depth_offset_decals) {
   if (has_decal && !_depth_offset_decals) {
     // Start the three-pass decal rendering if we're not using
     // Start the three-pass decal rendering if we're not using
@@ -252,7 +254,8 @@ traverse_below(CullTraverserData &data) {
     }
     }
 
 
     // Now visit all the node's children.
     // Now visit all the node's children.
-    PandaNode::Children children = node->get_children(_current_thread);
+    PandaNode::Children children = node_reader->get_children();
+    node_reader->release();
     int num_children = children.get_num_children();
     int num_children = children.get_num_children();
     if (node->has_selective_visibility()) {
     if (node->has_selective_visibility()) {
       int i = node->get_first_visible_child();
       int i = node->get_first_visible_child();
@@ -523,6 +526,8 @@ start_decal(const CullTraverserData &data) {
     return;
     return;
   }
   }
 
 
+  const PandaNodePipelineReader *node_reader = data.node_reader();
+
   // Build a chain of CullableObjects.  The head of the chain will be
   // Build a chain of CullableObjects.  The head of the chain will be
   // all of the base Geoms in order, followed by an empty
   // all of the base Geoms in order, followed by an empty
   // CullableObject node, followed by all of the decal Geoms, in
   // CullableObject node, followed by all of the decal Geoms, in
@@ -531,7 +536,7 @@ start_decal(const CullTraverserData &data) {
   // Since the CullableObject is a linked list which gets built in
   // Since the CullableObject is a linked list which gets built in
   // LIFO order, we start with the decals.
   // LIFO order, we start with the decals.
   CullableObject *decals = (CullableObject *)NULL;
   CullableObject *decals = (CullableObject *)NULL;
-  PandaNode::Children cr = node->get_children(_current_thread);
+  PandaNode::Children cr = node_reader->get_children();
   int num_children = cr.get_num_children();
   int num_children = cr.get_num_children();
   if (node->has_selective_visibility()) {
   if (node->has_selective_visibility()) {
     int i = node->get_first_visible_child();
     int i = node->get_first_visible_child();
@@ -592,10 +597,11 @@ start_decal(const CullTraverserData &data) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 CullableObject *CullTraverser::
 CullableObject *CullTraverser::
 r_get_decals(CullTraverserData &data, CullableObject *decals) {
 r_get_decals(CullTraverserData &data, CullableObject *decals) {
-  if (data.is_in_view(_camera_mask, _current_thread)) {
+  if (data.is_in_view(_camera_mask)) {
+    PandaNodePipelineReader *node_reader = data.node_reader();
     PandaNode *node = data.node();
     PandaNode *node = data.node();
 
 
-    const RenderEffects *node_effects = node->get_effects();
+    const RenderEffects *node_effects = node_reader->get_effects();
     if (node_effects->has_show_bounds()) {
     if (node_effects->has_show_bounds()) {
       // If we should show the bounding volume for this node, make it
       // If we should show the bounding volume for this node, make it
       // up now.
       // up now.
@@ -605,18 +611,18 @@ r_get_decals(CullTraverserData &data, CullableObject *decals) {
     data.apply_transform_and_state(this);
     data.apply_transform_and_state(this);
 
 
     // First, visit all of the node's children.
     // First, visit all of the node's children.
-    int num_children = node->get_num_children();
+    int num_children = node_reader->get_num_children();
     if (node->has_selective_visibility()) {
     if (node->has_selective_visibility()) {
       int i = node->get_first_visible_child();
       int i = node->get_first_visible_child();
       while (i < num_children) {
       while (i < num_children) {
-        CullTraverserData next_data(data, node->get_child(i));
+        CullTraverserData next_data(data, node_reader->get_child(i));
         decals = r_get_decals(next_data, decals);
         decals = r_get_decals(next_data, decals);
         i = node->get_next_visible_child(i);
         i = node->get_next_visible_child(i);
       }
       }
       
       
     } else {
     } else {
       for (int i = num_children - 1; i >= 0; i--) {
       for (int i = num_children - 1; i >= 0; i--) {
-        CullTraverserData next_data(data, node->get_child(i));
+        CullTraverserData next_data(data, node_reader->get_child(i));
         decals = r_get_decals(next_data, decals);
         decals = r_get_decals(next_data, decals);
       }
       }
     }
     }

+ 34 - 5
panda/src/pgraph/cullTraverserData.I

@@ -26,8 +26,10 @@ CullTraverserData(const NodePath &start,
                   const TransformState *net_transform,
                   const TransformState *net_transform,
                   const RenderState *state,
                   const RenderState *state,
                   GeometricBoundingVolume *view_frustum,
                   GeometricBoundingVolume *view_frustum,
-                  GeometricBoundingVolume *guard_band) :
+                  GeometricBoundingVolume *guard_band,
+                  Thread *current_thread) :
   _node_path(start),
   _node_path(start),
+  _node_reader(start.node(), current_thread),
   _net_transform(net_transform),
   _net_transform(net_transform),
   _state(state),
   _state(state),
   _view_frustum(view_frustum),
   _view_frustum(view_frustum),
@@ -35,6 +37,7 @@ CullTraverserData(const NodePath &start,
   _cull_planes(CullPlanes::make_empty()),
   _cull_planes(CullPlanes::make_empty()),
   _draw_mask(DrawMask::all_on())
   _draw_mask(DrawMask::all_on())
 {
 {
+  _node_reader.check_bounds();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -45,6 +48,7 @@ CullTraverserData(const NodePath &start,
 INLINE CullTraverserData::
 INLINE CullTraverserData::
 CullTraverserData(const CullTraverserData &copy) :
 CullTraverserData(const CullTraverserData &copy) :
   _node_path(copy._node_path),
   _node_path(copy._node_path),
+  _node_reader(copy._node_reader),
   _net_transform(copy._net_transform),
   _net_transform(copy._net_transform),
   _state(copy._state),
   _state(copy._state),
   _view_frustum(copy._view_frustum),
   _view_frustum(copy._view_frustum),
@@ -62,6 +66,7 @@ CullTraverserData(const CullTraverserData &copy) :
 INLINE void CullTraverserData::
 INLINE void CullTraverserData::
 operator = (const CullTraverserData &copy) {
 operator = (const CullTraverserData &copy) {
   _node_path = copy._node_path;
   _node_path = copy._node_path;
+  _node_reader = copy._node_reader;
   _net_transform = copy._net_transform;
   _net_transform = copy._net_transform;
   _state = copy._state;
   _state = copy._state;
   _view_frustum = copy._view_frustum;
   _view_frustum = copy._view_frustum;
@@ -79,6 +84,7 @@ operator = (const CullTraverserData &copy) {
 INLINE CullTraverserData::
 INLINE CullTraverserData::
 CullTraverserData(const CullTraverserData &parent, PandaNode *child) :
 CullTraverserData(const CullTraverserData &parent, PandaNode *child) :
   _node_path(parent._node_path, child),
   _node_path(parent._node_path, child),
+  _node_reader(child, parent._node_reader.get_current_thread()),
   _net_transform(parent._net_transform),
   _net_transform(parent._net_transform),
   _state(parent._state),
   _state(parent._state),
   _view_frustum(parent._view_frustum),
   _view_frustum(parent._view_frustum),
@@ -86,6 +92,7 @@ CullTraverserData(const CullTraverserData &parent, PandaNode *child) :
   _cull_planes(parent._cull_planes),
   _cull_planes(parent._cull_planes),
   _draw_mask(parent._draw_mask)
   _draw_mask(parent._draw_mask)
 {
 {
+  _node_reader.check_bounds();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -107,6 +114,28 @@ node() const {
   return _node_path.node();
   return _node_path.node();
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: CullTraverserData::node_reader
+//       Access: Public
+//  Description: Returns the PipelineReader for the node traversed to
+//               so far.
+////////////////////////////////////////////////////////////////////
+INLINE PandaNodePipelineReader *CullTraverserData::
+node_reader() {
+  return &_node_reader;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CullTraverserData::node_reader
+//       Access: Public
+//  Description: Returns the PipelineReader for the node traversed to
+//               so far.
+////////////////////////////////////////////////////////////////////
+INLINE const PandaNodePipelineReader *CullTraverserData::
+node_reader() const {
+  return &_node_reader;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: CullTraverserData::get_net_transform
 //     Function: CullTraverserData::get_net_transform
 //       Access: Public
 //       Access: Public
@@ -128,13 +157,13 @@ get_net_transform(const CullTraverser *) const {
 //               work for future nodes.
 //               work for future nodes.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE bool CullTraverserData::
 INLINE bool CullTraverserData::
-is_in_view(const DrawMask &camera_mask, Thread *current_thread) {
-  if (node()->get_transform(current_thread)->is_invalid()) {
+is_in_view(const DrawMask &camera_mask) {
+  if (_node_reader.get_transform()->is_invalid()) {
     // If the transform is invalid, forget it.
     // If the transform is invalid, forget it.
     return false;
     return false;
   }
   }
 
 
-  if (!node()->compare_draw_mask(_draw_mask, camera_mask, current_thread)) {
+  if (!_node_reader.compare_draw_mask(_draw_mask, camera_mask)) {
     // If there are no draw bits in common with the camera, the node
     // If there are no draw bits in common with the camera, the node
     // is out.
     // is out.
     return false;
     return false;
@@ -148,7 +177,7 @@ is_in_view(const DrawMask &camera_mask, Thread *current_thread) {
   }
   }
 
 
   // Otherwise, compare the bounding volume to the frustum.
   // Otherwise, compare the bounding volume to the frustum.
-  return is_in_view_impl(current_thread);
+  return is_in_view_impl();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 12 - 14
panda/src/pgraph/cullTraverserData.cxx

@@ -51,24 +51,22 @@ get_modelview_transform(const CullTraverser *trav) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void CullTraverserData::
 void CullTraverserData::
 apply_transform_and_state(CullTraverser *trav) {
 apply_transform_and_state(CullTraverser *trav) {
-  Thread *current_thread = trav->get_current_thread();
-  PandaNode *node = _node_path.node();
-  CPT(RenderState) node_state = node->get_state(current_thread);
+  CPT(RenderState) node_state = _node_reader.get_state();
 
 
   if (trav->has_tag_state_key() && 
   if (trav->has_tag_state_key() && 
-      node->has_tag(trav->get_tag_state_key(), current_thread)) {
+      _node_reader.has_tag(trav->get_tag_state_key())) {
     // Here's a node that has been tagged with the special key for our
     // Here's a node that has been tagged with the special key for our
     // current camera.  This indicates some special state transition
     // current camera.  This indicates some special state transition
     // for this node, which is unique to this camera.
     // for this node, which is unique to this camera.
     const Camera *camera = trav->get_scene()->get_camera_node();
     const Camera *camera = trav->get_scene()->get_camera_node();
-    string tag_state = node->get_tag(trav->get_tag_state_key(), current_thread);
+    string tag_state = _node_reader.get_tag(trav->get_tag_state_key());
     node_state = node_state->compose(camera->get_tag_state(tag_state));
     node_state = node_state->compose(camera->get_tag_state(tag_state));
   }
   }
-  node->compose_draw_mask(_draw_mask, current_thread);
+  _node_reader.compose_draw_mask(_draw_mask);
 
 
-  apply_transform_and_state(trav, node->get_transform(current_thread),
-                            node_state, node->get_effects(current_thread),
-                            node->get_off_clip_planes(current_thread));
+  apply_transform_and_state(trav, _node_reader.get_transform(),
+                            node_state, _node_reader.get_effects(),
+                            _node_reader.get_off_clip_planes());
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -137,8 +135,8 @@ apply_transform_and_state(CullTraverser *trav,
 //  Description: The private implementation of is_in_view().
 //  Description: The private implementation of is_in_view().
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool CullTraverserData::
 bool CullTraverserData::
-is_in_view_impl(Thread *current_thread) {
-  CPT(BoundingVolume) node_volume = node()->get_bounds(current_thread);
+is_in_view_impl() {
+  CPT(BoundingVolume) node_volume = _node_reader.get_bounds();
   nassertr(node_volume->is_of_type(GeometricBoundingVolume::get_class_type()), false);
   nassertr(node_volume->is_of_type(GeometricBoundingVolume::get_class_type()), false);
   const GeometricBoundingVolume *node_gbv =
   const GeometricBoundingVolume *node_gbv =
     DCAST(GeometricBoundingVolume, node_volume);
     DCAST(GeometricBoundingVolume, node_volume);
@@ -173,7 +171,7 @@ is_in_view_impl(Thread *current_thread) {
     } else {
     } else {
       // The node is partially, but not completely, within the viewing
       // The node is partially, but not completely, within the viewing
       // frustum.
       // frustum.
-      if (node()->is_final(current_thread)) {
+      if (_node_reader.is_final()) {
         // Normally we'd keep testing child bounding volumes as we
         // Normally we'd keep testing child bounding volumes as we
         // continue down.  But this node has the "final" flag, so the
         // continue down.  But this node has the "final" flag, so the
         // user is claiming that there is some important reason we
         // user is claiming that there is some important reason we
@@ -207,7 +205,7 @@ is_in_view_impl(Thread *current_thread) {
       
       
     } else {
     } else {
       // The node is partially within one or more clip planes.
       // The node is partially within one or more clip planes.
-      if (node()->is_final(current_thread)) {
+      if (_node_reader.is_final()) {
         // Even though the node is only partially within the clip
         // Even though the node is only partially within the clip
         // planes, stop culling against them.
         // planes, stop culling against them.
         _cull_planes = CullPlanes::make_empty();
         _cull_planes = CullPlanes::make_empty();
@@ -230,7 +228,7 @@ test_within_clip_planes_impl(const CullTraverser *trav,
   int result = BoundingVolume::IF_all | BoundingVolume::IF_possible | BoundingVolume::IF_some;
   int result = BoundingVolume::IF_all | BoundingVolume::IF_possible | BoundingVolume::IF_some;
 
 
 
 
-  const BoundingVolume *node_volume = node()->get_bounds();
+  const BoundingVolume *node_volume = _node_reader.get_bounds();
   nassertr(node_volume->is_of_type(GeometricBoundingVolume::get_class_type()), result);
   nassertr(node_volume->is_of_type(GeometricBoundingVolume::get_class_type()), result);
   const GeometricBoundingVolume *node_gbv =
   const GeometricBoundingVolume *node_gbv =
     DCAST(GeometricBoundingVolume, node_volume);
     DCAST(GeometricBoundingVolume, node_volume);

+ 7 - 3
panda/src/pgraph/cullTraverserData.h

@@ -51,7 +51,8 @@ public:
                            const TransformState *net_transform,
                            const TransformState *net_transform,
                            const RenderState *state,
                            const RenderState *state,
                            GeometricBoundingVolume *view_frustum,
                            GeometricBoundingVolume *view_frustum,
-                           GeometricBoundingVolume *guard_band);
+                           GeometricBoundingVolume *guard_band,
+                           Thread *current_thread);
   INLINE CullTraverserData(const CullTraverserData &copy);
   INLINE CullTraverserData(const CullTraverserData &copy);
   INLINE void operator = (const CullTraverserData &copy); 
   INLINE void operator = (const CullTraverserData &copy); 
   INLINE CullTraverserData(const CullTraverserData &parent, 
   INLINE CullTraverserData(const CullTraverserData &parent, 
@@ -59,11 +60,13 @@ public:
   INLINE ~CullTraverserData();
   INLINE ~CullTraverserData();
 
 
   INLINE PandaNode *node() const;
   INLINE PandaNode *node() const;
+  INLINE PandaNodePipelineReader *node_reader();
+  INLINE const PandaNodePipelineReader *node_reader() const;
 
 
   CPT(TransformState) get_modelview_transform(const CullTraverser *trav) const;
   CPT(TransformState) get_modelview_transform(const CullTraverser *trav) const;
   INLINE const TransformState *get_net_transform(const CullTraverser *trav) const;
   INLINE const TransformState *get_net_transform(const CullTraverser *trav) const;
 
 
-  INLINE bool is_in_view(const DrawMask &camera_mask, Thread *current_thread);
+  INLINE bool is_in_view(const DrawMask &camera_mask);
   INLINE bool is_this_node_hidden(const CullTraverser *trav) const;
   INLINE bool is_this_node_hidden(const CullTraverser *trav) const;
 
 
   void apply_transform_and_state(CullTraverser *trav);
   void apply_transform_and_state(CullTraverser *trav);
@@ -74,6 +77,7 @@ public:
                                  const RenderAttrib *off_clip_planes);
                                  const RenderAttrib *off_clip_planes);
 
 
   WorkingNodePath _node_path;
   WorkingNodePath _node_path;
+  PandaNodePipelineReader _node_reader;
   CPT(TransformState) _net_transform;
   CPT(TransformState) _net_transform;
   CPT(RenderState) _state;
   CPT(RenderState) _state;
   PT(GeometricBoundingVolume) _view_frustum;
   PT(GeometricBoundingVolume) _view_frustum;
@@ -82,7 +86,7 @@ public:
   DrawMask _draw_mask;
   DrawMask _draw_mask;
 
 
 private:
 private:
-  bool is_in_view_impl(Thread *current_thread);
+  bool is_in_view_impl();
   int test_within_clip_planes_impl(const CullTraverser *trav,
   int test_within_clip_planes_impl(const CullTraverser *trav,
                                    const ClipPlaneAttrib *cpa) const;
                                    const ClipPlaneAttrib *cpa) const;
 
 

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

@@ -56,13 +56,13 @@ munge_geom(GraphicsStateGuardianBase *gsg,
     GeomPipelineReader geom_reader(_geom, current_thread);
     GeomPipelineReader geom_reader(_geom, current_thread);
     _munged_data = geom_reader.get_vertex_data();
     _munged_data = geom_reader.get_vertex_data();
 
 
-#ifndef NDEBUG
+#ifdef _DEBUG
     {
     {
       GeomVertexDataPipelineReader data_reader(_munged_data, current_thread);
       GeomVertexDataPipelineReader data_reader(_munged_data, current_thread);
       data_reader.check_array_readers();
       data_reader.check_array_readers();
       nassertv(geom_reader.check_valid(&data_reader));
       nassertv(geom_reader.check_valid(&data_reader));
     }
     }
-#endif  // NDEBUG
+#endif  // _DEBUG
 
 
     int geom_rendering = geom_reader.get_geom_rendering();
     int geom_rendering = geom_reader.get_geom_rendering();
     geom_rendering = _state->get_geom_rendering(geom_rendering);
     geom_rendering = _state->get_geom_rendering(geom_rendering);

+ 562 - 163
panda/src/pgraph/pandaNode.I

@@ -17,63 +17,6 @@
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 
 
 
 
-////////////////////////////////////////////////////////////////////
-//     Function: PandaNode::compose_draw_mask
-//       Access: Public
-//  Description: Computes the result of applying this node's draw
-//               masks to a running draw mask, as during a traversal.
-////////////////////////////////////////////////////////////////////
-INLINE void PandaNode::
-compose_draw_mask(DrawMask &running_draw_mask,
-                  Thread *current_thread) const {
-  CDHeavyReader cdata(_cycler_heavy, current_thread);
-  running_draw_mask = (running_draw_mask & ~cdata->_draw_control_mask) |
-    (cdata->_draw_show_mask & cdata->_draw_control_mask);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PandaNode::compare_draw_mask
-//       Access: Public
-//  Description: Compares the running draw mask computed during a
-//               traversal with this node's net draw masks.  Returns
-//               true if the node should be traversed into, or false
-//               if there is nothing at this level or below that will
-//               be visible to the indicated camera_mask.
-////////////////////////////////////////////////////////////////////
-INLINE bool PandaNode::
-compare_draw_mask(DrawMask running_draw_mask, DrawMask camera_mask,
-                  Thread *current_thread) const {
-  DrawMask net_draw_control_mask, net_draw_show_mask;
-
-  int pipeline_stage = current_thread->get_pipeline_stage();
-  CDBoundsStageReader cdata(_cycler_bounds, pipeline_stage, current_thread);
-  if (cdata->_last_update != cdata->_next_update) {
-    // The cache is stale; it needs to be rebuilt.
-    CDBoundsStageWriter cdataw = ((PandaNode *)this)->update_bounds(pipeline_stage, cdata); 
-    net_draw_control_mask = cdataw->_net_draw_control_mask;
-    net_draw_show_mask = cdataw->_net_draw_show_mask;
-  } else {
-    net_draw_control_mask = cdata->_net_draw_control_mask;
-    net_draw_show_mask = cdata->_net_draw_show_mask;
-  }
-
-  // Now the bits that are not in net_draw_control_mask--that is,
-  // those bits that are not changed by any of the nodes at this level
-  // and below--are taken from running_draw_mask, which is inherited
-  // from above.  On the other hand, the bits that *are* in
-  // net_draw_control_mask--those bits that are changed by any of the
-  // nodes at this level and below--are taken from net_draw_show_mask,
-  // which is propagated upwards from below.
-
-  // This way, we will traverse into this node if it has any children
-  // which want to be visited by the traversal, but we will avoid
-  // traversing into it if all of its children are hidden to this
-  // camera.
-  DrawMask compare_mask = (running_draw_mask & ~net_draw_control_mask) | (net_draw_show_mask & net_draw_control_mask);
-
-  return !((compare_mask & _overall_bit).is_zero()) && !((compare_mask & camera_mask).is_zero());
-}
-
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaNode::get_num_parents
 //     Function: PandaNode::get_num_parents
@@ -86,7 +29,7 @@ compare_draw_mask(DrawMask running_draw_mask, DrawMask camera_mask,
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE int PandaNode::
 INLINE int PandaNode::
 get_num_parents(Thread *current_thread) const {
 get_num_parents(Thread *current_thread) const {
-  CDLinksReader cdata(_cycler_links, current_thread);
+  CDReader cdata(_cycler, current_thread);
   return cdata->get_up()->size();
   return cdata->get_up()->size();
 }
 }
 
 
@@ -100,7 +43,7 @@ get_num_parents(Thread *current_thread) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE PandaNode *PandaNode::
 INLINE PandaNode *PandaNode::
 get_parent(int n, Thread *current_thread) const {
 get_parent(int n, Thread *current_thread) const {
-  CDLinksReader cdata(_cycler_links, current_thread);
+  CDReader cdata(_cycler, current_thread);
   const Up &up = *cdata->get_up();
   const Up &up = *cdata->get_up();
   nassertr(n >= 0 && n < (int)up.size(), NULL);
   nassertr(n >= 0 && n < (int)up.size(), NULL);
   return up[n].get_parent();
   return up[n].get_parent();
@@ -114,7 +57,7 @@ get_parent(int n, Thread *current_thread) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE int PandaNode::
 INLINE int PandaNode::
 find_parent(PandaNode *node, Thread *current_thread) const {
 find_parent(PandaNode *node, Thread *current_thread) const {
-  CDLinksReader cdata(_cycler_links, current_thread);
+  CDReader cdata(_cycler, current_thread);
   return do_find_parent(node, cdata);
   return do_find_parent(node, cdata);
 }
 }
 
 
@@ -128,7 +71,7 @@ find_parent(PandaNode *node, Thread *current_thread) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE int PandaNode::
 INLINE int PandaNode::
 get_num_children(Thread *current_thread) const {
 get_num_children(Thread *current_thread) const {
-  CDLinksReader cdata(_cycler_links, current_thread);
+  CDReader cdata(_cycler, current_thread);
   return cdata->get_down()->size();
   return cdata->get_down()->size();
 }
 }
 
 
@@ -142,7 +85,7 @@ get_num_children(Thread *current_thread) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE PandaNode *PandaNode::
 INLINE PandaNode *PandaNode::
 get_child(int n, Thread *current_thread) const {
 get_child(int n, Thread *current_thread) const {
-  CDLinksReader cdata(_cycler_links, current_thread);
+  CDReader cdata(_cycler, current_thread);
   const Down &down = *cdata->get_down();
   const Down &down = *cdata->get_down();
   nassertr(n >= 0 && n < (int)down.size(), NULL);
   nassertr(n >= 0 && n < (int)down.size(), NULL);
   return down[n].get_child();
   return down[n].get_child();
@@ -157,7 +100,7 @@ get_child(int n, Thread *current_thread) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE int PandaNode::
 INLINE int PandaNode::
 get_child_sort(int n, Thread *current_thread) const {
 get_child_sort(int n, Thread *current_thread) const {
-  CDLinksReader cdata(_cycler_links, current_thread);
+  CDReader cdata(_cycler, current_thread);
   const Down &down = *cdata->get_down();
   const Down &down = *cdata->get_down();
   nassertr(n >= 0 && n < (int)down.size(), -1);
   nassertr(n >= 0 && n < (int)down.size(), -1);
   return down[n].get_sort();
   return down[n].get_sort();
@@ -171,7 +114,7 @@ get_child_sort(int n, Thread *current_thread) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE int PandaNode::
 INLINE int PandaNode::
 find_child(PandaNode *node, Thread *current_thread) const {
 find_child(PandaNode *node, Thread *current_thread) const {
-  CDLinksReader cdata(_cycler_links, current_thread);
+  CDReader cdata(_cycler, current_thread);
   return do_find_child(node, cdata);
   return do_find_child(node, cdata);
 }
 }
 
 
@@ -236,7 +179,7 @@ unstash_child(PandaNode *child_node, Thread *current_thread) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE int PandaNode::
 INLINE int PandaNode::
 get_num_stashed(Thread *current_thread) const {
 get_num_stashed(Thread *current_thread) const {
-  CDLinksReader cdata(_cycler_links, current_thread);
+  CDReader cdata(_cycler, current_thread);
   return cdata->get_stashed()->size();
   return cdata->get_stashed()->size();
 }
 }
 
 
@@ -251,7 +194,7 @@ get_num_stashed(Thread *current_thread) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE PandaNode *PandaNode::
 INLINE PandaNode *PandaNode::
 get_stashed(int n, Thread *current_thread) const {
 get_stashed(int n, Thread *current_thread) const {
-  CDLinksReader cdata(_cycler_links, current_thread);
+  CDReader cdata(_cycler, current_thread);
   const Down &stashed = *cdata->get_stashed();
   const Down &stashed = *cdata->get_stashed();
   nassertr(n >= 0 && n < (int)stashed.size(), NULL);
   nassertr(n >= 0 && n < (int)stashed.size(), NULL);
   return stashed[n].get_child();
   return stashed[n].get_child();
@@ -266,7 +209,7 @@ get_stashed(int n, Thread *current_thread) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE int PandaNode::
 INLINE int PandaNode::
 get_stashed_sort(int n, Thread *current_thread) const {
 get_stashed_sort(int n, Thread *current_thread) const {
-  CDLinksReader cdata(_cycler_links, current_thread);
+  CDReader cdata(_cycler, current_thread);
   const Down &stashed = *cdata->get_stashed();
   const Down &stashed = *cdata->get_stashed();
   nassertr(n >= 0 && n < (int)stashed.size(), -1);
   nassertr(n >= 0 && n < (int)stashed.size(), -1);
   return stashed[n].get_sort();
   return stashed[n].get_sort();
@@ -280,7 +223,7 @@ get_stashed_sort(int n, Thread *current_thread) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE int PandaNode::
 INLINE int PandaNode::
 find_stashed(PandaNode *node, Thread *current_thread) const {
 find_stashed(PandaNode *node, Thread *current_thread) const {
-  CDLinksReader cdata(_cycler_links, current_thread);
+  CDReader cdata(_cycler, current_thread);
   return do_find_stashed(node, cdata);
   return do_find_stashed(node, cdata);
 }
 }
 
 
@@ -295,7 +238,7 @@ find_stashed(PandaNode *node, Thread *current_thread) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE const RenderAttrib *PandaNode::
 INLINE const RenderAttrib *PandaNode::
 get_attrib(TypeHandle type) const {
 get_attrib(TypeHandle type) const {
-  CDLightReader cdata(_cycler_light);
+  CDReader cdata(_cycler);
   int index = cdata->_state->find_attrib(type);
   int index = cdata->_state->find_attrib(type);
   if (index >= 0) {
   if (index >= 0) {
     return cdata->_state->get_attrib(index);
     return cdata->_state->get_attrib(index);
@@ -312,7 +255,7 @@ get_attrib(TypeHandle type) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE bool PandaNode::
 INLINE bool PandaNode::
 has_attrib(TypeHandle type) const {
 has_attrib(TypeHandle type) const {
-  CDLightReader cdata(_cycler_light);
+  CDReader cdata(_cycler);
   int index = cdata->_state->find_attrib(type);
   int index = cdata->_state->find_attrib(type);
   return (index >= 0);
   return (index >= 0);
 }
 }
@@ -325,7 +268,7 @@ has_attrib(TypeHandle type) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE const RenderEffect *PandaNode::
 INLINE const RenderEffect *PandaNode::
 get_effect(TypeHandle type) const {
 get_effect(TypeHandle type) const {
-  CDHeavyReader cdata(_cycler_heavy);
+  CDReader cdata(_cycler);
   int index = cdata->_effects->find_effect(type);
   int index = cdata->_effects->find_effect(type);
   if (index >= 0) {
   if (index >= 0) {
     return cdata->_effects->get_effect(index);
     return cdata->_effects->get_effect(index);
@@ -342,7 +285,7 @@ get_effect(TypeHandle type) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE bool PandaNode::
 INLINE bool PandaNode::
 has_effect(TypeHandle type) const {
 has_effect(TypeHandle type) const {
-  CDHeavyReader cdata(_cycler_heavy);
+  CDReader cdata(_cycler);
   int index = cdata->_effects->find_effect(type);
   int index = cdata->_effects->find_effect(type);
   return (index >= 0);
   return (index >= 0);
 }
 }
@@ -358,7 +301,7 @@ has_effect(TypeHandle type) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE const RenderState *PandaNode::
 INLINE const RenderState *PandaNode::
 get_state(Thread *current_thread) const {
 get_state(Thread *current_thread) const {
-  CDLightReader cdata(_cycler_light, current_thread);
+  CDReader cdata(_cycler, current_thread);
   return cdata->_state;
   return cdata->_state;
 }
 }
 
 
@@ -383,7 +326,7 @@ clear_state(Thread *current_thread) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE const RenderEffects *PandaNode::
 INLINE const RenderEffects *PandaNode::
 get_effects(Thread *current_thread) const {
 get_effects(Thread *current_thread) const {
-  CDHeavyReader cdata(_cycler_heavy, current_thread);
+  CDReader cdata(_cycler, current_thread);
   return cdata->_effects;
   return cdata->_effects;
 }
 }
 
 
@@ -407,7 +350,7 @@ clear_effects(Thread *current_thread) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE const TransformState *PandaNode::
 INLINE const TransformState *PandaNode::
 get_transform(Thread *current_thread) const {
 get_transform(Thread *current_thread) const {
-  CDLightReader cdata(_cycler_light, current_thread);
+  CDReader cdata(_cycler, current_thread);
   return cdata->_transform;
   return cdata->_transform;
 }
 }
 
 
@@ -431,7 +374,7 @@ clear_transform(Thread *current_thread) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 const TransformState *PandaNode::
 const TransformState *PandaNode::
 get_prev_transform(Thread *current_thread) const {
 get_prev_transform(Thread *current_thread) const {
-  CDLightReader cdata(_cycler_light, current_thread);
+  CDReader cdata(_cycler, current_thread);
   return cdata->_prev_transform;
   return cdata->_prev_transform;
 }
 }
 
 
@@ -459,7 +402,7 @@ has_dirty_prev_transform() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE string PandaNode::
 INLINE string PandaNode::
 get_tag(const string &key, Thread *current_thread) const {
 get_tag(const string &key, Thread *current_thread) const {
-  CDHeavyReader cdata(_cycler_heavy, current_thread);
+  CDReader cdata(_cycler, current_thread);
   TagData::const_iterator ti;
   TagData::const_iterator ti;
   ti = cdata->_tag_data.find(key);
   ti = cdata->_tag_data.find(key);
   if (ti != cdata->_tag_data.end()) {
   if (ti != cdata->_tag_data.end()) {
@@ -477,7 +420,7 @@ get_tag(const string &key, Thread *current_thread) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE bool PandaNode::
 INLINE bool PandaNode::
 has_tag(const string &key, Thread *current_thread) const {
 has_tag(const string &key, Thread *current_thread) const {
-  CDHeavyReader cdata(_cycler_heavy, current_thread);
+  CDReader cdata(_cycler, current_thread);
   TagData::const_iterator ti;
   TagData::const_iterator ti;
   ti = cdata->_tag_data.find(key);
   ti = cdata->_tag_data.find(key);
   return (ti != cdata->_tag_data.end());
   return (ti != cdata->_tag_data.end());
@@ -491,7 +434,7 @@ has_tag(const string &key, Thread *current_thread) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE bool PandaNode::
 INLINE bool PandaNode::
 has_tags() const {
 has_tags() const {
-  CDHeavyReader cdata(_cycler_heavy);
+  CDReader cdata(_cycler);
   if (!cdata->_tag_data.empty()) {
   if (!cdata->_tag_data.empty()) {
     return true;
     return true;
   }
   }
@@ -535,7 +478,7 @@ get_overall_bit() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE bool PandaNode::
 INLINE bool PandaNode::
 is_overall_hidden() const {
 is_overall_hidden() const {
-  CDHeavyReader cdata(_cycler_heavy);
+  CDReader cdata(_cycler);
   return ((cdata->_draw_show_mask | ~cdata->_draw_control_mask) & _overall_bit).is_zero();
   return ((cdata->_draw_show_mask | ~cdata->_draw_control_mask) & _overall_bit).is_zero();
 }
 }
 
 
@@ -570,7 +513,7 @@ set_overall_hidden(bool hidden) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE DrawMask PandaNode::
 INLINE DrawMask PandaNode::
 get_draw_control_mask() const {
 get_draw_control_mask() const {
-  CDHeavyReader cdata(_cycler_heavy);
+  CDReader cdata(_cycler);
   return cdata->_draw_control_mask;
   return cdata->_draw_control_mask;
 }
 }
 
 
@@ -582,7 +525,7 @@ get_draw_control_mask() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE DrawMask PandaNode::
 INLINE DrawMask PandaNode::
 get_draw_show_mask() const {
 get_draw_show_mask() const {
-  CDHeavyReader cdata(_cycler_heavy);
+  CDReader cdata(_cycler);
   return cdata->_draw_show_mask;
   return cdata->_draw_show_mask;
 }
 }
 
 
@@ -593,7 +536,7 @@ get_draw_show_mask() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE CollideMask PandaNode::
 INLINE CollideMask PandaNode::
 get_into_collide_mask() const {
 get_into_collide_mask() const {
-  CDHeavyReader cdata(_cycler_heavy);
+  CDReader cdata(_cycler);
   return cdata->_into_collide_mask;
   return cdata->_into_collide_mask;
 }
 }
 
 
@@ -645,7 +588,7 @@ get_internal_bounds() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE void PandaNode::
 INLINE void PandaNode::
 set_final(bool flag) {
 set_final(bool flag) {
-  CDHeavyWriter cdata(_cycler_heavy);
+  CDWriter cdata(_cycler);
   cdata->_final_bounds = flag;
   cdata->_final_bounds = flag;
 }
 }
 
 
@@ -659,7 +602,7 @@ set_final(bool flag) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE bool PandaNode::
 INLINE bool PandaNode::
 is_final(Thread *current_thread) const {
 is_final(Thread *current_thread) const {
-  CDHeavyReader cdata(_cycler_heavy, current_thread);
+  CDReader cdata(_cycler, current_thread);
   return cdata->_final_bounds;
   return cdata->_final_bounds;
 }
 }
 
 
@@ -673,7 +616,7 @@ is_final(Thread *current_thread) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE CPT(BoundingVolume) PandaNode::
 INLINE CPT(BoundingVolume) PandaNode::
 get_user_bounds(int pipeline_stage, Thread *current_thread) const {
 get_user_bounds(int pipeline_stage, Thread *current_thread) const {
-  CDHeavyStageReader cdata(_cycler_heavy, pipeline_stage, current_thread);
+  CDStageReader cdata(_cycler, pipeline_stage, current_thread);
   return cdata->_user_bounds;
   return cdata->_user_bounds;
 }
 }
 
 
@@ -682,7 +625,7 @@ get_user_bounds(int pipeline_stage, Thread *current_thread) const {
 //       Access: Protected
 //       Access: Protected
 //  Description: Indicates that the bounding volume, or something that
 //  Description: Indicates that the bounding volume, or something that
 //               influences the bounding volume (or any of the other
 //               influences the bounding volume (or any of the other
-//               things stored in CDataBounds, like net_collide_mask),
+//               things stored in CData, like net_collide_mask),
 //               may have changed for this node, and that it must be
 //               may have changed for this node, and that it must be
 //               recomputed.
 //               recomputed.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -692,7 +635,7 @@ mark_bounds_stale(int pipeline_stage, Thread *current_thread) const {
   // force_bounds_stale().
   // force_bounds_stale().
   bool is_stale_bounds;
   bool is_stale_bounds;
   {
   {
-    CDBoundsStageReader cdata(_cycler_bounds, pipeline_stage, current_thread);
+    CDStageReader cdata(_cycler, pipeline_stage, current_thread);
     is_stale_bounds = (cdata->_last_update != cdata->_next_update);
     is_stale_bounds = (cdata->_last_update != cdata->_next_update);
   }
   }
   if (!is_stale_bounds) {
   if (!is_stale_bounds) {
@@ -712,7 +655,7 @@ mark_bounds_stale(int pipeline_stage, Thread *current_thread) const {
 INLINE void PandaNode::
 INLINE void PandaNode::
 mark_internal_bounds_stale(int pipeline_stage, Thread *current_thread) {
 mark_internal_bounds_stale(int pipeline_stage, Thread *current_thread) {
   {
   {
-    CDHeavyStageWriter cdata(_cycler_heavy, pipeline_stage, current_thread);
+    CDStageWriter cdata(_cycler, pipeline_stage, current_thread);
     cdata->_internal_bounds_stale = true;
     cdata->_internal_bounds_stale = true;
   }
   }
   mark_bounds_stale(pipeline_stage, current_thread);
   mark_bounds_stale(pipeline_stage, current_thread);
@@ -737,7 +680,7 @@ mark_internal_bounds_stale(int pipeline_stage, Thread *current_thread) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE PandaNode::Children PandaNode::
 INLINE PandaNode::Children PandaNode::
 get_children(Thread *current_thread) const {
 get_children(Thread *current_thread) const {
-  CDLinksReader cdata(_cycler_links, current_thread);
+  CDReader cdata(_cycler, current_thread);
   return Children(cdata);
   return Children(cdata);
 }
 }
 
 
@@ -760,7 +703,7 @@ get_children(Thread *current_thread) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE PandaNode::Stashed PandaNode::
 INLINE PandaNode::Stashed PandaNode::
 get_stashed(Thread *current_thread) const {
 get_stashed(Thread *current_thread) const {
-  CDLinksReader cdata(_cycler_links, current_thread);
+  CDReader cdata(_cycler, current_thread);
   return Stashed(cdata);
   return Stashed(cdata);
 }
 }
 
 
@@ -773,7 +716,7 @@ get_stashed(Thread *current_thread) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE PandaNode::Parents PandaNode::
 INLINE PandaNode::Parents PandaNode::
 get_parents(Thread *current_thread) const {
 get_parents(Thread *current_thread) const {
-  CDLinksReader cdata(_cycler_links, current_thread);
+  CDReader cdata(_cycler, current_thread);
   return Parents(cdata);
   return Parents(cdata);
 }
 }
 
 
@@ -783,7 +726,7 @@ get_parents(Thread *current_thread) const {
 //  Description: The private implementation of find_parent().
 //  Description: The private implementation of find_parent().
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE int PandaNode::
 INLINE int PandaNode::
-do_find_parent(PandaNode *node, const CDataLinks *cdata) const {
+do_find_parent(PandaNode *node, const CData *cdata) const {
   const Up &up = *cdata->get_up();
   const Up &up = *cdata->get_up();
   Up::const_iterator ui = up.find(UpConnection(node));
   Up::const_iterator ui = up.find(UpConnection(node));
   if (ui == up.end()) {
   if (ui == up.end()) {
@@ -918,77 +861,22 @@ get_parent() const {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: PandaNode::CDataLight::Constructor
-//       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-INLINE PandaNode::CDataLight::
-CDataLight() {
-  _state = RenderState::make_empty();
-  _transform = TransformState::make_identity();
-  _prev_transform = TransformState::make_identity();
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PandaNode::CDataHeavy::Constructor
-//       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-INLINE PandaNode::CDataHeavy::
-CDataHeavy() {
-  _effects = RenderEffects::make_empty();
-  _draw_control_mask = DrawMask::all_off();
-  _draw_show_mask = DrawMask::all_on();
-  _into_collide_mask = CollideMask::all_off();
-  _user_bounds = NULL;
-  _internal_bounds = NULL;
-  _internal_bounds_stale = true;
-  _final_bounds = false;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PandaNode::CDataBounds::Constructor
-//       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-INLINE PandaNode::CDataBounds::
-CDataBounds() {
-  _net_collide_mask = CollideMask::all_off();
-  _net_draw_control_mask = DrawMask::all_off();
-  _net_draw_show_mask = DrawMask::all_off();
-  ++_next_update;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PandaNode::CDataLinks::Constructor
-//       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-INLINE PandaNode::CDataLinks::
-CDataLinks() : 
-  _down(new PandaNode::Down),
-  _stashed(new PandaNode::Down),
-  _up(new PandaNode::Up)
-{
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PandaNode::CDataLinks::get_down
+//     Function: PandaNode::CData::get_down
 //       Access: Public
 //       Access: Public
 //  Description: Returns a read-only pointer to the _down list.
 //  Description: Returns a read-only pointer to the _down list.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-INLINE const PandaNode::Down *PandaNode::CDataLinks::
+INLINE const PandaNode::Down *PandaNode::CData::
 get_down() const {
 get_down() const {
   return _down;
   return _down;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: PandaNode::CDataLinks::modify_down
+//     Function: PandaNode::CData::modify_down
 //       Access: Public
 //       Access: Public
 //  Description: Returns a modifiable, unique pointer to the _down
 //  Description: Returns a modifiable, unique pointer to the _down
 //               list.
 //               list.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-INLINE PandaNode::Down *PandaNode::CDataLinks::
+INLINE PandaNode::Down *PandaNode::CData::
 modify_down() {
 modify_down() {
   if (_down->get_ref_count() > 1) {
   if (_down->get_ref_count() > 1) {
     _down = new Down(*_down);
     _down = new Down(*_down);
@@ -997,22 +885,22 @@ modify_down() {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: PandaNode::CDataLinks::get_stashed
+//     Function: PandaNode::CData::get_stashed
 //       Access: Public
 //       Access: Public
 //  Description: Returns a read-only pointer to the _stashed list.
 //  Description: Returns a read-only pointer to the _stashed list.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-INLINE const PandaNode::Down *PandaNode::CDataLinks::
+INLINE const PandaNode::Down *PandaNode::CData::
 get_stashed() const {
 get_stashed() const {
   return _stashed;
   return _stashed;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: PandaNode::CDataLinks::modify_stashed
+//     Function: PandaNode::CData::modify_stashed
 //       Access: Public
 //       Access: Public
 //  Description: Returns a modifiable, unique pointer to the _stashed
 //  Description: Returns a modifiable, unique pointer to the _stashed
 //               list.
 //               list.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-INLINE PandaNode::Down *PandaNode::CDataLinks::
+INLINE PandaNode::Down *PandaNode::CData::
 modify_stashed() {
 modify_stashed() {
   if (_stashed->get_ref_count() > 1) {
   if (_stashed->get_ref_count() > 1) {
     _stashed = new Down(*_stashed);
     _stashed = new Down(*_stashed);
@@ -1021,22 +909,22 @@ modify_stashed() {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: PandaNode::CDataLinks::get_up
+//     Function: PandaNode::CData::get_up
 //       Access: Public
 //       Access: Public
 //  Description: Returns a read-only pointer to the _up list.
 //  Description: Returns a read-only pointer to the _up list.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-INLINE const PandaNode::Up *PandaNode::CDataLinks::
+INLINE const PandaNode::Up *PandaNode::CData::
 get_up() const {
 get_up() const {
   return _up;
   return _up;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: PandaNode::CDataLinks::modify_up
+//     Function: PandaNode::CData::modify_up
 //       Access: Public
 //       Access: Public
 //  Description: Returns a modifiable, unique pointer to the _up
 //  Description: Returns a modifiable, unique pointer to the _up
 //               list.
 //               list.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-INLINE PandaNode::Up *PandaNode::CDataLinks::
+INLINE PandaNode::Up *PandaNode::CData::
 modify_up() {
 modify_up() {
   if (_up->get_ref_count() > 1) {
   if (_up->get_ref_count() > 1) {
     _up = new Up(*_up);
     _up = new Up(*_up);
@@ -1059,7 +947,7 @@ Children() {
 //  Description:
 //  Description:
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE PandaNode::Children::
 INLINE PandaNode::Children::
-Children(const PandaNode::CDataLinks *cdata) :
+Children(const PandaNode::CData *cdata) :
   _down(cdata->get_down())
   _down(cdata->get_down())
 {
 {
 }
 }
@@ -1137,7 +1025,7 @@ Stashed() {
 //  Description:
 //  Description:
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE PandaNode::Stashed::
 INLINE PandaNode::Stashed::
-Stashed(const PandaNode::CDataLinks *cdata) :
+Stashed(const PandaNode::CData *cdata) :
   _stashed(cdata->get_stashed())
   _stashed(cdata->get_stashed())
 {
 {
 }
 }
@@ -1215,7 +1103,7 @@ Parents() {
 //  Description:
 //  Description:
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE PandaNode::Parents::
 INLINE PandaNode::Parents::
-Parents(const PandaNode::CDataLinks *cdata) :
+Parents(const PandaNode::CData *cdata) :
   _up(cdata->get_up())
   _up(cdata->get_up())
 {
 {
 }
 }
@@ -1263,3 +1151,514 @@ get_parent(int n) const {
   nassertr(n >= 0 && n < (int)_up->size(), NULL);
   nassertr(n >= 0 && n < (int)_up->size(), NULL);
   return (*_up)[n].get_parent();
   return (*_up)[n].get_parent();
 }
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNodePipelineReader::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE PandaNodePipelineReader::
+PandaNodePipelineReader(const PandaNode *object, Thread *current_thread) :
+  _object(object),
+  _current_thread(current_thread),
+  _cdata(object->_cycler.read(current_thread))
+{
+#ifdef _DEBUG
+  nassertv(_object->test_ref_count_nonzero());
+#ifdef DO_PIPELINING
+  nassertv(_cdata->test_ref_count_nonzero());
+#endif  // DO_PIPELINING
+#endif // _DEBUG
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNodePipelineReader::Copy Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE PandaNodePipelineReader::
+PandaNodePipelineReader(const PandaNodePipelineReader &copy) :
+  _object(copy._object),
+  _current_thread(copy._current_thread),
+  _cdata(copy._cdata)
+{
+  if (_cdata != (PandaNode::CData *)NULL) {
+    _object->_cycler.increment_read(_cdata);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNodePipelineReader::Copy Assignment Operator
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE void PandaNodePipelineReader::
+operator = (const PandaNodePipelineReader &copy) {
+  nassertv(_current_thread == copy._current_thread);
+
+  if (_cdata != (PandaNode::CData *)NULL) {
+    _object->_cycler.release_read(_cdata);
+  }
+
+  _object = copy._object;
+  _cdata = copy._cdata;
+
+  if (_cdata != (PandaNode::CData *)NULL) {
+    _object->_cycler.increment_read(_cdata);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNodePipelineReader::Destructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE PandaNodePipelineReader::
+~PandaNodePipelineReader() {
+  if (_cdata != (PandaNode::CData *)NULL) {
+    _object->_cycler.release_read(_cdata);
+  }
+
+#ifdef _DEBUG
+  _object = NULL;
+  _cdata = NULL;
+#endif  // _DEBUG
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNodePipelineReader::get_object
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE const PandaNode *PandaNodePipelineReader::
+get_object() const {
+  return _object;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNodePipelineReader::get_current_thread
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE Thread *PandaNodePipelineReader::
+get_current_thread() const {
+  return _current_thread;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNodePipelineReader::release
+//       Access: Public
+//  Description: Releases the lock on this object.  No future calls
+//               will be valid on this object.
+////////////////////////////////////////////////////////////////////
+INLINE void PandaNodePipelineReader::
+release() {
+  if (_cdata != (PandaNode::CData *)NULL) {
+    _object->_cycler.release_read(_cdata);
+    _cdata = NULL;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNodePipelineReader::check_bounds
+//       Access: Public
+//  Description: Ensures that the bounding volume is properly computed
+//               on this node.
+////////////////////////////////////////////////////////////////////
+INLINE void PandaNodePipelineReader::
+check_bounds() const {
+  nassertv(_cdata != (PandaNode::CData *)NULL);
+  if (_cdata->_last_update != _cdata->_next_update) {
+    // The cache is stale; it needs to be rebuilt.
+
+    // We should technically drop _cdata while we do this operation,
+    // but at the moment, it doesn't really matter.
+    int pipeline_stage = _current_thread->get_pipeline_stage();
+    PandaNode::CDStageReader fresh_cdata(_object->_cycler, pipeline_stage, _current_thread);
+    ((PandaNode *)_object)->update_bounds(pipeline_stage, fresh_cdata); 
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNodePipelineReader::compose_draw_mask
+//       Access: Public
+//  Description: Computes the result of applying this node's draw
+//               masks to a running draw mask, as during a traversal.
+////////////////////////////////////////////////////////////////////
+INLINE void PandaNodePipelineReader::
+compose_draw_mask(DrawMask &running_draw_mask) const {
+  nassertv(_cdata != (PandaNode::CData *)NULL);
+  running_draw_mask = (running_draw_mask & ~_cdata->_draw_control_mask) |
+    (_cdata->_draw_show_mask & _cdata->_draw_control_mask);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNodePipelineReader::compare_draw_mask
+//       Access: Public
+//  Description: Compares the running draw mask computed during a
+//               traversal with this node's net draw masks.  Returns
+//               true if the node should be traversed into, or false
+//               if there is nothing at this level or below that will
+//               be visible to the indicated camera_mask.
+////////////////////////////////////////////////////////////////////
+INLINE bool PandaNodePipelineReader::
+compare_draw_mask(DrawMask running_draw_mask, DrawMask camera_mask) const {
+  nassertr(_cdata != (PandaNode::CData *)NULL, false);
+  nassertr(_cdata->_last_update == _cdata->_next_update, false);
+
+  DrawMask net_draw_control_mask, net_draw_show_mask;
+  net_draw_control_mask = _cdata->_net_draw_control_mask;
+  net_draw_show_mask = _cdata->_net_draw_show_mask;
+
+  // Now the bits that are not in net_draw_control_mask--that is,
+  // those bits that are not changed by any of the nodes at this level
+  // and below--are taken from running_draw_mask, which is inherited
+  // from above.  On the other hand, the bits that *are* in
+  // net_draw_control_mask--those bits that are changed by any of the
+  // nodes at this level and below--are taken from net_draw_show_mask,
+  // which is propagated upwards from below.
+
+  // This way, we will traverse into this node if it has any children
+  // which want to be visited by the traversal, but we will avoid
+  // traversing into it if all of its children are hidden to this
+  // camera.
+  DrawMask compare_mask = (running_draw_mask & ~net_draw_control_mask) | (net_draw_show_mask & net_draw_control_mask);
+
+  return !((compare_mask & PandaNode::_overall_bit).is_zero()) && !((compare_mask & camera_mask).is_zero());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNodePipelineReader::get_num_parents
+//       Access: Published
+//  Description: Returns the number of parent nodes this node has.  If
+//               this number is greater than 1, the node has been
+//               multiply instanced.  The order of the parent nodes is
+//               not meaningful and is not related to the order in
+//               which the node was instanced to them.
+////////////////////////////////////////////////////////////////////
+INLINE int PandaNodePipelineReader::
+get_num_parents() const {
+  return _cdata->get_up()->size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNodePipelineReader::get_parent
+//       Access: Published
+//  Description: Returns the nth parent node of this node.  See
+//               get_num_parents().  Also see get_parents(), if your
+//               intention is to iterate through the complete list of
+//               parents; get_parents() is preferable in this case.
+////////////////////////////////////////////////////////////////////
+INLINE PandaNode *PandaNodePipelineReader::
+get_parent(int n) const {
+  const PandaNode::Up &up = *_cdata->get_up();
+  nassertr(n >= 0 && n < (int)up.size(), NULL);
+  return up[n].get_parent();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNodePipelineReader::find_parent
+//       Access: Published
+//  Description: Returns the index of the indicated parent node, if it
+//               is a parent, or -1 if it is not.
+////////////////////////////////////////////////////////////////////
+INLINE int PandaNodePipelineReader::
+find_parent(PandaNode *node) const {
+  return _object->do_find_parent(node, _cdata);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNodePipelineReader::get_num_children
+//       Access: Published
+//  Description: Returns the number of child nodes this node has.  The
+//               order of the child nodes *is* meaningful and is based
+//               on the sort number that was passed to add_child(),
+//               and also on the order in which the nodes were added.
+////////////////////////////////////////////////////////////////////
+INLINE int PandaNodePipelineReader::
+get_num_children() const {
+  return _cdata->get_down()->size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNodePipelineReader::get_child
+//       Access: Published
+//  Description: Returns the nth child node of this node.  See
+//               get_num_children().  Also see get_children(), if your
+//               intention is to iterate through the complete list of
+//               children; get_children() is preferable in this case.
+////////////////////////////////////////////////////////////////////
+INLINE PandaNode *PandaNodePipelineReader::
+get_child(int n) const {
+  const PandaNode::Down &down = *_cdata->get_down();
+  nassertr(n >= 0 && n < (int)down.size(), NULL);
+  return down[n].get_child();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNodePipelineReader::get_child_sort
+//       Access: Published
+//  Description: Returns the sort index of the nth child node of this
+//               node (that is, the number that was passed to
+//               add_child()).  See get_num_children().
+////////////////////////////////////////////////////////////////////
+INLINE int PandaNodePipelineReader::
+get_child_sort(int n) const {
+  const PandaNode::Down &down = *_cdata->get_down();
+  nassertr(n >= 0 && n < (int)down.size(), -1);
+  return down[n].get_sort();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNodePipelineReader::find_child
+//       Access: Published
+//  Description: Returns the index of the indicated child node, if it
+//               is a child, or -1 if it is not.
+////////////////////////////////////////////////////////////////////
+INLINE int PandaNodePipelineReader::
+find_child(PandaNode *node) const {
+  return _object->do_find_child(node, _cdata);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNodePipelineReader::get_num_stashed
+//       Access: Published
+//  Description: Returns the number of stashed nodes this node has.
+//               These are former children of the node that have been
+//               moved to the special stashed list via stash_child().
+////////////////////////////////////////////////////////////////////
+INLINE int PandaNodePipelineReader::
+get_num_stashed() const {
+  return _cdata->get_stashed()->size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNodePipelineReader::get_stashed
+//       Access: Published
+//  Description: Returns the nth stashed child of this node.  See
+//               get_num_stashed().  Also see get_stashed(), if your
+//               intention is to iterate through the complete list of
+//               stashed children; get_stashed() is preferable in this
+//               case.
+////////////////////////////////////////////////////////////////////
+INLINE PandaNode *PandaNodePipelineReader::
+get_stashed(int n) const {
+  const PandaNode::Down &stashed = *_cdata->get_stashed();
+  nassertr(n >= 0 && n < (int)stashed.size(), NULL);
+  return stashed[n].get_child();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNodePipelineReader::get_stashed_sort
+//       Access: Published
+//  Description: Returns the sort index of the nth stashed node of this
+//               node (that is, the number that was passed to
+//               add_child()).  See get_num_stashed().
+////////////////////////////////////////////////////////////////////
+INLINE int PandaNodePipelineReader::
+get_stashed_sort(int n) const {
+  const PandaNode::Down &stashed = *_cdata->get_stashed();
+  nassertr(n >= 0 && n < (int)stashed.size(), -1);
+  return stashed[n].get_sort();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNodePipelineReader::find_stashed
+//       Access: Published
+//  Description: Returns the index of the indicated stashed node, if
+//               it is a stashed child, or -1 if it is not.
+////////////////////////////////////////////////////////////////////
+INLINE int PandaNodePipelineReader::
+find_stashed(PandaNode *node) const {
+  return _object->do_find_stashed(node, _cdata);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNodePipelineReader::get_state
+//       Access: Published
+//  Description: Returns the complete RenderState that will be applied
+//               to all nodes at this level and below, as set on this
+//               node.  This returns only the RenderState set on this
+//               particular node, and has nothing to do with state
+//               that might be inherited from above.
+////////////////////////////////////////////////////////////////////
+INLINE const RenderState *PandaNodePipelineReader::
+get_state() const {
+  return _cdata->_state;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNodePipelineReader::get_effects
+//       Access: Published
+//  Description: Returns the complete RenderEffects that will be
+//               applied to this node.
+////////////////////////////////////////////////////////////////////
+INLINE const RenderEffects *PandaNodePipelineReader::
+get_effects() const {
+  return _cdata->_effects;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNodePipelineReader::get_transform
+//       Access: Published
+//  Description: Returns the transform that has been set on this
+//               particular node.  This is not the net transform from
+//               the root, but simply the transform on this particular
+//               node.
+////////////////////////////////////////////////////////////////////
+INLINE const TransformState *PandaNodePipelineReader::
+get_transform() const {
+  return _cdata->_transform;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNodePipelineReader::get_prev_transform
+//       Access: Published
+//  Description: Returns the transform that has been set as this
+//               node's "previous" position.  See
+//               set_prev_transform().
+////////////////////////////////////////////////////////////////////
+const TransformState *PandaNodePipelineReader::
+get_prev_transform() const {
+  return _cdata->_prev_transform;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNodePipelineReader::get_tag
+//       Access: Published
+//  Description: Retrieves the user-defined value that was previously
+//               set on this node for the particular key, if any.  If
+//               no value has been previously set, returns the empty
+//               string.
+////////////////////////////////////////////////////////////////////
+INLINE string PandaNodePipelineReader::
+get_tag(const string &key) const {
+  PandaNode::TagData::const_iterator ti;
+  ti = _cdata->_tag_data.find(key);
+  if (ti != _cdata->_tag_data.end()) {
+    return (*ti).second;
+  }
+  return string();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNodePipelineReader::has_tag
+//       Access: Published
+//  Description: Returns true if a value has been defined on this node
+//               for the particular key (even if that value is the
+//               empty string), or false if no value has been set.
+////////////////////////////////////////////////////////////////////
+INLINE bool PandaNodePipelineReader::
+has_tag(const string &key) const {
+  PandaNode::TagData::const_iterator ti;
+  ti = _cdata->_tag_data.find(key);
+  return (ti != _cdata->_tag_data.end());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNodePipelineReader::get_net_collide_mask
+//       Access: Published
+//  Description: Returns the union of all into_collide_mask() values
+//               set at CollisionNodes at this level and below.
+////////////////////////////////////////////////////////////////////
+INLINE CollideMask PandaNodePipelineReader::
+get_net_collide_mask() const {
+  nassertr(_cdata->_last_update == _cdata->_next_update, _cdata->_net_collide_mask);
+  return _cdata->_net_collide_mask;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNodePipelineReader::get_off_clip_planes
+//       Access: Published
+//  Description: Returns a ClipPlaneAttrib which represents the union
+//               of all of the clip planes that have been turned *off*
+//               at this level and below.
+////////////////////////////////////////////////////////////////////
+INLINE CPT(RenderAttrib) PandaNodePipelineReader::
+get_off_clip_planes() const {
+  nassertr(_cdata->_last_update == _cdata->_next_update, _cdata->_off_clip_planes);
+  return _cdata->_off_clip_planes;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNodePipelineReader::get_bounds
+//       Access: Published
+//  Description: Returns the external bounding volume of this node: a
+//               bounding volume that contains the user bounding
+//               volume, the internal bounding volume, and all of the
+//               children's bounding volumes.
+////////////////////////////////////////////////////////////////////
+INLINE CPT(BoundingVolume) PandaNodePipelineReader::
+get_bounds() const {
+  nassertr(_cdata->_last_update == _cdata->_next_update, _cdata->_external_bounds);
+  return _cdata->_external_bounds;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNodePipelineReader::is_final
+//       Access: Published
+//  Description: Returns the current state of the "final" flag.
+//               Initially, this flag is off (false), but it may be
+//               changed by an explicit call to set_final().  See
+//               set_final().
+////////////////////////////////////////////////////////////////////
+INLINE bool PandaNodePipelineReader::
+is_final() const {
+  return _cdata->_final_bounds;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNodePipelineReader::get_children
+//       Access: Public
+//  Description: Returns an object that can be used to walk through
+//               the list of children of the node.  When you intend to
+//               visit multiple children, using this is slightly
+//               faster than calling get_child() directly on the
+//               PandaNode, since this object avoids reopening the
+//               PipelineCycler each time.
+//
+//               This object also protects you from self-modifying
+//               loops (e.g. adding or removing children during
+//               traversal), since a virtual copy of the children is
+//               made ahead of time.  The virtual copy is fast--it is
+//               a form of copy-on-write, so the list is not actually
+//               copied unless it is modified during the traversal.
+////////////////////////////////////////////////////////////////////
+INLINE PandaNode::Children PandaNodePipelineReader::
+get_children() const {
+  return PandaNode::Children(_cdata);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNodePipelineReader::get_stashed
+//       Access: Public
+//  Description: Returns an object that can be used to walk through
+//               the list of children of the node.  When you intend to
+//               visit multiple children, using this is slightly
+//               faster than calling get_stashed() directly on the
+//               PandaNode, since this object avoids reopening the
+//               PipelineCycler each time.
+//
+//               This object also protects you from self-modifying
+//               loops (e.g. adding or removing children during
+//               traversal), since a virtual copy of the children is
+//               made ahead of time.  The virtual copy is fast--it is
+//               a form of copy-on-write, so the list is not actually
+//               copied unless it is modified during the traversal.
+////////////////////////////////////////////////////////////////////
+INLINE PandaNode::Stashed PandaNodePipelineReader::
+get_stashed() const {
+  return PandaNode::Stashed(_cdata);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNodePipelineReader::get_parents
+//       Access: Public
+//  Description: Returns an object that can be used to walk through
+//               the list of parents of the node, similar to
+//               get_children() and get_stashed().
+////////////////////////////////////////////////////////////////////
+INLINE PandaNode::Parents PandaNodePipelineReader::
+get_parents() const {
+  return PandaNode::Parents(_cdata);
+}

File diff suppressed because it is too large
+ 163 - 224
panda/src/pgraph/pandaNode.cxx


+ 98 - 104
panda/src/pgraph/pandaNode.h

@@ -110,12 +110,6 @@ public:
   virtual int get_visible_child() const;
   virtual int get_visible_child() const;
   virtual bool is_renderable() const;
   virtual bool is_renderable() const;
 
 
-  INLINE void compose_draw_mask(DrawMask &running_draw_mask,
-                                Thread *current_thread) const;
-  INLINE bool compare_draw_mask(DrawMask running_draw_mask,
-                                DrawMask camera_mask, 
-                                Thread *current_thread) const;
-
 PUBLISHED:
 PUBLISHED:
   PT(PandaNode) copy_subgraph(Thread *current_thread = Thread::get_current_thread()) const;
   PT(PandaNode) copy_subgraph(Thread *current_thread = Thread::get_current_thread()) const;
 
 
@@ -280,14 +274,11 @@ protected:
                                Thread *current_thread);
                                Thread *current_thread);
 
 
 private:
 private:
-  class CDataLight;
-  class CDataHeavy;
-  class CDataBounds;
-  class CDataLinks;
-
-  INLINE int do_find_parent(PandaNode *node, const CDataLinks *cdata) const;
-  int do_find_child(PandaNode *node, const CDataLinks *cdata) const;
-  int do_find_stashed(PandaNode *node, const CDataLinks *cdata) const;
+  class CData;
+
+  INLINE int do_find_parent(PandaNode *node, const CData *cdata) const;
+  int do_find_child(PandaNode *node, const CData *cdata) const;
+  int do_find_stashed(PandaNode *node, const CData *cdata) const;
   bool stage_remove_child(PandaNode *child_node, int pipeline_stage,
   bool stage_remove_child(PandaNode *child_node, int pipeline_stage,
                           Thread *current_thread);
                           Thread *current_thread);
   bool stage_replace_child(PandaNode *orig_child, PandaNode *new_child,
   bool stage_replace_child(PandaNode *orig_child, PandaNode *new_child,
@@ -387,21 +378,14 @@ private:
 #endif  // HAVE_PYTHON
 #endif  // HAVE_PYTHON
 
 
   
   
-  // This is the data that must be cycled between pipeline stages.  We
-  // store it in several different CData objects, in an attempt to
-  // minimize overhead caused by cycling data unnecessarily.  The
-  // things that are likely to change often (and at the same time) are
-  // grouped into the same CData object; things that change less often
-  // are grouped into a different one.
-
-  // The CDataLight object stores the lightweight parts of the node
-  // that are likely to change fairly often: transform and state.
-  class EXPCL_PANDA CDataLight : public CycleData {
+  // This is the data that must be cycled between pipeline stages. 
+
+  class EXPCL_PANDA CData : public CycleData {
   public:
   public:
-    INLINE CDataLight();
-    CDataLight(const CDataLight &copy);
-    virtual ~CDataLight();
-    ALLOC_DELETED_CHAIN(CDataLight);
+    CData();
+    CData(const CData &copy);
+    virtual ~CData();
+    ALLOC_DELETED_CHAIN(CData);
 
 
     virtual CycleData *make_copy() const;
     virtual CycleData *make_copy() const;
     virtual void write_datagram(BamWriter *manager, Datagram &dg) const;
     virtual void write_datagram(BamWriter *manager, Datagram &dg) const;
@@ -411,34 +395,18 @@ private:
       return PandaNode::get_class_type();
       return PandaNode::get_class_type();
     }
     }
 
 
+  public:
+    // This section contains the lightweight parts of the node that
+    // are likely to change fairly often: transform and state.
+
     NCPT(RenderState) _state;
     NCPT(RenderState) _state;
     NCPT(TransformState) _transform;
     NCPT(TransformState) _transform;
     NCPT(TransformState) _prev_transform;
     NCPT(TransformState) _prev_transform;
-  };
 
 
-  PipelineCycler<CDataLight> _cycler_light;
-  typedef CycleDataReader<CDataLight> CDLightReader;
-  typedef CycleDataWriter<CDataLight> CDLightWriter;
-  typedef CycleDataStageReader<CDataLight> CDLightStageReader;
-  typedef CycleDataStageWriter<CDataLight> CDLightStageWriter;
-
-  // The CDataHeavy object stores the heavierweight parts of the node
-  // that are less likely to change as often: tags, collide mask.
-  class EXPCL_PANDA CDataHeavy : public CycleData {
   public:
   public:
-    INLINE CDataHeavy();
-    CDataHeavy(const CDataHeavy &copy);
-    virtual ~CDataHeavy();
-    ALLOC_DELETED_CHAIN(CDataHeavy);
-
-    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);
-    virtual TypeHandle get_parent_type() const {
-      return PandaNode::get_class_type();
-    }
-
+    // This section contains the heavierweight parts of the node that
+    // are less likely to change as often: tags, collide mask.
+    
 #ifdef HAVE_PYTHON
 #ifdef HAVE_PYTHON
     void inc_py_refs();
     void inc_py_refs();
     void dec_py_refs();
     void dec_py_refs();
@@ -475,29 +443,12 @@ private:
     // This is true if the external bounds of this node should be
     // This is true if the external bounds of this node should be
     // deemed "final".  See set_final().
     // deemed "final".  See set_final().
     bool _final_bounds;
     bool _final_bounds;
-  };
 
 
-  PipelineCycler<CDataHeavy> _cycler_heavy;
-  typedef CycleDataReader<CDataHeavy> CDHeavyReader;
-  typedef CycleDataWriter<CDataHeavy> CDHeavyWriter;
-  typedef CycleDataStageReader<CDataHeavy> CDHeavyStageReader;
-  typedef CycleDataStageWriter<CDataHeavy> CDHeavyStageWriter;
-
-  // The CDataBounds object stores the data that is accumulated upward
-  // from the node's children: that is, the external bounding volume,
-  // and conceptually similar things like the net_collide_mask, etc.
-  // None of the data in this object is preserved in a bam file.
-  class EXPCL_PANDA CDataBounds : public CycleData {
   public:
   public:
-    INLINE CDataBounds();
-    CDataBounds(const CDataBounds &copy);
-    virtual ~CDataBounds();
-    ALLOC_DELETED_CHAIN(CDataBounds);
-
-    virtual CycleData *make_copy() const;
-    virtual TypeHandle get_parent_type() const {
-      return PandaNode::get_class_type();
-    }
+    // This section contains the data that is accumulated upward from
+    // the node's children: that is, the external bounding volume, and
+    // conceptually similar things like the net_collide_mask, etc.
+    // None of the data in this object is preserved in a bam file.
 
 
     // This is the union of all into_collide_mask bits for any nodes
     // This is the union of all into_collide_mask bits for any nodes
     // at and below this level.
     // at and below this level.
@@ -518,30 +469,10 @@ private:
 
 
     // When _last_update != _next_update, this cache is stale.
     // When _last_update != _next_update, this cache is stale.
     UpdateSeq _last_update, _next_update;
     UpdateSeq _last_update, _next_update;
-  };
 
 
-  PipelineCycler<CDataBounds> _cycler_bounds;
-  typedef CycleDataReader<CDataBounds> CDBoundsReader;
-  typedef CycleDataWriter<CDataBounds> CDBoundsWriter;
-  typedef CycleDataStageReader<CDataBounds> CDBoundsStageReader;
-  typedef CycleDataStageWriter<CDataBounds> CDBoundsStageWriter;
-
-  // The CDataLinks object stores the links to other nodes above and
-  // below this node in the graph.
-  class EXPCL_PANDA CDataLinks : public CycleData {
   public:
   public:
-    INLINE CDataLinks();
-    CDataLinks(const CDataLinks &copy);
-    virtual ~CDataLinks();
-    ALLOC_DELETED_CHAIN(CDataLinks);
-
-    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);
-    virtual TypeHandle get_parent_type() const {
-      return PandaNode::get_class_type();
-    }
+    // This section stores the links to other nodes above and below
+    // this node in the graph.
 
 
     void write_up_list(const Up &up_list,
     void write_up_list(const Up &up_list,
                        BamWriter *manager, Datagram &dg) const;
                        BamWriter *manager, Datagram &dg) const;
@@ -571,14 +502,13 @@ private:
     PT(Up) _up;
     PT(Up) _up;
   };
   };
 
 
-  PipelineCycler<CDataLinks> _cycler_links;
-  typedef CycleDataReader<CDataLinks> CDLinksReader;
-  typedef CycleDataWriter<CDataLinks> CDLinksWriter;
-  typedef CycleDataStageReader<CDataLinks> CDLinksStageReader;
-  typedef CycleDataStageWriter<CDataLinks> CDLinksStageWriter;
+  PipelineCycler<CData> _cycler;
+  typedef CycleDataReader<CData> CDReader;
+  typedef CycleDataWriter<CData> CDWriter;
+  typedef CycleDataStageReader<CData> CDStageReader;
+  typedef CycleDataStageWriter<CData> CDStageWriter;
 
 
-  CDBoundsStageWriter update_bounds(int pipeline_stage,
-                                    CDBoundsStageReader &cdata);
+  CDStageWriter update_bounds(int pipeline_stage, CDStageReader &cdata);
 
 
   static DrawMask _overall_bit;
   static DrawMask _overall_bit;
 
 
@@ -595,7 +525,7 @@ public:
   class EXPCL_PANDA Children {
   class EXPCL_PANDA Children {
   public:
   public:
     INLINE Children();
     INLINE Children();
-    INLINE Children(const CDataLinks *cdata);
+    INLINE Children(const CData *cdata);
     INLINE Children(const Children &copy);
     INLINE Children(const Children &copy);
     INLINE void operator = (const Children &copy);
     INLINE void operator = (const Children &copy);
 
 
@@ -611,7 +541,7 @@ public:
   class EXPCL_PANDA Stashed {
   class EXPCL_PANDA Stashed {
   public:
   public:
     INLINE Stashed();
     INLINE Stashed();
-    INLINE Stashed(const CDataLinks *cdata);
+    INLINE Stashed(const CData *cdata);
     INLINE Stashed(const Stashed &copy);
     INLINE Stashed(const Stashed &copy);
     INLINE void operator = (const Stashed &copy);
     INLINE void operator = (const Stashed &copy);
 
 
@@ -627,7 +557,7 @@ public:
   class EXPCL_PANDA Parents {
   class EXPCL_PANDA Parents {
   public:
   public:
     INLINE Parents();
     INLINE Parents();
-    INLINE Parents(const CDataLinks *cdata);
+    INLINE Parents(const CData *cdata);
     INLINE Parents(const Parents &copy);
     INLINE Parents(const Parents &copy);
     INLINE void operator = (const Parents &copy);
     INLINE void operator = (const Parents &copy);
 
 
@@ -678,6 +608,70 @@ private:
   friend class NodePath;
   friend class NodePath;
   friend class NodePathComponent;
   friend class NodePathComponent;
   friend class WorkingNodePath;
   friend class WorkingNodePath;
+  friend class PandaNodePipelineReader;
+};
+
+////////////////////////////////////////////////////////////////////
+//       Class : PandaNodePipelineReader
+// Description : Encapsulates the data from a PandaNode,
+//               pre-fetched for one stage of the pipeline.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA PandaNodePipelineReader {
+public:
+  INLINE PandaNodePipelineReader(const PandaNode *object, Thread *current_thread);
+  INLINE PandaNodePipelineReader(const PandaNodePipelineReader &copy);
+  INLINE void operator = (const PandaNodePipelineReader &copy);
+
+public:
+  INLINE ~PandaNodePipelineReader();
+  ALLOC_DELETED_CHAIN(PandaNodePipelineReader);
+
+  INLINE const PandaNode *get_object() const;
+  INLINE Thread *get_current_thread() const;
+
+  INLINE void release();
+
+  INLINE void check_bounds() const;
+
+  INLINE void compose_draw_mask(DrawMask &running_draw_mask) const;
+  INLINE bool compare_draw_mask(DrawMask running_draw_mask,
+                                DrawMask camera_mask) const;
+  
+  INLINE int get_num_parents() const;
+  INLINE PandaNode *get_parent(int n) const;
+  INLINE int find_parent(PandaNode *node) const;
+
+  INLINE int get_num_children() const;
+  INLINE PandaNode *get_child(int n) const;
+  INLINE int get_child_sort(int n) const;
+  INLINE int find_child(PandaNode *node) const;
+
+  INLINE int get_num_stashed() const;
+  INLINE PandaNode *get_stashed(int n) const;
+  INLINE int get_stashed_sort(int n) const;
+  INLINE int find_stashed(PandaNode *node) const;
+
+  INLINE const RenderState *get_state() const;
+  INLINE const RenderEffects *get_effects() const;
+  INLINE const TransformState *get_transform() const;
+  INLINE const TransformState *get_prev_transform() const;
+
+  INLINE string get_tag(const string &key) const;
+  INLINE bool has_tag(const string &key) const;
+
+  INLINE CollideMask get_net_collide_mask() const;
+  INLINE CPT(RenderAttrib) get_off_clip_planes() const;
+  INLINE CPT(BoundingVolume) get_bounds() const;
+  INLINE bool is_final() const;
+
+  INLINE PandaNode::Children get_children() const;
+  INLINE PandaNode::Stashed get_stashed() const;
+  INLINE PandaNode::Parents get_parents() const;
+
+private:
+  const PandaNode *_object;
+  Thread *_current_thread;
+  const PandaNode::CData *_cdata;
 };
 };
 
 
 INLINE ostream &operator << (ostream &out, const PandaNode &node) {
 INLINE ostream &operator << (ostream &out, const PandaNode &node) {

+ 4 - 1
panda/src/pgraph/portalNode.cxx

@@ -253,6 +253,8 @@ has_cull_callback() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool PortalNode::
 bool PortalNode::
 cull_callback(CullTraverser *trav, CullTraverserData &data) {
 cull_callback(CullTraverser *trav, CullTraverserData &data) {
+  Thread *current_thread = trav->get_current_thread();
+
   PortalClipper *portal_viewer = trav->get_portal_clipper();
   PortalClipper *portal_viewer = trav->get_portal_clipper();
   set_visible(false);
   set_visible(false);
   if (is_open() && !_cell_out.is_empty() && portal_viewer) {
   if (is_open() && !_cell_out.is_empty() && portal_viewer) {
@@ -312,7 +314,8 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) {
 
 
       CullTraverserData next_data(_cell_out, 
       CullTraverserData next_data(_cell_out, 
                                   cell_transform,
                                   cell_transform,
-                                  next_state, new_bh, NULL);
+                                  next_state, new_bh, NULL,
+                                  current_thread);
       //                                  data._state, new_bh, NULL);
       //                                  data._state, new_bh, NULL);
 
 
       // Make this cell show with the reduced frustum
       // Make this cell show with the reduced frustum

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