quadrilateralcowboy/idlib/bv/Frustum.cpp
2020-06-12 14:06:25 -07:00

2843 lines
78 KiB
C++

/*
===========================================================================
Doom 3 GPL Source Code
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
Doom 3 Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Doom 3 Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
#include "../precompiled.h"
#pragma hdrstop
//#define FRUSTUM_DEBUG
/*
bit 0 = min x
bit 1 = max x
bit 2 = min y
bit 3 = max y
bit 4 = min z
bit 5 = max z
*/
static int boxVertPlanes[8] = {
( (1<<0) | (1<<2) | (1<<4) ),
( (1<<1) | (1<<2) | (1<<4) ),
( (1<<1) | (1<<3) | (1<<4) ),
( (1<<0) | (1<<3) | (1<<4) ),
( (1<<0) | (1<<2) | (1<<5) ),
( (1<<1) | (1<<2) | (1<<5) ),
( (1<<1) | (1<<3) | (1<<5) ),
( (1<<0) | (1<<3) | (1<<5) ),
};
/*
============
BoxToPoints
============
*/
void BoxToPoints( const idVec3 &center, const idVec3 &extents, const idMat3 &axis, idVec3 points[8] ) {
idMat3 ax;
idVec3 temp[4];
ax[0] = extents[0] * axis[0];
ax[1] = extents[1] * axis[1];
ax[2] = extents[2] * axis[2];
temp[0] = center - ax[0];
temp[1] = center + ax[0];
temp[2] = ax[1] - ax[2];
temp[3] = ax[1] + ax[2];
points[0] = temp[0] - temp[3];
points[1] = temp[1] - temp[3];
points[2] = temp[1] + temp[2];
points[3] = temp[0] + temp[2];
points[4] = temp[0] - temp[2];
points[5] = temp[1] - temp[2];
points[6] = temp[1] + temp[3];
points[7] = temp[0] + temp[3];
}
/*
================
idFrustum::PlaneDistance
================
*/
float idFrustum::PlaneDistance( const idPlane &plane ) const {
float min, max;
AxisProjection( plane.Normal(), min, max );
if ( min + plane[3] > 0.0f ) {
return min + plane[3];
}
if ( max + plane[3] < 0.0f ) {
return max + plane[3];
}
return 0.0f;
}
/*
================
idFrustum::PlaneSide
================
*/
int idFrustum::PlaneSide( const idPlane &plane, const float epsilon ) const {
float min, max;
AxisProjection( plane.Normal(), min, max );
if ( min + plane[3] > epsilon ) {
return PLANESIDE_FRONT;
}
if ( max + plane[3] < epsilon ) {
return PLANESIDE_BACK;
}
return PLANESIDE_CROSS;
}
/*
============
idFrustum::CullPoint
============
*/
bool idFrustum::CullPoint( const idVec3 &point ) const {
idVec3 p;
float scale;
// transform point to frustum space
p = ( point - origin ) * axis.Transpose();
// test whether or not the point is within the frustum
if ( p.x < dNear || p.x > dFar ) {
return true;
}
scale = p.x * invFar;
if ( idMath::Fabs( p.y ) > dLeft * scale ) {
return true;
}
if ( idMath::Fabs( p.z ) > dUp * scale ) {
return true;
}
return false;
}
/*
============
idFrustum::CullLocalBox
Tests if any of the planes of the frustum can be used as a separating plane.
3 muls best case
25 muls worst case
============
*/
bool idFrustum::CullLocalBox( const idVec3 &localOrigin, const idVec3 &extents, const idMat3 &localAxis ) const {
float d1, d2;
idVec3 testOrigin;
idMat3 testAxis;
// near plane
d1 = dNear - localOrigin.x;
d2 = idMath::Fabs( extents[0] * localAxis[0][0] ) +
idMath::Fabs( extents[1] * localAxis[1][0] ) +
idMath::Fabs( extents[2] * localAxis[2][0] );
if ( d1 - d2 > 0.0f ) {
return true;
}
// far plane
d1 = localOrigin.x - dFar;
if ( d1 - d2 > 0.0f ) {
return true;
}
testOrigin = localOrigin;
testAxis = localAxis;
if ( testOrigin.y < 0.0f ) {
testOrigin.y = -testOrigin.y;
testAxis[0][1] = -testAxis[0][1];
testAxis[1][1] = -testAxis[1][1];
testAxis[2][1] = -testAxis[2][1];
}
// test left/right planes
d1 = dFar * testOrigin.y - dLeft * testOrigin.x;
d2 = idMath::Fabs( extents[0] * ( dFar * testAxis[0][1] - dLeft * testAxis[0][0] ) ) +
idMath::Fabs( extents[1] * ( dFar * testAxis[1][1] - dLeft * testAxis[1][0] ) ) +
idMath::Fabs( extents[2] * ( dFar * testAxis[2][1] - dLeft * testAxis[2][0] ) );
if ( d1 - d2 > 0.0f ) {
return true;
}
if ( testOrigin.z < 0.0f ) {
testOrigin.z = -testOrigin.z;
testAxis[0][2] = -testAxis[0][2];
testAxis[1][2] = -testAxis[1][2];
testAxis[2][2] = -testAxis[2][2];
}
// test up/down planes
d1 = dFar * testOrigin.z - dUp * testOrigin.x;
d2 = idMath::Fabs( extents[0] * ( dFar * testAxis[0][2] - dUp * testAxis[0][0] ) ) +
idMath::Fabs( extents[1] * ( dFar * testAxis[1][2] - dUp * testAxis[1][0] ) ) +
idMath::Fabs( extents[2] * ( dFar * testAxis[2][2] - dUp * testAxis[2][0] ) );
if ( d1 - d2 > 0.0f ) {
return true;
}
return false;
}
/*
============
idFrustum::CullBounds
Tests if any of the planes of the frustum can be used as a separating plane.
24 muls best case
37 muls worst case
============
*/
bool idFrustum::CullBounds( const idBounds &bounds ) const {
idVec3 localOrigin, center, extents;
idMat3 localAxis;
center = ( bounds[0] + bounds[1] ) * 0.5f;
extents = bounds[1] - center;
// transform the bounds into the space of this frustum
localOrigin = ( center - origin ) * axis.Transpose();
localAxis = axis.Transpose();
return CullLocalBox( localOrigin, extents, localAxis );
}
/*
============
idFrustum::CullBounds
Tests if any of the planes of the frustum can be used as a separating plane.
39 muls best case
61 muls worst case
============
*/
bool idFrustum::CullBox( const idBox &box ) const {
idVec3 localOrigin;
idMat3 localAxis;
// transform the box into the space of this frustum
localOrigin = ( box.GetCenter() - origin ) * axis.Transpose();
localAxis = box.GetAxis() * axis.Transpose();
return CullLocalBox( localOrigin, box.GetExtents(), localAxis );
}
/*
============
idFrustum::CullSphere
Tests if any of the planes of the frustum can be used as a separating plane.
9 muls best case
21 muls worst case
============
*/
bool idFrustum::CullSphere( const idSphere &sphere ) const {
float d, r, rs, sFar;
idVec3 center;
center = ( sphere.GetOrigin() - origin ) * axis.Transpose();
r = sphere.GetRadius();
// test near plane
if ( dNear - center.x > r ) {
return true;
}
// test far plane
if ( center.x - dFar > r ) {
return true;
}
rs = r * r;
sFar = dFar * dFar;
// test left/right planes
d = dFar * idMath::Fabs( center.y ) - dLeft * center.x;
if ( ( d * d ) > rs * ( sFar + dLeft * dLeft ) ) {
return true;
}
// test up/down planes
d = dFar * idMath::Fabs( center.z ) - dUp * center.x;
if ( ( d * d ) > rs * ( sFar + dUp * dUp ) ) {
return true;
}
return false;
}
/*
============
idFrustum::CullLocalFrustum
Tests if any of the planes of this frustum can be used as a separating plane.
0 muls best case
30 muls worst case
============
*/
bool idFrustum::CullLocalFrustum( const idFrustum &localFrustum, const idVec3 indexPoints[8], const idVec3 cornerVecs[4] ) const {
int index;
float dx, dy, dz, leftScale, upScale;
// test near plane
dy = -localFrustum.axis[1].x;
dz = -localFrustum.axis[2].x;
index = ( FLOATSIGNBITSET( dy ) << 1 ) | FLOATSIGNBITSET( dz );
dx = -cornerVecs[index].x;
index |= ( FLOATSIGNBITSET( dx ) << 2 );
if ( indexPoints[index].x < dNear ) {
return true;
}
// test far plane
dy = localFrustum.axis[1].x;
dz = localFrustum.axis[2].x;
index = ( FLOATSIGNBITSET( dy ) << 1 ) | FLOATSIGNBITSET( dz );
dx = cornerVecs[index].x;
index |= ( FLOATSIGNBITSET( dx ) << 2 );
if ( indexPoints[index].x > dFar ) {
return true;
}
leftScale = dLeft * invFar;
// test left plane
dy = dFar * localFrustum.axis[1].y - dLeft * localFrustum.axis[1].x;
dz = dFar * localFrustum.axis[2].y - dLeft * localFrustum.axis[2].x;
index = ( FLOATSIGNBITSET( dy ) << 1 ) | FLOATSIGNBITSET( dz );
dx = dFar * cornerVecs[index].y - dLeft * cornerVecs[index].x;
index |= ( FLOATSIGNBITSET( dx ) << 2 );
if ( indexPoints[index].y > indexPoints[index].x * leftScale ) {
return true;
}
// test right plane
dy = -dFar * localFrustum.axis[1].y - dLeft * localFrustum.axis[1].x;
dz = -dFar * localFrustum.axis[2].y - dLeft * localFrustum.axis[2].x;
index = ( FLOATSIGNBITSET( dy ) << 1 ) | FLOATSIGNBITSET( dz );
dx = -dFar * cornerVecs[index].y - dLeft * cornerVecs[index].x;
index |= ( FLOATSIGNBITSET( dx ) << 2 );
if ( indexPoints[index].y < -indexPoints[index].x * leftScale ) {
return true;
}
upScale = dUp * invFar;
// test up plane
dy = dFar * localFrustum.axis[1].z - dUp * localFrustum.axis[1].x;
dz = dFar * localFrustum.axis[2].z - dUp * localFrustum.axis[2].x;
index = ( FLOATSIGNBITSET( dy ) << 1 ) | FLOATSIGNBITSET( dz );
dx = dFar * cornerVecs[index].z - dUp * cornerVecs[index].x;
index |= ( FLOATSIGNBITSET( dx ) << 2 );
if ( indexPoints[index].z > indexPoints[index].x * upScale ) {
return true;
}
// test down plane
dy = -dFar * localFrustum.axis[1].z - dUp * localFrustum.axis[1].x;
dz = -dFar * localFrustum.axis[2].z - dUp * localFrustum.axis[2].x;
index = ( FLOATSIGNBITSET( dy ) << 1 ) | FLOATSIGNBITSET( dz );
dx = -dFar * cornerVecs[index].z - dUp * cornerVecs[index].x;
index |= ( FLOATSIGNBITSET( dx ) << 2 );
if ( indexPoints[index].z < -indexPoints[index].x * upScale ) {
return true;
}
return false;
}
/*
============
idFrustum::CullFrustum
Tests if any of the planes of this frustum can be used as a separating plane.
58 muls best case
88 muls worst case
============
*/
bool idFrustum::CullFrustum( const idFrustum &frustum ) const {
idFrustum localFrustum;
idVec3 indexPoints[8], cornerVecs[4];
// transform the given frustum into the space of this frustum
localFrustum = frustum;
localFrustum.origin = ( frustum.origin - origin ) * axis.Transpose();
localFrustum.axis = frustum.axis * axis.Transpose();
localFrustum.ToIndexPointsAndCornerVecs( indexPoints, cornerVecs );
return CullLocalFrustum( localFrustum, indexPoints, cornerVecs );
}
/*
============
idFrustum::CullLocalWinding
============
*/
bool idFrustum::CullLocalWinding( const idVec3 *points, const int numPoints, int *pointCull ) const {
int i, pCull, culled;
float leftScale, upScale;
leftScale = dLeft * invFar;
upScale = dUp * invFar;
culled = -1;
for ( i = 0; i < numPoints; i++ ) {
const idVec3 &p = points[i];
pCull = 0;
if ( p.x < dNear ) {
pCull = 1;
}
else if ( p.x > dFar ) {
pCull = 2;
}
if ( idMath::Fabs( p.y ) > p.x * leftScale ) {
pCull |= 4 << FLOATSIGNBITSET( p.y );
}
if ( idMath::Fabs( p.z ) > p.x * upScale ) {
pCull |= 16 << FLOATSIGNBITSET( p.z );
}
culled &= pCull;
pointCull[i] = pCull;
}
return ( culled != 0 );
}
/*
============
idFrustum::CullWinding
============
*/
bool idFrustum::CullWinding( const idWinding &winding ) const {
int i, *pointCull;
idVec3 *localPoints;
idMat3 transpose;
localPoints = (idVec3 *) _alloca16( winding.GetNumPoints() * sizeof( idVec3 ) );
pointCull = (int *) _alloca16( winding.GetNumPoints() * sizeof( int ) );
transpose = axis.Transpose();
for ( i = 0; i < winding.GetNumPoints(); i++ ) {
localPoints[i] = ( winding[i].ToVec3() - origin ) * transpose;
}
return CullLocalWinding( localPoints, winding.GetNumPoints(), pointCull );
}
/*
============
idFrustum::BoundsCullLocalFrustum
Tests if any of the bounding box planes can be used as a separating plane.
============
*/
bool idFrustum::BoundsCullLocalFrustum( const idBounds &bounds, const idFrustum &localFrustum, const idVec3 indexPoints[8], const idVec3 cornerVecs[4] ) const {
int index;
float dx, dy, dz;
dy = -localFrustum.axis[1].x;
dz = -localFrustum.axis[2].x;
index = ( FLOATSIGNBITSET( dy ) << 1 ) | FLOATSIGNBITSET( dz );
dx = -cornerVecs[index].x;
index |= ( FLOATSIGNBITSET( dx ) << 2 );
if ( indexPoints[index].x < bounds[0].x ) {
return true;
}
dy = localFrustum.axis[1].x;
dz = localFrustum.axis[2].x;
index = ( FLOATSIGNBITSET( dy ) << 1 ) | FLOATSIGNBITSET( dz );
dx = cornerVecs[index].x;
index |= ( FLOATSIGNBITSET( dx ) << 2 );
if ( indexPoints[index].x > bounds[1].x ) {
return true;
}
dy = -localFrustum.axis[1].y;
dz = -localFrustum.axis[2].y;
index = ( FLOATSIGNBITSET( dy ) << 1 ) | FLOATSIGNBITSET( dz );
dx = -cornerVecs[index].y;
index |= ( FLOATSIGNBITSET( dx ) << 2 );
if ( indexPoints[index].y < bounds[0].y ) {
return true;
}
dy = localFrustum.axis[1].y;
dz = localFrustum.axis[2].y;
index = ( FLOATSIGNBITSET( dy ) << 1 ) | FLOATSIGNBITSET( dz );
dx = cornerVecs[index].y;
index |= ( FLOATSIGNBITSET( dx ) << 2 );
if ( indexPoints[index].y > bounds[1].y ) {
return true;
}
dy = -localFrustum.axis[1].z;
dz = -localFrustum.axis[2].z;
index = ( FLOATSIGNBITSET( dy ) << 1 ) | FLOATSIGNBITSET( dz );
dx = -cornerVecs[index].z;
index |= ( FLOATSIGNBITSET( dx ) << 2 );
if ( indexPoints[index].z < bounds[0].z ) {
return true;
}
dy = localFrustum.axis[1].z;
dz = localFrustum.axis[2].z;
index = ( FLOATSIGNBITSET( dy ) << 1 ) | FLOATSIGNBITSET( dz );
dx = cornerVecs[index].z;
index |= ( FLOATSIGNBITSET( dx ) << 2 );
if ( indexPoints[index].z > bounds[1].z ) {
return true;
}
return false;
}
/*
============
idFrustum::LocalLineIntersection
7 divs
30 muls
============
*/
bool idFrustum::LocalLineIntersection( const idVec3 &start, const idVec3 &end ) const {
idVec3 dir;
float d1, d2, fstart, fend, lstart, lend, f, x;
float leftScale, upScale;
int startInside = 1;
leftScale = dLeft * invFar;
upScale = dUp * invFar;
dir = end - start;
// test near plane
if ( dNear > 0.0f ) {
d1 = dNear - start.x;
startInside &= FLOATSIGNBITSET( d1 );
if ( FLOATNOTZERO( d1 ) ) {
d2 = dNear - end.x;
if ( FLOATSIGNBITSET( d1 ) ^ FLOATSIGNBITSET( d2 ) ) {
f = d1 / ( d1 - d2 );
if ( idMath::Fabs( start.y + f * dir.y ) <= dNear * leftScale ) {
if ( idMath::Fabs( start.z + f * dir.z ) <= dNear * upScale ) {
return true;
}
}
}
}
}
// test far plane
d1 = start.x - dFar;
startInside &= FLOATSIGNBITSET( d1 );
if ( FLOATNOTZERO( d1 ) ) {
d2 = end.x - dFar;
if ( FLOATSIGNBITSET( d1 ) ^ FLOATSIGNBITSET( d2 ) ) {
f = d1 / ( d1 - d2 );
if ( idMath::Fabs( start.y + f * dir.y ) <= dFar * leftScale ) {
if ( idMath::Fabs( start.z + f * dir.z ) <= dFar * upScale ) {
return true;
}
}
}
}
fstart = dFar * start.y;
fend = dFar * end.y;
lstart = dLeft * start.x;
lend = dLeft * end.x;
// test left plane
d1 = fstart - lstart;
startInside &= FLOATSIGNBITSET( d1 );
if ( FLOATNOTZERO( d1 ) ) {
d2 = fend - lend;
if ( FLOATSIGNBITSET( d1 ) ^ FLOATSIGNBITSET( d2 ) ) {
f = d1 / ( d1 - d2 );
x = start.x + f * dir.x;
if ( x >= dNear && x <= dFar ) {
if ( idMath::Fabs( start.z + f * dir.z ) <= x * upScale ) {
return true;
}
}
}
}
// test right plane
d1 = -fstart - lstart;
startInside &= FLOATSIGNBITSET( d1 );
if ( FLOATNOTZERO( d1 ) ) {
d2 = -fend - lend;
if ( FLOATSIGNBITSET( d1 ) ^ FLOATSIGNBITSET( d2 ) ) {
f = d1 / ( d1 - d2 );
x = start.x + f * dir.x;
if ( x >= dNear && x <= dFar ) {
if ( idMath::Fabs( start.z + f * dir.z ) <= x * upScale ) {
return true;
}
}
}
}
fstart = dFar * start.z;
fend = dFar * end.z;
lstart = dUp * start.x;
lend = dUp * end.x;
// test up plane
d1 = fstart - lstart;
startInside &= FLOATSIGNBITSET( d1 );
if ( FLOATNOTZERO( d1 ) ) {
d2 = fend - lend;
if ( FLOATSIGNBITSET( d1 ) ^ FLOATSIGNBITSET( d2 ) ) {
f = d1 / ( d1 - d2 );
x = start.x + f * dir.x;
if ( x >= dNear && x <= dFar ) {
if ( idMath::Fabs( start.y + f * dir.y ) <= x * leftScale ) {
return true;
}
}
}
}
// test down plane
d1 = -fstart - lstart;
startInside &= FLOATSIGNBITSET( d1 );
if ( FLOATNOTZERO( d1 ) ) {
d2 = -fend - lend;
if ( FLOATSIGNBITSET( d1 ) ^ FLOATSIGNBITSET( d2 ) ) {
f = d1 / ( d1 - d2 );
x = start.x + f * dir.x;
if ( x >= dNear && x <= dFar ) {
if ( idMath::Fabs( start.y + f * dir.y ) <= x * leftScale ) {
return true;
}
}
}
}
return ( startInside != 0 );
}
/*
============
idFrustum::LocalRayIntersection
Returns true if the ray starts inside the frustum.
If there was an intersection scale1 <= scale2
============
*/
bool idFrustum::LocalRayIntersection( const idVec3 &start, const idVec3 &dir, float &scale1, float &scale2 ) const {
idVec3 end;
float d1, d2, fstart, fend, lstart, lend, f, x;
float leftScale, upScale;
int startInside = 1;
leftScale = dLeft * invFar;
upScale = dUp * invFar;
end = start + dir;
scale1 = idMath::INFINITY;
scale2 = -idMath::INFINITY;
// test near plane
if ( dNear > 0.0f ) {
d1 = dNear - start.x;
startInside &= FLOATSIGNBITSET( d1 );
d2 = dNear - end.x;
if ( d1 != d2 ) {
f = d1 / ( d1 - d2 );
if ( idMath::Fabs( start.y + f * dir.y ) <= dNear * leftScale ) {
if ( idMath::Fabs( start.z + f * dir.z ) <= dNear * upScale ) {
if ( f < scale1 ) scale1 = f;
if ( f > scale2 ) scale2 = f;
}
}
}
}
// test far plane
d1 = start.x - dFar;
startInside &= FLOATSIGNBITSET( d1 );
d2 = end.x - dFar;
if ( d1 != d2 ) {
f = d1 / ( d1 - d2 );
if ( idMath::Fabs( start.y + f * dir.y ) <= dFar * leftScale ) {
if ( idMath::Fabs( start.z + f * dir.z ) <= dFar * upScale ) {
if ( f < scale1 ) scale1 = f;
if ( f > scale2 ) scale2 = f;
}
}
}
fstart = dFar * start.y;
fend = dFar * end.y;
lstart = dLeft * start.x;
lend = dLeft * end.x;
// test left plane
d1 = fstart - lstart;
startInside &= FLOATSIGNBITSET( d1 );
d2 = fend - lend;
if ( d1 != d2 ) {
f = d1 / ( d1 - d2 );
x = start.x + f * dir.x;
if ( x >= dNear && x <= dFar ) {
if ( idMath::Fabs( start.z + f * dir.z ) <= x * upScale ) {
if ( f < scale1 ) scale1 = f;
if ( f > scale2 ) scale2 = f;
}
}
}
// test right plane
d1 = -fstart - lstart;
startInside &= FLOATSIGNBITSET( d1 );
d2 = -fend - lend;
if ( d1 != d2 ) {
f = d1 / ( d1 - d2 );
x = start.x + f * dir.x;
if ( x >= dNear && x <= dFar ) {
if ( idMath::Fabs( start.z + f * dir.z ) <= x * upScale ) {
if ( f < scale1 ) scale1 = f;
if ( f > scale2 ) scale2 = f;
}
}
}
fstart = dFar * start.z;
fend = dFar * end.z;
lstart = dUp * start.x;
lend = dUp * end.x;
// test up plane
d1 = fstart - lstart;
startInside &= FLOATSIGNBITSET( d1 );
d2 = fend - lend;
if ( d1 != d2 ) {
f = d1 / ( d1 - d2 );
x = start.x + f * dir.x;
if ( x >= dNear && x <= dFar ) {
if ( idMath::Fabs( start.y + f * dir.y ) <= x * leftScale ) {
if ( f < scale1 ) scale1 = f;
if ( f > scale2 ) scale2 = f;
}
}
}
// test down plane
d1 = -fstart - lstart;
startInside &= FLOATSIGNBITSET( d1 );
d2 = -fend - lend;
if ( d1 != d2 ) {
f = d1 / ( d1 - d2 );
x = start.x + f * dir.x;
if ( x >= dNear && x <= dFar ) {
if ( idMath::Fabs( start.y + f * dir.y ) <= x * leftScale ) {
if ( f < scale1 ) scale1 = f;
if ( f > scale2 ) scale2 = f;
}
}
}
return ( startInside != 0 );
}
/*
============
idFrustum::ContainsPoint
============
*/
bool idFrustum::ContainsPoint( const idVec3 &point ) const {
return !CullPoint( point );
}
/*
============
idFrustum::LocalFrustumIntersectsFrustum
============
*/
bool idFrustum::LocalFrustumIntersectsFrustum( const idVec3 points[8], const bool testFirstSide ) const {
int i;
// test if any edges of the other frustum intersect this frustum
for ( i = 0; i < 4; i++ ) {
if ( LocalLineIntersection( points[i], points[4+i] ) ) {
return true;
}
}
if ( testFirstSide ) {
for ( i = 0; i < 4; i++ ) {
if ( LocalLineIntersection( points[i], points[(i+1)&3] ) ) {
return true;
}
}
}
for ( i = 0; i < 4; i++ ) {
if ( LocalLineIntersection( points[4+i], points[4+((i+1)&3)] ) ) {
return true;
}
}
return false;
}
/*
============
idFrustum::LocalFrustumIntersectsBounds
============
*/
bool idFrustum::LocalFrustumIntersectsBounds( const idVec3 points[8], const idBounds &bounds ) const {
int i;
// test if any edges of the other frustum intersect this frustum
for ( i = 0; i < 4; i++ ) {
if ( bounds.LineIntersection( points[i], points[4+i] ) ) {
return true;
}
}
if ( dNear > 0.0f ) {
for ( i = 0; i < 4; i++ ) {
if ( bounds.LineIntersection( points[i], points[(i+1)&3] ) ) {
return true;
}
}
}
for ( i = 0; i < 4; i++ ) {
if ( bounds.LineIntersection( points[4+i], points[4+((i+1)&3)] ) ) {
return true;
}
}
return false;
}
/*
============
idFrustum::IntersectsBounds
============
*/
bool idFrustum::IntersectsBounds( const idBounds &bounds ) const {
idVec3 localOrigin, center, extents;
idMat3 localAxis;
center = ( bounds[0] + bounds[1] ) * 0.5f;
extents = bounds[1] - center;
localOrigin = ( center - origin ) * axis.Transpose();
localAxis = axis.Transpose();
if ( CullLocalBox( localOrigin, extents, localAxis ) ) {
return false;
}
idVec3 indexPoints[8], cornerVecs[4];
ToIndexPointsAndCornerVecs( indexPoints, cornerVecs );
if ( BoundsCullLocalFrustum( bounds, *this, indexPoints, cornerVecs ) ) {
return false;
}
idSwap( indexPoints[2], indexPoints[3] );
idSwap( indexPoints[6], indexPoints[7] );
if ( LocalFrustumIntersectsBounds( indexPoints, bounds ) ) {
return true;
}
BoxToPoints( localOrigin, extents, localAxis, indexPoints );
if ( LocalFrustumIntersectsFrustum( indexPoints, true ) ) {
return true;
}
return false;
}
/*
============
idFrustum::IntersectsBox
============
*/
bool idFrustum::IntersectsBox( const idBox &box ) const {
idVec3 localOrigin;
idMat3 localAxis;
localOrigin = ( box.GetCenter() - origin ) * axis.Transpose();
localAxis = box.GetAxis() * axis.Transpose();
if ( CullLocalBox( localOrigin, box.GetExtents(), localAxis ) ) {
return false;
}
idVec3 indexPoints[8], cornerVecs[4];
idFrustum localFrustum;
localFrustum = *this;
localFrustum.origin = ( origin - box.GetCenter() ) * box.GetAxis().Transpose();
localFrustum.axis = axis * box.GetAxis().Transpose();
localFrustum.ToIndexPointsAndCornerVecs( indexPoints, cornerVecs );
if ( BoundsCullLocalFrustum( idBounds( -box.GetExtents(), box.GetExtents() ), localFrustum, indexPoints, cornerVecs ) ) {
return false;
}
idSwap( indexPoints[2], indexPoints[3] );
idSwap( indexPoints[6], indexPoints[7] );
if ( LocalFrustumIntersectsBounds( indexPoints, idBounds( -box.GetExtents(), box.GetExtents() ) ) ) {
return true;
}
BoxToPoints( localOrigin, box.GetExtents(), localAxis, indexPoints );
if ( LocalFrustumIntersectsFrustum( indexPoints, true ) ) {
return true;
}
return false;
}
/*
============
idFrustum::IntersectsSphere
FIXME: test this
============
*/
#define VORONOI_INDEX( x, y, z ) ( x + y * 3 + z * 9 )
bool idFrustum::IntersectsSphere( const idSphere &sphere ) const {
int index, x, y, z;
float scale, r, d;
idVec3 p, dir, points[8];
if ( CullSphere( sphere ) ) {
return false;
}
x = y = z = 0;
dir.Zero();
p = ( sphere.GetOrigin() - origin ) * axis.Transpose();
if ( p.x <= dNear ) {
scale = dNear * invFar;
dir.y = idMath::Fabs( p.y ) - dLeft * scale;
dir.z = idMath::Fabs( p.z ) - dUp * scale;
}
else if ( p.x >= dFar ) {
dir.y = idMath::Fabs( p.y ) - dLeft;
dir.z = idMath::Fabs( p.z ) - dUp;
}
else {
scale = p.x * invFar;
dir.y = idMath::Fabs( p.y ) - dLeft * scale;
dir.z = idMath::Fabs( p.z ) - dUp * scale;
}
if ( dir.y > 0.0f ) {
y = ( 1 + FLOATSIGNBITNOTSET( p.y ) );
}
if ( dir.z > 0.0f ) {
z = ( 1 + FLOATSIGNBITNOTSET( p.z ) );
}
if ( p.x < dNear ) {
scale = dLeft * dNear * invFar;
if ( p.x < dNear + ( scale - p.y ) * scale * invFar ) {
scale = dUp * dNear * invFar;
if ( p.x < dNear + ( scale - p.z ) * scale * invFar ) {
x = 1;
}
}
}
else {
if ( p.x > dFar ) {
x = 2;
}
else if ( p.x > dFar + ( dLeft - p.y ) * dLeft * invFar ) {
x = 2;
}
else if ( p.x > dFar + ( dUp - p.z ) * dUp * invFar ) {
x = 2;
}
}
r = sphere.GetRadius();
index = VORONOI_INDEX( x, y, z );
switch( index ) {
case VORONOI_INDEX( 0, 0, 0 ): return true;
case VORONOI_INDEX( 1, 0, 0 ): return ( dNear - p.x < r );
case VORONOI_INDEX( 2, 0, 0 ): return ( p.x - dFar < r );
case VORONOI_INDEX( 0, 1, 0 ): d = dFar * p.y - dLeft * p.x; return ( d * d < r * r * ( dFar * dFar + dLeft * dLeft ) );
case VORONOI_INDEX( 0, 2, 0 ): d = -dFar * p.z - dLeft * p.x; return ( d * d < r * r * ( dFar * dFar + dLeft * dLeft ) );
case VORONOI_INDEX( 0, 0, 1 ): d = dFar * p.z - dUp * p.x; return ( d * d < r * r * ( dFar * dFar + dUp * dUp ) );
case VORONOI_INDEX( 0, 0, 2 ): d = -dFar * p.z - dUp * p.x; return ( d * d < r * r * ( dFar * dFar + dUp * dUp ) );
default: {
ToIndexPoints( points );
switch( index ) {
case VORONOI_INDEX( 1, 1, 1 ): return sphere.ContainsPoint( points[0] );
case VORONOI_INDEX( 2, 1, 1 ): return sphere.ContainsPoint( points[4] );
case VORONOI_INDEX( 1, 2, 1 ): return sphere.ContainsPoint( points[1] );
case VORONOI_INDEX( 2, 2, 1 ): return sphere.ContainsPoint( points[5] );
case VORONOI_INDEX( 1, 1, 2 ): return sphere.ContainsPoint( points[2] );
case VORONOI_INDEX( 2, 1, 2 ): return sphere.ContainsPoint( points[6] );
case VORONOI_INDEX( 1, 2, 2 ): return sphere.ContainsPoint( points[3] );
case VORONOI_INDEX( 2, 2, 2 ): return sphere.ContainsPoint( points[7] );
case VORONOI_INDEX( 1, 1, 0 ): return sphere.LineIntersection( points[0], points[2] );
case VORONOI_INDEX( 2, 1, 0 ): return sphere.LineIntersection( points[4], points[6] );
case VORONOI_INDEX( 1, 2, 0 ): return sphere.LineIntersection( points[1], points[3] );
case VORONOI_INDEX( 2, 2, 0 ): return sphere.LineIntersection( points[5], points[7] );
case VORONOI_INDEX( 1, 0, 1 ): return sphere.LineIntersection( points[0], points[1] );
case VORONOI_INDEX( 2, 0, 1 ): return sphere.LineIntersection( points[4], points[5] );
case VORONOI_INDEX( 0, 1, 1 ): return sphere.LineIntersection( points[0], points[4] );
case VORONOI_INDEX( 0, 2, 1 ): return sphere.LineIntersection( points[1], points[5] );
case VORONOI_INDEX( 1, 0, 2 ): return sphere.LineIntersection( points[2], points[3] );
case VORONOI_INDEX( 2, 0, 2 ): return sphere.LineIntersection( points[6], points[7] );
case VORONOI_INDEX( 0, 1, 2 ): return sphere.LineIntersection( points[2], points[6] );
case VORONOI_INDEX( 0, 2, 2 ): return sphere.LineIntersection( points[3], points[7] );
}
break;
}
}
return false;
}
/*
============
idFrustum::IntersectsFrustum
============
*/
bool idFrustum::IntersectsFrustum( const idFrustum &frustum ) const {
idVec3 indexPoints2[8], cornerVecs2[4];
idFrustum localFrustum2;
localFrustum2 = frustum;
localFrustum2.origin = ( frustum.origin - origin ) * axis.Transpose();
localFrustum2.axis = frustum.axis * axis.Transpose();
localFrustum2.ToIndexPointsAndCornerVecs( indexPoints2, cornerVecs2 );
if ( CullLocalFrustum( localFrustum2, indexPoints2, cornerVecs2 ) ) {
return false;
}
idVec3 indexPoints1[8], cornerVecs1[4];
idFrustum localFrustum1;
localFrustum1 = *this;
localFrustum1.origin = ( origin - frustum.origin ) * frustum.axis.Transpose();
localFrustum1.axis = axis * frustum.axis.Transpose();
localFrustum1.ToIndexPointsAndCornerVecs( indexPoints1, cornerVecs1 );
if ( frustum.CullLocalFrustum( localFrustum1, indexPoints1, cornerVecs1 ) ) {
return false;
}
idSwap( indexPoints2[2], indexPoints2[3] );
idSwap( indexPoints2[6], indexPoints2[7] );
if ( LocalFrustumIntersectsFrustum( indexPoints2, ( localFrustum2.dNear > 0.0f ) ) ) {
return true;
}
idSwap( indexPoints1[2], indexPoints1[3] );
idSwap( indexPoints1[6], indexPoints1[7] );
if ( frustum.LocalFrustumIntersectsFrustum( indexPoints1, ( localFrustum1.dNear > 0.0f ) ) ) {
return true;
}
return false;
}
/*
============
idFrustum::IntersectsWinding
============
*/
bool idFrustum::IntersectsWinding( const idWinding &winding ) const {
int i, j, *pointCull;
float min, max;
idVec3 *localPoints, indexPoints[8], cornerVecs[4];
idMat3 transpose;
idPlane plane;
localPoints = (idVec3 *) _alloca16( winding.GetNumPoints() * sizeof( idVec3 ) );
pointCull = (int *) _alloca16( winding.GetNumPoints() * sizeof( int ) );
transpose = axis.Transpose();
for ( i = 0; i < winding.GetNumPoints(); i++ ) {
localPoints[i] = ( winding[i].ToVec3() - origin ) * transpose;
}
// if the winding is culled
if ( CullLocalWinding( localPoints, winding.GetNumPoints(), pointCull ) ) {
return false;
}
winding.GetPlane( plane );
ToIndexPointsAndCornerVecs( indexPoints, cornerVecs );
AxisProjection( indexPoints, cornerVecs, plane.Normal(), min, max );
// if the frustum does not cross the winding plane
if ( min + plane[3] > 0.0f || max + plane[3] < 0.0f ) {
return false;
}
// test if any of the winding edges goes through the frustum
for ( i = 0; i < winding.GetNumPoints(); i++ ) {
j = (i+1)%winding.GetNumPoints();
if ( !( pointCull[i] & pointCull[j] ) ) {
if ( LocalLineIntersection( localPoints[i], localPoints[j] ) ) {
return true;
}
}
}
idSwap( indexPoints[2], indexPoints[3] );
idSwap( indexPoints[6], indexPoints[7] );
// test if any edges of the frustum intersect the winding
for ( i = 0; i < 4; i++ ) {
if ( winding.LineIntersection( plane, indexPoints[i], indexPoints[4+i] ) ) {
return true;
}
}
if ( dNear > 0.0f ) {
for ( i = 0; i < 4; i++ ) {
if ( winding.LineIntersection( plane, indexPoints[i], indexPoints[(i+1)&3] ) ) {
return true;
}
}
}
for ( i = 0; i < 4; i++ ) {
if ( winding.LineIntersection( plane, indexPoints[4+i], indexPoints[4+((i+1)&3)] ) ) {
return true;
}
}
return false;
}
/*
============
idFrustum::LineIntersection
Returns true if the line intersects the box between the start and end point.
============
*/
bool idFrustum::LineIntersection( const idVec3 &start, const idVec3 &end ) const {
return LocalLineIntersection( ( start - origin ) * axis.Transpose(), ( end - origin ) * axis.Transpose() );
}
/*
============
idFrustum::RayIntersection
Returns true if the ray intersects the bounds.
The ray can intersect the bounds in both directions from the start point.
If start is inside the frustum then scale1 < 0 and scale2 > 0.
============
*/
bool idFrustum::RayIntersection( const idVec3 &start, const idVec3 &dir, float &scale1, float &scale2 ) const {
if ( LocalRayIntersection( ( start - origin ) * axis.Transpose(), dir * axis.Transpose(), scale1, scale2 ) ) {
return true;
}
if ( scale1 <= scale2 ) {
return true;
}
return false;
}
/*
============
idFrustum::FromProjection
Creates a frustum which contains the projection of the bounds.
============
*/
bool idFrustum::FromProjection( const idBounds &bounds, const idVec3 &projectionOrigin, const float dFar ) {
return FromProjection( idBox( bounds, vec3_origin, mat3_identity ), projectionOrigin, dFar );
}
/*
============
idFrustum::FromProjection
Creates a frustum which contains the projection of the box.
============
*/
bool idFrustum::FromProjection( const idBox &box, const idVec3 &projectionOrigin, const float dFar ) {
int i, bestAxis;
float value, bestValue;
idVec3 dir;
assert( dFar > 0.0f );
this->dNear = this->dFar = this->invFar = 0.0f;
dir = box.GetCenter() - projectionOrigin;
if ( dir.Normalize() == 0.0f ) {
return false;
}
bestAxis = 0;
bestValue = idMath::Fabs( box.GetAxis()[0] * dir );
for ( i = 1; i < 3; i++ ) {
value = idMath::Fabs( box.GetAxis()[i] * dir );
if ( value * box.GetExtents()[bestAxis] * box.GetExtents()[bestAxis] < bestValue * box.GetExtents()[i] * box.GetExtents()[i] ) {
bestValue = value;
bestAxis = i;
}
}
#if 1
int j, minX, minY, maxY, minZ, maxZ;
idVec3 points[8];
minX = minY = maxY = minZ = maxZ = 0;
for ( j = 0; j < 2; j++ ) {
axis[0] = dir;
axis[1] = box.GetAxis()[bestAxis] - ( box.GetAxis()[bestAxis] * axis[0] ) * axis[0];
axis[1].Normalize();
axis[2].Cross( axis[0], axis[1] );
BoxToPoints( ( box.GetCenter() - projectionOrigin ) * axis.Transpose(), box.GetExtents(), box.GetAxis() * axis.Transpose(), points );
if ( points[0].x <= 1.0f ) {
return false;
}
minX = minY = maxY = minZ = maxZ = 0;
for ( i = 1; i < 8; i++ ) {
if ( points[i].x <= 1.0f ) {
return false;
}
if ( points[i].x < points[minX].x ) {
minX = i;
}
if ( points[minY].x * points[i].y < points[i].x * points[minY].y ) {
minY = i;
} else if ( points[maxY].x * points[i].y > points[i].x * points[maxY].y ) {
maxY = i;
}
if ( points[minZ].x * points[i].z < points[i].x * points[minZ].z ) {
minZ = i;
} else if ( points[maxZ].x * points[i].z > points[i].x * points[maxZ].z ) {
maxZ = i;
}
}
if ( j == 0 ) {
dir += idMath::Tan16( 0.5f * ( idMath::ATan16( points[minY].y, points[minY].x ) + idMath::ATan16( points[maxY].y, points[maxY].x ) ) ) * axis[1];
dir += idMath::Tan16( 0.5f * ( idMath::ATan16( points[minZ].z, points[minZ].x ) + idMath::ATan16( points[maxZ].z, points[maxZ].x ) ) ) * axis[2];
dir.Normalize();
}
}
this->origin = projectionOrigin;
this->dNear = points[minX].x;
this->dFar = dFar;
this->dLeft = Max( idMath::Fabs( points[minY].y / points[minY].x ), idMath::Fabs( points[maxY].y / points[maxY].x ) ) * dFar;
this->dUp = Max( idMath::Fabs( points[minZ].z / points[minZ].x ), idMath::Fabs( points[maxZ].z / points[maxZ].x ) ) * dFar;
this->invFar = 1.0f / dFar;
#elif 1
int j;
float f, x;
idBounds b;
idVec3 points[8];
for ( j = 0; j < 2; j++ ) {
axis[0] = dir;
axis[1] = box.GetAxis()[bestAxis] - ( box.GetAxis()[bestAxis] * axis[0] ) * axis[0];
axis[1].Normalize();
axis[2].Cross( axis[0], axis[1] );
BoxToPoints( ( box.GetCenter() - projectionOrigin ) * axis.Transpose(), box.GetExtents(), box.GetAxis() * axis.Transpose(), points );
b.Clear();
for ( i = 0; i < 8; i++ ) {
x = points[i].x;
if ( x <= 1.0f ) {
return false;
}
f = 1.0f / x;
points[i].y *= f;
points[i].z *= f;
b.AddPoint( points[i] );
}
if ( j == 0 ) {
dir += idMath::Tan16( 0.5f * ( idMath::ATan16( b[1][1] ) + idMath::ATan16( b[0][1] ) ) ) * axis[1];
dir += idMath::Tan16( 0.5f * ( idMath::ATan16( b[1][2] ) + idMath::ATan16( b[0][2] ) ) ) * axis[2];
dir.Normalize();
}
}
this->origin = projectionOrigin;
this->dNear = b[0][0];
this->dFar = dFar;
this->dLeft = Max( idMath::Fabs( b[0][1] ), idMath::Fabs( b[1][1] ) ) * dFar;
this->dUp = Max( idMath::Fabs( b[0][2] ), idMath::Fabs( b[1][2] ) ) * dFar;
this->invFar = 1.0f / dFar;
#else
float dist;
idVec3 org;
axis[0] = dir;
axis[1] = box.GetAxis()[bestAxis] - ( box.GetAxis()[bestAxis] * axis[0] ) * axis[0];
axis[1].Normalize();
axis[2].Cross( axis[0], axis[1] );
for ( i = 0; i < 3; i++ ) {
dist[i] = idMath::Fabs( box.GetExtents()[0] * ( axis[i] * box.GetAxis()[0] ) ) +
idMath::Fabs( box.GetExtents()[1] * ( axis[i] * box.GetAxis()[1] ) ) +
idMath::Fabs( box.GetExtents()[2] * ( axis[i] * box.GetAxis()[2] ) );
}
dist[0] = axis[0] * ( box.GetCenter() - projectionOrigin ) - dist[0];
if ( dist[0] <= 1.0f ) {
return false;
}
float invDist = 1.0f / dist[0];
this->origin = projectionOrigin;
this->dNear = dist[0];
this->dFar = dFar;
this->dLeft = dist[1] * invDist * dFar;
this->dUp = dist[2] * invDist * dFar;
this->invFar = 1.0f / dFar;
#endif
return true;
}
/*
============
idFrustum::FromProjection
Creates a frustum which contains the projection of the sphere.
============
*/
bool idFrustum::FromProjection( const idSphere &sphere, const idVec3 &projectionOrigin, const float dFar ) {
idVec3 dir;
float d, r, s, x, y;
assert( dFar > 0.0f );
dir = sphere.GetOrigin() - projectionOrigin;
d = dir.Normalize();
r = sphere.GetRadius();
if ( d <= r + 1.0f ) {
this->dNear = this->dFar = this->invFar = 0.0f;
return false;
}
origin = projectionOrigin;
axis = dir.ToMat3();
s = idMath::Sqrt( d * d - r * r );
x = r / d * s;
y = idMath::Sqrt( s * s - x * x );
this->dNear = d - r;
this->dFar = dFar;
this->dLeft = x / y * dFar;
this->dUp = dLeft;
this->invFar = 1.0f / dFar;
return true;
}
/*
============
idFrustum::ConstrainToBounds
Returns false if no part of the bounds extends beyond the near plane.
============
*/
bool idFrustum::ConstrainToBounds( const idBounds &bounds ) {
float min, max, newdFar;
bounds.AxisProjection( axis[0], min, max );
newdFar = max - axis[0] * origin;
if ( newdFar <= dNear ) {
MoveFarDistance( dNear + 1.0f );
return false;
}
MoveFarDistance( newdFar );
return true;
}
/*
============
idFrustum::ConstrainToBox
Returns false if no part of the box extends beyond the near plane.
============
*/
bool idFrustum::ConstrainToBox( const idBox &box ) {
float min, max, newdFar;
box.AxisProjection( axis[0], min, max );
newdFar = max - axis[0] * origin;
if ( newdFar <= dNear ) {
MoveFarDistance( dNear + 1.0f );
return false;
}
MoveFarDistance( newdFar );
return true;
}
/*
============
idFrustum::ConstrainToSphere
Returns false if no part of the sphere extends beyond the near plane.
============
*/
bool idFrustum::ConstrainToSphere( const idSphere &sphere ) {
float min, max, newdFar;
sphere.AxisProjection( axis[0], min, max );
newdFar = max - axis[0] * origin;
if ( newdFar <= dNear ) {
MoveFarDistance( dNear + 1.0f );
return false;
}
MoveFarDistance( newdFar );
return true;
}
/*
============
idFrustum::ConstrainToFrustum
Returns false if no part of the frustum extends beyond the near plane.
============
*/
bool idFrustum::ConstrainToFrustum( const idFrustum &frustum ) {
float min, max, newdFar;
frustum.AxisProjection( axis[0], min, max );
newdFar = max - axis[0] * origin;
if ( newdFar <= dNear ) {
MoveFarDistance( dNear + 1.0f );
return false;
}
MoveFarDistance( newdFar );
return true;
}
/*
============
idFrustum::ToPlanes
planes point outwards
============
*/
void idFrustum::ToPlanes( idPlane planes[6] ) const {
int i;
idVec3 scaled[2];
idVec3 points[4];
planes[0].Normal() = -axis[0];
planes[0].SetDist( -dNear );
planes[1].Normal() = axis[0];
planes[1].SetDist( dFar );
scaled[0] = axis[1] * dLeft;
scaled[1] = axis[2] * dUp;
points[0] = scaled[0] + scaled[1];
points[1] = -scaled[0] + scaled[1];
points[2] = -scaled[0] - scaled[1];
points[3] = scaled[0] - scaled[1];
for ( i = 0; i < 4; i++ ) {
planes[i+2].Normal() = points[i].Cross( points[(i+1)&3] - points[i] );
planes[i+2].Normalize();
planes[i+2].FitThroughPoint( points[i] );
}
}
/*
============
idFrustum::ToPoints
============
*/
void idFrustum::ToPoints( idVec3 points[8] ) const {
idMat3 scaled;
scaled[0] = origin + axis[0] * dNear;
scaled[1] = axis[1] * ( dLeft * dNear * invFar );
scaled[2] = axis[2] * ( dUp * dNear * invFar );
points[0] = scaled[0] + scaled[1];
points[1] = scaled[0] - scaled[1];
points[2] = points[1] - scaled[2];
points[3] = points[0] - scaled[2];
points[0] += scaled[2];
points[1] += scaled[2];
scaled[0] = origin + axis[0] * dFar;
scaled[1] = axis[1] * dLeft;
scaled[2] = axis[2] * dUp;
points[4] = scaled[0] + scaled[1];
points[5] = scaled[0] - scaled[1];
points[6] = points[5] - scaled[2];
points[7] = points[4] - scaled[2];
points[4] += scaled[2];
points[5] += scaled[2];
}
/*
============
idFrustum::ToClippedPoints
============
*/
void idFrustum::ToClippedPoints( const float fractions[4], idVec3 points[8] ) const {
idMat3 scaled;
scaled[0] = origin + axis[0] * dNear;
scaled[1] = axis[1] * ( dLeft * dNear * invFar );
scaled[2] = axis[2] * ( dUp * dNear * invFar );
points[0] = scaled[0] + scaled[1];
points[1] = scaled[0] - scaled[1];
points[2] = points[1] - scaled[2];
points[3] = points[0] - scaled[2];
points[0] += scaled[2];
points[1] += scaled[2];
scaled[0] = axis[0] * dFar;
scaled[1] = axis[1] * dLeft;
scaled[2] = axis[2] * dUp;
points[4] = scaled[0] + scaled[1];
points[5] = scaled[0] - scaled[1];
points[6] = points[5] - scaled[2];
points[7] = points[4] - scaled[2];
points[4] += scaled[2];
points[5] += scaled[2];
points[4] = origin + fractions[0] * points[4];
points[5] = origin + fractions[1] * points[5];
points[6] = origin + fractions[2] * points[6];
points[7] = origin + fractions[3] * points[7];
}
/*
============
idFrustum::ToIndexPoints
============
*/
void idFrustum::ToIndexPoints( idVec3 indexPoints[8] ) const {
idMat3 scaled;
scaled[0] = origin + axis[0] * dNear;
scaled[1] = axis[1] * ( dLeft * dNear * invFar );
scaled[2] = axis[2] * ( dUp * dNear * invFar );
indexPoints[0] = scaled[0] - scaled[1];
indexPoints[2] = scaled[0] + scaled[1];
indexPoints[1] = indexPoints[0] + scaled[2];
indexPoints[3] = indexPoints[2] + scaled[2];
indexPoints[0] -= scaled[2];
indexPoints[2] -= scaled[2];
scaled[0] = origin + axis[0] * dFar;
scaled[1] = axis[1] * dLeft;
scaled[2] = axis[2] * dUp;
indexPoints[4] = scaled[0] - scaled[1];
indexPoints[6] = scaled[0] + scaled[1];
indexPoints[5] = indexPoints[4] + scaled[2];
indexPoints[7] = indexPoints[6] + scaled[2];
indexPoints[4] -= scaled[2];
indexPoints[6] -= scaled[2];
}
/*
============
idFrustum::ToIndexPointsAndCornerVecs
22 muls
============
*/
void idFrustum::ToIndexPointsAndCornerVecs( idVec3 indexPoints[8], idVec3 cornerVecs[4] ) const {
idMat3 scaled;
scaled[0] = origin + axis[0] * dNear;
scaled[1] = axis[1] * ( dLeft * dNear * invFar );
scaled[2] = axis[2] * ( dUp * dNear * invFar );
indexPoints[0] = scaled[0] - scaled[1];
indexPoints[2] = scaled[0] + scaled[1];
indexPoints[1] = indexPoints[0] + scaled[2];
indexPoints[3] = indexPoints[2] + scaled[2];
indexPoints[0] -= scaled[2];
indexPoints[2] -= scaled[2];
scaled[0] = axis[0] * dFar;
scaled[1] = axis[1] * dLeft;
scaled[2] = axis[2] * dUp;
cornerVecs[0] = scaled[0] - scaled[1];
cornerVecs[2] = scaled[0] + scaled[1];
cornerVecs[1] = cornerVecs[0] + scaled[2];
cornerVecs[3] = cornerVecs[2] + scaled[2];
cornerVecs[0] -= scaled[2];
cornerVecs[2] -= scaled[2];
indexPoints[4] = cornerVecs[0] + origin;
indexPoints[5] = cornerVecs[1] + origin;
indexPoints[6] = cornerVecs[2] + origin;
indexPoints[7] = cornerVecs[3] + origin;
}
/*
============
idFrustum::AxisProjection
18 muls
============
*/
void idFrustum::AxisProjection( const idVec3 indexPoints[8], const idVec3 cornerVecs[4], const idVec3 &dir, float &min, float &max ) const {
float dx, dy, dz;
int index;
dy = dir.x * axis[1].x + dir.y * axis[1].y + dir.z * axis[1].z;
dz = dir.x * axis[2].x + dir.y * axis[2].y + dir.z * axis[2].z;
index = ( FLOATSIGNBITSET( dy ) << 1 ) | FLOATSIGNBITSET( dz );
dx = dir.x * cornerVecs[index].x + dir.y * cornerVecs[index].y + dir.z * cornerVecs[index].z;
index |= ( FLOATSIGNBITSET( dx ) << 2 );
min = indexPoints[index] * dir;
index = ~index & 3;
dx = -dir.x * cornerVecs[index].x - dir.y * cornerVecs[index].y - dir.z * cornerVecs[index].z;
index |= ( FLOATSIGNBITSET( dx ) << 2 );
max = indexPoints[index] * dir;
}
/*
============
idFrustum::AxisProjection
40 muls
============
*/
void idFrustum::AxisProjection( const idVec3 &dir, float &min, float &max ) const {
idVec3 indexPoints[8], cornerVecs[4];
ToIndexPointsAndCornerVecs( indexPoints, cornerVecs );
AxisProjection( indexPoints, cornerVecs, dir, min, max );
}
/*
============
idFrustum::AxisProjection
76 muls
============
*/
void idFrustum::AxisProjection( const idMat3 &ax, idBounds &bounds ) const {
idVec3 indexPoints[8], cornerVecs[4];
ToIndexPointsAndCornerVecs( indexPoints, cornerVecs );
AxisProjection( indexPoints, cornerVecs, ax[0], bounds[0][0], bounds[1][0] );
AxisProjection( indexPoints, cornerVecs, ax[1], bounds[0][1], bounds[1][1] );
AxisProjection( indexPoints, cornerVecs, ax[2], bounds[0][2], bounds[1][2] );
}
/*
============
idFrustum::AddLocalLineToProjectionBoundsSetCull
============
*/
void idFrustum::AddLocalLineToProjectionBoundsSetCull( const idVec3 &start, const idVec3 &end, int &startCull, int &endCull, idBounds &bounds ) const {
idVec3 dir, p;
float d1, d2, fstart, fend, lstart, lend, f;
float leftScale, upScale;
int cull1, cull2;
#ifdef FRUSTUM_DEBUG
static idCVar r_showInteractionScissors( "r_showInteractionScissors", "0", CVAR_RENDERER | CVAR_INTEGER, "", 0, 2, idCmdSystem::ArgCompletion_Integer<0,2> );
if ( r_showInteractionScissors.GetInteger() > 1 ) {
session->rw->DebugLine( colorGreen, origin + start * axis, origin + end * axis );
}
#endif
leftScale = dLeft * invFar;
upScale = dUp * invFar;
dir = end - start;
fstart = dFar * start.y;
fend = dFar * end.y;
lstart = dLeft * start.x;
lend = dLeft * end.x;
// test left plane
d1 = -fstart + lstart;
d2 = -fend + lend;
cull1 = FLOATSIGNBITSET( d1 );
cull2 = FLOATSIGNBITSET( d2 );
if ( FLOATNOTZERO( d1 ) ) {
if ( FLOATSIGNBITSET( d1 ) ^ FLOATSIGNBITSET( d2 ) ) {
f = d1 / ( d1 - d2 );
p.x = start.x + f * dir.x;
if ( p.x > 0.0f ) {
p.z = start.z + f * dir.z;
if ( idMath::Fabs( p.z ) <= p.x * upScale ) {
p.y = 1.0f;
p.z = p.z * dFar / ( p.x * dUp );
bounds.AddPoint( p );
}
}
}
}
// test right plane
d1 = fstart + lstart;
d2 = fend + lend;
cull1 |= FLOATSIGNBITSET( d1 ) << 1;
cull2 |= FLOATSIGNBITSET( d2 ) << 1;
if ( FLOATNOTZERO( d1 ) ) {
if ( FLOATSIGNBITSET( d1 ) ^ FLOATSIGNBITSET( d2 ) ) {
f = d1 / ( d1 - d2 );
p.x = start.x + f * dir.x;
if ( p.x > 0.0f ) {
p.z = start.z + f * dir.z;
if ( idMath::Fabs( p.z ) <= p.x * upScale ) {
p.y = -1.0f;
p.z = p.z * dFar / ( p.x * dUp );
bounds.AddPoint( p );
}
}
}
}
fstart = dFar * start.z;
fend = dFar * end.z;
lstart = dUp * start.x;
lend = dUp * end.x;
// test up plane
d1 = -fstart + lstart;
d2 = -fend + lend;
cull1 |= FLOATSIGNBITSET( d1 ) << 2;
cull2 |= FLOATSIGNBITSET( d2 ) << 2;
if ( FLOATNOTZERO( d1 ) ) {
if ( FLOATSIGNBITSET( d1 ) ^ FLOATSIGNBITSET( d2 ) ) {
f = d1 / ( d1 - d2 );
p.x = start.x + f * dir.x;
if ( p.x > 0.0f ) {
p.y = start.y + f * dir.y;
if ( idMath::Fabs( p.y ) <= p.x * leftScale ) {
p.y = p.y * dFar / ( p.x * dLeft );
p.z = 1.0f;
bounds.AddPoint( p );
}
}
}
}
// test down plane
d1 = fstart + lstart;
d2 = fend + lend;
cull1 |= FLOATSIGNBITSET( d1 ) << 3;
cull2 |= FLOATSIGNBITSET( d2 ) << 3;
if ( FLOATNOTZERO( d1 ) ) {
if ( FLOATSIGNBITSET( d1 ) ^ FLOATSIGNBITSET( d2 ) ) {
f = d1 / ( d1 - d2 );
p.x = start.x + f * dir.x;
if ( p.x > 0.0f ) {
p.y = start.y + f * dir.y;
if ( idMath::Fabs( p.y ) <= p.x * leftScale ) {
p.y = p.y * dFar / ( p.x * dLeft );
p.z = -1.0f;
bounds.AddPoint( p );
}
}
}
}
if ( cull1 == 0 && start.x > 0.0f ) {
// add start point to projection bounds
p.x = start.x;
p.y = start.y * dFar / ( start.x * dLeft );
p.z = start.z * dFar / ( start.x * dUp );
bounds.AddPoint( p );
}
if ( cull2 == 0 && end.x > 0.0f ) {
// add end point to projection bounds
p.x = end.x;
p.y = end.y * dFar / ( end.x * dLeft );
p.z = end.z * dFar / ( end.x * dUp );
bounds.AddPoint( p );
}
if ( start.x < bounds[0].x ) {
bounds[0].x = start.x < 0.0f ? 0.0f : start.x;
}
if ( end.x < bounds[0].x ) {
bounds[0].x = end.x < 0.0f ? 0.0f : end.x;
}
startCull = cull1;
endCull = cull2;
}
/*
============
idFrustum::AddLocalLineToProjectionBoundsUseCull
============
*/
void idFrustum::AddLocalLineToProjectionBoundsUseCull( const idVec3 &start, const idVec3 &end, int startCull, int endCull, idBounds &bounds ) const {
idVec3 dir, p;
float d1, d2, fstart, fend, lstart, lend, f;
float leftScale, upScale;
int clip;
clip = startCull ^ endCull;
if ( !clip ) {
return;
}
#ifdef FRUSTUM_DEBUG
static idCVar r_showInteractionScissors( "r_showInteractionScissors", "0", CVAR_RENDERER | CVAR_INTEGER, "", 0, 2, idCmdSystem::ArgCompletion_Integer<0,2> );
if ( r_showInteractionScissors.GetInteger() > 1 ) {
session->rw->DebugLine( colorGreen, origin + start * axis, origin + end * axis );
}
#endif
leftScale = dLeft * invFar;
upScale = dUp * invFar;
dir = end - start;
if ( clip & (1|2) ) {
fstart = dFar * start.y;
fend = dFar * end.y;
lstart = dLeft * start.x;
lend = dLeft * end.x;
if ( clip & 1 ) {
// test left plane
d1 = -fstart + lstart;
d2 = -fend + lend;
if ( FLOATNOTZERO( d1 ) ) {
if ( FLOATSIGNBITSET( d1 ) ^ FLOATSIGNBITSET( d2 ) ) {
f = d1 / ( d1 - d2 );
p.x = start.x + f * dir.x;
if ( p.x > 0.0f ) {
p.z = start.z + f * dir.z;
if ( idMath::Fabs( p.z ) <= p.x * upScale ) {
p.y = 1.0f;
p.z = p.z * dFar / ( p.x * dUp );
bounds.AddPoint( p );
}
}
}
}
}
if ( clip & 2 ) {
// test right plane
d1 = fstart + lstart;
d2 = fend + lend;
if ( FLOATNOTZERO( d1 ) ) {
if ( FLOATSIGNBITSET( d1 ) ^ FLOATSIGNBITSET( d2 ) ) {
f = d1 / ( d1 - d2 );
p.x = start.x + f * dir.x;
if ( p.x > 0.0f ) {
p.z = start.z + f * dir.z;
if ( idMath::Fabs( p.z ) <= p.x * upScale ) {
p.y = -1.0f;
p.z = p.z * dFar / ( p.x * dUp );
bounds.AddPoint( p );
}
}
}
}
}
}
if ( clip & (4|8) ) {
fstart = dFar * start.z;
fend = dFar * end.z;
lstart = dUp * start.x;
lend = dUp * end.x;
if ( clip & 4 ) {
// test up plane
d1 = -fstart + lstart;
d2 = -fend + lend;
if ( FLOATNOTZERO( d1 ) ) {
if ( FLOATSIGNBITSET( d1 ) ^ FLOATSIGNBITSET( d2 ) ) {
f = d1 / ( d1 - d2 );
p.x = start.x + f * dir.x;
if ( p.x > 0.0f ) {
p.y = start.y + f * dir.y;
if ( idMath::Fabs( p.y ) <= p.x * leftScale ) {
p.y = p.y * dFar / ( p.x * dLeft );
p.z = 1.0f;
bounds.AddPoint( p );
}
}
}
}
}
if ( clip & 8 ) {
// test down plane
d1 = fstart + lstart;
d2 = fend + lend;
if ( FLOATNOTZERO( d1 ) ) {
if ( FLOATSIGNBITSET( d1 ) ^ FLOATSIGNBITSET( d2 ) ) {
f = d1 / ( d1 - d2 );
p.x = start.x + f * dir.x;
if ( p.x > 0.0f ) {
p.y = start.y + f * dir.y;
if ( idMath::Fabs( p.y ) <= p.x * leftScale ) {
p.y = p.y * dFar / ( p.x * dLeft );
p.z = -1.0f;
bounds.AddPoint( p );
}
}
}
}
}
}
}
/*
============
idFrustum::BoundsRayIntersection
Returns true if the ray starts inside the bounds.
If there was an intersection scale1 <= scale2
============
*/
bool idFrustum::BoundsRayIntersection( const idBounds &bounds, const idVec3 &start, const idVec3 &dir, float &scale1, float &scale2 ) const {
idVec3 end, p;
float d1, d2, f;
int i, startInside = 1;
scale1 = idMath::INFINITY;
scale2 = -idMath::INFINITY;
end = start + dir;
for ( i = 0; i < 2; i++ ) {
d1 = start.x - bounds[i].x;
startInside &= FLOATSIGNBITSET( d1 ) ^ i;
d2 = end.x - bounds[i].x;
if ( d1 != d2 ) {
f = d1 / ( d1 - d2 );
p.y = start.y + f * dir.y;
if ( bounds[0].y <= p.y && p.y <= bounds[1].y ) {
p.z = start.z + f * dir.z;
if ( bounds[0].z <= p.z && p.z <= bounds[1].z ) {
if ( f < scale1 ) scale1 = f;
if ( f > scale2 ) scale2 = f;
}
}
}
d1 = start.y - bounds[i].y;
startInside &= FLOATSIGNBITSET( d1 ) ^ i;
d2 = end.y - bounds[i].y;
if ( d1 != d2 ) {
f = d1 / ( d1 - d2 );
p.x = start.x + f * dir.x;
if ( bounds[0].x <= p.x && p.x <= bounds[1].x ) {
p.z = start.z + f * dir.z;
if ( bounds[0].z <= p.z && p.z <= bounds[1].z ) {
if ( f < scale1 ) scale1 = f;
if ( f > scale2 ) scale2 = f;
}
}
}
d1 = start.z - bounds[i].z;
startInside &= FLOATSIGNBITSET( d1 ) ^ i;
d2 = end.z - bounds[i].z;
if ( d1 != d2 ) {
f = d1 / ( d1 - d2 );
p.x = start.x + f * dir.x;
if ( bounds[0].x <= p.x && p.x <= bounds[1].x ) {
p.y = start.y + f * dir.y;
if ( bounds[0].y <= p.y && p.y <= bounds[1].y ) {
if ( f < scale1 ) scale1 = f;
if ( f > scale2 ) scale2 = f;
}
}
}
}
return ( startInside != 0 );
}
/*
============
idFrustum::ProjectionBounds
============
*/
bool idFrustum::ProjectionBounds( const idBounds &bounds, idBounds &projectionBounds ) const {
return ProjectionBounds( idBox( bounds, vec3_origin, mat3_identity ), projectionBounds );
}
#ifndef __linux__
/*
============
idFrustum::ProjectionBounds
============
*/
bool idFrustum::ProjectionBounds( const idBox &box, idBounds &projectionBounds ) const {
int i, p1, p2, pointCull[8], culled, outside;
float scale1, scale2;
idFrustum localFrustum;
idVec3 points[8], localOrigin;
idMat3 localAxis, localScaled;
idBounds bounds( -box.GetExtents(), box.GetExtents() );
// if the frustum origin is inside the bounds
if ( bounds.ContainsPoint( ( origin - box.GetCenter() ) * box.GetAxis().Transpose() ) ) {
// bounds that cover the whole frustum
float boxMin, boxMax, base;
base = origin * axis[0];
box.AxisProjection( axis[0], boxMin, boxMax );
projectionBounds[0].x = boxMin - base;
projectionBounds[1].x = boxMax - base;
projectionBounds[0].y = projectionBounds[0].z = -1.0f;
projectionBounds[1].y = projectionBounds[1].z = 1.0f;
return true;
}
projectionBounds.Clear();
// transform the bounds into the space of this frustum
localOrigin = ( box.GetCenter() - origin ) * axis.Transpose();
localAxis = box.GetAxis() * axis.Transpose();
BoxToPoints( localOrigin, box.GetExtents(), localAxis, points );
// test outer four edges of the bounds
culled = -1;
outside = 0;
for ( i = 0; i < 4; i++ ) {
p1 = i;
p2 = 4 + i;
AddLocalLineToProjectionBoundsSetCull( points[p1], points[p2], pointCull[p1], pointCull[p2], projectionBounds );
culled &= pointCull[p1] & pointCull[p2];
outside |= pointCull[p1] | pointCull[p2];
}
// if the bounds are completely outside this frustum
if ( culled ) {
return false;
}
// if the bounds are completely inside this frustum
if ( !outside ) {
return true;
}
// test the remaining edges of the bounds
for ( i = 0; i < 4; i++ ) {
p1 = i;
p2 = (i+1)&3;
AddLocalLineToProjectionBoundsUseCull( points[p1], points[p2], pointCull[p1], pointCull[p2], projectionBounds );
}
for ( i = 0; i < 4; i++ ) {
p1 = 4 + i;
p2 = 4 + ((i+1)&3);
AddLocalLineToProjectionBoundsUseCull( points[p1], points[p2], pointCull[p1], pointCull[p2], projectionBounds );
}
// if the bounds extend beyond two or more boundaries of this frustum
if ( outside != 1 && outside != 2 && outside != 4 && outside != 8 ) {
localOrigin = ( origin - box.GetCenter() ) * box.GetAxis().Transpose();
localScaled = axis * box.GetAxis().Transpose();
localScaled[0] *= dFar;
localScaled[1] *= dLeft;
localScaled[2] *= dUp;
// test the outer edges of this frustum for intersection with the bounds
if ( (outside & 2) && (outside & 8) ) {
BoundsRayIntersection( bounds, localOrigin, localScaled[0] - localScaled[1] - localScaled[2], scale1, scale2 );
if ( scale1 <= scale2 && scale1 >= 0.0f ) {
projectionBounds.AddPoint( idVec3( scale1 * dFar, -1.0f, -1.0f ) );
projectionBounds.AddPoint( idVec3( scale2 * dFar, -1.0f, -1.0f ) );
}
}
if ( (outside & 2) && (outside & 4) ) {
BoundsRayIntersection( bounds, localOrigin, localScaled[0] - localScaled[1] + localScaled[2], scale1, scale2 );
if ( scale1 <= scale2 && scale1 >= 0.0f ) {
projectionBounds.AddPoint( idVec3( scale1 * dFar, -1.0f, 1.0f ) );
projectionBounds.AddPoint( idVec3( scale2 * dFar, -1.0f, 1.0f ) );
}
}
if ( (outside & 1) && (outside & 8) ) {
BoundsRayIntersection( bounds, localOrigin, localScaled[0] + localScaled[1] - localScaled[2], scale1, scale2 );
if ( scale1 <= scale2 && scale1 >= 0.0f ) {
projectionBounds.AddPoint( idVec3( scale1 * dFar, 1.0f, -1.0f ) );
projectionBounds.AddPoint( idVec3( scale2 * dFar, 1.0f, -1.0f ) );
}
}
if ( (outside & 1) && (outside & 2) ) {
BoundsRayIntersection( bounds, localOrigin, localScaled[0] + localScaled[1] + localScaled[2], scale1, scale2 );
if ( scale1 <= scale2 && scale1 >= 0.0f ) {
projectionBounds.AddPoint( idVec3( scale1 * dFar, 1.0f, 1.0f ) );
projectionBounds.AddPoint( idVec3( scale2 * dFar, 1.0f, 1.0f ) );
}
}
}
return true;
}
#endif
/*
============
idFrustum::ProjectionBounds
============
*/
bool idFrustum::ProjectionBounds( const idSphere &sphere, idBounds &projectionBounds ) const {
float d, r, rs, sFar;
idVec3 center;
projectionBounds.Clear();
center = ( sphere.GetOrigin() - origin ) * axis.Transpose();
r = sphere.GetRadius();
rs = r * r;
sFar = dFar * dFar;
// test left/right planes
d = dFar * idMath::Fabs( center.y ) - dLeft * center.x;
if ( ( d * d ) > rs * ( sFar + dLeft * dLeft ) ) {
return false;
}
// test up/down planes
d = dFar * idMath::Fabs( center.z ) - dUp * center.x;
if ( ( d * d ) > rs * ( sFar + dUp * dUp ) ) {
return false;
}
// bounds that cover the whole frustum
projectionBounds[0].x = 0.0f;
projectionBounds[1].x = dFar;
projectionBounds[0].y = projectionBounds[0].z = -1.0f;
projectionBounds[1].y = projectionBounds[1].z = 1.0f;
return true;
}
/*
============
idFrustum::ProjectionBounds
============
*/
bool idFrustum::ProjectionBounds( const idFrustum &frustum, idBounds &projectionBounds ) const {
int i, p1, p2, pointCull[8], culled, outside;
float scale1, scale2;
idFrustum localFrustum;
idVec3 points[8], localOrigin;
idMat3 localScaled;
// if the frustum origin is inside the other frustum
if ( frustum.ContainsPoint( origin ) ) {
// bounds that cover the whole frustum
float frustumMin, frustumMax, base;
base = origin * axis[0];
frustum.AxisProjection( axis[0], frustumMin, frustumMax );
projectionBounds[0].x = frustumMin - base;
projectionBounds[1].x = frustumMax - base;
projectionBounds[0].y = projectionBounds[0].z = -1.0f;
projectionBounds[1].y = projectionBounds[1].z = 1.0f;
return true;
}
projectionBounds.Clear();
// transform the given frustum into the space of this frustum
localFrustum = frustum;
localFrustum.origin = ( frustum.origin - origin ) * axis.Transpose();
localFrustum.axis = frustum.axis * axis.Transpose();
localFrustum.ToPoints( points );
// test outer four edges of the other frustum
culled = -1;
outside = 0;
for ( i = 0; i < 4; i++ ) {
p1 = i;
p2 = 4 + i;
AddLocalLineToProjectionBoundsSetCull( points[p1], points[p2], pointCull[p1], pointCull[p2], projectionBounds );
culled &= pointCull[p1] & pointCull[p2];
outside |= pointCull[p1] | pointCull[p2];
}
// if the other frustum is completely outside this frustum
if ( culled ) {
return false;
}
// if the other frustum is completely inside this frustum
if ( !outside ) {
return true;
}
// test the remaining edges of the other frustum
if ( localFrustum.dNear > 0.0f ) {
for ( i = 0; i < 4; i++ ) {
p1 = i;
p2 = (i+1)&3;
AddLocalLineToProjectionBoundsUseCull( points[p1], points[p2], pointCull[p1], pointCull[p2], projectionBounds );
}
}
for ( i = 0; i < 4; i++ ) {
p1 = 4 + i;
p2 = 4 + ((i+1)&3);
AddLocalLineToProjectionBoundsUseCull( points[p1], points[p2], pointCull[p1], pointCull[p2], projectionBounds );
}
// if the other frustum extends beyond two or more boundaries of this frustum
if ( outside != 1 && outside != 2 && outside != 4 && outside != 8 ) {
localOrigin = ( origin - frustum.origin ) * frustum.axis.Transpose();
localScaled = axis * frustum.axis.Transpose();
localScaled[0] *= dFar;
localScaled[1] *= dLeft;
localScaled[2] *= dUp;
// test the outer edges of this frustum for intersection with the other frustum
if ( (outside & 2) && (outside & 8) ) {
frustum.LocalRayIntersection( localOrigin, localScaled[0] - localScaled[1] - localScaled[2], scale1, scale2 );
if ( scale1 <= scale2 && scale1 >= 0.0f ) {
projectionBounds.AddPoint( idVec3( scale1 * dFar, -1.0f, -1.0f ) );
projectionBounds.AddPoint( idVec3( scale2 * dFar, -1.0f, -1.0f ) );
}
}
if ( (outside & 2) && (outside & 4) ) {
frustum.LocalRayIntersection( localOrigin, localScaled[0] - localScaled[1] + localScaled[2], scale1, scale2 );
if ( scale1 <= scale2 && scale1 >= 0.0f ) {
projectionBounds.AddPoint( idVec3( scale1 * dFar, -1.0f, 1.0f ) );
projectionBounds.AddPoint( idVec3( scale2 * dFar, -1.0f, 1.0f ) );
}
}
if ( (outside & 1) && (outside & 8) ) {
frustum.LocalRayIntersection( localOrigin, localScaled[0] + localScaled[1] - localScaled[2], scale1, scale2 );
if ( scale1 <= scale2 && scale1 >= 0.0f ) {
projectionBounds.AddPoint( idVec3( scale1 * dFar, 1.0f, -1.0f ) );
projectionBounds.AddPoint( idVec3( scale2 * dFar, 1.0f, -1.0f ) );
}
}
if ( (outside & 1) && (outside & 2) ) {
frustum.LocalRayIntersection( localOrigin, localScaled[0] + localScaled[1] + localScaled[2], scale1, scale2 );
if ( scale1 <= scale2 && scale1 >= 0.0f ) {
projectionBounds.AddPoint( idVec3( scale1 * dFar, 1.0f, 1.0f ) );
projectionBounds.AddPoint( idVec3( scale2 * dFar, 1.0f, 1.0f ) );
}
}
}
return true;
}
/*
============
idFrustum::ProjectionBounds
============
*/
bool idFrustum::ProjectionBounds( const idWinding &winding, idBounds &projectionBounds ) const {
int i, p1, p2, *pointCull, culled, outside;
float scale;
idVec3 *localPoints;
idMat3 transpose, scaled;
idPlane plane;
projectionBounds.Clear();
// transform the winding points into the space of this frustum
localPoints = (idVec3 *) _alloca16( winding.GetNumPoints() * sizeof( idVec3 ) );
transpose = axis.Transpose();
for ( i = 0; i < winding.GetNumPoints(); i++ ) {
localPoints[i] = ( winding[i].ToVec3() - origin ) * transpose;
}
// test the winding edges
culled = -1;
outside = 0;
pointCull = (int *) _alloca16( winding.GetNumPoints() * sizeof( int ) );
for ( i = 0; i < winding.GetNumPoints(); i += 2 ) {
p1 = i;
p2 = (i+1)%winding.GetNumPoints();
AddLocalLineToProjectionBoundsSetCull( localPoints[p1], localPoints[p2], pointCull[p1], pointCull[p2], projectionBounds );
culled &= pointCull[p1] & pointCull[p2];
outside |= pointCull[p1] | pointCull[p2];
}
// if completely culled
if ( culled ) {
return false;
}
// if completely inside
if ( !outside ) {
return true;
}
// test remaining winding edges
for ( i = 1; i < winding.GetNumPoints(); i += 2 ) {
p1 = i;
p2 = (i+1)%winding.GetNumPoints();
AddLocalLineToProjectionBoundsUseCull( localPoints[p1], localPoints[p2], pointCull[p1], pointCull[p2], projectionBounds );
}
// if the winding extends beyond two or more boundaries of this frustum
if ( outside != 1 && outside != 2 && outside != 4 && outside != 8 ) {
winding.GetPlane( plane );
scaled[0] = axis[0] * dFar;
scaled[1] = axis[1] * dLeft;
scaled[2] = axis[2] * dUp;
// test the outer edges of this frustum for intersection with the winding
if ( (outside & 2) && (outside & 8) ) {
if ( winding.RayIntersection( plane, origin, scaled[0] - scaled[1] - scaled[2], scale ) ) {
projectionBounds.AddPoint( idVec3( scale * dFar, -1.0f, -1.0f ) );
}
}
if ( (outside & 2) && (outside & 4) ) {
if ( winding.RayIntersection( plane, origin, scaled[0] - scaled[1] + scaled[2], scale ) ) {
projectionBounds.AddPoint( idVec3( scale * dFar, -1.0f, 1.0f ) );
}
}
if ( (outside & 1) && (outside & 8) ) {
if ( winding.RayIntersection( plane, origin, scaled[0] + scaled[1] - scaled[2], scale ) ) {
projectionBounds.AddPoint( idVec3( scale * dFar, 1.0f, -1.0f ) );
}
}
if ( (outside & 1) && (outside & 2) ) {
if ( winding.RayIntersection( plane, origin, scaled[0] + scaled[1] + scaled[2], scale ) ) {
projectionBounds.AddPoint( idVec3( scale * dFar, 1.0f, 1.0f ) );
}
}
}
return true;
}
/*
============
idFrustum::ClipFrustumToBox
Clips the frustum far extents to the box.
============
*/
void idFrustum::ClipFrustumToBox( const idBox &box, float clipFractions[4], int clipPlanes[4] ) const {
int i, index;
float f, minf;
idMat3 scaled, localAxis, transpose;
idVec3 localOrigin, cornerVecs[4];
idBounds bounds;
transpose = box.GetAxis();
transpose.TransposeSelf();
localOrigin = ( origin - box.GetCenter() ) * transpose;
localAxis = axis * transpose;
scaled[0] = localAxis[0] * dFar;
scaled[1] = localAxis[1] * dLeft;
scaled[2] = localAxis[2] * dUp;
cornerVecs[0] = scaled[0] + scaled[1];
cornerVecs[1] = scaled[0] - scaled[1];
cornerVecs[2] = cornerVecs[1] - scaled[2];
cornerVecs[3] = cornerVecs[0] - scaled[2];
cornerVecs[0] += scaled[2];
cornerVecs[1] += scaled[2];
bounds[0] = -box.GetExtents();
bounds[1] = box.GetExtents();
minf = ( dNear + 1.0f ) * invFar;
for ( i = 0; i < 4; i++ ) {
index = FLOATSIGNBITNOTSET( cornerVecs[i].x );
f = ( bounds[index].x - localOrigin.x ) / cornerVecs[i].x;
clipFractions[i] = f;
clipPlanes[i] = 1 << index;
index = FLOATSIGNBITNOTSET( cornerVecs[i].y );
f = ( bounds[index].y - localOrigin.y ) / cornerVecs[i].y;
if ( f < clipFractions[i] ) {
clipFractions[i] = f;
clipPlanes[i] = 4 << index;
}
index = FLOATSIGNBITNOTSET( cornerVecs[i].z );
f = ( bounds[index].z - localOrigin.z ) / cornerVecs[i].z;
if ( f < clipFractions[i] ) {
clipFractions[i] = f;
clipPlanes[i] = 16 << index;
}
// make sure the frustum is not clipped between the frustum origin and the near plane
if ( clipFractions[i] < minf ) {
clipFractions[i] = minf;
}
}
}
/*
============
idFrustum::ClipLine
Returns true if part of the line is inside the frustum.
Does not clip to the near and far plane.
============
*/
bool idFrustum::ClipLine( const idVec3 localPoints[8], const idVec3 points[8], int startIndex, int endIndex, idVec3 &start, idVec3 &end, int &startClip, int &endClip ) const {
float d1, d2, fstart, fend, lstart, lend, f, x;
float leftScale, upScale;
float scale1, scale2;
int startCull, endCull;
idVec3 localStart, localEnd, localDir;
leftScale = dLeft * invFar;
upScale = dUp * invFar;
localStart = localPoints[startIndex];
localEnd = localPoints[endIndex];
localDir = localEnd - localStart;
startClip = endClip = -1;
scale1 = idMath::INFINITY;
scale2 = -idMath::INFINITY;
fstart = dFar * localStart.y;
fend = dFar * localEnd.y;
lstart = dLeft * localStart.x;
lend = dLeft * localEnd.x;
// test left plane
d1 = -fstart + lstart;
d2 = -fend + lend;
startCull = FLOATSIGNBITSET( d1 );
endCull = FLOATSIGNBITSET( d2 );
if ( FLOATNOTZERO( d1 ) ) {
if ( FLOATSIGNBITSET( d1 ) ^ FLOATSIGNBITSET( d2 ) ) {
f = d1 / ( d1 - d2 );
x = localStart.x + f * localDir.x;
if ( x >= 0.0f ) {
if ( idMath::Fabs( localStart.z + f * localDir.z ) <= x * upScale ) {
if ( f < scale1 ) { scale1 = f; startClip = 0; }
if ( f > scale2 ) { scale2 = f; endClip = 0; }
}
}
}
}
// test right plane
d1 = fstart + lstart;
d2 = fend + lend;
startCull |= FLOATSIGNBITSET( d1 ) << 1;
endCull |= FLOATSIGNBITSET( d2 ) << 1;
if ( FLOATNOTZERO( d1 ) ) {
if ( FLOATSIGNBITSET( d1 ) ^ FLOATSIGNBITSET( d2 ) ) {
f = d1 / ( d1 - d2 );
x = localStart.x + f * localDir.x;
if ( x >= 0.0f ) {
if ( idMath::Fabs( localStart.z + f * localDir.z ) <= x * upScale ) {
if ( f < scale1 ) { scale1 = f; startClip = 1; }
if ( f > scale2 ) { scale2 = f; endClip = 1; }
}
}
}
}
fstart = dFar * localStart.z;
fend = dFar * localEnd.z;
lstart = dUp * localStart.x;
lend = dUp * localEnd.x;
// test up plane
d1 = -fstart + lstart;
d2 = -fend + lend;
startCull |= FLOATSIGNBITSET( d1 ) << 2;
endCull |= FLOATSIGNBITSET( d2 ) << 2;
if ( FLOATNOTZERO( d1 ) ) {
if ( FLOATSIGNBITSET( d1 ) ^ FLOATSIGNBITSET( d2 ) ) {
f = d1 / ( d1 - d2 );
x = localStart.x + f * localDir.x;
if ( x >= 0.0f ) {
if ( idMath::Fabs( localStart.y + f * localDir.y ) <= x * leftScale ) {
if ( f < scale1 ) { scale1 = f; startClip = 2; }
if ( f > scale2 ) { scale2 = f; endClip = 2; }
}
}
}
}
// test down plane
d1 = fstart + lstart;
d2 = fend + lend;
startCull |= FLOATSIGNBITSET( d1 ) << 3;
endCull |= FLOATSIGNBITSET( d2 ) << 3;
if ( FLOATNOTZERO( d1 ) ) {
if ( FLOATSIGNBITSET( d1 ) ^ FLOATSIGNBITSET( d2 ) ) {
f = d1 / ( d1 - d2 );
x = localStart.x + f * localDir.x;
if ( x >= 0.0f ) {
if ( idMath::Fabs( localStart.y + f * localDir.y ) <= x * leftScale ) {
if ( f < scale1 ) { scale1 = f; startClip = 3; }
if ( f > scale2 ) { scale2 = f; endClip = 3; }
}
}
}
}
// if completely inside
if ( !( startCull | endCull ) ) {
start = points[startIndex];
end = points[endIndex];
return true;
}
else if ( scale1 <= scale2 ) {
if ( !startCull ) {
start = points[startIndex];
startClip = -1;
}
else {
start = points[startIndex] + scale1 * ( points[endIndex] - points[startIndex] );
}
if ( !endCull ) {
end = points[endIndex];
endClip = -1;
}
else {
end = points[startIndex] + scale2 * ( points[endIndex] - points[startIndex] );
}
return true;
}
return false;
}
/*
============
idFrustum::AddLocalCapsToProjectionBounds
============
*/
static int capPointIndex[4][2] = {
{ 0, 3 },
{ 1, 2 },
{ 0, 1 },
{ 2, 3 }
};
ID_INLINE bool idFrustum::AddLocalCapsToProjectionBounds( const idVec3 endPoints[4], const int endPointCull[4], const idVec3 &point, int pointCull, int pointClip, idBounds &projectionBounds ) const {
int *p;
if ( pointClip < 0 ) {
return false;
}
p = capPointIndex[pointClip];
AddLocalLineToProjectionBoundsUseCull( endPoints[p[0]], point, endPointCull[p[0]], pointCull, projectionBounds );
AddLocalLineToProjectionBoundsUseCull( endPoints[p[1]], point, endPointCull[p[1]], pointCull, projectionBounds );
return true;
}
/*
============
idFrustum::ClippedProjectionBounds
============
*/
bool idFrustum::ClippedProjectionBounds( const idFrustum &frustum, const idBox &clipBox, idBounds &projectionBounds ) const {
int i, p1, p2, clipPointCull[8], clipPlanes[4], usedClipPlanes, nearCull, farCull, outside;
int pointCull[2], startClip, endClip, boxPointCull[8];
float clipFractions[4], s1, s2, t1, t2, leftScale, upScale;
idFrustum localFrustum;
idVec3 clipPoints[8], localPoints1[8], localPoints2[8], localOrigin1, localOrigin2, start, end;
idMat3 localAxis1, localAxis2, transpose;
idBounds clipBounds;
// if the frustum origin is inside the other frustum
if ( frustum.ContainsPoint( origin ) ) {
// bounds that cover the whole frustum
float clipBoxMin, clipBoxMax, frustumMin, frustumMax, base;
base = origin * axis[0];
clipBox.AxisProjection( axis[0], clipBoxMin, clipBoxMax );
frustum.AxisProjection( axis[0], frustumMin, frustumMax );
projectionBounds[0].x = Max( clipBoxMin, frustumMin ) - base;
projectionBounds[1].x = Min( clipBoxMax, frustumMax ) - base;
projectionBounds[0].y = projectionBounds[0].z = -1.0f;
projectionBounds[1].y = projectionBounds[1].z = 1.0f;
return true;
}
projectionBounds.Clear();
// clip the outer edges of the given frustum to the clip bounds
frustum.ClipFrustumToBox( clipBox, clipFractions, clipPlanes );
usedClipPlanes = clipPlanes[0] | clipPlanes[1] | clipPlanes[2] | clipPlanes[3];
// transform the clipped frustum to the space of this frustum
transpose = axis;
transpose.TransposeSelf();
localFrustum = frustum;
localFrustum.origin = ( frustum.origin - origin ) * transpose;
localFrustum.axis = frustum.axis * transpose;
localFrustum.ToClippedPoints( clipFractions, clipPoints );
// test outer four edges of the clipped frustum
for ( i = 0; i < 4; i++ ) {
p1 = i;
p2 = 4 + i;
AddLocalLineToProjectionBoundsSetCull( clipPoints[p1], clipPoints[p2], clipPointCull[p1], clipPointCull[p2], projectionBounds );
}
// get cull bits for the clipped frustum
outside = clipPointCull[0] | clipPointCull[1] | clipPointCull[2] | clipPointCull[3] |
clipPointCull[4] | clipPointCull[5] | clipPointCull[6] | clipPointCull[7];
nearCull = clipPointCull[0] & clipPointCull[1] & clipPointCull[2] & clipPointCull[3];
farCull = clipPointCull[4] & clipPointCull[5] & clipPointCull[6] & clipPointCull[7];
// if the clipped frustum is not completely inside this frustum
if ( outside ) {
// test the remaining edges of the clipped frustum
if ( !nearCull && localFrustum.dNear > 0.0f ) {
for ( i = 0; i < 4; i++ ) {
p1 = i;
p2 = (i+1)&3;
AddLocalLineToProjectionBoundsUseCull( clipPoints[p1], clipPoints[p2], clipPointCull[p1], clipPointCull[p2], projectionBounds );
}
}
if ( !farCull ) {
for ( i = 0; i < 4; i++ ) {
p1 = 4 + i;
p2 = 4 + ((i+1)&3);
AddLocalLineToProjectionBoundsUseCull( clipPoints[p1], clipPoints[p2], clipPointCull[p1], clipPointCull[p2], projectionBounds );
}
}
}
// if the clipped frustum far end points are inside this frustum
if ( !( farCull && !( nearCull & farCull ) ) &&
// if the clipped frustum is not clipped to a single plane of the clip bounds
( clipPlanes[0] != clipPlanes[1] || clipPlanes[1] != clipPlanes[2] || clipPlanes[2] != clipPlanes[3] ) ) {
// transform the clip box into the space of the other frustum
transpose = frustum.axis;
transpose.TransposeSelf();
localOrigin1 = ( clipBox.GetCenter() - frustum.origin ) * transpose;
localAxis1 = clipBox.GetAxis() * transpose;
BoxToPoints( localOrigin1, clipBox.GetExtents(), localAxis1, localPoints1 );
// cull the box corners with the other frustum
leftScale = frustum.dLeft * frustum.invFar;
upScale = frustum.dUp * frustum.invFar;
for ( i = 0; i < 8; i++ ) {
idVec3 &p = localPoints1[i];
if ( !( boxVertPlanes[i] & usedClipPlanes ) || p.x <= 0.0f ) {
boxPointCull[i] = 1|2|4|8;
}
else {
boxPointCull[i] = 0;
if ( idMath::Fabs( p.y ) > p.x * leftScale ) {
boxPointCull[i] |= 1 << FLOATSIGNBITSET( p.y );
}
if ( idMath::Fabs( p.z ) > p.x * upScale ) {
boxPointCull[i] |= 4 << FLOATSIGNBITSET( p.z );
}
}
}
// transform the clip box into the space of this frustum
transpose = axis;
transpose.TransposeSelf();
localOrigin2 = ( clipBox.GetCenter() - origin ) * transpose;
localAxis2 = clipBox.GetAxis() * transpose;
BoxToPoints( localOrigin2, clipBox.GetExtents(), localAxis2, localPoints2 );
// clip the edges of the clip bounds to the other frustum and add the clipped edges to the projection bounds
for ( i = 0; i < 4; i++ ) {
p1 = i;
p2 = 4 + i;
if ( !( boxPointCull[p1] & boxPointCull[p2] ) ) {
if ( frustum.ClipLine( localPoints1, localPoints2, p1, p2, start, end, startClip, endClip ) ) {
AddLocalLineToProjectionBoundsSetCull( start, end, pointCull[0], pointCull[1], projectionBounds );
AddLocalCapsToProjectionBounds( clipPoints+4, clipPointCull+4, start, pointCull[0], startClip, projectionBounds );
AddLocalCapsToProjectionBounds( clipPoints+4, clipPointCull+4, end, pointCull[1], endClip, projectionBounds );
outside |= pointCull[0] | pointCull[1];
}
}
}
for ( i = 0; i < 4; i++ ) {
p1 = i;
p2 = (i+1)&3;
if ( !( boxPointCull[p1] & boxPointCull[p2] ) ) {
if ( frustum.ClipLine( localPoints1, localPoints2, p1, p2, start, end, startClip, endClip ) ) {
AddLocalLineToProjectionBoundsSetCull( start, end, pointCull[0], pointCull[1], projectionBounds );
AddLocalCapsToProjectionBounds( clipPoints+4, clipPointCull+4, start, pointCull[0], startClip, projectionBounds );
AddLocalCapsToProjectionBounds( clipPoints+4, clipPointCull+4, end, pointCull[1], endClip, projectionBounds );
outside |= pointCull[0] | pointCull[1];
}
}
}
for ( i = 0; i < 4; i++ ) {
p1 = 4 + i;
p2 = 4 + ((i+1)&3);
if ( !( boxPointCull[p1] & boxPointCull[p2] ) ) {
if ( frustum.ClipLine( localPoints1, localPoints2, p1, p2, start, end, startClip, endClip ) ) {
AddLocalLineToProjectionBoundsSetCull( start, end, pointCull[0], pointCull[1], projectionBounds );
AddLocalCapsToProjectionBounds( clipPoints+4, clipPointCull+4, start, pointCull[0], startClip, projectionBounds );
AddLocalCapsToProjectionBounds( clipPoints+4, clipPointCull+4, end, pointCull[1], endClip, projectionBounds );
outside |= pointCull[0] | pointCull[1];
}
}
}
}
// if the clipped frustum extends beyond two or more boundaries of this frustum
if ( outside != 1 && outside != 2 && outside != 4 && outside != 8 ) {
// transform this frustum into the space of the other frustum
transpose = frustum.axis;
transpose.TransposeSelf();
localOrigin1 = ( origin - frustum.origin ) * transpose;
localAxis1 = axis * transpose;
localAxis1[0] *= dFar;
localAxis1[1] *= dLeft;
localAxis1[2] *= dUp;
// transform this frustum into the space of the clip bounds
transpose = clipBox.GetAxis();
transpose.TransposeSelf();
localOrigin2 = ( origin - clipBox.GetCenter() ) * transpose;
localAxis2 = axis * transpose;
localAxis2[0] *= dFar;
localAxis2[1] *= dLeft;
localAxis2[2] *= dUp;
clipBounds[0] = -clipBox.GetExtents();
clipBounds[1] = clipBox.GetExtents();
// test the outer edges of this frustum for intersection with both the other frustum and the clip bounds
if ( (outside & 2) && (outside & 8) ) {
frustum.LocalRayIntersection( localOrigin1, localAxis1[0] - localAxis1[1] - localAxis1[2], s1, s2 );
if ( s1 <= s2 && s1 >= 0.0f ) {
BoundsRayIntersection( clipBounds, localOrigin2, localAxis2[0] - localAxis2[1] - localAxis2[2], t1, t2 );
if ( t1 <= t2 && t2 > s1 && t1 < s2 ) {
projectionBounds.AddPoint( idVec3( s1 * dFar, -1.0f, -1.0f ) );
projectionBounds.AddPoint( idVec3( s2 * dFar, -1.0f, -1.0f ) );
}
}
}
if ( (outside & 2) && (outside & 4) ) {
frustum.LocalRayIntersection( localOrigin1, localAxis1[0] - localAxis1[1] + localAxis1[2], s1, s2 );
if ( s1 <= s2 && s1 >= 0.0f ) {
BoundsRayIntersection( clipBounds, localOrigin2, localAxis2[0] - localAxis2[1] + localAxis2[2], t1, t2 );
if ( t1 <= t2 && t2 > s1 && t1 < s2 ) {
projectionBounds.AddPoint( idVec3( s1 * dFar, -1.0f, 1.0f ) );
projectionBounds.AddPoint( idVec3( s2 * dFar, -1.0f, 1.0f ) );
}
}
}
if ( (outside & 1) && (outside & 8) ) {
frustum.LocalRayIntersection( localOrigin1, localAxis1[0] + localAxis1[1] - localAxis1[2], s1, s2 );
if ( s1 <= s2 && s1 >= 0.0f ) {
BoundsRayIntersection( clipBounds, localOrigin2, localAxis2[0] + localAxis2[1] - localAxis2[2], t1, t2 );
if ( t1 <= t2 && t2 > s1 && t1 < s2 ) {
projectionBounds.AddPoint( idVec3( s1 * dFar, 1.0f, -1.0f ) );
projectionBounds.AddPoint( idVec3( s2 * dFar, 1.0f, -1.0f ) );
}
}
}
if ( (outside & 1) && (outside & 2) ) {
frustum.LocalRayIntersection( localOrigin1, localAxis1[0] + localAxis1[1] + localAxis1[2], s1, s2 );
if ( s1 <= s2 && s1 >= 0.0f ) {
BoundsRayIntersection( clipBounds, localOrigin2, localAxis2[0] + localAxis2[1] + localAxis2[2], t1, t2 );
if ( t1 <= t2 && t2 > s1 && t1 < s2 ) {
projectionBounds.AddPoint( idVec3( s1 * dFar, 1.0f, 1.0f ) );
projectionBounds.AddPoint( idVec3( s2 * dFar, 1.0f, 1.0f ) );
}
}
}
}
return true;
}