Sprite component / esys_sprite ignores atlas ids (#81 x #85): only the 16x16 sprite table is drawn #90

Closed
opened 2026-09-02 08:26:16 +02:00 by orkun · 1 comment
Owner

property Sprite { id … } documents id as "a sprite id (png_load / atlas cell)", and the #85 changelog says the engine draws Sprite entities from Position. But esys_sprite calls rt_draw_sprite_ex(id, …) (runtime/native/image.ludic), which reads only the fixed 16x16 spr_px table (if id >= spr_n return, SPR_SZ loops). Atlas sprites from Sprite.sheet / Sprite.cell / Sprite.cell_span live in a separate id space (at_nspr in atlas.ludic) with variable sizes, so a Sprite { id: Sprite.cell_span(...) } either draws the wrong table sprite or nothing.

Consequence: a game using the new atlas API (#81) — e.g. 16x32 characters as cell_span(sheet, c, r, 1, 2) — cannot adopt the engine sprite-render system (#85) and must keep a hand-written Render pass with Sprite.draw_scaled. The two headline 0.3 features don't compose.

Proposal: make the Sprite component's id an atlas-aware handle — either (a) unify the id spaces (png_load registers into the atlas registry too, so one draw path handles table and atlas sprites, any size), or (b) have esys_sprite dispatch: table id → rt_draw_sprite_ex, atlas id → atlas_draw_scaled (plus flip/tint), with a tag bit or an explicit Sprite.kind field. Also honour scale for atlas sprites and add Sprite.draw_ex(id, x, y, scale, flip, tint) so hand-drawn passes get flip/tint too.

Found migrating Emberdepths to 0.3.

`property Sprite { id … }` documents `id` as "a sprite id (png_load / atlas cell)", and the #85 changelog says the engine draws `Sprite` entities from `Position`. But `esys_sprite` calls `rt_draw_sprite_ex(id, …)` (runtime/native/image.ludic), which reads only the fixed 16x16 `spr_px` table (`if id >= spr_n return`, `SPR_SZ` loops). Atlas sprites from `Sprite.sheet` / `Sprite.cell` / `Sprite.cell_span` live in a separate id space (`at_nspr` in atlas.ludic) with variable sizes, so a `Sprite { id: Sprite.cell_span(...) }` either draws the wrong table sprite or nothing. Consequence: a game using the new atlas API (#81) — e.g. 16x32 characters as `cell_span(sheet, c, r, 1, 2)` — cannot adopt the engine sprite-render system (#85) and must keep a hand-written Render pass with `Sprite.draw_scaled`. The two headline 0.3 features don't compose. Proposal: make the `Sprite` component's id an atlas-aware handle — either (a) unify the id spaces (png_load registers into the atlas registry too, so one draw path handles table and atlas sprites, any size), or (b) have `esys_sprite` dispatch: table id → `rt_draw_sprite_ex`, atlas id → `atlas_draw_scaled` (plus flip/tint), with a tag bit or an explicit `Sprite.kind` field. Also honour `scale` for atlas sprites and add `Sprite.draw_ex(id, x, y, scale, flip, tint)` so hand-drawn passes get flip/tint too. Found migrating Emberdepths to 0.3.
Author
Owner

Shipped on main: the Sprite component is now atlas-aware.

  • Sprite { id, atlas: 1 } (new atlas field in ludic.core, default 0) marks id as a Sprite.cell / Sprite.cell_span / Sprite.strip id, and esys_sprite routes it through atlas_draw_ex(id, x, y, scale, flip, tint) — any cell size, multi-cell spans (16x32 characters) included, with scale, flip and tint honoured. atlas: 0 keeps the 16x16 png_load table path, byte-identical for existing games.
  • The chosen shape is proposal (b) with an explicit field rather than a tag bit: the two id spaces stay separate (no png_load re-registration), and the flag is a plain component field so it round-trips through world_save / reflection like everything else.
  • Sprite.draw_ex(id, x, y, scale, flip, tint) is not added as a separate surface — atlas_draw_ex is what the engine system calls; if a hand-drawn pass needs flip/tint, say so and it is a one-line namespace entry.
  • Regression: examples/library/sprite_atlas.ludic (in x test) draws a two-cell span through the engine system and checks by pixel readback that both cells drew, that tint applied to every opaque pixel, and that atlas: 0 does not route to the atlas.

Also fixed on the way (it blocked exactly this migration): a windowed ludicc -o build that reached the audio runtime only indirectly (the atlas/Assets.* preload queue imports it) failed at link with undefined snd_* symbols because the audio.ll + AVFoundation link was gated on a game-level Audio.* call. Importing runtime/native/audio.ludic now flags the backend link itself.

Commit: ad54884

Shipped on main: the `Sprite` component is now atlas-aware. - `Sprite { id, atlas: 1 }` (new `atlas` field in `ludic.core`, default 0) marks `id` as a `Sprite.cell` / `Sprite.cell_span` / `Sprite.strip` id, and `esys_sprite` routes it through `atlas_draw_ex(id, x, y, scale, flip, tint)` — any cell size, multi-cell spans (16x32 characters) included, with `scale`, `flip` and `tint` honoured. `atlas: 0` keeps the 16x16 `png_load` table path, byte-identical for existing games. - The chosen shape is proposal (b) with an explicit field rather than a tag bit: the two id spaces stay separate (no `png_load` re-registration), and the flag is a plain component field so it round-trips through `world_save` / reflection like everything else. - `Sprite.draw_ex(id, x, y, scale, flip, tint)` is not added as a separate surface — `atlas_draw_ex` is what the engine system calls; if a hand-drawn pass needs flip/tint, say so and it is a one-line namespace entry. - Regression: `examples/library/sprite_atlas.ludic` (in `x test`) draws a two-cell span through the engine system and checks by pixel readback that both cells drew, that tint applied to every opaque pixel, and that `atlas: 0` does not route to the atlas. Also fixed on the way (it blocked exactly this migration): a **windowed** `ludicc -o` build that reached the audio runtime only indirectly (the atlas/`Assets.*` preload queue imports it) failed at link with undefined `snd_*` symbols because the `audio.ll` + AVFoundation link was gated on a game-level `Audio.*` call. Importing `runtime/native/audio.ludic` now flags the backend link itself. Commit: ad54884
orkun closed this issue 2026-09-04 00:36:22 +02:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: workshopsoft/ludic#90
No description provided.