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
|
|
@ -16,18 +16,32 @@ namespace AcDream.Core.Physics;
|
|||
public sealed class PhysicsDataCache
|
||||
{
|
||||
private readonly ConcurrentDictionary<uint, GfxObjPhysics> _gfxObj = new();
|
||||
private readonly ConcurrentDictionary<uint, GfxObjVisualBounds> _visualBounds = new();
|
||||
private readonly ConcurrentDictionary<uint, SetupPhysics> _setup = new();
|
||||
private readonly ConcurrentDictionary<uint, CellPhysics> _cellStruct = new();
|
||||
|
||||
/// <summary>
|
||||
/// Extract and cache the physics BSP + polygon data from a GfxObj.
|
||||
/// No-ops if the id is already cached or the GfxObj has no physics data.
|
||||
/// Extract and cache the physics BSP + polygon data from a GfxObj,
|
||||
/// PLUS always cache a visual AABB from the vertex data regardless of
|
||||
/// the HasPhysics flag. The visual AABB is used as a collision fallback
|
||||
/// for entities whose Setup has no retail physics data — it lets the
|
||||
/// user collide with decorative meshes that don't have a CylSphere or
|
||||
/// per-part BSP.
|
||||
/// </summary>
|
||||
public void CacheGfxObj(uint gfxObjId, GfxObj gfxObj)
|
||||
{
|
||||
// Always cache a visual AABB from the mesh vertices — this is cheap
|
||||
// and fed by the mesh data that's already loaded. It serves as the
|
||||
// fallback collision shape for pure-visual entities.
|
||||
if (!_visualBounds.ContainsKey(gfxObjId) && gfxObj.VertexArray != null)
|
||||
{
|
||||
_visualBounds[gfxObjId] = ComputeVisualBounds(gfxObj.VertexArray);
|
||||
}
|
||||
|
||||
if (_gfxObj.ContainsKey(gfxObjId)) return;
|
||||
if (!gfxObj.Flags.HasFlag(GfxObjFlags.HasPhysics)) return;
|
||||
if (gfxObj.PhysicsBSP?.Root is null) return;
|
||||
if (gfxObj.VertexArray is null) return;
|
||||
|
||||
_gfxObj[gfxObjId] = new GfxObjPhysics
|
||||
{
|
||||
|
|
@ -39,6 +53,58 @@ public sealed class PhysicsDataCache
|
|||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the cached visual AABB for a GfxObj, or null if not cached.
|
||||
/// </summary>
|
||||
public GfxObjVisualBounds? GetVisualBounds(uint gfxObjId) =>
|
||||
_visualBounds.TryGetValue(gfxObjId, out var vb) ? vb : null;
|
||||
|
||||
/// <summary>
|
||||
/// Compute a tight axis-aligned bounding box over all vertices in the mesh.
|
||||
/// Used as a fallback collision shape for entities whose Setup has no
|
||||
/// physics data — we approximate collision using the visual extent.
|
||||
/// </summary>
|
||||
private static GfxObjVisualBounds ComputeVisualBounds(VertexArray vertexArray)
|
||||
{
|
||||
if (vertexArray.Vertices == null || vertexArray.Vertices.Count == 0)
|
||||
{
|
||||
return new GfxObjVisualBounds
|
||||
{
|
||||
Min = Vector3.Zero,
|
||||
Max = Vector3.Zero,
|
||||
Center = Vector3.Zero,
|
||||
Radius = 0f,
|
||||
HalfExtents = Vector3.Zero,
|
||||
};
|
||||
}
|
||||
|
||||
var min = new Vector3(float.MaxValue);
|
||||
var max = new Vector3(float.MinValue);
|
||||
foreach (var kv in vertexArray.Vertices)
|
||||
{
|
||||
var p = kv.Value.Origin;
|
||||
if (p.X < min.X) min.X = p.X;
|
||||
if (p.Y < min.Y) min.Y = p.Y;
|
||||
if (p.Z < min.Z) min.Z = p.Z;
|
||||
if (p.X > max.X) max.X = p.X;
|
||||
if (p.Y > max.Y) max.Y = p.Y;
|
||||
if (p.Z > max.Z) max.Z = p.Z;
|
||||
}
|
||||
|
||||
var center = (min + max) * 0.5f;
|
||||
var halfExt = (max - min) * 0.5f;
|
||||
float radius = halfExt.Length();
|
||||
|
||||
return new GfxObjVisualBounds
|
||||
{
|
||||
Min = min,
|
||||
Max = max,
|
||||
Center = center,
|
||||
Radius = radius,
|
||||
HalfExtents = halfExt,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extract and cache the collision shape data from a Setup.
|
||||
/// No-ops if the id is already cached.
|
||||
|
|
@ -145,6 +211,26 @@ public sealed class PhysicsDataCache
|
|||
public int CellStructCount => _cellStruct.Count;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Visual AABB of a GfxObj mesh — populated for every cached GfxObj regardless
|
||||
/// of whether it has physics data. Used as a collision fallback shape for
|
||||
/// entities whose Setup has no CylSpheres/Spheres/Radius (pure decorative
|
||||
/// meshes). Provides an approximate cylinder matching the visible mesh extent.
|
||||
/// </summary>
|
||||
public sealed class GfxObjVisualBounds
|
||||
{
|
||||
/// <summary>Local-space minimum corner of the mesh AABB.</summary>
|
||||
public required Vector3 Min { get; init; }
|
||||
/// <summary>Local-space maximum corner of the mesh AABB.</summary>
|
||||
public required Vector3 Max { get; init; }
|
||||
/// <summary>Center of the local-space AABB.</summary>
|
||||
public required Vector3 Center { get; init; }
|
||||
/// <summary>Local-space radius (diagonal half-length) — loose bound.</summary>
|
||||
public required float Radius { get; init; }
|
||||
/// <summary>Local-space half-extents ((Max - Min) * 0.5).</summary>
|
||||
public required Vector3 HalfExtents { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A physics polygon with pre-resolved vertex positions and pre-computed plane.
|
||||
/// ACE pre-computes these in its Polygon constructor; we do it at cache time
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue