// DECLARATION:
class BoundingVolume
{
public:
enum TestResult
{
TEST_OUTSIDE,
TEST_INTERSECT,
TEST_INSIDE
};
public:
virtual TestResult testIntersection( const vec3 &point ) const = 0;
virtual TestResult testIntersection( shared_ptr<const BoundingBox> box ) const = 0;
virtual TestResult testIntersection( shared_ptr<const BoundingSphere> sphere ) const = 0;
};
class BoundingBox : public BoundingVolume
{
public:
vec3 minimum;
vec3 maximum;
vec3 position;
public:
BoundingBox( vec3 minimum, vec3 maximum, vec3 position = vec3(0.0f) ) : BoundingVolume()
{
this->minimum = minimum;
this->maximum = maximum;
this->position = position;
}
virtual vec3 getPositiveVertex( const vec3 &normal ) const;
virtual vec3 getNegativeVertex( const vec3 &normal ) const;
TestResult testIntersection( const vec3 &point ) const;
TestResult testIntersection( shared_ptr<const BoundingBox> box ) const;
TestResult testIntersection( shared_ptr<const BoundingSphere> sphere ) const;
};
class Frustum : public BoundingVolume
{
public:
enum Plane
{
PLANE_BACK,
PLANE_FRONT,
PLANE_RIGHT,
PLANE_LEFT,
PLANE_TOP,
PLANE_BOTTOM
};
public:
Frustum( const mat4 &viewMatrix, const mat4 &projectionMatrix );
const vec4 &getPlane( const int plane ) const;
TestResult testIntersection( const vec3 &point ) const;
TestResult testIntersection( shared_ptr<const BoundingBox> box ) const;
TestResult testIntersection( shared_ptr<const BoundingSphere> sphere ) const;
protected:
vec4 m_planes[6];
};
// RELEVANT IMPLEMENTATION:
// compute frustum planes from view and projection matrices
Frustum::Frustum( const mat4 &viewMatrix, const mat4 &projectionMatrix ) : BoundingVolume()
{
const mat4 &v = viewMatrix;
const mat4 &p = projectionMatrix;
mat4 clipMatrix;
clipMatrix[0][0] = v[0][0 ]*p[0][0]+v[0][1]*p[1][0]+v[0][2]*p[2][0]+v[0][3]*p[3][0];
clipMatrix[1][0] = v[0][0]*p[0][1]+v[0][1]*p[1][1]+v[0][2]*p[2][1]+v[0][3]*p[3][1];
clipMatrix[2][0] = v[0][0]*p[0][2]+v[0][1]*p[1][2]+v[0][2]*p[2][2]+v[0][3]*p[3][2];
clipMatrix[3][0] = v[0][0]*p[0][3]+v[0][1]*p[1][3]+v[0][2]*p[2][3]+v[0][3]*p[3][3];
clipMatrix[0][1] = v[1][0]*p[0][0]+v[1][1]*p[1][0]+v[1][2]*p[2][0]+v[1][3]*p[3][0];
clipMatrix[1][1] = v[1][0]*p[0][1]+v[1][1]*p[1][1]+v[1][2]*p[2][1]+v[1][3]*p[3][1];
clipMatrix[2][1] = v[1][0]*p[0][2]+v[1][1]*p[1][2]+v[1][2]*p[2][2]+v[1][3]*p[3][2];
clipMatrix[3][1] = v[1][0]*p[0][3]+v[1][1]*p[1][3]+v[1][2]*p[2][3]+v[1][3]*p[3][3];
clipMatrix[0][2] = v[2][0]*p[0][0]+v[2][1]*p[1][0]+v[2][2]*p[2][0]+v[2][3]*p[3][0];
clipMatrix[1][2] = v[2][0]*p[0][1]+v[2][1]*p[1][1]+v[2][2]*p[2][1]+v[2][3]*p[3][1];
clipMatrix[2][2] = v[2][0]*p[0][2]+v[2][1]*p[1][2]+v[2][2]*p[2][2]+v[2][3]*p[3][2];
clipMatrix[3][2] = v[2][0]*p[0][3]+v[2][1]*p[1][3]+v[2][2]*p[2][3]+v[2][3]*p[3][3];
clipMatrix[0][3] = v[3][0]*p[0][0]+v[3][1]*p[1][0]+v[3][2]*p[2][0]+v[3][3]*p[3][0];
clipMatrix[1][3] = v[3][0]*p[0][1]+v[3][1]*p[1][1]+v[3][2]*p[2][1]+v[3][3]*p[3][1];
clipMatrix[2][3] = v[3][0]*p[0][2]+v[3][1]*p[1][2]+v[3][2]*p[2][2]+v[3][3]*p[3][2];
clipMatrix[3][3] = v[3][0]*p[0][3]+v[3][1]*p[1][3]+v[3][2]*p[2][3]+v[3][3]*p[3][3];
m_planes[PLANE_RIGHT].x = clipMatrix[3][0]-clipMatrix[0][0];
m_planes[PLANE_RIGHT].y = clipMatrix[3][1]-clipMatrix[0][1];
m_planes[PLANE_RIGHT].z = clipMatrix[3][2]-clipMatrix[0][2];
m_planes[PLANE_RIGHT].w = clipMatrix[3][3]-clipMatrix[0][3];
m_planes[PLANE_LEFT].x = clipMatrix[3][0]+clipMatrix[0][0];
m_planes[PLANE_LEFT].y = clipMatrix[3][1]+clipMatrix[0][1];
m_planes[PLANE_LEFT].z = clipMatrix[3][2]+clipMatrix[0][2];
m_planes[PLANE_LEFT].w = clipMatrix[3][3]+clipMatrix[0][3];
m_planes[PLANE_BOTTOM].x = clipMatrix[3][0]+clipMatrix[1][0];
m_planes[PLANE_BOTTOM].y = clipMatrix[3][1]+clipMatrix[1][1];
m_planes[PLANE_BOTTOM].z = clipMatrix[3][2]+clipMatrix[1][2];
m_planes[PLANE_BOTTOM].w = clipMatrix[3][3]+clipMatrix[1][3];
m_planes[PLANE_TOP].x = clipMatrix[3][0]-clipMatrix[1][0];
m_planes[PLANE_TOP].y = clipMatrix[3][1]-clipMatrix[1][1];
m_planes[PLANE_TOP].z = clipMatrix[3][2]-clipMatrix[1][2];
m_planes[PLANE_TOP].w = clipMatrix[3][3]-clipMatrix[1][3];
m_planes[PLANE_BACK].x = clipMatrix[3][0]-clipMatrix[2][0];
m_planes[PLANE_BACK].y = clipMatrix[3][1]-clipMatrix[2][1];
m_planes[PLANE_BACK].z = clipMatrix[3][2]-clipMatrix[2][2];
m_planes[PLANE_BACK].w = clipMatrix[3][3]-clipMatrix[2][3];
m_planes[PLANE_FRONT].x = clipMatrix[3][0]+clipMatrix[2][0];
m_planes[PLANE_FRONT].y = clipMatrix[3][1]+clipMatrix[2][1];
m_planes[PLANE_FRONT].z = clipMatrix[3][2]+clipMatrix[2][2];
m_planes[PLANE_FRONT].w = clipMatrix[3][3]+clipMatrix[2][3];
for( int i = 0; i < 6; i++ )
{
m_planes[i] = glm::normalize( m_planes[i] );
}
}
// check whether an AABB intersects the frustum
BoundingVolume::TestResult Frustum::testIntersection( shared_ptr<const BoundingBox> box ) const
{
TestResult result = TEST_INSIDE;
for( uint i = 0; i < 6; i++ )
{
const float pos = m_planes[i].w;
const vec3 normal = vec3(m_planes[i]);
if( glm::dot(normal, box->getPositiveVertex(normal))+pos < 0.0f )
{
return TEST_OUTSIDE;
}
if( glm::dot(normal, box->getNegativeVertex(normal))+pos < 0.0f )
{
result = TEST_INTERSECT;
}
}
return result;
}
vec3 BoundingBox::getPositiveVertex( const vec3 &normal ) const
{
vec3 positiveVertex = minimum;
if( normal.x >= 0.0f ) positiveVertex.x = maximum.x;
if( normal.y >= 0.0f ) positiveVertex.y = maximum.y;
if( normal.z >= 0.0f ) positiveVertex.z = maximum.z;
return position+positiveVertex;
}
vec3 BoundingBox::getNegativeVertex( const vec3 &normal ) const
{
vec3 negativeVertex = maximum;
if( normal.x >= 0.0f ) negativeVertex.x = minimum.x;
if( normal.y >= 0.0f ) negativeVertex.y = minimum.y;
if( normal.z >= 0.0f ) negativeVertex.z = minimum.z;
return position+negativeVertex;
}