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
|
|
@ -7,6 +7,7 @@ using AcDream.Core.Meshing;
|
|||
using AcDream.Core.Rendering;
|
||||
using AcDream.Core.Terrain;
|
||||
using AcDream.Core.World;
|
||||
using AcDream.App.Rendering.Selection;
|
||||
using DatReaderWriter.Enums;
|
||||
using Silk.NET.OpenGL;
|
||||
|
||||
|
|
@ -85,6 +86,7 @@ public sealed unsafe class WbDrawDispatcher : IDisposable
|
|||
private readonly TextureCache _textures;
|
||||
private readonly WbMeshAdapter _meshAdapter;
|
||||
private readonly EntitySpawnAdapter _entitySpawnAdapter;
|
||||
private readonly IRetailSelectionRenderSink? _selectionSink;
|
||||
|
||||
private readonly BindlessSupport _bindless;
|
||||
|
||||
|
|
@ -354,7 +356,8 @@ public sealed unsafe class WbDrawDispatcher : IDisposable
|
|||
EntitySpawnAdapter entitySpawnAdapter,
|
||||
BindlessSupport bindless,
|
||||
EntityClassificationCache classificationCache,
|
||||
AcDream.Core.Rendering.TranslucencyFadeManager translucencyFades)
|
||||
AcDream.Core.Rendering.TranslucencyFadeManager translucencyFades,
|
||||
IRetailSelectionRenderSink? selectionSink = null)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(gl);
|
||||
ArgumentNullException.ThrowIfNull(shader);
|
||||
|
|
@ -371,6 +374,7 @@ public sealed unsafe class WbDrawDispatcher : IDisposable
|
|||
_entitySpawnAdapter = entitySpawnAdapter;
|
||||
_cache = classificationCache;
|
||||
_translucencyFades = translucencyFades;
|
||||
_selectionSink = selectionSink;
|
||||
|
||||
_bindless = bindless ?? throw new ArgumentNullException(nameof(bindless));
|
||||
_instanceSsbo = _gl.GenBuffer();
|
||||
|
|
@ -1187,6 +1191,13 @@ public sealed unsafe class WbDrawDispatcher : IDisposable
|
|||
{
|
||||
ApplyCacheHit(cachedEntry!, entityWorld, AppendInstanceToGroup);
|
||||
|
||||
// The cache is populated only after every MeshRef rendered
|
||||
// successfully. Publish the same parts for retail picking now;
|
||||
// CPhysicsPart::Draw only participates after the visible draw
|
||||
// path has accepted a real part.
|
||||
if (_selectionSink is not null)
|
||||
PublishCachedSelectionParts(entity, entityWorld);
|
||||
|
||||
// anyVao recovery: when the first visible entity in the frame
|
||||
// takes the fast path, no slow-path lookup has populated
|
||||
// anyVao yet. Look up THIS entity's first MeshRef once via
|
||||
|
|
@ -1374,6 +1385,11 @@ public sealed unsafe class WbDrawDispatcher : IDisposable
|
|||
}
|
||||
|
||||
ClassifyBatches(partData, partGfxObjId, model, entity, meshRef, palHash, metaTable, restPose, opacityMultiplier, collector);
|
||||
_selectionSink?.AddVisiblePart(
|
||||
entity,
|
||||
unchecked((partIdx << 16) | (setupPartIndex & 0xFFFF)),
|
||||
(uint)partGfxObjId,
|
||||
model);
|
||||
drewAny = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -1394,6 +1410,11 @@ public sealed unsafe class WbDrawDispatcher : IDisposable
|
|||
{
|
||||
var model = meshRef.PartTransform * entityWorld;
|
||||
ClassifyBatches(renderData, gfxObjId, model, entity, meshRef, palHash, metaTable, restPose: meshRef.PartTransform, opacityMultiplier: opacityMultiplier, collector: collector);
|
||||
_selectionSink?.AddVisiblePart(
|
||||
entity,
|
||||
partIdx,
|
||||
(uint)gfxObjId,
|
||||
model);
|
||||
drewAny = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -1813,6 +1834,44 @@ public sealed unsafe class WbDrawDispatcher : IDisposable
|
|||
set: set);
|
||||
}
|
||||
|
||||
private void PublishCachedSelectionParts(WorldEntity entity, Matrix4x4 entityWorld)
|
||||
{
|
||||
for (int outerPartIndex = 0; outerPartIndex < entity.MeshRefs.Count; outerPartIndex++)
|
||||
{
|
||||
var meshRef = entity.MeshRefs[outerPartIndex];
|
||||
var renderData = _meshAdapter.TryGetRenderData(meshRef.GfxObjId);
|
||||
if (renderData is null)
|
||||
continue;
|
||||
|
||||
if (!renderData.IsSetup || renderData.SetupParts.Count == 0)
|
||||
{
|
||||
_selectionSink!.AddVisiblePart(
|
||||
entity,
|
||||
outerPartIndex,
|
||||
meshRef.GfxObjId,
|
||||
meshRef.PartTransform * entityWorld);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int setupPartIndex = 0;
|
||||
setupPartIndex < renderData.SetupParts.Count;
|
||||
setupPartIndex++)
|
||||
{
|
||||
var (partGfxObjId, partTransform) = renderData.SetupParts[setupPartIndex];
|
||||
if (_meshAdapter.TryGetRenderData(partGfxObjId) is null)
|
||||
continue;
|
||||
_selectionSink!.AddVisiblePart(
|
||||
entity,
|
||||
unchecked((outerPartIndex << 16) | (setupPartIndex & 0xFFFF)),
|
||||
(uint)partGfxObjId,
|
||||
ComposePartWorldMatrix(
|
||||
entityWorld,
|
||||
meshRef.PartTransform,
|
||||
partTransform));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static IndirectGroupInput ToInput(InstanceGroup g) => new(
|
||||
IndexCount: g.IndexCount,
|
||||
FirstIndex: g.FirstIndex,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue