From faad1e333d356d312cf99f6167d197044a5ad2b7 Mon Sep 17 00:00:00 2001 From: David Rose Date: Sat, 2 Jun 2007 00:59:32 +0000 Subject: [PATCH] optional BoundingBox --- panda/src/collide/collisionNode.cxx | 54 +- panda/src/collide/collisionPolygon.cxx | 42 +- panda/src/framework/windowFramework.cxx | 19 +- panda/src/gobj/geom.cxx | 41 +- .../src/grutil/pipeOcclusionCullTraverser.cxx | 101 ++- panda/src/grutil/pipeOcclusionCullTraverser.h | 3 + panda/src/mathutil/Sources.pp | 3 + panda/src/mathutil/boundingBox.I | 120 ++++ panda/src/mathutil/boundingBox.cxx | 643 ++++++++++++++++++ panda/src/mathutil/boundingBox.h | 126 ++++ panda/src/mathutil/boundingHexahedron.I | 28 +- panda/src/mathutil/boundingHexahedron.cxx | 238 +++++-- panda/src/mathutil/boundingHexahedron.h | 27 +- panda/src/mathutil/boundingLine.I | 21 + panda/src/mathutil/boundingLine.cxx | 71 +- panda/src/mathutil/boundingLine.h | 6 +- panda/src/mathutil/boundingPlane.cxx | 47 ++ panda/src/mathutil/boundingPlane.h | 2 + panda/src/mathutil/boundingSphere.I | 21 + panda/src/mathutil/boundingSphere.cxx | 241 ++++++- panda/src/mathutil/boundingSphere.h | 5 + panda/src/mathutil/boundingVolume.I | 10 +- panda/src/mathutil/boundingVolume.cxx | 130 +++- panda/src/mathutil/boundingVolume.h | 26 +- panda/src/mathutil/config_mathutil.cxx | 8 + panda/src/mathutil/config_mathutil.h | 3 + panda/src/mathutil/finiteBoundingVolume.cxx | 23 + panda/src/mathutil/finiteBoundingVolume.h | 2 +- panda/src/mathutil/mathutil_composite1.cxx | 1 + panda/src/mathutil/omniBoundingVolume.cxx | 31 + panda/src/mathutil/omniBoundingVolume.h | 4 + panda/src/pgraph/cullTraverser.cxx | 46 ++ panda/src/pgraph/geomNode.cxx | 40 +- panda/src/pgraph/geomNode.h | 1 + panda/src/pgraph/pandaNode.cxx | 69 +- 35 files changed, 2022 insertions(+), 231 deletions(-) create mode 100644 panda/src/mathutil/boundingBox.I create mode 100644 panda/src/mathutil/boundingBox.cxx create mode 100644 panda/src/mathutil/boundingBox.h diff --git a/panda/src/collide/collisionNode.cxx b/panda/src/collide/collisionNode.cxx index 8a94687740..46203108f3 100644 --- a/panda/src/collide/collisionNode.cxx +++ b/panda/src/collide/collisionNode.cxx @@ -32,6 +32,8 @@ #include "bamWriter.h" #include "clockObject.h" #include "boundingSphere.h" +#include "boundingBox.h" +#include "config_mathutil.h" TypeHandle CollisionNode::_type_handle; @@ -309,41 +311,43 @@ set_from_collide_mask(CollideMask mask) { void CollisionNode:: compute_internal_bounds(PandaNode::BoundsData *bdata, int pipeline_stage, 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 child_volumes_ref; pvector child_volumes; - pvector cpt_volumes; + bool all_box = true; Solids::const_iterator gi; for (gi = _solids.begin(); gi != _solids.end(); ++gi) { CPT(CollisionSolid) solid = (*gi).get_read_pointer(); 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(); - - bool success = - bound->around(child_begin, child_end); - -#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 (!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; + } } } -#endif - bdata->_internal_bounds = bound; + PT(GeometricBoundingVolume) gbv = new BoundingBox; + + 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; + } + + 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_bounds_stale = false; } diff --git a/panda/src/collide/collisionPolygon.cxx b/panda/src/collide/collisionPolygon.cxx index 8d94401188..0ab83aa0fe 100644 --- a/panda/src/collide/collisionPolygon.cxx +++ b/panda/src/collide/collisionPolygon.cxx @@ -25,7 +25,7 @@ #include "collisionSegment.h" #include "config_collide.h" #include "cullTraverserData.h" -#include "boundingSphere.h" +#include "boundingBox.h" #include "pointerToArray.h" #include "geomNode.h" #include "geom.h" @@ -386,27 +386,31 @@ write(ostream &out, int indent_level) const { //////////////////////////////////////////////////////////////////// PT(BoundingVolume) CollisionPolygon:: 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); - - // Now actually compute the bounding volume by putting it around all - // of our vertices. - LMatrix4f to_3d_mat; - rederive_to_3d_mat(to_3d_mat); - pvector vertices; - Points::const_iterator pi; - for (pi = _points.begin(); pi != _points.end(); ++pi) { - vertices.push_back(to_3d((*pi)._p, to_3d_mat)); + if (_points.empty()) { + return new BoundingBox; } - const LPoint3f *vertices_begin = &vertices[0]; - const LPoint3f *vertices_end = vertices_begin + vertices.size(); - gbv->around(vertices_begin, vertices_end); + LMatrix4f to_3d_mat; + rederive_to_3d_mat(to_3d_mat); - return bound; + 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 new BoundingBox(n, x); } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/framework/windowFramework.cxx b/panda/src/framework/windowFramework.cxx index 9a7998d0af..3fd18f948b 100644 --- a/panda/src/framework/windowFramework.cxx +++ b/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. nassertv(volume != (BoundingVolume *)NULL); 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)) { framework_cat.warning() << "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)); // 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)); // And that the near plane is far enough forward. diff --git a/panda/src/gobj/geom.cxx b/panda/src/gobj/geom.cxx index 7101843b8d..2e8c2bd231 100644 --- a/panda/src/gobj/geom.cxx +++ b/panda/src/gobj/geom.cxx @@ -26,7 +26,9 @@ #include "bamReader.h" #include "bamWriter.h" #include "boundingSphere.h" +#include "boundingBox.h" #include "mutexHolder.h" +#include "config_mathutil.h" UpdateSeq Geom::_next_modified; 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 { 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. CPT(GeomVertexData) vertex_data = cdata->_data.get_read_pointer(); vertex_data = vertex_data->animate_vertices(current_thread); // 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; - 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); + if (found_any) { // 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(); - const LPoint3f *points_begin = &points[0]; - const LPoint3f *points_end = points_begin + 2; - gbv->around(points_begin, points_end); + PT(BoundingSphere) sphere = new BoundingSphere; + sphere->extend_by(&box); + cdata->_internal_bounds = sphere; + + } else { + // The user requested a BoundingBox, or did not specify. + cdata->_internal_bounds = new BoundingBox(min, max); + } Primitives::const_iterator pi; 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(); 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->_internal_bounds_stale = false; } diff --git a/panda/src/grutil/pipeOcclusionCullTraverser.cxx b/panda/src/grutil/pipeOcclusionCullTraverser.cxx index 11308f9752..22f10b0453 100644 --- a/panda/src/grutil/pipeOcclusionCullTraverser.cxx +++ b/panda/src/grutil/pipeOcclusionCullTraverser.cxx @@ -21,8 +21,10 @@ #include "graphicsPipe.h" #include "drawCullHandler.h" #include "boundingSphere.h" +#include "boundingBox.h" #include "geomVertexWriter.h" #include "geomTristrips.h" +#include "geomTriangles.h" #include "pStatTimer.h" #include "cullBinManager.h" #include "configVariableInt.h" @@ -115,6 +117,7 @@ PipeOcclusionCullTraverser(GraphicsOutput *host) { _internal_cull_handler = NULL; make_sphere(); + make_box(); make_solid_test_state(); _live = true; @@ -199,8 +202,8 @@ set_scene(SceneSetup *scene_setup, GraphicsStateGuardianBase *gsgbase) { _current_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. PStatTimer timer2(_draw_occlusion_pcollector); @@ -433,7 +436,7 @@ make_sphere() { // static const int num_stacks = 8; 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()); PT(GeomTristrips) strip = new GeomTristrips(Geom::UH_stream); @@ -474,6 +477,57 @@ compute_sphere_point(float latitude, float longitude) { 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 // Access: Private @@ -514,7 +568,7 @@ get_volume_viz(const BoundingVolume *vol, 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); CPT(TransformState) local_transform = 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. const LPoint3f ¢er = modelview_transform->get_pos(); 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; } // The sphere looks good. geom = _sphere_geom; 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. diff --git a/panda/src/grutil/pipeOcclusionCullTraverser.h b/panda/src/grutil/pipeOcclusionCullTraverser.h index cd177ce1c4..4b4f4ab2bd 100644 --- a/panda/src/grutil/pipeOcclusionCullTraverser.h +++ b/panda/src/grutil/pipeOcclusionCullTraverser.h @@ -74,6 +74,8 @@ protected: private: void make_sphere(); static Vertexf compute_sphere_point(float latitude, float longitude); + void make_box(); + void make_solid_test_state(); bool get_volume_viz(const BoundingVolume *vol, @@ -111,6 +113,7 @@ private: PT(OcclusionQueryContext) _next_query; PT(Geom) _sphere_geom; + PT(Geom) _box_geom; CPT(RenderState) _solid_test_state; class PendingObject { diff --git a/panda/src/mathutil/Sources.pp b/panda/src/mathutil/Sources.pp index f64c47d043..424c530bb9 100644 --- a/panda/src/mathutil/Sources.pp +++ b/panda/src/mathutil/Sources.pp @@ -13,6 +13,7 @@ #define SOURCES \ boundingHexahedron.I boundingHexahedron.h boundingLine.I \ boundingLine.h \ + boundingBox.I boundingBox.h \ boundingPlane.I boundingPlane.h \ boundingSphere.I boundingSphere.h \ boundingVolume.I boundingVolume.h config_mathutil.h \ @@ -39,6 +40,7 @@ #define INCLUDED_SOURCES \ boundingHexahedron.cxx boundingLine.cxx \ + boundingBox.cxx \ boundingPlane.cxx \ boundingSphere.cxx \ boundingVolume.cxx config_mathutil.cxx fftCompressor.cxx \ @@ -59,6 +61,7 @@ #define INSTALL_HEADERS \ boundingHexahedron.I boundingHexahedron.h boundingLine.I \ boundingLine.h \ + boundingBox.I boundingBox.h \ boundingPlane.I boundingPlane.h \ boundingSphere.I boundingSphere.h boundingVolume.I \ boundingVolume.h config_mathutil.h fftCompressor.h \ diff --git a/panda/src/mathutil/boundingBox.I b/panda/src/mathutil/boundingBox.I new file mode 100644 index 0000000000..d64dbfa9fc --- /dev/null +++ b/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 +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// 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])); +} diff --git a/panda/src/mathutil/boundingBox.cxx b/panda/src/mathutil/boundingBox.cxx new file mode 100644 index 0000000000..d00e32067c --- /dev/null +++ b/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 +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#include "boundingBox.h" +#include "boundingSphere.h" +#include "boundingHexahedron.h" +#include "boundingLine.h" +#include "boundingPlane.h" +#include "config_mathutil.h" +#include "dcast.h" + +#include +#include + +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; + } +} diff --git a/panda/src/mathutil/boundingBox.h b/panda/src/mathutil/boundingBox.h new file mode 100644 index 0000000000..70bc61961d --- /dev/null +++ b/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 +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#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 diff --git a/panda/src/mathutil/boundingHexahedron.I b/panda/src/mathutil/boundingHexahedron.I index e89a6ee44b..2833bc791e 100644 --- a/panda/src/mathutil/boundingHexahedron.I +++ b/panda/src/mathutil/boundingHexahedron.I @@ -16,26 +16,52 @@ // //////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: BoundingHexahedron::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// INLINE_MATHUTIL BoundingHexahedron:: BoundingHexahedron() { } +//////////////////////////////////////////////////////////////////// +// Function: BoundingHexahedron::get_num_points +// Access: Published +// Description: Returns 8: the number of vertices of a hexahedron. +//////////////////////////////////////////////////////////////////// INLINE_MATHUTIL int BoundingHexahedron:: get_num_points() const { return num_points; } +//////////////////////////////////////////////////////////////////// +// Function: BoundingHexahedron::get_point +// Access: Published +// Description: Returns the nth vertex of the hexahedron. +//////////////////////////////////////////////////////////////////// INLINE_MATHUTIL LPoint3f BoundingHexahedron:: 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]; } +//////////////////////////////////////////////////////////////////// +// Function: BoundingHexahedron::get_num_planes +// Access: Published +// Description: Returns 6: the number of faces of a hexahedron. +//////////////////////////////////////////////////////////////////// INLINE_MATHUTIL int BoundingHexahedron:: get_num_planes() const { return num_planes; } +//////////////////////////////////////////////////////////////////// +// Function: BoundingHexahedron::get_plane +// Access: Published +// Description: Returns the nth face of the hexahedron. +//////////////////////////////////////////////////////////////////// INLINE_MATHUTIL Planef BoundingHexahedron:: get_plane(int n) const { nassertr(n >= 0 && n < num_planes, Planef()); diff --git a/panda/src/mathutil/boundingHexahedron.cxx b/panda/src/mathutil/boundingHexahedron.cxx index 198b6585d8..388985521e 100644 --- a/panda/src/mathutil/boundingHexahedron.cxx +++ b/panda/src/mathutil/boundingHexahedron.cxx @@ -18,6 +18,7 @@ #include "boundingHexahedron.h" #include "boundingSphere.h" +#include "boundingBox.h" #include "config_mathutil.h" #include @@ -25,6 +26,11 @@ TypeHandle BoundingHexahedron::_type_handle; +//////////////////////////////////////////////////////////////////// +// Function: BoundingHexahedron::Constructor +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// BoundingHexahedron:: BoundingHexahedron(const Frustumf &frustum, bool is_ortho, CoordinateSystem cs) { @@ -60,6 +66,11 @@ BoundingHexahedron(const Frustumf &frustum, bool is_ortho, } } +//////////////////////////////////////////////////////////////////// +// Function: BoundingHexahedron::Constructor +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// BoundingHexahedron:: BoundingHexahedron(const LPoint3f &fll, const LPoint3f &flr, const LPoint3f &fur, const LPoint3f &ful, @@ -79,11 +90,21 @@ BoundingHexahedron(const LPoint3f &fll, const LPoint3f &flr, set_planes(); } +//////////////////////////////////////////////////////////////////// +// Function: BoundingHexahedron::make_copy +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// BoundingVolume *BoundingHexahedron:: make_copy() const { return new BoundingHexahedron(*this); } +//////////////////////////////////////////////////////////////////// +// Function: BoundingHexahedron::get_min +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// LPoint3f BoundingHexahedron:: get_min() const { nassertr(!is_empty(), LPoint3f(0.0f, 0.0f, 0.0f)); @@ -98,6 +119,11 @@ get_min() const { return m; } +//////////////////////////////////////////////////////////////////// +// Function: BoundingHexahedron::get_max +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// LPoint3f BoundingHexahedron:: get_max() const { nassertr(!is_empty(), LPoint3f(0.0f, 0.0f, 0.0f)); @@ -112,6 +138,11 @@ get_max() const { return m; } +//////////////////////////////////////////////////////////////////// +// Function: BoundingHexahedron::get_approx_center +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// LPoint3f BoundingHexahedron:: get_approx_center() const { nassertr(!is_empty(), LPoint3f(0.0f, 0.0f, 0.0f)); @@ -119,6 +150,11 @@ get_approx_center() const { return _centroid; } +//////////////////////////////////////////////////////////////////// +// Function: BoundingHexahedron::xform +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// void BoundingHexahedron:: xform(const LMatrix4f &mat) { if (!is_empty() && !is_infinite()) { @@ -130,6 +166,11 @@ xform(const LMatrix4f &mat) { } } +//////////////////////////////////////////////////////////////////// +// Function: BoundingHexahedron::output +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// void BoundingHexahedron:: output(ostream &out) const { if (is_empty()) { @@ -141,6 +182,11 @@ output(ostream &out) const { } } +//////////////////////////////////////////////////////////////////// +// Function: BoundingHexahedron::write +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// void BoundingHexahedron:: write(ostream &out, int indent_level) const { if (is_empty()) { @@ -158,11 +204,21 @@ write(ostream &out, int indent_level) const { } } +//////////////////////////////////////////////////////////////////// +// Function: BoundingHexahedron::extend_other +// Access: Protected, Virtual +// Description: +//////////////////////////////////////////////////////////////////// bool BoundingHexahedron:: extend_other(BoundingVolume *other) const { return other->extend_by_hexahedron(this); } +//////////////////////////////////////////////////////////////////// +// Function: BoundingHexahedron::around_other +// Access: Protected, Virtual +// Description: +//////////////////////////////////////////////////////////////////// bool BoundingHexahedron:: around_other(BoundingVolume *other, const BoundingVolume **first, @@ -170,56 +226,21 @@ around_other(BoundingVolume *other, return other->around_hexahedrons(first, last); } +//////////////////////////////////////////////////////////////////// +// Function: BoundingHexahedron::contains_other +// Access: Protected, Virtual +// Description: +//////////////////////////////////////////////////////////////////// int BoundingHexahedron:: contains_other(const BoundingVolume *other) const { 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:: contains_point(const LPoint3f &point) const { if (is_empty()) { @@ -241,6 +262,11 @@ contains_point(const LPoint3f &point) const { } } +//////////////////////////////////////////////////////////////////// +// Function: BoundingHexahedron::contains_lineseg +// Access: Protected, Virtual +// Description: +//////////////////////////////////////////////////////////////////// int BoundingHexahedron:: contains_lineseg(const LPoint3f &a, const LPoint3f &b) const { 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:: contains_sphere(const BoundingSphere *sphere) const { nassertr(!is_empty(), 0); @@ -297,37 +328,111 @@ contains_sphere(const BoundingSphere *sphere) const { return result; } +//////////////////////////////////////////////////////////////////// +// Function: BoundingHexahedron::contains_box +// Access: Protected, Virtual +// Description: +//////////////////////////////////////////////////////////////////// int BoundingHexahedron:: -contains_hexahedron(const BoundingHexahedron *hexahedron) const { +contains_box(const BoundingBox *box) const { nassertr(!is_empty(), 0); - nassertr(!hexahedron->is_empty(), 0); + nassertr(!box->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 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(); - 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(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:: +contains_hexahedron(const BoundingHexahedron *hexahedron) const { + nassertr(!is_empty(), 0); + nassertr(!hexahedron->is_empty(), 0); + + // 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(); + + 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(hexahedron->get_point(i)) < 0.0f) { + // This point is outside the plane. + all_in = false; + } + } + + if (!all_in) { + result &= ~IF_all; + } + } + } + + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: BoundingHexahedron::set_planes +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// void BoundingHexahedron:: set_planes() { _planes[0] = Planef(_points[0], _points[3], _points[2]); @@ -358,6 +463,11 @@ set_planes() { } } +//////////////////////////////////////////////////////////////////// +// Function: BoundingHexahedron::set_centroid +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// void BoundingHexahedron:: set_centroid() { LPoint3f net = _points[0]; diff --git a/panda/src/mathutil/boundingHexahedron.h b/panda/src/mathutil/boundingHexahedron.h index 0601a15270..c0ed9f155c 100644 --- a/panda/src/mathutil/boundingHexahedron.h +++ b/panda/src/mathutil/boundingHexahedron.h @@ -32,21 +32,24 @@ // Class : BoundingHexahedron // Description : This defines a bounding convex hexahedron. It is // 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 { public: INLINE_MATHUTIL BoundingHexahedron(); + +PUBLISHED: BoundingHexahedron(const Frustumf &frustum, bool is_ortho, CoordinateSystem cs = CS_default); BoundingHexahedron(const LPoint3f &fll, const LPoint3f &flr, const LPoint3f &fur, const LPoint3f &ful, const LPoint3f &nll, const LPoint3f &nlr, const LPoint3f &nur, const LPoint3f &nul); + +public: ALLOC_DELETED_CHAIN(BoundingHexahedron); virtual BoundingVolume *make_copy() const; @@ -59,6 +62,7 @@ public: virtual void output(ostream &out) const; virtual void write(ostream &out, int indent_level = 0) const; +PUBLISHED: INLINE_MATHUTIL int get_num_points() const; INLINE_MATHUTIL LPoint3f get_point(int n) const; INLINE_MATHUTIL int get_num_planes() const; @@ -71,21 +75,10 @@ protected: 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_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_lineseg(const LPoint3f &a, const LPoint3f &b) const; virtual int contains_sphere(const BoundingSphere *sphere) const; + virtual int contains_box(const BoundingBox *box) const; virtual int contains_hexahedron(const BoundingHexahedron *hexahedron) const; private: diff --git a/panda/src/mathutil/boundingLine.I b/panda/src/mathutil/boundingLine.I index 739184afa3..4ec42db7c2 100644 --- a/panda/src/mathutil/boundingLine.I +++ b/panda/src/mathutil/boundingLine.I @@ -16,10 +16,21 @@ // //////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: BoundingLine::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// INLINE_MATHUTIL BoundingLine:: BoundingLine() { } +//////////////////////////////////////////////////////////////////// +// Function: BoundingLine::Constructor +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// INLINE_MATHUTIL BoundingLine:: BoundingLine(const LPoint3f &a, const LPoint3f &b) : _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:: get_point_a() const { nassertr(!is_empty(), _origin); @@ -38,6 +54,11 @@ get_point_a() const { return _origin; } +//////////////////////////////////////////////////////////////////// +// Function: BoundingLine::get_point_b +// Access: Published +// Description: Returns the second point that defines the line. +//////////////////////////////////////////////////////////////////// INLINE_MATHUTIL LPoint3f BoundingLine:: get_point_b() const { nassertr(!is_empty(), _origin); diff --git a/panda/src/mathutil/boundingLine.cxx b/panda/src/mathutil/boundingLine.cxx index 3add323eaa..bfb5dacee4 100644 --- a/panda/src/mathutil/boundingLine.cxx +++ b/panda/src/mathutil/boundingLine.cxx @@ -24,11 +24,21 @@ TypeHandle BoundingLine::_type_handle; +//////////////////////////////////////////////////////////////////// +// Function: BoundingLine::make_copy +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// BoundingVolume *BoundingLine:: make_copy() const { return new BoundingLine(*this); } +//////////////////////////////////////////////////////////////////// +// Function: BoundingLine::get_approx_center +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// LPoint3f BoundingLine:: get_approx_center() const { 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; } +//////////////////////////////////////////////////////////////////// +// Function: BoundingLine::xform +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// void BoundingLine:: xform(const LMatrix4f &mat) { nassertv(!mat.is_nan()); @@ -51,6 +66,11 @@ xform(const LMatrix4f &mat) { } } +//////////////////////////////////////////////////////////////////// +// Function: BoundingLine::output +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// void BoundingLine:: output(ostream &out) const { if (is_empty()) { @@ -62,11 +82,21 @@ output(ostream &out) const { } } +//////////////////////////////////////////////////////////////////// +// Function: BoundingLine::extend_other +// Access: Protected, Virtual +// Description: +//////////////////////////////////////////////////////////////////// bool BoundingLine:: extend_other(BoundingVolume *other) const { return other->extend_by_line(this); } +//////////////////////////////////////////////////////////////////// +// Function: BoundingLine::around_other +// Access: Protected, Virtual +// Description: +//////////////////////////////////////////////////////////////////// bool BoundingLine:: around_other(BoundingVolume *other, const BoundingVolume **first, @@ -74,12 +104,21 @@ around_other(BoundingVolume *other, return other->around_lines(first, last); } +//////////////////////////////////////////////////////////////////// +// Function: BoundingLine::contains_other +// Access: Protected, Virtual +// Description: +//////////////////////////////////////////////////////////////////// int BoundingLine:: contains_other(const BoundingVolume *other) const { return other->contains_line(this); } - +//////////////////////////////////////////////////////////////////// +// Function: BoundingLine::extend_by_line +// Access: Protected, Virtual +// Description: +//////////////////////////////////////////////////////////////////// bool BoundingLine:: extend_by_line(const BoundingLine *line) { nassertr(!line->is_empty() && !line->is_infinite(), false); @@ -95,6 +134,11 @@ extend_by_line(const BoundingLine *line) { return true; } +//////////////////////////////////////////////////////////////////// +// Function: BoundingLine::contains_sphere +// Access: Protected, Virtual +// Description: +//////////////////////////////////////////////////////////////////// int BoundingLine:: contains_sphere(const BoundingSphere *sphere) const { 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:: sqr_dist_to_line(const LPoint3f &point) const { nassertr(!point.is_nan(), 0.0f); diff --git a/panda/src/mathutil/boundingLine.h b/panda/src/mathutil/boundingLine.h index ddd0b965e3..0de9a0953d 100644 --- a/panda/src/mathutil/boundingLine.h +++ b/panda/src/mathutil/boundingLine.h @@ -35,8 +35,10 @@ // arbitrary points on the line. //////////////////////////////////////////////////////////////////// class EXPCL_PANDA BoundingLine : public GeometricBoundingVolume { -PUBLISHED: +public: INLINE_MATHUTIL BoundingLine(); + +PUBLISHED: INLINE_MATHUTIL BoundingLine(const LPoint3f &a, const LPoint3f &b); ALLOC_DELETED_CHAIN(BoundingLine); @@ -62,6 +64,7 @@ protected: virtual bool extend_by_line(const BoundingLine *line); 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; @@ -88,6 +91,7 @@ private: static TypeHandle _type_handle; friend class BoundingSphere; + friend class BoundingBox; }; #include "boundingLine.I" diff --git a/panda/src/mathutil/boundingPlane.cxx b/panda/src/mathutil/boundingPlane.cxx index 7a0f1bf99e..cff1756450 100644 --- a/panda/src/mathutil/boundingPlane.cxx +++ b/panda/src/mathutil/boundingPlane.cxx @@ -151,3 +151,50 @@ contains_sphere(const BoundingSphere *sphere) const { 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; +} diff --git a/panda/src/mathutil/boundingPlane.h b/panda/src/mathutil/boundingPlane.h index 55ff397501..b98e5931a4 100644 --- a/panda/src/mathutil/boundingPlane.h +++ b/panda/src/mathutil/boundingPlane.h @@ -59,6 +59,7 @@ protected: virtual bool extend_by_plane(const BoundingPlane *plane); virtual int contains_sphere(const BoundingSphere *sphere) const; + virtual int contains_box(const BoundingBox *box) const; private: Planef _plane; @@ -81,6 +82,7 @@ private: static TypeHandle _type_handle; friend class BoundingSphere; + friend class BoundingBox; }; #include "boundingPlane.I" diff --git a/panda/src/mathutil/boundingSphere.I b/panda/src/mathutil/boundingSphere.I index 0fe94e5d18..98e4616961 100644 --- a/panda/src/mathutil/boundingSphere.I +++ b/panda/src/mathutil/boundingSphere.I @@ -16,10 +16,21 @@ // //////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: BoundingSphere::Constructor +// Access: Published +// Description: Constructs an empty sphere. +//////////////////////////////////////////////////////////////////// INLINE_MATHUTIL BoundingSphere:: BoundingSphere() { } +//////////////////////////////////////////////////////////////////// +// Function: BoundingSphere::Constructor +// Access: Published +// Description: Constructs a specific sphere. +//////////////////////////////////////////////////////////////////// INLINE_MATHUTIL BoundingSphere:: BoundingSphere(const LPoint3f ¢er, float radius) : _center(center), _radius(radius) @@ -30,6 +41,11 @@ BoundingSphere(const LPoint3f ¢er, float radius) : } } +//////////////////////////////////////////////////////////////////// +// Function: BoundingSphere::get_center +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// INLINE_MATHUTIL const LPoint3f &BoundingSphere:: get_center() const { nassertr(!is_empty(), _center); @@ -37,6 +53,11 @@ get_center() const { return _center; } +//////////////////////////////////////////////////////////////////// +// Function: BoundingSphere::get_radius +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// INLINE_MATHUTIL float BoundingSphere:: get_radius() const { nassertr(!is_empty(), 0.0f); diff --git a/panda/src/mathutil/boundingSphere.cxx b/panda/src/mathutil/boundingSphere.cxx index 1a862bf961..5a8ca323b9 100644 --- a/panda/src/mathutil/boundingSphere.cxx +++ b/panda/src/mathutil/boundingSphere.cxx @@ -17,6 +17,7 @@ //////////////////////////////////////////////////////////////////// #include "boundingSphere.h" +#include "boundingBox.h" #include "boundingHexahedron.h" #include "boundingLine.h" #include "boundingPlane.h" @@ -28,11 +29,21 @@ TypeHandle BoundingSphere::_type_handle; +//////////////////////////////////////////////////////////////////// +// Function: BoundingSphere::make_copy +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// BoundingVolume *BoundingSphere:: make_copy() const { return new BoundingSphere(*this); } +//////////////////////////////////////////////////////////////////// +// Function: BoundingSphere::get_min +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// LPoint3f BoundingSphere:: get_min() const { nassertr(!is_empty(), LPoint3f(0.0f, 0.0f, 0.0f)); @@ -42,6 +53,11 @@ get_min() const { _center[2] - _radius); } +//////////////////////////////////////////////////////////////////// +// Function: BoundingSphere::get_max +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// LPoint3f BoundingSphere:: get_max() const { nassertr(!is_empty(), LPoint3f(0.0f, 0.0f, 0.0f)); @@ -51,6 +67,27 @@ get_max() const { _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:: get_approx_center() const { nassertr(!is_empty(), LPoint3f(0.0f, 0.0f, 0.0f)); @@ -58,6 +95,11 @@ get_approx_center() const { return get_center(); } +//////////////////////////////////////////////////////////////////// +// Function: BoundingSphere::xform +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// void BoundingSphere:: xform(const LMatrix4f &mat) { nassertv(!mat.is_nan()); @@ -99,6 +141,11 @@ xform(const LMatrix4f &mat) { } } +//////////////////////////////////////////////////////////////////// +// Function: BoundingSphere::output +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// void BoundingSphere:: output(ostream &out) const { if (is_empty()) { @@ -110,11 +157,21 @@ output(ostream &out) const { } } +//////////////////////////////////////////////////////////////////// +// Function: BoundingSphere::extend_other +// Access: Protected, Virtual +// Description: +//////////////////////////////////////////////////////////////////// bool BoundingSphere:: extend_other(BoundingVolume *other) const { return other->extend_by_sphere(this); } +//////////////////////////////////////////////////////////////////// +// Function: BoundingSphere::around_other +// Access: Protected, Virtual +// Description: +//////////////////////////////////////////////////////////////////// bool BoundingSphere:: around_other(BoundingVolume *other, const BoundingVolume **first, @@ -122,12 +179,22 @@ around_other(BoundingVolume *other, return other->around_spheres(first, last); } +//////////////////////////////////////////////////////////////////// +// Function: BoundingSphere::contains_other +// Access: Protected, Virtual +// Description: +//////////////////////////////////////////////////////////////////// int BoundingSphere:: contains_other(const BoundingVolume *other) const { return other->contains_sphere(this); } +//////////////////////////////////////////////////////////////////// +// Function: BoundingSphere::extend_by_point +// Access: Protected, Virtual +// Description: +//////////////////////////////////////////////////////////////////// bool BoundingSphere:: extend_by_point(const LPoint3f &point) { nassertr(!point.is_nan(), false); @@ -146,6 +213,11 @@ extend_by_point(const LPoint3f &point) { return true; } +//////////////////////////////////////////////////////////////////// +// Function: BoundingSphere::extend_by_sphere +// Access: Protected, Virtual +// Description: +//////////////////////////////////////////////////////////////////// bool BoundingSphere:: extend_by_sphere(const BoundingSphere *sphere) { nassertr(!sphere->is_empty() && !sphere->is_infinite(), false); @@ -163,39 +235,76 @@ extend_by_sphere(const BoundingSphere *sphere) { return true; } +//////////////////////////////////////////////////////////////////// +// Function: BoundingSphere::extend_by_box +// Access: Protected, Virtual +// Description: +//////////////////////////////////////////////////////////////////// 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()) { _center = (min1 + max1) * 0.5f; _radius = length(LVector3f(max1 - _center)); _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; } +//////////////////////////////////////////////////////////////////// +// 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:: around_points(const LPoint3f *first, const LPoint3f *last) { 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. const LPoint3f *p = first; @@ -279,42 +388,66 @@ around_points(const LPoint3f *first, const LPoint3f *last) { return true; } +//////////////////////////////////////////////////////////////////// +// Function: BoundingSphere::around_spheres +// Access: Protected, Virtual +// Description: +//////////////////////////////////////////////////////////////////// bool BoundingSphere:: around_spheres(const BoundingVolume **first, const BoundingVolume **last) { return around_finite(first, last); } +//////////////////////////////////////////////////////////////////// +// Function: BoundingSphere::around_boxes +// Access: Protected, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +bool BoundingSphere:: +around_boxes(const BoundingVolume **first, + const BoundingVolume **last) { + return around_finite(first, last); +} + +//////////////////////////////////////////////////////////////////// +// Function: BoundingSphere::around_hexahedrons +// Access: Protected, Virtual +// Description: +//////////////////////////////////////////////////////////////////// bool BoundingSphere:: around_hexahedrons(const BoundingVolume **first, const BoundingVolume **last) { return around_finite(first, last); } +//////////////////////////////////////////////////////////////////// +// Function: BoundingSphere::around_finite +// Access: Protected +// Description: +//////////////////////////////////////////////////////////////////// bool BoundingSphere:: 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. + // 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. const BoundingVolume **p = first; nassertr(!(*p)->is_empty() && !(*p)->is_infinite(), false); - nassertr((*p)->is_of_type(FiniteBoundingVolume::get_class_type()), false); const FiniteBoundingVolume *vol = DCAST(FiniteBoundingVolume, *p); LPoint3f min_box = vol->get_min(); 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) { 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); LPoint3f min1 = vol->get_min(); LPoint3f max1 = vol->get_max(); @@ -325,8 +458,8 @@ around_finite(const BoundingVolume **first, max(max_box[1], max1[1]), 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. _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); } 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; for (p = first; p != last; ++p) { 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); float dist = length(sphere->_center - _center); _radius = max(_radius, dist + sphere->_radius); + } 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; } +//////////////////////////////////////////////////////////////////// +// Function: BoundingSphere::contains_point +// Access: Protected, Virtual +// Description: +//////////////////////////////////////////////////////////////////// int BoundingSphere:: contains_point(const LPoint3f &point) const { 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:: contains_lineseg(const LPoint3f &a, const LPoint3f &b) const { 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 // Access: Protected, Virtual diff --git a/panda/src/mathutil/boundingSphere.h b/panda/src/mathutil/boundingSphere.h index 02d7d4635c..f785066ebb 100644 --- a/panda/src/mathutil/boundingSphere.h +++ b/panda/src/mathutil/boundingSphere.h @@ -40,6 +40,7 @@ public: 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); @@ -60,6 +61,7 @@ protected: 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); @@ -67,6 +69,8 @@ protected: 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, @@ -76,6 +80,7 @@ protected: 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; diff --git a/panda/src/mathutil/boundingVolume.I b/panda/src/mathutil/boundingVolume.I index a002ec4360..d8f3789526 100644 --- a/panda/src/mathutil/boundingVolume.I +++ b/panda/src/mathutil/boundingVolume.I @@ -28,7 +28,7 @@ BoundingVolume() { //////////////////////////////////////////////////////////////////// // Function: BoundingVolume::is_empty -// Access: Public +// Access: Published // Description: Any kind of volume might be empty. This is a // degenerate volume that contains no points; it's not // the same as, for instance, a sphere with radius zero, @@ -42,7 +42,7 @@ is_empty() const { //////////////////////////////////////////////////////////////////// // Function: BoundingVolume::is_infinite -// Access: Public +// Access: Published // Description: The other side of the empty coin is an infinite // volume. This is a degenerate state of a normally // finite volume that contains all points. (Note that @@ -63,7 +63,7 @@ is_infinite() const { //////////////////////////////////////////////////////////////////// // Function: BoundingVolume::set_infinite -// Access: Public +// Access: Published // Description: Marks the volume as infinite, even if it is normally // finite. You can think of this as an infinite // extend_by() operation. @@ -75,7 +75,7 @@ set_infinite() { //////////////////////////////////////////////////////////////////// // Function: BoundingVolume::extend_by -// Access: Public +// Access: Published // Description: Increases the size of the volume to include the given // volume. //////////////////////////////////////////////////////////////////// @@ -96,7 +96,7 @@ extend_by(const BoundingVolume *vol) { //////////////////////////////////////////////////////////////////// // Function: BoundingVolume::contains -// Access: Public +// Access: Published // Description: Returns the appropriate set of IntersectionFlags to // indicate the amount of intersection with the // indicated volume. diff --git a/panda/src/mathutil/boundingVolume.cxx b/panda/src/mathutil/boundingVolume.cxx index c0e1a88744..e96c8069f1 100644 --- a/panda/src/mathutil/boundingVolume.cxx +++ b/panda/src/mathutil/boundingVolume.cxx @@ -26,7 +26,7 @@ TypeHandle BoundingVolume::_type_handle; //////////////////////////////////////////////////////////////////// // Function: BoundingVolume::around -// Access: Public +// Access: Published // Description: Resets the volume to enclose only the volumes // indicated. Returns true if successful, false if the // 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 -// Access: Public +// Access: Published // Description: //////////////////////////////////////////////////////////////////// void BoundingVolume:: @@ -80,6 +80,27 @@ write(ostream &out, int indent_level) const { 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 // Access: Protected, Virtual @@ -89,6 +110,23 @@ write(ostream &out, int indent_level) const { //////////////////////////////////////////////////////////////////// bool BoundingVolume:: 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; return false; } @@ -102,6 +140,8 @@ extend_by_sphere(const BoundingSphere *) { //////////////////////////////////////////////////////////////////// bool BoundingVolume:: extend_by_hexahedron(const BoundingHexahedron *) { + mathutil_cat.warning() + << get_type() << "::extend_by_hexahedron() called\n"; _flags = F_infinite; return false; } @@ -115,6 +155,8 @@ extend_by_hexahedron(const BoundingHexahedron *) { //////////////////////////////////////////////////////////////////// bool BoundingVolume:: extend_by_line(const BoundingLine *) { + mathutil_cat.warning() + << get_type() << "::extend_by_line() called\n"; _flags = F_infinite; return false; } @@ -128,6 +170,8 @@ extend_by_line(const BoundingLine *) { //////////////////////////////////////////////////////////////////// bool BoundingVolume:: extend_by_plane(const BoundingPlane *) { + mathutil_cat.warning() + << get_type() << "::extend_by_plane() called\n"; _flags = F_infinite; return false; } @@ -141,6 +185,23 @@ extend_by_plane(const BoundingPlane *) { //////////////////////////////////////////////////////////////////// bool 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; return false; } @@ -154,6 +215,8 @@ around_spheres(const BoundingVolume **, const BoundingVolume **) { //////////////////////////////////////////////////////////////////// bool BoundingVolume:: around_hexahedrons(const BoundingVolume **, const BoundingVolume **) { + mathutil_cat.warning() + << get_type() << "::around_hexahedrons() called\n"; _flags = F_infinite; return false; } @@ -174,6 +237,9 @@ around_lines(const BoundingVolume **, const BoundingVolume **) { return true; } + mathutil_cat.warning() + << get_type() << "::around_lines() called\n"; + // Otherwise, we might do better, and we require each class to // define a function. If we get here, the function isn't defined, // so we return false to indicate this. @@ -196,6 +262,9 @@ around_planes(const BoundingVolume **, const BoundingVolume **) { return true; } + mathutil_cat.warning() + << get_type() << "::around_planes() called\n"; + // Otherwise, we might do better, and we require each class to // define a function. If we get here, the function isn't defined, // so we return false to indicate this. @@ -211,6 +280,22 @@ around_planes(const BoundingVolume **, const BoundingVolume **) { //////////////////////////////////////////////////////////////////// int BoundingVolume:: 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; } @@ -223,6 +308,8 @@ contains_sphere(const BoundingSphere *) const { //////////////////////////////////////////////////////////////////// int BoundingVolume:: contains_hexahedron(const BoundingHexahedron *) const { + mathutil_cat.warning() + << get_type() << "::contains_hexahedron() called\n"; return IF_dont_understand; } @@ -235,6 +322,8 @@ contains_hexahedron(const BoundingHexahedron *) const { //////////////////////////////////////////////////////////////////// int BoundingVolume:: contains_line(const BoundingLine *) const { + mathutil_cat.warning() + << get_type() << "::contains_line() called\n"; return IF_dont_understand; } @@ -247,5 +336,42 @@ contains_line(const BoundingLine *) const { //////////////////////////////////////////////////////////////////// int BoundingVolume:: contains_plane(const BoundingPlane *) const { + mathutil_cat.warning() + << get_type() << "::contains_plane() called\n"; 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; +} + diff --git a/panda/src/mathutil/boundingVolume.h b/panda/src/mathutil/boundingVolume.h index b18b0a5f78..b3bb932405 100644 --- a/panda/src/mathutil/boundingVolume.h +++ b/panda/src/mathutil/boundingVolume.h @@ -26,6 +26,7 @@ #include "deletedChain.h" class BoundingSphere; +class BoundingBox; class BoundingHexahedron; class BoundingLine; class BoundingPlane; @@ -42,8 +43,10 @@ class BoundingPlane; // sort. //////////////////////////////////////////////////////////////////// class EXPCL_PANDA BoundingVolume : public TypedReferenceCount { -PUBLISHED: +public: INLINE_MATHUTIL BoundingVolume(); + +PUBLISHED: virtual BoundingVolume *make_copy() const=0; INLINE_MATHUTIL bool is_empty() const; @@ -96,6 +99,19 @@ PUBLISHED: virtual void output(ostream &out) const=0; 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: enum Flags { F_empty = 0x01, @@ -118,12 +134,15 @@ protected: // These functions are the second dispatch point. They actually do // the work. 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_line(const BoundingLine *line); virtual bool extend_by_plane(const BoundingPlane *plane); 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); virtual bool around_lines(const BoundingVolume **first, @@ -132,6 +151,7 @@ protected: const BoundingVolume **last); virtual int contains_sphere(const BoundingSphere *sphere) const; + virtual int contains_box(const BoundingBox *box) const; virtual int contains_hexahedron(const BoundingHexahedron *hexahedron) const; virtual int contains_line(const BoundingLine *line) const; virtual int contains_plane(const BoundingPlane *plane) const; @@ -155,6 +175,7 @@ private: static TypeHandle _type_handle; friend class BoundingSphere; + friend class BoundingBox; friend class BoundingHexahedron; friend class BoundingLine; friend class BoundingPlane; @@ -164,4 +185,7 @@ INLINE_MATHUTIL ostream &operator << (ostream &out, const BoundingVolume &bound) #include "boundingVolume.I" +EXPCL_PANDA ostream &operator << (ostream &out, BoundingVolume::BoundsType type); +EXPCL_PANDA istream &operator >> (istream &in, BoundingVolume::BoundsType &type); + #endif diff --git a/panda/src/mathutil/config_mathutil.cxx b/panda/src/mathutil/config_mathutil.cxx index 8f6012bab5..a1edba51a5 100644 --- a/panda/src/mathutil/config_mathutil.cxx +++ b/panda/src/mathutil/config_mathutil.cxx @@ -22,6 +22,7 @@ #include "finiteBoundingVolume.h" #include "omniBoundingVolume.h" #include "boundingSphere.h" +#include "boundingBox.h" #include "boundingHexahedron.h" #include "boundingLine.h" #include "boundingPlane.h" @@ -44,9 +45,16 @@ ConfigVariableDouble fft_exponent ConfigVariableDouble fft_error_threshold ("fft-error-threshold", 0.2); +ConfigVariableEnum 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) { BoundingHexahedron::init_type(); BoundingSphere::init_type(); + BoundingBox::init_type(); BoundingVolume::init_type(); FiniteBoundingVolume::init_type(); GeometricBoundingVolume::init_type(); diff --git a/panda/src/mathutil/config_mathutil.h b/panda/src/mathutil/config_mathutil.h index d8bcee9474..1139f3e06b 100644 --- a/panda/src/mathutil/config_mathutil.h +++ b/panda/src/mathutil/config_mathutil.h @@ -22,6 +22,8 @@ #include "pandabase.h" #include "notifyCategoryProxy.h" #include "configVariableDouble.h" +#include "configVariableEnum.h" +#include "boundingVolume.h" NotifyCategoryDecl(mathutil, EXPCL_PANDA, EXPTP_PANDA); @@ -29,6 +31,7 @@ extern ConfigVariableDouble fft_offset; extern ConfigVariableDouble fft_factor; extern ConfigVariableDouble fft_exponent; extern ConfigVariableDouble fft_error_threshold; +extern EXPCL_PANDA ConfigVariableEnum bounds_type; #endif diff --git a/panda/src/mathutil/finiteBoundingVolume.cxx b/panda/src/mathutil/finiteBoundingVolume.cxx index e25d305da3..24265bf935 100644 --- a/panda/src/mathutil/finiteBoundingVolume.cxx +++ b/panda/src/mathutil/finiteBoundingVolume.cxx @@ -17,5 +17,28 @@ //////////////////////////////////////////////////////////////////// #include "finiteBoundingVolume.h" +#include "boundingBox.h" 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(); +} diff --git a/panda/src/mathutil/finiteBoundingVolume.h b/panda/src/mathutil/finiteBoundingVolume.h index 7963637910..b81374512a 100644 --- a/panda/src/mathutil/finiteBoundingVolume.h +++ b/panda/src/mathutil/finiteBoundingVolume.h @@ -34,7 +34,7 @@ class EXPCL_PANDA FiniteBoundingVolume : public GeometricBoundingVolume { PUBLISHED: virtual LPoint3f get_min() const=0; virtual LPoint3f get_max() const=0; - + virtual float get_volume() const; public: static TypeHandle get_class_type() { diff --git a/panda/src/mathutil/mathutil_composite1.cxx b/panda/src/mathutil/mathutil_composite1.cxx index 74baf2879c..9097ad375d 100644 --- a/panda/src/mathutil/mathutil_composite1.cxx +++ b/panda/src/mathutil/mathutil_composite1.cxx @@ -1,5 +1,6 @@ #include "boundingHexahedron.cxx" #include "boundingLine.cxx" +#include "boundingBox.cxx" #include "boundingPlane.cxx" #include "boundingSphere.cxx" #include "boundingVolume.cxx" diff --git a/panda/src/mathutil/omniBoundingVolume.cxx b/panda/src/mathutil/omniBoundingVolume.cxx index ac08338557..0d11adedeb 100644 --- a/panda/src/mathutil/omniBoundingVolume.cxx +++ b/panda/src/mathutil/omniBoundingVolume.cxx @@ -117,6 +117,16 @@ extend_by_sphere(const BoundingSphere *) { 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 // Access: Public, Virtual @@ -148,6 +158,17 @@ around_spheres(const BoundingVolume **, return true; } +//////////////////////////////////////////////////////////////////// +// Function: OmniBoundingVolume::around_boxes +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +bool OmniBoundingVolume:: +around_boxes(const BoundingVolume **, + const BoundingVolume **) { + return true; +} + //////////////////////////////////////////////////////////////////// // Function: OmniBoundingVolume::around_hexahedrons // Access: Public, Virtual @@ -189,6 +210,16 @@ contains_sphere(const BoundingSphere *) const { 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 // Access: Public, Virtual diff --git a/panda/src/mathutil/omniBoundingVolume.h b/panda/src/mathutil/omniBoundingVolume.h index 0e59b2bcd6..4359754c44 100644 --- a/panda/src/mathutil/omniBoundingVolume.h +++ b/panda/src/mathutil/omniBoundingVolume.h @@ -50,12 +50,15 @@ protected: 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); 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); @@ -63,6 +66,7 @@ protected: 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; public: static TypeHandle get_class_type() { diff --git a/panda/src/pgraph/cullTraverser.cxx b/panda/src/pgraph/cullTraverser.cxx index 8ed62aab64..b1676e2869 100644 --- a/panda/src/pgraph/cullTraverser.cxx +++ b/panda/src/pgraph/cullTraverser.cxx @@ -30,10 +30,12 @@ #include "geomNode.h" #include "config_pgraph.h" #include "boundingSphere.h" +#include "boundingBox.h" #include "boundingHexahedron.h" #include "portalClipper.h" #include "geom.h" #include "geomTristrips.h" +#include "geomTriangles.h" #include "geomLinestrips.h" #include "geomVertexWriter.h" @@ -426,6 +428,50 @@ make_bounds_viz(const BoundingVolume *vol) { geom = new Geom(vdata); 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 { pgraph_cat.warning() << "Don't know how to draw a representation of " diff --git a/panda/src/pgraph/geomNode.cxx b/panda/src/pgraph/geomNode.cxx index b682aebfe9..7f571f46b4 100644 --- a/panda/src/pgraph/geomNode.cxx +++ b/panda/src/pgraph/geomNode.cxx @@ -33,6 +33,8 @@ #include "pset.h" #include "config_pgraph.h" #include "graphicsStateGuardianBase.h" +#include "boundingBox.h" +#include "config_mathutil.h" TypeHandle GeomNode::_type_handle; @@ -682,30 +684,46 @@ compute_internal_bounds(PandaNode::BoundsData *bdata, int pipeline_stage, Thread *current_thread) const { 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 child_volumes; - - CDStageReader cdata(_cycler, pipeline_stage, current_thread); + bool all_box = true; GeomList::const_iterator gi; CPT(GeomList) geoms = cdata->get_geoms(); for (gi = geoms->begin(); gi != geoms->end(); ++gi) { const GeomEntry &entry = (*gi); 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(); } - 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_bounds_stale = false; } diff --git a/panda/src/pgraph/geomNode.h b/panda/src/pgraph/geomNode.h index a2da476bd8..5e1e67bad5 100644 --- a/panda/src/pgraph/geomNode.h +++ b/panda/src/pgraph/geomNode.h @@ -143,6 +143,7 @@ private: typedef CycleDataReader CDReader; typedef CycleDataWriter CDWriter; typedef CycleDataStageReader CDStageReader; + typedef CycleDataLockedStageReader CDLockedStageReader; typedef CycleDataStageWriter CDStageWriter; public: diff --git a/panda/src/pgraph/pandaNode.cxx b/panda/src/pgraph/pandaNode.cxx index 095c2fd018..d8346af84a 100644 --- a/panda/src/pgraph/pandaNode.cxx +++ b/panda/src/pgraph/pandaNode.cxx @@ -27,7 +27,9 @@ #include "accumulatedAttribs.h" #include "clipPlaneAttrib.h" #include "boundingSphere.h" +#include "boundingBox.h" #include "pStatTimer.h" +#include "config_mathutil.h" // This category is just temporary for debugging convenience. NotifyCategoryDecl(drawmask, EXPCL_PANDA, EXPTP_PANDA); @@ -3413,10 +3415,17 @@ update_bounds(int pipeline_stage, PandaNode::CDLockedStageReader &cdata) { pvector child_volumes_ref; pvector child_volumes; + bool all_box = true; CPT(BoundingVolume) internal_bounds = 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; @@ -3466,8 +3475,13 @@ update_bounds(int pipeline_stage, PandaNode::CDLockedStageReader &cdata) { } 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; } 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); - 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; } } @@ -3543,21 +3562,33 @@ update_bounds(int pipeline_stage, PandaNode::CDLockedStageReader &cdata) { cdataw->_off_clip_planes = off_clip_planes; 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); - if (!transform->is_identity()) { - gbv->xform(transform->get_mat()); - } + PT(GeometricBoundingVolume) gbv; - cdataw->_external_bounds = 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. + + 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; if (drawmask_cat.is_debug()) {