fix(movement): correct facing direction quaternion convention

AC heading convention: 0=West, 90=North, 180=East, 270=South.
Our internal yaw: 0=+X (East), PI/2=+Y (North).
Conversion: heading_deg = 180 - yaw_degrees, then holtburger's
from_heading formula: theta=(450-heading).toRad, w=cos(θ/2), z=sin(θ/2).

Previously sent raw Yaw as axis-angle rotation which was ~90° off.
Other clients now see correct facing direction.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-04-14 10:42:19 +02:00
parent 5634e7114b
commit 6523c7199b

View file

@ -1896,8 +1896,7 @@ public sealed class GameWindow : IDisposable
float localY = result.Position.Y - (lbY - _liveCenterY) * 192f;
uint wireCellId = ((uint)lbX << 24) | ((uint)lbY << 16) | (result.CellId & 0xFFFFu);
var wirePos = new System.Numerics.Vector3(localX, localY, result.Position.Z);
var wireRot = System.Numerics.Quaternion.CreateFromAxisAngle(
System.Numerics.Vector3.UnitZ, _playerController.Yaw);
var wireRot = YawToAcQuaternion(_playerController.Yaw);
if (result.MotionStateChanged)
{
@ -1956,6 +1955,34 @@ public sealed class GameWindow : IDisposable
}
}
/// <summary>
/// Convert our internal yaw (math convention: 0=+X East, PI/2=+Y North)
/// to AC's quaternion heading convention.
/// AC heading: 0=West, 90=North, 180=East, 270=South.
/// Formula from holtburger Quaternion::from_heading.
/// </summary>
private static System.Numerics.Quaternion YawToAcQuaternion(float yaw)
{
// Our yaw → AC heading in degrees:
// yaw=0 → East → AC 180°, yaw=PI/2 → North → AC 90°
// heading_deg = 180 - yaw_degrees
float yawDeg = yaw * (180f / MathF.PI);
float headingDeg = 180f - yawDeg;
if (headingDeg < 0f) headingDeg += 360f;
if (headingDeg >= 360f) headingDeg -= 360f;
// holtburger from_heading: theta = (450 - heading_deg) in radians
float theta = (450f - headingDeg) * (MathF.PI / 180f);
float halfTheta = theta * 0.5f;
float w = MathF.Cos(halfTheta);
float z = MathF.Sin(halfTheta);
// Canonicalize: w must be non-negative
if (w < 0f) { w = -w; z = -z; }
return new System.Numerics.Quaternion(0f, 0f, z, w);
}
private void OnCameraModeChanged(bool isFlyMode)
{
if (_input is null) return;