mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-15 03:30:53 +01:00
Grid chunks now use the GridChunkPartition method for generating a set of collision rectangles and their bounds.
Grid collision rectangles are now drawn as a blue overlay in the CollisionOverlay debug view. GridChunkPartition now also returns the bounding rectangle, which is a union of all the collision rectangles. Each IPhysShape now handles it's own debug drawing with a new method, DebugDraw.
This commit is contained in:
@@ -95,6 +95,7 @@ namespace Robust.Client.Debugging
|
||||
protected override void Draw(DrawingHandleBase handle)
|
||||
{
|
||||
var worldHandle = (DrawingHandleWorld) handle;
|
||||
var drawing = new PhysDrawingAdapter(worldHandle);
|
||||
|
||||
var viewport = _eyeManager.GetWorldViewport();
|
||||
foreach (var boundingBox in _componentManager.GetAllComponents<ICollidableComponent>())
|
||||
@@ -107,7 +108,6 @@ namespace Robust.Client.Debugging
|
||||
continue;
|
||||
|
||||
var worldBox = boundingBox.WorldAABB;
|
||||
var colorFill = Color.Green.WithAlpha(0.25f);
|
||||
var colorEdge = Color.Red.WithAlpha(0.33f);
|
||||
|
||||
// if not on screen, or too small, continue
|
||||
@@ -116,24 +116,36 @@ namespace Robust.Client.Debugging
|
||||
|
||||
foreach (var shape in boundingBox.PhysicsShapes)
|
||||
{
|
||||
// TODO: Add a debug drawing function to IPhysShape
|
||||
if (shape is PhysShapeAabb aabb)
|
||||
{
|
||||
var shapeWorldBox = aabb.LocalBounds.Translated(transform.WorldPosition);
|
||||
worldHandle.DrawRect(shapeWorldBox, colorFill);
|
||||
}
|
||||
else if (shape is PhysShapeRect rect)
|
||||
{
|
||||
worldHandle.SetTransform(transform.WorldMatrix);
|
||||
worldHandle.DrawRect(rect.Rectangle, colorFill);
|
||||
worldHandle.SetTransform(Matrix3.Identity);
|
||||
}
|
||||
shape.DebugDraw(drawing, transform.WorldMatrix, in viewport);
|
||||
}
|
||||
|
||||
|
||||
// draw AABB
|
||||
worldHandle.DrawRect(worldBox, colorEdge, false);
|
||||
}
|
||||
}
|
||||
|
||||
private class PhysDrawingAdapter : DebugDrawingHandle
|
||||
{
|
||||
private readonly DrawingHandleWorld _handle;
|
||||
|
||||
public PhysDrawingAdapter(DrawingHandleWorld worldHandle)
|
||||
{
|
||||
_handle = worldHandle;
|
||||
}
|
||||
|
||||
public override Color GridFillColor => Color.Blue.WithAlpha(0.05f);
|
||||
public override Color RectFillColor => Color.Green.WithAlpha(0.25f);
|
||||
|
||||
public override void DrawRect(in Box2 box, in Color color)
|
||||
{
|
||||
_handle.DrawRect(box, color);
|
||||
}
|
||||
|
||||
public override void SetTransform(in Matrix3 transform)
|
||||
{
|
||||
_handle.SetTransform(transform);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class EntityPositionOverlay : Overlay
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Robust.Shared.Map
|
||||
@@ -8,8 +9,10 @@ namespace Robust.Shared.Map
|
||||
/// </summary>
|
||||
internal static class GridChunkPartition
|
||||
{
|
||||
public static void PartitionChunk(IMapChunk chunk, ref IList<Box2> rectangles)
|
||||
public static void PartitionChunk(IMapChunk chunk, ref IList<Box2> rectangles, out Box2i bounds)
|
||||
{
|
||||
rectangles.Clear();
|
||||
|
||||
var size = chunk.ChunkSize;
|
||||
|
||||
// copy 2d img
|
||||
@@ -21,6 +24,8 @@ namespace Robust.Shared.Map
|
||||
|
||||
Partition(size, size, image, out var blocks, out var blockCount);
|
||||
|
||||
bounds = new Box2i();
|
||||
|
||||
// convert blocks to rectangles array.
|
||||
for(int i=0;i< blockCount; i++)
|
||||
{
|
||||
@@ -33,9 +38,12 @@ namespace Robust.Shared.Map
|
||||
var bottom = block.x1;
|
||||
var top = block.x2 + 1;
|
||||
|
||||
var box = new Box2(left, bottom, right, top);
|
||||
rectangles.Add(new Box2(left, bottom, right, top));
|
||||
|
||||
rectangles.Add(box);
|
||||
if(bounds.Size.Equals(Vector2i.Zero))
|
||||
bounds = new Box2i(left, bottom, right, top);
|
||||
else
|
||||
bounds = bounds.Union(new Box2i(left, bottom, right, top));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using Robust.Shared.Timing;
|
||||
using System.Collections.Generic;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Robust.Shared.Map
|
||||
{
|
||||
@@ -6,8 +8,13 @@ namespace Robust.Shared.Map
|
||||
internal interface IMapChunkInternal : IMapChunk
|
||||
{
|
||||
/// <summary>
|
||||
/// The last game simulation tick that this chunk was modified.
|
||||
/// The last game simulation tick that this chunk was modified.
|
||||
/// </summary>
|
||||
GameTick LastModifiedTick { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The physical collision boxes of this chunk.
|
||||
/// </summary>
|
||||
IEnumerable<Box2> CollisionBoxes { get; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,10 +17,13 @@ namespace Robust.Shared.Map
|
||||
private readonly SnapGridCell[,] _snapGrid;
|
||||
|
||||
private Box2i _cachedBounds;
|
||||
private IList<Box2> _colBoxes;
|
||||
|
||||
/// <inheritdoc />
|
||||
public GameTick LastModifiedTick { get; private set; }
|
||||
|
||||
public IEnumerable<Box2> CollisionBoxes => _colBoxes;
|
||||
|
||||
/// <summary>
|
||||
/// Constructs an instance of a MapGrid chunk.
|
||||
/// </summary>
|
||||
@@ -37,6 +40,7 @@ namespace Robust.Shared.Map
|
||||
|
||||
_tiles = new Tile[ChunkSize, ChunkSize];
|
||||
_snapGrid = new SnapGridCell[ChunkSize, ChunkSize];
|
||||
_colBoxes = new List<Box2>(0);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -121,7 +125,7 @@ namespace Robust.Shared.Map
|
||||
LastModifiedTick = _grid.CurTick;
|
||||
|
||||
_tiles[xIndex, yIndex] = tile;
|
||||
CheckBounds(new Vector2i(xIndex, yIndex), tile.IsEmpty);
|
||||
RegenerateCollision();
|
||||
|
||||
_grid.NotifyTileChanged(newTileRef, oldTile);
|
||||
}
|
||||
@@ -233,117 +237,10 @@ namespace Robust.Shared.Map
|
||||
}
|
||||
}
|
||||
|
||||
private void CheckBounds(in Vector2i indices, bool empty)
|
||||
private void RegenerateCollision()
|
||||
{
|
||||
var tileBounds = new Box2i(0,0,1, 1).Translated(indices);
|
||||
|
||||
if (!empty)
|
||||
{
|
||||
// placing tiles can only expand the bounds, it is easier to just blind union
|
||||
_cachedBounds = _cachedBounds.Size.Equals(Vector2i.Zero) ? tileBounds : _cachedBounds.Union(tileBounds);
|
||||
}
|
||||
else
|
||||
{
|
||||
// removing tiles can only shrink the bounds, this is not an easy thing to detect
|
||||
ReduceBoundsX(_tiles, tileBounds, ref _cachedBounds);
|
||||
ReduceBoundsY(_tiles, tileBounds, ref _cachedBounds);
|
||||
}
|
||||
}
|
||||
|
||||
private static void ReduceBoundsX(in Tile[,] tiles, Box2i tBounds, ref Box2i cBounds)
|
||||
{
|
||||
if (tBounds.Left != cBounds.Left && tBounds.Right != cBounds.Right)
|
||||
return; // nothing to do, we are not on an edge
|
||||
|
||||
var left = tBounds.Left == cBounds.Left;
|
||||
|
||||
// removing a tile can shrink the side more than one tile
|
||||
while (cBounds.Width > 0)
|
||||
{
|
||||
// check if we are the only tile holding the side out
|
||||
if(!AnyTileOnY(tiles, new Vector2i(cBounds.Bottom, cBounds.Top), tBounds.BottomLeft))
|
||||
return; // our removal does not modify the edge, we are done here
|
||||
|
||||
// shrink the chunk bounds
|
||||
int newLeft;
|
||||
int newRight;
|
||||
if (left)
|
||||
{
|
||||
newLeft = cBounds.Left + 1;
|
||||
newRight = cBounds.Right;
|
||||
tBounds = tBounds.Translated(new Vector2i(1, 0));
|
||||
}
|
||||
else
|
||||
{
|
||||
newLeft = cBounds.Left;
|
||||
newRight = cBounds.Right - 1;
|
||||
tBounds = tBounds.Translated(new Vector2i(-1, 0));
|
||||
}
|
||||
|
||||
cBounds = new Box2i(newLeft, cBounds.Bottom, newRight, cBounds.Top);
|
||||
}
|
||||
}
|
||||
|
||||
private static void ReduceBoundsY(in Tile[,] tiles, Box2i tBounds, ref Box2i cBounds)
|
||||
{
|
||||
if (tBounds.Bottom != cBounds.Bottom && tBounds.Top != cBounds.Top)
|
||||
return; // nothing to do, we are not on an edge
|
||||
|
||||
var bottom = tBounds.Bottom == cBounds.Bottom; // which side we are moving
|
||||
|
||||
// removing a tile can shrink the side more than one tile
|
||||
while (cBounds.Height > 0)
|
||||
{
|
||||
// check if we are the only tile holding the side out
|
||||
if (!AnyTileOnX(tiles, new Vector2i(cBounds.Left, cBounds.Right), tBounds.BottomLeft))
|
||||
return; // our removal does not modify the edge
|
||||
|
||||
// shrink the chunk bounds
|
||||
int newBottom;
|
||||
int newTop;
|
||||
if (bottom)
|
||||
{
|
||||
newBottom = cBounds.Bottom + 1;
|
||||
newTop = cBounds.Top;
|
||||
tBounds = tBounds.Translated(new Vector2i(0, 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
newBottom = cBounds.Bottom;
|
||||
newTop = cBounds.Top - 1;
|
||||
tBounds = tBounds.Translated(new Vector2i(0, -1));
|
||||
}
|
||||
|
||||
cBounds = new Box2i(cBounds.Left, newBottom, cBounds.Right, newTop);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool AnyTileOnX(in Tile[,] tiles, Vector2i extents, in Vector2i indices)
|
||||
{
|
||||
var y = indices.Y;
|
||||
for (var x = extents.X; x < extents.Y; x++)
|
||||
{
|
||||
if(tiles[x, y].IsEmpty)
|
||||
continue;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool AnyTileOnY(in Tile[,] tiles, Vector2i extents, in Vector2i indices)
|
||||
{
|
||||
var x = indices.X;
|
||||
for (var y = extents.X; y < extents.Y; y++)
|
||||
{
|
||||
if(tiles[x, y].IsEmpty)
|
||||
continue;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
// generate collision rects
|
||||
GridChunkPartition.PartitionChunk(this, ref _colBoxes, out _cachedBounds);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -48,6 +48,22 @@ namespace Robust.Shared.Map
|
||||
_mapGrid = (IMapGridInternal)mapMan.GetGrid(_gridId);
|
||||
}
|
||||
|
||||
public void DebugDraw(DebugDrawingHandle handle, in Matrix3 modelMatrix, in Box2 worldViewport)
|
||||
{
|
||||
handle.SetTransform(modelMatrix);
|
||||
foreach (var chunk in _mapGrid.GetMapChunks().Values)
|
||||
{
|
||||
foreach (var box in chunk.CollisionBoxes)
|
||||
{
|
||||
var localChunkPos = new Vector2(chunk.Indices.X, chunk.Indices.Y) * _mapGrid.ChunkSize;
|
||||
var localBox = box.Translated(localChunkPos);
|
||||
|
||||
handle.DrawRect(localBox, handle.GridFillColor);
|
||||
}
|
||||
}
|
||||
handle.SetTransform(Matrix3.Identity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new instance of <see cref="PhysShapeGrid"/>.
|
||||
/// </summary>
|
||||
|
||||
17
Robust.Shared/Physics/DebugDrawingHandle.cs
Normal file
17
Robust.Shared/Physics/DebugDrawingHandle.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Robust.Shared.Physics
|
||||
{
|
||||
public abstract class DebugDrawingHandle
|
||||
{
|
||||
public abstract Color GridFillColor { get; }
|
||||
public abstract Color RectFillColor { get; }
|
||||
|
||||
public abstract void DrawRect(in Box2 box, in Color color);
|
||||
|
||||
public abstract void SetTransform(in Matrix3 transform);
|
||||
}
|
||||
}
|
||||
@@ -26,5 +26,7 @@ namespace Robust.Shared.Physics
|
||||
int CollisionMask { get; set; }
|
||||
|
||||
void ApplyState();
|
||||
|
||||
void DebugDraw(DebugDrawingHandle handle, in Matrix3 modelMatrix, in Box2 worldViewport);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,6 +46,17 @@ namespace Robust.Shared.Physics
|
||||
/// <inheritdoc />
|
||||
public void ApplyState() { }
|
||||
|
||||
public void DebugDraw(DebugDrawingHandle handle, in Matrix3 modelMatrix, in Box2 worldViewport)
|
||||
{
|
||||
var m = Matrix3.Identity;
|
||||
m.R0C2 = modelMatrix.R0C2;
|
||||
m.R1C2 = modelMatrix.R1C2;
|
||||
|
||||
handle.SetTransform(m);
|
||||
handle.DrawRect(LocalBounds, handle.RectFillColor);
|
||||
handle.SetTransform(Matrix3.Identity);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Box2 CalculateLocalBounds(Angle rotation)
|
||||
{
|
||||
|
||||
@@ -37,6 +37,13 @@ namespace Robust.Shared.Physics
|
||||
/// <inheritdoc />
|
||||
public void ApplyState() { }
|
||||
|
||||
public void DebugDraw(DebugDrawingHandle handle, in Matrix3 modelMatrix, in Box2 worldViewport)
|
||||
{
|
||||
handle.SetTransform(modelMatrix);
|
||||
handle.DrawRect(Rectangle, handle.RectFillColor);
|
||||
handle.SetTransform(Matrix3.Identity);
|
||||
}
|
||||
|
||||
public void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
serializer.DataField(ref _collisionLayer, "layer", 0);
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace Robust.UnitTesting.Shared.Map
|
||||
IList<Box2> rects = new List<Box2>(8);
|
||||
|
||||
// Act
|
||||
GridChunkPartition.PartitionChunk(chunk, ref rects);
|
||||
GridChunkPartition.PartitionChunk(chunk, ref rects, out var bounds);
|
||||
|
||||
// Assert
|
||||
Assert.That(rects.Count, Is.EqualTo(5));
|
||||
@@ -43,6 +43,8 @@ namespace Robust.UnitTesting.Shared.Map
|
||||
Assert.That(rects[2], Is.EqualTo(new Box2(1, 0, 2, 4)));
|
||||
Assert.That(rects[3], Is.EqualTo(new Box2(2, 1, 3, 3)));
|
||||
Assert.That(rects[4], Is.EqualTo(new Box2(3, 2, 4, 3)));
|
||||
|
||||
Assert.That(bounds, Is.EqualTo(new Box2i(0,0,4,4)));
|
||||
}
|
||||
|
||||
// origin is top left
|
||||
@@ -61,13 +63,15 @@ namespace Robust.UnitTesting.Shared.Map
|
||||
IList<Box2> rects = new List<Box2>(16);
|
||||
|
||||
// Act
|
||||
GridChunkPartition.PartitionChunk(chunk, ref rects);
|
||||
GridChunkPartition.PartitionChunk(chunk, ref rects, out var bounds);
|
||||
|
||||
// Assert
|
||||
Assert.That(rects.Count, Is.EqualTo(1));
|
||||
|
||||
// box origin is bottom left
|
||||
Assert.That(rects[0], Is.EqualTo(new Box2(1,0,3,3)));
|
||||
|
||||
Assert.That(bounds, Is.EqualTo(new Box2i(1, 0, 3, 3)));
|
||||
}
|
||||
|
||||
private static IMapChunkInternal ChunkFactory(ushort size, int[] tiles)
|
||||
|
||||
Reference in New Issue
Block a user