namespace AcDream.App.Rendering;
///
/// Builds the layer-indexed adapter table consumed by
/// terrain_modern.frag. Retail passes TerrainTex::tex_tiling
/// directly to ImgTex::TileCSI / ImgTex::MergeTexture
/// (`TexMerge::CopyAndTile` 0x00503580, `TexMerge::Merge` 0x005038C0).
/// The table is the modern texture-array equivalent of that per-texture
/// argument.
///
internal static class TerrainTextureTilingTable
{
// WorldBuilder's extracted terrain-array path uses the same 36-layer
// contract. Packed terrain layer indices are validated against this
// capacity before the shader table is built.
internal const int LayerCapacity = 36;
///
/// std140 stride of one float in the shader's uTexTiling array.
/// std140 pads every array element out to 16 bytes, which is why the block is
/// 576 bytes rather than the 144 the payload occupies. Packing four values
/// per vec4 would be tighter but would change the shader's use site;
/// keeping the element type means uTexTiling[int(layer)] reads exactly
/// as it did when the array was a loose uniform.
///
internal const int UniformElementStrideBytes = 16;
///
/// Size of the TerrainTiling uniform buffer
/// (GpuBindingModel.UniformTerrainTiling, binding 3) that has carried
/// this table since slice V6f-2. Asserted against the stride above rather
/// than written as a literal, because the two disagreeing is a mismatch only
/// a wrongly-tiled terrain texture would reveal.
///
internal const int UniformBufferBytes = LayerCapacity * UniformElementStrideBytes;
internal static float[] Build(IEnumerable<(uint Layer, uint RepeatCount)> entries)
{
ArgumentNullException.ThrowIfNull(entries);
var table = new float[LayerCapacity];
Array.Fill(table, 1f);
foreach (var (layer, repeatCount) in entries)
{
if (layer >= LayerCapacity)
{
throw new InvalidOperationException(
$"Terrain atlas layer {layer} exceeds the shader capacity of {LayerCapacity} layers.");
}
// Preserve the dat value exactly. Retail forwards the unsigned
// field without normalizing it in both CopyAndTile and Merge.
table[layer] = repeatCount;
}
return table;
}
}