You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Code/CryEngine/Cry3DEngine/VisArea_Jobs.cpp

810 lines
25 KiB
C++

/*
* 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.
// Description : Visibility areas
#include "Cry3DEngine_precompiled.h"
#include "StatObj.h"
#include "ObjMan.h"
#include "VisAreas.h"
#include "3dEngine.h"
#include "TimeOfDay.h"
#include "AABBSV.h"
#include "Cry_LegacyPhysUtils.h"
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
bool InsidePolygon(const Vec3* __restrict polygon, int N, const Vec3& p)
{
int counter = 0;
int i;
float xinters;
const Vec3* p1 = &polygon[0];
for (i = 1; i <= N; i++)
{
const Vec3* p2 = &polygon[i % N];
if (p.y > min(p1->y, p2->y))
{
if (p.y <= max(p1->y, p2->y))
{
if (p.x <= max(p1->x, p2->x))
{
if (p1->y != p2->y)
{
xinters = (p.y - p1->y) * (p2->x - p1->x) / (p2->y - p1->y) + p1->x;
if (p1->x == p2->x || p.x <= xinters)
{
counter++;
}
}
}
}
}
p1 = p2;
}
if (counter % 2 == 0)
{
return(false);
}
else
{
return(true);
}
}
///////////////////////////////////////////////////////////////////////////////
bool InsideSpherePolygon(Vec3* polygon, int N, Sphere& S)
{
int i;
Vec3 p1, p2;
p1 = polygon[0];
for (i = 1; i <= N; i++)
{
p2 = polygon[i % N];
Vec3 vA(p1 - S.center);
Vec3 vD(p2 - p1);
vA.z = vD.z = 0;
vD.NormalizeSafe();
float fB = vD.Dot(vA);
float fC = vA.Dot(vA) - S.radius * S.radius;
if (fB * fB >= fC) //at least 1 root
{
return(true);
}
p1 = p2;
}
return(false);
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
void CVisAreaManager::GetNearestCubeProbe(float& fMinDistance, int& nMaxPriority, CLightEntity*& pNearestLight, const AABB* pBBox)
{
{
uint32 dwSize = m_lstVisAreas.Count();
for (uint32 dwI = 0; dwI < dwSize; ++dwI)
{
if (m_lstVisAreas[dwI]->m_pObjectsTree)
{
if (!pBBox || Overlap::AABB_AABB(*m_lstVisAreas[dwI]->GetAABBox(), *pBBox))
{
m_lstVisAreas[dwI]->m_pObjectsTree->GetNearestCubeProbe(fMinDistance, nMaxPriority, pNearestLight, pBBox);
}
}
}
}
{
uint32 dwSize = m_lstPortals.Count();
for (uint32 dwI = 0; dwI < dwSize; ++dwI)
{
if (m_lstPortals[dwI]->m_pObjectsTree)
{
if (!pBBox || Overlap::AABB_AABB(*m_lstPortals[dwI]->GetAABBox(), *pBBox))
{
m_lstPortals[dwI]->m_pObjectsTree->GetNearestCubeProbe(fMinDistance, nMaxPriority, pNearestLight, pBBox);
}
}
}
}
}
///////////////////////////////////////////////////////////////////////////////
void CVisAreaManager::GetObjectsByType(PodArray<IRenderNode*>& lstObjects, EERType objType, const AABB* pBBox, ObjectTreeQueryFilterCallback filterCallback)
{
{
uint32 dwSize = m_lstVisAreas.Count();
for (uint32 dwI = 0; dwI < dwSize; ++dwI)
{
if (m_lstVisAreas[dwI]->m_pObjectsTree)
{
if (!pBBox || Overlap::AABB_AABB(*m_lstVisAreas[dwI]->GetAABBox(), *pBBox))
{
m_lstVisAreas[dwI]->m_pObjectsTree->GetObjectsByType(lstObjects, objType, pBBox, filterCallback);
}
}
}
}
{
uint32 dwSize = m_lstPortals.Count();
for (uint32 dwI = 0; dwI < dwSize; ++dwI)
{
if (m_lstPortals[dwI]->m_pObjectsTree)
{
if (!pBBox || Overlap::AABB_AABB(*m_lstPortals[dwI]->GetAABBox(), *pBBox))
{
m_lstPortals[dwI]->m_pObjectsTree->GetObjectsByType(lstObjects, objType, pBBox, filterCallback);
}
}
}
}
}
///////////////////////////////////////////////////////////////////////////////
bool CVisAreaManager::IsOccludedByOcclVolumes(const AABB& objBox, const SRenderingPassInfo& passInfo, bool bCheckOnlyIndoorVolumes)
{
PodArray<CVisArea*>& rList = bCheckOnlyIndoorVolumes ? m_lstIndoorActiveOcclVolumes : m_lstActiveOcclVolumes;
for (int i = 0; i < rList.Count(); i++)
{
CCamera* pCamera = rList[i]->m_arrOcclCamera[passInfo.GetRecursiveLevel()];
bool bAllIn = 0;
if (pCamera)
{
// reduce code size by skipping many cache lookups in the camera functions as well as the camera constructor
char bufCamera[sizeof(CCamera)] _ALIGN(128);
memcpy(bufCamera, pCamera, sizeof(CCamera));
if (alias_cast<CCamera*>(bufCamera)->IsAABBVisible_EH(objBox, &bAllIn))
{
if (bAllIn)
{
return true;
}
}
}
}
return false;
}
///////////////////////////////////////////////////////////////////////////////
IVisArea* CVisAreaManager::GetVisAreaFromPos(const Vec3& vPos)
{
FUNCTION_PROFILER_3DENGINE;
if (!m_pAABBTree)
{
UpdateAABBTree();
}
return m_pAABBTree->FindVisarea(vPos);
}
///////////////////////////////////////////////////////////////////////////////
bool CVisArea::IsBoxOverlapVisArea(const AABB& objBox)
{
if (!Overlap::AABB_AABB(objBox, m_boxArea))
{
return false;
}
PodArray<Vec3>& polygonA = s_tmpPolygonA;
polygonA.Clear();
polygonA.Add(Vec3(objBox.min.x, objBox.min.y, objBox.min.z));
polygonA.Add(Vec3(objBox.min.x, objBox.max.y, objBox.min.z));
polygonA.Add(Vec3(objBox.max.x, objBox.max.y, objBox.min.z));
polygonA.Add(Vec3(objBox.max.x, objBox.min.y, objBox.min.z));
return Overlap::Polygon_Polygon2D< PodArray<Vec3> >(polygonA, m_lstShapePoints);
}
#define PORTAL_GEOM_BBOX_EXTENT 1.5f
///////////////////////////////////////////////////////////////////////////////
void CVisArea::UpdateGeometryBBox()
{
m_boxStatics.max = m_boxArea.max;
m_boxStatics.min = m_boxArea.min;
if (IsPortal())
{ // fix for big objects passing portal
m_boxStatics.max += Vec3(PORTAL_GEOM_BBOX_EXTENT, PORTAL_GEOM_BBOX_EXTENT, PORTAL_GEOM_BBOX_EXTENT);
m_boxStatics.min -= Vec3(PORTAL_GEOM_BBOX_EXTENT, PORTAL_GEOM_BBOX_EXTENT, PORTAL_GEOM_BBOX_EXTENT);
}
if (m_pObjectsTree)
{
PodArray<IRenderNode*> lstObjects;
m_pObjectsTree->GetObjectsByType(lstObjects, eERType_StaticMeshRenderComponent, NULL);
for (int i = 0; i < lstObjects.Count(); i++)
{
AABB aabb;
lstObjects[i]->FillBBox(aabb);
m_boxStatics.Add(aabb);
}
}
}
///////////////////////////////////////////////////////////////////////////////
void CVisArea::UpdateClipVolume()
{
if (m_lstShapePoints.Count() < 3)
{
return;
}
const int nPoints = m_lstShapePoints.size();
const int nVertexCount = nPoints * 2;
const int nIndexCount = (2 * nPoints + 2 * (nPoints - 2)) * 3; // 2*nPoints for sides, nPoints-2 for top and nPoints-2 for bottom
m_pClipVolumeMesh = NULL;
if (nPoints < 3)
{
return;
}
std::vector<SVF_P3F_C4B_T2F> vertices;
std::vector<vtx_idx> indices;
vertices.resize(nVertexCount);
indices.resize(nIndexCount);
std::vector<Vec2> triangulationPoints;
triangulationPoints.resize(nPoints + 1);
MARK_UNUSED triangulationPoints[nPoints].x;
for (int i = 0; i < nPoints; ++i)
{
int nPointIdx = IsShapeClockwise() ? nPoints - 1 - i : i;
vertices[i].xyz = m_lstShapePoints[nPointIdx];
vertices[i].color.dcolor = 0xFFFFFFFF;
vertices[i].st = Vec2(ZERO);
vertices[i + nPoints].xyz = m_lstShapePoints[nPointIdx] + Vec3(0, 0, m_fHeight);
vertices[i + nPoints].color.dcolor = 0xFFFFFFFF;
vertices[i + nPoints].st = Vec2(ZERO);
triangulationPoints[i] = Vec2(m_lstShapePoints[nPointIdx]);
}
// triangulate shape first
std::vector<int> triangleIndices;
triangleIndices.resize((nPoints - 2) * 3);
MARK_UNUSED triangleIndices[triangleIndices.size() - 1];
int nTris = LegacyCryPhysicsUtils::TriangulatePoly(&triangulationPoints[0], triangulationPoints.size(), &triangleIndices[0], triangleIndices.size());
if (nTris == nPoints - 2) // triangulation success?
{
// top and bottom faces
size_t nCurIndex = 0;
for (; nCurIndex < triangleIndices.size(); nCurIndex += 3)
{
indices[nCurIndex + 2] = triangleIndices[nCurIndex + 0];
indices[nCurIndex + 1] = triangleIndices[nCurIndex + 1];
indices[nCurIndex + 0] = triangleIndices[nCurIndex + 2];
indices[triangleIndices.size() + nCurIndex + 0] = triangleIndices[nCurIndex + 0] + nPoints;
indices[triangleIndices.size() + nCurIndex + 1] = triangleIndices[nCurIndex + 1] + nPoints;
indices[triangleIndices.size() + nCurIndex + 2] = triangleIndices[nCurIndex + 2] + nPoints;
}
nCurIndex = 2 * triangleIndices.size();
// side faces
for (int i = 0; i < nPoints; i++)
{
vtx_idx bl = i;
vtx_idx br = (i + 1) % nPoints;
indices[nCurIndex++] = bl;
indices[nCurIndex++] = br + nPoints;
indices[nCurIndex++] = bl + nPoints;
indices[nCurIndex++] = bl;
indices[nCurIndex++] = br;
indices[nCurIndex++] = br + nPoints;
}
m_pClipVolumeMesh = gEnv->pRenderer->CreateRenderMeshInitialized(&vertices[0], vertices.size(),
eVF_P3F_C4B_T2F, &indices[0], indices.size(), prtTriangleList,
"ClipVolume", GetName(), eRMT_Dynamic);
}
}
void CVisArea::GetClipVolumeMesh(_smart_ptr<IRenderMesh>& renderMesh, Matrix34& worldTM) const
{
renderMesh = m_pClipVolumeMesh;
worldTM = Matrix34(IDENTITY);
}
uint CVisArea::GetClipVolumeFlags() const
{
int nFlags = IClipVolume::eClipVolumeIsVisArea;
nFlags |= IsConnectedToOutdoor() ? IClipVolume::eClipVolumeConnectedToOutdoor : 0;
nFlags |= IsAffectedByOutLights() ? IClipVolume::eClipVolumeAffectedBySun : 0;
nFlags |= IsIgnoringGI() ? IClipVolume::eClipVolumeIgnoreGI : 0;
nFlags |= IsIgnoringOutdoorAO() ? IClipVolume::eClipVolumeIgnoreOutdoorAO : 0;
return nFlags;
}
///////////////////////////////////////////////////////////////////////////////
void CVisArea::UpdatePortalBlendInfo()
{
if (m_bThisIsPortal && m_lstConnections.Count() > 0 && GetCVars()->e_PortalsBlend > 0 && m_fPortalBlending > 0)
{
SClipVolumeBlendInfo blendInfo;
Vec3 vPlanePoints[2][3];
uint nPointCount[2] = {0, 0};
for (int p = 0; p < m_lstShapePoints.Count(); ++p)
{
Vec3 vTestPoint = m_lstShapePoints[p] + Vec3(0, 0, m_fHeight * 0.5f);
int nVisAreaIndex = (m_lstConnections[0] && m_lstConnections[0]->IsPointInsideVisArea(vTestPoint)) ? 0 : 1;
if (nPointCount[nVisAreaIndex] < 2)
{
vPlanePoints[nVisAreaIndex][nPointCount[nVisAreaIndex]] = m_lstShapePoints[p];
nPointCount[nVisAreaIndex]++;
}
}
for (int i = 0; i < 2; ++i)
{
if (nPointCount[i] == 2)
{
if (IsShapeClockwise())
{
std::swap(vPlanePoints[i][0], vPlanePoints[i][1]);
}
blendInfo.blendPlanes[i] = Plane::CreatePlane(vPlanePoints[i][0], vPlanePoints[i][0] + Vec3(0, 0, m_fHeight), vPlanePoints[i][1]);
blendInfo.blendVolumes[i] = i < m_lstConnections.Count() ? m_lstConnections[i] : NULL;
// make sure plane normal points inside portal
if (nPointCount[(i + 1) % 2] > 0)
{
Vec3 c(ZERO);
for (uint j = 0; j < nPointCount[(i + 1) % 2]; ++j)
{
c += vPlanePoints[(i + 1) % 2][j];
}
c /= (float)nPointCount[(i + 1) % 2];
if (blendInfo.blendPlanes[i].DistFromPlane(c) < 0)
{
blendInfo.blendPlanes[i].n = -blendInfo.blendPlanes[i].n;
blendInfo.blendPlanes[i].d = -blendInfo.blendPlanes[i].d;
}
}
}
else
{
blendInfo.blendPlanes[i] = Plane(Vec3(ZERO), 0);
blendInfo.blendVolumes[i] = NULL;
}
}
// weight planes by user specified importance. works because we renormalize in the shader
float planeWeight = clamp_tpl(m_fPortalBlending, 1e-5f, 1.f - 1e-5f);
blendInfo.blendPlanes[0].n *= planeWeight;
blendInfo.blendPlanes[0].d *= planeWeight;
blendInfo.blendPlanes[1].n *= 1 - planeWeight;
blendInfo.blendPlanes[1].d *= 1 - planeWeight;
GetRenderer()->EF_SetDeferredClipVolumeBlendData(this, blendInfo);
}
}
///////////////////////////////////////////////////////////////////////////////
bool CVisAreaManager::SetEntityArea(IRenderNode* pEnt, const AABB& objBox, const float fObjRadiusSqr)
{
assert(pEnt);
Vec3 vEntCenter = Get3DEngine()->GetEntityRegisterPoint(pEnt);
// find portal containing object center
CVisArea* pVisArea = NULL;
const int kNumPortals = m_lstPortals.Count();
for (int v = 0; v < kNumPortals; v++)
{
CVisArea* pCurrentPortal = m_lstPortals[v];
PrefetchLine(pCurrentPortal, 256);
PrefetchLine(pCurrentPortal, 384);
if (pCurrentPortal->IsPointInsideVisArea(vEntCenter))
{
pVisArea = pCurrentPortal;
if (!pVisArea->m_pObjectsTree)
{
pVisArea->m_pObjectsTree = COctreeNode::Create(DEFAULT_SID, pVisArea->m_boxArea, pVisArea);
}
pVisArea->m_pObjectsTree->InsertObject(pEnt, objBox, fObjRadiusSqr, vEntCenter);
break;
}
}
if (!pVisArea && pEnt->m_dwRndFlags & ERF_REGISTER_BY_BBOX)
{
AABB aabb;
pEnt->FillBBox(aabb);
for (int v = 0; v < kNumPortals; v++)
{
CVisArea* pCurrentPortal = m_lstPortals[v];
PrefetchLine(pCurrentPortal, 256);
PrefetchLine(pCurrentPortal, 384);
if (pCurrentPortal->IsBoxOverlapVisArea(aabb))
{
pVisArea = pCurrentPortal;
if (!pVisArea->m_pObjectsTree)
{
pVisArea->m_pObjectsTree = COctreeNode::Create(DEFAULT_SID, pVisArea->m_boxArea, pVisArea);
}
pVisArea->m_pObjectsTree->InsertObject(pEnt, objBox, fObjRadiusSqr, vEntCenter);
break;
}
}
}
if (!pVisArea) // if portal not found - find volume
{
const int kNumVisAreas = m_lstVisAreas.Count();
for (int v = 0; v < kNumVisAreas; v++)
{
CVisArea* pCurrentVisArea = m_lstVisAreas[v];
PrefetchLine(pCurrentVisArea, 256);
PrefetchLine(pCurrentVisArea, 384);
if (pCurrentVisArea->IsPointInsideVisArea(vEntCenter))
{
pVisArea = pCurrentVisArea;
if (!pVisArea->m_pObjectsTree)
{
pVisArea->m_pObjectsTree = COctreeNode::Create(DEFAULT_SID, pVisArea->m_boxArea, pVisArea);
}
pVisArea->m_pObjectsTree->InsertObject(pEnt, objBox, fObjRadiusSqr, vEntCenter);
break;
}
}
}
if (pVisArea &&
(pEnt->GetRenderNodeType() == eERType_StaticMeshRenderComponent)
) // update bbox of exit portal
{
if (pVisArea->IsPortal())
{
pVisArea->UpdateGeometryBBox();
}
}
return pVisArea != 0;
}
///////////////////////////////////////////////////////////////////////////////
CVisArea* SAABBTreeNode::FindVisarea(const Vec3& vPos)
{
if (nodeBox.IsContainPoint(vPos))
{
if (nodeAreas.Count())
{ // leaf
for (int i = 0; i < nodeAreas.Count(); i++)
{
if (nodeAreas[i]->m_bActive && nodeAreas[i]->IsPointInsideVisArea(vPos))
{
return nodeAreas[i];
}
}
}
else
{ // node
for (int i = 0; i < 2; i++)
{
if (arrChilds[i])
{
if (CVisArea* pArea = arrChilds[i]->FindVisarea(vPos))
{
return pArea;
}
}
}
}
}
return NULL;
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Current scheme: Will never move sphere center, just clip radius, even to 0.
bool CVisArea::ClipToVisArea(bool bInside, Sphere& sphere, Vec3 const& vNormal)
{
FUNCTION_PROFILER_3DENGINE;
/*
Clip PointZ PointXY
In In In inside, clip Z and XY
In In Out outside, return 0
In Out In outside, return 0
In Out Out outside, return 0
Out In In inside, return 0
Out In Out outside, clip XY
Out Out In outside, clip Z
Out Out Out outside, clip XY
*/
bool bClipXY = false, bClipZ = false;
if (bInside)
{
// Clip to 0 if center outside.
if (!IsPointInsideVisArea(sphere.center))
{
sphere.radius = 0.f;
return true;
}
bClipXY = bClipZ = true;
}
else
{
if (Overlap::Point_AABB(sphere.center, m_boxArea))
{
if (InsidePolygon(m_lstShapePoints.begin(), m_lstShapePoints.size(), sphere.center))
{
sphere.radius = 0.f;
return true;
}
else
{
bClipXY = true;
}
}
else if (InsidePolygon(m_lstShapePoints.begin(), m_lstShapePoints.size(), sphere.center))
{
bClipZ = true;
}
else
{
bClipXY = true;
}
}
float fOrigRadius = sphere.radius;
if (bClipZ)
{
// Check against vertical planes.
float fDist = min(abs(m_boxArea.max.z - sphere.center.z), abs(sphere.center.z - m_boxArea.min.z));
float fRadiusScale = sqrt_tpl(max(1.f - sqr(vNormal.z), 0.f));
if (fDist < sphere.radius * fRadiusScale)
{
sphere.radius = fDist / fRadiusScale;
if (sphere.radius <= 0.f)
{
return true;
}
}
}
if (bClipXY)
{
Vec3 vP1 = m_lstShapePoints[0];
vP1.z = clamp_tpl(sphere.center.z, m_boxArea.min.z, m_boxArea.max.z);
for (int n = m_lstShapePoints.Count() - 1; n >= 0; n--)
{
Vec3 vP0 = m_lstShapePoints[n];
vP0.z = vP1.z;
// Compute nearest vector from center to plane.
Vec3 vP = vP0 - sphere.center;
Vec3 vD = vP1 - vP0;
float fN = -(vP * vD);
if (fN > 0.f)
{
float fD = vD.GetLengthSquared();
if (fN >= fD)
{
vP += vD;
}
else
{
vP += vD * (fN / fD);
}
}
// Check distance only in planar direction.
float fDist = vP.GetLength() * 0.99f;
float fRadiusScale = fDist > 0.f ? sqrt_tpl(max(1.f - sqr(vNormal * vP) / sqr(fDist), 0.f)) : 1.f;
if (fDist < sphere.radius * fRadiusScale)
{
sphere.radius = fDist / fRadiusScale;
if (sphere.radius <= 0.f)
{
return true;
}
}
vP1 = vP0;
}
}
return sphere.radius < fOrigRadius;
}
///////////////////////////////////////////////////////////////////////////////
bool CVisArea::IsPointInsideVisArea(const Vec3& vPos) const
{
const int kNumPoints = m_lstShapePoints.Count();
if (kNumPoints)
{
const Vec3* pLstShapePoints = &m_lstShapePoints[0];
PrefetchLine(pLstShapePoints, 0);
if (Overlap::Point_AABB(vPos, m_boxArea))
{
if (InsidePolygon(pLstShapePoints, kNumPoints, vPos))
{
return true;
}
}
}
return false;
}
///////////////////////////////////////////////////////////////////////////////
bool CVisArea::IsSphereInsideVisArea(const Vec3& vPos, const f32 fRadius)
{
Sphere S(vPos, fRadius);
if (Overlap::Sphere_AABB(S, m_boxArea))
{
if (InsidePolygon(&m_lstShapePoints[0], m_lstShapePoints.Count(), vPos) || InsideSpherePolygon(&m_lstShapePoints[0], m_lstShapePoints.Count(), S))
{
return true;
}
}
return false;
}
///////////////////////////////////////////////////////////////////////////////
const AABB* CVisArea::GetAABBox() const
{
return &m_boxArea;
}
///////////////////////////////////////////////////////////////////////////////
bool CVisArea::IsPortal() const
{
return m_bThisIsPortal;
}
float CVisArea::CalcSignedArea()
{
float fArea = 0;
for (int i = 0; i < m_lstShapePoints.Count(); i++)
{
const Vec3& v0 = m_lstShapePoints[i];
const Vec3& v1 = m_lstShapePoints[(i + 1) % m_lstShapePoints.Count()];
fArea += v0.x * v1.y - v1.x * v0.y;
}
return fArea / 2;
}
void CVisArea::GetShapePoints(const Vec3*& pPoints, size_t& nPoints)
{
if (m_lstShapePoints.empty())
{
pPoints = 0;
nPoints = 0;
}
else
{
pPoints = &m_lstShapePoints[0];
nPoints = m_lstShapePoints.size();
}
}
float CVisArea::GetHeight()
{
return m_fHeight;
}
///////////////////////////////////////////////////////////////////////////////
bool CVisArea::FindVisArea(IVisArea* pAnotherArea, int nMaxRecursion, bool bSkipDisabledPortals)
{
// collect visited areas in order to prevent visiting it again
StaticDynArray<CVisArea*, 1024> lVisitedParents;
return FindVisAreaReqursive(pAnotherArea, nMaxRecursion, bSkipDisabledPortals, lVisitedParents);
}
///////////////////////////////////////////////////////////////////////////////
bool CVisArea::FindVisAreaReqursive(IVisArea* pAnotherArea, int nMaxReqursion, bool bSkipDisabledPortals, StaticDynArray<CVisArea*, 1024>& arrVisitedParents)
{
arrVisitedParents.push_back(this);
if (pAnotherArea == this)
{
return true;
}
if (pAnotherArea == NULL && IsConnectedToOutdoor())
{
return true;
}
bool bFound = false;
if (nMaxReqursion > 1)
{
for (int p = 0; p < m_lstConnections.Count(); p++)
{
if (!bSkipDisabledPortals || m_lstConnections[p]->IsActive())
{
if (std::find(arrVisitedParents.begin(), arrVisitedParents.end(), m_lstConnections[p]) == arrVisitedParents.end())
{
if (m_lstConnections[p]->FindVisAreaReqursive(pAnotherArea, nMaxReqursion - 1, bSkipDisabledPortals, arrVisitedParents))
{
bFound = true;
break;
}
}
}
}
}
return bFound;
}
///////////////////////////////////////////////////////////////////////////////
bool CVisArea::IsConnectedToOutdoor() const
{
if (IsPortal()) // check if this portal has just one conection
{
return m_lstConnections.Count() == 1;
}
// find portals with just one conection
for (int p = 0; p < m_lstConnections.Count(); p++)
{
CVisArea* pPortal = m_lstConnections[p];
if (pPortal->m_lstConnections.Count() == 1)
{
return true;
}
}
return false;
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////