Files
Commonwealth-Online-Public/plugin/include/F4TInterpolationSystem.h
T
andrew a11c7d4475 Add interpolation system and integrate into proxy
Introduce a waypoint-based interpolation system and integrate it into the proxy actor controller. Added F4T::InterpolationSystem (header + implementation) with TimePoint, InterpolationComponent, Lerp helpers and Update() logic. Integrates per-slot InterpolationComponent into F4TProxyActorController, switches to kinematic SetPosition-based smoothing (per-frame Lerp with snap thresholds) and removes character-controller velocity injection to prevent drift; also clears waypoints on hard snaps and uses steady_clock for timing. Added a descriptor-based bulk animation write (ApplyDesiredStateToGraphDescriptorBased) in ProxyAnimationSync for efficient graph variable updates with failure logging. Also included testing and debugging docs (phase2-interpolation-testing.md and phase2-interpolation-debugging.md) and flipped some debug flags to false.
2026-06-03 22:46:57 +12:00

82 lines
2.2 KiB
C++

#pragma once
#include <chrono>
#include <deque>
#include <cstdint>
#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<TimePoint> 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;
}