navmap stuff

This commit is contained in:
metalgearsloth
2024-08-15 22:17:12 +10:00
parent 0a8d73fe41
commit d1f89dd4be
5 changed files with 467 additions and 478 deletions

View File

@@ -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;

View File

@@ -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);
/// <summary>
/// Offset for the data to be drawn at.
/// </summary>
public Vector2 Offset;
public List<(Vector2, Vector2)> TileLines = new();
public List<(Vector2, Vector2)> TileRects = new();
public Dictionary<Vector2i, Vector2[]> TilePolygons = new();
private Dictionary<Vector2i, Vector2i> _horizLines = new();
private Dictionary<Vector2i, Vector2i> _horizLinesReversed = new();
private Dictionary<Vector2i, Vector2i> _vertLines = new();
private Dictionary<Vector2i, Vector2i> _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.
/// <summary>
/// Called if navmap updates
/// </summary>
public event Action? OnUpdate;
public NavMapData()
{
IoCManager.InjectDependencies(this);
}
public void Draw(DrawingHandleBase handle, Func<Vector2, Vector2> scale, Box2 localAABB)
{
var verts = new ValueList<Vector2>(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<Vector2>(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<MapGridComponent?> entity)
{
if (!_entManager.TryGetComponent(entity.Owner, out entity.Comp))
{
return;
}
var lookup = _entManager.System<EntityLookupSystem>();
var tiles = _entManager.System<SharedMapSystem>().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<MapGridComponent?, NavMapComponent?> 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<MapGridComponent?, NavMapComponent?> 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<Vector2i, Vector2i> lookup,
Dictionary<Vector2i, Vector2i> 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);
}
}

View File

@@ -45,13 +45,7 @@ public partial class NavMapControl : MapGridControl
public Dictionary<EntityCoordinates, (bool Visible, Color Color)> TrackedCoordinates = new();
public Dictionary<NetEntity, NavMapBlip> 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<Color, Color> _sRGBLookUp = new();
protected Color BackgroundColor;
protected float BackgroundOpacity = 0.9f;
private int _targetFontsize = 8;
private Dictionary<Vector2i, Vector2i> _horizLines = new();
private Dictionary<Vector2i, Vector2i> _horizLinesReversed = new();
private Dictionary<Vector2i, Vector2i> _vertLines = new();
private Dictionary<Vector2i, Vector2i> _vertLinesReversed = new();
// Components
private NavMapComponent? _navMap;
private MapGridComponent? _grid;
@@ -119,7 +104,7 @@ public partial class NavMapControl : MapGridControl
_transformSystem = EntManager.System<SharedTransformSystem>();
_navMapSystem = EntManager.System<SharedNavMapSystem>();
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<Vector2> 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<Vector2>(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<Vector2>(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<Vector2i, Vector2i> lookup,
Dictionary<Vector2i, Vector2i> 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());

View File

@@ -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<Color, Color> _sRGBLookUp = new Dictionary<Color, Color>();
public PowerMonitoringCableNetworksComponent? PowerMonitoringCableNetworks;
public List<PowerMonitoringConsoleLineGroup> HiddenLineGroups = new();
public List<PowerMonitoringConsoleLine> PowerCableNetwork = new();
public List<PowerMonitoringConsoleLine> 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);
}
}

View File

@@ -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<Entity<StationAiVisionComponent>> _seeds = new();
private HashSet<Entity<SurveillanceCameraVisualsComponent>> _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<Vector2i>();
var opaque = new HashSet<Vector2i>();
var occluders = new HashSet<Entity<OccluderComponent>>();
var viewportTiles = new HashSet<Vector2i>();
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<IGameTiming>().RealTime;
var noiseTexture = _entManager.System<SpriteSystem>()
.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<ShaderPrototype>("StencilMask").Instance());
worldHandle.DrawTextureRect(_stencilTexture!.Texture, worldBounds);
worldHandle.DrawTextureRect(_blep!.Texture, worldBounds);
// Draw the static
worldHandle.UseShader(_proto.Index<ShaderPrototype>("StencilDraw").Instance());