Recursive portal culling is implemented. This is the 3rd phase. There is one bug where portals are behaving weird when camera is right on the portal plane

This commit is contained in:
Asad M. Zaman 2004-06-07 23:40:20 +00:00
parent 9ede67f706
commit bd2a0ea086
4 changed files with 72 additions and 52 deletions

View File

@ -101,24 +101,43 @@ draw_camera_frustum() {
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Function: PortalClipper::set_reduced_frustum // Function: PortalClipper::set_view_frustum
// Access: Public // Access: Public
// Description: set the current reduced frustum before traversing // Description: set the current view frustum before traversing
// //
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
INLINE void PortalClipper:: INLINE void PortalClipper::
set_reduced_frustum(BoundingHexahedron *frustum) { set_view_frustum(BoundingHexahedron *frustum) {
_reduced_frustum = frustum; _view_frustum = frustum;
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Function: PortalClipper::is_in_view // Function: PortalClipper::is_facing_view
// Access: Public
// Description: checks if the portal plane (in camera space)
// is facing the camera's near plane
////////////////////////////////////////////////////////////////////
INLINE bool PortalClipper::
is_facing_view(Planef portal_plane)
{
Planef view_plane(_view_frustum->get_point(4), _view_frustum->get_point(5), _view_frustum->get_point(6));
// use the view_frustum's near plane to calculate direction
pgraph_cat.debug() << portal_plane.get_normal() << "; " << -view_plane.get_normal() << endl;
float direction = portal_plane.get_normal().dot(-view_plane.get_normal());
pgraph_cat.debug() << "Found direction of " << direction << endl;
return (direction < _FACING_THRESHOLD);
}
////////////////////////////////////////////////////////////////////
// Function: PortalClipper::is_whole_portal_in_view
// Access: Public // Access: Public
// Description: checks if portal_node is within the view frustum. // Description: checks if portal_node is within the view frustum.
// If so, then the portal is worth considering. This // If so, then the portal is worth considering. This
// is a 1st level test to weed out most of the portals // is a 1st level test to weed out most of the portals
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
INLINE bool PortalClipper:: INLINE bool PortalClipper::
is_in_view(const NodePath &node_path) { is_whole_portal_in_view(const NodePath &node_path) {
const BoundingVolume *bv = &_portal_node->get_bound(); const BoundingVolume *bv = &_portal_node->get_bound();
@ -130,27 +149,28 @@ is_in_view(const NodePath &node_path) {
CPT(TransformState) ctransform = node_path.get_transform(_scene_setup->get_cull_center()); CPT(TransformState) ctransform = node_path.get_transform(_scene_setup->get_cull_center());
gbv->xform(ctransform->get_mat()); gbv->xform(ctransform->get_mat());
int result = _reduced_frustum->contains(gbv); int result = _view_frustum->contains(gbv);
pgraph_cat.debug() << "1st level test if portal is in view " << result << endl;
pgraph_cat.debug() << "1st level test if portal: " << *_view_frustum << " is in view " << result << endl;
return (result != 0); return (result != 0);
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Function: PortalClipper::facing_camera // Function: PortalClipper::is_partial_portal_in_view
// Access: Public // Access: Public
// Description: checks if any of the _coords is within the view frustum. // Description: checks if any of the _coords is within the view frustum.
// If so, then the portal is facing the camera. 2nd level // If so, then the portal is facing the camera. 2nd level
// test to make sure this portal is worth visiting // test to make sure this portal is worth visiting
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
INLINE bool PortalClipper:: INLINE bool PortalClipper::
is_facing_camera(const NodePath &node_path) { is_partial_portal_in_view(const NodePath &node_path) {
int result = 0; int result = 0;
// check if the camera_center to portal_center lineseg is in view // check if any of the _coords in tested frustum
for (int j=0; j<_num_vert; ++j) { for (int j=0; j<_num_vert; ++j) {
result |= _reduced_frustum->contains(_coords[j]); result |= _view_frustum->contains(_coords[j]);
} }
pgraph_cat.debug() << "frustum->contains(coord) result = " << result << endl; pgraph_cat.debug() << "frustum: " << *_view_frustum << " contains(coord) result = " << result << endl;
return (result != 0); return (result != 0);
} }

View File

@ -47,7 +47,7 @@ PortalClipper(GeometricBoundingVolume *frustum, SceneSetup *scene_setup) {
_geom_point = new GeomPoint; _geom_point = new GeomPoint;
_geom_linestrip = new GeomLinestrip; _geom_linestrip = new GeomLinestrip;
_view_frustum = _reduced_frustum = DCAST(BoundingHexahedron, frustum); _view_frustum = DCAST(BoundingHexahedron, frustum);
_scene_setup = scene_setup; _scene_setup = scene_setup;
} }
@ -260,6 +260,10 @@ prepare_portal(const NodePath &node_path)
temp[2] = temp[2]*cmat; temp[2] = temp[2]*cmat;
temp[3] = temp[3]*cmat; temp[3] = temp[3]*cmat;
Planef portal_plane(temp[0], temp[1], temp[2]);
if (!is_facing_view(portal_plane))
return;
pgraph_cat.spam() << "after transformation to camera space" << endl; pgraph_cat.spam() << "after transformation to camera space" << endl;
pgraph_cat.spam() << temp[0] << endl; pgraph_cat.spam() << temp[0] << endl;
pgraph_cat.spam() << temp[1] << endl; pgraph_cat.spam() << temp[1] << endl;
@ -275,9 +279,6 @@ prepare_portal(const NodePath &node_path)
pgraph_cat.spam() << "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]);
float y; float y;
y = get_plane_depth(min_x, min_z, &portal_plane); y = get_plane_depth(min_x, min_z, &portal_plane);
@ -303,7 +304,7 @@ prepare_portal(const NodePath &node_path)
pgraph_cat.spam() << _coords[3] << endl; pgraph_cat.spam() << _coords[3] << endl;
// check if portal is in view // check if portal is in view
if (is_in_view(node_path)) { if (is_whole_portal_in_view(node_path)) {
pgraph_cat.debug() << "portal passed 1st level test \n"; pgraph_cat.debug() << "portal passed 1st level test \n";
// ok, now lets add the original portal // ok, now lets add the original portal
@ -376,7 +377,7 @@ clip_portal(const NodePath &node_path)
from_direction = _coords[(j+1)%_num_vert] - _coords[j]; from_direction = _coords[(j+1)%_num_vert] - _coords[j];
is_intersect = plane.intersects_line(t, from_origin, from_direction); is_intersect = plane.intersects_line(t, from_origin, from_direction);
if (is_intersect && (t > 0.0 && t < 1.0)) { if (is_intersect && (t > 0.0 && t < 1.0)) {
pgraph_cat.debug() << "bottom plane intersected segement " << j << "->" pgraph_cat.debug() << "bottom plane intersected segment " << j << "->"
<< (j+1)%_num_vert << " at t=" << t << endl; << (j+1)%_num_vert << " at t=" << t << endl;
cut_point = from_origin + t*from_direction; cut_point = from_origin + t*from_direction;
pgraph_cat.debug() << "cut_point: " << cut_point << endl; pgraph_cat.debug() << "cut_point: " << cut_point << endl;
@ -400,7 +401,7 @@ clip_portal(const NodePath &node_path)
from_direction = _coords[(j+1)%_num_vert] - _coords[j]; from_direction = _coords[(j+1)%_num_vert] - _coords[j];
is_intersect = plane.intersects_line(t, from_origin, from_direction); is_intersect = plane.intersects_line(t, from_origin, from_direction);
if (is_intersect && (t > 0.0 && t < 1.0)) { if (is_intersect && (t > 0.0 && t < 1.0)) {
pgraph_cat.debug() << "top plane intersected segement " << j << "->" pgraph_cat.debug() << "top plane intersected segment " << j << "->"
<< (j+1)%_num_vert << " at t=" << t << endl; << (j+1)%_num_vert << " at t=" << t << endl;
cut_point = from_origin + t*from_direction; cut_point = from_origin + t*from_direction;
pgraph_cat.debug() << "cut_point: " << cut_point << endl; pgraph_cat.debug() << "cut_point: " << cut_point << endl;
@ -424,7 +425,7 @@ clip_portal(const NodePath &node_path)
from_direction = _coords[(j+1)%_num_vert] - _coords[j]; from_direction = _coords[(j+1)%_num_vert] - _coords[j];
is_intersect = plane.intersects_line(t, from_origin, from_direction); is_intersect = plane.intersects_line(t, from_origin, from_direction);
if (is_intersect && (t > 0.0 && t < 1.0)) { if (is_intersect && (t > 0.0 && t < 1.0)) {
pgraph_cat.debug() << "right plane intersected segement " << j << "->" pgraph_cat.debug() << "right plane intersected segment " << j << "->"
<< (j+1)%_num_vert << " at t=" << t << endl; << (j+1)%_num_vert << " at t=" << t << endl;
cut_point = from_origin + t*from_direction; cut_point = from_origin + t*from_direction;
pgraph_cat.debug() << "cut_point: " << cut_point << endl; pgraph_cat.debug() << "cut_point: " << cut_point << endl;
@ -448,7 +449,7 @@ clip_portal(const NodePath &node_path)
from_direction = _coords[(j+1)%_num_vert] - _coords[j]; from_direction = _coords[(j+1)%_num_vert] - _coords[j];
is_intersect = plane.intersects_line(t, from_origin, from_direction); is_intersect = plane.intersects_line(t, from_origin, from_direction);
if (is_intersect && (t > 0.0 && t < 1.0)) { if (is_intersect && (t > 0.0 && t < 1.0)) {
pgraph_cat.debug() << "left plane intersected segement " << j << "->" pgraph_cat.debug() << "left plane intersected segment " << j << "->"
<< (j+1)%_num_vert << " at t=" << t << endl; << (j+1)%_num_vert << " at t=" << t << endl;
cut_point = from_origin + t*from_direction; cut_point = from_origin + t*from_direction;
pgraph_cat.debug() << "cut_point: " << cut_point << endl; pgraph_cat.debug() << "cut_point: " << cut_point << endl;
@ -473,7 +474,7 @@ clip_portal(const NodePath &node_path)
draw_to(_coords[0]); draw_to(_coords[0]);
// 2nd level test, more accurate to determine if the portal is worth visiting // 2nd level test, more accurate to determine if the portal is worth visiting
if (!is_facing_camera(node_path)) { if (!is_partial_portal_in_view(node_path)) {
pgraph_cat.debug() << "portal failed 2nd level test \n"; pgraph_cat.debug() << "portal failed 2nd level test \n";
_num_vert = 0; _num_vert = 0;
} }
@ -562,13 +563,11 @@ get_reduced_frustum(const NodePath &node_path)
_view_frustum->get_point(4), _view_frustum->get_point(5), _view_frustum->get_point(4), _view_frustum->get_point(5),
_view_frustum->get_point(6), _view_frustum->get_point(7)); _view_frustum->get_point(6), _view_frustum->get_point(7));
pgraph_cat.spam() << *reduced_frustum << endl; pgraph_cat.debug() << *reduced_frustum << endl;
// draw this hexahedron // draw this hexahedron
_color = Colorf(0,0,1,1); _color = Colorf(0,0,1,1);
draw_hexahedron(DCAST(BoundingHexahedron, reduced_frustum)); draw_hexahedron(DCAST(BoundingHexahedron, reduced_frustum));
_reduced_frustum = DCAST(BoundingHexahedron, reduced_frustum);
return reduced_frustum; return reduced_frustum;
} }

View File

@ -63,12 +63,12 @@ public:
PortalClipper(GeometricBoundingVolume *frustum, SceneSetup *scene_setup); PortalClipper(GeometricBoundingVolume *frustum, SceneSetup *scene_setup);
~PortalClipper(); ~PortalClipper();
INLINE bool is_in_view(const NodePath &node_path); INLINE bool is_whole_portal_in_view(const NodePath &node_path);
INLINE bool is_facing_camera(const NodePath &node_path); INLINE bool is_partial_portal_in_view(const NodePath &node_path);
INLINE bool is_facing_view(Planef portal_plane);
void prepare_portal(const NodePath &node_path); void prepare_portal(const NodePath &node_path);
void clip_portal(const NodePath &node_path); void clip_portal(const NodePath &node_path);
PT(BoundingVolume) get_reduced_frustum(const NodePath &node_path); PT(BoundingVolume) get_reduced_frustum(const NodePath &node_path);
void draw_lines(); void draw_lines();
@ -83,7 +83,7 @@ public:
INLINE float get_plane_depth(float x, float z, Planef *portal_plane); INLINE float get_plane_depth(float x, float z, Planef *portal_plane);
INLINE void set_reduced_frustum(BoundingHexahedron *frustum); INLINE void set_view_frustum(BoundingHexahedron *frustum);
public: public:
static TypeHandle get_class_type() { static TypeHandle get_class_type() {
@ -129,7 +129,6 @@ private:
PT(GeomLinestrip) _geom_linestrip; PT(GeomLinestrip) _geom_linestrip;
BoundingHexahedron *_view_frustum; BoundingHexahedron *_view_frustum;
BoundingHexahedron *_reduced_frustum;
PortalNode *_portal_node; // current working portal for dereference ease PortalNode *_portal_node; // current working portal for dereference ease

View File

@ -48,7 +48,7 @@ PortalNode(const string &name) :
{ {
_zone_in = NULL; _zone_in = NULL;
_zone_out = NULL; _zone_out = NULL;
_visible = false; _visible = true;
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
@ -65,7 +65,7 @@ PortalNode(const PortalNode &copy) :
{ {
_zone_in = NULL; _zone_in = NULL;
_zone_out = NULL; _zone_out = NULL;
_visible = false; _visible = true;
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
@ -180,28 +180,30 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) {
PortalClipper *portal_viewer = trav->get_portal_clipper(); PortalClipper *portal_viewer = trav->get_portal_clipper();
if (!_zone_out.is_empty() && portal_viewer) { if (!_zone_out.is_empty() && portal_viewer) {
//CullTraverserData next_data(data, _zone_out); //CullTraverserData next_data(data, _zone_out);
if (is_visible()) { pgraph_cat.debug() << "checking portal node " << *this << endl;
pgraph_cat.debug() << "portal node visible " << *this << endl; PT(GeometricBoundingVolume) vf = trav->get_view_frustum();
PT(GeometricBoundingVolume) vf = trav->get_view_frustum(); PT(BoundingVolume) reduced_frustum;
PT(BoundingVolume) reduced_frustum;
// following three functions do nothing, if the portal is not visible
portal_viewer->prepare_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;
vf = DCAST(GeometricBoundingVolume, reduced_frustum);
portal_viewer->set_view_frustum(DCAST(BoundingHexahedron,vf->make_copy()));
// trasform it to cull_center space
CPT(TransformState) cull_center_transform =
portal_viewer->_scene_setup->get_cull_center().get_transform(_zone_out);
vf->xform(cull_center_transform->get_mat());
portal_viewer->prepare_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;
vf = DCAST(GeometricBoundingVolume, reduced_frustum);
// trasform it to cull_center space
CPT(TransformState) cull_center_transform =
portal_viewer->_scene_setup->get_cull_center().get_transform(_zone_out);
vf->xform(cull_center_transform->get_mat());
}
pgraph_cat.spam() << "vf is " << *vf << "\n"; pgraph_cat.spam() << "vf is " << *vf << "\n";
// Get the net trasform of the _zone_out // Get the net trasform of the _zone_out
CPT(TransformState) zone_transform = _zone_out.get_net_transform(); CPT(TransformState) zone_transform = _zone_out.get_net_transform();
CullTraverserData next_data(_zone_out, trav->get_render_transform()->compose(zone_transform), CullTraverserData next_data(_zone_out, trav->get_render_transform()->compose(zone_transform),
zone_transform, zone_transform,
trav->get_initial_state(), vf, trav->get_initial_state(), vf,