fix(app): terrain cull exempt + yaw preservation on Tab toggle

1. Terrain culling: TerrainRenderer.Draw now accepts neverCullLandblockId,
   matching StaticMeshRenderer. Both renderers skip frustum-culling the
   player's current landblock. Previously only entities were exempt but
   the terrain under the player still disappeared when looking away.

2. Yaw drift on Tab toggle: render loop stores rotation as Yaw - PI/2
   (AC model facing offset), but yaw extraction on re-entering player
   mode didn't compensate. Each Tab cycle rotated the player 90 degrees.
   Now adds PI/2 back when extracting from the quaternion.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-04-12 21:35:40 +02:00
parent a3b389603d
commit 29fe0c0714
2 changed files with 10 additions and 7 deletions

View file

@ -224,13 +224,15 @@ public sealed class GameWindow : IDisposable
playerEntity.Position, pinitCellId & 0xFFFFu, playerEntity.Position, pinitCellId & 0xFFFFu,
System.Numerics.Vector3.Zero, 100f); // huge step height for initial snap System.Numerics.Vector3.Zero, 100f); // huge step height for initial snap
_playerController.SetPosition(initResult.Position, initResult.CellId); _playerController.SetPosition(initResult.Position, initResult.CellId);
// Derive initial yaw from the entity's server-sent rotation // Derive initial yaw from the entity's rotation.
// rather than hardcoding. Extract yaw from the quaternion. // The render loop stores rotation as Yaw - PI/2 (to
// compensate for AC models facing +Y at identity), so
// we add PI/2 back when extracting to get the real yaw.
var q = playerEntity.Rotation; var q = playerEntity.Rotation;
float yaw = MathF.Atan2( float rawYaw = MathF.Atan2(
2f * (q.W * q.Z + q.X * q.Y), 2f * (q.W * q.Z + q.X * q.Y),
1f - 2f * (q.Y * q.Y + q.Z * q.Z)); 1f - 2f * (q.Y * q.Y + q.Z * q.Z));
_playerController.Yaw = yaw; _playerController.Yaw = rawYaw + MathF.PI / 2f;
_chaseCamera = new AcDream.App.Rendering.ChaseCamera _chaseCamera = new AcDream.App.Rendering.ChaseCamera
{ {
@ -1756,7 +1758,7 @@ public sealed class GameWindow : IDisposable
{ {
var camera = _cameraController.Active; var camera = _cameraController.Active;
var frustum = AcDream.App.Rendering.FrustumPlanes.FromViewProjection(camera.View * camera.Projection); var frustum = AcDream.App.Rendering.FrustumPlanes.FromViewProjection(camera.View * camera.Projection);
_terrain?.Draw(camera, frustum);
// Never cull the landblock the player is currently on. // Never cull the landblock the player is currently on.
uint? playerLb = null; uint? playerLb = null;
if (_playerMode && _playerController is not null) if (_playerMode && _playerController is not null)
@ -1766,6 +1768,7 @@ public sealed class GameWindow : IDisposable
int ply = _liveCenterY + (int)System.Math.Floor(pp.Y / 192f); int ply = _liveCenterY + (int)System.Math.Floor(pp.Y / 192f);
playerLb = (uint)((plx << 24) | (ply << 16) | 0xFFFF); playerLb = (uint)((plx << 24) | (ply << 16) | 0xFFFF);
} }
_terrain?.Draw(camera, frustum, neverCullLandblockId: playerLb);
_staticMesh?.Draw(camera, _worldState.LandblockEntries, frustum, _staticMesh?.Draw(camera, _worldState.LandblockEntries, frustum,
neverCullLandblockId: playerLb); neverCullLandblockId: playerLb);

View file

@ -116,7 +116,7 @@ public sealed unsafe class TerrainRenderer : IDisposable
_landblocks.Remove(landblockId); _landblocks.Remove(landblockId);
} }
public void Draw(ICamera camera, FrustumPlanes? frustum = null) public void Draw(ICamera camera, FrustumPlanes? frustum = null, uint? neverCullLandblockId = null)
{ {
_shader.Use(); _shader.Use();
_shader.SetMatrix4("uView", camera.View); _shader.SetMatrix4("uView", camera.View);
@ -135,7 +135,7 @@ public sealed unsafe class TerrainRenderer : IDisposable
foreach (var lb in _landblocks.Values) foreach (var lb in _landblocks.Values)
{ {
if (frustum is not null) if (frustum is not null && lb.LandblockId != neverCullLandblockId)
{ {
var aabbMin = new Vector3(lb.WorldOrigin.X, lb.WorldOrigin.Y, lb.MinZ); var aabbMin = new Vector3(lb.WorldOrigin.X, lb.WorldOrigin.Y, lb.MinZ);
var aabbMax = new Vector3(lb.WorldOrigin.X + 192f, lb.WorldOrigin.Y + 192f, lb.MaxZ); var aabbMax = new Vector3(lb.WorldOrigin.X + 192f, lb.WorldOrigin.Y + 192f, lb.MaxZ);