//////////////////////////////////////////////////////////////////////////// // // Crytek Engine Source File. // Copyright (C), Crytek Studios, 2002. // ------------------------------------------------------------------------- // File name: statobjmandraw.cpp // Version: v1.00 // Created: 28/5/2001 by Vladimir Kajalin // Compilers: Visual Studio.NET // Description: Render all entities in the sector together with shadows // ------------------------------------------------------------------------- // History: // //////////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "StatObj.h" #include "objman.h" #include "visareas.h" #include "terrain_sector.h" #include "3dengine.h" #include "cbuffer.h" #include "3dengine.h" #include "cryparticlespawninfo.h" #include "lightman.h" #include #define MAX_SHADOW_VOLUME_LEN 8.f void CObjManager::ProcessActiveShadowReceiving(IEntityRender * pEnt, float fEntDistance, CDLight * pLight, bool bLMapGeneration) { ShadowMapLightSource * & pLsource = pEnt->GetEntityRS()->pShadowMapInfo->pShadowMapFrustumContainerPassiveCasters; if(!pLsource) { // make lsource pLsource = new ShadowMapLightSource; ShadowMapFrustum lof; lof.pOwner = pEnt; pLsource->m_LightFrustums.Add(lof); } // get needed size of shadow map int nTexSize = GetCVars()->e_max_shadow_map_size/2*GetCVars()->e_active_shadow_maps_receving; float fDistToTheCamera = pEnt->m_arrfDistance[m_nRenderStackLevel]; Vec3d vBoxMin,vBoxMax; pEnt->GetBBox(vBoxMin,vBoxMax); float fCasterRadius = (vBoxMax-vBoxMin).len()*0.5f; while( nTexSize*fDistToTheCamera > fCasterRadius*GetCVars()->e_shadow_maps_size_ratio ) nTexSize /= 2; // get obj space light pos Vec3d vObjSpaceLightPos; Matrix44 objMatrix; IStatObj * pStatObj = pEnt->GetEntityStatObj(0, &objMatrix); if(pStatObj) { objMatrix.Invert44(); vObjSpaceLightPos = objMatrix.TransformVectorOLD(pLight ? pLight->m_Origin : m_p3DEngine->GetSunPosition()); } else vObjSpaceLightPos = (pLight ? pLight->m_Origin : m_p3DEngine->GetSunPosition()) - pEnt->GetPos(); // make shadow map frustum for receiving (include all objects into frustum) assert(pLsource->GetShadowMapFrustum()); if(pLsource->GetShadowMapFrustum()) { if( nTexSize != pLsource->GetShadowMapFrustum()->nTexSize || !IsEquivalent(pLsource->vObjSpaceSrcPos, vObjSpaceLightPos, 0.001f) || pEnt->HasChanged() || pLsource->GetShadowMapFrustum()->depth_tex_id==0 ) { pLsource->GetShadowMapFrustum()->bUpdateRequested = true; pLsource->vSrcPos = (pLight ? pLight->m_Origin : m_p3DEngine->GetSunPosition()) - pEnt->GetPos(); pLsource->vObjSpaceSrcPos = vObjSpaceLightPos; pLsource->fRadius = pLight ? pLight->m_fRadius : 5000000; ShadowMapFrustum * lof = MakeEntityShadowFrustum(pLsource->GetShadowMapFrustum(), pLsource, pEnt, EST_DEPTH_BUFFER, SMC_DYNAMICS | SMC_STATICS | SMC_ALLOW_PASSIVE_SHADOWMAP_CASTERS); pLsource->GetShadowMapFrustum()->nTexSize = nTexSize; lof->dwFlags = SMFF_ACTIVE_SHADOW_MAP; } pLsource->GetShadowMapFrustum()->nDLightId = pLsource->nDLightId = pLight ? pLight->m_Id : -1; } // add frustum to the list of shadow casters assert(pLsource); { ShadowMapLightSourceInstance LightSourceInfo; LightSourceInfo.m_pLS = pLsource; LightSourceInfo.m_vProjTranslation = pEnt->GetPos(); LightSourceInfo.m_fProjScale = 1.f; Vec3d vThisEntityPos = pEnt->GetPos(); LightSourceInfo.m_fDistance = 0; LightSourceInfo.m_pReceiver = pEnt; if(LightSourceInfo.m_pLS->m_LightFrustums.Count() && LightSourceInfo.m_pLS->m_LightFrustums[0].pEntityList && LightSourceInfo.m_pLS->m_LightFrustums[0].pEntityList->Count()) { pEnt->GetEntityRS()->pShadowMapInfo->pShadowMapCasters->Add(LightSourceInfo); RequestEntityShadowMapGeneration(pEnt); } } } void CObjManager::RequestEntityShadowMapGeneration(IEntityRender * pEntityRnd) { CCObject * pObj = GetIdentityCCObject(); pObj->m_pShadowCasters = pEntityRnd->GetShadowMapCasters(); GetRenderer()->EF_AddEf(0, m_p3DEngine->m_pREShadowMapGenerator, m_p3DEngine->m_pSHShadowMapGen, NULL, pObj, 0); if(GetCVars()->e_shadow_maps_frustums && pEntityRnd->GetShadowMapFrustumContainer()) pEntityRnd->GetShadowMapFrustumContainer()->m_LightFrustums.Get(0)->DrawFrustum(GetRenderer(), pEntityRnd->GetPos(), 1.f); if(GetCVars()->e_shadow_maps_frustums && pEntityRnd->GetShadowMapFrustumContainerPassiveCasters()) pEntityRnd->GetShadowMapFrustumContainerPassiveCasters()->m_LightFrustums.Get(0)->DrawFrustum(GetRenderer(), pEntityRnd->GetPos(), 1.f); } char BoxSides[0x40*8] = { 0,0,0,0, 0,0,0,0, //00 0,4,6,2, 0,0,0,4, //01 7,5,1,3, 0,0,0,4, //02 0,0,0,0, 0,0,0,0, //03 0,1,5,4, 0,0,0,4, //04 0,1,5,4, 6,2,0,6, //05 7,5,4,0, 1,3,0,6, //06 0,0,0,0, 0,0,0,0, //07 7,3,2,6, 0,0,0,4, //08 0,4,6,7, 3,2,0,6, //09 7,5,1,3, 2,6,0,6, //0a 0,0,0,0, 0,0,0,0, //0b 0,0,0,0, 0,0,0,0, //0c 0,0,0,0, 0,0,0,0, //0d 0,0,0,0, 0,0,0,0, //0e 0,0,0,0, 0,0,0,0, //0f 0,2,3,1, 0,0,0,4, //10 0,4,6,2, 3,1,0,6, //11 7,5,1,0, 2,3,0,6, //12 0,0,0,0, 0,0,0,0, //13 0,2,3,1, 5,4,0,6, //14 1,5,4,6, 2,3,0,6, //15 7,5,4,0, 2,3,0,6, //16 0,0,0,0, 0,0,0,0, //17 0,2,6,7, 3,1,0,6, //18 0,4,6,7, 3,1,0,6, //19 7,5,1,0, 2,6,0,6, //1a 0,0,0,0, 0,0,0,0, //1b 0,0,0,0, 0,0,0,0, //1c 0,0,0,0, 0,0,0,0, //1d 0,0,0,0, 0,0,0,0, //1e 0,0,0,0, 0,0,0,0, //1f 7,6,4,5, 0,0,0,4, //20 0,4,5,7, 6,2,0,6, //21 7,6,4,5, 1,3,0,6, //22 0,0,0,0, 0,0,0,0, //23 7,6,4,0, 1,5,0,6, //24 0,1,5,7, 6,2,0,6, //25 7,6,4,0, 1,3,0,6, //26 0,0,0,0, 0,0,0,0, //27 7,3,2,6, 4,5,0,6, //28 0,4,5,7, 3,2,0,6, //29 6,4,5,1, 3,2,0,6, //2a 0,0,0,0, 0,0,0,0, //2b 0,0,0,0, 0,0,0,0, //2c 0,0,0,0, 0,0,0,0, //2d 0,0,0,0, 0,0,0,0, //2e 0,0,0,0, 0,0,0,0, //2f 0,0,0,0, 0,0,0,0, //30 0,0,0,0, 0,0,0,0, //31 0,0,0,0, 0,0,0,0, //32 0,0,0,0, 0,0,0,0, //33 0,0,0,0, 0,0,0,0, //34 0,0,0,0, 0,0,0,0, //35 0,0,0,0, 0,0,0,0, //36 0,0,0,0, 0,0,0,0, //37 0,0,0,0, 0,0,0,0, //38 0,0,0,0, 0,0,0,0, //39 0,0,0,0, 0,0,0,0, //3a 0,0,0,0, 0,0,0,0, //3b 0,0,0,0, 0,0,0,0, //3c 0,0,0,0, 0,0,0,0, //3d 0,0,0,0, 0,0,0,0, //3e 0,0,0,0, 0,0,0,0, //3f }; float CObjManager::GetSortOffset( const Vec3d & vPos, const Vec3d & vCamPos, float fUserWaterLevel ) { float fWaterLevel = fUserWaterLevel>WATER_LEVEL_UNKNOWN ? fUserWaterLevel : m_pTerrain->GetWaterLevel(); if ((0.5f-m_nRenderStackLevel)*(vCamPos.z - fWaterLevel)*(vPos.z - fWaterLevel)>0) return -1000000; else return 1000000; } void CObjManager::RenderObjectVegetationNonCastersNoFogVolume( IEntityRender * pEntityRS,uint nDLightMask, const CCamera & EntViewCamera, bool bNotAllInFrustum, float fMaxViewDist, IEntityRenderInfo * pEntInfo) { assert(pEntInfo); Vec3d vCenter = pEntInfo->m_vWSCenter; float fEntDistance = pEntInfo->m_fEntDistance; // do not draw if marked to be not drawn unsigned int nRenderFlags = pEntityRS->GetRndFlags(); // set only x strongest light bits and do frustum test Vec3d vLightIntensity(0,0,0); Vec3d vBoxMin = pEntityRS->m_vWSBoxMin, vBoxMax = pEntityRS->m_vWSBoxMax; float fEntRadius = pEntityRS->m_fWSRadius; IRenderer * pRend = GetRenderer(); CVars * pCVars = GetCVars(); CDLight * pStrongestLightForTranspGeom = NULL; // check only original bbox if(bNotAllInFrustum && !EntViewCamera.IsAABBVisibleFast( AABB( vBoxMin, vBoxMax ))) return; // for big objects (registered in sector 00) - get light mask from 00 sector if(pEntityRS->m_pSector == m_pTerrain->m_arrSecInfoTable[0][0]) { CSectorInfo * pSectorInfo = m_pTerrain->GetSecInfo(vCenter); if(pSectorInfo) nDLightMask = pSectorInfo->m_nDynLightMask; } list2 * pSources = m_p3DEngine->GetDynamicLightSources(); if(nDLightMask==1 && pSources->Count() && pSources->GetAt(0).m_Flags & DLF_SUN) pStrongestLightForTranspGeom = pSources->Get(0); else if(nDLightMask) m_p3DEngine->CheckDistancesToLightSources(nDLightMask, vCenter, fEntRadius, pEntityRS, 8, &pStrongestLightForTranspGeom, 1, &vLightIntensity); // check cvars assert(pEntityRS->GetEntityRenderType() == eERType_Vegetation); // check all possible occlusions for outdoor objects if(fEntRadius && pCVars->e_portals!=3) { // test occlusion of outdoor objects by mountains if(m_fZoomFactor && fEntDistance/m_fZoomFactor > 48 && !pEntityRS->m_pVisArea) if(IsBoxOccluded(vBoxMin, vBoxMax, fEntDistance/m_fZoomFactor, &pEntityRS->OcclState)) return; // test occl by antiportals if(GetVisAreaManager()->IsOccludedByOcclVolumes(vBoxMin,vBoxMax, pEntityRS->m_pVisArea!=NULL)) return; } // store for later use (like tree sprites rendering) pEntityRS->m_arrfDistance[m_nRenderStackLevel] = fEntDistance; // mark as rendered in this frame pEntityRS->SetDrawFrame( GetFrameID(), m_nRenderStackLevel ); // process object particles (rain drops) if(GetCVars()->e_rain_amount) ProcessEntityParticles(pEntityRS,fEntDistance); // set render params SRendParams DrawParams; // DrawParams.fSQDistance = fEntDistance*fEntDistance; DrawParams.nDLightMask = nDLightMask; DrawParams.nFogVolumeID = 0; DrawParams.fDistance = fEntDistance; DrawParams.vAmbientColor = m_vOutdoorAmbientColor; if(GetCVars()->e_objects_fade_on_distance) DrawParams.fAlpha = min(1.f,(1.f - fEntDistance / fMaxViewDist)*6); // modulate ambient color by envir color Vec3d vWorldColor = Get3DEngine()->GetWorldColor(); DrawParams.vAmbientColor.x *= vWorldColor.x; DrawParams.vAmbientColor.y *= vWorldColor.y; DrawParams.vAmbientColor.z *= vWorldColor.z; const Vec3d vCamPos = EntViewCamera.GetPos(); DrawParams.fCustomSortOffset = GetSortOffset(vCenter,vCamPos); // ignore ambient color set by artist DrawParams.dwFObjFlags = FOB_IGNOREMATERIALAMBIENT; if(pRend->EF_GetHeatVision()) DrawParams.nShaderTemplate = EFT_HEATVISION; // draw bbox if (pCVars->e_bboxes) GetRenderer()->Draw3dBBox(pEntityRS->m_vWSBoxMin, pEntityRS->m_vWSBoxMax); // set light mask for transparent geometry if(pStrongestLightForTranspGeom) { DrawParams.nStrongestDLightMask = 1<m_Id; if(DrawParams.fAlpha<1.f) // set it for entire object if entire object is transparent DrawParams.nDLightMask = DrawParams.nStrongestDLightMask & DrawParams.nDLightMask; } pEntityRS->DrawEntity(DrawParams); } void CObjManager::RenderObject( IEntityRender * pEntityRS, int nFogVolumeID, uint nDLightMask, bool bLMapGeneration, const CCamera & EntViewCamera, Vec3d * pvAmbColor, Vec3d * pvDynAmbColor, VolumeInfo * pFogVolume, bool bNotAllInFrustum, float fMaxViewDist, IEntityRenderInfo * pEntInfo) { // FUNCTION_PROFILER_FAST( GetSystem(),PROFILE_3DENGINE,m_bProfilerEnabled ); #ifdef _DEBUG const char * szName = pEntityRS->GetName(); const char * szClassName = pEntityRS->GetEntityClassName(); if(strstr(szName, "Player")) { pEntityRS->SetRndFlags(ERF_HIDDEN, !GetCVars()->e_player); if(!GetCVars()->e_player) return; } // be sure fps weapon is not rendered this way assert(!pEntityRS->GetEntityCharacter(0) || !(pEntityRS->GetEntityCharacter(0)->GetFlags() & CS_FLAG_DRAW_NEAR)); // is position valid if(!_finite(pEntityRS->GetPos().x) || !_finite(pEntityRS->GetPos().y)) { Warning(0,0,"Warning: CObjManager::RenderObject: Entity position undefined: %s", szName); return; } #endif // _DEBUG float fEntDistance; Vec3d vCenter; const Vec3d vCamPos = EntViewCamera.GetPos(); if(pEntInfo) { vCenter = pEntInfo->m_vWSCenter; fEntDistance = pEntInfo->m_fEntDistance; } else { // find distance to the camera vCenter.x = (pEntityRS->m_vWSBoxMin.x+pEntityRS->m_vWSBoxMax.x)*0.5f; vCenter.y = (pEntityRS->m_vWSBoxMin.y+pEntityRS->m_vWSBoxMax.y)*0.5f; vCenter.z = (pEntityRS->m_vWSBoxMin.z+pEntityRS->m_vWSBoxMax.z)*0.5f; // check max view distance sq const float dx = vCamPos.x-vCenter.x; const float dy = vCamPos.y-vCenter.y; const float fEntDistanceSQ = (dx*dx+dy*dy); // must be 2d for sprites #ifdef _DEBUG if(fEntDistanceSQ<0 || !_finite(fEntDistanceSQ)) { Warning(0,0,"Warning: CObjManager::RenderObject: Entity bbox undefined: %s, fEntDistanceSQ=%.2f", szName, fEntDistanceSQ); return; } #endif // _DEBUG if(fEntDistanceSQ > fMaxViewDist*fMaxViewDist && m_fZoomFactor >= 0.99f && !bLMapGeneration) return; // check max view distance fEntDistance = cry_sqrtf(fEntDistanceSQ)*m_fZoomFactor; assert(fEntDistance>=0 && _finite(fEntDistance)); if(fEntDistance > fMaxViewDist) if(!bLMapGeneration) return; // do not cull objects during lightmap generation if(bLMapGeneration) fEntDistance = 0; // early sphere test agains left and right camera planes if( bNotAllInFrustum ) if( EntViewCamera.GetFrustumPlane(FR_PLANE_RIGHT)->DistFromPlane(vCenter) > (TERRAIN_SECTORS_MAX_OVERLAPPING+pEntityRS->m_fWSRadius) || EntViewCamera.GetFrustumPlane(FR_PLANE_LEFT)->DistFromPlane(vCenter) > (TERRAIN_SECTORS_MAX_OVERLAPPING+pEntityRS->m_fWSRadius) ) if(!bLMapGeneration) return; } // do not draw if already rendered in this frame if(pEntityRS->GetDrawFrame(m_nRenderStackLevel) == GetFrameID()) { /* if(pEntityRS->GetEntityVisArea()) { // process object scissor settings if(pEntityRS->GetShadowFrame(m_nRenderStackLevel) == GetFrameID()) { // merge with previous scissor from this frame (needed when object is visible thru 2 portals) pEntityRS->GetEntityRS()->nScissorX1 = min(pEntityRS->GetEntityRS()->nScissorX1,EntViewCamera.m_ScissorInfo.x1); pEntityRS->GetEntityRS()->nScissorY1 = min(pEntityRS->GetEntityRS()->nScissorY1,EntViewCamera.m_ScissorInfo.y1); pEntityRS->GetEntityRS()->nScissorX2 = max(pEntityRS->GetEntityRS()->nScissorX2,EntViewCamera.m_ScissorInfo.x2); pEntityRS->GetEntityRS()->nScissorY2 = max(pEntityRS->GetEntityRS()->nScissorY2,EntViewCamera.m_ScissorInfo.y2); } else { // set new pEntityRS->GetEntityRS()->nScissorX1 = EntViewCamera.m_ScissorInfo.x1; pEntityRS->GetEntityRS()->nScissorY1 = EntViewCamera.m_ScissorInfo.y1; pEntityRS->GetEntityRS()->nScissorX2 = EntViewCamera.m_ScissorInfo.x2; pEntityRS->GetEntityRS()->nScissorY2 = EntViewCamera.m_ScissorInfo.y2; } }*/ return; // already drawn } // do not draw if marked to be not drawn unsigned int nRenderFlags = pEntityRS->GetRndFlags(); if(nRenderFlags&ERF_HIDDEN || pEntityRS->m_dwRndFlags&ERF_MERGED) return; // for big objects (registered in sector 00) - get light mask from 00 sector if(pEntityRS->m_pSector == m_pTerrain->m_arrSecInfoTable[0][0]) { CSectorInfo * pSectorInfo = m_pTerrain->GetSecInfo(vCenter); if(pSectorInfo) nDLightMask = pSectorInfo->m_nDynLightMask; } // set only x strongest light bits and do frustum test Vec3d vLightIntensity(0,0,0); Vec3d vBoxMin = pEntityRS->m_vWSBoxMin, vBoxMax = pEntityRS->m_vWSBoxMax; bool bEntityBodyVisible = true; CDLight * pStrongestShadowLight = 0; float fEntRadius = pEntityRS->m_fWSRadius; IRenderer * pRend = GetRenderer(); CVars * pCVars = GetCVars(); CDLight * pStrongestLightForTranspGeom = NULL; if(pCVars->e_stencil_shadows && (nRenderFlags&ERF_CASTSHADOWVOLUME || nRenderFlags&ERF_CASTSHADOWMAPS) && fEntRadius) { // adjust bbox by shadow before frustum check list2 * pSources = m_p3DEngine->GetDynamicLightSources(); if(nDLightMask==1 && pSources->Count() && pSources->GetAt(0).m_Flags & DLF_SUN) pStrongestShadowLight = pStrongestLightForTranspGeom = pSources->Get(0); else if(nDLightMask) pStrongestShadowLight = m_p3DEngine->CheckDistancesToLightSources(nDLightMask, vCenter, fEntRadius, pEntityRS, 8, &pStrongestLightForTranspGeom, 1, &vLightIntensity); bool bAdjustBBoxByShadowSize = (nRenderFlags&ERF_CASTSHADOWVOLUME) && pStrongestShadowLight && (pStrongestShadowLight->m_Flags & DLF_CASTSHADOW_VOLUME); bAdjustBBoxByShadowSize |= (nRenderFlags&ERF_CASTSHADOWMAPS) && pStrongestShadowLight && (pStrongestShadowLight->m_Flags & DLF_CASTSHADOW_MAPS); bool bShadowIsVisible = false; if(pStrongestShadowLight) { // shadow bbox frustum test float fShadowVolumeExtent = CalculateEntityShadowVolumeExtent(pEntityRS, pStrongestShadowLight); pEntityRS->GetBBox(vBoxMin, vBoxMax); // project geometry bbox MakeShadowBBox(vBoxMin, vBoxMax, pStrongestShadowLight->m_Origin, pStrongestShadowLight->m_fRadius, fShadowVolumeExtent); bShadowIsVisible = true; if(!(nRenderFlags&ERF_DONOTCHECKVIS)) if( bNotAllInFrustum ) if(!EntViewCamera.IsAABBVisible_exact(AABB( vBoxMin, vBoxMax )) && !bLMapGeneration) if(!pEntityRS->GetLight() || pEntityRS->GetLight()->m_pOwner!=pEntityRS) bShadowIsVisible = false; // shadow is not visible if(pCVars->e_stencil_shadows==2) pRend->Draw3dBBox(vBoxMin,vBoxMax); pEntityRS->GetRenderBBox(vBoxMin, vBoxMax); // restore bbox } bEntityBodyVisible = pEntityRS->m_bForceBBox || !bNotAllInFrustum || (nRenderFlags&ERF_DONOTCHECKVIS) || EntViewCamera.IsAABBVisible_exact( AABB( vBoxMin, vBoxMax )); if(!bEntityBodyVisible && !pStrongestShadowLight && !bLMapGeneration && !bShadowIsVisible) return; } else { // check only original bbox bEntityBodyVisible = pEntityRS->m_bForceBBox || !bNotAllInFrustum || (nRenderFlags&ERF_DONOTCHECKVIS) || EntViewCamera.IsAABBVisible_exact( AABB( vBoxMin, vBoxMax )); if(!bEntityBodyVisible && !bLMapGeneration) return; list2 * pSources = m_p3DEngine->GetDynamicLightSources(); if(nDLightMask==1 && pSources->Count() && pSources->GetAt(0).m_Flags & DLF_SUN) pStrongestShadowLight = pStrongestLightForTranspGeom = pSources->Get(0); else if(nDLightMask) pStrongestShadowLight = m_p3DEngine->CheckDistancesToLightSources(nDLightMask, vCenter, fEntRadius, pEntityRS, 8, &pStrongestLightForTranspGeom, 1, &vLightIntensity); } if(fEntDistance > 2000.f && !bLMapGeneration) { /* Warning(0,0, "Warning: CObjManager::RenderObject: Invalid object skipped: %s, fEntDistance=%.2f, GetPos() = (%.2f,%.2f,%.2f)", pEntityRS->GetName(), fEntDistance, pEntityRS->GetPos().x,pEntityRS->GetPos().y,pEntityRS->GetPos().z);*/ return; // skip invalid objects - usually only objects with invalid very big scale will reach this point } // check cvars EERType eERType = pEntityRS->GetEntityRenderType(); switch(eERType) { case eERType_Brush: if(!pCVars->e_brushes) return; break; case eERType_Vegetation: if(!pCVars->e_vegetation) return; break; case eERType_Unknown: if(!pCVars->e_entities) return; if(nRenderFlags&ERF_FIRST_PERSON_CAMERA_OWNER) if(!GetCVars()->e_player) return; if(!m_nRenderStackLevel) if(nRenderFlags&ERF_CASTSHADOWMAPS) if(GetCVars()->e_shadow_spots && m_lstEntitiesShadowSpots.Count()<32 && fEntDistance<32) m_lstEntitiesShadowSpots.Add(pEntityRS); break; } // check all possible occlusions for outdoor objects if(fEntRadius && !bLMapGeneration && pCVars->e_portals!=3) { // test occlusion of outdoor objects by mountains if(m_fZoomFactor && fEntDistance/m_fZoomFactor > 48 && !pEntityRS->m_pVisArea) if(IsBoxOccluded(vBoxMin, vBoxMax, fEntDistance/m_fZoomFactor, &pEntityRS->OcclState)) return; // test occl by antiportals if(GetVisAreaManager()->IsOccludedByOcclVolumes(vBoxMin,vBoxMax, pEntityRS->m_pVisArea!=NULL)) return; } // store for later use (like tree sprites rendering) pEntityRS->m_arrfDistance[m_nRenderStackLevel] = fEntDistance; // mark as rendered in this frame if(bEntityBodyVisible) pEntityRS->SetDrawFrame( GetFrameID(), m_nRenderStackLevel ); // process object particles (rain drops) if(GetCVars()->e_rain_amount) ProcessEntityParticles(pEntityRS,fEntDistance); // update scissor SRendParams DrawParams; // GetRenderer()->SetGlobalShaderTemplateId(EFT_WHITESHADOW); DrawParams.nShaderTemplate = GetRenderer()->GetGlobalShaderTemplateId(); // DrawParams.fSQDistance = fEntDistance*fEntDistance; /* if(pEntityRS->GetShadowFrame(m_nRenderStackLevel) == GetFrameID()) { // merge, this case maybe not needed pEntityRS->GetEntityRS()->nScissorX1 = min(pEntityRS->GetEntityRS()->nScissorX1,EntViewCamera.m_ScissorInfo.x1); pEntityRS->GetEntityRS()->nScissorY1 = min(pEntityRS->GetEntityRS()->nScissorY1,EntViewCamera.m_ScissorInfo.y1); pEntityRS->GetEntityRS()->nScissorX2 = max(pEntityRS->GetEntityRS()->nScissorX2,EntViewCamera.m_ScissorInfo.x2); pEntityRS->GetEntityRS()->nScissorY2 = max(pEntityRS->GetEntityRS()->nScissorY2,EntViewCamera.m_ScissorInfo.y2); } else { // set pEntityRS->GetEntityRS()->nScissorX1 = EntViewCamera.m_ScissorInfo.x1; pEntityRS->GetEntityRS()->nScissorY1 = EntViewCamera.m_ScissorInfo.y1; pEntityRS->GetEntityRS()->nScissorX2 = EntViewCamera.m_ScissorInfo.x2; pEntityRS->GetEntityRS()->nScissorY2 = EntViewCamera.m_ScissorInfo.y2; }*/ int nDLightMaskBeforeLightPassesSeparation = nDLightMask; DrawParams.nDLightMask = nDLightMask; DrawParams.nFogVolumeID = 0; DrawParams.fDistance = fEntDistance; DrawParams.vAmbientColor = (pvAmbColor && pCVars->e_portals!=4) ? (*pvAmbColor) : m_vOutdoorAmbientColor; if(GetCVars()->e_objects_fade_on_distance) DrawParams.fAlpha = min(1.f,(1.f - fEntDistance / fMaxViewDist)*6); // adjust ambient level depending on lsources around for not lightmapped objects if(!(nRenderFlags & ERF_USELIGHTMAPS && pEntityRS->HasLightmap(0)) && pvAmbColor) if((vLightIntensity.x || vLightIntensity.y || vLightIntensity.z) && GetCVars()->e_dynamic_ambient_ratio) { vLightIntensity.x *= GetCVars()->e_dynamic_ambient_ratio; vLightIntensity.y *= GetCVars()->e_dynamic_ambient_ratio; vLightIntensity.z *= GetCVars()->e_dynamic_ambient_ratio; if(pvDynAmbColor) { vLightIntensity.x *= pvDynAmbColor->x; vLightIntensity.y *= pvDynAmbColor->y; vLightIntensity.z *= pvDynAmbColor->z; } DrawParams.vAmbientColor += vLightIntensity; DrawParams.vAmbientColor.CheckMin(Vec3d(1.f,1.f,1.f)); } // modulate ambient color by envir color Vec3d vWorldColor = Get3DEngine()->GetWorldColor(); DrawParams.vAmbientColor.x *= vWorldColor.x; DrawParams.vAmbientColor.y *= vWorldColor.y; DrawParams.vAmbientColor.z *= vWorldColor.z; DrawParams.fCustomSortOffset = GetSortOffset(vCenter,vCamPos); // ignore ambient color set by artist DrawParams.dwFObjFlags = FOB_IGNOREMATERIALAMBIENT; if(nRenderFlags&ERF_SELECTED) { DrawParams.vAmbientColor += Vec3d(0.2f,0.3f,0) + Vec3d(0.1f,0,0)*(int(GetTimer()->GetCurrTime()*12)%3==0); DrawParams.vAmbientColor.CheckMin(Vec3d(1.f,1.f,1.f)); DrawParams.dwFObjFlags |= FOB_SELECTED; } assert(DrawParams.vAmbientColor.x>=0 && DrawParams.vAmbientColor.x<=1.f); assert(DrawParams.vAmbientColor.y>=0 && DrawParams.vAmbientColor.y<=1.f); assert(DrawParams.vAmbientColor.z>=0 && DrawParams.vAmbientColor.z<=1.f); if(pRend->EF_GetHeatVision()) DrawParams.nShaderTemplate = EFT_HEATVISION; // process shadow maps CStatObj * pEntStatObj = NULL; if( pCVars->e_shadow_maps && m_nRenderStackLevel==0 && (nRenderFlags&ERF_RECVSHADOWMAPS || nRenderFlags&ERF_CASTSHADOWMAPS) && pStrongestShadowLight && (pStrongestShadowLight->m_Flags & DLF_SUN || (pStrongestShadowLight->m_pOwner && pStrongestShadowLight->m_pOwner->GetRndFlags()&ERF_CASTSHADOWMAPS)) && fEntDistance<(pEntityRS->GetRenderRadius()*GetCVars()->e_shadow_maps_view_dist_ratio) && (!pEntityRS->GetLight() || pEntityRS->GetContainer()) && (pEntityRS->GetLight()!=pStrongestShadowLight) && (pStrongestShadowLight->m_pOwner != pEntityRS) && // do not cast from it own light (!(pEntStatObj = (CStatObj*)pEntityRS->GetEntityStatObj(0,NULL,true)) || pEntStatObj->GetRenderTrisCount()) && pEntityRS->IsEntityHasSomethingToRender()) { SetupEntityShadowMapping( pEntityRS, &DrawParams, bLMapGeneration, fEntDistance, pStrongestShadowLight ); } else { DrawParams.pShadowMapCasters = 0; /* if(pEntityRS->GetEntityRS() && pEntityRS->GetEntityRS()->pShadowMapInfo && m_nRenderStackLevel==0) { delete pEntityRS->GetEntityRS()->pShadowMapInfo; pEntityRS->GetEntityRS()->pShadowMapInfo=0; }*/ } // draw bbox if (pCVars->e_bboxes) GetRenderer()->Draw3dBBox(pEntityRS->m_vWSBoxMin, pEntityRS->m_vWSBoxMax); // only shadow is needed during terrain light maps calculation if(bLMapGeneration) return; // find cases when lighting need to be rendered in separate pass if(pEntityRS->GetShadowFrame(m_nRenderStackLevel) != (unsigned short)GetFrameID()) if(pCVars->e_stencil_shadows && pStrongestShadowLight && fEntRadius && !GetCVars()->e_debug_lights) { // make list of entities for each light casting shadow volume uint nNewMask = DrawParams.nDLightMask; for(int i=0; DrawParams.nDLightMask; i++) { if(DrawParams.nDLightMask & 1) { // todo: remove EF_Query assert(iGetDynamicLightSources()->Count()); CDLight * pDLight = m_p3DEngine->GetDynamicLightSources()->Get(i); assert(pDLight == (CDLight*)pRend->EF_Query(EFQ_LightSource, i)); if(pDLight && (pDLight->m_Flags & DLF_CASTSHADOW_VOLUME)) { nNewMask &= ~(1<m_Origin,0.1f) )) { DrawParams.nDLightMask = DrawParams.nDLightMask>>1; continue; // light is inside but caster is outside } assert(m_lstLightEntities[i].Find(pEntityRS)<0); if(pEntityRS->IsEntityHasSomethingToRender()) m_lstLightEntities[i].Add(pEntityRS); } } DrawParams.nDLightMask = DrawParams.nDLightMask>>1; } DrawParams.nDLightMask = nNewMask; DrawParams.nSortValue = EFSLIST_STENCIL; pEntityRS->GetEntityRS()->nStrongestLightId = pStrongestShadowLight->m_Id; } // mark shadow as processed pEntityRS->SetShadowFrame( GetFrameID(), m_nRenderStackLevel ); // set light mask for transparent geometry if(pStrongestLightForTranspGeom) { DrawParams.nStrongestDLightMask = 1<m_Id; if(DrawParams.fAlpha<1.f) // set it for entire object if entire object is transparent DrawParams.nDLightMask = DrawParams.nStrongestDLightMask & DrawParams.nDLightMask; } if(bEntityBodyVisible) { // draw entity components (ambient(z-pass) with lights without shadowvolumes and fog) bool bFogNeeded = nFogVolumeID>0; if(pFogVolume && !pFogVolume->InsideBBox((vCamPos.z>pFogVolume->vBoxMax.z) ? vCenter : Vec3d(vCenter.x,vCenter.y,vBoxMin.z))) bFogNeeded = false; else if(fEntDistance>256) bFogNeeded = false; else pEntityRS->m_nFogVolumeID = nFogVolumeID; // allows to sort correctly underwater fog passes if(bFogNeeded && nDLightMaskBeforeLightPassesSeparation == DrawParams.nDLightMask && !pEntityRS->GetEntityVisArea()) // can make overlays double fogged { DrawParams.nFogVolumeID = nFogVolumeID; // include fog now if no lights was separated bFogNeeded = false; } // skip fog and detail passes if this is not last pass if(nDLightMaskBeforeLightPassesSeparation != DrawParams.nDLightMask) DrawParams.dwFObjFlags |= FOB_ZPASS; DrawParams.dwFObjFlags |= (~pEntityRS->m_dwRndFlags) & FOB_TRANS_MASK; if(!pEntityRS->DrawEntity(DrawParams)) return; DrawParams.dwFObjFlags &= ~FOB_ZPASS; if(bFogNeeded && nFogVolumeID && !DrawParams.nFogVolumeID) { // render separate fog pass if still was not rendered // assert(nDLightMaskBewforeSeparatingShadowCasters != DrawParams.nDLightMask); DrawParams.nSortValue = eS_FogShader; // add fog to be rendered later, after light passes DrawParams.nDLightMask = 0; DrawParams.dwFObjFlags |= FOB_FOGPASS; DrawParams.nFogVolumeID = nFogVolumeID; if(!pEntityRS->DrawEntity(DrawParams)) return; } } // add occluders into c-buffer if( !m_nRenderStackLevel && fEntDistancee_cbuffer && !bLMapGeneration && bEntityBodyVisible) { // only use occlusion volume of first entity object Matrix44 objMatrix; IStatObj * pStatObj = pEntityRS->GetEntityStatObj(0, &objMatrix); list2 * plstOcclVolVerts=0; list2 * plstOcclVolInds=0; if( pStatObj && pStatObj->GetOcclusionVolume(plstOcclVolVerts, plstOcclVolInds) && pCVars->e_cbuffer ) { m_pCoverageBuffer->AddMesh( &(*plstOcclVolVerts)[0], (*plstOcclVolVerts).Count(), &(*plstOcclVolInds)[0], (*plstOcclVolInds).Count(), &objMatrix); } } } int __cdecl CObjManager__Cmp_EntTmpDistance(const void* v1, const void* v2) { IEntityRender * p1 = *((IEntityRender**)v1); IEntityRender * p2 = *((IEntityRender**)v2); if(p1==(IEntityRender*)-1) return 1; if(p2==(IEntityRender*)-1) return -1; if(p1->GetEntityRS()->fTmpDistance > p2->GetEntityRS()->fTmpDistance) return 1; else if(p1->GetEntityRS()->fTmpDistance < p2->GetEntityRS()->fTmpDistance) return -1; return 0; } void CObjManager::DrawEntitiesLightPass() { FUNCTION_PROFILER( GetSystem(),PROFILE_3DENGINE ); Vec3d vWorldColor = Get3DEngine()->GetWorldColor(); for(int nLightId=0; nLightIdEF_Query(EFQ_LightSource, nLightId); if(!pDLight || !m_lstLightEntities[nLightId].Count()) continue; assert(pDLight->m_Flags & DLF_CASTSHADOW_VOLUME); bool bUseStencilStateTest = false; // clear stencil GetRenderer()->EF_AddEf(0, m_pREClearStencil, m_p3DEngine->m_pSHClearStencil, NULL, GetIdentityCCObject(), 0, NULL, EFSLIST_STENCIL); // sort entities by distance to light source const Vec3d & vLightPos = pDLight->m_Origin; for(int nEntId=0; nEntIdGetRenderBBox(vBoxMin,vBoxMax); pEntityRS->GetEntityRS()->fTmpDistance = GetDistance(vLightPos,(vBoxMin+vBoxMax)*0.5f); } qsort(&m_lstLightEntities[nLightId][0], m_lstLightEntities[nLightId].Count(), sizeof(m_lstLightEntities[nLightId][0]), CObjManager__Cmp_EntTmpDistance); // draw SS entity shadow volumes into stencil for(int nEntId=0; nEntIdGetRndFlags()&ERF_SELFSHADOW)) continue; if(!(pEntityRS->GetRndFlags()&ERF_CASTSHADOWVOLUME)) continue; if(GetCVars()->e_stencil_shadows_only_from_strongest_light) if(pEntityRS->GetEntityRS()->nStrongestLightId != pDLight->m_Id) continue; SRendParams DrawParams; DrawParams.pShadowVolumeLightSource = pDLight; // set scissor // DrawParams.nScissorX1 = pEntityRS->GetEntityRS()->nScissorX1; // DrawParams.nScissorY1 = pEntityRS->GetEntityRS()->nScissorY1; // DrawParams.nScissorX2 = pEntityRS->GetEntityRS()->nScissorX2; //DrawParams.nScissorY2 = pEntityRS->GetEntityRS()->nScissorY2; DrawParams.pCaller = pEntityRS; DrawParams.nDLightMask = 1<m_Id; DrawParams.nSortValue=EFSLIST_STENCIL; DrawParams.fDistance = pEntityRS->m_arrfDistance[m_nRenderStackLevel]; DrawParams.fShadowVolumeExtent = CalculateEntityShadowVolumeExtent(pEntityRS, pDLight); DrawParams.dwFObjFlags |= (~pEntityRS->m_dwRndFlags) & FOB_TRANS_MASK; if(DrawParams.fShadowVolumeExtent>1.f) { DrawParams.dwFObjFlags |= (pEntityRS->m_dwRndFlags&ERF_SELECTED) ? FOB_SELECTED : 0; pEntityRS->DrawEntity(DrawParams); bUseStencilStateTest = true; } } // draw NSS entity light pass and THEN shadow volumes into stencil for(int nEntId=0; nEntIdGetRndFlags()&ERF_SELFSHADOW) continue; if(!(pDLight->m_Flags & DLF_LM && pEntityRS->GetRndFlags()&ERF_USELIGHTMAPS && pEntityRS->HasLightmap(0)) || pDLight->m_SpecColor != Col_Black) { // light pass SRendParams DrawParams; DrawParams.nDLightMask = (1<m_Flags & DLF_CASTSHADOW_VOLUME); assert(m_lstLightEntities[nLightId].Count()); DrawParams.pStateShader = m_p3DEngine->m_pSHStencilState; } DrawParams.fDistance = pEntityRS->m_arrfDistance[m_nRenderStackLevel]; DrawParams.dwFObjFlags |= (~pEntityRS->m_dwRndFlags) & FOB_TRANS_MASK; DrawParams.dwFObjFlags |= (pEntityRS->m_dwRndFlags&ERF_SELECTED) ? FOB_SELECTED : 0; pEntityRS->DrawEntity(DrawParams); } // shadow volumes if(pEntityRS->GetRndFlags()&ERF_CASTSHADOWVOLUME) { if(GetCVars()->e_stencil_shadows_only_from_strongest_light) if(pEntityRS->GetEntityRS()->nStrongestLightId != pDLight->m_Id) continue; SRendParams DrawParams; DrawParams.pShadowVolumeLightSource = pDLight; // set scissor // DrawParams.nScissorX1 = pEntityRS->GetEntityRS()->nScissorX1; // DrawParams.nScissorY1 = pEntityRS->GetEntityRS()->nScissorY1; // DrawParams.nScissorX2 = pEntityRS->GetEntityRS()->nScissorX2; // DrawParams.nScissorY2 = pEntityRS->GetEntityRS()->nScissorY2; DrawParams.pCaller = pEntityRS; DrawParams.nDLightMask = 1<m_Id; DrawParams.nSortValue=EFSLIST_STENCIL; DrawParams.fDistance = pEntityRS->m_arrfDistance[m_nRenderStackLevel]; DrawParams.fShadowVolumeExtent = CalculateEntityShadowVolumeExtent(pEntityRS, pDLight); if(DrawParams.fShadowVolumeExtent>1.f) { DrawParams.dwFObjFlags |= (~pEntityRS->m_dwRndFlags) & FOB_TRANS_MASK; DrawParams.dwFObjFlags |= (pEntityRS->m_dwRndFlags&ERF_SELECTED) ? FOB_SELECTED : 0; pEntityRS->DrawEntity(DrawParams); bUseStencilStateTest = true; } } } // draw SS light pass with stencil test for(int nEntId=0; nEntIdIsOutdoorAreasVisible()) m_pTerrain->RenderDLightOnHeightMap(pDLight); // light on terrain } else if(!(pDLight->m_Flags & DLF_LM && pEntityRS->GetRndFlags()&ERF_USELIGHTMAPS && pEntityRS->HasLightmap(0)) || pDLight->m_SpecColor != Col_Black) { // entity light pass if(!(pEntityRS->GetRndFlags()&ERF_SELFSHADOW)) continue; SRendParams DrawParams; DrawParams.nDLightMask = (1<m_Flags & DLF_CASTSHADOW_VOLUME); assert(m_lstLightEntities[nLightId].Count()); DrawParams.pStateShader = m_p3DEngine->m_pSHStencilState; } DrawParams.fDistance = pEntityRS->m_arrfDistance[m_nRenderStackLevel]; DrawParams.dwFObjFlags |= (~pEntityRS->m_dwRndFlags) & FOB_TRANS_MASK; DrawParams.dwFObjFlags |= (pEntityRS->m_dwRndFlags&ERF_SELECTED) ? FOB_SELECTED : 0; pEntityRS->DrawEntity(DrawParams); } } // draw shadow pass with stencil test for lightmapped objects if(bUseStencilStateTest) // todo: add shadow-object bbox test for(int nEntId=0; nEntIdGetRndFlags()&ERF_USELIGHTMAPS && pEntityRS->HasLightmap(0) && pDLight->m_Flags & DLF_LM && pDLight->m_Flags & DLF_CASTSHADOW_VOLUME) { if(pDLight->m_Flags & DLF_DIRECTIONAL) { // aditionall ambient pass with stencil test for outdoor brushes if(pEntityRS->GetEntityVisArea()) continue; // scip indoor objects // detect if object recives shadow bool bDraw = false; for(int nCasterId=0; nCasterIdGetRndFlags()&ERF_CASTSHADOWVOLUME && pEntityRS != m_lstLightEntities[nLightId][nCasterId] && !m_lstLightEntities[nLightId][nCasterId]->GetEntityVisArea()) { if( IsSphereAffectedByShadow( m_lstLightEntities[nLightId][nCasterId], pEntityRS, pDLight)) { IEntityRender * pCaster = m_lstLightEntities[nLightId][nCasterId]; float fShadowVolumeExtent = CalculateEntityShadowVolumeExtent(pCaster, pDLight); Vec3d vShadowBoxMin=pCaster->m_vWSBoxMin, vShadowBoxMax=pCaster->m_vWSBoxMax; MakeShadowBBox(vShadowBoxMin, vShadowBoxMax, pDLight->m_Origin, pDLight->m_fRadius, fShadowVolumeExtent); if(AABB(vShadowBoxMin, vShadowBoxMax).IsIntersectBox(AABB(pEntityRS->m_vWSBoxMin,pEntityRS->m_vWSBoxMax))) { bDraw = true; break; } } } } if(!bDraw) continue; SRendParams DrawParams; DrawParams.vAmbientColor = m_vOutdoorAmbientColor; DrawParams.vAmbientColor.x *= vWorldColor.x; DrawParams.vAmbientColor.y *= vWorldColor.y; DrawParams.vAmbientColor.z *= vWorldColor.z; DrawParams.dwFObjFlags = FOB_IGNOREMATERIALAMBIENT; DrawParams.dwFObjFlags |= (~pEntityRS->m_dwRndFlags) & FOB_TRANS_MASK; DrawParams.nDLightMask = 0;//(1<m_Flags & DLF_CASTSHADOW_VOLUME); assert(m_lstLightEntities[nLightId].Count()); DrawParams.pStateShader = m_p3DEngine->m_pSHStencilStateInv; } DrawParams.fDistance = pEntityRS->m_arrfDistance[m_nRenderStackLevel]; // turn of lightmaps and render uint nOrigFlags = pEntityRS->GetRndFlags(); pEntityRS->SetRndFlags(ERF_USELIGHTMAPS, false); DrawParams.dwFObjFlags |= (pEntityRS->m_dwRndFlags&ERF_SELECTED) ? FOB_SELECTED : 0; pEntityRS->DrawEntity(DrawParams); pEntityRS->SetRndFlags(nOrigFlags); } else { // inverted light pass SRendParams DrawParams; DrawParams.vAmbientColor = Vec3d(0,0,0); // (pvAmbColor && pCVars->e_portals!=4) ? (*pvAmbColor) : m_vOutdoorAmbientColor; DrawParams.nDLightMask = (1<m_Flags & DLF_CASTSHADOW_VOLUME); assert(m_lstLightEntities[nLightId].Count()); DrawParams.pStateShader = m_p3DEngine->m_pSHStencilStateInv; } DrawParams.fDistance = pEntityRS->m_arrfDistance[m_nRenderStackLevel]; DrawParams.dwFObjFlags |= (~pEntityRS->m_dwRndFlags) & FOB_TRANS_MASK; DrawParams.dwFObjFlags |= (pEntityRS->m_dwRndFlags&ERF_SELECTED) ? FOB_SELECTED : 0; pEntityRS->DrawEntity(DrawParams); } } } m_lstLightEntities[nLightId].Clear(); } } void CObjManager::SetupEntityShadowMapping( IEntityRender * pEnt, SRendParams * pDrawParams, bool bLMapGeneration, float fEntDistance, CDLight * pDLight ) { FUNCTION_PROFILER( GetSystem(),PROFILE_3DENGINE ); // bool bPassiveShadowReseiver = // GetCVars()->e_active_shadow_maps_receving ? (pEnt->GetEntityRenderType()!=eERType_Unknown) : true; // Init ShadowMapInfo structure if(!pEnt->GetEntityRS()->pShadowMapInfo) pEnt->GetEntityRS()->pShadowMapInfo = new IEntityRenderState::ShadowMapInfo(); // leak // this list will contain shadow map casters if(!pEnt->GetEntityRS()->pShadowMapInfo->pShadowMapCasters) pEnt->GetEntityRS()->pShadowMapInfo->pShadowMapCasters = new list2; else pEnt->GetEntityRS()->pShadowMapInfo->pShadowMapCasters->Clear(); if(pEnt->GetRndFlags()&ERF_CASTSHADOWMAPS) { // make shadow frustum to project shadow to the world (and this entity) ProcessShadowMapCasting(pEnt, pDLight); if(GetCVars()->e_entities_debug) m_lstDebugEntityList.Add(pEnt); } else if(pEnt->GetEntityRS()->pShadowMapInfo->pShadowMapFrustumContainer && !bLMapGeneration) { // unmake if not needed delete pEnt->GetEntityRS()->pShadowMapInfo->pShadowMapFrustumContainer->m_LightFrustums.Get(0)->pEntityList; pEnt->GetEntityRS()->pShadowMapInfo->pShadowMapFrustumContainer->m_LightFrustums.Get(0)->pEntityList=0; delete pEnt->GetEntityRS()->pShadowMapInfo->pShadowMapFrustumContainer->m_LightFrustums.Get(0)->pModelsList; pEnt->GetEntityRS()->pShadowMapInfo->pShadowMapFrustumContainer->m_LightFrustums.Get(0)->pModelsList=0; if(pEnt->GetEntityRenderType()!=eERType_Vegetation) // vegetations share containers delete pEnt->GetEntityRS()->pShadowMapInfo->pShadowMapFrustumContainer; pEnt->GetEntityRS()->pShadowMapInfo->pShadowMapFrustumContainer=0; } if(pEnt->GetRndFlags()&ERF_RECVSHADOWMAPS_ACTIVE && GetCVars()->e_active_shadow_maps_receving) { // make frustum containing all potential shadow casters (even not marked for shadow casting) ProcessActiveShadowReceiving(pEnt, fEntDistance, pDLight, bLMapGeneration); } else if(pEnt->GetRndFlags()&ERF_RECVSHADOWMAPS && GetCVars()->e_shadow_maps_receiving) { // make list of potential active shadow map casters to cast to this entity MakeShadowMapInstancesList(pEnt, fEntDistance, pEnt->GetEntityRS()->pShadowMapInfo->pShadowMapCasters, SMC_STATICS | SMC_DYNAMICS, pDLight); } else if(pEnt->GetEntityRS()->pShadowMapInfo->pShadowMapCasters && !bLMapGeneration && !(pEnt->GetRndFlags()&ERF_CASTSHADOWMAPS)) { pEnt->GetEntityRS()->pShadowMapInfo->pShadowMapCasters->Clear(); delete pEnt->GetEntityRS()->pShadowMapInfo->pShadowMapCasters; pEnt->GetEntityRS()->pShadowMapInfo->pShadowMapCasters = 0; } // pass result to output list2 * pLsList = pEnt->GetEntityRS()->pShadowMapInfo->pShadowMapCasters; if(pLsList && pLsList->Count() && pLsList->Get(0)->m_pLS && pLsList->Get(0)->m_pLS->GetShadowMapFrustum()) { assert(pLsList->Get(0)->m_pReceiver == pEnt || !pLsList->Get(0)->m_pReceiver); if( pLsList->Count() == 1 && pLsList->Get(0)->m_pLS->GetShadowMapFrustum()->pOwner == pLsList->Get(0)->m_pReceiver && !(pLsList->Get(0)->m_pLS->GetShadowMapFrustum()->dwFlags & SMFF_ACTIVE_SHADOW_MAP)) assert(pDrawParams->pShadowMapCasters == NULL); // skip single self shadowing pass else pDrawParams->pShadowMapCasters = pLsList; } // if entity do not receive shadow - no more than this entity should be in the casters list assert(pEnt->GetRndFlags()&ERF_RECVSHADOWMAPS || ((pDrawParams->pShadowMapCasters == 0) || (pDrawParams->pShadowMapCasters->Count() == 1))); // add to list of outdoor shadow maps if(!m_nRenderStackLevel && (pEnt->GetRndFlags()&ERF_CASTSHADOWMAPS)) if(m_lstStatEntitiesShadowMaps.Count()<64) // don't go crazy if(GetCVars()->e_shadow_maps_from_static_objects || (pEnt->GetEntityRenderType()==eERType_Unknown)) m_lstStatEntitiesShadowMaps.Add(pEnt); } /* int __cdecl CObjManager__Cmp_EntRadius(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 0; } */ void CObjManager::RenderEntitiesShadowMapsOnTerrain(bool bLMapGeneration, CREShadowMapGen * pREShadowMapGenerator) { FUNCTION_PROFILER( GetSystem(),PROFILE_3DENGINE ); if(GetCVars()->e_shadow_maps && m_lstStatEntitiesShadowMaps.Count()) { // Sort entities by size to avoid problems with overlaping // qsort(&m_lstStatEntitiesShadowMaps[0], m_lstStatEntitiesShadowMaps.Count(), sizeof(m_lstStatEntitiesShadowMaps[0]), CObjManager__Cmp_EntRadius); for(int i=0; ie_shadow_spots && iGetRenderBBox(vEntBoxMin,vEntBoxMax); Vec3d vEntCenter = (vEntBoxMin+vEntBoxMax)*0.5f; float fRadius = (vEntBoxMax-vEntBoxMin).len(); ((C3DEngine*)Get3DEngine())->DrawShadowSpotOnTerrain(vEntCenter, fRadius*0.5f); } m_lstEntitiesShadowSpots.Clear(); } CFColor CObjManager::CalcShadowOnTerrainColor(float fAlpha, bool bLMapGeneration) { // calc shadow color // float fAmbLevel = (m_vOutdoorAmbientColor.x+m_vOutdoorAmbientColor.y+m_vOutdoorAmbientColor.z)*0.33f*0.25f; //if(fAmbLevel<0.01f) //fAmbLevel=0.01f; // if(fAlpha < fAmbLevel) // fAlpha = fAmbLevel; if(bLMapGeneration) return CFColor(0,0,0); Vec3d vAverColor = m_vOutdoorAmbientColor*(1.f-fAlpha)*0.5f + Vec3d(1,1,1)*fAlpha; return CFColor ( min(255.f,vAverColor.x), min(255.f,vAverColor.y), min(255.f,vAverColor.z)); } void CObjManager::RenderEntityShadowOnTerrain(IEntityRender * pEntityRnd, bool bLMapGeneration, CREShadowMapGen * pREShadowMapGenerator) { float fEntRenderRadius = pEntityRnd->GetRenderRadius(); if(fEntRenderRadius<2.f) fEntRenderRadius=2.f; Vec3d vShadowOffset = GetNormalized(m_p3DEngine->GetSunPosition())*fEntRenderRadius; IEntityRenderState * pRendState = pEntityRnd->GetEntityRS(); if( pEntityRnd->GetShadowMapFrustumContainer() && pEntityRnd->GetShadowMapFrustumContainer()->m_LightFrustums.Count() && pRendState->pShadowMapInfo->pShadowMapCasters && fEntRenderRadius) { Vec3d vEntBoxMin,vEntBoxMax; pEntityRnd->GetBBox(vEntBoxMin,vEntBoxMax); Vec3d vEntCenter = (vEntBoxMin+vEntBoxMax)*0.5f; float fDistance = bLMapGeneration ? 0 : GetDistance(vEntCenter, GetViewCamera().GetPos()); float fAlpha = (1.f - fDistance/(pEntityRnd->GetRenderRadius()*GetCVars()->e_shadow_maps_view_dist_ratio))*3.f; CCObject * pObj = GetIdentityCCObject(); pObj->m_ObjFlags |= FOB_IGNOREREPOINTER; if(fAlpha<0) fAlpha=0; pObj->m_SortId = 100.f*fAlpha; if(fAlpha>1.f) fAlpha = 1.f; if(GetCVars()->e_shadow_maps_fade_from_light_bit && !bLMapGeneration) { int nAreaSize = int(pEntityRnd->GetRenderRadius())/CTerrain::GetHeightMapUnitSize()*CTerrain::GetHeightMapUnitSize()+CTerrain::GetHeightMapUnitSize(); if(nAreaSize>8) nAreaSize=8; int x1 = int(vEntCenter.x + nAreaSize/2)/nAreaSize*nAreaSize; int y1 = int(vEntCenter.y + nAreaSize/2)/nAreaSize*nAreaSize; for(int x=(x1-nAreaSize); x<=(x1+nAreaSize); x+=CTerrain::GetHeightMapUnitSize()) for(int y=(y1-nAreaSize); y<=(y1+nAreaSize); y+=CTerrain::GetHeightMapUnitSize()) { bool bLight = m_pTerrain->IsOnTheLight(x,y); if(!bLight) fAlpha -= (0.15f * CTerrain::GetHeightMapUnitSize()/nAreaSize * CTerrain::GetHeightMapUnitSize()/nAreaSize); } if(fAlpha<0) fAlpha=0; } if(fAlpha<0) fAlpha=0; ShadowMapLightSource * pShadowMapFrustumContainer = pEntityRnd->GetShadowMapFrustumContainer(); // pObj->m_pShadowFrustum = pShadowMapFrustumContainer ? pShadowMapFrustumContainer->GetShadowMapFrustum() : 0; pObj->m_pShadowCasters = pEntityRnd->GetShadowMapCasters(); pObj->m_Color = CalcShadowOnTerrainColor(1.f-fAlpha, bLMapGeneration); Vec3d vPos = vEntCenter - vShadowOffset; float fQuadRadius = fEntRenderRadius*2.f; if(fQuadRadius>100) fQuadRadius=100; // setup fading out pObj->m_TempVars[0] = vEntCenter.x; pObj->m_TempVars[1] = vEntCenter.y; pObj->m_TempVars[2] = vEntBoxMin.z; pObj->m_Color.a = bLMapGeneration ? 0 : (0.75f/fQuadRadius); // allign by unit size int nUnitSize = CTerrain::GetHeightMapUnitSize(); vPos.x = float(int(vPos.x + 0.5f*nUnitSize)/nUnitSize*nUnitSize); vPos.y = float(int(vPos.y + 0.5f*nUnitSize)/nUnitSize*nUnitSize); vPos.z = float(int(vPos.z + 0.5f*nUnitSize)/nUnitSize*nUnitSize); fQuadRadius = float(int(fQuadRadius + 0.5f*nUnitSize)/nUnitSize*nUnitSize); bool bREAdded = false; if(!pEntityRnd->GetEntityVisArea() && GetVisAreaManager()->IsOutdoorAreasVisible() && (!(pEntityRnd->GetRndFlags() & ERF_CASTSHADOWINTOLIGHTMAP) || bLMapGeneration) && fAlpha>0) { // generate shadow map and render terrain shadow pass if( vPos.x<0 || vPos.x>=CTerrain::GetTerrainSize() || vPos.y<0 || vPos.y>=CTerrain::GetTerrainSize() ) return; // bool bRecalcLeafBuffers = (bLMapGeneration || pRendState->vPrevTerShadowPos != vPos || pRendState->fPrevTerShadowRadius != fQuadRadius); bool bRecalcLeafBuffers = (bLMapGeneration || (!IsEquivalent(pRendState->pShadowMapInfo->vPrevTerShadowPos,vPos)) || pRendState->pShadowMapInfo->fPrevTerShadowRadius != fQuadRadius); pRendState->pShadowMapInfo->vPrevTerShadowPos = vPos; pRendState->pShadowMapInfo->fPrevTerShadowRadius = fQuadRadius; if(!pRendState->pShadowMapInfo->pShadowMapLeafBuffersList) pRendState->pShadowMapInfo->pShadowMapLeafBuffersList = new list2; pRendState->pShadowMapInfo->pShadowMapLeafBuffersList->PreAllocate(16,16); bREAdded = m_pTerrain->RenderAreaLeafBuffers(vPos, fQuadRadius, 0, pRendState->pShadowMapInfo->pShadowMapLeafBuffersList->GetElements(), pRendState->pShadowMapInfo->pShadowMapLeafBuffersList->Count(), pObj, m_pTerrain->m_pTerrainShadowPassEf, bRecalcLeafBuffers, "EntityShadowOnTerrain",0,0, pRendState->pShadowMapInfo->pShadowMapFrustumContainer->GetShadowMapFrustum(), &Vec3d(pEntityRnd->GetPos()), (pEntityRnd->GetEntityRenderType() == eERType_Vegetation) ? pEntityRnd->GetScale() : 1.f); } if(pREShadowMapGenerator && !bREAdded) { // just generate shadow map to use in indoors GetRenderer()->EF_AddEf(0, pREShadowMapGenerator, m_p3DEngine->m_pSHShadowMapGen, NULL, pObj, 0); } if(GetCVars()->e_shadow_maps_frustums) pEntityRnd->GetShadowMapFrustumContainer()->m_LightFrustums.Get(0)->DrawFrustum(GetRenderer(), pEntityRnd->GetPos(), 1.f); } } float CObjManager::CalculateEntityShadowVolumeExtent(IEntityRender * pEntity, CDLight * pDLight) { FUNCTION_PROFILER_FAST( GetSystem(),PROFILE_3DENGINE,m_bProfilerEnabled ); // calculate optimal extent for outdoor entity Vec3d vMinObjBBox, vMaxObjBBox; pEntity->GetRenderBBox(vMinObjBBox, vMaxObjBBox); Vec3d vObjCenter = (vMinObjBBox+vMaxObjBBox)*0.5; float fObjRadius = (vMaxObjBBox-vMinObjBBox).Length()*0.5f; float fObjLightDist = GetDistance(pDLight->m_Origin,vObjCenter) - fObjRadius; if(fObjLightDist<0.01f) fObjLightDist=0.01f; float fRadiusForShadow = pDLight->m_fRadius; CVisArea * pArea = (CVisArea*)pEntity->GetEntityVisArea(); if(pArea && pDLight->m_Flags & DLF_THIS_AREA_ONLY) { // limit light radius by area box Vec3d vLightDir = pDLight->m_Origin - vObjCenter; Vec3d vLightDirOrig = vLightDir; vLightDir.SetLen(pDLight->m_fRadius); vLightDir.x = fabsf(vLightDir.x); vLightDir.y = fabsf(vLightDir.y); vLightDir.z = fabsf(vLightDir.z); for(int a=0; a<3; a++) { float fMaxDistToBoxFace = vLightDirOrig[a]<0 ? pArea->m_vBoxMax[a]-pDLight->m_Origin[a] : pDLight->m_Origin[a]-pArea->m_vBoxMin[a]; if(vLightDir[a] > (fMaxDistToBoxFace)) vLightDir /= vLightDir[a] / (fMaxDistToBoxFace); } fRadiusForShadow = vLightDir.Length(); } if(fRadiusForShadow>(fObjLightDist+MAX_SHADOW_VOLUME_LEN)) fRadiusForShadow=fObjLightDist+MAX_SHADOW_VOLUME_LEN; float fShadowVolumeExtent = fRadiusForShadow/fObjLightDist; if(fShadowVolumeExtent<1.f) fShadowVolumeExtent=1.f; else if(fShadowVolumeExtent>20.f) fShadowVolumeExtent=20.f; return fShadowVolumeExtent; } void CObjManager::ProcessEntityParticles(IEntityRender * pEnt, float fEntDistance) { const float fMaxRainDropsDist = 12.f; if(!GetCVars()->e_rain_amount || fEntDistance>fMaxRainDropsDist) return; ParticleParams PartParams; PartParams.fFocus = 1.5f; PartParams.fLifeTime = 0.5f; PartParams.fSize = 0.02f; PartParams.fSizeSpeed = 0; PartParams.fSpeed = 1.f; PartParams.vGravity(0,0,-5.f); PartParams.nCount = 15; PartParams.eBlendType = ParticleBlendType_ColorBased; PartParams.nTexId = GetRenderer()->LoadTexture("cloud"); float fAlpha = GetCVars()->e_rain_amount*min(1.f,(1.f-fEntDistance/fMaxRainDropsDist)*2.f); PartParams.vColorStart = Get3DEngine()->GetFogColor()*fAlpha; PartParams.vColorEnd = Get3DEngine()->GetFogColor()*fAlpha; PartParams.vDirection(0,0,1); for(int i=0; i<2; i++) { // anim objects ICryCharInstance * pChar = pEnt->GetEntityCharacter(i); if(pChar) pChar->RemoveParticleEmitter(-1); if(pChar && (pChar->GetFlags() & CS_FLAG_DRAW_MODEL)) { CryParticleSpawnInfo SpawnParams; SpawnParams.fSpawnRate = GetCVars()->e_rain_amount*120.f; SpawnParams.nFlags = CryParticleSpawnInfo::FLAGS_RAIN_MODE | CryParticleSpawnInfo::FLAGS_ONE_TIME_SPAWN; if(pChar->GetFlags() & CS_FLAG_DRAW_NEAR) PartParams.nParticleFlags |= PART_FLAG_DRAW_NEAR; else PartParams.nParticleFlags &= ~PART_FLAG_DRAW_NEAR; pChar->AddParticleEmitter(PartParams,SpawnParams); } } /* { pEnt->GetEntityRS()->fParticleTimer += GetTimer()->GetFrameTime(); Vec3d vBoxMin, vBoxMax; pEnt->GetRenderBBox(vBoxMin, vBoxMax); float fArea = (vBoxMax.x-vBoxMin.x)*(vBoxMax.y-vBoxMin.y); if(fArea) { const float fDropPeriod = max(0.002f, (1.f-GetCVars()->e_rain_amount)/45.f/fArea); while(pEnt->GetEntityRS()->fParticleTimer > fDropPeriod) { pEnt->GetEntityRS()->fParticleTimer -= fDropPeriod; Matrix44 StatObjMat; CStatObj * pStatObj = (CStatObj*)pEnt->GetEntityStatObj(0,&StatObjMat); if(pStatObj) pStatObj->SpawnParticles(PartParams,StatObjMat,true); } } }*/ } void CObjManager::MakeShadowBBox(Vec3d & vBoxMin, Vec3d & vBoxMax, const Vec3d & vLightPos, float fLightRadius, float fShadowVolumeExtent) { FUNCTION_PROFILER_FAST( GetSystem(),PROFILE_3DENGINE,m_bProfilerEnabled ); Vec3d arrVerts3d[8] = { Vec3d(vBoxMin.x,vBoxMin.y,vBoxMin.z), Vec3d(vBoxMin.x,vBoxMax.y,vBoxMin.z), Vec3d(vBoxMax.x,vBoxMin.y,vBoxMin.z), Vec3d(vBoxMax.x,vBoxMax.y,vBoxMin.z), Vec3d(vBoxMin.x,vBoxMin.y,vBoxMax.z), Vec3d(vBoxMin.x,vBoxMax.y,vBoxMax.z), Vec3d(vBoxMax.x,vBoxMin.y,vBoxMax.z), Vec3d(vBoxMax.x,vBoxMax.y,vBoxMax.z) }; for(int i=0; i<8; i++) { Vec3d vLightDir = arrVerts3d[i] - vLightPos; /* float fDistToLightPos = max(0.1f,vLightDir.Length()); float fDistToLightRadius = fLightRadius - fDistToLightPos; float t = max(0,fDistToLightRadius/fDistToLightPos); arrVerts3d[i] = arrVerts3d[i] + vLightDir * t; */ arrVerts3d[i] = vLightPos+vLightDir*fShadowVolumeExtent; vBoxMin.CheckMin(arrVerts3d[i]); vBoxMax.CheckMax(arrVerts3d[i]); } } bool CObjManager::ProcessShadowMapCasting(IEntityRender * pEnt, CDLight * pDLight) { if(pEnt->GetEntityRenderType() == eERType_Vegetation) { // shadow casting already prepared at loading time, todo: move it in real-time pEnt->GetEntityRS()->pShadowMapInfo->pShadowMapFrustumContainer = pEnt->GetShadowMapFrustumContainer(); pEnt->GetShadowMapCasters(); } else { ShadowMapLightSource * & pLsource = pEnt->GetEntityRS()->pShadowMapInfo->pShadowMapFrustumContainer; if(!pLsource) { // make lsource pLsource = new ShadowMapLightSource; ShadowMapFrustum lof; lof.pOwner = pEnt; pLsource->m_LightFrustums.Add(lof); } // get needed size of shadow map int nTexSize = GetCVars()->e_max_shadow_map_size; float fDistToTheCamera = pEnt->m_arrfDistance[m_nRenderStackLevel]; Vec3d vBoxMin,vBoxMax; pEnt->GetBBox(vBoxMin,vBoxMax); float fCasterRadius = (vBoxMax-vBoxMin).len()*0.5f; while( nTexSize*fDistToTheCamera > fCasterRadius*GetCVars()->e_shadow_maps_size_ratio ) nTexSize /= 2; // get obj space light pos Vec3d vObjSpaceLightPos; Matrix44 objMatrix; IStatObj * pStatObj = pEnt->GetEntityStatObj(0, &objMatrix); if(pStatObj) { objMatrix.Invert44(); vObjSpaceLightPos = objMatrix.TransformVectorOLD(pDLight ? pDLight->m_Origin : m_p3DEngine->GetSunPosition()); } else vObjSpaceLightPos = (pDLight ? pDLight->m_Origin : m_p3DEngine->GetSunPosition()) - pEnt->GetPos(); // request shadow map update if needed assert(pDLight); if(!pDLight) return true; // process shadow map for casting if( nTexSize != pLsource->GetShadowMapFrustum()->nTexSize || !IsEquivalent(pLsource->vObjSpaceSrcPos, vObjSpaceLightPos, 0.01f) || pEnt->HasChanged() || pLsource->GetShadowMapFrustum()->depth_tex_id==0 ) { pLsource->GetShadowMapFrustum()->bUpdateRequested = true; pLsource->vSrcPos = (pDLight ? pDLight->m_Origin : m_p3DEngine->GetSunPosition()) - pEnt->GetPos(); pLsource->vObjSpaceSrcPos = vObjSpaceLightPos; pLsource->fRadius = pDLight ? pDLight->m_fRadius : 5000000; MakeEntityShadowFrustum(pLsource->GetShadowMapFrustum(), pLsource, pEnt, EST_DEPTH_BUFFER, 0); pLsource->GetShadowMapFrustum()->nTexSize = nTexSize; } if(pLsource->GetShadowMapFrustum()) { float fAlpha = (1.f-fDistToTheCamera/(pEnt->GetRenderRadius()*GetCVars()->e_shadow_maps_view_dist_ratio))*3.f; pLsource->GetShadowMapFrustum()->fAlpha = min(1.f,fAlpha); pLsource->GetShadowMapFrustum()->nDLightId = pLsource->nDLightId = pDLight ? pDLight->m_Id : -1; } // add this frustum to the list of shadow casters for self shadowing and shadow on terrain if(pEnt->GetEntityRS()->pShadowMapInfo->pShadowMapFrustumContainer) { ShadowMapLightSourceInstance LightSourceInfo; LightSourceInfo.m_pLS = pEnt->GetEntityRS()->pShadowMapInfo->pShadowMapFrustumContainer; LightSourceInfo.m_vProjTranslation = pEnt->GetPos(); LightSourceInfo.m_fProjScale = 1.f; Vec3d vThisEntityPos = pEnt->GetPos(); LightSourceInfo.m_fDistance = 0; LightSourceInfo.m_pReceiver = pEnt; if(LightSourceInfo.m_pLS->m_LightFrustums.Count()) pEnt->GetEntityRS()->pShadowMapInfo->pShadowMapCasters->Add(LightSourceInfo); } } return true; } bool CObjManager::IsSphereAffectedByShadow(IEntityRender * pCaster, IEntityRender * pReceiver, CDLight * pLight) { FUNCTION_PROFILER_FAST( GetSystem(),PROFILE_3DENGINE,m_bProfilerEnabled ); assert(pLight->m_Flags & DLF_DIRECTIONAL); // get spheres Vec3d vBoxMin,vBoxMax; pCaster->GetBBox(vBoxMin,vBoxMax); Vec3d vCasterCenter = (vBoxMin+vBoxMax)*0.5f; float fCasterRadius = (vBoxMax-vBoxMin).len()*0.5f; // GetRenderer()->Draw3dBBox(vBoxMin,vBoxMax,DPRIM_SOLID_SPHERE); pReceiver->GetBBox(vBoxMin,vBoxMax); Vec3d vReceiverCenter = (vBoxMin+vBoxMax)*0.5f; float fReceiverRadius = (vBoxMax-vBoxMin).len()*0.5f; //GetRenderer()->Draw3dBBox(vBoxMin,vBoxMax,DPRIM_SOLID_SPHERE); // define rays and distances Vec3d vCasterDir = vCasterCenter - pLight->m_Origin; float fDistToCaster = vCasterDir.len(); Vec3d vReceiverDir = vReceiverCenter - pLight->m_Origin; float fDistToReceiver = vReceiverDir.len(); if((fDistToReceiver+fReceiverRadius) < (fDistToCaster-fCasterRadius)) return false; if((fDistToReceiver-fReceiverRadius) > (fDistToCaster+fCasterRadius+MAX_SHADOW_VOLUME_LEN)) return false; // move caster to receiver vCasterDir *= (fDistToReceiver/fDistToCaster); float fDist = vCasterDir.GetDistance(vReceiverDir); return fDist < (fCasterRadius+fReceiverRadius); }