Skip to content

A material can choose its own lights on the Wasm engine (lightsNode) - #618

Merged
jonit-dev merged 3 commits into
developfrom
feat/material-lights
Nov 6, 2026
Merged

jonit-dev merged 3 commits into
developfrom
feat/material-lights

Conversation

@jonit-dev

@jonit-dev jonit-dev commented Nov 6, 2026 •

Copy link
Copy Markdown
Contributor

What is this PR about?

three.js lets a material sum only the lights it lists: material.lightsNode = lights([cabin]). The owned Wasm engine refused it (TN_NATIVE_UNCATALOGUED_LIGHTS: lights is not available on the Wasm engine.) and the scene aborted. This PR makes it work on the owned engine (Wasm on web, and the shared C++ the V8 desktop player runs).

Why does it matter?

Starlance (sandbox friction log, 2026-11-05) needs to light a camera-parented cockpit with its own cabin lamp. The sun reached the interior unshadowed and blew near-black materials to cream, so the game shipped a metalness hack instead. With this PR the game writes the portable three.js line and the sun stays off the interior.

What changed

  • lights([...]) crosses as one engine object per light (browser bridge and V8 bridge), and lightsNode is bound on every node material (catalog, registry, generated types).
  • The renderer marks each direct light that a material's lightsNode leaves out as excluded in that program's light layout, keys the program on the listed light ids, and writes only the listed ambient and hemisphere terms into that program's frame constants. Materials without a lightsNode are unchanged.
  • New conformance row material-lights: blue sun, ambient, warm cabin lamp parented to the camera; right knot and dashboard panel use lights([cabin]).

Proof

  • ✅ Red: owned Wasm engine on develop fails the row with TN_NATIVE_UNCATALOGUED_LIGHTS (the friction). Green: the row passes; Wasm capture vs upstream three.js pixelMismatchRatio 0, perceptualDeltaE 0 (2 runs per arm, headed Chromium WebGPU, adapter nvidia/turing).
  • ✅ Provenance: the AFTER page reports the Wasm engine's own globals (__tnWebThreads "on (8)", 35 MB Wasm heap, 396 crossings).
  • ✅ Visual judge (fresh Opus subagent, criteria set first): IMPROVEMENT, 4/4 criteria PASS (cabin-only light on the right knot and panel, sun kept on the left knot, match with upstream).
  • ✅ Unit: new red-green browser-tsl.spec.ts test; three-native 491/491; conformance runner 90/90; registry/CI script specs 28/28; starter-post bundle 11/11; lint pass.
  • ✅ Native core (CI's native-engine recipe, local): ctest -L native-engine 247/247, incl. the registry snapshot and new red-green cases in abi_test (lights marshalling) and material_test (excluded light declares nothing).
  • ✅ pnpm budgets with a re-measured native coverage record.
  • ⚠️ Unverified locally: the V8 desktop player run (v8_adapter_test needs the V8 player build); the merge queue's full board runs it.

BEFORE / AFTER / TARGET

…the owned engine

three's `material.lightsNode = lights([cabin])` refused on the Wasm engine with
TN_NATIVE_UNCATALOGUED_LIGHTS, so a camera-parented cockpit could not light its
interior with its own cabin lamp while the sun stayed off it (Starlance friction,
2026-11-05).

- `lights([...])` crosses as one engine object per light (browser and V8 bridges)
  and builds a `lights` node; `lightsNode` is bound on every node material.
- The renderer marks each direct light a lightsNode leaves out as excluded in the
  program's light layout ('-'), keys the program on the listed ids, and writes only
  the listed ambient and hemisphere terms into that program's frame slab.
- Conformance row `material-lights`: the Wasm engine matches upstream three
  (pixelMismatchRatio 0, deltaE 0); develop fails it with the original refusal.
…n the native core tests

abi_test: lights([...]) keeps one engine object per light in order and refuses a non-light.
material_test: a layout with an excluded light ('-') declares no uniforms for it (standard,
physical, lambert, phong); red without the shader skip, green with it. native-engine ctest 247/247.
@jonit-dev
jonit-dev added this pull request to the merge queue Nov 6, 2026
@github-actions

github-actions Bot commented Nov 6, 2026

Copy link
Copy Markdown
Contributor

The merge queue stopped on template-nonvisual (runner) (failure).

Fix, push, re-enqueue.

@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Nov 6, 2026
@jonit-dev

Copy link
Copy Markdown
Contributor Author

Merge-group run 42153247173 went red on template-nonvisual (runner) → "Check pristine template": ERR_PNPM_FETCH_404 GET https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.478.tgz at 03:04:15 UTC, 10 s after npm published that version (03:04:05). Registry propagation, not this diff: the tarball now returns 200, and the same runner scaffold (packed from this branch) passes pnpm install && pnpm run check locally. Re-queued.

@jonit-dev
jonit-dev added this pull request to the merge queue Nov 6, 2026
@jonit-dev
jonit-dev removed this pull request from the merge queue due to a manual request Nov 6, 2026
@jonit-dev
jonit-dev merged commit 0a98178 into develop Nov 6, 2026
37 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant