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.
1492 lines
50 KiB
C++
1492 lines
50 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 : Draws geometry caches
|
|
|
|
|
|
#include "Cry3DEngine_precompiled.h"
|
|
|
|
#if defined(USE_GEOM_CACHES)
|
|
|
|
#include "GeomCacheRenderNode.h"
|
|
#include "GeomCacheManager.h"
|
|
#include "MatMan.h"
|
|
|
|
#include <AzCore/Jobs/JobFunction.h>
|
|
|
|
namespace
|
|
{
|
|
// Constants
|
|
const float kDefaultMaxViewDist = 1000.0f;
|
|
}
|
|
|
|
CGeomCacheRenderNode::CGeomCacheRenderNode()
|
|
: m_pGeomCache(NULL)
|
|
, m_playbackTime(0.0f)
|
|
, m_pPhysicalEntity(NULL)
|
|
, m_maxViewDist(kDefaultMaxViewDist)
|
|
, m_bBox(0.0f)
|
|
, m_currentAABB(0.0f)
|
|
, m_currentDisplayAABB(0.0f)
|
|
, m_standInVisible(eStandInType_None)
|
|
, m_pStandIn(NULL)
|
|
, m_pFirstFrameStandIn(NULL)
|
|
, m_pLastFrameStandIn(NULL)
|
|
, m_standInDistance(0.0f)
|
|
, m_streamInDistance(0.0f)
|
|
, m_bInitialized(false)
|
|
, m_bLooping(false)
|
|
, m_bIsStreaming(false)
|
|
, m_bFilledFrameOnce(false)
|
|
, m_bBoundsChanged(true)
|
|
, m_bDrawing(true)
|
|
, m_bTransformReady(true)
|
|
{
|
|
m_matrix.SetIdentity();
|
|
m_pMaterial = GetMatMan()->GetDefaultMaterial();
|
|
|
|
SetRndFlags(ERF_HAS_CASTSHADOWMAPS, true);
|
|
SetRndFlags(ERF_CASTSHADOWMAPS, true);
|
|
}
|
|
|
|
CGeomCacheRenderNode::~CGeomCacheRenderNode()
|
|
{
|
|
Clear(true);
|
|
|
|
if (m_pGeomCache)
|
|
{
|
|
m_pGeomCache->RemoveListener(this);
|
|
m_pGeomCache = nullptr;
|
|
}
|
|
|
|
m_pMaterial = nullptr;
|
|
|
|
Get3DEngine()->FreeRenderNodeState(this);
|
|
}
|
|
|
|
const char* CGeomCacheRenderNode::GetEntityClassName() const
|
|
{
|
|
return "GeomCache";
|
|
}
|
|
|
|
const char* CGeomCacheRenderNode::GetName() const
|
|
{
|
|
if (m_pGeomCache)
|
|
{
|
|
return m_pGeomCache->GetFilePath();
|
|
}
|
|
|
|
return "GeomCacheNotSet";
|
|
}
|
|
|
|
Vec3 CGeomCacheRenderNode::GetPos([[maybe_unused]] bool bWorldOnly) const
|
|
{
|
|
assert(bWorldOnly);
|
|
return m_matrix.GetTranslation();
|
|
}
|
|
|
|
void CGeomCacheRenderNode::SetBBox(const AABB& bBox)
|
|
{
|
|
m_bBox = bBox;
|
|
}
|
|
|
|
const AABB CGeomCacheRenderNode::GetBBox() const
|
|
{
|
|
return m_bBox;
|
|
}
|
|
|
|
void CGeomCacheRenderNode::UpdateBBox()
|
|
{
|
|
AABB newAABB;
|
|
|
|
const Vec3 vCamPos = Get3DEngine()->GetRenderingCamera().GetPosition();
|
|
float distance = Distance::Point_Point(vCamPos, m_matrix.GetTranslation());
|
|
|
|
const bool bGeomCacheLoaded = m_pGeomCache ? m_pGeomCache->IsLoaded() : false;
|
|
|
|
const bool bAllowStandIn = GetCVars()->e_Lods != 0;
|
|
const bool bInStandInDistance = distance > m_standInDistance && bAllowStandIn;
|
|
|
|
EStandInType selectedStandIn = SelectStandIn();
|
|
IStatObj* pStandIn = GetStandIn(selectedStandIn);
|
|
|
|
if (pStandIn && (bInStandInDistance || !bGeomCacheLoaded))
|
|
{
|
|
m_standInVisible = selectedStandIn;
|
|
newAABB = pStandIn->GetAABB();
|
|
}
|
|
else
|
|
{
|
|
m_standInVisible = eStandInType_None;
|
|
newAABB = m_currentDisplayAABB;
|
|
}
|
|
|
|
if (newAABB.min != m_currentAABB.min || newAABB.max != m_currentAABB.max)
|
|
{
|
|
m_bBoundsChanged = true;
|
|
m_currentAABB = newAABB;
|
|
}
|
|
|
|
if (m_streamInDistance > 0.0f)
|
|
{
|
|
m_bIsStreaming = distance <= m_streamInDistance;
|
|
}
|
|
}
|
|
|
|
void CGeomCacheRenderNode::GetLocalBounds(AABB& bbox)
|
|
{
|
|
bbox = m_currentAABB;
|
|
}
|
|
|
|
bool CGeomCacheRenderNode::DidBoundsChange()
|
|
{
|
|
bool bBoundsChanged = m_bBoundsChanged;
|
|
|
|
if (bBoundsChanged)
|
|
{
|
|
CalcBBox();
|
|
}
|
|
|
|
m_bBoundsChanged = false;
|
|
return bBoundsChanged;
|
|
}
|
|
|
|
/* Geom caches are rendered using a custom render element for performance reasons (CREGeomCache).
|
|
* We only call mfAdd once per material, so a lot of meshes can be rendered with just one CRenderObject in the render pipeline.
|
|
* Mesh and transform updates run asynchronously started from FillFrameAsync and are synchronized in the render thread (CREGeomCache::Update)
|
|
* Visible meshes are added to a SMeshRenderData vector in UpdateTransformsRec. The lists are rendered in CREGeomCache::mfDraw
|
|
* Downside is that meshes in the cache are not sorted by depth for transparency passes
|
|
*/
|
|
void CGeomCacheRenderNode::Render(const struct SRendParams& rendParams, const SRenderingPassInfo& passInfo)
|
|
{
|
|
FUNCTION_PROFILER_3DENGINE;
|
|
|
|
if (!m_bInitialized || !m_bDrawing || (m_renderMeshes.empty() && m_renderMeshUpdateContexts.empty())
|
|
|| !m_pGeomCache || m_dwRndFlags & ERF_HIDDEN || !passInfo.RenderGeomCaches())
|
|
{
|
|
return;
|
|
}
|
|
|
|
m_pGeomCache->SetLastDrawMainFrameId(passInfo.GetMainFrameID());
|
|
|
|
SRendParams drawParams = rendParams;
|
|
|
|
drawParams.pMatrix = &m_matrix;
|
|
drawParams.nClipVolumeStencilRef = 0;
|
|
drawParams.ppRNTmpData = &m_pRNTmpData;
|
|
|
|
switch (m_standInVisible)
|
|
{
|
|
case eStandInType_None:
|
|
{
|
|
#ifndef _RELEASE
|
|
if (GetCVars()->e_GeomCacheDebugDrawMode == 3)
|
|
{
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
drawParams.pMaterial = m_pMaterial;
|
|
|
|
IRenderer* const pRenderer = GetRenderer();
|
|
CRenderObject* pRenderObject = pRenderer->EF_GetObject_Temp(passInfo.ThreadID());
|
|
|
|
if (pRenderObject)
|
|
{
|
|
FillRenderObject(drawParams, passInfo, m_pMaterial, pRenderObject);
|
|
|
|
if (m_pRenderElements.size() > 0 && passInfo.IsGeneralPass())
|
|
{
|
|
// Only need to call this once because SRenderObjData::m_pInstance is the same for all of them
|
|
m_pRenderElements.begin()->second.m_pRenderElement->SetupMotionBlur(pRenderObject, passInfo);
|
|
}
|
|
|
|
for (TRenderElementMap::iterator iter = m_pRenderElements.begin(); iter != m_pRenderElements.end(); ++iter)
|
|
{
|
|
const uint materialId = iter->first;
|
|
CREGeomCache* pCREGeomCache = iter->second.m_pRenderElement;
|
|
|
|
SShaderItem& shaderItem = m_pMaterial->GetShaderItem(materialId);
|
|
const int renderList = rendParams.nRenderList;
|
|
const int afterWater = rendParams.nAfterWater;
|
|
SRendItemSorter rendItemSorter(rendParams.rendItemSorter);
|
|
|
|
pRenderer->EF_AddEf(pCREGeomCache, shaderItem, pRenderObject, passInfo, renderList, afterWater, rendItemSorter);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case eStandInType_Default:
|
|
{
|
|
// Override material if there stand in has a material that is not default
|
|
_smart_ptr<IMaterial> pStandInMaterial = m_pStandIn->GetMaterial();
|
|
drawParams.pMaterial = (pStandInMaterial && !pStandInMaterial->IsDefault()) ? pStandInMaterial : drawParams.pMaterial;
|
|
|
|
m_pStandIn->Render(drawParams, passInfo);
|
|
break;
|
|
}
|
|
case eStandInType_FirstFrame:
|
|
{
|
|
// Override material if there stand in has a material that is not default
|
|
_smart_ptr<IMaterial> pStandInMaterial = m_pFirstFrameStandIn->GetMaterial();
|
|
drawParams.pMaterial = (pStandInMaterial && !pStandInMaterial->IsDefault()) ? pStandInMaterial : drawParams.pMaterial;
|
|
|
|
m_pFirstFrameStandIn->Render(drawParams, passInfo);
|
|
break;
|
|
}
|
|
case eStandInType_LastFrame:
|
|
{
|
|
// Override material if there stand in has a material that is not default
|
|
_smart_ptr<IMaterial> pStandInMaterial = m_pLastFrameStandIn->GetMaterial();
|
|
drawParams.pMaterial = (pStandInMaterial && !pStandInMaterial->IsDefault()) ? pStandInMaterial : drawParams.pMaterial;
|
|
|
|
m_pLastFrameStandIn->Render(drawParams, passInfo);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CGeomCacheRenderNode::SetMaterial(_smart_ptr<IMaterial> pMat)
|
|
{
|
|
if (pMat)
|
|
{
|
|
m_pMaterial = pMat;
|
|
}
|
|
else if (m_pGeomCache)
|
|
{
|
|
_smart_ptr<IMaterial> pMaterial = m_pGeomCache->GetMaterial();
|
|
m_pMaterial = pMaterial;
|
|
}
|
|
else
|
|
{
|
|
m_pMaterial = GetMatMan()->GetDefaultMaterial();
|
|
}
|
|
|
|
UpdatePhysicalMaterials();
|
|
}
|
|
|
|
_smart_ptr<IMaterial> CGeomCacheRenderNode::GetMaterial([[maybe_unused]] Vec3* pHitPos)
|
|
{
|
|
if (m_pMaterial)
|
|
{
|
|
return m_pMaterial;
|
|
}
|
|
else if (m_pGeomCache)
|
|
{
|
|
return m_pGeomCache->GetMaterial();
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
float CGeomCacheRenderNode::GetMaxViewDist()
|
|
{
|
|
return m_maxViewDist * m_fViewDistanceMultiplier;
|
|
}
|
|
|
|
void CGeomCacheRenderNode::GetMemoryUsage(ICrySizer* pSizer) const
|
|
{
|
|
SIZER_COMPONENT_NAME(pSizer, "GeomCache");
|
|
pSizer->AddObject(this, sizeof(*this));
|
|
}
|
|
|
|
void CGeomCacheRenderNode::SetMatrix(const Matrix34& matrix)
|
|
{
|
|
m_matrix = matrix;
|
|
CalcBBox();
|
|
}
|
|
|
|
void CGeomCacheRenderNode::CalcBBox()
|
|
{
|
|
m_bBox = AABB(0.0f);
|
|
|
|
if (!m_pGeomCache || !m_pGeomCache->IsValid())
|
|
{
|
|
return;
|
|
}
|
|
|
|
m_bBox.SetTransformedAABB(m_matrix, m_currentAABB);
|
|
}
|
|
|
|
bool CGeomCacheRenderNode::LoadGeomCache(const char* sGeomCacheFileName)
|
|
{
|
|
Clear(false);
|
|
|
|
m_pGeomCache = static_cast<CGeomCache*>(Get3DEngine()->LoadGeomCache(sGeomCacheFileName));
|
|
|
|
if (m_pGeomCache && !m_pGeomCache->IsValid())
|
|
{
|
|
m_pGeomCache = NULL;
|
|
}
|
|
|
|
if (m_pGeomCache)
|
|
{
|
|
m_currentAABB = m_pGeomCache->GetAABB();
|
|
m_bBoundsChanged = true;
|
|
m_pMaterial = m_pGeomCache->GetMaterial();
|
|
|
|
const std::vector<SGeomCacheStaticNodeData>& staticNodeData = m_pGeomCache->GetStaticNodeData();
|
|
m_nodeMatrices.resize(staticNodeData.size());
|
|
uint currentNodeIndex = 0;
|
|
InitTransformsRec(currentNodeIndex, staticNodeData, QuatTNS(IDENTITY));
|
|
|
|
m_pGeomCache->AddListener(this);
|
|
|
|
if (m_pGeomCache->IsLoaded())
|
|
{
|
|
return Initialize();
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void CGeomCacheRenderNode::SetGeomCache(_smart_ptr<IGeomCache> geomCache)
|
|
{
|
|
Clear(false);
|
|
|
|
if (geomCache == nullptr || !geomCache->IsValid())
|
|
{
|
|
return;
|
|
}
|
|
|
|
m_pGeomCache = static_cast<CGeomCache*>(geomCache.get());
|
|
|
|
m_currentAABB = m_pGeomCache->GetAABB();
|
|
m_bBoundsChanged = true;
|
|
m_pMaterial = m_pGeomCache->GetMaterial();
|
|
|
|
const std::vector<SGeomCacheStaticNodeData>& staticNodeData = m_pGeomCache->GetStaticNodeData();
|
|
m_nodeMatrices.resize(staticNodeData.size());
|
|
uint currentNodeIndex = 0;
|
|
InitTransformsRec(currentNodeIndex, staticNodeData, QuatTNS(IDENTITY));
|
|
|
|
m_pGeomCache->AddListener(this);
|
|
|
|
if (m_pGeomCache->IsLoaded())
|
|
{
|
|
Initialize();
|
|
}
|
|
}
|
|
|
|
bool CGeomCacheRenderNode::Initialize()
|
|
{
|
|
assert(!m_bInitialized);
|
|
if (m_bInitialized)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (m_pGeomCache)
|
|
{
|
|
if (!InitializeRenderMeshes())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const std::vector<SGeomCacheStaticMeshData>& staticMeshData = m_pGeomCache->GetStaticMeshData();
|
|
|
|
const uint numMeshes = staticMeshData.size();
|
|
for (uint i = 0; i < numMeshes; ++i)
|
|
{
|
|
const SGeomCacheStaticMeshData& meshData = staticMeshData[i];
|
|
uint numMaterials = meshData.m_materialIds.size();
|
|
|
|
for (uint j = 0; j < numMaterials; ++j)
|
|
{
|
|
const uint16 materialId = meshData.m_materialIds[j];
|
|
if (m_pRenderElements.find(materialId) == m_pRenderElements.end())
|
|
{
|
|
CREGeomCache* pRenderElement = static_cast<CREGeomCache*>(GetRenderer()->EF_CreateRE(eDATA_GeomCache));
|
|
|
|
SGeomCacheRenderElementData renderElementData;
|
|
renderElementData.m_pRenderElement = pRenderElement;
|
|
renderElementData.m_pUpdateState = (volatile int*)NULL;
|
|
renderElementData.m_pCurrentFillData = NULL;
|
|
|
|
m_pRenderElements[materialId] = renderElementData;
|
|
pRenderElement->InitializeRenderElement(numMeshes, &m_renderMeshes[0], materialId);
|
|
}
|
|
}
|
|
}
|
|
|
|
GetGeomCacheManager()->RegisterForStreaming(this);
|
|
|
|
m_bInitialized = true;
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void CGeomCacheRenderNode::Clear(bool bWaitForStreamingJobs)
|
|
{
|
|
m_bInitialized = false;
|
|
|
|
GetGeomCacheManager()->UnRegisterForStreaming(this, bWaitForStreamingJobs);
|
|
|
|
m_renderMeshes.clear();
|
|
m_renderMeshUpdateContexts.clear();
|
|
|
|
for (TRenderElementMap::iterator iter = m_pRenderElements.begin(); iter != m_pRenderElements.end(); ++iter)
|
|
{
|
|
CREGeomCache* pCREGeomCache = iter->second.m_pRenderElement;
|
|
pCREGeomCache->Release();
|
|
}
|
|
|
|
m_currentAABB = AABB(0.0f);
|
|
m_currentDisplayAABB = AABB(0.0f);
|
|
m_pRenderElements.clear();
|
|
}
|
|
|
|
void CGeomCacheRenderNode::SetPlaybackTime(const float time)
|
|
{
|
|
if (m_pGeomCache)
|
|
{
|
|
const float duration = m_pGeomCache->GetDuration();
|
|
const bool bInsideTimeRange = (time >= 0.0f && (m_bLooping || time <= duration));
|
|
|
|
float clampedTime = time < 0.0f ? 0.0f : time;
|
|
if (!m_bLooping)
|
|
{
|
|
clampedTime = time > duration ? duration : time;
|
|
}
|
|
|
|
m_playbackTime = clampedTime;
|
|
m_streamingTime = clampedTime;
|
|
|
|
if (m_pGeomCache && bInsideTimeRange)
|
|
{
|
|
StartStreaming(clampedTime);
|
|
return;
|
|
}
|
|
}
|
|
|
|
StopStreaming();
|
|
}
|
|
|
|
void CGeomCacheRenderNode::StartStreaming(const float time)
|
|
{
|
|
if (m_pGeomCache && time >= 0.0f && (m_bLooping || time <= m_pGeomCache->GetDuration()))
|
|
{
|
|
m_streamingTime = time;
|
|
m_bIsStreaming = true;
|
|
}
|
|
}
|
|
|
|
void CGeomCacheRenderNode::StopStreaming()
|
|
{
|
|
m_bIsStreaming = false;
|
|
}
|
|
|
|
bool CGeomCacheRenderNode::IsLooping() const
|
|
{
|
|
return m_bLooping;
|
|
}
|
|
|
|
void CGeomCacheRenderNode::SetLooping(const bool bEnable)
|
|
{
|
|
if (m_pGeomCache && m_pGeomCache->GetNumFrames() <= 1)
|
|
{
|
|
// looping a 1 frame cache is the same as not looping it. However the underlying logic on how we stream and read the GeomCache
|
|
// from disk breaks for a 1 frame loop. So we explicitly don't allow looping of a single frame cache, which doesn't make a
|
|
// visible difference to the user, since a 1 frame loop looks the same as playing back once.
|
|
m_bLooping = false;
|
|
}
|
|
else
|
|
{
|
|
m_bLooping = bEnable;
|
|
}
|
|
}
|
|
|
|
bool CGeomCacheRenderNode::IsStreaming() const
|
|
{
|
|
return m_bIsStreaming && m_pGeomCache && !m_pGeomCache->PlaybackFromMemory();
|
|
}
|
|
|
|
float CGeomCacheRenderNode::GetPrecachedTime() const
|
|
{
|
|
return GetGeomCacheManager()->GetPrecachedTime(this);
|
|
}
|
|
|
|
void CGeomCacheRenderNode::StartAsyncUpdate()
|
|
{
|
|
FUNCTION_PROFILER_3DENGINE;
|
|
|
|
m_bTransformReady = false;
|
|
|
|
for (TRenderElementMap::iterator iter = m_pRenderElements.begin(); iter != m_pRenderElements.end(); ++iter)
|
|
{
|
|
SGeomCacheRenderElementData& data = iter->second;
|
|
CREGeomCache* pCREGeomCache = data.m_pRenderElement;
|
|
data.m_pUpdateState = pCREGeomCache->SetAsyncUpdateState(data.m_threadId);
|
|
data.m_pCurrentFillData = pCREGeomCache->GetMeshFillDataPtr();
|
|
}
|
|
|
|
const std::vector<SGeomCacheStaticMeshData>& staticMeshData = m_pGeomCache->GetStaticMeshData();
|
|
|
|
const uint numDynamicRenderMeshes = m_renderMeshUpdateContexts.size();
|
|
for (uint i = 0; i < numDynamicRenderMeshes; ++i)
|
|
{
|
|
SGeomCacheRenderMeshUpdateContext& updateContext = m_renderMeshUpdateContexts[i];
|
|
|
|
_smart_ptr<IRenderMesh> pRenderMesh = SetupDynamicRenderMesh(updateContext);
|
|
m_renderMeshUpdateContexts[i].m_pRenderMesh = pRenderMesh;
|
|
|
|
const SGeomCacheStaticMeshData& currentMeshData = staticMeshData[updateContext.m_meshId];
|
|
const uint numMaterials = currentMeshData.m_materialIds.size();
|
|
for (uint j = 0; j < numMaterials; ++j)
|
|
{
|
|
const uint16 materialId = currentMeshData.m_materialIds[j];
|
|
SGeomCacheRenderElementData& data = m_pRenderElements[materialId];
|
|
CREGeomCache::SMeshRenderData& meshData = (*data.m_pCurrentFillData)[updateContext.m_meshId];
|
|
meshData.m_pRenderMesh = pRenderMesh;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CGeomCacheRenderNode::SkipFrameFill()
|
|
{
|
|
const uint numDynamicRenderMeshes = m_renderMeshUpdateContexts.size();
|
|
for (uint i = 0; i < numDynamicRenderMeshes; ++i)
|
|
{
|
|
SGeomCacheRenderMeshUpdateContext& updateContext = m_renderMeshUpdateContexts[i];
|
|
if (updateContext.m_pUpdateState)
|
|
{
|
|
CryInterlockedDecrement(updateContext.m_pUpdateState);
|
|
}
|
|
}
|
|
|
|
for (TRenderElementMap::iterator iter = m_pRenderElements.begin(); iter != m_pRenderElements.end(); ++iter)
|
|
{
|
|
CryInterlockedDecrement(iter->second.m_pUpdateState);
|
|
}
|
|
|
|
m_bTransformReady = true;
|
|
m_bTransformReadyCV.Notify();
|
|
}
|
|
|
|
bool CGeomCacheRenderNode::FillFrameAsync(const char* const pFloorFrameData, const char* const pCeilFrameData, const float lerpFactor)
|
|
{
|
|
FUNCTION_PROFILER_3DENGINE;
|
|
|
|
CryAutoLock<CryCriticalSection> fillLock(m_fillCS);
|
|
|
|
if ((m_renderMeshes.empty() && m_renderMeshUpdateContexts.empty())
|
|
|| (!m_renderMeshUpdateContexts.empty() && m_renderMeshUpdateContexts[0].m_pUpdateState == NULL)
|
|
|| (m_standInVisible != eStandInType_None && m_bFilledFrameOnce))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const CGeomCache* const pGeomCache = m_pGeomCache;
|
|
assert(pGeomCache);
|
|
|
|
if (!pGeomCache)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const std::vector<SGeomCacheStaticMeshData>& staticMeshData = pGeomCache->GetStaticMeshData();
|
|
const std::vector<SGeomCacheStaticNodeData>& staticNodeData = pGeomCache->GetStaticNodeData();
|
|
|
|
const uint numMeshes = staticMeshData.size();
|
|
const uint numNodes = staticNodeData.size();
|
|
|
|
if (numMeshes == 0 || numNodes == 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Computer pointers to mesh & node data in frames
|
|
const GeomCacheFile::SFrameHeader* const floorFrameHeader = reinterpret_cast<const GeomCacheFile::SFrameHeader* const>(pFloorFrameData);
|
|
const char* pFloorMeshData = pFloorFrameData + sizeof(GeomCacheFile::SFrameHeader);
|
|
const char* const pFloorNodeData = pFloorFrameData + sizeof(GeomCacheFile::SFrameHeader) + floorFrameHeader->m_nodeDataOffset;
|
|
|
|
const GeomCacheFile::SFrameHeader* const ceilFrameHeader = reinterpret_cast<const GeomCacheFile::SFrameHeader* const>(pCeilFrameData);
|
|
const char* pCeilMeshData = pCeilFrameData + sizeof(GeomCacheFile::SFrameHeader);
|
|
const char* const pCeilNodeData = pCeilFrameData + sizeof(GeomCacheFile::SFrameHeader) + ceilFrameHeader->m_nodeDataOffset;
|
|
|
|
// Update geom cache AABB
|
|
AABB floorAABB(Vec3(floorFrameHeader->m_frameAABBMin[0], floorFrameHeader->m_frameAABBMin[1], floorFrameHeader->m_frameAABBMin[2]),
|
|
Vec3(floorFrameHeader->m_frameAABBMax[0], floorFrameHeader->m_frameAABBMax[1], floorFrameHeader->m_frameAABBMax[2]));
|
|
AABB ceilAABB(Vec3(ceilFrameHeader->m_frameAABBMin[0], ceilFrameHeader->m_frameAABBMin[1], ceilFrameHeader->m_frameAABBMin[2]),
|
|
Vec3(ceilFrameHeader->m_frameAABBMax[0], ceilFrameHeader->m_frameAABBMax[1], ceilFrameHeader->m_frameAABBMax[2]));
|
|
|
|
m_currentDisplayAABB = floorAABB;
|
|
m_currentDisplayAABB.Add(ceilAABB);
|
|
|
|
// Update meshes & clear instances
|
|
for (uint meshId = 0; meshId < numMeshes; ++meshId)
|
|
{
|
|
for (TRenderElementMap::iterator iter = m_pRenderElements.begin(); iter != m_pRenderElements.end(); ++iter)
|
|
{
|
|
SGeomCacheRenderElementData& data = iter->second;
|
|
(*data.m_pCurrentFillData)[meshId].m_instances.clear();
|
|
}
|
|
}
|
|
|
|
// Add instance for current frame
|
|
uint currentMeshIndex = 0;
|
|
uint currentNodeIndex = 0;
|
|
uint currentNodeDataOffset = 0;
|
|
|
|
UpdateTransformsRec(currentMeshIndex, currentNodeIndex, staticNodeData, staticMeshData,
|
|
currentNodeDataOffset, pFloorNodeData, pCeilNodeData, QuatTNS(IDENTITY), lerpFactor);
|
|
|
|
m_bTransformReady = true;
|
|
m_bTransformReadyCV.Notify();
|
|
|
|
UpdatePhysicalEntity(NULL);
|
|
|
|
uint currentRenderMesh = 0;
|
|
for (uint meshId = 0; meshId < numMeshes; ++meshId)
|
|
{
|
|
const SGeomCacheStaticMeshData* pStaticMeshData = &staticMeshData[meshId];
|
|
if (pStaticMeshData->m_animatedStreams != 0)
|
|
{
|
|
size_t offsetToNextMesh = 0;
|
|
float meshLerpFactor = lerpFactor;
|
|
SGeomCacheRenderMeshUpdateContext* pUpdateContext = &m_renderMeshUpdateContexts[currentRenderMesh++];
|
|
|
|
if (GeomCacheDecoder::PrepareFillMeshData(*pUpdateContext, *pStaticMeshData, pFloorMeshData, pCeilMeshData, offsetToNextMesh, meshLerpFactor))
|
|
{
|
|
AZ::Job* job = AZ::CreateJobFunction([this, pUpdateContext, pStaticMeshData, pFloorMeshData, pCeilMeshData, meshLerpFactor]() { this->UpdateMesh_JobEntry(pUpdateContext, pStaticMeshData, pFloorMeshData, pCeilMeshData, meshLerpFactor); }, true, nullptr);
|
|
job->Start();
|
|
}
|
|
else
|
|
{
|
|
CryInterlockedDecrement(pUpdateContext->m_pUpdateState);
|
|
}
|
|
|
|
pFloorMeshData += offsetToNextMesh;
|
|
pCeilMeshData += offsetToNextMesh;
|
|
}
|
|
}
|
|
|
|
for (TRenderElementMap::iterator iter = m_pRenderElements.begin(); iter != m_pRenderElements.end(); ++iter)
|
|
{
|
|
SGeomCacheRenderElementData& data = iter->second;
|
|
data.m_pRenderElement->DisplayFilledBuffer(data.m_threadId);
|
|
CryInterlockedDecrement(iter->second.m_pUpdateState);
|
|
}
|
|
|
|
m_bFilledFrameOnce = true;
|
|
return true;
|
|
}
|
|
|
|
void CGeomCacheRenderNode::UpdateMesh_JobEntry(SGeomCacheRenderMeshUpdateContext *pUpdateContext, const SGeomCacheStaticMeshData *pStaticMeshData,
|
|
const char* pFloorMeshData, const char* pCeilMeshData, float lerpFactor)
|
|
{
|
|
GeomCacheDecoder::FillMeshDataFromDecodedFrame(m_bFilledFrameOnce, *pUpdateContext, *pStaticMeshData, pFloorMeshData, pCeilMeshData, lerpFactor);
|
|
CryInterlockedDecrement(pUpdateContext->m_pUpdateState);
|
|
}
|
|
|
|
void CGeomCacheRenderNode::ClearFillData()
|
|
{
|
|
FUNCTION_PROFILER_3DENGINE;
|
|
|
|
const std::vector<SGeomCacheStaticMeshData>& staticMeshData = m_pGeomCache->GetStaticMeshData();
|
|
const uint numMeshes = staticMeshData.size();
|
|
|
|
// Clear dynamic render meshes in fill buffer to release their unused memory
|
|
for (uint meshId = 0; meshId < numMeshes; ++meshId)
|
|
{
|
|
const SGeomCacheStaticMeshData& meshData = staticMeshData[meshId];
|
|
|
|
if (meshData.m_animatedStreams != 0)
|
|
{
|
|
for (TRenderElementMap::iterator iter = m_pRenderElements.begin(); iter != m_pRenderElements.end(); ++iter)
|
|
{
|
|
SGeomCacheRenderElementData& data = iter->second;
|
|
DynArray<CREGeomCache::SMeshRenderData>* pFillData = data.m_pRenderElement->GetMeshFillDataPtr();
|
|
(*pFillData)[meshId].m_pRenderMesh = NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CGeomCacheRenderNode::InitTransformsRec(uint& currentNodeIndex, const std::vector<SGeomCacheStaticNodeData>& staticNodeData, const QuatTNS& currentTransform)
|
|
{
|
|
const SGeomCacheStaticNodeData& currentNodeData = staticNodeData[currentNodeIndex];
|
|
const QuatTNS newTransformQuat = currentTransform * currentNodeData.m_localTransform;
|
|
const Matrix34 newTransformMatrix(newTransformQuat);
|
|
m_nodeMatrices[currentNodeIndex] = newTransformMatrix;
|
|
|
|
currentNodeIndex += 1;
|
|
|
|
const uint32 numChildren = currentNodeData.m_numChildren;
|
|
for (uint32 i = 0; i < numChildren; ++i)
|
|
{
|
|
InitTransformsRec(currentNodeIndex, staticNodeData, newTransformQuat);
|
|
}
|
|
}
|
|
|
|
void CGeomCacheRenderNode::UpdateTransformsRec(uint& currentNodeIndex, uint& currentMeshIndex, const std::vector<SGeomCacheStaticNodeData>& staticNodeData,
|
|
const std::vector<SGeomCacheStaticMeshData>& staticMeshData, uint& currentNodeDataOffset, const char* const pFloorNodeData,
|
|
const char* const pCeilNodeData, const QuatTNS& currentTransform, const float lerpFactor)
|
|
{
|
|
const SGeomCacheStaticNodeData& currentNodeData = staticNodeData[currentNodeIndex];
|
|
|
|
const uint32 floorNodeFlags = *reinterpret_cast<const uint32* const>(pFloorNodeData + currentNodeDataOffset);
|
|
const uint32 ceilNodeFlags = *reinterpret_cast<const uint32* const>(pCeilNodeData + currentNodeDataOffset);
|
|
currentNodeDataOffset += sizeof(uint32);
|
|
|
|
QuatTNS newTransformQuat;
|
|
|
|
// Update transform
|
|
if (currentNodeData.m_transformType == GeomCacheFile::eTransformType_Constant)
|
|
{
|
|
// Matrix from static data
|
|
newTransformQuat = currentTransform * currentNodeData.m_localTransform;
|
|
}
|
|
else
|
|
{
|
|
// Matrix from frame data
|
|
const QuatTNS* const pFloorTransform = reinterpret_cast<const QuatTNS* const>(pFloorNodeData + currentNodeDataOffset);
|
|
const QuatTNS* const pCeilTransform = reinterpret_cast<const QuatTNS* const>(pCeilNodeData + currentNodeDataOffset);
|
|
|
|
QuatTNS interpolatedTransform;
|
|
if (!(floorNodeFlags& GeomCacheFile::eFrameFlags_Hidden) && !(ceilNodeFlags & GeomCacheFile::eFrameFlags_Hidden))
|
|
{
|
|
interpolatedTransform.q = Quat::CreateSlerp(pFloorTransform->q, pCeilTransform->q, lerpFactor);
|
|
interpolatedTransform.t = Vec3::CreateLerp(pFloorTransform->t, pCeilTransform->t, lerpFactor);
|
|
interpolatedTransform.s = Vec3::CreateLerp(pFloorTransform->s, pCeilTransform->s, lerpFactor);
|
|
}
|
|
else if (!(floorNodeFlags & GeomCacheFile::eFrameFlags_Hidden))
|
|
{
|
|
interpolatedTransform = *pFloorTransform;
|
|
}
|
|
else
|
|
{
|
|
interpolatedTransform = *pCeilTransform;
|
|
}
|
|
|
|
newTransformQuat = currentTransform * interpolatedTransform;
|
|
currentNodeDataOffset += sizeof(QuatTNS);
|
|
}
|
|
|
|
Matrix34 newTransformMatrix(newTransformQuat);
|
|
|
|
if (currentNodeData.m_type == GeomCacheFile::eNodeType_Mesh)
|
|
{
|
|
const SGeomCacheStaticMeshData& currentMeshData = staticMeshData[currentNodeData.m_meshOrGeometryIndex];
|
|
|
|
const bool bVisible = ((floorNodeFlags& GeomCacheFile::eFrameFlags_Hidden) == 0);
|
|
|
|
if (bVisible)
|
|
{
|
|
CREGeomCache::SMeshInstance meshInstance;
|
|
meshInstance.m_aabb = currentMeshData.m_aabb;
|
|
meshInstance.m_matrix = newTransformMatrix;
|
|
meshInstance.m_prevMatrix = m_bFilledFrameOnce ? m_nodeMatrices[currentNodeIndex] : newTransformMatrix;
|
|
|
|
#ifndef _RELEASE
|
|
const int debugDrawMode = GetCVars()->e_GeomCacheDebugDrawMode;
|
|
if (debugDrawMode == 0 || debugDrawMode > 2
|
|
|| (debugDrawMode == 1 && currentMeshData.m_animatedStreams != 0)
|
|
|| (debugDrawMode == 2 && currentMeshData.m_animatedStreams == 0))
|
|
#endif
|
|
{
|
|
const uint numMaterials = currentMeshData.m_materialIds.size();
|
|
for (uint i = 0; i < numMaterials; ++i)
|
|
{
|
|
const uint16 materialId = currentMeshData.m_materialIds[i];
|
|
SGeomCacheRenderElementData& data = m_pRenderElements[materialId];
|
|
(*data.m_pCurrentFillData)[currentNodeData.m_meshOrGeometryIndex].m_instances.push_back(meshInstance);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
m_nodeMatrices[currentNodeIndex] = newTransformMatrix;
|
|
|
|
currentNodeIndex += 1;
|
|
|
|
const uint32 numChildren = currentNodeData.m_numChildren;
|
|
for (uint32 i = 0; i < numChildren; ++i)
|
|
{
|
|
UpdateTransformsRec(currentNodeIndex, currentMeshIndex, staticNodeData, staticMeshData, currentNodeDataOffset,
|
|
pFloorNodeData, pCeilNodeData, newTransformQuat, lerpFactor);
|
|
}
|
|
}
|
|
|
|
void CGeomCacheRenderNode::FillRenderObject(const SRendParams& rendParams, [[maybe_unused]] const SRenderingPassInfo& passInfo, _smart_ptr<IMaterial> pMaterial, CRenderObject* pRenderObject)
|
|
{
|
|
FUNCTION_PROFILER_3DENGINE;
|
|
|
|
|
|
pRenderObject->m_pRenderNode = rendParams.pRenderNode;
|
|
pRenderObject->m_fSort = rendParams.fCustomSortOffset;
|
|
pRenderObject->m_fDistance = rendParams.fDistance;
|
|
|
|
pRenderObject->m_ObjFlags |= FOB_DYNAMIC_OBJECT;
|
|
pRenderObject->m_ObjFlags |= rendParams.dwFObjFlags;
|
|
|
|
pRenderObject->m_II.m_AmbColor = rendParams.AmbientColor;
|
|
|
|
if (rendParams.nTextureID >= 0)
|
|
{
|
|
pRenderObject->m_nTextureID = rendParams.nTextureID;
|
|
}
|
|
|
|
pRenderObject->m_II.m_Matrix = *rendParams.pMatrix;
|
|
pRenderObject->m_nClipVolumeStencilRef = rendParams.nClipVolumeStencilRef;
|
|
pRenderObject->m_fAlpha = rendParams.fAlpha;
|
|
pRenderObject->m_DissolveRef = rendParams.nDissolveRef;
|
|
|
|
if (rendParams.nAfterWater)
|
|
{
|
|
pRenderObject->m_ObjFlags |= FOB_AFTER_WATER;
|
|
}
|
|
else
|
|
{
|
|
pRenderObject->m_ObjFlags &= ~FOB_AFTER_WATER;
|
|
}
|
|
|
|
pRenderObject->m_pCurrMaterial = pMaterial;
|
|
}
|
|
|
|
bool CGeomCacheRenderNode::InitializeRenderMeshes()
|
|
{
|
|
const std::vector<SGeomCacheStaticMeshData>& staticMeshData = m_pGeomCache->GetStaticMeshData();
|
|
|
|
const uint numMeshes = staticMeshData.size();
|
|
for (uint i = 0; i < numMeshes; ++i)
|
|
{
|
|
const SGeomCacheStaticMeshData& meshData = staticMeshData[i];
|
|
|
|
IRenderMesh* pRenderMesh = NULL;
|
|
|
|
// Only meshes with constant topology for now. TODO: Add support for heterogeneous meshes.
|
|
if (meshData.m_animatedStreams == 0)
|
|
{
|
|
pRenderMesh = GetGeomCacheManager()->GetMeshManager().GetStaticRenderMesh(meshData.m_hash);
|
|
|
|
assert(pRenderMesh != NULL);
|
|
if (!pRenderMesh)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else if (meshData.m_animatedStreams != 0)
|
|
{
|
|
SGeomCacheRenderMeshUpdateContext updateContext;
|
|
updateContext.m_prevPositions.resize(meshData.m_numVertices, Vec3(0.0f, 0.0f, 0.0f));
|
|
updateContext.m_meshId = i;
|
|
m_renderMeshUpdateContexts.push_back(updateContext);
|
|
pRenderMesh = NULL;
|
|
}
|
|
|
|
m_renderMeshes.push_back(pRenderMesh);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
_smart_ptr<IRenderMesh> CGeomCacheRenderNode::SetupDynamicRenderMesh(SGeomCacheRenderMeshUpdateContext& updateContext)
|
|
{
|
|
FUNCTION_PROFILER_3DENGINE;
|
|
|
|
const std::vector<SGeomCacheStaticMeshData>& staticMeshData = m_pGeomCache->GetStaticMeshData();
|
|
const SGeomCacheStaticMeshData& meshData = staticMeshData[updateContext.m_meshId];
|
|
|
|
// Create zero cleared render mesh
|
|
const uint numMaterials = meshData.m_numIndices.size();
|
|
uint numIndices = 0;
|
|
for (uint i = 0; i < numMaterials; ++i)
|
|
{
|
|
numIndices += meshData.m_numIndices[i];
|
|
}
|
|
|
|
_smart_ptr<IRenderMesh> pRenderMesh = gEnv->pRenderer->CreateRenderMeshInitialized(NULL, meshData.m_numVertices,
|
|
eVF_P3F_C4B_T2F, NULL, numIndices, prtTriangleList,
|
|
"GeomCacheDynamicMesh", m_pGeomCache->GetFilePath(), eRMT_Dynamic);
|
|
|
|
pRenderMesh->LockForThreadAccess();
|
|
|
|
updateContext.m_pIndices = pRenderMesh->GetIndexPtr(FSL_VIDEO_CREATE);
|
|
updateContext.m_pPositions.data = (Vec3*)pRenderMesh->GetPosPtrNoCache(updateContext.m_pPositions.iStride, FSL_VIDEO_CREATE);
|
|
updateContext.m_pColors.data = (UCol*)pRenderMesh->GetColorPtr(updateContext.m_pColors.iStride, FSL_VIDEO_CREATE);
|
|
updateContext.m_pTexcoords.data = (Vec2*)pRenderMesh->GetUVPtrNoCache(updateContext.m_pTexcoords.iStride, FSL_VIDEO_CREATE);
|
|
updateContext.m_pTangents.data = (SPipTangents*)pRenderMesh->GetTangentPtr(updateContext.m_pTangents.iStride, FSL_VIDEO_CREATE);
|
|
updateContext.m_pVelocities.data = (Vec3*)pRenderMesh->GetVelocityPtr(updateContext.m_pVelocities.iStride, FSL_VIDEO_CREATE);
|
|
|
|
CRenderChunk chunk;
|
|
chunk.nNumVerts = meshData.m_numVertices;
|
|
uint32 currentIndexOffset = 0;
|
|
|
|
std::vector<CRenderChunk> chunks;
|
|
chunks.reserve(numMaterials);
|
|
|
|
for (uint i = 0; i < numMaterials; ++i)
|
|
{
|
|
chunk.nFirstIndexId = currentIndexOffset;
|
|
chunk.nNumIndices = meshData.m_numIndices[i];
|
|
chunk.m_nMatID = meshData.m_materialIds[i];
|
|
chunks.push_back(chunk);
|
|
currentIndexOffset += chunk.nNumIndices;
|
|
}
|
|
|
|
pRenderMesh->SetRenderChunks(&chunks[0], numMaterials, false);
|
|
|
|
updateContext.m_pUpdateState = pRenderMesh->SetAsyncUpdateState();
|
|
pRenderMesh->UnLockForThreadAccess();
|
|
|
|
return pRenderMesh;
|
|
}
|
|
|
|
void CGeomCacheRenderNode::SetStandIn(const char* pFilePath, const char* pMaterial)
|
|
{
|
|
m_pStandIn = Get3DEngine()->LoadStatObjAutoRef(pFilePath);
|
|
|
|
if (m_pStandIn)
|
|
{
|
|
m_pStandIn->SetMaterial(GetMatMan()->LoadMaterial(pMaterial));
|
|
}
|
|
}
|
|
|
|
void CGeomCacheRenderNode::SetFirstFrameStandIn(const char* pFilePath, const char* pMaterial)
|
|
{
|
|
m_pFirstFrameStandIn = Get3DEngine()->LoadStatObjAutoRef(pFilePath);
|
|
|
|
if (m_pFirstFrameStandIn)
|
|
{
|
|
m_pFirstFrameStandIn->SetMaterial(GetMatMan()->LoadMaterial(pMaterial));
|
|
}
|
|
}
|
|
|
|
void CGeomCacheRenderNode::SetLastFrameStandIn(const char* pFilePath, const char* pMaterial)
|
|
{
|
|
m_pLastFrameStandIn = Get3DEngine()->LoadStatObjAutoRef(pFilePath);
|
|
|
|
if (m_pLastFrameStandIn)
|
|
{
|
|
m_pLastFrameStandIn->SetMaterial(GetMatMan()->LoadMaterial(pMaterial));
|
|
}
|
|
}
|
|
|
|
void CGeomCacheRenderNode::SetStandInDistance(const float distance)
|
|
{
|
|
m_standInDistance = distance;
|
|
}
|
|
|
|
void CGeomCacheRenderNode::SetStreamInDistance(const float distance)
|
|
{
|
|
m_streamInDistance = distance;
|
|
}
|
|
|
|
CGeomCacheRenderNode::EStandInType CGeomCacheRenderNode::SelectStandIn() const
|
|
{
|
|
const bool bFirstFrame = m_playbackTime == 0.0f;
|
|
const bool bLastFrame = !m_bLooping && (m_pGeomCache ? (m_playbackTime >= m_pGeomCache->GetDuration()) : false);
|
|
|
|
if (bFirstFrame && m_pFirstFrameStandIn && m_pFirstFrameStandIn->GetRenderMesh())
|
|
{
|
|
return eStandInType_FirstFrame;
|
|
}
|
|
else if (bLastFrame && m_pLastFrameStandIn && m_pLastFrameStandIn->GetRenderMesh())
|
|
{
|
|
return eStandInType_LastFrame;
|
|
}
|
|
else if (m_pStandIn && m_pStandIn->GetRenderMesh())
|
|
{
|
|
return eStandInType_Default;
|
|
}
|
|
|
|
return eStandInType_None;
|
|
}
|
|
|
|
IStatObj* CGeomCacheRenderNode::GetStandIn(const EStandInType type) const
|
|
{
|
|
switch (type)
|
|
{
|
|
case eStandInType_Default:
|
|
return m_pStandIn;
|
|
case eStandInType_FirstFrame:
|
|
return m_pFirstFrameStandIn;
|
|
case eStandInType_LastFrame:
|
|
return m_pLastFrameStandIn;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void CGeomCacheRenderNode::DebugDraw(const SGeometryDebugDrawInfo& info, float fExtrudeScale, uint nodeIndex) const
|
|
{
|
|
CryAutoLock<CryCriticalSection> fillLock(m_fillCS);
|
|
|
|
if (!m_bDrawing)
|
|
{
|
|
return;
|
|
}
|
|
|
|
switch (m_standInVisible)
|
|
{
|
|
case eStandInType_None:
|
|
{
|
|
if (m_pGeomCache && m_nodeMatrices.size() > 0)
|
|
{
|
|
const std::vector<SGeomCacheStaticNodeData>& staticNodeData = m_pGeomCache->GetStaticNodeData();
|
|
nodeIndex = std::min(nodeIndex, (uint)(staticNodeData.size() - 1));
|
|
DebugDrawRec(info, fExtrudeScale, nodeIndex, staticNodeData);
|
|
}
|
|
break;
|
|
}
|
|
case eStandInType_Default:
|
|
{
|
|
m_pStandIn->DebugDraw(info, fExtrudeScale);
|
|
break;
|
|
}
|
|
case eStandInType_FirstFrame:
|
|
{
|
|
m_pFirstFrameStandIn->DebugDraw(info, fExtrudeScale);
|
|
break;
|
|
}
|
|
case eStandInType_LastFrame:
|
|
{
|
|
m_pLastFrameStandIn->DebugDraw(info, fExtrudeScale);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CGeomCacheRenderNode::DebugDrawRec(const SGeometryDebugDrawInfo& info, float fExtrudeScale,
|
|
uint& currentNodeIndex, const std::vector<SGeomCacheStaticNodeData>& staticNodeData) const
|
|
{
|
|
const SGeomCacheStaticNodeData& currentNodeData = staticNodeData[currentNodeIndex];
|
|
|
|
if (currentNodeData.m_type == GeomCacheFile::eNodeType_Mesh)
|
|
{
|
|
for (TRenderElementMap::const_iterator iter = m_pRenderElements.begin(); iter != m_pRenderElements.end(); ++iter)
|
|
{
|
|
CREGeomCache* pCREGeomCache = iter->second.m_pRenderElement;
|
|
DynArray<CREGeomCache::SMeshRenderData>* pFillData = pCREGeomCache->GetRenderDataPtr();
|
|
|
|
if (pFillData)
|
|
{
|
|
CREGeomCache::SMeshRenderData& renderData = (*pFillData)[currentNodeData.m_meshOrGeometryIndex];
|
|
IRenderMesh* pRenderMesh = renderData.m_pRenderMesh.get();
|
|
|
|
if (renderData.m_instances.size() > 0 && pRenderMesh)
|
|
{
|
|
Matrix34 pieceMatrix = m_matrix * m_nodeMatrices[currentNodeIndex];
|
|
|
|
SGeometryDebugDrawInfo subInfo = info;
|
|
subInfo.tm = pieceMatrix;
|
|
|
|
pRenderMesh->DebugDraw(subInfo, ~0, fExtrudeScale);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
currentNodeIndex += 1;
|
|
|
|
const uint32 numChildren = currentNodeData.m_numChildren;
|
|
for (uint32 i = 0; i < numChildren; ++i)
|
|
{
|
|
DebugDrawRec(info, fExtrudeScale, currentNodeIndex, staticNodeData);
|
|
}
|
|
}
|
|
|
|
bool CGeomCacheRenderNode::RayIntersectionRec(SRayHitInfo& hitInfo, _smart_ptr<IMaterial> pCustomMtl, uint* pHitNodeIndex,
|
|
uint& currentNodeIndex, const std::vector<SGeomCacheStaticNodeData>& staticNodeData, SRayHitInfo& hitOut, float& fMinDistance) const
|
|
{
|
|
const SGeomCacheStaticNodeData& currentNodeData = staticNodeData[currentNodeIndex];
|
|
|
|
bool bHit = false;
|
|
|
|
if (currentNodeData.m_type == GeomCacheFile::eNodeType_Mesh)
|
|
{
|
|
for (TRenderElementMap::const_iterator iter = m_pRenderElements.begin(); iter != m_pRenderElements.end(); ++iter)
|
|
{
|
|
CREGeomCache* pCREGeomCache = iter->second.m_pRenderElement;
|
|
DynArray<CREGeomCache::SMeshRenderData>* pFillData = pCREGeomCache->GetRenderDataPtr();
|
|
|
|
if (pFillData)
|
|
{
|
|
CREGeomCache::SMeshRenderData& renderData = (*pFillData)[currentNodeData.m_meshOrGeometryIndex];
|
|
IRenderMesh* pRenderMesh = renderData.m_pRenderMesh.get();
|
|
|
|
if (renderData.m_instances.size() > 0 && pRenderMesh)
|
|
{
|
|
Matrix34 pieceMatrix = m_matrix * m_nodeMatrices[currentNodeIndex];
|
|
|
|
AABB meshAABB = m_pGeomCache->GetStaticMeshData()[currentNodeData.m_meshOrGeometryIndex].m_aabb;
|
|
|
|
AABB pieceWorldAABB;
|
|
pieceWorldAABB.SetTransformedAABB(pieceMatrix, meshAABB);
|
|
|
|
Vec3 vOut;
|
|
if (!Intersect::Ray_AABB(hitInfo.inRay, pieceWorldAABB, vOut))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
Matrix34 invPieceMatrix = pieceMatrix.GetInverted();
|
|
|
|
// Transform ray into sub-object local space.
|
|
SRayHitInfo subHitInfo = hitInfo;
|
|
ZeroStruct(subHitInfo);
|
|
subHitInfo.inReferencePoint = invPieceMatrix.TransformPoint(hitInfo.inReferencePoint);
|
|
subHitInfo.inRay.origin = invPieceMatrix.TransformPoint(hitInfo.inRay.origin);
|
|
subHitInfo.inRay.direction = invPieceMatrix.TransformVector(hitInfo.inRay.direction);
|
|
|
|
if (CRenderMeshUtils::RayIntersection(pRenderMesh, subHitInfo, NULL))
|
|
{
|
|
const uint materialId = iter->first;
|
|
_smart_ptr<IMaterial> pMaterial = const_cast<CGeomCacheRenderNode*>(this)->GetMaterial(NULL);
|
|
_smart_ptr<IMaterial> pSubMaterial = pMaterial->GetSafeSubMtl(materialId);
|
|
|
|
if (subHitInfo.nHitMatID == materialId)
|
|
{
|
|
subHitInfo.vHitPos = pieceMatrix.TransformPoint(subHitInfo.vHitPos);
|
|
subHitInfo.fDistance = hitInfo.inReferencePoint.GetDistance(subHitInfo.vHitPos);
|
|
|
|
if (subHitInfo.fDistance < fMinDistance)
|
|
{
|
|
bHit = true;
|
|
fMinDistance = subHitInfo.fDistance;
|
|
hitOut = subHitInfo;
|
|
|
|
hitOut.nHitMatID = materialId;
|
|
if (pSubMaterial)
|
|
{
|
|
hitInfo.nHitSurfaceID = pSubMaterial->GetSurfaceTypeId();
|
|
}
|
|
|
|
if (pHitNodeIndex)
|
|
{
|
|
*pHitNodeIndex = currentNodeIndex;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
currentNodeIndex += 1;
|
|
|
|
const uint32 numChildren = currentNodeData.m_numChildren;
|
|
for (uint32 i = 0; i < numChildren; ++i)
|
|
{
|
|
bHit = RayIntersectionRec(hitInfo, pCustomMtl, pHitNodeIndex, currentNodeIndex, staticNodeData, hitOut, fMinDistance) || bHit;
|
|
}
|
|
|
|
return bHit;
|
|
}
|
|
|
|
#ifndef _RELEASE
|
|
void CGeomCacheRenderNode::DebugRender()
|
|
{
|
|
if (m_pGeomCache && GetCVars()->e_GeomCacheDebugDrawMode == 3)
|
|
{
|
|
const std::vector<SGeomCacheStaticNodeData>& staticNodeData = m_pGeomCache->GetStaticNodeData();
|
|
uint currentNodeIndex = 0;
|
|
InstancingDebugDrawRec(currentNodeIndex, staticNodeData);
|
|
}
|
|
}
|
|
|
|
void CGeomCacheRenderNode::InstancingDebugDrawRec(uint& currentNodeIndex, const std::vector<SGeomCacheStaticNodeData>& staticNodeData)
|
|
{
|
|
CryAutoLock<CryCriticalSection> fillLock(m_fillCS);
|
|
|
|
const SGeomCacheStaticNodeData& currentNodeData = staticNodeData[currentNodeIndex];
|
|
|
|
ColorF colors[] = {
|
|
Col_Aquamarine, Col_Blue, Col_BlueViolet, Col_Brown, Col_CadetBlue, Col_Coral, Col_CornflowerBlue, Col_Cyan,
|
|
Col_DimGrey, Col_FireBrick, Col_ForestGreen, Col_Gold, Col_Goldenrod, Col_Gray, Col_Green, Col_GreenYellow, Col_IndianRed, Col_Khaki,
|
|
Col_LightBlue, Col_LightGray, Col_LightSteelBlue, Col_LightWood, Col_LimeGreen, Col_Magenta, Col_Maroon, Col_MedianWood, Col_MediumAquamarine,
|
|
Col_MediumBlue, Col_MediumForestGreen, Col_MediumGoldenrod, Col_MediumOrchid, Col_MediumSeaGreen, Col_MediumSlateBlue, Col_MediumSpringGreen,
|
|
Col_MediumTurquoise, Col_MediumVioletRed, Col_MidnightBlue, Col_Navy, Col_NavyBlue, Col_Orange, Col_OrangeRed, Col_Orchid, Col_PaleGreen,
|
|
Col_Pink, Col_Plum, Col_Red, Col_Salmon, Col_SeaGreen, Col_Sienna, Col_SkyBlue, Col_SlateBlue, Col_SpringGreen, Col_SteelBlue, Col_Tan,
|
|
Col_Thistle, Col_Transparent, Col_Turquoise, Col_Violet, Col_VioletRed, Col_Wheat, Col_Yellow
|
|
};
|
|
|
|
const uint kNumColors = sizeof(colors) / sizeof(ColorF);
|
|
|
|
if (currentNodeData.m_type == GeomCacheFile::eNodeType_Mesh)
|
|
{
|
|
for (TRenderElementMap::const_iterator iter = m_pRenderElements.begin(); iter != m_pRenderElements.end(); ++iter)
|
|
{
|
|
CREGeomCache* pCREGeomCache = iter->second.m_pRenderElement;
|
|
DynArray<CREGeomCache::SMeshRenderData>* pFillData = pCREGeomCache->GetRenderDataPtr();
|
|
|
|
if (pFillData)
|
|
{
|
|
CREGeomCache::SMeshRenderData& renderData = (*pFillData)[currentNodeData.m_meshOrGeometryIndex];
|
|
IRenderMesh* pRenderMesh = renderData.m_pRenderMesh.get();
|
|
|
|
if (renderData.m_instances.size() > 0 && pRenderMesh)
|
|
{
|
|
Matrix34 pieceMatrix = m_matrix * m_nodeMatrices[currentNodeIndex];
|
|
|
|
SGeometryDebugDrawInfo info;
|
|
info.bNoLines = true;
|
|
info.bExtrude = false;
|
|
info.tm = pieceMatrix;
|
|
|
|
const uint64 kMul = 0x9ddfea08eb382d69ULL;
|
|
uint64 hash = (uint64)alias_cast<UINT_PTR>(pRenderMesh);
|
|
hash ^= (hash >> 47);
|
|
hash *= kMul;
|
|
|
|
info.color = colors[hash % kNumColors];
|
|
|
|
pRenderMesh->DebugDraw(info);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
currentNodeIndex += 1;
|
|
|
|
const uint32 numChildren = currentNodeData.m_numChildren;
|
|
for (uint32 i = 0; i < numChildren; ++i)
|
|
{
|
|
InstancingDebugDrawRec(currentNodeIndex, staticNodeData);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
bool CGeomCacheRenderNode::RayIntersection(SRayHitInfo& hitInfo, _smart_ptr<IMaterial> pCustomMtl, uint* pNodeIndex) const
|
|
{
|
|
CryAutoLock<CryCriticalSection> fillLock(m_fillCS);
|
|
|
|
switch (m_standInVisible)
|
|
{
|
|
case eStandInType_None:
|
|
{
|
|
if (m_pGeomCache && m_nodeMatrices.size() > 0)
|
|
{
|
|
const std::vector<SGeomCacheStaticNodeData>& staticNodeData = m_pGeomCache->GetStaticNodeData();
|
|
|
|
SRayHitInfo hitOut;
|
|
float fMinDistance = std::numeric_limits<float>::max();
|
|
uint currentNodeIndex = 0;
|
|
|
|
if (RayIntersectionRec(hitInfo, pCustomMtl, pNodeIndex, currentNodeIndex, staticNodeData, hitOut, fMinDistance))
|
|
{
|
|
// Restore input ray/reference point.
|
|
hitOut.inReferencePoint = hitInfo.inReferencePoint;
|
|
hitOut.inRay = hitInfo.inRay;
|
|
hitOut.fDistance = fMinDistance;
|
|
|
|
hitInfo = hitOut;
|
|
return true;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case eStandInType_Default:
|
|
{
|
|
return m_pStandIn->RayIntersection(hitInfo, pCustomMtl);
|
|
}
|
|
case eStandInType_FirstFrame:
|
|
{
|
|
return m_pFirstFrameStandIn->RayIntersection(hitInfo, pCustomMtl);
|
|
}
|
|
case eStandInType_LastFrame:
|
|
{
|
|
return m_pLastFrameStandIn->RayIntersection(hitInfo, pCustomMtl);
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
uint CGeomCacheRenderNode::GetNodeCount() const
|
|
{
|
|
if (!m_pGeomCache)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
const uint numNodes = m_pGeomCache->GetStaticNodeData().size();
|
|
return numNodes;
|
|
}
|
|
|
|
Matrix34 CGeomCacheRenderNode::GetNodeTransform(const uint nodeIndex) const
|
|
{
|
|
FUNCTION_PROFILER_3DENGINE;
|
|
|
|
{
|
|
CryAutoLock<CryMutex> lock(m_bTransformsReadyCS);
|
|
while (!m_bTransformReady)
|
|
{
|
|
m_bTransformReadyCV.Wait(m_bTransformsReadyCS);
|
|
}
|
|
}
|
|
|
|
if (nodeIndex >= m_nodeMatrices.size() || !m_pGeomCache)
|
|
{
|
|
return Matrix34(IDENTITY);
|
|
}
|
|
|
|
return m_nodeMatrices[nodeIndex];
|
|
}
|
|
|
|
const char* CGeomCacheRenderNode::GetNodeName(const uint nodeIndex) const
|
|
{
|
|
if (!m_pGeomCache)
|
|
{
|
|
return "";
|
|
}
|
|
|
|
const std::vector<SGeomCacheStaticNodeData>& staticNodeData = m_pGeomCache->GetStaticNodeData();
|
|
return staticNodeData[nodeIndex].m_name.c_str();
|
|
}
|
|
|
|
uint32 CGeomCacheRenderNode::GetNodeNameHash(const uint nodeIndex) const
|
|
{
|
|
if (!m_pGeomCache)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
const std::vector<SGeomCacheStaticNodeData>& staticNodeData = m_pGeomCache->GetStaticNodeData();
|
|
return staticNodeData[nodeIndex].m_nameHash;
|
|
}
|
|
|
|
bool CGeomCacheRenderNode::IsNodeDataValid(const uint nodeIndex) const
|
|
{
|
|
FUNCTION_PROFILER_3DENGINE;
|
|
|
|
{
|
|
CryAutoLock<CryMutex> lock(m_bTransformsReadyCS);
|
|
while (!m_bTransformReady)
|
|
{
|
|
m_bTransformReadyCV.Wait(m_bTransformsReadyCS);
|
|
}
|
|
}
|
|
|
|
if (nodeIndex >= m_nodeMatrices.size() || !m_pGeomCache)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void CGeomCacheRenderNode::InitPhysicalEntity(IPhysicalEntity* pPhysicalEntity, const pe_articgeomparams& params)
|
|
{
|
|
m_pPhysicalEntity = pPhysicalEntity;
|
|
UpdatePhysicalEntity(¶ms);
|
|
}
|
|
|
|
void CGeomCacheRenderNode::UpdatePhysicalEntity(const pe_articgeomparams* pParams)
|
|
{
|
|
if (!m_pPhysicalEntity)
|
|
{
|
|
return;
|
|
}
|
|
|
|
const std::vector<SGeomCacheStaticNodeData>& staticNodeData = m_pGeomCache->GetStaticNodeData();
|
|
const std::vector<phys_geometry*>& physicsGeometries = m_pGeomCache->GetPhysicsGeometries();
|
|
|
|
Matrix34 scaleMatrix = GetMatrix();
|
|
const Vec3 scale = Vec3(scaleMatrix.GetColumn0().GetLength(), scaleMatrix.GetColumn1().GetLength(), scaleMatrix.GetColumn2().GetLength());
|
|
scaleMatrix.SetScale(scale);
|
|
|
|
const uint numNodes = staticNodeData.size();
|
|
for (uint i = 0; i < numNodes; ++i)
|
|
{
|
|
const SGeomCacheStaticNodeData& nodeData = staticNodeData[i];
|
|
if (nodeData.m_type == GeomCacheFile::eNodeType_PhysicsGeometry)
|
|
{
|
|
const Matrix34 nodeTransform = GetNodeTransform(i);
|
|
phys_geometry* pGeometry = physicsGeometries[nodeData.m_meshOrGeometryIndex];
|
|
if (pGeometry)
|
|
{
|
|
Matrix34 nodeMatrix = scaleMatrix * nodeTransform;
|
|
|
|
if (pParams)
|
|
{
|
|
pe_articgeomparams params = *pParams;
|
|
m_pPhysicalEntity->AddGeometry(pGeometry, ¶ms, i);
|
|
}
|
|
|
|
pe_params_part params;
|
|
params.pMtx3x4 = &nodeMatrix;
|
|
params.partid = i;
|
|
m_pPhysicalEntity->SetParams(¶ms);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CGeomCacheRenderNode::UpdatePhysicalMaterials()
|
|
{
|
|
if (m_pPhysicalEntity && m_pMaterial)
|
|
{
|
|
int surfaceTypesId[MAX_SUB_MATERIALS] = { 0 };
|
|
const int numIds = m_pMaterial->FillSurfaceTypeIds(surfaceTypesId);
|
|
|
|
pe_params_part params;
|
|
params.nMats = numIds;
|
|
params.pMatMapping = surfaceTypesId;
|
|
m_pPhysicalEntity->SetParams(¶ms);
|
|
}
|
|
}
|
|
|
|
void CGeomCacheRenderNode::UpdateStreamableComponents(float fImportance, float fDistance, bool bFullUpdate, int nLod, const float fInvScale, bool bDrawNear)
|
|
{
|
|
Matrix34A matrix = GetMatrix();
|
|
|
|
const bool bAllowStandIn = GetCVars()->e_Lods != 0;
|
|
const bool bStreamInGeomCache = !m_pStandIn || (fDistance <= std::max(m_standInDistance, m_streamInDistance)) || !bAllowStandIn;
|
|
if (m_pGeomCache && bStreamInGeomCache)
|
|
{
|
|
m_pGeomCache->UpdateStreamableComponents(fImportance, matrix, this, bFullUpdate);
|
|
}
|
|
|
|
static_cast<CMatInfo*>(m_pMaterial.get())->PrecacheMaterial(fDistance * fInvScale, NULL, bFullUpdate, bDrawNear);
|
|
|
|
PrecacheStandIn(m_pStandIn, fImportance, fDistance, bFullUpdate, nLod, fInvScale, bDrawNear);
|
|
PrecacheStandIn(m_pFirstFrameStandIn, fImportance, fDistance, bFullUpdate, nLod, fInvScale, bDrawNear);
|
|
PrecacheStandIn(m_pLastFrameStandIn, fImportance, fDistance, bFullUpdate, nLod, fInvScale, bDrawNear);
|
|
}
|
|
|
|
void CGeomCacheRenderNode::PrecacheStandIn(IStatObj* pStandIn, float fImportance, float fDistance, bool bFullUpdate, int nLod, const float fInvScale, bool bDrawNear)
|
|
{
|
|
if (pStandIn)
|
|
{
|
|
IStatObj* pLod = pStandIn->GetLodObject(nLod, true);
|
|
if (pLod)
|
|
{
|
|
IObjManager* pObjManager = GetObjManager();
|
|
Matrix34A matrix = GetMatrix();
|
|
static_cast<CStatObj*>(pLod)->UpdateStreamableComponents(fImportance, matrix, bFullUpdate, nLod);
|
|
pObjManager->PrecacheStatObjMaterial(pLod->GetMaterial(), fDistance * fInvScale, pLod, bFullUpdate, bDrawNear);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CGeomCacheRenderNode::OnGeomCacheStaticDataLoaded()
|
|
{
|
|
Initialize();
|
|
}
|
|
|
|
void CGeomCacheRenderNode::OnGeomCacheStaticDataUnloaded()
|
|
{
|
|
Clear(false);
|
|
}
|
|
|
|
#endif
|