Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
253 changes: 250 additions & 3 deletions Robust.Client/Graphics/Clyde/Clyde.GridRendering.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Numerics;
using OpenToolkit.Graphics.OpenGL4;
using Robust.Client.ResourceManagement;
using Robust.Shared.Collections;
using Robust.Shared.Enums;
using Robust.Shared.GameObjects;
using Robust.Shared.Graphics;
Expand Down Expand Up @@ -157,6 +158,16 @@ private void _drawGrids(Viewport viewport, Box2 worldAABB, Box2Rotated worldBoun
GL.DrawElements(GetQuadGLPrimitiveType(), datum.EdgeCount * GetQuadBatchIndexCount(), DrawElementsType.UnsignedShort, 0);
CheckGlError();
}

if (_drawTileEdges && datum.InteriorEdgeCount > 0)
{
BindVertexArray(datum.InteriorEdgeVAO);
CheckGlError();

_debugStats.LastGLDrawCalls += 1;
GL.DrawElements(GetQuadGLPrimitiveType(), datum.InteriorEdgeCount * GetQuadBatchIndexCount(), DrawElementsType.UnsignedShort, 0);
CheckGlError();
}
}

requiresFlush = false;
Expand Down Expand Up @@ -279,6 +290,14 @@ private void _updateChunkMesh(Entity<MapGridComponent> grid, MapChunk chunk, Map
}

private void _updateChunkEdges(Entity<MapGridComponent> grid, MapChunk chunk, MapChunkData datum)
{
_updateChunkExteriorEdges(grid, chunk, datum);
_updateChunkInteriorEdges(grid, chunk, datum);

datum.EdgeDirty = false;
}

private void _updateChunkExteriorEdges(Entity<MapGridComponent> grid, MapChunk chunk, MapChunkData datum)
{
// Need a buffer that can potentially store all neighbor tiles
Span<ushort> indexBuffer = EnsureSize(ref _chunkMeshBuilderIndexBuffer, _indicesPerChunk(chunk) * 8);
Expand Down Expand Up @@ -315,7 +334,7 @@ private void _updateChunkEdges(Entity<MapGridComponent> grid, MapChunk chunk, Ma
continue;

// If it's the same tile then no edge to be drawn.
if (tile.TypeId == neighborTile.TypeId || neighborDef.EdgeSprites.Count == 0)
if (tile.TypeId == neighborTile.TypeId || neighborDef.EdgeSprites.Count == 0 || neighborDef.BordersMode != TileBordersMode.Exterior8Patch)
continue;

// If neighbor is a lower or same priority then us then don't draw on our tile.
Expand Down Expand Up @@ -348,7 +367,209 @@ private void _updateChunkEdges(Entity<MapGridComponent> grid, MapChunk chunk, Ma
datum.EdgeVBO.Reallocate(vertSlice);

datum.EdgeCount = i;
datum.EdgeDirty = false;
}

private void _updateTileInterior8Patch(Entity<MapGridComponent> grid, int gridX, int gridY, Tile tile, Span<ushort> indexBuffer, Span<Vertex2D> vertexBuffer, ref int i)
{
var maps = _entityManager.System<SharedMapSystem>();

// Edge render
for (var nx = -1; nx <= 1; nx++)
{
for (var ny = -1; ny <= 1; ny++)
{
if (nx == 0 && ny == 0)
continue;

var isCorner = nx != 0 && ny != 0;

var neighborIndices = new Vector2i(gridX + nx, gridY + ny);
var xNeighborIndices = new Vector2i(gridX + nx, gridY);
var yNeighborIndices = new Vector2i(gridX, gridY + ny);

var neighborIsSame = maps.TryGetTile(grid.Comp, neighborIndices, out var neighborTile) && neighborTile.TypeId == tile.TypeId;
var xNeighborIsSame = maps.TryGetTile(grid.Comp, xNeighborIndices, out var xNeighborTile) && xNeighborTile.TypeId == tile.TypeId;
var yNeighborIsSame = maps.TryGetTile(grid.Comp, yNeighborIndices, out var yNeighborTile) && yNeighborTile.TypeId == tile.TypeId;

switch (isCorner)
{
case false when neighborIsSame:
case true when xNeighborIsSame && yNeighborIsSame && neighborIsSame:
continue;
}

var direction = new Vector2i(nx, ny).AsDirection();
var regionMaybe = _tileDefinitionManager.TileAtlasRegion(tile.TypeId, direction);

if (regionMaybe == null)
continue;

var region = regionMaybe[0];
WriteTileToBuffers(i, gridX, gridY, vertexBuffer, indexBuffer, region, 0);
i += 1;
}
}
}

private void _updateTileInterior4Of16Patch(Entity<MapGridComponent> grid, int gridX, int gridY, Tile tile, Span<ushort> indexBuffer, Span<Vertex2D> vertexBuffer, ref int i)
{
var maps = _entityManager.System<SharedMapSystem>();

var index = new Vector2i(gridX, gridY);

var north = !maps.TryGetTile(grid.Comp, index + new Vector2i(0, 1), out var northTile) || northTile.TypeId != tile.TypeId;
var northEast = !maps.TryGetTile(grid.Comp, index + new Vector2i(1, 1), out var northEastTile) || northEastTile.TypeId != tile.TypeId;
var east = !maps.TryGetTile(grid.Comp, index + new Vector2i(1, 0), out var eastTile) || eastTile.TypeId != tile.TypeId;
var southEast = !maps.TryGetTile(grid.Comp, index + new Vector2i(1, -1), out var southEastTile) || southEastTile.TypeId != tile.TypeId;
var south = !maps.TryGetTile(grid.Comp, index + new Vector2i(0, -1), out var southTile) || southTile.TypeId != tile.TypeId;
var southWest = !maps.TryGetTile(grid.Comp, index + new Vector2i(-1, -1), out var southWestTile) || southWestTile.TypeId != tile.TypeId;
var west = !maps.TryGetTile(grid.Comp, index + new Vector2i(-1, 0), out var westTile) || westTile.TypeId != tile.TypeId;
var northWest = !maps.TryGetTile(grid.Comp, index + new Vector2i(-1, 1), out var northWestTile) || northWestTile.TypeId != tile.TypeId;

var edges = new ValueList<Interior4Of16Edge>();

// the full tile gets highest priority
if (north && east && west && south)
{
north = northEast = east = southEast = south = southWest = west = northWest = false;
edges.Add(Interior4Of16Edge.Full);
}
// followed by the three-sideds
if (north && east && south)
{
north = northEast = east = southEast = south = southWest = northWest = false;
edges.Add(Interior4Of16Edge.SideNorthEastSouth);
}
if (north && east && west)
{
north = northEast = east = southEast = southWest = west = northWest = false;
edges.Add(Interior4Of16Edge.SideNorthEastWest);
}
if (east && south && west)
{
northEast = east = southEast = south = southWest = west = northWest = false;
edges.Add(Interior4Of16Edge.SideEastSouthWest);
}
if (north && south && west)
{
north = northEast = southEast = south = southWest = west = northWest = false;
edges.Add(Interior4Of16Edge.SideNorthSouthWest);
}
// and then the two-sideds
if (north && east)
{
north = northEast = east = southEast = northWest = false;
edges.Add(Interior4Of16Edge.SideNorthEast);
}
if (south && east)
{
northEast = east = southEast = south = southWest = false;
edges.Add(Interior4Of16Edge.SideSouthEast);
}
if (north && west)
{
north = northEast = southWest = west = northWest = false;
edges.Add(Interior4Of16Edge.SideNorthWest);
}
if (south && west)
{
southEast = south = southWest = west = northWest = false;
edges.Add(Interior4Of16Edge.SideSouthWest);
}
// then we do the one-sideds
if (north)
{
north = northEast = northWest = false;
edges.Add(Interior4Of16Edge.SideNorth);
}
if (east)
{
northEast = east = southEast = false;
edges.Add(Interior4Of16Edge.SideEast);
}
if (south)
{
southEast = south = southWest = false;
edges.Add(Interior4Of16Edge.SideSouth);
}
if (west)
{
southWest = west = northWest = false;
edges.Add(Interior4Of16Edge.SideWest);
}
// finally we have the corner pieces
if (northEast)
edges.Add(Interior4Of16Edge.CornerNorthEast);

if (southEast)
edges.Add(Interior4Of16Edge.CornerSouthEast);

if (northWest)
edges.Add(Interior4Of16Edge.CornerNorthWest);

if (southWest)
edges.Add(Interior4Of16Edge.CornerSouthWest);

foreach (var edge in edges)
{
var regionMaybe = _tileDefinitionManager.TileAtlasRegion(tile.TypeId, edge);

if (regionMaybe == null)
continue;

var region = regionMaybe[0];
WriteTileToBuffers(i, gridX, gridY, vertexBuffer, indexBuffer, region, 0);
i += 1;
}
}

private void _updateChunkInteriorEdges(Entity<MapGridComponent> grid, MapChunk chunk, MapChunkData datum)
{
// Need a buffer that can potentially store all neighbor tiles
Span<ushort> indexBuffer = EnsureSize(ref _chunkMeshBuilderIndexBuffer, _indicesPerChunk(chunk) * 8);
Span<Vertex2D> vertexBuffer = EnsureSize(ref _chunkMeshBuilderVertexBuffer, _verticesPerChunk(chunk) * 8);

var i = 0;
var chunkSize = grid.Comp.ChunkSize;
var chunkOriginScaled = chunk.Indices * chunkSize;

for (ushort x = 0; x < chunkSize; x++)
{
for (ushort y = 0; y < chunkSize; y++)
{
var gridX = x + chunkOriginScaled.X;
var gridY = y + chunkOriginScaled.Y;
var tile = chunk.GetTile(x, y);

if (!_tileDefinitionManager.TryGetDefinition(tile.TypeId, out var tileDef))
continue;

switch (tileDef.BordersMode)
{
case TileBordersMode.Interior8Patch:
_updateTileInterior8Patch(grid, gridX, gridY, tile, indexBuffer, vertexBuffer, ref i);
break;
case TileBordersMode.Interior4Of16:
_updateTileInterior4Of16Patch(grid, gridX, gridY, tile, indexBuffer, vertexBuffer, ref i);
break;
default:
continue;
}
}
}

// We don't save the edge buffers back because we might need to re-use it if a neighbor chunk updates.
var indexSlice = indexBuffer[..(i * GetQuadBatchIndexCount())];
var vertSlice = vertexBuffer[..(i * 4)];

GL.BindVertexArray(datum.InteriorEdgeVAO);
CheckGlError();
datum.InteriorEdgeEBO.Use();
datum.InteriorEdgeVBO.Use();
datum.InteriorEdgeEBO.Reallocate(indexSlice);
datum.InteriorEdgeVBO.Reallocate(vertSlice);

datum.InteriorEdgeCount = i;
}

private unsafe void _initChunkBuffers(Entity<MapGridComponent> grid, MapChunk chunk, MapChunkData datum)
Expand Down Expand Up @@ -389,7 +610,7 @@ private unsafe void _initChunkBuffers(Entity<MapGridComponent> grid, MapChunk ch
var edgeEbo = new GLBuffer(this, BufferTarget.ElementArrayBuffer, BufferUsageHint.DynamicDraw,
eboSize * 8, $"Grid {grid.Owner} chunk {chunk.Indices} EdgeEBO");

ObjectLabelMaybe(ObjectLabelIdentifier.VertexArray, vao, $"Grid {grid.Owner} chunk {chunk.Indices} EdgeVAO");
ObjectLabelMaybe(ObjectLabelIdentifier.VertexArray, edgeVao, $"Grid {grid.Owner} chunk {chunk.Indices} EdgeVAO");
SetupVAOLayout();
CheckGlError();

Expand All @@ -399,6 +620,27 @@ private unsafe void _initChunkBuffers(Entity<MapGridComponent> grid, MapChunk ch
datum.EdgeEBO = edgeEbo;
datum.EdgeVBO = edgeVbo;
datum.EdgeVAO = edgeVao;

// InteriorEdgeVAO
var interiorEdgeVao = GenVertexArray();
BindVertexArray(interiorEdgeVao);
CheckGlError();

var interiorEdgeVbo = new GLBuffer(this, BufferTarget.ArrayBuffer, BufferUsageHint.DynamicDraw,
vboSize * 8, $"Grid {grid.Owner} chunk {chunk.Indices} InteriorEdgeVBO");
var interiorEdgeEbo = new GLBuffer(this, BufferTarget.ElementArrayBuffer, BufferUsageHint.DynamicDraw,
eboSize * 8, $"Grid {grid.Owner} chunk {chunk.Indices} InteriorEdgeEBO");

ObjectLabelMaybe(ObjectLabelIdentifier.VertexArray, interiorEdgeVao, $"Grid {grid.Owner} chunk {chunk.Indices} InteriorEdgeVAO");
SetupVAOLayout();
CheckGlError();

interiorEdgeVbo.Use();
interiorEdgeEbo.Use();

datum.InteriorEdgeEBO = interiorEdgeEbo;
datum.InteriorEdgeVBO = interiorEdgeVbo;
datum.InteriorEdgeVAO = interiorEdgeVao;
}

private void DeleteChunk(MapChunkData data)
Expand Down Expand Up @@ -523,6 +765,11 @@ private sealed class MapChunkData
public GLBuffer EBO = default!;
public int TileCount;

public uint InteriorEdgeVAO;
public GLBuffer InteriorEdgeVBO = default!;
public GLBuffer InteriorEdgeEBO = default!;
public int InteriorEdgeCount;

public uint EdgeVAO;
public GLBuffer EdgeVBO = default!;
public GLBuffer EdgeEBO = default!;
Expand Down
Loading
Loading