TileAtlasLayout.kt
src/jvmMain/kotlin/afield/simulation/flow/TileAtlasLayout.kt · 45 lines
Maps tile slots of a afield.graphics.render.TileResidency onto pixel coordinates in one GPU atlas texture, with a halo border around each tile.
Each slot occupies a square of cellsPerTile + 2 * halo pixels, laid out in the residency's
columns and rows. TileAtlasLayout.pixelX and TileAtlasLayout.pixelY address a cell inside a
slot (negative cells and cells past the tile reach into the halo), and
TileAtlasLayout.updateHeaders packs four ints per slot (world tile x and y, generation,
occupied) for upload. The layout owns no textures and schedules no work. Halos are meant to be
filled from resident neighbouring tiles rather than by wrapping atlas coordinates, so the layout
implies no periodic world boundary.
package afield.simulation.flow
import afield.graphics.render.TileResidency
class TileAtlasLayout(val residency: TileResidency, val cellsPerTile: Int, val halo: Int = 1)
GPU atlas addressing without ownership of textures or command scheduling.
init
init {
require(cellsPerTile > 0 && halo >= 0)
require(cellsPerTile.toLong() + halo * 2L <= Int.MAX_VALUE)
}
val stride = cellsPerTile + halo * 2
init
init {
require(stride.toLong() * residency.columns <= Int.MAX_VALUE)
require(stride.toLong() * residency.rows <= Int.MAX_VALUE)
}
val width = stride * residency.columns
val height = stride * residency.rows
val headers = IntArray(residency.capacity * 4)
fun updateHeaders(): IntArray = residency.pack(headers)
fun pixelX(slot: Int, cell: Int): Int
fun pixelX(slot: Int, cell: Int): Int {
require(slot in 0 until residency.capacity && cell in -halo until cellsPerTile + halo)
return (slot % residency.columns) * stride + halo + cell
}
fun pixelY(slot: Int, cell: Int): Int
fun pixelY(slot: Int, cell: Int): Int {
require(slot in 0 until residency.capacity && cell in -halo until cellsPerTile + halo)
return (slot / residency.columns) * stride + halo + cell
}
Populate each halo from a resident neighbour, never by wrapping atlas UVs. These functions deliberately do not imply periodic world boundary conditions.