Drive proxy cadence from measured motion

Tie proxy locomotion/cadence to actual per-frame movement and motion-feedback instead of only replaying transmitted Speed.

Changes:
- docs/dev-log.md: add detailed dev log describing measured-cadence fix and cadence/animationSpeed work.
- plugin/include/F4TProxyPuppet.h: added NiPoint3 include and new API: SetProxyLocomotionState, SetProxyDesiredSpeed (legacy alias), and ApplyProxyLocomotionFrame.
- plugin/lib/commonlibf4/include/RE/A/ActorMotionFeedbackOutput.h: new tentative struct for motion-feedback output.
- plugin/lib/commonlibf4/include/RE/Fallout.h: include new ActorMotionFeedbackOutput header.
- plugin/src/F4TProxyActorController.cpp: track per-frame timing/puppet state, compute frame delta, use proxy->Move() with measured frameDelta and call ApplyProxyLocomotionFrame (instead of SetPosition/Lerp-only), and pass measured graph speed/direction to proxy.
- plugin/src/F4TProxyAnimationSync.cpp: use SetProxyLocomotionState to include direction when writing proxy locomotion state.
- plugin/src/F4TProxyPuppet.cpp: introduce ProxyLocomotionState map, sync controller motion fields, compute/push engine motion-feedback (via Compute/Update feedback vtable calls), use UpdateNoAI + manual motion feedback to drive cadence, implement ApplyProxyLocomotionFrame which derives velocity from world delta, clamps/chooses feedback speed (uses measured horizontal speed unless provided graph speed is valid), applies an idle threshold (<8 u/s -> idle), and writes graph speed back to the animation graph.

Why: This makes foot cadence reflect on-screen translation (eliminating mild foot slide/stop-overstay) by measuring applied displacement each frame, updating controller velocity/motion-feedback, and re-asserting animation graph inputs after UpdateNoAI.
This commit is contained in:
2026-06-04 21:39:26 +12:00
parent abef899505
commit 65553a0c61
7 changed files with 347 additions and 49 deletions
+16 -4
View File
@@ -1,5 +1,7 @@
#pragma once
#include "RE/N/NiPoint3.h"
#include <cstdint>
#include <vector>
@@ -23,12 +25,22 @@ namespace F4T::ProxyPuppet
// from the game-thread proxy controller each tick. Thread-safe.
void SetActiveProxyFormIDs(std::vector<std::uint32_t> a_formIDs);
// Records the locomotion graph Speed the proxy should hold. The Update hook
// re-asserts this immediately after the engine's per-frame actor update (which
// otherwise resets Speed to 0 on a SetPosition-driven actor). Negative speed
// means "no override" (idle). Thread-safe.
// Records locomotion state the puppet should hold. Negative speed means idle
// (no override). Thread-safe.
void SetProxyLocomotionState(std::uint32_t a_formID, float a_speed, float a_direction);
// Legacy alias for speed-only callers.
void SetProxyDesiredSpeed(std::uint32_t a_formID, float a_speed);
// Syncs controller/movement-output velocity and pushes motion feedback into the
// animation graph. Call once per frame after applying proxy displacement (e.g. Move).
void ApplyProxyLocomotionFrame(
RE::Actor& a_actor,
const RE::NiPoint3& a_worldDelta,
float a_deltaSeconds,
float a_graphSpeed,
float a_direction);
// Thread-safe membership test used by the vtable hook to gate proxies from
// real NPCs (which must keep their engine-driven locomotion).
bool IsProxyFormID(std::uint32_t a_formID);