shader-templates/live-registry.ts
PURPOSE
Registry of live_shader templates — GLSL source shipped directly to runtime rendering with no bake step. These are the starting-point fragments for authoring live_shader VFX components: shaders dispatched per-frame against runtime-varying uniforms (endpoint world positions, per-instance seeds, elapsed seconds) instead of being pre-rasterized into a baked atlas. Currently registers three templates: lightning_beam, adaptive_beam, chain_arc_dynamic.
OWNS
LIVE_TEMPLATES: Record<LiveTemplateId, LiveTemplateSpec>— single source of truth for runtime-dispatched shader templates.LiveTemplateIdunion —'lightning_beam' | 'adaptive_beam' | 'chain_arc_dynamic'.LiveTemplateSpecinterface —{ id, name, fragGlsl, uniformSchema, previewSampleInputs }.UniformDeclinterface — local re-declaration of the uniform-entry shape (structural fallback; documented as a subset compatible withcomponent-schema.tstask 4.1).- Module-private
SHARED_UNIFORMSarray — three uniforms every live template inherits (u_resolution,u_palette,u_intensity).
READS FROM
./live/lightning_beam.frag— beam-between-two-points with flicker (spanBetween-style)../live/adaptive_beam.frag— laser / rail with separate halo and inner core widths../live/chain_arc_dynamic.frag— per-jump segment for a chain-arc with lifetime-driven fade.
PUSHES TO
Nothing — pure data export. Consumers (the weapon workbench’s component editor, library pane, and new-component modal) import LIVE_TEMPLATES to populate the live-shader picker, seed fragGlsl into a new component, and feed previewSampleInputs into the preview renderer.
DOES NOT
- Does not compile, link, or run GLSL — only references frag strings imported from sibling files.
- Does not bake to a texture atlas — that is the explicit point of the live family (vs. the baked registry in
registry.ts). - Does not validate uniform values, instance counts, or endpoint positions at runtime —
rangeis advisory metadata for the editor. - Does not own the shared uniform contract globally — it re-declares
UniformDecllocally as a structural fallback and documents the planned reconciliation withcomponent-schema.ts. - Does not include the
handle: 'radius' | 'angle'variants in practice; onlyhandle: 'point'is used by current entries. - Does not own
familytagging — there is nofamilyfield onLiveTemplateSpec; the whole file is implicitly one family.
Signals
| Signal | Source | Shape |
|---|---|---|
LIVE_TEMPLATES[id] | exported const | LiveTemplateSpec — { id, name, fragGlsl, uniformSchema, previewSampleInputs } |
LiveTemplateId | type | 'lightning_beam' | 'adaptive_beam' | 'chain_arc_dynamic' |
LiveTemplateSpec | interface | spec contract (adds name + previewSampleInputs vs. baked TemplateSpec) |
UniformDecl | interface | { name, type, doc, range?, default?, handle?, handleColor? } |
Entry points
- Weapon workbench
NewComponentModallistsLIVE_TEMPLATESso a newlive_shadercomponent can be seeded from a template id. ComponentEditorreadsLIVE_TEMPLATES[id].fragGlslas the starting GLSL anduniformSchemato render uniform editors.ForgeLibraryPanesurfaces the template names for the live-shader category in the library tree.- Preview renderer feeds
previewSampleInputsinto the shader to draw a canonical preview frame without needing live gameplay state.
Pattern notes
- Three templates, all spanBetween-shape. Every current entry takes
uFromXY+uToXYvec2endpoint pair (withhandle: 'point'so the workbench draws drag gizmos) plus auWidthpixel scalar. The family is implicitly “directed effect between two world points.” - Shared uniform contract.
SHARED_UNIFORMSis spread into every entry’suniformSchemaso all live templates start withu_resolution(vec2 px),u_palette(vec3[4]Quilez cosine palette flat), andu_intensity(HDR multiplier 0–4, default 1). Per-template uniforms are appended after. - Pixel-space uniforms. All position / width uniforms are in canvas / world pixels, not normalized [0,1] UV — different from the baked
registry.tsconvention where most ranges are 0–1. - Time / seed split.
lightning_beamandchain_arc_dynamicdeclare per-instanceuSeed: float [0,1]plus auTimeSecclock (game-start for lightning, jump-start for chain_arc).adaptive_beamis time-stateless.chain_arc_dynamicadditionally declaresuLifetimeMs(50–500 ms) so the shader can fade based onuTimeSec / uLifetimeMs. previewSampleInputsis a flatRecord<string, unknown>keyed by uniform name. Vec2 values are[x, y]arrays,vec3[4]palette is one length-12 flat array of floats. The 720×720 canvas resolution is hard-coded in every preview.- Local
UniformDeclre-declaration. Header comment notes this is a structural fallback subset; if task 4.1 landscomponent-schema.tsas the canonical schema, this should stay compatible. - Default palette per template is encoded in
previewSampleInputs:lightning_beam— blue-cyan accent (0, 0.5, 0.8).adaptive_beam— mid-spectrum default (0, 0.33, 0.67).chain_arc_dynamic— purple-blue accent (0.2, 0.4, 0.9),u_intensity: 2(hotter).
- No
familyfield. Unlike bakedTemplateSpec,LiveTemplateSpeccarries no family tag — the whole file is the family. Library-pane grouping for live shaders must be inferred from the registry’s existence rather than per-entry metadata.
EXTRACT-CANDIDATE
- Duplicate
UniformDecl/UniformEntrytypes. This file’sUniformDeclandregistry.ts’sUniformEntryare near-identical (same fields, sameUniformTypeunion, samehandle/handleColoreditor metadata) — divergence is thatUniformDecl.defaultis optional whileUniformEntry.defaultis required, andUniformDeclhas adocfield thatUniformEntrymakes optional. Header comment explicitly flags this as a structural fallback pendingcomponent-schema.ts(task 4.1). Action: collapse to one shared uniform-schema type imported fromcomponent-schema.tsonce that exists, drop the local re-declaration. - Two parallel template registries.
LIVE_TEMPLATES(this file, 3 entries) andTEMPLATES(registry.ts, 66 entries) export different*Specshapes —LiveTemplateSpecaddsname+previewSampleInputs, dropsfamily. The bakedTemplateSpechas noname(id doubles as display) and no preview inputs. Consider unifying under oneShaderTemplateSpecwithkind: 'baked' | 'live'and optionalpreviewSampleInputs, so the workbench library pane can iterate one collection. previewSampleInputsis missing from baked templates. The baked registry uses per-uniformdefaultvalues to drive the preview; live templates instead carry a separatepreviewSampleInputsmap. Either back-fillpreviewSampleInputsonto baked entries or derive live previews fromdefaultvalues — having two preview conventions is the kind of thing that drifts.SHARED_UNIFORMSis duplicated as comments in baked registry. The bakedregistry.tsdocuments the shared uniform contract (u_time,u_resolution,u_palette[4],u_intensity) only in the add-a-template header comment and does not push them throughuniformSchema. Live templates do push the shared three explicitly. Pick one convention.namefield is hand-written display copy. Worth treating consistently — either every template across both registries gets aname, or both rely on id-to-title-case derivation in the UI.- All three live templates are span-between shapes. If a future live template diverges (e.g. point-source aura, mesh-bound projection), revisit whether the implicit “two endpoints + width” pattern wants to be enforced by typing rather than convention.
- No
familyon live templates. IfLIVE_TEMPLATESgrows, add a family field (e.g.'beam' | 'arc' | 'aura') so the library pane can group entries — otherwise the picker becomes a flat unsorted list. UniformDecl.typeincludesvec3andvec3[4]but only'float'and'vec2'are used in per-template entries;vec3[4]appears once via the sharedu_paletteandvec3is declared but unused. Same observation applies to baked registry — candidates for trimming or completing the helper set.