mirror of
https://github.com/overte-org/overte.git
synced 2025-06-17 17:20:18 +02:00
865 lines
36 KiB
C++
865 lines
36 KiB
C++
//
|
|
// ViewFrustum.cpp
|
|
// libraries/shared/src
|
|
//
|
|
// Created by Brad Hefta-Gaub on 04/11/13.
|
|
// Copyright 2013 High Fidelity, Inc.
|
|
//
|
|
// Distributed under the Apache License, Version 2.0.
|
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
|
//
|
|
|
|
#include <algorithm>
|
|
#include <array>
|
|
|
|
#include <glm/glm.hpp>
|
|
#include <glm/gtx/quaternion.hpp>
|
|
#include <glm/gtx/transform.hpp>
|
|
#include <glm/gtx/vector_angle.hpp>
|
|
#include <QtCore/QDebug>
|
|
|
|
|
|
#include "GeometryUtil.h"
|
|
#include "GLMHelpers.h"
|
|
#include "NumericalConstants.h"
|
|
#include "SharedLogging.h"
|
|
//#include "OctreeConstants.h"
|
|
#include "ViewFrustum.h"
|
|
|
|
using namespace std;
|
|
|
|
void ViewFrustum::setOrientation(const glm::quat& orientationAsQuaternion) {
|
|
_orientation = orientationAsQuaternion;
|
|
_right = glm::vec3(orientationAsQuaternion * glm::vec4(IDENTITY_RIGHT, 0.0f));
|
|
_up = glm::vec3(orientationAsQuaternion * glm::vec4(IDENTITY_UP, 0.0f));
|
|
_direction = glm::vec3(orientationAsQuaternion * glm::vec4(IDENTITY_FORWARD, 0.0f));
|
|
_view = glm::translate(mat4(), _position) * glm::mat4_cast(_orientation);
|
|
}
|
|
|
|
void ViewFrustum::setPosition(const glm::vec3& position) {
|
|
_position = position;
|
|
_view = glm::translate(mat4(), _position) * glm::mat4_cast(_orientation);
|
|
}
|
|
|
|
// Order cooresponds to the order defined in the BoxVertex enum.
|
|
static const glm::vec4 NDC_VALUES[NUM_FRUSTUM_CORNERS] = {
|
|
glm::vec4(-1.0f, -1.0f, -1.0f, 1.0f),
|
|
glm::vec4(1.0f, -1.0f, -1.0f, 1.0f),
|
|
glm::vec4(1.0f, 1.0f, -1.0f, 1.0f),
|
|
glm::vec4(-1.0f, 1.0f, -1.0f, 1.0f),
|
|
glm::vec4(-1.0f, -1.0f, 1.0f, 1.0f),
|
|
glm::vec4(1.0f, -1.0f, 1.0f, 1.0f),
|
|
glm::vec4(1.0f, 1.0f, 1.0f, 1.0f),
|
|
glm::vec4(-1.0f, 1.0f, 1.0f, 1.0f),
|
|
};
|
|
|
|
void ViewFrustum::setProjection(const glm::mat4& projection) {
|
|
_projection = projection;
|
|
glm::mat4 inverseProjection = glm::inverse(projection);
|
|
|
|
// compute frustum corners
|
|
for (int i = 0; i < NUM_FRUSTUM_CORNERS; ++i) {
|
|
_corners[i] = inverseProjection * NDC_VALUES[i];
|
|
_corners[i] /= _corners[i].w;
|
|
}
|
|
|
|
// compute frustum properties
|
|
_nearClip = -_corners[BOTTOM_LEFT_NEAR].z;
|
|
_farClip = -_corners[BOTTOM_LEFT_FAR].z;
|
|
_aspectRatio = (_corners[TOP_RIGHT_NEAR].x - _corners[BOTTOM_LEFT_NEAR].x) /
|
|
(_corners[TOP_RIGHT_NEAR].y - _corners[BOTTOM_LEFT_NEAR].y);
|
|
glm::vec4 top = inverseProjection * vec4(0.0f, 1.0f, -1.0f, 1.0f);
|
|
top /= top.w;
|
|
_fieldOfView = abs(glm::degrees(2.0f * abs(glm::angle(vec3(0.0f, 0.0f, -1.0f), glm::normalize(vec3(top))))));
|
|
_height = _corners[TOP_RIGHT_NEAR].y - _corners[BOTTOM_RIGHT_NEAR].y;
|
|
_width = _corners[TOP_RIGHT_NEAR].x - _corners[TOP_LEFT_NEAR].x;
|
|
}
|
|
|
|
// ViewFrustum::calculate()
|
|
//
|
|
// Description: this will calculate the view frustum bounds for a given position and direction
|
|
//
|
|
// Notes on how/why this works:
|
|
// http://www.lighthouse3d.com/tutorials/view-frustum-culling/view-frustums-shape/
|
|
//
|
|
void ViewFrustum::calculate() {
|
|
|
|
// find the intersections of the rays through the corners with the clip planes in view space,
|
|
// then transform them to world space
|
|
glm::mat4 worldMatrix = glm::translate(_position) * glm::mat4(glm::mat3(_right, _up, -_direction));
|
|
glm::vec4 v;
|
|
for (int i = 0; i < NUM_FRUSTUM_CORNERS; ++i) {
|
|
v = worldMatrix * _corners[i];
|
|
v /= v.w;
|
|
_cornersWorld[i] = glm::vec3(v);
|
|
}
|
|
|
|
// compute the six planes
|
|
// The planes are defined such that the normal points towards the inside of the view frustum.
|
|
// Testing if an object is inside the view frustum is performed by computing on which side of
|
|
// the plane the object resides. This can be done computing the signed distance from the point
|
|
// to the plane. If it is on the side that the normal is pointing, i.e. the signed distance
|
|
// is positive, then it is on the right side of the respective plane. If an object is on the
|
|
// right side of all six planes then the object is inside the frustum.
|
|
|
|
// the function set3Points assumes that the points are given in counter clockwise order, assume you
|
|
// are inside the frustum, facing the plane. Start with any point, and go counter clockwise for
|
|
// three consecutive points
|
|
_planes[TOP_PLANE].set3Points(_cornersWorld[TOP_RIGHT_NEAR], _cornersWorld[TOP_LEFT_NEAR], _cornersWorld[TOP_LEFT_FAR]);
|
|
_planes[BOTTOM_PLANE].set3Points(_cornersWorld[BOTTOM_LEFT_NEAR], _cornersWorld[BOTTOM_RIGHT_NEAR], _cornersWorld[BOTTOM_RIGHT_FAR]);
|
|
_planes[LEFT_PLANE].set3Points(_cornersWorld[BOTTOM_LEFT_NEAR], _cornersWorld[BOTTOM_LEFT_FAR], _cornersWorld[TOP_LEFT_FAR]);
|
|
_planes[RIGHT_PLANE].set3Points(_cornersWorld[BOTTOM_RIGHT_FAR], _cornersWorld[BOTTOM_RIGHT_NEAR], _cornersWorld[TOP_RIGHT_FAR]);
|
|
_planes[NEAR_PLANE].set3Points(_cornersWorld[BOTTOM_RIGHT_NEAR], _cornersWorld[BOTTOM_LEFT_NEAR], _cornersWorld[TOP_LEFT_NEAR]);
|
|
_planes[FAR_PLANE].set3Points(_cornersWorld[BOTTOM_LEFT_FAR], _cornersWorld[BOTTOM_RIGHT_FAR], _cornersWorld[TOP_RIGHT_FAR]);
|
|
|
|
// Also calculate our projection matrix in case people want to project points...
|
|
// Projection matrix : Field of View, ratio, display range : near to far
|
|
glm::vec3 lookAt = _position + _direction;
|
|
glm::mat4 view = glm::lookAt(_position, lookAt, _up);
|
|
|
|
// Our ModelViewProjection : multiplication of our 3 matrices (note: model is identity, so we can drop it)
|
|
_ourModelViewProjectionMatrix = _projection * view; // Remember, matrix multiplication is the other way around
|
|
}
|
|
|
|
//enum { TOP_PLANE = 0, BOTTOM_PLANE, LEFT_PLANE, RIGHT_PLANE, NEAR_PLANE, FAR_PLANE };
|
|
const char* ViewFrustum::debugPlaneName (int plane) const {
|
|
switch (plane) {
|
|
case TOP_PLANE: return "Top Plane";
|
|
case BOTTOM_PLANE: return "Bottom Plane";
|
|
case LEFT_PLANE: return "Left Plane";
|
|
case RIGHT_PLANE: return "Right Plane";
|
|
case NEAR_PLANE: return "Near Plane";
|
|
case FAR_PLANE: return "Far Plane";
|
|
}
|
|
return "Unknown";
|
|
}
|
|
|
|
void ViewFrustum::fromByteArray(const QByteArray& input) {
|
|
|
|
// From the wire!
|
|
glm::vec3 cameraPosition;
|
|
glm::quat cameraOrientation;
|
|
float cameraCenterRadius;
|
|
float cameraFov;
|
|
float cameraAspectRatio;
|
|
float cameraNearClip;
|
|
float cameraFarClip;
|
|
|
|
const unsigned char* startPosition = reinterpret_cast<const unsigned char*>(input.constData());
|
|
const unsigned char* sourceBuffer = startPosition;
|
|
|
|
// camera details
|
|
memcpy(&cameraPosition, sourceBuffer, sizeof(cameraPosition));
|
|
sourceBuffer += sizeof(cameraPosition);
|
|
sourceBuffer += unpackOrientationQuatFromBytes(sourceBuffer, cameraOrientation);
|
|
sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*)sourceBuffer, &cameraFov);
|
|
sourceBuffer += unpackFloatRatioFromTwoByte(sourceBuffer, cameraAspectRatio);
|
|
sourceBuffer += unpackClipValueFromTwoByte(sourceBuffer, cameraNearClip);
|
|
sourceBuffer += unpackClipValueFromTwoByte(sourceBuffer, cameraFarClip);
|
|
memcpy(&cameraCenterRadius, sourceBuffer, sizeof(cameraCenterRadius));
|
|
sourceBuffer += sizeof(cameraCenterRadius);
|
|
|
|
setPosition(cameraPosition);
|
|
setOrientation(cameraOrientation);
|
|
setCenterRadius(cameraCenterRadius);
|
|
|
|
// Also make sure it's got the correct lens details from the camera
|
|
if (0.0f != cameraAspectRatio &&
|
|
0.0f != cameraNearClip &&
|
|
0.0f != cameraFarClip &&
|
|
cameraNearClip != cameraFarClip) {
|
|
setProjection(glm::perspective(
|
|
glm::radians(cameraFov),
|
|
cameraAspectRatio,
|
|
cameraNearClip,
|
|
cameraFarClip));
|
|
|
|
calculate();
|
|
}
|
|
}
|
|
|
|
|
|
QByteArray ViewFrustum::toByteArray() {
|
|
static const int LARGE_ENOUGH = 1024;
|
|
QByteArray viewFrustumDataByteArray(LARGE_ENOUGH, 0);
|
|
unsigned char* destinationBuffer = reinterpret_cast<unsigned char*>(viewFrustumDataByteArray.data());
|
|
unsigned char* startPosition = destinationBuffer;
|
|
|
|
// camera details
|
|
memcpy(destinationBuffer, &_position, sizeof(_position));
|
|
destinationBuffer += sizeof(_position);
|
|
destinationBuffer += packOrientationQuatToBytes(destinationBuffer, _orientation);
|
|
destinationBuffer += packFloatAngleToTwoByte(destinationBuffer, _fieldOfView);
|
|
destinationBuffer += packFloatRatioToTwoByte(destinationBuffer, _aspectRatio);
|
|
destinationBuffer += packClipValueToTwoByte(destinationBuffer, _nearClip);
|
|
destinationBuffer += packClipValueToTwoByte(destinationBuffer, _farClip);
|
|
memcpy(destinationBuffer, &_centerSphereRadius, sizeof(_centerSphereRadius));
|
|
destinationBuffer += sizeof(_centerSphereRadius);
|
|
|
|
return viewFrustumDataByteArray.left(destinationBuffer - startPosition);
|
|
}
|
|
|
|
ViewFrustum::intersection ViewFrustum::calculateCubeFrustumIntersection(const AACube& cube) const {
|
|
// only check against frustum
|
|
ViewFrustum::intersection result = INSIDE;
|
|
for(int i = 0; i < NUM_FRUSTUM_PLANES; i++) {
|
|
const glm::vec3& normal = _planes[i].getNormal();
|
|
// check distance to farthest cube point
|
|
if ( _planes[i].distance(cube.getFarthestVertex(normal)) < 0.0f) {
|
|
return OUTSIDE;
|
|
} else {
|
|
// check distance to nearest cube point
|
|
if (_planes[i].distance(cube.getNearestVertex(normal)) < 0.0f) {
|
|
// cube straddles the plane
|
|
result = INTERSECT;
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
const float HALF_SQRT_THREE = 0.8660254f;
|
|
|
|
ViewFrustum::intersection ViewFrustum::calculateCubeKeyholeIntersection(const AACube& cube) const {
|
|
// check against centeral sphere
|
|
ViewFrustum::intersection sphereResult = INTERSECT;
|
|
glm::vec3 cubeOffset = cube.calcCenter() - _position;
|
|
float distance = glm::length(cubeOffset);
|
|
if (distance > EPSILON) {
|
|
glm::vec3 vertex = cube.getFarthestVertex(cubeOffset) - _position;
|
|
if (glm::dot(vertex, cubeOffset) < _centerSphereRadius * distance) {
|
|
// the most outward cube vertex is inside central sphere
|
|
return INSIDE;
|
|
}
|
|
if (!cube.touchesSphere(_position, _centerSphereRadius)) {
|
|
sphereResult = OUTSIDE;
|
|
}
|
|
} else if (_centerSphereRadius > HALF_SQRT_THREE * cube.getScale()) {
|
|
// the cube is in center of sphere and its bounding radius is inside
|
|
return INSIDE;
|
|
}
|
|
|
|
// check against frustum
|
|
ViewFrustum::intersection frustumResult = calculateCubeFrustumIntersection(cube);
|
|
|
|
return (frustumResult == OUTSIDE) ? sphereResult : frustumResult;
|
|
}
|
|
|
|
bool ViewFrustum::pointIntersectsFrustum(const glm::vec3& point) const {
|
|
// only check against frustum
|
|
for(int i = 0; i < NUM_FRUSTUM_PLANES; ++i) {
|
|
float distance = _planes[i].distance(point);
|
|
if (distance < 0.0f) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool ViewFrustum::sphereIntersectsFrustum(const glm::vec3& center, float radius) const {
|
|
// only check against frustum
|
|
for(int i = 0; i < NUM_FRUSTUM_PLANES; i++) {
|
|
float distance = _planes[i].distance(center);
|
|
if (distance < -radius) {
|
|
// This is outside the regular frustum, so just return the value from checking the keyhole
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool ViewFrustum::boxIntersectsFrustum(const AABox& box) const {
|
|
// only check against frustum
|
|
for(int i = 0; i < NUM_FRUSTUM_PLANES; i++) {
|
|
const glm::vec3& normal = _planes[i].getNormal();
|
|
// check distance to farthest box point
|
|
if ( _planes[i].distance(box.getFarthestVertex(normal)) < 0.0f) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool ViewFrustum::boxInsideFrustum(const AABox& box) const {
|
|
// only check against frustum
|
|
for (int i = 0; i < NUM_FRUSTUM_PLANES; i++) {
|
|
const glm::vec3& normal = _planes[i].getNormal();
|
|
// check distance to nearest box point
|
|
if (_planes[i].distance(box.getNearestVertex(normal)) < 0.0f) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool ViewFrustum::sphereIntersectsKeyhole(const glm::vec3& center, float radius) const {
|
|
// check positive touch against central sphere
|
|
if (glm::length(center - _position) <= (radius + _centerSphereRadius)) {
|
|
return true;
|
|
}
|
|
// check negative touches against frustum planes
|
|
for(int i = 0; i < NUM_FRUSTUM_PLANES; i++) {
|
|
if ( _planes[i].distance(center) < -radius) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool ViewFrustum::cubeIntersectsKeyhole(const AACube& cube) const {
|
|
// check positive touch against central sphere
|
|
if (cube.touchesSphere(_position, _centerSphereRadius)) {
|
|
return true;
|
|
}
|
|
// check negative touches against frustum planes
|
|
for(int i = 0; i < NUM_FRUSTUM_PLANES; i++) {
|
|
const glm::vec3& normal = _planes[i].getNormal();
|
|
if ( _planes[i].distance(cube.getFarthestVertex(normal)) < 0.0f) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool ViewFrustum::boxIntersectsKeyhole(const AABox& box) const {
|
|
// check positive touch against central sphere
|
|
if (box.touchesSphere(_position, _centerSphereRadius)) {
|
|
return true;
|
|
}
|
|
// check negative touches against frustum planes
|
|
for(int i = 0; i < NUM_FRUSTUM_PLANES; i++) {
|
|
const glm::vec3& normal = _planes[i].getNormal();
|
|
if ( _planes[i].distance(box.getFarthestVertex(normal)) < 0.0f) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool closeEnough(float a, float b, float relativeError) {
|
|
assert(relativeError >= 0.0f);
|
|
// NOTE: we add EPSILON to the denominator so we can avoid checking for division by zero.
|
|
// This method works fine when: fabsf(a + b) >> EPSILON
|
|
return fabsf(a - b) / (0.5f * fabsf(a + b) + EPSILON) < relativeError;
|
|
}
|
|
|
|
// TODO: the slop and relative error should be passed in by argument rather than hard-coded.
|
|
bool ViewFrustum::isVerySimilar(const ViewFrustum& other) const {
|
|
const float MIN_POSITION_SLOP_SQUARED = 25.0f; // 5 meters squared
|
|
const float MIN_ORIENTATION_DOT = 0.9924039f; // dot product of two quaternions 10 degrees apart
|
|
const float MIN_RELATIVE_ERROR = 0.01f; // 1%
|
|
|
|
return glm::distance2(_position, other._position) < MIN_POSITION_SLOP_SQUARED &&
|
|
fabsf(glm::dot(_orientation, other._orientation)) > MIN_ORIENTATION_DOT &&
|
|
closeEnough(_fieldOfView, other._fieldOfView, MIN_RELATIVE_ERROR) &&
|
|
closeEnough(_aspectRatio, other._aspectRatio, MIN_RELATIVE_ERROR) &&
|
|
closeEnough(_nearClip, other._nearClip, MIN_RELATIVE_ERROR) &&
|
|
closeEnough(_farClip, other._farClip, MIN_RELATIVE_ERROR) &&
|
|
closeEnough(_focalLength, other._focalLength, MIN_RELATIVE_ERROR) &&
|
|
closeEnough(_centerSphereRadius, other._centerSphereRadius, MIN_RELATIVE_ERROR);
|
|
}
|
|
|
|
PickRay ViewFrustum::computePickRay(float x, float y) {
|
|
glm::vec3 pickRayOrigin;
|
|
glm::vec3 pickRayDirection;
|
|
computePickRay(x, y, pickRayOrigin, pickRayDirection);
|
|
return PickRay(pickRayOrigin, pickRayDirection);
|
|
}
|
|
|
|
void ViewFrustum::computePickRay(float x, float y, glm::vec3& origin, glm::vec3& direction) const {
|
|
origin = _cornersWorld[TOP_LEFT_NEAR] + x * (_cornersWorld[TOP_RIGHT_NEAR] - _cornersWorld[TOP_LEFT_NEAR]) +
|
|
y * (_cornersWorld[BOTTOM_LEFT_NEAR] - _cornersWorld[TOP_LEFT_NEAR]);
|
|
direction = glm::normalize(origin - _position);
|
|
}
|
|
|
|
void ViewFrustum::computeOffAxisFrustum(float& left, float& right, float& bottom, float& top, float& nearValue, float& farValue,
|
|
glm::vec4& nearClipPlane, glm::vec4& farClipPlane) const {
|
|
// find the minimum and maximum z values, which will be our near and far clip distances
|
|
nearValue = FLT_MAX;
|
|
farValue = -FLT_MAX;
|
|
for (int i = 0; i < NUM_FRUSTUM_CORNERS; i++) {
|
|
nearValue = min(nearValue, -_corners[i].z);
|
|
farValue = max(farValue, -_corners[i].z);
|
|
}
|
|
|
|
// make sure the near clip isn't too small to be valid
|
|
const float MIN_NEAR = 0.01f;
|
|
nearValue = max(MIN_NEAR, nearValue);
|
|
|
|
// get the near/far normal and use it to find the clip planes
|
|
glm::vec4 normal = glm::vec4(0.0f, 0.0f, 1.0f, 0.0f);
|
|
nearClipPlane = glm::vec4(-normal.x, -normal.y, -normal.z, glm::dot(normal, _corners[0]));
|
|
farClipPlane = glm::vec4(normal.x, normal.y, normal.z, -glm::dot(normal, _corners[4]));
|
|
|
|
// compute the focal proportion (zero is near clip, one is far clip)
|
|
float focalProportion = (_focalLength - _nearClip) / (_farClip - _nearClip);
|
|
|
|
// get the extents at Z = -near
|
|
left = FLT_MAX;
|
|
right = -FLT_MAX;
|
|
bottom = FLT_MAX;
|
|
top = -FLT_MAX;
|
|
for (int i = 0; i < 4; i++) {
|
|
glm::vec4 corner = glm::mix(_corners[i], _corners[i + 4], focalProportion);
|
|
glm::vec4 intersection = corner * (-nearValue / corner.z);
|
|
left = min(left, intersection.x);
|
|
right = max(right, intersection.x);
|
|
bottom = min(bottom, intersection.y);
|
|
top = max(top, intersection.y);
|
|
}
|
|
}
|
|
|
|
void ViewFrustum::printDebugDetails() const {
|
|
qCDebug(shared, "ViewFrustum::printDebugDetails()...");
|
|
qCDebug(shared, "_position=%f,%f,%f", (double)_position.x, (double)_position.y, (double)_position.z );
|
|
qCDebug(shared, "_direction=%f,%f,%f", (double)_direction.x, (double)_direction.y, (double)_direction.z );
|
|
qCDebug(shared, "_up=%f,%f,%f", (double)_up.x, (double)_up.y, (double)_up.z );
|
|
qCDebug(shared, "_right=%f,%f,%f", (double)_right.x, (double)_right.y, (double)_right.z );
|
|
qCDebug(shared, "_fieldOfView=%f", (double)_fieldOfView);
|
|
qCDebug(shared, "_aspectRatio=%f", (double)_aspectRatio);
|
|
qCDebug(shared, "_centerSphereRadius=%f", (double)_centerSphereRadius);
|
|
qCDebug(shared, "_nearClip=%f", (double)_nearClip);
|
|
qCDebug(shared, "_farClip=%f", (double)_farClip);
|
|
qCDebug(shared, "_focalLength=%f", (double)_focalLength);
|
|
}
|
|
|
|
glm::vec2 ViewFrustum::projectPoint(glm::vec3 point, bool& pointInView) const {
|
|
|
|
glm::vec4 pointVec4 = glm::vec4(point, 1.0f);
|
|
glm::vec4 projectedPointVec4 = _ourModelViewProjectionMatrix * pointVec4;
|
|
pointInView = (projectedPointVec4.w > 0.0f); // math! If the w result is negative then the point is behind the viewer
|
|
|
|
// what happens with w is 0???
|
|
float x = projectedPointVec4.x / projectedPointVec4.w;
|
|
float y = projectedPointVec4.y / projectedPointVec4.w;
|
|
glm::vec2 projectedPoint(x,y);
|
|
|
|
// if the point is out of view we also need to flip the signs of x and y
|
|
if (!pointInView) {
|
|
projectedPoint.x = -x;
|
|
projectedPoint.y = -y;
|
|
}
|
|
|
|
return projectedPoint;
|
|
}
|
|
|
|
|
|
const int MAX_POSSIBLE_COMBINATIONS = 43;
|
|
|
|
const int hullVertexLookup[MAX_POSSIBLE_COMBINATIONS][MAX_PROJECTED_POLYGON_VERTEX_COUNT+1] = {
|
|
// Number of vertices in shadow polygon for the visible faces, then a list of the index of each vertice from the AACube
|
|
|
|
//0
|
|
{0}, // inside
|
|
{4, BOTTOM_RIGHT_NEAR, BOTTOM_RIGHT_FAR, TOP_RIGHT_FAR, TOP_RIGHT_NEAR}, // right
|
|
{4, BOTTOM_LEFT_FAR, BOTTOM_LEFT_NEAR, TOP_LEFT_NEAR, TOP_LEFT_FAR }, // left
|
|
{0}, // n/a
|
|
|
|
//4
|
|
{4, BOTTOM_RIGHT_NEAR, BOTTOM_LEFT_NEAR, BOTTOM_LEFT_FAR, BOTTOM_RIGHT_FAR}, // bottom
|
|
//5
|
|
{6, BOTTOM_RIGHT_NEAR, BOTTOM_LEFT_NEAR, BOTTOM_LEFT_FAR, BOTTOM_RIGHT_FAR, TOP_RIGHT_FAR, TOP_RIGHT_NEAR },//bottom, right
|
|
{6, BOTTOM_RIGHT_NEAR, BOTTOM_LEFT_NEAR, TOP_LEFT_NEAR, TOP_LEFT_FAR, BOTTOM_LEFT_FAR, BOTTOM_RIGHT_FAR, },//bottom, left
|
|
{0}, // n/a
|
|
//8
|
|
{4, TOP_RIGHT_NEAR, TOP_RIGHT_FAR, TOP_LEFT_FAR, TOP_LEFT_NEAR}, // top
|
|
{6, TOP_RIGHT_NEAR, BOTTOM_RIGHT_NEAR, BOTTOM_RIGHT_FAR, TOP_RIGHT_FAR, TOP_LEFT_FAR, TOP_LEFT_NEAR}, // top, right
|
|
{6, TOP_RIGHT_NEAR, TOP_RIGHT_FAR, TOP_LEFT_FAR, BOTTOM_LEFT_FAR, BOTTOM_LEFT_NEAR, TOP_LEFT_NEAR}, // top, left
|
|
{0}, // n/a
|
|
{0}, // n/a
|
|
{0}, // n/a
|
|
{0}, // n/a
|
|
{0}, // n/a
|
|
//16
|
|
{4, BOTTOM_LEFT_NEAR, BOTTOM_RIGHT_NEAR, TOP_RIGHT_NEAR, TOP_LEFT_NEAR }, // front or near
|
|
|
|
{6, BOTTOM_LEFT_NEAR, BOTTOM_RIGHT_NEAR, BOTTOM_RIGHT_FAR, TOP_RIGHT_FAR, TOP_RIGHT_NEAR, TOP_LEFT_NEAR }, // front, right
|
|
{6, BOTTOM_LEFT_FAR, BOTTOM_LEFT_NEAR, BOTTOM_RIGHT_NEAR, TOP_RIGHT_NEAR, TOP_LEFT_NEAR, TOP_LEFT_FAR, }, // front, left
|
|
{0}, // n/a
|
|
//20
|
|
{6, BOTTOM_LEFT_NEAR, BOTTOM_LEFT_FAR, BOTTOM_RIGHT_FAR, BOTTOM_RIGHT_NEAR, TOP_RIGHT_NEAR, TOP_LEFT_NEAR }, // front,bottom
|
|
|
|
//21
|
|
{6, BOTTOM_LEFT_NEAR, BOTTOM_LEFT_FAR, BOTTOM_RIGHT_FAR, TOP_RIGHT_FAR, TOP_RIGHT_NEAR, TOP_LEFT_NEAR }, //front,bottom,right
|
|
//22
|
|
{6, BOTTOM_LEFT_FAR, BOTTOM_RIGHT_FAR, BOTTOM_RIGHT_NEAR, TOP_RIGHT_NEAR, TOP_LEFT_NEAR, TOP_LEFT_FAR }, //front,bottom,left
|
|
{0}, // n/a
|
|
|
|
{6, BOTTOM_LEFT_NEAR, BOTTOM_RIGHT_NEAR, TOP_RIGHT_NEAR, TOP_RIGHT_FAR, TOP_LEFT_FAR, TOP_LEFT_NEAR}, // front, top
|
|
|
|
{6, BOTTOM_LEFT_NEAR, BOTTOM_RIGHT_NEAR, BOTTOM_RIGHT_FAR, TOP_RIGHT_FAR, TOP_LEFT_FAR, TOP_LEFT_NEAR }, // front, top, right
|
|
|
|
{6, BOTTOM_LEFT_FAR, BOTTOM_LEFT_NEAR, BOTTOM_RIGHT_NEAR, TOP_RIGHT_NEAR, TOP_RIGHT_FAR, TOP_LEFT_FAR }, // front, top, left
|
|
{0}, // n/a
|
|
{0}, // n/a
|
|
{0}, // n/a
|
|
{0}, // n/a
|
|
{0}, // n/a
|
|
//32
|
|
{4, BOTTOM_RIGHT_FAR, BOTTOM_LEFT_FAR, TOP_LEFT_FAR, TOP_RIGHT_FAR }, // back
|
|
{6, BOTTOM_RIGHT_NEAR, BOTTOM_RIGHT_FAR, BOTTOM_LEFT_FAR, TOP_LEFT_FAR, TOP_RIGHT_FAR, TOP_RIGHT_NEAR}, // back, right
|
|
//34
|
|
{6, BOTTOM_RIGHT_FAR, BOTTOM_LEFT_FAR, BOTTOM_LEFT_NEAR, TOP_LEFT_NEAR, TOP_LEFT_FAR, TOP_RIGHT_FAR }, // back, left
|
|
|
|
|
|
{0}, // n/a
|
|
//36
|
|
{6, BOTTOM_RIGHT_NEAR, BOTTOM_LEFT_NEAR, BOTTOM_LEFT_FAR, TOP_LEFT_FAR, TOP_RIGHT_FAR, BOTTOM_RIGHT_FAR}, // back, bottom
|
|
{6, BOTTOM_RIGHT_NEAR, BOTTOM_LEFT_NEAR, BOTTOM_LEFT_FAR, TOP_LEFT_FAR, TOP_RIGHT_FAR, TOP_RIGHT_NEAR},//back, bottom, right
|
|
|
|
// 38
|
|
{6, BOTTOM_RIGHT_NEAR, BOTTOM_LEFT_NEAR, TOP_LEFT_NEAR, TOP_LEFT_FAR, TOP_RIGHT_FAR, BOTTOM_RIGHT_FAR },//back, bottom, left
|
|
{0}, // n/a
|
|
|
|
// 40
|
|
{6, BOTTOM_RIGHT_FAR, BOTTOM_LEFT_FAR, TOP_LEFT_FAR, TOP_LEFT_NEAR, TOP_RIGHT_NEAR, TOP_RIGHT_FAR}, // back, top
|
|
|
|
{6, BOTTOM_RIGHT_NEAR, BOTTOM_RIGHT_FAR, BOTTOM_LEFT_FAR, TOP_LEFT_FAR, TOP_LEFT_NEAR, TOP_RIGHT_NEAR}, // back, top, right
|
|
//42
|
|
{6, TOP_RIGHT_NEAR, TOP_RIGHT_FAR, BOTTOM_RIGHT_FAR, BOTTOM_LEFT_FAR, BOTTOM_LEFT_NEAR, TOP_LEFT_NEAR}, // back, top, left
|
|
};
|
|
|
|
template <typename TBOX>
|
|
CubeProjectedPolygon ViewFrustum::computeProjectedPolygon(const TBOX& box) const {
|
|
const glm::vec3& bottomNearRight = box.getCorner();
|
|
glm::vec3 topFarLeft = box.calcTopFarLeft();
|
|
|
|
int lookUp = ((_position.x < bottomNearRight.x)) // 1 = right | compute 6-bit
|
|
+ ((_position.x > topFarLeft.x) << 1) // 2 = left | code to
|
|
+ ((_position.y < bottomNearRight.y) << 2) // 4 = bottom | classify camera
|
|
+ ((_position.y > topFarLeft.y) << 3) // 8 = top | with respect to
|
|
+ ((_position.z < bottomNearRight.z) << 4) // 16 = front/near | the 6 defining
|
|
+ ((_position.z > topFarLeft.z) << 5); // 32 = back/far | planes
|
|
|
|
int vertexCount = hullVertexLookup[lookUp][0]; //look up number of vertices
|
|
|
|
CubeProjectedPolygon projectedPolygon(vertexCount);
|
|
|
|
bool pointInView = true;
|
|
bool allPointsInView = false; // assume the best, but wait till we know we have a vertex
|
|
bool anyPointsInView = false; // assume the worst!
|
|
if (vertexCount) {
|
|
allPointsInView = true; // assume the best!
|
|
for (int i = 0; i < vertexCount; i++) {
|
|
int vertexNum = hullVertexLookup[lookUp][i + 1];
|
|
glm::vec3 point = box.getVertex((BoxVertex)vertexNum);
|
|
glm::vec2 projectedPoint = projectPoint(point, pointInView);
|
|
allPointsInView = allPointsInView && pointInView;
|
|
anyPointsInView = anyPointsInView || pointInView;
|
|
projectedPolygon.setVertex(i, projectedPoint);
|
|
}
|
|
|
|
/***
|
|
// Now that we've got the polygon, if it extends beyond the clipping window, then let's clip it
|
|
// NOTE: This clipping does not improve our overall performance. It basically causes more polygons to
|
|
// end up in the same quad/half and so the polygon lists get longer, and that's more calls to polygon.occludes()
|
|
if ( (projectedPolygon.getMaxX() > PolygonClip::RIGHT_OF_CLIPPING_WINDOW ) ||
|
|
(projectedPolygon.getMaxY() > PolygonClip::TOP_OF_CLIPPING_WINDOW ) ||
|
|
(projectedPolygon.getMaxX() < PolygonClip::LEFT_OF_CLIPPING_WINDOW ) ||
|
|
(projectedPolygon.getMaxY() < PolygonClip::BOTTOM_OF_CLIPPING_WINDOW) ) {
|
|
|
|
CoverageRegion::_clippedPolygons++;
|
|
|
|
glm::vec2* clippedVertices;
|
|
int clippedVertexCount;
|
|
PolygonClip::clipToScreen(projectedPolygon.getVertices(), vertexCount, clippedVertices, clippedVertexCount);
|
|
|
|
// Now reset the vertices of our projectedPolygon object
|
|
projectedPolygon.setVertexCount(clippedVertexCount);
|
|
for(int i = 0; i < clippedVertexCount; i++) {
|
|
projectedPolygon.setVertex(i, clippedVertices[i]);
|
|
}
|
|
delete[] clippedVertices;
|
|
|
|
lookUp += PROJECTION_CLIPPED;
|
|
}
|
|
***/
|
|
}
|
|
// set the distance from our camera position, to the closest vertex
|
|
float distance = glm::distance(getPosition(), box.calcCenter());
|
|
projectedPolygon.setDistance(distance);
|
|
projectedPolygon.setAnyInView(anyPointsInView);
|
|
projectedPolygon.setAllInView(allPointsInView);
|
|
projectedPolygon.setProjectionType(lookUp); // remember the projection type
|
|
return projectedPolygon;
|
|
}
|
|
|
|
CubeProjectedPolygon ViewFrustum::getProjectedPolygon(const AACube& box) const {
|
|
return computeProjectedPolygon(box);
|
|
}
|
|
|
|
CubeProjectedPolygon ViewFrustum::getProjectedPolygon(const AABox& box) const {
|
|
return computeProjectedPolygon(box);
|
|
}
|
|
|
|
bool ViewFrustum::getProjectedRect(const AABox& box, glm::vec2& bottomLeft, glm::vec2& topRight) const {
|
|
using Edge = std::pair<int, int>;
|
|
|
|
const int VERTEX_COUNT = 8;
|
|
const int EDGE_COUNT = 12;
|
|
// In theory, after clipping a box with a plane, only 4 new vertices at max
|
|
// should be created but due to potential imprecisions (edge almost parallel to
|
|
// near plane for instance) there might be more
|
|
const int MAX_VERTEX_COUNT = VERTEX_COUNT + 4 + 2;
|
|
|
|
std::array<glm::vec3, MAX_VERTEX_COUNT> vertices;
|
|
std::array<Edge, EDGE_COUNT> boxEdges{ {
|
|
Edge{BOTTOM_LEFT_NEAR, BOTTOM_RIGHT_NEAR},
|
|
Edge{TOP_LEFT_NEAR, TOP_RIGHT_NEAR},
|
|
Edge{BOTTOM_LEFT_FAR, BOTTOM_RIGHT_FAR},
|
|
Edge{TOP_LEFT_FAR, TOP_RIGHT_FAR},
|
|
Edge{BOTTOM_LEFT_NEAR, TOP_LEFT_NEAR},
|
|
Edge{BOTTOM_LEFT_FAR, TOP_LEFT_FAR},
|
|
Edge{BOTTOM_RIGHT_NEAR, TOP_RIGHT_NEAR},
|
|
Edge{BOTTOM_RIGHT_FAR, TOP_RIGHT_FAR},
|
|
Edge{BOTTOM_LEFT_NEAR, BOTTOM_LEFT_FAR},
|
|
Edge{TOP_LEFT_NEAR, TOP_LEFT_FAR},
|
|
Edge{BOTTOM_RIGHT_NEAR, BOTTOM_RIGHT_FAR},
|
|
Edge{TOP_RIGHT_NEAR, TOP_RIGHT_FAR}
|
|
} };
|
|
std::array<float, VERTEX_COUNT> distancesToNearPlane;
|
|
std::bitset<MAX_VERTEX_COUNT> areVerticesInside;
|
|
int vertexCount = VERTEX_COUNT;
|
|
int i;
|
|
|
|
// Clip the hull with the near plane.
|
|
const auto& nearPlane = _planes[NEAR_PLANE];
|
|
|
|
for (i = 0; i < VERTEX_COUNT; i++) {
|
|
vertices[i] = box.getVertex(static_cast<BoxVertex>(i));
|
|
distancesToNearPlane[i] = nearPlane.distance(vertices[i]);
|
|
}
|
|
|
|
for (i = 0; i < EDGE_COUNT; i++) {
|
|
const auto& edgeVertexIndices = boxEdges[i];
|
|
const auto& startVertex = vertices[edgeVertexIndices.first];
|
|
const auto& endVertex = vertices[edgeVertexIndices.second];
|
|
float startVertexDistance = distancesToNearPlane[edgeVertexIndices.first];
|
|
float endVertexDistance = distancesToNearPlane[edgeVertexIndices.second];
|
|
bool isStartPointInside = startVertexDistance >= 0.0f;
|
|
bool isEndPointInside = endVertexDistance >= 0.0f;
|
|
|
|
areVerticesInside.set(edgeVertexIndices.first, isStartPointInside);
|
|
areVerticesInside.set(edgeVertexIndices.second, isEndPointInside);
|
|
|
|
if (isStartPointInside != isEndPointInside) {
|
|
// One of the two vertices is behind the near plane so add a new clipped vertex
|
|
// add tag it as projectable.
|
|
vertices[vertexCount] = startVertex + (endVertex - startVertex) * (startVertexDistance / (startVertexDistance - endVertexDistance));
|
|
areVerticesInside.set(vertexCount);
|
|
vertexCount++;
|
|
}
|
|
}
|
|
|
|
// Project points that are inside
|
|
bottomLeft.x = std::numeric_limits<float>::max();
|
|
bottomLeft.y = std::numeric_limits<float>::max();
|
|
topRight.x = -std::numeric_limits<float>::max();
|
|
topRight.y = -std::numeric_limits<float>::max();
|
|
for (i = 0; i < vertexCount; i++) {
|
|
if (areVerticesInside[i]) {
|
|
bool isPointInside;
|
|
auto projectedPoint = projectPoint(vertices[i], isPointInside);
|
|
bottomLeft.x = std::min(bottomLeft.x, projectedPoint.x);
|
|
bottomLeft.y = std::min(bottomLeft.y, projectedPoint.y);
|
|
topRight.x = std::max(topRight.x, projectedPoint.x);
|
|
topRight.y = std::max(topRight.y, projectedPoint.y);
|
|
}
|
|
}
|
|
|
|
bottomLeft.x = glm::clamp(bottomLeft.x, -1.0f, 1.0f);
|
|
bottomLeft.y = glm::clamp(bottomLeft.y, -1.0f, 1.0f);
|
|
topRight.x = glm::clamp(topRight.x, -1.0f, 1.0f);
|
|
topRight.y = glm::clamp(topRight.y, -1.0f, 1.0f);
|
|
|
|
return areVerticesInside.any();
|
|
}
|
|
|
|
// Similar strategy to getProjectedPolygon() we use the knowledge of camera position relative to the
|
|
// axis-aligned voxels to determine which of the voxels vertices must be the furthest. No need for
|
|
// squares and square-roots. Just compares.
|
|
void ViewFrustum::getFurthestPointFromCamera(const AACube& box, glm::vec3& furthestPoint) const {
|
|
const glm::vec3& bottomNearRight = box.getCorner();
|
|
float scale = box.getScale();
|
|
float halfScale = scale * 0.5f;
|
|
|
|
if (_position.x < bottomNearRight.x + halfScale) {
|
|
// we are to the right of the center, so the left edge is furthest
|
|
furthestPoint.x = bottomNearRight.x + scale;
|
|
} else {
|
|
furthestPoint.x = bottomNearRight.x;
|
|
}
|
|
|
|
if (_position.y < bottomNearRight.y + halfScale) {
|
|
// we are below of the center, so the top edge is furthest
|
|
furthestPoint.y = bottomNearRight.y + scale;
|
|
} else {
|
|
furthestPoint.y = bottomNearRight.y;
|
|
}
|
|
|
|
if (_position.z < bottomNearRight.z + halfScale) {
|
|
// we are to the near side of the center, so the far side edge is furthest
|
|
furthestPoint.z = bottomNearRight.z + scale;
|
|
} else {
|
|
furthestPoint.z = bottomNearRight.z;
|
|
}
|
|
}
|
|
|
|
const ViewFrustum::Corners ViewFrustum::getCorners(const float depth) const {
|
|
glm::vec3 normal = glm::normalize(_direction);
|
|
|
|
auto getCorner = [&](enum::BoxVertex nearCorner, enum::BoxVertex farCorner) {
|
|
auto dir = glm::normalize(_cornersWorld[nearCorner] - _cornersWorld[farCorner]);
|
|
auto factor = depth / glm::dot(dir, normal);
|
|
return _position + factor * dir;
|
|
};
|
|
|
|
return Corners{
|
|
getCorner(TOP_LEFT_NEAR, TOP_LEFT_FAR),
|
|
getCorner(TOP_RIGHT_NEAR, TOP_RIGHT_FAR),
|
|
getCorner(BOTTOM_LEFT_NEAR, BOTTOM_LEFT_FAR),
|
|
getCorner(BOTTOM_RIGHT_NEAR, BOTTOM_RIGHT_FAR)
|
|
};
|
|
}
|
|
|
|
float ViewFrustum::distanceToCamera(const glm::vec3& point) const {
|
|
glm::vec3 temp = getPosition() - point;
|
|
float distanceToPoint = sqrtf(glm::dot(temp, temp));
|
|
return distanceToPoint;
|
|
}
|
|
|
|
void ViewFrustum::evalProjectionMatrix(glm::mat4& proj) const {
|
|
proj = _projection;
|
|
}
|
|
|
|
glm::mat4 ViewFrustum::evalProjectionMatrixRange(float rangeNear, float rangeFar) const {
|
|
|
|
// make sure range near far make sense
|
|
assert(rangeNear > 0.0f);
|
|
assert(rangeFar > rangeNear);
|
|
|
|
// recreate a projection matrix for only a range of depth of this frustum.
|
|
|
|
// take the current projection
|
|
glm::mat4 rangeProj = _projection;
|
|
|
|
float A = -(rangeFar + rangeNear) / (rangeFar - rangeNear);
|
|
float B = -2.0f * rangeFar*rangeNear / ((rangeFar - rangeNear));
|
|
|
|
rangeProj[2][2] = A;
|
|
rangeProj[3][2] = B;
|
|
return rangeProj;
|
|
}
|
|
|
|
|
|
void ViewFrustum::evalViewTransform(Transform& view) const {
|
|
view.setTranslation(getPosition());
|
|
view.setRotation(getOrientation());
|
|
}
|
|
|
|
void ViewFrustum::invalidate() {
|
|
// these setting should make nearly all intersection tests fail
|
|
for (int i = 0; i < NUM_FRUSTUM_PLANES; ++i) {
|
|
_planes[i].invalidate();
|
|
}
|
|
_centerSphereRadius = -1.0e6f; // -10^6 should be negative enough
|
|
}
|
|
|
|
void ViewFrustum::getSidePlanes(::Plane planes[4]) const {
|
|
planes[0] = _planes[TOP_PLANE];
|
|
planes[1] = _planes[BOTTOM_PLANE];
|
|
planes[2] = _planes[LEFT_PLANE];
|
|
planes[3] = _planes[RIGHT_PLANE];
|
|
}
|
|
|
|
void ViewFrustum::getTransformedSidePlanes(const Transform& transform, ::Plane planes[4]) const {
|
|
glm::mat4 normalTransform;
|
|
transform.getInverseTransposeMatrix(normalTransform);
|
|
getSidePlanes(planes);
|
|
for (auto i = 0; i < 4; i++) {
|
|
// We assume the transform doesn't have a non uniform scale component to apply the
|
|
// transform to the normal without using the correct transpose of inverse.
|
|
auto transformedNormal = normalTransform * Transform::Vec4(planes[i].getNormal(), 0.0f);
|
|
auto planePoint = transform.transform(planes[i].getPoint());
|
|
glm::vec3 planeNormal(transformedNormal.x, transformedNormal.y, transformedNormal.z);
|
|
planes[i].setNormalAndPoint(planeNormal, planePoint);
|
|
}
|
|
}
|
|
|
|
void ViewFrustum::getUniformlyTransformedSidePlanes(const Transform& transform, ::Plane planes[4]) const {
|
|
getSidePlanes(planes);
|
|
for (auto i = 0; i < 4; i++) {
|
|
// We assume the transform doesn't have a non uniform scale component to apply the
|
|
// transform to the normal without using the correct transpose of inverse.
|
|
auto planeNormal = transform.transformDirection(planes[i].getNormal());
|
|
auto planePoint = transform.transform(planes[i].getPoint());
|
|
planes[i].setNormalAndPoint(planeNormal, planePoint);
|
|
}
|
|
}
|
|
|
|
void ViewFrustum::tesselateSides(Triangle triangles[8]) const {
|
|
tesselateSides(_cornersWorld, triangles);
|
|
}
|
|
|
|
void ViewFrustum::tesselateSides(const Transform& transform, Triangle triangles[8]) const {
|
|
glm::vec3 points[8];
|
|
|
|
for (auto i = 0; i < 8; i++) {
|
|
points[i] = transform.transform(_cornersWorld[i]);
|
|
}
|
|
|
|
tesselateSides(points, triangles);
|
|
}
|
|
|
|
void ViewFrustum::tesselateSidesAndFar(const Transform& transform, Triangle triangles[10], float farDistance) const {
|
|
glm::vec3 points[8];
|
|
|
|
// First 4 points are at near
|
|
for (auto i = 0; i < 4; i++) {
|
|
points[i] = transform.transform(_cornersWorld[i]);
|
|
}
|
|
auto farCorners = getCorners(farDistance);
|
|
|
|
points[BOTTOM_LEFT_FAR] = transform.transform(farCorners.bottomLeft);
|
|
points[BOTTOM_RIGHT_FAR] = transform.transform(farCorners.bottomRight);
|
|
points[TOP_LEFT_FAR] = transform.transform(farCorners.topLeft);
|
|
points[TOP_RIGHT_FAR] = transform.transform(farCorners.topRight);
|
|
|
|
tesselateSides(points, triangles);
|
|
// Add far side
|
|
triangles[8].v0 = points[BOTTOM_LEFT_FAR];
|
|
triangles[8].v1 = points[BOTTOM_RIGHT_FAR];
|
|
triangles[8].v2 = points[TOP_RIGHT_FAR];
|
|
|
|
triangles[9].v0 = points[BOTTOM_LEFT_FAR];
|
|
triangles[9].v1 = points[TOP_LEFT_FAR];
|
|
triangles[9].v2 = points[TOP_RIGHT_FAR];
|
|
}
|
|
|
|
void ViewFrustum::tesselateSides(const glm::vec3 points[8], Triangle triangles[8]) {
|
|
static_assert(BOTTOM_RIGHT_NEAR == (BOTTOM_LEFT_NEAR + 1), "Assuming a certain sequence in corners");
|
|
static_assert(TOP_RIGHT_NEAR == (BOTTOM_RIGHT_NEAR + 1), "Assuming a certain sequence in corners");
|
|
static_assert(TOP_LEFT_NEAR == (TOP_RIGHT_NEAR + 1), "Assuming a certain sequence in corners");
|
|
static_assert(BOTTOM_RIGHT_FAR == (BOTTOM_LEFT_FAR + 1), "Assuming a certain sequence in corners");
|
|
static_assert(TOP_RIGHT_FAR == (BOTTOM_RIGHT_FAR + 1), "Assuming a certain sequence in corners");
|
|
static_assert(TOP_LEFT_FAR == (TOP_RIGHT_FAR + 1), "Assuming a certain sequence in corners");
|
|
static const int triangleVertexIndices[8][3] = {
|
|
{ BOTTOM_LEFT_NEAR, BOTTOM_LEFT_FAR, BOTTOM_RIGHT_FAR },{ BOTTOM_LEFT_NEAR, BOTTOM_RIGHT_NEAR, BOTTOM_RIGHT_FAR },
|
|
{ BOTTOM_RIGHT_NEAR, TOP_RIGHT_NEAR, TOP_RIGHT_FAR },{ BOTTOM_RIGHT_NEAR, BOTTOM_RIGHT_FAR, TOP_RIGHT_FAR },
|
|
{ TOP_RIGHT_NEAR, TOP_LEFT_NEAR, TOP_RIGHT_FAR },{ TOP_LEFT_NEAR, TOP_RIGHT_FAR, TOP_LEFT_FAR },
|
|
{ BOTTOM_LEFT_NEAR, TOP_LEFT_NEAR, TOP_LEFT_FAR },{ BOTTOM_LEFT_NEAR, BOTTOM_LEFT_FAR, TOP_LEFT_FAR }
|
|
};
|
|
|
|
for (auto i = 0; i < 8; i++) {
|
|
auto& triangle = triangles[i];
|
|
auto vertexIndices = triangleVertexIndices[i];
|
|
|
|
triangle.v0 = points[vertexIndices[0]];
|
|
triangle.v1 = points[vertexIndices[1]];
|
|
triangle.v2 = points[vertexIndices[2]];
|
|
}
|
|
}
|
|
|
|
bool ViewFrustum::isPerspective() const {
|
|
return _projection[3][2] != 0.0f && _projection[2][3] != 0.0f && _projection[3][3] == 0.0f;
|
|
}
|