#if REAL_T_IS_DOUBLE using real_t = System.Double; #else using real_t = System.Single; #endif using System; using System.Runtime.InteropServices; namespace Godot { /// /// Axis-Aligned Bounding Box. AABB consists of a position, a size, and /// several utility functions. It is typically used for fast overlap tests. /// [Serializable] [StructLayout(LayoutKind.Sequential)] public struct AABB : IEquatable { private Vector3 _position; private Vector3 _size; /// /// Beginning corner. Typically has values lower than End. /// /// Directly uses a private field. public Vector3 Position { get { return _position; } set { _position = value; } } /// /// Size from Position to End. Typically all components are positive. /// If the size is negative, you can use to fix it. /// /// Directly uses a private field. public Vector3 Size { get { return _size; } set { _size = value; } } /// /// Ending corner. This is calculated as plus /// . Setting this value will change the size. /// /// Getting is equivalent to `value = Position + Size`, setting is equivalent to `Size = value - Position`. public Vector3 End { get { return _position + _size; } set { _size = value - _position; } } /// /// Returns an AABB with equivalent position and size, modified so that /// the most-negative corner is the origin and the size is positive. /// /// The modified AABB. public AABB Abs() { Vector3 end = End; Vector3 topLeft = new Vector3(Mathf.Min(_position.x, end.x), Mathf.Min(_position.y, end.y), Mathf.Min(_position.z, end.z)); return new AABB(topLeft, _size.Abs()); } /// /// Returns true if this AABB completely encloses another one. /// /// The other AABB that may be enclosed. /// A bool for whether or not this AABB encloses `b`. public bool Encloses(AABB with) { Vector3 src_min = _position; Vector3 src_max = _position + _size; Vector3 dst_min = with._position; Vector3 dst_max = with._position + with._size; return src_min.x <= dst_min.x && src_max.x > dst_max.x && src_min.y <= dst_min.y && src_max.y > dst_max.y && src_min.z <= dst_min.z && src_max.z > dst_max.z; } /// /// Returns this AABB expanded to include a given point. /// /// The point to include. /// The expanded AABB. public AABB Expand(Vector3 point) { Vector3 begin = _position; Vector3 end = _position + _size; if (point.x < begin.x) { begin.x = point.x; } if (point.y < begin.y) { begin.y = point.y; } if (point.z < begin.z) { begin.z = point.z; } if (point.x > end.x) { end.x = point.x; } if (point.y > end.y) { end.y = point.y; } if (point.z > end.z) { end.z = point.z; } return new AABB(begin, end - begin); } /// /// Returns the area of the AABB. /// /// The area. public real_t GetArea() { return _size.x * _size.y * _size.z; } /// /// Gets the position of one of the 8 endpoints of the AABB. /// /// Which endpoint to get. /// An endpoint of the AABB. public Vector3 GetEndpoint(int idx) { switch (idx) { case 0: return new Vector3(_position.x, _position.y, _position.z); case 1: return new Vector3(_position.x, _position.y, _position.z + _size.z); case 2: return new Vector3(_position.x, _position.y + _size.y, _position.z); case 3: return new Vector3(_position.x, _position.y + _size.y, _position.z + _size.z); case 4: return new Vector3(_position.x + _size.x, _position.y, _position.z); case 5: return new Vector3(_position.x + _size.x, _position.y, _position.z + _size.z); case 6: return new Vector3(_position.x + _size.x, _position.y + _size.y, _position.z); case 7: return new Vector3(_position.x + _size.x, _position.y + _size.y, _position.z + _size.z); default: throw new ArgumentOutOfRangeException(nameof(idx), String.Format("Index is {0}, but a value from 0 to 7 is expected.", idx)); } } /// /// Returns the normalized longest axis of the AABB. /// /// A vector representing the normalized longest axis of the AABB. public Vector3 GetLongestAxis() { var axis = new Vector3(1f, 0f, 0f); real_t max_size = _size.x; if (_size.y > max_size) { axis = new Vector3(0f, 1f, 0f); max_size = _size.y; } if (_size.z > max_size) { axis = new Vector3(0f, 0f, 1f); } return axis; } /// /// Returns the index of the longest axis of the AABB. /// /// A index for which axis is longest. public Vector3.Axis GetLongestAxisIndex() { var axis = Vector3.Axis.X; real_t max_size = _size.x; if (_size.y > max_size) { axis = Vector3.Axis.Y; max_size = _size.y; } if (_size.z > max_size) { axis = Vector3.Axis.Z; } return axis; } /// /// Returns the scalar length of the longest axis of the AABB. /// /// The scalar length of the longest axis of the AABB. public real_t GetLongestAxisSize() { real_t max_size = _size.x; if (_size.y > max_size) max_size = _size.y; if (_size.z > max_size) max_size = _size.z; return max_size; } /// /// Returns the normalized shortest axis of the AABB. /// /// A vector representing the normalized shortest axis of the AABB. public Vector3 GetShortestAxis() { var axis = new Vector3(1f, 0f, 0f); real_t max_size = _size.x; if (_size.y < max_size) { axis = new Vector3(0f, 1f, 0f); max_size = _size.y; } if (_size.z < max_size) { axis = new Vector3(0f, 0f, 1f); } return axis; } /// /// Returns the index of the shortest axis of the AABB. /// /// A index for which axis is shortest. public Vector3.Axis GetShortestAxisIndex() { var axis = Vector3.Axis.X; real_t max_size = _size.x; if (_size.y < max_size) { axis = Vector3.Axis.Y; max_size = _size.y; } if (_size.z < max_size) { axis = Vector3.Axis.Z; } return axis; } /// /// Returns the scalar length of the shortest axis of the AABB. /// /// The scalar length of the shortest axis of the AABB. public real_t GetShortestAxisSize() { real_t max_size = _size.x; if (_size.y < max_size) max_size = _size.y; if (_size.z < max_size) max_size = _size.z; return max_size; } /// /// Returns the support point in a given direction. /// This is useful for collision detection algorithms. /// /// The direction to find support for. /// A vector representing the support. public Vector3 GetSupport(Vector3 dir) { Vector3 half_extents = _size * 0.5f; Vector3 ofs = _position + half_extents; return ofs + new Vector3( dir.x > 0f ? -half_extents.x : half_extents.x, dir.y > 0f ? -half_extents.y : half_extents.y, dir.z > 0f ? -half_extents.z : half_extents.z); } /// /// Returns a copy of the AABB grown a given amount of units towards all the sides. /// /// The amount to grow by. /// The grown AABB. public AABB Grow(real_t by) { var res = this; res._position.x -= by; res._position.y -= by; res._position.z -= by; res._size.x += 2.0f * by; res._size.y += 2.0f * by; res._size.z += 2.0f * by; return res; } /// /// Returns true if the AABB is flat or empty, or false otherwise. /// /// A bool for whether or not the AABB has area. public bool HasNoArea() { return _size.x <= 0f || _size.y <= 0f || _size.z <= 0f; } /// /// Returns true if the AABB has no surface (no size), or false otherwise. /// /// A bool for whether or not the AABB has area. public bool HasNoSurface() { return _size.x <= 0f && _size.y <= 0f && _size.z <= 0f; } /// /// Returns true if the AABB contains a point, or false otherwise. /// /// The point to check. /// A bool for whether or not the AABB contains `point`. public bool HasPoint(Vector3 point) { if (point.x < _position.x) return false; if (point.y < _position.y) return false; if (point.z < _position.z) return false; if (point.x > _position.x + _size.x) return false; if (point.y > _position.y + _size.y) return false; if (point.z > _position.z + _size.z) return false; return true; } /// /// Returns the intersection of this AABB and `b`. /// /// The other AABB. /// The clipped AABB. public AABB Intersection(AABB with) { Vector3 src_min = _position; Vector3 src_max = _position + _size; Vector3 dst_min = with._position; Vector3 dst_max = with._position + with._size; Vector3 min, max; if (src_min.x > dst_max.x || src_max.x < dst_min.x) { return new AABB(); } min.x = src_min.x > dst_min.x ? src_min.x : dst_min.x; max.x = src_max.x < dst_max.x ? src_max.x : dst_max.x; if (src_min.y > dst_max.y || src_max.y < dst_min.y) { return new AABB(); } min.y = src_min.y > dst_min.y ? src_min.y : dst_min.y; max.y = src_max.y < dst_max.y ? src_max.y : dst_max.y; if (src_min.z > dst_max.z || src_max.z < dst_min.z) { return new AABB(); } min.z = src_min.z > dst_min.z ? src_min.z : dst_min.z; max.z = src_max.z < dst_max.z ? src_max.z : dst_max.z; return new AABB(min, max - min); } /// /// Returns true if the AABB overlaps with `b` /// (i.e. they have at least one point in common). /// /// If `includeBorders` is true, they will also be considered overlapping /// if their borders touch, even without intersection. /// /// The other AABB to check for intersections with. /// Whether or not to consider borders. /// A bool for whether or not they are intersecting. public bool Intersects(AABB with, bool includeBorders = false) { if (includeBorders) { if (_position.x > with._position.x + with._size.x) return false; if (_position.x + _size.x < with._position.x) return false; if (_position.y > with._position.y + with._size.y) return false; if (_position.y + _size.y < with._position.y) return false; if (_position.z > with._position.z + with._size.z) return false; if (_position.z + _size.z < with._position.z) return false; } else { if (_position.x >= with._position.x + with._size.x) return false; if (_position.x + _size.x <= with._position.x) return false; if (_position.y >= with._position.y + with._size.y) return false; if (_position.y + _size.y <= with._position.y) return false; if (_position.z >= with._position.z + with._size.z) return false; if (_position.z + _size.z <= with._position.z) return false; } return true; } /// /// Returns true if the AABB is on both sides of `plane`. /// /// The plane to check for intersection. /// A bool for whether or not the AABB intersects the plane. public bool IntersectsPlane(Plane plane) { Vector3[] points = { new Vector3(_position.x, _position.y, _position.z), new Vector3(_position.x, _position.y, _position.z + _size.z), new Vector3(_position.x, _position.y + _size.y, _position.z), new Vector3(_position.x, _position.y + _size.y, _position.z + _size.z), new Vector3(_position.x + _size.x, _position.y, _position.z), new Vector3(_position.x + _size.x, _position.y, _position.z + _size.z), new Vector3(_position.x + _size.x, _position.y + _size.y, _position.z), new Vector3(_position.x + _size.x, _position.y + _size.y, _position.z + _size.z) }; bool over = false; bool under = false; for (int i = 0; i < 8; i++) { if (plane.DistanceTo(points[i]) > 0) { over = true; } else { under = true; } } return under && over; } /// /// Returns true if the AABB intersects the line segment between `from` and `to`. /// /// The start of the line segment. /// The end of the line segment. /// A bool for whether or not the AABB intersects the line segment. public bool IntersectsSegment(Vector3 from, Vector3 to) { real_t min = 0f; real_t max = 1f; for (int i = 0; i < 3; i++) { real_t segFrom = from[i]; real_t segTo = to[i]; real_t boxBegin = _position[i]; real_t boxEnd = boxBegin + _size[i]; real_t cmin, cmax; if (segFrom < segTo) { if (segFrom > boxEnd || segTo < boxBegin) { return false; } real_t length = segTo - segFrom; cmin = segFrom < boxBegin ? (boxBegin - segFrom) / length : 0f; cmax = segTo > boxEnd ? (boxEnd - segFrom) / length : 1f; } else { if (segTo > boxEnd || segFrom < boxBegin) { return false; } real_t length = segTo - segFrom; cmin = segFrom > boxEnd ? (boxEnd - segFrom) / length : 0f; cmax = segTo < boxBegin ? (boxBegin - segFrom) / length : 1f; } if (cmin > min) { min = cmin; } if (cmax < max) { max = cmax; } if (max < min) { return false; } } return true; } /// /// Returns a larger AABB that contains this AABB and `b`. /// /// The other AABB. /// The merged AABB. public AABB Merge(AABB with) { Vector3 beg1 = _position; Vector3 beg2 = with._position; var end1 = new Vector3(_size.x, _size.y, _size.z) + beg1; var end2 = new Vector3(with._size.x, with._size.y, with._size.z) + beg2; var min = new Vector3( beg1.x < beg2.x ? beg1.x : beg2.x, beg1.y < beg2.y ? beg1.y : beg2.y, beg1.z < beg2.z ? beg1.z : beg2.z ); var max = new Vector3( end1.x > end2.x ? end1.x : end2.x, end1.y > end2.y ? end1.y : end2.y, end1.z > end2.z ? end1.z : end2.z ); return new AABB(min, max - min); } /// /// Constructs an AABB from a position and size. /// /// The position. /// The size, typically positive. public AABB(Vector3 position, Vector3 size) { _position = position; _size = size; } /// /// Constructs an AABB from a position, width, height, and depth. /// /// The position. /// The width, typically positive. /// The height, typically positive. /// The depth, typically positive. public AABB(Vector3 position, real_t width, real_t height, real_t depth) { _position = position; _size = new Vector3(width, height, depth); } /// /// Constructs an AABB from x, y, z, and size. /// /// The position's X coordinate. /// The position's Y coordinate. /// The position's Z coordinate. /// The size, typically positive. public AABB(real_t x, real_t y, real_t z, Vector3 size) { _position = new Vector3(x, y, z); _size = size; } /// /// Constructs an AABB from x, y, z, width, height, and depth. /// /// The position's X coordinate. /// The position's Y coordinate. /// The position's Z coordinate. /// The width, typically positive. /// The height, typically positive. /// The depth, typically positive. public AABB(real_t x, real_t y, real_t z, real_t width, real_t height, real_t depth) { _position = new Vector3(x, y, z); _size = new Vector3(width, height, depth); } public static bool operator ==(AABB left, AABB right) { return left.Equals(right); } public static bool operator !=(AABB left, AABB right) { return !left.Equals(right); } public override bool Equals(object obj) { if (obj is AABB) { return Equals((AABB)obj); } return false; } public bool Equals(AABB other) { return _position == other._position && _size == other._size; } /// /// Returns true if this AABB and `other` are approximately equal, by running /// on each component. /// /// The other AABB to compare. /// Whether or not the AABBs are approximately equal. public bool IsEqualApprox(AABB other) { return _position.IsEqualApprox(other._position) && _size.IsEqualApprox(other._size); } public override int GetHashCode() { return _position.GetHashCode() ^ _size.GetHashCode(); } public override string ToString() { return String.Format("{0}, {1}", new object[] { _position.ToString(), _size.ToString() }); } public string ToString(string format) { return String.Format("{0}, {1}", new object[] { _position.ToString(format), _size.ToString(format) }); } } }