Browse Source

several pgraph refinements

David Rose 24 years ago
parent
commit
d210511b8b
38 changed files with 313 additions and 231 deletions
  1. 1 2
      panda/src/display/graphicsEngine.cxx
  2. 4 0
      panda/src/graph/boundedObject.cxx
  3. 1 1
      panda/src/loader/bamFile.h
  4. 1 1
      panda/src/pgraph/Sources.pp
  5. 50 23
      panda/src/pgraph/cullTraverserData.I
  6. 8 8
      panda/src/pgraph/cullTraverserData.cxx
  7. 16 5
      panda/src/pgraph/cullTraverserData.h
  8. 1 1
      panda/src/pgraph/pandaNode.I
  9. 7 3
      panda/src/pgraph/pandaNode.cxx
  10. 0 1
      panda/src/pgraph/pgraph_composite2.cxx
  11. 36 39
      panda/src/pgraph/qpcullTraverser.cxx
  12. 6 6
      panda/src/pgraph/qpcullTraverser.h
  13. 2 2
      panda/src/pgraph/qpgeomNode.cxx
  14. 3 0
      panda/src/pgraph/qpgeomTransformer.I
  15. 1 0
      panda/src/pgraph/qpgeomTransformer.cxx
  16. 15 0
      panda/src/pgraph/qplodNode.cxx
  17. 1 0
      panda/src/pgraph/qplodNode.h
  18. 15 0
      panda/src/pgraph/qpmodelNode.cxx
  19. 1 0
      panda/src/pgraph/qpmodelNode.h
  20. 0 28
      panda/src/pgraph/qpnodePath.I
  21. 29 0
      panda/src/pgraph/qpnodePath.cxx
  22. 2 3
      panda/src/pgraph/qpnodePath.h
  23. 6 2
      panda/src/pgraph/qpsceneGraphReducer.cxx
  24. 15 0
      panda/src/pgraph/qpsequenceNode.cxx
  25. 1 0
      panda/src/pgraph/qpsequenceNode.h
  26. 10 0
      panda/src/pgraph/workingNodePath.I
  27. 1 0
      panda/src/pgraph/workingNodePath.h
  28. 7 7
      panda/src/pgui/pgFrameStyle.cxx
  29. 2 1
      panda/src/pgui/pgFrameStyle.h
  30. 4 4
      panda/src/pgui/qppgButton.cxx
  31. 3 2
      panda/src/pgui/qppgEntry.I
  32. 14 17
      panda/src/pgui/qppgEntry.cxx
  33. 5 6
      panda/src/pgui/qppgEntry.h
  34. 34 52
      panda/src/pgui/qppgItem.cxx
  35. 5 5
      panda/src/pgui/qppgItem.h
  36. 1 1
      panda/src/pgui/qppgTop.cxx
  37. 4 10
      panda/src/pgui/qppgWaitBar.cxx
  38. 1 1
      panda/src/pgui/qppgWaitBar.h

+ 1 - 2
panda/src/display/graphicsEngine.cxx

@@ -281,8 +281,7 @@ do_cull(CullHandler *cull_handler, const qpNodePath &camera,
     }
   }
   
-  
-  trav.traverse(scene.node());
+  trav.traverse(scene);
 }
 
 ////////////////////////////////////////////////////////////////////

+ 4 - 0
panda/src/graph/boundedObject.cxx

@@ -104,6 +104,10 @@ BoundingVolume *BoundedObject::
 recompute_bound() {
   CDWriter cdata(_cycler);
   switch (cdata->_bound_type) {
+  case BVT_static:
+    // Don't change it if it's a static volume.
+    break;
+
   case BVT_dynamic_sphere:
     cdata->_bound = new BoundingSphere;
     break;

+ 1 - 1
panda/src/loader/bamFile.h

@@ -45,7 +45,7 @@ class Filename;
 //               common scene graph files.
 ////////////////////////////////////////////////////////////////////
 class EXPCL_PANDA BamFile {
-public:
+PUBLISHED:
   BamFile();
   ~BamFile();
 

+ 1 - 1
panda/src/pgraph/Sources.pp

@@ -41,7 +41,7 @@
     materialAttrib.I materialAttrib.h \
     qpmodelNode.I qpmodelNode.h \
     qpmodelRoot.I qpmodelRoot.h \
-    qpnodePath.I qpnodePath.h \
+    qpnodePath.I qpnodePath.h qpnodePath.cxx \
     qpnodePathCollection.I qpnodePathCollection.h \
     qpnodePathComponent.I qpnodePathComponent.h \
     qpnodePathLerps.h \

+ 50 - 23
panda/src/pgraph/cullTraverserData.I

@@ -23,11 +23,13 @@
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 INLINE CullTraverserData::
-CullTraverserData(const TransformState *render_transform,
+CullTraverserData(const qpNodePath &start,
+                  const TransformState *render_transform,
                   const TransformState *net_transform,
                   const RenderState *state,
                   GeometricBoundingVolume *view_frustum,
                   GeometricBoundingVolume *guard_band) :
+  _node_path(start),
   _render_transform(render_transform),
   _net_transform(net_transform),
   _state(state),
@@ -37,32 +39,42 @@ CullTraverserData(const TransformState *render_transform,
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: CullTraverserData::Copy Constructor
+//     Function: CullTraverserData::Constructor
 //       Access: Public
-//  Description: 
+//  Description: This constructor creates a CullTraverserData object
+//               that reflects the next node down in the traversal.
+////////////////////////////////////////////////////////////////////
+INLINE CullTraverserData::
+CullTraverserData(const CullTraverserData &parent, PandaNode *child) :
+  _node_path(parent._node_path, child),
+  _render_transform(parent._render_transform),
+  _net_transform(parent._net_transform),
+  _state(parent._state),
+  _view_frustum(parent._view_frustum),
+  _guard_band(parent._guard_band)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CullTraverserData::Copy Constructor
+//       Access: Private
+//  Description: Do not copy CullTraverserData objects.
 ////////////////////////////////////////////////////////////////////
 INLINE CullTraverserData::
 CullTraverserData(const CullTraverserData &copy) :
-  _render_transform(copy._render_transform),
-  _net_transform(copy._net_transform),
-  _state(copy._state),
-  _view_frustum(copy._view_frustum),
-  _guard_band(copy._guard_band)
+  _node_path(qpNodePath())
 {
+  nassertv(false);
 }
 
 ////////////////////////////////////////////////////////////////////
 //     Function: CullTraverserData::Copy Assignment Operator
-//       Access: Public
-//  Description: 
+//       Access: Private
+//  Description: Do not copy CullTraverserData objects.
 ////////////////////////////////////////////////////////////////////
 INLINE void CullTraverserData::
 operator = (const CullTraverserData &copy) {
-  _render_transform = copy._render_transform;
-  _net_transform = copy._net_transform;
-  _state = copy._state;
-  _view_frustum = copy._view_frustum;
-  _guard_band = copy._guard_band;
+  nassertv(false);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -74,18 +86,33 @@ INLINE CullTraverserData::
 ~CullTraverserData() {
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: CullTraverserData::node
+//       Access: Public
+//  Description: Returns the node traversed to so far.
+////////////////////////////////////////////////////////////////////
+INLINE PandaNode *CullTraverserData::
+node() const {
+  return _node_path.node();
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: CullTraverserData::is_in_view
 //       Access: Public
-//  Description: Returns true if the node is within the view frustum,
-//               false otherwise.  If the node's bounding volume falls
-//               completely within the view frustum, this will also
-//               reset the view frustum pointer, saving some work for
-//               future nodes.
+//  Description: Returns true if the current node is within the view
+//               frustum, false otherwise.  If the node's bounding
+//               volume falls completely within the view frustum, this
+//               will also reset the view frustum pointer, saving some
+//               work for future nodes.
 ////////////////////////////////////////////////////////////////////
 INLINE bool CullTraverserData::
-is_in_view(PandaNode *node, const DrawMask &camera_mask) {
-  if ((node->get_draw_mask() & camera_mask).is_zero()) {
+is_in_view(const DrawMask &camera_mask) {
+  if (node()->get_transform()->is_invalid()) {
+    // If the transform is invalid, forget it.
+    return false;
+  }
+
+  if ((node()->get_draw_mask() & camera_mask).is_zero()) {
     // If there are no draw bits in common with the camera, the node
     // is out.
     return false;
@@ -98,5 +125,5 @@ is_in_view(PandaNode *node, const DrawMask &camera_mask) {
   }
 
   // Otherwise, compare the bounding volume to the frustum.
-  return is_in_view_impl(node);
+  return is_in_view_impl();
 }

+ 8 - 8
panda/src/pgraph/cullTraverserData.cxx

@@ -28,13 +28,13 @@
 ////////////////////////////////////////////////////////////////////
 //     Function: CullTraverserData::apply_transform_and_state
 //       Access: Public
-//  Description: Applies the transform and state from the indicated
+//  Description: Applies the transform and state from the current
 //               node onto the current data.  This also evaluates
 //               billboards, etc.
 ////////////////////////////////////////////////////////////////////
 void CullTraverserData::
-apply_transform_and_state(qpCullTraverser *trav, PandaNode *node) {
-  const TransformState *node_transform = node->get_transform();
+apply_transform_and_state(qpCullTraverser *trav) {
+  const TransformState *node_transform = node()->get_transform();
   if (!node_transform->is_identity()) {
     _render_transform = _render_transform->compose(node_transform);
     _net_transform = _net_transform->compose(node_transform);
@@ -69,9 +69,9 @@ apply_transform_and_state(qpCullTraverser *trav, PandaNode *node) {
     }
   }
 
-  _state = _state->compose(node->get_state());
+  _state = _state->compose(node()->get_state());
 
-  const RenderEffects *node_effects = node->get_effects();
+  const RenderEffects *node_effects = node()->get_effects();
   const BillboardEffect *billboard = node_effects->get_billboard();
   if (billboard != (const BillboardEffect *)NULL) {
     // Got to apply a billboard transform here.
@@ -94,11 +94,11 @@ apply_transform_and_state(qpCullTraverser *trav, PandaNode *node) {
 //  Description: The private implementation of is_in_view().
 ////////////////////////////////////////////////////////////////////
 bool CullTraverserData::
-is_in_view_impl(PandaNode *node) {
+is_in_view_impl() {
   // By the time we get here, we know we have a viewing frustum.
   nassertr(_view_frustum != (GeometricBoundingVolume *)NULL, true);
 
-  const BoundingVolume &node_volume = node->get_bound();
+  const BoundingVolume &node_volume = node()->get_bound();
   nassertr(node_volume.is_of_type(GeometricBoundingVolume::get_class_type()), false);
   const GeometricBoundingVolume *node_gbv =
     DCAST(GeometricBoundingVolume, &node_volume);
@@ -123,7 +123,7 @@ is_in_view_impl(PandaNode *node) {
     _view_frustum = (GeometricBoundingVolume *)NULL;
 
   } else {
-    if (node->is_final()) {
+    if (node()->is_final()) {
       // The bounding volume is partially, but not completely,
       // within the viewing frustum.  Normally we'd keep testing
       // child bounding volumes as we continue down.  But this node

+ 16 - 5
panda/src/pgraph/cullTraverserData.h

@@ -21,6 +21,7 @@
 
 #include "pandabase.h"
 
+#include "workingNodePath.h"
 #include "renderState.h"
 #include "transformState.h"
 #include "geometricBoundingVolume.h"
@@ -46,18 +47,28 @@ class qpCullTraverser;
 ////////////////////////////////////////////////////////////////////
 class EXPCL_PANDA CullTraverserData {
 public:
-  INLINE CullTraverserData(const TransformState *render_transform,
+  INLINE CullTraverserData(const qpNodePath &start,
+                           const TransformState *render_transform,
                            const TransformState *net_transform,
                            const RenderState *state,
                            GeometricBoundingVolume *view_frustum,
                            GeometricBoundingVolume *guard_band);
+  INLINE CullTraverserData(const CullTraverserData &parent, 
+                           PandaNode *child);
+
+private:
   INLINE CullTraverserData(const CullTraverserData &copy);
-  INLINE void operator = (const CullTraverserData &copy);
+  INLINE void operator = (const CullTraverserData &copy); 
+
+public:
   INLINE ~CullTraverserData();
 
-  INLINE bool is_in_view(PandaNode *node, const DrawMask &camera_mask);
-  void apply_transform_and_state(qpCullTraverser *trav, PandaNode *node);
+  INLINE PandaNode *node() const;
+
+  INLINE bool is_in_view(const DrawMask &camera_mask);
+  void apply_transform_and_state(qpCullTraverser *trav);
 
+  WorkingNodePath _node_path;
   CPT(TransformState) _render_transform;
   CPT(TransformState) _net_transform;
   CPT(RenderState) _state;
@@ -65,7 +76,7 @@ public:
   PT(GeometricBoundingVolume) _guard_band;
 
 private:
-  bool is_in_view_impl(PandaNode *node);
+  bool is_in_view_impl();
   static CPT(RenderState) get_fake_view_frustum_cull_effect();
 };
 

+ 1 - 1
panda/src/pgraph/pandaNode.I

@@ -739,7 +739,7 @@ get_bound() const {
 ////////////////////////////////////////////////////////////////////
 INLINE const BoundingVolume &PandaNode::
 get_internal_bound() const {
-  if (_internal_bound.is_bound_stale()) {
+  if (is_bound_stale() || _internal_bound.is_bound_stale()) {
     ((PandaNode *)this)->recompute_internal_bound();
   }
   return _internal_bound.get_bound();

+ 7 - 3
panda/src/pgraph/pandaNode.cxx

@@ -1157,17 +1157,21 @@ propagate_stale_bound() {
 ////////////////////////////////////////////////////////////////////
 BoundingVolume *PandaNode::
 recompute_bound() {
-  // First, get ourselves a fresh, empty bounding volume.
+  // Get the internal bound before we do anything else, since the
+  // is_bound_stale() flag may also apply to this.
+  const BoundingVolume *internal_bound = &get_internal_bound();
+
+  // Now, get ourselves a fresh, empty bounding volume.  This will
+  // reset the is_bound_stale() flag if it was set.
   BoundingVolume *bound = BoundedObject::recompute_bound();
   nassertr(bound != (BoundingVolume*)NULL, bound);
 
   // Now actually compute the bounding volume by putting it around all
   // of our child bounding volumes.
-
   pvector<const BoundingVolume *> child_volumes;
 
   // It goes around this node's internal bounding volume . . .
-  child_volumes.push_back(&get_internal_bound());
+  child_volumes.push_back(internal_bound);
 
   CDReader cdata(_cycler);
   Down::const_iterator di;

+ 0 - 1
panda/src/pgraph/pgraph_composite2.cxx

@@ -10,7 +10,6 @@
 #include "materialAttrib.cxx"
 #include "qpmodelNode.cxx"
 #include "qpmodelRoot.cxx"
-#include "qpnodePath.cxx"
 #include "qpnodePathCollection.cxx"
 #include "qpnodePathComponent.cxx"
 #include "qpnodePathLerps.cxx"

+ 36 - 39
panda/src/pgraph/qpcullTraverser.cxx

@@ -65,44 +65,39 @@ qpCullTraverser(const qpCullTraverser &copy) :
 //  Description: Begins the traversal from the indicated node.
 ////////////////////////////////////////////////////////////////////
 void qpCullTraverser::
-traverse(PandaNode *root) {
+traverse(qpNodePath &root) {
   nassertv(_cull_handler != (CullHandler *)NULL);
 
-  CullTraverserData data(_render_transform, TransformState::make_identity(),
+  CullTraverserData data(root,
+                         _render_transform, TransformState::make_identity(),
                          _initial_state, _view_frustum, _guard_band);
-  traverse(root, data);
+  traverse(data);
 }
 
 ////////////////////////////////////////////////////////////////////
 //     Function: qpCullTraverser::traverse
 //       Access: Public
-//  Description: Traverses from the indicated node with the given
-//               data, which has not yet been converted into the
-//               node's space.
+//  Description: Traverses from the next node with the given
+//               data, which has been constructed with the node but
+//               has not yet been converted into the node's space.
 ////////////////////////////////////////////////////////////////////
 void qpCullTraverser::
-traverse(PandaNode *node, const CullTraverserData &data) {
+traverse(CullTraverserData &data) {
   // Most nodes will have no transform or state, and will not
   // contain decals or require a special cull callback.  As an
   // optimization, we should tag nodes with these properties as
   // being "fancy", and skip this processing for non-fancy nodes.
+  if (data.is_in_view(_camera_mask)) {
+    data.apply_transform_and_state(this);
 
-  if (node->get_transform()->is_invalid()) {
-    // If the transform is invalid, forget it.
-    return;
-  }
-
-  CullTraverserData next_data(data);
-  if (next_data.is_in_view(node, _camera_mask)) {
-    next_data.apply_transform_and_state(this, node);
-
+    PandaNode *node = data.node();
     if (node->has_cull_callback()) {
-      if (!node->cull_callback(this, next_data)) {
+      if (!node->cull_callback(this, data)) {
         return;
       }
     }
 
-    traverse_below(node, next_data);
+    traverse_below(data);
   }
 }
 
@@ -114,10 +109,11 @@ traverse(PandaNode *node, const CullTraverserData &data) {
 //               node's space.
 ////////////////////////////////////////////////////////////////////
 void qpCullTraverser::
-traverse_below(PandaNode *node, const CullTraverserData &data) {
+traverse_below(const CullTraverserData &data) {
+  PandaNode *node = data.node();
   const RenderEffects *node_effects = node->get_effects();
   if (node_effects->has_decal()) {
-    start_decal(node, data);
+    start_decal(data);
     
   } else {
     if (node->is_geom_node()) {
@@ -139,13 +135,15 @@ traverse_below(PandaNode *node, const CullTraverserData &data) {
     if (node->has_selective_visibility()) {
       int i = node->get_first_visible_child();
       while (i < num_children) {
-        traverse(node->get_child(i), data);
+        CullTraverserData next_data(data, node->get_child(i));
+        traverse(next_data);
         i = node->get_next_visible_child(i);
       }
       
     } else {
       for (int i = 0; i < num_children; i++) {
-        traverse(node->get_child(i), data);
+        CullTraverserData next_data(data, node->get_child(i));
+        traverse(next_data);
       }
     }
   }
@@ -159,7 +157,8 @@ traverse_below(PandaNode *node, const CullTraverserData &data) {
 //               to find all the decal geoms.
 ////////////////////////////////////////////////////////////////////
 void qpCullTraverser::
-start_decal(PandaNode *node, const CullTraverserData &data) {
+start_decal(const CullTraverserData &data) {
+  PandaNode *node = data.node();
   if (!node->is_geom_node()) {
     pgraph_cat.error()
       << "DecalEffect applied to " << *node << ", not a GeomNode.\n";
@@ -179,13 +178,15 @@ start_decal(PandaNode *node, const CullTraverserData &data) {
   if (node->has_selective_visibility()) {
     int i = node->get_first_visible_child();
     while (i < num_children) {
-      decals = r_get_decals(cr.get_child(i), data, decals);
+      CullTraverserData next_data(data, cr.get_child(i));
+      decals = r_get_decals(next_data, decals);
       i = node->get_next_visible_child(i);
     }
     
   } else {
     for (int i = num_children - 1; i >= 0; i--) {
-      decals = r_get_decals(cr.get_child(i), data, decals);
+      CullTraverserData next_data(data, cr.get_child(i));
+      decals = r_get_decals(next_data, decals);
     }
   }
 
@@ -219,30 +220,26 @@ start_decal(PandaNode *node, const CullTraverserData &data) {
 //               they were encountered in the scene graph).
 ////////////////////////////////////////////////////////////////////
 CullableObject *qpCullTraverser::
-r_get_decals(PandaNode *node, const CullTraverserData &data,
-             CullableObject *decals) {
-  if (node->get_transform()->is_invalid()) {
-    // If the transform is invalid, forget it.
-    return decals;
-  }
+r_get_decals(CullTraverserData &data, CullableObject *decals) {
+  if (data.is_in_view(_camera_mask)) {
+    data.apply_transform_and_state(this);
 
-  CullTraverserData next_data(data);
-  if (next_data.is_in_view(node, _camera_mask)) {
-    next_data.apply_transform_and_state(this, node);
+    PandaNode *node = data.node();
 
     // First, visit all of the node's children.
-    PandaNode::Children cr = node->get_children();
-    int num_children = cr.get_num_children();
+    int num_children = node->get_num_children();
     if (node->has_selective_visibility()) {
       int i = node->get_first_visible_child();
       while (i < num_children) {
-        decals = r_get_decals(cr.get_child(i), next_data, decals);
+        CullTraverserData next_data(data, node->get_child(i));
+        decals = r_get_decals(next_data, decals);
         i = node->get_next_visible_child(i);
       }
       
     } else {
       for (int i = num_children - 1; i >= 0; i--) {
-        decals = r_get_decals(cr.get_child(i), next_data, decals);
+        CullTraverserData next_data(data, node->get_child(i));
+        decals = r_get_decals(next_data, decals);
       }
     }
 
@@ -252,7 +249,7 @@ r_get_decals(PandaNode *node, const CullTraverserData &data,
       
       int num_geoms = geom_node->get_num_geoms();
       for (int i = num_geoms - 1; i >= 0; i--) {
-        decals = new CullableObject(next_data, geom_node, i, decals);
+        decals = new CullableObject(data, geom_node, i, decals);
       }
     }
   }

+ 6 - 6
panda/src/pgraph/qpcullTraverser.h

@@ -32,6 +32,7 @@ class PandaNode;
 class CullHandler;
 class CullTraverserData;
 class CullableObject;
+class qpNodePath;
 
 ////////////////////////////////////////////////////////////////////
 //       Class : CullTraverser
@@ -67,14 +68,13 @@ public:
   INLINE void set_cull_handler(CullHandler *cull_handler);
   INLINE CullHandler *get_cull_handler() const;
 
-  void traverse(PandaNode *root);
-  void traverse(PandaNode *node, const CullTraverserData &data);
-  void traverse_below(PandaNode *node, const CullTraverserData &data);
+  void traverse(qpNodePath &root);
+  void traverse(CullTraverserData &data);
+  void traverse_below(const CullTraverserData &data);
 
 private:
-  void start_decal(PandaNode *node, const CullTraverserData &data);
-  CullableObject *r_get_decals(PandaNode *node,
-                               const CullTraverserData &data,
+  void start_decal(const CullTraverserData &data);
+  CullableObject *r_get_decals(CullTraverserData &data,
                                CullableObject *decals);
 
   CPT(RenderState) _initial_state;

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

@@ -235,7 +235,7 @@ write_geoms(ostream &out, int indent_level) const {
   for (gi = cdata->_geoms.begin(); gi != cdata->_geoms.end(); ++gi) {
     const GeomEntry &entry = (*gi);
     indent(out, indent_level + 2) 
-      << *entry._geom << " (" << *entry._state << ")\n";
+      << *entry._geom << " " << *entry._state << "\n";
   }
 }
 
@@ -253,7 +253,7 @@ write_verbose(ostream &out, int indent_level) const {
   for (gi = cdata->_geoms.begin(); gi != cdata->_geoms.end(); ++gi) {
     const GeomEntry &entry = (*gi);
     indent(out, indent_level + 2) 
-      << *entry._geom << " (" << *entry._state << ")\n";
+      << *entry._geom << " " << *entry._state << "\n";
     entry._geom->write_verbose(out, indent_level + 4);
   }
 }

+ 3 - 0
panda/src/pgraph/qpgeomTransformer.I

@@ -63,5 +63,8 @@ operator < (const qpGeomTransformer::SourceTexCoords &other) const {
 ////////////////////////////////////////////////////////////////////
 INLINE bool qpGeomTransformer::SourceColors::
 operator < (const qpGeomTransformer::SourceColors &other) const {
+  if (_colors != other._colors) {
+    return _colors < other._colors;
+  }
   return (_scale.compare_to(other._scale) < 0);
 }

+ 1 - 0
panda/src/pgraph/qpgeomTransformer.cxx

@@ -296,6 +296,7 @@ transform_colors(Geom *geom, const LVecBase4f &scale) {
     // transformed this array?
     SourceColors sc;
     sc._scale = scale;
+    sc._colors = colors;
 
     PTA_Colorf &new_colors = _tcolors[sc];
 

+ 15 - 0
panda/src/pgraph/qplodNode.cxx

@@ -68,6 +68,21 @@ make_copy() const {
   return new qpLODNode(*this);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: qpLODNode::safe_to_combine
+//       Access: Public, Virtual
+//  Description: Returns true if it is generally safe to combine this
+//               particular kind of PandaNode with other kinds of
+//               PandaNodes, adding children or whatever.  For
+//               instance, an LODNode should not be combined with any
+//               other PandaNode, because its set of children is
+//               meaningful.
+////////////////////////////////////////////////////////////////////
+bool qpLODNode::
+safe_to_combine() const {
+  return false;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: qpLODNode::xform
 //       Access: Public, Virtual

+ 1 - 0
panda/src/pgraph/qplodNode.h

@@ -40,6 +40,7 @@ protected:
   INLINE qpLODNode(const qpLODNode &copy);
 public:
   virtual PandaNode *make_copy() const;
+  virtual bool safe_to_combine() const;
   virtual void xform(const LMatrix4f &mat);
   virtual bool has_cull_callback() const;
   virtual bool cull_callback(qpCullTraverser *trav, CullTraverserData &data);

+ 15 - 0
panda/src/pgraph/qpmodelNode.cxx

@@ -65,6 +65,21 @@ safe_to_transform() const {
   return !_preserve_transform;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: qpModelNode::safe_to_combine
+//       Access: Public, Virtual
+//  Description: Returns true if it is generally safe to combine this
+//               particular kind of PandaNode with other kinds of
+//               PandaNodes, adding children or whatever.  For
+//               instance, an LODNode should not be combined with any
+//               other PandaNode, because its set of children is
+//               meaningful.
+////////////////////////////////////////////////////////////////////
+bool qpModelNode::
+safe_to_combine() const {
+  return false;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: qpModelNode::preserve_name
 //       Access: Public, Virtual

+ 1 - 0
panda/src/pgraph/qpmodelNode.h

@@ -47,6 +47,7 @@ public:
 
   virtual bool safe_to_flatten() const;
   virtual bool safe_to_transform() const;
+  virtual bool safe_to_combine() const;
   virtual bool preserve_name() const;
 
 PUBLISHED:

+ 0 - 28
panda/src/pgraph/qpnodePath.I

@@ -597,34 +597,6 @@ get_mat() const {
   return node()->get_transform()->get_mat();
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: qpNodePath::has_color_scale
-//       Access: Published
-//  Description: Returns true if a color scale has been applied
-//               to the referenced node, false otherwise.  It is still
-//               possible that color at this node might have been
-//               scaled by an ancestor node.
-////////////////////////////////////////////////////////////////////
-INLINE bool qpNodePath::
-has_color_scale() const {
-  nassertr(false, false);
-  return false;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: qpNodePath::clear_color_scale
-//       Access: Published
-//  Description: Completely removes any color scale from the
-//               referenced node.  This is preferable to simply
-//               setting the color scale to identity, as it also
-//               removes the overhead associated with having a color
-//               scale at all.
-////////////////////////////////////////////////////////////////////
-INLINE void qpNodePath::
-clear_color_scale() {
-  nassertv(false);
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: qpNodePath::set_color_scale
 //       Access: Published

+ 29 - 0
panda/src/pgraph/qpnodePath.cxx

@@ -721,6 +721,35 @@ set_mat(const LMatrix4f &mat) {
   set_transform(TransformState::make_mat(mat));
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: qpNodePath::has_color_scale
+//       Access: Published
+//  Description: Returns true if a color scale has been applied
+//               to the referenced node, false otherwise.  It is still
+//               possible that color at this node might have been
+//               scaled by an ancestor node.
+////////////////////////////////////////////////////////////////////
+bool qpNodePath::
+has_color_scale() const {
+  nassertr_always(!is_empty(), false);
+  return node()->has_attrib(ColorScaleAttrib::get_class_type());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpNodePath::clear_color_scale
+//       Access: Published
+//  Description: Completely removes any color scale from the
+//               referenced node.  This is preferable to simply
+//               setting the color scale to identity, as it also
+//               removes the overhead associated with having a color
+//               scale at all.
+////////////////////////////////////////////////////////////////////
+void qpNodePath::
+clear_color_scale() {
+  nassertv_always(!is_empty());
+  node()->clear_attrib(ColorScaleAttrib::get_class_type());
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: qpNodePath::set_color_scale
 //       Access: Published

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

@@ -284,9 +284,8 @@ PUBLISHED:
   INLINE bool has_mat() const;
   INLINE const LMatrix4f &get_mat() const;
 
-  INLINE bool has_color_scale() const;
-  INLINE void clear_color_scale();
-
+  bool has_color_scale() const;
+  void clear_color_scale();
   void set_color_scale(const LVecBase4f &scale);
   INLINE void set_color_scale(float sx, float sy, float sz, float sa);
   INLINE void set_sr(float sr);

+ 6 - 2
panda/src/pgraph/qpsceneGraphReducer.cxx

@@ -23,8 +23,10 @@
 #include "colorScaleAttrib.h"
 
 #include "qpgeomNode.h"
+#include "pointerTo.h"
 #include "geom.h"
 #include "indent.h"
+#include "plist.h"
 
 ////////////////////////////////////////////////////////////////////
 //     Function: qpSceneGraphReducer::AccumulatedAttribs::write
@@ -487,7 +489,9 @@ r_flatten(PandaNode *grandparent_node, PandaNode *parent_node,
   // saved above is no longer accurate, so hereafter we must ask the
   // node for its real child list.
   if (combine_siblings && parent_node->get_num_children() >= 2) {
-    num_nodes += flatten_siblings(parent_node);
+    if (parent_node->safe_to_combine()) {
+      num_nodes += flatten_siblings(parent_node);
+    }
   }
 
   if (parent_node->get_num_children() == 1) {
@@ -646,7 +650,7 @@ consider_child(PandaNode *grandparent_node, PandaNode *parent_node,
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: qpSceneGraphReducer::consider_siblings
+//     Function: qpSceneGraphReducer:c:onsider_siblings
 //       Access: Protected, Virtual
 //  Description: Decides whether or not the indicated sibling nodes
 //               should be collapsed into a single node or not.

+ 15 - 0
panda/src/pgraph/qpsequenceNode.cxx

@@ -32,6 +32,21 @@ make_copy() const {
   return new CData(*this);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: qpSequenceNode::safe_to_combine
+//       Access: Public, Virtual
+//  Description: Returns true if it is generally safe to combine this
+//               particular kind of PandaNode with other kinds of
+//               PandaNodes, adding children or whatever.  For
+//               instance, an LODNode should not be combined with any
+//               other PandaNode, because its set of children is
+//               meaningful.
+////////////////////////////////////////////////////////////////////
+bool qpSequenceNode::
+safe_to_combine() const {
+  return false;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: qpSequenceNode::CData::write_datagram
 //       Access: Public, Virtual

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

@@ -37,6 +37,7 @@ public:
   qpSequenceNode(const qpSequenceNode &copy);
 
   virtual PandaNode *make_copy() const;
+  virtual bool safe_to_combine() const;
 
   virtual bool has_cull_callback() const;
   virtual bool cull_callback(qpCullTraverser *trav, CullTraverserData &data);

+ 10 - 0
panda/src/pgraph/workingNodePath.I

@@ -70,3 +70,13 @@ get_node_path() const {
   nassertr(result._head != (qpNodePathComponent *)NULL, qpNodePath::fail());
   return result;
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: WorkingNodePath::node
+//       Access: Public
+//  Description: Returns the node traversed to so far.
+////////////////////////////////////////////////////////////////////
+INLINE PandaNode *WorkingNodePath::
+node() const {
+  return _node;
+}

+ 1 - 0
panda/src/pgraph/workingNodePath.h

@@ -53,6 +53,7 @@ public:
   INLINE ~WorkingNodePath();
 
   INLINE qpNodePath get_node_path() const;
+  INLINE PandaNode *node() const;
 
 private:
   PT(qpNodePathComponent) r_get_node_path() const;

+ 7 - 7
panda/src/pgui/pgFrameStyle.cxx

@@ -23,6 +23,7 @@
 #include "pandaNode.h"
 #include "transparencyAttrib.h"
 #include "pointerTo.h"
+#include "qpnodePath.h"
 
 #include "geomNode.h"
 #include "transparencyProperty.h"
@@ -168,16 +169,16 @@ generate_into(Node *node, const LVecBase4f &frame) {
 //               indicated size, and parents it to the indicated node,
 //               with a scene graph sort order of -1.
 //
-//               The return value is the generated node, if any, or
-//               NULL if nothing is generated.
+//               The return value is the generated NodePath, if any,
+//               or an empty NodePath if nothing is generated.
 ////////////////////////////////////////////////////////////////////
-PandaNode *PGFrameStyle::
-generate_into(PandaNode *node, const LVecBase4f &frame) {
+qpNodePath PGFrameStyle::
+generate_into(const qpNodePath &parent, const LVecBase4f &frame) {
   PT(PandaNode) new_node;
 
   switch (_type) {
   case T_none:
-    return (PandaNode *)NULL;
+    return qpNodePath();
 
   case T_flat:
     new_node = qpgenerate_flat_geom(frame);
@@ -209,8 +210,7 @@ generate_into(PandaNode *node, const LVecBase4f &frame) {
   }
 
   // Adding the node to the parent keeps the reference count.
-  node->add_child(new_node);
-  return new_node;
+  return parent.attach_new_node(new_node);
 }
 
 ////////////////////////////////////////////////////////////////////

+ 2 - 1
panda/src/pgui/pgFrameStyle.h

@@ -27,6 +27,7 @@
 class NodeRelation;
 class Node;
 class PandaNode;
+class qpNodePath;
 
 ////////////////////////////////////////////////////////////////////
 //       Class : PGFrameStyle
@@ -65,7 +66,7 @@ PUBLISHED:
 public:
   bool xform(const LMatrix4f &mat);
   NodeRelation *generate_into(Node *node, const LVecBase4f &frame);
-  PandaNode *generate_into(PandaNode *node, const LVecBase4f &frame);
+  qpNodePath generate_into(const qpNodePath &parent, const LVecBase4f &frame);
 
 private:
   PT_Node generate_flat_geom(const LVecBase4f &frame);

+ 4 - 4
panda/src/pgui/qppgButton.cxx

@@ -186,10 +186,10 @@ setup(const string &label) {
   PT(PandaNode) rollover = new PandaNode("rollover");
   PT(PandaNode) inactive = new PandaNode("inactive");
 
-  get_state_def(S_ready)->add_child(ready);
-  get_state_def(S_depressed)->add_child(depressed);
-  get_state_def(S_rollover)->add_child(rollover);
-  get_state_def(S_inactive)->add_child(inactive);
+  get_state_def(S_ready).attach_new_node(ready);
+  get_state_def(S_depressed).attach_new_node(depressed);
+  get_state_def(S_rollover).attach_new_node(rollover);
+  get_state_def(S_inactive).attach_new_node(inactive);
 
   ready->add_child(geom);
   depressed->add_child(geom);

+ 3 - 2
panda/src/pgui/qppgEntry.I

@@ -191,7 +191,7 @@ get_blink_rate() const {
 //               the cursor.  You can attach suitable cursor geometry
 //               to this node.
 ////////////////////////////////////////////////////////////////////
-INLINE PandaNode *qpPGEntry:: 
+INLINE const qpNodePath &qpPGEntry:: 
 get_cursor_def() {
   return _cursor_def;
 }
@@ -204,7 +204,8 @@ get_cursor_def() {
 ////////////////////////////////////////////////////////////////////
 INLINE void qpPGEntry:: 
 clear_cursor_def() {
-  get_cursor_def()->remove_all_children();
+  _cursor_def.remove_node();
+  _cursor_def = _text_render_root.attach_new_node("cursor");
 }
 
 ////////////////////////////////////////////////////////////////////

+ 14 - 17
panda/src/pgui/qppgEntry.cxx

@@ -20,6 +20,7 @@
 #include "pgMouseWatcherParameter.h"
 
 #include "qpcullTraverser.h"
+#include "cullTraverserData.h"
 #include "throw_event.h"
 #include "transformState.h"
 #include "mouseWatcherParameter.h"
@@ -50,10 +51,8 @@ qpPGEntry(const string &name) :
   _blink_start = 0.0f;
   _blink_rate = 1.0f;
 
-  _text_render_root = new PandaNode("text_root");
-  _current_text_node = (PandaNode *)NULL;
-  _cursor_def = new PandaNode("cursor");
-  _text_render_root->add_child(_cursor_def);
+  _text_render_root = qpNodePath("text_root");
+  _cursor_def = _text_render_root.attach_new_node("cursor");
   _cursor_visible = true;
 
   _cursor_keys_active = true;
@@ -149,8 +148,8 @@ cull_callback(qpCullTraverser *trav, CullTraverserData &data) {
   update_cursor();
 
   // Now render the text.
-  nassertr(_text_render_root != (PandaNode *)NULL, true);
-  trav->traverse(_text_render_root, data);
+  CullTraverserData next_data(data, _text_render_root.node());
+  trav->traverse(next_data);
 
   // Now continue to render everything else below this node.
   return true;
@@ -596,12 +595,12 @@ setup(float width, int num_lines) {
   ls.set_color(0.0f, 0.0f, 0.0f, 1.0f);
   ls.move_to(0.0f, 0.0f, -0.15f * line_height);
   ls.draw_to(0.0f, 0.0f, 0.85f * line_height);
-  get_cursor_def()->add_child(ls.create());
+  get_cursor_def().attach_new_node(ls.create());
   
   /*
   // An underscore cursor would work too.
   text_node->set_text("_");
-  get_cursor_def()->add_child(text_node->generate());
+  get_cursor_def().attach_new_node(text_node->generate());
   */
 }
 
@@ -782,11 +781,11 @@ update_text() {
       line._left = _last_text_def->get_left();
     }
 
-    if (_current_text_node != (PandaNode *)NULL) {
-      _text_render_root->remove_child(_current_text_node);
+    if (!_current_text.is_empty()) {
+      _current_text.remove_node();
     }
-    _current_text_node = _last_text_def->generate();
-    _text_render_root->add_child(_current_text_node);
+    _current_text = 
+      _text_render_root.attach_new_node(_last_text_def->generate());
     _text_geom_stale = false;
     _cursor_stale = true;
   }
@@ -824,7 +823,7 @@ update_cursor() {
     float line_height = _last_text_def->get_line_height();
 
     LVecBase3f trans(_ww_lines[row]._left + width, 0.0f, -line_height * row);
-    _cursor_def->set_transform(TransformState::make_pos(trans));
+    _cursor_def.set_pos(trans);
 
     _cursor_stale = false;
   }
@@ -851,11 +850,9 @@ void qpPGEntry::
 show_hide_cursor(bool visible) {
   if (visible != _cursor_visible) {
     if (visible) {
-      // Reveal the cursor.
-      _cursor_def->set_draw_mask(DrawMask::all_on());
+      _cursor_def.show();
     } else {
-      // Hide the cursor.
-      _cursor_def->set_draw_mask(DrawMask::all_off());
+      _cursor_def.hide();
     }
     _cursor_visible = visible;
   }

+ 5 - 6
panda/src/pgui/qppgEntry.h

@@ -88,7 +88,7 @@ PUBLISHED:
   INLINE void set_blink_rate(float blink_rate);
   INLINE float get_blink_rate() const;
 
-  INLINE PandaNode *get_cursor_def();
+  INLINE const qpNodePath &get_cursor_def();
   INLINE void clear_cursor_def();
 
   INLINE void set_cursor_keys_active(bool flag);
@@ -139,13 +139,12 @@ private:
   typedef pvector< PT(qpTextNode) > TextDefs;
   TextDefs _text_defs;
 
-  // This node is the root of the subgraph that renders both the text
-  // and the cursor.
-  PT(PandaNode) _text_render_root;
+  // This is the subgraph that renders both the text and the cursor.
+  qpNodePath _text_render_root;
 
   // This is the node for rendering the actual text that is parented
   // to the above node when the text is generated.
-  PT(PandaNode) _current_text_node;
+  qpNodePath _current_text;
   qpTextNode *_last_text_def;
   bool _text_geom_stale;
 
@@ -163,7 +162,7 @@ private:
   // This is the node that represents the cursor geometry.  It is also
   // attached to the above node, and is transformed around and/or
   // hidden according to the cursor's position and blink state.
-  PandaNode *_cursor_def;
+  qpNodePath _cursor_def;
 
   double _blink_start;
   double _blink_rate;

+ 34 - 52
panda/src/pgui/qppgItem.cxx

@@ -28,6 +28,7 @@
 #include "string_utils.h"
 #include "qpnodePath.h"
 #include "qpcullTraverser.h"
+#include "cullTraverserData.h"
 
 #ifdef HAVE_AUDIO
 #include "audioSound.h"
@@ -118,15 +119,13 @@ xform(const LMatrix4f &mat) {
   // Transform the individual states and their frame styles.
   StateDefs::iterator di;
   for (di = _state_defs.begin(); di != _state_defs.end(); ++di) {
-    PandaNode *node = (*di)._node;
-    if (node != (PandaNode *)NULL) {
-      // Apply the matrix to the previous transform.
-      node->set_transform(node->get_transform()->compose(TransformState::make_mat(mat)));
-
-      // Now flatten the transform into the subgraph.
-      qpSceneGraphReducer gr;
-      gr.apply_attribs(node);
-    }
+    qpNodePath &root = (*di)._root;
+    // Apply the matrix to the previous transform.
+    root.set_transform(root.get_transform()->compose(TransformState::make_mat(mat)));
+
+    // Now flatten the transform into the subgraph.
+    qpSceneGraphReducer gr;
+    gr.apply_attribs(root.node());
 
     // Transform the frame style too.
     if ((*di)._frame_style.xform(mat)) {
@@ -193,8 +192,9 @@ cull_callback(qpCullTraverser *trav, CullTraverserData &data) {
   if (has_state_def(get_state())) {
     // This item has a current state definition that we should use
     // to render the item.
-    PandaNode *def = get_state_def(get_state());
-    trav->traverse(def, data);
+    qpNodePath &root = get_state_def(get_state());
+    CullTraverserData next_data(data, root.node());
+    trav->traverse(next_data);
   }
 
   // Now continue to render everything else below this node.
@@ -223,9 +223,9 @@ recompute_internal_bound() {
   // get_state_def() on each one, to ensure that the frames are
   // updated correctly before we measure their bounding volumes.
   for (int i = 0; i < (int)_state_defs.size(); i++) {
-    PandaNode *node = get_state_def(i);
-    if (node != (PandaNode *)NULL) {
-      child_volumes.push_back(&node->get_bound());
+    qpNodePath &root = get_state_def(i);
+    if (!root.is_empty()) {
+      child_volumes.push_back(&root.node()->get_bound());
     }
   }
 
@@ -582,7 +582,7 @@ has_state_def(int state) const {
   if (state < 0 || state >= (int)_state_defs.size()) {
     return false;
   }
-  return (_state_defs[state]._node != (PandaNode *)NULL);
+  return (!_state_defs[state]._root.is_empty());
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -598,12 +598,8 @@ clear_state_def(int state) {
     return;
   }
 
-  PandaNode *node = _state_defs[state]._node;
-  if (node != (PandaNode *)NULL) {
-    node->remove_all_children();
-  }
-
-  _state_defs[state]._frame_node = (PandaNode *)NULL;
+  _state_defs[state]._root = qpNodePath();
+  _state_defs[state]._frame = qpNodePath();
   _state_defs[state]._frame_stale = true;
 
   mark_bound_stale();
@@ -617,14 +613,14 @@ clear_state_def(int state) {
 //               indicated state.  The first time this is called for a
 //               particular state index, it may create the Node.
 ////////////////////////////////////////////////////////////////////
-PandaNode *qpPGItem::
+qpNodePath &qpPGItem::
 get_state_def(int state) {
-  nassertr(state >= 0 && state < 1000, (PandaNode *)NULL);  // Sanity check.
+  nassertr(state >= 0 && state < 1000, get_state_def(0));  // Sanity check.
   slot_state_def(state);
 
-  if (_state_defs[state]._node == (PandaNode *)NULL) {
+  if (_state_defs[state]._root.is_empty()) {
     // Create a new node.
-    _state_defs[state]._node = new PandaNode("state_" + format_string(state));
+    _state_defs[state]._root = qpNodePath("state_" + format_string(state));
     _state_defs[state]._frame_stale = true;
   }
 
@@ -632,7 +628,7 @@ get_state_def(int state) {
     update_frame(state);
   }
 
-  return _state_defs[state]._node;
+  return _state_defs[state]._root;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -641,16 +637,16 @@ get_state_def(int state) {
 //  Description: Parents an instance of the bottom node of the
 //               indicated NodePath to the indicated state index.
 ////////////////////////////////////////////////////////////////////
-void qpPGItem::
+qpNodePath qpPGItem::
 instance_to_state_def(int state, const qpNodePath &path) {
   if (path.is_empty()) {
-    // If the path is empty, quietly do nothing.
-    return;
+    // If the source is empty, quietly do nothing.
+    return qpNodePath();
   }
 
-  get_state_def(state)->add_child(path.node());
-
   mark_bound_stale();
+
+  return path.instance_to(get_state_def(state));
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -677,8 +673,8 @@ void qpPGItem::
 set_frame_style(int state, const PGFrameStyle &style) {
   // Get the state def node, mainly to ensure that this state is
   // slotted and listed as having been defined.
-  PandaNode *def = get_state_def(state);
-  nassertv(def != (PandaNode *)NULL);
+  qpNodePath &root = get_state_def(state);
+  nassertv(!root.is_empty());
 
   _state_defs[state]._frame_style = style;
   _state_defs[state]._frame_stale = true;
@@ -799,13 +795,7 @@ void qpPGItem::
 update_frame(int state) {
   // First, remove the old frame geometry, if any.
   if (state >= 0 && state < (int)_state_defs.size()) {
-    PandaNode *old_frame = _state_defs[state]._frame_node;
-    if (old_frame != (PandaNode *)NULL) {
-      PandaNode *node = _state_defs[state]._node;
-      nassertv(node != (PandaNode *)NULL);
-      node->remove_child(old_frame);
-      _state_defs[state]._frame_node = (PandaNode *)NULL;
-    }
+    _state_defs[state]._frame.remove_node();
   }
 
   // We must turn off the stale flag first, before we call
@@ -815,10 +805,9 @@ update_frame(int state) {
 
   // Now create new frame geometry.
   if (has_frame()) {
-    PandaNode *node = get_state_def(state);
-    nassertv(node != (PandaNode *)NULL);
-    _state_defs[state]._frame_node = 
-      _state_defs[state]._frame_style.generate_into(node, _frame);
+    qpNodePath &root = get_state_def(state);
+    _state_defs[state]._frame = 
+      _state_defs[state]._frame_style.generate_into(root, _frame);
   }
 }
 
@@ -834,14 +823,7 @@ mark_frames_stale() {
   StateDefs::iterator di;
   for (di = _state_defs.begin(); di != _state_defs.end(); ++di) {
     // Remove the old frame, if any.
-    PandaNode *old_frame = (*di)._frame_node;
-    if (old_frame != (PandaNode *)NULL) {
-      PandaNode *node = (*di)._node;
-      nassertv(node != (PandaNode *)NULL);
-      node->remove_child(old_frame);
-      (*di)._frame_node = (PandaNode *)NULL;
-    }
-
+    (*di)._frame.remove_node();
     (*di)._frame_stale = true;
   }
   mark_bound_stale();

+ 5 - 5
panda/src/pgui/qppgItem.h

@@ -25,6 +25,7 @@
 #include "pgFrameStyle.h"
 
 #include "pandaNode.h"
+#include "qpnodePath.h"
 #include "luse.h"
 #include "pointerTo.h"
 #include "qptextNode.h"
@@ -34,7 +35,6 @@
 class PGTop;
 class MouseWatcherParameter;
 class AudioSound;
-class qpNodePath;
 
 ////////////////////////////////////////////////////////////////////
 //       Class : qpPGItem
@@ -108,8 +108,8 @@ PUBLISHED:
   int get_num_state_defs() const;
   void clear_state_def(int state);
   bool has_state_def(int state) const;
-  PandaNode *get_state_def(int state);
-  void instance_to_state_def(int state, const qpNodePath &chain);
+  qpNodePath &get_state_def(int state);
+  qpNodePath instance_to_state_def(int state, const qpNodePath &path);
 
   PGFrameStyle get_frame_style(int state);
   void set_frame_style(int state, const PGFrameStyle &style);
@@ -171,9 +171,9 @@ private:
 
   class StateDef {
   public:
-    PT(PandaNode) _node;
+    qpNodePath _root;
     PGFrameStyle _frame_style;
-    PT(PandaNode) _frame_node;
+    qpNodePath _frame;
     bool _frame_stale;
   };
   typedef pvector<StateDef> StateDefs;

+ 1 - 1
panda/src/pgui/qppgTop.cxx

@@ -118,7 +118,7 @@ cull_callback(qpCullTraverser *trav, CullTraverserData &data) {
   // PGTop node, for the convenience of PGItems to register themselves
   // as they are drawn.
   PGCullTraverser pg_trav(this, trav);
-  pg_trav.traverse_below(this, data);
+  pg_trav.traverse_below(data);
 
   // We've taken care of the traversal, thank you.
   return false;

+ 4 - 10
panda/src/pgui/qppgWaitBar.cxx

@@ -34,7 +34,6 @@ qpPGWaitBar(const string &name) : qpPGItem(name)
   _range = 100.0;
   _value = 0.0;
   _bar_state = -1;
-  _bar_node = (PandaNode *)NULL;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -58,7 +57,6 @@ qpPGWaitBar(const qpPGWaitBar &copy) :
   _value(copy._value)
 {
   _bar_state = -1;
-  _bar_node = (PandaNode *)NULL;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -155,16 +153,12 @@ update() {
   }
 
   // Remove the old bar geometry, if any.
-  if (_bar_node != (PandaNode *)NULL) {
-    PandaNode *parent_node = _bar_node->get_parent(0);
-    parent_node->remove_child(_bar_node);
-    _bar_node = (PandaNode *)NULL;
-  }
+  _bar.remove_node();
 
   // Now create new bar geometry.
   if (_value != 0.0 && _range != 0.0) {
-    PandaNode *node = get_state_def(state);
-    nassertv(node != (PandaNode *)NULL);
+    qpNodePath &root = get_state_def(state);
+    nassertv(!root.is_empty());
 
     PGFrameStyle style = get_frame_style(state);
     const LVecBase4f &frame = get_frame();
@@ -181,7 +175,7 @@ update() {
     frac = max(min(frac, 1.0f), 0.0f);
     bar_frame[1] = bar_frame[0] + frac * (bar_frame[1] - bar_frame[0]);
     
-    _bar_node = _bar_style.generate_into(node, bar_frame);
+    _bar = _bar_style.generate_into(root, bar_frame);
   }
 
   // Indicate that the bar is current for this state.

+ 1 - 1
panda/src/pgui/qppgWaitBar.h

@@ -63,7 +63,7 @@ private:
   float _range, _value;
   int _bar_state;
   PGFrameStyle _bar_style;
-  PT(PandaNode) _bar_node;
+  qpNodePath _bar;
 
 public:
   static TypeHandle get_class_type() {