Files
mrgameeng/src/MrGameEng.Graphics/Layers.cs
T
Leonid Pershin fd6343bd09
CI / build-test (push) Failing after 1m8s
@
Add MrGameEng.AI utility-AI module; format codebase with CSharpier

New MrGameEng.AI module (ResponseCurve, Consideration, UtilityAction,
UtilityAi selector, Blackboard) plus CSharpier formatting applied across
the whole engine. Documents the CSharpier convention in CLAUDE.md.
@
2026-06-12 07:19:10 +03:00

95 lines
3.3 KiB
C#

namespace MrGameEng.Graphics;
/// <summary>Compact identifier of a render layer. Obtained from <see cref="LayerRegistry.Register"/>.</summary>
public readonly record struct LayerId(byte Value)
{
/// <summary>The default layer (the first one registered).</summary>
public static readonly LayerId Default = new(0);
}
/// <summary>Coordinate space a layer is drawn in.</summary>
public enum LayerSpace
{
/// <summary>Drawn through the active camera's transform.</summary>
World,
/// <summary>Drawn in screen coordinates, ignoring the camera (HUD, UI). Never culled.</summary>
Screen,
}
/// <summary>How sprites are ordered within a layer.</summary>
public enum LayerSortMode
{
/// <summary>Order by the sprite's <see cref="Sprite.Depth"/> value (smaller = drawn first).</summary>
Depth,
/// <summary>
/// Order by the entity's world Y position (<see cref="Transform2D.Position"/>):
/// top-down games, lower on screen = drawn in front. Place sprite origins at the
/// feet/base so the sort point matches the visual anchor.
/// </summary>
YSort,
}
/// <summary>A registered render layer.</summary>
public sealed record RenderLayer(LayerId Id, string Name, LayerSpace Space, LayerSortMode SortMode);
/// <summary>
/// Registry of render layers. Layers are registered up front (typically when the renderer is
/// created) and drawn in registration order. Maximum 256 layers. Reads are lock-free and
/// thread-safe (the renderer reads layers from parallel submit workers); registration swaps
/// an immutable snapshot, so registering mid-frame never tears a concurrent read.
/// </summary>
public sealed class LayerRegistry
{
private readonly object _sync = new();
private volatile RenderLayer[] _layers = [];
/// <summary>Creates a registry containing the built-in "Default" world layer.</summary>
public LayerRegistry() => Register("Default");
/// <summary>Number of registered layers.</summary>
public int Count => _layers.Length;
/// <summary>Registers a layer drawn after all previously registered ones.</summary>
public LayerId Register(
string name,
LayerSpace space = LayerSpace.World,
LayerSortMode sortMode = LayerSortMode.Depth
)
{
lock (_sync)
{
var layers = _layers;
if (layers.Length == 256)
{
throw new InvalidOperationException(
"Maximum number of render layers (256) reached."
);
}
var id = new LayerId((byte)layers.Length);
var grown = new RenderLayer[layers.Length + 1];
Array.Copy(layers, grown, layers.Length);
grown[layers.Length] = new RenderLayer(id, name, space, sortMode);
_layers = grown;
return id;
}
}
/// <summary>Returns the layer with the given id; throws when the id was never registered.</summary>
public RenderLayer this[LayerId id]
{
get
{
var layers = _layers;
return id.Value < layers.Length
? layers[id.Value]
: throw new ArgumentOutOfRangeException(
nameof(id),
$"Render layer {id.Value} is not registered (registered: {layers.Length})."
);
}
}
}