/* * 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 "StdAfx.h" #include "StatObj.h" #include "../RenderDll/Common/Shadow_Renderer.h" #include "IndexedMesh.h" #include "VisAreas.h" #include "GeomQuery.h" #include "DeformableNode.h" #include "MatMan.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(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.pFoliage || rParams.pInstance || rParams.m_pVisArea || pInstInfo || rParams.nVisionParams || rParams.nHUDSilhouettesParams || rParams.nSubObjHideMask) { pOD = pObj->GetObjData(); pOD->m_pSkinningData = rParams.pFoliage ? rParams.pFoliage->GetSkinningData(*rParams.pMatrix, passInfo) : NULL; if (pOD->m_pSkinningData) { pObj->m_ObjFlags |= FOB_SKINNED; } 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.pFoliage) { pObj->m_ObjFlags |= FOB_DYNAMIC_OBJECT; } 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(CRenderObject* pObj, 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; } } } } }