From d1f89dd4be83233e22cf5dd062b2581f3c6da062 Mon Sep 17 00:00:00 2001 From: metalgearsloth Date: Thu, 15 Aug 2024 22:17:12 +1000 Subject: [PATCH] navmap stuff --- .../CrewMonitoringNavMapControl.cs | 10 +- Content.Client/Pinpointer/NavMapData.cs | 389 ++++++++++++++++++ Content.Client/Pinpointer/UI/NavMapControl.cs | 374 +---------------- .../PowerMonitoringConsoleNavMapControl.cs | 46 +-- Content.Client/StationAi/StationAiOverlay.cs | 126 ++---- 5 files changed, 467 insertions(+), 478 deletions(-) create mode 100644 Content.Client/Pinpointer/NavMapData.cs diff --git a/Content.Client/Medical/CrewMonitoring/CrewMonitoringNavMapControl.cs b/Content.Client/Medical/CrewMonitoring/CrewMonitoringNavMapControl.cs index 340cc9af891..fb3b12122a0 100644 --- a/Content.Client/Medical/CrewMonitoring/CrewMonitoringNavMapControl.cs +++ b/Content.Client/Medical/CrewMonitoring/CrewMonitoringNavMapControl.cs @@ -15,9 +15,9 @@ public sealed partial class CrewMonitoringNavMapControl : NavMapControl public CrewMonitoringNavMapControl() : base() { - WallColor = new Color(192, 122, 196); - TileColor = new(71, 42, 72); - BackgroundColor = Color.FromSrgb(TileColor.WithAlpha(BackgroundOpacity)); + NavData.WallColor = Color.FromSrgb(new Color(192, 122, 196)); + NavData.TileColor = Color.FromSrgb(new(71, 42, 72)); + BackgroundColor = NavData.TileColor.WithAlpha(BackgroundOpacity); _trackedEntityLabel = new Label { @@ -41,7 +41,7 @@ public sealed partial class CrewMonitoringNavMapControl : NavMapControl }; _trackedEntityPanel.AddChild(_trackedEntityLabel); - this.AddChild(_trackedEntityPanel); + AddChild(_trackedEntityPanel); } protected override void FrameUpdate(FrameEventArgs args) @@ -56,7 +56,7 @@ public sealed partial class CrewMonitoringNavMapControl : NavMapControl return; } - foreach ((var netEntity, var blip) in TrackedEntities) + foreach (var (netEntity, blip) in TrackedEntities) { if (netEntity != Focus) continue; diff --git a/Content.Client/Pinpointer/NavMapData.cs b/Content.Client/Pinpointer/NavMapData.cs new file mode 100644 index 00000000000..c2bfaba4414 --- /dev/null +++ b/Content.Client/Pinpointer/NavMapData.cs @@ -0,0 +1,389 @@ +using System.Numerics; +using System.Runtime.InteropServices; +using Content.Shared.Atmos; +using Content.Shared.Pinpointer; +using Robust.Client.Graphics; +using Robust.Shared.Collections; +using Robust.Shared.Map.Components; +using Robust.Shared.Physics; +using Robust.Shared.Physics.Collision.Shapes; +using Robust.Shared.Utility; + +namespace Content.Client.Pinpointer; + +public sealed class NavMapData +{ + [Dependency] private readonly IEntityManager _entManager = default!; + + // Default colors + public Color WallColor = Color.ToSrgb(new Color(102, 217, 102)); + public Color TileColor = new(30, 67, 30); + + /// + /// Offset for the data to be drawn at. + /// + public Vector2 Offset; + + public List<(Vector2, Vector2)> TileLines = new(); + public List<(Vector2, Vector2)> TileRects = new(); + + public Dictionary TilePolygons = new(); + + private Dictionary _horizLines = new(); + private Dictionary _horizLinesReversed = new(); + private Dictionary _vertLines = new(); + private Dictionary _vertLinesReversed = new(); + + protected float FullWallInstep = 0.165f; + protected float ThinWallThickness = 0.165f; + protected float ThinDoorThickness = 0.30f; + + // TODO: Power should be updating it on its own. + /// + /// Called if navmap updates + /// + public event Action? OnUpdate; + + public NavMapData() + { + IoCManager.InjectDependencies(this); + } + + public void Draw(DrawingHandleBase handle, Func scale, Box2 localAABB) + { + var verts = new ValueList(TileLines.Count * 2); + + // Draw floor tiles + if (TilePolygons.Count != 0) + { + foreach (var tri in TilePolygons.Values) + { + verts.Clear(); + + foreach (var vert in tri) + { + verts.Add(new Vector2(vert.X, -vert.Y)); + } + + handle.DrawPrimitives(DrawPrimitiveTopology.TriangleFan, verts.Span, TileColor); + } + } + + // Draw map lines + if (TileLines.Count != 0) + { + verts.Clear(); + + foreach (var (o, t) in TileLines) + { + var origin = scale.Invoke(o - Offset); + var terminus = scale.Invoke(t - Offset); + + verts.Add(origin); + verts.Add(terminus); + } + + if (verts.Count > 0) + handle.DrawPrimitives(DrawPrimitiveTopology.LineList, verts.Span, WallColor); + } + + // Draw map rects + if (TileRects.Count != 0) + { + var rects = new ValueList(TileRects.Count * 8); + + foreach (var (lt, rb) in TileRects) + { + var leftTop = scale.Invoke(lt - Offset); + var rightBottom = scale.Invoke(rb - Offset); + + var rightTop = new Vector2(rightBottom.X, leftTop.Y); + var leftBottom = new Vector2(leftTop.X, rightBottom.Y); + + rects.Add(leftTop); + rects.Add(rightTop); + rects.Add(rightTop); + rects.Add(rightBottom); + rects.Add(rightBottom); + rects.Add(leftBottom); + rects.Add(leftBottom); + rects.Add(leftTop); + } + + if (rects.Count > 0) + handle.DrawPrimitives(DrawPrimitiveTopology.LineList, rects.Span, WallColor); + } + } + + public void UpdateNavMap(EntityUid entity) + { + // Clear stale values + TilePolygons.Clear(); + TileLines.Clear(); + TileRects.Clear(); + + UpdateNavMapFloorTiles(entity); + UpdateNavMapWallLines(entity); + UpdateNavMapAirlocks(entity); + } + + private void UpdateNavMapFloorTiles(Entity entity) + { + if (!_entManager.TryGetComponent(entity.Owner, out entity.Comp)) + { + return; + } + + var lookup = _entManager.System(); + var tiles = _entManager.System().GetAllTilesEnumerator(entity.Owner, entity.Comp); + + while (tiles.MoveNext(out var tile)) + { + var box = lookup.GetLocalBounds(tile.Value.GridIndices, entity.Comp.TileSize); + box = new Box2(box.Left, -box.Bottom, box.Right, -box.Top); + var arr = new Vector2[4]; + + arr[0] = box.BottomLeft; + arr[1] = box.BottomRight; + arr[2] = box.TopRight; + arr[3] = box.TopLeft; + + TilePolygons[tile.Value.GridIndices] = arr; + } + } + + private void UpdateNavMapWallLines(Entity entity) + { + if (!_entManager.TryGetComponent(entity.Owner, out entity.Comp1) || + !_entManager.TryGetComponent(entity.Owner, out entity.Comp2)) + { + return; + } + + // We'll use the following dictionaries to combine collinear wall lines + _horizLines.Clear(); + _horizLinesReversed.Clear(); + _vertLines.Clear(); + _vertLinesReversed.Clear(); + + const int southMask = (int) AtmosDirection.South << (int) NavMapChunkType.Wall; + const int eastMask = (int) AtmosDirection.East << (int) NavMapChunkType.Wall; + const int westMask = (int) AtmosDirection.West << (int) NavMapChunkType.Wall; + const int northMask = (int) AtmosDirection.North << (int) NavMapChunkType.Wall; + + foreach (var (chunkOrigin, chunk) in entity.Comp2.Chunks) + { + for (var i = 0; i < SharedNavMapSystem.ArraySize; i++) + { + var tileData = chunk.TileData[i] & SharedNavMapSystem.WallMask; + if (tileData == 0) + continue; + + tileData >>= (int) NavMapChunkType.Wall; + + var relativeTile = SharedNavMapSystem.GetTileFromIndex(i); + var tile = (chunk.Origin * SharedNavMapSystem.ChunkSize + relativeTile) * entity.Comp1.TileSize; + + if (tileData != SharedNavMapSystem.AllDirMask) + { + AddRectForThinWall(tileData, tile); + continue; + } + + tile = tile with { Y = -tile.Y }; + NavMapChunk? neighborChunk; + + // North edge + var neighborData = 0; + if (relativeTile.Y != SharedNavMapSystem.ChunkSize - 1) + neighborData = chunk.TileData[i+1]; + else if (entity.Comp2.Chunks.TryGetValue(chunkOrigin + Vector2i.Up, out neighborChunk)) + neighborData = neighborChunk.TileData[i + 1 - SharedNavMapSystem.ChunkSize]; + + if ((neighborData & southMask) == 0) + { + AddOrUpdateNavMapLine(tile + new Vector2i(0, -entity.Comp1.TileSize), + tile + new Vector2i(entity.Comp1.TileSize, -entity.Comp1.TileSize), _horizLines, + _horizLinesReversed); + } + + // East edge + neighborData = 0; + if (relativeTile.X != SharedNavMapSystem.ChunkSize - 1) + neighborData = chunk.TileData[i + SharedNavMapSystem.ChunkSize]; + else if (entity.Comp2.Chunks.TryGetValue(chunkOrigin + Vector2i.Right, out neighborChunk)) + neighborData = neighborChunk.TileData[i + SharedNavMapSystem.ChunkSize - SharedNavMapSystem.ArraySize]; + + if ((neighborData & westMask) == 0) + { + AddOrUpdateNavMapLine(tile + new Vector2i(entity.Comp1.TileSize, -entity.Comp1.TileSize), + tile + new Vector2i(entity.Comp1.TileSize, 0), _vertLines, _vertLinesReversed); + } + + // South edge + neighborData = 0; + if (relativeTile.Y != 0) + neighborData = chunk.TileData[i - 1]; + else if (entity.Comp2.Chunks.TryGetValue(chunkOrigin + Vector2i.Down, out neighborChunk)) + neighborData = neighborChunk.TileData[i - 1 + SharedNavMapSystem.ChunkSize]; + + if ((neighborData & northMask) == 0) + { + AddOrUpdateNavMapLine(tile, tile + new Vector2i(entity.Comp1.TileSize, 0), _horizLines, + _horizLinesReversed); + } + + // West edge + neighborData = 0; + if (relativeTile.X != 0) + neighborData = chunk.TileData[i - SharedNavMapSystem.ChunkSize]; + else if (entity.Comp2.Chunks.TryGetValue(chunkOrigin + Vector2i.Left, out neighborChunk)) + neighborData = neighborChunk.TileData[i - SharedNavMapSystem.ChunkSize + SharedNavMapSystem.ArraySize]; + + if ((neighborData & eastMask) == 0) + { + AddOrUpdateNavMapLine(tile + new Vector2i(0, -entity.Comp1.TileSize), tile, _vertLines, + _vertLinesReversed); + } + + // Add a diagonal line for interiors. Unless there are a lot of double walls, there is no point combining these + TileLines.Add((tile + new Vector2(0, -entity.Comp1.TileSize), tile + new Vector2(entity.Comp1.TileSize, 0))); + } + } + + // Record the combined lines + foreach (var (origin, terminal) in _horizLines) + { + TileLines.Add((origin, terminal)); + } + + foreach (var (origin, terminal) in _vertLines) + { + TileLines.Add((origin, terminal)); + } + } + + private void UpdateNavMapAirlocks(Entity entity) + { + if (!_entManager.TryGetComponent(entity.Owner, out entity.Comp1) || + !_entManager.TryGetComponent(entity.Owner, out entity.Comp2)) + { + return; + } + + foreach (var chunk in entity.Comp2.Chunks.Values) + { + for (var i = 0; i < SharedNavMapSystem.ArraySize; i++) + { + var tileData = chunk.TileData[i] & SharedNavMapSystem.AirlockMask; + if (tileData == 0) + continue; + + tileData >>= (int) NavMapChunkType.Airlock; + + var relative = SharedNavMapSystem.GetTileFromIndex(i); + var tile = (chunk.Origin * SharedNavMapSystem.ChunkSize + relative) * entity.Comp1.TileSize; + + // If the edges of an airlock tile are not all occupied, draw a thin airlock for each edge + if (tileData != SharedNavMapSystem.AllDirMask) + { + AddRectForThinAirlock(tileData, tile); + continue; + } + + // Otherwise add a single full tile airlock + TileRects.Add((new Vector2(tile.X + FullWallInstep, -tile.Y - FullWallInstep), + new Vector2(tile.X - FullWallInstep + 1f, -tile.Y + FullWallInstep - 1))); + + TileLines.Add((new Vector2(tile.X + 0.5f, -tile.Y - FullWallInstep), + new Vector2(tile.X + 0.5f, -tile.Y + FullWallInstep - 1))); + } + } + } + + private void AddRectForThinWall(int tileData, Vector2i tile) + { + var leftTop = new Vector2(-0.5f, 0.5f - ThinWallThickness); + var rightBottom = new Vector2(0.5f, 0.5f); + + for (var i = 0; i < SharedNavMapSystem.Directions; i++) + { + var dirMask = 1 << i; + if ((tileData & dirMask) == 0) + continue; + + var tilePosition = new Vector2(tile.X + 0.5f, -tile.Y - 0.5f); + + // TODO NAVMAP + // Consider using faster rotation operations, given that these are always 90 degree increments + var angle = -((AtmosDirection) dirMask).ToAngle(); + TileRects.Add((angle.RotateVec(leftTop) + tilePosition, angle.RotateVec(rightBottom) + tilePosition)); + } + } + + private void AddRectForThinAirlock(int tileData, Vector2i tile) + { + var leftTop = new Vector2(-0.5f + FullWallInstep, 0.5f - FullWallInstep - ThinDoorThickness); + var rightBottom = new Vector2(0.5f - FullWallInstep, 0.5f - FullWallInstep); + var centreTop = new Vector2(0f, 0.5f - FullWallInstep - ThinDoorThickness); + var centreBottom = new Vector2(0f, 0.5f - FullWallInstep); + + for (var i = 0; i < SharedNavMapSystem.Directions; i++) + { + var dirMask = 1 << i; + if ((tileData & dirMask) == 0) + continue; + + var tilePosition = new Vector2(tile.X + 0.5f, -tile.Y - 0.5f); + var angle = -((AtmosDirection) dirMask).ToAngle(); + TileRects.Add((angle.RotateVec(leftTop) + tilePosition, angle.RotateVec(rightBottom) + tilePosition)); + TileLines.Add((angle.RotateVec(centreTop) + tilePosition, angle.RotateVec(centreBottom) + tilePosition)); + } + } + + public void AddOrUpdateNavMapLine( + Vector2i origin, + Vector2i terminus, + Dictionary lookup, + Dictionary lookupReversed) + { + Vector2i foundTermius; + Vector2i foundOrigin; + + // Does our new line end at the beginning of an existing line? + if (lookup.Remove(terminus, out foundTermius)) + { + DebugTools.Assert(lookupReversed[foundTermius] == terminus); + + // Does our new line start at the end of an existing line? + if (lookupReversed.Remove(origin, out foundOrigin)) + { + // Our new line just connects two existing lines + DebugTools.Assert(lookup[foundOrigin] == origin); + lookup[foundOrigin] = foundTermius; + lookupReversed[foundTermius] = foundOrigin; + } + else + { + // Our new line precedes an existing line, extending it further to the left + lookup[origin] = foundTermius; + lookupReversed[foundTermius] = origin; + } + return; + } + + // Does our new line start at the end of an existing line? + if (lookupReversed.Remove(origin, out foundOrigin)) + { + // Our new line just extends an existing line further to the right + DebugTools.Assert(lookup[foundOrigin] == origin); + lookup[foundOrigin] = terminus; + lookupReversed[terminus] = foundOrigin; + return; + } + + // Completely disconnected line segment. + lookup.Add(origin, terminus); + lookupReversed.Add(terminus, origin); + } +} diff --git a/Content.Client/Pinpointer/UI/NavMapControl.cs b/Content.Client/Pinpointer/UI/NavMapControl.cs index 413b41c36a6..d7d821b37f5 100644 --- a/Content.Client/Pinpointer/UI/NavMapControl.cs +++ b/Content.Client/Pinpointer/UI/NavMapControl.cs @@ -45,13 +45,7 @@ public partial class NavMapControl : MapGridControl public Dictionary TrackedCoordinates = new(); public Dictionary TrackedEntities = new(); - public List<(Vector2, Vector2)> TileLines = new(); - public List<(Vector2, Vector2)> TileRects = new(); - public List<(Vector2[], Color)> TilePolygons = new(); - - // Default colors - public Color WallColor = new(102, 217, 102); - public Color TileColor = new(30, 67, 30); + public NavMapData NavData = new(); // Constants protected float UpdateTime = 1.0f; @@ -61,22 +55,13 @@ public partial class NavMapControl : MapGridControl protected static float MaxDisplayedRange = 128f; protected static float DefaultDisplayedRange = 48f; protected float MinmapScaleModifier = 0.075f; - protected float FullWallInstep = 0.165f; - protected float ThinWallThickness = 0.165f; - protected float ThinDoorThickness = 0.30f; // Local variables private float _updateTimer = 1.0f; - private Dictionary _sRGBLookUp = new(); protected Color BackgroundColor; protected float BackgroundOpacity = 0.9f; private int _targetFontsize = 8; - private Dictionary _horizLines = new(); - private Dictionary _horizLinesReversed = new(); - private Dictionary _vertLines = new(); - private Dictionary _vertLinesReversed = new(); - // Components private NavMapComponent? _navMap; private MapGridComponent? _grid; @@ -119,7 +104,7 @@ public partial class NavMapControl : MapGridControl _transformSystem = EntManager.System(); _navMapSystem = EntManager.System(); - BackgroundColor = Color.FromSrgb(TileColor.WithAlpha(BackgroundOpacity)); + BackgroundColor = Color.FromSrgb(NavData.TileColor.WithAlpha(BackgroundOpacity)); RectClipContent = true; HorizontalExpand = true; @@ -179,13 +164,12 @@ public partial class NavMapControl : MapGridControl public void ForceNavMapUpdate() { - EntManager.TryGetComponent(MapUid, out _navMap); - EntManager.TryGetComponent(MapUid, out _grid); - EntManager.TryGetComponent(MapUid, out _xform); - EntManager.TryGetComponent(MapUid, out _physics); - EntManager.TryGetComponent(MapUid, out _fixtures); + if (MapUid == null) + { + return; + } - UpdateNavMap(); + NavData.UpdateNavMap(MapUid.Value); } public void CenterToCoordinates(EntityCoordinates coordinates) @@ -228,7 +212,7 @@ public partial class NavMapControl : MapGridControl { if (!blip.Selectable) continue; - + var currentDistance = (_transformSystem.ToMapCoordinates(blip.Coordinates).Position - worldPosition).Length(); if (closestDistance < currentDistance || currentDistance * MinimapScale > MaxSelectableDistance) @@ -293,80 +277,11 @@ public partial class NavMapControl : MapGridControl if (_physics != null) offset += _physics.LocalCenter; - var offsetVec = new Vector2(offset.X, -offset.Y); - - // Wall sRGB - if (!_sRGBLookUp.TryGetValue(WallColor, out var wallsRGB)) - { - wallsRGB = Color.ToSrgb(WallColor); - _sRGBLookUp[WallColor] = wallsRGB; - } - - // Draw floor tiles - if (TilePolygons.Any()) - { - Span verts = new Vector2[8]; - - foreach (var (polygonVerts, polygonColor) in TilePolygons) - { - for (var i = 0; i < polygonVerts.Length; i++) - { - var vert = polygonVerts[i] - offset; - verts[i] = ScalePosition(new Vector2(vert.X, -vert.Y)); - } - - handle.DrawPrimitives(DrawPrimitiveTopology.TriangleFan, verts[..polygonVerts.Length], polygonColor); - } - } - - // Draw map lines - if (TileLines.Any()) - { - var lines = new ValueList(TileLines.Count * 2); - - foreach (var (o, t) in TileLines) - { - var origin = ScalePosition(o - offsetVec); - var terminus = ScalePosition(t - offsetVec); - - lines.Add(origin); - lines.Add(terminus); - } - - if (lines.Count > 0) - handle.DrawPrimitives(DrawPrimitiveTopology.LineList, lines.Span, wallsRGB); - } - - // Draw map rects - if (TileRects.Any()) - { - var rects = new ValueList(TileRects.Count * 8); - - foreach (var (lt, rb) in TileRects) - { - var leftTop = ScalePosition(lt - offsetVec); - var rightBottom = ScalePosition(rb - offsetVec); - - var rightTop = new Vector2(rightBottom.X, leftTop.Y); - var leftBottom = new Vector2(leftTop.X, rightBottom.Y); - - rects.Add(leftTop); - rects.Add(rightTop); - rects.Add(rightTop); - rects.Add(rightBottom); - rects.Add(rightBottom); - rects.Add(leftBottom); - rects.Add(leftBottom); - rects.Add(leftTop); - } - - if (rects.Count > 0) - handle.DrawPrimitives(DrawPrimitiveTopology.LineList, rects.Span, wallsRGB); - } + NavData.Offset = new Vector2(offset.X, -offset.Y); + NavData.Draw(handle, ScalePosition, Box2.UnitCentered); // Invoke post wall drawing action - if (PostWallDrawingAction != null) - PostWallDrawingAction.Invoke(handle); + PostWallDrawingAction?.Invoke(handle); // Beacons if (_beacons.Pressed) @@ -436,279 +351,18 @@ public partial class NavMapControl : MapGridControl protected override void FrameUpdate(FrameEventArgs args) { // Update the timer + // TODO: Sub to state changes. _updateTimer += args.DeltaSeconds; if (_updateTimer >= UpdateTime) { _updateTimer -= UpdateTime; - UpdateNavMap(); + if (MapUid != null) + NavData.UpdateNavMap(MapUid.Value); } } - protected virtual void UpdateNavMap() - { - // Clear stale values - TilePolygons.Clear(); - TileLines.Clear(); - TileRects.Clear(); - - UpdateNavMapFloorTiles(); - UpdateNavMapWallLines(); - UpdateNavMapAirlocks(); - } - - private void UpdateNavMapFloorTiles() - { - if (_fixtures == null) - return; - - var verts = new Vector2[8]; - - foreach (var fixture in _fixtures.Fixtures.Values) - { - if (fixture.Shape is not PolygonShape poly) - continue; - - for (var i = 0; i < poly.VertexCount; i++) - { - var vert = poly.Vertices[i]; - verts[i] = new Vector2(MathF.Round(vert.X), MathF.Round(vert.Y)); - } - - TilePolygons.Add((verts[..poly.VertexCount], TileColor)); - } - } - - private void UpdateNavMapWallLines() - { - if (_navMap == null || _grid == null) - return; - - // We'll use the following dictionaries to combine collinear wall lines - _horizLines.Clear(); - _horizLinesReversed.Clear(); - _vertLines.Clear(); - _vertLinesReversed.Clear(); - - const int southMask = (int) AtmosDirection.South << (int) NavMapChunkType.Wall; - const int eastMask = (int) AtmosDirection.East << (int) NavMapChunkType.Wall; - const int westMask = (int) AtmosDirection.West << (int) NavMapChunkType.Wall; - const int northMask = (int) AtmosDirection.North << (int) NavMapChunkType.Wall; - - foreach (var (chunkOrigin, chunk) in _navMap.Chunks) - { - for (var i = 0; i < SharedNavMapSystem.ArraySize; i++) - { - var tileData = chunk.TileData[i] & SharedNavMapSystem.WallMask; - if (tileData == 0) - continue; - - tileData >>= (int) NavMapChunkType.Wall; - - var relativeTile = SharedNavMapSystem.GetTileFromIndex(i); - var tile = (chunk.Origin * SharedNavMapSystem.ChunkSize + relativeTile) * _grid.TileSize; - - if (tileData != SharedNavMapSystem.AllDirMask) - { - AddRectForThinWall(tileData, tile); - continue; - } - - tile = tile with { Y = -tile.Y }; - NavMapChunk? neighborChunk; - - // North edge - var neighborData = 0; - if (relativeTile.Y != SharedNavMapSystem.ChunkSize - 1) - neighborData = chunk.TileData[i+1]; - else if (_navMap.Chunks.TryGetValue(chunkOrigin + Vector2i.Up, out neighborChunk)) - neighborData = neighborChunk.TileData[i + 1 - SharedNavMapSystem.ChunkSize]; - - if ((neighborData & southMask) == 0) - { - AddOrUpdateNavMapLine(tile + new Vector2i(0, -_grid.TileSize), - tile + new Vector2i(_grid.TileSize, -_grid.TileSize), _horizLines, - _horizLinesReversed); - } - - // East edge - neighborData = 0; - if (relativeTile.X != SharedNavMapSystem.ChunkSize - 1) - neighborData = chunk.TileData[i + SharedNavMapSystem.ChunkSize]; - else if (_navMap.Chunks.TryGetValue(chunkOrigin + Vector2i.Right, out neighborChunk)) - neighborData = neighborChunk.TileData[i + SharedNavMapSystem.ChunkSize - SharedNavMapSystem.ArraySize]; - - if ((neighborData & westMask) == 0) - { - AddOrUpdateNavMapLine(tile + new Vector2i(_grid.TileSize, -_grid.TileSize), - tile + new Vector2i(_grid.TileSize, 0), _vertLines, _vertLinesReversed); - } - - // South edge - neighborData = 0; - if (relativeTile.Y != 0) - neighborData = chunk.TileData[i - 1]; - else if (_navMap.Chunks.TryGetValue(chunkOrigin + Vector2i.Down, out neighborChunk)) - neighborData = neighborChunk.TileData[i - 1 + SharedNavMapSystem.ChunkSize]; - - if ((neighborData & northMask) == 0) - { - AddOrUpdateNavMapLine(tile, tile + new Vector2i(_grid.TileSize, 0), _horizLines, - _horizLinesReversed); - } - - // West edge - neighborData = 0; - if (relativeTile.X != 0) - neighborData = chunk.TileData[i - SharedNavMapSystem.ChunkSize]; - else if (_navMap.Chunks.TryGetValue(chunkOrigin + Vector2i.Left, out neighborChunk)) - neighborData = neighborChunk.TileData[i - SharedNavMapSystem.ChunkSize + SharedNavMapSystem.ArraySize]; - - if ((neighborData & eastMask) == 0) - { - AddOrUpdateNavMapLine(tile + new Vector2i(0, -_grid.TileSize), tile, _vertLines, - _vertLinesReversed); - } - - // Add a diagonal line for interiors. Unless there are a lot of double walls, there is no point combining these - TileLines.Add((tile + new Vector2(0, -_grid.TileSize), tile + new Vector2(_grid.TileSize, 0))); - } - } - - // Record the combined lines - foreach (var (origin, terminal) in _horizLines) - { - TileLines.Add((origin, terminal)); - } - - foreach (var (origin, terminal) in _vertLines) - { - TileLines.Add((origin, terminal)); - } - } - - private void UpdateNavMapAirlocks() - { - if (_navMap == null || _grid == null) - return; - - foreach (var chunk in _navMap.Chunks.Values) - { - for (var i = 0; i < SharedNavMapSystem.ArraySize; i++) - { - var tileData = chunk.TileData[i] & SharedNavMapSystem.AirlockMask; - if (tileData == 0) - continue; - - tileData >>= (int) NavMapChunkType.Airlock; - - var relative = SharedNavMapSystem.GetTileFromIndex(i); - var tile = (chunk.Origin * SharedNavMapSystem.ChunkSize + relative) * _grid.TileSize; - - // If the edges of an airlock tile are not all occupied, draw a thin airlock for each edge - if (tileData != SharedNavMapSystem.AllDirMask) - { - AddRectForThinAirlock(tileData, tile); - continue; - } - - // Otherwise add a single full tile airlock - TileRects.Add((new Vector2(tile.X + FullWallInstep, -tile.Y - FullWallInstep), - new Vector2(tile.X - FullWallInstep + 1f, -tile.Y + FullWallInstep - 1))); - - TileLines.Add((new Vector2(tile.X + 0.5f, -tile.Y - FullWallInstep), - new Vector2(tile.X + 0.5f, -tile.Y + FullWallInstep - 1))); - } - } - } - - private void AddRectForThinWall(int tileData, Vector2i tile) - { - var leftTop = new Vector2(-0.5f, 0.5f - ThinWallThickness); - var rightBottom = new Vector2(0.5f, 0.5f); - - for (var i = 0; i < SharedNavMapSystem.Directions; i++) - { - var dirMask = 1 << i; - if ((tileData & dirMask) == 0) - continue; - - var tilePosition = new Vector2(tile.X + 0.5f, -tile.Y - 0.5f); - - // TODO NAVMAP - // Consider using faster rotation operations, given that these are always 90 degree increments - var angle = -((AtmosDirection) dirMask).ToAngle(); - TileRects.Add((angle.RotateVec(leftTop) + tilePosition, angle.RotateVec(rightBottom) + tilePosition)); - } - } - - private void AddRectForThinAirlock(int tileData, Vector2i tile) - { - var leftTop = new Vector2(-0.5f + FullWallInstep, 0.5f - FullWallInstep - ThinDoorThickness); - var rightBottom = new Vector2(0.5f - FullWallInstep, 0.5f - FullWallInstep); - var centreTop = new Vector2(0f, 0.5f - FullWallInstep - ThinDoorThickness); - var centreBottom = new Vector2(0f, 0.5f - FullWallInstep); - - for (var i = 0; i < SharedNavMapSystem.Directions; i++) - { - var dirMask = 1 << i; - if ((tileData & dirMask) == 0) - continue; - - var tilePosition = new Vector2(tile.X + 0.5f, -tile.Y - 0.5f); - var angle = -((AtmosDirection) dirMask).ToAngle(); - TileRects.Add((angle.RotateVec(leftTop) + tilePosition, angle.RotateVec(rightBottom) + tilePosition)); - TileLines.Add((angle.RotateVec(centreTop) + tilePosition, angle.RotateVec(centreBottom) + tilePosition)); - } - } - - protected void AddOrUpdateNavMapLine( - Vector2i origin, - Vector2i terminus, - Dictionary lookup, - Dictionary lookupReversed) - { - Vector2i foundTermius; - Vector2i foundOrigin; - - // Does our new line end at the beginning of an existing line? - if (lookup.Remove(terminus, out foundTermius)) - { - DebugTools.Assert(lookupReversed[foundTermius] == terminus); - - // Does our new line start at the end of an existing line? - if (lookupReversed.Remove(origin, out foundOrigin)) - { - // Our new line just connects two existing lines - DebugTools.Assert(lookup[foundOrigin] == origin); - lookup[foundOrigin] = foundTermius; - lookupReversed[foundTermius] = foundOrigin; - } - else - { - // Our new line precedes an existing line, extending it further to the left - lookup[origin] = foundTermius; - lookupReversed[foundTermius] = origin; - } - return; - } - - // Does our new line start at the end of an existing line? - if (lookupReversed.Remove(origin, out foundOrigin)) - { - // Our new line just extends an existing line further to the right - DebugTools.Assert(lookup[foundOrigin] == origin); - lookup[foundOrigin] = terminus; - lookupReversed[terminus] = foundOrigin; - return; - } - - // Completely disconnected line segment. - lookup.Add(origin, terminus); - lookupReversed.Add(terminus, origin); - } - protected Vector2 GetOffset() { return Offset + (_physics?.LocalCenter ?? new Vector2()); diff --git a/Content.Client/Power/PowerMonitoringConsoleNavMapControl.cs b/Content.Client/Power/PowerMonitoringConsoleNavMapControl.cs index d5057416cf8..dd297788196 100644 --- a/Content.Client/Power/PowerMonitoringConsoleNavMapControl.cs +++ b/Content.Client/Power/PowerMonitoringConsoleNavMapControl.cs @@ -20,9 +20,7 @@ public sealed partial class PowerMonitoringConsoleNavMapControl : NavMapControl private readonly Color[] _powerCableColors = { Color.OrangeRed, Color.Yellow, Color.LimeGreen }; private readonly Vector2[] _powerCableOffsets = { new Vector2(-0.2f, -0.2f), Vector2.Zero, new Vector2(0.2f, 0.2f) }; - private Dictionary _sRGBLookUp = new Dictionary(); - public PowerMonitoringCableNetworksComponent? PowerMonitoringCableNetworks; public List HiddenLineGroups = new(); public List PowerCableNetwork = new(); public List FocusCableNetwork = new(); @@ -34,20 +32,19 @@ public sealed partial class PowerMonitoringConsoleNavMapControl : NavMapControl private MapGridComponent? _grid; - public PowerMonitoringConsoleNavMapControl() : base() + public PowerMonitoringConsoleNavMapControl() { // Set colors - TileColor = new Color(30, 57, 67); - WallColor = new Color(102, 164, 217); - BackgroundColor = Color.FromSrgb(TileColor.WithAlpha(BackgroundOpacity)); + NavData.TileColor = Color.FromSrgb(new Color(30, 57, 67)); + BackgroundColor = NavData.TileColor.WithAlpha(BackgroundOpacity); PostWallDrawingAction += DrawAllCableNetworks; + + NavData.OnUpdate += UpdateNavMap; } - protected override void UpdateNavMap() + private void UpdateNavMap() { - base.UpdateNavMap(); - if (Owner == null) return; @@ -64,14 +61,14 @@ public sealed partial class PowerMonitoringConsoleNavMapControl : NavMapControl return; // Draw full cable network - if (PowerCableNetwork != null && PowerCableNetwork.Count > 0) + if (PowerCableNetwork.Count > 0) { - var modulator = (FocusCableNetwork != null && FocusCableNetwork.Count > 0) ? Color.DimGray : Color.White; + var modulator = (FocusCableNetwork.Count > 0) ? Color.DimGray : Color.White; DrawCableNetwork(handle, PowerCableNetwork, modulator); } // Draw focus network - if (FocusCableNetwork != null && FocusCableNetwork.Count > 0) + if (FocusCableNetwork.Count > 0) DrawCableNetwork(handle, FocusCableNetwork, Color.White); } @@ -106,15 +103,8 @@ public sealed partial class PowerMonitoringConsoleNavMapControl : NavMapControl if (cableNetwork.Count > 0) { - var color = _powerCableColors[cableNetworkIdx] * modulator; - - if (!_sRGBLookUp.TryGetValue(color, out var sRGB)) - { - sRGB = Color.ToSrgb(color); - _sRGBLookUp[color] = sRGB; - } - - handle.DrawPrimitives(DrawPrimitiveTopology.LineList, cableNetwork.Span, sRGB); + var color = Color.ToSrgb(_powerCableColors[cableNetworkIdx] * modulator); + handle.DrawPrimitives(DrawPrimitiveTopology.LineList, cableNetwork.Span, color); } } } @@ -164,15 +154,9 @@ public sealed partial class PowerMonitoringConsoleNavMapControl : NavMapControl if (cableVertexUV.Count > 0) { - var color = _powerCableColors[cableNetworkIdx] * modulator; + var color = Color.ToSrgb(_powerCableColors[cableNetworkIdx] * modulator); - if (!_sRGBLookUp.TryGetValue(color, out var sRGB)) - { - sRGB = Color.ToSrgb(color); - _sRGBLookUp[color] = sRGB; - } - - handle.DrawPrimitives(DrawPrimitiveTopology.TriangleList, cableVertexUV.Span, sRGB); + handle.DrawPrimitives(DrawPrimitiveTopology.TriangleList, cableVertexUV.Span, color); } } } @@ -236,7 +220,7 @@ public sealed partial class PowerMonitoringConsoleNavMapControl : NavMapControl if (neighbor) { // Add points - AddOrUpdateNavMapLine(tile, tile + new Vector2i(_grid.TileSize, 0), horizLines, horizLinesReversed); + NavData.AddOrUpdateNavMapLine(tile, tile + new Vector2i(_grid.TileSize, 0), horizLines, horizLinesReversed); } // North @@ -254,7 +238,7 @@ public sealed partial class PowerMonitoringConsoleNavMapControl : NavMapControl if (neighbor) { // Add points - AddOrUpdateNavMapLine(tile + new Vector2i(0, -_grid.TileSize), tile, vertLines, vertLinesReversed); + NavData.AddOrUpdateNavMapLine(tile + new Vector2i(0, -_grid.TileSize), tile, vertLines, vertLinesReversed); } } diff --git a/Content.Client/StationAi/StationAiOverlay.cs b/Content.Client/StationAi/StationAiOverlay.cs index 522512b5537..a242fdb1da0 100644 --- a/Content.Client/StationAi/StationAiOverlay.cs +++ b/Content.Client/StationAi/StationAiOverlay.cs @@ -1,13 +1,14 @@ using System.Numerics; +using Content.Client.Pinpointer; using Content.Client.SurveillanceCamera; -using Content.Shared.StationAi; +using Content.Shared.NPC; using Robust.Client.GameObjects; using Robust.Client.Graphics; using Robust.Shared.Enums; using Robust.Shared.Map; +using Robust.Shared.Physics; +using Robust.Shared.Physics.Components; using Robust.Shared.Prototypes; -using Robust.Shared.Timing; -using Robust.Shared.Utility; namespace Content.Client.StationAi; @@ -20,10 +21,12 @@ public sealed class StationAiOverlay : Overlay public override OverlaySpace Space => OverlaySpace.WorldSpace; - private HashSet> _seeds = new(); + private HashSet> _seeds = new(); private IRenderTexture? _staticTexture; - public IRenderTexture? _stencilTexture; + public IRenderTexture? _blep; + + protected NavMapData _data = new(); public StationAiOverlay() { @@ -32,11 +35,11 @@ public sealed class StationAiOverlay : Overlay protected override void Draw(in OverlayDrawArgs args) { - if (_stencilTexture?.Texture.Size != args.Viewport.Size) + if (_blep?.Texture.Size != args.Viewport.Size) { _staticTexture?.Dispose(); - _stencilTexture?.Dispose(); - _stencilTexture = _clyde.CreateRenderTarget(args.Viewport.Size, new RenderTargetFormatParameters(RenderTargetColorFormat.Rgba8Srgb), name: "station-ai-stencil"); + _blep?.Dispose(); + _blep = _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"); @@ -61,10 +64,12 @@ public sealed class StationAiOverlay : Overlay var cleared = new HashSet(); var opaque = new HashSet(); var occluders = new HashSet>(); - var viewportTiles = new HashSet(); if (grid != null) { + _data.UpdateNavMap(gridUid); + _data.Offset = Vector2.Zero; + // Code based upon https://github.com/OpenDreamProject/OpenDream/blob/c4a3828ccb997bf3722673620460ebb11b95ccdf/OpenDreamShared/Dream/ViewAlgorithm.cs var tileEnumerator = maps.GetTilesEnumerator(gridUid, grid, expandedBounds, ignoreEmpty: false); @@ -100,15 +105,6 @@ public sealed class StationAiOverlay : Overlay foreach (var seed in _seeds) { - if (!seed.Comp.Enabled) - continue; - - // TODO: Iterate tiles direct. - if (!seed.Comp.Occluded) - { - - } - var range = 7.5f; boundary.Clear(); seedTiles.Clear(); @@ -220,76 +216,42 @@ public sealed class StationAiOverlay : Overlay visibleTiles.Add(tile); } } - - // TODO: Combine tiles into viewer draw-calls - var gridMatrix = xforms.GetWorldMatrix(gridUid); - var matty = Matrix3x2.Multiply(gridMatrix, invMatrix); - - // Draw visible tiles to stencil - worldHandle.RenderInRenderTarget(_stencilTexture!, () => - { - if (!gridUid.IsValid()) - return; - - worldHandle.SetTransform(matty); - - foreach (var tile in visibleTiles) - { - var aabb = lookups.GetLocalBounds(tile, grid!.TileSize); - worldHandle.DrawRect(aabb, Color.White); - } - }, - Color.Transparent); - - // Create static texture - var curTime = IoCManager.Resolve().RealTime; - - var noiseTexture = _entManager.System() - .GetFrame(new SpriteSpecifier.Rsi(new ResPath("/Textures/Interface/noise.rsi"), "noise"), curTime); - - // Once this is gucci optimise rendering. - worldHandle.RenderInRenderTarget(_staticTexture!, - () => - { - // TODO: Handle properly - if (!gridUid.IsValid()) - return; - - worldHandle.SetTransform(matty); - - tileEnumerator = maps.GetTilesEnumerator(gridUid, grid!, worldBounds, ignoreEmpty: false); - - while (tileEnumerator.MoveNext(out var tileRef)) - { - if (visibleTiles.Contains(tileRef.GridIndices)) - continue; - - var bounds = lookups.GetLocalBounds(tileRef, grid!.TileSize); - worldHandle.DrawTextureRect(noiseTexture, bounds, Color.White.WithAlpha(80)); - } - - }, - Color.Black); } - // Not on a grid - else + + // TODO: Combine tiles into viewer draw-calls + var matrix = xforms.GetWorldMatrix(gridUid); + var matty = Matrix3x2.Multiply(matrix, invMatrix); + + // Draw visible tiles to stencil + worldHandle.RenderInRenderTarget(_blep!, () => { - worldHandle.RenderInRenderTarget(_stencilTexture!, () => - { - }, - Color.Transparent); + if (!gridUid.IsValid()) + return; - worldHandle.RenderInRenderTarget(_staticTexture!, - () => - { - worldHandle.SetTransform(Matrix3x2.Identity); - worldHandle.DrawRect(worldBounds, Color.Black); - }, Color.Black); - } + worldHandle.SetTransform(matty); + + foreach (var tile in visibleTiles) + { + var aabb = lookups.GetLocalBounds(tile, grid!.TileSize); + worldHandle.DrawRect(aabb, Color.White); + } + }, + Color.Transparent); + + // Create background texture. + worldHandle.RenderInRenderTarget(_staticTexture!, + () => + { + worldHandle.SetTransform(invMatrix); + worldHandle.DrawRect(worldAabb, Color.Black); + worldHandle.SetTransform(matty); + + _data.Draw(worldHandle, vec => new Vector2(vec.X, -vec.Y), worldAabb); + }, Color.Transparent); // Use the lighting as a mask worldHandle.UseShader(_proto.Index("StencilMask").Instance()); - worldHandle.DrawTextureRect(_stencilTexture!.Texture, worldBounds); + worldHandle.DrawTextureRect(_blep!.Texture, worldBounds); // Draw the static worldHandle.UseShader(_proto.Index("StencilDraw").Instance());