frustum.cpp

Engine/source/math/util/frustum.cpp

More...

Public Variables

sGFXProjRotMatrix (EulerF((M_PI_F/2.0f), 0.0f, 0.0f))

Detailed Description

Public Variables

const MatrixF sGFXProjRotMatrix (EulerF((M_PI_F/2.0f), 0.0f, 0.0f))
  1
  2//-----------------------------------------------------------------------------
  3// Copyright (c) 2012 GarageGames, LLC
  4//
  5// Permission is hereby granted, free of charge, to any person obtaining a copy
  6// of this software and associated documentation files (the "Software"), to
  7// deal in the Software without restriction, including without limitation the
  8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  9// sell copies of the Software, and to permit persons to whom the Software is
 10// furnished to do so, subject to the following conditions:
 11//
 12// The above copyright notice and this permission notice shall be included in
 13// all copies or substantial portions of the Software.
 14//
 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 21// IN THE SOFTWARE.
 22//-----------------------------------------------------------------------------
 23
 24#include "platform/platform.h"
 25#include "math/util/frustum.h"
 26
 27#include "math/mMathFn.h"
 28#include "math/mathUtils.h"
 29#include "math/mSphere.h"
 30#include "platform/profiler.h"
 31
 32static const MatrixF sGFXProjRotMatrix( EulerF( (M_PI_F / 2.0f), 0.0f, 0.0f ) );
 33
 34
 35//TODO: For OBB/frustum intersections and ortho frustums, we can resort to a much quicker AABB/OBB test
 36
 37
 38// Must be CW ordered for face[0] of each edge!  Keep in mind that normals
 39// are pointing *inwards* and thus what appears CCW outside is CW inside.
 40FrustumData::EdgeListType FrustumData::smEdges
 41(
 42   PolyhedronData::Edge( PlaneNear, PlaneTop, NearTopRight, NearTopLeft ),
 43   PolyhedronData::Edge( PlaneNear, PlaneBottom, NearBottomLeft, NearBottomRight ),
 44   PolyhedronData::Edge( PlaneNear, PlaneLeft, NearTopLeft, NearBottomLeft ),
 45   PolyhedronData::Edge( PlaneNear, PlaneRight, NearTopRight, NearBottomRight ),
 46   PolyhedronData::Edge( PlaneFar, PlaneTop, FarTopLeft, FarTopRight ),
 47   PolyhedronData::Edge( PlaneFar, PlaneBottom, FarBottomRight, FarBottomLeft ),
 48   PolyhedronData::Edge( PlaneFar, PlaneLeft, FarBottomLeft, FarTopLeft ),
 49   PolyhedronData::Edge( PlaneFar, PlaneRight, FarTopRight, FarBottomRight ),
 50   PolyhedronData::Edge( PlaneTop, PlaneLeft, FarTopLeft, NearTopLeft ),
 51   PolyhedronData::Edge( PlaneTop, PlaneRight, NearTopRight, FarTopRight ),
 52   PolyhedronData::Edge( PlaneBottom, PlaneLeft, NearBottomLeft, FarBottomLeft ),
 53   PolyhedronData::Edge( PlaneBottom, PlaneRight, FarBottomRight, NearBottomRight )
 54);
 55
 56
 57//-----------------------------------------------------------------------------
 58
 59Frustum::Frustum( bool isOrtho,
 60                  F32 nearLeft, 
 61                  F32 nearRight, 
 62                  F32 nearTop, 
 63                  F32 nearBottom, 
 64                  F32 nearDist,                  
 65                  F32 farDist,
 66                  const MatrixF &transform )
 67{
 68   mTransform = transform;
 69   mPosition = transform.getPosition();
 70
 71   mNearLeft = nearLeft;
 72   mNearRight = nearRight;
 73   mNearTop = nearTop;
 74   mNearBottom = nearBottom;
 75   mNearDist = nearDist;
 76   mFarDist = farDist;
 77   mIsOrtho = isOrtho;
 78
 79   mNumTiles = 1;
 80   mCurrTile.set(0,0);
 81   mTileOverlap.set(0.0f, 0.0f);
 82
 83   mProjectionOffset.zero();
 84   mProjectionOffsetMatrix.identity();
 85}
 86
 87//-----------------------------------------------------------------------------
 88
 89void Frustum::set(   bool isOrtho,
 90                     F32 fovYInRadians, 
 91                     F32 aspectRatio, 
 92                     F32 nearDist, 
 93                     F32 farDist,
 94                     const MatrixF &transform )
 95{
 96   F32 left, right, top, bottom;
 97   MathUtils::makeFrustum( &left, &right, &top, &bottom, fovYInRadians, aspectRatio, nearDist );
 98
 99   tile( &left, &right, &top, &bottom, mNumTiles, mCurrTile, mTileOverlap );
100   set( isOrtho, left, right, top, bottom, nearDist, farDist, transform );
101}
102
103//-----------------------------------------------------------------------------
104
105void Frustum::set(   bool isOrtho,
106                     F32 nearLeft, 
107                     F32 nearRight, 
108                     F32 nearTop, 
109                     F32 nearBottom, 
110                     F32 nearDist, 
111                     F32 farDist,
112                     const MatrixF &transform )
113{
114   mTransform = transform;
115   mPosition = mTransform.getPosition();
116
117   mNearLeft = nearLeft;
118   mNearRight = nearRight;
119   mNearTop = nearTop;
120   mNearBottom = nearBottom;
121   mNearDist = nearDist;
122   mFarDist = farDist;
123   mIsOrtho = isOrtho;
124
125   mDirty = true;
126}
127
128//-----------------------------------------------------------------------------
129
130#if 0
131void MatrixF &projMat, bool normalize )
132{ 
133   // From "Fast Extraction of Viewing Frustum Planes from the World-View-Projection Matrix"
134   // by Gil Gribb and Klaus Hartmann.
135   //
136   // http://www2.ravensoft.com/users/ggribb/plane%20extraction.pdf
137
138   // Right clipping plane.
139   PlaneRight ].set(  projMat[3] - projMat[0], 
140                                             projMat[7] - projMat[4], 
141                                             projMat[11] - projMat[8],
142                                             projMat[15] - projMat[12] );
143
144   // Left clipping plane.
145   PlaneLeft ].set(   projMat[3] + projMat[0], 
146                                             projMat[7] + projMat[4], 
147                                             projMat[11] + projMat[8], 
148                                             projMat[15] + projMat[12] );
149
150   // Bottom clipping plane.
151   PlaneBottom ].set( projMat[3] + projMat[1], 
152                                             projMat[7] + projMat[5], 
153                                             projMat[11] + projMat[9], 
154                                             projMat[15] + projMat[13] );
155
156   // Top clipping plane.
157   PlaneTop ].set(    projMat[3] - projMat[1], 
158                                             projMat[7] - projMat[5], 
159                                             projMat[11] - projMat[9], 
160                                             projMat[15] - projMat[13] );
161
162   // Near clipping plane
163   PlaneNear ].set(   projMat[3] + projMat[2], 
164                                             projMat[7] + projMat[6], 
165                                             projMat[11] + projMat[10],
166                                             projMat[15] + projMat[14] );
167
168   // Far clipping plane.
169   PlaneFar ].set(    projMat[3] - projMat[2], 
170                                             projMat[7] - projMat[6], 
171                                             projMat[11] - projMat[10], 
172                                             projMat[15] - projMat[14] );
173
174   if( normalize )
175   {
176      for( i )
177         i ].normalize();
178   }
179
180   /*// Create the corner points via plane intersections.
181   mPlanes[ PlaneNear ].intersect( mPlanes[ PlaneTop ], mPlanes[ PlaneLeft ], &mPoints[ NearTopLeft ] );
182   mPlanes[ PlaneNear ].intersect( mPlanes[ PlaneTop ], mPlanes[ PlaneRight ], &mPoints[ NearTopRight ] );
183   mPlanes[ PlaneNear ].intersect( mPlanes[ PlaneBottom ], mPlanes[ PlaneLeft ], &mPoints[ NearBottomLeft ] );
184   mPlanes[ PlaneNear ].intersect( mPlanes[ PlaneBottom ], mPlanes[ PlaneRight ], &mPoints[ NearBottomRight ] );
185   mPlanes[ PlaneFar ].intersect( mPlanes[ PlaneTop ], mPlanes[ PlaneLeft ], &mPoints[ FarTopLeft ] );
186   mPlanes[ PlaneFar ].intersect( mPlanes[ PlaneTop ], mPlanes[ PlaneRight ], &mPoints[ FarTopRight ] );
187   mPlanes[ PlaneFar ].intersect( mPlanes[ PlaneBottom ], mPlanes[ PlaneLeft ], &mPoints[ FarBottomLeft ] );
188   mPlanes[ PlaneFar ].intersect( mPlanes[ PlaneBottom ], mPlanes[ PlaneRight ], &mPoints[ FarBottomRight ] );
189   */
190
191   // Update the axis aligned bounding box.
192   _updateBounds();
193}
194#endif
195
196//-----------------------------------------------------------------------------
197
198void Frustum::setNearDist( F32 nearDist )
199{
200   setNearFarDist( nearDist, mFarDist );
201}
202
203//-----------------------------------------------------------------------------
204
205void Frustum::setFarDist( F32 farDist )
206{
207   setNearFarDist( mNearDist, farDist );
208}
209
210//-----------------------------------------------------------------------------
211
212void Frustum::setNearFarDist( F32 nearDist, F32 farDist )
213{
214   if( mNearDist == nearDist && mFarDist == farDist )
215      return;
216
217   // Recalculate the frustum.
218   MatrixF xfm( mTransform );
219
220   const F32 CENTER_EPSILON = 0.001f;
221   F32 centerX = mNearLeft + (mNearRight - mNearLeft) * 0.5;
222   F32 centerY = mNearBottom + (mNearTop - mNearBottom) * 0.5;
223   if ((centerX > CENTER_EPSILON || centerX < -CENTER_EPSILON) || (centerY > CENTER_EPSILON || centerY < -CENTER_EPSILON) )
224   {
225      // Off-center projection, so re-calc use the new distances
226      FovPort expectedFovPort;
227      expectedFovPort.leftTan = -(mNearLeft / mNearDist);
228      expectedFovPort.rightTan = (mNearRight / mNearDist);
229      expectedFovPort.upTan = (mNearTop / mNearDist);
230      expectedFovPort.downTan = -(mNearBottom / mNearDist);
231      MathUtils::makeFovPortFrustum(this, mIsOrtho, nearDist, farDist, expectedFovPort);
232   }
233   else
234   {
235      // Projection is not off-center, use the normal code
236      set(mIsOrtho, getFov(), getAspectRatio(), nearDist, farDist, xfm);
237   }
238}
239
240//-----------------------------------------------------------------------------
241
242void Frustum::cropNearFar(F32 newNearDist, F32 newFarDist)
243{
244   const F32 newOverOld = newNearDist / mNearDist;
245
246   set( mIsOrtho, mNearLeft * newOverOld, mNearRight * newOverOld, mNearTop * newOverOld, mNearBottom * newOverOld, 
247      newNearDist, newFarDist, mTransform);
248}
249
250//-----------------------------------------------------------------------------
251
252bool Frustum::bakeProjectionOffset()
253{
254   // Nothing to bake if ortho
255   if( mIsOrtho )
256      return false;
257
258   // Nothing to bake if no offset
259   if( mProjectionOffset.isZero() )
260      return false;
261
262   // Near plane points in camera space
263   Point3F np[4];
264   np[0].set( mNearLeft, mNearDist, mNearTop );       // NearTopLeft
265   np[1].set( mNearRight, mNearDist, mNearTop );      // NearTopRight
266   np[2].set( mNearLeft, mNearDist, mNearBottom );    // NearBottomLeft
267   np[3].set( mNearRight, mNearDist, mNearBottom );   // NearBottomRight
268
269   // Generate the near plane
270   PlaneF nearPlane( np[0], np[1], np[3] );
271
272   // Far plane points in camera space
273   const F32 farOverNear = mFarDist / mNearDist;
274   Point3F fp0( mNearLeft * farOverNear, mFarDist, mNearTop * farOverNear );     // FarTopLeft
275   Point3F fp1( mNearRight * farOverNear, mFarDist, mNearTop * farOverNear );    // FarTopRight
276   Point3F fp2( mNearLeft * farOverNear, mFarDist, mNearBottom * farOverNear );  // FarBottomLeft
277   Point3F fp3( mNearRight * farOverNear, mFarDist, mNearBottom * farOverNear ); // FarBottomRight
278
279   // Generate the far plane
280   PlaneF farPlane( fp0, fp1, fp3 );
281
282   // The offset camera point
283   Point3F offsetCamera( mProjectionOffset.x, 0.0f, mProjectionOffset.y );
284
285   // The near plane point we'll be using for our calculations below
286   U32 nIndex = 0;
287   if( mProjectionOffset.x < 0.0 )
288   {
289      // Offset to the left so we'll need to use the near plane point on the right
290      nIndex = 1;
291   }
292   if( mProjectionOffset.y > 0.0 )
293   {
294      // Offset to the top so we'll need to use the near plane point at the bottom
295      nIndex += 2;
296   }
297
298   // Begin by calculating the offset point on the far plane as it goes
299   // from the offset camera to the edge of the near plane.
300   Point3F farPoint;
301   Point3F fdir = np[nIndex] - offsetCamera;
302   fdir.normalize();
303   if( farPlane.intersect(offsetCamera, fdir, &farPoint) )
304   {
305      // Calculate the new near plane edge from the non-offset camera position
306      // to the far plane point from above.
307      Point3F nearPoint;
308      Point3F ndir = farPoint;
309      ndir.normalize();
310      if( nearPlane.intersect( Point3F::Zero, ndir, &nearPoint) )
311      {
312         // Handle a x offset
313         if( mProjectionOffset.x < 0.0 )
314         {
315            // The new near plane right side
316            mNearRight = nearPoint.x;
317         }
318         else if( mProjectionOffset.x > 0.0 )
319         {
320            // The new near plane left side
321            mNearLeft = nearPoint.x;
322         }
323
324         // Handle a y offset
325         if( mProjectionOffset.y < 0.0 )
326         {
327            // The new near plane top side
328            mNearTop = nearPoint.y;
329         }
330         else if( mProjectionOffset.y > 0.0 )
331         {
332            // The new near plane bottom side
333            mNearBottom = nearPoint.y;
334         }
335      }
336   }
337
338   mDirty = true;
339
340   // Indicate that we've modified the frustum
341   return true;
342}
343
344//-----------------------------------------------------------------------------
345
346void FrustumData::_update() const
347{
348   if( !mDirty )
349      return;
350
351   PROFILE_SCOPE( Frustum_update );
352
353   const Point3F& cameraPos = mPosition;
354
355   // Build the frustum points in camera space first.
356
357   if( mIsOrtho )
358   {
359      mPoints[ NearTopLeft ].set( mNearLeft, mNearDist, mNearTop );
360      mPoints[ NearTopRight ].set( mNearRight, mNearDist, mNearTop );
361      mPoints[ NearBottomLeft ].set( mNearLeft, mNearDist, mNearBottom );
362      mPoints[ NearBottomRight ].set( mNearRight, mNearDist, mNearBottom );
363      mPoints[ FarTopLeft ].set( mNearLeft, mFarDist, mNearTop );
364      mPoints[ FarTopRight ].set( mNearRight, mFarDist, mNearTop );
365      mPoints[ FarBottomLeft ].set( mNearLeft, mFarDist, mNearBottom );
366      mPoints[ FarBottomRight ].set( mNearRight, mFarDist, mNearBottom );
367   }
368   else
369   {
370      const F32 farOverNear = mFarDist / mNearDist;
371
372      mPoints[ NearTopLeft ].set( mNearLeft, mNearDist, mNearTop );
373      mPoints[ NearTopRight ].set( mNearRight, mNearDist, mNearTop );
374      mPoints[ NearBottomLeft ].set( mNearLeft, mNearDist, mNearBottom );
375      mPoints[ NearBottomRight ].set( mNearRight, mNearDist, mNearBottom );
376      mPoints[ FarTopLeft ].set( mNearLeft * farOverNear, mFarDist, mNearTop * farOverNear );
377      mPoints[ FarTopRight ].set( mNearRight * farOverNear, mFarDist, mNearTop * farOverNear );
378      mPoints[ FarBottomLeft ].set( mNearLeft * farOverNear, mFarDist, mNearBottom * farOverNear );
379      mPoints[ FarBottomRight ].set( mNearRight * farOverNear, mFarDist, mNearBottom * farOverNear );
380   }
381
382   // Transform the points into the desired culling space.
383
384   for( U32 i = 0; i < mPoints.size(); ++ i )
385      mTransform.mulP( mPoints[ i ] );
386
387   // Update the axis aligned bounding box from 
388   // the newly transformed points.
389
390   mBounds = Box3F::aroundPoints( mPoints.address(), mPoints.size() );
391
392   // Finally build the planes.
393
394   if( mIsOrtho )
395   {
396      mPlanes[ PlaneLeft ].set(   mPoints[ NearBottomLeft ], 
397                                  mPoints[ FarTopLeft ], 
398                                  mPoints[ FarBottomLeft ] );
399
400      mPlanes[ PlaneRight ].set(  mPoints[ NearTopRight ], 
401                                  mPoints[ FarBottomRight ], 
402                                  mPoints[ FarTopRight ] );
403
404      mPlanes[ PlaneTop ].set(    mPoints[ FarTopRight ], 
405                                  mPoints[ NearTopLeft ], 
406                                  mPoints[ NearTopRight ] );
407
408      mPlanes[ PlaneBottom ].set( mPoints[ NearBottomRight ], 
409                                  mPoints[ FarBottomLeft ], 
410                                  mPoints[ FarBottomRight ] );
411
412      mPlanes[ PlaneNear ].set(   mPoints[ NearTopLeft ], 
413                                  mPoints[ NearBottomLeft ], 
414                                  mPoints[ NearTopRight ] );
415
416      mPlanes[ PlaneFar ].set(    mPoints[ FarTopLeft ], 
417                                  mPoints[ FarTopRight ], 
418                                  mPoints[ FarBottomLeft ] );
419   }
420   else
421   {
422      mPlanes[ PlaneLeft ].set(   cameraPos,
423                                  mPoints[ NearTopLeft ], 
424                                  mPoints[ NearBottomLeft ] );
425
426      mPlanes[ PlaneRight ].set(  cameraPos,
427                                  mPoints[ NearBottomRight ], 
428                                  mPoints[ NearTopRight ] );
429
430      mPlanes[ PlaneTop ].set(    cameraPos,
431                                  mPoints[ NearTopRight ], 
432                                  mPoints[ NearTopLeft ] );
433
434      mPlanes[ PlaneBottom ].set( cameraPos,
435                                  mPoints[ NearBottomLeft ], 
436                                  mPoints[ NearBottomRight ] );
437
438      mPlanes[ PlaneNear ].set(   mPoints[ NearTopLeft ],
439                                  mPoints[ NearBottomLeft ], 
440                                  mPoints[ NearTopRight ] );
441
442      mPlanes[ PlaneFar ].set(    mPoints[ FarTopLeft ],
443                                  mPoints[ FarTopRight ], 
444                                  mPoints[ FarBottomLeft ] );
445   }
446
447   // If the frustum plane orientation doesn't match mIsInverted
448   // now, invert all the plane normals.
449   //
450   // Note that if we have a transform matrix with a negative scale,
451   // then the initial planes we have computed will always be inverted.
452
453   const bool inverted = mPlanes[ PlaneNear ].whichSide( cameraPos ) == PlaneF::Front;
454   if( inverted != mIsInverted )
455   {
456      for( U32 i = 0; i < mPlanes.size(); ++ i )
457         mPlanes[ i ].invert();
458   }
459
460   AssertFatal( mPlanes[ PlaneNear ].whichSide( cameraPos ) != PlaneF::Front,
461      "Frustum::_update - Viewpoint lies on front side of near plane!" );
462
463   // And now the center points which are mostly just used in debug rendering.
464
465   mPlaneCenters[ PlaneLeftCenter ] = (   mPoints[ NearTopLeft ] + 
466                                          mPoints[ NearBottomLeft ] + 
467                                          mPoints[ FarTopLeft ] + 
468                                          mPoints[ FarBottomLeft ] ) / 4.0f;
469
470   mPlaneCenters[ PlaneRightCenter ] = (  mPoints[ NearTopRight ] + 
471                                          mPoints[ NearBottomRight ] + 
472                                          mPoints[ FarTopRight ] + 
473                                          mPoints[ FarBottomRight ] ) / 4.0f;
474
475   mPlaneCenters[ PlaneTopCenter ] = ( mPoints[ NearTopLeft ] + 
476                                       mPoints[ NearTopRight ] + 
477                                       mPoints[ FarTopLeft ] + 
478                                       mPoints[ FarTopRight ] ) / 4.0f;
479
480   mPlaneCenters[ PlaneBottomCenter ] = ( mPoints[ NearBottomLeft ] + 
481                                          mPoints[ NearBottomRight ] + 
482                                          mPoints[ FarBottomLeft ] + 
483                                          mPoints[ FarBottomRight ] ) / 4.0f;
484
485   mPlaneCenters[ PlaneNearCenter ] = (   mPoints[ NearTopLeft ] + 
486                                          mPoints[ NearTopRight ] + 
487                                          mPoints[ NearBottomLeft ] + 
488                                          mPoints[ NearBottomRight ] ) / 4.0f;
489
490   mPlaneCenters[ PlaneFarCenter ] = ( mPoints[ FarTopLeft ] + 
491                                       mPoints[ FarTopRight ] + 
492                                       mPoints[ FarBottomLeft ] + 
493                                       mPoints[ FarBottomRight ] ) / 4.0f;
494
495   // Done.
496
497   mDirty = false;
498}
499
500//-----------------------------------------------------------------------------
501
502void Frustum::invert()
503{
504   mIsInverted = !mIsInverted;
505   _update();
506}
507
508//-----------------------------------------------------------------------------
509
510void Frustum::setTransform( const MatrixF &mat )
511{
512   mTransform = mat;
513   mPosition = mTransform.getPosition();
514   mDirty = true;
515}
516
517//-----------------------------------------------------------------------------
518
519void Frustum::scaleFromCenter( F32 scale )
520{
521   // Extract the fov and aspect ratio.
522   F32 fovInRadians = mAtan2( (mNearTop - mNearBottom)*mNumTiles/2.0f, mNearDist ) * 2.0f;
523   F32 aspectRatio = (mNearRight - mNearLeft)/(mNearTop - mNearBottom);
524
525   // Now move the near and far planes out.
526   F32 halfDist = ( mFarDist - mNearDist ) / 2.0f;
527   mNearDist   -= halfDist * ( scale - 1.0f );
528   mFarDist    += halfDist * ( scale - 1.0f );
529
530   // Setup the new scaled frustum.
531   set( mIsOrtho, fovInRadians, aspectRatio, mNearDist, mFarDist, mTransform );
532}
533
534//-----------------------------------------------------------------------------
535
536void Frustum::mul( const MatrixF& mat )
537{
538   mTransform.mul( mat );
539   mDirty = true;
540}
541
542//-----------------------------------------------------------------------------
543
544void Frustum::mulL( const MatrixF& mat )
545{
546   MatrixF last( mTransform );
547   mTransform.mul( mat, last );
548
549   mDirty = true;
550}
551
552//-----------------------------------------------------------------------------
553
554void Frustum::setProjectionOffset(const Point2F& offsetMat)
555{
556   mProjectionOffset = offsetMat;
557   mProjectionOffsetMatrix.identity();
558   mProjectionOffsetMatrix.setPosition(Point3F(mProjectionOffset.x, mProjectionOffset.y, 0.0f));
559}
560
561//-----------------------------------------------------------------------------
562
563void Frustum::getProjectionMatrix( MatrixF *proj, bool gfxRotate ) const
564{
565   if (mIsOrtho)
566   {
567      MathUtils::makeOrthoProjection(proj, mNearLeft, mNearRight, mNearTop, mNearBottom, mNearDist, mFarDist, gfxRotate);
568      proj->mulL(mProjectionOffsetMatrix);
569   }
570   else
571   {
572      MathUtils::makeProjection(proj, mNearLeft, mNearRight, mNearTop, mNearBottom, mNearDist, mFarDist, gfxRotate);
573      proj->mulL(mProjectionOffsetMatrix);
574   }
575}
576
577//-----------------------------------------------------------------------------
578
579void Frustum::tileFrustum(U32 numTiles, const Point2I& curTile, Point2F overlap)
580{
581   //These will be stored to re-tile the frustum if needed
582   mNumTiles = numTiles; 
583   mCurrTile = curTile; 
584   mTileOverlap = overlap;
585   
586   tile(&mNearLeft, &mNearRight, &mNearTop, &mNearBottom, mNumTiles, mCurrTile, mTileOverlap);
587}
588
589//-----------------------------------------------------------------------------
590
591void Frustum::tile( F32 *left, F32 *right, F32 *top, F32 *bottom, U32 numTiles, const Point2I& curTile, Point2F overlap )
592{
593   if (numTiles == 1)
594      return;
595
596   Point2F tileSize( ( *right - *left ) / (F32)numTiles, 
597                     ( *top - *bottom ) / (F32)numTiles );
598   
599   F32 leftOffset   = tileSize.x*overlap.x;
600   F32 rightOffset  = tileSize.x*overlap.x*2;
601   F32 bottomOffset = tileSize.y*overlap.y;
602   F32 topOffset    = tileSize.y*overlap.y*2;
603
604   *left += tileSize.x * curTile.x - leftOffset;
605   *right = *left + tileSize.x + rightOffset;
606   *bottom += tileSize.y * curTile.y - bottomOffset;
607   *top = *bottom + tileSize.y + topOffset;
608}
609