fix(app): Phase B.2 — fix camera rotation offset + pass server MotionTableId to walk animation
Two fixes: 1. Camera side-view: AC character models face +Y in their default orientation, but our yaw convention has +X at yaw=0. Added a -PI/2 offset to the character's rotation so the model faces the actual walk direction. Camera was already correct — it was the model rotation that was 90 degrees off. 2. No walk animation: UpdatePlayerAnimation loaded the Setup directly from dats, but Setup.DefaultMotionTable is 0 for human characters — the real motion table comes from the server's PhysicsDescriptionFlag.MTable field in CreateObject. Without the override, GetIdleCycle returned null for every command. Now stores _playerMotionTableId from the spawn event and passes it as motionTableIdOverride to GetIdleCycle. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
b341193cfe
commit
980e79dae9
1 changed files with 21 additions and 2 deletions
|
|
@ -99,6 +99,7 @@ public sealed class GameWindow : IDisposable
|
|||
private bool _playerMode;
|
||||
private uint _playerServerGuid;
|
||||
private uint? _playerCurrentAnimCommand;
|
||||
private uint? _playerMotionTableId; // server-sent MotionTable override for the player's character
|
||||
// Accumulated mouse X delta for player turning; written in mouse-move
|
||||
// callback, consumed + reset in OnUpdate each frame.
|
||||
private float _playerMouseDeltaX;
|
||||
|
|
@ -776,6 +777,13 @@ public sealed class GameWindow : IDisposable
|
|||
// UpdateMotion / UpdatePosition events can reseat this entity by guid.
|
||||
_entitiesByServerGuid[spawn.Guid] = entity;
|
||||
|
||||
// Phase B.2: capture the server-sent MotionTableId for our own
|
||||
// character so UpdatePlayerAnimation can pass it to GetIdleCycle.
|
||||
// The Setup's DefaultMotionTable is often 0 for human characters;
|
||||
// the real table comes from PhysicsDescriptionFlag.MTable.
|
||||
if (spawn.Guid == _playerServerGuid && spawn.MotionTableId is not null)
|
||||
_playerMotionTableId = spawn.MotionTableId;
|
||||
|
||||
// Phase 6.4: register for per-frame playback if we resolved a real
|
||||
// cycle with a non-zero framerate and at least two frames in the
|
||||
// cycle (single-frame poses are static and don't need ticking).
|
||||
|
|
@ -1512,8 +1520,13 @@ public sealed class GameWindow : IDisposable
|
|||
if (_entitiesByServerGuid.TryGetValue(_playerServerGuid, out var pe))
|
||||
{
|
||||
pe.Position = result.Position;
|
||||
// AC character models face +Y in their default orientation.
|
||||
// Our yaw convention has cos(yaw)=+X at yaw=0, so yaw=0
|
||||
// means facing +X. Offset by -PI/2 so the model faces the
|
||||
// actual walk direction (at yaw=0, model rotation = -PI/2
|
||||
// = facing +X instead of the model's default +Y).
|
||||
pe.Rotation = System.Numerics.Quaternion.CreateFromAxisAngle(
|
||||
System.Numerics.Vector3.UnitZ, _playerController.Yaw);
|
||||
System.Numerics.Vector3.UnitZ, _playerController.Yaw - MathF.PI / 2f);
|
||||
}
|
||||
|
||||
// Update chase camera.
|
||||
|
|
@ -1801,7 +1814,13 @@ public sealed class GameWindow : IDisposable
|
|||
|
||||
ushort cmdOverride = (ushort)(animCommand & 0xFFFFu);
|
||||
var cycle = AcDream.Core.Meshing.MotionResolver.GetIdleCycle(
|
||||
ae.Setup, _dats, commandOverride: cmdOverride);
|
||||
ae.Setup, _dats,
|
||||
motionTableIdOverride: _playerMotionTableId,
|
||||
commandOverride: cmdOverride);
|
||||
|
||||
Console.WriteLine($"[PLAYER-ANIM] cmd=0x{animCommand:X8} cmdOverride=0x{cmdOverride:X4} " +
|
||||
$"cycle={(cycle is null ? "NULL" : $"fr={cycle.Framerate:F1} low={cycle.LowFrame} high={cycle.HighFrame}")} " +
|
||||
$"setup=0x{pe.SourceGfxObjOrSetupId:X8} mtable=0x{(uint)ae.Setup.DefaultMotionTable:X8}");
|
||||
|
||||
if (cycle is null || cycle.Framerate == 0f || cycle.HighFrame <= cycle.LowFrame) return;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue