Cafu Engine
Interpolator.hpp
1 /*
2 Cafu Engine, http://www.cafu.de/
3 Copyright (c) Carsten Fuchs and other contributors.
4 This project is licensed under the terms of the MIT license.
5 */
6 
7 #ifndef CAFU_GAMESYS_INTERPOLATOR_HPP_INCLUDED
8 #define CAFU_GAMESYS_INTERPOLATOR_HPP_INCLUDED
9 
10 #include "Math3D/Quaternion.hpp"
11 #include "Variables.hpp"
12 
13 
14 namespace cf
15 {
16  namespace GameSys
17  {
18  /// A common base class for "approximators" (interpolators and extrapolators), so that
19  /// approximators of different types can easily be managed together.
20  ///
21  /// ApproxBaseT%s are used in ComponentBaseT in order to advance values over client
22  /// frames in order to bridge the larger intervals between server frames, especially
23  /// the origin and the orientation of movable NPC entities.
25  {
26  public:
27 
28  virtual ~ApproxBaseT() { }
29 
30  /// Returns the variable that this is an interpolator for.
31  virtual cf::TypeSys::VarBaseT* GetVar() const = 0;
32 
33  /// Re-initializes this interpolator to the variable's current value.
34  virtual void ReInit() = 0;
35 
36  /// Updates the interpolator to interpolate from the current interpolated value to
37  /// the current value of the variable (the new target value).
38  virtual void UpdateTargetValue() = 0;
39 
40  /// Updates the interpolator to account for the new target value after the local
41  /// human player's reprediction has been performed.
42  virtual void UpdateAfterReprediction() = 0;
43 
44  /// Updates the interpolator to account for the new target value after the local
45  /// human player's prediction has been performed.
46  virtual void UpdateAfterPrediction() = 0;
47 
48  /// Advances the interpolation over the given time.
49  virtual void AdvanceTime(float Time) = 0;
50 
51  /// Assigns the interpolation's current value to the variable.
52  virtual void SetCurrentValue() = 0;
53 
54  /// Assigns the interpolation's target value to the variable.
55  virtual void SetTargetValue() = 0;
56  };
57 
58 
59  /// Linearly interpolates a value over a period of time.
60  /// The course of the interpolation is adapted/updated whenever a new reference value is set.
61  /// This is mostly intended for game entities on clients, where we interpolate from the "current" value
62  /// towards the reference value of the most recent server update. If the subsequent server update happens
63  /// to arrive later than the one before it, our interpolation may (briefly) turn into an extrapolation
64  /// for bridging the gap.
65  template<class T>
67  {
68  public:
69 
71  : m_Value(v),
72  m_A(v.Get()),
73  m_B(m_A),
74  m_Total(0.0f),
75  m_Time(0.0f)
76  {
77  }
78 
79  cf::TypeSys::VarBaseT* GetVar() const override
80  {
81  return &m_Value;
82  }
83 
84  void ReInit() override
85  {
86  m_A = m_Value.Get();
87  m_B = m_A;
88  m_Total = 0.0f;
89  m_Time = 0.0f;
90  }
91 
92  void UpdateTargetValue() override
93  {
94  m_A = GetCurrentValue();
95  m_B = m_Value.Get();
96  m_Total = m_Time;
97  m_Time = 0.0f;
98  }
99 
100  void UpdateAfterReprediction() override
101  {
102  m_B = m_Value.Get();
103  }
104 
105  void UpdateAfterPrediction() override
106  {
107  // The prediction moved the player from m_B to m_Value.Get().
108  m_A = m_A + (m_Value.Get() - m_B);
109  m_B = m_Value.Get();
110  }
111 
112  void AdvanceTime(float Time) override
113  {
114  m_Time += Time;
115  }
116 
117  void SetCurrentValue() override
118  {
119  m_Value.Set(GetCurrentValue());
120  }
121 
122  void SetTargetValue() override
123  {
124  m_Value.Set(m_B);
125  }
126 
127 
128  private:
129 
130  // bool CanContinue(const T& DeltaY) const;
131 
132  T GetCurrentValue() const
133  {
134  if (m_Total < 0.001f) return m_B;
135 
136  const float f = m_Time / m_Total;
137 
138  return m_A * (1.0f - f) + m_B * f;
139  }
140 
141 
142  cf::TypeSys::VarT<T>& m_Value; ///< The value that is interpolated.
143  T m_A; ///< The start value that we're interpolating from.
144  T m_B; ///< The target value that we're interpolating to.
145  float m_Total; ///< The total time that the interpolation is expected to take from A to B.
146  float m_Time; ///< The actual time that has passed since we started from A.
147  };
148 
149 
150 #if 0
151  template<class T> inline bool VarInterpolatorT<T>::CanContinue(const T& DeltaY) const
152  {
153  return DeltaY < 128;
154  }
155 
156  template<> inline bool VarInterpolatorT<Vector2fT>::CanContinue(const Vector2fT& DeltaY) const
157  {
158  return length(DeltaY) < 128;
159  }
160 
161  template<> inline bool VarInterpolatorT<Vector3fT>::CanContinue(const Vector3fT& DeltaY) const
162  {
163  return length(DeltaY) < 128;
164  }
165 
166  template<> inline bool VarInterpolatorT<Vector3dT>::CanContinue(const Vector3dT& DeltaY) const
167  {
168  return length(DeltaY) < 128;
169  }
170 #endif
171 
172 
173  /// This class is like VarInterpolatorT, but for Vector3fT%s that represent quaternions.
174  class VarSlerpT : public ApproxBaseT
175  {
176  public:
177 
179  : m_Value(v),
181  m_B(m_A),
182  m_Total(0.0f),
183  m_Time(0.0f)
184  {
185  assert(v.HasFlag("IsQuat"));
186  }
187 
188  cf::TypeSys::VarBaseT* GetVar() const override
189  {
190  return &m_Value;
191  }
192 
193  void ReInit() override
194  {
195  m_A = cf::math::QuaternionfT::FromXYZ(m_Value.Get());
196  m_B = m_A;
197  m_Total = 0.0f;
198  m_Time = 0.0f;
199  }
200 
201  void UpdateTargetValue() override
202  {
203  m_A = m_Total > 0.001f ? slerp(m_A, m_B, m_Time / m_Total) : m_B;
204  m_B = cf::math::QuaternionfT::FromXYZ(m_Value.Get());
205  m_Total = m_Time;
206  m_Time = 0.0f;
207  }
208 
209  void UpdateAfterReprediction() override
210  {
211  m_B = cf::math::QuaternionfT::FromXYZ(m_Value.Get());
212  }
213 
214  void UpdateAfterPrediction() override
215  {
217 
218  // TODO: This method should be properly implemented before use!
219  assert(false);
220 
221  // The prediction rotated the player from m_B to V.
222  // m_A = m_A + (V - m_B); // Well...
223  m_B = V;
224  }
225 
226  void AdvanceTime(float Time) override
227  {
228  m_Time += Time;
229  }
230 
231  void SetCurrentValue() override
232  {
233  m_Value.Set(m_Total > 0.001f ? slerp(m_A, m_B, m_Time / m_Total).GetXYZ() : m_B.GetXYZ());
234  }
235 
236  void SetTargetValue() override
237  {
238  m_Value.Set(m_B.GetXYZ());
239  }
240 
241 
242  private:
243 
244  cf::TypeSys::VarT<Vector3fT>& m_Value; ///< The value that is interpolated.
245  cf::math::QuaternionfT m_A; ///< The start value that we're interpolating from.
246  cf::math::QuaternionfT m_B; ///< The target value that we're interpolating to.
247  float m_Total; ///< The total time that the interpolation is expected to take from A to B.
248  float m_Time; ///< The actual time that has passed since we started from A.
249  };
250 
251 
252  /// This visitor is used to obtain an ApproxBaseT instance for a variable.
254  {
255  public:
256 
259 
260  ApproxBaseT* TransferApprox();
261 
262  void visit(cf::TypeSys::VarT<float>& Var);
263  void visit(cf::TypeSys::VarT<double>& Var);
264  void visit(cf::TypeSys::VarT<int>& Var);
265  void visit(cf::TypeSys::VarT<unsigned int>& Var);
266  void visit(cf::TypeSys::VarT<uint16_t>& Var);
267  void visit(cf::TypeSys::VarT<uint8_t>& Var);
268  void visit(cf::TypeSys::VarT<bool>& Var);
269  void visit(cf::TypeSys::VarT<std::string>& Var);
270  void visit(cf::TypeSys::VarT<Vector2fT>& Var);
271  void visit(cf::TypeSys::VarT<Vector3fT>& Var);
272  void visit(cf::TypeSys::VarT<Vector3dT>& Var);
273  void visit(cf::TypeSys::VarT<BoundingBox3dT>& Var);
274  void visit(cf::TypeSys::VarArrayT<uint32_t>& Var);
275  void visit(cf::TypeSys::VarArrayT<uint16_t>& Var);
276  void visit(cf::TypeSys::VarArrayT<uint8_t>& Var);
277  void visit(cf::TypeSys::VarArrayT<std::string>& Var);
278 
279 
280  private:
281 
282  ApproxBaseT* m_Approx;
283  };
284 
285 
286 #if 0
287  /// Linearly interpolates a value over a period of time.
288  /// The course of the interpolation is adapted/updated whenever a new reference value is set.
289  /// This is mostly intended for game entities on clients, where we interpolate from the "current" value
290  /// towards the reference value of the most recent server update. If the subsequent server update happens
291  /// to arrive later than the one before it, our interpolation may (briefly) turn into an extrapolation
292  /// for bridging the gap.
293  template<class T>
294  class InterpolatorT : public ApproxBaseT
295  {
296  public:
297 
298  InterpolatorT(T& v)
299  : m_Value(v),
300  m_LastValue(v),
301  m_Gradient(T()), // If T is float, this initializes m_Gradient with 0.0f.
302  m_ExtTime(0.0f)
303  {
304  }
305 
306  void ReInit() override
307  {
308  m_LastValue = m_Value;
309  m_Gradient = T();
310  m_ExtTime = 0.0f;
311  }
312 
313  void UpdateTargetValue() override
314  {
315  const T NewRef = m_Value;
316  m_Value = m_LastValue;
317 
318  UpdateRef(NewRef);
319  }
320 
321  void AdvanceTime(float Time) override
322  {
323  m_Value += m_Gradient*Time;
324  m_ExtTime += Time;
325 
326  m_LastValue = m_Value;
327  }
328 
329 
330  private:
331 
332  bool CanContinue(const T& DeltaY) const
333  {
334  return length(DeltaY) < 5000.0f;
335  }
336 
337  /// Sets a new reference value: the value that we should interpolate to.
338  void UpdateRef(const T& Ref)
339  {
340  const T DeltaY = Ref - m_Value;
341 
342  if (m_ExtTime < 0.0001f || !CanContinue(DeltaY))
343  {
344  m_Value = Ref;
345  m_LastValue = Ref;
346  m_Gradient = T();
347  }
348  else
349  {
350  m_Gradient = DeltaY/m_ExtTime;
351  }
352 
353  m_ExtTime = 0.0f;
354  }
355 
356 
357  T& m_Value; ///< The value that is interpolated.
358  T m_LastValue; ///< The last interpolated value. Normally m_LastValue == m_Value.
359  T m_Gradient; ///< The change in the value over time.
360  float m_ExtTime; ///< The time we've been interpolating since the reference value was last set.
361  };
362 
363 
364  /// Linearly extrapolates a value over a period of time.
365  /// The course of the extrapolation is adapted/updated whenever a new reference value is set.
366  /// This is mostly intended for game entities on clients, where we try to extrapolate values
367  /// close to where we expect the next server update will bring them to.
368  ///
369  /// Unfortunately, extrapolation is less useful and worthwhile than it initially seems, because
370  /// when it is applied to entity origins, all kinds of unwanted side effects can happen: items
371  /// that fall to the floor penetrate the ground plane, then resurface; the wings of closing doors
372  /// bump into each other, then retract; lifts overshoot their halting position; grenades that are
373  /// deflected by walls briefly vanish into them; etc.
374  template<class T>
375  class ExtrapolatorT : public ApproxBaseT
376  {
377  public:
378 
379  ExtrapolatorT(T& v)
380  : m_Value(v),
381  m_LastValue(v),
382  m_Gradient(T()), // If T is float, this initializes m_Gradient with 0.0f.
383  m_LastRef(v),
384  m_ExtTime(0.0f)
385  {
386  }
387 
388  /// Used to re-initialize this extrapolator at the current value.
389  void ReInit() override
390  {
391  m_LastValue = m_Value;
392  m_Gradient = T();
393  m_LastRef = m_Value;
394  m_ExtTime = 0.0f;
395  }
396 
397  /// The user calls this method in order to let the extrapolator know that the extrapolated value was changed externally.
398  void UpdateTargetValue() override
399  {
400  const T NewRef = m_Value;
401  m_Value = m_LastValue;
402 
403  UpdateRef(NewRef);
404  }
405 
406  /// Advances the extrapolation over the given time.
407  void AdvanceTime(float Time) override
408  {
409  m_Value += m_Gradient*Time;
410  m_ExtTime += Time;
411 
412  m_LastValue = m_Value;
413  }
414 
415 
416  private:
417 
418  bool CanContinue(const T& DeltaY) const
419  {
420  return length(DeltaY) < 5000.0f;
421  }
422 
423  /// Sets a new reference value: the value that the extrapolated value currently "should have".
424  void UpdateRef(const T& Ref)
425  {
426  /// m_Value was extrapolated starting from its own value at the time the reference value was last set,
427  /// using the gradient m_Gradient that was applied over the the extrapolation time m_ExtTime.
428  /// If the new reference value Ref now turns out to be different from m_Value, it used a gradient TrueG
429  /// other than the expected gradient from the previous m_LastRef to m_LastRef.
430  /// Considering that the value changed from m_LastRef to Ref, and assuming that this time TrueG will remain
431  /// constant over the next extrapolation period, the new expected value is Ref + (Ref - m_LastRef), and the
432  /// change that is required to get from m_Value to the new expected value is 2*Ref - m_LastRef - m_Value.
433  const T DeltaY = Ref*2 - m_LastRef - m_Value;
434 
435  if (m_ExtTime < 0.0001f || !CanContinue(DeltaY))
436  {
437  m_Value = Ref;
438  m_LastValue = Ref;
439  m_Gradient = T();
440  }
441  else
442  {
443  m_Gradient = DeltaY/m_ExtTime;
444  }
445 
446  m_LastRef = Ref;
447  m_ExtTime = 0.0f;
448  }
449 
450 
451  T& m_Value; ///< The value that is extrapolated.
452  T m_LastValue; ///< The last extrapolated value. Normally m_LastValue == m_Value.
453  T m_Gradient; ///< The change in the value over time.
454  T m_LastRef; ///< The last reference value (*not* necessarily the value from which the extrapolation started).
455  float m_ExtTime; ///< The time we've been extrapolating since the reference value was last set.
456  };
457 #endif
458  }
459 }
460 
461 #endif
This is the base class for the visitors of VarTs.
Definition: Variables.hpp:266
void UpdateAfterReprediction() override
Updates the interpolator to account for the new target value after the local human player's repredict...
Definition: Interpolator.hpp:100
void UpdateTargetValue() override
Updates the interpolator to interpolate from the current interpolated value to the current value of t...
Definition: Interpolator.hpp:92
cf::TypeSys::VarBaseT * GetVar() const override
Returns the variable that this is an interpolator for.
Definition: Interpolator.hpp:79
virtual void SetTargetValue()=0
Assigns the interpolation's target value to the variable.
static QuaternionT FromXYZ(const Vector3T< float > &Vec)
Constructs a quaternion from the first three components (x, y, z) of a unit quaternion.
Definition: Quaternion.hpp:61
This is a "wrapper" around a normal C++ variable specifically of type ArrayT<T>.
Definition: SetCompVar.hpp:16
void SetTargetValue() override
Assigns the interpolation's target value to the variable.
Definition: Interpolator.hpp:122
This class is like VarInterpolatorT, but for Vector3fTs that represent quaternions.
Definition: Interpolator.hpp:174
This visitor is used to obtain an ApproxBaseT instance for a variable.
Definition: Interpolator.hpp:253
virtual void UpdateAfterReprediction()=0
Updates the interpolator to account for the new target value after the local human player's repredict...
This is a "wrapper" around a normal C++ variable.
Definition: SetCompVar.hpp:15
void SetCurrentValue() override
Assigns the interpolation's current value to the variable.
Definition: Interpolator.hpp:231
void SetTargetValue() override
Assigns the interpolation's target value to the variable.
Definition: Interpolator.hpp:236
This class represents a 2-dimensional vector.
Definition: Vector2.hpp:21
void UpdateAfterReprediction() override
Updates the interpolator to account for the new target value after the local human player's repredict...
Definition: Interpolator.hpp:209
void UpdateAfterPrediction() override
Updates the interpolator to account for the new target value after the local human player's predictio...
Definition: Interpolator.hpp:105
void SetCurrentValue() override
Assigns the interpolation's current value to the variable.
Definition: Interpolator.hpp:117
virtual void AdvanceTime(float Time)=0
Advances the interpolation over the given time.
cf::TypeSys::VarBaseT * GetVar() const override
Returns the variable that this is an interpolator for.
Definition: Interpolator.hpp:188
void UpdateAfterPrediction() override
Updates the interpolator to account for the new target value after the local human player's predictio...
Definition: Interpolator.hpp:214
void AdvanceTime(float Time) override
Advances the interpolation over the given time.
Definition: Interpolator.hpp:226
Vector3T< T > GetXYZ() const
Returns the x, y and z components as a Vector3T<T>.
Definition: Quaternion.hpp:81
virtual void UpdateAfterPrediction()=0
Updates the interpolator to account for the new target value after the local human player's predictio...
A common base class for "approximators" (interpolators and extrapolators), so that approximators of d...
Definition: Interpolator.hpp:24
void ReInit() override
Re-initializes this interpolator to the variable's current value.
Definition: Interpolator.hpp:193
virtual void UpdateTargetValue()=0
Updates the interpolator to interpolate from the current interpolated value to the current value of t...
void ReInit() override
Re-initializes this interpolator to the variable's current value.
Definition: Interpolator.hpp:84
This is the common base class for the VarT classes.
Definition: Variables.hpp:113
virtual void ReInit()=0
Re-initializes this interpolator to the variable's current value.
Linearly interpolates a value over a period of time.
Definition: Interpolator.hpp:66
void AdvanceTime(float Time) override
Advances the interpolation over the given time.
Definition: Interpolator.hpp:112
virtual void SetCurrentValue()=0
Assigns the interpolation's current value to the variable.
virtual cf::TypeSys::VarBaseT * GetVar() const =0
Returns the variable that this is an interpolator for.
const T & Get() const
Returns the value of this variable.
Definition: Variables.hpp:182
void UpdateTargetValue() override
Updates the interpolator to interpolate from the current interpolated value to the current value of t...
Definition: Interpolator.hpp:201