@babylonjs/lite-gl 0.1.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.
package/README.md ADDED
@@ -0,0 +1,135 @@
1
+ # @babylonjs/lite-gl
2
+
3
+ A tiny, **function-based, tree-shakeable WebGL2 micro-engine** for fullscreen
4
+ shader effects, sprites and dynamic textures — the WebGL counterpart of
5
+ [`@babylonjs/lite`](https://github.com/BabylonJS/Babylon-Lite). No classes, no
6
+ scene graph: you call plain functions against an opaque `GLEngineContext`, so a
7
+ bundler keeps only what you import.
8
+
9
+ It is a focused subset of Babylon.js' rendering primitives, validated to render
10
+ **near-identically** (within ±1–2 LSB ANGLE / SwiftShader codegen noise) to
11
+ Babylon's `ThinEngine` / `EffectRenderer` / `SpriteRenderer` / `HtmlElementTexture`
12
+ path — every feature has a side-by-side parity scene in `tests/gl/parity/` (and,
13
+ downstream, the NeonBrush effect suite). Swapping `@babylonjs/core` for lite-gl
14
+ typically shrinks an effect's shipped bundle **~10–16×** (≈4–6 KB gzip vs
15
+ ≈40–80 KB).
16
+
17
+ > **WebGL2 only.** The context is created with `canvas.getContext("webgl2")`.
18
+
19
+ ## Install
20
+
21
+ ```bash
22
+ npm install @babylonjs/lite-gl
23
+ ```
24
+
25
+ ## Quick start — an animated fullscreen effect
26
+
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";
39
+
40
+ const canvas = document.getElementById("renderCanvas") as HTMLCanvasElement;
41
+ const engine = createGLEngine(canvas, { alpha: false });
42
+
43
+ // The wrapper compiles + owns the effect. `vertexSource` defaults to a built-in
44
+ // fullscreen-quad shader (exposing a `vUv` varying), so only `fragmentSource`
45
+ // is required.
46
+ const wrapper = createEffectWrapper(engine, {
47
+ name: "gradient",
48
+ fragmentSource: `#version 300 es
49
+ precision highp float;
50
+ in vec2 vUv;
51
+ out vec4 glFragColor;
52
+ uniform float uTime;
53
+ void main() {
54
+ glFragColor = vec4(0.5 + 0.5 * cos(uTime + vUv.xyx + vec3(0.0, 2.0, 4.0)), 1.0);
55
+ }`,
56
+ uniformNames: ["uTime"],
57
+ });
58
+
59
+ const start = performance.now();
60
+ runRenderLoop(engine, () => {
61
+ if (!isEffectReady(engine, wrapper.effect)) return; // shaders compile async
62
+ resizeGLEngine(engine);
63
+ setViewport(engine);
64
+ applyEffectWrapper(wrapper);
65
+ setEffectFloat(engine, wrapper.effect, "uTime", (performance.now() - start) / 1000);
66
+ drawEffect(engine);
67
+ });
68
+ ```
69
+
70
+ ## Entry points
71
+
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`). |
89
+
90
+ ### Core API (`@babylonjs/lite-gl`)
91
+
92
+ - **Engine / lifecycle** — `createGLEngine`, `disposeGLEngine`, `resizeGLEngine`,
93
+ `getRenderWidth`, `getRenderHeight`, `get/setHardwareScalingLevel`,
94
+ `getRenderingCanvas`, and `on/offContextLost` + `on/offContextRestored`
95
+ (context-loss is handled: effects and textures are rebuilt on restore).
96
+ - **Render loop** — `runRenderLoop`, `stopRenderLoop`.
97
+ - **Effects** — `createEffect`, `isEffectReady`, `executeWhenCompiled`,
98
+ `useEffect`, `disposeEffect`, and the cached uniform setters
99
+ `setEffectFloat` / `…Float2` / `…Float3` / `…Float4` / `…Int` /
100
+ `…Color3` / `…Color4` / `…Texture`.
101
+ - **Fullscreen renderer** — `createEffectWrapper`, `applyEffectWrapper`,
102
+ `drawEffect`, `setViewport`, `disposeEffectWrapper`.
103
+ - **Textures** — `createRawTexture` (typed-array upload, LDR byte formats),
104
+ `createFloatTexture` (float / half-float HDR opt-in), `generateTextureMipMaps`,
105
+ `loadTexture2D` (async URL upload with a 1×1 placeholder), `bindTexture`,
106
+ `disposeTexture`, plus `updateRawTexture`, `updateTextureSamplingMode`,
107
+ `updateTextureWrapMode`, `createTextureFromHandle`.
108
+ - **Dynamic textures** — `createDynamicTexture`, `updateDynamicTexture`,
109
+ `clearDynamicTextureSource` (the retained source is replayed on context-restore).
110
+ Also at `@babylonjs/lite-gl/dynamic-texture`.
111
+ - **Render targets** — `createRenderTarget` (RGBA8 FBO with a sampleable color
112
+ `GLTexture` + optional depth / stencil renderbuffer), `createFloatRenderTarget`
113
+ (float / half-float HDR opt-in), `bindRenderTarget` (cached, null = the canvas),
114
+ `resizeRenderTarget`, `generateRenderTargetMipMaps`, `readRenderTargetPixels`,
115
+ `disposeRenderTarget`, plus the `createPingPong` / `resizePingPong` /
116
+ `disposePingPong` feedback helper. Also at `@babylonjs/lite-gl/render-target`.
117
+ - **Meshes / buffers** — `createVertexBuffer`, `updateVertexBuffer`,
118
+ `createIndexBuffer`, `bindAttributes`, `drawIndexed` (instanced), `disposeBuffer`.
119
+ Also at `@babylonjs/lite-gl/mesh`.
120
+ - **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`).
124
+ - **Blend** — `setBlendMode` + `GLBlendMode` (`DISABLE` / `ADD` / `ALPHA` /
125
+ `PREMULTIPLIED`), matching Babylon's `setAlphaMode` parameters.
126
+
127
+ ## Demos
128
+
129
+ Runnable scenes for every feature live in the repo's GL **lab**
130
+ (`lab/gl/`) — fullscreen effects, textures, sprites, blend modes,
131
+ HTML-element textures and render-to-texture round-trips.
132
+
133
+ ## License
134
+
135
+ Apache-2.0
@@ -0,0 +1,231 @@
1
+ /**
2
+ * Clear the currently-bound framebuffer's color / depth / stencil buffers — the
3
+ * lite-gl equivalent of Babylon's `clear(color, backBuffer, depth, stencil)`.
4
+ * Depth/stencil clears respect the current write masks (set them first via
5
+ * {@link setDepthState} / {@link setStencilState}). No-op when nothing is
6
+ * requested or the context is lost/disposed.
7
+ *
8
+ * @param engine - The engine.
9
+ * @param options - Which buffers to clear (and the color value).
10
+ */
11
+ export declare function clearEngine(engine: GLEngineContext, options: GLClearOptions): void;
12
+
13
+ /**
14
+ * Opt-in: give a `/render-target` {@link GLRenderTarget} a stencil attachment,
15
+ * replacing the core's depth-only `DEPTH_COMPONENT16` renderbuffer with either a
16
+ * packed **`DEPTH24_STENCIL8`** buffer (default — depth *and* stencil) or a
17
+ * stencil-only **`STENCIL_INDEX8`** buffer.
18
+ *
19
+ * Stencil is intentionally NOT a {@link createRenderTarget} option: keeping this
20
+ * helper in the `/depth-stencil` sub-entry means the stencil/packed renderbuffer
21
+ * code tree-shakes out of every bundle that only needs a color (and optional
22
+ * depth) target.
23
+ *
24
+ * The attachment is **restore-correct**: it is rebuilt automatically — at the new
25
+ * size on {@link resizeRenderTarget}, and into the fresh framebuffer after a
26
+ * `webglcontextrestored` event — so the stencil survives for the life of the
27
+ * target, and {@link disposeRenderTarget} releases it along with the target.
28
+ *
29
+ * No-op on a lost/disposed context or a disposed target.
30
+ *
31
+ * @param engine - The engine that owns `rt`.
32
+ * @param rt - The render target to attach the stencil buffer to.
33
+ * @param options - `depth` (default `true`): when `true` the attachment is a
34
+ * packed depth+stencil buffer (`DEPTH24_STENCIL8` on `DEPTH_STENCIL_ATTACHMENT`)
35
+ * — the common case, and the correct choice when the target was created with
36
+ * `generateDepthBuffer: true`. When `false` the attachment is stencil-only
37
+ * (`STENCIL_INDEX8` on `STENCIL_ATTACHMENT`).
38
+ * @throws If a renderbuffer handle could not be allocated or the framebuffer is
39
+ * incomplete after attaching.
40
+ */
41
+ export declare function generateRenderTargetStencil(engine: GLEngineContext, rt: GLRenderTarget, options?: {
42
+ depth?: boolean;
43
+ }): void;
44
+
45
+ /** Options for {@link clearEngine}. */
46
+ export declare interface GLClearOptions {
47
+ /** When set, clears the color buffer to this RGBA color (alpha default 1). */
48
+ color?: {
49
+ r: number;
50
+ g: number;
51
+ b: number;
52
+ a?: number;
53
+ };
54
+ /** Clear the depth buffer (respects the current depth write mask). */
55
+ depth?: boolean;
56
+ /** Clear the stencil buffer (respects the current stencil write mask). */
57
+ stencil?: boolean;
58
+ }
59
+
60
+ /** Depth-buffer configuration for {@link setDepthState}. Omitted fields are
61
+ * left unchanged. */
62
+ export declare interface GLDepthState {
63
+ /** Enable/disable the depth test (`gl.enable/disable(DEPTH_TEST)`). */
64
+ test?: boolean;
65
+ /** Enable/disable depth writes (`gl.depthMask`). */
66
+ write?: boolean;
67
+ /** Depth comparison function (`gl.depthFunc`), e.g. `gl.LESS`. */
68
+ func?: GLenum;
69
+ }
70
+
71
+ /** Read-only WebGL2 capability limits, queried once at context creation. */
72
+ declare interface GLEngineCaps {
73
+ /** `gl.MAX_TEXTURE_SIZE` — largest supported texture dimension, in texels. */
74
+ readonly maxTextureSize: number;
75
+ /** `gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS` — number of sampler binding slots. */
76
+ readonly maxTextureUnits: number;
77
+ /** The `KHR_parallel_shader_compile` extension used for async link polling,
78
+ * or null when unsupported — linking is then treated as synchronous. */
79
+ readonly parallelShaderCompile: {
80
+ COMPLETION_STATUS_KHR: number;
81
+ } | null;
82
+ /** True when 32-bit float color attachments are renderable
83
+ * (`EXT_color_buffer_float`). Mirrors Babylon's `caps.textureFloatRender`. */
84
+ readonly textureFloatRender: boolean;
85
+ /** True when 32-bit float textures support linear filtering
86
+ * (`OES_texture_float_linear`). Mirrors `caps.textureFloatLinearFiltering`. */
87
+ readonly textureFloatLinearFiltering: boolean;
88
+ /** True when 16-bit half-float color attachments are renderable
89
+ * (`EXT_color_buffer_float` or `EXT_color_buffer_half_float`). Mirrors
90
+ * `caps.textureHalfFloatRender`. */
91
+ readonly textureHalfFloatRender: boolean;
92
+ /** Half-float linear filtering — always `true` in WebGL2 (it is core).
93
+ * Kept as a field to mirror Babylon's `caps.textureHalfFloatLinearFiltering`. */
94
+ readonly textureHalfFloatLinearFiltering: boolean;
95
+ /** Whether non-power-of-two textures need POT dimensions for mips / wrap.
96
+ * Always `false` in WebGL2 (NPOT is core). Mirrors `engine.needPOTTextures`. */
97
+ readonly needPOTTextures: boolean;
98
+ }
99
+
100
+ /**
101
+ * Pure-state handle for a WebGL2 canvas + its cached GL state.
102
+ *
103
+ * INVARIANT: consumers MUST NOT mutate GL state directly through `engine.gl`.
104
+ * Doing so silently corrupts the cache in `_state`. The package owns every
105
+ * GL call. (`engine.gl` is exposed only so downstream code that already has the
106
+ * pattern of poking `engine._gl.getExtension(...)` can do that, but must NOT
107
+ * call `bindTexture`/`useProgram`/`bindBuffer`/`viewport`/etc.)
108
+ */
109
+ declare interface GLEngineContext {
110
+ /** The canvas the WebGL2 context was acquired from. An `OffscreenCanvas` is
111
+ * supported for worker render paths (e.g. the Lottie player); it has no CSS
112
+ * box, so it must be sized explicitly via `setGLEngineSize` rather than the
113
+ * CSS-derived `resizeGLEngine`. */
114
+ readonly canvas: HTMLCanvasElement | OffscreenCanvas;
115
+ /** The raw WebGL2 context. Do NOT mutate GL state through it — see the
116
+ * type-level invariant above; the package owns every state-changing call. */
117
+ readonly gl: WebGL2RenderingContext;
118
+ /** Queried capability limits for this context. */
119
+ readonly caps: GLEngineCaps;
120
+
121
+ /**
122
+ * An offscreen render target — a `WebGLFramebuffer` wrapping a color
123
+ * {@link GLTexture} and an optional depth / stencil renderbuffer. The lite-gl
124
+ * counterpart of Babylon's `RenderTargetWrapper`.
125
+ */
126
+ declare interface GLRenderTarget {
127
+ /** The color attachment, sampleable like any other {@link GLTexture}
128
+ * (`setEffectTexture` / `bindTexture`). For an owned attachment its handle
129
+ * is swapped on `webglcontextrestored` while consumers keep this same
130
+ * reference. */
131
+ texture: GLTexture_2;
132
+ /** Color attachment width in texels. */
133
+ width: number;
134
+ /** Color attachment height in texels. */
135
+ height: number;
136
+ /** True once the color attachment + framebuffer are allocated. */
137
+ isReady: boolean;
138
+
139
+ /** Stencil configuration for {@link setStencilState}. Omitted fields are left
140
+ * unchanged. The `func`/`ref`/`funcMask` triple and the
141
+ * `opFail`/`opZFail`/`opZPass` triple are each applied as a unit (any member
142
+ * present re-issues that GL call, merging the unspecified members from cache). */
143
+ export declare interface GLStencilState {
144
+ /** Enable/disable the stencil test (`gl.enable/disable(STENCIL_TEST)`). */
145
+ test?: boolean;
146
+ /** Stencil write mask (`gl.stencilMask`). */
147
+ mask?: number;
148
+ /** Comparison function (`gl.stencilFunc` arg 1), e.g. `gl.ALWAYS`. */
149
+ func?: GLenum;
150
+ /** Reference value (`gl.stencilFunc` arg 2). */
151
+ ref?: number;
152
+ /** Comparison mask (`gl.stencilFunc` arg 3). */
153
+ funcMask?: number;
154
+ /** Op when the stencil test fails (`gl.stencilOp` arg 1). */
155
+ opFail?: GLenum;
156
+ /** Op when the stencil test passes but depth fails (`gl.stencilOp` arg 2). */
157
+ opZFail?: GLenum;
158
+ /** Op when both stencil and depth pass (`gl.stencilOp` arg 3). */
159
+ opZPass?: GLenum;
160
+ }
161
+
162
+ /**
163
+ * Pure-state texture handle. The `handle` field is MUTABLE so the same logical
164
+ * texture survives a `webglcontextrestored` event — every consumer keeps the
165
+ * same `GLTexture` reference; only the internal `WebGLTexture` is swapped.
166
+ *
167
+ * `loadTexture2D` also uses the same handle for the 1×1 placeholder upload AND
168
+ * the final image upload — so a `bindTexture(engine, unit, tex)` made before the
169
+ * image has decoded remains valid once the image arrives.
170
+ */
171
+ declare interface GLTexture_2 {
172
+ /** The live `WebGLTexture`. MUTABLE — swapped for a fresh handle on
173
+ * `webglcontextrestored` while consumers keep the same `GLTexture` reference. */
174
+ handle: WebGLTexture;
175
+ /** GL texture target (always `gl.TEXTURE_2D` for this package). */
176
+ readonly target: GLenum;
177
+ /** Texture width in texels. Updated once an async upload resolves. */
178
+ width: number;
179
+ /** Texture height in texels. Updated once an async upload resolves. */
180
+ height: number;
181
+ /** True when the texture is safe to sample with final content (placeholders
182
+ * read as not-ready until their image/upload completes). */
183
+ isReady: boolean;
184
+
185
+ /**
186
+ * Buffer the color write mask into the DESIRED state — the lite-gl equivalent of
187
+ * Babylon's `setColorWrite` (which passes the same flag to all four channels).
188
+ * Flushed to GL (`gl.colorMask`) by `applyGLStates` before the next draw /
189
+ * clear.
190
+ *
191
+ * @param engine - The engine.
192
+ * @param r - Write red.
193
+ * @param g - Write green.
194
+ * @param b - Write blue.
195
+ * @param a - Write alpha.
196
+ */
197
+ export declare function setColorMask(engine: GLEngineContext, r: boolean, g: boolean, b: boolean, a: boolean): void;
198
+
199
+ /**
200
+ * Enable/disable face culling and (optionally) set the cull face — the lite-gl
201
+ * equivalent of `engine.depthCullingState.cull` + `cullFace`.
202
+ *
203
+ * @param engine - The engine.
204
+ * @param enabled - Enable (`true`) or disable (`false`) `gl.CULL_FACE`.
205
+ * @param face - Optional cull face (`gl.BACK` / `gl.FRONT` / `gl.FRONT_AND_BACK`).
206
+ */
207
+ export declare function setCullState(engine: GLEngineContext, enabled: boolean, face?: GLenum): void;
208
+
209
+ /**
210
+ * Buffer depth-buffer state (test enable, write mask, comparison function) into
211
+ * the DESIRED state — the lite-gl equivalent of mutating Babylon's
212
+ * `engine.depthCullingState.{depthTest,depthMask,depthFunc}`. Flushed to GL by
213
+ * `applyGLStates` before the next draw / clear; omitted fields are untouched.
214
+ *
215
+ * @param engine - The engine.
216
+ * @param state - The depth fields to change. Omitted fields are untouched.
217
+ */
218
+ export declare function setDepthState(engine: GLEngineContext, state: GLDepthState): void;
219
+
220
+ /**
221
+ * Buffer stencil state (test enable, write mask, comparison func triple, op
222
+ * triple) into the DESIRED state — the lite-gl equivalent of mutating Babylon's
223
+ * `engine.stencilState.*`. Flushed by `applyGLStates` before the next draw /
224
+ * clear; omitted fields are untouched (merge-from-desired).
225
+ *
226
+ * @param engine - The engine.
227
+ * @param state - The stencil fields to change. Omitted fields are untouched.
228
+ */
229
+ export declare function setStencilState(engine: GLEngineContext, state: GLStencilState): void;
230
+
231
+ export { }
@@ -0,0 +1,277 @@
1
+ import { R as RS_DEPTH_TEST, b as RS_DESIRED, c as RS_DEPTH_MASK, d as RS_DEPTH_FUNC, e as RS_CULL_ENABLED, f as RS_CULL_FACE, g as RS_STENCIL_TEST, h as RS_STENCIL_MASK, i as RS_STENCIL_FUNC_FUNC, j as RS_STENCIL_FUNC_REF, k as RS_STENCIL_FUNC_MASK, l as RS_STENCIL_OP_FAIL, m as RS_STENCIL_OP_ZFAIL, n as RS_STENCIL_OP_ZPASS, o as RS_COLOR_MASK, p as RS_CLEAR_R, q as RS_CLEAR_G, r as RS_CLEAR_B, s as RS_CLEAR_A, a as applyGLStates } from "./state--j_ncWIi.js";
2
+ const COLOR_BUFFER_BIT = 16384;
3
+ const DEPTH_BUFFER_BIT = 256;
4
+ const STENCIL_BUFFER_BIT = 1024;
5
+ const DEPTH_TEST = 2929;
6
+ const CULL_FACE = 2884;
7
+ const STENCIL_TEST = 2960;
8
+ const FRAMEBUFFER = 36160;
9
+ const RENDERBUFFER = 36161;
10
+ const DEPTH24_STENCIL8 = 35056;
11
+ const STENCIL_INDEX8 = 36168;
12
+ const DEPTH_STENCIL_ATTACHMENT = 33306;
13
+ const STENCIL_ATTACHMENT = 36128;
14
+ const DEPTH_ATTACHMENT = 36096;
15
+ const FRAMEBUFFER_COMPLETE = 36053;
16
+ function setDepthState(engine, state) {
17
+ if (engine._isLost || engine._disposed) {
18
+ return;
19
+ }
20
+ const s = engine._state;
21
+ if (state.test !== void 0) {
22
+ s.rs[RS_DEPTH_TEST + RS_DESIRED] = state.test ? 1 : 0;
23
+ }
24
+ if (state.write !== void 0) {
25
+ s.rs[RS_DEPTH_MASK + RS_DESIRED] = state.write ? 1 : 0;
26
+ }
27
+ if (state.func !== void 0) {
28
+ s.rs[RS_DEPTH_FUNC + RS_DESIRED] = state.func;
29
+ }
30
+ s._flushDepthCull = flushDepthCull;
31
+ s.statesDirty = true;
32
+ }
33
+ function setCullState(engine, enabled, face) {
34
+ if (engine._isLost || engine._disposed) {
35
+ return;
36
+ }
37
+ const s = engine._state;
38
+ s.rs[RS_CULL_ENABLED + RS_DESIRED] = enabled ? 1 : 0;
39
+ if (face !== void 0) {
40
+ s.rs[RS_CULL_FACE + RS_DESIRED] = face;
41
+ }
42
+ s._flushDepthCull = flushDepthCull;
43
+ s.statesDirty = true;
44
+ }
45
+ function setStencilState(engine, state) {
46
+ if (engine._isLost || engine._disposed) {
47
+ return;
48
+ }
49
+ const s = engine._state;
50
+ if (state.test !== void 0) {
51
+ s.rs[RS_STENCIL_TEST + RS_DESIRED] = state.test ? 1 : 0;
52
+ }
53
+ if (state.mask !== void 0) {
54
+ s.rs[RS_STENCIL_MASK + RS_DESIRED] = state.mask;
55
+ }
56
+ if (state.func !== void 0) {
57
+ s.rs[RS_STENCIL_FUNC_FUNC + RS_DESIRED] = state.func;
58
+ }
59
+ if (state.ref !== void 0) {
60
+ s.rs[RS_STENCIL_FUNC_REF + RS_DESIRED] = state.ref;
61
+ }
62
+ if (state.funcMask !== void 0) {
63
+ s.rs[RS_STENCIL_FUNC_MASK + RS_DESIRED] = state.funcMask;
64
+ }
65
+ if (state.opFail !== void 0) {
66
+ s.rs[RS_STENCIL_OP_FAIL + RS_DESIRED] = state.opFail;
67
+ }
68
+ if (state.opZFail !== void 0) {
69
+ s.rs[RS_STENCIL_OP_ZFAIL + RS_DESIRED] = state.opZFail;
70
+ }
71
+ if (state.opZPass !== void 0) {
72
+ s.rs[RS_STENCIL_OP_ZPASS + RS_DESIRED] = state.opZPass;
73
+ }
74
+ s._flushStencil = flushStencil;
75
+ s.statesDirty = true;
76
+ }
77
+ function setColorMask(engine, r, g, b, a) {
78
+ if (engine._isLost || engine._disposed) {
79
+ return;
80
+ }
81
+ const packed = (r ? 8 : 0) | (g ? 4 : 0) | (b ? 2 : 0) | (a ? 1 : 0);
82
+ const s = engine._state;
83
+ s.rs[RS_COLOR_MASK + RS_DESIRED] = packed;
84
+ s._flushColorMask = flushColorMask;
85
+ s.statesDirty = true;
86
+ }
87
+ function clearEngine(engine, options) {
88
+ if (engine._isLost || engine._disposed) {
89
+ return;
90
+ }
91
+ const gl = engine.gl;
92
+ let mask = 0;
93
+ if (options.color !== void 0) {
94
+ const c = options.color;
95
+ const a = c.a ?? 1;
96
+ const rs = engine._state.rs;
97
+ if (rs[RS_CLEAR_R] !== c.r || rs[RS_CLEAR_G] !== c.g || rs[RS_CLEAR_B] !== c.b || rs[RS_CLEAR_A] !== a) {
98
+ gl.clearColor(c.r, c.g, c.b, a);
99
+ rs[RS_CLEAR_R] = c.r;
100
+ rs[RS_CLEAR_G] = c.g;
101
+ rs[RS_CLEAR_B] = c.b;
102
+ rs[RS_CLEAR_A] = a;
103
+ }
104
+ mask |= COLOR_BUFFER_BIT;
105
+ }
106
+ if (options.depth === true) {
107
+ mask |= DEPTH_BUFFER_BIT;
108
+ }
109
+ if (options.stencil === true) {
110
+ mask |= STENCIL_BUFFER_BIT;
111
+ }
112
+ if (mask !== 0) {
113
+ applyGLStates(engine);
114
+ gl.clear(mask);
115
+ }
116
+ }
117
+ function flushDepthCull(engine) {
118
+ const gl = engine.gl;
119
+ const rs = engine._state.rs;
120
+ const dTest = rs[RS_DEPTH_TEST + RS_DESIRED];
121
+ if (dTest !== rs[RS_DEPTH_TEST]) {
122
+ rs[RS_DEPTH_TEST] = dTest;
123
+ if (dTest === 1) {
124
+ gl.enable(DEPTH_TEST);
125
+ } else {
126
+ gl.disable(DEPTH_TEST);
127
+ }
128
+ }
129
+ const dMask = rs[RS_DEPTH_MASK + RS_DESIRED];
130
+ if (dMask !== rs[RS_DEPTH_MASK]) {
131
+ rs[RS_DEPTH_MASK] = dMask;
132
+ gl.depthMask(dMask === 1);
133
+ }
134
+ const dFunc = rs[RS_DEPTH_FUNC + RS_DESIRED];
135
+ if (dFunc !== rs[RS_DEPTH_FUNC]) {
136
+ rs[RS_DEPTH_FUNC] = dFunc;
137
+ gl.depthFunc(dFunc);
138
+ }
139
+ const dCull = rs[RS_CULL_ENABLED + RS_DESIRED];
140
+ if (dCull !== rs[RS_CULL_ENABLED]) {
141
+ rs[RS_CULL_ENABLED] = dCull;
142
+ if (dCull === 1) {
143
+ gl.enable(CULL_FACE);
144
+ } else {
145
+ gl.disable(CULL_FACE);
146
+ }
147
+ }
148
+ const dCullFace = rs[RS_CULL_FACE + RS_DESIRED];
149
+ if (dCullFace !== rs[RS_CULL_FACE]) {
150
+ rs[RS_CULL_FACE] = dCullFace;
151
+ gl.cullFace(dCullFace);
152
+ }
153
+ }
154
+ function flushStencil(engine) {
155
+ const gl = engine.gl;
156
+ const rs = engine._state.rs;
157
+ const dTest = rs[RS_STENCIL_TEST + RS_DESIRED];
158
+ if (dTest !== rs[RS_STENCIL_TEST]) {
159
+ rs[RS_STENCIL_TEST] = dTest;
160
+ if (dTest === 1) {
161
+ gl.enable(STENCIL_TEST);
162
+ } else {
163
+ gl.disable(STENCIL_TEST);
164
+ }
165
+ }
166
+ const dMask = rs[RS_STENCIL_MASK + RS_DESIRED];
167
+ if (dMask !== rs[RS_STENCIL_MASK]) {
168
+ rs[RS_STENCIL_MASK] = dMask;
169
+ gl.stencilMask(dMask);
170
+ }
171
+ const dFuncFunc = rs[RS_STENCIL_FUNC_FUNC + RS_DESIRED];
172
+ const dFuncRef = rs[RS_STENCIL_FUNC_REF + RS_DESIRED];
173
+ const dFuncMask = rs[RS_STENCIL_FUNC_MASK + RS_DESIRED];
174
+ if (dFuncFunc !== rs[RS_STENCIL_FUNC_FUNC] || dFuncRef !== rs[RS_STENCIL_FUNC_REF] || dFuncMask !== rs[RS_STENCIL_FUNC_MASK]) {
175
+ rs[RS_STENCIL_FUNC_FUNC] = dFuncFunc;
176
+ rs[RS_STENCIL_FUNC_REF] = dFuncRef;
177
+ rs[RS_STENCIL_FUNC_MASK] = dFuncMask;
178
+ gl.stencilFunc(dFuncFunc, dFuncRef, dFuncMask);
179
+ }
180
+ const dOpFail = rs[RS_STENCIL_OP_FAIL + RS_DESIRED];
181
+ const dOpZFail = rs[RS_STENCIL_OP_ZFAIL + RS_DESIRED];
182
+ const dOpZPass = rs[RS_STENCIL_OP_ZPASS + RS_DESIRED];
183
+ if (dOpFail !== rs[RS_STENCIL_OP_FAIL] || dOpZFail !== rs[RS_STENCIL_OP_ZFAIL] || dOpZPass !== rs[RS_STENCIL_OP_ZPASS]) {
184
+ rs[RS_STENCIL_OP_FAIL] = dOpFail;
185
+ rs[RS_STENCIL_OP_ZFAIL] = dOpZFail;
186
+ rs[RS_STENCIL_OP_ZPASS] = dOpZPass;
187
+ gl.stencilOp(dOpFail, dOpZFail, dOpZPass);
188
+ }
189
+ }
190
+ function flushColorMask(engine) {
191
+ const rs = engine._state.rs;
192
+ const dColorMask = rs[RS_COLOR_MASK + RS_DESIRED];
193
+ if (dColorMask !== rs[RS_COLOR_MASK]) {
194
+ rs[RS_COLOR_MASK] = dColorMask;
195
+ engine.gl.colorMask((dColorMask & 8) !== 0, (dColorMask & 4) !== 0, (dColorMask & 2) !== 0, (dColorMask & 1) !== 0);
196
+ }
197
+ }
198
+ function generateRenderTargetStencil(engine, rt, options) {
199
+ if (engine._isLost || engine._disposed || rt._disposed) {
200
+ return;
201
+ }
202
+ const packDepth = (options == null ? void 0 : options.depth) ?? true;
203
+ const attachment = packDepth ? DEPTH_STENCIL_ATTACHMENT : STENCIL_ATTACHMENT;
204
+ const format = packDepth ? DEPTH24_STENCIL8 : STENCIL_INDEX8;
205
+ const build = (e) => {
206
+ const gl = e.gl;
207
+ const prevFb = e._state.boundFramebuffer;
208
+ const newRb = gl.createRenderbuffer();
209
+ if (newRb === null) {
210
+ throw new Error("lite-gl: gl.createRenderbuffer returned null (render target stencil)");
211
+ }
212
+ let committed = false;
213
+ try {
214
+ gl.bindFramebuffer(FRAMEBUFFER, rt._framebuffer);
215
+ e._state.boundFramebuffer = rt._framebuffer;
216
+ gl.bindRenderbuffer(RENDERBUFFER, newRb);
217
+ gl.renderbufferStorage(RENDERBUFFER, format, rt.width, rt.height);
218
+ gl.framebufferRenderbuffer(FRAMEBUFFER, attachment, RENDERBUFFER, newRb);
219
+ gl.bindRenderbuffer(RENDERBUFFER, null);
220
+ const status = gl.checkFramebufferStatus(FRAMEBUFFER);
221
+ if (status !== FRAMEBUFFER_COMPLETE) {
222
+ throw new Error(`lite-gl: render target framebuffer incomplete after stencil attach (status 0x${status.toString(16)})`);
223
+ }
224
+ if (rt._depthStencil !== null) {
225
+ gl.deleteRenderbuffer(rt._depthStencil);
226
+ }
227
+ rt._depthStencil = newRb;
228
+ committed = true;
229
+ } finally {
230
+ if (!committed) {
231
+ gl.framebufferRenderbuffer(FRAMEBUFFER, attachment, RENDERBUFFER, null);
232
+ gl.deleteRenderbuffer(newRb);
233
+ }
234
+ if (e._state.boundFramebuffer !== prevFb) {
235
+ gl.bindFramebuffer(FRAMEBUFFER, prevFb);
236
+ e._state.boundFramebuffer = prevFb;
237
+ }
238
+ }
239
+ };
240
+ const prevHook = rt._rebuildDepthStencil;
241
+ const prevDepthStencil = rt._depthStencil;
242
+ try {
243
+ build(engine);
244
+ } catch (err) {
245
+ rt._rebuildDepthStencil = prevHook;
246
+ try {
247
+ if (prevHook !== void 0) {
248
+ prevHook(engine);
249
+ } else if (prevDepthStencil !== null) {
250
+ reattachCoreDepthBuffer(engine, rt, prevDepthStencil);
251
+ }
252
+ } catch {
253
+ }
254
+ throw err;
255
+ }
256
+ rt._rebuildDepthStencil = build;
257
+ }
258
+ function reattachCoreDepthBuffer(engine, rt, depthBuffer) {
259
+ const gl = engine.gl;
260
+ const prevFb = engine._state.boundFramebuffer;
261
+ gl.bindFramebuffer(FRAMEBUFFER, rt._framebuffer);
262
+ engine._state.boundFramebuffer = rt._framebuffer;
263
+ gl.framebufferRenderbuffer(FRAMEBUFFER, DEPTH_ATTACHMENT, RENDERBUFFER, depthBuffer);
264
+ if (engine._state.boundFramebuffer !== prevFb) {
265
+ gl.bindFramebuffer(FRAMEBUFFER, prevFb);
266
+ engine._state.boundFramebuffer = prevFb;
267
+ }
268
+ }
269
+ export {
270
+ clearEngine,
271
+ generateRenderTargetStencil,
272
+ setColorMask,
273
+ setCullState,
274
+ setDepthState,
275
+ setStencilState
276
+ };
277
+ //# sourceMappingURL=depth-stencil.js.map