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/3dEngine.cpp

5538 lines
172 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 : Implementation of I3DEngine interface methods
#include "Cry3DEngine_precompiled.h"
#include "3dEngine.h"
#include <MathConversion.h>
#include <AzFramework/Terrain/TerrainDataRequestBus.h>
#include <Terrain/Bus/TerrainProviderBus.h>
#include "VisAreas.h"
#include "ObjMan.h"
#include "Ocean.h"
#include <CryPhysicsDeprecation.h>
#include "DecalManager.h"
#include "IndexedMesh.h"
#include "MatMan.h"
#include "CullBuffer.h"
#include "CGF/CGFLoader.h"
#include "CGF/ChunkFileWriters.h"
#include "CGF/ReadOnlyChunkFile.h"
#include "CloudRenderNode.h"
#include "CloudsManager.h"
#include "SkyLightManager.h"
#include "FogVolumeRenderNode.h"
#include "DecalRenderNode.h"
#include "TimeOfDay.h"
#include "LightEntity.h"
#include "FogVolumeRenderNode.h"
#include "ObjectsTree.h"
#include "WaterVolumeRenderNode.h"
#include "DistanceCloudRenderNode.h"
#include "VolumeObjectRenderNode.h"
#include "RenderMeshMerger.h"
#include "DeferredCollisionEvent.h"
#include "OpticsManager.h"
#include "GeomCacheRenderNode.h"
#include "GeomCacheManager.h"
#include "ClipVolumeManager.h"
#include <IRemoteCommand.h>
#include "PostEffectGroup.h"
#include "MainThreadRenderRequestBus.h"
#include "ObjMan.h"
#include "Environment/OceanEnvironmentBus.h"
#include <limits>
#include <CryPath.h>
#if !defined(EXCLUDE_DOCUMENTATION_PURPOSE)
#include "PrismRenderNode.h"
#endif // EXCLUDE_DOCUMENTATION_PURPOSE
#include <AzFramework/IO/FileOperations.h>
#include <AzFramework/Physics/WindBus.h>
#include <AzCore/Interface/Interface.h>
#include <AzCore/Jobs/LegacyJobExecutor.h>
#include <AzCore/Math/MathUtils.h>
#include <AzCore/std/parallel/mutex.h>
#include <MathConversion.h>
// required for LARGE_INTEGER used by QueryPerformanceCounter
#ifdef WIN32
#include <CryWindows.h>
#endif
threadID Cry3DEngineBase::m_nMainThreadId = 0;
bool Cry3DEngineBase::m_bRenderTypeEnabled[eERType_TypesNum];
ISystem* Cry3DEngineBase::m_pSystem = 0;
IRenderer* Cry3DEngineBase::m_pRenderer = 0;
ITimer* Cry3DEngineBase::m_pTimer = 0;
ILog* Cry3DEngineBase::m_pLog = 0;
COcean* Cry3DEngineBase::m_pOcean = nullptr;
CObjManager* Cry3DEngineBase::m_pObjManager = 0;
IConsole* Cry3DEngineBase::m_pConsole = 0;
C3DEngine* Cry3DEngineBase::m_p3DEngine = 0;
CVars* Cry3DEngineBase::m_pCVars = 0;
AZ::IO::IArchive* Cry3DEngineBase::m_pCryPak = nullptr;
IOpticsManager* Cry3DEngineBase::m_pOpticsManager = 0;
CDecalManager* Cry3DEngineBase::m_pDecalManager = 0;
CSkyLightManager* Cry3DEngineBase::m_pSkyLightManager = 0;
CCloudsManager* Cry3DEngineBase::m_pCloudsManager = 0;
CVisAreaManager* Cry3DEngineBase::m_pVisAreaManager = 0;
CClipVolumeManager* Cry3DEngineBase::m_pClipVolumeManager = 0;
CRenderMeshMerger* Cry3DEngineBase::m_pRenderMeshMerger = 0;
CMatMan* Cry3DEngineBase::m_pMatMan = 0;
IStreamedObjectListener* Cry3DEngineBase::m_pStreamListener = 0;
#if defined(USE_GEOM_CACHES)
CGeomCacheManager* Cry3DEngineBase::m_pGeomCacheManager = 0;
#endif
bool Cry3DEngineBase::m_bLevelLoadingInProgress = false;
bool Cry3DEngineBase::m_bIsInRenderScene = false;
int Cry3DEngineBase::m_CpuFlags = 0;
#if !defined(CONSOLE)
bool Cry3DEngineBase::m_bEditor = false;
#endif //!defined(CONSOLE)
ESystemConfigSpec Cry3DEngineBase::m_LightConfigSpec = CONFIG_VERYHIGH_SPEC;
int Cry3DEngineBase::m_arrInstancesCounter[eERType_TypesNum];
#define LAST_POTENTIALLY_VISIBLE_TIME 2
namespace
{
class CLoadLogListener
: public ILoaderCGFListener
{
public:
virtual ~CLoadLogListener(){}
virtual void Warning(const char* format) {Cry3DEngineBase::Warning("%s", format); }
virtual void Error(const char* format) {Cry3DEngineBase::Error("%s", format); }
virtual bool IsValidationEnabled() { return Cry3DEngineBase::GetCVars()->e_StatObjValidate != 0; }
};
}
namespace OceanGlobals
{
float g_oceanLevel = 0.0f;
float g_oceanStep = 1.0f;
AZStd::recursive_mutex g_oceanParamsMutex;
}
//////////////////////////////////////////////////////////////////////
C3DEngine::C3DEngine(ISystem* pSystem)
{
//#if defined(_DEBUG) && defined(WIN32)
// _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
//#endif
// Level info
m_fSunSpecMult = 1.f;
m_bAreaActivationInUse = false;
Cry3DEngineBase::m_nMainThreadId = CryGetCurrentThreadId();
Cry3DEngineBase::m_pSystem = pSystem;
Cry3DEngineBase::m_pRenderer = gEnv->pRenderer;
Cry3DEngineBase::m_pTimer = gEnv->pTimer;
Cry3DEngineBase::m_pLog = gEnv->pLog;
Cry3DEngineBase::m_pConsole = gEnv->pConsole;
Cry3DEngineBase::m_p3DEngine = this;
Cry3DEngineBase::m_pCryPak = gEnv->pCryPak;
Cry3DEngineBase::m_pCVars = 0;
Cry3DEngineBase::m_pRenderMeshMerger = new CRenderMeshMerger;
Cry3DEngineBase::m_pMatMan = new CMatMan;
Cry3DEngineBase::m_pStreamListener = NULL;
Cry3DEngineBase::m_CpuFlags = pSystem->GetCPUFlags();
memset(Cry3DEngineBase::m_arrInstancesCounter, 0, sizeof(Cry3DEngineBase::m_arrInstancesCounter));
#if !defined(CONSOLE)
m_bEditor = gEnv->IsEditor();
#endif
m_pObjectsTree = nullptr;
m_pCVars = new CVars();
Cry3DEngineBase::m_pCVars = m_pCVars;
m_pTimeOfDay = NULL;
m_postEffectGroups = std::make_unique<PostEffectGroupManager>();
m_postEffectBaseGroup = m_postEffectGroups->GetGroup("Base");
if (IPostEffectGroup* pPostEffectsDefaultGroup = m_postEffectGroups->GetGroup(m_defaultPostEffectGroup))
{
pPostEffectsDefaultGroup->SetEnable(true);
}
m_szLevelFolder[0] = 0;
m_pSun = 0;
m_nFlags = 0;
m_pSkyMat = 0;
m_pSkyLowSpecMat = 0;
m_pTerrainWaterMat = 0;
m_nWaterBottomTexId = 0;
m_vSunDir = Vec3(5.f, 5.f, DISTANCE_TO_THE_SUN);
m_vSunDirRealtime = Vec3(5.f, 5.f, DISTANCE_TO_THE_SUN).GetNormalized();
m_nBlackTexID = 0;
// create components
m_pObjManager = CryAlignedNew<CObjManager>();
m_pDecalManager = 0;//new CDecalManager (m_pSystem, this);
m_pCloudsManager = new CCloudsManager;
m_pOpticsManager = 0;
m_pVisAreaManager = 0;
m_pClipVolumeManager = new CClipVolumeManager();
m_pSkyLightManager = CryAlignedNew<CSkyLightManager>();
// create REs
m_pRESky = 0;
m_pREHDRSky = 0;
m_pPhysMaterialEnumerator = 0;
m_fMaxViewDistHighSpec = 8000;
m_fMaxViewDistLowSpec = 1000;
m_fSkyBoxAngle = 0;
m_fSkyBoxStretching = 0;
m_physicsAreaUpdatesHandler = AZStd::make_unique<class PhysicsAreaUpdatesHandler>(m_PhysicsAreaUpdates);
m_bOcean = true;
m_nOceanRenderFlags = 0;
m_bSunShadows = true;
m_nSunAdditionalCascades = 0;
m_CachedShadowsBounds.Reset();
m_nCachedShadowsUpdateStrategy = ShadowMapFrustum::ShadowCacheData::eFullUpdate;
m_fSunClipPlaneRange = 256.0f;
m_fSunClipPlaneRangeShift = 0.0f;
m_nRealLightsNum = m_nDeferredLightsNum = 0;
m_pCoverageBuffer = CryAlignedNew<CCullBuffer>();
m_fLightsHDRDynamicPowerFactor = 0.0f;
m_vHDRFilmCurveParams = Vec4(1.0f, 1.0f, 1.0f, 1.0f);
m_vHDREyeAdaptation = Vec3(0.05f, 0.8f, 0.9f);
m_fHDRBloomAmount = 0.0f;
m_vColorBalance = Vec3(1.0f, 1.0f, 1.0f);
m_fHDRSaturation = 1.0f;
m_vSkyHightlightPos.Set(0, 0, 0);
m_vSkyHightlightCol.Set(0, 0, 0);
m_fSkyHighlightSize = 0;
m_volFogGlobalDensity = 0.02f;
m_volFogGlobalDensityMultiplierLDR = 1.0f;
m_volFogFinalDensityClamp = 1.0f;
m_idMatLeaves = -1;
m_oceanFogColor = 0.2f * Vec3(29.0f, 102.0f, 141.0f) / 255.0f;
m_oceanFogColorShallow = Vec3(0, 0, 0); //0.5f * Vec3( 206.0f, 249.0f, 253.0f ) / 255.0f;
m_oceanFogDensity = 0; //0.2f;
m_oceanCausticsDistanceAtten = 100.0f;
m_oceanCausticDepth = 8.0f;
m_oceanCausticIntensity = 1.0f;
m_oceanWindDirection = 1;
m_oceanWindSpeed = 4.0f;
m_oceanWavesSpeed = 1.0f;
m_oceanWavesAmount = 1.5f;
m_oceanWavesSize = 0.75f;
m_fParticlesAmbientMultiplier = m_fParticlesLightMultiplier = 1.f;
m_fRefreshSceneDataCVarsSumm = -1;
m_nRenderTypeEnableCVarSum = -1;
if (!m_LTPRootFree.pNext)
{
m_LTPRootFree.pNext = &m_LTPRootFree;
m_LTPRootFree.pPrev = &m_LTPRootFree;
}
if (!m_LTPRootUsed.pNext)
{
m_LTPRootUsed.pNext = &m_LTPRootUsed;
m_LTPRootUsed.pPrev = &m_LTPRootUsed;
}
m_bResetRNTmpDataPool = false;
m_fSunDirUpdateTime = 0;
m_vSunDirNormalized.zero();
m_volFogRamp = Vec3(0, 100.0f, 0);
m_volFogShadowRange = Vec3(0.1f, 0, 0);
m_volFogShadowDarkening = Vec3(0.25f, 1, 1);
m_volFogShadowEnable = Vec3(0, 0, 0);
m_volFog2CtrlParams = Vec3(64.0f, 0.0f, 1.0f);
m_volFog2ScatteringParams = Vec3(1.0f, 0.3f, 0.6f);
m_volFog2Ramp = Vec3(0.0f, 0.0f, 0.0f);
m_volFog2Color = Vec3(1.0f, 1.0f, 1.0f);
m_volFog2GlobalDensity = Vec3(0.1f, 1.0f, 0.4f);
m_volFog2HeightDensity = Vec3(0.0f, 1.0f, 0.1f);
m_volFog2HeightDensity2 = Vec3(4000.0f, 0.0001f, 0.95f);
m_volFog2Color1 = Vec3(1.0f, 1.0f, 1.0f);
m_volFog2Color2 = Vec3(1.0f, 1.0f, 1.0f);
m_nightSkyHorizonCol = Vec3(0, 0, 0);
m_nightSkyZenithCol = Vec3(0, 0, 0);
m_nightSkyZenithColShift = 0;
m_nightSkyStarIntensity = 0;
m_moonDirection = Vec3(0, 0, 0);
m_nightMoonCol = Vec3(0, 0, 0);
m_nightMoonSize = 0;
m_nightMoonInnerCoronaCol = Vec3(0, 0, 0);
m_nightMoonInnerCoronaScale = 1.0f;
m_nightMoonOuterCoronaCol = Vec3(0, 0, 0);
m_nightMoonOuterCoronaScale = 1.0f;
m_moonRotationLatitude = 0;
m_moonRotationLongitude = 0;
m_skyboxMultiplier = 1.0f;
m_dayNightIndicator = 1.0f;
m_fogColor2 = Vec3(0, 0, 0);
m_fogColorRadial = Vec3(0, 0, 0);
m_volFogHeightDensity = Vec3(0, 1, 0);
m_volFogHeightDensity2 = Vec3(4000.0f, 0, 0);
m_volFogGradientCtrl = Vec3(1, 1, 1);
m_vFogColor = Vec3(1.0f, 1.0f, 1.0f);
m_vAmbGroundCol = Vec3(0.0f, 0.0f, 0.0f);
m_dawnStart = 350.0f / 60.0f;
m_dawnEnd = 360.0f / 60.0f;
m_duskStart = 12.0f + 360.0f / 60.0f;
m_duskEnd = 12.0f + 370.0f / 60.0f;
m_fCloudShadingSunLightMultiplier = 0;
m_fCloudShadingSkyLightMultiplier = 0;
m_vCloudShadingCustomSunColor = Vec3(0, 0, 0);
m_vCloudShadingCustomSkyColor = Vec3(0, 0, 0);
m_vPrevMainFrameCamPos.Set(-1000000.f, -1000000.f, -1000000.f);
m_fAverageCameraSpeed = 0;
m_vAverageCameraMoveDir = Vec3(0);
m_bContentPrecacheRequested = false;
m_bTerrainTextureStreamingInProgress = false;
m_bLayersActivated = false;
m_eShadowMode = ESM_NORMAL;
ClearDebugFPSInfo();
m_fMaxViewDistScale = 1.f;
m_ptexIconLowMemoryUsage = NULL;
m_ptexIconAverageMemoryUsage = NULL;
m_ptexIconHighMemoryUsage = NULL;
m_ptexIconEditorConnectedToConsole = NULL;
m_pScreenshotCallback = 0;
m_bInShutDown = false;
m_bInUnload = false;
m_bInLoad = false;
m_nCloudShadowTexId = 0;
m_nNightMoonTexId = 0;
m_pDeferredPhysicsEventManager = new CDeferredPhysicsEventManager();
#if defined(USE_GEOM_CACHES)
m_pGeomCacheManager = new CGeomCacheManager();
#endif
m_LightVolumesMgr.Init();
m_pBreakableBrushHeap = NULL;
m_nWindSamplePositions = 0;
m_pWindSamplePositions = NULL;
m_fZoomFactor = 0.0f;
m_fAmbMaxHeight = 0.0f;
m_fAmbMinHeight = 0.0f;
m_pLightQuality = NULL;
m_fSaturation = 0.0f;
m_fGsmRange = 0.0f;
m_fGsmRangeStep = 0.0f;
m_fShadowsConstBias = 0.0f;
m_fShadowsSlopeBias = 0.0f;
m_nCustomShadowFrustumCount = 0;
m_bHeightMapAoEnabled = false;
m_bendingPoolIdx = 0;
m_levelLoaded = false;
}
//////////////////////////////////////////////////////////////////////
C3DEngine::~C3DEngine()
{
m_bInShutDown = true;
m_bInUnload = true;
m_bInLoad = false;
CheckMemoryHeap();
ShutDown();
delete m_pTimeOfDay;
delete m_pDecalManager;
delete m_pVisAreaManager;
SAFE_DELETE(m_pClipVolumeManager);
CryAlignedDelete(m_pCoverageBuffer);
m_pCoverageBuffer = 0;
CryAlignedDelete(m_pSkyLightManager);
m_pSkyLightManager = 0;
SAFE_DELETE(m_pObjectsTree);
// delete m_pSceneTree;
delete m_pRenderMeshMerger;
delete m_pMatMan;
m_pMatMan = 0;
delete m_pCloudsManager;
delete m_pCVars;
delete m_pDeferredPhysicsEventManager;
}
bool C3DEngine::CheckMinSpec(uint32 nMinSpec)
{
return Cry3DEngineBase::CheckMinSpec(nMinSpec);
}
bool C3DEngine::Init()
{
m_pOpticsManager = new COpticsManager;
m_pSystem->SetIOpticsManager(m_pOpticsManager);
for (int i = 0; i < eERType_TypesNum; i++)
{
m_bRenderTypeEnabled[i] = true;
}
UpdateRenderTypeEnableLookup();
// Allocate the temporary pool used for allocations during streaming and loading
const size_t tempPoolSize = static_cast<size_t>(GetCVars()->e_3dEngineTempPoolSize) << 10;
AZ_Assert(tempPoolSize != 0, "Temp pool size should not be 0.");
{
if (!CTemporaryPool::Initialize(tempPoolSize))
{
AZ_Assert(false, "Could not initialize initialize temporary pool for 3D Engine startup.");
return false;
}
}
SFrameLodInfo frameLodInfo;
frameLodInfo.fLodRatio = GetCVars()->e_LodRatio;
frameLodInfo.fTargetSize = GetCVars()->e_LodFaceAreaTargetSize;
AZ_Assert(frameLodInfo.fTargetSize > 0.f, "FrameLodInfo target size should be greater than 0.");
if (frameLodInfo.fTargetSize <= 0.f)
{
frameLodInfo.fTargetSize = 1.f;
}
frameLodInfo.nMinLod = GetCVars()->e_LodMin;
frameLodInfo.nMaxLod = GetCVars()->e_LodMax;
if (GetCVars()->e_Lods == 0)
{
frameLodInfo.nMinLod = 0;
frameLodInfo.nMaxLod = 0;
}
SetFrameLodInfo(frameLodInfo);
return true;
}
bool C3DEngine::IsCameraAnd3DEngineInvalid(const SRenderingPassInfo& passInfo, const char* szCaller)
{
const CCamera& rCamera = passInfo.GetCamera();
const float MAX_M23_REPORTED = 3000000.f; // MAT => upped from 100,000 which spammed this message on spear and cityhall. Really should stop editor generating
// water levels that trigger this message.
if (!_finite(rCamera.GetMatrix().m03) || !_finite(rCamera.GetMatrix().m13) || !_finite(rCamera.GetMatrix().m23) || GetMaxViewDistance() <= 0 ||
rCamera.GetMatrix().m23 < -MAX_M23_REPORTED || rCamera.GetMatrix().m23 > MAX_M23_REPORTED || rCamera.GetFov() < 0.0001f || rCamera.GetFov() > gf_PI)
{
Error("Bad camera passed to 3DEngine from %s: Pos=(%.1f, %.1f, %.1f), Fov=%.1f, MaxViewDist=%.1f. Maybe the water level is too extreme.",
szCaller,
rCamera.GetMatrix().m03, rCamera.GetMatrix().m13, rCamera.GetMatrix().m23,
rCamera.GetFov(), _finite(rCamera.GetMatrix().m03) ? GetMaxViewDistance() : 0);
return true;
}
return false;
}
void C3DEngine::OnFrameStart()
{
FUNCTION_PROFILER_3DENGINE;
m_nRenderWorldUSecs = 0;
m_pDeferredPhysicsEventManager->Update();
m_bendingPoolIdx = (m_bendingPoolIdx + 1) % NUM_BENDING_POOLS;
m_bendingPool[m_bendingPoolIdx].resize(0);
#if defined(USE_GEOM_CACHES)
if (!gEnv->IsDedicated())
{
if (m_pGeomCacheManager)
{
m_pGeomCacheManager->StreamingUpdate();
}
}
#endif
//update texture load handlers
for (TTextureLoadHandlers::iterator iter = m_textureLoadHandlers.begin(); iter != m_textureLoadHandlers.end(); iter++)
{
(*iter)->Update();
}
}
float GetOceanLevelCallback(int ix, int iy)
{
using namespace OceanGlobals;
return OceanToggle::IsActive() ? OceanRequest::GetAccurateOceanHeight(Vec3(ix * g_oceanStep, iy * g_oceanStep, g_oceanLevel)) : gEnv->p3DEngine->GetAccurateOceanHeight(Vec3(ix * g_oceanStep, iy * g_oceanStep, g_oceanLevel));
}
unsigned char GetOceanSurfTypeCallback([[maybe_unused]] int ix, [[maybe_unused]] int iy)
{
return 0;
}
void C3DEngine::Update()
{
FUNCTION_PROFILER_3DENGINE_LEGACYONLY;
AZ_TRACE_METHOD();
ProcessAsyncStaticObjectLoadRequests();
m_LightConfigSpec = (ESystemConfigSpec)GetCurrentLightSpec();
if (GetObjManager())
{
GetObjManager()->ClearStatObjGarbage();
}
if (m_pDecalManager)
{
m_pDecalManager->Update(GetTimer()->GetFrameTime());
}
if (GetCVars()->e_PrecacheLevel == 3)
{
PrecacheLevel(true, 0, 0);
}
DebugDraw_Draw();
ProcessCVarsChange();
{
using namespace OceanGlobals;
AZStd::lock_guard<decltype(g_oceanParamsMutex)> lock(g_oceanParamsMutex);
g_oceanLevel = OceanToggle::IsActive() ? OceanRequest::GetOceanLevel() : GetWaterLevel();
}
CRenderMeshUtils::ClearHitCache();
CleanUpOldDecals();
CDecalRenderNode::ResetDecalUpdatesCounter();
if (m_pBreakableBrushHeap)
{
m_pBreakableBrushHeap->Cleanup();
}
// make sure all jobs from the previous frame have finished
threadID nThreadID;
gEnv->pRenderer->EF_Query(EFQ_RenderThreadList, nThreadID);
gEnv->pRenderer->GetFinalizeRendItemJobExecutor(nThreadID)->WaitForCompletion();
gEnv->pRenderer->GetFinalizeShadowRendItemJobExecutor(nThreadID)->WaitForCompletion();
UpdateRNTmpDataPool(m_bResetRNTmpDataPool);
m_bResetRNTmpDataPool = false;
m_PhysicsAreaUpdates.GarbageCollect();
}
void C3DEngine::Tick()
{
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::System);
// make sure all jobs from the previous frame have finished (also in Tick since Update is not called during loading)
threadID nThreadID = 0;
gEnv->pRenderer->EF_Query(EFQ_RenderThreadList, nThreadID);
gEnv->pRenderer->GetFinalizeRendItemJobExecutor(nThreadID)->WaitForCompletion();
gEnv->pRenderer->GetFinalizeShadowRendItemJobExecutor(nThreadID)->WaitForCompletion();
AZ::MainThreadRenderRequestBus::ExecuteQueuedEvents();
AZ::MaterialNotificationEventBus::ExecuteQueuedEvents();
// clear stored cameras from last frame
m_RenderingPassCameras[nThreadID].resize(0);
}
void C3DEngine::ProcessCVarsChange()
{
static int nObjectLayersActivation = -1;
if (nObjectLayersActivation != GetCVars()->e_ObjectLayersActivation)
{
if (GetCVars()->e_ObjectLayersActivation == 2)
{
ActivateObjectsLayer(~0, true, true, true, true, "ALL_OBJECTS");
}
if (GetCVars()->e_ObjectLayersActivation == 3)
{
ActivateObjectsLayer(~0, false, true, true, true, "ALL_OBJECTS");
}
nObjectLayersActivation = GetCVars()->e_ObjectLayersActivation;
}
float fNewCVarsSumm =
GetCVars()->e_ShadowsCastViewDistRatio +
GetCVars()->e_Dissolve +
GetFloatCVar(e_DissolveDistMin) +
GetFloatCVar(e_DissolveDistMax) +
GetFloatCVar(e_DissolveDistband) +
GetCVars()->e_ViewDistRatio +
GetCVars()->e_ViewDistMin +
GetCVars()->e_ViewDistRatioDetail +
GetCVars()->e_DefaultMaterial +
GetGeomDetailScreenRes() +
GetCVars()->e_Portals +
GetCVars()->e_DebugDraw +
GetFloatCVar(e_ViewDistCompMaxSize) +
GetCVars()->e_DecalsDefferedStatic +
GetRenderer()->GetWidth();
if (m_fRefreshSceneDataCVarsSumm != -1 && m_fRefreshSceneDataCVarsSumm != fNewCVarsSumm)
{
UpdateStatInstGroups();
AZ::Aabb terrainAabb = AZ::Aabb::CreateFromPoint(AZ::Vector3::CreateZero());
AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult(terrainAabb, &AzFramework::Terrain::TerrainDataRequests::GetTerrainAabb);
float terrainSize = AZ::GetMax(terrainAabb.GetXExtent(), terrainAabb.GetYExtent());
// re-register every instance in level
constexpr float UNREASONABLY_SMALL_TERRAIN_SIZE = 1.0f;
constexpr float VERY_LARGE_TERRAIN_SIZE = 16.0f * 1024.0f;
if (terrainSize < UNREASONABLY_SMALL_TERRAIN_SIZE)
{
//Only happens when the runtime terrain system was excluded from this build.
terrainSize = VERY_LARGE_TERRAIN_SIZE;
}
GetObjManager()->ReregisterEntitiesInArea(
Vec3(-terrainSize, -terrainSize, -terrainSize),
Vec3(terrainSize * 2.f, terrainSize * 2.f, terrainSize * 2.f));
// refresh vegetation properties
UpdateStatInstGroups();
// force refresh of temporary data associated with visible objects
MarkRNTmpDataPoolForReset();
}
m_fRefreshSceneDataCVarsSumm = fNewCVarsSumm;
int nRenderTypeEnableCVarSum =
(GetCVars()->e_Entities << 2);
if (m_nRenderTypeEnableCVarSum != nRenderTypeEnableCVarSum)
{
m_nRenderTypeEnableCVarSum = nRenderTypeEnableCVarSum;
UpdateRenderTypeEnableLookup();
}
{
float fNewCVarsSumm2 =
GetCVars()->e_LodRatio;
static float fCVarsSumm2 = fNewCVarsSumm2;
if (fCVarsSumm2 != fNewCVarsSumm2)
{
MarkRNTmpDataPoolForReset();
fCVarsSumm2 = fNewCVarsSumm2;
}
}
SFrameLodInfo frameLodInfo;
frameLodInfo.fLodRatio = GetCVars()->e_LodRatio;
frameLodInfo.fTargetSize = GetCVars()->e_LodFaceAreaTargetSize;
CRY_ASSERT(frameLodInfo.fTargetSize > 0.f);
if (frameLodInfo.fTargetSize <= 0.f)
{
frameLodInfo.fTargetSize = 1.f;
}
frameLodInfo.nMinLod = GetCVars()->e_LodMin;
frameLodInfo.nMaxLod = GetCVars()->e_LodMax;
if (GetCVars()->e_Lods == 0)
{
frameLodInfo.nMinLod = 0;
frameLodInfo.nMaxLod = 0;
}
SetFrameLodInfo(frameLodInfo);
}
//////////////////////////////////////////////////////////////////////
void C3DEngine::ShutDown()
{
if (GetRenderer() != GetSystem()->GetIRenderer())
{
CryFatalError("Renderer was deallocated before I3DEngine::ShutDown() call");
}
UnlockCGFResources();
UnloadLevel();
#if defined(USE_GEOM_CACHES)
delete m_pGeomCacheManager;
m_pGeomCacheManager = 0;
#endif
if (m_pOpticsManager)
{
delete m_pOpticsManager;
m_pOpticsManager = 0;
m_pSystem->SetIOpticsManager(m_pOpticsManager);
}
CryAlignedDelete(m_pObjManager);
m_pObjManager = 0;
// Free the temporary pool's underlying storage
// and reset the pool
if (!CTemporaryPool::Shutdown())
{
CryFatalError("C3DEngine::Shutdown() could not shutdown temporary pool");
}
COctreeNode::Shutdown();
}
#ifndef _RELEASE
void C3DEngine::ProcessStreamingLatencyTest(const CCamera& camIn, CCamera& camOut, const SRenderingPassInfo& passInfo)
{
static float fSQTestOffset = 0;
static PodArray<ITexture*> arrTestTextures;
static ITexture* pTestTexture = 0;
static ITexture* pLastNotReadyTexture = 0;
static float fStartTime = 0;
static float fDelayStartTime = 0;
static size_t nMaxTexUsage = 0;
static int nOpenRequestCount = 0;
SStreamEngineOpenStats stats;
gEnv->pSystem->GetStreamEngine()->GetStreamingOpenStatistics(stats);
if (stats.nOpenRequestCount > nOpenRequestCount)
{
nOpenRequestCount = stats.nOpenRequestCount;
}
else
{
nOpenRequestCount = max(0, nOpenRequestCount + stats.nOpenRequestCount) / 2;
}
ICVar* pTSFlush = GetConsole()->GetCVar("r_TexturesStreamingDebug");
if (GetCVars()->e_SQTestBegin == 1)
{ // Init waiting few seconds until streaming is stabilized and all required textures are loaded
PrintMessage("======== Starting streaming latency test ========");
fDelayStartTime = GetCurTimeSec();
nMaxTexUsage = 0;
GetCVars()->e_SQTestBegin = 2;
PrintMessage("Waiting %.1f seconds and zero requests and no camera movement", GetCVars()->e_SQTestDelay);
if (ICVar* pPart = GetConsole()->GetCVar("e_Particles"))
{
pPart->Set(0);
}
if (ICVar* pAI = GetConsole()->GetCVar("sys_AI"))
{
pAI->Set(0);
}
}
else if (GetCVars()->e_SQTestBegin == 2)
{ // Perform waiting
if (GetCurTimeSec() - fDelayStartTime > GetCVars()->e_SQTestDelay && !nOpenRequestCount && m_fAverageCameraSpeed < .01f)
{
pTSFlush->Set(0);
GetCVars()->e_SQTestBegin = 3;
}
else
{
pTSFlush->Set(3);
}
}
else if (GetCVars()->e_SQTestBegin == 3)
{ // Build a list of all important loaded textures
PrintMessage("Collect information about loaded textures");
fSQTestOffset = (float)GetCVars()->e_SQTestDistance;
arrTestTextures.Clear();
SRendererQueryGetAllTexturesParam param;
GetRenderer()->EF_Query(EFQ_GetAllTextures, param);
if (param.pTextures)
{
for (uint32 i = 0; i < param.numTextures; i++)
{
ITexture* pTexture = param.pTextures[i];
if (pTexture->GetAccessFrameId() > (int)(passInfo.GetMainFrameID() - 4))
{
if (pTexture->GetMinLoadedMip() <= GetCVars()->e_SQTestMip)
{
if (pTexture->IsStreamable())
{
if (pTexture->GetWidth() * pTexture->GetHeight() >= 256 * 256)
{
arrTestTextures.Add(pTexture);
if (strstr(pTexture->GetName(), GetCVars()->e_SQTestTextureName->GetString()))
{
pTestTexture = pTexture;
PrintMessage("Test texture name: %s", pTexture->GetName());
}
}
}
}
}
}
}
GetRenderer()->EF_Query(EFQ_GetAllTexturesRelease, param);
PrintMessage("%d test textures found", arrTestTextures.Count());
PrintMessage("Teleporting camera to offset position");
GetCVars()->e_SQTestBegin = 4;
}
else if (GetCVars()->e_SQTestBegin == 4)
{ // Init waiting few seconds until streaming is stabilized and all required textures are loaded
fDelayStartTime = GetCurTimeSec();
GetCVars()->e_SQTestBegin = 5;
PrintMessage("Waiting %.1f seconds and zero requests and no camera movement", GetCVars()->e_SQTestDelay);
}
else if (GetCVars()->e_SQTestBegin == 5)
{ // Move camera to offset position and perform waiting
Matrix34 mat = camIn.GetMatrix();
Vec3 vPos = camIn.GetPosition() - camIn.GetViewdir() * fSQTestOffset;
mat.SetTranslation(vPos);
camOut.SetMatrix(mat);
if (GetCurTimeSec() - fDelayStartTime > GetCVars()->e_SQTestDelay && !nOpenRequestCount && m_fAverageCameraSpeed < .01f)
{
PrintMessage("Begin camera movement");
GetCVars()->e_SQTestBegin = 6;
pTSFlush->Set(0);
}
else
{
pTSFlush->Set(3);
}
}
else if (GetCVars()->e_SQTestBegin == 6)
{ // Process camera movement from offset position to test point
Matrix34 mat = camIn.GetMatrix();
Vec3 vPos = camIn.GetPosition() - camIn.GetViewdir() * fSQTestOffset;
mat.SetTranslation(vPos);
camOut.SetMatrix(mat);
fSQTestOffset -= GetTimer()->GetFrameTime() * (float)GetCVars()->e_SQTestMoveSpeed;
STextureStreamingStats statsTex(true);
m_pRenderer->EF_Query(EFQ_GetTexStreamingInfo, statsTex);
nMaxTexUsage = max(nMaxTexUsage, statsTex.nRequiredStreamedTexturesSize);
if (fSQTestOffset <= 0)
{
PrintMessage("Finished camera movement");
fStartTime = GetCurTimeSec();
PrintMessage("Waiting for %d textures to stream in ...", arrTestTextures.Count());
GetCVars()->e_SQTestBegin = 7;
pLastNotReadyTexture = 0;
}
}
else if (GetCVars()->e_SQTestBegin == 7)
{ // Wait until test all needed textures are loaded again
STextureStreamingStats statsTex(true);
m_pRenderer->EF_Query(EFQ_GetTexStreamingInfo, statsTex);
nMaxTexUsage = max(nMaxTexUsage, statsTex.nRequiredStreamedTexturesSize);
if (pTestTexture)
{
if (pTestTexture->GetMinLoadedMip() <= GetCVars()->e_SQTestMip)
{
PrintMessage("BINGO: Selected test texture loaded in %.1f sec", GetCurTimeSec() - fStartTime);
pTestTexture = NULL;
if (!arrTestTextures.Count())
{
GetCVars()->e_SQTestBegin = 0;
GetConsole()->GetCVar("e_SQTestBegin")->Set(0);
}
}
}
if (arrTestTextures.Count())
{
int nFinishedNum = 0;
for (int i = 0; i < arrTestTextures.Count(); i++)
{
if (arrTestTextures[i]->GetMinLoadedMip() <= GetCVars()->e_SQTestMip)
{
nFinishedNum++;
}
else
{
pLastNotReadyTexture = arrTestTextures[i];
}
}
if (nFinishedNum == arrTestTextures.Count())
{
PrintMessage("BINGO: %d of %d test texture loaded in %.1f sec", nFinishedNum, arrTestTextures.Count(), GetCurTimeSec() - fStartTime);
if (pLastNotReadyTexture)
{
PrintMessage("LastNotReadyTexture: %s [%d x %d]", pLastNotReadyTexture->GetName(), pLastNotReadyTexture->GetWidth(), pLastNotReadyTexture->GetHeight());
}
PrintMessage("MaxTexUsage: %" PRISIZE_T " MB", nMaxTexUsage / 1024 / 1024);
arrTestTextures.Clear();
GetCVars()->e_SQTestBegin = 0;
GetConsole()->GetCVar("e_SQTestBegin")->Set(0);
m_arrProcessStreamingLatencyTestResults.Add(GetCurTimeSec() - fStartTime);
m_arrProcessStreamingLatencyTexNum.Add(nFinishedNum);
if (GetCVars()->e_SQTestCount == 0)
{
const char* testResultsFile = "@usercache@/TestResults/Streaming_Latency_Test.xml";
AZ::IO::HandleType resultsFile = gEnv->pCryPak->FOpen(testResultsFile, "wb");
if (resultsFile != AZ::IO::InvalidHandle)
{
float fAverTime = 0;
for (int i = 0; i < m_arrProcessStreamingLatencyTestResults.Count(); i++)
{
fAverTime += m_arrProcessStreamingLatencyTestResults[i];
}
fAverTime /= m_arrProcessStreamingLatencyTestResults.Count();
int nAverTexNum = 0;
for (int i = 0; i < m_arrProcessStreamingLatencyTexNum.Count(); i++)
{
nAverTexNum += m_arrProcessStreamingLatencyTexNum[i];
}
nAverTexNum /= m_arrProcessStreamingLatencyTexNum.Count();
AZ::IO::Print(resultsFile, "<phase name=\"Streaming_Latency_Test\">\n"
"<metrics name=\"Streaming\">\n"
"<metric name=\"AvrLatency\" value=\"%.1f\"/>\n"
"<metric name=\"AvrTexNum\" value=\"%d\"/>\n"
"</metrics>\n"
"</phase>\n",
fAverTime,
nAverTexNum);
gEnv->pCryPak->FClose(resultsFile);
}
if (GetCVars()->e_SQTestExitOnFinish)
{
GetSystem()->Quit();
}
}
}
else if ((passInfo.GetMainFrameID() & 31) == 0)
{
PrintMessage("Waiting: %d of %d test texture loaded in %.1f sec", nFinishedNum, arrTestTextures.Count(), GetCurTimeSec() - fStartTime);
}
}
}
}
#endif
//////////////////////////////////////////////////////////////////////
void C3DEngine::UpdateRenderingCamera([[maybe_unused]] const char* szCallerName, const SRenderingPassInfo& passInfo)
{
CCamera newCam = passInfo.GetCamera();
if (passInfo.IsGeneralPass())
{
SVOGILegacyRequestBus::Broadcast(&SVOGILegacyRequests::UpdateVoxelData);
}
if IsCVarConstAccess(constexpr) (bool(GetFloatCVar(e_CameraRotationSpeed)))
{
Matrix34 mat = passInfo.GetCamera().GetMatrix();
Matrix33 matRot;
matRot.SetRotationZ(-GetCurTimeSec() * GetFloatCVar(e_CameraRotationSpeed));
newCam.SetMatrix(mat * matRot);
}
#if !defined(_RELEASE)
{
//this feature move the camera along with the player to a certain position and sets the angle accordingly
// (does not work via goto)
//u can switch it off again via e_CameraGoto 0
const char* const pCamGoto = GetCVars()->e_CameraGoto->GetString();
assert(pCamGoto);
if (strlen(pCamGoto) > 1)
{
Ang3 aAngDeg;
Vec3 vPos;
int args = azsscanf(pCamGoto, "%f %f %f %f %f %f", &vPos.x, &vPos.y, &vPos.z, &aAngDeg.x, &aAngDeg.y, &aAngDeg.z);
if (args >= 3)
{
Vec3 curPos = newCam.GetPosition();
if (fabs(vPos.x - curPos.x) > 10.f || fabs(vPos.y - curPos.y) > 10.f || fabs(vPos.z - curPos.z) > 10.f)
{
char buf[128];
sprintf_s(buf, "goto %f %f %f", vPos.x, vPos.y, vPos.z);
gEnv->pConsole->ExecuteString(buf);
}
if (args >= 6)
{
Matrix34 mat = passInfo.GetCamera().GetMatrix();
mat.SetTranslation(vPos);
mat.SetRotation33(Matrix33::CreateRotationXYZ(DEG2RAD(aAngDeg)));
newCam.SetMatrix(mat);
}
}
}
}
// Streaming latency test
if (GetCVars()->e_SQTestCount && !GetCVars()->e_SQTestBegin)
{
GetConsole()->GetCVar("e_SQTestBegin")->Set(1);
GetConsole()->GetCVar("e_SQTestCount")->Set(GetCVars()->e_SQTestCount - 1);
}
if (GetCVars()->e_SQTestBegin)
{
ProcessStreamingLatencyTest(passInfo.GetCamera(), newCam, passInfo);
}
#endif
// set the camera if e_cameraFreeze is not set
if (GetCVars()->e_CameraFreeze || GetCVars()->e_CoverageBufferDebugFreeze)
{
DrawSphere(GetRenderingCamera().GetPosition(), .05f);
// always set camera to request position for the renderer, allows debugging with e_camerafreeze
GetRenderer()->SetCamera(gEnv->pSystem->GetViewCamera());
}
else
{
m_RenderingCamera = newCam;
// always set camera to request position for the renderer, allows debugging with e_camerafreeze
GetRenderer()->SetCamera(newCam);
}
// now we have a valid camera, we can start generation of the occlusion buffer
// only needed for editor here, in game we spawn the job more early
if (passInfo.IsGeneralPass() && GetCVars()->e_StatObjBufferRenderTasks)
{
if (gEnv->IsEditor())
{
GetObjManager()->PrepareCullbufferAsync(passInfo.GetCamera());
}
else
{
assert(IsEquivalent(passInfo.GetCamera().GetViewdir(), GetObjManager()->GetCullThread().GetViewDir())); // early set camera differs from current main camera - will cause occlusion errors
}
}
/////////////////////////////////////////////////////////////////////////////
// update streaming priority of newly seen CComponentRenders (fix for streaming system issue)
for (int i = 0, nSize = m_deferredRenderComponentStreamingPriorityUpdates.size(); i < nSize; ++i)
{
IRenderNode* pRenderNode = m_deferredRenderComponentStreamingPriorityUpdates[i];
AABB aabb = pRenderNode->GetBBox();
const Vec3& vCamPos = GetRenderingCamera().GetPosition();
float fEntDistance = sqrt_tpl(Distance::Point_AABBSq(vCamPos, aabb)) * passInfo.GetZoomFactor();
GetObjManager()->UpdateRenderNodeStreamingPriority(pRenderNode, fEntDistance, 1.0f, false, passInfo);
if (GetCVars()->e_StreamCgfDebug == 2)
{
PrintMessage("C3DEngine::RegisterEntity__GetObjManager()->UpdateRenderNodeStreamingPriority %s", pRenderNode->GetName());
}
}
m_deferredRenderComponentStreamingPriorityUpdates.resize(0);
}
void C3DEngine::GetSvoStaticTextures(I3DEngine::SSvoStaticTexInfo& svoInfo, PodArray<I3DEngine::SLightTI>* pLightsTI_S, PodArray<I3DEngine::SLightTI>* pLightsTI_D)
{
SVOGILegacyRequestBus::Broadcast(&SVOGILegacyRequests::GetSvoStaticTextures, svoInfo, pLightsTI_S, pLightsTI_D);
}
void C3DEngine::GetSvoBricksForUpdate(PodArray<SSvoNodeInfo>& arrNodeInfo, bool getDynamic)
{
SVOGILegacyRequestBus::Broadcast(&SVOGILegacyRequests::GetSvoBricksForUpdate, arrNodeInfo, getDynamic);
}
#if defined(FEATURE_SVO_GI)
void C3DEngine::LoadTISettings(XmlNodeRef pInputNode)
{
const char* szXmlNodeName = "Total_Illumination_v2";
if (gEnv->pConsole->GetCVar("e_svoTI_Active"))
{
gEnv->pConsole->GetCVar("e_svoTI_Active")->Set(GetXMLAttribText(pInputNode, szXmlNodeName, "Active", "0"));
gEnv->pConsole->GetCVar("e_svoTI_InjectionMultiplier")->Set(GetXMLAttribText(pInputNode, szXmlNodeName, "InjectionMultiplier", "0"));
gEnv->pConsole->GetCVar("e_svoTI_NumberOfBounces")->Set(GetXMLAttribText(pInputNode, szXmlNodeName, "NumberOfBounces", "0"));
gEnv->pConsole->GetCVar("e_svoTI_Saturation")->Set(GetXMLAttribText(pInputNode, szXmlNodeName, "Saturation", "0"));
gEnv->pConsole->GetCVar("e_svoTI_ConeMaxLength")->Set(GetXMLAttribText(pInputNode, szXmlNodeName, "ConeMaxLength", "0"));
gEnv->pConsole->GetCVar("e_svoTI_DiffuseConeWidth")->Set(GetXMLAttribText(pInputNode, szXmlNodeName, "DiffuseConeWidth", "0"));
gEnv->pConsole->GetCVar("e_svoTI_SSAOAmount")->Set(GetXMLAttribText(pInputNode, szXmlNodeName, "SSAOAmount", "0"));
gEnv->pConsole->GetCVar("e_svoTI_UseLightProbes")->Set(GetXMLAttribText(pInputNode, szXmlNodeName, "UseLightProbes", "0"));
gEnv->pConsole->GetCVar("e_svoTI_AmbientOffsetRed")->Set(GetXMLAttribText(pInputNode, szXmlNodeName, "AmbientOffsetRed", "1"));
gEnv->pConsole->GetCVar("e_svoTI_AmbientOffsetGreen")->Set(GetXMLAttribText(pInputNode, szXmlNodeName, "AmbientOffsetGreen", "1"));
gEnv->pConsole->GetCVar("e_svoTI_AmbientOffsetBlue")->Set(GetXMLAttribText(pInputNode, szXmlNodeName, "AmbientOffsetBlue", "1"));
gEnv->pConsole->GetCVar("e_svoTI_AmbientOffsetBias")->Set(GetXMLAttribText(pInputNode, szXmlNodeName, "AmbientOffsetBias", ".1"));
gEnv->pConsole->GetCVar("e_svoTI_IntegrationMode")->Set(GetXMLAttribText(pInputNode, szXmlNodeName, "IntegrationMode", "0"));
if (gEnv->pConsole->GetCVar("e_svoTI_IntegrationMode")->GetIVal() < 1) // AO
{
gEnv->pConsole->GetCVar("e_svoTI_NumberOfBounces")->Set("1");
}
}
}
#endif
void C3DEngine::PrepareOcclusion(const CCamera& rCamera)
{
if (!gEnv->IsEditor() && GetCVars()->e_StatObjBufferRenderTasks && !gEnv->IsFMVPlaying() && (!IsEquivalent(rCamera.GetPosition(), Vec3(0, 0, 0), VEC_EPSILON) || GetRenderer()->IsPost3DRendererEnabled()))
{
GetObjManager()->PrepareCullbufferAsync(rCamera);
}
}
void C3DEngine::EndOcclusion()
{
GetObjManager()->EndOcclusionCulling();
}
IStatObj* C3DEngine::LoadStatObjUnsafeManualRef(const char* fileName, const char* geomName, IStatObj::SSubObject** subObject,
bool useStreaming, unsigned long loadingFlags, const void* data, int dataSize)
{
return LoadStatObjInternal(fileName, geomName, subObject, useStreaming, loadingFlags, &CObjManager::LoadStatObjUnsafeManualRef, data, dataSize);
}
_smart_ptr<IStatObj> C3DEngine::LoadStatObjAutoRef(const char* fileName, const char* geomName, IStatObj::SSubObject** subObject,
bool useStreaming, unsigned long loadingFlags, const void* data, int dataSize)
{
return LoadStatObjInternal(fileName, geomName, subObject, useStreaming, loadingFlags, &CObjManager::LoadStatObjAutoRef, data, dataSize);
}
template<typename TReturn>
TReturn C3DEngine::LoadStatObjInternal(const char* fileName, const char* geomName, IStatObj::SSubObject** subObject, bool useStreaming,
unsigned long loadingFlags, LoadStatObjFunc<TReturn> loadStatObjFunc, const void* data, int dataSize)
{
if (!fileName || !fileName[0])
{
m_pSystem->Warning(VALIDATOR_MODULE_3DENGINE, VALIDATOR_ERROR, 0, 0, "I3DEngine::LoadStatObj: filename is not specified");
return nullptr;
}
if (!m_pObjManager)
{
m_pObjManager = CryAlignedNew<CObjManager>();
}
CObjManager* pObjManager = static_cast<CObjManager*>(m_pObjManager);
return (pObjManager->*loadStatObjFunc)(fileName, geomName, subObject, useStreaming, loadingFlags, data, dataSize, nullptr);
}
void C3DEngine::LoadStatObjAsync(I3DEngine::LoadStaticObjectAsyncResult resultCallback, const char* szFileName, const char* szGeomName, bool bUseStreaming, unsigned long nLoadingFlags)
{
CRY_ASSERT_MESSAGE(szFileName && szFileName[0], "LoadStatObjAsync: Invalid filename");
CRY_ASSERT_MESSAGE(m_pObjManager, "Object manager is not ready.");
StaticObjectAsyncLoadRequest request;
request.m_callback = resultCallback;
request.m_filename = szFileName;
request.m_geomName = szGeomName;
request.m_useStreaming = bUseStreaming;
request.m_loadingFlags = nLoadingFlags;
{
AZStd::lock_guard<AZStd::mutex> lock(m_statObjQueueLock);
m_statObjLoadRequests.push(std::move(request));
}
}
void C3DEngine::ProcessAsyncStaticObjectLoadRequests()
{
// Same scheme as skinned meshes: CharacterManager::ProcessAsyncLoadRequests.
enum
{
kMaxLoadsPerFrame = 20
};
size_t loadsThisFrame = 0;
while (loadsThisFrame < kMaxLoadsPerFrame)
{
StaticObjectAsyncLoadRequest request;
{
AZStd::lock_guard<AZStd::mutex> lock(m_statObjQueueLock);
if (m_statObjLoadRequests.empty())
{
break;
}
request = std::move(m_statObjLoadRequests.front());
m_statObjLoadRequests.pop();
}
_smart_ptr<IStatObj> object = LoadStatObjAutoRef(request.m_filename.c_str(), request.m_geomName.c_str(), nullptr, request.m_useStreaming, request.m_loadingFlags);
request.m_callback(object);
++loadsThisFrame;
}
}
IStatObj* C3DEngine::FindStatObjectByFilename(const char* filename)
{
if (filename == NULL)
{
return NULL;
}
if (filename[0] == 0)
{
return NULL;
}
return m_pObjManager->FindStaticObjectByFilename(filename);
}
void C3DEngine::RegisterEntity(IRenderNode* pEnt, int nSID, int nSIDConsideredSafe)
{
FUNCTION_PROFILER_3DENGINE;
if (gEnv->mMainThreadId != CryGetCurrentThreadId())
{
CryFatalError("C3DEngine::RegisterEntity should only be called on main thread.");
}
uint32 nFrameID = GetRenderer()->GetFrameID();
AsyncOctreeUpdate(pEnt, nSID, nSIDConsideredSafe, nFrameID, false);
}
void C3DEngine::UnRegisterEntityDirect(IRenderNode* pEnt)
{
UnRegisterEntityImpl(pEnt);
}
void C3DEngine::UnRegisterEntityAsJob(IRenderNode* pEnt)
{
AsyncOctreeUpdate(pEnt, (int)0, (int)0, (int)0, true);
}
bool C3DEngine::CreateDecalInstance(const CryEngineDecalInfo& decal, CDecal* pCallerManagedDecal)
{
if (!GetCVars()->e_Decals && !pCallerManagedDecal)
{
return false;
}
return m_pDecalManager->Spawn(decal, pCallerManagedDecal);
}
void C3DEngine::SelectEntity(IRenderNode* pEntity)
{
static IRenderNode* pSelectedNode;
static float fLastTime;
if (pEntity && GetCVars()->e_Decals == 3)
{
float fCurTime = gEnv->pTimer->GetAsyncCurTime();
if (fCurTime - fLastTime < 1.0f)
{
return;
}
fLastTime = fCurTime;
if (pSelectedNode)
{
pSelectedNode->SetRndFlags(ERF_SELECTED, false);
}
pEntity->SetRndFlags(ERF_SELECTED, true);
pSelectedNode = pEntity;
}
}
void C3DEngine::CreateDecal(const struct CryEngineDecalInfo& decal)
{
IF (!GetCVars()->e_DecalsAllowGameDecals, 0)
{
return;
}
if (GetCVars()->e_Decals == 2)
{
IRenderNode* pRN = decal.ownerInfo.pRenderNode;
PrintMessage("Debug: C3DEngine::CreateDecal: Pos=(%.1f,%.1f,%.1f) Size=%.2f DecalMaterial=%s HitObjectName=%s(%s)",
decal.vPos.x, decal.vPos.y, decal.vPos.z, decal.fSize, decal.szMaterialName,
pRN ? pRN->GetName() : "NULL", pRN ? pRN->GetEntityClassName() : "NULL");
}
assert(!decal.pExplicitRightUpFront); // only game-play decals come here
static uint32 nGroupId = 0;
nGroupId++;
if ((GetCVars()->e_DecalsDefferedStatic == 1 && decal.pExplicitRightUpFront) ||
(GetCVars()->e_DecalsDefferedDynamic == 1 && !decal.pExplicitRightUpFront &&
(!decal.ownerInfo.pRenderNode ||
decal.ownerInfo.pRenderNode->GetRenderNodeType() == eERType_StaticMeshRenderComponent ||
decal.fGrowTimeAlpha || decal.fSize > GetFloatCVar(e_DecalsDefferedDynamicMinSize)))
&& !decal.bForceSingleOwner)
{
CryEngineDecalInfo decal_adjusted = decal;
decal_adjusted.nGroupId = nGroupId;
decal_adjusted.bDeferred = true;
m_pDecalManager->SpawnHierarchical(decal_adjusted, NULL);
return;
}
if (decal.ownerInfo.pRenderNode && decal.fSize > 0.5f && !decal.bForceSingleOwner)
{
PodArray<SRNInfo> lstEntities;
Vec3 vRadius(decal.fSize, decal.fSize, decal.fSize);
const AABB cExplosionBox(decal.vPos - vRadius, decal.vPos + vRadius);
if (CVisArea* pArea = (CVisArea*)decal.ownerInfo.pRenderNode->GetEntityVisArea())
{
if (pArea->m_pObjectsTree)
{
pArea->m_pObjectsTree->MoveObjectsIntoList(&lstEntities, &cExplosionBox, false, true, true, true);
}
}
else
{
Get3DEngine()->MoveObjectsIntoListGlobal(&lstEntities, &cExplosionBox, false, true, true, true);
}
for (int i = 0; i < lstEntities.Count(); i++)
{
// decals on statobj's of render node
CryEngineDecalInfo decalOnRenderNode = decal;
decalOnRenderNode.ownerInfo.pRenderNode = lstEntities[i].pNode;
decalOnRenderNode.nGroupId = nGroupId;
// if(decalOnRenderNode.ownerInfo.pRenderNode->GetRenderNodeType() != decal.ownerInfo.pRenderNode->GetRenderNodeType())
// continue;
if (decalOnRenderNode.ownerInfo.pRenderNode->GetRndFlags() & ERF_HIDDEN)
{
continue;
}
m_pDecalManager->SpawnHierarchical(decalOnRenderNode, NULL);
}
}
else
{
CryEngineDecalInfo decalStatic = decal;
decalStatic.nGroupId = nGroupId;
m_pDecalManager->SpawnHierarchical(decalStatic, NULL);
}
}
//////////////////////////////////////////////////////////////////////////
void C3DEngine::SetSunColor(Vec3 vColor)
{
if (m_pObjManager)
{
m_pObjManager->SetSunColor(vColor);
m_pObjManager->SetSunAnimColor(vColor);
}
}
Vec3 C3DEngine::GetSunAnimColor()
{
if (m_pObjManager)
{
return m_pObjManager->GetSunAnimColor();
}
return Vec3();
}
void C3DEngine::SetSunAnimColor(const Vec3& sunAnimColor)
{
if (m_pObjManager)
{
m_pObjManager->SetSunAnimColor(sunAnimColor);
}
}
float C3DEngine::GetSunAnimSpeed()
{
if (m_pObjManager)
{
return m_pObjManager->GetSunAnimSpeed();
}
return 0.0f;
}
void C3DEngine::SetSunAnimSpeed(float sunAnimSpeed)
{
if (m_pObjManager)
{
m_pObjManager->SetSunAnimSpeed(sunAnimSpeed);
}
}
AZ::u8 C3DEngine::GetSunAnimPhase()
{
if (m_pObjManager)
{
return m_pObjManager->GetSunAnimPhase();
}
return 0;
}
void C3DEngine::SetSunAnimPhase(AZ::u8 sunAnimPhase)
{
if (m_pObjManager)
{
m_pObjManager->SetSunAnimPhase(sunAnimPhase);
}
}
AZ::u8 C3DEngine::GetSunAnimIndex()
{
if (m_pObjManager)
{
return m_pObjManager->GetSunAnimIndex();
}
return 0;
}
void C3DEngine::SetSunAnimIndex(AZ::u8 sunAnimIndex)
{
if (m_pObjManager)
{
m_pObjManager->SetSunAnimIndex(sunAnimIndex);
}
}
void C3DEngine::SetSSAOAmount(float fMul)
{
if (m_pObjManager)
{
m_pObjManager->SetSSAOAmount(fMul);
}
}
void C3DEngine::SetSSAOContrast(float fMul)
{
if (m_pObjManager)
{
m_pObjManager->SetSSAOContrast(fMul);
}
}
void C3DEngine::RemoveAllStaticObjects([[maybe_unused]] int nSID)
{
if (!IsObjectTreeReady())
{
return;
}
PodArray<SRNInfo> lstObjects;
// Don't remove objects from the octree, since our query will return more objects than just the ones we're looking for.
const bool removeObjectsFromOctree = false;
// Skip gathering decals, since we're only looking for vegetation.
const bool skipDecals = true;
const bool skipERFNoDecalNodeDecals = true;
// Skip dynamic vegetation, since we're only looking to remove static vegetation.
const bool skipDynamicObjects = true;
GetObjectTree()->MoveObjectsIntoList(&lstObjects, nullptr, removeObjectsFromOctree, skipDecals, skipERFNoDecalNodeDecals, skipDynamicObjects);
}
void C3DEngine::OnExplosion(Vec3 vPos, float fRadius, [[maybe_unused]] bool bDeformTerrain)
{
if (GetCVars()->e_Decals == 2)
{
PrintMessage("Debug: C3DEngine::OnExplosion: Pos=(%.1f,%.1f,%.1f) fRadius=%.2f", vPos.x, vPos.y, vPos.z, fRadius);
}
auto terrain = AzFramework::Terrain::TerrainDataRequestBus::FindFirstHandler();
if (!terrain)
{
return;
}
AZ::Aabb terrainAabb = terrain->GetTerrainAabb();
if (!terrainAabb.Contains(LYVec3ToAZVec3(vPos)) || fRadius <= 0)
{
return; // out of terrain
}
const AZ::Vector2 terrainGridResolution = terrain->GetTerrainGridResolution();
const float unitSizeX = terrainGridResolution.GetX();
const float unitSizeY = terrainGridResolution.GetY();
// do not create decals near the terrain holes
{
for (float x = vPos.x - fRadius; x <= vPos.x + fRadius + 1.0f; x += unitSizeX)
{
for (float y = vPos.y - fRadius; y <= vPos.y + fRadius + 1.0f; y += unitSizeY)
{
if (terrain->GetIsHoleFromFloats(x, y))
{
return;
}
}
}
}
// reduce ground decals size depending on distance to the ground
float terrainHeight = terrain->GetHeightFromFloats(vPos.x, vPos.y);
float fExploHeight = vPos.z - terrainHeight;
// delete decals what can not be correctly updated
Vec3 vRadius(fRadius, fRadius, fRadius);
AABB areaBox(vPos - vRadius, vPos + vRadius);
Get3DEngine()->DeleteDecalsInRange(&areaBox, NULL);
}
float C3DEngine::GetMaxViewDistance(bool bScaled)
{
// lerp between specs
float fMaxViewDist;
// camera height lerp factor
if (OceanToggle::IsActive() && !OceanRequest::OceanIsEnabled())
{
fMaxViewDist = m_fMaxViewDistHighSpec;
}
else
{
// spec lerp factor
float lerpSpec = clamp_tpl(GetCVars()->e_MaxViewDistSpecLerp, 0.0f, 1.0f);
// lerp between specs
fMaxViewDist = m_fMaxViewDistLowSpec * (1.f - lerpSpec) + m_fMaxViewDistHighSpec * lerpSpec;
const float waterLevel = OceanToggle::IsActive() ? OceanRequest::GetOceanLevel() : GetWaterLevel();
float lerpHeight = clamp_tpl(max(0.f, GetSystem()->GetViewCamera().GetPosition().z - waterLevel) / GetFloatCVar(e_MaxViewDistFullDistCamHeight), 0.0f, 1.0f);
// lerp between prev result and high spec
fMaxViewDist = fMaxViewDist * (1.f - lerpHeight) + m_fMaxViewDistHighSpec * lerpHeight;
}
if (bScaled)
{
fMaxViewDist *= m_fMaxViewDistScale;
}
// for debugging
const float fMaxViewDistCVar = GetFloatCVar(e_MaxViewDistance);
fMaxViewDist = (float)fsel(fMaxViewDistCVar, fMaxViewDistCVar, fMaxViewDist);
fMaxViewDist = (float)fsel(fabsf(fMaxViewDist) - 0.100001f, fMaxViewDist, 0.100001f);
// eliminate some floating point inconsistency here, there's no point in nitpicking 7999.9995 view distance vs 8000
fMaxViewDist = AZ::ClampIfCloseMag<float>(fMaxViewDist, float(round(fMaxViewDist)), 0.01f);
return fMaxViewDist;
}
void C3DEngine::SetFrameLodInfo(const SFrameLodInfo& frameLodInfo)
{
if (frameLodInfo.fLodRatio != m_frameLodInfo.fLodRatio || frameLodInfo.fTargetSize != m_frameLodInfo.fTargetSize)
{
++m_frameLodInfo.nID;
m_frameLodInfo.fLodRatio = frameLodInfo.fLodRatio;
m_frameLodInfo.fTargetSize = frameLodInfo.fTargetSize;
}
m_frameLodInfo.nMinLod = frameLodInfo.nMinLod;
m_frameLodInfo.nMaxLod = frameLodInfo.nMaxLod;
}
void C3DEngine::SetFogColor(const Vec3& vFogColor)
{
m_vFogColor = vFogColor;
GetRenderer()->SetClearColor(m_vFogColor);
}
Vec3 C3DEngine::GetFogColor()
{
return m_vFogColor;
}
void C3DEngine::GetSkyLightParameters(Vec3& sunDir, Vec3& sunIntensity, float& Km, float& Kr, float& g, Vec3& rgbWaveLengths)
{
CSkyLightManager::SSkyDomeCondition skyCond;
m_pSkyLightManager->GetCurSkyDomeCondition(skyCond);
g = skyCond.m_g;
Km = skyCond.m_Km;
Kr = skyCond.m_Kr;
sunIntensity = skyCond.m_sunIntensity;
rgbWaveLengths = skyCond.m_rgbWaveLengths;
sunDir = skyCond.m_sunDirection;
}
void C3DEngine::SetSkyLightParameters(const Vec3& sunDir, const Vec3& sunIntensity, float Km, float Kr, float g, const Vec3& rgbWaveLengths, bool forceImmediateUpdate)
{
CSkyLightManager::SSkyDomeCondition skyCond;
skyCond.m_g = g;
skyCond.m_Km = Km;
skyCond.m_Kr = Kr;
skyCond.m_sunIntensity = sunIntensity;
skyCond.m_rgbWaveLengths = rgbWaveLengths;
skyCond.m_sunDirection = sunDir;
m_pSkyLightManager->SetSkyDomeCondition(skyCond);
if (forceImmediateUpdate && IsHDRSkyMaterial(GetSkyMaterial()))
{
m_pSkyLightManager->FullUpdate();
}
}
void C3DEngine::SetLightsHDRDynamicPowerFactor(const float value)
{
m_fLightsHDRDynamicPowerFactor = value;
}
float C3DEngine::GetLightsHDRDynamicPowerFactor() const
{
return m_fLightsHDRDynamicPowerFactor;
}
bool C3DEngine::IsTessellationAllowedForShadowMap(const SRenderingPassInfo& passInfo) const
{
#ifdef MESH_TESSELLATION_ENGINE
SRenderingPassInfo::EShadowMapType shadowType = passInfo.GetShadowMapType();
switch (shadowType)
{
case SRenderingPassInfo::SHADOW_MAP_GSM:
return passInfo.ShadowFrustumLod() < GetCVars()->e_ShadowsTessellateCascades;
case SRenderingPassInfo::SHADOW_MAP_LOCAL:
return GetCVars()->e_ShadowsTessellateDLights != 0;
case SRenderingPassInfo::SHADOW_MAP_NONE:
default:
return false;
}
#endif //#ifdef MESH_TESSELLATION_ENGINE
return false;
}
void C3DEngine::SetPhysMaterialEnumerator(IPhysMaterialEnumerator* pPhysMaterialEnumerator)
{
m_pPhysMaterialEnumerator = pPhysMaterialEnumerator;
}
IPhysMaterialEnumerator* C3DEngine::GetPhysMaterialEnumerator()
{
return m_pPhysMaterialEnumerator;
}
float C3DEngine::GetDistanceToSectorWithWater()
{
const Vec3 camPosition = GetRenderingCamera().GetPosition();
const float minDistance = 0.1f;
const bool oceanActive = OceanToggle::IsActive();
const bool oceanEnabled = OceanRequest::OceanIsEnabled();
float distance = minDistance;
if (oceanActive && !oceanEnabled)
{
distance = std::numeric_limits<float>::infinity();
}
else
{
if (oceanActive && oceanEnabled)
{
distance = camPosition.z - OceanRequest::GetOceanLevel();
}
else
{
distance = std::numeric_limits<float>::infinity();
}
}
return max(distance, minDistance);
}
Vec3 C3DEngine::GetSunColor() const
{
return m_pObjManager ? m_pObjManager->GetSunColor() : Vec3(0, 0, 0);
}
float C3DEngine::GetSSAOAmount() const
{
return m_pObjManager ? m_pObjManager->GetSSAOAmount() : 1.0f;
}
float C3DEngine::GetSSAOContrast() const
{
return m_pObjManager ? m_pObjManager->GetSSAOContrast() : 1.0f;
}
void C3DEngine::SetRainParams(const SRainParams& rainParams)
{
if (m_pObjManager)
{
m_pObjManager->GetRainParams().bIgnoreVisareas = rainParams.bIgnoreVisareas;
m_pObjManager->GetRainParams().bDisableOcclusion = rainParams.bDisableOcclusion;
m_pObjManager->GetRainParams().qRainRotation = rainParams.qRainRotation;
m_pObjManager->GetRainParams().vWorldPos = rainParams.vWorldPos;
m_pObjManager->GetRainParams().vColor = rainParams.vColor;
m_pObjManager->GetRainParams().fAmount = rainParams.fAmount;
m_pObjManager->GetRainParams().fCurrentAmount = rainParams.fCurrentAmount;
m_pObjManager->GetRainParams().fRadius = rainParams.fRadius;
m_pObjManager->GetRainParams().fFakeGlossiness = rainParams.fFakeGlossiness;
m_pObjManager->GetRainParams().fFakeReflectionAmount = rainParams.fFakeReflectionAmount;
m_pObjManager->GetRainParams().fDiffuseDarkening = rainParams.fDiffuseDarkening;
m_pObjManager->GetRainParams().fRainDropsAmount = rainParams.fRainDropsAmount;
m_pObjManager->GetRainParams().fRainDropsSpeed = rainParams.fRainDropsSpeed;
m_pObjManager->GetRainParams().fRainDropsLighting = rainParams.fRainDropsLighting;
m_pObjManager->GetRainParams().fMistAmount = rainParams.fMistAmount;
m_pObjManager->GetRainParams().fMistHeight = rainParams.fMistHeight;
m_pObjManager->GetRainParams().fPuddlesAmount = rainParams.fPuddlesAmount;
m_pObjManager->GetRainParams().fPuddlesMaskAmount = rainParams.fPuddlesMaskAmount;
m_pObjManager->GetRainParams().fPuddlesRippleAmount = rainParams.fPuddlesRippleAmount;
m_pObjManager->GetRainParams().fSplashesAmount = rainParams.fSplashesAmount;
m_pObjManager->GetRainParams().nUpdateFrameID = GetRenderer()->GetFrameID();
}
}
bool C3DEngine::GetRainParams(SRainParams& rainParams)
{
bool bRet = false;
const int nFrmID = GetRenderer()->GetFrameID();
if (m_pObjManager)
{
// Copy shared rain data only
rainParams.bIgnoreVisareas = m_pObjManager->GetRainParams().bIgnoreVisareas;
rainParams.bDisableOcclusion = m_pObjManager->GetRainParams().bDisableOcclusion;
rainParams.qRainRotation = m_pObjManager->GetRainParams().qRainRotation;
rainParams.vWorldPos = m_pObjManager->GetRainParams().vWorldPos;
rainParams.vColor = m_pObjManager->GetRainParams().vColor;
rainParams.fAmount = m_pObjManager->GetRainParams().fAmount;
rainParams.fCurrentAmount = m_pObjManager->GetRainParams().fCurrentAmount;
rainParams.fRadius = m_pObjManager->GetRainParams().fRadius;
rainParams.fFakeGlossiness = m_pObjManager->GetRainParams().fFakeGlossiness;
rainParams.fFakeReflectionAmount = m_pObjManager->GetRainParams().fFakeReflectionAmount;
rainParams.fDiffuseDarkening = m_pObjManager->GetRainParams().fDiffuseDarkening;
rainParams.fRainDropsAmount = m_pObjManager->GetRainParams().fRainDropsAmount;
rainParams.fRainDropsSpeed = m_pObjManager->GetRainParams().fRainDropsSpeed;
rainParams.fRainDropsLighting = m_pObjManager->GetRainParams().fRainDropsLighting;
rainParams.fMistAmount = m_pObjManager->GetRainParams().fMistAmount;
rainParams.fMistHeight = m_pObjManager->GetRainParams().fMistHeight;
rainParams.fPuddlesAmount = m_pObjManager->GetRainParams().fPuddlesAmount;
rainParams.fPuddlesMaskAmount = m_pObjManager->GetRainParams().fPuddlesMaskAmount;
rainParams.fPuddlesRippleAmount = m_pObjManager->GetRainParams().fPuddlesRippleAmount;
rainParams.fSplashesAmount = m_pObjManager->GetRainParams().fSplashesAmount;
if (!IsOutdoorVisible() && !rainParams.bIgnoreVisareas)
{
rainParams.fAmount = 0.f;
}
bRet = m_pObjManager->GetRainParams().nUpdateFrameID == nFrmID;
}
return bRet;
}
void C3DEngine::SetSnowSurfaceParams(const Vec3& vCenter, float fRadius, float fSnowAmount, float fFrostAmount, float fSurfaceFreezing)
{
if (m_pObjManager)
{
m_pObjManager->GetSnowParams().m_vWorldPos = vCenter;
m_pObjManager->GetSnowParams().m_fRadius = fRadius;
m_pObjManager->GetSnowParams().m_fSnowAmount = fSnowAmount;
m_pObjManager->GetSnowParams().m_fFrostAmount = fFrostAmount;
m_pObjManager->GetSnowParams().m_fSurfaceFreezing = fSurfaceFreezing;
}
}
bool C3DEngine::GetSnowSurfaceParams(Vec3& vCenter, float& fRadius, float& fSnowAmount, float& fFrostAmount, float& fSurfaceFreezing)
{
bool bRet = false;
if (m_pObjManager)
{
vCenter = m_pObjManager->GetSnowParams().m_vWorldPos;
fRadius = m_pObjManager->GetSnowParams().m_fRadius;
fSnowAmount = 0.f;
fFrostAmount = 0.f;
fSurfaceFreezing = 0.f;
if (IsOutdoorVisible())
{
fSnowAmount = m_pObjManager->GetSnowParams().m_fSnowAmount;
fFrostAmount = m_pObjManager->GetSnowParams().m_fFrostAmount;
fSurfaceFreezing = m_pObjManager->GetSnowParams().m_fSurfaceFreezing;
}
bRet = true;
}
return bRet;
}
void C3DEngine::SetSnowFallParams(int nSnowFlakeCount, float fSnowFlakeSize, float fSnowFallBrightness, float fSnowFallGravityScale, float fSnowFallWindScale, float fSnowFallTurbulence, float fSnowFallTurbulenceFreq)
{
if (m_pObjManager)
{
m_pObjManager->GetSnowParams().m_nSnowFlakeCount = nSnowFlakeCount;
m_pObjManager->GetSnowParams().m_fSnowFlakeSize = fSnowFlakeSize;
m_pObjManager->GetSnowParams().m_fSnowFallBrightness = fSnowFallBrightness;
m_pObjManager->GetSnowParams().m_fSnowFallGravityScale = fSnowFallGravityScale;
m_pObjManager->GetSnowParams().m_fSnowFallWindScale = fSnowFallWindScale;
m_pObjManager->GetSnowParams().m_fSnowFallTurbulence = fSnowFallTurbulence;
m_pObjManager->GetSnowParams().m_fSnowFallTurbulenceFreq = fSnowFallTurbulenceFreq;
}
}
bool C3DEngine::GetSnowFallParams(int& nSnowFlakeCount, float& fSnowFlakeSize, float& fSnowFallBrightness, float& fSnowFallGravityScale, float& fSnowFallWindScale, float& fSnowFallTurbulence, float& fSnowFallTurbulenceFreq)
{
bool bRet = false;
if (m_pObjManager)
{
nSnowFlakeCount = 0;
fSnowFlakeSize = 0.f;
fSnowFallBrightness = 0.f;
fSnowFallGravityScale = 0.f;
fSnowFallWindScale = 0.f;
fSnowFallTurbulence = 0.f;
fSnowFallTurbulenceFreq = 0.f;
if (IsOutdoorVisible())
{
nSnowFlakeCount = m_pObjManager->GetSnowParams().m_nSnowFlakeCount;
fSnowFlakeSize = m_pObjManager->GetSnowParams().m_fSnowFlakeSize;
fSnowFallBrightness = m_pObjManager->GetSnowParams().m_fSnowFallBrightness;
fSnowFallGravityScale = m_pObjManager->GetSnowParams().m_fSnowFallGravityScale;
fSnowFallWindScale = m_pObjManager->GetSnowParams().m_fSnowFallWindScale;
fSnowFallTurbulence = m_pObjManager->GetSnowParams().m_fSnowFallTurbulence;
fSnowFallTurbulenceFreq = m_pObjManager->GetSnowParams().m_fSnowFallTurbulenceFreq;
}
bRet = true;
}
return bRet;
}
void C3DEngine::SetSunDir(const Vec3& newSunDir)
{
Vec3 vSunDirNormalized = newSunDir.normalized();
m_vSunDirRealtime = vSunDirNormalized;
if (vSunDirNormalized.Dot(m_vSunDirNormalized) < GetFloatCVar(e_SunAngleSnapDot) ||
GetCurTimeSec() - m_fSunDirUpdateTime > GetFloatCVar(e_SunAngleSnapSec))
{
m_vSunDirNormalized = vSunDirNormalized;
m_vSunDir = m_vSunDirNormalized * DISTANCE_TO_THE_SUN;
m_fSunDirUpdateTime = GetCurTimeSec();
}
}
Vec3 C3DEngine::GetSunDir() const
{
return m_vSunDir;
}
Vec3 C3DEngine::GetRealtimeSunDirNormalized() const
{
return m_vSunDirRealtime;
}
void C3DEngine::FreeRenderNodeState(IRenderNode* pEnt)
{
// make sure we don't try to update the streaming priority if an object
// was added and removed in the same frame
int nElementID = m_deferredRenderComponentStreamingPriorityUpdates.Find(pEnt);
if (nElementID != -1)
{
m_deferredRenderComponentStreamingPriorityUpdates.DeleteFastUnsorted(nElementID);
}
m_pObjManager->RemoveFromRenderAllObjectDebugInfo(pEnt);
#if !defined(_RELEASE)
if (!gEnv->IsDedicated())
{
//As render nodes can be deleted in many places, it's possible that the map of render nodes used by stats gathering (r_stats 6, perfHUD, debug gun) could get aliased.
//Ensure that this node is removed from the map to prevent a dereference after deletion.
gEnv->pRenderer->ForceRemoveNodeFromDrawCallsMap(pEnt);
}
#endif
m_lstAlwaysVisible.Delete(pEnt);
if (m_pDecalManager && (pEnt->m_nInternalFlags & IRenderNode::DECAL_OWNER))
{
m_pDecalManager->OnEntityDeleted(pEnt);
}
if (pEnt->GetRenderNodeType() == eERType_Light)
{
GetRenderer()->OnEntityDeleted(pEnt);
}
if (pEnt->GetRndFlags() & (ERF_CASTSHADOWMAPS | ERF_HAS_CASTSHADOWMAPS))
{ // make sure pointer to object will not be used somewhere in the renderer
#if !defined(_RELEASE)
if (!(pEnt->GetRndFlags() & ERF_HAS_CASTSHADOWMAPS))
{
Warning("IRenderNode has ERF_CASTSHADOWMAPS set but not ERF_HAS_CASTSHADOWMAPS, name: '%s', class: '%s'.", pEnt->GetName(), pEnt->GetEntityClassName());
}
#endif
Get3DEngine()->OnCasterDeleted(pEnt);
}
UnRegisterEntityImpl(pEnt);
if (pEnt->m_pRNTmpData)
{
Get3DEngine()->FreeRNTmpData(&pEnt->m_pRNTmpData);
assert(!pEnt->m_pRNTmpData);
}
}
const char* C3DEngine::GetLevelFilePath(const char* szFileName)
{
cry_strcpy(m_sGetLevelFilePathTmpBuff, m_szLevelFolder);
if (*szFileName && (*szFileName == '/' || *szFileName == '\\'))
{
cry_strcat(m_sGetLevelFilePathTmpBuff, szFileName + 1);
}
else
{
cry_strcat(m_sGetLevelFilePathTmpBuff, szFileName);
}
return m_sGetLevelFilePathTmpBuff;
}
void C3DEngine::ActivatePortal(const Vec3& vPos, bool bActivate, const char* szEntityName)
{
if (m_pVisAreaManager)
{
m_pVisAreaManager->ActivatePortal(vPos, bActivate, szEntityName);
}
}
bool C3DEngine::SetStatInstGroup(int nGroupId, const IStatInstGroup& siGroup, int nSID)
{
if (m_pObjManager->GetListStaticTypes().Count() == 0)
{
AZ_Warning("C3DEngine", false, "Trying to set a Stat instance without an initialized Object manager. This might be caused by using the vegetation system without terrain.");
return false;
}
if ((nSID < 0) || (nSID >= m_pObjManager->GetListStaticTypes().Count()))
{
AZ_Assert(false, "Invalid StatInst ID: %d (should be > 0 and < %d)", nSID, m_pObjManager->GetListStaticTypes().Count());
return false;
}
m_fRefreshSceneDataCVarsSumm = -100;
m_pObjManager->GetListStaticTypes()[nSID].resize(max(nGroupId + 1, m_pObjManager->GetListStaticTypes()[nSID].Count()));
if (nGroupId < 0 || nGroupId >= m_pObjManager->GetListStaticTypes()[nSID].Count())
{
return false;
}
StatInstGroup& rGroup = m_pObjManager->GetListStaticTypes()[nSID][nGroupId];
// If the object was changed in the editor, ResetActiveNodes will need to be called later
// Keep track of the previous object so we can check for this later
_smart_ptr<IStatObj> pPreviousObject = rGroup.pStatObj;
rGroup.pStatObj = siGroup.pStatObj;
if (rGroup.pStatObj)
{
cry_strcpy(rGroup.szFileName, siGroup.pStatObj->GetFilePath());
}
else
{
rGroup.szFileName[0] = 0;
}
rGroup.bHideability = siGroup.bHideability;
rGroup.bHideabilitySecondary = siGroup.bHideabilitySecondary;
rGroup.nPlayerHideable = siGroup.nPlayerHideable;
rGroup.fBending = siGroup.fBending;
rGroup.nCastShadowMinSpec = siGroup.nCastShadowMinSpec;
rGroup.bRecvShadow = siGroup.bRecvShadow;
rGroup.bDynamicDistanceShadows = siGroup.bDynamicDistanceShadows;
rGroup.bUseAlphaBlending = siGroup.bUseAlphaBlending;
rGroup.fSpriteDistRatio = siGroup.fSpriteDistRatio;
rGroup.fLodDistRatio = siGroup.fLodDistRatio;
rGroup.fShadowDistRatio = siGroup.fShadowDistRatio;
rGroup.fMaxViewDistRatio = siGroup.fMaxViewDistRatio;
rGroup.fBrightness = siGroup.fBrightness;
_smart_ptr<IMaterial> pPreviousGroupMaterial = rGroup.pMaterial;
rGroup.pMaterial = siGroup.pMaterial;
rGroup.fDensity = siGroup.fDensity;
rGroup.fElevationMax = siGroup.fElevationMax;
rGroup.fElevationMin = siGroup.fElevationMin;
rGroup.fSize = siGroup.fSize;
rGroup.fSizeVar = siGroup.fSizeVar;
rGroup.fSlopeMax = siGroup.fSlopeMax;
rGroup.fSlopeMin = siGroup.fSlopeMin;
rGroup.fStiffness = siGroup.fStiffness;
rGroup.fDamping = siGroup.fDamping;
rGroup.fVariance = siGroup.fVariance;
rGroup.fAirResistance = siGroup.fAirResistance;
rGroup.bRandomRotation = siGroup.bRandomRotation;
rGroup.nRotationRangeToTerrainNormal = siGroup.nRotationRangeToTerrainNormal;
rGroup.nMaterialLayers = siGroup.nMaterialLayers;
rGroup.bAllowIndoor = siGroup.bAllowIndoor;
rGroup.fAlignToTerrainCoefficient = siGroup.fAlignToTerrainCoefficient;
bool previousAutoMerged = rGroup.bAutoMerged;
rGroup.bAutoMerged = siGroup.bAutoMerged;
rGroup.minConfigSpec = siGroup.minConfigSpec;
rGroup.nID = siGroup.nID;
rGroup.Update(GetCVars(), Get3DEngine()->GetGeomDetailScreenRes());
MarkRNTmpDataPoolForReset();
return true;
}
bool C3DEngine::GetStatInstGroup(int nGroupId, IStatInstGroup& siGroup, int nSID)
{
assert(nSID >= 0 && nSID < m_pObjManager->GetListStaticTypes().Count());
if (nGroupId < 0 || nGroupId >= m_pObjManager->GetListStaticTypes()[nSID].Count())
{
return false;
}
StatInstGroup& rGroup = m_pObjManager->GetListStaticTypes()[nSID][nGroupId];
siGroup.pStatObj = rGroup.pStatObj;
if (siGroup.pStatObj)
{
cry_strcpy(siGroup.szFileName, rGroup.pStatObj->GetFilePath());
}
siGroup.bHideability = rGroup.bHideability;
siGroup.bHideabilitySecondary = rGroup.bHideabilitySecondary;
siGroup.nPlayerHideable = rGroup.nPlayerHideable;
siGroup.fBending = rGroup.fBending;
siGroup.nCastShadowMinSpec = rGroup.nCastShadowMinSpec;
siGroup.bRecvShadow = rGroup.bRecvShadow;
siGroup.bDynamicDistanceShadows = rGroup.bDynamicDistanceShadows;
siGroup.bUseAlphaBlending = rGroup.bUseAlphaBlending;
siGroup.fSpriteDistRatio = rGroup.fSpriteDistRatio;
siGroup.fLodDistRatio = rGroup.fLodDistRatio;
siGroup.fShadowDistRatio = rGroup.fShadowDistRatio;
siGroup.fMaxViewDistRatio = rGroup.fMaxViewDistRatio;
siGroup.fBrightness = rGroup.fBrightness;
siGroup.pMaterial = rGroup.pMaterial;
siGroup.fDensity = rGroup.fDensity;
siGroup.fElevationMax = rGroup.fElevationMax;
siGroup.fElevationMin = rGroup.fElevationMin;
siGroup.fSize = rGroup.fSize;
siGroup.fSizeVar = rGroup.fSizeVar;
siGroup.fSlopeMax = rGroup.fSlopeMax;
siGroup.fSlopeMin = rGroup.fSlopeMin;
siGroup.bAutoMerged = rGroup.bAutoMerged;
siGroup.fStiffness = rGroup.fStiffness;
siGroup.fDamping = rGroup.fDamping;
siGroup.fVariance = rGroup.fVariance;
siGroup.fAirResistance = rGroup.fAirResistance;
siGroup.nID = rGroup.nID;
return true;
}
void C3DEngine::UpdateStatInstGroups()
{
if (!m_pObjManager)
{
return;
}
for (uint32 nSID = 0; nSID < m_pObjManager->GetListStaticTypes().size(); nSID++)
{
PodArray<StatInstGroup>& rGroupTable = m_pObjManager->GetListStaticTypes()[nSID];
for (uint32 nGroupId = 0; nGroupId < rGroupTable.size(); nGroupId++)
{
StatInstGroup& rGroup = rGroupTable[nGroupId];
rGroup.Update(GetCVars(), Get3DEngine()->GetGeomDetailScreenRes());
}
}
}
void C3DEngine::GetMemoryUsage(class ICrySizer* pSizer) const
{
pSizer->AddObject(this, sizeof(*this) + (GetCVars()->e_StreamCgfDebug ? 100 * 1024 * 1024 : 0));
pSizer->AddObject(m_pCVars);
pSizer->AddObject(m_lstStaticLights);
pSizer->AddObject(m_arrLightProjFrustums);
pSizer->AddObject(arrFPSforSaveLevelStats);
pSizer->AddObject(m_lstAlwaysVisible);
if (CTemporaryPool* pPool = CTemporaryPool::Get())
{
SIZER_COMPONENT_NAME(pSizer, "Temporary Pool");
pPool->GetMemoryUsage(pSizer);
}
{
SIZER_COMPONENT_NAME(pSizer, "RenderMeshMerger");
m_pRenderMeshMerger->GetMemoryUsage(pSizer);
}
{
SIZER_COMPONENT_NAME(pSizer, "Optics");
pSizer->AddObject(m_pOpticsManager);
}
{
SIZER_COMPONENT_NAME(pSizer, "SkyLightManager");
pSizer->AddObject(m_pSkyLightManager);
}
{
SIZER_COMPONENT_NAME(pSizer, "DecalManager");
pSizer->AddObject(m_pDecalManager);
}
{
SIZER_COMPONENT_NAME(pSizer, "OutdoorObjectsTree");
pSizer->AddObject(m_pObjectsTree);
}
{
SIZER_COMPONENT_NAME(pSizer, "ObjManager");
pSizer->AddObject(m_pObjManager);
}
{
SIZER_COMPONENT_NAME(pSizer, "Ocean");
pSizer->AddObject(m_pOcean);
}
{
SIZER_COMPONENT_NAME(pSizer, "VisAreas");
pSizer->AddObject(m_pVisAreaManager);
}
{
SIZER_COMPONENT_NAME(pSizer, "ClipVolumes");
pSizer->AddObject(m_pClipVolumeManager);
}
{
SIZER_COMPONENT_NAME(pSizer, "CoverageBuffer");
pSizer->AddObject(m_pCoverageBuffer);
}
{
SIZER_COMPONENT_NAME(pSizer, "RNTmpDataPool");
CRNTmpData* pNext = NULL;
for (CRNTmpData* pElem = m_LTPRootFree.pNext; pElem != &m_LTPRootFree; pElem = pNext)
{
pSizer->AddObject(pElem, sizeof(*pElem));
pNext = pElem->pNext;
}
for (CRNTmpData* pElem = m_LTPRootUsed.pNext; pElem != &m_LTPRootUsed; pElem = pNext)
{
pSizer->AddObject(pElem, sizeof(*pElem));
pNext = pElem->pNext;
}
}
}
void C3DEngine::GetResourceMemoryUsage(ICrySizer* pSizer, const AABB& cstAABB)
{
//////////////////////////////////////////////////////////////////////////
std::vector<IRenderNode*> cFoundRenderNodes;
unsigned int nFoundObjects(0);
nFoundObjects = GetObjectsInBox(cstAABB);
cFoundRenderNodes.resize(nFoundObjects, NULL);
GetObjectsInBox(cstAABB, &cFoundRenderNodes.front());
size_t nCurrentRenderNode(0);
size_t nTotalNumberOfRenderNodes(0);
nTotalNumberOfRenderNodes = cFoundRenderNodes.size();
for (nCurrentRenderNode = 0; nCurrentRenderNode < nTotalNumberOfRenderNodes; ++nCurrentRenderNode)
{
IRenderNode*& piRenderNode = cFoundRenderNodes[nCurrentRenderNode];
_smart_ptr<IMaterial> piMaterial(piRenderNode->GetMaterialOverride());
if (!piMaterial)
{
piMaterial = piRenderNode->GetMaterial();
}
if (piMaterial)
{
piMaterial->GetResourceMemoryUsage(pSizer);
}
{
IRenderMesh* piMesh(NULL);
size_t nCount(0);
piMesh = piRenderNode->GetRenderMesh(nCount);
for (; piMesh; ++nCount, piMesh = piRenderNode->GetRenderMesh(nCount))
{
// Timur, RenderMesh may not be loaded due to streaming!
piMesh->GetMemoryUsage(pSizer, IRenderMesh::MEM_USAGE_COMBINED);
}
}
}
//////////////////////////////////////////////////////////////////////////
}
bool C3DEngine::IsUnderWater(const Vec3& vPos) const
{
bool bUnderWater = false;
// Check underwater
AZ_UNUSED(vPos)
CRY_PHYSICS_REPLACEMENT_ASSERT();
return bUnderWater;
}
void C3DEngine::SetOceanRenderFlags(uint8 nFlags)
{
m_nOceanRenderFlags = nFlags;
}
uint32 C3DEngine::GetOceanVisiblePixelsCount() const
{
return COcean::GetVisiblePixelsCount();
}
float C3DEngine::GetBottomLevel(const Vec3& referencePos, float maxRelevantDepth, [[maybe_unused]] int objflags)
{
FUNCTION_PROFILER_3DENGINE;
float terrainWorldZ = AzFramework::Terrain::TerrainDataRequests::GetDefaultTerrainHeight();
AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult(terrainWorldZ
, &AzFramework::Terrain::TerrainDataRequests::GetHeightFromFloats
, referencePos.x, referencePos.y, AzFramework::Terrain::TerrainDataRequests::Sampler::BILINEAR, nullptr);
const float padding = 0.2f;
float rayLength;
// NOTE: Terrain is above referencePos, so referencePos is probably inside a voxel or something.
if (terrainWorldZ <= referencePos.z)
{
rayLength = min(maxRelevantDepth, (referencePos.z - terrainWorldZ));
}
else
{
rayLength = maxRelevantDepth;
}
rayLength += padding * 2.0f;
// Get bottom level
CRY_PHYSICS_REPLACEMENT_ASSERT();
// Terrain was above or too far below referencePos, and no solid object was close enough.
return BOTTOM_LEVEL_UNKNOWN;
}
float C3DEngine::GetBottomLevel(const Vec3& referencePos, float maxRelevantDepth /* = 10.0f */)
{
return GetBottomLevel (referencePos, maxRelevantDepth, ent_terrain | ent_static | ent_sleeping_rigid | ent_rigid);
}
float C3DEngine::GetBottomLevel(const Vec3& referencePos, int objflags)
{
return GetBottomLevel (referencePos, 10.0f, objflags);
}
#if defined(USE_GEOM_CACHES)
IGeomCache* C3DEngine::LoadGeomCache(const char* szFileName)
{
if (!szFileName || !szFileName[0])
{
m_pSystem->Warning(VALIDATOR_MODULE_3DENGINE, VALIDATOR_ERROR, 0, 0, "I3DEngine::LoadGeomCache: filename is not specified");
return 0;
}
return m_pGeomCacheManager->LoadGeomCache(szFileName);
}
IGeomCache* C3DEngine::FindGeomCacheByFilename(const char* szFileName)
{
if (!szFileName || szFileName[0] == 0)
{
return NULL;
}
return m_pGeomCacheManager->FindGeomCacheByFilename(szFileName);
}
#endif
namespace
{
// The return value is the next position of the source buffer.
int ReadFromBuffer(const char* pSource, int nSourceSize, int nSourcePos, void* pDest, int nDestSize)
{
if (pDest == 0 || nDestSize == 0)
{
return 0;
}
if (nSourcePos < 0 || nSourcePos >= nSourceSize)
{
return 0;
}
memcpy(pDest, &pSource[nSourcePos], nDestSize);
return nSourcePos + nDestSize;
}
}
IStatObj* C3DEngine::LoadDesignerObject(int nVersion, const char* szBinaryStream, int size)
{
if (nVersion < 0 || nVersion > 2)
{
return NULL;
}
int nBufferPos = 0;
int32 nSubObjectCount = 0;
nBufferPos = ReadFromBuffer(szBinaryStream, size, nBufferPos, &nSubObjectCount, sizeof(int32));
IStatObj* pStatObj = gEnv->p3DEngine->CreateStatObj();
if (!pStatObj)
{
return NULL;
}
std::vector<IStatObj*> statObjList;
if (nSubObjectCount == 2)
{
pStatObj->AddSubObject(gEnv->p3DEngine->CreateStatObj());
pStatObj->AddSubObject(gEnv->p3DEngine->CreateStatObj());
pStatObj->GetIndexedMesh()->FreeStreams();
statObjList.push_back(pStatObj->GetSubObject(0)->pStatObj);
statObjList.push_back(pStatObj->GetSubObject(1)->pStatObj);
}
else
{
statObjList.push_back(pStatObj);
}
if (nVersion == 2)
{
int32 nStaticObjFlags = 0;
nBufferPos = ReadFromBuffer(szBinaryStream, size, nBufferPos, &nStaticObjFlags, sizeof(int32));
pStatObj->SetFlags(nStaticObjFlags);
}
for (int k = 0, iCount(statObjList.size()); k < iCount; ++k)
{
int32 nPositionCount = 0;
int32 nTexCoordCount = 0;
int32 nFaceCount = 0;
int32 nIndexCount = 0;
int32 nTangentCount = 0;
int32 nSubsetCount = 0;
nBufferPos = ReadFromBuffer(szBinaryStream, size, nBufferPos, &nPositionCount, sizeof(int32));
nBufferPos = ReadFromBuffer(szBinaryStream, size, nBufferPos, &nTexCoordCount, sizeof(int32));
if (nPositionCount <= 0 || nTexCoordCount <= 0)
{
return NULL;
}
if (nVersion == 0)
{
nBufferPos = ReadFromBuffer(szBinaryStream, size, nBufferPos, &nFaceCount, sizeof(int32));
if (nFaceCount <= 0)
{
return NULL;
}
}
else if (nVersion >= 1)
{
nBufferPos = ReadFromBuffer(szBinaryStream, size, nBufferPos, &nIndexCount, sizeof(int32));
if (nIndexCount <= 0)
{
return NULL;
}
nBufferPos = ReadFromBuffer(szBinaryStream, size, nBufferPos, &nTangentCount, sizeof(int32));
}
nBufferPos = ReadFromBuffer(szBinaryStream, size, nBufferPos, &nSubsetCount, sizeof(int32));
IIndexedMesh* pMesh = statObjList[k]->GetIndexedMesh();
if (!pMesh)
{
return NULL;
}
pMesh->FreeStreams();
pMesh->SetVertexCount((int)nPositionCount);
pMesh->SetFaceCount((int)nFaceCount);
pMesh->SetIndexCount((int)nIndexCount);
pMesh->SetTexCoordCount((int)nTexCoordCount);
Vec3* const positions = pMesh->GetMesh()->GetStreamPtr<Vec3>(CMesh::POSITIONS);
Vec3* const normals = pMesh->GetMesh()->GetStreamPtr<Vec3>(CMesh::NORMALS);
SMeshTexCoord* const texcoords = pMesh->GetMesh()->GetStreamPtr<SMeshTexCoord>(CMesh::TEXCOORDS);
nBufferPos = ReadFromBuffer(szBinaryStream, size, nBufferPos, positions, sizeof(Vec3) * nPositionCount);
nBufferPos = ReadFromBuffer(szBinaryStream, size, nBufferPos, normals, sizeof(Vec3) * nPositionCount);
nBufferPos = ReadFromBuffer(szBinaryStream, size, nBufferPos, texcoords, sizeof(SMeshTexCoord) * nTexCoordCount);
if (nVersion == 0)
{
SMeshFace* const faces = pMesh->GetMesh()->GetStreamPtr<SMeshFace>(CMesh::FACES);
nBufferPos = ReadFromBuffer(szBinaryStream, size, nBufferPos, faces, sizeof(SMeshFace) * nFaceCount);
}
else if (nVersion >= 1)
{
vtx_idx* const indices = pMesh->GetMesh()->GetStreamPtr<vtx_idx>(CMesh::INDICES);
if constexpr (sizeof(vtx_idx) == sizeof(uint16))
{
nBufferPos = ReadFromBuffer(szBinaryStream, size, nBufferPos, indices, sizeof(uint16) * nIndexCount);
}
else
{
uint16* indices16 = new uint16[nIndexCount];
nBufferPos = ReadFromBuffer(szBinaryStream, size, nBufferPos, indices16, sizeof(uint16) * nIndexCount);
for (int32 i = 0; i < nIndexCount; ++i)
{
indices[i] = (vtx_idx)indices16[i];
}
delete [] indices16;
}
pMesh->SetTangentCount((int)nTangentCount);
if (nTangentCount > 0)
{
SMeshTangents* const tangents = pMesh->GetMesh()->GetStreamPtr<SMeshTangents>(CMesh::TANGENTS);
nBufferPos = ReadFromBuffer(szBinaryStream, size, nBufferPos, tangents, sizeof(SMeshTangents) * nTangentCount);
}
}
pMesh->SetSubSetCount(nSubsetCount);
for (int i = 0; i < nSubsetCount; ++i)
{
SMeshSubset subset;
nBufferPos = ReadFromBuffer(szBinaryStream, size, nBufferPos, &subset.vCenter, sizeof(Vec3));
nBufferPos = ReadFromBuffer(szBinaryStream, size, nBufferPos, &subset.fRadius, sizeof(float));
nBufferPos = ReadFromBuffer(szBinaryStream, size, nBufferPos, &subset.fTexelDensity, sizeof(float));
int32 nFirstIndexId = 0;
int32 nNumIndices = 0;
int32 nFirstVertId = 0;
int32 nNumVerts = 0;
int32 nMatID = 0;
int32 nMatFlags = 0;
int32 nPhysicalizeType = 0;
nBufferPos = ReadFromBuffer(szBinaryStream, size, nBufferPos, &nFirstIndexId, sizeof(int32));
nBufferPos = ReadFromBuffer(szBinaryStream, size, nBufferPos, &nNumIndices, sizeof(int32));
nBufferPos = ReadFromBuffer(szBinaryStream, size, nBufferPos, &nFirstVertId, sizeof(int32));
nBufferPos = ReadFromBuffer(szBinaryStream, size, nBufferPos, &nNumVerts, sizeof(int32));
nBufferPos = ReadFromBuffer(szBinaryStream, size, nBufferPos, &nMatID, sizeof(int32));
nBufferPos = ReadFromBuffer(szBinaryStream, size, nBufferPos, &nMatFlags, sizeof(int32));
nBufferPos = ReadFromBuffer(szBinaryStream, size, nBufferPos, &nPhysicalizeType, sizeof(int32));
pMesh->SetSubsetBounds(i, subset.vCenter, subset.fRadius);
pMesh->SetSubsetIndexVertexRanges(i, (int)nFirstIndexId, (int)nNumIndices, (int)nFirstVertId, (int)nNumVerts);
pMesh->SetSubsetMaterialId(i, nMatID == -1 ? 0 : (int)nMatID);
pMesh->SetSubsetMaterialProperties(i, (int)nMatFlags, (int)nPhysicalizeType, eVF_P3F_C4B_T2F);
}
if (nVersion == 0)
{
#if defined(WIN32) || defined(WIN64)
pMesh->Optimize();
#endif
}
statObjList[k]->Invalidate(true);
}
return pStatObj;
}
float C3DEngine::GetWaterLevel(const Vec3* pvPos, IPhysicalEntity* pent, bool bAccurate)
{
FUNCTION_PROFILER_3DENGINE;
if (!OceanGlobals::g_oceanParamsMutex.try_lock())
{
return OceanGlobals::g_oceanLevel;
}
bool bInVisarea = m_pVisAreaManager && m_pVisAreaManager->GetVisAreaFromPos(*pvPos) != 0;
AZ_UNUSED(pent);
float max_level = (!bInVisarea) ? (bAccurate ? GetAccurateOceanHeight(*pvPos) : GetWaterLevel()) : WATER_LEVEL_UNKNOWN;
OceanGlobals::g_oceanParamsMutex.unlock();
return max(WATER_LEVEL_UNKNOWN, max_level);
}
void C3DEngine::SetShadowsGSMCache(bool bCache)
{
if (bCache)
{
m_nGsmCache = m_pConsole->GetCVar("r_ShadowsCache")->GetIVal();
}
else
{
m_nGsmCache = 0;
}
}
float C3DEngine::GetAccurateOceanHeight(const Vec3& pCurrPos) const
{
FUNCTION_PROFILER_3DENGINE;
static int nFrameID = -1;
const int nEngineFrameID = GetRenderer()->GetFrameID();
static float fWaterLevel = 0;
if (nFrameID != nEngineFrameID && m_pOcean)
{
fWaterLevel = OceanToggle::IsActive() ? OceanRequest::GetOceanLevel() : m_pOcean->GetWaterLevel();
nFrameID = nEngineFrameID;
}
const float waterLevel = fWaterLevel + COcean::GetWave(pCurrPos, GetRenderer()->GetFrameID());
return waterLevel;
}
I3DEngine::CausticsParams C3DEngine::GetCausticsParams() const
{
I3DEngine::CausticsParams params;
if (OceanToggle::IsActive())
{
params.tiling = OceanRequest::GetCausticsTiling();
params.distanceAttenuation = OceanRequest::GetCausticsDistanceAttenuation();
params.depth = OceanRequest::GetCausticsDepth();
params.intensity = OceanRequest::GetCausticsIntensity();
}
else
{
params.tiling = m_oceanCausticsTiling;
params.distanceAttenuation = m_oceanCausticsDistanceAtten;
params.depth = m_oceanCausticDepth;
params.intensity = m_oceanCausticIntensity;
}
params.height = 0.0f; // @TODO: (@pruiksma) Parameter not currently accessible, need to decide whether to rip out or start using. [LY-62048]
return params;
}
void C3DEngine::GetHDRSetupParams(Vec4 pParams[5]) const
{
float hdrBloomAmount;
GetPostEffectParam("Global_User_HDRBloom", hdrBloomAmount);
pParams[0] = m_vHDRFilmCurveParams;
pParams[1] = Vec4(hdrBloomAmount * 0.3f, hdrBloomAmount * 0.3f, hdrBloomAmount * 0.3f, m_fGrainAmount);
pParams[2] = Vec4(m_vColorBalance, m_fHDRSaturation);
pParams[3] = Vec4(m_vHDREyeAdaptation, 1.0f);
pParams[4] = Vec4(m_vHDREyeAdaptationLegacy, 1.0f);
};
I3DEngine::OceanAnimationData C3DEngine::GetOceanAnimationParams() const
{
I3DEngine::OceanAnimationData data;
if (OceanToggle::IsActive())
{
data.fWavesAmount = OceanRequest::GetWavesAmount();
data.fWavesSize = OceanRequest::GetWavesSize();
data.fWavesSpeed = OceanRequest::GetWavesSpeed();
data.fWindDirection = OceanRequest::GetWindDirection();
data.fWindSpeed = OceanRequest::GetWindSpeed();
}
else
{
data.fWavesAmount = m_oceanWavesAmount;
data.fWavesSize = m_oceanWavesSize;
data.fWavesSpeed = m_oceanWavesSpeed;
data.fWindDirection = m_oceanWindDirection;
data.fWindSpeed = m_oceanWindSpeed;
}
sincos_tpl(data.fWindDirection, &data.fWindDirectionU, &data.fWindDirectionV);
return data;
}
IVisArea* C3DEngine::CreateVisArea(uint64 visGUID)
{
return m_pObjManager ? m_pVisAreaManager->CreateVisArea(visGUID) : 0;
}
void C3DEngine::DeleteVisArea(IVisArea* pVisArea)
{
if (!m_pVisAreaManager->IsValidVisAreaPointer((CVisArea*)pVisArea))
{
Warning("I3DEngine::DeleteVisArea: Invalid VisArea pointer");
return;
}
if (m_pObjManager)
{
CVisArea* pArea = (CVisArea*)pVisArea;
PodArray<SRNInfo> lstEntitiesInArea;
if (pArea->m_pObjectsTree)
{
pArea->m_pObjectsTree->MoveObjectsIntoList(&lstEntitiesInArea, NULL);
}
// unregister from indoor
for (int i = 0; i < lstEntitiesInArea.Count(); i++)
{
Get3DEngine()->UnRegisterEntityDirect(lstEntitiesInArea[i].pNode);
}
if (pArea->m_pObjectsTree)
{
assert(pArea->m_pObjectsTree->GetObjectsCount(eMain) == 0);
}
m_pVisAreaManager->DeleteVisArea((CVisArea*)pVisArea);
for (int i = 0; i < lstEntitiesInArea.Count(); i++)
{
Get3DEngine()->RegisterEntity(lstEntitiesInArea[i].pNode);
}
}
}
void C3DEngine::UpdateVisArea(IVisArea* pVisArea, const Vec3* pPoints, int nCount, const char* szName,
const SVisAreaInfo& info, bool bReregisterObjects)
{
if (!m_pObjManager)
{
return;
}
CVisArea* pArea = (CVisArea*)pVisArea;
// PrintMessage("C3DEngine::UpdateVisArea: %s", szName);
Vec3 vTotalBoxMin = pArea->m_boxArea.min;
Vec3 vTotalBoxMax = pArea->m_boxArea.max;
m_pVisAreaManager->UpdateVisArea((CVisArea*)pVisArea, pPoints, nCount, szName, info);
if (((CVisArea*)pVisArea)->m_pObjectsTree && ((CVisArea*)pVisArea)->m_pObjectsTree->GetObjectsCount(eMain))
{
// merge old and new bboxes
vTotalBoxMin.CheckMin(pArea->m_boxArea.min);
vTotalBoxMax.CheckMax(pArea->m_boxArea.max);
}
else
{
vTotalBoxMin = pArea->m_boxArea.min;
vTotalBoxMax = pArea->m_boxArea.max;
}
if (bReregisterObjects)
{
m_pObjManager->ReregisterEntitiesInArea(vTotalBoxMin - Vec3(8, 8, 8), vTotalBoxMax + Vec3(8, 8, 8));
}
}
IClipVolume* C3DEngine::CreateClipVolume()
{
return m_pClipVolumeManager->CreateClipVolume();
}
void C3DEngine::DeleteClipVolume(IClipVolume* pClipVolume)
{
m_pClipVolumeManager->DeleteClipVolume(pClipVolume);
}
void C3DEngine::UpdateClipVolume(IClipVolume* pClipVolume, _smart_ptr<IRenderMesh> pRenderMesh, IBSPTree3D* pBspTree, const Matrix34& worldTM, bool bActive, uint32 flags, const char* szName)
{
m_pClipVolumeManager->UpdateClipVolume(pClipVolume, pRenderMesh, pBspTree, worldTM, bActive, flags, szName);
}
void C3DEngine::ResetParticlesAndDecals()
{
if (m_pDecalManager)
{
m_pDecalManager->Reset();
}
}
IRenderNode* C3DEngine::CreateRenderNode(EERType type)
{
switch (type)
{
case eERType_Cloud:
{
CCloudRenderNode* pCloud = new CCloudRenderNode();
return pCloud;
}
case eERType_FogVolume:
{
CFogVolumeRenderNode* pFogVolume = new CFogVolumeRenderNode();
return pFogVolume;
}
case eERType_Decal:
{
CDecalRenderNode* pDecal = new CDecalRenderNode();
return pDecal;
}
case eERType_WaterVolume:
{
CWaterVolumeRenderNode* pWaterVolume = new CWaterVolumeRenderNode();
return pWaterVolume;
}
case eERType_DistanceCloud:
{
CDistanceCloudRenderNode* pRenderNode = new CDistanceCloudRenderNode();
return pRenderNode;
}
case eERType_VolumeObject:
{
IVolumeObjectRenderNode* pRenderNode = new CVolumeObjectRenderNode();
return pRenderNode;
}
#if !defined(EXCLUDE_DOCUMENTATION_PURPOSE)
case eERType_PrismObject:
{
IPrismRenderNode* pRenderNode = new CPrismRenderNode();
return pRenderNode;
}
#endif // EXCLUDE_DOCUMENTATION_PURPOSE
#if defined(USE_GEOM_CACHES)
case eERType_GeomCache:
{
IRenderNode* pRenderNode = new CGeomCacheRenderNode();
return pRenderNode;
}
#endif
}
assert(!"C3DEngine::CreateRenderNode: Specified node type is not supported.");
return 0;
}
void C3DEngine::DeleteRenderNode(IRenderNode* pRenderNode)
{
UnRegisterEntityDirect(pRenderNode);
delete pRenderNode;
}
Vec3 C3DEngine::GetWind(const AABB& box, bool bIndoors) const
{
FUNCTION_PROFILER_3DENGINE;
#if defined(CONSOLE_CONST_CVAR_MODE)
if constexpr (!CVars::e_Wind)
#else
if (!m_pCVars->e_Wind)
#endif
{
return Vec3_Zero;
}
// Start with global wind.
Vec3 vWind = GetGlobalWind(bIndoors);
#if defined(CONSOLE_CONST_CVAR_MODE)
if constexpr (CVars::e_WindAreas)
#else
if (m_pCVars->e_WindAreas)
#endif
{
const Physics::WindRequests* windRequests = AZ::Interface<Physics::WindRequests>::Get();
if (windRequests)
{
const AZ::Aabb aabb = LyAABBToAZAabb(box);
const AZ::Vector3 wind = windRequests->GetWind(aabb);
vWind += AZVec3ToLYVec3(wind);
}
}
return vWind;
}
Vec3 C3DEngine::GetGlobalWind(bool bIndoors) const
{
FUNCTION_PROFILER_3DENGINE;
// We assume indoor wind is zero.
if (!m_pCVars->e_Wind || bIndoors)
{
return Vec3_Zero;
}
const Physics::WindRequests* windRequests = AZ::Interface<Physics::WindRequests>::Get();
if (windRequests)
{
const AZ::Vector3 wind = windRequests->GetGlobalWind();
return AZVec3ToLYVec3(wind);
}
return Vec3_Zero;
}
bool C3DEngine::SampleWind(Vec3* pSamples, int nSamples, const AABB& volume, bool bIndoors) const
{
FUNCTION_PROFILER_3DENGINE;
if (!m_pCVars->e_Wind || nSamples == 0)
{
return false;
}
IF (m_nWindSamplePositions < (size_t)nSamples, 0)
{
if (m_pWindSamplePositions)
{
CryModuleMemalignFree(m_pWindSamplePositions);
}
m_nWindSamplePositions = nSamples;
m_pWindSamplePositions = (Vec3*)CryModuleMemalign(m_nWindSamplePositions * sizeof(Vec3), 128u);
}
Vec3* pPositions = m_pWindSamplePositions;
memcpy(pPositions, pSamples, sizeof(Vec3) * nSamples);
// Start with global wind.
Vec3 vWind = GetGlobalWind(bIndoors);
for (int i = 0; i < nSamples; ++i)
{
pSamples[i] = vWind;
}
#if defined(CONSOLE_CONST_CVAR_MODE)
if constexpr (CVars::e_WindAreas)
#else
if (m_pCVars->e_WindAreas)
#endif
{
const Physics::WindRequests* windRequests = AZ::Interface<Physics::WindRequests>::Get();
if (windRequests)
{
for (int i = 0; i < nSamples; ++i)
{
const AZ::Vector3 position = LYVec3ToAZVec3(pPositions[i]);
const AZ::Vector3 wind = windRequests->GetWind(position);
pSamples[i] += AZVec3ToLYVec3(wind);
}
}
}
AZ_UNUSED(volume);
return true;
}
void C3DEngine::SetupBending(CRenderObject*& pObj, const IRenderNode* pNode, const float fRadiusVert, const SRenderingPassInfo& passInfo, bool alreadyDuplicated)
{
FUNCTION_PROFILER_3DENGINE;
if (!GetCVars()->e_VegetationBending)
{
return;
}
if (!pNode->m_pRNTmpData)
{
return;
}
// Get/Update PhysAreaChanged Proxy
{
// Lock to avoid a situation where two threads simultaneously find that nProxId is ~0,
// which would result in two physics proxies for the same render node which eventually leads to a crash
AUTO_LOCK(m_PhysicsAreaUpdates.m_Mutex);
const uint32 nProxyId = pNode->m_pRNTmpData->nPhysAreaChangedProxyId;
if (nProxyId != ~0)
{
m_PhysicsAreaUpdates.UpdateProxy(pNode, nProxyId);
}
else
{
pNode->m_pRNTmpData->nPhysAreaChangedProxyId = m_PhysicsAreaUpdates.CreateProxy(pNode, Area_Air);
}
}
CRNTmpData::SRNUserData& userData = pNode->m_pRNTmpData->userData;
const bool needsUpdate = userData.nBendingLastFrame != (passInfo.GetMainFrameID() & ~(3 << 29));
const bool isFirstFrame = userData.nBendingLastFrame == 0;
const Vec3& vObjPos = pObj->GetTranslation();
const float fMaxViewDist = const_cast<IRenderNode*>(pNode)->GetMaxViewDist();
const float fBendingAttenuation = 1.f - (pObj->m_fDistance / (fMaxViewDist * GetFloatCVar(e_WindBendingDistRatio)));
uint32 bendingMaskOr = 0u;
uint32 bendingMaskAnd = ~FOB_BENDED;
if (fBendingAttenuation > 0.f && userData.m_Bending.m_fMainBendingScale > 0.f)
{
bendingMaskOr = FOB_BENDED;
bendingMaskAnd = ~0u;
userData.m_BendingPrev = userData.m_Bending;
if (needsUpdate)
{
userData.nBendingLastFrame = passInfo.GetMainFrameID();
static const float fBEND_RESPONSE = 0.25f;
static const float fMAX_BENDING = 2.f;
static const float fWAVE_PARALLEL = 0.008f;
static const float fWAVE_TRANSVERSE = 0.002f;
if (!userData.bWindCurrent)
{
userData.vCurrentWind = m_p3DEngine->GetWind(pNode->GetBBox(), !!pNode->GetEntityVisArea());
userData.bWindCurrent = true;
}
// Soft clamp bending from wind amplitude.
Vec2 vBending = Vec2(userData.vCurrentWind) * fBEND_RESPONSE;
vBending *= fMAX_BENDING / (fMAX_BENDING + vBending.GetLength());
vBending *= fBendingAttenuation;
SBending* pBending = &userData.m_Bending;
float fWaveFreq = 0.4f / (fRadiusVert + 1.f) + 0.2f;
if (!userData.bBendingSet)
{
// First time shown, set full bending.
pBending->m_vBending = vBending;
userData.bBendingSet = true;
}
else
{
// Already visible, fade toward current value.
float fInterp = min(gEnv->pTimer->GetFrameTime() * fWaveFreq * 0.5f, 1.f);
pBending->m_vBending += (vBending - pBending->m_vBending) * fInterp;
}
pBending->m_Waves[0].m_Level = 0.000f;
pBending->m_Waves[0].m_Freq = fWaveFreq;
pBending->m_Waves[0].m_Phase = vObjPos.x * 0.125f;
pBending->m_Waves[0].m_Amp = pBending->m_vBending.x * fWAVE_PARALLEL + pBending->m_vBending.y * fWAVE_TRANSVERSE;
pBending->m_Waves[0].m_eWFType = eWF_Sin;
pBending->m_Waves[1].m_Level = 0.000f;
pBending->m_Waves[1].m_Freq = fWaveFreq * 1.125f;
pBending->m_Waves[1].m_Phase = vObjPos.y * 0.125f;
pBending->m_Waves[1].m_Amp = pBending->m_vBending.y * fWAVE_PARALLEL - pBending->m_vBending.x * fWAVE_TRANSVERSE;
pBending->m_Waves[1].m_eWFType = eWF_Sin;
}
// When starting fresh, we use the same bend info for previous so
// that we don't get crazy motion changes.
if (isFirstFrame)
{
userData.m_BendingPrev = userData.m_Bending;
}
}
if (!alreadyDuplicated)
{
pObj = GetRenderer()->EF_DuplicateRO(pObj, passInfo);
}
SRenderObjData* pOD = GetRenderer()->EF_GetObjData(pObj, true, passInfo.ThreadID());
if (!pOD)
{
return;
}
pObj->m_ObjFlags |= bendingMaskOr | FOB_DYNAMIC_OBJECT | FOB_MOTION_BLUR;
pObj->m_ObjFlags &= bendingMaskAnd;
pOD->m_pBending = Get3DEngine()->GetBendingEntry(&userData.m_Bending, passInfo);
pOD->m_BendingPrev = Get3DEngine()->GetBendingEntry(&userData.m_BendingPrev, passInfo);
}
IVisArea* C3DEngine::GetVisAreaFromPos(const Vec3& vPos)
{
if (m_pObjManager && m_pVisAreaManager)
{
return m_pVisAreaManager->GetVisAreaFromPos(vPos);
}
return 0;
}
bool C3DEngine::IntersectsVisAreas(const AABB& box, void** pNodeCache)
{
if (m_pObjManager && m_pVisAreaManager)
{
return m_pVisAreaManager->IntersectsVisAreas(box, pNodeCache);
}
return false;
}
bool C3DEngine::ClipToVisAreas(IVisArea* pInside, Sphere& sphere, Vec3 const& vNormal, void* pNodeCache)
{
if (pInside)
{
return pInside->ClipToVisArea(true, sphere, vNormal);
}
else if (m_pVisAreaManager)
{
return m_pVisAreaManager->ClipOutsideVisAreas(sphere, vNormal, pNodeCache);
}
return false;
}
bool C3DEngine::IsVisAreasConnected(IVisArea* pArea1, IVisArea* pArea2, int nMaxRecursion, bool bSkipDisabledPortals)
{
if (pArea1 == pArea2)
{
return (true); // includes the case when both pArea1 & pArea2 are NULL (totally outside)
}
// not considered by the other checks
if (!pArea1 || !pArea2)
{
return (false); // avoid a crash - better to put this check only
}
// here in one place than in all the places where this function is called
nMaxRecursion *= 2; // include portals since portals are the areas
if (m_pObjManager && m_pVisAreaManager)
{
return ((CVisArea*)pArea1)->FindVisArea((CVisArea*)pArea2, nMaxRecursion, bSkipDisabledPortals);
}
return false;
}
bool C3DEngine::IsOutdoorVisible()
{
if (m_pObjManager && m_pVisAreaManager)
{
return m_pVisAreaManager->IsOutdoorAreasVisible();
}
return false;
}
void C3DEngine::EnableOceanRendering(bool bOcean)
{
m_bOcean = bOcean;
}
IObjManager* C3DEngine::GetObjManager()
{
return Cry3DEngineBase::GetObjManager();
}
//////////////////////////////////////////////////////////////////////////
IMaterialHelpers& C3DEngine::GetMaterialHelpers()
{
return Cry3DEngineBase::GetMatMan()->s_materialHelpers;
}
IMaterialManager* C3DEngine::GetMaterialManager()
{
return Cry3DEngineBase::GetMatMan();
}
//////////////////////////////////////////////////////////////////////////
void C3DEngine::AddTextureLoadHandler(ITextureLoadHandler* pHandler)
{
stl::push_back_unique(m_textureLoadHandlers, pHandler);
}
void C3DEngine::RemoveTextureLoadHandler(ITextureLoadHandler* pHandler)
{
stl::find_and_erase(m_textureLoadHandlers, pHandler);
}
ITextureLoadHandler* C3DEngine::GetTextureLoadHandlerForImage(const char* path)
{
const char* ext = PathUtil::GetExt(path);
for (TTextureLoadHandlers::iterator iter = m_textureLoadHandlers.begin(); iter != m_textureLoadHandlers.end(); iter++)
{
if ((*iter)->SupportsExtension(ext))
{
return *iter;
}
}
return nullptr;
}
void C3DEngine::CheckMemoryHeap()
{
assert (CryMemory::IsHeapValid());
}
//////////////////////////////////////////////////////////////////////////
int C3DEngine::GetLoadedObjectCount()
{
int nObjectsLoaded = m_pObjManager ? m_pObjManager->GetLoadedObjectCount() : 0;
return nObjectsLoaded;
}
//////////////////////////////////////////////////////////////////////////
void C3DEngine::GetLoadedStatObjArray(IStatObj** pObjectsArray, int& nCount)
{
if (m_pObjManager)
{
m_pObjManager->GetLoadedStatObjArray(pObjectsArray, nCount);
}
else
{
nCount = 0;
}
}
//////////////////////////////////////////////////////////////////////////
void C3DEngine::GetObjectsStreamingStatus(SObjectsStreamingStatus& outStatus)
{
if (m_pObjManager)
{
m_pObjManager->GetObjectsStreamingStatus(outStatus);
}
else
{
memset(&outStatus, 0, sizeof(outStatus));
}
}
//////////////////////////////////////////////////////////////////////////
void C3DEngine::GetStreamingSubsystemData(int subsystem, SStremaingBandwidthData& outData)
{
switch (subsystem)
{
case eStreamTaskTypeSound:
// Audio: bandwidth stats
break;
case eStreamTaskTypeGeometry:
m_pObjManager->GetBandwidthStats(&outData.fBandwidthRequested);
break;
case eStreamTaskTypeTexture:
gEnv->pRenderer->GetBandwidthStats(&outData.fBandwidthRequested);
break;
}
#if defined(STREAMENGINE_ENABLE_STATS)
gEnv->pSystem->GetStreamEngine()->GetBandwidthStats((EStreamTaskType)subsystem, &outData.fBandwidthActual);
#endif
}
//////////////////////////////////////////////////////////////////////////
void C3DEngine::DeleteEntityDecals(IRenderNode* pEntity)
{
if (m_pDecalManager && pEntity && (pEntity->m_nInternalFlags & IRenderNode::DECAL_OWNER))
{
m_pDecalManager->OnEntityDeleted(pEntity);
}
}
void C3DEngine::DeleteDecalsInRange(AABB* pAreaBox, IRenderNode* pEntity)
{
if (m_pDecalManager)
{
m_pDecalManager->DeleteDecalsInRange(pAreaBox, pEntity);
}
}
void C3DEngine::LockCGFResources()
{
if (m_pObjManager)
{
m_pObjManager->SetLockCGFResources(true);
}
}
void C3DEngine::UnlockCGFResources()
{
if (m_pObjManager)
{
bool bNeedToFreeCGFs = m_pObjManager->IsLockCGFResources();
m_pObjManager->SetLockCGFResources(false);
if (bNeedToFreeCGFs)
{
m_pObjManager->FreeNotUsedCGFs();
}
}
}
void C3DEngine::FreeUnusedCGFResources()
{
if (m_pObjManager)
{
m_pObjManager->FreeNotUsedCGFs();
}
}
void CLightEntity::ShadowMapInfo::Release([[maybe_unused]] struct IRenderer* pRenderer)
{
delete this;
}
//////////////////////////////////////////////////////////////////////////
IIndexedMesh* C3DEngine::CreateIndexedMesh()
{
return new CIndexedMesh();
}
void C3DEngine::SerializeState(TSerialize ser)
{
ser.Value("m_bOcean", m_bOcean);
ser.Value("m_moonRotationLatitude", m_moonRotationLatitude);
ser.Value("m_moonRotationLongitude", m_moonRotationLongitude);
ser.Value("m_eShadowMode", *(alias_cast<int*>(&m_eShadowMode)));
if (ser.IsReading())
{
UpdateMoonDirection();
}
if (m_pDecalManager)
{
m_pDecalManager->Serialize(ser);
}
m_pTimeOfDay->Serialize(ser);
}
void C3DEngine::PostSerialize(bool /*bReading*/)
{
}
void C3DEngine::InitMaterialDefautMappingAxis(_smart_ptr<IMaterial> pMat)
{
uint8 arrProj[3] = {'X', 'Y', 'Z'};
pMat->m_ucDefautMappingAxis = 'Z';
pMat->m_fDefautMappingScale = 1.f;
for (int c = 0; c < 3 && c < pMat->GetSubMtlCount(); c++)
{
_smart_ptr<IMaterial> pSubMat = (_smart_ptr<IMaterial>)pMat->GetSubMtl(c);
pSubMat->m_ucDefautMappingAxis = arrProj[c];
pSubMat->m_fDefautMappingScale = pMat->m_fDefautMappingScale;
}
}
//////////////////////////////////////////////////////////////////////////
CContentCGF* C3DEngine::CreateChunkfileContent(const char* filename)
{
return new CContentCGF(filename);
}
void C3DEngine::ReleaseChunkfileContent(CContentCGF* pCGF)
{
delete pCGF;
}
bool C3DEngine::LoadChunkFileContent(CContentCGF* pCGF, const char* filename, bool bNoWarningMode, bool bCopyChunkFile)
{
CLoadLogListener listener;
if (!pCGF)
{
FileWarning(0, filename, "CGF Loading Failed: no content instance passed");
}
else
{
CLoaderCGF cgf;
CReadOnlyChunkFile* pChunkFile = new CReadOnlyChunkFile(bCopyChunkFile, bNoWarningMode);
if (cgf.LoadCGF(pCGF, filename, *pChunkFile, &listener))
{
pCGF->SetChunkFile(pChunkFile);
return true;
}
CryWarning(VALIDATOR_MODULE_3DENGINE, VALIDATOR_WARNING, "%s: Failed to load chunk file: '%s'", __FUNCTION__, cgf.GetLastError());
delete pChunkFile;
}
return false;
}
bool C3DEngine::LoadChunkFileContentFromMem(CContentCGF* pCGF, const void* pData, size_t nDataLen, uint32 nLoadingFlags, bool bNoWarningMode, bool bCopyChunkFile)
{
if (!pCGF)
{
FileWarning(0, "<memory>", "CGF Loading Failed: no content instance passed");
}
else
{
CLoadLogListener listener;
CLoaderCGF cgf;
CReadOnlyChunkFile* pChunkFile = new CReadOnlyChunkFile(bCopyChunkFile, bNoWarningMode);
if (cgf.LoadCGFFromMem(pCGF, pData, nDataLen, *pChunkFile, &listener, nLoadingFlags))
{
pCGF->SetChunkFile(pChunkFile);
return true;
}
CryWarning(VALIDATOR_MODULE_3DENGINE, VALIDATOR_WARNING, "%s: Failed to load chunk file: '%s'", __FUNCTION__, cgf.GetLastError());
delete pChunkFile;
}
return false;
}
IChunkFile* C3DEngine::CreateChunkFile(bool bReadOnly)
{
if (bReadOnly)
{
return new CReadOnlyChunkFile(false);
}
else
{
return new CChunkFile;
}
}
//////////////////////////////////////////////////////////////////////////
ChunkFile::IChunkFileWriter* C3DEngine::CreateChunkFileWriter(EChunkFileFormat eFormat, AZ::IO::IArchive* pPak, const char* filename) const
{
ChunkFile::CryPakFileWriter* const p = new ChunkFile::CryPakFileWriter();
if (!p)
{
return 0;
}
if (!p->Create(pPak, filename))
{
delete p;
return 0;
}
const ChunkFile::MemorylessChunkFileWriter::EChunkFileFormat fmt =
(eFormat == I3DEngine::eChunkFileFormat_0x745)
? ChunkFile::MemorylessChunkFileWriter::eChunkFileFormat_0x745
: ChunkFile::MemorylessChunkFileWriter::eChunkFileFormat_0x746;
return new ChunkFile::MemorylessChunkFileWriter(fmt, p);
}
void C3DEngine::ReleaseChunkFileWriter(ChunkFile::IChunkFileWriter* p) const
{
if (p)
{
delete p->GetWriter();
delete p;
}
}
//////////////////////////////////////////////////////////////////////////
bool C3DEngine::CreateOcean(_smart_ptr<IMaterial> pTerrainWaterMat, float waterLevel)
{
// make ocean surface
SAFE_DELETE(m_pOcean);
if (pTerrainWaterMat == nullptr)
{
if (!m_pTerrainWaterMat)
{
return false;
}
pTerrainWaterMat = m_pTerrainWaterMat;
}
if (OceanToggle::IsActive() ? OceanRequest::OceanIsEnabled() : (waterLevel > 0))
{
m_pOcean = new COcean(pTerrainWaterMat, waterLevel);
}
return m_pOcean != nullptr;
}
void C3DEngine::DeleteOcean()
{
SAFE_DELETE(m_pOcean);
}
void C3DEngine::ChangeOceanMaterial(_smart_ptr<IMaterial> pMat)
{
if (m_pOcean)
{
m_pOcean->SetMaterial(pMat);
}
}
void C3DEngine::ChangeOceanWaterLevel(float fWaterLevel)
{
COcean::SetWaterLevelInfo(fWaterLevel);
if (m_pOcean)
{
m_pOcean->SetWaterLevel(fWaterLevel);
}
}
void C3DEngine::SetStreamableListener(IStreamedObjectListener* pListener)
{
m_pStreamListener = pListener;
}
void C3DEngine::PrecacheLevel(bool bPrecacheAllVisAreas, Vec3* pPrecachePoints, int nPrecachePointsNum)
{
LOADING_TIME_PROFILE_SECTION;
if (GetVisAreaManager())
{
GetVisAreaManager()->PrecacheLevel(bPrecacheAllVisAreas, pPrecachePoints, nPrecachePointsNum);
}
}
//////////////////////////////////////////////////////////////////////////
void C3DEngine::SetGlobalParameter(E3DEngineParameter param, const Vec3& v)
{
float fValue = v.x;
switch (param)
{
case E3DPARAM_SUN_COLOR:
SetSunColor(v);
break;
case E3DPARAM_SUN_SPECULAR_MULTIPLIER:
m_fSunSpecMult = v.x;
break;
case E3DPARAM_AMBIENT_GROUND_COLOR:
m_vAmbGroundCol = v;
break;
case E3DPARAM_AMBIENT_MIN_HEIGHT:
m_fAmbMaxHeight = v.x;
break;
case E3DPARAM_AMBIENT_MAX_HEIGHT:
m_fAmbMinHeight = v.x;
break;
case E3DPARAM_SKY_HIGHLIGHT_POS:
m_vSkyHightlightPos = v;
break;
case E3DPARAM_SKY_HIGHLIGHT_COLOR:
m_vSkyHightlightCol = v;
break;
case E3DPARAM_SKY_HIGHLIGHT_SIZE:
m_fSkyHighlightSize = fValue > 0.0f ? fValue : 0.0f;
break;
case E3DPARAM_VOLFOG_RAMP:
m_volFogRamp = v;
break;
case E3DPARAM_VOLFOG_SHADOW_RANGE:
m_volFogShadowRange = v;
break;
case E3DPARAM_VOLFOG_SHADOW_DARKENING:
m_volFogShadowDarkening = v;
break;
case E3DPARAM_VOLFOG_SHADOW_ENABLE:
m_volFogShadowEnable = v;
break;
case E3DPARAM_VOLFOG2_CTRL_PARAMS:
m_volFog2CtrlParams = v;
break;
case E3DPARAM_VOLFOG2_SCATTERING_PARAMS:
m_volFog2ScatteringParams = v;
break;
case E3DPARAM_VOLFOG2_RAMP:
m_volFog2Ramp = v;
break;
case E3DPARAM_VOLFOG2_COLOR:
m_volFog2Color = v;
break;
case E3DPARAM_VOLFOG2_GLOBAL_DENSITY:
m_volFog2GlobalDensity = v;
break;
case E3DPARAM_VOLFOG2_HEIGHT_DENSITY:
m_volFog2HeightDensity = v;
break;
case E3DPARAM_VOLFOG2_HEIGHT_DENSITY2:
m_volFog2HeightDensity2 = v;
break;
case E3DPARAM_VOLFOG2_COLOR1:
m_volFog2Color1 = v;
break;
case E3DPARAM_VOLFOG2_COLOR2:
m_volFog2Color2 = v;
break;
case E3DPARAM_NIGHSKY_HORIZON_COLOR:
m_nightSkyHorizonCol = v;
break;
case E3DPARAM_NIGHSKY_ZENITH_COLOR:
m_nightSkyZenithCol = v;
break;
case E3DPARAM_NIGHSKY_ZENITH_SHIFT:
m_nightSkyZenithColShift = v.x;
break;
case E3DPARAM_NIGHSKY_STAR_INTENSITY:
m_nightSkyStarIntensity = v.x;
break;
case E3DPARAM_NIGHSKY_MOON_COLOR:
m_nightMoonCol = v;
break;
case E3DPARAM_NIGHSKY_MOON_SIZE:
m_nightMoonSize = v.x;
break;
case E3DPARAM_NIGHSKY_MOON_INNERCORONA_COLOR:
m_nightMoonInnerCoronaCol = v;
break;
case E3DPARAM_NIGHSKY_MOON_INNERCORONA_SCALE:
m_nightMoonInnerCoronaScale = v.x;
break;
case E3DPARAM_NIGHSKY_MOON_OUTERCORONA_COLOR:
m_nightMoonOuterCoronaCol = v;
break;
case E3DPARAM_NIGHSKY_MOON_OUTERCORONA_SCALE:
m_nightMoonOuterCoronaScale = v.x;
break;
case E3DPARAM_OCEANFOG_COLOR:
m_oceanFogColor = v;
break;
case E3DPARAM_OCEANFOG_DENSITY:
m_oceanFogDensity = v.x;
break;
case E3DPARAM_SKY_MOONROTATION:
m_moonRotationLatitude = v.x;
m_moonRotationLongitude = v.y;
UpdateMoonDirection();
break;
case E3DPARAM_SKYBOX_MULTIPLIER:
m_skyboxMultiplier = v.x;
break;
case E3DPARAM_DAY_NIGHT_INDICATOR:
m_dayNightIndicator = v.x;
// Audio: Set daylight parameter
break;
case E3DPARAM_FOG_COLOR2:
m_fogColor2 = v;
break;
case E3DPARAM_FOG_RADIAL_COLOR:
m_fogColorRadial = v;
break;
case E3DPARAM_VOLFOG_HEIGHT_DENSITY:
m_volFogHeightDensity = Vec3(v.x, v.y, 0);
break;
case E3DPARAM_VOLFOG_HEIGHT_DENSITY2:
m_volFogHeightDensity2 = Vec3(v.x, v.y, 0);
break;
case E3DPARAM_VOLFOG_GRADIENT_CTRL:
m_volFogGradientCtrl = v;
break;
case E3DPARAM_VOLFOG_GLOBAL_DENSITY:
m_volFogGlobalDensity = v.x;
m_volFogFinalDensityClamp = v.z;
break;
case E3DPARAM_COLORGRADING_FILTERS_PHOTOFILTER_COLOR:
m_pPhotoFilterColor = Vec4(v.x, v.y, v.z, 1);
GetPostEffectBaseGroup()->SetParam("clr_ColorGrading_PhotoFilterColor", m_pPhotoFilterColor);
break;
case E3DPARAM_COLORGRADING_FILTERS_PHOTOFILTER_DENSITY:
m_fPhotoFilterColorDensity = fValue;
GetPostEffectBaseGroup()->SetParam("ColorGrading_PhotoFilterColorDensity", m_fPhotoFilterColorDensity);
break;
case E3DPARAM_COLORGRADING_FILTERS_GRAIN:
m_fGrainAmount = fValue;
GetPostEffectBaseGroup()->SetParam("ColorGrading_GrainAmount", m_fGrainAmount);
break;
case E3DPARAM_SKY_SKYBOX_ANGLE: // sky box rotation
m_fSkyBoxAngle = fValue;
break;
case E3DPARAM_SKY_SKYBOX_STRETCHING: // sky box stretching
m_fSkyBoxStretching = fValue;
break;
case E3DPARAM_HDR_FILMCURVE_SHOULDER_SCALE:
m_vHDRFilmCurveParams.x = v.x;
break;
case E3DPARAM_HDR_FILMCURVE_LINEAR_SCALE:
m_vHDRFilmCurveParams.y = v.x;
break;
case E3DPARAM_HDR_FILMCURVE_TOE_SCALE:
m_vHDRFilmCurveParams.z = v.x;
break;
case E3DPARAM_HDR_FILMCURVE_WHITEPOINT:
m_vHDRFilmCurveParams.w = v.x;
break;
case E3DPARAM_HDR_EYEADAPTATION_PARAMS:
m_vHDREyeAdaptation = v;
break;
case E3DPARAM_HDR_EYEADAPTATION_PARAMS_LEGACY:
m_vHDREyeAdaptationLegacy = v;
break;
case E3DPARAM_HDR_BLOOM_AMOUNT:
m_fHDRBloomAmount = v.x;
break;
case E3DPARAM_HDR_COLORGRADING_COLOR_SATURATION:
m_fHDRSaturation = v.x;
break;
case E3DPARAM_HDR_COLORGRADING_COLOR_BALANCE:
m_vColorBalance = v;
break;
case E3DPARAM_CLOUDSHADING_MULTIPLIERS:
m_fCloudShadingSunLightMultiplier = (v.x >= 0) ? v.x : 0;
m_fCloudShadingSkyLightMultiplier = (v.y >= 0) ? v.y : 0;
break;
case E3DPARAM_CLOUDSHADING_SUNCOLOR:
m_vCloudShadingCustomSunColor = v;
break;
case E3DPARAM_CLOUDSHADING_SKYCOLOR:
m_vCloudShadingCustomSkyColor = v;
break;
case E3DPARAM_NIGHSKY_MOON_DIRECTION: // moon direction is fixed per level or updated via FG node (E3DPARAM_SKY_MOONROTATION)
default:
assert(0);
break;
}
}
//////////////////////////////////////////////////////////////////////////
void C3DEngine::GetGlobalParameter(E3DEngineParameter param, Vec3& v)
{
switch (param)
{
case E3DPARAM_SUN_COLOR:
v = GetSunColor();
break;
case E3DPARAM_SUN_SPECULAR_MULTIPLIER:
v = Vec3(m_fSunSpecMult, 0, 0);
break;
case E3DPARAM_AMBIENT_GROUND_COLOR:
v = m_vAmbGroundCol;
break;
case E3DPARAM_AMBIENT_MIN_HEIGHT:
v = Vec3(m_fAmbMaxHeight, 0, 0);
break;
case E3DPARAM_AMBIENT_MAX_HEIGHT:
v = Vec3(m_fAmbMinHeight, 0, 0);
break;
case E3DPARAM_SKY_HIGHLIGHT_POS:
v = m_vSkyHightlightPos;
break;
case E3DPARAM_SKY_HIGHLIGHT_COLOR:
v = m_vSkyHightlightCol;
break;
case E3DPARAM_SKY_HIGHLIGHT_SIZE:
v = Vec3(m_fSkyHighlightSize, 0, 0);
break;
case E3DPARAM_VOLFOG_RAMP:
v = m_volFogRamp;
break;
case E3DPARAM_VOLFOG_SHADOW_RANGE:
v = m_volFogShadowRange;
break;
case E3DPARAM_VOLFOG_SHADOW_DARKENING:
v = m_volFogShadowDarkening;
break;
case E3DPARAM_VOLFOG_SHADOW_ENABLE:
v = m_volFogShadowEnable;
break;
case E3DPARAM_VOLFOG2_CTRL_PARAMS:
v = m_volFog2CtrlParams;
break;
case E3DPARAM_VOLFOG2_SCATTERING_PARAMS:
v = m_volFog2ScatteringParams;
break;
case E3DPARAM_VOLFOG2_RAMP:
v = m_volFog2Ramp;
break;
case E3DPARAM_VOLFOG2_COLOR:
v = m_volFog2Color;
break;
case E3DPARAM_VOLFOG2_GLOBAL_DENSITY:
v = m_volFog2GlobalDensity;
break;
case E3DPARAM_VOLFOG2_HEIGHT_DENSITY:
v = m_volFog2HeightDensity;
break;
case E3DPARAM_VOLFOG2_HEIGHT_DENSITY2:
v = m_volFog2HeightDensity2;
break;
case E3DPARAM_VOLFOG2_COLOR1:
v = m_volFog2Color1;
break;
case E3DPARAM_VOLFOG2_COLOR2:
v = m_volFog2Color2;
break;
case E3DPARAM_NIGHSKY_HORIZON_COLOR:
v = m_nightSkyHorizonCol;
break;
case E3DPARAM_NIGHSKY_ZENITH_COLOR:
v = m_nightSkyZenithCol;
break;
case E3DPARAM_NIGHSKY_ZENITH_SHIFT:
v = Vec3(m_nightSkyZenithColShift, 0, 0);
break;
case E3DPARAM_NIGHSKY_STAR_INTENSITY:
v = Vec3(m_nightSkyStarIntensity, 0, 0);
break;
case E3DPARAM_NIGHSKY_MOON_DIRECTION:
v = m_moonDirection;
break;
case E3DPARAM_NIGHSKY_MOON_COLOR:
v = m_nightMoonCol;
break;
case E3DPARAM_NIGHSKY_MOON_SIZE:
v = Vec3(m_nightMoonSize, 0, 0);
break;
case E3DPARAM_NIGHSKY_MOON_INNERCORONA_COLOR:
v = m_nightMoonInnerCoronaCol;
break;
case E3DPARAM_NIGHSKY_MOON_INNERCORONA_SCALE:
v = Vec3(m_nightMoonInnerCoronaScale, 0, 0);
break;
case E3DPARAM_NIGHSKY_MOON_OUTERCORONA_COLOR:
v = m_nightMoonOuterCoronaCol;
break;
case E3DPARAM_NIGHSKY_MOON_OUTERCORONA_SCALE:
v = Vec3(m_nightMoonOuterCoronaScale, 0, 0);
break;
case E3DPARAM_SKY_MOONROTATION:
v = Vec3(m_moonRotationLatitude, m_moonRotationLongitude, 0);
break;
case E3DPARAM_OCEANFOG_COLOR:
v = m_oceanFogColor;
break;
case E3DPARAM_OCEANFOG_DENSITY:
v = Vec3(m_oceanFogDensity, 0, 0);
break;
case E3DPARAM_SKYBOX_MULTIPLIER:
v = Vec3(m_skyboxMultiplier, 0, 0);
break;
case E3DPARAM_DAY_NIGHT_INDICATOR:
v = Vec3(m_dayNightIndicator, 0, 0);
break;
case E3DPARAM_FOG_COLOR2:
v = m_fogColor2;
break;
case E3DPARAM_FOG_RADIAL_COLOR:
v = m_fogColorRadial;
break;
case E3DPARAM_VOLFOG_HEIGHT_DENSITY:
v = m_volFogHeightDensity;
break;
case E3DPARAM_VOLFOG_HEIGHT_DENSITY2:
v = m_volFogHeightDensity2;
break;
case E3DPARAM_VOLFOG_GRADIENT_CTRL:
v = m_volFogGradientCtrl;
break;
case E3DPARAM_VOLFOG_GLOBAL_DENSITY:
v = Vec3(m_volFogGlobalDensity, m_volFogGlobalDensityMultiplierLDR, m_volFogFinalDensityClamp);
break;
case E3DPARAM_COLORGRADING_FILTERS_PHOTOFILTER_COLOR:
v = Vec3(m_pPhotoFilterColor.x, m_pPhotoFilterColor.y, m_pPhotoFilterColor.z);
break;
case E3DPARAM_COLORGRADING_FILTERS_PHOTOFILTER_DENSITY:
v = Vec3(m_fPhotoFilterColorDensity, 0, 0);
break;
case E3DPARAM_COLORGRADING_FILTERS_GRAIN:
v = Vec3(m_fGrainAmount, 0, 0);
break;
case E3DPARAM_HDR_FILMCURVE_SHOULDER_SCALE:
v = Vec3(m_vHDRFilmCurveParams.x, 0, 0);
break;
case E3DPARAM_HDR_FILMCURVE_LINEAR_SCALE:
v = Vec3(m_vHDRFilmCurveParams.y, 0, 0);
break;
case E3DPARAM_HDR_FILMCURVE_TOE_SCALE:
v = Vec3(m_vHDRFilmCurveParams.z, 0, 0);
break;
case E3DPARAM_HDR_FILMCURVE_WHITEPOINT:
v = Vec3(m_vHDRFilmCurveParams.w, 0, 0);
break;
case E3DPARAM_HDR_EYEADAPTATION_PARAMS:
v = m_vHDREyeAdaptation;
break;
case E3DPARAM_HDR_EYEADAPTATION_PARAMS_LEGACY:
v = m_vHDREyeAdaptationLegacy;
break;
case E3DPARAM_HDR_BLOOM_AMOUNT:
v = Vec3(m_fHDRBloomAmount, 0, 0);
break;
case E3DPARAM_HDR_COLORGRADING_COLOR_SATURATION:
v = Vec3(m_fHDRSaturation, 0, 0);
break;
case E3DPARAM_HDR_COLORGRADING_COLOR_BALANCE:
v = m_vColorBalance;
break;
case E3DPARAM_CLOUDSHADING_MULTIPLIERS:
v = Vec3(m_fCloudShadingSunLightMultiplier, m_fCloudShadingSkyLightMultiplier, 0);
break;
case E3DPARAM_CLOUDSHADING_SUNCOLOR:
v = m_vCloudShadingCustomSunColor;
break;
case E3DPARAM_CLOUDSHADING_SKYCOLOR:
v = m_vCloudShadingCustomSkyColor;
break;
default:
assert(0);
break;
}
}
//////////////////////////////////////////////////////////////////////////
void C3DEngine::SetCachedShadowBounds(const AABB& shadowBounds, float fAdditionalCascadesScale)
{
const Vec3 boxSize = shadowBounds.GetSize();
const bool isValid = boxSize.x > 0 && boxSize.y > 0 && boxSize.z > 0;
m_CachedShadowsBounds = isValid ? shadowBounds : AABB(AABB::RESET);
m_fCachedShadowsCascadeScale = fAdditionalCascadesScale;
m_nCachedShadowsUpdateStrategy = ShadowMapFrustum::ShadowCacheData::eFullUpdate;
}
//////////////////////////////////////////////////////////////////////////
void C3DEngine::SetRecomputeCachedShadows(uint nUpdateStrategy)
{
m_nCachedShadowsUpdateStrategy = nUpdateStrategy;
}
//////////////////////////////////////////////////////////////////////////
void C3DEngine::SetShadowsCascadesBias(const float* pCascadeConstBias, const float* pCascadeSlopeBias)
{
memcpy(m_pShadowCascadeConstBias, pCascadeConstBias, sizeof(float) * MAX_SHADOW_CASCADES_NUM);
memcpy(m_pShadowCascadeSlopeBias, pCascadeSlopeBias, sizeof(float) * MAX_SHADOW_CASCADES_NUM);
}
int C3DEngine::GetShadowsCascadeCount([[maybe_unused]] const CDLight* pLight) const
{
int nCascadeCount = m_eShadowMode == ESM_HIGHQUALITY ? MAX_GSM_LODS_NUM : GetCVars()->e_GsmLodsNum;
return clamp_tpl(nCascadeCount, 0, MAX_GSM_LODS_NUM);
}
//////////////////////////////////////////////////////////////////////////
bool C3DEngine::CheckIntersectClouds(const Vec3& p1, const Vec3& p2)
{
return m_pCloudsManager->CheckIntersectClouds(p1, p2);
}
void C3DEngine::OnRenderMeshDeleted(IRenderMesh* pRenderMesh)
{
if (m_pDecalManager)
{
m_pDecalManager->OnRenderMeshDeleted(pRenderMesh);
}
}
bool C3DEngine::RayObjectsIntersection2D([[maybe_unused]] Vec3 vStart, [[maybe_unused]] Vec3 vEnd, [[maybe_unused]] Vec3& vHitPoint, [[maybe_unused]] EERType eERType)
{
#ifdef SUPPORT_TERRAIN_AO_PRE_COMPUTATIONS
float fClosestHitDistance = 1000000;
AABB aabb;
aabb.Reset();
aabb.Add(vStart);
aabb.Add(vEnd);
if (IsObjectTreeReady())
{
if (Overlap::AABB_AABB2D(aabb, m_pObjectsTree->GetNodeBox())) //!!!mpeykov: 2D?
{
m_pObjectsTree->RayObjectsIntersection2D(vStart, vEnd, vHitPoint, fClosestHitDistance, eERType);
}
}
return (fClosestHitDistance < 1000000);
#else
assert(!"C3DEngine::RayObjectsIntersection2D not supported on consoles");
return 0;
#endif
}
bool C3DEngine::RenderMeshRayIntersection(IRenderMesh* pRenderMesh, SRayHitInfo& hitInfo, _smart_ptr<IMaterial> pCustomMtl)
{
return CRenderMeshUtils::RayIntersection(pRenderMesh, hitInfo, pCustomMtl);
}
static SBending sBendRemoved;
void C3DEngine::FreeRNTmpData(CRNTmpData** ppInfo)
{
AUTO_LOCK(m_checkCreateRNTmpData);
CRNTmpData* pTmpRNTData = (*ppInfo);
assert(pTmpRNTData->pNext != &m_LTPRootFree);
assert(pTmpRNTData->pPrev != &m_LTPRootFree);
assert(pTmpRNTData != &m_LTPRootUsed);
if (gEnv->mMainThreadId != CryGetCurrentThreadId())
{
CryFatalError("CRNTmpData should only be allocated and free'd on main thread.");
}
if (pTmpRNTData != &m_LTPRootUsed)
{
pTmpRNTData->Unlink();
}
// Mark for phys area changed proxy for deletion
if (pTmpRNTData->nPhysAreaChangedProxyId != ~0)
{
m_PhysicsAreaUpdates.ResetProxy(pTmpRNTData->nPhysAreaChangedProxyId);
pTmpRNTData->nPhysAreaChangedProxyId = ~0;
}
if (pTmpRNTData->nFrameInfoId != ~0)
{
m_elementFrameInfo[pTmpRNTData->nFrameInfoId].Reset();
pTmpRNTData->nFrameInfoId = ~0;
}
{
#ifdef SUPP_HWOBJ_OCCL
if (pTmpRNTData->userData.m_OcclState.pREOcclusionQuery)
{
pTmpRNTData->userData.m_OcclState.pREOcclusionQuery->Release(false);
pTmpRNTData->userData.m_OcclState.pREOcclusionQuery = NULL;
}
#endif
}
if (pTmpRNTData != &m_LTPRootUsed)
{
pTmpRNTData->Link(&m_LTPRootFree);
*(pTmpRNTData->pOwnerRef) = NULL;
}
}
void C3DEngine::UpdateRNTmpDataPool(bool bFreeAll)
{
// if we are freeing the whole pool, make sure no jobs are still running which could use the RNTmpObjects
if (bFreeAll)
{
threadID nThreadID;
gEnv->pRenderer->EF_Query(EFQ_MainThreadList, nThreadID);
gEnv->pRenderer->GetFinalizeRendItemJobExecutor(nThreadID)->WaitForCompletion();
gEnv->pRenderer->GetFinalizeShadowRendItemJobExecutor(nThreadID)->WaitForCompletion();
}
//CryLogAlways("UpdateRNTmpDataPool bFreeAll 0x%x nMainFrameID 0X%x", bFreeAll, nMainFrameID );
FUNCTION_PROFILER_3DENGINE;
// Ensure continues memory
m_elementFrameInfo.CoalesceMemory();
const uint32 nSize = m_elementFrameInfo.size();
if (nSize == 0)
{
return;
}
const uint32 nMainFrameID = GetRenderer()->GetFrameID(false);
const uint32 nTmpDataMaxFrames = (uint32)GetCVars()->e_RNTmpDataPoolMaxFrames;
if (!bFreeAll && nMainFrameID >= nTmpDataMaxFrames)
{
const uint32 nLastValidFrame = nMainFrameID - nTmpDataMaxFrames;
uint32 nNumItemsToDelete = 0;
SFrameInfo* pFrontIter = &m_elementFrameInfo[0];
SFrameInfo* pBackIter = &m_elementFrameInfo[nSize - 1];
SFrameInfo* pHead = pFrontIter;
// Handle single element case
IF (nSize == 1, 0)
{
if (pFrontIter->bIsValid && pFrontIter->nLastUsedFrameId >= nLastValidFrame)
{
if (pFrontIter->bIsValid)
{
FreeRNTmpData(pFrontIter->ppRNTmpData);
}
++nNumItemsToDelete;
}
}
// Move invalid elements to back of array and free if timed out
while (pFrontIter < pBackIter)
{
while (pFrontIter->bIsValid && pFrontIter->nLastUsedFrameId >= nLastValidFrame && pFrontIter < pBackIter)
{
++pFrontIter;
} // Find invalid element at front
while (!(pBackIter->bIsValid && pBackIter->nLastUsedFrameId >= nLastValidFrame) && pFrontIter < pBackIter) // Find valid element at back
{
// Element timed out
if (pBackIter->bIsValid)
{
FreeRNTmpData(pBackIter->ppRNTmpData);
pBackIter->bIsValid = false;
}
--pBackIter;
++nNumItemsToDelete;
}
if (pFrontIter < pBackIter)
{
// Element timed out
if (pFrontIter->bIsValid)
{
FreeRNTmpData(pFrontIter->ppRNTmpData);
}
// Replace invalid front element with back element
// Note: No need to swap because we cut the data from the array at the end anyway
memcpy(pFrontIter, pBackIter, sizeof(SFrameInfo));
(*pFrontIter->ppRNTmpData)->nFrameInfoId = (uint32)(pFrontIter - pHead);
pBackIter->bIsValid = false; // safety
pBackIter->ppRNTmpData = 0;
--pBackIter;
++pFrontIter;
++nNumItemsToDelete;
}
}
assert(nSize == m_elementFrameInfo.size());
m_elementFrameInfo.resize(nSize - nNumItemsToDelete);
}
else if (bFreeAll) // Free all
{
SFrameInfo* pFrontIter = &m_elementFrameInfo[0];
SFrameInfo* pBackIter = &m_elementFrameInfo[nSize - 1];
++pBackIter; // Point to element after last element in array
// Free all
while (pFrontIter != pBackIter)
{
// Element timed out
if (pFrontIter->bIsValid)
{
FreeRNTmpData(pFrontIter->ppRNTmpData);
}
++pFrontIter;
}
m_elementFrameInfo.resize(0);
}
}
void C3DEngine::FreeRNTmpDataPool()
{
// move all into m_LTPRootFree
UpdateRNTmpDataPool(true);
AUTO_LOCK(m_checkCreateRNTmpData);
if (gEnv->mMainThreadId != CryGetCurrentThreadId())
{
CryFatalError("CRNTmpData should only be allocated and free'd on main thread.");
}
// delete all elements of m_LTPRootFree
CRNTmpData* pNext = NULL;
for (CRNTmpData* pElem = m_LTPRootFree.pNext; pElem != &m_LTPRootFree; pElem = pNext)
{
pNext = pElem->pNext;
pElem->Unlink();
delete pElem;
}
}
void C3DEngine::CopyObjectsByType(EERType objType, const AABB* pBox, PodArray<IRenderNode*>* plstObjects, ObjectTreeQueryFilterCallback filterCallback)
{
GetObjectsByTypeGlobal(*plstObjects, objType, pBox, filterCallback);
if (GetVisAreaManager())
{
GetVisAreaManager()->GetObjectsByType(*plstObjects, objType, pBox, filterCallback);
}
}
void C3DEngine::CopyObjects(const AABB* pBox, PodArray<IRenderNode*>* plstObjects)
{
if (IsObjectTreeReady())
{
m_pObjectsTree->GetObjects(*plstObjects, pBox);
}
if (GetVisAreaManager())
{
GetVisAreaManager()->GetObjects(*plstObjects, pBox);
}
}
uint32 C3DEngine::GetObjectsByType(EERType objType, IRenderNode** pObjects)
{
PodArray<IRenderNode*> lstObjects;
CopyObjectsByType(objType, NULL, &lstObjects);
if (pObjects && !lstObjects.IsEmpty())
{
memcpy(pObjects, &lstObjects[0], lstObjects.GetDataSize());
}
return lstObjects.Count();
}
uint32 C3DEngine::GetObjectsByTypeInBox(EERType objType, const AABB& bbox, IRenderNode** pObjects, ObjectTreeQueryFilterCallback filterCallback)
{
PodArray<IRenderNode*> lstObjects;
CopyObjectsByType(objType, &bbox, &lstObjects, filterCallback);
if (pObjects && !lstObjects.IsEmpty())
{
memcpy(pObjects, &lstObjects[0], lstObjects.GetDataSize());
}
return lstObjects.Count();
}
void C3DEngine::GetObjectsByTypeInBox(EERType objType, const AABB& bbox, PodArray<IRenderNode*>* pLstObjects, ObjectTreeQueryFilterCallback filterCallback)
{
CopyObjectsByType(objType, &bbox, pLstObjects, filterCallback);
}
uint32 C3DEngine::GetObjectsInBox(const AABB& bbox, IRenderNode** pObjects)
{
PodArray<IRenderNode*> lstObjects;
CopyObjects(&bbox, &lstObjects);
if (pObjects && !lstObjects.IsEmpty())
{
memcpy(pObjects, &lstObjects[0], lstObjects.GetDataSize());
}
return lstObjects.Count();
}
uint32 C3DEngine::GetObjectsByFlags(uint dwFlags, IRenderNode** pObjects /* =0 */)
{
PodArray<IRenderNode*> lstObjects;
if (Get3DEngine()->IsObjectTreeReady())
{
Get3DEngine()->GetObjectTree()->GetObjectsByFlags(dwFlags, lstObjects);
}
if (GetVisAreaManager())
{
GetVisAreaManager()->GetObjectsByFlags(dwFlags, lstObjects);
}
if (pObjects && !lstObjects.IsEmpty())
{
memcpy(pObjects, &lstObjects[0], lstObjects.GetDataSize());
}
return lstObjects.Count();
}
void C3DEngine::OnObjectModified([[maybe_unused]] IRenderNode* pRenderNode, uint dwFlags)
{
if ((dwFlags & (ERF_CASTSHADOWMAPS | ERF_HAS_CASTSHADOWMAPS)) != 0)
{
SetRecomputeCachedShadows(ShadowMapFrustum::ShadowCacheData::eFullUpdate);
}
}
int SImageInfo::GetMemoryUsage()
{
int nSize = 0;
if (detailInfo.pImgMips[0])
{
nSize += (int)((float)(detailInfo.nDim * detailInfo.nDim * sizeof(ColorB)) * 1.3f);
}
if (baseInfo.pImgMips[0])
{
nSize += (int)((float)(baseInfo.nDim * baseInfo.nDim * sizeof(ColorB)) * 1.3f);
}
return nSize;
}
byte** C3DEngine::AllocateMips(byte* pImage, int nDim, byte** pImageMips)
{
memset(pImageMips, 0, SImageSubInfo::nMipsNum * sizeof(pImageMips[0]));
pImageMips[0] = new byte[nDim * nDim * sizeof(ColorB)];
memcpy(pImageMips[0], pImage, nDim * nDim * sizeof(ColorB));
ColorB* pMipMain = (ColorB*)pImageMips[0];
for (int nMip = 1; (nDim >> nMip) && nMip < SImageSubInfo::nMipsNum; nMip++)
{
int nDimMip = nDim >> nMip;
int nSubSize = 1 << nMip;
pImageMips[nMip] = new byte[nDimMip * nDimMip * sizeof(ColorB)];
ColorB* pMipThis = (ColorB*)pImageMips[nMip];
for (int x = 0; x < nDimMip; x++)
{
for (int y = 0; y < nDimMip; y++)
{
ColorF colSumm(0, 0, 0, 0);
float fCount = 0;
for (int _x = x * nSubSize - nSubSize / 2; _x < x * nSubSize + nSubSize + nSubSize / 2; _x++)
{
for (int _y = y * nSubSize - nSubSize / 2; _y < y * nSubSize + nSubSize + nSubSize / 2; _y++)
{
int nMask = nDim - 1;
int id = (_x & nMask) * nDim + (_y & nMask);
colSumm.r += 1.f / 255.f * pMipMain[id].r;
colSumm.g += 1.f / 255.f * pMipMain[id].g;
colSumm.b += 1.f / 255.f * pMipMain[id].b;
colSumm.a += 1.f / 255.f * pMipMain[id].a;
fCount++;
}
}
colSumm /= fCount;
colSumm.Clamp(0, 1);
pMipThis[x * nDimMip + y] = colSumm;
}
}
}
return pImageMips;
}
void C3DEngine::RegisterForStreaming(IStreamable* pObj)
{
if (GetObjManager())
{
GetObjManager()->RegisterForStreaming(pObj);
}
}
void C3DEngine::UnregisterForStreaming(IStreamable* pObj)
{
if (GetObjManager())
{
GetObjManager()->UnregisterForStreaming(pObj);
}
}
SImageSubInfo* C3DEngine::GetImageInfo(const char* pName)
{
if (m_imageInfos.find(string(pName)) != m_imageInfos.end())
{
return m_imageInfos[string(pName)];
}
return NULL;
}
SImageSubInfo* C3DEngine::RegisterImageInfo(byte** pMips, int nDim, const char* pName)
{
if (m_imageInfos.find(string(pName)) != m_imageInfos.end())
{
return m_imageInfos[string(pName)];
}
assert(pMips && pMips[0]);
SImageSubInfo* pImgSubInfo = new SImageSubInfo;
pImgSubInfo->nDim = nDim;
int nMipDim = pImgSubInfo->nDim;
for (int m = 0; m < SImageSubInfo::nMipsNum && nMipDim; m++)
{
pImgSubInfo->pImgMips[m] = new byte[nMipDim * nMipDim * 4];
memcpy(pImgSubInfo->pImgMips[m], pMips[m], nMipDim * nMipDim * 4);
nMipDim /= 2;
}
const string strFileName = pName;
pImgSubInfo->nReady = 1;
m_imageInfos[strFileName] = pImgSubInfo;
return pImgSubInfo;
}
void C3DEngine::SyncProcessStreamingUpdate()
{
if (m_pObjManager)
{
m_pObjManager->ProcessObjectsStreaming_Finish();
}
}
void C3DEngine::SetScreenshotCallback(IScreenshotCallback* pCallback)
{
m_pScreenshotCallback = pCallback;
}
void C3DEngine::ActivateObjectsLayer(uint16 nLayerId, bool bActivate, bool bPhys, bool bObjects, bool bStaticLights, const char* pLayerName, IGeneralMemoryHeap* pHeap, bool bCheckLayerActivation /*=true*/)
{
if (bCheckLayerActivation && !IsAreaActivationInUse())
{
return;
}
if (bActivate)
{
m_bLayersActivated = true;
}
if (bActivate && m_nFramesSinceLevelStart <= 1)
{
m_vPrevMainFrameCamPos.Set(-1000000.f, -1000000.f, -1000000.f);
}
FUNCTION_PROFILER_3DENGINE;
PrintMessage("%s object layer %s (Id = %d) (LevelFrameId = %d)", bActivate ? "Activating" : "Deactivating", pLayerName, nLayerId, m_nFramesSinceLevelStart);
INDENT_LOG_DURING_SCOPE();
if (bObjects)
{
if (IsObjectTreeReady())
{
m_pObjectsTree->ActivateObjectsLayer(nLayerId, bActivate, bPhys, pHeap);
}
if (m_pVisAreaManager)
{
m_pVisAreaManager->ActivateObjectsLayer(nLayerId, bActivate, bPhys, pHeap);
}
}
if (bStaticLights)
{
for (size_t i = 0; i < m_lstStaticLights.size(); ++i)
{
ILightSource* pLight = m_lstStaticLights[i];
if (pLight->GetLayerId() == nLayerId)
{
pLight->SetRndFlags(ERF_HIDDEN, !bActivate);
}
}
}
}
void C3DEngine::GetLayerMemoryUsage(uint16 nLayerId, ICrySizer* pSizer, int* pNumBrushes, int* pNumDecals) const
{
if (pNumBrushes)
{
*pNumBrushes = 0;
}
if (pNumDecals)
{
*pNumDecals = 0;
}
if (m_pObjectsTree != nullptr)
{
m_pObjectsTree->GetLayerMemoryUsage(nLayerId, pSizer, pNumBrushes, pNumDecals);
}
}
void C3DEngine::SkipLayerLoading(uint16 nLayerId, bool bClearList)
{
if (bClearList)
{
m_skipedLayers.clear();
}
m_skipedLayers.insert(nLayerId);
}
bool C3DEngine::IsLayerSkipped(uint16 nLayerId)
{
return m_skipedLayers.find(nLayerId) != m_skipedLayers.end() ? true : false;
}
void C3DEngine::PrecacheRenderNode(IRenderNode* pObj, float fEntDistanceReal)
{
FUNCTION_PROFILER_3DENGINE;
// PrintMessage("==== PrecacheRenderNodePrecacheRenderNode: %s ====", pObj->GetName());
if (m_pObjManager)
{
int dwOldRndFlags = pObj->m_dwRndFlags;
pObj->m_dwRndFlags &= ~ERF_HIDDEN;
SRenderingPassInfo passInfo = SRenderingPassInfo::CreateGeneralPassRenderingInfo(gEnv->pSystem->GetViewCamera());
m_pObjManager->UpdateRenderNodeStreamingPriority(pObj, fEntDistanceReal, 1.0f, fEntDistanceReal < GetFloatCVar(e_StreamCgfFastUpdateMaxDistance), passInfo, true);
pObj->m_dwRndFlags = dwOldRndFlags;
}
}
void C3DEngine::CleanUpOldDecals()
{
FUNCTION_PROFILER_3DENGINE;
static uint32 nLastIndex = 0;
const uint32 nDECALS_PER_FRAME = 50;
if (uint32 nNumDecalRenderNodes = m_decalRenderNodes.size())
{
for (uint32 i = 0, end = __min(nDECALS_PER_FRAME, nNumDecalRenderNodes); i < end; ++i, ++nLastIndex)
{
// wrap around at the end to restart at the beginning
if (nLastIndex >= nNumDecalRenderNodes)
{
nLastIndex = 0;
}
m_decalRenderNodes[nLastIndex]->CleanUpOldDecals();
}
}
}
void C3DEngine::UpdateRenderTypeEnableLookup()
{
SetRenderNodeTypeEnabled(eERType_RenderComponent, (GetCVars()->e_Entities != 0));
SetRenderNodeTypeEnabled(eERType_StaticMeshRenderComponent, (GetCVars()->e_Entities != 0));
SetRenderNodeTypeEnabled(eERType_DynamicMeshRenderComponent, (GetCVars()->e_Entities != 0));
SetRenderNodeTypeEnabled(eERType_SkinnedMeshRenderComponent, (GetCVars()->e_Entities != 0));
}
void C3DEngine::SetRenderNodeMaterialAtPosition(EERType eNodeType, const Vec3& vPos, _smart_ptr<IMaterial> pMat)
{
PodArray<IRenderNode*> lstObjects;
AABB aabbPos(vPos - Vec3(.1f, .1f, .1f), vPos + Vec3(.1f, .1f, .1f));
GetObjectsByTypeGlobal(lstObjects, eNodeType, &aabbPos);
if (GetVisAreaManager())
{
GetVisAreaManager()->GetObjectsByType(lstObjects, eNodeType, &aabbPos);
}
for (int i = 0; i < lstObjects.Count(); i++)
{
PrintMessage("Game changed render node material: %s EERType:%d pos: (%d,%d,%d)",
pMat ? pMat->GetName() : "NULL",
(int)eNodeType,
(int)vPos.x, (int)vPos.y, (int)vPos.z);
lstObjects[i]->SetMaterial(pMat);
}
}
void C3DEngine::OverrideCameraPrecachePoint(const Vec3& vPos)
{
if (m_pObjManager)
{
m_pObjManager->GetStreamPreCacheCameras()[0].vPosition = vPos;
m_pObjManager->SetCameraPrecacheOverridden(true);
}
}
int C3DEngine::AddPrecachePoint(const Vec3& vPos, const Vec3& vDir, float fTimeOut, float fImportanceFactor)
{
if (m_pObjManager)
{
if (m_pObjManager->GetStreamPreCachePointDefs().size() >= CObjManager::MaxPrecachePoints)
{
size_t nOldestIdx = 0;
int nOldestId = INT_MAX;
for (size_t i = 1, c = m_pObjManager->GetStreamPreCachePointDefs().size(); i < c; ++i)
{
if (m_pObjManager->GetStreamPreCachePointDefs()[i].nId < nOldestId)
{
nOldestIdx = i;
nOldestId = m_pObjManager->GetStreamPreCachePointDefs()[i].nId;
}
}
assert (nOldestIdx > 0);
CryWarning(VALIDATOR_MODULE_3DENGINE, VALIDATOR_WARNING, "Precache points full - evicting oldest (%f, %f, %f)",
m_pObjManager->GetStreamPreCacheCameras()[nOldestIdx].vPosition.x,
m_pObjManager->GetStreamPreCacheCameras()[nOldestIdx].vPosition.y,
m_pObjManager->GetStreamPreCacheCameras()[nOldestIdx].vPosition.z);
m_pObjManager->GetStreamPreCachePointDefs().DeleteFastUnsorted((int)nOldestIdx);
m_pObjManager->GetStreamPreCacheCameras().DeleteFastUnsorted((int)nOldestIdx);
}
SObjManPrecachePoint pp;
pp.nId = m_pObjManager->IncrementNextPrecachePointId();
pp.expireTime = gEnv->pTimer->GetAsyncTime() + CTimeValue(fTimeOut);
SObjManPrecacheCamera pc;
pc.vPosition = vPos;
pc.bbox = AABB(vPos, GetCVars()->e_StreamPredictionBoxRadius);
pc.vDirection = vDir;
pc.fImportanceFactor = fImportanceFactor;
m_pObjManager->GetStreamPreCachePointDefs().Add(pp);
m_pObjManager->GetStreamPreCacheCameras().Add(pc);
return pp.nId;
}
return -1;
}
void C3DEngine::ClearPrecachePoint(int id)
{
if (m_pObjManager)
{
for (size_t i = 1, c = m_pObjManager->GetStreamPreCachePointDefs().size(); i < c; ++i)
{
if (m_pObjManager->GetStreamPreCachePointDefs()[i].nId == id)
{
m_pObjManager->GetStreamPreCachePointDefs().DeleteFastUnsorted((int)i);
m_pObjManager->GetStreamPreCacheCameras().DeleteFastUnsorted((int)i);
break;
}
}
}
}
void C3DEngine::ClearAllPrecachePoints()
{
if (m_pObjManager)
{
m_pObjManager->GetStreamPreCachePointDefs().resize(1);
m_pObjManager->GetStreamPreCacheCameras().resize(1);
}
}
void C3DEngine::GetPrecacheRoundIds(int pRoundIds[MAX_STREAM_PREDICTION_ZONES])
{
if (m_pObjManager)
{
pRoundIds[0] = m_pObjManager->GetUpdateStreamingPrioriryRoundIdFast();
pRoundIds[1] = m_pObjManager->GetUpdateStreamingPrioriryRoundId();
}
}
SBending* C3DEngine::GetBendingEntry(SBending* pSrc, [[maybe_unused]] const SRenderingPassInfo& passInfo)
{
assert(pSrc);
SBending* pStorage = m_bendingPool[m_bendingPoolIdx].push_back_new();
*pStorage = *pSrc;
return pStorage;
}
///////////////////////////////////////////////////////////////////////////////
CCamera* C3DEngine::GetRenderingPassCamera(const CCamera& rCamera)
{
threadID nThreadID = 0;
gEnv->pRenderer->EF_Query(EFQ_RenderThreadList, nThreadID);
CCamera* pCamera = m_RenderingPassCameras[nThreadID].push_back_new();
*pCamera = rCamera;
return pCamera;
}
void C3DEngine::GetCollisionClass(SCollisionClass& collclass, int tableIndex)
{
if ((unsigned)tableIndex < m_collisionClasses.size())
{
collclass = m_collisionClasses[tableIndex];
}
else
{
collclass = SCollisionClass(0, 0);
}
}
class C3DEngine::PhysicsAreaUpdatesHandler
: private Physics::WindNotificationsBus::Handler
{
public:
explicit PhysicsAreaUpdatesHandler(C3DEngine::PhysicsAreaUpdates& physicsAreaUpdates)
: m_physicsAreaUpdates(physicsAreaUpdates)
{
Physics::WindNotificationsBus::Handler::BusConnect();
}
~PhysicsAreaUpdatesHandler()
{
Physics::WindNotificationsBus::Handler::BusDisconnect();
}
private:
// Physics::WindNotificationsBus::Handler
void OnGlobalWindChanged() override
{
// Using same 'global wind' area size value CryPhysics code had
const AZ::Vector3 globalWindHalfBound(1e7);
AZ::Aabb globalWindAabb = AZ::Aabb::CreateFromMinMax(-globalWindHalfBound, globalWindHalfBound);
OnWindChanged(globalWindAabb);
}
void OnWindChanged(const AZ::Aabb& aabb) override
{
SAreaChangeRecord record;
record.boxAffected = AZAabbToLyAABB(aabb);
record.uPhysicsMask = Area_Air;
m_physicsAreaUpdates.SetAreaDirty(record);
}
C3DEngine::PhysicsAreaUpdates& m_physicsAreaUpdates;
};
void C3DEngine::PhysicsAreaUpdates::SetAreaDirty(const SAreaChangeRecord& rec)
{
// Merge with existing bb if close enough and same medium
AUTO_LOCK(m_Mutex);
static const float fMERGE_THRESHOLD = 2.f;
float fNewVolume = rec.boxAffected.GetVolume();
for (uint i = 0; i < m_DirtyAreas.size(); i++)
{
if (m_DirtyAreas[i].uPhysicsMask == rec.uPhysicsMask)
{
AABB bbUnion = rec.boxAffected;
bbUnion.Add(m_DirtyAreas[i].boxAffected);
if (bbUnion.GetVolume() <= (fNewVolume + m_DirtyAreas[i].boxAffected.GetVolume()) * fMERGE_THRESHOLD)
{
m_DirtyAreas[i].boxAffected = bbUnion;
return;
}
}
}
m_DirtyAreas.push_back(rec);
}
void C3DEngine::PhysicsAreaUpdates::Update()
{
//
// (bethelz) This whole class is only used for CParticleEffect right now.
//
FUNCTION_PROFILER_3DENGINE;
AUTO_LOCK(m_Mutex);
if (m_DirtyAreas.empty())
{
return;
}
// Check area against registered proxies
int nSizeAreasChanged = (int)m_DirtyAreas.size();
int nSizeProxies = m_Proxies.size();
// Access elements via [i] as the thread safe list does not always safe its element in a continues array
for (int i = 0; i < nSizeProxies; ++i)
{
const SPhysAreaNodeProxy& proxy = m_Proxies[i];
IF (!proxy.bIsValid, 0)
{
continue;
}
for (int j = 0; j < nSizeAreasChanged; ++j)
{
const SAreaChangeRecord& rec = m_DirtyAreas[j];
if ((proxy.uPhysicsMask & rec.uPhysicsMask) && Overlap::AABB_AABB(proxy.bbox, rec.boxAffected))
{
if (rec.uPhysicsMask & Area_Air)
{
if (proxy.pRenderNode->m_pRNTmpData)
{
proxy.pRenderNode->m_pRNTmpData->userData.bWindCurrent = 0;
}
}
proxy.pRenderNode->OnPhysAreaChange();
break;
}
}
}
m_DirtyAreas.resize(0);
}
void C3DEngine::PhysicsAreaUpdates::Reset()
{
stl::free_container(m_DirtyAreas);
}
uint32 C3DEngine::PhysicsAreaUpdates::CreateProxy(const IRenderNode* pRenderNode, uint16 uPhysicsMask)
{
size_t nIndex = ~0;
SPhysAreaNodeProxy* proxy = m_Proxies.push_back_new(nIndex);
proxy->pRenderNode = (IRenderNode*)pRenderNode;
proxy->uPhysicsMask = uPhysicsMask;
proxy->bIsValid = true;
proxy->bbox = pRenderNode->GetBBox();
return nIndex;
}
void C3DEngine::PhysicsAreaUpdates::UpdateProxy(const IRenderNode* pRenderNode, uint32 nProxyId)
{
m_Proxies[nProxyId].bbox = pRenderNode->GetBBox();
}
void C3DEngine::PhysicsAreaUpdates::ResetProxy(uint32 proxyId)
{
m_Proxies[proxyId].Reset();
}
void C3DEngine::PhysicsAreaUpdates::GarbageCollect()
{
// Ensure list is continues in memory
m_Proxies.CoalesceMemory();
const uint32 nSize = m_Proxies.size();
if (nSize == 0)
{
return;
}
SPhysAreaNodeProxy* pFrontIter = &m_Proxies[0];
SPhysAreaNodeProxy* pBackIter = &m_Proxies[nSize - 1];
const SPhysAreaNodeProxy* pHead = pFrontIter;
uint32 nNumItemsToDelete = 0;
// Move invalid nodes to the back of the array
do
{
while (pFrontIter->bIsValid && pFrontIter < pBackIter)
{
++pFrontIter;
}
while (!pBackIter->bIsValid && pFrontIter < pBackIter)
{
--pBackIter;
++nNumItemsToDelete;
}
if (pFrontIter < pBackIter)
{
// Replace invalid front element with back element
// Note: No need to swap because we cut the data from the array at the end anyway
memcpy(pFrontIter, pBackIter, sizeof(SPhysAreaNodeProxy));
AZ_Assert(pFrontIter->pRenderNode && pFrontIter->pRenderNode->m_pRNTmpData, "The front iterator should have a valid m_pRNTmpData after the data from the valid back iterator has been copied to it.");
if (pFrontIter->pRenderNode && pFrontIter->pRenderNode->m_pRNTmpData)
{
pFrontIter->pRenderNode->m_pRNTmpData->nPhysAreaChangedProxyId = (uint32)(pFrontIter - pHead);
}
pBackIter->bIsValid = false;
--pBackIter;
++pFrontIter;
++nNumItemsToDelete;
}
} while (pFrontIter < pBackIter);
// Cut off invalid elements
m_Proxies.resize(nSize - nNumItemsToDelete);
}
void C3DEngine::UpdateShaderItems()
{
if (GetMatMan())
{
GetMatMan()->UpdateShaderItems();
}
}
void C3DEngine::OnCameraTeleport()
{
MarkRNTmpDataPoolForReset();
}
IObjManager* C3DEngine::GetObjectManager()
{
return m_pObjManager;
}
const IObjManager* C3DEngine::GetObjectManager() const
{
return m_pObjManager;
}
bool C3DEngine::RemoveObjectsInArea(Vec3 vExploPos, float fExploRadius)
{
bool bEverythingDeleted = true;
Vec3 vRadius(fExploRadius, fExploRadius, fExploRadius);
PodArray<SRNInfo> lstEntities;
const AABB cExplosionBox(vExploPos - vRadius, vExploPos + vRadius);
MoveObjectsIntoListGlobal(&lstEntities, &cExplosionBox, false, true, true, true);
// remove small objects around
{
for (int i = 0; i < lstEntities.Count(); i++)
{
IRenderNode* pRenderNode = lstEntities[i].pNode;
AABB entBox = pRenderNode->GetBBox();
float fEntRadius = entBox.GetRadius();
Vec3 vEntCenter = pRenderNode->GetBBox().GetCenter();
float fDist = vExploPos.GetDistance(vEntCenter);
if (fDist < fExploRadius + fEntRadius &&
Overlap::Sphere_AABB(Sphere(vExploPos, fExploRadius), entBox))
{
if (fDist >= fExploRadius)
{ //
Matrix34A objMat;
CStatObj* pStatObj = (CStatObj*)pRenderNode->GetEntityStatObj(0, 0, &objMat);
if (!pStatObj)
{
continue;
}
objMat.Invert();
//assert(0);
Vec3 vOSExploPos = objMat.TransformPoint(vExploPos);
Vec3 vScaleTest(0, 0, 1.f);
vScaleTest = objMat.TransformVector(vScaleTest);
float fObjScaleInv = vScaleTest.len();
if (!pStatObj->IsSphereOverlap(Sphere(vOSExploPos, fExploRadius * fObjScaleInv)))
{
continue;
}
}
}
}
}
return bEverythingDeleted;
}
#ifndef _RELEASE
//////////////////////////////////////////////////////////////////////////
// onscreen infodebug code for e_debugDraw >= 100
//////////////////////////////////////////////////////////////////////////
void C3DEngine::AddObjToDebugDrawList(SObjectInfoToAddToDebugDrawList& objInfo)
{
m_DebugDrawListMgr.AddObject(objInfo);
}
bool CDebugDrawListMgr::m_dumpLogRequested = false;
bool CDebugDrawListMgr::m_freezeRequested = false;
bool CDebugDrawListMgr::m_unfreezeRequested = false;
uint32 CDebugDrawListMgr::m_filter = I3DEngine::DLOT_ALL;
//////////////////////////////////////////////////////////////////////////
CDebugDrawListMgr::CDebugDrawListMgr()
{
m_isFrozen = false;
ClearFrameData();
ClearConsoleCommandRequestVars();
m_assets.reserve(32); // just a reasonable value
m_drawBoxes.reserve(256); // just a reasonable value
}
//////////////////////////////////////////////////////////////////////////
void CDebugDrawListMgr::ClearFrameData()
{
m_counter = 0;
m_assetCounter = 0;
m_assets.clear();
m_drawBoxes.clear();
m_indexLeastValueAsset = 0;
CheckFilterCVar();
}
//////////////////////////////////////////////////////////////////////////
void CDebugDrawListMgr::ClearConsoleCommandRequestVars()
{
m_dumpLogRequested = false;
m_freezeRequested = false;
m_unfreezeRequested = false;
}
//////////////////////////////////////////////////////////////////////////
void CDebugDrawListMgr::AddObject(I3DEngine::SObjectInfoToAddToDebugDrawList& newObjInfo)
{
if (m_isFrozen)
{
return;
}
m_lock.Lock();
m_counter++;
if (!ShouldFilterOutObject(newObjInfo))
{
TAssetInfo newAsset(newObjInfo);
TObjectDrawBoxInfo newDrawBox(newObjInfo);
TAssetInfo* pAssetDuplicated = FindDuplicate(newAsset);
if (pAssetDuplicated)
{
pAssetDuplicated->numInstances++;
newDrawBox.assetID = pAssetDuplicated->ID;
m_drawBoxes.push_back(newDrawBox);
pAssetDuplicated->drawCalls = max(pAssetDuplicated->drawCalls, newAsset.drawCalls);
}
else
{
newAsset.ID = m_assetCounter;
newDrawBox.assetID = newAsset.ID;
bool used = false;
// list not full, so we add
if (m_assets.size() < uint(Cry3DEngineBase::GetCVars()->e_DebugDrawListSize))
{
used = true;
m_assets.push_back(newAsset);
}
else // if is full, only use it if value is greater than the current minimum, and then it substitutes the lowest slot
{
const TAssetInfo& leastValueAsset = m_assets[ m_indexLeastValueAsset ];
if (leastValueAsset < newAsset)
{
used = true;
m_assets[ m_indexLeastValueAsset ] = newAsset;
}
}
if (used)
{
m_assetCounter++;
m_drawBoxes.push_back(newDrawBox);
FindNewLeastValueAsset();
}
}
}
m_lock.Unlock();
}
//////////////////////////////////////////////////////////////////////////
CDebugDrawListMgr::TAssetInfo* CDebugDrawListMgr::FindDuplicate(const TAssetInfo& asset)
{
std::vector<TAssetInfo>::iterator iter = m_assets.begin();
std::vector<TAssetInfo>::const_iterator endArray = m_assets.end();
while (iter != endArray)
{
TAssetInfo& currAsset = *iter;
if (currAsset.fileName == asset.fileName)
{
return &currAsset;
}
++iter;
}
return NULL;
}
//////////////////////////////////////////////////////////////////////////
bool CDebugDrawListMgr::ShouldFilterOutObject(const I3DEngine::SObjectInfoToAddToDebugDrawList& object)
{
if (m_filter == I3DEngine::DLOT_ALL)
{
return false;
}
return (m_filter & object.type) == 0;
}
//////////////////////////////////////////////////////////////////////////
void CDebugDrawListMgr::FindNewLeastValueAsset()
{
uint32 size = m_assets.size();
for (uint32 i = 0; i < size; ++i)
{
const TAssetInfo& currAsset = m_assets[ i ];
const TAssetInfo& leastValueAsset = m_assets[ m_indexLeastValueAsset ];
if (currAsset < leastValueAsset)
{
m_indexLeastValueAsset = i;
}
}
}
//////////////////////////////////////////////////////////////////////////
void CDebugDrawListMgr::Update()
{
m_lock.Lock();
if (!m_isFrozen)
{
std::sort(m_assets.begin(), m_assets.end(), CDebugDrawListMgr::SortComparison);
}
gEnv->pRenderer->CollectDrawCallsInfoPerNode(true);
// it displays values from the previous frame. This mean that if it is disabled, and then enabled again later on, it will display bogus values for 1 frame...but i dont care (yet)
float X = 10.f;
float Y = 100.f;
if (m_isFrozen)
{
PrintText(X, Y, Col_Red, "FROZEN DEBUGINFO");
}
Y += 20.f;
PrintText(X, Y, Col_White, "total assets: %d Ordered by: Showing:", m_counter);
PrintText(X + 240, Y, Col_Yellow, GetStrCurrMode());
TMyStandardString filterStr;
GetStrCurrFilter(filterStr);
PrintText(X + 420, Y, Col_Yellow, filterStr.c_str());
Y += 20.f;
float XName = 270;
const char* pHeaderStr = "";
switch (Cry3DEngineBase::GetCVars()->e_DebugDraw)
{
case LM_TRI_COUNT:
pHeaderStr = " tris " " meshMem " "rep " " type ";
break;
case LM_VERT_COUNT:
pHeaderStr = " verts " " meshMem " "rep " " type ";
break;
case LM_DRAWCALLS:
pHeaderStr = "draw Calls " " tris " "rep " " type ";
break;
case LM_TEXTMEM:
pHeaderStr = " texMem " " meshMem " "rep " " type ";
break;
case LM_MESHMEM:
pHeaderStr = " meshMem " " texMem " "rep " " type ";
break;
}
PrintText(X, Y, Col_White, pHeaderStr);
const int standardNameSize = 48;
PrintText(XName, Y, Col_White, "Entity (class)");
PrintText(XName + (standardNameSize + 2) * 7, Y, Col_White, "File name");
Y += 20.f;
for (uint32 i = 0; i < m_assets.size(); ++i)
{
ColorF colorLine = Col_Cyan;
if (Cry3DEngineBase::GetCVars()->e_DebugDrawListBBoxIndex - 1 == i)
{
colorLine = Col_Blue;
}
const TAssetInfo& currAsset = m_assets[ i ];
TMyStandardString texMemoryStr;
TMyStandardString meshMemoryStr;
MemToString(currAsset.texMemory, texMemoryStr);
MemToString(currAsset.meshMemory, meshMemoryStr);
switch (Cry3DEngineBase::GetCVars()->e_DebugDraw)
{
case LM_TRI_COUNT:
PrintText(X, Y + 20.f * i, colorLine, "%7d " "%s " "%3d " "%s",
currAsset.numTris, meshMemoryStr.c_str(), currAsset.numInstances, GetAssetTypeName(currAsset.type));
break;
case LM_VERT_COUNT:
PrintText(X, Y + 20.f * i, colorLine, "%7d " "%s " "%3d " "%s",
currAsset.numVerts, meshMemoryStr.c_str(), currAsset.numInstances, GetAssetTypeName(currAsset.type));
break;
case LM_DRAWCALLS:
{
PrintText(X, Y + 20.f * i, colorLine, " %5d " "%7d " "%3d " "%s",
currAsset.drawCalls, currAsset.numTris, currAsset.numInstances, GetAssetTypeName(currAsset.type));
}
break;
case LM_TEXTMEM:
PrintText(X, Y + 20.f * i, colorLine, "%s " "%s " "%3d " "%s",
texMemoryStr.c_str(), meshMemoryStr.c_str(), currAsset.numInstances, GetAssetTypeName(currAsset.type));
break;
case LM_MESHMEM:
PrintText(X, Y + 20.f * i, colorLine, "%s " "%s " "%3d " "%s",
meshMemoryStr.c_str(), texMemoryStr.c_str(), currAsset.numInstances, GetAssetTypeName(currAsset.type));
break;
}
int filenameSep = 7 * (max(standardNameSize, int(currAsset.name.length())) + 2);
float XFileName = XName + filenameSep;
PrintText(XName, Y + 20.f * i, colorLine, currAsset.name.c_str());
PrintText(XFileName, Y + 20.f * i, colorLine, currAsset.fileName.c_str());
}
if (Cry3DEngineBase::GetCVars()->e_DebugDrawListBBoxIndex > 0 && uint(Cry3DEngineBase::GetCVars()->e_DebugDrawListBBoxIndex - 1) < m_assets.size())
{
const TAssetInfo& assetInfo = m_assets[ Cry3DEngineBase::GetCVars()->e_DebugDrawListBBoxIndex - 1 ];
uint32 numBoxesDrawn = 0;
for (uint32 i = 0; i < m_drawBoxes.size(); ++i)
{
const TObjectDrawBoxInfo& drawBox = m_drawBoxes[ i ];
if (drawBox.assetID == assetInfo.ID)
{
gEnv->pRenderer->GetIRenderAuxGeom()->DrawAABB(drawBox.bbox, drawBox.mat, true, ColorB(0, 0, 255, 100), eBBD_Faceted);
numBoxesDrawn++;
if (numBoxesDrawn >= assetInfo.numInstances)
{
break;
}
}
}
}
if (m_freezeRequested)
{
m_isFrozen = true;
}
if (m_unfreezeRequested)
{
m_isFrozen = false;
}
if (m_dumpLogRequested)
{
DumpLog();
}
ClearConsoleCommandRequestVars();
if (!m_isFrozen)
{
ClearFrameData();
m_assets.reserve(Cry3DEngineBase::GetCVars()->e_DebugDrawListSize);
}
m_lock.Unlock();
}
//////////////////////////////////////////////////////////////////////////
void CDebugDrawListMgr::PrintText(float x, float y, const ColorF& fColor, const char* label_text, ...)
{
va_list args;
va_start(args, label_text);
SDrawTextInfo ti;
ti.xscale = ti.yscale = 1.2f;
ti.flags = eDrawText_2D | eDrawText_FixedSize | eDrawText_Monospace;
{
ti.color[0] = fColor[0];
ti.color[1] = fColor[1];
ti.color[2] = fColor[2];
ti.color[3] = fColor[3];
}
gEnv->pRenderer->DrawTextQueued(Vec3(x, y, 0.5f), ti, label_text, args);
va_end(args);
}
//////////////////////////////////////////////////////////////////////////
void CDebugDrawListMgr::MemToString(uint32 memVal, TMyStandardString& outStr)
{
if (memVal < 1024 * 1024)
{
outStr.Format("%5.1f kb", memVal / (1024.f));
}
else
{
outStr.Format("%5.1f MB", memVal / (1024.f * 1024.f));
}
}
//////////////////////////////////////////////////////////////////////////
bool CDebugDrawListMgr::TAssetInfo::operator<(const TAssetInfo& other) const
{
switch (Cry3DEngineBase::GetCVars()->e_DebugDraw)
{
case LM_TRI_COUNT:
return (numTris < other.numTris);
case LM_VERT_COUNT:
return (numVerts < other.numVerts);
case LM_DRAWCALLS:
return (drawCalls < other.drawCalls);
case LM_TEXTMEM:
return (texMemory < other.texMemory);
case LM_MESHMEM:
return (meshMemory < other.meshMemory);
default:
assert(false);
return false;
}
}
//////////////////////////////////////////////////////////////////////////
CDebugDrawListMgr::TAssetInfo::TAssetInfo(const I3DEngine::SObjectInfoToAddToDebugDrawList& objInfo)
{
type = objInfo.type;
if (!objInfo.pClassName)
{
// custom functions to avoid any heap allocation
MyString_Assign(name, objInfo.pName);
MyStandardString_Concatenate(name, "(");
MyStandardString_Concatenate(name, objInfo.pClassName);
MyStandardString_Concatenate(name, ")");
}
MyFileNameString_Assign(fileName, objInfo.pFileName);
numTris = objInfo.numTris;
numVerts = objInfo.numVerts;
texMemory = objInfo.texMemory;
meshMemory = objInfo.meshMemory;
drawCalls = gEnv->pRenderer->GetDrawCallsPerNode(objInfo.pRenderNode);
numInstances = 1;
ID = UNDEFINED_ASSET_ID;
}
//////////////////////////////////////////////////////////////////////////
CDebugDrawListMgr::TObjectDrawBoxInfo::TObjectDrawBoxInfo(const I3DEngine::SObjectInfoToAddToDebugDrawList& objInfo)
{
mat.SetIdentity();
bbox.Reset();
if (objInfo.pMat)
{
mat = *objInfo.pMat;
}
if (objInfo.pBox)
{
bbox = *objInfo.pBox;
}
assetID = UNDEFINED_ASSET_ID;
}
//////////////////////////////////////////////////////////////////////////
void CDebugDrawListMgr::MyStandardString_Concatenate(TMyStandardString& outStr, const char* pStr)
{
if (pStr && outStr.length() < outStr.capacity())
{
outStr._ConcatenateInPlace(pStr, min(strlen(pStr), outStr.capacity() - outStr.length()));
}
}
//////////////////////////////////////////////////////////////////////////
void CDebugDrawListMgr::MyFileNameString_Assign(TFilenameString& outStr, const char* pStr)
{
char tempBuf[ outStr.MAX_SIZE + 1 ];
uint32 outInd = 0;
if (pStr)
{
while (*pStr != 0 && outInd < outStr.MAX_SIZE)
{
tempBuf[outInd] = *pStr;
outInd++;
if (*pStr == '%' && outInd < outStr.MAX_SIZE)
{
tempBuf[outInd] = '%';
outInd++;
}
pStr++;
}
}
tempBuf[ outInd ] = 0;
outStr = tempBuf;
}
//////////////////////////////////////////////////////////////////////////
void CDebugDrawListMgr::ConsoleCommand(IConsoleCmdArgs* args)
{
if (args->GetArgCount() > 1 && args->GetArg(1))
{
switch (toupper(args->GetArg(1)[0]))
{
case 'F':
m_freezeRequested = true;
break;
case 'C':
m_unfreezeRequested = true;
break;
case 'D':
m_dumpLogRequested = true;
break;
}
}
}
//////////////////////////////////////////////////////////////////////////
void CDebugDrawListMgr::CheckFilterCVar()
{
ICVar* pCVar = gEnv->pConsole->GetCVar("e_debugdrawlistfilter");
if (!pCVar)
{
return;
}
const char* pVal = pCVar->GetString();
if (pVal && azstricmp(pVal, "all") == 0)
{
m_filter = I3DEngine::DLOT_ALL;
return;
}
m_filter = 0;
while (pVal && pVal[0])
{
switch (toupper(pVal[0]))
{
case 'C':
m_filter |= I3DEngine::DLOT_CHARACTER;
break;
case 'S':
m_filter |= I3DEngine::DLOT_STATOBJ;
break;
}
++pVal;
}
}
//////////////////////////////////////////////////////////////////////////
void CDebugDrawListMgr::DumpLog()
{
TMyStandardString filterStr;
GetStrCurrFilter(filterStr);
CryLog("--------------------------------------------------------------------------------");
CryLog(" DebugDrawList infodebug");
CryLog("--------------------------------------------------------------------------------");
CryLog(" total objects: %d Ordered by: %s Showing: %s", m_counter, GetStrCurrMode(), filterStr.c_str());
CryLog(PRINTF_EMPTY_FORMAT);
CryLog(" tris verts draw Calls texMem meshMem type");
CryLog(" ------- -------- ---------- -------- -------- ----------");
for (uint32 i = 0; i < m_assets.size(); ++i)
{
const TAssetInfo& currAsset = m_assets[ i ];
TMyStandardString texMemoryStr;
TMyStandardString meshMemoryStr;
MemToString(currAsset.texMemory, texMemoryStr);
MemToString(currAsset.meshMemory, meshMemoryStr);
CryLog("%8d %8d %5d %s %s %s %s %s", currAsset.numTris, currAsset.numVerts, currAsset.drawCalls,
texMemoryStr.c_str(), meshMemoryStr.c_str(), GetAssetTypeName(currAsset.type), currAsset.name.c_str(), currAsset.fileName.c_str());
}
CryLog("--------------------------------------------------------------------------------");
}
//////////////////////////////////////////////////////////////////////////
const char* CDebugDrawListMgr::GetStrCurrMode()
{
const char* pModesNames[] =
{
"Tri count",
"Vert count",
"Draw calls",
"Texture memory",
"Mesh memory"
};
uint32 index = Cry3DEngineBase::GetCVars()->e_DebugDraw - LM_BASENUMBER;
uint32 numElems = sizeof(pModesNames) / sizeof *(pModesNames);
if (index < numElems)
{
return pModesNames[index];
}
else
{
return "<UNKNOWN>";
}
}
//////////////////////////////////////////////////////////////////////////
void CDebugDrawListMgr::GetStrCurrFilter(TMyStandardString& strOut)
{
const char* pFilterNames[] =
{
"",
"Characters",
"StatObjs"
};
uint32 numElems = sizeof(pFilterNames) / sizeof *(pFilterNames);
for (uint32 i = 1, bitVal = 1; i < numElems; ++i)
{
if ((bitVal & m_filter) != 0)
{
if (strOut.size() > 0)
{
strOut += "+";
}
strOut += pFilterNames[i];
}
bitVal *= 2;
}
if (strOut.size() == 0)
{
strOut = "ALL";
}
}
//////////////////////////////////////////////////////////////////////////
const char* CDebugDrawListMgr::GetAssetTypeName(I3DEngine::EDebugDrawListAssetTypes type)
{
const char* pNames[] =
{
"",
"Brush ",
"Vegetation",
"Character ",
"StatObj "
};
uint32 numElems = sizeof(pNames) / sizeof *(pNames);
for (uint32 i = 1, bitVal = 1; i < numElems; ++i)
{
if (bitVal == type)
{
return pNames[i];
}
bitVal *= 2;
}
return "<UNKNOWN>";
}
#endif //RELEASE
void C3DEngine::GetStatObjAndMatTables(DynArray<IStatObj*>* pStatObjTable, DynArray<_smart_ptr<IMaterial> >* pMatTable, DynArray<IStatInstGroup*>* pStatInstGroupTable, uint32 nObjTypeMask)
{
SHotUpdateInfo exportInfo;
exportInfo.nObjTypeMask = nObjTypeMask;
std::vector<IStatObj*> statObjTable;
std::vector<_smart_ptr<IMaterial> > matTable;
std::vector<IStatInstGroup*> statInstGroupTable;
if (Get3DEngine() && Get3DEngine()->IsObjectTreeReady())
{
Get3DEngine()->GetObjectTree()->GenerateStatObjAndMatTables((pStatObjTable != NULL) ? &statObjTable : NULL,
(pMatTable != NULL) ? &matTable : NULL,
(pStatInstGroupTable != NULL) ? &statInstGroupTable : NULL,
&exportInfo);
}
if (GetVisAreaManager())
{
GetVisAreaManager()->GenerateStatObjAndMatTables((pStatObjTable != NULL) ? &statObjTable : NULL,
(pMatTable != NULL) ? &matTable : NULL,
(pStatInstGroupTable != NULL) ? &statInstGroupTable : NULL,
&exportInfo);
}
if (pStatObjTable)
{
pStatObjTable->resize(statObjTable.size());
for (size_t i = 0; i < statObjTable.size(); ++i)
{
(*pStatObjTable)[i] = statObjTable[i];
}
statObjTable.clear();
}
if (pMatTable)
{
pMatTable->resize(matTable.size());
for (size_t i = 0; i < matTable.size(); ++i)
{
(*pMatTable)[i] = matTable[i];
}
matTable.clear();
}
if (pStatInstGroupTable)
{
pStatInstGroupTable->resize(statInstGroupTable.size());
for (size_t i = 0; i < statInstGroupTable.size(); ++i)
{
(*pStatInstGroupTable)[i] = statInstGroupTable[i];
}
statInstGroupTable.clear();
}
}