// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
#pragma once
#include "Engine/Platform/Platform.h"
#include "Vector3.h"
#include "CollisionsHelper.h"
///
/// Represents an axis-aligned bounding box in three dimensional space.
///
API_STRUCT() struct FLAXENGINE_API BoundingBox
{
DECLARE_SCRIPTING_TYPE_MINIMAL(BoundingBox);
public:
///
/// A which represents an empty space.
///
static const BoundingBox Empty;
///
/// A located at zero point with zero size.
///
static const BoundingBox Zero;
public:
///
/// The minimum point of the box.
///
API_FIELD() Vector3 Minimum;
///
/// The maximum point of the box.
///
API_FIELD() Vector3 Maximum;
public:
///
/// Empty constructor.
///
BoundingBox() = default;
///
/// Initializes a new instance of the struct.
///
/// The location of the empty bounding box.
BoundingBox(const Vector3& point)
: Minimum(point)
, Maximum(point)
{
}
///
/// Initializes a new instance of the struct.
///
/// The minimum vertex of the bounding box.
/// The maximum vertex of the bounding box.
BoundingBox(const Vector3& minimum, const Vector3& maximum)
: Minimum(minimum)
, Maximum(maximum)
{
}
public:
String ToString() const;
public:
///
/// Gets the eight corners of the bounding box.
///
/// An array of points representing the eight corners of the bounding box.
void GetCorners(Float3 corners[8]) const;
///
/// Gets the eight corners of the bounding box.
///
/// An array of points representing the eight corners of the bounding box.
void GetCorners(Double3 corners[8]) const;
///
/// Calculates volume of the box.
///
/// The box volume.
Real GetVolume() const
{
Vector3 size;
Vector3::Subtract(Maximum, Minimum, size);
return size.X * size.Y * size.Z;
}
///
/// Calculates the size of the box.
///
/// The box size.
Vector3 GetSize() const
{
Vector3 size;
Vector3::Subtract(Maximum, Minimum, size);
return size;
}
///
/// Calculates the size of the box.
///
/// The result box size.
void GetSize(Vector3& result) const
{
Vector3::Subtract(Maximum, Minimum, result);
}
///
/// Sets the size of the box.
///
/// The box size to set.
void SetSize(const Vector3& value)
{
Vector3 center;
GetCenter(center);
const Vector3 sizeHalf = value * 0.5f;
Minimum = center - sizeHalf;
Maximum = center + sizeHalf;
}
///
/// Gets the center point location.
///
/// The box center.
Vector3 GetCenter() const
{
return Minimum + (Maximum - Minimum) * 0.5f;
}
///
/// Gets the center point location.
///
/// The result box center.
void GetCenter(Vector3& result) const
{
result = Minimum + (Maximum - Minimum) * 0.5f;
}
///
/// Sets the center point location.
///
/// The box center to set.
void SetCenter(const Vector3& value)
{
const Vector3 sizeHalf = GetSize() * 0.5f;
Minimum = value - sizeHalf;
Maximum = value + sizeHalf;
}
public:
static bool NearEqual(const BoundingBox& a, const BoundingBox& b)
{
return Vector3::NearEqual(a.Minimum, b.Minimum) && Vector3::NearEqual(a.Maximum, b.Maximum);
}
static bool NearEqual(const BoundingBox& a, const BoundingBox& b, Real epsilon)
{
return Vector3::NearEqual(a.Minimum, b.Minimum, epsilon) && Vector3::NearEqual(a.Maximum, b.Maximum, epsilon);
}
public:
///
/// Merges the box with a point.
///
/// The point to add to the box bounds.
void Merge(const Vector3& point)
{
Vector3::Min(Minimum, point, Minimum);
Vector3::Max(Maximum, point, Maximum);
}
///
/// Merges the box with the other box.
///
/// The other box to add to the box bounds.
void Merge(const BoundingBox& box)
{
Vector3::Min(Minimum, box.Minimum, Minimum);
Vector3::Max(Maximum, box.Maximum, Maximum);
}
///
/// Creates the bounding box that is offseted by the given vector. Adds the offset value to minimum and maximum points.
///
/// The offset.
/// The result.
BoundingBox MakeOffsetted(const Vector3& offset) const;
public:
FORCE_INLINE bool operator==(const BoundingBox& other) const
{
return Minimum == other.Minimum && Maximum == other.Maximum;
}
FORCE_INLINE bool operator!=(const BoundingBox& other) const
{
return Minimum != other.Minimum || Maximum != other.Maximum;
}
FORCE_INLINE BoundingBox operator*(const Matrix& matrix) const
{
BoundingBox result;
Transform(*this, matrix, result);
return result;
}
public:
///
/// Constructs a Bounding Box that fully contains the given pair of points.
///
/// The first point that will be contained by the box.
/// The second point that will be contained by the box.
/// The constructed bounding box.
static void FromPoints(const Vector3& pointA, const Vector3& pointB, BoundingBox& result)
{
Vector3::Min(pointA, pointB, result.Minimum);
Vector3::Max(pointA, pointB, result.Maximum);
}
///
/// Constructs a Bounding Box that fully contains the given points.
///
/// The points that will be contained by the box.
/// The amount of points to use.
/// The constructed bounding box.
static void FromPoints(const Float3* points, int32 pointsCount, BoundingBox& result);
///
/// Constructs a Bounding Box that fully contains the given points.
///
/// The points that will be contained by the box.
/// The amount of points to use.
/// The constructed bounding box.
static void FromPoints(const Double3* points, int32 pointsCount, BoundingBox& result);
///
/// Constructs a Bounding Box from a given sphere.
///
/// The sphere that will designate the extents of the box.
/// The constructed bounding box.
static void FromSphere(const BoundingSphere& sphere, BoundingBox& result);
///
/// Constructs a Bounding Box from a given sphere.
///
/// The sphere that will designate the extents of the box.
/// The constructed bounding box.
static BoundingBox FromSphere(const BoundingSphere& sphere);
///
/// Constructs a Bounding Box that is as large as the total combined area of the two specified boxes.
///
/// The first box to merge.
/// The second box to merge.
/// The constructed bounding box.
static void Merge(const BoundingBox& value1, const BoundingBox& value2, BoundingBox& result)
{
Vector3::Min(value1.Minimum, value2.Minimum, result.Minimum);
Vector3::Max(value1.Maximum, value2.Maximum, result.Maximum);
}
///
/// Transforms the bounding box using the specified matrix.
///
/// The box.
/// The matrix.
/// The result transformed box.
static BoundingBox Transform(const BoundingBox& box, const Matrix& matrix);
///
/// Creates the bounding box that is offseted by the given vector. Adds the offset value to minimum and maximum points.
///
/// The box.
/// The bounds offset.
/// The offsetted bounds.
static BoundingBox MakeOffsetted(const BoundingBox& box, const Vector3& offset);
///
/// Creates the bounding box that is scaled by the given factor. Applies scale to the size of the bounds.
///
/// The box.
/// The bounds scale.
/// The scaled bounds.
static BoundingBox MakeScaled(const BoundingBox& box, Real scale);
///
/// Transforms the bounding box using the specified matrix.
///
/// The box.
/// The matrix.
/// The result transformed box.
static void Transform(const BoundingBox& box, const Matrix& matrix, BoundingBox& result);
///
/// Transforms the bounding box using the specified transformation.
///
/// The box.
/// The transformation.
/// The result transformed box.
static void Transform(const BoundingBox& box, const ::Transform& transform, BoundingBox& result);
public:
///
/// Determines if there is an intersection between the current object and a Ray.
///
/// The ray to test.
/// Whether the two objects intersected.
FORCE_INLINE bool Intersects(const Ray& ray) const
{
Real distance;
return CollisionsHelper::RayIntersectsBox(ray, *this, distance);
}
///
/// Determines if there is an intersection between the current object and a Ray.
///
/// The ray to test.
/// When the method completes, contains the distance of the intersection, or 0 if there was no intersection.
/// Whether the two objects intersected.
FORCE_INLINE bool Intersects(const Ray& ray, Real& distance) const
{
return CollisionsHelper::RayIntersectsBox(ray, *this, distance);
}
///
/// Determines if there is an intersection between the current object and a Ray.
///
/// The ray to test.
/// When the method completes, contains the distance of the intersection, or 0 if there was no intersection.
/// When the method completes, contains the intersection surface normal vector, or Vector3::Up if there was no intersection.
/// Whether the two objects intersected.
FORCE_INLINE bool Intersects(const Ray& ray, Real& distance, Vector3& normal) const
{
return CollisionsHelper::RayIntersectsBox(ray, *this, distance, normal);
}
///
/// Determines if there is an intersection between the current object and a Ray.
///
/// The ray to test.
/// When the method completes, contains the point of intersection, or if there was no intersection.
/// Whether the two objects intersected.
FORCE_INLINE bool Intersects(const Ray& ray, Vector3& point) const
{
return CollisionsHelper::RayIntersectsBox(ray, *this, point);
}
///
/// Determines if there is an intersection between the current object and a Plane.
///
/// The plane to test.
/// Whether the two objects intersected.
FORCE_INLINE PlaneIntersectionType Intersects(const Plane& plane) const
{
return CollisionsHelper::PlaneIntersectsBox(plane, *this);
}
///
/// Determines if there is an intersection between the current object and a Bounding Box.
///
/// The box to test.
/// Whether the two objects intersected.
FORCE_INLINE bool Intersects(const BoundingBox& box) const
{
return CollisionsHelper::BoxIntersectsBox(*this, box);
}
///
/// Determines if there is an intersection between the current object and a Bounding Sphere.
///
/// The sphere to test.
/// Whether the two objects intersected.
FORCE_INLINE bool Intersects(const BoundingSphere& sphere) const
{
return CollisionsHelper::BoxIntersectsSphere(*this, sphere);
}
///
/// Determines whether the current objects contains a point.
///
/// The point to test.
/// The type of containment the two objects have.
FORCE_INLINE ContainmentType Contains(const Vector3& point) const
{
return CollisionsHelper::BoxContainsPoint(*this, point);
}
///
/// Determines whether the current objects contains a Bounding Box.
///
/// The box to test.
/// The type of containment the two objects have.
FORCE_INLINE ContainmentType Contains(const BoundingBox& box) const
{
return CollisionsHelper::BoxContainsBox(*this, box);
}
///
/// Determines whether the current objects contains a Bounding Sphere.
///
/// The sphere to test.
/// The type of containment the two objects have.
FORCE_INLINE ContainmentType Contains(const BoundingSphere& sphere) const
{
return CollisionsHelper::BoxContainsSphere(*this, sphere);
}
///
/// Determines the distance between a Bounding Box and a point.
///
/// The point to test.
/// The distance between bounding box and a point.
FORCE_INLINE Real Distance(const Vector3& point) const
{
return CollisionsHelper::DistanceBoxPoint(*this, point);
}
///
/// Determines the distance between two Bounding Boxed.
///
/// The bounding box to test.
/// The distance between bounding boxes.
FORCE_INLINE Real Distance(const BoundingBox& box) const
{
return CollisionsHelper::DistanceBoxBox(*this, box);
}
};
template<>
struct TIsPODType
{
enum { Value = true };
};
DEFINE_DEFAULT_FORMATTING(BoundingBox, "Minimum:{0} Maximum:{1}", v.Minimum, v.Maximum);