feat(selection): port retail polygon picking and vivid marker
Replace the projected Setup-sphere rectangle and independent physics-wall ray with retail's render-coupled picker: only visible server-object parts participate, each exact drawing sphere broad-phases the camera-eye ray, and first-in-DAT-order visual polygon hits globally outrank sphere fallbacks. Replace the devtools-only procedural triangles with the retained gameplay VividTargetIndicator using retail client-enum surfaces 1..4, radar-blip colorization, Setup selection-sphere framing, and the exact eight-pixel viewport clamp. Release build succeeds with zero warnings and all 5,886 tests pass with five intentional skips. Co-authored-by: OpenAI Codex <codex@openai.com>
This commit is contained in:
parent
0f82a08f0a
commit
146a963aeb
26 changed files with 1302 additions and 1340 deletions
|
|
@ -0,0 +1,76 @@
|
|||
using System.Numerics;
|
||||
using AcDream.Core.Selection;
|
||||
using DatReaderWriter;
|
||||
using DatReaderWriter.DBObjs;
|
||||
|
||||
namespace AcDream.App.Rendering.Selection;
|
||||
|
||||
/// <summary>
|
||||
/// Decodes the exact CPU geometry consumed by retail mouse selection. The broad
|
||||
/// sphere comes from the drawing-BSP root; polygons retain GfxObj DAT order.
|
||||
/// </summary>
|
||||
internal sealed class RetailSelectionGeometryCache
|
||||
{
|
||||
private readonly DatCollection _dats;
|
||||
private readonly object _datLock;
|
||||
// Render-thread owned. Dictionary permits a cached null for a GfxObj which
|
||||
// legitimately has no drawing BSP; ConcurrentDictionary does not.
|
||||
private readonly Dictionary<uint, RetailSelectionMesh?> _cache = new();
|
||||
|
||||
public RetailSelectionGeometryCache(DatCollection dats, object datLock)
|
||||
{
|
||||
_dats = dats ?? throw new ArgumentNullException(nameof(dats));
|
||||
_datLock = datLock ?? throw new ArgumentNullException(nameof(datLock));
|
||||
}
|
||||
|
||||
public RetailSelectionMesh? Resolve(uint gfxObjId)
|
||||
{
|
||||
if (_cache.TryGetValue(gfxObjId, out var cached))
|
||||
return cached;
|
||||
RetailSelectionMesh? loaded = Load(gfxObjId);
|
||||
_cache[gfxObjId] = loaded;
|
||||
return loaded;
|
||||
}
|
||||
|
||||
private RetailSelectionMesh? Load(uint gfxObjId)
|
||||
{
|
||||
GfxObj? gfx;
|
||||
lock (_datLock)
|
||||
gfx = _dats.Get<GfxObj>(gfxObjId);
|
||||
|
||||
var root = gfx?.DrawingBSP?.Root;
|
||||
if (gfx is null || root is null || root.BoundingSphere.Radius <= 0f)
|
||||
return null;
|
||||
|
||||
var polygons = new List<RetailSelectionPolygon>(gfx.Polygons.Count);
|
||||
foreach (var entry in gfx.Polygons)
|
||||
{
|
||||
var source = entry.Value;
|
||||
if (source.VertexIds.Count < 3)
|
||||
continue;
|
||||
|
||||
var vertices = new Vector3[source.VertexIds.Count];
|
||||
bool valid = true;
|
||||
for (int i = 0; i < source.VertexIds.Count; i++)
|
||||
{
|
||||
if (!gfx.VertexArray.Vertices.TryGetValue((ushort)source.VertexIds[i], out var vertex))
|
||||
{
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
vertices[i] = vertex.Origin;
|
||||
}
|
||||
if (!valid)
|
||||
continue;
|
||||
|
||||
polygons.Add(new RetailSelectionPolygon(
|
||||
vertices,
|
||||
SingleSided: (int)source.SidesType == 0));
|
||||
}
|
||||
|
||||
return new RetailSelectionMesh(
|
||||
root.BoundingSphere.Origin,
|
||||
root.BoundingSphere.Radius,
|
||||
polygons);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue