aboutsummaryrefslogtreecommitdiff
path: root/dep/g3dlite/source/Box.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dep/g3dlite/source/Box.cpp')
-rw-r--r--dep/g3dlite/source/Box.cpp394
1 files changed, 237 insertions, 157 deletions
diff --git a/dep/g3dlite/source/Box.cpp b/dep/g3dlite/source/Box.cpp
index f7c112ae3a5..3ee3dc04013 100644
--- a/dep/g3dlite/source/Box.cpp
+++ b/dep/g3dlite/source/Box.cpp
@@ -1,11 +1,11 @@
/**
- @file Box.cpp
+ \file G3D.lib/source/Box.cpp
Box class
- @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+ \maintainer Morgan McGuire, http://graphics.cs.williams.edu, Michael Mara
- @created 2001-06-02
- @edited 2006-02-05
+ \created 2001-06-02
+ \edited 2013-04-13
*/
#include "G3D/Box.h"
@@ -13,126 +13,207 @@
#include "G3D/Plane.h"
#include "G3D/AABox.h"
#include "G3D/CoordinateFrame.h"
+#include "G3D/vectorMath.h"
+#include "G3D/Any.h"
namespace G3D {
-/**
- Sets a field on four vertices. Used by the constructor.
- */
-#define setMany(i0, i1, i2, i3, field, extreme) \
- _corner[i0].field = _corner[i1].field = \
- _corner[i2].field = _corner[i3].field = \
- (extreme).field
-
-Box::Box() {
+Box::Box() : _area(0), _volume(0) {
+}
+
+
+Box::Box
+ (const Point3& min,
+ const Point3& max) {
+ init(min.min(max), min.max(max));
+}
+
+
+Box::Box(const Point3& min) {
+ init(min, min);
+}
+
+
+Box::Box
+ (const Point3& min,
+ const Point3& max,
+ const CFrame& c) {
+ init(min.min(max), min.max(max));
+ *this = c.toWorldSpace(*this);
}
Box::Box(const AABox& b) {
+ debugAssert(! b.isEmpty());
init(b.low(), b.high());
}
-Box::Box(class BinaryInput& b) {
- deserialize(b);
+
+Box::Box(BinaryInput& b) {
+ deserialize(b);
}
-void Box::serialize(class BinaryOutput& b) const {
- int i;
- for (i = 0; i < 8; ++i) {
- _corner[i].serialize(b);
- }
+Box::Box(const Any& a) {
+ if (a.name() == "Box::inf") {
+ *this = Box::inf();
+ } else {
+ a.verifyName("Box", "AABox", "Point3");
+
+ if (a.name() == "Point3") {
+ *this = Box(Point3(a));
+ } else if (a.size() == 1) {
+ // Single point
+ *this = Box(Point3(a[0]));
+ } else if (a.size() == 2) {
+ *this = Box(Point3(a[0]), Point3(a[1]));
+ } else {
+ // Oriented box
+ a.verifySize(2);
+ a.verifyName("Box");
+ *this = Box(Point3(a[0]), Point3(a[1]), CFrame(a[2]));
+ }
+ }
+}
+
- // Other state can be reconstructed
+Any Box::toAny() const {
+ if (! isFinite()) {
+ return Any(Any::ARRAY, "Box::inf");
+ } else {
+ CFrame c;
+ getLocalFrame(c);
+ if (c.rotation == Matrix3::identity()) {
+ // Aligned box
+ AABox b;
+ getBounds(b);
+ return b.toAny();
+ } else {
+ // Oriented box
+ Any a(Any::ARRAY, "Box");
+
+ AABox b;
+ c.toObjectSpace(*this).getBounds(b);
+ a.append(b.low(), b.high(), c);
+ return a;
+ }
+ }
}
-void Box::deserialize(class BinaryInput& b) {
- int i;
+Box Box::operator*(float f) const {
+ Box b;
+
+ for (int i = 0; i < 3; ++i) {
+ b._edgeVector[i] = _edgeVector[i] * f;
+ b._center = _center * f;
+ b._area = _area * square(f * f);
+ b._volume = _area * (f * f * f);
+ }
- _center = Vector3::zero();
- for (i = 0; i < 8; ++i) {
- _corner[i].deserialize(b);
- _center += _corner[i];
- }
+ return b;
+}
- _center = _center / 8;
-
- // Reconstruct other state from the corners
- _axis[0] = _corner[5] - _corner[4];
- _axis[1] = _corner[7] - _corner[4];
- _axis[2] = _corner[0] - _corner[4];
+void Box::serialize(BinaryOutput& b) const {
+ int i;
for (i = 0; i < 3; ++i) {
- _extent[i] = _axis[i].magnitude();
- _axis[i] /= _extent[i];
+ _edgeVector[i].serialize(b);
}
+ _center.serialize(b);
- _volume = _extent.x * _extent.y * _extent.z;
-
- _area = 2 *
- (_extent.x * _extent.y +
- _extent.y * _extent.z +
- _extent.z * _extent.x);
+ // Other state can be reconstructed
}
-Box::Box(
- const Vector3& min,
- const Vector3& max) {
+void Box::deserialize(class BinaryInput& b) {
+ int i;
+ for (i = 0; i < 3; ++i) {
+ _edgeVector[i].deserialize(b);
+ }
+ _center.deserialize(b);
- init(min.min(max), min.max(max));
+ float extent0 = extent(0);
+ float extent1 = extent(1);
+ float extent2 = extent(2);
+ _volume = extent0 * extent1 * extent2;
+ _area = 2 *
+ (extent0 * extent1 +
+ extent1 * extent2 +
+ extent2 * extent0);
}
-void Box::init(
- const Vector3& min,
- const Vector3& max) {
+
+void Box::init
+ (const Point3& min,
+ const Point3& max) {
debugAssert(
(min.x <= max.x) &&
(min.y <= max.y) &&
(min.z <= max.z));
- setMany(0, 1, 2, 3, z, max);
- setMany(4, 5, 6, 7, z, min);
-
- setMany(1, 2, 5, 6, x, max);
- setMany(0, 3, 4, 7, x, min);
-
- setMany(3, 2, 6, 7, y, max);
- setMany(0, 1, 5, 4, y, min);
+ _center = (max + min) * 0.5f;
- _extent = max - min;
+ Vector3 bounds = Vector3(max.x - min.x, max.y - min.y, max.z - min.z);
+ _edgeVector[0] = Vector3(bounds.x, 0, 0);
+ _edgeVector[1] = Vector3(0, bounds.y, 0);
+ _edgeVector[2] = Vector3(0, 0, bounds.z);
+ bool finiteExtent = true;
+
+ for (int i = 0; i < 3; ++i) {
+ if (! G3D::isFinite(extent(i))) {
+ finiteExtent = false;
+ // If the extent is infinite along an axis, make the center zero to avoid NaNs
+ _center[i] = 0.0f;
+ }
+ }
- _axis[0] = Vector3::unitX();
- _axis[1] = Vector3::unitY();
- _axis[2] = Vector3::unitZ();
- if (_extent.isFinite()) {
- _volume = _extent.x * _extent.y * _extent.z;
+ if (finiteExtent) {
+ _volume = bounds.x * bounds.y * bounds.z;
} else {
_volume = G3D::finf();
}
- debugAssert(! isNaN(_extent.x));
+ debugAssert(! _edgeVector[0].isNaN());
_area = 2 *
- (_extent.x * _extent.y +
- _extent.y * _extent.z +
- _extent.z * _extent.x);
+ (bounds.x * bounds.y +
+ bounds.y * bounds.z +
+ bounds.z * bounds.x);
+}
- _center = (max + min) * 0.5f;
- // If the extent is infinite along an axis, make the center zero to avoid NaNs
- for (int i = 0; i < 3; ++i) {
- if (! G3D::isFinite(_extent[i])) {
- _center[i] = 0.0f;
- }
+Vector3 Box::corner(int i) const{
+ debugAssert(i < 8);
+ // The corner forms a bit mask (xyz), where a one indicates we should
+ // add half of the corresponding edge vector from center, and a zero indicates
+ // we should subtract it. Note:
+ // 1 = 001
+ // 2 = 010
+ // 4 = 100
+ //
+ // The following bit-hacky code shows this directly:
+ // return _center + ((_edgeVector[0] * ((i&1) - 0.5) +
+ // _edgeVector[1] * (((i>>1)&1) - 0.5) +
+ // _edgeVector[2] * (((i>>2)&1) - 0.5)));
+ // This method is implemented as a swtich statement due to being marginally faster than the bit-hack method
+ // Also, the _center + 0.5f * (...) is repeated every time for similarly speed-based reasons.
+ switch(i) {
+ case 0: return _center + (0.5f * (-_edgeVector[0] - _edgeVector[1] - _edgeVector[2]));
+ case 1: return _center + (0.5f * ( _edgeVector[0] - _edgeVector[1] - _edgeVector[2]));
+ case 2: return _center + (0.5f * (-_edgeVector[0] + _edgeVector[1] - _edgeVector[2]));
+ case 3: return _center + (0.5f * ( _edgeVector[0] + _edgeVector[1] - _edgeVector[2]));
+ case 4: return _center + (0.5f * (-_edgeVector[0] - _edgeVector[1] + _edgeVector[2]));
+ case 5: return _center + (0.5f * ( _edgeVector[0] - _edgeVector[1] + _edgeVector[2]));
+ case 6: return _center + (0.5f * (-_edgeVector[0] + _edgeVector[1] + _edgeVector[2]));
+ default: return _center + (0.5f * ( _edgeVector[0] + _edgeVector[1] + _edgeVector[2]));//case 7
}
+
}
-
float Box::volume() const {
return _volume;
}
@@ -144,11 +225,14 @@ float Box::area() const {
void Box::getLocalFrame(CoordinateFrame& frame) const {
+ const Vector3& axis0 = axis(0);
+ const Vector3& axis1 = axis(1);
+ const Vector3& axis2 = axis(2);
frame.rotation = Matrix3(
- _axis[0][0], _axis[1][0], _axis[2][0],
- _axis[0][1], _axis[1][1], _axis[2][1],
- _axis[0][2], _axis[1][2], _axis[2][2]);
+ axis0[0], axis1[0], axis2[0],
+ axis0[1], axis1[1], axis2[1],
+ axis0[2], axis1[2], axis2[2]);
frame.translation = _center;
}
@@ -161,30 +245,30 @@ CoordinateFrame Box::localFrame() const {
}
-void Box::getFaceCorners(int f, Vector3& v0, Vector3& v1, Vector3& v2, Vector3& v3) const {
+void Box::getFaceCorners(int f, Point3& v0, Point3& v1, Point3& v2, Point3& v3) const {
switch (f) {
case 0:
- v0 = _corner[0]; v1 = _corner[1]; v2 = _corner[2]; v3 = _corner[3];
+ v0 = corner(0); v1 = corner(2); v2 = corner(3); v3 = corner(1);
break;
case 1:
- v0 = _corner[1]; v1 = _corner[5]; v2 = _corner[6]; v3 = _corner[2];
+ v0 = corner(1); v1 = corner(3); v2 = corner(7); v3 = corner(5);
break;
case 2:
- v0 = _corner[7]; v1 = _corner[6]; v2 = _corner[5]; v3 = _corner[4];
+ v0 = corner(6); v1 = corner(4); v2 = corner(5); v3 = corner(7);
break;
case 3:
- v0 = _corner[2]; v1 = _corner[6]; v2 = _corner[7]; v3 = _corner[3];
+ v0 = corner(3); v1 = corner(2); v2 = corner(6); v3 = corner(7);
break;
case 4:
- v0 = _corner[3]; v1 = _corner[7]; v2 = _corner[4]; v3 = _corner[0];
+ v0 = corner(2); v1 = corner(0); v2 = corner(4); v3 = corner(6);
break;
case 5:
- v0 = _corner[1]; v1 = _corner[0]; v2 = _corner[4]; v3 = _corner[5];
+ v0 = corner(1); v1 = corner(5); v2 = corner(4); v3 = corner(0);
break;
default:
@@ -196,8 +280,8 @@ void Box::getFaceCorners(int f, Vector3& v0, Vector3& v1, Vector3& v2, Vector3&
int Box::dummy = 0;
-bool Box::culledBy(
- const Array<Plane>& plane,
+bool Box::culledBy
+ (const Array<Plane>& plane,
int& cullingPlane,
const uint32 _inMask,
uint32& childMask) const {
@@ -208,13 +292,11 @@ bool Box::culledBy(
childMask = 0;
// See if there is one plane for which all of the
- // vertices are in the negative half space.
+ // vertices are in the negative half space.
for (int p = 0; p < plane.size(); ++p) {
- // Only test planes that are not masked
- if ((inMask & 1) != 0) {
-
- Vector3 corner;
+ // Only test planes that are not masked
+ if ((inMask & 1) != 0) {
int numContained = 0;
int v = 0;
@@ -222,93 +304,94 @@ bool Box::culledBy(
// We can early-out only if we have found one point on each
// side of the plane (i.e. if we are straddling). That
// occurs when (numContained < v) && (numContained > 0)
- for (v = 0; (v < 8) && ((numContained == v) || (numContained == 0)); ++v) {
- if (plane[p].halfSpaceContains(_corner[v])) {
+ for (v = 0; (v < 8) && ((numContained == v) || (numContained == 0)); ++v) {
+ if (plane[p].halfSpaceContains(corner(v))) {
++numContained;
}
- }
+ }
- if (numContained == 0) {
- // Plane p culled the box
- cullingPlane = p;
+ if (numContained == 0) {
+ // Plane p culled the box
+ cullingPlane = p;
// The caller should not recurse into the children,
// since the parent is culled. If they do recurse,
// make them only test against this one plane, which
// will immediately cull the volume.
childMask = 1 << p;
- return true;
+ return true;
} else if (numContained < v) {
// The bounding volume straddled the plane; we have
// to keep testing against this plane
childMask |= (1 << p);
}
- }
+ }
// Move on to the next bit.
- inMask = inMask >> 1;
+ inMask = inMask >> 1;
}
// None of the planes could cull this box
- cullingPlane = -1;
+ cullingPlane = -1;
return false;
}
-bool Box::culledBy(
- const Array<Plane>& plane,
- int& cullingPlane,
- const uint32 _inMask) const {
+bool Box::culledBy
+ (const Array<Plane>& plane,
+ int& cullingPlane,
+ const uint32 _inMask) const {
- uint32 inMask = _inMask;
- assert(plane.size() < 31);
+ uint32 inMask = _inMask;
+ assert(plane.size() < 31);
// See if there is one plane for which all of the
- // vertices are in the negative half space.
+ // vertices are in the negative half space.
for (int p = 0; p < plane.size(); ++p) {
- // Only test planes that are not masked
- if ((inMask & 1) != 0) {
-
- bool culled = true;
+ // Only test planes that are not masked
+ if ((inMask & 1) != 0) {
+
+ bool culled = true;
int v;
- // Assume this plane culls all points. See if there is a point
- // not culled by the plane... early out when at least one point
+ // Assume this plane culls all points. See if there is a point
+ // not culled by the plane... early out when at least one point
// is in the positive half space.
- for (v = 0; (v < 8) && culled; ++v) {
+ for (v = 0; (v < 8) && culled; ++v) {
culled = ! plane[p].halfSpaceContains(corner(v));
- }
+ }
- if (culled) {
- // Plane p culled the box
- cullingPlane = p;
+ if (culled) {
+ // Plane p culled the box
+ cullingPlane = p;
- return true;
+ return true;
}
- }
+ }
// Move on to the next bit.
- inMask = inMask >> 1;
+ inMask = inMask >> 1;
}
// None of the planes could cull this box
- cullingPlane = -1;
+ cullingPlane = -1;
return false;
}
-bool Box::contains(
- const Vector3& point) const {
+bool Box::contains
+ (const Point3& point) const {
// Form axes from three edges, transform the point into that
// space, and perform 3 interval tests
-
- Vector3 u = _corner[4] - _corner[0];
- Vector3 v = _corner[3] - _corner[0];
- Vector3 w = _corner[1] - _corner[0];
+ // TODO: Write in a more intuitive way. I left it as it was before after figuring it out, but
+ // this should make no sense to someone who is just starting to read this code.
+ const Vector3& u = _edgeVector[2];
+ const Vector3& v = _edgeVector[1];
+ const Vector3& w = _edgeVector[0];
Matrix3 M = Matrix3(u.x, v.x, w.x,
u.y, v.y, w.y,
@@ -316,7 +399,7 @@ bool Box::contains(
// M^-1 * (point - _corner[0]) = point in unit cube's object space
// compute the inverse of M
- Vector3 osPoint = M.inverse() * (point - _corner[0]);
+ Vector3 osPoint = M.inverse() * (point - corner(0));
return
(osPoint.x >= 0) &&
@@ -327,13 +410,13 @@ bool Box::contains(
(osPoint.z <= 1);
}
-#undef setMany
+
void Box::getRandomSurfacePoint(Vector3& P, Vector3& N) const {
- float aXY = _extent.x * _extent.y;
- float aYZ = _extent.y * _extent.z;
- float aZX = _extent.z * _extent.x;
+ float aXY = extent(0) * extent(1);
+ float aYZ = extent(1) * extent(2);
+ float aZX = extent(2) * extent(0);
float r = (float)uniformRandom(0, aXY + aYZ + aZX);
@@ -343,20 +426,20 @@ void Box::getRandomSurfacePoint(Vector3& P, Vector3& N) const {
// The probability of choosing a given face is proportional to
// its area.
if (r < aXY) {
- P = _axis[0] * (float)uniformRandom(-0.5, 0.5) * _extent.x +
- _axis[1] * (float)uniformRandom(-0.5, 0.5) * _extent.y +
- _center + _axis[2] * d * _extent.z * 0.5f;
- N = _axis[2] * d;
+ P = _edgeVector[0] * (float)uniformRandom(-0.5, 0.5) +
+ _edgeVector[1] * (float)uniformRandom(-0.5, 0.5) +
+ _center + _edgeVector[2] * d * 0.5f;
+ N = axis(2) * d;
} else if (r < aYZ) {
- P = _axis[1] * (float)uniformRandom(-0.5, 0.5) * _extent.y +
- _axis[2] * (float)uniformRandom(-0.5, 0.5) * _extent.z +
- _center + _axis[0] * d * _extent.x * 0.5f;
- N = _axis[0] * d;
+ P = _edgeVector[1] * (float)uniformRandom(-0.5, 0.5) +
+ _edgeVector[2] * (float)uniformRandom(-0.5, 0.5) +
+ _center + _edgeVector[0] * d * 0.5f;
+ N = axis(0) * d;
} else {
- P = _axis[2] * (float)uniformRandom(-0.5, 0.5) * _extent.z +
- _axis[0] *(float) uniformRandom(-0.5, 0.5) * _extent.x +
- _center + _axis[1] * d * _extent.y * 0.5f;
- N = _axis[1] * d;
+ P = _edgeVector[2] * (float)uniformRandom(-0.5, 0.5) +
+ _edgeVector[0] *(float) uniformRandom(-0.5, 0.5) +
+ _center + _edgeVector[1] * d * 0.5f;
+ N = axis(1) * d;
}
}
@@ -365,28 +448,25 @@ Vector3 Box::randomInteriorPoint() const {
Vector3 sum = _center;
for (int a = 0; a < 3; ++a) {
- sum += _axis[a] * (float)uniformRandom(-0.5, 0.5) * _extent[a];
+ sum += _edgeVector[a] * (float)uniformRandom(-0.5, 0.5);
}
return sum;
}
+
Box Box::inf() {
return Box(-Vector3::inf(), Vector3::inf());
}
-void Box::getBounds(class AABox& aabb) const {
-
- Vector3 lo = _corner[0];
- Vector3 hi = lo;
- for (int v = 1; v < 8; ++v) {
- const Vector3& C = _corner[v];
- lo = lo.min(C);
- hi = hi.max(C);
+void Box::getBounds(AABox& aabb) const {
+ debugAssert(! _edgeVector[0].isNaN());
+ debugAssert(! _center.isNaN());
+ aabb = AABox::empty();
+ for (int i = 0; i < 8; ++i) {
+ aabb.merge(corner(i));
}
-
- aabb = AABox(lo, hi);
}