Implement more OBB methods

This commit is contained in:
2024-03-19 14:20:12 -04:00
parent 9f60f296c6
commit cd58676ece
2 changed files with 307 additions and 6 deletions

View File

@@ -6,6 +6,11 @@
namespace J3ML::Geometry {
/// A 3D arbitrarily oriented bounding box
// This data structure represents a box in 3D space. The local axes of this box can be arbitrarily oriented/rotated
// with respect to the global world coordinate system. This allows OBBs to more tightly bound objects than AABBs do,
// which always align with the world space axes. This flexibility has the drawback that the geometry tests and operations
// involving OBBs are more costly, and representing an OBB in memory takes more space (15 floats vs 6 floats)
class OBB : public Shape
{
public:
@@ -16,7 +21,9 @@ namespace J3ML::Geometry {
// Specifies normalized direction vectors for the local axes
Vector3 axis[3];
/// Default constructor that does not initialize any member values.
OBB() {}
// Constructs an OBB by explicitly initializing all member values
OBB(const Vector3& pos, const Vector3& radii, const Vector3& axis0, const Vector3& axis1, const Vector3& axis2);
OBB(const AABB& aabb);
inline static int NumFaces() { return 6; }
@@ -24,7 +31,7 @@ namespace J3ML::Geometry {
inline static int NumVertices() { return 8; }
Polyhedron ToPolyhedron() const;
//PBVolume<6> ToPBVolume() const;
AABB MinimalEnclosingAABB() const
{
AABB aabb;
@@ -38,6 +45,8 @@ namespace J3ML::Geometry {
Vector3 HalfSize() const;
Vector3 Diagonal() const;
Vector3 HalfDiagonal() const;
void Transform(const Matrix3x3& transform);
void Transform(const Matrix4x4& transform);
bool IsFinite() const;
bool IsDegenerate() const;
Vector3 CenterPoint() const;
@@ -45,9 +54,31 @@ namespace J3ML::Geometry {
Vector3 AnyPointFast() const;
float Volume();
float SurfaceArea();
float Volume() const;
float SurfaceArea() const;
Geometry::LineSegment Edge(int edgeIndex) const;
Vector3 CornerPoint(int cornerIndex) const;
Vector3 PointInside(float x, float y, float z) const;
Vector3 PointInside(const Vector3& pt) const;
void ProjectToAxis(const Vector3 &direction, float &outMin, float &outMax) const;
int UniqueFaceNormals(Vector3 *out) const;
int UniqueEdgeDirections(Vector3 *out) const;
Vector3 PointOnEdge(int edgeIndex, float u) const;
Vector3 FaceCenterPoint(int faceIndex) const;
void GetCornerPoints(Vector3 *outPointArray) const;
void GetFacePlanes(Plane *outPlaneArray) const;
Plane FacePlane(int faceIndex) const;
void ExtremePointsAlongDirection(const Vector3 &dir, const Vector3 *pointArray, int numPoints, int &idxSmallest,
int &idxLargest, float &smallestD, float &largestD);
};
}

View File

@@ -3,6 +3,7 @@
#include <J3ML/Geometry/LineSegment.h>
#include <J3ML/Geometry/Polyhedron.h>
#include <J3ML/Geometry/OBB.h>
#include <J3ML/Geometry/Sphere.h>
namespace J3ML::Geometry
{
@@ -41,8 +42,277 @@ namespace J3ML::Geometry
return p;
}
Vector3 OBB::CornerPoint(int cornerIndex) const {
// TODO: implement
return Vector3();
Sphere OBB::MinimalEnclosingSphere() const {
Sphere s;
s.Position = pos;
s.Radius = HalfDiagonal().Length();
return s;
}
Sphere OBB::MaximalContainedSphere() const {
Sphere s;
s.Position = pos;
s.Radius = r.MinElement();
return s;
}
bool OBB::IsFinite() const {
return pos.IsFinite() && r.IsFinite() && axis[0].IsFinite() && axis[1].IsFinite() && axis[2].IsFinite();
}
bool OBB::IsDegenerate() const
{
return !(r.x > 0.f && r.y > 0.f && r.z > 0.f);
}
Vector3 OBB::CenterPoint() const
{
return pos;
}
Vector3 OBB::PointInside(float x, float y, float z) const
{
assert(0.f <= x && x <= 1.f);
assert(0.f <= y && y <= 1.f);
assert(0.f <= z && z <= 1.f);
return pos + axis[0] * (2.f * r.x * x - r.x)
+ axis[1] * (2.f * r.y * y - r.y)
+ axis[2] * (2.f * r.z * z - r.z);
}
Vector3 OBB::PointInside(const Vector3& pt) const
{
return PointInside(pt.x, pt.y, pt.z);
}
LineSegment OBB::Edge(int edgeIndex) const
{
assert(0 <= edgeIndex && edgeIndex <= 11);
switch(edgeIndex)
{
default: // For release builds where Assert() is disabled, return always the first option if out-of-bounds
case 0: return LineSegment(CornerPoint(0), CornerPoint(1));
case 1: return LineSegment(CornerPoint(0), CornerPoint(2));
case 2: return LineSegment(CornerPoint(0), CornerPoint(4));
case 3: return LineSegment(CornerPoint(1), CornerPoint(3));
case 4: return LineSegment(CornerPoint(1), CornerPoint(5));
case 5: return LineSegment(CornerPoint(2), CornerPoint(3));
case 6: return LineSegment(CornerPoint(2), CornerPoint(6));
case 7: return LineSegment(CornerPoint(3), CornerPoint(7));
case 8: return LineSegment(CornerPoint(4), CornerPoint(5));
case 9: return LineSegment(CornerPoint(4), CornerPoint(6));
case 10: return LineSegment(CornerPoint(5), CornerPoint(7));
case 11: return LineSegment(CornerPoint(6), CornerPoint(7));
}
}
Vector3 OBB::CornerPoint(int cornerIndex) const
{
assert(0 <= cornerIndex && cornerIndex <= 7);
switch(cornerIndex)
{
default: // For release builds, return always the first option if out of bounds
case 0: return pos - r.x * axis[0] - r.y * axis[1] - r.z * axis[2];
case 1: return pos - r.x * axis[0] - r.y * axis[1] + r.z * axis[2];
case 2: return pos - r.x * axis[0] + r.y * axis[1] - r.z * axis[2];
case 3: return pos - r.x * axis[0] + r.y * axis[1] + r.z * axis[2];
case 4: return pos + r.x * axis[0] - r.y * axis[1] - r.z * axis[2];
case 5: return pos + r.x * axis[0] - r.y * axis[1] + r.z * axis[2];
case 6: return pos + r.x * axis[0] + r.y * axis[1] - r.z * axis[2];
case 7: return pos + r.x * axis[0] + r.y * axis[1] + r.z * axis[2];
}
}
Vector3 OBB::ExtremePoint(const Vector3& direction) const
{
Vector3 pt = pos;
pt += axis[0] * (Vector3::Dot(direction, axis[0]) >= 0.f ? r.x : -r.x);
pt += axis[1] * (Vector3::Dot(direction, axis[1]) >= 0.f ? r.y : -r.y);
pt += axis[2] * (Vector3::Dot(direction, axis[2]) >= 0.f ? r.z : -r.z);
return pt;
}
Vector3 OBB::ExtremePoint(const Vector3& direction, float &projectionDistance) const
{
Vector3 extremePoint = ExtremePoint(direction);
projectionDistance = extremePoint.Dot(direction);
return extremePoint;
}
void OBB::ProjectToAxis(const Vector3& direction, float& outMin, float& outMax) const
{
float x = std::abs(Vector3::Dot(direction, axis[0]) * r.x);
float y = std::abs(Vector3::Dot(direction, axis[1]) * r.y);
float z = std::abs(Vector3::Dot(direction, axis[2]) * r.z);
float pt = Vector3::Dot(direction, pos);
outMin = pt - x - y - z;
outMax = pt + x + y + z;
}
int OBB::UniqueFaceNormals(Vector3* out) const
{
out[0] = axis[0];
out[1] = axis[1];
out[2] = axis[2];
return 3;
}
int OBB::UniqueEdgeDirections(Vector3* out) const
{
out[0] = axis[0];
out[1] = axis[1];
out[2] = axis[2];
return 3;
}
Vector3 OBB::PointOnEdge(int edgeIndex, float u) const
{
assert(0 <= edgeIndex && edgeIndex <= 11);
assert(0 <= u && u <= 1.f);
edgeIndex = std::clamp(edgeIndex, 0, 11);
Vector3 d = axis[edgeIndex/4] * (2.f * u - 1.f) * r[edgeIndex/4];
switch(edgeIndex)
{
default:
case 0: return pos - r.y * axis[1] - r.z * axis[2] + d;
case 1: return pos - r.y * axis[1] + r.z * axis[2] + d;
case 2: return pos + r.y * axis[1] - r.z * axis[2] + d;
case 3: return pos + r.y * axis[1] + r.z * axis[2] + d;
case 4: return pos - r.x * axis[0] - r.z * axis[2] + d;
case 5: return pos - r.x * axis[0] + r.z * axis[2] + d;
case 6: return pos + r.x * axis[0] - r.z * axis[2] + d;
case 7: return pos + r.x * axis[0] + r.z * axis[2] + d;
case 8: return pos - r.x * axis[0] - r.y * axis[1] + d;
case 9: return pos - r.x * axis[0] + r.y * axis[1] + d;
case 10: return pos + r.x * axis[0] - r.y * axis[1] + d;
case 11: return pos + r.x * axis[0] + r.y * axis[1] + d;
}
}
Vector3 OBB::FaceCenterPoint(int faceIndex) const
{
assert(0 <= faceIndex && faceIndex <= 6);
switch(faceIndex)
{
default:
case 0: return pos - r.x * axis[0];
case 1: return pos + r.x * axis[0];
case 2: return pos - r.y * axis[1];
case 3: return pos + r.y * axis[1];
case 4: return pos - r.z * axis[2];
case 5: return pos + r.z * axis[2];
}
}
Vector3 OBB::FacePoint(int faceIndex, float u, float v) const
{
assert(0 <= faceIndex && faceIndex <= 5);
assert(0 <= u && u <= 1.f);
assert(0 <= v && v <= 1.f);
int uIdx = faceIndex/2;
int vIdx = (faceIndex/2 + 1) % 3;
Vector3 U = axis[uIdx] * (2.f * u - 1.f) * r[uIdx];
Vector3 V = axis[vIdx] * (2.f * v - 1.f) * r[vIdx];
switch(faceIndex)
{
default:
case 0: return pos - r.z * axis[2] + U + V;
case 1: return pos + r.z * axis[2] + U + V;
case 2: return pos - r.x * axis[0] + U + V;
case 3: return pos + r.x * axis[0] + U + V;
case 4: return pos - r.y * axis[1] + U + V;
case 5: return pos + r.y * axis[1] + U + V;
}
}
Plane OBB::FacePlane(int faceIndex) const
{
assert(0 <= faceIndex && faceIndex <= 5);
switch(faceIndex)
{
default:
case 0: return Plane(FaceCenterPoint(0), -axis[0]);
case 1: return Plane(FaceCenterPoint(1), axis[0]);
case 2: return Plane(FaceCenterPoint(2), -axis[1]);
case 3: return Plane(FaceCenterPoint(3), axis[1]);
case 4: return Plane(FaceCenterPoint(4), -axis[2]);
case 5: return Plane(FaceCenterPoint(5), axis[2]);
}
}
void OBB::GetCornerPoints(Vector3 *outPointArray) const
{
assert(outPointArray);
for (int i = 0; i < 8; ++i)
outPointArray[i] = CornerPoint(i);
}
void OBB::GetFacePlanes(Plane *outPlaneArray) const
{
assert(outPlaneArray);
for (int i = 0; i < 6; ++i)
outPlaneArray[i] = FacePlane(i);
}
void OBB::ExtremePointsAlongDirection(const Vector3& dir, const Vector3* pointArray, int numPoints, int &idxSmallest, int &idxLargest, float &smallestD, float &largestD)
{
assert(pointArray || numPoints == 0);
idxSmallest = idxLargest = 0;
smallestD = INFINITY;
largestD = -INFINITY;
for (int i = 0; i < numPoints; ++i)
{
float d = Vector3::Dot(pointArray[i], dir);
if (d < smallestD)
{
smallestD = d;
idxSmallest = i;
}
if (d > largestD)
{
largestD = d;
idxLargest = i;
}
}
}
Vector3 OBB::Size() const {
return r * 2.f;
}
Vector3 OBB::HalfSize() const {
return r;
}
Vector3 OBB::Diagonal() const {
return 2.f * HalfDiagonal();
}
Vector3 OBB::HalfDiagonal() const {
return axis[0] * r[0] + axis[1] * r[1] + axis[2] * r[2];
}
float OBB::Volume() const {
Vector3 size = Size();
return size.x*size.y*size.z;
}
float OBB::SurfaceArea() const {
const Vector3 size = Size();
return 2.f * (size.x*size.y + size.x*size.z + size.y*size.z);
}
}