BVB Source Codes

CRYENGINE Show ParticleEffect.cpp Source code

Return Download CRYENGINE: download ParticleEffect.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:   particleeffect.cpp
  5. //  Version:     v1.00
  6. //  Created:     10/7/2003 by Timur.
  7. //  Compilers:   Visual Studio.NET
  8. //  Description:
  9. // -------------------------------------------------------------------------
  10. //  History:
  11. //
  12. ////////////////////////////////////////////////////////////////////////////
  13.  
  14. #include "StdAfx.h"
  15. #include "ParticleEffect.h"
  16. #include "ParticleEmitter.h"
  17. #include "ParticleManager.h"
  18. #include <CrySystem/Profilers/IStatoscope.h>
  19. #include <CryAudio/IAudioSystem.h>
  20.  
  21. //////////////////////////////////////////////////////////////////////////
  22. // TypeInfo XML serialisation code
  23.  
  24. #include <CryParticleSystem/ParticleParams_TypeInfo.h>
  25.  
  26. DEFINE_INTRUSIVE_LINKED_LIST(CParticleEffect)
  27.  
  28. static const int nSERIALIZE_VERSION = 28;
  29. static const int nMIN_SERIALIZE_VERSION = 19;
  30.  
  31. // Write a struct to an XML node.
  32. template<class T>
  33. void ToXML(IXmlNode& xml, T& val, const T& def_val, FToString flags)
  34. {
  35.         CTypeInfo const& info = TypeInfo(&val);
  36.  
  37.         for (const CTypeInfo::CVarInfo* pVar = info.NextSubVar(nullptr);
  38.              pVar != nullptr;
  39.              pVar = info.NextSubVar(pVar))
  40.         {
  41.                 if (pVar->GetElemSize() == 0)
  42.                         continue;
  43.  
  44.                 if (&pVar->Type == &TypeInfo((void**)0))
  45.                         // Never serialize pointers.
  46.                         continue;
  47.  
  48.                 assert(pVar->GetDim() == 1);
  49.                 cstr name = pVar->GetName();
  50.  
  51.                 if (name[0] == '_')
  52.                         // Do not serialize private vars.
  53.                         continue;
  54.  
  55.                 string str = pVar->ToString(&val, flags, &def_val);
  56.                 if (flags.SkipDefault() && str.length() == 0)
  57.                         continue;
  58.  
  59.                 xml.setAttr(name, str);
  60.         }
  61. }
  62.  
  63. // Read a struct from an XML node.
  64. template<class T>
  65. void FromXML(IXmlNode& xml, T& val, FFromString flags)
  66. {
  67.         CTypeInfo const& info = TypeInfo(&val);
  68.         int nAttrs = xml.getNumAttributes();
  69.         for (int a = 0; a < nAttrs; a++)
  70.         {
  71.                 cstr sKey, sVal;
  72.                 xml.getAttributeByIndex(a, &sKey, &sVal);
  73.  
  74.                 CTypeInfo::CVarInfo const* pVar = info.FindSubVar(sKey);
  75.                 if (pVar)
  76.                 {
  77.                         assert(pVar->GetDim() == 1);
  78.                         pVar->FromString(&val, sVal, flags);
  79.                 }
  80.         }
  81. }
  82.  
  83. template<class T>
  84. bool GetAttr(IXmlNode const& node, cstr attr, T& val)
  85. {
  86.         cstr sAttr = node.getAttr(attr);
  87.         return *sAttr && TypeInfo(&val).FromString(&val, sAttr, FFromString().SkipEmpty(1));
  88. }
  89.  
  90. template<class T>
  91. T GetAttrValue(IXmlNode const& node, cstr attr, T defval)
  92. {
  93.         GetAttr(node, attr, defval);
  94.         return defval;
  95. }
  96.  
  97. template<class T>
  98. void SetParamRange(TVarParam<T>& param, float fValMin, float fValMax)
  99. {
  100.         if (abs(fValMin) > abs(fValMax))
  101.                 std::swap(fValMin, fValMax);
  102.         if (fValMax != 0.f)
  103.                 param.Set(fValMax, (fValMax - fValMin) / fValMax);
  104.         else
  105.                 param.Set(0.f, 0.f);
  106. }
  107.  
  108. template<class T>
  109. void AdjustParamRange(TVarParam<T>& param, float fAdjust)
  110. {
  111.         float fValMax = param(VMAX) + fAdjust,
  112.               fValMin = param(VMIN) + fAdjust;
  113.         SetParamRange(param, fValMin, fValMax);
  114. }
  115.  
  116. //////////////////////////////////////////////////////////////////////////
  117. // ResourceParticleParams implementation
  118.  
  119. static const float fTRAVEL_SAFETY = 0.1f;
  120. static const float fSIZE_SAFETY = 0.1f;
  121.  
  122. struct SEmitParams
  123. {
  124.         Vec3  vAxis;
  125.         float fCosMin, fCosMax;
  126.         float fSpeedMin, fSpeedMax;
  127.  
  128.         void SetOmniDir()
  129.         { fCosMin = -1.f; fCosMax = 1.f; }
  130.         bool bOmniDir() const
  131.         { return fCosMin <= -1.f && fCosMax >= 1.f; }
  132.         bool bSingleDir() const
  133.         { return fCosMin >= 1.f; }
  134. };
  135.  
  136. namespace Travel
  137. {
  138. float TravelDistanceApprox(const Vec3& vVel0, float fTime, const SForceParams& forces)
  139. {
  140.         if (fTime <= 0.f)
  141.                 return 0.f;
  142.  
  143.         Vec3 vVel = vVel0;
  144.  
  145.         // Check if trajectory has a speed minimum
  146.         float dt[3] = { fTime, 0.f, 0.f };
  147.         float s[5] = { vVel.GetLengthFast() };
  148.         int nS = 1;
  149.  
  150.         if (forces.fDrag * fTime >= fDRAG_APPROX_THRESHOLD)
  151.         {
  152.                 float fInvDrag = 1.f / forces.fDrag;
  153.                 Vec3 vTerm = forces.vWind + forces.vAccel * fInvDrag;
  154.                 /*
  155.                    s^2 = (V d + VT (1 - d))^2                                   ; d = e^(-b t)
  156.                      = (VT + (V-VT) e^(-b t))^2
  157.                      = VT^2 + 2 VT*(V-VT) e^(-b t) + (V-VT)^2 e^(-2b t)
  158.                    s^2\t = -b 2 VT*(V-VT) e^(-b t) - 2b (V-VT)^2 e^(-2b t) = 0
  159.                    -VT*(V-VT) = (V-VT)^2 e^(-b t)
  160.                    e^(-b t) = -VT*(V-VT) / (V-VT)^2
  161.                    t = -log ( VT*(VT-V) / (VT-V)^2) / b
  162.                  */
  163.                 Vec3 vDV = vTerm - vVel;
  164.                 float fDD = vDV.len2(), fTD = vTerm * vDV;
  165.                 if (fDD * fTD > 0.f)
  166.                 {
  167.                         float fT = -logf(fTD / fDD) * fInvDrag;
  168.                         if (fT > 0.f && fT < fTime)
  169.                         {
  170.                                 dt[0] = fT;
  171.                                 dt[1] = fTime - fT;
  172.                         }
  173.                 }
  174.  
  175.                 for (int t = 0; dt[t] > 0.f; t++)
  176.                 {
  177.                         float fDecay = 1.f - expf(-forces.fDrag * dt[t] * 0.5f);
  178.                         for (int i = 0; i < 2; i++)
  179.                         {
  180.                                 vVel = Lerp(vVel, vTerm, fDecay);
  181.                                 s[nS++] = vVel.GetLengthFast();
  182.                         }
  183.                 }
  184.         }
  185.         else
  186.         {
  187.                 // Fast approx drag computation.
  188.                 Vec3 vAccel = forces.vAccel + (forces.vWind - vVel) * forces.fDrag;
  189.                 float fVA = vVel * vAccel,
  190.                       fAA = vAccel * vAccel;
  191.                 if (fVA * fAA < 0.f && -fVA < fTime * fAA)
  192.                 {
  193.                         dt[0] = -fVA / fAA;
  194.                         dt[1] = fTime - dt[0];
  195.                 }
  196.  
  197.                 for (int t = 0; dt[t] > 0.f; t++)
  198.                 {
  199.                         Vec3 vD = forces.vAccel * (dt[t] * 0.5f);
  200.                         for (int i = 0; i < 2; i++)
  201.                         {
  202.                                 vVel += vD;
  203.                                 s[nS++] = vVel.GetLengthFast();
  204.                         }
  205.                 }
  206.         }
  207.  
  208.         if (nS == 5)
  209.         {
  210.                 // 2-segment quadratic approximation
  211.                 return ((s[0] + s[1] * 4.f + s[2]) * dt[0]
  212.                         + (s[2] + s[3] * 4.f + s[4]) * dt[1]) * 0.16666666f;
  213.         }
  214.         else
  215.         {
  216.                 // Single segment quadratic approximation
  217.                 return (s[0] + s[1] * 4.f + s[2]) * 0.16666666f * dt[0];
  218.         }
  219. }
  220.  
  221. float TravelVolume(const AABB& bbSource, const AABB& bbTravel, float fDist, float fSize)
  222. {
  223.         Vec3 V = bbSource.GetSize() + bbTravel.GetSize() + Vec3(fSize);
  224.         Vec3 T = bbTravel.GetCenter().abs().GetNormalized() * fDist;
  225.  
  226.         return V.x * V.y * V.z + V.x * V.y * T.z + V.x * T.y * V.z + T.x * V.y * V.z;
  227. }
  228.  
  229. void AddTravelVec(AABB& bb, Vec3 vVel, SForceParams const& force, float fTime)
  230. {
  231.         Vec3 vTravel(ZERO);
  232.         Travel(vTravel, vVel, fTime, force);
  233.         vTravel += vVel * force.fStretch;
  234.         bb.Add(vTravel);
  235. }
  236.  
  237. void AddTravel(AABB& bb, Vec3 const& vVel, SForceParams const& force, float fTime, int nAxes)
  238. {
  239.         if (force.fStretch != 0.f)
  240.         {
  241.                 // Add start point
  242.                 bb.Add(vVel * force.fStretch);
  243.         }
  244.  
  245.         // Add end point.
  246.         AddTravelVec(bb, vVel, force, fTime);
  247.  
  248.         // Find time of min/max vals of each component, by solving for v[i] = 0
  249.         if (nAxes && force.fDrag != 0)
  250.         {
  251.                 // vt = a/d + w
  252.                 // v = (v0-vt) e^(-d t) + vt
  253.                 // vs = v + v\t s
  254.                 //              = (1 - d s)(v0-vt) e^(-d t) + vt
  255.                 //              = 0
  256.                 // e^(-d t) = vt / ((1 - d s)(vt-v0))
  257.                 // t = -log( vt / ((1 - d s)(vt-v0)) ) / d
  258.                 float fInvDrag = 1.f / force.fDrag;
  259.                 for (int i = 0; nAxes; i++, nAxes >>= 1)
  260.                 {
  261.                         if (nAxes & 1)
  262.                         {
  263.                                 float fVT = force.vAccel[i] * fInvDrag + force.vWind[i];
  264.                                 float d = (fVT - vVel[i]) * (1.f - force.fDrag * force.fStretch);
  265.                                 if (fVT * d > 0.f)
  266.                                 {
  267.                                         float fT = -logf(fVT / d) * fInvDrag;
  268.                                         if (fT > 0.f && fT < fTime)
  269.                                                 AddTravelVec(bb, vVel, force, fT);
  270.                                 }
  271.                         }
  272.                 }
  273.         }
  274.         else
  275.         {
  276.                 for (int i = 0; nAxes; i++, nAxes >>= 1)
  277.                 {
  278.                         if (nAxes & 1)
  279.                         {
  280.                                 if (force.vAccel[i] != 0.f)
  281.                                 {
  282.                                         // ps = p + v s
  283.                                         // vs = p\t + v\t s
  284.                                         //              = v + a s
  285.                                         // vs = 0
  286.                                         //              = v0 + a (t+s)
  287.                                         // t = -v0/a - s
  288.                                         float fT = -vVel[i] / force.vAccel[i] - force.fStretch;
  289.                                         if (fT > 0.f && fT < fTime)
  290.                                                 AddTravelVec(bb, vVel, force, fT);
  291.                                 }
  292.                         }
  293.                 }
  294.         }
  295. }
  296.  
  297. Vec3 GetExtremeEmitVec(Vec3 const& vRefDir, SEmitParams const& emit)
  298. {
  299.         float fEmitCos = vRefDir * emit.vAxis;
  300.         if (fEmitCos >= emit.fCosMin && fEmitCos <= emit.fCosMax)
  301.         {
  302.                 // Emit dir is in the cone.
  303.                 return vRefDir * emit.fSpeedMax;
  304.         }
  305.         else
  306.         {
  307.                 // Find dir in emission cone closest to ref dir.
  308.                 Vec3 vEmitPerpX = vRefDir - emit.vAxis * fEmitCos;
  309.                 float fPerpLenSq = vEmitPerpX.GetLengthSquared();
  310.  
  311.                 float fCos = clamp_tpl(fEmitCos, emit.fCosMin, emit.fCosMax);
  312.                 Vec3 vEmitMax = emit.vAxis * fCos + vEmitPerpX * sqrt_fast_tpl((1.f - fCos * fCos) / (fPerpLenSq + FLT_MIN));
  313.                 vEmitMax *= if_else(vEmitMax * vRefDir >= 0.0f, emit.fSpeedMin, emit.fSpeedMax);
  314.                 return vEmitMax;
  315.         }
  316. }
  317.  
  318. void AddEmitDirs(AABB& bb, Vec3 const& vRefDir, SEmitParams const& emit, SForceParams const& force, float fTime, int nAxes)
  319. {
  320.         Vec3 vEmit = GetExtremeEmitVec(vRefDir, emit);
  321.         AddTravel(bb, vEmit, force, fTime, nAxes);
  322.         vEmit = GetExtremeEmitVec(-vRefDir, emit);
  323.         AddTravel(bb, vEmit, force, fTime, nAxes);
  324. }
  325.  
  326. inline float GetSinMax(float fCosMin, float fCosMax)
  327. {
  328.         return fCosMin * fCosMax < 0.f ? 1.f : sqrtf(1.f - min(fCosMin * fCosMin, fCosMax * fCosMax));
  329. }
  330.  
  331. inline float MaxComponent(Vec3 const& v)
  332. {
  333.         return max(max(abs(v.x), abs(v.y)), abs(v.z));
  334. }
  335.  
  336. // Compute bounds of a cone of emission, with applied gravity.
  337. void TravelBB(AABB& bb, SEmitParams const& emit, SForceParams const& force, float fTime, int nAxes)
  338. {
  339.         if (emit.fSpeedMax == 0.f)
  340.         {
  341.                 AddTravel(bb, Vec3(0), force, fTime, nAxes);
  342.                 return;
  343.         }
  344.         else if (emit.bSingleDir())
  345.         {
  346.                 AddTravel(bb, emit.vAxis * emit.fSpeedMax, force, fTime, nAxes);
  347.                 if (emit.fSpeedMin != emit.fSpeedMax)
  348.                         AddTravel(bb, emit.vAxis * emit.fSpeedMin, force, fTime, nAxes);
  349.                 return;
  350.         }
  351.  
  352.         // First expand box from emission in cardinal directions.
  353.         AddEmitDirs(bb, Vec3(1, 0, 0), emit, force, fTime, nAxes & 1);
  354.         AddEmitDirs(bb, Vec3(0, 1, 0), emit, force, fTime, nAxes & 2);
  355.         AddEmitDirs(bb, Vec3(0, 0, 1), emit, force, fTime, nAxes & 4);
  356.  
  357.         // Add extreme dirs along gravity.
  358.         Vec3 vDir;
  359.         if (CheckNormalize(vDir, force.vAccel))
  360.         {
  361.                 if (MaxComponent(vDir) < 0.999f)
  362.                         AddEmitDirs(bb, vDir, emit, force, fTime, 0);
  363.         }
  364.  
  365.         // And wind.
  366.         if (force.fDrag > 0.f && CheckNormalize(vDir, force.vWind))
  367.         {
  368.                 if (MaxComponent(vDir) < 0.999f)
  369.                         AddEmitDirs(bb, vDir, emit, force, fTime, 0);
  370.         }
  371. }
  372. };
  373.  
  374. void ResourceParticleParams::GetStaticBounds(AABB& bb, const QuatTS& loc, const SPhysForces& forces, const FStaticBounds& opts) const
  375. {
  376.         // Compute spawn source box.
  377.         bb = GetEmitOffsetBounds();
  378.  
  379.         if (bSpaceLoop && bBindEmitterToCamera)
  380.         {
  381.                 // Use CameraMaxDistance as additional space loop size.
  382.                 bb.max.y = max(bb.max.y, +fCameraMaxDistance);
  383.         }
  384.  
  385.         bb.SetTransformedAABB(Matrix34(loc), bb);
  386.         bb.Expand(opts.vSpawnSize);
  387.  
  388.         if (!bSpaceLoop)
  389.         {
  390.                 AABB bbTrav(0.f);
  391.                 GetMaxTravelBounds(bbTrav, loc, forces, opts);
  392.  
  393.                 // Expand by a safety factor.
  394.                 bbTrav.min *= (1.f + fTRAVEL_SAFETY);
  395.                 bbTrav.max *= (1.f + fTRAVEL_SAFETY);
  396.  
  397.                 bb.Augment(bbTrav);
  398.         }
  399.  
  400.         if (eFacing == eFacing.Water && HasWater(forces.plWater))
  401.         {
  402.                 // Move to water plane
  403.                 float fDist0 = bb.min.z - forces.plWater.DistFromPlane(bb.min);
  404.                 float fDistD = abs(forces.plWater.n.x * (bb.max.x - bb.min.x)) + abs(forces.plWater.n.y * (bb.max.y - bb.min.y));
  405.                 bb.min.z = fDist0 - fDistD;
  406.                 bb.max.z = fDist0 + fDistD;
  407.         }
  408.  
  409.         // Particle size.
  410.         if (opts.bWithSize)
  411.         {
  412.                 float fMaxSize = GetMaxVisibleSize();
  413.                 fMaxSize *= loc.s * (1.f + fSIZE_SAFETY);
  414.                 bb.Expand(Vec3(fMaxSize));
  415.         }
  416.         if (bMoveRelativeEmitter)
  417.                 // Expand a bit for constant movement inaccuracy.
  418.                 bb.Expand(Vec3(0.01f));
  419. }
  420.  
  421. float ResourceParticleParams::GetMaxVisibleSize() const
  422. {
  423.         float fMaxSize = fSize.GetMaxValue() * GetMaxObjectSize(pStatObj);
  424.         if (LightSource.fIntensity)
  425.                 fMaxSize = max(fMaxSize, LightSource.fRadius.GetMaxValue());
  426.         fMaxSize += abs(fCameraDistanceOffset);
  427.         return fMaxSize;
  428. }
  429.  
  430. template<class Var>
  431. void ResourceParticleParams::GetEmitParams(SEmitParams& emit, const QuatTS& loc, const SPhysForces& forces, const FStaticBounds& opts, const Var& var) const
  432. {
  433.         emit.vAxis = bFocusGravityDir ? -forces.vAccel.GetNormalizedSafe(Vec3(0, 0, -1)) : loc.q.GetColumn1();
  434.  
  435.         // Max sin and cos of emission relative to emit dir.
  436.         if (fFocusCameraDir)
  437.         {
  438.                 // Focus can point in any direction.
  439.                 emit.SetOmniDir();
  440.         }
  441.         else if (fFocusAngle)
  442.         {
  443.                 // Incorporate focus variation into min/max cos.
  444.                 float fAngleMax = DEG2RAD(fFocusAngle(var.RMax(), var.EMax())), fAngleMin = DEG2RAD(fFocusAngle(var.RMin(), var.EMin()));
  445.                 float fAzimuthMax = DEG2RAD(fFocusAzimuth(var.RMax(), var.EMax())), fAzimuthMin = DEG2RAD(fFocusAzimuth(var.RMin(), var.EMin()));
  446.  
  447.                 emit.fCosMax = cosf(DEG2RAD(fEmitAngle(VMIN, var.EMin())));
  448.                 emit.fCosMin = cosf(min(opts.fAngMax + DEG2RAD(fEmitAngle(VMAX, var.EMax())) + (fAngleMax - fAngleMin + fAzimuthMax - fAzimuthMin) * 0.5f, gf_PI));
  449.  
  450.                 // Rotate focus about axis.
  451.                 emit.vAxis = (loc.q * Quat::CreateRotationXYZ(Ang3((fAngleMin + fAngleMax) * 0.5f, (fAzimuthMin + fAzimuthMax) * 0.5f, 0))).GetColumn1();
  452.         }
  453.         else
  454.         {
  455.                 // Fixed focus.
  456.                 emit.fCosMax = cosf(DEG2RAD(fEmitAngle(VMIN, var.EMin())));
  457.                 emit.fCosMin = cosf(DEG2RAD(fEmitAngle(VMAX, var.EMax())));
  458.         }
  459.  
  460.         if (bEmitOffsetDir)
  461.         {
  462.                 AABB bb = GetEmitOffsetBounds();
  463.                 if (bb.max.z > 0)
  464.                         emit.fCosMax = 1.f;
  465.                 if (bb.min.z < 0)
  466.                         emit.fCosMin = -1.f;
  467.                 else if (bb.min.x < 0 || bb.max.x > 0 || bb.min.y < 0 || bb.max.y > 0)
  468.                         emit.fCosMin = min(emit.fCosMin, 0.f);
  469.         }
  470.  
  471.         emit.fSpeedMin = fSpeed(VMIN, var.EMin()) * loc.s * opts.fSpeedScale;
  472.         emit.fSpeedMax = fSpeed(VMAX, var.EMax()) * loc.s * opts.fSpeedScale;
  473. }
  474.  
  475. void ResourceParticleParams::GetMaxTravelBounds(AABB& bbResult, const QuatTS& loc, const SPhysForces& forces, const FStaticBounds& opts) const
  476. {
  477.         float fTime = min(+opts.fMaxLife, GetMaxParticleLife());
  478.         if (fTime <= 0.f)
  479.                 return;
  480.  
  481.         // Emission direction.
  482.         SEmitParams emit;
  483.         GetEmitParams(emit, loc, forces, opts, FEmitterRandom());
  484.  
  485.         SForceParams force;
  486.         force.vWind = forces.vWind * fAirResistance.fWindScale;
  487.         force.fStretch = !GetTailSteps() ? fStretch(VMAX) * max(fStretch.fOffsetRatio + 1.f, 0.f) : 0.f;
  488.  
  489.         // Perform separate checks for variations in drag and gravity.
  490.         float afDrag[2] = { fAirResistance(VMAX), fAirResistance(VMIN) };
  491.         float afGrav[2] = { fGravityScale(VMAX), fGravityScale(VMIN) };
  492.         for (int iDragIndex = (afDrag[1] != afDrag[0] ? 1 : 0); iDragIndex >= 0; iDragIndex--)
  493.         {
  494.                 force.fDrag = afDrag[iDragIndex];
  495.                 for (int iGravIndex = (afGrav[1] != afGrav[0] ? 1 : 0); iGravIndex >= 0; iGravIndex--)
  496.                 {
  497.                         force.vAccel = forces.vAccel * afGrav[iGravIndex] + vAcceleration * loc.s;
  498.                         Travel::TravelBB(bbResult, emit, force, fTime, 7);
  499.                 }
  500.         }
  501.  
  502.         if (fTurbulence3DSpeed)
  503.         {
  504.                 // Expansion from 3D turbulence.        a = T t^-/2;  d = a/2 t^2 = T/2 t^(3/2)
  505.                 float fAccel = fTurbulence3DSpeed(VMAX) * isqrt_tpl(fTime) * (1.f + fTRAVEL_SAFETY);
  506.                 SForceParams forcesTurb;
  507.                 forcesTurb.vAccel = Vec3(fAccel);
  508.                 forcesTurb.vWind.zero();
  509.                 forcesTurb.fDrag = fAirResistance(VMIN);
  510.                 forcesTurb.fStretch = 0.0f;
  511.                 Vec3 vTrav(ZERO), vVel(ZERO);
  512.                 Travel::Travel(vTrav, vVel, fTime, forcesTurb);
  513.                 bbResult.Expand(vTrav);
  514.         }
  515.  
  516.         // Expansion from spiral vortex.
  517.         if (fTurbulenceSpeed)
  518.         {
  519.                 float fVortex = fTurbulenceSize(VMAX) * loc.s;
  520.                 bbResult.Expand(Vec3(fVortex));
  521.         }
  522. }
  523.  
  524. float ResourceParticleParams::GetTravelBounds(AABB& bbResult, const QuatTS& loc, const SForceParams& forces, const FStaticBounds& opts, const FEmitterFixed& var) const
  525. {
  526.         float fTime = min(+opts.fMaxLife, GetMaxParticleLife());
  527.         if (fTime <= 0.f)
  528.                 return 0.f;
  529.  
  530.         // Emission direction
  531.         SEmitParams emit;
  532.         GetEmitParams(emit, loc, forces, opts, var);
  533.  
  534.         // Get destination BB
  535.  
  536.         bbResult.Reset();
  537.         Travel::TravelBB(bbResult, emit, forces, fTime, 0);
  538.         bbResult.Move(loc.t);
  539.  
  540.         // Estimate distance along arc
  541.         return Travel::TravelDistanceApprox(emit.vAxis * (emit.fSpeedMax + emit.fSpeedMin) * 0.5f, fTime, forces);
  542. }
  543.  
  544. void ResourceParticleParams::ComputeShaderData()
  545. {
  546.         ShaderData = SParticleShaderData();
  547.  
  548.         ShaderData.m_tileSize[0] = 1.0f / TextureTiling.nTilesX;
  549.         ShaderData.m_tileSize[1] = 1.0f / TextureTiling.nTilesY;
  550.         ShaderData.m_frameCount = (float)TextureTiling.nAnimFramesCount;
  551.         ShaderData.m_firstTile = (float)TextureTiling.nFirstTile;
  552.  
  553.         ShaderData.m_expansion[0] =
  554.           eFacing == eFacing.Camera || eFacing == eFacing.CameraX ? 1.f   // X expansion and curvature
  555.           : -1.f;                                                         // X expansion only
  556.         ShaderData.m_expansion[1] =
  557.           HasVariableVertexCount() ? 0.f                                  // no Y expansion or curvature
  558.           : eFacing == eFacing.Camera ? 1.f                               // Y expansion and curvature
  559.           : -1.f;                                                         // Y expansion only
  560.         ShaderData.m_curvature =
  561.           eFacing == eFacing.Camera || eFacing == eFacing.CameraX ?
  562.           +fCurvature
  563.           : 0.f;
  564.         ShaderData.m_textureFrequency = Connection ? float(Connection.fTextureFrequency) : 1.0f;
  565.  
  566.         // light energy normalizer for the light equation:
  567.         //      L = max(0, cos(a)*(1-y)+y)
  568.         //      cos(a) = dot(l, n)
  569.         //      y = back lighting
  570.         const float y = fDiffuseBacklighting;
  571.         const float energyNorm = (y < 0.5) ? (1 - y) : (1.0f / (4.0f * y));
  572.         const float kiloScale = 1000.0f;
  573.         const float toLightUnitScale = kiloScale / RENDERER_LIGHT_UNIT_SCALE;
  574.         ShaderData.m_diffuseLighting = fDiffuseLighting * energyNorm;
  575.         ShaderData.m_emissiveLighting = fEmissiveLighting * toLightUnitScale;
  576.         ShaderData.m_backLighting = fDiffuseBacklighting;
  577.  
  578.         ShaderData.m_softnessMultiplier = bSoftParticle.fSoftness;
  579.         ShaderData.m_sphericalApproximation = fSphericalApproximation * 0.999f;
  580.         ShaderData.m_thickness = fVolumeThickness;
  581.  
  582.         // x = alpha scale, y = alpha clip min, z = alpha clip max
  583.         float fAlphaScale = fAlpha.GetMaxValue();
  584.         ShaderData.m_alphaTest[0][0] = AlphaClip.fScale.Min * fAlphaScale;
  585.         ShaderData.m_alphaTest[1][0] = (AlphaClip.fScale.Max - AlphaClip.fScale.Min) * fAlphaScale;
  586.         ShaderData.m_alphaTest[0][1] = AlphaClip.fSourceMin.Min;
  587.         ShaderData.m_alphaTest[1][1] = AlphaClip.fSourceMin.Max - AlphaClip.fSourceMin.Min;
  588.         ShaderData.m_alphaTest[0][2] = AlphaClip.fSourceWidth.Min;
  589.         ShaderData.m_alphaTest[1][2] = AlphaClip.fSourceWidth.Max - AlphaClip.fSourceWidth.Min;
  590. }
  591.  
  592. void ResourceParticleParams::ComputeEnvironmentFlags()
  593. {
  594.         // Needs updated environ if particles interact with gravity or air.
  595.         nEnvFlags = 0;
  596.  
  597.         if (!bEnabled)
  598.                 return;
  599.  
  600.         if (eFacing == eFacing.Water)
  601.                 nEnvFlags |= ENV_WATER;
  602.         if (fAirResistance && fAirResistance.fWindScale)
  603.                 nEnvFlags |= ENV_WIND;
  604.         if (fGravityScale || bFocusGravityDir)
  605.                 nEnvFlags |= ENV_GRAVITY;
  606.         if (ePhysicsType == ePhysicsType.SimpleCollision)
  607.         {
  608.                 if (bCollideTerrain)
  609.                         nEnvFlags |= ENV_TERRAIN;
  610.                 if (bCollideStaticObjects)
  611.                         nEnvFlags |= ENV_STATIC_ENT;
  612.                 if (bCollideDynamicObjects)
  613.                         nEnvFlags |= ENV_DYNAMIC_ENT;
  614.                 if (nEnvFlags & ENV_COLLIDE_ANY)
  615.                         nEnvFlags |= ENV_COLLIDE_INFO;
  616.         }
  617.         else if (ePhysicsType >= EPhysics::SimplePhysics && nMaxCollisionEvents > 0)
  618.                 nEnvFlags |= ENV_COLLIDE_INFO;
  619.  
  620.         // Rendering params.
  621.         if (fSize && fAlpha)
  622.         {
  623.                 if (pStatObj != 0)
  624.                         nEnvFlags |= REN_GEOMETRY;
  625.                 else if (eFacing == eFacing.Decal)
  626.                 {
  627.                         if (pMaterial != 0)
  628.                                 nEnvFlags |= REN_DECAL;
  629.                 }
  630.                 else
  631.                 {
  632.                         if (pMaterial != 0)
  633.                                 nEnvFlags |= REN_SPRITE;
  634.                 }
  635.                 if (bCastShadows && pStatObj)   // only particle with geometry can cast shadows right now
  636.                         nEnvFlags |= REN_CAST_SHADOWS;
  637.  
  638.                 if ((nEnvFlags & (REN_SPRITE | REN_GEOMETRY)) && eBlendType != eBlendType.Additive && !Connection)
  639.                         nEnvFlags |= REN_SORT;
  640.         }
  641.         if (LightSource.fIntensity && LightSource.fRadius)
  642.                 nEnvFlags |= REN_LIGHTS;
  643.         if (!sStartTrigger.empty() || !sStopTrigger.empty())
  644.                 nEnvFlags |= EFF_AUDIO;
  645.         if (eForceGeneration != eForceGeneration.None)
  646.                 nEnvFlags |= EFF_FORCE;
  647.  
  648.         if (!fParticleLifeTime || bRemainWhileVisible           // Infinite particle lifetime
  649.             || bBindEmitterToCamera                             // Visible every frame
  650.             || (GetCVars()->e_ParticlesMinPhysicsDynamicBounds && ePhysicsType >= GetCVars()->e_ParticlesMinPhysicsDynamicBounds)
  651.             || bForceDynamicBounds
  652.             )
  653.                 nEnvFlags |= EFF_DYNAMIC_BOUNDS;
  654.  
  655.         if (bBindEmitterToCamera)
  656.                 nEnvFlags |= REN_BIND_CAMERA;
  657.  
  658.         //
  659.         // Compute desired and allowed renderer flags.
  660.         //
  661.  
  662.         nRenObjFlags.Clear();
  663.         nRenObjFlags.SetState(1,
  664.                               eBlendType
  665.                               + !HasVariableVertexCount() * FOB_POINT_SPRITE
  666.                               + bOctagonalShape * FOB_OCTAGONAL
  667.                               + bTessellation * FOB_ALLOW_TESSELLATION
  668.                               + TextureTiling.bAnimBlend * OS_ANIM_BLEND
  669.                               + OS_ENVIRONMENT_CUBEMAP // this will be automatically turned off when lighting is disabled
  670.                               + bReceiveShadows * FOB_INSHADOW
  671.                               + bNotAffectedByFog * FOB_NO_FOG
  672.                               + (bSoftParticle && bSoftParticle.fSoftness) * FOB_SOFT_PARTICLE
  673.                               + bDrawOnTop * OS_NODEPTH_TEST
  674.                               + bDrawNear * FOB_NEAREST
  675.                               );
  676.  
  677.         // Disable impossible states.
  678.         nRenObjFlags.SetState(-1,
  679.                               bDrawOnTop * FOB_NEAREST
  680.                               + (TextureTiling.nAnimFramesCount <= 1) * OS_ANIM_BLEND
  681.                               + (bDrawNear || bDrawOnTop) * FOB_SOFT_PARTICLE
  682.                               + bDrawNear * FOB_ALLOW_TESSELLATION
  683.                               + !fDiffuseLighting * (OS_ENVIRONMENT_CUBEMAP | FOB_INSHADOW)
  684.                               + HasVariableVertexCount() * (FOB_MOTION_BLUR | FOB_OCTAGONAL | FOB_POINT_SPRITE)
  685.                               );
  686.  
  687.         // Construct config spec mask for allowed consoles.
  688.         mConfigSpecMask = ((BIT(eConfigMax) * 2 - 1) & ~(BIT(eConfigMin) - 1)) << CONFIG_LOW_SPEC;
  689.         mConfigSpecMask |=
  690.           Platforms.PS4 * BIT(CONFIG_ORBIS)
  691.           + Platforms.XBoxOne * BIT(CONFIG_DURANGO)
  692.         ;
  693. }
  694.  
  695. bool ResourceParticleParams::IsActive() const
  696. {
  697.         if (!bEnabled)
  698.                 return false;
  699.  
  700.         // If Quality cvar is set, it must match ConfigMin-Max params.
  701.         if (GetCVars()->e_ParticlesQuality)
  702.         {
  703.                 if (!(mConfigSpecMask & BIT(GetCVars()->e_ParticlesQuality)))
  704.                         return false;
  705.         }
  706.  
  707.         // Get platform / config spec
  708.         ESystemConfigSpec config_spec = gEnv->pSystem->GetConfigSpec();
  709.  
  710.         if (config_spec <= CONFIG_VERYHIGH_SPEC)
  711.         {
  712.                 // PC platform. Match DX settings.
  713.                 if (!Platforms.PCDX11)
  714.                         return false;
  715.         }
  716.  
  717.         if (config_spec > CONFIG_VERYHIGH_SPEC || !GetCVars()->e_ParticlesQuality)
  718.         {
  719.                 // For consoles, or PC if e_ParticlesQuality not set, match against pure config spec
  720.                 if (!(mConfigSpecMask & BIT(config_spec)))
  721.                         return false;
  722.         }
  723.  
  724.         return true;
  725. }
  726.  
  727. //////////////////////////////////////////////////////////////////////////
  728. int ResourceParticleParams::LoadResources(const char* pEffectName)
  729. {
  730.         // Load only what is not yet loaded. Check everything, but caller may check params.bResourcesLoaded first.
  731.         // Call UnloadResources to force unload/reload.
  732.         LOADING_TIME_PROFILE_SECTION(gEnv->pSystem);
  733.  
  734.         if (!bEnabled)
  735.         {
  736.                 nEnvFlags = 0;
  737.                 return 0;
  738.         }
  739.  
  740.         if (ResourcesLoaded() || !gEnv->pRenderer)
  741.         {
  742.                 ComputeShaderData();
  743.                 ComputeEnvironmentFlags();
  744.                 return 0;
  745.         }
  746.  
  747.         int nLoaded = 0;
  748.  
  749.         // Load material.
  750.         if (!pMaterial && (!sTexture.empty() || !sMaterial.empty()))
  751.         {
  752.                 if (!sMaterial.empty())
  753.                 {
  754.                         pMaterial = Get3DEngine()->GetMaterialManager()->LoadMaterial(sMaterial.c_str());
  755.                         if (!pMaterial || pMaterial == Get3DEngine()->GetMaterialManager()->GetDefaultMaterial())
  756.                         {
  757.                                 CryWarning(VALIDATOR_MODULE_3DENGINE, VALIDATOR_WARNING, "Particle effect material %s not found",
  758.                                            sMaterial.c_str());
  759.                                 // Load error texture for artist debugging.
  760.                                 pMaterial = 0;
  761.                         }
  762.                 }
  763.                 else if (!sTexture.empty() && sGeometry.empty())
  764.                 {
  765.                         const uint32 textureLoadFlags = FT_DONT_STREAM;
  766.                         const int nTexId = GetRenderer()->EF_LoadTexture(sTexture.c_str(), textureLoadFlags)->GetTextureID();
  767.                         if (nTexId <= 0)
  768.                                 CryWarning(VALIDATOR_MODULE_3DENGINE, VALIDATOR_WARNING, "Particle effect texture %s not found", sTexture.c_str());
  769.                         else
  770.                                 nLoaded++;
  771.  
  772.                         pMaterial = gEnv->p3DEngine->GetMaterialManager()->CreateMaterial(pEffectName);
  773.                         if (gEnv->pRenderer)
  774.                         {
  775.                                 SInputShaderResourcesPtr pResources = gEnv->pRenderer->EF_CreateInputShaderResource();
  776.                                 pResources->m_Textures[EFTT_DIFFUSE].m_Name = sTexture.c_str();
  777.                                 SShaderItem shaderItem = gEnv->pRenderer->EF_LoadShaderItem("Particles", false, 0, pResources);
  778.                                 pMaterial->AssignShaderItem(shaderItem);
  779.                         }
  780.                         Vec3 white = Vec3(1.0f, 1.0f, 1.0f);
  781.                         float defaultOpacity = 1.0f;
  782.                         pMaterial->SetGetMaterialParamVec3("diffuse", white, false);
  783.                         pMaterial->SetGetMaterialParamFloat("opacity", defaultOpacity, false);
  784.                         pMaterial->RequestTexturesLoading(0.0f);
  785.                 }
  786.                 else if (sGeometry.empty())
  787.                 {
  788.                         SShaderItem shader = pMaterial->GetShaderItem();
  789.                         if (!shader.m_pShader)
  790.                                 CryWarning(VALIDATOR_MODULE_3DENGINE, VALIDATOR_WARNING, "Particle effect material %s has invalid shader",
  791.                                            sMaterial.c_str());
  792.                         nLoaded++;
  793.                 }
  794.                 fTexAspect = 0.0f;
  795.         }
  796.         if (eFacing == eFacing.Decal && sMaterial.empty())
  797.         {
  798.                 CryWarning(VALIDATOR_MODULE_3DENGINE, VALIDATOR_WARNING, "Particle effect has no material for decal, texture = %s",
  799.                            sTexture.c_str());
  800.         }
  801.  
  802.         // Set aspect ratio.
  803.         if (fTexAspect == 0.f)
  804.         {
  805.                 UpdateTextureAspect();
  806.         }
  807.  
  808.         // Load geometry.
  809.         if (!pStatObj && !sGeometry.empty())
  810.         {
  811.                 pStatObj = Get3DEngine()->LoadStatObj(sGeometry.c_str(), NULL, NULL, bStreamable);
  812.                 if (!pStatObj)
  813.                         CryWarning(VALIDATOR_MODULE_3DENGINE, VALIDATOR_WARNING, "Particle effect geometry not found: %s", sGeometry.c_str());
  814.                 else
  815.                         nLoaded++;
  816.         }
  817.  
  818.         ComputeShaderData();
  819.         ComputeEnvironmentFlags();
  820.  
  821.         nEnvFlags |= EFF_LOADED;
  822.         return nLoaded;
  823. }
  824.  
  825. void ResourceParticleParams::UpdateTextureAspect()
  826. {
  827.         TextureTiling.Correct();
  828.  
  829.         fTexAspect = 1.f;
  830.  
  831.         if (!pMaterial)
  832.                 return;
  833.         SShaderItem shaderItem = pMaterial->GetShaderItem();
  834.         IRenderShaderResources* pResources = shaderItem.m_pShaderResources;
  835.         if (!pResources)
  836.                 return;
  837.  
  838.         SEfResTexture* pResTexture = pResources->GetTexture(EFTT_DIFFUSE);
  839.         ITexture* pTexture = pResTexture ? pResTexture->m_Sampler.m_pITex : nullptr;
  840.         if (pTexture)
  841.         {
  842.                 float fWidth = pTexture->GetWidth() / (float)TextureTiling.nTilesX;
  843.                 float fHeight = pTexture->GetHeight() / (float)TextureTiling.nTilesY;
  844.                 if (fHeight == 0.f)
  845.                         fTexAspect = 0.f;
  846.                 else
  847.                         fTexAspect = fWidth / fHeight;
  848.         }
  849. }
  850.  
  851. //////////////////////////////////////////////////////////////////////////
  852. void ResourceParticleParams::UnloadResources()
  853. {
  854.         pStatObj = 0;
  855.         pMaterial = 0;
  856.         fTexAspect = 0.f;
  857.         nEnvFlags &= ~EFF_LOADED;
  858. }
  859.  
  860. //////////////////////////////////////////////////////////////////////////
  861. //
  862. // Struct and TypeInfo for reading older params
  863. //
  864. struct CompatibilityParticleParams
  865.         : ParticleParams, ZeroInit<CompatibilityParticleParams>, Cry3DEngineBase
  866. {
  867.         int    nVersion;
  868.         string sSandboxVersion;
  869.  
  870.         // Version 26
  871.         float fBounciness;
  872.         DEFINE_ENUM(EMoveRelative,
  873.                     No,
  874.                     Yes,
  875.                     YesWithTail
  876.                     )
  877.         EMoveRelative eMoveRelEmitter;        // Particle motion is in emitter space
  878.  
  879.         // Version 25
  880.         ETrinary tDX11;
  881.         bool     bGeometryInPieces;
  882.  
  883.         // Version 24
  884.         float fAlphaTest;
  885.         float fCameraDistanceBias;
  886.         int   nDrawLast;
  887.  
  888.         // Version 22
  889.         float  fPosRandomOffset;
  890.         float  fAlphaScale;
  891.         float  fGravityScaleBias;
  892.         float  fCollisionPercentage;
  893.  
  894.         bool   bSimpleParticle;
  895.         bool   bSecondGeneration, bSpawnOnParentCollision, bSpawnOnParentDeath;
  896.  
  897.         string sAllowHalfRes;
  898.  
  899.         // Version 20
  900.         bool bBindToEmitter;
  901.         bool bTextureUnstreamable, bGeometryUnstreamable;
  902.  
  903.         // Version 19
  904.         bool bIgnoreAttractor;
  905.  
  906.         void Correct(class CParticleEffect* pEffect);
  907.  
  908.         // Suppress uninitMemberVar due to inheritance from ZeroInit templated class
  909.         // cppcheck-suppress uninitMemberVar
  910.         CompatibilityParticleParams(int nVer, cstr sSandboxVer, const ParticleParams& paramsDefault)
  911.                 : ParticleParams(paramsDefault),
  912.                 nVersion(nVer),
  913.                 sSandboxVersion(sSandboxVer)
  914.         {
  915.                 if (nVersion < 21)
  916.                 {
  917.                         // Use old default values.
  918.                         fCount = 1.f;
  919.                         fParticleLifeTime = 1.f;
  920.                         fSpeed = 1.f;
  921.                 }
  922.         }
  923.  
  924.         STRUCT_INFO;
  925. };
  926.  
  927. //////////////////////////////////////////////////////////////////////////
  928. STRUCT_INFO_BEGIN(CompatibilityParticleParams)
  929. BASE_INFO(ParticleParams)
  930.  
  931. // Version 26
  932. VAR_INFO(fBounciness)
  933. VAR_INFO(eMoveRelEmitter)
  934.  
  935. // Version 24
  936. ALIAS_INFO(fRotationalDragScale, fAirResistance.fRotationalDragScale)
  937. ALIAS_INFO(fWindScale, fAirResistance.fWindScale)
  938. VAR_INFO(fAlphaTest)
  939. VAR_INFO(fCameraDistanceBias)
  940. VAR_INFO(nDrawLast)
  941.  
  942. // Version 22
  943. VAR_INFO(fPosRandomOffset)
  944. VAR_INFO(fAlphaScale)
  945. VAR_INFO(fGravityScaleBias)
  946. VAR_INFO(fCollisionPercentage)
  947.  
  948. VAR_INFO(bSimpleParticle)
  949. VAR_INFO(bSecondGeneration)
  950. VAR_INFO(bSpawnOnParentCollision)
  951. VAR_INFO(bSpawnOnParentDeath)
  952.  
  953. VAR_INFO(sAllowHalfRes)
  954.  
  955. // Ver 22
  956. ALIAS_INFO(bOctogonalShape, bOctagonalShape)
  957. ALIAS_INFO(bOffsetInnerScale, fOffsetInnerFraction)
  958. VAR_INFO(bSimpleParticle)
  959.  
  960. // Version 20
  961. VAR_INFO(bBindToEmitter)
  962. VAR_INFO(bTextureUnstreamable)
  963. VAR_INFO(bGeometryUnstreamable)
  964.  
  965. // Ver 19
  966. ALIAS_INFO(fLightSourceRadius, LightSource.fRadius)
  967. ALIAS_INFO(fLightSourceIntensity, LightSource.fIntensity)
  968. ALIAS_INFO(fStretchOffsetRatio, fStretch.fOffsetRatio)
  969. ALIAS_INFO(nTailSteps, fTailLength.nTailSteps)
  970. VAR_INFO(bIgnoreAttractor)
  971. STRUCT_INFO_END(CompatibilityParticleParams)
  972.  
  973. //////////////////////////////////////////////////////////////////////////
  974. void CompatibilityParticleParams::Correct(CParticleEffect* pEffect)
  975. {
  976.         CTypeInfo const& info = ::TypeInfo(this);
  977.  
  978.         // Convert any obsolete parameters set.
  979.         switch (nVersion)
  980.         {
  981.         case 19:
  982.                 if (bIgnoreAttractor)
  983.                         TargetAttraction.eTarget = TargetAttraction.eTarget.Ignore;
  984.  
  985.         case 20:
  986.                 // Obsolete parameter, set equivalent params.
  987.                 if (bBindToEmitter && !ePhysicsType)
  988.                 {
  989.                         bMoveRelativeEmitter.base() = true;
  990.                         vPositionOffset.zero();
  991.                         vRandomOffset.zero();
  992.                         fSpeed = 0.f;
  993.                         fInheritVelocity = 0.f;
  994.                         fGravityScale = 0.f;
  995.                         fAirResistance.Set(0.f);
  996.                         vAcceleration.zero();
  997.                         fTurbulence3DSpeed = 0.f;
  998.                         fTurbulenceSize = 0.f;
  999.                         fTurbulenceSpeed = 0.f;
  1000.                         bSpaceLoop = false;
  1001.                 }
  1002.  
  1003.                 if (bTextureUnstreamable || bGeometryUnstreamable)
  1004.                         bStreamable = false;
  1005.  
  1006.         case 21:
  1007.                 if (fFocusAzimuth.GetRandomRange() > 0.f)
  1008.                 {
  1009.                         // Convert confusing semantics of FocusAzimuth random variation in previous version.
  1010.                         if (fFocusAngle.GetMaxValue() == 0.f)
  1011.                         {
  1012.                                 // If FocusAngle = 0, FocusAzimuth is inactive.
  1013.                                 fFocusAzimuth.Set(0.f);
  1014.                         }
  1015.                         else if (fFocusAzimuth.GetMaxValue() > 0.f && fFocusAzimuth.GetRandomRange() == 1.f)
  1016.                         {
  1017.                                 // Assume intention was to vary between 0 and max value, as with all other params.
  1018.                         }
  1019.                         else
  1020.                         {
  1021.                                 // Convert previous absolute-360-based random range to standard relative.
  1022.                                 float fValMin = fFocusAzimuth.GetMaxValue();
  1023.                                 float fValMax = fValMin + fFocusAzimuth.GetRandomRange() * 360.f;
  1024.                                 if (fValMax > 360.f)
  1025.                                 {
  1026.                                         fValMax -= 360.f;
  1027.                                         fValMin -= 360.f;
  1028.                                 }
  1029.                                 if (-fValMin > fValMax)
  1030.                                         fFocusAzimuth.Set(fValMin, (fValMax - fValMin) / -fValMin);
  1031.                                 else
  1032.                                         fFocusAzimuth.Set(fValMax, (fValMax - fValMin) / fValMax);
  1033.                         }
  1034.                 }
  1035.  
  1036.         case 22:
  1037.                 // Replace special-purpose flag with equivalent settings.
  1038.                 if (bSimpleParticle)
  1039.                 {
  1040.                         sMaterial = "";
  1041.                         fEmissiveLighting = 1;
  1042.                         fDiffuseLighting = 0;
  1043.                         fDiffuseBacklighting = 0;
  1044.                         bReceiveShadows = bCastShadows = bTessellation = false;
  1045.                         bSoftParticle.set(false);
  1046.                         bNotAffectedByFog = true;
  1047.                 }
  1048.  
  1049.                 if (fAlphaScale != 0.f)
  1050.                         fAlpha.Set(fAlpha.GetMaxValue() * fAlphaScale);
  1051.  
  1052.                 // Convert obsolete bias to proper random range.
  1053.                 if (fGravityScaleBias != 0.f)
  1054.                         AdjustParamRange(fGravityScale, fGravityScaleBias);
  1055.  
  1056.                 if (sAllowHalfRes == "Allowed" || sAllowHalfRes == "Forced")
  1057.                         bHalfRes = true;
  1058.  
  1059.                 if (fCollisionPercentage != 0.f)
  1060.                         fCollisionFraction = fCollisionPercentage * 0.01f;
  1061.  
  1062.                 // Convert to current spawn enum.
  1063.                 if (bSecondGeneration)
  1064.                 {
  1065.                         eSpawnIndirection =
  1066.                                 bSpawnOnParentCollision ? ESpawn::ParentCollide
  1067.                                 : bSpawnOnParentDeath ? ESpawn::ParentDeath
  1068.                                 : ESpawn::ParentStart;
  1069.                 }
  1070.  
  1071.                 if (fPosRandomOffset != 0.f)
  1072.                 {
  1073.                         // Convert to new roundness fraction.
  1074.                         vRandomOffset = Vec3(vRandomOffset) + Vec3(fPosRandomOffset);
  1075.                         fOffsetRoundness = fPosRandomOffset / max(max(vRandomOffset.x, vRandomOffset.y), vRandomOffset.z);
  1076.                 }
  1077.  
  1078.         case 23:
  1079.         case 24:
  1080.                 // Scaling by parent size.
  1081.                 // Incorrect scaling of speed and offsets by parent particle size:
  1082.                 //   Particle Version < 22, CryEngine < 3.3.3:         Correct
  1083.                 //   Particle version = 22, CryEngine 3.3.4 .. 3.4.x:  Correct
  1084.                 //   Particle version = 22, CryEngine 3.5.1 .. 3.5.3:  INCORRECT
  1085.                 //   Particle version = 23, CryEngine 3.5.4:           INCORRECT
  1086.                 //   Particle version = 24, CryEngine 3.5.5 .. 3.5.6:  INCORRECT
  1087.                 //   Particle version = 24, CryEngine 3.5.7 .. 3.5.9:  Correct
  1088.                 //   Particle version = 25, CryEngine 3.5.10 ..:       Correct
  1089.                 if ((nVersion == 23 || nVersion >= 22 && sSandboxVersion >= "3.5.1" && sSandboxVersion < "3.5.7") && eSpawnIndirection && pEffect->GetParent())
  1090.                 {
  1091.                         // Print diagnostics when any corrections made.
  1092.                         float fParentSize = pEffect->GetParent()->GetParticleParams().fSize.GetValueFromMod(1.f, 0.f);
  1093.                         if (fParentSize != 1.f)
  1094.                         {
  1095.                                 if (fSpeed)
  1096.                                 {
  1097.                                         Warning("Particle Effect '%s' (version %d, %s): Speed corrected by parent scale %g from %g to %g",
  1098.                                                 pEffect->GetFullName().c_str(), nVersion, sSandboxVersion.c_str(), fParentSize,
  1099.                                                 +fSpeed, +fSpeed * fParentSize);
  1100.                                         fSpeed.Set(fSpeed.GetMaxValue() * fParentSize);
  1101.                                 }
  1102.                                 if (!vPositionOffset.IsZero())
  1103.                                 {
  1104.                                         Warning("Particle Effect '%s' (version %d, %s): PositionOffset corrected by parent scale %g from (%g,%g,%g) to (%g,%g,%g)",
  1105.                                                 pEffect->GetFullName().c_str(), nVersion, sSandboxVersion.c_str(), fParentSize,
  1106.                                                 +vPositionOffset.x, +vPositionOffset.y, +vPositionOffset.z,
  1107.                                                 +vPositionOffset.x * fParentSize, +vPositionOffset.y * fParentSize, +vPositionOffset.z * fParentSize);
  1108.                                         vPositionOffset.x = vPositionOffset.x * fParentSize;
  1109.                                         vPositionOffset.y = vPositionOffset.y * fParentSize;
  1110.                                         vPositionOffset.z = vPositionOffset.z * fParentSize;
  1111.                                 }
  1112.                                 if (!vRandomOffset.IsZero())
  1113.                                 {
  1114.                                         Warning("Particle Effect '%s' (version %d, %s): RandomOffset corrected by parent scale %g from (%g,%g,%g) to (%g,%g,%g)",
  1115.                                                 pEffect->GetFullName().c_str(), nVersion, sSandboxVersion.c_str(), fParentSize,
  1116.                                                 +vRandomOffset.x, +vRandomOffset.y, +vRandomOffset.z,
  1117.                                                 +vRandomOffset.x * fParentSize, +vRandomOffset.y * fParentSize, +vRandomOffset.z * fParentSize);
  1118.                                         vRandomOffset.x = vRandomOffset.x * fParentSize;
  1119.                                         vRandomOffset.y = vRandomOffset.y * fParentSize;
  1120.                                         vRandomOffset.z = vRandomOffset.z * fParentSize;
  1121.                                 }
  1122.                         }
  1123.                 }
  1124.  
  1125.                 // Alpha test changes.
  1126.                 if (fAlphaTest != 0.f)
  1127.                         AlphaClip.fSourceMin.Min = AlphaClip.fSourceMin.Max = fAlphaTest;
  1128.  
  1129.                 // Sort param changes.
  1130.                 if (nDrawLast)
  1131.                         fSortOffset = nDrawLast * -0.01f;
  1132.                 if (fCameraDistanceBias != 0.f)
  1133.                         fCameraDistanceOffset = -fCameraDistanceBias;
  1134.  
  1135.         case 25:
  1136.                 // DX11 spec
  1137.                 if (tDX11 == ETrinary(false))
  1138.                         Platforms.PCDX11 = false;
  1139.  
  1140.                 // Fix reversed PivotY.
  1141.                 fPivotY.Set(-fPivotY.GetMaxValue());
  1142.  
  1143.                 if (bGeometryInPieces)
  1144.                         eGeometryPieces = eGeometryPieces.AllPieces;
  1145.                 else if (!sGeometry.empty())
  1146.                 {
  1147.                         if (_smart_ptr<IStatObj> pStatObj = Get3DEngine()->LoadStatObj(sGeometry.c_str(), NULL, NULL, bStreamable))
  1148.                                 if (GetSubGeometryCount(pStatObj))
  1149.                                         eGeometryPieces = eGeometryPieces.RandomPiece;
  1150.                 }
  1151.  
  1152.         case 26:
  1153.                 // Bounciness conversion
  1154.                 if (fBounciness > 0.f)
  1155.                         fElasticity = fBounciness;
  1156.                 else if (fBounciness < 0.f)
  1157.                 {
  1158.                         nMaxCollisionEvents = 1;
  1159.                         eFinalCollision = eFinalCollision.Die;
  1160.                 }
  1161.                 if (eMoveRelEmitter)
  1162.                 {
  1163.                         bMoveRelativeEmitter.base() = true;
  1164.                         if (eMoveRelEmitter == EMoveRelative::YesWithTail)
  1165.                                 bMoveRelativeEmitter.bMoveTail = true;
  1166.                 }
  1167.                 // back lighting and energy conservative correction
  1168.                 {
  1169.                         fDiffuseBacklighting = fDiffuseBacklighting * 0.5f;
  1170.                         const float y = fDiffuseBacklighting;
  1171.                         const float energyDenorm = (y < 0.5f) ? 1.0f / (1 - y) : 4.0f * y;
  1172.                         fDiffuseLighting = fDiffuseLighting * energyDenorm;
  1173.                 }
  1174.  
  1175.         case 27:
  1176.                 fEmissiveLighting = fEmissiveLighting * 10.0f;
  1177.         }
  1178.         ;
  1179.  
  1180.         // Universal corrections.
  1181.         if (!fTailLength)
  1182.                 fTailLength.nTailSteps = 0;
  1183.  
  1184.         TextureTiling.Correct();
  1185.  
  1186.         if (bSpaceLoop && fCameraMaxDistance == 0.f && GetEmitOffsetBounds().GetVolume() == 0.f)
  1187.         {
  1188.                 Warning("Particle Effect '%s' has zero space loop volume: disabled", pEffect->GetFullName().c_str());
  1189.                 bEnabled = false;
  1190.         }
  1191. }
  1192.  
  1193. //////////////////////////////////////////////////////////////////////////
  1194. // ParticleEffect implementation
  1195.  
  1196. //////////////////////////////////////////////////////////////////////////
  1197. CParticleEffect::CParticleEffect()
  1198.         : m_parent(0),
  1199.         m_pParticleParams(0)
  1200. {
  1201. }
  1202.  
  1203. CParticleEffect::CParticleEffect(const CParticleEffect& in, bool bResetInheritance)
  1204.         : m_parent(0),
  1205.         m_strName(in.m_strName),
  1206.         m_pParticleParams(new ResourceParticleParams(*in.m_pParticleParams))
  1207. {
  1208.         if (bResetInheritance)
  1209.                 m_pParticleParams->eInheritance = ParticleParams::EInheritance();
  1210.  
  1211.         m_children.resize(in.m_children.size());
  1212.         for (int i = 0; i < m_children.size(); ++i)
  1213.         {
  1214.                 m_children.at(i) = new CParticleEffect(in.m_children[i], bResetInheritance);
  1215.                 m_children[i].m_parent = this;
  1216.         }
  1217. }
  1218.  
  1219. CParticleEffect::CParticleEffect(const char* sName)
  1220.         : m_parent(0),
  1221.         m_pParticleParams(0),
  1222.         m_strName(sName)
  1223. {
  1224. }
  1225.  
  1226. #define TEMPORARY_EFFECT_NAME "(Temporary)"
  1227.  
  1228. CParticleEffect::CParticleEffect(const ParticleParams& params)
  1229.         : m_parent(0),
  1230.         m_strName(TEMPORARY_EFFECT_NAME)
  1231. {
  1232.         m_pParticleParams = new ResourceParticleParams(params);
  1233.         LoadResources(false);
  1234.  
  1235.         if (!(m_pParticleParams->nEnvFlags & (REN_ANY | EFF_ANY)))
  1236.         {
  1237.                 // Assume custom params with no specified texture or geometry can have geometry particles added.
  1238.                 m_pParticleParams->nEnvFlags |= REN_GEOMETRY;
  1239.         }
  1240. }
  1241.  
  1242. bool CParticleEffect::IsTemporary() const
  1243. {
  1244.         return m_strName == TEMPORARY_EFFECT_NAME;
  1245. }
  1246.  
  1247. //////////////////////////////////////////////////////////////////////////
  1248. CParticleEffect::~CParticleEffect()
  1249. {
  1250.         UnloadResources();
  1251.         for (auto& child : m_children)
  1252.                 child.m_parent = nullptr;
  1253.         delete m_pParticleParams;
  1254. }
  1255.  
  1256. void CParticleEffect::SetEnabled(bool bEnabled)
  1257. {
  1258.         if (bEnabled != IsEnabled())
  1259.         {
  1260.                 InstantiateParams();
  1261.                 m_pParticleParams->bEnabled = bEnabled;
  1262.                 CParticleManager::Instance()->UpdateEmitters(this);
  1263.         }
  1264. }
  1265.  
  1266. //////////////////////////////////////////////////////////////////////////
  1267. void CParticleEffect::SetName(cstr sNewName)
  1268. {
  1269.         if (!m_parent)
  1270.         {
  1271.                 // Top-level effect. Should be fully qualified with library and group names.
  1272.                 // Remove and reinsert in set.
  1273.                 if (m_strName != sNewName)
  1274.                 {
  1275.                         CParticleManager::Instance()->RenameEffect(this, sNewName);
  1276.                         m_strName = sNewName;
  1277.                 }
  1278.         }
  1279.         else
  1280.         {
  1281.                 // Child effect. Use only final component, and prefix with parent name.
  1282.                 cstr sBaseName = strrchr(sNewName, '.');
  1283.                 sBaseName = sBaseName ? sBaseName + 1 : sNewName;
  1284.  
  1285.                 // Ensure unique name.
  1286.                 stack_string sNewBase;
  1287.                 for (int i = m_parent->m_children.size() - 1; i >= 0; i--)
  1288.                 {
  1289.                         const CParticleEffect* pSibling = &m_parent->m_children[i];
  1290.                         if (pSibling != this && pSibling->m_strName == sBaseName)
  1291.                         {
  1292.                                 // Extract and replace number.
  1293.                                 cstr p = sBaseName + strlen(sBaseName);
  1294.                                 while (p > sBaseName && (p[-1] >= '0' && p[-1] <= '9'))
  1295.                                         p--;
  1296.                                 int nIndex = atoi(p);
  1297.                                 sNewBase.assign(sBaseName, p);
  1298.                                 sNewBase.append(stack_string(ToString(nIndex + 1)));
  1299.                                 sBaseName = sNewBase;
  1300.  
  1301.                                 // Reset loop.
  1302.                                 i = m_parent->m_children.size() - 1;
  1303.                         }
  1304.                 }
  1305.  
  1306.                 m_strName = sBaseName;
  1307.         }
  1308. }
  1309.  
  1310. //////////////////////////////////////////////////////////////////////////
  1311. void CParticleEffect::InstantiateParams()
  1312. {
  1313.         if (!m_pParticleParams)
  1314.                 m_pParticleParams = new ResourceParticleParams(CParticleManager::Instance()->GetDefaultParams());
  1315. }
  1316.  
  1317. //////////////////////////////////////////////////////////////////////////
  1318. const char* CParticleEffect::GetBaseName() const
  1319. {
  1320.         const char* sName = m_strName.c_str();
  1321.         if (m_parent)
  1322.         {
  1323.                 // Should only have base name.
  1324.                 assert(!strchr(sName, '.'));
  1325.         }
  1326.         else
  1327.         {
  1328.                 // Return everything after lib name.
  1329.                 if (const char* sBase = strchr(sName, '.'))
  1330.                         sName = sBase + 1;
  1331.         }
  1332.         return sName;
  1333. }
  1334.  
  1335. stack_string CParticleEffect::GetFullName() const
  1336. {
  1337.         if (!m_parent)
  1338.                 return m_strName;
  1339.         stack_string temp = m_parent->GetFullName();
  1340.         temp += ".";
  1341.         temp += m_strName;
  1342.         return temp;
  1343. }
  1344.  
  1345. //////////////////////////////////////////////////////////////////////////
  1346. void CParticleEffect::SetParent(IParticleEffect* pParent)
  1347. {
  1348.         if (pParent == m_parent)
  1349.                 return;
  1350.  
  1351.         _smart_ptr<IParticleEffect> ref_ptr = this;
  1352.         if (m_parent)
  1353.         {
  1354.                 int i = m_parent->FindChild(this);
  1355.                 if (i >= 0)
  1356.                         m_parent->m_children.erase(i);
  1357.                 CParticleManager::Instance()->UpdateEmitters(m_parent);
  1358.         }
  1359.         if (pParent)
  1360.         {
  1361.                 if (!m_parent)
  1362.                         // Was previously top-level effect.
  1363.                         CParticleManager::Instance()->DeleteEffect(this);
  1364.                 static_cast<CParticleEffect*>(pParent)->m_children.push_back(this);
  1365.         }
  1366.         m_parent = static_cast<CParticleEffect*>(pParent);
  1367.         CParticleManager::Instance()->UpdateEmitters(this);
  1368. }
  1369.  
  1370. //////////////////////////////////////////////////////////////////////////
  1371. void CParticleEffect::ClearChilds()
  1372. {
  1373.         m_children.clear();
  1374.         CParticleManager::Instance()->UpdateEmitters(this);
  1375. }
  1376.  
  1377. //////////////////////////////////////////////////////////////////////////
  1378. void CParticleEffect::InsertChild(int slot, IParticleEffect* pEffect)
  1379. {
  1380.         if (slot < 0)
  1381.                 slot = 0;
  1382.         if (slot > m_children.size())
  1383.                 slot = m_children.size();
  1384.  
  1385.         assert(pEffect);
  1386.         CParticleEffect* pEff = (CParticleEffect*) pEffect;
  1387.         pEff->m_parent = this;
  1388.         m_children.insert(m_children.begin() + slot, pEff);
  1389.         CParticleManager::Instance()->UpdateEmitters(this);
  1390. }
  1391.  
  1392. //////////////////////////////////////////////////////////////////////////
  1393. int CParticleEffect::FindChild(IParticleEffect* pEffect) const
  1394. {
  1395.         int i = 0;
  1396.         for (auto& child : m_children)
  1397.         {
  1398.                 if (&child == pEffect)
  1399.                         return i;
  1400.                 ++i;
  1401.         }
  1402.         return -1;
  1403. }
  1404.  
  1405. CParticleEffect* CParticleEffect::FindChild(const char* szChildName) const
  1406. {
  1407.         for (auto& child : m_children)
  1408.         {
  1409.                 if (child.m_strName == szChildName)
  1410.                         return &child;
  1411.         }
  1412.  
  1413.         return 0;
  1414. }
  1415.  
  1416. const CParticleEffect* CParticleEffect::FindActiveEffect(int nVersion) const
  1417. {
  1418.         // Find active effect in tree most closely matching version.
  1419.         const CParticleEffect* pFoundEffect = 0;
  1420.         int nFoundVersion = 0;
  1421.  
  1422.         if (!nVersion)
  1423.                 nVersion = nSERIALIZE_VERSION;
  1424.  
  1425.         // Check parent effect.
  1426.         if (m_pParticleParams && m_pParticleParams->IsActive())
  1427.         {
  1428.                 // Check version against effect name.
  1429.                 int nEffectVersion = atoi(GetBaseName());
  1430.                 if (nEffectVersion <= nVersion)
  1431.                 {
  1432.                         pFoundEffect = this;
  1433.                         nFoundVersion = nEffectVersion;
  1434.                 }
  1435.         }
  1436.  
  1437.         // Look for matching children, which take priority.
  1438.         for (auto& child : m_children)
  1439.         {
  1440.                 if (const CParticleEffect* pChild = child.FindActiveEffect(nVersion))
  1441.                 {
  1442.                         int nEffectVersion = atoi(pChild->GetBaseName());
  1443.                         if (nEffectVersion >= nFoundVersion)
  1444.                         {
  1445.                                 pFoundEffect = pChild;
  1446.                                 nFoundVersion = nEffectVersion;
  1447.                         }
  1448.                 }
  1449.         }
  1450.  
  1451.         return pFoundEffect;
  1452. }
  1453.  
  1454. //////////////////////////////////////////////////////////////////////////
  1455. bool CParticleEffect::LoadResources(bool bAll, cstr sSource) const
  1456. {
  1457.         // Check file access if sSource specified.
  1458.         if (sSource && !ResourcesLoaded(true) && !CParticleManager::Instance()->CanAccessFiles(GetFullName(), sSource))
  1459.                 return false;
  1460.  
  1461.         int nLoaded = 0;
  1462.  
  1463.         if (IsEnabled() && !m_pParticleParams->ResourcesLoaded())
  1464.         {
  1465.                 CRY_DEFINE_ASSET_SCOPE("Particle Effect", GetFullName());
  1466.  
  1467. #ifdef CRY_PFX1_BAIL_UNSUPPORTED
  1468.                 if (GetParent() == 0)
  1469.                         gEnv->pLog->Log("PFX1 feature set \"%s\"", GetName());
  1470. #endif
  1471.  
  1472.                 nLoaded = m_pParticleParams->LoadResources(GetFullName().c_str());
  1473.         }
  1474.         if (bAll)
  1475.                 for (auto& child : m_children)
  1476.                         nLoaded += (int)child.LoadResources(true);
  1477.         return nLoaded > 0;
  1478. }
  1479.  
  1480. void CParticleEffect::UnloadResources(bool bAll) const
  1481. {
  1482.         if (m_pParticleParams)
  1483.                 m_pParticleParams->UnloadResources();
  1484.         if (bAll)
  1485.                 for (auto& child : m_children)
  1486.                         child.UnloadResources(true);
  1487. }
  1488.  
  1489. bool CParticleEffect::ResourcesLoaded(bool bAll) const
  1490. {
  1491.         // Return whether all resources loaded.
  1492.         if (m_pParticleParams && !m_pParticleParams->ResourcesLoaded())
  1493.                 return false;
  1494.         if (bAll)
  1495.                 for (auto& child : m_children)
  1496.                         if (!child.ResourcesLoaded(true))
  1497.                                 return false;
  1498.         return true;
  1499. }
  1500.  
  1501. const ParticleParams& CParticleEffect::GetDefaultParams() const
  1502. {
  1503.         return GetDefaultParams(m_pParticleParams ? m_pParticleParams->eInheritance : ParticleParams::EInheritance(), 0);
  1504. }
  1505.  
  1506. const ParticleParams& CParticleEffect::GetDefaultParams(ParticleParams::EInheritance eInheritance, int nVersion) const
  1507. {
  1508.         if (eInheritance == eInheritance.Parent)
  1509.         {
  1510.                 if (m_parent)
  1511.                         return m_parent->GetParams();
  1512.                 eInheritance = eInheritance.System;
  1513.         }
  1514.  
  1515.         return CParticleManager::Instance()->GetDefaultParams(eInheritance, nVersion);
  1516. }
  1517.  
  1518. static int UpdateDefaultValues(const CTypeInfo& info, void* data, const void* def_data, const void* new_data)
  1519. {
  1520.         int nUpdated = 0;
  1521.         if (info.HasSubVars())
  1522.         {
  1523.                 for (const CTypeInfo::CVarInfo* pVar = info.NextSubVar(nullptr);
  1524.                      pVar != nullptr;
  1525.                      pVar = info.NextSubVar(pVar))
  1526.                         nUpdated += UpdateDefaultValues(pVar->Type, pVar->GetAddress(data), pVar->GetAddress(def_data), pVar->GetAddress(new_data));
  1527.         }
  1528.         else
  1529.         {
  1530.                 if (info.ValueEqual(data, def_data) && !info.ValueEqual(data, new_data))
  1531.                 {
  1532.                         info.FromValue(data, new_data, info);
  1533.                         return 1;
  1534.                 }
  1535.         }
  1536.         return nUpdated;
  1537. }
  1538.  
  1539. void CParticleEffect::PropagateParticleParams(const ParticleParams& params)
  1540. {
  1541.         for (auto& child : m_children)
  1542.         {
  1543.                 if (child.m_pParticleParams && child.m_pParticleParams->eInheritance == ParticleParams::EInheritance::Parent)
  1544.                 {
  1545.                         // Update all inherited values from new params.
  1546.                         UpdateDefaultValues(TypeInfo(m_pParticleParams), child.m_pParticleParams, m_pParticleParams, &params);
  1547.                         child.PropagateParticleParams(params);
  1548.                 }
  1549.         }
  1550. }
  1551.  
  1552. //////////////////////////////////////////////////////////////////////////
  1553. bool CParticleEffect::IsActive(bool bAll) const
  1554. {
  1555.         // Make sure effect and all indirect parents are active in current render context.
  1556.         for (const CParticleEffect* pEffect = this;; pEffect = pEffect->m_parent)
  1557.         {
  1558.                 if (!(pEffect && pEffect->m_pParticleParams && pEffect->m_pParticleParams->IsActive()))
  1559.                         return false;
  1560.                 if (!pEffect->m_pParticleParams->eSpawnIndirection)
  1561.                         break;
  1562.         }
  1563.  
  1564.         if (m_pParticleParams->nEnvFlags & (REN_ANY | EFF_ANY))
  1565.                 // Has visible or other effects.
  1566.                 return true;
  1567.  
  1568.         for (auto& child : m_children)
  1569.                 if (bAll || child.GetIndirectParent())
  1570.                         if (child.IsActive(true))
  1571.                                 return true;
  1572.         return false;
  1573. }
  1574.  
  1575. uint32 CParticleEffect::GetEnvironFlags(bool bAll) const
  1576. {
  1577.         uint32 nFlags = m_pParticleParams ? m_pParticleParams->nEnvFlags : 0;
  1578.         if (bAll)
  1579.         {
  1580.                 for (auto& child : m_children)
  1581.                         if (child.IsActive())
  1582.                                 nFlags |= child.GetEnvironFlags(true);
  1583.         }
  1584.         return nFlags;
  1585. }
  1586.  
  1587. void CParticleEffect::SetParticleParams(const ParticleParams& params)
  1588. {
  1589.         CRY_DEFINE_ASSET_SCOPE("Particle Effect", GetFullName());
  1590.  
  1591.         InstantiateParams();
  1592.         if (params.sTexture != m_pParticleParams->sTexture || params.sMaterial != m_pParticleParams->sMaterial)
  1593.         {
  1594.                 m_pParticleParams->pMaterial = 0;
  1595.                 m_pParticleParams->nEnvFlags &= ~EFF_LOADED;
  1596.         }
  1597.         if (params.sGeometry != m_pParticleParams->sGeometry)
  1598.         {
  1599.                 m_pParticleParams->pStatObj = 0;
  1600.                 m_pParticleParams->nEnvFlags &= ~EFF_LOADED;
  1601.         }
  1602.         if (params.TextureTiling.nTilesX != m_pParticleParams->TextureTiling.nTilesX || params.TextureTiling.nTilesY != m_pParticleParams->TextureTiling.nTilesY)
  1603.         {
  1604.                 m_pParticleParams->fTexAspect = 0.f;
  1605.                 m_pParticleParams->nEnvFlags &= ~EFF_LOADED;
  1606.         }
  1607.  
  1608.         PropagateParticleParams(params);
  1609.  
  1610.         static_cast<ParticleParams&>(*m_pParticleParams) = params;
  1611.         m_pParticleParams->LoadResources(GetFullName().c_str());
  1612.         CParticleManager::Instance()->UpdateEmitters(this);
  1613. }
  1614.  
  1615. const ParticleParams& CParticleEffect::GetParticleParams() const
  1616. {
  1617.         if (m_pParticleParams)
  1618.                 return *m_pParticleParams;
  1619.         return CParticleManager::Instance()->GetDefaultParams();
  1620. }
  1621.  
  1622. //////////////////////////////////////////////////////////////////////////
  1623. IParticleEmitter* CParticleEffect::Spawn(const ParticleLoc& loc, const SpawnParams* pSpawnParams)
  1624. {
  1625.         return CParticleManager::Instance()->CreateEmitter(loc, this, pSpawnParams);
  1626. }
  1627.  
  1628. //////////////////////////////////////////////////////////////////////////
  1629. void CParticleEffect::Serialize(XmlNodeRef node, bool bLoading, bool bAll)
  1630. {
  1631.         XmlNodeRef root = node;
  1632.         while (root->getParent())
  1633.                 root = root->getParent();
  1634.  
  1635.         if (bLoading)
  1636.         {
  1637.                 if (m_strName.empty())
  1638.                 {
  1639.                         if (m_parent)
  1640.                                 // Set simple name, will be automatically qualified with hierarchy.
  1641.                                 SetName(node->getAttr("Name"));
  1642.                         else
  1643.                         {
  1644.                                 // Qualify with library name.
  1645.                                 stack_string sFullName = root->getAttr("Name");
  1646.                                 if (sFullName.empty())
  1647.                                         sFullName = root->getTag();
  1648.                                 if (!sFullName.empty())
  1649.                                         sFullName += ".";
  1650.                                 sFullName += node->getAttr("Name");
  1651.                                 SetName(sFullName.c_str());
  1652.                         }
  1653.                 }
  1654.  
  1655.                 int nVersion = nSERIALIZE_VERSION;
  1656.                 cstr sSandboxVersion = "3.5";
  1657.                 if (root->getAttr("ParticleVersion", nVersion))
  1658.                 {
  1659.                         if (nVersion < nMIN_SERIALIZE_VERSION || nVersion > nSERIALIZE_VERSION)
  1660.                         {
  1661.                                 Warning("Particle Effect %s version (%d) out of supported range (%d-%d); may change in behavior",
  1662.                                         GetName(), nVersion, nMIN_SERIALIZE_VERSION, nSERIALIZE_VERSION);
  1663.                         }
  1664.                 }
  1665.                 root->getAttr("SandboxVersion", &sSandboxVersion);
  1666.  
  1667.                 bool bEnabled = false;
  1668.                 XmlNodeRef paramsNode = node->findChild("Params");
  1669.                 if (paramsNode && (gEnv->IsEditor() || GetAttrValue(*paramsNode, "Enabled", true)))
  1670.                 {
  1671.                         // Init params, then read from XML.
  1672.                         bEnabled = true;
  1673.  
  1674.                         // Get defaults base, and initialize params
  1675.                         ParticleParams::EInheritance eInheritance = GetAttrValue(*paramsNode, "Inheritance", ParticleParams::EInheritance());
  1676.                         CompatibilityParticleParams params(nVersion, sSandboxVersion, GetDefaultParams(eInheritance, nVersion));
  1677.  
  1678.                         FromXML(*paramsNode, params, FFromString().SkipEmpty(1));
  1679.  
  1680.                         params.Correct(this);
  1681.  
  1682.                         if (!m_pParticleParams)
  1683.                                 m_pParticleParams = new ResourceParticleParams(params);
  1684.                         else
  1685.                         {
  1686.                                 bool bLoadResources = m_pParticleParams->ResourcesLoaded();
  1687.                                 m_pParticleParams->UnloadResources();
  1688.                                 m_pParticleParams->~ResourceParticleParams();
  1689.                                 new(m_pParticleParams) ResourceParticleParams(params);
  1690.                                 if (bLoadResources)
  1691.                                         m_pParticleParams->LoadResources(GetFullName().c_str());
  1692.                         }
  1693.                 }
  1694.  
  1695.                 if (bAll)
  1696.                 {
  1697.                         // Serialize children.
  1698.                         XmlNodeRef childsNode = node->findChild("Childs");
  1699.                         if (childsNode)
  1700.                         {
  1701.                                 for (int i = 0; i < childsNode->getChildCount(); i++)
  1702.                                 {
  1703.                                         XmlNodeRef xchild = childsNode->getChild(i);
  1704.                                         _smart_ptr<IParticleEffect> pChildEffect;
  1705.  
  1706.                                         if (cstr sChildName = xchild->getAttr("Name"))
  1707.                                                 pChildEffect = FindChild(sChildName);
  1708.  
  1709.                                         if (!pChildEffect)
  1710.                                         {
  1711.                                                 pChildEffect = new CParticleEffect();
  1712.                                                 pChildEffect->SetParent(this);
  1713.                                         }
  1714.  
  1715.                                         pChildEffect->Serialize(xchild, bLoading, bAll);
  1716.                                         if (pChildEffect->GetParent() && !pChildEffect->GetParticleParams().eSpawnIndirection)
  1717.                                                 bEnabled = true;
  1718.                                 }
  1719.                         }
  1720.  
  1721.                         if (!gEnv->IsEditor() && !bEnabled && m_parent)
  1722.                         {
  1723.                                 // Remove fully disabled child effects at load-time.
  1724.                                 SetParent(NULL);
  1725.                         }
  1726.                 }
  1727.  
  1728.                 // Convert old internal targeting params
  1729.                 if (nVersion < 23 && IsEnabled())
  1730.                 {
  1731.                         ParticleParams::STargetAttraction::ETargeting& eTarget = m_pParticleParams->TargetAttraction.eTarget;
  1732.                         if (eTarget != eTarget.Ignore)
  1733.                         {
  1734.                                 if (m_parent && m_parent->IsEnabled() && m_parent->GetParams().eForceGeneration == ParticleParams::EForce::_Target)
  1735.                                 {
  1736.                                         // Parent generated target attraction
  1737.                                         eTarget = eTarget.OwnEmitter;
  1738.                                 }
  1739.                                 if (GetParams().eForceGeneration == GetParams().eForceGeneration._Target)
  1740.                                 {
  1741.                                         m_pParticleParams->eForceGeneration = ParticleParams::EForce::None;
  1742.                                         if (m_children.empty())
  1743.                                                 // Target attraction set on childless effect, intention probably to target self
  1744.                                                 eTarget = eTarget.OwnEmitter;
  1745.                                 }
  1746.                         }
  1747.                 }
  1748.         }
  1749.         else
  1750.         {
  1751.                 // Saving.
  1752.                 bool bSerializeNamedFields = !!GetCVars()->e_ParticlesSerializeNamedFields;
  1753.  
  1754.                 node->setAttr("Name", GetBaseName());
  1755.                 root->setAttr("ParticleVersion", bSerializeNamedFields ? nSERIALIZE_VERSION : 23);
  1756.  
  1757.                 if (m_pParticleParams)
  1758.                 {
  1759.                         // Save particle params.
  1760.                         XmlNodeRef paramsNode = node->newChild("Params");
  1761.  
  1762.                         ToXML<ParticleParams>(*paramsNode, *m_pParticleParams, GetDefaultParams(),
  1763.                                               FToString().SkipDefault(1).NamedFields(bSerializeNamedFields));
  1764.                 }
  1765.  
  1766.                 if (bAll && !m_children.empty())
  1767.                 {
  1768.                         // Serialize children.
  1769.                         XmlNodeRef childsNode = node->newChild("Childs");
  1770.                         for (auto& child : m_children)
  1771.                         {
  1772.                                 XmlNodeRef xchild = childsNode->newChild("Particles");
  1773.                                 child.Serialize(xchild, bLoading, bAll);
  1774.                         }
  1775.                 }
  1776.  
  1777.                 if (m_strName == "System.Default")
  1778.                         gEnv->pParticleManager->SetDefaultEffect(this);
  1779.         }
  1780. }
  1781.  
  1782. void CParticleEffect::Serialize(Serialization::IArchive& ar)
  1783. {
  1784. }
  1785.  
  1786. void CParticleEffect::Reload(bool bAll)
  1787. {
  1788.         if (XmlNodeRef node = CParticleManager::Instance()->ReadEffectNode(GetFullName()))
  1789.         {
  1790.                 Serialize(node, true, bAll);
  1791.                 LoadResources(true);
  1792.         }
  1793. }
  1794.  
  1795. IParticleAttributes& CParticleEffect::GetAttributes()
  1796. {
  1797.         static class CNullParticleAttributes : public IParticleAttributes
  1798.         {
  1799.                 virtual void         UpdateScriptTable(const SmartScriptTable& scriptTable)  {}
  1800.                 virtual TAttributeId FindAttributeIdByName(cstr name) const                  { return -1; }
  1801.                 virtual uint         GetNumAttributes() const                                { return 0; }
  1802.                 virtual cstr         GetAttributeName(uint idx) const                        { return nullptr; }
  1803.                 virtual EType        GetAttributeType(uint idx) const                        { return ET_Float; }
  1804.                 virtual bool         GetAsBoolean(TAttributeId id, bool defaultValue) const  { return defaultValue; }
  1805.                 virtual int          GetAsInteger(TAttributeId id, int defaultValue) const   { return defaultValue; }
  1806.                 virtual float        GetAsFloat(TAttributeId id, float defaultValue) const   { return defaultValue; }
  1807.                 virtual ColorB       GetAsColorB(TAttributeId id, ColorB defaultValue) const { return defaultValue; }
  1808.                 virtual ColorF       GetAsColorF(TAttributeId id, ColorF defaultValue) const { return defaultValue; }
  1809.                 virtual void         SetAsBoolean(TAttributeId id, bool value)               {}
  1810.                 virtual int          SetAsInteger(TAttributeId id, int value)                { return value; }
  1811.                 virtual float        SetAsFloat(TAttributeId id, float value)                { return value; }
  1812.                 virtual void         SetAsColor(TAttributeId id, ColorB value)               {}
  1813.                 virtual void         SetAsColor(TAttributeId id, ColorF value)               {}
  1814.         } nullAttributes;
  1815.         return nullAttributes;
  1816. }
  1817.  
  1818. void CParticleEffect::GetMemoryUsage(ICrySizer* pSizer) const
  1819. {
  1820.         if (!pSizer->AddObjectSize(this))
  1821.                 return;
  1822.  
  1823.         pSizer->AddObject(m_strName);
  1824.         pSizer->AddObject(m_children);
  1825.         pSizer->AddObject(m_pParticleParams);
  1826. }
  1827.  
  1828. void CParticleEffect::GetEffectCounts(SEffectCounts& counts) const
  1829. {
  1830.         counts.nLoaded++;
  1831.         if (IsEnabled())
  1832.         {
  1833.                 counts.nEnabled++;
  1834.                 if (GetParams().ResourcesLoaded())
  1835.                         counts.nUsed++;
  1836.                 if (IsActive())
  1837.                         counts.nActive++;
  1838.         }
  1839.  
  1840.         for (auto& child : m_children)
  1841.                 child.GetEffectCounts(counts);
  1842. }
  1843.  
  1844. CParticleEffect* CParticleEffect::GetIndirectParent() const
  1845. {
  1846.         for (CParticleEffect const* pEffect = this; pEffect; pEffect = pEffect->m_parent)
  1847.                 if (pEffect->IsEnabled() && pEffect->GetParams().eSpawnIndirection)
  1848.                         return pEffect->m_parent;
  1849.         return 0;
  1850. }
  1851.  
  1852. float CParticleEffect::Get(FMaxEffectLife const& opts) const
  1853. {
  1854.         float fLife = 0.f;
  1855.         if (IsActive())
  1856.         {
  1857.                 const ResourceParticleParams& params = GetParams();
  1858.                 if (opts.fEmitterMaxLife() > 0.f)
  1859.                 {
  1860.                         fLife = params.fPulsePeriod ? fHUGE : params.GetMaxEmitterLife();
  1861.                         if (const CParticleEffect* pParent = GetIndirectParent())
  1862.                         {
  1863.                                 float fParentLife = pParent->GetParams().GetMaxParticleLife();
  1864.                                 fLife = min(fLife, fParentLife);
  1865.                         }
  1866.                         fLife = min(fLife, opts.fEmitterMaxLife());
  1867.  
  1868.                         if (opts.bParticleLife())
  1869.                                 fLife += params.fParticleLifeTime.GetMaxValue();
  1870.                 }
  1871.                 else if (opts.bParticleLife())
  1872.                         fLife += params.GetMaxParticleLife();
  1873.         }
  1874.  
  1875.         if (opts.bAllChildren() || opts.bIndirectChildren())
  1876.         {
  1877.                 for (auto& child : m_children)
  1878.                 {
  1879.                         if (child.GetIndirectParent())
  1880.                                 fLife = max(fLife, child.Get(opts().fEmitterMaxLife(fLife).bAllChildren(1)));
  1881.                         else if (opts.bAllChildren())
  1882.                                 fLife = max(fLife, child.Get(opts));
  1883.                 }
  1884.         }
  1885.         return fLife;
  1886. }
  1887.  
  1888. float CParticleEffect::GetEquilibriumAge(bool bAll) const
  1889. {
  1890.         float fEquilibriumAge = 0.f;
  1891.         bool bHasEquilibrium = IsActive() && GetParams().HasEquilibrium();
  1892.         if (bHasEquilibrium)
  1893.         {
  1894.                 fEquilibriumAge = GetParams().fSpawnDelay.GetMaxValue() + GetMaxParticleFullLife();
  1895.                 if (const CParticleEffect* pParent = GetIndirectParent())
  1896.                         fEquilibriumAge += pParent->GetEquilibriumAge(false);
  1897.         }
  1898.  
  1899.         if (bAll)
  1900.         {
  1901.                 for (auto& child : m_children)
  1902.                         if (child.IsEnabled() && !child.GetParams().eSpawnIndirection)
  1903.                                 fEquilibriumAge = max(fEquilibriumAge, child.GetEquilibriumAge(true));
  1904.         }
  1905.  
  1906.         return fEquilibriumAge;
  1907. }
  1908.  
  1909. float CParticleEffect::GetMaxParticleSize(bool bParent) const
  1910. {
  1911.         if (!m_pParticleParams)
  1912.                 return 0.f;
  1913.  
  1914.         float fMaxSize = m_pParticleParams->fSize.GetMaxValue();
  1915.         if (!bParent && m_pParticleParams->pStatObj)
  1916.                 fMaxSize *= m_pParticleParams->pStatObj->GetRadius();
  1917.  
  1918.         if (m_parent && m_pParticleParams->eSpawnIndirection && m_pParticleParams->bMoveRelativeEmitter.ScaleWithSize())
  1919.                 fMaxSize *= m_parent->GetMaxParticleSize(true);
  1920.         return fMaxSize;
  1921. }
  1922.  
  1923. IStatObj::SSubObject* GetSubGeometry(IStatObj* pParent, int i)
  1924. {
  1925.         assert(pParent);
  1926.         if (IStatObj::SSubObject* pSub = pParent->GetSubObject(i))
  1927.                 if (pSub->nType == STATIC_SUB_OBJECT_MESH && pSub->pStatObj && pSub->pStatObj->GetRenderMesh())
  1928.                         return pSub;
  1929.         return NULL;
  1930. }
  1931.  
  1932. int GetSubGeometryCount(IStatObj* pParent)
  1933. {
  1934.         int nPieces = 0;
  1935.         for (int i = pParent->GetSubObjectCount() - 1; i >= 0; i--)
  1936.                 if (GetSubGeometry(pParent, i))
  1937.                         nPieces++;
  1938.         return nPieces;
  1939. }
  1940.  
downloadParticleEffect.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