bake-multi-res

PURPOSE

Bake a single baked-component definition at multiple tile sizes in one call, returning one PNG-bearing BakeResult per resolution. Runtime selects the appropriate resolution based on DPR × visual size; ComponentEditor uses this to write size-suffixed atlas PNGs (e.g. body_bolt_t64.png).

OWNS

  • MultiResBakeResult interface — { tileSize: number; result: BakeResult }.
  • DEFAULT_BAKE_RESOLUTIONSreadonly [64, 128, 256].
  • bakeComponentMultiRes(def, tileSizes?) — async function returning MultiResBakeResult[].

READS FROM

  • ./bakebakeComponent (per-pass execution) and ComponentDef type.
  • ./atlas-formatBakeResult type.
  • def.baked.bake.tileSize — used as hint only; overridden per pass.

PUSHES TO

  • Returns MultiResBakeResult[] to the caller. No I/O of its own.
  • Caller (ComponentEditor) POSTs each result.pngBytes to /__dev/atlas-write with a size-suffixed filename.

DOES NOT

  • Does not write files, hit the network, or touch the atlas registry.
  • Does not mutate the input def — patches a shallow clone per pass.
  • Does not parallelize bakes; passes run sequentially via await in a for loop.
  • Does not handle non-baked components — throws if def.kind !== 'baked' or def.baked missing.

Signals

  • Throws Error("bakeComponentMultiRes: <id> is not a baked component") when the component definition is not of kind === 'baked' or lacks a baked block.

Entry points

  • Called by ComponentEditor (VFX workbench UI) to produce the per-resolution atlas PNG set for a baked component.
  • Default invocation bakes at 64 / 128 / 256 px tiles; caller may supply any readonly number[] of tile sizes.

Pattern notes

  • The component’s baked.bake.tileSize is treated as a hint only — each pass deep-patches bake: { ...def.baked.bake, tileSize } so the per-pass tileSize wins. The original def is untouched.
  • Sequential await (not Promise.all) — bakes likely share a Canvas / GPU context, so serialization is intentional.
  • The size-suffix filename convention (<id>_t<size>.png) is enforced by the caller, not this module.

EXTRACT-CANDIDATE

  • The pattern “patch one nested field per pass, await, collect results” is generic enough to live in a shared mapAsyncWithPatch helper if other workbench tooling adopts it. Currently single-use; not yet warranted.
  • DEFAULT_BAKE_RESOLUTIONS could move to a shared atlas-resolution constants module if runtime-side DPR resolution selection grows beyond ad-hoc thresholds.