BVB Source Codes

CRYENGINE Show SkyLightManager.cpp Source code

Return Download CRYENGINE: download SkyLightManager.cpp Source code - Download CRYENGINE Source code - Type:.cpp
  1. // Copyright 2001-2016 Crytek GmbH / Crytek Group. All rights reserved.
  2.  
  3. /*************************************************************************
  4.    -------------------------------------------------------------------------
  5.    $Id$
  6.    $DateTime$
  7.  
  8.    -------------------------------------------------------------------------
  9.    History:
  10.    - 09:05:2005   14:32 : Created by Carsten Wenzel
  11.  
  12. *************************************************************************/
  13.  
  14. #include "StdAfx.h"
  15. #include "SkyLightManager.h"
  16. #include "SkyLightNishita.h"
  17. #include <CryRenderer/RenderElements/CRESky.h>
  18. #include <CryThreading/IJobManager_JobDelegator.h>
  19.  
  20. DECLARE_JOB("SkyUpdate", TSkyJob, CSkyLightManager::UpdateInternal);
  21. static JobManager::SJobState g_JobState;
  22.  
  23. CSkyLightManager::CSkyLightManager()
  24.         : m_pSkyLightNishita(CryAlignedNew<CSkyLightNishita>())
  25.         , m_pSkyDomeMesh(0)
  26.         , m_curSkyDomeCondition()
  27.         , m_updatingSkyDomeCondition()
  28.         , m_numSkyDomeColorsComputed(SSkyLightRenderParams::skyDomeTextureSize)
  29.         , m_curBackBuffer(0)
  30.         , m_lastFrameID(0)
  31.         , m_curSkyHemiColor()
  32.         , m_curHazeColor(0.0f, 0.0f, 0.0f)
  33.         , m_curHazeColorMieNoPremul(0.0f, 0.0f, 0.0f)
  34.         , m_curHazeColorRayleighNoPremul(0.0f, 0.0f, 0.0f)
  35.         , m_skyHemiColorAccum()
  36.         , m_hazeColorAccum(0.0f, 0.0f, 0.0f)
  37.         , m_hazeColorMieNoPremulAccum(0.0f, 0.0f, 0.0f)
  38.         , m_hazeColorRayleighNoPremulAccum(0.0f, 0.0f, 0.0f)
  39.         , m_bFlushFullUpdate(false)
  40.         , m_renderParams()
  41. {
  42.         MEMSTAT_CONTEXT(EMemStatContextTypes::MSC_Other, 0, "SkyLightManager");
  43.  
  44.         if (GetRenderer())
  45.         {
  46.                 InitSkyDomeMesh();
  47.         }
  48.  
  49.         m_updateRequested[0] = m_updateRequested[1] = 0;
  50.  
  51.         // init textures with default data
  52.         m_skyDomeTextureDataMie[0].resize(SSkyLightRenderParams::skyDomeTextureSize);
  53.         m_skyDomeTextureDataMie[1].resize(SSkyLightRenderParams::skyDomeTextureSize);
  54.         m_skyDomeTextureDataRayleigh[0].resize(SSkyLightRenderParams::skyDomeTextureSize);
  55.         m_skyDomeTextureDataRayleigh[1].resize(SSkyLightRenderParams::skyDomeTextureSize);
  56.  
  57.         // init time stamps
  58.         m_skyDomeTextureTimeStamp[0] = gEnv->nMainFrameID;
  59.         m_skyDomeTextureTimeStamp[1] = gEnv->nMainFrameID;
  60.  
  61.         // init sky hemisphere colors and accumulators
  62.         memset(m_curSkyHemiColor, 0, sizeof(m_curSkyHemiColor));
  63.         memset(m_skyHemiColorAccum, 0, sizeof(m_skyHemiColorAccum));
  64.  
  65.         // set default render parameters
  66.         UpdateRenderParams();
  67. }
  68.  
  69. CSkyLightManager::~CSkyLightManager()
  70. {
  71.         CryAlignedDelete(m_pSkyLightNishita);
  72. }
  73.  
  74. inline static void Sync()
  75. {
  76.         gEnv->GetJobManager()->WaitForJob(g_JobState);
  77. }
  78.  
  79. void CSkyLightManager::PushUpdateParams()
  80. {
  81.         //pushes the update parameters, explicite call since engine requests asynchronously
  82.         memcpy(&m_reqSkyDomeCondition[0], &m_reqSkyDomeCondition[1], sizeof(SSkyDomeCondition));
  83.         m_updateRequested[0] = m_updateRequested[1];
  84.         m_updateRequested[1] = 0;
  85. }
  86.  
  87. void CSkyLightManager::SetSkyDomeCondition(const SSkyDomeCondition& skyDomeCondition)
  88. {
  89.         m_reqSkyDomeCondition[1] = skyDomeCondition;
  90.         m_updateRequested[1] = 1;
  91. }
  92.  
  93. void CSkyLightManager::FullUpdate()
  94. {
  95.         Sync();
  96.         PushUpdateParams();
  97.         TSkyJob job(GetRenderer()->GetFrameID(false), SSkyLightRenderParams::skyDomeTextureSize, (int)1);
  98.         job.SetClassInstance(this);
  99.         job.RegisterJobState(&g_JobState);
  100.         job.Run();
  101.         m_needRenderParamUpdate = true;
  102.         m_bFlushFullUpdate = true;
  103. }
  104.  
  105. void CSkyLightManager::IncrementalUpdate(f32 updateRatioPerFrame, const SRenderingPassInfo& passInfo)
  106. {
  107.         Sync();
  108.  
  109.         FUNCTION_PROFILER_3DENGINE;
  110.  
  111.         // get current ID of "main" frame (no recursive rendering),
  112.         // incremental update should only be processed once per frame
  113.         if (m_lastFrameID != passInfo.GetMainFrameID())
  114.         {
  115.                 int32 numUpdate((int32) ((f32) SSkyLightRenderParams::skyDomeTextureSize * updateRatioPerFrame / 100.0f + 0.5f));
  116.                 numUpdate = clamp_tpl(numUpdate, 1, SSkyLightRenderParams::skyDomeTextureSize);
  117.                 if (m_needRenderParamUpdate)
  118.                         UpdateRenderParams();     // update render params
  119.  
  120.                 PushUpdateParams();
  121.  
  122.                 TSkyJob job(passInfo.GetMainFrameID(), numUpdate, (int)0);
  123.                 job.SetClassInstance(this);
  124.                 job.RegisterJobState(&g_JobState);
  125.                 job.Run();
  126.         }
  127. }
  128.  
  129. void CSkyLightManager::UpdateInternal(int32 newFrameID, int32 numUpdates, int callerIsFullUpdate)
  130. {
  131.         FUNCTION_PROFILER_3DENGINE;
  132.  
  133.         // update sky dome if requested -- requires last update request to be fully processed!
  134.         int procUpdate = callerIsFullUpdate;
  135.         procUpdate |= (int)IsSkyDomeUpdateFinished();
  136.         procUpdate &= m_updateRequested[0];
  137.         if (procUpdate)
  138.         {
  139.                 // set sky dome settings
  140.                 memcpy(&m_updatingSkyDomeCondition, &m_reqSkyDomeCondition[0], sizeof(SSkyDomeCondition));
  141.                 m_pSkyLightNishita->SetSunDirection(m_updatingSkyDomeCondition.m_sunDirection);
  142.                 m_pSkyLightNishita->SetRGBWaveLengths(m_updatingSkyDomeCondition.m_rgbWaveLengths);
  143.                 m_pSkyLightNishita->SetAtmosphericConditions(m_updatingSkyDomeCondition.m_sunIntensity,
  144.                                                              1e-4f * m_updatingSkyDomeCondition.m_Km, 1e-4f * m_updatingSkyDomeCondition.m_Kr, m_updatingSkyDomeCondition.m_g); // scale mie and rayleigh scattering for more convenient editing in time of day dialog
  145.  
  146.                 // update request has been accepted
  147.                 m_updateRequested[0] = 0;
  148.                 m_numSkyDomeColorsComputed = 0;
  149.  
  150.                 // reset sky & haze color accumulator
  151.                 m_hazeColorAccum = Vec3(0.0f, 0.0f, 0.0f);
  152.                 m_hazeColorMieNoPremulAccum = Vec3(0.0f, 0.0f, 0.0f);
  153.                 m_hazeColorRayleighNoPremulAccum = Vec3(0.0f, 0.0f, 0.0f);
  154.                 memset(m_skyHemiColorAccum, 0, sizeof(m_skyHemiColorAccum));
  155.         }
  156.  
  157.         // any work to do?
  158.         if (false == IsSkyDomeUpdateFinished())
  159.         {
  160.                 if (numUpdates <= 0)
  161.                 {
  162.                         // do a full update
  163.                         numUpdates = SSkyLightRenderParams::skyDomeTextureSize;
  164.                 }
  165.  
  166.                 // find minimally required work load for this incremental update
  167.                 numUpdates = min(SSkyLightRenderParams::skyDomeTextureSize - m_numSkyDomeColorsComputed, numUpdates);
  168.  
  169.                 // perform color computations
  170.                 SkyDomeTextureData& skyDomeTextureDataMie(m_skyDomeTextureDataMie[GetBackBuffer()]);
  171.                 SkyDomeTextureData& skyDomeTextureDataRayleigh(m_skyDomeTextureDataRayleigh[GetBackBuffer()]);
  172.  
  173.                 int32 numSkyDomeColorsComputed(m_numSkyDomeColorsComputed);
  174.                 for (; numUpdates > 0; --numUpdates, ++numSkyDomeColorsComputed)
  175.                 {
  176.                         // calc latitude/longitude
  177.                         int lon(numSkyDomeColorsComputed / SSkyLightRenderParams::skyDomeTextureWidth);
  178.                         int lat(numSkyDomeColorsComputed % SSkyLightRenderParams::skyDomeTextureWidth);
  179.  
  180.                         float lonArc(DEG2RAD((float) lon * 90.0f / (float) SSkyLightRenderParams::skyDomeTextureHeight));
  181.                         float latArc(DEG2RAD((float) lat * 360.0f / (float) SSkyLightRenderParams::skyDomeTextureWidth));
  182.  
  183.                         float sinLon(0);
  184.                         float cosLon(0);
  185.                         sincos_tpl(lonArc, &sinLon, &cosLon);
  186.                         float sinLat(0);
  187.                         float cosLat(0);
  188.                         sincos_tpl(latArc, &sinLat, &cosLat);
  189.  
  190.                         // calc sky direction for given update latitude/longitude (hemisphere)
  191.                         Vec3 skyDir(sinLon * cosLat, sinLon * sinLat, cosLon);
  192.  
  193.                         // compute color
  194.                         //Vec3 skyColAtDir( 0.0, 0.0, 0.0 );
  195.                         Vec3 skyColAtDirMieNoPremul(0.0, 0.0, 0.0);
  196.                         Vec3 skyColAtDirRayleighNoPremul(0.0, 0.0, 0.0);
  197.                         Vec3 skyColAtDirRayleigh(0.0, 0.0, 0.0);
  198.  
  199.                         m_pSkyLightNishita->ComputeSkyColor(skyDir, 0, &skyColAtDirMieNoPremul, &skyColAtDirRayleighNoPremul, &skyColAtDirRayleigh);
  200.  
  201.                         // store color in texture
  202.                         skyDomeTextureDataMie[numSkyDomeColorsComputed] = CryHalf4(skyColAtDirMieNoPremul.x, skyColAtDirMieNoPremul.y, skyColAtDirMieNoPremul.z, 1.0f);
  203.                         skyDomeTextureDataRayleigh[numSkyDomeColorsComputed] = CryHalf4(skyColAtDirRayleighNoPremul.x, skyColAtDirRayleighNoPremul.y, skyColAtDirRayleighNoPremul.z, 1.0f);
  204.  
  205.                         // update haze color accum (accumulate second last sample row)
  206.                         if (lon == SSkyLightRenderParams::skyDomeTextureHeight - 2)
  207.                         {
  208.                                 m_hazeColorAccum += skyColAtDirRayleigh;
  209.                                 m_hazeColorMieNoPremulAccum += skyColAtDirMieNoPremul;
  210.                                 m_hazeColorRayleighNoPremulAccum += skyColAtDirRayleighNoPremul;
  211.                         }
  212.  
  213.                         // update sky hemisphere color accumulator
  214.                         int y(lon >> SSkyLightRenderParams::skyDomeTextureHeightBy2Log);
  215.                         int x(((lat + SSkyLightRenderParams::skyDomeTextureWidthBy8) & (SSkyLightRenderParams::skyDomeTextureWidth - 1)) >> SSkyLightRenderParams::skyDomeTextureWidthBy4Log);
  216.                         int skyHemiColAccumIdx(x * y + y);
  217.                         assert(((unsigned int)skyHemiColAccumIdx) < 5);
  218.                         m_skyHemiColorAccum[skyHemiColAccumIdx] += skyColAtDirRayleigh;
  219.                 }
  220.  
  221.                 m_numSkyDomeColorsComputed = numSkyDomeColorsComputed;
  222.  
  223.                 // sky dome update finished?
  224.                 if (false != IsSkyDomeUpdateFinished())
  225.                 {
  226.                         // update time stamp
  227.                         m_skyDomeTextureTimeStamp[GetBackBuffer()] = newFrameID;
  228.  
  229.                         // get new haze color
  230.                         const float c_invNumHazeSamples(1.0f / (float) SSkyLightRenderParams::skyDomeTextureWidth);
  231.                         m_curHazeColor = m_hazeColorAccum * c_invNumHazeSamples;
  232.                         m_curHazeColorMieNoPremul = m_hazeColorMieNoPremulAccum * c_invNumHazeSamples;
  233.                         m_curHazeColorRayleighNoPremul = m_hazeColorRayleighNoPremulAccum * c_invNumHazeSamples;
  234.  
  235.                         // get new sky hemisphere colors
  236.                         const float c_scaleHemiTop(2.0f / (SSkyLightRenderParams::skyDomeTextureWidth * SSkyLightRenderParams::skyDomeTextureHeight));
  237.                         const float c_scaleHemiSide(8.0f / (SSkyLightRenderParams::skyDomeTextureWidth * SSkyLightRenderParams::skyDomeTextureHeight));
  238.                         m_curSkyHemiColor[0] = m_skyHemiColorAccum[0] * c_scaleHemiTop;
  239.                         m_curSkyHemiColor[1] = m_skyHemiColorAccum[1] * c_scaleHemiSide;
  240.                         m_curSkyHemiColor[2] = m_skyHemiColorAccum[2] * c_scaleHemiSide;
  241.                         m_curSkyHemiColor[3] = m_skyHemiColorAccum[3] * c_scaleHemiSide;
  242.                         m_curSkyHemiColor[4] = m_skyHemiColorAccum[4] * c_scaleHemiSide;
  243.  
  244.                         // toggle sky light buffers
  245.                         ToggleBuffer();
  246.                 }
  247.         }
  248.  
  249.         // update frame ID
  250.         m_lastFrameID = newFrameID;
  251. }
  252.  
  253. void CSkyLightManager::SetQuality(int32 quality)
  254. {
  255.         if (quality != m_pSkyLightNishita->GetInScatteringIntegralStepSize())
  256.         {
  257.                 Sync();
  258.                 // when setting new quality we need to start sky dome update from scratch...
  259.                 // ... to avoid "artifacts" in the resulting texture
  260.                 m_numSkyDomeColorsComputed = 0;
  261.                 m_pSkyLightNishita->SetInScatteringIntegralStepSize(quality);
  262.         }
  263. }
  264.  
  265. const SSkyLightRenderParams* CSkyLightManager::GetRenderParams() const
  266. {
  267.         return &m_renderParams;
  268. }
  269.  
  270. void CSkyLightManager::UpdateRenderParams()
  271. {
  272.         // sky dome mesh data
  273.         m_renderParams.m_pSkyDomeMesh = m_pSkyDomeMesh;
  274.  
  275.         // sky dome texture access
  276.         m_renderParams.m_skyDomeTextureTimeStamp = m_skyDomeTextureTimeStamp[GetFrontBuffer()];
  277.         m_renderParams.m_pSkyDomeTextureDataMie = (const void*) &m_skyDomeTextureDataMie[GetFrontBuffer()][0];
  278.         m_renderParams.m_pSkyDomeTextureDataRayleigh = (const void*) &m_skyDomeTextureDataRayleigh[GetFrontBuffer()][0];
  279.         m_renderParams.m_skyDomeTexturePitch = SSkyLightRenderParams::skyDomeTextureWidth * sizeof(CryHalf4);
  280.  
  281.         // shader constants for final per-pixel phase computation
  282.         m_renderParams.m_partialMieInScatteringConst = m_pSkyLightNishita->GetPartialMieInScatteringConst();
  283.         m_renderParams.m_partialRayleighInScatteringConst = m_pSkyLightNishita->GetPartialRayleighInScatteringConst();
  284.         Vec3 sunDir(m_pSkyLightNishita->GetSunDirection());
  285.         m_renderParams.m_sunDirection = Vec4(sunDir.x, sunDir.y, sunDir.z, 0.0f);
  286.         m_renderParams.m_phaseFunctionConsts = m_pSkyLightNishita->GetPhaseFunctionConsts();
  287.         m_renderParams.m_hazeColor = Vec4(m_curHazeColor.x, m_curHazeColor.y, m_curHazeColor.z, 0);
  288.         m_renderParams.m_hazeColorMieNoPremul = Vec4(m_curHazeColorMieNoPremul.x, m_curHazeColorMieNoPremul.y, m_curHazeColorMieNoPremul.z, 0);
  289.         m_renderParams.m_hazeColorRayleighNoPremul = Vec4(m_curHazeColorRayleighNoPremul.x, m_curHazeColorRayleighNoPremul.y, m_curHazeColorRayleighNoPremul.z, 0);
  290.  
  291.         // set sky hemisphere colors
  292.         m_renderParams.m_skyColorTop = m_curSkyHemiColor[0];
  293.         m_renderParams.m_skyColorNorth = m_curSkyHemiColor[3];
  294.         m_renderParams.m_skyColorWest = m_curSkyHemiColor[4];
  295.         m_renderParams.m_skyColorSouth = m_curSkyHemiColor[1];
  296.         m_renderParams.m_skyColorEast = m_curSkyHemiColor[2];
  297.  
  298.         // copy sky dome condition params
  299.         m_curSkyDomeCondition = m_updatingSkyDomeCondition;
  300.  
  301.         m_needRenderParamUpdate = 0;
  302. }
  303.  
  304. void CSkyLightManager::GetCurSkyDomeCondition(SSkyDomeCondition& skyCond) const
  305. {
  306.         skyCond = m_curSkyDomeCondition;
  307. }
  308.  
  309. bool CSkyLightManager::IsSkyDomeUpdateFinished() const
  310. {
  311.         return(SSkyLightRenderParams::skyDomeTextureSize == m_numSkyDomeColorsComputed);
  312. }
  313.  
  314. void CSkyLightManager::InitSkyDomeMesh()
  315. {
  316.         ReleaseSkyDomeMesh();
  317.  
  318.         if (!GetRenderer())
  319.                 return;
  320.  
  321. #if CRY_PLATFORM_MOBILE
  322.         const uint32 c_numRings(10);
  323.         const uint32 c_numSections(10);
  324. #else
  325.         const uint32 c_numRings(20);
  326.         const uint32 c_numSections(20);
  327. #endif
  328.         const uint32 c_numSkyDomeVertices((c_numRings + 1) * (c_numSections + 1));
  329.         const uint32 c_numSkyDomeTriangles(2 * c_numRings * c_numSections);
  330.         const uint32 c_numSkyDomeIndices(c_numSkyDomeTriangles * 3);
  331.  
  332.         std::vector<vtx_idx> skyDomeIndices;
  333.         std::vector<SVF_P3F_C4B_T2F> skyDomeVertices;
  334.  
  335.         // setup buffers with source data
  336.         skyDomeVertices.reserve(c_numSkyDomeVertices);
  337.         skyDomeIndices.reserve(c_numSkyDomeIndices);
  338.  
  339.         // calculate vertices
  340.         float sectionSlice(DEG2RAD(360.0f / (float) c_numSections));
  341.         float ringSlice(DEG2RAD(180.0f / (float) c_numRings));
  342.         for (uint32 a(0); a <= c_numRings; ++a)
  343.         {
  344.                 float w(sinf(a * ringSlice));
  345.                 float z(cosf(a * ringSlice));
  346.  
  347.                 for (uint32 i(0); i <= c_numSections; ++i)
  348.                 {
  349.                         SVF_P3F_C4B_T2F v;
  350.  
  351.                         float ii(i - a * 0.5f);   // Gives better tessellation, requires texture address mode to be "wrap"
  352.                                                   // for u when rendering (see v.st[ 0 ] below). Otherwise set ii = i;
  353.                         v.xyz = Vec3(cosf(ii * sectionSlice) * w, sinf(ii * sectionSlice) * w, z);
  354.                         assert(fabs(v.xyz.GetLengthSquared() - 1.0) < 1e-2 /*1e-4*/);       // because of FP-16 precision
  355.                         v.st = Vec2(ii / (float) c_numSections, 2.0f * (float) a / (float) c_numRings);
  356.                         skyDomeVertices.push_back(v);
  357.                 }
  358.         }
  359.  
  360.         // build faces
  361.         for (uint32 a(0); a < c_numRings; ++a)
  362.         {
  363.                 for (uint32 i(0); i < c_numSections; ++i)
  364.                 {
  365.                         skyDomeIndices.push_back((vtx_idx) (a * (c_numSections + 1) + i + 1));
  366.                         skyDomeIndices.push_back((vtx_idx) (a * (c_numSections + 1) + i));
  367.                         skyDomeIndices.push_back((vtx_idx) ((a + 1) * (c_numSections + 1) + i + 1));
  368.  
  369.                         skyDomeIndices.push_back((vtx_idx) ((a + 1) * (c_numSections + 1) + i));
  370.                         skyDomeIndices.push_back((vtx_idx) ((a + 1) * (c_numSections + 1) + i + 1));
  371.                         skyDomeIndices.push_back((vtx_idx) (a * (c_numSections + 1) + i));
  372.                 }
  373.         }
  374.  
  375.         // sanity checks
  376.         assert(skyDomeVertices.size() == c_numSkyDomeVertices);
  377.         assert(skyDomeIndices.size() == c_numSkyDomeIndices);
  378.  
  379.         // create static buffers in renderer
  380.         m_pSkyDomeMesh = gEnv->pRenderer->CreateRenderMeshInitialized(&skyDomeVertices[0], c_numSkyDomeVertices, eVF_P3F_C4B_T2F,
  381.                                                                       &skyDomeIndices[0], c_numSkyDomeIndices, prtTriangleList, "SkyHDR", "SkyHDR");
  382.  
  383.         m_lastFrameID = 0;
  384.         m_needRenderParamUpdate = true;
  385. }
  386.  
  387. void CSkyLightManager::ReleaseSkyDomeMesh()
  388. {
  389.         m_renderParams.m_pSkyDomeMesh = NULL;
  390.         m_pSkyDomeMesh = NULL;
  391. }
  392.  
  393. int CSkyLightManager::GetFrontBuffer() const
  394. {
  395.         assert(m_curBackBuffer >= 0 && m_curBackBuffer <= 1);
  396.         return((m_curBackBuffer + 1) & 1);
  397. }
  398.  
  399. int CSkyLightManager::GetBackBuffer() const
  400. {
  401.         assert(m_curBackBuffer >= 0 && m_curBackBuffer <= 1);
  402.         return(m_curBackBuffer);
  403. }
  404.  
  405. void CSkyLightManager::ToggleBuffer()
  406. {
  407.         assert(m_curBackBuffer >= 0 && m_curBackBuffer <= 1);
  408.         //better enforce cache flushing then making PPU wait til job has been finished
  409.         m_curBackBuffer = (m_curBackBuffer + 1) & 1;
  410.         m_needRenderParamUpdate = 1;
  411. }
  412.  
  413. void CSkyLightManager::GetMemoryUsage(ICrySizer* pSizer) const
  414. {
  415.         pSizer->AddObject(this, sizeof(*this));
  416.         pSizer->AddObject(m_pSkyLightNishita);
  417.         pSizer->AddObject(m_skyDomeTextureDataMie[0]);
  418.         pSizer->AddObject(m_skyDomeTextureDataMie[1]);
  419.         pSizer->AddObject(m_skyDomeTextureDataRayleigh[0]);
  420.         pSizer->AddObject(m_skyDomeTextureDataRayleigh[1]);
  421. }
  422.  
downloadSkyLightManager.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