209 lines
5.6 KiB
C++
209 lines
5.6 KiB
C++
#ifndef DIGGLER_AABB_HPP
|
|
#define DIGGLER_AABB_HPP
|
|
|
|
#include <limits>
|
|
#include <vector>
|
|
|
|
#include "platform/types/vec3.hpp"
|
|
|
|
namespace diggler {
|
|
|
|
///
|
|
/// @brief Axis-Aligned Bounding Box.
|
|
///
|
|
template<typename Vec3 = vec3>
|
|
class AABB {
|
|
public:
|
|
using T = typename Vec3::value_type;
|
|
|
|
union {
|
|
struct { Vec3 v1, v2; };
|
|
struct { Vec3 min, max; };
|
|
};
|
|
|
|
AABB(const Vec3 &vec1 = Vec3(), const Vec3 &vec2 = Vec3()) {
|
|
set(vec1, vec2);
|
|
}
|
|
|
|
///
|
|
/// @brief Sets the AABB points.
|
|
/// @param vec1 First point.
|
|
/// @param vec2 Second point.
|
|
/// @note Points coordinates are sorted so that each individual coordinate of vec2 is greater than vec1.
|
|
///
|
|
void set(const Vec3 &vec1, const Vec3 &vec2) {
|
|
v1 = vec1;
|
|
v2 = vec2;
|
|
if (v1.x > v2.x)
|
|
std::swap(v1.x, v2.x);
|
|
if (v1.y > v2.y)
|
|
std::swap(v1.y, v2.y);
|
|
if (v1.z > v2.z)
|
|
std::swap(v1.z, v2.z);
|
|
}
|
|
|
|
///
|
|
/// @brief Checks if a point is in the AABB.
|
|
/// @param point Point to check intersection with.
|
|
/// @returns `true` if point is in AABB, `false` otherwise.
|
|
///
|
|
bool intersects(const Vec3 &point) const {
|
|
if (point.x > v1.x && point.x < v2.x &&
|
|
point.y > v1.y && point.y < v2.y &&
|
|
point.z > v1.z && point.z < v2.z)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
///
|
|
/// @brief Checks if another AABB intersects this one.
|
|
/// @param other AABB to check intersection with.
|
|
/// @returns `true` if AABB intersects, `false` otherwise.
|
|
///
|
|
bool intersects(const AABB<Vec3> &other) const {
|
|
bool xOverlap = !(other.v1.x > v2.x || other.v2.x < v1.x);
|
|
bool yOverlap = !(other.v1.y > v2.y || other.v2.y < v1.y);
|
|
bool zOverlap = !(other.v1.z > v2.z || other.v2.z < v1.z);
|
|
return xOverlap && yOverlap && zOverlap;
|
|
}
|
|
|
|
///
|
|
/// @brief Does swept AABB collision computation.
|
|
/// @param other Other AABB to check swept collsion with.
|
|
/// @param vx,vy,vz Velocity.
|
|
/// @param[out] nx,ny,nz Collision normal.
|
|
///
|
|
float sweptCollision(AABB<Vec3> other, float vx, float vy, float vz, float &nx, float &ny, float &nz) {
|
|
T xInvEntry, yInvEntry, zInvEntry;
|
|
T xInvExit, yInvExit, zInvExit;
|
|
|
|
// find the distance between the objects on the near and far sides for both x and y
|
|
if (vx > 0.0f) {
|
|
xInvEntry = other.v1.x - v2.x;
|
|
xInvExit = other.v2.x - v1.x;
|
|
} else {
|
|
xInvEntry = other.v2.x - v1.x;
|
|
xInvExit = other.v1.x - v2.x;
|
|
}
|
|
|
|
if (vy > 0.0f) {
|
|
yInvEntry = other.v1.y - v2.y;
|
|
yInvExit = other.v2.y - v1.y;
|
|
} else {
|
|
yInvEntry = other.v2.y - v1.y;
|
|
yInvExit = other.v1.y - v2.y;
|
|
}
|
|
|
|
if (vz > 0.0f) {
|
|
zInvEntry = other.v1.z - v2.z;
|
|
zInvExit = other.v2.z - v1.z;
|
|
} else {
|
|
zInvEntry = other.v2.z - v1.z;
|
|
zInvExit = other.v1.z - v2.z;
|
|
}
|
|
|
|
// find time of collision and time of leaving for each axis (if statement is to prevent divide by zero)
|
|
float xEntry, yEntry, zEntry;
|
|
float xExit, yExit, zExit;
|
|
|
|
if (vx == 0.0f) {
|
|
xEntry = -std::numeric_limits<float>::infinity();
|
|
xExit = std::numeric_limits<float>::infinity();
|
|
} else {
|
|
xEntry = xInvEntry / vx;
|
|
xExit = xInvExit / vx;
|
|
}
|
|
|
|
if (vy == 0.0f) {
|
|
yEntry = -std::numeric_limits<float>::infinity();
|
|
yExit = std::numeric_limits<float>::infinity();
|
|
} else {
|
|
yEntry = yInvEntry / vy;
|
|
yExit = yInvExit / vy;
|
|
}
|
|
|
|
if (vz == 0.0f) {
|
|
zEntry = -std::numeric_limits<float>::infinity();
|
|
zExit = std::numeric_limits<float>::infinity();
|
|
} else {
|
|
zEntry = zInvEntry / vz;
|
|
zExit = zInvExit / vz;
|
|
}
|
|
|
|
// find the earliest/latest times of collision
|
|
float entryTime = std::max(std::max(xEntry, yEntry), zEntry);
|
|
float exitTime = std::min(std::min(xExit, yExit), zExit);
|
|
|
|
if ( entryTime > exitTime ||
|
|
(xEntry < 0.0f && yEntry < 0.0f && zEntry < 0.0f) ||
|
|
(xEntry > 1.0f || yEntry > 1.0f || zEntry > 1.0f)) {
|
|
// No collision
|
|
nx = ny = nz = 0.0f;
|
|
return 1.0f;
|
|
} else { // O noes I haz hit!
|
|
if (xEntry > yEntry && yEntry > zEntry) {
|
|
ny = nz = 0.0f;
|
|
nx = xInvEntry < 0.0f ? 1.0f : -1.0f;
|
|
} else if (yEntry > zEntry) {
|
|
nx = nz = 0.0f;
|
|
ny = yInvEntry < 0.0f ? 1.0f : -1.0f;
|
|
} else {
|
|
nx = ny = 0.0f;
|
|
nz = zInvEntry < 0.0f ? 1.0f : -1.0f;
|
|
}
|
|
|
|
// return the time of collision
|
|
return entryTime;
|
|
}
|
|
}
|
|
};
|
|
|
|
extern template class AABB<>;
|
|
|
|
template<typename Vec3 = typename glm::vec3>
|
|
class AABBVector : public std::vector<AABB<Vec3>> {
|
|
public:
|
|
using T = typename Vec3::value_type;
|
|
|
|
///
|
|
/// @brief Checks if a point is in the AABB vector.
|
|
/// @param point Point to check intersection with.
|
|
/// @returns `true` if point is in one AABB or more, `false` otherwise.
|
|
///
|
|
bool intersects(const Vec3 &point) const {
|
|
for (const AABB<Vec3> &box : *this)
|
|
if (box.intersects(point))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
///
|
|
/// @brief Checks if an AABB intersects any of this vector.
|
|
/// @param other AABB to check intersection with.
|
|
/// @returns `true` if AABBs intersects, `false` otherwise.
|
|
///
|
|
bool intersects(const AABB<Vec3> &other) const {
|
|
for (const AABB<Vec3> &box : *this)
|
|
if (box.intersects(other))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
///
|
|
/// @brief Checks if an AABB vector intersects this one.
|
|
/// @param other AABB vector to check intersection with.
|
|
/// @returns `true` if AABBs intersects, `false` otherwise.
|
|
///
|
|
bool intersects(const AABBVector<Vec3> &other) const {
|
|
for (const AABB<Vec3> &obox : other)
|
|
for (const AABB<Vec3> &box : *this)
|
|
if (box.intersects(obox))
|
|
return true;
|
|
return false;
|
|
}
|
|
};
|
|
|
|
}
|
|
|
|
#endif /* DIGGLER_AABB_HPP */
|