mirror of
https://github.com/dhewm/dhewm3.git
synced 2024-11-24 05:11:21 +00:00
2844 lines
78 KiB
C++
2844 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 ¢er, 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;
|
||
|
}
|