AxisAlignedBox.h#

Parent directory (math)

Class used to handle common AABB operations.

Includes#

  • PVRCore/glm.h

  • PVRCore/types/Types.h

  • cfloat

Namespaces#

Classes#

Functions#

Source Code#

#pragma once
#include "PVRCore/types/Types.h"
#include "PVRCore/glm.h"
#include <cfloat>

namespace pvr {
namespace math {

struct Frustum
{
    glm::vec4 minusX;
    glm::vec4 plusX;
    glm::vec4 minusY;
    glm::vec4 plusY;
    glm::vec4 minusZ;
    glm::vec4 plusZ;

    glm::vec3 points[8];
};

struct ViewingFrustum : public Frustum
{
    bool isFrustum() const
    {
        bool xopp = (glm::dot(glm::vec3(minusX), glm::vec3(plusX)) < 0);
        bool yopp = (glm::dot(glm::vec3(minusY), glm::vec3(plusY)) < 0);
        bool zopp = (glm::dot(glm::vec3(minusZ), glm::vec3(plusZ)) < 0);
        return xopp && yopp && zopp;
    };
};

inline float distancePointToPlane(const glm::vec3& point, const glm::vec4& plane) { return glm::dot(point, glm::vec3(plane.x, plane.y, plane.z)) + plane.w; }

inline bool pointOnSide(const glm::vec3& point, const glm::vec4& plane) { return distancePointToPlane(point, plane) >= 0.0f; }

inline void getFrustumPlanes(pvr::Api api, const glm::mat4& projection_from_world, ViewingFrustum& frustum_out)
{
    const glm::vec4 row0 = glm::row(projection_from_world, 0);
    const glm::vec4 row1 = glm::row(projection_from_world, 1) * (api == pvr::Api::Vulkan ? -1.f : 1.f);
    const glm::vec4 row2 = glm::row(projection_from_world, 2);
    const glm::vec4 row3 = glm::row(projection_from_world, 3);

    frustum_out.minusX = row3 + row0;
    frustum_out.plusX = row3 - row0;
    frustum_out.minusY = row3 + row1;
    frustum_out.plusY = row3 - row1;
    frustum_out.minusZ = row3 + row2;
    frustum_out.plusZ = row3 - row2;

    frustum_out.minusX = frustum_out.minusX * (1 / glm::length(glm::vec3(frustum_out.minusX)));
    frustum_out.plusX = frustum_out.plusX * (1 / glm::length(glm::vec3(frustum_out.plusX)));
    frustum_out.minusY = frustum_out.minusY * (1 / glm::length(glm::vec3(frustum_out.minusY)));
    frustum_out.plusY = frustum_out.plusY * (1 / glm::length(glm::vec3(frustum_out.plusY)));
    frustum_out.minusZ = frustum_out.minusZ * (1 / glm::length(glm::vec3(frustum_out.minusZ)));
    frustum_out.plusZ = frustum_out.plusZ * (1 / glm::length(glm::vec3(frustum_out.plusZ)));
}

inline glm::vec3 IntersectPlanes(glm::vec4 p0, glm::vec4 p1, glm::vec4 p2)
{
    glm::vec3 bxc = glm::cross(glm::vec3(p1), glm::vec3(p2));
    glm::vec3 cxa = glm::cross(glm::vec3(p2), glm::vec3(p0));
    glm::vec3 axb = glm::cross(glm::vec3(p0), glm::vec3(p1));
    glm::vec3 r = -p0.w * bxc - p1.w * cxa - p2.w * axb;
    return r * (1 / glm::dot(glm::vec3(p0), bxc));
}

inline void getFrustumPoints(ViewingFrustum& frustum_out)
{
    frustum_out.points[0] = IntersectPlanes(frustum_out.minusZ, frustum_out.minusX, frustum_out.minusY);
    frustum_out.points[1] = IntersectPlanes(frustum_out.minusZ, frustum_out.minusX, frustum_out.plusY);
    frustum_out.points[2] = IntersectPlanes(frustum_out.minusZ, frustum_out.plusX, frustum_out.plusY);
    frustum_out.points[3] = IntersectPlanes(frustum_out.minusZ, frustum_out.plusX, frustum_out.minusY);
    frustum_out.points[4] = IntersectPlanes(frustum_out.plusZ, frustum_out.minusX, frustum_out.minusY);
    frustum_out.points[5] = IntersectPlanes(frustum_out.plusZ, frustum_out.minusX, frustum_out.plusY);
    frustum_out.points[6] = IntersectPlanes(frustum_out.plusZ, frustum_out.plusX, frustum_out.plusY);
    frustum_out.points[7] = IntersectPlanes(frustum_out.plusZ, frustum_out.plusX, frustum_out.minusY);
}

class AxisAlignedBoxMinMax;

class AxisAlignedBox
{
    glm::vec3 _center;
    glm::vec3 _halfExtent;

public:
    AxisAlignedBox(const glm::vec3& center = glm::vec3(0.0f), const glm::vec3& halfExtent = glm::vec3(0.f)) : _center(center), _halfExtent(halfExtent) {}

    AxisAlignedBox(const AxisAlignedBoxMinMax& copyFrom);

    void clear()
    {
        _center.x = _center.y = _center.z = 0.0f;
        _halfExtent = glm::vec3(0.f);
    }

    void setMinMax(const glm::vec3& min, const glm::vec3& max)
    {
        // compute the center.
        _center = (max + min) * .5f;
        _halfExtent = (max - min) * .5f;
    }

    void set(const glm::vec3& center, const glm::vec3& halfExtent)
    {
        _center = center;
        _halfExtent = halfExtent;
    }

    void add(const glm::vec3& point) { setMinMax(glm::min(point, getMin()), glm::max(point, getMax())); }

    void add(const AxisAlignedBox& aabb)
    {
        add(aabb.getMin());
        add(aabb.getMax());
    }

    void add(float x, float y, float z) { add(glm::vec3(x, y, z)); }

    glm::vec3 getMin() const { return _center - _halfExtent; }

    glm::vec3 getMax() const { return _center + _halfExtent; }

    void getMinMax(glm::vec3& outMin, glm::vec3& outMax) const
    {
        outMin = _center - _halfExtent;
        outMax = _center + _halfExtent;
    }

    void transform(const glm::mat4& m, AxisAlignedBox& outAABB) const
    {
        outAABB._center = glm::vec3(m[3]) + (glm::mat3(m) * this->center());

        glm::mat3 absModelMatrix;
        absModelMatrix[0] = glm::vec3(fabs(m[0][0]), fabs(m[0][1]), fabs(m[0][2]));
        absModelMatrix[1] = glm::vec3(fabs(m[1][0]), fabs(m[1][1]), fabs(m[1][2]));
        absModelMatrix[2] = glm::vec3(fabs(m[2][0]), fabs(m[2][1]), fabs(m[2][2]));

        outAABB._halfExtent = glm::vec3(absModelMatrix * this->getHalfExtent());
    }

    glm::vec3 getSize() const { return _halfExtent + _halfExtent; }

    glm::vec3 getHalfExtent() const { return _halfExtent; }

    glm::vec3 topLeftFar() const { return _center + glm::vec3(-_halfExtent.x, _halfExtent.y, _halfExtent.z); }

    glm::vec3 topCenterFar() const { return _center + glm::vec3(0, _halfExtent.y, _halfExtent.z); }

    glm::vec3 topRightFar() const { return _center + _halfExtent; }

    glm::vec3 topLeftNear() const { return _center + glm::vec3(-_halfExtent.x, _halfExtent.y, -_halfExtent.z); }

    glm::vec3 topCenterNear() const { return _center + glm::vec3(0., _halfExtent.y, -_halfExtent.z); }

    glm::vec3 topRightNear() const { return _center + glm::vec3(_halfExtent.x, _halfExtent.y, -_halfExtent.z); }

    glm::vec3 center() const { return _center; }

    glm::vec3 centerLeftNear() const { return _center + glm::vec3(-_halfExtent.x, 0, -_halfExtent.z); }
    glm::vec3 centerNear() const { return _center + glm::vec3(0, 0, -_halfExtent.z); }

    glm::vec3 centerRightNear() const { return _center + glm::vec3(_halfExtent.x, 0, -_halfExtent.z); }

    glm::vec3 centerLeftFar() const { return _center + glm::vec3(-_halfExtent.x, 0, _halfExtent.z); }

    glm::vec3 centerFar() const { return _center + glm::vec3(0, 0, _halfExtent.z); }

    glm::vec3 centerRightFar() const { return _center + glm::vec3(_halfExtent.x, 0, _halfExtent.z); }

    glm::vec3 bottomLeftNear() const { return _center + glm::vec3(-_halfExtent.x, -_halfExtent.y, -_halfExtent.z); }

    glm::vec3 bottomCenterNear() const { return _center + glm::vec3(0, -_halfExtent.y, -_halfExtent.z); }

    glm::vec3 bottomRightNear() const { return _center + glm::vec3(_halfExtent.x, -_halfExtent.y, -_halfExtent.z); }

    glm::vec3 bottomLeftFar() const { return _center + glm::vec3(-_halfExtent.x, -_halfExtent.y, _halfExtent.z); }

    glm::vec3 bottomCenterFar() const { return _center + glm::vec3(0, -_halfExtent.y, _halfExtent.z); }

    glm::vec3 bottomRightFar() const { return _center + glm::vec3(_halfExtent.x, -_halfExtent.y, _halfExtent.z); }

    void mergeBox(const AxisAlignedBox& rhs) { setMinMax(glm::min(getMin(), rhs.getMin()), glm::max(getMax(), rhs.getMax())); }

    bool operator==(const AxisAlignedBox& rhs) const { return _center == rhs._center && _halfExtent == rhs._halfExtent; }

    bool operator!=(const AxisAlignedBox& rhs) const { return !(*this == rhs); }
};

namespace {
inline bool noPointsOnSide(glm::vec3 blf, glm::vec3 tlf, glm::vec3 brf, glm::vec3 trf, glm::vec3 bln, glm::vec3 tln, glm::vec3 brn, glm::vec3 trn, glm::vec4 plane)
{
    if (pointOnSide(blf, plane) || pointOnSide(tlf, plane) || pointOnSide(brf, plane) || pointOnSide(trf, plane) || pointOnSide(bln, plane) || pointOnSide(tln, plane) ||
        pointOnSide(brn, plane) || pointOnSide(trn, plane))
    { return false; }
    return true;
}
} // namespace

inline bool aabbInFrustum(const AxisAlignedBox& box, const ViewingFrustum& frustum)
{
    glm::vec3 blf = box.bottomLeftFar();
    glm::vec3 tlf = box.topLeftFar();
    glm::vec3 brf = box.bottomRightFar();
    glm::vec3 trf = box.topRightFar();

    glm::vec3 bln = box.bottomLeftNear();
    glm::vec3 tln = box.topLeftNear();
    glm::vec3 brn = box.bottomRightNear();
    glm::vec3 trn = box.topRightNear();

    if (noPointsOnSide(blf, tlf, brf, trf, bln, tln, brn, trn, frustum.minusX) || noPointsOnSide(blf, tlf, brf, trf, bln, tln, brn, trn, frustum.plusX) ||
        noPointsOnSide(blf, tlf, brf, trf, bln, tln, brn, trn, frustum.minusY) || noPointsOnSide(blf, tlf, brf, trf, bln, tln, brn, trn, frustum.plusY) ||
        noPointsOnSide(blf, tlf, brf, trf, bln, tln, brn, trn, frustum.minusZ) || noPointsOnSide(blf, tlf, brf, trf, bln, tln, brn, trn, frustum.plusZ))
    { return false; }
    return true;
}

class AxisAlignedBoxMinMax
{
    glm::vec3 _min;
    glm::vec3 _max;

public:
    AxisAlignedBoxMinMax() : _min(std::numeric_limits<float>::max()), _max(std::numeric_limits<float>::lowest()) {}

    explicit AxisAlignedBoxMinMax(const AxisAlignedBox& copyFrom) : _min(copyFrom.getMin()), _max(copyFrom.getMax()) {}

    void setMin(const glm::vec3& min) { _min = min; }
    void setMax(const glm::vec3& max) { _max = max; }
    const glm::vec3& getMin() const { return _min; }
    const glm::vec3& getMax() const { return _max; }

    void add(const glm::vec3& point)
    {
        _min = glm::min(_min, point);
        _max = glm::max(_max, point);
    }
};

inline AxisAlignedBox::AxisAlignedBox(const AxisAlignedBoxMinMax& copyFrom) { setMinMax(copyFrom.getMin(), copyFrom.getMax()); }

} // namespace math
} // namespace pvr