Files
FlaxEngine/Source/Engine/Core/Math/OrientedBoundingBox.cpp
2021-01-02 14:28:49 +01:00

354 lines
10 KiB
C++

// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#include "OrientedBoundingBox.h"
#include "BoundingSphere.h"
#include "BoundingBox.h"
#include "Ray.h"
#include "../Types/String.h"
OrientedBoundingBox::OrientedBoundingBox(const BoundingBox& bb)
{
const Vector3 center = bb.Minimum + (bb.Maximum - bb.Minimum) / 2.0f;
Extents = bb.Maximum - center;
Matrix::Translation(center, Transformation);
}
OrientedBoundingBox::OrientedBoundingBox(Vector3 points[], int32 pointCount)
{
ASSERT(points && pointCount > 0);
Vector3 minimum = points[0];
Vector3 maximum = points[0];
for (int32 i = 1; i < pointCount; i++)
{
Vector3::Min(minimum, points[i], minimum);
Vector3::Max(maximum, points[i], maximum);
}
const Vector3 center = minimum + (maximum - minimum) / 2.0f;
Extents = maximum - center;
Matrix::Translation(center, Transformation);
}
String OrientedBoundingBox::ToString() const
{
return String::Format(TEXT("{}"), *this);
}
void OrientedBoundingBox::GetCorners(Vector3 corners[8]) const
{
Vector3 xv = Vector3(Extents.X, 0, 0);
Vector3 yv = Vector3(0, Extents.Y, 0);
Vector3 zv = Vector3(0, 0, Extents.Z);
Vector3::TransformNormal(xv, Transformation, xv);
Vector3::TransformNormal(yv, Transformation, yv);
Vector3::TransformNormal(zv, Transformation, zv);
const Vector3 center = Transformation.GetTranslation();
corners[0] = center + xv + yv + zv;
corners[1] = center + xv + yv - zv;
corners[2] = center - xv + yv - zv;
corners[3] = center - xv + yv + zv;
corners[4] = center + xv - yv + zv;
corners[5] = center + xv - yv - zv;
corners[6] = center - xv - yv - zv;
corners[7] = center - xv - yv + zv;
}
Vector3 OrientedBoundingBox::GetSize() const
{
Vector3 xv = Vector3(Extents.X * 2, 0, 0);
Vector3 yv = Vector3(0, Extents.Y * 2, 0);
Vector3 zv = Vector3(0, 0, Extents.Z * 2);
Vector3::TransformNormal(xv, Transformation, xv);
Vector3::TransformNormal(yv, Transformation, yv);
Vector3::TransformNormal(zv, Transformation, zv);
return Vector3(xv.Length(), yv.Length(), zv.Length());
}
Vector3 OrientedBoundingBox::GetSizeSquared() const
{
Vector3 xv = Vector3(Extents.X * 2, 0, 0);
Vector3 yv = Vector3(0, Extents.Y * 2, 0);
Vector3 zv = Vector3(0, 0, Extents.Z * 2);
Vector3::TransformNormal(xv, Transformation, xv);
Vector3::TransformNormal(yv, Transformation, yv);
Vector3::TransformNormal(zv, Transformation, zv);
return Vector3(xv.LengthSquared(), yv.LengthSquared(), zv.LengthSquared());
}
BoundingBox OrientedBoundingBox::GetBoundingBox() const
{
BoundingBox result;
Vector3 corners[8];
GetCorners(corners);
BoundingBox::FromPoints(corners, 8, result);
return result;
}
void OrientedBoundingBox::GetBoundingBox(BoundingBox& result) const
{
Vector3 corners[8];
GetCorners(corners);
BoundingBox::FromPoints(corners, 8, result);
}
ContainmentType OrientedBoundingBox::Contains(const Vector3& point, float* distance) const
{
// Transform the point into the obb coordinates
Matrix invTrans;
Matrix::Invert(Transformation, invTrans);
Vector3 locPoint;
Vector3::TransformCoordinate(point, invTrans, locPoint);
locPoint.X = Math::Abs(locPoint.X);
locPoint.Y = Math::Abs(locPoint.Y);
locPoint.Z = Math::Abs(locPoint.Z);
if (distance)
{
// Get minimum distance to edge in local space
Vector3 tmp;
Vector3::Subtract(Extents, locPoint, tmp);
tmp.Absolute();
const float minDstToEdgeLocal = tmp.MinValue();
// Transform distance to world space
Vector3 dstVec = Vector3::UnitX * minDstToEdgeLocal;
Vector3::TransformNormal(dstVec, Transformation, dstVec);
*distance = dstVec.Length();
}
// Simple axes-aligned BB check
if (locPoint.X < Extents.X && locPoint.Y < Extents.Y && locPoint.Z < Extents.Z)
return ContainmentType::Contains;
if (Vector3::NearEqual(locPoint, Extents))
return ContainmentType::Intersects;
return ContainmentType::Disjoint;
}
ContainmentType OrientedBoundingBox::Contains(int32 pointsCnt, Vector3 points[]) const
{
ASSERT(pointsCnt > 0);
Matrix invTrans;
Matrix::Invert(Transformation, invTrans);
bool containsAll = true;
bool containsAny = false;
for (int32 i = 0; i < pointsCnt; i++)
{
Vector3 locPoint;
Vector3::TransformCoordinate(points[i], invTrans, locPoint);
locPoint.X = Math::Abs(locPoint.X);
locPoint.Y = Math::Abs(locPoint.Y);
locPoint.Z = Math::Abs(locPoint.Z);
// Simple axes-aligned BB check
if (Vector3::NearEqual(locPoint, Extents))
containsAny = true;
if (locPoint.X < Extents.X && locPoint.Y < Extents.Y && locPoint.Z < Extents.Z)
containsAny = true;
else
containsAll = false;
}
if (containsAll)
return ContainmentType::Contains;
if (containsAny)
return ContainmentType::Intersects;
return ContainmentType::Disjoint;
}
ContainmentType OrientedBoundingBox::Contains(const BoundingSphere& sphere, bool ignoreScale) const
{
Matrix invTrans;
Matrix::Invert(Transformation, invTrans);
// Transform sphere center into the obb coordinates
Vector3 locCenter;
Vector3::TransformCoordinate(sphere.Center, invTrans, locCenter);
float locRadius;
if (ignoreScale)
{
locRadius = sphere.Radius;
}
else
{
// Transform sphere radius into the obb coordinates
Vector3 vRadius = Vector3::UnitX * sphere.Radius;
Vector3::TransformNormal(vRadius, invTrans, vRadius);
locRadius = vRadius.Length();
}
// Perform regular BoundingBox to BoundingSphere containment check
const Vector3 minusExtents = -Extents;
Vector3 vector;
Vector3::Clamp(locCenter, minusExtents, Extents, vector);
const float distance = Vector3::DistanceSquared(locCenter, vector);
if (distance > locRadius * locRadius)
return ContainmentType::Disjoint;
if (minusExtents.X + locRadius <= locCenter.X
&& locCenter.X <= Extents.X - locRadius
&& (Extents.X - minusExtents.X > locRadius
&& minusExtents.Y + locRadius <= locCenter.Y)
&& (locCenter.Y <= Extents.Y - locRadius
&& Extents.Y - minusExtents.Y > locRadius
&& (minusExtents.Z + locRadius <= locCenter.Z
&& locCenter.Z <= Extents.Z - locRadius
&& Extents.Z - minusExtents.Z > locRadius)))
{
return ContainmentType::Contains;
}
return ContainmentType::Intersects;
}
/*ContainmentType OrientedBoundingBox::Contains(const OrientedBoundingBox& obb)
{
Vector3 obbCorners[8];
obb.GetCorners(obbCorners);
auto cornersCheck = Contains(8, obbCorners);
if (cornersCheck != ContainmentType::Disjoint)
return cornersCheck;
// http://www.3dkingdoms.com/weekly/bbox.cpp
Vector3 SizeA = Extents;
Vector3 SizeB = obb.Extents;
Vector3 RotA[3];
Vector3 RotB[3];
GetRows(Transformation, RotA);
GetRows(obb.Transformation, RotB);
Matrix R = Matrix::Identity; // Rotation from B to A
Matrix AR = Matrix::Identity; // absolute values of R matrix, to use with box extents
float ExtentA, ExtentB, Separation;
int i, k;
// Calculate B to A rotation matrix
for (i = 0; i < 3; i++)
{
for (k = 0; k < 3; k++)
{
R[i, k] = Vector3::Dot(RotA[i], RotB[k]);
AR[i, k] = Math::Abs(R[i, k]);
}
}
// Vector separating the centers of Box B and of Box A
Vector3 vSepWS = obb.GetCenter() - GetCenter();
// Rotated into Box A's coordinates
Vector3 vSepA = Vector3(Vector3::Dot(vSepWS, RotA[0]), Vector3::Dot(vSepWS, RotA[1]), Vector3::Dot(vSepWS, RotA[2]));
// Test if any of A's basis vectors separate the box
for (i = 0; i < 3; i++)
{
ExtentA = SizeA[i];
ExtentB = Vector3::Dot(SizeB, Vector3(AR[i, 0], AR[i, 1], AR[i, 2]));
Separation = Math::Abs(vSepA[i]);
if (Separation > ExtentA + ExtentB)
return ContainmentType::Disjoint;
}
// Test if any of B's basis vectors separate the box
for (k = 0; k < 3; k++)
{
ExtentA = Vector3::Dot(SizeA, Vector3(AR[0, k], AR[1, k], AR[2, k]));
ExtentB = SizeB[k];
Separation = Math::Abs(Vector3::Dot(vSepA, Vector3(R[0, k], R[1, k], R[2, k])));
if (Separation > ExtentA + ExtentB)
return ContainmentType::Disjoint;
}
// Now test Cross Products of each basis vector combination ( A[i], B[k] )
for (i = 0; i < 3; i++)
{
for (k = 0; k < 3; k++)
{
int i1 = (i + 1) % 3, i2 = (i + 2) % 3;
int k1 = (k + 1) % 3, k2 = (k + 2) % 3;
ExtentA = SizeA[i1] * AR[i2, k] + SizeA[i2] * AR[i1, k];
ExtentB = SizeB[k1] * AR[i, k2] + SizeB[k2] * AR[i, k1];
Separation = Math::Abs(vSepA[i2] * R[i1, k] - vSepA[i1] * R[i2, k]);
if (Separation > ExtentA + ExtentB)
return ContainmentType::Disjoint;
}
}
// No separating axis found, the boxes overlap
return ContainmentType::Intersects;
}*/
bool OrientedBoundingBox::Intersects(const Ray& ray, Vector3& point) const
{
// Put ray in box space
Matrix invTrans;
Matrix::Invert(Transformation, invTrans);
Ray bRay;
Vector3::TransformNormal(ray.Direction, invTrans, bRay.Direction);
Vector3::TransformCoordinate(ray.Position, invTrans, bRay.Position);
// Perform a regular ray to BoundingBox check
const BoundingBox bb = BoundingBox(-Extents, Extents);
const bool intersects = CollisionsHelper::RayIntersectsBox(bRay, bb, point);
// Put the result intersection back to world
if (intersects)
Vector3::TransformCoordinate(point, Transformation, point);
return intersects;
}
bool OrientedBoundingBox::Intersects(const Ray& ray, float& distance) const
{
Vector3 point;
const bool result = Intersects(ray, point);
distance = Vector3::Distance(ray.Position, point);
return result;
}
bool OrientedBoundingBox::Intersects(const Ray& ray, float& distance, Vector3& normal) const
{
// Put ray in box space
Matrix invTrans;
Matrix::Invert(Transformation, invTrans);
Ray bRay;
Vector3::TransformNormal(ray.Direction, invTrans, bRay.Direction);
Vector3::TransformCoordinate(ray.Position, invTrans, bRay.Position);
// Perform a regular ray to BoundingBox check
const BoundingBox bb(-Extents, Extents);
if (CollisionsHelper::RayIntersectsBox(bRay, bb, distance, normal))
{
// Put the result intersection back to world
Vector3::TransformNormal(normal, Transformation, normal);
normal.Normalize();
return true;
}
return false;
}