feat(lighting): add the @codexo/exojs-lighting extension package - #664
Conversation
Forward normal-mapped point lighting for sprites, shaded inside the sprite fragment stage so a lit scene costs no extra pass and no extra draw call. PointLight is plain mutable world-space data. LightingSystem collects lights and packs them, together with the active count and the ambient term, into one rgba32f DataTexture per frame - a header column plus one column per light. That is why the light count is a shader loop bound instead of a compiled-in constant: core user uniforms are one vec4 per name, so a uniform array would have capped the scene at a handful of lights and needed a recompile to change. LitSpriteMaterial samples a tangent-space normal map next to the albedo and rotates the normal by the instance's local-to-world basis, so spinning and mirrored sprites keep their bumps facing the right way. One normal map per material (= per atlas) is the v1 contract; a second atlas takes a second material and breaks the batch at that boundary. No deferred path. Also stop type-aware lint rules from throwing on every package's rolldown.config.ts: those files belong to no package's TypeScript program, so the ProjectService has no types to serve for them. Claude-Session: https://claude.ai/code/session_01NSrXpH1WyqP7udpWWhHz1i
… both backends normal-mapped-sprites drops its hand-written material and uniform bookkeeping for LightingSystem/LitSpriteMaterial. many-lights is new: a slider walks the scene from 1 to 48 point lights over a normal-mapped floor, which is only a slider because the light list is a data texture - a uniform array would have been a recompile per count, and a cap far below 48. Browser pixel tests on both backends assert the same three facts: the falloff reaches the framebuffer, a mirrored instance shades identically to an upright one, and the two quads stay in a single draw call. The WebGPU one also pops a validation scope, because binding an rgba32f texture through the sprite material's group(2) layout is the part that could silently be invalid. Claude-Session: https://claude.ai/code/session_01NSrXpH1WyqP7udpWWhHz1i
The GLSL compile suite globs every package's shader files and requires each to be either half of a program pair or declared standalone. `lit-sprite.frag` is a sprite-material fragment, so it only compiles once the renderer has spliced in the base-texture slot table and `sampleBase()`; the suite now composes it the same way it already composes the text-atlas fragments, and links it against `sprite-material.vert` - which stops being "standalone, fragment comes from the application" now that an in-repo fragment exists for it. Also map `@codexo/exojs-lighting` in test/tsconfig.json so the two lighting browser suites type-check. Claude-Session: https://claude.ai/code/session_01NSrXpH1WyqP7udpWWhHz1i
Bundle ReportChanges will increase total bundle size by 18.06MB (57.88%) ⬆️
Affected Assets, Files, and Routes:view changes for bundle: site-server-esmAssets Changed:
App Routes Affected:
view changes for bundle: exo-full-iife-Exo-iifeAssets Changed:
Files in
view changes for bundle: exo-full-iife-min-Exo-iifeAssets Changed:
Files in
|
Forward normal-mapped point lighting for sprites, as an opt-in extension package.
PointLight: plain mutable world-space data (x,y,radius,color,intensity,height).LightingSystem: collects lights and packs them, with the active count and the ambient term, into onergba32fdata texture per frame; registers on anySystemRegistry. The light list being a texture rather than a uniform array makes the light count a shader loop bound instead of a compiled-in constant.LitSpriteMaterial: samples a tangent-space normal map next to the albedo and rotates the normal by the instance's local-to-world basis (the Core seam from feat(rendering): expose world position and basis to custom sprite materials #659), so spinning and mirrored sprites keep their bumps facing the right way. Sprites sharing one material stay in one batch; no extra pass, no extra draw call.lighting/normal-mapped-sprites(moved onto the package) andlighting/many-lights(1 to 48 lights, one draw call), both verified in the browser. Unit tests for packing and lifecycle, pixel tests on WebGL2 and WebGPU.release:bootstrapat the next coordinated release (noted in RELEASING.md).No Core changes were needed:
DataTexturewithrgba32falready works as a material texture on both backends.https://claude.ai/code/session_01NSrXpH1WyqP7udpWWhHz1i