123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390 |
- // Copyright (C) 2002-2012 Nikolaus Gebhardt
- // This file is part of the "Irrlicht Engine".
- // For conditions of distribution and use, see copyright notice in irrlicht.h
- #pragma once
- #include "irrMath.h"
- #include "plane3d.h"
- #include "line3d.h"
- namespace irr
- {
- namespace core
- {
- //! Axis aligned bounding box in 3d dimensional space.
- /** Has some useful methods used with occlusion culling or clipping.
- */
- template <class T>
- class aabbox3d
- {
- public:
- //! Default Constructor.
- constexpr aabbox3d() :
- MinEdge(-1, -1, -1), MaxEdge(1, 1, 1) {}
- //! Constructor with min edge and max edge.
- constexpr aabbox3d(const vector3d<T> &min, const vector3d<T> &max) :
- MinEdge(min), MaxEdge(max) {}
- //! Constructor with only one point.
- constexpr aabbox3d(const vector3d<T> &init) :
- MinEdge(init), MaxEdge(init) {}
- //! Constructor with min edge and max edge as single values, not vectors.
- constexpr aabbox3d(T minx, T miny, T minz, T maxx, T maxy, T maxz) :
- MinEdge(minx, miny, minz), MaxEdge(maxx, maxy, maxz) {}
- // operators
- //! Equality operator
- /** \param other box to compare with.
- \return True if both boxes are equal, else false. */
- constexpr inline bool operator==(const aabbox3d<T> &other) const
- {
- return (MinEdge == other.MinEdge && other.MaxEdge == MaxEdge);
- }
- //! Inequality operator
- /** \param other box to compare with.
- \return True if both boxes are different, else false. */
- constexpr inline bool operator!=(const aabbox3d<T> &other) const
- {
- return !(MinEdge == other.MinEdge && other.MaxEdge == MaxEdge);
- }
- // functions
- //! Resets the bounding box to a one-point box.
- /** \param x X coord of the point.
- \param y Y coord of the point.
- \param z Z coord of the point. */
- void reset(T x, T y, T z)
- {
- MaxEdge.set(x, y, z);
- MinEdge = MaxEdge;
- }
- //! Resets the bounding box.
- /** \param initValue New box to set this one to. */
- void reset(const aabbox3d<T> &initValue)
- {
- *this = initValue;
- }
- //! Resets the bounding box to a one-point box.
- /** \param initValue New point. */
- void reset(const vector3d<T> &initValue)
- {
- MaxEdge = initValue;
- MinEdge = initValue;
- }
- //! Adds a point to the bounding box
- /** The box grows bigger, if point was outside of the box.
- \param p: Point to add into the box. */
- void addInternalPoint(const vector3d<T> &p)
- {
- addInternalPoint(p.X, p.Y, p.Z);
- }
- //! Adds another bounding box
- /** The box grows bigger, if the new box was outside of the box.
- \param b: Other bounding box to add into this box. */
- void addInternalBox(const aabbox3d<T> &b)
- {
- addInternalPoint(b.MaxEdge);
- addInternalPoint(b.MinEdge);
- }
- //! Adds a point to the bounding box
- /** The box grows bigger, if point is outside of the box.
- \param x X coordinate of the point to add to this box.
- \param y Y coordinate of the point to add to this box.
- \param z Z coordinate of the point to add to this box. */
- void addInternalPoint(T x, T y, T z)
- {
- if (x > MaxEdge.X)
- MaxEdge.X = x;
- if (y > MaxEdge.Y)
- MaxEdge.Y = y;
- if (z > MaxEdge.Z)
- MaxEdge.Z = z;
- if (x < MinEdge.X)
- MinEdge.X = x;
- if (y < MinEdge.Y)
- MinEdge.Y = y;
- if (z < MinEdge.Z)
- MinEdge.Z = z;
- }
- //! Get center of the bounding box
- /** \return Center of the bounding box. */
- vector3d<T> getCenter() const
- {
- return (MinEdge + MaxEdge) / 2;
- }
- //! Get extent of the box (maximal distance of two points in the box)
- /** \return Extent of the bounding box. */
- vector3d<T> getExtent() const
- {
- return MaxEdge - MinEdge;
- }
- //! Get radius of the bounding sphere
- /** \return Radius of the bounding sphere. */
- T getRadius() const
- {
- const T radius = getExtent().getLength() / 2;
- return radius;
- }
- //! Check if the box is empty.
- /** This means that there is no space between the min and max edge.
- \return True if box is empty, else false. */
- bool isEmpty() const
- {
- return MinEdge.equals(MaxEdge);
- }
- //! Get the volume enclosed by the box in cubed units
- T getVolume() const
- {
- const vector3d<T> e = getExtent();
- return e.X * e.Y * e.Z;
- }
- //! Get the surface area of the box in squared units
- T getArea() const
- {
- const vector3d<T> e = getExtent();
- return 2 * (e.X * e.Y + e.X * e.Z + e.Y * e.Z);
- }
- //! Stores all 8 edges of the box into an array
- /** \param edges: Pointer to array of 8 edges. */
- void getEdges(vector3d<T> *edges) const
- {
- const core::vector3d<T> middle = getCenter();
- const core::vector3d<T> diag = middle - MaxEdge;
- /*
- Edges are stored in this way:
- Hey, am I an ascii artist, or what? :) niko.
- /3--------/7
- / | / |
- / | / |
- 1---------5 |
- | /2- - -|- -6
- | / | /
- |/ | /
- 0---------4/
- */
- edges[0].set(middle.X + diag.X, middle.Y + diag.Y, middle.Z + diag.Z);
- edges[1].set(middle.X + diag.X, middle.Y - diag.Y, middle.Z + diag.Z);
- edges[2].set(middle.X + diag.X, middle.Y + diag.Y, middle.Z - diag.Z);
- edges[3].set(middle.X + diag.X, middle.Y - diag.Y, middle.Z - diag.Z);
- edges[4].set(middle.X - diag.X, middle.Y + diag.Y, middle.Z + diag.Z);
- edges[5].set(middle.X - diag.X, middle.Y - diag.Y, middle.Z + diag.Z);
- edges[6].set(middle.X - diag.X, middle.Y + diag.Y, middle.Z - diag.Z);
- edges[7].set(middle.X - diag.X, middle.Y - diag.Y, middle.Z - diag.Z);
- }
- //! Repairs the box.
- /** Necessary if for example MinEdge and MaxEdge are swapped. */
- void repair()
- {
- T t;
- if (MinEdge.X > MaxEdge.X) {
- t = MinEdge.X;
- MinEdge.X = MaxEdge.X;
- MaxEdge.X = t;
- }
- if (MinEdge.Y > MaxEdge.Y) {
- t = MinEdge.Y;
- MinEdge.Y = MaxEdge.Y;
- MaxEdge.Y = t;
- }
- if (MinEdge.Z > MaxEdge.Z) {
- t = MinEdge.Z;
- MinEdge.Z = MaxEdge.Z;
- MaxEdge.Z = t;
- }
- }
- // Check if MaxEdge > MinEdge
- bool isValid() const
- {
- if (MinEdge.X > MaxEdge.X)
- return false;
- if (MinEdge.Y > MaxEdge.Y)
- return false;
- if (MinEdge.Z > MaxEdge.Z)
- return false;
- return true;
- }
- //! Calculates a new interpolated bounding box.
- /** d=0 returns other, d=1 returns this, all other values blend between
- the two boxes.
- \param other Other box to interpolate between
- \param d Value between 0.0f and 1.0f.
- \return Interpolated box. */
- aabbox3d<T> getInterpolated(const aabbox3d<T> &other, f32 d) const
- {
- f32 inv = 1.0f - d;
- return aabbox3d<T>((other.MinEdge * inv) + (MinEdge * d),
- (other.MaxEdge * inv) + (MaxEdge * d));
- }
- //! Determines if a point is within this box.
- /** Border is included (IS part of the box)!
- \param p: Point to check.
- \return True if the point is within the box and false if not */
- bool isPointInside(const vector3d<T> &p) const
- {
- return (p.X >= MinEdge.X && p.X <= MaxEdge.X &&
- p.Y >= MinEdge.Y && p.Y <= MaxEdge.Y &&
- p.Z >= MinEdge.Z && p.Z <= MaxEdge.Z);
- }
- //! Determines if a point is within this box and not its borders.
- /** Border is excluded (NOT part of the box)!
- \param p: Point to check.
- \return True if the point is within the box and false if not. */
- bool isPointTotalInside(const vector3d<T> &p) const
- {
- return (p.X > MinEdge.X && p.X < MaxEdge.X &&
- p.Y > MinEdge.Y && p.Y < MaxEdge.Y &&
- p.Z > MinEdge.Z && p.Z < MaxEdge.Z);
- }
- //! Check if this box is completely inside the 'other' box.
- /** \param other: Other box to check against.
- \return True if this box is completely inside the other box,
- otherwise false. */
- bool isFullInside(const aabbox3d<T> &other) const
- {
- return (MinEdge.X >= other.MinEdge.X && MinEdge.Y >= other.MinEdge.Y && MinEdge.Z >= other.MinEdge.Z &&
- MaxEdge.X <= other.MaxEdge.X && MaxEdge.Y <= other.MaxEdge.Y && MaxEdge.Z <= other.MaxEdge.Z);
- }
- //! Returns the intersection of this box with another, if possible.
- aabbox3d<T> intersect(const aabbox3d<T> &other) const
- {
- aabbox3d<T> out;
- if (!intersectsWithBox(other))
- return out;
- out.MaxEdge.X = min_(MaxEdge.X, other.MaxEdge.X);
- out.MaxEdge.Y = min_(MaxEdge.Y, other.MaxEdge.Y);
- out.MaxEdge.Z = min_(MaxEdge.Z, other.MaxEdge.Z);
- out.MinEdge.X = max_(MinEdge.X, other.MinEdge.X);
- out.MinEdge.Y = max_(MinEdge.Y, other.MinEdge.Y);
- out.MinEdge.Z = max_(MinEdge.Z, other.MinEdge.Z);
- return out;
- }
- //! Determines if the axis-aligned box intersects with another axis-aligned box.
- /** \param other: Other box to check a intersection with.
- \return True if there is an intersection with the other box,
- otherwise false. */
- bool intersectsWithBox(const aabbox3d<T> &other) const
- {
- return (MinEdge.X <= other.MaxEdge.X && MinEdge.Y <= other.MaxEdge.Y && MinEdge.Z <= other.MaxEdge.Z &&
- MaxEdge.X >= other.MinEdge.X && MaxEdge.Y >= other.MinEdge.Y && MaxEdge.Z >= other.MinEdge.Z);
- }
- //! Tests if the box intersects with a line
- /** \param line: Line to test intersection with.
- \return True if there is an intersection , else false. */
- bool intersectsWithLine(const line3d<T> &line) const
- {
- return intersectsWithLine(line.getMiddle(), line.getVector().normalize(),
- (T)(line.getLength() * 0.5));
- }
- //! Tests if the box intersects with a line
- /** \param linemiddle Center of the line.
- \param linevect Vector of the line.
- \param halflength Half length of the line.
- \return True if there is an intersection, else false. */
- bool intersectsWithLine(const vector3d<T> &linemiddle,
- const vector3d<T> &linevect, T halflength) const
- {
- const vector3d<T> e = getExtent() * (T)0.5;
- const vector3d<T> t = getCenter() - linemiddle;
- if ((fabs(t.X) > e.X + halflength * fabs(linevect.X)) ||
- (fabs(t.Y) > e.Y + halflength * fabs(linevect.Y)) ||
- (fabs(t.Z) > e.Z + halflength * fabs(linevect.Z)))
- return false;
- T r = e.Y * (T)fabs(linevect.Z) + e.Z * (T)fabs(linevect.Y);
- if (fabs(t.Y * linevect.Z - t.Z * linevect.Y) > r)
- return false;
- r = e.X * (T)fabs(linevect.Z) + e.Z * (T)fabs(linevect.X);
- if (fabs(t.Z * linevect.X - t.X * linevect.Z) > r)
- return false;
- r = e.X * (T)fabs(linevect.Y) + e.Y * (T)fabs(linevect.X);
- if (fabs(t.X * linevect.Y - t.Y * linevect.X) > r)
- return false;
- return true;
- }
- //! Classifies a relation with a plane.
- /** \param plane Plane to classify relation to.
- \return Returns ISREL3D_FRONT if the box is in front of the plane,
- ISREL3D_BACK if the box is behind the plane, and
- ISREL3D_CLIPPED if it is on both sides of the plane. */
- EIntersectionRelation3D classifyPlaneRelation(const plane3d<T> &plane) const
- {
- vector3d<T> nearPoint(MaxEdge);
- vector3d<T> farPoint(MinEdge);
- if (plane.Normal.X > (T)0) {
- nearPoint.X = MinEdge.X;
- farPoint.X = MaxEdge.X;
- }
- if (plane.Normal.Y > (T)0) {
- nearPoint.Y = MinEdge.Y;
- farPoint.Y = MaxEdge.Y;
- }
- if (plane.Normal.Z > (T)0) {
- nearPoint.Z = MinEdge.Z;
- farPoint.Z = MaxEdge.Z;
- }
- if (plane.Normal.dotProduct(nearPoint) + plane.D > (T)0)
- return ISREL3D_FRONT;
- if (plane.Normal.dotProduct(farPoint) + plane.D > (T)0)
- return ISREL3D_CLIPPED;
- return ISREL3D_BACK;
- }
- //! The near edge
- vector3d<T> MinEdge;
- //! The far edge
- vector3d<T> MaxEdge;
- };
- //! Typedef for a f32 3d bounding box.
- typedef aabbox3d<f32> aabbox3df;
- //! Typedef for an integer 3d bounding box.
- typedef aabbox3d<s32> aabbox3di;
- } // end namespace core
- } // end namespace irr
|