atlas-format

PURPOSE

Type-only module defining the on-disk schema for baked VFX atlases produced by the vfx-workbench. Declares the contract between the bake step (which emits PNG grids + JSON manifests under data/vfx/atlas/) and any runtime/tooling that consumes them.

OWNS

  • AtlasCategory — union of 'muzzle' | 'body' | 'impact' | 'persistent'. The four buckets baked VFX layers fall into.
  • AtlasLayer — per-layer manifest entry. Fields: componentId, name, category, kind: 'baked', layerIndex, frameCount, fps, tileSize, gridCols, gridRows, loopMode ('oneshot' | 'loop' | 'parameterized'), pivot {x,y}, anchors, palette (number[][]), shaderTemplate, shaderParams, bakedAt, atlasPath.
  • AtlasManifest — top-level per-category manifest. Fields: category, format ('png-grid' | 'ktx2-astc' | 'ktx2-etc2'), atlasDir (relative to data/vfx/atlas/), layers: AtlasLayer[].
  • BakeResult — return shape from a bake operation. Fields: pngBytes: Uint8Array, gridW, gridH (= grid cols/rows * tileSize), manifestEntry: Omit<AtlasLayer, 'layerIndex' | 'bakedAt'>.

READS FROM

Nothing. No imports.

PUSHES TO

Nothing at runtime. Compile-time consumers only.

DOES NOT

  • Implement any baking, encoding, or I/O. Strictly type declarations.
  • Define palette format beyond number[][] — interpretation lives in the baker/shader template.
  • Constrain anchors shape (Record<string, unknown>) — bake-step is responsible.
  • Encode KTX2 formats — format field permits ASTC/ETC2 variants but no module here handles them.
  • Stamp layerIndex or bakedAt in BakeResult.manifestEntry — caller fills those when assembling the full AtlasLayer for the manifest.

Signals

None — pure types.

Entry points

Imported by any vfx-workbench module that bakes, reads, or validates atlas manifests. Consumers compose BakeResult into an AtlasLayer by adding layerIndex + bakedAt, then push into AtlasManifest.layers.

Pattern notes

  • kind: 'baked' literal leaves room for future non-baked layer kinds (procedural, runtime) without breaking the union.
  • format union allows targeting either uncompressed PNG grids (dev/web fallback) or compressed KTX2 (mobile GPU formats: ASTC for iOS/modern Android, ETC2 for older Android).
  • gridW/gridH in BakeResult are derived (gridCols * tileSize, gridRows * tileSize); duplicated on the result for convenience so callers don’t re-multiply.
  • atlasDir is relative to data/vfx/atlas/ — consumers must prepend the root.
  • atlasPath on the layer is the per-layer file path; together with atlasDir on the manifest they fully locate the PNG/KTX2.
  • Omit<AtlasLayer, 'layerIndex' | 'bakedAt'> enforces the rule that the bake step doesn’t know its own slot in the manifest or commit the bake timestamp — both are stamped by the orchestrator.

EXTRACT-CANDIDATE

  • AtlasCategory and the loopMode union are likely duplicated against any VFX authoring/runtime types (component definitions, particle pools). If those types exist separately, consolidate into a shared vfx-types module.
  • format enum ('png-grid' | 'ktx2-astc' | 'ktx2-etc2') is a candidate for a shared texture-format type used by the renderer’s texture loader.