XRoom_Unity/Assets/Photon/FusionXRHost/Scripts/Utils/TimedRingBuffer.cs
2025-05-31 10:20:20 +03:30

178 lines
5.8 KiB
C#

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Fusion.Tools
{
public interface ICopiable<T>
{
// Ensure that all fields from source are used to fill the values of this object
public void CopyValuesFrom(T source) { }
}
public enum InterpolationStatus
{
ValidFromTo,
ValidTo,
ValidFrom,
InvalidBoundaries
}
public struct InterpolationInfo<T>
{
public TimedRingBufferEntry<T>? fromEntry;
public TimedRingBufferEntry<T>? toEntry;
public float alpha;
public InterpolationStatus status;
public T from
{
get
{
if (fromEntry == null) throw new System.Exception("'From' unavailable. Status: " + status);
return fromEntry.GetValueOrDefault().data;
}
}
public T to
{
get
{
if (toEntry == null) throw new System.Exception("'To' unavailable. Status: " + status);
return toEntry.GetValueOrDefault().data;
}
}
}
public struct TimedRingBufferEntry<T>
{
public float time;
public T data;
}
// Simple ring buffer storing time alongside data in Add(), to be able to interpolate at a certain point in the past with InterpolateInfo()
public struct TimedRingbuffer<T> where T: ICopiable<T>
{
TimedRingBufferEntry<T>[] entries;
// Contains the orderered index list
int[] indexes;
int nextIndex;
int firstIndex;
int bufferSize;
public TimedRingbuffer(int size = 10)
{
entries = new TimedRingBufferEntry<T>[size];
bufferSize = size;
nextIndex = 0;
firstIndex = -1;
indexes = new int[0];
}
public InterpolationInfo<T> InterpolateInfo(float atTime)
{
InterpolationInfo<T> interpolationInfo = default;
TimedRingBufferEntry<T>? fromEntry = default;
TimedRingBufferEntry<T>? toEntry = default;
for(int i = 0; i < indexes.Length; i++)
{
if (entries[i].time < atTime)
{
if (fromEntry == null || fromEntry.GetValueOrDefault().time < entries[i].time)
{
fromEntry = entries[i];
}
}
if (atTime < entries[i].time)
{
if (toEntry == null || entries[i].time < toEntry.GetValueOrDefault().time )
{
// If the entries where properly entered (with increasing time), the first valid to should be the last check, and we could break here
toEntry = entries[i];
}
}
}
if (fromEntry != null)
{
if(toEntry != null)
{
interpolationInfo.status = InterpolationStatus.ValidFromTo;
float fromTime = fromEntry.GetValueOrDefault().time;
float toTime = toEntry.GetValueOrDefault().time;
if (fromTime == toTime)
{
Debug.LogError("Two entries at same time");
interpolationInfo.alpha = 0;
}
else
{
interpolationInfo.alpha = (atTime - fromTime)/(toTime - fromTime);
if (interpolationInfo.alpha < 0 || interpolationInfo.alpha > 1)
{
Debug.LogError("Problem in stored times");
}
}
}
else
{
interpolationInfo.status = InterpolationStatus.ValidFrom;
interpolationInfo.alpha = 0;
}
}
else
{
if (toEntry != null)
{
interpolationInfo.status = InterpolationStatus.ValidTo;
interpolationInfo.alpha = 1;
}
else
{
interpolationInfo.status = InterpolationStatus.InvalidBoundaries;
}
}
interpolationInfo.fromEntry = fromEntry;
interpolationInfo.toEntry = toEntry;
return interpolationInfo;
}
public void Add(T data, float time)
{
entries[nextIndex].data.CopyValuesFrom(data);
entries[nextIndex].time = time;
UpdateIndexes();
}
void UpdateIndexes() {
// 2 initial cases: buffer never filled once (firstIndex == 0), and buffer filled once (firstIndex == nextIndex)
bool bufferFilledOnce = firstIndex == nextIndex;
if ((nextIndex + 1) >= bufferSize)
{
// Data will go other the max index
bufferFilledOnce = true;
}
if (firstIndex == -1)
{
firstIndex = 0;
}
nextIndex = ((nextIndex + 1) % bufferSize);
if (bufferFilledOnce)
{
// The buffer has already been filled once
firstIndex = nextIndex;
}
int validIndexCount = (nextIndex > firstIndex)?(nextIndex - firstIndex):(nextIndex + bufferSize - firstIndex);
if(indexes.Length != validIndexCount)
{
indexes = new int[validIndexCount];
}
int index = firstIndex;
for(int i = 0; i < validIndexCount; i++)
{
indexes[i] = (firstIndex + i) % bufferSize;
}
}
}
}