@babylonjs/lite-gl 0.1.0 → 1.0.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 -35
  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 +190 -290
  37. package/render-target.js +459 -328
  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/mesh.js CHANGED
@@ -1,334 +1,459 @@
1
- import { I as getEffectAttributeLocation, m as onContextRestored, k as offContextRestored } from "./effect-BxxwfB_O.js";
2
- import { a as applyGLStates } from "./state--j_ncWIi.js";
3
- const ARRAY_BUFFER = 34962;
4
- const ELEMENT_ARRAY_BUFFER = 34963;
5
- const STATIC_DRAW = 35044;
6
- const DYNAMIC_DRAW = 35048;
7
- const FLOAT = 5126;
8
- const TRIANGLES = 4;
9
- const UNSIGNED_SHORT = 5123;
10
- const UNSIGNED_INT = 5125;
11
- function createVertexBuffer(engine, data, dynamic = false) {
12
- const gl = engine.gl;
13
- const handle = gl.createBuffer();
14
- if (handle === null) {
15
- throw new Error("lite-gl: gl.createBuffer returned null (vertex buffer)");
16
- }
17
- const vb = {
18
- handle,
19
- byteLength: data.byteLength,
20
- _data: data,
21
- _dynamic: dynamic,
22
- _disposed: false,
23
- _deleteGpu: () => {
24
- },
25
- _restore: () => {
26
- }
27
- };
28
- vb._deleteGpu = (g) => {
29
- g.deleteBuffer(vb.handle);
30
- };
31
- vb._restore = (target) => {
32
- const g = target.gl;
33
- const fresh = g.createBuffer();
34
- if (fresh === null) {
35
- return;
36
- }
37
- vb.handle = fresh;
38
- bindArrayBufferRaw(target, fresh);
39
- g.bufferData(ARRAY_BUFFER, vb._data, vb._dynamic ? DYNAMIC_DRAW : STATIC_DRAW);
40
- };
41
- bindArrayBufferRaw(engine, handle);
42
- gl.bufferData(ARRAY_BUFFER, data, dynamic ? DYNAMIC_DRAW : STATIC_DRAW);
43
- engine._buffers.push(vb);
44
- return vb;
1
+ import { onContextRestored, offContextRestored } from "./context.js";
2
+ import { getEffectAttributeLocation } from "./effect.js";
3
+ import { applyGLStates } from "./apply-states.js";
4
+ /** GL `gl.ARRAY_BUFFER`. */
5
+ const ARRAY_BUFFER = 0x8892;
6
+ /** GL `gl.ELEMENT_ARRAY_BUFFER`. */
7
+ const ELEMENT_ARRAY_BUFFER = 0x8893;
8
+ /** GL `gl.STATIC_DRAW`. */
9
+ const STATIC_DRAW = 0x88e4;
10
+ /** GL `gl.DYNAMIC_DRAW`. */
11
+ const DYNAMIC_DRAW = 0x88e8;
12
+ /** GL `gl.FLOAT`. */
13
+ const FLOAT = 0x1406;
14
+ /** GL `gl.TRIANGLES`. */
15
+ const TRIANGLES = 0x0004;
16
+ /** GL `gl.UNSIGNED_SHORT`. */
17
+ const UNSIGNED_SHORT = 0x1403;
18
+ /** GL `gl.UNSIGNED_INT`. */
19
+ const UNSIGNED_INT = 0x1405;
20
+ /* ──────────────────────────────── buffers ──────────────────────────────── */
21
+ /**
22
+ * Create a GPU vertex buffer from interleaved float data.
23
+ *
24
+ * @param engine - The engine.
25
+ * @param data - The vertex data. Retained by reference for context-restore — do
26
+ * not mutate it in place; use {@link updateVertexBuffer} to change contents.
27
+ * @param dynamic - Hint that the buffer will be updated frequently
28
+ * (`DYNAMIC_DRAW`). Default `false` (`STATIC_DRAW`).
29
+ * @returns The new {@link GLVertexBuffer}.
30
+ */
31
+ export function createVertexBuffer(engine, data, dynamic = false) {
32
+ const gl = engine.gl;
33
+ const handle = gl.createBuffer();
34
+ if (handle === null) {
35
+ throw new Error("lite-gl: gl.createBuffer returned null (vertex buffer)");
36
+ }
37
+ const vb = {
38
+ handle,
39
+ byteLength: data.byteLength,
40
+ _data: data,
41
+ _dynamic: dynamic,
42
+ _disposed: false,
43
+ _deleteGpu: () => { },
44
+ _restore: () => { },
45
+ };
46
+ vb._deleteGpu = (g) => {
47
+ g.deleteBuffer(vb.handle);
48
+ };
49
+ vb._restore = (target) => {
50
+ const g = target.gl;
51
+ const fresh = g.createBuffer();
52
+ if (fresh === null) {
53
+ return;
54
+ }
55
+ vb.handle = fresh;
56
+ bindArrayBufferRaw(target, fresh);
57
+ g.bufferData(ARRAY_BUFFER, vb._data, vb._dynamic ? DYNAMIC_DRAW : STATIC_DRAW);
58
+ };
59
+ bindArrayBufferRaw(engine, handle);
60
+ gl.bufferData(ARRAY_BUFFER, data, dynamic ? DYNAMIC_DRAW : STATIC_DRAW);
61
+ engine._buffers.push(vb);
62
+ return vb;
45
63
  }
46
- function updateVertexBuffer(engine, vb, data, dstByteOffset = 0) {
47
- if (engine._isLost || engine._disposed || vb._disposed) {
48
- return;
49
- }
50
- bindArrayBufferRaw(engine, vb.handle);
51
- engine.gl.bufferSubData(ARRAY_BUFFER, dstByteOffset, data);
52
- if (dstByteOffset === 0 && data.byteLength >= vb.byteLength) {
53
- vb._data = data;
54
- vb.byteLength = data.byteLength;
55
- }
64
+ /**
65
+ * Upload new contents into (part of) a vertex buffer via `bufferSubData`. A
66
+ * full-buffer update from offset 0 also refreshes the retained CPU data used
67
+ * for context-restore.
68
+ *
69
+ * @param engine - The engine.
70
+ * @param vb - The vertex buffer to update.
71
+ * @param data - The new float data.
72
+ * @param dstByteOffset - Destination byte offset within the buffer. Default 0.
73
+ */
74
+ export function updateVertexBuffer(engine, vb, data, dstByteOffset = 0) {
75
+ if (engine._isLost || engine._disposed || vb._disposed) {
76
+ return;
77
+ }
78
+ bindArrayBufferRaw(engine, vb.handle);
79
+ engine.gl.bufferSubData(ARRAY_BUFFER, dstByteOffset, data);
80
+ if (dstByteOffset === 0 && data.byteLength >= vb.byteLength) {
81
+ vb._data = data;
82
+ vb.byteLength = data.byteLength;
83
+ }
56
84
  }
57
- function createIndexBuffer(engine, data) {
58
- const gl = engine.gl;
59
- const handle = gl.createBuffer();
60
- if (handle === null) {
61
- throw new Error("lite-gl: gl.createBuffer returned null (index buffer)");
62
- }
63
- const is32 = data instanceof Uint32Array;
64
- const ib = {
65
- handle,
66
- count: data.length,
67
- is32Bits: is32,
68
- _data: data,
69
- _disposed: false,
70
- _deleteGpu: () => {
71
- },
72
- _restore: () => {
73
- }
74
- };
75
- ib._deleteGpu = (g) => {
76
- g.deleteBuffer(ib.handle);
77
- };
78
- ib._restore = (target) => {
79
- const g = target.gl;
80
- const fresh = g.createBuffer();
81
- if (fresh === null) {
82
- return;
83
- }
84
- ib.handle = fresh;
85
- bindDefaultVao(target);
86
- g.bindBuffer(ELEMENT_ARRAY_BUFFER, fresh);
87
- target._state.boundElementBuffer = fresh;
88
- g.bufferData(ELEMENT_ARRAY_BUFFER, ib._data, STATIC_DRAW);
89
- };
90
- bindDefaultVao(engine);
91
- gl.bindBuffer(ELEMENT_ARRAY_BUFFER, handle);
92
- engine._state.boundElementBuffer = handle;
93
- gl.bufferData(ELEMENT_ARRAY_BUFFER, data, STATIC_DRAW);
94
- engine._buffers.push(ib);
95
- return ib;
85
+ /**
86
+ * Create a GPU index buffer. `Uint16Array` → 16-bit indices, `Uint32Array` →
87
+ * 32-bit. Binds the default VAO first so it never corrupts the quad / sprite
88
+ * VAO element bindings.
89
+ *
90
+ * @param engine - The engine.
91
+ * @param data - The index data. Retained by reference for context-restore.
92
+ * @returns The new {@link GLIndexBuffer}.
93
+ */
94
+ export function createIndexBuffer(engine, data) {
95
+ const gl = engine.gl;
96
+ const handle = gl.createBuffer();
97
+ if (handle === null) {
98
+ throw new Error("lite-gl: gl.createBuffer returned null (index buffer)");
99
+ }
100
+ const is32 = data instanceof Uint32Array;
101
+ const ib = {
102
+ handle,
103
+ count: data.length,
104
+ is32Bits: is32,
105
+ _data: data,
106
+ _disposed: false,
107
+ _deleteGpu: () => { },
108
+ _restore: () => { },
109
+ };
110
+ ib._deleteGpu = (g) => {
111
+ g.deleteBuffer(ib.handle);
112
+ };
113
+ ib._restore = (target) => {
114
+ const g = target.gl;
115
+ const fresh = g.createBuffer();
116
+ if (fresh === null) {
117
+ return;
118
+ }
119
+ ib.handle = fresh;
120
+ bindDefaultVao(target);
121
+ g.bindBuffer(ELEMENT_ARRAY_BUFFER, fresh);
122
+ target._state.boundElementBuffer = fresh;
123
+ g.bufferData(ELEMENT_ARRAY_BUFFER, ib._data, STATIC_DRAW);
124
+ };
125
+ bindDefaultVao(engine);
126
+ gl.bindBuffer(ELEMENT_ARRAY_BUFFER, handle);
127
+ engine._state.boundElementBuffer = handle;
128
+ gl.bufferData(ELEMENT_ARRAY_BUFFER, data, STATIC_DRAW);
129
+ engine._buffers.push(ib);
130
+ return ib;
96
131
  }
97
- function disposeBuffer(engine, buffer) {
98
- if (buffer._disposed) {
99
- return;
100
- }
101
- buffer._disposed = true;
102
- const i = engine._buffers.indexOf(buffer);
103
- if (i !== -1) {
104
- engine._buffers.splice(i, 1);
105
- }
106
- const s = engine._state;
107
- if (!engine._isLost && !engine._disposed) {
108
- engine.gl.deleteBuffer(buffer.handle);
109
- }
110
- if (s.boundArrayBuffer === buffer.handle) {
111
- s.boundArrayBuffer = null;
112
- }
113
- if (s.boundElementBuffer === buffer.handle) {
114
- s.boundElementBuffer = null;
115
- }
132
+ /** Dispose a vertex or index buffer (delete the GL buffer + unregister). Clears
133
+ * the array/element-buffer cache slot if it pointed at this buffer. Idempotent. */
134
+ export function disposeBuffer(engine, buffer) {
135
+ if (buffer._disposed) {
136
+ return;
137
+ }
138
+ buffer._disposed = true;
139
+ const i = engine._buffers.indexOf(buffer);
140
+ if (i !== -1) {
141
+ engine._buffers.splice(i, 1);
142
+ }
143
+ const s = engine._state;
144
+ if (!engine._isLost && !engine._disposed) {
145
+ engine.gl.deleteBuffer(buffer.handle);
146
+ }
147
+ if (s.boundArrayBuffer === buffer.handle) {
148
+ s.boundArrayBuffer = null;
149
+ }
150
+ if (s.boundElementBuffer === buffer.handle) {
151
+ s.boundElementBuffer = null;
152
+ }
116
153
  }
117
- function bindIndexBuffer(engine, ib) {
118
- if (engine._isLost || engine._disposed || ib._disposed) {
119
- return;
120
- }
121
- bindDefaultVao(engine);
122
- const s = engine._state;
123
- if (s.boundElementBuffer === ib.handle) {
124
- return;
125
- }
126
- engine.gl.bindBuffer(ELEMENT_ARRAY_BUFFER, ib.handle);
127
- s.boundElementBuffer = ib.handle;
154
+ /* ────────────────────────────── binding + draw ──────────────────────────── */
155
+ /**
156
+ * Bind an index buffer as the current element-array buffer (on the default
157
+ * VAO). Cached. The lite-gl equivalent of Babylon's `_bindIndexBufferWithCache`.
158
+ *
159
+ * @param engine - The engine.
160
+ * @param ib - The index buffer to bind.
161
+ */
162
+ export function bindIndexBuffer(engine, ib) {
163
+ if (engine._isLost || engine._disposed || ib._disposed) {
164
+ return;
165
+ }
166
+ bindDefaultVao(engine);
167
+ const s = engine._state;
168
+ if (s.boundElementBuffer === ib.handle) {
169
+ return;
170
+ }
171
+ engine.gl.bindBuffer(ELEMENT_ARRAY_BUFFER, ib.handle);
172
+ s.boundElementBuffer = ib.handle;
128
173
  }
129
- function bindAttributes(engine, vb, descriptors, effect, computeStride = false) {
130
- if (engine._isLost || engine._disposed || vb._disposed || !effect.isReady) {
131
- return;
132
- }
133
- const gl = engine.gl;
134
- const s = engine._state;
135
- bindDefaultVao(engine);
136
- bindArrayBufferRaw(engine, vb.handle);
137
- let stride = 0;
138
- if (computeStride) {
174
+ /**
175
+ * Configure vertex attributes from `vb`, reproducing Babylon's
176
+ * `bindInstancesBuffer` exactly. For each descriptor: resolves the location
177
+ * (explicit `index` or via the effect), enables the attribute array, issues
178
+ * `vertexAttribPointer`, and sets the vertex divisor (`undefined → 1`). Every
179
+ * touched location is tracked so {@link unbindInstanceAttributes} can reset its
180
+ * divisor afterwards.
181
+ *
182
+ * `computeStride` controls the GL stride passed to `vertexAttribPointer`:
183
+ * - `false` (default) → stride `0`: each attribute is independently tightly
184
+ * packed. Combined with overlapping `offset`s this yields the "sliding window"
185
+ * ShapeBuilder uses for distance-field tape buffers.
186
+ * - `true` → stride = Σ(`size`·4 bytes): interleaved per-vertex/per-instance.
187
+ *
188
+ * Runs on the default (null) VAO — never corrupts the quad / sprite VAOs.
189
+ * No-op on a lost/disposed context or before the effect is ready.
190
+ *
191
+ * @param engine - The engine.
192
+ * @param vb - The buffer supplying the attribute data.
193
+ * @param descriptors - The attribute layout.
194
+ * @param effect - The effect whose attribute locations resolve unnamed indices.
195
+ * @param computeStride - See above. Default `false`.
196
+ */
197
+ export function bindAttributes(engine, vb, descriptors, effect, computeStride = false) {
198
+ if (engine._isLost || engine._disposed || vb._disposed || !effect.isReady) {
199
+ return;
200
+ }
201
+ const gl = engine.gl;
202
+ const s = engine._state;
203
+ bindDefaultVao(engine);
204
+ bindArrayBufferRaw(engine, vb.handle);
205
+ let stride = 0;
206
+ if (computeStride) {
207
+ for (let i = 0; i < descriptors.length; i++) {
208
+ stride += descriptors[i].size * 4;
209
+ }
210
+ }
139
211
  for (let i = 0; i < descriptors.length; i++) {
140
- stride += descriptors[i].size * 4;
141
- }
142
- }
143
- for (let i = 0; i < descriptors.length; i++) {
144
- const d = descriptors[i];
145
- const loc = d.index !== void 0 ? d.index : d.name !== void 0 ? getEffectAttributeLocation(engine, effect, d.name) : -1;
146
- if (loc < 0) {
147
- continue;
148
- }
149
- if (!s.enabledAttribs[loc]) {
150
- gl.enableVertexAttribArray(loc);
151
- s.enabledAttribs[loc] = true;
152
- }
153
- gl.vertexAttribPointer(loc, d.size, d.type ?? FLOAT, d.normalized ?? false, stride, d.offset ?? 0);
154
- gl.vertexAttribDivisor(loc, d.divisor === void 0 ? 1 : d.divisor);
155
- s.instanceLocations.push(loc);
156
- }
212
+ const d = descriptors[i];
213
+ const loc = d.index !== undefined ? d.index : d.name !== undefined ? getEffectAttributeLocation(engine, effect, d.name) : -1;
214
+ if (loc < 0) {
215
+ continue;
216
+ }
217
+ if (!s.enabledAttribs[loc]) {
218
+ gl.enableVertexAttribArray(loc);
219
+ s.enabledAttribs[loc] = true;
220
+ }
221
+ gl.vertexAttribPointer(loc, d.size, d.type ?? FLOAT, d.normalized ?? false, stride, d.offset ?? 0);
222
+ gl.vertexAttribDivisor(loc, d.divisor === undefined ? 1 : d.divisor);
223
+ s.instanceLocations.push(loc);
224
+ }
157
225
  }
158
- function unbindInstanceAttributes(engine) {
159
- if (engine._isLost || engine._disposed) {
160
- return;
161
- }
162
- const gl = engine.gl;
163
- const locs = engine._state.instanceLocations;
164
- for (let i = 0; i < locs.length; i++) {
165
- gl.vertexAttribDivisor(locs[i], 0);
166
- }
167
- locs.length = 0;
226
+ /**
227
+ * Reset the vertex divisor of every attribute touched by {@link bindAttributes}
228
+ * back to 0 — the lite-gl equivalent of Babylon's `unbindInstanceAttributes`.
229
+ * Call after an instanced draw so a following non-instanced draw is not skewed.
230
+ * No-op on a lost/disposed context.
231
+ *
232
+ * @param engine - The engine.
233
+ */
234
+ export function unbindInstanceAttributes(engine) {
235
+ if (engine._isLost || engine._disposed) {
236
+ return;
237
+ }
238
+ const gl = engine.gl;
239
+ const locs = engine._state.instanceLocations;
240
+ for (let i = 0; i < locs.length; i++) {
241
+ gl.vertexAttribDivisor(locs[i], 0);
242
+ }
243
+ locs.length = 0;
168
244
  }
169
- function drawIndexed(engine, ib, indexCount, indexStart = 0, instanceCount = 0) {
170
- if (engine._isLost || engine._disposed || ib._disposed) {
171
- return;
172
- }
173
- if (engine._state.currentProgram === null) {
174
- return;
175
- }
176
- bindIndexBuffer(engine, ib);
177
- const gl = engine.gl;
178
- const type = ib.is32Bits ? UNSIGNED_INT : UNSIGNED_SHORT;
179
- const byteOffset = indexStart * (ib.is32Bits ? 4 : 2);
180
- applyGLStates(engine);
181
- if (instanceCount > 0) {
182
- gl.drawElementsInstanced(TRIANGLES, indexCount, type, byteOffset, instanceCount);
183
- } else {
184
- gl.drawElements(TRIANGLES, indexCount, type, byteOffset);
185
- }
245
+ /**
246
+ * Draw indexed triangles from `ib` — the lite-gl equivalent of Babylon's
247
+ * `drawElementsType` (triangle fill mode). When `instanceCount > 0` issues
248
+ * `drawElementsInstanced`, otherwise `drawElements`. No-op on a lost/disposed
249
+ * context or when no program is current.
250
+ *
251
+ * @param engine - The engine.
252
+ * @param ib - The index buffer (also bound as a side-effect, cached).
253
+ * @param indexCount - Number of indices to draw.
254
+ * @param indexStart - First index offset (in indices, not bytes). Default 0.
255
+ * @param instanceCount - Instance count for instanced draws. Default 0
256
+ * (non-instanced).
257
+ */
258
+ export function drawIndexed(engine, ib, indexCount, indexStart = 0, instanceCount = 0) {
259
+ if (engine._isLost || engine._disposed || ib._disposed) {
260
+ return;
261
+ }
262
+ if (engine._state.currentProgram === null) {
263
+ return;
264
+ }
265
+ bindIndexBuffer(engine, ib);
266
+ const gl = engine.gl;
267
+ const type = ib.is32Bits ? UNSIGNED_INT : UNSIGNED_SHORT;
268
+ const byteOffset = indexStart * (ib.is32Bits ? 4 : 2);
269
+ applyGLStates(engine);
270
+ if (instanceCount > 0) {
271
+ gl.drawElementsInstanced(TRIANGLES, indexCount, type, byteOffset, instanceCount);
272
+ }
273
+ else {
274
+ gl.drawElements(TRIANGLES, indexCount, type, byteOffset);
275
+ }
186
276
  }
187
- function createMeshVao(engine, vertexBuffers, indexBuffer, effect) {
188
- const gl = engine.gl;
189
- if (!effect.isReady) {
190
- throw new Error("lite-gl: createMeshVao requires a ready effect (poll isEffectReady first)");
191
- }
192
- const handle = gl.createVertexArray();
193
- if (handle === null) {
194
- throw new Error("lite-gl: gl.createVertexArray returned null (mesh VAO)");
195
- }
196
- const bindings = [];
197
- for (const vbb of vertexBuffers) {
198
- let stride = 0;
199
- if (vbb.computeStride) {
200
- for (let i = 0; i < vbb.attributes.length; i++) {
201
- stride += vbb.attributes[i].size * 4;
202
- }
203
- }
204
- const attrs = [];
205
- for (let i = 0; i < vbb.attributes.length; i++) {
206
- const d = vbb.attributes[i];
207
- const loc = d.index !== void 0 ? d.index : d.name !== void 0 ? getEffectAttributeLocation(engine, effect, d.name) : -1;
208
- if (loc < 0) {
209
- continue;
210
- }
211
- attrs.push({
212
- loc,
213
- size: d.size,
214
- type: d.type ?? FLOAT,
215
- normalized: d.normalized ?? false,
216
- stride,
217
- offset: d.offset ?? 0,
218
- divisor: d.divisor === void 0 ? 1 : d.divisor
219
- });
220
- }
221
- bindings.push({ buffer: vbb.buffer, attrs });
222
- }
223
- const vao = { handle, _disposed: false, _indexBuffer: indexBuffer, _bindings: bindings, _restore: () => {
224
- } };
225
- vao._restore = () => {
226
- if (engine._isLost || engine._disposed || vao._disposed) {
227
- return;
277
+ /**
278
+ * Record a static mesh's attribute layout + index binding into a new VAO. The
279
+ * effect MUST be ready (its attribute locations are resolved here, once). Returns
280
+ * a {@link GLMeshVao} to draw with {@link drawMesh}.
281
+ *
282
+ * @param engine - The engine.
283
+ * @param vertexBuffers - One or more buffers + their attribute layouts.
284
+ * @param indexBuffer - The index buffer recorded into the VAO.
285
+ * @param effect - The effect whose attribute locations resolve unnamed indices.
286
+ * @returns The recorded {@link GLMeshVao}.
287
+ */
288
+ export function createMeshVao(engine, vertexBuffers, indexBuffer, effect) {
289
+ const gl = engine.gl;
290
+ // The effect's attribute locations are resolved (once) below; an unready effect
291
+ // would resolve every name to -1 → a silently-empty VAO. Fail fast — callers
292
+ // must gate on isEffectReady (see scene10's first-ready-frame creation).
293
+ if (!effect.isReady) {
294
+ throw new Error("lite-gl: createMeshVao requires a ready effect (poll isEffectReady first)");
295
+ }
296
+ const handle = gl.createVertexArray();
297
+ if (handle === null) {
298
+ throw new Error("lite-gl: gl.createVertexArray returned null (mesh VAO)");
228
299
  }
229
- const fresh = engine.gl.createVertexArray();
230
- if (fresh === null) {
231
- return;
300
+ // Resolve every attribute location ONCE (stable across re-link), so a restore
301
+ // re-record needs only the buffer handles, not the effect.
302
+ const bindings = [];
303
+ for (const vbb of vertexBuffers) {
304
+ let stride = 0;
305
+ if (vbb.computeStride) {
306
+ for (let i = 0; i < vbb.attributes.length; i++) {
307
+ stride += vbb.attributes[i].size * 4;
308
+ }
309
+ }
310
+ const attrs = [];
311
+ for (let i = 0; i < vbb.attributes.length; i++) {
312
+ const d = vbb.attributes[i];
313
+ const loc = d.index !== undefined ? d.index : d.name !== undefined ? getEffectAttributeLocation(engine, effect, d.name) : -1;
314
+ if (loc < 0) {
315
+ continue;
316
+ }
317
+ attrs.push({
318
+ loc,
319
+ size: d.size,
320
+ type: d.type ?? FLOAT,
321
+ normalized: d.normalized ?? false,
322
+ stride,
323
+ offset: d.offset ?? 0,
324
+ divisor: d.divisor === undefined ? 1 : d.divisor,
325
+ });
326
+ }
327
+ bindings.push({ buffer: vbb.buffer, attrs });
232
328
  }
233
- vao.handle = fresh;
329
+ const vao = { handle, _disposed: false, _indexBuffer: indexBuffer, _bindings: bindings, _restore: () => { } };
330
+ vao._restore = () => {
331
+ if (engine._isLost || engine._disposed || vao._disposed) {
332
+ return;
333
+ }
334
+ const fresh = engine.gl.createVertexArray();
335
+ if (fresh === null) {
336
+ return;
337
+ }
338
+ vao.handle = fresh;
339
+ recordMeshVao(engine, vao);
340
+ };
234
341
  recordMeshVao(engine, vao);
235
- };
236
- recordMeshVao(engine, vao);
237
- onContextRestored(engine, vao._restore);
238
- return vao;
239
- }
240
- function bindMeshVao(engine, vao) {
241
- if (engine._isLost || engine._disposed || vao._disposed) {
242
- return;
243
- }
244
- const s = engine._state;
245
- if (s.boundVao === vao.handle) {
246
- return;
247
- }
248
- engine.gl.bindVertexArray(vao.handle);
249
- s.boundVao = vao.handle;
250
- s.boundElementBuffer = vao._indexBuffer.handle;
251
- }
252
- function drawMesh(engine, vao, instanceCount = 0) {
253
- if (engine._isLost || engine._disposed || vao._disposed) {
254
- return;
255
- }
256
- if (engine._state.currentProgram === null) {
257
- return;
258
- }
259
- bindMeshVao(engine, vao);
260
- applyGLStates(engine);
261
- const ib = vao._indexBuffer;
262
- const gl = engine.gl;
263
- const type = ib.is32Bits ? UNSIGNED_INT : UNSIGNED_SHORT;
264
- if (instanceCount > 0) {
265
- gl.drawElementsInstanced(TRIANGLES, ib.count, type, 0, instanceCount);
266
- } else {
267
- gl.drawElements(TRIANGLES, ib.count, type, 0);
268
- }
342
+ onContextRestored(engine, vao._restore);
343
+ return vao;
269
344
  }
270
- function disposeMeshVao(engine, vao) {
271
- if (vao._disposed) {
272
- return;
273
- }
274
- vao._disposed = true;
275
- offContextRestored(engine, vao._restore);
276
- if (!engine._isLost && !engine._disposed) {
345
+ /**
346
+ * Bind a {@link GLMeshVao} (cached `gl.bindVertexArray`). Rarely called directly —
347
+ * {@link drawMesh} binds it for you. Binding restores the VAO's recorded element
348
+ * binding, so the element-buffer cache is updated in lock-step.
349
+ *
350
+ * @param engine - The engine.
351
+ * @param vao - The mesh VAO to bind.
352
+ */
353
+ export function bindMeshVao(engine, vao) {
354
+ if (engine._isLost || engine._disposed || vao._disposed) {
355
+ return;
356
+ }
277
357
  const s = engine._state;
278
- engine.gl.deleteVertexArray(vao.handle);
279
358
  if (s.boundVao === vao.handle) {
280
- engine.gl.bindVertexArray(null);
281
- s.boundVao = null;
282
- s.boundElementBuffer = null;
359
+ return;
283
360
  }
284
- }
361
+ engine.gl.bindVertexArray(vao.handle);
362
+ s.boundVao = vao.handle;
363
+ s.boundElementBuffer = vao._indexBuffer.handle;
285
364
  }
286
- function recordMeshVao(engine, vao) {
287
- const gl = engine.gl;
288
- const s = engine._state;
289
- gl.bindVertexArray(vao.handle);
290
- s.boundVao = vao.handle;
291
- for (const b of vao._bindings) {
292
- bindArrayBufferRaw(engine, b.buffer.handle);
293
- for (const a of b.attrs) {
294
- gl.enableVertexAttribArray(a.loc);
295
- gl.vertexAttribPointer(a.loc, a.size, a.type, a.normalized, a.stride, a.offset);
296
- gl.vertexAttribDivisor(a.loc, a.divisor);
297
- }
298
- }
299
- gl.bindBuffer(ELEMENT_ARRAY_BUFFER, vao._indexBuffer.handle);
300
- gl.bindVertexArray(null);
301
- s.boundVao = null;
302
- s.boundElementBuffer = null;
365
+ /**
366
+ * Draw a static mesh recorded with {@link createMeshVao}: binds its VAO (cached),
367
+ * flushes deferred GL state, and issues ONE `drawElements` (or
368
+ * `drawElementsInstanced` when `instanceCount > 0`) over the VAO's full index
369
+ * buffer. No per-draw attribute (re)binding. No-op on a lost/disposed context, a
370
+ * disposed VAO, or when no program is current.
371
+ *
372
+ * @param engine - The engine.
373
+ * @param vao - The mesh VAO to draw.
374
+ * @param instanceCount - Instance count for instanced draws. Default 0 (non-instanced).
375
+ */
376
+ export function drawMesh(engine, vao, instanceCount = 0) {
377
+ if (engine._isLost || engine._disposed || vao._disposed) {
378
+ return;
379
+ }
380
+ if (engine._state.currentProgram === null) {
381
+ return;
382
+ }
383
+ bindMeshVao(engine, vao);
384
+ applyGLStates(engine);
385
+ const ib = vao._indexBuffer;
386
+ const gl = engine.gl;
387
+ const type = ib.is32Bits ? UNSIGNED_INT : UNSIGNED_SHORT;
388
+ if (instanceCount > 0) {
389
+ gl.drawElementsInstanced(TRIANGLES, ib.count, type, 0, instanceCount);
390
+ }
391
+ else {
392
+ gl.drawElements(TRIANGLES, ib.count, type, 0);
393
+ }
303
394
  }
304
- function bindDefaultVao(engine) {
305
- const s = engine._state;
306
- if (s.boundVao !== null) {
307
- engine.gl.bindVertexArray(null);
395
+ /** Dispose a {@link GLMeshVao}: delete the VAO and unregister its restore hook.
396
+ * Does NOT dispose the vertex/index buffers (the caller owns those). Idempotent. */
397
+ export function disposeMeshVao(engine, vao) {
398
+ if (vao._disposed) {
399
+ return;
400
+ }
401
+ vao._disposed = true;
402
+ offContextRestored(engine, vao._restore);
403
+ if (!engine._isLost && !engine._disposed) {
404
+ const s = engine._state;
405
+ engine.gl.deleteVertexArray(vao.handle);
406
+ if (s.boundVao === vao.handle) {
407
+ // Return to the default VAO so the cache stays coherent.
408
+ engine.gl.bindVertexArray(null);
409
+ s.boundVao = null;
410
+ s.boundElementBuffer = null;
411
+ }
412
+ }
413
+ }
414
+ /** @internal Record `vao._bindings` + the index binding into `vao.handle`. Leaves
415
+ * the default (null) VAO bound afterwards (so the mesh VAO is not left current),
416
+ * forgetting the element-buffer cache. Touches neither `s.enabledAttribs` (those
417
+ * track the DEFAULT VAO) nor the divisor reset — all of that lives in the VAO. */
418
+ function recordMeshVao(engine, vao) {
419
+ const gl = engine.gl;
420
+ const s = engine._state;
421
+ gl.bindVertexArray(vao.handle);
422
+ s.boundVao = vao.handle;
423
+ for (const b of vao._bindings) {
424
+ bindArrayBufferRaw(engine, b.buffer.handle);
425
+ for (const a of b.attrs) {
426
+ gl.enableVertexAttribArray(a.loc);
427
+ gl.vertexAttribPointer(a.loc, a.size, a.type, a.normalized, a.stride, a.offset);
428
+ gl.vertexAttribDivisor(a.loc, a.divisor);
429
+ }
430
+ }
431
+ gl.bindBuffer(ELEMENT_ARRAY_BUFFER, vao._indexBuffer.handle);
432
+ gl.bindVertexArray(null);
308
433
  s.boundVao = null;
309
434
  s.boundElementBuffer = null;
310
- }
311
435
  }
436
+ /* ──────────────────────────── internal helpers ──────────────────────────── */
437
+ /** Bind the default (null) VAO if not already bound. The mesh path lives here so
438
+ * it never disturbs the quad / sprite VAO state. Binding a VAO restores ITS
439
+ * element-array binding, so the element-buffer cache is forgotten on the
440
+ * switch (the next `bindIndexBuffer` re-binds). */
441
+ function bindDefaultVao(engine) {
442
+ const s = engine._state;
443
+ if (s.boundVao !== null) {
444
+ engine.gl.bindVertexArray(null);
445
+ s.boundVao = null;
446
+ s.boundElementBuffer = null;
447
+ }
448
+ }
449
+ /** Cached `gl.bindBuffer(ARRAY_BUFFER, …)`. ARRAY_BUFFER binding is global (not
450
+ * VAO state), so this cache is coherent across VAO switches. */
312
451
  function bindArrayBufferRaw(engine, handle) {
313
- const s = engine._state;
314
- if (s.boundArrayBuffer === handle) {
315
- return;
316
- }
317
- engine.gl.bindBuffer(ARRAY_BUFFER, handle);
318
- s.boundArrayBuffer = handle;
452
+ const s = engine._state;
453
+ if (s.boundArrayBuffer === handle) {
454
+ return;
455
+ }
456
+ engine.gl.bindBuffer(ARRAY_BUFFER, handle);
457
+ s.boundArrayBuffer = handle;
319
458
  }
320
- export {
321
- bindAttributes,
322
- bindIndexBuffer,
323
- bindMeshVao,
324
- createIndexBuffer,
325
- createMeshVao,
326
- createVertexBuffer,
327
- disposeBuffer,
328
- disposeMeshVao,
329
- drawIndexed,
330
- drawMesh,
331
- unbindInstanceAttributes,
332
- updateVertexBuffer
333
- };
334
- //# sourceMappingURL=mesh.js.map
459
+ //# sourceMappingURL=mesh.js.map