Browse Source

optional BoundingBox

David Rose 18 years ago
parent
commit
faad1e333d
35 changed files with 2017 additions and 226 deletions
  1. 28 24
      panda/src/collide/collisionNode.cxx
  2. 21 17
      panda/src/collide/collisionPolygon.cxx
  3. 13 6
      panda/src/framework/windowFramework.cxx
  4. 26 15
      panda/src/gobj/geom.cxx
  5. 95 6
      panda/src/grutil/pipeOcclusionCullTraverser.cxx
  6. 3 0
      panda/src/grutil/pipeOcclusionCullTraverser.h
  7. 3 0
      panda/src/mathutil/Sources.pp
  8. 120 0
      panda/src/mathutil/boundingBox.I
  9. 643 0
      panda/src/mathutil/boundingBox.cxx
  10. 126 0
      panda/src/mathutil/boundingBox.h
  11. 27 1
      panda/src/mathutil/boundingHexahedron.I
  12. 172 62
      panda/src/mathutil/boundingHexahedron.cxx
  13. 10 17
      panda/src/mathutil/boundingHexahedron.h
  14. 21 0
      panda/src/mathutil/boundingLine.I
  15. 70 1
      panda/src/mathutil/boundingLine.cxx
  16. 5 1
      panda/src/mathutil/boundingLine.h
  17. 47 0
      panda/src/mathutil/boundingPlane.cxx
  18. 2 0
      panda/src/mathutil/boundingPlane.h
  19. 21 0
      panda/src/mathutil/boundingSphere.I
  20. 204 37
      panda/src/mathutil/boundingSphere.cxx
  21. 5 0
      panda/src/mathutil/boundingSphere.h
  22. 5 5
      panda/src/mathutil/boundingVolume.I
  23. 128 2
      panda/src/mathutil/boundingVolume.cxx
  24. 25 1
      panda/src/mathutil/boundingVolume.h
  25. 8 0
      panda/src/mathutil/config_mathutil.cxx
  26. 3 0
      panda/src/mathutil/config_mathutil.h
  27. 23 0
      panda/src/mathutil/finiteBoundingVolume.cxx
  28. 1 1
      panda/src/mathutil/finiteBoundingVolume.h
  29. 1 0
      panda/src/mathutil/mathutil_composite1.cxx
  30. 31 0
      panda/src/mathutil/omniBoundingVolume.cxx
  31. 4 0
      panda/src/mathutil/omniBoundingVolume.h
  32. 46 0
      panda/src/pgraph/cullTraverser.cxx
  33. 29 11
      panda/src/pgraph/geomNode.cxx
  34. 1 0
      panda/src/pgraph/geomNode.h
  35. 50 19
      panda/src/pgraph/pandaNode.cxx

+ 28 - 24
panda/src/collide/collisionNode.cxx

@@ -32,6 +32,8 @@
 #include "bamWriter.h"
 #include "bamWriter.h"
 #include "clockObject.h"
 #include "clockObject.h"
 #include "boundingSphere.h"
 #include "boundingSphere.h"
+#include "boundingBox.h"
+#include "config_mathutil.h"
 
 
 TypeHandle CollisionNode::_type_handle;
 TypeHandle CollisionNode::_type_handle;
 
 
@@ -309,41 +311,43 @@ set_from_collide_mask(CollideMask mask) {
 void CollisionNode::
 void CollisionNode::
 compute_internal_bounds(PandaNode::BoundsData *bdata, int pipeline_stage, 
 compute_internal_bounds(PandaNode::BoundsData *bdata, int pipeline_stage, 
                         Thread *current_thread) const {
                         Thread *current_thread) const {
-  // First, get ourselves a fresh, empty bounding volume.
-  PT(BoundingVolume) bound = new BoundingSphere;
-
-  // Now actually compute the bounding volume by putting it around all
-  // of our solids' bounding volumes.
+  pvector<CPT(BoundingVolume) > child_volumes_ref;
   pvector<const BoundingVolume *> child_volumes;
   pvector<const BoundingVolume *> child_volumes;
-  pvector<CPT(BoundingVolume) > cpt_volumes;
+  bool all_box = true;
 
 
   Solids::const_iterator gi;
   Solids::const_iterator gi;
   for (gi = _solids.begin(); gi != _solids.end(); ++gi) {
   for (gi = _solids.begin(); gi != _solids.end(); ++gi) {
     CPT(CollisionSolid) solid = (*gi).get_read_pointer();
     CPT(CollisionSolid) solid = (*gi).get_read_pointer();
     CPT(BoundingVolume) volume = solid->get_bounds();
     CPT(BoundingVolume) volume = solid->get_bounds();
-    cpt_volumes.push_back(volume);
-    child_volumes.push_back(volume);
-  }
 
 
-  const BoundingVolume **child_begin = &child_volumes[0];
-  const BoundingVolume **child_end = child_begin + child_volumes.size();
+    if (!volume->is_empty()) {
+      child_volumes_ref.push_back(volume);
+      child_volumes.push_back(volume);
+      if (!volume->is_exact_type(BoundingBox::get_class_type())) {
+        all_box = false;
+      }
+    }
+  }
 
 
-  bool success =
-    bound->around(child_begin, child_end);
+  PT(GeometricBoundingVolume) gbv = new BoundingBox;
 
 
-#ifdef NOTIFY_DEBUG
-  if (!success) {
-    collide_cat.error()
-      << "Unable to generate bounding volume for " << *this << ":\n"
-      << "Cannot put " << bound->get_type() << " around:\n";
-    for (int i = 0; i < (int)child_volumes.size(); i++) {
-      collide_cat.error(false)
-        << "  " << *child_volumes[i] << "\n";
-    }
+  if (bounds_type == BoundingVolume::BT_box ||
+      (bounds_type != BoundingVolume::BT_sphere && all_box)) {
+    // If all of the child volumes are a BoundingBox, then our volume
+    // is also a BoundingBox.
+    gbv = new BoundingBox;
+  } else {
+    // Otherwise, it's a sphere.
+    gbv = new BoundingSphere;
   }
   }
-#endif
 
 
-  bdata->_internal_bounds = bound;
+  if (child_volumes.size() > 0) {
+    const BoundingVolume **child_begin = &child_volumes[0];
+    const BoundingVolume **child_end = child_begin + child_volumes.size();
+    ((BoundingVolume *)gbv)->around(child_begin, child_end);
+  }
+  
+  bdata->_internal_bounds = gbv;
   bdata->_internal_vertices = 0;
   bdata->_internal_vertices = 0;
   bdata->_internal_bounds_stale = false;
   bdata->_internal_bounds_stale = false;
 }
 }

+ 21 - 17
panda/src/collide/collisionPolygon.cxx

@@ -25,7 +25,7 @@
 #include "collisionSegment.h"
 #include "collisionSegment.h"
 #include "config_collide.h"
 #include "config_collide.h"
 #include "cullTraverserData.h"
 #include "cullTraverserData.h"
-#include "boundingSphere.h"
+#include "boundingBox.h"
 #include "pointerToArray.h"
 #include "pointerToArray.h"
 #include "geomNode.h"
 #include "geomNode.h"
 #include "geom.h"
 #include "geom.h"
@@ -386,27 +386,31 @@ write(ostream &out, int indent_level) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 PT(BoundingVolume) CollisionPolygon::
 PT(BoundingVolume) CollisionPolygon::
 compute_internal_bounds() const {
 compute_internal_bounds() const {
-  // First, get ourselves a fresh, empty bounding volume.
-  PT(BoundingVolume) bound = CollisionSolid::compute_internal_bounds();
-  nassertr(bound != (BoundingVolume*)0L, bound);
-
-  GeometricBoundingVolume *gbv = DCAST(GeometricBoundingVolume, bound);
+  if (_points.empty()) {
+    return new BoundingBox;
+  }
 
 
-  // Now actually compute the bounding volume by putting it around all
-  // of our vertices.
   LMatrix4f to_3d_mat;
   LMatrix4f to_3d_mat;
   rederive_to_3d_mat(to_3d_mat);
   rederive_to_3d_mat(to_3d_mat);
-  pvector<LPoint3f> vertices;
-  Points::const_iterator pi;
-  for (pi = _points.begin(); pi != _points.end(); ++pi) {
-    vertices.push_back(to_3d((*pi)._p, to_3d_mat));
-  }
 
 
-  const LPoint3f *vertices_begin = &vertices[0];
-  const LPoint3f *vertices_end = vertices_begin + vertices.size();
-  gbv->around(vertices_begin, vertices_end);
+  Points::const_iterator pi = _points.begin();
+  LPoint3f p = to_3d((*pi)._p, to_3d_mat);
+
+  LPoint3f x = p;
+  LPoint3f n = p;
+
+  for (++pi; pi != _points.end(); ++pi) {
+    p = to_3d((*pi)._p, to_3d_mat);
+
+    n.set(min(n[0], p[0]),
+          min(n[1], p[1]),
+          min(n[2], p[2]));
+    x.set(max(x[0], p[0]),
+          max(x[1], p[1]),
+          max(x[2], p[2]));
+  }
 
 
-  return bound;
+  return new BoundingBox(n, x);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 13 - 6
panda/src/framework/windowFramework.cxx

@@ -450,12 +450,19 @@ center_trackball(const NodePath &object) {
   // We expect at least a geometric bounding volume around the world.
   // We expect at least a geometric bounding volume around the world.
   nassertv(volume != (BoundingVolume *)NULL);
   nassertv(volume != (BoundingVolume *)NULL);
   nassertv(volume->is_of_type(GeometricBoundingVolume::get_class_type()));
   nassertv(volume->is_of_type(GeometricBoundingVolume::get_class_type()));
-  GeometricBoundingVolume *gbv = DCAST(GeometricBoundingVolume, volume);
+  CPT(GeometricBoundingVolume) gbv = DCAST(GeometricBoundingVolume, volume);
 
 
-  // Determine the bounding sphere around the world.  The topmost
-  // BoundingVolume might itself be a sphere (it's likely), but since
-  // it might not, we'll take no chances and make our own sphere.
-  PT(BoundingSphere) sphere = new BoundingSphere;
+  if (object.has_parent()) {
+    CPT(TransformState) net_transform = object.get_parent().get_net_transform();
+    PT(GeometricBoundingVolume) new_gbv = DCAST(GeometricBoundingVolume, gbv->make_copy());
+    new_gbv->xform(net_transform->get_mat());
+    gbv = new_gbv;
+  }
+
+  // Determine the bounding sphere around the object.  The
+  // BoundingVolume might be a sphere (it's likely), but since it
+  // might not, we'll take no chances and make our own sphere.
+  PT(BoundingSphere) sphere = new BoundingSphere(gbv->get_approx_center(), 0.0f);
   if (!sphere->extend_by(gbv)) {
   if (!sphere->extend_by(gbv)) {
     framework_cat.warning()
     framework_cat.warning()
       << "Cannot determine bounding volume of " << object << "\n";
       << "Cannot determine bounding volume of " << object << "\n";
@@ -497,7 +504,7 @@ center_trackball(const NodePath &object) {
     distance = radius / ctan(deg_2_rad(min(fov[0], fov[1]) / 2.0f));
     distance = radius / ctan(deg_2_rad(min(fov[0], fov[1]) / 2.0f));
 
 
     // Ensure the far plane is far enough back to see the entire object.
     // Ensure the far plane is far enough back to see the entire object.
-    float ideal_far_plane = distance + radius;
+    float ideal_far_plane = distance + radius * 1.5;
     lens->set_far(max(lens->get_default_far(), ideal_far_plane));
     lens->set_far(max(lens->get_default_far(), ideal_far_plane));
 
 
     // And that the near plane is far enough forward.
     // And that the near plane is far enough forward.

+ 26 - 15
panda/src/gobj/geom.cxx

@@ -26,7 +26,9 @@
 #include "bamReader.h"
 #include "bamReader.h"
 #include "bamWriter.h"
 #include "bamWriter.h"
 #include "boundingSphere.h"
 #include "boundingSphere.h"
+#include "boundingBox.h"
 #include "mutexHolder.h"
 #include "mutexHolder.h"
+#include "config_mathutil.h"
 
 
 UpdateSeq Geom::_next_modified;
 UpdateSeq Geom::_next_modified;
 PStatCollector Geom::_draw_primitive_setup_pcollector("Draw:Primitive:Setup");
 PStatCollector Geom::_draw_primitive_setup_pcollector("Draw:Primitive:Setup");
@@ -1074,30 +1076,32 @@ void Geom::
 compute_internal_bounds(Geom::CData *cdata, Thread *current_thread) const {
 compute_internal_bounds(Geom::CData *cdata, Thread *current_thread) const {
   int num_vertices = 0;
   int num_vertices = 0;
 
 
-  // First, get ourselves a fresh, empty bounding volume.
-  PT(BoundingVolume) bound = new BoundingSphere;
-  GeometricBoundingVolume *gbv = DCAST(GeometricBoundingVolume, bound);
-
   // Get the vertex data, after animation.
   // Get the vertex data, after animation.
   CPT(GeomVertexData) vertex_data = cdata->_data.get_read_pointer();
   CPT(GeomVertexData) vertex_data = cdata->_data.get_read_pointer();
   vertex_data = vertex_data->animate_vertices(current_thread);
   vertex_data = vertex_data->animate_vertices(current_thread);
 
 
   // Now actually compute the bounding volume.  We do this by using
   // Now actually compute the bounding volume.  We do this by using
-  // calc_tight_bounds to determine our minmax first.
-  LPoint3f points[2];
+  // calc_tight_bounds to determine our box first.
+  LPoint3f min, max;
   bool found_any = false;
   bool found_any = false;
-  do_calc_tight_bounds(points[0], points[1], found_any, vertex_data,
+  do_calc_tight_bounds(min, max, found_any, vertex_data,
 		       false, LMatrix4f::ident_mat(), cdata, current_thread);
 		       false, LMatrix4f::ident_mat(), cdata, current_thread);
+
   if (found_any) {
   if (found_any) {
     // Then we put the bounding volume around both of those points.
     // Then we put the bounding volume around both of those points.
-    // Technically, we should put it around the eight points at the
-    // corners of the rectangular solid, but we happen to know that
-    // the two diagonally opposite points is good enough to define any
-    // of our bound volume types.
+    if (bounds_type == BoundingVolume::BT_sphere) {
+      // The user specifically requested a BoundingSphere, so oblige.
+      BoundingBox box(min, max);
+      box.local_object();
+
+      PT(BoundingSphere) sphere = new BoundingSphere;
+      sphere->extend_by(&box);
+      cdata->_internal_bounds = sphere;
 
 
-    const LPoint3f *points_begin = &points[0];
-    const LPoint3f *points_end = points_begin + 2;
-    gbv->around(points_begin, points_end);
+    } else {
+      // The user requested a BoundingBox, or did not specify.
+      cdata->_internal_bounds = new BoundingBox(min, max);
+    }
 
 
     Primitives::const_iterator pi;
     Primitives::const_iterator pi;
     for (pi = cdata->_primitives.begin(); 
     for (pi = cdata->_primitives.begin(); 
@@ -1106,9 +1110,16 @@ compute_internal_bounds(Geom::CData *cdata, Thread *current_thread) const {
       CPT(GeomPrimitive) prim = (*pi).get_read_pointer();
       CPT(GeomPrimitive) prim = (*pi).get_read_pointer();
       num_vertices += prim->get_num_vertices();
       num_vertices += prim->get_num_vertices();
     }
     }
+
+  } else {
+    // No points; empty bounding volume.
+    if (bounds_type == BoundingVolume::BT_sphere) {
+      cdata->_internal_bounds = new BoundingSphere;
+    } else {
+      cdata->_internal_bounds = new BoundingBox;
+    }
   }
   }
 
 
-  cdata->_internal_bounds = bound;
   cdata->_nested_vertices = num_vertices;
   cdata->_nested_vertices = num_vertices;
   cdata->_internal_bounds_stale = false;
   cdata->_internal_bounds_stale = false;
 }
 }

+ 95 - 6
panda/src/grutil/pipeOcclusionCullTraverser.cxx

@@ -21,8 +21,10 @@
 #include "graphicsPipe.h"
 #include "graphicsPipe.h"
 #include "drawCullHandler.h"
 #include "drawCullHandler.h"
 #include "boundingSphere.h"
 #include "boundingSphere.h"
+#include "boundingBox.h"
 #include "geomVertexWriter.h"
 #include "geomVertexWriter.h"
 #include "geomTristrips.h"
 #include "geomTristrips.h"
+#include "geomTriangles.h"
 #include "pStatTimer.h"
 #include "pStatTimer.h"
 #include "cullBinManager.h"
 #include "cullBinManager.h"
 #include "configVariableInt.h"
 #include "configVariableInt.h"
@@ -115,6 +117,7 @@ PipeOcclusionCullTraverser(GraphicsOutput *host) {
   _internal_cull_handler = NULL;
   _internal_cull_handler = NULL;
 
 
   make_sphere();
   make_sphere();
+  make_box();
   make_solid_test_state();
   make_solid_test_state();
 
 
   _live = true;
   _live = true;
@@ -199,8 +202,8 @@ set_scene(SceneSetup *scene_setup, GraphicsStateGuardianBase *gsgbase) {
 
 
   _current_query = NULL;
   _current_query = NULL;
   _next_query = NULL;
   _next_query = NULL;
-
-  make_sphere();
+  
+  make_sphere();  // Temporary, so we can experiment with num vertices
 
 
   // Begin by rendering all the occluders into our internal scene.
   // Begin by rendering all the occluders into our internal scene.
   PStatTimer timer2(_draw_occlusion_pcollector);
   PStatTimer timer2(_draw_occlusion_pcollector);
@@ -433,7 +436,7 @@ make_sphere() {
   //  static const int num_stacks = 8;
   //  static const int num_stacks = 8;
 
 
   PT(GeomVertexData) vdata = new GeomVertexData
   PT(GeomVertexData) vdata = new GeomVertexData
-    ("occlusion", GeomVertexFormat::get_v3(), Geom::UH_static);
+    ("occlusion_sphere", GeomVertexFormat::get_v3(), Geom::UH_static);
   GeomVertexWriter vertex(vdata, InternalName::get_vertex());
   GeomVertexWriter vertex(vdata, InternalName::get_vertex());
   
   
   PT(GeomTristrips) strip = new GeomTristrips(Geom::UH_stream);
   PT(GeomTristrips) strip = new GeomTristrips(Geom::UH_stream);
@@ -474,6 +477,57 @@ compute_sphere_point(float latitude, float longitude) {
   return p;
   return p;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: PipeOcclusionCullTraverser::make_box
+//       Access: Private
+//  Description: Constructs a unit box for testing visibility of
+//               bounding boxes.
+////////////////////////////////////////////////////////////////////
+void PipeOcclusionCullTraverser::
+make_box() {
+  PT(GeomVertexData) vdata = new GeomVertexData
+    ("occlusion_box", GeomVertexFormat::get_v3(), Geom::UH_static);
+  GeomVertexWriter vertex(vdata, InternalName::get_vertex());
+  
+  vertex.add_data3f(0.0f, 0.0f, 0.0f);
+  vertex.add_data3f(0.0f, 0.0f, 1.0f);
+  vertex.add_data3f(0.0f, 1.0f, 0.0f);
+  vertex.add_data3f(0.0f, 1.0f, 1.0f);
+  vertex.add_data3f(1.0f, 0.0f, 0.0f);
+  vertex.add_data3f(1.0f, 0.0f, 1.0f);
+  vertex.add_data3f(1.0f, 1.0f, 0.0f);
+  vertex.add_data3f(1.0f, 1.0f, 1.0f);
+    
+  PT(GeomTriangles) tris = new GeomTriangles(Geom::UH_static);
+  tris->add_vertices(0, 4, 5);
+  tris->close_primitive();
+  tris->add_vertices(0, 5, 1);
+  tris->close_primitive();
+  tris->add_vertices(4, 6, 7);
+  tris->close_primitive();
+  tris->add_vertices(4, 7, 5);
+  tris->close_primitive();
+  tris->add_vertices(6, 2, 3);
+  tris->close_primitive();
+  tris->add_vertices(6, 3, 7);
+  tris->close_primitive();
+  tris->add_vertices(2, 0, 1);
+  tris->close_primitive();
+  tris->add_vertices(2, 1, 3);
+  tris->close_primitive();
+  tris->add_vertices(1, 5, 7);
+  tris->close_primitive();
+  tris->add_vertices(1, 7, 3);
+  tris->close_primitive();
+  tris->add_vertices(2, 6, 4);
+  tris->close_primitive();
+  tris->add_vertices(2, 4, 0);
+  tris->close_primitive();
+  
+  _box_geom = new Geom(vdata);
+  _box_geom->add_primitive(tris);
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PipeOcclusionCullTraverser::make_solid_test_state
 //     Function: PipeOcclusionCullTraverser::make_solid_test_state
 //       Access: Private
 //       Access: Private
@@ -514,7 +568,7 @@ get_volume_viz(const BoundingVolume *vol,
     return false;
     return false;
   }
   }
 
 
-  if (vol->is_of_type(BoundingSphere::get_class_type())) {
+  if (vol->is_exact_type(BoundingSphere::get_class_type())) {
     const BoundingSphere *sphere = DCAST(BoundingSphere, vol);
     const BoundingSphere *sphere = DCAST(BoundingSphere, vol);
     CPT(TransformState) local_transform = 
     CPT(TransformState) local_transform = 
       TransformState::make_pos_hpr_scale(sphere->get_center(),
       TransformState::make_pos_hpr_scale(sphere->get_center(),
@@ -530,14 +584,49 @@ get_volume_viz(const BoundingVolume *vol,
     // since it's intersecting the near plane.
     // since it's intersecting the near plane.
     const LPoint3f &center = modelview_transform->get_pos();
     const LPoint3f &center = modelview_transform->get_pos();
     const LVecBase3f &radius = modelview_transform->get_scale();
     const LVecBase3f &radius = modelview_transform->get_scale();
-    float max_radius = max(max(radius[0], radius[1]), radius[2]);
-    if (center[1] <= max_radius) {
+    if (center[1] - radius[1] < 0.0f) {
       return false;
       return false;
     }
     }
 
 
     // The sphere looks good.
     // The sphere looks good.
     geom = _sphere_geom;
     geom = _sphere_geom;
     return true;
     return true;
+
+  } else if (vol->is_exact_type(BoundingBox::get_class_type())) {
+    const BoundingBox *box = DCAST(BoundingBox, vol);
+    CPT(TransformState) local_transform = 
+      TransformState::make_pos_hpr_scale(box->get_minq(),
+                                         LVecBase3f(0, 0, 0),
+                                         box->get_maxq() - box->get_minq());
+    net_transform = net_transform->compose(local_transform);
+
+    modelview_transform = _internal_trav->get_world_transform()->compose(net_transform);
+
+    // See if the bounding box is clipped by the near plane.  If it
+    // is, the occlusion test may fail, so we won't bother performing
+    // it for this object.  Anyway, it's not occluded by anything,
+    // since it's intersecting the near plane.
+    static const LPoint3f points[8] = {
+      LPoint3f(0.0f, 0.0f, 0.0f),
+      LPoint3f(0.0f, 0.0f, 1.0f),
+      LPoint3f(0.0f, 1.0f, 0.0f),
+      LPoint3f(0.0f, 1.0f, 1.0f),
+      LPoint3f(1.0f, 0.0f, 0.0f),
+      LPoint3f(1.0f, 0.0f, 1.0f),
+      LPoint3f(1.0f, 1.0f, 0.0f),
+      LPoint3f(1.0f, 1.0f, 1.0f),
+    };
+    const LMatrix4f &mat = modelview_transform->get_mat();
+    for (int i = 0; i < 8; ++i) {
+      LPoint3f p = points[i] * mat;
+      if (p[1] < 0.0f) {
+        return false;
+      }
+    }
+
+    // The box looks good.
+    geom = _box_geom;
+    return true;
   }
   }
 
 
   // Don't have a suitable representation for this bounding volume.
   // Don't have a suitable representation for this bounding volume.

+ 3 - 0
panda/src/grutil/pipeOcclusionCullTraverser.h

@@ -74,6 +74,8 @@ protected:
 private:
 private:
   void make_sphere();
   void make_sphere();
   static Vertexf compute_sphere_point(float latitude, float longitude);
   static Vertexf compute_sphere_point(float latitude, float longitude);
+  void make_box();
+
   void make_solid_test_state();
   void make_solid_test_state();
 
 
   bool get_volume_viz(const BoundingVolume *vol, 
   bool get_volume_viz(const BoundingVolume *vol, 
@@ -111,6 +113,7 @@ private:
   PT(OcclusionQueryContext) _next_query;
   PT(OcclusionQueryContext) _next_query;
 
 
   PT(Geom) _sphere_geom;
   PT(Geom) _sphere_geom;
+  PT(Geom) _box_geom;
   CPT(RenderState) _solid_test_state;
   CPT(RenderState) _solid_test_state;
 
 
   class PendingObject {
   class PendingObject {

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

@@ -13,6 +13,7 @@
   #define SOURCES  \
   #define SOURCES  \
     boundingHexahedron.I boundingHexahedron.h boundingLine.I  \
     boundingHexahedron.I boundingHexahedron.h boundingLine.I  \
     boundingLine.h \
     boundingLine.h \
+    boundingBox.I boundingBox.h \
     boundingPlane.I boundingPlane.h \
     boundingPlane.I boundingPlane.h \
     boundingSphere.I boundingSphere.h  \
     boundingSphere.I boundingSphere.h  \
     boundingVolume.I boundingVolume.h config_mathutil.h  \
     boundingVolume.I boundingVolume.h config_mathutil.h  \
@@ -39,6 +40,7 @@
 
 
   #define INCLUDED_SOURCES \
   #define INCLUDED_SOURCES \
     boundingHexahedron.cxx boundingLine.cxx \
     boundingHexahedron.cxx boundingLine.cxx \
+    boundingBox.cxx \
     boundingPlane.cxx \
     boundingPlane.cxx \
     boundingSphere.cxx  \
     boundingSphere.cxx  \
     boundingVolume.cxx config_mathutil.cxx fftCompressor.cxx  \
     boundingVolume.cxx config_mathutil.cxx fftCompressor.cxx  \
@@ -59,6 +61,7 @@
   #define INSTALL_HEADERS \
   #define INSTALL_HEADERS \
     boundingHexahedron.I boundingHexahedron.h boundingLine.I \
     boundingHexahedron.I boundingHexahedron.h boundingLine.I \
     boundingLine.h \
     boundingLine.h \
+    boundingBox.I boundingBox.h \
     boundingPlane.I boundingPlane.h \
     boundingPlane.I boundingPlane.h \
     boundingSphere.I boundingSphere.h boundingVolume.I \
     boundingSphere.I boundingSphere.h boundingVolume.I \
     boundingVolume.h config_mathutil.h fftCompressor.h \
     boundingVolume.h config_mathutil.h fftCompressor.h \

+ 120 - 0
panda/src/mathutil/boundingBox.I

@@ -0,0 +1,120 @@
+// Filename: boundingBox.I
+// Created by:  drose (31May07)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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: BoundingBox::Constructor
+//       Access: Published
+//  Description: Constructs an empty box object.
+////////////////////////////////////////////////////////////////////
+INLINE_MATHUTIL BoundingBox::
+BoundingBox() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingBox::Constructor
+//       Access: Published
+//  Description: Constructs a specific box object.
+////////////////////////////////////////////////////////////////////
+INLINE_MATHUTIL BoundingBox::
+BoundingBox(const LPoint3f &min, const LPoint3f &max) :
+  _min(min), _max(max)
+{
+#ifdef NDEBUG
+  _flags = F_empty;
+  nassertv(!_min.is_nan() && !_max.is_nan());
+  nassertv(_min[0] <= _max[0] && _min[1] <= _max[1] && _min[2] <= _max[2]);
+#endif  // NDEBUG
+  _flags = 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingBox::get_minq
+//       Access: Public
+//  Description: An inline accessor for the minimum value.  get_min()
+//               would also work, but it is a virtual non-inline
+//               method.
+////////////////////////////////////////////////////////////////////
+INLINE_MATHUTIL const LPoint3f &BoundingBox::
+get_minq() const {
+  nassertr(!is_empty(), _min);
+  nassertr(!is_infinite(), _min);
+  return _min;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingBox::get_maxq
+//       Access: Public
+//  Description: An inline accessor for the maximum value.  get_max()
+//               would also work, but it is a virtual non-inline
+//               method.
+////////////////////////////////////////////////////////////////////
+INLINE_MATHUTIL const LPoint3f &BoundingBox::
+get_maxq() const {
+  nassertr(!is_empty(), _max);
+  nassertr(!is_infinite(), _max);
+  return _max;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingBox::get_num_points
+//       Access: Published
+//  Description: Returns 8: the number of vertices of a rectangular solid.
+////////////////////////////////////////////////////////////////////
+INLINE_MATHUTIL int BoundingBox::
+get_num_points() const {
+  return 8;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingBox::get_point
+//       Access: Published
+//  Description: Returns the nth vertex of the rectangular solid.
+////////////////////////////////////////////////////////////////////
+INLINE_MATHUTIL LPoint3f BoundingBox::
+get_point(int n) const {
+  nassertr(n >= 0 && n < 8, LPoint3f::zero());
+  
+  // We do some trickery assuming that _min and _max are consecutive
+  // in memory.
+  const LPoint3f *a = &_min;
+  return LPoint3f(a[(n>>2)&1][0], a[(n>>1)&1][1], a[(n)&1][2]);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingBox::get_num_planes
+//       Access: Published
+//  Description: Returns 6: the number of faces of a rectangular solid.
+////////////////////////////////////////////////////////////////////
+INLINE_MATHUTIL int BoundingBox::
+get_num_planes() const {
+  return 6;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingBox::get_plane
+//       Access: Published
+//  Description: Returns the nth face of the rectangular solid.
+////////////////////////////////////////////////////////////////////
+INLINE_MATHUTIL Planef BoundingBox::
+get_plane(int n) const {
+  nassertr(n >= 0 && n < 6, Planef());
+  return Planef(get_point(plane_def[n][0]),
+                get_point(plane_def[n][1]),
+                get_point(plane_def[n][2]));
+}

+ 643 - 0
panda/src/mathutil/boundingBox.cxx

@@ -0,0 +1,643 @@
+// Filename: boundingBox.cxx
+// Created by:  drose (31May07)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 "boundingBox.h"
+#include "boundingSphere.h"
+#include "boundingHexahedron.h"
+#include "boundingLine.h"
+#include "boundingPlane.h"
+#include "config_mathutil.h"
+#include "dcast.h"
+
+#include <math.h>
+#include <algorithm>
+
+const int BoundingBox::plane_def[6][3] = {
+  { 0, 4, 5 },
+  { 4, 6, 7 },
+  { 6, 2, 3 },
+  { 2, 0, 1 },
+  { 1, 5, 7 },
+  { 2, 6, 4 },
+};
+
+TypeHandle BoundingBox::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingBox::make_copy
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+BoundingVolume *BoundingBox::
+make_copy() const {
+  return new BoundingBox(*this);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingBox::get_min
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+LPoint3f BoundingBox::
+get_min() const {
+  nassertr(!is_empty(), _min);
+  nassertr(!is_infinite(), _min);
+  return _min;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingBox::get_max
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+LPoint3f BoundingBox::
+get_max() const {
+  nassertr(!is_empty(), _max);
+  nassertr(!is_infinite(), _max);
+  return _max;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingBox::get_volume
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+float BoundingBox::
+get_volume() const {
+  nassertr(!is_infinite(), 0.0f);
+  if (is_empty()) {
+    return 0.0f;
+  }
+
+  // Volume of a box: width x depth x height
+  return (_max[0] - _min[0]) * (_max[1] - _min[1]) * (_max[2] - _min[2]);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingBox::get_approx_center
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+LPoint3f BoundingBox::
+get_approx_center() const {
+  nassertr(!is_empty(), LPoint3f::zero());
+  nassertr(!is_infinite(), LPoint3f::zero());
+  return (_min + _max) * 0.5f;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingBox::xform
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void BoundingBox::
+xform(const LMatrix4f &mat) {
+  nassertv(!mat.is_nan());
+
+  if (!is_empty() && !is_infinite()) {
+    // We need to transform the eight corners of the cube, and then
+    // determine the new box.
+    LPoint3f x = get_point(0) * mat;
+    LPoint3f n = x;
+    for (int i = 1; i < 8; ++i) {
+      LPoint3f p = get_point(i) * mat;
+      n.set(min(n[0], p[0]), min(n[1], p[1]), min(n[2], p[2]));
+      x.set(max(x[0], p[0]), max(x[1], p[1]), max(x[2], p[2]));
+    }
+    _max = x;
+    _min = n;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingBox::output
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void BoundingBox::
+output(ostream &out) const {
+  if (is_empty()) {
+    out << "bbox, empty";
+  } else if (is_infinite()) {
+    out << "bbox, infinite";
+  } else {
+    out << "bbox, (" << _min << ") to (" << _max << ")";
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingBox::extend_other
+//       Access: Protected, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+bool BoundingBox::
+extend_other(BoundingVolume *other) const {
+  return other->extend_by_box(this);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingBox::around_other
+//       Access: Protected, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+bool BoundingBox::
+around_other(BoundingVolume *other,
+             const BoundingVolume **first,
+             const BoundingVolume **last) const {
+  return other->around_boxes(first, last);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingBox::contains_other
+//       Access: Protected, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+int BoundingBox::
+contains_other(const BoundingVolume *other) const {
+  return other->contains_box(this);
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingBox::extend_by_point
+//       Access: Protected, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+bool BoundingBox::
+extend_by_point(const LPoint3f &point) {
+  nassertr(!point.is_nan(), false);
+
+  if (is_empty()) {
+    _min = point;
+    _max = point;
+    _flags = 0;
+
+  } else if (!is_infinite()) {
+    _min.set(min(_min[0], point[0]), min(_min[1], point[1]), min(_min[2], point[2]));
+    _max.set(max(_max[0], point[0]), max(_max[1], point[1]), max(_max[2], point[2]));
+  }
+
+  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
+//  Description: 
+////////////////////////////////////////////////////////////////////
+bool BoundingBox::
+extend_by_box(const BoundingBox *box) {
+  nassertr(!box->is_empty() && !box->is_infinite(), false);
+  nassertr(!is_infinite(), false);
+
+  if (is_empty()) {
+    _min = box->_min;
+    _max = box->_max;
+    _flags = 0;
+
+  } else {
+    _min.set(min(_min[0], box->_min[0]), 
+             min(_min[1], box->_min[1]), 
+             min(_min[2], box->_min[2]));
+    _max.set(max(_max[0], box->_max[0]), 
+             max(_max[1], box->_max[1]), 
+             max(_max[2], box->_max[2]));
+  }
+  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
+//  Description: 
+////////////////////////////////////////////////////////////////////
+bool BoundingBox::
+extend_by_finite(const FiniteBoundingVolume *volume) {
+  nassertr(!volume->is_empty(), false);
+
+  LVector3f min1 = volume->get_min();
+  LVector3f max1 = volume->get_max();
+
+  if (is_empty()) {
+    _min = min1;
+    _max = max1;
+    _flags = 0;
+
+  } else {
+    _min.set(min(_min[0], min1[0]), 
+             min(_min[1], min1[1]), 
+             min(_min[2], min1[2]));
+    _max.set(max(_max[0], max1[0]), 
+             max(_max[1], max1[1]), 
+             max(_max[2], max1[2]));
+  }
+
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingBox::around_points
+//       Access: Protected, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+bool BoundingBox::
+around_points(const LPoint3f *first, const LPoint3f *last) {
+  nassertr(first != last, false);
+
+  // Get the minmax of all the points to construct a bounding box.
+  const LPoint3f *p = first;
+
+#ifndef NDEBUG
+  // Skip any NaN points.
+  int skipped_nan = 0;
+  while (p != last && (*p).is_nan()) {
+    ++p;
+    ++skipped_nan;
+  }
+  if (p == last) {
+    mathutil_cat.warning()
+      << "BoundingBox around NaN\n";
+    return false;
+  }
+#endif
+
+  _min = *p;
+  _max = *p;
+  ++p;
+
+#ifndef NDEBUG
+  // Skip more NaN points.
+  while (p != last && (*p).is_nan()) {
+    ++p;
+    ++skipped_nan;
+  }
+#endif
+
+  while (p != last) {
+#ifndef NDEBUG
+    // Skip more NaN points.
+    if ((*p).is_nan()) {
+      ++skipped_nan;
+    } else
+#endif
+      {
+        _min.set(min(_min[0], (*p)[0]),
+                 min(_min[1], (*p)[1]),
+                 min(_min[2], (*p)[2]));
+        _max.set(max(_max[0], (*p)[0]),
+                 max(_max[1], (*p)[1]),
+                 max(_max[2], (*p)[2]));
+      }
+    ++p;
+  }
+
+#ifndef NDEBUG
+  if (skipped_nan != 0) {
+    mathutil_cat.warning()
+      << "BoundingBox ignored " << skipped_nan << " NaN points of "
+      << (last - first) << " total.\n";
+  }
+#endif
+
+  _flags = 0;
+
+  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
+//  Description: 
+////////////////////////////////////////////////////////////////////
+bool BoundingBox::
+around_finite(const BoundingVolume **first,
+              const BoundingVolume **last) {
+  nassertr(first != last, false);
+
+  // We're given a set of bounding volumes, at least the first one of
+  // which is guaranteed to be finite and nonempty.  Some others may
+  // not be.
+
+  // First, get the box of all the points to construct a bounding
+  // box.
+  const BoundingVolume **p = first;
+  nassertr(!(*p)->is_empty() && !(*p)->is_infinite(), false);
+  const FiniteBoundingVolume *vol = DCAST(FiniteBoundingVolume, *p);
+  _min = vol->get_min();
+  _max = vol->get_max();
+  
+  for (++p; p != last; ++p) {
+    nassertr(!(*p)->is_infinite(), false);
+    if (!(*p)->is_empty()) {
+      const FiniteBoundingVolume *vol = DCAST(FiniteBoundingVolume, *p);
+      LPoint3f min1 = vol->get_min();
+      LPoint3f max1 = vol->get_max();
+      _min.set(min(_min[0], min1[0]),
+               min(_min[1], min1[1]),
+               min(_min[2], min1[2]));
+      _max.set(max(_max[0], max1[0]),
+               max(_max[1], max1[1]),
+               max(_max[2], max1[2]));
+    }
+  }
+
+  _flags = 0;
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingBox::contains_point
+//       Access: Protected, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+int BoundingBox::
+contains_point(const LPoint3f &point) const {
+  nassertr(!point.is_nan(), IF_no_intersection);
+
+  if (is_empty()) {
+    return IF_no_intersection;
+
+  } else if (is_infinite()) {
+    return IF_possible | IF_some | IF_all;
+
+  } else {
+    if (point[0] >= _min[0] && point[0] <= _max[0] &&
+        point[1] >= _min[1] && point[1] <= _max[1] &&
+        point[2] >= _min[2] && point[2] <= _max[2]) {
+      return IF_possible | IF_some | IF_all;
+    } else {
+      return IF_no_intersection;
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingBox::contains_lineseg
+//       Access: Protected, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+int BoundingBox::
+contains_lineseg(const LPoint3f &a, const LPoint3f &b) const {
+  nassertr(!a.is_nan() && !b.is_nan(), IF_no_intersection);
+
+  if (a == b) {
+    return contains_point(a);
+  }
+  if (is_empty()) {
+    return IF_no_intersection;
+
+  } else if (is_infinite()) {
+    return IF_possible | IF_some | IF_all;
+
+  } else {
+    // Set a bit for each plane a and b are on the wrong side of.
+    unsigned int a_bits = 0;
+
+    if (a[0] < _min[0]) {
+      a_bits |= 0x01;
+    } else if (a[0] > _max[0]) {
+      a_bits |= 0x02;
+    }
+
+    if (a[1] < _min[1]) {
+      a_bits |= 0x04;
+    } else if (a[1] > _max[1]) {
+      a_bits |= 0x08;
+    }
+
+    if (a[2] < _min[2]) {
+      a_bits |= 0x10;
+    } else if (a[2] > _max[2]) {
+      a_bits |= 0x20;
+    }
+
+    unsigned int b_bits = 0;
+
+    if (b[0] < _min[0]) {
+      b_bits |= 0x01;
+    } else if (b[0] > _max[0]) {
+      b_bits |= 0x02;
+    }
+
+    if (b[1] < _min[1]) {
+      b_bits |= 0x04;
+    } else if (b[1] > _max[1]) {
+      b_bits |= 0x08;
+    }
+
+    if (b[2] < _min[2]) {
+      b_bits |= 0x10;
+    } else if (b[2] > _max[2]) {
+      b_bits |= 0x20;
+    }
+
+    if ((a_bits & b_bits) != 0) {
+      // If there are any bits in common, the segment is wholly
+      // outside the box (both points are on the wrong side of the
+      // same plane).
+      return IF_no_intersection;
+
+    } else if ((a_bits | b_bits) == 0) {
+      // If there are no bits at all, the segment is wholly within the
+      // box.
+      return IF_possible | IF_some | IF_all;
+
+    } else if (a_bits == 0 || b_bits == 0) {
+      // If either point is within the box, the segment is partially
+      // within the box.
+      return IF_possible | IF_some;
+
+    } else {
+      unsigned int differ = (a_bits ^ b_bits);
+      if (differ == 0x03 || differ == 0x0c || differ == 0x30) {
+        // If the line segment stretches straight across the box, the
+        // segment is partially within.
+        return IF_possible | IF_some;
+
+      } else {
+        // Otherwise, it's hard to tell whether it does or doesn't.
+        return IF_possible;
+      }
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     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
+//  Description: Double-dispatch support: called by contains_other()
+//               when the type we're testing for intersection is known
+//               to be a box.
+////////////////////////////////////////////////////////////////////
+int BoundingBox::
+contains_box(const BoundingBox *box) const {
+  nassertr(!is_empty() && !is_infinite(), 0);
+  nassertr(!box->is_empty() && !box->is_infinite(), 0);
+
+  const LPoint3f &min1 = box->get_minq();
+  const LPoint3f &max1 = box->get_maxq();
+  
+  if (min1[0] >= _min[0] && max1[0] <= _max[0] &&
+      min1[1] >= _min[1] && max1[1] <= _max[1] &&
+      min1[2] >= _min[2] && max1[2] <= _max[2]) {
+    // The other volume is completely within this volume.
+    return IF_possible | IF_some | IF_all;
+
+  } else if (max1[0] >= _min[0] && min1[0] <= _max[0] &&
+             max1[1] >= _min[1] && min1[1] <= _max[1] &&
+             max1[2] >= _min[2] && min1[2] <= _max[2]) {
+    // The other volume is partially within this volume.
+    return IF_possible;
+
+  } else {
+    // The other volume is not within this volume.
+    return IF_no_intersection;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingBox::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 BoundingBox::
+contains_hexahedron(const BoundingHexahedron *hexahedron) const {
+  return contains_finite(hexahedron);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingBox::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 BoundingBox::
+contains_line(const BoundingLine *line) const {
+  return line->contains_box(this) & ~IF_all;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingBox::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 BoundingBox::
+contains_plane(const BoundingPlane *plane) const {
+  return plane->contains_box(this) & ~IF_all;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingBox::contains_finite
+//       Access: Protected
+//  Description: 
+////////////////////////////////////////////////////////////////////
+int BoundingBox::
+contains_finite(const FiniteBoundingVolume *volume) const {
+  nassertr(!is_empty() && !is_infinite(), 0);
+  nassertr(!volume->is_empty() && !volume->is_infinite(), 0);
+
+  LPoint3f min1 = volume->get_min();
+  LPoint3f max1 = volume->get_max();
+  
+  if (min1[0] >= _min[0] && max1[0] <= _max[0] &&
+      min1[1] >= _min[1] && max1[1] <= _max[1] &&
+      min1[2] >= _min[2] && max1[2] <= _max[2]) {
+    // The other volume is completely within this volume.
+    return IF_possible | IF_some | IF_all;
+
+  } else if (max1[0] >= _min[0] && min1[0] <= _max[0] &&
+             max1[1] >= _min[1] && min1[1] <= _max[1] &&
+             max1[2] >= _min[2] && min1[2] <= _max[2]) {
+    // The other volume is partially within this volume.
+    return IF_possible;
+
+  } else {
+    // The other volume is not within this volume.
+    return IF_no_intersection;
+  }
+}

+ 126 - 0
panda/src/mathutil/boundingBox.h

@@ -0,0 +1,126 @@
+// Filename: boundingBox.h
+// Created by:  drose (31May07)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 BOUNDINGBOX_H
+#define BOUNDINGBOX_H
+
+#include "pandabase.h"
+
+#include "finiteBoundingVolume.h"
+#include "plane.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : BoundingBox
+// Description : An axis-aligned bounding box; that is, a minimum and
+//               maximum coordinate triple.
+//
+//               This box is always axis-aligned.  If you need a more
+//               general bounding box, try BoundingHexahedron.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA BoundingBox : public FiniteBoundingVolume {
+PUBLISHED:
+  INLINE_MATHUTIL BoundingBox();
+  INLINE_MATHUTIL BoundingBox(const LPoint3f &min, const LPoint3f &max);
+  ALLOC_DELETED_CHAIN(BoundingBox);
+
+public:
+  virtual BoundingVolume *make_copy() const;
+
+  virtual LPoint3f get_min() const;
+  virtual LPoint3f get_max() const;
+  virtual float get_volume() const;
+
+  virtual LPoint3f get_approx_center() const;
+  virtual void xform(const LMatrix4f &mat);
+
+  virtual void output(ostream &out) const;
+
+PUBLISHED:
+  INLINE_MATHUTIL int get_num_points() const;
+  INLINE_MATHUTIL LPoint3f get_point(int n) const;
+  INLINE_MATHUTIL int get_num_planes() const;
+  INLINE_MATHUTIL Planef get_plane(int n) const;
+
+public:
+  // Inline accessors for speed.
+  INLINE_MATHUTIL const LPoint3f &get_minq() const;
+  INLINE_MATHUTIL const LPoint3f &get_maxq() const;
+
+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_point(const LPoint3f &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 around_points(const LPoint3f *first,
+                             const LPoint3f *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 int contains_point(const LPoint3f &point) const;
+  virtual int contains_lineseg(const LPoint3f &a, const LPoint3f &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;
+
+private:
+  LPoint3f _min;
+  LPoint3f _max;
+
+  static const int plane_def[6][3];
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    FiniteBoundingVolume::init_type();
+    register_type(_type_handle, "BoundingBox",
+                  FiniteBoundingVolume::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 BoundingSphere;
+};
+
+#include "boundingBox.I"
+
+#endif

+ 27 - 1
panda/src/mathutil/boundingHexahedron.I

@@ -16,26 +16,52 @@
 //
 //
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 
 
+
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingHexahedron::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
 INLINE_MATHUTIL BoundingHexahedron::
 INLINE_MATHUTIL BoundingHexahedron::
 BoundingHexahedron() {
 BoundingHexahedron() {
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingHexahedron::get_num_points
+//       Access: Published
+//  Description: Returns 8: the number of vertices of a hexahedron.
+////////////////////////////////////////////////////////////////////
 INLINE_MATHUTIL int BoundingHexahedron::
 INLINE_MATHUTIL int BoundingHexahedron::
 get_num_points() const {
 get_num_points() const {
   return num_points;
   return num_points;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingHexahedron::get_point
+//       Access: Published
+//  Description: Returns the nth vertex of the hexahedron.
+////////////////////////////////////////////////////////////////////
 INLINE_MATHUTIL LPoint3f BoundingHexahedron::
 INLINE_MATHUTIL LPoint3f BoundingHexahedron::
 get_point(int n) const {
 get_point(int n) const {
-  nassertr(n >= 0 && n < num_points, LPoint3f(0.0f, 0.0f, 0.0f));
+  nassertr(n >= 0 && n < num_points, LPoint3f::zero());
   return _points[n];
   return _points[n];
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingHexahedron::get_num_planes
+//       Access: Published
+//  Description: Returns 6: the number of faces of a hexahedron.
+////////////////////////////////////////////////////////////////////
 INLINE_MATHUTIL int BoundingHexahedron::
 INLINE_MATHUTIL int BoundingHexahedron::
 get_num_planes() const {
 get_num_planes() const {
   return num_planes;
   return num_planes;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingHexahedron::get_plane
+//       Access: Published
+//  Description: Returns the nth face of the hexahedron.
+////////////////////////////////////////////////////////////////////
 INLINE_MATHUTIL Planef BoundingHexahedron::
 INLINE_MATHUTIL Planef BoundingHexahedron::
 get_plane(int n) const {
 get_plane(int n) const {
   nassertr(n >= 0 && n < num_planes, Planef());
   nassertr(n >= 0 && n < num_planes, Planef());

+ 172 - 62
panda/src/mathutil/boundingHexahedron.cxx

@@ -18,6 +18,7 @@
 
 
 #include "boundingHexahedron.h"
 #include "boundingHexahedron.h"
 #include "boundingSphere.h"
 #include "boundingSphere.h"
+#include "boundingBox.h"
 #include "config_mathutil.h"
 #include "config_mathutil.h"
 
 
 #include <math.h>
 #include <math.h>
@@ -25,6 +26,11 @@
 
 
 TypeHandle BoundingHexahedron::_type_handle;
 TypeHandle BoundingHexahedron::_type_handle;
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingHexahedron::Constructor
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
 BoundingHexahedron::
 BoundingHexahedron::
 BoundingHexahedron(const Frustumf &frustum, bool is_ortho,
 BoundingHexahedron(const Frustumf &frustum, bool is_ortho,
                    CoordinateSystem cs) {
                    CoordinateSystem cs) {
@@ -60,6 +66,11 @@ BoundingHexahedron(const Frustumf &frustum, bool is_ortho,
   }
   }
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingHexahedron::Constructor
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
 BoundingHexahedron::
 BoundingHexahedron::
 BoundingHexahedron(const LPoint3f &fll, const LPoint3f &flr,
 BoundingHexahedron(const LPoint3f &fll, const LPoint3f &flr,
                    const LPoint3f &fur, const LPoint3f &ful,
                    const LPoint3f &fur, const LPoint3f &ful,
@@ -79,11 +90,21 @@ BoundingHexahedron(const LPoint3f &fll, const LPoint3f &flr,
   set_planes();
   set_planes();
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingHexahedron::make_copy
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
 BoundingVolume *BoundingHexahedron::
 BoundingVolume *BoundingHexahedron::
 make_copy() const {
 make_copy() const {
   return new BoundingHexahedron(*this);
   return new BoundingHexahedron(*this);
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingHexahedron::get_min
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
 LPoint3f BoundingHexahedron::
 LPoint3f BoundingHexahedron::
 get_min() const {
 get_min() const {
   nassertr(!is_empty(), LPoint3f(0.0f, 0.0f, 0.0f));
   nassertr(!is_empty(), LPoint3f(0.0f, 0.0f, 0.0f));
@@ -98,6 +119,11 @@ get_min() const {
   return m;
   return m;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingHexahedron::get_max
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
 LPoint3f BoundingHexahedron::
 LPoint3f BoundingHexahedron::
 get_max() const {
 get_max() const {
   nassertr(!is_empty(), LPoint3f(0.0f, 0.0f, 0.0f));
   nassertr(!is_empty(), LPoint3f(0.0f, 0.0f, 0.0f));
@@ -112,6 +138,11 @@ get_max() const {
   return m;
   return m;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingHexahedron::get_approx_center
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
 LPoint3f BoundingHexahedron::
 LPoint3f BoundingHexahedron::
 get_approx_center() const {
 get_approx_center() const {
   nassertr(!is_empty(), LPoint3f(0.0f, 0.0f, 0.0f));
   nassertr(!is_empty(), LPoint3f(0.0f, 0.0f, 0.0f));
@@ -119,6 +150,11 @@ get_approx_center() const {
   return _centroid;
   return _centroid;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingHexahedron::xform
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
 void BoundingHexahedron::
 void BoundingHexahedron::
 xform(const LMatrix4f &mat) {
 xform(const LMatrix4f &mat) {
   if (!is_empty() && !is_infinite()) {
   if (!is_empty() && !is_infinite()) {
@@ -130,6 +166,11 @@ xform(const LMatrix4f &mat) {
   }
   }
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingHexahedron::output
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
 void BoundingHexahedron::
 void BoundingHexahedron::
 output(ostream &out) const {
 output(ostream &out) const {
   if (is_empty()) {
   if (is_empty()) {
@@ -141,6 +182,11 @@ output(ostream &out) const {
   }
   }
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingHexahedron::write
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
 void BoundingHexahedron::
 void BoundingHexahedron::
 write(ostream &out, int indent_level) const {
 write(ostream &out, int indent_level) const {
   if (is_empty()) {
   if (is_empty()) {
@@ -158,11 +204,21 @@ write(ostream &out, int indent_level) const {
   }
   }
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingHexahedron::extend_other
+//       Access: Protected, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
 bool BoundingHexahedron::
 bool BoundingHexahedron::
 extend_other(BoundingVolume *other) const {
 extend_other(BoundingVolume *other) const {
   return other->extend_by_hexahedron(this);
   return other->extend_by_hexahedron(this);
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingHexahedron::around_other
+//       Access: Protected, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
 bool BoundingHexahedron::
 bool BoundingHexahedron::
 around_other(BoundingVolume *other,
 around_other(BoundingVolume *other,
              const BoundingVolume **first,
              const BoundingVolume **first,
@@ -170,56 +226,21 @@ around_other(BoundingVolume *other,
   return other->around_hexahedrons(first, last);
   return other->around_hexahedrons(first, last);
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingHexahedron::contains_other
+//       Access: Protected, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
 int BoundingHexahedron::
 int BoundingHexahedron::
 contains_other(const BoundingVolume *other) const {
 contains_other(const BoundingVolume *other) const {
   return other->contains_hexahedron(this);
   return other->contains_hexahedron(this);
 }
 }
 
 
-
-bool BoundingHexahedron::
-extend_by_point(const LPoint3f &) {
-  mathutil_cat.error()
-    << "BoundingHexahedron::extend_by_point() called\n";
-  return false;
-}
-
-bool BoundingHexahedron::
-extend_by_sphere(const BoundingSphere *) {
-  mathutil_cat.error()
-    << "BoundingHexahedron::extend_by_sphere() called\n";
-  return false;
-}
-
-bool BoundingHexahedron::
-extend_by_hexahedron(const BoundingHexahedron *) {
-  mathutil_cat.error()
-    << "BoundingHexahedron::extend_by_hexahedron() called\n";
-  return false;
-}
-
-bool BoundingHexahedron::
-around_points(const LPoint3f *, const LPoint3f *) {
-  mathutil_cat.error()
-    << "BoundingHexahedron::around_points() called\n";
-  return false;
-}
-
-bool BoundingHexahedron::
-around_spheres(const BoundingVolume **,
-               const BoundingVolume **) {
-  mathutil_cat.error()
-    << "BoundingHexahedron::around_spheres() called\n";
-  return false;
-}
-
-bool BoundingHexahedron::
-around_hexahedrons(const BoundingVolume **,
-                   const BoundingVolume **) {
-  mathutil_cat.error()
-    << "BoundingHexahedron::around_hexahedrons() called\n";
-  return false;
-}
-
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingHexahedron::contains_point
+//       Access: Protected, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
 int BoundingHexahedron::
 int BoundingHexahedron::
 contains_point(const LPoint3f &point) const {
 contains_point(const LPoint3f &point) const {
   if (is_empty()) {
   if (is_empty()) {
@@ -241,6 +262,11 @@ contains_point(const LPoint3f &point) const {
   }
   }
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingHexahedron::contains_lineseg
+//       Access: Protected, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
 int BoundingHexahedron::
 int BoundingHexahedron::
 contains_lineseg(const LPoint3f &a, const LPoint3f &b) const {
 contains_lineseg(const LPoint3f &a, const LPoint3f &b) const {
   if (is_empty()) {
   if (is_empty()) {
@@ -267,6 +293,11 @@ contains_lineseg(const LPoint3f &a, const LPoint3f &b) const {
   }
   }
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingHexahedron::contains_sphere
+//       Access: Protected, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
 int BoundingHexahedron::
 int BoundingHexahedron::
 contains_sphere(const BoundingSphere *sphere) const {
 contains_sphere(const BoundingSphere *sphere) const {
   nassertr(!is_empty(), 0);
   nassertr(!is_empty(), 0);
@@ -297,37 +328,111 @@ contains_sphere(const BoundingSphere *sphere) const {
   return result;
   return result;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingHexahedron::contains_box
+//       Access: Protected, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+int BoundingHexahedron::
+contains_box(const BoundingBox *box) const {
+  nassertr(!is_empty(), 0);
+  nassertr(!box->is_empty(), 0);
+
+  // Put the box inside a sphere for the purpose of this test.
+  const LPoint3f &min = box->get_minq();
+  const LPoint3f &max = box->get_maxq();
+  LPoint3f center = (min + max) * 0.5f;
+  float radius2 = (max - center).length_squared();
+
+  int result = IF_possible | IF_some | IF_all;
+
+  for (int i = 0; i < num_planes; i++) {
+    const Planef &p = _planes[i];
+    float dist = p.dist_to_plane(center);
+    float dist2 = dist * dist;
+
+    if (dist >= 0.0f && dist2 > radius2) {
+      // The sphere is completely in front of this plane; it's thus
+      // completely outside of the hexahedron.
+      return IF_no_intersection;
+
+    } else if (dist < 0.0f && dist2 < radius2) {
+      // The sphere is not completely behind this plane, but some of
+      // it is.
+
+      // Look a little closer.
+      bool all_in = true;
+      for (int i = 0; i < 8 && all_in; ++i) {
+        if (p.dist_to_plane(box->get_point(i)) < 0.0f) {
+          // This point is outside the plane.
+          all_in = false;
+        }
+      }
+
+      if (!all_in) {
+        result &= ~IF_all;
+      }
+    }
+  }
+
+  return result;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingHexahedron::contains_hexahedron
+//       Access: Protected, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
 int BoundingHexahedron::
 int BoundingHexahedron::
 contains_hexahedron(const BoundingHexahedron *hexahedron) const {
 contains_hexahedron(const BoundingHexahedron *hexahedron) const {
   nassertr(!is_empty(), 0);
   nassertr(!is_empty(), 0);
   nassertr(!hexahedron->is_empty(), 0);
   nassertr(!hexahedron->is_empty(), 0);
 
 
-  // Check minmax.
-  LPoint3f min1 = get_min();
-  LPoint3f min2 = hexahedron->get_min();
-  LPoint3f max1 = get_max();
-  LPoint3f max2 = hexahedron->get_max();
+  // Put the hexahedron inside a sphere for the purposes of this test.
+  LPoint3f min = hexahedron->get_min();
+  LPoint3f max = hexahedron->get_max();
+  LPoint3f center = (min + max) * 0.5f;
+  float radius2 = (max - center).length_squared();
 
 
-  if (min1[0] > max2[0] || min1[1] > max2[1] || min1[2] > max2[2] ||
-      min2[0] > max1[0] || min2[1] > max1[1] || min2[2] > max1[2] ||
-      max1[0] < min2[0] || max1[1] < min2[1] || max1[2] < min2[2] ||
-      max2[0] < min1[0] || max2[1] < min1[1] || max2[2] < min1[2]) {
-    return IF_no_intersection;
-  }
+  int result = IF_possible | IF_some | IF_all;
 
 
-  int result = IF_possible | IF_all;
+  for (int i = 0; i < num_planes; i++) {
+    const Planef &p = _planes[i];
+    float dist = p.dist_to_plane(center);
+    float dist2 = dist * dist;
 
 
-  for (int i = 0; i < num_points; i++) {
-    if (contains_point(hexahedron->_points[i])) {
-      result |= IF_some;
-    } else {
-      result &= ~IF_all;
+    if (dist >= 0.0f && dist2 > radius2) {
+      // The sphere is completely in front of this plane; it's thus
+      // completely outside of the hexahedron.
+      return IF_no_intersection;
+
+    } else if (dist < 0.0f && dist2 < radius2) {
+      // The sphere is not completely behind this plane, but some of
+      // it is.
+
+      // Look a little closer.
+      bool all_in = true;
+      for (int i = 0; i < 8 && all_in; ++i) {
+        if (p.dist_to_plane(hexahedron->get_point(i)) < 0.0f) {
+          // This point is outside the plane.
+          all_in = false;
+        }
+      }
+
+      if (!all_in) {
+        result &= ~IF_all;
+      }
     }
     }
   }
   }
 
 
   return result;
   return result;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingHexahedron::set_planes
+//       Access: Private
+//  Description: 
+////////////////////////////////////////////////////////////////////
 void BoundingHexahedron::
 void BoundingHexahedron::
 set_planes() {
 set_planes() {
   _planes[0] = Planef(_points[0], _points[3], _points[2]);
   _planes[0] = Planef(_points[0], _points[3], _points[2]);
@@ -358,6 +463,11 @@ set_planes() {
   }
   }
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingHexahedron::set_centroid
+//       Access: Private
+//  Description: 
+////////////////////////////////////////////////////////////////////
 void BoundingHexahedron::
 void BoundingHexahedron::
 set_centroid() {
 set_centroid() {
   LPoint3f net = _points[0];
   LPoint3f net = _points[0];

+ 10 - 17
panda/src/mathutil/boundingHexahedron.h

@@ -32,21 +32,24 @@
 //       Class : BoundingHexahedron
 //       Class : BoundingHexahedron
 // Description : This defines a bounding convex hexahedron.  It is
 // Description : This defines a bounding convex hexahedron.  It is
 //               typically used to represent a frustum, but may
 //               typically used to represent a frustum, but may
-//               represent any enclosing convex hexahedron.
-//
-//               This class does not support any of the around() or
-//               extend_by() functions, but all other functionality
-//               should be well-defined.
+//               represent any enclosing convex hexahedron, including
+//               simple boxes.  However, if all you want is an
+//               axis-aligned bounding box, you may be better off with
+//               the simpler BoundingBox class.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 class EXPCL_PANDA BoundingHexahedron : public FiniteBoundingVolume {
 class EXPCL_PANDA BoundingHexahedron : public FiniteBoundingVolume {
 public:
 public:
   INLINE_MATHUTIL BoundingHexahedron();
   INLINE_MATHUTIL BoundingHexahedron();
+
+PUBLISHED:
   BoundingHexahedron(const Frustumf &frustum, bool is_ortho,
   BoundingHexahedron(const Frustumf &frustum, bool is_ortho,
                      CoordinateSystem cs = CS_default);
                      CoordinateSystem cs = CS_default);
   BoundingHexahedron(const LPoint3f &fll, const LPoint3f &flr,
   BoundingHexahedron(const LPoint3f &fll, const LPoint3f &flr,
                      const LPoint3f &fur, const LPoint3f &ful,
                      const LPoint3f &fur, const LPoint3f &ful,
                      const LPoint3f &nll, const LPoint3f &nlr,
                      const LPoint3f &nll, const LPoint3f &nlr,
                      const LPoint3f &nur, const LPoint3f &nul);
                      const LPoint3f &nur, const LPoint3f &nul);
+
+public:
   ALLOC_DELETED_CHAIN(BoundingHexahedron);
   ALLOC_DELETED_CHAIN(BoundingHexahedron);
   virtual BoundingVolume *make_copy() const;
   virtual BoundingVolume *make_copy() const;
 
 
@@ -59,6 +62,7 @@ public:
   virtual void output(ostream &out) const;
   virtual void output(ostream &out) const;
   virtual void write(ostream &out, int indent_level = 0) const;
   virtual void write(ostream &out, int indent_level = 0) const;
 
 
+PUBLISHED:
   INLINE_MATHUTIL int get_num_points() const;
   INLINE_MATHUTIL int get_num_points() const;
   INLINE_MATHUTIL LPoint3f get_point(int n) const;
   INLINE_MATHUTIL LPoint3f get_point(int n) const;
   INLINE_MATHUTIL int get_num_planes() const;
   INLINE_MATHUTIL int get_num_planes() const;
@@ -71,21 +75,10 @@ protected:
                             const BoundingVolume **last) const;
                             const BoundingVolume **last) const;
   virtual int contains_other(const BoundingVolume *other) const;
   virtual int contains_other(const BoundingVolume *other) const;
 
 
-
-  virtual bool extend_by_point(const LPoint3f &point);
-  virtual bool extend_by_sphere(const BoundingSphere *sphere);
-  virtual bool extend_by_hexahedron(const BoundingHexahedron *hexahedron);
-
-  virtual bool around_points(const LPoint3f *first,
-                             const LPoint3f *last);
-  virtual bool around_spheres(const BoundingVolume **first,
-                              const BoundingVolume **last);
-  virtual bool around_hexahedrons(const BoundingVolume **first,
-                                  const BoundingVolume **last);
-
   virtual int contains_point(const LPoint3f &point) const;
   virtual int contains_point(const LPoint3f &point) const;
   virtual int contains_lineseg(const LPoint3f &a, const LPoint3f &b) const;
   virtual int contains_lineseg(const LPoint3f &a, const LPoint3f &b) const;
   virtual int contains_sphere(const BoundingSphere *sphere) 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_hexahedron(const BoundingHexahedron *hexahedron) const;
 
 
 private:
 private:

+ 21 - 0
panda/src/mathutil/boundingLine.I

@@ -16,10 +16,21 @@
 //
 //
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 
 
+
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingLine::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
 INLINE_MATHUTIL BoundingLine::
 INLINE_MATHUTIL BoundingLine::
 BoundingLine() {
 BoundingLine() {
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingLine::Constructor
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
 INLINE_MATHUTIL BoundingLine::
 INLINE_MATHUTIL BoundingLine::
 BoundingLine(const LPoint3f &a, const LPoint3f &b) :
 BoundingLine(const LPoint3f &a, const LPoint3f &b) :
   _origin(a), _vector(b - a)
   _origin(a), _vector(b - a)
@@ -31,6 +42,11 @@ BoundingLine(const LPoint3f &a, const LPoint3f &b) :
   }
   }
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingLine::get_point_a
+//       Access: Published
+//  Description: Returns the first point that defines the line.
+////////////////////////////////////////////////////////////////////
 INLINE_MATHUTIL const LPoint3f &BoundingLine::
 INLINE_MATHUTIL const LPoint3f &BoundingLine::
 get_point_a() const {
 get_point_a() const {
   nassertr(!is_empty(), _origin);
   nassertr(!is_empty(), _origin);
@@ -38,6 +54,11 @@ get_point_a() const {
   return _origin;
   return _origin;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingLine::get_point_b
+//       Access: Published
+//  Description: Returns the second point that defines the line.
+////////////////////////////////////////////////////////////////////
 INLINE_MATHUTIL LPoint3f BoundingLine::
 INLINE_MATHUTIL LPoint3f BoundingLine::
 get_point_b() const {
 get_point_b() const {
   nassertr(!is_empty(), _origin);
   nassertr(!is_empty(), _origin);

+ 70 - 1
panda/src/mathutil/boundingLine.cxx

@@ -24,11 +24,21 @@
 
 
 TypeHandle BoundingLine::_type_handle;
 TypeHandle BoundingLine::_type_handle;
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingLine::make_copy
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
 BoundingVolume *BoundingLine::
 BoundingVolume *BoundingLine::
 make_copy() const {
 make_copy() const {
   return new BoundingLine(*this);
   return new BoundingLine(*this);
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingLine::get_approx_center
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
 LPoint3f BoundingLine::
 LPoint3f BoundingLine::
 get_approx_center() const {
 get_approx_center() const {
   nassertr(!is_empty(), LPoint3f(0.0f, 0.0f, 0.0f));
   nassertr(!is_empty(), LPoint3f(0.0f, 0.0f, 0.0f));
@@ -36,6 +46,11 @@ get_approx_center() const {
   return (get_point_a() + get_point_b()) / 2.0;
   return (get_point_a() + get_point_b()) / 2.0;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingLine::xform
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
 void BoundingLine::
 void BoundingLine::
 xform(const LMatrix4f &mat) {
 xform(const LMatrix4f &mat) {
   nassertv(!mat.is_nan());
   nassertv(!mat.is_nan());
@@ -51,6 +66,11 @@ xform(const LMatrix4f &mat) {
   }
   }
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingLine::output
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
 void BoundingLine::
 void BoundingLine::
 output(ostream &out) const {
 output(ostream &out) const {
   if (is_empty()) {
   if (is_empty()) {
@@ -62,11 +82,21 @@ output(ostream &out) const {
   }
   }
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingLine::extend_other
+//       Access: Protected, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
 bool BoundingLine::
 bool BoundingLine::
 extend_other(BoundingVolume *other) const {
 extend_other(BoundingVolume *other) const {
   return other->extend_by_line(this);
   return other->extend_by_line(this);
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingLine::around_other
+//       Access: Protected, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
 bool BoundingLine::
 bool BoundingLine::
 around_other(BoundingVolume *other,
 around_other(BoundingVolume *other,
              const BoundingVolume **first,
              const BoundingVolume **first,
@@ -74,12 +104,21 @@ around_other(BoundingVolume *other,
   return other->around_lines(first, last);
   return other->around_lines(first, last);
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingLine::contains_other
+//       Access: Protected, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
 int BoundingLine::
 int BoundingLine::
 contains_other(const BoundingVolume *other) const {
 contains_other(const BoundingVolume *other) const {
   return other->contains_line(this);
   return other->contains_line(this);
 }
 }
 
 
-
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingLine::extend_by_line
+//       Access: Protected, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
 bool BoundingLine::
 bool BoundingLine::
 extend_by_line(const BoundingLine *line) {
 extend_by_line(const BoundingLine *line) {
   nassertr(!line->is_empty() && !line->is_infinite(), false);
   nassertr(!line->is_empty() && !line->is_infinite(), false);
@@ -95,6 +134,11 @@ extend_by_line(const BoundingLine *line) {
   return true;
   return true;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingLine::contains_sphere
+//       Access: Protected, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
 int BoundingLine::
 int BoundingLine::
 contains_sphere(const BoundingSphere *sphere) const {
 contains_sphere(const BoundingSphere *sphere) const {
   nassertr(!is_empty() && !is_infinite(), 0);
   nassertr(!is_empty() && !is_infinite(), 0);
@@ -109,6 +153,31 @@ contains_sphere(const BoundingSphere *sphere) const {
   }
   }
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingLine::contains_box
+//       Access: Protected, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+int BoundingLine::
+contains_box(const BoundingBox *box) const {
+  nassertr(!is_empty() && !is_infinite(), 0);
+  nassertr(!box->is_empty() && !box->is_infinite(), 0);
+
+  LPoint3f center = (box->get_minq() + box->get_maxq()) * 0.5f;
+  float r2 = (box->get_maxq() - box->get_minq()).length_squared() * 0.25f;
+
+  if (r2 >= sqr_dist_to_line(center)) {
+    return IF_possible;
+  } else {
+    return IF_no_intersection;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingLine::sqr_dist_to_line
+//       Access: Protected
+//  Description: 
+////////////////////////////////////////////////////////////////////
 float BoundingLine::
 float BoundingLine::
 sqr_dist_to_line(const LPoint3f &point) const {
 sqr_dist_to_line(const LPoint3f &point) const {
   nassertr(!point.is_nan(), 0.0f);
   nassertr(!point.is_nan(), 0.0f);

+ 5 - 1
panda/src/mathutil/boundingLine.h

@@ -35,8 +35,10 @@
 //               arbitrary points on the line.
 //               arbitrary points on the line.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 class EXPCL_PANDA BoundingLine : public GeometricBoundingVolume {
 class EXPCL_PANDA BoundingLine : public GeometricBoundingVolume {
-PUBLISHED:
+public:
   INLINE_MATHUTIL BoundingLine();
   INLINE_MATHUTIL BoundingLine();
+
+PUBLISHED:
   INLINE_MATHUTIL BoundingLine(const LPoint3f &a, const LPoint3f &b);
   INLINE_MATHUTIL BoundingLine(const LPoint3f &a, const LPoint3f &b);
   ALLOC_DELETED_CHAIN(BoundingLine);
   ALLOC_DELETED_CHAIN(BoundingLine);
 
 
@@ -62,6 +64,7 @@ protected:
   virtual bool extend_by_line(const BoundingLine *line);
   virtual bool extend_by_line(const BoundingLine *line);
 
 
   virtual int contains_sphere(const BoundingSphere *sphere) const;
   virtual int contains_sphere(const BoundingSphere *sphere) const;
+  virtual int contains_box(const BoundingBox *box) const;
 
 
   float sqr_dist_to_line(const LPoint3f &point) const;
   float sqr_dist_to_line(const LPoint3f &point) const;
 
 
@@ -88,6 +91,7 @@ private:
   static TypeHandle _type_handle;
   static TypeHandle _type_handle;
 
 
   friend class BoundingSphere;
   friend class BoundingSphere;
+  friend class BoundingBox;
 };
 };
 
 
 #include "boundingLine.I"
 #include "boundingLine.I"

+ 47 - 0
panda/src/mathutil/boundingPlane.cxx

@@ -151,3 +151,50 @@ contains_sphere(const BoundingSphere *sphere) const {
     return IF_no_intersection;
     return IF_no_intersection;
   }
   }
 }
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingPlane::contains_box
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+int BoundingPlane::
+contains_box(const BoundingBox *box) const {
+  nassertr(!is_empty() && !is_infinite(), 0);
+  nassertr(!box->is_empty() && !box->is_infinite(), 0);
+
+  // Put the box inside a sphere for the purpose of this test.
+  const LPoint3f &min = box->get_minq();
+  const LPoint3f &max = box->get_maxq();
+  LPoint3f center = (min + max) * 0.5f;
+  float radius2 = (max - center).length_squared();
+
+  int result = IF_possible | IF_some | IF_all;
+
+  float dist = _plane.dist_to_plane(center);
+  float dist2 = dist * dist;
+
+  if (dist >= 0.0f && dist2 > radius2) {
+    // The sphere is completely in front of this plane; it's thus
+    // completely outside of the hexahedron.
+    return IF_no_intersection;
+    
+  } else if (dist < 0.0f && dist2 < radius2) {
+    // The sphere is not completely behind this plane, but some of
+    // it is.
+    
+    // Look a little closer.
+    bool all_in = true;
+    for (int i = 0; i < 8 && all_in; ++i) {
+      if (_plane.dist_to_plane(box->get_point(i)) < 0.0f) {
+        // This point is outside the plane.
+        all_in = false;
+      }
+    }
+    
+    if (!all_in) {
+      result &= ~IF_all;
+    }
+  }
+
+  return result;
+}

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

@@ -59,6 +59,7 @@ protected:
   virtual bool extend_by_plane(const BoundingPlane *plane);
   virtual bool extend_by_plane(const BoundingPlane *plane);
 
 
   virtual int contains_sphere(const BoundingSphere *sphere) const;
   virtual int contains_sphere(const BoundingSphere *sphere) const;
+  virtual int contains_box(const BoundingBox *box) const;
 
 
 private:
 private:
   Planef _plane;
   Planef _plane;
@@ -81,6 +82,7 @@ private:
   static TypeHandle _type_handle;
   static TypeHandle _type_handle;
 
 
   friend class BoundingSphere;
   friend class BoundingSphere;
+  friend class BoundingBox;
 };
 };
 
 
 #include "boundingPlane.I"
 #include "boundingPlane.I"

+ 21 - 0
panda/src/mathutil/boundingSphere.I

@@ -16,10 +16,21 @@
 //
 //
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 
 
+
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingSphere::Constructor
+//       Access: Published
+//  Description: Constructs an empty sphere.
+////////////////////////////////////////////////////////////////////
 INLINE_MATHUTIL BoundingSphere::
 INLINE_MATHUTIL BoundingSphere::
 BoundingSphere() {
 BoundingSphere() {
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingSphere::Constructor
+//       Access: Published
+//  Description: Constructs a specific sphere.
+////////////////////////////////////////////////////////////////////
 INLINE_MATHUTIL BoundingSphere::
 INLINE_MATHUTIL BoundingSphere::
 BoundingSphere(const LPoint3f &center, float radius) :
 BoundingSphere(const LPoint3f &center, float radius) :
   _center(center), _radius(radius)
   _center(center), _radius(radius)
@@ -30,6 +41,11 @@ BoundingSphere(const LPoint3f &center, float radius) :
   }
   }
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingSphere::get_center
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
 INLINE_MATHUTIL const LPoint3f &BoundingSphere::
 INLINE_MATHUTIL const LPoint3f &BoundingSphere::
 get_center() const {
 get_center() const {
   nassertr(!is_empty(), _center);
   nassertr(!is_empty(), _center);
@@ -37,6 +53,11 @@ get_center() const {
   return _center;
   return _center;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingSphere::get_radius
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
 INLINE_MATHUTIL float BoundingSphere::
 INLINE_MATHUTIL float BoundingSphere::
 get_radius() const {
 get_radius() const {
   nassertr(!is_empty(), 0.0f);
   nassertr(!is_empty(), 0.0f);

+ 204 - 37
panda/src/mathutil/boundingSphere.cxx

@@ -17,6 +17,7 @@
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 
 
 #include "boundingSphere.h"
 #include "boundingSphere.h"
+#include "boundingBox.h"
 #include "boundingHexahedron.h"
 #include "boundingHexahedron.h"
 #include "boundingLine.h"
 #include "boundingLine.h"
 #include "boundingPlane.h"
 #include "boundingPlane.h"
@@ -28,11 +29,21 @@
 
 
 TypeHandle BoundingSphere::_type_handle;
 TypeHandle BoundingSphere::_type_handle;
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingSphere::make_copy
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
 BoundingVolume *BoundingSphere::
 BoundingVolume *BoundingSphere::
 make_copy() const {
 make_copy() const {
   return new BoundingSphere(*this);
   return new BoundingSphere(*this);
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingSphere::get_min
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
 LPoint3f BoundingSphere::
 LPoint3f BoundingSphere::
 get_min() const {
 get_min() const {
   nassertr(!is_empty(), LPoint3f(0.0f, 0.0f, 0.0f));
   nassertr(!is_empty(), LPoint3f(0.0f, 0.0f, 0.0f));
@@ -42,6 +53,11 @@ get_min() const {
                   _center[2] - _radius);
                   _center[2] - _radius);
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingSphere::get_max
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
 LPoint3f BoundingSphere::
 LPoint3f BoundingSphere::
 get_max() const {
 get_max() const {
   nassertr(!is_empty(), LPoint3f(0.0f, 0.0f, 0.0f));
   nassertr(!is_empty(), LPoint3f(0.0f, 0.0f, 0.0f));
@@ -51,6 +67,27 @@ get_max() const {
                   _center[2] + _radius);
                   _center[2] + _radius);
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingSphere::get_volume
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+float BoundingSphere::
+get_volume() const {
+  nassertr(!is_infinite(), 0.0f);
+  if (is_empty()) {
+    return 0.0f;
+  }
+
+  // Volume of a sphere: four-thirds pi r cubed.
+  return 4.0f / 3.0f * MathNumbers::pi_f * _radius * _radius * _radius;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingSphere::get_approx_center
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
 LPoint3f BoundingSphere::
 LPoint3f BoundingSphere::
 get_approx_center() const {
 get_approx_center() const {
   nassertr(!is_empty(), LPoint3f(0.0f, 0.0f, 0.0f));
   nassertr(!is_empty(), LPoint3f(0.0f, 0.0f, 0.0f));
@@ -58,6 +95,11 @@ get_approx_center() const {
   return get_center();
   return get_center();
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingSphere::xform
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
 void BoundingSphere::
 void BoundingSphere::
 xform(const LMatrix4f &mat) {
 xform(const LMatrix4f &mat) {
   nassertv(!mat.is_nan());
   nassertv(!mat.is_nan());
@@ -99,6 +141,11 @@ xform(const LMatrix4f &mat) {
   }
   }
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingSphere::output
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
 void BoundingSphere::
 void BoundingSphere::
 output(ostream &out) const {
 output(ostream &out) const {
   if (is_empty()) {
   if (is_empty()) {
@@ -110,11 +157,21 @@ output(ostream &out) const {
   }
   }
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingSphere::extend_other
+//       Access: Protected, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
 bool BoundingSphere::
 bool BoundingSphere::
 extend_other(BoundingVolume *other) const {
 extend_other(BoundingVolume *other) const {
   return other->extend_by_sphere(this);
   return other->extend_by_sphere(this);
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingSphere::around_other
+//       Access: Protected, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
 bool BoundingSphere::
 bool BoundingSphere::
 around_other(BoundingVolume *other,
 around_other(BoundingVolume *other,
              const BoundingVolume **first,
              const BoundingVolume **first,
@@ -122,12 +179,22 @@ around_other(BoundingVolume *other,
   return other->around_spheres(first, last);
   return other->around_spheres(first, last);
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingSphere::contains_other
+//       Access: Protected, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
 int BoundingSphere::
 int BoundingSphere::
 contains_other(const BoundingVolume *other) const {
 contains_other(const BoundingVolume *other) const {
   return other->contains_sphere(this);
   return other->contains_sphere(this);
 }
 }
 
 
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingSphere::extend_by_point
+//       Access: Protected, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
 bool BoundingSphere::
 bool BoundingSphere::
 extend_by_point(const LPoint3f &point) {
 extend_by_point(const LPoint3f &point) {
   nassertr(!point.is_nan(), false);
   nassertr(!point.is_nan(), false);
@@ -146,6 +213,11 @@ extend_by_point(const LPoint3f &point) {
   return true;
   return true;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingSphere::extend_by_sphere
+//       Access: Protected, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
 bool BoundingSphere::
 bool BoundingSphere::
 extend_by_sphere(const BoundingSphere *sphere) {
 extend_by_sphere(const BoundingSphere *sphere) {
   nassertr(!sphere->is_empty() && !sphere->is_infinite(), false);
   nassertr(!sphere->is_empty() && !sphere->is_infinite(), false);
@@ -163,39 +235,76 @@ extend_by_sphere(const BoundingSphere *sphere) {
   return true;
   return true;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingSphere::extend_by_box
+//       Access: Protected, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
 bool BoundingSphere::
 bool BoundingSphere::
-extend_by_hexahedron(const BoundingHexahedron *hexahedron) {
-  return extend_by_finite(hexahedron);
-}
-
-bool BoundingSphere::
-extend_by_finite(const FiniteBoundingVolume *volume) {
-  nassertr(!volume->is_empty(), false);
-
-  LVector3f min1 = volume->get_min();
-  LVector3f max1 = volume->get_max();
+extend_by_box(const BoundingBox *box) {
+  const LVector3f &min1 = box->get_minq();
+  const LVector3f &max1 = box->get_maxq();
 
 
   if (is_empty()) {
   if (is_empty()) {
     _center = (min1 + max1) * 0.5f;
     _center = (min1 + max1) * 0.5f;
     _radius = length(LVector3f(max1 - _center));
     _radius = length(LVector3f(max1 - _center));
     _flags = 0;
     _flags = 0;
-  } else {
-    LVector3f v = max1 - _center;
-    float dist2 = dot(v, v);
 
 
-    if (dist2 > _radius * _radius) {
-      _radius = sqrtf(dist2);
+  } else {
+    // Find the minimum radius necessary to reach the corner.
+    float max_dist2 = -1.0;
+    for (int i = 0; i < 8; ++i) {
+      float dist2 = (box->get_point(i) - _center).length_squared();
+      if (dist2 > max_dist2) {
+        max_dist2 = dist2;
+      }
+    }
+    if (max_dist2 > _radius * _radius) {
+      _radius = csqrt(max_dist2);
     }
     }
   }
   }
 
 
   return true;
   return true;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingSphere::extend_by_hexahedron
+//       Access: Protected, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+bool BoundingSphere::
+extend_by_hexahedron(const BoundingHexahedron *hexahedron) {
+  nassertr(!hexahedron->is_empty(), false);
+
+  BoundingBox box(hexahedron->get_min(), hexahedron->get_max());
+  box.local_object();
+  return extend_by_box(&box);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingSphere::extend_by_finite
+//       Access: Protected
+//  Description: 
+////////////////////////////////////////////////////////////////////
+bool BoundingSphere::
+extend_by_finite(const FiniteBoundingVolume *volume) {
+  nassertr(!volume->is_empty(), false);
+
+  BoundingBox box(volume->get_min(), volume->get_max());
+  box.local_object();
+  return extend_by_box(&box);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingSphere::around_points
+//       Access: Protected, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
 bool BoundingSphere::
 bool BoundingSphere::
 around_points(const LPoint3f *first, const LPoint3f *last) {
 around_points(const LPoint3f *first, const LPoint3f *last) {
   nassertr(first != last, false);
   nassertr(first != last, false);
 
 
-  // First, get the minmax of all the points to construct a bounding
+  // First, get the box of all the points to construct a bounding
   // box.
   // box.
   const LPoint3f *p = first;
   const LPoint3f *p = first;
 
 
@@ -279,42 +388,66 @@ around_points(const LPoint3f *first, const LPoint3f *last) {
   return true;
   return true;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingSphere::around_spheres
+//       Access: Protected, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
 bool BoundingSphere::
 bool BoundingSphere::
 around_spheres(const BoundingVolume **first,
 around_spheres(const BoundingVolume **first,
                const BoundingVolume **last) {
                const BoundingVolume **last) {
   return around_finite(first, 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::
 bool BoundingSphere::
 around_hexahedrons(const BoundingVolume **first,
 around_hexahedrons(const BoundingVolume **first,
                    const BoundingVolume **last) {
                    const BoundingVolume **last) {
   return around_finite(first, last);
   return around_finite(first, last);
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingSphere::around_finite
+//       Access: Protected
+//  Description: 
+////////////////////////////////////////////////////////////////////
 bool BoundingSphere::
 bool BoundingSphere::
 around_finite(const BoundingVolume **first,
 around_finite(const BoundingVolume **first,
               const BoundingVolume **last) {
               const BoundingVolume **last) {
   nassertr(first != last, false);
   nassertr(first != last, false);
 
 
-  // We're given a set of bounding volumes, at least the first one of
-  // which is guaranteed to be finite and nonempty.  Some others may
-  // not be.
+  // We're given a set of bounding volumes, all of which are finite,
+  // and at least the first one of which is guaranteed to be nonempty.
+  // Some others may not be.
 
 
-  // First, get the minmax of all the points to construct a bounding
+  // First, get the box of all the points to construct a bounding
   // box.
   // box.
   const BoundingVolume **p = first;
   const BoundingVolume **p = first;
   nassertr(!(*p)->is_empty() && !(*p)->is_infinite(), false);
   nassertr(!(*p)->is_empty() && !(*p)->is_infinite(), false);
-  nassertr((*p)->is_of_type(FiniteBoundingVolume::get_class_type()), false);
   const FiniteBoundingVolume *vol = DCAST(FiniteBoundingVolume, *p);
   const FiniteBoundingVolume *vol = DCAST(FiniteBoundingVolume, *p);
   LPoint3f min_box = vol->get_min();
   LPoint3f min_box = vol->get_min();
   LPoint3f max_box = vol->get_max();
   LPoint3f max_box = vol->get_max();
 
 
-  bool any_unknown = false;
+  bool any_spheres = vol->is_exact_type(BoundingSphere::get_class_type());
 
 
   for (++p; p != last; ++p) {
   for (++p; p != last; ++p) {
     nassertr(!(*p)->is_infinite(), false);
     nassertr(!(*p)->is_infinite(), false);
-    if (!(*p)->is_empty() &&
-        (*p)->is_of_type(FiniteBoundingVolume::get_class_type())) {
+    if (!(*p)->is_empty()) {
       const FiniteBoundingVolume *vol = DCAST(FiniteBoundingVolume, *p);
       const FiniteBoundingVolume *vol = DCAST(FiniteBoundingVolume, *p);
       LPoint3f min1 = vol->get_min();
       LPoint3f min1 = vol->get_min();
       LPoint3f max1 = vol->get_max();
       LPoint3f max1 = vol->get_max();
@@ -325,8 +458,8 @@ around_finite(const BoundingVolume **first,
                   max(max_box[1], max1[1]),
                   max(max_box[1], max1[1]),
                   max(max_box[2], max1[2]));
                   max(max_box[2], max1[2]));
 
 
-      if (!(*p)->is_of_type(BoundingSphere::get_class_type())) {
-        any_unknown = true;
+      if (vol->is_exact_type(BoundingSphere::get_class_type())) {
+        any_spheres = true;
       }
       }
     }
     }
   }
   }
@@ -334,27 +467,39 @@ around_finite(const BoundingVolume **first,
   // Now take the center of the bounding box as the center of the sphere.
   // Now take the center of the bounding box as the center of the sphere.
   _center = (min_box + max_box) * 0.5f;
   _center = (min_box + max_box) * 0.5f;
 
 
-  if (any_unknown) {
-    // If we have any volumes in the list that we don't know what to
-    // do with, we'll have to make the bounding sphere large enough to
-    // enclose the bounding box.  Less than ideal, but too bad.
+  if (!any_spheres) {
+    // Since there are no spheres in the list, we have to make this
+    // sphere fully enclose all of the bounding boxes.
     _radius = length(max_box - _center);
     _radius = length(max_box - _center);
 
 
   } else {
   } else {
-    // Otherwise, we do understand all the volumes in the list; make
-    // the sphere as tight as we can.
+    // We might be able to go tighter, by lopping off the corners of
+    // the spheres.
     _radius = 0.0f;
     _radius = 0.0f;
     for (p = first; p != last; ++p) {
     for (p = first; p != last; ++p) {
       if (!(*p)->is_empty()) {
       if (!(*p)->is_empty()) {
-        if ((*p)->is_of_type(BoundingSphere::get_class_type())) {
+        if ((*p)->is_exact_type(BoundingSphere::get_class_type())) {
+          // This is a sphere; consider its corner.
           const BoundingSphere *sphere = DCAST(BoundingSphere, *p);
           const BoundingSphere *sphere = DCAST(BoundingSphere, *p);
           float dist = length(sphere->_center - _center);
           float dist = length(sphere->_center - _center);
           _radius = max(_radius, dist + sphere->_radius);
           _radius = max(_radius, dist + sphere->_radius);
+          
         } else {
         } else {
-          // Shouldn't get here, unless we missed a type from above.
-          mathutil_cat.error()
-            << "Unexpected type in BoundingSphere::around_finite()\n";
-          nassertr(false, false);
+          // This is a nonsphere.  We fit around it.
+          const FiniteBoundingVolume *vol = DCAST(FiniteBoundingVolume, *p);
+
+          BoundingBox box(vol->get_min(), vol->get_max());
+          box.local_object();
+          
+          // Find the minimum radius necessary to reach the corner.
+          float max_dist2 = -1.0;
+          for (int i = 0; i < 8; ++i) {
+            float dist2 = (box.get_point(i) - _center).length_squared();
+            if (dist2 > max_dist2) {
+              max_dist2 = dist2;
+            }
+          }
+          _radius = max(_radius, csqrt(max_dist2));
         }
         }
       }
       }
     }
     }
@@ -364,6 +509,11 @@ around_finite(const BoundingVolume **first,
   return true;
   return true;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingSphere::contains_point
+//       Access: Protected, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
 int BoundingSphere::
 int BoundingSphere::
 contains_point(const LPoint3f &point) const {
 contains_point(const LPoint3f &point) const {
   nassertr(!point.is_nan(), IF_no_intersection);
   nassertr(!point.is_nan(), IF_no_intersection);
@@ -382,6 +532,11 @@ contains_point(const LPoint3f &point) const {
   }
   }
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingSphere::contains_lineseg
+//       Access: Protected, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
 int BoundingSphere::
 int BoundingSphere::
 contains_lineseg(const LPoint3f &a, const LPoint3f &b) const {
 contains_lineseg(const LPoint3f &a, const LPoint3f &b) const {
   nassertr(!a.is_nan() && !b.is_nan(), IF_no_intersection);
   nassertr(!a.is_nan() && !b.is_nan(), IF_no_intersection);
@@ -470,6 +625,18 @@ contains_sphere(const BoundingSphere *sphere) const {
   }
   }
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingSphere::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 BoundingSphere::
+contains_box(const BoundingBox *box) const {
+  return box->contains_sphere(this) & ~IF_all;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: BoundingSphere::contains_hexahedron
 //     Function: BoundingSphere::contains_hexahedron
 //       Access: Protected, Virtual
 //       Access: Protected, Virtual

+ 5 - 0
panda/src/mathutil/boundingSphere.h

@@ -40,6 +40,7 @@ public:
 
 
   virtual LPoint3f get_min() const;
   virtual LPoint3f get_min() const;
   virtual LPoint3f get_max() const;
   virtual LPoint3f get_max() const;
+  virtual float get_volume() const;
 
 
   virtual LPoint3f get_approx_center() const;
   virtual LPoint3f get_approx_center() const;
   virtual void xform(const LMatrix4f &mat);
   virtual void xform(const LMatrix4f &mat);
@@ -60,6 +61,7 @@ protected:
 
 
   virtual bool extend_by_point(const LPoint3f &point);
   virtual bool extend_by_point(const LPoint3f &point);
   virtual bool extend_by_sphere(const BoundingSphere *sphere);
   virtual bool extend_by_sphere(const BoundingSphere *sphere);
+  virtual bool extend_by_box(const BoundingBox *box);
   virtual bool extend_by_hexahedron(const BoundingHexahedron *hexahedron);
   virtual bool extend_by_hexahedron(const BoundingHexahedron *hexahedron);
   bool extend_by_finite(const FiniteBoundingVolume *volume);
   bool extend_by_finite(const FiniteBoundingVolume *volume);
 
 
@@ -67,6 +69,8 @@ protected:
                              const LPoint3f *last);
                              const LPoint3f *last);
   virtual bool around_spheres(const BoundingVolume **first,
   virtual bool around_spheres(const BoundingVolume **first,
                               const BoundingVolume **last);
                               const BoundingVolume **last);
+  virtual bool around_boxes(const BoundingVolume **first,
+                            const BoundingVolume **last);
   virtual bool around_hexahedrons(const BoundingVolume **first,
   virtual bool around_hexahedrons(const BoundingVolume **first,
                                   const BoundingVolume **last);
                                   const BoundingVolume **last);
   bool around_finite(const BoundingVolume **first,
   bool around_finite(const BoundingVolume **first,
@@ -76,6 +80,7 @@ protected:
   virtual int contains_lineseg(const LPoint3f &a, const LPoint3f &b) const;
   virtual int contains_lineseg(const LPoint3f &a, const LPoint3f &b) const;
   virtual int contains_hexahedron(const BoundingHexahedron *hexahedron) const;
   virtual int contains_hexahedron(const BoundingHexahedron *hexahedron) const;
   virtual int contains_sphere(const BoundingSphere *sphere) 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_line(const BoundingLine *line) const;
   virtual int contains_plane(const BoundingPlane *plane) const;
   virtual int contains_plane(const BoundingPlane *plane) const;
 
 

+ 5 - 5
panda/src/mathutil/boundingVolume.I

@@ -28,7 +28,7 @@ BoundingVolume() {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: BoundingVolume::is_empty
 //     Function: BoundingVolume::is_empty
-//       Access: Public
+//       Access: Published
 //  Description: Any kind of volume might be empty.  This is a
 //  Description: Any kind of volume might be empty.  This is a
 //               degenerate volume that contains no points; it's not
 //               degenerate volume that contains no points; it's not
 //               the same as, for instance, a sphere with radius zero,
 //               the same as, for instance, a sphere with radius zero,
@@ -42,7 +42,7 @@ is_empty() const {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: BoundingVolume::is_infinite
 //     Function: BoundingVolume::is_infinite
-//       Access: Public
+//       Access: Published
 //  Description: The other side of the empty coin is an infinite
 //  Description: The other side of the empty coin is an infinite
 //               volume.  This is a degenerate state of a normally
 //               volume.  This is a degenerate state of a normally
 //               finite volume that contains all points.  (Note that
 //               finite volume that contains all points.  (Note that
@@ -63,7 +63,7 @@ is_infinite() const {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: BoundingVolume::set_infinite
 //     Function: BoundingVolume::set_infinite
-//       Access: Public
+//       Access: Published
 //  Description: Marks the volume as infinite, even if it is normally
 //  Description: Marks the volume as infinite, even if it is normally
 //               finite.  You can think of this as an infinite
 //               finite.  You can think of this as an infinite
 //               extend_by() operation.
 //               extend_by() operation.
@@ -75,7 +75,7 @@ set_infinite() {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: BoundingVolume::extend_by
 //     Function: BoundingVolume::extend_by
-//       Access: Public
+//       Access: Published
 //  Description: Increases the size of the volume to include the given
 //  Description: Increases the size of the volume to include the given
 //               volume.
 //               volume.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -96,7 +96,7 @@ extend_by(const BoundingVolume *vol) {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: BoundingVolume::contains
 //     Function: BoundingVolume::contains
-//       Access: Public
+//       Access: Published
 //  Description: Returns the appropriate set of IntersectionFlags to
 //  Description: Returns the appropriate set of IntersectionFlags to
 //               indicate the amount of intersection with the
 //               indicate the amount of intersection with the
 //               indicated volume.
 //               indicated volume.

+ 128 - 2
panda/src/mathutil/boundingVolume.cxx

@@ -26,7 +26,7 @@ TypeHandle BoundingVolume::_type_handle;
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: BoundingVolume::around
 //     Function: BoundingVolume::around
-//       Access: Public
+//       Access: Published
 //  Description: Resets the volume to enclose only the volumes
 //  Description: Resets the volume to enclose only the volumes
 //               indicated.  Returns true if successful, false if the
 //               indicated.  Returns true if successful, false if the
 //               volume doesn't know how to do that or can't do that.
 //               volume doesn't know how to do that or can't do that.
@@ -72,7 +72,7 @@ around(const BoundingVolume **first, const BoundingVolume **last) {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: BoundingVolume::write
 //     Function: BoundingVolume::write
-//       Access: Public
+//       Access: Published
 //  Description:
 //  Description:
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void BoundingVolume::
 void BoundingVolume::
@@ -80,6 +80,27 @@ write(ostream &out, int indent_level) const {
   indent(out, indent_level) << *this << "\n";
   indent(out, indent_level) << *this << "\n";
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingVolume::string_bounds_type
+//       Access: Public, Static
+//  Description: Returns the BoundsType corresponding to the indicated
+//               string.
+////////////////////////////////////////////////////////////////////
+BoundingVolume::BoundsType BoundingVolume::
+string_bounds_type(const string &str) {
+  if (strcmp(str.c_str(), "best") == 0) {
+    return BT_best;
+
+  } else if (strcmp(str.c_str(), "sphere") == 0) {
+    return BT_sphere;
+
+  } else if (strcmp(str.c_str(), "box") == 0) {
+    return BT_box;
+  }
+
+  return BT_invalid;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: BoundingVolume::extend_by_sphere
 //     Function: BoundingVolume::extend_by_sphere
 //       Access: Protected, Virtual
 //       Access: Protected, Virtual
@@ -89,6 +110,23 @@ write(ostream &out, int indent_level) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool BoundingVolume::
 bool BoundingVolume::
 extend_by_sphere(const BoundingSphere *) {
 extend_by_sphere(const BoundingSphere *) {
+  mathutil_cat.warning()
+    << get_type() << "::extend_by_sphere() called\n";
+  _flags = F_infinite;
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingVolume::extend_by_box
+//       Access: Protected, Virtual
+//  Description: Double-dispatch support: called by extend_other()
+//               when the type we're extending by is known to be a
+//               box.
+////////////////////////////////////////////////////////////////////
+bool BoundingVolume::
+extend_by_box(const BoundingBox *) {
+  mathutil_cat.warning()
+    << get_type() << "::extend_by_box() called\n";
   _flags = F_infinite;
   _flags = F_infinite;
   return false;
   return false;
 }
 }
@@ -102,6 +140,8 @@ extend_by_sphere(const BoundingSphere *) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool BoundingVolume::
 bool BoundingVolume::
 extend_by_hexahedron(const BoundingHexahedron *) {
 extend_by_hexahedron(const BoundingHexahedron *) {
+  mathutil_cat.warning()
+    << get_type() << "::extend_by_hexahedron() called\n";
   _flags = F_infinite;
   _flags = F_infinite;
   return false;
   return false;
 }
 }
@@ -115,6 +155,8 @@ extend_by_hexahedron(const BoundingHexahedron *) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool BoundingVolume::
 bool BoundingVolume::
 extend_by_line(const BoundingLine *) {
 extend_by_line(const BoundingLine *) {
+  mathutil_cat.warning()
+    << get_type() << "::extend_by_line() called\n";
   _flags = F_infinite;
   _flags = F_infinite;
   return false;
   return false;
 }
 }
@@ -128,6 +170,8 @@ extend_by_line(const BoundingLine *) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool BoundingVolume::
 bool BoundingVolume::
 extend_by_plane(const BoundingPlane *) {
 extend_by_plane(const BoundingPlane *) {
+  mathutil_cat.warning()
+    << get_type() << "::extend_by_plane() called\n";
   _flags = F_infinite;
   _flags = F_infinite;
   return false;
   return false;
 }
 }
@@ -141,6 +185,23 @@ extend_by_plane(const BoundingPlane *) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool BoundingVolume::
 bool BoundingVolume::
 around_spheres(const BoundingVolume **, const BoundingVolume **) {
 around_spheres(const BoundingVolume **, const BoundingVolume **) {
+  mathutil_cat.warning()
+    << get_type() << "::around_spheres() called\n";
+  _flags = F_infinite;
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingVolume::around_boxes
+//       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 nonempty box.
+////////////////////////////////////////////////////////////////////
+bool BoundingVolume::
+around_boxes(const BoundingVolume **, const BoundingVolume **) {
+  mathutil_cat.warning()
+    << get_type() << "::around_boxes() called\n";
   _flags = F_infinite;
   _flags = F_infinite;
   return false;
   return false;
 }
 }
@@ -154,6 +215,8 @@ around_spheres(const BoundingVolume **, const BoundingVolume **) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool BoundingVolume::
 bool BoundingVolume::
 around_hexahedrons(const BoundingVolume **, const BoundingVolume **) {
 around_hexahedrons(const BoundingVolume **, const BoundingVolume **) {
+  mathutil_cat.warning()
+    << get_type() << "::around_hexahedrons() called\n";
   _flags = F_infinite;
   _flags = F_infinite;
   return false;
   return false;
 }
 }
@@ -174,6 +237,9 @@ around_lines(const BoundingVolume **, const BoundingVolume **) {
     return true;
     return true;
   }
   }
 
 
+  mathutil_cat.warning()
+    << get_type() << "::around_lines() called\n";
+
   // Otherwise, we might do better, and we require each class to
   // Otherwise, we might do better, and we require each class to
   // define a function.  If we get here, the function isn't defined,
   // define a function.  If we get here, the function isn't defined,
   // so we return false to indicate this.
   // so we return false to indicate this.
@@ -196,6 +262,9 @@ around_planes(const BoundingVolume **, const BoundingVolume **) {
     return true;
     return true;
   }
   }
 
 
+  mathutil_cat.warning()
+    << get_type() << "::around_planes() called\n";
+
   // Otherwise, we might do better, and we require each class to
   // Otherwise, we might do better, and we require each class to
   // define a function.  If we get here, the function isn't defined,
   // define a function.  If we get here, the function isn't defined,
   // so we return false to indicate this.
   // so we return false to indicate this.
@@ -211,6 +280,22 @@ around_planes(const BoundingVolume **, const BoundingVolume **) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 int BoundingVolume::
 int BoundingVolume::
 contains_sphere(const BoundingSphere *) const {
 contains_sphere(const BoundingSphere *) const {
+  mathutil_cat.warning()
+    << get_type() << "::contains_sphere() called\n";
+  return IF_dont_understand;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BoundingVolume::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 BoundingVolume::
+contains_box(const BoundingBox *) const {
+  mathutil_cat.warning()
+    << get_type() << "::contains_box() called\n";
   return IF_dont_understand;
   return IF_dont_understand;
 }
 }
 
 
@@ -223,6 +308,8 @@ contains_sphere(const BoundingSphere *) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 int BoundingVolume::
 int BoundingVolume::
 contains_hexahedron(const BoundingHexahedron *) const {
 contains_hexahedron(const BoundingHexahedron *) const {
+  mathutil_cat.warning()
+    << get_type() << "::contains_hexahedron() called\n";
   return IF_dont_understand;
   return IF_dont_understand;
 }
 }
 
 
@@ -235,6 +322,8 @@ contains_hexahedron(const BoundingHexahedron *) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 int BoundingVolume::
 int BoundingVolume::
 contains_line(const BoundingLine *) const {
 contains_line(const BoundingLine *) const {
+  mathutil_cat.warning()
+    << get_type() << "::contains_line() called\n";
   return IF_dont_understand;
   return IF_dont_understand;
 }
 }
 
 
@@ -247,5 +336,42 @@ contains_line(const BoundingLine *) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 int BoundingVolume::
 int BoundingVolume::
 contains_plane(const BoundingPlane *) const {
 contains_plane(const BoundingPlane *) const {
+  mathutil_cat.warning()
+    << get_type() << "::contains_plane() called\n";
   return IF_dont_understand;
   return IF_dont_understand;
 }
 }
+
+ostream &
+operator << (ostream &out, BoundingVolume::BoundsType type) {
+  switch (type) {
+  case BoundingVolume::BT_best:
+    return out << "best";
+
+  case BoundingVolume::BT_sphere:
+    return out << "sphere";
+
+  case BoundingVolume::BT_box:
+    return out << "box";
+
+  case BoundingVolume::BT_invalid:
+    return out << "invalid";
+  }
+
+  mathutil_cat.error()
+    << "Invalid BoundingVolume::BoundsType value: " << (int)type << "\n";
+  nassertr(false, out);
+  return out;
+}
+
+istream &
+operator >> (istream &in, BoundingVolume::BoundsType &type) {
+  string word;
+  in >> word;
+  type = BoundingVolume::string_bounds_type(word);
+  if (type == BoundingVolume::BT_invalid) {
+    mathutil_cat->error()
+      << "Invalid BoundingVolume::BoundsType string: " << word << "\n";
+  }
+  return in;
+}
+

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

@@ -26,6 +26,7 @@
 #include "deletedChain.h"
 #include "deletedChain.h"
 
 
 class BoundingSphere;
 class BoundingSphere;
+class BoundingBox;
 class BoundingHexahedron;
 class BoundingHexahedron;
 class BoundingLine;
 class BoundingLine;
 class BoundingPlane;
 class BoundingPlane;
@@ -42,8 +43,10 @@ class BoundingPlane;
 //               sort.
 //               sort.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 class EXPCL_PANDA BoundingVolume : public TypedReferenceCount {
 class EXPCL_PANDA BoundingVolume : public TypedReferenceCount {
-PUBLISHED:
+public:
   INLINE_MATHUTIL BoundingVolume();
   INLINE_MATHUTIL BoundingVolume();
+
+PUBLISHED:
   virtual BoundingVolume *make_copy() const=0;
   virtual BoundingVolume *make_copy() const=0;
 
 
   INLINE_MATHUTIL bool is_empty() const;
   INLINE_MATHUTIL bool is_empty() const;
@@ -96,6 +99,19 @@ PUBLISHED:
   virtual void output(ostream &out) const=0;
   virtual void output(ostream &out) const=0;
   virtual void write(ostream &out, int indent_level = 0) const;
   virtual void write(ostream &out, int indent_level = 0) const;
 
 
+  // This enum is used to control the automatic generation of bounding
+  // volumes.
+  enum BoundsType {
+    BT_best,
+    BT_sphere,
+    BT_box,
+
+    BT_invalid,    // Do not use this one
+  };
+
+public:
+  static BoundsType string_bounds_type(const string &str);
+
 protected:
 protected:
   enum Flags {
   enum Flags {
     F_empty        = 0x01,
     F_empty        = 0x01,
@@ -118,12 +134,15 @@ protected:
   // These functions are the second dispatch point.  They actually do
   // These functions are the second dispatch point.  They actually do
   // the work.
   // the work.
   virtual bool extend_by_sphere(const BoundingSphere *sphere);
   virtual bool extend_by_sphere(const BoundingSphere *sphere);
+  virtual bool extend_by_box(const BoundingBox *box);
   virtual bool extend_by_hexahedron(const BoundingHexahedron *hexahedron);
   virtual bool extend_by_hexahedron(const BoundingHexahedron *hexahedron);
   virtual bool extend_by_line(const BoundingLine *line);
   virtual bool extend_by_line(const BoundingLine *line);
   virtual bool extend_by_plane(const BoundingPlane *plane);
   virtual bool extend_by_plane(const BoundingPlane *plane);
 
 
   virtual bool around_spheres(const BoundingVolume **first,
   virtual bool around_spheres(const BoundingVolume **first,
                               const BoundingVolume **last);
                               const BoundingVolume **last);
+  virtual bool around_boxes(const BoundingVolume **first,
+                            const BoundingVolume **last);
   virtual bool around_hexahedrons(const BoundingVolume **first,
   virtual bool around_hexahedrons(const BoundingVolume **first,
                                   const BoundingVolume **last);
                                   const BoundingVolume **last);
   virtual bool around_lines(const BoundingVolume **first,
   virtual bool around_lines(const BoundingVolume **first,
@@ -132,6 +151,7 @@ protected:
                             const BoundingVolume **last);
                             const BoundingVolume **last);
 
 
   virtual int contains_sphere(const BoundingSphere *sphere) 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_hexahedron(const BoundingHexahedron *hexahedron) const;
   virtual int contains_line(const BoundingLine *line) const;
   virtual int contains_line(const BoundingLine *line) const;
   virtual int contains_plane(const BoundingPlane *plane) const;
   virtual int contains_plane(const BoundingPlane *plane) const;
@@ -155,6 +175,7 @@ private:
   static TypeHandle _type_handle;
   static TypeHandle _type_handle;
 
 
   friend class BoundingSphere;
   friend class BoundingSphere;
+  friend class BoundingBox;
   friend class BoundingHexahedron;
   friend class BoundingHexahedron;
   friend class BoundingLine;
   friend class BoundingLine;
   friend class BoundingPlane;
   friend class BoundingPlane;
@@ -164,4 +185,7 @@ INLINE_MATHUTIL ostream &operator << (ostream &out, const BoundingVolume &bound)
 
 
 #include "boundingVolume.I"
 #include "boundingVolume.I"
 
 
+EXPCL_PANDA ostream &operator << (ostream &out, BoundingVolume::BoundsType type);
+EXPCL_PANDA istream &operator >> (istream &in, BoundingVolume::BoundsType &type);
+
 #endif
 #endif

+ 8 - 0
panda/src/mathutil/config_mathutil.cxx

@@ -22,6 +22,7 @@
 #include "finiteBoundingVolume.h"
 #include "finiteBoundingVolume.h"
 #include "omniBoundingVolume.h"
 #include "omniBoundingVolume.h"
 #include "boundingSphere.h"
 #include "boundingSphere.h"
+#include "boundingBox.h"
 #include "boundingHexahedron.h"
 #include "boundingHexahedron.h"
 #include "boundingLine.h"
 #include "boundingLine.h"
 #include "boundingPlane.h"
 #include "boundingPlane.h"
@@ -44,9 +45,16 @@ ConfigVariableDouble fft_exponent
 ConfigVariableDouble fft_error_threshold
 ConfigVariableDouble fft_error_threshold
 ("fft-error-threshold", 0.2);
 ("fft-error-threshold", 0.2);
 
 
+ConfigVariableEnum<BoundingVolume::BoundsType> bounds_type
+("bounds-type", BoundingVolume::BT_sphere,
+ PRC_DESC("Specify the type of bounding volume that is created automatically "
+          "by Panda to enclose geometry.  Use 'sphere' or 'box', or use "
+          "'best' to let Panda decide which is most appropriate."));
+
 ConfigureFn(config_mathutil) {
 ConfigureFn(config_mathutil) {
   BoundingHexahedron::init_type();
   BoundingHexahedron::init_type();
   BoundingSphere::init_type();
   BoundingSphere::init_type();
+  BoundingBox::init_type();
   BoundingVolume::init_type();
   BoundingVolume::init_type();
   FiniteBoundingVolume::init_type();
   FiniteBoundingVolume::init_type();
   GeometricBoundingVolume::init_type();
   GeometricBoundingVolume::init_type();

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

@@ -22,6 +22,8 @@
 #include "pandabase.h"
 #include "pandabase.h"
 #include "notifyCategoryProxy.h"
 #include "notifyCategoryProxy.h"
 #include "configVariableDouble.h"
 #include "configVariableDouble.h"
+#include "configVariableEnum.h"
+#include "boundingVolume.h"
 
 
 NotifyCategoryDecl(mathutil, EXPCL_PANDA, EXPTP_PANDA);
 NotifyCategoryDecl(mathutil, EXPCL_PANDA, EXPTP_PANDA);
 
 
@@ -29,6 +31,7 @@ extern ConfigVariableDouble fft_offset;
 extern ConfigVariableDouble fft_factor;
 extern ConfigVariableDouble fft_factor;
 extern ConfigVariableDouble fft_exponent;
 extern ConfigVariableDouble fft_exponent;
 extern ConfigVariableDouble fft_error_threshold;
 extern ConfigVariableDouble fft_error_threshold;
+extern EXPCL_PANDA ConfigVariableEnum<BoundingVolume::BoundsType> bounds_type;
 
 
 #endif
 #endif
 
 

+ 23 - 0
panda/src/mathutil/finiteBoundingVolume.cxx

@@ -17,5 +17,28 @@
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 
 
 #include "finiteBoundingVolume.h"
 #include "finiteBoundingVolume.h"
+#include "boundingBox.h"
 
 
 TypeHandle FiniteBoundingVolume::_type_handle;
 TypeHandle FiniteBoundingVolume::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: FiniteBoundingVolume::get_volume
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+float FiniteBoundingVolume::
+get_volume() const {
+  nassertr(!is_infinite(), 0.0f);
+  if (is_empty()) {
+    return 0.0f;
+  }
+
+  mathutil_cat.warning()
+    << get_type() << "::get_volume() called\n";
+
+  // We don't know how to compute the volume of this shape correctly;
+  // just calculate the volume of its containing box.
+  BoundingBox box(get_min(), get_max());
+  box.local_object();
+  return box.get_volume();
+}

+ 1 - 1
panda/src/mathutil/finiteBoundingVolume.h

@@ -34,7 +34,7 @@ class EXPCL_PANDA FiniteBoundingVolume : public GeometricBoundingVolume {
 PUBLISHED:
 PUBLISHED:
   virtual LPoint3f get_min() const=0;
   virtual LPoint3f get_min() const=0;
   virtual LPoint3f get_max() const=0;
   virtual LPoint3f get_max() const=0;
-
+  virtual float get_volume() const;
 
 
 public:
 public:
   static TypeHandle get_class_type() {
   static TypeHandle get_class_type() {

+ 1 - 0
panda/src/mathutil/mathutil_composite1.cxx

@@ -1,5 +1,6 @@
 #include "boundingHexahedron.cxx"
 #include "boundingHexahedron.cxx"
 #include "boundingLine.cxx"
 #include "boundingLine.cxx"
+#include "boundingBox.cxx"
 #include "boundingPlane.cxx"
 #include "boundingPlane.cxx"
 #include "boundingSphere.cxx"
 #include "boundingSphere.cxx"
 #include "boundingVolume.cxx"
 #include "boundingVolume.cxx"

+ 31 - 0
panda/src/mathutil/omniBoundingVolume.cxx

@@ -117,6 +117,16 @@ extend_by_sphere(const BoundingSphere *) {
   return true;
   return true;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: OmniBoundingVolume::extend_by_box
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+bool OmniBoundingVolume::
+extend_by_box(const BoundingBox *) {
+  return true;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: OmniBoundingVolume::extend_by_hexahedron
 //     Function: OmniBoundingVolume::extend_by_hexahedron
 //       Access: Public, Virtual
 //       Access: Public, Virtual
@@ -148,6 +158,17 @@ around_spheres(const BoundingVolume **,
   return true;
   return true;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: OmniBoundingVolume::around_boxes
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+bool OmniBoundingVolume::
+around_boxes(const BoundingVolume **,
+             const BoundingVolume **) {
+  return true;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: OmniBoundingVolume::around_hexahedrons
 //     Function: OmniBoundingVolume::around_hexahedrons
 //       Access: Public, Virtual
 //       Access: Public, Virtual
@@ -189,6 +210,16 @@ contains_sphere(const BoundingSphere *) const {
   return IF_possible | IF_some | IF_all;
   return IF_possible | IF_some | IF_all;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: OmniBoundingVolume::contains_box
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+int OmniBoundingVolume::
+contains_box(const BoundingBox *) const {
+  return IF_possible | IF_some | IF_all;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: OmniBoundingVolume::contains_hexahedron
 //     Function: OmniBoundingVolume::contains_hexahedron
 //       Access: Public, Virtual
 //       Access: Public, Virtual

+ 4 - 0
panda/src/mathutil/omniBoundingVolume.h

@@ -50,12 +50,15 @@ protected:
 
 
   virtual bool extend_by_point(const LPoint3f &point);
   virtual bool extend_by_point(const LPoint3f &point);
   virtual bool extend_by_sphere(const BoundingSphere *sphere);
   virtual bool extend_by_sphere(const BoundingSphere *sphere);
+  virtual bool extend_by_box(const BoundingBox *box);
   virtual bool extend_by_hexahedron(const BoundingHexahedron *hexahedron);
   virtual bool extend_by_hexahedron(const BoundingHexahedron *hexahedron);
 
 
   virtual bool around_points(const LPoint3f *first,
   virtual bool around_points(const LPoint3f *first,
                              const LPoint3f *last);
                              const LPoint3f *last);
   virtual bool around_spheres(const BoundingVolume **first,
   virtual bool around_spheres(const BoundingVolume **first,
                               const BoundingVolume **last);
                               const BoundingVolume **last);
+  virtual bool around_boxes(const BoundingVolume **first,
+                            const BoundingVolume **last);
   virtual bool around_hexahedrons(const BoundingVolume **first,
   virtual bool around_hexahedrons(const BoundingVolume **first,
                                   const BoundingVolume **last);
                                   const BoundingVolume **last);
 
 
@@ -63,6 +66,7 @@ protected:
   virtual int contains_lineseg(const LPoint3f &a, const LPoint3f &b) const;
   virtual int contains_lineseg(const LPoint3f &a, const LPoint3f &b) const;
   virtual int contains_hexahedron(const BoundingHexahedron *hexahedron) const;
   virtual int contains_hexahedron(const BoundingHexahedron *hexahedron) const;
   virtual int contains_sphere(const BoundingSphere *sphere) const;
   virtual int contains_sphere(const BoundingSphere *sphere) const;
+  virtual int contains_box(const BoundingBox *box) const;
 
 
 public:
 public:
   static TypeHandle get_class_type() {
   static TypeHandle get_class_type() {

+ 46 - 0
panda/src/pgraph/cullTraverser.cxx

@@ -30,10 +30,12 @@
 #include "geomNode.h"
 #include "geomNode.h"
 #include "config_pgraph.h"
 #include "config_pgraph.h"
 #include "boundingSphere.h"
 #include "boundingSphere.h"
+#include "boundingBox.h"
 #include "boundingHexahedron.h"
 #include "boundingHexahedron.h"
 #include "portalClipper.h"
 #include "portalClipper.h"
 #include "geom.h"
 #include "geom.h"
 #include "geomTristrips.h"
 #include "geomTristrips.h"
+#include "geomTriangles.h"
 #include "geomLinestrips.h"
 #include "geomLinestrips.h"
 #include "geomVertexWriter.h"
 #include "geomVertexWriter.h"
 
 
@@ -426,6 +428,50 @@ make_bounds_viz(const BoundingVolume *vol) {
     geom = new Geom(vdata);
     geom = new Geom(vdata);
     geom->add_primitive(strip);
     geom->add_primitive(strip);
 
 
+  } else if (vol->is_of_type(FiniteBoundingVolume::get_class_type())) {
+    const FiniteBoundingVolume *fvol = DCAST(FiniteBoundingVolume, vol);
+
+    BoundingBox box(fvol->get_min(), fvol->get_max());
+    box.local_object();
+
+    PT(GeomVertexData) vdata = new GeomVertexData
+      ("bounds", GeomVertexFormat::get_v3(),
+       Geom::UH_stream);
+    GeomVertexWriter vertex(vdata, InternalName::get_vertex());
+
+    for (int i = 0; i < 8; ++i ) {
+      vertex.add_data3f(box.get_point(i));
+    }
+    
+    PT(GeomTriangles) tris = new GeomTriangles(Geom::UH_stream);
+    tris->add_vertices(0, 4, 5);
+    tris->close_primitive();
+    tris->add_vertices(0, 5, 1);
+    tris->close_primitive();
+    tris->add_vertices(4, 6, 7);
+    tris->close_primitive();
+    tris->add_vertices(4, 7, 5);
+    tris->close_primitive();
+    tris->add_vertices(6, 2, 3);
+    tris->close_primitive();
+    tris->add_vertices(6, 3, 7);
+    tris->close_primitive();
+    tris->add_vertices(2, 0, 1);
+    tris->close_primitive();
+    tris->add_vertices(2, 1, 3);
+    tris->close_primitive();
+    tris->add_vertices(1, 5, 7);
+    tris->close_primitive();
+    tris->add_vertices(1, 7, 3);
+    tris->close_primitive();
+    tris->add_vertices(2, 6, 4);
+    tris->close_primitive();
+    tris->add_vertices(2, 4, 0);
+    tris->close_primitive();
+
+    geom = new Geom(vdata);
+    geom->add_primitive(tris);
+
   } else {
   } else {
     pgraph_cat.warning()
     pgraph_cat.warning()
       << "Don't know how to draw a representation of "
       << "Don't know how to draw a representation of "

+ 29 - 11
panda/src/pgraph/geomNode.cxx

@@ -33,6 +33,8 @@
 #include "pset.h"
 #include "pset.h"
 #include "config_pgraph.h"
 #include "config_pgraph.h"
 #include "graphicsStateGuardianBase.h"
 #include "graphicsStateGuardianBase.h"
+#include "boundingBox.h"
+#include "config_mathutil.h"
 
 
 TypeHandle GeomNode::_type_handle;
 TypeHandle GeomNode::_type_handle;
 
 
@@ -682,30 +684,46 @@ compute_internal_bounds(PandaNode::BoundsData *bdata, int pipeline_stage,
                         Thread *current_thread) const {
                         Thread *current_thread) const {
   int num_vertices = 0;
   int num_vertices = 0;
 
 
-  // First, get ourselves a fresh, empty bounding volume.
-  PT(BoundingVolume) bound = new BoundingSphere;
+  CDLockedStageReader cdata(_cycler, pipeline_stage, current_thread);
 
 
-  // Now actually compute the bounding volume by putting it around all
-  // of our geoms' bounding volumes.
   pvector<const BoundingVolume *> child_volumes;
   pvector<const BoundingVolume *> child_volumes;
-
-  CDStageReader cdata(_cycler, pipeline_stage, current_thread);
+  bool all_box = true;
 
 
   GeomList::const_iterator gi;
   GeomList::const_iterator gi;
   CPT(GeomList) geoms = cdata->get_geoms();
   CPT(GeomList) geoms = cdata->get_geoms();
   for (gi = geoms->begin(); gi != geoms->end(); ++gi) {
   for (gi = geoms->begin(); gi != geoms->end(); ++gi) {
     const GeomEntry &entry = (*gi);
     const GeomEntry &entry = (*gi);
     CPT(Geom) geom = entry._geom.get_read_pointer();
     CPT(Geom) geom = entry._geom.get_read_pointer();
-    child_volumes.push_back(geom->get_bounds());
+    const BoundingVolume *volume = geom->get_bounds();
+
+    if (!volume->is_empty()) {
+      child_volumes.push_back(volume);
+      if (!volume->is_exact_type(BoundingBox::get_class_type())) {
+        all_box = false;
+      }
+    }
     num_vertices += geom->get_nested_vertices();
     num_vertices += geom->get_nested_vertices();
   }
   }
 
 
-  const BoundingVolume **child_begin = &child_volumes[0];
-  const BoundingVolume **child_end = child_begin + child_volumes.size();
+  PT(GeometricBoundingVolume) gbv;
 
 
-  bound->around(child_begin, child_end);
+  if (bounds_type == BoundingVolume::BT_box ||
+      (bounds_type != BoundingVolume::BT_sphere && all_box)) {
+    // If all of the child volumes are a BoundingBox, then our volume
+    // is also a BoundingBox.
+    gbv = new BoundingBox;
+  } else {
+    // Otherwise, it's a sphere.
+    gbv = new BoundingSphere;
+  }
 
 
-  bdata->_internal_bounds = bound;
+  if (child_volumes.size() > 0) {
+    const BoundingVolume **child_begin = &child_volumes[0];
+    const BoundingVolume **child_end = child_begin + child_volumes.size();
+    ((BoundingVolume *)gbv)->around(child_begin, child_end);
+  }
+  
+  bdata->_internal_bounds = gbv;
   bdata->_internal_vertices = num_vertices;
   bdata->_internal_vertices = num_vertices;
   bdata->_internal_bounds_stale = false;
   bdata->_internal_bounds_stale = false;
 }
 }

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

@@ -143,6 +143,7 @@ private:
   typedef CycleDataReader<CData> CDReader;
   typedef CycleDataReader<CData> CDReader;
   typedef CycleDataWriter<CData> CDWriter;
   typedef CycleDataWriter<CData> CDWriter;
   typedef CycleDataStageReader<CData> CDStageReader;
   typedef CycleDataStageReader<CData> CDStageReader;
+  typedef CycleDataLockedStageReader<CData> CDLockedStageReader;
   typedef CycleDataStageWriter<CData> CDStageWriter;
   typedef CycleDataStageWriter<CData> CDStageWriter;
 
 
 public:
 public:

+ 50 - 19
panda/src/pgraph/pandaNode.cxx

@@ -27,7 +27,9 @@
 #include "accumulatedAttribs.h"
 #include "accumulatedAttribs.h"
 #include "clipPlaneAttrib.h"
 #include "clipPlaneAttrib.h"
 #include "boundingSphere.h"
 #include "boundingSphere.h"
+#include "boundingBox.h"
 #include "pStatTimer.h"
 #include "pStatTimer.h"
+#include "config_mathutil.h"
 
 
 // This category is just temporary for debugging convenience.
 // This category is just temporary for debugging convenience.
 NotifyCategoryDecl(drawmask, EXPCL_PANDA, EXPTP_PANDA);
 NotifyCategoryDecl(drawmask, EXPCL_PANDA, EXPTP_PANDA);
@@ -3413,10 +3415,17 @@ update_bounds(int pipeline_stage, PandaNode::CDLockedStageReader &cdata) {
     pvector<CPT(BoundingVolume) > child_volumes_ref;
     pvector<CPT(BoundingVolume) > child_volumes_ref;
     pvector<const BoundingVolume *> child_volumes;
     pvector<const BoundingVolume *> child_volumes;
   
   
+    bool all_box = true;
     CPT(BoundingVolume) internal_bounds = 
     CPT(BoundingVolume) internal_bounds = 
       get_internal_bounds(pipeline_stage, current_thread);
       get_internal_bounds(pipeline_stage, current_thread);
-    child_volumes_ref.push_back(internal_bounds);
-    child_volumes.push_back(internal_bounds);
+
+    if (!internal_bounds->is_empty()) {
+      child_volumes_ref.push_back(internal_bounds);
+      child_volumes.push_back(internal_bounds);
+      if (!internal_bounds->is_exact_type(BoundingBox::get_class_type())) {
+        all_box = false;
+      }
+    }
 
 
     int num_vertices = cdata->_internal_vertices;
     int num_vertices = cdata->_internal_vertices;
 
 
@@ -3466,8 +3475,13 @@ update_bounds(int pipeline_stage, PandaNode::CDLockedStageReader &cdata) {
         }
         }
             
             
 	off_clip_planes = orig_cp->compose_off(child_cdataw->_off_clip_planes);
 	off_clip_planes = orig_cp->compose_off(child_cdataw->_off_clip_planes);
-	child_volumes_ref.push_back(child_cdataw->_external_bounds);
-	child_volumes.push_back(child_cdataw->_external_bounds);
+        if (!child_cdataw->_external_bounds->is_empty()) {
+          child_volumes_ref.push_back(child_cdataw->_external_bounds);
+          child_volumes.push_back(child_cdataw->_external_bounds);
+          if (!child_cdataw->_external_bounds->is_exact_type(BoundingBox::get_class_type())) {
+            all_box = false;
+          }
+        }
         num_vertices += child_cdataw->_nested_vertices;
         num_vertices += child_cdataw->_nested_vertices;
 
 
       } else {
       } else {
@@ -3498,8 +3512,13 @@ update_bounds(int pipeline_stage, PandaNode::CDLockedStageReader &cdata) {
         }
         }
 
 
 	off_clip_planes = orig_cp->compose_off(child_cdata->_off_clip_planes);
 	off_clip_planes = orig_cp->compose_off(child_cdata->_off_clip_planes);
-	child_volumes_ref.push_back(child_cdata->_external_bounds);
-	child_volumes.push_back(child_cdata->_external_bounds);
+        if (!child_cdata->_external_bounds->is_empty()) {
+          child_volumes_ref.push_back(child_cdata->_external_bounds);
+          child_volumes.push_back(child_cdata->_external_bounds);
+          if (!child_cdata->_external_bounds->is_exact_type(BoundingBox::get_class_type())) {
+            all_box = false;
+          }
+        }
         num_vertices += child_cdata->_nested_vertices;
         num_vertices += child_cdata->_nested_vertices;
       }
       }
     }
     }
@@ -3543,21 +3562,33 @@ update_bounds(int pipeline_stage, PandaNode::CDLockedStageReader &cdata) {
 	cdataw->_off_clip_planes = off_clip_planes;
 	cdataw->_off_clip_planes = off_clip_planes;
         cdataw->_nested_vertices = num_vertices;
         cdataw->_nested_vertices = num_vertices;
 
 
-	// Compute the bounding sphere around all of our child
-	// volumes.
-	PT(GeometricBoundingVolume) gbv = new BoundingSphere;
-	const BoundingVolume **child_begin = &child_volumes[0];
-	const BoundingVolume **child_end = child_begin + child_volumes.size();
-	((BoundingVolume *)gbv)->around(child_begin, child_end);
-  
-	// If we have a transform, apply it to the bounding volume we
-	// just computed.
 	CPT(TransformState) transform = get_transform(current_thread);
 	CPT(TransformState) transform = get_transform(current_thread);
-	if (!transform->is_identity()) {
-	  gbv->xform(transform->get_mat());
-	}
+        PT(GeometricBoundingVolume) gbv;
+
+        if (bounds_type == BoundingVolume::BT_box ||
+            (bounds_type != BoundingVolume::BT_sphere && all_box && transform->is_identity())) {
+          // If all of the child volumes are a BoundingBox, and we
+          // have no transform, then our volume is also a
+          // BoundingBox.
 
 
-	cdataw->_external_bounds = gbv;
+          gbv = new BoundingBox;
+        } else {
+          // Otherwise, it's a sphere.
+          gbv = new BoundingSphere;
+        }
+
+        if (child_volumes.size() > 0) {
+          const BoundingVolume **child_begin = &child_volumes[0];
+          const BoundingVolume **child_end = child_begin + child_volumes.size();
+          ((BoundingVolume *)gbv)->around(child_begin, child_end);
+        }
+        
+        // If we have a transform, apply it to the bounding volume we
+        // just computed.
+        if (!transform->is_identity()) {
+          gbv->xform(transform->get_mat());
+        }
+        cdataw->_external_bounds = gbv;
 	cdataw->_last_update = next_update;
 	cdataw->_last_update = next_update;
 
 
         if (drawmask_cat.is_debug()) {
         if (drawmask_cat.is_debug()) {