using System;
using System.Collections.Generic;
using System.Linq;
using Vintagestory.API.MathTools;
using VSClientOptimizer.Config;
using VSClientOptimizer.Math;
using VSClientOptimizer.Interpolation;
namespace VSClientOptimizer.Performance
{
///
/// Frame Generation - Creates intermediate frames between real frames for smoother motion
/// Similar to AMD FSR Frame Generation or NVIDIA DLSS 3 Frame Generation
///
public class FrameGenerator
{
private Queue frameHistory = new Queue();
private const int MaxFrameHistory = 3;
private float generationMultiplier = 2.0f;
private bool useMotionVectors = true;
public class FrameSnapshot
{
public long TimestampMs;
public Dictionary EntityStates;
public FrameSnapshot(long timestamp)
{
TimestampMs = timestamp;
EntityStates = new Dictionary();
}
}
public FrameGenerator(float multiplier, bool useMotion)
{
generationMultiplier = System.Math.Max(1.0f, System.Math.Min(4.0f, multiplier)); // Clamp 1-4x
useMotionVectors = useMotion;
}
public void AddFrame(long timestamp, Dictionary states)
{
var snapshot = new FrameSnapshot(timestamp);
foreach (var kvp in states)
{
snapshot.EntityStates[kvp.Key] = kvp.Value;
}
frameHistory.Enqueue(snapshot);
if (frameHistory.Count > MaxFrameHistory)
frameHistory.Dequeue();
}
public List GenerateIntermediateFrames()
{
var generatedFrames = new List();
if (frameHistory.Count < 2 || generationMultiplier <= 1.0f)
return generatedFrames;
var frames = frameHistory.ToArray();
var lastFrame = frames[frames.Length - 2];
var currentFrame = frames[frames.Length - 1];
long timeDiff = currentFrame.TimestampMs - lastFrame.TimestampMs;
if (timeDiff <= 0) return generatedFrames;
// Generate intermediate frames
int intermediateCount = (int)(generationMultiplier - 1);
for (int i = 1; i <= intermediateCount; i++)
{
float t = i / generationMultiplier;
long interpTime = lastFrame.TimestampMs + (long)(timeDiff * t);
var interpFrame = new FrameSnapshot(interpTime);
// Interpolate each entity's state
foreach (var entityId in lastFrame.EntityStates.Keys)
{
if (currentFrame.EntityStates.TryGetValue(entityId, out var currentState) &&
lastFrame.EntityStates.TryGetValue(entityId, out var lastState))
{
var interpState = InterpolateEntityState(lastState, currentState, t);
interpFrame.EntityStates[entityId] = interpState;
}
}
generatedFrames.Add(interpFrame);
}
return generatedFrames;
}
private EntityPositionSnapshot InterpolateEntityState(
EntityPositionSnapshot from,
EntityPositionSnapshot to,
float t)
{
// Position interpolation
Vector3 fromPos = new Vector3(from.Position.X, from.Position.Y, from.Position.Z);
Vector3 toPos = new Vector3(to.Position.X, to.Position.Y, to.Position.Z);
Vector3 interpPos;
if (useMotionVectors && (from.Velocity.X != 0 || from.Velocity.Y != 0 || from.Velocity.Z != 0))
{
// Use motion vectors for smoother interpolation
double deltaTime = (to.TimestampMs - from.TimestampMs) / 1000.0;
Vector3 motionPredicted = fromPos + (from.Velocity * (deltaTime * t));
interpPos = Vector3.Lerp(Vector3.Lerp(fromPos, toPos, t), motionPredicted, 0.3f);
}
else
{
interpPos = Vector3.Lerp(fromPos, toPos, t);
}
// Rotation interpolation
Rotation3 fromRot = new Rotation3(from.Rotation.X, from.Rotation.Y, from.Rotation.Z);
Rotation3 toRot = new Rotation3(to.Rotation.X, to.Rotation.Y, to.Rotation.Z);
Rotation3 interpRot = Rotation3.Lerp(fromRot, toRot, t);
// Velocity interpolation
Vector3 interpVel = Vector3.Lerp(from.Velocity, to.Velocity, t);
long interpTime = from.TimestampMs + (long)((to.TimestampMs - from.TimestampMs) * t);
return new EntityPositionSnapshot(
new Vec3d(interpPos.X, interpPos.Y, interpPos.Z),
new Vec3f(interpRot.Pitch, interpRot.Yaw, interpRot.Roll),
interpTime,
interpVel
);
}
}
}