// Copyright (c) 2012-2021 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() { } /// /// 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(Vector3 corners[8]) const { corners[0] = Vector3(Minimum.X, Maximum.Y, Maximum.Z); corners[1] = Vector3(Maximum.X, Maximum.Y, Maximum.Z); corners[2] = Vector3(Maximum.X, Minimum.Y, Maximum.Z); corners[3] = Vector3(Minimum.X, Minimum.Y, Maximum.Z); corners[4] = Vector3(Minimum.X, Maximum.Y, Minimum.Z); corners[5] = Vector3(Maximum.X, Maximum.Y, Minimum.Z); corners[6] = Vector3(Maximum.X, Minimum.Y, Minimum.Z); corners[7] = Vector3(Minimum.X, Minimum.Y, Minimum.Z); } /// /// Calculates volume of the box. /// /// The box volume. float 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); Minimum = center - value; Maximum = center + value; } /// /// 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, float 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 { BoundingBox result; result.Minimum = Minimum + offset; result.Maximum = Maximum + offset; return result; } 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 Vector3* 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 BoundingBox FromPoints(const Vector3* points, int32 pointsCount) { BoundingBox result; FromPoints(points, pointsCount, result); return 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) { BoundingBox result; FromSphere(sphere, result); return result; } /// /// 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) { BoundingBox result; Transform(box, matrix, result); return result; } /// /// Creates the bounding box that is offseted by the given vector. Adds the offset value to minimum and maximum points. /// /// The box. /// The offset. /// The result. static BoundingBox MakeOffsetted(const BoundingBox& box, const Vector3& offset) { BoundingBox result; result.Minimum = box.Minimum + offset; result.Maximum = box.Maximum + offset; return result; } /// /// 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); 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 { float 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, float& 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, float& 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); } }; template<> struct TIsPODType { enum { Value = true }; }; DEFINE_DEFAULT_FORMATTING(BoundingBox, "Minimum:{0} Maximum:{1}", v.Minimum, v.Maximum);