// Copyright (c) 2012-2024 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);