ソースを参照

pgraph view-frustum cull

David Rose 24 年 前
コミット
3200a5f929

+ 10 - 0
panda/src/display/config_display.cxx

@@ -58,6 +58,16 @@ const bool pipe_spec_is_remote = config_display.Defined("pipe-machine")
 const bool compare_state_by_pointer =
 const bool compare_state_by_pointer =
 config_display.GetBool("compare-state-by-pointer", true);
 config_display.GetBool("compare-state-by-pointer", true);
 
 
+// This is normally true to enable the cull traversal to perform
+// state-sorting and alpha-sorting.  Turn this false to disable these
+// features and likely improve cull performance at the expense of draw
+// (and at the expense of correct alpha).
+const bool cull_sorting = config_display.GetBool("cull-sorting", true);
+
+// This is normally true; set it false to disable view-frustum culling
+// (primarily useful for debugging).
+const bool qpview_frustum_cull = config_display.GetBool("view-frustum-cull", true);
+
 const float gsg_clear_r = config_display.GetFloat("gsg-clear-r", 0.0);
 const float gsg_clear_r = config_display.GetFloat("gsg-clear-r", 0.0);
 const float gsg_clear_g = config_display.GetFloat("gsg-clear-g", 0.0);
 const float gsg_clear_g = config_display.GetFloat("gsg-clear-g", 0.0);
 const float gsg_clear_b = config_display.GetFloat("gsg-clear-b", 0.0);
 const float gsg_clear_b = config_display.GetFloat("gsg-clear-b", 0.0);

+ 2 - 0
panda/src/display/config_display.h

@@ -36,6 +36,8 @@ extern const bool pipe_spec_is_file;
 extern const bool pipe_spec_is_remote;
 extern const bool pipe_spec_is_remote;
 
 
 extern const bool compare_state_by_pointer;
 extern const bool compare_state_by_pointer;
+extern const bool cull_sorting;
+extern const bool qpview_frustum_cull;
 
 
 extern const float gsg_clear_r;
 extern const float gsg_clear_r;
 extern const float gsg_clear_g;
 extern const float gsg_clear_g;

+ 125 - 92
panda/src/display/graphicsEngine.cxx

@@ -17,6 +17,7 @@
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 
 
 #include "graphicsEngine.h"
 #include "graphicsEngine.h"
+#include "config_display.h"
 #include "pipeline.h"
 #include "pipeline.h"
 #include "drawCullHandler.h"
 #include "drawCullHandler.h"
 #include "binCullHandler.h"
 #include "binCullHandler.h"
@@ -79,8 +80,11 @@ remove_window(GraphicsWindow *window) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void GraphicsEngine::
 void GraphicsEngine::
 render_frame() {
 render_frame() {
-  //  cull_and_draw_together();
-  cull_bin_draw();
+  if (cull_sorting) {
+    cull_bin_draw();
+  } else {
+    cull_and_draw_together();
+  }
 
 
   // **** This doesn't belong here; it really belongs in the Pipeline,
   // **** This doesn't belong here; it really belongs in the Pipeline,
   // but here it is for now.
   // but here it is for now.
@@ -120,68 +124,18 @@ cull_and_draw_together() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void GraphicsEngine::
 void GraphicsEngine::
 cull_and_draw_together(GraphicsWindow *win, DisplayRegion *dr) {
 cull_and_draw_together(GraphicsWindow *win, DisplayRegion *dr) {
-  const NodeChain &camera = dr->get_qpcamera();
-  if (camera.is_empty()) {
-    // No camera, no draw.
-    return;
-  }
-
-  qpCamera *camera_node;
-  DCAST_INTO_V(camera_node, camera.node());
-
-  if (!camera_node->is_active()) {
-    // Camera inactive, no draw.
-    return;
-  }
-
-  Lens *lens = camera_node->get_lens();
-  if (lens == (Lens *)NULL) {
-    // No lens, no draw.
-    return;
-  }
-
-  NodeChain scene = camera_node->get_scene();
-  if (scene.is_empty()) {
-    // No scene, no draw.
-    return;
-  }
-
   GraphicsStateGuardian *gsg = win->get_gsg();
   GraphicsStateGuardian *gsg = win->get_gsg();
   nassertv(gsg != (GraphicsStateGuardian *)NULL);
   nassertv(gsg != (GraphicsStateGuardian *)NULL);
 
 
-  if (!gsg->set_lens(lens)) {
-    // The lens is inappropriate somehow.
-    display_cat.error()
-      << gsg->get_type() << " cannot render with " << lens->get_type()
-      << "\n";
-    return;
-  }
-
-  DrawCullHandler cull_handler(gsg);
-  qpCullTraverser trav;
-  trav.set_cull_handler(&cull_handler);
-
-  // The world transform is computed from the camera's position; we
-  // then might need to adjust it into the GSG's internal coordinate
-  // system.
-  trav.set_camera_transform(scene.get_rel_transform(camera));
-
-  CPT(TransformState) render_transform = camera.get_rel_transform(scene);
-  CoordinateSystem external_cs = gsg->get_coordinate_system();
-  CoordinateSystem internal_cs = gsg->get_internal_coordinate_system();
-  if (internal_cs != CS_default && internal_cs != external_cs) {
-    CPT(TransformState) cs_transform = 
-      TransformState::make_mat(LMatrix4f::convert_mat(external_cs, internal_cs));
-    render_transform = cs_transform->compose(render_transform);
+  if (set_gsg_lens(gsg, dr)) {
+    DisplayRegionStack old_dr = gsg->push_display_region(dr);
+    gsg->prepare_display_region();
+    
+    DrawCullHandler cull_handler(gsg);
+    do_cull(&cull_handler, dr->get_qpcamera(), gsg);
+    
+    gsg->pop_display_region(old_dr);
   }
   }
-  trav.set_render_transform(render_transform);
-  
-  DisplayRegionStack old_dr = gsg->push_display_region(dr);
-  gsg->prepare_display_region();
-  
-  trav.traverse(scene.node());
-  
-  gsg->pop_display_region(old_dr);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -217,7 +171,34 @@ cull_bin_draw() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void GraphicsEngine::
 void GraphicsEngine::
 cull_bin_draw(GraphicsWindow *win, DisplayRegion *dr) {
 cull_bin_draw(GraphicsWindow *win, DisplayRegion *dr) {
-  const NodeChain &camera = dr->get_qpcamera();
+  GraphicsStateGuardian *gsg = win->get_gsg();
+  nassertv(gsg != (GraphicsStateGuardian *)NULL);
+
+  PT(CullResult) cull_result = dr->_cull_result;
+  if (cull_result == (CullResult *)NULL) {
+    cull_result = new CullResult(gsg);
+  }
+
+  BinCullHandler cull_handler(cull_result);
+  do_cull(&cull_handler, dr->get_qpcamera(), gsg);
+
+  cull_result->finish_cull();
+
+  // Save the results for next frame.
+  dr->_cull_result = cull_result->make_next();
+
+  // Now draw.
+  do_draw(cull_result, gsg, dr);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsEngine::do_cull
+//       Access: Private
+//  Description: Fires off a cull traversal using the indicated camera.
+////////////////////////////////////////////////////////////////////
+void GraphicsEngine::
+do_cull(CullHandler *cull_handler, const NodeChain &camera,
+        GraphicsStateGuardian *gsg) {
   if (camera.is_empty()) {
   if (camera.is_empty()) {
     // No camera, no draw.
     // No camera, no draw.
     return;
     return;
@@ -243,32 +224,21 @@ cull_bin_draw(GraphicsWindow *win, DisplayRegion *dr) {
     return;
     return;
   }
   }
 
 
-  GraphicsStateGuardian *gsg = win->get_gsg();
-  nassertv(gsg != (GraphicsStateGuardian *)NULL);
-
-  if (!gsg->set_lens(lens)) {
-    // The lens is inappropriate somehow.
-    display_cat.error()
-      << gsg->get_type() << " cannot render with " << lens->get_type()
-      << "\n";
-    return;
-  }
-
-  PT(CullResult) cull_result = dr->_cull_result;
-  if (cull_result == (CullResult *)NULL) {
-    cull_result = new CullResult(gsg);
-  }
-
-  BinCullHandler cull_handler(cull_result);
   qpCullTraverser trav;
   qpCullTraverser trav;
-  trav.set_cull_handler(&cull_handler);
+  trav.set_cull_handler(cull_handler);
 
 
-  // The world transform is computed from the camera's position; we
-  // then might need to adjust it into the GSG's internal coordinate
-  // system.
-  trav.set_camera_transform(scene.get_rel_transform(camera));
+  // We will need both the camera transform (the net transform from
+  // the scene to the camera) and the world transform (the camera
+  // transform inverse, or the net transform from the camera to the
+  // scene).
+  CPT(TransformState) camera_transform = scene.get_rel_transform(camera);
+  CPT(TransformState) world_transform = camera.get_rel_transform(scene);
+
+  // The render transform is the same as the world transform, except
+  // it is converted into the GSG's internal coordinate system.  This
+  // is the transform that the GSG will apply to all of its vertices.
+  CPT(TransformState) render_transform = world_transform;
 
 
-  CPT(TransformState) render_transform = camera.get_rel_transform(scene);
   CoordinateSystem external_cs = gsg->get_coordinate_system();
   CoordinateSystem external_cs = gsg->get_coordinate_system();
   CoordinateSystem internal_cs = gsg->get_internal_coordinate_system();
   CoordinateSystem internal_cs = gsg->get_internal_coordinate_system();
   if (internal_cs != CS_default && internal_cs != external_cs) {
   if (internal_cs != CS_default && internal_cs != external_cs) {
@@ -276,17 +246,80 @@ cull_bin_draw(GraphicsWindow *win, DisplayRegion *dr) {
       TransformState::make_mat(LMatrix4f::convert_mat(external_cs, internal_cs));
       TransformState::make_mat(LMatrix4f::convert_mat(external_cs, internal_cs));
     render_transform = cs_transform->compose(render_transform);
     render_transform = cs_transform->compose(render_transform);
   }
   }
+
+  trav.set_camera_transform(scene.get_rel_transform(camera));
   trav.set_render_transform(render_transform);
   trav.set_render_transform(render_transform);
+
+  if (qpview_frustum_cull) {
+    // If we're to be performing view-frustum culling, determine the
+    // bounding volume associated with the current viewing frustum.
+
+    // First, we have to get the current viewing frustum, which comes
+    // from the lens.
+    PT(BoundingVolume) bv = lens->make_bounds();
+
+    if (bv != (BoundingVolume *)NULL &&
+        bv->is_of_type(GeometricBoundingVolume::get_class_type())) {
+      // Transform it into the appropriate coordinate space.
+      PT(GeometricBoundingVolume) local_frustum;
+      local_frustum = DCAST(GeometricBoundingVolume, bv->make_copy());
+      local_frustum->xform(camera_transform->get_mat());
+
+      trav.set_view_frustum(local_frustum);
+    }
+  }
+  
   
   
   trav.traverse(scene.node());
   trav.traverse(scene.node());
-  cull_result->finish_cull();
+}
 
 
-  // Save the results for next frame.
-  dr->_cull_result = cull_result->make_next();
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsEngine::do_draw
+//       Access: Private
+//  Description: Draws the previously-culled scene.
+////////////////////////////////////////////////////////////////////
+void GraphicsEngine::
+do_draw(CullResult *cull_result, GraphicsStateGuardian *gsg,
+        DisplayRegion *dr) {
+  if (set_gsg_lens(gsg, dr)) {
+    DisplayRegionStack old_dr = gsg->push_display_region(dr);
+    gsg->prepare_display_region();
+    cull_result->draw();
+    gsg->pop_display_region(old_dr);
+  }
+}
 
 
-  // Now draw.
-  DisplayRegionStack old_dr = gsg->push_display_region(dr);
-  gsg->prepare_display_region();
-  cull_result->draw();
-  gsg->pop_display_region(old_dr);
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsEngine::set_gsg_lens
+//       Access: Private
+//  Description: Sets up the GSG to draw with the lens from the
+//               indicated DisplayRegion.  Returns true if the lens is
+//               acceptable, false otherwise.
+////////////////////////////////////////////////////////////////////
+bool GraphicsEngine::
+set_gsg_lens(GraphicsStateGuardian *gsg, DisplayRegion *dr) {
+  const NodeChain &camera = dr->get_qpcamera();
+  if (camera.is_empty()) {
+    // No camera, no draw.
+    return false;
+  }
+
+  qpCamera *camera_node;
+  DCAST_INTO_R(camera_node, camera.node(), false);
+
+  Lens *lens = camera_node->get_lens();
+  if (lens == (Lens *)NULL) {
+    // No lens, no draw.
+    return false;
+  }
+
+  if (!gsg->set_lens(lens)) {
+    // The lens is inappropriate somehow.
+    display_cat.error()
+      << gsg->get_type() << " cannot render with " << lens->get_type()
+      << "\n";
+    return false;
+  }
+
+  return true;
 }
 }

+ 7 - 0
panda/src/display/graphicsEngine.h

@@ -57,6 +57,13 @@ private:
   void cull_bin_draw();
   void cull_bin_draw();
   void cull_bin_draw(GraphicsWindow *win, DisplayRegion *dr);
   void cull_bin_draw(GraphicsWindow *win, DisplayRegion *dr);
 
 
+  void do_cull(CullHandler *cull_handler, const NodeChain &camera,
+               GraphicsStateGuardian *gsg);
+  void do_draw(CullResult *cull_result, GraphicsStateGuardian *gsg,
+               DisplayRegion *dr);
+
+  bool set_gsg_lens(GraphicsStateGuardian *gsg, DisplayRegion *dr);
+
   Pipeline *_pipeline;
   Pipeline *_pipeline;
 
 
   typedef pset<PT(GraphicsWindow)> Windows;
   typedef pset<PT(GraphicsWindow)> Windows;

+ 5 - 0
panda/src/pgraph/config_pgraph.cxx

@@ -47,6 +47,11 @@ ConfigureFn(config_pgraph) {
   init_libpgraph();
   init_libpgraph();
 }
 }
 
 
+// Set this true to cause culling to be performed by rendering the
+// object in red wireframe, rather than actually culling it.  This
+// helps make culling errors obvious.
+const bool qpfake_view_frustum_cull = config_pgraph.GetBool("fake-view-frustum-cull", false);
+
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: init_libpgraph
 //     Function: init_libpgraph

+ 2 - 0
panda/src/pgraph/config_pgraph.h

@@ -26,6 +26,8 @@
 ConfigureDecl(config_pgraph, EXPCL_PANDA, EXPTP_PANDA);
 ConfigureDecl(config_pgraph, EXPCL_PANDA, EXPTP_PANDA);
 NotifyCategoryDecl(pgraph, EXPCL_PANDA, EXPTP_PANDA);
 NotifyCategoryDecl(pgraph, EXPCL_PANDA, EXPTP_PANDA);
 
 
+extern const bool qpfake_view_frustum_cull;
+
 extern EXPCL_PANDA void init_libpgraph();
 extern EXPCL_PANDA void init_libpgraph();
 
 
 #endif
 #endif

+ 131 - 8
panda/src/pgraph/qpcullTraverser.cxx

@@ -23,6 +23,9 @@
 #include "cullHandler.h"
 #include "cullHandler.h"
 #include "dcast.h"
 #include "dcast.h"
 #include "qpgeomNode.h"
 #include "qpgeomNode.h"
+#include "colorAttrib.h"
+#include "textureAttrib.h"
+#include "config_pgraph.h"
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: qpCullTraverser::Constructor
 //     Function: qpCullTraverser::Constructor
@@ -74,6 +77,39 @@ set_render_transform(const TransformState *render_transform) {
   _render_transform = render_transform;
   _render_transform = render_transform;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: qpCullTraverser::set_view_frustum
+//       Access: Public
+//  Description: Specifies the bounding volume that corresponds to the
+//               viewing frustum.  Any primitives that fall entirely
+//               outside of this volume are not drawn.
+////////////////////////////////////////////////////////////////////
+void qpCullTraverser::
+set_view_frustum(GeometricBoundingVolume *view_frustum) {
+  _view_frustum = view_frustum;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCullTraverser::set_guard_band
+//       Access: Public
+//  Description: Specifies the bounding volume to use for detecting
+//               guard band clipping.  This is a render optimization
+//               for certain cards that support this feature; the
+//               guard band is a 2-d area than the frame buffer.
+//               If a primitive will appear entirely within the guard
+//               band after perspective transform, it may be drawn
+//               correctly with clipping disabled, for a small
+//               performance gain.
+//
+//               This is the bounding volume that corresponds to the
+//               2-d guard band.  If a primitive is entirely within
+//               this area, clipping will be disabled on the GSG.
+////////////////////////////////////////////////////////////////////
+void qpCullTraverser::
+set_guard_band(GeometricBoundingVolume *guard_band) {
+  _guard_band = guard_band;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: qpCullTraverser::set_cull_handler
 //     Function: qpCullTraverser::set_cull_handler
 //       Access: Public
 //       Access: Public
@@ -95,7 +131,7 @@ traverse(PandaNode *root) {
   nassertv(_cull_handler != (CullHandler *)NULL);
   nassertv(_cull_handler != (CullHandler *)NULL);
 
 
   r_traverse(root, _render_transform, TransformState::make_identity(),
   r_traverse(root, _render_transform, TransformState::make_identity(),
-             _initial_state, 0);
+             _initial_state, _view_frustum, _guard_band);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -107,12 +143,93 @@ void qpCullTraverser::
 r_traverse(PandaNode *node, 
 r_traverse(PandaNode *node, 
            const TransformState *render_transform,
            const TransformState *render_transform,
            const TransformState *net_transform,
            const TransformState *net_transform,
-           const RenderState *state, int flags) {
-  CPT(TransformState) next_render_transform = 
-    render_transform->compose(node->get_transform());
-  CPT(TransformState) next_net_transform = 
-    net_transform->compose(node->get_transform());
-  CPT(RenderState) next_state = state->compose(node->get_state());
+           const RenderState *state, 
+           GeometricBoundingVolume *view_frustum,
+           GeometricBoundingVolume *guard_band) {
+  CPT(RenderState) next_state = state;
+
+  if (view_frustum != (GeometricBoundingVolume *)NULL) {
+    // If we have a viewing frustum, check to see if the node's
+    // bounding volume falls within it.
+    const BoundingVolume &node_volume = node->get_bound();
+    nassertv(node_volume.is_of_type(GeometricBoundingVolume::get_class_type()));
+    const GeometricBoundingVolume *node_gbv =
+      DCAST(GeometricBoundingVolume, &node_volume);
+
+    int result = view_frustum->contains(node_gbv);
+    if (result == BoundingVolume::IF_no_intersection) {
+      // No intersection at all.  Cull.
+      if (!qpfake_view_frustum_cull) {
+        return;
+      }
+
+      // If we have fake view-frustum culling enabled, instead of
+      // actually culling an object we simply force it to be drawn in
+      // red wireframe.
+      view_frustum = (GeometricBoundingVolume *)NULL;
+      CPT(RenderState) fake_effect = RenderState::make
+        (ColorAttrib::make_flat(Colorf(1.0f, 0.0f, 0.0f, 1.0f)),
+         TextureAttrib::make_off(),
+         1000);
+      next_state = next_state->compose(fake_effect);
+
+    } else if ((result & BoundingVolume::IF_all) != 0) {
+      // The node and its descendants are completely enclosed within
+      // the frustum.  No need to cull further.
+      view_frustum = (GeometricBoundingVolume *)NULL;
+
+    } else {
+      if (node->is_final()) {
+        // The bounding volume is partially, but not completely,
+        // within the viewing frustum.  Normally we'd keep testing
+        // child bounded volumes as we continue down.  But this node
+        // has the "final" flag, so the user is claiming that there is
+        // some important reason we should consider everything visible
+        // at this point.  So be it.
+        view_frustum = (GeometricBoundingVolume *)NULL;
+      }
+    }
+  }
+
+  CPT(TransformState) next_render_transform = render_transform;
+  CPT(TransformState) next_net_transform = net_transform;
+  PT(GeometricBoundingVolume) next_view_frustum = view_frustum;
+  PT(GeometricBoundingVolume) next_guard_band = guard_band;
+
+  const TransformState *transform = node->get_transform();
+  if (!transform->is_identity()) {
+    next_render_transform = render_transform->compose(transform);
+    next_net_transform = net_transform->compose(transform);
+
+    if ((view_frustum != (GeometricBoundingVolume *)NULL) ||
+        (guard_band != (GeometricBoundingVolume *)NULL)) {
+      // We need to move the viewing frustums into the node's
+      // coordinate space by applying the node's inverse transform.
+      if (transform->is_singular()) {
+        // But we can't invert a singular transform!  Instead of
+        // trying, we'll just give up on frustum culling from this
+        // point down.
+        view_frustum = (GeometricBoundingVolume *)NULL;
+        guard_band = (GeometricBoundingVolume *)NULL;
+
+      } else {
+        CPT(TransformState) inv_transform = 
+          transform->invert_compose(TransformState::make_identity());
+        
+        if (view_frustum != (GeometricBoundingVolume *)NULL) {
+          next_view_frustum = DCAST(GeometricBoundingVolume, view_frustum->make_copy());
+          next_view_frustum->xform(inv_transform->get_mat());
+        }
+        
+        if (guard_band != (GeometricBoundingVolume *)NULL) {
+          next_guard_band = DCAST(GeometricBoundingVolume, guard_band->make_copy());
+          next_guard_band->xform(inv_transform->get_mat());
+        }
+      }
+    }
+  }
+
+  next_state = next_state->compose(node->get_state());
 
 
   const BillboardAttrib *billboard = state->get_billboard();
   const BillboardAttrib *billboard = state->get_billboard();
   if (billboard != (const BillboardAttrib *)NULL) {
   if (billboard != (const BillboardAttrib *)NULL) {
@@ -121,6 +238,11 @@ r_traverse(PandaNode *node,
       billboard->do_billboard(net_transform, _camera_transform);
       billboard->do_billboard(net_transform, _camera_transform);
     next_render_transform = next_render_transform->compose(billboard_transform);
     next_render_transform = next_render_transform->compose(billboard_transform);
     next_net_transform = next_net_transform->compose(billboard_transform);
     next_net_transform = next_net_transform->compose(billboard_transform);
+
+    // We can't reliably cull within a billboard, because the geometry
+    // might get rotated out of its bounding volume.  So once we get
+    // within a billboard, we consider it all visible.
+    next_view_frustum = (GeometricBoundingVolume *)NULL;
   }
   }
 
 
   if (node->is_geom_node()) {
   if (node->is_geom_node()) {
@@ -140,6 +262,7 @@ r_traverse(PandaNode *node,
   PandaNode::Children cr = node->get_children();
   PandaNode::Children cr = node->get_children();
   int num_children = cr.get_num_children();
   int num_children = cr.get_num_children();
   for (int i = 0; i < num_children; i++) {
   for (int i = 0; i < num_children; i++) {
-    r_traverse(cr.get_child(i), next_render_transform, next_net_transform, next_state, flags);
+    r_traverse(cr.get_child(i), next_render_transform, next_net_transform,
+               next_state, next_view_frustum, next_guard_band);
   }
   }
 }
 }

+ 8 - 1
panda/src/pgraph/qpcullTraverser.h

@@ -23,6 +23,7 @@
 
 
 #include "renderState.h"
 #include "renderState.h"
 #include "transformState.h"
 #include "transformState.h"
+#include "geometricBoundingVolume.h"
 #include "pointerTo.h"
 #include "pointerTo.h"
 
 
 class PandaNode;
 class PandaNode;
@@ -43,6 +44,8 @@ public:
   void set_initial_state(const RenderState *initial_state);
   void set_initial_state(const RenderState *initial_state);
   void set_camera_transform(const TransformState *camera_transform);
   void set_camera_transform(const TransformState *camera_transform);
   void set_render_transform(const TransformState *render_transform);
   void set_render_transform(const TransformState *render_transform);
+  void set_view_frustum(GeometricBoundingVolume *view_frustum);
+  void set_guard_band(GeometricBoundingVolume *guard_band);
   void set_cull_handler(CullHandler *cull_handler);
   void set_cull_handler(CullHandler *cull_handler);
 
 
   void traverse(PandaNode *root);
   void traverse(PandaNode *root);
@@ -50,11 +53,15 @@ public:
 private:
 private:
   void r_traverse(PandaNode *node, const TransformState *render_transform,
   void r_traverse(PandaNode *node, const TransformState *render_transform,
                   const TransformState *net_transform,
                   const TransformState *net_transform,
-                  const RenderState *state, int flags);
+                  const RenderState *state,
+                  GeometricBoundingVolume *view_frustum,
+                  GeometricBoundingVolume *guard_band);
 
 
   CPT(RenderState) _initial_state;
   CPT(RenderState) _initial_state;
   CPT(TransformState) _camera_transform;
   CPT(TransformState) _camera_transform;
   CPT(TransformState) _render_transform;
   CPT(TransformState) _render_transform;
+  PT(GeometricBoundingVolume) _view_frustum;
+  PT(GeometricBoundingVolume) _guard_band;
   CullHandler *_cull_handler;
   CullHandler *_cull_handler;
 };
 };