Follow-up to #4: 2D lighting tiers 3-4 + Light2D/Occluder as ECS components #47

Closed
opened 2026-08-31 13:34:52 +02:00 by orkun · 1 comment
Owner

Follow-up to #4, which shipped the light-accumulation core in a71279a: the Light.* namespace — Light.ambient (scene modulate), Light.point (additive radial glow, linear falloff), and Light.occlude/Light.clear_occluders (hard shadows). That is the imperative escape hatch the proposal named, covering tiers 1 (ambient + additive radial lights) and 2 (hard shadows).

This tracks the remaining tiers and the ECS-native shape:

  • ECS components: Light2D / Occluder consumed automatically by a built-in Render phase (draw scene → multiply by ambient → accumulate each Light2D → carve Occluder shadows), so a torch is just an entity carrying Position + Light2D. This needs an engine-owned system over user components — the same ECS hook #43 is blocked on; do it once and both features land.
  • Tier 3 — normal-mapped sprites (a per-sprite normal buffer so lights shade surfaces).
  • Tier 4 — soft shadows / penumbra, colored/gel lights, light cookies, and a day/night helper (Light.time_of_day(t) animating ambient + a DirectionalLight2D-style global light).
  • Falloff curve options (the proposal's falloff exponent) and a spot/cone light.

Related: #4 (the shipped core), #43 (the engine-owned-system hook the ECS-component layer needs).

Follow-up to #4, which shipped the light-accumulation core in a71279a: the `Light.*` namespace — `Light.ambient` (scene modulate), `Light.point` (additive radial glow, linear falloff), and `Light.occlude`/`Light.clear_occluders` (hard shadows). That is the imperative escape hatch the proposal named, covering tiers 1 (ambient + additive radial lights) and 2 (hard shadows). This tracks the remaining tiers and the ECS-native shape: - [ ] **ECS components**: `Light2D` / `Occluder` consumed automatically by a built-in Render phase (draw scene → multiply by ambient → accumulate each `Light2D` → carve `Occluder` shadows), so a torch is just an entity carrying `Position + Light2D`. This needs an **engine-owned system over user components** — the same ECS hook #43 is blocked on; do it once and both features land. - [ ] Tier 3 — normal-mapped sprites (a per-sprite normal buffer so lights shade surfaces). - [ ] Tier 4 — soft shadows / penumbra, colored/gel lights, light cookies, and a day/night helper (`Light.time_of_day(t)` animating ambient + a `DirectionalLight2D`-style global light). - [ ] Falloff curve options (the proposal's `falloff` exponent) and a spot/cone light. Related: #4 (the shipped core), #43 (the engine-owned-system hook the ECS-component layer needs).
Author
Owner

Shipped the ECS-native shape — the checkbox this issue named as needing "an engine-owned system over user components" — in 3679ce1, on the hook landed in #43.

A torch is now just an entity carrying Light2D { x, y, radius, color, intensity }, a wall an entity carrying Occluder { x, y, w, h }, and one Ambient { color } entity sets the night tint. The engine runs the whole light pass at the end of the Render phase — ambient modulate → carve occluder shadows → accumulate each additive radial Light2D → present — with no Light.* calls wired. It reuses the deterministic accumulation core from #4 (light_ambient / light_occlude / light_point) but consumes components through the by-name reflection ABI instead of imperative calls. A Light2D/Occluder reads its position from a Position { x, y } component when the entity carries one, else its own x/y, so "Position + Light2D" and a self-positioned light both work.

Runtime in runtime/native/systems_light.ludic (split from the animation systems so a game without lights never links the light pass). Documented under Engine-owned systems in LANGUAGE.md.

Proof. examples/library/light_ecs.ludic draws a white scene, spawns Ambient/Light2D/Occluder entities, drives one tick_render, and samples the framebuffer with Screen.pixel: ambient-only corner = 32, torch centre lit, a point the pillar shadows = 32 (ambient), an unblocked equidistant point lit → 32 1 32 1, now in the regression suite (74 passed, self-host fixpoint intact, no golden drift).

The remaining render-quality tiers — normal-mapped sprites (tier 3), soft shadows/penumbra, spot/cone lights, falloff-curve options, cookies and a day/night helper (tier 4) — extend the accumulation core and need no further ECS plumbing; tracked in #49. Colored lights already work (per-Light2D color). Closing as the ECS-native shape is delivered.

Shipped the **ECS-native shape** — the checkbox this issue named as needing "an engine-owned system over user components" — in 3679ce1, on the hook landed in #43. A torch is now just an entity carrying `Light2D { x, y, radius, color, intensity }`, a wall an entity carrying `Occluder { x, y, w, h }`, and one `Ambient { color }` entity sets the night tint. The engine runs the whole light pass at the **end of the Render phase** — ambient modulate → carve occluder shadows → accumulate each additive radial `Light2D` → present — with **no `Light.*` calls wired**. It reuses the deterministic accumulation core from #4 (`light_ambient` / `light_occlude` / `light_point`) but consumes components through the by-name reflection ABI instead of imperative calls. A `Light2D`/`Occluder` reads its position from a `Position { x, y }` component when the entity carries one, else its own `x`/`y`, so "Position + Light2D" and a self-positioned light both work. Runtime in `runtime/native/systems_light.ludic` (split from the animation systems so a game without lights never links the light pass). Documented under *Engine-owned systems* in LANGUAGE.md. **Proof.** `examples/library/light_ecs.ludic` draws a white scene, spawns Ambient/Light2D/Occluder entities, drives one `tick_render`, and samples the framebuffer with `Screen.pixel`: ambient-only corner = 32, torch centre lit, a point the pillar shadows = 32 (ambient), an unblocked equidistant point lit → `32 1 32 1`, now in the regression suite (74 passed, self-host fixpoint intact, no golden drift). The remaining render-quality tiers — normal-mapped sprites (tier 3), soft shadows/penumbra, spot/cone lights, falloff-curve options, cookies and a day/night helper (tier 4) — extend the accumulation core and need no further ECS plumbing; tracked in #49. Colored lights already work (per-`Light2D` `color`). Closing as the ECS-native shape is delivered.
orkun closed this issue 2026-08-31 14:50:25 +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#47
No description provided.