@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/depth-stencil.js CHANGED
@@ -1,277 +1,414 @@
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;
1
+ import { applyGLStates } from "./apply-states.js";
2
+ import { RS_CLEAR_A, RS_CLEAR_B, RS_CLEAR_G, RS_CLEAR_R, RS_COLOR_MASK, RS_CULL_ENABLED, RS_CULL_FACE, RS_DEPTH_FUNC, RS_DEPTH_MASK, RS_DEPTH_TEST, RS_DESIRED, RS_STENCIL_FUNC_FUNC, RS_STENCIL_FUNC_MASK, RS_STENCIL_FUNC_REF, RS_STENCIL_MASK, RS_STENCIL_OP_FAIL, RS_STENCIL_OP_ZFAIL, RS_STENCIL_OP_ZPASS, RS_STENCIL_TEST, } from "./state.js";
3
+ // ── Clear bits (used by `clearEngine`).
4
+ /** GL clear bits. */
5
+ const COLOR_BUFFER_BIT = 0x4000;
6
+ const DEPTH_BUFFER_BIT = 0x0100;
7
+ const STENCIL_BUFFER_BIT = 0x0400;
8
+ // ── Test-enable enums for the per-category reconcilers below.
9
+ /** GL `gl.DEPTH_TEST`. */
10
+ const DEPTH_TEST = 0x0b71;
11
+ /** GL `gl.CULL_FACE`. */
12
+ const CULL_FACE = 0x0b44;
13
+ /** GL `gl.STENCIL_TEST`. */
14
+ const STENCIL_TEST = 0x0b90;
15
+ // ── Framebuffer / renderbuffer enums (used only by generateRenderTargetStencil).
16
+ // Module-local consts mirror render-target.ts's constant style. Because the
17
+ // render-target core no longer references the stencil/packed enums, they live
18
+ // here and tree-shake away for consumers that never opt into a stencil buffer.
19
+ /** GL `gl.FRAMEBUFFER`. */
20
+ const FRAMEBUFFER = 0x8d40;
21
+ /** GL `gl.RENDERBUFFER`. */
22
+ const RENDERBUFFER = 0x8d41;
23
+ /** GL `gl.DEPTH24_STENCIL8` — packed depth+stencil sized format. */
24
+ const DEPTH24_STENCIL8 = 0x88f0;
25
+ /** GL `gl.STENCIL_INDEX8` stencil-only sized format. */
26
+ const STENCIL_INDEX8 = 0x8d48;
27
+ /** GL `gl.DEPTH_STENCIL_ATTACHMENT`. */
28
+ const DEPTH_STENCIL_ATTACHMENT = 0x821a;
29
+ /** GL `gl.STENCIL_ATTACHMENT`. */
30
+ const STENCIL_ATTACHMENT = 0x8d20;
31
+ /** GL `gl.DEPTH_ATTACHMENT` where the render-target core attaches its depth-only
32
+ * renderbuffer (re-established when rolling back a failed stencil attach). */
33
+ const DEPTH_ATTACHMENT = 0x8d00;
34
+ /** GL `gl.FRAMEBUFFER_COMPLETE`. */
35
+ const FRAMEBUFFER_COMPLETE = 0x8cd5;
36
+ /**
37
+ * Buffer depth-buffer state (test enable, write mask, comparison function) into
38
+ * the DESIRED state — the lite-gl equivalent of mutating Babylon's
39
+ * `engine.depthCullingState.{depthTest,depthMask,depthFunc}`. Flushed to GL by
40
+ * `applyGLStates` before the next draw / clear; omitted fields are untouched.
41
+ *
42
+ * @param engine - The engine.
43
+ * @param state - The depth fields to change. Omitted fields are untouched.
44
+ */
45
+ export function setDepthState(engine, state) {
46
+ if (engine._isLost || engine._disposed) {
47
+ return;
48
+ }
49
+ const s = engine._state;
50
+ if (state.test !== undefined) {
51
+ s.rs[RS_DEPTH_TEST + RS_DESIRED] = state.test ? 1 : 0;
52
+ }
53
+ if (state.write !== undefined) {
54
+ s.rs[RS_DEPTH_MASK + RS_DESIRED] = state.write ? 1 : 0;
55
+ }
56
+ if (state.func !== undefined) {
57
+ s.rs[RS_DEPTH_FUNC + RS_DESIRED] = state.func;
58
+ }
59
+ s._flushDepthCull = flushDepthCull;
60
+ s.statesDirty = true;
32
61
  }
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;
62
+ /**
63
+ * Enable/disable face culling and (optionally) set the cull face — the lite-gl
64
+ * equivalent of `engine.depthCullingState.cull` + `cullFace`.
65
+ *
66
+ * @param engine - The engine.
67
+ * @param enabled - Enable (`true`) or disable (`false`) `gl.CULL_FACE`.
68
+ * @param face - Optional cull face (`gl.BACK` / `gl.FRONT` / `gl.FRONT_AND_BACK`).
69
+ */
70
+ export function setCullState(engine, enabled, face) {
71
+ if (engine._isLost || engine._disposed) {
72
+ return;
73
+ }
74
+ const s = engine._state;
75
+ s.rs[RS_CULL_ENABLED + RS_DESIRED] = enabled ? 1 : 0;
76
+ if (face !== undefined) {
77
+ s.rs[RS_CULL_FACE + RS_DESIRED] = face;
78
+ }
79
+ s._flushDepthCull = flushDepthCull;
80
+ s.statesDirty = true;
44
81
  }
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;
82
+ /**
83
+ * Buffer stencil state (test enable, write mask, comparison func triple, op
84
+ * triple) into the DESIRED state — the lite-gl equivalent of mutating Babylon's
85
+ * `engine.stencilState.*`. Flushed by `applyGLStates` before the next draw /
86
+ * clear; omitted fields are untouched (merge-from-desired).
87
+ *
88
+ * @param engine - The engine.
89
+ * @param state - The stencil fields to change. Omitted fields are untouched.
90
+ */
91
+ export function setStencilState(engine, state) {
92
+ if (engine._isLost || engine._disposed) {
93
+ return;
94
+ }
95
+ const s = engine._state;
96
+ if (state.test !== undefined) {
97
+ s.rs[RS_STENCIL_TEST + RS_DESIRED] = state.test ? 1 : 0;
98
+ }
99
+ if (state.mask !== undefined) {
100
+ s.rs[RS_STENCIL_MASK + RS_DESIRED] = state.mask;
101
+ }
102
+ if (state.func !== undefined) {
103
+ s.rs[RS_STENCIL_FUNC_FUNC + RS_DESIRED] = state.func;
104
+ }
105
+ if (state.ref !== undefined) {
106
+ s.rs[RS_STENCIL_FUNC_REF + RS_DESIRED] = state.ref;
107
+ }
108
+ if (state.funcMask !== undefined) {
109
+ s.rs[RS_STENCIL_FUNC_MASK + RS_DESIRED] = state.funcMask;
110
+ }
111
+ if (state.opFail !== undefined) {
112
+ s.rs[RS_STENCIL_OP_FAIL + RS_DESIRED] = state.opFail;
113
+ }
114
+ if (state.opZFail !== undefined) {
115
+ s.rs[RS_STENCIL_OP_ZFAIL + RS_DESIRED] = state.opZFail;
116
+ }
117
+ if (state.opZPass !== undefined) {
118
+ s.rs[RS_STENCIL_OP_ZPASS + RS_DESIRED] = state.opZPass;
119
+ }
120
+ s._flushStencil = flushStencil;
121
+ s.statesDirty = true;
76
122
  }
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;
123
+ /**
124
+ * Buffer the color write mask into the DESIRED state — the lite-gl equivalent of
125
+ * Babylon's `setColorWrite` (which passes the same flag to all four channels).
126
+ * Flushed to GL (`gl.colorMask`) by `applyGLStates` before the next draw /
127
+ * clear.
128
+ *
129
+ * @param engine - The engine.
130
+ * @param r - Write red.
131
+ * @param g - Write green.
132
+ * @param b - Write blue.
133
+ * @param a - Write alpha.
134
+ */
135
+ export function setColorMask(engine, r, g, b, a) {
136
+ if (engine._isLost || engine._disposed) {
137
+ return;
138
+ }
139
+ const packed = (r ? 8 : 0) | (g ? 4 : 0) | (b ? 2 : 0) | (a ? 1 : 0);
140
+ const s = engine._state;
141
+ s.rs[RS_COLOR_MASK + RS_DESIRED] = packed;
142
+ s._flushColorMask = flushColorMask;
143
+ s.statesDirty = true;
86
144
  }
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
- }
145
+ /**
146
+ * Clear the currently-bound framebuffer's color / depth / stencil buffers — the
147
+ * lite-gl equivalent of Babylon's `clear(color, backBuffer, depth, stencil)`.
148
+ * Depth/stencil clears respect the current write masks (set them first via
149
+ * {@link setDepthState} / {@link setStencilState}). No-op when nothing is
150
+ * requested or the context is lost/disposed.
151
+ *
152
+ * @param engine - The engine.
153
+ * @param options - Which buffers to clear (and the color value).
154
+ */
155
+ export function clearEngine(engine, options) {
156
+ if (engine._isLost || engine._disposed) {
157
+ return;
158
+ }
159
+ const gl = engine.gl;
160
+ let mask = 0;
161
+ if (options.color !== undefined) {
162
+ const c = options.color;
163
+ const a = c.a ?? 1;
164
+ const rs = engine._state.rs;
165
+ // Cached: gl.clearColor is per-context state GL retains, so only re-issue
166
+ // it when the requested color actually changes (Babylon re-sets it every
167
+ // clear; this elides the redundant JS↔native call for constant backgrounds).
168
+ if (rs[RS_CLEAR_R] !== c.r || rs[RS_CLEAR_G] !== c.g || rs[RS_CLEAR_B] !== c.b || rs[RS_CLEAR_A] !== a) {
169
+ gl.clearColor(c.r, c.g, c.b, a);
170
+ rs[RS_CLEAR_R] = c.r;
171
+ rs[RS_CLEAR_G] = c.g;
172
+ rs[RS_CLEAR_B] = c.b;
173
+ rs[RS_CLEAR_A] = a;
174
+ }
175
+ mask |= COLOR_BUFFER_BIT;
176
+ }
177
+ if (options.depth === true) {
178
+ mask |= DEPTH_BUFFER_BIT;
179
+ }
180
+ if (options.stencil === true) {
181
+ mask |= STENCIL_BUFFER_BIT;
182
+ }
183
+ if (mask !== 0) {
184
+ // Babylon parity: a clear respects the current depth/stencil/color write
185
+ // masks, so flush any deferred state before clearing.
186
+ applyGLStates(engine);
187
+ gl.clear(mask);
188
+ }
116
189
  }
190
+ /* ─────────────────── deferred render-state reconcilers ──────────────────────
191
+ * The per-category half of `applyGLStates`. Each setter above installs the
192
+ * matching reconciler onto its `_state._flush*` slot; `applyGLStates`
193
+ * (apply-states.ts) dispatches ONLY through those slots. Co-locating them with
194
+ * their setters makes each one reachable solely when its setter is in the bundle,
195
+ * so a scene that never touches depth/cull, stencil, or color-mask tree-shakes
196
+ * the corresponding reconciler — and its GL code — away entirely. Each reconciles
197
+ * its DESIRED (`rs[RS_X + RS_DESIRED]`) slots against the ACTUAL twins (`rs[RS_X]`)
198
+ * and issues only the `gl.*` calls that changed, in the same order and with the
199
+ * same elision rules as the former monolithic flush. */
200
+ /** Reconcile depth + cull (Babylon's `_depthCullingState.apply`). @internal */
117
201
  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
- }
202
+ const gl = engine.gl;
203
+ const rs = engine._state.rs;
204
+ const dTest = rs[RS_DEPTH_TEST + RS_DESIRED];
205
+ if (dTest !== rs[RS_DEPTH_TEST]) {
206
+ rs[RS_DEPTH_TEST] = dTest;
207
+ if (dTest === 1) {
208
+ gl.enable(DEPTH_TEST);
209
+ }
210
+ else {
211
+ gl.disable(DEPTH_TEST);
212
+ }
213
+ }
214
+ const dMask = rs[RS_DEPTH_MASK + RS_DESIRED];
215
+ if (dMask !== rs[RS_DEPTH_MASK]) {
216
+ rs[RS_DEPTH_MASK] = dMask;
217
+ gl.depthMask(dMask === 1);
218
+ }
219
+ const dFunc = rs[RS_DEPTH_FUNC + RS_DESIRED];
220
+ if (dFunc !== rs[RS_DEPTH_FUNC]) {
221
+ rs[RS_DEPTH_FUNC] = dFunc;
222
+ gl.depthFunc(dFunc);
223
+ }
224
+ const dCull = rs[RS_CULL_ENABLED + RS_DESIRED];
225
+ if (dCull !== rs[RS_CULL_ENABLED]) {
226
+ rs[RS_CULL_ENABLED] = dCull;
227
+ if (dCull === 1) {
228
+ gl.enable(CULL_FACE);
229
+ }
230
+ else {
231
+ gl.disable(CULL_FACE);
232
+ }
233
+ }
234
+ const dCullFace = rs[RS_CULL_FACE + RS_DESIRED];
235
+ if (dCullFace !== rs[RS_CULL_FACE]) {
236
+ rs[RS_CULL_FACE] = dCullFace;
237
+ gl.cullFace(dCullFace);
238
+ }
153
239
  }
240
+ /** Reconcile the stencil test / mask / func-triple / op-triple (Babylon's
241
+ * `_stencilState.apply`); each triple is issued as a unit. @internal */
154
242
  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
- }
243
+ const gl = engine.gl;
244
+ const rs = engine._state.rs;
245
+ const dTest = rs[RS_STENCIL_TEST + RS_DESIRED];
246
+ if (dTest !== rs[RS_STENCIL_TEST]) {
247
+ rs[RS_STENCIL_TEST] = dTest;
248
+ if (dTest === 1) {
249
+ gl.enable(STENCIL_TEST);
250
+ }
251
+ else {
252
+ gl.disable(STENCIL_TEST);
253
+ }
254
+ }
255
+ const dMask = rs[RS_STENCIL_MASK + RS_DESIRED];
256
+ if (dMask !== rs[RS_STENCIL_MASK]) {
257
+ rs[RS_STENCIL_MASK] = dMask;
258
+ gl.stencilMask(dMask);
259
+ }
260
+ const dFuncFunc = rs[RS_STENCIL_FUNC_FUNC + RS_DESIRED];
261
+ const dFuncRef = rs[RS_STENCIL_FUNC_REF + RS_DESIRED];
262
+ const dFuncMask = rs[RS_STENCIL_FUNC_MASK + RS_DESIRED];
263
+ if (dFuncFunc !== rs[RS_STENCIL_FUNC_FUNC] || dFuncRef !== rs[RS_STENCIL_FUNC_REF] || dFuncMask !== rs[RS_STENCIL_FUNC_MASK]) {
264
+ rs[RS_STENCIL_FUNC_FUNC] = dFuncFunc;
265
+ rs[RS_STENCIL_FUNC_REF] = dFuncRef;
266
+ rs[RS_STENCIL_FUNC_MASK] = dFuncMask;
267
+ gl.stencilFunc(dFuncFunc, dFuncRef, dFuncMask);
268
+ }
269
+ const dOpFail = rs[RS_STENCIL_OP_FAIL + RS_DESIRED];
270
+ const dOpZFail = rs[RS_STENCIL_OP_ZFAIL + RS_DESIRED];
271
+ const dOpZPass = rs[RS_STENCIL_OP_ZPASS + RS_DESIRED];
272
+ if (dOpFail !== rs[RS_STENCIL_OP_FAIL] || dOpZFail !== rs[RS_STENCIL_OP_ZFAIL] || dOpZPass !== rs[RS_STENCIL_OP_ZPASS]) {
273
+ rs[RS_STENCIL_OP_FAIL] = dOpFail;
274
+ rs[RS_STENCIL_OP_ZFAIL] = dOpZFail;
275
+ rs[RS_STENCIL_OP_ZPASS] = dOpZPass;
276
+ gl.stencilOp(dOpFail, dOpZFail, dOpZPass);
277
+ }
189
278
  }
279
+ /** Reconcile the packed color-write mask (Babylon's `setColorWrite`). @internal */
190
280
  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
- }
281
+ const rs = engine._state.rs;
282
+ const dColorMask = rs[RS_COLOR_MASK + RS_DESIRED];
283
+ if (dColorMask !== rs[RS_COLOR_MASK]) {
284
+ rs[RS_COLOR_MASK] = dColorMask;
285
+ engine.gl.colorMask((dColorMask & 8) !== 0, (dColorMask & 4) !== 0, (dColorMask & 2) !== 0, (dColorMask & 1) !== 0);
286
+ }
197
287
  }
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;
288
+ /**
289
+ * Opt-in: give a `/render-target` {@link GLRenderTarget} a stencil attachment,
290
+ * replacing the core's depth-only `DEPTH_COMPONENT16` renderbuffer with either a
291
+ * packed **`DEPTH24_STENCIL8`** buffer (default — depth *and* stencil) or a
292
+ * stencil-only **`STENCIL_INDEX8`** buffer.
293
+ *
294
+ * Stencil is intentionally NOT a {@link createRenderTarget} option: keeping this
295
+ * helper in the depth-stencil module means the stencil/packed renderbuffer
296
+ * code tree-shakes out of every bundle that only needs a color (and optional
297
+ * depth) target.
298
+ *
299
+ * The attachment is **restore-correct**: it is rebuilt automatically — at the new
300
+ * size on {@link resizeRenderTarget}, and into the fresh framebuffer after a
301
+ * `webglcontextrestored` event — so the stencil survives for the life of the
302
+ * target, and {@link disposeRenderTarget} releases it along with the target.
303
+ *
304
+ * No-op on a lost/disposed context or a disposed target.
305
+ *
306
+ * @param engine - The engine that owns `rt`.
307
+ * @param rt - The render target to attach the stencil buffer to.
308
+ * @param options - `depth` (default `true`): when `true` the attachment is a
309
+ * packed depth+stencil buffer (`DEPTH24_STENCIL8` on `DEPTH_STENCIL_ATTACHMENT`)
310
+ * — the common case, and the correct choice when the target was created with
311
+ * `generateDepthBuffer: true`. When `false` the attachment is stencil-only
312
+ * (`STENCIL_INDEX8` on `STENCIL_ATTACHMENT`).
313
+ * @throws If a renderbuffer handle could not be allocated or the framebuffer is
314
+ * incomplete after attaching.
315
+ */
316
+ export function generateRenderTargetStencil(engine, rt, options) {
317
+ if (engine._isLost || engine._disposed || rt._disposed) {
318
+ return;
319
+ }
320
+ const packDepth = options?.depth ?? true;
321
+ const attachment = packDepth ? DEPTH_STENCIL_ATTACHMENT : STENCIL_ATTACHMENT;
322
+ const format = packDepth ? DEPTH24_STENCIL8 : STENCIL_INDEX8;
323
+ const build = (e) => {
324
+ const gl = e.gl;
325
+ // Capture the caller's draw target so this helper is STATE-NEUTRAL: it must
326
+ // not silently redirect subsequent draws to `rt` (during an internal
327
+ // rebuild `prevFb` is `rt._framebuffer`, which the core re-checks next).
328
+ const prevFb = e._state.boundFramebuffer;
329
+ const newRb = gl.createRenderbuffer();
330
+ if (newRb === null) {
331
+ throw new Error("lite-gl: gl.createRenderbuffer returned null (render target stencil)");
332
+ }
333
+ let committed = false;
334
+ try {
335
+ gl.bindFramebuffer(FRAMEBUFFER, rt._framebuffer);
336
+ e._state.boundFramebuffer = rt._framebuffer;
337
+ gl.bindRenderbuffer(RENDERBUFFER, newRb);
338
+ gl.renderbufferStorage(RENDERBUFFER, format, rt.width, rt.height);
339
+ gl.framebufferRenderbuffer(FRAMEBUFFER, attachment, RENDERBUFFER, newRb);
340
+ gl.bindRenderbuffer(RENDERBUFFER, null);
341
+ const status = gl.checkFramebufferStatus(FRAMEBUFFER);
342
+ if (status !== FRAMEBUFFER_COMPLETE) {
343
+ throw new Error(`lite-gl: render target framebuffer incomplete after stencil attach (status 0x${status.toString(16)})`);
344
+ }
345
+ // Commit only after a complete attachment: release the buffer we
346
+ // replaced (the core depth-only one, or our own from a prior rebuild).
347
+ if (rt._depthStencil !== null) {
348
+ gl.deleteRenderbuffer(rt._depthStencil);
349
+ }
350
+ rt._depthStencil = newRb;
351
+ committed = true;
352
+ }
353
+ finally {
354
+ if (!committed) {
355
+ // Any non-committed exit (incomplete framebuffer OR an unexpected
356
+ // GL throw): detach + delete the buffer we couldn't adopt so it
357
+ // never leaks, leaving the attachment point empty for the
358
+ // caller-level rollback to re-establish the prior buffer.
359
+ gl.framebufferRenderbuffer(FRAMEBUFFER, attachment, RENDERBUFFER, null);
360
+ gl.deleteRenderbuffer(newRb);
361
+ }
362
+ // Restore the caller's draw target + the bound-framebuffer cache.
363
+ if (e._state.boundFramebuffer !== prevFb) {
364
+ gl.bindFramebuffer(FRAMEBUFFER, prevFb);
365
+ e._state.boundFramebuffer = prevFb;
366
+ }
367
+ }
368
+ };
369
+ // Build once now, but COMMIT the resize/restore hook only if it succeeds — a
370
+ // failed opt-in must leave the target exactly as it was. The packed attach
371
+ // above can clear the core `DEPTH_ATTACHMENT`, so on failure restore the prior
372
+ // hook AND re-establish the prior depth/stencil attachment (the prior hook's,
373
+ // or the core depth-only buffer).
374
+ const prevHook = rt._rebuildDepthStencil;
375
+ const prevDepthStencil = rt._depthStencil;
246
376
  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;
377
+ build(engine);
378
+ }
379
+ catch (err) {
380
+ rt._rebuildDepthStencil = prevHook;
381
+ try {
382
+ if (prevHook !== undefined) {
383
+ prevHook(engine);
384
+ }
385
+ else if (prevDepthStencil !== null) {
386
+ reattachCoreDepthBuffer(engine, rt, prevDepthStencil);
387
+ }
388
+ }
389
+ catch {
390
+ // Best-effort restore; surface the original failure below.
391
+ }
392
+ throw err;
393
+ }
394
+ rt._rebuildDepthStencil = build;
257
395
  }
396
+ /**
397
+ * Re-attach a core depth-only renderbuffer at `DEPTH_ATTACHMENT` — used to roll a
398
+ * render target back when a packed {@link generateRenderTargetStencil} attach
399
+ * fails completeness (attaching at `DEPTH_STENCIL_ATTACHMENT` clears the core
400
+ * `DEPTH_ATTACHMENT`). State-neutral: restores the caller's bound framebuffer.
401
+ * @internal
402
+ */
258
403
  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
- }
404
+ const gl = engine.gl;
405
+ const prevFb = engine._state.boundFramebuffer;
406
+ gl.bindFramebuffer(FRAMEBUFFER, rt._framebuffer);
407
+ engine._state.boundFramebuffer = rt._framebuffer;
408
+ gl.framebufferRenderbuffer(FRAMEBUFFER, DEPTH_ATTACHMENT, RENDERBUFFER, depthBuffer);
409
+ if (engine._state.boundFramebuffer !== prevFb) {
410
+ gl.bindFramebuffer(FRAMEBUFFER, prevFb);
411
+ engine._state.boundFramebuffer = prevFb;
412
+ }
268
413
  }
269
- export {
270
- clearEngine,
271
- generateRenderTargetStencil,
272
- setColorMask,
273
- setCullState,
274
- setDepthState,
275
- setStencilState
276
- };
277
- //# sourceMappingURL=depth-stencil.js.map
414
+ //# sourceMappingURL=depth-stencil.js.map