mirror of
https://github.com/copyrighttxt/watrbx-game-engine.git
synced 2026-09-04 20:57:49 +00:00
149 lines
4.1 KiB
C++
149 lines
4.1 KiB
C++
#include "RbxG3D/Frustum.h"
|
|
#include "rbx/Debug.h"
|
|
#include "util/Math.h"
|
|
#include "util/Extents.h"
|
|
|
|
using G3D::Plane;
|
|
using G3D::Vector3;
|
|
|
|
using namespace RBX;
|
|
|
|
// creates a frustum in a local coordinate space with the apex at the origin
|
|
// translates the frustum into the space defined by apex, dir and up
|
|
// performing the floating point operations in a local space helps reduce numerical errors when all of the points are far from the origin
|
|
Frustum::Frustum(const Vector3& apex, const Vector3& dir, const Vector3& up, const float nearDist, const float farDist, const float fovx, const float fovy)
|
|
{
|
|
Matrix3 rotation;
|
|
|
|
// up and dir should already be normalized and perpendicular to each other
|
|
RBXASSERT(dir.isUnit());
|
|
RBXASSERT(up.isUnit());
|
|
|
|
// due to rounding errors the cross product may not be a unit vector even though it should be.
|
|
const Vector3 right = up.cross(dir).unit();
|
|
|
|
rotation.setColumn(0, right);
|
|
rotation.setColumn(1, up);
|
|
rotation.setColumn(2, -dir);
|
|
|
|
// Near plane (wind backwards so normal faces into frustum)
|
|
// Recall that nearPlane, farPlane are positive numbers, so
|
|
// we need to negate them to produce actual z values.
|
|
faceArray.append(Plane(Vector3(0.0f, 0.0f, -1.0f), Vector3(0,0,-nearDist)));
|
|
|
|
// Right plane
|
|
faceArray.append(Plane(Vector3(-cosf(fovx/2.0f), 0.0f, -sinf(fovx/2.0f)), Vector3::zero()));
|
|
|
|
// Left plane
|
|
faceArray.append(Plane(Vector3(-faceArray.last().normal().x, 0.0f, faceArray.last().normal().z), Vector3::zero()));
|
|
|
|
// Bottom plane
|
|
faceArray.append(Plane(Vector3(0.0f, cosf(fovy/2.0f), -sinf(fovy/2.0f)), Vector3::zero()));
|
|
|
|
// Top plane
|
|
faceArray.append(Plane(Vector3(0.0f, -faceArray.last().normal().y, faceArray.last().normal().z), Vector3::zero()));
|
|
|
|
// Far plane
|
|
if (farDist < inf())
|
|
{
|
|
faceArray.append(Plane(Vector3(0.0f, 0.0f, 1.0f), Vector3(0.0f, 0.0f, -farDist)));
|
|
}
|
|
|
|
// Transform planes to world space
|
|
for (int face = 0; face < faceArray.size(); ++face)
|
|
{
|
|
// Since there is no scale factor, we don't have to
|
|
// worry about the inverse transpose of the normal.
|
|
Vector3 normal;
|
|
float d;
|
|
|
|
faceArray[face].getEquation(normal, d);
|
|
|
|
Vector3 newNormal = rotation * normal;
|
|
|
|
if (G3D::isFinite(d)) {
|
|
d = (newNormal * -d + apex).dot(newNormal);
|
|
faceArray[face] = Plane(newNormal, newNormal * d);
|
|
} else {
|
|
// When d is infinite, we can't multiply 0's by it without
|
|
// generating NaNs.
|
|
faceArray[face] = Plane::fromEquation(newNormal.x, newNormal.y, newNormal.z, d);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool Frustum::containsAABB(const Extents& aabb) const
|
|
{
|
|
for (int ii = 0; ii < 8; ++ii)
|
|
{
|
|
Vector3 corner = aabb.getCorner(ii);
|
|
if (!containsPoint(corner))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool Frustum::intersectsAABB(const RBX::Extents& aabb, const G3D::CoordinateFrame& extentsFrame) const
|
|
{
|
|
RBX::Extents worldBox = aabb.toWorldSpace(extentsFrame);
|
|
Vector3 extent = worldBox.size() * 0.5f;
|
|
Vector3 center = worldBox.center();
|
|
|
|
for (int ii = 0; ii < 6; ++ii)
|
|
{
|
|
const Plane& plane = faceArray[ii];
|
|
|
|
float d = plane.normal().dot(center) - plane.distance();
|
|
float r = Vector3(fabsf(plane.normal().x), fabsf(plane.normal().y), fabsf(plane.normal().z)).dot(extent);
|
|
|
|
// box is in negative half-space => outside the frustum
|
|
if (d + r < 0)
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool Frustum::containsAABB(const Extents& aabb, const G3D::CoordinateFrame& extentsFrame) const
|
|
{
|
|
for (int ii = 0; ii < 8; ++ii)
|
|
{
|
|
Vector3 corner = aabb.getCorner(ii);
|
|
Vector3 world = extentsFrame.pointToWorldSpace(corner);
|
|
if (!containsPoint(world))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool Frustum::containsPoint(const Vector3& point) const
|
|
{
|
|
for (int ii = 0; ii < faceArray.size(); ++ii)
|
|
{
|
|
const Plane& plane = faceArray[ii];
|
|
if (!plane.halfSpaceContains(point))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
bool Frustum::intersectsSphere(const Vector3& center, float radius) const
|
|
{
|
|
for (int ii = 0; ii < faceArray.size(); ++ii)
|
|
{
|
|
const Plane& p = faceArray[ii];
|
|
Plane offsetplane(p.normal(), p.distance() - radius);
|
|
if (!offsetplane.halfSpaceContains(center))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|