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/VisAreas.cpp

1149 lines
38 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"
PodArray<CVisArea*> CVisArea::m_lUnavailableAreas;
PodArray<Vec3> CVisArea::s_tmpLstPortVertsClipped;
PodArray<Vec3> CVisArea::s_tmpLstPortVertsSS;
PodArray<Vec3> CVisArea::s_tmpPolygonA;
PodArray<IRenderNode*> CVisArea::s_tmpLstLights;
CPolygonClipContext CVisArea::s_tmpClipContext;
PodArray<CCamera> CVisArea::s_tmpCameras;
int CVisArea::s_nGetDistanceThruVisAreasCallCounter = 0;
void CVisArea::Update(const Vec3* pPoints, int nCount, const char* szName, const SVisAreaInfo& info)
{
assert(m_pVisAreaColdData);
cry_strcpy(m_pVisAreaColdData->m_sName, szName);
_strlwr_s(m_pVisAreaColdData->m_sName, sizeof(m_pVisAreaColdData->m_sName));
m_bThisIsPortal = strstr(m_pVisAreaColdData->m_sName, "portal") != 0;
m_bIgnoreSky = (strstr(m_pVisAreaColdData->m_sName, "ignoresky") != 0) || info.bIgnoreSkyColor;
m_fHeight = info.fHeight;
m_vAmbientColor = info.vAmbientColor;
m_bAffectedByOutLights = info.bAffectedByOutLights;
m_bSkyOnly = info.bSkyOnly;
m_fViewDistRatio = info.fViewDistRatio;
m_bDoubleSide = info.bDoubleSide;
m_bUseDeepness = info.bUseDeepness;
m_bUseInIndoors = info.bUseInIndoors;
m_bOceanVisible = info.bOceanIsVisible;
m_bIgnoreGI = info.bIgnoreGI;
m_bIgnoreOutdoorAO = info.bIgnoreOutdoorAO;
m_fPortalBlending = info.fPortalBlending;
m_lstShapePoints.PreAllocate(nCount, nCount);
if (nCount)
{
memcpy(&m_lstShapePoints[0], pPoints, sizeof(Vec3) * nCount);
}
// update bbox
m_boxArea.max = SetMinBB();
m_boxArea.min = SetMaxBB();
for (int i = 0; i < nCount; i++)
{
m_boxArea.max.CheckMax(pPoints[i]);
m_boxArea.min.CheckMin(pPoints[i]);
m_boxArea.max.CheckMax(pPoints[i] + Vec3(0, 0, m_fHeight));
m_boxArea.min.CheckMin(pPoints[i] + Vec3(0, 0, m_fHeight));
}
UpdateGeometryBBox();
UpdateClipVolume();
}
void CVisArea::StaticReset()
{
stl::free_container(m_lUnavailableAreas);
stl::free_container(s_tmpLstPortVertsClipped);
stl::free_container(s_tmpLstPortVertsSS);
stl::free_container(s_tmpPolygonA);
stl::free_container(s_tmpLstLights);
stl::free_container(s_tmpCameras);
s_tmpClipContext.Reset();
}
void CVisArea::Init()
{
m_fGetDistanceThruVisAreasMinDistance = 10000.f;
m_nGetDistanceThruVisAreasLastCallID = -1;
m_pVisAreaColdData = NULL;
m_boxStatics.min = m_boxStatics.max = m_boxArea.min = m_boxArea.max = Vec3(0, 0, 0);
m_nRndFrameId = -1;
m_bActive = true;
m_fHeight = 0;
m_vAmbientColor(0, 0, 0);
m_vConnNormals[0] = m_vConnNormals[1] = Vec3(0, 0, 0);
m_bAffectedByOutLights = false;
m_fDistance = 0;
m_bOceanVisible = m_bSkyOnly = false;
memset(m_arrOcclCamera, 0, sizeof(m_arrOcclCamera));
m_fViewDistRatio = 100.f;
m_bDoubleSide = true;
m_bUseDeepness = false;
m_bUseInIndoors = false;
m_bIgnoreSky = m_bThisIsPortal = false;
m_bIgnoreGI = false;
m_bIgnoreOutdoorAO = false;
m_lstCurCamerasCap = 0;
m_lstCurCamerasLen = 0;
m_lstCurCamerasIdx = 0;
m_nVisGUID = 0;
m_fPortalBlending = 0.5f;
m_nStencilRef = 0;
}
CVisArea::CVisArea()
{
Init();
}
CVisArea::CVisArea(VisAreaGUID visGUID)
{
Init();
m_nVisGUID = visGUID;
}
CVisArea::~CVisArea()
{
for (int i = 0; i < MAX_RECURSION_LEVELS; i++)
{
SAFE_DELETE(m_arrOcclCamera[i]);
}
GetVisAreaManager()->OnVisAreaDeleted(this);
if (m_pVisAreaColdData->m_dataType == eCDT_Portal)
{
SPortalColdData* pPortalColdData = static_cast<SPortalColdData*>(m_pVisAreaColdData);
if (pPortalColdData->m_pRNTmpData)
{
Get3DEngine()->FreeRNTmpData(&pPortalColdData->m_pRNTmpData);
}
}
}
float LineSegDistanceSqr(const Vec3& vPos, const Vec3& vP0, const Vec3& vP1)
{
// Dist of line seg A(+D) from origin:
// P = A + D t[0..1]
// d^2(t) = (A + D t)^2 = A^2 + 2 A*D t + D^2 t^2
// d^2\t = 2 A*D + 2 D^2 t = 0
// tmin = -A*D / D^2 clamp_tpl(0,1)
// Pmin = A + D tmin
Vec3 vP = vP0 - vPos;
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);
}
}
return vP.GetLengthSquared();
}
void CVisArea::FindSurroundingVisAreaReqursive(int nMaxReqursion, bool bSkipDisabledPortals, PodArray<IVisArea*>* pVisitedAreas, int nMaxVisitedAreas, int nDeepness, PodArray<CVisArea*>* pUnavailableAreas)
{
pUnavailableAreas->Add(this);
if (pVisitedAreas && pVisitedAreas->Count() < nMaxVisitedAreas)
{
pVisitedAreas->Add(this);
}
if (nMaxReqursion > (nDeepness + 1))
{
for (int p = 0; p < m_lstConnections.Count(); p++)
{
if (!bSkipDisabledPortals || m_lstConnections[p]->IsActive())
{
if (-1 == pUnavailableAreas->Find(m_lstConnections[p]))
{
m_lstConnections[p]->FindSurroundingVisAreaReqursive(nMaxReqursion, bSkipDisabledPortals, pVisitedAreas, nMaxVisitedAreas, nDeepness + 1, pUnavailableAreas);
}
}
}
}
}
void CVisArea::FindSurroundingVisArea(int nMaxReqursion, bool bSkipDisabledPortals, PodArray<IVisArea*>* pVisitedAreas, int nMaxVisitedAreas, int nDeepness)
{
if (pVisitedAreas)
{
if (pVisitedAreas->capacity() < (uint32)nMaxVisitedAreas)
{
pVisitedAreas->PreAllocate(nMaxVisitedAreas);
}
}
m_lUnavailableAreas.Clear();
m_lUnavailableAreas.PreAllocate(nMaxVisitedAreas, 0);
FindSurroundingVisAreaReqursive(nMaxReqursion, bSkipDisabledPortals, pVisitedAreas, nMaxVisitedAreas, nDeepness, &m_lUnavailableAreas);
}
int CVisArea::GetVisFrameId()
{
return m_nRndFrameId;
}
bool Is2dLinesIntersect(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4)
{
float fDiv = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1);
if (fabs(fDiv) < 0.00001f)
{
return false;
}
float ua = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) / fDiv;
float ub = ((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3)) / fDiv;
return ua > 0 && ua < 1 && ub > 0 && ub < 1;
}
Vec3 CVisArea::GetConnectionNormal(CVisArea* pPortal)
{
assert(m_lstShapePoints.Count() >= 3);
// find side of shape intersecting with portal
int nIntersNum = 0;
Vec3 arrNormals[2] = {Vec3(0, 0, 0), Vec3(0, 0, 0)};
for (int v = 0; v < m_lstShapePoints.Count(); v++)
{
nIntersNum = 0;
arrNormals[0] = Vec3(0, 0, 0);
arrNormals[1] = Vec3(0, 0, 0);
const Vec3& v0 = m_lstShapePoints[v];
const Vec3& v1 = m_lstShapePoints[(v + 1) % m_lstShapePoints.Count()];
for (int p = 0; p < pPortal->m_lstShapePoints.Count(); p++)
{
const Vec3& p0 = pPortal->m_lstShapePoints[p];
const Vec3& p1 = pPortal->m_lstShapePoints[(p + 1) % pPortal->m_lstShapePoints.Count()];
if (Is2dLinesIntersect(v0.x, v0.y, v1.x, v1.y, p0.x, p0.y, p1.x, p1.y))
{
Vec3 vNormal = (v0 - v1).GetNormalized().Cross(Vec3(0, 0, 1.f));
if (nIntersNum < 2)
{
arrNormals[nIntersNum++] = (IsShapeClockwise()) ? -vNormal : vNormal;
}
}
}
if (nIntersNum == 2)
{
break;
}
}
if (nIntersNum == 2 &&
//IsEquivalent(arrNormals[0] == arrNormals[1])
arrNormals[0].IsEquivalent(arrNormals[1], VEC_EPSILON)
)
{
return arrNormals[0];
}
{
int nBottomPoints = 0;
for (int p = 0; p < pPortal->m_lstShapePoints.Count() && p < 4; p++)
{
if (IsPointInsideVisArea(pPortal->m_lstShapePoints[p]))
{
nBottomPoints++;
}
}
int nUpPoints = 0;
for (int p = 0; p < pPortal->m_lstShapePoints.Count() && p < 4; p++)
{
if (IsPointInsideVisArea(pPortal->m_lstShapePoints[p] + Vec3(0, 0, pPortal->m_fHeight)))
{
nUpPoints++;
}
}
if (nBottomPoints == 0 && nUpPoints == 4)
{
return Vec3(0, 0, 1);
}
if (nBottomPoints == 4 && nUpPoints == 0)
{
return Vec3(0, 0, -1);
}
}
return Vec3(0, 0, 0);
}
void CVisArea::UpdatePortalCameraPlanes(CCamera& cam, Vec3* pVerts, bool NotForcePlaneSet, const SRenderingPassInfo& passInfo)
{
const Vec3& vCamPos = passInfo.GetCamera().GetPosition();
Plane plane, farPlane;
farPlane = *passInfo.GetCamera().GetFrustumPlane(FR_PLANE_FAR);
cam.SetFrustumPlane(FR_PLANE_FAR, farPlane);
//plane.SetPlane(pVerts[0],pVerts[2],pVerts[1]); // can potentially create a plane facing the wrong way
plane.SetPlane(-farPlane.n, pVerts[0]);
cam.SetFrustumPlane(FR_PLANE_NEAR, plane);
plane.SetPlane(vCamPos, pVerts[3], pVerts[2]); // update plane only if it reduces fov
if (!NotForcePlaneSet || plane.n.Dot(cam.GetFrustumPlane(FR_PLANE_LEFT)->n) <
cam.GetFrustumPlane(FR_PLANE_RIGHT)->n.Dot(cam.GetFrustumPlane(FR_PLANE_LEFT)->n))
{
cam.SetFrustumPlane(FR_PLANE_RIGHT, plane);
}
plane.SetPlane(vCamPos, pVerts[1], pVerts[0]); // update plane only if it reduces fov
if (!NotForcePlaneSet || plane.n.Dot(cam.GetFrustumPlane(FR_PLANE_RIGHT)->n) <
cam.GetFrustumPlane(FR_PLANE_LEFT)->n.Dot(cam.GetFrustumPlane(FR_PLANE_RIGHT)->n))
{
cam.SetFrustumPlane(FR_PLANE_LEFT, plane);
}
plane.SetPlane(vCamPos, pVerts[0], pVerts[3]); // update plane only if it reduces fov
if (!NotForcePlaneSet || plane.n.Dot(cam.GetFrustumPlane(FR_PLANE_TOP)->n) <
cam.GetFrustumPlane(FR_PLANE_BOTTOM)->n.Dot(cam.GetFrustumPlane(FR_PLANE_TOP)->n))
{
cam.SetFrustumPlane(FR_PLANE_BOTTOM, plane);
}
plane.SetPlane(vCamPos, pVerts[2], pVerts[1]); // update plane only if it reduces fov
if (!NotForcePlaneSet || plane.n.Dot(cam.GetFrustumPlane(FR_PLANE_BOTTOM)->n) <
cam.GetFrustumPlane(FR_PLANE_TOP)->n.Dot(cam.GetFrustumPlane(FR_PLANE_BOTTOM)->n))
{
cam.SetFrustumPlane(FR_PLANE_TOP, plane);
}
Vec3 arrvPortVertsCamSpace[4];
for (int i = 0; i < 4; i++)
{
arrvPortVertsCamSpace[i] = pVerts[i] - cam.GetPosition();
}
cam.SetFrustumVertices(arrvPortVertsCamSpace);
//cam.UpdateFrustum();
if (GetCVars()->e_Portals == 5)
{
float farrColor[4] = {1, 1, 1, 1};
// GetRenderer()->SetMaterialColor(1,1,1,1);
DrawLine(pVerts[0], pVerts[1]);
GetRenderer()->DrawLabelEx(pVerts[0], 1, farrColor, false, true, "0");
DrawLine(pVerts[1], pVerts[2]);
GetRenderer()->DrawLabelEx(pVerts[1], 1, farrColor, false, true, "1");
DrawLine(pVerts[2], pVerts[3]);
GetRenderer()->DrawLabelEx(pVerts[2], 1, farrColor, false, true, "2");
DrawLine(pVerts[3], pVerts[0]);
GetRenderer()->DrawLabelEx(pVerts[3], 1, farrColor, false, true, "3");
}
}
int __cdecl CVisAreaManager__CmpDistToPortal(const void* v1, const void* v2);
void CVisArea::PreRender(int nReqursionLevel,
CCamera CurCamera, CVisArea* pParent, CVisArea* pCurPortal,
bool* pbOutdoorVisible, PodArray<CCamera>* plstOutPortCameras, bool* pbSkyVisible, bool* pbOceanVisible,
PodArray<CVisArea*>& lstVisibleAreas,
const SRenderingPassInfo& passInfo)
{
// mark as rendered
if (m_nRndFrameId != passInfo.GetFrameID())
{
m_lstCurCamerasIdx = 0;
m_lstCurCamerasLen = 0;
if (m_lstCurCamerasCap)
{
m_lstCurCamerasIdx = s_tmpCameras.size();
s_tmpCameras.resize(s_tmpCameras.size() + m_lstCurCamerasCap);
}
}
m_nRndFrameId = passInfo.GetFrameID();
if (m_bAffectedByOutLights)
{
GetVisAreaManager()->m_bSunIsNeeded = true;
}
if (m_lstCurCamerasLen == m_lstCurCamerasCap)
{
int newIdx = s_tmpCameras.size();
m_lstCurCamerasCap += max(1, m_lstCurCamerasCap / 2);
s_tmpCameras.resize(newIdx + m_lstCurCamerasCap);
if (m_lstCurCamerasLen)
{
memcpy(&s_tmpCameras[newIdx], &s_tmpCameras[m_lstCurCamerasIdx], m_lstCurCamerasLen * sizeof(CCamera));
}
m_lstCurCamerasIdx = newIdx;
}
s_tmpCameras[m_lstCurCamerasIdx + m_lstCurCamerasLen] = CurCamera;
++m_lstCurCamerasLen;
if (lstVisibleAreas.Find(this) < 0)
{
lstVisibleAreas.Add(this);
m_nStencilRef = GetRenderer()->EF_AddDeferredClipVolume(this);
}
// check recursion and portal activity
if (!nReqursionLevel || !m_bActive)
{
return;
}
if (pParent && m_bThisIsPortal && m_lstConnections.Count() == 1 && // detect entrance
!IsPointInsideVisArea(passInfo.GetCamera().GetPosition())) // detect camera in outdoors
{
AABB boxAreaEx = m_boxArea;
float fZNear = CurCamera.GetNearPlane();
boxAreaEx.min -= Vec3(fZNear, fZNear, fZNear);
boxAreaEx.max += Vec3(fZNear, fZNear, fZNear);
if (!CurCamera.IsAABBVisible_E(boxAreaEx)) // if portal is invisible
{
return; // stop recursion
}
}
bool bCanSeeThruThisArea = true;
// prepare new camera for next areas
if (m_bThisIsPortal && m_lstConnections.Count() &&
(this != pCurPortal || !pCurPortal->IsPointInsideVisArea(CurCamera.GetPosition())))
{
Vec3 vCenter = m_boxArea.GetCenter();
float fRadius = m_boxArea.GetRadius();
Vec3 vPortNorm = (!pParent || pParent == m_lstConnections[0] || m_lstConnections.Count() == 1) ?
m_vConnNormals[0] : m_vConnNormals[1];
// exit/entrance portal has only one normal in direction to outdoors, so flip it to the camera
if (m_lstConnections.Count() == 1 && !pParent)
{
vPortNorm = -vPortNorm;
}
// back face check
Vec3 vPortToCamDir = CurCamera.GetPosition() - vCenter;
if (vPortToCamDir.Dot(vPortNorm) < 0)
{
return;
}
if (!m_bDoubleSide)
{
if (vPortToCamDir.Dot(m_vConnNormals[0]) < 0)
{
return;
}
}
Vec3 arrPortVerts[4];
Vec3 arrPortVertsOtherSide[4];
bool barrPortVertsOtherSideValid = false;
if (pParent && !vPortNorm.IsEquivalent(Vec3(0, 0, 0), VEC_EPSILON) && vPortNorm.z)
{ // up/down portal
int nEven = IsShapeClockwise();
if (vPortNorm.z > 0)
{
nEven = !nEven;
}
for (int i = 0; i < 4; i++)
{
arrPortVerts[i] = m_lstShapePoints[nEven ? (3 - i) : i] + Vec3(0, 0, m_fHeight) * (vPortNorm.z > 0);
arrPortVertsOtherSide[i] = m_lstShapePoints[nEven ? (3 - i) : i] + Vec3(0, 0, m_fHeight) * (vPortNorm.z < 0);
}
barrPortVertsOtherSideValid = true;
}
else if (!vPortNorm.IsEquivalent(Vec3(0, 0, 0), VEC_EPSILON) && vPortNorm.z == 0)
{ // basic portal
Vec3 arrInAreaPoint[2] = {Vec3(0, 0, 0), Vec3(0, 0, 0)};
int arrInAreaPointId[2] = {-1, -1};
int nInAreaPointCounter = 0;
Vec3 arrOutAreaPoint[2] = {Vec3(0, 0, 0), Vec3(0, 0, 0)};
int nOutAreaPointCounter = 0;
// find 2 points of portal in this area (or in this outdoors)
for (int i = 0; i < m_lstShapePoints.Count() && nInAreaPointCounter < 2; i++)
{
Vec3 vTestPoint = m_lstShapePoints[i] + Vec3(0, 0, m_fHeight * 0.5f);
CVisArea* pAnotherArea = m_lstConnections[0];
if ((pParent && (pParent->IsPointInsideVisArea(vTestPoint))) ||
(!pParent && (!pAnotherArea->IsPointInsideVisArea(vTestPoint))))
{
arrInAreaPointId[nInAreaPointCounter] = i;
arrInAreaPoint[nInAreaPointCounter++] = m_lstShapePoints[i];
}
}
// find 2 points of portal not in this area (or not in this outdoors)
for (int i = 0; i < m_lstShapePoints.Count() && nOutAreaPointCounter < 2; i++)
{
Vec3 vTestPoint = m_lstShapePoints[i] + Vec3(0, 0, m_fHeight * 0.5f);
CVisArea* pAnotherArea = m_lstConnections[0];
if ((pParent && (pParent->IsPointInsideVisArea(vTestPoint))) ||
(!pParent && (!pAnotherArea->IsPointInsideVisArea(vTestPoint))))
{
}
else
{
arrOutAreaPoint[nOutAreaPointCounter++] = m_lstShapePoints[i];
}
}
if (nInAreaPointCounter == 2)
{ // success, take into account volume and portal shape versts order
int nEven = IsShapeClockwise();
if (arrInAreaPointId[1] - arrInAreaPointId[0] != 1)
{
nEven = !nEven;
}
arrPortVerts[0] = arrInAreaPoint[nEven];
arrPortVerts[1] = arrInAreaPoint[nEven] + Vec3(0, 0, m_fHeight);
arrPortVerts[2] = arrInAreaPoint[!nEven] + Vec3(0, 0, m_fHeight);
arrPortVerts[3] = arrInAreaPoint[!nEven];
nEven = !nEven;
arrPortVertsOtherSide[0] = arrOutAreaPoint[nEven];
arrPortVertsOtherSide[1] = arrOutAreaPoint[nEven] + Vec3(0, 0, m_fHeight);
arrPortVertsOtherSide[2] = arrOutAreaPoint[!nEven] + Vec3(0, 0, m_fHeight);
arrPortVertsOtherSide[3] = arrOutAreaPoint[!nEven];
barrPortVertsOtherSideValid = true;
}
else
{ // something wrong
Warning("CVisArea::PreRender: Invalid portal: %s", m_pVisAreaColdData->m_sName);
return;
}
}
else if (!pParent && vPortNorm.z == 0 && m_lstConnections.Count() == 1)
{ // basic entrance portal
Vec3 vBorder = (vPortNorm.Cross(Vec3(0, 0, 1.f))).GetNormalized() * fRadius;
arrPortVerts[0] = vCenter - Vec3(0, 0, 1.f) * fRadius - vBorder;
arrPortVerts[1] = vCenter + Vec3(0, 0, 1.f) * fRadius - vBorder;
arrPortVerts[2] = vCenter + Vec3(0, 0, 1.f) * fRadius + vBorder;
arrPortVerts[3] = vCenter - Vec3(0, 0, 1.f) * fRadius + vBorder;
}
else if (!pParent && vPortNorm.z != 0 && m_lstConnections.Count() == 1)
{ // up/down entrance portal
Vec3 vBorder = (vPortNorm.Cross(Vec3(0, 1, 0.f))).GetNormalized() * fRadius;
arrPortVerts[0] = vCenter - Vec3(0, 1, 0.f) * fRadius + vBorder;
arrPortVerts[1] = vCenter + Vec3(0, 1, 0.f) * fRadius + vBorder;
arrPortVerts[2] = vCenter + Vec3(0, 1, 0.f) * fRadius - vBorder;
arrPortVerts[3] = vCenter - Vec3(0, 1, 0.f) * fRadius - vBorder;
}
else
{ // something wrong or areabox portal - use simple solution
if (vPortNorm.IsEquivalent(Vec3(0, 0, 0), VEC_EPSILON))
{
vPortNorm = (vCenter - passInfo.GetCamera().GetPosition()).GetNormalized();
}
Vec3 vBorder = (vPortNorm.Cross(Vec3(0, 0, 1.f))).GetNormalized() * fRadius;
arrPortVerts[0] = vCenter - Vec3(0, 0, 1.f) * fRadius - vBorder;
arrPortVerts[1] = vCenter + Vec3(0, 0, 1.f) * fRadius - vBorder;
arrPortVerts[2] = vCenter + Vec3(0, 0, 1.f) * fRadius + vBorder;
arrPortVerts[3] = vCenter - Vec3(0, 0, 1.f) * fRadius + vBorder;
}
if (GetCVars()->e_Portals == 4) // make color recursion dependent
{
GetRenderer()->SetMaterialColor(1, 1, passInfo.IsGeneralPass(), 1);
}
Vec3 vPortalFaceCenter = (arrPortVerts[0] + arrPortVerts[1] + arrPortVerts[2] + arrPortVerts[3]) / 4;
vPortToCamDir = CurCamera.GetPosition() - vPortalFaceCenter;
if (vPortToCamDir.GetNormalized().Dot(vPortNorm) < -0.01f)
{
UpdatePortalBlendInfo();
return;
}
const bool Upright = (fabsf(vPortNorm.z) < FLT_EPSILON);
CCamera camParent = CurCamera;
// clip portal quad by camera planes
PodArray<Vec3>& lstPortVertsClipped = s_tmpLstPortVertsClipped;
lstPortVertsClipped.Clear();
lstPortVertsClipped.AddList(arrPortVerts, 4);
ClipPortalVerticesByCameraFrustum(&lstPortVertsClipped, camParent);
AABB aabb;
aabb.Reset();
if (lstPortVertsClipped.Count() > 2)
{
// find screen space bounds of clipped portal
for (int i = 0; i < lstPortVertsClipped.Count(); i++)
{
Vec3 vSS;
GetRenderer()->ProjectToScreen(lstPortVertsClipped[i].x, lstPortVertsClipped[i].y, lstPortVertsClipped[i].z, &vSS.x, &vSS.y, &vSS.z);
vSS.y = 100 - vSS.y;
aabb.Add(vSS);
}
}
else
{
if (!CVisArea::IsSphereInsideVisArea(CurCamera.GetPosition(), CurCamera.GetNearPlane()))
{
bCanSeeThruThisArea = false;
}
}
if (lstPortVertsClipped.Count() > 2 && aabb.min.z > 0.01f)
{
PodArray<Vec3>& lstPortVertsSS = s_tmpLstPortVertsSS;
lstPortVertsSS.PreAllocate(4, 4);
// get 3d positions of portal bounds
{
int i = 0;
float w = (float)GetRenderer()->GetWidth();
float h = (float)GetRenderer()->GetHeight();
float d = 0.01f;
GetRenderer()->UnProjectFromScreen(aabb.min.x * w / 100, aabb.min.y * h / 100, d, &lstPortVertsSS[i].x, &lstPortVertsSS[i].y, &lstPortVertsSS[i].z);
i++;
GetRenderer()->UnProjectFromScreen(aabb.min.x * w / 100, aabb.max.y * h / 100, d, &lstPortVertsSS[i].x, &lstPortVertsSS[i].y, &lstPortVertsSS[i].z);
i++;
GetRenderer()->UnProjectFromScreen(aabb.max.x * w / 100, aabb.max.y * h / 100, d, &lstPortVertsSS[i].x, &lstPortVertsSS[i].y, &lstPortVertsSS[i].z);
i++;
GetRenderer()->UnProjectFromScreen(aabb.max.x * w / 100, aabb.min.y * h / 100, d, &lstPortVertsSS[i].x, &lstPortVertsSS[i].y, &lstPortVertsSS[i].z);
i++;
CurCamera.m_ScissorInfo.x1 = uint16(CLAMP(aabb.min.x * w / 100, 0, w));
CurCamera.m_ScissorInfo.y1 = uint16(CLAMP(aabb.min.y * h / 100, 0, h));
CurCamera.m_ScissorInfo.x2 = uint16(CLAMP(aabb.max.x * w / 100, 0, w));
CurCamera.m_ScissorInfo.y2 = uint16(CLAMP(aabb.max.y * h / 100, 0, h));
}
if (GetCVars()->e_Portals == 4)
{
for (int i = 0; i < lstPortVertsSS.Count(); i++)
{
float farrColor[4] = { float((nReqursionLevel & 1) > 0), float((nReqursionLevel & 2) > 0), float((nReqursionLevel & 4) > 0), 1};
ColorF c(farrColor[0], farrColor[1], farrColor[2], farrColor[3]);
DrawSphere(lstPortVertsSS[i], 0.002f, c);
GetRenderer()->DrawLabelEx(lstPortVertsSS[i], 0.1f, farrColor, false, true, "%d", i);
}
}
UpdatePortalCameraPlanes(CurCamera, lstPortVertsSS.GetElements(), Upright, passInfo);
bCanSeeThruThisArea =
(CurCamera.m_ScissorInfo.x1 < CurCamera.m_ScissorInfo.x2) &&
(CurCamera.m_ScissorInfo.y1 < CurCamera.m_ScissorInfo.y2);
}
if (m_bUseDeepness && bCanSeeThruThisArea && barrPortVertsOtherSideValid)
{
Vec3 vOtherSideBoxMax = SetMinBB();
Vec3 vOtherSideBoxMin = SetMaxBB();
for (int i = 0; i < 4; i++)
{
vOtherSideBoxMin.CheckMin(arrPortVertsOtherSide[i] - Vec3(0.01f, 0.01f, 0.01f));
vOtherSideBoxMax.CheckMax(arrPortVertsOtherSide[i] + Vec3(0.01f, 0.01f, 0.01f));
}
bCanSeeThruThisArea = CurCamera.IsAABBVisible_E(AABB(vOtherSideBoxMin, vOtherSideBoxMax));
}
if (bCanSeeThruThisArea && pParent && m_lstConnections.Count() == 1)
{ // set this camera for outdoor
if (nReqursionLevel >= 1)
{
if (!m_bSkyOnly)
{
if (plstOutPortCameras)
{
plstOutPortCameras->Add(CurCamera);
plstOutPortCameras->Last().m_pPortal = this;
}
if (pbOutdoorVisible)
{
*pbOutdoorVisible = true;
}
}
else if (pbSkyVisible)
{
*pbSkyVisible = true;
}
}
UpdatePortalBlendInfo();
return;
}
}
// sort portals by distance
if (!m_bThisIsPortal && m_lstConnections.Count())
{
for (int p = 0; p < m_lstConnections.Count(); p++)
{
CVisArea* pNeibVolume = m_lstConnections[p];
pNeibVolume->m_fDistance = CurCamera.GetPosition().GetDistance((pNeibVolume->m_boxArea.min + pNeibVolume->m_boxArea.max) * 0.5f);
}
qsort(&m_lstConnections[0], m_lstConnections.Count(),
sizeof(m_lstConnections[0]), CVisAreaManager__CmpDistToPortal);
}
if (m_bOceanVisible && pbOceanVisible)
{
*pbOceanVisible = true;
}
// recurse to connections
for (int p = 0; p < m_lstConnections.Count(); p++)
{
CVisArea* pNeibVolume = m_lstConnections[p];
if (pNeibVolume != pParent)
{
if (!m_bThisIsPortal)
{ // skip far portals
float fRadius = (pNeibVolume->m_boxArea.max - pNeibVolume->m_boxArea.min).GetLength() * 0.5f * GetFloatCVar(e_ViewDistRatioPortals) / 60.f;
if (pNeibVolume->m_fDistance * passInfo.GetZoomFactor() > fRadius * pNeibVolume->m_fViewDistRatio)
{
continue;
}
Vec3 vPortNorm = (this == pNeibVolume->m_lstConnections[0] || pNeibVolume->m_lstConnections.Count() == 1) ?
pNeibVolume->m_vConnNormals[0] : pNeibVolume->m_vConnNormals[1];
// back face check
Vec3 vPortToCamDir = CurCamera.GetPosition() - pNeibVolume->GetAABBox()->GetCenter();
if (vPortToCamDir.Dot(vPortNorm) < 0)
{
continue;
}
}
if ((bCanSeeThruThisArea || m_lstConnections.Count() == 1) && (m_bThisIsPortal || CurCamera.IsAABBVisible_F(pNeibVolume->m_boxStatics)))
{
pNeibVolume->PreRender(nReqursionLevel - 1, CurCamera, this, pCurPortal, pbOutdoorVisible, plstOutPortCameras, pbSkyVisible, pbOceanVisible, lstVisibleAreas, passInfo);
}
}
}
if (m_bThisIsPortal)
{
UpdatePortalBlendInfo();
}
}
//! return list of visareas connected to specified visarea (can return portals and sectors)
int CVisArea::GetRealConnections(IVisArea** pAreas, int nMaxConnNum, [[maybe_unused]] bool bSkipDisabledPortals)
{
int nOut = 0;
for (int nArea = 0; nArea < m_lstConnections.Count(); nArea++)
{
if (nOut < nMaxConnNum)
{
pAreas[nOut] = (IVisArea*)m_lstConnections[nArea];
}
nOut++;
}
return nOut;
}
//! return list of sectors conected to specified sector or portal (returns sectors only)
// todo: change the way it returns data
int CVisArea::GetVisAreaConnections(IVisArea** pAreas, int nMaxConnNum, bool bSkipDisabledPortals)
{
int nOut = 0;
if (IsPortal())
{
/* for(int nArea=0; nArea<m_lstConnections.Count(); nArea++)
{
if(nOut<nMaxConnNum)
pAreas[nOut] = (IVisArea*)m_lstConnections[nArea];
nOut++;
}*/
return min(nMaxConnNum, GetRealConnections(pAreas, nMaxConnNum, bSkipDisabledPortals));
}
else
{
for (int nPort = 0; nPort < m_lstConnections.Count(); nPort++)
{
CVisArea* pPortal = m_lstConnections[nPort];
assert(pPortal->IsPortal());
for (int nArea = 0; nArea < pPortal->m_lstConnections.Count(); nArea++)
{
if (pPortal->m_lstConnections[nArea] != this)
{
if (!bSkipDisabledPortals || pPortal->IsActive())
{
if (nOut < nMaxConnNum)
{
pAreas[nOut] = (IVisArea*)pPortal->m_lstConnections[nArea];
}
nOut++;
break; // take first valid connection
}
}
}
}
}
return min(nMaxConnNum, nOut);
}
bool CVisArea::IsPortalValid()
{
int nCount = m_lstConnections.Count();
if (nCount > 2 || nCount == 0)
{
return false;
}
for (int i = 0; i < nCount; i++)
{
if (m_vConnNormals[i].IsEquivalent(Vec3(0, 0, 0), VEC_EPSILON))
{
return false;
}
}
if (nCount > 1)
{
if (m_vConnNormals[0].Dot(m_vConnNormals[1]) > -0.99f)
{
return false;
}
}
return true;
}
bool CVisArea::IsPortalIntersectAreaInValidWay(CVisArea* pPortal)
{
const Vec3& v1Min = pPortal->m_boxArea.min;
const Vec3& v1Max = pPortal->m_boxArea.max;
const Vec3& v2Min = m_boxArea.min;
const Vec3& v2Max = 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)
{
// vertical portal
for (int v = 0; v < m_lstShapePoints.Count(); v++)
{
int nIntersNum = 0;
bool arrIntResult[4] = { 0, 0, 0, 0 };
for (int p = 0; p < pPortal->m_lstShapePoints.Count() && p < 4; p++)
{
const Vec3& v0 = m_lstShapePoints[v];
const Vec3& v1 = m_lstShapePoints[(v + 1) % m_lstShapePoints.Count()];
const Vec3& p0 = pPortal->m_lstShapePoints[p];
const Vec3& p1 = pPortal->m_lstShapePoints[(p + 1) % pPortal->m_lstShapePoints.Count()];
if (Is2dLinesIntersect(v0.x, v0.y, v1.x, v1.y, p0.x, p0.y, p1.x, p1.y))
{
nIntersNum++;
arrIntResult[p] = true;
}
}
if (nIntersNum == 2 && arrIntResult[0] == arrIntResult[2] && arrIntResult[1] == arrIntResult[3])
{
return true;
}
}
// horisontal portal
{
int nBottomPoints = 0, nUpPoints = 0;
for (int p = 0; p < pPortal->m_lstShapePoints.Count() && p < 4; p++)
{
if (IsPointInsideVisArea(pPortal->m_lstShapePoints[p]))
{
nBottomPoints++;
}
}
for (int p = 0; p < pPortal->m_lstShapePoints.Count() && p < 4; p++)
{
if (IsPointInsideVisArea(pPortal->m_lstShapePoints[p] + Vec3(0, 0, pPortal->m_fHeight)))
{
nUpPoints++;
}
}
if (nBottomPoints == 0 && nUpPoints == 4)
{
return true;
}
if (nBottomPoints == 4 && nUpPoints == 0)
{
return true;
}
}
}
}
}
return false;
}
/*
void CVisArea::SetTreeId(int nTreeId)
{
if(m_nTreeId == nTreeId)
return;
m_nTreeId = nTreeId;
for(int p=0; p<m_lstConnections.Count(); p++)
m_lstConnections[p]->SetTreeId(nTreeId);
}
*/
bool CVisArea::IsShapeClockwise()
{
float fClockWise =
(m_lstShapePoints[0].x - m_lstShapePoints[1].x) * (m_lstShapePoints[2].y - m_lstShapePoints[1].y) -
(m_lstShapePoints[0].y - m_lstShapePoints[1].y) * (m_lstShapePoints[2].x - m_lstShapePoints[1].x);
return fClockWise > 0;
}
void CVisArea::DrawAreaBoundsIntoCBuffer([[maybe_unused]] CCullBuffer* pCBuffer)
{
assert(!"temprary not supported");
/* if(m_lstShapePoints.Count()!=4)
return;
Vec3 arrVerts[8];
int arrIndices[24];
int v=0;
int i=0;
for(int p=0; p<4 && p<m_lstShapePoints.Count(); p++)
{
arrVerts[v++] = m_lstShapePoints[p];
arrVerts[v++] = m_lstShapePoints[p] + Vec3(0,0,m_fHeight);
arrIndices[i++] = (p*2+0)%8;
arrIndices[i++] = (p*2+1)%8;
arrIndices[i++] = (p*2+2)%8;
arrIndices[i++] = (p*2+3)%8;
arrIndices[i++] = (p*2+2)%8;
arrIndices[i++] = (p*2+1)%8;
}
Matrix34 mat;
mat.SetIdentity();
pCBuffer->AddMesh(arrVerts,8,arrIndices,24,&mat);*/
}
void CVisArea::ClipPortalVerticesByCameraFrustum(PodArray<Vec3>* pPolygon, const CCamera& cam)
{
Plane planes[5] = {
*cam.GetFrustumPlane(FR_PLANE_RIGHT),
*cam.GetFrustumPlane(FR_PLANE_LEFT),
*cam.GetFrustumPlane(FR_PLANE_TOP),
*cam.GetFrustumPlane(FR_PLANE_BOTTOM),
*cam.GetFrustumPlane(FR_PLANE_NEAR)
};
const PodArray<Vec3>& clipped = s_tmpClipContext.Clip(*pPolygon, planes, 4);
pPolygon->Clear();
pPolygon->AddList(clipped);
}
void CVisArea::GetMemoryUsage(ICrySizer* pSizer)
{
// pSizer->AddContainer(m_lstEntities[STATIC_OBJECTS]);
//pSizer->AddContainer(m_lstEntities[DYNAMIC_OBJECTS]);
// TODO: include obects tree
if (m_pObjectsTree)
{
SIZER_COMPONENT_NAME(pSizer, "IndoorObjectsTree");
m_pObjectsTree->GetMemoryUsage(pSizer);
}
// for(int nStatic=0; nStatic<2; nStatic++)
//for(int i=0; i<m_lstEntities[nStatic].Count(); i++)
// nSize += m_lstEntities[nStatic][i].pNode->GetMemoryUsage();
pSizer->AddObject(this, sizeof(*this));
}
void CVisArea::AddConnectedAreas(PodArray<CVisArea*>& lstAreas, int nMaxRecursion)
{
if (lstAreas.Find(this) < 0)
{
lstAreas.Add(this);
// add connected areas
if (nMaxRecursion)
{
nMaxRecursion--;
for (int p = 0; p < m_lstConnections.Count(); p++)
{
m_lstConnections[p]->AddConnectedAreas(lstAreas, nMaxRecursion);
}
}
}
}
bool CVisArea::GetDistanceThruVisAreas(AABB vCurBoxIn, IVisArea* pTargetArea, const AABB& targetBox, int nMaxReqursion, float& fResDist)
{
return GetDistanceThruVisAreasReq(vCurBoxIn, 0, pTargetArea, targetBox, nMaxReqursion, fResDist, NULL, s_nGetDistanceThruVisAreasCallCounter++);
}
float DistanceAABB(const AABB& aBox, const AABB& bBox)
{
float result = 0;
for (int i = 0; i < 3; ++i)
{
const float& aMin = aBox.min[i];
const float& aMax = aBox.max[i];
const float& bMin = bBox.min[i];
const float& bMax = bBox.max[i];
if (aMin > bMax)
{
const float delta = bMax - aMin;
result += delta * delta;
}
else if (bMin > aMax)
{
const float delta = aMax - bMin;
result += delta * delta;
}
// else the projection intervals overlap.
}
return sqrt(result);
}
bool CVisArea::GetDistanceThruVisAreasReq(AABB vCurBoxIn, float fCurDistIn, IVisArea* pTargetArea, const AABB& targetBox, int nMaxReqursion, float& fResDist, CVisArea* pPrevArea, int nCallID)
{
if (pTargetArea == this || (pTargetArea == NULL && IsConnectedToOutdoor()))
{ // target area is found
fResDist = min(fResDist, fCurDistIn + DistanceAABB(vCurBoxIn, targetBox));
return true;
}
// if we already visited this area and last time input distance was smaller - makes no sence to continue
if (nCallID == m_nGetDistanceThruVisAreasLastCallID)
{
if (fCurDistIn >= m_fGetDistanceThruVisAreasMinDistance)
{
return false;
}
}
m_nGetDistanceThruVisAreasLastCallID = nCallID;
m_fGetDistanceThruVisAreasMinDistance = fCurDistIn;
fResDist = FLT_MAX;
bool bFound = false;
if (nMaxReqursion > 1)
{
for (int p = 0; p < m_lstConnections.Count(); p++)
{
if ((m_lstConnections[p] != pPrevArea) && m_lstConnections[p]->IsActive())
{
AABB vCurBox = vCurBoxIn;
float fCurDist = fCurDistIn;
float dist = FLT_MAX;
if (IsPortal())
{
vCurBox = vCurBoxIn;
fCurDist = fCurDistIn;
}
else
{
vCurBox = *m_lstConnections[p]->GetAABBox();
fCurDist = fCurDistIn + DistanceAABB(vCurBox, vCurBoxIn);
}
if (m_lstConnections[p]->GetDistanceThruVisAreasReq(vCurBox, fCurDist, pTargetArea, targetBox, nMaxReqursion - 1, dist, this, nCallID))
{
bFound = true;
fResDist = min(fResDist, dist);
}
}
}
}
return bFound;
}
void CVisArea::OffsetPosition(const Vec3& delta)
{
m_boxArea.Move(delta);
m_boxStatics.Move(delta);
for (int i = 0; i < m_lstShapePoints.Count(); i++)
{
m_lstShapePoints[i] += delta;
}
if (m_pObjectsTree)
{
m_pObjectsTree->OffsetObjects(delta);
}
}