csplugincommon/rendermanager/shadow_pssm.h
Go to the documentation of this file.00001 /* 00002 Copyright (C) 2012 by Matthieu Kraus 00003 2008 by Frank Richter 00004 00005 This library is free software; you can redistribute it and/or 00006 modify it under the terms of the GNU Library General Public 00007 License as published by the Free Software Foundation; either 00008 version 2 of the License, or (at your option) any later version. 00009 00010 This library is distributed in the hope that it will be useful, 00011 but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00013 Library General Public License for more details. 00014 00015 You should have received a copy of the GNU Library General Public 00016 License along with this library; if not, write to the Free 00017 Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 00018 */ 00019 00020 #ifndef __CS_CSPLUGINCOMMON_RENDERMANAGER_SHADOW_PSSM_H__ 00021 #define __CS_CSPLUGINCOMMON_RENDERMANAGER_SHADOW_PSSM_H__ 00022 00027 #include "ivideo/shader/shader.h" 00028 00029 #include "csutil/cfgacc.h" 00030 00031 #include "cstool/meshfilter.h" 00032 00033 #include "csplugincommon/rendermanager/operations.h" 00034 #include "csplugincommon/rendermanager/rendertree.h" 00035 #include "csplugincommon/rendermanager/shadow_common.h" 00036 #include "csplugincommon/rendermanager/standardsorter.h" 00037 #include "csplugincommon/rendermanager/viscull.h" 00038 00039 #include "csgeom/matrix4.h" 00040 #include "csgeom/projections.h" 00041 00042 namespace CS 00043 { 00044 namespace RenderManager 00045 { 00046 struct ShadowPSSMExtraMeshData 00047 { 00048 }; 00049 00050 inline csVector2 Vector4To2(const csVector4& v, float* z = nullptr) 00051 { 00052 if(z) *z = v.z/v.w; 00053 return csVector2(v.x/v.w, v.y/v.w); 00054 } 00055 00056 inline csVector3 Vector4To3(const csVector4& v) 00057 { 00058 return csVector3(v.x/v.w, v.y/v.w, v.z/v.w); 00059 } 00060 00061 // helper function that builds a projection space box 00062 // the z value isn't transformed and only the positive z direction 00063 // is taken into account 00064 inline csBox3 ProjectBox(const csBox3& boxIn, const CS::Math::Matrix4& proj, const CS::Math::Matrix4& projInv) 00065 { 00066 // clamp incoming box to positive z 00067 csBox3 box(csBox3(-FLT_MAX,-FLT_MAX,SMALL_Z, FLT_MAX,FLT_MAX,FLT_MAX) * boxIn); 00068 00069 // convenience variables 00070 float boxNear = box.MinZ(); 00071 float boxFar = box.MaxZ(); 00072 00073 // projection space near and far planes 00074 float boxNearPS, boxFarPS; 00075 00076 // transform far plane to projection space 00077 csBox2 farPS(Vector4To2(proj * csVector4(box.GetCorner(CS_BOX_CORNER_XYZ)), &boxFarPS)); 00078 farPS.AddBoundingVertexSmart(Vector4To2(proj * csVector4(box.GetCorner(CS_BOX_CORNER_xYZ)))); 00079 farPS.AddBoundingVertexSmart(Vector4To2(proj * csVector4(box.GetCorner(CS_BOX_CORNER_xyZ)))); 00080 farPS.AddBoundingVertexSmart(Vector4To2(proj * csVector4(box.GetCorner(CS_BOX_CORNER_XyZ)))); 00081 00082 // clip to actual frustum 00083 csBox2 farNDC(csBox2(-1,-1,1,1) * farPS); 00084 00085 // if the far plane doesn't intersect with the frustum the whole box doesn't 00086 if(farNDC.Empty()) 00087 { 00088 return csBox3(); 00089 } 00090 00091 // transform near plane to projection space 00092 csBox2 nearPS(Vector4To2(proj * csVector4(box.GetCorner(CS_BOX_CORNER_XYz)), &boxNearPS)); 00093 nearPS.AddBoundingVertexSmart(Vector4To2(proj * csVector4(box.GetCorner(CS_BOX_CORNER_xYz)))); 00094 nearPS.AddBoundingVertexSmart(Vector4To2(proj * csVector4(box.GetCorner(CS_BOX_CORNER_xyz)))); 00095 nearPS.AddBoundingVertexSmart(Vector4To2(proj * csVector4(box.GetCorner(CS_BOX_CORNER_Xyz)))); 00096 00097 // clip to actual frustum 00098 csBox2 nearNDC(csBox2(-1,-1,1,1) * nearPS); 00099 00100 // if near plane intersects with the frustum we are done 00101 if(!nearNDC.Empty()) 00102 { 00103 // return union of the boxes 00104 nearNDC += farNDC; 00105 return csBox3(nearNDC.MinX(), nearNDC.MinY(), boxNear, 00106 nearNDC.MaxX(), nearNDC.MaxY(), boxFar); 00107 } 00108 // else we have to take special care to use the actual near plane of the part 00109 // that lies within the frustum 00110 else 00111 { 00112 // start with current near plane 00113 float nearZ = boxNear; 00114 00115 // check whether x axis needs special care 00116 if(nearNDC.MinX() > nearNDC.MaxX()) 00117 { 00118 // check on which side we are 00119 if(farNDC.MinX() > 0) 00120 { 00121 // unproject clipped near 00122 csVector4 nearClipped(projInv * csVector4(1,0,boxNearPS)); 00123 float nearClippedX(nearClipped.x/nearClipped.w); 00124 00125 // unproject clipped far max 00126 csVector4 farClipped(projInv * csVector4(1,0,boxFarPS)); 00127 float farClippedX(farClipped.x/farClipped.w); 00128 00129 // calculate actual required near plane 00130 nearZ = csMax(nearZ, boxFar - (boxFar - boxNear) * (box.MinX() - farClippedX) / (nearClippedX - farClippedX)); 00131 } 00132 else 00133 { 00134 // unproject clipped near 00135 csVector4 nearClipped(projInv * csVector4(-1,0,boxNearPS)); 00136 float nearClippedX(nearClipped.x/nearClipped.w); 00137 00138 // unproject clipped far max 00139 csVector4 farClipped(projInv * csVector4(-1,0,boxFarPS)); 00140 float farClippedX(farClipped.x/farClipped.w); 00141 00142 // calculate actual required near plane 00143 nearZ = csMax(nearZ, boxFar - (boxFar - boxNear) * (box.MaxX() - farClippedX) / (nearClippedX - farClippedX)); 00144 } 00145 } 00146 00147 // check whether y axis needs special care 00148 if(nearNDC.MinY() > nearNDC.MaxY()) 00149 { 00150 // check on which side we are 00151 if(farNDC.MinY() > 0) 00152 { 00153 // unproject clipped near 00154 csVector4 nearClipped(projInv * csVector4(0,1,boxNearPS)); 00155 float nearClippedY(nearClipped.y/nearClipped.w); 00156 00157 // unproject clipped far max 00158 csVector4 farClipped(projInv * csVector4(0,1,boxFarPS)); 00159 float farClippedY(farClipped.y/farClipped.w); 00160 00161 // calculate actual required near plane 00162 nearZ = csMax(nearZ, boxFar - (boxFar - boxNear) * (box.MinY() - farClippedY) / (nearClippedY - farClippedY)); 00163 } 00164 else 00165 { 00166 // unproject clipped near 00167 csVector4 nearClipped(projInv * csVector4(0,-1,boxNearPS)); 00168 float nearClippedY(nearClipped.y/nearClipped.w); 00169 00170 // unproject clipped far max 00171 csVector4 farClipped(projInv * csVector4(0,-1,boxFarPS)); 00172 float farClippedY(farClipped.y/farClipped.w); 00173 00174 // calculate actual required near plane 00175 nearZ = csMax(nearZ, boxFar - (boxFar - boxNear) * (box.MaxY() - farClippedY) / (nearClippedY - farClippedY)); 00176 } 00177 } 00178 00179 // set our new depth value 00180 boxNear = nearZ; 00181 00182 // calculate new near plane now that we have the right depth 00183 nearNDC.StartBoundingBox(Vector4To2(proj * csVector4(box.MinX(), box.MinY(), boxNear))); 00184 nearNDC.AddBoundingVertexSmart(Vector4To2(proj * csVector4(box.MaxX(), box.MinY(), boxNear))); 00185 nearNDC.AddBoundingVertexSmart(Vector4To2(proj * csVector4(box.MinX(), box.MaxY(), boxNear))); 00186 nearNDC.AddBoundingVertexSmart(Vector4To2(proj * csVector4(box.MaxX(), box.MaxY(), boxNear))); 00187 00188 // clip to actual frustum 00189 nearNDC *= csBox2(-1,-1,1,1); 00190 } 00191 00192 // return union of the boxes 00193 nearNDC += farNDC; 00194 return csBox3(nearNDC.MinX(), nearNDC.MinY(), boxNear, 00195 nearNDC.MaxX(), nearNDC.MaxY(), boxFar); 00196 } 00197 00198 inline csBox3 UnprojectBox(const csBox3& box, const CS::Math::Matrix4 proj) 00199 { 00200 // start a new bounding box 00201 csBox3 projBox; 00202 00203 // get inverse projection 00204 CS::Math::Matrix4 projInv(proj.GetInverse()); 00205 00206 for(int i = 0; i < 8; ++i) 00207 { 00208 // add projected corner to our new bbox 00209 projBox.AddBoundingVertexSmart(Vector4To3(projInv * csVector4(box.GetCorner(i)))); 00210 } 00211 00212 return projBox; 00213 } 00214 00215 inline CS::Math::Matrix4 CreateCropMatrix(float left, float right, 00216 float bottom, float top, 00217 float nearZ, float farZ, 00218 csVector4* unscale = nullptr) 00219 { 00220 // calculate crop scale 00221 float cropScaleX = 2.0f/(right - left); 00222 float cropScaleY = 2.0f/(top - bottom); 00223 float cropScaleZ = 2.0f/(farZ - nearZ); 00224 00225 // calculate crop shift 00226 float cropShiftX = (left + right)/(left - right); 00227 float cropShiftY = (bottom + top)/(bottom - top); 00228 float cropShiftZ = (nearZ + farZ)/(nearZ - farZ); 00229 00230 // assemble matrix 00231 CS::Math::Matrix4 crop( 00232 cropScaleX, 0, 0, cropShiftX, 00233 0, cropScaleY, 0, cropShiftY, 00234 0, 0, cropScaleZ, cropShiftZ, 00235 0, 0, 0, 1 00236 ); 00237 00238 if(unscale) 00239 { 00240 // calculate uncrop scale 00241 unscale->x = 0.5f * (right - left); 00242 unscale->y = 0.5f * (top - bottom); 00243 00244 // calculate uncrop shift 00245 unscale->z = 0.5f * (left + right); 00246 unscale->w = 0.5f * (bottom + top); 00247 } 00248 00249 return crop; 00250 } 00251 00252 inline CS::Math::Matrix4 CreateFrustumMatrix(float left, float right, 00253 float bottom, float top, 00254 float nearZ, float farZ) 00255 { 00256 // convenience variable 00257 float two_near = 2.0f*nearZ; 00258 00259 // calculate frustum scale 00260 float frustumScaleX = two_near/(right - left); 00261 float frustumScaleY = two_near/(top - bottom); 00262 float frustumScaleZ = (farZ + nearZ)/(farZ - nearZ); 00263 00264 // calculate frustum shift 00265 float frustumShiftX = (left + right)/(left - right); 00266 float frustumShiftY = (bottom + top)/(bottom - top); 00267 float frustumShiftZ = -two_near*farZ/(farZ - nearZ); 00268 00269 // assemble matrix 00270 return CS::Math::Matrix4( 00271 frustumScaleX, 0, frustumShiftX, 0, 00272 0, frustumScaleY, frustumShiftY, 0, 00273 0, 0, frustumScaleZ, frustumShiftZ, 00274 0, 0, 1, 0 00275 ); 00276 } 00277 00278 template<typename RenderTreeType, typename LayerConfigType> 00279 class ShadowPSSM 00280 { 00281 public: 00282 // forward declarations 00283 struct LightData; 00284 struct PersistentData; 00285 00286 // structure used by light setup - we only need to let it know the 00287 // number of sub lights we need - rest is done outside of light setup 00288 struct CachedLightData : public CS::Memory::CustomAllocated 00289 { 00290 // Number of virtual lights needed for this light 00291 uint GetSublightNum() const { return subLightNum; } 00292 00294 void SetupFrame(RenderTreeType&, ShadowPSSM&, iLight* l) 00295 { 00296 // set number of sub-lights 00297 subLightNum = l->GetType() == CS_LIGHT_POINTLIGHT ? 6 : 1; 00298 } 00299 00301 void ClearFrameData() {} 00302 00303 protected: 00304 uint subLightNum; 00305 }; 00306 00307 // context setup type for shadow map creation 00308 class ShadowContextSetup 00309 { 00310 public: 00311 // typedefs for convenience 00312 typedef ShadowContextSetup ThisType; 00313 typedef StandardPortalSetup<RenderTreeType, ThisType> PortalSetupType; 00314 00315 ShadowContextSetup(const SingleRenderLayer& layerConfig, iShaderManager* shaderManager, 00316 typename PortalSetupType::PersistentData& portalPersist, int maxPortalRecurse, bool debugSplits) 00317 : layerConfig(layerConfig), shaderManager(shaderManager), portalPersist(portalPersist), 00318 recurseCount(0), maxPortalRecurse(maxPortalRecurse), debugSplits(debugSplits) 00319 { 00320 } 00321 00322 void operator()(typename RenderTreeType::ContextNode& context, 00323 typename PortalSetupType::ContextSetupData& portalSetupData, 00324 bool recursePortals = true) 00325 { 00326 if(recurseCount > maxPortalRecurse) return; 00327 00328 CS::RenderManager::RenderView* rview = context.renderView; 00329 iSector* sector = rview->GetThisSector(); 00330 00331 // @@@ This is somewhat "boilerplate" sector/rview setup. 00332 sector->PrepareDraw(rview); 00333 00334 // Make sure the clip-planes are ok 00335 CS::RenderViewClipper::SetupClipPlanes(rview->GetRenderContext()); 00336 00337 if(debugSplits) 00338 context.owner.AddDebugClipPlanes(rview); 00339 00340 // Do the culling 00341 iVisibilityCuller* culler = sector->GetVisibilityCuller(); 00342 Viscull<RenderTreeType>(context, rview, culler); 00343 00344 // Set up all portals 00345 if(recursePortals) 00346 { 00347 recurseCount++; 00348 PortalSetupType portalSetup(portalPersist, *this); 00349 portalSetup(context, portalSetupData); 00350 recurseCount--; 00351 } 00352 00353 // Sort the mesh lists 00354 { 00355 StandardMeshSorter<RenderTreeType> mySorter(rview->GetEngine()); 00356 mySorter.SetupCameraLocation(rview->GetCamera()->GetTransform().GetOrigin()); 00357 ForEachMeshNode(context, mySorter); 00358 } 00359 00360 // After sorting, assign in-context per-mesh indices 00361 { 00362 SingleMeshContextNumbering<RenderTreeType> numbering; 00363 ForEachMeshNode(context, numbering); 00364 } 00365 00366 // Setup the SV arrays 00367 // Push the default stuff 00368 SetupStandardSVs(context, layerConfig, shaderManager, sector); 00369 00370 // Setup the material&mesh SVs 00371 { 00372 StandardSVSetup<RenderTreeType, SingleRenderLayer> svSetup( 00373 context.svArrays, layerConfig); 00374 00375 ForEachMeshNode(context, svSetup); 00376 } 00377 00378 SetupStandardShader(context, shaderManager, layerConfig); 00379 00380 // Setup shaders and tickets 00381 SetupStandardTicket(context, shaderManager, layerConfig); 00382 } 00383 00384 00385 private: 00386 const SingleRenderLayer& layerConfig; 00387 iShaderManager* shaderManager; 00388 typename PortalSetupType::PersistentData& portalPersist; 00389 00390 int recurseCount; 00391 int maxPortalRecurse; 00392 bool debugSplits; 00393 }; 00394 00395 // Data used by the shadow handler that needs to persist over multiple frames. 00396 struct PersistentData 00397 { 00398 // Called every frame/RenderView() execution, use for housekeeping. 00399 void UpdateNewFrame() 00400 { 00401 // iterate over all the lights 00402 typename LightHash::GlobalIterator lightIt = lightHash.GetIterator(); 00403 while(lightIt.HasNext()) 00404 { 00405 csWeakRef<iLight> light; 00406 LightData& lightData = lightIt.NextNoAdvance(light); 00407 00408 // clear data if light is gone or out of date 00409 bool needsDelete = !light.IsValid(); 00410 if(!needsDelete) 00411 { 00412 if(lightData.updateNumber != light->GetMovable()->GetUpdateNumber()) 00413 { 00414 needsDelete = true; 00415 } 00416 } 00417 00418 if(needsDelete) 00419 { 00420 lightHash.DeleteElement(lightIt); 00421 continue; 00422 } 00423 lightIt.Advance(); 00424 00425 // go over the frustums for this light and clear the temp data 00426 for(size_t f = 0; f < lightData.frustums.GetSize(); ++f) 00427 { 00428 typename LightData::Frustum& frustum = lightData.frustums[f]; 00429 00430 // @@@TODO: add slice caching 00431 frustum.slicesHash.DeleteAll(); 00432 } 00433 } 00434 00435 csTicks time = csGetTicks(); 00436 settings.AdvanceFrame(time); 00437 lightVarsPersist.UpdateNewFrame(); 00438 portalPersist.UpdateNewFrame(); 00439 } 00440 00441 // Set the prefix for configuration settings 00442 void SetConfigPrefix(const char* prefix) 00443 { 00444 configPrefix = prefix; 00445 } 00446 00447 // Called upon plugin initialization 00448 void Initialize(iObjectRegistry* objectReg, RenderTreeBase::DebugPersistent& dbgPersist) 00449 { 00450 // get shader manager and graphics3d handles 00451 graphics3D = csQueryRegistry<iGraphics3D>(objectReg); 00452 shaderManager = csQueryRegistry<iShaderManager>(objectReg); 00453 { 00454 // initialize SV IDs 00455 iShaderVarStringSet* strings = shaderManager->GetSVNameStringset(); 00456 svNames.SetStrings(strings); 00457 00458 unscaleID = strings->Request("light shadow map unscale"); 00459 clipID = strings->Request("light shadow map clip"); 00460 } 00461 00462 // we need those infos after reading config, but only to calculate the splits 00463 float fixedCloseShadow; 00464 float farZ; 00465 00466 // start with reading config settings 00467 { 00468 csConfigAccess cfg(objectReg); 00469 00470 csString prefix(configPrefix); 00471 prefix += '.'; 00472 00473 // read shadow type settings 00474 settings.ReadSettings(objectReg, cfg->GetStr(prefix + csString("ShadowsType"), "Depth")); 00475 00476 // read shadow settings 00477 limitedShadow = cfg->GetBool(prefix + csString("LimitedShadow"), false); 00478 // @@@QUESTION: old PSSM just increased numSplits by 1 unconditionally - why? 00479 numSplits = cfg->GetInt(prefix + csString("NumSplits"), 2); 00480 farZ = cfg->GetFloat(prefix + csString("FarZ"), 100); 00481 maxPortalRecurse = cfg->GetInt(prefix + csString("MaxShadowPortalRecurse", 3)); 00482 int shadowMapRes = cfg->GetInt(prefix + csString("ShadowMapResolution"), 512); 00483 shadowMapResD = cfg->GetInt(prefix + csString("ShadowMapResolution.Directional"), shadowMapRes); 00484 shadowMapResP = cfg->GetInt(prefix + csString("ShadowMapResolution.Point"), shadowMapRes >> 1); 00485 shadowMapResS = cfg->GetInt(prefix + csString("ShadowMapResolution.Spot"), shadowMapRes >> 1); 00486 shadowMapResC = cfg->GetInt(prefix + csString("CloseShadowMapResolution"), shadowMapRes); 00487 fixedCloseShadow = cfg->GetFloat(prefix + csString("FixedCloseShadow"), 0); 00488 doFixedCloseShadow = fixedCloseShadow > 0 && fixedCloseShadow < farZ; 00489 00490 // read debug settings 00491 dbgSplit = dbgPersist.RegisterDebugFlag("draw.pssm.split.frustum"); 00492 dbgShadowTex = dbgPersist.RegisterDebugFlag("textures.shadow"); 00493 } 00494 00495 // create and initalize empty SMs 00496 csRef<iTextureManager> tm = graphics3D->GetTextureManager(); 00497 for(size_t i = 0; i < settings.targets.GetSize(); ++i) 00498 { 00499 // get target 00500 const ShadowSettings::Target* target = settings.targets[i]; 00501 00502 // get cache belonging to target 00503 const TextureCache& cache = target->texCache; 00504 00505 // create texture 00506 csRef<iTextureHandle> tex = tm->CreateTexture(1, 1, csimg2D, cache.GetFormat(), cache.GetFlags()); 00507 00508 // add it to our empty SM set 00509 emptySM[target->attachment] = tex; 00510 00511 // attach it as render target for initialization 00512 graphics3D->SetRenderTarget(tex, false, 0, target->attachment); 00513 } 00514 00515 // initalize textures 00516 // @@@FIXME: this doesn't properly initialize depth textures 00517 { 00518 // create quad 00519 csSimpleRenderMesh quad; 00520 00521 // create vertices 00522 csVector3 quadVerts[4] = { 00523 csVector3 (-1.0f, -1.0f, 1.0f), 00524 csVector3 (-1.0f, 1.0f, 1.0f), 00525 csVector3 ( 1.0f, 1.0f, 1.0f), 00526 csVector3 ( 1.0f, -1.0f, 1.0f) 00527 }; 00528 00529 // set mesh properties 00530 quad.vertexCount = 4; 00531 quad.vertices = quadVerts; 00532 quad.shader = nullptr; 00533 quad.z_buf_mode = CS_ZBUF_FILL; 00534 quad.mixmode = CS_FX_TRANSPARENT; 00535 quad.alphaType.autoAlphaMode = false; 00536 quad.alphaType.alphaType = csAlphaMode::alphaNone; 00537 00538 // set projection 00539 graphics3D->SetProjectionMatrix(CS::Math::Matrix4()); 00540 00541 // start draw 00542 graphics3D->BeginDraw(CSDRAW_3DGRAPHICS | CSDRAW_CLEARZBUFFER | CSDRAW_CLEARSCREEN); 00543 00544 // set cam 00545 graphics3D->SetWorldToCamera(csOrthoTransform()); 00546 00547 // draw quad 00548 graphics3D->DrawSimpleMesh(quad); 00549 00550 // finish draw 00551 graphics3D->FinishDraw(); 00552 } 00553 00554 // initialize persistent context setup data 00555 portalPersist.Initialize(shaderManager, graphics3D, dbgPersist); 00556 layerConfig = SingleRenderLayer(settings.shadowShaderType, settings.shadowDefaultShader); 00557 00558 // now set up our splitting scheme 00559 { 00560 float n = SMALL_Z; // near plane 00561 float f = farZ; // far plane 00562 00563 // fixed closed shadowing creates an extra slice 00564 if(doFixedCloseShadow) 00565 { 00566 splitDists.Push(n); 00567 n = fixedCloseShadow; 00568 } 00569 00570 // practical split scheme uses a blend of logarithmic and uniform split schemes 00571 const float logStep = pow(f/n, 1.0f/(float)numSplits); // logarithmic step 00572 const float uniStep = (f/n-1.0f)/(float)numSplits; // uniform step 00573 const float blend = 0.5f; // blend factor for mix(uni, log, blend) 00574 00575 // for N slices we need N+1 distances 00576 for(int i = 0; i <= numSplits; ++i) 00577 { 00578 // calculate logarithmic split distance: (far/near)^(i/N) 00579 const float logSplit = pow(logStep, i); 00580 00581 // calcualte uniform split distance: 1 + (far/near-1)*(i/N); 00582 const float uniSplit = 1.0f+i*uniStep; 00583 00584 // blend both with (1-blend)*uni + blend*log 00585 const float split = (1.0f - blend)*uniSplit + blend*logSplit; 00586 00587 // we factored out the near plane during the calculation, so multiply by it now 00588 splitDists.Push(n*split); 00589 } 00590 00591 if(doFixedCloseShadow) 00592 { 00593 // we have an extra slice for fixed close-up shadows 00594 ++numSplits; 00595 } 00596 } 00597 } 00598 00599 // typedefs 00600 typedef csHash<LightData, csWeakRef<iLight>, CS::Memory::AllocatorMalloc, 00601 csArraySafeCopyElementHandler<CS::Container::HashElement<LightData, csWeakRef<iLight> > > > LightHash; 00602 00603 // config settings 00604 csString configPrefix; 00605 00606 // shadow settings 00607 ShadowSettings settings; // shadow type specific settings 00608 int numSplits; // number of slices per frustum 00609 bool doFixedCloseShadow; // special close up shadowing 00610 bool limitedShadow; // limited shadowing (inclusive instead of exclusive) 00611 int maxPortalRecurse; // portal traversal recursion limit 00612 00613 // shadow map settings 00614 int shadowMapResD; // directional light 00615 int shadowMapResP; // point light 00616 int shadowMapResS; // spot light 00617 int shadowMapResC; // close up 00618 00619 // debug settings 00620 uint dbgSplit; 00621 uint dbgShadowTex; 00622 00623 // runtime data 00624 00625 // object refs 00626 csRef<iShaderManager> shaderManager; 00627 csRef<iGraphics3D> graphics3D; 00628 00629 // SV IDs 00630 csLightShaderVarCache svNames; 00631 LightingVariablesHelper::PersistentData lightVarsPersist; 00632 CS::ShaderVarStringID unscaleID; 00633 CS::ShaderVarStringID clipID; 00634 00635 // empty shadow map set 00636 csRef<iTextureHandle> emptySM[rtaNumAttachments]; 00637 00638 // hash holding the data for all known lights 00639 LightHash lightHash; 00640 00641 // array holding our splitting distances - those are constant for PSSM 00642 csArray<float> splitDists; 00643 00644 // persistent data for context setup 00645 typename ShadowContextSetup::PortalSetupType::PersistentData portalPersist; 00646 SingleRenderLayer layerConfig; 00647 }; 00648 00649 // structure we're using to store light data 00650 struct LightData 00651 { 00652 // constructor - sets the projection and creates frustums 00653 LightData(iLight* l, PersistentData& persist) 00654 { 00655 // set our transform 00656 light2world = l->GetMovable()->GetFullTransform(); 00657 00658 // set our update number 00659 updateNumber = l->GetMovable()->GetUpdateNumber(); 00660 00661 // set projection transform 00662 { 00663 // get cutoff distance 00664 float cutoff = l->GetCutoffDistance(); 00665 if(l->GetType() == CS_LIGHT_DIRECTIONAL) 00666 { 00667 project = CreateCropMatrix( 00668 -cutoff, cutoff, 00669 -cutoff, cutoff, 00670 -1.0f, 1.0f); 00671 } 00672 else 00673 { 00674 float outer = sqrt(0.5f); 00675 if(l->GetType() == CS_LIGHT_SPOTLIGHT) 00676 { 00677 float inner; 00678 l->GetSpotLightFalloff (inner, outer); 00679 } 00680 00681 float base = (1.0f / outer) * sqrt (1.0f - outer * outer); 00682 project = CreateFrustumMatrix( 00683 -base, base, 00684 -base, base, 00685 1.0f, cutoff); 00686 } 00687 00688 projectInverse = project.GetInverse(); 00689 } 00690 00691 // array of rotation matrices 00692 static const csMatrix3 rotations[] = 00693 { 00694 csMatrix3(), // identity 00695 csMatrix3(1, 0, 0, // -90° about x 00696 0, 0, 1, 00697 0, -1, 0), 00698 csMatrix3(1, 0, 0, // +90° about x 00699 0, 0, -1, 00700 0, 1, 0), 00701 csMatrix3(0, 0, -1, // -90° about y 00702 0, 1, 0, 00703 1, 0, 0), 00704 csMatrix3(0, 0, 1, // +90° about y 00705 0, 1, 0, 00706 -1, 0, 0), 00707 csMatrix3(1, 0, 0, // 180° about x 00708 0, -1, 0, 00709 0, 0, -1) 00710 }; 00711 00712 // get light bounding box 00713 const csBox3& lightBox = l->GetLocalBBox(); 00714 00715 // check how many frustums we'll need and allocate them 00716 int numFrustums = l->GetType() == CS_LIGHT_POINTLIGHT ? 6 : 1; 00717 frustums.SetSize(numFrustums); 00718 00719 // create frustums 00720 for(int i = 0; i < numFrustums; ++i) 00721 { 00722 Frustum& frustum = frustums[i]; 00723 00724 // init setup frame to minimum 00725 frustum.setupFrame = 0; 00726 00727 // set mesh filter mode 00728 if(persist.limitedShadow) 00729 frustum.meshFilter.SetFilterMode(CS::Utility::MESH_FILTER_INCLUDE); 00730 00731 // set transform 00732 frustum.frust2light = csReversibleTransform(rotations[i], csVector3(0)); 00733 00734 // create unbounded frustum bounding box in frustum space 00735 csBox3 boxFS(csVector3(-FLT_MAX, -FLT_MAX, 0), 00736 csVector3(FLT_MAX, FLT_MAX, FLT_MAX));; 00737 00738 // transform box to light space 00739 frustum.boxLS = boxFS / frustum.frust2light; 00740 00741 // get intersection of frustum and light box 00742 frustum.boxLS *= lightBox; 00743 } 00744 } 00745 00746 // structures representing this light 00747 00748 // structure that represents a single frustum slice 00749 // each frustum has multiple of those for each view 00750 struct Slice 00751 { 00752 // indicator whether this slice needs to be drawn, 00753 // i.e. whether it's bounding box is not empty in light space 00754 bool draw; 00755 00756 // bounding box of this slice in projection space 00757 csBox3 boxPS; 00758 00759 // bounding box of receivers in this slice in projection space 00760 csBox3 receiversPS; 00761 00762 // @@@TODO: add slice caching 00763 // - csRefs for the SVs 00764 // - cached cam for the context (culling) 00765 // tmp SVs - uninit if Slice won't be drawn 00766 csShaderVariable* projectSV; 00767 csShaderVariable* unscaleSV; 00768 csShaderVariable* dimSV; 00769 csShaderVariable* clipSV; 00770 csShaderVariable* textureSV[rtaNumAttachments]; 00771 }; 00772 00773 // structure holding all slices for a view together 00774 // with some meta-data 00775 struct Slices 00776 { 00777 uint setupFrame; 00778 csArray<Slice> slices; 00779 }; 00780 00781 // structure that represents a view frustum for our light 00782 // for point lights we have 6 of those, else just 1 00783 struct Frustum 00784 { 00785 // transform This == frustum Other == light 00786 csReversibleTransform frust2light; 00787 00788 // bounding box of this frustum in light space 00789 csBox3 boxLS; 00790 00791 // bounding box of casters in this frustum in projection space 00792 csBox3 castersPS; 00793 00794 // frame at which casters were last updated 00795 uint setupFrame; 00796 00797 // filter with all the meshes that should/shouldn't be drawn 00798 // (inclusive for limited shadow casting, exclusive otherwise) 00799 CS::Utility::MeshFilter meshFilter; 00800 00801 // hash with the slice arrays for various views 00802 csHash<Slices, csWeakRef<CS::RenderManager::RenderView> > slicesHash; 00803 }; 00804 00805 // actual light data 00806 00807 // transform This == light Other == world 00808 csReversibleTransform light2world; 00809 00810 // movable update number 00811 long updateNumber; 00812 00813 // projection matrix and it's inverse 00814 CS::Math::Matrix4 project; 00815 CS::Math::Matrix4 projectInverse; 00816 00817 // the frustums that'll represent this light 00818 csArray<Frustum> frustums; 00819 }; 00820 00821 // Shadow method specific parameters 00822 // we do all the shadow setup here as we don't want to rely on light setup 00823 struct ShadowParameters 00824 { 00825 public: 00826 ShadowParameters(PersistentData& persist, RenderTreeType& renderTree, CS::RenderManager::RenderView* rview) : 00827 persist(persist), renderTree(renderTree), rview(rview), cam(rview->GetCamera()), sector(rview->GetThisSector()), 00828 svHelper(persist.lightVarsPersist), svNames(persist.svNames) 00829 { 00830 } 00831 00832 // handle setup for a light - should be done before meshes are handled 00833 // creates frustum slices for the light in our view 00834 void operator()(iLight* light) 00835 { 00836 // check whether the light casts shadows 00837 if(light->GetFlags().Check(CS_LIGHT_NOSHADOWS)) 00838 return; 00839 00840 typename PersistentData::LightHash& lightHash = persist.lightHash; 00841 00842 // check whether this light is known already 00843 if(!lightHash.Contains(light)) 00844 { 00845 // new light, add it to cache 00846 lightHash.Put(light, LightData(light, persist)); 00847 } 00848 00849 // get light data 00850 LightData& lightData = *lightHash[light]; 00851 00852 // get current frame 00853 uint currentFrame = rview->GetCurrentFrameNumber(); 00854 00855 // go over all frustums 00856 for(size_t f = 0; f < lightData.frustums.GetSize(); ++f) 00857 { 00858 typename LightData::Frustum& frustum = lightData.frustums[f]; 00859 00860 // check whether we already have slices set up 00861 if(frustum.slicesHash.Contains(rview)) 00862 { 00863 // we have one, check whether we set slices up this frame 00864 if(frustum.slicesHash[rview]->setupFrame == currentFrame) 00865 { 00866 // nothing to be done 00867 continue; 00868 } 00869 } 00870 else 00871 { 00872 frustum.slicesHash.Put(rview, typename LightData::Slices()); 00873 } 00874 00875 // update frame setup 00876 frustum.slicesHash[rview]->setupFrame = currentFrame; 00877 00878 // get slice array 00879 csArray<typename LightData::Slice>& slices = frustum.slicesHash[rview]->slices; 00880 00881 // @@@TODO: add slice caching 00882 00883 // set size to num of slices we want 00884 slices.SetSize(persist.numSplits); 00885 00886 // get corners of our view 00887 int viewWidth = persist.graphics3D->GetWidth(); 00888 int viewHeight = persist.graphics3D->GetHeight(); 00889 csVector2 view[4] = 00890 { 00891 csVector2(0, 0), 00892 csVector2(viewWidth, 0), 00893 csVector2(viewWidth, viewHeight), 00894 csVector2(0, viewHeight) 00895 }; 00896 00897 // get transform with This == Frustum and Other == View 00898 csTransform frust2view(frustum.frust2light * lightData.light2world / cam->GetTransform()); 00899 00900 // get original camera for inverse perspective 00901 iCamera* origCam = rview->GetOriginalCamera(); 00902 00903 CS_ASSERT_MSG("PSSM shadow implementation requires a perspective camera", 00904 csRef<iPerspectiveCamera>(scfQueryInterface<iPerspectiveCamera>(origCam))); 00905 00906 // set the slice data 00907 for(size_t s = 0; s < slices.GetSize(); ++s) 00908 { 00909 typename LightData::Slice& slice = slices[s]; 00910 00911 // clear receiver box 00912 slice.receiversPS.StartBoundingBox(); 00913 00914 // create slice box in frustum space 00915 csBox3 boxFS; 00916 for(int c = 0; c < 4; ++c) 00917 { 00918 const csVector2& corner = view[c]; 00919 for(int side = 0; side < 2; ++side) 00920 { 00921 // get corner in view space 00922 // @@@FIXME: this doesn't work with portals because iCustomMatrixCamera 00923 // is used for portals which doesn't implement InvPerspective 00924 // so we get 0 for all points 00925 csVector3 vVS(origCam->InvPerspective(corner, persist.splitDists[s+side])); 00926 00927 // transform to frustum space 00928 csVector3 vFS(frust2view * vVS); 00929 00930 // add corner to frustum space box 00931 boxFS.AddBoundingVertex(vFS); 00932 } 00933 } 00934 00935 if(renderTree.IsDebugFlagEnabled(persist.dbgSplit)) 00936 { 00937 renderTree.AddDebugBBox(frustum.boxLS, 00938 lightData.light2world.GetInverse(), 00939 csColor(0, 1, 0)); 00940 } 00941 00942 // check if this is the fixed close up shadow slice if there is any 00943 if(!(persist.doFixedCloseShadow && s == 0)) 00944 { 00945 // intersect with frustum box for non-fixed slices 00946 boxFS *= frustum.frust2light * frustum.boxLS; 00947 } 00948 00949 if(renderTree.IsDebugFlagEnabled(persist.dbgSplit)) 00950 { 00951 renderTree.AddDebugBBox(boxFS, 00952 (frustum.frust2light * lightData.light2world).GetInverse(), 00953 csColor(1, 0, 0)); 00954 } 00955 00956 // check whether box is empty in frustum space 00957 slice.draw = !boxFS.Empty(); 00958 00959 if(slice.draw) 00960 { 00961 // project box 00962 slice.boxPS = ProjectBox(boxFS, lightData.project, lightData.projectInverse); 00963 00964 // don't draw this slice if it's empty in projection space 00965 slice.draw = !slice.boxPS.Empty(); 00966 } 00967 } 00968 } 00969 } 00970 00971 // handles setup for a mesh node (simply forwards processing to HandleMesh for all contained meshes) 00972 void operator()(typename RenderTreeType::MeshNode* node) 00973 { 00974 for(size_t i = 0; i < node->meshes.GetSize(); ++i) 00975 { 00976 HandleMesh(node->meshes[i]); 00977 } 00978 } 00979 00980 // final handling during a context processing - done after all meshes are processed 00981 // creates SVs required for light setup for all non-empty slices 00982 void operator()() 00983 { 00984 // get current frame 00985 uint currentFrame = rview->GetCurrentFrameNumber(); 00986 00987 // go over all lights and get the frustums 00988 typename PersistentData::LightHash::GlobalIterator it = persist.lightHash.GetIterator(); 00989 while(it.HasNext()) 00990 { 00991 // get light data 00992 csWeakRef<iLight> light; 00993 LightData& lightData = it.Next(light); 00994 00995 for(size_t f = 0; f < lightData.frustums.GetSize(); ++f) 00996 { 00997 // get our frustum 00998 typename LightData::Frustum& frustum = lightData.frustums[f]; 00999 01000 // skip frustum if it doesn't belong to our rview 01001 if(!frustum.slicesHash.Contains(rview)) 01002 continue; 01003 01004 // keep track whether any slice will be drawn at all 01005 bool draw = false; 01006 01007 // get slice array 01008 csArray<typename LightData::Slice>& slices = frustum.slicesHash[rview]->slices; 01009 01010 // check which slices will be drawn 01011 CS_ALLOC_STACK_ARRAY(bool, sliceDraw, slices.GetSize()); 01012 for(size_t s = 0; s < slices.GetSize(); ++s) 01013 { 01014 typename LightData::Slice& slice = slices[s]; 01015 01016 // check whether this slice will be used 01017 sliceDraw[s] = slice.draw && (!slice.receiversPS.Empty() || (persist.doFixedCloseShadow && s == 0)); 01018 draw |= sliceDraw[s]; 01019 } 01020 01021 // skip setup if this frustum won't be used 01022 if(!draw) 01023 continue; 01024 01025 // ensure projection space casters box and mesh filter are up to date 01026 if(frustum.setupFrame != currentFrame) 01027 { 01028 // update setup-frame 01029 frustum.setupFrame = currentFrame; 01030 01031 // update casters bounding box and filter 01032 SetupFrustum(lightData, frustum); 01033 } 01034 01035 // @@@TODO: could we use cubemaps for point lights? 01036 // @@@TODO: could we draw all slices at once using instancing? 01037 01038 // setup the slices that'll be used 01039 for(size_t s = 0; s < slices.GetSize(); ++s) 01040 { 01041 typename LightData::Slice& slice = slices[s]; 01042 01043 // not used, skip 01044 if(!sliceDraw[s]) 01045 continue; 01046 01047 // set up SVs and create the target 01048 SetupTarget(light, lightData, frustum, slice, persist.doFixedCloseShadow && s == 0); 01049 01050 // set clipping range 01051 slice.clipSV->SetValue(csVector2(persist.splitDists[s], persist.splitDists[s+1])); 01052 } 01053 } 01054 } 01055 } 01056 01057 protected: 01058 void SetupFrustum(LightData& lightData, typename LightData::Frustum& frustum) 01059 { 01060 // clear casters box 01061 frustum.castersPS.StartBoundingBox(); 01062 01063 // clear filter 01064 frustum.meshFilter.Clear(); 01065 01066 // get all meshes in the frustum box 01067 iVisibilityCuller* culler = sector->GetVisibilityCuller(); 01068 csRef<iVisibilityObjectIterator> objects = culler->VisTest(frustum.boxLS / lightData.light2world); 01069 01070 // calculate world -> light -> frustum transform 01071 csTransform frust2world(frustum.frust2light * lightData.light2world); 01072 01073 // iterate over all meshes 01074 while(objects->HasNext()) 01075 { 01076 // get object 01077 iVisibilityObject* object = objects->Next(); 01078 01079 // get mesh wrapper 01080 iMeshWrapper* meshWrapper = object->GetMeshWrapper(); 01081 01082 // get mesh flags 01083 csFlags meshFlags = meshWrapper->GetFlags(); 01084 01085 // check whether this object is a caster in our mode 01086 bool casting = (!persist.limitedShadow && !meshFlags.Check(CS_ENTITY_NOSHADOWCAST)) 01087 || ( persist.limitedShadow && meshFlags.Check(CS_ENTITY_LIMITEDSHADOWCAST)); 01088 01089 // check whether we want this mesh filtered: 01090 // for limited casting casters are included 01091 // for normal casting non-casters are excluded 01092 if(casting ^ !persist.limitedShadow) 01093 { 01094 frustum.meshFilter.AddFilterMesh(meshWrapper); 01095 } 01096 01097 // if this mesh is a caster add it's bounding box to the caster box 01098 if(casting) 01099 { 01100 // project bounding box 01101 csBox3 meshBoxPS(ProjectBox(frust2world * object->GetBBox(), lightData.project, lightData.projectInverse)); 01102 01103 // add box to casters bounding box 01104 frustum.castersPS += meshBoxPS; 01105 } 01106 } 01107 } 01108 01109 void SetupTarget(iLight* light, LightData& lightData, typename LightData::Frustum& frustum, typename LightData::Slice& slice, bool fixed) 01110 { 01111 // @@@TODO: add slice caching (persistent SVs, ...) 01112 01113 // create SVs 01114 CS::ShaderVarStringID svID; 01115 01116 // shadow map dimension 01117 svID = svNames.GetLightSVId(csLightShaderVarCache::lightShadowMapPixelSize); 01118 slice.dimSV = svHelper.CreateTempSV(svID); 01119 01120 // projection 01121 svID = svNames.GetLightSVId(csLightShaderVarCache::lightShadowMapProjection); 01122 slice.projectSV = svHelper.CreateTempSV(svID); 01123 01124 // unscale transform 01125 slice.unscaleSV = svHelper.CreateTempSV(persist.unscaleID); 01126 01127 // clipping 01128 slice.clipSV = svHelper.CreateTempSV(persist.clipID); 01129 01130 const ShadowSettings::TargetArray& targets = persist.settings.targets; 01131 for(size_t t = 0; t < targets.GetSize(); ++t) 01132 { 01133 const ShadowSettings::Target* target = targets[t]; 01134 slice.textureSV[target->attachment] = svHelper.CreateTempSV(target->svName); 01135 } 01136 01137 // setup everything 01138 01139 // box we'll use while calculating the crop matrix 01140 csBox3 objectBox(slice.boxPS); 01141 01142 // if we aren't using fixed close up shadows, our receivers in this slice 01143 // make up the object box 01144 if(!fixed) 01145 { 01146 objectBox *= slice.receiversPS; 01147 } 01148 01149 // build target box 01150 csBox3 targetBox(objectBox * frustum.castersPS); 01151 01152 // specially set z-range 01153 targetBox.SetMin(2, csMin(frustum.castersPS.MinZ(), objectBox.MinZ())); 01154 targetBox.SetMax(2, csMin(frustum.castersPS.MaxZ(), objectBox.MaxZ()) + EPSILON); 01155 01156 // check whether we have anything to draw (casters and receivers overlap in screen space) 01157 bool empty = targetBox.Empty(); 01158 01159 if(empty) 01160 { 01161 // set our object range to default for empty maps 01162 targetBox = csBox3(-1, -1, SMALL_Z, 1, 1, light->GetCutoffDistance()); 01163 } 01164 01165 // projection matrix we'll use 01166 CS::Math::Matrix4 project; 01167 01168 // unscale transform (only important for spot/point lights) 01169 csVector4 unscale; 01170 01171 // simply chain the default light projection with a crop matrix 01172 // if the light projection is orthogonal 01173 if(light->GetType() == CS_LIGHT_DIRECTIONAL) 01174 { 01175 project = CreateCropMatrix( 01176 targetBox.MinX(), targetBox.MaxX(), 01177 targetBox.MinY(), targetBox.MaxY(), 01178 targetBox.MinZ(), targetBox.MaxZ(), 01179 &unscale) * lightData.project; 01180 } 01181 // for perspective transforms build a new one with adjusted 01182 // near and far planes for improved precision 01183 else 01184 { 01185 // find the base for our frustum 01186 float outer = sqrt(0.5f); 01187 if(light->GetType() == CS_LIGHT_SPOTLIGHT) 01188 { 01189 float inner; 01190 light->GetSpotLightFalloff (inner, outer); 01191 } 01192 01193 float base = (targetBox.MinZ() / outer) * sqrt (1.0f - outer * outer); 01194 01195 // perform a frustum projection and crop in projection space 01196 project = CreateCropMatrix( 01197 targetBox.MinX(), targetBox.MaxX(), 01198 targetBox.MinY(), targetBox.MaxY(), 01199 -1.0f, 1.0f, 01200 &unscale) * CreateFrustumMatrix( 01201 -base, base, 01202 -base, base, 01203 targetBox.MinZ(), targetBox.MaxZ()); 01204 } 01205 01206 // chain frustum rotation into projection 01207 project = project * CS::Math::Matrix4(frustum.frust2light); 01208 01209 // set unscale 01210 slice.unscaleSV->SetValue(unscale); 01211 01212 // set projection 01213 slice.projectSV->SetValue(project); 01214 01215 // draw cropped box if debugging splits 01216 if(renderTree.IsDebugFlagEnabled(persist.dbgSplit)) 01217 { 01218 // find clipped box in light space 01219 csBox3 debugBox(UnprojectBox(csBox3(-1,-1,-1, 1,1,1), project)); 01220 01221 renderTree.AddDebugBBox(debugBox, 01222 lightData.light2world.GetInverse(), 01223 csColor(0, 0, 1)); 01224 } 01225 01226 // fixup matrix 01227 static const CS::Math::Matrix4 fixup( 01228 1, 0, 0, 0, 01229 0,-1, 0, 0, 01230 0, 0,-1, 0, 01231 0, 0, 0, 1 01232 ); 01233 01234 // tweak output a bit to save some operations in the shaders 01235 project = fixup * project; 01236 01237 // get our shadow map size 01238 int mapSize; 01239 if(fixed) 01240 { 01241 mapSize = persist.shadowMapResC; 01242 } 01243 else 01244 { 01245 // for non-fixed slices res may depend on the light-type 01246 switch(light->GetType()) 01247 { 01248 case CS_LIGHT_DIRECTIONAL: 01249 mapSize = persist.shadowMapResD; 01250 break; 01251 01252 case CS_LIGHT_POINTLIGHT: 01253 mapSize = persist.shadowMapResP; 01254 break; 01255 01256 case CS_LIGHT_SPOTLIGHT: 01257 mapSize = persist.shadowMapResS; 01258 break; 01259 01260 default: 01261 mapSize = persist.shadowMapResD; 01262 // unknown light-type, bail out - this MUSTN'T happen 01263 CS_ASSERT(false); 01264 break; 01265 } 01266 } 01267 01268 // set dimSV accordingly 01269 slice.dimSV->SetValue(csVector4(1.0f/mapSize,1.0f/mapSize,mapSize,mapSize)); 01270 01271 // for an empty target we don't have to draw anything 01272 if(empty) 01273 { 01274 // set textures to empty ones 01275 for(size_t t = 0; t < targets.GetSize(); ++t) 01276 { 01277 // setup target 01278 ShadowSettings::Target* target = targets[t]; 01279 iTextureHandle* tex = persist.emptySM[target->attachment]; 01280 slice.textureSV[target->attachment]->SetValue(tex); 01281 01282 // add debug draw if wanted 01283 if(renderTree.IsDebugFlagEnabled(persist.dbgShadowTex)) 01284 renderTree.AddDebugTexture(tex); 01285 } 01286 return; 01287 } 01288 01289 // create camera 01290 csRef<iCustomMatrixCamera> shadowCam = rview->GetEngine()->CreateCustomMatrixCamera(); 01291 shadowCam->SetProjectionMatrix(project); 01292 shadowCam->GetCamera()->SetTransform(lightData.light2world); 01293 01294 // create render view 01295 csRef<CS::RenderManager::RenderView> shadowView; 01296 shadowView = renderTree.GetPersistentData().renderViews.CreateRenderView(rview); 01297 shadowView->SetEngine(rview->GetEngine()); 01298 shadowView->SetThisSector(rview->GetThisSector()); 01299 shadowView->SetMeshFilter(frustum.meshFilter); 01300 shadowView->SetViewDimensions(mapSize, mapSize); 01301 01302 // set cam on our new view 01303 shadowView->SetCamera(shadowCam->GetCamera()); 01304 shadowView->SetOriginalCamera(rview->GetOriginalCamera()); 01305 01306 // set clipper 01307 { 01308 csBox2 clipBox(0, 0, mapSize, mapSize); 01309 csRef<iClipper2D> clipper; 01310 clipper.AttachNew(new csBoxClipper(clipBox)); 01311 shadowView->SetClipper(clipper); 01312 } 01313 01314 // create context 01315 typename RenderTreeType::ContextNode* context = renderTree.CreateContext(shadowView); 01316 context->drawFlags = CSDRAW_CLEARSCREEN | CSDRAW_CLEARZBUFFER; 01317 context->postEffects = persist.settings.postEffects; 01318 01319 // setup post-effects if required 01320 if(context->postEffects.IsValid()) 01321 { 01322 context->postEffects->SetupView(mapSize, mapSize, context->perspectiveFixup); 01323 } 01324 01325 // setup rendertargets 01326 for(size_t t = 0; t < targets.GetSize(); ++t) 01327 { 01328 // setup target 01329 ShadowSettings::Target* target = targets[t]; 01330 iTextureHandle* tex = target->texCache.QueryUnusedTexture(mapSize, mapSize); 01331 slice.textureSV[target->attachment]->SetValue(tex); 01332 context->renderTargets[target->attachment].texHandle = tex; 01333 01334 // add debug draw if wanted 01335 if(renderTree.IsDebugFlagEnabled(persist.dbgShadowTex)) 01336 renderTree.AddDebugTexture(tex); 01337 } 01338 01339 // create portal data 01340 typename ShadowContextSetup::PortalSetupType::ContextSetupData portalData(context); 01341 01342 // setup the new context 01343 ShadowContextSetup contextSetup(persist.layerConfig, persist.shaderManager, 01344 persist.portalPersist, persist.maxPortalRecurse, 01345 renderTree.IsDebugFlagEnabled(persist.dbgSplit)); 01346 contextSetup(*context, portalData); 01347 } 01348 01349 // handle setup for a mesh - done after all lights are known 01350 // this adds the mesh box in projection space the the frusta/slices 01351 // depending on it's settings and our shadowing mode to do scene-dependent 01352 // cropping for improved shadow map usage 01353 void HandleMesh(typename RenderTreeType::MeshNode::SingleMesh& mesh) 01354 { 01355 // check whether this mesh receives shadows - if not we're done 01356 if(mesh.meshFlags.Check(CS_ENTITY_NOSHADOWRECEIVE)) 01357 return; 01358 01359 // get mesh bbox 01360 csBox3 meshBox(mesh.renderMesh->bbox); 01361 01362 // get world2object transform so we don't compute it multiple times later 01363 csTransform world2object(mesh.renderMesh->object2world.GetInverse()); 01364 01365 // transform mesh bounding box to view space 01366 csTransform view2mesh(cam->GetTransform() * world2object); 01367 csBox3 meshBoxView(view2mesh * meshBox); 01368 01369 // for all lights 01370 typename PersistentData::LightHash::GlobalIterator it = persist.lightHash.GetIterator(); 01371 while(it.HasNext()) 01372 { 01373 // get light data 01374 csWeakRef<iLight> light; 01375 LightData& lightData = it.Next(light); 01376 01377 // calculate object -> world -> light transform 01378 csTransform light2object(lightData.light2world * world2object); 01379 01380 // for all frustums 01381 for(size_t f = 0; f < lightData.frustums.GetSize(); ++f) 01382 { 01383 // get the frustum to process 01384 typename LightData::Frustum& frustum = lightData.frustums[f]; 01385 01386 // check whether this frustum is setup for our rview 01387 if(!frustum.slicesHash.Contains(rview)) 01388 break; 01389 01390 // calculate object -> world -> light -> frustum transform 01391 csTransform frust2object(frustum.frust2light * light2object); 01392 01393 // transform mesh bounding box to frustum space 01394 // @@@TODO: we can save this projection if we first check whether we are in any slice 01395 csBox3 meshBoxPS(ProjectBox(frust2object * meshBox, lightData.project, lightData.projectInverse)); 01396 01397 if(meshBoxPS.Empty()) 01398 continue; 01399 01400 // get view-dependent furstum slices 01401 csArray<typename LightData::Slice>& slices = frustum.slicesHash[rview]->slices; 01402 01403 // for all slices 01404 for(size_t s = 0; s < slices.GetSize(); ++s) 01405 { 01406 // get frustum slice to process 01407 typename LightData::Slice& slice = slices[s]; 01408 01409 // skip this slice if it won't be drawn 01410 if(!slice.draw) 01411 continue; 01412 01413 // skip this slice if the mesh isn't part of it 01414 if(meshBoxView.MaxZ() < persist.splitDists[s] 01415 || meshBoxView.MinZ() > persist.splitDists[s+1]) 01416 continue; 01417 01418 // skip this slice if the mesh doesn't intersect with it 01419 if(!slice.boxPS.Overlap(meshBoxPS)) 01420 continue; 01421 01422 // add frustum space mesh box to receivers 01423 slice.receiversPS += meshBoxPS; 01424 } 01425 } 01426 } 01427 } 01428 01429 01430 PersistentData& persist; 01431 RenderTreeType& renderTree; 01432 CS::RenderManager::RenderView* rview; 01433 iCamera* cam; 01434 iSector* sector; 01435 01436 LightingVariablesHelper svHelper; 01437 csLightShaderVarCache& svNames; 01438 }; 01439 01440 ShadowPSSM(PersistentData& persist, 01441 const LayerConfigType& layerConfig, 01442 typename RenderTreeType::MeshNode* node, 01443 ShadowParameters& param) : persist(persist), rview(node->GetOwner().renderView) 01444 { 01445 } 01446 01447 ShadowPSSM(PersistentData& persist, CS::RenderManager::RenderView* rview) 01448 : persist(persist), rview(rview) 01449 { 01450 } 01451 01452 // forward rendering handlers 01453 01454 // set up shadowing for a mesh-light combination - used by light setup 01455 uint HandleOneLight(typename RenderTreeType::MeshNode::SingleMesh& mesh, 01456 iLight* light, CachedLightData&, 01457 csShaderVariableStack* lightStacks, 01458 uint l, uint f) 01459 { 01460 CS_ASSERT(rview); 01461 01462 // @@@FIXME: light setup is broken and cannot handle different layer spreads for different lights 01463 return f == 0 ? 1 : 0; 01464 01465 // check whether the light creates shadows 01466 // check whether the mesh receives shadows 01467 // check whether this light is known 01468 if(light->GetFlags().Check(CS_LIGHT_NOSHADOWS) 01469 || mesh.meshFlags.Check(CS_ENTITY_NOSHADOWRECEIVE) 01470 || !persist.lightHash.Contains(light)) 01471 return f == 0 ? 1 : 0; 01472 01473 // get mesh box in view space 01474 csTransform view2object(rview->GetCamera()->GetTransform() / mesh.renderMesh->object2world); 01475 csBox3 boxView(view2object * mesh.renderMesh->bbox); 01476 01477 // get sv helper 01478 LightingVariablesHelper svHelper(persist.lightVarsPersist); 01479 01480 // get our light data for that light 01481 CS_ASSERT_MSG("thou shall not create lights during context setup", persist.lightHash.Contains(light)); 01482 LightData& lightData = *persist.lightHash.GetElementPointer(light); 01483 01484 // get our frustum 01485 CS_ASSERT(f < lightData.frustums.GetSize()); 01486 typename LightData::Frustum& frustum = lightData.frustums[f]; 01487 01488 // get the slices for our view 01489 CS_ASSERT(frustum.slicesHash.Contains(rview)); 01490 csArray<typename LightData::Slice>& slices = frustum.slicesHash[rview]->slices; 01491 01492 // go over the slices and check which ones will be used for this mesh 01493 uint spread = 0; 01494 uint index = 0; 01495 for(size_t s = 0; s < slices.GetSize(); ++s) 01496 { 01497 typename LightData::Slice& slice = slices[s]; 01498 01499 // check whether this slice is active 01500 if(!slice.draw) 01501 continue; 01502 01503 // check whether the mesh goes into that slice 01504 if(boxView.MaxZ() < persist.splitDists[s] || boxView.MinZ() > persist.splitDists[s+1]) 01505 continue; 01506 01507 // we'll use this one, setup SVs 01508 svHelper.MergeAsArrayItem(lightStacks[index], slice.projectSV, l); 01509 svHelper.MergeAsArrayItem(lightStacks[index], slice.unscaleSV, l); 01510 svHelper.MergeAsArrayItem(lightStacks[index], slice.dimSV, l); 01511 // @@@TODO: why this if? 01512 if(lightStacks[index].GetSize() > slice.clipSV->GetName()) 01513 lightStacks[index][slice.clipSV->GetName()] = slice.clipSV; 01514 01515 const ShadowSettings::TargetArray& targets = persist.settings.targets; 01516 for(size_t t = 0; t < targets.GetSize(); ++t) 01517 { 01518 const ShadowSettings::Target* target = targets[t]; 01519 svHelper.MergeAsArrayItem(lightStacks[index], slice.textureSV[target->attachment], l); 01520 } 01521 01522 // now mask the spread and increment the index 01523 spread |= 1 << index; 01524 ++index; 01525 } 01526 01527 return spread; 01528 } 01529 01530 // return true when we need a final pass - used by light setup 01531 static bool NeedFinalHandleLight() { return false; } 01532 01533 // final pass called by light setup for each light 01534 // if we returned true for NeedFinalHandleLight 01535 void FinalHandleLight(iLight*, CachedLightData&) { } 01536 01537 // return which flags should be masked out for light comparison 01538 // by light setup 01539 csFlags GetLightFlagsMask() const { return csFlags(0); } 01540 01541 // return up to how many layers shadows for a light may need in light setup 01542 size_t GetLightLayerSpread() const { return persist.numSplits; } 01543 01544 // deferred handlers 01545 01546 // set up shadowing for one sub-light for deferred rendering 01547 int PushVariables(iLight* light, uint f, csShaderVariableStack& svStack) 01548 { 01549 // deferred light renderer should only use us for shadow-casting lights 01550 CS_ASSERT(!light->GetFlags().Check(CS_LIGHT_NOSHADOWS)); 01551 01552 // get sv helper 01553 LightingVariablesHelper svHelper(persist.lightVarsPersist); 01554 01555 // get our light data for that light 01556 typename PersistentData::LightHash& lightHash = persist.lightHash; 01557 01558 // particles create lights mid-frame - don't draw those 01559 // until we have data 01560 if(!lightHash.Contains(light)) 01561 return 0; 01562 01563 LightData& lightData = *lightHash.GetElementPointer(light); 01564 01565 // keep track into which index we'll push this map 01566 int index = 0; 01567 01568 // get our frustum 01569 CS_ASSERT(f < lightData.frustums.GetSize()); 01570 typename LightData::Frustum& frustum = lightData.frustums[f]; 01571 01572 // get the slices for our view 01573 CS_ASSERT(frustum.slicesHash.Contains(rview)); 01574 csArray<typename LightData::Slice>& slices = frustum.slicesHash[rview]->slices; 01575 01576 // go over the slices and check which ones will be used for this mesh 01577 for(size_t s = 0; s < slices.GetSize(); ++s) 01578 { 01579 typename LightData::Slice& slice = slices[s]; 01580 01581 // check whether this slice is active 01582 if(!slice.draw || slice.receiversPS.Empty()) 01583 continue; 01584 01585 // we'll use this one, setup SVs 01586 bool success = true; 01587 success &= svHelper.MergeAsArrayItem(svStack, slice.projectSV, index); 01588 success &= svHelper.MergeAsArrayItem(svStack, slice.unscaleSV, index); 01589 success &= svHelper.MergeAsArrayItem(svStack, slice.dimSV, index); 01590 success &= svHelper.MergeAsArrayItem(svStack, slice.clipSV, index); 01591 01592 const ShadowSettings::TargetArray& targets = persist.settings.targets; 01593 for(size_t t = 0; t < targets.GetSize(); ++t) 01594 { 01595 const ShadowSettings::Target* target = targets[t]; 01596 success &= svHelper.MergeAsArrayItem(svStack, slice.textureSV[target->attachment], index); 01597 } 01598 CS_ASSERT(success); 01599 01600 // increment index 01601 ++index; 01602 } 01603 01604 return index; 01605 } 01606 01607 uint GetSublightNum(const iLight* light) const 01608 { 01609 return light->GetType() == CS_LIGHT_POINTLIGHT ? 6 : 1; 01610 } 01611 01612 protected: 01613 PersistentData& persist; 01614 CS::RenderManager::RenderView* rview; 01615 }; 01616 } // RenderManager 01617 } // CS 01618 01619 #endif // __CS_CSPLUGINCOMMON_RENDERMANAGER_SHADOW_PSSM_H__
Generated for Crystal Space 2.1 by doxygen 1.6.1
