#pragma once #include #include #include #include "RE/N/NiPoint3.h" namespace F4T::InterpolationSystem { // TimePoint: Represents a position/rotation at a specific game tick struct TimePoint { RE::NiPoint3 Position{}; float Rotation{}; // Z-axis rotation (angleZ) std::chrono::steady_clock::time_point Timestamp{}; uint64_t Tick{ 0 }; TimePoint() = default; TimePoint(const RE::NiPoint3& a_pos, float a_rot, uint64_t a_tick) : Position(a_pos), Rotation(a_rot), Tick(a_tick), Timestamp(std::chrono::steady_clock::now()) { } }; // InterpolationComponent: Manages smooth movement between waypoints struct InterpolationComponent { // Deque maintains 2 waypoints for lerp: current and next std::deque TimePoints; // Last interpolated position (for delta checks) RE::NiPoint3 LastInterpolatedPosition{}; // Add a new waypoint to the queue void AddWaypoint(const TimePoint& a_point) noexcept { TimePoints.push_back(a_point); } // Get the current number of waypoints std::size_t GetWaypointCount() const noexcept { return TimePoints.size(); } // Clear all waypoints void Clear() noexcept { TimePoints.clear(); } // Check if we have enough waypoints for interpolation (need at least 2) bool HasEnoughWaypoints() const noexcept { return TimePoints.size() >= 2; } }; // Update interpolation for an actor // Returns the interpolated position if interpolation occurred, nullopt otherwise struct InterpolationResult { RE::NiPoint3 Position{}; float Rotation{}; bool IsInterpolating{ false }; }; // Update the interpolation for a remote player // a_currentTime: Current game time (in milliseconds or ticks) // Returns interpolated position/rotation if movement is happening InterpolationResult Update( InterpolationComponent& a_component, uint64_t a_currentTick) noexcept; // Helper: Linear interpolation between two 3D points RE::NiPoint3 Lerp(const RE::NiPoint3& a_from, const RE::NiPoint3& a_to, float a_alpha) noexcept; // Helper: Circular interpolation for rotation (handles wrap-around) float LerpRotation(float a_from, float a_to, float a_alpha) noexcept; }