mirror of
https://github.com/overte-org/overte.git
synced 2025-06-25 16:29:49 +02:00
remove OVERSEND hack add ViewFrustum::calculateProjection() method used by OctreeQueryNode
674 lines
28 KiB
C++
674 lines
28 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 <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))))));
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
void ViewFrustum::calculateProjection() {
|
|
if (0.0f != _aspectRatio && 0.0f != _nearClip && 0.0f != _farClip && _nearClip != _farClip) {
|
|
// _projection is calculated from the frustum parameters
|
|
_projection = glm::perspective( glm::radians(_fieldOfView), _aspectRatio, _nearClip, _farClip);
|
|
|
|
// frustum corners are computed from inverseProjection
|
|
glm::mat4 inverseProjection = glm::inverse(_projection);
|
|
for (int i = 0; i < NUM_FRUSTUM_CORNERS; ++i) {
|
|
_corners[i] = inverseProjection * NDC_VALUES[i];
|
|
_corners[i] /= _corners[i].w;
|
|
}
|
|
|
|
// finally calculate planes and _ourModelViewProjectionMatrix
|
|
calculate();
|
|
}
|
|
}
|
|
|
|
//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::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;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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
|
|
};
|
|
|
|
CubeProjectedPolygon ViewFrustum::getProjectedPolygon(const AACube& 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;
|
|
}
|
|
|
|
// 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
|
|
}
|