Forráskód Böngészése

first pass prototype of portal culling system

Asad M. Zaman 21 éve
szülő
commit
f1de5451a3

+ 3 - 0
panda/src/pgraph/Sources.pp

@@ -69,6 +69,7 @@
     pandaNode.I pandaNode.h \
     planeNode.I planeNode.h \
     pointLight.I pointLight.h \
+    portalClipper.I portalClipper.h \
     renderAttrib.I renderAttrib.h \
     renderEffect.I renderEffect.h \
     renderEffects.I renderEffects.h \
@@ -153,6 +154,7 @@
     pandaNode.cxx \
     planeNode.cxx \
     pointLight.cxx \
+    portalClipper.cxx \
     renderAttrib.cxx \
     renderEffect.cxx \
     renderEffects.cxx \
@@ -233,6 +235,7 @@
     pandaNode.I pandaNode.h \
     planeNode.I planeNode.h \
     pointLight.I pointLight.h \
+    portalClipper.I portalClipper.h \
     renderAttrib.I renderAttrib.h \
     renderEffect.I renderEffect.h \
     renderEffects.I renderEffects.h \

+ 27 - 0
panda/src/pgraph/camera.I

@@ -115,3 +115,30 @@ INLINE DrawMask Camera::
 get_camera_mask() const {
   return _camera_mask;
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: Camera::set_cull_center
+//       Access: Public
+//  Description: Specifies the point from which the culling operations
+//               are performed.  Normally, this is the same as the
+//               camera, and that is the default if this is not
+//               specified; but it may sometimes be useful to perform
+//               the culling from some other viewpoint, particularly
+//               when you are debugging the culling itself.
+////////////////////////////////////////////////////////////////////
+INLINE void Camera::
+set_cull_center(const NodePath &cull_center) {
+  _cull_center = cull_center;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Camera::get_cull_center
+//       Access: Public
+//  Description: Returns the point from which the culling operations
+//               will be performed, if it was set by
+//               set_cull_center(), or the empty NodePath otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE const NodePath &Camera::
+get_cull_center() const {
+  return _cull_center;
+}

+ 4 - 0
panda/src/pgraph/camera.h

@@ -59,12 +59,16 @@ PUBLISHED:
   INLINE void set_camera_mask(DrawMask mask);
   INLINE DrawMask get_camera_mask() const;
 
+  INLINE void set_cull_center(const NodePath &cull_center);
+  INLINE const NodePath &get_cull_center() const;
+
 private:
   void add_display_region(DisplayRegion *display_region);
   void remove_display_region(DisplayRegion *display_region);
 
   bool _active;
   NodePath _scene;
+  NodePath _cull_center;
 
   DrawMask _camera_mask;
 

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

@@ -62,6 +62,7 @@
 #include "pandaNode.h"
 #include "planeNode.h"
 #include "pointLight.h"
+#include "portalClipper.h"
 #include "renderAttrib.h"
 #include "renderEffect.h"
 #include "renderEffects.h"
@@ -95,6 +96,10 @@ ConfigureFn(config_pgraph) {
 // helps make culling errors obvious.
 const bool fake_view_frustum_cull = config_pgraph.GetBool("fake-view-frustum-cull", false);
 
+// Set this true to enable portal clipping. This will enable the renderer to cull
+// more objects that are clipped if not in the current list ot portals
+const bool allow_portal_cull = config_pgraph.GetBool("allow-portal-cull", false);
+
 // Set this true to make ambiguous path warning messages generate an
 // assertion failure instead of just a warning (which can then be
 // trapped with assert-abort).
@@ -203,6 +208,7 @@ init_libpgraph() {
   PandaNode::init_type();
   PlaneNode::init_type();
   PointLight::init_type();
+  PortalClipper::init_type();
   RenderAttrib::init_type();
   RenderEffect::init_type();
   RenderEffects::init_type();
@@ -256,6 +262,7 @@ init_libpgraph() {
   PandaNode::register_with_read_factory();
   PlaneNode::register_with_read_factory();
   PointLight::register_with_read_factory();
+  //PortalClipper::register_with_read_factory();
   RenderEffects::register_with_read_factory();
   RenderModeAttrib::register_with_read_factory();
   RenderState::register_with_read_factory();

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

@@ -30,6 +30,7 @@ NotifyCategoryDecl(pgraph, EXPCL_PANDA, EXPTP_PANDA);
 NotifyCategoryDecl(loader, EXPCL_PANDA, EXPTP_PANDA);
 
 extern const bool fake_view_frustum_cull;
+extern const bool allow_portal_cull;
 extern const bool unambiguous_graph;
 extern const bool allow_unrelated_wrt;
 extern const bool paranoid_compose;

+ 83 - 9
panda/src/pgraph/cullTraverser.cxx

@@ -26,11 +26,9 @@
 #include "geomNode.h"
 #include "config_pgraph.h"
 #include "boundingSphere.h"
+#include "boundingHexahedron.h"
 #include "geomSphere.h"
-#include "colorAttrib.h"
-#include "renderModeAttrib.h"
-#include "cullFaceAttrib.h"
-#include "depthOffsetAttrib.h"
+#include "portalClipper.h"
 
 #ifndef CPPPARSER
 PStatCollector CullTraverser::_nodes_pcollector("Nodes");
@@ -79,10 +77,65 @@ traverse(const NodePath &root) {
   nassertv(_cull_handler != (CullHandler *)NULL);
   nassertv(_scene_setup != (SceneSetup *)NULL);
 
-  CullTraverserData data(root, get_render_transform(),
-                         TransformState::make_identity(),
-                         _initial_state, _view_frustum, _guard_band);
-  traverse(data);
+  if (allow_portal_cull) {
+    PT(GeometricBoundingVolume) vf = _view_frustum;
+    pgraph_cat.spam() << "_view_frustum is " << *_view_frustum << "\n";
+
+    GeometricBoundingVolume *local_frustum = NULL;
+    PT(BoundingVolume) bv = _scene_setup->get_lens()->make_bounds();
+    if (bv != (BoundingVolume *)NULL &&
+        bv->is_of_type(GeometricBoundingVolume::get_class_type())) {
+      
+      local_frustum = DCAST(GeometricBoundingVolume, bv);
+    }
+    pgraph_cat.spam() << "local_frustum is " << *local_frustum << "\n";
+      
+    PortalClipper portal_viewer(local_frustum, _scene_setup);
+    portal_viewer.draw_camera_frustum();
+
+    // for each portal draw its frustum
+    for (int portal_idx=1; portal_idx<2; ++portal_idx) {
+      PT(BoundingVolume) reduced_frustum;
+
+      portal_viewer.prepare_portal(portal_idx);
+      portal_viewer.clip_portal(portal_idx);
+      if ((reduced_frustum = portal_viewer.get_reduced_frustum(portal_idx))) {
+        pgraph_cat.debug() << "got reduced frustum " << reduced_frustum << endl;
+        vf = DCAST(GeometricBoundingVolume, reduced_frustum);
+        CPT(TransformState) cull_center_transform = 
+          _scene_setup->get_cull_center().get_transform(_scene_setup->get_scene_root());
+        vf->xform(cull_center_transform->get_mat());
+      }
+    }
+    pgraph_cat.spam() << "vf is " << *vf << "\n";
+
+    CullTraverserData data(root, get_render_transform(),
+                           TransformState::make_identity(),
+                           _initial_state, _view_frustum, 
+                           vf, _guard_band);
+
+    traverse(data);
+    
+    // finally add the lines to be drawn
+    portal_viewer.draw_lines();
+      
+    // Render the frustum relative to the cull center.
+    NodePath cull_center = _scene_setup->get_cull_center();
+    CPT(TransformState) transform = cull_center.get_transform(root);
+    
+    CullTraverserData my_data(data, portal_viewer._previous);
+    my_data._render_transform = my_data._render_transform->compose(transform);
+    traverse(my_data);
+    pgraph_cat.debug() << "finished portal culling\n";
+  }
+  else {
+    CullTraverserData data(root, get_render_transform(),
+                           TransformState::make_identity(),
+                           _initial_state, _view_frustum, 
+                           NULL, _guard_band);
+    
+    traverse(data);
+  }
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -98,9 +151,29 @@ traverse(CullTraverserData &data) {
   // contain decals or require a special cull callback.  As an
   // optimization, we should tag nodes with these properties as
   // being "fancy", and skip this processing for non-fancy nodes.
+  
   if (data.is_in_view(_camera_mask)) {
     PandaNode *node = data.node();
-
+    pgraph_cat.spam() << "\n" << data._node_path << "\n";
+    
+    // let me see the names, curious
+    unsigned int loc = node->get_name().find("pTypeArchway");
+    if (loc != string::npos) {
+      node->output(pgraph_cat.debug());
+      pgraph_cat.spam() << endl;
+      if (data._reduced_frustum) {
+        pgraph_cat.debug() << "setting reduced frustum to this node\n";
+
+        if (data._view_frustum) {
+          pgraph_cat.spam() << *data._view_frustum << endl;
+        }
+        pgraph_cat.spam() << *data._reduced_frustum << endl;
+
+        data._view_frustum = data._reduced_frustum;
+        data._reduced_frustum = NULL;
+      }
+    }
+  
     const RenderEffects *node_effects = node->get_effects();
     if (node_effects->has_show_bounds()) {
       // If we should show the bounding volume for this node, make it
@@ -141,6 +214,7 @@ void CullTraverser::
 traverse_below(CullTraverserData &data) {
   _nodes_pcollector.add_level(1);
   PandaNode *node = data.node();
+
   const RenderEffects *node_effects = node->get_effects();
   bool has_decal = node_effects->has_decal();
   if (has_decal && !_depth_offset_decals) {

+ 2 - 1
panda/src/pgraph/cullTraverser.h

@@ -33,8 +33,9 @@
 
 class PandaNode;
 class CullHandler;
-class CullTraverserData;
 class CullableObject;
+class CullTraverserData;
+class PortalClipper;
 class NodePath;
 
 ////////////////////////////////////////////////////////////////////

+ 5 - 0
panda/src/pgraph/cullTraverserData.I

@@ -28,12 +28,14 @@ CullTraverserData(const NodePath &start,
                   const TransformState *net_transform,
                   const RenderState *state,
                   GeometricBoundingVolume *view_frustum,
+                  GeometricBoundingVolume *reduced_frustum,
                   GeometricBoundingVolume *guard_band) :
   _node_path(start),
   _render_transform(render_transform),
   _net_transform(net_transform),
   _state(state),
   _view_frustum(view_frustum),
+  _reduced_frustum(reduced_frustum),
   _guard_band(guard_band)
 {
 }
@@ -50,6 +52,7 @@ CullTraverserData(const CullTraverserData &copy) :
   _net_transform(copy._net_transform),
   _state(copy._state),
   _view_frustum(copy._view_frustum),
+  _reduced_frustum(copy._reduced_frustum),
   _guard_band(copy._guard_band)
 {
 }
@@ -66,6 +69,7 @@ operator = (const CullTraverserData &copy) {
   _net_transform = copy._net_transform;
   _state = copy._state;
   _view_frustum = copy._view_frustum;
+  _reduced_frustum = copy._reduced_frustum;
   _guard_band = copy._guard_band;
 }
 
@@ -82,6 +86,7 @@ CullTraverserData(const CullTraverserData &parent, PandaNode *child) :
   _net_transform(parent._net_transform),
   _state(parent._state),
   _view_frustum(parent._view_frustum),
+  _reduced_frustum(parent._reduced_frustum),
   _guard_band(parent._guard_band)
 {
 }

+ 7 - 0
panda/src/pgraph/cullTraverserData.cxx

@@ -62,6 +62,7 @@ apply_transform_and_state(CullTraverser *trav,
     _render_transform = _render_transform->compose(node_transform);
 
     if ((_view_frustum != (GeometricBoundingVolume *)NULL) ||
+        (_reduced_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.
@@ -70,6 +71,7 @@ apply_transform_and_state(CullTraverser *trav,
         // trying, we'll just give up on frustum culling from this
         // point down.
         _view_frustum = (GeometricBoundingVolume *)NULL;
+        _reduced_frustum = (GeometricBoundingVolume *)NULL;
         _guard_band = (GeometricBoundingVolume *)NULL;
 
       } else {
@@ -82,6 +84,11 @@ apply_transform_and_state(CullTraverser *trav,
           _view_frustum = DCAST(GeometricBoundingVolume, _view_frustum->make_copy());
           _view_frustum->xform(inv_transform->get_mat());
         }
+
+        if (_reduced_frustum != (GeometricBoundingVolume *)NULL) {
+          _reduced_frustum = DCAST(GeometricBoundingVolume, _reduced_frustum->make_copy());
+          _reduced_frustum->xform(inv_transform->get_mat());
+        }
         
         if (_guard_band != (GeometricBoundingVolume *)NULL) {
           _guard_band = DCAST(GeometricBoundingVolume, _guard_band->make_copy());

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

@@ -52,6 +52,7 @@ public:
                            const TransformState *net_transform,
                            const RenderState *state,
                            GeometricBoundingVolume *view_frustum,
+                           GeometricBoundingVolume *reduced_frustum,
                            GeometricBoundingVolume *guard_band);
   INLINE CullTraverserData(const CullTraverserData &copy);
   INLINE void operator = (const CullTraverserData &copy); 
@@ -73,6 +74,7 @@ public:
   CPT(TransformState) _net_transform;
   CPT(RenderState) _state;
   PT(GeometricBoundingVolume) _view_frustum;
+  PT(GeometricBoundingVolume) _reduced_frustum;
   PT(GeometricBoundingVolume) _guard_band;
 
 private:

+ 1 - 0
panda/src/pgraph/pgraph_composite2.cxx

@@ -18,6 +18,7 @@
 #include "pandaNode.cxx"
 #include "planeNode.cxx"
 #include "pointLight.cxx"
+#include "portalClipper.cxx"
 #include "renderAttrib.cxx"
 #include "renderEffect.cxx"
 #include "renderEffects.cxx"

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

@@ -0,0 +1,119 @@
+// Filename: portalClipper.I
+// Created by:  masad (4May04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: PortalClipper::Point::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE PortalClipper::Point::
+Point() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PortalClipper::Point::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE PortalClipper::Point::
+Point(const LVecBase3f &point, const Colorf &color) :
+  _point(point[0], point[1], point[2]),
+  _color(color)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PortalClipper::Point::Copy Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE PortalClipper::Point::
+Point(const PortalClipper::Point &copy) :
+  _point(copy._point),
+  _color(copy._color)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PortalClipper::Point::Copy Assignment Operator
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE void PortalClipper::Point::
+operator = (const PortalClipper::Point &copy) {
+  _point = copy._point;
+  _color = copy._color;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PortalClipper::move_to
+//       Access: Public
+//  Description: Moves the pen to the given point without drawing a
+//               line.  When followed by draw_to(), this marks the
+//               first point of a line segment; when followed by
+//               move_to() or create(), this creates a single point.
+////////////////////////////////////////////////////////////////////
+INLINE void PortalClipper::
+move_to(float x, float y, float z) {
+  move_to(Vertexf(x, y, z));
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PortalClipper::draw_to
+//       Access: Public
+//  Description: Draws a line segment from the pen's last position
+//               (the last call to move_to or draw_to) to the
+//               indicated point.  move_to() and draw_to() only update
+//               tables; the actual drawing is performed when create()
+//               is called.
+////////////////////////////////////////////////////////////////////
+INLINE void PortalClipper::
+draw_to(float x, float y, float z) {
+  draw_to(Vertexf(x, y, z));
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PortalClipper::draw_camera_frustum
+//       Access: Public
+//  Description: Draw the current camera frustum in white color
+//           
+////////////////////////////////////////////////////////////////////
+void PortalClipper::
+draw_camera_frustum()
+{
+  _color = Colorf(1,1,1,1);
+  draw_hexahedron(_hex_frustum);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PortalClipper::facing_camera
+//       Access: Public
+//  Description: checks if the _coords that forms  the plane is
+//               facing the camera
+////////////////////////////////////////////////////////////////////
+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));
+  
+  float direction = portal_plane.get_normal().dot(camera_plane.get_normal());
+  pgraph_cat.debug() << "Found direction of " << direction << endl;
+  return (direction > 0);
+}

+ 445 - 0
panda/src/pgraph/portalClipper.cxx

@@ -0,0 +1,445 @@
+// Filename: portalClipper.cxx
+// Created by:  masad (4May04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "portalClipper.h"
+#include "cullTraverser.h"
+#include "cullTraverserData.h"
+#include "transformState.h"
+#include "renderState.h"
+#include "fogAttrib.h"
+#include "cullHandler.h"
+#include "dcast.h"
+#include "geomNode.h"
+#include "config_pgraph.h"
+#include "boundingSphere.h"
+#include "geomSphere.h"
+#include "colorAttrib.h"
+#include "renderModeAttrib.h"
+#include "cullFaceAttrib.h"
+#include "depthOffsetAttrib.h"
+
+TypeHandle PortalClipper::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: PortalClipper::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+PortalClipper::
+PortalClipper(GeometricBoundingVolume *frustum, SceneSetup *scene_setup) {
+  _previous = new GeomNode("my_frustum");
+  _geom_line = new GeomLine;
+  _geom_point = new GeomPoint;
+  _geom_linestrip = new GeomLinestrip;
+
+  _hex_frustum = DCAST(BoundingHexahedron, frustum);
+
+  _scene_setup = scene_setup;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PortalClipper::Destructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+PortalClipper::
+~PortalClipper() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PortalClipper::move_to
+//       Access: Public
+//  Description: Moves the pen to the given point without drawing a
+//               line.  When followed by draw_to(), this marks the
+//               first point of a line segment; when followed by
+//               move_to() or create(), this creates a single point.
+////////////////////////////////////////////////////////////////////
+void PortalClipper::
+move_to(const LVecBase3f &v) {
+  // We create a new SegmentList with the initial point in it.
+  SegmentList segs;
+  segs.push_back(Point(v, _color));
+
+  // And add this list to the list of segments.
+  _list.push_back(segs);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PortalClipper::draw_to
+//       Access: Public
+//  Description: Draws a line segment from the pen's last position
+//               (the last call to move_to or draw_to) to the
+//               indicated point.  move_to() and draw_to() only update
+//               tables; the actual drawing is performed when create()
+//               is called.
+////////////////////////////////////////////////////////////////////
+void PortalClipper::
+draw_to(const LVecBase3f &v) {
+  if (_list.empty()) {
+    // Let our first call to draw_to() be an implicit move_to().
+    move_to(v);
+
+  } else {
+    // Get the current SegmentList, which was the last one we added to
+    // the LineList.
+    SegmentList &segs = _list.back();
+
+    // Add the new point.
+    segs.push_back(Point(v, _color));
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PortalClipper::draw a portal frustum
+//       Access: Public
+//  Description: Given the BoundingHexahedron draw it using lines
+//           
+////////////////////////////////////////////////////////////////////
+void PortalClipper::
+draw_hexahedron(BoundingHexahedron *frustum)
+{
+  /*
+  pgraph_cat.debug() << "frustum points " << frustum->get_num_points() << endl;
+
+  pgraph_cat.debug() << frustum->get_point(0) << endl;
+  pgraph_cat.debug() << frustum->get_point(1) << endl;
+  pgraph_cat.debug() << frustum->get_point(2) << endl;
+  pgraph_cat.debug() << frustum->get_point(3) << endl;
+  pgraph_cat.debug() << frustum->get_point(4) << endl;
+  pgraph_cat.debug() << frustum->get_point(5) << endl;
+  pgraph_cat.debug() << frustum->get_point(6) << endl;
+  pgraph_cat.debug() << frustum->get_point(7) << endl;
+  */
+
+  // walk the view frustum as it should be drawn
+  move_to(frustum->get_point(0));
+  draw_to(frustum->get_point(1));
+  draw_to(frustum->get_point(2));
+  draw_to(frustum->get_point(3));
+
+  move_to(frustum->get_point(4));
+  draw_to(frustum->get_point(0));
+  draw_to(frustum->get_point(3));
+  draw_to(frustum->get_point(7));
+
+  move_to(frustum->get_point(5));
+  draw_to(frustum->get_point(4));
+  draw_to(frustum->get_point(7));
+  draw_to(frustum->get_point(6));
+
+  move_to(frustum->get_point(1));
+  draw_to(frustum->get_point(5));
+  draw_to(frustum->get_point(6));
+  draw_to(frustum->get_point(2));
+
+}
+////////////////////////////////////////////////////////////////////
+//     Function: PortalClipper::draw the lines
+//       Access: Public
+//  Description: Draw all the lines in the buffer
+//           
+////////////////////////////////////////////////////////////////////
+void PortalClipper::
+draw_lines()
+{
+  if (!_list.empty()) {
+    _created_verts.clear();
+    _created_colors.clear();
+    
+    // One array each for the indices into these arrays for points
+    // and lines, and one for our line-segment lengths array.
+    PTA_ushort point_index;
+    PTA_ushort line_index;
+    PTA_int lengths;
+    
+    // Now fill up the arrays.
+    int v = 0;
+    LineList::const_iterator ll;
+    SegmentList::const_iterator sl;
+    
+    for (ll = _list.begin(); ll != _list.end(); ll++) {
+      const SegmentList &segs = (*ll);
+      
+      if (segs.size() < 2) {
+        point_index.push_back(v);
+      } else {
+        lengths.push_back(segs.size());
+      }
+      
+      for (sl = segs.begin(); sl != segs.end(); sl++) {
+        if (segs.size() >= 2) {
+          line_index.push_back(v);
+        }
+        _created_verts.push_back((*sl)._point);
+        _created_colors.push_back((*sl)._color);
+        v++;
+        //nassertr(v == (int)_created_verts.size(), previous);
+      }
+    }
+
+
+    // Now create the lines.
+    Geom *geom;
+    if (line_index.size() > 0) {
+      // Create a new Geom and add the line segments.
+      if (line_index.size() <= 2) {
+        // Here's a special case: just one line segment.
+        _geom_line->set_num_prims(1);
+        _geom_line->set_width(_thick);
+        geom = _geom_line;
+
+      } else {
+        // The more normal case: multiple line segments, connected
+        // end-to-end like a series of linestrips.
+        _geom_linestrip->set_num_prims(lengths.size());
+        _geom_linestrip->set_lengths(lengths);
+        _geom_linestrip->set_width(_thick);
+        geom = _geom_linestrip;
+      }
+
+      geom->set_colors(_created_colors, G_PER_VERTEX, line_index);
+      geom->set_coords(_created_verts, line_index);
+
+      //geom->write_verbose(cerr, 0);
+
+      _previous->add_geom(geom);
+      pgraph_cat.debug() << "added geometry" << endl;
+    }
+  }
+}
+////////////////////////////////////////////////////////////////////
+//     Function: PortalClipper::prepare the portal
+//       Access: Public
+//  Description: Given the portal draw the frustum with line segs
+//               for now. More functionalities coming up
+////////////////////////////////////////////////////////////////////
+void PortalClipper::
+prepare_portal(int idx)
+{
+  SegmentList segs;
+  char portal_name[128];
+
+  // print some messages to see if i am getting to this part
+  pgraph_cat.debug() << "creating portal clipper " << idx << endl;
+
+  // walk the portal
+  sprintf(portal_name, "**/portal%d", idx);
+  NodePath portal_nodepath = _scene_setup->get_scene_root().find(portal_name);
+  if (!portal_nodepath.is_empty()) {
+    pgraph_cat.debug() << "portal nodepath " << portal_nodepath << 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;
+    */
+    
+    // Get the camera transformation matrix
+    CPT(TransformState) ctransform = portal_nodepath.get_transform(_scene_setup->get_cull_center());
+    //CPT(TransformState) ctransform = portal_nodepath.get_transform(_scene_setup->get_camera_path());
+    LMatrix4f cmat = ctransform->get_mat();
+    pgraph_cat.debug() << cmat << endl;
+    
+    // Get the geometry from the portal    
+    PandaNode *portal_node = portal_nodepath.node();
+    GeomNode *portal_geom = DCAST(GeomNode, portal_node);
+    
+    //portal_geom->write_verbose(pgraph_cat.debug(false), 0);
+    
+    int num_geoms = portal_geom->get_num_geoms();
+    pgraph_cat.debug() << "num geometry in portal " << num_geoms << endl;
+    
+    PTA_ushort index;
+    PT(Geom) geom = portal_geom->get_geom(0);
+    _num_vert = geom->get_num_vertices();
+    PTA_Vertexf coords;
+    geom->get_coords(coords, index);
+
+    /*    
+    pgraph_cat.debug() << "before transformation to camera space" << 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;
+    */
+    
+    _coords[0] = coords[0]*cmat;
+    _coords[1] = coords[1]*cmat;
+    _coords[2] = coords[3]*cmat;  // flip with 3rd vertex
+    _coords[3] = coords[2]*cmat;  // flip with 2nd vertex
+    
+    /*
+    pgraph_cat.debug() << "after transformation to camera space" << 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;
+    */
+    
+    //geom->write_verbose(pgraph_cat.debug(false), 0);
+
+    // check if facing camera
+    if (is_facing_camera()) {
+    
+      // ok, now lets add the near plane to this 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]);
+    
+      pgraph_cat.debug() << "assembled portal" << idx << " frustum points" << endl;
+    }
+    else {
+      _num_vert = 0;
+    }
+  }
+  else {
+    _num_vert = 0;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     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(int idx)
+{
+  int num_planes = _hex_frustum->get_num_planes();
+
+  /*
+  pgraph_cat.debug() << "Number of planes " << num_planes << endl;
+
+  // 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);
+    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;
+      }
+    }
+  }
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: PortalClipper::get_reduced_frustum
+//       Access: Public
+//  Description: After clipping the portal, form the new sides and 
+//               fill in the new frustum. Return true if success
+////////////////////////////////////////////////////////////////////
+PT(BoundingVolume) PortalClipper::
+get_reduced_frustum(int idx)
+{
+  int num_planes = 6;
+  LPoint3f intersect_points[4];
+  
+#if 0
+  // calculate the new side planes
+  for (int i=0; i<_num_vert; ++i) {
+    // get the vectors, Vi+1 and Vi
+    LVector3f front(_coords[(i+1)%_num_vert]);
+    LVector3f back(_coords[i]);
+    // get the cross product of these two vectors
+    LVector3f normal = front.cross(back);
+    normal.normalize();
+    frustum_planes[i+1] = Planef(normal, LPoint3f(0,0,0));
+    frustum_planes[i+1].output(pgraph_cat.debug());
+    pgraph_cat.debug() << endl;
+  }
+#else
+  // 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 false;
+  
+  float t;
+  // find intersection of 7->0 with far
+  LPoint3f from_origin = _hex_frustum->get_point(7);
+  LVector3f from_direction = _coords[0] - from_origin;
+  bool is_intersect = _hex_frustum->get_plane(0).intersects_line(t, from_origin, from_direction);
+  if (is_intersect) {
+    pgraph_cat.debug() << "far plane intersected 7->0 at t=" << t << endl;
+    intersect_points[0] = from_origin + t*from_direction;
+    pgraph_cat.debug() << intersect_points[0] << endl;
+  }
+  
+  // find intersection of 4->1 with far
+  from_origin = _hex_frustum->get_point(4);
+  from_direction = _coords[1] - from_origin;
+  is_intersect = _hex_frustum->get_plane(0).intersects_line(t, from_origin, from_direction);
+  if (is_intersect) {
+    pgraph_cat.debug() << "far plane intersected 4->1 at t=" << t << endl;
+    intersect_points[1] = from_origin + t*from_direction;
+    pgraph_cat.debug() << intersect_points[1] << endl;
+  }
+  // find intersection of 5->2 with far
+  from_origin = _hex_frustum->get_point(5);
+  from_direction = _coords[2] - from_origin;
+  is_intersect = _hex_frustum->get_plane(0).intersects_line(t, from_origin, from_direction);
+  if (is_intersect) {
+    pgraph_cat.debug() << "far plane intersected 5->2 at t=" << t << endl;
+    intersect_points[2] = from_origin + t*from_direction;
+    pgraph_cat.debug() << intersect_points[2] << endl;
+  }
+  // find intersection of 6->3 with far
+  from_origin = _hex_frustum->get_point(6);
+  from_direction = _coords[3] - from_origin;
+  is_intersect = _hex_frustum->get_plane(0).intersects_line(t, from_origin, from_direction);
+  if (is_intersect) {
+    pgraph_cat.debug() << "far plane intersected 6->3 at t=" << t << endl;
+    intersect_points[3] = from_origin + t*from_direction;
+    pgraph_cat.debug() << intersect_points[3] << endl;
+  }
+  
+  // 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],
+                       _hex_frustum->get_point(4), _hex_frustum->get_point(5),
+                       _hex_frustum->get_point(6), _hex_frustum->get_point(7));
+
+  pgraph_cat.debug() << *reduced_frustum << endl;
+
+  // draw this hexahedron
+  _color = Colorf(0,0,1,1);
+  draw_hexahedron(DCAST(BoundingHexahedron, reduced_frustum));
+
+#endif
+  return reduced_frustum;
+}

+ 136 - 0
panda/src/pgraph/portalClipper.h

@@ -0,0 +1,136 @@
+// Filename: portalClipper.h
+// Created by:  masad (4May04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef PORTALCLIPPER_H
+#define PORTALCLIPPER_H
+
+#include "pandabase.h"
+
+#include "geom.h"
+#include "sceneSetup.h"
+#include "renderState.h"
+#include "transformState.h"
+#include "geometricBoundingVolume.h"
+#include "boundingHexahedron.h"
+#include "pointerTo.h"
+#include "drawMask.h"
+#include "typedObject.h"
+#include "pStatCollector.h"
+
+#include "geom.h"
+#include "geomPoint.h"
+#include "geomLine.h"
+#include "geomLinestrip.h"
+#include "geomNode.h"
+
+class PandaNode;
+class CullHandler;
+class CullTraverserData;
+class CullableObject;
+class NodePath;
+
+////////////////////////////////////////////////////////////////////
+//       Class : PortalClipper
+// Description : This object performs a depth-first traversal of the
+//               scene graph, with optional view-frustum culling,
+//               collecting CullState and searching for GeomNodes.
+//               Each renderable Geom encountered is passed along with
+//               its associated RenderState to the CullHandler object.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA PortalClipper : public TypedObject {
+public:
+  PortalClipper(GeometricBoundingVolume *frustum, SceneSetup *scene_setup);
+  ~PortalClipper();
+
+  INLINE bool is_facing_camera();
+  void prepare_portal(int idx);
+
+  void clip_portal(int idx);
+
+  PT(BoundingVolume) get_reduced_frustum(int idx);
+
+  void draw_lines();
+  INLINE void draw_camera_frustum();
+  void draw_hexahedron(BoundingHexahedron *frustum);
+
+  INLINE void move_to(float x, float y, float z);
+  void move_to(const LVecBase3f &v);
+
+  INLINE void draw_to(float x, float y, float z);
+  void draw_to(const LVecBase3f &v);
+
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    TypedObject::init_type();
+    register_type(_type_handle, "PortalClipper",
+                  TypedObject::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+
+private:
+  class Point {
+  public:
+    INLINE Point();
+    INLINE Point(const LVecBase3f &point, const Colorf &color);
+    INLINE Point(const Point &copy);
+    INLINE void operator = (const Point &copy);
+
+    Vertexf _point;
+    Colorf _color;
+  };
+
+  typedef pvector<Point> SegmentList;
+  typedef pvector<SegmentList> LineList;
+
+  LineList _list;
+  Colorf _color;
+  float _thick;
+
+  PTA_Vertexf _created_verts;
+  PTA_Colorf _created_colors;
+
+  PT(GeomLine) _geom_line;
+  PT(GeomPoint) _geom_point;
+  PT(GeomLinestrip) _geom_linestrip;
+
+  BoundingHexahedron *_hex_frustum;
+  SceneSetup *_scene_setup;
+
+  int _num_vert;
+  Vertexf _coords[4];
+
+public:
+  PT(GeomNode) _previous;
+};
+
+#include "portalClipper.I"
+
+#endif
+
+
+  

+ 17 - 0
panda/src/pgraph/sceneSetup.I

@@ -109,6 +109,23 @@ get_lens() const {
   return _lens;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: SceneSetup::get_cull_center
+//       Access: Public
+//  Description: Returns the point from which the culling operations
+//               will be performed.  This is normally the camera, but
+//               if camera->set_cull_center() has been specified, it
+//               will be that special node instead.
+////////////////////////////////////////////////////////////////////
+INLINE const NodePath &SceneSetup::
+get_cull_center() const {
+  if (_camera_node->get_cull_center().is_empty()) {
+    return _camera_path;
+  } else {
+    return _camera_node->get_cull_center();
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: SceneSetup::set_camera_transform
 //       Access: Public

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

@@ -50,6 +50,8 @@ public:
   INLINE void set_lens(const Lens *lens);
   INLINE const Lens *get_lens() const;
 
+  INLINE const NodePath &get_cull_center() const;
+
   INLINE void set_camera_transform(const TransformState *camera_transform);
   INLINE const TransformState *get_camera_transform() const;
 

+ 8 - 1
panda/src/pgraph/workingNodePath.cxx

@@ -111,6 +111,13 @@ r_get_node_path() const {
   nassertr(comp != (NodePathComponent *)NULL, NULL);
 
   PT(NodePathComponent) result = PandaNode::get_component(comp, _node);
-  nassertr(result != (NodePathComponent *)NULL, NULL);
+  if (result == (NodePathComponent *)NULL) {
+    // This means we found a disconnected chain in the
+    // WorkingNodePath's ancestry: the node above this node isn't
+    // connected.  In this case, don't attempt to go higher; just
+    // truncate the NodePath at the bottom of the disconnect.
+    return PandaNode::get_top_component(_node, true);
+  }
+
   return result;
 }