//////////////////////////////////////////////////////////////////////////// // // Crytek Engine Source File. // Copyright (C), Crytek Studios, 2002. // ------------------------------------------------------------------------- // File name: statobjmanshadows.cpp // Version: v1.00 // Created: 2/6/2002 by Vladimir Kajalin // Compilers: Visual Studio.NET // Description: Shadow casters/reseivers relations // ------------------------------------------------------------------------- // History: // //////////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "objman.h" #include "visareas.h" #include "3dengine.h" #include #define MIN_SHADOW_CASTER_VIEW_DIST 16 void CObjManager::MakeShadowCastersListInArea(CBasicArea*pArea, IEntityRender * pReceiver, list2 * pEntList, int dwAllowedTypes, Vec3d vLightPos, float fLightRadius) { FUNCTION_PROFILER_FAST( GetSystem(),PROFILE_3DENGINE,m_bProfilerEnabled ); bool bCmpCasterReceiverDistances = !(GetRenderer()->GetFeatures() & (RFT_DEPTHMAPS | RFT_SHADOWMAP_SELFSHADOW)); Shadowvolume ResShadowVolume; { Vec3d vBoxMin,vBoxMax; pReceiver->GetBBox(vBoxMin,vBoxMax); AABB aabbReceiver(vBoxMin,vBoxMax+Vec3d(0.01f,0.01f,0.01f)); NAABB_SV::AABB_ReceiverShadowVolume(vLightPos, aabbReceiver, ResShadowVolume);//, min(fDistFromLsToCaster+pCaster->GetRenderRadius()*4, fLightRadius)); } float fDistFromLsToReceiver = GetDistance(vLightPos, pReceiver->GetPos()); int nStaticsAllowed = int(GetCVars()->e_shadow_maps_from_static_objects>0); for(int nStatic=0; nStatic<=nStaticsAllowed; nStatic++) { list2 * pList = &pArea->m_lstEntities[nStatic]; if(nStatic && pArea->m_StaticEntitiesSorted && !(dwAllowedTypes&SMC_ALLOW_PASSIVE_SHADOWMAP_CASTERS)) pList = &pArea->m_lstStaticShadowMapCasters; for(int e=0; eCount(); e++) { IEntityRender * pCaster = (*pList)[e]; if (e+1 < pList->Count()) { IEntityRender * pNext = (*pList)[e+1]; cryPrefetchT0SSE(pNext); } if(pCaster->m_fWSMaxViewDistm_StaticEntitiesSorted) continue; else break; // break in sorted list } #ifdef _DEBUG const char * szClass = pCaster->GetEntityClassName(); const char * szName = pCaster->GetName(); #endif // _DEBUG int dwRndFlags = pCaster->GetRndFlags(); bool bTakeThisOne = false; { // take only allowed active shadow casters if((pCaster->IsStatic() && (dwAllowedTypes & SMC_STATICS )) || (!pCaster->IsStatic() && (dwAllowedTypes & SMC_DYNAMICS)) ) { if(dwAllowedTypes&SMC_ALLOW_PASSIVE_SHADOWMAP_CASTERS) bTakeThisOne = true; else if(dwRndFlags & ERF_CASTSHADOWMAPS && pCaster->GetEntityRS()->pShadowMapInfo) { ShadowMapLightSource * pFrustumContainer = pCaster->GetShadowMapFrustumContainer(); if( pFrustumContainer && pFrustumContainer->m_LightFrustums.Count() && pFrustumContainer->m_LightFrustums.Get(0)->pLs ) bTakeThisOne = true; } } } if(bTakeThisOne && pCaster != pReceiver && !(dwRndFlags&ERF_HIDDEN) && (!pCaster->GetLight() || pCaster->GetContainer())) { float fDistFromLsToCaster = GetDistance(vLightPos, pCaster->GetPos()); if(bCmpCasterReceiverDistances) if(fDistFromLsToReceiver < fDistFromLsToCaster) continue; if(fDistFromLsToReceiver - fDistFromLsToCaster > pCaster->GetRenderRadius()*4+pReceiver->GetRenderRadius()) continue; // check if caster is in receiver frustum Vec3d vBoxMin,vBoxMax; pCaster->GetBBox(vBoxMin,vBoxMax); AABB aabbCaster(vBoxMin,vBoxMax+Vec3d(0.01f,0.01f,0.01f)); bool bIntersect = NAABB_SV::Is_AABB_In_ShadowVolume(ResShadowVolume, aabbCaster); if(bIntersect && pEntList->Find(pCaster)<0) pEntList->Add(pCaster); } } } } void CObjManager::MakeShadowCastersList(IEntityRender * pReceiver, list2 * pEntList, int dwAllowedTypes, Vec3d vLightPos, float fLightRadius) { FUNCTION_PROFILER_FAST( GetSystem(),PROFILE_3DENGINE,m_bProfilerEnabled ); assert(vLightPos.len()>1); // world space pos required pEntList->Clear(); if(pReceiver->m_pVisArea && pReceiver->IsEntityAreasVisible()) MakeShadowCastersListInArea(pReceiver->m_pVisArea, pReceiver, pEntList, dwAllowedTypes, vLightPos, fLightRadius); else { // make list of sectors around // find 2d bounds in sectors array Vec3d vBoxMin,vBoxMax; pReceiver->GetBBox(vBoxMin,vBoxMax); // get 2d bounds in sectors array int min_x = (int)(((vBoxMin.x - 16.f)/CTerrain::GetSectorSize())); int min_y = (int)(((vBoxMin.y - 16.f)/CTerrain::GetSectorSize())); int max_x = (int)(((vBoxMax.x + 16.f)/CTerrain::GetSectorSize())); int max_y = (int)(((vBoxMax.y + 16.f)/CTerrain::GetSectorSize())); // limit bounds if(min_x<0) min_x=0; else if(min_x>=CTerrain::GetSectorsTableSize()) min_x=CTerrain::GetSectorsTableSize()-1; if(min_y<0) min_y=0; else if(min_y>=CTerrain::GetSectorsTableSize()) min_y=CTerrain::GetSectorsTableSize()-1; if(max_x<0) max_x=0; else if(max_x>=CTerrain::GetSectorsTableSize()) max_x=CTerrain::GetSectorsTableSize()-1; if(max_y<0) max_y=0; else if(max_y>=CTerrain::GetSectorsTableSize()) max_y=CTerrain::GetSectorsTableSize()-1; m_lstTmpSectors_MELFP.Clear(); m_lstTmpSectors_MELFP.Add(m_pTerrain->m_arrSecInfoTable[0][0]); for(int x=min_x; x<=max_x && x>=0 && x<=CTerrain::GetTerrainSize(); x++) for(int y=min_y; y<=max_y && y>=0 && y<=CTerrain::GetTerrainSize(); y++) { CSectorInfo * pSectorInfo = m_pTerrain->m_arrSecInfoTable[x][y]; // check if sector cast shadow to the receiver if(m_lstTmpSectors_MELFP.Find(pSectorInfo)<0 && pSectorInfo) { Shadowvolume sv; Vec3d vBoxMin,vBoxMax; pReceiver->GetBBox(vBoxMin,vBoxMax); AABB aabbReceiver(vBoxMin,vBoxMax); AABB aabbCaster(pSectorInfo->m_vBoxMin,pSectorInfo->m_vBoxMax); aabbCaster.max.z += 0.01f; NAABB_SV::AABB_ShadowVolume(vLightPos, aabbCaster, sv, fLightRadius); bool bIntersect = NAABB_SV::Is_AABB_In_ShadowVolume(sv, aabbReceiver); if(bIntersect) m_lstTmpSectors_MELFP.Add(pSectorInfo); } } // make list of entities for(int s=0; s * pLSourceInstances, int dwAllowedTypes, CDLight * pLight) { if(!GetCVars()->e_shadow_maps || obj_distance > pReceiver->GetRenderRadius()*32) return; // get entities in area lstEntList_MLSMCIA.Clear(); if(dwAllowedTypes) MakeShadowCastersList(pReceiver, &lstEntList_MLSMCIA, dwAllowedTypes, pLight->m_Origin, pLight->m_fRadius); lstEntList_MLSMCIA.Delete(pReceiver); // add this entity if self shadowing allowed if( GetCVars()->e_shadow_maps_self_shadowing && pReceiver->GetRndFlags() & ERF_SELFSHADOW && GetRenderer()->GetFeatures() & (RFT_DEPTHMAPS | RFT_SHADOWMAP_SELFSHADOW)) lstEntList_MLSMCIA.InsertBefore(pReceiver,0); // make list of shadow casters for(int e=0; eGetEntityClassName(); const char * szName = lstEntList_MLSMCIA[e]->GetName(); if( !lstEntList_MLSMCIA[e]->GetShadowMapFrustumContainer() || !lstEntList_MLSMCIA[e]->GetShadowMapFrustumContainer()->m_LightFrustums.Count() || !(lstEntList_MLSMCIA[e]->GetRndFlags()&ERF_CASTSHADOWMAPS) ) continue; ShadowMapLightSourceInstance LightSourceInfo; LightSourceInfo.m_pLS = lstEntList_MLSMCIA[e]->GetShadowMapFrustumContainer(); LightSourceInfo.m_vProjTranslation = lstEntList_MLSMCIA[e]->GetPos(); LightSourceInfo.m_fProjScale = lstEntList_MLSMCIA[e]->GetScale(); Vec3d vThisEntityPos = pReceiver->GetPos(); LightSourceInfo.m_fDistance = GetDistance(vThisEntityPos,lstEntList_MLSMCIA[e]->GetPos()) - lstEntList_MLSMCIA[e]->GetRenderRadius()/3; LightSourceInfo.m_pReceiver = pReceiver; if(!LightSourceInfo.m_pLS->m_LightFrustums.Count())// || !LightSourceInfo.m_pLS->m_LightFrustums[0].depth_tex_id) continue; pLSourceInstances->Add(LightSourceInfo); } // select only closest pLSourceInstances->SortByDistanceMember(true,1); while(pLSourceInstances->Count()>GetCVars()->e_shadow_maps_max_casters_per_object) pLSourceInstances->DeleteLast(); } ShadowMapFrustum * CObjManager::MakeEntityShadowFrustum(ShadowMapFrustum * pFrustum, ShadowMapLightSource * pLs, IEntityRender * pEnt, EShadowType nShadowType, int dwAllowedTypes) { Vec3d vMin,vMax; pEnt->GetBBox(vMin,vMax); Vec3d visual_center = (vMin+vMax)*0.5f - pEnt->GetPos(); float model_radius = GetDistance(vMax,vMin) * 0.5f; float model_radiusXY = GetDistance(Vec3d(vMax.x,vMax.y,0),Vec3d(vMin.x,vMin.y,0)) * 0.5f; pFrustum->target = visual_center; Vec3d dir = pFrustum->target - pLs->vSrcPos; float dist = dir.Length(); pFrustum->ProjRatio = model_radiusXY/model_radius; if(pFrustum->ProjRatio>1.f) pFrustum->ProjRatio=1.f; pFrustum->FOV = (float)RAD2DEG(cry_atanf((model_radius+0.25f)/dist))*1.9f; if(pFrustum->FOV>120.f) pFrustum->FOV=120.f; pFrustum->min_dist = dist - (model_radius + ((dwAllowedTypes&SMC_ALLOW_PASSIVE_SHADOWMAP_CASTERS) ? 32 : 0)); if(pFrustum->min_dist<0.25) pFrustum->min_dist=0.25; pFrustum->max_dist = dist + ((dwAllowedTypes&SMC_ALLOW_PASSIVE_SHADOWMAP_CASTERS) ? -0.75f : model_radius); pFrustum->pLs = pLs; pFrustum->shadow_type = nShadowType; // make entities list if(pFrustum->pEntityList) pFrustum->pEntityList->Clear(); else pFrustum->pEntityList = new list2; if(dwAllowedTypes) MakeShadowCastersList(pEnt, pFrustum->pEntityList, dwAllowedTypes, pLs->vSrcPos+pEnt->GetPos(), pLs->fRadius); else pFrustum->pEntityList->Add(pEnt); return pFrustum; } static int Cmp_Shadow_Size(const void* v1, const void* v2) { CStatObjInst* p1 = *((CStatObjInst**)(v1)); CStatObjInst* p2 = *((CStatObjInst**)(v2)); if(p1->m_fScale > p2->m_fScale) return -1; else if(p1->m_fScale < p2->m_fScale) return 1; return 0; } static int __cdecl CObjManager_Cmp_EntSize(const void* v1, const void* v2) { IEntityRender* p1 = *((IEntityRender**)(v1)); IEntityRender* p2 = *((IEntityRender**)(v2)); if(p1->GetRenderRadius() > p2->GetRenderRadius()) return -1; else if(p1->GetRenderRadius() < p2->GetRenderRadius()) return 1; return (p1>p2) ? 1 : -1; } void CObjManager::DrawAllShadowsOnTheGroundInSector(list2 * pEntList) { /////////////////////////////////////////////////////////////////////////////////////////////////////////// // Draw entity shadows /////////////////////////////////////////////////////////////////////////////////////////////////////////// GetRenderer()->ResetToDefault(); GetRenderer()->ClearDepthBuffer(); GetRenderer()->ClearColorBuffer(Vec3d(1,1,1)); GetRenderer()->SetClearColor(Vec3d(1,1,1)); // sort by size to draw small shadows last or skip them if(pEntList && pEntList->Count()) qsort(&(*pEntList)[0], (*pEntList).Count(), sizeof((*pEntList)[0]), CObjManager_Cmp_EntSize); GetRenderer()->EF_StartEf(); int nRealCout=0; for( int i=0; iCount(); i++ ) { IEntityRender * pEnt = (*pEntList)[i]; if(!(pEnt->GetRndFlags() & ERF_HIDDEN)) if(pEnt->GetRndFlags() & ERF_CASTSHADOWINTOLIGHTMAP) if(!pEnt->GetEntityVisArea()) { assert(m_nRenderStackLevel == 0); pEnt->SetDrawFrame(-100,m_nRenderStackLevel); int nFlags = pEnt->GetRndFlags(); pEnt->SetRndFlags(nFlags|ERF_CASTSHADOWMAPS|ERF_RECVSHADOWMAPS); pEnt->SetRndFlags(ERF_CASTSHADOWINTOLIGHTMAP,false); int nRealLightsNum = ((C3DEngine*)Get3DEngine())->GetRealLightsNum(); assert(nRealLightsNum==1); RenderObject(pEnt,0,1,true,GetViewCamera(),NULL,0,0,false,pEnt->GetMaxViewDist()); RenderEntitiesShadowMapsOnTerrain(true, 0); // increase frame id to help shadow map menager in renderer unsigned short * pPtr2FrameID = (unsigned short *)GetRenderer()->EF_Query(EFQ_Pointer2FrameID); if(pPtr2FrameID) (*pPtr2FrameID)++; pEnt->SetRndFlags(nFlags); nRealCout++; if((GetRenderer()->GetType() == R_GL_RENDERER) && nRealCout>=64) // maximum number of shadow maps in frame is limited in ogl to MAX_DYNAMIC_SHADOW_MAPS_COUNT break; } } GetRenderer()->EF_EndEf3D(SHDF_SORT); // free shadow pass leafbuffers for( int i=0; iCount(); i++ ) { IEntityRender * pEnt = (*pEntList)[i]; IEntityRenderState * pEntRendState = pEnt->m_pEntityRenderState; if(pEntRendState && pEntRendState->pShadowMapInfo && pEntRendState->pShadowMapInfo->pShadowMapLeafBuffersList) { for(int i=0; ipShadowMapInfo->pShadowMapLeafBuffersList->Count(); i++) { if(pEntRendState->pShadowMapInfo->pShadowMapLeafBuffersList->GetAt(i)) { GetRenderer()->DeleteLeafBuffer(pEntRendState->pShadowMapInfo->pShadowMapLeafBuffersList->GetAt(i)); pEntRendState->pShadowMapInfo->pShadowMapLeafBuffersList->GetAt(i)=0; } } delete pEntRendState->pShadowMapInfo->pShadowMapLeafBuffersList; pEntRendState->pShadowMapInfo->pShadowMapLeafBuffersList=0; } } }