feat(ui): debug overlay + refined input controls
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
This commit is contained in:
parent
6b4e7569a3
commit
ff325abd7b
20 changed files with 2734 additions and 268 deletions
172
src/AcDream.App/Rendering/DebugLineRenderer.cs
Normal file
172
src/AcDream.App/Rendering/DebugLineRenderer.cs
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using System.Runtime.InteropServices;
|
||||
using Silk.NET.OpenGL;
|
||||
|
||||
namespace AcDream.App.Rendering;
|
||||
|
||||
/// <summary>
|
||||
/// Minimal GL debug line renderer for visualizing collision shapes,
|
||||
/// bounding boxes, and other debug geometry. Collect lines each frame
|
||||
/// via <see cref="AddLine"/> / <see cref="AddCylinder"/>, then call
|
||||
/// <see cref="Flush"/> to upload + draw them.
|
||||
///
|
||||
/// Uses a single shared VBO that's respecialized each frame. Vertex
|
||||
/// format is (vec3 pos, vec3 color) = 24 bytes per vertex.
|
||||
/// </summary>
|
||||
public sealed unsafe class DebugLineRenderer : IDisposable
|
||||
{
|
||||
private readonly GL _gl;
|
||||
private readonly Shader _shader;
|
||||
private readonly uint _vao;
|
||||
private readonly uint _vbo;
|
||||
|
||||
private readonly List<float> _buffer = new(4096);
|
||||
private int _vertexCount;
|
||||
private int _capacityBytes;
|
||||
|
||||
public DebugLineRenderer(GL gl, string shaderDir)
|
||||
{
|
||||
_gl = gl;
|
||||
_shader = new Shader(gl,
|
||||
Path.Combine(shaderDir, "debug_line.vert"),
|
||||
Path.Combine(shaderDir, "debug_line.frag"));
|
||||
|
||||
_vao = _gl.GenVertexArray();
|
||||
_vbo = _gl.GenBuffer();
|
||||
|
||||
_gl.BindVertexArray(_vao);
|
||||
_gl.BindBuffer(BufferTargetARB.ArrayBuffer, _vbo);
|
||||
|
||||
// 24-byte stride: vec3 pos + vec3 color
|
||||
_gl.EnableVertexAttribArray(0);
|
||||
_gl.VertexAttribPointer(0, 3, VertexAttribPointerType.Float, false, 6 * sizeof(float), (void*)0);
|
||||
_gl.EnableVertexAttribArray(1);
|
||||
_gl.VertexAttribPointer(1, 3, VertexAttribPointerType.Float, false, 6 * sizeof(float), (void*)(3 * sizeof(float)));
|
||||
|
||||
_gl.BindBuffer(BufferTargetARB.ArrayBuffer, 0);
|
||||
_gl.BindVertexArray(0);
|
||||
}
|
||||
|
||||
/// <summary>Clear accumulated lines. Call at the start of each frame.</summary>
|
||||
public void Begin()
|
||||
{
|
||||
_buffer.Clear();
|
||||
_vertexCount = 0;
|
||||
}
|
||||
|
||||
public void AddLine(Vector3 a, Vector3 b, Vector3 color)
|
||||
{
|
||||
_buffer.Add(a.X); _buffer.Add(a.Y); _buffer.Add(a.Z);
|
||||
_buffer.Add(color.X); _buffer.Add(color.Y); _buffer.Add(color.Z);
|
||||
_buffer.Add(b.X); _buffer.Add(b.Y); _buffer.Add(b.Z);
|
||||
_buffer.Add(color.X); _buffer.Add(color.Y); _buffer.Add(color.Z);
|
||||
_vertexCount += 2;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draw a cylinder as 2 polygon rings (base + top) connected by 4
|
||||
/// vertical line segments at 0/90/180/270 degrees.
|
||||
/// </summary>
|
||||
public void AddCylinder(Vector3 basePos, float radius, float height, Vector3 color)
|
||||
{
|
||||
const int segments = 16;
|
||||
Vector3 top = basePos + new Vector3(0, 0, height);
|
||||
|
||||
// Ring vertices
|
||||
var baseRing = new Vector3[segments];
|
||||
var topRing = new Vector3[segments];
|
||||
for (int i = 0; i < segments; i++)
|
||||
{
|
||||
float theta = i * (MathF.PI * 2f / segments);
|
||||
float cx = MathF.Cos(theta) * radius;
|
||||
float cy = MathF.Sin(theta) * radius;
|
||||
baseRing[i] = new Vector3(basePos.X + cx, basePos.Y + cy, basePos.Z);
|
||||
topRing[i] = new Vector3(top.X + cx, top.Y + cy, top.Z);
|
||||
}
|
||||
|
||||
// Base ring
|
||||
for (int i = 0; i < segments; i++)
|
||||
AddLine(baseRing[i], baseRing[(i + 1) % segments], color);
|
||||
// Top ring
|
||||
for (int i = 0; i < segments; i++)
|
||||
AddLine(topRing[i], topRing[(i + 1) % segments], color);
|
||||
// 4 vertical connectors
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
int idx = i * (segments / 4);
|
||||
AddLine(baseRing[idx], topRing[idx], color);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draw an axis-aligned box as 12 edges.
|
||||
/// </summary>
|
||||
public void AddBox(Vector3 min, Vector3 max, Vector3 color)
|
||||
{
|
||||
Vector3[] c =
|
||||
{
|
||||
new(min.X, min.Y, min.Z),
|
||||
new(max.X, min.Y, min.Z),
|
||||
new(max.X, max.Y, min.Z),
|
||||
new(min.X, max.Y, min.Z),
|
||||
new(min.X, min.Y, max.Z),
|
||||
new(max.X, min.Y, max.Z),
|
||||
new(max.X, max.Y, max.Z),
|
||||
new(min.X, max.Y, max.Z),
|
||||
};
|
||||
// Bottom
|
||||
AddLine(c[0], c[1], color); AddLine(c[1], c[2], color);
|
||||
AddLine(c[2], c[3], color); AddLine(c[3], c[0], color);
|
||||
// Top
|
||||
AddLine(c[4], c[5], color); AddLine(c[5], c[6], color);
|
||||
AddLine(c[6], c[7], color); AddLine(c[7], c[4], color);
|
||||
// Verticals
|
||||
AddLine(c[0], c[4], color); AddLine(c[1], c[5], color);
|
||||
AddLine(c[2], c[6], color); AddLine(c[3], c[7], color);
|
||||
}
|
||||
|
||||
/// <summary>Upload + draw all accumulated lines.</summary>
|
||||
public void Flush(Matrix4x4 view, Matrix4x4 projection)
|
||||
{
|
||||
if (_vertexCount == 0) return;
|
||||
|
||||
_shader.Use();
|
||||
_shader.SetMatrix4("uView", view);
|
||||
_shader.SetMatrix4("uProjection", projection);
|
||||
|
||||
_gl.BindVertexArray(_vao);
|
||||
_gl.BindBuffer(BufferTargetARB.ArrayBuffer, _vbo);
|
||||
|
||||
int neededBytes = _buffer.Count * sizeof(float);
|
||||
if (neededBytes > _capacityBytes)
|
||||
{
|
||||
fixed (float* ptr = CollectionsMarshal.AsSpan(_buffer))
|
||||
_gl.BufferData(BufferTargetARB.ArrayBuffer, (nuint)neededBytes, ptr, BufferUsageARB.DynamicDraw);
|
||||
_capacityBytes = neededBytes;
|
||||
}
|
||||
else
|
||||
{
|
||||
fixed (float* ptr = CollectionsMarshal.AsSpan(_buffer))
|
||||
_gl.BufferSubData(BufferTargetARB.ArrayBuffer, 0, (nuint)neededBytes, ptr);
|
||||
}
|
||||
|
||||
// Depth test on so lines get occluded by geometry (but we want them
|
||||
// visible through geometry — disable depth test so everything shows).
|
||||
bool wasDepthEnabled = _gl.IsEnabled(EnableCap.DepthTest);
|
||||
_gl.Disable(EnableCap.DepthTest);
|
||||
|
||||
_gl.DrawArrays(PrimitiveType.Lines, 0, (uint)_vertexCount);
|
||||
|
||||
if (wasDepthEnabled) _gl.Enable(EnableCap.DepthTest);
|
||||
|
||||
_gl.BindVertexArray(0);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_gl.DeleteVertexArray(_vao);
|
||||
_gl.DeleteBuffer(_vbo);
|
||||
_shader.Dispose();
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue