feat(rendering): add Light.* — deterministic 2D light accumulation with hard shadows (#4)
A software light pass over the framebuffer, run in a render phase after drawing the scene: Light.ambient multiplies the scene toward a tint (night/cave mood), Light.point additively accumulates a radial glow with linear falloff clamped per channel, and Light.occlude / Light.clear_occluders cast hard shadows by blocking a light's rays against rectangular occluders. Integer + Q16.16 fixed throughout, so a scene lights identically every run and in a headless render (diffable). Engine in runtime/native/light.ludic, spliced on demand (g_uses_light) like the regex/query runtimes; namespace wired in emit_call.ludic. Ships issue #4 tiers 1 (ambient + additive radial lights) and 2 (hard shadows). Normal-mapped sprites, soft shadows, a day/night directional light, and auto-consuming Light2D/Occluder components are follow-ups (the auto-system hook is tracked by #43). - runtime/native/light.ludic: the light-accumulation engine (isqrt falloff, segment/occluder shadow test, ambient modulate) - examples/library/lighting.ludic: 14 pixel-readback assertions - docs/language/light/: Light.ambient/point/occlude/clear_occluders - tools/x/test.ludic: lighting.ludic in the regression suite Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
31cfbc2465
commit
a71279a7b9
12 changed files with 15747 additions and 14968 deletions
|
|
@ -238,6 +238,16 @@ function emit_ns_call(ns: pointer, meth: pointer, e: Node) -> Val {
|
|||
if (meth == "flood") { bare = "grid_flood"; push(labels, "x"); push(labels, "y"); push(labels, "wall") }
|
||||
if (meth == "a_star") { bare = "path_a_star"; push(labels, "x0"); push(labels, "y0"); push(labels, "x1"); push(labels, "y1"); push(labels, "wall") }
|
||||
}
|
||||
# Light.* — the 2D light-accumulation pass (runtime/native/light.ludic, spliced
|
||||
# on demand). A game runs it in its render phase: ambient multiplies the scene
|
||||
# down, point adds a radial glow (blocked by occluders -> hard shadows). Screen
|
||||
# space, deterministic (integer + Q16.16), diffable. `energy` is a fixed.
|
||||
if (ns == "Light") {
|
||||
if (meth == "ambient") { bare = "light_ambient"; push(labels, "color") }
|
||||
if (meth == "point") { bare = "light_point"; push(labels, "x"); push(labels, "y"); push(labels, "radius"); push(labels, "color"); push(labels, "energy") }
|
||||
if (meth == "occlude") { bare = "light_occlude"; push(labels, "x"); push(labels, "y"); push(labels, "width"); push(labels, "height") }
|
||||
if (meth == "clear_occluders") { bare = "light_clear_occluders" }
|
||||
}
|
||||
# Query.* — ECS spatial queries over the reflection ABI (runtime/native/query.ludic,
|
||||
# spliced on demand). `prop` is a property id (World.prop_id); the spatial forms
|
||||
# read two int fields (field ids) as (x, y). nearest/first return an entity (-1 =
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue