The remaining lighting tiers from the original proposal, all extending the deterministic accumulation core (light.ludic) — no new ECS plumbing: - Light.spot: cone / flashlight lights (direction + spread degrees), with a self-contained integer atan2-in-degrees and a feathered edge. - Light.falloff: a brightness-ramp exponent (1 linear, 2 quadratic, …) via repeated fixed multiply. - Light.soft: soft shadows — an area-sampled light so an occluder edge fades through a penumbra instead of a hard cut. - Light.gel + Light.clear_gel: colour cookies — a light gels from its centre colour to a rim colour. - Light.normal + Light.clear_normals + Light.height: a normal G-buffer so surfaces shade by facing (N·L), not distance alone (tier 3). - Light.time_of_day: a day/night ambient ramp from a single 0..1 value. The engine lighting system (systems_light.ludic) consumes matching optional Light2D fields — direction/spread/falloff/softness/gel — each defaulting off so an older five-field Light2D lights exactly as before. Every tier is integer + Q16.16 fixed, so scenes light identically on every run and headless. Worked example + regression: examples/library/light_tiers.ludic (1 1 1 1 1 1 1 1 1). Nine new docs/language/light pages. Full suite 76 passed, self-host C-free fixpoint intact, no golden drift. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1.5 KiB
1.5 KiB
| id | name | category | kind | tokens | sig | tip | order | ns | member |
|---|---|---|---|---|---|---|---|---|---|
| light-normal | Light.normal | light | namespace-method | Light.normal | Light.normal(x, y, width, height, nx, ny) | Stamp a surface normal over a rectangle so lights shade it by facing (N·L). | 10 | Light | normal |
Stamps a surface normal over a screen rectangle into the light pass's normal buffer. Where a normal is stamped, a light shades the surface by the angle it faces — the Lambert term N·L — not by distance alone: a wall facing a torch is bright, one turned away is dim, giving flat sprites a sense of relief (tier 3). nx and ny are the normal's x/y as a fixed in [-1, 1] (a surface facing straight at the camera is 0, 0); the z component is derived. Unstamped pixels are unaffected, and the buffer is allocated only when you stamp — clear it each frame with Light.clear_normals. Deterministic (Q16.16 dot product).
Parameters:
x,y,width,height— the screen rectangle to stampnx,ny— the surface normal's x/y, afixedin[-1, 1]
program Demo {
property Wall { x: int = 0, y: int = 0 }
model Block { Wall }
handler Render phase Update {
Screen.clear(0)
Light.ambient(Color.rgb(24, 24, 32))
Light.clear_normals()
Light.normal(60, 50, 16, 24, 0.7, 0.0) # this face is tilted rightward
Light.point(120, 62, 90, Color.rgb(255, 240, 200), 1.0)
Screen.show()
}
}