Add detailed design and implementation scaffolding for TiltedEvolution-style animation synchronization. New documentation: animation-architecture-alignment.md, animation-sync-analysis.md, f4-animation-descriptor.md, phase1-3-completion-report.md and updates to dev-log.md describing Phase 1-3 progress. Plugin: introduce F4AnimationDescriptor (plugin/include/F4AnimationDescriptor.h, plugin/src/F4AnimationDescriptor.cpp) and extend remote state handling (plugin/include/F4TRemotePlayerState.h). Integrate protocol/state extensions and refactor points: plugin/src/F4TNetworking.cpp now parses optional actorStateFlags and actionEvents; proxy controller and animation sync files (F4TProxyActorController.cpp, F4TProxyAnimationSync.cpp) updated to support descriptor-based bulk variable snapshots and actor state replication. Server/tools: update server/dev_server_app.py and server/fake_client.py to handle and display the new optional fields. These changes are additive and backward-compatible and set up phases for action capture, dynamic proxy spawn, and action-replay integration.
222 lines
7.7 KiB
Markdown
222 lines
7.7 KiB
Markdown
# Fallout 4 Animation Graph Variables & Descriptor Mapping
|
|
|
|
## Overview
|
|
|
|
This document maps Fallout 4's animation graph variable names and indices for the humanoid master behavior graph. This is the foundation for implementing descriptor-based animation synchronization similar to TiltedEvolution's model.
|
|
|
|
**Goal:** Replace per-frame string-based graph variable writes (e.g., `SetGraphVariableFloat("Speed", 85.0)`) with efficient indexed bulk writes from a pre-computed descriptor table.
|
|
|
|
---
|
|
|
|
## Animation Graph Variable Categories
|
|
|
|
Fallout 4's humanoid master behavior graph organizes variables into three types:
|
|
|
|
### 1. Float Variables (Locomotion & Direction)
|
|
|
|
Used to control movement speed, direction, and animation blending.
|
|
|
|
| Variable Name | Index | Type | Role | Network Sync | Notes |
|
|
|---|---|---|---|---|---|
|
|
| `Speed` | TBD | Float | Locomotion blend speed (0-105) | ✅ YES | Primary locomotion driver |
|
|
| `direction` | TBD | Float | Facing/movement direction (degrees) | ✅ YES | Rotation relative to movement |
|
|
| `speedSampled` | TBD | Float | Sampled speed for smoothing | ❌ Derive | Computed from velocity |
|
|
| `speedDamped` | TBD | Float | Damped/smoothed speed | ❌ Derive | Animation system internal |
|
|
| `pitchGunAim` | TBD | Float | Pitch angle when aiming | ❌ No | Combat specific |
|
|
| `weaponAdjust` | TBD | Float | Weapon positioning | ❌ No | Combat specific |
|
|
| `velocityZ` | TBD | Float | Vertical velocity | ❌ No | Handled by Havok |
|
|
|
|
**Speed Buckets (Estimated):**
|
|
- Idle: 0.0
|
|
- Walk: 35-55
|
|
- Run: 55-85
|
|
- Sprint: 85-105
|
|
|
|
### 2. Boolean Variables (State Flags)
|
|
|
|
Control state transitions and mode changes.
|
|
|
|
| Variable Name | Index | Type | Role | Network Sync | Notes |
|
|
|---|---|---|---|---|---|
|
|
| `isSprinting` | TBD | Bool | Sprint mode active | ✅ YES | Triggers sprint animations |
|
|
| `isSneaking` | TBD | Bool | Sneak/crouch mode | ✅ YES | Triggers sneak locomotion |
|
|
| `isMoving` | TBD | Bool | Any movement active | ✅ YES | Idle ↔ movement transition |
|
|
| `bMotionDriven` | TBD | Bool | Motion-driven locomotion | ❌ No | Engine internal |
|
|
| `bInMoveState` | TBD | Bool | In locomotion state machine | ❌ No | Engine internal |
|
|
| `bSprintOK` | TBD | Bool | Sprint available | ❌ No | Game state dependent |
|
|
|
|
### 3. Integer Variables (Equipment & State)
|
|
|
|
Track equipped items and combat state.
|
|
|
|
| Variable Name | Index | Type | Role | Network Sync | Notes |
|
|
|---|---|---|---|---|---|
|
|
| `iLeftHandType` | TBD | Int | Left hand weapon/item type | ❌ No | Inventory sync separate |
|
|
| `iRightHandEquipped` | TBD | Int | Right hand equipped? (0/1) | ❌ No | Inventory sync separate |
|
|
| `iIsInSneak` | TBD | Int | Sneak state (0/1/2) | ❌ No | Derived from isSneaking bool |
|
|
|
|
---
|
|
|
|
## Variable Index Discovery Process
|
|
|
|
To determine actual indices for FO4, we need to:
|
|
|
|
### 1. Extract Indices via AnimationGraphManager
|
|
|
|
```cpp
|
|
// Pseudocode to dump actual variable indices
|
|
void DumpAnimationGraphVariables(RE::Actor* actor) {
|
|
auto graphHolder = static_cast<RE::IAnimationGraphManagerHolder*>(actor);
|
|
RE::BSTSmartPointer<RE::BSAnimationGraphManager> manager;
|
|
|
|
if (graphHolder->GetAnimationGraphManagerImpl(manager) && manager) {
|
|
// Access manager->behaviorGraph->animationVariables
|
|
// Iterate through variable cache and log names + indices
|
|
// Compare with known string names to build reverse mapping
|
|
}
|
|
}
|
|
```
|
|
|
|
### 2. Verify via Creation Kit
|
|
|
|
- Open the humanoid master behavior graph in Behavior Editor
|
|
- Right-click variables to see internal indices/cache layout
|
|
- Cross-reference with runtime dumps
|
|
|
|
### 3. Cross-Reference with CommonLibF4 Headers
|
|
|
|
- Search `RE/H/BSAnimationGraphManager.h` for hint structs
|
|
- Look for variable container definitions
|
|
|
|
---
|
|
|
|
## Comparison with TiltedEvolution (Skyrim SE)
|
|
|
|
TiltedEvolution's `AnimationGraphDescriptor_Master_Behavior.cpp` syncs:
|
|
|
|
**Skyrim SE Synced Booleans:**
|
|
- `kIsSprinting` (index 50)
|
|
- `kIsSneaking` (index 186)
|
|
- `kbInMoveState` (index 98)
|
|
- `kbMotionDriven` (index 41)
|
|
- `kisMoving` (index 284)
|
|
|
|
**Skyrim SE Synced Floats:**
|
|
- `kSpeed` (index 0)
|
|
- `kSpeedSampled` (index 40)
|
|
- `kSpeedDamped` (index 183)
|
|
- `kDirection` (index 1)
|
|
- `kSpeedWalk`, `kSpeedRun` (indices 4, 5)
|
|
|
|
**FO4 Likely Similar:**
|
|
- Speed float: core locomotion
|
|
- Direction float: facing
|
|
- isSprinting, isSneaking: bool state flags
|
|
- velocityZ: vertical velocity (Havok-driven, derived)
|
|
|
|
---
|
|
|
|
## Minimum Viable Sync Set
|
|
|
|
For initial Phase 1-2 implementation, sync only the essential variables needed for basic locomotion:
|
|
|
|
| Variable | Type | Index | Reason |
|
|
|----------|------|-------|--------|
|
|
| `Speed` | Float | TBD | Must have; drives walk/run/sprint |
|
|
| `direction` | Float | TBD | Must have; face remote player's heading |
|
|
| `isSprinting` | Bool | TBD | Should have; sprint visual distinction |
|
|
| `isSneaking` | Bool | TBD | Should have; sneak locomotion + crouch |
|
|
| `isMoving` | Bool | TBD | Should have; idle ↔ movement transition |
|
|
|
|
**Deferred (Phase 2+):**
|
|
- Combat variables (aiming, weapon adjust, etc.)
|
|
- Jumping/falling (vertical velocity)
|
|
- Facial expressions / emotion
|
|
- IK targets
|
|
|
|
---
|
|
|
|
## Current F4T Implementation (Before Refactor)
|
|
|
|
Current `F4TProxyAnimationSync.cpp` writes these by string name:
|
|
|
|
```cpp
|
|
// Per-frame, on every proxy actor:
|
|
TrySetGraphFloat(graphHolder, "Speed", desired.graphSpeed);
|
|
TrySetGraphFloat(graphHolder, "speed", desired.graphSpeed); // fallback
|
|
TrySetGraphFloat(graphHolder, "SpeedLower", desired.graphSpeed);
|
|
TrySetGraphBool(graphHolder, "IsSprinting", desired.isSprinting);
|
|
```
|
|
|
|
**Issues:**
|
|
- String lookup overhead (one `BSFixedString` allocation per write, per frame, per proxy)
|
|
- No caching of variable indices
|
|
- If a variable name is wrong, silent failure (write returns false)
|
|
- No explicit velocity injection (character controller velocity = 0)
|
|
|
|
---
|
|
|
|
## Proposed F4AnimationDescriptor Model
|
|
|
|
After Phase 1 research, we'll build:
|
|
|
|
```cpp
|
|
// plugin/include/F4AnimationDescriptor.h
|
|
class F4AnimationDescriptor {
|
|
public:
|
|
// Per-graph-type lookup
|
|
static F4AnimationDescriptor& GetHumanoidDescriptor();
|
|
|
|
// Bulk read/write by index
|
|
void SaveVariablesFromCache(
|
|
RE::IAnimationGraphManagerHolder& holder,
|
|
AnimationVariableSnapshot& snapshot);
|
|
|
|
void LoadVariablesToCache(
|
|
RE::IAnimationGraphManagerHolder& holder,
|
|
const AnimationVariableSnapshot& snapshot);
|
|
|
|
private:
|
|
std::vector<std::string> floatVariableNames;
|
|
std::vector<std::string> boolVariableNames;
|
|
std::vector<std::string> intVariableNames;
|
|
};
|
|
```
|
|
|
|
**Benefits:**
|
|
- Pre-computed once at startup
|
|
- O(1) index lookups instead of O(n) string searches
|
|
- Batch reads/writes in one graph manager lock
|
|
- Matches TiltedEvolution's proven architecture
|
|
|
|
---
|
|
|
|
## Testing Checklist (Phase 1.3)
|
|
|
|
- [ ] Verify `Actor::Move(0.016f, {0,0,0}, false)` works on dynamically spawned actors
|
|
- [ ] Verify `SetLinearVelocityImpl()` accessible via Move return value
|
|
- [ ] Test with proxy in same cell as player
|
|
- [ ] Confirm velocity persists across frames
|
|
- [ ] Log animation graph state transitions (idle → walk → run)
|
|
- [ ] Verify no crashes with velocity injection
|
|
- [ ] Document any FO4-specific gotchas vs Skyrim
|
|
|
|
---
|
|
|
|
## Next Steps
|
|
|
|
1. **Phase 1.1:** Use debug logging to extract actual variable indices from running F4
|
|
2. **Phase 1.2:** Map TiltedEvolution animation flow into F4T terms (see `animation-architecture-alignment.md`)
|
|
3. **Phase 1.3:** Test character controller velocity injection on dynamic proxies
|
|
4. **Phase 2:** Extend `RemotePlayerState` with actor state flags + action events
|
|
5. **Phase 3:** Implement `F4AnimationDescriptor` class with bulk read/write
|
|
|
|
---
|
|
|
|
## References
|
|
|
|
- TiltedEvolution: `Code/encoding/Structs/Skyrim/AnimationGraphDescriptor_Master_Behavior.cpp`
|
|
- CommonLibF4: `RE/H/BSAnimationGraphManager.h`, `RE/H/IAnimationGraphManagerHolder.h`
|
|
- Fallout 4 CK: Behavior Editor (humanoid master behavior graph)
|
|
|