using System.Buffers; using System.Collections.Concurrent; using System.Runtime.InteropServices; namespace Sandbox.Navigation.Generation; [SkipHotload] [StructLayout( LayoutKind.Explicit, Size = 8 )] internal struct CompactCell { [FieldOffset( 0 )] public int Index; [FieldOffset( 4 )] public int Count; } [SkipHotload] [StructLayout( LayoutKind.Explicit, Size = 8 )] internal struct CompactSpan { [FieldOffset( 0 )] public ushort StartY; [FieldOffset( 2 )] public ushort Region; [FieldOffset( 4 )] private int connectionsAndHeight; public int Con { get => connectionsAndHeight & 0xFFFFFF; set => connectionsAndHeight = (connectionsAndHeight & ~0xFFFFFF) | (value & 0xFFFFFF); } public byte Height { get => (byte)((connectionsAndHeight >> 24) & 0xFF); set => connectionsAndHeight = (connectionsAndHeight & 0xFFFFFF) | ((value & 0xFF) << 24); } } [SkipHotload] internal partial class CompactHeightfield : IDisposable { public int Width; public int Height; public int SpanCount; public int WalkableHeight; public int WalkableClimb; public int BorderSize; public ushort MaxDistance; public ushort MaxRegions; public Vector3 BMin; public Vector3 BMax; public float CellSize; public float CellHeight; public Span Cells => cellsArray.AsSpan( 0, Width * Height ); private CompactCell[] cellsArray; public Span Spans => spansArray.AsSpan( 0, SpanCount ); private CompactSpan[] spansArray; public Span Areas => areasArray.AsSpan( 0, SpanCount ); private int[] areasArray; internal void Init( int width, int height, int spanCount, int walkableHeight, int walkableClimb, Vector3 bmin, Vector3 bmax, float cellSize, float cellHeight ) { Width = width; Height = height; SpanCount = spanCount; WalkableHeight = walkableHeight; WalkableClimb = walkableClimb; MaxRegions = 0; BMin = bmin; BMax = bmax; BMax.y += walkableHeight * cellHeight; CellSize = cellSize; CellHeight = cellHeight; if ( cellsArray == null || cellsArray.Length < Width * Height ) { if ( cellsArray != null ) ArrayPool.Shared.Return( cellsArray ); cellsArray = ArrayPool.Shared.Rent( Width * Height * 2 ); } if ( spansArray == null || spansArray.Length < SpanCount ) { if ( spansArray != null ) ArrayPool.Shared.Return( spansArray ); spansArray = ArrayPool.Shared.Rent( SpanCount * 2 ); } if ( areasArray == null || areasArray.Length < SpanCount ) { if ( areasArray != null ) ArrayPool.Shared.Return( areasArray ); areasArray = ArrayPool.Shared.Rent( SpanCount * 2 ); } // We intentionally do NOT clear/fill here. All used elements // are fully written during BuildCompactHeightfield, and then // SpanCount is reduced to the actual number of written spans. } private CompactHeightfield() { // Private constructor for Copy } public void CopyTo( CompactHeightfield dest ) { dest.Init( Width, Height, SpanCount, WalkableHeight, WalkableClimb, BMin, BMax, CellSize, CellHeight ); Cells.CopyTo( dest.Cells ); Spans.CopyTo( dest.Spans ); Areas.CopyTo( dest.Areas ); } public CompactHeightfield Copy() { var copy = new CompactHeightfield(); CopyTo( copy ); return copy; } private static ConcurrentQueue _pool = new(); public static CompactHeightfield GetPooled() { return _pool.TryDequeue( out var hf ) ? hf : new CompactHeightfield(); } public void Dispose() { _pool.Enqueue( this ); // Those will get disposed on shutdown, i guess //ArrayPool.Shared.Return( cellsArray ); //ArrayPool.Shared.Return( spansArray ); //ArrayPool.Shared.Return( areasArray ); } }