/* * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or * its licensors. * * For complete copyright and license terms please see the LICENSE at the root of this * distribution (the "License"). All use of this software is governed by the License, * or, if provided, by the license below or the license accompanying this file. Do not * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * */ // Original file Copyright Crytek GMBH or its affiliates, used under license. #include "StdAfx.h" #include #include "StatObj.h" #include "ObjMan.h" #include "VisAreas.h" #include "3dEngine.h" #include "ILMSerializationManager.h" #include "TimeOfDay.h" #include "AABBSV.h" #define DEFAULT_INITIAL_PORTALS 1 #define DEFAULT_INITIAL_VISAREAS 1 #define DEFAULT_INITIAL_OCCLAREAS 1 CVisAreaManager::CVisAreaManager() { m_pCurPortal = m_pCurArea = 0; m_bOutdoorVisible = false; m_bSkyVisible = false; m_bSunIsNeeded = false; m_bOceanVisible = false; m_pAABBTree = NULL; m_visAreas.PreAllocate(DEFAULT_INITIAL_VISAREAS); m_visAreaColdData.PreAllocate(DEFAULT_INITIAL_VISAREAS); m_portals.PreAllocate(DEFAULT_INITIAL_PORTALS); m_portalColdData.PreAllocate(DEFAULT_INITIAL_PORTALS); m_occlAreas.PreAllocate(DEFAULT_INITIAL_OCCLAREAS); m_occlAreaColdData.PreAllocate(DEFAULT_INITIAL_OCCLAREAS); m_segVisAreas.Clear(); m_segPortals.Clear(); m_segOcclAreas.Clear(); } void CVisAreaManager::DeleteAllVisAreas() { for (int i = 0; i < m_lstVisAreas.Count(); i++) { if (m_visAreas.Find(m_lstVisAreas[i]) >= 0) { delete m_lstVisAreas[i]; } else { delete m_lstVisAreas[i]->GetColdData(); delete m_lstVisAreas[i]; } } m_visAreas.Clear(); m_visAreaColdData.Clear(); m_lstVisAreas.Clear(); for (int i = 0; i < m_lstPortals.Count(); i++) { if (m_portals.Find(m_lstPortals[i]) >= 0) { delete m_lstPortals[i]; } else { delete m_lstPortals[i]->GetColdData(); delete m_lstPortals[i]; } } m_portals.Clear(); m_portalColdData.Clear(); m_lstPortals.Clear(); for (int i = 0; i < m_lstOcclAreas.Count(); i++) { if (m_occlAreas.Find(m_lstOcclAreas[i]) >= 0) { delete m_lstOcclAreas[i]; } else { delete m_lstOcclAreas[i]->GetColdData(); delete m_lstOcclAreas[i]; } } m_occlAreas.Clear(); m_occlAreaColdData.Clear(); m_lstOcclAreas.Clear(); stl::free_container(CVisArea::m_lUnavailableAreas); } void SAABBTreeNode::OffsetPosition(const Vec3& delta) { nodeBox.Move(delta); if (!nodeAreas.Count()) { for (int i = 0; i < 2; i++) { if (arrChilds[i]) { arrChilds[i]->OffsetPosition(delta); } } } } CVisAreaManager::~CVisAreaManager() { if (GetRenderer()) { GetRenderer()->EF_ClearDeferredClipVolumesList(); } DeleteAllVisAreas(); delete m_pAABBTree; m_pAABBTree = NULL; } SAABBTreeNode::SAABBTreeNode(PodArray& lstAreas, AABB box, int nRecursion) { memset(this, 0, sizeof(*this)); nodeBox = box; nRecursion++; if (nRecursion > 8 || lstAreas.Count() < 8) { nodeAreas.AddList(lstAreas); return; } PodArray lstAreas0, lstAreas1; Vec3 vSize = nodeBox.GetSize(); Vec3 vCenter = nodeBox.GetCenter(); AABB nodeBox0 = nodeBox; AABB nodeBox1 = nodeBox; if (vSize.x >= vSize.y && vSize.x >= vSize.z) { nodeBox0.min.x = vCenter.x; nodeBox1.max.x = vCenter.x; } else if (vSize.y >= vSize.x && vSize.y >= vSize.z) { nodeBox0.min.y = vCenter.y; nodeBox1.max.y = vCenter.y; } else { nodeBox0.min.z = vCenter.z; nodeBox1.max.z = vCenter.z; } for (int i = 0; i < lstAreas.Count(); i++) { if (Overlap::AABB_AABB(nodeBox0, *lstAreas[i]->GetAABBox())) { lstAreas0.Add(lstAreas[i]); } if (Overlap::AABB_AABB(nodeBox1, *lstAreas[i]->GetAABBox())) { lstAreas1.Add(lstAreas[i]); } } if (lstAreas0.Count()) { arrChilds[0] = new SAABBTreeNode(lstAreas0, nodeBox0, nRecursion); } if (lstAreas1.Count()) { arrChilds[1] = new SAABBTreeNode(lstAreas1, nodeBox1, nRecursion); } } SAABBTreeNode::~SAABBTreeNode() { delete arrChilds[0]; delete arrChilds[1]; } SAABBTreeNode* SAABBTreeNode::GetTopNode(const AABB& box, void** pNodeCache) { AABB boxClip = box; boxClip.ClipToBox(nodeBox); SAABBTreeNode* pNode = this; if (pNodeCache) { pNode = (SAABBTreeNode*)*pNodeCache; if (!pNode || !pNode->nodeBox.ContainsBox(boxClip)) { pNode = this; } } // Find top node containing box. for (;; ) { int i; for (i = 0; i < 2; i++) { if (pNode->arrChilds[i] && pNode->arrChilds[i]->nodeBox.ContainsBox(boxClip)) { pNode = pNode->arrChilds[i]; break; } } if (i == 2) { break; } } if (pNodeCache) { *(SAABBTreeNode**)pNodeCache = pNode; } return pNode; } bool SAABBTreeNode::IntersectsVisAreas(const AABB& box) { if (nodeBox.IsIntersectBox(box)) { if (nodeAreas.Count()) { // leaf for (int i = 0; i < nodeAreas.Count(); i++) { if (nodeAreas[i]->m_bActive && nodeAreas[i]->m_boxArea.IsIntersectBox(box)) { return true; } } } else { // node for (int i = 0; i < 2; i++) { if (arrChilds[i]) { if (arrChilds[i]->IntersectsVisAreas(box)) { return true; } } } } } return false; } int SAABBTreeNode::ClipOutsideVisAreas(Sphere& sphere, Vec3 const& vNormal) { int nClipped = 0; if (sphere.radius > FLT_MAX * 0.01f || Overlap::Sphere_AABB(sphere, nodeBox)) { if (nodeAreas.Count()) { // leaf for (int i = 0; i < nodeAreas.Count(); i++) { if (nodeAreas[i]->m_bActive && Overlap::Sphere_AABB(sphere, nodeAreas[i]->m_boxArea)) { nClipped += nodeAreas[i]->ClipToVisArea(false, sphere, vNormal); } } } else { // node for (int i = 0; i < 2; i++) { if (arrChilds[i]) { nClipped += arrChilds[i]->ClipOutsideVisAreas(sphere, vNormal); } } } } return nClipped; } void CVisAreaManager::UpdateAABBTree() { delete m_pAABBTree; PodArray lstAreas; lstAreas.AddList(m_lstPortals); lstAreas.AddList(m_lstVisAreas); AABB nodeBox; nodeBox.min = Vec3(1000000, 1000000, 1000000); nodeBox.max = -nodeBox.min; for (int i = 0; i < lstAreas.Count(); i++) { nodeBox.Add(*lstAreas[i]->GetAABBox()); } m_pAABBTree = new SAABBTreeNode(lstAreas, nodeBox); } bool CVisAreaManager::IsEntityVisible(IRenderNode* pEnt) { if (GetCVars()->e_Portals == 3) { return true; } if (!pEnt->GetEntityVisArea()) { return IsOutdoorAreasVisible(); } return true; } void CVisAreaManager::SetCurAreas(const SRenderingPassInfo& passInfo) { m_pCurArea = 0; m_pCurPortal = 0; if (!GetCVars()->e_Portals) { return; } if (!m_pAABBTree) { UpdateAABBTree(); } CVisArea* pFound = m_pAABBTree->FindVisarea(passInfo.GetCamera().GetOccPos()); #ifdef _DEBUG // find camera portal id for (int v = 0; v < m_lstPortals.Count(); v++) { if (m_lstPortals[v]->m_bActive && m_lstPortals[v]->IsPointInsideVisArea(passInfo.GetCamera().GetOccPos())) { m_pCurPortal = m_lstPortals[v]; break; } } // if not inside any portal - try to find area if (!m_pCurPortal) { // int nFoundAreasNum = 0; // find camera area for (int nVolumeId = 0; nVolumeId < m_lstVisAreas.Count(); nVolumeId++) { if (m_lstVisAreas[nVolumeId]->IsPointInsideVisArea(passInfo.GetCamera().GetOccPos())) { // nFoundAreasNum++; m_pCurArea = m_lstVisAreas[nVolumeId]; break; } } // if(nFoundAreasNum>1) // if more than one area found - set cur area to undefined { // todo: try to set joining portal as current // m_pCurArea = 0; } } assert(pFound == m_pCurArea || pFound == m_pCurPortal); #endif // _DEBUG if (pFound) { if (pFound->IsPortal()) { m_pCurPortal = pFound; } else { m_pCurArea = pFound; } } // camera is in outdoors m_lstActiveEntransePortals.Clear(); if (!m_pCurArea && !m_pCurPortal) { MakeActiveEntransePortalsList(&passInfo.GetCamera(), m_lstActiveEntransePortals, 0, passInfo); } /* if(m_pCurArea) { IVisArea * arrAreas[8]; int nres = m_pCurArea->GetVisAreaConnections(arrAreas, 8); nres=nres; } DefineTrees();*/ /* if(GetCVars()->e_Portals == 4) { if(m_pCurPortal) { IVisArea * arrAreas[64]; int nConnections = m_pCurPortal->GetVisAreaConnections(arrAreas,64); PrintMessage("CurPortal = %s, nConnections = %d", m_pCurPortal->m_sName, nConnections); } if(m_pCurArea) { IVisArea * arrAreas[64]; int nConnections = m_pCurArea->GetVisAreaConnections(arrAreas,64); PrintMessage("CurArea = %s, nRes = %d", m_pCurArea->m_sName, nConnections); } }*/ } bool CVisAreaManager::IsSkyVisible() { return m_bSkyVisible; } bool CVisAreaManager::IsOceanVisible() { return m_bOceanVisible; } bool CVisAreaManager::IsOutdoorAreasVisible() { if (!m_pCurArea && !m_pCurPortal) { m_bOutdoorVisible = true; return m_bOutdoorVisible; // camera not in the areas } if (m_pCurPortal && m_pCurPortal->m_lstConnections.Count() == 1) { m_bOutdoorVisible = true; return m_bOutdoorVisible; // camera is in exit portal } if (m_bOutdoorVisible) { return true; // exit is visible } // note: outdoor camera is no modified in this case return false; } /*void CVisAreaManager::SetAreaFogVolume(CTerrain * pTerrain, CVisArea * pVisArea) { pVisArea->m_pFogVolume=0; for(int f=0; fGetFogVolumes().Count(); f++) { const Vec3 & v1Min = Get3DEngine()->GetFogVolumes()[f].box.min; const Vec3 & v1Max = Get3DEngine()->GetFogVolumes()[f].box.max; const Vec3 & v2Min = pVisArea->m_boxArea.min; const Vec3 & v2Max = pVisArea->m_boxArea.max; if(v1Max.x>v2Min.x && v2Max.x>v1Min.x) if(v1Max.y>v2Min.y && v2Max.y>v1Min.y) if(v1Max.z>v2Min.z && v2Max.z>v1Min.z) if(!Get3DEngine()->GetFogVolumes()[f].bOcean) { Vec3 arrVerts3d[8] = { Vec3(v1Min.x,v1Min.y,v1Min.z), Vec3(v1Min.x,v1Max.y,v1Min.z), Vec3(v1Max.x,v1Min.y,v1Min.z), Vec3(v1Max.x,v1Max.y,v1Min.z), Vec3(v1Min.x,v1Min.y,v1Max.z), Vec3(v1Min.x,v1Max.y,v1Max.z), Vec3(v1Max.x,v1Min.y,v1Max.z), Vec3(v1Max.x,v1Max.y,v1Max.z) }; bool bIntersect = false; for(int i=0; i<8; i++) if(pVisArea->IsPointInsideVisArea(arrVerts3d[i])) { bIntersect = true; break; } if(!bIntersect) if(pVisArea->IsPointInsideVisArea((v1Min+v1Max)*0.5f)) bIntersect = true; if(!bIntersect) { for(int i=0; im_lstShapePoints.Count(); i++) if(Get3DEngine()->GetFogVolumes()[f].IsInsideBBox(pVisArea->m_lstShapePoints[i])) { bIntersect = true; break; } } if(!bIntersect) { Vec3 vCenter = (pVisArea->m_boxArea.min+pVisArea->m_boxArea.max)*0.5f; if(Get3DEngine()->GetFogVolumes()[f].IsInsideBBox(vCenter)) bIntersect = true; } if(bIntersect) { pVisArea->m_pFogVolume = &Get3DEngine()->GetFogVolumes()[f]; Get3DEngine()->GetFogVolumes()[f].bIndoorOnly = true; pTerrain->UnregisterFogVolumeFromOutdoor(&Get3DEngine()->GetFogVolumes()[f]); break; } } } }*/ void CVisAreaManager::PortalsDrawDebug() { UpdateConnections(); /* if(m_pCurArea) { for(int p=0; pm_lstConnections.Count(); p++) { CVisArea * pPortal = m_pCurArea->m_lstConnections[p]; float fBlink = gEnv->pTimer->GetFrameStartTime().GetPeriodicFraction(1.0f)>0.5f ? 1.0f : 0.0f; float fError = pPortal->IsPortalValid() ? 1.0f : fBlink; GetRenderer()->SetMaterialColor(fError,fError*(pPortal->m_lstConnections.Count()<2),0,0.25f); DrawBBox(pPortal->m_boxArea.min, pPortal->m_boxArea.max, DPRIM_SOLID_BOX); GetRenderer()->DrawLabel((pPortal->m_boxArea.min+ pPortal->m_boxArea.max)*0.5f, 2,pPortal->m_sName); } } else*/ { // debug draw areas GetRenderer()->SetMaterialColor(0, 1, 0, 0.25f); Vec3 oneVec(1, 1, 1); for (int v = 0; v < m_lstVisAreas.Count(); v++) { DrawBBox(m_lstVisAreas[v]->m_boxArea.min, m_lstVisAreas[v]->m_boxArea.max);//, DPRIM_SOLID_BOX); GetRenderer()->DrawLabelEx((m_lstVisAreas[v]->m_boxArea.min + m_lstVisAreas[v]->m_boxArea.max) * 0.5f, 1, (float*)&oneVec, 0, 1, m_lstVisAreas[v]->GetName()); GetRenderer()->SetMaterialColor(0, 1, 0, 0.25f); DrawBBox(m_lstVisAreas[v]->m_boxStatics, Col_LightGray); } // debug draw portals for (int v = 0; v < m_lstPortals.Count(); v++) { CVisArea* pPortal = m_lstPortals[v]; float fBlink = gEnv->pTimer->GetFrameStartTime().GetPeriodicFraction(1.0f) > 0.5f ? 1.0f : 0.0f; float fError = pPortal->IsPortalValid() ? 1.f : fBlink; ColorB col( (int)clamp_tpl(fError * 255.0f, 0.0f, 255.0f), (int)clamp_tpl(fError * (pPortal->m_lstConnections.Count() < 2) * 255.0f, 0.0f, 255.0f), 0, 64); DrawBBox(pPortal->m_boxArea.min, pPortal->m_boxArea.max, col); GetRenderer()->DrawLabelEx((pPortal->m_boxArea.min + pPortal->m_boxArea.max) * 0.5f, 1, (float*)&oneVec, 0, 1, pPortal->GetName()); Vec3 vCenter = (pPortal->m_boxArea.min + pPortal->m_boxArea.max) * 0.5f; DrawBBox(vCenter - Vec3(0.1f, 0.1f, 0.1f), vCenter + Vec3(0.1f, 0.1f, 0.1f)); int nConnections = pPortal->m_lstConnections.Count(); if (nConnections == 1) { col = ColorB(0, 255, 0, 64); } else { col = ColorB(0, 0, 255, 64); } for (int i = 0; i < nConnections && i < 2; i++) { DrawLine(vCenter, vCenter + pPortal->m_vConnNormals[i], col); } DrawBBox(pPortal->m_boxStatics.min, pPortal->m_boxStatics.max, col); } /* // debug draw area shape GetRenderer()->SetMaterialColor(0,0,1,0.25f); for(int v=0; vm_lstShapePoints.Count(); p++) GetRenderer()->DrawLabel(m_lstVisAreas[v]->m_lstShapePoints[p], 2,"%d", p); for(int v=0; vm_lstShapePoints.Count(); p++) GetRenderer()->DrawLabel(m_lstPortals[v]->m_lstShapePoints[p], 2,"%d", p);*/ } } void CVisAreaManager::DrawVisibleSectors(const SRenderingPassInfo& passInfo, SRendItemSorter& rendItemSorter) { FUNCTION_PROFILER_3DENGINE_LEGACYONLY; AZ_TRACE_METHOD(); for (int i = 0; i < m_lstVisibleAreas.Count(); i++) { CVisArea* pArea = m_lstVisibleAreas[i]; if (pArea->m_pObjectsTree) { for (int c = 0; c < pArea->m_lstCurCamerasLen; c++) { rendItemSorter.IncreaseOctreeCounter(); // create a new RenderingPassInfo object, which a camera matching the visarea pArea->m_pObjectsTree->Render_Object_Nodes(false, OCTREENODE_RENDER_FLAG_OBJECTS, SRenderingPassInfo::CreateTempRenderingInfo(CVisArea::s_tmpCameras[pArea->m_lstCurCamerasIdx + c], passInfo), rendItemSorter); } } } rendItemSorter.IncreaseGroupCounter(); } void CVisAreaManager::CheckVis(const SRenderingPassInfo& passInfo) { FUNCTION_PROFILER_3DENGINE_LEGACYONLY; AZ_TRACE_METHOD(); if (passInfo.IsGeneralPass()) { m_bOutdoorVisible = false; m_bSkyVisible = false; m_bOceanVisible = false; CVisArea::s_tmpCameras.Clear(); } m_lstOutdoorPortalCameras.Clear(); m_lstVisibleAreas.Clear(); m_bSunIsNeeded = false; GetRenderer()->EF_ClearDeferredClipVolumesList(); SetCurAreas(passInfo); CCamera camRoot = passInfo.GetCamera(); camRoot.m_ScissorInfo.x1 = 0; camRoot.m_ScissorInfo.y1 = 0; camRoot.m_ScissorInfo.x2 = GetRenderer()->GetWidth(); // todo: use values from camera camRoot.m_ScissorInfo.y2 = GetRenderer()->GetHeight(); if (GetCVars()->e_Portals == 3) { // draw everything for debug for (int i = 0; i < m_lstVisAreas.Count(); i++) { if (camRoot.IsAABBVisible_F(AABB(m_lstVisAreas[i]->m_boxArea.min, m_lstVisAreas[i]->m_boxArea.max))) { m_lstVisAreas[i]->PreRender(0, camRoot, 0, m_pCurPortal, &m_bOutdoorVisible, &m_lstOutdoorPortalCameras, &m_bSkyVisible, &m_bOceanVisible, m_lstVisibleAreas, passInfo); } } for (int i = 0; i < m_lstPortals.Count(); i++) { if (camRoot.IsAABBVisible_F(AABB(m_lstPortals[i]->m_boxArea.min, m_lstPortals[i]->m_boxArea.max))) { m_lstPortals[i]->PreRender(0, camRoot, 0, m_pCurPortal, &m_bOutdoorVisible, &m_lstOutdoorPortalCameras, &m_bSkyVisible, &m_bOceanVisible, m_lstVisibleAreas, passInfo); } } } else { if (passInfo.IsRecursivePass()) { // use another starting point for reflections CVisArea* pVisArea = (CVisArea*)GetVisAreaFromPos(camRoot.GetOccPos()); if (pVisArea) { pVisArea->PreRender(3, camRoot, 0, m_pCurPortal, &m_bOutdoorVisible, &m_lstOutdoorPortalCameras, &m_bSkyVisible, &m_bOceanVisible, m_lstVisibleAreas, passInfo); } } else if (m_pCurArea) { // camera inside some sector m_pCurArea->PreRender(GetCVars()->e_PortalsMaxRecursion, camRoot, 0, m_pCurPortal, &m_bOutdoorVisible, &m_lstOutdoorPortalCameras, &m_bSkyVisible, &m_bOceanVisible, m_lstVisibleAreas, passInfo); for (int ii = 0; ii < m_lstOutdoorPortalCameras.Count(); ii++) // process all exit portals { // for each portal build list of potentially visible entrances into other areas MakeActiveEntransePortalsList(&m_lstOutdoorPortalCameras[ii], m_lstActiveEntransePortals, (CVisArea*)m_lstOutdoorPortalCameras[ii].m_pPortal, passInfo); for (int i = 0; i < m_lstActiveEntransePortals.Count(); i++) // entrance into another building is visible { m_lstActiveEntransePortals[i]->PreRender(i == 0 ? 5 : 1, m_lstOutdoorPortalCameras[ii], 0, m_pCurPortal, 0, 0, 0, 0, m_lstVisibleAreas, passInfo); } } // reset scissor if skybox is visible also thru skyboxonly portal if (m_bSkyVisible && m_lstOutdoorPortalCameras.Count() == 1) { m_lstOutdoorPortalCameras[0].m_ScissorInfo.x1 = m_lstOutdoorPortalCameras[0].m_ScissorInfo.x2 = m_lstOutdoorPortalCameras[0].m_ScissorInfo.y1 = m_lstOutdoorPortalCameras[0].m_ScissorInfo.y2 = 0; } } else if (m_pCurPortal) { // camera inside some portal m_pCurPortal->PreRender(GetCVars()->e_PortalsMaxRecursion - 1, camRoot, 0, m_pCurPortal, &m_bOutdoorVisible, &m_lstOutdoorPortalCameras, &m_bSkyVisible, &m_bOceanVisible, m_lstVisibleAreas, passInfo); if (m_pCurPortal->m_lstConnections.Count() == 1) { m_lstOutdoorPortalCameras.Clear(); // camera in outdoor } if (m_pCurPortal->m_lstConnections.Count() == 1 || m_lstOutdoorPortalCameras.Count()) { // if camera is in exit portal or exit is visible MakeActiveEntransePortalsList(m_lstOutdoorPortalCameras.Count() ? &m_lstOutdoorPortalCameras[0] : &camRoot, m_lstActiveEntransePortals, m_lstOutdoorPortalCameras.Count() ? (CVisArea*)m_lstOutdoorPortalCameras[0].m_pPortal : m_pCurPortal, passInfo); for (int i = 0; i < m_lstActiveEntransePortals.Count(); i++) // entrance into another building is visible { m_lstActiveEntransePortals[i]->PreRender(i == 0 ? 5 : 1, m_lstOutdoorPortalCameras.Count() ? m_lstOutdoorPortalCameras[0] : camRoot, 0, m_pCurPortal, 0, 0, 0, 0, m_lstVisibleAreas, passInfo); } // m_lstOutdoorPortalCameras.Clear(); // otherwise ocean in fleet was not scissored } } else if (m_lstActiveEntransePortals.Count()) { // camera in outdoors - process visible entrance portals for (int i = 0; i < m_lstActiveEntransePortals.Count(); i++) { m_lstActiveEntransePortals[i]->PreRender(5, camRoot, 0, m_lstActiveEntransePortals[i], &m_bOutdoorVisible, &m_lstOutdoorPortalCameras, &m_bSkyVisible, &m_bOceanVisible, m_lstVisibleAreas, passInfo); } m_lstActiveEntransePortals.Clear(); // do not recurse to another building since we already processed all potential entrances m_lstOutdoorPortalCameras.Clear(); // use default camera m_bOutdoorVisible = true; } } if (GetCVars()->e_Portals == 2) { PortalsDrawDebug(); } } void CVisAreaManager::ActivatePortal(const Vec3& vPos, bool bActivate, const char* szEntityName) { // bool bFound = false; for (int v = 0; v < m_lstPortals.Count(); v++) { AABB aabb; aabb.min = m_lstPortals[v]->m_boxArea.min - Vec3(0.5f, 0.5f, 0.1f); aabb.max = m_lstPortals[v]->m_boxArea.max + Vec3(0.5f, 0.5f, 0.0f); if (Overlap::Point_AABB(vPos, aabb)) { m_lstPortals[v]->m_bActive = bActivate; // switch to PrintComment once portals activation is working stable PrintMessage("I3DEngine::ActivatePortal(): Portal %s is %s by entity %s at position(%.1f,%.1f,%.1f)", m_lstPortals[v]->GetName(), bActivate ? "Enabled" : "Disabled", szEntityName, vPos.x, vPos.y, vPos.z); // bFound = true; } } /* if(!bFound) { PrintComment("I3DEngine::ActivatePortal(): Portal not found for entity %s at position(%.1f,%.1f,%.1f)", szEntityName, vPos.x, vPos.y, vPos.z); } */ } /* bool CVisAreaManager::IsEntityInVisibleArea(IRenderNodeState * pRS) { if( pRS && pRS->plstVisAreaId && pRS->plstVisAreaId->Count() ) { PodArray * pVisAreas = pRS->plstVisAreaId; for(int n=0; nCount(); n++) if( m_lstVisAreas[pVisAreas->GetAt(n)].m_nVisFrameId==passInfo.GetFrameID() ) break; if(n==pVisAreas->Count()) return false; // no visible areas } else return false; // entity is not inside return true; } */ bool CVisAreaManager::IsValidVisAreaPointer(CVisArea* pVisArea) { if (m_lstVisAreas.Find(pVisArea) < 0 && m_lstPortals.Find(pVisArea) < 0 && m_lstOcclAreas.Find(pVisArea) < 0) { return false; } return true; } //This is only called from the editor, so pVisArea will not be pool allocated by type bool CVisAreaManager::DeleteVisArea(CVisArea* pVisArea) { bool bFound = false; if (m_lstVisAreas.Delete(pVisArea) || m_lstPortals.Delete(pVisArea) || m_lstOcclAreas.Delete(pVisArea)) { delete pVisArea; bFound = true; } m_lstActiveOcclVolumes.Delete(pVisArea); m_lstIndoorActiveOcclVolumes.Delete(pVisArea); m_lstActiveEntransePortals.Delete(pVisArea); m_pCurArea = 0; m_pCurPortal = 0; UpdateConnections(); delete m_pAABBTree; m_pAABBTree = NULL; return bFound; } /*void CVisAreaManager::LoadVisAreaShapeFromXML(XmlNodeRef pDoc) { for(int i=0; ifindChild("Objects"); if (pObjectsNode) { for (int i = 0; i < pObjectsNode->getChildCount(); i++) { XmlNodeRef pNode = pObjectsNode->getChild(i); if (pNode->isTag("Object")) { const char * pType = pNode->getAttr("Type"); if (strstr(pType,"OccluderArea") || strstr(pType,"VisArea") || strstr(pType,"Portal")) { CVisArea * pArea = new CVisArea(); pArea->m_boxArea.max=SetMinBB(); pArea->m_boxArea.min=SetMaxBB(); // set name strcpy(pArea->m_sName, pNode->getAttr("Name")); strlwr(pArea->m_sName); // set height pNode->getAttr("Height",pArea->m_fHeight); // set ambient color pNode->getAttr("AmbientColor", pArea->m_vAmbColor); // set dynamic ambient color // pNode->getAttr("DynAmbientColor", pArea->m_vDynAmbColor); // set SkyOnly flag pNode->getAttr("SkyOnly", pArea->m_bSkyOnly); // set AfectedByOutLights flag pNode->getAttr("AffectedBySun", pArea->m_bAfectedByOutLights); // set ViewDistRatio pNode->getAttr("ViewDistRatio", pArea->m_fViewDistRatio); // set DoubleSide flag pNode->getAttr("DoubleSide", pArea->m_bDoubleSide); // set UseInIndoors flag pNode->getAttr("UseInIndoors", pArea->m_bUseInIndoors); if(strstr(pType, "OccluderArea")) m_lstOcclAreas.Add(pArea); else if(strstr(pArea->m_sName,"portal") || strstr(pType,"Portal")) m_lstPortals.Add(pArea); else m_lstVisAreas.Add(pArea); // load vertices XmlNodeRef pPointsNode = pNode->findChild("Points"); if (pPointsNode) for (int i = 0; i < pPointsNode->getChildCount(); i++) { XmlNodeRef pPointNode = pPointsNode->getChild(i); Vec3 vPos; if (pPointNode->isTag("Point") && pPointNode->getAttr("Pos", vPos)) { pArea->m_lstShapePoints.Add(vPos); pArea->m_boxArea.max.CheckMax(vPos); pArea->m_boxArea.min.CheckMin(vPos); pArea->m_boxArea.max.CheckMax(vPos+Vec3(0,0,pArea->m_fHeight)); pArea->m_boxArea.min.CheckMin(vPos+Vec3(0,0,pArea->m_fHeight)); } } pArea->UpdateGeometryBBox(); } } } } // load area boxes to support old way // LoadVisAreaBoxFromXML(pDoc); }*/ //THIS SHOULD ONLY BE CALLED BY THE EDITOR void CVisAreaManager::UpdateVisArea(CVisArea* pArea, const Vec3* pPoints, int nCount, const char* szName, const SVisAreaInfo& info) { // on first update there will be nothing to delete, area will be added into list only in this function // If pArea is in these lists, then remove it. const VisAreaGUID& areaGUID = pArea->GetGUID(); for (int i = 0; i < m_lstVisAreas.Count(); ++i) { CVisArea* pAreaInList = m_lstVisAreas[i]; if (areaGUID == pAreaInList->GetGUID()) { m_lstVisAreas.Delete(i); --i; } } for (int i = 0; i < m_lstPortals.Count(); ++i) { CVisArea* pPortalInList = m_lstPortals[i]; if (areaGUID == pPortalInList->GetGUID()) { m_lstPortals.Delete(i); --i; } } for (int i = 0; i < m_lstOcclAreas.Count(); ++i) { CVisArea* pOccAreaInList = m_lstOcclAreas[i]; if (areaGUID == pOccAreaInList->GetGUID()) { m_lstOcclAreas.Delete(i); --i; } } SGenericColdData* pColdData = pArea->GetColdData(); if (pColdData != NULL) { if (pColdData->m_dataType == eCDT_Portal) { SPortalColdData* pPortalColdData = static_cast(pColdData); if (pPortalColdData->m_pRNTmpData) { Get3DEngine()->FreeRNTmpData(&pPortalColdData->m_pRNTmpData); pPortalColdData->m_pRNTmpData = NULL; } } delete pArea->GetColdData(); pArea->SetColdDataPtr(NULL); } SGenericColdData* pColdDataPtr = NULL; char sTemp[64]; cry_strcpy(sTemp, szName); _strlwr_s(sTemp, sizeof(sTemp)); bool bPortal = false; bool bVisArea = false; bool bOcclArea = false; //TODO: Refactor with code below so it's not horrible if (strstr(sTemp, "portal")) { pColdDataPtr = new SPortalColdData(); bPortal = true; } else if (strstr(sTemp, "visarea")) { pColdDataPtr = new SGenericColdData(); bVisArea = true; } else if (strstr(sTemp, "occlarea")) { pColdDataPtr = new SGenericColdData(); bOcclArea = true; } else { pColdDataPtr = new SGenericColdData(); } assert(pColdDataPtr); pArea->SetColdDataPtr(pColdDataPtr); pArea->Update(pPoints, nCount, sTemp, info); if (bPortal) { if (pArea->m_lstConnections.Count() == 1) { pArea->UpdateGeometryBBox(); } m_lstPortals.Add(pArea); } else if (bVisArea) { m_lstVisAreas.Add(pArea); } else if (bOcclArea) { m_lstOcclAreas.Add(pArea); } UpdateConnections(); delete m_pAABBTree; m_pAABBTree = NULL; } void CVisAreaManager::UpdateConnections() { // Reset connectivity for (int p = 0; p < m_lstPortals.Count(); p++) { m_lstPortals[p]->m_lstConnections.Clear(); } for (int v = 0; v < m_lstVisAreas.Count(); v++) { m_lstVisAreas[v]->m_lstConnections.Clear(); } // Init connectivity - check intersection of all areas and portals for (int p = 0; p < m_lstPortals.Count(); p++) { for (int v = 0; v < m_lstVisAreas.Count(); v++) { if (m_lstVisAreas[v]->IsPortalIntersectAreaInValidWay(m_lstPortals[p])) { // if bboxes intersect m_lstVisAreas[v]->m_lstConnections.Add(m_lstPortals[p]); m_lstPortals[p]->m_lstConnections.Add(m_lstVisAreas[v]); // set portal direction Vec3 vNormal = m_lstVisAreas[v]->GetConnectionNormal(m_lstPortals[p]); if (m_lstPortals[p]->m_lstConnections.Count() <= 2) { m_lstPortals[p]->m_vConnNormals[m_lstPortals[p]->m_lstConnections.Count() - 1] = vNormal; } } } } } void CVisAreaManager::MoveObjectsIntoList(PodArray* plstVisAreasEntities, const AABB& boxArea, bool bRemoveObjects) { for (int p = 0; p < m_lstPortals.Count(); p++) { if (m_lstPortals[p]->m_pObjectsTree && Overlap::AABB_AABB(m_lstPortals[p]->m_boxArea, boxArea)) { m_lstPortals[p]->m_pObjectsTree->MoveObjectsIntoList(plstVisAreasEntities, bRemoveObjects ? NULL : &boxArea, bRemoveObjects); } } for (int v = 0; v < m_lstVisAreas.Count(); v++) { if (m_lstVisAreas[v]->m_pObjectsTree && Overlap::AABB_AABB(m_lstVisAreas[v]->m_boxArea, boxArea)) { m_lstVisAreas[v]->m_pObjectsTree->MoveObjectsIntoList(plstVisAreasEntities, bRemoveObjects ? NULL : &boxArea, bRemoveObjects); } } } bool CVisAreaManager::IntersectsVisAreas(const AABB& box, void** pNodeCache) { FUNCTION_PROFILER_3DENGINE; if (!m_pAABBTree) { UpdateAABBTree(); } SAABBTreeNode* pTopNode = m_pAABBTree->GetTopNode(box, pNodeCache); return pTopNode->IntersectsVisAreas(box); } bool CVisAreaManager::ClipOutsideVisAreas(Sphere& sphere, Vec3 const& vNormal, void* pNodeCache) { FUNCTION_PROFILER_3DENGINE; if (!m_pAABBTree) { UpdateAABBTree(); } AABB box(sphere.center - Vec3(sphere.radius), sphere.center + Vec3(sphere.radius)); SAABBTreeNode* pTopNode = m_pAABBTree->GetTopNode(box, &pNodeCache); return pTopNode->ClipOutsideVisAreas(sphere, vNormal) > 0; } //This is used by the editor. Use the visareas pool for all areas, so prefetching // is still safe. CVisArea* CVisAreaManager::CreateVisArea(VisAreaGUID visGUID) { return new CVisArea(visGUID); } bool CVisAreaManager::IsEntityVisAreaVisibleReqursive(CVisArea* pVisArea, int nMaxReqursion, PodArray* pUnavailableAreas, const CDLight* pLight, const SRenderingPassInfo& passInfo) { int nAreaId = pUnavailableAreas->Count(); pUnavailableAreas->Add(pVisArea); bool bFound = false; if (pVisArea) { // check is lsource area was rendered in prev frame if (abs(pVisArea->m_nRndFrameId - passInfo.GetFrameID()) > 2) { if (nMaxReqursion > 1) { for (int n = 0; n < pVisArea->m_lstConnections.Count(); n++) { // loop other sectors CVisArea* pNeibArea = (CVisArea*)pVisArea->m_lstConnections[n]; if (-1 == pUnavailableAreas->Find(pNeibArea) && (!pLight || Overlap::Sphere_AABB(Sphere(pLight->m_Origin, pLight->m_fRadius), *pNeibArea->GetAABBox()))) { if (IsEntityVisAreaVisibleReqursive(pNeibArea, nMaxReqursion - 1, pUnavailableAreas, pLight, passInfo)) { bFound = true; break; }//if visible } }// for } } else { bFound = true; } } else if (IsOutdoorAreasVisible()) //Indirect - outdoor can be a problem! { bFound = true; } pUnavailableAreas->Delete(nAreaId); return bFound; } bool CVisAreaManager::IsEntityVisAreaVisible(IRenderNode* pEnt, int nMaxReqursion, const CDLight* pLight, const SRenderingPassInfo& passInfo) { if (!pEnt) { return false; } PodArray& lUnavailableAreas = m_tmpLstUnavailableAreas; lUnavailableAreas.Clear(); lUnavailableAreas.PreAllocate(nMaxReqursion, 0); return IsEntityVisAreaVisibleReqursive((CVisArea*)pEnt->GetEntityVisArea(), nMaxReqursion, &lUnavailableAreas, pLight, passInfo); /* if(pEnt->GetEntityVisArea()) { if(pEnt->GetEntityVisArea())//->IsPortal()) { // check is lsource area was rendered in prev frame CVisArea * pVisArea = pEnt->GetEntityVisArea(); int nRndFrameId = passInfo.GetFrameID(); if(abs(pVisArea->m_nRndFrameId - nRndFrameId)>2) { if(!nCheckNeighbors) return false; // this area is not visible // try neibhour areas bool bFound = false; if(pEnt->GetEntityVisArea()->IsPortal()) { CVisArea * pPort = pEnt->GetEntityVisArea(); for(int n=0; nm_lstConnections.Count(); n++) { // loop other sectors CVisArea * pNeibArea = (CVisArea*)pPort->m_lstConnections[n]; if(abs(pNeibArea->m_nRndFrameId - passInfo.GetFrameID())<=2) { bFound=true; break; } } } else { for(int t=0; !bFound && tm_lstConnections.Count(); t++) { // loop portals CVisArea * pPort = (CVisArea*)pVisArea->m_lstConnections[t]; if(abs(pPort->m_nRndFrameId - passInfo.GetFrameID())<=2) { bFound=true; break; } for(int n=0; nm_lstConnections.Count(); n++) { // loop other sectors CVisArea * pNeibArea = (CVisArea*)pPort->m_lstConnections[n]; if(abs(pNeibArea->m_nRndFrameId - passInfo.GetFrameID())<=2) { bFound=true; break; } } } } if(!bFound) return false; return true; } } else return false; // not visible } else if(!IsOutdoorAreasVisible()) return false; return true; */ } int __cdecl CVisAreaManager__CmpDistToPortal(const void* v1, const void* v2) { CVisArea* p1 = *((CVisArea**)v1); CVisArea* p2 = *((CVisArea**)v2); if (!p1 || !p2) { return 0; } if (p1->m_fDistance > p2->m_fDistance) { return 1; } else if (p1->m_fDistance < p2->m_fDistance) { return -1; } return 0; } void CVisAreaManager::MakeActiveEntransePortalsList(const CCamera* pCamera, PodArray& lstActiveEntransePortals, CVisArea* pThisPortal, const SRenderingPassInfo& passInfo) { lstActiveEntransePortals.Clear(); float fZoomFactor = pCamera ? (0.2f + 0.8f * (RAD2DEG(pCamera->GetFov()) / 90.f)) : 1.f; for (int nPortalId = 0; nPortalId < m_lstPortals.Count(); nPortalId++) { CVisArea* pPortal = m_lstPortals[nPortalId]; if (pPortal->m_lstConnections.Count() == 1 && pPortal != pThisPortal && pPortal->IsActive() && !pPortal->m_bSkyOnly) { if (!pCamera || pCamera->IsAABBVisible_F(pPortal->m_boxStatics)) { Vec3 vNormal = pPortal->m_lstConnections[0]->GetConnectionNormal(pPortal); Vec3 vCenter = (pPortal->m_boxArea.min + pPortal->m_boxArea.max) * 0.5f; if (vNormal.Dot(vCenter - (pCamera ? pCamera->GetPosition() : passInfo.GetCamera().GetPosition())) < 0) { continue; } /* if(pCurPortal) { vNormal = pCurPortal->m_vConnNormals[0]; if(vNormal.Dot(vCenter - curCamera.GetPosition())<0) continue; } */ pPortal->m_fDistance = pPortal->m_boxArea.GetDistance(pCamera ? pCamera->GetPosition() : passInfo.GetCamera().GetPosition()); float fRadius = (pPortal->m_boxArea.max - pPortal->m_boxArea.min).GetLength() * 0.5f; if (pPortal->m_fDistance * fZoomFactor > fRadius * pPortal->m_fViewDistRatio * GetFloatCVar(e_ViewDistRatioPortals) / 60.f) { continue; } SPortalColdData* pColdData = static_cast(pPortal->GetColdData()); Get3DEngine()->CheckCreateRNTmpData(&pColdData->m_pRNTmpData, NULL, passInfo); // test occlusion if (GetObjManager()->IsBoxOccluded(pPortal->m_boxStatics, pPortal->m_fDistance, &pColdData->m_pRNTmpData->userData.m_OcclState, false, eoot_PORTAL, passInfo)) { continue; } lstActiveEntransePortals.Add(pPortal); // if(GetCVars()->e_Portals==3) // DrawBBox(pPortal->m_boxStatics.min, pPortal->m_boxStatics.max); } } } // sort by distance if (lstActiveEntransePortals.Count()) { qsort(&lstActiveEntransePortals[0], lstActiveEntransePortals.Count(), sizeof(lstActiveEntransePortals[0]), CVisAreaManager__CmpDistToPortal); // m_pCurPortal = lstActiveEntransePortals[0]; } } void CVisAreaManager::DrawOcclusionAreasIntoCBuffer(CCullBuffer* pCBuffer, const SRenderingPassInfo& passInfo) { FUNCTION_PROFILER_3DENGINE; m_lstActiveOcclVolumes.Clear(); m_lstIndoorActiveOcclVolumes.Clear(); #if defined(OCCLUSIONCULLER_W) m_allActiveVerts.resize(0); m_allActiveVerts.reserve(m_lstOcclAreas.Count()); #endif float fZoomFactor = 0.2f + 0.8f * (RAD2DEG(passInfo.GetCamera().GetFov()) / 90.f); float fDistRatio = GetFloatCVar(e_OcclusionVolumesViewDistRatio) / fZoomFactor; if (GetCVars()->e_OcclusionVolumes) { for (int i = 0; i < m_lstOcclAreas.Count(); i++) { CVisArea* pArea = m_lstOcclAreas[i]; if (passInfo.GetCamera().IsAABBVisible_E(pArea->m_boxArea)) { float fRadius = (pArea->m_boxArea.min - pArea->m_boxArea.max).GetLength(); Vec3 vPos = (pArea->m_boxArea.min + pArea->m_boxArea.max) * 0.5f; float fDist = passInfo.GetCamera().GetPosition().GetDistance(vPos); if (fDist < fRadius * pArea->m_fViewDistRatio * fDistRatio && pArea->m_lstShapePoints.Count() >= 2) { int nRecursiveLevel = passInfo.GetRecursiveLevel(); if (!pArea->m_arrOcclCamera[nRecursiveLevel]) { pArea->m_arrOcclCamera[nRecursiveLevel] = new CCamera; } *pArea->m_arrOcclCamera[nRecursiveLevel] = passInfo.GetCamera(); SActiveVerts activeVerts; if (pArea->m_lstShapePoints.Count() == 4) { activeVerts.arrvActiveVerts[0] = pArea->m_lstShapePoints[0]; activeVerts.arrvActiveVerts[1] = pArea->m_lstShapePoints[1]; activeVerts.arrvActiveVerts[2] = pArea->m_lstShapePoints[2]; activeVerts.arrvActiveVerts[3] = pArea->m_lstShapePoints[3]; } else { activeVerts.arrvActiveVerts[0] = pArea->m_lstShapePoints[0]; activeVerts.arrvActiveVerts[1] = pArea->m_lstShapePoints[0] + Vec3(0, 0, pArea->m_fHeight); activeVerts.arrvActiveVerts[2] = pArea->m_lstShapePoints[1] + Vec3(0, 0, pArea->m_fHeight); activeVerts.arrvActiveVerts[3] = pArea->m_lstShapePoints[1]; } Plane plane; plane.SetPlane(activeVerts.arrvActiveVerts[0], activeVerts.arrvActiveVerts[2], activeVerts.arrvActiveVerts[1]); if (plane.DistFromPlane(passInfo.GetCamera().GetPosition()) < 0) { std::swap(activeVerts.arrvActiveVerts[0], activeVerts.arrvActiveVerts[3]); std::swap(activeVerts.arrvActiveVerts[1], activeVerts.arrvActiveVerts[2]); } else if (!pArea->m_bDoubleSide) { continue; } // GetRenderer()->SetMaterialColor(1,0,0,1); pArea->UpdatePortalCameraPlanes(*pArea->m_arrOcclCamera[passInfo.GetRecursiveLevel()], activeVerts.arrvActiveVerts, false, passInfo); // make far plane never clip anything #if defined(OCCLUSIONCULLER_W) m_allActiveVerts.push_back(activeVerts); #endif Plane newNearPlane; newNearPlane.SetPlane(activeVerts.arrvActiveVerts[0], activeVerts.arrvActiveVerts[2], activeVerts.arrvActiveVerts[1]); pArea->m_arrOcclCamera[passInfo.GetRecursiveLevel()]->SetFrustumPlane(FR_PLANE_NEAR, newNearPlane); Plane newFarPlane; newFarPlane.SetPlane(Vec3(0, 1, -1024), Vec3(1, 0, -1024), Vec3(0, 0, -1024)); pArea->m_arrOcclCamera[passInfo.GetRecursiveLevel()]->SetFrustumPlane(FR_PLANE_FAR, newFarPlane); //pArea->m_arrOcclCamera[m_nRenderStackLevel]->UpdateFrustum(); m_lstActiveOcclVolumes.Add(pArea); pArea->m_fDistance = fDist; } } } } if (m_lstActiveOcclVolumes.Count()) { // sort occluders by distance to the camera qsort(&m_lstActiveOcclVolumes[0], m_lstActiveOcclVolumes.Count(), sizeof(m_lstActiveOcclVolumes[0]), CVisAreaManager__CmpDistToPortal); // remove occluded occluders for (int i = m_lstActiveOcclVolumes.Count() - 1; i >= 0; i--) { CVisArea* pArea = m_lstActiveOcclVolumes[i]; AABB extrudedBox = pArea->m_boxStatics; extrudedBox.min -= Vec3(VEC_EPSILON, VEC_EPSILON, VEC_EPSILON); extrudedBox.max += Vec3(VEC_EPSILON, VEC_EPSILON, VEC_EPSILON); if (IsOccludedByOcclVolumes(extrudedBox, passInfo)) { m_lstActiveOcclVolumes.Delete(i); } } #ifdef OCCLUSIONCULLER_W // draw them into the CBuffer for (size_t i = 0, size = m_allActiveVerts.size(); i < size; i++) { pCBuffer->AddOccluderPlane(m_allActiveVerts[i].arrvActiveVerts); } #endif // put indoor occluders into separate list for (int i = m_lstActiveOcclVolumes.Count() - 1; i >= 0; i--) { CVisArea* pArea = m_lstActiveOcclVolumes[i]; if (pArea->m_bUseInIndoors) { m_lstIndoorActiveOcclVolumes.Add(pArea); } } if (GetCVars()->e_Portals == 4) { // show really active occluders for (int i = 0; i < m_lstActiveOcclVolumes.Count(); i++) { CVisArea* pArea = m_lstActiveOcclVolumes[i]; GetRenderer()->SetMaterialColor(0, 1, 0, 1); DrawBBox(pArea->m_boxStatics.min, pArea->m_boxStatics.max); } } } } void CVisAreaManager::GetStreamingStatus(int& nLoadedSectors, int& nTotalSectors) { nLoadedSectors = 0; nTotalSectors = m_lstPortals.Count() + m_lstVisAreas.Count(); } void CVisAreaManager::GetMemoryUsage(ICrySizer* pSizer) const { // areas for (int v = 0; v < m_lstVisAreas.Count(); v++) { m_lstVisAreas[v]->GetMemoryUsage(pSizer); } // portals for (int v = 0; v < m_lstPortals.Count(); v++) { m_lstPortals[v]->GetMemoryUsage(pSizer); } // occl areas for (int v = 0; v < m_lstOcclAreas.Count(); v++) { m_lstOcclAreas[v]->GetMemoryUsage(pSizer); } pSizer->AddObject(this, sizeof(*this)); } void CVisAreaManager::PrecacheLevel(bool bPrecacheAllVisAreas, Vec3* pPrecachePoints, int nPrecachePointsNum) { CryLog("Precaching the level ..."); // gEnv->pLog->UpdateLoadingScreen(0); float fPrecacheTimeStart = GetTimer()->GetAsyncCurTime(); GetRenderer()->EnableSwapBuffers((GetCVars()->e_PrecacheLevel >= 2) ? true : false); uint32 dwPrecacheLocations = 0; Vec3 arrCamDir[6] = { Vec3(1, 0, 0), Vec3(-1, 0, 0), Vec3(0, 1, 0), Vec3(0, -1, 0), Vec3(0, 0, 1), Vec3(0, 0, -1) }; //loop over all sectors and place a light in the middle of the sector for (int v = 0; v < m_lstVisAreas.Count() && bPrecacheAllVisAreas; v++) { GetRenderer()->EF_Query(EFQ_IncrementFrameID); ++dwPrecacheLocations; // find real geometry bbox /* bool bGeomFound = false; Vec3 vBoxMin(100000.f,100000.f,100000.f); Vec3 vBoxMax(-100000.f,-100000.f,-100000.f); for(int s=0; sm_lstEntities[s].Count(); e++) { AABB aabbBox = m_lstVisAreas[v]->m_lstEntities[s][e].aabbBox; vBoxMin.CheckMin(aabbBox.min); vBoxMax.CheckMax(aabbBox.max); bGeomFound = true; }*/ Vec3 vAreaCenter = m_lstVisAreas[v]->m_boxArea.GetCenter(); CryLog(" Precaching VisArea %s", m_lstVisAreas[v]->GetName()); //place camera in the middle of a sector and render sector from different directions for (int i = 0; i < 6 /*&& bGeomFound*/; i++) { GetRenderer()->BeginFrame(); // setup camera CCamera cam = gEnv->pSystem->GetViewCamera(); Matrix33 mat = Matrix33::CreateRotationVDir(arrCamDir[i], 0); cam.SetMatrix(mat); cam.SetPosition(vAreaCenter); cam.SetFrustum(GetRenderer()->GetWidth(), GetRenderer()->GetHeight(), gf_PI / 2, cam.GetNearPlane(), cam.GetFarPlane()); // Get3DEngine()->SetupCamera(cam); Get3DEngine()->RenderWorld(SHDF_ZPASS | SHDF_ALLOWHDR | SHDF_ALLOWPOSTPROCESS | SHDF_ALLOW_WATER | SHDF_ALLOW_AO, SRenderingPassInfo::CreateGeneralPassRenderingInfo(cam), "PrecacheVisAreas"); GetRenderer()->RenderDebug(); GetRenderer()->EndFrame(); if (GetCVars()->e_PrecacheLevel >= 2) { CrySleep(200); } } } CryLog("Precached %d visarea sectors", dwPrecacheLocations); //-------------------------------------------------------------------------------------- //---- PRE-FETCHING OF RENDER-DATA IN OUTDOORS ---- //-------------------------------------------------------------------------------------- // loop over all cam-position in the level and render this part of the level from 6 different directions for (int p = 0; pPrecachePoints && p < nPrecachePointsNum; p++) //loop over outdoor-camera position { CryLog(" Precaching PrecacheCamera point %d of %d", p, nPrecachePointsNum); for (int i = 0; i < 6; i++) //loop over 6 camera orientations { GetRenderer()->BeginFrame(); // setup camera CCamera cam = gEnv->pSystem->GetViewCamera(); Matrix33 mat = Matrix33::CreateRotationVDir(arrCamDir[i], 0); cam.SetMatrix(mat); cam.SetPosition(pPrecachePoints[p]); cam.SetFrustum(GetRenderer()->GetWidth(), GetRenderer()->GetHeight(), gf_PI / 2, cam.GetNearPlane(), cam.GetFarPlane()); Get3DEngine()->RenderWorld(SHDF_ZPASS | SHDF_ALLOWHDR | SHDF_ALLOWPOSTPROCESS | SHDF_ALLOW_WATER | SHDF_ALLOW_AO, SRenderingPassInfo::CreateGeneralPassRenderingInfo(cam), "PrecacheOutdoor"); GetRenderer()->RenderDebug(); GetRenderer()->EndFrame(); if (GetCVars()->e_PrecacheLevel >= 2) { CrySleep(1000); } } } CryLog("Precached %d PrecacheCameraXX points", nPrecachePointsNum); GetRenderer()->EnableSwapBuffers(true); float fPrecacheTime = GetTimer()->GetAsyncCurTime() - fPrecacheTimeStart; CryLog("Level Precache finished in %.2f seconds", fPrecacheTime); } void CVisAreaManager::GetObjectsAround(Vec3 vExploPos, float fExploRadius, PodArray* pEntList, bool bSkip_ERF_NO_DECALNODE_DECALS, bool bSkipDynamicObjects) { AABB aabbBox(vExploPos - Vec3(fExploRadius, fExploRadius, fExploRadius), vExploPos + Vec3(fExploRadius, fExploRadius, fExploRadius)); CVisArea* pVisArea = (CVisArea*)GetVisAreaFromPos(vExploPos); if (pVisArea && pVisArea->m_pObjectsTree) { pVisArea->m_pObjectsTree->MoveObjectsIntoList(pEntList, &aabbBox, false, true, bSkip_ERF_NO_DECALNODE_DECALS, bSkipDynamicObjects); } /* // find static objects around for(int i=0; pVisArea && im_lstEntities[STATIC_OBJECTS].Count(); i++) { IRenderNode * pRenderNode = pVisArea->m_lstEntities[STATIC_OBJECTS][i].pNode; if(bSkip_ERF_NO_DECALNODE_DECALS && pRenderNode->GetRndFlags()&ERF_NO_DECALNODE_DECALS) continue; if(pRenderNode->GetRenderNodeType() == eERType_Decal) continue; if(Overlap::Sphere_AABB(Sphere(vExploPos,fExploRadius), pRenderNode->GetBBox())) if(pEntList->Find(pRenderNode)<0) pEntList->Add(pRenderNode); } }*/ } void CVisAreaManager::IntersectWithBox(const AABB& aabbBox, PodArray* plstResult, bool bOnlyIfVisible) { for (int p = 0; p < m_lstPortals.Count(); p++) { if (m_lstPortals[p]->m_boxArea.min.x < aabbBox.max.x&& m_lstPortals[p]->m_boxArea.max.x > aabbBox.min.x && m_lstPortals[p]->m_boxArea.min.y < aabbBox.max.y&& m_lstPortals[p]->m_boxArea.max.y > aabbBox.min.y) { plstResult->Add(m_lstPortals[p]); } } for (int v = 0; v < m_lstVisAreas.Count(); v++) { if (m_lstVisAreas[v]->m_boxArea.min.x < aabbBox.max.x&& m_lstVisAreas[v]->m_boxArea.max.x > aabbBox.min.x && m_lstVisAreas[v]->m_boxArea.min.y < aabbBox.max.y&& m_lstVisAreas[v]->m_boxArea.max.y > aabbBox.min.y) { plstResult->Add(m_lstVisAreas[v]); } } } int CVisAreaManager::GetNumberOfVisArea() const { return m_lstPortals.size() + m_lstVisAreas.size(); } IVisArea* CVisAreaManager::GetVisAreaById(int nID) const { if (nID < 0) { return NULL; } if (nID < (int)m_lstPortals.size()) { return m_lstPortals[ nID ]; } nID -= m_lstPortals.size(); if (nID < (int)m_lstVisAreas.size()) { return m_lstVisAreas[ nID ]; } return NULL; } ////////////////////////////////////////////////////////////////////////// void CVisAreaManager::AddListener(IVisAreaCallback* pListener) { if (m_lstCallbacks.Find(pListener) < 0) { m_lstCallbacks.Add(pListener); } } ////////////////////////////////////////////////////////////////////////// void CVisAreaManager::RemoveListener(IVisAreaCallback* pListener) { m_lstCallbacks.Delete(pListener); } void CVisAreaManager::CloneRegion(const AABB& region, const Vec3& offset, float zRotation) { PodArray points; PodArray visAreas; IntersectWithBox(region, &visAreas, false); Vec3 localOrigin = region.GetCenter(); Matrix34 l2w(Matrix33::CreateRotationZ(zRotation)); l2w.SetTranslation(offset); int numAreas = visAreas.size(); for (int i = 0; i < numAreas; ++i) { CVisArea* pSrcArea = visAreas[i]; CVisArea* pCloneArea = CreateVisArea(0); SVisAreaInfo info; info.fHeight = pSrcArea->m_fHeight; info.vAmbientColor = pSrcArea->m_vAmbientColor; info.bAffectedByOutLights = pSrcArea->m_bAffectedByOutLights; info.bIgnoreSkyColor = pSrcArea->m_bIgnoreSky; info.bSkyOnly = pSrcArea->m_bSkyOnly; info.fViewDistRatio = pSrcArea->m_fViewDistRatio; info.bDoubleSide = pSrcArea->m_bDoubleSide; info.bUseDeepness = pSrcArea->m_bUseDeepness; info.bUseInIndoors = pSrcArea->m_bUseInIndoors; info.bOceanIsVisible = pSrcArea->m_bOceanVisible; info.bIgnoreGI = pSrcArea->m_bIgnoreGI; info.bIgnoreOutdoorAO = pSrcArea->m_bIgnoreOutdoorAO; points = pSrcArea->m_lstShapePoints; int numPoints = points.size(); for (int p = 0; p < numPoints; ++p) { Vec3& point = points[p]; point -= localOrigin; point = l2w * point; } const char* pName = pSrcArea->m_pVisAreaColdData->m_sName; UpdateVisArea(pCloneArea, &points[0], numPoints, pName, info); } } void CVisAreaManager::ClearRegion(const AABB& region) { PodArray visAreas; IntersectWithBox(region, &visAreas, false); bool updated = false; // What we're doing here is basically just what's done in DeleteVisArea, but this // should be a pooled vis area, so we don't want to actually delete it. Instead we // just unregister them and let the pool cleanup actually destruct them. int numAreas = visAreas.size(); for (int i = 0; i < numAreas; ++i) { CVisArea* pVisArea = visAreas[i]; // IntersectWithBox only checks x and y, but we want to also make sure it's in the z if (pVisArea->m_boxArea.min.z < region.max.z && pVisArea->m_boxArea.max.z > region.min.z) { bool deletedVis = m_lstVisAreas.Delete(pVisArea); bool deletedPortal = m_lstPortals.Delete(pVisArea); bool deletedOccluder = m_lstOcclAreas.Delete(pVisArea); CRY_ASSERT_MESSAGE(!deletedVis || (m_visAreas.Find(pVisArea) >= 0), "Should only clear pooled vis areas, going to leak"); CRY_ASSERT_MESSAGE(!deletedPortal || (m_portals.Find(pVisArea) >= 0), "Should only clear pooled portals, going to leak"); CRY_ASSERT_MESSAGE(!deletedOccluder || (m_occlAreas.Find(pVisArea) >= 0), "Should only clear pooled occluders, going to leak"); if (deletedVis || deletedPortal || deletedOccluder) { updated = true; } m_lstActiveOcclVolumes.Delete(pVisArea); m_lstIndoorActiveOcclVolumes.Delete(pVisArea); m_lstActiveEntransePortals.Delete(pVisArea); } } if (updated) { m_pCurArea = NULL; m_pCurPortal = NULL; UpdateConnections(); delete m_pAABBTree; m_pAABBTree = NULL; } } void CVisAreaManager::ClearTerrainNodes() { for (const CVisArea* area : m_lstVisAreas) { if (area->m_pObjectsTree) { area->m_pObjectsTree->UpdateTerrainNodes(nullptr); } } } void CVisAreaManager::MarkAllSectorsAsUncompiled(const IRenderNode* pRenderNode) { for (int p = 0; p < m_lstPortals.Count(); p++) { if (m_lstPortals[p]->m_pObjectsTree) { m_lstPortals[p]->m_pObjectsTree->MarkAsUncompiled(pRenderNode); } } for (int v = 0; v < m_lstVisAreas.Count(); v++) { if (m_lstVisAreas[v]->m_pObjectsTree) { m_lstVisAreas[v]->m_pObjectsTree->MarkAsUncompiled(pRenderNode); } } } void CVisAreaManager::ActivateObjectsLayer(uint16 nLayerId, bool bActivate, bool bPhys, IGeneralMemoryHeap* pHeap) { { uint32 dwSize = m_lstVisAreas.Count(); for (uint32 dwI = 0; dwI < dwSize; ++dwI) { if (m_lstVisAreas[dwI]->m_pObjectsTree) { m_lstVisAreas[dwI]->m_pObjectsTree->ActivateObjectsLayer(nLayerId, bActivate, bPhys, pHeap); } } } { uint32 dwSize = m_lstPortals.Count(); for (uint32 dwI = 0; dwI < dwSize; ++dwI) { if (m_lstPortals[dwI]->m_pObjectsTree) { m_lstPortals[dwI]->m_pObjectsTree->ActivateObjectsLayer(nLayerId, bActivate, bPhys, pHeap); } } } } void CVisAreaManager::GetObjects(PodArray& lstObjects, const AABB* pBBox) { { uint32 dwSize = m_lstVisAreas.Count(); for (uint32 dwI = 0; dwI < dwSize; ++dwI) { if (m_lstVisAreas[dwI]->m_pObjectsTree) { m_lstVisAreas[dwI]->m_pObjectsTree->GetObjects(lstObjects, pBBox); } } } { uint32 dwSize = m_lstPortals.Count(); for (uint32 dwI = 0; dwI < dwSize; ++dwI) { if (m_lstPortals[dwI]->m_pObjectsTree) { m_lstPortals[dwI]->m_pObjectsTree->GetObjects(lstObjects, pBBox); } } } } void CVisAreaManager::GetObjectsByFlags(uint dwFlags, PodArray& lstObjects) { { uint32 dwSize = m_lstVisAreas.Count(); for (uint32 dwI = 0; dwI < dwSize; ++dwI) { if (m_lstVisAreas[dwI]->m_pObjectsTree) { m_lstVisAreas[dwI]->m_pObjectsTree->GetObjectsByFlags(dwFlags, lstObjects); } } } { uint32 dwSize = m_lstPortals.Count(); for (uint32 dwI = 0; dwI < dwSize; ++dwI) { if (m_lstPortals[dwI]->m_pObjectsTree) { m_lstPortals[dwI]->m_pObjectsTree->GetObjectsByFlags(dwFlags, lstObjects); } } } } void CVisAreaManager::GenerateStatObjAndMatTables(std::vector* pStatObjTable, std::vector<_smart_ptr>* pMatTable, std::vector* pStatInstGroupTable, SHotUpdateInfo* pExportInfo) { { uint32 dwSize = m_lstVisAreas.Count(); for (uint32 dwI = 0; dwI < dwSize; ++dwI) { if (m_lstVisAreas[dwI]->m_pObjectsTree) { m_lstVisAreas[dwI]->m_pObjectsTree->GenerateStatObjAndMatTables(pStatObjTable, pMatTable, pStatInstGroupTable, pExportInfo); } } } { uint32 dwSize = m_lstPortals.Count(); for (uint32 dwI = 0; dwI < dwSize; ++dwI) { if (m_lstPortals[dwI]->m_pObjectsTree) { m_lstPortals[dwI]->m_pObjectsTree->GenerateStatObjAndMatTables(pStatObjTable, pMatTable, pStatInstGroupTable, pExportInfo); } } } } bool CVisAreaManager::IsAABBVisibleFromPoint(AABB& box, Vec3 pos) { CVisArea* pAreaBox = (CVisArea*)GetVisAreaFromPos(box.GetCenter()); CVisArea* pAreaPos = (CVisArea*)GetVisAreaFromPos(pos); if (!pAreaBox && !pAreaPos) { return true; // no indoors involved } PodArray arrPortals; int nRecursion = 0; Shadowvolume sv; NAABB_SV::AABB_ReceiverShadowVolume(pos, box, sv); bool bRes = false; bRes = FindShortestPathToVisArea(pAreaPos, pAreaBox, arrPortals, nRecursion, sv); GetRenderer()->DrawLabel(box.GetCenter(), 2, "-%s-", bRes ? "Y" : "N"); GetRenderer()->DrawLabel(pos, 2, "-X-"); DrawLine(pos, box.GetCenter()); DrawBBox(box, bRes ? Col_White : Col_NavyBlue); return bRes; } bool CVisAreaManager::FindShortestPathToVisArea(CVisArea* pThisArea, CVisArea* pTargetArea, PodArray& arrVisitedAreas, int& nRecursion, const Shadowvolume& sv) { // skip double processing if (arrVisitedAreas.Find(pThisArea) >= 0) { return false; } // check if point to box frustum intersects pThisArea visarea if (pThisArea && !NAABB_SV::Is_AABB_In_ShadowVolume(sv, *pThisArea->GetAABBox())) { return false; } // check if box visarea reached if (pThisArea == pTargetArea) { return true; } // register as already processed arrVisitedAreas.Add(pThisArea); // recurse to connections if (pThisArea) { for (int p = 0; p < pThisArea->m_lstConnections.Count(); p++) { if (FindShortestPathToVisArea(pThisArea->m_lstConnections[p], pTargetArea, arrVisitedAreas, nRecursion, sv)) { return true; } } if (pThisArea->IsPortal() && pThisArea->m_lstConnections.Count() == 1 && !pThisArea->m_bSkyOnly) { if (FindShortestPathToVisArea(NULL, pTargetArea, arrVisitedAreas, nRecursion, sv)) { return true; } } } else { for (int p = 0; p < m_lstPortals.Count(); p++) { if (m_lstPortals[p]->IsPortal() && m_lstPortals[p]->m_lstConnections.Count() == 1 && !m_lstPortals[p]->m_bSkyOnly) { if (FindShortestPathToVisArea(m_lstPortals[p], pTargetArea, arrVisitedAreas, nRecursion, sv)) { return true; } } } } return false; } CVisArea* CVisAreaManager::CreateTypeVisArea() { CVisArea* pNewVisArea = new CVisArea(); SGenericColdData* pColdData = &m_visAreaColdData.AddNew(); m_visAreas.Add(pNewVisArea); pColdData->ResetGenericData(); pNewVisArea->SetColdDataPtr(pColdData); return pNewVisArea; } CVisArea* CVisAreaManager::CreateTypePortal() { CVisArea* pNewPortal = new CVisArea(); SPortalColdData* pColdData = &m_portalColdData.AddNew(); m_portals.Add(pNewPortal); pColdData->ResetPortalData(); pNewPortal->SetColdDataPtr(pColdData); return pNewPortal; } CVisArea* CVisAreaManager::CreateTypeOcclArea() { CVisArea* pNewOcclArea = new CVisArea(); SGenericColdData* pColdData = &m_occlAreaColdData.AddNew(); m_occlAreas.Add(pNewOcclArea); pColdData->ResetGenericData(); pNewOcclArea->SetColdDataPtr(pColdData); return pNewOcclArea; } void CVisAreaManager::InitAABBTree() { IF (!m_pAABBTree, 0) { UpdateAABBTree(); } } ////////////////////////////////////////////////////////////////////// // Segmented World void CVisAreaManager::ReleaseInactiveSegments() { for (int i = 0; i < m_arrDeletedVisArea.Count(); i++) { int nSlotID = m_arrDeletedVisArea[i]; SAFE_DELETE(m_visAreas[nSlotID]->m_pObjectsTree); } m_arrDeletedVisArea.Clear(); for (int i = 0; i < m_arrDeletedPortal.Count(); i++) { int nSlotID = m_arrDeletedPortal[i]; SAFE_DELETE(m_portals[nSlotID]->m_pObjectsTree); } m_arrDeletedPortal.Clear(); for (int i = 0; i < m_arrDeletedOcclArea.Count(); i++) { int nSlotID = m_arrDeletedOcclArea[i]; SAFE_DELETE(m_occlAreas[nSlotID]->m_pObjectsTree); } m_arrDeletedOcclArea.Clear(); } bool CVisAreaManager::CreateSegment(int nSID) { if (nSID >= m_visAreaSegmentData.Count()) { m_visAreaSegmentData.PreAllocate(nSID + 1, nSID + 1); m_portalSegmentData.PreAllocate(nSID + 1, nSID + 1); if (GetCVars()->e_OcclusionVolumes) { m_occlAreaSegmentData.PreAllocate(nSID + 1, nSID + 1); } } return true; } bool CVisAreaManager::DeleteSegment(int nSID, bool bDeleteNow) { if (nSID < 0 || (size_t)nSID >= m_visAreaSegmentData.size()) { return false; } DeleteVisAreaSegment(nSID, m_visAreaSegmentData, m_lstVisAreas, m_visAreas, m_arrDeletedVisArea); DeleteVisAreaSegment(nSID, m_portalSegmentData, m_lstPortals, m_portals, m_arrDeletedPortal); if (GetCVars()->e_OcclusionVolumes) { DeleteVisAreaSegment(nSID, m_occlAreaSegmentData, m_lstOcclAreas, m_occlAreas, m_arrDeletedOcclArea); } if (bDeleteNow) { ReleaseInactiveSegments(); } return true; } void CVisAreaManager::DeleteVisAreaSegment(int nSID, PodArray& visAreaSegmentData, PodArray& lstVisAreas, PodArray& visAreas, PodArray& deletedVisAreas) { std::vector& visAreasInSegment = visAreaSegmentData[nSID].m_visAreaIndices; for (size_t i = 0; i < visAreasInSegment.size(); i++) { int index = visAreasInSegment[i]; assert(index >= 0 && index < visAreas.Count()); CSWVisArea* pVisArea = (CSWVisArea*)visAreas[index]; pVisArea->Release(); // delete the visarea if it's ref count reaches zero if (!pVisArea->NumRefs()) { deletedVisAreas.push_back(index); } } visAreasInSegment.clear(); for (int i = 0; i < lstVisAreas.Count(); i++) { CSWVisArea* pVisArea = (CSWVisArea*)lstVisAreas[i]; if (!pVisArea->NumRefs()) { lstVisAreas.Delete(i); } } } CVisArea* CVisAreaManager::FindVisAreaByGuid(VisAreaGUID guid, PodArray& lstVisAreas) { if (!guid) { for (int i = 0; i < lstVisAreas.Count(); i++) { if (lstVisAreas[i] && guid == lstVisAreas[i]->m_nVisGUID) { return lstVisAreas[i]; } } } return NULL; } void CVisAreaManager::OffsetPosition(const Vec3& delta) { for (int i = 0; i < m_lstVisAreas.Count(); i++) { m_lstVisAreas[i]->OffsetPosition(delta); } for (int i = 0; i < m_lstPortals.Count(); i++) { m_lstPortals[i]->OffsetPosition(delta); } for (int i = 0; i < m_lstOcclAreas.Count(); i++) { m_lstOcclAreas[i]->OffsetPosition(delta); } if (m_pAABBTree) { m_pAABBTree->OffsetPosition(delta); } }