Torque3D Documentation / _generateds / pssmLightShadowMap.cpp

pssmLightShadowMap.cpp

Engine/source/lighting/shadowMap/pssmLightShadowMap.cpp

More...

Public Defines

Public Functions

Detailed Description

Public Defines

ENABLE_CULL_ASSERT() 

Public Functions

AFTER_MODULE_INIT(Sim )

  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 "lighting/shadowMap/pssmLightShadowMap.h"
 26
 27#include "lighting/common/lightMapParams.h"
 28#include "console/console.h"
 29#include "scene/sceneManager.h"
 30#include "scene/sceneRenderState.h"
 31#include "lighting/lightManager.h"
 32#include "gfx/gfxDevice.h"
 33#include "gfx/gfxTransformSaver.h"
 34#include "gfx/util/gfxFrustumSaver.h"
 35#include "renderInstance/renderPassManager.h"
 36#include "gui/controls/guiBitmapCtrl.h"
 37#include "lighting/shadowMap/shadowMapManager.h"
 38#include "materials/shaderData.h"
 39#include "ts/tsShapeInstance.h"
 40#include "console/consoleTypes.h"
 41#include "math/mathUtils.h"
 42
 43
 44AFTER_MODULE_INIT( Sim )
 45{
 46   Con::addVariable( "$pref::PSSM::detailAdjustScale", 
 47      TypeF32, &PSSMLightShadowMap::smDetailAdjustScale,
 48      "@brief Scales the model LOD when rendering into the PSSM shadow.\n"
 49      "Use this to reduce the draw calls when rendering the shadow by having "
 50      "meshes LOD out nearer to the camera than normal.\n"
 51      "@see $pref::TS::detailAdjust\n"
 52      "@ingroup AdvancedLighting" );
 53
 54   Con::addVariable( "$pref::PSSM::smallestVisiblePixelSize", 
 55      TypeF32, &PSSMLightShadowMap::smSmallestVisiblePixelSize,
 56      "@brief The smallest pixel size an object can be and still be rendered into the PSSM shadow.\n"
 57      "Use this to force culling of small objects which contribute little to the final shadow.\n"
 58      "@see $pref::TS::smallestVisiblePixelSize\n"
 59      "@ingroup AdvancedLighting" );
 60}
 61
 62F32 PSSMLightShadowMap::smDetailAdjustScale = 0.85f;
 63F32 PSSMLightShadowMap::smSmallestVisiblePixelSize = 25.0f;
 64
 65
 66PSSMLightShadowMap::PSSMLightShadowMap( LightInfo *light )
 67   :  LightShadowMap( light ),
 68      mNumSplits( 1 ),
 69      mLogWeight(0.91f)
 70{
 71   for (U32 i = 0; i <= MAX_SPLITS; i++) //% depth distance
 72      mSplitDist[i] = mPow(F32(i/<a href="/coding/class/classpssmlightshadowmap/#classpssmlightshadowmap_1a4025701a042ce1edafc61f099b9c58ee">MAX_SPLITS</a>),2.0f);
 73
 74   mIsViewDependent = true;
 75}
 76
 77void PSSMLightShadowMap::_setNumSplits( U32 numSplits, U32 texSize )
 78{
 79   AssertFatal(numSplits > 0 && numSplits <= MAX_SPLITS,
 80      avar("PSSMLightShadowMap::_setNumSplits() - Splits must be between 1 and %d!", MAX_SPLITS));
 81
 82   releaseTextures();
 83  
 84   mNumSplits = numSplits;
 85   mTexSize = texSize;
 86   F32 texWidth, texHeight;
 87
 88   // If the split count is less than 4 then do a
 89   // 1xN layout of shadow maps...
 90   if ( mNumSplits < 4 )
 91   {
 92      texHeight = texSize;
 93      texWidth = texSize * mNumSplits;
 94
 95      for ( U32 i = 0; i < 4; i++ )
 96      {
 97         mViewports[i].extent.set(texSize, texSize);
 98         mViewports[i].point.set(texSize*i, 0);
 99      }
100   } 
101   else 
102   {
103      // ... with 4 splits do a 2x2.
104      texWidth = texHeight = texSize * 2;
105
106      for ( U32 i = 0; i < 4; i++ )
107      {
108         F32 xOff = (i == 1 || i == 3) ? 0.5f : 0.0f;
109         F32 yOff = (i > 1) ? 0.5f : 0.0f;
110         mViewports[i].extent.set( texSize, texSize );
111         mViewports[i].point.set( xOff * texWidth, yOff * texHeight );
112      }
113   }
114
115   mShadowMapTex.set(   texWidth, texHeight, 
116                        ShadowMapFormat, &ShadowMapProfile, 
117                        "PSSMLightShadowMap" );
118}
119
120void PSSMLightShadowMap::_calcSplitPos(const Frustum& currFrustum)
121{
122   const F32 nearDist = 0.01f; // TODO: Should this be adjustable or different?
123   const F32 farDist = currFrustum.getFarDist();
124
125   for ( U32 i = 1; i < mNumSplits; i++ )
126   {
127      F32 step = (F32) i / (F32) mNumSplits;
128      F32 logSplit = nearDist * mPow(farDist / nearDist, step);
129      F32 linearSplit = nearDist + (farDist - nearDist) * step;
130      mSplitDist[i] = mLerp( linearSplit, logSplit, mClampF( mLogWeight, 0.0f, 1.0f ) );
131   }
132
133   mSplitDist[0] = nearDist;
134   mSplitDist[mNumSplits] = farDist;
135}
136
137Box3F PSSMLightShadowMap::_calcClipSpaceAABB(const Frustum& f, const MatrixF& transform, F32 farDist)
138{
139   // Calculate frustum center
140   Point3F center(0,0,0);
141   for (U32 i = 0; i < 8; i++)   
142   {
143      const Point3F& pt = f.getPoints()[i];      
144      center += pt;
145   }
146   center /= 8;
147
148   // Calculate frustum bounding sphere radius
149   F32 radius = 0.0f;
150   for (U32 i = 0; i < 8; i++)      
151      radius = getMax(radius, (f.getPoints()[i] - center).lenSquared());
152   radius = mFloor( mSqrt(radius) );
153      
154   // Now build box for sphere
155   Box3F result;
156   Point3F radiusBox(radius, radius, radius);
157   result.minExtents = center - radiusBox;
158   result.maxExtents = center + radiusBox;
159
160   // Transform to light projection space
161   transform.mul(result);
162   
163   return result;   
164}
165
166// This "rounds" the projection matrix to remove subtexel movement during shadow map
167// rasterization.  This is here to reduce shadow shimmering.
168void PSSMLightShadowMap::_roundProjection(const MatrixF& lightMat, const MatrixF& cropMatrix, Point3F &offset, U32 splitNum)
169{
170   // Round to the nearest shadowmap texel, this helps reduce shimmering
171   MatrixF currentProj = GFX->getProjectionMatrix();
172   currentProj = cropMatrix * currentProj * lightMat;
173
174   // Project origin to screen.
175   Point4F originShadow4F(0,0,0,1);
176   currentProj.mul(originShadow4F);
177   Point2F originShadow(originShadow4F.x / originShadow4F.w, originShadow4F.y / originShadow4F.w);   
178
179   // Convert to texture space (0..shadowMapSize)
180   F32 t = mNumSplits < 4 ? mShadowMapTex->getWidth() / mNumSplits : mShadowMapTex->getWidth() / 2;
181   Point2F texelsToTexture(t / 2.0f, mShadowMapTex->getHeight() / 2.0f);
182   if (mNumSplits >= 4) texelsToTexture.y *= 0.5f;
183   originShadow.convolve(texelsToTexture);
184
185   // Clamp to texel boundary
186   Point2F originRounded;
187   originRounded.x = mFloor(originShadow.x + 0.5f);
188   originRounded.y = mFloor(originShadow.y + 0.5f);
189
190   // Subtract origin to get an offset to recenter everything on texel boundaries
191   originRounded -= originShadow;
192
193   // Convert back to texels (0..1) and offset
194   originRounded.convolveInverse(texelsToTexture);
195   offset.x += originRounded.x;
196   offset.y += originRounded.y;
197}
198
199void PSSMLightShadowMap::_render(   RenderPassManager* renderPass,
200                                    const SceneRenderState *diffuseState )
201{
202   PROFILE_SCOPE(PSSMLightShadowMap_render);
203
204   const ShadowMapParams *params = mLight->getExtended<ShadowMapParams>();
205   const LightMapParams *lmParams = mLight->getExtended<LightMapParams>();
206   const bool bUseLightmappedGeometry = lmParams ? !lmParams->representedInLightmap || lmParams->includeLightmappedGeometryInShadow : true;
207
208   const U32 texSize = getBestTexSize( params->numSplits < 4 ? params->numSplits : 2 );
209
210   if (  mShadowMapTex.isNull() || 
211         mNumSplits != params->numSplits || 
212         mTexSize != texSize )
213   {
214      _setNumSplits( params->numSplits, texSize );
215      mShadowMapDepth = _getDepthTarget( mShadowMapTex->getWidth(), mShadowMapTex->getHeight() );   
216   }
217   mLogWeight = params->logWeight;
218
219   Frustum fullFrustum( diffuseState->getCameraFrustum() );
220   fullFrustum.cropNearFar(fullFrustum.getNearDist(), params->shadowDistance);
221
222   GFXFrustumSaver frustSaver;
223   GFXTransformSaver saver;
224
225   // Set our render target
226   GFX->pushActiveRenderTarget();
227   mTarget->attachTexture( GFXTextureTarget::Color0, mShadowMapTex );
228   mTarget->attachTexture( GFXTextureTarget::DepthStencil, mShadowMapDepth );
229   GFX->setActiveRenderTarget( mTarget );
230   GFX->clear( GFXClearStencil | GFXClearZBuffer | GFXClearTarget, ColorI(255,255,255), 1.0f, 0 );
231
232   // Calculate our standard light matrices
233   MatrixF lightMatrix;
234   calcLightMatrices( lightMatrix, diffuseState->getCameraFrustum() );
235   lightMatrix.inverse();
236   MatrixF lightViewProj = GFX->getProjectionMatrix() * lightMatrix;
237
238   // TODO: This is just retrieving the near and far calculated
239   // in calcLightMatrices... we should make that clear.
240   F32 pnear, pfar;
241   GFX->getFrustum( NULL, NULL, NULL, NULL, &pnear, &pfar, NULL );
242
243   // Set our view up
244   GFX->setWorldMatrix(lightMatrix);
245   MatrixF toLightSpace = lightMatrix; // * invCurrentView;
246
247   _calcSplitPos(fullFrustum);
248   
249   mWorldToLightProj = GFX->getProjectionMatrix() * toLightSpace;
250
251   // Apply the PSSM 
252   const F32 savedSmallestVisible = TSShapeInstance::smSmallestVisiblePixelSize;
253   const F32 savedDetailAdjust = TSShapeInstance::smDetailAdjust;
254   TSShapeInstance::smDetailAdjust *= smDetailAdjustScale;
255   TSShapeInstance::smSmallestVisiblePixelSize = smSmallestVisiblePixelSize;
256
257   Vector< Vector<PlaneF> > _extraCull;
258   _calcPlanesCullForShadowCasters( _extraCull, fullFrustum, mLight->getDirection() );
259
260   for (U32 i = 0; i < mNumSplits; i++)
261   {
262      GFXTransformSaver splitSaver;
263
264      // Calculate a sub-frustum
265      Frustum subFrustum(fullFrustum);
266      subFrustum.cropNearFar(mSplitDist[i], mSplitDist[i+1]);
267
268      // Calculate our AABB in the light's clip space.
269      Box3F clipAABB = _calcClipSpaceAABB(subFrustum, lightViewProj, fullFrustum.getFarDist());
270 
271      // Calculate our crop matrix
272      Point3F scale(2.0f / (clipAABB.maxExtents.x - clipAABB.minExtents.x),
273         2.0f / (clipAABB.maxExtents.y - clipAABB.minExtents.y),
274         1.0f);
275
276      // TODO: This seems to produce less "pops" of the
277      // shadow resolution as the camera spins around and
278      // it should produce pixels that are closer to being
279      // square.
280      //
281      // Still is it the right thing to do?
282      //
283      scale.y = scale.x = ( getMin( scale.x, scale.y ) ); 
284      //scale.x = mFloor(scale.x); 
285      //scale.y = mFloor(scale.y); 
286
287      Point3F offset(   -0.5f * (clipAABB.maxExtents.x + clipAABB.minExtents.x) * scale.x,
288                        -0.5f * (clipAABB.maxExtents.y + clipAABB.minExtents.y) * scale.y,
289                        0.0f );
290
291      MatrixF cropMatrix(true);
292      cropMatrix.scale(scale);
293      cropMatrix.setPosition(offset);
294
295      _roundProjection(lightMatrix, cropMatrix, offset, i);
296
297      cropMatrix.setPosition(offset);      
298
299      // Save scale/offset for shader computations
300      mScaleProj[i].set(scale);
301      mOffsetProj[i].set(offset);
302
303      // Adjust the far plane to the max z we got (maybe add a little to deal with split overlap)
304      bool isOrtho;
305      {
306         F32 left, right, bottom, top, nearDist, farDist;
307         GFX->getFrustum(&left, &right, &bottom, &top, &nearDist, &farDist,&isOrtho);
308         // BTRTODO: Fix me!
309         farDist = clipAABB.maxExtents.z;
310         if (!isOrtho)
311            GFX->setFrustum(left, right, bottom, top, nearDist, farDist);
312         else
313         {
314            // Calculate a new far plane, add a fudge factor to avoid bringing
315            // the far plane in too close.
316            F32 newFar = pfar * clipAABB.maxExtents.z + 1.0f;
317            mFarPlaneScalePSSM[i] = (pfar - pnear) / (newFar - pnear);
318            GFX->setOrtho(left, right, bottom, top, pnear, newFar, true);
319         }
320      }
321
322      // Crop matrix multiply needs to be post-projection.
323      MatrixF alightProj = GFX->getProjectionMatrix();
324      alightProj = cropMatrix * alightProj;
325
326      // Set our new projection
327      GFX->setProjectionMatrix(alightProj);
328
329      // Render into the quad of the shadow map we are using.
330      GFX->setViewport(mViewports[i]);
331
332      SceneManager* sceneManager = diffuseState->getSceneManager();
333
334      // The frustum is currently the  full size and has not had
335      // cropping applied.
336      //
337      // We make that adjustment here.
338
339      const Frustum& uncroppedFrustum = GFX->getFrustum();
340      Frustum croppedFrustum;
341      scale *= 0.5f;
342      croppedFrustum.set(
343         isOrtho,
344         uncroppedFrustum.getNearLeft() / scale.x,
345         uncroppedFrustum.getNearRight() / scale.x,
346         uncroppedFrustum.getNearTop() / scale.y,
347         uncroppedFrustum.getNearBottom() / scale.y,
348         uncroppedFrustum.getNearDist(),
349         uncroppedFrustum.getFarDist(),
350         uncroppedFrustum.getTransform()
351      );
352
353      MatrixF camera = GFX->getWorldMatrix();
354      camera.inverse();
355      croppedFrustum.setTransform( camera );
356
357      // Setup the scene state and use the diffuse state
358      // camera position and screen metrics values so that
359      // lod is done the same as in the diffuse pass.
360
361      SceneRenderState shadowRenderState
362      (
363         sceneManager,
364         SPT_Shadow,
365         SceneCameraState( diffuseState->getViewport(), croppedFrustum,
366                           GFX->getWorldMatrix(), GFX->getProjectionMatrix() ),
367         renderPass
368      );
369
370      shadowRenderState.getMaterialDelegate().bind( this, &LightShadowMap::getShadowMaterial );
371      shadowRenderState.renderNonLightmappedMeshes( true );
372      shadowRenderState.renderLightmappedMeshes( bUseLightmappedGeometry );
373
374      shadowRenderState.setDiffuseCameraTransform( diffuseState->getCameraTransform() );
375      shadowRenderState.setWorldToScreenScale( diffuseState->getWorldToScreenScale() );
376
377      PlaneSetF planeSet( _extraCull[i].address(), _extraCull[i].size() );
378      shadowRenderState.getCullingState().setExtraPlanesCull( planeSet );
379
380      U32 objectMask = SHADOW_TYPEMASK;
381      if ( i == mNumSplits-1 && params->lastSplitTerrainOnly )
382         objectMask = TerrainObjectType;
383
384      sceneManager->renderSceneNoLights( &shadowRenderState, objectMask );
385
386      shadowRenderState.getCullingState().clearExtraPlanesCull();
387
388      _debugRender( &shadowRenderState );
389   }
390
391   // Restore the original TS lod settings.
392   TSShapeInstance::smSmallestVisiblePixelSize = savedSmallestVisible;
393   TSShapeInstance::smDetailAdjust = savedDetailAdjust;
394
395   // Release our render target
396   mTarget->resolve();
397   GFX->popActiveRenderTarget();
398}
399
400void PSSMLightShadowMap::setShaderParameters(GFXShaderConstBuffer* params, LightingShaderConstants* lsc)
401{
402   PROFILE_SCOPE( PSSMLightShadowMap_setShaderParameters );
403
404   AssertFatal(mNumSplits > 0 && mNumSplits <= MAX_SPLITS,
405      avar("PSSMLightShadowMap::_setNumSplits() - Splits must be between 1 and %d!", MAX_SPLITS));
406
407   if ( lsc->mTapRotationTexSC->isValid() )
408      GFX->setTexture( lsc->mTapRotationTexSC->getSamplerRegister(), 
409                        SHADOWMGR->getTapRotationTex() );
410
411   const ShadowMapParams *p = mLight->getExtended<ShadowMapParams>();
412
413   Point4F  sx(Point4F::Zero), 
414            sy(Point4F::Zero),
415            ox(Point4F::Zero), 
416            oy(Point4F::Zero), 
417            aXOff(Point4F::Zero), 
418            aYOff(Point4F::Zero);
419
420   for (U32 i = 0; i < mNumSplits; i++)
421   {
422      sx[i] = mScaleProj[i].x;
423      sy[i] = mScaleProj[i].y;
424      ox[i] = mOffsetProj[i].x;
425      oy[i] = mOffsetProj[i].y;
426   }
427
428   Point2F shadowMapAtlas;
429   if (mNumSplits < 4)
430   {
431      shadowMapAtlas.x = 1.0f / (F32)mNumSplits;
432      shadowMapAtlas.y = 1.0f;
433   
434      // 1xmNumSplits
435      for (U32 i = 0; i < mNumSplits; i++)
436         aXOff[i] = (F32)i * shadowMapAtlas.x;
437   }
438   else
439   {
440      shadowMapAtlas.set(0.5f, 0.5f);
441  
442      // 2x2
443      for (U32 i = 0; i < mNumSplits; i++)
444      {
445         if (i == 1 || i == 3)
446            aXOff[i] = 0.5f;
447         if (i > 1)
448            aYOff[i] = 0.5f;
449      }
450   }
451
452   // These values change based on static/dynamic.
453   params->setSafe(lsc->mScaleXSC, sx);
454   params->setSafe(lsc->mScaleYSC, sy);
455   params->setSafe(lsc->mOffsetXSC, ox);
456   params->setSafe(lsc->mOffsetYSC, oy);
457   params->setSafe(lsc->mFarPlaneScalePSSM, mFarPlaneScalePSSM);
458
459   params->setSafe(lsc->mAtlasXOffsetSC, aXOff);
460   params->setSafe(lsc->mAtlasYOffsetSC, aYOff);
461   params->setSafe(lsc->mAtlasScaleSC, shadowMapAtlas);
462
463   Point4F lightParams( mLight->getRange().x, p->overDarkFactor.x, 0.0f, 0.0f );
464   params->setSafe( lsc->mLightParamsSC, lightParams );
465
466   Point2F fadeStartLength(p->fadeStartDist, 0.0f);
467   if (fadeStartLength.x == 0.0f)
468   {
469      // By default, lets fade the last half of the last split.
470      fadeStartLength.x = (mSplitDist[mNumSplits-1] + mSplitDist[mNumSplits]) / 2.0f;
471   }
472   fadeStartLength.y = 1.0f / (mSplitDist[mNumSplits] - fadeStartLength.x);
473   params->setSafe( lsc->mFadeStartLength, fadeStartLength);
474   
475   params->setSafe( lsc->mOverDarkFactorPSSM, p->overDarkFactor);
476
477   // The softness is a factor of the texel size.
478   params->setSafe( lsc->mShadowSoftnessConst, p->shadowSoftness * ( 1.0f / mTexSize ) );
479}
480
481void PSSMLightShadowMap::_calcPlanesCullForShadowCasters(Vector< Vector<PlaneF> > &out, const Frustum &viewFrustum, const Point3F &_ligthDir)
482{
483
484#define ENABLE_CULL_ASSERT
485
486   PROFILE_SCOPE(PSSMLightShadowMap_render_getCullFrustrum);
487
488   Point3F ligthDir = _ligthDir;
489   PlaneF lightFarPlane, lightNearPlane;
490   MatrixF lightFarPlaneMat(true);
491   MatrixF invLightFarPlaneMat(true);
492
493   // init data
494   {
495      ligthDir.normalize();
496      Point3F viewDir = viewFrustum.getTransform().getForwardVector();
497      viewDir.normalize();
498      const Point3F viewPosition = viewFrustum.getPosition();
499      const F32 viewDistance = viewFrustum.getBounds().len();
500      lightNearPlane = PlaneF(viewPosition + (viewDistance * -ligthDir), ligthDir);
501
502      const Point3F lightFarPlanePos = viewPosition + (viewDistance * ligthDir);
503      lightFarPlane = PlaneF(lightFarPlanePos, -ligthDir);
504
505      lightFarPlaneMat = MathUtils::createOrientFromDir(-ligthDir);
506      lightFarPlaneMat.setPosition(lightFarPlanePos);
507      lightFarPlaneMat.invertTo(&invLightFarPlaneMat);
508   }
509
510   Vector<Point2F> projVertices;
511
512   //project all frustum vertices into plane
513   // all vertices are 2d and local to far plane
514   projVertices.setSize(8);
515   for (int i = 0; i < 8; ++i) //
516   {
517      const Point3F &point = viewFrustum.getPoints()[i];
518#ifdef ENABLE_CULL_ASSERT
519      AssertFatal( PlaneF::Front == lightNearPlane.whichSide(point), "" );
520      AssertFatal( PlaneF::Front == lightFarPlane.whichSide(point), "" );
521#endif
522
523      Point3F localPoint(lightFarPlane.project(point));
524      invLightFarPlaneMat.mulP(localPoint);
525      projVertices[i] = Point2F(localPoint.x, localPoint.z);
526   }
527
528   //create hull arround projected proints
529   Vector<Point2F> hullVerts;
530   MathUtils::mBuildHull2D(projVertices, hullVerts);
531
532   Vector<PlaneF> planes;
533   planes.push_back(lightNearPlane);
534   planes.push_back(lightFarPlane);
535
536   //build planes
537   for (int i = 0; i < (hullVerts.size() - 1); ++i)
538   {
539      Point2F pos2D = (hullVerts[i] + hullVerts[i + 1]) / 2;
540      Point3F pos3D(pos2D.x, 0, pos2D.y);
541
542      Point3F pos3DA(hullVerts[i].x, 0, hullVerts[i].y);
543      Point3F pos3DB(hullVerts[i + 1].x, 0, hullVerts[i + 1].y);
544
545      // move hull points to 3d space
546      lightFarPlaneMat.mulP(pos3D);
547      lightFarPlaneMat.mulP(pos3DA);
548      lightFarPlaneMat.mulP(pos3DB);
549
550      PlaneF plane(pos3D, MathUtils::mTriangleNormal(pos3DB, pos3DA, (pos3DA - ligthDir)));
551      planes.push_back(plane);
552   }
553
554   //recalculate planes for each splits
555   for (int split = 0; split < mNumSplits; ++split)
556   {
557      Frustum subFrustum(viewFrustum);
558      subFrustum.cropNearFar(mSplitDist[split], mSplitDist[split + 1]);
559      subFrustum.setFarDist(getMin(subFrustum.getFarDist()*2.5f, viewFrustum.getFarDist()));
560      subFrustum.update();
561
562      Vector<PlaneF> subPlanes = planes;
563
564      for (int planeIdx = 0; planeIdx < subPlanes.size(); ++planeIdx)
565      {
566         PlaneF &plane = subPlanes[planeIdx];
567         F32 minDist = 0;
568
569         //calculate near vertex distance
570         for (int vertexIdx = 0; vertexIdx < 8; ++vertexIdx)
571         {
572            Point3F point = subFrustum.getPoints()[vertexIdx];
573            minDist = getMin(plane.distToPlane(point), minDist);
574         }
575
576         // move plane to near vertex
577         Point3F newPos = plane.getPosition() + (plane.getNormal() * minDist);
578         plane = PlaneF(newPos, plane.getNormal());
579
580#ifdef ENABLE_CULL_ASSERT
581         for(int x = 0; x < 8; ++x)
582         {
583            AssertFatal( PlaneF::Back != plane.whichSide( subFrustum.getPoints()[x] ), "");
584         }
585#endif
586      }
587
588      out.push_back(subPlanes);
589   }
590
591#undef ENABLE_CULL_ASSERT
592
593}
594