From 6f63fea6e9245e189f368f97be3e32e9b210580e Mon Sep 17 00:00:00 2001 From: metalgearsloth Date: Fri, 23 Aug 2024 13:31:52 +1000 Subject: [PATCH] navmap work --- .../CrewMonitoringNavMapControl.cs | 10 +- Content.Client/Pinpointer/NavMapData.cs | 406 ++++++++++++++++++ Content.Client/Pinpointer/UI/NavMapControl.cs | 376 +--------------- .../PowerMonitoringConsoleNavMapControl.cs | 46 +- .../Silicons/StationAi/StationAiOverlay.cs | 43 +- 5 files changed, 471 insertions(+), 410 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 340cc9af891c..fb3b12122a07 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 CrewMonitoringNavMapControl() : base() }; _trackedEntityPanel.AddChild(_trackedEntityLabel); - this.AddChild(_trackedEntityPanel); + AddChild(_trackedEntityPanel); } protected override void FrameUpdate(FrameEventArgs args) @@ -56,7 +56,7 @@ protected override void FrameUpdate(FrameEventArgs args) 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 000000000000..b3953eff30ac --- /dev/null +++ b/Content.Client/Pinpointer/NavMapData.cs @@ -0,0 +1,406 @@ +using System.Numerics; +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.Timing; +using Robust.Shared.Utility; + +namespace Content.Client.Pinpointer; + +public sealed class NavMapData +{ + [Dependency] private readonly IEntityManager _entManager = default!; + + public Entity Entity; + + // Default colors + public Color WallColor = Color.ToSrgb(new Color(102, 217, 102)); + public Color TileColor = new Color(65, 252, 3); + + /// + /// 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; + + // TODO: Subscribe to statechanges on navmapcomponent + + public NavMapData() + { + IoCManager.InjectDependencies(this); + } + + public void Draw(DrawingHandleBase handle, Func scale, Box2 localAABB) + { + var verts = new ValueList(TileLines.Count * 2); + var maps = _entManager.System(); + + // Draw floor tiles + if (TilePolygons.Count != 0) + { + verts.Clear(); + var tilesEnumerator = maps.GetLocalTilesEnumerator(Entity, Entity, localAABB); + + while (tilesEnumerator.MoveNext(out var tileRef)) + { + if (!TilePolygons.TryGetValue(tileRef.GridIndices, out var polys)) + continue; + + for (var i = 0; i < polys.Length; i++) + { + verts.Add(scale.Invoke(polys[i])); + } + } + + handle.DrawPrimitives(DrawPrimitiveTopology.TriangleList, 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(Entity entity) + { + if (!_entManager.TryGetComponent(entity.Owner, out entity.Comp)) + return; + + Entity = (entity.Owner, entity.Comp); + + // Clear stale values + TilePolygons.Clear(); + TileLines.Clear(); + TileRects.Clear(); + + UpdateNavMapFloorTiles(entity.Owner); + UpdateNavMapWallLines((entity.Owner, entity.Comp, null)); + UpdateNavMapAirlocks((entity.Owner, entity.Comp, null)); + } + + 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).Enlarged(-0.45f); + box = new Box2(box.Left, -box.Bottom, box.Right, -box.Top); + var arr = new Vector2[6]; + + arr[0] = box.BottomLeft; + arr[1] = box.BottomRight; + arr[2] = box.TopLeft; + + arr[3] = box.BottomRight; + arr[4] = box.TopLeft; + arr[5] = box.TopRight; + + 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 413b41c36a6f..adbe9d724013 100644 --- a/Content.Client/Pinpointer/UI/NavMapControl.cs +++ b/Content.Client/Pinpointer/UI/NavMapControl.cs @@ -30,7 +30,6 @@ public partial class NavMapControl : MapGridControl { [Dependency] private IResourceCache _cache = default!; private readonly SharedTransformSystem _transformSystem; - private readonly SharedNavMapSystem _navMapSystem; public EntityUid? Owner; public EntityUid? MapUid; @@ -45,13 +44,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 +54,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; @@ -117,9 +101,8 @@ public NavMapControl() : base(MinDisplayedRange, MaxDisplayedRange, DefaultDispl IoCManager.InjectDependencies(this); _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 +162,12 @@ public NavMapControl() : base(MinDisplayedRange, MaxDisplayedRange, DefaultDispl 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 +210,7 @@ protected override void KeyBindUp(GUIBoundKeyEventArgs args) { if (!blip.Selectable) continue; - + var currentDistance = (_transformSystem.ToMapCoordinates(blip.Coordinates).Position - worldPosition).Length(); if (closestDistance < currentDistance || currentDistance * MinimapScale > MaxSelectableDistance) @@ -293,80 +275,11 @@ protected override void Draw(DrawingHandleScreen handle) 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 +349,18 @@ protected override void Draw(DrawingHandleScreen handle) protected override void FrameUpdate(FrameEventArgs args) { // Update the timer + // TODO: Sub to state changes. _updateTimer += args.DeltaSeconds; if (_updateTimer >= UpdateTime) { _updateTimer -= UpdateTime; - UpdateNavMap(); - } - } - - 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))); - } + if (MapUid != null) + NavData.UpdateNavMap(MapUid.Value); } } - 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 d5057416cf84..dd2977881965 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 void DrawAllCableNetworks(DrawingHandleScreen handle) 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 void DrawCableNetwork(DrawingHandleScreen handle, List 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 void DrawCableNetwork(DrawingHandleScreen handle, List 0) { - var color = _powerCableColors[cableNetworkIdx] * modulator; - - if (!_sRGBLookUp.TryGetValue(color, out var sRGB)) - { - sRGB = Color.ToSrgb(color); - _sRGBLookUp[color] = sRGB; - } + var color = Color.ToSrgb(_powerCableColors[cableNetworkIdx] * modulator); - handle.DrawPrimitives(DrawPrimitiveTopology.TriangleList, cableVertexUV.Span, sRGB); + handle.DrawPrimitives(DrawPrimitiveTopology.TriangleList, cableVertexUV.Span, color); } } } @@ -236,7 +220,7 @@ public List GetDecodedPowerCableChunks(Dictionary GetDecodedPowerCableChunks(Dictionary().FrameTime.TotalSeconds; + + if (_accumulator <= 0f) + { + _accumulator += UpdateRate; + // TODO: I hate this shit + _data.UpdateNavMap((gridUid, grid)); - _visibleTiles.Clear(); - _entManager.System().GetView((gridUid, grid), worldBounds, _visibleTiles); + // TODO: Pass in attached entity's grid. + // TODO: Credit OD on the moved to code + // TODO: Call the moved-to code here. - var gridMatrix = xforms.GetWorldMatrix(gridUid); + _visibleTiles.Clear(); + _entManager.System().GetView((gridUid, grid), worldBounds, _visibleTiles); + } + + var (_, _, gridMatrix, gridInvMatrix) = xforms.GetWorldPositionRotationMatrixWithInv(gridUid); var matty = Matrix3x2.Multiply(gridMatrix, invMatrix); // Draw visible tiles to stencil @@ -77,16 +95,17 @@ protected override void Draw(in OverlayDrawArgs args) }, Color.Transparent); - // Once this is gucci optimise rendering. + // Create background texture. worldHandle.RenderInRenderTarget(_staticTexture!, () => { worldHandle.SetTransform(invMatrix); - var shader = _proto.Index("CameraStatic").Instance(); - worldHandle.UseShader(shader); - worldHandle.DrawRect(worldBounds, Color.White); - }, - Color.Black); + worldHandle.DrawRect(worldBounds, Color.Black); + worldHandle.SetTransform(matty); + var localAABB = gridInvMatrix.TransformBox(worldBounds); + + _data.Draw(worldHandle, vec => new Vector2(vec.X, -vec.Y), localAABB); + }, Color.Transparent); } // Not on a grid else