BVB Source Codes

CRYENGINE Show ParticleContainer.cpp Source code

Return Download CRYENGINE: download ParticleContainer.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:   ParticleContainer.h
  5. //  Version:     v1.00
  6. //  Created:     11/03/2010 by Corey (split out from other files).
  7. //  Compilers:   Visual Studio.NET
  8. //  Description:
  9. // -------------------------------------------------------------------------
  10. //  History:
  11. //
  12. ////////////////////////////////////////////////////////////////////////////
  13.  
  14. #include "StdAfx.h"
  15. #include "ParticleContainer.h"
  16. #include "ParticleEmitter.h"
  17. #include "Particle.h"
  18.  
  19. #include <CryThreading/IJobManager_JobDelegator.h>
  20.  
  21. #define fMAX_STATIC_BB_RADIUS        4096.f   // Static bounding box above this size forces dynamic BB
  22. #define fMAX_RELATIVE_TAIL_DEVIATION 0.1f
  23.  
  24. #define nMAX_ITERATIONS              8
  25. #define fMAX_FRAME_LIMIT_TIME        0.25f    // Max frame time at which to enforce iteration limit
  26. #define fMAX_LINEAR_STEP_TIME        4.f      // Max time to test ahead
  27.  
  28. template<class T>
  29. inline void move_raw_array_elem(T* pDest, T* pSrc)
  30. {
  31.         T val = *pSrc;
  32.         if (pSrc < pDest)
  33.                 memmove(pSrc, pSrc + 1, (char*)pDest - (char*)(pSrc + 1));
  34.         else
  35.                 memmove(pDest + 1, pDest, (char*)pSrc - (char*)pDest);
  36.         *pDest = val;
  37. }
  38.  
  39. //////////////////////////////////////////////////////////////////////////
  40. CParticleContainer::CParticleContainer(CParticleContainer* pParent, CParticleEmitter* pMain, CParticleEffect const* pEffect)
  41.         : m_pEffect(&non_const(*pEffect))
  42.         , m_nEmitterSequence(0)
  43.         , m_pMainEmitter(pMain)
  44.         , m_fAgeLastUpdate(0.f)
  45.         , m_fAgeStaticBoundsStable(0.f)
  46.         , m_fContainerLife(0.f)
  47. {
  48.         ZeroStruct(m_pBeforeWaterRO);
  49.         ZeroStruct(m_pAfterWaterRO);
  50.         ZeroStruct(m_pRecursiveRO);
  51.  
  52.         assert(pEffect);
  53.         assert(pEffect->IsActive() || gEnv->IsEditing());
  54.         m_pParams = &m_pEffect->GetParams();
  55.         assert(m_pParams->nEnvFlags & EFF_LOADED);
  56.  
  57.         if (m_pParams->eSpawnIndirection)
  58.                 m_pParentContainer = pParent;
  59.         else if (pParent && pParent->IsIndirect())
  60.                 m_pParentContainer = pParent->GetParent();
  61.         else
  62.                 m_pParentContainer = 0;
  63.  
  64.         // To do: Invalidate static bounds on updating areas.
  65.         // To do: Use dynamic bounds if in non-uniform areas ??
  66.         m_bbWorld.Reset();
  67.         m_bbWorldStat.Reset();
  68.         m_bbWorldDyn.Reset();
  69.  
  70.         OnEffectChange();
  71.  
  72.         m_nChildFlags = 0;
  73.         m_bUnused = false;
  74.         m_fMaxParticleFullLife = m_pEffect->GetMaxParticleFullLife();
  75.  
  76.         m_nNeedJobUpdate = 0;
  77.  
  78.         if (!m_pParentContainer)
  79.         {
  80.                 // Not an indirect container, so it will need only a single SubEmitter - allocate it here
  81.                 AddEmitter(pMain);
  82.         }
  83.  
  84.         // Do not use coverage buffer culling for 'draw near' or 'on top' particles
  85.         if (pMain && (GetParams().bDrawNear || GetParams().bDrawOnTop))
  86.                 pMain->SetRndFlags(ERF_RENDER_ALWAYS, true);
  87. }
  88.  
  89. CParticleContainer::~CParticleContainer()
  90. {
  91.         ResetRenderObjects();
  92. }
  93.  
  94. uint32 CParticleContainer::GetEnvironmentFlags() const
  95. {
  96.         return m_nEnvFlags & CParticleManager::Instance()->GetAllowedEnvironmentFlags();
  97. }
  98.  
  99. void CParticleContainer::OnEffectChange()
  100. {
  101.         m_nEnvFlags = m_pParams->nEnvFlags;
  102.  
  103.         // Update existing particle history arrays if needed.
  104.         int nPrevSteps = m_nHistorySteps;
  105.         m_nHistorySteps = m_pParams->GetTailSteps();
  106.  
  107.         // Do not use coverage buffer culling for 'draw near' or 'on top' particles
  108.         if (m_pParams->bDrawNear || m_pParams->bDrawOnTop)
  109.                 GetMain().SetRndFlags(ERF_RENDER_ALWAYS, true);
  110.  
  111. #if CRY_PLATFORM_DESKTOP
  112.         if (gEnv->IsEditor() && m_Particles.size() > 0)
  113.         {
  114.                 if (m_nHistorySteps > nPrevSteps || NeedsCollisionInfo())
  115.                 {
  116.                         for (auto& p : m_Particles)
  117.                         {
  118.                                 // Alloc and connect emitter structures.
  119.                                 p.UpdateAllocations(nPrevSteps);
  120.                         }
  121.                 }
  122.         }
  123. #endif
  124.  
  125.         if (m_pParentContainer)
  126.         {
  127.                 // Inherit dynamic bounds requirement.
  128.                 m_nEnvFlags |= m_pParentContainer->GetEnvironmentFlags() & EFF_DYNAMIC_BOUNDS;
  129.                 for (CParticleContainer* pParent = m_pParentContainer; pParent; pParent = pParent->GetParent())
  130.                         pParent->m_nChildFlags |= m_nEnvFlags;
  131.  
  132.                 // Pre-compute extents of parent attached object.
  133.                 if (GetParams().eAttachType)
  134.                 {
  135.                         if (IStatObj* pStatObj = m_pParentContainer->GetParams().pStatObj)
  136.                                 pStatObj->GetExtent(GetParams().eAttachForm);
  137.                 }
  138.         }
  139.  
  140.         // Prevent invalid particle references to old geometry when editing Geometry param.
  141.         if (gEnv->IsEditing() && GetParams().pStatObj)
  142.                 m_ExternalStatObjs.push_back(GetParams().pStatObj);
  143. }
  144.  
  145. CParticleSubEmitter* CParticleContainer::AddEmitter(CParticleSource* pSource)
  146. {
  147.         if (void* pMem = m_Emitters.push_back_new())
  148.         {
  149.                 return new(pMem) CParticleSubEmitter(pSource, this);
  150.         }
  151.         return NULL;
  152. }
  153.  
  154. CParticle* CParticleContainer::AddParticle(SParticleUpdateContext& context, const CParticle& part)
  155. {
  156.         FUNCTION_PROFILER(GetISystem(), PROFILE_PARTICLE);
  157.  
  158.         const ResourceParticleParams& params = GetParams();
  159.  
  160.         if (params.Connection)
  161.         {
  162.                 // Segregate by emitter, and always in front of previous particles.
  163.                 // To do: faster search for multi-emitter containers.
  164.                 int nSequence = part.GetEmitter()->GetSequence();
  165.                 for (auto& p : m_Particles)
  166.                 {
  167.                         if (p.GetEmitterSequence() == nSequence)
  168.                                 return m_Particles.insert(&p, part);
  169.                 }
  170.         }
  171.         else if (context.nEnvFlags & REN_SORT)
  172.         {
  173.                 float fNewDist = part.GetMinDist(context.vMainCamPos);
  174.  
  175.                 if (!context.aParticleSort.empty())
  176.                 {
  177.                         // More accurate sort, using sort data in SParticleUpdateContext.
  178.                         size_t nNewPos = min((size_t)context.aParticleSort.size() - 1, m_Particles.size());
  179.                         SParticleUpdateContext::SSortElem* pNew = &context.aParticleSort[nNewPos];
  180.                         pNew->pPart = NULL;
  181.                         pNew->fDist = -fNewDist;
  182.  
  183.                         SParticleUpdateContext::SSortElem* pFound;
  184.                         if (context.nSortQuality == 1)
  185.                         {
  186.                                 // Find nearest (progressive distance) particle. Order: log(2) n
  187.                                 pFound = std::lower_bound(context.aParticleSort.begin(), pNew, *pNew);
  188.                         }
  189.                         else
  190.                         {
  191.                                 // Find min distance error. Order: n
  192.                                 float fMinError = 0.f;
  193.                                 float fCumError = 0.f;
  194.                                 pFound = context.aParticleSort.begin();
  195.                                 for (size_t i = 0; i < nNewPos; i++)
  196.                                 {
  197.                                         fCumError += context.aParticleSort[i].fDist + fNewDist;
  198.                                         if (fCumError < fMinError)
  199.                                         {
  200.                                                 fMinError = fCumError;
  201.                                                 pFound = &context.aParticleSort[i + 1];
  202.                                         }
  203.                                 }
  204.                         }
  205.  
  206. #ifdef SORT_DEBUG
  207.                         // Compute sort error. Slow!
  208.                         {
  209.                                 static float fAvgError = 0.f;
  210.                                 static float fCount = 0.f;
  211.                                 float fError = 0.f;
  212.                                 float fDir = 1.f;
  213.                                 for (ParticleList<CParticle>::const_iterator pPart(m_Particles); pPart; ++pPart)
  214.                                 {
  215.                                         if (pPart == pFound->pPart)
  216.                                                 fDir = -1.f;
  217.                                         float fDist = pPart->GetMinDist(context.vMainCamPos);
  218.                                         fError += max(fDir * (fNewDist - fDist), 0.f);
  219.                                 }
  220.  
  221.                                 fAvgError = (fAvgError * fCount + fError) / (fCount + 1.f);
  222.                                 fCount += 1.f;
  223.                         }
  224. #endif
  225.  
  226.                         // Move next particle in particle list, and corresponding data in local arrays.
  227.                         CParticle* pPart = m_Particles.insert(pFound->pPart, part);
  228.                         pNew->pPart = pPart;
  229.                         move_raw_array_elem(pFound, pNew);
  230.                         return pPart;
  231.                 }
  232.                 else
  233.                 {
  234.                         // Approximate sort; push to front or back of list, based on position relative to bb center.
  235.                         if (fNewDist < context.fCenterCamDist)
  236.                                 return m_Particles.push_back(part);
  237.                 }
  238.         }
  239.  
  240.         // Push newest to front.
  241.         return m_Particles.push_front(part);
  242. }
  243.  
  244. void CParticleContainer::EmitParticle(const EmitParticleData* pData)
  245. {
  246.         // Queue particle emission.
  247.         EmitParticleData* pNew = m_DeferredEmitParticles.push_back();
  248.         if (pNew && pData)
  249.         {
  250.                 *pNew = *pData;
  251.  
  252.                 if (pData->pPhysEnt || pData->bHasLocation || pData->bHasVel)
  253.                 {
  254.                         // Static bounds no longer reliable.
  255.                         SetDynamicBounds();
  256.                 }
  257.                 if (pData->pStatObj)
  258.                 {
  259.                         // Add reference to StatObj for lifetime of container.
  260.                         assert(GetEnvironmentFlags() & REN_GEOMETRY);
  261.                         m_ExternalStatObjs.push_back(pData->pStatObj);
  262.                 }
  263.         }
  264. }
  265.  
  266. void CParticleContainer::ComputeStaticBounds(AABB& bb, bool bWithSize, float fMaxLife)
  267. {
  268.         FUNCTION_PROFILER(GetISystem(), PROFILE_PARTICLE);
  269.  
  270.         const ResourceParticleParams& params = GetParams();
  271.  
  272.         QuatTS loc = GetMain().GetLocation();
  273.         AABB bbSpawn(0.f);
  274.  
  275.         FStaticBounds opts;
  276.  
  277.         if (m_pParentContainer)
  278.         {
  279.                 // Expand by parent spawn volume.
  280.                 bool bParentSize = params.eAttachType != GeomType_None;
  281.                 float fMaxParentLife = params.GetMaxSpawnDelay();
  282.                 if (params.bContinuous || params.bMoveRelativeEmitter)
  283.                 {
  284.                         if (params.fEmitterLifeTime)
  285.                                 fMaxParentLife += params.fEmitterLifeTime.GetMaxValue();
  286.                         else
  287.                                 fMaxParentLife = fHUGE;
  288.                 }
  289.                 m_pParentContainer->ComputeStaticBounds(bbSpawn, bParentSize, fMaxParentLife);
  290.                 loc.t = bbSpawn.GetCenter();
  291.                 opts.fAngMax = m_pParentContainer->GetParams().GetMaxRotationAngle();
  292.         }
  293.         else if (GetMain().GetEmitGeom())
  294.         {
  295.                 // Expand by attached geom.
  296.                 GetMain().GetEmitGeom().GetAABB(bbSpawn, GetMain().GetSpawnParams().eAttachType, loc);
  297.                 loc.t = bbSpawn.GetCenter();
  298.         }
  299.  
  300.         loc.s = GetMaxParticleScale();
  301.         opts.vSpawnSize = bbSpawn.GetSize() * 0.5f;
  302.         opts.fSpeedScale = GetMain().GetSpawnParams().fSpeedScale;
  303.         opts.bWithSize = bWithSize;
  304.         opts.fMaxLife = fMaxLife;
  305.  
  306.         SPhysForces forces;
  307.         GetMain().GetPhysEnviron().GetForces(forces, loc.t, m_nEnvFlags);
  308.         params.GetStaticBounds(bb, loc, forces, opts);
  309.  
  310.         // Adjust for internal/external target.
  311.         ParticleTarget target;
  312.         if (GetTarget(target, GetDirectEmitter()))
  313.         {
  314.                 AABB bbTarg(target.vTarget, target.fRadius);
  315.                 bbTarg.Expand(bb.GetSize() * 0.5f);
  316.                 bb.Add(bbTarg);
  317.         }
  318. }
  319.  
  320. void CParticleContainer::UpdateState()
  321. {
  322.         FUNCTION_PROFILER(GetISystem(), PROFILE_PARTICLE);
  323.  
  324.         UpdateContainerLife();
  325.  
  326.         if (!NeedsDynamicBounds())
  327.         {
  328.                 if (m_bbWorldStat.IsReset())
  329.                 {
  330.                         ComputeStaticBounds(m_bbWorldStat);
  331.                         if (m_bbWorldStat.GetRadiusSqr() >= sqr(fMAX_STATIC_BB_RADIUS))
  332.                                 SetDynamicBounds();
  333.                 }
  334.         }
  335.  
  336.         if (NeedsDynamicBounds() || !m_DeferredEmitParticles.empty())
  337.         {
  338.                 UpdateParticles();
  339.                 m_DeferredEmitParticles.clear();
  340.         }
  341.  
  342.         if (NeedsDynamicBounds())
  343.         {
  344.                 SyncUpdateParticles();
  345.                 m_bbWorld = m_bbWorldDyn;
  346.         }
  347.         else
  348.         {
  349.                 m_bbWorld = m_bbWorldStat;
  350.                 if (!StaticBoundsStable())
  351.                         m_bbWorld.Add(m_bbWorldDyn);
  352.         }
  353. }
  354.  
  355. // To do: Add flags for movement/gravity/wind.
  356. float CParticleContainer::InvalidateStaticBounds()
  357. {
  358.         if (!NeedsDynamicBounds())
  359.         {
  360.                 m_bbWorldStat.Reset();
  361.                 if (m_pParams->NeedsExtendedBounds() && !m_Particles.empty())
  362.                         return m_fAgeStaticBoundsStable = GetAge() + m_pParams->GetMaxParticleLife();
  363.         }
  364.         return 0.f;
  365. }
  366.  
  367. bool CParticleContainer::GetTarget(ParticleTarget& target, const CParticleSubEmitter* pSubEmitter) const
  368. {
  369.         ParticleParams::STargetAttraction::ETargeting eTargeting = GetParams().TargetAttraction.eTarget;
  370.         if (eTargeting == eTargeting.Ignore)
  371.                 return false;
  372.  
  373.         target = GetMain().GetTarget();
  374.         if (!target.bPriority && eTargeting == eTargeting.OwnEmitter && pSubEmitter)
  375.         {
  376.                 // Local target override.
  377.                 target.bTarget = true;
  378.                 target.vTarget = pSubEmitter->GetSource().GetLocation().t;
  379.                 target.vVelocity = pSubEmitter->GetSource().GetVelocity().vLin;
  380.                 target.fRadius = 0.f;
  381.         }
  382.         return target.bTarget;
  383. }
  384.  
  385. void CParticleContainer::ComputeUpdateContext(SParticleUpdateContext& context, float fUpdateTime)
  386. {
  387.         const ResourceParticleParams& params = GetParams();
  388.  
  389.         context.fUpdateTime = fUpdateTime;
  390.         context.fMaxLinearStepTime = fMAX_LINEAR_STEP_TIME;
  391.  
  392.         context.nEnvFlags = GetEnvironmentFlags();
  393.  
  394.         // Sorting support.
  395.         context.nSortQuality = max((int)params.nSortQuality, GetCVars()->e_ParticlesSortQuality);
  396.         context.vMainCamPos = gEnv->p3DEngine->GetRenderingCamera().GetPosition();
  397.         const AABB& bb = NeedsDynamicBounds() ? m_bbWorldDyn : m_bbWorldStat;
  398.         context.fCenterCamDist = (context.vMainCamPos - bb.GetCenter()).GetLength() - params.fSize.GetMaxValue();
  399.  
  400.         // Compute rotations in 3D for geometry particles, or those set to 3D orientation.
  401.         context.b3DRotation = params.eFacing == params.eFacing.Free || params.eFacing == params.eFacing.Decal
  402.                               || params.pStatObj;
  403.  
  404.         // Emission bounds parameters.
  405.         context.vEmitBox = params.vRandomOffset;
  406.         context.vEmitScale.zero();
  407.  
  408.         if (params.fOffsetRoundness)
  409.         {
  410.                 float fRound = params.fOffsetRoundness * max(max(context.vEmitBox.x, context.vEmitBox.y), context.vEmitBox.z);
  411.                 Vec3 vRound(min(context.vEmitBox.x, fRound), min(context.vEmitBox.y, fRound), min(context.vEmitBox.z, fRound));
  412.                 context.vEmitBox -= vRound;
  413.                 context.vEmitScale(vRound.x ? 1 / vRound.x : 0, vRound.y ? 1 / vRound.y : 0, vRound.z ? 1 / vRound.z : 0);
  414.         }
  415.  
  416.         if (params.bSpaceLoop)
  417.         {
  418.                 // Use emission bounding volume.
  419.                 AABB bbSpaceLocal = params.GetEmitOffsetBounds();
  420.                 if (GetMain().GetEnvFlags() & REN_BIND_CAMERA)
  421.                 {
  422.                         // Use CameraMaxDistance as alternate space loop size.
  423.                         bbSpaceLocal.Add(Vec3(0.f), params.fCameraMaxDistance);
  424.  
  425.                         // Limit bounds to camera frustum.
  426.                         bbSpaceLocal.min.y = max(bbSpaceLocal.min.y, +params.fCameraMinDistance);
  427.  
  428.                         // Scale cam dist limits to handle zoom.
  429.                         CCamera const& cam = gEnv->p3DEngine->GetRenderingCamera();
  430.                         float fHSinCos[2];
  431.                         sincos_tpl(cam.GetHorizontalFov() * 0.5f, &fHSinCos[0], &fHSinCos[1]);
  432.                         float fVSinCos[2];
  433.                         sincos_tpl(cam.GetFov() * 0.5f, &fVSinCos[0], &fVSinCos[1]);
  434.                         float fZoom = 1.f / cam.GetFov();
  435.  
  436.                         float fMin = fHSinCos[1] * fVSinCos[1] * (params.fCameraMinDistance * fZoom);
  437.  
  438.                         bbSpaceLocal.max.CheckMin(Vec3(fHSinCos[0], 1.f, fVSinCos[0]) * (bbSpaceLocal.max.y * fZoom));
  439.                         bbSpaceLocal.min.CheckMax(Vec3(-bbSpaceLocal.max.x, fMin, -bbSpaceLocal.max.z));
  440.                 }
  441.  
  442.                 // Compute params for quick space-loop bounds checking.
  443.                 QuatTS const& loc = GetMain().GetLocation();
  444.                 context.SpaceLoop.vCenter = loc * bbSpaceLocal.GetCenter();
  445.                 context.SpaceLoop.vSize = bbSpaceLocal.GetSize() * (0.5f * loc.s);
  446.                 Matrix33 mat(loc.q);
  447.                 for (int a = 0; a < 3; a++)
  448.                 {
  449.                         context.SpaceLoop.vScaledAxes[a] = mat.GetColumn(a);
  450.                         context.SpaceLoop.vSize[a] = max(context.SpaceLoop.vSize[a], 1e-6f);
  451.                         context.SpaceLoop.vScaledAxes[a] /= context.SpaceLoop.vSize[a];
  452.                 }
  453.         }
  454.  
  455.         ParticleParams::STargetAttraction::ETargeting eTarget = params.TargetAttraction.eTarget;
  456.         context.bHasTarget = eTarget == eTarget.OwnEmitter
  457.                              || (eTarget == eTarget.External && GetMain().GetTarget().bTarget);
  458.  
  459.         context.fMinStepTime = min(fUpdateTime, fMAX_FRAME_LIMIT_TIME) / float(nMAX_ITERATIONS);
  460.  
  461.         // Compute time and distance limits in certain situations.
  462.         context.fMaxLinearDeviation = fHUGE;
  463.         if (GetHistorySteps() > 0)
  464.         {
  465.                 context.fMaxLinearDeviation = fMAX_RELATIVE_TAIL_DEVIATION * params.fSize.GetMaxValue();
  466.                 context.fMinStepTime = min(context.fMinStepTime, params.fTailLength.GetMaxValue() * 0.5f / GetHistorySteps());
  467.         }
  468.         if (context.nEnvFlags & ENV_COLLIDE_ANY)
  469.         {
  470.                 context.fMaxLinearDeviation = min(context.fMaxLinearDeviation, fMAX_COLLIDE_DEVIATION);
  471.         }
  472.  
  473.         // If random turbulence enabled, limit linear time based on deviation limit.
  474.         // dp(t) = Turb/2 t^(3/2)
  475.         // t = (2 Dev/Turb)^(2/3)
  476.         if (params.fTurbulence3DSpeed)
  477.         {
  478.                 context.fMaxLinearStepTime = min(context.fMaxLinearStepTime, powf(2.f * context.fMaxLinearDeviation / params.fTurbulence3DSpeed, 0.666f));
  479.         }
  480.  
  481.         // If vortex turbulence enabled, find max rotation angle for allowed deviation.
  482.         float fVortexRadius = params.fTurbulenceSize.GetMaxValue() * GetMaxParticleScale();
  483.         if (fVortexRadius > context.fMaxLinearDeviation)
  484.         {
  485.                 // dev(r, a) = r (1 - sqrt( (cos(a)+1) / 2 ))
  486.                 // cos(a) = 2 (1 - dev/r)^2 - 1
  487.                 float fCos = sqr(1.f - context.fMaxLinearDeviation / fVortexRadius) * 2.f - 1.f;
  488.                 float fAngle = RAD2DEG(acos(clamp_tpl(fCos, -1.f, 1.f)));
  489.                 context.fMaxLinearStepTime = div_min(fAngle, fabs(params.fTurbulenceSpeed.GetMaxValue()), context.fMaxLinearStepTime);
  490.         }
  491.  
  492.         if (NeedsDynamicBounds() || !StaticBoundsStable()
  493.             || (GetCVars()->e_ParticlesDebug & AlphaBits('bx')) || GetMain().IsEditSelected())
  494.         {
  495.                 // Accumulate dynamic bounds.
  496.                 context.pbbDynamicBounds = &m_bbWorldDyn;
  497.                 if (NeedsDynamicBounds() || !StaticBoundsStable())
  498.                         m_bbWorldDyn.Reset();
  499.         }
  500.         else
  501.                 context.pbbDynamicBounds = NULL;
  502.  
  503.         context.fDensityAdjust = 1.f;
  504. }
  505.  
  506. //////////////////////////////////////////////////////////////////////////
  507. void CParticleContainer::UpdateParticles()
  508. {
  509.         if (m_nNeedJobUpdate > 0)
  510.                 m_nNeedJobUpdate--;
  511.  
  512.         float fAge = GetAge();
  513.         float fUpdateTime = fAge - m_fAgeLastUpdate;
  514.         if (fUpdateTime > 0.f || !m_DeferredEmitParticles.empty())
  515.         {
  516.                 FUNCTION_PROFILER_CONTAINER(this);
  517.  
  518.                 if (m_pParentContainer)
  519.                 {
  520.                         m_pParentContainer->UpdateParticles();
  521.                 }
  522.  
  523.                 SParticleUpdateContext context;
  524.                 ComputeUpdateContext(context, fUpdateTime);
  525.  
  526.                 GetMain().GetPhysEnviron().LockAreas(context.nEnvFlags, 1);
  527.  
  528.                 // Update all particle positions etc, delete dead particles.
  529.                 UpdateParticleStates(context);
  530.  
  531.                 GetMain().GetPhysEnviron().LockAreas(context.nEnvFlags, -1);
  532.  
  533.                 m_fAgeLastUpdate = fAge;
  534.         }
  535. }
  536.  
  537. void CParticleContainer::SyncUpdateParticles()
  538. {
  539.         GetMain().SyncUpdateParticlesJob();
  540.         UpdateParticles();
  541. }
  542.  
  543. void CParticleContainer::UpdateParticleStates(SParticleUpdateContext& context)
  544. {
  545.         const ResourceParticleParams& params = GetParams();
  546.         float fLifetimeCheck = params.bRemainWhileVisible ? -fHUGE : 0.f;
  547.  
  548.         // Emit new particles.
  549.         if ((context.nEnvFlags & REN_SORT) && context.nSortQuality > 0)
  550.         {
  551.                 // Allocate particle sort helper array. Overestimate size from current + expected emitted count.
  552.                 int nMaxParticles = GetMaxParticleCount(context);
  553.                 STACK_ARRAY(SParticleUpdateContext::SSortElem, aParticleSort, nMaxParticles);
  554.                 context.aParticleSort.set(aParticleSort, nMaxParticles);
  555.         }
  556.  
  557.         SParticleUpdateContext::SSortElem* pElem = context.aParticleSort.begin();
  558.         for (auto& part : m_Particles)
  559.         {
  560.                 if (!part.IsAlive(fLifetimeCheck))
  561.                 {
  562.                         if (part.NumRefs() == 0)
  563.                         {
  564.                                 m_Particles.erase(&part);
  565.                                 continue;
  566.                         }
  567.                         else
  568.                         {
  569.                                 part.Hide();
  570.                                 m_Counts.ParticlesReject++;
  571.                         }
  572.                 }
  573.                 else
  574.                 {
  575.                         part.Update(context, context.fUpdateTime);
  576.                 }
  577.  
  578.                 if (!context.aParticleSort.empty())
  579.                 {
  580.                         assert(pElem < context.aParticleSort.end());
  581.                         pElem->pPart = &part;
  582.                         pElem->fDist = -part.GetMinDist(context.vMainCamPos);
  583.  
  584.                         if (context.nSortQuality == 1 && pElem > context.aParticleSort.begin() && pElem->fDist < pElem[-1].fDist)
  585.                         {
  586.                                 // Force progressive sort order.
  587.                                 pElem->fDist = pElem[-1].fDist;
  588.                         }
  589.                         pElem++;
  590.                 }
  591.         }
  592.  
  593.         // Emit new particles.
  594.         UpdateEmitters(&context);
  595. }
  596.  
  597. void CParticleContainer::UpdateEmitters(SParticleUpdateContext* pUpdateContext)
  598. {
  599.         // Remove finished emitters; those with effects components removed in UpdateEffects, main thread.
  600.         bool bEraseEmitters = !(m_nEnvFlags & EFF_ANY) && IsIndirect();
  601.  
  602.         for (auto& e : m_Emitters)
  603.         {
  604.                 e.UpdateState();
  605.                 if (pUpdateContext)
  606.                 {
  607.                         // Emit particles.
  608.  
  609.                         // Emit deferred particles first.
  610.                         // Array will only be present for 1st container update, which should have just one emitter.
  611.                         for (auto& emit : m_DeferredEmitParticles)
  612.                         {
  613.                                 if (!emit.bHasLocation)
  614.                                         emit.Location = e.GetSource().GetLocation();
  615.                                 e.EmitParticle(*pUpdateContext, emit);
  616.                         }
  617.  
  618.                         // Emit regular particles.
  619.                         if (e.GetSource().IsAlive(-pUpdateContext->fUpdateTime))
  620.                                 e.EmitParticles(*pUpdateContext);
  621.                 }
  622.  
  623.                 if (bEraseEmitters && !e.GetSource().IsAlive() && e.NumRefs() == 0)
  624.                         m_Emitters.erase(&e);
  625.         }
  626. }
  627.  
  628. void CParticleContainer::UpdateEffects()
  629. {
  630.         for (auto& e : m_Emitters)
  631.         {
  632.                 if (e.GetSource().IsAlive())
  633.                 {
  634.                         e.UpdateState();
  635.                         if (m_nEnvFlags & EFF_FORCE)
  636.                                 e.UpdateForce();
  637.                         if (m_nEnvFlags & EFF_AUDIO)
  638.                                 e.UpdateAudio();
  639.                 }
  640.                 else
  641.                 {
  642.                         // Emitters with effects removed here.
  643.                         e.Deactivate();
  644.                         if (IsIndirect() && e.NumRefs() == 0)
  645.                                 m_Emitters.erase(&e);
  646.                 }
  647.         }
  648. }
  649.  
  650. /* Water sorting / filtering:
  651.  
  652.                   Effect:::::::::::::::::::::::::::
  653.    Emitter      Camera          above                                   both                                    below
  654.    -------      ------          -----                                   ----                                    -----
  655.    above                above                   AFTER                                   AFTER                                   skip
  656.         below                   BEFORE                          BEFORE                          skip
  657.  
  658.    both         above                   AFTER\below             AFTER[\below]   BEFORE\above
  659.         below                   BEFORE\below    AFTER[\above]   AFTER\above
  660.  
  661.    below                above                   skip                                    BEFORE                          BEFORE
  662.         below                   skip                                    AFTER                                   AFTER
  663.  */
  664. void CParticleContainer::Render(SRendParams const& RenParams, SPartRenderParams const& PRParams, const SRenderingPassInfo& passInfo)
  665. {
  666.         FUNCTION_PROFILER_CONTAINER(this);
  667.  
  668.         const ResourceParticleParams* pParams = m_pParams;
  669.  
  670.         //It doesn't look like the nEnvFlags prefetch will be much use due its use almost immediately afterwards, but we're often cache
  671.         //      missing on pParams->bEnabled, so that will give time for the cache line to be fetched
  672.         PrefetchLine(&pParams->nEnvFlags, 0);
  673.         PrefetchLine(&m_bbWorld, 0);
  674.         PrefetchLine(pParams, 128);
  675.  
  676.         // Individual container distance culling.
  677.         const float fDistSq = m_bbWorld.GetDistanceSqr(passInfo.GetCamera().GetPosition());
  678.         float fMaxDist = GetEffect()->GetMaxParticleSize() * pParams->fViewDistanceAdjust * PRParams.m_fMaxAngularDensity;
  679.  
  680.         if (fDistSq > sqr(fMaxDist) && !pParams->fMinPixels)
  681.                 return;
  682.  
  683.         IF (pParams->fCameraMaxDistance, 0)
  684.         {
  685.                 float fFOV = passInfo.GetCamera().GetFov();
  686.                 if (fDistSq * sqr(fFOV) > sqr(pParams->fCameraMaxDistance))
  687.                         return;
  688.         }
  689.  
  690.         // Water and indoor culling tests.
  691.         if (!(pParams->tVisibleIndoors * GetMain().GetVisEnviron().OriginIndoors()))
  692.                 return;
  693.         if (!(pParams->tVisibleUnderwater * GetMain().GetPhysEnviron().m_tUnderWater))
  694.                 return;
  695.  
  696.         uint32 nRenderFlags = GetEnvironmentFlags() & PRParams.m_nRenFlags;
  697.  
  698.         if (!passInfo.IsAuxWindow())
  699.         {
  700.                 if (!m_nNeedJobUpdate)
  701.                         // Was not scheduled for threaded update, so sync existing emitter update before creating new job.
  702.                         GetMain().SyncUpdateParticlesJob();
  703.  
  704.                 // Flag this and all parents for update this frame; Set count to 2, to automatically prime updates at start of next frame too.
  705.                 if (GetCVars()->e_ParticlesThread)
  706.                 {
  707.                         for (CParticleContainer* pCont = this; pCont; pCont = pCont->GetParent())
  708.                                 pCont->SetNeedJobUpdate(2);
  709.                 }
  710.                 else
  711.                 {
  712.                         UpdateParticles();
  713.                 }
  714.         }
  715.         else
  716.         {
  717.                 SyncUpdateParticles();
  718.         }
  719.  
  720.         if (nRenderFlags & REN_LIGHTS)
  721.         {
  722.                 SyncUpdateParticles();
  723.                 RenderLights(RenParams, passInfo);
  724.         }
  725.  
  726.         if (nRenderFlags & REN_GEOMETRY)
  727.         {
  728.                 SyncUpdateParticles();
  729.                 RenderGeometry(RenParams, passInfo);
  730.         }
  731.         else if (nRenderFlags & REN_DECAL)
  732.         {
  733.                 SyncUpdateParticles();
  734.                 RenderDecals(passInfo);
  735.         }
  736.         else if (nRenderFlags & REN_SPRITE)
  737.         {
  738.                 // Copy pre-computed render and state flags.
  739.                 uint64 nObjFlags = pParams->nRenObjFlags & PRParams.m_nRenObjFlags;
  740.  
  741.                 IF (pParams->eFacing == pParams->eFacing.Water, 0)
  742.                 {
  743.                         // Water-aligned particles are always rendered before water, to avoid intersection artifacts
  744.                         if (passInfo.IsRecursivePass())
  745.                                 return;
  746.                         IF (Get3DEngine()->GetOceanRenderFlags() & OCR_NO_DRAW, 0)
  747.                                 return;
  748.                 }
  749.                 else
  750.                 {
  751.                         // Other particles determine whether to render before or after water, or both
  752.                         bool bCameraUnderWater = passInfo.IsCameraUnderWater() ^ passInfo.IsRecursivePass();
  753.                         if (!passInfo.IsRecursivePass() && pParams->tVisibleUnderwater == ETrinary() && GetMain().GetPhysEnviron().m_tUnderWater == ETrinary())
  754.                         {
  755.                                 // Must render in 2 passes.
  756.                                 if (!(nObjFlags & FOB_AFTER_WATER))
  757.                                 {
  758.                                         SPartRenderParams PRParamsAW = PRParams;
  759.                                         PRParamsAW.m_nRenObjFlags |= FOB_AFTER_WATER;
  760.                                         Render(RenParams, PRParamsAW, passInfo);
  761.                                 }
  762.                         }
  763.                         else if (pParams->tVisibleUnderwater * bCameraUnderWater
  764.                                  && GetMain().GetPhysEnviron().m_tUnderWater * bCameraUnderWater)
  765.                         {
  766.                                 nObjFlags |= FOB_AFTER_WATER;
  767.                         }
  768.                 }
  769.  
  770.                 const uint threadId = passInfo.ThreadID();
  771.                 CRenderObject** pCachedRO = nullptr;
  772.                 if (passInfo.IsRecursivePass())
  773.                         pCachedRO = m_pRecursiveRO;
  774.                 else if (nObjFlags & FOB_AFTER_WATER)
  775.                         pCachedRO = m_pAfterWaterRO;
  776.                 else
  777.                         pCachedRO = m_pBeforeWaterRO;
  778.                 CRenderObject* pRenderObject = pCachedRO[threadId];
  779.                 if (!pRenderObject)
  780.                 {
  781.                         pRenderObject = CreateRenderObject(nObjFlags);
  782.                         pCachedRO[threadId] = pRenderObject;
  783.                 }
  784.  
  785.                 SAddParticlesToSceneJob& job = CParticleManager::Instance()->GetParticlesToSceneJob(passInfo);
  786.                 job.pPVC = this;
  787.                 job.pRenderObject = pRenderObject;
  788.                 SRenderObjData* pOD = job.pRenderObject->GetObjData();
  789.  
  790.                 if (nObjFlags & FOB_OCTAGONAL)
  791.                 {
  792.                         // Size threshold for octagonal rendering.
  793.                         static const float fOCTAGONAL_PIX_THRESHOLD = 20;
  794.                         if (sqr(pParams->fSize.GetMaxValue() * pParams->fFillRateCost * passInfo.GetCamera().GetViewSurfaceZ())
  795.                             < sqr(fOCTAGONAL_PIX_THRESHOLD * passInfo.GetCamera().GetFov()) * fDistSq)
  796.                                 nObjFlags &= ~FOB_OCTAGONAL;
  797.                 }
  798.                
  799.                 job.pRenderObject->m_ObjFlags = (nObjFlags & ~0xFF) | RenParams.dwFObjFlags;
  800.  
  801.                 pOD->m_FogVolumeContribIdx = PRParams.m_nFogVolumeContribIdx;
  802.  
  803.                 pOD->m_LightVolumeId = PRParams.m_nDeferredLightVolumeId;
  804.  
  805.                 if (GetMain().m_pTempData)
  806.                         *((Vec4*)&pOD->m_fTempVars[0]) = GetMain().m_pTempData->userData.vEnvironmentProbeMults;
  807.                 else
  808.                         *((Vec4*)&pOD->m_fTempVars[0]) = Vec4(1.0f, 1.0f, 1.0f, 1.0f);
  809.                 ;
  810.  
  811.                 // Set sort distance based on params and bounding box.
  812.                 if (pParams->fSortBoundsScale == PRParams.m_fMainBoundsScale)
  813.                         job.pRenderObject->m_fDistance = PRParams.m_fCamDistance;
  814.                 else
  815.                         job.pRenderObject->m_fDistance = GetMain().GetNearestDistance(passInfo.GetCamera().GetPosition(), pParams->fSortBoundsScale);
  816.                 job.pRenderObject->m_fDistance += pParams->fSortOffset;
  817.  
  818.                 //
  819.                 // Set remaining SAddParticlesToSceneJob data.
  820.                 //
  821.  
  822.                 job.pShaderItem = &pParams->pMaterial->GetShaderItem();
  823.                 if (job.pShaderItem->m_pShader && (job.pShaderItem->m_pShader->GetFlags() & EF_REFRACTIVE))
  824.                         SetScreenBounds(passInfo.GetCamera(), pOD->m_screenBounds);
  825.                 if (pParams->fTexAspect == 0.f)
  826.                         non_const(*m_pParams).UpdateTextureAspect();
  827.  
  828.                 job.nCustomTexId = RenParams.nTextureID;
  829.  
  830.                 passInfo.GetIRenderView()->AddPermanentObject(
  831.                         pRenderObject,
  832.                         passInfo);
  833.         }
  834. }
  835.  
  836. CRenderObject* CParticleContainer::CreateRenderObject(uint64 nObjFlags)
  837. {
  838.         const ResourceParticleParams* pParams = m_pParams;
  839.         CRenderObject* pRenderObject = gEnv->pRenderer->EF_GetObject();
  840.         SRenderObjData* pOD = pRenderObject->GetObjData();
  841.  
  842.         pRenderObject->m_pRE = gEnv->pRenderer->EF_CreateRE(eDATA_Particle);
  843.         pRenderObject->m_II.m_Matrix.SetIdentity();
  844.         pRenderObject->m_RState = uint8(nObjFlags);
  845.         pOD->m_pParticleShaderData = &GetEffect()->GetParams().ShaderData;
  846.  
  847.         IF(!!pParams->fHeatScale, 0)
  848.         {
  849.                 pOD->m_nVisionScale = MAX_HEATSCALE;
  850.                 uint32 nHeatAmount = pParams->fHeatScale.GetStore();
  851.                 pOD->m_nVisionParams = (nHeatAmount << 24) | (nHeatAmount << 16) | (nHeatAmount << 8) | (0);
  852.         }
  853.  
  854.         pRenderObject->m_ParticleObjFlags = (pParams->bHalfRes ? CREParticle::ePOF_HALF_RES : 0)
  855.                 | (pParams->bVolumeFog ? CREParticle::ePOF_VOLUME_FOG : 0);
  856.  
  857.         return pRenderObject;
  858. }
  859.  
  860. void CParticleContainer::ResetRenderObjects()
  861. {
  862.         for (uint threadId = 0; threadId < RT_COMMAND_BUF_COUNT; ++threadId)
  863.         {
  864.                 if (m_pAfterWaterRO[threadId])
  865.                 {
  866.                         if (m_pAfterWaterRO[threadId]->m_pRE)
  867.                                 m_pAfterWaterRO[threadId]->m_pRE->Release();
  868.                         gEnv->pRenderer->EF_FreeObject(m_pAfterWaterRO[threadId]);
  869.                 }
  870.                 if (m_pBeforeWaterRO[threadId])
  871.                 {
  872.                         if (m_pBeforeWaterRO[threadId]->m_pRE)
  873.                                 m_pBeforeWaterRO[threadId]->m_pRE->Release();
  874.                         gEnv->pRenderer->EF_FreeObject(m_pBeforeWaterRO[threadId]);
  875.                 }
  876.                 if (m_pRecursiveRO[threadId])
  877.                 {
  878.                         if (m_pRecursiveRO[threadId]->m_pRE)
  879.                                 m_pRecursiveRO[threadId]->m_pRE->Release();
  880.                         gEnv->pRenderer->EF_FreeObject(m_pRecursiveRO[threadId]);
  881.                 }
  882.                 m_pAfterWaterRO[threadId] = nullptr;
  883.                 m_pBeforeWaterRO[threadId] = nullptr;
  884.                 m_pRecursiveRO[threadId] = nullptr;
  885.         }
  886. }
  887.  
  888. void CParticleContainer::SetScreenBounds(const CCamera& cam, uint8 aScreenBounds[4])
  889. {
  890.         const int32 align16 = (16 - 1);
  891.         const int32 shift16 = 4;
  892.         int iOut[4];
  893.         int nWidth = cam.GetViewSurfaceX();
  894.         int nHeight = cam.GetViewSurfaceZ();
  895.         AABB posAABB = AABB(cam.GetPosition(), 1.00f);
  896.         if (!posAABB.IsIntersectBox(m_bbWorld))
  897.         {
  898.                 cam.CalcScreenBounds(&iOut[0], &m_bbWorld, nWidth, nHeight);
  899.                 if (((iOut[2] - iOut[0]) == nWidth) && ((iOut[3] - iOut[1]) == nHeight))
  900.                 {
  901.                         iOut[2] += 16;  // Split fullscreen particles and fullscreen geometry. Better to use some sort of ID/Flag, but this will do the job for now
  902.                 }
  903.                 aScreenBounds[0] = min(iOut[0] >> shift16, (int32)255);
  904.                 aScreenBounds[1] = min(iOut[1] >> shift16, (int32)255);
  905.                 aScreenBounds[2] = min((iOut[2] + align16) >> shift16, (int32)255);
  906.                 aScreenBounds[3] = min((iOut[3] + align16) >> shift16, (int32)255);
  907.         }
  908.         else
  909.         {
  910.                 aScreenBounds[0] = 0;
  911.                 aScreenBounds[1] = 0;
  912.                 aScreenBounds[2] = min((nWidth >> shift16) + 1, (int32)255);
  913.                 aScreenBounds[3] = min((nHeight >> shift16) + 1, (int32)255);
  914.         }
  915. }
  916.  
  917. bool CParticleContainer::NeedsUpdate() const
  918. {
  919.         return GetMain().GetAge() > m_fAgeLastUpdate;
  920. }
  921.  
  922. void CParticleContainer::SetDynamicBounds()
  923. {
  924.         m_nEnvFlags |= EFF_DYNAMIC_BOUNDS;
  925.         for (CParticleContainer* pParent = m_pParentContainer; pParent; pParent = pParent->GetParent())
  926.                 pParent->m_nChildFlags |= EFF_DYNAMIC_BOUNDS;
  927.         GetMain().AddEnvFlags(EFF_DYNAMIC_BOUNDS);
  928. }
  929.  
  930. float CParticleContainer::GetMaxParticleScale() const
  931. {
  932.         return (m_pParentContainer && GetParams().bMoveRelativeEmitter.ScaleWithSize() ?
  933.                 m_pParentContainer->GetEffect()->GetMaxParticleSize() : 1.f)
  934.                * GetMain().GetParticleScale();
  935. }
  936.  
  937. float CParticleContainer::GetEmitterLife() const
  938. {
  939.         float fEmitterLife = GetMain().GetStopAge();
  940.         if (!m_pParentContainer)
  941.         {
  942.                 if (!m_pParams->fPulsePeriod)
  943.                 {
  944.                         if (!m_Emitters.empty())
  945.                                 fEmitterLife = min(fEmitterLife, m_Emitters.front().GetStopAge());
  946.                         else
  947.                                 fEmitterLife = 0.f;
  948.                 }
  949.         }
  950.         else
  951.         {
  952.                 fEmitterLife = min(fEmitterLife, m_pParentContainer->GetContainerLife());
  953.                 if (!m_pParams->bContinuous)
  954.                         fEmitterLife = min(fEmitterLife, m_pParentContainer->GetEmitterLife() + m_pParams->GetMaxSpawnDelay());
  955.         }
  956.         return fEmitterLife;
  957. }
  958.  
  959. int CParticleContainer::GetMaxParticleCount(const SParticleUpdateContext& context) const
  960. {
  961.         const ResourceParticleParams& params = GetParams();
  962.  
  963.         // Start count is existing number of particles plus deferred
  964.         int nCount = m_Particles.size() + m_DeferredEmitParticles.size();
  965.  
  966.         // Quickly (over)estimate max emissions for this frame
  967.         float fEmitCount = params.fCount.GetMaxValue();
  968.         if (!GetParent())
  969.                 fEmitCount *= GetMain().GetEmitCountScale();
  970.         if (params.eGeometryPieces == params.eGeometryPieces.AllPieces && params.pStatObj != 0)
  971.         {
  972.                 // Emit 1 particle per piece.
  973.                 int nPieces = GetSubGeometryCount(params.pStatObj);
  974.                 fEmitCount *= max(nPieces, 1);
  975.         }
  976.  
  977.         float fEmitTime = min(context.fUpdateTime, GetMaxParticleFullLife());
  978.  
  979.         if (params.bContinuous)
  980.         {
  981.                 // Compute emission rate
  982.                 float fLifeTime = params.GetMaxParticleLife();
  983.                 if (params.fEmitterLifeTime)
  984.                         fLifeTime = min(fLifeTime, params.fEmitterLifeTime(VMIN));
  985.  
  986.                 // Determine time window to update.
  987.                 if (fEmitTime < fLifeTime)
  988.                         fEmitCount *= fEmitTime / fLifeTime;
  989.                 if (params.fMaintainDensity)
  990.                         fEmitCount *= fMAX_DENSITY_ADJUST;
  991.         }
  992.  
  993.         // Account for pulsing
  994.         int nEmitCount = int_ceil(fEmitCount) * m_Emitters.size();
  995.         if (params.fPulsePeriod)
  996.                 nEmitCount *= int_ceil(fEmitTime / max(params.fPulsePeriod(VMIN), 0.1f));
  997.         nCount += nEmitCount;
  998.  
  999.         return nCount;
  1000. }
  1001.  
  1002. void CParticleContainer::UpdateContainerLife(float fAgeAdjust)
  1003. {
  1004.         m_fAgeLastUpdate += fAgeAdjust;
  1005.         m_fAgeStaticBoundsStable += fAgeAdjust;
  1006.  
  1007.         if (CParticleSubEmitter* pDirectEmitter = GetDirectEmitter())
  1008.                 pDirectEmitter->UpdateState(fAgeAdjust);
  1009.         if (m_pParams->bRemainWhileVisible && GetMain().TimeNotRendered() < 0.5f)
  1010.                 m_fContainerLife = GetAge() + 0.5f;
  1011.         else
  1012.         {
  1013.                 m_fContainerLife = GetEmitterLife() + m_pParams->fParticleLifeTime.GetMaxValue();
  1014.                 if (!m_pParams->fParticleLifeTime && m_pParentContainer)
  1015.                         // Zero particle life implies particles live as long as parent emitter lives.
  1016.                         m_fContainerLife += m_pParentContainer->m_fContainerLife;
  1017.         }
  1018. }
  1019.  
  1020. float CParticleContainer::GetAge() const
  1021. {
  1022.         return GetMain().GetAge();
  1023. }
  1024.  
  1025. void CParticleContainer::Reset()
  1026. {
  1027.         // Free all particle and sub-emitter memory.
  1028.         m_Particles.clear();
  1029.         if (IsIndirect())
  1030.                 m_Emitters.clear();
  1031.         m_fAgeLastUpdate = 0.f;
  1032.         m_bbWorldDyn.Reset();
  1033. }
  1034.  
  1035. // Stat functions.
  1036. void CParticleContainer::GetCounts(SParticleCounts& counts) const
  1037. {
  1038.         counts.EmittersAlloc += 1.f;
  1039.         counts.ParticlesAlloc += m_Particles.size();
  1040.  
  1041.         if (GetTimeToUpdate() == 0.f)
  1042.         {
  1043.                 // Was updated this frame.
  1044.                 AddArray(FloatArray(counts), FloatArray(m_Counts));
  1045.                 counts.EmittersActive += 1.f;
  1046.                 counts.ParticlesActive += m_Particles.size();
  1047.                 counts.SubEmittersActive += m_Emitters.size();
  1048.  
  1049.                 if (m_Counts.ParticlesCollideTest)
  1050.                 {
  1051.                         counts.nCollidingEmitters += 1;
  1052.                         counts.nCollidingParticles += (int)m_Counts.ParticlesCollideTest;
  1053.                 }
  1054.  
  1055.                 if ((m_nEnvFlags & REN_ANY) && !m_bbWorldDyn.IsReset())
  1056.                 {
  1057.                         counts.DynamicBoundsVolume += m_bbWorldDyn.GetVolume();
  1058.                         counts.StaticBoundsVolume += m_bbWorld.GetVolume();
  1059.                         if (!m_bbWorld.ContainsBox(m_bbWorldDyn))
  1060.                         {
  1061.                                 AABB bbDynClip = m_bbWorldDyn;
  1062.                                 bbDynClip.ClipToBox(m_bbWorld);
  1063.                                 float fErrorVol = m_bbWorldDyn.GetVolume() - bbDynClip.GetVolume();
  1064.                                 counts.ErrorBoundsVolume += fErrorVol;
  1065.                         }
  1066.                 }
  1067.         }
  1068. }
  1069.  
  1070. void CParticleContainer::GetMemoryUsage(ICrySizer* pSizer) const
  1071. {
  1072.         m_Particles.GetMemoryUsagePlain(pSizer);
  1073.         pSizer->AddObject(&m_Particles, CParticle::GetAllocationSize(this) * m_Particles.size());
  1074.         m_Emitters.GetMemoryUsage(pSizer);
  1075.         m_DeferredEmitParticles.GetMemoryUsagePlain(pSizer);
  1076.         m_ExternalStatObjs.GetMemoryUsagePlain(pSizer);
  1077. }
  1078.  
  1079. void CParticleContainer::OffsetPosition(const Vec3& delta)
  1080. {
  1081.         m_bbWorld.Move(delta);
  1082.         m_bbWorldStat.Move(delta);
  1083.         m_bbWorldDyn.Move(delta);
  1084.  
  1085.         for (auto& e : m_Emitters)
  1086.                 e.OffsetPosition(delta);
  1087.  
  1088.         for (auto& p : m_Particles)
  1089.                 p.OffsetPosition(delta);
  1090. }
  1091.  
  1092. void CParticleContainer::OnHidden()
  1093. {
  1094.         for (auto& e : m_Emitters)
  1095.         {
  1096.                 e.Deactivate();
  1097.         }
  1098. }
  1099.  
  1100. void CParticleContainer::OnUnhidden()
  1101. {
  1102.         if (m_nEnvFlags & EFF_AUDIO)
  1103.         {
  1104.                 for (auto& e : m_Emitters)
  1105.                 {
  1106.                         e.UpdateAudio();
  1107.                 }
  1108.         }
  1109. }
  1110.  
downloadParticleContainer.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