feat(bake-sprite): end-to-end sprite-mode orchestrator + CLI dispatch
This commit is contained in:
110
src/bake-sprite.js
Normal file
110
src/bake-sprite.js
Normal file
@@ -0,0 +1,110 @@
|
||||
// src/bake-sprite.js
|
||||
// Sprite-Mode bake orchestrator. Reads sources, packs shelves, composes
|
||||
// the diffuse atlas, writes outputs + lock.
|
||||
|
||||
const Jimp = require('jimp');
|
||||
const { scanSpriteSourceDir } = require('./scan-sprite');
|
||||
const { packShelf } = require('./pack-shelf');
|
||||
const { loadSpriteLock, mergeSpriteLock } = require('./lock-sprite');
|
||||
const { writeSpriteOutputs } = require('./write-sprite');
|
||||
|
||||
async function bakeSprite(opts) {
|
||||
const {
|
||||
inDir,
|
||||
outDir,
|
||||
atlasId,
|
||||
stripPrefix = '',
|
||||
maxSize = 4096,
|
||||
padPx = 1,
|
||||
lockPath,
|
||||
verbose,
|
||||
} = opts;
|
||||
|
||||
if (!atlasId) throw new Error('bake-sprite: --atlas-id is required');
|
||||
if (!inDir) throw new Error('bake-sprite: --in is required');
|
||||
if (!outDir) throw new Error('bake-sprite: --out is required');
|
||||
if (padPx < 0 || !Number.isInteger(padPx)) {
|
||||
throw new Error(`bake-sprite: --pad-px must be non-negative integer, got ${padPx}`);
|
||||
}
|
||||
|
||||
const scan = scanSpriteSourceDir(inDir, { stripPrefix });
|
||||
if (scan.errors.length > 0) {
|
||||
const e = new Error(scan.errors[0]);
|
||||
e.scanErrors = scan.errors;
|
||||
throw e;
|
||||
}
|
||||
if (scan.sources.length === 0) {
|
||||
throw new Error(`bake-sprite: no sprites found in ${inDir}`);
|
||||
}
|
||||
|
||||
// Load source images + final padded rect-sizes
|
||||
const rects = [];
|
||||
for (const s of scan.sources) {
|
||||
const img = await Jimp.read(s.sourcePath);
|
||||
const innerW = img.bitmap.width;
|
||||
const innerH = img.bitmap.height;
|
||||
// Padding: add 2*padPx to both dimensions, sprite content sits
|
||||
// at (x+padPx, y+padPx); UV-JSON exposes (x+padPx, y+padPx, innerW, innerH).
|
||||
rects.push({
|
||||
alias: s.alias,
|
||||
w: innerW + 2 * padPx,
|
||||
h: innerH + 2 * padPx,
|
||||
sourceFile: s.sourceFile,
|
||||
image: img,
|
||||
innerW,
|
||||
innerH,
|
||||
padPx,
|
||||
});
|
||||
}
|
||||
|
||||
const pack = packShelf(rects, maxSize);
|
||||
if (pack.error) {
|
||||
throw new Error(pack.message);
|
||||
}
|
||||
|
||||
// Build placed entries: x/y point to the INNER sprite origin
|
||||
// (padded-rect top-left + padPx). The writer composites the image
|
||||
// at this position; the UV-JSON exposes the same inner rect.
|
||||
const placed = pack.placed.map(p => ({
|
||||
alias: p.alias,
|
||||
x: p.x + p.padPx,
|
||||
y: p.y + p.padPx,
|
||||
w: p.innerW,
|
||||
h: p.innerH,
|
||||
sourceFile: p.sourceFile,
|
||||
image: p.image,
|
||||
}));
|
||||
|
||||
// Lock-merge
|
||||
const existingLock = lockPath ? loadSpriteLock(lockPath) : null;
|
||||
const { merged: lock, removed } = mergeSpriteLock(existingLock, scan.sources);
|
||||
lock.atlas_id = atlasId;
|
||||
|
||||
if (removed.length > 0) {
|
||||
for (const r of removed) {
|
||||
console.error(`WARN bake-sprite: alias "${r}" was in lock but is no longer in input — kept in lock for consumer-template safety; remove manually if intentional`);
|
||||
}
|
||||
}
|
||||
|
||||
await writeSpriteOutputs({
|
||||
atlasId,
|
||||
outDir,
|
||||
placed,
|
||||
boundsW: pack.boundsW,
|
||||
boundsH: pack.boundsH,
|
||||
lock,
|
||||
});
|
||||
|
||||
if (verbose) {
|
||||
console.log(`bake-sprite: ${placed.length} sprites, ${pack.boundsW}x${pack.boundsH} atlas`);
|
||||
}
|
||||
|
||||
return {
|
||||
atlasId,
|
||||
spriteCount: placed.length,
|
||||
boundsW: pack.boundsW,
|
||||
boundsH: pack.boundsH,
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = { bakeSprite };
|
||||
@@ -8,6 +8,10 @@ const { probeImageDims, composeAtlas, buildAtlasJson, writeOutputs } = require('
|
||||
const { validateBlob14Sources, validateBlob14Collision } = require('./schema');
|
||||
|
||||
async function bake(opts) {
|
||||
if (opts.mode === 'sprite') {
|
||||
const { bakeSprite } = require('./bake-sprite');
|
||||
return bakeSprite(opts);
|
||||
}
|
||||
const {
|
||||
inDir,
|
||||
outDir,
|
||||
|
||||
Reference in New Issue
Block a user