2
0
Эх сурвалжийг харах

UnionBoundingVolume, IntersectionBoundingVolume, Camera::set_cull_bounds()

David Rose 14 жил өмнө
parent
commit
d34c964e53

+ 1 - 1
panda/src/display/graphicsEngine.cxx

@@ -1153,7 +1153,7 @@ do_cull(CullHandler *cull_handler, SceneSetup *scene_setup,
 
     // First, we have to get the current viewing frustum, which comes
     // from the lens.
-    PT(BoundingVolume) bv = scene_setup->get_lens()->make_bounds();
+    PT(BoundingVolume) bv = scene_setup->get_cull_bounds();
 
     if (bv != (BoundingVolume *)NULL &&
         bv->is_of_type(GeometricBoundingVolume::get_class_type())) {

+ 9 - 3
panda/src/mathutil/Sources.pp

@@ -20,6 +20,7 @@
     fftCompressor.h finiteBoundingVolume.h frustum.h  \
     frustum_src.I frustum_src.h geometricBoundingVolume.I  \
     geometricBoundingVolume.h \
+    intersectionBoundingVolume.h intersectionBoundingVolume.I \
     linmath_events.h \
     look_at.h look_at_src.I  \
     look_at_src.cxx look_at_src.h \
@@ -38,7 +39,8 @@
     rotate_to.h rotate_to_src.cxx \
     stackedPerlinNoise2.h stackedPerlinNoise2.I \
     stackedPerlinNoise3.h stackedPerlinNoise3.I \
-    triangulator.h triangulator.I
+    triangulator.h triangulator.I \
+    unionBoundingVolume.h unionBoundingVolume.I
 
   #define INCLUDED_SOURCES \
     boundingHexahedron.cxx boundingLine.cxx \
@@ -47,6 +49,7 @@
     boundingSphere.cxx  \
     boundingVolume.cxx config_mathutil.cxx fftCompressor.cxx  \
     finiteBoundingVolume.cxx geometricBoundingVolume.cxx  \
+    intersectionBoundingVolume.cxx \
     look_at.cxx \
     linmath_events.cxx \
     mersenne.cxx \
@@ -62,7 +65,8 @@
     rotate_to.cxx \
     stackedPerlinNoise2.cxx \
     stackedPerlinNoise3.cxx \
-    triangulator.cxx
+    triangulator.cxx \
+    unionBoundingVolume.cxx
 
   #define INSTALL_HEADERS \
     boundingHexahedron.I boundingHexahedron.h boundingLine.I \
@@ -75,6 +79,7 @@
     pta_LVecBase4.h pta_LVecBase2.h \
     finiteBoundingVolume.h frustum.h frustum_src.I frustum_src.h \
     geometricBoundingVolume.I geometricBoundingVolume.h look_at.h \
+    intersectionBoundingVolume.h intersectionBoundingVolume.I \
     look_at_src.I look_at_src.h \
     linmath_events.h \
     mersenne.h \
@@ -88,7 +93,8 @@
     rotate_to.h rotate_to_src.cxx \
     stackedPerlinNoise2.h stackedPerlinNoise2.I \
     stackedPerlinNoise3.h stackedPerlinNoise3.I \
-    triangulator.h triangulator.I
+    triangulator.h triangulator.I \
+    unionBoundingVolume.h unionBoundingVolume.I
 
 
 

+ 5 - 70
panda/src/mathutil/boundingBox.cxx

@@ -203,16 +203,6 @@ extend_by_point(const LPoint3 &point) {
   return true;
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: BoundingBox::extend_by_sphere
-//       Access: Protected, Virtual
-//  Description: 
-////////////////////////////////////////////////////////////////////
-bool BoundingBox::
-extend_by_sphere(const BoundingSphere *sphere) {
-  return extend_by_finite(sphere);
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: BoundingBox::extend_by_box
 //       Access: Protected, Virtual
@@ -239,24 +229,14 @@ extend_by_box(const BoundingBox *box) {
   return true;
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: BoundingBox::extend_by_hexahedron
-//       Access: Protected, Virtual
-//  Description: 
-////////////////////////////////////////////////////////////////////
-bool BoundingBox::
-extend_by_hexahedron(const BoundingHexahedron *hexahedron) {
-  return extend_by_finite(hexahedron);
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: BoundingBox::extend_by_finite
-//       Access: Protected
+//       Access: Protected, Virtual
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 bool BoundingBox::
 extend_by_finite(const FiniteBoundingVolume *volume) {
-  nassertr(!volume->is_empty(), false);
+  nassertr(!volume->is_empty() && !volume->is_infinite(), false);
 
   LVector3 min1 = volume->get_min();
   LVector3 max1 = volume->get_max();
@@ -347,47 +327,14 @@ around_points(const LPoint3 *first, const LPoint3 *last) {
   return true;
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: BoundingBox::around_spheres
-//       Access: Protected, Virtual
-//  Description: 
-////////////////////////////////////////////////////////////////////
-bool BoundingBox::
-around_spheres(const BoundingVolume **first,
-               const BoundingVolume **last) {
-  return around_finite(first, last);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: BoundingBox::around_boxes
-//       Access: Protected, Virtual
-//  Description: 
-////////////////////////////////////////////////////////////////////
-bool BoundingBox::
-around_boxes(const BoundingVolume **first,
-             const BoundingVolume **last) {
-  return around_finite(first, last);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: BoundingBox::around_hexahedrons
-//       Access: Protected, Virtual
-//  Description: 
-////////////////////////////////////////////////////////////////////
-bool BoundingBox::
-around_hexahedrons(const BoundingVolume **first,
-                   const BoundingVolume **last) {
-  return around_finite(first, last);
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: BoundingBox::around_finite
-//       Access: Protected
+//       Access: Protected, Virtual
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 bool BoundingBox::
 around_finite(const BoundingVolume **first,
-              const BoundingVolume **last) {
+                 const BoundingVolume **last) {
   nassertr(first != last, false);
 
   // We're given a set of bounding volumes, at least the first one of
@@ -538,18 +485,6 @@ contains_lineseg(const LPoint3 &a, const LPoint3 &b) const {
   }
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: BoundingBox::contains_sphere
-//       Access: Protected, Virtual
-//  Description: Double-dispatch support: called by contains_other()
-//               when the type we're testing for intersection is known
-//               to be a sphere.
-////////////////////////////////////////////////////////////////////
-int BoundingBox::
-contains_sphere(const BoundingSphere *sphere) const {
-  return contains_finite(sphere);
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: BoundingBox::contains_box
 //       Access: Protected, Virtual
@@ -630,7 +565,7 @@ contains_plane(const BoundingPlane *plane) const {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: BoundingBox::contains_finite
-//       Access: Protected
+//       Access: Protected, Virtual
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 int BoundingBox::

+ 4 - 13
panda/src/mathutil/boundingBox.h

@@ -71,30 +71,21 @@ protected:
 
 
   virtual bool extend_by_point(const LPoint3 &point);
-  virtual bool extend_by_sphere(const BoundingSphere *sphere);
   virtual bool extend_by_box(const BoundingBox *box);
-  virtual bool extend_by_hexahedron(const BoundingHexahedron *hexahedron);
-  bool extend_by_finite(const FiniteBoundingVolume *volume);
+  virtual bool extend_by_finite(const FiniteBoundingVolume *volume);
 
   virtual bool around_points(const LPoint3 *first,
                              const LPoint3 *last);
-  virtual bool around_spheres(const BoundingVolume **first,
-                              const BoundingVolume **last);
-  virtual bool around_boxes(const BoundingVolume **first,
-                            const BoundingVolume **last);
-  virtual bool around_hexahedrons(const BoundingVolume **first,
-                                  const BoundingVolume **last);
-  bool around_finite(const BoundingVolume **first,
-                     const BoundingVolume **last);
+  virtual bool around_finite(const BoundingVolume **first,
+                             const BoundingVolume **last);
 
   virtual int contains_point(const LPoint3 &point) const;
   virtual int contains_lineseg(const LPoint3 &a, const LPoint3 &b) const;
   virtual int contains_hexahedron(const BoundingHexahedron *hexahedron) const;
-  virtual int contains_sphere(const BoundingSphere *sphere) const;
   virtual int contains_box(const BoundingBox *box) const;
   virtual int contains_line(const BoundingLine *line) const;
   virtual int contains_plane(const BoundingPlane *plane) const;
-  int contains_finite(const FiniteBoundingVolume *volume) const;
+  virtual int contains_finite(const FiniteBoundingVolume *volume) const;
 
 private:
   LPoint3 _min;

+ 6 - 39
panda/src/mathutil/boundingSphere.cxx

@@ -42,8 +42,8 @@ make_copy() const {
 ////////////////////////////////////////////////////////////////////
 LPoint3 BoundingSphere::
 get_min() const {
-  nassertr(!is_empty(), LPoint3(0.0f, 0.0f, 0.0f));
-  nassertr(!is_infinite(), LPoint3(0.0f, 0.0f, 0.0f));
+  nassertr(!is_empty(), LPoint3::zero());
+  nassertr(!is_infinite(), LPoint3::zero());
   return LPoint3(_center[0] - _radius,
                   _center[1] - _radius,
                   _center[2] - _radius);
@@ -56,8 +56,8 @@ get_min() const {
 ////////////////////////////////////////////////////////////////////
 LPoint3 BoundingSphere::
 get_max() const {
-  nassertr(!is_empty(), LPoint3(0.0f, 0.0f, 0.0f));
-  nassertr(!is_infinite(), LPoint3(0.0f, 0.0f, 0.0f));
+  nassertr(!is_empty(), LPoint3::zero());
+  nassertr(!is_infinite(), LPoint3::zero());
   return LPoint3(_center[0] + _radius,
                   _center[1] + _radius,
                   _center[2] + _radius);
@@ -86,8 +86,8 @@ get_volume() const {
 ////////////////////////////////////////////////////////////////////
 LPoint3 BoundingSphere::
 get_approx_center() const {
-  nassertr(!is_empty(), LPoint3(0.0f, 0.0f, 0.0f));
-  nassertr(!is_infinite(), LPoint3(0.0f, 0.0f, 0.0f));
+  nassertr(!is_empty(), LPoint3::zero());
+  nassertr(!is_infinite(), LPoint3::zero());
   return get_center();
 }
 
@@ -384,39 +384,6 @@ around_points(const LPoint3 *first, const LPoint3 *last) {
   return true;
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: BoundingSphere::around_spheres
-//       Access: Protected, Virtual
-//  Description: 
-////////////////////////////////////////////////////////////////////
-bool BoundingSphere::
-around_spheres(const BoundingVolume **first,
-               const BoundingVolume **last) {
-  return around_finite(first, last);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: BoundingSphere::around_boxes
-//       Access: Protected, Virtual
-//  Description: 
-////////////////////////////////////////////////////////////////////
-bool BoundingSphere::
-around_boxes(const BoundingVolume **first,
-             const BoundingVolume **last) {
-  return around_finite(first, last);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: BoundingSphere::around_hexahedrons
-//       Access: Protected, Virtual
-//  Description: 
-////////////////////////////////////////////////////////////////////
-bool BoundingSphere::
-around_hexahedrons(const BoundingVolume **first,
-                   const BoundingVolume **last) {
-  return around_finite(first, last);
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: BoundingSphere::around_finite
 //       Access: Protected

+ 3 - 9
panda/src/mathutil/boundingSphere.h

@@ -62,18 +62,12 @@ protected:
   virtual bool extend_by_sphere(const BoundingSphere *sphere);
   virtual bool extend_by_box(const BoundingBox *box);
   virtual bool extend_by_hexahedron(const BoundingHexahedron *hexahedron);
-  bool extend_by_finite(const FiniteBoundingVolume *volume);
+  virtual bool extend_by_finite(const FiniteBoundingVolume *volume);
 
   virtual bool around_points(const LPoint3 *first,
                              const LPoint3 *last);
-  virtual bool around_spheres(const BoundingVolume **first,
-                              const BoundingVolume **last);
-  virtual bool around_boxes(const BoundingVolume **first,
-                            const BoundingVolume **last);
-  virtual bool around_hexahedrons(const BoundingVolume **first,
-                                  const BoundingVolume **last);
-  bool around_finite(const BoundingVolume **first,
-                     const BoundingVolume **last);
+  virtual bool around_finite(const BoundingVolume **first,
+                             const BoundingVolume **last);
 
   virtual int contains_point(const LPoint3 &point) const;
   virtual int contains_lineseg(const LPoint3 &a, const LPoint3 &b) const;

+ 164 - 70
panda/src/mathutil/boundingVolume.cxx

@@ -14,6 +14,8 @@
 
 #include "boundingVolume.h"
 #include "finiteBoundingVolume.h"
+#include "unionBoundingVolume.h"
+#include "intersectionBoundingVolume.h"
 
 #include "indent.h"
 
@@ -192,11 +194,8 @@ string_bounds_type(const string &str) {
 //               sphere.
 ////////////////////////////////////////////////////////////////////
 bool BoundingVolume::
-extend_by_sphere(const BoundingSphere *) {
-  mathutil_cat.warning()
-    << get_type() << "::extend_by_sphere() called\n";
-  _flags = F_infinite;
-  return false;
+extend_by_sphere(const BoundingSphere *sphere) {
+  return extend_by_finite(sphere);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -207,11 +206,8 @@ extend_by_sphere(const BoundingSphere *) {
 //               box.
 ////////////////////////////////////////////////////////////////////
 bool BoundingVolume::
-extend_by_box(const BoundingBox *) {
-  mathutil_cat.warning()
-    << get_type() << "::extend_by_box() called\n";
-  _flags = F_infinite;
-  return false;
+extend_by_box(const BoundingBox *box) {
+  return extend_by_finite(box);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -222,11 +218,8 @@ extend_by_box(const BoundingBox *) {
 //               hexahedron.
 ////////////////////////////////////////////////////////////////////
 bool BoundingVolume::
-extend_by_hexahedron(const BoundingHexahedron *) {
-  mathutil_cat.warning()
-    << get_type() << "::extend_by_hexahedron() called\n";
-  _flags = F_infinite;
-  return false;
+extend_by_hexahedron(const BoundingHexahedron *hexahedron) {
+  return extend_by_finite(hexahedron);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -237,11 +230,8 @@ extend_by_hexahedron(const BoundingHexahedron *) {
 //               line.
 ////////////////////////////////////////////////////////////////////
 bool BoundingVolume::
-extend_by_line(const BoundingLine *) {
-  mathutil_cat.warning()
-    << get_type() << "::extend_by_line() called\n";
-  _flags = F_infinite;
-  return false;
+extend_by_line(const BoundingLine *line) {
+  return extend_by_geometric(line);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -252,9 +242,53 @@ extend_by_line(const BoundingLine *) {
 //               plane.
 ////////////////////////////////////////////////////////////////////
 bool BoundingVolume::
-extend_by_plane(const BoundingPlane *) {
+extend_by_plane(const BoundingPlane *plane) {
+  return extend_by_geometric(plane);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingVolume::extend_by_union
+//       Access: Protected, Virtual
+//  Description: Double-dispatch support: called by extend_other()
+//               when the type we're extending by is known to be a
+//               union.
+////////////////////////////////////////////////////////////////////
+bool BoundingVolume::
+extend_by_union(const UnionBoundingVolume *unionv) {
+  return extend_by_geometric(unionv);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingVolume::extend_by_intersection
+//       Access: Protected, Virtual
+//  Description: Double-dispatch support: called by extend_other()
+//               when the type we're extending by is known to be a
+//               intersection.
+////////////////////////////////////////////////////////////////////
+bool BoundingVolume::
+extend_by_intersection(const IntersectionBoundingVolume *intersection) {
+  return extend_by_geometric(intersection);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingVolume::extend_by_finite
+//       Access: Protected, Virtual
+//  Description: Generic handler for a FiniteBoundingVolume.
+////////////////////////////////////////////////////////////////////
+bool BoundingVolume::
+extend_by_finite(const FiniteBoundingVolume *volume) {
+  return extend_by_geometric(volume);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingVolume::extend_by_geometric
+//       Access: Protected, Virtual
+//  Description: Generic handler for a GeometricBoundingVolume.
+////////////////////////////////////////////////////////////////////
+bool BoundingVolume::
+extend_by_geometric(const GeometricBoundingVolume *volume) {
   mathutil_cat.warning()
-    << get_type() << "::extend_by_plane() called\n";
+    << get_type() << "::extend_by_geometric() called with " << volume->get_type() << "\n";
   _flags = F_infinite;
   return false;
 }
@@ -267,11 +301,8 @@ extend_by_plane(const BoundingPlane *) {
 //               known to be a nonempty sphere.
 ////////////////////////////////////////////////////////////////////
 bool BoundingVolume::
-around_spheres(const BoundingVolume **, const BoundingVolume **) {
-  mathutil_cat.warning()
-    << get_type() << "::around_spheres() called\n";
-  _flags = F_infinite;
-  return false;
+around_spheres(const BoundingVolume **first, const BoundingVolume **last) {
+  return around_finite(first, last);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -282,11 +313,8 @@ around_spheres(const BoundingVolume **, const BoundingVolume **) {
 //               known to be a nonempty box.
 ////////////////////////////////////////////////////////////////////
 bool BoundingVolume::
-around_boxes(const BoundingVolume **, const BoundingVolume **) {
-  mathutil_cat.warning()
-    << get_type() << "::around_boxes() called\n";
-  _flags = F_infinite;
-  return false;
+around_boxes(const BoundingVolume **first, const BoundingVolume **last) {
+  return around_finite(first, last);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -297,11 +325,8 @@ around_boxes(const BoundingVolume **, const BoundingVolume **) {
 //               known to be a nonempty hexahedron.
 ////////////////////////////////////////////////////////////////////
 bool BoundingVolume::
-around_hexahedrons(const BoundingVolume **, const BoundingVolume **) {
-  mathutil_cat.warning()
-    << get_type() << "::around_hexahedrons() called\n";
-  _flags = F_infinite;
-  return false;
+around_hexahedrons(const BoundingVolume **first, const BoundingVolume **last) {
+  return around_finite(first, last);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -312,15 +337,8 @@ around_hexahedrons(const BoundingVolume **, const BoundingVolume **) {
 //               known to be a nonempty line.
 ////////////////////////////////////////////////////////////////////
 bool BoundingVolume::
-around_lines(const BoundingVolume **, const BoundingVolume **) {
-  _flags = F_infinite;
-
-  mathutil_cat.warning()
-    << get_type() << "::around_lines() called\n";
-
-  // If we get here, the function isn't defined by a subclass, so we
-  // return false to indicate this.
-  return false;
+around_lines(const BoundingVolume **first, const BoundingVolume **last) {
+  return around_geometric(first, last);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -331,14 +349,54 @@ around_lines(const BoundingVolume **, const BoundingVolume **) {
 //               known to be a nonempty plane.
 ////////////////////////////////////////////////////////////////////
 bool BoundingVolume::
-around_planes(const BoundingVolume **, const BoundingVolume **) {
-  _flags = F_infinite;
+around_planes(const BoundingVolume **first, const BoundingVolume **last) {
+  return around_geometric(first, last);
+}
 
-  mathutil_cat.warning()
-    << get_type() << "::around_planes() called\n";
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingVolume::around_unions
+//       Access: Protected, Virtual
+//  Description: Double-dispatch support: called by around_other()
+//               when the type of the first element in the list is
+//               known to be a union object.
+////////////////////////////////////////////////////////////////////
+bool BoundingVolume::
+around_unions(const BoundingVolume **first, const BoundingVolume **last) {
+  return around_geometric(first, last);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingVolume::around_intersections
+//       Access: Protected, Virtual
+//  Description: Double-dispatch support: called by around_other()
+//               when the type of the first element in the list is
+//               known to be an intersection object.
+////////////////////////////////////////////////////////////////////
+bool BoundingVolume::
+around_intersections(const BoundingVolume **first, const BoundingVolume **last) {
+  return around_geometric(first, last);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingVolume::around_finite
+//       Access: Protected, Virtual
+//  Description: Generic handler for a FiniteBoundingVolume.
+////////////////////////////////////////////////////////////////////
+bool BoundingVolume::
+around_finite(const BoundingVolume **first, const BoundingVolume **last) {
+  return around_geometric(first, last);
+}
 
-  // If we get here, the function isn't defined by a subclass, so we
-  // return false to indicate this.
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingVolume::around_geometric
+//       Access: Protected, Virtual
+//  Description: Generic handler for a GeometricBoundingVolume.
+////////////////////////////////////////////////////////////////////
+bool BoundingVolume::
+around_geometric(const BoundingVolume **first, const BoundingVolume **last) {
+  mathutil_cat.warning()
+    << get_type() << "::extend_by_geometric() called with " << first[0]->get_type() << "\n";
+  _flags = F_infinite;
   return false;
 }
 
@@ -350,10 +408,8 @@ around_planes(const BoundingVolume **, const BoundingVolume **) {
 //               to be a sphere.
 ////////////////////////////////////////////////////////////////////
 int BoundingVolume::
-contains_sphere(const BoundingSphere *) const {
-  mathutil_cat.warning()
-    << get_type() << "::contains_sphere() called\n";
-  return IF_dont_understand;
+contains_sphere(const BoundingSphere *sphere) const {
+  return contains_finite(sphere);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -364,10 +420,8 @@ contains_sphere(const BoundingSphere *) const {
 //               to be a box.
 ////////////////////////////////////////////////////////////////////
 int BoundingVolume::
-contains_box(const BoundingBox *) const {
-  mathutil_cat.warning()
-    << get_type() << "::contains_box() called\n";
-  return IF_dont_understand;
+contains_box(const BoundingBox *box) const {
+  return contains_finite(box);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -378,10 +432,8 @@ contains_box(const BoundingBox *) const {
 //               to be a hexahedron.
 ////////////////////////////////////////////////////////////////////
 int BoundingVolume::
-contains_hexahedron(const BoundingHexahedron *) const {
-  mathutil_cat.warning()
-    << get_type() << "::contains_hexahedron() called\n";
-  return IF_dont_understand;
+contains_hexahedron(const BoundingHexahedron *hexahedron) const {
+  return contains_finite(hexahedron);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -392,10 +444,8 @@ contains_hexahedron(const BoundingHexahedron *) const {
 //               to be a line.
 ////////////////////////////////////////////////////////////////////
 int BoundingVolume::
-contains_line(const BoundingLine *) const {
-  mathutil_cat.warning()
-    << get_type() << "::contains_line() called\n";
-  return IF_dont_understand;
+contains_line(const BoundingLine *line) const {
+  return contains_geometric(line);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -406,9 +456,53 @@ contains_line(const BoundingLine *) const {
 //               to be a plane.
 ////////////////////////////////////////////////////////////////////
 int BoundingVolume::
-contains_plane(const BoundingPlane *) const {
+contains_plane(const BoundingPlane *plane) const {
+  return contains_geometric(plane);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingVolume::contains_union
+//       Access: Protected, Virtual
+//  Description: Double-dispatch support: called by contains_other()
+//               when the type we're testing for intersection is known
+//               to be a union object.
+////////////////////////////////////////////////////////////////////
+int BoundingVolume::
+contains_union(const UnionBoundingVolume *unionv) const {
+  return unionv->other_contains_union(this);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingVolume::contains_intersection
+//       Access: Protected, Virtual
+//  Description: Double-dispatch support: called by contains_other()
+//               when the type we're testing for intersection is known
+//               to be an intersection object.
+////////////////////////////////////////////////////////////////////
+int BoundingVolume::
+contains_intersection(const IntersectionBoundingVolume *intersection) const {
+  return intersection->other_contains_intersection(this);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingVolume::contains_finite
+//       Access: Protected, Virtual
+//  Description: Generic handler for a FiniteBoundingVolume.
+////////////////////////////////////////////////////////////////////
+int BoundingVolume::
+contains_finite(const FiniteBoundingVolume *volume) const {
+  return contains_geometric(volume);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingVolume::contains_geometric
+//       Access: Protected, Virtual
+//  Description: Generic handler for a GeometricBoundingVolume.
+////////////////////////////////////////////////////////////////////
+int BoundingVolume::
+contains_geometric(const GeometricBoundingVolume *volume) const {
   mathutil_cat.warning()
-    << get_type() << "::contains_plane() called\n";
+    << get_type() << "::contains_geometric() called with " << volume->get_type() << "\n";
   return IF_dont_understand;
 }
 

+ 21 - 1
panda/src/mathutil/boundingVolume.h

@@ -28,6 +28,8 @@ class BoundingBox;
 class BoundingHexahedron;
 class BoundingLine;
 class BoundingPlane;
+class UnionBoundingVolume;
+class IntersectionBoundingVolume;
 
 
 ////////////////////////////////////////////////////////////////////
@@ -143,6 +145,10 @@ protected:
   virtual bool extend_by_hexahedron(const BoundingHexahedron *hexahedron);
   virtual bool extend_by_line(const BoundingLine *line);
   virtual bool extend_by_plane(const BoundingPlane *plane);
+  virtual bool extend_by_union(const UnionBoundingVolume *unionv);
+  virtual bool extend_by_intersection(const IntersectionBoundingVolume *intersection);
+  virtual bool extend_by_finite(const FiniteBoundingVolume *volume);
+  virtual bool extend_by_geometric(const GeometricBoundingVolume *volume);
 
   virtual bool around_spheres(const BoundingVolume **first,
                               const BoundingVolume **last);
@@ -153,13 +159,25 @@ protected:
   virtual bool around_lines(const BoundingVolume **first,
                             const BoundingVolume **last);
   virtual bool around_planes(const BoundingVolume **first,
-                            const BoundingVolume **last);
+                             const BoundingVolume **last);
+  virtual bool around_unions(const BoundingVolume **first,
+                             const BoundingVolume **last);
+  virtual bool around_intersections(const BoundingVolume **first,
+                                    const BoundingVolume **last);
+  virtual bool around_finite(const BoundingVolume **first,
+                             const BoundingVolume **last);
+  virtual bool around_geometric(const BoundingVolume **first,
+                                const BoundingVolume **last);
 
   virtual int contains_sphere(const BoundingSphere *sphere) const;
   virtual int contains_box(const BoundingBox *box) const;
   virtual int contains_hexahedron(const BoundingHexahedron *hexahedron) const;
   virtual int contains_line(const BoundingLine *line) const;
   virtual int contains_plane(const BoundingPlane *plane) const;
+  virtual int contains_union(const UnionBoundingVolume *unionv) const;
+  virtual int contains_intersection(const IntersectionBoundingVolume *intersection) const;
+  virtual int contains_finite(const FiniteBoundingVolume *volume) const;
+  virtual int contains_geometric(const GeometricBoundingVolume *volume) const;
 
 
 public:
@@ -184,6 +202,8 @@ private:
   friend class BoundingHexahedron;
   friend class BoundingLine;
   friend class BoundingPlane;
+  friend class UnionBoundingVolume;
+  friend class IntersectionBoundingVolume;
 };
 
 INLINE_MATHUTIL ostream &operator << (ostream &out, const BoundingVolume &bound);

+ 24 - 1
panda/src/mathutil/config_mathutil.cxx

@@ -22,6 +22,8 @@
 #include "boundingHexahedron.h"
 #include "boundingLine.h"
 #include "boundingPlane.h"
+#include "unionBoundingVolume.h"
+#include "intersectionBoundingVolume.h"
 #include "linmath_events.h"
 #include "dconfig.h"
 #include "pandaSystem.h"
@@ -29,6 +31,10 @@
 Configure(config_mathutil);
 NotifyCategoryDef(mathutil, "");
 
+ConfigureFn(config_mathutil) {
+  init_libmathutil();
+}
+
 ConfigVariableDouble fft_offset
 ("fft-offset", 0.001);
 
@@ -47,7 +53,22 @@ ConfigVariableEnum<BoundingVolume::BoundsType> bounds_type
           "by Panda to enclose geometry.  Use 'sphere' or 'box', or use "
           "'best' to let Panda decide which is most appropriate."));
 
-ConfigureFn(config_mathutil) {
+////////////////////////////////////////////////////////////////////
+//     Function: init_libmathutil
+//  Description: Initializes the library.  This must be called at
+//               least once before any of the functions or classes in
+//               this library can be used.  Normally it will be
+//               called by the static initializers and need not be
+//               called explicitly, but special cases exist.
+////////////////////////////////////////////////////////////////////
+void
+init_libmathutil() {
+  static bool initialized = false;
+  if (initialized) {
+    return;
+  }
+  initialized = true;
+
   BoundingHexahedron::init_type();
   BoundingSphere::init_type();
   BoundingBox::init_type();
@@ -55,6 +76,8 @@ ConfigureFn(config_mathutil) {
   FiniteBoundingVolume::init_type();
   GeometricBoundingVolume::init_type();
   OmniBoundingVolume::init_type();
+  UnionBoundingVolume::init_type();
+  IntersectionBoundingVolume::init_type();
   BoundingLine::init_type();
   BoundingPlane::init_type();
   EventStoreVec2::init_type("EventStoreVec2");

+ 2 - 0
panda/src/mathutil/config_mathutil.h

@@ -29,6 +29,8 @@ extern ConfigVariableDouble fft_exponent;
 extern ConfigVariableDouble fft_error_threshold;
 extern EXPCL_PANDA_MATHUTIL ConfigVariableEnum<BoundingVolume::BoundsType> bounds_type;
 
+extern EXPCL_PANDA_MATHUTIL void init_libmathutil();
+
 #endif
 
 

+ 46 - 0
panda/src/mathutil/intersectionBoundingVolume.I

@@ -0,0 +1,46 @@
+// Filename: intersectionBoundingVolume.I
+// Created by:  drose (08Feb12)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: IntersectionBoundingVolume::Constructor
+//       Access: Published
+//  Description: Constructs an empty intersection.
+////////////////////////////////////////////////////////////////////
+INLINE_MATHUTIL IntersectionBoundingVolume::
+IntersectionBoundingVolume() {
+  _flags = F_infinite;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: IntersectionBoundingVolume::get_num_components
+//       Access: Published
+//  Description: Returns the number of components in the intersection.
+////////////////////////////////////////////////////////////////////
+INLINE_MATHUTIL int IntersectionBoundingVolume::
+get_num_components() const {
+  return (int)_components.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: IntersectionBoundingVolume::get_component
+//       Access: Published
+//  Description: Returns the nth component in the intersection.
+////////////////////////////////////////////////////////////////////
+INLINE_MATHUTIL const GeometricBoundingVolume *IntersectionBoundingVolume::
+get_component(int n) const {
+  nassertr(n >= 0 && n < (int)_components.size(), NULL);
+  return _components[n];
+}

+ 592 - 0
panda/src/mathutil/intersectionBoundingVolume.cxx

@@ -0,0 +1,592 @@
+// Filename: intersectionBoundingVolume.cxx
+// Created by:  drose (08Feb12)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#include "intersectionBoundingVolume.h"
+#include "unionBoundingVolume.h"
+#include "config_mathutil.h"
+#include "dcast.h"
+
+TypeHandle IntersectionBoundingVolume::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: IntersectionBoundingVolume::Copy Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+IntersectionBoundingVolume::
+IntersectionBoundingVolume(const IntersectionBoundingVolume &copy) :
+  GeometricBoundingVolume(copy),
+  _components(copy._components)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: IntersectionBoundingVolume::make_copy
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+BoundingVolume *IntersectionBoundingVolume::
+make_copy() const {
+  return new IntersectionBoundingVolume(*this);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: IntersectionBoundingVolume::get_approx_center
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+LPoint3 IntersectionBoundingVolume::
+get_approx_center() const {
+  nassertr(!is_empty(), LPoint3::zero());
+  nassertr(!is_infinite(), LPoint3::zero());
+
+  LPoint3 center = LPoint3::zero();
+  for (Components::const_iterator ci = _components.begin();
+       ci != _components.end();
+       ++ci) {
+    center += (*ci)->get_approx_center();
+  }
+
+  return center / (PN_stdfloat)_components.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: IntersectionBoundingVolume::xform
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void IntersectionBoundingVolume::
+xform(const LMatrix4 &mat) {
+  nassertv(!mat.is_nan());
+
+  for (Components::iterator ci = _components.begin();
+       ci != _components.end();
+       ++ci) {
+    PT(GeometricBoundingVolume) copy = DCAST(GeometricBoundingVolume, (*ci)->make_copy());
+    copy->xform(mat);
+    (*ci) = copy;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: IntersectionBoundingVolume::output
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void IntersectionBoundingVolume::
+output(ostream &out) const {
+  if (is_empty()) {
+    out << "intersection, empty";
+  } else if (is_infinite()) {
+    out << "intersection, infinite";
+  } else {
+    out << "intersection [";
+    for (Components::const_iterator ci = _components.begin();
+         ci != _components.end();
+         ++ci) {
+      out << " " << *(*ci);
+    }
+    out << " ]";
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: IntersectionBoundingVolume::write
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void IntersectionBoundingVolume::
+write(ostream &out, int indent_level) const {
+  if (is_empty()) {
+    indent(out, indent_level) << "intersection, empty\n";
+  } else if (is_infinite()) {
+    indent(out, indent_level) << "intersection, infinite\n";
+  } else {
+    indent(out, indent_level) << "intersection {\n";
+    for (Components::const_iterator ci = _components.begin();
+         ci != _components.end();
+         ++ci) {
+      (*ci)->write(out, indent_level + 2);
+    }
+    indent(out, indent_level) << "}\n";
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: IntersectionBoundingVolume::clear_components
+//       Access: Published
+//  Description: Removes all components from the volume.
+////////////////////////////////////////////////////////////////////
+void IntersectionBoundingVolume::
+clear_components() {
+  _components.clear();
+  _flags = F_infinite;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: IntersectionBoundingVolume::add_component
+//       Access: Published
+//  Description: Adds a new component to the volume.  This does not
+//               necessarily increase the total number of components
+//               by one, and you may or may not be able to find this
+//               component in the volume by a subsequent call to
+//               get_component(); certain optimizations may prevent
+//               the component from being added, or have other
+//               unexpected effects on the total set of components.
+////////////////////////////////////////////////////////////////////
+void IntersectionBoundingVolume::
+add_component(const GeometricBoundingVolume *component) {
+  CPT(GeometricBoundingVolume) gbv;
+
+  if (component->is_exact_type(UnionBoundingVolume::get_class_type())) {
+    // Here's a special case.  We'll construct a new union that
+    // includes only those components that have some intersection with
+    // our existing components.  (No need to include the components
+    // that have no intersection.)
+    PT(UnionBoundingVolume) unionv = DCAST(UnionBoundingVolume, component->make_copy());
+    unionv->filter_intersection(this);
+
+    // Save the modified union in a PT() so it won't be destructed.
+    gbv = unionv.p();
+
+    if (unionv->get_num_components() == 1) {
+      // If there's only one component left, use just that one.
+      gbv = unionv->get_component(0);
+    }
+
+    component = gbv;
+  }
+
+  if (component->is_empty()) {
+    _flags = F_empty;
+    _components.clear();
+
+  } else if (component->is_infinite() || is_empty()) {
+    // No-op.
+
+  } else if (component->is_exact_type(IntersectionBoundingVolume::get_class_type())) {
+    // Another special case.  Just more components.
+    const IntersectionBoundingVolume *other = DCAST(IntersectionBoundingVolume, component);
+    for (Components::const_iterator ci = other->_components.begin();
+         ci != other->_components.end();
+         ++ci) {
+      add_component(*ci);
+    }
+    
+  } else {
+    // The general case.
+    size_t i = 0;
+    while (i < _components.size()) {
+      const GeometricBoundingVolume *existing = _components[i];
+      ++i;
+
+      int result = component->contains(existing);
+      if ((result & IF_all) != 0) {
+        // The existing component is entirely within this one; no need
+        // to do anything with it.
+        return;
+
+      } else if (result == 0) {
+        // No intersection between these components; we're now empty.
+        _flags = F_empty;
+        _components.clear();
+        return;
+      }
+
+      result = existing->contains(component);
+      if ((result & IF_all) != 0) {
+        // This new component is entirely within an existing
+        // component; no need to keep the existing one.
+        --i;
+        _components.erase(_components.begin() + i);
+
+      } else if (result == 0) {
+        // No intersection between these components; we're now empty.
+        _flags = F_empty;
+        _components.clear();
+        return;
+      }
+    }
+
+    _flags &= ~F_infinite;
+    _components.push_back(component);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: IntersectionBoundingVolume::extend_other
+//       Access: Protected, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+bool IntersectionBoundingVolume::
+extend_other(BoundingVolume *other) const {
+  return other->extend_by_intersection(this);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: IntersectionBoundingVolume::around_other
+//       Access: Protected, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+bool IntersectionBoundingVolume::
+around_other(BoundingVolume *other,
+             const BoundingVolume **first,
+             const BoundingVolume **last) const {
+  return other->around_intersections(first, last);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: IntersectionBoundingVolume::contains_other
+//       Access: Protected, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+int IntersectionBoundingVolume::
+contains_other(const BoundingVolume *other) const {
+  return other->contains_intersection(this);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: IntersectionBoundingVolume::contains_point
+//       Access: Protected, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+int IntersectionBoundingVolume::
+contains_point(const LPoint3 &point) const {
+  nassertr(!point.is_nan(), IF_no_intersection);
+
+  int result = IF_possible | IF_some | IF_all;
+  for (Components::const_iterator ci = _components.begin();
+       ci != _components.end();
+       ++ci) {
+    int this_result = (*ci)->contains(point);
+    if ((this_result & IF_dont_understand) != 0) {
+      result |= IF_dont_understand;
+      break;
+    }
+    result &= this_result;
+    if ((result & IF_possible) == 0) {
+      // No point in looking further.
+      break;
+    }
+  }
+
+  return result;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: IntersectionBoundingVolume::contains_lineseg
+//       Access: Protected, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+int IntersectionBoundingVolume::
+contains_lineseg(const LPoint3 &a, const LPoint3 &b) const {
+  nassertr(!a.is_nan() && !b.is_nan(), IF_no_intersection);
+
+  int result = IF_possible | IF_some | IF_all;
+  for (Components::const_iterator ci = _components.begin();
+       ci != _components.end();
+       ++ci) {
+    int this_result = (*ci)->contains(a, b);
+    if ((this_result & IF_dont_understand) != 0) {
+      result |= IF_dont_understand;
+      break;
+    }
+    result &= this_result;
+    if ((result & IF_possible) == 0) {
+      // No point in looking further.
+      break;
+    }
+  }
+
+  return result;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: IntersectionBoundingVolume::contains_sphere
+//       Access: Protected, Virtual
+//  Description: Double-dispatch support: called by contains_other()
+//               when the type we're testing for intersection is known
+//               to be a sphere.
+////////////////////////////////////////////////////////////////////
+int IntersectionBoundingVolume::
+contains_sphere(const BoundingSphere *sphere) const {
+  int result = IF_possible | IF_some | IF_all;
+  for (Components::const_iterator ci = _components.begin();
+       ci != _components.end();
+       ++ci) {
+    int this_result = (*ci)->contains_sphere(sphere);
+    if ((this_result & IF_dont_understand) != 0) {
+      result |= IF_dont_understand;
+      break;
+    }
+    result &= this_result;
+    if ((result & IF_possible) == 0) {
+      // No point in looking further.
+      break;
+    }
+  }
+
+  return result;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: IntersectionBoundingVolume::contains_box
+//       Access: Protected, Virtual
+//  Description: Double-dispatch support: called by contains_other()
+//               when the type we're testing for intersection is known
+//               to be a box.
+////////////////////////////////////////////////////////////////////
+int IntersectionBoundingVolume::
+contains_box(const BoundingBox *box) const {
+  int result = IF_possible | IF_some | IF_all;
+  for (Components::const_iterator ci = _components.begin();
+       ci != _components.end();
+       ++ci) {
+    int this_result = (*ci)->contains_box(box);
+    if ((this_result & IF_dont_understand) != 0) {
+      result |= IF_dont_understand;
+      break;
+    }
+    result &= this_result;
+    if ((result & IF_possible) == 0) {
+      // No point in looking further.
+      break;
+    }
+  }
+
+  return result;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: IntersectionBoundingVolume::contains_hexahedron
+//       Access: Protected, Virtual
+//  Description: Double-dispatch support: called by contains_other()
+//               when the type we're testing for intersection is known
+//               to be a hexahedron.
+////////////////////////////////////////////////////////////////////
+int IntersectionBoundingVolume::
+contains_hexahedron(const BoundingHexahedron *hexahedron) const {
+  int result = IF_possible | IF_some | IF_all;
+  for (Components::const_iterator ci = _components.begin();
+       ci != _components.end();
+       ++ci) {
+    int this_result = (*ci)->contains_hexahedron(hexahedron);
+    if ((this_result & IF_dont_understand) != 0) {
+      result |= IF_dont_understand;
+      break;
+    }
+    result &= this_result;
+    if ((result & IF_possible) == 0) {
+      // No point in looking further.
+      break;
+    }
+  }
+
+  return result;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: IntersectionBoundingVolume::contains_line
+//       Access: Protected, Virtual
+//  Description: Double-dispatch support: called by contains_other()
+//               when the type we're testing for intersection is known
+//               to be a line.
+////////////////////////////////////////////////////////////////////
+int IntersectionBoundingVolume::
+contains_line(const BoundingLine *line) const {
+  int result = IF_possible | IF_some | IF_all;
+  for (Components::const_iterator ci = _components.begin();
+       ci != _components.end();
+       ++ci) {
+    int this_result = (*ci)->contains_line(line);
+    if ((this_result & IF_dont_understand) != 0) {
+      result |= IF_dont_understand;
+      break;
+    }
+    result &= this_result;
+    if ((result & IF_possible) == 0) {
+      // No point in looking further.
+      break;
+    }
+  }
+
+  return result;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: IntersectionBoundingVolume::contains_plane
+//       Access: Protected, Virtual
+//  Description: Double-dispatch support: called by contains_other()
+//               when the type we're testing for intersection is known
+//               to be a plane.
+////////////////////////////////////////////////////////////////////
+int IntersectionBoundingVolume::
+contains_plane(const BoundingPlane *plane) const {
+  int result = IF_possible | IF_some | IF_all;
+  for (Components::const_iterator ci = _components.begin();
+       ci != _components.end();
+       ++ci) {
+    int this_result = (*ci)->contains_plane(plane);
+    if ((this_result & IF_dont_understand) != 0) {
+      result |= IF_dont_understand;
+      break;
+    }
+    result &= this_result;
+    if ((result & IF_possible) == 0) {
+      // No point in looking further.
+      break;
+    }
+  }
+
+  return result;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: IntersectionBoundingVolume::contains_union
+//       Access: Protected, Virtual
+//  Description: Double-dispatch support: called by contains_other()
+//               when the type we're testing for intersection is known
+//               to be a union object.
+////////////////////////////////////////////////////////////////////
+int IntersectionBoundingVolume::
+contains_union(const UnionBoundingVolume *unionv) const {
+  int result = IF_possible | IF_some | IF_all;
+  for (Components::const_iterator ci = _components.begin();
+       ci != _components.end();
+       ++ci) {
+    int this_result = (*ci)->contains_union(unionv);
+    if ((this_result & IF_dont_understand) != 0) {
+      result |= IF_dont_understand;
+      break;
+    }
+    result &= this_result;
+    if ((result & IF_possible) == 0) {
+      // No point in looking further.
+      break;
+    }
+  }
+
+  return result;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: IntersectionBoundingVolume::contains_intersection
+//       Access: Protected, Virtual
+//  Description: Double-dispatch support: called by contains_other()
+//               when the type we're testing for intersection is known
+//               to be an intersection object.
+////////////////////////////////////////////////////////////////////
+int IntersectionBoundingVolume::
+contains_intersection(const IntersectionBoundingVolume *intersection) const {
+  int result = IF_possible | IF_some | IF_all;
+  for (Components::const_iterator ci = _components.begin();
+       ci != _components.end();
+       ++ci) {
+    int this_result = (*ci)->contains_intersection(intersection);
+    if ((this_result & IF_dont_understand) != 0) {
+      result |= IF_dont_understand;
+      break;
+    }
+    result &= this_result;
+    if ((result & IF_possible) == 0) {
+      // No point in looking further.
+      break;
+    }
+  }
+
+  return result;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: IntersectionBoundingVolume::contains_finite
+//       Access: Protected, Virtual
+//  Description: Generic handler for a FiniteBoundingVolume.
+////////////////////////////////////////////////////////////////////
+int IntersectionBoundingVolume::
+contains_finite(const FiniteBoundingVolume *volume) const {
+  int result = IF_possible | IF_some | IF_all;
+  for (Components::const_iterator ci = _components.begin();
+       ci != _components.end();
+       ++ci) {
+    int this_result = (*ci)->contains_finite(volume);
+    if ((this_result & IF_dont_understand) != 0) {
+      result |= IF_dont_understand;
+      break;
+    }
+    result &= this_result;
+    if ((result & IF_possible) == 0) {
+      // No point in looking further.
+      break;
+    }
+  }
+
+  return result;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: IntersectionBoundingVolume::contains_geometric
+//       Access: Protected, Virtual
+//  Description: Generic handler for a GeometricBoundingVolume.
+////////////////////////////////////////////////////////////////////
+int IntersectionBoundingVolume::
+contains_geometric(const GeometricBoundingVolume *volume) const {
+  int result = IF_possible | IF_some | IF_all;
+  for (Components::const_iterator ci = _components.begin();
+       ci != _components.end();
+       ++ci) {
+    int this_result = (*ci)->contains_geometric(volume);
+    if ((this_result & IF_dont_understand) != 0) {
+      result |= IF_dont_understand;
+      break;
+    }
+    result &= this_result;
+    if ((result & IF_possible) == 0) {
+      // No point in looking further.
+      break;
+    }
+  }
+
+  return result;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: IntersectionBoundingVolume::other_contains_intersection
+//       Access: Protected, Virtual
+//  Description: Generic reverse-direction comparison.  Called by
+//               BoundingVolumes that do not implement
+//               contains_intersection() explicitly.  This returns the test
+//               of whether the other volume contains this volume.
+////////////////////////////////////////////////////////////////////
+int IntersectionBoundingVolume::
+other_contains_intersection(const BoundingVolume *volume) const {
+  int result = IF_possible | IF_some | IF_all;
+  for (Components::const_iterator ci = _components.begin();
+       ci != _components.end();
+       ++ci) {
+    int this_result = volume->contains(*ci);
+    if ((this_result & IF_dont_understand) != 0) {
+      result |= IF_dont_understand;
+      break;
+    }
+    result &= this_result;
+    if ((result & IF_possible) == 0) {
+      // No point in looking further.
+      break;
+    }
+  }
+
+  return result;
+}
+

+ 104 - 0
panda/src/mathutil/intersectionBoundingVolume.h

@@ -0,0 +1,104 @@
+// Filename: intersectionBoundingVolume.h
+// Created by:  drose (08Feb12)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef INTERSECTIONBOUNDINGVOLUME_H
+#define INTERSECTIONBOUNDINGVOLUME_H
+
+#include "pandabase.h"
+
+#include "geometricBoundingVolume.h"
+#include "pvector.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : IntersectionBoundingVolume
+// Description : This special bounding volume is the intersection of all of
+//               its constituent bounding volumes.
+//
+//               A point is defined to be within an
+//               IntersectionBoundingVolume if it is within all of its
+//               component bounding volumes.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA_MATHUTIL IntersectionBoundingVolume : public GeometricBoundingVolume {
+PUBLISHED:
+  INLINE_MATHUTIL IntersectionBoundingVolume();
+  ALLOC_DELETED_CHAIN(IntersectionBoundingVolume);
+
+public:
+  IntersectionBoundingVolume(const IntersectionBoundingVolume &copy);
+
+public:
+  virtual BoundingVolume *make_copy() const;
+
+  virtual LPoint3 get_approx_center() const;
+  virtual void xform(const LMatrix4 &mat);
+
+  virtual void output(ostream &out) const;
+  virtual void write(ostream &out, int indent_level) const;
+
+PUBLISHED:
+  INLINE_MATHUTIL int get_num_components() const;
+  INLINE_MATHUTIL const GeometricBoundingVolume *get_component(int n) const;
+  MAKE_SEQ(get_components, get_num_components, get_component);
+
+  void clear_components();
+  void add_component(const GeometricBoundingVolume *component);
+
+protected:
+  virtual bool extend_other(BoundingVolume *other) const;
+  virtual bool around_other(BoundingVolume *other,
+                            const BoundingVolume **first,
+                            const BoundingVolume **last) const;
+  virtual int contains_other(const BoundingVolume *other) const;
+
+  virtual int contains_point(const LPoint3 &point) const;
+  virtual int contains_lineseg(const LPoint3 &a, const LPoint3 &b) const;
+  virtual int contains_sphere(const BoundingSphere *sphere) const;
+  virtual int contains_box(const BoundingBox *box) const;
+  virtual int contains_hexahedron(const BoundingHexahedron *hexahedron) const;
+  virtual int contains_line(const BoundingLine *line) const;
+  virtual int contains_plane(const BoundingPlane *plane) const;
+  virtual int contains_union(const UnionBoundingVolume *unionv) const;
+  virtual int contains_intersection(const IntersectionBoundingVolume *intersection) const;
+  virtual int contains_finite(const FiniteBoundingVolume *volume) const;
+  virtual int contains_geometric(const GeometricBoundingVolume *volume) const;
+  int other_contains_intersection(const BoundingVolume *other) const;
+
+private:
+  typedef pvector<CPT(GeometricBoundingVolume) > Components;
+  Components _components;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    GeometricBoundingVolume::init_type();
+    register_type(_type_handle, "IntersectionBoundingVolume",
+                  GeometricBoundingVolume::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;
+
+  friend class BoundingVolume;
+};
+
+#include "intersectionBoundingVolume.I"
+
+#endif
+

+ 2 - 0
panda/src/mathutil/p3mathutil_composite1.cxx

@@ -6,6 +6,8 @@
 #include "boundingVolume.cxx"
 #include "finiteBoundingVolume.cxx"
 #include "geometricBoundingVolume.cxx"
+#include "intersectionBoundingVolume.cxx"
 #include "omniBoundingVolume.cxx"
+#include "unionBoundingVolume.cxx"
 #include "parabola.cxx"
 #include "config_mathutil.cxx"

+ 44 - 0
panda/src/mathutil/unionBoundingVolume.I

@@ -0,0 +1,44 @@
+// Filename: unionBoundingVolume.I
+// Created by:  drose (08Feb12)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: UnionBoundingVolume::Constructor
+//       Access: Published
+//  Description: Constructs an empty union.
+////////////////////////////////////////////////////////////////////
+INLINE_MATHUTIL UnionBoundingVolume::
+UnionBoundingVolume() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: UnionBoundingVolume::get_num_components
+//       Access: Published
+//  Description: Returns the number of components in the union.
+////////////////////////////////////////////////////////////////////
+INLINE_MATHUTIL int UnionBoundingVolume::
+get_num_components() const {
+  return (int)_components.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: UnionBoundingVolume::get_component
+//       Access: Published
+//  Description: Returns the nth component in the union.
+////////////////////////////////////////////////////////////////////
+INLINE_MATHUTIL const GeometricBoundingVolume *UnionBoundingVolume::
+get_component(int n) const {
+  nassertr(n >= 0 && n < (int)_components.size(), NULL);
+  return _components[n];
+}

+ 559 - 0
panda/src/mathutil/unionBoundingVolume.cxx

@@ -0,0 +1,559 @@
+// Filename: unionBoundingVolume.cxx
+// Created by:  drose (08Feb12)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#include "unionBoundingVolume.h"
+#include "config_mathutil.h"
+#include "dcast.h"
+#include "indent.h"
+
+TypeHandle UnionBoundingVolume::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: UnionBoundingVolume::Copy Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+UnionBoundingVolume::
+UnionBoundingVolume(const UnionBoundingVolume &copy) :
+  GeometricBoundingVolume(copy),
+  _components(copy._components)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: UnionBoundingVolume::make_copy
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+BoundingVolume *UnionBoundingVolume::
+make_copy() const {
+  return new UnionBoundingVolume(*this);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: UnionBoundingVolume::get_approx_center
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+LPoint3 UnionBoundingVolume::
+get_approx_center() const {
+  nassertr(!is_empty(), LPoint3::zero());
+  nassertr(!is_infinite(), LPoint3::zero());
+
+  LPoint3 center = LPoint3::zero();
+  for (Components::const_iterator ci = _components.begin();
+       ci != _components.end();
+       ++ci) {
+    center += (*ci)->get_approx_center();
+  }
+
+  return center / (PN_stdfloat)_components.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: UnionBoundingVolume::xform
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void UnionBoundingVolume::
+xform(const LMatrix4 &mat) {
+  nassertv(!mat.is_nan());
+
+  for (Components::iterator ci = _components.begin();
+       ci != _components.end();
+       ++ci) {
+    PT(GeometricBoundingVolume) copy = DCAST(GeometricBoundingVolume, (*ci)->make_copy());
+    copy->xform(mat);
+    (*ci) = copy;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: UnionBoundingVolume::output
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void UnionBoundingVolume::
+output(ostream &out) const {
+  if (is_empty()) {
+    out << "union, empty";
+  } else if (is_infinite()) {
+    out << "union, infinite";
+  } else {
+    out << "union [";
+    for (Components::const_iterator ci = _components.begin();
+         ci != _components.end();
+         ++ci) {
+      out << " " << *(*ci);
+    }
+    out << " ]";
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: UnionBoundingVolume::write
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void UnionBoundingVolume::
+write(ostream &out, int indent_level) const {
+  if (is_empty()) {
+    indent(out, indent_level) << "union, empty\n";
+  } else if (is_infinite()) {
+    indent(out, indent_level) << "union, infinite\n";
+  } else {
+    indent(out, indent_level) << "union {\n";
+    for (Components::const_iterator ci = _components.begin();
+         ci != _components.end();
+         ++ci) {
+      (*ci)->write(out, indent_level + 2);
+    }
+    indent(out, indent_level) << "}\n";
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: UnionBoundingVolume::clear_components
+//       Access: Published
+//  Description: Removes all components from the volume.
+////////////////////////////////////////////////////////////////////
+void UnionBoundingVolume::
+clear_components() {
+  _components.clear();
+  _flags = F_empty;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: UnionBoundingVolume::add_component
+//       Access: Published
+//  Description: Adds a new component to the volume.  This does not
+//               necessarily increase the total number of components
+//               by one, and you may or may not be able to find this
+//               component in the volume by a subsequent call to
+//               get_component(); certain optimizations may prevent
+//               the component from being added, or have other
+//               unexpected effects on the total set of components.
+////////////////////////////////////////////////////////////////////
+void UnionBoundingVolume::
+add_component(const GeometricBoundingVolume *component) {
+  if (component->is_infinite()) {
+    _flags = F_infinite;
+    _components.clear();
+
+  } else if (component->is_empty() || is_infinite()) {
+    // No-op.
+
+  } else {
+    size_t i = 0;
+    while (i < _components.size()) {
+      const GeometricBoundingVolume *existing = _components[i];
+      ++i;
+
+      int result = existing->contains(component);
+      if ((result & IF_all) != 0) {
+        // This new component is entirely within an existing
+        // component; no need to do anything with it.
+        return;
+      }
+
+      result = component->contains(existing);
+      if ((result & IF_all) != 0) {
+        // The existing component is entirely within this one; no need
+        // to keep the existing one.
+        --i;
+        _components.erase(_components.begin() + i);
+      }
+    }
+
+    _flags &= ~F_empty;
+    _components.push_back(component);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: UnionBoundingVolume::filter_intersection
+//       Access: Published
+//  Description: Removes from the union any components that have no
+//               intersection with the indicated volume.
+////////////////////////////////////////////////////////////////////
+void UnionBoundingVolume::
+filter_intersection(const BoundingVolume *volume) {
+  size_t i = 0;
+  while (i < _components.size()) {
+    const GeometricBoundingVolume *existing = _components[i];
+    ++i;
+
+    int result = volume->contains(existing);
+    if ((result & IF_possible) == 0) {
+      // There is no intersection.  Remove this component.
+      --i;
+      _components.erase(_components.begin() + i);
+    }
+  }
+
+  if (_components.empty()) {
+    _flags |= F_empty;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: UnionBoundingVolume::extend_other
+//       Access: Protected, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+bool UnionBoundingVolume::
+extend_other(BoundingVolume *other) const {
+  return other->extend_by_union(this);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: UnionBoundingVolume::around_other
+//       Access: Protected, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+bool UnionBoundingVolume::
+around_other(BoundingVolume *other,
+             const BoundingVolume **first,
+             const BoundingVolume **last) const {
+  return other->around_unions(first, last);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: UnionBoundingVolume::contains_other
+//       Access: Protected, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+int UnionBoundingVolume::
+contains_other(const BoundingVolume *other) const {
+  return other->contains_union(this);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: UnionBoundingVolume::extend_by_geometric
+//       Access: Protected
+//  Description: 
+////////////////////////////////////////////////////////////////////
+bool UnionBoundingVolume::
+extend_by_geometric(const GeometricBoundingVolume *volume) {
+  add_component(volume);
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: UnionBoundingVolume::around_geometric
+//       Access: Protected
+//  Description: 
+////////////////////////////////////////////////////////////////////
+bool UnionBoundingVolume::
+around_geometric(const BoundingVolume **first,
+                 const BoundingVolume **last) {
+  nassertr(first != last, false);
+
+  clear_components();
+
+  const BoundingVolume **p = first;
+  while (p != last) {
+    nassertr(!(*p)->is_infinite(), false);
+    if (!(*p)->is_empty()) {
+      const GeometricBoundingVolume *volume = (*p)->as_geometric_bounding_volume();
+      if (volume != (GeometricBoundingVolume *)NULL) {
+        add_component(volume);
+      } else {
+        set_infinite();
+        _components.clear();
+        return false;
+      }
+    }
+  }
+
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: UnionBoundingVolume::contains_point
+//       Access: Protected, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+int UnionBoundingVolume::
+contains_point(const LPoint3 &point) const {
+  nassertr(!point.is_nan(), IF_no_intersection);
+
+  int result = 0;
+  for (Components::const_iterator ci = _components.begin();
+       ci != _components.end();
+       ++ci) {
+    result |= (*ci)->contains(point);
+    if ((result & (IF_all | IF_dont_understand)) != 0) {
+      // No point in looking further.
+      break;
+    }
+  }
+
+  return result;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: UnionBoundingVolume::contains_lineseg
+//       Access: Protected, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+int UnionBoundingVolume::
+contains_lineseg(const LPoint3 &a, const LPoint3 &b) const {
+  nassertr(!a.is_nan() && !b.is_nan(), IF_no_intersection);
+
+  int result = 0;
+  for (Components::const_iterator ci = _components.begin();
+       ci != _components.end();
+       ++ci) {
+    result |= (*ci)->contains(a, b);
+    if ((result & (IF_all | IF_dont_understand)) != 0) {
+      // No point in looking further.
+      break;
+    }
+  }
+
+  return result;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: UnionBoundingVolume::contains_sphere
+//       Access: Protected, Virtual
+//  Description: Double-dispatch support: called by contains_other()
+//               when the type we're testing for intersection is known
+//               to be a sphere.
+////////////////////////////////////////////////////////////////////
+int UnionBoundingVolume::
+contains_sphere(const BoundingSphere *sphere) const {
+  int result = 0;
+  for (Components::const_iterator ci = _components.begin();
+       ci != _components.end();
+       ++ci) {
+    result |= (*ci)->contains_sphere(sphere);
+    if ((result & (IF_all | IF_dont_understand)) != 0) {
+      // No point in looking further.
+      break;
+    }
+  }
+
+  return result;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: UnionBoundingVolume::contains_box
+//       Access: Protected, Virtual
+//  Description: Double-dispatch support: called by contains_other()
+//               when the type we're testing for intersection is known
+//               to be a box.
+////////////////////////////////////////////////////////////////////
+int UnionBoundingVolume::
+contains_box(const BoundingBox *box) const {
+  int result = 0;
+  for (Components::const_iterator ci = _components.begin();
+       ci != _components.end();
+       ++ci) {
+    result |= (*ci)->contains_box(box);
+    if ((result & (IF_all | IF_dont_understand)) != 0) {
+      // No point in looking further.
+      break;
+    }
+  }
+
+  return result;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: UnionBoundingVolume::contains_hexahedron
+//       Access: Protected, Virtual
+//  Description: Double-dispatch support: called by contains_other()
+//               when the type we're testing for intersection is known
+//               to be a hexahedron.
+////////////////////////////////////////////////////////////////////
+int UnionBoundingVolume::
+contains_hexahedron(const BoundingHexahedron *hexahedron) const {
+  int result = 0;
+  for (Components::const_iterator ci = _components.begin();
+       ci != _components.end();
+       ++ci) {
+    result |= (*ci)->contains_hexahedron(hexahedron);
+    if ((result & (IF_all | IF_dont_understand)) != 0) {
+      // No point in looking further.
+      break;
+    }
+  }
+
+  return result;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: UnionBoundingVolume::contains_line
+//       Access: Protected, Virtual
+//  Description: Double-dispatch support: called by contains_other()
+//               when the type we're testing for intersection is known
+//               to be a line.
+////////////////////////////////////////////////////////////////////
+int UnionBoundingVolume::
+contains_line(const BoundingLine *line) const {
+  int result = 0;
+  for (Components::const_iterator ci = _components.begin();
+       ci != _components.end();
+       ++ci) {
+    result |= (*ci)->contains_line(line);
+    if ((result & (IF_all | IF_dont_understand)) != 0) {
+      // No point in looking further.
+      break;
+    }
+  }
+
+  return result;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: UnionBoundingVolume::contains_plane
+//       Access: Protected, Virtual
+//  Description: Double-dispatch support: called by contains_other()
+//               when the type we're testing for intersection is known
+//               to be a plane.
+////////////////////////////////////////////////////////////////////
+int UnionBoundingVolume::
+contains_plane(const BoundingPlane *plane) const {
+  int result = 0;
+  for (Components::const_iterator ci = _components.begin();
+       ci != _components.end();
+       ++ci) {
+    result |= (*ci)->contains_plane(plane);
+    if ((result & (IF_all | IF_dont_understand)) != 0) {
+      // No point in looking further.
+      break;
+    }
+  }
+
+  return result;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: UnionBoundingVolume::contains_union
+//       Access: Protected, Virtual
+//  Description: Double-dispatch support: called by contains_other()
+//               when the type we're testing for intersection is known
+//               to be a union object.
+////////////////////////////////////////////////////////////////////
+int UnionBoundingVolume::
+contains_union(const UnionBoundingVolume *unionv) const {
+  int result = 0;
+  for (Components::const_iterator ci = _components.begin();
+       ci != _components.end();
+       ++ci) {
+    result |= (*ci)->contains_union(unionv);
+    if ((result & (IF_all | IF_dont_understand)) != 0) {
+      // No point in looking further.
+      break;
+    }
+  }
+
+  return result;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: UnionBoundingVolume::contains_intersection
+//       Access: Protected, Virtual
+//  Description: Double-dispatch support: called by contains_other()
+//               when the type we're testing for intersection is known
+//               to be an intersection object.
+////////////////////////////////////////////////////////////////////
+int UnionBoundingVolume::
+contains_intersection(const IntersectionBoundingVolume *intersection) const {
+  int result = 0;
+  for (Components::const_iterator ci = _components.begin();
+       ci != _components.end();
+       ++ci) {
+    result |= (*ci)->contains_intersection(intersection);
+    if ((result & (IF_all | IF_dont_understand)) != 0) {
+      // No point in looking further.
+      break;
+    }
+  }
+
+  return result;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: UnionBoundingVolume::contains_finite
+//       Access: Protected, Virtual
+//  Description: Generic handler for a FiniteBoundingVolume.
+////////////////////////////////////////////////////////////////////
+int UnionBoundingVolume::
+contains_finite(const FiniteBoundingVolume *volume) const {
+  int result = 0;
+  for (Components::const_iterator ci = _components.begin();
+       ci != _components.end();
+       ++ci) {
+    result |= (*ci)->contains_finite(volume);
+    if ((result & (IF_all | IF_dont_understand)) != 0) {
+      // No point in looking further.
+      break;
+    }
+  }
+
+  return result;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: UnionBoundingVolume::contains_geometric
+//       Access: Protected, Virtual
+//  Description: Generic handler for a GeometricBoundingVolume.
+////////////////////////////////////////////////////////////////////
+int UnionBoundingVolume::
+contains_geometric(const GeometricBoundingVolume *volume) const {
+  int result = 0;
+  for (Components::const_iterator ci = _components.begin();
+       ci != _components.end();
+       ++ci) {
+    result |= (*ci)->contains_geometric(volume);
+    if ((result & (IF_all | IF_dont_understand)) != 0) {
+      // No point in looking further.
+      break;
+    }
+  }
+
+  return result;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: UnionBoundingVolume::other_contains_union
+//       Access: Protected, Virtual
+//  Description: Generic reverse-direction comparison.  Called by
+//               BoundingVolumes that do not implement
+//               contains_union() explicitly.  This returns the test
+//               of whether the other volume contains this volume.
+////////////////////////////////////////////////////////////////////
+int UnionBoundingVolume::
+other_contains_union(const BoundingVolume *volume) const {
+  int all_result = IF_possible | IF_some | IF_all;
+  int some_result = 0;
+  for (Components::const_iterator ci = _components.begin();
+       ci != _components.end();
+       ++ci) {
+    int this_result = volume->contains(*ci);
+    if ((this_result & IF_dont_understand) != 0) {
+      some_result |= IF_dont_understand;
+      break;
+    }
+    all_result &= this_result;
+    some_result |= this_result;
+  }
+
+  some_result &= ~IF_all;
+  return some_result | all_result;
+}
+

+ 108 - 0
panda/src/mathutil/unionBoundingVolume.h

@@ -0,0 +1,108 @@
+// Filename: unionBoundingVolume.h
+// Created by:  drose (08Feb12)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef UNIONBOUNDINGVOLUME_H
+#define UNIONBOUNDINGVOLUME_H
+
+#include "pandabase.h"
+
+#include "geometricBoundingVolume.h"
+#include "pvector.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : UnionBoundingVolume
+// Description : This special bounding volume is the union of all of
+//               its constituent bounding volumes.
+//
+//               A point is defined to be within a UnionBoundingVolume
+//               if it is within any one or more of its component
+//               bounding volumes.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA_MATHUTIL UnionBoundingVolume : public GeometricBoundingVolume {
+PUBLISHED:
+  INLINE_MATHUTIL UnionBoundingVolume();
+  ALLOC_DELETED_CHAIN(UnionBoundingVolume);
+
+public:
+  UnionBoundingVolume(const UnionBoundingVolume &copy);
+
+  virtual BoundingVolume *make_copy() const;
+
+  virtual LPoint3 get_approx_center() const;
+  virtual void xform(const LMatrix4 &mat);
+
+  virtual void output(ostream &out) const;
+  virtual void write(ostream &out, int indent_level) const;
+
+PUBLISHED:
+  INLINE_MATHUTIL int get_num_components() const;
+  INLINE_MATHUTIL const GeometricBoundingVolume *get_component(int n) const;
+  MAKE_SEQ(get_components, get_num_components, get_component);
+
+  void clear_components();
+  void add_component(const GeometricBoundingVolume *component);
+
+  void filter_intersection(const BoundingVolume *volume);
+
+protected:
+  virtual bool extend_other(BoundingVolume *other) const;
+  virtual bool around_other(BoundingVolume *other,
+                            const BoundingVolume **first,
+                            const BoundingVolume **last) const;
+  virtual int contains_other(const BoundingVolume *other) const;
+
+  virtual bool extend_by_geometric(const GeometricBoundingVolume *volume);
+  virtual bool around_geometric(const BoundingVolume **first,
+                                const BoundingVolume **last);
+
+  virtual int contains_point(const LPoint3 &point) const;
+  virtual int contains_lineseg(const LPoint3 &a, const LPoint3 &b) const;
+  virtual int contains_sphere(const BoundingSphere *sphere) const;
+  virtual int contains_box(const BoundingBox *box) const;
+  virtual int contains_hexahedron(const BoundingHexahedron *hexahedron) const;
+  virtual int contains_line(const BoundingLine *line) const;
+  virtual int contains_plane(const BoundingPlane *plane) const;
+  virtual int contains_union(const UnionBoundingVolume *unionv) const;
+  virtual int contains_intersection(const IntersectionBoundingVolume *intersection) const;
+  virtual int contains_finite(const FiniteBoundingVolume *volume) const;
+  virtual int contains_geometric(const GeometricBoundingVolume *volume) const;
+  int other_contains_union(const BoundingVolume *other) const;
+
+private:
+  typedef pvector<CPT(GeometricBoundingVolume) > Components;
+  Components _components;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    GeometricBoundingVolume::init_type();
+    register_type(_type_handle, "UnionBoundingVolume",
+                  GeometricBoundingVolume::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;
+
+  friend class BoundingVolume;
+};
+
+#include "unionBoundingVolume.I"
+
+#endif

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

@@ -148,6 +148,34 @@ get_cull_center() const {
   return _cull_center;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: Camera::set_cull_bounds
+//       Access: Published
+//  Description: Specifies the bounding volume that should be used to
+//               perform culling from this camera.  Normally, this is
+//               the bounding volume returned from the active lens'
+//               make_bounds() call, but you may override this to
+//               specify a custom volume if you require.  The
+//               specified bounding volume will be understood to be in
+//               the coordinate space of the get_cull_center() node.
+////////////////////////////////////////////////////////////////////
+INLINE void Camera::
+set_cull_bounds(BoundingVolume *cull_bounds) {
+  _cull_bounds = cull_bounds;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Camera::get_cull_bounds
+//       Access: Published
+//  Description: Returns the custom cull volume that was set by
+//               set_cull_bounds(), if any, or NULL if no custom cull
+//               volume was set.
+////////////////////////////////////////////////////////////////////
+INLINE BoundingVolume *Camera::
+get_cull_bounds() const {
+  return _cull_bounds;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: Camera::set_lod_center
 //       Access: Published

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

@@ -63,6 +63,9 @@ PUBLISHED:
   INLINE void set_cull_center(const NodePath &cull_center);
   INLINE const NodePath &get_cull_center() const;
 
+  INLINE void set_cull_bounds(BoundingVolume *cull_bounds);
+  INLINE BoundingVolume *get_cull_bounds() const;
+
   INLINE void set_lod_center(const NodePath &lod_center);
   INLINE const NodePath &get_lod_center() const;
 
@@ -90,6 +93,7 @@ private:
   bool _active;
   NodePath _scene;
   NodePath _cull_center;
+  PT(BoundingVolume) _cull_bounds;
   NodePath _lod_center;
 
   DrawMask _camera_mask;

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

@@ -206,6 +206,25 @@ get_cull_center() const {
   }
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: SceneSetup::get_cull_bounds
+//       Access: Published
+//  Description: Returns the bounding volume that should be used to
+//               perform view-frustum culling (in the space of
+//               get_cull_center()).  This is normally the current
+//               lens' bounding volume, but it may be overridden with
+//               Camera::set_cull_bounds().
+////////////////////////////////////////////////////////////////////
+INLINE PT(BoundingVolume) SceneSetup::
+get_cull_bounds() const {
+  PT(BoundingVolume) bounds = _camera_node->get_cull_bounds();
+  if (bounds != (BoundingVolume *)NULL) {
+    return bounds;
+  }
+
+  return _lens->make_bounds();
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: SceneSetup::set_initial_state
 //       Access: Published

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

@@ -60,6 +60,7 @@ PUBLISHED:
   INLINE bool get_inverted() const;
 
   INLINE const NodePath &get_cull_center() const;
+  INLINE PT(BoundingVolume) get_cull_bounds() const;
 
   INLINE void set_initial_state(const RenderState *initial_state);
   INLINE const RenderState *get_initial_state() const;