2 ===========================================================================
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
22 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.
24 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.
26 ===========================================================================
29 #include "../precompiled.h"
32 //#define FRUSTUM_DEBUG
42 static int boxVertPlanes[8] = {
43 ( (1<<0) | (1<<2) | (1<<4) ),
44 ( (1<<1) | (1<<2) | (1<<4) ),
45 ( (1<<1) | (1<<3) | (1<<4) ),
46 ( (1<<0) | (1<<3) | (1<<4) ),
47 ( (1<<0) | (1<<2) | (1<<5) ),
48 ( (1<<1) | (1<<2) | (1<<5) ),
49 ( (1<<1) | (1<<3) | (1<<5) ),
50 ( (1<<0) | (1<<3) | (1<<5) ),
58 void BoxToPoints( const idVec3 ¢er, const idVec3 &extents, const idMat3 &axis, idVec3 points[8] ) {
62 ax[0] = extents[0] * axis[0];
63 ax[1] = extents[1] * axis[1];
64 ax[2] = extents[2] * axis[2];
65 temp[0] = center - ax[0];
66 temp[1] = center + ax[0];
67 temp[2] = ax[1] - ax[2];
68 temp[3] = ax[1] + ax[2];
69 points[0] = temp[0] - temp[3];
70 points[1] = temp[1] - temp[3];
71 points[2] = temp[1] + temp[2];
72 points[3] = temp[0] + temp[2];
73 points[4] = temp[0] - temp[2];
74 points[5] = temp[1] - temp[2];
75 points[6] = temp[1] + temp[3];
76 points[7] = temp[0] + temp[3];
81 idFrustum::PlaneDistance
84 float idFrustum::PlaneDistance( const idPlane &plane ) const {
87 AxisProjection( plane.Normal(), min, max );
88 if ( min + plane[3] > 0.0f ) {
89 return min + plane[3];
91 if ( max + plane[3] < 0.0f ) {
92 return max + plane[3];
102 int idFrustum::PlaneSide( const idPlane &plane, const float epsilon ) const {
105 AxisProjection( plane.Normal(), min, max );
106 if ( min + plane[3] > epsilon ) {
107 return PLANESIDE_FRONT;
109 if ( max + plane[3] < epsilon ) {
110 return PLANESIDE_BACK;
112 return PLANESIDE_CROSS;
120 bool idFrustum::CullPoint( const idVec3 &point ) const {
124 // transform point to frustum space
125 p = ( point - origin ) * axis.Transpose();
126 // test whether or not the point is within the frustum
127 if ( p.x < dNear || p.x > dFar ) {
130 scale = p.x * invFar;
131 if ( idMath::Fabs( p.y ) > dLeft * scale ) {
134 if ( idMath::Fabs( p.z ) > dUp * scale ) {
142 idFrustum::CullLocalBox
144 Tests if any of the planes of the frustum can be used as a separating plane.
150 bool idFrustum::CullLocalBox( const idVec3 &localOrigin, const idVec3 &extents, const idMat3 &localAxis ) const {
156 d1 = dNear - localOrigin.x;
157 d2 = idMath::Fabs( extents[0] * localAxis[0][0] ) +
158 idMath::Fabs( extents[1] * localAxis[1][0] ) +
159 idMath::Fabs( extents[2] * localAxis[2][0] );
160 if ( d1 - d2 > 0.0f ) {
165 d1 = localOrigin.x - dFar;
166 if ( d1 - d2 > 0.0f ) {
170 testOrigin = localOrigin;
171 testAxis = localAxis;
173 if ( testOrigin.y < 0.0f ) {
174 testOrigin.y = -testOrigin.y;
175 testAxis[0][1] = -testAxis[0][1];
176 testAxis[1][1] = -testAxis[1][1];
177 testAxis[2][1] = -testAxis[2][1];
180 // test left/right planes
181 d1 = dFar * testOrigin.y - dLeft * testOrigin.x;
182 d2 = idMath::Fabs( extents[0] * ( dFar * testAxis[0][1] - dLeft * testAxis[0][0] ) ) +
183 idMath::Fabs( extents[1] * ( dFar * testAxis[1][1] - dLeft * testAxis[1][0] ) ) +
184 idMath::Fabs( extents[2] * ( dFar * testAxis[2][1] - dLeft * testAxis[2][0] ) );
185 if ( d1 - d2 > 0.0f ) {
189 if ( testOrigin.z < 0.0f ) {
190 testOrigin.z = -testOrigin.z;
191 testAxis[0][2] = -testAxis[0][2];
192 testAxis[1][2] = -testAxis[1][2];
193 testAxis[2][2] = -testAxis[2][2];
196 // test up/down planes
197 d1 = dFar * testOrigin.z - dUp * testOrigin.x;
198 d2 = idMath::Fabs( extents[0] * ( dFar * testAxis[0][2] - dUp * testAxis[0][0] ) ) +
199 idMath::Fabs( extents[1] * ( dFar * testAxis[1][2] - dUp * testAxis[1][0] ) ) +
200 idMath::Fabs( extents[2] * ( dFar * testAxis[2][2] - dUp * testAxis[2][0] ) );
201 if ( d1 - d2 > 0.0f ) {
210 idFrustum::CullBounds
212 Tests if any of the planes of the frustum can be used as a separating plane.
218 bool idFrustum::CullBounds( const idBounds &bounds ) const {
219 idVec3 localOrigin, center, extents;
222 center = ( bounds[0] + bounds[1] ) * 0.5f;
223 extents = bounds[1] - center;
225 // transform the bounds into the space of this frustum
226 localOrigin = ( center - origin ) * axis.Transpose();
227 localAxis = axis.Transpose();
229 return CullLocalBox( localOrigin, extents, localAxis );
234 idFrustum::CullBounds
236 Tests if any of the planes of the frustum can be used as a separating plane.
242 bool idFrustum::CullBox( const idBox &box ) const {
246 // transform the box into the space of this frustum
247 localOrigin = ( box.GetCenter() - origin ) * axis.Transpose();
248 localAxis = box.GetAxis() * axis.Transpose();
250 return CullLocalBox( localOrigin, box.GetExtents(), localAxis );
255 idFrustum::CullSphere
257 Tests if any of the planes of the frustum can be used as a separating plane.
263 bool idFrustum::CullSphere( const idSphere &sphere ) const {
264 float d, r, rs, sFar;
267 center = ( sphere.GetOrigin() - origin ) * axis.Transpose();
268 r = sphere.GetRadius();
271 if ( dNear - center.x > r ) {
276 if ( center.x - dFar > r ) {
283 // test left/right planes
284 d = dFar * idMath::Fabs( center.y ) - dLeft * center.x;
285 if ( ( d * d ) > rs * ( sFar + dLeft * dLeft ) ) {
289 // test up/down planes
290 d = dFar * idMath::Fabs( center.z ) - dUp * center.x;
291 if ( ( d * d ) > rs * ( sFar + dUp * dUp ) ) {
300 idFrustum::CullLocalFrustum
302 Tests if any of the planes of this frustum can be used as a separating plane.
308 bool idFrustum::CullLocalFrustum( const idFrustum &localFrustum, const idVec3 indexPoints[8], const idVec3 cornerVecs[4] ) const {
310 float dx, dy, dz, leftScale, upScale;
313 dy = -localFrustum.axis[1].x;
314 dz = -localFrustum.axis[2].x;
315 index = ( FLOATSIGNBITSET( dy ) << 1 ) | FLOATSIGNBITSET( dz );
316 dx = -cornerVecs[index].x;
317 index |= ( FLOATSIGNBITSET( dx ) << 2 );
319 if ( indexPoints[index].x < dNear ) {
324 dy = localFrustum.axis[1].x;
325 dz = localFrustum.axis[2].x;
326 index = ( FLOATSIGNBITSET( dy ) << 1 ) | FLOATSIGNBITSET( dz );
327 dx = cornerVecs[index].x;
328 index |= ( FLOATSIGNBITSET( dx ) << 2 );
330 if ( indexPoints[index].x > dFar ) {
334 leftScale = dLeft * invFar;
337 dy = dFar * localFrustum.axis[1].y - dLeft * localFrustum.axis[1].x;
338 dz = dFar * localFrustum.axis[2].y - dLeft * localFrustum.axis[2].x;
339 index = ( FLOATSIGNBITSET( dy ) << 1 ) | FLOATSIGNBITSET( dz );
340 dx = dFar * cornerVecs[index].y - dLeft * cornerVecs[index].x;
341 index |= ( FLOATSIGNBITSET( dx ) << 2 );
343 if ( indexPoints[index].y > indexPoints[index].x * leftScale ) {
348 dy = -dFar * localFrustum.axis[1].y - dLeft * localFrustum.axis[1].x;
349 dz = -dFar * localFrustum.axis[2].y - dLeft * localFrustum.axis[2].x;
350 index = ( FLOATSIGNBITSET( dy ) << 1 ) | FLOATSIGNBITSET( dz );
351 dx = -dFar * cornerVecs[index].y - dLeft * cornerVecs[index].x;
352 index |= ( FLOATSIGNBITSET( dx ) << 2 );
354 if ( indexPoints[index].y < -indexPoints[index].x * leftScale ) {
358 upScale = dUp * invFar;
361 dy = dFar * localFrustum.axis[1].z - dUp * localFrustum.axis[1].x;
362 dz = dFar * localFrustum.axis[2].z - dUp * localFrustum.axis[2].x;
363 index = ( FLOATSIGNBITSET( dy ) << 1 ) | FLOATSIGNBITSET( dz );
364 dx = dFar * cornerVecs[index].z - dUp * cornerVecs[index].x;
365 index |= ( FLOATSIGNBITSET( dx ) << 2 );
367 if ( indexPoints[index].z > indexPoints[index].x * upScale ) {
372 dy = -dFar * localFrustum.axis[1].z - dUp * localFrustum.axis[1].x;
373 dz = -dFar * localFrustum.axis[2].z - dUp * localFrustum.axis[2].x;
374 index = ( FLOATSIGNBITSET( dy ) << 1 ) | FLOATSIGNBITSET( dz );
375 dx = -dFar * cornerVecs[index].z - dUp * cornerVecs[index].x;
376 index |= ( FLOATSIGNBITSET( dx ) << 2 );
378 if ( indexPoints[index].z < -indexPoints[index].x * upScale ) {
387 idFrustum::CullFrustum
389 Tests if any of the planes of this frustum can be used as a separating plane.
395 bool idFrustum::CullFrustum( const idFrustum &frustum ) const {
396 idFrustum localFrustum;
397 idVec3 indexPoints[8], cornerVecs[4];
399 // transform the given frustum into the space of this frustum
400 localFrustum = frustum;
401 localFrustum.origin = ( frustum.origin - origin ) * axis.Transpose();
402 localFrustum.axis = frustum.axis * axis.Transpose();
404 localFrustum.ToIndexPointsAndCornerVecs( indexPoints, cornerVecs );
406 return CullLocalFrustum( localFrustum, indexPoints, cornerVecs );
411 idFrustum::CullLocalWinding
414 bool idFrustum::CullLocalWinding( const idVec3 *points, const int numPoints, int *pointCull ) const {
415 int i, pCull, culled;
416 float leftScale, upScale;
418 leftScale = dLeft * invFar;
419 upScale = dUp * invFar;
422 for ( i = 0; i < numPoints; i++ ) {
423 const idVec3 &p = points[i];
428 else if ( p.x > dFar ) {
431 if ( idMath::Fabs( p.y ) > p.x * leftScale ) {
432 pCull |= 4 << FLOATSIGNBITSET( p.y );
434 if ( idMath::Fabs( p.z ) > p.x * upScale ) {
435 pCull |= 16 << FLOATSIGNBITSET( p.z );
438 pointCull[i] = pCull;
441 return ( culled != 0 );
446 idFrustum::CullWinding
449 bool idFrustum::CullWinding( const idWinding &winding ) const {
454 localPoints = (idVec3 *) _alloca16( winding.GetNumPoints() * sizeof( idVec3 ) );
455 pointCull = (int *) _alloca16( winding.GetNumPoints() * sizeof( int ) );
457 transpose = axis.Transpose();
458 for ( i = 0; i < winding.GetNumPoints(); i++ ) {
459 localPoints[i] = ( winding[i].ToVec3() - origin ) * transpose;
462 return CullLocalWinding( localPoints, winding.GetNumPoints(), pointCull );
467 idFrustum::BoundsCullLocalFrustum
469 Tests if any of the bounding box planes can be used as a separating plane.
472 bool idFrustum::BoundsCullLocalFrustum( const idBounds &bounds, const idFrustum &localFrustum, const idVec3 indexPoints[8], const idVec3 cornerVecs[4] ) const {
476 dy = -localFrustum.axis[1].x;
477 dz = -localFrustum.axis[2].x;
478 index = ( FLOATSIGNBITSET( dy ) << 1 ) | FLOATSIGNBITSET( dz );
479 dx = -cornerVecs[index].x;
480 index |= ( FLOATSIGNBITSET( dx ) << 2 );
482 if ( indexPoints[index].x < bounds[0].x ) {
486 dy = localFrustum.axis[1].x;
487 dz = localFrustum.axis[2].x;
488 index = ( FLOATSIGNBITSET( dy ) << 1 ) | FLOATSIGNBITSET( dz );
489 dx = cornerVecs[index].x;
490 index |= ( FLOATSIGNBITSET( dx ) << 2 );
492 if ( indexPoints[index].x > bounds[1].x ) {
496 dy = -localFrustum.axis[1].y;
497 dz = -localFrustum.axis[2].y;
498 index = ( FLOATSIGNBITSET( dy ) << 1 ) | FLOATSIGNBITSET( dz );
499 dx = -cornerVecs[index].y;
500 index |= ( FLOATSIGNBITSET( dx ) << 2 );
502 if ( indexPoints[index].y < bounds[0].y ) {
506 dy = localFrustum.axis[1].y;
507 dz = localFrustum.axis[2].y;
508 index = ( FLOATSIGNBITSET( dy ) << 1 ) | FLOATSIGNBITSET( dz );
509 dx = cornerVecs[index].y;
510 index |= ( FLOATSIGNBITSET( dx ) << 2 );
512 if ( indexPoints[index].y > bounds[1].y ) {
516 dy = -localFrustum.axis[1].z;
517 dz = -localFrustum.axis[2].z;
518 index = ( FLOATSIGNBITSET( dy ) << 1 ) | FLOATSIGNBITSET( dz );
519 dx = -cornerVecs[index].z;
520 index |= ( FLOATSIGNBITSET( dx ) << 2 );
522 if ( indexPoints[index].z < bounds[0].z ) {
526 dy = localFrustum.axis[1].z;
527 dz = localFrustum.axis[2].z;
528 index = ( FLOATSIGNBITSET( dy ) << 1 ) | FLOATSIGNBITSET( dz );
529 dx = cornerVecs[index].z;
530 index |= ( FLOATSIGNBITSET( dx ) << 2 );
532 if ( indexPoints[index].z > bounds[1].z ) {
541 idFrustum::LocalLineIntersection
547 bool idFrustum::LocalLineIntersection( const idVec3 &start, const idVec3 &end ) const {
549 float d1, d2, fstart, fend, lstart, lend, f, x;
550 float leftScale, upScale;
553 leftScale = dLeft * invFar;
554 upScale = dUp * invFar;
558 if ( dNear > 0.0f ) {
559 d1 = dNear - start.x;
560 startInside &= FLOATSIGNBITSET( d1 );
561 if ( FLOATNOTZERO( d1 ) ) {
563 if ( FLOATSIGNBITSET( d1 ) ^ FLOATSIGNBITSET( d2 ) ) {
564 f = d1 / ( d1 - d2 );
565 if ( idMath::Fabs( start.y + f * dir.y ) <= dNear * leftScale ) {
566 if ( idMath::Fabs( start.z + f * dir.z ) <= dNear * upScale ) {
576 startInside &= FLOATSIGNBITSET( d1 );
577 if ( FLOATNOTZERO( d1 ) ) {
579 if ( FLOATSIGNBITSET( d1 ) ^ FLOATSIGNBITSET( d2 ) ) {
580 f = d1 / ( d1 - d2 );
581 if ( idMath::Fabs( start.y + f * dir.y ) <= dFar * leftScale ) {
582 if ( idMath::Fabs( start.z + f * dir.z ) <= dFar * upScale ) {
589 fstart = dFar * start.y;
591 lstart = dLeft * start.x;
592 lend = dLeft * end.x;
595 d1 = fstart - lstart;
596 startInside &= FLOATSIGNBITSET( d1 );
597 if ( FLOATNOTZERO( d1 ) ) {
599 if ( FLOATSIGNBITSET( d1 ) ^ FLOATSIGNBITSET( d2 ) ) {
600 f = d1 / ( d1 - d2 );
601 x = start.x + f * dir.x;
602 if ( x >= dNear && x <= dFar ) {
603 if ( idMath::Fabs( start.z + f * dir.z ) <= x * upScale ) {
611 d1 = -fstart - lstart;
612 startInside &= FLOATSIGNBITSET( d1 );
613 if ( FLOATNOTZERO( d1 ) ) {
615 if ( FLOATSIGNBITSET( d1 ) ^ FLOATSIGNBITSET( d2 ) ) {
616 f = d1 / ( d1 - d2 );
617 x = start.x + f * dir.x;
618 if ( x >= dNear && x <= dFar ) {
619 if ( idMath::Fabs( start.z + f * dir.z ) <= x * upScale ) {
626 fstart = dFar * start.z;
628 lstart = dUp * start.x;
632 d1 = fstart - lstart;
633 startInside &= FLOATSIGNBITSET( d1 );
634 if ( FLOATNOTZERO( d1 ) ) {
636 if ( FLOATSIGNBITSET( d1 ) ^ FLOATSIGNBITSET( d2 ) ) {
637 f = d1 / ( d1 - d2 );
638 x = start.x + f * dir.x;
639 if ( x >= dNear && x <= dFar ) {
640 if ( idMath::Fabs( start.y + f * dir.y ) <= x * leftScale ) {
648 d1 = -fstart - lstart;
649 startInside &= FLOATSIGNBITSET( d1 );
650 if ( FLOATNOTZERO( d1 ) ) {
652 if ( FLOATSIGNBITSET( d1 ) ^ FLOATSIGNBITSET( d2 ) ) {
653 f = d1 / ( d1 - d2 );
654 x = start.x + f * dir.x;
655 if ( x >= dNear && x <= dFar ) {
656 if ( idMath::Fabs( start.y + f * dir.y ) <= x * leftScale ) {
663 return ( startInside != 0 );
668 idFrustum::LocalRayIntersection
670 Returns true if the ray starts inside the frustum.
671 If there was an intersection scale1 <= scale2
674 bool idFrustum::LocalRayIntersection( const idVec3 &start, const idVec3 &dir, float &scale1, float &scale2 ) const {
676 float d1, d2, fstart, fend, lstart, lend, f, x;
677 float leftScale, upScale;
680 leftScale = dLeft * invFar;
681 upScale = dUp * invFar;
684 scale1 = idMath::INFINITY;
685 scale2 = -idMath::INFINITY;
688 if ( dNear > 0.0f ) {
689 d1 = dNear - start.x;
690 startInside &= FLOATSIGNBITSET( d1 );
693 f = d1 / ( d1 - d2 );
694 if ( idMath::Fabs( start.y + f * dir.y ) <= dNear * leftScale ) {
695 if ( idMath::Fabs( start.z + f * dir.z ) <= dNear * upScale ) {
696 if ( f < scale1 ) scale1 = f;
697 if ( f > scale2 ) scale2 = f;
705 startInside &= FLOATSIGNBITSET( d1 );
708 f = d1 / ( d1 - d2 );
709 if ( idMath::Fabs( start.y + f * dir.y ) <= dFar * leftScale ) {
710 if ( idMath::Fabs( start.z + f * dir.z ) <= dFar * upScale ) {
711 if ( f < scale1 ) scale1 = f;
712 if ( f > scale2 ) scale2 = f;
717 fstart = dFar * start.y;
719 lstart = dLeft * start.x;
720 lend = dLeft * end.x;
723 d1 = fstart - lstart;
724 startInside &= FLOATSIGNBITSET( d1 );
727 f = d1 / ( d1 - d2 );
728 x = start.x + f * dir.x;
729 if ( x >= dNear && x <= dFar ) {
730 if ( idMath::Fabs( start.z + f * dir.z ) <= x * upScale ) {
731 if ( f < scale1 ) scale1 = f;
732 if ( f > scale2 ) scale2 = f;
738 d1 = -fstart - lstart;
739 startInside &= FLOATSIGNBITSET( d1 );
742 f = d1 / ( d1 - d2 );
743 x = start.x + f * dir.x;
744 if ( x >= dNear && x <= dFar ) {
745 if ( idMath::Fabs( start.z + f * dir.z ) <= x * upScale ) {
746 if ( f < scale1 ) scale1 = f;
747 if ( f > scale2 ) scale2 = f;
752 fstart = dFar * start.z;
754 lstart = dUp * start.x;
758 d1 = fstart - lstart;
759 startInside &= FLOATSIGNBITSET( d1 );
762 f = d1 / ( d1 - d2 );
763 x = start.x + f * dir.x;
764 if ( x >= dNear && x <= dFar ) {
765 if ( idMath::Fabs( start.y + f * dir.y ) <= x * leftScale ) {
766 if ( f < scale1 ) scale1 = f;
767 if ( f > scale2 ) scale2 = f;
773 d1 = -fstart - lstart;
774 startInside &= FLOATSIGNBITSET( d1 );
777 f = d1 / ( d1 - d2 );
778 x = start.x + f * dir.x;
779 if ( x >= dNear && x <= dFar ) {
780 if ( idMath::Fabs( start.y + f * dir.y ) <= x * leftScale ) {
781 if ( f < scale1 ) scale1 = f;
782 if ( f > scale2 ) scale2 = f;
787 return ( startInside != 0 );
792 idFrustum::ContainsPoint
795 bool idFrustum::ContainsPoint( const idVec3 &point ) const {
796 return !CullPoint( point );
801 idFrustum::LocalFrustumIntersectsFrustum
804 bool idFrustum::LocalFrustumIntersectsFrustum( const idVec3 points[8], const bool testFirstSide ) const {
807 // test if any edges of the other frustum intersect this frustum
808 for ( i = 0; i < 4; i++ ) {
809 if ( LocalLineIntersection( points[i], points[4+i] ) ) {
813 if ( testFirstSide ) {
814 for ( i = 0; i < 4; i++ ) {
815 if ( LocalLineIntersection( points[i], points[(i+1)&3] ) ) {
820 for ( i = 0; i < 4; i++ ) {
821 if ( LocalLineIntersection( points[4+i], points[4+((i+1)&3)] ) ) {
831 idFrustum::LocalFrustumIntersectsBounds
834 bool idFrustum::LocalFrustumIntersectsBounds( const idVec3 points[8], const idBounds &bounds ) const {
837 // test if any edges of the other frustum intersect this frustum
838 for ( i = 0; i < 4; i++ ) {
839 if ( bounds.LineIntersection( points[i], points[4+i] ) ) {
843 if ( dNear > 0.0f ) {
844 for ( i = 0; i < 4; i++ ) {
845 if ( bounds.LineIntersection( points[i], points[(i+1)&3] ) ) {
850 for ( i = 0; i < 4; i++ ) {
851 if ( bounds.LineIntersection( points[4+i], points[4+((i+1)&3)] ) ) {
861 idFrustum::IntersectsBounds
864 bool idFrustum::IntersectsBounds( const idBounds &bounds ) const {
865 idVec3 localOrigin, center, extents;
868 center = ( bounds[0] + bounds[1] ) * 0.5f;
869 extents = bounds[1] - center;
871 localOrigin = ( center - origin ) * axis.Transpose();
872 localAxis = axis.Transpose();
874 if ( CullLocalBox( localOrigin, extents, localAxis ) ) {
878 idVec3 indexPoints[8], cornerVecs[4];
880 ToIndexPointsAndCornerVecs( indexPoints, cornerVecs );
882 if ( BoundsCullLocalFrustum( bounds, *this, indexPoints, cornerVecs ) ) {
886 idSwap( indexPoints[2], indexPoints[3] );
887 idSwap( indexPoints[6], indexPoints[7] );
889 if ( LocalFrustumIntersectsBounds( indexPoints, bounds ) ) {
893 BoxToPoints( localOrigin, extents, localAxis, indexPoints );
895 if ( LocalFrustumIntersectsFrustum( indexPoints, true ) ) {
904 idFrustum::IntersectsBox
907 bool idFrustum::IntersectsBox( const idBox &box ) const {
911 localOrigin = ( box.GetCenter() - origin ) * axis.Transpose();
912 localAxis = box.GetAxis() * axis.Transpose();
914 if ( CullLocalBox( localOrigin, box.GetExtents(), localAxis ) ) {
918 idVec3 indexPoints[8], cornerVecs[4];
919 idFrustum localFrustum;
921 localFrustum = *this;
922 localFrustum.origin = ( origin - box.GetCenter() ) * box.GetAxis().Transpose();
923 localFrustum.axis = axis * box.GetAxis().Transpose();
924 localFrustum.ToIndexPointsAndCornerVecs( indexPoints, cornerVecs );
926 if ( BoundsCullLocalFrustum( idBounds( -box.GetExtents(), box.GetExtents() ), localFrustum, indexPoints, cornerVecs ) ) {
930 idSwap( indexPoints[2], indexPoints[3] );
931 idSwap( indexPoints[6], indexPoints[7] );
933 if ( LocalFrustumIntersectsBounds( indexPoints, idBounds( -box.GetExtents(), box.GetExtents() ) ) ) {
937 BoxToPoints( localOrigin, box.GetExtents(), localAxis, indexPoints );
939 if ( LocalFrustumIntersectsFrustum( indexPoints, true ) ) {
948 idFrustum::IntersectsSphere
953 #define VORONOI_INDEX( x, y, z ) ( x + y * 3 + z * 9 )
955 bool idFrustum::IntersectsSphere( const idSphere &sphere ) const {
958 idVec3 p, dir, points[8];
960 if ( CullSphere( sphere ) ) {
967 p = ( sphere.GetOrigin() - origin ) * axis.Transpose();
969 if ( p.x <= dNear ) {
970 scale = dNear * invFar;
971 dir.y = idMath::Fabs( p.y ) - dLeft * scale;
972 dir.z = idMath::Fabs( p.z ) - dUp * scale;
974 else if ( p.x >= dFar ) {
975 dir.y = idMath::Fabs( p.y ) - dLeft;
976 dir.z = idMath::Fabs( p.z ) - dUp;
979 scale = p.x * invFar;
980 dir.y = idMath::Fabs( p.y ) - dLeft * scale;
981 dir.z = idMath::Fabs( p.z ) - dUp * scale;
983 if ( dir.y > 0.0f ) {
984 y = ( 1 + FLOATSIGNBITNOTSET( p.y ) );
986 if ( dir.z > 0.0f ) {
987 z = ( 1 + FLOATSIGNBITNOTSET( p.z ) );
990 scale = dLeft * dNear * invFar;
991 if ( p.x < dNear + ( scale - p.y ) * scale * invFar ) {
992 scale = dUp * dNear * invFar;
993 if ( p.x < dNear + ( scale - p.z ) * scale * invFar ) {
1002 else if ( p.x > dFar + ( dLeft - p.y ) * dLeft * invFar ) {
1005 else if ( p.x > dFar + ( dUp - p.z ) * dUp * invFar ) {
1010 r = sphere.GetRadius();
1011 index = VORONOI_INDEX( x, y, z );
1013 case VORONOI_INDEX( 0, 0, 0 ): return true;
1014 case VORONOI_INDEX( 1, 0, 0 ): return ( dNear - p.x < r );
1015 case VORONOI_INDEX( 2, 0, 0 ): return ( p.x - dFar < r );
1016 case VORONOI_INDEX( 0, 1, 0 ): d = dFar * p.y - dLeft * p.x; return ( d * d < r * r * ( dFar * dFar + dLeft * dLeft ) );
1017 case VORONOI_INDEX( 0, 2, 0 ): d = -dFar * p.z - dLeft * p.x; return ( d * d < r * r * ( dFar * dFar + dLeft * dLeft ) );
1018 case VORONOI_INDEX( 0, 0, 1 ): d = dFar * p.z - dUp * p.x; return ( d * d < r * r * ( dFar * dFar + dUp * dUp ) );
1019 case VORONOI_INDEX( 0, 0, 2 ): d = -dFar * p.z - dUp * p.x; return ( d * d < r * r * ( dFar * dFar + dUp * dUp ) );
1021 ToIndexPoints( points );
1023 case VORONOI_INDEX( 1, 1, 1 ): return sphere.ContainsPoint( points[0] );
1024 case VORONOI_INDEX( 2, 1, 1 ): return sphere.ContainsPoint( points[4] );
1025 case VORONOI_INDEX( 1, 2, 1 ): return sphere.ContainsPoint( points[1] );
1026 case VORONOI_INDEX( 2, 2, 1 ): return sphere.ContainsPoint( points[5] );
1027 case VORONOI_INDEX( 1, 1, 2 ): return sphere.ContainsPoint( points[2] );
1028 case VORONOI_INDEX( 2, 1, 2 ): return sphere.ContainsPoint( points[6] );
1029 case VORONOI_INDEX( 1, 2, 2 ): return sphere.ContainsPoint( points[3] );
1030 case VORONOI_INDEX( 2, 2, 2 ): return sphere.ContainsPoint( points[7] );
1031 case VORONOI_INDEX( 1, 1, 0 ): return sphere.LineIntersection( points[0], points[2] );
1032 case VORONOI_INDEX( 2, 1, 0 ): return sphere.LineIntersection( points[4], points[6] );
1033 case VORONOI_INDEX( 1, 2, 0 ): return sphere.LineIntersection( points[1], points[3] );
1034 case VORONOI_INDEX( 2, 2, 0 ): return sphere.LineIntersection( points[5], points[7] );
1035 case VORONOI_INDEX( 1, 0, 1 ): return sphere.LineIntersection( points[0], points[1] );
1036 case VORONOI_INDEX( 2, 0, 1 ): return sphere.LineIntersection( points[4], points[5] );
1037 case VORONOI_INDEX( 0, 1, 1 ): return sphere.LineIntersection( points[0], points[4] );
1038 case VORONOI_INDEX( 0, 2, 1 ): return sphere.LineIntersection( points[1], points[5] );
1039 case VORONOI_INDEX( 1, 0, 2 ): return sphere.LineIntersection( points[2], points[3] );
1040 case VORONOI_INDEX( 2, 0, 2 ): return sphere.LineIntersection( points[6], points[7] );
1041 case VORONOI_INDEX( 0, 1, 2 ): return sphere.LineIntersection( points[2], points[6] );
1042 case VORONOI_INDEX( 0, 2, 2 ): return sphere.LineIntersection( points[3], points[7] );
1052 idFrustum::IntersectsFrustum
1055 bool idFrustum::IntersectsFrustum( const idFrustum &frustum ) const {
1056 idVec3 indexPoints2[8], cornerVecs2[4];
1057 idFrustum localFrustum2;
1059 localFrustum2 = frustum;
1060 localFrustum2.origin = ( frustum.origin - origin ) * axis.Transpose();
1061 localFrustum2.axis = frustum.axis * axis.Transpose();
1062 localFrustum2.ToIndexPointsAndCornerVecs( indexPoints2, cornerVecs2 );
1064 if ( CullLocalFrustum( localFrustum2, indexPoints2, cornerVecs2 ) ) {
1068 idVec3 indexPoints1[8], cornerVecs1[4];
1069 idFrustum localFrustum1;
1071 localFrustum1 = *this;
1072 localFrustum1.origin = ( origin - frustum.origin ) * frustum.axis.Transpose();
1073 localFrustum1.axis = axis * frustum.axis.Transpose();
1074 localFrustum1.ToIndexPointsAndCornerVecs( indexPoints1, cornerVecs1 );
1076 if ( frustum.CullLocalFrustum( localFrustum1, indexPoints1, cornerVecs1 ) ) {
1080 idSwap( indexPoints2[2], indexPoints2[3] );
1081 idSwap( indexPoints2[6], indexPoints2[7] );
1083 if ( LocalFrustumIntersectsFrustum( indexPoints2, ( localFrustum2.dNear > 0.0f ) ) ) {
1087 idSwap( indexPoints1[2], indexPoints1[3] );
1088 idSwap( indexPoints1[6], indexPoints1[7] );
1090 if ( frustum.LocalFrustumIntersectsFrustum( indexPoints1, ( localFrustum1.dNear > 0.0f ) ) ) {
1099 idFrustum::IntersectsWinding
1102 bool idFrustum::IntersectsWinding( const idWinding &winding ) const {
1103 int i, j, *pointCull;
1105 idVec3 *localPoints, indexPoints[8], cornerVecs[4];
1109 localPoints = (idVec3 *) _alloca16( winding.GetNumPoints() * sizeof( idVec3 ) );
1110 pointCull = (int *) _alloca16( winding.GetNumPoints() * sizeof( int ) );
1112 transpose = axis.Transpose();
1113 for ( i = 0; i < winding.GetNumPoints(); i++ ) {
1114 localPoints[i] = ( winding[i].ToVec3() - origin ) * transpose;
1117 // if the winding is culled
1118 if ( CullLocalWinding( localPoints, winding.GetNumPoints(), pointCull ) ) {
1122 winding.GetPlane( plane );
1124 ToIndexPointsAndCornerVecs( indexPoints, cornerVecs );
1125 AxisProjection( indexPoints, cornerVecs, plane.Normal(), min, max );
1127 // if the frustum does not cross the winding plane
1128 if ( min + plane[3] > 0.0f || max + plane[3] < 0.0f ) {
1132 // test if any of the winding edges goes through the frustum
1133 for ( i = 0; i < winding.GetNumPoints(); i++ ) {
1134 j = (i+1)%winding.GetNumPoints();
1135 if ( !( pointCull[i] & pointCull[j] ) ) {
1136 if ( LocalLineIntersection( localPoints[i], localPoints[j] ) ) {
1142 idSwap( indexPoints[2], indexPoints[3] );
1143 idSwap( indexPoints[6], indexPoints[7] );
1145 // test if any edges of the frustum intersect the winding
1146 for ( i = 0; i < 4; i++ ) {
1147 if ( winding.LineIntersection( plane, indexPoints[i], indexPoints[4+i] ) ) {
1151 if ( dNear > 0.0f ) {
1152 for ( i = 0; i < 4; i++ ) {
1153 if ( winding.LineIntersection( plane, indexPoints[i], indexPoints[(i+1)&3] ) ) {
1158 for ( i = 0; i < 4; i++ ) {
1159 if ( winding.LineIntersection( plane, indexPoints[4+i], indexPoints[4+((i+1)&3)] ) ) {
1169 idFrustum::LineIntersection
1171 Returns true if the line intersects the box between the start and end point.
1174 bool idFrustum::LineIntersection( const idVec3 &start, const idVec3 &end ) const {
1175 return LocalLineIntersection( ( start - origin ) * axis.Transpose(), ( end - origin ) * axis.Transpose() );
1180 idFrustum::RayIntersection
1182 Returns true if the ray intersects the bounds.
1183 The ray can intersect the bounds in both directions from the start point.
1184 If start is inside the frustum then scale1 < 0 and scale2 > 0.
1187 bool idFrustum::RayIntersection( const idVec3 &start, const idVec3 &dir, float &scale1, float &scale2 ) const {
1188 if ( LocalRayIntersection( ( start - origin ) * axis.Transpose(), dir * axis.Transpose(), scale1, scale2 ) ) {
1191 if ( scale1 <= scale2 ) {
1199 idFrustum::FromProjection
1201 Creates a frustum which contains the projection of the bounds.
1204 bool idFrustum::FromProjection( const idBounds &bounds, const idVec3 &projectionOrigin, const float dFar ) {
1205 return FromProjection( idBox( bounds, vec3_origin, mat3_identity ), projectionOrigin, dFar );
1210 idFrustum::FromProjection
1212 Creates a frustum which contains the projection of the box.
1215 bool idFrustum::FromProjection( const idBox &box, const idVec3 &projectionOrigin, const float dFar ) {
1217 float value, bestValue;
1220 assert( dFar > 0.0f );
1222 this->dNear = this->dFar = this->invFar = 0.0f;
1224 dir = box.GetCenter() - projectionOrigin;
1225 if ( dir.Normalize() == 0.0f ) {
1230 bestValue = idMath::Fabs( box.GetAxis()[0] * dir );
1231 for ( i = 1; i < 3; i++ ) {
1232 value = idMath::Fabs( box.GetAxis()[i] * dir );
1233 if ( value * box.GetExtents()[bestAxis] * box.GetExtents()[bestAxis] < bestValue * box.GetExtents()[i] * box.GetExtents()[i] ) {
1241 int j, minX, minY, maxY, minZ, maxZ;
1244 minX = minY = maxY = minZ = maxZ = 0;
1246 for ( j = 0; j < 2; j++ ) {
1249 axis[1] = box.GetAxis()[bestAxis] - ( box.GetAxis()[bestAxis] * axis[0] ) * axis[0];
1250 axis[1].Normalize();
1251 axis[2].Cross( axis[0], axis[1] );
1253 BoxToPoints( ( box.GetCenter() - projectionOrigin ) * axis.Transpose(), box.GetExtents(), box.GetAxis() * axis.Transpose(), points );
1255 if ( points[0].x <= 1.0f ) {
1259 minX = minY = maxY = minZ = maxZ = 0;
1260 for ( i = 1; i < 8; i++ ) {
1261 if ( points[i].x <= 1.0f ) {
1264 if ( points[i].x < points[minX].x ) {
1267 if ( points[minY].x * points[i].y < points[i].x * points[minY].y ) {
1269 } else if ( points[maxY].x * points[i].y > points[i].x * points[maxY].y ) {
1272 if ( points[minZ].x * points[i].z < points[i].x * points[minZ].z ) {
1274 } else if ( points[maxZ].x * points[i].z > points[i].x * points[maxZ].z ) {
1280 dir += idMath::Tan16( 0.5f * ( idMath::ATan16( points[minY].y, points[minY].x ) + idMath::ATan16( points[maxY].y, points[maxY].x ) ) ) * axis[1];
1281 dir += idMath::Tan16( 0.5f * ( idMath::ATan16( points[minZ].z, points[minZ].x ) + idMath::ATan16( points[maxZ].z, points[maxZ].x ) ) ) * axis[2];
1286 this->origin = projectionOrigin;
1287 this->dNear = points[minX].x;
1289 this->dLeft = Max( idMath::Fabs( points[minY].y / points[minY].x ), idMath::Fabs( points[maxY].y / points[maxY].x ) ) * dFar;
1290 this->dUp = Max( idMath::Fabs( points[minZ].z / points[minZ].x ), idMath::Fabs( points[maxZ].z / points[maxZ].x ) ) * dFar;
1291 this->invFar = 1.0f / dFar;
1300 for ( j = 0; j < 2; j++ ) {
1303 axis[1] = box.GetAxis()[bestAxis] - ( box.GetAxis()[bestAxis] * axis[0] ) * axis[0];
1304 axis[1].Normalize();
1305 axis[2].Cross( axis[0], axis[1] );
1307 BoxToPoints( ( box.GetCenter() - projectionOrigin ) * axis.Transpose(), box.GetExtents(), box.GetAxis() * axis.Transpose(), points );
1310 for ( i = 0; i < 8; i++ ) {
1318 b.AddPoint( points[i] );
1322 dir += idMath::Tan16( 0.5f * ( idMath::ATan16( b[1][1] ) + idMath::ATan16( b[0][1] ) ) ) * axis[1];
1323 dir += idMath::Tan16( 0.5f * ( idMath::ATan16( b[1][2] ) + idMath::ATan16( b[0][2] ) ) ) * axis[2];
1328 this->origin = projectionOrigin;
1329 this->dNear = b[0][0];
1331 this->dLeft = Max( idMath::Fabs( b[0][1] ), idMath::Fabs( b[1][1] ) ) * dFar;
1332 this->dUp = Max( idMath::Fabs( b[0][2] ), idMath::Fabs( b[1][2] ) ) * dFar;
1333 this->invFar = 1.0f / dFar;
1341 axis[1] = box.GetAxis()[bestAxis] - ( box.GetAxis()[bestAxis] * axis[0] ) * axis[0];
1342 axis[1].Normalize();
1343 axis[2].Cross( axis[0], axis[1] );
1345 for ( i = 0; i < 3; i++ ) {
1346 dist[i] = idMath::Fabs( box.GetExtents()[0] * ( axis[i] * box.GetAxis()[0] ) ) +
1347 idMath::Fabs( box.GetExtents()[1] * ( axis[i] * box.GetAxis()[1] ) ) +
1348 idMath::Fabs( box.GetExtents()[2] * ( axis[i] * box.GetAxis()[2] ) );
1351 dist[0] = axis[0] * ( box.GetCenter() - projectionOrigin ) - dist[0];
1352 if ( dist[0] <= 1.0f ) {
1355 float invDist = 1.0f / dist[0];
1357 this->origin = projectionOrigin;
1358 this->dNear = dist[0];
1360 this->dLeft = dist[1] * invDist * dFar;
1361 this->dUp = dist[2] * invDist * dFar;
1362 this->invFar = 1.0f / dFar;
1371 idFrustum::FromProjection
1373 Creates a frustum which contains the projection of the sphere.
1376 bool idFrustum::FromProjection( const idSphere &sphere, const idVec3 &projectionOrigin, const float dFar ) {
1378 float d, r, s, x, y;
1380 assert( dFar > 0.0f );
1382 dir = sphere.GetOrigin() - projectionOrigin;
1383 d = dir.Normalize();
1384 r = sphere.GetRadius();
1386 if ( d <= r + 1.0f ) {
1387 this->dNear = this->dFar = this->invFar = 0.0f;
1391 origin = projectionOrigin;
1392 axis = dir.ToMat3();
1394 s = idMath::Sqrt( d * d - r * r );
1396 y = idMath::Sqrt( s * s - x * x );
1398 this->dNear = d - r;
1400 this->dLeft = x / y * dFar;
1402 this->invFar = 1.0f / dFar;
1409 idFrustum::ConstrainToBounds
1411 Returns false if no part of the bounds extends beyond the near plane.
1414 bool idFrustum::ConstrainToBounds( const idBounds &bounds ) {
1415 float min, max, newdFar;
1417 bounds.AxisProjection( axis[0], min, max );
1418 newdFar = max - axis[0] * origin;
1419 if ( newdFar <= dNear ) {
1420 MoveFarDistance( dNear + 1.0f );
1423 MoveFarDistance( newdFar );
1429 idFrustum::ConstrainToBox
1431 Returns false if no part of the box extends beyond the near plane.
1434 bool idFrustum::ConstrainToBox( const idBox &box ) {
1435 float min, max, newdFar;
1437 box.AxisProjection( axis[0], min, max );
1438 newdFar = max - axis[0] * origin;
1439 if ( newdFar <= dNear ) {
1440 MoveFarDistance( dNear + 1.0f );
1443 MoveFarDistance( newdFar );
1449 idFrustum::ConstrainToSphere
1451 Returns false if no part of the sphere extends beyond the near plane.
1454 bool idFrustum::ConstrainToSphere( const idSphere &sphere ) {
1455 float min, max, newdFar;
1457 sphere.AxisProjection( axis[0], min, max );
1458 newdFar = max - axis[0] * origin;
1459 if ( newdFar <= dNear ) {
1460 MoveFarDistance( dNear + 1.0f );
1463 MoveFarDistance( newdFar );
1469 idFrustum::ConstrainToFrustum
1471 Returns false if no part of the frustum extends beyond the near plane.
1474 bool idFrustum::ConstrainToFrustum( const idFrustum &frustum ) {
1475 float min, max, newdFar;
1477 frustum.AxisProjection( axis[0], min, max );
1478 newdFar = max - axis[0] * origin;
1479 if ( newdFar <= dNear ) {
1480 MoveFarDistance( dNear + 1.0f );
1483 MoveFarDistance( newdFar );
1491 planes point outwards
1494 void idFrustum::ToPlanes( idPlane planes[6] ) const {
1499 planes[0].Normal() = -axis[0];
1500 planes[0].SetDist( -dNear );
1501 planes[1].Normal() = axis[0];
1502 planes[1].SetDist( dFar );
1504 scaled[0] = axis[1] * dLeft;
1505 scaled[1] = axis[2] * dUp;
1506 points[0] = scaled[0] + scaled[1];
1507 points[1] = -scaled[0] + scaled[1];
1508 points[2] = -scaled[0] - scaled[1];
1509 points[3] = scaled[0] - scaled[1];
1511 for ( i = 0; i < 4; i++ ) {
1512 planes[i+2].Normal() = points[i].Cross( points[(i+1)&3] - points[i] );
1513 planes[i+2].Normalize();
1514 planes[i+2].FitThroughPoint( points[i] );
1523 void idFrustum::ToPoints( idVec3 points[8] ) const {
1526 scaled[0] = origin + axis[0] * dNear;
1527 scaled[1] = axis[1] * ( dLeft * dNear * invFar );
1528 scaled[2] = axis[2] * ( dUp * dNear * invFar );
1530 points[0] = scaled[0] + scaled[1];
1531 points[1] = scaled[0] - scaled[1];
1532 points[2] = points[1] - scaled[2];
1533 points[3] = points[0] - scaled[2];
1534 points[0] += scaled[2];
1535 points[1] += scaled[2];
1537 scaled[0] = origin + axis[0] * dFar;
1538 scaled[1] = axis[1] * dLeft;
1539 scaled[2] = axis[2] * dUp;
1541 points[4] = scaled[0] + scaled[1];
1542 points[5] = scaled[0] - scaled[1];
1543 points[6] = points[5] - scaled[2];
1544 points[7] = points[4] - scaled[2];
1545 points[4] += scaled[2];
1546 points[5] += scaled[2];
1551 idFrustum::ToClippedPoints
1554 void idFrustum::ToClippedPoints( const float fractions[4], idVec3 points[8] ) const {
1557 scaled[0] = origin + axis[0] * dNear;
1558 scaled[1] = axis[1] * ( dLeft * dNear * invFar );
1559 scaled[2] = axis[2] * ( dUp * dNear * invFar );
1561 points[0] = scaled[0] + scaled[1];
1562 points[1] = scaled[0] - scaled[1];
1563 points[2] = points[1] - scaled[2];
1564 points[3] = points[0] - scaled[2];
1565 points[0] += scaled[2];
1566 points[1] += scaled[2];
1568 scaled[0] = axis[0] * dFar;
1569 scaled[1] = axis[1] * dLeft;
1570 scaled[2] = axis[2] * dUp;
1572 points[4] = scaled[0] + scaled[1];
1573 points[5] = scaled[0] - scaled[1];
1574 points[6] = points[5] - scaled[2];
1575 points[7] = points[4] - scaled[2];
1576 points[4] += scaled[2];
1577 points[5] += scaled[2];
1579 points[4] = origin + fractions[0] * points[4];
1580 points[5] = origin + fractions[1] * points[5];
1581 points[6] = origin + fractions[2] * points[6];
1582 points[7] = origin + fractions[3] * points[7];
1587 idFrustum::ToIndexPoints
1590 void idFrustum::ToIndexPoints( idVec3 indexPoints[8] ) const {
1593 scaled[0] = origin + axis[0] * dNear;
1594 scaled[1] = axis[1] * ( dLeft * dNear * invFar );
1595 scaled[2] = axis[2] * ( dUp * dNear * invFar );
1597 indexPoints[0] = scaled[0] - scaled[1];
1598 indexPoints[2] = scaled[0] + scaled[1];
1599 indexPoints[1] = indexPoints[0] + scaled[2];
1600 indexPoints[3] = indexPoints[2] + scaled[2];
1601 indexPoints[0] -= scaled[2];
1602 indexPoints[2] -= scaled[2];
1604 scaled[0] = origin + axis[0] * dFar;
1605 scaled[1] = axis[1] * dLeft;
1606 scaled[2] = axis[2] * dUp;
1608 indexPoints[4] = scaled[0] - scaled[1];
1609 indexPoints[6] = scaled[0] + scaled[1];
1610 indexPoints[5] = indexPoints[4] + scaled[2];
1611 indexPoints[7] = indexPoints[6] + scaled[2];
1612 indexPoints[4] -= scaled[2];
1613 indexPoints[6] -= scaled[2];
1618 idFrustum::ToIndexPointsAndCornerVecs
1623 void idFrustum::ToIndexPointsAndCornerVecs( idVec3 indexPoints[8], idVec3 cornerVecs[4] ) const {
1626 scaled[0] = origin + axis[0] * dNear;
1627 scaled[1] = axis[1] * ( dLeft * dNear * invFar );
1628 scaled[2] = axis[2] * ( dUp * dNear * invFar );
1630 indexPoints[0] = scaled[0] - scaled[1];
1631 indexPoints[2] = scaled[0] + scaled[1];
1632 indexPoints[1] = indexPoints[0] + scaled[2];
1633 indexPoints[3] = indexPoints[2] + scaled[2];
1634 indexPoints[0] -= scaled[2];
1635 indexPoints[2] -= scaled[2];
1637 scaled[0] = axis[0] * dFar;
1638 scaled[1] = axis[1] * dLeft;
1639 scaled[2] = axis[2] * dUp;
1641 cornerVecs[0] = scaled[0] - scaled[1];
1642 cornerVecs[2] = scaled[0] + scaled[1];
1643 cornerVecs[1] = cornerVecs[0] + scaled[2];
1644 cornerVecs[3] = cornerVecs[2] + scaled[2];
1645 cornerVecs[0] -= scaled[2];
1646 cornerVecs[2] -= scaled[2];
1648 indexPoints[4] = cornerVecs[0] + origin;
1649 indexPoints[5] = cornerVecs[1] + origin;
1650 indexPoints[6] = cornerVecs[2] + origin;
1651 indexPoints[7] = cornerVecs[3] + origin;
1656 idFrustum::AxisProjection
1661 void idFrustum::AxisProjection( const idVec3 indexPoints[8], const idVec3 cornerVecs[4], const idVec3 &dir, float &min, float &max ) const {
1665 dy = dir.x * axis[1].x + dir.y * axis[1].y + dir.z * axis[1].z;
1666 dz = dir.x * axis[2].x + dir.y * axis[2].y + dir.z * axis[2].z;
1667 index = ( FLOATSIGNBITSET( dy ) << 1 ) | FLOATSIGNBITSET( dz );
1668 dx = dir.x * cornerVecs[index].x + dir.y * cornerVecs[index].y + dir.z * cornerVecs[index].z;
1669 index |= ( FLOATSIGNBITSET( dx ) << 2 );
1670 min = indexPoints[index] * dir;
1672 dx = -dir.x * cornerVecs[index].x - dir.y * cornerVecs[index].y - dir.z * cornerVecs[index].z;
1673 index |= ( FLOATSIGNBITSET( dx ) << 2 );
1674 max = indexPoints[index] * dir;
1679 idFrustum::AxisProjection
1684 void idFrustum::AxisProjection( const idVec3 &dir, float &min, float &max ) const {
1685 idVec3 indexPoints[8], cornerVecs[4];
1687 ToIndexPointsAndCornerVecs( indexPoints, cornerVecs );
1688 AxisProjection( indexPoints, cornerVecs, dir, min, max );
1693 idFrustum::AxisProjection
1698 void idFrustum::AxisProjection( const idMat3 &ax, idBounds &bounds ) const {
1699 idVec3 indexPoints[8], cornerVecs[4];
1701 ToIndexPointsAndCornerVecs( indexPoints, cornerVecs );
1702 AxisProjection( indexPoints, cornerVecs, ax[0], bounds[0][0], bounds[1][0] );
1703 AxisProjection( indexPoints, cornerVecs, ax[1], bounds[0][1], bounds[1][1] );
1704 AxisProjection( indexPoints, cornerVecs, ax[2], bounds[0][2], bounds[1][2] );
1709 idFrustum::AddLocalLineToProjectionBoundsSetCull
1712 void idFrustum::AddLocalLineToProjectionBoundsSetCull( const idVec3 &start, const idVec3 &end, int &startCull, int &endCull, idBounds &bounds ) const {
1714 float d1, d2, fstart, fend, lstart, lend, f;
1715 float leftScale, upScale;
1718 #ifdef FRUSTUM_DEBUG
1719 static idCVar r_showInteractionScissors( "r_showInteractionScissors", "0", CVAR_RENDERER | CVAR_INTEGER, "", 0, 2, idCmdSystem::ArgCompletion_Integer<0,2> );
1720 if ( r_showInteractionScissors.GetInteger() > 1 ) {
1721 session->rw->DebugLine( colorGreen, origin + start * axis, origin + end * axis );
1725 leftScale = dLeft * invFar;
1726 upScale = dUp * invFar;
1729 fstart = dFar * start.y;
1730 fend = dFar * end.y;
1731 lstart = dLeft * start.x;
1732 lend = dLeft * end.x;
1735 d1 = -fstart + lstart;
1737 cull1 = FLOATSIGNBITSET( d1 );
1738 cull2 = FLOATSIGNBITSET( d2 );
1739 if ( FLOATNOTZERO( d1 ) ) {
1740 if ( FLOATSIGNBITSET( d1 ) ^ FLOATSIGNBITSET( d2 ) ) {
1741 f = d1 / ( d1 - d2 );
1742 p.x = start.x + f * dir.x;
1744 p.z = start.z + f * dir.z;
1745 if ( idMath::Fabs( p.z ) <= p.x * upScale ) {
1747 p.z = p.z * dFar / ( p.x * dUp );
1748 bounds.AddPoint( p );
1755 d1 = fstart + lstart;
1757 cull1 |= FLOATSIGNBITSET( d1 ) << 1;
1758 cull2 |= FLOATSIGNBITSET( d2 ) << 1;
1759 if ( FLOATNOTZERO( d1 ) ) {
1760 if ( FLOATSIGNBITSET( d1 ) ^ FLOATSIGNBITSET( d2 ) ) {
1761 f = d1 / ( d1 - d2 );
1762 p.x = start.x + f * dir.x;
1764 p.z = start.z + f * dir.z;
1765 if ( idMath::Fabs( p.z ) <= p.x * upScale ) {
1767 p.z = p.z * dFar / ( p.x * dUp );
1768 bounds.AddPoint( p );
1774 fstart = dFar * start.z;
1775 fend = dFar * end.z;
1776 lstart = dUp * start.x;
1780 d1 = -fstart + lstart;
1782 cull1 |= FLOATSIGNBITSET( d1 ) << 2;
1783 cull2 |= FLOATSIGNBITSET( d2 ) << 2;
1784 if ( FLOATNOTZERO( d1 ) ) {
1785 if ( FLOATSIGNBITSET( d1 ) ^ FLOATSIGNBITSET( d2 ) ) {
1786 f = d1 / ( d1 - d2 );
1787 p.x = start.x + f * dir.x;
1789 p.y = start.y + f * dir.y;
1790 if ( idMath::Fabs( p.y ) <= p.x * leftScale ) {
1791 p.y = p.y * dFar / ( p.x * dLeft );
1793 bounds.AddPoint( p );
1800 d1 = fstart + lstart;
1802 cull1 |= FLOATSIGNBITSET( d1 ) << 3;
1803 cull2 |= FLOATSIGNBITSET( d2 ) << 3;
1804 if ( FLOATNOTZERO( d1 ) ) {
1805 if ( FLOATSIGNBITSET( d1 ) ^ FLOATSIGNBITSET( d2 ) ) {
1806 f = d1 / ( d1 - d2 );
1807 p.x = start.x + f * dir.x;
1809 p.y = start.y + f * dir.y;
1810 if ( idMath::Fabs( p.y ) <= p.x * leftScale ) {
1811 p.y = p.y * dFar / ( p.x * dLeft );
1813 bounds.AddPoint( p );
1819 if ( cull1 == 0 && start.x > 0.0f ) {
1820 // add start point to projection bounds
1822 p.y = start.y * dFar / ( start.x * dLeft );
1823 p.z = start.z * dFar / ( start.x * dUp );
1824 bounds.AddPoint( p );
1827 if ( cull2 == 0 && end.x > 0.0f ) {
1828 // add end point to projection bounds
1830 p.y = end.y * dFar / ( end.x * dLeft );
1831 p.z = end.z * dFar / ( end.x * dUp );
1832 bounds.AddPoint( p );
1835 if ( start.x < bounds[0].x ) {
1836 bounds[0].x = start.x < 0.0f ? 0.0f : start.x;
1838 if ( end.x < bounds[0].x ) {
1839 bounds[0].x = end.x < 0.0f ? 0.0f : end.x;
1848 idFrustum::AddLocalLineToProjectionBoundsUseCull
1851 void idFrustum::AddLocalLineToProjectionBoundsUseCull( const idVec3 &start, const idVec3 &end, int startCull, int endCull, idBounds &bounds ) const {
1853 float d1, d2, fstart, fend, lstart, lend, f;
1854 float leftScale, upScale;
1857 clip = startCull ^ endCull;
1862 #ifdef FRUSTUM_DEBUG
1863 static idCVar r_showInteractionScissors( "r_showInteractionScissors", "0", CVAR_RENDERER | CVAR_INTEGER, "", 0, 2, idCmdSystem::ArgCompletion_Integer<0,2> );
1864 if ( r_showInteractionScissors.GetInteger() > 1 ) {
1865 session->rw->DebugLine( colorGreen, origin + start * axis, origin + end * axis );
1869 leftScale = dLeft * invFar;
1870 upScale = dUp * invFar;
1873 if ( clip & (1|2) ) {
1875 fstart = dFar * start.y;
1876 fend = dFar * end.y;
1877 lstart = dLeft * start.x;
1878 lend = dLeft * end.x;
1882 d1 = -fstart + lstart;
1884 if ( FLOATNOTZERO( d1 ) ) {
1885 if ( FLOATSIGNBITSET( d1 ) ^ FLOATSIGNBITSET( d2 ) ) {
1886 f = d1 / ( d1 - d2 );
1887 p.x = start.x + f * dir.x;
1889 p.z = start.z + f * dir.z;
1890 if ( idMath::Fabs( p.z ) <= p.x * upScale ) {
1892 p.z = p.z * dFar / ( p.x * dUp );
1893 bounds.AddPoint( p );
1902 d1 = fstart + lstart;
1904 if ( FLOATNOTZERO( d1 ) ) {
1905 if ( FLOATSIGNBITSET( d1 ) ^ FLOATSIGNBITSET( d2 ) ) {
1906 f = d1 / ( d1 - d2 );
1907 p.x = start.x + f * dir.x;
1909 p.z = start.z + f * dir.z;
1910 if ( idMath::Fabs( p.z ) <= p.x * upScale ) {
1912 p.z = p.z * dFar / ( p.x * dUp );
1913 bounds.AddPoint( p );
1921 if ( clip & (4|8) ) {
1923 fstart = dFar * start.z;
1924 fend = dFar * end.z;
1925 lstart = dUp * start.x;
1930 d1 = -fstart + lstart;
1932 if ( FLOATNOTZERO( d1 ) ) {
1933 if ( FLOATSIGNBITSET( d1 ) ^ FLOATSIGNBITSET( d2 ) ) {
1934 f = d1 / ( d1 - d2 );
1935 p.x = start.x + f * dir.x;
1937 p.y = start.y + f * dir.y;
1938 if ( idMath::Fabs( p.y ) <= p.x * leftScale ) {
1939 p.y = p.y * dFar / ( p.x * dLeft );
1941 bounds.AddPoint( p );
1950 d1 = fstart + lstart;
1952 if ( FLOATNOTZERO( d1 ) ) {
1953 if ( FLOATSIGNBITSET( d1 ) ^ FLOATSIGNBITSET( d2 ) ) {
1954 f = d1 / ( d1 - d2 );
1955 p.x = start.x + f * dir.x;
1957 p.y = start.y + f * dir.y;
1958 if ( idMath::Fabs( p.y ) <= p.x * leftScale ) {
1959 p.y = p.y * dFar / ( p.x * dLeft );
1961 bounds.AddPoint( p );
1972 idFrustum::BoundsRayIntersection
1974 Returns true if the ray starts inside the bounds.
1975 If there was an intersection scale1 <= scale2
1978 bool idFrustum::BoundsRayIntersection( const idBounds &bounds, const idVec3 &start, const idVec3 &dir, float &scale1, float &scale2 ) const {
1981 int i, startInside = 1;
1983 scale1 = idMath::INFINITY;
1984 scale2 = -idMath::INFINITY;
1988 for ( i = 0; i < 2; i++ ) {
1989 d1 = start.x - bounds[i].x;
1990 startInside &= FLOATSIGNBITSET( d1 ) ^ i;
1991 d2 = end.x - bounds[i].x;
1993 f = d1 / ( d1 - d2 );
1994 p.y = start.y + f * dir.y;
1995 if ( bounds[0].y <= p.y && p.y <= bounds[1].y ) {
1996 p.z = start.z + f * dir.z;
1997 if ( bounds[0].z <= p.z && p.z <= bounds[1].z ) {
1998 if ( f < scale1 ) scale1 = f;
1999 if ( f > scale2 ) scale2 = f;
2004 d1 = start.y - bounds[i].y;
2005 startInside &= FLOATSIGNBITSET( d1 ) ^ i;
2006 d2 = end.y - bounds[i].y;
2008 f = d1 / ( d1 - d2 );
2009 p.x = start.x + f * dir.x;
2010 if ( bounds[0].x <= p.x && p.x <= bounds[1].x ) {
2011 p.z = start.z + f * dir.z;
2012 if ( bounds[0].z <= p.z && p.z <= bounds[1].z ) {
2013 if ( f < scale1 ) scale1 = f;
2014 if ( f > scale2 ) scale2 = f;
2019 d1 = start.z - bounds[i].z;
2020 startInside &= FLOATSIGNBITSET( d1 ) ^ i;
2021 d2 = end.z - bounds[i].z;
2023 f = d1 / ( d1 - d2 );
2024 p.x = start.x + f * dir.x;
2025 if ( bounds[0].x <= p.x && p.x <= bounds[1].x ) {
2026 p.y = start.y + f * dir.y;
2027 if ( bounds[0].y <= p.y && p.y <= bounds[1].y ) {
2028 if ( f < scale1 ) scale1 = f;
2029 if ( f > scale2 ) scale2 = f;
2035 return ( startInside != 0 );
2040 idFrustum::ProjectionBounds
2043 bool idFrustum::ProjectionBounds( const idBounds &bounds, idBounds &projectionBounds ) const {
2044 return ProjectionBounds( idBox( bounds, vec3_origin, mat3_identity ), projectionBounds );
2051 idFrustum::ProjectionBounds
2054 bool idFrustum::ProjectionBounds( const idBox &box, idBounds &projectionBounds ) const {
2055 int i, p1, p2, pointCull[8], culled, outside;
2056 float scale1, scale2;
2057 idFrustum localFrustum;
2058 idVec3 points[8], localOrigin;
2059 idMat3 localAxis, localScaled;
2060 idBounds bounds( -box.GetExtents(), box.GetExtents() );
2062 // if the frustum origin is inside the bounds
2063 if ( bounds.ContainsPoint( ( origin - box.GetCenter() ) * box.GetAxis().Transpose() ) ) {
2064 // bounds that cover the whole frustum
2065 float boxMin, boxMax, base;
2067 base = origin * axis[0];
2068 box.AxisProjection( axis[0], boxMin, boxMax );
2070 projectionBounds[0].x = boxMin - base;
2071 projectionBounds[1].x = boxMax - base;
2072 projectionBounds[0].y = projectionBounds[0].z = -1.0f;
2073 projectionBounds[1].y = projectionBounds[1].z = 1.0f;
2078 projectionBounds.Clear();
2080 // transform the bounds into the space of this frustum
2081 localOrigin = ( box.GetCenter() - origin ) * axis.Transpose();
2082 localAxis = box.GetAxis() * axis.Transpose();
2083 BoxToPoints( localOrigin, box.GetExtents(), localAxis, points );
2085 // test outer four edges of the bounds
2088 for ( i = 0; i < 4; i++ ) {
2091 AddLocalLineToProjectionBoundsSetCull( points[p1], points[p2], pointCull[p1], pointCull[p2], projectionBounds );
2092 culled &= pointCull[p1] & pointCull[p2];
2093 outside |= pointCull[p1] | pointCull[p2];
2096 // if the bounds are completely outside this frustum
2101 // if the bounds are completely inside this frustum
2106 // test the remaining edges of the bounds
2107 for ( i = 0; i < 4; i++ ) {
2110 AddLocalLineToProjectionBoundsUseCull( points[p1], points[p2], pointCull[p1], pointCull[p2], projectionBounds );
2113 for ( i = 0; i < 4; i++ ) {
2116 AddLocalLineToProjectionBoundsUseCull( points[p1], points[p2], pointCull[p1], pointCull[p2], projectionBounds );
2119 // if the bounds extend beyond two or more boundaries of this frustum
2120 if ( outside != 1 && outside != 2 && outside != 4 && outside != 8 ) {
2122 localOrigin = ( origin - box.GetCenter() ) * box.GetAxis().Transpose();
2123 localScaled = axis * box.GetAxis().Transpose();
2124 localScaled[0] *= dFar;
2125 localScaled[1] *= dLeft;
2126 localScaled[2] *= dUp;
2128 // test the outer edges of this frustum for intersection with the bounds
2129 if ( (outside & 2) && (outside & 8) ) {
2130 BoundsRayIntersection( bounds, localOrigin, localScaled[0] - localScaled[1] - localScaled[2], scale1, scale2 );
2131 if ( scale1 <= scale2 && scale1 >= 0.0f ) {
2132 projectionBounds.AddPoint( idVec3( scale1 * dFar, -1.0f, -1.0f ) );
2133 projectionBounds.AddPoint( idVec3( scale2 * dFar, -1.0f, -1.0f ) );
2136 if ( (outside & 2) && (outside & 4) ) {
2137 BoundsRayIntersection( bounds, localOrigin, localScaled[0] - localScaled[1] + localScaled[2], scale1, scale2 );
2138 if ( scale1 <= scale2 && scale1 >= 0.0f ) {
2139 projectionBounds.AddPoint( idVec3( scale1 * dFar, -1.0f, 1.0f ) );
2140 projectionBounds.AddPoint( idVec3( scale2 * dFar, -1.0f, 1.0f ) );
2143 if ( (outside & 1) && (outside & 8) ) {
2144 BoundsRayIntersection( bounds, localOrigin, localScaled[0] + localScaled[1] - localScaled[2], scale1, scale2 );
2145 if ( scale1 <= scale2 && scale1 >= 0.0f ) {
2146 projectionBounds.AddPoint( idVec3( scale1 * dFar, 1.0f, -1.0f ) );
2147 projectionBounds.AddPoint( idVec3( scale2 * dFar, 1.0f, -1.0f ) );
2150 if ( (outside & 1) && (outside & 2) ) {
2151 BoundsRayIntersection( bounds, localOrigin, localScaled[0] + localScaled[1] + localScaled[2], scale1, scale2 );
2152 if ( scale1 <= scale2 && scale1 >= 0.0f ) {
2153 projectionBounds.AddPoint( idVec3( scale1 * dFar, 1.0f, 1.0f ) );
2154 projectionBounds.AddPoint( idVec3( scale2 * dFar, 1.0f, 1.0f ) );
2166 idFrustum::ProjectionBounds
2169 bool idFrustum::ProjectionBounds( const idSphere &sphere, idBounds &projectionBounds ) const {
2170 float d, r, rs, sFar;
2173 projectionBounds.Clear();
2175 center = ( sphere.GetOrigin() - origin ) * axis.Transpose();
2176 r = sphere.GetRadius();
2180 // test left/right planes
2181 d = dFar * idMath::Fabs( center.y ) - dLeft * center.x;
2182 if ( ( d * d ) > rs * ( sFar + dLeft * dLeft ) ) {
2186 // test up/down planes
2187 d = dFar * idMath::Fabs( center.z ) - dUp * center.x;
2188 if ( ( d * d ) > rs * ( sFar + dUp * dUp ) ) {
2192 // bounds that cover the whole frustum
2193 projectionBounds[0].x = 0.0f;
2194 projectionBounds[1].x = dFar;
2195 projectionBounds[0].y = projectionBounds[0].z = -1.0f;
2196 projectionBounds[1].y = projectionBounds[1].z = 1.0f;
2202 idFrustum::ProjectionBounds
2205 bool idFrustum::ProjectionBounds( const idFrustum &frustum, idBounds &projectionBounds ) const {
2206 int i, p1, p2, pointCull[8], culled, outside;
2207 float scale1, scale2;
2208 idFrustum localFrustum;
2209 idVec3 points[8], localOrigin;
2212 // if the frustum origin is inside the other frustum
2213 if ( frustum.ContainsPoint( origin ) ) {
2214 // bounds that cover the whole frustum
2215 float frustumMin, frustumMax, base;
2217 base = origin * axis[0];
2218 frustum.AxisProjection( axis[0], frustumMin, frustumMax );
2220 projectionBounds[0].x = frustumMin - base;
2221 projectionBounds[1].x = frustumMax - base;
2222 projectionBounds[0].y = projectionBounds[0].z = -1.0f;
2223 projectionBounds[1].y = projectionBounds[1].z = 1.0f;
2227 projectionBounds.Clear();
2229 // transform the given frustum into the space of this frustum
2230 localFrustum = frustum;
2231 localFrustum.origin = ( frustum.origin - origin ) * axis.Transpose();
2232 localFrustum.axis = frustum.axis * axis.Transpose();
2233 localFrustum.ToPoints( points );
2235 // test outer four edges of the other frustum
2238 for ( i = 0; i < 4; i++ ) {
2241 AddLocalLineToProjectionBoundsSetCull( points[p1], points[p2], pointCull[p1], pointCull[p2], projectionBounds );
2242 culled &= pointCull[p1] & pointCull[p2];
2243 outside |= pointCull[p1] | pointCull[p2];
2246 // if the other frustum is completely outside this frustum
2251 // if the other frustum is completely inside this frustum
2256 // test the remaining edges of the other frustum
2257 if ( localFrustum.dNear > 0.0f ) {
2258 for ( i = 0; i < 4; i++ ) {
2261 AddLocalLineToProjectionBoundsUseCull( points[p1], points[p2], pointCull[p1], pointCull[p2], projectionBounds );
2265 for ( i = 0; i < 4; i++ ) {
2268 AddLocalLineToProjectionBoundsUseCull( points[p1], points[p2], pointCull[p1], pointCull[p2], projectionBounds );
2271 // if the other frustum extends beyond two or more boundaries of this frustum
2272 if ( outside != 1 && outside != 2 && outside != 4 && outside != 8 ) {
2274 localOrigin = ( origin - frustum.origin ) * frustum.axis.Transpose();
2275 localScaled = axis * frustum.axis.Transpose();
2276 localScaled[0] *= dFar;
2277 localScaled[1] *= dLeft;
2278 localScaled[2] *= dUp;
2280 // test the outer edges of this frustum for intersection with the other frustum
2281 if ( (outside & 2) && (outside & 8) ) {
2282 frustum.LocalRayIntersection( localOrigin, localScaled[0] - localScaled[1] - localScaled[2], scale1, scale2 );
2283 if ( scale1 <= scale2 && scale1 >= 0.0f ) {
2284 projectionBounds.AddPoint( idVec3( scale1 * dFar, -1.0f, -1.0f ) );
2285 projectionBounds.AddPoint( idVec3( scale2 * dFar, -1.0f, -1.0f ) );
2288 if ( (outside & 2) && (outside & 4) ) {
2289 frustum.LocalRayIntersection( localOrigin, localScaled[0] - localScaled[1] + localScaled[2], scale1, scale2 );
2290 if ( scale1 <= scale2 && scale1 >= 0.0f ) {
2291 projectionBounds.AddPoint( idVec3( scale1 * dFar, -1.0f, 1.0f ) );
2292 projectionBounds.AddPoint( idVec3( scale2 * dFar, -1.0f, 1.0f ) );
2295 if ( (outside & 1) && (outside & 8) ) {
2296 frustum.LocalRayIntersection( localOrigin, localScaled[0] + localScaled[1] - localScaled[2], scale1, scale2 );
2297 if ( scale1 <= scale2 && scale1 >= 0.0f ) {
2298 projectionBounds.AddPoint( idVec3( scale1 * dFar, 1.0f, -1.0f ) );
2299 projectionBounds.AddPoint( idVec3( scale2 * dFar, 1.0f, -1.0f ) );
2302 if ( (outside & 1) && (outside & 2) ) {
2303 frustum.LocalRayIntersection( localOrigin, localScaled[0] + localScaled[1] + localScaled[2], scale1, scale2 );
2304 if ( scale1 <= scale2 && scale1 >= 0.0f ) {
2305 projectionBounds.AddPoint( idVec3( scale1 * dFar, 1.0f, 1.0f ) );
2306 projectionBounds.AddPoint( idVec3( scale2 * dFar, 1.0f, 1.0f ) );
2316 idFrustum::ProjectionBounds
2319 bool idFrustum::ProjectionBounds( const idWinding &winding, idBounds &projectionBounds ) const {
2320 int i, p1, p2, *pointCull, culled, outside;
2322 idVec3 *localPoints;
2323 idMat3 transpose, scaled;
2326 projectionBounds.Clear();
2328 // transform the winding points into the space of this frustum
2329 localPoints = (idVec3 *) _alloca16( winding.GetNumPoints() * sizeof( idVec3 ) );
2330 transpose = axis.Transpose();
2331 for ( i = 0; i < winding.GetNumPoints(); i++ ) {
2332 localPoints[i] = ( winding[i].ToVec3() - origin ) * transpose;
2335 // test the winding edges
2338 pointCull = (int *) _alloca16( winding.GetNumPoints() * sizeof( int ) );
2339 for ( i = 0; i < winding.GetNumPoints(); i += 2 ) {
2341 p2 = (i+1)%winding.GetNumPoints();
2342 AddLocalLineToProjectionBoundsSetCull( localPoints[p1], localPoints[p2], pointCull[p1], pointCull[p2], projectionBounds );
2343 culled &= pointCull[p1] & pointCull[p2];
2344 outside |= pointCull[p1] | pointCull[p2];
2347 // if completely culled
2352 // if completely inside
2357 // test remaining winding edges
2358 for ( i = 1; i < winding.GetNumPoints(); i += 2 ) {
2360 p2 = (i+1)%winding.GetNumPoints();
2361 AddLocalLineToProjectionBoundsUseCull( localPoints[p1], localPoints[p2], pointCull[p1], pointCull[p2], projectionBounds );
2364 // if the winding extends beyond two or more boundaries of this frustum
2365 if ( outside != 1 && outside != 2 && outside != 4 && outside != 8 ) {
2367 winding.GetPlane( plane );
2368 scaled[0] = axis[0] * dFar;
2369 scaled[1] = axis[1] * dLeft;
2370 scaled[2] = axis[2] * dUp;
2372 // test the outer edges of this frustum for intersection with the winding
2373 if ( (outside & 2) && (outside & 8) ) {
2374 if ( winding.RayIntersection( plane, origin, scaled[0] - scaled[1] - scaled[2], scale ) ) {
2375 projectionBounds.AddPoint( idVec3( scale * dFar, -1.0f, -1.0f ) );
2378 if ( (outside & 2) && (outside & 4) ) {
2379 if ( winding.RayIntersection( plane, origin, scaled[0] - scaled[1] + scaled[2], scale ) ) {
2380 projectionBounds.AddPoint( idVec3( scale * dFar, -1.0f, 1.0f ) );
2383 if ( (outside & 1) && (outside & 8) ) {
2384 if ( winding.RayIntersection( plane, origin, scaled[0] + scaled[1] - scaled[2], scale ) ) {
2385 projectionBounds.AddPoint( idVec3( scale * dFar, 1.0f, -1.0f ) );
2388 if ( (outside & 1) && (outside & 2) ) {
2389 if ( winding.RayIntersection( plane, origin, scaled[0] + scaled[1] + scaled[2], scale ) ) {
2390 projectionBounds.AddPoint( idVec3( scale * dFar, 1.0f, 1.0f ) );
2400 idFrustum::ClipFrustumToBox
2402 Clips the frustum far extents to the box.
2405 void idFrustum::ClipFrustumToBox( const idBox &box, float clipFractions[4], int clipPlanes[4] ) const {
2408 idMat3 scaled, localAxis, transpose;
2409 idVec3 localOrigin, cornerVecs[4];
2412 transpose = box.GetAxis();
2413 transpose.TransposeSelf();
2414 localOrigin = ( origin - box.GetCenter() ) * transpose;
2415 localAxis = axis * transpose;
2417 scaled[0] = localAxis[0] * dFar;
2418 scaled[1] = localAxis[1] * dLeft;
2419 scaled[2] = localAxis[2] * dUp;
2420 cornerVecs[0] = scaled[0] + scaled[1];
2421 cornerVecs[1] = scaled[0] - scaled[1];
2422 cornerVecs[2] = cornerVecs[1] - scaled[2];
2423 cornerVecs[3] = cornerVecs[0] - scaled[2];
2424 cornerVecs[0] += scaled[2];
2425 cornerVecs[1] += scaled[2];
2427 bounds[0] = -box.GetExtents();
2428 bounds[1] = box.GetExtents();
2430 minf = ( dNear + 1.0f ) * invFar;
2432 for ( i = 0; i < 4; i++ ) {
2434 index = FLOATSIGNBITNOTSET( cornerVecs[i].x );
2435 f = ( bounds[index].x - localOrigin.x ) / cornerVecs[i].x;
2436 clipFractions[i] = f;
2437 clipPlanes[i] = 1 << index;
2439 index = FLOATSIGNBITNOTSET( cornerVecs[i].y );
2440 f = ( bounds[index].y - localOrigin.y ) / cornerVecs[i].y;
2441 if ( f < clipFractions[i] ) {
2442 clipFractions[i] = f;
2443 clipPlanes[i] = 4 << index;
2446 index = FLOATSIGNBITNOTSET( cornerVecs[i].z );
2447 f = ( bounds[index].z - localOrigin.z ) / cornerVecs[i].z;
2448 if ( f < clipFractions[i] ) {
2449 clipFractions[i] = f;
2450 clipPlanes[i] = 16 << index;
2453 // make sure the frustum is not clipped between the frustum origin and the near plane
2454 if ( clipFractions[i] < minf ) {
2455 clipFractions[i] = minf;
2464 Returns true if part of the line is inside the frustum.
2465 Does not clip to the near and far plane.
2468 bool idFrustum::ClipLine( const idVec3 localPoints[8], const idVec3 points[8], int startIndex, int endIndex, idVec3 &start, idVec3 &end, int &startClip, int &endClip ) const {
2469 float d1, d2, fstart, fend, lstart, lend, f, x;
2470 float leftScale, upScale;
2471 float scale1, scale2;
2472 int startCull, endCull;
2473 idVec3 localStart, localEnd, localDir;
2475 leftScale = dLeft * invFar;
2476 upScale = dUp * invFar;
2478 localStart = localPoints[startIndex];
2479 localEnd = localPoints[endIndex];
2480 localDir = localEnd - localStart;
2482 startClip = endClip = -1;
2483 scale1 = idMath::INFINITY;
2484 scale2 = -idMath::INFINITY;
2486 fstart = dFar * localStart.y;
2487 fend = dFar * localEnd.y;
2488 lstart = dLeft * localStart.x;
2489 lend = dLeft * localEnd.x;
2492 d1 = -fstart + lstart;
2494 startCull = FLOATSIGNBITSET( d1 );
2495 endCull = FLOATSIGNBITSET( d2 );
2496 if ( FLOATNOTZERO( d1 ) ) {
2497 if ( FLOATSIGNBITSET( d1 ) ^ FLOATSIGNBITSET( d2 ) ) {
2498 f = d1 / ( d1 - d2 );
2499 x = localStart.x + f * localDir.x;
2501 if ( idMath::Fabs( localStart.z + f * localDir.z ) <= x * upScale ) {
2502 if ( f < scale1 ) { scale1 = f; startClip = 0; }
2503 if ( f > scale2 ) { scale2 = f; endClip = 0; }
2510 d1 = fstart + lstart;
2512 startCull |= FLOATSIGNBITSET( d1 ) << 1;
2513 endCull |= FLOATSIGNBITSET( d2 ) << 1;
2514 if ( FLOATNOTZERO( d1 ) ) {
2515 if ( FLOATSIGNBITSET( d1 ) ^ FLOATSIGNBITSET( d2 ) ) {
2516 f = d1 / ( d1 - d2 );
2517 x = localStart.x + f * localDir.x;
2519 if ( idMath::Fabs( localStart.z + f * localDir.z ) <= x * upScale ) {
2520 if ( f < scale1 ) { scale1 = f; startClip = 1; }
2521 if ( f > scale2 ) { scale2 = f; endClip = 1; }
2527 fstart = dFar * localStart.z;
2528 fend = dFar * localEnd.z;
2529 lstart = dUp * localStart.x;
2530 lend = dUp * localEnd.x;
2533 d1 = -fstart + lstart;
2535 startCull |= FLOATSIGNBITSET( d1 ) << 2;
2536 endCull |= FLOATSIGNBITSET( d2 ) << 2;
2537 if ( FLOATNOTZERO( d1 ) ) {
2538 if ( FLOATSIGNBITSET( d1 ) ^ FLOATSIGNBITSET( d2 ) ) {
2539 f = d1 / ( d1 - d2 );
2540 x = localStart.x + f * localDir.x;
2542 if ( idMath::Fabs( localStart.y + f * localDir.y ) <= x * leftScale ) {
2543 if ( f < scale1 ) { scale1 = f; startClip = 2; }
2544 if ( f > scale2 ) { scale2 = f; endClip = 2; }
2551 d1 = fstart + lstart;
2553 startCull |= FLOATSIGNBITSET( d1 ) << 3;
2554 endCull |= FLOATSIGNBITSET( d2 ) << 3;
2555 if ( FLOATNOTZERO( d1 ) ) {
2556 if ( FLOATSIGNBITSET( d1 ) ^ FLOATSIGNBITSET( d2 ) ) {
2557 f = d1 / ( d1 - d2 );
2558 x = localStart.x + f * localDir.x;
2560 if ( idMath::Fabs( localStart.y + f * localDir.y ) <= x * leftScale ) {
2561 if ( f < scale1 ) { scale1 = f; startClip = 3; }
2562 if ( f > scale2 ) { scale2 = f; endClip = 3; }
2568 // if completely inside
2569 if ( !( startCull | endCull ) ) {
2570 start = points[startIndex];
2571 end = points[endIndex];
2574 else if ( scale1 <= scale2 ) {
2576 start = points[startIndex];
2580 start = points[startIndex] + scale1 * ( points[endIndex] - points[startIndex] );
2583 end = points[endIndex];
2587 end = points[startIndex] + scale2 * ( points[endIndex] - points[startIndex] );
2596 idFrustum::AddLocalCapsToProjectionBounds
2599 static int capPointIndex[4][2] = {
2606 ID_INLINE bool idFrustum::AddLocalCapsToProjectionBounds( const idVec3 endPoints[4], const int endPointCull[4], const idVec3 &point, int pointCull, int pointClip, idBounds &projectionBounds ) const {
2609 if ( pointClip < 0 ) {
2612 p = capPointIndex[pointClip];
2613 AddLocalLineToProjectionBoundsUseCull( endPoints[p[0]], point, endPointCull[p[0]], pointCull, projectionBounds );
2614 AddLocalLineToProjectionBoundsUseCull( endPoints[p[1]], point, endPointCull[p[1]], pointCull, projectionBounds );
2620 idFrustum::ClippedProjectionBounds
2623 bool idFrustum::ClippedProjectionBounds( const idFrustum &frustum, const idBox &clipBox, idBounds &projectionBounds ) const {
2624 int i, p1, p2, clipPointCull[8], clipPlanes[4], usedClipPlanes, nearCull, farCull, outside;
2625 int pointCull[2], startClip, endClip, boxPointCull[8];
2626 float clipFractions[4], s1, s2, t1, t2, leftScale, upScale;
2627 idFrustum localFrustum;
2628 idVec3 clipPoints[8], localPoints1[8], localPoints2[8], localOrigin1, localOrigin2, start, end;
2629 idMat3 localAxis1, localAxis2, transpose;
2630 idBounds clipBounds;
2632 // if the frustum origin is inside the other frustum
2633 if ( frustum.ContainsPoint( origin ) ) {
2634 // bounds that cover the whole frustum
2635 float clipBoxMin, clipBoxMax, frustumMin, frustumMax, base;
2637 base = origin * axis[0];
2638 clipBox.AxisProjection( axis[0], clipBoxMin, clipBoxMax );
2639 frustum.AxisProjection( axis[0], frustumMin, frustumMax );
2641 projectionBounds[0].x = Max( clipBoxMin, frustumMin ) - base;
2642 projectionBounds[1].x = Min( clipBoxMax, frustumMax ) - base;
2643 projectionBounds[0].y = projectionBounds[0].z = -1.0f;
2644 projectionBounds[1].y = projectionBounds[1].z = 1.0f;
2648 projectionBounds.Clear();
2650 // clip the outer edges of the given frustum to the clip bounds
2651 frustum.ClipFrustumToBox( clipBox, clipFractions, clipPlanes );
2652 usedClipPlanes = clipPlanes[0] | clipPlanes[1] | clipPlanes[2] | clipPlanes[3];
2654 // transform the clipped frustum to the space of this frustum
2656 transpose.TransposeSelf();
2657 localFrustum = frustum;
2658 localFrustum.origin = ( frustum.origin - origin ) * transpose;
2659 localFrustum.axis = frustum.axis * transpose;
2660 localFrustum.ToClippedPoints( clipFractions, clipPoints );
2662 // test outer four edges of the clipped frustum
2663 for ( i = 0; i < 4; i++ ) {
2666 AddLocalLineToProjectionBoundsSetCull( clipPoints[p1], clipPoints[p2], clipPointCull[p1], clipPointCull[p2], projectionBounds );
2669 // get cull bits for the clipped frustum
2670 outside = clipPointCull[0] | clipPointCull[1] | clipPointCull[2] | clipPointCull[3] |
2671 clipPointCull[4] | clipPointCull[5] | clipPointCull[6] | clipPointCull[7];
2672 nearCull = clipPointCull[0] & clipPointCull[1] & clipPointCull[2] & clipPointCull[3];
2673 farCull = clipPointCull[4] & clipPointCull[5] & clipPointCull[6] & clipPointCull[7];
2675 // if the clipped frustum is not completely inside this frustum
2678 // test the remaining edges of the clipped frustum
2679 if ( !nearCull && localFrustum.dNear > 0.0f ) {
2680 for ( i = 0; i < 4; i++ ) {
2683 AddLocalLineToProjectionBoundsUseCull( clipPoints[p1], clipPoints[p2], clipPointCull[p1], clipPointCull[p2], projectionBounds );
2688 for ( i = 0; i < 4; i++ ) {
2691 AddLocalLineToProjectionBoundsUseCull( clipPoints[p1], clipPoints[p2], clipPointCull[p1], clipPointCull[p2], projectionBounds );
2696 // if the clipped frustum far end points are inside this frustum
2697 if ( !( farCull && !( nearCull & farCull ) ) &&
2698 // if the clipped frustum is not clipped to a single plane of the clip bounds
2699 ( clipPlanes[0] != clipPlanes[1] || clipPlanes[1] != clipPlanes[2] || clipPlanes[2] != clipPlanes[3] ) ) {
2701 // transform the clip box into the space of the other frustum
2702 transpose = frustum.axis;
2703 transpose.TransposeSelf();
2704 localOrigin1 = ( clipBox.GetCenter() - frustum.origin ) * transpose;
2705 localAxis1 = clipBox.GetAxis() * transpose;
2706 BoxToPoints( localOrigin1, clipBox.GetExtents(), localAxis1, localPoints1 );
2708 // cull the box corners with the other frustum
2709 leftScale = frustum.dLeft * frustum.invFar;
2710 upScale = frustum.dUp * frustum.invFar;
2711 for ( i = 0; i < 8; i++ ) {
2712 idVec3 &p = localPoints1[i];
2713 if ( !( boxVertPlanes[i] & usedClipPlanes ) || p.x <= 0.0f ) {
2714 boxPointCull[i] = 1|2|4|8;
2717 boxPointCull[i] = 0;
2718 if ( idMath::Fabs( p.y ) > p.x * leftScale ) {
2719 boxPointCull[i] |= 1 << FLOATSIGNBITSET( p.y );
2721 if ( idMath::Fabs( p.z ) > p.x * upScale ) {
2722 boxPointCull[i] |= 4 << FLOATSIGNBITSET( p.z );
2727 // transform the clip box into the space of this frustum
2729 transpose.TransposeSelf();
2730 localOrigin2 = ( clipBox.GetCenter() - origin ) * transpose;
2731 localAxis2 = clipBox.GetAxis() * transpose;
2732 BoxToPoints( localOrigin2, clipBox.GetExtents(), localAxis2, localPoints2 );
2734 // clip the edges of the clip bounds to the other frustum and add the clipped edges to the projection bounds
2735 for ( i = 0; i < 4; i++ ) {
2738 if ( !( boxPointCull[p1] & boxPointCull[p2] ) ) {
2739 if ( frustum.ClipLine( localPoints1, localPoints2, p1, p2, start, end, startClip, endClip ) ) {
2740 AddLocalLineToProjectionBoundsSetCull( start, end, pointCull[0], pointCull[1], projectionBounds );
2741 AddLocalCapsToProjectionBounds( clipPoints+4, clipPointCull+4, start, pointCull[0], startClip, projectionBounds );
2742 AddLocalCapsToProjectionBounds( clipPoints+4, clipPointCull+4, end, pointCull[1], endClip, projectionBounds );
2743 outside |= pointCull[0] | pointCull[1];
2748 for ( i = 0; i < 4; i++ ) {
2751 if ( !( boxPointCull[p1] & boxPointCull[p2] ) ) {
2752 if ( frustum.ClipLine( localPoints1, localPoints2, p1, p2, start, end, startClip, endClip ) ) {
2753 AddLocalLineToProjectionBoundsSetCull( start, end, pointCull[0], pointCull[1], projectionBounds );
2754 AddLocalCapsToProjectionBounds( clipPoints+4, clipPointCull+4, start, pointCull[0], startClip, projectionBounds );
2755 AddLocalCapsToProjectionBounds( clipPoints+4, clipPointCull+4, end, pointCull[1], endClip, projectionBounds );
2756 outside |= pointCull[0] | pointCull[1];
2761 for ( i = 0; i < 4; i++ ) {
2764 if ( !( boxPointCull[p1] & boxPointCull[p2] ) ) {
2765 if ( frustum.ClipLine( localPoints1, localPoints2, p1, p2, start, end, startClip, endClip ) ) {
2766 AddLocalLineToProjectionBoundsSetCull( start, end, pointCull[0], pointCull[1], projectionBounds );
2767 AddLocalCapsToProjectionBounds( clipPoints+4, clipPointCull+4, start, pointCull[0], startClip, projectionBounds );
2768 AddLocalCapsToProjectionBounds( clipPoints+4, clipPointCull+4, end, pointCull[1], endClip, projectionBounds );
2769 outside |= pointCull[0] | pointCull[1];
2775 // if the clipped frustum extends beyond two or more boundaries of this frustum
2776 if ( outside != 1 && outside != 2 && outside != 4 && outside != 8 ) {
2778 // transform this frustum into the space of the other frustum
2779 transpose = frustum.axis;
2780 transpose.TransposeSelf();
2781 localOrigin1 = ( origin - frustum.origin ) * transpose;
2782 localAxis1 = axis * transpose;
2783 localAxis1[0] *= dFar;
2784 localAxis1[1] *= dLeft;
2785 localAxis1[2] *= dUp;
2787 // transform this frustum into the space of the clip bounds
2788 transpose = clipBox.GetAxis();
2789 transpose.TransposeSelf();
2790 localOrigin2 = ( origin - clipBox.GetCenter() ) * transpose;
2791 localAxis2 = axis * transpose;
2792 localAxis2[0] *= dFar;
2793 localAxis2[1] *= dLeft;
2794 localAxis2[2] *= dUp;
2796 clipBounds[0] = -clipBox.GetExtents();
2797 clipBounds[1] = clipBox.GetExtents();
2799 // test the outer edges of this frustum for intersection with both the other frustum and the clip bounds
2800 if ( (outside & 2) && (outside & 8) ) {
2801 frustum.LocalRayIntersection( localOrigin1, localAxis1[0] - localAxis1[1] - localAxis1[2], s1, s2 );
2802 if ( s1 <= s2 && s1 >= 0.0f ) {
2803 BoundsRayIntersection( clipBounds, localOrigin2, localAxis2[0] - localAxis2[1] - localAxis2[2], t1, t2 );
2804 if ( t1 <= t2 && t2 > s1 && t1 < s2 ) {
2805 projectionBounds.AddPoint( idVec3( s1 * dFar, -1.0f, -1.0f ) );
2806 projectionBounds.AddPoint( idVec3( s2 * dFar, -1.0f, -1.0f ) );
2810 if ( (outside & 2) && (outside & 4) ) {
2811 frustum.LocalRayIntersection( localOrigin1, localAxis1[0] - localAxis1[1] + localAxis1[2], s1, s2 );
2812 if ( s1 <= s2 && s1 >= 0.0f ) {
2813 BoundsRayIntersection( clipBounds, localOrigin2, localAxis2[0] - localAxis2[1] + localAxis2[2], t1, t2 );
2814 if ( t1 <= t2 && t2 > s1 && t1 < s2 ) {
2815 projectionBounds.AddPoint( idVec3( s1 * dFar, -1.0f, 1.0f ) );
2816 projectionBounds.AddPoint( idVec3( s2 * dFar, -1.0f, 1.0f ) );
2820 if ( (outside & 1) && (outside & 8) ) {
2821 frustum.LocalRayIntersection( localOrigin1, localAxis1[0] + localAxis1[1] - localAxis1[2], s1, s2 );
2822 if ( s1 <= s2 && s1 >= 0.0f ) {
2823 BoundsRayIntersection( clipBounds, localOrigin2, localAxis2[0] + localAxis2[1] - localAxis2[2], t1, t2 );
2824 if ( t1 <= t2 && t2 > s1 && t1 < s2 ) {
2825 projectionBounds.AddPoint( idVec3( s1 * dFar, 1.0f, -1.0f ) );
2826 projectionBounds.AddPoint( idVec3( s2 * dFar, 1.0f, -1.0f ) );
2830 if ( (outside & 1) && (outside & 2) ) {
2831 frustum.LocalRayIntersection( localOrigin1, localAxis1[0] + localAxis1[1] + localAxis1[2], s1, s2 );
2832 if ( s1 <= s2 && s1 >= 0.0f ) {
2833 BoundsRayIntersection( clipBounds, localOrigin2, localAxis2[0] + localAxis2[1] + localAxis2[2], t1, t2 );
2834 if ( t1 <= t2 && t2 > s1 && t1 < s2 ) {
2835 projectionBounds.AddPoint( idVec3( s1 * dFar, 1.0f, 1.0f ) );
2836 projectionBounds.AddPoint( idVec3( s2 * dFar, 1.0f, 1.0f ) );