overte/libraries/voxels/src/ViewFrustum.cpp
2013-07-19 01:00:55 -07:00

627 lines
28 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// ViewFrustum.cpp
// hifi
//
// Created by Brad Hefta-Gaub on 04/11/13.
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
//
// Simple view frustum class.
//
#include <algorithm>
#include <glm/gtx/transform.hpp>
#include <QDebug>
#include "CoverageMap.h"
#include "GeometryUtil.h"
#include "SharedUtil.h"
#include "ViewFrustum.h"
#include "VoxelConstants.h"
using namespace std;
ViewFrustum::ViewFrustum() :
_position(0,0,0),
_orientation(),
_direction(0,0,0),
_up(0,0,0),
_right(0,0,0),
_fieldOfView(0.0),
_aspectRatio(1.0),
_nearClip(0.1),
_farClip(500.0),
_keyholeRadius(DEFAULT_KEYHOLE_RADIUS),
_farTopLeft(0,0,0),
_farTopRight(0,0,0),
_farBottomLeft(0,0,0),
_farBottomRight(0,0,0),
_nearTopLeft(0,0,0),
_nearTopRight(0,0,0),
_nearBottomLeft(0,0,0),
_nearBottomRight(0,0,0)
{
}
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_FRONT, 0.0f));
}
/////////////////////////////////////////////////////////////////////////////////////
// ViewFrustum::calculateViewFrustum()
//
// 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() {
// compute the off-axis frustum parameters as we would for glFrustum
float left, right, bottom, top, nearVal, farVal;
glm::vec4 nearClipPlane, farClipPlane;
computeOffAxisFrustum(left, right, bottom, top, nearVal, farVal, nearClipPlane, farClipPlane);
// start with the corners of the near frustum window
glm::vec3 topLeft(left, top, -nearVal);
glm::vec3 topRight(right, top, -nearVal);
glm::vec3 bottomLeft(left, bottom, -nearVal);
glm::vec3 bottomRight(right, bottom, -nearVal);
// 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::translate(_eyeOffsetPosition) * glm::mat4_cast(_eyeOffsetOrientation);
_farTopLeft = glm::vec3(worldMatrix * glm::vec4(topLeft *
(-farClipPlane.w / glm::dot(topLeft, glm::vec3(farClipPlane))), 1.0f));
_farTopRight = glm::vec3(worldMatrix * glm::vec4(topRight *
(-farClipPlane.w / glm::dot(topRight, glm::vec3(farClipPlane))), 1.0f));
_farBottomLeft = glm::vec3(worldMatrix * glm::vec4(bottomLeft *
(-farClipPlane.w / glm::dot(bottomLeft, glm::vec3(farClipPlane))), 1.0f));
_farBottomRight = glm::vec3(worldMatrix * glm::vec4(bottomRight *
(-farClipPlane.w / glm::dot(bottomRight, glm::vec3(farClipPlane))), 1.0f));
_nearTopLeft = glm::vec3(worldMatrix * glm::vec4(topLeft *
(-nearClipPlane.w / glm::dot(topLeft, glm::vec3(nearClipPlane))), 1.0f));
_nearTopRight = glm::vec3(worldMatrix * glm::vec4(topRight *
(-nearClipPlane.w / glm::dot(topRight, glm::vec3(nearClipPlane))), 1.0f));
_nearBottomLeft = glm::vec3(worldMatrix * glm::vec4(bottomLeft *
(-nearClipPlane.w / glm::dot(bottomLeft, glm::vec3(nearClipPlane))), 1.0f));
_nearBottomRight = glm::vec3(worldMatrix * glm::vec4(bottomRight *
(-nearClipPlane.w / glm::dot(bottomRight, glm::vec3(nearClipPlane))), 1.0f));
// compute the offset position and axes in world space
_offsetPosition = glm::vec3(worldMatrix * glm::vec4(0.0f, 0.0f, 0.0f, 1.0f));
_offsetDirection = glm::vec3(worldMatrix * glm::vec4(0.0f, 0.0f, -1.0f, 0.0f));
_offsetUp = glm::vec3(worldMatrix * glm::vec4(0.0f, 1.0f, 0.0f, 0.0f));
_offsetRight = glm::vec3(worldMatrix * glm::vec4(1.0f, 0.0f, 0.0f, 0.0f));
// 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(_nearTopRight,_nearTopLeft,_farTopLeft);
_planes[BOTTOM_PLANE].set3Points(_nearBottomLeft,_nearBottomRight,_farBottomRight);
_planes[LEFT_PLANE ].set3Points(_nearBottomLeft,_farBottomLeft,_farTopLeft);
_planes[RIGHT_PLANE ].set3Points(_farBottomRight,_nearBottomRight,_nearTopRight);
_planes[NEAR_PLANE ].set3Points(_nearBottomRight,_nearBottomLeft,_nearTopLeft);
_planes[FAR_PLANE ].set3Points(_farBottomLeft,_farBottomRight,_farTopRight);
// Also calculate our projection matrix in case people want to project points...
// Projection matrix : Field of View, ratio, display range : near to far
glm::mat4 projection = glm::perspective(_fieldOfView, _aspectRatio, _nearClip, _farClip);
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
// Set up our keyhole bounding box...
glm::vec3 corner = _position - _keyholeRadius;
_keyholeBoundingBox = AABox(corner,(_keyholeRadius * 2.0f));
}
//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";
}
ViewFrustum::location ViewFrustum::pointInKeyhole(const glm::vec3& point) const {
ViewFrustum::location result = INTERSECT;
float distance = glm::distance(point, _position);
if (distance > _keyholeRadius) {
result = OUTSIDE;
} else if (distance < _keyholeRadius) {
result = INSIDE;
}
return result;
}
// To determine if two spheres intersect, simply calculate the distance between the centers of the two spheres.
// If the distance is greater than the sum of the two sphere radii, they dont intersect. Otherwise they intersect.
// If the distance plus the radius of sphere A is less than the radius of sphere B then, sphere A is inside of sphere B
ViewFrustum::location ViewFrustum::sphereInKeyhole(const glm::vec3& center, float radius) const {
ViewFrustum::location result = INTERSECT;
float distance = glm::distance(center, _position);
if (distance > (radius + _keyholeRadius)) {
result = OUTSIDE;
} else if ((distance + radius) < _keyholeRadius) {
result = INSIDE;
}
return result;
}
// A box is inside a sphere if all of its corners are inside the sphere
// A box intersects a sphere if any of its edges (as rays) interesect the sphere
// A box is outside a sphere if none of its edges (as rays) interesect the sphere
ViewFrustum::location ViewFrustum::boxInKeyhole(const AABox& box) const {
// First check to see if the box is in the bounding box for the sphere, if it's not, then we can short circuit
// this and not check with sphere penetration which is more expensive
if (!_keyholeBoundingBox.contains(box)) {
return OUTSIDE;
}
glm::vec3 penetration;
bool intersects = box.findSpherePenetration(_position, _keyholeRadius, penetration);
ViewFrustum::location result = OUTSIDE;
// if the box intersects the sphere, then it may also be inside... calculate further
if (intersects) {
result = INTERSECT;
// test all the corners, if they are all inside the sphere, the entire box is in the sphere
bool allPointsInside = true; // assume the best
for (int v = BOTTOM_LEFT_NEAR; v < TOP_LEFT_FAR; v++) {
glm::vec3 vertex = box.getVertex((BoxVertex)v);
if (!pointInKeyhole(vertex)) {
allPointsInside = false;
break;
}
}
if (allPointsInside) {
result = INSIDE;
}
}
return result;
}
ViewFrustum::location ViewFrustum::pointInFrustum(const glm::vec3& point) const {
ViewFrustum::location regularResult = INSIDE;
ViewFrustum::location keyholeResult = OUTSIDE;
// If we have a keyholeRadius, check that first, since it's cheaper
if (_keyholeRadius >= 0.0f) {
keyholeResult = pointInKeyhole(point);
}
if (keyholeResult == INSIDE) {
return keyholeResult;
}
// If we're not known to be INSIDE the keyhole, then check the regular frustum
for(int i=0; i < 6; i++) {
float distance = _planes[i].distance(point);
if (distance < 0) {
return keyholeResult; // escape early will be the value from checking the keyhole
}
}
return regularResult;
}
ViewFrustum::location ViewFrustum::sphereInFrustum(const glm::vec3& center, float radius) const {
ViewFrustum::location regularResult = INSIDE;
ViewFrustum::location keyholeResult = OUTSIDE;
// If we have a keyholeRadius, check that first, since it's cheaper
if (_keyholeRadius >= 0.0f) {
keyholeResult = sphereInKeyhole(center, radius);
}
if (keyholeResult == INSIDE) {
return keyholeResult;
}
float distance;
for(int i=0; i < 6; i++) {
distance = _planes[i].distance(center);
if (distance < -radius) {
// This is outside the regular frustum, so just return the value from checking the keyhole
return keyholeResult;
} else if (distance < radius) {
regularResult = INTERSECT;
}
}
return regularResult;
}
ViewFrustum::location ViewFrustum::boxInFrustum(const AABox& box) const {
ViewFrustum::location regularResult = INSIDE;
ViewFrustum::location keyholeResult = OUTSIDE;
// If we have a keyholeRadius, check that first, since it's cheaper
if (_keyholeRadius >= 0.0f) {
keyholeResult = boxInKeyhole(box);
}
if (keyholeResult == INSIDE) {
return keyholeResult;
}
for(int i=0; i < 6; i++) {
const glm::vec3& normal = _planes[i].getNormal();
const glm::vec3& boxVertexP = box.getVertexP(normal);
float planeToBoxVertexPDistance = _planes[i].distance(boxVertexP);
const glm::vec3& boxVertexN = box.getVertexN(normal);
float planeToBoxVertexNDistance = _planes[i].distance(boxVertexN);
if (planeToBoxVertexPDistance < 0) {
// This is outside the regular frustum, so just return the value from checking the keyhole
return keyholeResult;
} else if (planeToBoxVertexNDistance < 0) {
regularResult = INTERSECT;
}
}
return regularResult;
}
bool testMatches(glm::quat lhs, glm::quat rhs) {
return (fabs(lhs.x - rhs.x) <= EPSILON && fabs(lhs.y - rhs.y) <= EPSILON && fabs(lhs.z - rhs.z) <= EPSILON
&& fabs(lhs.w - rhs.w) <= EPSILON);
}
bool testMatches(glm::vec3 lhs, glm::vec3 rhs) {
return (fabs(lhs.x - rhs.x) <= EPSILON && fabs(lhs.y - rhs.y) <= EPSILON && fabs(lhs.z - rhs.z) <= EPSILON);
}
bool testMatches(float lhs, float rhs) {
return (fabs(lhs - rhs) <= EPSILON);
}
bool ViewFrustum::matches(const ViewFrustum& compareTo, bool debug) const {
bool result =
testMatches(compareTo._position, _position ) &&
testMatches(compareTo._direction, _direction ) &&
testMatches(compareTo._up, _up ) &&
testMatches(compareTo._right, _right ) &&
testMatches(compareTo._fieldOfView, _fieldOfView ) &&
testMatches(compareTo._aspectRatio, _aspectRatio ) &&
testMatches(compareTo._nearClip, _nearClip ) &&
testMatches(compareTo._farClip, _farClip ) &&
testMatches(compareTo._eyeOffsetPosition, _eyeOffsetPosition ) &&
testMatches(compareTo._eyeOffsetOrientation, _eyeOffsetOrientation);
if (!result && debug) {
qDebug("ViewFrustum::matches()... result=%s\n", debug::valueOf(result));
qDebug("%s -- compareTo._position=%f,%f,%f _position=%f,%f,%f\n",
(testMatches(compareTo._position,_position) ? "MATCHES " : "NO MATCH"),
compareTo._position.x, compareTo._position.y, compareTo._position.z,
_position.x, _position.y, _position.z );
qDebug("%s -- compareTo._direction=%f,%f,%f _direction=%f,%f,%f\n",
(testMatches(compareTo._direction, _direction) ? "MATCHES " : "NO MATCH"),
compareTo._direction.x, compareTo._direction.y, compareTo._direction.z,
_direction.x, _direction.y, _direction.z );
qDebug("%s -- compareTo._up=%f,%f,%f _up=%f,%f,%f\n",
(testMatches(compareTo._up, _up) ? "MATCHES " : "NO MATCH"),
compareTo._up.x, compareTo._up.y, compareTo._up.z,
_up.x, _up.y, _up.z );
qDebug("%s -- compareTo._right=%f,%f,%f _right=%f,%f,%f\n",
(testMatches(compareTo._right, _right) ? "MATCHES " : "NO MATCH"),
compareTo._right.x, compareTo._right.y, compareTo._right.z,
_right.x, _right.y, _right.z );
qDebug("%s -- compareTo._fieldOfView=%f _fieldOfView=%f\n",
(testMatches(compareTo._fieldOfView, _fieldOfView) ? "MATCHES " : "NO MATCH"),
compareTo._fieldOfView, _fieldOfView);
qDebug("%s -- compareTo._aspectRatio=%f _aspectRatio=%f\n",
(testMatches(compareTo._aspectRatio, _aspectRatio) ? "MATCHES " : "NO MATCH"),
compareTo._aspectRatio, _aspectRatio);
qDebug("%s -- compareTo._nearClip=%f _nearClip=%f\n",
(testMatches(compareTo._nearClip, _nearClip) ? "MATCHES " : "NO MATCH"),
compareTo._nearClip, _nearClip);
qDebug("%s -- compareTo._farClip=%f _farClip=%f\n",
(testMatches(compareTo._farClip, _farClip) ? "MATCHES " : "NO MATCH"),
compareTo._farClip, _farClip);
qDebug("%s -- compareTo._eyeOffsetPosition=%f,%f,%f _eyeOffsetPosition=%f,%f,%f\n",
(testMatches(compareTo._eyeOffsetPosition, _eyeOffsetPosition) ? "MATCHES " : "NO MATCH"),
compareTo._eyeOffsetPosition.x, compareTo._eyeOffsetPosition.y, compareTo._eyeOffsetPosition.z,
_eyeOffsetPosition.x, _eyeOffsetPosition.y, _eyeOffsetPosition.z);
qDebug("%s -- compareTo._eyeOffsetOrientation=%f,%f,%f,%f _eyeOffsetOrientation=%f,%f,%f,%f\n",
(testMatches(compareTo._eyeOffsetOrientation, _eyeOffsetOrientation) ? "MATCHES " : "NO MATCH"),
compareTo._eyeOffsetOrientation.x, compareTo._eyeOffsetOrientation.y,
compareTo._eyeOffsetOrientation.z, compareTo._eyeOffsetOrientation.w,
_eyeOffsetOrientation.x, _eyeOffsetOrientation.y, _eyeOffsetOrientation.z, _eyeOffsetOrientation.w);
}
return result;
}
void ViewFrustum::computePickRay(float x, float y, glm::vec3& origin, glm::vec3& direction) const {
origin = _nearTopLeft + x*(_nearTopRight - _nearTopLeft) + y*(_nearBottomLeft - _nearTopLeft);
direction = glm::normalize(origin - _position);
}
void ViewFrustum::computeOffAxisFrustum(float& left, float& right, float& bottom, float& top, float& near, float& far,
glm::vec4& nearClipPlane, glm::vec4& farClipPlane) const {
// compute our dimensions the usual way
float hheight = _nearClip * tanf(_fieldOfView * 0.5f * PI_OVER_180);
float hwidth = _aspectRatio * hheight;
// get our frustum corners in view space
glm::mat4 eyeMatrix = glm::mat4_cast(glm::inverse(_eyeOffsetOrientation)) * glm::translate(-_eyeOffsetPosition);
glm::vec4 corners[8];
float farScale = _farClip / _nearClip;
corners[0] = eyeMatrix * glm::vec4(-hwidth, -hheight, -_nearClip, 1.0f);
corners[1] = eyeMatrix * glm::vec4(hwidth, -hheight, -_nearClip, 1.0f);
corners[2] = eyeMatrix * glm::vec4(hwidth, hheight, -_nearClip, 1.0f);
corners[3] = eyeMatrix * glm::vec4(-hwidth, hheight, -_nearClip, 1.0f);
corners[4] = eyeMatrix * glm::vec4(-hwidth * farScale, -hheight * farScale, -_farClip, 1.0f);
corners[5] = eyeMatrix * glm::vec4(hwidth * farScale, -hheight * farScale, -_farClip, 1.0f);
corners[6] = eyeMatrix * glm::vec4(hwidth * farScale, hheight * farScale, -_farClip, 1.0f);
corners[7] = eyeMatrix * glm::vec4(-hwidth * farScale, hheight * farScale, -_farClip, 1.0f);
// find the minimum and maximum z values, which will be our near and far clip distances
near = FLT_MAX;
far = -FLT_MAX;
for (int i = 0; i < 8; i++) {
near = min(near, -corners[i].z);
far = max(far, -corners[i].z);
}
// get the near/far normal and use it to find the clip planes
glm::vec4 normal = eyeMatrix * 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]));
// 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 intersection = corners[i] * (-near / corners[i].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 {
qDebug("ViewFrustum::printDebugDetails()... \n");
qDebug("_position=%f,%f,%f\n", _position.x, _position.y, _position.z );
qDebug("_direction=%f,%f,%f\n", _direction.x, _direction.y, _direction.z );
qDebug("_up=%f,%f,%f\n", _up.x, _up.y, _up.z );
qDebug("_right=%f,%f,%f\n", _right.x, _right.y, _right.z );
qDebug("_fieldOfView=%f\n", _fieldOfView);
qDebug("_aspectRatio=%f\n", _aspectRatio);
qDebug("_nearClip=%f\n", _nearClip);
qDebug("_farClip=%f\n", _farClip);
qDebug("_eyeOffsetPosition=%f,%f,%f\n", _eyeOffsetPosition.x, _eyeOffsetPosition.y, _eyeOffsetPosition.z );
qDebug("_eyeOffsetOrientation=%f,%f,%f,%f\n", _eyeOffsetOrientation.x, _eyeOffsetOrientation.y, _eyeOffsetOrientation.z,
_eyeOffsetOrientation.w );
}
glm::vec2 ViewFrustum::projectPoint(glm::vec3 point, bool& pointInView) const {
glm::vec4 pointVec4 = glm::vec4(point,1) ;
glm::vec4 projectedPointVec4 = _ourModelViewProjectionMatrix * pointVec4;
pointInView = (projectedPointVec4.w > 0); // 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 AABox
//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
};
VoxelProjectedPolygon ViewFrustum::getProjectedPolygon(const AABox& box) const {
const glm::vec3& bottomNearRight = box.getCorner();
const glm::vec3& topFarLeft = box.getTopFarLeft();
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
VoxelProjectedPolygon 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.getCenter());
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.
glm::vec3 ViewFrustum::getFurthestPointFromCamera(const AABox& box) const {
const glm::vec3& center = box.getCenter();
const glm::vec3& bottomNearRight = box.getCorner();
const glm::vec3& topFarLeft = box.getTopFarLeft();
glm::vec3 furthestPoint;
if (_position.x < center.x) {
// we are to the right of the center, so the left edge is furthest
furthestPoint.x = topFarLeft.x;
} else {
// we are to the left of the center, so the right edge is furthest (at center ok too)
furthestPoint.x = bottomNearRight.x;
}
if (_position.y < center.y) {
// we are below of the center, so the top edge is furthest
furthestPoint.y = topFarLeft.y;
} else {
// we are above the center, so the lower edge is furthest (at center ok too)
furthestPoint.y = bottomNearRight.y;
}
if (_position.z < center.z) {
// we are to the near side of the center, so the far side edge is furthest
furthestPoint.z = topFarLeft.z;
} else {
// we are to the far side of the center, so the near side edge is furthest (at center ok too)
furthestPoint.z = bottomNearRight.z;
}
return furthestPoint;
}