BVB Source Codes

CRYENGINE Show ParticleEmitter.cpp Source code

Return Download CRYENGINE: download ParticleEmitter.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:   ParticleEmitter.cpp
  5. //  Created:     18/7/2003 by Timur.
  6. //  Modified:    17/3/2005 by Scott Peter
  7. //  Compilers:   Visual Studio.NET
  8. //  Description:
  9. // -------------------------------------------------------------------------
  10. //  History:
  11. //
  12. ////////////////////////////////////////////////////////////////////////////
  13.  
  14. #include "StdAfx.h"
  15. #include "ParticleEmitter.h"
  16. #include "ParticleContainer.h"
  17. #include "ParticleSubEmitter.h"
  18. #include <CryAnimation/ICryAnimation.h>
  19. #include "Particle.h"
  20. #include "FogVolumeRenderNode.h"
  21. #include <CryMemory/STLGlobalAllocator.h>
  22. #include <CryString/StringUtils.h>
  23.  
  24. #include <CryThreading/IJobManager_JobDelegator.h>
  25.  
  26. static const float fUNSEEN_EMITTER_RESET_TIME = 5.f;    // Max time to wait before freeing unseen emitter resources
  27. static const float fVELOCITY_SMOOTHING_TIME = 0.125f;   // Interval to smooth computed entity velocity
  28.  
  29. /*
  30.    Scheme for Emitter updating & bounding volume computation.
  31.  
  32.    Whenever possible, we update emitter particles only on viewing.
  33.    However, the bounding volume for all particles must be precomputed,
  34.    and passed to the renderer,  for visibility. Thus, whenever possible,
  35.    we precompute a Static Bounding Box, loosely estimating the possible
  36.    travel of all particles, without actually updating them.
  37.  
  38.    Currently, Dynamic BBs are computed for emitters that perform physics,
  39.    or are affected by non-uniform physical forces.
  40.  */
  41.  
  42. ///////////////////////////////////////////////////////////////////////////////////////////////
  43. void UpdateParticlesEmitter(CParticleEmitter* pEmitter)
  44. {
  45.         pEmitter->UpdateAllParticlesJob();
  46. }
  47.  
  48. DECLARE_JOB("Particles:Update", TUpdateParticlesJob, UpdateParticlesEmitter);
  49.  
  50. JobManager::SJobState* CParticleBatchDataManager::AddUpdateJob(CParticleEmitter* pEmitter)
  51. {
  52.         JobManager::SJobState* pJobState = GetJobState();
  53.  
  54.         TUpdateParticlesJob job(pEmitter);
  55.         job.RegisterJobState(pJobState);
  56.         job.Run();
  57.         return pJobState;
  58. }
  59.  
  60. ///////////////////////////////////////////////////////////////////////////////////////////////
  61. void CParticleEmitter::AddUpdateParticlesJob()
  62. {
  63.         FUNCTION_PROFILER(GetISystem(), PROFILE_PARTICLE);
  64.  
  65.         if (!GetCVars()->e_ParticlesThread)
  66.         {
  67.                 UpdateParticlesEmitter(this);
  68.                 return;
  69.         }
  70.  
  71.         // Queue background job for particle updates.
  72.         assert(!m_pUpdateParticlesJobState);
  73.  
  74.         m_pUpdateParticlesJobState = CParticleManager::Instance()->AddUpdateJob(this);
  75. }
  76.  
  77. void CParticleEmitter::UpdateAllParticlesJob()
  78. {
  79.         for (auto& c : m_Containers)
  80.         {
  81.                 if (c.NeedJobUpdate())
  82.                         c.UpdateParticles();
  83.                 else
  84.                         c.UpdateEmitters();
  85.         }
  86. }
  87.  
  88. void CParticleEmitter::SyncUpdateParticlesJob()
  89. {
  90.         if (m_pUpdateParticlesJobState)
  91.         {
  92.                 FUNCTION_PROFILER(gEnv->pSystem, PROFILE_PARTICLE);
  93.                 PARTICLE_LIGHT_SYNC_PROFILER();
  94.                 gEnv->GetJobManager()->WaitForJob(*m_pUpdateParticlesJobState);
  95.                 m_pUpdateParticlesJobState = NULL;
  96.         }
  97. }
  98.  
  99. //////////////////////////////////////////////////////////////////////////
  100. IMaterial* CParticleEmitter::GetMaterial(Vec3*) const
  101. {
  102.         if (m_pMaterial)
  103.                 return m_pMaterial;
  104.         if (!m_Containers.empty())
  105.                 return m_Containers.front().GetParams().pMaterial;
  106.         return NULL;
  107. }
  108.  
  109. float CParticleEmitter::GetMaxViewDist()
  110. {
  111.         // Max particles/radian, modified by emitter settings.
  112.         return CParticleManager::Instance()->GetMaxAngularDensity(gEnv->pSystem->GetViewCamera())
  113.                * m_fMaxParticleSize
  114.                * GetParticleScale()
  115.                * m_fViewDistRatio;
  116. }
  117.  
  118. bool CParticleEmitter::IsAlive() const
  119. {
  120.         if (m_fAge <= m_fDeathAge)
  121.                 return true;
  122.  
  123.         if (GetSpawnParams().fPulsePeriod > 0.f)
  124.                 // Between pulses.
  125.                 return true;
  126.  
  127.         if (m_fAge < 0.f)
  128.                 // Emitter paused.
  129.                 return true;
  130.  
  131.         // Natural death.
  132.         return false;
  133. }
  134.  
  135. void CParticleEmitter::UpdateState()
  136. {
  137.         FUNCTION_PROFILER(GetISystem(), PROFILE_PARTICLE);
  138.  
  139.         uint32 nEnvFlags = GetEnvFlags();
  140.         Vec3 vPos = GetLocation().t;
  141.  
  142.         bool bUpdateBounds = (nEnvFlags & REN_ANY) && (
  143.           m_bbWorld.IsReset() ||
  144.           (nEnvFlags & EFF_DYNAMIC_BOUNDS) ||
  145. #ifndef _RELEASE
  146.           (GetCVars()->e_ParticlesDebug & AlphaBit('d')) ||
  147. #endif
  148.           m_fAge <= m_fBoundsStableAge
  149.           );
  150.  
  151.         if (m_nEnvFlags & ENV_PHYS_AREA)
  152.         {
  153.                 bool bCreateAreaChangeProxy = m_nPhysAreaChangedProxyId == ~0;
  154.  
  155.                 IF (bCreateAreaChangeProxy || !(m_PhysEnviron.m_nNonUniformFlags & EFF_LOADED), 0)
  156.                 {
  157.                         // For initial bounds computation, query the physical environment at the origin.
  158.                         m_PhysEnviron.GetPhysAreas(CParticleManager::Instance()->GetPhysEnviron(), AABB(vPos),
  159.                                                    Get3DEngine()->GetVisAreaFromPos(vPos) != NULL, m_nEnvFlags, true, this);
  160.  
  161.                         // Get phys area changed proxy if not set yet
  162.                         if (bCreateAreaChangeProxy)
  163.                         {
  164.                                 uint16 uPhysicsMask = 0;
  165.                                 if (m_nEnvFlags & ENV_WATER)
  166.                                         uPhysicsMask |= Area_Water;
  167.                                 if (m_nEnvFlags & ENV_WIND)
  168.                                         uPhysicsMask |= Area_Air;
  169.                                 if (m_nEnvFlags & ENV_GRAVITY)
  170.                                         uPhysicsMask |= Area_Gravity;
  171.                                 m_nPhysAreaChangedProxyId = CParticleManager::Instance()->GetPhysAreaChangedProxy(this, uPhysicsMask);
  172.                         }
  173.  
  174.                         bUpdateBounds = true;
  175.                 }
  176.         }
  177.  
  178.         bool bUpdateState = (GetRndFlags()&ERF_HIDDEN)==0 && (bUpdateBounds || m_fAge >= m_fStateChangeAge);
  179.         if (bUpdateState)
  180.         {
  181.                 m_fStateChangeAge = fHUGE;
  182.  
  183.                 // Update states and bounds.
  184.                 if (!m_Containers.empty())
  185.                         UpdateContainers();
  186.                 else
  187.                         AllocEmitters();
  188.  
  189.                 if (bUpdateBounds && !m_SpawnParams.bNowhere)
  190.                 {
  191.                         // Re-query environment to include full bounds, and water volumes for clipping.
  192.                         m_VisEnviron.Update(GetLocation().t, m_bbWorld);
  193.                         m_PhysEnviron.GetPhysAreas(CParticleManager::Instance()->GetPhysEnviron(), m_bbWorld,
  194.                                                    m_VisEnviron.OriginIndoors(), m_nEnvFlags | ENV_WATER, true, this);
  195.                 }
  196.  
  197.                 // Update phys area changed proxy
  198.                 if (m_nPhysAreaChangedProxyId != ~0)
  199.                         CParticleManager::Instance()->UpdatePhysAreaChangedProxy(this, m_nPhysAreaChangedProxyId, true);
  200.         }
  201. }
  202.  
  203. void CParticleEmitter::Activate(bool bActive)
  204. {
  205.         float fPrevAge = m_fAge;
  206.         if (bActive)
  207.                 Start(min(-m_fAge, 0.f));
  208.         else
  209.                 Stop();
  210.         UpdateTimes(m_fAge - fPrevAge);
  211. }
  212.  
  213. void CParticleEmitter::Restart()
  214. {
  215.         float fPrevAge = m_fAge;
  216.         Start();
  217.         UpdateTimes(m_fAge - fPrevAge);
  218. }
  219.  
  220. void CParticleEmitter::UpdateTimes(float fAgeAdjust)
  221. {
  222.         if (m_Containers.empty() && m_pTopEffect)
  223.         {
  224.                 // Set death age to effect maximum.
  225.                 m_fDeathAge = m_pTopEffect->Get(FMaxEffectLife().bAllChildren(1).fEmitterMaxLife(m_fStopAge).bParticleLife(1));
  226.                 m_fStateChangeAge = m_fAge;
  227.         }
  228.         else
  229.         {
  230.                 m_fDeathAge = 0.f;
  231.                 m_fStateChangeAge = fHUGE;
  232.  
  233.                 // Update subemitter timings, and compute exact death age.
  234.                 for (auto& c : m_Containers)
  235.                 {
  236.                         c.UpdateContainerLife(fAgeAdjust);
  237.                         m_fDeathAge = max(m_fDeathAge, c.GetContainerLife());
  238.                         m_fStateChangeAge = min(m_fStateChangeAge, c.GetContainerLife());
  239.                 }
  240.         }
  241.  
  242.         m_fAgeLastRendered = min(m_fAgeLastRendered, m_fAge);
  243.         UpdateResetAge();
  244. }
  245.  
  246. void CParticleEmitter::Kill()
  247. {
  248.         Reset();
  249.         if (m_fDeathAge >= 0.f)
  250.         {
  251.                 CParticleSource::Kill();
  252.                 m_fDeathAge = -fHUGE;
  253.         }
  254. }
  255.  
  256. void CParticleEmitter::UpdateResetAge()
  257. {
  258.         m_fResetAge = m_fAge + fUNSEEN_EMITTER_RESET_TIME;
  259. }
  260.  
  261. void CParticleEmitter::UpdateEmitCountScale()
  262. {
  263.         m_fEmitCountScale = m_SpawnParams.fCountScale * GetCVars()->e_ParticlesLod;
  264.         if (m_SpawnParams.bCountPerUnit)
  265.         {
  266.                 // Count multiplied by geometry extent.
  267.                 m_fEmitCountScale *= GetEmitGeom().GetExtent(m_SpawnParams.eAttachType, m_SpawnParams.eAttachForm);
  268.                 m_fEmitCountScale *= ScaleExtent(m_SpawnParams.eAttachForm, GetLocation().s);
  269.         }
  270. }
  271.  
  272. CParticleContainer* CParticleEmitter::AddContainer(CParticleContainer* pParentContainer, const CParticleEffect* pEffect)
  273. {
  274.         void* pMem = m_Containers.push_back_new();
  275.         if (pMem)
  276.                 return new(pMem) CParticleContainer(pParentContainer, this, pEffect);
  277.         return NULL;
  278. }
  279.  
  280. //////////////////////////////////////////////////////////////////////////
  281. // CParticleEmitter implementation.
  282. ////////////////////////////////////////////////////////////////////////
  283. CParticleEmitter::CParticleEmitter(const IParticleEffect* pEffect, const QuatTS& loc, const SpawnParams* pSpawnParams)
  284.         : m_fMaxParticleSize(0.f)
  285.         , m_fEmitCountScale(1.f)
  286.         , m_fViewDistRatio(1.f)
  287.         , m_timeLastUpdate(GetParticleTimer()->GetFrameStartTime())
  288.         , m_fDeathAge(0.f)
  289.         , m_fStateChangeAge(0.f)
  290.         , m_fAgeLastRendered(0.f)
  291.         , m_fBoundsStableAge(0.f)
  292.         , m_fResetAge(fUNSEEN_EMITTER_RESET_TIME)
  293.         , m_pUpdateParticlesJobState(NULL)
  294.         , m_nPhysAreaChangedProxyId(~0)
  295. {
  296.         m_nInternalFlags |= IRenderNode::REQUIRES_FORWARD_RENDERING | IRenderNode::REQUIRES_NEAREST_CUBEMAP;
  297.  
  298.         m_nEntitySlot = -1;
  299.         m_nEnvFlags = 0;
  300.         m_bbWorld.Reset();
  301.  
  302.         m_Loc = loc;
  303.  
  304.         if (pSpawnParams)
  305.                 m_SpawnParams = *pSpawnParams;
  306.  
  307.         GetInstCount(GetRenderNodeType())++;
  308.  
  309.         SetEffect(pEffect);
  310.  
  311.         // Set active
  312.         float fStartAge = 0.f;
  313.         if (m_SpawnParams.bPrime)
  314.                 fStartAge = m_pTopEffect->GetEquilibriumAge(true);
  315.         Start(-fStartAge);
  316.  
  317.         UpdateTimes();
  318. }
  319.  
  320. //////////////////////////////////////////////////////////////////////////
  321. CParticleEmitter::~CParticleEmitter()
  322. {
  323.         if (m_nPhysAreaChangedProxyId != ~0)
  324.         {
  325.                 CParticleManager::Instance()->UpdatePhysAreaChangedProxy(this, m_nPhysAreaChangedProxyId, false);
  326.         }
  327.         m_p3DEngine->FreeRenderNodeState(this); // Also does unregister entity.
  328.         GetInstCount(GetRenderNodeType())--;
  329.         GetEmitGeom().Release();
  330. }
  331.  
  332. //////////////////////////////////////////////////////////////////////////
  333. void CParticleEmitter::Register(bool b, bool bImmediate)
  334. {
  335.         if (!b)
  336.         {
  337.                 if (m_nEmitterFlags & ePEF_Registered)
  338.                 {
  339.                         bImmediate ? m_p3DEngine->UnRegisterEntityDirect(this) : m_p3DEngine->UnRegisterEntityAsJob(this);
  340.                         m_nEmitterFlags &= ~ePEF_Registered;
  341.                 }
  342.         }
  343.         else
  344.         {
  345.                 // Register top-level render node if applicable.
  346.                 if (m_SpawnParams.bNowhere)
  347.                 {
  348.                         bImmediate ? m_p3DEngine->UnRegisterEntityDirect(this) : m_p3DEngine->UnRegisterEntityAsJob(this);
  349.                 }
  350.                 else if (!(m_nEmitterFlags & ePEF_Registered))
  351.                 {
  352.                         if (!m_bbWorld.IsReset())
  353.                         {
  354.                                 // Register node.
  355.                                 // Normally, use emitter's designated position for visibility.
  356.                                 // However, if all bounds are computed dynamically, they might not contain the origin, so use bounds for visibility.
  357.                                 if (m_bbWorld.IsContainPoint(GetPos()))
  358.                                         SetRndFlags(ERF_REGISTER_BY_POSITION, true);
  359.                                 else
  360.                                         SetRndFlags(ERF_REGISTER_BY_POSITION, false);
  361.                                 if (m_SpawnParams.bRegisterByBBox)
  362.                                         SetRndFlags(ERF_REGISTER_BY_BBOX, true);
  363.                                 m_p3DEngine->RegisterEntity(this);
  364.                                 m_nEmitterFlags |= ePEF_Registered;
  365.                         }
  366.                         else if (m_nEmitterFlags & ePEF_Registered)
  367.                         {
  368.                                 bImmediate ? m_p3DEngine->UnRegisterEntityDirect(this) : m_p3DEngine->UnRegisterEntityAsJob(this);
  369.                                 m_nEmitterFlags &= ~ePEF_Registered;
  370.                         }
  371.                 }
  372.         }
  373. }
  374.  
  375. //////////////////////////////////////////////////////////////////////////
  376. string CParticleEmitter::GetDebugString(char type) const
  377. {
  378.         string s = GetName();
  379.         if (type == 's')
  380.         {
  381.                 // Serialization debugging.
  382.                 IEntity* pEntity = GetEntity();
  383.                 if (pEntity)
  384.                         s += string().Format(" entity=%s slot=%d", pEntity->GetName(), m_nEntitySlot);
  385.                 if (IsIndependent())
  386.                         s += " Indep";
  387.                 if (IsActive())
  388.                         s += " Active";
  389.                 else if (IsAlive())
  390.                         s += " Dormant";
  391.                 else
  392.                         s += " Dead";
  393.         }
  394.         else
  395.         {
  396.                 SParticleCounts counts;
  397.                 GetCounts(counts);
  398.                 s += string().Format(" E=%.0f P=%.0f R=%0.f", counts.EmittersActive, counts.ParticlesActive, counts.ParticlesRendered);
  399.         }
  400.  
  401.         s += string().Format(" age=%.3f", GetAge());
  402.         return s;
  403. }
  404.  
  405. //////////////////////////////////////////////////////////////////////////
  406. void CParticleEmitter::AddEffect(CParticleContainer* pParentContainer, const CParticleEffect* pEffect, bool bUpdate)
  407. {
  408.         CParticleContainer* pContainer = pParentContainer;
  409.  
  410.         // Add if playable in current config.
  411.         if (pEffect->IsActive())
  412.         {
  413.                 CParticleContainer* pNext = 0;
  414.                 if (bUpdate)
  415.                 {
  416.                         // Look for existing container
  417.                         for (auto& c : m_Containers)
  418.                         {
  419.                                 if (!c.IsUsed())
  420.                                 {
  421.                                         if (c.GetEffect() == pEffect && c.IsIndirect() == !!pEffect->GetIndirectParent())
  422.                                         {
  423.                                                 pContainer = &c;
  424.                                                 c.SetUsed(true);
  425.                                                 c.OnEffectChange();
  426.                                                 c.InvalidateStaticBounds();
  427.                                                 break;
  428.                                         }
  429.                                         if (!pNext)
  430.                                                 pNext = &c;
  431.                                 }
  432.                         }
  433.                 }
  434.                 if (pContainer == pParentContainer)
  435.                 {
  436.                         // Add new container
  437.                         if (void* pMem = pNext ? m_Containers.insert_new(pNext) : m_Containers.push_back_new())
  438.                                 pContainer = new(pMem) CParticleContainer(pParentContainer, this, pEffect);
  439.                         else
  440.                                 return;
  441.                 }
  442.         }
  443.  
  444.         // Recurse effect tree.
  445.         if (pEffect)
  446.         {
  447.                 for (int i = 0, n = pEffect->GetChildCount(); i < n; i++)
  448.                 {
  449.                         if (CParticleEffect const* pChild = static_cast<const CParticleEffect*>(pEffect->GetChild(i)))
  450.                         {
  451.                                 AddEffect(pContainer, pChild, bUpdate);
  452.                         }
  453.                 }
  454.         }
  455. }
  456.  
  457. void CParticleEmitter::RefreshEffect()
  458. {
  459.         FUNCTION_PROFILER(GetISystem(), PROFILE_PARTICLE);
  460.  
  461.         if (!m_pTopEffect)
  462.                 return;
  463.  
  464.         m_pTopEffect->LoadResources(true);
  465.  
  466.         if (m_Containers.empty())
  467.         {
  468.                 // Create new containers
  469.                 AddEffect(0, m_pTopEffect, false);
  470.         }
  471.         else
  472.         {
  473.                 // Make sure the particle update isn't running anymore when we update an effect
  474.                 SyncUpdateParticlesJob();
  475.  
  476.                 // Update existing containers
  477.                 for (auto& c : m_Containers)
  478.                         c.SetUsed(false);
  479.  
  480.                 AddEffect(0, m_pTopEffect, true);
  481.  
  482.                 // Remove unused containers, in reverse order, children before parents
  483.                 for (auto& c : m_Containers.reversed())
  484.                 {
  485.                         if (!c.IsUsed())
  486.                                 m_Containers.erase(&c);
  487.                 }
  488.         }
  489.  
  490.         m_nEnvFlags = 0;
  491.         m_nRenObjFlags = 0;
  492.         m_fMaxParticleSize = 0.f;
  493.  
  494.         for (auto& c : m_Containers)
  495.         {
  496.                 m_nEnvFlags |= c.GetEnvironmentFlags();
  497.                 m_nRenObjFlags |= c.GetParams().nRenObjFlags.On;
  498.                 m_fMaxParticleSize = max(m_fMaxParticleSize, c.GetEffect()->GetMaxParticleSize() * c.GetParams().fViewDistanceAdjust);
  499.                 if (c.GetParams().fMinPixels)
  500.                         m_fMaxParticleSize = fHUGE;
  501.         }
  502.  
  503.         m_bbWorld.Reset();
  504.         m_VisEnviron.Invalidate();
  505.         m_PhysEnviron.Clear();
  506.  
  507.         // Force state update.
  508.         m_fStateChangeAge = -fHUGE;
  509.         m_fDeathAge = fHUGE;
  510.         m_nEmitterFlags |= ePEF_NeedsEntityUpdate;
  511.  
  512.         m_Vel = ZERO;
  513. }
  514.  
  515. void CParticleEmitter::SetEffect(IParticleEffect const* pEffect)
  516. {
  517.         if (m_pTopEffect != pEffect)
  518.         {
  519.                 m_pTopEffect = &non_const(*static_cast<CParticleEffect const*>(pEffect));
  520.                 Reset();
  521.                 RefreshEffect();
  522.         }
  523. }
  524.  
  525. void CParticleEmitter::CreateIndirectEmitters(CParticleSource* pSource, CParticleContainer* pCont)
  526. {
  527.         for (auto& child : m_Containers)
  528.         {
  529.                 if (child.GetParent() == pCont)
  530.                 {
  531.                         // Found an indirect container for this emitter.
  532.                         if (!child.AddEmitter(pSource))
  533.                                 break;
  534.                 }
  535.         }
  536. }
  537.  
  538. void CParticleEmitter::SetLocation(const QuatTS& loc)
  539. {
  540.         if (!IsEquivalent(GetLocation(), loc, 0.0045f, 1e-5f))
  541.         {
  542.                 InvalidateStaticBounds();
  543.                 m_VisEnviron.Invalidate();
  544.         }
  545.  
  546.         if (!(m_nEmitterFlags & ePEF_HasPhysics))
  547.         {
  548.                 // Infer velocity from movement, smoothing over time.
  549.                 if (float fStep = GetParticleTimer()->GetFrameTime())
  550.                 {
  551.                         Velocity3 vv;
  552.                         vv.FromDelta(QuatT(GetLocation().q, GetLocation().t), QuatT(loc.q, loc.t), max(fStep, fVELOCITY_SMOOTHING_TIME));
  553.                         m_Vel += vv;
  554.                 }
  555.         }
  556.  
  557.         m_Loc = loc;
  558. }
  559.  
  560. void CParticleEmitter::ResetUnseen()
  561. {
  562.         if (!(GetEnvFlags() & (EFF_DYNAMIC_BOUNDS | EFF_ANY)))
  563.         {
  564.                 // Can remove all emitter structures.
  565.                 m_Containers.clear();
  566.         }
  567.         else
  568.         {
  569.                 // Some containers require update every frame; reset the others.
  570.                 for (auto& c : m_Containers.reversed())
  571.                         if (!c.NeedsDynamicBounds())
  572.                                 c.Reset();
  573.         }
  574. }
  575.  
  576. void CParticleEmitter::AllocEmitters()
  577. {
  578.         // (Re)alloc cleared emitters as needed.
  579.         if (m_Containers.empty())
  580.         {
  581.                 SyncUpdateParticlesJob();
  582.  
  583.                 AddEffect(0, m_pTopEffect, false);
  584.                 UpdateContainers();
  585.         }
  586. }
  587.  
  588. void CParticleEmitter::UpdateContainers()
  589. {
  590.         AABB bbPrev = m_bbWorld;
  591.         m_bbWorld.Reset();
  592.         m_fDeathAge = 0.f;
  593.         for (auto& c : m_Containers)
  594.         {
  595.                 c.UpdateState();
  596.                 float fContainerLife = c.GetContainerLife();
  597.                 m_fDeathAge = max(m_fDeathAge, fContainerLife);
  598.                 if (fContainerLife > m_fAge)
  599.                         m_fStateChangeAge = min(m_fStateChangeAge, fContainerLife);
  600.  
  601.                 if (c.GetEnvironmentFlags() & REN_ANY)
  602.                         m_bbWorld.Add(c.GetBounds());
  603.  
  604.                 if (IsIndependent() && fContainerLife >= fHUGE * 0.5f)
  605.                         Warning("Immortal independent effect spawned: %s", c.GetEffect()->GetFullName().c_str());
  606.         }
  607.         if (!IsEquivalent(bbPrev, m_bbWorld))
  608.                 Register(false);
  609. }
  610.  
  611. bool CParticleEmitter::IsInstant() const
  612. {
  613.         return GetStopAge() + GetSpawnParams().fPulsePeriod == 0.f;
  614. }
  615.  
  616. void CParticleEmitter::SetEmitGeom(GeomRef const& geom)
  617. {
  618.         GeomRef emit_new(geom);
  619.         if (emit_new != GetEmitGeom())
  620.         {
  621.                 emit_new.AddRef();
  622.                 GetEmitGeom().Release();
  623.                 static_cast<GeomRef&>(*this) = emit_new;
  624.                 InvalidateStaticBounds();
  625.         }
  626.  
  627.         if (m_SpawnParams.eAttachType)
  628.         {
  629.                 m_nEmitterFlags |= ePEF_HasAttachment;
  630.                 GetEmitGeom().GetExtent(m_SpawnParams.eAttachType, m_SpawnParams.eAttachForm);
  631.         }
  632.         else
  633.                 m_nEmitterFlags &= ~ePEF_HasAttachment;
  634. }
  635.  
  636. void CParticleEmitter::SetSpawnParams(SpawnParams const& spawnParams)
  637. {
  638.         if (ZeroIsHuge(spawnParams.fPulsePeriod) < 0.1f)
  639.                 Warning("Particle emitter (effect %s) PulsePeriod %g too low to be useful",
  640.                         GetName(), spawnParams.fPulsePeriod);
  641.         if (spawnParams.eAttachType != m_SpawnParams.eAttachType ||
  642.             spawnParams.fSizeScale != m_SpawnParams.fSizeScale ||
  643.             spawnParams.fSpeedScale != m_SpawnParams.fSpeedScale)
  644.         {
  645.                 InvalidateStaticBounds();
  646.         }
  647.         if (spawnParams.bPrime > m_SpawnParams.bPrime)
  648.         {
  649.                 float fStartAge = m_pTopEffect->GetEquilibriumAge(true);
  650.                 if (fStartAge > m_fAge)
  651.                 {
  652.                         Start(-fStartAge);
  653.                         UpdateTimes();
  654.                 }
  655.         }
  656.  
  657.         m_SpawnParams = spawnParams;
  658. }
  659.  
  660. IEntity* CParticleEmitter::GetEntity() const
  661. {
  662.         return GetOwnerEntity();
  663. }
  664.  
  665. bool GetPhysicalVelocity(Velocity3& Vel, IEntity* pEnt, const Vec3& vPos)
  666. {
  667.         if (pEnt)
  668.         {
  669.                 if (IPhysicalEntity* pPhysEnt = pEnt->GetPhysics())
  670.                 {
  671.                         pe_status_dynamics dyn;
  672.                         if (pPhysEnt->GetStatus(&dyn))
  673.                         {
  674.                                 Vel.vLin = dyn.v;
  675.                                 Vel.vRot = dyn.w;
  676.                                 Vel.vLin = Vel.VelocityAt(vPos - dyn.centerOfMass);
  677.                                 return true;
  678.                         }
  679.                 }
  680.                 if (pEnt = pEnt->GetParent())
  681.                         return GetPhysicalVelocity(Vel, pEnt, vPos);
  682.         }
  683.         return false;
  684. }
  685.  
  686. void CParticleEmitter::UpdateFromEntity()
  687. {
  688.         FUNCTION_PROFILER(GetISystem(), PROFILE_PARTICLE);
  689.  
  690.         m_nEmitterFlags &= ~(ePEF_HasPhysics | ePEF_HasTarget | ePEF_HasAttachment);
  691.  
  692.         // Get emitter entity.
  693.         IEntity* pEntity = GetEntity();
  694.  
  695.         // Set external target.
  696.         if (!m_Target.bPriority)
  697.         {
  698.                 ParticleTarget target;
  699.                 if (pEntity)
  700.                 {
  701.                         for (IEntityLink* pLink = pEntity->GetEntityLinks(); pLink; pLink = pLink->next)
  702.                         {
  703.                                 if (!stricmp(pLink->name, "Target") || !strnicmp(pLink->name, "Target-", 7))
  704.                                 {
  705.                                         if (IEntity* pTarget = gEnv->pEntitySystem->GetEntity(pLink->entityId))
  706.                                         {
  707.                                                 target.bTarget = true;
  708.                                                 target.vTarget = pTarget->GetPos();
  709.  
  710.                                                 Velocity3 Vel(ZERO);
  711.                                                 GetPhysicalVelocity(Vel, pTarget, GetLocation().t);
  712.                                                 target.vVelocity = Vel.vLin;
  713.  
  714.                                                 AABB bb;
  715.                                                 pTarget->GetLocalBounds(bb);
  716.                                                 target.fRadius = max(bb.min.len(), bb.max.len());
  717.                                                 m_nEmitterFlags |= ePEF_HasTarget;
  718.                                                 break;
  719.                                         }
  720.                                 }
  721.                         }
  722.                 }
  723.  
  724.                 if (target.bTarget != m_Target.bTarget || target.vTarget != m_Target.vTarget)
  725.                         InvalidateStaticBounds();
  726.                 m_Target = target;
  727.         }
  728.  
  729.         bool bShadows = (GetEnvFlags() & REN_CAST_SHADOWS) != 0;
  730.  
  731.         // Get entity of attached parent.
  732.         if (pEntity)
  733.         {
  734.                 if (!(pEntity->GetFlags() & ENTITY_FLAG_CASTSHADOW))
  735.                         bShadows = false;
  736.  
  737.                 if (pEntity->GetPhysics())
  738.                         m_nEmitterFlags |= ePEF_HasPhysics;
  739.  
  740.                 if (pEntity->GetParent())
  741.                         pEntity = pEntity->GetParent();
  742.  
  743.                 if (pEntity->GetPhysics())
  744.                         m_nEmitterFlags |= ePEF_HasPhysics;
  745.  
  746.                 if (m_SpawnParams.eAttachType != GeomType_None)
  747.                 {
  748.                         // If entity attached, find attached physics and geometry on parent.
  749.                         GeomRef geom;
  750.                         int iSlot = geom.Set(pEntity, m_nEntitySlot);
  751.                         if (iSlot >= 0)
  752.                         {
  753.                                 SetMatrix(pEntity->GetSlotWorldTM(iSlot));
  754.                         }
  755.  
  756.                         SetEmitGeom(geom);
  757.                 }
  758.         }
  759.  
  760.         if (bShadows)
  761.                 SetRndFlags(ERF_CASTSHADOWMAPS | ERF_HAS_CASTSHADOWMAPS, true);
  762.         else
  763.                 SetRndFlags(ERF_CASTSHADOWMAPS, false);
  764. }
  765.  
  766. void CParticleEmitter::GetLocalBounds(AABB& bbox)
  767. {
  768.         if (!m_bbWorld.IsReset())
  769.         {
  770.                 bbox.min = m_bbWorld.min - GetLocation().t;
  771.                 bbox.max = m_bbWorld.max - GetLocation().t;
  772.         }
  773.         else
  774.         {
  775.                 bbox.min = bbox.max = Vec3(0);
  776.         }
  777. }
  778.  
  779. void CParticleEmitter::Update()
  780. {
  781.         FUNCTION_PROFILER(GetISystem(), PROFILE_PARTICLE);
  782.  
  783.         m_fAge += (GetParticleTimer()->GetFrameStartTime() - m_timeLastUpdate).GetSeconds() * m_SpawnParams.fTimeScale;
  784.         m_timeLastUpdate = GetParticleTimer()->GetFrameStartTime();
  785.  
  786.         if (m_SpawnParams.fPulsePeriod > 0.f && m_fAge < m_fStopAge)
  787.         {
  788.                 // Apply external pulsing (restart).
  789.                 if (m_fAge >= m_SpawnParams.fPulsePeriod)
  790.                 {
  791.                         float fPulseAge = m_fAge - fmodf(m_fAge, m_SpawnParams.fPulsePeriod);
  792.                         m_fAge -= fPulseAge;
  793.                         m_fStopAge -= fPulseAge;
  794.                         UpdateTimes(-fPulseAge);
  795.                 }
  796.         }
  797.         else if (m_fAge > m_fDeathAge)
  798.                 return;
  799.  
  800.         if (m_nEmitterFlags & (ePEF_NeedsEntityUpdate | ePEF_HasTarget | ePEF_HasAttachment))
  801.         {
  802.                 m_nEmitterFlags &= ~ePEF_NeedsEntityUpdate;
  803.                 UpdateFromEntity();
  804.         }
  805.  
  806.         if (m_fAge > m_fResetAge)
  807.         {
  808.                 ResetUnseen();
  809.                 m_fResetAge = fHUGE;
  810.         }
  811.  
  812.         // Update velocity
  813.         Velocity3 Vel;
  814.         if ((m_nEmitterFlags & ePEF_HasPhysics) && GetPhysicalVelocity(Vel, GetEntity(), GetLocation().t))
  815.         {
  816.                 // Get velocity from physics.
  817.                 m_Vel = Vel;
  818.         }
  819.         else
  820.         {
  821.                 // Decay velocity, using approx exponential decay, so we reach zero soon.
  822.                 float fDecay = max(1.f - GetParticleTimer()->GetFrameTime() / fVELOCITY_SMOOTHING_TIME, 0.f);
  823.                 m_Vel *= fDecay;
  824.         }
  825.  
  826.         if ((m_nEnvFlags & REN_BIND_CAMERA) && gEnv->pSystem)
  827.         {
  828.                 SetMatrix(gEnv->pSystem->GetViewCamera().GetMatrix());
  829.         }
  830.  
  831.         UpdateEmitCountScale();
  832.  
  833.         // Update emitter state and bounds.
  834.         UpdateState();
  835. }
  836.  
  837. void CParticleEmitter::UpdateEffects()
  838. {
  839.         FUNCTION_PROFILER(GetISystem(), PROFILE_PARTICLE);
  840.  
  841.         if (!(GetRndFlags() & ERF_HIDDEN))
  842.         {
  843.                 for (auto& c : m_Containers)
  844.                 {
  845.                         if (c.GetEnvironmentFlags() & EFF_ANY)
  846.                                 c.UpdateEffects();
  847.                 }
  848.         }
  849. }
  850.  
  851. void CParticleEmitter::EmitParticle(const EmitParticleData* pData)
  852. {
  853.         AllocEmitters();
  854.         if (!m_Containers.empty())
  855.         {
  856.                 m_Containers.front().EmitParticle(pData);
  857.                 UpdateResetAge();
  858.                 m_fStateChangeAge = m_fAge;
  859.         }
  860. }
  861.  
  862. void CParticleEmitter::SetEntity(IEntity* pEntity, int nSlot)
  863. {
  864.         SetOwnerEntity(pEntity);
  865.  
  866.         m_nEntitySlot = nSlot;
  867.         m_nEmitterFlags |= ePEF_NeedsEntityUpdate;
  868.         m_Vel = ZERO;
  869.  
  870.         for (auto& c : m_Containers)
  871.                 if (CParticleSubEmitter* e = c.GetDirectEmitter())
  872.                         e->ResetLoc();
  873. }
  874.  
  875. void CParticleEmitter::InvalidateCachedEntityData()
  876. {
  877.         m_nEmitterFlags |= ePEF_NeedsEntityUpdate;
  878.         m_Vel = ZERO;
  879. }
  880.  
  881. void CParticleEmitter::Render(SRendParams const& RenParams, const SRenderingPassInfo& passInfo)
  882. {
  883.         FUNCTION_PROFILER(GetISystem(), PROFILE_PARTICLE);
  884.         PARTICLE_LIGHT_PROFILER();
  885.  
  886.         IF (!passInfo.RenderParticles(), 0)
  887.                 return;
  888.  
  889.         IF (passInfo.IsRecursivePass() && (m_nEnvFlags & REN_BIND_CAMERA), 0)
  890.                 // Render only in main camera.
  891.                 return;
  892.  
  893.         if (!IsActive())
  894.                 return;
  895.  
  896.         AllocEmitters();
  897.  
  898.         // Check combined emitter flags.
  899.         uint32 nRenFlags = REN_SPRITE | REN_GEOMETRY;
  900.         uint32 nRenRequiredFlags = 0;
  901.  
  902.         if (passInfo.IsShadowPass())
  903.         {
  904.                 nRenRequiredFlags |= REN_CAST_SHADOWS;
  905.         }
  906.         else if (!passInfo.IsAuxWindow())
  907.         {
  908.                 nRenFlags |= REN_DECAL;
  909.                 if (!passInfo.IsRecursivePass())
  910.                         nRenFlags |= REN_LIGHTS;
  911.         }
  912.  
  913.         uint32 nEnvFlags = m_nEnvFlags & CParticleManager::Instance()->GetAllowedEnvironmentFlags();
  914.         nRenFlags &= nEnvFlags;
  915.  
  916.         if (!nRenFlags)
  917.                 return;
  918.  
  919.         uint32 nRenRequiredCheckFlags = nRenRequiredFlags & ~nRenFlags;
  920.         if ((nRenRequiredFlags & nEnvFlags) != nRenRequiredFlags)
  921.                 return;
  922.  
  923.         if (!passInfo.IsShadowPass())
  924.         {
  925.                 m_fAgeLastRendered = GetAge();
  926.                 UpdateResetAge();
  927.         }
  928.  
  929.         SPartRenderParams PartParams;
  930.         ZeroStruct(PartParams);
  931.  
  932.         ColorF fogVolumeContrib;
  933.         CFogVolumeRenderNode::TraceFogVolumes(GetPos(), fogVolumeContrib, passInfo);
  934.         PartParams.m_nFogVolumeContribIdx = GetRenderer()->PushFogVolumeContribution(fogVolumeContrib, passInfo);
  935.  
  936.         // Compute camera distance with top effect's SortBoundsScale.
  937.         PartParams.m_fMainBoundsScale = m_pTopEffect->IsEnabled() ? +m_pTopEffect->GetParams().fSortBoundsScale : 1.f;
  938.         if (PartParams.m_fMainBoundsScale == 1.f && RenParams.fDistance > 0.f)
  939.                 // Use 3DEngine-computed BB distance.
  940.                 PartParams.m_fCamDistance = RenParams.fDistance;
  941.         else
  942.                 // Compute with custom bounds scale and adjustment for camera inside BB.
  943.                 PartParams.m_fCamDistance = GetNearestDistance(passInfo.GetCamera().GetPosition(), PartParams.m_fMainBoundsScale);
  944.  
  945.         // Compute max allowed res.
  946.         PartParams.m_fMaxAngularDensity = CParticleManager::Instance()->GetMaxAngularDensity(passInfo.GetCamera()) * GetParticleScale() * m_fViewDistRatio;
  947.         bool bHDRModeEnabled = false;
  948.         GetRenderer()->EF_Query(EFQ_HDRModeEnabled, bHDRModeEnabled);
  949.         PartParams.m_fHDRDynamicMultiplier = bHDRModeEnabled ? HDRDynamicMultiplier : 1.f;
  950.  
  951.         PartParams.m_nRenObjFlags = CParticleManager::Instance()->GetRenderFlags();
  952.  
  953.         if (!passInfo.IsAuxWindow())
  954.         {
  955.                 CLightVolumesMgr& lightVolumeManager = m_p3DEngine->GetLightVolumeManager();
  956.                 PartParams.m_nDeferredLightVolumeId = lightVolumeManager.RegisterVolume(GetPos(), m_bbWorld.GetRadius() * 0.5f, RenParams.nClipVolumeStencilRef, passInfo);
  957.         }
  958.         else
  959.         {
  960.                 // Emitter in preview window, etc
  961.                 PartParams.m_nRenObjFlags.SetState(-1, FOB_INSHADOW | FOB_SOFT_PARTICLE | FOB_MOTION_BLUR);
  962.         }
  963.  
  964.         if (!PartParams.m_nDeferredLightVolumeId)
  965.                 PartParams.m_nRenObjFlags.SetState(-1, FOB_LIGHTVOLUME);
  966.         if (RenParams.m_pVisArea && !RenParams.m_pVisArea->IsAffectedByOutLights())
  967.                 PartParams.m_nRenObjFlags.SetState(-1, FOB_IN_DOORS | FOB_INSHADOW);
  968.         if (RenParams.dwFObjFlags & FOB_NEAREST)
  969.                 PartParams.m_nRenObjFlags.SetState(-1, FOB_SOFT_PARTICLE);
  970.  
  971.         if (passInfo.IsAuxWindow())
  972.         {
  973.                 // Stand-alone rendering.
  974.                 CParticleManager::Instance()->PrepareForRender(passInfo);
  975.         }
  976.  
  977.         // Render relevant containers.
  978.         PartParams.m_nRenFlags = nRenFlags;
  979.         int nThreadJobs = 0;
  980.         for (auto& c : m_Containers)
  981.         {
  982.                 uint32 nContainerFlags = c.GetEnvironmentFlags();
  983.                 if (nContainerFlags & nRenFlags)
  984.                 {
  985.                         if ((nContainerFlags & nRenRequiredCheckFlags) == nRenRequiredFlags)
  986.                         {
  987.                                 if (GetAge() <= c.GetContainerLife())
  988.                                 {
  989.                                         c.Render(RenParams, PartParams, passInfo);
  990.                                         nThreadJobs += c.NeedJobUpdate();
  991.                                 }
  992.                         }
  993.                 }
  994.         }
  995.  
  996.         if (passInfo.IsAuxWindow())
  997.         {
  998.                 // Stand-alone rendering. Submit render object.
  999.                 CParticleManager::Instance()->FinishParticleRenderTasks(passInfo);
  1000.                 RenderDebugInfo();
  1001.         }
  1002.         else if (nThreadJobs && !m_pUpdateParticlesJobState)
  1003.         {
  1004.                 // Schedule new emitter update, for containers first rendered this frame.
  1005.                 AddUpdateParticlesJob();
  1006.         }
  1007. }
  1008.  
  1009. float CParticleEmitter::GetNearestDistance(const Vec3& vPos, float fBoundsScale) const
  1010. {
  1011.         if (fBoundsScale == 0.f)
  1012.                 return (vPos - m_Loc.t).GetLength();
  1013.  
  1014.         AABB bb;
  1015.         float fAbsScale = abs(fBoundsScale);
  1016.         if (fAbsScale == 1.f)
  1017.         {
  1018.                 bb = m_bbWorld;
  1019.         }
  1020.         else
  1021.         {
  1022.                 // Scale toward origin
  1023.                 bb.min = Lerp(m_Loc.t, m_bbWorld.min, fAbsScale);
  1024.                 bb.max = Lerp(m_Loc.t, m_bbWorld.max, fAbsScale);
  1025.         }
  1026.         bb.min -= vPos;
  1027.         bb.max -= vPos;
  1028.  
  1029.         if (fBoundsScale < 0.f)
  1030.         {
  1031.                 // Find furthest point.
  1032.                 Vec3 vFar;
  1033.                 vFar.x = max(abs(bb.min.x), abs(bb.max.x));
  1034.                 vFar.y = max(abs(bb.min.y), abs(bb.max.y));
  1035.                 vFar.z = max(abs(bb.min.z), abs(bb.max.z));
  1036.                 return vFar.GetLength();
  1037.         }
  1038.  
  1039.         // Find nearest point.
  1040.         Vec3 vNear(ZERO);
  1041.         vNear.CheckMin(bb.max);
  1042.         vNear.CheckMax(bb.min);
  1043.         float fDistanceSq = vNear.GetLengthSquared();
  1044.  
  1045.         if (fDistanceSq > 0.f)
  1046.         {
  1047.                 return sqrt_tpl(fDistanceSq);
  1048.         }
  1049.         else
  1050.         {
  1051.                 // When point inside bounds, return negative distance, for stable sorting.
  1052.                 vNear = -bb.min;
  1053.                 vNear.CheckMin(bb.max);
  1054.                 float fMinDist = min(vNear.x, min(vNear.y, vNear.z));
  1055.                 return -fMinDist;
  1056.         }
  1057. }
  1058.  
  1059. // Disable printf argument verification since it is generated at runtime
  1060. #if defined(__GNUC__)
  1061.         #if __GNUC__ >= 4 && __GNUC__MINOR__ < 7
  1062.                 #pragma GCC diagnostic ignored "-Wformat-security"
  1063.         #else
  1064.                 #pragma GCC diagnostic push
  1065.                 #pragma GCC diagnostic ignored "-Wformat-security"
  1066.         #endif
  1067. #endif
  1068.  
  1069. void CParticleEmitter::RenderDebugInfo()
  1070. {
  1071.         if (TimeNotRendered() < 0.25f)
  1072.         {
  1073.                 bool bSelected = IsEditSelected();
  1074.                 if (bSelected || (GetCVars()->e_ParticlesDebug & AlphaBit('b')))
  1075.                 {
  1076.                         AABB const& bb = GetBBox();
  1077.                         if (bb.IsReset())
  1078.                                 return;
  1079.  
  1080.                         AABB bbDyn;
  1081.                         GetDynamicBounds(bbDyn);
  1082.  
  1083.                         CCamera const& cam = GetRenderer()->GetCamera();
  1084.                         ColorF color(1, 1, 1, 1);
  1085.  
  1086.                         // Compute label position, in bb clipped to camera.
  1087.                         Vec3 vPos = GetLocation().t;
  1088.                         Vec3 vLabel = vPos;
  1089.  
  1090.                         // Clip to cam frustum.
  1091.                         float fBorder = 1.f;
  1092.                         for (int i = 0; i < FRUSTUM_PLANES; i++)
  1093.                         {
  1094.                                 Plane const& pl = *cam.GetFrustumPlane(i);
  1095.                                 float f = pl.DistFromPlane(vLabel) + fBorder;
  1096.                                 if (f > 0.f)
  1097.                                         vLabel -= pl.n * f;
  1098.                         }
  1099.  
  1100.                         Vec3 vDist = vLabel - cam.GetPosition();
  1101.                         vLabel += (cam.GetViewdir() * (vDist * cam.GetViewdir()) - vDist) * 0.1f;
  1102.                         vLabel.CheckMax(bbDyn.min);
  1103.                         vLabel.CheckMin(bbDyn.max);
  1104.  
  1105.                         SParticleCounts counts;
  1106.                         GetCounts(counts);
  1107.                         float fPixToScreen = 1.f / ((float)GetRenderer()->GetWidth() * (float)GetRenderer()->GetHeight());
  1108.  
  1109.                         if (!bSelected)
  1110.                         {
  1111.                                 // Randomize hue by effect.
  1112.                                 color = CryStringUtils::CalculateHash(GetEffect()->GetName());
  1113.                                 color.r = color.r * 0.5f + 0.5f;
  1114.                                 color.g = color.g * 0.5f + 0.5f;
  1115.                                 color.b = color.b * 0.5f + 0.5f;
  1116.  
  1117.                                 // Modulate alpha by screen-space bounds extents.
  1118.                                 int aiOut[4];
  1119.                                 cam.CalcScreenBounds(aiOut, &bbDyn, GetRenderer()->GetWidth(), GetRenderer()->GetHeight());
  1120.                                 float fCoverage = (aiOut[2] - aiOut[0]) * (aiOut[3] - aiOut[1]) * fPixToScreen;
  1121.  
  1122.                                 // And by pixel and particle counts.
  1123.                                 float fWeight = sqrt_fast_tpl(fCoverage * counts.ParticlesRendered * counts.PixelsRendered * fPixToScreen);
  1124.  
  1125.                                 color.a = clamp_tpl(fWeight, 0.2f, 1.f);
  1126.                         }
  1127.  
  1128.                         IRenderAuxGeom* pRenAux = GetRenderer()->GetIRenderAuxGeom();
  1129.                         pRenAux->SetRenderFlags(SAuxGeomRenderFlags());
  1130.  
  1131.                         stack_string sLabel = GetName();
  1132.                         sLabel += stack_string().Format(" P=%.0f F=%.3f S/D=",
  1133.                                                         counts.ParticlesRendered,
  1134.                                                         counts.PixelsRendered * fPixToScreen);
  1135.                         if (counts.DynamicBoundsVolume > 0.f)
  1136.                         {
  1137.                                 sLabel += stack_string().Format("%.2f", counts.StaticBoundsVolume / counts.DynamicBoundsVolume);
  1138.                                 if (counts.ErrorBoundsVolume > 0.f)
  1139.                                         sLabel += stack_string().Format(" E/D=%3f", counts.ErrorBoundsVolume / counts.DynamicBoundsVolume);
  1140.                         }
  1141.                         else
  1142.                                 sLabel += "~0";
  1143.  
  1144.                         if (counts.ParticlesCollideTest)
  1145.                                 sLabel += stack_string().Format(" Col=%.0f/%.0f", counts.ParticlesCollideHit, counts.ParticlesCollideTest);
  1146.                         if (counts.ParticlesClip)
  1147.                                 sLabel += stack_string().Format(" Clip=%.0f", counts.ParticlesClip);
  1148.  
  1149.                         ColorF colLabel = color;
  1150.                         if (!bb.ContainsBox(bbDyn))
  1151.                         {
  1152.                                 // Tint red.
  1153.                                 colLabel.r = 1.f;
  1154.                                 colLabel.g *= 0.5f;
  1155.                                 colLabel.b *= 0.5f;
  1156.                                 colLabel.a = (color.a + 1.f) * 0.5f;
  1157.                         }
  1158.                         else if (m_nEnvFlags & EFF_DYNAMIC_BOUNDS)
  1159.                         {
  1160.                                 // Tint yellow.
  1161.                                 colLabel.r = 1.f;
  1162.                                 colLabel.g = 1.f;
  1163.                                 colLabel.b *= 0.5f;
  1164.                         }
  1165.                         IRenderAuxText::DrawLabelEx(vLabel, 1.5f, (float*)&colLabel, true, true, sLabel);
  1166.  
  1167.                         // Compare static and dynamic boxes.
  1168.                         if (!bbDyn.IsReset())
  1169.                         {
  1170.                                 if (bbDyn.min != bb.min || bbDyn.max != bb.max)
  1171.                                 {
  1172.                                         // Separate dynamic BB.
  1173.                                         // Color outlying points bright red, draw connecting lines.
  1174.                                         ColorF colorGood = color * 0.8f;
  1175.                                         ColorF colorBad = colLabel;
  1176.                                         colorBad.a = colorGood.a;
  1177.                                         Vec3 vStat[8], vDyn[8];
  1178.                                         ColorB clrDyn[8];
  1179.                                         for (int i = 0; i < 8; i++)
  1180.                                         {
  1181.                                                 vStat[i] = Vec3(i & 1 ? bb.max.x : bb.min.x, i & 2 ? bb.max.y : bb.min.y, i & 4 ? bb.max.z : bb.min.z);
  1182.                                                 vDyn[i] = Vec3(i & 1 ? bbDyn.max.x : bbDyn.min.x, i & 2 ? bbDyn.max.y : bbDyn.min.y, i & 4 ? bbDyn.max.z : bbDyn.min.z);
  1183.                                                 clrDyn[i] = bb.IsContainPoint(vDyn[i]) ? colorGood : colorBad;
  1184.                                                 pRenAux->DrawLine(vStat[i], color, vDyn[i], clrDyn[i]);
  1185.                                         }
  1186.  
  1187.                                         // Draw dyn bb.
  1188.                                         if (bb.ContainsBox(bbDyn))
  1189.                                         {
  1190.                                                 pRenAux->DrawAABB(bbDyn, false, colorGood, eBBD_Faceted);
  1191.                                         }
  1192.                                         else
  1193.                                         {
  1194.                                                 pRenAux->DrawLine(vDyn[0], clrDyn[0], vDyn[1], clrDyn[1]);
  1195.                                                 pRenAux->DrawLine(vDyn[0], clrDyn[0], vDyn[2], clrDyn[2]);
  1196.                                                 pRenAux->DrawLine(vDyn[0], clrDyn[0], vDyn[4], clrDyn[4]);
  1197.  
  1198.                                                 pRenAux->DrawLine(vDyn[1], clrDyn[1], vDyn[3], clrDyn[3]);
  1199.                                                 pRenAux->DrawLine(vDyn[1], clrDyn[1], vDyn[5], clrDyn[5]);
  1200.  
  1201.                                                 pRenAux->DrawLine(vDyn[2], clrDyn[2], vDyn[3], clrDyn[3]);
  1202.                                                 pRenAux->DrawLine(vDyn[2], clrDyn[2], vDyn[6], clrDyn[6]);
  1203.  
  1204.                                                 pRenAux->DrawLine(vDyn[3], clrDyn[3], vDyn[7], clrDyn[7]);
  1205.  
  1206.                                                 pRenAux->DrawLine(vDyn[4], clrDyn[4], vDyn[5], clrDyn[5]);
  1207.                                                 pRenAux->DrawLine(vDyn[4], clrDyn[4], vDyn[6], clrDyn[6]);
  1208.  
  1209.                                                 pRenAux->DrawLine(vDyn[5], clrDyn[5], vDyn[7], clrDyn[7]);
  1210.  
  1211.                                                 pRenAux->DrawLine(vDyn[6], clrDyn[6], vDyn[7], clrDyn[7]);
  1212.                                         }
  1213.                                 }
  1214.                         }
  1215.  
  1216.                         pRenAux->DrawAABB(bb, false, color, eBBD_Faceted);
  1217.                 }
  1218.  
  1219.                 if (bSelected)
  1220.                 {
  1221.                         // Draw emission volume(s)
  1222.                         IRenderAuxGeom* pRenAux = GetRenderer()->GetIRenderAuxGeom();
  1223.  
  1224.                         int c = 0;
  1225.                         for (const auto& cont : m_Containers)
  1226.                         {
  1227.                                 if (!cont.IsIndirect())
  1228.                                 {
  1229.                                         c++;
  1230.                                         ColorB color(c & 1 ? 255 : 128, c & 2 ? 255 : 128, c & 4 ? 255 : 128, 128);
  1231.  
  1232.                                         const ParticleParams& params = cont.GetEffect()->GetParams();
  1233.                                         pRenAux->DrawAABB(params.GetEmitOffsetBounds(), Matrix34(GetLocation()), false, color, eBBD_Faceted);
  1234.                                 }
  1235.                         }
  1236.                 }
  1237.  
  1238. #if REFRACTION_PARTIAL_RESOLVE_DEBUG_VIEWS
  1239.                 // Render refraction partial resolve bounding boxes
  1240.                 static ICVar* pRefractionPartialResolvesDebugCVar = gEnv->pConsole->GetCVar("r_RefractionPartialResolvesDebug");
  1241.                 if (pRefractionPartialResolvesDebugCVar && pRefractionPartialResolvesDebugCVar->GetIVal() == eRPR_DEBUG_VIEW_3D_BOUNDS)
  1242.                 {
  1243.                         if (IRenderAuxGeom* pAuxRenderer = gEnv->pRenderer->GetIRenderAuxGeom())
  1244.                         {
  1245.                                 SAuxGeomRenderFlags oldRenderFlags = pAuxRenderer->GetRenderFlags();
  1246.  
  1247.                                 SAuxGeomRenderFlags newRenderFlags;
  1248.                                 newRenderFlags.SetDepthTestFlag(e_DepthTestOff);
  1249.                                 newRenderFlags.SetAlphaBlendMode(e_AlphaBlended);
  1250.                                 pAuxRenderer->SetRenderFlags(newRenderFlags);
  1251.  
  1252.                                 // Render all bounding boxes for containers that have refractive particles
  1253.                                 for (auto& c : m_Containers)
  1254.                                 {
  1255.                                         if (c.WasRenderedPrevFrame())
  1256.                                         {
  1257.                                                 const ResourceParticleParams& params = c.GetParams();
  1258.  
  1259.                                                 IMaterial* pMatToUse = params.pMaterial;
  1260.  
  1261.                                                 if (pMatToUse)
  1262.                                                 {
  1263.                                                         IShader* pShader = pMatToUse->GetShaderItem().m_pShader;
  1264.                                                         if (pShader && (pShader->GetFlags() & EF_REFRACTIVE))
  1265.                                                         {
  1266.                                                                 const AABB& aabb = c.GetBounds();
  1267.                                                                 const bool bSolid = true;
  1268.                                                                 const ColorB solidColor(64, 64, 255, 64);
  1269.                                                                 pAuxRenderer->DrawAABB(aabb, bSolid, solidColor, eBBD_Faceted);
  1270.  
  1271.                                                                 const ColorB wireframeColor(255, 0, 0, 255);
  1272.                                                                 pAuxRenderer->DrawAABB(aabb, !bSolid, wireframeColor, eBBD_Faceted);
  1273.                                                         }
  1274.                                                 }
  1275.                                         }
  1276.  
  1277.                                         // Set previous Aux render flags back again
  1278.                                         pAuxRenderer->SetRenderFlags(oldRenderFlags);
  1279.                                 }
  1280.                         }
  1281.                 }
  1282. #endif
  1283.         }
  1284. }
  1285. #if defined(__GNUC__)
  1286.         #if __GNUC__ >= 4 && __GNUC__MINOR__ < 7
  1287.                 #pragma GCC diagnostic error "-Wformat-security"
  1288.         #else
  1289.                 #pragma GCC diagnostic pop
  1290.         #endif
  1291. #endif
  1292.  
  1293. void CParticleEmitter::SerializeState(TSerialize ser)
  1294. {
  1295.         // Time values.
  1296.         ser.Value("Age", m_fAge);
  1297.         ser.Value("StopAge", m_fStopAge);
  1298.         if (ser.IsReading())
  1299.         {
  1300.                 UpdateTimes();
  1301.                 m_timeLastUpdate = GetParticleTimer()->GetFrameStartTime();
  1302.         }
  1303.  
  1304.         // Spawn params.
  1305.         ser.Value("SizeScale", m_SpawnParams.fSizeScale);
  1306.         ser.Value("SpeedScale", m_SpawnParams.fSpeedScale);
  1307.         ser.Value("TimeScale", m_SpawnParams.fTimeScale);
  1308.         ser.Value("CountScale", m_SpawnParams.fCountScale);
  1309.         ser.Value("CountPerUnit", m_SpawnParams.bCountPerUnit);
  1310.         ser.Value("PulsePeriod", m_SpawnParams.fPulsePeriod);
  1311. }
  1312.  
  1313. void CParticleEmitter::Hide(bool bHide)
  1314. {
  1315.         IParticleEmitter::Hide(bHide);
  1316.         if (bHide)
  1317.         {
  1318.                 for (auto& c : m_Containers)
  1319.                 {
  1320.                         c.OnHidden();
  1321.                 }
  1322.         }
  1323.         else
  1324.         {
  1325.                 if (m_nEnvFlags & EFF_ANY)
  1326.                         AllocEmitters();
  1327.                 for (auto& c : m_Containers)
  1328.                 {
  1329.                         c.OnUnhidden();
  1330.                 }
  1331.         }
  1332. }
  1333.  
  1334. void CParticleEmitter::GetMemoryUsage(ICrySizer* pSizer) const
  1335. {
  1336.         m_Containers.GetMemoryUsage(pSizer);
  1337.         m_PhysEnviron.GetMemoryUsage(pSizer);
  1338. }
  1339.  
  1340. bool CParticleEmitter::UpdateStreamableComponents(float fImportance, const Matrix34A& objMatrix, IRenderNode* pRenderNode, float fEntDistance, bool bFullUpdate, int nLod)
  1341. {
  1342.         FUNCTION_PROFILER_3DENGINE;
  1343.  
  1344.         IRenderer* pRenderer = GetRenderer();
  1345.         for (const auto& c : m_Containers)
  1346.         {
  1347.                 ResourceParticleParams const& params = c.GetParams();
  1348.                 if (params.pStatObj)
  1349.                 {
  1350.                         CStatObj* pStatObj = static_cast<CStatObj*>(params.pStatObj.get());
  1351.                         IMaterial* pMaterial = pStatObj->GetMaterial();
  1352.                         m_pObjManager->PrecacheStatObj(pStatObj, nLod, objMatrix, pMaterial, fImportance, fEntDistance, bFullUpdate, false);
  1353.                 }
  1354.  
  1355.                 if (CMatInfo* pMatInfo = (CMatInfo*)(IMaterial*)params.pMaterial)
  1356.                         pMatInfo->PrecacheMaterial(fEntDistance, NULL, bFullUpdate);
  1357.         }
  1358.  
  1359.         return true;
  1360. }
  1361.  
  1362. EntityId CParticleEmitter::GetAttachedEntityId()
  1363. {
  1364.         if (GetOwnerEntity())
  1365.                 GetOwnerEntity()->GetId();
  1366.         return INVALID_ENTITYID;
  1367. }
  1368.  
  1369. IParticleAttributes& CParticleEmitter::GetAttributes()
  1370. {
  1371.         return m_pTopEffect->GetAttributes();
  1372. }
  1373.  
  1374. void CParticleEmitter::OffsetPosition(const Vec3& delta)
  1375. {
  1376.         SMoveState::OffsetPosition(delta);
  1377.         if (m_pTempData) m_pTempData->OffsetPosition(delta);
  1378.         m_Target.vTarget += delta;
  1379.         m_bbWorld.Move(delta);
  1380.  
  1381.         for (auto& c : m_Containers)
  1382.                 c.OffsetPosition(delta);
  1383. }
  1384.  
downloadParticleEmitter.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