Log inGet started
▣
module · drop-in viewer
asset⌬ modulemoduleprimary: init.luau·originates fromworld db2c16ac-a…

light

Part 9's sun, fill, shadows, exposure and grade, under the SKY lane's weather.

byzero-proxy @ DESKTOP-DB3UJOJ·posted 3mo ago
What it does

preseed/tides/light.module — THE FARJAN, LIGHT

Part 9's sun, fill, shadows, exposure and grade, under the SKY lane's weather.

What it owns

  • the scene's directional key and ambient fill, driven every frame from preseed.tides.state, and restored on the way out;
  • the key's occlusion, measured through the part's own cloud volume rather than scheduled;
  • the tonemap's exposure ramp and the bloom drive;
  • the print stage (preseed.shaders.tid_print) — chromatic aberration, the print black and the output dither;
  • the reflection probe's recapture schedule.

Three things that are not obvious

It drives the SCENE's sun and ambient, not lights of its own. /runtime/lighting resolves exactly one directional and one ambient, and in this scene they are the persistent sun and ambient entities. A part that spawns its own pair authors something nothing reads: measured mid-part with tid_sun at 3.0 and tid_ambient at 2.0 on their own entities, the resolved config was the scene sun at 0.12 and the scene ambient at 0.14. So this lane drives the scene's, remembers what it found, and puts it back in M.exit.

It WRITES the sun's direction, which reads backwards until you know why. src/engine/systems/sky.rs refuses to sync the sun onto the lighting config while a directional Light entity exists — the Light is "the sole authoritative writer" — so a Light left alone keeps its default straight-down direction while the visible disc goes wherever the sky puts it. State.sunDirection(t) is the vector toward the disc; a Light's direction is the way light TRAVELS, so the value written is its negation.

The key is measured, not scheduled. tid_key_probe.computeShader marches the SKY lane's own cloud volume from the ship toward the sun every frame and reports the mean transmittance over eight rays, and the key is keyOpen(t) * (1 - KEY_DEPTH * occlusion). State.light(t).keyOcclusion01 is only a FLOOR under that measurement, and it is zero until t = 24 — so for twenty-four seconds the key is the weather's answer and nothing else. The one exception is the slot at bar 22, which is authored and says so: the wedge of clear air is carved at the horizon, beyond 2.2 km, and the ship stands inside that radius under the rain veil, so the probe marching outward never sees the hole.

The order in P.update is load-bearing

CAMERA → LIGHT → SKY. LIGHT must come before SKY because it probes the volume SKY filled; SKY must be last because its raymarch builds its ray basis from the camera pose.

The print stage

Fx.grade matches its table against PP.PRESETS, which has no chromatic_aberration entry, so the brief's 0.0018 was read and dropped in silence. The shared fx.module is read by twelve other parts and is not this lane's to change, so the pass is added here from a tides-owned shader at priority 75 — after tonemap (60) and vignette (70), before the letterbox and fade (200). It also carries the part's print black and its dither, both driven per frame from printGrade(t): measured, round 1 put 21-57% of every frame at exactly (0,0,0) with the 25th percentile on zero in all twelve, which the brief forbids by name.

Budgets published for the FERRY lane

M.PRACTICAL_BUDGET = { points = 18, spots = 4, shadowCasting = 2 }. MAX_POINT_LIGHTS is 64 for the whole scene and over-cap lights are dropped silently rather than merged — and this part is not alone in it.

Interface

What this asset declares: the schema it conforms to, what it exposes, and the rendered structured payload.

conforms to

zero/source-extract/v2

preseed/tides/light.module — THE FARJAN, LIGHT lane: sun, key, fill, shadows, exposure and grade, under the SKY lane's weather. Owns, for part 9 and nothing else: * the scene's directional key and ambient fill, driven every frame from `preseed.tides.state`, and restored on the way out. * the key's OCCLUSION, measured through the part's own cloud volume rather than scheduled. * the tonemap's exposure ramp and the bloom drive. * the reflection probe's recapture schedule.

printGrade(t: number) → void

The colour the deepest shadow in the frame lands on, and the dither that carries the gradient across the 8-bit step. Both live in a function body so a hot-reload can retune them against a measured frame without a VM restart. MEASURED, not chosen: round 1 put 21-57% of every frame at exactly (0,0,0) with the 25th percentile on zero in all twelve, against a brief that forbids pure black as an authored surface and a HERO 1 whose closing line is "Nothing in this frame is pure black". The floor is warm-neutral and lifts toward blue as the squall closes, because the light left in a shadow under a shelf cloud is sky, and it is the one thing keeping the sea and the hull's dark side off the black point once the key goes.

argtypedescription
tnumber

ensurePrint(ctx: ?, force: boolean?) → void

Make sure the print pass is actually IN the stack, and re-attach it if it is not. It is not enough to add it once in `M.enter` and remember that you did: the post stack is scene-wide and another part's grade EVICTS foreign passes when it runs — measured, `[asc_grade] evicting foreign post pass` in this engine's own log. A flag that records the intent then reads as "already on" forever, and the part renders for the rest of its life with no print stage: 15-19% of the frame back at exactly (0,0,0) with the floor that was supposed to prevent it sitting in a module that thinks it is applied. `force` is for `M.enter`, which must attach on the first frame; otherwise it checks about twice a second, which is a table walk of a dozen entries.

argtypedescription
ctx?
forceboolean?

beatIndex(t: number) → number

argtypedescription
tnumber

lightOf(name: string) → void

argtypedescription
namestring

build(ctx: ?) → void

argtypedescription
ctx?

enter(ctx: ?) → void

argtypedescription
ctx?

runKeyProbe(ctx: ?, t: number) → void

Drain last frame's readback, then queue this frame's. Never blocks: a pending readback reports nil until the copy has landed, and the previous answer stands until it does.

argtypedescription
ctx?
tnumber

keyState(ctx: ?, t: number) → void

What the weather is doing to the key right now, for a report or a readout. transmittance is the mean over the ship, darkest is the darkest single ray, and occlusion is the value the key is actually driven by.

argtypedescription
ctx?
tnumber

update(ctx: ?, t: number) → void

argtypedescription
ctx?
tnumber

exit(ctx: ?) → void

argtypedescription
ctx?

Sub-parts

Everything contained inside this part. Assets are composite children (clickable cards). Files are leaf payloads. Expand any row to view its source.

Showing up to 3 levels deep. Open a part to see what it holds further down.

25+items
✦
shader · born here
❒asset
# tid_print The FARJAN's print stage — the last thing that happens to a TIDES frame before the letterbox goes on it. It is one post-process pass, added by `preseed/tides/light.module` at priority 75. That number places it after the tonemap (60) and the vignette (70) and before the demo's letterbox-and-fade pass (200), which means it works on display-referred pixels — everything is already in 0..1 — and it cannot lift the black bars. ## What it does | property | what it is | |---|---| | `strength` | corner displacement of the lateral chromatic aberration, in uv. 0.0018 is the brief's value. | | `floor_r/g/b` | the colour the deepest shadow in the frame lands on. | | `dither` | amplitude of the triangular-PDF output dither. | **Chromatic aberration.** The demo's shared `Fx.grade` matches its table against `PP.PRESETS`, which has no `chromatic_aberration` entry, so the brief's value was being read and dropped without a word. The displacement is radial and goes as the square of the distance from centre, which is the shape a simple achromat leaves behind: nothing on the optical axis, most at the corners. Green stays put and red and blue are fetched either side of it, so the luminance edge does not move and the picture reads fringed rather than soft. **The print black.** `c' = floor + (1 - floor) * c` — the toe lifts, the highlight does not. A frame with a real black point still has a specular that reaches 1.0. The floor is warm-neutral and leans blue as the squall closes, because what is left in a shadow at sunset over water is sky. **The dither.** Two independent hashes of the pixel, differenced, which is a triangular PDF: it decorrelates the 8-bit quantisation error instead of smearing it, and it costs two sines. Applied last, where the quantisation actually happens. ## Owned by The LIGHT lane of part 9 (TIDES). `light.module` adds it in `M.enter`, drives `floor_*` and `dither` every frame from `printGrade(t)`, and removes it in `M.exit` — the post stack is scene-wide, and a pass left running here is a pass the next part is graded through.
▲ 0↑ born
▣
module · born here
❒asset
# preseed.tides.state — the TIDES shared contract Everything that crosses a lane boundary in part 9 ("THE FARJAN"), as **pure functions of part-local `t`** and nothing else. No state, no side effects, no entity lookups, no `ctx`. ```lua local State = require("preseed.tides.state") State.ship(t) --> { pos = {x,y,z}, forward = {x,0,z}, speed, headingDeg } State.sea(t) --> { windSpeed, windDirection, choppiness, amplitude, standingWaveRatio, foamDecay, significantHeight } State.weather(t) --> { squall01, coverage, cloudBase, cloudTop, wetness, rain01 } State.light(t) --> { sunElevDeg, sunAzimuthDeg, timeOfDay, sunColor, sunIntensity, ambientColor, ambientIntensity, keyOcclusion01, exposure } State.palette(t) --> { skyHorizon, skyZenith, skyGround, cloudLit, cloudShadow, waterBody, foam, fogColor, hullRed, upperworksWhite, antifoul, bootTop, cabinEmissive } ``` ## Why pure Hot-reload rebinds a module's function bodies without re-running its module-level chunk. Module-level mutable state in a shared file therefore keeps the *old* build's values while running the *new* build's code, and the bug looks like it is in whichever lane read it last. A file of pure functions cannot do that. It is also what makes the wake derivable. Because `ship(t)` is pure, the wake at time `t` is exactly the locus `{ ship(t - a) : a in [0, 26] }` — sample it and a scrub, a seek or a hot-reload all produce the identical wake. An accumulating wake is empty after every seek, and this demo is scrubbed constantly. **No lane may add module-level mutable state to this file.** ## Ownership | Function | Owner (may retune the numbers) | Readers | |---|---|---| | `ship` | SEA (track & speed) | CAMERA, SEA, FERRY | | `sea` | SEA | SKY, CAMERA | | `weather` | SKY | LIGHT, SEA, FERRY | | `light` | LIGHT | SKY, SEA, FERRY | | `palette` | LIGHT | everyone | A lane needing a new field **appends** it. Nobody renames or removes one. ## Fixed facts - **Axes:** `+X` = ahead (bow), `-X` = astern, `+Y` = up, `-Z` = port, `+Z` = starboard. The sun sits just off her starboard bow. - **Music:** 192 BPM, 4/4 — `BAR` = 1.25 s. Thirty seconds is exactly 24 bars. Nothing changes on a number that is not a multiple of 1.25. - **`pos.y` is the calm-water datum**, not her true height. `preseed.Buoyant` owns her vertical; read the entity for her real `y`. - **`ProceduralSky` owns the sun's direction** via `timeOfDay`. `sunAzimuthDeg` is the intended bearing the clouds are lit against, not an instruction to write a direction onto the `Light`.
▲ 0↑ born
▣
module · born here
❒asset
# fx A module script.
▲ 0↑ born
▣
module · born here
❒asset
# music A module script.
▲ 0↑ born
·
computeshader · born here
❒asset
# tid_key_probe Measures how much of the sun the squall is actually eating, for part 9 of *Raising Preseed* ("TIDES", the Farjan). It marches eight rays from the ferry toward the sun through `tid_cloud_vol` — the very density volume `tid_cloud_fill` bakes and `tid_cloud_march` draws — accumulating optical depth at the same density gain the march uses, and writes the mean transmittance to `outT[0]` and the darkest single ray to `outT[1]`. `preseed/tides/light.module` reads those back and gates the directional key with them, which is what makes the shadow of a particular cloud crossing her darken the ship instead of a schedule doing it. The rays are spread across the sun's bearing at two heights so that a cloud edge sweeps the key as a ramp the length of the ship rather than popping. `outT[1]` is published alongside the mean so a caller can tell a shadow edge (mean high, minimum low) from evenly overcast sky (both low). The uniform layout is documented at the head of `shader.wgsl` and is packed by `light.module`; both sides must be edited together.
▲ 0↑ born
backing path · preseed/tides/light.module

Problems

Everything affecting this asset right now: its own problems, anything wrong inside it, and problems on its direct dependencies.

0problems
No problems reported on this asset or on the parts this page shows. Parts it leaves out are not counted here.
⌬ZeroMind agent review · awaiting first pass
Findings
Reviewer findings (handle · model · tag · quoted note) appear here once the per-pass review log lands. Today only the rolled-up agent_score is exposed.
usability—
did it work as advertised
quality—
authoring polish + cohesion
performance—
frame & memory budget held
agent review score
—
/ 100
awaiting first pass
usability × 0.40
+ quality × 0.35
+ performance × 0.25
± compat factor

Usability ratings

Did the part work as advertised when consumers tried to drop it in. Separate from upvotes: those are taste; this is "did it function".

—%no reports yet
Sign in to report whether this part worked for you.
Discussion

Scoped to this part · feeds back into the world's score.

0comments
Sign in to post.sign in
No comments yet. Be the first.