Adds the first on-screen HUD for the dev client plus today's mouse-control refinements. Also lands yesterday's scenery-alignment changes that were left uncommitted in the working tree. Overlay: - BitmapFont rasterizes a system TTF via StbTrueTypeSharp into a 512x512 R8 atlas at startup (Consolas on Windows, DejaVu/Menlo fallbacks) - TextRenderer batches 2D quads in screen-space with ortho projection; one shader + two draw calls (rect then text) for panel backgrounds under glyphs - DebugOverlay composes info / stats / compass / help panels on top of the 3D scene; toggles via F1/F4/F5/F6; transient toasts for key events - DebugLineRenderer and its shaders (carried over from the scenery work) are properly committed in this commit Controls: - Per-mode mouse sensitivity (Chase 0.15, Fly 1.0, Orbit 1.0); F8/F9 to adjust the active mode multiplicatively (x1.2) - Hold RMB to free-orbit the chase camera around the player; release stays at the new angle (no snap-back) - Mouse-wheel zooms chase distance between 2m and 40m - Chase pitch widened to [-0.7, 1.4] so mouse-Y tilts both ways from the default neutral angle Scenery alignment (carried from yesterday's session): - ShadowObjectRegistry AllEntriesForDebug + Scale field - SceneryGenerator uses ACViewer's OnRoad polygon test + baseLoc + set_heading rotation - BSPQuery dispatchers accept localToWorld so normals/offsets transform correctly per part - TransitionTypes.CylinderCollision rewritten with wall-slide + push-out - PhysicsDataCache caches visual-mesh AABB for scenery that lacks physics Setup bounds
93 lines
3.5 KiB
C#
93 lines
3.5 KiB
C#
using System;
|
|
using System.Numerics;
|
|
|
|
namespace AcDream.App.Rendering;
|
|
|
|
/// <summary>
|
|
/// Third-person chase camera that follows behind and above a player
|
|
/// character. Implements <see cref="ICamera"/> so it plugs into the
|
|
/// existing renderer pipeline.
|
|
/// </summary>
|
|
public sealed class ChaseCamera : ICamera
|
|
{
|
|
public Vector3 Position { get; private set; }
|
|
public float Aspect { get; set; } = 16f / 9f;
|
|
public float FovY { get; set; } = MathF.PI / 3f;
|
|
|
|
/// <summary>Distance behind the player. Clamped to [<see cref="DistanceMin"/>, <see cref="DistanceMax"/>].</summary>
|
|
public float Distance { get; set; } = 8f;
|
|
public const float DistanceMin = 2f;
|
|
public const float DistanceMax = 40f;
|
|
|
|
/// <summary>Camera pitch above horizontal (radians). Positive = look down.</summary>
|
|
public float Pitch { get; set; } = 0.35f; // ~20 degrees
|
|
|
|
/// <summary>
|
|
/// Additional yaw applied on top of the player's heading when positioning
|
|
/// the camera. Used by the hold-RMB "inspect" mode to orbit around the
|
|
/// player without rotating the character. Snap to 0 to return the camera
|
|
/// to directly behind the player.
|
|
/// </summary>
|
|
public float YawOffset { get; set; } = 0f;
|
|
|
|
/// <summary>Vertical offset from the player's feet to the look-at point (eye height).</summary>
|
|
public float EyeHeight { get; set; } = 1.5f;
|
|
|
|
// Pitch range: negative values place the camera below the player's Z
|
|
// (at distance * sin(Pitch)) so the player can be viewed from a low
|
|
// angle. Clamped to -0.7 to avoid pushing the camera deep underground;
|
|
// at -0.7 and Distance=8 the camera is ~5m below player-Z which will
|
|
// clip terrain on hills but is OK on flat ground. 1.4 ≈ looking
|
|
// straight down. Wider than the old [0.05, 1.4] so mouse-Y moves the
|
|
// camera in both directions from the neutral [~20°] default.
|
|
private const float PitchMin = -0.7f;
|
|
private const float PitchMax = 1.4f;
|
|
|
|
private float _playerYaw;
|
|
private Vector3 _lookAt;
|
|
|
|
public Matrix4x4 View =>
|
|
Matrix4x4.CreateLookAt(Position, _lookAt, Vector3.UnitZ);
|
|
|
|
public Matrix4x4 Projection =>
|
|
Matrix4x4.CreatePerspectiveFieldOfView(FovY, Aspect, 1f, 5000f);
|
|
|
|
/// <summary>
|
|
/// Update the camera position to follow the player.
|
|
/// </summary>
|
|
public void Update(Vector3 playerPosition, float playerYaw)
|
|
{
|
|
_playerYaw = playerYaw;
|
|
_lookAt = playerPosition + new Vector3(0f, 0f, EyeHeight);
|
|
|
|
// Camera offset: behind the player (-forward direction) plus any
|
|
// YawOffset for the hold-RMB inspect orbit mode.
|
|
float effectiveYaw = playerYaw + YawOffset;
|
|
float forwardX = MathF.Cos(effectiveYaw);
|
|
float forwardY = MathF.Sin(effectiveYaw);
|
|
|
|
float horizontalDist = Distance * MathF.Cos(Pitch);
|
|
float verticalDist = Distance * MathF.Sin(Pitch);
|
|
|
|
Position = new Vector3(
|
|
playerPosition.X - forwardX * horizontalDist,
|
|
playerPosition.Y - forwardY * horizontalDist,
|
|
playerPosition.Z + EyeHeight + verticalDist);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adjust pitch by a delta (from mouse Y movement).
|
|
/// </summary>
|
|
public void AdjustPitch(float delta)
|
|
{
|
|
Pitch = Math.Clamp(Pitch + delta, PitchMin, PitchMax);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adjust distance (zoom) by a delta, clamped to [DistanceMin, DistanceMax].
|
|
/// </summary>
|
|
public void AdjustDistance(float delta)
|
|
{
|
|
Distance = Math.Clamp(Distance + delta, DistanceMin, DistanceMax);
|
|
}
|
|
}
|