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; } }