@babylonjs/lite-gl 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/README.md +10 -34
  2. package/apply-states.d.ts +1 -0
  3. package/apply-states.js +30 -0
  4. package/apply-states.js.map +1 -0
  5. package/blend.d.ts +123 -0
  6. package/blend.js +194 -0
  7. package/blend.js.map +1 -0
  8. package/context.d.ts +130 -0
  9. package/context.js +354 -0
  10. package/context.js.map +1 -0
  11. package/depth-stencil.d.ts +155 -231
  12. package/depth-stencil.js +399 -262
  13. package/depth-stencil.js.map +1 -1
  14. package/dynamic-texture.d.ts +59 -149
  15. package/dynamic-texture.js +123 -69
  16. package/dynamic-texture.js.map +1 -1
  17. package/effect-renderer.d.ts +65 -0
  18. package/effect-renderer.js +132 -0
  19. package/effect-renderer.js.map +1 -0
  20. package/effect.d.ts +142 -0
  21. package/effect.js +465 -0
  22. package/effect.js.map +1 -0
  23. package/html-texture.d.ts +44 -143
  24. package/html-texture.js +87 -81
  25. package/html-texture.js.map +1 -1
  26. package/index.d.ts +23 -1482
  27. package/index.js +42 -263
  28. package/index.js.map +1 -1
  29. package/mesh.d.ts +210 -307
  30. package/mesh.js +436 -311
  31. package/mesh.js.map +1 -1
  32. package/package.json +5 -29
  33. package/render-loop.d.ts +8 -0
  34. package/render-loop.js +62 -0
  35. package/render-loop.js.map +1 -0
  36. package/render-target.d.ts +233 -290
  37. package/render-target.js +534 -335
  38. package/render-target.js.map +1 -1
  39. package/scissor.d.ts +30 -72
  40. package/scissor.js +47 -33
  41. package/scissor.js.map +1 -1
  42. package/shader.d.ts +22 -0
  43. package/shader.js +65 -0
  44. package/shader.js.map +1 -0
  45. package/sprites.d.ts +182 -263
  46. package/sprites.js +426 -10
  47. package/sprites.js.map +1 -1
  48. package/state.d.ts +126 -0
  49. package/state.js +153 -0
  50. package/state.js.map +1 -0
  51. package/texture.d.ts +142 -0
  52. package/texture.js +433 -0
  53. package/texture.js.map +1 -0
  54. package/effect-BxxwfB_O.js +0 -737
  55. package/effect-BxxwfB_O.js.map +0 -1
  56. package/sprites--1oyVtJ3.js +0 -437
  57. package/sprites--1oyVtJ3.js.map +0 -1
  58. package/state--j_ncWIi.js +0 -155
  59. package/state--j_ncWIi.js.map +0 -1
  60. package/texture-DaMd1gGm.js +0 -329
  61. package/texture-DaMd1gGm.js.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"file":"effect-BxxwfB_O.js","sources":["../src/context.ts","../src/shader.ts","../src/effect.ts"],"sourcesContent":["import type { GLEffect } from \"./effect.js\";\nimport type { GLTexture } from \"./texture.js\";\nimport type { GLRenderTarget } from \"./render-target.js\";\nimport { createGLState, resetGLState, resetGLStateCache, type GLState } from \"./state.js\";\n\n/**\n * Minimal shape of a context-restorable GL buffer (vertex / index), kept local\n * to this module so the public `GLEngineContext` type carries no dependency on\n * the tree-shakeable `mesh` entry. `mesh.ts`'s `GLVertexBuffer` /\n * `GLIndexBuffer` satisfy it structurally.\n * @internal\n */\nexport interface GLManagedBuffer {\n handle: WebGLBuffer;\n /** @internal */\n _disposed: boolean;\n /** @internal */\n _deleteGpu: (gl: WebGL2RenderingContext) => void;\n /** @internal */\n _restore: (engine: GLEngineContext) => void;\n}\n\n/** Constructor options forwarded to `canvas.getContext('webgl2', …)`. */\nexport interface GLEngineOptions {\n /** Default: true. */\n alpha?: boolean;\n /** Default: true. */\n premultipliedAlpha?: boolean;\n /** Default: false. */\n antialias?: boolean;\n /** Default: false. */\n preserveDrawingBuffer?: boolean;\n /** Default: false — disabled for fullscreen-quad workloads. */\n depth?: boolean;\n /** Default: false. */\n stencil?: boolean;\n /** Default: \"default\". */\n powerPreference?: WebGLPowerPreference;\n /** Default: false. */\n failIfMajorPerformanceCaveat?: boolean;\n}\n\n/** Read-only WebGL2 capability limits, queried once at context creation. */\nexport interface GLEngineCaps {\n /** `gl.MAX_TEXTURE_SIZE` — largest supported texture dimension, in texels. */\n readonly maxTextureSize: number;\n /** `gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS` — number of sampler binding slots. */\n readonly maxTextureUnits: number;\n /** The `KHR_parallel_shader_compile` extension used for async link polling,\n * or null when unsupported — linking is then treated as synchronous. */\n readonly parallelShaderCompile: { COMPLETION_STATUS_KHR: number } | null;\n /** True when 32-bit float color attachments are renderable\n * (`EXT_color_buffer_float`). Mirrors Babylon's `caps.textureFloatRender`. */\n readonly textureFloatRender: boolean;\n /** True when 32-bit float textures support linear filtering\n * (`OES_texture_float_linear`). Mirrors `caps.textureFloatLinearFiltering`. */\n readonly textureFloatLinearFiltering: boolean;\n /** True when 16-bit half-float color attachments are renderable\n * (`EXT_color_buffer_float` or `EXT_color_buffer_half_float`). Mirrors\n * `caps.textureHalfFloatRender`. */\n readonly textureHalfFloatRender: boolean;\n /** Half-float linear filtering — always `true` in WebGL2 (it is core).\n * Kept as a field to mirror Babylon's `caps.textureHalfFloatLinearFiltering`. */\n readonly textureHalfFloatLinearFiltering: boolean;\n /** Whether non-power-of-two textures need POT dimensions for mips / wrap.\n * Always `false` in WebGL2 (NPOT is core). Mirrors `engine.needPOTTextures`. */\n readonly needPOTTextures: boolean;\n}\n\n/**\n * Pure-state handle for a WebGL2 canvas + its cached GL state.\n *\n * INVARIANT: consumers MUST NOT mutate GL state directly through `engine.gl`.\n * Doing so silently corrupts the cache in `_state`. The package owns every\n * GL call. (`engine.gl` is exposed only so downstream code that already has the\n * pattern of poking `engine._gl.getExtension(...)` can do that, but must NOT\n * call `bindTexture`/`useProgram`/`bindBuffer`/`viewport`/etc.)\n */\nexport interface GLEngineContext {\n /** The canvas the WebGL2 context was acquired from. An `OffscreenCanvas` is\n * supported for worker render paths (e.g. the Lottie player); it has no CSS\n * box, so it must be sized explicitly via `setGLEngineSize` rather than the\n * CSS-derived `resizeGLEngine`. */\n readonly canvas: HTMLCanvasElement | OffscreenCanvas;\n /** The raw WebGL2 context. Do NOT mutate GL state through it — see the\n * type-level invariant above; the package owns every state-changing call. */\n readonly gl: WebGL2RenderingContext;\n /** Queried capability limits for this context. */\n readonly caps: GLEngineCaps;\n /**\n * Hardware-scaling-level — drawingBufferWidth = clientWidth * dpr / _hsl.\n * @internal\n */\n _hsl: number;\n /**\n * rAF id when a render loop is active, 0 otherwise.\n * @internal\n */\n _rafId: number;\n /**\n * Per-frame callbacks. `runRenderLoop` is a no-op if `fn` is already\n * registered (matches Babylon `AbstractEngine.runRenderLoop`).\n * @internal\n */\n _loops: ((dt: number) => void)[];\n /**\n * Timestamp of last frame for delta computation.\n * @internal\n */\n _prevNow: number;\n /**\n * Cached GL state. See §4 of 00-lite-gl.md.\n * @internal\n */\n _state: GLState;\n /**\n * Live effect registry — populated by `createEffect`, removed by\n * `disposeEffect`. Used by the context-restored protocol to rebuild\n * programs.\n * @internal\n */\n _effects: GLEffect[];\n /**\n * Per-engine effect cache keyed by source descriptor (vertex+fragment\n * source, defines, attribute/uniform/sampler names). `createEffect` returns\n * (and ref-counts) the cached `GLEffect` when an identical descriptor is\n * requested, so identical shaders share ONE WebGLProgram — letting\n * `useEffect`'s current-program cache elide redundant `gl.useProgram`.\n * @internal\n */\n _effectCache: Map<string, GLEffect>;\n /**\n * Live texture registry — populated by `createRawTexture` /\n * `loadTexture2D` / `createHtmlElementTexture`. Used by the\n * context-restored protocol to replay uploads.\n * @internal\n */\n _textures: GLTexture[];\n /**\n * Live render-target registry — populated by `createRenderTarget`. Used by\n * the context-restored protocol to rebuild framebuffers + attachments after\n * their color textures have been replayed. Empty (and tree-shaken) when the\n * render-target module is unused.\n * @internal\n */\n _renderTargets: GLRenderTarget[];\n /**\n * The render target currently bound for drawing (`bindRenderTarget`), or\n * `null` for the default canvas framebuffer. Tracked so that leaving a\n * mipmapped target (`bindRenderTarget(engine, null)` or switching to\n * another target) can regenerate its mip chain — mirroring Babylon's\n * auto-mipmap on `unBindFramebuffer`. Reset to `null` on context-lost.\n * @internal\n */\n _currentRenderTarget: GLRenderTarget | null;\n /**\n * Live vertex / index buffer registry — populated by `createVertexBuffer` /\n * `createIndexBuffer`. Used by the context-restored protocol to re-upload\n * the retained CPU data into fresh GL buffers. Empty (and tree-shaken) when\n * the mesh module is unused.\n * @internal\n */\n _buffers: GLManagedBuffer[];\n /** @internal */\n _onLost: (() => void)[];\n /** @internal */\n _onRestored: (() => void)[];\n /**\n * True between `webglcontextlost` and `webglcontextrestored`. While\n * true, every `setEffect*` / `bindTexture` / `drawEffect` is a no-op.\n * @internal\n */\n _isLost: boolean;\n /**\n * True once the context has been disposed; subsequent calls become no-ops.\n * @internal\n */\n _disposed: boolean;\n /**\n * DOM handlers — retained so dispose can `removeEventListener` them.\n * @internal\n */\n _lostHandler: (e: Event) => void;\n /** @internal */\n _restoredHandler: () => void;\n /**\n * True when a render loop was active at the moment of `webglcontextlost`,\n * so we can resume it from the restored handler.\n * @internal\n */\n _wasLoopActive: boolean;\n /**\n * Installed by `runRenderLoop` on first call. Lets the context-restored\n * handler resume a loop without a circular runtime import from render-loop.\n * Null if the render-loop module is tree-shaken out.\n * @internal\n */\n _scheduleFrame: ((engine: GLEngineContext) => void) | null;\n}\n\n/** Acquire a WebGL2 context on the canvas and build the pure-state handle.\n * Accepts an `HTMLCanvasElement` or an `OffscreenCanvas` (worker render paths —\n * e.g. the Lottie player — which size the drawing buffer via `setGLEngineSize`).\n * Throws if WebGL2 is unsupported. */\nexport function createGLEngine(canvas: HTMLCanvasElement | OffscreenCanvas, options?: GLEngineOptions): GLEngineContext {\n const o = options ?? {};\n const attrs: WebGLContextAttributes = {\n alpha: o.alpha ?? true,\n premultipliedAlpha: o.premultipliedAlpha ?? true,\n antialias: o.antialias ?? false,\n preserveDrawingBuffer: o.preserveDrawingBuffer ?? false,\n depth: o.depth ?? false,\n stencil: o.stencil ?? false,\n powerPreference: o.powerPreference ?? \"default\",\n failIfMajorPerformanceCaveat: o.failIfMajorPerformanceCaveat ?? false,\n };\n const gl = canvas.getContext(\"webgl2\", attrs) as WebGL2RenderingContext | null;\n if (gl === null) {\n throw new Error(\"lite-gl: WebGL2 is not supported on this canvas\");\n }\n\n const parallelExt = gl.getExtension(\"KHR_parallel_shader_compile\") as { COMPLETION_STATUS_KHR: number } | null;\n // Probe color-buffer-float support. `getExtension` both queries AND enables\n // the extension, so this call is what makes float/half-float attachments\n // renderable for the render-target module.\n const colorBufferFloat = gl.getExtension(\"EXT_color_buffer_float\") !== null;\n const colorBufferHalfFloat = gl.getExtension(\"EXT_color_buffer_half_float\") !== null;\n const floatLinear = gl.getExtension(\"OES_texture_float_linear\") !== null;\n const caps: GLEngineCaps = {\n maxTextureSize: gl.getParameter(gl.MAX_TEXTURE_SIZE) as number,\n maxTextureUnits: gl.getParameter(gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS) as number,\n parallelShaderCompile: parallelExt,\n textureFloatRender: colorBufferFloat,\n textureFloatLinearFiltering: floatLinear,\n // In WebGL2, EXT_color_buffer_float also makes RGBA16F renderable; the\n // half-float-only extension is the fallback for drivers exposing just it.\n textureHalfFloatRender: colorBufferFloat || colorBufferHalfFloat,\n // Half-float linear filtering is core in WebGL2 (no extension needed).\n textureHalfFloatLinearFiltering: true,\n // NPOT textures are core in WebGL2 — mips/wrap work at any size.\n needPOTTextures: false,\n };\n\n const engine: GLEngineContext = {\n canvas,\n gl,\n caps,\n _hsl: 1,\n _rafId: 0,\n _loops: [],\n _prevNow: 0,\n _state: createGLState(caps.maxTextureUnits),\n _effects: [],\n _effectCache: new Map(),\n _textures: [],\n _renderTargets: [],\n _currentRenderTarget: null,\n _buffers: [],\n _onLost: [],\n _onRestored: [],\n _isLost: false,\n _disposed: false,\n _lostHandler: () => {},\n _restoredHandler: () => {},\n _wasLoopActive: false,\n _scheduleFrame: null,\n };\n\n engine._lostHandler = (e: Event) => handleContextLost(engine, e);\n engine._restoredHandler = () => handleContextRestored(engine);\n canvas.addEventListener(\"webglcontextlost\", engine._lostHandler as EventListener, false);\n canvas.addEventListener(\"webglcontextrestored\", engine._restoredHandler, false);\n\n return engine;\n}\n\n/** Stops the render loop, removes DOM listeners, releases all known effects\n * and textures, then marks the context disposed. The browser-owned canvas\n * is left intact. */\nexport function disposeGLEngine(engine: GLEngineContext): void {\n if (engine._disposed) {\n return;\n }\n engine._disposed = true;\n if (engine._rafId !== 0) {\n cancelAnimationFrame(engine._rafId);\n engine._rafId = 0;\n }\n engine._loops.length = 0;\n engine.canvas.removeEventListener(\"webglcontextlost\", engine._lostHandler as EventListener, false);\n engine.canvas.removeEventListener(\"webglcontextrestored\", engine._restoredHandler, false);\n\n const gl = engine.gl;\n // Iterate snapshots — the dispose paths splice into the registries.\n const effects = engine._effects.slice();\n for (const eff of effects) {\n if (!eff._disposed) {\n eff._disposed = true;\n eff.isReady = false;\n gl.deleteProgram(eff.program);\n gl.deleteShader(eff._vs);\n gl.deleteShader(eff._fs);\n }\n }\n engine._effects.length = 0;\n engine._effectCache.clear();\n const textures = engine._textures.slice();\n for (const tex of textures) {\n if (!tex._disposed) {\n tex._disposed = true;\n gl.deleteTexture(tex.handle);\n }\n }\n engine._textures.length = 0;\n // Render targets own their FBO + renderbuffer + color texture; free them via\n // the per-RT closure so context.ts needs no runtime import of render-target.\n const rts = engine._renderTargets.slice();\n for (const rt of rts) {\n if (!rt._disposed) {\n rt._disposed = true;\n rt._deleteGpu(gl);\n }\n }\n engine._renderTargets.length = 0;\n\n // Mesh vertex/index buffers own a single GL buffer each; free via closure.\n const buffers = engine._buffers.slice();\n for (const buf of buffers) {\n if (!buf._disposed) {\n buf._disposed = true;\n buf._deleteGpu(gl);\n }\n }\n engine._buffers.length = 0;\n\n resetGLState(engine._state);\n engine._currentRenderTarget = null;\n engine._onLost.length = 0;\n engine._onRestored.length = 0;\n}\n\n/** Match drawing-buffer size to (clientSize × devicePixelRatio / _hsl). No-op\n * if size already matches. Never touches viewport — `setViewport` owns that. */\nexport function resizeGLEngine(engine: GLEngineContext): void {\n if (engine._disposed || engine._isLost) {\n return;\n }\n const canvas = engine.canvas;\n // An OffscreenCanvas has no CSS box (no clientWidth/clientHeight); it is sized\n // explicitly via setGLEngineSize, so CSS-derived auto-resize is a no-op for it.\n if (!(\"clientWidth\" in canvas)) {\n return;\n }\n const dpr = typeof devicePixelRatio === \"number\" ? devicePixelRatio : 1;\n const w = Math.max(1, Math.floor((canvas.clientWidth * dpr) / engine._hsl));\n const h = Math.max(1, Math.floor((canvas.clientHeight * dpr) / engine._hsl));\n if (canvas.width !== w) {\n canvas.width = w;\n }\n if (canvas.height !== h) {\n canvas.height = h;\n }\n}\n\n/**\n * Set the drawing-buffer size to an EXPLICIT width/height in physical pixels\n * (each divided by `_hsl`), independent of the canvas client/CSS size — the\n * counterpart of Babylon's `ThinEngine.setSize`. Use when the render resolution\n * is computed directly (e.g. animation dimensions × scale × dpr) rather than\n * derived from CSS layout via {@link resizeGLEngine}. No-op on a disposed\n * engine. Never touches the viewport — `setViewport` owns that.\n *\n * @param engine - The engine to resize.\n * @param width - Target drawing-buffer width in physical pixels.\n * @param height - Target drawing-buffer height in physical pixels.\n */\nexport function setGLEngineSize(engine: GLEngineContext, width: number, height: number): void {\n if (engine._disposed) {\n return;\n }\n const canvas = engine.canvas;\n const w = Math.max(1, Math.floor(width / engine._hsl));\n const h = Math.max(1, Math.floor(height / engine._hsl));\n if (canvas.width !== w) {\n canvas.width = w;\n }\n if (canvas.height !== h) {\n canvas.height = h;\n }\n}\n\n/**\n * Invalidate lite-gl's cached GL state so the next state-setting call in each\n * category (program / buffer / texture / VAO / framebuffer bindings, blend,\n * depth, stencil, scissor, color-mask, viewport, unpack) is re-issued instead\n * of elided.\n *\n * Use this when a host application that SHARES this WebGL2 context mutates raw\n * `gl.*` state outside lite-gl — e.g. a save/restore wrapper that resets the\n * context to GL defaults around each render scope, or any interop layer that\n * issues GL calls directly. Without it, lite-gl's redundant-call elision would\n * skip the state changes needed to re-establish the scope, producing corrupted\n * output. Owned GPU resources (the shared quad, render targets, meshes,\n * effects, textures) are preserved — only the cached \"current GL state\" is\n * reset. Mirrors Babylon's `Engine.wipeCaches()`.\n *\n * No-op while the context is lost or the engine is disposed.\n */\nexport function wipeGLStateCache(engine: GLEngineContext): void {\n if (engine._disposed || engine._isLost) {\n return;\n }\n resetGLStateCache(engine._state);\n}\n\n/** Drawing-buffer width in physical pixels (`canvas.width`). */\nexport function getRenderWidth(engine: GLEngineContext): number {\n return engine.canvas.width;\n}\n\n/** Drawing-buffer height in physical pixels (`canvas.height`). */\nexport function getRenderHeight(engine: GLEngineContext): number {\n return engine.canvas.height;\n}\n\n/** Current hardware-scaling factor — drawing-buffer = clientSize × dpr / level. */\nexport function getHardwareScalingLevel(engine: GLEngineContext): number {\n return engine._hsl;\n}\n\n/** Updates the hardware-scaling factor and triggers a resize. */\nexport function setHardwareScalingLevel(engine: GLEngineContext, level: number): void {\n if (level <= 0 || !isFinite(level)) {\n return;\n }\n engine._hsl = level;\n resizeGLEngine(engine);\n}\n\n/** The backing canvas element (an `HTMLCanvasElement`, or an `OffscreenCanvas`\n * for worker render paths). */\nexport function getRenderingCanvas(engine: GLEngineContext): HTMLCanvasElement | OffscreenCanvas {\n return engine.canvas;\n}\n\n/** Register a `webglcontextlost` callback. Duplicate registrations are ignored. */\nexport function onContextLost(engine: GLEngineContext, cb: () => void): void {\n if (engine._onLost.indexOf(cb) === -1) {\n engine._onLost.push(cb);\n }\n}\n\n/** Remove a previously-registered context-lost callback. */\nexport function offContextLost(engine: GLEngineContext, cb: () => void): void {\n const i = engine._onLost.indexOf(cb);\n if (i !== -1) {\n engine._onLost.splice(i, 1);\n }\n}\n\n/** Register a `webglcontextrestored` callback. Duplicate registrations are ignored. */\nexport function onContextRestored(engine: GLEngineContext, cb: () => void): void {\n if (engine._onRestored.indexOf(cb) === -1) {\n engine._onRestored.push(cb);\n }\n}\n\n/** Remove a previously-registered context-restored callback. */\nexport function offContextRestored(engine: GLEngineContext, cb: () => void): void {\n const i = engine._onRestored.indexOf(cb);\n if (i !== -1) {\n engine._onRestored.splice(i, 1);\n }\n}\n\n/* ───────────────────────── internal: loss / restore ───────────────────── */\n\nfunction handleContextLost(engine: GLEngineContext, e: Event): void {\n // Opt-in to restore. Without preventDefault() the browser will NOT fire\n // webglcontextrestored later.\n e.preventDefault();\n if (engine._isLost || engine._disposed) {\n return;\n }\n engine._isLost = true;\n engine._wasLoopActive = engine._rafId !== 0;\n if (engine._rafId !== 0) {\n cancelAnimationFrame(engine._rafId);\n engine._rafId = 0;\n }\n resetGLState(engine._state);\n engine._currentRenderTarget = null;\n for (const eff of engine._effects) {\n eff.isReady = false;\n eff._samplersAssigned = false;\n eff.uniformLocations = {};\n eff.attributeLocations = {};\n // Clear value caches so the first frame after restore re-uploads\n // every uniform into the freshly-linked program.\n clearObject(eff._lastF1);\n clearObject(eff._lastVec);\n clearObject(eff._lastI1);\n // Do NOT gl.deleteProgram — handle is already dead per WebGL spec.\n }\n for (const tex of engine._textures) {\n tex._wasReady = tex.isReady;\n tex.isReady = false;\n }\n const cbs = engine._onLost.slice();\n for (const cb of cbs) {\n try {\n cb();\n } catch (err) {\n console.error(\"lite-gl: onLost callback threw\", err);\n }\n }\n}\n\nfunction handleContextRestored(engine: GLEngineContext): void {\n if (engine._disposed) {\n return;\n }\n for (const eff of engine._effects) {\n if (!eff._disposed) {\n eff._restore(engine);\n }\n }\n for (const tex of engine._textures) {\n if (!tex._disposed) {\n const newHandle = engine.gl.createTexture();\n if (newHandle !== null) {\n tex.handle = newHandle;\n tex._upload(engine);\n tex.isReady = tex._wasReady;\n }\n }\n }\n // Clear the lost flag BEFORE the render-target / buffer rebuilds: their\n // `_restore` closures route through the same guarded build/upload paths used\n // at create time (which no-op while `_isLost`), whereas the effect / texture\n // replay above is unguarded and unaffected by the flag's value.\n engine._isLost = false;\n // Render targets rebuild their own color texture + FBO + renderbuffer AFTER\n // the standalone texture replay above (an RT's color texture is owned by the\n // RT, not the `_textures` registry).\n for (const rt of engine._renderTargets) {\n if (!rt._disposed) {\n rt._restore(engine);\n }\n }\n // Mesh buffers re-upload their retained CPU data into fresh GL buffers.\n for (const buf of engine._buffers) {\n if (!buf._disposed) {\n buf._restore(engine);\n }\n }\n if (engine._wasLoopActive && engine._loops.length > 0 && engine._scheduleFrame !== null) {\n engine._scheduleFrame(engine);\n }\n const cbs = engine._onRestored.slice();\n for (const cb of cbs) {\n try {\n cb();\n } catch (err) {\n console.error(\"lite-gl: onRestored callback threw\", err);\n }\n }\n}\n\nfunction clearObject(o: { [k: string]: unknown }): void {\n for (const key in o) {\n delete o[key];\n }\n}\n","/**\n * Shader / program compilation helpers. Pure functions — they take a raw\n * `WebGL2RenderingContext` and never touch the cache layer. Used by\n * `effect.ts` during `createEffect` and during the context-restored\n * re-compile path.\n */\n\n/** Compile a single shader stage. Returns the shader handle; sets the\n * `errorOut` array's element 0 to a non-null string on failure. */\nexport function compileShader(gl: WebGL2RenderingContext, source: string, stage: GLenum, errorOut: (string | null)[]): WebGLShader | null {\n const shader = gl.createShader(stage);\n if (shader === null) {\n errorOut[0] = \"gl.createShader returned null\";\n return null;\n }\n gl.shaderSource(shader, source);\n gl.compileShader(shader);\n if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {\n errorOut[0] = gl.getShaderInfoLog(shader) ?? \"shader compile failed\";\n gl.deleteShader(shader);\n return null;\n }\n return shader;\n}\n\n/** Attach + bind + link. Returns the program handle. Does NOT block on\n * link completion — callers use `pollLinkStatus` to drive the parallel-\n * shader-compile state machine. */\nexport function linkProgram(gl: WebGL2RenderingContext, vs: WebGLShader, fs: WebGLShader, attributeNames: readonly string[]): WebGLProgram | null {\n const program = gl.createProgram();\n if (program === null) {\n return null;\n }\n gl.attachShader(program, vs);\n gl.attachShader(program, fs);\n // Bind every declared attribute, but in particular guarantee that the FIRST\n // attribute (`position`) maps to location 0 — the shared fullscreen-quad VAO\n // depends on this so the same VAO works across every effect's program.\n // Must run BEFORE linkProgram. The GLSL conversion also emits\n // `layout(location = N)` as belt-and-suspenders.\n for (let i = 0; i < attributeNames.length; i++) {\n const name = attributeNames[i];\n if (name !== undefined) {\n gl.bindAttribLocation(program, i, name);\n }\n }\n gl.linkProgram(program);\n return program;\n}\n\n/** Returns `true` when the program has finished linking and can be queried.\n * When the `KHR_parallel_shader_compile` extension is present, this is the\n * cheap async-friendly poll; without it, link is synchronous so the answer\n * is always `true`. */\nexport function isLinkComplete(gl: WebGL2RenderingContext, program: WebGLProgram, parallel: { COMPLETION_STATUS_KHR: number } | null): boolean {\n if (parallel === null) {\n return true;\n }\n return Boolean(gl.getProgramParameter(program, parallel.COMPLETION_STATUS_KHR));\n}\n\n/** Returns null on success, the info log on failure. */\nexport function getLinkError(gl: WebGL2RenderingContext, program: WebGLProgram): string | null {\n if (gl.getProgramParameter(program, gl.LINK_STATUS)) {\n return null;\n }\n return gl.getProgramInfoLog(program) ?? \"link failed\";\n}\n","import type { GLEngineContext } from \"./context.js\";\nimport { compileShader, getLinkError, isLinkComplete, linkProgram } from \"./shader.js\";\nimport { bindTexture, type GLTexture } from \"./texture.js\";\n\n/** Inputs to `createEffect`: shader sources plus the uniform, sampler and\n * attribute names whose locations are resolved during readiness finalization. */\nexport interface GLEffectOptions {\n /** Human-readable label, surfaced in compile/link error messages. */\n name: string;\n /** GLSL ES 3.00 source, ready for `gl.shaderSource`. */\n vertexSource: string;\n /** GLSL ES 3.00 source, ready for `gl.shaderSource`. */\n fragmentSource: string;\n /** Declared uniform names. Locations are resolved during readiness\n * finalization. Names not declared here are legal but allocate cache\n * slots lazily on first setter use. */\n uniformNames: readonly string[];\n /** Declared sampler names, in unit-assignment order. Each gets a fixed\n * texture unit assigned during readiness finalization, and\n * `gl.uniform1i(loc, unit)` is called exactly once per program lifetime\n * (re-run after `webglcontextrestored`). */\n samplerNames: readonly string[];\n /** Default `[\"position\"]`. The first attribute is bound to location 0 via\n * `gl.bindAttribLocation(program, 0, name)` BEFORE link, so the shared\n * fullscreen-quad VAO always feeds the same location. */\n attributeNames?: readonly string[];\n /** Optional `#define` block. Each unique `defines` string must be paired\n * with the same vertex/fragment source via a separate `createEffect` call —\n * the package does NOT cache compiled variants. */\n defines?: string;\n}\n\n/** A compiled + linked shader program with cached uniform, sampler and\n * attribute locations. Created by `createEffect`; most fields are managed\n * internally — drive it via `isEffectReady` / `useEffect` / the `setEffect*`\n * setters rather than mutating it directly. */\nexport interface GLEffect {\n /** The `name` from the originating `GLEffectOptions`. */\n readonly name: string;\n /** The options this effect was created from (retained for context-restore). */\n readonly options: GLEffectOptions;\n /** The live `WebGLProgram`. Swapped for a fresh handle after context-restore. */\n program: WebGLProgram;\n /** @internal */\n _vs: WebGLShader;\n /** @internal */\n _fs: WebGLShader;\n /** Resolved during readiness finalization. Missing names map to `null` —\n * setters with a `null` location are silent no-ops (matches Babylon). */\n uniformLocations: { [name: string]: WebGLUniformLocation | null };\n /** Fixed unit assignment for declared samplers, index into\n * `_state.boundTextures`. */\n samplerUnits: { [name: string]: number };\n /**\n * True once `gl.uniform1i(samplerLoc, unit)` has been issued for every\n * declared sampler. Cleared on context-lost so finalization re-runs.\n * @internal\n */\n _samplersAssigned: boolean;\n /** Resolved attribute locations, keyed by attribute name. */\n attributeLocations: { [name: string]: number };\n /**\n * Last-UPLOADED scalar floats. A setter that skips the upload must NOT\n * update this — otherwise a later real set with the same value would\n * incorrectly elide and the GPU would keep stale data.\n * @internal\n */\n readonly _lastF1: { [name: string]: number };\n /**\n * Last-UPLOADED vector floats (vec2/vec3/vec4). Plain `number[]` (NOT\n * `Float32Array`) so values like `0.1` compare equal across frames.\n * @internal\n */\n readonly _lastVec: { [name: string]: number[] };\n /** @internal */\n readonly _lastI1: { [name: string]: number };\n /** True once the program has linked and finalization has run; the\n * `setEffect*` setters are no-ops until then. Poll `isEffectReady` to advance it. */\n isReady: boolean;\n /** @internal */\n _compileError: string | null;\n /** @internal */\n _disposed: boolean;\n /**\n * Number of live `createEffect` callers sharing this effect (and its one\n * `WebGLProgram`). `createEffect` returns the cached effect and bumps this\n * when an identical descriptor is requested; `disposeEffect` decrements it\n * and only tears the program down when it reaches 0.\n * @internal\n */\n _refCount: number;\n /**\n * The `_effectCache` key this effect was stored under (the source\n * descriptor). Used by `disposeEffect` to evict the cache entry at refcount\n * 0.\n * @internal\n */\n _cacheKey: string;\n /**\n * Callbacks fired exactly once on the first transition to ready.\n * @internal\n */\n readonly _onCompiled: ((effect: GLEffect) => void)[];\n /**\n * Replay closure for context-restore. Re-compiles + relinks into a fresh\n * `program` field. Finalization happens lazily on the next `isEffectReady`\n * poll.\n * @internal\n */\n _restore: (engine: GLEngineContext) => void;\n}\n\n/** Compile + link a new effect. Does NOT block on link completion — `isReady`\n * starts false; consumers poll `isEffectReady` (typically from their render\n * callback) to drive finalization. */\nexport function createEffect(engine: GLEngineContext, options: GLEffectOptions): GLEffect {\n const attribs = options.attributeNames ?? [\"position\"];\n const gl = engine.gl;\n\n // Per-engine cache keyed by the source descriptor. Identical descriptors\n // share ONE GLEffect (and thus one WebGLProgram), so `useEffect`'s\n // current-program cache can elide redundant `gl.useProgram` across the\n // consumers. The key covers every input that affects the program or its\n // readiness finalization, joined by a NUL separator (cannot appear in GLSL).\n const cacheKey = [options.vertexSource, options.fragmentSource, options.defines ?? \"\", attribs.join(\",\"), options.uniformNames.join(\",\"), options.samplerNames.join(\",\")].join(\n \"\\u0000\"\n );\n const cached = engine._effectCache.get(cacheKey);\n if (cached !== undefined && !cached._disposed) {\n cached._refCount++;\n return cached;\n }\n\n const compileErr: (string | null)[] = [null];\n const finalVS = applyDefines(options.vertexSource, options.defines);\n const finalFS = applyDefines(options.fragmentSource, options.defines);\n\n const vs = compileShader(gl, finalVS, gl.VERTEX_SHADER, compileErr);\n if (vs === null) {\n throw new Error(`lite-gl: ${options.name} vertex compile failed: ${compileErr[0] ?? \"unknown\"}`);\n }\n const fs = compileShader(gl, finalFS, gl.FRAGMENT_SHADER, compileErr);\n if (fs === null) {\n gl.deleteShader(vs);\n throw new Error(`lite-gl: ${options.name} fragment compile failed: ${compileErr[0] ?? \"unknown\"}`);\n }\n const program = linkProgram(gl, vs, fs, attribs);\n if (program === null) {\n gl.deleteShader(vs);\n gl.deleteShader(fs);\n throw new Error(`lite-gl: ${options.name} program allocation failed`);\n }\n\n const effect: GLEffect = {\n name: options.name,\n options,\n program,\n _vs: vs,\n _fs: fs,\n uniformLocations: {},\n samplerUnits: {},\n _samplersAssigned: false,\n attributeLocations: {},\n _lastF1: {},\n _lastVec: {},\n _lastI1: {},\n isReady: false,\n _compileError: null,\n _disposed: false,\n _refCount: 1,\n _cacheKey: cacheKey,\n _onCompiled: [],\n _restore: () => {},\n };\n\n effect._restore = (target: GLEngineContext): void => {\n const g = target.gl;\n const newVS = compileShader(g, applyDefines(options.vertexSource, options.defines), g.VERTEX_SHADER, [null]);\n const newFS = compileShader(g, applyDefines(options.fragmentSource, options.defines), g.FRAGMENT_SHADER, [null]);\n if (newVS === null || newFS === null) {\n effect._compileError = \"context-restore: shader compile failed\";\n return;\n }\n const newProg = linkProgram(g, newVS, newFS, attribs);\n if (newProg === null) {\n g.deleteShader(newVS);\n g.deleteShader(newFS);\n effect._compileError = \"context-restore: program allocation failed\";\n return;\n }\n effect.program = newProg;\n effect._vs = newVS;\n effect._fs = newFS;\n effect.isReady = false;\n effect._samplersAssigned = false;\n effect.uniformLocations = {};\n effect.attributeLocations = {};\n effect._compileError = null;\n };\n\n engine._effects.push(effect);\n engine._effectCache.set(cacheKey, effect);\n return effect;\n}\n\n/** Poll the link state and, on first success, run finalization (uniform-\n * location resolution + one-shot sampler-unit `uniform1i` assignment +\n * `_onCompiled` callbacks). Returns `true` once the effect is usable. */\nexport function isEffectReady(engine: GLEngineContext, effect: GLEffect): boolean {\n if (effect.isReady) {\n return true;\n }\n if (effect._disposed || engine._isLost || engine._disposed) {\n return false;\n }\n if (effect._compileError !== null) {\n return false;\n }\n if (!isLinkComplete(engine.gl, effect.program, engine.caps.parallelShaderCompile)) {\n return false;\n }\n const linkErr = getLinkError(engine.gl, effect.program);\n if (linkErr !== null) {\n effect._compileError = linkErr;\n console.error(`lite-gl: ${effect.name} link failed:`, linkErr);\n return false;\n }\n finalizeEffect(engine, effect);\n return true;\n}\n\n/** Resolves uniform/attribute locations, binds the program (cached), and\n * issues the one-time `gl.uniform1i(samplerLoc, unit)` per declared sampler. */\nfunction finalizeEffect(engine: GLEngineContext, effect: GLEffect): void {\n const gl = engine.gl;\n const program = effect.program;\n for (const name of effect.options.uniformNames) {\n effect.uniformLocations[name] = gl.getUniformLocation(program, name);\n }\n const attribs = effect.options.attributeNames ?? [\"position\"];\n for (const name of attribs) {\n effect.attributeLocations[name] = gl.getAttribLocation(program, name);\n }\n // Switch to this program via the cached helper so _state.currentProgram\n // stays consistent — no raw gl.useProgram outside useEffect.\n useEffect(engine, effect);\n let unit = 0;\n for (const name of effect.options.samplerNames) {\n const loc = gl.getUniformLocation(program, name);\n if (loc !== null) {\n gl.uniform1i(loc, unit);\n }\n effect.samplerUnits[name] = unit;\n unit++;\n }\n effect._samplersAssigned = true;\n effect.isReady = true;\n // Fire and clear the one-shot ready callbacks.\n const cbs = effect._onCompiled.slice();\n effect._onCompiled.length = 0;\n for (const cb of cbs) {\n try {\n cb(effect);\n } catch (err) {\n console.error(`lite-gl: ${effect.name} onCompiled callback threw`, err);\n }\n }\n}\n\n/** Fires `cb` synchronously if the effect is already ready; otherwise queues\n * it for the next finalization. */\nexport function executeWhenCompiled(engine: GLEngineContext, effect: GLEffect, cb: (e: GLEffect) => void): void {\n if (isEffectReady(engine, effect)) {\n cb(effect);\n return;\n }\n effect._onCompiled.push(cb);\n}\n\n/** Decrement the share count and, on the last release, delete the effect's\n * program + shaders, unregister it from the context + cache, and clear the\n * cached current-program if it pointed at this effect. A shared effect (still\n * referenced by another `createEffect` caller) is kept alive. Call exactly ONCE\n * per `createEffect` (each call decrements the ref count); safe (no-op) once the\n * effect is fully torn down. */\nexport function disposeEffect(engine: GLEngineContext, effect: GLEffect): void {\n if (effect._disposed) {\n return;\n }\n if (--effect._refCount > 0) {\n return;\n }\n effect._disposed = true;\n effect.isReady = false;\n const i = engine._effects.indexOf(effect);\n if (i !== -1) {\n engine._effects.splice(i, 1);\n }\n engine._effectCache.delete(effect._cacheKey);\n if (!engine._isLost && !engine._disposed) {\n engine.gl.deleteProgram(effect.program);\n engine.gl.deleteShader(effect._vs);\n engine.gl.deleteShader(effect._fs);\n }\n if (engine._state.currentProgram === effect.program) {\n engine._state.currentProgram = null;\n }\n effect._onCompiled.length = 0;\n}\n\n/** Cached `gl.useProgram`. No-op when the effect is not ready or already current. */\nexport function useEffect(engine: GLEngineContext, effect: GLEffect): void {\n if (engine._isLost || engine._disposed || effect._disposed) {\n return;\n }\n if (engine._state.currentProgram === effect.program) {\n return;\n }\n engine.gl.useProgram(effect.program);\n engine._state.currentProgram = effect.program;\n}\n\n/* ──────────────────────────── cached setters ────────────────────────────\n *\n * Each setter has the shape:\n * 1. bail when context lost / effect not ready (no cache write)\n * 2. lookup uniform location; bail on null (no cache write)\n * 3. compare against last-UPLOADED value; bail on equality\n * 4. write to cache, issue gl.uniform*\n *\n * Step 3's comparison is intentionally bit-equal (===) — NaN inputs re-upload\n * every frame because `NaN !== NaN`, which is the correct safety net for\n * caller bugs.\n */\n\n/** Cached `gl.uniform1f`. No-op when context-lost, the effect isn't ready, the\n * uniform is absent, or the value is unchanged since last upload. */\nexport function setEffectFloat(engine: GLEngineContext, effect: GLEffect, name: string, x: number): void {\n if (engine._isLost || !effect.isReady) {\n return;\n }\n const loc = effect.uniformLocations[name];\n if (loc === null || loc === undefined) {\n return;\n }\n if (effect._lastF1[name] === x) {\n return;\n }\n effect._lastF1[name] = x;\n engine.gl.uniform1f(loc, x);\n}\n\n/** Cached `gl.uniform2f`. No-op when context-lost, the effect isn't ready, the\n * uniform is absent, or the value is unchanged since last upload. */\nexport function setEffectFloat2(engine: GLEngineContext, effect: GLEffect, name: string, x: number, y: number): void {\n if (engine._isLost || !effect.isReady) {\n return;\n }\n const loc = effect.uniformLocations[name];\n if (loc === null || loc === undefined) {\n return;\n }\n let v = effect._lastVec[name];\n if (v !== undefined && v[0] === x && v[1] === y) {\n return;\n }\n if (v === undefined) {\n v = [x, y];\n effect._lastVec[name] = v;\n } else {\n v[0] = x;\n v[1] = y;\n }\n engine.gl.uniform2f(loc, x, y);\n}\n\n/** Cached `gl.uniform3f`. No-op when context-lost, the effect isn't ready, the\n * uniform is absent, or the value is unchanged since last upload. */\nexport function setEffectFloat3(engine: GLEngineContext, effect: GLEffect, name: string, x: number, y: number, z: number): void {\n if (engine._isLost || !effect.isReady) {\n return;\n }\n const loc = effect.uniformLocations[name];\n if (loc === null || loc === undefined) {\n return;\n }\n let v = effect._lastVec[name];\n if (v !== undefined && v[0] === x && v[1] === y && v[2] === z) {\n return;\n }\n if (v === undefined) {\n v = [x, y, z];\n effect._lastVec[name] = v;\n } else {\n v[0] = x;\n v[1] = y;\n v[2] = z;\n }\n engine.gl.uniform3f(loc, x, y, z);\n}\n\n/** Cached `gl.uniform4f`. No-op when context-lost, the effect isn't ready, the\n * uniform is absent, or the value is unchanged since last upload. */\nexport function setEffectFloat4(engine: GLEngineContext, effect: GLEffect, name: string, x: number, y: number, z: number, w: number): void {\n if (engine._isLost || !effect.isReady) {\n return;\n }\n const loc = effect.uniformLocations[name];\n if (loc === null || loc === undefined) {\n return;\n }\n let v = effect._lastVec[name];\n if (v !== undefined && v[0] === x && v[1] === y && v[2] === z && v[3] === w) {\n return;\n }\n if (v === undefined) {\n v = [x, y, z, w];\n effect._lastVec[name] = v;\n } else {\n v[0] = x;\n v[1] = y;\n v[2] = z;\n v[3] = w;\n }\n engine.gl.uniform4f(loc, x, y, z, w);\n}\n\n/** Cached `gl.uniform3f` from an r/g/b color object. Delegates to `setEffectFloat3`. */\nexport function setEffectColor3(engine: GLEngineContext, effect: GLEffect, name: string, c: { r: number; g: number; b: number }): void {\n setEffectFloat3(engine, effect, name, c.r, c.g, c.b);\n}\n\n/** Cached `gl.uniform4f` from an r/g/b/a color object. Delegates to `setEffectFloat4`. */\nexport function setEffectColor4(engine: GLEngineContext, effect: GLEffect, name: string, c: { r: number; g: number; b: number; a: number }): void {\n setEffectFloat4(engine, effect, name, c.r, c.g, c.b, c.a);\n}\n\n/** Cached `gl.uniform2f` from an `{x,y}` vector object — the lite-gl equivalent\n * of Babylon's `Effect.setVector2`. Delegates to `setEffectFloat2`. */\nexport function setEffectVector2(engine: GLEngineContext, effect: GLEffect, name: string, v: { x: number; y: number }): void {\n setEffectFloat2(engine, effect, name, v.x, v.y);\n}\n\n/** Cached `gl.uniform4f` from an r/g/b/a color WITHOUT premultiplication — the\n * lite-gl equivalent of Babylon's `Effect.setDirectColor4`. Delegates to\n * `setEffectFloat4` (lite-gl never premultiplies in the uniform setters, so\n * this matches `setEffectColor4`; the distinct name eases the ShapeBuilder\n * port). */\nexport function setEffectDirectColor4(engine: GLEngineContext, effect: GLEffect, name: string, c: { r: number; g: number; b: number; a: number }): void {\n setEffectFloat4(engine, effect, name, c.r, c.g, c.b, c.a);\n}\n\n/** Cached `gl.uniform1i`. No-op when context-lost, the effect isn't ready, the\n * uniform is absent, or the value is unchanged since last upload. */\nexport function setEffectInt(engine: GLEngineContext, effect: GLEffect, name: string, x: number): void {\n if (engine._isLost || !effect.isReady) {\n return;\n }\n const loc = effect.uniformLocations[name];\n if (loc === null || loc === undefined) {\n return;\n }\n if (effect._lastI1[name] === x) {\n return;\n }\n effect._lastI1[name] = x;\n engine.gl.uniform1i(loc, x);\n}\n\n/* ── Matrix / array setters (uploaded every call — not value-cached) ──────────\n *\n * Matrices and arrays usually change every frame; element-wise caching would\n * cost more than the upload it saves, so these always issue the GL call (the\n * location lookup + ready guards still apply). They cover the ShapeBuilder\n * surface (`setMatrix3x3`, `setFloatArray`, `setFloatArray4`) plus a general\n * 4×4 matrix setter for any future mesh consumer. */\n\n/** `gl.uniformMatrix4fv` from a column-major 4×4 matrix — the lite-gl\n * equivalent of Babylon's `Effect.setMatrix`. Not value-cached. */\nexport function setEffectMatrix(engine: GLEngineContext, effect: GLEffect, name: string, matrix: Float32Array | number[]): void {\n if (engine._isLost || !effect.isReady) {\n return;\n }\n const loc = effect.uniformLocations[name];\n if (loc === null || loc === undefined) {\n return;\n }\n engine.gl.uniformMatrix4fv(loc, false, matrix);\n}\n\n/** `gl.uniformMatrix3fv` from a column-major 3×3 matrix — the lite-gl\n * equivalent of Babylon's `Effect.setMatrix3x3`. Not value-cached. */\nexport function setEffectMatrix3x3(engine: GLEngineContext, effect: GLEffect, name: string, matrix: Float32Array | number[]): void {\n if (engine._isLost || !effect.isReady) {\n return;\n }\n const loc = effect.uniformLocations[name];\n if (loc === null || loc === undefined) {\n return;\n }\n engine.gl.uniformMatrix3fv(loc, false, matrix);\n}\n\n/** `gl.uniform1fv` — a flat float array (`Effect.setFloatArray` / `setArray`).\n * Not value-cached. */\nexport function setEffectFloatArray(engine: GLEngineContext, effect: GLEffect, name: string, array: Float32Array | number[]): void {\n if (engine._isLost || !effect.isReady) {\n return;\n }\n const loc = effect.uniformLocations[name];\n if (loc === null || loc === undefined) {\n return;\n }\n engine.gl.uniform1fv(loc, array);\n}\n\n/** `gl.uniform4fv` — an array of `vec4`s (`Effect.setFloatArray4` / `setArray4`).\n * Not value-cached. */\nexport function setEffectFloatArray4(engine: GLEngineContext, effect: GLEffect, name: string, array: Float32Array | number[]): void {\n if (engine._isLost || !effect.isReady) {\n return;\n }\n const loc = effect.uniformLocations[name];\n if (loc === null || loc === undefined) {\n return;\n }\n engine.gl.uniform4fv(loc, array);\n}\n\n/** `gl.uniform1iv` — a flat int array (`Effect.setIntArray`). Not value-cached. */\nexport function setEffectIntArray(engine: GLEngineContext, effect: GLEffect, name: string, array: Int32Array | number[]): void {\n if (engine._isLost || !effect.isReady) {\n return;\n }\n const loc = effect.uniformLocations[name];\n if (loc === null || loc === undefined) {\n return;\n }\n engine.gl.uniform1iv(loc, array);\n}\n\n/** Bind a texture to the sampler's pre-assigned unit (§4.4). NO `gl.uniform1i`\n * is issued — that was done exactly once per program lifetime during\n * finalization. This is the key win over Babylon's `Effect.setTexture` which\n * re-issues the sampler binding on every call. */\nexport function setEffectTexture(engine: GLEngineContext, effect: GLEffect, samplerName: string, tex: GLTexture): void {\n if (engine._isLost || !effect.isReady) {\n return;\n }\n const unit = effect.samplerUnits[samplerName];\n if (unit === undefined) {\n return;\n }\n bindTexture(engine, unit, tex);\n}\n\n/** Resolve (and cache) a vertex-attribute location by name — the lite-gl\n * equivalent of Babylon's `Effect.getAttributeLocationByName`. Locations for\n * declared `attributeNames` are resolved during finalization; this also serves\n * the mesh / instancing path, where attribute names are not declared up-front.\n * Returns `-1` when the attribute is absent (matching `gl.getAttribLocation`).\n * @internal */\nexport function getEffectAttributeLocation(engine: GLEngineContext, effect: GLEffect, name: string): number {\n const cached = effect.attributeLocations[name];\n if (cached !== undefined) {\n return cached;\n }\n const loc = engine.gl.getAttribLocation(effect.program, name);\n effect.attributeLocations[name] = loc;\n return loc;\n}\n\n/* ──────────────────────────── internal helpers ──────────────────────────── */\n\n/** Inject `options.defines` between the `#version`/precision header and the\n * user shader body. Supports a `// __DEFINES__` marker (preferred — keeps the\n * runtime regex-free, per spec §6.2) OR auto-detects the end of the leading\n * preprocessor / precision lines. */\nfunction applyDefines(source: string, defines: string | undefined): string {\n if (defines === undefined || defines.length === 0) {\n return source;\n }\n const marker = \"// __DEFINES__\";\n const idx = source.indexOf(marker);\n if (idx !== -1) {\n return source.slice(0, idx) + defines + source.slice(idx + marker.length);\n }\n // Fallback: insert after the last `precision` line (or after `#version`).\n const lines = source.split(\"\\n\");\n let insertAt = 0;\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n if (line === undefined) {\n continue;\n }\n const trimmed = line.trim();\n if (trimmed.startsWith(\"#version\") || trimmed.startsWith(\"precision \")) {\n insertAt = i + 1;\n }\n }\n lines.splice(insertAt, 0, defines);\n return lines.join(\"\\n\");\n}\n"],"names":[],"mappings":"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"}
@@ -1,437 +0,0 @@
1
- import { c as createEffect, m as onContextRestored, k as offContextRestored, d as disposeEffect, j as isEffectReady, u as useEffect, D as setEffectTexture } from "./effect-BxxwfB_O.js";
2
- import { t as RS_BLEND_ENABLED, b as RS_DESIRED, u as RS_BLEND_EQ_RGB, v as RS_BLEND_EQ_A, w as RS_BLEND_SRC_RGB, x as RS_BLEND_DST_RGB, y as RS_BLEND_SRC_A, z as RS_BLEND_DST_A, a as applyGLStates } from "./state--j_ncWIi.js";
3
- const GLBlendMode = {
4
- /** No blending — `gl.disable(gl.BLEND)`. (`Constants.ALPHA_DISABLE`) */
5
- DISABLE: 0,
6
- /** Additive — `blendFuncSeparate(SRC_ALPHA, ONE, ZERO, ONE)`. (`Constants.ALPHA_ADD`) */
7
- ADD: 1,
8
- /** Standard (non-premultiplied) alpha — `blendFuncSeparate(SRC_ALPHA, ONE_MINUS_SRC_ALPHA, ONE, ONE)`. (`Constants.ALPHA_COMBINE`) */
9
- ALPHA: 2,
10
- /** Premultiplied alpha — `blendFuncSeparate(ONE, ONE_MINUS_SRC_ALPHA, ONE, ONE)`. (`Constants.ALPHA_PREMULTIPLIED`) */
11
- PREMULTIPLIED: 7
12
- };
13
- const GLBlendEquation = {
14
- /** `src + dst` (the GL default). */
15
- ADD: 32774,
16
- /** `src - dst`. */
17
- SUBTRACT: 32778,
18
- /** `dst - src`. */
19
- REVERSE_SUBTRACT: 32779,
20
- /** `min(src, dst)`. */
21
- MIN: 32775,
22
- /** `max(src, dst)`. */
23
- MAX: 32776
24
- };
25
- const FUNC_ADD = 32774;
26
- const BLEND = 3042;
27
- function setBlendMode(engine, mode) {
28
- if (engine._isLost || engine._disposed) {
29
- return;
30
- }
31
- const gl = engine.gl;
32
- switch (mode) {
33
- case GLBlendMode.DISABLE:
34
- disableBlend(engine);
35
- return;
36
- case GLBlendMode.ADD:
37
- applyBlend(engine, gl.SRC_ALPHA, gl.ONE, gl.ZERO, gl.ONE, FUNC_ADD, FUNC_ADD);
38
- return;
39
- case GLBlendMode.ALPHA:
40
- applyBlend(engine, gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE, FUNC_ADD, FUNC_ADD);
41
- return;
42
- case GLBlendMode.PREMULTIPLIED:
43
- applyBlend(engine, gl.ONE, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE, FUNC_ADD, FUNC_ADD);
44
- return;
45
- }
46
- }
47
- function setBlendState(engine, state) {
48
- if (engine._isLost || engine._disposed) {
49
- return;
50
- }
51
- applyBlend(engine, state.srcRGB, state.dstRGB, state.srcAlpha, state.dstAlpha, state.equationRGB ?? FUNC_ADD, state.equationAlpha ?? FUNC_ADD);
52
- }
53
- function disableBlend(engine) {
54
- if (engine._isLost || engine._disposed) {
55
- return;
56
- }
57
- const s = engine._state;
58
- s.rs[RS_BLEND_ENABLED + RS_DESIRED] = 0;
59
- s._flushBlend = flushBlend;
60
- s.statesDirty = true;
61
- }
62
- function applyBlend(engine, srcRGB, dstRGB, srcAlpha, dstAlpha, eqRGB, eqAlpha) {
63
- const rs = engine._state.rs;
64
- rs[RS_BLEND_ENABLED + RS_DESIRED] = 1;
65
- rs[RS_BLEND_SRC_RGB + RS_DESIRED] = srcRGB;
66
- rs[RS_BLEND_DST_RGB + RS_DESIRED] = dstRGB;
67
- rs[RS_BLEND_SRC_A + RS_DESIRED] = srcAlpha;
68
- rs[RS_BLEND_DST_A + RS_DESIRED] = dstAlpha;
69
- rs[RS_BLEND_EQ_RGB + RS_DESIRED] = eqRGB;
70
- rs[RS_BLEND_EQ_A + RS_DESIRED] = eqAlpha;
71
- engine._state._flushBlend = flushBlend;
72
- engine._state.statesDirty = true;
73
- }
74
- function flushBlend(engine) {
75
- const gl = engine.gl;
76
- const rs = engine._state.rs;
77
- const dBlend = rs[RS_BLEND_ENABLED + RS_DESIRED];
78
- if (dBlend === -1) {
79
- return;
80
- }
81
- if (dBlend !== rs[RS_BLEND_ENABLED]) {
82
- rs[RS_BLEND_ENABLED] = dBlend;
83
- if (dBlend === 1) {
84
- gl.enable(BLEND);
85
- } else {
86
- gl.disable(BLEND);
87
- }
88
- }
89
- if (dBlend !== 1) {
90
- return;
91
- }
92
- const eqRGB = rs[RS_BLEND_EQ_RGB + RS_DESIRED];
93
- const eqA = rs[RS_BLEND_EQ_A + RS_DESIRED];
94
- if (rs[RS_BLEND_EQ_RGB] !== eqRGB || rs[RS_BLEND_EQ_A] !== eqA) {
95
- gl.blendEquationSeparate(eqRGB, eqA);
96
- rs[RS_BLEND_EQ_RGB] = eqRGB;
97
- rs[RS_BLEND_EQ_A] = eqA;
98
- }
99
- const srcRGB = rs[RS_BLEND_SRC_RGB + RS_DESIRED];
100
- const dstRGB = rs[RS_BLEND_DST_RGB + RS_DESIRED];
101
- const srcA = rs[RS_BLEND_SRC_A + RS_DESIRED];
102
- const dstA = rs[RS_BLEND_DST_A + RS_DESIRED];
103
- if (rs[RS_BLEND_SRC_RGB] !== srcRGB || rs[RS_BLEND_DST_RGB] !== dstRGB || rs[RS_BLEND_SRC_A] !== srcA || rs[RS_BLEND_DST_A] !== dstA) {
104
- gl.blendFuncSeparate(srcRGB, dstRGB, srcA, dstA);
105
- rs[RS_BLEND_SRC_RGB] = srcRGB;
106
- rs[RS_BLEND_DST_RGB] = dstRGB;
107
- rs[RS_BLEND_SRC_A] = srcA;
108
- rs[RS_BLEND_DST_A] = dstA;
109
- }
110
- }
111
- const SPRITE_VERTEX_SOURCE = `#version 300 es
112
- precision highp float;
113
- layout(location = 0) in vec4 position;
114
- layout(location = 1) in vec2 options;
115
- layout(location = 2) in vec2 offsets;
116
- layout(location = 3) in vec2 inverts;
117
- layout(location = 4) in vec4 cellInfo;
118
- layout(location = 5) in vec4 color;
119
- uniform mat4 view;
120
- uniform mat4 projection;
121
- out vec2 vUV;
122
- out vec4 vColor;
123
- void main(void) {
124
- vec3 viewPos = (view * vec4(position.xyz, 1.0)).xyz;
125
- float angle = position.w;
126
- vec2 size = vec2(options.x, options.y);
127
- vec2 offset = offsets.xy;
128
- vec2 cornerPos = vec2(offset.x - 0.5, offset.y - 0.5) * size;
129
- vec3 rotatedCorner;
130
- rotatedCorner.x = cornerPos.x * cos(angle) - cornerPos.y * sin(angle);
131
- rotatedCorner.y = cornerPos.x * sin(angle) + cornerPos.y * cos(angle);
132
- rotatedCorner.z = 0.0;
133
- viewPos += rotatedCorner;
134
- gl_Position = projection * vec4(viewPos, 1.0);
135
- vColor = color;
136
- vec2 uvOffset = vec2(abs(offset.x - inverts.x), abs(1.0 - offset.y - inverts.y));
137
- vec2 uvPlace = cellInfo.xy;
138
- vec2 uvSize = cellInfo.zw;
139
- vUV.x = uvPlace.x + uvSize.x * uvOffset.x;
140
- vUV.y = uvPlace.y + uvSize.y * uvOffset.y;
141
- }`;
142
- const SPRITE_FRAGMENT_SOURCE = `#version 300 es
143
- precision highp float;
144
- in vec2 vUV;
145
- in vec4 vColor;
146
- uniform sampler2D diffuseSampler;
147
- out vec4 glFragColor;
148
- void main(void) {
149
- vec4 color = texture(diffuseSampler, vUV);
150
- color *= vColor;
151
- glFragColor = color;
152
- }`;
153
- const FLOATS_PER_VERTEX = 18;
154
- const VERTS_PER_SPRITE = 4;
155
- const INDICES_PER_SPRITE = 6;
156
- const BYTES_PER_FLOAT = 4;
157
- const VERTEX_STRIDE_BYTES = FLOATS_PER_VERTEX * BYTES_PER_FLOAT;
158
- const SPRITE_EPSILON = 0.01;
159
- const MAX_CAPACITY = 16384;
160
- const CORNER_OFFSET_X = [0, 1, 1, 0];
161
- const CORNER_OFFSET_Y = [0, 0, 1, 1];
162
- const SPRITE_ATTRIBUTES = ["position", "options", "offsets", "inverts", "cellInfo", "color"];
163
- function createSpriteRenderer(engine, options) {
164
- const capacity = options.capacity;
165
- if (!Number.isInteger(capacity) || capacity < 1 || capacity > MAX_CAPACITY) {
166
- throw new Error(`lite-gl: sprite renderer capacity must be an integer in [1, ${MAX_CAPACITY}], got ${capacity}`);
167
- }
168
- const cellWidth = options.cellWidth ?? 1;
169
- const cellHeight = options.cellHeight ?? 1;
170
- if (!(cellWidth > 0) || !(cellHeight > 0)) {
171
- throw new Error("lite-gl: sprite renderer cellWidth/cellHeight must be > 0");
172
- }
173
- const effect = createEffect(engine, {
174
- name: "sprites",
175
- vertexSource: SPRITE_VERTEX_SOURCE,
176
- fragmentSource: SPRITE_FRAGMENT_SOURCE,
177
- uniformNames: ["view", "projection"],
178
- samplerNames: ["diffuseSampler"],
179
- attributeNames: SPRITE_ATTRIBUTES
180
- });
181
- const indices = new Uint16Array(capacity * INDICES_PER_SPRITE);
182
- for (let i = 0; i < capacity; i++) {
183
- const v = i * VERTS_PER_SPRITE;
184
- const o = i * INDICES_PER_SPRITE;
185
- indices[o] = v;
186
- indices[o + 1] = v + 1;
187
- indices[o + 2] = v + 2;
188
- indices[o + 3] = v;
189
- indices[o + 4] = v + 2;
190
- indices[o + 5] = v + 3;
191
- }
192
- const renderer = {
193
- texture: options.texture,
194
- cellWidth,
195
- cellHeight,
196
- epsilon: options.epsilon ?? SPRITE_EPSILON,
197
- blendMode: options.blendMode ?? GLBlendMode.ALPHA,
198
- autoResetAlpha: options.autoResetAlpha ?? true,
199
- disableDepthWrite: options.disableDepthWrite ?? false,
200
- capacity,
201
- _engine: engine,
202
- _effect: effect,
203
- _vao: null,
204
- _vbo: null,
205
- _ibo: null,
206
- _vertexData: new Float32Array(capacity * VERTS_PER_SPRITE * FLOATS_PER_VERTEX),
207
- _indices: indices,
208
- _restore: () => {
209
- },
210
- _disposed: false
211
- };
212
- renderer._restore = () => {
213
- buildSpriteBuffers(renderer);
214
- };
215
- onContextRestored(engine, renderer._restore);
216
- buildSpriteBuffers(renderer);
217
- return renderer;
218
- }
219
- function renderSprites(renderer, sprites, deltaTime, viewMatrix, projectionMatrix) {
220
- const engine = renderer._engine;
221
- if (engine._isLost || engine._disposed || renderer._disposed) {
222
- return;
223
- }
224
- const tex = renderer.texture;
225
- if (!tex.isReady || sprites.length === 0) {
226
- return;
227
- }
228
- const effect = renderer._effect;
229
- if (!isEffectReady(engine, effect)) {
230
- return;
231
- }
232
- if (renderer._vao === null || renderer._vbo === null) {
233
- return;
234
- }
235
- const vd = renderer._vertexData;
236
- const eps = renderer.epsilon;
237
- const texW = tex.width;
238
- const texH = tex.height;
239
- const cellW = renderer.cellWidth;
240
- const cellH = renderer.cellHeight;
241
- const rowSize = texW / cellW;
242
- const cellWidthN = cellW / texW;
243
- const cellHeightN = cellH / texH;
244
- const cap = renderer.capacity;
245
- const count = sprites.length;
246
- let visible = 0;
247
- for (let i = 0; i < count && visible < cap; i++) {
248
- const sprite = sprites[i];
249
- if (sprite === void 0 || sprite.isVisible === false) {
250
- continue;
251
- }
252
- let cellLeft;
253
- let cellTop;
254
- let cellWN;
255
- let cellHN;
256
- if (sprite.uSize !== void 0) {
257
- cellLeft = sprite.uOffset ?? 0;
258
- cellTop = sprite.vOffset ?? 0;
259
- cellWN = sprite.uSize / texW;
260
- cellHN = (sprite.vSize ?? 0) / texH;
261
- } else {
262
- const cellIndex = (sprite.cellIndex ?? 0) > 0 ? sprite.cellIndex : 0;
263
- const row = cellIndex / rowSize >> 0;
264
- cellLeft = (cellIndex - row * rowSize) * cellW / texW;
265
- cellTop = row * cellH / texH;
266
- cellWN = cellWidthN;
267
- cellHN = cellHeightN;
268
- }
269
- const px = sprite.position.x;
270
- const py = sprite.position.y;
271
- const pz = sprite.position.z;
272
- const angle = sprite.angle;
273
- const w = sprite.width;
274
- const h = sprite.height;
275
- const invU = sprite.invertU === true ? 1 : 0;
276
- const invV = sprite.invertV === true ? 1 : 0;
277
- const color = sprite.color;
278
- const cr = color !== void 0 ? color.r : 1;
279
- const cg = color !== void 0 ? color.g : 1;
280
- const cb = color !== void 0 ? color.b : 1;
281
- const ca = color !== void 0 ? color.a : 1;
282
- let off = visible * VERTS_PER_SPRITE * FLOATS_PER_VERTEX;
283
- for (let c = 0; c < VERTS_PER_SPRITE; c++) {
284
- const ox = CORNER_OFFSET_X[c] === 0 ? eps : 1 - eps;
285
- const oy = CORNER_OFFSET_Y[c] === 0 ? eps : 1 - eps;
286
- vd[off] = px;
287
- vd[off + 1] = py;
288
- vd[off + 2] = pz;
289
- vd[off + 3] = angle;
290
- vd[off + 4] = w;
291
- vd[off + 5] = h;
292
- vd[off + 6] = ox;
293
- vd[off + 7] = oy;
294
- vd[off + 8] = invU;
295
- vd[off + 9] = invV;
296
- vd[off + 10] = cellLeft;
297
- vd[off + 11] = cellTop;
298
- vd[off + 12] = cellWN;
299
- vd[off + 13] = cellHN;
300
- vd[off + 14] = cr;
301
- vd[off + 15] = cg;
302
- vd[off + 16] = cb;
303
- vd[off + 17] = ca;
304
- off += FLOATS_PER_VERTEX;
305
- }
306
- visible++;
307
- }
308
- if (visible === 0) {
309
- return;
310
- }
311
- const gl = engine.gl;
312
- const s = engine._state;
313
- const vao = renderer._vao;
314
- if (s.boundVao !== vao) {
315
- gl.bindVertexArray(vao);
316
- s.boundVao = vao;
317
- s.boundElementBuffer = renderer._ibo;
318
- }
319
- const vbo = renderer._vbo;
320
- if (s.boundArrayBuffer !== vbo) {
321
- gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
322
- s.boundArrayBuffer = vbo;
323
- }
324
- const floatCount = visible * VERTS_PER_SPRITE * FLOATS_PER_VERTEX;
325
- gl.bufferSubData(gl.ARRAY_BUFFER, 0, vd, 0, floatCount);
326
- useEffect(engine, effect);
327
- const viewLoc = effect.uniformLocations["view"];
328
- if (viewLoc !== null && viewLoc !== void 0) {
329
- gl.uniformMatrix4fv(viewLoc, false, viewMatrix);
330
- }
331
- const projLoc = effect.uniformLocations["projection"];
332
- if (projLoc !== null && projLoc !== void 0) {
333
- gl.uniformMatrix4fv(projLoc, false, projectionMatrix);
334
- }
335
- setEffectTexture(engine, effect, "diffuseSampler", tex);
336
- setBlendMode(engine, renderer.blendMode);
337
- applyGLStates(engine);
338
- gl.drawElements(gl.TRIANGLES, visible * INDICES_PER_SPRITE, gl.UNSIGNED_SHORT, 0);
339
- if (renderer.autoResetAlpha) {
340
- setBlendMode(engine, GLBlendMode.DISABLE);
341
- }
342
- }
343
- function setSpriteRendererTexture(renderer, texture) {
344
- if (renderer._disposed) {
345
- return;
346
- }
347
- renderer.texture = texture;
348
- }
349
- function disposeSpriteRenderer(renderer) {
350
- if (renderer._disposed) {
351
- return;
352
- }
353
- renderer._disposed = true;
354
- const engine = renderer._engine;
355
- offContextRestored(engine, renderer._restore);
356
- disposeEffect(engine, renderer._effect);
357
- if (!engine._isLost && !engine._disposed) {
358
- const gl = engine.gl;
359
- const s = engine._state;
360
- if (renderer._vao !== null) {
361
- gl.deleteVertexArray(renderer._vao);
362
- if (s.boundVao === renderer._vao) {
363
- s.boundVao = null;
364
- }
365
- }
366
- if (renderer._vbo !== null) {
367
- gl.deleteBuffer(renderer._vbo);
368
- if (s.boundArrayBuffer === renderer._vbo) {
369
- s.boundArrayBuffer = null;
370
- }
371
- }
372
- if (renderer._ibo !== null) {
373
- gl.deleteBuffer(renderer._ibo);
374
- if (s.boundElementBuffer === renderer._ibo) {
375
- s.boundElementBuffer = null;
376
- }
377
- }
378
- }
379
- renderer._vao = null;
380
- renderer._vbo = null;
381
- renderer._ibo = null;
382
- }
383
- function buildSpriteBuffers(renderer) {
384
- const engine = renderer._engine;
385
- if (engine._isLost || engine._disposed) {
386
- return;
387
- }
388
- const gl = engine.gl;
389
- const s = engine._state;
390
- const vao = gl.createVertexArray();
391
- if (vao === null) {
392
- throw new Error("lite-gl: gl.createVertexArray returned null (sprite VAO)");
393
- }
394
- renderer._vao = vao;
395
- gl.bindVertexArray(vao);
396
- s.boundVao = vao;
397
- const vbo = gl.createBuffer();
398
- if (vbo === null) {
399
- throw new Error("lite-gl: gl.createBuffer returned null (sprite VBO)");
400
- }
401
- renderer._vbo = vbo;
402
- gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
403
- s.boundArrayBuffer = vbo;
404
- gl.bufferData(gl.ARRAY_BUFFER, renderer._vertexData.byteLength, gl.DYNAMIC_DRAW);
405
- gl.enableVertexAttribArray(0);
406
- gl.vertexAttribPointer(0, 4, gl.FLOAT, false, VERTEX_STRIDE_BYTES, 0);
407
- gl.enableVertexAttribArray(1);
408
- gl.vertexAttribPointer(1, 2, gl.FLOAT, false, VERTEX_STRIDE_BYTES, 4 * BYTES_PER_FLOAT);
409
- gl.enableVertexAttribArray(2);
410
- gl.vertexAttribPointer(2, 2, gl.FLOAT, false, VERTEX_STRIDE_BYTES, 6 * BYTES_PER_FLOAT);
411
- gl.enableVertexAttribArray(3);
412
- gl.vertexAttribPointer(3, 2, gl.FLOAT, false, VERTEX_STRIDE_BYTES, 8 * BYTES_PER_FLOAT);
413
- gl.enableVertexAttribArray(4);
414
- gl.vertexAttribPointer(4, 4, gl.FLOAT, false, VERTEX_STRIDE_BYTES, 10 * BYTES_PER_FLOAT);
415
- gl.enableVertexAttribArray(5);
416
- gl.vertexAttribPointer(5, 4, gl.FLOAT, false, VERTEX_STRIDE_BYTES, 14 * BYTES_PER_FLOAT);
417
- const ibo = gl.createBuffer();
418
- if (ibo === null) {
419
- throw new Error("lite-gl: gl.createBuffer returned null (sprite IBO)");
420
- }
421
- renderer._ibo = ibo;
422
- gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibo);
423
- s.boundElementBuffer = ibo;
424
- gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, renderer._indices, gl.STATIC_DRAW);
425
- }
426
- export {
427
- GLBlendEquation as G,
428
- GLBlendMode as a,
429
- disposeSpriteRenderer as b,
430
- createSpriteRenderer as c,
431
- disableBlend as d,
432
- setBlendState as e,
433
- setSpriteRendererTexture as f,
434
- renderSprites as r,
435
- setBlendMode as s
436
- };
437
- //# sourceMappingURL=sprites--1oyVtJ3.js.map