using System.Numerics; using Content.Shared.Silicons.StationAi; using Robust.Client.Graphics; using Robust.Client.Player; using Robust.Shared.Enums; using Robust.Shared.Map.Components; using Robust.Shared.Physics; using Robust.Shared.Prototypes; using Robust.Shared.Timing; namespace Content.Client.Silicons.StationAi; public sealed class StationAiOverlay : Overlay { [Dependency] private readonly IClyde _clyde = default!; [Dependency] private readonly IEntityManager _entManager = default!; [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly IPlayerManager _player = default!; [Dependency] private readonly IPrototypeManager _proto = default!; public override OverlaySpace Space => OverlaySpace.WorldSpace; private readonly HashSet _visibleTiles = new(); private IRenderTexture? _staticTexture; private IRenderTexture? _stencilTexture; private float _updateRate = 1f / 30f; private float _accumulator; public StationAiOverlay() { IoCManager.InjectDependencies(this); } protected override void Draw(in OverlayDrawArgs args) { if (_stencilTexture?.Texture.Size != args.Viewport.Size) { _staticTexture?.Dispose(); _stencilTexture?.Dispose(); _stencilTexture = _clyde.CreateRenderTarget(args.Viewport.Size, new RenderTargetFormatParameters(RenderTargetColorFormat.Rgba8Srgb), name: "station-ai-stencil"); _staticTexture = _clyde.CreateRenderTarget(args.Viewport.Size, new RenderTargetFormatParameters(RenderTargetColorFormat.Rgba8Srgb), name: "station-ai-static"); } var worldHandle = args.WorldHandle; var worldBounds = args.WorldBounds; var playerEnt = _player.LocalEntity; _entManager.TryGetComponent(playerEnt, out TransformComponent? playerXform); var gridUid = playerXform?.GridUid ?? EntityUid.Invalid; _entManager.TryGetComponent(gridUid, out MapGridComponent? grid); _entManager.TryGetComponent(gridUid, out BroadphaseComponent? broadphase); var invMatrix = args.Viewport.GetWorldToLocalMatrix(); _accumulator -= (float) _timing.FrameTime.TotalSeconds; if (grid != null && broadphase != null) { var lookups = _entManager.System(); var xforms = _entManager.System(); if (_accumulator <= 0f) { _accumulator = MathF.Max(0f, _accumulator + _updateRate); _visibleTiles.Clear(); _entManager.System().GetView((gridUid, broadphase, grid), worldBounds, _visibleTiles); } var gridMatrix = xforms.GetWorldMatrix(gridUid); var matty = Matrix3x2.Multiply(gridMatrix, invMatrix); // Draw visible tiles to stencil worldHandle.RenderInRenderTarget(_stencilTexture!, () => { worldHandle.SetTransform(matty); foreach (var tile in _visibleTiles) { var aabb = lookups.GetLocalBounds(tile, grid.TileSize); worldHandle.DrawRect(aabb, Color.White); } }, Color.Transparent); // Once this is gucci optimise rendering. worldHandle.RenderInRenderTarget(_staticTexture!, () => { worldHandle.SetTransform(invMatrix); var shader = _proto.Index("CameraStatic").Instance(); worldHandle.UseShader(shader); worldHandle.DrawRect(worldBounds, Color.White); }, Color.Black); } // Not on a grid else { worldHandle.RenderInRenderTarget(_stencilTexture!, () => { }, Color.Transparent); worldHandle.RenderInRenderTarget(_staticTexture!, () => { worldHandle.SetTransform(Matrix3x2.Identity); worldHandle.DrawRect(worldBounds, Color.Black); }, Color.Black); } // Use the lighting as a mask worldHandle.UseShader(_proto.Index("StencilMask").Instance()); worldHandle.DrawTextureRect(_stencilTexture!.Texture, worldBounds); // Draw the static worldHandle.UseShader(_proto.Index("StencilDraw").Instance()); worldHandle.DrawTextureRect(_staticTexture!.Texture, worldBounds); worldHandle.SetTransform(Matrix3x2.Identity); worldHandle.UseShader(null); } }