Files
sbox-public/engine/Sandbox.Engine/Game/Navigation/Generation/CompactHeightField.cs
s&box team 71f266059a Open source release
This commit imports the C# engine code and game files, excluding C++ source code.

[Source-Commit: ceb3d758046e50faa6258bc3b658a30c97743268]
2025-11-24 09:05:18 +00:00

137 lines
3.6 KiB
C#

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<CompactCell> Cells => cellsArray.AsSpan( 0, Width * Height );
private CompactCell[] cellsArray;
public Span<CompactSpan> Spans => spansArray.AsSpan( 0, SpanCount );
private CompactSpan[] spansArray;
public Span<int> 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<CompactCell>.Shared.Return( cellsArray );
cellsArray = ArrayPool<CompactCell>.Shared.Rent( Width * Height * 2 );
}
if ( spansArray == null || spansArray.Length < SpanCount )
{
if ( spansArray != null ) ArrayPool<CompactSpan>.Shared.Return( spansArray );
spansArray = ArrayPool<CompactSpan>.Shared.Rent( SpanCount * 2 );
}
if ( areasArray == null || areasArray.Length < SpanCount )
{
if ( areasArray != null ) ArrayPool<int>.Shared.Return( areasArray );
areasArray = ArrayPool<int>.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<CompactHeightfield> _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<CompactCell>.Shared.Return( cellsArray );
//ArrayPool<CompactSpan>.Shared.Return( spansArray );
//ArrayPool<byte>.Shared.Return( areasArray );
}
}