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.
82 lines
2.2 KiB
C++
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;
|
|
}
|