11using System ;
22using System . Collections . Generic ;
3+ using System . Linq ;
34using UnityEngine ;
45
56namespace Unity . Netcode
@@ -11,7 +12,7 @@ namespace Unity.Netcode
1112 /// <typeparam name="T">The type of interpolated value</typeparam>
1213 public abstract class BufferedLinearInterpolator < T > where T : struct
1314 {
14- internal float MaxInterpolationBound = 3 .0f;
15+ internal float MaxInterpolationBound = 1 .0f;
1516 protected internal struct BufferedItem
1617 {
1718 public T Item ;
@@ -31,14 +32,7 @@ public BufferedItem(T item, double timeSent)
3132
3233 private const double k_SmallValue = 9.999999439624929E-11 ; // copied from Vector3's equal operator
3334
34- protected internal T m_InterpStartValue ;
35- protected internal T m_CurrentInterpValue ;
36- protected internal T m_InterpEndValue ;
37-
38- private double m_EndTimeConsumed ;
39- private double m_StartTimeConsumed ;
40-
41- protected internal readonly List < BufferedItem > m_Buffer = new List < BufferedItem > ( k_BufferCountLimit ) ;
35+ protected internal readonly Queue < BufferedItem > m_Buffer = new Queue < BufferedItem > ( k_BufferCountLimit ) ;
4236
4337
4438
@@ -71,6 +65,7 @@ public BufferedItem(T item, double timeSent)
7165 private BufferedItem m_LastBufferedItemReceived ;
7266 private int m_NbItemsReceivedThisFrame ;
7367
68+ protected internal T m_CurrentInterpValue ;
7469 private int m_LifetimeConsumedCount ;
7570
7671 private bool InvalidState => m_Buffer . Count == 0 && m_LifetimeConsumedCount == 0 ;
@@ -96,8 +91,12 @@ internal void ConvertTransformSpace(Transform transform, bool inLocalSpace)
9691 public void Clear ( )
9792 {
9893 m_Buffer . Clear ( ) ;
99- m_EndTimeConsumed = 0.0d ;
100- m_StartTimeConsumed = 0.0d ;
94+ m_CurrentInterpValue = default ;
95+ InterpolateState = new CurrentState ( )
96+ {
97+ CurrentValue = default ,
98+ LerpT = 0.0000001f ,
99+ } ;
101100 }
102101
103102 /// <summary>
@@ -108,18 +107,20 @@ public void Clear()
108107 public void ResetTo ( T targetValue , double serverTime )
109108 {
110109 m_LifetimeConsumedCount = 1 ;
111- m_InterpStartValue = targetValue ;
112- m_InterpEndValue = targetValue ;
113- m_CurrentInterpValue = targetValue ;
114110 m_Buffer . Clear ( ) ;
115- m_EndTimeConsumed = 0.0d ;
116- m_StartTimeConsumed = 0.0d ;
117-
111+ m_Name = GetType ( ) . Name ;
112+ m_CurrentInterpValue = targetValue ;
113+ InterpolateState = new CurrentState ( )
114+ {
115+ CurrentValue = targetValue ,
116+ LerpT = 0.0000001f ,
117+ } ;
118118 Update ( 0 , serverTime , serverTime ) ;
119119 }
120120
121121 // todo if I have value 1, 2, 3 and I'm treating 1 to 3, I shouldn't interpolate between 1 and 3, I should interpolate from 1 to 2, then from 2 to 3 to get the best path
122- private void TryConsumeFromBuffer ( double renderTime , double serverTime )
122+ #if OLDSTUFF
123+ private void TryConsumeFromBufferLDK ( double renderTime , double serverTime )
123124 {
124125 int consumedCount = 0 ;
125126 // only consume if we're ready
@@ -152,15 +153,21 @@ private void TryConsumeFromBuffer(double renderTime, double serverTime)
152153 else if ( consumedCount == 0 )
153154 {
154155 // Interpolating to new value, end becomes start. We then look in our buffer for a new end.
155- m_StartTimeConsumed = m_EndTimeConsumed ;
156- m_InterpStartValue = m_InterpEndValue ;
156+
157+ // !!!! This does not account for gaps between values !!!
158+ // if last entry is > several ticks then the range is going to be very large!
159+ m_StartTimeConsumed = renderTime ;
160+ m_InterpStartValue = m_CurrentInterpValue ;
157161 }
158162
159- if ( bufferedValue . TimeSent > m_EndTimeConsumed )
163+ if ( ( bufferedValue . TimeSent - m_StartTimeConsumed ) >= k_TickFrequency )
160164 {
161165 itemToInterpolateTo = bufferedValue ;
162166 m_EndTimeConsumed = bufferedValue . TimeSent ;
163167 m_InterpEndValue = bufferedValue . Item ;
168+ m_Buffer . RemoveAt ( i ) ;
169+ m_LifetimeConsumedCount ++ ;
170+ break ;
164171 }
165172 }
166173
@@ -171,6 +178,80 @@ private void TryConsumeFromBuffer(double renderTime, double serverTime)
171178 }
172179 }
173180 }
181+ #endif
182+ private string m_Name ;
183+
184+ internal struct CurrentState
185+ {
186+ public BufferedItem ? Start ;
187+
188+ public BufferedItem ? End ;
189+
190+ public double RelativeTime ;
191+
192+ public T CurrentValue ;
193+
194+ public float LerpT ;
195+ }
196+
197+ internal CurrentState InterpolateState ;
198+
199+ private void TryConsumeFromBuffer ( double renderTime , double serverTime )
200+ {
201+ // If we don't have our initial buffered item/starting point or our end point or the end point's time sent is less than the
202+ // render time
203+ if ( ! InterpolateState . Start . HasValue || ! InterpolateState . End . HasValue || InterpolateState . End . Value . TimeSent < renderTime )
204+ {
205+ BufferedItem ? previousItem = null ;
206+ while ( m_Buffer . TryPeek ( out BufferedItem potentialItem ) )
207+ {
208+ if ( previousItem . HasValue && previousItem . Value . TimeSent == potentialItem . TimeSent )
209+ {
210+ break ;
211+ }
212+
213+ if ( potentialItem . TimeSent <= serverTime )
214+ {
215+ // We want to initialize and then always set the end
216+ if ( ! InterpolateState . Start . HasValue )
217+ {
218+ if ( m_Buffer . TryDequeue ( out BufferedItem start ) )
219+ {
220+ InterpolateState . Start = start ;
221+ InterpolateState . RelativeTime = InterpolateState . Start . Value . TimeSent ;
222+ InterpolateState . CurrentValue = start . Item ;
223+ InterpolateState . LerpT = 0.0f ;
224+ InterpolateState . Start = start ;
225+ }
226+ }
227+ else if ( ! InterpolateState . End . HasValue || InterpolateState . End . Value . TimeSent < potentialItem . TimeSent )
228+ {
229+ if ( m_Buffer . TryDequeue ( out BufferedItem end ) )
230+ {
231+ if ( InterpolateState . End . HasValue )
232+ {
233+ InterpolateState . Start = InterpolateState . End ;
234+ //m_CurrentState.RelativeTime = m_CurrentState.End.Value.TimeSent;
235+ }
236+ InterpolateState . End = end ;
237+ InterpolateState . LerpT = 0.0f ;
238+ m_LifetimeConsumedCount ++ ;
239+ break ;
240+ }
241+ }
242+ else
243+ {
244+ break ;
245+ }
246+ }
247+ if ( ! InterpolateState . Start . HasValue )
248+ {
249+ break ;
250+ }
251+ previousItem = potentialItem ;
252+ }
253+ }
254+ }
174255
175256 /// <summary>
176257 /// Convenience version of 'Update' mainly for testing
@@ -201,48 +282,33 @@ public T Update(float deltaTime, double renderTime, double serverTime)
201282 throw new InvalidOperationException ( "trying to update interpolator when no data has been added to it yet" ) ;
202283 }
203284
204- // Interpolation example to understand the math below
205- // 4 4.5 6 6.5
206- // | | | |
207- // A render B Server
208-
209- if ( m_LifetimeConsumedCount >= 1 ) // shouldn't interpolate between default values, let's wait to receive data first, should only interpolate between real measurements
285+ // Only interpolate when there is a start and end point and we have not already reached the end value
286+ if ( InterpolateState . Start . HasValue && InterpolateState . End . HasValue )
210287 {
211- float t = 1.0f ;
212- double range = m_EndTimeConsumed - m_StartTimeConsumed ;
213- if ( range > k_SmallValue )
288+ if ( InterpolateState . LerpT < 1.0f )
214289 {
215- var rangeFactor = 1.0f / ( float ) range ;
216-
217- t = ( ( float ) renderTime - ( float ) m_StartTimeConsumed ) * rangeFactor ;
218-
219- if ( t < 0.0f )
290+ InterpolateState . RelativeTime = Math . Clamp ( InterpolateState . RelativeTime + deltaTime , 0.000001f , InterpolateState . End . Value . TimeSent ) ;
291+ //var t = 1.0f - Mathf.Clamp((float)((m_EndTimeConsumed - renderTime) * rangeFactor), 0.0f, 1.0f);
292+ //var alt_t = 1.0f - Mathf.Clamp((float)((renderTime - m_StartTimeConsumed) * rangeFactor), 0.0f, 1.0f);
293+ InterpolateState . LerpT = ( float ) ( InterpolateState . RelativeTime / InterpolateState . End . Value . TimeSent ) ;
294+ InterpolateState . CurrentValue = Interpolate ( InterpolateState . CurrentValue , InterpolateState . End . Value . Item , InterpolateState . LerpT ) ;
295+ if ( InterpolateState . LerpT < 1.0f )
220296 {
221- // There is no mechanism to guarantee renderTime to not be before m_StartTimeConsumed
222- // This clamps t to a minimum of 0 and fixes issues with longer frames and pauses
223-
224- if ( NetworkLog . CurrentLogLevel <= LogLevel . Developer )
225- {
226- NetworkLog . LogError ( $ "renderTime was before m_StartTimeConsumed. This should never happen. { nameof ( renderTime ) } is { renderTime } , { nameof ( m_StartTimeConsumed ) } is { m_StartTimeConsumed } ") ;
227- }
228- t = 0.0f ;
297+ m_CurrentInterpValue = Interpolate ( m_CurrentInterpValue , InterpolateState . CurrentValue , 0.5f ) ;
229298 }
230-
231- if ( t > MaxInterpolationBound ) // max extrapolation
299+ else
232300 {
233- // TODO this causes issues with teleport, investigate
234- t = 1.0f ;
301+ m_CurrentInterpValue = InterpolateState . CurrentValue ;
235302 }
236303 }
237-
238- var target = InterpolateUnclamped ( m_InterpStartValue , m_InterpEndValue , t ) ;
239- m_CurrentInterpValue = Interpolate ( m_CurrentInterpValue , target , deltaTime / MaximumInterpolationTime ) ; // second interpolate to smooth out extrapolation jumps
240304 }
241305
242306 m_NbItemsReceivedThisFrame = 0 ;
243307 return m_CurrentInterpValue ;
244308 }
245309
310+
311+ private double m_LastSentTime = 0.0f ;
246312 /// <summary>
247313 /// Add measurements to be used during interpolation. These will be buffered before being made available to be displayed as "latest value".
248314 /// </summary>
@@ -261,18 +327,23 @@ public void AddMeasurement(T newMeasurement, double sentTime)
261327 m_LastBufferedItemReceived = new BufferedItem ( newMeasurement , sentTime ) ;
262328 ResetTo ( newMeasurement , sentTime ) ;
263329 // Next line keeps renderTime above m_StartTimeConsumed. Fixes pause/unpause issues
264- m_Buffer . Add ( m_LastBufferedItemReceived ) ;
330+ m_Buffer . Enqueue ( m_LastBufferedItemReceived ) ;
265331 }
266332
267333 return ;
268334 }
269335
270336 // Part the of reason for disabling extrapolation is how we add and use measurements over time.
271337 // TODO: Add detailed description of this area in Jira ticket
272- if ( sentTime > m_EndTimeConsumed || m_LifetimeConsumedCount == 0 ) // treat only if value is newer than the one being interpolated to right now
338+ if ( sentTime > m_LastSentTime || m_LifetimeConsumedCount == 0 ) // treat only if value is newer than the one being interpolated to right now
273339 {
274340 m_LastBufferedItemReceived = new BufferedItem ( newMeasurement , sentTime ) ;
275- m_Buffer . Add ( m_LastBufferedItemReceived ) ;
341+ m_Buffer . Enqueue ( m_LastBufferedItemReceived ) ;
342+ m_LastSentTime = sentTime ;
343+ }
344+ else
345+ {
346+ Debug . Log ( $ "[{ m_Name } ] Dropping measurement -- Time: { sentTime } Value: { newMeasurement } ") ;
276347 }
277348 }
278349
@@ -315,7 +386,7 @@ protected override float InterpolateUnclamped(float start, float end, float time
315386 {
316387 // Disabling Extrapolation:
317388 // TODO: Add Jira Ticket
318- return Mathf . Lerp ( start , end , time ) ;
389+ return Mathf . LerpUnclamped ( start , end , time ) ;
319390 }
320391
321392 /// <inheritdoc />
@@ -348,11 +419,11 @@ protected override Quaternion InterpolateUnclamped(Quaternion start, Quaternion
348419 {
349420 if ( IsSlerp )
350421 {
351- return Quaternion . Slerp ( start , end , time ) ;
422+ return Quaternion . SlerpUnclamped ( start , end , time ) ;
352423 }
353424 else
354425 {
355- return Quaternion . Lerp ( start , end , time ) ;
426+ return Quaternion . LerpUnclamped ( start , end , time ) ;
356427 }
357428 }
358429
@@ -384,16 +455,19 @@ private Quaternion ConvertToNewTransformSpace(Transform transform, Quaternion ro
384455
385456 protected internal override void OnConvertTransformSpace ( Transform transform , bool inLocalSpace )
386457 {
387- for ( int i = 0 ; i < m_Buffer . Count ; i ++ )
458+ var buffer = m_Buffer . ToList ( ) ;
459+ m_Buffer . Clear ( ) ;
460+ for ( int i = 0 ; i < buffer . Count ; i ++ )
388461 {
389- var entry = m_Buffer [ i ] ;
462+ var entry = buffer [ i ] ;
390463 entry . Item = ConvertToNewTransformSpace ( transform , entry . Item , inLocalSpace ) ;
391- m_Buffer [ i ] = entry ;
464+ m_Buffer . Enqueue ( entry ) ;
392465 }
393-
394- m_InterpStartValue = ConvertToNewTransformSpace ( transform , m_InterpStartValue , inLocalSpace ) ;
466+ InterpolateState . CurrentValue = ConvertToNewTransformSpace ( transform , InterpolateState . CurrentValue , inLocalSpace ) ;
395467 m_CurrentInterpValue = ConvertToNewTransformSpace ( transform , m_CurrentInterpValue , inLocalSpace ) ;
396- m_InterpEndValue = ConvertToNewTransformSpace ( transform , m_InterpEndValue , inLocalSpace ) ;
468+ var end = InterpolateState . End . Value ;
469+ end . Item = ConvertToNewTransformSpace ( transform , end . Item , inLocalSpace ) ;
470+ InterpolateState . End = end ;
397471
398472 base . OnConvertTransformSpace ( transform , inLocalSpace ) ;
399473 }
@@ -414,11 +488,11 @@ protected override Vector3 InterpolateUnclamped(Vector3 start, Vector3 end, floa
414488 {
415489 if ( IsSlerp )
416490 {
417- return Vector3 . Slerp ( start , end , time ) ;
491+ return Vector3 . SlerpUnclamped ( start , end , time ) ;
418492 }
419493 else
420494 {
421- return Vector3 . Lerp ( start , end , time ) ;
495+ return Vector3 . LerpUnclamped ( start , end , time ) ;
422496 }
423497 }
424498
@@ -450,16 +524,20 @@ private Vector3 ConvertToNewTransformSpace(Transform transform, Vector3 position
450524
451525 protected internal override void OnConvertTransformSpace ( Transform transform , bool inLocalSpace )
452526 {
453- for ( int i = 0 ; i < m_Buffer . Count ; i ++ )
527+ var buffer = m_Buffer . ToList ( ) ;
528+ m_Buffer . Clear ( ) ;
529+ for ( int i = 0 ; i < buffer . Count ; i ++ )
454530 {
455- var entry = m_Buffer [ i ] ;
531+ var entry = buffer [ i ] ;
456532 entry . Item = ConvertToNewTransformSpace ( transform , entry . Item , inLocalSpace ) ;
457- m_Buffer [ i ] = entry ;
533+ m_Buffer . Enqueue ( entry ) ;
458534 }
459535
460- m_InterpStartValue = ConvertToNewTransformSpace ( transform , m_InterpStartValue , inLocalSpace ) ;
536+ InterpolateState . CurrentValue = ConvertToNewTransformSpace ( transform , InterpolateState . CurrentValue , inLocalSpace ) ;
461537 m_CurrentInterpValue = ConvertToNewTransformSpace ( transform , m_CurrentInterpValue , inLocalSpace ) ;
462- m_InterpEndValue = ConvertToNewTransformSpace ( transform , m_InterpEndValue , inLocalSpace ) ;
538+ var end = InterpolateState . End . Value ;
539+ end . Item = ConvertToNewTransformSpace ( transform , end . Item , inLocalSpace ) ;
540+ InterpolateState . End = end ;
463541
464542 base . OnConvertTransformSpace ( transform , inLocalSpace ) ;
465543 }
0 commit comments