BVB Source Codes

CRYENGINE Show PlaySequenceNode.cpp Source code

Return Download CRYENGINE: download PlaySequenceNode.cpp Source code - Download CRYENGINE Source code - Type:.cpp
  1. // Copyright 2001-2016 Crytek GmbH / Crytek Group. All rights reserved.
  2.  
  3. #include "StdAfx.h"
  4. #include "CryActionCVars.h"
  5.  
  6. #include <CryFlowGraph/IFlowBaseNode.h>
  7. #include <CryAnimation/ICryAnimation.h>
  8. #include <CryMovie/IMovieSystem.h>
  9. #include <IViewSystem.h>
  10.  
  11. class CPlaySequence_Node : public CFlowBaseNode<eNCT_Instanced>, public IMovieListener
  12. {
  13.         enum INPUTS
  14.         {
  15.                 EIP_Sequence,
  16.                 EIP_Start,
  17.                 EIP_Pause,
  18.                 EIP_Stop,
  19.                 EIP_Precache,
  20.                 EIP_BreakOnStop,
  21.                 EIP_BlendPosSpeed,
  22.                 EIP_BlendRotSpeed,
  23.                 EIP_PerformBlendOut,
  24.                 EIP_StartTime,
  25.                 EIP_PlaySpeed,
  26.                 EIP_JumpToTime,
  27.                 EIP_TriggerJumpToTime,
  28.                 EIP_SequenceId, // deprecated
  29.                 EIP_TriggerJumpToEnd,
  30.         };
  31.  
  32.         enum OUTPUTS
  33.         {
  34.                 EOP_Started = 0,
  35.                 EOP_Done,
  36.                 EOP_Finished,
  37.                 EOP_Aborted,
  38.                 EOP_SequenceTime,
  39.                 EOP_CurrentSpeed,
  40.         };
  41.  
  42.         typedef enum
  43.         {
  44.                 PS_Stopped,
  45.                 PS_Playing,
  46.                 PS_Last
  47.         } EPlayingState;
  48.  
  49.         _smart_ptr<IAnimSequence> m_pSequence;
  50.         SActivationInfo           m_actInfo;
  51.         EPlayingState             m_playingState;
  52.         float                     m_currentTime;
  53.         float                     m_currentSpeed;
  54.  
  55. public:
  56.         CPlaySequence_Node(SActivationInfo* pActInfo)
  57.         {
  58.                 m_pSequence = 0;
  59.                 m_actInfo = *pActInfo;
  60.                 m_playingState = PS_Stopped;
  61.                 m_currentSpeed = 0.0f;
  62.                 m_currentTime = 0.0f;
  63.         };
  64.  
  65.         virtual void GetMemoryUsage(ICrySizer* s) const
  66.         {
  67.                 s->Add(*this);
  68.         }
  69.  
  70.         ~CPlaySequence_Node()
  71.         {
  72.                 if (m_pSequence)
  73.                 {
  74.                         IMovieSystem* pMovieSystem = gEnv->pMovieSystem;
  75.                         if (pMovieSystem)
  76.                         {
  77.                                 pMovieSystem->RemoveMovieListener(m_pSequence, this);
  78.                         }
  79.                 }
  80.         };
  81.  
  82.         IFlowNodePtr Clone(SActivationInfo* pActInfo)
  83.         {
  84.                 return new CPlaySequence_Node(pActInfo);
  85.         }
  86.  
  87.         virtual void Serialize(SActivationInfo* pActInfo, TSerialize ser)
  88.         {
  89.                 ser.BeginGroup("Local");
  90.                 if (ser.IsWriting())
  91.                 {
  92.                         ser.EnumValue("m_playingState", m_playingState, PS_Stopped, PS_Last);
  93.                         ser.Value("curTime", m_currentTime);
  94.                         ser.Value("m_playSpeed", m_currentSpeed);
  95.                         bool bPaused = m_pSequence ? m_pSequence->IsPaused() : false;
  96.                         ser.Value("m_paused", bPaused);
  97.                 }
  98.                 else
  99.                 {
  100.                         EPlayingState playingState;
  101.  
  102.                         {
  103.                                 // for backward compatibility - TODO: remove
  104.                                 bool playing = false;
  105.                                 ser.Value("m_bPlaying", playing);
  106.                                 playingState = playing ? PS_Playing : PS_Stopped;
  107.                                 /// end remove
  108.                         }
  109.  
  110.                         ser.EnumValue("m_playingState", playingState, PS_Stopped, PS_Last);
  111.  
  112.                         ser.Value("curTime", m_currentTime);
  113.                         ser.Value("m_playSpeed", m_currentSpeed);
  114.  
  115.                         bool bPaused;
  116.                         ser.Value("m_paused", bPaused);
  117.  
  118.                         if (playingState == PS_Playing)
  119.                         {
  120.                                 // restart sequence, possibly at the last frame
  121.                                 StartSequence(pActInfo, m_currentTime, m_currentSpeed, false);
  122.  
  123.                                 if (m_pSequence && bPaused)
  124.                                 {
  125.                                         m_pSequence->Pause();
  126.                                 }
  127.                         }
  128.                         else
  129.                         {
  130.                                 // this unregisters only! because all sequences have been stopped already
  131.                                 // by MovieSystem's Reset in GameSerialize.cpp
  132.                                 StopSequence(pActInfo, true);
  133.                         }
  134.                 }
  135.                 ser.EndGroup();
  136.         }
  137.  
  138.         virtual void GetConfiguration(SFlowNodeConfig& config)
  139.         {
  140.                 static const SInputPortConfig in_config[] = {
  141.                         InputPortConfig<string>("seq_Sequence_File", _HELP("Name of the Sequence"),                                                                      _HELP("Sequence")),
  142.                         InputPortConfig_Void("Trigger",              _HELP("Starts the sequence"),                                                                       _HELP("StartTrigger")),
  143.                         InputPortConfig_Void("Pause",                _HELP("Pauses the sequence"),                                                                       _HELP("PauseTrigger")),
  144.                         InputPortConfig_Void("Stop",                 _HELP("Stops the sequence"),                                                                        _HELP("StopTrigger")),
  145.                         InputPortConfig_Void("Precache",             _HELP("Precache keys that start in the first seconds of the animation. (Solves streaming issues)"), _HELP("PrecacheTrigger")),
  146.                         InputPortConfig<bool>("BreakOnStop",         false,                                                                                              _HELP("If set to 'true', stopping the sequence doesn't jump to end.")),
  147.                         InputPortConfig<float>("BlendPosSpeed",      0.0f,                                                                                               _HELP("Speed at which position gets blended into animation.")),
  148.                         InputPortConfig<float>("BlendRotSpeed",      0.0f,                                                                                               _HELP("Speed at which rotation gets blended into animation.")),
  149.                         InputPortConfig<bool>("PerformBlendOut",     false,                                                                                              _HELP("If set to 'true' the cutscene will blend out after it has finished to the new view (please reposition player when 'Started' happens).")),
  150.                         InputPortConfig<float>("StartTime",          0.0f,                                                                                               _HELP("Start time from which the sequence'll begin playing.")),
  151.                         InputPortConfig<float>("PlaySpeed",          1.0f,                                                                                               _HELP("Speed that this sequence plays at. 1.0 = normal speed, 0.5 = half speed, 2.0 = double speed.")),
  152.                         InputPortConfig<float>("JumpToTime",         0.0f,                                                                                               _HELP("Jump to a specific time in the sequence.")),
  153.                         InputPortConfig_Void("TriggerJumpToTime",    _HELP("Trigger the animation to jump to 'JumpToTime' seconds.")),
  154.                         InputPortConfig<int>("seqid_SequenceId",     0,                                                                                                  _HELP("ID of the Sequence"),                                                                                                                    _HELP("SequenceId (DEPRECATED)")),
  155.                         InputPortConfig_Void("TriggerJumpToEnd",     _HELP("Trigger the animation to jump to the end of the sequence.")),
  156.                         { 0 }
  157.                 };
  158.                 static const SOutputPortConfig out_config[] = {
  159.                         OutputPortConfig_Void("Started",      _HELP("Triggered when sequence is started")),
  160.                         OutputPortConfig_Void("Done",         _HELP("Triggered when sequence is stopped [either via StopTrigger or aborted via Code]"),_HELP("Done")),
  161.                         OutputPortConfig_Void("Finished",     _HELP("Triggered when sequence finished normally")),
  162.                         OutputPortConfig_Void("Aborted",      _HELP("Triggered when sequence is aborted (Stopped and BreakOnStop true or via Code)")),
  163.                         OutputPortConfig_Void("SequenceTime", _HELP("Current time of the sequence")),
  164.                         OutputPortConfig_Void("CurrentSpeed", _HELP("Speed at which the sequence is being played")),
  165.                         { 0 }
  166.                 };
  167.                 config.sDescription = _HELP("Plays a Trackview Sequence");
  168.                 config.pInputPorts = in_config;
  169.                 config.pOutputPorts = out_config;
  170.                 config.SetCategory(EFLN_APPROVED);
  171.         }
  172.  
  173.         virtual void ProcessEvent(EFlowEvent event, SActivationInfo* pActInfo)
  174.         {
  175.                 switch (event)
  176.                 {
  177.                 case eFE_Update:
  178.                         {
  179.                                 UpdatePermanentOutputs();
  180.                                 if (!gEnv->pMovieSystem || !m_pSequence)
  181.                                         pActInfo->pGraph->SetRegularlyUpdated(pActInfo->myID, false);
  182.                         }
  183.                         break;
  184.  
  185.                 case eFE_Activate:
  186.                         {
  187.                                 if (IsPortActive(pActInfo, EIP_Stop))
  188.                                 {
  189.                                         bool bWasPlaying = m_playingState == PS_Playing;
  190.                                         const bool bLeaveTime = GetPortBool(pActInfo, EIP_BreakOnStop);
  191.  
  192.                                         StopSequence(pActInfo, false, true, bLeaveTime);
  193.                                         // we trigger manually, as we unregister before the callback happens
  194.  
  195.                                         if (bWasPlaying)
  196.                                         {
  197.                                                 ActivateOutput(pActInfo, EOP_Done, true);    // signal we're done
  198.                                                 ActivateOutput(pActInfo, EOP_Aborted, true); // signal it's been aborted
  199.                                                 NotifyEntities();
  200.                                         }
  201.                                 }
  202.                                 if (IsPortActive(pActInfo, EIP_Start))
  203.                                 {
  204.                                         StartSequence(pActInfo, GetPortFloat(pActInfo, EIP_StartTime), GetPortFloat(pActInfo, EIP_PlaySpeed));
  205.                                 }
  206.                                 if (IsPortActive(pActInfo, EIP_Pause))
  207.                                 {
  208.                                         PauseSequence(pActInfo);
  209.                                 }
  210.                                 if (IsPortActive(pActInfo, EIP_Precache))
  211.                                 {
  212.                                         PrecacheSequence(pActInfo, GetPortFloat(pActInfo, EIP_StartTime));
  213.                                 }
  214.                                 if (IsPortActive(pActInfo, EIP_TriggerJumpToTime))
  215.                                 {
  216.                                         if (gEnv->pMovieSystem && m_pSequence)
  217.                                         {
  218.                                                 SAnimTime time(GetPortFloat(pActInfo, EIP_JumpToTime));
  219.                                                 time = clamp_tpl(time, m_pSequence->GetTimeRange().start, m_pSequence->GetTimeRange().end);
  220.                                                 gEnv->pMovieSystem->SetPlayingTime(m_pSequence, time);
  221.                                         }
  222.                                 }
  223.                                 else if (IsPortActive(pActInfo, EIP_TriggerJumpToEnd))
  224.                                 {
  225.                                         if (gEnv->pMovieSystem && m_pSequence)
  226.                                         {
  227.                                                 SAnimTime endTime = m_pSequence->GetTimeRange().end;
  228.                                                 gEnv->pMovieSystem->SetPlayingTime(m_pSequence, endTime);
  229.                                         }
  230.                                 }
  231.                                 if (IsPortActive(pActInfo, EIP_PlaySpeed))
  232.                                 {
  233.                                         const float playSpeed = GetPortFloat(pActInfo, EIP_PlaySpeed);
  234.                                         if (gEnv->pMovieSystem)
  235.                                         {
  236.                                                 gEnv->pMovieSystem->SetPlayingSpeed(m_pSequence, playSpeed);
  237.                                         }
  238.                                 }
  239.                                 break;
  240.                         }
  241.  
  242.                 case eFE_Initialize:
  243.                         {
  244.                                 StopSequence(pActInfo);
  245.                         }
  246.                         break;
  247.                 }
  248.                 ;
  249.         };
  250.  
  251.         virtual void OnMovieEvent(IMovieListener::EMovieEvent event, IAnimSequence* pSequence)
  252.         {
  253.                 if (pSequence && pSequence == m_pSequence)
  254.                 {
  255.                         if (event == IMovieListener::eMovieEvent_Started)
  256.                         {
  257.                                 m_playingState = PS_Playing;
  258.                         }
  259.                         else if (event == IMovieListener::eMovieEvent_Stopped)
  260.                         {
  261.                                 ActivateOutput(&m_actInfo, EOP_Done, true);
  262.                                 ActivateOutput(&m_actInfo, EOP_Finished, true);
  263.                                 NotifyEntities();
  264.                                 SequenceStopped();
  265.                         }
  266.                         else if (event == IMovieListener::eMovieEvent_Aborted)
  267.                         {
  268.                                 SequenceAborted();
  269.                         }
  270.                         else if (event == IMovieListener::eMovieEvent_Updated)
  271.                         {
  272.                                 m_currentTime = gEnv->pMovieSystem->GetPlayingTime(pSequence).ToFloat();
  273.                                 m_currentSpeed = gEnv->pMovieSystem->GetPlayingSpeed(pSequence);
  274.                         }
  275.                 }
  276.         }
  277.  
  278. protected:
  279.  
  280.         void SequenceAborted()
  281.         {
  282.                 ActivateOutput(&m_actInfo, EOP_Done, true);
  283.                 ActivateOutput(&m_actInfo, EOP_Aborted, true);
  284.                 NotifyEntities();
  285.                 SequenceStopped();
  286.         }
  287.  
  288.         void SequenceStopped()
  289.         {
  290.                 UpdatePermanentOutputs();
  291.                 if (gEnv->pMovieSystem && m_pSequence)
  292.                 {
  293.                         gEnv->pMovieSystem->RemoveMovieListener(m_pSequence, this);
  294.                 }
  295.                 m_pSequence = 0;
  296.                 m_actInfo.pGraph->SetRegularlyUpdated(m_actInfo.myID, false);
  297.                 m_playingState = PS_Stopped;
  298.         }
  299.  
  300.         void PrecacheSequence(SActivationInfo* pActInfo, float startTime = 0.0f)
  301.         {
  302.                 IMovieSystem* movieSys = gEnv->pMovieSystem;
  303.                 if (movieSys)
  304.                 {
  305.                         IAnimSequence* pSequence = movieSys->FindSequence(GetPortString(pActInfo, EIP_Sequence));
  306.                         if (pSequence == NULL)
  307.                                 pSequence = movieSys->FindSequenceById((uint32)GetPortInt(pActInfo, EIP_SequenceId));
  308.                         if (pSequence)
  309.                         {
  310.                                 pSequence->PrecacheData(SAnimTime(startTime));
  311.                         }
  312.                 }
  313.         }
  314.  
  315.         void StopSequence(SActivationInfo* pActInfo, bool bUnRegisterOnly = false, bool bAbort = false, bool bLeaveTime = false)
  316.         {
  317.                 IMovieSystem* const pMovieSystem = gEnv->pMovieSystem;
  318.  
  319.                 if (pMovieSystem && m_pSequence)
  320.                 {
  321.                         // we remove first to NOT get notified!
  322.                         pMovieSystem->RemoveMovieListener(m_pSequence, this);
  323.                         if (!bUnRegisterOnly && pMovieSystem->IsPlaying(m_pSequence))
  324.                         {
  325.                                 if (bAbort) // stops sequence and leaves it at current position
  326.                                 {
  327.                                         pMovieSystem->AbortSequence(m_pSequence, bLeaveTime);
  328.                                 }
  329.                                 else
  330.                                 {
  331.                                         pMovieSystem->StopSequence(m_pSequence);
  332.                                 }
  333.                         }
  334.                         SequenceStopped();
  335.                 }
  336.         }
  337.  
  338.         void UpdatePermanentOutputs()
  339.         {
  340.                 if (gEnv->pMovieSystem && m_pSequence)
  341.                 {
  342.                         const float currentTime = gEnv->pMovieSystem->GetPlayingTime(m_pSequence).ToFloat();
  343.                         const float currentSpeed = gEnv->pMovieSystem->GetPlayingSpeed(m_pSequence);
  344.                         ActivateOutput(&m_actInfo, EOP_SequenceTime, currentTime);
  345.                         ActivateOutput(&m_actInfo, EOP_CurrentSpeed, currentSpeed);
  346.                 }
  347.         }
  348.  
  349.         void StartSequence(SActivationInfo* pActInfo, float curTime = 0.0f, float curSpeed = 1.0f, bool bNotifyStarted = true)
  350.         {
  351.                 IMovieSystem* pMovieSystem = gEnv->pMovieSystem;
  352.                 if (!pMovieSystem)
  353.                 {
  354.                         return;
  355.                 }
  356.  
  357.                 IAnimSequence* pSequence = pMovieSystem->FindSequence(GetPortString(pActInfo, EIP_Sequence));
  358.                 if (pSequence == NULL)
  359.                 {
  360.                         pSequence = pMovieSystem->FindSequenceById((uint32)GetPortInt(pActInfo, EIP_SequenceId));
  361.                 }
  362.  
  363.                 // If sequence was changed in the meantime, stop old sequence first
  364.                 if (m_pSequence && pSequence != m_pSequence)
  365.                 {
  366.                         pMovieSystem->RemoveMovieListener(m_pSequence, this);
  367.                         pMovieSystem->StopSequence(m_pSequence);
  368.                 }
  369.  
  370.                 m_pSequence = pSequence;
  371.  
  372.                 if (m_pSequence)
  373.                 {
  374.                         if (m_pSequence->IsPaused())
  375.                         {
  376.                                 m_pSequence->Resume();
  377.                                 return;
  378.                         }
  379.  
  380.                         m_pSequence->Resume();
  381.                         pMovieSystem->AddMovieListener(m_pSequence, this);
  382.                         pMovieSystem->PlaySequence(m_pSequence, NULL, true, false);
  383.                         pMovieSystem->SetPlayingTime(m_pSequence, SAnimTime(curTime));
  384.                         pMovieSystem->SetPlayingSpeed(m_pSequence, curSpeed);
  385.  
  386.                         // set blend parameters only for tracks with Director Node, having camera track inside
  387.                         IViewSystem* pViewSystem = CCryAction::GetCryAction()->GetIViewSystem();
  388.                         if (pViewSystem && m_pSequence->GetActiveDirector())
  389.                         {
  390.                                 bool bDirectorNodeHasCameraTrack = false;
  391.                                 IAnimNode* pDirectorNode = m_pSequence->GetActiveDirector();
  392.  
  393.                                 if (pDirectorNode)
  394.                                 {
  395.                                         for (int trackId = 0; trackId < pDirectorNode->GetTrackCount(); ++trackId)
  396.                                         {
  397.                                                 IAnimTrack* pTrack = pDirectorNode->GetTrackByIndex(trackId);
  398.                                                 if (pTrack && pTrack->GetParameterType() == eAnimParamType_Camera && pTrack->GetNumKeys() > 0)
  399.                                                 {
  400.                                                         bDirectorNodeHasCameraTrack = true;
  401.                                                         break;
  402.                                                 }
  403.                                         }
  404.                                 }
  405.  
  406.                                 if (bDirectorNodeHasCameraTrack)
  407.                                 {
  408.                                         float blendPosSpeed = GetPortFloat(pActInfo, EIP_BlendPosSpeed);
  409.                                         float blendRotSpeed = GetPortFloat(pActInfo, EIP_BlendRotSpeed);
  410.                                         bool performBlendOut = GetPortBool(pActInfo, EIP_PerformBlendOut);
  411.                                         pViewSystem->SetBlendParams(blendPosSpeed, blendRotSpeed, performBlendOut);
  412.                                 }
  413.                         }
  414.  
  415.                         if (bNotifyStarted)
  416.                         {
  417.                                 ActivateOutput(pActInfo, EOP_Started, true);
  418.                         }
  419.  
  420.                         pActInfo->pGraph->SetRegularlyUpdated(pActInfo->myID, true);
  421.                 }
  422.                 else
  423.                 {
  424.                         // sequence was not found -> hint
  425.                         GameWarning("[flow] Animations:PlaySequence: Sequence \"%s\" not found", GetPortString(pActInfo, 0).c_str());
  426.                         // maybe we should trigger the output, but if sequence is not found this should be an error
  427.                 }
  428.  
  429.                 NotifyEntities();
  430.                 // this can happens when a timedemo is being recorded and the sequence is flagged as CUTSCENE
  431.                 if (m_pSequence && m_playingState == PS_Playing && !pMovieSystem->IsPlaying(m_pSequence))
  432.                 {
  433.                         SequenceAborted();
  434.                 }
  435.  
  436.                 if (CCryActionCVars::Get().g_disableSequencePlayback)
  437.                 {
  438.                         if (gEnv->pMovieSystem && m_pSequence)
  439.                         {
  440.                                 SAnimTime endTime = m_pSequence->GetTimeRange().end;
  441.                                 gEnv->pMovieSystem->SetPlayingTime(m_pSequence, endTime);
  442.                         }
  443.                 }
  444.         }
  445.  
  446.         void PauseSequence(SActivationInfo* pActInfo)
  447.         {
  448.                 if (m_playingState != PS_Playing)
  449.                 {
  450.                         return;
  451.                 }
  452.  
  453.                 IMovieSystem* pMovieSys = gEnv->pMovieSystem;
  454.                 if (!pMovieSys)
  455.                 {
  456.                         return;
  457.                 }
  458.  
  459.                 if (m_pSequence)
  460.                 {
  461.                         m_pSequence->Pause();
  462.                 }
  463.         }
  464.  
  465.         void NotifyEntityScript(IEntity* pEntity)
  466.         {
  467.                 IScriptTable* pEntityScript = pEntity->GetScriptTable();
  468.                 if (pEntityScript)
  469.                 {
  470.                         if (m_playingState == PS_Playing && pEntityScript->HaveValue("OnSequenceStart"))
  471.                         {
  472.                                 Script::CallMethod(pEntityScript, "OnSequenceStart");
  473.                         }
  474.                         else if (m_playingState == PS_Stopped && pEntityScript->HaveValue("OnSequenceStop"))
  475.                         {
  476.                                 Script::CallMethod(pEntityScript, "OnSequenceStop");
  477.                         }
  478.                 }
  479.         }
  480.  
  481.         void NotifyEntities()
  482.         {
  483.                 IMovieSystem* pMovieSystem = gEnv->pMovieSystem;
  484.                 if (!pMovieSystem || !m_pSequence)
  485.                 {
  486.                         return;
  487.                 }
  488.  
  489.                 int nodeCount = m_pSequence->GetNodeCount();
  490.                 for (int i = 0; i < nodeCount; ++i)
  491.                 {
  492.                         IAnimNode* pNode = m_pSequence->GetNode(i);
  493.                         if (pNode)
  494.                         {
  495.                                 IAnimEntityNode* pAnimEntityNode = pNode->QueryEntityNodeInterface();
  496.                                 if (pAnimEntityNode)
  497.                                 {
  498.                                         IEntity* pEntity = pAnimEntityNode->GetEntity();
  499.                                         if (pEntity)
  500.                                         {
  501.                                                 NotifyEntityScript(pEntity);
  502.                                         }
  503.  
  504.                                         if (EntityGUID* guid = pAnimEntityNode->GetEntityGuid())
  505.                                         {
  506.                                                 EntityId id = gEnv->pEntitySystem->FindEntityByGuid(*guid);
  507.                                                 if (id != 0)
  508.                                                 {
  509.                                                         IEntity* pEntity2 = gEnv->pEntitySystem->GetEntity(id);
  510.                                                         if (pEntity2)
  511.                                                         {
  512.                                                                 NotifyEntityScript(pEntity2);
  513.                                                         }
  514.                                                 }
  515.                                         }
  516.                                 }
  517.                         }
  518.                 }
  519.         }
  520. };
  521.  
  522. REGISTER_FLOW_NODE("Animations:PlaySequence", CPlaySequence_Node);
  523.  
downloadPlaySequenceNode.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