Angablade ha revisionato questo gist . Vai alla revisione
1 file changed, 133 insertions
framegen.cs(file creato)
| @@ -0,0 +1,133 @@ | |||
| 1 | + | using System; | |
| 2 | + | using System.Collections.Generic; | |
| 3 | + | using System.Linq; | |
| 4 | + | using Vintagestory.API.MathTools; | |
| 5 | + | using VSClientOptimizer.Config; | |
| 6 | + | using VSClientOptimizer.Math; | |
| 7 | + | using VSClientOptimizer.Interpolation; | |
| 8 | + | ||
| 9 | + | namespace VSClientOptimizer.Performance | |
| 10 | + | { | |
| 11 | + | /// <summary> | |
| 12 | + | /// Frame Generation - Creates intermediate frames between real frames for smoother motion | |
| 13 | + | /// Similar to AMD FSR Frame Generation or NVIDIA DLSS 3 Frame Generation | |
| 14 | + | /// </summary> | |
| 15 | + | public class FrameGenerator | |
| 16 | + | { | |
| 17 | + | private Queue<FrameSnapshot> frameHistory = new Queue<FrameSnapshot>(); | |
| 18 | + | private const int MaxFrameHistory = 3; | |
| 19 | + | private float generationMultiplier = 2.0f; | |
| 20 | + | private bool useMotionVectors = true; | |
| 21 | + | ||
| 22 | + | public class FrameSnapshot | |
| 23 | + | { | |
| 24 | + | public long TimestampMs; | |
| 25 | + | public Dictionary<long, EntityPositionSnapshot> EntityStates; | |
| 26 | + | ||
| 27 | + | public FrameSnapshot(long timestamp) | |
| 28 | + | { | |
| 29 | + | TimestampMs = timestamp; | |
| 30 | + | EntityStates = new Dictionary<long, EntityPositionSnapshot>(); | |
| 31 | + | } | |
| 32 | + | } | |
| 33 | + | ||
| 34 | + | public FrameGenerator(float multiplier, bool useMotion) | |
| 35 | + | { | |
| 36 | + | generationMultiplier = System.Math.Max(1.0f, System.Math.Min(4.0f, multiplier)); // Clamp 1-4x | |
| 37 | + | useMotionVectors = useMotion; | |
| 38 | + | } | |
| 39 | + | ||
| 40 | + | public void AddFrame(long timestamp, Dictionary<long, EntityPositionSnapshot> states) | |
| 41 | + | { | |
| 42 | + | var snapshot = new FrameSnapshot(timestamp); | |
| 43 | + | foreach (var kvp in states) | |
| 44 | + | { | |
| 45 | + | snapshot.EntityStates[kvp.Key] = kvp.Value; | |
| 46 | + | } | |
| 47 | + | ||
| 48 | + | frameHistory.Enqueue(snapshot); | |
| 49 | + | if (frameHistory.Count > MaxFrameHistory) | |
| 50 | + | frameHistory.Dequeue(); | |
| 51 | + | } | |
| 52 | + | ||
| 53 | + | public List<FrameSnapshot> GenerateIntermediateFrames() | |
| 54 | + | { | |
| 55 | + | var generatedFrames = new List<FrameSnapshot>(); | |
| 56 | + | ||
| 57 | + | if (frameHistory.Count < 2 || generationMultiplier <= 1.0f) | |
| 58 | + | return generatedFrames; | |
| 59 | + | ||
| 60 | + | var frames = frameHistory.ToArray(); | |
| 61 | + | var lastFrame = frames[frames.Length - 2]; | |
| 62 | + | var currentFrame = frames[frames.Length - 1]; | |
| 63 | + | ||
| 64 | + | long timeDiff = currentFrame.TimestampMs - lastFrame.TimestampMs; | |
| 65 | + | if (timeDiff <= 0) return generatedFrames; | |
| 66 | + | ||
| 67 | + | // Generate intermediate frames | |
| 68 | + | int intermediateCount = (int)(generationMultiplier - 1); | |
| 69 | + | for (int i = 1; i <= intermediateCount; i++) | |
| 70 | + | { | |
| 71 | + | float t = i / generationMultiplier; | |
| 72 | + | long interpTime = lastFrame.TimestampMs + (long)(timeDiff * t); | |
| 73 | + | ||
| 74 | + | var interpFrame = new FrameSnapshot(interpTime); | |
| 75 | + | ||
| 76 | + | // Interpolate each entity's state | |
| 77 | + | foreach (var entityId in lastFrame.EntityStates.Keys) | |
| 78 | + | { | |
| 79 | + | if (currentFrame.EntityStates.TryGetValue(entityId, out var currentState) && | |
| 80 | + | lastFrame.EntityStates.TryGetValue(entityId, out var lastState)) | |
| 81 | + | { | |
| 82 | + | var interpState = InterpolateEntityState(lastState, currentState, t); | |
| 83 | + | interpFrame.EntityStates[entityId] = interpState; | |
| 84 | + | } | |
| 85 | + | } | |
| 86 | + | ||
| 87 | + | generatedFrames.Add(interpFrame); | |
| 88 | + | } | |
| 89 | + | ||
| 90 | + | return generatedFrames; | |
| 91 | + | } | |
| 92 | + | ||
| 93 | + | private EntityPositionSnapshot InterpolateEntityState( | |
| 94 | + | EntityPositionSnapshot from, | |
| 95 | + | EntityPositionSnapshot to, | |
| 96 | + | float t) | |
| 97 | + | { | |
| 98 | + | // Position interpolation | |
| 99 | + | Vector3 fromPos = new Vector3(from.Position.X, from.Position.Y, from.Position.Z); | |
| 100 | + | Vector3 toPos = new Vector3(to.Position.X, to.Position.Y, to.Position.Z); | |
| 101 | + | ||
| 102 | + | Vector3 interpPos; | |
| 103 | + | if (useMotionVectors && (from.Velocity.X != 0 || from.Velocity.Y != 0 || from.Velocity.Z != 0)) | |
| 104 | + | { | |
| 105 | + | // Use motion vectors for smoother interpolation | |
| 106 | + | double deltaTime = (to.TimestampMs - from.TimestampMs) / 1000.0; | |
| 107 | + | Vector3 motionPredicted = fromPos + (from.Velocity * (deltaTime * t)); | |
| 108 | + | interpPos = Vector3.Lerp(Vector3.Lerp(fromPos, toPos, t), motionPredicted, 0.3f); | |
| 109 | + | } | |
| 110 | + | else | |
| 111 | + | { | |
| 112 | + | interpPos = Vector3.Lerp(fromPos, toPos, t); | |
| 113 | + | } | |
| 114 | + | ||
| 115 | + | // Rotation interpolation | |
| 116 | + | Rotation3 fromRot = new Rotation3(from.Rotation.X, from.Rotation.Y, from.Rotation.Z); | |
| 117 | + | Rotation3 toRot = new Rotation3(to.Rotation.X, to.Rotation.Y, to.Rotation.Z); | |
| 118 | + | Rotation3 interpRot = Rotation3.Lerp(fromRot, toRot, t); | |
| 119 | + | ||
| 120 | + | // Velocity interpolation | |
| 121 | + | Vector3 interpVel = Vector3.Lerp(from.Velocity, to.Velocity, t); | |
| 122 | + | ||
| 123 | + | long interpTime = from.TimestampMs + (long)((to.TimestampMs - from.TimestampMs) * t); | |
| 124 | + | ||
| 125 | + | return new EntityPositionSnapshot( | |
| 126 | + | new Vec3d(interpPos.X, interpPos.Y, interpPos.Z), | |
| 127 | + | new Vec3f(interpRot.Pitch, interpRot.Yaw, interpRot.Roll), | |
| 128 | + | interpTime, | |
| 129 | + | interpVel | |
| 130 | + | ); | |
| 131 | + | } | |
| 132 | + | } | |
| 133 | + | } | |
Più nuovi
Più vecchi