open_toontown_panda3d/panda/src/mathutil/boundingBox.cxx

661 lines
20 KiB
C++

// Filename: boundingBox.cxx
// Created by: drose (31May07)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
#include "boundingBox.h"
#include "boundingSphere.h"
#include "boundingHexahedron.h"
#include "boundingLine.h"
#include "boundingPlane.h"
#include "config_mathutil.h"
#include "dcast.h"
#include <math.h>
#include <algorithm>
const int BoundingBox::plane_def[6][3] = {
{ 0, 4, 5 },
{ 4, 6, 7 },
{ 6, 2, 3 },
{ 2, 0, 1 },
{ 1, 5, 7 },
{ 2, 6, 4 },
};
TypeHandle BoundingBox::_type_handle;
////////////////////////////////////////////////////////////////////
// Function: BoundingBox::make_copy
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
BoundingVolume *BoundingBox::
make_copy() const {
return new BoundingBox(*this);
}
////////////////////////////////////////////////////////////////////
// Function: BoundingBox::get_min
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
LPoint3f BoundingBox::
get_min() const {
nassertr(!is_empty(), _min);
nassertr(!is_infinite(), _min);
return _min;
}
////////////////////////////////////////////////////////////////////
// Function: BoundingBox::get_max
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
LPoint3f BoundingBox::
get_max() const {
nassertr(!is_empty(), _max);
nassertr(!is_infinite(), _max);
return _max;
}
////////////////////////////////////////////////////////////////////
// Function: BoundingBox::get_volume
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
float BoundingBox::
get_volume() const {
nassertr(!is_infinite(), 0.0f);
if (is_empty()) {
return 0.0f;
}
// Volume of a box: width x depth x height
return (_max[0] - _min[0]) * (_max[1] - _min[1]) * (_max[2] - _min[2]);
}
////////////////////////////////////////////////////////////////////
// Function: BoundingBox::get_approx_center
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
LPoint3f BoundingBox::
get_approx_center() const {
nassertr(!is_empty(), LPoint3f::zero());
nassertr(!is_infinite(), LPoint3f::zero());
return (_min + _max) * 0.5f;
}
////////////////////////////////////////////////////////////////////
// Function: BoundingBox::xform
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
void BoundingBox::
xform(const LMatrix4f &mat) {
nassertv(!mat.is_nan());
if (!is_empty() && !is_infinite()) {
// We need to transform the eight corners of the cube, and then
// determine the new box.
LPoint3f x = get_point(0) * mat;
LPoint3f n = x;
for (int i = 1; i < 8; ++i) {
LPoint3f p = get_point(i) * mat;
n.set(min(n[0], p[0]), min(n[1], p[1]), min(n[2], p[2]));
x.set(max(x[0], p[0]), max(x[1], p[1]), max(x[2], p[2]));
}
_max = x;
_min = n;
}
}
////////////////////////////////////////////////////////////////////
// Function: BoundingBox::output
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
void BoundingBox::
output(ostream &out) const {
if (is_empty()) {
out << "bbox, empty";
} else if (is_infinite()) {
out << "bbox, infinite";
} else {
out << "bbox, (" << _min << ") to (" << _max << ")";
}
}
////////////////////////////////////////////////////////////////////
// Function: BoundingBox::as_bounding_box
// Access: Public, Virtual
// Description: Virtual downcast method. Returns this object as a
// pointer of the indicated type, if it is in fact that
// type. Returns NULL if it is not that type.
////////////////////////////////////////////////////////////////////
const BoundingBox *BoundingBox::
as_bounding_box() const {
return this;
}
////////////////////////////////////////////////////////////////////
// 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 {
// First, try the quick bounding-box test. If that's decisive,
// we'll accept it.
int result = contains_finite(hexahedron);
if (result == IF_no_intersection || ((result & IF_all) != 0)) {
return result;
}
// If that was inconclusive, we'll look more closely with the
// somewhat more expensive reverse answer.
return hexahedron->contains_box(this) & ~IF_all;
}
////////////////////////////////////////////////////////////////////
// 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;
}
}