feat(selection): port retail offscreen target indicator

This commit is contained in:
Erik 2026-07-18 07:41:12 +02:00
parent ea4f52ec51
commit ec1bb19609
9 changed files with 404 additions and 97 deletions

View file

@ -2294,7 +2294,6 @@ public sealed class GameWindow : IDisposable
() => _playerServerGuid,
() => _persistedGameplay.VividTargetingIndicator,
ResolveVividTargetInfo,
guid => _retailSelectionScene?.WasVisible(guid) == true,
GetSelectionCamera),
Indicators: new AcDream.App.UI.IndicatorRuntimeBindings(
SpellBook,
@ -13667,7 +13666,7 @@ public sealed class GameWindow : IDisposable
private AcDream.App.UI.Layout.VividTargetInfo? ResolveVividTargetInfo(uint guid)
{
if (!_visibleEntitiesByServerGuid.ContainsKey(guid)
if (!_entitiesByServerGuid.ContainsKey(guid)
|| !TryGetEntitySelectionSphere(guid, out var center, out float radius))
return null;
@ -13715,17 +13714,25 @@ public sealed class GameWindow : IDisposable
worldCenter = default;
worldRadius = 0f;
if (!_visibleEntitiesByServerGuid.TryGetValue(guid, out var entity)) return false;
if (!LastSpawns.TryGetValue(guid, out var spawn)) return false;
if (spawn.SetupTableId is not uint setupId) return false;
if (_dats is null) return false;
if (!_dats.TryGet<DatReaderWriter.DBObjs.Setup>(setupId, out var setup)) return false;
if (!_entitiesByServerGuid.TryGetValue(guid, out var entity)) return false;
// SmartBox::GetObjectBoundingBox @ 0x00452E20 installs a 0.1-unit
// origin sphere when CPhysicsObj::GetSelectionSphere has no authored
// Setup sphere. Preserve that presentation for every materialized
// object rather than dropping the marker entirely.
worldCenter = entity.Position;
worldRadius = 0.1f;
if (!LastSpawns.TryGetValue(guid, out var spawn)
|| spawn.SetupTableId is not uint setupId
|| _dats is null
|| !_dats.TryGet<DatReaderWriter.DBObjs.Setup>(setupId, out var setup))
return true;
// DAT Setup carries `SelectionSphere` (Origin + Radius). A zero
// radius means the Setup didn't bake one — fall back to the
// caller's other path.
// radius means the Setup did not author one, so retain the retail
// 0.1-unit fallback installed above.
var sel = setup.SelectionSphere;
if (sel is null || sel.Radius <= 1e-4f) return false;
if (sel is null || sel.Radius <= 1e-4f) return true;
// Retail GetSelectionSphere applies part-array scale to the
// sphere center (component-wise) and to the radius (Z-scale

View file

@ -16,8 +16,6 @@ internal sealed class RetailSelectionScene : IRetailSelectionRenderSink, IRetail
private List<RetailSelectionPart> _building = new();
private List<RetailSelectionPart> _published = new();
private readonly HashSet<PartKey> _buildingKeys = new();
private HashSet<uint> _buildingGuids = new();
private HashSet<uint> _publishedGuids = new();
private FrustumPlanes? _viewFrustum;
private readonly record struct PartKey(uint LocalEntityId, int PartIndex, uint GfxObjId);
@ -47,7 +45,6 @@ internal sealed class RetailSelectionScene : IRetailSelectionRenderSink, IRetail
{
_building.Clear();
_buildingKeys.Clear();
_buildingGuids.Clear();
_viewFrustum = null;
}
@ -82,13 +79,11 @@ internal sealed class RetailSelectionScene : IRetailSelectionRenderSink, IRetail
partIndex,
partWorld,
mesh));
_buildingGuids.Add(entity.ServerGuid);
}
public void CompleteFrame()
{
(_published, _building) = (_building, _published);
(_publishedGuids, _buildingGuids) = (_buildingGuids, _publishedGuids);
}
public uint? Pick(
@ -107,8 +102,6 @@ internal sealed class RetailSelectionScene : IRetailSelectionRenderSink, IRetail
ray.Origin, ray.Direction, _published, skipServerGuid)?.ServerGuid;
}
public bool WasVisible(uint serverGuid) => _publishedGuids.Contains(serverGuid);
internal static bool DrawingSphereIntersectsFrustum(
RetailSelectionMesh mesh,
Matrix4x4 localToWorld,