Files
FlaxEngine/Source/Engine/Core/Math/OrientedBoundingBox.cpp
2023-01-10 15:29:37 +01:00

244 lines
8.2 KiB
C++

// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
#include "OrientedBoundingBox.h"
#include "BoundingSphere.h"
#include "BoundingBox.h"
#include "Matrix.h"
#include "Ray.h"
#include "../Types/String.h"
OrientedBoundingBox::OrientedBoundingBox(const BoundingBox& bb)
{
const Vector3 center = bb.Minimum + (bb.Maximum - bb.Minimum) * 0.5f;
Extents = bb.Maximum - center;
Transformation = ::Transform(center);
}
OrientedBoundingBox::OrientedBoundingBox(const Vector3& extents, const Matrix& transformation)
: Extents(extents)
{
transformation.Decompose(Transformation);
}
OrientedBoundingBox::OrientedBoundingBox(const Vector3& extents, const Matrix3x3& rotationScale, const Vector3& translation)
: Extents(extents)
, Transformation(translation, rotationScale)
{
}
OrientedBoundingBox::OrientedBoundingBox(const Vector3& minimum, const Vector3& maximum)
{
const Vector3 center = minimum + (maximum - minimum) * 0.5f;
Extents = maximum - center;
Transformation = ::Transform(center);
}
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) * 0.5f;
Extents = maximum - center;
Transformation = ::Transform(center);
}
String OrientedBoundingBox::ToString() const
{
return String::Format(TEXT("{}"), *this);
}
void OrientedBoundingBox::GetCorners(Float3 corners[8]) const
{
const Float3 xv = Transformation.LocalToWorldVector(Vector3((float)Extents.X, 0, 0));
const Float3 yv = Transformation.LocalToWorldVector(Vector3(0, (float)Extents.Y, 0));
const Float3 zv = Transformation.LocalToWorldVector(Vector3(0, 0, (float)Extents.Z));
const Float3 center = Transformation.Translation;
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;
}
void OrientedBoundingBox::GetCorners(Double3 corners[8]) const
{
const Double3 xv = Transformation.LocalToWorldVector(Vector3((float)Extents.X, 0, 0));
const Double3 yv = Transformation.LocalToWorldVector(Vector3(0, (float)Extents.Y, 0));
const Double3 zv = Transformation.LocalToWorldVector(Vector3(0, 0, (float)Extents.Z));
const Double3 center = Transformation.Translation;
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
{
const Vector3 xv = Transformation.LocalToWorldVector(Vector3(Extents.X * 2, 0, 0));
const Vector3 yv = Transformation.LocalToWorldVector(Vector3(0, Extents.Y * 2, 0));
const Vector3 zv = Transformation.LocalToWorldVector(Vector3(0, 0, Extents.Z * 2));
return Vector3(xv.Length(), yv.Length(), zv.Length());
}
Vector3 OrientedBoundingBox::GetSizeSquared() const
{
const Vector3 xv = Transformation.LocalToWorldVector(Vector3(Extents.X * 2, 0, 0));
const Vector3 yv = Transformation.LocalToWorldVector(Vector3(0, Extents.Y * 2, 0));
const Vector3 zv = Transformation.LocalToWorldVector(Vector3(0, 0, Extents.Z * 2));
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);
}
void OrientedBoundingBox::Transform(const Matrix& matrix)
{
::Transform transform;
matrix.Decompose(transform);
Transformation = transform.LocalToWorld(Transformation);
}
void OrientedBoundingBox::Transform(const ::Transform& transform)
{
Transformation = transform.LocalToWorld(Transformation);
}
ContainmentType OrientedBoundingBox::Contains(const Vector3& point, Real* distance) const
{
// Transform the point into the obb coordinates
Vector3 locPoint;
Transformation.WorldToLocal(point, 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);
const Real minDstToEdgeLocal = tmp.GetAbsolute().MinValue();
// Transform distance to world space
Vector3 dstVec = Vector3::UnitX * minDstToEdgeLocal;
Transformation.LocalToWorldVector(dstVec, 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(const BoundingSphere& sphere, bool ignoreScale) const
{
// Transform sphere center into the obb coordinates
Vector3 locCenter;
Transformation.WorldToLocal(sphere.Center, locCenter);
Real locRadius;
if (ignoreScale)
{
locRadius = sphere.Radius;
}
else
{
// Transform sphere radius into the obb coordinates
Vector3 vRadius = Vector3::UnitX * sphere.Radius;
Transformation.LocalToWorldVector(vRadius, vRadius);
locRadius = vRadius.Length();
}
// Perform regular BoundingBox to BoundingSphere containment check
const Vector3 minusExtents = -Extents;
Vector3 vector;
Vector3::Clamp(locCenter, minusExtents, Extents, vector);
const Real 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;
}
bool OrientedBoundingBox::Intersects(const Ray& ray, Vector3& point) const
{
// Put ray in box space
Ray bRay;
Transformation.WorldToLocalVector(ray.Direction, bRay.Direction);
Transformation.WorldToLocal(ray.Position, bRay.Position);
// Perform a regular ray to BoundingBox check
const BoundingBox bb(-Extents, Extents);
const bool intersects = CollisionsHelper::RayIntersectsBox(bRay, bb, point);
// Put the result intersection back to world
if (intersects)
Transformation.LocalToWorld(point, point);
return intersects;
}
bool OrientedBoundingBox::Intersects(const Ray& ray, Real& distance) const
{
Vector3 point;
const bool result = Intersects(ray, point);
distance = Vector3::Distance(ray.Position, point);
return result;
}
bool OrientedBoundingBox::Intersects(const Ray& ray, Real& distance, Vector3& normal) const
{
// Put ray in box space
Ray bRay;
Transformation.WorldToLocalVector(ray.Direction, bRay.Direction);
Transformation.WorldToLocal(ray.Position, 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
Transformation.LocalToWorldVector(normal, normal);
normal.Normalize();
return true;
}
return false;
}