/* * 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 "StdAfx.h" #include #include #include #include "3dEngine.h" #include #include #include #include #include "VisAreas.h" #include "ObjMan.h" #include "Ocean.h" #include "DecalManager.h" #include "Vegetation.h" #include "IndexedMesh.h" #include "MatMan.h" #include "Brush.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 "RoadRenderNode.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 "RopeRenderNode.h" #include "RenderMeshMerger.h" #include "PhysCallbacks.h" #include "DeferredCollisionEvent.h" #include "MergedMeshRenderNode.h" #if ENABLE_CRY_PHYSICS #include "BreakableGlassRenderNode.h" #include "BreezeGenerator.h" #endif #include "OpticsManager.h" #include "GeomCacheRenderNode.h" #include "GeomCacheManager.h" #include "ClipVolumeManager.h" #include #include #include "PostEffectGroup.h" #include "MainThreadRenderRequestBus.h" #include "ObjMan.h" #include "DeformableNode.h" #include "Environment/OceanEnvironmentBus.h" #include #include "VegetationPoolManager.h" #include "ParticleManager.h" #if !defined(EXCLUDE_DOCUMENTATION_PURPOSE) #include "PrismRenderNode.h" #endif // EXCLUDE_DOCUMENTATION_PURPOSE #include #include #include #include #include #include #include // required for LARGE_INTEGER used by QueryPerformanceCounter #ifdef WIN32 #include #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; #if ENABLE_CRY_PHYSICS IPhysicalWorld* Cry3DEngineBase::m_pPhysicalWorld = 0; #endif 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; ICryPak* Cry3DEngineBase::m_pCryPak = 0; IParticleManager* Cry3DEngineBase::m_pPartManager = 0; 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; CMergedMeshesManager* Cry3DEngineBase::m_pMergedMeshesManager = 0; #if ENABLE_CRY_PHYSICS CBreezeGenerator* Cry3DEngineBase::m_pBreezeGenerator = 0; #endif IStreamedObjectListener* Cry3DEngineBase::m_pStreamListener = 0; #if defined(USE_GEOM_CACHES) CGeomCacheManager* Cry3DEngineBase::m_pGeomCacheManager = 0; #endif bool Cry3DEngineBase::m_bProfilerEnabled = false; bool Cry3DEngineBase::m_bLevelLoadingInProgress = false; bool Cry3DEngineBase::m_bIsInRenderScene = false; int Cry3DEngineBase::m_mergedMeshesPoolSize = 0; 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 // The only direct particle function needed by 3DEngine, implemented in the same DLL. extern IParticleManager* CreateParticleManager(bool bEnable); 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; CVegetation::InitVegDecomprTable(); Cry3DEngineBase::m_nMainThreadId = CryGetCurrentThreadId(); Cry3DEngineBase::m_pSystem = pSystem; Cry3DEngineBase::m_pRenderer = gEnv->pRenderer; Cry3DEngineBase::m_pTimer = gEnv->pTimer; Cry3DEngineBase::m_pLog = gEnv->pLog; #if ENABLE_CRY_PHYSICS Cry3DEngineBase::m_pPhysicalWorld = gEnv->pPhysicalWorld; #endif 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_pMergedMeshesManager = new CMergedMeshesManager; Cry3DEngineBase::m_pMatMan = new CMatMan; #if ENABLE_CRY_PHYSICS Cry3DEngineBase::m_pBreezeGenerator = new CBreezeGenerator; #endif 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(); 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_vegetationPoolManager = nullptr; m_nBlackTexID = 0; // create components m_pObjManager = CryAlignedNew(); m_pDecalManager = 0;//new CDecalManager (m_pSystem, this); m_pCloudsManager = new CCloudsManager; m_pPartManager = 0; m_pOpticsManager = 0; m_pVisAreaManager = 0; m_pClipVolumeManager = new CClipVolumeManager(); m_pSkyLightManager = CryAlignedNew(); // create REs m_pRESky = 0; m_pREHDRSky = 0; m_pPhysMaterialEnumerator = 0; m_fMaxViewDistHighSpec = 8000; m_fMaxViewDistLowSpec = 1000; m_fTerrainDetailMaterialsViewDistRatio = 1.f; m_fSkyBoxAngle = 0; m_fSkyBoxStretching = 0; #if ENABLE_CRY_PHYSICS m_pGlobalWind = 0; m_vWindSpeed(1, 0, 0); #endif m_physicsAreaUpdatesHandler = AZStd::make_unique(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(); union { CStatObjFoliage* CStatObjFoliage::* next; INT_PTR inext; } tmp; tmp.inext = 0; tmp.next = &CStatObjFoliage::m_next; m_pFirstFoliage = m_pLastFoliage = (CStatObjFoliage*)((INT_PTR)&m_pFirstFoliage - tmp.inext); 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; /* // structures should not grow too much - commented out as in 64bit they do assert(sizeof(CVegetation)-sizeof(IRenderNode)<=52); assert(sizeof(CBrush)-sizeof(IRenderNode)<=120); assert(sizeof(IRenderNode)<=96); */ 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; m_vegetationPoolManager = nullptr; 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); } ////////////////////////////////////////////////////////////////////// void C3DEngine::RemoveEntInFoliage(int i, IPhysicalEntity* pent) { #if ENABLE_CRY_PHYSICS IPhysicalEntity* pent1 = m_pPhysicalWorld->GetPhysicalEntityById(m_arrEntsInFoliage[i].id); pe_params_foreign_data pfd; if (!pent) { pent = pent1; } else if (pent != pent1) { // probably someone overwrote foreign flags for (i = m_arrEntsInFoliage.size() - 1; i >= 0 && pent != m_pPhysicalWorld->GetPhysicalEntityById(m_arrEntsInFoliage[i].id); i--) { ; } if (i < 0) { return; } } if (pent && pent->GetParams(&pfd) && (pfd.iForeignFlags >> 8 & 255) == i + 1) { MARK_UNUSED pfd.pForeignData, pfd.iForeignData, pfd.iForeignFlags; pfd.iForeignFlagsAND = 255; pent->SetParams(&pfd, 1); } int j = m_arrEntsInFoliage.size() - 1; if (i < j) { m_arrEntsInFoliage[i] = m_arrEntsInFoliage[j]; if ((pent = m_pPhysicalWorld->GetPhysicalEntityById(m_arrEntsInFoliage[i].id)) && pent->GetParams(&pfd) && (pfd.iForeignFlags >> 8 & 255) == j + 1) { MARK_UNUSED pfd.pForeignData, pfd.iForeignData; pfd.iForeignFlags = pfd.iForeignFlags & 255 | (i + 1) << 8; pent->SetParams(&pfd, 1); } } m_arrEntsInFoliage.DeleteLast(); #else // Remove foliage phys entity CRY_PHYSICS_REPLACEMENT_ASSERT(); #endif // ENABLE_CRY_PHYSICS } bool C3DEngine::Init() { m_pPartManager = CreateParticleManager(!gEnv->IsDedicated()); m_pSystem->SetIParticleManager(m_pPartManager); 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(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; if (m_pPartManager) { m_pPartManager->OnFrameStart(); } 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) && !defined(DEDICATED_SERVER) 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(int ix, int iy) { return 0; } void C3DEngine::Update() { m_bProfilerEnabled = gEnv->pFrameProfileSystem->IsProfiling(); FUNCTION_PROFILER_3DENGINE_LEGACYONLY; AZ_TRACE_METHOD(); ProcessAsyncStaticObjectLoadRequests(); m_LightConfigSpec = (ESystemConfigSpec)GetCurrentLightSpec(); if (GetObjManager()) { GetObjManager()->ClearStatObjGarbage(); } if (m_bEditor) { // Force update all roads int roadRenderNodeUpdateCount = m_lstRoadRenderNodesForUpdate.Count(); while (roadRenderNodeUpdateCount > 0) { RoadRenderNodeRebuildQueue_Update(); roadRenderNodeUpdateCount--; } } else { RoadRenderNodeRebuildQueue_Update(); } if (m_pDecalManager) { m_pDecalManager->Update(GetTimer()->GetFrameTime()); } if (GetCVars()->e_PrecacheLevel == 3) { PrecacheLevel(true, 0, 0); } DebugDraw_Draw(); ProcessCVarsChange(); #if ENABLE_CRY_PHYSICS pe_params_area pa; IPhysicalEntity* pArea; if ((pArea = gEnv->pPhysicalWorld->AddGlobalArea())->GetParams(&pa)) #endif // ENABLE_CRY_PHYSICS { using namespace OceanGlobals; AZStd::lock_guard lock(g_oceanParamsMutex); g_oceanLevel = OceanToggle::IsActive() ? OceanRequest::GetOceanLevel() : GetWaterLevel(); #if ENABLE_CRY_PHYSICS bool bOceanEnabled = GetFloatCVar(e_PhysOceanCell) > 0 && (OceanToggle::IsActive() ? OceanRequest::OceanIsEnabled() : g_oceanLevel > 0); if (bOceanEnabled && (!pa.pGeom || g_oceanStep != GetFloatCVar(e_PhysOceanCell))) { new(&pa)pe_params_area; primitives::heightfield hf; hf.origin.zero().z = g_oceanLevel; g_oceanStep = GetFloatCVar(e_PhysOceanCell); hf.size.set((int)(8192 / g_oceanStep), (int)(8192 / g_oceanStep)); hf.step.set(g_oceanStep, g_oceanStep); hf.fpGetHeightCallback = GetOceanLevelCallback; hf.fpGetSurfTypeCallback = GetOceanSurfTypeCallback; hf.Basis.SetIdentity(); hf.bOriented = 0; hf.typemask = 255; hf.typehole = 255; hf.heightscale = 1.0f; pa.pGeom = gEnv->pPhysicalWorld->GetGeomManager()->CreatePrimitive(primitives::heightfield::type, &hf); pArea->SetParams(&pa); OceanAnimationData params = GetOceanAnimationParams(); pe_params_buoyancy pb; pb.waterFlow = Vec3(params.fWindDirectionV, params.fWindDirectionU, 0) * (params.fWavesSpeed * 0.25f); // tone down the speed } else if (!bOceanEnabled && pa.pGeom) { new(&pa)pe_params_area; pa.pGeom = 0; pArea->SetParams(&pa); pe_params_buoyancy pb; pb.waterFlow.zero(); pArea->SetParams(&pb); } #endif // ENABLE_CRY_PHYSICS } CRenderMeshUtils::ClearHitCache(); CleanUpOldDecals(); CDecalRenderNode::ResetDecalUpdatesCounter(); #if ENABLE_CRY_PHYSICS if (m_pBreezeGenerator) { m_pBreezeGenerator->Update(); } #endif 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_VegetationAlignToTerrainAmount + GetCVars()->e_ShadowsCastViewDistRatio + GetCVars()->e_Dissolve + GetFloatCVar(e_DissolveDistMin) + GetFloatCVar(e_DissolveDistMax) + GetFloatCVar(e_DissolveDistband) + GetCVars()->e_VegetationUseTerrainColor + GetCVars()->e_TerrainDetailMaterials + GetCVars()->e_ViewDistRatio + GetCVars()->e_ViewDistMin + GetCVars()->e_ViewDistRatioDetail + GetCVars()->e_ViewDistRatioVegetation + 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.GetWidth(), terrainAabb.GetHeight()); // 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)); LegacyTerrain::LegacyTerrainDataRequestBus::Broadcast(&LegacyTerrain::LegacyTerrainDataRequests::ResetTerrainVertBuffers); // refresh vegetation properties UpdateStatInstGroups(); // force refresh of temporary data associated with visible objects MarkRNTmpDataPoolForReset(); } m_fRefreshSceneDataCVarsSumm = fNewCVarsSumm; int nRenderTypeEnableCVarSum = (GetCVars()->e_Vegetation << 0) + (GetCVars()->e_Brushes << 1) + (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 delete m_pPartManager; m_pPartManager = 0; m_pSystem->SetIParticleManager(m_pPartManager); if (m_pOpticsManager) { delete m_pOpticsManager; m_pOpticsManager = 0; m_pSystem->SetIOpticsManager(m_pOpticsManager); } CryAlignedDelete(m_pObjManager); m_pObjManager = 0; #if ENABLE_CRY_PHYSICS if (GetPhysicalWorld()) { CPhysCallbacks::Done(); } #endif // ENABLE_CRY_PHYSICS // Free the temporary pool's underlying storage // and reset the pool if (!CTemporaryPool::Shutdown()) { CryFatalError("C3DEngine::Shutdown() could not shutdown temporary pool"); } COctreeNode::Shutdown(); } #ifdef WIN64 #pragma warning( push ) //AMD Port #pragma warning( disable : 4311 ) #endif #ifndef _RELEASE void C3DEngine::ProcessStreamingLatencyTest(const CCamera& camIn, CCamera& camOut, const SRenderingPassInfo& passInfo) { static float fSQTestOffset = 0; static PodArray 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 = "@cache@/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, "\n" "\n" "\n" "\n" "\n" "\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(const char* szCallerName, const SRenderingPassInfo& passInfo) { CCamera newCam = passInfo.GetCamera(); if (passInfo.IsGeneralPass()) { SVOGILegacyRequestBus::Broadcast(&SVOGILegacyRequests::UpdateVoxelData); } if (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 } } #if AZ_RENDER_TO_TEXTURE_GEM_ENABLED if (!passInfo.IsRenderSceneToTexturePass()) #endif // if AZ_RENDER_TO_TEXTURE_GEM_ENABLED { ///////////////////////////////////////////////////////////////////////////// // Update Foliage float dt = GetTimer()->GetFrameTime(); CStatObjFoliage* pFoliage, *pFoliageNext; for (pFoliage = m_pFirstFoliage; &pFoliage->m_next != &m_pFirstFoliage; pFoliage = pFoliageNext) { pFoliageNext = pFoliage->m_next; pFoliage->Update(dt, GetRenderingCamera()); } for (int i = m_arrEntsInFoliage.size() - 1; i >= 0; i--) { if ((m_arrEntsInFoliage[i].timeIdle += dt) > 0.3f) { RemoveEntInFoliage(i); } } } ///////////////////////////////////////////////////////////////////////////// // 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* pLightsTI_S, PodArray* pLightsTI_D) { SVOGILegacyRequestBus::Broadcast(&SVOGILegacyRequests::GetSvoStaticTextures, svoInfo, pLightsTI_S, pLightsTI_D); } void C3DEngine::GetSvoBricksForUpdate(PodArray& 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(); } #ifdef WIN64 #pragma warning( pop ) //AMD Port #endif 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 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 TReturn C3DEngine::LoadStatObjInternal(const char* fileName, const char* geomName, IStatObj::SSubObject** subObject, bool useStreaming, unsigned long loadingFlags, LoadStatObjFunc 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* pObjManager = static_cast(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 lock(m_statObjQueueLock); m_statObjLoadRequests.push(std::move(request)); } } IDeformableNode* C3DEngine::CreateDeformableNode() { return new CDeformableNode(); } void C3DEngine::DestroyDeformableNode(IDeformableNode* node) { SAFE_DELETE(node); } 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 lock(m_statObjQueueLock); if (m_statObjLoadRequests.empty()) { break; } request = std::move(m_statObjLoadRequests.front()); m_statObjLoadRequests.pop(); } _smart_ptr 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); } LegacyProceduralVegetation::IVegetationPoolManager& C3DEngine::GetIVegetationPoolManager() { if (!m_vegetationPoolManager) { m_vegetationPoolManager = AZStd::make_unique(); AZ_Assert(m_vegetationPoolManager, "Failed to instantiate VegetationPoolManager."); } return static_cast(*m_vegetationPoolManager.get()); } 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_Brush || 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 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(int nSID) { if (!IsObjectTreeReady()) { return; } PodArray 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); GetObjectTree()->PrepareForRemovalOfAllVegetationCasters(); for (int i = 0; i < lstObjects.Count(); i++) { IRenderNode* pNode = lstObjects.GetAt(i).pNode; switch (pNode->GetRenderNodeType()) { case eERType_Vegetation: case eERType_MergedMesh: // If we have a static vegetation render node, delete it. (This will also remove it // from the octree) pNode->ReleaseNode(); break; } } GetObjectTree()->RemoveAllVegetationCasters(); Vegetation::StaticVegetationNotificationBus::Broadcast(&Vegetation::StaticVegetationNotificationBus::Events::VegetationCleared); } void C3DEngine::OnExplosion(Vec3 vPos, float fRadius, 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; } } } } // try to remove objects around bool bGroundDeformationAllowed = RemoveObjectsInArea(vPos, fRadius) && bDeformTerrain && GetCVars()->e_TerrainDeformations; // reduce ground decals size depending on distance to the ground float terrainHeight = terrain->GetHeightFromFloats(vPos.x, vPos.y); float fExploHeight = vPos.z - terrainHeight; if (bGroundDeformationAllowed && (fExploHeight > -0.1f) && fExploHeight < fRadius && fRadius > 0.125f) { // update roads { PodArray lstRoads; AABB bbox(vPos - Vec3(fRadius, fRadius, fRadius), vPos + Vec3(fRadius, fRadius, fRadius)); Get3DEngine()->GetObjectsByTypeGlobal(lstRoads, eERType_Road, &bbox); for (int i = 0; i < lstRoads.Count(); i++) { CRoadRenderNode* pRoad = (CRoadRenderNode*)lstRoads[i]; pRoad->OnTerrainChanged(); } } } // 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(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::infinity(); } else { auto legacyTerrain = LegacyTerrain::LegacyTerrainDataRequestBus::FindFirstHandler(); if (!legacyTerrain) { // if there is no terrain, or it is invalid, check if we still have an ocean if (oceanActive && oceanEnabled) { distance = camPosition.z - OceanRequest::GetOceanLevel(); } else { distance = std::numeric_limits::infinity(); } } else { // if the camera is over terrain, try to use the terrain's stored distance to water AZ::Aabb terrainBounds = AZ::Aabb::CreateFromPoint(AZ::Vector3::CreateZero()); AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult(terrainBounds, &AzFramework::Terrain::TerrainDataRequests::GetTerrainAabb); const AABB terrainBBox = AZAabbToLyAABB(terrainBounds); bool bCameraInTerrainBounds = Overlap::Point_AABB2D(camPosition, terrainBBox); const float terrainDistanceToSectorWithWater = legacyTerrain->GetDistanceToSectorWithWater(); if (bCameraInTerrainBounds && (terrainDistanceToSectorWithWater > minDistance)) { distance = terrainDistanceToSectorWithWater; } else { distance = camPosition.z - (oceanActive ? OceanRequest::GetOceanLevel() : GetWaterLevel()); } } } 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, Statoscope) 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 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 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.bUseTerrainColor = siGroup.bUseTerrainColor; 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()); auto legacyTerrain = LegacyTerrain::LegacyTerrainDataRequestBus::FindFirstHandler(); if (legacyTerrain) { legacyTerrain->MarkAllSectorsAsUncompiled(); } // Refresh if we've enabled dynamic merged mesh generation and either the group material has changed, the mesh has changed, or our automerge setting has changed if (gEnv->IsEditor() && (pPreviousGroupMaterial != rGroup.pMaterial || pPreviousObject != rGroup.pStatObj)) { StatObjEventBus::MultiHandler::BusDisconnect(pPreviousObject); m_pMergedMeshesManager->ResetActiveNodes(); if (rGroup.pStatObj) { StatObjEventBus::MultiHandler::BusConnect(rGroup.pStatObj); } } MarkRNTmpDataPoolForReset(); return true; } void C3DEngine::OnStatObjReloaded() { m_pMergedMeshesManager->ResetActiveNodes(); } 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& 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(m_arrEntsInFoliage); pSizer->AddObject(m_lstRoadRenderNodesForUpdate); pSizer->AddObject(m_lstKilledVegetations); 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, "Particles"); pSizer->AddObject(m_pPartManager); } { 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, "Terrain"); LegacyTerrain::LegacyTerrainDataRequestBus::Broadcast(&LegacyTerrain::LegacyTerrainDataRequests::GetMemoryUsage, pSizer); } { SIZER_COMPONENT_NAME(pSizer, "Ocean"); pSizer->AddObject(m_pOcean); } { SIZER_COMPONENT_NAME(pSizer, "LegacyProceduralVegetation"); pSizer->AddObject(m_vegetationPoolManager.get()); } { 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 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 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); } } } ////////////////////////////////////////////////////////////////////////// LegacyTerrain::LegacyTerrainDataRequestBus::Broadcast(&LegacyTerrain::LegacyTerrainDataRequests::GetResourceMemoryUsage, pSizer, cstAABB); } bool C3DEngine::IsUnderWater(const Vec3& vPos) const { bool bUnderWater = false; #if ENABLE_CRY_PHYSICS for (IPhysicalEntity* pArea = 0; pArea = GetPhysicalWorld()->GetNextArea(pArea); ) { if (bUnderWater) { // Must finish iteration to unlock areas. continue; } pe_params_buoyancy buoy; if (pArea->GetParams(&buoy) && buoy.iMedium == 0) { if (!is_unused(buoy.waterPlane.n)) { if (buoy.waterPlane.n * vPos < buoy.waterPlane.n * buoy.waterPlane.origin) { pe_status_contains_point st; st.pt = vPos; if (pArea->GetStatus(&st)) { bUnderWater = true; } } } } } #else // Check underwater CRY_PHYSICS_REPLACEMENT_ASSERT(); #endif // ENABLE_CRY_PHYSICS 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, 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; #if ENABLE_CRY_PHYSICS ray_hit hit; int rayFlags = geom_colltype_player << rwi_colltype_bit | rwi_stop_at_pierceable; if (m_pPhysicalWorld->RayWorldIntersection(referencePos + Vec3(0, 0, padding), Vec3(0, 0, -rayLength), ent_terrain | ent_static | ent_sleeping_rigid | ent_rigid, rayFlags, &hit, 1)) { return hit.pt.z; } #else // Get bottom level CRY_PHYSICS_REPLACEMENT_ASSERT(); #endif // ENABLE_CRY_PHYSICS // 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 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(CMesh::POSITIONS); Vec3* const normals = pMesh->GetMesh()->GetStreamPtr(CMesh::NORMALS); SMeshTexCoord* const texcoords = pMesh->GetMesh()->GetStreamPtr(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(CMesh::FACES); nBufferPos = ReadFromBuffer(szBinaryStream, size, nBufferPos, faces, sizeof(SMeshFace) * nFaceCount); } else if (nVersion >= 1) { vtx_idx* const indices = pMesh->GetMesh()->GetStreamPtr(CMesh::INDICES); if (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(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; #if ENABLE_CRY_PHYSICS Vec3 gravity; pe_params_buoyancy pb[4]; int iMedium = 0; // medium values, -1 == ignore medium areas, 0 = water areas, 1 = air areas. int i, nBuoys = m_pPhysicalWorld->CheckAreas(*pvPos, gravity, pb, 4, iMedium, ZERO, pent); #endif float max_level = (!bInVisarea) ? (bAccurate ? GetAccurateOceanHeight(*pvPos) : GetWaterLevel()) : WATER_LEVEL_UNKNOWN; #if ENABLE_CRY_PHYSICS for (i = 0; i < nBuoys; i++) { if (pb[i].iMedium == 0 && (!bInVisarea || fabs_tpl(pb[i].waterPlane.origin.x) + fabs_tpl(pb[i].waterPlane.origin.y) > 0)) { float fVolumeLevel = pvPos->z - pb[i].waterPlane.n.z * (pb[i].waterPlane.n * (*pvPos - pb[i].waterPlane.origin)); // it's not ocean if (pb[i].waterPlane.origin.y + pb[i].waterPlane.origin.x != 0.0f) { max_level = max(max_level, fVolumeLevel); } } } #endif // ENABLE_CRY_PHYSICS 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; // if(pArea->m_pObjectsTree) // pArea->m_pObjectsTree->FreeAreaBrushes(true); PodArray 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 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_pPartManager) { m_pPartManager->Reset(false); } if (m_pDecalManager) { m_pDecalManager->Reset(); } CStatObjFoliage* pFoliage, * pFoliageNext; for (pFoliage = m_pFirstFoliage; &pFoliage->m_next != &m_pFirstFoliage; pFoliage = pFoliageNext) { pFoliageNext = pFoliage->m_next; delete pFoliage; } ReRegisterKilledVegetationInstances(); } IRenderNode* C3DEngine::CreateRenderNode(EERType type) { switch (type) { case eERType_Brush: { CBrush* pBrush = new CBrush(); return pBrush; } case eERType_Vegetation: { CVegetation* pVeget = new CVegetation(); return pVeget; } case eERType_Cloud: { CCloudRenderNode* pCloud = new CCloudRenderNode(); return pCloud; } case eERType_FogVolume: { CFogVolumeRenderNode* pFogVolume = new CFogVolumeRenderNode(); return pFogVolume; } case eERType_Road: { CRoadRenderNode* pRoad = new CRoadRenderNode(); return pRoad; } 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; } #if ENABLE_CRY_PHYSICS case eERType_Rope: { IRopeRenderNode* pRenderNode = new CRopeRenderNode(); return pRenderNode; } #endif // ENABLE_CRY_PHYSICS 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 ENABLE_CRY_PHYSICS case eERType_BreakableGlass: { IBreakableGlassRenderNode* pRenderNode = new CBreakableGlassRenderNode(); return pRenderNode; } #endif // ENABLE_CRY_PHYSICS case eERType_MergedMesh: { IRenderNode* pRenderNode = new CMergedMeshRenderNode(); return pRenderNode; } #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; } #if ENABLE_CRY_PHYSICS void C3DEngine::SetWind(const Vec3& vWind) { m_vWindSpeed = vWind; if (!m_vWindSpeed.IsZero()) { #if ENABLE_CRY_PHYSICS // Maintain a large physics area for global wind. if (!m_pGlobalWind) { primitives::sphere geomSphere; geomSphere.center = Vec3(0); geomSphere.r = 1e7f; IGeometry* pGeom = m_pPhysicalWorld->GetGeomManager()->CreatePrimitive(primitives::sphere::type, &geomSphere); m_pGlobalWind = GetPhysicalWorld()->AddArea(pGeom, Vec3(ZERO), Quat(IDENTITY), 1.0f); pe_params_foreign_data fd; fd.iForeignFlags = PFF_OUTDOOR_AREA; m_pGlobalWind->SetParams(&fd); } // The wind shouldn't even be a half space if there's no ocean, so set the water level arbitrarily low. const float noOceanAirDepth = -100000.0f; float waterLevel = OceanToggle::IsActive() ? OceanRequest::GetOceanLevelOrDefault(noOceanAirDepth) : GetWaterLevel(); // Set medium half-space above water level. Set standard air density. pe_params_buoyancy pb; pb.iMedium = 1; // 1 == air pb.waterPlane.n.Set(0, 0, -1); pb.waterPlane.origin.Set(0, 0, waterLevel); pb.waterFlow = m_vWindSpeed; pb.waterResistance = 1; pb.waterDensity = 1; m_pGlobalWind->SetParams(&pb); #endif // ENABLE_CRY_PHYSICS } else if (m_pGlobalWind) { #if ENABLE_CRY_PHYSICS GetPhysicalWorld()->DestroyPhysicalEntity(m_pGlobalWind); #endif m_pGlobalWind = 0; } } #endif // ENABLE_CRY_PHYSICS Vec3 C3DEngine::GetWind(const AABB& box, bool bIndoors) const { FUNCTION_PROFILER_3DENGINE; if (!m_pCVars->e_Wind) { return Vec3_Zero; } // Start with global wind. Vec3 vWind = GetGlobalWind(bIndoors); if (m_pCVars->e_WindAreas) { #if ENABLE_CRY_PHYSICS // Iterate all areas, looking for wind areas. Skip first (global) area, and global wind. pe_params_buoyancy pb; IPhysicalEntity* pArea = GetPhysicalWorld()->GetNextArea(0); if (pArea) { assert(!(pArea->GetParams(&pb) && pb.iMedium == 1)); while (pArea = GetPhysicalWorld()->GetNextArea(pArea)) { if (pArea != m_pGlobalWind && pArea->GetParams(&pb) && pb.iMedium == 1) { if (bIndoors) { // Skip outdoor areas. pe_params_foreign_data fd; if (pArea->GetParams(&fd) && (fd.iForeignFlags & PFF_OUTDOOR_AREA)) { continue; } } pe_status_area area; area.ctr = box.GetCenter(); area.size = box.GetSize() * 0.5f; if (pArea->GetStatus(&area)) { vWind += area.pb.waterFlow; } } } } #else const Physics::WindRequests* windRequests = AZ::Interface::Get(); if (windRequests) { const AZ::Aabb aabb = LyAABBToAZAabb(box); const AZ::Vector3 wind = windRequests->GetWind(aabb); vWind += AZVec3ToLYVec3(wind); } #endif } 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; } #if ENABLE_CRY_PHYSICS return m_vWindSpeed; #else const Physics::WindRequests* windRequests = AZ::Interface::Get(); if (windRequests) { const AZ::Vector3 wind = windRequests->GetGlobalWind(); return AZVec3ToLYVec3(wind); } return Vec3_Zero; #endif } 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 ENABLE_CRY_PHYSICS if (m_pCVars->e_WindAreas) { #if ENABLE_CRY_PHYSICS // Iterate all areas, looking for wind areas. Skip first (global) area, and global wind. pe_params_buoyancy pb; IPhysicalEntity* pArea = GetPhysicalWorld()->GetNextArea(0); if (pArea && pPositions) { assert(!(pArea->GetParams(&pb) && pb.iMedium == 1)); while (pArea = GetPhysicalWorld()->GetNextArea(pArea)) { if (pArea != m_pGlobalWind && pArea->GetParams(&pb) && pb.iMedium == 1) { if (bIndoors) { // Skip outdoor areas. pe_params_foreign_data fd; if (pArea->GetParams(&fd) && (fd.iForeignFlags & PFF_OUTDOOR_AREA)) { continue; } } pe_status_pos sp; IF (pArea->GetStatus(&sp) == 0 || volume.IsIntersectBox(AABB(sp.pos + sp.BBox[0], sp.pos + sp.BBox[1])) == 0, 0) { continue; } pe_status_area area; area.size = Vec3(0, 0, 0); for (int i = 0; i < nSamples; ++i) { area.ctr = pPositions[i]; if (pArea->GetStatus(&area)) { pSamples[i] += area.pb.waterFlow; } } } } } #else const Physics::WindRequests* windRequests = AZ::Interface::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); } } #endif } #endif // ENABLE_CRY_PHYSICS 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(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; if (gEnv->pCharacterManager) { ICharacterManager::Statistics AnimStats; gEnv->pCharacterManager->GetStatistics(AnimStats); nObjectsLoaded += AnimStats.numAnimObjectModels + AnimStats.numCharModels; } 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(struct IRenderer* pRenderer) { delete this; } ////////////////////////////////////////////////////////////////////////// IIndexedMesh* C3DEngine::CreateIndexedMesh() { return new CIndexedMesh(); } void C3DEngine::SerializeState(TSerialize ser) { if (ser.IsReading()) { CStatObjFoliage* pFoliage, * pFoliageNext; for (pFoliage = m_pFirstFoliage; &pFoliage->m_next != &m_pFirstFoliage; pFoliage = pFoliageNext) { pFoliageNext = pFoliage->m_next; delete pFoliage; } } ser.Value("m_bOcean", m_bOcean); ser.Value("m_moonRotationLatitude", m_moonRotationLatitude); ser.Value("m_moonRotationLongitude", m_moonRotationLongitude); ser.Value("m_eShadowMode", *(alias_cast(&m_eShadowMode))); if (ser.IsReading()) { UpdateMoonDirection(); } if (m_pDecalManager) { m_pDecalManager->Serialize(ser); } m_pPartManager->Serialize(ser); m_pTimeOfDay->Serialize(ser); } void C3DEngine::PostSerialize(bool bReading) { m_pPartManager->PostSerialize(bReading); } void C3DEngine::InitMaterialDefautMappingAxis(_smart_ptr 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 pSubMat = (_smart_ptr)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, "", "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, ICryPak* 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 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 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; // refresh cached shadow casters if (GetCVars()->e_DynamicDistanceShadows != 0) { if (m_pObjectsTree != nullptr) { m_pObjectsTree->MarkAsUncompiled(); } } } ////////////////////////////////////////////////////////////////////////// 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(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(Vec3 vStart, Vec3 vEnd, Vec3& vHitPoint, 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 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->userData.m_pFoliage) { pTmpRNTData->userData.m_pFoliage->Release(); pTmpRNTData->userData.m_pFoliage = NULL; } } 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* plstObjects, ObjectTreeQueryFilterCallback filterCallback) { GetObjectsByTypeGlobal(*plstObjects, objType, pBox, filterCallback); if (GetVisAreaManager()) { GetVisAreaManager()->GetObjectsByType(*plstObjects, objType, pBox, filterCallback); } } void C3DEngine::CopyObjects(const AABB* pBox, PodArray* plstObjects) { if (IsObjectTreeReady()) { m_pObjectsTree->GetObjects(*plstObjects, pBox); } if (GetVisAreaManager()) { GetVisAreaManager()->GetObjects(*plstObjects, pBox); } } uint32 C3DEngine::GetObjectsByType(EERType objType, IRenderNode** pObjects) { PodArray 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 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* pLstObjects, ObjectTreeQueryFilterCallback filterCallback) { CopyObjectsByType(objType, &bbox, pLstObjects, filterCallback); } uint32 C3DEngine::GetObjectsInBox(const AABB& bbox, IRenderNode** pObjects) { PodArray 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 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::ObjectsTreeMarkAsUncompiled(const IRenderNode* pRenderNode) { if (Cry3DEngineBase::Get3DEngine()->IsObjectTreeReady()) { Cry3DEngineBase::Get3DEngine()->GetObjectTree()->MarkAsUncompiled(pRenderNode); } if (GetVisAreaManager()) { GetVisAreaManager()->MarkAllSectorsAsUncompiled(pRenderNode); } } void C3DEngine::OnObjectModified(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::PrecacheCharacter(IRenderNode* pObj, const float fImportance, ICharacterInstance* pCharacter, _smart_ptr pSlotMat, const Matrix34& matParent, const float fEntDistance, const float fScale, int nMaxDepth, bool bForceStreamingSystemUpdate, const SRenderingPassInfo& passInfo) { if (m_pObjManager) { m_pObjManager->PrecacheCharacter(pObj, fImportance, pCharacter, pSlotMat, matParent, fEntDistance, fScale, nMaxDepth, false, true, 0); if (bForceStreamingSystemUpdate) { m_pObjManager->ProcessObjectsStreaming(passInfo); } } } 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)); SetRenderNodeTypeEnabled(eERType_Brush, (GetCVars()->e_Brushes != 0)); SetRenderNodeTypeEnabled(eERType_Vegetation, (GetCVars()->e_Vegetation != 0)); } void C3DEngine::SetRenderNodeMaterialAtPosition(EERType eNodeType, const Vec3& vPos, _smart_ptr pMat) { PodArray 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(); } } void static DrawMeter(float scale, float& x, float& y, int nWidth, int nHeight, IRenderAuxGeom* pAuxRenderer) { vtx_idx indLines[ 8 ] = { 0, 1, 1, 2, 2, 3, 3, 0 }; const int c_yStepSizeTextMeter(8); const float barWidth = 0.20f; //normalised screen size const float yellowStart = 0.5f * barWidth; const float redStart = 0.75f * barWidth; Vec3 frame[ 4 ] = { Vec3((x - 1) / (float) nWidth, (y - 1) / (float) nHeight, 0), Vec3(x / (float) nWidth + barWidth, (y - 1) / (float) nHeight, 0), Vec3(x / (float) nWidth + barWidth, (y + c_yStepSizeTextMeter + 1) / (float) nHeight, 0), Vec3((x - 1) / (float) nWidth, (y + c_yStepSizeTextMeter + 1) / (float) nHeight, 0) }; pAuxRenderer->DrawLines(frame, 4, indLines, 8, ColorB(255, 255, 255, 255)); // draw meter itself vtx_idx indTri[ 6 ] = { 0, 1, 2, 0, 2, 3 }; // green part (0.0 <= scale <= 0.5) { float lScale(max(min(scale, 0.5f), 0.0f)); Vec3 bar[ 4 ] = { Vec3(x / (float) nWidth, y / (float) nHeight, 0), Vec3(x / (float) nWidth + lScale * barWidth, y / (float) nHeight, 0), Vec3(x / (float) nWidth + lScale * barWidth, (y + c_yStepSizeTextMeter) / (float) nHeight, 0), Vec3(x / (float) nWidth, (y + c_yStepSizeTextMeter) / (float) nHeight, 0) }; pAuxRenderer->DrawTriangles(bar, 4, indTri, 6, ColorB(0, 255, 0, 255)); } // green to yellow part (0.5 < scale <= 0.75) if (scale > 0.5f) { float lScale(min(scale, 0.75f)); Vec3 bar[ 4 ] = { Vec3(x / (float) nWidth + yellowStart, y / (float) nHeight, 0), Vec3(x / (float) nWidth + lScale * barWidth, y / (float) nHeight, 0), Vec3(x / (float) nWidth + lScale * barWidth, (y + c_yStepSizeTextMeter) / (float) nHeight, 0), Vec3(x / (float) nWidth + yellowStart, (y + c_yStepSizeTextMeter) / (float) nHeight, 0) }; float color[ 4 ]; // get right color if (lScale <= 0.5f) { color[ 0 ] = 0; color[ 1 ] = 1; color[ 2 ] = 0; color[ 3 ] = 1; } else if (lScale <= 0.75f) { color[ 0 ] = (lScale - 0.5f) * 4.0f; color[ 1 ] = 1; color[ 2 ] = 0; color[ 3 ] = 1; } else if (lScale <= 1.0f) { color[ 0 ] = 1; color[ 1 ] = 1 - (lScale - 0.75f) * 4.0f; color[ 2 ] = 0; color[ 3 ] = 1; } else { float time(gEnv->pTimer->GetAsyncCurTime()); float blink(sinf(time * 6.28f) * 0.5f + 0.5f); color[ 0 ] = 1; color[ 1 ] = blink; color[ 2 ] = blink; color[ 3 ] = 1; } ColorB colSegStart(0, 255, 0, 255); ColorB colSegEnd((uint8)(color[ 0 ] * 255), (uint8)(color[ 1 ] * 255), (uint8)(color[ 2 ] * 255), (uint8)(color[ 3 ] * 255)); ColorB col[ 4 ] = { colSegStart, colSegEnd, colSegEnd, colSegStart }; pAuxRenderer->DrawTriangles(bar, 4, indTri, 6, col); } // yellow to red part (0.75 < scale <= 1.0) if (scale > 0.75f) { float lScale(min(scale, 1.0f)); Vec3 bar[ 4 ] = { Vec3(x / (float) nWidth + redStart, y / (float) nHeight, 0), Vec3(x / (float) nWidth + lScale * barWidth, y / (float) nHeight, 0), Vec3(x / (float) nWidth + lScale * barWidth, (y + c_yStepSizeTextMeter) / (float) nHeight, 0), Vec3(x / (float) nWidth + redStart, (y + c_yStepSizeTextMeter) / (float) nHeight, 0) }; float color[ 4 ]; if (lScale <= 0.5f) { color[ 0 ] = 0; color[ 1 ] = 1; color[ 2 ] = 0; color[ 3 ] = 1; } else if (lScale <= 0.75f) { color[ 0 ] = (lScale - 0.5f) * 4.0f; color[ 1 ] = 1; color[ 2 ] = 0; color[ 3 ] = 1; } else if (lScale <= 1.0f) { color[ 0 ] = 1; color[ 1 ] = 1 - (lScale - 0.75f) * 4.0f; color[ 2 ] = 0; color[ 3 ] = 1; } else { float time(gEnv->pTimer->GetAsyncCurTime()); float blink(sinf(time * 6.28f) * 0.5f + 0.5f); color[ 0 ] = 1; color[ 1 ] = blink; color[ 2 ] = blink; color[ 3 ] = 1; } ColorB colSegStart(255, 255, 0, 255); ColorB colSegEnd((uint8)(color[ 0 ] * 255), (uint8)(color[ 1 ] * 255), (uint8)(color[ 2 ] * 255), (uint8)(color[ 3 ] * 255)); ColorB col[ 4 ] = { colSegStart, colSegEnd, colSegEnd, colSegStart }; pAuxRenderer->DrawTriangles(bar, 4, indTri, 6, col); } } SBending* C3DEngine::GetBendingEntry(SBending* pSrc, 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); // CParticleManager needs to update cached force data CParticleManager::Instance()->OnPhysAreaChange(); } 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 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; } } if (pRenderNode->GetRenderNodeType() == eERType_Vegetation && (fEntRadius < (fExploRadius - fDist * .5f)) && !((CVegetation*)pRenderNode)->IsBreakable()) { // remove this object UnRegisterEntityAsJob(pRenderNode); pRenderNode->Dephysicalize(); if (!(pRenderNode->m_dwRndFlags & ERF_PROCEDURAL)) { m_lstKilledVegetations.Add(pRenderNode); } } else { // if something was impossible to destroy - disable deformation bEverythingDeleted = false; } } } } 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++; // type is not always totally determined before this point if (newObjInfo.pClassName) { if (strcmp(newObjInfo.pClassName, "Brush") == 0) // TODO: could not find a better way of checking if it is a brush { newObjInfo.type = I3DEngine::DLOT_BRUSH; } else if (strcmp(newObjInfo.pClassName, "Vegetation") == 0) // TODO: could not find a better way of checking if it is vegetation { newObjInfo.type = I3DEngine::DLOT_VEGETATION; } } 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::iterator iter = m_assets.begin(); std::vector::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 'B': m_filter |= I3DEngine::DLOT_BRUSH; break; case 'V': m_filter |= I3DEngine::DLOT_VEGETATION; break; 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 ""; } } ////////////////////////////////////////////////////////////////////////// void CDebugDrawListMgr::GetStrCurrFilter(TMyStandardString& strOut) { const char* pFilterNames[] = { "", "Brushes", "Vegetation", "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 ""; } #endif //RELEASE // RoadRenderNode Recompile Queue void C3DEngine::RoadRenderNodeRebuildQueue_Add(CRoadRenderNode* roadRenderNodePtr) { if (Get3DEngine()->m_lstRoadRenderNodesForUpdate.Find(roadRenderNodePtr) < 0) { Get3DEngine()->m_lstRoadRenderNodesForUpdate.Add(roadRenderNodePtr); } } void C3DEngine::RoadRenderNodeRebuildQueue_Remove(CRoadRenderNode* roadRenderNodePtr) { m_lstRoadRenderNodesForUpdate.Delete(roadRenderNodePtr); } bool C3DEngine::RoadRenderNodeRebuildQueue_Update() { PodArray& rList = m_lstRoadRenderNodesForUpdate; if (rList.Count()) { CRoadRenderNode* pCurrRoadRenderNode = rList[0]; bool compileResult = pCurrRoadRenderNode->Compile(); rList.Delete(0); if (!compileResult) { // Requeue roads that fail to build (waiting for terrain) rList.Add(pCurrRoadRenderNode); } } return rList.Count() > 0; } void C3DEngine::GetStatObjAndMatTables(DynArray* pStatObjTable, DynArray<_smart_ptr >* pMatTable, DynArray* pStatInstGroupTable, uint32 nObjTypeMask) { SHotUpdateInfo exportInfo; exportInfo.nObjTypeMask = nObjTypeMask; std::vector statObjTable; std::vector<_smart_ptr > matTable; std::vector 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(); } } void C3DEngine::ClipTriangleHelper(const PodArray& lstInds, const Plane pPlanes[4], PodArray& posBuffer, PodArray& lstClippedInds) const { CPolygonClipContext clipContext; int indexCount = lstInds.Count(); for (int i = 0; i < indexCount; i += 3) { CRoadRenderNode::ClipTriangle(clipContext, posBuffer, lstInds, lstClippedInds, i, pPlanes); } }