Leviathan  0.8.0.0
Leviathan game engine
StateInterpolator.h
Go to the documentation of this file.
1 // Leviathan Game Engine
2 // Copyright (c) 2012-2017 Henri Hyyryläinen
3 #pragma once
4 #include "Define.h"
5 // ------------------------------------ //
6 #include "StateHolder.h"
7 
8 namespace Leviathan{
9 
11 public:
12 
20  template<class StateT, class ComponentT>
21  static std::tuple<bool, StateT> Interpolate(const StateHolder<StateT> &stateholder,
22  ObjectID entity, ComponentT* entitycomponent, int currenttick, int timeintick)
23  {
24  // TODO: should this be stored in the component?
25  auto* entitysStates = stateholder.GetEntityStates(entity);
26 
27  if(!entitysStates){
28  // Probably shouldn't throw here to make the code that uses this simpler
29  entitycomponent->StateMarked = false;
30  return std::make_tuple(false, StateT());
31  //throw Leviathan::InvalidState("Interpolated entity has no states in StateHolder");
32  }
33 
34  // Find interpolation start spot //
35  if(!entitycomponent->InterpolatingStartState ||
36  !entitysStates->IsStateValid(entitycomponent->InterpolatingStartState))
37  {
38  entitycomponent->InterpolatingEndState = nullptr;
39  entitycomponent->InterpolatingStartState = entitysStates->GetOldest();
40 
41  if(!entitycomponent->InterpolatingStartState){
42 
43  // No states to interpolate //
44  entitycomponent->StateMarked = false;
45  return std::make_tuple(false, StateT());
46  }
47 
48  // Adjust clock if the initial tick has been changed //
49  if(entitycomponent->InterpolatingStartTime != 0.f){
50 
51  if(entitycomponent->InterpolatingRemoteStartTick !=
52  entitycomponent->InterpolatingStartState->TickNumber)
53  {
54  AdjustClock(entitycomponent);
55  }
56  }
57  }
58 
59  // We cast the time from int64_t to float (visual studio was giving warnings about
60  // losing data here)
61  const float currentTime = static_cast<float>((currenttick * TICKSPEED) + timeintick);
62 
63  // Find ending state //
64  if(!entitycomponent->InterpolatingEndState){
65 
66  // TODO: should we only allow TickNumber + 2 states to be interpolated to
67  // as that is the way source engine does it and would avoid jitter if we miss
68  // one state packet later (use INTERPOLATION_TIME / TICKSPEED ?)
69  entitycomponent->InterpolatingEndState = entitysStates->GetMatchingOrNewer(
70  entitycomponent->InterpolatingStartState->TickNumber + 1);
71 
72  if(!entitycomponent->InterpolatingEndState){
73 
74  // No ending state found //
75  entitycomponent->StateMarked = false;
76  // Only one state should allow interpolating to the one available state
77  // with the same function so we return the first state here
78  //return std::make_tuple(false, StateT());
79  return std::make_tuple(true, *entitycomponent->InterpolatingStartState);
80  }
81 
82  // Initialize the remote time counter if this is the first time we start
83  // interpolating
84  if(entitycomponent->InterpolatingStartTime == 0.f){
85  entitycomponent->InterpolatingStartTime = currentTime;
86  entitycomponent->InterpolatingRemoteStartTick =
87  entitycomponent->InterpolatingStartState->TickNumber;
88  }
89  }
90 
91  const float passed = currentTime - entitycomponent->InterpolatingStartTime;
92 
93  // TODO: do we need to check for currentTime < 0?
94 
95  if(passed <= EPSILON)
96  return std::make_tuple(true, *entitycomponent->InterpolatingStartState);
97 
98  // Duration is clamped to INTERPOLATION_TIME to make entities
99  // that have stopped moving not take a ridiculously long time
100  // to move to their new positions
101  const auto duration = std::min((
102  entitycomponent->InterpolatingEndState->TickNumber -
103  entitycomponent->InterpolatingStartState->TickNumber) * TICKSPEED,
105 
106  if(passed == duration)
107  return std::make_tuple(true, *entitycomponent->InterpolatingEndState);
108 
109  // Check for having finished interpolating //
110  if(passed > duration){
111 
112  entitycomponent->InterpolatingStartState = entitycomponent->InterpolatingEndState;
113  entitycomponent->InterpolatingEndState = nullptr;
114 
115  AdjustClock(entitycomponent);
116 
117  // We need to recurse to get the correct interpolation state //
118  return Interpolate(stateholder, entity, entitycomponent, currenttick, timeintick);
119  }
120 
121  const float progress = passed / duration;
122 
123  return std::make_tuple(true, entitycomponent->InterpolatingStartState->Interpolate(
124  *entitycomponent->InterpolatingEndState, progress));
125  }
126 
127  template<class ComponentT>
128  static void AdjustClock(ComponentT &entitycomponent)
129  {
130  const auto difference = entitycomponent->InterpolatingStartState->TickNumber
131  - entitycomponent->InterpolatingRemoteStartTick;
132 
133  entitycomponent->InterpolatingStartTime += difference * TICKSPEED;
134  entitycomponent->InterpolatingRemoteStartTick =
135  entitycomponent->InterpolatingStartState->TickNumber;
136  }
137 
138 };
139 
140 
141 
142 
143 }
144 
int32_t ObjectID
Definition: EntityCommon.h:11
constexpr auto INTERPOLATION_TIME
Definition: Define.h:25
constexpr auto TICKSPEED
Number of milliseconds between engine and world ticks.
Definition: Define.h:22
static std::tuple< bool, StateT > Interpolate(const StateHolder< StateT > &stateholder, ObjectID entity, ComponentT *entitycomponent, int currenttick, int timeintick)
Interpolates states for component.
static void AdjustClock(ComponentT &entitycomponent)
constexpr float EPSILON
Definition: Define.h:63
ObjectsComponentStates< StateT > const * GetEntityStates(ObjectID id) const
Returns a pointer to entity's states if they exist.
Definition: StateHolder.h:264
Holds state objects of type for quick access by ObjectID.
Definition: GameWorld.h:31
The access mask controls which registered functions and classes a script sees.
Definition: GameModule.h:12