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

1070 lines
35 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 : prepare and add render element into renderer
#include "Cry3DEngine_precompiled.h"
#include "StatObj.h"
#include "../RenderDll/Common/Shadow_Renderer.h"
#include "IndexedMesh.h"
#include "VisAreas.h"
#include "GeomQuery.h"
#include "MatMan.h"
#include <CryPath.h>
void CStatObj::Render(const SRendParams& rParams, const SRenderingPassInfo& passInfo)
{
FUNCTION_PROFILER_3DENGINE;
if (m_nFlags & STATIC_OBJECT_HIDDEN)
{
return;
}
#ifndef _RELEASE
int nMaxDrawCalls = GetCVars()->e_MaxDrawCalls;
if (nMaxDrawCalls > 0)
{
// Don't calculate the number of drawcalls every single time a statobj is rendered.
// This creates a flickering effect with objects appearing and disappearing indicating that the limit has been reached.
static int nCurrObjCounter = 0;
if (((nCurrObjCounter++) & 31) == 1)
{
if (GetRenderer()->GetCurrentNumberOfDrawCalls() > nMaxDrawCalls)
{
return;
}
}
}
#endif // _RELEASE
CRenderObject* pObj = GetRenderer()->EF_GetObject_Temp(passInfo.ThreadID());
FillRenderObject(rParams, rParams.pRenderNode, m_pMaterial, NULL, pObj, passInfo);
RenderInternal(pObj, rParams.nSubObjHideMask, rParams.lodValue, passInfo, SRendItemSorter(rParams.rendItemSorter), rParams.bForceDrawStatic);
}
void CStatObj::RenderStreamingDebugInfo([[maybe_unused]] CRenderObject* pRenderObject)
{
#ifndef _RELEASE
// CStatObj * pStreamable = m_pParentObject ? m_pParentObject : this;
IStatObj* pStreamable = m_pLod0 ? m_pLod0 : this;
int nKB = 0;
if (pStreamable->GetRenderMesh())
{
nKB += pStreamable->GetRenderMeshMemoryUsage();
}
for (int nLod = 1; pStreamable->GetLods() && nLod < MAX_STATOBJ_LODS_NUM; nLod++)
{
IStatObj* pLod = (CStatObj*)pStreamable->GetLods()[nLod];
if (!pLod)
{
continue;
}
if (pLod->GetRenderMesh())
{
nKB += pLod->GetRenderMeshMemoryUsage();
}
}
nKB >>= 10;
if (nKB > GetCVars()->e_StreamCgfDebugMinObjSize)
{
// nKB = GetStreamableContentMemoryUsage(true) >> 10;
const char* pComment = 0;
pStreamable = pStreamable->GetParentObject() ? pStreamable->GetParentObject() : pStreamable;
if (!pStreamable->IsUnloadable())
{
pComment = "No stream";
}
else if (!pStreamable->IsLodsAreLoadedFromSeparateFile() && pStreamable->GetLoadedLodsNum())
{
pComment = "Single";
}
else if (pStreamable->GetLoadedLodsNum() > 1)
{
pComment = "Split";
}
else
{
pComment = "No LODs";
}
int nDiff = SATURATEB(int(float(nKB - GetCVars()->e_StreamCgfDebugMinObjSize) / max((int)1, GetCVars()->e_StreamCgfDebugMinObjSize) * 255));
DrawBBoxLabeled(AABB(m_vBoxMin, m_vBoxMax), pRenderObject->m_II.m_Matrix, ColorB(nDiff, 255 - nDiff, 0, 255),
"%.2f mb, %s", 1.f / 1024.f * (float)nKB, pComment);
}
#endif //_RELEASE
}
//////////////////////////////////////////////////////////////////////
void CStatObj::RenderCoverInfo(CRenderObject* pRenderObject)
{
for (int i = 0; i < GetSubObjectCount(); ++i)
{
const IStatObj::SSubObject* subObject = GetSubObject(i);
if (subObject->nType != STATIC_SUB_OBJECT_DUMMY)
{
continue;
}
if (strstr(subObject->name, "$cover") == 0)
{
continue;
}
Vec3 localBoxMin = -subObject->helperSize * 0.5f;
Vec3 localBoxMax = subObject->helperSize * 0.5f;
GetRenderer()->GetIRenderAuxGeom()->DrawAABB(
AABB(localBoxMin, localBoxMax),
pRenderObject->m_II.m_Matrix * subObject->localTM,
true, ColorB(192, 0, 255, 255),
eBBD_Faceted);
}
}
//////////////////////////////////////////////////////////////////////
void CStatObj::FillRenderObject(const SRendParams& rParams, IRenderNode* pRenderNode, _smart_ptr<IMaterial> pMaterial,
SInstancingInfo* pInstInfo, CRenderObject*& pObj, const SRenderingPassInfo& passInfo)
{
// FUNCTION_PROFILER_3DENGINE;
////////////////////////////////////////////////////////////////////////////////////////////////////
// Specify transformation
////////////////////////////////////////////////////////////////////////////////////////////////////
IRenderer* pRend = GetRenderer();
assert(pObj);
if (!pObj)
{
return;
}
pObj->m_pRenderNode = pRenderNode;
pObj->m_fSort = rParams.fCustomSortOffset;
SRenderObjData* pOD = NULL;
if (rParams.pInstance || rParams.m_pVisArea || pInstInfo || rParams.nVisionParams || rParams.nHUDSilhouettesParams || rParams.nSubObjHideMask)
{
pOD = pObj->GetObjData();
pOD->m_uniqueObjectId = reinterpret_cast<uintptr_t>(rParams.pInstance);
pOD->m_nHUDSilhouetteParams = rParams.nHUDSilhouettesParams;
if (rParams.nSubObjHideMask && (m_pMergedRenderMesh != NULL))
{
// Only pass SubObject hide mask for merged objects, because they have a correct correlation between Hide Mask and Render Chunks.
pOD->m_nSubObjHideMask = rParams.nSubObjHideMask;
pObj->m_ObjFlags |= FOB_MESH_SUBSET_INDICES;
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// Set flags
////////////////////////////////////////////////////////////////////////////////////////////////////
pObj->m_ObjFlags |= rParams.dwFObjFlags;
if (rParams.nTextureID >= 0)
{
pObj->m_nTextureID = rParams.nTextureID;
}
assert(rParams.pMatrix);
{
pObj->m_II.m_Matrix = *rParams.pMatrix;
}
pObj->m_II.m_AmbColor = rParams.AmbientColor;
pObj->m_nClipVolumeStencilRef = rParams.nClipVolumeStencilRef;
pObj->m_ObjFlags |= FOB_PARTICLE_SHADOWS;
pObj->m_fAlpha = rParams.fAlpha;
pObj->m_DissolveRef = rParams.nDissolveRef;
////////////////////////////////////////////////////////////////////////////////////////////////////
// Process bending
////////////////////////////////////////////////////////////////////////////////////////////////////
if (pRenderNode && pRenderNode->GetRndFlags() & ERF_RECVWIND)
{
Get3DEngine()->SetupBending(pObj, pRenderNode, m_fRadiusVert, passInfo, false);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// Set render quality
////////////////////////////////////////////////////////////////////////////////////////////////////
pObj->m_nRenderQuality = (uint16)(rParams.fRenderQuality * 65535.0f);
pObj->m_fDistance = rParams.fDistance;
{
//clear, when exchange the state of pLightMapInfo to NULL, the pObj parameters must be update...
pObj->m_nSort = fastround_positive(rParams.fDistance * 2.0f);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// Add render elements
////////////////////////////////////////////////////////////////////////////////////////////////////
if (rParams.pMaterial)
{
pMaterial = rParams.pMaterial;
}
// prepare multi-layer stuff to render object
if (!rParams.nMaterialLayersBlend && rParams.nMaterialLayers)
{
uint8 nFrozenLayer = (rParams.nMaterialLayers & MTL_LAYER_FROZEN) ? MTL_LAYER_FROZEN_MASK : 0;
uint8 nWetLayer = (rParams.nMaterialLayers & MTL_LAYER_WET) ? MTL_LAYER_WET_MASK : 0;
pObj->m_nMaterialLayers = (uint32)(nFrozenLayer << 24) | (nWetLayer << 16);
}
else
{
pObj->m_nMaterialLayers = rParams.nMaterialLayersBlend;
}
if (rParams.nCustomData || rParams.nCustomFlags)
{
if (!pOD)
{
pOD = pObj->GetObjData();
}
pOD->m_nCustomData = rParams.nCustomData;
pOD->m_nCustomFlags = rParams.nCustomFlags;
}
if (rParams.nAfterWater)
{
pObj->m_ObjFlags |= FOB_AFTER_WATER;
}
else
{
pObj->m_ObjFlags &= ~FOB_AFTER_WATER;
}
pObj->m_pRenderNode = rParams.pRenderNode;
pObj->m_pCurrMaterial = pMaterial;
pObj->m_NoDecalReceiver = rParams.NoDecalReceiver;
if (Get3DEngine()->IsTessellationAllowed(pObj, passInfo))
{
// Allow this RO to be tessellated, however actual tessellation will be applied if enabled in material
pObj->m_ObjFlags |= FOB_ALLOW_TESSELLATION;
}
}
//////////////////////////////////////////////////////////////////////////
bool CStatObj::RenderDebugInfo([[maybe_unused]] CRenderObject* pObj, [[maybe_unused]] const SRenderingPassInfo& passInfo)
{
#ifndef _RELEASE
if (!passInfo.IsGeneralPass())
{
return false;
}
IRenderer* pRend = GetRenderer();
_smart_ptr<IMaterial> pMaterial = pObj->m_pCurrMaterial;
IRenderAuxGeom* pAuxGeom = GetRenderer()->GetIRenderAuxGeom();
if (!pAuxGeom)
{
return false;
}
Matrix34 tm = pObj->m_II.m_Matrix;
// Convert "camera space" to "world space"
if (pObj->m_ObjFlags & FOB_NEAREST)
{
tm.AddTranslation(gEnv->pRenderer->GetCamera().GetPosition());
}
AABB bbox(m_vBoxMin, m_vBoxMax);
bool bOnlyBoxes = GetCVars()->e_DebugDraw == -1;
int e_DebugDraw = GetCVars()->e_DebugDraw;
string e_DebugDrawFilter = GetCVars()->e_DebugDrawFilter->GetString();
bool bHasHelperFilter = e_DebugDrawFilter != "";
bool bFiltered = false;
if (e_DebugDraw == 1)
{
string name;
if (!m_szGeomName.empty())
{
name = m_szGeomName.c_str();
}
else
{
name = PathUtil::GetFile(m_szFileName.c_str());
}
bFiltered = name.find(e_DebugDrawFilter) == string::npos;
}
if ((GetCVars()->e_DebugDraw == 1 || bOnlyBoxes) && !bFiltered)
{
if (!m_bMerged)
{
pAuxGeom->DrawAABB(bbox, tm, false, ColorB(0, 255, 255, 128), eBBD_Faceted);
}
else
{
pAuxGeom->DrawAABB(bbox, tm, false, ColorB(255, 200, 0, 128), eBBD_Faceted);
}
}
bool bNoText = e_DebugDraw < 0;
if (e_DebugDraw < 0)
{
e_DebugDraw = -e_DebugDraw;
}
if (m_nRenderTrisCount > 0 && !bOnlyBoxes && !bFiltered)
{ // cgf's name and tris num
int nThisLod = 0;
if (m_pLod0 && m_pLod0->GetLods())
{
for (int i = 0; i < MAX_STATOBJ_LODS_NUM; i++)
{
if (m_pLod0->GetLods()[i] == this)
{
nThisLod = i;
break;
}
}
}
const int nMaxUsableLod = (m_pLod0) ? m_pLod0->GetMaxUsableLod() : m_nMaxUsableLod;
const int nRealNumLods = (m_pLod0) ? m_pLod0->GetLoadedLodsNum() : m_nLoadedLodsNum;
int nNumLods = nRealNumLods;
if (nNumLods > nMaxUsableLod + 1)
{
nNumLods = nMaxUsableLod + 1;
}
int nLod = nThisLod;
if (nLod > nNumLods - 1)
{
nLod = nNumLods - 1;
}
Vec3 pos = tm.TransformPoint((m_vBoxMin + m_vBoxMax) * 0.5f);
float color[4] = {1, 1, 1, 1};
int nMats = m_pRenderMesh ? m_pRenderMesh->GetChunks().size() : 0;
int nRenderMats = 0;
if (nMats)
{
for (int i = 0; i < nMats; ++i)
{
CRenderChunk& rc = m_pRenderMesh->GetChunks()[i];
if (rc.pRE && rc.nNumIndices && rc.nNumVerts && ((rc.m_nMatFlags & MTL_FLAG_NODRAW) == 0))
{
++nRenderMats;
}
}
}
switch (e_DebugDraw)
{
case 1:
{
const char* shortName = "";
if (!m_szGeomName.empty())
{
shortName = m_szGeomName.c_str();
}
else
{
shortName = PathUtil::GetFile(m_szFileName.c_str());
}
if (nNumLods > 1)
{
pRend->DrawLabelEx(pos, 1.3f, color, true, true, "%s\n%d (LOD %d/%d)", shortName, m_nRenderTrisCount, nLod, nNumLods);
}
else
{
pRend->DrawLabelEx(pos, 1.3f, color, true, true, "%s\n%d", shortName, m_nRenderTrisCount);
}
}
break;
case 2:
{
//////////////////////////////////////////////////////////////////////////
// Show colored poly count.
//////////////////////////////////////////////////////////////////////////
int fMult = 1;
int nTris = m_nRenderTrisCount;
ColorB clr = ColorB(0, 0, 0, 255);
if (nTris >= 20000 * fMult)
{
clr = ColorB(255, 0, 0, 255);
}
else if (nTris >= 10000 * fMult)
{
clr = ColorB(255, 255, 0, 255);
}
else if (nTris >= 5000 * fMult)
{
clr = ColorB(0, 255, 0, 255);
}
else if (nTris >= 2500 * fMult)
{
clr = ColorB(0, 255, 255, 255);
}
else if (nTris > 1250 * fMult)
{
clr = ColorB(0, 0, 255, 255);
}
if (pMaterial)
{
pMaterial = GetMatMan()->GetDefaultHelperMaterial();
}
if (pObj)
{
pObj->m_II.m_AmbColor = ColorF(clr.r / 155.0f, clr.g / 155.0f, clr.b / 155.0f, 1);
pObj->m_nMaterialLayers = 0;
pObj->m_ObjFlags |= FOB_SELECTED;
}
if (!bNoText)
{
pRend->DrawLabelEx(pos, 1.3f, color, true, true, "%d", m_nRenderTrisCount);
}
return false;
//////////////////////////////////////////////////////////////////////////
}
case 3:
{
//////////////////////////////////////////////////////////////////////////
// Show Lods
//////////////////////////////////////////////////////////////////////////
ColorB clr;
if (nNumLods < 2)
{
if (m_nRenderTrisCount <= GetCVars()->e_LodMinTtris || nRealNumLods > 1)
{
clr = ColorB(50, 50, 50, 255);
}
else
{
clr = ColorB(255, 0, 0, 255);
float fAngle = gEnv->pTimer->GetFrameStartTime().GetPeriodicFraction(1.0f) * gf_PI2;
clr.g = 127 + (int)(sinf(fAngle) * 120); // flashing color
}
}
else
{
if (nLod == 0)
{
clr = ColorB(255, 0, 0, 255);
}
else if (nLod == 1)
{
clr = ColorB(0, 255, 0, 255);
}
else if (nLod == 2)
{
clr = ColorB(0, 0, 255, 255);
}
else if (nLod == 3)
{
clr = ColorB(0, 255, 255, 255);
}
else if (nLod == 4)
{
clr = ColorB(255, 255, 0, 255);
}
else if (nLod == 5)
{
clr = ColorB(255, 0, 255, 255);
}
else
{
clr = ColorB(255, 255, 255, 255);
}
}
if (pMaterial)
{
pMaterial = GetMatMan()->GetDefaultHelperMaterial();
}
if (pObj)
{
pObj->m_II.m_AmbColor = ColorF(clr.r / 180.0f, clr.g / 180.0f, clr.b / 180.0f, 1);
pObj->m_nMaterialLayers = 0;
pObj->m_ObjFlags |= FOB_SELECTED;
}
// Don't skip objects with single lod (as they should flash)
if (pObj && !bNoText)
{
const int nLod0 = nNumLods > 1 ? GetMinUsableLod() : 0; // Always 0 if one lod
const int maxLod = nNumLods > 1 ? GetMaxUsableLod() : 0; // Always 0 if one lod
clr.toFloat4(color); // Actually use the colours calculated already to indicate lod
const bool bRenderNodeValid(pObj && pObj->m_pRenderNode && ((UINT_PTR)(void*)(pObj->m_pRenderNode) > 0));
IRenderNode* pRN = (IRenderNode*)pObj->m_pRenderNode;
pRend->DrawLabelEx(pos, 1.3f, color, true, true, "%d [%d;%d] (%d/%.1f)",
nLod, nLod0, maxLod,
bRenderNodeValid ? pRN->GetLodRatio() : -1, pObj->m_fDistance);
}
return false;
//////////////////////////////////////////////////////////////////////////
}
case 4:
{
// Show texture usage.
if (m_pRenderMesh)
{
int nTexMemUsage = m_pRenderMesh->GetTextureMemoryUsage(pMaterial);
pRend->DrawLabelEx(pos, 1.3f, color, true, true, "%d", nTexMemUsage / 1024); // in KByte
}
}
break;
case 5:
{
//////////////////////////////////////////////////////////////////////////
// Show Num Render materials.
//////////////////////////////////////////////////////////////////////////
ColorB clr(0, 0, 0, 0);
if (nRenderMats == 1)
{
clr = ColorB(0, 0, 255, 255);
}
else if (nRenderMats == 2)
{
clr = ColorB(0, 255, 255, 255);
}
else if (nRenderMats == 3)
{
clr = ColorB(0, 255, 0, 255);
}
else if (nRenderMats == 4)
{
clr = ColorB(255, 0, 255, 255);
}
else if (nRenderMats == 5)
{
clr = ColorB(255, 255, 0, 255);
}
else if (nRenderMats >= 6)
{
clr = ColorB(255, 0, 0, 255);
}
else if (nRenderMats >= 11)
{
clr = ColorB(255, 255, 255, 255);
}
if (pMaterial)
{
pMaterial = GetMatMan()->GetDefaultHelperMaterial();
}
if (pObj)
{
pObj->m_II.m_AmbColor = ColorF(clr.r / 155.0f, clr.g / 155.0f, clr.b / 155.0f, 1);
pObj->m_nMaterialLayers = 0;
pObj->m_ObjFlags |= FOB_SELECTED;
}
if (!bNoText)
{
pRend->DrawLabelEx(pos, 1.3f, color, true, true, "%d", nRenderMats);
}
}
break;
case 6:
{
if (pMaterial)
{
pMaterial = GetMatMan()->GetDefaultHelperMaterial();
}
ColorF col(1, 1, 1, 1);
if (pObj)
{
pObj->m_nMaterialLayers = 0;
col = pObj->m_II.m_AmbColor;
}
pRend->DrawLabelEx(pos, 1.3f, color, true, true, "%d,%d,%d,%d", (int)(col.r * 255.0f), (int)(col.g * 255.0f), (int)(col.b * 255.0f), (int)(col.a * 255.0f));
}
break;
case 7:
if (m_pRenderMesh)
{
int nTexMemUsage = m_pRenderMesh->GetTextureMemoryUsage(pMaterial);
pRend->DrawLabelEx(pos, 1.3f, color, true, true, "%d,%d,%d", m_nRenderTrisCount, nRenderMats, nTexMemUsage / 1024);
}
break;
case 13:
{
#ifdef SUPPORT_TERRAIN_AO_PRE_COMPUTATIONS
float fOcclusion = GetOcclusionAmount();
pRend->DrawLabelEx(pos, 1.3f, color, true, true, "%.2f", fOcclusion);
#endif
}
break;
case 16:
{
// Draw stats for object selected by debug gun
if (GetRenderer()->IsDebugRenderNode((IRenderNode*)pObj->m_pRenderNode))
{
const char* shortName = PathUtil::GetFile(m_szFileName.c_str());
int texMemUsage = 0;
if (m_pRenderMesh)
{
texMemUsage = m_pRenderMesh->GetTextureMemoryUsage(pMaterial);
}
pAuxGeom->DrawAABB(bbox, tm, false, ColorB(0, 255, 255, 128), eBBD_Faceted);
float yellow[4] = {1.f, 1.f, 0.f, 1.f};
const float yOffset = 165.f;
const float xOffset = 970.f;
if (m_pParentObject == NULL)
{
pRend->Draw2dLabel(xOffset, 40.f, 1.5f, yellow, false, "%s", shortName);
pRend->Draw2dLabel(xOffset, yOffset, 1.5f, color, false,
//"Mesh: %s\n"
"LOD: %d/%d\n"
"Num Instances: %d\n"
"Num Tris: %d\n"
"Tex Mem usage: %.2f kb\n"
"Mesh Mem usage: %.2f kb\n"
"Num Materials: %d\n"
"Mesh Type: %s\n",
//shortName,
nLod, nNumLods,
m_nUsers,
m_nRenderTrisCount,
texMemUsage / 1024.f,
m_nRenderMeshMemoryUsage / 1024.f,
nRenderMats,
m_pRenderMesh->GetTypeName());
}
else
{
for (int i = 0; i < m_pParentObject->SubObjectCount(); i++)
{
//find subobject position
if (m_pParentObject->SubObject(i).pStatObj == this)
{
//only render the header once
if (i == 0)
{
pRend->Draw2dLabel(600.f, 40.f, 2.f, yellow, false, "Debug Gun: %s", shortName);
}
float y = yOffset + ((i % 4) * 150.f);
float x = xOffset - (floor(i / 4.f) * 200.f);
pRend->Draw2dLabel(x, y, 1.5f, color, false,
"Sub Mesh: %s\n"
"LOD: %d/%d\n"
"Num Instances: %d\n"
"Num Tris: %d\n"
"Tex Mem usage: %.2f kb\n"
"Mesh Mem usage: %.2f kb\n"
"Num Materials: %d\n"
"Mesh Type: %s\n",
m_szGeomName.c_str() ? m_szGeomName.c_str() : "UNKNOWN",
nLod, nNumLods,
m_nUsers,
m_nRenderTrisCount,
texMemUsage / 1024.f,
m_nRenderMeshMemoryUsage / 1024.f,
nRenderMats,
m_pRenderMesh->GetTypeName());
break;
}
}
}
}
}
break;
case 19: // Displays the triangle count of physic proxies.
if (!bNoText)
{
int nPhysTrisCount = 0;
for (int j = 0; j < MAX_PHYS_GEOMS_TYPES; ++j)
{
if (GetPhysGeom(j))
{
nPhysTrisCount += GetPhysGeom(j)->pGeom->GetPrimitiveCount();
}
}
if (nPhysTrisCount == 0)
{
color[3] = 0.1f;
}
pRend->DrawLabelEx(pos, 1.3f, color, true, true, "%d", nPhysTrisCount);
}
return false;
case 22:
{
// Show texture usage.
if (m_pRenderMesh)
{
pRend->DrawLabelEx(pos, 1.3f, color, true, true, "[LOD %d: %d]", nLod, m_pRenderMesh->GetVerticesCount());
}
}
break;
case 23:
{
if (pObj && pObj->m_pRenderNode)
{
IRenderNode* pRenderNode = (IRenderNode*)pObj->m_pRenderNode;
const bool bCastsShadow = (pRenderNode->GetRndFlags() & ERF_CASTSHADOWMAPS) != 0;
ColorF clr = bCastsShadow ? ColorF(1.f, 0.f, 0.f, 1.0f) : ColorF(0.f, 1.f, 0.f, 1.f);
int nIndices = 0;
int nIndicesNoShadow = 0;
// figure out how many primitives actually cast shadows
if (pMaterial && bCastsShadow)
{
for (int i = 0; i < nMats; ++i)
{
CRenderChunk& rc = m_pRenderMesh->GetChunks()[i];
if (rc.pRE && rc.nNumIndices && rc.nNumVerts && ((rc.m_nMatFlags & MTL_FLAG_NODRAW) == 0))
{
SShaderItem& ShaderItem = pMaterial->GetShaderItem(rc.m_nMatID);
IRenderShaderResources* pR = ShaderItem.m_pShaderResources;
if (pR && (pR->GetResFlags() & MTL_FLAG_NOSHADOW))
{
nIndicesNoShadow += rc.nNumIndices;
}
nIndices += rc.nNumIndices;
}
}
Vec3 red, green;
ColorF(1.f, 0.f, 0.f, 1.0f).toHSV(red.x, red.y, red.z);
ColorF(0.f, 1.f, 0.f, 1.0f).toHSV(green.x, green.y, green.z);
Vec3 c = Vec3::CreateLerp(red, green, (float)nIndicesNoShadow / max(nIndices, 1));
clr.fromHSV(c.x, c.y, c.z);
pMaterial = GetMatMan()->GetDefaultHelperMaterial();
}
pObj->m_II.m_AmbColor = clr;
pObj->m_nMaterialLayers = 0;
pObj->m_ObjFlags |= FOB_SELECTED;
}
return false;
}
case 24:
case 25:
{
// display a label for this render node if the triangle count equals or exceeds
// e_DebugDrawLodMinTriangles and the object has no lods, or has too few lods
int minTriangleCount = GetCVars()->e_DebugDrawLodMinTriangles;
if (m_nLoadedTrisCount >= minTriangleCount)
{
IRenderNode* pRN = (IRenderNode*)pObj->m_pRenderNode;
const char* shortName = "";
if (!m_szGeomName.empty())
{
shortName = m_szGeomName.c_str();
}
else
{
shortName = PathUtil::GetFile(m_szFileName.c_str());
}
if (nNumLods == 1)
{
color[1] = color[2] = 0.0f;
IRenderer::RNDrawcallsMapNode& drawCallsPerNode = pRend->GetDrawCallsInfoPerNodePreviousFrame();
auto iter = drawCallsPerNode.find(pRN);
if (iter != drawCallsPerNode.end())
{
pRend->DrawLabelEx(pos, 1.3f, color, true, true, "%s (%d)\n%d/%d/%d/%d/%d", shortName, m_nLoadedTrisCount, iter->second.nZpass, iter->second.nGeneral, iter->second.nTransparent, iter->second.nShadows, iter->second.nMisc);
}
else
{
pRend->DrawLabelEx(pos, 1.3f, color, true, true, "%s (%d)", shortName, m_nLoadedTrisCount);
}
}
else if (e_DebugDraw == 25 && nNumLods < MAX_STATOBJ_LODS_NUM) // 25 adds in drawing of objects that should be at a lower lod than exists
{
float lodDistance = sqrt(m_fGeometricMeanFaceArea);
float nextLodDistance = lodDistance * (nNumLods) / (pRN->GetLodRatioNormalized() * gEnv->p3DEngine->GetFrameLodInfo().fTargetSize);
if (pObj->m_fDistance > nextLodDistance)
{
color[0] = color[1] = 0.0f;
pRend->DrawLabelEx(pos, 1.3f, color, true, true, "%s (%d)", shortName, m_nLoadedTrisCount);
}
}
}
}
break;
}
}
if (GetCVars()->e_DebugDraw == 15 && !bOnlyBoxes)
{
// helpers
for (int i = 0; i < (int)m_subObjects.size(); i++)
{
SSubObject* pSubObject = &(m_subObjects[i]);
if (pSubObject->nType == STATIC_SUB_OBJECT_MESH && pSubObject->pStatObj)
{
continue;
}
if (bHasHelperFilter)
{
if (pSubObject->name.find(e_DebugDrawFilter) == string::npos)
{
continue;
}
}
// make object matrix
Matrix34 tMat = tm * pSubObject->tm;
Vec3 pos = tMat.GetTranslation();
// draw axes
float s = 0.02f;
ColorB col(0, 255, 255, 255);
pAuxGeom->DrawAABB(AABB(Vec3(-s, -s, -s), Vec3(s, s, s)), tMat, false, col, eBBD_Faceted);
pAuxGeom->DrawLine(pos + s * tMat.GetColumn1(), col, pos + 3.f * s * tMat.GetColumn1(), col);
// text
float color[4] = {0, 1, 1, 1};
pRend->DrawLabelEx(pos, 1.3f, color, true, true, "%s", pSubObject->name.c_str());
}
}
if (Get3DEngine()->IsDebugDrawListEnabled())
{
I3DEngine::SObjectInfoToAddToDebugDrawList objectInfo;
if (pObj->m_pRenderNode)
{
objectInfo.pName = ((IRenderNode*)pObj->m_pRenderNode)->GetName();
objectInfo.pClassName = ((IRenderNode*)pObj->m_pRenderNode)->GetEntityClassName();
}
else
{
objectInfo.pName = "";
objectInfo.pClassName = "";
}
objectInfo.pFileName = m_szFileName.c_str();
if (m_pRenderMesh && pObj->m_pCurrMaterial)
{
objectInfo.texMemory = m_pRenderMesh->GetTextureMemoryUsage(pObj->m_pCurrMaterial);
}
else
{
objectInfo.texMemory = 0;
}
objectInfo.numTris = m_nRenderTrisCount;
objectInfo.numVerts = m_nLoadedVertexCount;
objectInfo.meshMemory = m_nRenderMeshMemoryUsage;
objectInfo.pMat = &tm;
objectInfo.pBox = &bbox;
objectInfo.type = I3DEngine::DLOT_STATOBJ;
objectInfo.pRenderNode = (IRenderNode*)(pObj->m_pRenderNode);
Get3DEngine()->AddObjToDebugDrawList(objectInfo);
}
#endif //_RELEASE
return false;
}
//
// StatObj functions.
//
float CStatObj::GetExtent(EGeomForm eForm)
{
int nSubCount = m_subObjects.size();
if (!nSubCount)
{
return m_pRenderMesh ? m_pRenderMesh->GetExtent(eForm) : 0.f;
}
CGeomExtent& ext = m_Extents.Make(eForm);
if (!ext)
{
// Create parts for main and sub-objects.
ext.ReserveParts(1 + nSubCount);
ext.AddPart(m_pRenderMesh ? m_pRenderMesh->GetExtent(eForm) : 0.f);
// Evaluate sub-objects.
for (int i = 0; i < nSubCount; i++)
{
IStatObj::SSubObject* pSub = &m_subObjects[i];
if (pSub->nType == STATIC_SUB_OBJECT_MESH && pSub->pStatObj)
{
float fExt = pSub->pStatObj->GetExtent(eForm);
if (eForm == GeomForm_Edges)
{
fExt *= powf(pSub->tm.Determinant(), 0.333f);
}
else if (eForm == GeomForm_Surface)
{
fExt *= powf(pSub->tm.Determinant(), 0.667f);
}
else if (eForm == GeomForm_Volume)
{
fExt *= pSub->tm.Determinant();
}
ext.AddPart(fExt);
}
else
{
ext.AddPart(0.f);
}
}
}
return ext.TotalExtent();
}
void CStatObj::GetRandomPos(PosNorm& ran, EGeomForm eForm) const
{
if (!m_subObjects.empty())
{
CGeomExtent const& ext = m_Extents[eForm];
int iSubObj = ext.RandomPart();
if (iSubObj-- > 0)
{
IStatObj::SSubObject const* pSub = &m_subObjects[iSubObj];
assert(pSub && pSub->pStatObj);
pSub->pStatObj->GetRandomPos(ran, eForm);
ran <<= pSub->tm;
return;
}
}
if (m_pRenderMesh)
{
m_pRenderMesh->GetRandomPos(ran, eForm);
}
else
{
ran.zero();
}
}
void CStatObj::ComputeGeometricMean(SMeshLodInfo& lodInfo)
{
lodInfo.Clear();
lodInfo.fGeometricMean = m_fGeometricMeanFaceArea;
lodInfo.nFaceCount = m_nRenderTrisCount;
if (GetFlags() & STATIC_OBJECT_COMPOUND)
{
for (uint i = 0; i < m_subObjects.size(); ++i)
{
if (m_subObjects[i].nType == STATIC_SUB_OBJECT_MESH && m_subObjects[i].bShadowProxy == false && m_subObjects[i].pStatObj != NULL)
{
SMeshLodInfo subLodInfo;
static_cast<CStatObj*>(m_subObjects[i].pStatObj)->ComputeGeometricMean(subLodInfo);
lodInfo.Merge(subLodInfo);
}
}
}
}
//////////////////////////////////////////////////////////////////////////
void CStatObj::DebugDraw(const SGeometryDebugDrawInfo& info, float fExtrdueScale)
{
if (m_nFlags & STATIC_OBJECT_COMPOUND && !m_bMerged)
{
// Draw sub objects.
for (int i = 0; i < (int)m_subObjects.size(); i++)
{
if (!m_subObjects[i].pStatObj || m_subObjects[i].bHidden || m_subObjects[i].nType != STATIC_SUB_OBJECT_MESH)
{
continue;
}
SGeometryDebugDrawInfo subInfo = info;
subInfo.tm = info.tm * m_subObjects[i].localTM;
m_subObjects[i].pStatObj->DebugDraw(subInfo, fExtrdueScale);
}
}
else if (m_pRenderMesh)
{
m_pRenderMesh->DebugDraw(info, ~0, fExtrdueScale);
}
else
{
// No RenderMesh in here, so probably no geometry in highest LOD, find it in lower LODs
if (m_pLODs)
{
assert(m_nMaxUsableLod < MAX_STATOBJ_LODS_NUM);
for (int nLod = 0; nLod <= (int)m_nMaxUsableLod; nLod++)
{
if (m_pLODs[nLod] && m_pLODs[nLod]->m_pRenderMesh)
{
m_pLODs[nLod]->m_pRenderMesh->DebugDraw(info, ~0, fExtrdueScale);
break;
}
}
}
}
}