BVB Source Codes

CRYENGINE Show ParticleRender.cpp Source code

Return Download CRYENGINE: download ParticleRender.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:   ParticleRender.cpp
  5. //  Version:     v1.00
  6. //  Created:     28/5/2001 by Vladimir Kajalin
  7. //  Evolved:     2005 by J Scott Peter
  8. //  Description: Rendering functions for CParticle and CParticleContainer
  9. // -------------------------------------------------------------------------
  10. //
  11. ////////////////////////////////////////////////////////////////////////////
  12.  
  13. #include "StdAfx.h"
  14.  
  15. #include "Particle.h"
  16. #include "ParticleManager.h"
  17. #include "ParticleEmitter.h"
  18. #include "ObjMan.h"
  19. #include "3dEngine.h"
  20. #include "ClipVolumeManager.h"
  21.  
  22. #include <CryThreading/IJobManager_JobDelegator.h>
  23.  
  24. template<class T>
  25. class SaveRestore
  26. {
  27. public:
  28.         SaveRestore(T& var)
  29.                 : m_pVar(&var), m_Val(var)
  30.         {}
  31.         SaveRestore(T& var, T const& newval)
  32.                 : m_pVar(&var), m_Val(var)
  33.         {
  34.                 *m_pVar = newval;
  35.         }
  36.         ~SaveRestore()
  37.         {
  38.                 *m_pVar = m_Val;
  39.         }
  40. protected:
  41.         T* m_pVar;
  42.         T  m_Val;
  43. };
  44.  
  45. struct SParticleRenderData
  46. {
  47.         // Computed params needed for rendering.
  48.         float fSize;              // Scale for sprite or geometry.
  49.         float fDistCamSq;         // Distance^2 from camera.
  50.         Vec3  vCamDir;            // camera direction
  51.         Vec3  vOffsetCam;         // Offset from camera.
  52.  
  53.         Vec3  GetDirCam(float fBlendDist) const
  54.         {
  55.                 if (fBlendDist <= 0.0f)
  56.                 {
  57.                         return vOffsetCam * isqrt_tpl(fDistCamSq);
  58.                 }
  59.                 else
  60.                 {
  61.                         const float camDist = sqrt_tpl(fDistCamSq);
  62.                         const float fOrientLerpFactor = min(camDist, fBlendDist) / fBlendDist;
  63.  
  64.                         const Vec3 vToPlane = vCamDir;
  65.                         const Vec3 vToCam = vOffsetCam * isqrt_tpl(fDistCamSq);
  66.                         const Vec3 vOrient = Lerp(vToPlane, vToCam, fOrientLerpFactor);
  67.  
  68.                         return vOrient.GetNormalizedFast();
  69.                 }
  70.         }
  71. };
  72.  
  73. struct SParticleVertexContext
  74. {
  75.         // Constant data.
  76.         const ResourceParticleParams&
  77.                     m_Params;
  78.         SCameraInfo m_CamInfo;
  79.         Vec3        m_vCamPos;
  80.         Vec3        m_vRenderOffset;
  81.  
  82.         bool        m_bFrustumCull,                 // Culling data.
  83.                      m_bOcclusionCull,
  84.                      m_bComputeBoundingSphere;
  85.         IVisArea* m_pClipVisArea;                   // Vis env to clip against, if needed.
  86.         float     m_fClipWaterSense;                // Direction of water to clip against, else 0.
  87.         Matrix34  m_matInvCamera;
  88.         float     m_fProjectH, m_fProjectV;
  89.  
  90.         uint64    m_uVertexFlags;
  91.         int       m_nMaxParticleVertices,
  92.                   m_nMaxParticleIndices;
  93.         UCol      m_TexInfo;                        // Default tex coords for vertex.
  94.  
  95.         float     m_fInvMinPix;
  96.         float     m_fMaxPixFactor;
  97.         float     m_fMinDrawAngle;
  98.         float     m_fFillFactor;
  99.         float     m_fFillMax;
  100.         float     m_fFillFade;
  101.         float     m_fDistFuncCoefs[3];              // Coefficients for alpha(dist^2) function.
  102.         float     m_fMinAlphaMod;                   // Minimum alpha modulation (relative to effect max alpha) to render.
  103.         Vec2      m_vTexAspect;                     // Multipliers for non-square textures (max is always 1).
  104.         float     m_fPivotYScale;
  105.         float     m_fAnimPosScale;                  // Multiplier for texture animation pos.
  106.  
  107.         // Modified data.
  108.         float m_fPixelsProcessed;
  109.         float m_fPixelsRendered;
  110.         int   m_nParticlesCulled;
  111.         int   m_nParticlesClipped;
  112.  
  113.         // Connected-particle info.
  114.         int          m_nPrevSequence;
  115.         int          m_nSequenceParticles;
  116.         Vec3         m_vPrevPos;
  117.         SVF_Particle m_PrevVert;
  118.  
  119.         SParticleVertexContext(CParticleContainer* pContainer, const SCameraInfo& camInfo, uint64 uVertexFlags = 0, float fMaxContainerPixels = fHUGE)
  120.                 : m_Params(pContainer->GetParams()), m_CamInfo(camInfo), m_vCamPos(camInfo.pCamera->GetPosition()), m_uVertexFlags(uVertexFlags)
  121.         {
  122.                 Init(fMaxContainerPixels, pContainer);
  123.         }
  124.  
  125.         void         Init(float fMaxContainerPixels, CParticleContainer* pContainer);
  126.  
  127.         inline float DistFunc(float fDist) const
  128.         {
  129.                 return clamp_tpl(m_fDistFuncCoefs[0] + m_fDistFuncCoefs[1] * fDist + m_fDistFuncCoefs[2] * fDist * fDist, 0.f, 1.f);
  130.         }
  131. };
  132.  
  133. ///////////////////////////////////////////////////////////////////////////////
  134. // adapter class to vertices to VMEM code path, since regular writes to VMEM
  135. // are slow, we collect them in a stack memory space and write chunks to VMEM
  136.  
  137. // Dependent types and functions.
  138. static void CopyToVideoMemory(void* pDst, void* pSrc, uint32 nSize, uint32 nTransferID)
  139. {
  140.         assert(((uintptr_t)pDst & 127) == ((uintptr_t)pSrc & 127));
  141.         memcpy(pDst, pSrc, nSize);
  142. }
  143.  
  144. ///////////////////////////////////////////////////////////////////////////////
  145. template<class T, uint nBUFFER_SIZE, uint nCHUNK_SIZE, int nMEM_TRANSFER_ID>
  146. struct SBufferedWriter
  147. {
  148.         SBufferedWriter(FixedDynArray<T>& aDestMem)
  149.                 : m_pDestMem(&aDestMem)
  150.                 , m_aDestMemBegin((char*)aDestMem.begin())
  151.                 , m_nFlushedBytes(0)
  152.                 , m_nWrittenDestBytes(0)
  153.         {
  154.                 uint nAlignOffset = alias_cast<uint>(m_aDestMemBegin - (char*)m_aSrcBuffer) & 127;
  155.                 m_aSrcMemBegin = m_aSrcBuffer + nAlignOffset;
  156.                 int nCapacity = (sizeof(m_aSrcBuffer) - nAlignOffset) / sizeof(T);
  157.                 nCapacity = min(nCapacity, aDestMem.capacity());
  158.                 m_aSrcArray.set(ArrayT((T*)m_aSrcMemBegin, nCapacity));
  159.         }
  160.  
  161.         ~SBufferedWriter()
  162.         {
  163.                 // Write remaining data.
  164.                 FlushData(UnflushedBytes());
  165.  
  166.                 // Set final count of elems written to dest buffer.
  167.                 assert(m_nWrittenDestBytes % sizeof(T) == 0);
  168.                 m_pDestMem->resize(m_nWrittenDestBytes / sizeof(T), eNoInit);
  169.         }
  170.  
  171.         ILINE FixedDynArray<T>& Array()
  172.         {
  173.                 return m_aSrcArray;
  174.         }
  175.  
  176.         ILINE T* CheckAvailable(int nElems)
  177.         {
  178.                 // Transfer data chunks no longer needed
  179.                 uint nUnFlushedBytes = UnflushedBytes();
  180.                 if (nUnFlushedBytes >= nCHUNK_SIZE)
  181.                 {
  182.                         FlushData(nUnFlushedBytes & ~(nCHUNK_SIZE - 1));
  183.                 }
  184.  
  185.                 // If buffer is too full for new elements, clear out flushed data.
  186.                 if (m_aSrcArray.available() < nElems)
  187.                 {
  188.                         WrapBuffer();
  189.                         if (m_aSrcArray.available() < nElems)
  190.                                 return 0;
  191.                 }
  192.                 return m_aSrcArray.end();
  193.         }
  194.  
  195. private:
  196.  
  197.         FixedDynArray<T>* m_pDestMem;                       // Destination array in VMEM.
  198.         char*             m_aDestMemBegin;                  // Cached pointer to start of VMEM.
  199.         uint              m_nFlushedBytes;                  // How much written from src buffer.
  200.         uint              m_nWrittenDestBytes;              // Total written to dest buffer.
  201.         FixedDynArray<T>  m_aSrcArray;                      // Array for writing, references following buffer:
  202.         char*             m_aSrcMemBegin;                   // Aligned pointer into SrcBuffer;
  203.         char              m_aSrcBuffer[nBUFFER_SIZE + 128]; // Raw buffer for writing.
  204.  
  205.         uint              UnflushedBytes() const
  206.         {
  207.                 return check_cast<uint>((char*)m_aSrcArray.end() - m_aSrcMemBegin - m_nFlushedBytes);
  208.         }
  209.  
  210.         void FlushData(uint nFlushBytes)
  211.         {
  212.                 if (nFlushBytes)
  213.                 {
  214.                         assert(m_nWrittenDestBytes + nFlushBytes <= m_pDestMem->capacity() * sizeof(T));
  215.                         CopyToVideoMemory(m_aDestMemBegin + m_nWrittenDestBytes, m_aSrcMemBegin + m_nFlushedBytes, nFlushBytes, nMEM_TRANSFER_ID);
  216.                         m_nWrittenDestBytes += nFlushBytes;
  217.                         m_nFlushedBytes += nFlushBytes;
  218.                 }
  219.         }
  220.  
  221.         void WrapBuffer()
  222.         {
  223.                 // Copy unflushed bytes to the buffer beginning
  224.                 uint nUnFlushedBytes = UnflushedBytes();
  225.                 memcpy(m_aSrcMemBegin, m_aSrcMemBegin + m_nFlushedBytes, nUnFlushedBytes);
  226.                 uint nAvailableBytes = check_cast<uint>(m_aSrcBuffer + sizeof(m_aSrcBuffer) - m_aSrcMemBegin - nUnFlushedBytes);
  227.                 nAvailableBytes = min(nAvailableBytes, m_pDestMem->capacity() * (uint)sizeof(T) - m_nWrittenDestBytes - nUnFlushedBytes);
  228.                 m_aSrcArray.set(ArrayT((T*)(m_aSrcMemBegin + nUnFlushedBytes), int(nAvailableBytes / sizeof(T))));
  229.                 m_nFlushedBytes = 0;
  230.         }
  231.  
  232. };
  233.  
  234. ///////////////////////////////////////////////////////////////////////////////
  235. struct CRY_ALIGN(128) SLocalRenderVertices
  236. {
  237.         SLocalRenderVertices(SRenderVertices & DestVerts)
  238.                 : aVertices(DestVerts.aVertices)
  239.                   , aIndices(DestVerts.aIndices)
  240.                   , nBaseVertexIndex(0)
  241.         {
  242.         }
  243.  
  244.         // Reserve space in SRenderVertices, flushing data if needed.
  245.         bool CheckAvailable(int nVertices, int nIndices)
  246.         {
  247.                 return aVertices.CheckAvailable(nVertices) && aIndices.CheckAvailable(nIndices);
  248.         }
  249.  
  250.         ILINE static void DuplicateVertices(SVF_Particle* pDest, int nCount, const SVF_Particle& Src)
  251.         {
  252.                 static const uint nStride = sizeof(SVF_Particle) / sizeof(uint);
  253.                 CryPrefetch(pDest + nCount);
  254.                 const uint* ps = reinterpret_cast<const uint*>(&Src);
  255.                 uint* pd = reinterpret_cast<uint*>(pDest);
  256.                 while (nCount-- > 0)
  257.                 {
  258.                         for (uint i = 0; i < nStride; i++)
  259.                                 *pd++ = ps[i];
  260.                 }
  261.         }
  262.  
  263.         ILINE void AddVertex(const SVF_Particle& vert)
  264.         {
  265.                 aVertices.Array().push_back(vert);
  266.         }
  267.         ILINE void AddDualVertices(const SVF_Particle& vert)
  268.         {
  269.                 // Expand in transverse axis.
  270.                 aVertices.Array().push_back(vert);
  271.                 aVertices.Array().push_back(vert)->st.x = 255;
  272.         }
  273.  
  274.         ILINE static void SetQuadVertices(SVF_Particle aV[4])
  275.         {
  276.                 aV[1].st.x = 255;
  277.                 aV[2].st.y = 255;
  278.                 aV[3].st.x = 255;
  279.                 aV[3].st.y = 255;
  280.         }
  281.  
  282.         ILINE void ExpandQuadVertices()
  283.         {
  284.                 SVF_Particle& vert = aVertices.Array().back();
  285.                 DuplicateVertices(aVertices.Array().append(3, eNoInit), 3, vert);
  286.                 SetQuadVertices(&vert);
  287.         }
  288.  
  289.         ILINE static void SetOctVertices(SVF_Particle aV[8])
  290.         {
  291.                 aV[0].st.x = 75;
  292.                 aV[1].st.x = 180;
  293.                 aV[2].st.x = 255;
  294.                 aV[2].st.y = 75;
  295.                 aV[3].st.x = 255;
  296.                 aV[3].st.y = 180;
  297.                 aV[4].st.x = 180;
  298.                 aV[4].st.y = 255;
  299.                 aV[5].st.x = 75;
  300.                 aV[5].st.y = 255;
  301.                 aV[6].st.y = 180;
  302.                 aV[7].st.y = 75;
  303.         }
  304.  
  305.         ILINE void ExpandOctVertices()
  306.         {
  307.                 SVF_Particle& vert = aVertices.Array().back();
  308.                 DuplicateVertices(aVertices.Array().append(7, eNoInit), 7, vert);
  309.                 SetOctVertices(&vert);
  310.         }
  311.  
  312.         ILINE void AddQuadIndices(int nVertAdvance = 4, int bStrip = 0)
  313.         {
  314.                 uint16* pIndices = aIndices.Array().append(bStrip ? 4 : 6, eNoInit);
  315.  
  316.                 pIndices[0] = 0 + nBaseVertexIndex;
  317.                 pIndices[1] = 1 + nBaseVertexIndex;
  318.                 pIndices[2] = 2 + nBaseVertexIndex;
  319.                 pIndices[3] = 3 + nBaseVertexIndex;
  320.  
  321.                 if (!bStrip)
  322.                 {
  323.                         pIndices[4] = 2 + nBaseVertexIndex;
  324.                         pIndices[5] = 1 + nBaseVertexIndex;
  325.                 }
  326.  
  327.                 nBaseVertexIndex += nVertAdvance;
  328.         }
  329.  
  330.         ILINE void AddOctIndices()
  331.         {
  332.                 uint16* pIndices = aIndices.Array().append(18, eNoInit);
  333.  
  334.                 pIndices[0] = 0 + nBaseVertexIndex;
  335.                 pIndices[1] = 1 + nBaseVertexIndex;
  336.                 pIndices[2] = 2 + nBaseVertexIndex;
  337.                 pIndices[3] = 0 + nBaseVertexIndex;
  338.                 pIndices[4] = 2 + nBaseVertexIndex;
  339.                 pIndices[5] = 4 + nBaseVertexIndex;
  340.                 pIndices[6] = 2 + nBaseVertexIndex;
  341.                 pIndices[7] = 3 + nBaseVertexIndex;
  342.                 pIndices[8] = 4 + nBaseVertexIndex;
  343.                 pIndices[9] = 0 + nBaseVertexIndex;
  344.                 pIndices[10] = 4 + nBaseVertexIndex;
  345.                 pIndices[11] = 6 + nBaseVertexIndex;
  346.                 pIndices[12] = 4 + nBaseVertexIndex;
  347.                 pIndices[13] = 5 + nBaseVertexIndex;
  348.                 pIndices[14] = 6 + nBaseVertexIndex;
  349.                 pIndices[15] = 6 + nBaseVertexIndex;
  350.                 pIndices[16] = 7 + nBaseVertexIndex;
  351.                 pIndices[17] = 0 + nBaseVertexIndex;
  352.  
  353.                 nBaseVertexIndex += 8;
  354.         }
  355.  
  356.         void AddPolyIndices(int nVerts, int bStrip = 0)
  357.         {
  358.                 nVerts >>= 1;
  359.                 while (--nVerts > 0)
  360.                         AddQuadIndices(2, bStrip);
  361.  
  362.                 // Final quad.
  363.                 nBaseVertexIndex += 2;
  364.         }
  365.  
  366. private:
  367.  
  368.         // Individual buffers for vertices and indices
  369.         SBufferedWriter<SVF_Particle, 16* 1024, 4* 1024, 5> aVertices;
  370.         SBufferedWriter<uint16, 4* 1024, 1* 1024, 6> aIndices;
  371.         int nBaseVertexIndex;
  372.  
  373. };
  374.  
  375. ///////////////////////////////////////////////////////////////////////////////
  376. //
  377. // Vertex manipulation functions.
  378. //
  379.  
  380. static ILINE void RotateAxes(Vec3& v0, Vec3& v1, const Vec2& vRot)
  381. {
  382.         Vec3 vXAxis = v0 * vRot.y - v1 * vRot.x;
  383.         Vec3 vYAxis = v0 * vRot.x + v1 * vRot.y;
  384.         v0 = vXAxis;
  385.         v1 = vYAxis;
  386. }
  387.  
  388. static ILINE void RotateAxes(Vec3& v0, Vec3& v1, const Vec3& v1Align)
  389. {
  390.         // Rotate within axis plane to align v1 to v1Align.
  391.         RotateAxes(v0, v1, Vec2(v0 * v1Align, v1 * v1Align).GetNormalized());
  392. }
  393.  
  394. static ILINE void ExpandAxis(Vec3& vAxis, const Vec3& vExpand, float fLengths)
  395. {
  396.         if (fLengths > FLT_MIN)
  397.                 vAxis += vExpand * ((vExpand | vAxis) / fLengths);
  398. }
  399.  
  400. ///////////////////////////////////////////////////////////////////////////////
  401. bool CParticle::RenderGeometry(SRendParams& RenParamsShared, SParticleVertexContext& Context, const SRenderingPassInfo& passInfo) const
  402. {
  403.         // Render 3d object
  404.         if (!m_pMeshObj)
  405.                 return false;
  406.  
  407.         ResourceParticleParams const& params = Context.m_Params;
  408.         float fRelativeAge = GetRelativeAge();
  409.         float fRadius = m_pMeshObj->GetRadius();
  410.  
  411.         SParticleRenderData RenderData;
  412.         ComputeRenderData(RenderData, Context, fRadius);
  413.         if (RenderData.fSize == 0.f)
  414.                 return false;
  415.  
  416.         // Color and alpha.
  417.         ColorF cColor = params.cColor.GetValueFromMod(m_BaseMods.Color, fRelativeAge);
  418.         cColor.a = ComputeRenderAlpha(RenderData, fRelativeAge, Context);
  419.         if (cColor.a < Context.m_fMinAlphaMod)
  420.                 return false;
  421.  
  422.         cColor.a *= RenParamsShared.fAlpha * params.GetAlphaFromMod(cColor.a);
  423.  
  424.         if (passInfo.IsShadowPass())
  425.         {
  426.                 // Shadow alpha (and color) not supported, scale size instead.
  427.                 RenderData.fSize *= cColor.a;
  428.                 cColor = Col_White;
  429.         }
  430.  
  431.         // Get matrix.
  432.         Matrix34 matPart;
  433.         GetRenderMatrix(matPart, m_Loc, RenderData, Context);
  434.  
  435.         if ((params.eFacing == params.eFacing.Camera || params.eFacing == params.eFacing.CameraX) && GetEmitter())
  436.         {
  437.                 // Re-apply parameter-derived rotation, using emitter orientation as the base
  438.                 matPart = matPart * Matrix33(GetSource().GetLocation().q.GetInverted() * m_Loc.q);
  439.         }
  440.  
  441.         if (m_pMeshObj && params.IsGeometryCentered())
  442.         {
  443.                 // Recenter object pre-rotation.
  444.                 Vec3 vCenter = m_pMeshObj->GetAABB().GetCenter();
  445.                 matPart.SetTranslation(matPart * -vCenter);
  446.         }
  447.  
  448.         if (params.bDrawNear)
  449.         {
  450.                 // DrawNear is now rendered in "Camera Space", particles aren't in "camera space" because there's no
  451.                 // clear relationship between them and the camera. They need to be rendered in the nearest pass to
  452.                 // avoid clipping, so remove camera pos here to put them into "camera space". Not ideal, but we merged
  453.                 // "camera space" and "Nearest" so don't have another option.
  454.                 matPart.AddTranslation(-Context.m_vCamPos);
  455.         }
  456.  
  457.         // Render separate draw call.
  458.         SaveRestore<ColorF> SaveColor(RenParamsShared.AmbientColor);
  459.         SaveRestore<float> SaveAlpha(RenParamsShared.fAlpha);
  460.  
  461.         // Apply particle color to RenParams.Ambient, tho it's not quite the same thing.
  462.         RenParamsShared.AmbientColor.r *= cColor.r;
  463.         RenParamsShared.AmbientColor.g *= cColor.g;
  464.         RenParamsShared.AmbientColor.b *= cColor.b;
  465.         RenParamsShared.fAlpha = cColor.a;
  466.  
  467.         RenParamsShared.fDistance = max(sqrt_fast_tpl(RenderData.fDistCamSq) - RenderData.fSize * fRadius, 0.f);
  468.  
  469.         RenParamsShared.pMatrix = &matPart;
  470.  
  471.         RenParamsShared.pInstance = &non_const(*this);
  472.         m_pMeshObj->Render(RenParamsShared, passInfo);
  473.  
  474.         return true;
  475. }
  476.  
  477. void CParticleContainer::RenderGeometry(const SRendParams& RenParams, const SRenderingPassInfo& passInfo)
  478. {
  479.         FUNCTION_PROFILER_CONTAINER(this);
  480.  
  481.         assert(!NeedsUpdate());
  482.  
  483.         if (m_Particles.empty())
  484.                 return;
  485.  
  486.         const ResourceParticleParams& params = GetParams();
  487.         SRendParams RenParamsGeom = RenParams;
  488.         RenParamsGeom.dwFObjFlags |= FOB_TRANS_MASK;
  489.         RenParamsGeom.pMaterial = params.pMaterial;
  490.         if (params.bDrawNear)
  491.         {
  492.                 RenParamsGeom.dwFObjFlags |= FOB_NEAREST;
  493.                 //RenParamsGeom.nRenderList = EFSLIST_POSTPROCESS;
  494.         }
  495.         RenParamsGeom.fDistance += params.fSortOffset;
  496.  
  497.         // Trigger transparency rendering if any particles can be transparent.
  498.         if (!passInfo.IsShadowPass() && params.fAlpha(VMIN) < 1.f)
  499.                 RenParamsGeom.fAlpha *= 0.999f;
  500.  
  501.         float fEmissive = params.fEmissiveLighting;
  502.         bool bHDRModeEnabled = false;
  503.         GetRenderer()->EF_Query(EFQ_HDRModeEnabled, bHDRModeEnabled);
  504.         if (bHDRModeEnabled)
  505.                 fEmissive *= HDRDynamicMultiplier; // Assuming HDRDynamic 1.0
  506.         RenParamsGeom.AmbientColor *= params.fDiffuseLighting + fEmissive;
  507.  
  508.         // Set up shared and unique geom rendering.
  509.         SParticleVertexContext Context(this, passInfo);
  510.  
  511.         m_Counts.EmittersRendered += 1.f;
  512.  
  513.         for (const auto& part : m_Particles)
  514.         {
  515.                 m_Counts.ParticlesRendered += part.RenderGeometry(RenParamsGeom, Context, passInfo);
  516.         }
  517. }
  518.  
  519. void CParticle::GetTextureRect(RectF& rectTex, Vec3& vTexBlend) const
  520. {
  521.         ResourceParticleParams const& params = GetParams();
  522.  
  523.         rectTex.w = 1.f / params.TextureTiling.nTilesX;
  524.         rectTex.h = 1.f / params.TextureTiling.nTilesY;
  525.  
  526.         vTexBlend.z = 0.f;
  527.         float fFrame = m_nTileVariant;
  528.  
  529.         if (params.TextureTiling.nAnimFramesCount > 1)
  530.         {
  531.                 float fAnimPos = params.TextureTiling.GetAnimPos(m_fAge, GetRelativeAge());
  532.                 fAnimPos *= params.TextureTiling.GetAnimPosScale();
  533.                 fFrame += fAnimPos;
  534.  
  535.                 if (params.TextureTiling.bAnimBlend)
  536.                 {
  537.                         vTexBlend.z = fFrame - floor(fFrame);
  538.                         float fFrameBlend = floor(fFrame) + 1.f;
  539.                         if (fFrameBlend >= params.TextureTiling.nFirstTile + params.TextureTiling.nAnimFramesCount)
  540.                                 fFrameBlend = params.TextureTiling.nFirstTile;
  541.  
  542.                         vTexBlend.x = fFrameBlend * rectTex.w;
  543.                         vTexBlend.y = floor(vTexBlend.x) * rectTex.h;
  544.                         vTexBlend.x -= floor(vTexBlend.x);
  545.                 }
  546.  
  547.                 fFrame = floor(fFrame);
  548.         }
  549.  
  550.         rectTex.x = fFrame * rectTex.w;
  551.         rectTex.y = floor(rectTex.x) * rectTex.h;
  552.         rectTex.x -= floor(rectTex.x);
  553. }
  554.  
  555. void CParticleContainer::RenderDecals(const SRenderingPassInfo& passInfo)
  556. {
  557.         FUNCTION_PROFILER_CONTAINER(this);
  558.  
  559.         assert(GetEnvironmentFlags() & REN_DECAL);
  560.  
  561.         ResourceParticleParams const& params = GetParams();
  562.  
  563.         SParticleVertexContext Context(this, passInfo);
  564.  
  565.         SDeferredDecal decal;
  566.         decal.pMaterial = params.pMaterial;
  567.         decal.nSortOrder = clamp_tpl(int(params.fSortOffset * 100.f), 0, 255);
  568.         decal.nFlags = 0;
  569.  
  570.         for (const auto& part : m_Particles)
  571.         {
  572.                 SParticleRenderData RenderData;
  573.  
  574.                 part.ComputeRenderData(RenderData, Context);
  575.  
  576.                 if (RenderData.fSize < FLT_EPSILON)
  577.                         continue;
  578.  
  579.                 Vec3 av[4];
  580.                 part.GetRenderMatrix(av[0], av[1], av[2], av[3], part.GetLocation(), RenderData, Context);
  581.                 decal.projMatrix.SetFromVectors(av[0], av[2], av[1], av[3]);
  582.  
  583.                 Vec3 vTexBlend;
  584.                 part.GetTextureRect(decal.rectTexture, vTexBlend);
  585.                 float fAlphaMod = part.GetAlphaMod();
  586.                 float fAlphaScale = params.GetAlphaFromMod(fAlphaMod);
  587.                 decal.fAlpha = fAlphaScale * (1.f - vTexBlend.z);
  588.                 decal.fGrowAlphaRef = div_min(params.AlphaClip.fSourceMin.Interp(fAlphaMod), fAlphaScale, 1.f);
  589.  
  590.                 GetRenderer()->EF_AddDeferredDecal(decal, passInfo);
  591.  
  592.                 if (vTexBlend.z > 0.f)
  593.                 {
  594.                         // Render decal for blended anim frame
  595.                         decal.rectTexture.x = vTexBlend.x;
  596.                         decal.rectTexture.y = vTexBlend.y;
  597.                         decal.fAlpha = fAlphaScale * vTexBlend.z;
  598.                         GetRenderer()->EF_AddDeferredDecal(decal, passInfo);
  599.                 }
  600.         }
  601. }
  602.  
  603. void CParticle::AddLight(const SRendParams& RenParams, const SRenderingPassInfo& passInfo) const
  604. {
  605.         ParticleParams const& params = GetParams();
  606.         CCamera const& cam = passInfo.GetCamera();
  607.  
  608.         float fRelativeAge = GetRelativeAge();
  609.         const float fFillLightIntensity = params.LightSource.fIntensity.GetValueFromMod(m_BaseMods.LightSourceIntensity, fRelativeAge);
  610.         const float fFillLightRadius = params.LightSource.fRadius.GetValueFromMod(m_BaseMods.LightSourceRadius, fRelativeAge);
  611.         if (GetCVars()->e_DynamicLights && !passInfo.IsRecursivePass()
  612.             && fFillLightIntensity * fFillLightRadius > 0.001f
  613.             && m_Loc.t.GetSquaredDistance(cam.GetPosition()) < sqr(fFillLightRadius * GetFloatCVar(e_ParticlesLightsViewDistRatio))
  614.             && cam.IsSphereVisible_F(Sphere(m_Loc.t, fFillLightRadius)))
  615.         {
  616.                 // Deferred light.
  617.                 CDLight dl;
  618.                 dl.SetPosition(m_Loc.t);
  619.                 dl.m_fRadius = fFillLightRadius;
  620.                 dl.m_Color = params.cColor.GetValueFromMod(m_BaseMods.Color, fRelativeAge) * Color3F(fFillLightIntensity);
  621.                 dl.m_nStencilRef[0] = params.LightSource.bAffectsThisAreaOnly ? RenParams.nClipVolumeStencilRef : CClipVolumeManager::AffectsEverythingStencilRef;
  622.                 dl.m_nStencilRef[1] = CClipVolumeManager::InactiveVolumeStencilRef;
  623.                 dl.m_Flags |= DLF_DEFERRED_LIGHT;
  624.                 if (params.LightSource.eLightAffectsFog == params.LightSource.eLightAffectsFog.FogOnly)
  625.                         dl.m_Flags |= DLF_VOLUMETRIC_FOG_ONLY | DLF_VOLUMETRIC_FOG;
  626.                 else if (params.LightSource.eLightAffectsFog == params.LightSource.eLightAffectsFog.Both)
  627.                         dl.m_Flags |= DLF_VOLUMETRIC_FOG;
  628.  
  629.                 const float fMinColorThreshold = GetFloatCVar(e_ParticlesLightMinColorThreshold);
  630.                 const float fMinRadiusThreshold = GetFloatCVar(e_ParticlesLightMinRadiusThreshold);
  631.  
  632.                 const ColorF& cColor = dl.m_Color;
  633.                 const Vec4* vLight = (Vec4*) &dl.m_Origin.x;
  634.                 if ((cColor.r + cColor.g + cColor.b) > fMinColorThreshold && vLight->w > fMinRadiusThreshold)
  635.                 {
  636.                         Get3DEngine()->SetupLightScissors(&dl, passInfo);
  637.                         dl.m_n3DEngineUpdateFrameID = passInfo.GetMainFrameID();
  638.                         Get3DEngine()->AddLightToRenderer(dl, 1.f, passInfo);
  639.                 }
  640.         }
  641. }
  642.  
  643. void CParticleContainer::RenderLights(const SRendParams& RenParams, const SRenderingPassInfo& passInfo)
  644. {
  645.         FUNCTION_PROFILER_CONTAINER(this);
  646.  
  647.         assert(!NeedsUpdate());
  648.  
  649.         assert(GetEnvironmentFlags() & REN_LIGHTS);
  650.  
  651.         for (const auto& part : m_Particles)
  652.         {
  653.                 part.AddLight(RenParams, passInfo);
  654.         }
  655. }
  656.  
  657. void CParticle::GetRenderMatrix(Vec3& vX, Vec3& vY, Vec3& vZ, Vec3& vT, const QuatTS& loc, const SParticleRenderData& RenderData, const SParticleVertexContext& Context) const
  658. {
  659.         ResourceParticleParams const& params = Context.m_Params;
  660.         float fRelativeAge = GetRelativeAge();
  661.  
  662.         vT = loc.t + Context.m_vRenderOffset;
  663.  
  664.         ParticleParams::EFacing eFacing = params.eFacing == params.eFacing.Camera && params.GetTailSteps() ? params.eFacing.CameraX : +params.eFacing;
  665.         if (eFacing == eFacing.Camera)
  666.         {
  667.                 // Set axes to camera plane, keeping vX horizontal.
  668.                 vY = RenderData.GetDirCam(params.fPlaneAlignBlendDistance);
  669.                 vX = Vec3(vY.y, -vY.x + FLT_EPSILON, 0).GetNormalizedFast();
  670.                 vZ = vX ^ vY;
  671.  
  672.                 if (params.bOrientToVelocity)
  673.                 {
  674.                         // Already rotated so Z is in velocity dir.
  675.                         Vec3 vVelDir = loc.q.GetColumn2();
  676.                         RotateAxes(vX, vZ, vVelDir);
  677.  
  678.                         // Allow Z axis to project into screen.
  679.                         if (params.fTexAspect < 1.f)
  680.                         {
  681.                                 float fProj = abs(vVelDir * vY);
  682.                                 fProj = max(fProj, params.fTexAspect);
  683.                                 vZ *= fProj;
  684.                         }
  685.                 }
  686.         }
  687.         else
  688.         {
  689.                 vZ = loc.q.GetColumn2();
  690.                 if (eFacing == eFacing.CameraX)
  691.                         vX = (RenderData.vOffsetCam ^ vZ).GetNormalized();
  692.                 else
  693.                         vX = loc.q.GetColumn0();
  694.                 vY = vZ ^ vX;
  695.         }
  696.  
  697.         if (m_fAngle != 0.f)
  698.         {
  699.                 // Apply planar rotation.
  700.                 Vec2 vDir;
  701.                 sincos_tpl(m_fAngle, &vDir.x, &vDir.y);
  702.                 RotateAxes(vX, vZ, vDir);
  703.         }
  704.  
  705.         // Scale axes by particle size, and texture aspect/thickness
  706.         Vec3 vScale
  707.         (
  708.           RenderData.fSize * Context.m_vTexAspect.x * (m_bFlippedTexture ? -1.f : 1.f)
  709.           * params.fAspect.GetValueFromMod(m_BaseMods.Aspect, fRelativeAge),
  710.           RenderData.fSize * params.fThickness,
  711.           RenderData.fSize * Context.m_vTexAspect.y
  712.         );
  713.  
  714.         vX *= vScale.x;
  715.         vY *= vScale.y;
  716.         vZ *= vScale.z;
  717.  
  718.         // Stretch.
  719.         if (params.fStretch && !params.GetTailSteps())
  720.         {
  721.                 // Disallow stretching further back than starting position.
  722.                 float fStretch = params.fStretch.GetValueFromMod(m_BaseMods.StretchOrTail, fRelativeAge);
  723.                 if (fStretch * (1.f - params.fStretch.fOffsetRatio) > m_fAge)
  724.                         fStretch = m_fAge / (1.f - params.fStretch.fOffsetRatio);
  725.  
  726.                 Vec3 vStretch = m_Vel.vLin * fStretch;
  727.                 fStretch = vStretch.GetLengthFast();
  728.                 ExpandAxis(vX, vStretch, fStretch * abs(vScale.x));
  729.                 ExpandAxis(vY, vStretch, fStretch * abs(vScale.y));
  730.                 ExpandAxis(vZ, vStretch, fStretch * abs(vScale.z));
  731.                 vT += vStretch * params.fStretch.fOffsetRatio;
  732.         }
  733.  
  734.         if (params.fPivotX)
  735.         {
  736.                 vT += vX * params.fPivotX.GetValueFromMod(m_BaseMods.PivotX, fRelativeAge);
  737.         }
  738.         if (params.fPivotY)
  739.         {
  740.                 vT += vZ * (params.fPivotY.GetValueFromMod(m_BaseMods.PivotY, fRelativeAge) * Context.m_fPivotYScale);
  741.         }
  742. }
  743.  
  744. static void AlignVert(SVF_Particle& vert, const ParticleParams& params, const Vec3& vYAxis)
  745. {
  746.         // Rotate to match vYAxis exactly.
  747.         float fYLen2 = vYAxis.GetLengthSquared();
  748.         if (fYLen2 > FLT_MIN)
  749.         {
  750.                 if (params.eFacing == params.eFacing.Camera)
  751.                 {
  752.                         Vec2 vProj(vert.xaxis * vYAxis, vert.yaxis * vYAxis);
  753.                         float fProjLen2 = vProj.GetLength2();
  754.                         if (fProjLen2 > FLT_MIN)
  755.                         {
  756.                                 vProj *= isqrt_fast_tpl(fProjLen2);
  757.                                 vert.xaxis = vert.xaxis * vProj.y - vert.yaxis * vProj.x;
  758.                         }
  759.                 }
  760.                 else
  761.                 {
  762.                         float fPrevYLen2 = vert.yaxis.GetLengthSquared();
  763.                         if (fPrevYLen2 > FLT_MIN)
  764.                         {
  765.                                 Quat qAlign = Quat::CreateRotationV0V1(vert.yaxis * isqrt_fast_tpl(fPrevYLen2), vYAxis * isqrt_fast_tpl(fYLen2));
  766.                                 vert.xaxis = qAlign * vert.xaxis;
  767.                         }
  768.                 }
  769.                 vert.yaxis = vYAxis;
  770.         }
  771. }
  772.  
  773. void FinishConnectedSequence(SLocalRenderVertices& alloc, SParticleVertexContext& Context)
  774. {
  775.         if (Context.m_nSequenceParticles++ > 0)
  776.         {
  777.                 // End of connected polygon
  778.                 AlignVert(Context.m_PrevVert, Context.m_Params, (Context.m_PrevVert.xyz - Context.m_vPrevPos));
  779.                 Context.m_PrevVert.color.a = 0;
  780.                 alloc.AddDualVertices(Context.m_PrevVert);
  781.                 alloc.AddQuadIndices(4, Context.m_uVertexFlags & FOB_ALLOW_TESSELLATION);
  782.         }
  783. }
  784.  
  785. void CParticle::ComputeRenderData(SParticleRenderData& RenderData, const SParticleVertexContext& Context, float fObjectSize) const
  786. {
  787.         RenderData.vCamDir = Context.m_CamInfo.pCamera->GetViewdir();
  788.         RenderData.vOffsetCam = m_Loc.t - Context.m_vCamPos;
  789.         RenderData.fDistCamSq = RenderData.vOffsetCam.GetLengthSquared();
  790.  
  791.         RenderData.fSize = m_Loc.s;
  792.  
  793.         IF (Context.m_fMinDrawAngle != 0, false)
  794.         {
  795.                 float fRadius = RenderData.fSize * fObjectSize;
  796.                 if (sqr(fRadius) < sqr(Context.m_fMinDrawAngle) * RenderData.fDistCamSq)
  797.                         RenderData.fSize = Context.m_fMinDrawAngle * sqrt_fast_tpl(RenderData.fDistCamSq) / fObjectSize;
  798.         }
  799. }
  800.  
  801. static const float fMIN_OCCLUSION_CULL_PIX = 64.f;
  802.  
  803. float CParticle::ComputeRenderAlpha(const SParticleRenderData& RenderData, float fRelativeAge, SParticleVertexContext& Context) const
  804. {
  805.         ResourceParticleParams const& params = Context.m_Params;
  806.  
  807.         Sphere sphere;
  808.         if (Context.m_bComputeBoundingSphere)
  809.         {
  810.                 sphere.center = m_Loc.t + Context.m_vRenderOffset;
  811.                 sphere.radius = RenderData.fSize * GetBaseRadius();
  812.  
  813.                 if (m_aPosHistory && m_aPosHistory[0].IsUsed())
  814.                 {
  815.                         // Add oldest element.
  816.                         Vec3 vTailAxis = (m_aPosHistory[0].Loc.t - m_Loc.t) * 0.5f;
  817.                         sphere.center += vTailAxis;
  818.                         sphere.radius += vTailAxis.GetLengthFast();
  819.                 }
  820.                 else if (params.fStretch)
  821.                 {
  822.                         float fStretch = params.fStretch.GetValueFromMod(m_BaseMods.StretchOrTail, GetRelativeAge());
  823.                         float fStretchLen = fStretch * m_Vel.vLin.GetLengthFast();
  824.                         sphere.radius += fStretchLen * (1.f + abs(params.fStretch.fOffsetRatio));
  825.                 }
  826.         }
  827.  
  828.         if (Context.m_bFrustumCull)
  829.         {
  830.                 // Cull against view frustum
  831.                 Vec3 vPosCam = Context.m_matInvCamera * sphere.center;
  832.                 if (max(abs(vPosCam.x) - sphere.radius * Context.m_fProjectH, abs(vPosCam.z) - sphere.radius * Context.m_fProjectV) > vPosCam.y)
  833.                 {
  834.                         Context.m_nParticlesCulled++;
  835.                         return 0.f;
  836.                 }
  837.         }
  838.  
  839.         // Area of particle in square radians.
  840.         float fFillPix = div_min(square(RenderData.fSize) * Context.m_fFillFactor, RenderData.fDistCamSq, Context.m_fFillMax);
  841.  
  842.         if (Context.m_bOcclusionCull)
  843.         {
  844.                 // Cull against the occlusion buffer
  845.                 if (fFillPix >= fMIN_OCCLUSION_CULL_PIX)
  846.                 {
  847.                         // Compute exact axes
  848.                         SVF_Particle Vert;
  849.                         SetVertexLocation(Vert, m_Loc, RenderData, Context);
  850.  
  851.                         if (!GetObjManager()->CheckOcclusion_TestQuad(Vert.xyz, Vert.xaxis, Vert.yaxis))
  852.                         {
  853.                                 Context.m_nParticlesCulled++;
  854.                                 return 0.f;
  855.                         }
  856.                 }
  857.         }
  858.  
  859.         // Get particle alpha, adjusted for screen fill limit.
  860.         float fAlpha = params.fAlpha.GetValueFromBase(m_BaseMods.Alpha, fRelativeAge);
  861.  
  862.         // Fade near size limits.
  863.         fAlpha *=
  864.           clamp_tpl(fFillPix * Context.m_fInvMinPix - 1.f, 0.f, 1.f) *
  865.           clamp_tpl(fFillPix * Context.m_fMaxPixFactor + 2.f, 0.f, 1.f) *
  866.           Context.DistFunc(RenderData.fDistCamSq);
  867.  
  868.         // fade out when angle to camera gets too steep
  869.         if (params.eFacing != params.eFacing.Camera && params.fFadeAtViewCosAngle > 0)
  870.         {
  871.                 const Vec3 vDist = m_Loc.t - Context.m_vCamPos;
  872.                 const Vec3 vNormal = GetNormal();
  873.                 const Vec3 vCamDir = vDist / sqrt_fast_tpl(RenderData.fDistCamSq);
  874.                 const float cosViewAngle = abs(vCamDir.Dot(vNormal));
  875.  
  876.                 const float fFadeCoefficient = CLAMP(cosViewAngle / params.fFadeAtViewCosAngle, 0.f, 1.f);
  877.                 fAlpha *= powf(fFadeCoefficient, 8.f);
  878.         }
  879.  
  880.         if (Context.m_fClipWaterSense != 0.f)
  881.         {
  882.                 // Clip against water plane
  883.                 Plane pl;
  884.                 float fWaterDist = GetMain().GetPhysEnviron().GetWaterPlane(pl, sphere.center, sphere.radius) * Context.m_fClipWaterSense;
  885.                 if (Context.m_uVertexFlags & FOB_AFTER_WATER)
  886.                         fWaterDist += sphere.radius * 2.f;
  887.                 if (fWaterDist <= 0.f)
  888.                 {
  889.                         Context.m_nParticlesClipped++;
  890.                         return 0.f;
  891.                 }
  892.                 else if (fWaterDist < sphere.radius)
  893.                 {
  894.                         fAlpha *= fWaterDist / sphere.radius;
  895.                 }
  896.         }
  897.  
  898.         // Clip against visibility areas
  899.         IF (Context.m_pClipVisArea, false)
  900.         {
  901.                 Vec3 vNormal;
  902.                 if (params.eFacing == params.eFacing.Camera)
  903.                         vNormal = RenderData.GetDirCam(params.fPlaneAlignBlendDistance);
  904.                 else
  905.                         vNormal = GetNormal();
  906.  
  907.                 float fRadius = sphere.radius;
  908.                 sphere.radius += fRadius;
  909.                 if (GetMain().GetVisEnviron().ClipVisAreas(Context.m_pClipVisArea, sphere, vNormal))
  910.                 {
  911.                         sphere.radius -= fRadius;
  912.                         if (sphere.radius <= 0.f)
  913.                         {
  914.                                 Context.m_nParticlesClipped++;
  915.                                 return 0.f;
  916.                         }
  917.                         else
  918.                         {
  919.                                 fAlpha *= sphere.radius / fRadius;
  920.                         }
  921.                 }
  922.         }
  923.  
  924.         if (fAlpha < Context.m_fMinAlphaMod)
  925.                 return 0.f;
  926.  
  927.         // Track screen fill.
  928.         fFillPix *= params.fFillRateCost;
  929.         Context.m_fPixelsProcessed += fFillPix;
  930.  
  931.         // Cull nearest (latest) particles to enforce max screen fill.
  932.         float fAdjust = 2.f - Context.m_fPixelsRendered * Context.m_fFillFade;
  933.         if (fAdjust < 1.f)
  934.         {
  935.                 fAlpha *= fAdjust;
  936.                 if (fAlpha < Context.m_fMinAlphaMod)
  937.                         return 0.f;
  938.         }
  939.  
  940.         Context.m_fPixelsRendered += fFillPix;
  941.         return fAlpha;
  942. }
  943.  
  944. //////////////////////////////////////////////////////////////////////////
  945. void CParticle::SetVertices(SLocalRenderVertices& alloc, SParticleVertexContext& Context, uint8 uAlpha) const
  946. {
  947.         ResourceParticleParams const& params = Context.m_Params;
  948.  
  949.         float fRelativeAge = GetRelativeAge();
  950.  
  951.         SParticleRenderData RenderData;
  952.         ComputeRenderData(RenderData, Context);
  953.  
  954.         SVF_Particle BaseVert;
  955.  
  956.         // Get color, convert to 8-bit, and use pre-computed alpha.
  957.         ColorF cColor = params.cColor.GetValueFromMod(m_BaseMods.Color, fRelativeAge);
  958.         BaseVert.color.r = float_to_ufrac8(cColor.r);
  959.         BaseVert.color.g = float_to_ufrac8(cColor.g);
  960.         BaseVert.color.b = float_to_ufrac8(cColor.b);
  961.         BaseVert.color.a = uAlpha;
  962.  
  963.         SetVertexLocation(BaseVert, m_Loc, RenderData, Context);
  964.  
  965.         // Texture info.
  966.         BaseVert.st = Context.m_TexInfo;
  967.         BaseVert.st.z = m_nTileVariant;
  968.         if (params.TextureTiling.nAnimFramesCount > 1)
  969.         {
  970.                 // Select anim frame based on particle or emitter age.
  971.                 float fAnimPos = params.Connection ?
  972.                                  params.TextureTiling.GetAnimPos(GetEmitter()->GetAge(), GetEmitter()->GetRelativeAge()) :
  973.                                  params.TextureTiling.GetAnimPos(m_fAge, fRelativeAge);
  974.  
  975.                 // z = integer tile, w = blend fraction
  976.                 fAnimPos *= Context.m_fAnimPosScale;
  977.                 BaseVert.st.z += int(fAnimPos);
  978.                 BaseVert.st.w = float_to_ufrac8(fAnimPos - int(fAnimPos));
  979.         }
  980.  
  981.         if (params.Connection)
  982.         {
  983.                 // Fill in basic texture information.
  984.                 float fTexY;
  985.                 if (params.Connection.eTextureMapping == params.Connection.eTextureMapping.PerStream)
  986.                 {
  987.                         float fEmitterAge = GetEmitter()->GetAge();
  988.                         float fAgeAdjust = max(fEmitterAge - GetEmitter()->GetStopAge(), 0.f);
  989.                         fTexY = div_min(max(m_fAge, 0.f) - fAgeAdjust, min(m_fStopAge - fAgeAdjust, fEmitterAge - GetEmitter()->GetStartAge()), 1.f);
  990.                 }
  991.                 else
  992.                 {
  993.                         int nIndex = Context.m_nPrevSequence == GetEmitterSequence() ? Context.m_nSequenceParticles : -1;
  994.                         if (GetEmitter()->GetSequence() == GetEmitterSequence())
  995.                                 nIndex -= GetEmitter()->GetEmitIndex() - 1;
  996.                         fTexY = float(nIndex) + params.fCount.GetMaxValue() - min(m_fAge, 0.f) / m_fStopAge * params.fCount.GetMaxValue();
  997.                 }
  998.  
  999.                 fTexY = params.Connection.bTextureMirror ? fabs(fmod(fTexY, 2.f) - 1.f) : fmod(fTexY, 1.f);
  1000.                 if (params.Connection.fTextureFrequency < 0.f)
  1001.                         fTexY = 1.f - fTexY;
  1002.                 BaseVert.st.y = float_to_ufrac8(fTexY);
  1003.  
  1004.                 // Defer writing vertices one iteration.
  1005.                 if (Context.m_nPrevSequence == GetEmitterSequence())
  1006.                 {
  1007.                         // Connected to previous particle. Write previous vertex, aligning to neighboring particles.
  1008.                         if (Context.m_nSequenceParticles++ == 0)
  1009.                                 // Write first vertex
  1010.                                 AlignVert(Context.m_PrevVert, params, (BaseVert.xyz - Context.m_PrevVert.xyz));
  1011.                         else
  1012.                         {
  1013.                                 // Write subsequent vertex
  1014.                                 alloc.AddQuadIndices(2, Context.m_uVertexFlags & FOB_ALLOW_TESSELLATION);
  1015.                                 AlignVert(Context.m_PrevVert, params, (BaseVert.xyz - Context.m_vPrevPos) * 0.5f);
  1016.                         }
  1017.                         Context.m_PrevVert.color.a = BaseVert.color.a;
  1018.                         alloc.AddDualVertices(Context.m_PrevVert);
  1019.                         Context.m_vPrevPos = Context.m_PrevVert.xyz;
  1020.                 }
  1021.                 else
  1022.                 {
  1023.                         FinishConnectedSequence(alloc, Context);
  1024.  
  1025.                         // Start of new polygon
  1026.                         Context.m_nPrevSequence = GetEmitterSequence();
  1027.                         Context.m_nSequenceParticles = 0;
  1028.                 }
  1029.  
  1030.                 Context.m_PrevVert = BaseVert;
  1031.  
  1032.                 return;
  1033.         }
  1034.  
  1035.         if (m_aPosHistory)
  1036.         {
  1037.                 return SetTailVertices(BaseVert, RenderData, alloc, Context);
  1038.         }
  1039.  
  1040.         // Store vertex
  1041.         alloc.AddVertex(BaseVert);
  1042.  
  1043.         if (!(Context.m_uVertexFlags & (FOB_POINT_SPRITE | FOB_ALLOW_TESSELLATION)))
  1044.         {
  1045.                 // Expand vertices, create indices.
  1046.                 if (Context.m_uVertexFlags & FOB_OCTAGONAL)
  1047.                 {
  1048.                         // Expand to 8 verts.
  1049.                         alloc.ExpandOctVertices();
  1050.                         alloc.AddOctIndices();
  1051.                 }
  1052.                 else
  1053.                 {
  1054.                         // Expand to 4 verts.
  1055.                         alloc.ExpandQuadVertices();
  1056.                         alloc.AddQuadIndices();
  1057.                 }
  1058.         }
  1059. }
  1060.  
  1061. void CParticle::SetTailVertices(const SVF_Particle& BaseVert, SParticleRenderData RenderData, SLocalRenderVertices& alloc, SParticleVertexContext const& Context) const
  1062. {
  1063.         SVF_Particle TailVert = BaseVert;
  1064.  
  1065.         // Expand first segment backward.
  1066.         TailVert.xyz -= TailVert.yaxis;
  1067.         alloc.AddDualVertices(TailVert);
  1068.  
  1069.         TailVert.xyz = BaseVert.xyz;
  1070.  
  1071.         float fRelativeAge = GetRelativeAge();
  1072.         float fTailLength = min(Context.m_Params.fTailLength.GetValueFromMod(m_BaseMods.StretchOrTail, fRelativeAge), m_fAge);
  1073.  
  1074.         int nPos = GetContainer().GetHistorySteps() - 1;
  1075.         for (; nPos >= 0; nPos--)
  1076.         {
  1077.                 if (m_aPosHistory[nPos].IsUsed())
  1078.                         break;
  1079.         }
  1080.         if (nPos < 0 || fTailLength == 0.f)
  1081.         {
  1082.                 // Return regular quad.
  1083.                 TailVert.xyz += TailVert.yaxis;
  1084.                 TailVert.st.y = 255;
  1085.                 alloc.AddDualVertices(TailVert);
  1086.                 alloc.AddQuadIndices(4, Context.m_uVertexFlags & FOB_ALLOW_TESSELLATION);
  1087.                 return;
  1088.         }
  1089.  
  1090.         // Center of current particle. Estimate tex coord from number of active segments.
  1091.         const float fTexEnd = div_min(RenderData.fSize, m_Vel.vLin.GetLengthFast() * fTailLength + RenderData.fSize + RenderData.fSize, 0.5f);
  1092.         const float fTexTimeGradient = (1.f - fTexEnd - fTexEnd) / fTailLength;
  1093.         const float fYAxisScale = 0.5f / (RenderData.fSize * abs(Context.m_vTexAspect.y));
  1094.  
  1095.         TailVert.st.y = float_to_ufrac8(fTexEnd);
  1096.         alloc.AddDualVertices(TailVert);
  1097.  
  1098.         // Fill with past positions.
  1099.         Vec3 vPrevPos = m_Loc.t;
  1100.         int nVerts = 4;
  1101.         for (; nPos >= 0; nPos--)
  1102.         {
  1103.                 QuatTS qtsLoc = m_aPosHistory[nPos].Loc;
  1104.  
  1105.                 float fAgeDelta = m_fAge - m_aPosHistory[nPos].fAge;
  1106.                 if (fAgeDelta > fTailLength)
  1107.                 {
  1108.                         // Interpolate oldest 2 locations if necessary.
  1109.                         if (m_fAge - fTailLength > m_aPosHistory[nPos + 1].fAge)
  1110.                                 break;
  1111.                         float fT = (m_fAge - fTailLength - m_aPosHistory[nPos].fAge) / (m_aPosHistory[nPos + 1].fAge - m_aPosHistory[nPos].fAge);
  1112.                         if (fT > 0.f)
  1113.                                 qtsLoc.SetNLerp(qtsLoc, m_aPosHistory[nPos + 1].Loc, fT);
  1114.                         fAgeDelta = fTailLength;
  1115.                 }
  1116.  
  1117.                 RenderData.vOffsetCam = qtsLoc.t - Context.m_vCamPos;
  1118.                 RenderData.fDistCamSq = RenderData.vOffsetCam.GetLengthSquared();
  1119.  
  1120.                 SetVertexLocation(TailVert, qtsLoc, RenderData, Context);
  1121.  
  1122.                 if (nPos > 0)
  1123.                 {
  1124.                         float fLen = (m_aPosHistory[nPos - 1].Loc.t - vPrevPos).GetLengthFast();
  1125.                         TailVert.yaxis *= fLen * fYAxisScale;
  1126.                         vPrevPos = m_aPosHistory[nPos].Loc.t;
  1127.                 }
  1128.  
  1129.                 float fTexY = fTexEnd + fTexTimeGradient * fAgeDelta;
  1130.                 TailVert.st.y = float_to_ufrac8(fTexY);
  1131.                 alloc.AddDualVertices(TailVert);
  1132.                 nVerts += 2;
  1133.         }
  1134.  
  1135.         // Half particle at tail end. Expand on y axis.
  1136.         TailVert.xyz += TailVert.yaxis;
  1137.         TailVert.st.y = 255;
  1138.         alloc.AddDualVertices(TailVert);
  1139.         nVerts += 2;
  1140.  
  1141.         // Create indices for variable-length particles.
  1142.         alloc.AddPolyIndices(nVerts, Context.m_uVertexFlags & FOB_ALLOW_TESSELLATION);
  1143. }
  1144.  
  1145. // Determine which particles are visible, save computed alphas in array for reuse, return required particle, vertex, and index count, update stats.
  1146. int CParticleContainer::CullParticles(SParticleVertexContext& Context, int& nVertices, int& nIndices, uint8 auParticleAlpha[])
  1147. {
  1148.         FUNCTION_PROFILER_CONTAINER(this);
  1149.  
  1150.         assert(!NeedsUpdate());
  1151.  
  1152.         int nParticlesRendered = 0;
  1153.  
  1154.         for (const auto& part : m_Particles)
  1155.         {
  1156.                 // Get particle size and area.
  1157.                 SParticleRenderData RenderData;
  1158.                 part.ComputeRenderData(RenderData, Context);
  1159.  
  1160.                 // Save final alpha value.
  1161.                 float fAlpha = part.ComputeRenderAlpha(RenderData, part.GetRelativeAge(), Context);
  1162.                 *auParticleAlpha = float_to_ufrac8(fAlpha);
  1163.                 if (*auParticleAlpha + Context.m_Params.Connection)
  1164.                         nParticlesRendered++;
  1165.                 ++auParticleAlpha;
  1166.         }
  1167.  
  1168.         nVertices = Context.m_nMaxParticleVertices * nParticlesRendered;
  1169.         nIndices = Context.m_nMaxParticleIndices * nParticlesRendered;
  1170.  
  1171.         m_Counts.ParticlesRendered += nParticlesRendered;
  1172.         m_Counts.EmittersRendered += 1.f * !!nParticlesRendered;
  1173.         m_Counts.PixelsProcessed += Context.m_fPixelsProcessed;
  1174.         m_Counts.PixelsRendered += Context.m_fPixelsRendered;
  1175.         m_Counts.ParticlesClip += (float)(Context.m_nParticlesClipped + Context.m_nParticlesCulled);
  1176.  
  1177.         return nParticlesRendered;
  1178. }
  1179.  
  1180. void CParticleContainer::WriteVerticesToVMEM(SParticleVertexContext& Context, SRenderVertices& RenderVerts, const uint8 auParticleAlpha[])
  1181. PREFAST_SUPPRESS_WARNING(6262)   // Function uses '33724' bytes of stack: exceeds /analyze:stacksize'16384'.
  1182. {
  1183.         FUNCTION_PROFILER_CONTAINER(this);
  1184.  
  1185.         assert(!NeedsUpdate());
  1186.  
  1187.         // Adapter to buffer writes to vmem.
  1188.         SLocalRenderVertices alloc(RenderVerts);
  1189.         uint8 uAlphaCullMask = Context.m_Params.Connection ? 1 : 0;
  1190.  
  1191.         for (const auto& part : m_Particles)
  1192.         {
  1193.                 uint8 uAlpha = *auParticleAlpha++;
  1194.                 if (!(uAlpha | uAlphaCullMask))
  1195.                         // Culled from previous pass
  1196.                         continue;
  1197.  
  1198.                 if (!alloc.CheckAvailable(Context.m_nMaxParticleVertices, Context.m_nMaxParticleIndices))
  1199.                         // Out of vertex/index memory.
  1200.                         break;
  1201.  
  1202.                 part.SetVertices(alloc, Context, uAlpha);
  1203.         }
  1204.  
  1205.         if (Context.m_Params.Connection)
  1206.         {
  1207.                 if (alloc.CheckAvailable(Context.m_nMaxParticleVertices, Context.m_nMaxParticleIndices))
  1208.                         FinishConnectedSequence(alloc, Context);
  1209.         }
  1210.  
  1211.         RenderVerts.fPixels = Context.m_fPixelsProcessed;
  1212. }
  1213.  
  1214. void SParticleVertexContext::Init(float fMaxContainerPixels, CParticleContainer* pContainer)
  1215. {
  1216.         ResourceParticleParams const& params = m_Params;
  1217.         CParticleEmitter const& emitterMain = pContainer->GetMain();
  1218.         CVars const& cvars = *emitterMain.GetCVars();
  1219.  
  1220.         m_TexInfo.dcolor = 0;
  1221.  
  1222.         // Compute max vertices and indices needed per particle.
  1223.         if (pContainer->GetHistorySteps())
  1224.         {
  1225.                 m_nMaxParticleVertices = (pContainer->GetHistorySteps() + 3) * 2;
  1226.                 m_nMaxParticleIndices = (m_nMaxParticleVertices - 2) * (m_uVertexFlags & FOB_ALLOW_TESSELLATION ? 2 : 3);
  1227.         }
  1228.         else if (params.Connection)
  1229.         {
  1230.                 m_nMaxParticleVertices = 2;
  1231.                 m_nMaxParticleIndices = m_uVertexFlags & FOB_ALLOW_TESSELLATION ? 4 : 6;
  1232.         }
  1233.         else if (m_uVertexFlags & (FOB_POINT_SPRITE | FOB_ALLOW_TESSELLATION))
  1234.         {
  1235.                 m_nMaxParticleVertices = 1;
  1236.                 m_nMaxParticleIndices = 0;
  1237.                 if (m_uVertexFlags & FOB_OCTAGONAL)
  1238.                         // Flag for instanced vertex shader
  1239.                         m_TexInfo.x = 8;
  1240.         }
  1241.         else if (m_uVertexFlags & FOB_OCTAGONAL)
  1242.         {
  1243.                 m_nMaxParticleVertices = 8;
  1244.                 m_nMaxParticleIndices = 18;
  1245.         }
  1246.         else
  1247.         {
  1248.                 m_nMaxParticleVertices = 4;
  1249.                 m_nMaxParticleIndices = 6;
  1250.         }
  1251.  
  1252.         m_vRenderOffset = (emitterMain.GetLocation().t - m_vCamPos).GetNormalized() * params.fCameraDistanceOffset;
  1253.         m_vCamPos -= m_vRenderOffset;
  1254.  
  1255.         const float fAngularRes = m_CamInfo.pCamera->GetAngularResolution();
  1256.         m_fMaxPixFactor = params.nEnvFlags & REN_SPRITE ?
  1257.                           -2.f * params.fFillRateCost / sqr(max(cvars.e_ParticlesMaxDrawScreen * fAngularRes, 1.f))
  1258.                           : 0.f;
  1259.         m_fFillMax = (float) m_CamInfo.pCamera->GetViewSurfaceX() * m_CamInfo.pCamera->GetViewSurfaceZ();
  1260.  
  1261.         m_vTexAspect.x = m_vTexAspect.y = m_fPivotYScale = 1.f;
  1262.         if (!(params.nEnvFlags & REN_GEOMETRY))
  1263.         {
  1264.                 m_vTexAspect.y = -1.f;
  1265.                 if (params.fTexAspect < 1.f)
  1266.                         m_vTexAspect.x = params.fTexAspect;
  1267.                 else
  1268.                         m_vTexAspect.y /= params.fTexAspect;
  1269.         }
  1270.         if (params.nEnvFlags & REN_SPRITE)
  1271.                 m_fPivotYScale = -1.f;
  1272.  
  1273.         m_fFillFactor = 4.f * abs(m_vTexAspect.x * m_vTexAspect.y) * m_fFillMax;
  1274.         if (m_uVertexFlags & FOB_OCTAGONAL)
  1275.                 m_fFillFactor *= 0.8285f;
  1276.         m_fFillFade = 2.f / fMaxContainerPixels;
  1277.  
  1278.         m_fInvMinPix = sqr(CParticleManager::Instance()->GetMaxAngularDensity(*m_CamInfo.pCamera) * emitterMain.GetViewDistRatioFloat() * params.fViewDistanceAdjust)
  1279.                 / m_fFillFactor;
  1280.  
  1281.         m_fDistFuncCoefs[0] = 1.f;
  1282.         m_fDistFuncCoefs[1] = m_fDistFuncCoefs[2] = 0.f;
  1283.  
  1284.         // Minimum alpha mod value to render, based on param alpha scaling
  1285.         m_fMinAlphaMod = params.Connection ? 0.f : div_min(cvars.e_ParticlesMinDrawAlpha, params.fAlpha.GetMaxValue() * params.AlphaClip.fScale.Max, 1.f);
  1286.  
  1287.         // Size = Pix/2 * Dist / AngularRes
  1288.         const float fMinPixels = max(+params.fMinPixels, float(params.Connection) * 0.125f);
  1289.         m_fMinDrawAngle = fMinPixels * 0.5f / fAngularRes;
  1290.  
  1291.         // Zoom factor is angular res ratio between render and main camera.
  1292.         float fZoom = 1.f / m_CamInfo.pCamera->GetFov();
  1293.         if (params.fCameraMaxDistance > 0.f)
  1294.         {
  1295.                 float fNear = sqr(params.fCameraMinDistance * fZoom);
  1296.                 float fFar = sqr(params.fCameraMaxDistance * fZoom);
  1297.                 float fBorder = (fFar - fNear) * 0.1f;
  1298.                 if (fNear == 0.f)
  1299.                         // No border on near side.
  1300.                         fNear = -fBorder;
  1301.  
  1302.                 /*      f(x) = (1 - ((x-C)/R)^2) / s            ; C = (N+F)/2, R = (F-N)/2
  1303.                       = (-NF + (N+F) x - x^2) / s
  1304.                     f(N+B) = 1
  1305.                     s = B(F-N-B)
  1306.                  */
  1307.                 float fS = 1.f / (fBorder * (fFar - fNear - fBorder));
  1308.                 m_fDistFuncCoefs[0] = -fNear * fFar * fS;
  1309.                 m_fDistFuncCoefs[1] = (fNear + fFar) * fS;
  1310.                 m_fDistFuncCoefs[2] = -fS;
  1311.         }
  1312.         else if (params.fCameraMinDistance > 0.f)
  1313.         {
  1314.                 float fBorder = params.fCameraMinDistance * fZoom * 0.25f;
  1315.                 m_fDistFuncCoefs[1] = 1.f / fBorder;
  1316.                 m_fDistFuncCoefs[0] = -4.f;
  1317.         }
  1318.  
  1319.         m_fAnimPosScale = params.TextureTiling.GetAnimPosScale();
  1320.  
  1321.         // Culling context.
  1322.         m_bFrustumCull = cvars.e_ParticlesCullAgainstViewFrustum
  1323.                          && (params.nEnvFlags & REN_SPRITE) && !params.Connection;
  1324.         m_bOcclusionCull = cvars.e_ParticlesCullAgainstOcclusionBuffer
  1325.                            && (params.nEnvFlags & REN_SPRITE) && !params.Connection && !pContainer->GetHistorySteps()
  1326.                            && !params.bDrawNear && !params.bDrawOnTop && !emitterMain.GetSpawnParams().bNowhere;
  1327.  
  1328.         if (m_bFrustumCull)
  1329.         {
  1330.                 m_matInvCamera = m_CamInfo.pCamera->GetViewMatrix();
  1331.                 Vec3 vFrust = m_CamInfo.pCamera->GetEdgeP();
  1332.                 float fInvX = abs(1.f / vFrust.x),
  1333.                       fInvZ = abs(1.f / vFrust.z);
  1334.  
  1335.                 non_const(m_matInvCamera.GetRow4(0)) *= vFrust.y * fInvX;
  1336.                 non_const(m_matInvCamera.GetRow4(2)) *= vFrust.y * fInvZ;
  1337.  
  1338.                 m_fProjectH = sqrt_tpl(sqr(vFrust.x) + sqr(vFrust.y)) * fInvX;
  1339.                 m_fProjectV = sqrt_tpl(sqr(vFrust.z) + sqr(vFrust.y)) * fInvZ;
  1340.         }
  1341.  
  1342.         m_fClipWaterSense = 0.f;
  1343.         const bool bAllowClip = !(cvars.e_ParticlesDebug & AlphaBit('c'));
  1344.         const SPhysEnviron& PhysEnv = emitterMain.GetPhysEnviron();
  1345.         if (bAllowClip && params.eFacing != params.eFacing.Water && PhysEnv.m_tUnderWater == ETrinary())
  1346.         {
  1347.                 if (params.nEnvFlags & REN_SPRITE)
  1348.                         m_fClipWaterSense = ((m_CamInfo.bCameraUnderwater ^ m_CamInfo.bRecursivePass) == !!(m_uVertexFlags & FOB_AFTER_WATER)) ? -1.f : 1.f;
  1349.                 else if (params.nEnvFlags & REN_GEOMETRY)
  1350.                         m_fClipWaterSense = params.tVisibleUnderwater == ETrinary(false) ? 1.f : params.tVisibleUnderwater == ETrinary(true) ? -1.f : 0.f;
  1351.         }
  1352.  
  1353.         // Vis env to clip against, if needed.
  1354.         m_pClipVisArea = bAllowClip ? emitterMain.GetVisEnviron().GetClipVisArea(m_CamInfo.pCameraVisArea, pContainer->GetBounds()) : 0;
  1355.  
  1356.         m_bComputeBoundingSphere = m_bFrustumCull || m_fClipWaterSense != 0.f || m_pClipVisArea;
  1357.  
  1358.         m_fPixelsProcessed = m_fPixelsRendered = 0.f;
  1359.         m_nParticlesCulled = m_nParticlesClipped = 0;
  1360.  
  1361.         // Init connected-particle context
  1362.         m_nPrevSequence = -1;
  1363.         m_nSequenceParticles = 0;
  1364.         m_PrevVert.xyz = m_vPrevPos = emitterMain.GetLocation().t;
  1365. }
  1366.  
  1367. void CParticleContainer::ComputeVertices(const SCameraInfo& camInfo, CREParticle* pRE, uint64 uVertexFlags, float fMaxPixels)
  1368. {
  1369.         FUNCTION_PROFILER(GetISystem(), PROFILE_PARTICLE);
  1370.         PARTICLE_LIGHT_PROFILER();
  1371.  
  1372.         // Update, if not yet done.
  1373.         assert(GetEnvironmentFlags() & REN_SPRITE);
  1374.         assert(!m_pParams->pStatObj);
  1375.         assert(!NeedsUpdate());
  1376.  
  1377.         if (!m_Particles.empty())
  1378.         {
  1379.                 SParticleVertexContext Context(this, camInfo, uVertexFlags, fMaxPixels);
  1380.  
  1381.                 // Culling stage
  1382.                 int nVertices = 0, nIndices = 0;
  1383.                 STACK_ARRAY(uint8, auParticleAlpha, m_Particles.size());
  1384.  
  1385.                 if (CullParticles(Context, nVertices, nIndices, auParticleAlpha))
  1386.                 {
  1387.                         // Rendering stage
  1388.                         SRenderVertices* pRenderVerts = pRE->AllocVertices(nVertices, nIndices);
  1389.                         assert(pRenderVerts);
  1390.                         if (pRenderVerts->aVertices.capacity() < nVertices || pRenderVerts->aIndices.capacity() < nIndices)
  1391.                                 CParticleManager::Instance()->MarkAsOutOfMemory();
  1392.                         WriteVerticesToVMEM(Context, *pRenderVerts, auParticleAlpha);
  1393.                 }
  1394.         }
  1395. }
  1396.  
  1397. //////////////////////////////////////////////////////////////////////////
  1398. struct SVertexIndexPoolUsageCmp
  1399. {
  1400.         // sort that the bigger entry is always at positon 0
  1401.         ILINE bool operator()(const SVertexIndexPoolUsage& rA, const SVertexIndexPoolUsage& rB) const
  1402.         {
  1403.                 return rA.nVertexMemory > rB.nVertexMemory;
  1404.         }
  1405. };
  1406.  
  1407. void CParticleManager::AddVertexIndexPoolUsageEntry(uint32 nVertexMemory, uint32 nIndexMemory, const char* pContainerName)
  1408. {
  1409. #if defined(PARTICLE_COLLECT_VERT_IND_POOL_USAGE)
  1410.         SVertexIndexPoolUsage arrPoolUsage[nVertexIndexPoolUsageEntries];
  1411.         memcpy(arrPoolUsage, m_arrVertexIndexPoolUsage, sizeof(arrPoolUsage));
  1412.         if (nVertexMemory > arrPoolUsage[nVertexIndexPoolUsageEntries - 1].nVertexMemory)
  1413.         {
  1414.                 // for SPUs simulation time, we need to generate a stack copy of the list to sort
  1415.  
  1416.                 SVertexIndexPoolUsage newEntry = { nVertexMemory, nIndexMemory, pContainerName };
  1417.                 arrPoolUsage[nVertexIndexPoolUsageEntries - 1] = newEntry;
  1418.                 // sort to move the biggest elements to the front, which has the nice side effect that empty
  1419.                 // elements with a nVertexMemory of 0 are moved to the back, thus the algorithm handles
  1420.                 // removing of empty and smallest elements in the same way
  1421.                 std::sort(arrPoolUsage, arrPoolUsage + nVertexIndexPoolUsageEntries, SVertexIndexPoolUsageCmp());
  1422.         }
  1423.         memcpy(m_arrVertexIndexPoolUsage, arrPoolUsage, sizeof(arrPoolUsage));
  1424.         m_nRequieredVertexPoolMemory += nVertexMemory;
  1425.         m_nRequieredIndexPoolMemory += nIndexMemory;
  1426.         m_nRendererParticleContainer += 1;
  1427. #endif
  1428. }
  1429.  
  1430. void CParticleManager::MarkAsOutOfMemory()
  1431. {
  1432. #if defined(PARTICLE_COLLECT_VERT_IND_POOL_USAGE)
  1433.         m_bOutOfVertexIndexPoolMemory = true;
  1434. #endif
  1435. }
  1436.  
downloadParticleRender.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