39 lines
1.5 KiB
C#
39 lines
1.5 KiB
C#
namespace AcDream.Core.World;
|
|
|
|
/// <summary>
|
|
/// Allocates stable, collision-free ids for procedurally generated scenery.
|
|
///
|
|
/// <para>
|
|
/// The top nibble is fixed at <c>0x8</c>, so bit 31 continues to identify
|
|
/// procedural scenery to every renderer and physics consumer. The remaining
|
|
/// 28 bits are X(8), Y(8), and a 12-bit per-landblock counter:
|
|
/// <c>0x8XXYYIII</c>. No consumer decodes the former byte-aligned
|
|
/// <c>0x80XXYYII</c> layout; they classify only on bit 31.
|
|
/// </para>
|
|
///
|
|
/// <para>
|
|
/// The old 8-bit counter aborted generation after 256 drawable spawns. That
|
|
/// exception rejected the entire streamed landblock before render publication,
|
|
/// exposing the grey clear color as portal space ended. A 12-bit counter gives
|
|
/// 4096 entries while retaining full landblock coordinates. Overflow fails
|
|
/// before it can alias the next landblock.
|
|
/// </para>
|
|
/// </summary>
|
|
public static class ProceduralSceneryIdAllocator
|
|
{
|
|
public const uint MaxCounter = 0xFFFu;
|
|
|
|
public static uint Base(uint landblockX, uint landblockY)
|
|
=> 0x80000000u
|
|
| ((landblockX & 0xFFu) << 20)
|
|
| ((landblockY & 0xFFu) << 12);
|
|
|
|
public static uint Allocate(uint landblockX, uint landblockY, ref uint counter)
|
|
{
|
|
if (counter > MaxCounter)
|
|
throw new InvalidDataException(
|
|
$"Landblock ({landblockX & 0xFFu:X2},{landblockY & 0xFFu:X2}) exceeds the 4096-entry procedural scenery id namespace.");
|
|
|
|
return Base(landblockX, landblockY) + counter++;
|
|
}
|
|
}
|