diff options
Diffstat (limited to 'dep/g3dlite/source/Box.cpp')
-rw-r--r-- | dep/g3dlite/source/Box.cpp | 394 |
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); } |