Newer
Older
rpg / packages / engine / tools / atlas.mjs
/**
 * Сборка спрайт-атласа (Pixi Spritesheet): кадры в одну строку + JSON-описание.
 * Модуль ничего не знает о контенте: кадры — массив Uint8Array w×h×4 (RGBA),
 * имена и размеры задаёт вызывающий.
 */
import { encodePng } from './png.mjs';

/**
 * Кадры в строку атласа шириной frames.length × frameW.
 * @param frames  массив Uint8Array (frameW×frameH×4, RGBA)
 * @param names   имена кадров (ключи JSON.frames)
 * @param opts    { frameW, frameH, image } — image: имя PNG рядом с JSON
 * @returns { png: Buffer, json: object }
 */
export function buildAtlas(frames, names, { frameW, frameH, image }) {
    if (frames.length !== names.length) {
        throw new Error(`кадры и имена разной длины: ${frames.length} != ${names.length}`);
    }
    const sheetW = frameW * frames.length;
    const sheet = new Uint8Array(sheetW * frameH * 4);
    const framesJson = {};
    frames.forEach((src, i) => {
        if (src.length !== frameW * frameH * 4) {
            throw new Error(`кадр ${names[i]}: ожидается ${frameW}×${frameH}×4 байт, пришло ${src.length}`);
        }
        for (let y = 0; y < frameH; y++) {
            for (let x = 0; x < frameW; x++) {
                sheet.set(src.subarray((y * frameW + x) * 4, (y * frameW + x) * 4 + 4), ((y * sheetW) + i * frameW + x) * 4);
            }
        }
        framesJson[names[i]] = {
            frame: { x: i * frameW, y: 0, w: frameW, h: frameH },
            rotated: false,
            trimmed: false,
            sourceSize: { w: frameW, h: frameH }
        };
    });
    return {
        png: encodePng(sheetW, frameH, Buffer.from(sheet)),
        json: {
            frames: framesJson,
            meta: { image, size: { w: sheetW, h: frameH }, scale: 1 }
        }
    };
}