portals are now clipped against view frustum to generate reduced frustums. There is a bug, which I will work on Monday

This commit is contained in:
Asad M. Zaman 2004-06-05 01:56:33 +00:00
parent 862e9811c6
commit 01171fd7d2
4 changed files with 246 additions and 102 deletions

View File

@ -97,41 +97,62 @@ draw_to(float x, float y, float z) {
INLINE void PortalClipper::
draw_camera_frustum() {
_color = Colorf(1,1,1,1);
draw_hexahedron(_hex_frustum);
draw_hexahedron(_view_frustum);
}
////////////////////////////////////////////////////////////////////
// Function: PortalClipper::set_reduced_frustum
// Access: Public
// Description: set the current reduced frustum before traversing
//
////////////////////////////////////////////////////////////////////
INLINE void PortalClipper::
set_reduced_frustum(BoundingHexahedron *frustum) {
_reduced_frustum = frustum;
}
////////////////////////////////////////////////////////////////////
// Function: PortalClipper::is_in_view
// Access: Public
// Description: checks if portal_node is within the view frustum.
// If so, then the portal is worth considering. This
// is a 1st level test to weed out most of the portals
////////////////////////////////////////////////////////////////////
INLINE bool PortalClipper::
is_in_view(const NodePath &node_path) {
const BoundingVolume *bv = &_portal_node->get_bound();
// I am about to xform this gbv, so lets make a copy
BoundingVolume *cbv = bv->make_copy();
GeometricBoundingVolume *gbv = DCAST(GeometricBoundingVolume, cbv);
// trasform it to camera space
CPT(TransformState) ctransform = node_path.get_transform(_scene_setup->get_cull_center());
gbv->xform(ctransform->get_mat());
int result = _reduced_frustum->contains(gbv);
pgraph_cat.debug() << "1st level test if portal is in view " << result << endl;
return (result != 0);
}
////////////////////////////////////////////////////////////////////
// Function: PortalClipper::facing_camera
// Access: Public
// Description: checks if the _coords that forms the plane is
// facing the camera
// Description: checks if any of the _coords is within the view frustum.
// If so, then the portal is facing the camera. 2nd level
// test to make sure this portal is worth visiting
////////////////////////////////////////////////////////////////////
INLINE bool PortalClipper::
is_facing_camera()
{
Planef portal_plane(_coords[0], _coords[1], _coords[2]);
Planef camera_plane(_hex_frustum->get_point(4), _hex_frustum->get_point(5), _hex_frustum->get_point(6));
#if 0
// use the camera's near plane to calculate direction
pgraph_cat.debug() << portal_plane.get_normal() << "; " << -camera_plane.get_normal() << endl;
float direction = portal_plane.get_normal().dot(-camera_plane.get_normal());
pgraph_cat.debug() << "Found direction of " << direction << endl;
return (direction < _FACING_THRESHOLD);
#else
// use the center of camera's near plane to the center of the
// portal_plane to calulate the direction
LPoint3f portal_center = (_coords[0] + _coords[1] + _coords[2] + _coords[3]) / 4;
LPoint3f camera_center = (_hex_frustum->get_point(4) + _hex_frustum->get_point(5) + _hex_frustum->get_point(6) + _hex_frustum->get_point(7)) / 4;
LVector3f camera_to_portal = portal_center - camera_center;
camera_to_portal.normalize();
pgraph_cat.debug() << "portal_center " << portal_center << "; camera_center " << camera_center << "; camera_to_portal " << camera_to_portal << endl;
float direction = camera_plane.get_normal().dot(camera_to_portal);
_color = Colorf(0,1,0,1);
move_to(camera_center);
draw_to(portal_center);
#endif
pgraph_cat.debug() << "Found direction of " << direction << endl;
return (direction < _FACING_THRESHOLD);
is_facing_camera(const NodePath &node_path) {
int result = 0;
// check if the camera_center to portal_center lineseg is in view
for (int j=0; j<_num_vert; ++j) {
result |= _reduced_frustum->contains(_coords[j]);
}
pgraph_cat.debug() << "frustum->contains(coord) result = " << result << endl;
return (result != 0);
}
////////////////////////////////////////////////////////////////////
@ -144,8 +165,8 @@ get_plane_depth(float x, float z, Planef *portal_plane) {
float y = 0.0;
// Plane equation: Ax + By + Cz + D = 0
// y = (Ax + Cz + D) / -B
pgraph_cat.debug() << *portal_plane << endl;
pgraph_cat.debug() << portal_plane->_v.v._0 << " " << portal_plane->_v.v._1 << " "
pgraph_cat.spam() << *portal_plane << endl;
pgraph_cat.spam() << portal_plane->_v.v._0 << " " << portal_plane->_v.v._1 << " "
<< portal_plane->_v.v._2 << " " << portal_plane->_v.v._3 << endl;
if (portal_plane->_v.v._1 != 0.0) {

View File

@ -47,7 +47,7 @@ PortalClipper(GeometricBoundingVolume *frustum, SceneSetup *scene_setup) {
_geom_point = new GeomPoint;
_geom_linestrip = new GeomLinestrip;
_hex_frustum = DCAST(BoundingHexahedron, frustum);
_view_frustum = _reduced_frustum = DCAST(BoundingHexahedron, frustum);
_scene_setup = scene_setup;
}
@ -203,7 +203,7 @@ draw_lines()
//geom->write_verbose(cerr, 0);
_previous->add_geom(geom);
pgraph_cat.debug() << "added geometry" << endl;
pgraph_cat.spam() << "added geometry" << endl;
}
}
}
@ -220,51 +220,51 @@ prepare_portal(const NodePath &node_path)
// Get the Portal Node from this node_path
PandaNode *node = node_path.node();
PortalNode *portal_node = NULL;
_portal_node = NULL;
if (node->is_of_type(PortalNode::get_class_type()))
portal_node = DCAST(PortalNode, node);
_portal_node = DCAST(PortalNode, node);
// walk the portal
_num_vert = 0;
// Get the geometry from the portal
pgraph_cat.debug() << *portal_node << endl;
pgraph_cat.spam() << *_portal_node << endl;
/*
// Get the World transformation matrix
CPT(TransformState) wtransform = portal_nodepath.get_transform(_scene_setup->get_scene_root());
LMatrix4f wmat = wtransform->get_mat();
pgraph_cat.debug() << wmat << endl;
pgraph_cat.spam() << wmat << endl;
*/
// Get the camera transformation matrix
CPT(TransformState) ctransform = node_path.get_transform(_scene_setup->get_cull_center());
//CPT(TransformState) ctransform = node_path.get_transform(_scene_setup->get_camera_path());
LMatrix4f cmat = ctransform->get_mat();
pgraph_cat.debug() << cmat << endl;
pgraph_cat.spam() << cmat << endl;
Vertexf temp[4];
temp[0] = portal_node->get_vertex(0);
temp[1] = portal_node->get_vertex(1);
temp[2] = portal_node->get_vertex(2);
temp[3] = portal_node->get_vertex(3);
temp[0] = _portal_node->get_vertex(0);
temp[1] = _portal_node->get_vertex(1);
temp[2] = _portal_node->get_vertex(2);
temp[3] = _portal_node->get_vertex(3);
pgraph_cat.debug() << "before transformation to camera space" << endl;
pgraph_cat.debug() << temp[0] << endl;
pgraph_cat.debug() << temp[1] << endl;
pgraph_cat.debug() << temp[2] << endl;
pgraph_cat.debug() << temp[3] << endl;
pgraph_cat.spam() << "before transformation to camera space" << endl;
pgraph_cat.spam() << temp[0] << endl;
pgraph_cat.spam() << temp[1] << endl;
pgraph_cat.spam() << temp[2] << endl;
pgraph_cat.spam() << temp[3] << endl;
temp[0] = temp[0]*cmat;
temp[1] = temp[1]*cmat;
temp[2] = temp[2]*cmat;
temp[3] = temp[3]*cmat;
pgraph_cat.debug() << "after transformation to camera space" << endl;
pgraph_cat.debug() << temp[0] << endl;
pgraph_cat.debug() << temp[1] << endl;
pgraph_cat.debug() << temp[2] << endl;
pgraph_cat.debug() << temp[3] << endl;
pgraph_cat.spam() << "after transformation to camera space" << endl;
pgraph_cat.spam() << temp[0] << endl;
pgraph_cat.spam() << temp[1] << endl;
pgraph_cat.spam() << temp[2] << endl;
pgraph_cat.spam() << temp[3] << endl;
float min_x, max_x, min_z, max_z;
@ -273,7 +273,7 @@ prepare_portal(const NodePath &node_path)
min_z = min(min(min(temp[0][2], temp[1][2]), temp[2][2]), temp[3][2]);
max_z = max(max(max(temp[0][2], temp[1][2]), temp[2][2]), temp[3][2]);
pgraph_cat.debug() << "min_x " << min_x << ";max_x " << max_x << ";min_z " << min_z << ";max_z " << max_z << endl;
pgraph_cat.spam() << "min_x " << min_x << ";max_x " << max_x << ";min_z " << min_z << ";max_z " << max_z << endl;
Planef portal_plane(temp[0], temp[1], temp[2]);
@ -281,29 +281,30 @@ prepare_portal(const NodePath &node_path)
float y;
y = get_plane_depth(min_x, min_z, &portal_plane);
pgraph_cat.debug() << "plane's depth is " << y << endl;
pgraph_cat.spam() << "plane's depth is " << y << endl;
_coords[0].set(min_x, y, min_z);
y = get_plane_depth(max_x, min_z, &portal_plane);
pgraph_cat.debug() << "plane's depth is " << y << endl;
pgraph_cat.spam() << "plane's depth is " << y << endl;
_coords[1].set(max_x, y, min_z);
y = get_plane_depth(max_x, max_z, &portal_plane);
pgraph_cat.debug() << "plane's depth is " << y << endl;
pgraph_cat.spam() << "plane's depth is " << y << endl;
_coords[2].set(max_x, y, max_z);
y = get_plane_depth(min_x, max_z, &portal_plane);
pgraph_cat.debug() << "plane's depth is " << y << endl;
pgraph_cat.spam() << "plane's depth is " << y << endl;
_coords[3].set(min_x, y, max_z);
pgraph_cat.debug() << "after min max calculation" << endl;
pgraph_cat.debug() << _coords[0] << endl;
pgraph_cat.debug() << _coords[1] << endl;
pgraph_cat.debug() << _coords[2] << endl;
pgraph_cat.debug() << _coords[3] << endl;
pgraph_cat.spam() << "after min max calculation" << endl;
pgraph_cat.spam() << _coords[0] << endl;
pgraph_cat.spam() << _coords[1] << endl;
pgraph_cat.spam() << _coords[2] << endl;
pgraph_cat.spam() << _coords[3] << endl;
// check if facing camera
if (is_facing_camera()) {
// check if portal is in view
if (is_in_view(node_path)) {
pgraph_cat.debug() << "portal passed 1st level test \n";
// ok, now lets add the original portal
_color = Colorf(0,1,1,1);
@ -314,15 +315,15 @@ prepare_portal(const NodePath &node_path)
draw_to(temp[0]);
// ok, now lets add the min_max portal
_color = Colorf(1,0,0,1);
_color = Colorf(1,1,0,1);
move_to(_coords[0]);
draw_to(_coords[1]);
draw_to(_coords[2]);
draw_to(_coords[3]);
draw_to(_coords[0]);
pgraph_cat.debug() << "assembled " << portal_node->get_name() << ": frustum points" << endl;
_num_vert = portal_node->get_num_vertices();
pgraph_cat.spam() << "assembled " << _portal_node->get_name() << ": frustum points" << endl;
_num_vert = _portal_node->get_num_vertices();
}
}
@ -335,7 +336,7 @@ prepare_portal(const NodePath &node_path)
void PortalClipper::
clip_portal(const NodePath &node_path)
{
int num_planes = _hex_frustum->get_num_planes();
int num_planes = _view_frustum->get_num_planes();
if (!_num_vert)
return;
@ -346,24 +347,136 @@ clip_portal(const NodePath &node_path)
// print out the planes. plane 0 should be far and plane 5 should be near
// so we are only concerned with the 4 side planes.
for (int i=0; i<num_planes; ++i) {
Planef plane = _hex_frustum->get_plane(i);
Planef plane = _view_frustum->get_plane(i);
plane.output(pgraph_cat.debug());
pgraph_cat.debug() << endl;
}
*/
for (int i=1; i<num_planes-1; ++i) {
Planef plane = _hex_frustum->get_plane(i);
for (int j=0; j<_num_vert; ++j) {
float t;
LPoint3f from_origin = _coords[j];
LVector3f from_direction = _coords[(j+1)%_num_vert] - _coords[j];
bool is_intersect = plane.intersects_line(t, from_origin, from_direction);
if (is_intersect) {
pgraph_cat.debug() << "plane " << i << " intersected segement " << j << "->" << (j+1)%_num_vert << " at t=" << t << endl;
// ViewFrustum -> Logical Planes -> Portal Edge
// Plane0 -> far plane -> None
// plane5 -> near plane -> None
// Plane1 -> bottom plane -> 0-1
// Plane3 -> top plane -> 1-2
// Plane2 -> right plane -> 2-3
// Plane4 -> left plane -> 3-0
int j;
float t;
Planef plane;
bool is_intersect;
LPoint3f from_origin;
LPoint3f cut_point;
LVector3f from_direction;
// Look for intersection with the view frustum's bottom_plane to bottom_edge of portal
plane = _view_frustum->get_plane(1);
for (j=0; j<_num_vert; ++j) {
from_origin = _coords[j];
from_direction = _coords[(j+1)%_num_vert] - _coords[j];
is_intersect = plane.intersects_line(t, from_origin, from_direction);
if (is_intersect && (t > 0.0 && t < 1.0)) {
pgraph_cat.debug() << "bottom plane intersected segement " << j << "->"
<< (j+1)%_num_vert << " at t=" << t << endl;
cut_point = from_origin + t*from_direction;
pgraph_cat.debug() << "cut_point: " << cut_point << endl;
if (j == 1) {
// means bottom should cut 1->2 by moving 1 to the intersection point
_coords[1] = cut_point;
}
else if (j == 3) {
// means bottom should cut 3->0 by moving 0 to the intersection point
_coords[0] = cut_point;
}
else
pgraph_cat.debug() << "ignored for now for simplicity \n";
}
}
// Look for intersection with the view frustum's top_plane to top_edge of portal
plane = _view_frustum->get_plane(3);
for (j=0; j<_num_vert; ++j) {
from_origin = _coords[j];
from_direction = _coords[(j+1)%_num_vert] - _coords[j];
is_intersect = plane.intersects_line(t, from_origin, from_direction);
if (is_intersect && (t > 0.0 && t < 1.0)) {
pgraph_cat.debug() << "top plane intersected segement " << j << "->"
<< (j+1)%_num_vert << " at t=" << t << endl;
cut_point = from_origin + t*from_direction;
pgraph_cat.debug() << "cut_point: " << cut_point << endl;
if (j == 1) {
// means top should cut 1->2 by moving 2 to the intersection point
_coords[2] = cut_point;
}
else if (j == 3) {
// means top should cut 3->0 by moving 3 to the intersection point
_coords[3] = cut_point;
}
else
pgraph_cat.debug() << "ignored for now for simplicity \n";
}
}
// Look for intersection with the view frustum's right_plane to right_edge of portal
plane = _view_frustum->get_plane(2);
for (j=0; j<_num_vert; ++j) {
from_origin = _coords[j];
from_direction = _coords[(j+1)%_num_vert] - _coords[j];
is_intersect = plane.intersects_line(t, from_origin, from_direction);
if (is_intersect && (t > 0.0 && t < 1.0)) {
pgraph_cat.debug() << "right plane intersected segement " << j << "->"
<< (j+1)%_num_vert << " at t=" << t << endl;
cut_point = from_origin + t*from_direction;
pgraph_cat.debug() << "cut_point: " << cut_point << endl;
if (j == 0) {
// means right should cut 0->1 by moving 1 to the intersection point
_coords[1] = cut_point;
}
else if (j == 2) {
// means bottom should cut 2->3 by moving 2 to the intersection point
_coords[2] = cut_point;
}
else
pgraph_cat.debug() << "ignored for now for simplicity \n";
}
}
// Look for intersection with the view frustum's left_plane to left_edge of portal
plane = _view_frustum->get_plane(4);
for (j=0; j<_num_vert; ++j) {
from_origin = _coords[j];
from_direction = _coords[(j+1)%_num_vert] - _coords[j];
is_intersect = plane.intersects_line(t, from_origin, from_direction);
if (is_intersect && (t > 0.0 && t < 1.0)) {
pgraph_cat.debug() << "left plane intersected segement " << j << "->"
<< (j+1)%_num_vert << " at t=" << t << endl;
cut_point = from_origin + t*from_direction;
pgraph_cat.debug() << "cut_point: " << cut_point << endl;
if (j == 0) {
// means left should cut 0->1 by moving 0 to the intersection point
_coords[0] = cut_point;
}
else if (j == 2) {
// means bottom should cut 2->3 by moving 3 to the intersection point
_coords[3] = cut_point;
}
else
pgraph_cat.debug() << "ignored for now for simplicity \n";
}
}
// ok, now lets add the clipped portal
_color = Colorf(1,0,0,1);
move_to(_coords[0]);
draw_to(_coords[1]);
draw_to(_coords[2]);
draw_to(_coords[3]);
draw_to(_coords[0]);
// 2nd level test, more accurate to determine if the portal is worth visiting
if (!is_facing_camera(node_path)) {
pgraph_cat.debug() << "portal failed 2nd level test \n";
_num_vert = 0;
}
}
@ -390,55 +503,55 @@ get_reduced_frustum(const NodePath &node_path)
float t;
bool visible = true;
// find intersection of 7->3 with far
LPoint3f from_origin = _hex_frustum->get_point(7);
LPoint3f from_origin = _view_frustum->get_point(7);
LVector3f from_direction = _coords[3] - from_origin;
bool is_intersect = _hex_frustum->get_plane(0).intersects_line(t, from_origin, from_direction);
bool is_intersect = _view_frustum->get_plane(0).intersects_line(t, from_origin, from_direction);
if (is_intersect && t >= 0.0) { // has to be positive, else camera is not looking at the portal
pgraph_cat.debug() << "far plane intersected 7->3 at t=" << t << endl;
pgraph_cat.spam() << "far plane intersected 7->3 at t=" << t << endl;
intersect_points[0] = from_origin + t*from_direction;
pgraph_cat.debug() << intersect_points[0] << endl;
pgraph_cat.spam() << intersect_points[0] << endl;
}
else
visible = false;
// find intersection of 4->0 with far
from_origin = _hex_frustum->get_point(4);
from_origin = _view_frustum->get_point(4);
from_direction = _coords[0] - from_origin;
is_intersect = _hex_frustum->get_plane(0).intersects_line(t, from_origin, from_direction);
is_intersect = _view_frustum->get_plane(0).intersects_line(t, from_origin, from_direction);
if (is_intersect && t >= 0.0) { // has to be positive, else camera is not looking at the portal
pgraph_cat.debug() << "far plane intersected 4->0 at t=" << t << endl;
pgraph_cat.spam() << "far plane intersected 4->0 at t=" << t << endl;
intersect_points[1] = from_origin + t*from_direction;
pgraph_cat.debug() << intersect_points[1] << endl;
pgraph_cat.spam() << intersect_points[1] << endl;
}
else
visible = false;
// find intersection of 5->1 with far
from_origin = _hex_frustum->get_point(5);
from_origin = _view_frustum->get_point(5);
from_direction = _coords[1] - from_origin;
is_intersect = _hex_frustum->get_plane(0).intersects_line(t, from_origin, from_direction);
is_intersect = _view_frustum->get_plane(0).intersects_line(t, from_origin, from_direction);
if (is_intersect && t >= 0.0) { // has to be positive, else camera is not looking at the portal
pgraph_cat.debug() << "far plane intersected 5->1 at t=" << t << endl;
pgraph_cat.spam() << "far plane intersected 5->1 at t=" << t << endl;
intersect_points[2] = from_origin + t*from_direction;
pgraph_cat.debug() << intersect_points[2] << endl;
pgraph_cat.spam() << intersect_points[2] << endl;
}
else
visible = false;
// find intersection of 6->2 with far
from_origin = _hex_frustum->get_point(6);
from_origin = _view_frustum->get_point(6);
from_direction = _coords[2] - from_origin;
is_intersect = _hex_frustum->get_plane(0).intersects_line(t, from_origin, from_direction);
is_intersect = _view_frustum->get_plane(0).intersects_line(t, from_origin, from_direction);
if (is_intersect && t >= 0.0) { // has to be positive, else camera is not looking at the portal
pgraph_cat.debug() << "far plane intersected 6->2 at t=" << t << endl;
pgraph_cat.spam() << "far plane intersected 6->2 at t=" << t << endl;
intersect_points[3] = from_origin + t*from_direction;
pgraph_cat.debug() << intersect_points[3] << endl;
pgraph_cat.spam() << intersect_points[3] << endl;
}
else
visible = false;
if (!visible) {
pgraph_cat.debug() << "portal is not visible from current camera look at" << endl;
pgraph_cat.spam() << "portal is not visible from current camera look at" << endl;
return NULL;
}
@ -446,14 +559,16 @@ get_reduced_frustum(const NodePath &node_path)
PT(BoundingVolume) reduced_frustum = new
BoundingHexahedron(intersect_points[1], intersect_points[2],
intersect_points[3], intersect_points[0],
_hex_frustum->get_point(4), _hex_frustum->get_point(5),
_hex_frustum->get_point(6), _hex_frustum->get_point(7));
_view_frustum->get_point(4), _view_frustum->get_point(5),
_view_frustum->get_point(6), _view_frustum->get_point(7));
pgraph_cat.debug() << *reduced_frustum << endl;
pgraph_cat.spam() << *reduced_frustum << endl;
// draw this hexahedron
_color = Colorf(0,0,1,1);
draw_hexahedron(DCAST(BoundingHexahedron, reduced_frustum));
_reduced_frustum = DCAST(BoundingHexahedron, reduced_frustum);
return reduced_frustum;
}

View File

@ -24,6 +24,7 @@
#include "geom.h"
#include "sceneSetup.h"
#include "renderState.h"
#include "portalNode.h"
#include "transformState.h"
#include "geometricBoundingVolume.h"
#include "boundingHexahedron.h"
@ -39,6 +40,7 @@
#include "geomNode.h"
class PandaNode;
class PortalNode;
class CullHandler;
class CullTraverserData;
class CullableObject;
@ -61,7 +63,8 @@ public:
PortalClipper(GeometricBoundingVolume *frustum, SceneSetup *scene_setup);
~PortalClipper();
INLINE bool is_facing_camera();
INLINE bool is_in_view(const NodePath &node_path);
INLINE bool is_facing_camera(const NodePath &node_path);
void prepare_portal(const NodePath &node_path);
void clip_portal(const NodePath &node_path);
@ -80,6 +83,8 @@ public:
INLINE float get_plane_depth(float x, float z, Planef *portal_plane);
INLINE void set_reduced_frustum(BoundingHexahedron *frustum);
public:
static TypeHandle get_class_type() {
return _type_handle;
@ -123,7 +128,10 @@ private:
PT(GeomPoint) _geom_point;
PT(GeomLinestrip) _geom_linestrip;
BoundingHexahedron *_hex_frustum;
BoundingHexahedron *_view_frustum;
BoundingHexahedron *_reduced_frustum;
PortalNode *_portal_node; // current working portal for dereference ease
int _num_vert;
Vertexf _coords[4];

View File

@ -186,7 +186,7 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) {
PT(BoundingVolume) reduced_frustum;
portal_viewer->prepare_portal(data._node_path.get_node_path());
//portal_viewer->clip_portal(data._node_path.get_node_path());
portal_viewer->clip_portal(data._node_path.get_node_path());
if ((reduced_frustum = portal_viewer->get_reduced_frustum(data._node_path.get_node_path()))) {
// This reduced frustum is in camera space
pgraph_cat.debug() << "got reduced frustum " << reduced_frustum << endl;
@ -197,7 +197,7 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) {
portal_viewer->_scene_setup->get_cull_center().get_transform(_zone_out);
vf->xform(cull_center_transform->get_mat());
}
pgraph_cat.debug() << "vf is " << *vf << "\n";
pgraph_cat.spam() << "vf is " << *vf << "\n";
// Get the net trasform of the _zone_out
CPT(TransformState) zone_transform = _zone_out.get_net_transform();
@ -206,7 +206,7 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) {
zone_transform,
trav->get_initial_state(), vf,
trav->get_guard_band());
pgraph_cat.debug() << "cull_callback: traversing " << _zone_out.get_name() << endl;
pgraph_cat.spam() << "cull_callback: traversing " << _zone_out.get_name() << endl;
// Make this zone show with the reduced frustum
_zone_out.show();
trav->traverse(next_data);