dhewm3-sdk/idlib/bv/Frustum.cpp
dhewg afebd7e1e5 Untangle the epic precompiled.h mess
Don't include the lazy precompiled.h everywhere, only what's
required for the compilation unit.
platform.h needs to be included instead to provide all essential
defines and types.
All includes use the relative path to the neo or the game
specific root.
Move all idlib related includes from idlib/Lib.h to precompiled.h.
precompiled.h still exists for the MFC stuff in tools/.
Add some missing header guards.
2018-08-20 01:46:28 +02:00

2841 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 "sys/platform.h"
#include "idlib/containers/List.h"
#include "idlib/bv/Frustum.h"
//#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 );
}
/*
============
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;
}
/*
============
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;
}