Browse Source

*** empty log message ***

David Rose 25 years ago
parent
commit
faa13d10e5
81 changed files with 1318 additions and 369 deletions
  1. 22 9
      direct/src/showbase/showBase.cxx
  2. 4 7
      direct/src/showbase/showBase.h
  3. 1 1
      panda/src/collide/collisionRay.I
  4. 2 2
      panda/src/collide/collisionRay.cxx
  5. 2 2
      panda/src/collide/collisionRay.h
  6. 4 0
      panda/src/cull/config_cull.cxx
  7. 2 0
      panda/src/cull/config_cull.h
  8. 3 0
      panda/src/cull/cullLevelState.h
  9. 9 4
      panda/src/cull/cullState.I
  10. 13 5
      panda/src/cull/cullState.cxx
  11. 8 6
      panda/src/cull/cullState.h
  12. 33 0
      panda/src/cull/cullStateLookup.cxx
  13. 8 1
      panda/src/cull/cullStateSubtree.cxx
  14. 49 11
      panda/src/cull/cullTraverser.I
  15. 71 21
      panda/src/cull/cullTraverser.cxx
  16. 13 5
      panda/src/cull/cullTraverser.h
  17. 7 8
      panda/src/cull/geomBinBackToFront.I
  18. 10 5
      panda/src/cull/geomBinBackToFront.cxx
  19. 2 2
      panda/src/cull/geomBinBackToFront.h
  20. 7 8
      panda/src/cull/geomBinFixed.I
  21. 5 7
      panda/src/cull/geomBinFixed.cxx
  22. 2 2
      panda/src/cull/geomBinFixed.h
  23. 9 11
      panda/src/cull/geomBinUnsorted.cxx
  24. 1 1
      panda/src/display/graphicsStateGuardian.I
  25. 4 4
      panda/src/display/graphicsStateGuardian.h
  26. 3 3
      panda/src/dxgsg/dxGraphicsStateGuardian.cxx
  27. 2 2
      panda/src/dxgsg/dxGraphicsStateGuardian.h
  28. 5 5
      panda/src/effects/lensFlareNode.cxx
  29. 3 2
      panda/src/effects/lensFlareNode.h
  30. 11 3
      panda/src/express/pointerTo.I
  31. 10 2
      panda/src/express/referenceCount.I
  32. 0 32
      panda/src/express/referenceCount.cxx
  33. 1 6
      panda/src/express/referenceCount.h
  34. 3 3
      panda/src/glgsg/glGraphicsStateGuardian.cxx
  35. 2 2
      panda/src/glgsg/glGraphicsStateGuardian.h
  36. 13 3
      panda/src/graph/Sources.pp
  37. 219 0
      panda/src/graph/arcChain.I
  38. 79 0
      panda/src/graph/arcChain.cxx
  39. 105 0
      panda/src/graph/arcChain.h
  40. 8 1
      panda/src/graph/config_graph.cxx
  41. 1 0
      panda/src/graph/config_graph.h
  42. 11 2
      panda/src/graph/matrixTransition.I
  43. 1 1
      panda/src/graph/node.cxx
  44. 2 2
      panda/src/graph/node.h
  45. 14 0
      panda/src/graph/nodeRelation.I
  46. 6 6
      panda/src/graph/nodeRelation.cxx
  47. 9 1
      panda/src/graph/nodeRelation.h
  48. 1 1
      panda/src/graph/nodeTransition.cxx
  49. 2 2
      panda/src/graph/nodeTransition.h
  50. 176 32
      panda/src/graph/wrt.I
  51. 14 8
      panda/src/graph/wrt.h
  52. 32 25
      panda/src/gui/guiManager.cxx
  53. 2 1
      panda/src/gui/guiManager.h
  54. 1 1
      panda/src/light/spotlight.cxx
  55. 49 12
      panda/src/putil/updateSeq.I
  56. 8 4
      panda/src/putil/updateSeq.h
  57. 3 3
      panda/src/ribgsg/ribGraphicsStateGuardian.cxx
  58. 2 2
      panda/src/ribgsg/ribGraphicsStateGuardian.h
  59. 5 3
      panda/src/sgattrib/billboardTransition.cxx
  60. 1 1
      panda/src/sgattrib/billboardTransition.h
  61. 3 2
      panda/src/sgattrib/drawBoundsTransition.cxx
  62. 1 1
      panda/src/sgattrib/drawBoundsTransition.h
  63. 1 1
      panda/src/sgattrib/pruneTransition.cxx
  64. 1 1
      panda/src/sgattrib/pruneTransition.h
  65. 30 13
      panda/src/sgraph/projectionNode.cxx
  66. 5 5
      panda/src/sgraph/projectionNode.h
  67. 92 3
      panda/src/sgraph/renderTraverser.I
  68. 22 1
      panda/src/sgraph/renderTraverser.h
  69. 31 5
      panda/src/sgraphutil/directRenderTraverser.cxx
  70. 2 1
      panda/src/sgraphutil/directRenderTraverser.h
  71. 5 8
      panda/src/sgraphutil/frustumCullTraverser.I
  72. 3 3
      panda/src/sgraphutil/frustumCullTraverser.h
  73. 2 2
      panda/src/shader/shaderTransition.cxx
  74. 1 1
      panda/src/shader/shaderTransition.h
  75. 6 6
      panda/src/switchnode/LODNode.cxx
  76. 1 1
      panda/src/switchnode/LODNode.h
  77. 4 5
      panda/src/switchnode/sequenceNode.cxx
  78. 1 1
      panda/src/switchnode/sequenceNode.h
  79. 1 0
      panda/src/testbed/Sources.pp
  80. 4 4
      panda/src/testbed/chat_test.cxx
  81. 7 12
      panda/src/testbed/demo.cxx

+ 22 - 9
direct/src/showbase/showBase.cxx

@@ -66,6 +66,7 @@ public:
     _data_root(data_root),
     _initial_state(initial_state),
     _app_traverser(RenderRelation::get_class_type()) { }
+  virtual ~WindowCallback() { }
   
   virtual void draw(bool) {
     _app_traverser.traverse(_render);
@@ -156,7 +157,7 @@ PT(GraphicsWindow) make_graphics_window(GraphicsPipe *pipe,
 // can contain 2-d geometry and will be rendered on top of the
 // existing 3-d window.  Returns the top node of the scene graph.
 NodePath
-setup_panda_2d(PT(GraphicsWindow) win) {
+setup_panda_2d(GraphicsWindow *win) {
   PT(Node) render2d_top;
   
   render2d_top = new NamedNode("render2d_top");
@@ -165,6 +166,12 @@ setup_panda_2d(PT(GraphicsWindow) win) {
 
   // Set up some overrides to turn off certain properties which we
   // probably won't need for 2-d objects.
+
+  // It's particularly important to turn off the depth test, since
+  // we'll be keeping the same depth buffer already filled by the
+  // previously-drawn 3-d scene--we don't want to pay for a clear
+  // operation, but we also don't want to collide with that depth
+  // buffer.
   render2d_arc->set_transition(new DepthTestTransition(DepthTestProperty::M_none), 1);
   render2d_arc->set_transition(new DepthWriteTransition(DepthWriteTransition::off()), 1);
   render2d_arc->set_transition(new LightTransition(LightTransition::all_off()), 1);
@@ -174,24 +181,30 @@ setup_panda_2d(PT(GraphicsWindow) win) {
   // Create a 2-d camera.
   Camera *cam2d = new Camera("cam2d");
   new RenderRelation(render2d, cam2d);
-  cam2d->set_scene(render2d_top);
 
   Frustumf frustum2d;
   frustum2d.make_ortho(-1000,1000);
   cam2d->set_projection(OrthoProjection(frustum2d));
 
-  // Now create a new layer.
+  add_render_layer(win, render2d_top, cam2d);
+
+  return NodePath(render2d_arc);
+}
+
+// Create an auxiliary scene graph starting at the indicated node,
+// layered on top of the previously-created layers.
+void
+add_render_layer(GraphicsWindow *win, Node *render_top, Camera *camera) {
   GraphicsChannel *chan = win->get_channel(0);
-  nassertr(chan != (GraphicsChannel *)NULL, NodePath());
+  nassertv(chan != (GraphicsChannel *)NULL);
 
   GraphicsLayer *layer = chan->make_layer();
-  nassertr(layer != (GraphicsLayer *)NULL, NodePath());
+  nassertv(layer != (GraphicsLayer *)NULL);
 
   DisplayRegion *dr = layer->make_display_region();
-  nassertr(dr != (DisplayRegion *)NULL, NodePath());
-  dr->set_camera(cam2d);
-
-  return NodePath(render2d_arc);
+  nassertv(dr != (DisplayRegion *)NULL);
+  camera->set_scene(render_top);
+  dr->set_camera(camera);
 }
 
 // Enable the collision traversal using a particular traverser.

+ 4 - 7
direct/src/showbase/showBase.h

@@ -17,16 +17,11 @@
 #include <nodePath.h>
 #include <dconfig.h>
 
-#define testint 1
-#define testfloat 1.2345
-#define testcstring "testcstring"
-#include <string>
-#define teststring string("teststring")
-
 ConfigureDecl(config_showbase, EXPCL_DIRECT, EXPTP_DIRECT);
 typedef Config::Config<ConfigureGetConfig_config_showbase> ConfigShowbase;
 
 class CollisionTraverser;
+class Camera;
 
 BEGIN_PUBLISH
 
@@ -39,7 +34,9 @@ EXPCL_DIRECT PT(GraphicsWindow)
 		       NodeAttributes &initial_state
 		       );	
 
-EXPCL_DIRECT NodePath setup_panda_2d(PT(GraphicsWindow) win);
+EXPCL_DIRECT NodePath setup_panda_2d(GraphicsWindow *win);
+EXPCL_DIRECT void add_render_layer(GraphicsWindow *win, Node *render_top,
+				   Camera *camera);
 
 EXPCL_DIRECT void set_collision_traverser(CollisionTraverser *traverser);
 EXPCL_DIRECT CollisionTraverser *get_collision_traverser();

+ 1 - 1
panda/src/collide/collisionRay.I

@@ -128,6 +128,6 @@ get_direction() const {
 //               the screen given a camera and a mouse location.
 ////////////////////////////////////////////////////////////////////
 INLINE bool CollisionRay::
-set_projection(const ProjectionNode *camera, float px, float py) {
+set_projection(ProjectionNode *camera, float px, float py) {
   return set_projection(camera, LPoint2f(px, py));
 }

+ 2 - 2
panda/src/collide/collisionRay.cxx

@@ -75,8 +75,8 @@ output(ostream &out) const {
 //               otherwise.
 ////////////////////////////////////////////////////////////////////
 bool CollisionRay::
-set_projection(const ProjectionNode *camera, const LPoint2f &point) {
-  const Projection *proj = camera->get_projection();
+set_projection(ProjectionNode *camera, const LPoint2f &point) {
+  Projection *proj = camera->get_projection();
 
   bool success = true;
   if (!proj->extrude(point, _origin, _direction)) {

+ 2 - 2
panda/src/collide/collisionRay.h

@@ -49,8 +49,8 @@ PUBLISHED:
   INLINE void set_direction(float x, float y, float z);
   INLINE const LVector3f &get_direction() const;
 
-  bool set_projection(const ProjectionNode *camera, const LPoint2f &point);
-  INLINE bool set_projection(const ProjectionNode *camera, float px, float py);
+  bool set_projection(ProjectionNode *camera, const LPoint2f &point);
+  INLINE bool set_projection(ProjectionNode *camera, float px, float py);
 
 protected:
   virtual void recompute_bound();

+ 4 - 0
panda/src/cull/config_cull.cxx

@@ -33,3 +33,7 @@ ConfigureFn(config_cull) {
   DirectRenderTransition::init_type();
 }
 
+// Set this true to force all of the caching to blow itself away every
+// frame.  Normally you would only do this during testing.
+const bool cull_force_update = config_cull.GetBool("cull-force-update", false);
+

+ 2 - 0
panda/src/cull/config_cull.h

@@ -11,4 +11,6 @@
 
 NotifyCategoryDecl(cull, EXPCL_PANDA, EXPTP_PANDA);
 
+extern const bool cull_force_update;
+
 #endif

+ 3 - 0
panda/src/cull/cullLevelState.h

@@ -10,6 +10,8 @@
 
 #include "cullStateLookup.h"
 
+#include <updateSeq.h>
+
 ////////////////////////////////////////////////////////////////////
 //       Class : CullLevelState
 // Description : This is the state information the
@@ -19,6 +21,7 @@
 class EXPCL_PANDA CullLevelState {
 public:
   CullStateLookup *_lookup;
+  UpdateSeq _now;
 };
 
 #endif

+ 9 - 4
panda/src/cull/cullState.I

@@ -79,8 +79,13 @@ clear_current_nodes() {
 //               be visible this frame.
 ////////////////////////////////////////////////////////////////////
 INLINE void CullState::
-record_current_geom_node(const PT(GeomNode) &node) {
-  _current_geom_nodes.push_back(node);
+record_current_geom_node(const ArcChain &arc_chain) {
+  if (cull_cat.is_spam()) {
+    cull_cat.spam()
+      << "Recording geom node " << arc_chain << " with state " << (void *)this
+      << "\n";
+  }
+  _current_geom_nodes.push_back(arc_chain);
   _empty_frames_count = 0;
 }
 
@@ -92,8 +97,8 @@ record_current_geom_node(const PT(GeomNode) &node) {
 //               visible this frame.
 ////////////////////////////////////////////////////////////////////
 INLINE void CullState::
-record_current_direct_node(const PT_Node &node) {
-  _current_direct_nodes.push_back(node);
+record_current_direct_node(const ArcChain &arc_chain) {
+  _current_direct_nodes.push_back(arc_chain);
   _empty_frames_count = 0;
 }
 

+ 13 - 5
panda/src/cull/cullState.cxx

@@ -4,6 +4,7 @@
 ////////////////////////////////////////////////////////////////////
 
 #include "cullState.h"
+#include "config_cull.h"
 
 #include <indent.h>
 #include <graphicsStateGuardian.h>
@@ -26,7 +27,14 @@ check_currency(Node *node, const AllTransitionsWrapper &,
   nassertr(vi != _verified.end(), false);
 
   UpdateSeq verified_stamp = (*vi).second;
-  if (verified_stamp == now) {
+
+  if (cull_cat.is_spam()) {
+    cull_cat.spam()
+      << "Checking currency for " << *node << ", verified_stamp = "
+      << verified_stamp << " now = " << now << "\n";
+  }
+
+  if (verified_stamp == now && !verified_stamp.is_fresh()) {
     return true;
   }
 
@@ -63,10 +71,10 @@ write(ostream &out, int indent_level) const {
   if (!_current_geom_nodes.empty()) {
     CurrentGeomNodes::const_iterator ci;
     ci = _current_geom_nodes.begin();
-    out << " (" << *(*ci);
+    out << " (" << (*ci);
     ++ci;
     while (ci != _current_geom_nodes.end()) {
-      out << ", " << *(*ci);
+      out << ", " << (*ci);
       ++ci;
     }
     out << ")";
@@ -77,10 +85,10 @@ write(ostream &out, int indent_level) const {
   if (!_current_direct_nodes.empty()) {
     CurrentDirectNodes::const_iterator ci;
     ci = _current_direct_nodes.begin();
-    out << " (" << *(*ci);
+    out << " (" << (*ci);
     ++ci;
     while (ci != _current_direct_nodes.end()) {
-      out << ", " << *(*ci);
+      out << ", " << (*ci);
       ++ci;
     }
     out << ")";

+ 8 - 6
panda/src/cull/cullState.h

@@ -8,6 +8,8 @@
 
 #include <pandabase.h>
 
+#include "config_cull.h"
+
 #include <geomNode.h>
 #include <allTransitionsWrapper.h>
 #include <allAttributesWrapper.h>
@@ -15,10 +17,10 @@
 #include <pointerToArray.h>
 #include <updateSeq.h>
 #include <referenceCount.h>
-#include <pt_Node.h>
-#include <vector_PT_Node.h>
+#include <arcChain.h>
 
 #include <map>
+#include <vector>
 
 class GraphicsStateGuardian;
 class GeomBin;
@@ -34,8 +36,8 @@ class GeomBin;
 ////////////////////////////////////////////////////////////////////
 class EXPCL_PANDA CullState : public ReferenceCount {
 public:
-  typedef vector<PT(GeomNode)> CurrentGeomNodes;
-  typedef vector_PT_Node CurrentDirectNodes;
+  typedef vector<ArcChain> CurrentGeomNodes;
+  typedef vector<ArcChain> CurrentDirectNodes;
 
 public:
   INLINE CullState(const AllTransitionsWrapper &trans);
@@ -50,8 +52,8 @@ public:
   INLINE void mark_verified(Node *node, UpdateSeq now);
 
   INLINE void clear_current_nodes();
-  INLINE void record_current_geom_node(const PT(GeomNode) &node);
-  INLINE void record_current_direct_node(const PT_Node &node);
+  INLINE void record_current_geom_node(const ArcChain &arc_chain);
+  INLINE void record_current_direct_node(const ArcChain &arc_chain);
   INLINE bool is_empty() const;
   INLINE int count_current_nodes() const;
   INLINE int get_empty_frames_count() const;

+ 33 - 0
panda/src/cull/cullStateLookup.cxx

@@ -50,20 +50,36 @@ CullState *CullStateLookup::
 find_node(Node *node,
 	  const AllTransitionsWrapper &trans,
 	  UpdateSeq now) {
+  if (cull_cat.is_spam()) {
+    cull_cat.spam()
+      << "Looking up " << *node << " in lookup " << (void *)this << "\n";
+  }
   CullStates::iterator csi;
   csi = _cull_states.find(node);
   if (csi == _cull_states.end()) {
     // No entry for the node.
+    if (cull_cat.is_spam()) {
+      cull_cat.spam()
+	<< "No entry for the node.\n";
+    }
     return NULL;
   }
 
   CullState *cs = (*csi).second;
   if (cs->check_currency(node, trans, now)) {
     // The entry is current enough to use.
+    if (cull_cat.is_spam()) {
+      cull_cat.spam()
+	<< "The entry is found and current.\n";
+    }
     return cs;
   }
 
   // The entry is stale; remove it and return NULL.
+  if (cull_cat.is_spam()) {
+    cull_cat.spam()
+      << "The entry is stale.\n";
+  }
   _cull_states.erase(csi);
   return NULL;
 }
@@ -94,10 +110,19 @@ get_subtree(const PT(NodeRelation) &arc,
 	    const AllTransitionsWrapper &trans,
 	    Node *top_subtree,
 	    UpdateSeq now) {
+  if (cull_cat.is_spam()) {
+    cull_cat.spam()
+      << "Getting subtree for " << *arc << " in lookup "
+      << (void *)this << "\n";
+  }
   Subtrees::iterator si;
   si = _subtrees.find(arc);
   if (si == _subtrees.end()) {
     // No entry for the arc.
+    if (cull_cat.is_spam()) {
+      cull_cat.spam()
+	<< "No entry for the arc; creating new one.\n";
+    }
     CullStateSubtree *subtree = 
       new CullStateSubtree(this, trans, top_subtree, now);
     _subtrees.insert(Subtrees::value_type(arc, subtree));
@@ -107,10 +132,18 @@ get_subtree(const PT(NodeRelation) &arc,
   CullStateSubtree *subtree = (*si).second;
   if (subtree->check_currency(trans, top_subtree, now)) {
     // The entry is current enough to use.
+    if (cull_cat.is_spam()) {
+      cull_cat.spam()
+	<< "The entry is found and current.\n";
+    }
     return subtree;
   }
 
   // The entry is stale; update it.
+  if (cull_cat.is_spam()) {
+    cull_cat.spam()
+      << "The entry is stale.\n";
+  }
   subtree->update(trans, top_subtree, now);
   return subtree;
 }

+ 8 - 1
panda/src/cull/cullStateSubtree.cxx

@@ -4,6 +4,7 @@
 ////////////////////////////////////////////////////////////////////
 
 #include "cullStateSubtree.h"
+#include "config_cull.h"
 
 
 ////////////////////////////////////////////////////////////////////
@@ -25,13 +26,19 @@ CullStateSubtree::
 bool CullStateSubtree::
 check_currency(const AllTransitionsWrapper &, Node *top_subtree, 
 	       UpdateSeq now) {
+  if (cull_cat.is_spam()) {
+    cull_cat.spam()
+      << "Checking currency for subtree " << (void *)this
+      << ", _verified = " << _verified << " now = " << now << "\n";
+  }
+
   // Make sure we've still got the same top_subtree node.
   if (_top_subtree != top_subtree) {
     return false;
   }
 
   // First, check the verified time stamp.
-  if (_verified == now) {
+  if (_verified == now && !_verified.is_fresh()) {
     return true;
   }
 

+ 49 - 11
panda/src/cull/cullTraverser.I

@@ -56,6 +56,22 @@ draw_geom(GeomNode *geom_node, const AllAttributesWrapper &initial_state) {
   geom_node->draw(_gsg);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: CullTraverser::draw_geom
+//       Access: Public
+//  Description: Sets up the GSG to draw the indicated GeomNode in the
+//               indicated state.  If the state so demands it and the
+//               GeomNode has children, draws the rest of the
+//               hierarchy below it immediately.
+////////////////////////////////////////////////////////////////////
+INLINE void CullTraverser::
+draw_geom(const ArcChain &arc_chain, const AllAttributesWrapper &initial_state) {
+  nassertv(!arc_chain.empty());
+  GeomNode *geom_node;
+  DCAST_INTO_V(geom_node, arc_chain.back()->get_child());
+  draw_geom(geom_node, initial_state);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: CullTraverser::draw_direct
 //       Access: Public
@@ -63,13 +79,16 @@ draw_geom(GeomNode *geom_node, const AllAttributesWrapper &initial_state) {
 //               indicated node, and including all nested nodes below.
 ////////////////////////////////////////////////////////////////////
 INLINE void CullTraverser::
-draw_direct(Node *node, const AllAttributesWrapper &initial_state) {
+draw_direct(const ArcChain &arc_chain, 
+	    const AllAttributesWrapper &initial_state) {
+  nassertv(!arc_chain.empty());
+  Node *node = arc_chain.back()->get_child();
   if (cull_cat.is_spam()) {
     cull_cat.spam() 
       << "Drawing " << *node << " in direct mode.\n";
   }
   nassertv(node != (Node *)NULL);
-  DirectRenderTraverser drt(_gsg, _graph_type);
+  DirectRenderTraverser drt(_gsg, _graph_type, arc_chain);
   drt.traverse(node, initial_state, AllTransitionsWrapper());
 }
 
@@ -87,14 +106,15 @@ draw_direct(Node *node, const AllAttributesWrapper &initial_state) {
 INLINE CullStateLookup *CullTraverser::
 add_instance(NodeRelation *arc, const AllTransitionsWrapper &trans,
 	     Node *top_subtree, const CullLevelState &level_state) {
-  return level_state._lookup->get_subtree(arc, trans, top_subtree, _now);
+  return level_state._lookup->get_subtree
+    (arc, trans, top_subtree, level_state._now);
 }
 
 ////////////////////////////////////////////////////////////////////
 //     Function: CullTraverser::find_bin_state
 //       Access: Private
 //  Description: Looks for a CullState in the set that corresponds to
-//               the indicates set of transitions; if one is found,
+//               the indicated set of transitions; if one is found,
 //               returns its pointer; otherwise, creates a new one and
 //               returns it.
 ////////////////////////////////////////////////////////////////////
@@ -108,14 +128,32 @@ find_bin_state(const AllTransitionsWrapper &trans) {
   // corresponding CullState that is (now) stored in the set.
   pair<States::iterator, bool> result = _states.insert(cs);
 
-  /*
-  if (result.second) {
-    // The insert succeeded, so the CullState was not there
-    // previously.
-    cerr << "Created CullState for:\n";
-    trans.write(cerr, 2);
+  if (cull_cat.is_spam()) {
+    if (result.second) {
+      // The insert succeeded, so the CullState was not there
+      // previously.
+      cull_cat.spam() 
+	<< "Created CullState " << (void *)cs << " for:\n";
+      trans.write(cull_cat.spam(false), 2);
+    } else {
+      cull_cat.spam() 
+	<< "Found existing CullState " << (void *)(*result.first)
+	<< " for:\n";
+      trans.write(cull_cat.spam(false), 2);
+    }
   }
-  */
 		       
   return *result.first;
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: CullTraverser::backward_arc
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE void CullTraverser::
+backward_arc(NodeRelation *arc, NullTransitionWrapper &,
+	     NullAttributeWrapper &, NullAttributeWrapper &,
+	     const CullLevelState &) {
+  mark_backward_arc(arc);
+}

+ 71 - 21
panda/src/cull/cullTraverser.cxx

@@ -14,9 +14,10 @@
 #include <wrt.h>
 #include <frustumCullTraverser.h>
 #include <graphicsStateGuardian.h>
-#include <billboardTransition.h>
 #include <decalTransition.h>
 #include <pruneTransition.h>
+#include <transformTransition.h>
+#include <nodeTransitionWrapper.h>
 #include <indent.h>
 #include <config_sgraphutil.h>  // for implicit_app_traversal
 #include <pStatTimer.h>
@@ -36,8 +37,9 @@ PStatCollector CullTraverser::_draw_pcollector =
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 CullTraverser::
-CullTraverser(GraphicsStateGuardian *gsg, TypeHandle graph_type) :
-  RenderTraverser(gsg, graph_type)
+CullTraverser(GraphicsStateGuardian *gsg, TypeHandle graph_type,
+	      const ArcChain &arc_chain) :
+  RenderTraverser(gsg, graph_type, arc_chain)
 {
   _default_bin = new GeomBinNormal("default", this);
   _nested_count = 0;
@@ -85,9 +87,14 @@ traverse(Node *root,
 
   nassertv(!_bins.empty());
 
-  _now = last_graph_update[_graph_type];
-
   bool is_initial = (_nested_count == 0);
+  if (is_initial) {
+    if (cull_force_update) {
+      _now = UpdateSeq::fresh();
+    } else {
+      _now = UpdateSeq::initial();
+    }
+  }
   _nested_count++;
 
   if (is_initial) {
@@ -101,8 +108,25 @@ traverse(Node *root,
 
   CullLevelState level_state;
   level_state._lookup = &_lookup;
-  fc_traverse(root, *this, NullAttributeWrapper(), level_state,
-	      _gsg, _graph_type);
+  level_state._now = _now;
+
+  // Determine the relative transform matrix from the camera to our
+  // starting node.  This is important for proper view-frustum
+  // culling.
+  LMatrix4f rel_from_camera;
+  NodeTransitionWrapper ntw(TransformTransition::get_class_type());
+  wrt(_gsg->get_current_projection_node(), root, begin(), end(),
+      ntw, get_graph_type());
+  const TransformTransition *tt;
+  if (get_transition_into(tt, ntw)) {
+    rel_from_camera = tt->get_matrix();
+  } else {
+    // No relative transform.
+    rel_from_camera = LMatrix4f::ident_mat();
+  }
+
+  fc_traverse(root, rel_from_camera, *this, NullAttributeWrapper(), 
+	      level_state, _gsg, _graph_type);
 
   if (is_initial) {
     draw();
@@ -262,9 +286,12 @@ clean_out_old_states() {
 //               appropriate bin.
 ////////////////////////////////////////////////////////////////////
 void CullTraverser::
-add_geom_node(const PT(GeomNode) &node, const AllTransitionsWrapper &trans,
+add_geom_node(GeomNode *node, const AllTransitionsWrapper &trans,
 	      const CullLevelState &level_state) {
   nassertv(node != (GeomNode *)NULL);
+  const ArcChain &arc_chain = get_arc_chain();
+  nassertv(!arc_chain.empty());
+  nassertv(arc_chain.back()->get_child() == node);
 
   AllTransitionsWrapper complete_trans;
   level_state._lookup->compose_trans(trans, complete_trans);
@@ -275,17 +302,23 @@ add_geom_node(const PT(GeomNode) &node, const AllTransitionsWrapper &trans,
   // remove it here just to be paranoid.
   complete_trans.clear_transition(DirectRenderTransition::get_class_type());
 
-  CullState *cs = level_state._lookup->find_node(node, complete_trans, _now);
+  CullState *cs = level_state._lookup->find_node
+    (node, complete_trans, level_state._now);
   if (cs == (CullState *)NULL) {
+    if (cull_cat.is_spam()) {
+      cull_cat.spam()
+	<< "Finding a new bin state\n";
+    }
+
     // The node didn't have a previously-associated CullState that we
     // could use, so determine a new one for it.
     cs = find_bin_state(complete_trans);
     nassertv(cs != (CullState *)NULL);
 
-    level_state._lookup->record_node(node, cs, _now);
+    level_state._lookup->record_node(node, cs, level_state._now);
   }
 
-  cs->record_current_geom_node(node);
+  cs->record_current_geom_node(arc_chain);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -294,13 +327,16 @@ add_geom_node(const PT(GeomNode) &node, const AllTransitionsWrapper &trans,
 //  Description: Records the indicated Node as one that is immediately
 //               under a DirectRenderTransition, and thus it and its
 //               subtree should be rendered directly, in a depth-first
-//               traversal.  This is usuall done when the render order
+//               traversal.  This is usually done when the render order
 //               is very important within a small subtree of nodes.
 ////////////////////////////////////////////////////////////////////
 void CullTraverser::
-add_direct_node(const PT_Node &node, const AllTransitionsWrapper &trans,
+add_direct_node(Node *node, const AllTransitionsWrapper &trans,
 		const CullLevelState &level_state) {
   nassertv(node != (Node *)NULL);
+  const ArcChain &arc_chain = get_arc_chain();
+  nassertv(!arc_chain.empty());
+  nassertv(arc_chain.back()->get_child() == node);
 
   AllTransitionsWrapper complete_trans;
   level_state._lookup->compose_trans(trans, complete_trans);
@@ -309,17 +345,18 @@ add_direct_node(const PT_Node &node, const AllTransitionsWrapper &trans,
   // and interfere with state-sorting.
   complete_trans.clear_transition(DirectRenderTransition::get_class_type());
 
-  CullState *cs = level_state._lookup->find_node(node, complete_trans, _now);
+  CullState *cs = level_state._lookup->find_node
+    (node, complete_trans, level_state._now);
   if (cs == (CullState *)NULL) {
     // The node didn't have a previously-associated CullState that we
     // could use, so determine a new one for it.
     cs = find_bin_state(complete_trans);
     nassertv(cs != (CullState *)NULL);
 
-    level_state._lookup->record_node(node, cs, _now);
+    level_state._lookup->record_node(node, cs, level_state._now);
   }
 
-  cs->record_current_direct_node(node);
+  cs->record_current_direct_node(arc_chain);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -346,6 +383,11 @@ forward_arc(NodeRelation *arc, NullTransitionWrapper &,
 
   AllTransitionsWrapper trans;
 
+  UpdateSeq last_update = arc->get_last_update();
+  if (level_state._now < last_update) {
+    level_state._now = last_update;
+  }
+
   bool is_instanced = (node->get_num_parents(_graph_type) > 1);
   bool is_geom = node->is_of_type(GeomNode::get_class_type());
   bool node_has_sub_render = node->has_sub_render();
@@ -354,10 +396,18 @@ forward_arc(NodeRelation *arc, NullTransitionWrapper &,
     arc->has_transition(DirectRenderTransition::get_class_type()) ||
     arc->has_transition(DecalTransition::get_class_type());
 
+  if (arc_has_sub_render) {
+    level_state._now = UpdateSeq::fresh();
+  }
+  _now = level_state._now;
+
+  mark_forward_arc(arc);
+
   if (cull_cat.is_spam()) {
     cull_cat.spam() 
       << "Reached " << *node << ":\n"
-      << "is_instanced = " << is_instanced
+      << " now = " << level_state._now
+      << " is_instanced = " << is_instanced
       << " is_geom = " << is_geom
       << " node_has_sub_render = " << node_has_sub_render
       << " arc_has_sub_render = " << arc_has_sub_render
@@ -382,10 +432,9 @@ forward_arc(NodeRelation *arc, NullTransitionWrapper &,
 
       AllTransitionsWrapper new_trans;
 
-      if (!arc->sub_render_trans(attrib, new_trans, _gsg)) {
-	return false;
-      }
-      if (!node->sub_render(attrib, new_trans, _gsg)) {
+      if (!arc->sub_render_trans(attrib, new_trans, this) ||
+	  !node->sub_render(attrib, new_trans, this)) {
+	mark_backward_arc(arc);
 	return false;
       }
 
@@ -399,6 +448,7 @@ forward_arc(NodeRelation *arc, NullTransitionWrapper &,
     // traverse any further beyond it--the rest of the subgraph
     // beginning at this node will be traversed when the node is
     // rendered.
+    mark_backward_arc(arc);
     return false;
   }
 

+ 13 - 5
panda/src/cull/cullTraverser.h

@@ -37,7 +37,8 @@ class EXPCL_PANDA CullTraverser :
   public RenderTraverser, 
   public TraverserVisitor<NullTransitionWrapper, CullLevelState> {
 public:
-  CullTraverser(GraphicsStateGuardian *gsg, TypeHandle graph_type);
+  CullTraverser(GraphicsStateGuardian *gsg, TypeHandle graph_type,
+		const ArcChain &arc_chain = ArcChain());
   virtual ~CullTraverser();
 
   INLINE void set_default_bin(GeomBin *bin);
@@ -49,7 +50,9 @@ public:
 
   INLINE void draw_geom(GeomNode *geom_node,
 			const AllAttributesWrapper &initial_state);
-  INLINE void draw_direct(Node *node,
+  INLINE void draw_geom(const ArcChain &arc_chain,
+			const AllAttributesWrapper &initial_state);
+  INLINE void draw_direct(const ArcChain &arc_chain,
 			  const AllAttributesWrapper &initial_state);
 
 PUBLISHED:
@@ -60,10 +63,10 @@ private:
   void draw();
   void clean_out_old_states();
 
-  void add_geom_node(const PT(GeomNode) &node, 
+  void add_geom_node(GeomNode *node, 
 		     const AllTransitionsWrapper &trans,
 		     const CullLevelState &level_state);
-  void add_direct_node(const PT_Node &node, 
+  void add_direct_node(Node *node, 
 		       const AllTransitionsWrapper &trans,
 		       const CullLevelState &level_state);
 		     
@@ -83,8 +86,12 @@ public:
 		   NullAttributeWrapper &pre, NullAttributeWrapper &post,
 		   CullLevelState &level_state);
 
+  INLINE void 
+  backward_arc(NodeRelation *arc, NullTransitionWrapper &trans,
+	       NullAttributeWrapper &pre, NullAttributeWrapper &post,
+	       const CullLevelState &level_state);
+
 private:
-  UpdateSeq _now;
   AllAttributesWrapper _initial_state;
 
   typedef set<PT(GeomBin)> Bins;
@@ -96,6 +103,7 @@ private:
 
   CullStateLookup _lookup;
   int _nested_count;
+  UpdateSeq _now;
 
 public:
   // Statistics

+ 7 - 8
panda/src/cull/geomBinBackToFront.I

@@ -12,10 +12,10 @@
 ////////////////////////////////////////////////////////////////////
 INLINE GeomBinBackToFront::NodeEntry::
 NodeEntry(float distance, const PT(CullState) &state, 
-	  Node *node, bool is_direct) :
+	  const ArcChain &arc_chain, bool is_direct) :
   _distance(distance),
   _state(state),
-  _node(node),
+  _arc_chain(arc_chain),
   _is_direct(is_direct)
 {
 }
@@ -29,7 +29,7 @@ INLINE GeomBinBackToFront::NodeEntry::
 NodeEntry(const GeomBinBackToFront::NodeEntry &copy) :
   _distance(copy._distance),
   _state(copy._state),
-  _node(copy._node),
+  _arc_chain(copy._arc_chain),
   _is_direct(copy._is_direct)
 {
 }
@@ -43,7 +43,7 @@ INLINE void GeomBinBackToFront::NodeEntry::
 operator = (const NodeEntry &copy) {
   _distance = copy._distance;
   _state = copy._state;
-  _node = copy._node;
+  _arc_chain = copy._arc_chain;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -63,12 +63,11 @@ operator < (const NodeEntry &other) const {
 ////////////////////////////////////////////////////////////////////
 INLINE void GeomBinBackToFront::NodeEntry::
 draw(CullTraverser *trav) const {
-  nassertv(_node != (GeomNode *)NULL);
+  nassertv(!_arc_chain.empty());
   if (_is_direct) {
-    trav->draw_direct(_node, AllAttributesWrapper(_state->get_attributes()));
+    trav->draw_direct(_arc_chain, AllAttributesWrapper(_state->get_attributes()));
   } else {
-    trav->draw_geom(DCAST(GeomNode, _node), 
-		    AllAttributesWrapper(_state->get_attributes()));
+    trav->draw_geom(_arc_chain, AllAttributesWrapper(_state->get_attributes()));
   }
 }
 

+ 10 - 5
panda/src/cull/geomBinBackToFront.cxx

@@ -31,7 +31,7 @@ clear_current_states() {
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomBinBackToFront::record_current_state
 //       Access: Public, Virtual
-//  Description: Called each frame by the CullTraverser to indicated
+//  Description: Called each frame by the CullTraverser to indicate
 //               that the given CullState (and all of its current
 //               GeomNodes) is visible this frame.
 ////////////////////////////////////////////////////////////////////
@@ -45,7 +45,10 @@ record_current_state(GraphicsStateGuardian *gsg, CullState *cs, int,
 
   CullState::geom_const_iterator gi;
   for (gi = cs->geom_begin(); gi != cs->geom_end(); ++gi) {
-    GeomNode *node = (*gi);
+    const ArcChain &arc_chain = (*gi);
+    nassertv(!arc_chain.empty());
+    GeomNode *node;
+    DCAST_INTO_V(node, arc_chain.back()->get_child());
     nassertv(node != (GeomNode *)NULL);
     const BoundingVolume &volume = node->get_bound();
 
@@ -60,13 +63,15 @@ record_current_state(GraphicsStateGuardian *gsg, CullState *cs, int,
       }
 
       float distance = gsg->compute_distance_to(center);
-      _node_entries.insert(NodeEntry(distance, cs, node, false));
+      _node_entries.insert(NodeEntry(distance, cs, arc_chain, false));
     }
   }
 
   CullState::direct_const_iterator di;
   for (di = cs->direct_begin(); di != cs->direct_end(); ++di) {
-    Node *node = (*di);
+    const ArcChain &arc_chain = (*di);
+    nassertv(!arc_chain.empty());
+    Node *node = arc_chain.back()->get_child();
     nassertv(node != (Node *)NULL);
 
     const BoundingVolume &volume = node->get_bound();
@@ -119,7 +124,7 @@ record_current_state(GraphicsStateGuardian *gsg, CullState *cs, int,
       }
     }
 
-    _node_entries.insert(NodeEntry(distance, cs, node, true));
+    _node_entries.insert(NodeEntry(distance, cs, arc_chain, true));
   }
 }
 

+ 2 - 2
panda/src/cull/geomBinBackToFront.h

@@ -40,7 +40,7 @@ private:
   class NodeEntry {
   public:
     INLINE NodeEntry(float distance, const PT(CullState) &state, 
-		     Node *node, bool is_direct);
+		     const ArcChain &arc_chain, bool is_direct);
     INLINE NodeEntry(const NodeEntry &copy);
     INLINE void operator = (const NodeEntry &copy);
 
@@ -51,7 +51,7 @@ private:
   private:
     float _distance;
     PT(CullState) _state;
-    Node *_node;
+    ArcChain _arc_chain;
     bool _is_direct;
   };
 

+ 7 - 8
panda/src/cull/geomBinFixed.I

@@ -12,10 +12,10 @@
 ////////////////////////////////////////////////////////////////////
 INLINE GeomBinFixed::NodeEntry::
 NodeEntry(int draw_order, const PT(CullState) &state, 
-	  Node *node, bool is_direct) :
+	  const ArcChain &arc_chain, bool is_direct) :
   _draw_order(draw_order),
   _state(state),
-  _node(node),
+  _arc_chain(arc_chain),
   _is_direct(is_direct)
 {
 }
@@ -29,7 +29,7 @@ INLINE GeomBinFixed::NodeEntry::
 NodeEntry(const GeomBinFixed::NodeEntry &copy) :
   _draw_order(copy._draw_order),
   _state(copy._state),
-  _node(copy._node),
+  _arc_chain(copy._arc_chain),
   _is_direct(copy._is_direct)
 {
 }
@@ -43,7 +43,7 @@ INLINE void GeomBinFixed::NodeEntry::
 operator = (const NodeEntry &copy) {
   _draw_order = copy._draw_order;
   _state = copy._state;
-  _node = copy._node;
+  _arc_chain = copy._arc_chain;
   _is_direct = copy._is_direct;
 }
 
@@ -64,12 +64,11 @@ operator < (const NodeEntry &other) const {
 ////////////////////////////////////////////////////////////////////
 INLINE void GeomBinFixed::NodeEntry::
 draw(CullTraverser *trav) const {
-  nassertv(_node != (GeomNode *)NULL);
+  nassertv(!_arc_chain.empty());
   if (_is_direct) {
-    trav->draw_direct(_node, AllAttributesWrapper(_state->get_attributes()));
+    trav->draw_direct(_arc_chain, AllAttributesWrapper(_state->get_attributes()));
   } else {
-    trav->draw_geom(DCAST(GeomNode, _node), 
-		    AllAttributesWrapper(_state->get_attributes()));
+    trav->draw_geom(_arc_chain, AllAttributesWrapper(_state->get_attributes()));
   }
 }
 

+ 5 - 7
panda/src/cull/geomBinFixed.cxx

@@ -32,7 +32,7 @@ clear_current_states() {
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomBinFixed::record_current_state
 //       Access: Public, Virtual
-//  Description: Called each frame by the CullTraverser to indicated
+//  Description: Called each frame by the CullTraverser to indicate
 //               that the given CullState (and all of its current
 //               GeomNodes) is visible this frame.
 ////////////////////////////////////////////////////////////////////
@@ -46,16 +46,14 @@ record_current_state(GraphicsStateGuardian *, CullState *cs,
 
   CullState::geom_const_iterator gi;
   for (gi = cs->geom_begin(); gi != cs->geom_end(); ++gi) {
-    GeomNode *node = (*gi);
-    nassertv(node != (GeomNode *)NULL);
-    _node_entries.insert(NodeEntry(draw_order, cs, node, false));
+    const ArcChain &arc_chain = (*gi);
+    _node_entries.insert(NodeEntry(draw_order, cs, arc_chain, false));
   }
 
   CullState::direct_const_iterator di;
   for (di = cs->direct_begin(); di != cs->direct_end(); ++di) {
-    Node *node = (*di);
-    nassertv(node != (Node *)NULL);
-    _node_entries.insert(NodeEntry(draw_order, cs, node, true));
+    const ArcChain &arc_chain = (*di);
+    _node_entries.insert(NodeEntry(draw_order, cs, arc_chain, true));
   }
 }
 

+ 2 - 2
panda/src/cull/geomBinFixed.h

@@ -37,7 +37,7 @@ private:
   class NodeEntry {
   public:
     INLINE NodeEntry(int draw_order, const PT(CullState) &state, 
-		     Node *node, bool is_direct);
+		     const ArcChain &arc_chain, bool is_direct);
     INLINE NodeEntry(const NodeEntry &copy);
     INLINE void operator = (const NodeEntry &copy);
 
@@ -48,7 +48,7 @@ private:
   private:
     int _draw_order;
     PT(CullState) _state;
-    Node *_node;
+    ArcChain _arc_chain;
     bool _is_direct;
   };
 

+ 9 - 11
panda/src/cull/geomBinUnsorted.cxx

@@ -90,26 +90,24 @@ draw(CullTraverser *trav) {
 
       CullState::geom_iterator gi;
       for (gi = cs->geom_begin(); gi != cs->geom_end(); ++gi) {
-	GeomNode *geom = (*gi);
-	nassertv(geom != (GeomNode *)NULL);
+	const ArcChain &arc_chain = (*gi);
+	nassertv(!arc_chain.empty());
+	GeomNode *geom_node;
+	DCAST_INTO_V(geom_node, arc_chain.back()->get_child());
 	if (cull_cat.is_spam()) {
 	  cull_cat.spam()
-	    << "Drawing " << *geom << "\n";
+	    << "Drawing " << *geom_node << "\n";
 	}
-	geom->draw(gsg);
+	geom_node->draw(gsg);
       }
     }
 
     CullState::direct_iterator di;
     for (di = cs->direct_begin(); di != cs->direct_end(); ++di) {
-      Node *node = (*di);
-      nassertv(node != (Node *)NULL);
+      const ArcChain &arc_chain = (*di);
+      nassertv(!arc_chain.empty());
 	  
-      if (cull_cat.is_spam()) {
-	cull_cat.spam()
-	  << "Drawing direct: " << *node << "\n";
-      }
-      trav->draw_direct(node, cs->get_attributes());
+      trav->draw_direct(arc_chain, cs->get_attributes());
     }
   }
 }

+ 1 - 1
panda/src/display/graphicsStateGuardian.I

@@ -49,7 +49,7 @@ get_state() const {
 //               projection node (i.e. the camera) for this scene, as
 //               set by the last call to render_subgraph().
 ////////////////////////////////////////////////////////////////////
-INLINE const ProjectionNode *GraphicsStateGuardian::
+INLINE ProjectionNode *GraphicsStateGuardian::
 get_current_projection_node(void) const { 
   return _current_projection_node;
 }

+ 4 - 4
panda/src/display/graphicsStateGuardian.h

@@ -68,10 +68,10 @@ public:
   virtual void prepare_display_region()=0;
 
   virtual void render_frame(const AllAttributesWrapper &initial_state)=0;
-  virtual void render_scene(Node *root, const ProjectionNode *projnode,
+  virtual void render_scene(Node *root, ProjectionNode *projnode,
 			    const AllAttributesWrapper &initial_state)=0;
   virtual void render_subgraph(RenderTraverser *traverser, 
-			       Node *subgraph, const ProjectionNode *projnode,
+			       Node *subgraph, ProjectionNode *projnode,
 			       const AllAttributesWrapper &initial_state,
 			       const AllTransitionsWrapper &net_trans)=0;
   virtual void render_subgraph(RenderTraverser *traverser,
@@ -99,7 +99,7 @@ public:
 
   RenderBuffer get_render_buffer(int buffer_type);
 
-  INLINE const ProjectionNode* get_current_projection_node(void) const ;
+  INLINE ProjectionNode *get_current_projection_node(void) const ;
   INLINE const Node* get_current_root_node(void) const;
 
   INLINE CPT(DisplayRegion) get_current_display_region(void) const;
@@ -144,7 +144,7 @@ protected:
 
   // These must be set by render_scene().
   Node *_current_root_node;
-  const ProjectionNode *_current_projection_node;
+  ProjectionNode *_current_projection_node;
   CPT(DisplayRegion) _current_display_region;
 
   // This is used by wants_normals()

+ 3 - 3
panda/src/dxgsg/dxGraphicsStateGuardian.cxx

@@ -875,7 +875,7 @@ render_frame(const AllAttributesWrapper &initial_state) {
 //               may be modified during rendering.
 ////////////////////////////////////////////////////////////////////
 void DXGraphicsStateGuardian::
-render_scene(Node *root, const ProjectionNode *projnode,
+render_scene(Node *root, ProjectionNode *projnode,
          const AllAttributesWrapper &initial_state) {
 #ifdef GSG_VERBOSE
   _pass_number = 0;
@@ -905,11 +905,11 @@ render_scene(Node *root, const ProjectionNode *projnode,
 ////////////////////////////////////////////////////////////////////
 void DXGraphicsStateGuardian::
 render_subgraph(RenderTraverser *traverser,
-        Node *subgraph, const ProjectionNode *projnode,
+        Node *subgraph, ProjectionNode *projnode,
         const AllAttributesWrapper &initial_state,
         const AllTransitionsWrapper &net_trans) {
   activate();
-  const ProjectionNode *old_projection_node = _current_projection_node;
+  ProjectionNode *old_projection_node = _current_projection_node;
   _current_projection_node = projnode;
   LMatrix4f old_projection_mat = _current_projection_mat;
 

+ 2 - 2
panda/src/dxgsg/dxGraphicsStateGuardian.h

@@ -85,10 +85,10 @@ public:
   virtual void prepare_display_region();
 
   virtual void render_frame(const AllAttributesWrapper &initial_state);
-  virtual void render_scene(Node *root, const ProjectionNode *projnode,
+  virtual void render_scene(Node *root, ProjectionNode *projnode,
 			    const AllAttributesWrapper &initial_state);
   virtual void render_subgraph(RenderTraverser *traverser, 
-			       Node *subgraph, const ProjectionNode *projnode,
+			       Node *subgraph, ProjectionNode *projnode,
 			       const AllAttributesWrapper &initial_state,
 			       const AllTransitionsWrapper &net_trans);
   virtual void render_subgraph(RenderTraverser *traverser,

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

@@ -14,6 +14,7 @@
 #include <billboardTransition.h>
 #include <transformAttribute.h>
 #include <transparencyTransition.h>
+#include <renderTraverser.h>
 
 #include <orthoProjection.h>
 #include <perspectiveProjection.h>
@@ -295,15 +296,14 @@ render_children(const vector_relation &arcs, const AllAttributesWrapper &attrib,
 ////////////////////////////////////////////////////////////////////
 bool LensFlareNode::
 sub_render(const AllAttributesWrapper &attrib, AllTransitionsWrapper &trans,
-	   GraphicsStateGuardianBase *gsgbase) 
-{
-  GraphicsStateGuardian *gsg = DCAST(GraphicsStateGuardian, gsgbase);
+	   RenderTraverser *trav) {
+  GraphicsStateGuardian *gsg = trav->get_gsg();
 
   nassertr(_light_node != (Node*) NULL, false);
 
   //First we need the light position
-  const ProjectionNode *camera_node = gsg->get_current_projection_node();
-  const PerspectiveProjection *pp = DCAST(PerspectiveProjection, camera_node->get_projection());
+  ProjectionNode *camera_node = gsg->get_current_projection_node();
+  PerspectiveProjection *pp = DCAST(PerspectiveProjection, camera_node->get_projection());
 
   LPoint3f light_pos = get_rel_pos(_light_node, camera_node);
   

+ 3 - 2
panda/src/effects/lensFlareNode.h

@@ -38,8 +38,9 @@ PUBLISHED:
   INLINE void set_light_source(PT_Node source);
 
 public:
-  virtual bool sub_render(const AllAttributesWrapper &attrib, AllTransitionsWrapper &trans, 
-			  GraphicsStateGuardianBase *gsgbase);
+  virtual bool sub_render(const AllAttributesWrapper &attrib,
+			  AllTransitionsWrapper &trans, 
+			  RenderTraverser *trav);
   virtual bool has_sub_render() const;
   
 private:

+ 11 - 3
panda/src/express/pointerTo.I

@@ -50,9 +50,12 @@ template<class T>
 void PointerToBase<T>::
 reassign(To *ptr) {
   if (ptr != _ptr) {
-    if (_ptr != (To *)NULL) {
-      unref_delete(_ptr);
-    }
+    // First save the old pointer; we won't delete it until we have
+    // assigned the new one.  We do this just in case there are
+    // cascading effects from deleting this pointer that might
+    // inadvertently delete the new one.  (Don't laugh--it's
+    // happened!)
+    To *old_ptr = _ptr;
     
     _ptr = ptr;
     if (_ptr != (To *)NULL) {
@@ -72,6 +75,11 @@ reassign(To *ptr) {
       }
 #endif
     }
+
+    // Now delete the old pointer.
+    if (old_ptr != (To *)NULL) {
+      unref_delete(old_ptr);
+    }
   }
 }
 

+ 10 - 2
panda/src/express/referenceCount.I

@@ -125,7 +125,6 @@ prepare_delete() {
   _ref_count = -100;
 }
 
-#ifdef NDEBUG
 ////////////////////////////////////////////////////////////////////
 //     Function: ReferenceCount::unref_consider_delete
 //       Access: Public
@@ -143,7 +142,6 @@ unref_consider_delete() {
   unref();
   return (get_ref_count() == 0);
 }
-#endif
 
 ////////////////////////////////////////////////////////////////////
 //     Function: ReferenceCount::get_ref_count
@@ -291,6 +289,16 @@ template<class RefCountType>
 INLINE void
 unref_delete(RefCountType *ptr) {
   if (((ReferenceCount *)ptr)->unref_consider_delete()) {
+#ifndef NDEBUG
+    if (get_leak_memory()) {
+      // In leak-memory mode, we don't actually delete the pointer,
+      // although we do call the destructor explicitly.  This has
+      // exactly the same effect as deleting it, without actually
+      // freeing up the memory it uses.
+      ptr->~RefCountType();
+      return;
+    }
+#endif
     delete ptr;
   }
 }

+ 0 - 32
panda/src/express/referenceCount.cxx

@@ -6,35 +6,3 @@
 #include "referenceCount.h"
 
 TypeHandle ReferenceCount::_type_handle;
-
-#ifndef NDEBUG
-////////////////////////////////////////////////////////////////////
-//     Function: ReferenceCount::unref_consider_delete
-//       Access: Public
-//  Description: This function is called by the global unref_delete()
-//               function.  This function unrefs the pointer and
-//               returns true if it should be deleted (i.e. the count
-//               has gone to zero), or false otherwise.  It doesn't
-//               delete itself because (a) a method cannot safely
-//               delete this, and (b) we don't have a virtual
-//               destructor anyway.  The decision of whether to delete
-//               is left up to unref_delete().
-////////////////////////////////////////////////////////////////////
-bool ReferenceCount::
-unref_consider_delete() {
-  unref();
-
-  if (get_leak_memory()) {
-    // In leak-memory mode, we don't actually delete anything.
-    // However, we do want to call prepare_delete() when the count
-    // reaches zero, to reset the refcount to -100 as a deleted flag.
-    if (get_ref_count() == 0) {
-      prepare_delete();
-      MemoryUsage::remove_pointer(this);
-    }
-    return false;
-  }
-
-  return (get_ref_count() == 0);
-}
-#endif

+ 1 - 6
panda/src/express/referenceCount.h

@@ -19,6 +19,7 @@
 
 #include "typeHandle.h"
 #include "memoryUsage.h"
+#include "config_express.h"
 
 #include <stdlib.h>
 
@@ -44,13 +45,7 @@ public:
   // These functions are not part of the normal API, but they have to
   // be public.  You shouldn't generally call these directly.
   INLINE void prepare_delete();
-#ifdef NDEBUG
-  // unref_consider_delete() is inline only if we are compiling
-  // NDEBUG.
   INLINE bool unref_consider_delete();
-#else
-  bool unref_consider_delete();
-#endif
 
 PUBLISHED:
   INLINE int get_ref_count() const;

+ 3 - 3
panda/src/glgsg/glGraphicsStateGuardian.cxx

@@ -554,7 +554,7 @@ render_frame(const AllAttributesWrapper &initial_state) {
 //               may be modified during rendering.
 ////////////////////////////////////////////////////////////////////
 void GLGraphicsStateGuardian::
-render_scene(Node *root, const ProjectionNode *projnode,
+render_scene(Node *root, ProjectionNode *projnode,
          const AllAttributesWrapper &initial_state) {
 #ifdef GSG_VERBOSE
   _pass_number = 0;
@@ -584,7 +584,7 @@ render_scene(Node *root, const ProjectionNode *projnode,
 ////////////////////////////////////////////////////////////////////
 void GLGraphicsStateGuardian::
 render_subgraph(RenderTraverser *traverser,
-        Node *subgraph, const ProjectionNode *projnode,
+        Node *subgraph, ProjectionNode *projnode,
         const AllAttributesWrapper &initial_state,
         const AllTransitionsWrapper &net_trans) {
   // Calling activate() frequently seems to be intolerably expensive
@@ -593,7 +593,7 @@ render_subgraph(RenderTraverser *traverser,
 
   //  activate();
 
-  const ProjectionNode *old_projection_node = _current_projection_node;
+  ProjectionNode *old_projection_node = _current_projection_node;
   _current_projection_node = projnode;
   LMatrix4f old_projection_mat = _current_projection_mat;
 

+ 2 - 2
panda/src/glgsg/glGraphicsStateGuardian.h

@@ -62,10 +62,10 @@ public:
   virtual void prepare_display_region();
 
   virtual void render_frame(const AllAttributesWrapper &initial_state);
-  virtual void render_scene(Node *root, const ProjectionNode *projnode,
+  virtual void render_scene(Node *root, ProjectionNode *projnode,
 			    const AllAttributesWrapper &initial_state);
   virtual void render_subgraph(RenderTraverser *traverser, 
-			       Node *subgraph, const ProjectionNode *projnode,
+			       Node *subgraph, ProjectionNode *projnode,
 			       const AllAttributesWrapper &initial_state,
 			       const AllTransitionsWrapper &net_trans);
   virtual void render_subgraph(RenderTraverser *traverser,

+ 13 - 3
panda/src/graph/Sources.pp

@@ -9,12 +9,19 @@
     allAttributesWrapper.I allAttributesWrapper.cxx \
     allAttributesWrapper.h allTransitionsWrapper.I \
     allTransitionsWrapper.cxx allTransitionsWrapper.h \
-    bitMask32Transition.cxx bitMask32Transition.h boundedObject.I \
+    arcChain.I arcChain.cxx arcChain.h \
+    bitMask32Transition.cxx bitMask32Transition.h \
+    bitMaskAttribute.I bitMaskAttribute.h \
+    bitMaskTransition.I bitMaskTransition.h \
+    boundedObject.I \
     boundedObject.N boundedObject.cxx boundedObject.h config_graph.cxx \
     config_graph.h graphReducer.cxx graphReducer.h immediateAttribute.I \
     immediateAttribute.cxx immediateAttribute.h immediateTransition.I \
     immediateTransition.cxx immediateTransition.h \
-    lmatrix4fTransition.cxx lmatrix4fTransition.h multiNodeAttribute.I \
+    lmatrix4fTransition.cxx lmatrix4fTransition.h \
+    matrixAttribute.I matrixAttribute.h matrixTransition.I \
+    matrixTransition.h \
+    multiNodeAttribute.I \
     multiNodeAttribute.cxx multiNodeAttribute.h multiNodeTransition.I \
     multiNodeTransition.cxx multiNodeTransition.h namedNode.I \
     namedNode.cxx namedNode.h node.I node.cxx node.h nodeAttribute.I \
@@ -36,11 +43,14 @@
     onOffTransition.I onOffTransition.cxx onOffTransition.h \
     onTransition.I onTransition.cxx onTransition.h pt_NamedNode.N \
     pt_NamedNode.cxx pt_NamedNode.h pt_Node.N pt_Node.cxx pt_Node.h \
-    vector_PT_Node.cxx vector_PT_Node.h
+    setTransitionHelpers.I setTransitionHelpers.h \
+    traverserVisitor.I traverserVisitor.h \
+    vector_PT_Node.cxx vector_PT_Node.h wrt.I wrt.h
 
   #define INSTALL_HEADERS \
     allAttributesWrapper.I allAttributesWrapper.h \
     allTransitionsWrapper.I allTransitionsWrapper.h \
+    arcChain.I arcChain.h \
     bitMask32Transition.h bitMaskAttribute.I bitMaskAttribute.h \
     bitMaskTransition.I bitMaskTransition.h boundedObject.I \
     boundedObject.h config_graph.h dftraverser.I dftraverser.h \

+ 219 - 0
panda/src/graph/arcChain.I

@@ -0,0 +1,219 @@
+// Filename: arcChain.I
+// Created by:  drose (05Jan01)
+// 
+////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////
+//     Function: ArcChain::ArcComponent::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE ArcChain::ArcComponent::
+ArcComponent(NodeRelation *arc, ArcComponent *next) :
+  _arc(arc),
+  _next(next)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ArcChain::ForwardIterator::Constructor
+//       Access: Public
+//  Description: This is an STL-style iterator that can be used to
+//               traverse the full list of arcs traversed so far by
+//               the ArcChain.  Its primary purpose is as a
+//               parameter to wrt() so we can compute a correct
+//               relative wrt to the particular instance we've reached
+//               so far.
+////////////////////////////////////////////////////////////////////
+INLINE ArcChain::ForwardIterator::
+ForwardIterator(ArcChain::ArcComponent *comp) : _comp(comp) {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ArcChain::ForwardIterator::Dereference Operator
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE NodeRelation *ArcChain::ForwardIterator::
+operator * () const {
+  nassertr(_comp != (ArcComponent *)NULL, NULL);
+  return _comp->_arc;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ArcChain::ForwardIterator::Increment Operator
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE void ArcChain::ForwardIterator::
+operator ++() {
+  nassertv(_comp != (ArcComponent *)NULL);
+  _comp = _comp->_next;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ArcChain::ForwardIterator::Equality Operator
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE bool ArcChain::ForwardIterator::
+operator == (const ArcChain::ForwardIterator &other) const {
+  return _comp == other._comp;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ArcChain::ForwardIterator::Inequality Operator
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE bool ArcChain::ForwardIterator::
+operator != (const ArcChain::ForwardIterator &other) const {
+  return _comp != other._comp;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ArcChain::Default Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE ArcChain::
+ArcChain() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ArcChain::Copy Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE ArcChain::
+ArcChain(const ArcChain &copy) :
+  _head(copy._head)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ArcChain::Copy Assignment Operator
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE void ArcChain::
+operator = (const ArcChain &copy) {
+  _head = copy._head;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ArcChain::begin
+//       Access: Public
+//  Description: Returns an iterator that can be used to traverse the
+//               list of arcs.
+////////////////////////////////////////////////////////////////////
+INLINE ArcChain::const_iterator ArcChain::
+begin() const {
+  return ForwardIterator(_head);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ArcChain::end
+//       Access: Public
+//  Description: Returns an iterator that can be used to traverse the
+//               list of arcs.
+////////////////////////////////////////////////////////////////////
+INLINE ArcChain::const_iterator ArcChain::
+end() const {
+  return ForwardIterator();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ArcChain::empty
+//       Access: Public
+//  Description: Returns true if the list of arcs returned by begin()
+//               .. end() is empty (i.e. begin() == end()), false
+//               otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool ArcChain::
+empty() const {
+  return (_head == (ArcComponent *)NULL);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ArcChain::push_back
+//       Access: Public
+//  Description: Appends the indicated arc onto the end of the chain.
+////////////////////////////////////////////////////////////////////
+INLINE void ArcChain::
+push_back(NodeRelation *arc) {
+  _head = new ArcComponent(arc, _head);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ArcChain::pop_back
+//       Access: Public
+//  Description: Removes the last arc from the end of the chain.
+////////////////////////////////////////////////////////////////////
+INLINE void ArcChain::
+pop_back() {
+  nassertv(_head != (ArcComponent *)NULL);
+  _head = _head->_next;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ArcChain::back
+//       Access: Public
+//  Description: Returns the last arc in the chain.
+////////////////////////////////////////////////////////////////////
+INLINE NodeRelation *ArcChain::
+back() const {
+  nassertr(_head != (ArcComponent *)NULL, (NodeRelation *)NULL);
+  return _head->_arc;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ArcChain::operator ==
+//       Access: Public
+//  Description: Returns true if the two chains are equivalent; that
+//               is, if they contain the same list of arcs in the same
+//               order.
+////////////////////////////////////////////////////////////////////
+INLINE bool ArcChain::
+operator == (const ArcChain &other) const {
+  return (compare_to(other) == 0);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ArcChain::operator !=
+//       Access: Public
+//  Description: Returns true if the two chains are not equivalent.
+////////////////////////////////////////////////////////////////////
+INLINE bool ArcChain::
+operator != (const ArcChain &other) const {
+  return !operator == (other);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ArcChain::operator <
+//       Access: Public
+//  Description: Returns true if this ArcChain sorts before the other
+//               one, false otherwise.  The sorting order of two
+//               nonequivalent ArcChains is consistent but undefined,
+//               and is useful only for storing ArcChains in a sorted
+//               container like an STL set.
+////////////////////////////////////////////////////////////////////
+INLINE bool ArcChain::
+operator < (const ArcChain &other) const {
+  return (compare_to(other) < 0);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ArcChain::output
+//       Access: Public
+//  Description: Writes a sensible description of the ArcChain to the
+//               indicated output stream.
+////////////////////////////////////////////////////////////////////
+INLINE void ArcChain::
+output(ostream &out) const {
+  if (empty()) {
+    out << "(empty)";
+  } else {
+    r_output(out, _head);
+  }
+}

+ 79 - 0
panda/src/graph/arcChain.cxx

@@ -0,0 +1,79 @@
+// Filename: arcChain.cxx
+// Created by:  drose (05Jan01)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "arcChain.h"
+#include "node.h"
+#include "namedNode.h"
+
+////////////////////////////////////////////////////////////////////
+//     Function: ArcChain::compare_to
+//       Access: Public
+//  Description: Returns a number less than zero if this ArcChain
+//               sorts before the other one, greater than zero if it
+//               sorts after, or zero if they are equivalent.
+//
+//               Two ArcChains are considered equivalent if they
+//               consist of exactly the same list of arcs in the same
+//               order.  Otherwise, they are different; different
+//               ArcChains will be ranked in a consistent but
+//               undefined ordering; the ordering is useful only for
+//               placing the ArcChains in a sorted container like an
+//               STL set.
+////////////////////////////////////////////////////////////////////
+int ArcChain::
+compare_to(const ArcChain &other) const {
+  ArcComponent *a = _head;
+  ArcComponent *b = other._head;
+
+  while (a != (ArcComponent *)NULL && b != (ArcComponent *)NULL) {
+    if (a < b) {
+      return -1;
+    } else if (a > b) {
+      return 1;
+    }
+
+    a = a->_next;
+    b = b->_next;
+  }
+
+  if (a < b) {
+    return -1;
+  } else if (a > b) {
+    return 1;
+  }
+
+  return 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ArcChain::r_output
+//       Access: Private
+//  Description: The recursive implementation of output(), this writes
+//               the names of each arc component in order from
+//               beginning to end, by first walking to the end of the
+//               linked list and then outputting from there.
+////////////////////////////////////////////////////////////////////
+void ArcChain::
+r_output(ostream &out, ArcComponent *comp) const {
+  ArcComponent *next = comp->_next;
+  if (next != (ArcComponent *)NULL) {
+    // This is not the head of the list; keep going up.
+    r_output(out, next);
+    out << "/";
+  }
+
+  // Now output this component.
+  Node *node = comp->_arc->get_child();
+  if (node->is_of_type(NamedNode::get_class_type())) {
+    NamedNode *named_node = DCAST(NamedNode, node);
+    if (named_node->has_name()) {
+      out << named_node->get_name();
+    } else {
+      out << node->get_type();
+    }
+  } else {
+    out << node->get_type();
+  }
+}

+ 105 - 0
panda/src/graph/arcChain.h

@@ -0,0 +1,105 @@
+// Filename: arcChain.h
+// Created by:  drose (05Jan01)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef ARCCHAIN_H
+#define ARCCHAIN_H
+
+#include <pandabase.h>
+
+#include "nodeRelation.h"
+
+#include <pointerTo.h>
+#include <referenceCount.h>
+#include <notify.h>
+
+////////////////////////////////////////////////////////////////////
+//       Class : ArcChain
+// Description : This defines a singly-linked chain of arcs from one
+//               point (e.g. the root of the scene graph) to another
+//               point (e.g. a leaf), although no assumption is made
+//               about the relationship between the arcs.  It is
+//               simply a list of arcs that may only be lengthened or
+//               shortened, or copied.
+//
+//               This list may be copied by reference using the copy
+//               constructor; the new copy may be appended to without
+//               modifying the source (but individual nodes may not be
+//               modified).
+//
+//               This serves as the fundamental implementation of
+//               NodePaths, for instance, and also is used during
+//               render traversals to manage the list of arcs
+//               traversed so far (and thus compute unambiguous
+//               wrt's).
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA ArcChain {
+private:
+  // We maintain our own linked list structure here instead of using
+  // some STL structure, so we can efficiently copy-construct these
+  // things by sharing the initial part of the list.
+
+  // We have a singly-linked list whose head is the bottom arc of the
+  // path, and whose tail is the top arc of the path.  Thus, we can
+  // copy the entire path by simply copying the head pointer, and we
+  // can then append to or shorten our own path without affecting the
+  // paths we're sharing ArcComponents with.  Very LISPy.
+  class ArcComponent : public ReferenceCount {
+  public:
+    INLINE ArcComponent(NodeRelation *arc, ArcComponent *next);
+    PT(NodeRelation) _arc;
+    PT(ArcComponent) _next;
+  };
+
+  PT(ArcComponent) _head;
+
+  // This is a supporting class for iterating through all the arcs via
+  // begin() .. end().
+  class ForwardIterator {
+  public:
+    INLINE ForwardIterator(ArcComponent *comp = NULL);
+    INLINE NodeRelation *operator * () const;
+    INLINE void operator ++();
+    INLINE bool operator == (const ForwardIterator &other) const;
+    INLINE bool operator != (const ForwardIterator &other) const;
+
+  private:
+    ArcComponent *_comp;
+  };
+
+public:
+  typedef ForwardIterator iterator;
+  typedef ForwardIterator const_iterator;
+
+  INLINE ArcChain();
+  INLINE ArcChain(const ArcChain &copy);
+  INLINE void operator = (const ArcChain &copy);
+
+  INLINE const_iterator begin() const;
+  INLINE const_iterator end() const;
+  INLINE bool empty() const;
+
+  INLINE void push_back(NodeRelation *arc);
+  INLINE void pop_back();
+  INLINE NodeRelation *back() const;
+
+  INLINE bool operator == (const ArcChain &other) const;
+  INLINE bool operator != (const ArcChain &other) const;
+  INLINE bool operator < (const ArcChain &other) const;
+  int compare_to(const ArcChain &other) const;
+
+  INLINE void output(ostream &out) const;
+
+private:
+  void r_output(ostream &out, ArcComponent *comp) const;
+};
+
+INLINE ostream &operator << (ostream &out, const ArcChain &arc_chain) {
+  arc_chain.output(out);
+  return out;
+}
+
+#include "arcChain.I"
+
+#endif

+ 8 - 1
panda/src/graph/config_graph.cxx

@@ -49,9 +49,16 @@ ConfigureFn(config_graph) {
 // wrt(), so that the second time wrt() is called on a particular
 // node-node pair it will be much cheaper than the first time.  This
 // is the default behavior; you'd only want to turn it off if for some
-// reason it was broken.
+// reason it was broken.  This is true by default and cannot be turned
+// off in optimized (NDEBUG) mode.
 const bool cache_wrt = config_graph.GetBool("cache-wrt", true);
 
+// Set this true to force abort() to be called if an ambiguous wrt()
+// call is made.  This will hopefully allow the programmer to get a
+// stack dump and determine who is issuing the ambiguous wrt().  This
+// cannot be turned on in optimized (NDEBUG) mode.
+const bool ambiguous_wrt_abort = config_graph.GetBool("ambiguous-wrt-abort", false);
+
 // Set this true to double-check the cached value of wrt(), above, by
 // performing an explicit uncached wrt() and comparing the results.
 // Obviously very slow.  This cannot be turned on in optimized

+ 1 - 0
panda/src/graph/config_graph.h

@@ -14,6 +14,7 @@ NotifyCategoryDecl(wrt, EXPCL_PANDA, EXPTP_PANDA);
 
 // Configure variables for graph package.
 extern const bool EXPCL_PANDA cache_wrt;
+extern const bool EXPCL_PANDA ambiguous_wrt_abort;
 extern const bool EXPCL_PANDA paranoid_wrt;
 extern const bool EXPCL_PANDA paranoid_graph;
 

+ 11 - 2
panda/src/graph/matrixTransition.I

@@ -176,8 +176,17 @@ internal_compare_to(const NodeTransition *other) const {
   const MatrixTransition<Matrix> *ot;
   DCAST_INTO_R(ot, other, false);
 
-  //  return _matrix.compare_to(ot->_matrix);
-  return this - other;
+  // Should we bother comparing matrices componentwise, or should we
+  // just assume that any two different Matrix pointers are probably
+  // different matrices?
+
+  // For now, we compare componentwise.  It makes paranoid_wrt more
+  // sensible, and it doesn't seem to make a big different to
+  // performance.
+  return _matrix.compare_to(ot->_matrix, 0.001);
+
+  // Uncomment this line instead to compare matrices pointerwise.
+  //  return this - other;
 }
 
 ////////////////////////////////////////////////////////////////////

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

@@ -232,7 +232,7 @@ get_child(TypeHandle type, int index) const {
 ////////////////////////////////////////////////////////////////////
 bool Node::
 sub_render(const AllAttributesWrapper &, AllTransitionsWrapper &, 
-	   GraphicsStateGuardianBase *) {
+	   RenderTraverser *) {
   return true;
 }
 

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

@@ -15,7 +15,7 @@
 #include <luse.h>
 
 class NodeAttributes;
-class GraphicsStateGuardianBase;
+class RenderTraverser;
 class AllAttributesWrapper;
 class AllTransitionsWrapper;
 class BamWriter;
@@ -70,7 +70,7 @@ public:
   // It may or may not intercept the render traversal.
   virtual bool sub_render(const AllAttributesWrapper &attrib,
 			  AllTransitionsWrapper &trans,
-			  GraphicsStateGuardianBase *gsgbase);
+			  RenderTraverser *trav);
   virtual bool has_sub_render() const;
 
 PUBLISHED:

+ 14 - 0
panda/src/graph/nodeRelation.I

@@ -315,6 +315,20 @@ compare_transitions_to(const NodeRelation *arc) const {
   return _transitions.compare_to(arc->_transitions);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: NodeRelation::get_last_update
+//       Access: Public
+//  Description: Returns the sequence number associated with the last
+//               time this arc was changed, for instance to change its
+//               state or to reparent it.  The only sensible thing you
+//               can do with this is store it and note whether it
+//               increases.
+////////////////////////////////////////////////////////////////////
+INLINE UpdateSeq NodeRelation::
+get_last_update() const {
+  return _last_update;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: NodeRelation::create_typed_arc
 //       Access: Public, Static

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

@@ -382,11 +382,11 @@ compose_transitions_from(const NodeTransitions &trans) {
 bool NodeRelation::
 sub_render_trans(const AllAttributesWrapper &attrib,
 		 AllTransitionsWrapper &trans,
-		 GraphicsStateGuardianBase *gsgbase) {
+		 RenderTraverser *trav) {
   bool all_true = true;
   NodeTransitions::const_iterator ti;
   for (ti = _transitions.begin(); ti != _transitions.end(); ++ti) {
-    if (!(*ti).second->sub_render(this, attrib, trans, gsgbase)) {
+    if (!(*ti).second->sub_render(this, attrib, trans, trav)) {
       all_true = false;
     }
   }
@@ -449,7 +449,7 @@ attach() {
 
   // Blow out the cache and increment the current update sequence.
   _net_transitions.clear();
-  ++last_graph_update[_type];
+  _last_update = ++last_graph_update[_type];
 
   _parent->force_bound_stale();
   mark_bound_stale();
@@ -491,7 +491,7 @@ detach() {
 
   // Blow out the cache and increment the current update sequence.
   _net_transitions.clear();
-  ++last_graph_update[_type];
+  _last_update = ++last_graph_update[_type];
 
   return result;
 }
@@ -522,7 +522,7 @@ detach_below() {
 
   // Blow out the cache and increment the current update sequence.
   _net_transitions.clear();
-  ++last_graph_update[_type];
+  _last_update = ++last_graph_update[_type];
 
   return result;
 }
@@ -544,7 +544,7 @@ changed_transition(TypeHandle trans_type) {
   if (_net_transitions != (NodeTransitionCache *)NULL) {
     _net_transitions->clear_transition(trans_type);
   }
-  last_graph_update[get_type()]++;
+  _last_update = ++last_graph_update[get_type()];
 }
 
 ////////////////////////////////////////////////////////////////////

+ 9 - 1
panda/src/graph/nodeRelation.h

@@ -102,10 +102,12 @@ PUBLISHED:
 
   INLINE int compare_transitions_to(const NodeRelation *arc) const;
 
+  INLINE UpdateSeq get_last_update() const;
+
 public:
   bool sub_render_trans(const AllAttributesWrapper &attrib,
 			AllTransitionsWrapper &trans,
-			GraphicsStateGuardianBase *gsgbase);
+			RenderTraverser *trav);
   bool has_sub_render_trans() const;
 
 public:
@@ -154,6 +156,12 @@ private:
   PT(NodeTransitionCache) _net_transitions;
   Node *_top_subtree;
 
+  // This is updated with the current update sequence each time the
+  // arc is changed (for instance, to change its state or to reparent
+  // it or something).  It exists to support caching in the cull
+  // traversal.
+  UpdateSeq _last_update;
+
 public:
   static void register_with_read_factory(void);
   virtual void write_datagram(BamWriter* manager, Datagram &me);  

+ 1 - 1
panda/src/graph/nodeTransition.cxx

@@ -36,7 +36,7 @@ get_handle() const {
 ////////////////////////////////////////////////////////////////////
 bool NodeTransition::
 sub_render(NodeRelation *, const AllAttributesWrapper &, 
-	   AllTransitionsWrapper &, GraphicsStateGuardianBase *) {
+	   AllTransitionsWrapper &, RenderTraverser *) {
   return true;
 }
 

+ 2 - 2
panda/src/graph/nodeTransition.h

@@ -15,7 +15,7 @@ class NodeAttribute;
 class NodeAttributes;
 class NodeTransitions;
 class NodeRelation;
-class GraphicsStateGuardianBase;
+class RenderTraverser;
 class AllAttributesWrapper;
 class AllTransitionsWrapper;
 class BamWriter;
@@ -67,7 +67,7 @@ public:
   virtual bool sub_render(NodeRelation *arc,
 			  const AllAttributesWrapper &attrib,
 			  AllTransitionsWrapper &trans,
-			  GraphicsStateGuardianBase *gsgbase);
+			  RenderTraverser *trav);
   virtual bool has_sub_render() const;
 
   virtual void output(ostream &out) const;

+ 176 - 32
panda/src/graph/wrt.I

@@ -1,13 +1,8 @@
-// Filename: wrt.cxx
+// Filename: wrt.I
 // Created by:  drose (26Oct98)
 // 
 ////////////////////////////////////////////////////////////////////
 
-#include "wrt.h"
-#include "nodeRelation.h"
-#include "node.h"
-#include "config_graph.h"
-
 ////////////////////////////////////////////////////////////////////
 //     Function: get_cached_net_transition
 //  Description: Returns the net transition from the root of the
@@ -148,10 +143,27 @@ get_cached_net_transition(const Node *node,
     }
 
     if (parent_arc == (NodeRelation *)NULL) {
+#ifndef NDEBUG
       // No, it wasn't mentioned.  Issue a warning and use the first
       // one.
-      graph_cat.warning()
-	<< *node << " has multiple parents; wrt() ambiguous.\n";
+      if (graph_cat.is_warning()) {
+	graph_cat.warning()
+	  << *node << " has " << urp.size() << " parents; wrt() ambiguous.\n"
+	  << "  parents are: ";
+	UpRelationPointers::const_iterator urpi;
+	urpi = urp.begin();
+	graph_cat.warning(false) << *(*urpi)->get_parent();
+	urpi++;
+	while (urpi != urp.end()) {  
+	  graph_cat.warning(false) << ", " << *(*urpi)->get_parent();
+	  urpi++;
+	}
+	graph_cat.warning(false) << "\n";
+      }
+      if (ambiguous_wrt_abort) {
+	abort();
+      }
+#endif
       parent_arc = *(urp.begin());
     }
   }
@@ -172,6 +184,7 @@ get_cached_net_transition(const Node *node,
   }
 }
 
+#ifndef NDEBUG
 ////////////////////////////////////////////////////////////////////
 //     Function: get_uncached_net_transition
 //  Description: Returns the net transition from the root of the graph
@@ -238,10 +251,27 @@ get_uncached_net_transition(const Node *node,
     }
 
     if (parent_arc == (NodeRelation *)NULL) {
+#ifndef NDEBUG
       // No, it wasn't mentioned.  Issue a warning and use the first
-      // one.
-      graph_cat.warning()
-	<< *node << " has multiple parents; wrt() ambiguous.\n";
+      // one. 
+      if (graph_cat.is_warning()) {
+	graph_cat.warning()
+	  << *node << " has " << urp.size() << " parents; wrt() ambiguous.\n"
+	  << "  parents are:";
+	UpRelationPointers::const_iterator urpi;
+	urpi = urp.begin();
+	graph_cat.warning(false) << *(*urpi)->get_parent();
+	urpi++;
+	while (urpi != urp.end()) {  
+	  graph_cat.warning(false) << ", " << *(*urpi)->get_parent();
+	  urpi++;
+	}
+	graph_cat.warning(false) << "\n";
+      }
+      if (ambiguous_wrt_abort) {
+	abort();
+      }
+#endif
       parent_arc = *(urp.begin());
     }
   }
@@ -256,7 +286,7 @@ get_uncached_net_transition(const Node *node,
 
   result.compose_in_place(next);
 }
-
+#endif
 
 
 ////////////////////////////////////////////////////////////////////
@@ -295,8 +325,11 @@ cached_wrt_base(const Node *from,
     if (check_from_trans.compare_to(net_from_trans) != 0) {
       graph_cat.warning()
 	<< "WRT cache from " << *from << " is invalid!\n"
-	<< "  cached value is " << net_from_trans << "\n"
-	<< "  should be " << check_from_trans << "\n";
+	<< "  cached value is:\n";
+      net_from_trans.write(graph_cat.warning(false), 4);
+      graph_cat.warning(false)
+	<< "  should be:\n";
+      check_from_trans.write(graph_cat.warning(false), 4);
       net_from_trans = check_from_trans;
     }
     
@@ -305,8 +338,11 @@ cached_wrt_base(const Node *from,
     if (check_to_trans.compare_to(result) != 0) {
       graph_cat.warning()
 	<< "WRT cache to " << *to << " is invalid!\n"
-	<< "  cached value is " << result << "\n"
-	<< "  should be " << check_to_trans << "\n";
+	<< "  cached value is:\n";
+      result.write(graph_cat.warning(false), 4);
+      graph_cat.warning(false)
+	<< "  should be:\n";
+      check_to_trans.write(graph_cat.warning(false), 4);
       result = check_to_trans;
     }
   }
@@ -315,6 +351,7 @@ cached_wrt_base(const Node *from,
   result.invert_compose_in_place(net_from_trans);
 }
 
+#ifndef NDEBUG
 ////////////////////////////////////////////////////////////////////
 //     Function: uncached_wrt_base
 //  Description: The implementation of wrt_base, below, but never
@@ -328,8 +365,6 @@ uncached_wrt_base(const Node *from,
 		  InputIterator2 to_arcs_begin, InputIterator2 to_arcs_end,
 		  TransitionWrapper &result,
 		  TypeHandle graph_type) {
-  //  UpdateSeq now = last_graph_update[graph_type];
-
   TransitionWrapper net_from_trans = TransitionWrapper::init_from(result);
   get_uncached_net_transition(from, from_arcs_begin, from_arcs_end,
 			      net_from_trans, graph_type);
@@ -337,6 +372,7 @@ uncached_wrt_base(const Node *from,
 			      result, graph_type);
   result.invert_compose_in_place(net_from_trans);
 }
+#endif
 
 ////////////////////////////////////////////////////////////////////
 //     Function: wrt_base
@@ -362,15 +398,17 @@ wrt_base(const Node *from,
 	 InputIterator2 to_arcs_begin, InputIterator2 to_arcs_end,
 	 TransitionWrapper &result,
 	 TypeHandle graph_type) {
-  if (cache_wrt) {
-    cached_wrt_base(from, from_arcs_begin, from_arcs_end,
-		    to, to_arcs_begin, to_arcs_end,
-		    result, graph_type);
-  } else {
+#ifndef NDEBUG
+  if (!cache_wrt) {
     uncached_wrt_base(from, from_arcs_begin, from_arcs_end,
 		      to, to_arcs_begin, to_arcs_end,
 		      result, graph_type);
+    return;
   }
+#endif
+  cached_wrt_base(from, from_arcs_begin, from_arcs_end,
+		  to, to_arcs_begin, to_arcs_end,
+		  result, graph_type);
 }
 
 template<class TransitionWrapper>
@@ -417,6 +455,7 @@ wrt(const Node *from,
 	   result, graph_type);
 }
 
+#ifndef NDEBUG
 template<class TransitionWrapper>
 INLINE void
 uncached_wrt(const Node *from, const Node *to,
@@ -458,17 +497,78 @@ uncached_wrt(const Node *from,
 		    to, to_arcs_begin, to_arcs_end,
 		    result, graph_type);
 }
+#endif
 
+#ifndef NDEBUG
 template<class TransitionWrapper>
 Node *
-wrt_subtree(NodeRelation *arc, Node *to, TransitionWrapper &result, 
-	    TypeHandle graph_type) {
-  if (!cache_wrt) {
-    // If we aren't caching wrt, do this the hard way.
-    uncached_wrt(arc->get_child(), &arc, &arc + 1, to, result, graph_type);
+get_uncached_wrt_subtree(Node *node, Node *to, TransitionWrapper &result, 
+			 TypeHandle graph_type) {
+  nassertr(node != (Node *)NULL, to);
+  UpRelations::const_iterator uri;
+  uri = node->_parents.find(graph_type);
+
+  if (uri == node->_parents.end()) {
+    // This node has no parents.  Stop here.
+    result.make_identity();
+    return to;
+  }
+
+  const UpRelationPointers &urp = (*uri).second;
+  if (urp.empty()) {
+    // Again, no parents.  Stop here.
+    result.make_identity();
     return to;
   }
 
+  if (node == to) {
+    // We've reached our stopping point.  Stop here.
+    result.make_identity();
+    return node;
+  }
+
+  if (urp.size() != 1) {
+    // There are multiple parents, so stop here.
+    result.make_identity();
+    return node;
+  }
+
+  const NodeRelation *parent_arc = *(urp.begin());
+
+  Node *stop = 
+    get_uncached_wrt_subtree(parent_arc->get_parent(), to,
+			     result, graph_type);
+
+  TransitionWrapper next = TransitionWrapper::init_from(result);
+  next.extract_from(parent_arc);
+
+  result.compose_in_place(next);
+
+  return stop;
+}
+
+template<class TransitionWrapper>
+INLINE Node *
+uncached_wrt_subtree(NodeRelation *arc, Node *to, TransitionWrapper &result, 
+		     TypeHandle graph_type) {
+  Node *stop = 
+    get_uncached_wrt_subtree(arc->get_parent(), to,
+			     result, graph_type);
+
+  TransitionWrapper next = TransitionWrapper::init_from(result);
+  next.extract_from(arc);
+
+  result.compose_in_place(next);
+
+  return stop;
+}
+
+#endif
+
+template<class TransitionWrapper>
+Node *
+cached_wrt_subtree(NodeRelation *arc, Node *to, TransitionWrapper &result, 
+		   TypeHandle graph_type) {
   UpdateSeq now = last_graph_update[graph_type];
 
   // First, determine the net transition up to the top of the current
@@ -507,8 +607,34 @@ wrt_subtree(NodeRelation *arc, Node *to, TransitionWrapper &result,
     // Now check the results.
     TransitionWrapper check_trans =
       TransitionWrapper::init_from(result);
-    uncached_wrt(arc->get_child(), &arc, &arc + 1, to, check_trans, 
-		 graph_type);
+    Node *top_subtree_3 = 
+      uncached_wrt_subtree(arc, to, check_trans, graph_type);
+
+    if (top_subtree_3 != top_subtree) {
+      graph_cat.warning()
+	<< "WRT subtree cache from " << *arc->get_child() << " to ";
+      if (to == (Node *)NULL) {
+	graph_cat.warning(false) << "(top)";
+      } else {
+	graph_cat.warning(false) << *to;
+      }
+      graph_cat.warning(false)
+	<< " computes incorrect top_subtree!\n"
+	<< "  computed ";
+      if (top_subtree == (Node *)NULL) {
+	graph_cat.warning(false) << "(top)\n";
+      } else {
+	graph_cat.warning(false) << *top_subtree << "\n";
+      }
+      graph_cat.warning(false)
+	<< "  should be ";
+      if (top_subtree_3 == (Node *)NULL) {
+	graph_cat.warning(false) << "(top)\n";
+      } else {
+	graph_cat.warning(false) << *top_subtree_3 << "\n";
+      }
+      top_subtree = top_subtree_3;
+    }
 
     if (check_trans.compare_to(result) != 0) {
       graph_cat.warning()
@@ -520,8 +646,11 @@ wrt_subtree(NodeRelation *arc, Node *to, TransitionWrapper &result,
       }
       graph_cat.warning(false)
 	<< " is invalid!\n"
-	<< "  cached value is " << result << "\n"
-	<< "  should be " << check_trans << "\n";
+	<< "  cached value is:\n";
+      result.write(graph_cat.warning(false), 4);
+      graph_cat.warning(false)
+	<< "  should be:\n";
+      check_trans.write(graph_cat.warning(false), 4);
       result = check_trans;
     }
   }
@@ -529,3 +658,18 @@ wrt_subtree(NodeRelation *arc, Node *to, TransitionWrapper &result,
 
   return top_subtree;
 }
+
+template<class TransitionWrapper>
+INLINE Node *
+wrt_subtree(NodeRelation *arc, Node *to, TransitionWrapper &result, 
+	    TypeHandle graph_type) {
+#ifndef NDEBUG
+  if (!cache_wrt) {
+    // If we aren't caching wrt, do this the hard way.
+    return uncached_wrt_subtree(arc, to, result, graph_type);
+  }
+#endif
+  return cached_wrt_subtree(arc, to, result, graph_type);
+}
+
+

+ 14 - 8
panda/src/graph/wrt.h

@@ -8,6 +8,10 @@
 
 #include <pandabase.h>
 
+#include "nodeRelation.h"
+#include "node.h"
+#include "config_graph.h"
+
 #include <typeHandle.h>
 
 class Node;
@@ -57,6 +61,7 @@ wrt(const Node *from,
     InputIterator2 to_arcs_begin, InputIterator2 to_arcs_end,
     TransitionWrapper &result, TypeHandle graph_type);
 
+#ifndef NDEBUG
 // Similar to the above, but always uncached.  Useful mainly for
 // debugging, or when you suspect the cache is invalid.  Also note
 // that you can configure 'cache-wrt' or 'paranoid-wrt' to disable or
@@ -87,17 +92,18 @@ uncached_wrt(const Node *from,
 	     const Node *to,
 	     InputIterator2 to_arcs_begin, InputIterator2 to_arcs_end,
 	     TransitionWrapper &result, TypeHandle graph_type);
+#endif
 
 
 // The following function is a bit different.  Rather than computing
 // the relative transform between two nodes, it computes the net
-// transform along the shortest unambigous path from the indicated arc
-// towards the root.  That is, this is the wrt between the child of
-// the indicated arc and the closest ancestor node that has multiple
-// parents, or the root of the scene graph if the arc only appears
-// once in the scene graph.  The return value is the particular node
-// with multiple parents at which the wrt stopped, or NULL if it went
-// all the way to the root.
+// transform along the shortest unambiguous path from the indicated
+// arc towards the root.  That is, this is the wrt between the child
+// of the indicated arc and the closest ancestor node that has
+// multiple parents, or the root of the scene graph if the arc only
+// appears once in the scene graph.  The return value is the
+// particular node with multiple parents at which the wrt stopped, or
+// NULL if it went all the way to the root.
 
 // This is extended just a bit further by allowing the user to specify
 // a "to" node.  This must be either NULL, or the expected top node,
@@ -113,7 +119,7 @@ uncached_wrt(const Node *from,
 // CullTraverser) that needs to cache a wrt-type value for many nodes
 // across the entire tree.
 template<class TransitionWrapper>
-Node *
+INLINE Node *
 wrt_subtree(NodeRelation *arc, Node *to, TransitionWrapper &result, 
 	    TypeHandle graph_type);
 

+ 32 - 25
panda/src/gui/guiManager.cxx

@@ -18,7 +18,8 @@
 
 GuiManager::GuiMap* GuiManager::_map = (GuiManager::GuiMap*)0L;
 
-GuiManager* GuiManager::get_ptr(GraphicsWindow* w, MouseAndKeyboard* mak) {
+GuiManager* GuiManager::get_ptr(GraphicsWindow* w, MouseAndKeyboard* mak,
+				Node *root2d) {
   GuiManager* ret;
   if (_map == (GuiMap*)0L) {
     if (gui_cat->is_debug())
@@ -83,30 +84,36 @@ GuiManager* GuiManager::get_ptr(GraphicsWindow* w, MouseAndKeyboard* mak) {
 			 << watcher->get_leave_pattern()
 			 << "' with 'gui-out-%r'" << endl;
     watcher->set_leave_pattern("gui-out-%r");
-    // next, create a 2d layer for the GUI stuff to live in.
-    Node* root2d_top = new NamedNode("GUI_top");
-    Node* root2d = new NamedNode("GUI");
-    NodeRelation* root2d_arc = new RenderRelation(root2d_top, root2d);
-    root2d_arc->set_transition(new DepthTestTransition(DepthTestProperty::M_none), 1);
-    root2d_arc->set_transition(new DepthWriteTransition(DepthWriteTransition::off()), 1);
-    root2d_arc->set_transition(new LightTransition(LightTransition::all_off()), 1);
-    root2d_arc->set_transition(new MaterialTransition(MaterialTransition::off()), 1);
-    root2d_arc->set_transition(new CullFaceTransition(CullFaceProperty::M_cull_none), 1);
-    PT(Camera) cam = new Camera("GUI_cam");
-    new RenderRelation(root2d, cam);
-    cam->set_scene(root2d_top);
-    Frustumf frust2d;
-    frust2d.make_ortho_2D();
-    cam->set_projection(OrthoProjection(frust2d));
-    GraphicsChannel *chan = w->get_channel(0);  // root/full-window channel
-    nassertr(chan != (GraphicsChannel*)0L, NULL);
-    GraphicsLayer *layer = chan->make_layer();
-    nassertr(layer != (GraphicsLayer*)0L, NULL);
-    DisplayRegion *dr = layer->make_display_region();
-    nassertr(dr != (DisplayRegion*)0L, NULL);
-    dr->set_camera(cam);
-    if (gui_cat->is_debug())
-      gui_cat->debug() << "2D layer created" << endl;
+
+    if (root2d == (Node *)NULL) {
+      // If we weren't given a 2-d scene graph, then create one now.
+      // It lives in its own layer.
+
+      Node* root2d_top = new NamedNode("GUI_top");
+      root2d = new NamedNode("GUI");
+      NodeRelation* root2d_arc = new RenderRelation(root2d_top, root2d);
+      root2d_arc->set_transition(new DepthTestTransition(DepthTestProperty::M_none), 1);
+      root2d_arc->set_transition(new DepthWriteTransition(DepthWriteTransition::off()), 1);
+      root2d_arc->set_transition(new LightTransition(LightTransition::all_off()), 1);
+      root2d_arc->set_transition(new MaterialTransition(MaterialTransition::off()), 1);
+      root2d_arc->set_transition(new CullFaceTransition(CullFaceProperty::M_cull_none), 1);
+      PT(Camera) cam = new Camera("GUI_cam");
+      new RenderRelation(root2d, cam);
+      cam->set_scene(root2d_top);
+      Frustumf frust2d;
+      frust2d.make_ortho_2D();
+      cam->set_projection(OrthoProjection(frust2d));
+      GraphicsChannel *chan = w->get_channel(0);  // root/full-window channel
+      nassertr(chan != (GraphicsChannel*)0L, NULL);
+      GraphicsLayer *layer = chan->make_layer();
+      nassertr(layer != (GraphicsLayer*)0L, NULL);
+      DisplayRegion *dr = layer->make_display_region();
+      nassertr(dr != (DisplayRegion*)0L, NULL);
+      dr->set_camera(cam);
+      if (gui_cat->is_debug())
+	gui_cat->debug() << "2D layer created" << endl;
+    }
+
     // now make the manager for this window
     ret = new GuiManager(watcher, root2d);
     if (gui_cat->is_debug())

+ 2 - 1
panda/src/gui/guiManager.h

@@ -32,7 +32,8 @@ private:
   INLINE GuiManager(MouseWatcher*, Node*);
 
 PUBLISHED:
-  static GuiManager* get_ptr(GraphicsWindow*, MouseAndKeyboard*);
+  static GuiManager* get_ptr(GraphicsWindow*, MouseAndKeyboard*,
+			     Node *root2d = (Node *)NULL);
 
   void add_region(GuiRegion*);
   void add_label(GuiLabel*);

+ 1 - 1
panda/src/light/spotlight.cxx

@@ -76,7 +76,7 @@ write(ostream &out, int indent_level) const {
 ////////////////////////////////////////////////////////////////////
 float Spotlight::get_cutoff_angle(void) const
 {
-  const Projection* proj = get_projection();
+  Projection* proj = ((ProjectionNode *)this)->get_projection();
   Frustumf frustum;
   float cutoff = 0;
   if (proj->get_type() == PerspectiveProjection::get_class_type()) {

+ 49 - 12
panda/src/putil/updateSeq.I

@@ -11,7 +11,7 @@
 ////////////////////////////////////////////////////////////////////
 INLINE UpdateSeq::
 UpdateSeq() {
-  _seq = (unsigned)SC_initial;
+  _seq = (unsigned int)SC_initial;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -32,7 +32,19 @@ initial() {
 INLINE UpdateSeq UpdateSeq::
 old() {
   UpdateSeq result;
-  result._seq = (unsigned)SC_old;
+  result._seq = (unsigned int)SC_old;
+  return result;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: UpdateSeq::fresh (named constructor)
+//       Access: Public, Static
+//  Description: Returns an UpdateSeq in the 'fresh' state.
+////////////////////////////////////////////////////////////////////
+INLINE UpdateSeq UpdateSeq::
+fresh() {
+  UpdateSeq result;
+  result._seq = (unsigned int)SC_fresh;
   return result;
 }
 
@@ -64,7 +76,7 @@ operator = (const UpdateSeq &copy) {
 ////////////////////////////////////////////////////////////////////
 INLINE void UpdateSeq::
 clear() {
-  _seq = (unsigned)SC_initial;
+  _seq = (unsigned int)SC_initial;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -75,7 +87,7 @@ clear() {
 ////////////////////////////////////////////////////////////////////
 INLINE bool UpdateSeq::
 is_initial() const {
-  return _seq == SC_initial;
+  return _seq == (unsigned int)SC_initial;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -85,18 +97,37 @@ is_initial() const {
 ////////////////////////////////////////////////////////////////////
 INLINE bool UpdateSeq::
 is_old() const {
-  return _seq == SC_old;
+  return _seq == (unsigned int)SC_old;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: UpdateSeq::is_fresh
+//       Access: Public
+//  Description: Returns true if the UpdateSeq is in the 'fresh'
+//               state.
+////////////////////////////////////////////////////////////////////
+INLINE bool UpdateSeq::
+is_fresh() const {
+  return _seq == (unsigned int)SC_fresh;
 }
 
 ////////////////////////////////////////////////////////////////////
 //     Function: UpdateSeq::is_special
 //       Access: Public
 //  Description: Returns true if the UpdateSeq is in any special
-//               states, i.e. 'initial' or 'old'.
+//               states, i.e. 'initial', 'old', or 'fresh'.
 ////////////////////////////////////////////////////////////////////
 INLINE bool UpdateSeq::
 is_special() const {
-  return _seq <= (unsigned)SC_old;
+  switch (_seq) {
+  case (unsigned int)SC_initial:
+  case (unsigned int)SC_old:
+  case (unsigned int)SC_fresh:
+    return true;
+
+  default:
+    return false;
+  }
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -127,10 +158,12 @@ operator != (const UpdateSeq &other) const {
 INLINE bool UpdateSeq::
 operator < (const UpdateSeq &other) const {
   // The special cases of SC_initial or SC_old are less than all other
-  // non-special numbers, and SC_initial is less than SC_old.  For all
-  // other cases, we use a circular comparision such that n < m iff
-  // (signed)(n - m) < 0.
-  return (is_special() || other.is_special()) ? (_seq < other._seq) :
+  // non-special numbers, and SC_initial is less than SC_old.  The
+  // special case of SC_fresh is greater than all other non-special
+  // numbers.  For all other cases, we use a circular comparision such
+  // that n < m iff (signed)(n - m) < 0.
+  return
+    (is_special() || other.is_special()) ? (_seq < other._seq) :
     ((signed int)(_seq - other._seq) < 0);
 }
 
@@ -155,7 +188,7 @@ operator ++ () {
   if (is_special()) {
     // Oops, wraparound.  We don't want to confuse the new value
     // with our special cases.
-    _seq = (unsigned)SC_old + 1;
+    _seq = (unsigned int)SC_old + 1;
   }
     
   return *this;
@@ -189,6 +222,10 @@ output(ostream &out) const {
     out << "old";
     break;
 
+  case SC_fresh:
+    out << "fresh";
+    break;
+
   default:
     out << _seq;
   }

+ 8 - 4
panda/src/putil/updateSeq.h

@@ -22,15 +22,17 @@
 //               first created.  This sequence is older than any other
 //               sequence number.  Secondly, a sequence number may be
 //               explicitly set to 'old'.  This is older than any
-//               other sequence number except 'initial'.  All other
-//               sequences are numeric and are monotonically
-//               increasing.
+//               other sequence number except 'initial'.  Finally, we
+//               have the explicit number 'fresh', which is newer
+//               than any other sequence number.  All other sequences
+//               are numeric and are monotonically increasing.
 ////////////////////////////////////////////////////////////////////
 class EXPCL_PANDA UpdateSeq {
 public:
   INLINE UpdateSeq();
   INLINE static UpdateSeq initial();
   INLINE static UpdateSeq old();
+  INLINE static UpdateSeq fresh();
 
   INLINE UpdateSeq(const UpdateSeq &copy);
   INLINE UpdateSeq &operator = (const UpdateSeq &copy);
@@ -39,6 +41,7 @@ public:
 
   INLINE bool is_initial() const;
   INLINE bool is_old() const;
+  INLINE bool is_fresh() const;
   INLINE bool is_special() const;
 
   INLINE bool operator == (const UpdateSeq &other) const;
@@ -55,9 +58,10 @@ private:
   enum SpecialCases {
     SC_initial = 0,
     SC_old = 1,
+    SC_fresh = ~0,
   };
 
-  unsigned _seq;
+  unsigned int _seq;
 };
 
 INLINE ostream &operator << (ostream &out, const UpdateSeq &value);

+ 3 - 3
panda/src/ribgsg/ribGraphicsStateGuardian.cxx

@@ -243,7 +243,7 @@ render_frame(const AllAttributesWrapper &initial_state) {
 //               may be modified during rendering.
 ////////////////////////////////////////////////////////////////////
 void RIBGraphicsStateGuardian::
-render_scene(Node *root, const ProjectionNode *projnode,
+render_scene(Node *root, ProjectionNode *projnode,
 	     const AllAttributesWrapper &initial_state) {
   _current_root_node = root;
 
@@ -262,10 +262,10 @@ render_scene(Node *root, const ProjectionNode *projnode,
 ////////////////////////////////////////////////////////////////////
 void RIBGraphicsStateGuardian::
 render_subgraph(RenderTraverser *traverser, 
-		Node *subgraph, const ProjectionNode *projnode,
+		Node *subgraph, ProjectionNode *projnode,
 		const AllAttributesWrapper &initial_state,
 		const AllTransitionsWrapper &net_trans) {
-  const ProjectionNode *old_projection_node = _current_projection_node;
+  ProjectionNode *old_projection_node = _current_projection_node;
   _current_projection_node = projnode;
 
   (*_output) << "\n";

+ 2 - 2
panda/src/ribgsg/ribGraphicsStateGuardian.h

@@ -40,10 +40,10 @@ public:
   virtual void prepare_display_region();
 
   virtual void render_frame(const AllAttributesWrapper &initial_state);
-  virtual void render_scene(Node *root, const ProjectionNode *projnode,
+  virtual void render_scene(Node *root, ProjectionNode *projnode,
 			    const AllAttributesWrapper &initial_state);
   virtual void render_subgraph(RenderTraverser *traverser, 
-			       Node *subgraph, const ProjectionNode *projnode,
+			       Node *subgraph, ProjectionNode *projnode,
 			       const AllAttributesWrapper &initial_state,
 			       const AllTransitionsWrapper &net_trans);
   virtual void render_subgraph(RenderTraverser *traverser,

+ 5 - 3
panda/src/sgattrib/billboardTransition.cxx

@@ -9,6 +9,7 @@
 #include "renderRelation.h"
 
 #include <graphicsStateGuardian.h>
+#include <renderTraverser.h>
 #include <projectionNode.h>
 #include <look_at.h>
 #include <nodeTransitionWrapper.h>
@@ -39,9 +40,9 @@ make_copy() const {
 ////////////////////////////////////////////////////////////////////
 bool BillboardTransition::
 sub_render(NodeRelation *arc, const AllAttributesWrapper &,
-	   AllTransitionsWrapper &trans, GraphicsStateGuardianBase *gsgbase) {
+	   AllTransitionsWrapper &trans, RenderTraverser *trav) {
   Node *node = arc->get_child();
-  GraphicsStateGuardian *gsg = DCAST(GraphicsStateGuardian, gsgbase);
+  GraphicsStateGuardian *gsg = trav->get_gsg();
 
   // Get the current camera from the gsg
   const ProjectionNode *camera = gsg->get_current_projection_node();
@@ -50,7 +51,8 @@ sub_render(NodeRelation *arc, const AllAttributesWrapper &,
   // And the relative coordinate space.
   LMatrix4f rel_mat;
   NodeTransitionWrapper ntw(TransformTransition::get_class_type());
-  wrt(camera, node, (&arc), (&arc) + 1, ntw, RenderRelation::get_class_type());
+  wrt(camera, node, trav->begin(), trav->end(), 
+      ntw, RenderRelation::get_class_type());
   TransformTransition *tt;
   if (!get_transition_into(tt, ntw)) {
     // No relative transform.

+ 1 - 1
panda/src/sgattrib/billboardTransition.h

@@ -45,7 +45,7 @@ public:
   virtual bool sub_render(NodeRelation *arc,
 			  const AllAttributesWrapper &attrib,
 			  AllTransitionsWrapper &trans,
-			  GraphicsStateGuardianBase *gsgbase);
+			  RenderTraverser *trav);
   virtual bool has_sub_render() const;
 
   virtual void output(ostream &out) const;

+ 3 - 2
panda/src/sgattrib/drawBoundsTransition.cxx

@@ -20,6 +20,7 @@
 #include <allTransitionsWrapper.h>
 #include <allAttributesWrapper.h>
 #include <graphicsStateGuardian.h>
+#include <renderTraverser.h>
 #include <geomSphere.h>
 
 TypeHandle DrawBoundsTransition::_type_handle;
@@ -70,8 +71,8 @@ make_copy() const {
 ////////////////////////////////////////////////////////////////////
 bool DrawBoundsTransition::
 sub_render(NodeRelation *arc, const AllAttributesWrapper &attrib,
-	   AllTransitionsWrapper &, GraphicsStateGuardianBase *gsgbase) {
-  GraphicsStateGuardian *gsg = DCAST(GraphicsStateGuardian, gsgbase);
+	   AllTransitionsWrapper &, RenderTraverser *trav) {
+  GraphicsStateGuardian *gsg = trav->get_gsg();
 
   const BoundingVolume &vol = arc->get_bound();
   if (!vol.is_empty() && !vol.is_infinite()) {

+ 1 - 1
panda/src/sgattrib/drawBoundsTransition.h

@@ -29,7 +29,7 @@ public:
   virtual bool sub_render(NodeRelation *arc,
 			  const AllAttributesWrapper &attrib,
 			  AllTransitionsWrapper &trans,
-			  GraphicsStateGuardianBase *gsgbase);
+			  RenderTraverser *trav);
   virtual bool has_sub_render() const;
 
   NodeAttributes _outside_attrib;

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

@@ -29,7 +29,7 @@ make_copy() const {
 ////////////////////////////////////////////////////////////////////
 bool PruneTransition::
 sub_render(NodeRelation *, const AllAttributesWrapper &,
-	   AllTransitionsWrapper &, GraphicsStateGuardianBase *) {
+	   AllTransitionsWrapper &, RenderTraverser *) {
   return false; 
 }
 

+ 1 - 1
panda/src/sgattrib/pruneTransition.h

@@ -29,7 +29,7 @@ public:
   virtual bool sub_render(NodeRelation *arc,
 			  const AllAttributesWrapper &attrib,
 			  AllTransitionsWrapper &trans,
-			  GraphicsStateGuardianBase *gsgbase);
+			  RenderTraverser *trav);
   virtual bool has_sub_render() const;
 
 public:

+ 30 - 13
panda/src/sgraph/projectionNode.cxx

@@ -30,26 +30,43 @@ make_copy() const {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: set_projection 
-//       Access:
-//  Description:
+//       Access: Public
+//  Description: Sets up the ProjectionNode using a copy of the
+//               indicated Projection.  If the original Projection is
+//               changed or destroyed, this ProjectionNode is not
+//               affected.
 ////////////////////////////////////////////////////////////////////
-void ProjectionNode::set_projection( const Projection& projection )
-{
-    _projection = projection.make_copy();
+void ProjectionNode::
+set_projection(const Projection &projection) {
+  _projection = projection.make_copy();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: share_projection 
+//       Access: Public
+//  Description: This is similar to set_projection(), but the
+//               Projection is assigned by pointer.  If the original
+//               Projection is changed, this ProjectionNode is
+//               immediately affected.
+////////////////////////////////////////////////////////////////////
+void ProjectionNode::
+share_projection(Projection *projection) {
+  _projection = projection;
 }
 
 ////////////////////////////////////////////////////////////////////
 //     Function: get_projection 
-//       Access:
-//  Description:
+//       Access: Public
+//  Description: Returns a pointer to particular Projection associated
+//               with this ProjectionNode.
 ////////////////////////////////////////////////////////////////////
-const Projection* ProjectionNode::get_projection( void ) const
-{
-  if (_projection == NULL) {
-    // If we have no projection yet, just return a default projection.
+Projection *ProjectionNode::
+get_projection() {
+  if (_projection == (Projection *)NULL) {
+    // If we have no projection yet, give us a default Projection.
     Frustumf f;
-    static PerspectiveProjection default_projection(f);
-    return &default_projection;
+    _projection = new PerspectiveProjection(f);
   }
+
   return _projection;
 }

+ 5 - 5
panda/src/sgraph/projectionNode.h

@@ -24,7 +24,7 @@
 ////////////////////////////////////////////////////////////////////
 class EXPCL_PANDA ProjectionNode : public NamedNode {
 PUBLISHED:
-  INLINE ProjectionNode(const string& name = "");
+  INLINE ProjectionNode(const string &name = "");
 
 public:
   INLINE ProjectionNode(const ProjectionNode &copy);
@@ -33,12 +33,12 @@ public:
   virtual Node *make_copy() const;
 
 PUBLISHED:  
-  void set_projection( const Projection& projection );
-  const Projection* get_projection( void ) const;
+  void set_projection(const Projection &projection);
+  void share_projection(Projection *projection);
+  Projection *get_projection();
   
 protected:
-  
-  PT(Projection)    		_projection;
+  PT(Projection) _projection;
   
 public:
   

+ 92 - 3
panda/src/sgraph/renderTraverser.I

@@ -7,15 +7,78 @@
 ////////////////////////////////////////////////////////////////////
 //     Function: RenderTraverser::Constructor
 //       Access: Public
-//  Description: 
+//  Description: Creates a new RenderTraverser ready to traverse a
+//               scene graph beginning at the end of the arc chain
+//               indicated in arc_chain (either an empty ArcChain, or
+//               the result of get_arc_chain() from some other
+//               traverser), with the indicated graph_type (usually
+//               RenderRelation) and for the indicated gsg.
 ////////////////////////////////////////////////////////////////////
 INLINE RenderTraverser::
-RenderTraverser(GraphicsStateGuardian *gsg, TypeHandle graph_type) :
+RenderTraverser(GraphicsStateGuardian *gsg, TypeHandle graph_type,
+		const ArcChain &arc_chain) :
   _gsg(gsg),
-  _graph_type(graph_type)
+  _graph_type(graph_type),
+  _arc_chain(arc_chain)
 {
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: RenderTraverser::Destructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE RenderTraverser::
+~RenderTraverser() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: RenderTraverser::get_arc_chain
+//       Access: Public
+//  Description: Returns the complete chain of arcs from the root to
+//               the current node in the traversal.
+////////////////////////////////////////////////////////////////////
+INLINE const ArcChain &RenderTraverser::
+get_arc_chain() const {
+  return _arc_chain;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: RenderTraverser::begin
+//       Access: Public
+//  Description: Returns an iterator that can be used to traverse the
+//               list of arcs from the root to the current node in the
+//               traversal.
+////////////////////////////////////////////////////////////////////
+INLINE RenderTraverser::const_iterator RenderTraverser::
+begin() const {
+  return _arc_chain.begin();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: RenderTraverser::end
+//       Access: Public
+//  Description: Returns an iterator that can be used to traverse the
+//               list of arcs from the root to the current node in the
+//               traversal.
+////////////////////////////////////////////////////////////////////
+INLINE RenderTraverser::const_iterator RenderTraverser::
+end() const {
+  return _arc_chain.end();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: RenderTraverser::empty
+//       Access: Public
+//  Description: Returns true if the list of arcs returned by begin()
+//               .. end() is empty (i.e. begin() == end()), false
+//               otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool RenderTraverser::
+empty() const {
+  return _arc_chain.empty();
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: RenderTraverser::get_gsg
 //       Access: Public
@@ -35,3 +98,29 @@ INLINE TypeHandle RenderTraverser::
 get_graph_type() const {
   return _graph_type;
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: RenderTraverser::mark_forward_arc
+//       Access: Public
+//  Description: To be called by the derived class's forward_arc()
+//               method, this updates the list of arcs traversed to
+//               include the current arc.
+////////////////////////////////////////////////////////////////////
+INLINE void RenderTraverser::
+mark_forward_arc(NodeRelation *arc) {
+  _arc_chain.push_back(arc);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: RenderTraverser::mark_backward_arc
+//       Access: Public
+//  Description: To be called by the derived class's forward_arc()
+//               method, this updates the list of arcs traversed to
+//               remove the current arc.
+////////////////////////////////////////////////////////////////////
+INLINE void RenderTraverser::
+mark_backward_arc(NodeRelation *arc) {
+  nassertv(!_arc_chain.empty());
+  nassertv(_arc_chain.back() == arc);
+  _arc_chain.pop_back();
+}

+ 22 - 1
panda/src/sgraph/renderTraverser.h

@@ -8,11 +8,14 @@
 
 #include <pandabase.h>
 
+#include <arcChain.h>
 #include <typeHandle.h>
 #include <typedReferenceCount.h>
+#include <notify.h>
 
 class GraphicsStateGuardian;
 class Node;
+class NodeRelation;
 class AllAttributesWrapper;
 class AllTransitionsWrapper;
 
@@ -26,7 +29,18 @@ class AllTransitionsWrapper;
 ////////////////////////////////////////////////////////////////////
 class EXPCL_PANDA RenderTraverser : public TypedReferenceCount {
 public:
-  INLINE RenderTraverser(GraphicsStateGuardian *gsg, TypeHandle graph_type);
+  INLINE RenderTraverser(GraphicsStateGuardian *gsg, 
+			 TypeHandle graph_type,
+			 const ArcChain &arc_chain);
+  INLINE ~RenderTraverser();
+
+  typedef ArcChain::iterator iterator;
+  typedef ArcChain::const_iterator const_iterator;
+  
+  INLINE const ArcChain &get_arc_chain() const;
+  INLINE const_iterator begin() const;
+  INLINE const_iterator end() const;
+  INLINE bool empty() const;
 
 PUBLISHED:
   INLINE GraphicsStateGuardian *get_gsg() const;
@@ -41,9 +55,16 @@ PUBLISHED:
   virtual void output(ostream &out) const;
   virtual void write(ostream &out, int indent_level = 0) const;
 
+protected:  
+  // These methods are to be called by derived classes as we traverse
+  // each arc.  They update the arc list returned by begin()/end().
+  INLINE void mark_forward_arc(NodeRelation *arc);
+  INLINE void mark_backward_arc(NodeRelation *arc);
+
 protected:
   GraphicsStateGuardian *_gsg;
   TypeHandle _graph_type;
+  ArcChain _arc_chain;
 
 public:
   static TypeHandle get_class_type() {

+ 31 - 5
panda/src/sgraphutil/directRenderTraverser.cxx

@@ -7,6 +7,7 @@
 #include "config_sgraphutil.h"
 #include "frustumCullTraverser.h"
 
+#include <wrt.h>
 #include <geomNode.h>
 #include <graphicsStateGuardian.h>
 #include <geometricBoundingVolume.h>
@@ -18,6 +19,8 @@
 #include <transformTransition.h>
 #include <allAttributesWrapper.h>
 #include <allTransitionsWrapper.h>
+#include <transformTransition.h>
+#include <nodeTransitionWrapper.h>
 #include <decalTransition.h>
 #include <decalAttribute.h>
 #include <pStatTimer.h>
@@ -32,8 +35,9 @@ PStatCollector DirectRenderTraverser::_draw_pcollector =
 //  Description:
 ////////////////////////////////////////////////////////////////////
 DirectRenderTraverser::
-DirectRenderTraverser(GraphicsStateGuardian *gsg, TypeHandle graph_type) :
-  RenderTraverser(gsg, graph_type)
+DirectRenderTraverser(GraphicsStateGuardian *gsg, TypeHandle graph_type,
+		      const ArcChain &arc_chain) :
+  RenderTraverser(gsg, graph_type, arc_chain)
 {
 }
 
@@ -72,7 +76,22 @@ traverse(Node *root,
     level_state._decal_mode = decal_attrib->is_on();
   }
 
-  fc_traverse(root, *this, render_state, level_state,
+  // Determine the relative transform matrix from the camera to our
+  // starting node.  This is important for proper view-frustum
+  // culling.
+  LMatrix4f rel_from_camera;
+  NodeTransitionWrapper ntw(TransformTransition::get_class_type());
+  wrt(_gsg->get_current_projection_node(), root, begin(), end(),
+      ntw, get_graph_type());
+  const TransformTransition *tt;
+  if (get_transition_into(tt, ntw)) {
+    rel_from_camera = tt->get_matrix();
+  } else {
+    // No relative transform.
+    rel_from_camera = LMatrix4f::ident_mat();
+  }
+
+  fc_traverse(root, rel_from_camera, *this, render_state, level_state,
 	      _gsg, _graph_type);
 
   if (level_state._decal_mode && 
@@ -100,7 +119,7 @@ reached_node(Node *node, AllAttributesWrapper &render_state,
 
   AllTransitionsWrapper new_trans;
 
-  if (!node->sub_render(render_state, new_trans, _gsg)) {
+  if (!node->sub_render(render_state, new_trans, this)) {
     return false;
   }
   render_state.apply_in_place(new_trans);
@@ -146,16 +165,22 @@ forward_arc(NodeRelation *arc, AllTransitionsWrapper &trans,
   // return true.  For Shader types, this will fire off another render
   // and return false.
 
+  mark_forward_arc(arc);
+
   AllTransitionsWrapper::const_iterator nti;
   for (nti = trans.begin(); nti != trans.end(); ++nti) {
     NodeTransition *t = (*nti).second.get_trans();
     AllTransitionsWrapper new_trans;
-    if (!t->sub_render(arc, post, new_trans, _gsg)) {
+    if (!t->sub_render(arc, post, new_trans, this)) {
       carry_on = false;
     }
     post.apply_in_place(new_trans);
   }
 
+  if (!carry_on) {
+    mark_backward_arc(arc);
+  }
+
   return carry_on;
 }
 
@@ -169,6 +194,7 @@ void DirectRenderTraverser::
 backward_arc(NodeRelation *arc, AllTransitionsWrapper &,
 	     AllAttributesWrapper &, AllAttributesWrapper &post,
 	     const DirectRenderLevelState &level_state) {
+  mark_backward_arc(arc);
   if (level_state._decal_mode) {
     // Reset the state to redraw the base geometry.
     _gsg->set_state(post.get_attributes(), true);

+ 2 - 1
panda/src/sgraphutil/directRenderTraverser.h

@@ -36,7 +36,8 @@ class EXPCL_PANDA DirectRenderTraverser :
   public RenderTraverser, 
   public TraverserVisitor<AllTransitionsWrapper, DirectRenderLevelState> {
 public:
-  DirectRenderTraverser(GraphicsStateGuardian *gsg, TypeHandle graph_type);
+  DirectRenderTraverser(GraphicsStateGuardian *gsg, TypeHandle graph_type,
+			const ArcChain &arc_chain = ArcChain());
   virtual ~DirectRenderTraverser();
 
   virtual void traverse(Node *root, 

+ 5 - 8
panda/src/sgraphutil/frustumCullTraverser.I

@@ -4,11 +4,11 @@
 ////////////////////////////////////////////////////////////////////
 
 #include "frustumCullTraverser.h"
-#include "get_rel_pos.h"
 #include "config_sgraphutil.h"
 
 #include <notify.h>
 #include <nodeTransitionWrapper.h>
+#include <transformTransition.h>
 #include <wrt.h>
 
 ////////////////////////////////////////////////////////////////////
@@ -18,7 +18,7 @@
 ////////////////////////////////////////////////////////////////////
 template<class Visitor, class LevelState>
 FrustumCullTraverser<Visitor, LevelState>::
-FrustumCullTraverser(Node *root,
+FrustumCullTraverser(Node *root, const LMatrix4f &rel_from_camera,
 		     Visitor &visitor,
 		     const AttributeWrapper &initial_render_state,
 		     const LevelState &initial_level_state,
@@ -36,7 +36,7 @@ FrustumCullTraverser(Node *root,
     // If we're to be performing view-frustum culling, determine the
     // bounding volume associated with the current viewing frustum.
 
-    const ProjectionNode *camera = _gsg->get_current_projection_node();
+    ProjectionNode *camera = _gsg->get_current_projection_node();
     if (camera != (const ProjectionNode *)NULL) {
       const Projection *proj = camera->get_projection();
       nassertv(proj != (const Projection *)NULL);
@@ -49,17 +49,14 @@ FrustumCullTraverser(Node *root,
       }
     }
 
-    LMatrix4f mat;
-    get_rel_mat(_gsg->get_current_projection_node(), root, mat);
-
     local_frustum = DCAST(GeometricBoundingVolume, _view_frustum->make_copy());
-    local_frustum->xform(mat);
+    local_frustum->xform(rel_from_camera);
 
     if (sgraphutil_cat.is_spam()) {
       sgraphutil_cat.spam()
 	<< "Beginning frustum cull, frustum is: " << *local_frustum << "\n"
 	<< "Transform is:\n";
-      mat.write(sgraphutil_cat.spam(false), 2);
+      rel_from_camera.write(sgraphutil_cat.spam(false), 2);
     }
   }
 

+ 3 - 3
panda/src/sgraphutil/frustumCullTraverser.h

@@ -28,7 +28,7 @@ public:
   typedef TYPENAME Visitor::TransitionWrapper TransitionWrapper;
   typedef TYPENAME Visitor::AttributeWrapper AttributeWrapper;
 
-  FrustumCullTraverser(Node *root,
+  FrustumCullTraverser(Node *root, const LMatrix4f &rel_from_camera,
 		       Visitor &visitor,
 		       const AttributeWrapper &initial_render_state,
 		       const LevelState &initial_level_state,
@@ -67,12 +67,12 @@ protected:
 // Convenience function.
 template<class Visitor, class AttributeWrapper, class LevelState>
 INLINE void
-fc_traverse(Node *root, Visitor &visitor,
+fc_traverse(Node *root, const LMatrix4f &rel_from_camera, Visitor &visitor,
 	    const AttributeWrapper &initial_render_state, 
 	    const LevelState &initial_level_state,
 	    GraphicsStateGuardian *gsg, TypeHandle graph_type) {
   FrustumCullTraverser<Visitor, LevelState> 
-    fct(root, visitor, initial_render_state, 
+    fct(root, rel_from_camera, visitor, initial_render_state, 
 	initial_level_state, gsg, graph_type);
 }
 

+ 2 - 2
panda/src/shader/shaderTransition.cxx

@@ -245,9 +245,9 @@ must_blend()
 ////////////////////////////////////////////////////////////////////
 bool ShaderTransition::
 sub_render(NodeRelation *arc, const AllAttributesWrapper &attrib,
-	   AllTransitionsWrapper &trans, GraphicsStateGuardianBase *gsgbase) {
+	   AllTransitionsWrapper &trans, RenderTraverser *trav) {
   Node *node = arc->get_child();
-  GraphicsStateGuardian *gsg = DCAST(GraphicsStateGuardian, gsgbase);
+  GraphicsStateGuardian *gsg = trav->get_gsg();
   bool multipass_on = false;
 
   // No shaders; never mind.

+ 1 - 1
panda/src/shader/shaderTransition.h

@@ -70,7 +70,7 @@ public:
   virtual bool sub_render(NodeRelation *arc,
 			  const AllAttributesWrapper &attrib,
 			  AllTransitionsWrapper &trans,
-			  GraphicsStateGuardianBase *gsgbase);
+			  RenderTraverser *trav);
   virtual bool has_sub_render() const;
 
 private:

+ 6 - 6
panda/src/switchnode/LODNode.cxx

@@ -12,12 +12,12 @@
 
 #include <graphicsStateGuardian.h>
 #include <get_rel_pos.h>
-#include <dftraverser.h>
 #include <luse.h>
 #include <renderRelation.h>
 #include <transformTransition.h>
 #include <allAttributesWrapper.h>
 #include <allTransitionsWrapper.h>
+#include <renderTraverser.h>
 
 ////////////////////////////////////////////////////////////////////
 // Static variables
@@ -69,9 +69,9 @@ output(ostream &out) const {
 ////////////////////////////////////////////////////////////////////
 bool LODNode::
 sub_render(const AllAttributesWrapper &attrib, AllTransitionsWrapper &trans,
-	   GraphicsStateGuardianBase *gsgbase) {
+	   RenderTraverser *trav) {
 
-  GraphicsStateGuardian *gsg = DCAST(GraphicsStateGuardian, gsgbase);
+  GraphicsStateGuardian *gsg = trav->get_gsg();
 
   // Get the current camera position from the gsg
   const ProjectionNode* camera = gsg->get_current_projection_node();
@@ -81,7 +81,8 @@ sub_render(const AllAttributesWrapper &attrib, AllTransitionsWrapper &trans,
   LPoint3f LOD_pos;
 
   NodeTransitionWrapper ntw(TransformTransition::get_class_type());
-  wrt(this, camera, ntw, RenderRelation::get_class_type());
+  wrt(this, camera, trav->begin(), trav->end(),
+      ntw, RenderRelation::get_class_type());
   const TransformTransition *tt;
   if (get_transition_into(tt, ntw)) {
     LOD_pos = _lod._center * tt->get_matrix();
@@ -110,8 +111,7 @@ sub_render(const AllAttributesWrapper &attrib, AllTransitionsWrapper &trans,
     new_trans.compose_in_place(arc_trans);
 
     // Now render everything from this node and below.
-    gsg->render_subgraph(gsg->get_render_traverser(), 
-			 arc->get_child(), attrib, new_trans);
+    gsg->render_subgraph(trav, arc->get_child(), attrib, new_trans);
 
   } else {
     if (switchnode_cat.is_debug()) {

+ 1 - 1
panda/src/switchnode/LODNode.h

@@ -51,7 +51,7 @@ public:
 
   virtual bool sub_render(const AllAttributesWrapper &attrib,
 			  AllTransitionsWrapper &trans,
-			  GraphicsStateGuardianBase *gsgbase);
+			  RenderTraverser *trav);
   virtual bool has_sub_render() const;
 
 public:

+ 4 - 5
panda/src/switchnode/sequenceNode.cxx

@@ -16,6 +16,7 @@
 #include <allAttributesWrapper.h>
 #include <allTransitionsWrapper.h>
 #include <renderRelation.h>
+#include <renderTraverser.h>
 
 ////////////////////////////////////////////////////////////////////
 // Static variables
@@ -62,9 +63,8 @@ set_switch_time(float switch_time)
 ////////////////////////////////////////////////////////////////////
 bool SequenceNode::
 sub_render(const AllAttributesWrapper &attrib, AllTransitionsWrapper &trans,
-	   GraphicsStateGuardianBase *gsgbase) 
-{
-  GraphicsStateGuardian *gsg = DCAST(GraphicsStateGuardian, gsgbase);
+	   RenderTraverser *trav) {
+  GraphicsStateGuardian *gsg = trav->get_gsg();
 
   // Determine which child to traverse
   int num_children = get_num_children(RenderRelation::get_class_type());
@@ -88,8 +88,7 @@ sub_render(const AllAttributesWrapper &attrib, AllTransitionsWrapper &trans,
     new_trans.compose_in_place(arc_trans);
 
     // Now render everything from this node and below.
-    gsg->render_subgraph(gsg->get_render_traverser(), 
-			 arc->get_child(), attrib, new_trans);
+    gsg->render_subgraph(trav, arc->get_child(), attrib, new_trans);
 
   } else {
     if (switchnode_cat.is_debug()) {

+ 1 - 1
panda/src/switchnode/sequenceNode.h

@@ -32,7 +32,7 @@ PUBLISHED:
 public:
   virtual bool sub_render(const AllAttributesWrapper &attrib,
 			  AllTransitionsWrapper &trans,
-			  GraphicsStateGuardianBase *gsgbase);
+			  RenderTraverser *trav);
   virtual bool has_sub_render() const;
 
 public:

+ 1 - 0
panda/src/testbed/Sources.pp

@@ -17,6 +17,7 @@
 
 #begin test_bin_target
   #define TARGET chat
+  #define LOCAL_LIBS $[LOCAL_LIBS] chat
 
   #define SOURCES \
     chat_test.cxx

+ 4 - 4
panda/src/testbed/chat_test.cxx

@@ -1,7 +1,6 @@
 #include <eventHandler.h>
 #include <chancfg.h>
 #include <textNode.h>
-#include <eggLoader.h>
 #include <mouse.h>
 #include <graphicsWindow.h>
 #include <chatInput.h>
@@ -50,22 +49,23 @@ void chat_keys(EventHandler& eh) {
   eh.add_hook("chat_exit", event_chat_exit);
   eh.add_hook("chat_overflow", event_chat_overflow);
 
-  PT_NamedNode font = loader.load_sync("ttf-comic"); 
+  PT_Node font = loader.load_sync("ttf-comic"); 
 
   // Create the input text node
   input_text_node = new TextNode("input_text_node");
   input_text_node->set_billboard(false);
   input_text_node->set_font(font.p());
   input_text_node->set_text("Press Enter to begin chat mode.");
+  input_text_node->set_wordwrap(12.0);
   RenderRelation *text_arc = new RenderRelation(cameras, input_text_node);
   LMatrix4f mat = LMatrix4f::scale_mat(0.25);
-  mat.set_row(3, LVector3f(-3, 8, -2.4));
+  mat.set_row(3, LVector3f(-3, 8, -1.4));
   text_arc->set_transition(new TransformTransition(mat));
   LightTransition *no_light = new LightTransition(LightTransition::all_off());
   text_arc->set_transition(no_light);
 
   chat_input = new ChatInput(input_text_node, "chat input");
-  chat_input->set_max_chars(20);
+  chat_input->set_max_lines(2);
 
   // Create the output text node
   output_text_node = new TextNode("output_text_node");

+ 7 - 12
panda/src/testbed/demo.cxx

@@ -78,6 +78,7 @@ RenderRelation *flare_arc = (RenderRelation *)NULL;
 DownRelationPointers *current_siblings;
 DownRelationPointers::iterator current_sib;
 
+/*
 void 
 setup_panda2d() {
   static bool already_setup = false;
@@ -173,7 +174,6 @@ setup_panda2d() {
   } else {
     nout << "Couldn't find lilsmiley.egg\n";
   }
-  
 }
 
 static void
@@ -191,6 +191,7 @@ event_out_label2d(CPT_Event) {
 
   label2d->set_card_color(0.5, 0.5, 0.5, 0.5);
 }
+*/
 
 
 static void
@@ -220,6 +221,9 @@ set_highlight(Node *node) {
     const BoundingVolume &vol = arc->get_bound();
     nout << "Bounding volume of arc is " << vol << "\n";
 
+    nout << "Transitions on arc:\n";
+    arc->write_transitions(nout, 2);
+
     arc->set_transition(new DrawBoundsTransition);
     bounds_arc = arc;
   }
@@ -408,15 +412,6 @@ event_h(CPT_Event) {
   }
 }
 
-static void
-event_2(CPT_Event) {
-  static bool is_setup = false;
-  if (!is_setup) {
-    setup_panda2d();
-    is_setup = true;
-  }
-}
-
 static void attach_sky() {
   // Load the sun and sky
   sky = DCAST(NamedNode, loader.load_sync("sky"));
@@ -556,8 +551,10 @@ void demo_keys(EventHandler&) {
   new RenderRelation( lights, dlight );
   have_dlight = true;
 
+  /*
   event_handler.add_hook("mw-in-label2d", event_in_label2d);
   event_handler.add_hook("mw-out-label2d", event_out_label2d);
+  */
 
   event_handler.add_hook("h", event_h);
   event_handler.add_hook("up", event_up);
@@ -574,8 +571,6 @@ void demo_keys(EventHandler&) {
   event_handler.add_hook("f8", event_fkey);
   event_handler.add_hook("f9", event_fkey);
 
-  event_handler.add_hook("2", event_2);
- 
   event_handler.add_hook("L", event_L);
   event_handler.add_hook("k", event_k);
   event_handler.add_hook("a", event_a);