Implements the §13 contract from the autotile-blob-styles design
paper (Rev 4):
- **Edge-replicated padding** (Rev 4 §13.3, mandatory): each tile
in the packed atlas gets a `--pad-px` ring (default 2) of edge-
replicated pixels. Inner content + UV coords unchanged; pack-
rects inflated by 2 * padPx before MaxRects. write.js's new
placeTileWithPadding helper handles both the inner-copy and
the edge-replicate sweep. Prevents bilinear-sampler bleed from
adjacent tiles in the atlas — was the green-flicker bug we
caught during 0.5.0c integration with vagrant-skeleton.
- **E1 — Schema slot validation**: new `--schema blob-14` flag
runs validateBlob14Sources against the source dir. Errors out
with clear "missing slot 7 (tee_full)" message if the 14 PNGs
don't match the canonical enumeration. New src/schema.js
module holds REQUIRED_SLOTS_BLOB14 + validateBlob14Sources +
validateBlob14Collision.
- **E2 — Collision sidecar**: bake.js looks for collision.json
in the source dir. If present + --schema blob-14: validate
14 slot entries exist via validateBlob14Collision. Copy
through to atlas output as tiles.collision.json (read by
consumer engines via the existing atlas-path resolver).
- **Opaque-flag alpha analysis** (powers lib-core.maps' opaque-
ceiling-cache in v0.5.0e): write.js's placeTileWithPadding
scans each tile's inner content for alpha === 255. If all
inner pixels are opaque, the tile entry in tiles.atlas.json
gets `"opaque": true`. Replaces lib-core.maps' slot-13-only
heuristic with real data.
Backwards-compat: pre-Rev-4 callers (test fixtures) use tile.w/h
without innerW/innerH. write.js falls back to w/h when innerW is
absent and padPx defaults to 0 — old code paths unchanged.
atlas_version bumped 1 -> 2 in tiles.atlas.json output. Consumers
key on atlas_id, not version, so this is a behavioural signal only.
Test surface: all 20 existing node:test cases still pass (pack +
scan + lock + 5 bake-* + write tests).
End-to-end verification: prototype-blob-geom re-baked through new
pipeline (8 atlases: 4 base styles + 3 tinted singles + testbench),
SPOREL_CI=1 vagrant-skeleton rc=0 with 60 render_frame_ok, no
magenta-placeholders or render-hook errors.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
51 lines
1.8 KiB
JavaScript
51 lines
1.8 KiB
JavaScript
#!/usr/bin/env node
|
|
// CLI entry. Parses args, dispatches to bake(), prints summary.
|
|
|
|
const { bake } = require('../src/bake');
|
|
|
|
function parseArgs(argv) {
|
|
const opts = {};
|
|
for (let i = 0; i < argv.length; i++) {
|
|
const a = argv[i];
|
|
const next = argv[i + 1];
|
|
switch (a) {
|
|
case '--in': opts.inDir = next; i++; break;
|
|
case '--out': opts.outDir = next; i++; break;
|
|
case '--atlas-id': opts.atlasId = next; i++; break;
|
|
case '--tile-size':
|
|
opts.tileSize = (next === 'auto') ? 'auto' : parseInt(next, 10);
|
|
i++;
|
|
break;
|
|
case '--max-size': opts.maxSize = parseInt(next, 10); i++; break;
|
|
case '--lock': opts.lockPath = next; i++; break;
|
|
case '--blocks-sight-pattern':
|
|
opts.blocksSightPattern = new RegExp(next); i++; break;
|
|
case '--pad-px': opts.padPx = parseInt(next, 10); i++; break;
|
|
case '--schema': opts.schema = next; i++; break;
|
|
case '--verbose': opts.verbose = true; break;
|
|
default:
|
|
console.error(`unknown arg: ${a}`);
|
|
process.exit(2);
|
|
}
|
|
}
|
|
return opts;
|
|
}
|
|
|
|
(async () => {
|
|
try {
|
|
const opts = parseArgs(process.argv.slice(2));
|
|
const result = await bake(opts);
|
|
console.log(
|
|
`OK ${result.atlasId}: ${result.tileCount} tiles, `
|
|
+ `${result.boundsW}x${result.boundsH} px atlas`
|
|
);
|
|
process.exit(0);
|
|
} catch (err) {
|
|
console.error('ERROR:', err.message);
|
|
if (err.scanErrors) {
|
|
for (const e of err.scanErrors) console.error(' ', e);
|
|
}
|
|
process.exit(1);
|
|
}
|
|
})();
|