BVB Source Codes

CRYENGINE Show Particle.cpp Source code

Return Download CRYENGINE: download Particle.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:   Particle.cpp
  5. //  Created:     28/5/2001 by Vladimir Kajalin
  6. //  Modified:    11/3/2005 by Scott Peter
  7. //  Compilers:   Visual Studio.NET
  8. // -------------------------------------------------------------------------
  9. //  History:
  10. //
  11. ////////////////////////////////////////////////////////////////////////////
  12.  
  13. #include "StdAfx.h"
  14.  
  15. #include "ParticleSystem/ParticleCommon.h"
  16. #include "Particle.h"
  17. #include "ParticleEmitter.h"
  18. #include "ParticleContainer.h"
  19. #include "terrain.h"
  20. #include <CryMath/GeomQuery.h>
  21. #include "MatMan.h"
  22.  
  23. CRY_PFX2_DBG
  24.  
  25. #define fMAX_NONUNIFORM_TRAVEL 0.25f
  26. #define fCOLLIDE_BUFFER_DIST   0.001f
  27. #define fATTACH_BUFFER_DIST    0.01f
  28. #define fMIN_TEST_AHEAD_MULT   2.5f
  29. #define fSLIDE_TEST_AHEAD_TIME 0.01f
  30.  
  31. namespace
  32. {
  33.  
  34. // A measure of how far a midpoint is from the line.
  35. // Return square of double triangle area.
  36. float InterpVariance(Vec3 const& v0, Vec3 const& v1, Vec3 const& v2)
  37. {
  38.         Vec3 v02 = v2 - v0;
  39.         Vec3 v01 = v1 - v0;
  40.         float fT = max(div_min(v01 * v02, v02.len2(), 1.f), 0.f);
  41.         return (v02 * fT - v01).len2();
  42. }
  43.  
  44. float InterpVariance(QuatTS const& qt0, QuatTS const& qt1, QuatTS const& qt2)
  45. {
  46.         return InterpVariance(qt0.t, qt1.t, qt2.t)
  47.                + InterpVariance(qt0.q.GetColumn2() * qt0.s, qt1.q.GetColumn2() * qt1.s, qt2.q.GetColumn2() * qt2.s);
  48. }
  49.  
  50. float Adjust(float fDensity, float fReduce)
  51. {
  52.         return 1.0f / (1.0f + (fDensity - 1.0f) * fReduce);
  53. }
  54.  
  55. }
  56.  
  57. //////////////////////////////////////////////////////////////////////////
  58. struct STargetForces : SPhysForces
  59. {
  60.         ParticleTarget target;
  61.  
  62.         STargetForces()
  63.                 : SPhysForces(ZERO) {}
  64. };
  65.  
  66. //////////////////////////////////////////////////////////////////////////
  67. // CParticle implementation
  68.  
  69. CParticleSource&              CParticle::GetSource() const
  70. { assert(m_pEmitter && m_pEmitter->NumRefs() > 0); return m_pEmitter->GetSource(); }
  71. CParticleEmitter&             CParticle::GetMain() const
  72. { return GetContainer().GetMain(); }
  73. ResourceParticleParams const& CParticle::GetParams() const
  74. { return GetContainer().GetParams(); }
  75.  
  76. //////////////////////////////////////////////////////////////////////////
  77. float CParticle::TravelSlide(SParticleState& state, SSlideInfo& sliding, float fTime, const Vec3& vExtAccel, float fMaxSlide, float fMinStepTime) const
  78. {
  79.         float fTravelTime = 0.f;
  80.         while (fTime > 0.f)
  81.         {
  82.                 // Convert wind drag to acceleration.
  83.                 float fPenAccel = vExtAccel * sliding.vNormal;
  84.                 if (fPenAccel >= 0.f)
  85.                 {
  86.                         // Not sliding along plane.
  87.                         sliding.ClearSliding(sliding.vNormal);
  88.                         break;
  89.                 }
  90.  
  91.                 GetContainer().GetCounts().ParticlesReiterate += 1.f;
  92.  
  93.                 // There is a force against the sliding plane.
  94.                 // Sliding if velocity is currently into the plane, or max bounce distance is less than the border.
  95.                 Vec3 vAccel = vExtAccel - sliding.vNormal * fPenAccel;
  96.                 float fPenVel = state.m_Vel.vLin * sliding.vNormal;
  97.                 state.m_Vel.vLin -= sliding.vNormal * fPenVel;
  98.                 float fFrictionAccel = sliding.fFriction * fPenAccel;
  99.                 float fVV = state.m_Vel.vLin.GetLengthSquared();
  100.                 if (fVV < FLT_EPSILON)
  101.                 {
  102.                         // Reduce acceleration by friction.
  103.                         float fAA = vAccel.GetLengthSquared();
  104.                         if (fAA <= sqr(fFrictionAccel))
  105.                         {
  106.                                 // No acceleration to overcome friction.
  107.                                 fTravelTime += fTime;
  108.                                 break;
  109.                         }
  110.                         vAccel *= 1.f + fFrictionAccel * isqrt_tpl(fAA);
  111.                 }
  112.                 else
  113.                         // Reduce acceleration in velocity direction.
  114.                         vAccel += state.m_Vel.vLin * (isqrt_tpl(fVV) * fFrictionAccel);
  115.  
  116.                 // Clamp based on estimated slide deviation.
  117.                 float fStepTime = fTime;
  118.                 float fMaxSlideTime = sqrt_fast_tpl(-2.f * fMaxSlide / fPenAccel);
  119.                 float fMaxTime = max(fMaxSlideTime - sliding.fSlidingTime, fMinStepTime);
  120.                 if (fMaxTime > FLT_EPSILON)
  121.                 {
  122.                         fStepTime = min(fStepTime, fMaxTime);
  123.  
  124.                         // Detect slide stoppage, and limit travel time.
  125.                         float fVA = state.m_Vel.vLin * vAccel;
  126.                         if (fVV < -fVA * fStepTime)
  127.                         {
  128.                                 // Stopped.
  129.                                 fStepTime = -fVV / fVA;
  130.                                 state.m_Loc.t += state.m_Vel.vLin * fStepTime + vAccel * (fStepTime * fStepTime * 0.5f);
  131.                                 state.m_Vel.vLin.zero();
  132.                                 state.m_Vel.vRot.zero();
  133.                                 fTravelTime += fTime;
  134.                                 fTime = 0.f;
  135.                         }
  136.                         else
  137.                         {
  138.                                 state.m_Loc.t += state.m_Vel.vLin * fStepTime - vAccel * (fStepTime * fStepTime * 0.5f);
  139.                                 state.m_Vel.vLin += vAccel * fStepTime;
  140.                                 state.m_Vel.vRot *= fStepTime * div_min(-fVA, fVV, 1.f);
  141.                                 fTravelTime += fStepTime;
  142.                                 fTime -= fStepTime;
  143.                         }
  144.  
  145.                         sliding.fSlidingTime += fStepTime;
  146.                 }
  147.  
  148.                 if (sliding.fSlidingTime >= fMaxSlideTime - 1e-6f)
  149.                 {
  150.                         // Require retest against entity.
  151.                         ray_hit hit;
  152.                         Vec3 vTest = vExtAccel.GetNormalized(fMaxSlide);
  153.                         GetContainer().GetCounts().ParticlesClip += 1.f;
  154.                         if (!SPhysEnviron::PhysicsCollision(hit, state.m_Loc.t - vTest, state.m_Loc.t + vTest,
  155.                                                             fCOLLIDE_BUFFER_DIST, sliding.pEntity ? ENV_COLLIDE_PHYSICS : ENV_TERRAIN, sliding.pEntity))
  156.                         {
  157.                                 sliding.Clear();
  158.                                 break;
  159.                         }
  160.  
  161.                         float fPenPos = (state.m_Loc.t - hit.pt) * hit.n;
  162.                         if (fPenPos < 0.f)
  163.                                 state.m_Loc.t -= fPenPos * hit.n;
  164.                         sliding.vNormal = hit.n;
  165.                         sliding.fSlidingTime = 0.f;
  166.                         float fBounce_;
  167.                         GetCollisionParams(hit.surface_idx, fBounce_, sliding.fFriction);
  168.                 }
  169.         }
  170.  
  171.         return fTravelTime;
  172. }
  173.  
  174. void CParticle::Move(SParticleState& state, float fTime, STargetForces const& forces_ext) const
  175. {
  176.         GetContainer().GetCounts().ParticlesReiterate += 1.f;
  177.  
  178.         ResourceParticleParams const& params = GetParams();
  179.  
  180.         float fRelativeAge = GetRelativeAge(fTime * 0.5f);
  181.  
  182.         SForceParams forces;
  183.         forces.vAccel = params.vAcceleration + forces_ext.vAccel * params.fGravityScale.GetValueFromMod(m_BaseMods.GravityScale, fRelativeAge);
  184.         forces.vWind = forces_ext.vWind * params.fAirResistance.fWindScale;
  185.         forces.fDrag = params.fAirResistance.GetValueFromMod(m_BaseMods.AirResistance, fRelativeAge);
  186.         forces.fStretch = 0.0f;
  187.  
  188.         Vec3 vVelOrig = state.m_Vel.vLin;
  189.  
  190.         if (params.fTurbulence3DSpeed)
  191.         {
  192.                 /*
  193.                    Random acceleration A (wind velocity times air resistance) applied continuously.
  194.                    In time quantum dt, dV = A dt, and dP = A/2 dt^2.
  195.                    In larger time periods, avg dv(t) = a dt sqrt(t/dt), and avg dp(t) = a/2 dt sqrt(t/dt) t.
  196.                    So effective acceleration ae(t) = dv(t) / t = a dt sqrt(t/dt) / t = a sqrt(dt/t)
  197.  
  198.                    Turbulence3DSpeed parameter indicates average velocity change dV in 1 s = dt,
  199.                    therefore, the param is used as the acceleration base, and ae(t) = A / sqrt(t)
  200.                  */
  201.  
  202.                 float fTurb = params.fTurbulence3DSpeed.GetValueFromMod(m_BaseMods.Turbulence3DSpeed, fRelativeAge);
  203.                 fTurb *= isqrt_tpl(fTime);
  204.  
  205.                 Vec3 vTurbulence;
  206.                 for (int a = 0; a < 3; a++)
  207.                 {
  208.                         CChaosKey key(*(uint32*)&state.m_Loc.t[a]);
  209.                         vTurbulence[a] = (key.Jumble(this) * 2.f - 1.f) * fTurb;
  210.                 }
  211.                 forces.vAccel += vTurbulence;
  212.         }
  213.  
  214.         // 2D spiral vortex.
  215.         if (params.fTurbulenceSize * params.fTurbulenceSpeed != 0.f)
  216.         {
  217.                 // Apply exact rotation differences between this and next step.
  218.                 // Do not adjust velocity, this is an artificial spiral motion.
  219.                 // However, GetVisualVelocity() returns the effective velocity with vortex motion.
  220.                 Vec3 vRotate = VortexRotation(state, false);
  221.  
  222.                 Travel::Travel(state.m_Loc.t, state.m_Vel.vLin, fTime, forces);
  223.  
  224.                 state.m_Loc.t += VortexRotation(state, false, fTime) - vRotate;
  225.         }
  226.         else
  227.         {
  228.                 Travel::Travel(state.m_Loc.t, state.m_Vel.vLin, fTime, forces);
  229.         }
  230.  
  231.         // Compute targeting motion.
  232.         if (forces_ext.target.bTarget)
  233.         {
  234.                 state.m_Vel.vLin = (state.m_Vel.vLin + vVelOrig) * 0.5f;
  235.                 TargetMovement(forces_ext.target, state, fTime, fRelativeAge);
  236.         }
  237.  
  238.         if (forces.fDrag * params.fAirResistance.fRotationalDragScale > 0.f)
  239.         {
  240.                 state.m_Vel.vRot *= expf(-forces.fDrag * params.fAirResistance.fRotationalDragScale * fTime);
  241.                 if (state.m_Vel.vRot.GetLengthSquared() == 0.f)
  242.                         state.m_Vel.vRot.zero();
  243.         }
  244. }
  245.  
  246. #if defined(_DEBUG)
  247. // Debug particles out of static bounds
  248. void CParticle::DebugBounds(SParticleState const& state) const
  249. {
  250.         if (GetCVars()->e_ParticlesDebug & AlphaBit('b'))
  251.         {
  252.                 static const Color3B clrDebug(0.99f, 0.01f, 0.15f);
  253.                 AABB const& bbStatic = GetContainer().GetStaticBounds();
  254.                 if (m_BaseMods.Color != clrDebug && !bbStatic.IsReset() && GetContainer().StaticBoundsStable())
  255.                 {
  256.                         AABB bbPart(AABB::RESET);
  257.                         UpdateBounds(bbPart, state);
  258.                         if (!bbStatic.ContainsBox(bbPart))
  259.                         {
  260.                                 AABB bbErr = bbPart;
  261.                                 bbPart.ClipToBox(bbStatic);
  262.                                 bbErr.min -= bbPart.min;
  263.                                 bbErr.max -= bbPart.max;
  264.                                 non_const(*this).m_BaseMods.Color = clrDebug;
  265.                         }
  266.                 }
  267.         }
  268. }
  269. #endif
  270.  
  271. //
  272. // Limit travel time to avoid excessive curvature.
  273. //
  274. float CParticle::MoveLinear(SParticleState& state, SCollisionInfo& coll, float fTime, STargetForces const& forces, float fMaxLinearDev, float fMaxSlideDev, float fMinStepTime) const
  275. {
  276.         // Clamp time based on turbulence params.
  277.  
  278.         if (coll.Sliding.IsSliding())
  279.         {
  280.                 // Move slightly forward, to infer accelerations.
  281.                 SParticleState stateTest = state;
  282.                 Move(stateTest, fSLIDE_TEST_AHEAD_TIME, forces);
  283.  
  284.                 Vec3 vExtAccel = (stateTest.m_Vel.vLin - state.m_Vel.vLin) / fSLIDE_TEST_AHEAD_TIME;
  285.                 float fSlideTime = TravelSlide(state, coll.Sliding, fTime, vExtAccel, fMaxSlideDev, fMinStepTime);
  286.                 if (!coll.Sliding.IsSliding())
  287.                         coll.Hit.Clear();
  288.                 if (fSlideTime > 0.f)
  289.                         return fSlideTime;
  290.         }
  291.  
  292.         SParticleState stateNew = state;
  293.  
  294.         Move(stateNew, fTime, forces);
  295.  
  296.         if (fMaxLinearDev < fHUGE * 0.5)
  297.         {
  298.                 // If curved path encompasses inflection point relative to last collide normal, split in 2.
  299.                 float fNV0 = state.m_Vel.vLin * coll.Sliding.vNormal;
  300.                 float fNV1 = stateNew.m_Vel.vLin * coll.Sliding.vNormal;
  301.                 if (fNV0 * fNV1 < 0.f)
  302.                 {
  303.                         // Stop just after inflection point -- increase threshold for further tests.
  304.                         float fCorrect = abs(fNV0 / (fNV0 - fNV1));
  305.                         fTime *= min(fCorrect + 0.1f, 0.9f);
  306.                         stateNew = state;
  307.                         Move(stateNew, fTime, forces);
  308.                 }
  309.  
  310.                 if (fTime > fMinStepTime)
  311.                 {
  312.                         Vec3 vDVel = stateNew.m_Vel.vLin - state.m_Vel.vLin;
  313.                         Vec3 vDPos = stateNew.m_Loc.t - state.m_Loc.t;
  314.                         float fDistSqr = vDPos.len2();
  315.                         float fDevSqrN = (vDVel.len2() * fDistSqr - sqr(vDVel * vDPos)) * sqr(fTime * 0.125f);
  316.                         if (fDevSqrN > fDistSqr * sqr(fMaxLinearDev))
  317.                         {
  318.                                 // Exceeds linear threshold. Deviation proportional to t
  319.                                 float fCorrect = fMaxLinearDev * isqrt_fast_tpl(fDevSqrN / fDistSqr);
  320.                                 float fNewTime = max(fTime * fCorrect * 0.75f, fMinStepTime);
  321.                                 if (fNewTime < fTime * 0.99f)
  322.                                 {
  323.                                         stateNew = state;
  324.                                         fTime = fNewTime;
  325.                                         Move(stateNew, fTime, forces);
  326.                                 }
  327.                         }
  328.                 }
  329.         }
  330.  
  331.         state = stateNew;
  332.         return fTime;
  333. }
  334.  
  335. bool CParticle::SHitInfo::TestHit(ray_hit& hit, const Vec3& vPos0, const Vec3& vPos1, const Vec3& vVel0, const Vec3& vVel1, float fMaxDev, float fRadius) const
  336. {
  337.         if (!HasPath())
  338.                 return false;
  339.  
  340.         if (HasHit())
  341.         {
  342.                 // Test against collision plane.
  343.                 float fDist0 = (vPos0 - vPos) * vNormal;
  344.                 float fDist1 = (vPos1 - vPos) * vNormal - fRadius;
  345.                 if (fDist1 < 0.f)
  346.                 {
  347.                         if (fDist0 >= 0.f)
  348.                         {
  349.                                 hit.dist = fDist0 / (fDist0 - fDist1);
  350.                                 hit.pt.SetLerp(vPos0, vPos1, hit.dist);
  351.                         }
  352.                         else
  353.                         {
  354.                                 hit.dist = 0.f;
  355.                                 hit.pt = vPos;
  356.                         }
  357.  
  358.                         // Find inflection point.
  359.                         float fV0 = vVel0 * vNormal,
  360.                               fV1 = vVel1 * vNormal;
  361.                         if (fV0 > 0.f && fV1 < 0.f)
  362.                         {
  363.                                 /*
  364.                                    c =  v0 + p0 - p1
  365.                                    d = -v1 - p0 + p1
  366.  
  367.                                    p = p0 * (1-t) + p1 * t + (c * (1-t) + d * t) * t * (1-t) * 4
  368.                                     = p0 + (p1-p0) t + (c + (d-c) t) t(1-t)
  369.                                  */
  370.                                 float fMid = fV0 / (fV0 - fV1);
  371.                                 Vec3 vVelMid;
  372.                                 vVelMid.SetLerp(vVel0, vVel1, fMid);
  373.                                 Vec3 vPosMid;
  374.                                 vPosMid.SetLerp(vPos0, vPos1, fMid);
  375.                                 vPosMid += (vPos0 - vPos1 + vVel0) * (fMid * (1.f - fMid) * (1.f - fMid));
  376.                                 vPosMid += (vPos1 - vPos0 - vVel1) * (fMid * fMid * (1.f - fMid));
  377.                                 float fDistMid = (vPosMid - vPos) * vNormal;
  378.                                 if (fDistMid > 0.f)
  379.                                 {
  380.                                         float fDist = fDistMid / (fDistMid - fDist1);
  381.                                         hit.pt.SetLerp(vPosMid, vPos1, fDist);
  382.                                         hit.dist = fMid + (1.f - fMid) * fDist;
  383.                                         assert(hit.dist >= 0.f && hit.dist <= 1.f);
  384.                                 }
  385.                         }
  386.  
  387.                         if ((hit.pt - vPos).len2() > sqr(fMaxDev))
  388.                         {
  389.                                 hit.dist = 1.f;
  390.                                 return false;
  391.                         }
  392.  
  393.                         hit.n = vNormal;
  394.                         hit.pt += hit.n * fRadius;
  395.                         hit.surface_idx = nSurfaceIdx;
  396.                         hit.pCollider = pEntity;
  397.                         return true;
  398.                 }
  399.         }
  400.  
  401.         // Test path proximity for hit or miss.
  402.         hit.dist = 1.f;
  403.  
  404.         Vec3 vPosRel = vPos1 - vPos;
  405.         float fT = vPosRel * vPathDir;
  406.         if (fT >= -fMaxDev && fT <= fPathLength + fMaxDev)
  407.                 if ((vPathDir * fT - vPosRel).len2() <= sqr(fMaxDev))
  408.                         return true;
  409.  
  410.         return false;
  411. }
  412.  
  413. bool CParticle::CheckCollision(ray_hit& hit, float fStepTime, SParticleUpdateContext const& context, STargetForces const& forces, const SParticleState& stateNew, SCollisionInfo& collNew)
  414. {
  415.         hit.dist = 1.f;
  416.         uint32 nCollideFlags = context.nEnvFlags & ENV_COLLIDE_ANY;
  417.         if (nCollideFlags & ENV_COLLIDE_CACHE && GetCVars()->e_ParticlesObjectCollisions < 3)
  418.         {
  419.                 // Cache collisions ahead in extended path.
  420.                 if (collNew.Hit.TestHit(hit, m_Loc.t, stateNew.m_Loc.t, m_Vel.vLin, stateNew.m_Vel.vLin, fMAX_COLLIDE_DEVIATION, fCOLLIDE_BUFFER_DIST))
  421.                         nCollideFlags &= ~ENV_COLLIDE_CACHE;
  422.                 else
  423.                 {
  424.                         // Path does not match cache.
  425.                         // Require new check.
  426.                         collNew.Hit.Clear();
  427.                         float fTestTime = min((m_fStopAge - m_fAge) * 1.1f, context.fMaxLinearStepTime);
  428.                         if (fTestTime >= fStepTime * fMIN_TEST_AHEAD_MULT)
  429.                         {
  430.                                 // Predict travel ahead, ignoring random movement params.
  431.                                 SParticleState stateTest = *this;
  432.                                 SCollisionInfo collTest;
  433.                                 if (m_pCollisionInfo)
  434.                                         collTest = *m_pCollisionInfo;
  435.                                 else
  436.                                         collTest.Sliding.vNormal = stateTest.m_Vel.vLin.GetNormalized();
  437.  
  438.                                 fTestTime = MoveLinear(stateTest, collTest, fTestTime, forces, fMAX_COLLIDE_DEVIATION, fHUGE, fStepTime);
  439.                                 if (fTestTime >= fStepTime * fMIN_TEST_AHEAD_MULT && stateTest.m_Loc.t != m_Loc.t)
  440.                                 {
  441.                                         ray_hit hit_cache;
  442.                                         GetContainer().GetCounts().ParticlesCollideTest += 1.f;
  443.                                         if (SPhysEnviron::PhysicsCollision(hit_cache, m_Loc.t, stateTest.m_Loc.t, 0.f, nCollideFlags & ENV_COLLIDE_CACHE))
  444.                                                 collNew.Hit.SetHit(m_Loc.t, hit_cache.pt, hit_cache.n, hit_cache.surface_idx, hit_cache.pCollider);
  445.                                         else
  446.                                                 collNew.Hit.SetMiss(m_Loc.t, stateTest.m_Loc.t);
  447.  
  448.                                         if (collNew.Hit.TestHit(hit, m_Loc.t, stateNew.m_Loc.t, m_Vel.vLin, stateNew.m_Vel.vLin, fMAX_COLLIDE_DEVIATION, fCOLLIDE_BUFFER_DIST))
  449.                                                 nCollideFlags &= ~ENV_COLLIDE_CACHE;
  450.                                 }
  451.                         }
  452.                 }
  453.                 if (hit.dist < 1.f)
  454.                         collNew.Clear();
  455. #ifdef _DEBUG
  456.                 else if (!(nCollideFlags & ENV_COLLIDE_CACHE))
  457.                 {
  458.                         int& iDrawHelpers = gEnv->pPhysicalWorld->GetPhysVars()->iDrawHelpers;
  459.                         int iSave = iDrawHelpers;
  460.                         iDrawHelpers = 0;
  461.                         ray_hit hit2;
  462.                         if (SPhysEnviron::PhysicsCollision(hit2, m_Loc.t, stateNew.m_Loc.t, fCOLLIDE_BUFFER_DIST, context.nEnvFlags & ENV_COLLIDE_CACHE))
  463.                                 GetContainer().GetCounts().ParticlesReject += 1.f;
  464.                         iDrawHelpers = iSave;
  465.                 }
  466. #endif
  467.         }
  468.         if (nCollideFlags)
  469.         {
  470.                 ray_hit hit2;
  471.                 GetContainer().GetCounts().ParticlesCollideTest += 1.f;
  472.                 if (SPhysEnviron::PhysicsCollision(hit2, m_Loc.t, stateNew.m_Loc.t, fCOLLIDE_BUFFER_DIST, nCollideFlags))
  473.                         if (hit2.dist < hit.dist)
  474.                                 hit = hit2;
  475.         }
  476.  
  477.         return hit.dist < 1.f;
  478. }
  479.  
  480. void CParticle::Init(SParticleUpdateContext const& context, float fAge, CParticleSubEmitter* pEmitter, const EmitParticleData& data)
  481. {
  482.         assert(pEmitter);
  483.         PREFAST_ASSUME(pEmitter);
  484.  
  485.         pEmitter->AddRef();
  486.         m_pEmitter = pEmitter;
  487.         m_pContainer = &pEmitter->GetContainer();
  488.  
  489.         CParticleContainer& rContainer = GetContainer();
  490.         ResourceParticleParams const& params = rContainer.GetParams();
  491.  
  492.         // Init allocations.
  493.         m_aPosHistory = 0;
  494.         if (int nSteps = rContainer.GetHistorySteps())
  495.         {
  496.                 if ((m_aPosHistory = (SParticleHistory*) ParticleObjectAllocator().Allocate(sizeof(SParticleHistory) * nSteps)))
  497.                 {
  498.                         for (int n = 0; n < nSteps; n++)
  499.                                 m_aPosHistory[n].SetUnused();
  500.                 }
  501.         }
  502.  
  503.         m_pCollisionInfo = 0;
  504.         if (rContainer.NeedsCollisionInfo())
  505.         {
  506.                 bool bCollide = RandomActivate(params.fCollisionFraction);
  507.                 if (bCollide && params.fCollisionCutoffDistance)
  508.                 {
  509.                         const float fDistSq = (data.Location.t - gEnv->p3DEngine->GetRenderingCamera().GetPosition()).GetLengthSquared();
  510.                         bCollide = fDistSq <= sqr(params.fCollisionCutoffDistance);
  511.                 }
  512.  
  513.                 if (bCollide)
  514.                 {
  515.                         if ((m_pCollisionInfo = (SCollisionInfo*) ParticleObjectAllocator().Allocate(sizeof(SCollisionInfo))))
  516.                         {
  517.                                 new(m_pCollisionInfo) SCollisionInfo(params.nMaxCollisionEvents);
  518.                         }
  519.                 }
  520.         }
  521.  
  522.         // Init all base values.
  523.         float fEmissionStrength = pEmitter->GetStrength(-fAge);
  524.  
  525.         m_fAge = 0.f;
  526.         if (!params.fParticleLifeTime)
  527.                 m_fStopAge = max(pEmitter->GetStopAge() - GetSource().GetAge(), 0.f);
  528.         else
  529.                 m_fStopAge = params.fParticleLifeTime(VRANDOM, fEmissionStrength) * Adjust(context.fDensityAdjust, params.fMaintainDensity.fReduceLifeTime);
  530.  
  531.         // Init base mods.
  532.         m_BaseMods.Size = params.fSize.GetVarMod(fEmissionStrength) * Adjust(context.fDensityAdjust, params.fMaintainDensity.fReduceSize);
  533.         m_BaseMods.Aspect = params.fAspect.GetVarMod(fEmissionStrength);
  534.         m_BaseMods.PivotX = params.fPivotX.GetVarMod(fEmissionStrength);
  535.         m_BaseMods.PivotY = params.fPivotY.GetVarMod(fEmissionStrength);
  536.         m_BaseMods.StretchOrTail = params.fTailLength ? params.fTailLength.GetVarMod(fEmissionStrength) : params.fStretch.GetVarMod(fEmissionStrength);
  537.  
  538.         m_BaseMods.AirResistance = params.fAirResistance.GetVarMod(fEmissionStrength);
  539.         m_BaseMods.GravityScale = params.fGravityScale.GetVarMod(fEmissionStrength);
  540.         m_BaseMods.Turbulence3DSpeed = params.fTurbulence3DSpeed.GetVarMod(fEmissionStrength);
  541.         m_BaseMods.TurbulenceSize = params.fTurbulenceSize.GetVarMod(fEmissionStrength);
  542.         m_BaseMods.TurbulenceSpeed = params.fTurbulenceSpeed.GetVarMod(fEmissionStrength);
  543.         m_BaseMods.fTargetRadius = params.TargetAttraction.fRadius.GetVarMod(fEmissionStrength);
  544.  
  545.         m_BaseMods.LightSourceIntensity = params.LightSource.fIntensity.GetVarMod(fEmissionStrength);
  546.         m_BaseMods.LightSourceRadius = params.LightSource.fRadius.GetVarMod(fEmissionStrength);
  547.  
  548.         m_BaseMods.Color = params.cColor.GetVarMod(fEmissionStrength);
  549.         m_BaseMods.Alpha = params.fAlpha.GetVarMod(fEmissionStrength) * Adjust(context.fDensityAdjust, params.fMaintainDensity.fReduceAlpha);
  550.  
  551.         m_nEmitterSequence = pEmitter->GetSequence();
  552.  
  553.         // Tile variant is randomized per stream for connected particles, per particle otherwise.
  554.         if (params.Connection)
  555.                 m_nTileVariant = pEmitter->GetChaosKey().Jumble(CChaosKey(&params.TextureTiling.nVariantCount)) * params.TextureTiling.nVariantCount;
  556.         else
  557.                 m_nTileVariant = cry_random(0U, params.TextureTiling.nVariantCount - 1);
  558.         m_nTileVariant = params.TextureTiling.nFirstTile + m_nTileVariant * params.TextureTiling.nAnimFramesCount;
  559.  
  560.         m_bFlippedTexture = RandomActivate(params.TextureTiling.fFlipChance);
  561.  
  562.         Set(data.pStatObj);
  563.         Set(data.pPhysEnt);
  564.  
  565.         IF (data.pPhysEnt && data.pStatObj, false)
  566.         {
  567.                 // Pre-generated physics entity.
  568.                 m_pPhysEnt->AddRef();
  569.                 pe_params_foreign_data pfd;
  570.                 pfd.iForeignData = PHYS_FOREIGN_ID_RIGID_PARTICLE;
  571.                 pfd.pForeignData = data.pPhysEnt;
  572.                 m_pPhysEnt->SetParams(&pfd);
  573.  
  574.                 // Get initial state.
  575.                 GetPhysicsState();
  576.  
  577.                 m_Loc.s = params.fSize.GetMaxValue() * data.Location.s;
  578.         }
  579.         else
  580.         {
  581.                 // Init location and velocity, overriding with data params if specified.
  582.                 if (!data.bHasLocation || !data.bHasVel)
  583.                 {
  584.                         InitPos(context, data.Location, fEmissionStrength);
  585.                 }
  586.                 if (data.bHasLocation)
  587.                 {
  588.                         m_Loc = data.Location;
  589.                 }
  590.                 if (data.bHasVel)
  591.                 {
  592.                         m_Vel = data.Velocity;
  593.                 }
  594.  
  595.                 if (params.ePhysicsType >= params.ePhysicsType.SimplePhysics)
  596.                 {
  597.                         // Emitter-generated physical particles.
  598.                         Physicalize();
  599.                 }
  600.         }
  601.  
  602.         if (data.pStatObj)
  603.                 data.pStatObj->AddRef();
  604.  
  605.         if (m_pPhysEnt && GetMain().GetVisEnviron().OriginIndoors())
  606.         {
  607.                 pe_params_flags pf;
  608.                 pf.flagsOR = pef_ignore_ocean;
  609.                 m_pPhysEnt->SetParams(&pf);
  610.         }
  611.  
  612.         Update(context, fAge, true);
  613. }
  614.  
  615. ////////////////////////////////////////////////////////////////////////////
  616. void CParticle::InitPos(SParticleUpdateContext const& context, QuatTS const& loc, float fEmissionStrength)
  617. {
  618.         ResourceParticleParams const& params = GetParams();
  619.         SpawnParams const& spawnParams = GetMain().GetSpawnParams();
  620.         SPhysEnviron const& PhysEnv = GetMain().GetPhysEnviron();
  621.         const CParticleSource& rSource = GetSource();
  622.  
  623.         // Position and orientation.
  624.         // Use main emitter scale for size, unless MoveRelEmitter, then use parent particle size.
  625.         m_Loc = loc;
  626.         if (!params.bMoveRelativeEmitter.ScaleWithSize())
  627.                 m_Loc.s = GetMain().GetParticleScale();
  628.  
  629.         // Emit geom.
  630.         EGeomType eAttachType = params.eSpawnIndirection ? params.eAttachType : spawnParams.eAttachType;
  631.         if (eAttachType != GeomType_None && rSource.GetEmitGeom())
  632.         {
  633.                 EGeomType eAttachType = params.eSpawnIndirection ? params.eAttachType : spawnParams.eAttachType;
  634.                 EGeomForm eAttachForm = params.eSpawnIndirection ? params.eAttachForm : spawnParams.eAttachForm;
  635.                 PosNorm ran = { m_Loc.t, m_Vel.vLin };
  636.                 bool bCentered = GetContainer().GetParent() && GetContainer().GetParent()->GetParams().IsGeometryCentered();
  637.                 rSource.GetEmitGeom().GetRandomPos(ran, cry_random_next(), eAttachType, eAttachForm, loc, bCentered);
  638.                 m_Loc.t = ran.vPos;
  639.                 m_Vel.vLin = ran.vNorm;
  640.         }
  641.         else
  642.         {
  643.                 // Focus direction.
  644.                 m_Vel.vLin = GetEmitter()->GetEmitFocusDir(loc, fEmissionStrength, params.bFocusRotatesEmitter ? &m_Loc.q : 0);
  645.         }
  646.  
  647.         // Offsets.
  648.         Vec3 vOffset = GenerateOffset(context);
  649.         if (params.bEmitOffsetDir)
  650.         {
  651.                 m_Vel.vLin = m_Loc.q * vOffset.GetNormalized();
  652.         }
  653.         vOffset += params.vPositionOffset;
  654.  
  655.         // To world orientation/scale.
  656.         m_Loc.t += m_Loc.q * vOffset * m_Loc.s;
  657.  
  658.         // Velocity.
  659.         float fPhi = params.fEmitAngle(VRANDOM, fEmissionStrength);
  660.         if ((fPhi > 0.f) && (m_Vel.vLin.len2() > FLT_EPSILON))
  661.         {
  662.                 //
  663.                 // Adjust angle to create a uniform distribution.
  664.                 //
  665.                 //              density distribution d(phi) = sin phi
  666.                 //              cumulative density c(phi) = Int(0,phi) sin x dx = 1 - cos phi
  667.                 //              normalised cn(phi) = (1 - cos phi) / (1 - cos phiMax)
  668.                 //              reverse function phi(cn) = acos_tpl(1 + cn(cos phiMax - 1))
  669.                 //
  670.  
  671.                 float fPhiMax = params.fEmitAngle.GetMaxValue();
  672.                 fPhi /= fPhiMax;
  673.                 fPhi = acos_tpl(1.f + fPhi * (cos_tpl(DEG2RAD(fPhiMax)) - 1.f));
  674.  
  675.                 float fTheta = cry_random(0.0f, DEG2RAD(360));
  676.  
  677.                 float fPhiCS[2], fThetaCS[2];
  678.                 sincos_tpl(fPhi, &fPhiCS[1], &fPhiCS[0]);
  679.                 sincos_tpl(fTheta, &fThetaCS[1], &fThetaCS[0]);
  680.  
  681.                 // Compute perpendicular bases.
  682.                 Vec3 vX;
  683.                 // Must compare against FLT_EPSILON - if y&z are < FLT_EPSILON then the NormalizeFast will give nan's
  684.                 if (fabs_tpl(m_Vel.vLin.z) > FLT_EPSILON)
  685.                         vX(0.f, -m_Vel.vLin.z, m_Vel.vLin.y);
  686.                 else
  687.                         vX(-m_Vel.vLin.y, m_Vel.vLin.x, 0.f);
  688.                 vX.NormalizeFast();
  689.                 Vec3 vY = m_Vel.vLin ^ vX;
  690.  
  691.                 m_Vel.vLin = m_Vel.vLin * fPhiCS[0] + (vX * fThetaCS[0] + vY * fThetaCS[1]) * fPhiCS[1];
  692.         }
  693.  
  694.         // Speed.
  695.         float fSpeed = params.fSpeed(VRANDOM, fEmissionStrength);
  696.         m_Vel.vLin *= fSpeed * spawnParams.fSpeedScale * m_Loc.s;
  697.         if (!params.bMoveRelativeEmitter && params.fInheritVelocity)
  698.                 m_Vel.vLin += rSource.GetVelocityAt(m_Loc.t) * params.fInheritVelocity;
  699.  
  700.         // Size.
  701.         m_Loc.s *= params.fSize.GetValueFromMod(m_BaseMods.Size, 0.f);
  702.  
  703.         // Initial orientation.
  704.         if (context.b3DRotation)
  705.         {
  706.                 // 3D absolute rotation. Compute quat from init angles, convert to world space.
  707.                 Vec3 r = params.vRandomAngles;
  708.                 Vec3 vAngles = DEG2RAD(params.vInitAngles + cry_random_componentwise(-r, r));
  709.                 m_Loc.q = m_Loc.q * Quat::CreateRotationXYZ(Ang3(vAngles));
  710.                 r = params.vRandomRotationRate;
  711.                 m_Vel.vRot = m_Loc.q * DEG2RAD(params.vRotationRate + cry_random_componentwise(-r, r));
  712.                 m_fAngle = 0.f;
  713.         }
  714.         else
  715.         {
  716.                 // 2D relative rotation about Y.
  717.                 m_fAngle = DEG2RAD(params.vInitAngles.y + cry_random(-1.0f, 1.0f) * params.vRandomAngles.y);
  718.                 m_Vel.vRot.y = DEG2RAD(params.vRotationRate.y + cry_random(-1.0f, 1.0f) * params.vRandomRotationRate.y);
  719.         }
  720.  
  721.         // Rotate to correct alignment.
  722.         Plane plWater;
  723.         if (params.eFacing == params.eFacing.Water)
  724.                 GetMain().GetPhysEnviron().GetWaterPlane(plWater, m_Loc.t);
  725.         UpdateAlignment(*this, context, plWater);
  726.         DebugBounds(*this);
  727. }
  728.  
  729. Vec3 CParticle::GenerateOffset(SParticleUpdateContext const& context)
  730. {
  731.         ResourceParticleParams const& params = GetParams();
  732.         Vec3 vOffset;
  733.         for (;; )
  734.         {
  735.                 vOffset.Set(
  736.                   cry_random(-1.0f, 1.0f) * params.vRandomOffset.x,
  737.                   cry_random(-1.0f, 1.0f) * params.vRandomOffset.y,
  738.                   cry_random(-1.0f, 1.0f) * params.vRandomOffset.z);
  739.                 if (params.fOffsetRoundness)
  740.                 {
  741.                         // Loop until random point is within rounded volume.
  742.                         Vec3 vR(max(abs(vOffset.x) - context.vEmitBox.x, 0.f), max(abs(vOffset.y) - context.vEmitBox.y, 0.f), max(abs(vOffset.z) - context.vEmitBox.z, 0.f));
  743.                         if (sqr(vR.x * context.vEmitScale.x) + sqr(vR.y * context.vEmitScale.y) + sqr(vR.z * context.vEmitScale.z) > 1.f)
  744.                                 continue;
  745.                 }
  746.  
  747.                 // Apply inner volume subtraction.
  748.                 if (params.fOffsetInnerFraction)
  749.                 {
  750.                         // Find max possible scaled offset.
  751.                         float fScaleMax = div_min(params.vRandomOffset.x, abs(vOffset.x), div_min(params.vRandomOffset.y, abs(vOffset.y), div_min(params.vRandomOffset.z, abs(vOffset.z), fHUGE)));
  752.                         Vec3 vOffsetMax = vOffset * fScaleMax;
  753.                         if (params.fOffsetRoundness)
  754.                         {
  755.                                 Vec3 vRMax(max(abs(vOffsetMax.x) - context.vEmitBox.x, 0.f), max(abs(vOffsetMax.y) - context.vEmitBox.y, 0.f), max(abs(vOffsetMax.z) - context.vEmitBox.z, 0.f));
  756.                                 float fRMaxSqr = sqr(vRMax.x * context.vEmitScale.x) + sqr(vRMax.y * context.vEmitScale.y) + sqr(vRMax.z * context.vEmitScale.z);
  757.                                 if (fRMaxSqr > 0.f)
  758.                                 {
  759.                                         vRMax *= isqrt_tpl(fRMaxSqr);
  760.                                         for (int a = 0; a < 3; a++)
  761.                                         {
  762.                                                 if (vRMax[a] > 0.f)
  763.                                                         vOffsetMax[a] = (context.vEmitBox[a] + vRMax[a]) * fsgnf(vOffsetMax[a]);
  764.                                         }
  765.                                 }
  766.                         }
  767.  
  768.                         // Interpolate between current ans max offsets.
  769.                         vOffset += (vOffsetMax - vOffset) * params.fOffsetInnerFraction;
  770.                 }
  771.  
  772.                 break;
  773.         }
  774.         return vOffset;
  775. }
  776.  
  777. void CParticle::UpdateBounds(AABB& bb, SParticleState const& state) const
  778. {
  779.         ParticleParams const& params = GetParams();
  780.         float fRadius = state.m_Loc.s * GetBaseRadius();
  781.         bb.Add(state.m_Loc.t, fRadius);
  782.  
  783.         if (m_aPosHistory && m_aPosHistory[0].IsUsed())
  784.         {
  785.                 // Add oldest element.
  786.                 bb.Add(m_aPosHistory[0].Loc.t, fRadius);
  787.         }
  788.         else if (params.fStretch)
  789.         {
  790.                 float fStretch = params.fStretch.GetValueFromMod(m_BaseMods.StretchOrTail, GetRelativeAge());
  791.                 Vec3 vStretch = m_Vel.vLin * fStretch;
  792.                 bb.Add(state.m_Loc.t + vStretch * (1.f + params.fStretch.fOffsetRatio), fRadius);
  793.                 bb.Add(state.m_Loc.t - vStretch * (1.f - params.fStretch.fOffsetRatio), fRadius);
  794.         }
  795. }
  796.  
  797. void CParticle::OffsetPosition(const Vec3& delta)
  798. {
  799.         SMoveState::OffsetPosition(delta);
  800.  
  801.         if (m_aPosHistory)
  802.         {
  803.                 for (SParticleHistory* pPos = m_aPosHistory + GetContainer().GetHistorySteps() - 1; pPos >= m_aPosHistory; pPos--)
  804.                 {
  805.                         pPos->Loc.t += delta;
  806.                 }
  807.         }
  808. }
  809.  
  810. Vec3 CParticle::GetVisualVelocity(SParticleState const& state, float fTime) const
  811. {
  812.         const ParticleParams& params = GetParams();
  813.         if (params.fTurbulenceSize * params.fTurbulenceSpeed != 0.f)
  814.                 return state.m_Vel.vLin + VortexRotation(state, true, fTime);
  815.         else
  816.                 return state.m_Vel.vLin;
  817. }
  818.  
  819. #ifdef PARTICLE_EDITOR_FUNCTIONS
  820. void CParticle::UpdateAllocations(int nPrevHistorySteps)
  821. {
  822.         SParticleHistory* aPrevHist = m_aPosHistory;
  823.         if (int nNewSteps = GetContainer().GetHistorySteps())
  824.         {
  825.                 if ((m_aPosHistory = (SParticleHistory*) ParticleObjectAllocator().Allocate(sizeof(SParticleHistory) * nNewSteps)))
  826.                 {
  827.                         if (aPrevHist)
  828.                                 memcpy(m_aPosHistory, aPrevHist, min(nPrevHistorySteps, nNewSteps) * sizeof(*aPrevHist));
  829.                         for (int n = nPrevHistorySteps; n < nNewSteps; n++)
  830.                                 m_aPosHistory[n].SetUnused();
  831.                 }
  832.         }
  833.         else
  834.                 m_aPosHistory = 0;
  835.  
  836.         ParticleObjectAllocator().Deallocate(aPrevHist, nPrevHistorySteps * sizeof(SParticleHistory));
  837.  
  838.         if (GetContainer().NeedsCollisionInfo() && !m_pCollisionInfo)
  839.         {
  840.                 if ((m_pCollisionInfo = (SCollisionInfo*) ParticleObjectAllocator().Allocate(sizeof(SCollisionInfo))))
  841.                 {
  842.                         new(m_pCollisionInfo) SCollisionInfo(GetParams().nMaxCollisionEvents);
  843.                 }
  844.         }
  845. }
  846. #endif
  847.  
  848. void CParticle::AddPosHistory(SParticleState const& stateNew)
  849. {
  850.         // Possibly store current position in history. Check significance against previous positions and stateNew.
  851.         float fRelativeAge = GetRelativeAge();
  852.         float fTailLength = GetParams().fTailLength.GetValueFromMod(m_BaseMods.StretchOrTail, fRelativeAge);
  853.  
  854.         int nCount = GetContainer().GetHistorySteps();
  855.         while (nCount > 0 && !m_aPosHistory[nCount - 1].IsUsed())
  856.                 nCount--;
  857.  
  858.         // Clear out old entries.
  859.         float fMinAge = m_fAge - fTailLength;
  860.         int nStart = 0;
  861.         while (nStart + 1 < nCount && m_aPosHistory[nStart + 1].fAge < fMinAge)
  862.                 nStart++;
  863.  
  864.         if (nStart > 0)
  865.         {
  866.                 for (int n = nStart; n < nCount; n++)
  867.                         m_aPosHistory[n - nStart] = m_aPosHistory[n];
  868.                 for (int n = nCount - nStart; n < nCount; n++)
  869.                         m_aPosHistory[n].SetUnused();
  870.                 nCount -= nStart;
  871.         }
  872.  
  873.         if (nCount == GetContainer().GetHistorySteps())
  874.         {
  875.                 // Remove least significant entry.
  876.                 int nLeast = nCount;
  877.                 float fLeastSignif = fHUGE;
  878.                 for (int n = 1; n <= nCount; n++)
  879.                 {
  880.                         QuatTS aqts[3];
  881.                         aqts[0] = m_aPosHistory[n - 1].Loc;
  882.  
  883.                         if (n < nCount)
  884.                                 aqts[1] = m_aPosHistory[n].Loc;
  885.                         else
  886.                                 aqts[1] = m_Loc;
  887.  
  888.                         if (n + 1 < nCount)
  889.                                 aqts[2] = m_aPosHistory[n + 1].Loc;
  890.                         else if (n + 1 == nCount)
  891.                                 aqts[2] = m_Loc;
  892.                         else
  893.                                 aqts[2] = stateNew.m_Loc;
  894.  
  895.                         float fSignif = InterpVariance(aqts[0], aqts[1], aqts[2]);
  896.                         if (fSignif < fLeastSignif)
  897.                         {
  898.                                 fLeastSignif = fSignif;
  899.                                 nLeast = n;
  900.                         }
  901.                 }
  902.                 if (nLeast != nCount)
  903.                 {
  904.                         // Delete entry.
  905.                         for (int n = nLeast; n < nCount - 1; n++)
  906.                                 m_aPosHistory[n] = m_aPosHistory[n + 1];
  907.                         nCount--;
  908.                 }
  909.         }
  910.         if (nCount < GetContainer().GetHistorySteps())
  911.         {
  912.                 m_aPosHistory[nCount].fAge = m_fAge;
  913.                 m_aPosHistory[nCount].Loc = m_Loc;
  914.         }
  915. }
  916.  
  917. void CParticle::Update(SParticleUpdateContext const& context, float fFrameTime, bool bNew)
  918. {
  919.         CParticleContainer& rContainer = GetContainer();
  920.         SPhysEnviron const& PhysEnv = GetMain().GetPhysEnviron();
  921.         ResourceParticleParams const& params = rContainer.GetParams();
  922.  
  923.         // Process only up to lifetime of particle, and handle negative initial age.
  924.         if (m_fAge + fFrameTime <= 0.f)
  925.         {
  926.                 m_fAge += fFrameTime;
  927.                 fFrameTime = 0.f;
  928.         }
  929.         else if (m_fAge < 0.f)
  930.         {
  931.                 fFrameTime += m_fAge;
  932.                 m_fAge = 0.f;
  933.         }
  934.         if (!params.bRemainWhileVisible)
  935.         {
  936.                 assert(m_fAge <= m_fStopAge);
  937.                 fFrameTime = min(fFrameTime, m_fStopAge - m_fAge);
  938.         }
  939.  
  940.         if (m_pCollisionInfo && m_pCollisionInfo->Stopped())
  941.         {
  942.                 // Particle is stopped
  943.                 m_fAge += max(fFrameTime, 0.f);
  944.                 return;
  945.         }
  946.  
  947.         ////////////////////////////////////////////////////////////////////////////////////////////////
  948.         // Move
  949.         ////////////////////////////////////////////////////////////////////////////////////////////////
  950.  
  951.         IF (m_pPhysEnt, false)
  952.         {
  953.                 // Use physics engine to move particles.
  954.                 GetPhysicsState();
  955.  
  956.                 // Get collision status.
  957.                 pe_status_collisions status;
  958.                 coll_history_item item;
  959.                 status.pHistory = &item;
  960.                 status.age = 1.f;
  961.                 status.bClearHistory = 1;
  962.  
  963.                 if (m_pPhysEnt->GetStatus(&status) > 0)
  964.                 {
  965.                         Collide();
  966.                         if (m_pCollisionInfo && !m_pCollisionInfo->Collide())
  967.                         {
  968.                                 // Final collision
  969.                                 if (params.eFinalCollision == params.eFinalCollision.Die)
  970.                                 {
  971.                                         // Kill particle on collision
  972.                                         Stop();
  973.                                         return;
  974.                                 }
  975.                                 else if (params.eFinalCollision == params.eFinalCollision.Stop)
  976.                                 {
  977.                                         m_pCollisionInfo->Stop();
  978.                                 }
  979.                         }
  980.                 }
  981.  
  982.         }
  983.         else
  984.         {
  985.                 uint32 nFlags = context.nEnvFlags & ENV_PHYS_AREA;
  986.                 uint32 nCollideFlags = m_pCollisionInfo ? context.nEnvFlags & ENV_COLLIDE_ANY : 0;
  987.                 float fMaxStepTime = fFrameTime;
  988.  
  989.                 // Transform emitter-relative particles by emitter movement; unless just emitted, then already current.
  990.                 if (params.bMoveRelativeEmitter && GetEmitter() && !bNew)
  991.                 {
  992.                         Vec3 vPreTrans;
  993.                         QuatTS qtMove;
  994.                         if (GetEmitter()->GetMoveRelative(vPreTrans, qtMove))
  995.                         {
  996.                                 m_Loc.t += vPreTrans;
  997.                                 Transform(qtMove);
  998.                                 m_Loc.q.Normalize();
  999.                                 if (params.bMoveRelativeEmitter.bMoveTail && m_aPosHistory)
  1000.                                 {
  1001.                                         for (SParticleHistory* pPos = m_aPosHistory + GetContainer().GetHistorySteps() - 1; pPos >= m_aPosHistory; pPos--)
  1002.                                         {
  1003.                                                 if (pPos->IsUsed())
  1004.                                                 {
  1005.                                                         pPos->Loc.t += vPreTrans;
  1006.                                                         pPos->Loc = qtMove * pPos->Loc;
  1007.                                                 }
  1008.                                         }
  1009.                                 }
  1010.                         }
  1011.                 }
  1012.  
  1013.                 // Get particle target.
  1014.                 STargetForces forces;
  1015.                 if (context.bHasTarget)
  1016.                         GetContainer().GetTarget(forces.target, GetEmitter());
  1017.  
  1018.                 while (fFrameTime > 0.f)
  1019.                 {
  1020.                         // Apply previously computed MaxStepTime.
  1021.                         float fStepTime = min(fFrameTime, fMaxStepTime);
  1022.                         fMaxStepTime = fFrameTime;
  1023.  
  1024.                         if (nFlags)
  1025.                                 PhysEnv.GetForces(forces, m_Loc.t, nFlags);
  1026.  
  1027.                         SParticleState stateNew = *this;
  1028.  
  1029.                         stateNew.m_Loc.s = params.fSize.GetValueFromMod(m_BaseMods.Size, GetRelativeAge(fStepTime)) * GetEmitter()->GetParticleScale();
  1030.  
  1031.                         SCollisionInfo collNew(-1);
  1032.                         if (m_pCollisionInfo)
  1033.                                 collNew = *m_pCollisionInfo;
  1034.  
  1035.                         fStepTime = MoveLinear(stateNew, collNew, min(fStepTime, context.fMaxLinearStepTime),
  1036.                                                forces, context.fMaxLinearDeviation, context.fMaxLinearDeviation, context.fMinStepTime);
  1037.  
  1038.                         Vec3 vMove = stateNew.m_Loc.t - m_Loc.t;
  1039.  
  1040.                         if (nFlags & PhysEnv.m_nNonUniformFlags && fStepTime > context.fMinStepTime)
  1041.                         {
  1042.                                 // Subdivide travel in non-uniform areas.
  1043.                                 float fMoveSqr = vMove.GetLengthSquared();
  1044.                                 if (fMoveSqr > sqr(fMAX_NONUNIFORM_TRAVEL))
  1045.                                 {
  1046.                                         SPhysForces forces2;
  1047.                                         PhysEnv.GetForces(forces2, stateNew.m_Loc.t, nFlags & (ENV_GRAVITY | ENV_WIND));
  1048.  
  1049.                                         // Estimate acceleration difference.
  1050.                                         Vec3 vErrorEst = forces2.vAccel - forces.vAccel;
  1051.                                         if (nFlags & ENV_WIND)
  1052.                                         {
  1053.                                                 float fDrag = params.fAirResistance.GetValueFromMod(m_BaseMods.AirResistance, GetRelativeAge(fStepTime * 0.5f));
  1054.                                                 vErrorEst += (forces2.vWind - forces.vWind) * (params.fAirResistance.fWindScale * fDrag);
  1055.                                         }
  1056.  
  1057.                                         float fDevSqr = vErrorEst.GetLengthSquared() * sqr(sqr(fStepTime) * 0.5f);
  1058.                                         if (fDevSqr > sqr(0.75f * fMAX_NONUNIFORM_TRAVEL))
  1059.                                         {
  1060.                                                 float fShorten = 0.75f * fMAX_NONUNIFORM_TRAVEL * isqrt_tpl(fDevSqr);
  1061.                                                 fShorten = min(fShorten, 0.75f);
  1062.                                                 fMaxStepTime = max(fStepTime * fShorten, context.fMinStepTime);
  1063.                                                 if (fMaxStepTime < fStepTime * 0.99f)
  1064.                                                         continue;
  1065.                                         }
  1066.                                 }
  1067.                         }
  1068.  
  1069.                         if (params.bSpaceLoop)
  1070.                         {
  1071.                                 // Wrap the particle into camera-aligned BB.
  1072.                                 Vec3 vPosSpaceLoop = stateNew.m_Loc.t - context.SpaceLoop.vCenter;
  1073.  
  1074.                                 for (int a = 0; a < 3; a++)
  1075.                                 {
  1076.                                         float fPosSpace = vPosSpaceLoop * context.SpaceLoop.vScaledAxes[a];
  1077.                                         if (abs(fPosSpace) > 1.f)
  1078.                                         {
  1079.                                                 float fCorrect = float(int(fPosSpace));
  1080.                                                 fCorrect += crymath::signnz(fCorrect);
  1081.                                                 stateNew.m_Loc.t -= context.SpaceLoop.vScaledAxes[a] * (fCorrect * sqr(context.SpaceLoop.vSize[a]));
  1082.                                         }
  1083.                                 }
  1084.                         }
  1085.  
  1086.                         DebugBounds(stateNew);
  1087.  
  1088.                         ////////////////////////////////////////////////////////////////////////////////////////////////
  1089.                         // Simple collision with physics entities
  1090.                         // Collision strategy:
  1091.                         //      * Move, with sliding from previous collisions
  1092.                         //      * Test collision, determine bounce or sliding
  1093.                         //      * Set sliding state for next frames, within slide distance
  1094.                         ////////////////////////////////////////////////////////////////////////////////////////////////
  1095.  
  1096.                         if (collNew.CanCollide() && !vMove.IsZero())
  1097.                         {
  1098.                                 ray_hit hit;
  1099.                                 if (CheckCollision(hit, fStepTime, context, forces, stateNew, collNew))
  1100.                                 {
  1101.                                         assert(hit.dist >= 0.f);
  1102.                                         rContainer.GetCounts().ParticlesCollideHit += 1.f;
  1103.  
  1104.                                         // Set particle to collision point.
  1105.                                         // Linearly interpolate velocity based on distance.
  1106.                                         stateNew.m_Vel.vLin.SetLerp(m_Vel.vLin, stateNew.m_Vel.vLin, hit.dist);
  1107.                                         stateNew.m_Loc.t = hit.pt;
  1108.  
  1109.                                         fStepTime = max(fStepTime * hit.dist, context.fMinStepTime);
  1110.  
  1111.                                         // Rotate to surface normal.
  1112.                                         RotateTo(stateNew.m_Loc.q, hit.n);
  1113.                                         stateNew.Collide(fStepTime);
  1114.  
  1115.                                         if (!collNew.Collide() && params.eFinalCollision != params.eFinalCollision.Ignore)
  1116.                                         {
  1117.                                                 // Last collision allowed
  1118.                                                 SetState(stateNew);
  1119.                                                 *m_pCollisionInfo = collNew;
  1120.                                                 m_fAge += fStepTime;
  1121.  
  1122.                                                 if (params.eFinalCollision == params.eFinalCollision.Die)
  1123.                                                 {
  1124.                                                         Stop();
  1125.                                                 }
  1126.                                                 else if (params.eFinalCollision == params.eFinalCollision.Stop)
  1127.                                                 {
  1128.                                                         m_Vel = Velocity3(ZERO);
  1129.                                                         m_pCollisionInfo->Stop();
  1130.                                                 }
  1131.  
  1132.                                                 return;
  1133.                                         }
  1134.                                         else
  1135.                                         {
  1136.                                                 if (hit.dist <= 0.f)
  1137.                                                         // Disable further collisions this frame
  1138.                                                         nCollideFlags = 0;
  1139.  
  1140.                                                 // Remove perpendicular velocity component.
  1141.                                                 float fVelPerp = stateNew.m_Vel.vLin * hit.n;
  1142.                                                 if (fVelPerp <= 0.f)
  1143.                                                 {
  1144.                                                         if (m_aPosHistory)
  1145.                                                         {
  1146.                                                                 // Store double history steps at bounce.
  1147.                                                                 UpdateAlignment(stateNew, context, forces.plWater, fStepTime);
  1148.                                                                 stateNew.m_fAge += fStepTime;
  1149.                                                                 AddPosHistory(stateNew);
  1150.                                                                 SetState(stateNew);
  1151.                                                                 fFrameTime -= fStepTime;
  1152.                                                                 fStepTime = 0.f;
  1153.                                                         }
  1154.  
  1155.                                                         stateNew.m_Vel.vLin -= hit.n * fVelPerp;
  1156.  
  1157.                                                         // Get phys params from material, or particle params.
  1158.                                                         float fElasticity, fSlidingFriction;
  1159.                                                         GetCollisionParams(hit.surface_idx, fElasticity, fSlidingFriction);
  1160.  
  1161.                                                         float fVelBounce = fVelPerp * -fElasticity;
  1162.                                                         float fAccelPerp = forces.vAccel * hit.n;
  1163.  
  1164.                                                         /*
  1165.                                                            float fDeltaVelTransverse = -fVelPerp * (1.f + fElasticity) * fSlidingFriction;
  1166.                                                            if (fDeltaVelTransverse)
  1167.                                                            {
  1168.                                                            // Apply dynamic friction during collision, regardless of sliding state.
  1169.                                                            // Perpendicular velocity change from bounce = (vp1 - vp0)
  1170.                                                            // Transverse velocity change from friction = (vp1 - vp0) * sliding_friction
  1171.                                                            stateNew.m_Vel.vLin -= stateNew.m_Vel.vLin * div_min(fDeltaVelTransverse, stateNew.m_Vel.vLin.GetLength(), 1.f);
  1172.                                                            }
  1173.                                                          */
  1174.  
  1175.                                                         // Sliding occurs when the bounce distance would be less than the collide buffer.
  1176.                                                         // d = - v^2 / 2a <= dmax
  1177.                                                         // v^2 <= -dmax*2a
  1178.                                                         if (sqr(fVelBounce) <= fCOLLIDE_BUFFER_DIST * fAccelPerp * -2.f)
  1179.                                                         {
  1180.                                                                 collNew.Sliding.SetSliding(hit.pCollider, hit.n, fSlidingFriction);
  1181.                                                         }
  1182.                                                         else
  1183.                                                         {
  1184.                                                                 // Bouncing.
  1185.                                                                 collNew.Sliding.ClearSliding(hit.n);
  1186.  
  1187.                                                                 // Bounce the particle, continue for remainder of frame.
  1188.                                                                 stateNew.m_Vel.vLin += hit.n * fVelBounce;
  1189.                                                         }
  1190.                                                 }
  1191.                                                 else
  1192.                                                         // Disable further collisions this frame
  1193.                                                         nCollideFlags = 0;
  1194.                                         }
  1195.                                 }
  1196.  
  1197.                         }
  1198.  
  1199.                         fStepTime = UpdateAlignment(stateNew, context, forces.plWater, fStepTime);
  1200.  
  1201.                         // Store computed state.
  1202.                         if (m_aPosHistory)
  1203.                                 AddPosHistory(stateNew);
  1204.  
  1205.                         SetState(stateNew);
  1206.                         if (m_pCollisionInfo)
  1207.                                 *m_pCollisionInfo = collNew;
  1208.  
  1209.                         fFrameTime -= fStepTime;
  1210.                         m_fAge += fStepTime;
  1211.                 }
  1212.  
  1213.                 rContainer.GetCounts().ParticlesReiterate -= 1.f;
  1214.         }
  1215.  
  1216.         m_fAge += max(fFrameTime, 0.f);
  1217.  
  1218.         // Update dynamic bounds if required.
  1219.         if (context.pbbDynamicBounds)
  1220.                 UpdateBounds(*context.pbbDynamicBounds);
  1221. }
  1222.  
  1223. float CParticle::UpdateAlignment(SParticleState& state, SParticleUpdateContext const& context, Plane const& plWater, float fStepTime) const
  1224. {
  1225.         ResourceParticleParams const& params = GetParams();
  1226.  
  1227.         // Apply rotation velocity.
  1228.         if (fStepTime != 0.f)
  1229.         {
  1230.                 if (context.b3DRotation)
  1231.                         // 3D rotation.
  1232.                         state.m_Loc.q = Quat::exp(state.m_Vel.vRot * (fStepTime * 0.5f)) * state.m_Loc.q;
  1233.                 else
  1234.                         // Just angle.
  1235.                         state.m_fAngle += m_Vel.vRot.y * fStepTime;
  1236.         }
  1237.  
  1238.         Vec3 vNormal(ZERO);
  1239.  
  1240.         switch (params.eFacing)
  1241.         {
  1242.         case ParticleParams::EFacing::Water:
  1243.                 {
  1244.                         // Project point and velocity onto plane.
  1245.                         if (HasWater(plWater))
  1246.                         {
  1247.                                 float fDist = plWater.DistFromPlane(state.m_Loc.t);
  1248.                                 state.m_Loc.t -= plWater.n * (fDist - params.vPositionOffset.z);
  1249.                                 vNormal = plWater.n;
  1250.                         }
  1251.                         else
  1252.                         {
  1253.                                 // No water, kill it.
  1254.                                 state.Stop();
  1255.                                 return fStepTime;
  1256.                         }
  1257.                         break;
  1258.                 }
  1259.         case ParticleParams::EFacing::Terrain:
  1260.                 {
  1261.                         if (CTerrain* const pTerrain = GetTerrain())
  1262.                         {
  1263.                                 // Project center and velocity onto plane.
  1264.                                 if (state.m_Loc.s > 0.f)
  1265.                                 {
  1266.                                         // Sample terrain around point to get heights and average normal.
  1267.                                         Vec3 avCorner[5];
  1268.                                         for (int c = 0; c < 5; c++)
  1269.                                         {
  1270.                                                 avCorner[c] = state.m_Loc.t;
  1271.                                                 if (c < 4)
  1272.                                                 {
  1273.                                                         avCorner[c].x += ((c & 1) * 2 - 1) * state.m_Loc.s;
  1274.                                                         avCorner[c].y += ((c & 2) - 1) * state.m_Loc.s;
  1275.                                                 }
  1276.                                                 avCorner[c].z = pTerrain->GetZApr(avCorner[c].x, avCorner[c].y, GetDefSID());
  1277.                                         }
  1278.  
  1279.                                         // Rotate sprite to average normal of quad.
  1280.                                         vNormal = (avCorner[3] - avCorner[0]) ^ (avCorner[2] - avCorner[1]);
  1281.                                         if (vNormal.z != 0.f)
  1282.                                         {
  1283.                                                 if (vNormal.z < 0.f)
  1284.                                                         vNormal = -vNormal;
  1285.                                                 vNormal.Normalize();
  1286.  
  1287.                                                 // Adjust z to match plane.
  1288.                                                 float fZAdd = -fHUGE;
  1289.                                                 for (int c = 0; c < 5; c++)
  1290.                                                         fZAdd = max(fZAdd, avCorner[c] * vNormal);
  1291.                                                 state.m_Loc.t.z += (fZAdd - state.m_Loc.t * vNormal) / vNormal.z + fATTACH_BUFFER_DIST;
  1292.                                         }
  1293.                                 }
  1294.                         }
  1295.                         break;
  1296.                 }
  1297.         case ParticleParams::EFacing::Velocity:
  1298.                 {
  1299.                         // Rotate to movement dir.
  1300.                         vNormal = GetVisualVelocity(state, fStepTime).GetNormalized();
  1301.                         break;
  1302.                 }
  1303.         case ParticleParams::EFacing::Horizontal:
  1304.                 {
  1305.                         vNormal = GetSource().GetLocation().q.GetColumn1();
  1306.  
  1307.                         // Confine particle motion.
  1308.                         Vec3 vAdjust = vNormal * (vNormal | (state.m_Loc.t - m_Loc.t));
  1309.                         state.m_Loc.t -= vAdjust;
  1310.                         break;
  1311.                 }
  1312.         }
  1313.  
  1314.         AlignTo(state, vNormal);
  1315.  
  1316.         return fStepTime;
  1317. }
  1318.  
  1319. void CParticle::AlignTo(SParticleState& state, const Vec3& vNormal) const
  1320. {
  1321.         const ParticleParams& params = GetParams();
  1322.  
  1323.         bool bNormal = vNormal.GetLengthSquared() > 0.f;
  1324.         if (params.eFacing != params.eFacing.Velocity)
  1325.         {
  1326.                 if (bNormal)
  1327.                 {
  1328.                         // Align velocities to normal
  1329.                         state.m_Vel.vLin = Quat::CreateRotationV0V1(state.m_Loc.q.GetColumn1(), vNormal) * state.m_Vel.vLin;
  1330.                         state.m_Vel.vLin -= vNormal * (state.m_Vel.vLin * vNormal);
  1331.                         state.m_Vel.vRot = vNormal * (state.m_Vel.vRot * vNormal);
  1332.                 }
  1333.  
  1334.                 if (params.bOrientToVelocity || m_aPosHistory)
  1335.                 {
  1336.                         // Orient in velocity direction.
  1337.                         Vec3 vOrient = GetVisualVelocity(state).GetNormalized();
  1338.                         vOrient -= vNormal * (vOrient * vNormal);
  1339.                         if (CheckNormalize(vOrient))
  1340.                         {
  1341.                                 if (bNormal)
  1342.                                 {
  1343.                                         // Rotate to align Y and Z axes
  1344.                                         Matrix33 mat;
  1345.                                         mat.SetColumn1(vNormal);
  1346.                                         mat.SetColumn2(vOrient);
  1347.                                         mat.SetColumn0(vNormal ^ vOrient);
  1348.                                         state.m_Loc.q = Quat(mat);
  1349.                                 }
  1350.                                 else
  1351.                                         // Just orient Z axis
  1352.                                         OrientTo(state.m_Loc.q, vOrient);
  1353.                                 return;
  1354.                         }
  1355.                 }
  1356.         }
  1357.  
  1358.         if (bNormal)
  1359.                 // Just orient Y axis
  1360.                 RotateTo(state.m_Loc.q, vNormal);
  1361. }
  1362.  
  1363. Vec3 CParticle::VortexRotation(SParticleState const& state, bool bVelocity, float fTime) const
  1364. {
  1365.         // Compute vortex rotational offset at current age.
  1366.         // p(t) = TSize e^(i TSpeed t) (t max 1)
  1367.         const ParticleParams& params = GetParams();
  1368.  
  1369.         float fRelativeAge = state.GetRelativeAge(fTime);
  1370.         float fVortexSpeed = DEG2RAD(params.fTurbulenceSpeed.GetValueFromMod(m_BaseMods.TurbulenceSpeed, fRelativeAge));
  1371.         float fAngle = fVortexSpeed * (state.m_fAge + fTime);
  1372.         float fVortexSize = params.fTurbulenceSize.GetValueFromMod(m_BaseMods.TurbulenceSize, fRelativeAge) * GetMain().GetParticleScale();
  1373.         if (bVelocity)
  1374.         {
  1375.                 // Compute the current vortex velocity (not magnitude)
  1376.                 fAngle += gf_PI * 0.5f;
  1377.                 fVortexSize *= fVortexSpeed;
  1378.         }
  1379.  
  1380.         Vec2 vRot;
  1381.         sincos_tpl(fAngle, &vRot.y, &vRot.x);
  1382.         vRot *= fVortexSize;
  1383.  
  1384.         // Scale down vortex size in first half rotation.
  1385.         fVortexSize *= div_min(abs(fAngle), gf_PI, 1.f);
  1386.  
  1387.         // Choose axes orthogonal to velocity.
  1388.         Vec3 const& vAxis = state.m_Vel.vLin;
  1389.         Vec3 vX = vAxis.GetOrthogonal();
  1390.         Vec3 vY = vX ^ vAxis;
  1391.  
  1392.         return vX.GetNormalized(vRot.x) + vY.GetNormalized(vRot.y);
  1393. }
  1394.  
  1395. void CParticle::TargetMovement(ParticleTarget const& target, SParticleState& state, float fTime, float fRelativeAge) const
  1396. {
  1397.         const ParticleParams& params = GetParams();
  1398.  
  1399.         float fTargetRadius = max(params.TargetAttraction.fRadius.GetValueFromMod(m_BaseMods.fTargetRadius, fRelativeAge) + target.fRadius, 0.f);
  1400.         bool bOrbiting = params.TargetAttraction.bOrbit && fTargetRadius > 0.f;
  1401.  
  1402.         state.m_Loc.t -= state.m_Vel.vLin * fTime;
  1403.  
  1404.         // Decompose current velocity into radial+angular components.
  1405.         Vec3 vPos = state.m_Loc.t - target.vTarget;
  1406.         Vec3 vVel = state.m_Vel.vLin - target.vVelocity;
  1407.  
  1408.         float fDist = vPos.GetLength();
  1409.         float fVel = vVel.GetLength();
  1410.         Vec3 vRadialDir = vPos.GetNormalized();
  1411.         float fRadialVel = vVel | vRadialDir;
  1412.         Vec3 vOrbitalVel = vVel - vRadialDir * fRadialVel;
  1413.         Vec3 vOrbitalDir = vOrbitalVel.GetNormalized();
  1414.         float fOrbitalVel = vOrbitalVel.GetLength();
  1415.  
  1416.         // Determine arrival time.
  1417.         float fArrivalTime = div_min(fTargetRadius - fDist, fRadialVel, fHUGE);
  1418.         if (fArrivalTime < 0.f)
  1419.                 fArrivalTime = fHUGE;
  1420.  
  1421.         // Goal is to reach target radius in a quarter revolution over particle's life.
  1422.         float fLife = max(state.m_fStopAge - state.m_fAge, 0.0f);
  1423.         fArrivalTime = div_min(gf_PI * 0.5f * fDist * fLife, fOrbitalVel * state.m_fStopAge, fArrivalTime);
  1424.  
  1425.         if (fArrivalTime > fLife)
  1426.         {
  1427.                 if (params.TargetAttraction.bExtendSpeed)
  1428.                         fArrivalTime = fLife;
  1429.         }
  1430.         else if (!bOrbiting)
  1431.         {
  1432.                 // Age particle prematurely based on target time.
  1433.                 state.m_fStopAge = state.m_fAge + fArrivalTime;
  1434.         }
  1435.  
  1436.         // Execute the orbit.
  1437.         float fNewDist;
  1438.         if (fArrivalTime > fTime)
  1439.         {
  1440.                 // Change particle direction, maintaining speed.
  1441.                 fRadialVel = (fTargetRadius - fDist) / fArrivalTime;
  1442.                 fOrbitalVel = sqrt_tpl(max(0.f, sqr(fVel) - sqr(fRadialVel)));
  1443.                 fNewDist = fDist + fRadialVel * fTime;
  1444.         }
  1445.         else
  1446.         {
  1447.                 // Arrives this time step.
  1448.                 fRadialVel = 0.f;
  1449.                 fOrbitalVel = fVel;
  1450.                 fNewDist = fTargetRadius;
  1451.         }
  1452.  
  1453.         if (fNewDist > 0.f)
  1454.         {
  1455.                 // Orbital travel needed.
  1456.                 // Compute angular movement, using geometric or arithmetic mean of start and end radii, which is a close approximation.
  1457.                 float fAngularVel = fDist > fTargetRadius ?
  1458.                                     fOrbitalVel* isqrt_tpl(fDist* fNewDist) :
  1459.                                     fOrbitalVel / ((fDist + fNewDist) * 0.5f);
  1460.                 float fVelAngle = fAngularVel * fTime;
  1461.  
  1462.                 float fCos, fSin;
  1463.                 sincos_tpl(fVelAngle, &fSin, &fCos);
  1464.                 state.m_Loc.t += (vRadialDir * (fCos - 1.f) + vOrbitalDir * fSin) * fDist;
  1465.  
  1466.                 // Recompute polar axes.
  1467.                 vOrbitalDir = vOrbitalDir * fCos - vRadialDir * fSin;
  1468.                 vRadialDir = (state.m_Loc.t - target.vTarget).GetNormalized();
  1469.         }
  1470.  
  1471.         state.m_Loc.t += vRadialDir * (fNewDist - fDist);
  1472.  
  1473.         state.m_Vel.vLin = vRadialDir * fRadialVel + vOrbitalDir * fOrbitalVel + target.vVelocity;
  1474.  
  1475.         if (params.TargetAttraction.bShrink)
  1476.         {
  1477.                 fDist = fNewDist - fTargetRadius;
  1478.                 if (fDist < state.m_Loc.s * GetBaseRadius())
  1479.                         state.m_Loc.s = max(fDist, 0.f) / GetBaseRadius();
  1480.         }
  1481.         assert(m_Loc.s >= 0.f);
  1482. }
  1483.  
  1484. void CParticle::GetCollisionParams(int nCollSurfaceIdx, float& fElasticity, float& fDrag) const
  1485. {
  1486.         // Get phys params from material, or particle params.
  1487.         fElasticity = GetParams().fElasticity;
  1488.         fDrag = GetParams().fDynamicFriction;
  1489.  
  1490.         IMaterialManager* pMatMan = GetMatMan();
  1491.  
  1492.         int iSurfaceIndex = GetSurfaceIndex();
  1493.         if (iSurfaceIndex > 0)
  1494.         {
  1495.                 ISurfaceType* pSurfPart = pMatMan->GetSurfaceType(iSurfaceIndex);
  1496.                 if (pSurfPart)
  1497.                 {
  1498.                         ISurfaceType::SPhysicalParams const& physPart = pSurfPart->GetPhyscalParams();
  1499.                         fElasticity = max(fElasticity, physPart.bouncyness);
  1500.                         fDrag = max(fDrag, physPart.friction);
  1501.                 }
  1502.  
  1503.                 // Combine with hit surface params.
  1504.                 ISurfaceType* pSurfHit = pMatMan->GetSurfaceType(nCollSurfaceIdx);
  1505.                 if (!pSurfHit)
  1506.                         pSurfHit = pMatMan->GetDefaultTerrainLayerMaterial()->GetSurfaceType();
  1507.                 ISurfaceType::SPhysicalParams const& physHit = pSurfHit->GetPhyscalParams();
  1508.  
  1509.                 fElasticity = clamp_tpl((fElasticity + physHit.bouncyness) * 0.5f, 0.f, 1.f);
  1510.                 fDrag = max((fDrag + physHit.friction) * 0.5f, 0.f);
  1511.         }
  1512. }
  1513.  
  1514. CParticle::~CParticle()
  1515. {
  1516.         m_pEmitter->Release();
  1517.  
  1518.         IF (m_pPhysEnt, 0)
  1519.         {
  1520.                 GetPhysicalWorld()->DestroyPhysicalEntity(m_pPhysEnt);
  1521.         }
  1522.  
  1523.         GeomRef::Release();
  1524.  
  1525.         IF (m_pCollisionInfo, 0)
  1526.         {
  1527.                 m_pCollisionInfo->Clear();
  1528.                 ParticleObjectAllocator().Deallocate(m_pCollisionInfo, sizeof(SCollisionInfo));
  1529.         }
  1530.  
  1531.         ParticleObjectAllocator().Deallocate(m_aPosHistory, sizeof(SParticleHistory) * m_pContainer->GetHistorySteps());
  1532. }
  1533.  
  1534. char GetMinAxis(Vec3 const& vVec)
  1535. {
  1536.         float x = fabs(vVec.x);
  1537.         float y = fabs(vVec.y);
  1538.         float z = fabs(vVec.z);
  1539.  
  1540.         if (x < y && x < z)
  1541.                 return 'x';
  1542.  
  1543.         if (y < x && y < z)
  1544.                 return 'y';
  1545.  
  1546.         return 'z';
  1547. }
  1548.  
  1549. #define fSPHERE_VOLUME float(4.f / 3.f * gf_PI)
  1550.  
  1551. int CParticle::GetSurfaceIndex() const
  1552. {
  1553.         ResourceParticleParams const& params = GetParams();
  1554.         if (params.sSurfaceType.nIndex)
  1555.                 // Explicit surface type.
  1556.                 return params.sSurfaceType.nIndex;
  1557.         if (params.pMaterial)
  1558.         {
  1559.                 // Get from material, if defined.
  1560.                 if (int index = params.pMaterial->GetSurfaceTypeId())
  1561.                         return index;
  1562.         }
  1563.         if (m_pMeshObj)
  1564.         {
  1565.                 // Get from geometry.
  1566.                 if (phys_geometry* pGeom = m_pMeshObj->GetPhysGeom())
  1567.                 {
  1568.                         if (pGeom->surface_idx < pGeom->nMats)
  1569.                                 return pGeom->pMatMapping[pGeom->surface_idx];
  1570.                         else
  1571.                                 return pGeom->surface_idx;
  1572.                 }
  1573.         }
  1574.         return 0;
  1575. }
  1576.  
  1577. void CParticle::Physicalize()
  1578. {
  1579.         ResourceParticleParams const& params = GetParams();
  1580.         SPhysEnviron const& PhysEnv = GetMain().GetPhysEnviron();
  1581.  
  1582.         Vec3 vGravity = PhysEnv.m_UniformForces.vAccel * params.fGravityScale.GetValueFromMod(m_BaseMods.GravityScale) + params.vAcceleration;
  1583.  
  1584.         pe_params_pos par_pos;
  1585.         par_pos.pos = m_Loc.t;
  1586.         par_pos.q = m_Loc.q;
  1587.  
  1588.         phys_geometry* pGeom = m_pMeshObj ? m_pMeshObj->GetPhysGeom() : 0;
  1589.  
  1590.         m_Loc.s = params.fSize.GetValueFromMod(m_BaseMods.Size) * GetEmitter()->GetParticleScale();
  1591.  
  1592.         if (params.ePhysicsType == params.ePhysicsType.RigidBody)
  1593.         {
  1594.                 if (!pGeom)
  1595.                         return;
  1596.  
  1597.                 // Make Physical Rigid Body.
  1598.                 m_pPhysEnt = GetPhysicalWorld()->CreatePhysicalEntity(PE_RIGID, &par_pos, m_pMeshObj, PHYS_FOREIGN_ID_RIGID_PARTICLE);
  1599.  
  1600.                 pe_geomparams partpos;
  1601.  
  1602.                 partpos.density = params.fDensity;
  1603.                 partpos.scale = m_Loc.s;
  1604.                 partpos.flagsCollider = geom_colltype_debris;
  1605.                 partpos.flags &= ~geom_colltype_debris; // don't collide with other particles.
  1606.  
  1607.                 // Override surface index if specified.
  1608.                 int idx =
  1609.                   params.sSurfaceType.nIndex ? params.sSurfaceType.nIndex :
  1610.                   params.pMaterial ? params.pMaterial->GetSurfaceTypeId() :
  1611.                   0;
  1612.                 if (idx)
  1613.                 {
  1614.                         partpos.pMatMapping = &idx;
  1615.                         partpos.nMats = 1;
  1616.                         partpos.surface_idx = 0;
  1617.                 }
  1618.                 m_pPhysEnt->AddGeometry(pGeom, &partpos, 0);
  1619.  
  1620.                 pe_simulation_params symparams;
  1621.                 symparams.minEnergy = (0.2f) * (0.2f);
  1622.                 symparams.damping = symparams.dampingFreefall = params.fAirResistance.GetValueFromMod(m_BaseMods.AirResistance);
  1623.  
  1624.                 // Note: Customized gravity currently doesn't work for rigid body.
  1625.                 symparams.gravity = symparams.gravityFreefall = vGravity;
  1626.                 //symparams.softness = symparams.softnessGroup = 0.003f;
  1627.                 //symparams.softnessAngular = symparams.softnessAngularGroup = 0.01f;
  1628.                 symparams.maxLoggedCollisions = params.nMaxCollisionEvents;
  1629.                 m_pPhysEnt->SetParams(&symparams);
  1630.  
  1631.                 pe_action_set_velocity velparam;
  1632.                 velparam.v = m_Vel.vLin;
  1633.                 velparam.w = m_Vel.vRot;
  1634.                 m_pPhysEnt->Action(&velparam);
  1635.         }
  1636.         else if (params.ePhysicsType == params.ePhysicsType.SimplePhysics)
  1637.         {
  1638.                 // Make Physical Particle.
  1639.                 m_pPhysEnt = GetPhysicalWorld()->CreatePhysicalEntity(PE_PARTICLE, &par_pos);
  1640.                 pe_params_particle part;
  1641.  
  1642.                 // Compute particle mass from volume of object.
  1643.                 part.size = m_Loc.s;
  1644.  
  1645.                 part.mass = params.fDensity * part.size * part.size * part.size;
  1646.                 if (m_pMeshObj)
  1647.                 {
  1648.                         part.size *= m_pMeshObj->GetRadius() + 0.05f;
  1649.                         if (pGeom)
  1650.                                 part.mass *= pGeom->V;
  1651.                         else
  1652.                                 part.mass *= m_pMeshObj->GetAABB().GetVolume() * fSPHERE_VOLUME / 8.f;
  1653.                 }
  1654.                 else
  1655.                 {
  1656.                         // Assume spherical volume.
  1657.                         part.mass *= fSPHERE_VOLUME;
  1658.                 }
  1659.  
  1660.                 part.thickness = params.fThickness * part.size;
  1661.                 part.velocity = m_Vel.vLin.GetLength();
  1662.                 if (part.velocity > 0.f)
  1663.                         part.heading = m_Vel.vLin / part.velocity;
  1664.                 part.q0 = m_Loc.q;
  1665.                 part.wspin = m_Vel.vRot;
  1666.                 part.q0 = m_Loc.q;
  1667.  
  1668.                 if (m_pMeshObj)
  1669.                 {
  1670.                         Vec3 vSize = m_pMeshObj->GetAABB().GetSize();
  1671.                         char cMinAxis = GetMinAxis(vSize);
  1672.                         part.normal = Vec3((cMinAxis == 'x') ? 1.f : 0, (cMinAxis == 'y') ? 1.f : 0, (cMinAxis == 'z') ? 1.f : 0);
  1673.                 }
  1674.  
  1675.                 part.surface_idx = GetSurfaceIndex();
  1676.                 part.flags = /*particle_no_roll|*/ particle_no_path_alignment;
  1677.                 part.kAirResistance = params.fAirResistance.GetValueFromMod(m_BaseMods.AirResistance);
  1678.                 part.gravity = vGravity;
  1679.  
  1680.                 m_pPhysEnt->SetParams(&part);
  1681.         }
  1682.  
  1683.         // Common settings.
  1684.         if (m_pPhysEnt)
  1685.         {
  1686.                 pe_params_flags pf;
  1687.                 pf.flagsOR = pef_never_affect_triggers;
  1688.                 pf.flagsOR |= pef_log_collisions;
  1689.                 m_pPhysEnt->SetParams(&pf);
  1690.                 m_pPhysEnt->AddRef();
  1691.         }
  1692. }
  1693.  
  1694. void CParticle::GetPhysicsState()
  1695. {
  1696.         if (m_pPhysEnt)
  1697.         {
  1698.                 pe_status_pos status_pos;
  1699.                 if (m_pPhysEnt->GetStatus(&status_pos))
  1700.                 {
  1701.                         m_Loc.t = status_pos.pos;
  1702.                         m_Loc.q = status_pos.q;
  1703.                 }
  1704.                 pe_status_dynamics status_dyn;
  1705.                 if (m_pPhysEnt->GetStatus(&status_dyn))
  1706.                 {
  1707.                         m_Vel.vLin = status_dyn.v;
  1708.                         m_Vel.vRot = status_dyn.w;
  1709.                 }
  1710.         }
  1711. }
  1712.  
  1713. size_t CParticle::GetAllocationSize(const CParticleContainer* pCont)
  1714. {
  1715.         return pCont->GetHistorySteps() * sizeof(SParticleHistory)
  1716.                + (pCont->NeedsCollisionInfo() ? sizeof(SCollisionInfo) : 0);
  1717. }
  1718.  
  1719. #ifdef _DEBUG
  1720.  
  1721. // Test for distribution evenness.
  1722. struct CChaosTest
  1723. {
  1724.         CChaosTest()
  1725.         {
  1726.                 CChaosKey keyBase(0U);
  1727.                 // cppcheck-suppress unreadVariable
  1728.                 float f[100];
  1729.                 for (uint32 i = 0; i < 100; i++)
  1730.                 {
  1731.                         f[i] = keyBase.Jumble(CChaosKey(i)) * 1.f;
  1732.                 }
  1733.         }
  1734. };
  1735.  
  1736. static CChaosTest ChaosTest;
  1737.  
  1738. #endif
  1739.  
downloadParticle.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