import { Container, Rectangle, Sprite, Texture } from "pixi.js";
import type { GenerateTextureOptions, TextureSource } from "pixi.js";
interface TextureRenderer {
generateTexture(options: GenerateTextureOptions | Container): Texture;
}
/**
* Packs a flat record of named textures into a single atlas and returns a
* TileAtlas wrapping it. All textures must be the same tileSize × tileSize.
*/
export function buildTileAtlas(
renderer: TextureRenderer,
tiles: Record<string, Texture>,
tileSize: number,
): TileAtlas {
const entries = Object.entries(tiles);
const count = entries.length;
const cols = Math.ceil(Math.sqrt(count));
const rows = Math.ceil(count / cols);
const container = new Container();
const layout: { name: string; col: number; row: number }[] = [];
entries.forEach(([name, texture], i) => {
const col = i % cols;
const row = Math.floor(i / cols);
const sprite = new Sprite(texture);
sprite.x = col * tileSize;
sprite.y = row * tileSize;
container.addChild(sprite);
layout.push({ name, col, row });
});
const atlas = renderer.generateTexture({
target: container,
resolution: 1,
frame: new Rectangle(0, 0, cols * tileSize, rows * tileSize),
});
container.destroy();
const textures: Record<string, Texture> = {};
for (const { name, col, row } of layout) {
textures[name] = new Texture({
source: atlas.source,
frame: new Rectangle(col * tileSize, row * tileSize, tileSize, tileSize),
});
}
return new TileAtlas(atlas.source, textures);
}
/** Holds a packed atlas and provides named texture lookup. */
export class TileAtlas {
private readonly _source: TextureSource;
private readonly _textures: Record<string, Texture>;
constructor(source: TextureSource, textures: Record<string, Texture>) {
this._source = source;
this._textures = textures;
}
/** Returns the sub-texture for the given name; throws if not found. */
get(name: string): Texture {
const tex = this._textures[name];
if (tex === undefined) throw new Error(`TileAtlas: unknown tile "${name}"`);
return tex;
}
/** The underlying GPU source, for passing to the Tilemap constructor. */
get source(): TextureSource {
return this._source;
}
}