Browse Source

PortalNode contributions by Erik Pojar

David Rose 16 years ago
parent
commit
fd305329c7

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

@@ -118,11 +118,12 @@ ConfigVariableBool allow_portal_cull
           "renderer to cull more objects that are clipped if not in the "
           "current list of portals.  This is still somewhat experimental."));
 
-ConfigVariableBool show_portal_debug
-("show-portal-debug", true,
- PRC_DESC("Set this true to show debug lines for portals.  This will draw "
-          "lines from the screen corners to the portal corners, this can "
-          "be useful when debugging."));
+ConfigVariableBool debug_portal_cull
+("debug-portal-cull", false,
+ PRC_DESC("Set this true to enable debug visualization during portal clipping."
+          "(You first need to enable portal culling, using the allow-portal-cull"
+          "variable.)"));
+
 
 ConfigVariableBool unambiguous_graph
 ("unambiguous-graph", false,

+ 1 - 1
panda/src/pgraph/config_pgraph.h

@@ -33,7 +33,7 @@ NotifyCategoryDecl(portal, EXPCL_PANDA_PGRAPH, EXPTP_PANDA_PGRAPH);
 extern ConfigVariableBool fake_view_frustum_cull;
 extern ConfigVariableBool clip_plane_cull;
 extern ConfigVariableBool allow_portal_cull;
-extern ConfigVariableBool show_portal_debug;
+extern ConfigVariableBool debug_portal_cull;
 extern ConfigVariableBool unambiguous_graph;
 extern ConfigVariableBool detect_graph_cycles;
 extern ConfigVariableBool no_unsupported_copy;

+ 4 - 3
panda/src/pgraph/cullTraverser.cxx

@@ -121,7 +121,8 @@ traverse(const NodePath &root) {
 
   if (allow_portal_cull) {
     // This _view_frustum is in cull_center space
-    PT(GeometricBoundingVolume) vf = _view_frustum;
+    //Erik: obsolete?
+    //PT(GeometricBoundingVolume) vf = _view_frustum;
 
     GeometricBoundingVolume *local_frustum = NULL;
     PT(BoundingVolume) bv = _scene_setup->get_lens()->make_bounds();
@@ -133,7 +134,7 @@ traverse(const NodePath &root) {
       
     // This local_frustum is in camera space
     PortalClipper portal_viewer(local_frustum, _scene_setup);
-    if (show_portal_debug) {
+    if (debug_portal_cull) {
       portal_viewer.draw_camera_frustum();
     }
     
@@ -147,7 +148,7 @@ traverse(const NodePath &root) {
     traverse(data);
     
     // Finally add the lines to be drawn
-    if (show_portal_debug) {
+    if (debug_portal_cull) {
       portal_viewer.draw_lines();
     }
     

+ 54 - 0
panda/src/pgraph/portalClipper.I

@@ -117,6 +117,56 @@ INLINE BoundingHexahedron *PortalClipper::
 get_reduced_frustum() const {
   return _reduced_frustum;
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: PortalClipper::set_clip_state
+//       Access: Public
+//  Description: Set the clip state of the current portal node
+//               This is done to remember the state for the child portal nodes
+//           
+////////////////////////////////////////////////////////////////////
+INLINE void PortalClipper::
+set_clip_state(const RenderState* clip_state) {
+  _clip_state = clip_state;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PortalClipper::get_clip_state
+//       Access: Published
+//  Description: Returns the stored clip state
+////////////////////////////////////////////////////////////////////
+INLINE const RenderState *PortalClipper::
+get_clip_state() const {
+  return _clip_state;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PortalClipper::set_reduced_viewport
+//       Access: Public
+//  Description: Set the current viewport that is being used
+//               by the portal clipper
+//           
+////////////////////////////////////////////////////////////////////
+INLINE void PortalClipper::
+set_reduced_viewport(const LPoint2f& min, const LPoint2f& max) {
+  _reduced_viewport_min = min;
+  _reduced_viewport_max = max;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: PortalClipper::get_reduced_viewport
+//       Access: Published
+//  Description: Return the reduced viewport
+////////////////////////////////////////////////////////////////////
+INLINE void PortalClipper::
+get_reduced_viewport(LPoint2f& min, LPoint2f& max) const  {
+  min = _reduced_viewport_min;
+  max = _reduced_viewport_max;
+}
+
+
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PortalClipper::is_facing_view
 //       Access: Public
@@ -159,6 +209,7 @@ is_whole_portal_in_view(LMatrix4f cmat) {
   return (result != 0);
 }
 
+/*
 ////////////////////////////////////////////////////////////////////
 //     Function: PortalClipper::is_partial_portal_in_view
 //       Access: Public
@@ -178,7 +229,9 @@ is_partial_portal_in_view() {
 
   return (result != 0);
 }
+*/
 
+/*
 ////////////////////////////////////////////////////////////////////
 //     Function: PortalClipper::get_plane_depth
 //       Access: Public
@@ -199,3 +252,4 @@ get_plane_depth(float x, float z, Planef *portal_plane) {
   }
   return y;    
 }
+*/

+ 83 - 309
panda/src/pgraph/portalClipper.cxx

@@ -39,11 +39,16 @@ TypeHandle PortalClipper::_type_handle;
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 PortalClipper::
-PortalClipper(GeometricBoundingVolume *frustum, SceneSetup *scene_setup) {
+PortalClipper(GeometricBoundingVolume *frustum, SceneSetup *scene_setup):
+_reduced_viewport_min(-1,-1),
+_reduced_viewport_max(1,1),
+_clip_state(0)
+{
   _previous = new GeomNode("my_frustum");
 
   _view_frustum = _reduced_frustum = DCAST(BoundingHexahedron, frustum);
 
+
   _scene_setup = scene_setup;
 }
 
@@ -142,13 +147,13 @@ draw_current_portal()
   draw_to(_portal_node->get_vertex(2));
   draw_to(_portal_node->get_vertex(3));
 }
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PortalClipper::draw the lines
 //       Access: Public
 //  Description: Draw all the lines in the buffer
-//               Yellow portal is the original geometry of the portal
-//               Cyan portal is the minmax adjusted portal
-//               Red portal is the clipped against frustum portal
+//               Cyan portal is the original geometry of the portal
+//               Yellow portal is the AA minmax & clipped portal
 //               Blue frustum is the frustum through portal
 //               White frustum is the camera frustum
 ////////////////////////////////////////////////////////////////////
@@ -213,33 +218,19 @@ draw_lines() {
 //  Description: Given the portal draw the frustum with line segs
 //               for now. More functionalities coming up
 ////////////////////////////////////////////////////////////////////
-void PortalClipper::
+bool PortalClipper::
 prepare_portal(const NodePath &node_path)
 {
-  SegmentList segs;
-
   // Get the Portal Node from this node_path
   PandaNode *node = node_path.node();
   _portal_node = NULL;
   if (node->is_of_type(PortalNode::get_class_type())) {  
     _portal_node = DCAST(PortalNode, node);
-    // lets draw the portal anyway
-    //draw_current_portal();
   }
 
-  // walk the portal
-  _num_vert = 0;
-
   // Get the geometry from the portal    
   portal_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();
-  portal_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());
@@ -265,8 +256,8 @@ prepare_portal(const NodePath &node_path)
 
   Planef portal_plane(temp[0], temp[1], temp[2]);
   if (!is_facing_view(portal_plane)) {
-    portal_cat.debug() << "portal failed 1st level test \n";
-    return;
+    portal_cat.debug() << "portal failed 1st level test (isn't facing the camera)\n";
+    return false;
   }
 
   portal_cat.spam() << "after transformation to camera space" << endl;
@@ -274,309 +265,92 @@ prepare_portal(const NodePath &node_path)
   portal_cat.spam() << temp[1] << endl;
   portal_cat.spam() << temp[2] << endl;
   portal_cat.spam() << temp[3] << endl;
+
+  // check if the portal intersects with the cameras 0 point (center of projection). In that case the portal will invert itself.
+  // portals intersecting the near plane or the 0 point are a weird case anyhow, therefore we don't reduce the frustum any further
+  // and just return true. In effect the portal doesn't reduce visibility but will draw everything in its out cell
+  if ((temp[0][1] <= 0) || (temp[1][1] <= 0) || (temp[2][1] <= 0) || (temp[3][1] <= 0)) {
+      portal_cat.debug() << "portal intersects with center of projection.." << endl;
+      return true;
+  }
   
-  float min_x, max_x, min_z, max_z;
+  // project portal points, so they are in the -1..1 range
+  LPoint3f projected_coords[4];
+  const Lens *lens = _scene_setup->get_lens();
+  lens->project(temp[0], projected_coords[0]);
+  lens->project(temp[1], projected_coords[1]);
+  lens->project(temp[2], projected_coords[2]);
+  lens->project(temp[3], projected_coords[3]);
+
+  portal_cat.spam() << "after projection to 2d" << endl;
+  portal_cat.spam() << projected_coords[0] << endl;
+  portal_cat.spam() << projected_coords[1] << endl;
+  portal_cat.spam() << projected_coords[2] << endl;
+  portal_cat.spam() << projected_coords[3] << endl;
+
+  // calculate axis aligned bounding box of the portal
+  float min_x, max_x, min_y, max_y;
+  min_x = min(min(min(projected_coords[0][0], projected_coords[1][0]), projected_coords[2][0]), projected_coords[3][0]);
+  max_x = max(max(max(projected_coords[0][0], projected_coords[1][0]), projected_coords[2][0]), projected_coords[3][0]);
+  min_y = min(min(min(projected_coords[0][1], projected_coords[1][1]), projected_coords[2][1]), projected_coords[3][1]);
+  max_y = max(max(max(projected_coords[0][1], projected_coords[1][1]), projected_coords[2][1]), projected_coords[3][1]);
+
+  portal_cat.spam() << "min_x " << min_x << ";max_x " << max_x << ";min_y " << min_y << ";max_y " << max_y << endl;
+
+  // clip the minima and maxima against the viewport
+  min_x = max(min_x, _reduced_viewport_min[0]);
+  min_y = max(min_y, _reduced_viewport_min[1]);
+  max_x = min(max_x, _reduced_viewport_max[0]);
+  max_y = min(max_y, _reduced_viewport_max[1]);
+
+  portal_cat.spam() << "after clipping: min_x " << min_x << ";max_x " << max_x << ";min_y " << min_y << ";max_y " << max_y << endl;
+
+  if ((min_x >= max_x) || (min_y >= max_y)) {
+      portal_cat.debug() << "portal got clipped away \n";
+      return false;
+  }
 
-  min_x = min(min(min(temp[0][0], temp[1][0]), temp[2][0]), temp[3][0]);
-  max_x = max(max(max(temp[0][0], temp[1][0]), temp[2][0]), temp[3][0]);
-  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]);
+  // here we know the portal is in view and we have its clipped extents
+  _reduced_viewport_min.set(min_x, min_y);
+  _reduced_viewport_max.set(max_x, max_y);
 
-  portal_cat.spam() << "min_x " << min_x << ";max_x " << max_x << ";min_z " << min_z << ";max_z " << max_z << endl;
+  // calculate the near and far points so we can construct a frustum
+  LPoint3f near_point[4];
+  LPoint3f far_point[4];
+  lens->extrude(LPoint2f(min_x, min_y), near_point[0], far_point[0]);
+  lens->extrude(LPoint2f(max_x, min_y), near_point[1], far_point[1]);
+  lens->extrude(LPoint2f(max_x, max_y), near_point[2], far_point[2]);
+  lens->extrude(LPoint2f(min_x, max_y), near_point[3], far_point[3]);
 
-  float y;
+  // With these points, construct the new reduced frustum
+  _reduced_frustum = new BoundingHexahedron(far_point[0], far_point[1], far_point[2], far_point[3],  
+                                            near_point[0], near_point[1], near_point[2], near_point[3]);
 
-  y = get_plane_depth(min_x, min_z, &portal_plane);
-  portal_cat.spam() << "plane's depth is " << y << endl;
-  _coords[0].set(min_x, y, min_z);
+  portal_cat.debug() << *_reduced_frustum << endl;
 
-  y = get_plane_depth(max_x, min_z, &portal_plane);
-  portal_cat.spam() << "plane's depth is " << y << endl;
-  _coords[1].set(max_x, y, min_z);
+  // do debug rendering, if requested
+  if (debug_portal_cull) {
+    // draw the reduced frustum
+    _color = Colorf(0,0,1,1);
+    draw_hexahedron(DCAST(BoundingHexahedron, _reduced_frustum));
 
-  y = get_plane_depth(max_x, max_z, &portal_plane);
-  portal_cat.spam() << "plane's depth is " << y << endl;
-  _coords[2].set(max_x, y, max_z);
+    // lets first add the clipped portal (in yellow)
+    _color = Colorf(1,1,0,1);
+    move_to((near_point[0]+far_point[0])/2.0); // I choose a point in the middle between near and far.. could also be some other z value.. 
+    draw_to((near_point[1]+far_point[1])/2.0);
+    draw_to((near_point[2]+far_point[2])/2.0);
+    draw_to((near_point[3]+far_point[3])/2.0);
+    draw_to((near_point[0]+far_point[0])/2.0);
 
-  y = get_plane_depth(min_x, max_z, &portal_plane);
-  portal_cat.spam() << "plane's depth is " << y << endl;
-  _coords[3].set(min_x, y, max_z);
-    
-  portal_cat.spam() << "after min max calculation" << endl;
-  portal_cat.spam() << _coords[0] << endl;
-  portal_cat.spam() << _coords[1] << endl;
-  portal_cat.spam() << _coords[2] << endl;
-  portal_cat.spam() << _coords[3] << endl;
-
-  // check if portal is in view
-  if (is_whole_portal_in_view(cmat)) {
-    // ok, now lets add the original portal
+    // ok, now lets add the original portal (in cyan) 
     _color = Colorf(0,1,1,1);
     move_to(temp[0]);
     draw_to(temp[1]);
     draw_to(temp[2]);
     draw_to(temp[3]);
     draw_to(temp[0]);
-
-    // ok, now lets add the min_max portal
-    _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]);
-    
-    portal_cat.spam() << "assembled " << _portal_node->get_name() << ": frustum points" << endl;
-    _num_vert = _portal_node->get_num_vertices();
-  }
-  else
-    portal_cat.debug() << "portal failed 2nd level test \n";
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PortalClipper::clip the portal
-//       Access: Public
-//  Description: From the frustum clip the portal against the frustum
-//               and form the new planes of the reduced view frustum
-////////////////////////////////////////////////////////////////////
-void PortalClipper::
-clip_portal(const NodePath &node_path)
-{
-  if (!_num_vert)
-    return;
-
-  // 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;
-  unsigned int xect=0;
-  LPoint3f from_origin;
-  LPoint3f cut_point;
-  LVector3f from_direction;
-
-  // Look for intersection with the view frustum's bottom_plane and portal edges
-  plane = _reduced_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)) {
-      xect |= 1 << 0;
-      portal_cat.debug() << "bottom plane intersected segment " << j << "->" 
-                         << (j+1)%_num_vert << " at t=" << t << endl;
-      cut_point = from_origin + t*from_direction;
-      portal_cat.spam() << "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
-        portal_cat.debug() << "ignored for now for simplicity \n";
-    }
-    else
-      portal_cat.spam() << "is_intersect: " << is_intersect << " at t = " << t << endl;
-  }
-
-  // Look for intersection with the view frustum's top_plane and portal edges
-  plane = _reduced_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)) {
-      xect |= 1 << 1;
-      portal_cat.debug() << "top plane intersected segment " << j << "->" 
-                         << (j+1)%_num_vert << " at t=" << t << endl;
-      cut_point = from_origin + t*from_direction;
-      portal_cat.spam() << "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
-        portal_cat.debug() << "ignored for now for simplicity \n";
-    }
-    else
-      portal_cat.spam() << "is_intersect: " << is_intersect << " at t = " << t << endl;
-  }
-
-  // Look for intersection with the view frustum's right_plane and portal edges
-  plane = _reduced_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)) {
-      xect |= 1 << 2;
-      portal_cat.debug() << "right plane intersected segment " << j << "->" 
-                         << (j+1)%_num_vert << " at t=" << t << endl;
-      cut_point = from_origin + t*from_direction;
-      portal_cat.spam() << "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 right should cut 2->3 by moving 2 to the intersection point
-        _coords[2] = cut_point;
-      }
-      else
-        portal_cat.debug() << "ignored for now for simplicity \n";
-    }
-    else
-      portal_cat.spam() << "is_intersect: " << is_intersect << " at t = " << t << endl;
   }
 
-  // Look for intersection with the view frustum's left_plane and portal edges
-  plane = _reduced_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)) {
-      xect |= 1 << 3;
-      portal_cat.debug() << "left plane intersected segment " << j << "->" 
-                         << (j+1)%_num_vert << " at t=" << t << endl;
-      cut_point = from_origin + t*from_direction;
-      portal_cat.spam() << "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 left should cut 2->3 by moving 3 to the intersection point
-        _coords[3] = cut_point;
-      }
-      else
-        portal_cat.debug() << "ignored for now for simplicity \n";
-    }
-    else
-      portal_cat.spam() << "is_intersect: " << is_intersect << " at t = " << t << endl;
-  }
-  // 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]);
-
-  // 3rd level test, more accurate to determine if the portal is worth visiting
-  portal_cat.debug() << "portal clipper flag: " << xect << endl;
-  if (xect == 0xf) {  //if all four planes intersected the portal, it is visible
-    return;
-  }
-  if (!is_partial_portal_in_view()) {
-    portal_cat.debug() << "portal failed 3rd level test \n";
-    _num_vert = 0;
-  }
+  return true;
 }
 
-
-////////////////////////////////////////////////////////////////////
-//     Function: PortalClipper::get_reduced_frustum
-//       Access: Public
-//  Description: After clipping the portal, form the new sides and 
-//               fill in the new frustum. Return the new frustum
-////////////////////////////////////////////////////////////////////
-PT(BoundingVolume) PortalClipper::
-get_reduced_frustum(const NodePath &node_path)
-{
-  //  int num_planes = 6;
-  LPoint3f intersect_points[4];
-  
-  // another approach to actually finding the points, so that 
-  // I can reuse the current BoundingHexahedron object. Apparently,
-  // it is better to construct this BH with bounding points, rather
-  // than bounding planes (which I might have to implement soon)
-  
-  if (!_num_vert)
-    return NULL;
-  
-  float t;
-  bool visible = true;
-
-  // find intersection of 7->3 with far
-  LPoint3f from_origin = _reduced_frustum->get_point(7);
-  LVector3f from_direction = _coords[3] - from_origin;
-  bool is_intersect = _reduced_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
-    portal_cat.spam() << "far plane intersected 7->3 at t=" << t << endl;
-    intersect_points[0] = from_origin + t*from_direction;
-    portal_cat.spam() << intersect_points[0] << endl;
-  }
-  else
-    visible = false;
-
-  // find intersection of 4->0 with far
-  from_origin = _reduced_frustum->get_point(4);
-  from_direction = _coords[0] - from_origin;
-  is_intersect = _reduced_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
-    portal_cat.spam() << "far plane intersected 4->0 at t=" << t << endl;
-    intersect_points[1] = from_origin + t*from_direction;
-    portal_cat.spam() << intersect_points[1] << endl;
-  }
-  else
-    visible = false;
-
-  // find intersection of 5->1 with far
-  from_origin = _reduced_frustum->get_point(5);
-  from_direction = _coords[1] - from_origin;
-  is_intersect = _reduced_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
-    portal_cat.spam() << "far plane intersected 5->1 at t=" << t << endl;
-    intersect_points[2] = from_origin + t*from_direction;
-    portal_cat.spam() << intersect_points[2] << endl;
-  }
-  else
-    visible = false;
-
-  // find intersection of 6->2 with far
-  from_origin = _reduced_frustum->get_point(6);
-  from_direction = _coords[2] - from_origin;
-  is_intersect = _reduced_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
-    portal_cat.spam() << "far plane intersected 6->2 at t=" << t << endl;
-    intersect_points[3] = from_origin + t*from_direction;
-    portal_cat.spam() << intersect_points[3] << endl;
-  }
-  else
-    visible = false;
-
-  if (!visible) {
-    portal_cat.spam() << "portal is not visible from current camera look at" << endl;
-    return NULL;
-  }
-  
-  // With these intersect_points, construct the new reduced frustum
-  PT(BoundingVolume) reduced_frustum = new
-    BoundingHexahedron(intersect_points[1], intersect_points[2],
-                       intersect_points[3], intersect_points[0],
-                       _reduced_frustum->get_point(4), _reduced_frustum->get_point(5),
-                       _reduced_frustum->get_point(6), _reduced_frustum->get_point(7));
-
-  portal_cat.debug() << *reduced_frustum << endl;
-
-  // draw this hexahedron
-  _color = Colorf(0,0,1,1);
-  draw_hexahedron(DCAST(BoundingHexahedron, reduced_frustum));
-
-  return reduced_frustum;
-}

+ 11 - 10
panda/src/pgraph/portalClipper.h

@@ -41,8 +41,6 @@ class CullTraverserData;
 class CullableObject;
 class NodePath;
 
-//#define _FACING_THRESHOLD -0.7  //about 45 degrees with the camera
-//#define _FACING_THRESHOLD -0.5  //about 60 degrees with the camera
 #define _FACING_THRESHOLD 0.0  //about 90 degrees with the camera
 
 ////////////////////////////////////////////////////////////////////
@@ -62,10 +60,8 @@ public:
   INLINE bool is_facing_view(Planef portal_plane);
   INLINE bool is_whole_portal_in_view(LMatrix4f cmat);
 
-  void prepare_portal(const NodePath &node_path);
-  void clip_portal(const NodePath &node_path);
-  PT(BoundingVolume) get_reduced_frustum(const NodePath &node_path);
-
+  bool prepare_portal(const NodePath &node_path);
+  
   void draw_lines();
   INLINE void draw_camera_frustum();
   void draw_hexahedron(BoundingHexahedron *frustum);
@@ -78,10 +74,12 @@ public:
 
   void draw_current_portal();
 
-  INLINE float get_plane_depth(float x, float z, Planef *portal_plane);
-
   INLINE BoundingHexahedron *get_reduced_frustum() const;
   INLINE void set_reduced_frustum(BoundingHexahedron *bh);
+  INLINE void get_reduced_viewport(LPoint2f& min, LPoint2f& max) const; 
+  INLINE void set_reduced_viewport(const LPoint2f& min, const LPoint2f& max);
+  INLINE const RenderState* get_clip_state() const; 
+  INLINE void set_clip_state(const RenderState* clip_state);
 
 public:
   static TypeHandle get_class_type() {
@@ -122,11 +120,14 @@ private:
 
   BoundingHexahedron *_view_frustum;
   BoundingHexahedron *_reduced_frustum;
+  LPoint2f _reduced_viewport_min;
+  LPoint2f _reduced_viewport_max;
+  CPT(RenderState) _clip_state; // each portal node needs to know the clip state of its "parent" portal Node
 
   PortalNode *_portal_node;  // current working portal for dereference ease
 
-  int _num_vert;
-  Vertexf _coords[4];
+  //int _num_vert;
+  //Vertexf _coords[4];
 
 public:
   PT(GeomNode) _previous;

+ 92 - 93
panda/src/pgraph/portalNode.cxx

@@ -29,12 +29,6 @@
 
 #include "plane.h"
 
-/*
-#ifndef CPPPARSER
-#include "../collide/collisionPlane.h"
-#endif
-*/
-
 TypeHandle PortalNode::_type_handle;
 
 
@@ -146,32 +140,21 @@ preserve_name() const {
 ////////////////////////////////////////////////////////////////////
 void PortalNode::
 enable_clipping_planes() {
+  _top_plane_node = new PlaneNode("top");
+  NodePath top_plane_np = NodePath(this).attach_new_node(_top_plane_node);
+
+  _bottom_plane_node = new PlaneNode("bottom");
+  NodePath bottom_plane_np = NodePath(this).attach_new_node(_bottom_plane_node);
+
   _left_plane_node = new PlaneNode("left");
   NodePath left_plane_np = NodePath(this).attach_new_node(_left_plane_node);
 
   _right_plane_node = new PlaneNode("right");
   NodePath right_plane_np = NodePath(this).attach_new_node(_right_plane_node);
 
-  /*
-  // for debugging visialization, attach a collsion plane to left and right each
-  _left_coll_node = new CollisionNode("left_coll");
-  _left_coll_node->set_into_collide_mask(CollideMask::all_off());
-  // prepare a collision plane to be set later 
-  PT(CollisionPlane) left_coll_plane = new CollisionPlane(Planef());
-  _left_coll_node->add_solid(left_coll_plane);
-  // attach it onto the _left_plane_np
-  left_plane_np.attach_new_node(_left_coll_node);
-
-  _right_coll_node = new CollisionNode("right_coll");
-  _right_coll_node->set_into_collide_mask(CollideMask::all_off());
-  // prepare a collision plane to be set later 
-  PT(CollisionPlane) right_coll_plane = new CollisionPlane(Planef());
-  _right_coll_node->add_solid(right_coll_plane);
-  // attach it onto the _left_plane_np
-  right_plane_np.attach_new_node(_right_coll_node);
-  */
-
   CPT(RenderAttrib) plane_attrib = ClipPlaneAttrib::make();
+  plane_attrib = DCAST(ClipPlaneAttrib, plane_attrib)->add_on_plane(NodePath(top_plane_np));
+  plane_attrib = DCAST(ClipPlaneAttrib, plane_attrib)->add_on_plane(NodePath(bottom_plane_np));
   plane_attrib = DCAST(ClipPlaneAttrib, plane_attrib)->add_on_plane(NodePath(left_plane_np));
   plane_attrib = DCAST(ClipPlaneAttrib, plane_attrib)->add_on_plane(NodePath(right_plane_np));
 
@@ -244,81 +227,97 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) {
   PortalClipper *portal_viewer = trav->get_portal_clipper();
   set_visible(false);
   if (is_open() && !_cell_out.is_empty() && portal_viewer) {
-    //CullTraverserData next_data(data, _cell_out);
     portal_cat.debug() << "checking portal node  " << *this << endl;
     PT(GeometricBoundingVolume) vf = trav->get_view_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()))) {
-      set_visible(true);
-      // This reduced frustum is in camera space
-      portal_cat.debug() << "got reduced frustum " << reduced_frustum << endl;
-      vf = DCAST(GeometricBoundingVolume, reduced_frustum);
-      
-      // keep a copy of this reduced frustum
-      PT(BoundingHexahedron) new_bh = DCAST(BoundingHexahedron, vf->make_copy());
-      
-      if (_clip_plane) {
-        // make a temp copy of this reduced frustum
-        PT(BoundingHexahedron) temp_bh = DCAST(BoundingHexahedron, vf->make_copy());
-        CPT(TransformState) ftransform = 
-          _cell_in.get_net_transform()->invert_compose(portal_viewer->_scene_setup->get_cull_center().get_net_transform());
-
-        temp_bh->xform(ftransform->get_mat());
+    
+    // remember old viewport and frustum, so we can restore them for the siblings. (it gets changed by the prepare_portal call)
+    LPoint2f old_reduced_viewport_min, old_reduced_viewport_max;
+    portal_viewer->get_reduced_viewport(old_reduced_viewport_min, old_reduced_viewport_max);
+    PT(BoundingHexahedron) old_bh = portal_viewer->get_reduced_frustum();
+
+    if (portal_viewer->prepare_portal(data._node_path.get_node_path())) {
+      if ((reduced_frustum = portal_viewer->get_reduced_frustum())) {
+        // remember current clip state, we might change it
+        CPT(RenderState) old_clip_state = portal_viewer->get_clip_state();
+
+        set_visible(true);
+        // The frustum is in camera space
+        vf = DCAST(GeometricBoundingVolume, reduced_frustum);
         
-        // set left/right clipping plane
-        _left_plane_node->set_plane(-temp_bh->get_plane(4)); // left plane of bh
-        _right_plane_node->set_plane(-temp_bh->get_plane(2));// right plane of bh
-
-        /*
-        // set this plane at the collision plane too for debugging
-        ((CollisionPlane*)_left_coll_node->get_solid(0))->set_plane(-temp_bh->get_plane(4));
-        ((CollisionPlane*)_right_coll_node->get_solid(0))->set_plane(-temp_bh->get_plane(2));
-        */
-      }
-
-      // Get the net trasform of the _cell_out as seen from the camera.
-      CPT(TransformState) cell_transform = 
-        //        trav->get_camera_transform()->invert_compose(_cell_out.get_net_transform());
-        _cell_out.get_net_transform();
-
-      CPT(TransformState) frustum_transform = 
-        _cell_out.get_net_transform()->invert_compose(portal_viewer->_scene_setup->get_cull_center().get_net_transform());
-
-      new_bh->xform(frustum_transform->get_mat());
-      
-      portal_cat.spam() << "new_bh is " << *new_bh << "\n";
-  
-      CPT(RenderState) next_state = data._state;
+        // create a copy of this reduced frustum, we'll transform it from camera space to the cell_out space
+        PT(BoundingHexahedron) new_bh = DCAST(BoundingHexahedron, vf->make_copy());
+        
+        // Get the net trasform of the _cell_out as seen from the camera.
+        CPT(TransformState) cell_transform = _cell_out.get_net_transform();
+        CPT(TransformState) frustum_transform = cell_transform ->invert_compose(portal_viewer->_scene_setup->get_cull_center().get_net_transform());
 
-      // attach clipping state if there is any
-      if (_clip_plane) {
-        next_state = next_state->compose(_clip_state);
+        // transform to _cell_out space
+        new_bh->xform(frustum_transform->get_mat());
+        
+        CPT(RenderState) next_state = data._state;
+
+        // set clipping planes, if desired..
+        if (_clip_plane) {
+          // create a copy of this reduced frustum, we'll transform it from camera space to this portal node's space (because the clip planes are attached to this node)
+          PT(BoundingHexahedron) temp_bh = DCAST(BoundingHexahedron, vf->make_copy());
+          CPT(TransformState) temp_frustum_transform = data._node_path.get_node_path().get_net_transform()->invert_compose(portal_viewer->_scene_setup->get_cull_center().get_net_transform());
+          
+          portal_cat.spam() << "clipping plane frustum transform " << *temp_frustum_transform << endl;
+          portal_cat.spam() << "frustum before transform " << *temp_bh << endl;	
+          // transform to portalNode space
+          temp_bh->xform(temp_frustum_transform->get_mat());
+
+          portal_cat.spam() << "frustum after transform " << *temp_bh << endl;
+          
+          _left_plane_node->set_plane(-temp_bh->get_plane(4)); // left plane of bh
+          _right_plane_node->set_plane(-temp_bh->get_plane(2));// right plane of bh
+          _top_plane_node->set_plane(-temp_bh->get_plane(3)); // top plane of bh
+          _bottom_plane_node->set_plane(-temp_bh->get_plane(1));// bottom plane of bh
+
+          portal_cat.spam() << "left plane " << *_left_plane_node << endl;
+          portal_cat.spam() << "right plane " << *_right_plane_node << endl;
+          portal_cat.spam() << "top plane " << *_top_plane_node << endl;
+          portal_cat.spam() << "bottom plane " << *_bottom_plane_node << endl;
+
+          // remember the clip state we just generated
+          portal_viewer->set_clip_state(_clip_state);
+
+          if (old_clip_state) {
+            portal_cat.spam() << "parent clip state " << *old_clip_state << endl;
+          } else {
+            portal_cat.spam() << "parent clip state None" << endl;
+          }
+          portal_cat.spam() << "own clip state " << *_clip_state << endl;
+          portal_cat.spam() << "next state " << *next_state << endl;
+
+          // undo parent clip state and compose our new clip state ito the new state
+          if (old_clip_state != NULL) {
+              next_state = old_clip_state->invert_compose(next_state);
+              portal_cat.spam() << "next state after removing parent state " << *next_state << endl;
+          }
+          next_state = next_state->compose(_clip_state);
+          portal_cat.spam() << "next state after composition " << *next_state << endl;
+        }
+
+        CullTraverserData next_data(_cell_out, 
+                                    cell_transform,
+                                    next_state, new_bh,
+                                    current_thread);
+
+        portal_viewer->set_reduced_frustum(new_bh);
+        portal_cat.spam() << "cull_callback: before traversing " << _cell_out.get_name() << endl;
+        trav->traverse_below(next_data);
+        portal_cat.spam() << "cull_callback: after traversing " << _cell_out.get_name() << endl;
+
+        // restore clip state
+        portal_viewer->set_clip_state(old_clip_state);
       }
-
-      CullTraverserData next_data(_cell_out, 
-                                  cell_transform,
-                                  next_state, new_bh,
-                                  current_thread);
-      //                                  data._state, new_bh, NULL);
-
-      // Make this cell show with the reduced frustum
-      //      _cell_out.show();
-      // all nodes visible through this portal, should have this node's frustum
-      PT(BoundingHexahedron) old_bh = portal_viewer->get_reduced_frustum();
-      portal_viewer->set_reduced_frustum(new_bh);
-      portal_cat.spam() << "cull_callback: before traversing " << _cell_out.get_name() << endl;
-      trav->traverse_below(next_data);
-      portal_cat.spam() << "cull_callback: after traversing " << _cell_out.get_name() << endl;
-      // make sure traverser is not drawing this node again
-      //    _cell_out.hide();
-
-      // reset portal viewer frustum for the siblings;
-      portal_viewer->set_reduced_frustum(old_bh);
     }
+    // reset portal viewer frustum for the siblings;
+    portal_viewer->set_reduced_frustum(old_bh);
+    // reset portal viewer viewport for the siblings;
+    portal_viewer->set_reduced_viewport(old_reduced_viewport_min, old_reduced_viewport_max);
   }
   // Now carry on to render our child nodes.
   return true;

+ 2 - 12
panda/src/pgraph/portalNode.h

@@ -23,12 +23,6 @@
 #include "nodePath.h"
 #include "pvector.h"
 
-/*
-//#ifndef CPPPARSER
-//#include "../collide/collisionSolid.h"
-//#include "../collide/collisionNode.h"
-//#endif
-*/
 ////////////////////////////////////////////////////////////////////
 //       Class : PortalNode 
 //       Description : A node in the scene graph that can hold a 
@@ -121,15 +115,11 @@ private:
   NodePath _cell_out;  // This is the cell it leads out to
 
   // enable plane clipping on this portal
-  /*
-#ifndef CPPPARSER
-  PT(CollisionNode) _left_coll_node; // for debugging visualization
-  PT(CollisionNode) _right_coll_node;// for debugging visualization
-#endif  
-  */
   bool _clip_plane;
   PT(PlaneNode) _left_plane_node;
   PT(PlaneNode) _right_plane_node;
+  PT(PlaneNode) _top_plane_node;
+  PT(PlaneNode) _bottom_plane_node;
   CPT(RenderState) _clip_state;
 
   bool _visible;