@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
package/README.md CHANGED
@@ -25,17 +25,7 @@ npm install @babylonjs/lite-gl
25
25
  ## Quick start — an animated fullscreen effect
26
26
 
27
27
  ```ts
28
- import {
29
- createGLEngine,
30
- createEffectWrapper,
31
- isEffectReady,
32
- applyEffectWrapper,
33
- drawEffect,
34
- setViewport,
35
- setEffectFloat,
36
- runRenderLoop,
37
- resizeGLEngine,
38
- } from "@babylonjs/lite-gl";
28
+ import { createGLEngine, createEffectWrapper, isEffectReady, applyEffectWrapper, drawEffect, setViewport, setEffectFloat, runRenderLoop, resizeGLEngine } from "@babylonjs/lite-gl";
39
29
 
40
30
  const canvas = document.getElementById("renderCanvas") as HTMLCanvasElement;
41
31
  const engine = createGLEngine(canvas, { alpha: false });
@@ -69,23 +59,13 @@ runRenderLoop(engine, () => {
69
59
 
70
60
  ## Entry points
71
61
 
72
- The full public API is available from the main `@babylonjs/lite-gl` barrel.
73
- `/sprites`, `/html-texture`, `/render-target`, `/mesh`, `/depth-stencil`,
74
- `/scissor` and `/dynamic-texture` are **also** exposed as dedicated sub-entries
75
- for consumers who prefer an explicit import the package is `sideEffects: false`,
76
- so a bundler tree-shakes away whichever features you don't use no matter which
77
- path you import from.
78
-
79
- | Import | Provides |
80
- | --- | --- |
81
- | `@babylonjs/lite-gl` | Everything: engine + render loop, effects & uniform setters, textures (incl. float / HDR + dynamic), the `EffectWrapper` fullscreen-quad renderer, render targets, meshes / vertex-index buffers + instancing, depth / stencil / color-mask / clear, scissor, blend modes, the sprite renderer, and HTML-element textures. |
82
- | `@babylonjs/lite-gl/sprites` | Just the sprite / instanced-quad renderer (`createSpriteRenderer`, `renderSprites`, `setSpriteRendererTexture`, `disposeSpriteRenderer`, `GLSprite`) — the lite-gl equivalent of Babylon's `SpriteRenderer`. |
83
- | `@babylonjs/lite-gl/html-texture` | Just textures backed by a `<canvas>` / `<img>` / `<video>` element (`createHtmlElementTexture`, `updateHtmlElementTexture`, `GLSamplingMode`). |
84
- | `@babylonjs/lite-gl/render-target` | Render-to-texture (FBO) + a ping-pong feedback helper (`createRenderTarget`, `createFloatRenderTarget`, `bindRenderTarget`, `resizeRenderTarget`, `generateRenderTargetMipMaps`, `readRenderTargetPixels`, `disposeRenderTarget`, `createPingPong`, `resizePingPong`, `disposePingPong`) — the lite-gl equivalent of Babylon's `RenderTargetTexture` / `createRenderTargetTexture`, for multi-pass and self-feedback effects. |
85
- | `@babylonjs/lite-gl/mesh` | Vertex / index buffers, attribute binding and instanced `drawIndexed` (`createVertexBuffer`, `updateVertexBuffer`, `createIndexBuffer`, `bindAttributes`, `drawIndexed`, …) — the lite-gl equivalent of Babylon's `VertexBuffer` / `Geometry`. |
86
- | `@babylonjs/lite-gl/depth-stencil` | Cached depth / stencil / cull / color-mask state + `clearEngine` (`setDepthState`, `setStencilState`, `setCullState`, `setColorMask`, `clearEngine`). |
87
- | `@babylonjs/lite-gl/scissor` | The cached scissor-test rectangle (`setScissor`, `disableScissor`). |
88
- | `@babylonjs/lite-gl/dynamic-texture` | Textures whose pixels are replayed from a retained source on context-restore (`createDynamicTexture`, `updateDynamicTexture`, `clearDynamicTextureSource`). |
62
+ The entire public API is available from the single `@babylonjs/lite-gl` entry:
63
+ engine + render loop, effects & uniform setters, textures (incl. float / HDR +
64
+ dynamic), the `EffectWrapper` fullscreen-quad renderer, render targets, meshes /
65
+ vertex-index buffers + instancing, depth / stencil / color-mask / clear, scissor,
66
+ blend modes, the sprite renderer, and HTML-element textures. The package is
67
+ `sideEffects: false`, so a bundler tree-shakes away whichever features you don't
68
+ use.
89
69
 
90
70
  ### Core API (`@babylonjs/lite-gl`)
91
71
 
@@ -107,20 +87,16 @@ path you import from.
107
87
  `updateTextureWrapMode`, `createTextureFromHandle`.
108
88
  - **Dynamic textures** — `createDynamicTexture`, `updateDynamicTexture`,
109
89
  `clearDynamicTextureSource` (the retained source is replayed on context-restore).
110
- Also at `@babylonjs/lite-gl/dynamic-texture`.
111
90
  - **Render targets** — `createRenderTarget` (RGBA8 FBO with a sampleable color
112
91
  `GLTexture` + optional depth / stencil renderbuffer), `createFloatRenderTarget`
113
92
  (float / half-float HDR opt-in), `bindRenderTarget` (cached, null = the canvas),
114
93
  `resizeRenderTarget`, `generateRenderTargetMipMaps`, `readRenderTargetPixels`,
115
94
  `disposeRenderTarget`, plus the `createPingPong` / `resizePingPong` /
116
- `disposePingPong` feedback helper. Also at `@babylonjs/lite-gl/render-target`.
95
+ `disposePingPong` feedback helper.
117
96
  - **Meshes / buffers** — `createVertexBuffer`, `updateVertexBuffer`,
118
97
  `createIndexBuffer`, `bindAttributes`, `drawIndexed` (instanced), `disposeBuffer`.
119
- Also at `@babylonjs/lite-gl/mesh`.
120
98
  - **Depth / stencil / scissor** — `setDepthState`, `setStencilState`,
121
- `setCullState`, `setColorMask`, `clearEngine` (also at
122
- `@babylonjs/lite-gl/depth-stencil`); `setScissor`, `disableScissor` (also at
123
- `@babylonjs/lite-gl/scissor`).
99
+ `setCullState`, `setColorMask`, `clearEngine`; `setScissor`, `disableScissor`.
124
100
  - **Blend** — `setBlendMode` + `GLBlendMode` (`DISABLE` / `ADD` / `ALPHA` /
125
101
  `PREMULTIPLIED`), matching Babylon's `setAlphaMode` parameters.
126
102
 
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Flush the deferred blend / depth+cull / stencil / color-mask state to GL by
3
+ * invoking each installed per-category reconciler in order. No-op on a
4
+ * lost/disposed context or when nothing has been marked dirty since the last
5
+ * flush. Reconcilers are installed lazily by their setters, so an uninstalled
6
+ * category is skipped at zero cost.
7
+ *
8
+ * @param engine - The engine whose deferred state is flushed.
9
+ * @internal
10
+ */
11
+ export function applyGLStates(engine) {
12
+ const s = engine._state;
13
+ if (engine._isLost || engine._disposed || !s.statesDirty) {
14
+ return;
15
+ }
16
+ if (s._flushBlend !== undefined) {
17
+ s._flushBlend(engine);
18
+ }
19
+ if (s._flushDepthCull !== undefined) {
20
+ s._flushDepthCull(engine);
21
+ }
22
+ if (s._flushStencil !== undefined) {
23
+ s._flushStencil(engine);
24
+ }
25
+ if (s._flushColorMask !== undefined) {
26
+ s._flushColorMask(engine);
27
+ }
28
+ s.statesDirty = false;
29
+ }
30
+ //# sourceMappingURL=apply-states.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apply-states.js","sourceRoot":"","sources":["../src/apply-states.ts"],"names":[],"mappings":"AAqCA;;;;;;;;;GASG;AACH,MAAM,UAAU,aAAa,CAAC,MAAuB;IACjD,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC;IACxB,IAAI,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC,SAAS,IAAI,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;QACvD,OAAO;IACX,CAAC;IACD,IAAI,CAAC,CAAC,WAAW,KAAK,SAAS,EAAE,CAAC;QAC9B,CAAC,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;IAC1B,CAAC;IACD,IAAI,CAAC,CAAC,eAAe,KAAK,SAAS,EAAE,CAAC;QAClC,CAAC,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC;IAC9B,CAAC;IACD,IAAI,CAAC,CAAC,aAAa,KAAK,SAAS,EAAE,CAAC;QAChC,CAAC,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;IAC5B,CAAC;IACD,IAAI,CAAC,CAAC,eAAe,KAAK,SAAS,EAAE,CAAC;QAClC,CAAC,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC;IAC9B,CAAC;IACD,CAAC,CAAC,WAAW,GAAG,KAAK,CAAC;AAC1B,CAAC","sourcesContent":["/**\n * Deferred render-state flush dispatcher — the lite-gl counterpart of Babylon's\n * `Engine.applyStates()` (`_depthCullingState.apply` / `_alphaState.apply` /\n * `_stencilState.apply`).\n *\n * blend.ts and depth-stencil.ts setters record only the DESIRED state (the\n * `rs[RS_X + RS_DESIRED]` slots of {@link GLState.rs}), raise `statesDirty`, and\n * INSTALL their per-category reconciler onto a `_state._flush*` slot the first\n * time they run. {@link applyGLStates} then simply dispatches through whichever\n * slots are populated — it owns NO reconciliation code itself.\n *\n * Why a dispatcher (not a monolith): the reconcilers (`flushBlend` in blend.ts;\n * `flushDepthCull` / `flushStencil` / `flushColorMask` in depth-stencil.ts) are\n * reachable ONLY through the engine-state slots their setters populate, so a\n * category whose setter is absent from a scene tree-shakes its reconciler — and\n * its GL code — out of the bundle. A clear-only scene (e.g. `gl-scissor`) that\n * never touches blend/depth/stencil/color-mask therefore ships none of them;\n * `applyGLStates` collapses to four cheap \"is it installed?\" checks. The fixed\n * dispatch order (blend → depth+cull → stencil → color-mask) reproduces the\n * former monolith's GL call order exactly.\n *\n * Each reconciler still issues only the `gl.*` calls that actually change and\n * updates the actual slots in lock-step, so intra-frame churn collapses and\n * cross-frame elision is preserved, matching Babylon exactly.\n *\n * Flush sites (call `applyGLStates` immediately before the GPU op):\n * - effect-renderer.ts `drawEffect`\n * - sprites.ts `renderSprites`\n * - mesh.ts `drawIndexed`\n * - depth-stencil.ts `clearEngine` (clear respects the depth/stencil/color\n * write masks, so they must be current first — Babylon parity)\n *\n * Kept dependency-light (type-only import of {@link GLEngineContext}) so it never\n * introduces an import cycle.\n */\nimport type { GLEngineContext } from \"./context.js\";\n\n/**\n * Flush the deferred blend / depth+cull / stencil / color-mask state to GL by\n * invoking each installed per-category reconciler in order. No-op on a\n * lost/disposed context or when nothing has been marked dirty since the last\n * flush. Reconcilers are installed lazily by their setters, so an uninstalled\n * category is skipped at zero cost.\n *\n * @param engine - The engine whose deferred state is flushed.\n * @internal\n */\nexport function applyGLStates(engine: GLEngineContext): void {\n const s = engine._state;\n if (engine._isLost || engine._disposed || !s.statesDirty) {\n return;\n }\n if (s._flushBlend !== undefined) {\n s._flushBlend(engine);\n }\n if (s._flushDepthCull !== undefined) {\n s._flushDepthCull(engine);\n }\n if (s._flushStencil !== undefined) {\n s._flushStencil(engine);\n }\n if (s._flushColorMask !== undefined) {\n s._flushColorMask(engine);\n }\n s.statesDirty = false;\n}\n"]}
package/blend.d.ts ADDED
@@ -0,0 +1,123 @@
1
+ /**
2
+ * Blend-mode state — the WebGL counterpart of Babylon's `Engine.setAlphaMode`
3
+ * (presets) and `AlphaState.setAlphaBlendFunctionParameters` /
4
+ * `setAlphaEquationParameters` (the arbitrary separate func + equation path).
5
+ *
6
+ * The numeric {@link GLBlendMode} values intentionally match Babylon's
7
+ * `Constants.ALPHA_*` (`ALPHA_DISABLE = 0`, `ALPHA_ADD = 1`, `ALPHA_COMBINE = 2`,
8
+ * `ALPHA_PREMULTIPLIED = 7`) so a consumer can forward raw Babylon constants
9
+ * without a translation table.
10
+ *
11
+ * DEFERRED MODEL (matches Babylon's `AlphaState`): these setters do NOT touch
12
+ * `gl.*`. They record only the DESIRED blend config into the `d*` mirror fields
13
+ * of `GLState` and raise `statesDirty`; `applyGLStates` (apply-states.ts) flushes
14
+ * the diff to GL right before each draw / clear. Setting blend A then B then A
15
+ * with no draw in between therefore applies exactly one blend state (A), and a
16
+ * blend left unchanged across frames re-issues nothing.
17
+ *
18
+ * Both {@link setBlendMode} and {@link setBlendState} feed the same desired
19
+ * fields, so the preset and arbitrary paths can never desync.
20
+ */
21
+ import { type GLEngineContext } from "./context.js";
22
+ /**
23
+ * Supported blend presets. Values mirror Babylon's `Constants.ALPHA_*` so the
24
+ * raw Babylon integers can be passed straight through.
25
+ */
26
+ export declare const GLBlendMode: {
27
+ /** No blending — `gl.disable(gl.BLEND)`. (`Constants.ALPHA_DISABLE`) */
28
+ readonly DISABLE: 0;
29
+ /** Additive — `blendFuncSeparate(SRC_ALPHA, ONE, ZERO, ONE)`. (`Constants.ALPHA_ADD`) */
30
+ readonly ADD: 1;
31
+ /** Standard (non-premultiplied) alpha — `blendFuncSeparate(SRC_ALPHA, ONE_MINUS_SRC_ALPHA, ONE, ONE)`. (`Constants.ALPHA_COMBINE`) */
32
+ readonly ALPHA: 2;
33
+ /** Premultiplied alpha — `blendFuncSeparate(ONE, ONE_MINUS_SRC_ALPHA, ONE, ONE)`. (`Constants.ALPHA_PREMULTIPLIED`) */
34
+ readonly PREMULTIPLIED: 7;
35
+ };
36
+ /** One of the {@link GLBlendMode} preset values (`0`, `1`, `2` or `7`). */
37
+ export type GLBlendMode = (typeof GLBlendMode)[keyof typeof GLBlendMode];
38
+ /**
39
+ * Blend equation presets — the values WebGL2 accepts for
40
+ * `gl.blendEquationSeparate`. Numeric values equal the GL enums so raw GL
41
+ * integers (or Babylon's identical `Constants.GL_ALPHA_EQUATION_*`) pass
42
+ * straight through.
43
+ */
44
+ export declare const GLBlendEquation: {
45
+ /** `src + dst` (the GL default). */
46
+ readonly ADD: 32774;
47
+ /** `src - dst`. */
48
+ readonly SUBTRACT: 32778;
49
+ /** `dst - src`. */
50
+ readonly REVERSE_SUBTRACT: 32779;
51
+ /** `min(src, dst)`. */
52
+ readonly MIN: 32775;
53
+ /** `max(src, dst)`. */
54
+ readonly MAX: 32776;
55
+ };
56
+ /** One of the {@link GLBlendEquation} preset values. */
57
+ export type GLBlendEquation = (typeof GLBlendEquation)[keyof typeof GLBlendEquation];
58
+ /**
59
+ * Arbitrary separate-channel blend configuration — the lite-gl equivalent of
60
+ * Babylon's `AlphaState.setAlphaBlendFunctionParameters` +
61
+ * `setAlphaEquationParameters`. All factor / equation fields are raw WebGL2
62
+ * enums (`gl.ONE`, `gl.SRC_ALPHA`, `gl.MIN`, …); use {@link GLBlendEquation} for
63
+ * the equations if you prefer named presets.
64
+ */
65
+ export interface GLBlendState {
66
+ /** RGB source factor (`gl.blendFuncSeparate` arg 1). */
67
+ srcRGB: GLenum;
68
+ /** RGB destination factor (`gl.blendFuncSeparate` arg 2). */
69
+ dstRGB: GLenum;
70
+ /** Alpha source factor (`gl.blendFuncSeparate` arg 3). */
71
+ srcAlpha: GLenum;
72
+ /** Alpha destination factor (`gl.blendFuncSeparate` arg 4). */
73
+ dstAlpha: GLenum;
74
+ /** RGB blend equation. Defaults to `FUNC_ADD`. */
75
+ equationRGB?: GLenum;
76
+ /** Alpha blend equation. Defaults to `FUNC_ADD`. */
77
+ equationAlpha?: GLenum;
78
+ }
79
+ /**
80
+ * Set the GL blend state to match Babylon's `setAlphaMode(mode)` exactly.
81
+ *
82
+ * | Mode | `gl.blendFuncSeparate(srcRGB, dstRGB, srcA, dstA)` |
83
+ * |--------------------|------------------------------------------------------------|
84
+ * | `DISABLE` (0) | — (`gl.disable(gl.BLEND)`) |
85
+ * | `ADD` (1) | `SRC_ALPHA, ONE, ZERO, ONE` |
86
+ * | `ALPHA` (2) | `SRC_ALPHA, ONE_MINUS_SRC_ALPHA, ONE, ONE` |
87
+ * | `PREMULTIPLIED` (7)| `ONE, ONE_MINUS_SRC_ALPHA, ONE, ONE` |
88
+ *
89
+ * No-op when the context is lost or disposed.
90
+ *
91
+ * @param engine - The engine whose GL blend state is updated.
92
+ * @param mode - The {@link GLBlendMode} preset to apply.
93
+ */
94
+ export declare function setBlendMode(engine: GLEngineContext, mode: GLBlendMode): void;
95
+ /**
96
+ * Enable blending with an arbitrary separate-channel function and equation —
97
+ * the lite-gl equivalent of Babylon's `AlphaState` with
98
+ * `setAlphaBlendFunctionParameters` + `setAlphaEquationParameters`.
99
+ *
100
+ * Supports every WebGL2 blend equation, including `MIN`, `MAX`,
101
+ * `FUNC_SUBTRACT` and `FUNC_REVERSE_SUBTRACT` (used by ShapeBuilder's darken /
102
+ * cutout blend modes). Records the desired config (flushed by `applyGLStates`
103
+ * before the next draw); at flush time the enable flag, the equation and the func
104
+ * are each cached independently, so a redundant state is fully elided and only
105
+ * the call whose params changed is re-issued — mirroring Babylon's `AlphaState`
106
+ * dirty flags. Because GL keeps the equation + func across `gl.disable(BLEND)`,
107
+ * re-enabling with unchanged params re-issues neither.
108
+ *
109
+ * No-op when the context is lost or disposed.
110
+ *
111
+ * @param engine - The engine whose desired blend state is updated.
112
+ * @param state - The separate-channel blend factors + equations to apply.
113
+ */
114
+ export declare function setBlendState(engine: GLEngineContext, state: GLBlendState): void;
115
+ /**
116
+ * Disable blending — the equivalent of Babylon's `AlphaState.alphaBlend = false`.
117
+ * Records the desired "blend off" state (flushed by `applyGLStates` before the
118
+ * next draw); the actual `gl.disable(gl.BLEND)` is elided when blending is
119
+ * already off at flush time. No-op when the context is lost or disposed.
120
+ *
121
+ * @param engine - The engine whose desired blend state is updated.
122
+ */
123
+ export declare function disableBlend(engine: GLEngineContext): void;
package/blend.js ADDED
@@ -0,0 +1,194 @@
1
+ import { RS_BLEND_DST_A, RS_BLEND_DST_RGB, RS_BLEND_ENABLED, RS_BLEND_EQ_A, RS_BLEND_EQ_RGB, RS_BLEND_SRC_A, RS_BLEND_SRC_RGB, RS_DESIRED } from "./state.js";
2
+ /**
3
+ * Supported blend presets. Values mirror Babylon's `Constants.ALPHA_*` so the
4
+ * raw Babylon integers can be passed straight through.
5
+ */
6
+ export const GLBlendMode = {
7
+ /** No blending — `gl.disable(gl.BLEND)`. (`Constants.ALPHA_DISABLE`) */
8
+ DISABLE: 0,
9
+ /** Additive — `blendFuncSeparate(SRC_ALPHA, ONE, ZERO, ONE)`. (`Constants.ALPHA_ADD`) */
10
+ ADD: 1,
11
+ /** Standard (non-premultiplied) alpha — `blendFuncSeparate(SRC_ALPHA, ONE_MINUS_SRC_ALPHA, ONE, ONE)`. (`Constants.ALPHA_COMBINE`) */
12
+ ALPHA: 2,
13
+ /** Premultiplied alpha — `blendFuncSeparate(ONE, ONE_MINUS_SRC_ALPHA, ONE, ONE)`. (`Constants.ALPHA_PREMULTIPLIED`) */
14
+ PREMULTIPLIED: 7,
15
+ };
16
+ /**
17
+ * Blend equation presets — the values WebGL2 accepts for
18
+ * `gl.blendEquationSeparate`. Numeric values equal the GL enums so raw GL
19
+ * integers (or Babylon's identical `Constants.GL_ALPHA_EQUATION_*`) pass
20
+ * straight through.
21
+ */
22
+ export const GLBlendEquation = {
23
+ /** `src + dst` (the GL default). */
24
+ ADD: 0x8006,
25
+ /** `src - dst`. */
26
+ SUBTRACT: 0x800a,
27
+ /** `dst - src`. */
28
+ REVERSE_SUBTRACT: 0x800b,
29
+ /** `min(src, dst)`. */
30
+ MIN: 0x8007,
31
+ /** `max(src, dst)`. */
32
+ MAX: 0x8008,
33
+ };
34
+ /** GL `FUNC_ADD` — the implicit equation used by the {@link GLBlendMode}
35
+ * presets (matching Babylon's `setAlphaMode`, which leaves it at the default). */
36
+ const FUNC_ADD = 0x8006;
37
+ /** GL `gl.BLEND`. */
38
+ const BLEND = 0x0be2;
39
+ /**
40
+ * Set the GL blend state to match Babylon's `setAlphaMode(mode)` exactly.
41
+ *
42
+ * | Mode | `gl.blendFuncSeparate(srcRGB, dstRGB, srcA, dstA)` |
43
+ * |--------------------|------------------------------------------------------------|
44
+ * | `DISABLE` (0) | — (`gl.disable(gl.BLEND)`) |
45
+ * | `ADD` (1) | `SRC_ALPHA, ONE, ZERO, ONE` |
46
+ * | `ALPHA` (2) | `SRC_ALPHA, ONE_MINUS_SRC_ALPHA, ONE, ONE` |
47
+ * | `PREMULTIPLIED` (7)| `ONE, ONE_MINUS_SRC_ALPHA, ONE, ONE` |
48
+ *
49
+ * No-op when the context is lost or disposed.
50
+ *
51
+ * @param engine - The engine whose GL blend state is updated.
52
+ * @param mode - The {@link GLBlendMode} preset to apply.
53
+ */
54
+ export function setBlendMode(engine, mode) {
55
+ if (engine._isLost || engine._disposed) {
56
+ return;
57
+ }
58
+ const gl = engine.gl;
59
+ switch (mode) {
60
+ case GLBlendMode.DISABLE:
61
+ disableBlend(engine);
62
+ return;
63
+ case GLBlendMode.ADD:
64
+ applyBlend(engine, gl.SRC_ALPHA, gl.ONE, gl.ZERO, gl.ONE, FUNC_ADD, FUNC_ADD);
65
+ return;
66
+ case GLBlendMode.ALPHA:
67
+ applyBlend(engine, gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE, FUNC_ADD, FUNC_ADD);
68
+ return;
69
+ case GLBlendMode.PREMULTIPLIED:
70
+ applyBlend(engine, gl.ONE, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE, FUNC_ADD, FUNC_ADD);
71
+ return;
72
+ }
73
+ }
74
+ /**
75
+ * Enable blending with an arbitrary separate-channel function and equation —
76
+ * the lite-gl equivalent of Babylon's `AlphaState` with
77
+ * `setAlphaBlendFunctionParameters` + `setAlphaEquationParameters`.
78
+ *
79
+ * Supports every WebGL2 blend equation, including `MIN`, `MAX`,
80
+ * `FUNC_SUBTRACT` and `FUNC_REVERSE_SUBTRACT` (used by ShapeBuilder's darken /
81
+ * cutout blend modes). Records the desired config (flushed by `applyGLStates`
82
+ * before the next draw); at flush time the enable flag, the equation and the func
83
+ * are each cached independently, so a redundant state is fully elided and only
84
+ * the call whose params changed is re-issued — mirroring Babylon's `AlphaState`
85
+ * dirty flags. Because GL keeps the equation + func across `gl.disable(BLEND)`,
86
+ * re-enabling with unchanged params re-issues neither.
87
+ *
88
+ * No-op when the context is lost or disposed.
89
+ *
90
+ * @param engine - The engine whose desired blend state is updated.
91
+ * @param state - The separate-channel blend factors + equations to apply.
92
+ */
93
+ export function setBlendState(engine, state) {
94
+ if (engine._isLost || engine._disposed) {
95
+ return;
96
+ }
97
+ applyBlend(engine, state.srcRGB, state.dstRGB, state.srcAlpha, state.dstAlpha, state.equationRGB ?? FUNC_ADD, state.equationAlpha ?? FUNC_ADD);
98
+ }
99
+ /**
100
+ * Disable blending — the equivalent of Babylon's `AlphaState.alphaBlend = false`.
101
+ * Records the desired "blend off" state (flushed by `applyGLStates` before the
102
+ * next draw); the actual `gl.disable(gl.BLEND)` is elided when blending is
103
+ * already off at flush time. No-op when the context is lost or disposed.
104
+ *
105
+ * @param engine - The engine whose desired blend state is updated.
106
+ */
107
+ export function disableBlend(engine) {
108
+ if (engine._isLost || engine._disposed) {
109
+ return;
110
+ }
111
+ const s = engine._state;
112
+ s.rs[RS_BLEND_ENABLED + RS_DESIRED] = 0;
113
+ s._flushBlend = flushBlend;
114
+ s.statesDirty = true;
115
+ }
116
+ /* ──────────────────────────── internal apply ──────────────────────────── */
117
+ /** Record a granular blend config into the DESIRED (`rs[RS_* + RS_DESIRED]`)
118
+ * slots and mark it dirty. No `gl.*` here — `applyGLStates` reconciles
119
+ * desired→actual at the next flush, caching the enable flag, equation and func
120
+ * independently so each is re-issued only when its params change (and never on a
121
+ * bare re-enable). */
122
+ function applyBlend(engine, srcRGB, dstRGB, srcAlpha, dstAlpha, eqRGB, eqAlpha) {
123
+ const rs = engine._state.rs;
124
+ rs[RS_BLEND_ENABLED + RS_DESIRED] = 1;
125
+ rs[RS_BLEND_SRC_RGB + RS_DESIRED] = srcRGB;
126
+ rs[RS_BLEND_DST_RGB + RS_DESIRED] = dstRGB;
127
+ rs[RS_BLEND_SRC_A + RS_DESIRED] = srcAlpha;
128
+ rs[RS_BLEND_DST_A + RS_DESIRED] = dstAlpha;
129
+ rs[RS_BLEND_EQ_RGB + RS_DESIRED] = eqRGB;
130
+ rs[RS_BLEND_EQ_A + RS_DESIRED] = eqAlpha;
131
+ engine._state._flushBlend = flushBlend;
132
+ engine._state.statesDirty = true;
133
+ }
134
+ /**
135
+ * Reconcile the deferred blend state DESIRED → ACTUAL — the per-category half of
136
+ * Babylon's `_alphaState.apply`. Installed onto `_state._flushBlend` by the blend
137
+ * setters and dispatched by {@link applyGLStates}; co-located here so a scene that
138
+ * never sets a blend mode tree-shakes both this reconciler and its GL code out of
139
+ * the bundle.
140
+ *
141
+ * The enable flag, the equation and the func are reconciled as THREE independent
142
+ * cached sub-states (mirroring Babylon's `_AlphaState` `_isAlphaBlendDirty` /
143
+ * `_isBlendEquationParametersDirty` / `_isBlendFunctionParametersDirty`). GL
144
+ * retains the blend equation + func across `gl.disable(BLEND)`, so their cached
145
+ * actual values stay valid while blending is off — re-enabling with unchanged
146
+ * params therefore issues ONLY `gl.enable(BLEND)` and re-issues neither
147
+ * `blendEquationSeparate` nor `blendFuncSeparate`. A desired `-1` (never
148
+ * requested) leaves GL untouched. @internal
149
+ */
150
+ function flushBlend(engine) {
151
+ const gl = engine.gl;
152
+ const rs = engine._state.rs;
153
+ const dBlend = rs[RS_BLEND_ENABLED + RS_DESIRED];
154
+ if (dBlend === -1) {
155
+ return;
156
+ }
157
+ // 1. enable / disable — independent of the func + equation cache below.
158
+ if (dBlend !== rs[RS_BLEND_ENABLED]) {
159
+ rs[RS_BLEND_ENABLED] = dBlend;
160
+ if (dBlend === 1) {
161
+ gl.enable(BLEND);
162
+ }
163
+ else {
164
+ gl.disable(BLEND);
165
+ }
166
+ }
167
+ // While blending is off the equation/func are not observable and GL keeps the
168
+ // last values, so leave their cache untouched (Babylon does the same) — the
169
+ // next enable with identical params then re-issues nothing.
170
+ if (dBlend !== 1) {
171
+ return;
172
+ }
173
+ // 2. equation — re-issued only when the params actually changed.
174
+ const eqRGB = rs[RS_BLEND_EQ_RGB + RS_DESIRED];
175
+ const eqA = rs[RS_BLEND_EQ_A + RS_DESIRED];
176
+ if (rs[RS_BLEND_EQ_RGB] !== eqRGB || rs[RS_BLEND_EQ_A] !== eqA) {
177
+ gl.blendEquationSeparate(eqRGB, eqA);
178
+ rs[RS_BLEND_EQ_RGB] = eqRGB;
179
+ rs[RS_BLEND_EQ_A] = eqA;
180
+ }
181
+ // 3. func — re-issued only when the params actually changed.
182
+ const srcRGB = rs[RS_BLEND_SRC_RGB + RS_DESIRED];
183
+ const dstRGB = rs[RS_BLEND_DST_RGB + RS_DESIRED];
184
+ const srcA = rs[RS_BLEND_SRC_A + RS_DESIRED];
185
+ const dstA = rs[RS_BLEND_DST_A + RS_DESIRED];
186
+ 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) {
187
+ gl.blendFuncSeparate(srcRGB, dstRGB, srcA, dstA);
188
+ rs[RS_BLEND_SRC_RGB] = srcRGB;
189
+ rs[RS_BLEND_DST_RGB] = dstRGB;
190
+ rs[RS_BLEND_SRC_A] = srcA;
191
+ rs[RS_BLEND_DST_A] = dstA;
192
+ }
193
+ }
194
+ //# sourceMappingURL=blend.js.map
package/blend.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blend.js","sourceRoot":"","sources":["../src/blend.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Blend-mode state — the WebGL counterpart of Babylon's `Engine.setAlphaMode`\n * (presets) and `AlphaState.setAlphaBlendFunctionParameters` /\n * `setAlphaEquationParameters` (the arbitrary separate func + equation path).\n *\n * The numeric {@link GLBlendMode} values intentionally match Babylon's\n * `Constants.ALPHA_*` (`ALPHA_DISABLE = 0`, `ALPHA_ADD = 1`, `ALPHA_COMBINE = 2`,\n * `ALPHA_PREMULTIPLIED = 7`) so a consumer can forward raw Babylon constants\n * without a translation table.\n *\n * DEFERRED MODEL (matches Babylon's `AlphaState`): these setters do NOT touch\n * `gl.*`. They record only the DESIRED blend config into the `d*` mirror fields\n * of `GLState` and raise `statesDirty`; `applyGLStates` (apply-states.ts) flushes\n * the diff to GL right before each draw / clear. Setting blend A then B then A\n * with no draw in between therefore applies exactly one blend state (A), and a\n * blend left unchanged across frames re-issues nothing.\n *\n * Both {@link setBlendMode} and {@link setBlendState} feed the same desired\n * fields, so the preset and arbitrary paths can never desync.\n */\nimport { type GLEngineContext } from \"./context.js\";\nimport { RS_BLEND_DST_A, RS_BLEND_DST_RGB, RS_BLEND_ENABLED, RS_BLEND_EQ_A, RS_BLEND_EQ_RGB, RS_BLEND_SRC_A, RS_BLEND_SRC_RGB, RS_DESIRED } from \"./state.js\";\n\n/**\n * Supported blend presets. Values mirror Babylon's `Constants.ALPHA_*` so the\n * raw Babylon integers can be passed straight through.\n */\nexport const GLBlendMode = {\n /** No blending — `gl.disable(gl.BLEND)`. (`Constants.ALPHA_DISABLE`) */\n DISABLE: 0,\n /** Additive — `blendFuncSeparate(SRC_ALPHA, ONE, ZERO, ONE)`. (`Constants.ALPHA_ADD`) */\n ADD: 1,\n /** Standard (non-premultiplied) alpha — `blendFuncSeparate(SRC_ALPHA, ONE_MINUS_SRC_ALPHA, ONE, ONE)`. (`Constants.ALPHA_COMBINE`) */\n ALPHA: 2,\n /** Premultiplied alpha — `blendFuncSeparate(ONE, ONE_MINUS_SRC_ALPHA, ONE, ONE)`. (`Constants.ALPHA_PREMULTIPLIED`) */\n PREMULTIPLIED: 7,\n} as const;\n\n/** One of the {@link GLBlendMode} preset values (`0`, `1`, `2` or `7`). */\nexport type GLBlendMode = (typeof GLBlendMode)[keyof typeof GLBlendMode];\n\n/**\n * Blend equation presets — the values WebGL2 accepts for\n * `gl.blendEquationSeparate`. Numeric values equal the GL enums so raw GL\n * integers (or Babylon's identical `Constants.GL_ALPHA_EQUATION_*`) pass\n * straight through.\n */\nexport const GLBlendEquation = {\n /** `src + dst` (the GL default). */\n ADD: 0x8006,\n /** `src - dst`. */\n SUBTRACT: 0x800a,\n /** `dst - src`. */\n REVERSE_SUBTRACT: 0x800b,\n /** `min(src, dst)`. */\n MIN: 0x8007,\n /** `max(src, dst)`. */\n MAX: 0x8008,\n} as const;\n\n/** One of the {@link GLBlendEquation} preset values. */\nexport type GLBlendEquation = (typeof GLBlendEquation)[keyof typeof GLBlendEquation];\n\n/**\n * Arbitrary separate-channel blend configuration — the lite-gl equivalent of\n * Babylon's `AlphaState.setAlphaBlendFunctionParameters` +\n * `setAlphaEquationParameters`. All factor / equation fields are raw WebGL2\n * enums (`gl.ONE`, `gl.SRC_ALPHA`, `gl.MIN`, …); use {@link GLBlendEquation} for\n * the equations if you prefer named presets.\n */\nexport interface GLBlendState {\n /** RGB source factor (`gl.blendFuncSeparate` arg 1). */\n srcRGB: GLenum;\n /** RGB destination factor (`gl.blendFuncSeparate` arg 2). */\n dstRGB: GLenum;\n /** Alpha source factor (`gl.blendFuncSeparate` arg 3). */\n srcAlpha: GLenum;\n /** Alpha destination factor (`gl.blendFuncSeparate` arg 4). */\n dstAlpha: GLenum;\n /** RGB blend equation. Defaults to `FUNC_ADD`. */\n equationRGB?: GLenum;\n /** Alpha blend equation. Defaults to `FUNC_ADD`. */\n equationAlpha?: GLenum;\n}\n\n/** GL `FUNC_ADD` — the implicit equation used by the {@link GLBlendMode}\n * presets (matching Babylon's `setAlphaMode`, which leaves it at the default). */\nconst FUNC_ADD = 0x8006;\n/** GL `gl.BLEND`. */\nconst BLEND = 0x0be2;\n\n/**\n * Set the GL blend state to match Babylon's `setAlphaMode(mode)` exactly.\n *\n * | Mode | `gl.blendFuncSeparate(srcRGB, dstRGB, srcA, dstA)` |\n * |--------------------|------------------------------------------------------------|\n * | `DISABLE` (0) | — (`gl.disable(gl.BLEND)`) |\n * | `ADD` (1) | `SRC_ALPHA, ONE, ZERO, ONE` |\n * | `ALPHA` (2) | `SRC_ALPHA, ONE_MINUS_SRC_ALPHA, ONE, ONE` |\n * | `PREMULTIPLIED` (7)| `ONE, ONE_MINUS_SRC_ALPHA, ONE, ONE` |\n *\n * No-op when the context is lost or disposed.\n *\n * @param engine - The engine whose GL blend state is updated.\n * @param mode - The {@link GLBlendMode} preset to apply.\n */\nexport function setBlendMode(engine: GLEngineContext, mode: GLBlendMode): void {\n if (engine._isLost || engine._disposed) {\n return;\n }\n const gl = engine.gl;\n switch (mode) {\n case GLBlendMode.DISABLE:\n disableBlend(engine);\n return;\n case GLBlendMode.ADD:\n applyBlend(engine, gl.SRC_ALPHA, gl.ONE, gl.ZERO, gl.ONE, FUNC_ADD, FUNC_ADD);\n return;\n case GLBlendMode.ALPHA:\n applyBlend(engine, gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE, FUNC_ADD, FUNC_ADD);\n return;\n case GLBlendMode.PREMULTIPLIED:\n applyBlend(engine, gl.ONE, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE, FUNC_ADD, FUNC_ADD);\n return;\n }\n}\n\n/**\n * Enable blending with an arbitrary separate-channel function and equation —\n * the lite-gl equivalent of Babylon's `AlphaState` with\n * `setAlphaBlendFunctionParameters` + `setAlphaEquationParameters`.\n *\n * Supports every WebGL2 blend equation, including `MIN`, `MAX`,\n * `FUNC_SUBTRACT` and `FUNC_REVERSE_SUBTRACT` (used by ShapeBuilder's darken /\n * cutout blend modes). Records the desired config (flushed by `applyGLStates`\n * before the next draw); at flush time the enable flag, the equation and the func\n * are each cached independently, so a redundant state is fully elided and only\n * the call whose params changed is re-issued — mirroring Babylon's `AlphaState`\n * dirty flags. Because GL keeps the equation + func across `gl.disable(BLEND)`,\n * re-enabling with unchanged params re-issues neither.\n *\n * No-op when the context is lost or disposed.\n *\n * @param engine - The engine whose desired blend state is updated.\n * @param state - The separate-channel blend factors + equations to apply.\n */\nexport function setBlendState(engine: GLEngineContext, state: GLBlendState): void {\n if (engine._isLost || engine._disposed) {\n return;\n }\n applyBlend(engine, state.srcRGB, state.dstRGB, state.srcAlpha, state.dstAlpha, state.equationRGB ?? FUNC_ADD, state.equationAlpha ?? FUNC_ADD);\n}\n\n/**\n * Disable blending — the equivalent of Babylon's `AlphaState.alphaBlend = false`.\n * Records the desired \"blend off\" state (flushed by `applyGLStates` before the\n * next draw); the actual `gl.disable(gl.BLEND)` is elided when blending is\n * already off at flush time. No-op when the context is lost or disposed.\n *\n * @param engine - The engine whose desired blend state is updated.\n */\nexport function disableBlend(engine: GLEngineContext): void {\n if (engine._isLost || engine._disposed) {\n return;\n }\n const s = engine._state;\n s.rs[RS_BLEND_ENABLED + RS_DESIRED] = 0;\n s._flushBlend = flushBlend;\n s.statesDirty = true;\n}\n\n/* ──────────────────────────── internal apply ──────────────────────────── */\n\n/** Record a granular blend config into the DESIRED (`rs[RS_* + RS_DESIRED]`)\n * slots and mark it dirty. No `gl.*` here — `applyGLStates` reconciles\n * desired→actual at the next flush, caching the enable flag, equation and func\n * independently so each is re-issued only when its params change (and never on a\n * bare re-enable). */\nfunction applyBlend(engine: GLEngineContext, srcRGB: number, dstRGB: number, srcAlpha: number, dstAlpha: number, eqRGB: number, eqAlpha: number): void {\n const rs = engine._state.rs;\n rs[RS_BLEND_ENABLED + RS_DESIRED] = 1;\n rs[RS_BLEND_SRC_RGB + RS_DESIRED] = srcRGB;\n rs[RS_BLEND_DST_RGB + RS_DESIRED] = dstRGB;\n rs[RS_BLEND_SRC_A + RS_DESIRED] = srcAlpha;\n rs[RS_BLEND_DST_A + RS_DESIRED] = dstAlpha;\n rs[RS_BLEND_EQ_RGB + RS_DESIRED] = eqRGB;\n rs[RS_BLEND_EQ_A + RS_DESIRED] = eqAlpha;\n engine._state._flushBlend = flushBlend;\n engine._state.statesDirty = true;\n}\n\n/**\n * Reconcile the deferred blend state DESIRED → ACTUAL — the per-category half of\n * Babylon's `_alphaState.apply`. Installed onto `_state._flushBlend` by the blend\n * setters and dispatched by {@link applyGLStates}; co-located here so a scene that\n * never sets a blend mode tree-shakes both this reconciler and its GL code out of\n * the bundle.\n *\n * The enable flag, the equation and the func are reconciled as THREE independent\n * cached sub-states (mirroring Babylon's `_AlphaState` `_isAlphaBlendDirty` /\n * `_isBlendEquationParametersDirty` / `_isBlendFunctionParametersDirty`). GL\n * retains the blend equation + func across `gl.disable(BLEND)`, so their cached\n * actual values stay valid while blending is off — re-enabling with unchanged\n * params therefore issues ONLY `gl.enable(BLEND)` and re-issues neither\n * `blendEquationSeparate` nor `blendFuncSeparate`. A desired `-1` (never\n * requested) leaves GL untouched. @internal\n */\nfunction flushBlend(engine: GLEngineContext): void {\n const gl = engine.gl;\n const rs = engine._state.rs;\n const dBlend = rs[RS_BLEND_ENABLED + RS_DESIRED]!;\n if (dBlend === -1) {\n return;\n }\n // 1. enable / disable — independent of the func + equation cache below.\n if (dBlend !== rs[RS_BLEND_ENABLED]) {\n rs[RS_BLEND_ENABLED] = dBlend;\n if (dBlend === 1) {\n gl.enable(BLEND);\n } else {\n gl.disable(BLEND);\n }\n }\n // While blending is off the equation/func are not observable and GL keeps the\n // last values, so leave their cache untouched (Babylon does the same) — the\n // next enable with identical params then re-issues nothing.\n if (dBlend !== 1) {\n return;\n }\n // 2. equation — re-issued only when the params actually changed.\n const eqRGB = rs[RS_BLEND_EQ_RGB + RS_DESIRED]!;\n const eqA = rs[RS_BLEND_EQ_A + RS_DESIRED]!;\n if (rs[RS_BLEND_EQ_RGB] !== eqRGB || rs[RS_BLEND_EQ_A] !== eqA) {\n gl.blendEquationSeparate(eqRGB, eqA);\n rs[RS_BLEND_EQ_RGB] = eqRGB;\n rs[RS_BLEND_EQ_A] = eqA;\n }\n // 3. func — re-issued only when the params actually changed.\n const srcRGB = rs[RS_BLEND_SRC_RGB + RS_DESIRED]!;\n const dstRGB = rs[RS_BLEND_DST_RGB + RS_DESIRED]!;\n const srcA = rs[RS_BLEND_SRC_A + RS_DESIRED]!;\n const dstA = rs[RS_BLEND_DST_A + RS_DESIRED]!;\n 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) {\n gl.blendFuncSeparate(srcRGB, dstRGB, srcA, dstA);\n rs[RS_BLEND_SRC_RGB] = srcRGB;\n rs[RS_BLEND_DST_RGB] = dstRGB;\n rs[RS_BLEND_SRC_A] = srcA;\n rs[RS_BLEND_DST_A] = dstA;\n }\n}\n"]}
package/context.d.ts ADDED
@@ -0,0 +1,130 @@
1
+ /** Constructor options forwarded to `canvas.getContext('webgl2', …)`. */
2
+ export interface GLEngineOptions {
3
+ /** Default: true. */
4
+ alpha?: boolean;
5
+ /** Default: true. */
6
+ premultipliedAlpha?: boolean;
7
+ /** Default: false. */
8
+ antialias?: boolean;
9
+ /** Default: false. */
10
+ preserveDrawingBuffer?: boolean;
11
+ /** Default: false — disabled for fullscreen-quad workloads. */
12
+ depth?: boolean;
13
+ /** Default: false. */
14
+ stencil?: boolean;
15
+ /** Default: "default". */
16
+ powerPreference?: WebGLPowerPreference;
17
+ /** Default: false. */
18
+ failIfMajorPerformanceCaveat?: boolean;
19
+ }
20
+ /** Read-only WebGL2 capability limits, queried once at context creation. */
21
+ export interface GLEngineCaps {
22
+ /** `gl.MAX_TEXTURE_SIZE` — largest supported texture dimension, in texels. */
23
+ readonly maxTextureSize: number;
24
+ /** `gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS` — number of sampler binding slots. */
25
+ readonly maxTextureUnits: number;
26
+ /** The `KHR_parallel_shader_compile` extension used for async link polling,
27
+ * or null when unsupported — linking is then treated as synchronous. */
28
+ readonly parallelShaderCompile: {
29
+ COMPLETION_STATUS_KHR: number;
30
+ } | null;
31
+ /** True when 32-bit float color attachments are renderable
32
+ * (`EXT_color_buffer_float`). Mirrors Babylon's `caps.textureFloatRender`. */
33
+ readonly textureFloatRender: boolean;
34
+ /** True when 32-bit float textures support linear filtering
35
+ * (`OES_texture_float_linear`). Mirrors `caps.textureFloatLinearFiltering`. */
36
+ readonly textureFloatLinearFiltering: boolean;
37
+ /** True when 16-bit half-float color attachments are renderable
38
+ * (`EXT_color_buffer_float` or `EXT_color_buffer_half_float`). Mirrors
39
+ * `caps.textureHalfFloatRender`. */
40
+ readonly textureHalfFloatRender: boolean;
41
+ /** Half-float linear filtering — always `true` in WebGL2 (it is core).
42
+ * Kept as a field to mirror Babylon's `caps.textureHalfFloatLinearFiltering`. */
43
+ readonly textureHalfFloatLinearFiltering: boolean;
44
+ /** Whether non-power-of-two textures need POT dimensions for mips / wrap.
45
+ * Always `false` in WebGL2 (NPOT is core). Mirrors `engine.needPOTTextures`. */
46
+ readonly needPOTTextures: boolean;
47
+ }
48
+ /**
49
+ * Pure-state handle for a WebGL2 canvas + its cached GL state.
50
+ *
51
+ * INVARIANT: consumers MUST NOT mutate GL state directly through `engine.gl`.
52
+ * Doing so silently corrupts the cache in `_state`. The package owns every
53
+ * GL call. (`engine.gl` is exposed only so downstream code that already has the
54
+ * pattern of poking `engine._gl.getExtension(...)` can do that, but must NOT
55
+ * call `bindTexture`/`useProgram`/`bindBuffer`/`viewport`/etc.)
56
+ */
57
+ export interface GLEngineContext {
58
+ /** The canvas the WebGL2 context was acquired from. An `OffscreenCanvas` is
59
+ * supported for worker render paths (e.g. the Lottie player); it has no CSS
60
+ * box, so it must be sized explicitly via `setGLEngineSize` rather than the
61
+ * CSS-derived `resizeGLEngine`. */
62
+ readonly canvas: HTMLCanvasElement | OffscreenCanvas;
63
+ /** The raw WebGL2 context. Do NOT mutate GL state through it — see the
64
+ * type-level invariant above; the package owns every state-changing call. */
65
+ readonly gl: WebGL2RenderingContext;
66
+ /** Queried capability limits for this context. */
67
+ readonly caps: GLEngineCaps;
68
+ }
69
+ /** Acquire a WebGL2 context on the canvas and build the pure-state handle.
70
+ * Accepts an `HTMLCanvasElement` or an `OffscreenCanvas` (worker render paths —
71
+ * e.g. the Lottie player — which size the drawing buffer via `setGLEngineSize`).
72
+ * Throws if WebGL2 is unsupported. */
73
+ export declare function createGLEngine(canvas: HTMLCanvasElement | OffscreenCanvas, options?: GLEngineOptions): GLEngineContext;
74
+ /** Stops the render loop, removes DOM listeners, releases all known effects
75
+ * and textures, then marks the context disposed. The browser-owned canvas
76
+ * is left intact. */
77
+ export declare function disposeGLEngine(engine: GLEngineContext): void;
78
+ /** Match drawing-buffer size to (clientSize × devicePixelRatio / _hsl). No-op
79
+ * if size already matches. Never touches viewport — `setViewport` owns that. */
80
+ export declare function resizeGLEngine(engine: GLEngineContext): void;
81
+ /**
82
+ * Set the drawing-buffer size to an EXPLICIT width/height in physical pixels
83
+ * (each divided by `_hsl`), independent of the canvas client/CSS size — the
84
+ * counterpart of Babylon's `ThinEngine.setSize`. Use when the render resolution
85
+ * is computed directly (e.g. animation dimensions × scale × dpr) rather than
86
+ * derived from CSS layout via {@link resizeGLEngine}. No-op on a disposed
87
+ * engine. Never touches the viewport — `setViewport` owns that.
88
+ *
89
+ * @param engine - The engine to resize.
90
+ * @param width - Target drawing-buffer width in physical pixels.
91
+ * @param height - Target drawing-buffer height in physical pixels.
92
+ */
93
+ export declare function setGLEngineSize(engine: GLEngineContext, width: number, height: number): void;
94
+ /**
95
+ * Invalidate lite-gl's cached GL state so the next state-setting call in each
96
+ * category (program / buffer / texture / VAO / framebuffer bindings, blend,
97
+ * depth, stencil, scissor, color-mask, viewport, unpack) is re-issued instead
98
+ * of elided.
99
+ *
100
+ * Use this when a host application that SHARES this WebGL2 context mutates raw
101
+ * `gl.*` state outside lite-gl — e.g. a save/restore wrapper that resets the
102
+ * context to GL defaults around each render scope, or any interop layer that
103
+ * issues GL calls directly. Without it, lite-gl's redundant-call elision would
104
+ * skip the state changes needed to re-establish the scope, producing corrupted
105
+ * output. Owned GPU resources (the shared quad, render targets, meshes,
106
+ * effects, textures) are preserved — only the cached "current GL state" is
107
+ * reset. Mirrors Babylon's `Engine.wipeCaches()`.
108
+ *
109
+ * No-op while the context is lost or the engine is disposed.
110
+ */
111
+ export declare function wipeGLStateCache(engine: GLEngineContext): void;
112
+ /** Drawing-buffer width in physical pixels (`canvas.width`). */
113
+ export declare function getRenderWidth(engine: GLEngineContext): number;
114
+ /** Drawing-buffer height in physical pixels (`canvas.height`). */
115
+ export declare function getRenderHeight(engine: GLEngineContext): number;
116
+ /** Current hardware-scaling factor — drawing-buffer = clientSize × dpr / level. */
117
+ export declare function getHardwareScalingLevel(engine: GLEngineContext): number;
118
+ /** Updates the hardware-scaling factor and triggers a resize. */
119
+ export declare function setHardwareScalingLevel(engine: GLEngineContext, level: number): void;
120
+ /** The backing canvas element (an `HTMLCanvasElement`, or an `OffscreenCanvas`
121
+ * for worker render paths). */
122
+ export declare function getRenderingCanvas(engine: GLEngineContext): HTMLCanvasElement | OffscreenCanvas;
123
+ /** Register a `webglcontextlost` callback. Duplicate registrations are ignored. */
124
+ export declare function onContextLost(engine: GLEngineContext, cb: () => void): void;
125
+ /** Remove a previously-registered context-lost callback. */
126
+ export declare function offContextLost(engine: GLEngineContext, cb: () => void): void;
127
+ /** Register a `webglcontextrestored` callback. Duplicate registrations are ignored. */
128
+ export declare function onContextRestored(engine: GLEngineContext, cb: () => void): void;
129
+ /** Remove a previously-registered context-restored callback. */
130
+ export declare function offContextRestored(engine: GLEngineContext, cb: () => void): void;