pssmLightShadowMap.cpp
Engine/source/lighting/shadowMap/pssmLightShadowMap.cpp
Public Defines
define
Public Functions
AFTER_MODULE_INIT(Sim )
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