BVB Source Codes

CRYENGINE Show ParticleManager.cpp Source code

Return Download CRYENGINE: download ParticleManager.cpp Source code - Download CRYENGINE Source code - Type:.cpp
  1. // Copyright 2001-2016 Crytek GmbH / Crytek Group. All rights reserved.
  2.  
  3. // -------------------------------------------------------------------------
  4. //  File name:   ParticleManager.cpp
  5. //  Version:     v1.00
  6. //  Created:     28/5/2001 by Vladimir Kajalin
  7. //  Compilers:   Visual Studio.NET
  8. //  Description: Manage particle effects and emitters
  9. // -------------------------------------------------------------------------
  10. //  History:
  11. //      - 03:2006                                : Modified by Jan M眉ller (Serialization)
  12. //
  13. ////////////////////////////////////////////////////////////////////////////
  14.  
  15. #include "StdAfx.h"
  16. #include <ParticleSystem/ParticleEffect.h>
  17. #include <ParticleSystem/ParticleEmitter.h>
  18. #include "ParticleManager.h"
  19. #include "ParticleEmitter.h"
  20. #include "ParticleContainer.h"
  21. #include "ParticleMemory.h"
  22. #include "3dEngine.h"
  23. #include "ParticleSystem/ParticleSystem.h"
  24.  
  25. #include <CryString/StringUtils.h>
  26. #include <CrySystem/Profilers/IStatoscope.h>
  27. #include <CryThreading/IJobManager_JobDelegator.h>
  28. #include <CrySystem/ZLib/IZLibCompressor.h>
  29. #include <CrySerialization/ClassFactory.h>
  30.  
  31. #define LIBRARY_PATH    "Libs/"
  32. #define EFFECTS_SUBPATH LIBRARY_PATH "Particles/"
  33. #define LEVEL_PATH      "Levels/"
  34.  
  35. using namespace minigui;
  36.  
  37. CRY_PFX2_DBG
  38.  
  39. //////////////////////////////////////////////////////////////////////////
  40. // Single direct entry point for particle clients.
  41. IParticleManager* CreateParticleManager(bool bEnable)
  42. {
  43.         return CryAlignedNew<CParticleManager>(bEnable);
  44. }
  45.  
  46. void DestroyParticleManager(IParticleManager* pParticleManager)
  47. {
  48.         CryAlignedDelete(static_cast<CParticleManager*>(pParticleManager));
  49. }
  50.  
  51. //////////////////////////////////////////////////////////////////////////
  52. // CParticleBatchDataManager implementation
  53.  
  54. void CParticleBatchDataManager::PrepareForRender(const SRenderingPassInfo& passInfo)
  55. {
  56.         if (passInfo.GetRecursiveLevel() == 0)
  57.                 m_ParticlesToScene[passInfo.ThreadID()].resize(0);
  58.         m_ParticlesJobStart[passInfo.ThreadID()][passInfo.GetRecursiveLevel()] = m_ParticlesToScene[passInfo.ThreadID()].size();
  59. }
  60. void CParticleBatchDataManager::FinishParticleRenderTasks(const SRenderingPassInfo& passInfo)
  61. {
  62.         FUNCTION_PROFILER_CONTAINER(this);
  63.  
  64.         // make sure no job/task runs anymore
  65.         SyncAllUpdateParticlesJobs();
  66.  
  67.         int nJobStart = m_ParticlesJobStart[passInfo.ThreadID()][passInfo.GetRecursiveLevel()];
  68.         if (m_ParticlesToScene[passInfo.ThreadID()].size() > nJobStart)
  69.         {
  70.                 GetRenderer()->EF_AddMultipleParticlesToScene(&m_ParticlesToScene[passInfo.ThreadID()][nJobStart], m_ParticlesToScene[passInfo.ThreadID()].size() - nJobStart, passInfo);
  71.         }
  72. }
  73.  
  74. void CParticleBatchDataManager::GetMemoryUsage(ICrySizer* pSizer) const
  75. {
  76.         for (int t = 0; t < RT_COMMAND_BUF_COUNT; ++t)
  77.                 pSizer->AddObject(m_ParticlesToScene[t]);
  78. }
  79.  
  80. void CParticleBatchDataManager::ResetData()
  81. {
  82.         for (int t = 0; t < RT_COMMAND_BUF_COUNT; ++t)
  83.                 stl::free_container(m_ParticlesToScene[t]);
  84.  
  85.         for (int i = 0, end = m_UpdateParticleStates.size(); i < end; ++i)
  86.                 delete m_UpdateParticleStates[i];
  87.  
  88.         stl::free_container(m_UpdateParticleStates);
  89.         m_nUsedStates = 0;
  90. }
  91.  
  92. void CParticleBatchDataManager::SyncAllUpdateParticlesJobs()
  93. {
  94.         for (int i = 0; i < m_nUsedStates; ++i)
  95.         {
  96.                 gEnv->GetJobManager()->WaitForJob(*m_UpdateParticleStates[i]);
  97.         }
  98.         m_nUsedStates = 0;
  99. }
  100.  
  101. //////////////////////////////////////////////////////////////////////////
  102. ITimer* g_pParticleTimer = NULL;
  103.  
  104. //////////////////////////////////////////////////////////////////////////
  105. CParticleManager::CParticleManager(bool bEnable)
  106. {
  107.         m_bEnabled = bEnable;
  108.         m_bRegisteredListener = false;
  109. #ifdef bEVENT_TIMINGS
  110.         m_iEventSwitch = 1;
  111.         m_timeThreshold = 0.f;
  112. #endif
  113.         m_pWidget = NULL;
  114.         g_pParticleTimer = gEnv->pTimer;
  115.         m_pLastDefaultParams = &GetDefaultParams();
  116.  
  117.         if (GetPhysicalWorld())
  118.         {
  119.                 GetPhysicalWorld()->AddEventClient(EventPhysAreaChange::id, &StaticOnPhysAreaChange, 0);
  120.  
  121.                 REGISTER_COMMAND("e_ParticleListEmitters", &CmdParticleListEmitters, 0, "Writes all emitters to log");
  122.                 REGISTER_COMMAND("e_ParticleListEffects", &CmdParticleListEffects, 0, "Writes all effects used and counts to log");
  123.                 REGISTER_COMMAND("e_ParticleMemory", &CmdParticleMemory, 0, "Displays current particle memory usage");
  124.         }
  125.  
  126.         // reset tracking data for dumping vertex/index pool usage
  127. #if defined(PARTICLE_COLLECT_VERT_IND_POOL_USAGE)
  128.         memset(m_arrVertexIndexPoolUsage, 0, sizeof(m_arrVertexIndexPoolUsage));
  129.         m_bOutOfVertexIndexPoolMemory = false;
  130.         m_nRequieredVertexPoolMemory = 0;
  131.         m_nRequieredIndexPoolMemory = 0;
  132.         m_nRendererParticleContainer = 0;
  133. #endif
  134.  
  135.         UpdateEngineData();
  136. }
  137.  
  138. CParticleManager::~CParticleManager()
  139. {
  140.         if (Get3DEngine()->GetIVisAreaManager())
  141.         {
  142.                 Get3DEngine()->GetIVisAreaManager()->RemoveListener(this);
  143.         }
  144.         GetPhysicalWorld()->RemoveEventClient(EventPhysAreaChange::id, &StaticOnPhysAreaChange, 0);
  145.         Reset();
  146.  
  147.         // Destroy references to shaders.
  148.         m_pPartLightShader = NULL;
  149.  
  150.         if (m_pWidget)
  151.         {
  152.                 gEnv->pSystem->GetPerfHUD()->RemoveWidget(m_pWidget);
  153.         }
  154. }
  155.  
  156. struct SortEffectStats
  157. {
  158.         typedef CParticleManager::TEffectStats::value_type SEffectStatEntry;
  159.  
  160.         float SParticleCounts::* _pSortField;
  161.  
  162.         SortEffectStats(float SParticleCounts::* pSortField)
  163.                 : _pSortField(pSortField) {}
  164.  
  165.         bool operator()(const SEffectStatEntry& a, const SEffectStatEntry& b) const
  166.         {
  167.                 return a.second.*_pSortField > b.second.*_pSortField;
  168.         }
  169. };
  170.  
  171. void CParticleManager::CollectEffectStats(TEffectStats& mapEffectStats, float SParticleCounts::* pSortField) const
  172. {
  173.         CRY_ASSERT_MESSAGE(false, "CParticleManager::CollectEffectStats is deprecated");
  174.  
  175.         /* Disabled as sorting a std::sort on a map is not allowed
  176.         //
  177.  
  178.         SParticleCounts countsTotal;
  179.         for (const auto& e : m_Emitters)
  180.         {
  181.                 for (const auto& c : e.GetContainers())
  182.                 {
  183.                         SParticleCounts countsEmitter;
  184.                         c.GetCounts(countsEmitter);
  185.                         SParticleCounts& countsEffect = mapEffectStats[c.GetEffect()];
  186.                         AddArray(FloatArray(countsEffect), FloatArray(countsEmitter));
  187.                         AddArray(FloatArray(countsTotal), FloatArray(countsEmitter));
  188.                 }
  189.         }
  190.  
  191.         // Add total to list.
  192.         mapEffectStats[NULL] = countsTotal;
  193.  
  194.        
  195.         // Re-sort by selected stat.
  196.         std::sort(mapEffectStats.begin(), mapEffectStats.end(), SortEffectStats(pSortField));
  197.         */
  198. }
  199.  
  200. void CParticleManager::GetCounts(SParticleCounts& counts)
  201. {
  202.         counts = m_GlobalCounts;
  203. }
  204.  
  205. void CParticleManager::GetMemoryUsage(ICrySizer* pSizer) const
  206. {
  207.         {
  208.                 SIZER_COMPONENT_NAME(pSizer, "Effects");
  209.                 pSizer->AddObject(m_Effects);
  210.                 pSizer->AddObject(m_LoadedLibs);
  211.         }
  212.         {
  213.                 SIZER_COMPONENT_NAME(pSizer, "Emitters");
  214.                 pSizer->AddObject(m_Emitters);
  215.         }
  216. }
  217.  
  218. void CParticleManager::PrintParticleMemory()
  219. {
  220.         // Get stats and mem usage for all effects.
  221.         ICrySizer* pSizer = GetSystem()->CreateSizer();
  222.         SEffectCounts eCounts;
  223.  
  224.         pSizer->AddObject(m_LoadedLibs);
  225.         pSizer->AddObject(m_Effects);
  226.  
  227.         for (auto& me : m_Effects)
  228.         {
  229.                 me.second->GetEffectCounts(eCounts);
  230.         }
  231.  
  232.         CryLogAlways("Particle effects: %d loaded, %d enabled, %d active, %" PRISIZE_T " KB, %" PRISIZE_T " allocs",
  233.                      eCounts.nLoaded, eCounts.nEnabled, eCounts.nActive, pSizer->GetTotalSize() >> 10, pSizer->GetObjectCount());
  234.  
  235.         // Get mem usage for all emitters.
  236.         pSizer->Reset();
  237.         pSizer->Add(*this);
  238.         m_Emitters.GetMemoryUsage(pSizer);
  239.  
  240.         stl::SPoolMemoryUsage memParticle = ParticleObjectAllocator().GetTotalMemory();
  241.  
  242.         CryLogAlways("Particle heap: %d KB used, %d KB freed, %d KB unused",
  243.                      int(memParticle.nUsed >> 10), int(memParticle.nPoolFree() >> 10), int(memParticle.nNonPoolFree() >> 10));
  244.         CryLogAlways("Non heap: %d KB", int(pSizer->GetTotalSize() - memParticle.nUsed) >> 10);
  245.  
  246.         pSizer->Release();
  247. }
  248.  
  249. void CParticleManager::Reset()
  250. {
  251.         m_bRegisteredListener = false;
  252.  
  253.         for (auto& e : m_Emitters)
  254.         {
  255.                 EraseEmitter(&e);
  256.         }
  257.  
  258.         ResetData();
  259.  
  260.         m_PhysEnv.Clear();
  261.  
  262.         // reset tracking data for dumping vertex/index pool usage
  263. #if defined(PARTICLE_COLLECT_VERT_IND_POOL_USAGE)
  264.         memset(m_arrVertexIndexPoolUsage, 0, sizeof(m_arrVertexIndexPoolUsage));
  265.         m_bOutOfVertexIndexPoolMemory = false;
  266.         m_nRequieredVertexPoolMemory = 0;
  267.         m_nRequieredIndexPoolMemory = 0;
  268.         m_nRendererParticleContainer = 0;
  269. #endif
  270. }
  271.  
  272. //////////////////////////////////////////////////////////////////////////
  273. void CParticleManager::ClearRenderResources(bool bForceClear)
  274. {
  275.         if (GetCVars()->e_ParticlesDebug & AlphaBit('m'))
  276.                 PrintParticleMemory();
  277.  
  278.         Reset();
  279.  
  280.         if (!GetCVars()->e_ParticlesPreload || bForceClear)
  281.         {
  282.                 m_Effects.clear();
  283.                 m_LoadedLibs.clear();
  284.                 m_Emitters.clear();
  285.                 m_pDefaultEffect = 0;
  286.                 m_pLastDefaultParams = &GetDefaultParams();
  287.         }
  288.  
  289.         CParticleEffect* pEffect = CParticleEffect::get_intrusive_list_root();
  290.         while (pEffect)
  291.         {
  292.                 pEffect->UnloadResources(false);
  293.                 pEffect = pEffect->get_next_intrusive();
  294.         }
  295.  
  296.         stl::free_container(m_Effects);
  297.  
  298.         ParticleObjectAllocator().ResetUsage();
  299.  
  300.         m_pPartLightShader = 0;
  301.         m_PhysEnv.FreeMemory();
  302. }
  303.  
  304. //////////////////////////////////////////////////////////////////////////
  305. void CParticleManager::ClearDeferredReleaseResources()
  306. {
  307.         // make sure all particles etc are gone, only the emitters are keep alive
  308.         for (auto& e : m_Emitters)
  309.                 e.Reset();
  310. }
  311.  
  312. //////////////////////////////////////////////////////////////////////////
  313. void CParticleManager::AddEventListener(IParticleEffectListener* pListener)
  314. {
  315.         assert(pListener);
  316.  
  317.         if (pListener)
  318.                 stl::push_back_unique(m_ListenersList, pListener);
  319. }
  320.  
  321. void CParticleManager::RemoveEventListener(IParticleEffectListener* pListener)
  322. {
  323.         assert(pListener);
  324.  
  325.         m_ListenersList.remove(pListener);
  326. }
  327.  
  328. //////////////////////////////////////////////////////////////////////////
  329. // Particle Effects.
  330. //////////////////////////////////////////////////////////////////////////
  331.  
  332. void CParticleManager::SetDefaultEffect(const IParticleEffect* pEffect)
  333. {
  334.         m_pDefaultEffect = pEffect ? new CParticleEffect(static_cast<const CParticleEffect&>(*pEffect), true) : NULL;
  335.         m_pLastDefaultParams = &GetDefaultParams();
  336.         ReloadAllEffects();
  337. }
  338.  
  339. const ParticleParams& CParticleManager::GetDefaultParams(ParticleParams::EInheritance eInheritance, int nVersion) const
  340. {
  341.         static ParticleParams s_paramsStandard;
  342.         static ParticleParams s_paramsZero(ZERO);
  343.  
  344.         if (eInheritance == eInheritance.System)
  345.         {
  346.                 if (m_pDefaultEffect)
  347.                 {
  348.                         if (const IParticleEffect* pEffect = m_pDefaultEffect->FindActiveEffect(nVersion))
  349.                                 return pEffect->GetParticleParams();
  350.                 }
  351.         }
  352.         else if (eInheritance == eInheritance.Zero)
  353.                 return s_paramsZero;
  354.         return s_paramsStandard;
  355. }
  356.  
  357. IParticleEffect* CParticleManager::CreateEffect()
  358. {
  359.         if (!m_bEnabled)
  360.                 return NULL;
  361.         return new CParticleEffect();
  362. }
  363.  
  364. //////////////////////////////////////////////////////////////////////////
  365. void CParticleManager::RenameEffect(CParticleEffect* pEffect, cstr sNewName)
  366. {
  367.         assert(pEffect);
  368.         pEffect->AddRef();
  369.         m_Effects.erase(pEffect->GetName());
  370.         if (*sNewName)
  371.                 m_Effects[sNewName] = pEffect;
  372.         pEffect->Release();
  373. }
  374.  
  375. //////////////////////////////////////////////////////////////////////////
  376. void CParticleManager::DeleteEffect(IParticleEffect* pEffect)
  377. {
  378.         if (!pEffect)
  379.                 return;
  380.         if (!pEffect->GetParent())
  381.                 m_Effects.erase(pEffect->GetName());
  382. }
  383.  
  384. //////////////////////////////////////////////////////////////////////////
  385. CParticleEffect* CParticleManager::FindLoadedEffect(cstr sEffectName)
  386. {
  387.         if (!m_bEnabled)
  388.                 return NULL;
  389.  
  390.         TEffectsList::iterator it = m_Effects.find(sEffectName);
  391.         if (it != m_Effects.end())
  392.                 return it->second;
  393.  
  394.         // Find sub-effect
  395.         if (const char* pDot = strchr(sEffectName, '.'))
  396.         {
  397.                 if (pDot = strrchr(pDot + 1, '.'))
  398.                 {
  399.                         // 2nd separator found, after library name
  400.                         string sBase(sEffectName, pDot);
  401.                         if (CParticleEffect* pParent = FindLoadedEffect(sBase))
  402.                                 return pParent->FindChild(pDot + 1);
  403.                 }
  404.         }
  405.         return NULL;
  406. }
  407.  
  408. //////////////////////////////////////////////////////////////////////////
  409. IParticleEffect* CParticleManager::FindEffect(cstr sEffectName, cstr sSource, bool bLoad)
  410. {
  411.         if (!m_bEnabled || !sEffectName || !*sEffectName)
  412.                 return NULL;
  413.  
  414.         LOADING_TIME_PROFILE_SECTION(gEnv->pSystem);
  415.  
  416.         pfx2::PParticleEffect pPfx2 = Cry3DEngineBase::m_pParticleSystem->FindEffect(sEffectName);
  417.         if (pPfx2)
  418.                 return pPfx2.get();
  419.  
  420.         CParticleEffect* pEffect = FindLoadedEffect(sEffectName);
  421.         if (!pEffect)
  422.         {
  423.                 if (CanAccessFiles(sEffectName))
  424.                 {
  425.                         if (cstr sDot = strchr(sEffectName, '.'))
  426.                         {
  427.                                 stack_string sLibraryName(sEffectName, sDot);
  428.                                 LoadLibrary(sLibraryName);
  429.  
  430.                                 // Try it again.
  431.                                 pEffect = FindLoadedEffect(sEffectName);
  432.                         }
  433.  
  434.                         if (!pEffect)
  435.                         {
  436.                                 Warning("Particle effect not found: '%s'%s%s", sEffectName, *sSource ? " from " : "", sSource);
  437.                         }
  438.                 }
  439.  
  440.                 if (!pEffect)
  441.                 {
  442.                         // Add an empty effect to avoid duplicate loads and warnings.
  443.                         pEffect = new CParticleEffect(sEffectName);
  444.                         m_Effects[pEffect->GetName()] = pEffect;
  445.                         return NULL;
  446.                 }
  447.         }
  448.  
  449.         MEMSTAT_CONTEXT(EMemStatContextTypes::MSC_Other, 0, "Particles");
  450.         MEMSTAT_CONTEXT_FMT(EMemStatContextTypes::MSC_ParticleEffect, EMemStatContextFlags::MSF_Instance, "%s", sEffectName);
  451.  
  452.         assert(pEffect);
  453.         if (pEffect->IsEnabled() || pEffect->GetChildCount())
  454.         {
  455.                 if (bLoad)
  456.                         pEffect->LoadResources(true, sSource);
  457.  
  458.                 if (GetCVars()->e_ParticlesConvertPfx1)
  459.                 {
  460.                         m_pParticleSystem->ConvertEffect(pEffect, GetCVars()->e_ParticlesConvertPfx1 > 1);
  461.                 }
  462.                 return pEffect;
  463.         }
  464.  
  465.         // Empty effect (either disabled params, or cached not found).
  466.         return NULL;
  467. }
  468.  
  469. //////////////////////////////////////////////////////////////////////////
  470. #ifdef bEVENT_TIMINGS
  471. void CParticleManager::LogEvents()
  472. {
  473.         WriteLock lock(m_EventLock);
  474.         if (GetCVars()->e_ParticlesDebug & AlphaBits('ed'))
  475.         {
  476.                 if (!m_aEvents.size())
  477.                 {
  478.                         // Start timing this frame.
  479.                         AddEventTiming("*Frame", 0);
  480.                 }
  481.                 else
  482.                 {
  483.                         // Finish and log frame.
  484.                         m_aEvents[0].timeEnd = max(GetParticleTimer()->GetAsyncCurTime(), m_aEvents[0].timeStart + GetParticleTimer()->GetRealFrameTime());
  485.                         float timeFrameStart = m_aEvents[0].timeStart,
  486.                               timeFrameTime = m_aEvents[0].timeEnd - m_aEvents[0].timeStart;
  487.  
  488.                         if (timeFrameTime > 0.f)
  489.                         {
  490.                                 static const int nWidth = 50;
  491.                                 int nSumStart = m_aEvents.size();
  492.                                 float timeTotal = 0.f;
  493.  
  494.                                 // Add to summary entries at end.
  495.                                 for (int e = 0; e < nSumStart; e++)
  496.                                 {
  497.                                         SEventTiming* pEvent = &m_aEvents[e];
  498.                                         if (pEvent->nContainerId)
  499.                                         {
  500.                                                 timeTotal += pEvent->timeEnd - pEvent->timeStart;
  501.                                                 int s;
  502.                                                 for (s = nSumStart; s < m_aEvents.size(); s++)
  503.                                                 {
  504.                                                         SEventTiming* pSum = &m_aEvents[s];
  505.                                                         if (pSum->sEvent == pEvent->sEvent && pSum->nThread == pEvent->nThread)
  506.                                                                 break;
  507.                                                 }
  508.                                                 if (s == m_aEvents.size())
  509.                                                 {
  510.                                                         // Add to summary.
  511.                                                         SEventTiming* pSum = m_aEvents.push_back();
  512.                                                         *pSum = m_aEvents[e];
  513.                                                         pSum->nContainerId = 0;
  514.                                                 }
  515.                                         }
  516.                                 }
  517.  
  518.                                 // Check against time threshold.
  519.                                 if ((GetCVars()->e_ParticlesDebug & AlphaBit('e')) || timeTotal > m_timeThreshold)
  520.                                 {
  521.                                         // Increase threshold for next time.
  522.                                         m_timeThreshold = timeTotal * 1.1f;
  523.  
  524.                                         for (auto& pEventC : m_aEvents)
  525.                                         {
  526.                                                 if (!pEventC->sEvent)
  527.                                                         continue;
  528.  
  529.                                                 // Iterate unique threads.
  530.                                                 for (int t = c; t < m_aEvents.size(); t++)
  531.                                                 {
  532.                                                         SEventTiming* pEventT = &m_aEvents[t];
  533.                                                         if (!pEventT->sEvent)
  534.                                                                 continue;
  535.                                                         if (pEventT->nContainerId != pEventC->nContainerId)
  536.                                                                 continue;
  537.  
  538.                                                         cstr sThread = gEnv->pThreadManager->GetThreadName(pEventT->nThread);
  539.                                                         if (pEventT == pEventC)     // Main thread.
  540.                                                                 GetLog()->LogToFile("%*s %s(%X) @%s",
  541.                                                                                     nWidth, "", pEventC->pEffect ? pEventC->pEffect->GetName() : "", pEventC->nContainerId, sThread);
  542.                                                         else
  543.                                                                 GetLog()->LogToFile("%*s   @%s", nWidth, "", sThread);
  544.  
  545.                                                         // Log event times.
  546.                                                         for (int e = t; e < m_aEvents.size(); e++)
  547.                                                         {
  548.                                                                 SEventTiming* pEvent = &m_aEvents[e];
  549.                                                                 if (!pEvent->sEvent)
  550.                                                                         continue;
  551.                                                                 if (pEvent->nContainerId != pEventT->nContainerId || pEvent->nThread != pEventT->nThread)
  552.                                                                         continue;
  553.  
  554.                                                                 // Construct thread timeline.
  555.                                                                 char sGraph[nWidth + 1];
  556.                                                                 memset(sGraph, ' ', nWidth);
  557.                                                                 sGraph[nWidth] = 0;
  558.  
  559.                                                                 int start_iter = pEventC->nContainerId ? e : 0;
  560.                                                                 int end_iter = pEventC->nContainerId ? e + 1 : nSumStart;
  561.                                                                 float timeStart = m_aEvents[e].timeStart,
  562.                                                                       timeEnd = m_aEvents[e].timeEnd,
  563.                                                                       timeTotal = 0.f;
  564.                                                                 for (int i = start_iter; i < end_iter; i++)
  565.                                                                 {
  566.                                                                         SEventTiming* pEventI = &m_aEvents[i];
  567.                                                                         if (pEventI->sEvent != pEvent->sEvent || pEventI->nThread != pEvent->nThread)
  568.                                                                                 continue;
  569.  
  570.                                                                         timeStart = min(timeStart, pEventI->timeStart);
  571.                                                                         timeEnd = max(timeEnd, pEventI->timeEnd);
  572.                                                                         timeTotal += pEventI->timeEnd - pEventI->timeStart;
  573.  
  574.                                                                         int nEventStart = int_round((pEventI->timeStart - timeFrameStart) * nWidth / timeFrameTime);
  575.                                                                         int nEventEnd = int_round((pEventI->timeEnd - timeFrameStart) * nWidth / timeFrameTime);
  576.  
  577.                                                                         nEventStart = min(nEventStart, nWidth - 1);
  578.                                                                         nEventEnd = min(max(nEventEnd, nEventStart + 1), nWidth);
  579.  
  580.                                                                         cstr sEvent = strrchr(pEventI->sEvent, ':');
  581.                                                                         char cEvent = sEvent ? sEvent[1] : *pEventI->sEvent;
  582.                                                                         memset(sGraph + nEventStart, cEvent, nEventEnd - nEventStart);
  583.                                                                 }
  584.  
  585.                                                                 GetLog()->LogToFile("%s     %.3f-%.3f [%.3f] %s",
  586.                                                                                     sGraph,
  587.                                                                                     (timeStart - timeFrameStart) * 1000.f, (timeEnd - timeFrameStart) * 1000.f, timeTotal * 1000.f,
  588.                                                                                     pEvent->sEvent);
  589.  
  590.                                                                 pEvent->sEvent = 0;
  591.                                                         }
  592.                                                 }
  593.                                         }
  594.                                 }
  595.                         }
  596.  
  597.                         // Clear log.
  598.                         GetCVars()->e_ParticlesDebug &= ~AlphaBit('e');
  599.                         m_aEvents.resize(0);
  600.                 }
  601.         }
  602.         else
  603.         {
  604.                 m_aEvents.resize(0);
  605.                 m_timeThreshold = 0.f;
  606.         }
  607.         m_iEventSwitch *= -1;
  608. }
  609. #endif
  610.  
  611. void CParticleManager::OnFrameStart()
  612. {
  613. #ifdef bEVENT_TIMINGS
  614.         LogEvents();
  615. #endif
  616.  
  617.         if (IsRuntime())
  618.                 ClearCachedLibraries();
  619.  
  620.         // Update allowed particle features.
  621.         UpdateEngineData();
  622.  
  623.         if (!m_bRegisteredListener && Get3DEngine()->GetIVisAreaManager())
  624.         {
  625.                 Get3DEngine()->GetIVisAreaManager()->AddListener(this);
  626.                 m_bRegisteredListener = true;
  627.         }
  628. }
  629.  
  630. void CParticleManager::Update()
  631. {
  632.         CRY_PROFILE_REGION(PROFILE_3DENGINE, "ParticleManager Update");
  633.         CRYPROFILE_SCOPE_PROFILE_MARKER("ParticleManager Update");
  634.  
  635.         if (m_bEnabled && GetCVars()->e_Particles)
  636.         {
  637.                 // move all stuff into its own scope to not measure physics time in the fiber
  638.                 FUNCTION_PROFILER_CONTAINER(0);
  639.                 PARTICLE_LIGHT_PROFILER();
  640.  
  641.                 {
  642.                         FRAME_PROFILER("SyncComputeVerticesJobs", GetSystem(), PROFILE_PARTICLE);
  643.                         GetRenderer()->SyncComputeVerticesJobs();
  644.                 }
  645.  
  646.                 CleanOldPhysAreaChangedProxies();
  647.                 UpdatePhysAreasChanged();
  648.  
  649.                 DumpAndResetVertexIndexPoolUsage();
  650.  
  651.                 bool bStatoscopeEffectStats = false;
  652.                 bool bCollectCounts = (GetCVars()->e_ParticlesDebug & 1)
  653.                                       || m_pWidget && m_pWidget->ShouldUpdate()
  654.                                       || gEnv->pStatoscope->RequiresParticleStats(bStatoscopeEffectStats);
  655.  
  656.                 ZeroStruct(m_GlobalCounts);
  657.  
  658.                 if (GetSystem()->IsPaused() || (GetCVars()->e_ParticlesDebug & AlphaBit('z')))
  659.                         return;
  660.  
  661.                 uint32 nActiveEmitter = 0;
  662.  
  663.                 // Check emitter states.
  664.                 for (auto& e : m_Emitters)
  665.                 {
  666.                         if (bCollectCounts)
  667.                                 e.GetAndClearCounts(m_GlobalCounts);
  668.  
  669.                         e.SetUpdateParticlesJobState(NULL);
  670.  
  671.                         bool bRenderedLastFrame = e.TimeNotRendered() == 0.f && e.GetAge() > 0.f;
  672.  
  673.                         e.Update();
  674.  
  675.                         if (e.IsActive())
  676.                         {
  677.                                 // Has particles
  678.                                 if (e.GetEnvFlags() & EFF_ANY)
  679.                                         e.UpdateEffects();
  680.                                 if (e.GetEnvFlags() & REN_ANY)
  681.                                         e.Register(true);
  682.                                 nActiveEmitter++;
  683.                                 if (bRenderedLastFrame)
  684.                                         e.AddUpdateParticlesJob();
  685.                         }
  686.                         else if (e.IsAlive())
  687.                         {
  688.                                 // Dormant
  689.                                 if (e.GetEnvFlags() & REN_ANY)
  690.                                         e.Reset();
  691.                         }
  692.                         else
  693.                         {
  694.                                 // Dead
  695.                                 EraseEmitter(&e);
  696.                         }
  697.                 }
  698.         }
  699. }
  700.  
  701. //////////////////////////////////////////////////////////////////////////
  702. CParticleEmitter* CParticleManager::CreateEmitter(const ParticleLoc& loc, const IParticleEffect* pEffect, const SpawnParams* pSpawnParams)
  703. {
  704.         PARTICLE_LIGHT_PROFILER();
  705.         FUNCTION_PROFILER(GetISystem(), PROFILE_PARTICLE);
  706.  
  707.         if (!pEffect)
  708.                 return NULL;
  709.  
  710.         void* pAlloc;
  711.         if (static_cast<const CParticleEffect*>(pEffect)->GetEnvironFlags(true) & EFF_FORCE)
  712.                 // Place emitters that create forces first in the list, so they are updated first.
  713.                 pAlloc = m_Emitters.push_front_new();
  714.         else
  715.                 pAlloc = m_Emitters.push_back_new();
  716.         if (!pAlloc)
  717.                 return NULL;
  718.  
  719.         CParticleEmitter* pEmitter = new(pAlloc) CParticleEmitter(pEffect, loc, pSpawnParams);
  720.         pEmitter->IParticleEmitter::AddRef();
  721.  
  722.         for (auto& pListener : m_ListenersList)
  723.         {
  724.                 pListener->OnCreateEmitter(pEmitter);
  725.         }
  726.         return pEmitter;
  727. }
  728.  
  729. IParticleEmitter* CParticleManager::CreateEmitter(const ParticleLoc& loc, const ParticleParams& Params, const SpawnParams* pSpawnParams)
  730. {
  731.         PARTICLE_LIGHT_PROFILER();
  732.         FUNCTION_PROFILER(GetISystem(), PROFILE_PARTICLE);
  733.  
  734.         if (!m_bEnabled)
  735.                 return NULL;
  736.  
  737.         // Create temporary effect for custom params.
  738.         CParticleEffect* pEffect = new CParticleEffect(Params);
  739.  
  740.         IParticleEmitter* pEmitter = CreateEmitter(loc, pEffect, pSpawnParams);
  741.  
  742.         if (!pEmitter)
  743.         {
  744.                 // If the emitter failed to be allocated, the effect will leak (as it won't be bound to the emitter). Free it.
  745.                 pEffect->Release();
  746.         }
  747.  
  748.         return pEmitter;
  749. }
  750.  
  751. //////////////////////////////////////////////////////////////////////////
  752. void CParticleManager::EraseEmitter(CParticleEmitter* pEmitter)
  753. {
  754.         if (pEmitter)
  755.         {
  756.                 // Free resources.
  757.                 pEmitter->Reset();
  758.  
  759.                 if (pEmitter->Unique() == 1 && !pEmitter->m_pOcNode)
  760.                 {
  761.                         // Free object itself if no other refs.
  762.                         for (auto& pListener : m_ListenersList)
  763.                         {
  764.                                 pListener->OnDeleteEmitter(pEmitter);
  765.                         }
  766.                         m_Emitters.erase(pEmitter);
  767.                 }
  768.         }
  769. }
  770.  
  771. void CParticleManager::DeleteEmitter(IParticleEmitter* pEmitter)
  772. {
  773.         if (pEmitter)
  774.                 pEmitter->Kill();
  775. }
  776.  
  777. void CParticleManager::DeleteEmitters(FEmitterFilter filter)
  778. {
  779.         for (auto& e : m_Emitters)
  780.         {
  781.                 if (filter(e))
  782.                         e.Kill();
  783.         }
  784. }
  785.  
  786. void CParticleManager::UpdateEmitters(IParticleEffect* pEffect)
  787. {
  788.         // Update all emitters with this effect tree.
  789.         for (auto& e : m_Emitters)
  790.         {
  791.                 for (IParticleEffect* pTest = pEffect; pTest; pTest = pTest->GetParent())
  792.                 {
  793.                         if (e.GetEffect() == pTest)
  794.                         {
  795.                                 e.RefreshEffect();
  796.                                 break;
  797.                         }
  798.                 }
  799.         }
  800. }
  801.  
  802. void CParticleManager::ReloadAllEffects()
  803. {
  804.         // Reload all current effects
  805.         for (auto& me : m_Effects)
  806.         {
  807.                 me.second->Reload(true);
  808.         }
  809.  
  810.         // Update all emitters.
  811.         for (auto& e : m_Emitters)
  812.         {
  813.                 e.RefreshEffect();
  814.         }
  815. }
  816.  
  817. bool CParticleManager::CanAccessFiles(cstr sObject, cstr sSource) const
  818. {
  819.         if (!gEnv->pCryPak->CheckFileAccessDisabled(0, 0))
  820.                 return true;
  821.  
  822.         // Check file access override.
  823.         if (GetCVars()->e_ParticlesAllowRuntimeLoad)
  824.                 return true;
  825.  
  826.         if (!sSource)
  827.                 sSource = "";
  828.         Warning("Particle asset read attempted at runtime: %s%s%s",
  829.                 sObject, (*sSource ? " from " : ""), sSource);
  830.  
  831.         return false;
  832. }
  833.  
  834. void CParticleManager::UpdateEngineData()
  835. {
  836.         m_nAllowedEnvironmentFlags = ~0;
  837.  
  838.         if (GetCVars())
  839.         {
  840.                 // Update allowed particle features.
  841.                 if (GetCVars()->e_ParticlesObjectCollisions < 2)
  842.                 {
  843.                         m_nAllowedEnvironmentFlags &= ~ENV_DYNAMIC_ENT;
  844.                         if (GetCVars()->e_ParticlesObjectCollisions < 1)
  845.                                 m_nAllowedEnvironmentFlags &= ~ENV_STATIC_ENT;
  846.                 }
  847.                 if (!GetCVars()->e_ParticlesLights || !GetCVars()->e_DynamicLights)
  848.                         m_nAllowedEnvironmentFlags &= ~REN_LIGHTS;
  849.         }
  850.  
  851.         // Render object flags, using cvar convention of 1 = allowed, 2 = forced.
  852.         m_RenderFlags.Clear();
  853.         m_RenderFlags.SetState(GetCVars()->e_ParticlesAnimBlend - 1, OS_ANIM_BLEND);
  854.         m_RenderFlags.SetState(GetCVars()->e_ParticlesMotionBlur - 1, FOB_MOTION_BLUR);
  855.         m_RenderFlags.SetState(GetCVars()->e_ParticlesShadows - 1, FOB_INSHADOW);
  856.         m_RenderFlags.SetState(GetCVars()->e_ParticlesSoftIntersect - 1, FOB_SOFT_PARTICLE);
  857.  
  858.         if (GetRenderer())
  859.         {
  860.                 bool bParticleTesselation = false;
  861.                 GetRenderer()->EF_Query(EFQ_ParticlesTessellation, bParticleTesselation);
  862.                 m_RenderFlags.SetState(int(bParticleTesselation) - 1, FOB_ALLOW_TESSELLATION);
  863.         }
  864.  
  865.         if (m_pLastDefaultParams != &GetDefaultParams())
  866.         {
  867.                 // Default effect or config spec changed.
  868.                 m_pLastDefaultParams = &GetDefaultParams();
  869.                 ReloadAllEffects();
  870.         }
  871. }
  872.  
  873. void CParticleManager::OnVisAreaDeleted(IVisArea* pVisArea)
  874. {
  875.         for (auto& e : m_Emitters)
  876.                 e.OnVisAreaDeleted(pVisArea);
  877. }
  878.  
  879. IParticleEmitter* CParticleManager::SerializeEmitter(TSerialize ser, IParticleEmitter* pIEmitter)
  880. {
  881.         ser.BeginGroup("Emitter");
  882.  
  883.         string sEffect;
  884.         QuatTS qLoc;
  885.  
  886.         CParticleEmitter* pEmitter = static_cast<CParticleEmitter*>(pIEmitter);
  887.         if (pEmitter)
  888.         {
  889.                 if (const IParticleEffect* pEffect = pEmitter->GetEffect())
  890.                         sEffect = pEffect->GetName();
  891.                 qLoc = pEmitter->GetLocation();
  892.         }
  893.  
  894.         ser.Value("Effect", sEffect);
  895.  
  896.         ser.Value("Pos", qLoc.t);
  897.         ser.Value("Rot", qLoc.q);
  898.         ser.Value("Scale", qLoc.s);
  899.  
  900.         if (ser.IsReading())
  901.         {
  902.                 if (IParticleEffect* pEffect = FindEffect(sEffect))
  903.                 {
  904.                         if (!pEmitter)
  905.                         {
  906.                                 pEmitter = CreateEmitter(qLoc, pEffect);
  907.                         }
  908.                         else
  909.                         {
  910.                                 pEmitter->SetLocation(qLoc);
  911.                                 pEmitter->SetEffect(pEffect);
  912.                         }
  913.                 }
  914.         }
  915.  
  916.         if (pEmitter)
  917.         {
  918.                 pEmitter->SerializeState(ser);
  919.         }
  920.  
  921.         ser.EndGroup();
  922.  
  923.         return pEmitter;
  924. }
  925.  
  926. //////////////////////////////////////////////////////////////////////////
  927. class CLibPathIterator
  928. {
  929. public:
  930.  
  931.         CLibPathIterator(cstr sLevelPath = "")
  932.                 : sPath(*sLevelPath ? sLevelPath : LEVEL_PATH), bDone(false)
  933.         {}
  934.         operator bool() const
  935.         { return !bDone; }
  936.         const string& operator*() const
  937.         { return sPath; }
  938.  
  939.         void operator++()
  940.         {
  941.                 if (sPath.empty())
  942.                         bDone = true;
  943.                 else
  944.                         sPath = PathUtil::GetParentDirectory(sPath);
  945.         }
  946.  
  947. protected:
  948.         string sPath;
  949.         bool   bDone;
  950. };
  951.  
  952. //////////////////////////////////////////////////////////////////////////
  953. bool CParticleManager::LoadPreloadLibList(const cstr filename, const bool bLoadResources)
  954. {
  955.         if (!m_bEnabled)
  956.                 return false;
  957.  
  958.         int nCount = 0;
  959.         CCryFile file;
  960.         if (file.Open(filename, "r"))
  961.         {
  962.                 const size_t nLen = file.GetLength();
  963.                 string sAllText;
  964.                 if (nLen > 0)
  965.                 {
  966.                         std::vector<char> buffer;
  967.                         buffer.resize(nLen, '\n');
  968.                         file.ReadRaw(&buffer[0], nLen);
  969.                         sAllText.assign(&buffer[0], nLen);
  970.                 }
  971.  
  972.                 int pos = 0;
  973.                 string sLine;
  974.                 while (!(sLine = sAllText.Tokenize("\r\n", pos)).empty())
  975.                 {
  976.                         if (LoadLibrary(sLine, NULL, bLoadResources))
  977.                         {
  978.                                 nCount++;
  979.                         }
  980.                 }
  981.         }
  982.         return nCount > 0;
  983. }
  984.  
  985. //////////////////////////////////////////////////////////////////////////
  986. bool CParticleManager::LoadLibrary(cstr sParticlesLibrary, cstr sParticlesLibraryFile, bool bLoadResources)
  987. {
  988.         if (!m_bEnabled)
  989.                 return false;
  990.  
  991.         if (m_LoadedLibs[sParticlesLibrary])
  992.         {
  993.                 // Already loaded.
  994.                 if (bLoadResources && CanAccessFiles(sParticlesLibrary))
  995.                 {
  996.                         // Iterate all fx in lib and load their resources.
  997.                         stack_string sPrefix = sParticlesLibrary;
  998.                         sPrefix += ".";
  999.                         for (auto& me : m_Effects)
  1000.                         {
  1001.                                 CParticleEffect* pEffect = me.second;
  1002.                                 if (pEffect && strnicmp(pEffect->GetName(), sPrefix, sPrefix.size()) == 0)
  1003.                                 {
  1004.                                         pEffect->LoadResources(true);
  1005.                                 }
  1006.                         }
  1007.                 }
  1008.                 return true;
  1009.         }
  1010.  
  1011.         if (!CanAccessFiles(sParticlesLibrary))
  1012.                 return false;
  1013.  
  1014.         if (strchr(sParticlesLibrary, '*'))
  1015.         {
  1016.                 // Wildcard load.
  1017.                 if (!sParticlesLibraryFile)
  1018.                 {
  1019.                         // Load libs from level-local and global paths.
  1020.                         int nCount = 0;
  1021.                         for (CLibPathIterator path(Get3DEngine()->GetLevelFilePath("")); path; ++path)
  1022.                                 nCount += LoadLibrary(sParticlesLibrary, PathUtil::Make(*path, EFFECTS_SUBPATH), bLoadResources);
  1023.                         return nCount > 0;
  1024.                 }
  1025.                 else
  1026.                 {
  1027.                         // Load from specified path.
  1028.                         string sLibPath = PathUtil::Make(sParticlesLibraryFile, sParticlesLibrary, "xml");
  1029.                         ICryPak* pack = gEnv->pCryPak;
  1030.                         _finddata_t fd;
  1031.                         intptr_t handle = pack->FindFirst(sLibPath, &fd);
  1032.                         int nCount = 0;
  1033.                         if (handle >= 0)
  1034.                         {
  1035.                                 do
  1036.                                 {
  1037.                                         if (LoadLibrary(PathUtil::GetFileName(fd.name), PathUtil::Make(sParticlesLibraryFile, fd.name), bLoadResources))
  1038.                                                 nCount++;
  1039.                                 }
  1040.                                 while (pack->FindNext(handle, &fd) >= 0);
  1041.                                 pack->FindClose(handle);
  1042.                         }
  1043.                         return nCount > 0;
  1044.                 }
  1045.         }
  1046.         else if (sParticlesLibrary[0] == '@')
  1047.         {
  1048.                 // first we try to load the level specific levelSpecificFile - if it exists, the libraries it lists are added
  1049.                 // to those from the default levelSpecificFile
  1050.                 string sFilename = PathUtil::Make(sParticlesLibraryFile, sParticlesLibrary + 1, "txt");
  1051.                 bool levelSpecificLoaded = LoadPreloadLibList(sFilename, bLoadResources);
  1052.  
  1053.                 // also load the default package
  1054.                 sFilename = PathUtil::Make(EFFECTS_SUBPATH, sParticlesLibrary + 1, "txt");
  1055.                 bool globalFileLoaded = LoadPreloadLibList(sFilename, bLoadResources);
  1056.                 return globalFileLoaded || levelSpecificLoaded;
  1057.         }
  1058.         else
  1059.         {
  1060.                 XmlNodeRef libNode;
  1061.                 if (sParticlesLibraryFile)
  1062.                 {
  1063.                         // Load from specified location.
  1064.                         libNode = GetISystem()->LoadXmlFromFile(sParticlesLibraryFile);
  1065.                 }
  1066.                 else
  1067.                 {
  1068.                         libNode = ReadLibrary(sParticlesLibrary);
  1069.                 }
  1070.  
  1071.                 if (!libNode)
  1072.                 {
  1073.                         return false;
  1074.                 }
  1075.  
  1076.                 LoadLibrary(sParticlesLibrary, libNode, bLoadResources);
  1077.         }
  1078.         return true;
  1079. }
  1080.  
  1081. XmlNodeRef CParticleManager::ReadLibrary(cstr sParticlesLibrary)
  1082. {
  1083.         MEMSTAT_CONTEXT(EMemStatContextTypes::MSC_Other, 0, "ParticleLibraries");
  1084.         MEMSTAT_CONTEXT_FMT(EMemStatContextTypes::MSC_ParticleLibrary, 0, "Particle lib (%s)", sParticlesLibrary);
  1085.  
  1086.         string sLibSubPath = PathUtil::Make(EFFECTS_SUBPATH, sParticlesLibrary, "xml");
  1087.         if (GetCVars()->e_ParticlesUseLevelSpecificLibs)
  1088.         {
  1089.                 // Look for library in level-specific, then general locations.
  1090.                 for (CLibPathIterator path(Get3DEngine()->GetLevelFilePath("")); path; ++path)
  1091.                 {
  1092.                         if (XmlNodeRef nodeLib = GetISystem()->LoadXmlFromFile(PathUtil::Make(*path, sLibSubPath)))
  1093.                                 return nodeLib;
  1094.                 }
  1095.                 return NULL;
  1096.         }
  1097.         else
  1098.         {
  1099.                 return GetISystem()->LoadXmlFromFile(sLibSubPath);
  1100.         }
  1101. }
  1102.  
  1103. static IXmlNode* FindEffectNode(IXmlNode* nodeParent, const stack_string& sEffectName)
  1104. {
  1105.         for (int i = 0; i < nodeParent->getChildCount(); i++)
  1106.         {
  1107.                 IXmlNode* node = nodeParent->getChild(i);
  1108.                 if (node->isTag("Particles"))
  1109.                 {
  1110.                         cstr sNodeName = node->getAttr("Name");
  1111.                         if (sEffectName.compareNoCase(sNodeName) == 0)
  1112.                                 return node;
  1113.                         size_t nNodeNameLen = strlen(sNodeName);
  1114.                         if (sEffectName.length() > nNodeNameLen && sEffectName[nNodeNameLen] == '.' && strncmp(sEffectName, sNodeName, nNodeNameLen) == 0)
  1115.                         {
  1116.                                 // Search sub-effects
  1117.                                 if (XmlNodeRef childsNode = node->findChild("Childs"))
  1118.                                 {
  1119.                                         stack_string sChildName = sEffectName.substr(nNodeNameLen + 1);
  1120.                                         if (IXmlNode* found = FindEffectNode(childsNode, sChildName))
  1121.                                                 return found;
  1122.                                 }
  1123.                         }
  1124.                 }
  1125.         }
  1126.         return NULL;
  1127. }
  1128.  
  1129. XmlNodeRef CParticleManager::ReadEffectNode(cstr sEffectName)
  1130. {
  1131.         // Read XML node for this effect from libraries
  1132.         if (cstr sDot = strchr(sEffectName, '.'))
  1133.         {
  1134.                 stack_string sLibraryName(sEffectName, sDot);
  1135.                 if (XmlNodeRef nodeLib = ReadLibrary(sLibraryName))
  1136.                 {
  1137.                         cstr sBaseName = sDot + 1;
  1138.                         if (IXmlNode* found = FindEffectNode(nodeLib, sBaseName))
  1139.                                 return found;
  1140.                 }
  1141.         }
  1142.  
  1143.         return NULL;
  1144. }
  1145.  
  1146. bool CParticleManager::LoadLibrary(cstr sParticlesLibrary, XmlNodeRef& libNode, bool bLoadResources)
  1147. {
  1148.         if (!m_bEnabled)
  1149.                 return false;
  1150.  
  1151.         MEMSTAT_CONTEXT(EMemStatContextTypes::MSC_Other, 0, "ParticleLibraries");
  1152.         MEMSTAT_CONTEXT_FMT(EMemStatContextTypes::MSC_ParticleLibrary, 0, "Particle lib (%s)", sParticlesLibrary);
  1153.  
  1154.         CRY_DEFINE_ASSET_SCOPE("ParticleLibrary", sParticlesLibrary);
  1155.  
  1156.         m_LoadedLibs[sParticlesLibrary] = libNode;
  1157.  
  1158.         // Load special defaults effects, if created (no warning otherwise).
  1159.         if (!m_pDefaultEffect && stricmp(sParticlesLibrary, "System") != 0 && gEnv->pCryPak->IsFileExist(EFFECTS_SUBPATH "System.xml"))
  1160.                 SetDefaultEffect("System.Default");
  1161.  
  1162.         // Load all nodes from the particle libaray.
  1163.         for (int i = 0, count = libNode->getChildCount(); i < count; i++)
  1164.         {
  1165.                 XmlNodeRef effectNode = libNode->getChild(i);
  1166.                 if (effectNode->isTag("Particles"))
  1167.                 {
  1168.                         string sEffectName = sParticlesLibrary;
  1169.                         sEffectName += ".";
  1170.                         sEffectName += effectNode->getAttr("Name");
  1171.  
  1172.                         // Load the full effect.
  1173.                         LoadEffect(sEffectName, effectNode, bLoadResources);
  1174.                 }
  1175.         }
  1176.         return true;
  1177. }
  1178.  
  1179. IParticleEffect* CParticleManager::LoadEffect(cstr sEffectName, XmlNodeRef& effectNode, bool bLoadResources, const cstr sSource)
  1180. {
  1181.         if (!m_bEnabled)
  1182.                 return NULL;
  1183.  
  1184.         MEMSTAT_CONTEXT_FMT(EMemStatContextTypes::MSC_ParticleEffect, 0, "%s", sEffectName);
  1185.  
  1186.         CParticleEffect* pEffect = FindLoadedEffect(sEffectName);
  1187.         if (!pEffect)
  1188.         {
  1189.                 pEffect = new CParticleEffect(sEffectName);
  1190.                 pEffect->Serialize(effectNode, true, true);
  1191.                 m_Effects[pEffect->GetName()] = pEffect;
  1192.         }
  1193.  
  1194.         if (bLoadResources)
  1195.                 pEffect->LoadResources(true, sSource);
  1196.  
  1197.         return pEffect;
  1198. }
  1199.  
  1200. void CParticleManager::ClearCachedLibraries()
  1201. {
  1202.         if (GetCVars()->e_ParticlesAllowRuntimeLoad)
  1203.                 return;
  1204.  
  1205.         if (m_LoadedLibs.empty())
  1206.                 return;
  1207.  
  1208.         if (GetCVars()->e_ParticlesDebug & AlphaBit('m'))
  1209.                 PrintParticleMemory();
  1210.  
  1211.         for (TEffectsList::iterator it = m_Effects.begin(); it != m_Effects.end(); )
  1212.         {
  1213.                 // Purge all unused effects.
  1214.                 CParticleEffect* pEffect = it->second;
  1215.                 const bool fxInUse = !pEffect->Unique() || pEffect->ResourcesLoaded(true);
  1216.                 if (!fxInUse)
  1217.                         it = m_Effects.erase(it);
  1218.                 else
  1219.                         ++it;
  1220.         }
  1221.         m_LoadedLibs.clear();
  1222.  
  1223.         if ((GetCVars()->e_ParticlesDebug & AlphaBit('m')) || GetCVars()->e_ParticlesDumpMemoryAfterMapLoad)
  1224.                 PrintParticleMemory();
  1225. }
  1226.  
  1227. IParticleEffectIteratorPtr CParticleManager::GetEffectIterator()
  1228. {
  1229.         return new CParticleEffectIterator(this);
  1230. }
  1231.  
  1232. void CParticleManager::RenderDebugInfo()
  1233. {
  1234.         bool bRefractivePartialResolveDebugView = false;
  1235. #if REFRACTION_PARTIAL_RESOLVE_DEBUG_VIEWS
  1236.         static ICVar* pRefractionPartialResolvesDebugCVar = gEnv->pConsole->GetCVar("r_RefractionPartialResolvesDebug");
  1237.         if (pRefractionPartialResolvesDebugCVar && pRefractionPartialResolvesDebugCVar->GetIVal() == eRPR_DEBUG_VIEW_3D_BOUNDS)
  1238.         {
  1239.                 bRefractivePartialResolveDebugView = true;
  1240.         }
  1241. #endif
  1242.         if ((GetCVars()->e_ParticlesDebug & AlphaBit('b')) || gEnv->IsEditing() || bRefractivePartialResolveDebugView)
  1243.         {
  1244.                 // Debug particle BBs.
  1245.                 FUNCTION_PROFILER(GetISystem(), PROFILE_PARTICLE);
  1246.                 for (auto& e : m_Emitters)
  1247.                         if (!e.GetSpawnParams().bNowhere)
  1248.                                 e.RenderDebugInfo();
  1249.         }
  1250. }
  1251.  
  1252. #ifdef bEVENT_TIMINGS
  1253.  
  1254. int CParticleManager::AddEventTiming(cstr sEvent, const CParticleContainer* pCont)
  1255. {
  1256.         if (!m_aEvents.size() && *sEvent != '*')
  1257.                 return -1;
  1258.  
  1259.         SEventTiming* pEvent = m_aEvents.push_back();
  1260.         pEvent->nContainerId = (uint32)pCont;
  1261.         pEvent->pEffect = pCont ? pCont->GetEffect() : 0;
  1262.         pEvent->nThread = CryGetCurrentThreadId();
  1263.         pEvent->sEvent = sEvent;
  1264.         pEvent->timeStart = GetParticleTimer()->GetAsyncCurTime();
  1265.  
  1266.         return m_aEvents.size() - 1;
  1267. }
  1268.  
  1269. #endif
  1270.  
  1271. //////////////////////////////////////////////////////////////////////////
  1272. void CParticleManager::Serialize(TSerialize ser)
  1273. {
  1274.         ser.BeginGroup("ParticleEmitters");
  1275.  
  1276.         if (ser.IsWriting())
  1277.         {
  1278.                 ListEmitters("before save");
  1279.  
  1280.                 int nCount = 0;
  1281.  
  1282.                 for (auto& e : m_Emitters)
  1283.                 {
  1284.                         if (e.NeedSerialize())
  1285.                         {
  1286.                                 SerializeEmitter(ser, &e);
  1287.                                 ++nCount;
  1288.                         }
  1289.                 }
  1290.                 ser.Value("Emitters", nCount);
  1291.         }
  1292.         else
  1293.         {
  1294.                 ListEmitters("before load");
  1295.  
  1296.                 // Clean up existing emitters.
  1297.                 for (auto& e : m_Emitters)
  1298.                 {
  1299.                         if (e.IsIndependent())
  1300.                         {
  1301.                                 // Would be serialized.
  1302.                                 EraseEmitter(&e);
  1303.                         }
  1304.                         else if (!e.IsAlive())
  1305.                         {
  1306.                                 // No longer exists at new time.
  1307.                                 EraseEmitter(&e);
  1308.                         }
  1309.                 }
  1310.  
  1311.                 int nCount = 0;
  1312.                 ser.Value("Emitters", nCount);
  1313.                 while (nCount-- > 0)
  1314.                 {
  1315.                         SerializeEmitter(ser);
  1316.                 }
  1317.         }
  1318.         ser.EndGroup();
  1319. }
  1320.  
  1321. void CParticleManager::PostSerialize(bool bReading)
  1322. {
  1323.         if (bReading)
  1324.                 ListEmitters("after load");
  1325. }
  1326.  
  1327. void CParticleManager::ListEmitters(cstr sDesc, bool bForce)
  1328. {
  1329.         if (bForce || GetCVars()->e_ParticlesDebug & AlphaBit('l'))
  1330.         {
  1331.                 // Count emitters.
  1332.                 int anEmitters[3] = { 0 };
  1333.  
  1334.                 // Log summary, and state of each emitter.
  1335.                 CryLog("Emitters %s: time %.3f", sDesc, GetParticleTimer()->GetFrameStartTime().GetSeconds());
  1336.                 for (const auto& e : m_Emitters)
  1337.                 {
  1338.                         if (e.IsActive())
  1339.                                 anEmitters[0]++;
  1340.                         else if (e.IsAlive())
  1341.                                 anEmitters[1]++;
  1342.                         else
  1343.                                 anEmitters[2]++;
  1344.                         CryLog(" %s", e.GetDebugString('s').c_str());
  1345.                 }
  1346.                 CryLog("Total: %d active, %d dormant, %d dead",
  1347.                        anEmitters[0], anEmitters[1], anEmitters[2]);
  1348.         }
  1349. }
  1350.  
  1351. void CParticleManager::ListEffects()
  1352. {
  1353.         // Collect all container stats, sum into effects map.
  1354.         TEffectStats mapEffectStats;
  1355.         CollectEffectStats(mapEffectStats, &SParticleCounts::ParticlesActive);
  1356.  
  1357.         // Header for CSV-formatted effect list.
  1358.         CryLogAlways(
  1359.           "Effect, "
  1360.           "Ems ren, Ems act, Ems all, "
  1361.           "Parts ren, Parts act, Parts all, "
  1362.           "Fill ren, Fill act, "
  1363.           "Stat vol, Dyn vol, Err vol,"
  1364.           "Coll test, Coll hit, Clip, "
  1365.           "Reiter, Reject"
  1366.           );
  1367.  
  1368.         for (auto& me : mapEffectStats)
  1369.         {
  1370.                 SParticleCounts const& counts = me.second;
  1371.                 float fPixToScreen = 1.f / ((float)GetRenderer()->GetWidth() * (float)GetRenderer()->GetHeight());
  1372.                 CryLogAlways(
  1373.                   "%s, "
  1374.                   "%.0f, %.0f, %.0f, "
  1375.                   "%.0f, %.0f, %.0f, "
  1376.                   "%.3f, %.3f, "
  1377.                   "%.0f, %.0f, %.2f, "
  1378.                   "%.2f, %.2f, %.2f, "
  1379.                   "%.0f, %.2f",
  1380.                   me.first ? me.first->GetFullName().c_str() : "TOTAL",
  1381.                   counts.EmittersRendered, counts.EmittersActive, counts.EmittersAlloc,
  1382.                   counts.ParticlesRendered, counts.ParticlesActive, counts.ParticlesAlloc,
  1383.                   counts.PixelsRendered * fPixToScreen, counts.PixelsProcessed * fPixToScreen,
  1384.                   counts.StaticBoundsVolume, counts.DynamicBoundsVolume, counts.ErrorBoundsVolume,
  1385.                   counts.ParticlesCollideTest, counts.ParticlesCollideHit, counts.ParticlesClip,
  1386.                   counts.ParticlesReiterate, counts.ParticlesReject
  1387.                   );
  1388.         }
  1389. }
  1390.  
  1391. void CParticleManager::CreatePerfHUDWidget()
  1392. {
  1393.         if (!m_pWidget)
  1394.         {
  1395.                 if (ICryPerfHUD* pPerfHUD = gEnv->pSystem->GetPerfHUD())
  1396.                 {
  1397.                         if (IMiniCtrl* pRenderMenu = pPerfHUD->GetMenu("Rendering"))
  1398.                         {
  1399.                                 m_pWidget = new CParticleWidget(pRenderMenu, pPerfHUD, this);
  1400.                         }
  1401.                 }
  1402.         }
  1403. }
  1404.  
  1405. void CParticleManager::DumpAndResetVertexIndexPoolUsage()
  1406. {
  1407. #if defined(PARTICLE_COLLECT_VERT_IND_POOL_USAGE)
  1408.         // don't show the out of vertex data at all if the user wants it
  1409.         if (GetCVars()->e_ParticlesProfile == 2)
  1410.                 return;
  1411.  
  1412.         const stl::SPoolMemoryUsage memParticles = ParticleObjectAllocator().GetTotalMemory();
  1413.  
  1414.         bool bOutOfMemory = m_bOutOfVertexIndexPoolMemory || ((GetCVars()->e_ParticlesPoolSize * 1024) - memParticles.nUsed) == 0;
  1415.         // dump information if we are out of memory or if dumping is enabled
  1416.         if (bOutOfMemory || GetCVars()->e_ParticlesProfile == 1)
  1417.         {
  1418.                 float fTextSideOffset = 10.0f;
  1419.                 float fTopOffset = 3.0f;
  1420.                 float fTextSize = 1.4f;
  1421.                 float fTextColorOutOfMemory[] = { 1.0f, 0.0f, 0.0f, 1.0f };
  1422.                 float fTextColor[] = { 1.0f, 1.0f, 1.0f, 1.0f };
  1423.                 float* pColor = bOutOfMemory ? fTextColorOutOfMemory : fTextColor;
  1424.                 float fScreenPix = (float)(GetRenderer()->GetWidth() * GetRenderer()->GetHeight());
  1425.  
  1426.                 SParticleCounts CurCounts;
  1427.                 m_pPartManager->GetCounts(CurCounts);
  1428.  
  1429.                 uint32 nParticleVertexBufferSize = 0;
  1430.                 uint32 nParticleIndexBufferSize = 0;
  1431.                 uint32 nMaxParticleContainer = 0;
  1432.                 gEnv->pRenderer->EF_Query(EFQ_GetParticleVertexBufferSize, nParticleVertexBufferSize);
  1433.                 gEnv->pRenderer->EF_Query(EFQ_GetParticleIndexBufferSize, nParticleIndexBufferSize);
  1434.                 gEnv->pRenderer->EF_Query(EFQ_GetMaxParticleContainer, nMaxParticleContainer);
  1435.  
  1436.                 IRenderAuxText::Draw2dLabel(fTextSideOffset, fTopOffset, fTextSize, pColor, false, "== Particle Profiler ==");
  1437.                 fTopOffset += 18.0f;
  1438.  
  1439.                 IRenderAuxText::Draw2dLabel(fTextSideOffset, fTopOffset, fTextSize, pColor, false, "- Particle Object Pool (Size %4d KB)",
  1440.                                              GetCVars()->e_ParticlesPoolSize);
  1441.                 fTopOffset += 18.0f;
  1442.  
  1443.                 IRenderAuxText::Draw2dLabel(fTextSideOffset, fTopOffset, fTextSize, pColor, false, "\tUsed %4u Max Used %4u Free %4u (KB)",
  1444.                                              uint(memParticles.nUsed >> 10), uint((memParticles.nUsed + memParticles.nPool)) >> 10, uint(memParticles.nFree() >> 10));
  1445.                 fTopOffset += 18.0f;
  1446.  
  1447.                 IRenderAuxText::Draw2dLabel(fTextSideOffset, fTopOffset, fTextSize, pColor, false, "- Particle VertexBuffer (Size %3d KB * 2)",
  1448.                                              nParticleVertexBufferSize / 1024);
  1449.                 fTopOffset += 18.0f;
  1450.  
  1451.                 IRenderAuxText::Draw2dLabel(fTextSideOffset, fTopOffset, fTextSize, pColor, false, "\tAvail. %3u Used %3u Free %3u",
  1452.                                              nParticleVertexBufferSize / (uint)sizeof(SVF_Particle), m_nRequieredVertexPoolMemory / (uint)sizeof(SVF_Particle),
  1453.                                              m_nRequieredVertexPoolMemory >= nParticleVertexBufferSize ? 0 : (nParticleVertexBufferSize - m_nRequieredVertexPoolMemory) / (uint)sizeof(SVF_Particle));
  1454.                 fTopOffset += 18.0f;
  1455.  
  1456.                 IRenderAuxText::Draw2dLabel(fTextSideOffset, fTopOffset, fTextSize, pColor, false, "- Particle IndexBuffer (Size %3d KB * 2)",
  1457.                                              nParticleIndexBufferSize / 1024);
  1458.                 fTopOffset += 18.0f;
  1459.  
  1460.                 IRenderAuxText::Draw2dLabel(fTextSideOffset, fTopOffset, fTextSize, pColor, false, "\tAvail. %3u Used %3u Free %3u",
  1461.                                              nParticleIndexBufferSize / (uint)sizeof(uint16), m_nRequieredIndexPoolMemory / (uint)sizeof(uint16),
  1462.                                              m_nRequieredIndexPoolMemory >= nParticleIndexBufferSize ? 0 : (nParticleIndexBufferSize - m_nRequieredIndexPoolMemory) / (uint)sizeof(uint16));
  1463.                 fTopOffset += 18.0f;
  1464.  
  1465.                 IRenderAuxText::Draw2dLabel(fTextSideOffset, fTopOffset, fTextSize, pColor, false, "- Render Stats");
  1466.                 fTopOffset += 18.0f;
  1467.  
  1468.                 /*
  1469.                     IRenderAuxText::Draw2dLabel( fTextSideOffset, fTopOffset, fTextSize, pColor, false, "\tContainer Rendered %3d (Limit %3d)",
  1470.                       m_nNumContainerToRender, nMaxParticleContainer);
  1471.                     fTopOffset += 18.0f;
  1472.  
  1473.                     IRenderAuxText::Draw2dLabel( fTextSideOffset, fTopOffset, fTextSize, pColor, false, "\tParticles Culled   %3d",
  1474.                       m_nParticlesCulled);
  1475.                     fTopOffset += 18.0f;
  1476.                  */
  1477.  
  1478.                 IRenderAuxText::Draw2dLabel(fTextSideOffset, fTopOffset, fTextSize, pColor, false, "- Fill Rate (Particle Pixels per Screen Pixel)");
  1479.                 fTopOffset += 18.0f;
  1480.  
  1481.                 IRenderAuxText::Draw2dLabel(fTextSideOffset, fTopOffset, fTextSize, pColor, false, "\tLimit    %3d", (int)GetCVars()->e_ParticlesMaxScreenFill);
  1482.                 fTopOffset += 18.0f;
  1483.  
  1484.                 IRenderAuxText::Draw2dLabel(fTextSideOffset, fTopOffset, fTextSize, pColor, false, "\tProcessed %3d", (int)(CurCounts.PixelsProcessed / fScreenPix));
  1485.                 fTopOffset += 18.0f;
  1486.  
  1487.                 IRenderAuxText::Draw2dLabel(fTextSideOffset, fTopOffset, fTextSize, pColor, false, "\tRendered  %3d", (int)(CurCounts.PixelsRendered / fScreenPix));
  1488.                 fTopOffset += 18.0f;
  1489.                 fTopOffset += 18.0f;
  1490.  
  1491.                 IRenderAuxText::Draw2dLabel(fTextSideOffset, fTopOffset, fTextSize, pColor, false, "- Top %d ParticleContainer in Vertex/Index Pool", nVertexIndexPoolUsageEntries);
  1492.                 fTopOffset += 18.0f;
  1493.  
  1494.                 for (int i = 0; i < nVertexIndexPoolUsageEntries; ++i)
  1495.                 {
  1496.                         if (m_arrVertexIndexPoolUsage[i].nVertexMemory == 0)
  1497.                                 break;
  1498.  
  1499.                         IRenderAuxText::Draw2dLabel(fTextSideOffset, fTopOffset, fTextSize, pColor, false, "Vert: %3d KB Ind: %3d KB Part: %3d %s", m_arrVertexIndexPoolUsage[i].nVertexMemory / 1024, m_arrVertexIndexPoolUsage[i].nIndexMemory / 1024, 0, m_arrVertexIndexPoolUsage[i].pContainerName);
  1500.                         fTopOffset += 18.0f;
  1501.                 }
  1502.         }
  1503.         memset(m_arrVertexIndexPoolUsage, 0, sizeof(m_arrVertexIndexPoolUsage));
  1504.         m_bOutOfVertexIndexPoolMemory = false;
  1505.         m_nRequieredVertexPoolMemory = 0;
  1506.         m_nRequieredIndexPoolMemory = 0;
  1507.         m_nRendererParticleContainer = 0;
  1508. #endif
  1509. }
  1510.  
  1511. //PerfHUD
  1512. CParticleWidget::CParticleWidget(IMiniCtrl* pParentMenu, ICryPerfHUD* pPerfHud, CParticleManager* pPartManager) : ICryPerfHUDWidget(eWidget_Particles)
  1513. {
  1514.         m_pPartMgr = pPartManager;
  1515.  
  1516.         m_pTable = pPerfHud->CreateTableMenuItem(pParentMenu, "Particles");
  1517.  
  1518.         pPerfHud->AddWidget(this);
  1519.  
  1520.         m_displayMode = PARTICLE_DISP_MODE_NONE;
  1521. }
  1522.  
  1523. CParticleWidget::~CParticleWidget()
  1524. {
  1525. }
  1526.  
  1527. void CParticleWidget::Enable(int mode)
  1528. {
  1529.         mode = min(mode, PARTICLE_DISP_MODE_NUM - 1);
  1530.         EPerfHUD_ParticleDisplayMode newMode = (EPerfHUD_ParticleDisplayMode)mode;
  1531.  
  1532.         if (m_displayMode != newMode)
  1533.         {
  1534.                 m_pTable->RemoveColumns();
  1535.                 m_pTable->AddColumn("Effect Name");
  1536.  
  1537.                 switch (newMode)
  1538.                 {
  1539.                 case PARTICLE_DISP_MODE_PARTICLE:
  1540.                 case PARTICLE_DISP_MODE_FILL:
  1541.                         m_pTable->AddColumn("P. Rendered");
  1542.                         m_pTable->AddColumn("P. Colliding");
  1543.                         m_pTable->AddColumn("P. Active");
  1544.                         m_pTable->AddColumn("P. Alloc");
  1545.                         m_pTable->AddColumn("P. Fill");
  1546.                         m_displayMode = newMode;
  1547.                         break;
  1548.  
  1549.                 case PARTICLE_DISP_MODE_EMITTER:
  1550.                         m_pTable->AddColumn("E. Rendered");
  1551.                         m_pTable->AddColumn("E. Colliding");
  1552.                         m_pTable->AddColumn("E. Active");
  1553.                         m_pTable->AddColumn("E. Alloc");
  1554.                         m_displayMode = newMode;
  1555.                         break;
  1556.  
  1557.                 default:
  1558.                         CryLogAlways("[Particle Stats] Attempting to set incorrect display mode set: %d", mode);
  1559.                         break;
  1560.                 }
  1561.         }
  1562.  
  1563.         m_pTable->Hide(false);
  1564. }
  1565.  
  1566. bool CParticleWidget::ShouldUpdate()
  1567. {
  1568.         return !m_pTable->IsHidden();
  1569. }
  1570.  
  1571. void CParticleWidget::Update()
  1572. {
  1573.         FUNCTION_PROFILER(GetISystem(), PROFILE_PARTICLE);
  1574.  
  1575.         m_pTable->ClearTable();
  1576.  
  1577.         ColorB col(255, 255, 255, 255);
  1578.  
  1579.         CParticleManager::TEffectStats mapEffectStats;
  1580.  
  1581.         switch (m_displayMode)
  1582.         {
  1583.         case PARTICLE_DISP_MODE_PARTICLE:
  1584.                 m_pPartMgr->CollectEffectStats(mapEffectStats, &SParticleCounts::ParticlesRendered);
  1585.                 break;
  1586.  
  1587.         case PARTICLE_DISP_MODE_FILL:
  1588.                 m_pPartMgr->CollectEffectStats(mapEffectStats, &SParticleCounts::PixelsRendered);
  1589.                 break;
  1590.  
  1591.         case PARTICLE_DISP_MODE_EMITTER:
  1592.                 m_pPartMgr->CollectEffectStats(mapEffectStats, &SParticleCounts::EmittersRendered);
  1593.                 break;
  1594.         }
  1595.  
  1596.         if (mapEffectStats[NULL].ParticlesAlloc)
  1597.         {
  1598.                 float fPixToScreen = 1.f / (gEnv->pRenderer->GetWidth() * gEnv->pRenderer->GetHeight());
  1599.                 for (auto& me : mapEffectStats)
  1600.                 {
  1601.                         m_pTable->AddData(0, col, "%s", me.first ? me.first->GetFullName().c_str() : "TOTAL");
  1602.  
  1603.                         SParticleCounts const& counts = me.second;
  1604.                         if (m_displayMode == PARTICLE_DISP_MODE_EMITTER)
  1605.                         {
  1606.                                 m_pTable->AddData(1, col, "%d", (int)counts.EmittersRendered);
  1607.                                 m_pTable->AddData(2, col, "%d", (int)counts.EmittersActive);
  1608.                                 m_pTable->AddData(3, col, "%d", (int)counts.EmittersAlloc);
  1609.                         }
  1610.                         else
  1611.                         {
  1612.                                 m_pTable->AddData(1, col, "%d", (int)counts.ParticlesRendered);
  1613.                                 m_pTable->AddData(2, col, "%d", (int)counts.ParticlesActive);
  1614.                                 m_pTable->AddData(3, col, "%d", (int)counts.ParticlesAlloc);
  1615.                                 m_pTable->AddData(4, col, "%.2f", counts.PixelsRendered * fPixToScreen);
  1616.                         }
  1617.                 }
  1618.         }
  1619. }
  1620.  
  1621. CParticleManager::SEffectsKey::SEffectsKey(const cstr& sName)
  1622. {
  1623.         stack_string lowerName(sName);
  1624.         CryStringUtils::toLowerInplace((char*)lowerName.c_str());
  1625.  
  1626.         IZLibCompressor* pZLib = GetISystem()->GetIZLibCompressor();
  1627.         assert(pZLib);
  1628.  
  1629.         SMD5Context context;
  1630.         pZLib->MD5Init(&context);
  1631.         pZLib->MD5Update(&context, (const char*)lowerName.c_str(), lowerName.size());
  1632.         pZLib->MD5Final(&context, c16);
  1633. }
  1634.  
  1635. //////////////////////////////////////////////////////////////////////////
  1636. uint32 CParticleManager::GetPhysAreaChangedProxy(CParticleEmitter* pEmitter, uint16 uPhysicsMask)
  1637. {
  1638.         size_t nIndex = ~0;
  1639.         SPhysAreaNodeProxy* proxy = ::new(m_physAreaChangedProxies.push_back_new(nIndex))SPhysAreaNodeProxy();
  1640.  
  1641.         proxy->pEmitter = pEmitter;
  1642.         proxy->uPhysicsMask = uPhysicsMask;
  1643.         proxy->bIsValid = true;
  1644.         proxy->bbox = pEmitter->GetBBox();
  1645.         return nIndex;
  1646. }
  1647.  
  1648. //////////////////////////////////////////////////////////////////////////
  1649. void CParticleManager::UpdatePhysAreaChangedProxy(CParticleEmitter* pEmitter, uint32 nProxyId, bool bValid)
  1650. {
  1651.         m_physAreaChangedProxies[nProxyId].bbox = pEmitter->GetBBox();
  1652.         m_physAreaChangedProxies[nProxyId].bIsValid = bValid;
  1653. }
  1654.  
  1655. //////////////////////////////////////////////////////////////////////////
  1656. void CParticleManager::CleanOldPhysAreaChangedProxies()
  1657. {
  1658.         // Ensure list is continues in memory
  1659.         m_physAreaChangedProxies.CoalesceMemory();
  1660.  
  1661.         const uint32 nSize = m_physAreaChangedProxies.size();
  1662.         if (nSize == 0)
  1663.                 return;
  1664.  
  1665.         SPhysAreaNodeProxy* pFrontIter = &m_physAreaChangedProxies[0];
  1666.         SPhysAreaNodeProxy* pBackIter = &m_physAreaChangedProxies[nSize - 1];
  1667.         const SPhysAreaNodeProxy* pHead = pFrontIter;
  1668.         uint32 nNumItemsToDelete = 0;
  1669.  
  1670.         // Move invalid nodes to the back of the array
  1671.         do
  1672.         {
  1673.                 while (pFrontIter->bIsValid && pFrontIter < pBackIter)
  1674.                 {
  1675.                         ++pFrontIter;
  1676.                 }
  1677.                 while (!pBackIter->bIsValid && pFrontIter < pBackIter)
  1678.                 {
  1679.                         --pBackIter;
  1680.                         ++nNumItemsToDelete;
  1681.                 }
  1682.  
  1683.                 if (pFrontIter < pBackIter)
  1684.                 {
  1685.                         // Replace invalid front element with back element
  1686.                         // Note: No need to swap because we cut the data from the array at the end anyway
  1687.                         memcpy(pFrontIter, pBackIter, sizeof(SPhysAreaNodeProxy));
  1688.                         pFrontIter->pEmitter->m_nPhysAreaChangedProxyId = (uint32)(pFrontIter - pHead);
  1689.                         pBackIter->bIsValid = false;
  1690.  
  1691.                         --pBackIter;
  1692.                         ++pFrontIter;
  1693.                         ++nNumItemsToDelete;
  1694.                 }
  1695.         }
  1696.         while (pFrontIter < pBackIter);
  1697.  
  1698.         // Cut off invalid elements
  1699.         m_physAreaChangedProxies.resize(nSize - nNumItemsToDelete);
  1700. }
  1701.  
  1702. void CParticleManager::AddUpdatedPhysArea(const SAreaChangeRecord& rec)
  1703. {
  1704.         // Merge with existing bb if close enough and same medium
  1705.         AUTO_LOCK(m_PhysAreaChangeLock);
  1706.         static const float fMERGE_THRESHOLD = 2.f;
  1707.         float fNewVolume = rec.boxAffected.GetVolume();
  1708.         for (uint i = 0; i < m_listPhysAreasChanged.size(); i++)
  1709.         {
  1710.                 if (m_listPhysAreasChanged[i].uPhysicsMask == rec.uPhysicsMask)
  1711.                 {
  1712.                         AABB bbUnion = rec.boxAffected;
  1713.                         bbUnion.Add(m_listPhysAreasChanged[i].boxAffected);
  1714.                         if (bbUnion.GetVolume() <= (fNewVolume + m_listPhysAreasChanged[i].boxAffected.GetVolume()) * fMERGE_THRESHOLD)
  1715.                         {
  1716.                                 m_listPhysAreasChanged[i].boxAffected = bbUnion;
  1717.                                 return;
  1718.                         }
  1719.                 }
  1720.         }
  1721.         m_listPhysAreasChanged.push_back(rec);
  1722. }
  1723.  
  1724. void CParticleManager::UpdatePhysAreasChanged()
  1725. {
  1726.         FUNCTION_PROFILER_3DENGINE;
  1727.         AUTO_LOCK(m_PhysAreaChangeLock);
  1728.         if (m_listPhysAreasChanged.empty())
  1729.                 return;
  1730.  
  1731.         // Check area against registered proxies
  1732.         int nSizeAreasChanged = (int)m_listPhysAreasChanged.size();
  1733.         int nSizeProxies = m_physAreaChangedProxies.size();
  1734.  
  1735.         // Access elements via [i] as the thread safe list does not always safe its element in a continues array
  1736.         for (int i = 0; i < nSizeProxies; ++i)
  1737.         {
  1738.                 const SPhysAreaNodeProxy& proxy = m_physAreaChangedProxies[i];
  1739.  
  1740.                 IF (!proxy.bIsValid, 0)
  1741.                 {
  1742.                         continue;
  1743.                 }
  1744.  
  1745.                 for (int j = 0; j < nSizeAreasChanged; ++j)
  1746.                 {
  1747.                         const SAreaChangeRecord& rec = m_listPhysAreasChanged[j];
  1748.                         if ((proxy.uPhysicsMask & rec.uPhysicsMask) && Overlap::AABB_AABB(proxy.bbox, rec.boxAffected))
  1749.                         {
  1750.                                 proxy.pEmitter->OnPhysAreaChange();
  1751.                                 break;
  1752.                         }
  1753.                 }
  1754.         }
  1755.  
  1756.         m_listPhysAreasChanged.resize(0);
  1757. }
  1758.  
downloadParticleManager.cpp Source code - Download CRYENGINE Source code
Related Source Codes/Software:
postal - 2017-06-11
reactide - Reactide is the first dedicated IDE for React web ... 2017-06-11
rkt - rkt is a pod-native container engine for Linux. It... 2017-06-11
uWebSockets - Tiny WebSockets https://for... 2017-06-11
realworld - TodoMVC for the RealWorld - Exemplary fullstack Me... 2017-06-11
CRYENGINE - CRYENGINE is a powerful real-time game development... 2017-06-11
goreplay - GoReplay is an open-source tool for capturing and ... 2017-06-10
pyenv - Simple Python version management 2017-06-10
redux-saga - An alternative side effect model for Redux apps ... 2017-06-10
angular-starter - 2017-06-10

 Back to top