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.
1837 lines
56 KiB
C++
1837 lines
56 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.
|
|
|
|
#include "Cry3DEngine_precompiled.h"
|
|
#include "CullBuffer.h"
|
|
#include "DecalRenderNode.h"
|
|
#include "RenderMeshMerger.h"
|
|
#include "DecalManager.h"
|
|
#include "VisAreas.h"
|
|
#include "LightEntity.h"
|
|
#include "WaterVolumeRenderNode.h"
|
|
#include "DistanceCloudRenderNode.h"
|
|
#include "ShadowCache.h"
|
|
#include "Ocean.h"
|
|
#include "IShader.h"
|
|
#include <PakLoadDataUtils.h>
|
|
|
|
#include <AzCore/Component/TransformBus.h>
|
|
|
|
#define MAX_NODE_NUM 7
|
|
|
|
const float fNodeMinSize = 8.f;
|
|
const float fObjectToNodeSizeRatio = 1.f / 8.f;
|
|
const float fMinShadowCasterViewDist = 8.f;
|
|
|
|
PodArray<COctreeNode*> COctreeNode::m_arrEmptyNodes;
|
|
bool COctreeNode::m_removeVegetationCastersOneByOne = true;
|
|
|
|
COctreeNode::~COctreeNode()
|
|
{
|
|
for (int l = 0; l < eRNListType_ListsNum; l++)
|
|
{
|
|
for (IRenderNode* pObj = m_arrObjects[l].m_pFirstNode, * pNext; pObj; pObj = pNext)
|
|
{
|
|
pNext = pObj->m_pNext;
|
|
|
|
if (pObj->IsAllocatedOutsideOf3DEngineDLL())
|
|
{
|
|
Get3DEngine()->UnRegisterEntityDirect(pObj);
|
|
}
|
|
else
|
|
{
|
|
pObj->ReleaseNode(true);
|
|
}
|
|
}
|
|
|
|
assert(!m_arrObjects[l].m_pFirstNode);
|
|
}
|
|
|
|
for (int i = 0; i < 8; i++)
|
|
{
|
|
delete m_arrChilds[i];
|
|
m_arrChilds[i] = NULL;
|
|
}
|
|
|
|
m_arrEmptyNodes.Delete(this);
|
|
|
|
if (GetObjManager())
|
|
{
|
|
GetObjManager()->GetArrStreamingNodeStack().Delete(this);
|
|
}
|
|
|
|
if (m_pRNTmpData)
|
|
{
|
|
Get3DEngine()->FreeRNTmpData(&m_pRNTmpData);
|
|
}
|
|
}
|
|
|
|
void COctreeNode::SetVisArea(CVisArea* pVisArea)
|
|
{
|
|
m_pVisArea = pVisArea;
|
|
for (int i = 0; i < 8; i++)
|
|
{
|
|
if (m_arrChilds[i])
|
|
{
|
|
m_arrChilds[i]->SetVisArea(pVisArea);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
extern float arrVegetation_fSpriteSwitchState[nThreadsNum];
|
|
|
|
|
|
void COctreeNode::Render_Object_Nodes(bool bNodeCompletelyInFrustum, int nRenderMask, const SRenderingPassInfo& passInfo, SRendItemSorter& rendItemSorter)
|
|
{
|
|
assert(nRenderMask & OCTREENODE_RENDER_FLAG_OBJECTS);
|
|
|
|
const CCamera& rCam = passInfo.GetCamera();
|
|
|
|
if (m_nOccludedFrameId == passInfo.GetFrameID())
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!bNodeCompletelyInFrustum && !rCam.IsAABBVisible_EH(m_objectsBox, &bNodeCompletelyInFrustum))
|
|
{
|
|
return;
|
|
}
|
|
|
|
const Vec3& vCamPos = rCam.GetPosition();
|
|
|
|
float fNodeDistanceSq = Distance::Point_AABBSq(vCamPos, m_objectsBox) * sqr(passInfo.GetZoomFactor());
|
|
|
|
if (fNodeDistanceSq > sqr(m_fObjectsMaxViewDist))
|
|
{
|
|
return;
|
|
}
|
|
|
|
float fNodeDistance = sqrt_tpl(fNodeDistanceSq);
|
|
|
|
Get3DEngine()->CheckCreateRNTmpData(&m_pRNTmpData, NULL, passInfo);
|
|
|
|
if (m_nLastVisFrameId != passInfo.GetFrameID() && m_pParent)
|
|
{
|
|
if (GetObjManager()->IsBoxOccluded(m_objectsBox, fNodeDistance, &m_pRNTmpData->userData.m_OcclState, m_pVisArea != NULL, eoot_OCCELL, passInfo))
|
|
{
|
|
m_nOccludedFrameId = passInfo.GetFrameID();
|
|
return;
|
|
}
|
|
}
|
|
|
|
m_nLastVisFrameId = passInfo.GetFrameID();
|
|
|
|
if (GetCVars()->e_ObjectsTreeBBoxes)
|
|
{
|
|
if (GetCVars()->e_ObjectsTreeBBoxes == 1)
|
|
{
|
|
const AABB& nodeBox = GetNodeBox();
|
|
DrawBBox(nodeBox, Col_Blue);
|
|
}
|
|
if (GetCVars()->e_ObjectsTreeBBoxes == 2)
|
|
{
|
|
DrawBBox(m_objectsBox, Col_Red);
|
|
}
|
|
}
|
|
|
|
m_fNodeDistance = fNodeDistance;
|
|
m_bNodeCompletelyInFrustum = bNodeCompletelyInFrustum;
|
|
|
|
if (HasAnyRenderableCandidates(passInfo))
|
|
{
|
|
// when using the occlusion culler, push the work to the jobs doing the occlusion checks, else just compute in main thread
|
|
if (GetCVars()->e_StatObjBufferRenderTasks == 1 && passInfo.IsGeneralPass())
|
|
{
|
|
GetObjManager()->PushIntoCullQueue(SCheckOcclusionJobData::CreateOctreeJobData(this, nRenderMask, rendItemSorter, &passInfo.GetCamera()));
|
|
}
|
|
else
|
|
{
|
|
RenderContentJobEntry(nRenderMask, passInfo, rendItemSorter, &passInfo.GetCamera());
|
|
}
|
|
|
|
rendItemSorter.IncreaseOctreeCounter();
|
|
}
|
|
|
|
int nFirst =
|
|
((vCamPos.x > m_vNodeCenter.x) ? 4 : 0) |
|
|
((vCamPos.y > m_vNodeCenter.y) ? 2 : 0) |
|
|
((vCamPos.z > m_vNodeCenter.z) ? 1 : 0);
|
|
|
|
if (m_arrChilds[nFirst ])
|
|
{
|
|
m_arrChilds[nFirst ]->Render_Object_Nodes(bNodeCompletelyInFrustum, nRenderMask, passInfo, rendItemSorter);
|
|
}
|
|
|
|
if (m_arrChilds[nFirst ^ 1])
|
|
{
|
|
m_arrChilds[nFirst ^ 1]->Render_Object_Nodes(bNodeCompletelyInFrustum, nRenderMask, passInfo, rendItemSorter);
|
|
}
|
|
|
|
if (m_arrChilds[nFirst ^ 2])
|
|
{
|
|
m_arrChilds[nFirst ^ 2]->Render_Object_Nodes(bNodeCompletelyInFrustum, nRenderMask, passInfo, rendItemSorter);
|
|
}
|
|
|
|
if (m_arrChilds[nFirst ^ 4])
|
|
{
|
|
m_arrChilds[nFirst ^ 4]->Render_Object_Nodes(bNodeCompletelyInFrustum, nRenderMask, passInfo, rendItemSorter);
|
|
}
|
|
|
|
if (m_arrChilds[nFirst ^ 3])
|
|
{
|
|
m_arrChilds[nFirst ^ 3]->Render_Object_Nodes(bNodeCompletelyInFrustum, nRenderMask, passInfo, rendItemSorter);
|
|
}
|
|
|
|
if (m_arrChilds[nFirst ^ 5])
|
|
{
|
|
m_arrChilds[nFirst ^ 5]->Render_Object_Nodes(bNodeCompletelyInFrustum, nRenderMask, passInfo, rendItemSorter);
|
|
}
|
|
|
|
if (m_arrChilds[nFirst ^ 6])
|
|
{
|
|
m_arrChilds[nFirst ^ 6]->Render_Object_Nodes(bNodeCompletelyInFrustum, nRenderMask, passInfo, rendItemSorter);
|
|
}
|
|
|
|
if (m_arrChilds[nFirst ^ 7])
|
|
{
|
|
m_arrChilds[nFirst ^ 7]->Render_Object_Nodes(bNodeCompletelyInFrustum, nRenderMask, passInfo, rendItemSorter);
|
|
}
|
|
}
|
|
|
|
void COctreeNode::CompileObjects()
|
|
{
|
|
FUNCTION_PROFILER_3DENGINE;
|
|
|
|
m_lstCasters.Clear();
|
|
|
|
m_bStaticInstancingIsDirty = true;
|
|
|
|
float fObjMaxViewDistance = 0;
|
|
|
|
size_t numCasters = 0;
|
|
const unsigned int nSkipShadowCastersRndFlags = ERF_HIDDEN | ERF_COLLISION_PROXY | ERF_RAYCAST_PROXY | ERF_STATIC_INSTANCING; // shadow casters with these render flags are ignored
|
|
|
|
for (int l = 0; l < eRNListType_ListsNum; l++)
|
|
{
|
|
for (IRenderNode* pObj = m_arrObjects[l].m_pFirstNode; pObj; pObj = pObj->m_pNext)
|
|
{
|
|
int nFlags = pObj->GetRndFlags();
|
|
|
|
IF (nFlags & nSkipShadowCastersRndFlags, 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
IF (GetCVars()->e_ShadowsPerObject && gEnv->p3DEngine->GetPerObjectShadow(pObj), 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
EERType eRType = pObj->GetRenderNodeType();
|
|
float WSMaxViewDist = pObj->GetMaxViewDist();
|
|
|
|
if (nFlags & ERF_CASTSHADOWMAPS && WSMaxViewDist > fMinShadowCasterViewDist && eRType != eERType_Light)
|
|
{
|
|
++numCasters;
|
|
}
|
|
}
|
|
}
|
|
|
|
m_lstCasters.reserve(numCasters);
|
|
|
|
IObjManager* pObjManager = GetObjManager();
|
|
|
|
// update node
|
|
for (int l = 0; l < eRNListType_ListsNum; l++)
|
|
{
|
|
for (IRenderNode* pObj = m_arrObjects[l].m_pFirstNode, * pNext; pObj; pObj = pNext)
|
|
{
|
|
pNext = pObj->m_pNext;
|
|
|
|
IF (pObj->m_dwRndFlags & ERF_HIDDEN, 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
EERType eRType = pObj->GetRenderNodeType();
|
|
|
|
// update max view distances
|
|
const float fNewMaxViewDist = pObj->GetMaxViewDist();
|
|
pObj->m_fWSMaxViewDist = fNewMaxViewDist;
|
|
|
|
// update REQUIRES_FORWARD_RENDERING flag
|
|
//IF(GetCVars()->e_ShadowsOnAlphaBlend,0)
|
|
{
|
|
pObj->m_nInternalFlags &= ~(IRenderNode::REQUIRES_FORWARD_RENDERING | IRenderNode::REQUIRES_NEAREST_CUBEMAP);
|
|
if (eRType != eERType_Light &&
|
|
eRType != eERType_Cloud &&
|
|
eRType != eERType_FogVolume &&
|
|
eRType != eERType_Decal &&
|
|
eRType != eERType_DistanceCloud)
|
|
{
|
|
if (CMatInfo* pMatInfo = (CMatInfo*)pObj->GetMaterial().get())
|
|
{
|
|
if (pMatInfo->IsForwardRenderingRequired())
|
|
{
|
|
pObj->m_nInternalFlags |= IRenderNode::REQUIRES_FORWARD_RENDERING;
|
|
}
|
|
|
|
if (pMatInfo->IsNearestCubemapRequired())
|
|
{
|
|
pObj->m_nInternalFlags |= IRenderNode::REQUIRES_NEAREST_CUBEMAP;
|
|
}
|
|
}
|
|
|
|
if (eRType == eERType_RenderComponent ||eRType == eERType_StaticMeshRenderComponent || eRType == eERType_DynamicMeshRenderComponent || eRType == eERType_SkinnedMeshRenderComponent)
|
|
{
|
|
int nSlotCount = pObj->GetSlotCount();
|
|
|
|
for (int s = 0; s < nSlotCount; s++)
|
|
{
|
|
if (CMatInfo* pMat = (CMatInfo*)pObj->GetEntitySlotMaterial(s).get())
|
|
{
|
|
if (pMat->IsForwardRenderingRequired())
|
|
{
|
|
pObj->m_nInternalFlags |= IRenderNode::REQUIRES_FORWARD_RENDERING;
|
|
}
|
|
if (pMat->IsNearestCubemapRequired())
|
|
{
|
|
pObj->m_nInternalFlags |= IRenderNode::REQUIRES_NEAREST_CUBEMAP;
|
|
}
|
|
}
|
|
|
|
if (IStatObj* pStatObj = pObj->GetEntityStatObj(s))
|
|
{
|
|
if (CMatInfo* pMat = (CMatInfo*)pStatObj->GetMaterial().get())
|
|
{
|
|
if (pMat->IsForwardRenderingRequired())
|
|
{
|
|
pObj->m_nInternalFlags |= IRenderNode::REQUIRES_FORWARD_RENDERING;
|
|
}
|
|
if (pMat->IsNearestCubemapRequired())
|
|
{
|
|
pObj->m_nInternalFlags |= IRenderNode::REQUIRES_NEAREST_CUBEMAP;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int nFlags = pObj->GetRndFlags();
|
|
|
|
// fill shadow casters list
|
|
const bool bHasPerObjectShadow = GetCVars()->e_ShadowsPerObject && gEnv->p3DEngine->GetPerObjectShadow(pObj);
|
|
if (!(nFlags & nSkipShadowCastersRndFlags) && nFlags & ERF_CASTSHADOWMAPS && fNewMaxViewDist > fMinShadowCasterViewDist &&
|
|
eRType != eERType_Light && !bHasPerObjectShadow)
|
|
{
|
|
COctreeNode* pNode = this;
|
|
while (pNode && !(pNode->m_renderFlags & ERF_CASTSHADOWMAPS))
|
|
{
|
|
pNode->m_renderFlags |= ERF_CASTSHADOWMAPS | ERF_HAS_CASTSHADOWMAPS;
|
|
pNode = pNode->m_pParent;
|
|
}
|
|
|
|
float fMaxCastDist = fNewMaxViewDist * GetCVars()->e_ShadowsCastViewDistRatio;
|
|
m_lstCasters.Add(SCasterInfo(pObj, fMaxCastDist, eRType));
|
|
}
|
|
|
|
fObjMaxViewDistance = max(fObjMaxViewDistance, fNewMaxViewDist);
|
|
}
|
|
}
|
|
|
|
if (fObjMaxViewDistance > m_fObjectsMaxViewDist)
|
|
{
|
|
COctreeNode* pNode = this;
|
|
while (pNode)
|
|
{
|
|
pNode->m_fObjectsMaxViewDist = max(pNode->m_fObjectsMaxViewDist, fObjMaxViewDistance);
|
|
pNode = pNode->m_pParent;
|
|
}
|
|
}
|
|
|
|
const Vec3& sunDir = Get3DEngine()->GetSunDirNormalized();
|
|
m_fpSunDirX = (uint32) (sunDir.x * 63.5f + 63.5f);
|
|
m_fpSunDirZ = (uint32) (sunDir.z * 63.5f + 63.5f);
|
|
m_fpSunDirYs = sunDir.y < 0.0f ? 1 : 0;
|
|
}
|
|
|
|
|
|
bool IsAABBInsideHull(const SPlaneObject* pHullPlanes, int nPlanesNum, const AABB& aabbBox);
|
|
bool IsSphereInsideHull(const SPlaneObject* pHullPlanes, int nPlanesNum, const Sphere& objSphere);
|
|
|
|
void COctreeNode::FillShadowCastersList(bool bNodeCompletellyInFrustum, CDLight* pLight, ShadowMapFrustum* pFr, PodArray<SPlaneObject>* pShadowHull, uint32 nRenderNodeFlags, const SRenderingPassInfo& passInfo)
|
|
{
|
|
if (GetCVars()->e_Objects)
|
|
{
|
|
if (m_renderFlags & ERF_CASTSHADOWMAPS)
|
|
{
|
|
FRAME_PROFILER("COctreeNode::FillShadowMapCastersList", GetSystem(), PROFILE_3DENGINE);
|
|
|
|
_MS_ALIGN(64) ShadowMapFrustumParams params;
|
|
params.pLight = pLight;
|
|
params.pFr = pFr;
|
|
params.pShadowHull = pShadowHull;
|
|
params.passInfo = &passInfo;
|
|
params.vCamPos = passInfo.GetCamera().GetPosition();
|
|
params.bSun = (pLight->m_Flags & DLF_SUN) != 0;
|
|
params.nRenderNodeFlags = nRenderNodeFlags;
|
|
|
|
FillShadowMapCastersList(params, bNodeCompletellyInFrustum);
|
|
}
|
|
}
|
|
}
|
|
|
|
void COctreeNode::FillDepthCubemapRenderList(const AABB& cubemapAABB, const SRenderingPassInfo& passInfo, PodArray<struct IShadowCaster*>* objectsList)
|
|
{
|
|
if (GetCVars()->e_Objects)
|
|
{
|
|
//get objects from this node
|
|
for (IRenderNode* obj = m_arrObjects[eRNListType_Unknown].m_pFirstNode; obj; obj = obj->m_pNext)
|
|
{
|
|
if (cubemapAABB.IsIntersectBox(obj->GetBBox()))
|
|
{
|
|
objectsList->Add(obj);
|
|
}
|
|
}
|
|
|
|
//check child nodes
|
|
for (int i = 0; i <= MAX_NODE_NUM; i++)
|
|
{
|
|
const bool bPrefetch = i < MAX_NODE_NUM && !!m_arrChilds[i + 1];
|
|
|
|
if (m_arrChilds[i])
|
|
{
|
|
m_arrChilds[i]->FillDepthCubemapRenderList(cubemapAABB, passInfo, objectsList);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void COctreeNode::FillShadowMapCastersList(const ShadowMapFrustumParams& params, bool bNodeCompletellyInFrustum)
|
|
{
|
|
if (!bNodeCompletellyInFrustum && !params.pFr->IntersectAABB(m_objectsBox, &bNodeCompletellyInFrustum))
|
|
{
|
|
return;
|
|
}
|
|
|
|
const int frameID = params.passInfo->GetFrameID();
|
|
if (params.bSun && bNodeCompletellyInFrustum)
|
|
{
|
|
nFillShadowCastersSkipFrameId = frameID;
|
|
}
|
|
|
|
if (params.pShadowHull && !IsAABBInsideHull(params.pShadowHull->GetElements(), params.pShadowHull->Count(), m_objectsBox))
|
|
{
|
|
nFillShadowCastersSkipFrameId = frameID;
|
|
return;
|
|
}
|
|
|
|
PrefetchLine(&m_lstCasters, 0);
|
|
|
|
const float fShadowsCastViewDistRatio = GetCVars()->e_ShadowsCastViewDistRatio;
|
|
if (fShadowsCastViewDistRatio != 0.0f)
|
|
{
|
|
float fNodeDistanceSqr = Distance::Point_AABBSq(params.vCamPos, m_objectsBox);
|
|
if (fNodeDistanceSqr > sqr(m_fObjectsMaxViewDist * fShadowsCastViewDistRatio))
|
|
{
|
|
nFillShadowCastersSkipFrameId = frameID;
|
|
return;
|
|
}
|
|
}
|
|
|
|
PrefetchLine(m_lstCasters.begin(), 0);
|
|
PrefetchLine(m_lstCasters.begin(), 128);
|
|
|
|
IRenderNode* pNotCaster = ((CLightEntity*)params.pLight->m_pOwner)->m_pNotCaster;
|
|
|
|
SCasterInfo* pCastersEnd = m_lstCasters.end();
|
|
for (SCasterInfo* pCaster = m_lstCasters.begin(); pCaster < pCastersEnd; pCaster++)
|
|
{
|
|
if (params.bSun && pCaster->nGSMFrameId == frameID && params.pShadowHull)
|
|
{
|
|
continue;
|
|
}
|
|
if (!IsRenderNodeTypeEnabled(pCaster->nRType))
|
|
{
|
|
continue;
|
|
}
|
|
if (pCaster->pNode == NULL || pCaster->pNode == pNotCaster)
|
|
{
|
|
continue;
|
|
}
|
|
if ((pCaster->nRenderNodeFlags & params.nRenderNodeFlags) == 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
float fDistanceSq = Distance::Point_PointSq(params.vCamPos, pCaster->objSphere.center);
|
|
if (fDistanceSq > sqr(pCaster->fMaxCastingDist + pCaster->objSphere.radius))
|
|
{
|
|
pCaster->nGSMFrameId = frameID;
|
|
continue;
|
|
}
|
|
|
|
bool bObjCompletellyInFrustum = bNodeCompletellyInFrustum;
|
|
if (!bObjCompletellyInFrustum && !params.pFr->IntersectAABB(pCaster->objBox, &bObjCompletellyInFrustum))
|
|
{
|
|
continue;
|
|
}
|
|
if (params.bSun && bObjCompletellyInFrustum)
|
|
{
|
|
pCaster->nGSMFrameId = frameID;
|
|
}
|
|
|
|
if (params.bSun && bObjCompletellyInFrustum)
|
|
{
|
|
pCaster->nGSMFrameId = frameID;
|
|
}
|
|
if (params.pShadowHull && !IsSphereInsideHull(params.pShadowHull->GetElements(), params.pShadowHull->Count(), pCaster->objSphere))
|
|
{
|
|
pCaster->nGSMFrameId = frameID;
|
|
continue;
|
|
}
|
|
|
|
if (pCaster->bCanExecuteAsRenderJob)
|
|
{
|
|
Get3DEngine()->CheckCreateRNTmpData(&pCaster->pNode->m_pRNTmpData, pCaster->pNode, *params.passInfo);
|
|
params.pFr->m_jobExecutedCastersList.Add(pCaster->pNode);
|
|
}
|
|
else
|
|
{
|
|
params.pFr->m_castersList.Add(pCaster->pNode);
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i <= MAX_NODE_NUM; i++)
|
|
{
|
|
const bool bPrefetch = i < MAX_NODE_NUM && !!m_arrChilds[i + 1];
|
|
|
|
if (m_arrChilds[i] && (m_arrChilds[i]->m_renderFlags & ERF_CASTSHADOWMAPS) && (!params.bSun || !params.pShadowHull || m_arrChilds[i]->nFillShadowCastersSkipFrameId != frameID))
|
|
{
|
|
m_arrChilds[i]->FillShadowMapCastersList(params, bNodeCompletellyInFrustum);
|
|
}
|
|
}
|
|
}
|
|
|
|
AABB COctreeNode::GetShadowCastersBox(const AABB* pBBox, const Matrix34* pShadowSpaceTransform)
|
|
{
|
|
AABB result(AABB::RESET);
|
|
if (!pBBox || Overlap::AABB_AABB(*pBBox, GetObjectsBBox()))
|
|
{
|
|
for (size_t i = 0; i < m_lstCasters.size(); ++i)
|
|
{
|
|
AABB casterBox = m_lstCasters[i].objBox;
|
|
if (!pBBox || Overlap::AABB_AABB(*pBBox, casterBox))
|
|
{
|
|
if (pShadowSpaceTransform)
|
|
{
|
|
casterBox = AABB::CreateTransformedAABB(*pShadowSpaceTransform, casterBox);
|
|
}
|
|
|
|
result.Add(casterBox);
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < 8; i++)
|
|
{
|
|
if (m_arrChilds[i])
|
|
{
|
|
result.Add(m_arrChilds[i]->GetShadowCastersBox(pBBox, pShadowSpaceTransform));
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
COctreeNode* COctreeNode::FindNodeContainingBox(const AABB& objBox)
|
|
{
|
|
{
|
|
const AABB& nodeBox = GetNodeBox();
|
|
if (!nodeBox.IsContainSphere(objBox.min, -0.01f) || !nodeBox.IsContainSphere(objBox.max, -0.01f))
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < 8; i++)
|
|
{
|
|
if (m_arrChilds[i])
|
|
{
|
|
if (COctreeNode* pFoundNode = m_arrChilds[i]->FindNodeContainingBox(objBox))
|
|
{
|
|
return pFoundNode;
|
|
}
|
|
}
|
|
}
|
|
|
|
return this;
|
|
}
|
|
|
|
void COctreeNode::MoveObjectsIntoList(PodArray<SRNInfo>* plstResultEntities, const AABB* pAreaBox,
|
|
bool bRemoveObjects, bool bSkipDecals, bool bSkip_ERF_NO_DECALNODE_DECALS, bool bSkipDynamicObjects,
|
|
EERType eRNType)
|
|
{
|
|
FUNCTION_PROFILER_3DENGINE;
|
|
|
|
if (pAreaBox && !Overlap::AABB_AABB(m_objectsBox, *pAreaBox))
|
|
{
|
|
return;
|
|
}
|
|
|
|
for (int l = 0; l < eRNListType_ListsNum; l++)
|
|
{
|
|
for (IRenderNode* pObj = m_arrObjects[l].m_pFirstNode, * pNext; pObj; pObj = pNext)
|
|
{
|
|
pNext = pObj->m_pNext;
|
|
|
|
if (eRNType < eERType_TypesNum && pObj->GetRenderNodeType() != eRNType)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (bSkipDecals && pObj->GetRenderNodeType() == eERType_Decal)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (bSkip_ERF_NO_DECALNODE_DECALS && pObj->GetRndFlags() & ERF_NO_DECALNODE_DECALS)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (bSkipDynamicObjects)
|
|
{
|
|
EERType eRType = pObj->GetRenderNodeType();
|
|
|
|
if (eRType == eERType_RenderComponent || eRType == eERType_DynamicMeshRenderComponent || eRType == eERType_SkinnedMeshRenderComponent)
|
|
{
|
|
if (pObj->IsMovableByGame())
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
else if (
|
|
eRType != eERType_StaticMeshRenderComponent)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (pAreaBox && !Overlap::AABB_AABB(pObj->GetBBox(), *pAreaBox))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (bRemoveObjects)
|
|
{
|
|
UnlinkObject(pObj);
|
|
|
|
CompileObjects();
|
|
}
|
|
|
|
plstResultEntities->Add(pObj);
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < 8; i++)
|
|
{
|
|
if (m_arrChilds[i])
|
|
{
|
|
m_arrChilds[i]->MoveObjectsIntoList(plstResultEntities, pAreaBox, bRemoveObjects, bSkipDecals, bSkip_ERF_NO_DECALNODE_DECALS, bSkipDynamicObjects, eRNType);
|
|
}
|
|
}
|
|
}
|
|
|
|
void COctreeNode::DeleteObjectsByFlag(int nRndFlag)
|
|
{
|
|
FUNCTION_PROFILER_3DENGINE;
|
|
for (int l = 0; l < eRNListType_ListsNum; l++)
|
|
{
|
|
for (IRenderNode* pObj = m_arrObjects[l].m_pFirstNode, * pNext; pObj; pObj = pNext)
|
|
{
|
|
pNext = pObj->m_pNext;
|
|
|
|
if (pObj->GetRndFlags() & nRndFlag)
|
|
{
|
|
DeleteObject(pObj);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < 8; i++)
|
|
{
|
|
if (m_arrChilds[i])
|
|
{
|
|
m_arrChilds[i]->DeleteObjectsByFlag(nRndFlag);
|
|
}
|
|
}
|
|
}
|
|
|
|
void COctreeNode::UnregisterEngineObjectsInArea(const SHotUpdateInfo* pExportInfo, PodArray<IRenderNode*>& arrUnregisteredObjects, bool bOnlyEngineObjects)
|
|
{
|
|
FUNCTION_PROFILER_3DENGINE;
|
|
|
|
const AABB* pAreaBox = (pExportInfo && !pExportInfo->areaBox.IsReset()) ? &pExportInfo->areaBox : NULL;
|
|
|
|
{
|
|
const AABB& nodeBox = GetNodeBox();
|
|
if (pAreaBox && !Overlap::AABB_AABB(nodeBox, *pAreaBox))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
uint32 nObjTypeMask = pExportInfo ? pExportInfo->nObjTypeMask : (uint32) ~0;
|
|
|
|
for (int l = 0; l < eRNListType_ListsNum; l++)
|
|
{
|
|
for (IRenderNode* pObj = m_arrObjects[l].m_pFirstNode, * pNext; pObj; pObj = pNext)
|
|
{
|
|
pNext = pObj->m_pNext;
|
|
|
|
EERType eType = pObj->GetRenderNodeType();
|
|
|
|
if (bOnlyEngineObjects)
|
|
{
|
|
if (!(nObjTypeMask & (1 << eType)))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (eType == eERType_Decal ||
|
|
eType == eERType_WaterVolume ||
|
|
eType == eERType_DistanceCloud )
|
|
{
|
|
Get3DEngine()->UnRegisterEntityAsJob(pObj);
|
|
arrUnregisteredObjects.Add(pObj);
|
|
CompileObjects();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Get3DEngine()->UnRegisterEntityAsJob(pObj);
|
|
arrUnregisteredObjects.Add(pObj);
|
|
CompileObjects();
|
|
}
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < 8; i++)
|
|
{
|
|
if (m_arrChilds[i])
|
|
{
|
|
m_arrChilds[i]->UnregisterEngineObjectsInArea(pExportInfo, arrUnregisteredObjects, bOnlyEngineObjects);
|
|
}
|
|
}
|
|
}
|
|
|
|
int COctreeNode::GetObjectsCount(EOcTeeNodeListType eListType)
|
|
{
|
|
int nCount = 0;
|
|
|
|
switch (eListType)
|
|
{
|
|
case eMain:
|
|
for (int l = 0; l < eRNListType_ListsNum; l++)
|
|
{
|
|
for (IRenderNode* pObj = m_arrObjects[l].m_pFirstNode; pObj; pObj = pObj->m_pNext)
|
|
{
|
|
nCount++;
|
|
}
|
|
}
|
|
break;
|
|
case eCasters:
|
|
for (int l = 0; l < eRNListType_ListsNum; l++)
|
|
{
|
|
for (IRenderNode* pObj = m_arrObjects[l].m_pFirstNode; pObj; pObj = pObj->m_pNext)
|
|
{
|
|
if (pObj->GetRndFlags() & ERF_CASTSHADOWMAPS)
|
|
{
|
|
nCount++;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
for (int i = 0; i < 8; i++)
|
|
{
|
|
if (m_arrChilds[i])
|
|
{
|
|
nCount += m_arrChilds[i]->GetObjectsCount(eListType);
|
|
}
|
|
}
|
|
|
|
return nCount;
|
|
}
|
|
|
|
|
|
void COctreeNode::GetMemoryUsage(ICrySizer* pSizer) const
|
|
{
|
|
for (int l = 0; l < eRNListType_ListsNum; l++)
|
|
{
|
|
for (IRenderNode* pObj = m_arrObjects[l].m_pFirstNode; pObj; pObj = pObj->m_pNext)
|
|
{
|
|
EERType eType = pObj->GetRenderNodeType();
|
|
pObj->GetMemoryUsage(pSizer);
|
|
}
|
|
}
|
|
|
|
{
|
|
SIZER_COMPONENT_NAME(pSizer, "ObjLists");
|
|
|
|
pSizer->AddObject(m_lstCasters);
|
|
}
|
|
|
|
for (int i = 0; i < 8; i++)
|
|
{
|
|
if (m_arrChilds[i])
|
|
{
|
|
m_arrChilds[i]->GetMemoryUsage(pSizer);
|
|
}
|
|
}
|
|
|
|
if (pSizer)
|
|
{
|
|
pSizer->AddObject(this, sizeof(*this));
|
|
}
|
|
}
|
|
|
|
void C3DEngine::GetObjectsByTypeGlobal(PodArray<IRenderNode*>& lstObjects, EERType objType, const AABB* pBBox, ObjectTreeQueryFilterCallback filterCallback)
|
|
{
|
|
if (Get3DEngine()->IsObjectTreeReady())
|
|
{
|
|
Get3DEngine()->GetObjectTree()->GetObjectsByType(lstObjects, objType, pBBox, filterCallback);
|
|
}
|
|
}
|
|
|
|
void C3DEngine::MoveObjectsIntoListGlobal(PodArray<SRNInfo>* plstResultEntities, const AABB* pAreaBox,
|
|
bool bRemoveObjects, bool bSkipDecals, bool bSkip_ERF_NO_DECALNODE_DECALS, bool bSkipDynamicObjects,
|
|
EERType eRNType)
|
|
{
|
|
if (Get3DEngine()->IsObjectTreeReady())
|
|
{
|
|
Get3DEngine()->GetObjectTree()->MoveObjectsIntoList(plstResultEntities, pAreaBox, bRemoveObjects, bSkipDecals, bSkip_ERF_NO_DECALNODE_DECALS, bSkipDynamicObjects, eRNType);
|
|
}
|
|
}
|
|
|
|
void COctreeNode::ActivateObjectsLayer(uint16 nLayerId, bool bActivate, bool bPhys, IGeneralMemoryHeap* pHeap)
|
|
{
|
|
for (IRenderNode* pObj = m_arrObjects[eRNListType_DecalsAndRoads].m_pFirstNode; pObj; pObj = pObj->m_pNext)
|
|
{
|
|
EERType eType = pObj->GetRenderNodeType();
|
|
|
|
if (eType == eERType_Decal)
|
|
{
|
|
CDecalRenderNode* pDecal = (CDecalRenderNode*)pObj;
|
|
if (pDecal->GetLayerId() == nLayerId || nLayerId == uint16(~0))
|
|
{
|
|
pDecal->SetRndFlags(ERF_HIDDEN, !bActivate);
|
|
|
|
if (bActivate)
|
|
{
|
|
pDecal->RequestUpdate();
|
|
}
|
|
else
|
|
{
|
|
pDecal->DeleteDecal();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for (IRenderNode* pObj = m_arrObjects[eRNListType_Unknown].m_pFirstNode; pObj; pObj = pObj->m_pNext)
|
|
{
|
|
if (pObj->GetRenderNodeType() == eERType_WaterVolume)
|
|
{
|
|
CWaterVolumeRenderNode* pWatVol = (CWaterVolumeRenderNode*)pObj;
|
|
if (pWatVol->GetLayerId() == nLayerId || nLayerId == uint16(~0))
|
|
{
|
|
pWatVol->SetRndFlags(ERF_HIDDEN, !bActivate);
|
|
|
|
if (GetCVars()->e_ObjectLayersActivationPhysics)
|
|
{
|
|
if (bActivate && bPhys)
|
|
{
|
|
pWatVol->Physicalize();
|
|
}
|
|
else
|
|
{
|
|
pWatVol->Dephysicalize();
|
|
}
|
|
}
|
|
else if (!bPhys)
|
|
{
|
|
pWatVol->Dephysicalize();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pObj->GetRenderNodeType() == eERType_DistanceCloud)
|
|
{
|
|
CDistanceCloudRenderNode* pCloud = (CDistanceCloudRenderNode*)pObj;
|
|
if (pCloud->GetLayerId() == nLayerId || nLayerId == uint16(~0))
|
|
{
|
|
pCloud->SetRndFlags(ERF_HIDDEN, !bActivate);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < 8; i++)
|
|
{
|
|
if (m_arrChilds[i])
|
|
{
|
|
m_arrChilds[i]->ActivateObjectsLayer(nLayerId, bActivate, bPhys, pHeap);
|
|
}
|
|
}
|
|
}
|
|
|
|
void COctreeNode::GetLayerMemoryUsage(uint16 nLayerId, ICrySizer* pSizer, int* pNumBrushes, int* pNumDecals)
|
|
{
|
|
for (IRenderNode* pObj = m_arrObjects[eRNListType_DecalsAndRoads].m_pFirstNode; pObj; pObj = pObj->m_pNext)
|
|
{
|
|
EERType eType = pObj->GetRenderNodeType();
|
|
|
|
if (eType == eERType_Decal)
|
|
{
|
|
CDecalRenderNode* pDecal = (CDecalRenderNode*)pObj;
|
|
if (pDecal->GetLayerId() == nLayerId || nLayerId == uint16(~0))
|
|
{
|
|
pDecal->GetMemoryUsage(pSizer);
|
|
if (pNumDecals)
|
|
{
|
|
(*pNumDecals)++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < 8; i++)
|
|
{
|
|
if (m_arrChilds[i])
|
|
{
|
|
m_arrChilds[i]->GetLayerMemoryUsage(nLayerId, pSizer, pNumBrushes, pNumDecals);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void COctreeNode::GetObjects(PodArray<IRenderNode*>& lstObjects, const AABB* pBBox)
|
|
{
|
|
if (pBBox && !Overlap::AABB_AABB(*pBBox, GetObjectsBBox()))
|
|
{
|
|
return;
|
|
}
|
|
|
|
unsigned int nCurrentObject(eRNListType_First);
|
|
for (nCurrentObject = eRNListType_First; nCurrentObject < eRNListType_ListsNum; ++nCurrentObject)
|
|
{
|
|
for (IRenderNode* pObj = m_arrObjects[nCurrentObject].m_pFirstNode; pObj; pObj = pObj->m_pNext)
|
|
{
|
|
if (!pBBox || Overlap::AABB_AABB(*pBBox, pObj->GetBBox()))
|
|
{
|
|
lstObjects.Add(pObj);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < 8; i++)
|
|
{
|
|
if (m_arrChilds[i])
|
|
{
|
|
m_arrChilds[i]->GetObjects(lstObjects, pBBox);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool COctreeNode::GetShadowCastersTimeSliced(IRenderNode* pIgnoreNode, ShadowMapFrustum* pFrustum, int renderNodeExcludeFlags, int& totalRemainingNodes, int nCurLevel, const SRenderingPassInfo& passInfo)
|
|
{
|
|
assert(pFrustum->pShadowCacheData);
|
|
|
|
if (totalRemainingNodes <= 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (!pFrustum->pShadowCacheData->mOctreePathNodeProcessed[nCurLevel])
|
|
{
|
|
if (pFrustum->aabbCasters.IsReset() || Overlap::AABB_AABB(pFrustum->aabbCasters, GetObjectsBBox()))
|
|
{
|
|
for (int l = 0; l < eRNListType_ListsNum; l++)
|
|
{
|
|
for (IRenderNode* pNode = m_arrObjects[l].m_pFirstNode; pNode; pNode = pNode->m_pNext)
|
|
{
|
|
if (!IsRenderNodeTypeEnabled(pNode->GetRenderNodeType()))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (pNode == pIgnoreNode)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
const int nFlags = pNode->GetRndFlags();
|
|
if (nFlags & (ERF_HIDDEN | ERF_COLLISION_PROXY | ERF_RAYCAST_PROXY | renderNodeExcludeFlags))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Ignore ERF_CASTSHADOWMAPS for ambient occlusion casters
|
|
if (pFrustum->m_eFrustumType != ShadowMapFrustum::e_HeightMapAO && (pNode->GetRndFlags() & ERF_CASTSHADOWMAPS) == 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (pFrustum->pShadowCacheData->mProcessedCasters.find(pNode) != pFrustum->pShadowCacheData->mProcessedCasters.end())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
AABB objBox = pNode->GetBBox();
|
|
const float fDistanceSq = Distance::Point_PointSq(passInfo.GetCamera().GetPosition(), objBox.GetCenter());
|
|
const float fMaxDist = pNode->GetMaxViewDist() * GetCVars()->e_ShadowsCastViewDistRatio + objBox.GetRadius();
|
|
|
|
if (fDistanceSq > sqr(fMaxDist))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// find closest loaded lod
|
|
for (int nSlot = 0; nSlot < pNode->GetSlotCount(); ++nSlot)
|
|
{
|
|
bool bCanRender = false;
|
|
|
|
if (IStatObj* pStatObj = pNode->GetEntityStatObj(nSlot))
|
|
{
|
|
for (int i = 0; i < MAX_STATOBJ_LODS_NUM; ++i)
|
|
{
|
|
IStatObj* pLod = pStatObj->GetLodObject(i);
|
|
|
|
if (pLod && pLod->m_eStreamingStatus == ecss_Ready)
|
|
{
|
|
bCanRender = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bCanRender)
|
|
{
|
|
if (pNode->CanExecuteRenderAsJob())
|
|
{
|
|
Get3DEngine()->CheckCreateRNTmpData(&pNode->m_pRNTmpData, pNode, passInfo);
|
|
pFrustum->m_jobExecutedCastersList.Add(pNode);
|
|
}
|
|
else
|
|
{
|
|
pFrustum->m_castersList.Add(pNode);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pFrustum->pShadowCacheData->mOctreePathNodeProcessed[nCurLevel] = true;
|
|
if (!pFrustum->m_castersList.IsEmpty() || !pFrustum->m_jobExecutedCastersList.IsEmpty())
|
|
{
|
|
--totalRemainingNodes;
|
|
}
|
|
}
|
|
|
|
|
|
for (int i = pFrustum->pShadowCacheData->mOctreePath[nCurLevel]; i < 8; ++i)
|
|
{
|
|
if (m_arrChilds[i] && (m_arrChilds[i]->m_renderFlags & ERF_CASTSHADOWMAPS))
|
|
{
|
|
bool bDone = m_arrChilds[i]->GetShadowCastersTimeSliced(pIgnoreNode, pFrustum, renderNodeExcludeFlags, totalRemainingNodes, nCurLevel + 1, passInfo);
|
|
|
|
if (!bDone)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
pFrustum->pShadowCacheData->mOctreePath[nCurLevel] = i;
|
|
}
|
|
|
|
// this subtree is fully processed: reset traversal state
|
|
pFrustum->pShadowCacheData->mOctreePath[nCurLevel] = 0;
|
|
pFrustum->pShadowCacheData->mOctreePathNodeProcessed[nCurLevel] = 0;
|
|
return true;
|
|
}
|
|
|
|
bool COctreeNode::IsObjectTypeInTheBox(EERType objType, const AABB& WSBBox)
|
|
{
|
|
if (!Overlap::AABB_AABB(WSBBox, GetObjectsBBox()))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
ERNListType eListType = IRenderNode::GetRenderNodeListId(objType);
|
|
|
|
for (IRenderNode* pObj = m_arrObjects[eListType].m_pFirstNode; pObj; pObj = pObj->m_pNext)
|
|
{
|
|
if (pObj->GetRenderNodeType() == objType)
|
|
{
|
|
if (Overlap::AABB_AABB(WSBBox, pObj->GetBBox()))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < 8; i++)
|
|
{
|
|
if (m_arrChilds[i])
|
|
{
|
|
if (m_arrChilds[i]->IsObjectTypeInTheBox(objType, WSBBox))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
#ifdef SUPPORT_TERRAIN_AO_PRE_COMPUTATIONS
|
|
|
|
bool COctreeNode::RayObjectsIntersection2D(Vec3 vStart, Vec3 vEnd, Vec3& vClosestHitPoint, float& fClosestHitDistance, EERType eERType)
|
|
{
|
|
FUNCTION_PROFILER_3DENGINE;
|
|
|
|
// Vec3 vBoxHitPoint;
|
|
// if(!Intersect::Ray_AABB(Ray(vStart, vEnd-vStart), m_objectsBox, vBoxHitPoint))
|
|
// return false;
|
|
|
|
if (vStart.x > m_objectsBox.max.x || vStart.y > m_objectsBox.max.y ||
|
|
vStart.x < m_objectsBox.min.x || vStart.y < m_objectsBox.min.y)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const bool oceanEnabled = OceanToggle::IsActive() ? OceanRequest::OceanIsEnabled() : true;
|
|
float fOceanLevel = WATER_LEVEL_UNKNOWN;
|
|
if (OceanToggle::IsActive())
|
|
{
|
|
fOceanLevel = OceanRequest::GetOceanLevel();
|
|
}
|
|
else
|
|
{
|
|
fOceanLevel = m_pOcean ? m_pOcean->GetWaterLevel() : WATER_LEVEL_UNKNOWN;
|
|
}
|
|
|
|
ERNListType eListType = IRenderNode::GetRenderNodeListId(eERType);
|
|
|
|
for (IRenderNode* pObj = m_arrObjects[eListType].m_pFirstNode; pObj; pObj = pObj->m_pNext)
|
|
{
|
|
uint32 dwFlags = pObj->GetRndFlags();
|
|
|
|
if (dwFlags & ERF_HIDDEN || !(dwFlags & ERF_CASTSHADOWMAPS) || dwFlags & ERF_COLLISION_PROXY)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (pObj->GetRenderNodeType() != eERType)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// if(!Intersect::Ray_AABB(Ray(vStart, vEnd-vStart), pObj->GetBBox(), vBoxHitPoint))
|
|
// continue;
|
|
|
|
const AABB& objBox = pObj->GetBBox();
|
|
|
|
if ((objBox.max.z - objBox.min.z) < 2.f)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (oceanEnabled && objBox.max.z < fOceanLevel)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (vStart.x > objBox.max.x || vStart.y > objBox.max.y || vStart.x < objBox.min.x || vStart.y < objBox.min.y)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
Matrix34A objMatrix;
|
|
CStatObj* pStatObj = (CStatObj*)pObj->GetEntityStatObj(0, 0, &objMatrix);
|
|
|
|
if (pStatObj->GetOcclusionAmount() < 0.32f)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
{
|
|
if (pStatObj->m_nFlags & STATIC_OBJECT_HIDDEN)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
Matrix34 matInv = objMatrix.GetInverted();
|
|
Vec3 vOSStart = matInv.TransformPoint(vStart);
|
|
Vec3 vOSEnd = matInv.TransformPoint(vEnd);
|
|
Vec3 vOSHitPoint(0, 0, 0), vOSHitNorm(0, 0, 0);
|
|
|
|
Vec3 vBoxHitPoint;
|
|
if (!Intersect::Ray_AABB(Ray(vOSStart, vOSEnd - vOSStart), pStatObj->GetAABB(), vBoxHitPoint))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
vOSHitPoint = vOSStart;
|
|
vOSHitPoint.z = pStatObj->GetObjectHeight(vOSStart.x, vOSStart.y);
|
|
|
|
if (vOSHitPoint.z != 0)
|
|
{
|
|
Vec3 vHitPoint = objMatrix.TransformPoint(vOSHitPoint);
|
|
float fDist = vHitPoint.GetDistance(vStart);
|
|
if (fDist < fClosestHitDistance)
|
|
{
|
|
fClosestHitDistance = fDist;
|
|
vClosestHitPoint = vHitPoint;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < 8; i++)
|
|
{
|
|
if (m_arrChilds[i])
|
|
{
|
|
m_arrChilds[i]->RayObjectsIntersection2D(vStart, vEnd, vClosestHitPoint, fClosestHitDistance, eERType);
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
#endif
|
|
|
|
void COctreeNode::GenerateStatObjAndMatTables(std::vector<IStatObj*>* pStatObjTable, std::vector<_smart_ptr<IMaterial> >* pMatTable, std::vector<IStatInstGroup*>* pStatInstGroupTable, SHotUpdateInfo* pExportInfo)
|
|
{
|
|
//if eERType number is changed, have to check this code.
|
|
COMPILE_TIME_ASSERT(eERType_TypesNum == 28);
|
|
|
|
AABB* pBox = (pExportInfo && !pExportInfo->areaBox.IsReset()) ? &pExportInfo->areaBox : NULL;
|
|
|
|
if (pBox && !Overlap::AABB_AABB(GetNodeBox(), *pBox))
|
|
{
|
|
return;
|
|
}
|
|
|
|
uint32 nObjTypeMask = pExportInfo ? pExportInfo->nObjTypeMask : (uint32) ~0;
|
|
|
|
for (int l = 0; l < eRNListType_ListsNum; l++)
|
|
{
|
|
for (IRenderNode* pObj = m_arrObjects[l].m_pFirstNode; pObj; pObj = pObj->m_pNext)
|
|
{
|
|
EERType eType = pObj->GetRenderNodeType();
|
|
|
|
if (!(nObjTypeMask & (1 << eType)))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//Add static meshes that have static transforms to the
|
|
//static object table.
|
|
if (eType == eERType_StaticMeshRenderComponent)
|
|
{
|
|
if (CObjManager::GetItemId<IStatObj>(pStatObjTable, pObj->GetEntityStatObj(), false) < 0)
|
|
{
|
|
pStatObjTable->push_back(pObj->GetEntityStatObj());
|
|
}
|
|
}
|
|
|
|
if (eType == eERType_Decal ||
|
|
eType == eERType_WaterVolume ||
|
|
eType == eERType_DistanceCloud ||
|
|
eType == eERType_StaticMeshRenderComponent)
|
|
{
|
|
if (CObjManager::GetItemId(pMatTable, pObj->GetMaterial(), false) < 0)
|
|
{
|
|
pMatTable->push_back(pObj->GetMaterial());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < 8; i++)
|
|
{
|
|
if (m_arrChilds[i])
|
|
{
|
|
m_arrChilds[i]->GenerateStatObjAndMatTables(pStatObjTable, pMatTable, pStatInstGroupTable, pExportInfo);
|
|
}
|
|
}
|
|
}
|
|
|
|
int COctreeNode::Cmp_OctreeNodeSize(const void* v1, const void* v2)
|
|
{
|
|
COctreeNode* pNode1 = *((COctreeNode**)v1);
|
|
COctreeNode* pNode2 = *((COctreeNode**)v2);
|
|
|
|
if (pNode1->GetNodeRadius2() > pNode2->GetNodeRadius2())
|
|
{
|
|
return +1;
|
|
}
|
|
if (pNode1->GetNodeRadius2() < pNode2->GetNodeRadius2())
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
COctreeNode* COctreeNode::FindChildFor([[maybe_unused]] IRenderNode* pObj, [[maybe_unused]] const AABB& objBox, [[maybe_unused]] const float fObjRadius, const Vec3& vObjCenter)
|
|
{
|
|
int nChildId =
|
|
((vObjCenter.x > m_vNodeCenter.x) ? 4 : 0) |
|
|
((vObjCenter.y > m_vNodeCenter.y) ? 2 : 0) |
|
|
((vObjCenter.z > m_vNodeCenter.z) ? 1 : 0);
|
|
|
|
if (!m_arrChilds[nChildId])
|
|
{
|
|
m_arrChilds[nChildId] = COctreeNode::Create(m_nSID, GetChildBBox(nChildId), m_pVisArea, this);
|
|
}
|
|
|
|
return m_arrChilds[nChildId];
|
|
}
|
|
|
|
|
|
bool COctreeNode::HasChildNodes()
|
|
{
|
|
if (!m_arrChilds[0] && !m_arrChilds[1] && !m_arrChilds[2] && !m_arrChilds[3])
|
|
{
|
|
if (!m_arrChilds[4] && !m_arrChilds[5] && !m_arrChilds[6] && !m_arrChilds[7])
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
int COctreeNode::CountChildNodes()
|
|
{
|
|
return
|
|
(m_arrChilds[0] != 0) +
|
|
(m_arrChilds[1] != 0) +
|
|
(m_arrChilds[2] != 0) +
|
|
(m_arrChilds[3] != 0) +
|
|
(m_arrChilds[4] != 0) +
|
|
(m_arrChilds[5] != 0) +
|
|
(m_arrChilds[6] != 0) +
|
|
(m_arrChilds[7] != 0);
|
|
}
|
|
|
|
void COctreeNode::ReleaseEmptyNodes()
|
|
{
|
|
FUNCTION_PROFILER_3DENGINE;
|
|
|
|
if (!m_arrEmptyNodes.Count())
|
|
{
|
|
return;
|
|
}
|
|
|
|
// sort childs first
|
|
qsort(m_arrEmptyNodes.GetElements(), m_arrEmptyNodes.Count(), sizeof(m_arrEmptyNodes[0]), Cmp_OctreeNodeSize);
|
|
|
|
int nInitCount = m_arrEmptyNodes.Count();
|
|
|
|
for (int i = 0; i < nInitCount && m_arrEmptyNodes.Count(); i++)
|
|
{
|
|
COctreeNode* pNode = m_arrEmptyNodes[0];
|
|
|
|
if (pNode && pNode->IsEmpty())
|
|
{
|
|
COctreeNode* pParent = pNode->m_pParent;
|
|
|
|
// unregister in parent
|
|
for (int n = 0; n < 8; n++)
|
|
{
|
|
if (pParent->m_arrChilds[n] == pNode)
|
|
{
|
|
pParent->m_arrChilds[n] = NULL;
|
|
}
|
|
}
|
|
|
|
delete pNode;
|
|
|
|
// request parent validation
|
|
if (pParent && pParent->IsEmpty() && m_arrEmptyNodes.Find(pParent) < 0)
|
|
{
|
|
m_arrEmptyNodes.Add(pParent);
|
|
}
|
|
}
|
|
|
|
// remove from list
|
|
m_arrEmptyNodes.Delete(pNode);
|
|
}
|
|
}
|
|
|
|
void COctreeNode::StaticReset()
|
|
{
|
|
ReleaseEmptyNodes();
|
|
stl::free_container(m_arrEmptyNodes);
|
|
}
|
|
|
|
static float Distance_PrecacheCam_AABBSq(const SObjManPrecacheCamera& a, const AABB& b)
|
|
{
|
|
float d2 = 0.0f;
|
|
|
|
if (a.bbox.max.x < b.min.x)
|
|
{
|
|
d2 += sqr(b.min.x - a.bbox.max.x);
|
|
}
|
|
if (b.max.x < a.bbox.min.x)
|
|
{
|
|
d2 += sqr(a.bbox.min.x - b.max.x);
|
|
}
|
|
if (a.bbox.max.y < b.min.y)
|
|
{
|
|
d2 += sqr(b.min.y - a.bbox.max.y);
|
|
}
|
|
if (b.max.y < a.bbox.min.y)
|
|
{
|
|
d2 += sqr(a.bbox.min.y - b.max.y);
|
|
}
|
|
if (a.bbox.max.z < b.min.z)
|
|
{
|
|
d2 += sqr(b.min.z - a.bbox.max.z);
|
|
}
|
|
if (b.max.z < a.bbox.min.z)
|
|
{
|
|
d2 += sqr(a.bbox.min.z - b.max.z);
|
|
}
|
|
|
|
return d2;
|
|
}
|
|
|
|
bool COctreeNode::UpdateStreamingPriority(PodArray<COctreeNode*>& arrRecursion, float fMinDist, float fMaxDist, bool bFullUpdate, const SObjManPrecacheCamera* pPrecacheCams, size_t nPrecacheCams, const SRenderingPassInfo& passInfo)
|
|
{
|
|
// FUNCTION_PROFILER_3DENGINE;
|
|
|
|
// Select the minimum distance to the node
|
|
float fNodeDistanceSq = Distance_PrecacheCam_AABBSq(pPrecacheCams[0], m_objectsBox);
|
|
for (size_t iPrecacheCam = 1; iPrecacheCam < nPrecacheCams; ++iPrecacheCam)
|
|
{
|
|
float fPcNodeDistanceSq = Distance_PrecacheCam_AABBSq(pPrecacheCams[iPrecacheCam], m_objectsBox);
|
|
fNodeDistanceSq = min(fNodeDistanceSq, fPcNodeDistanceSq);
|
|
}
|
|
float fNodeDistance = sqrt_tpl(fNodeDistanceSq);
|
|
|
|
if (passInfo.GetCamera().IsAABBVisible_E(GetNodeBox()))
|
|
{
|
|
fNodeDistance *= passInfo.GetZoomFactor();
|
|
}
|
|
|
|
const float fPredictionDistanceFar = GetFloatCVar(e_StreamPredictionDistanceFar);
|
|
|
|
if (fNodeDistance > min(m_fObjectsMaxViewDist, fMaxDist) + fPredictionDistanceFar)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
AABB objBox;
|
|
|
|
const bool bEnablePerNodeDistance = GetCVars()->e_StreamCgfUpdatePerNodeDistance > 0;
|
|
CVisArea* pRoot0 = GetVisAreaManager() ? GetVisAreaManager()->GetCurVisArea() : NULL;
|
|
|
|
float fMinDistSq = fMinDist * fMinDist;
|
|
|
|
PREFAST_SUPPRESS_WARNING(6255)
|
|
float* pfMinVisAreaDistSq = (float*)alloca(sizeof(float) * nPrecacheCams);
|
|
|
|
for (size_t iPrecacheCam = 0; iPrecacheCam < nPrecacheCams; ++iPrecacheCam)
|
|
{
|
|
float fMinVisAreaDist = 0.0f;
|
|
|
|
if (pRoot0)
|
|
{
|
|
// search from camera to entity visarea or outdoor
|
|
AABB aabbCam = pPrecacheCams[iPrecacheCam].bbox;
|
|
float fResDist = 10000.0f;
|
|
if (pRoot0->GetDistanceThruVisAreas(aabbCam, m_pVisArea, m_objectsBox, bFullUpdate ? 2 : GetCVars()->e_StreamPredictionMaxVisAreaRecursion, fResDist))
|
|
{
|
|
fMinVisAreaDist = fResDist;
|
|
}
|
|
}
|
|
else if (m_pVisArea)
|
|
{
|
|
// search from entity to outdoor
|
|
AABB aabbCam = pPrecacheCams[iPrecacheCam].bbox;
|
|
float fResDist = 10000.0f;
|
|
if (m_pVisArea->GetDistanceThruVisAreas(m_objectsBox, NULL, aabbCam, bFullUpdate ? 2 : GetCVars()->e_StreamPredictionMaxVisAreaRecursion, fResDist))
|
|
{
|
|
fMinVisAreaDist = fResDist;
|
|
}
|
|
}
|
|
|
|
pfMinVisAreaDistSq[iPrecacheCam] = fMinVisAreaDist * fMinVisAreaDist;
|
|
}
|
|
|
|
for (int l = 0; l < eRNListType_ListsNum; l++)
|
|
{
|
|
for (IRenderNode* pObj = m_arrObjects[l].m_pFirstNode; pObj; pObj = pObj->m_pNext)
|
|
{
|
|
if (pObj->m_pNext)
|
|
{
|
|
cryPrefetchT0SSE(pObj->m_pNext);
|
|
}
|
|
|
|
|
|
IF (pObj->m_dwRndFlags & ERF_HIDDEN, 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
#ifdef _DEBUG
|
|
const char* szName = pObj->GetName();
|
|
const char* szClassName = pObj->GetEntityClassName();
|
|
|
|
if (pObj->GetRndFlags() & ERF_SELECTED)
|
|
{
|
|
int selected = 1;
|
|
}
|
|
#endif // _DEBUG
|
|
|
|
pObj->FillBBox(objBox);
|
|
|
|
// stream more in zoom mode if in frustum
|
|
float fZoomFactorSq = passInfo.GetCamera().IsAABBVisible_E(objBox)
|
|
? passInfo.GetZoomFactor() * passInfo.GetZoomFactor()
|
|
: 1.0f;
|
|
|
|
for (size_t iPrecacheCam = 0; iPrecacheCam < nPrecacheCams; ++iPrecacheCam)
|
|
{
|
|
const Vec3& pcPosition = pPrecacheCams[iPrecacheCam].vPosition;
|
|
|
|
float fEntDistanceSq = Distance_PrecacheCam_AABBSq(pPrecacheCams[iPrecacheCam], objBox);
|
|
fEntDistanceSq = max(fEntDistanceSq, fMinDistSq);
|
|
fEntDistanceSq *= fZoomFactorSq;
|
|
fEntDistanceSq = max(fEntDistanceSq, pfMinVisAreaDistSq[iPrecacheCam]);
|
|
|
|
float fMaxDistComb = min(pObj->m_fWSMaxViewDist, fMaxDist) + fPredictionDistanceFar;
|
|
float fMaxDistCombSq = fMaxDistComb * fMaxDistComb;
|
|
|
|
if (/*fMinDistSq <= fEntDistanceSq &&*/ fEntDistanceSq < fMaxDistCombSq)
|
|
{
|
|
float fEntDistance = sqrt_tpl(fEntDistanceSq);
|
|
assert(fEntDistance >= 0 && _finite(fEntDistance));
|
|
|
|
float fDist = fEntDistance;
|
|
if (!bFullUpdate && fEntDistance < fNodeDistance && bEnablePerNodeDistance)
|
|
{
|
|
fDist = fNodeDistance;
|
|
}
|
|
|
|
// If we're inside the object, very close, or facing the object, set importance scale to 1.0f. Otherwise, 0.8f.
|
|
float fImportanceScale = (float)fsel(
|
|
4.0f - fEntDistance,
|
|
1.0f,
|
|
(float)fsel(
|
|
(objBox.GetCenter() - pcPosition).Dot(pPrecacheCams[iPrecacheCam].vDirection),
|
|
1.0f,
|
|
0.8f));
|
|
|
|
// I replaced fEntDistance with fNoideDistance here because of Timur request! It's suppose to be unified to-node-distance
|
|
GetObjManager()->UpdateRenderNodeStreamingPriority(pObj, fDist, fImportanceScale, bFullUpdate, passInfo);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Prioritise the first camera (probably the real camera)
|
|
int nFirst =
|
|
((pPrecacheCams[0].vPosition.x > m_vNodeCenter.x) ? 4 : 0) |
|
|
((pPrecacheCams[0].vPosition.y > m_vNodeCenter.y) ? 2 : 0) |
|
|
((pPrecacheCams[0].vPosition.z > m_vNodeCenter.z) ? 1 : 0);
|
|
|
|
if (m_arrChilds[nFirst ])
|
|
{
|
|
arrRecursion.Add(m_arrChilds[nFirst ]);
|
|
}
|
|
|
|
if (m_arrChilds[nFirst ^ 1])
|
|
{
|
|
arrRecursion.Add(m_arrChilds[nFirst ^ 1]);
|
|
}
|
|
|
|
if (m_arrChilds[nFirst ^ 2])
|
|
{
|
|
arrRecursion.Add(m_arrChilds[nFirst ^ 2]);
|
|
}
|
|
|
|
if (m_arrChilds[nFirst ^ 4])
|
|
{
|
|
arrRecursion.Add(m_arrChilds[nFirst ^ 4]);
|
|
}
|
|
|
|
if (m_arrChilds[nFirst ^ 3])
|
|
{
|
|
arrRecursion.Add(m_arrChilds[nFirst ^ 3]);
|
|
}
|
|
|
|
if (m_arrChilds[nFirst ^ 5])
|
|
{
|
|
arrRecursion.Add(m_arrChilds[nFirst ^ 5]);
|
|
}
|
|
|
|
if (m_arrChilds[nFirst ^ 6])
|
|
{
|
|
arrRecursion.Add(m_arrChilds[nFirst ^ 6]);
|
|
}
|
|
|
|
if (m_arrChilds[nFirst ^ 7])
|
|
{
|
|
arrRecursion.Add(m_arrChilds[nFirst ^ 7]);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
int COctreeNode::Load(AZ::IO::HandleType& fileHandle, int& nDataSize, std::vector<IStatObj*>* pStatObjTable, std::vector<_smart_ptr<IMaterial> >* pMatTable, EEndian eEndian, AABB* pBox, const SLayerVisibility* pLayerVisibilityMask)
|
|
{
|
|
return Load_T(fileHandle, nDataSize, pStatObjTable, pMatTable, eEndian, pBox, pLayerVisibilityMask);
|
|
}
|
|
int COctreeNode::Load(uint8*& f, int& nDataSize, std::vector<IStatObj*>* pStatObjTable, std::vector<_smart_ptr<IMaterial> >* pMatTable, EEndian eEndian, AABB* pBox, const SLayerVisibility* pLayerVisibilityMask)
|
|
{
|
|
return Load_T(f, nDataSize, pStatObjTable, pMatTable, eEndian, pBox, pLayerVisibilityMask);
|
|
}
|
|
|
|
template <class T>
|
|
int COctreeNode::Load_T(T& f, int& nDataSize, std::vector<IStatObj*>* pStatObjTable, std::vector<_smart_ptr<IMaterial> >* pMatTable, EEndian eEndian, AABB* pBox, const SLayerVisibility* pLayerVisibilityMask)
|
|
{
|
|
if (pBox && !Overlap::AABB_AABB(GetNodeBox(), *pBox))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
SOcTreeNodeChunk chunk;
|
|
if (!PakLoadDataUtils::LoadDataFromFile(&chunk, 1, f, nDataSize, eEndian))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
assert(chunk.nChunkVersion == OCTREENODE_CHUNK_VERSION || chunk.nChunkVersion == OCTREENODE_CHUNK_VERSION_OLD);
|
|
if (chunk.nChunkVersion != OCTREENODE_CHUNK_VERSION && chunk.nChunkVersion != OCTREENODE_CHUNK_VERSION_OLD)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (chunk.nObjectsBlockSize)
|
|
{
|
|
// load objects data into memory buffer, make sure buffer is aligned
|
|
|
|
auto pMemBlock = gEnv->pCryPak->PoolAllocMemoryBlock(chunk.nObjectsBlockSize + 8, "LoadObjectInstances");
|
|
byte* pPtr = (byte*)pMemBlock->m_address.get();
|
|
|
|
while (UINT_PTR(pPtr) & 3)
|
|
{
|
|
pPtr++;
|
|
}
|
|
|
|
if (!PakLoadDataUtils::LoadDataFromFile(pPtr, chunk.nObjectsBlockSize, f, nDataSize, eEndian))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (!m_bEditor)
|
|
{
|
|
LoadObjects(pPtr, pPtr + chunk.nObjectsBlockSize, pStatObjTable, pMatTable, eEndian, chunk.nChunkVersion, pLayerVisibilityMask);
|
|
}
|
|
}
|
|
|
|
// count number of nodes loaded
|
|
int nNodesNum = 1;
|
|
|
|
// process childs
|
|
for (int nChildId = 0; nChildId < 8; nChildId++)
|
|
{
|
|
if (chunk.ucChildsMask & (1 << nChildId))
|
|
{
|
|
if (!m_arrChilds[nChildId])
|
|
{
|
|
m_arrChilds[nChildId] = COctreeNode::Create(m_nSID, GetChildBBox(nChildId), m_pVisArea, this);
|
|
}
|
|
|
|
int nNewNodesNum = m_arrChilds[nChildId]->Load_T(f, nDataSize, pStatObjTable, pMatTable, eEndian, pBox, pLayerVisibilityMask);
|
|
|
|
if (!nNewNodesNum && !pBox)
|
|
{
|
|
return 0; // data error
|
|
}
|
|
nNodesNum += nNewNodesNum;
|
|
}
|
|
}
|
|
|
|
return nNodesNum;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#if ENGINE_ENABLE_COMPILATION
|
|
int COctreeNode::GetData(byte*& pData, int& nDataSize, std::vector<IStatObj*>* pStatObjTable, std::vector<_smart_ptr<IMaterial> >* pMatTable, std::vector<IStatInstGroup*>* pStatInstGroupTable, EEndian eEndian, SHotUpdateInfo* pExportInfo)
|
|
{
|
|
AABB* pBox = (pExportInfo && !pExportInfo->areaBox.IsReset()) ? &pExportInfo->areaBox : NULL;
|
|
|
|
const AABB& nodeBox = GetNodeBox();
|
|
if (pBox && !Overlap::AABB_AABB(nodeBox, *pBox))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (pData)
|
|
{
|
|
// get node data
|
|
SOcTreeNodeChunk chunk;
|
|
chunk.nChunkVersion = OCTREENODE_CHUNK_VERSION;
|
|
chunk.nodeBox = nodeBox;
|
|
|
|
// fill ChildsMask
|
|
chunk.ucChildsMask = 0;
|
|
for (int i = 0; i < 8; i++)
|
|
{
|
|
if (m_arrChilds[i])
|
|
{
|
|
chunk.ucChildsMask |= (1 << i);
|
|
}
|
|
}
|
|
|
|
CMemoryBlock memblock;
|
|
SaveObjects(&memblock, pStatObjTable, pMatTable, pStatInstGroupTable, eEndian, pExportInfo);
|
|
|
|
chunk.nObjectsBlockSize = memblock.GetSize();
|
|
|
|
AddToPtr(pData, nDataSize, chunk, eEndian);
|
|
|
|
AddToPtr(pData, nDataSize, (byte*)memblock.GetData(), memblock.GetSize(), eEndian);
|
|
}
|
|
else // just count size
|
|
{
|
|
nDataSize += sizeof(SOcTreeNodeChunk);
|
|
nDataSize += SaveObjects(NULL, NULL, NULL, NULL, eEndian, pExportInfo);
|
|
}
|
|
|
|
// count number of nodes loaded
|
|
int nNodesNum = 1;
|
|
|
|
// process childs
|
|
for (int i = 0; i < 8; i++)
|
|
{
|
|
if (m_arrChilds[i])
|
|
{
|
|
nNodesNum += m_arrChilds[i]->GetData(pData, nDataSize, pStatObjTable, pMatTable, pStatInstGroupTable, eEndian, pExportInfo);
|
|
}
|
|
}
|
|
|
|
return nNodesNum;
|
|
}
|
|
#endif
|
|
|
|
bool COctreeNode::CleanUpTree()
|
|
{
|
|
bool bChildObjectsFound = false;
|
|
for (int i = 0; i < 8; i++)
|
|
{
|
|
if (m_arrChilds[i])
|
|
{
|
|
if (!m_arrChilds[i]->CleanUpTree())
|
|
{
|
|
delete m_arrChilds[i];
|
|
m_arrChilds[i] = NULL;
|
|
}
|
|
else
|
|
{
|
|
bChildObjectsFound = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// update max view distances
|
|
|
|
m_fObjectsMaxViewDist = 0.f;
|
|
m_objectsBox = GetNodeBox();
|
|
|
|
for (int l = 0; l < eRNListType_ListsNum; l++)
|
|
{
|
|
for (IRenderNode* pObj = m_arrObjects[l].m_pFirstNode; pObj; pObj = pObj->m_pNext)
|
|
{
|
|
pObj->m_fWSMaxViewDist = pObj->GetMaxViewDist();
|
|
m_fObjectsMaxViewDist = max(m_fObjectsMaxViewDist, pObj->m_fWSMaxViewDist);
|
|
m_objectsBox.Add(pObj->GetBBox());
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < 8; i++)
|
|
{
|
|
if (m_arrChilds[i])
|
|
{
|
|
m_fObjectsMaxViewDist = max(m_fObjectsMaxViewDist, m_arrChilds[i]->m_fObjectsMaxViewDist);
|
|
m_objectsBox.Add(m_arrChilds[i]->m_objectsBox);
|
|
}
|
|
}
|
|
|
|
return (bChildObjectsFound || HasObjects());
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
bool COctreeNode::CheckRenderFlagsMinSpec(uint32 dwRndFlags)
|
|
{
|
|
int nRenderNodeMinSpec = (dwRndFlags & ERF_SPEC_BITS_MASK) >> ERF_SPEC_BITS_SHIFT;
|
|
return CheckMinSpec(nRenderNodeMinSpec);
|
|
}
|
|
|
|
void COctreeNode::OffsetObjects(const Vec3& offset)
|
|
{
|
|
CompileObjects();
|
|
m_objectsBox.Move(offset);
|
|
m_vNodeCenter += offset;
|
|
|
|
for (int l = 0; l < eRNListType_ListsNum; l++)
|
|
{
|
|
for (IRenderNode* pObj = m_arrObjects[l].m_pFirstNode; pObj; pObj = pObj->m_pNext)
|
|
{
|
|
pObj->OffsetPosition(offset);
|
|
}
|
|
}
|
|
for (int i = 0; i < 8; ++i)
|
|
{
|
|
if (m_arrChilds[i])
|
|
{
|
|
m_arrChilds[i]->OffsetObjects(offset);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool COctreeNode::HasAnyRenderableCandidates(const SRenderingPassInfo& passInfo) const
|
|
{
|
|
// This checks if anything will be rendered, assuming we pass occlusion checks
|
|
// This is based on COctreeNode::RenderContentJobEntry's implementation,
|
|
// if that would do nothing, we can skip the running of occlusion and rendering jobs for this node
|
|
const bool bDecalsAndRoads = passInfo.RenderDecals() && m_arrObjects[eRNListType_DecalsAndRoads].m_pFirstNode != NULL;
|
|
const bool bUnknown = m_arrObjects[eRNListType_Unknown].m_pFirstNode != NULL;
|
|
return bDecalsAndRoads || bUnknown;
|
|
}
|