@bigmistqke/view.gl 0.2.0 → 0.3.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 (43) hide show
  1. package/README.md +82 -27
  2. package/dev/dist/404.html +19 -0
  3. package/dev/dist/assets/bezier-DrA4cU_H.js +292 -0
  4. package/dev/dist/assets/game-of-life-BYlzWhEL.js +186 -0
  5. package/dev/dist/assets/glsl-composition-D_BaFFij.js +97 -0
  6. package/dev/dist/assets/grid-DfXHNLBm.js +181 -0
  7. package/dev/dist/assets/index-C2N3u8wj.css +12 -0
  8. package/dev/dist/assets/index-D-N0FDYs.js +110 -0
  9. package/dev/dist/assets/main-CSE2fki3.js +32 -0
  10. package/dev/dist/assets/overview-IeUhUflE.js +80 -0
  11. package/dev/dist/assets/pix-sim-DMJJA_WO.js +500 -0
  12. package/dev/dist/assets/ray-casting-DTQetDPd.js +411 -0
  13. package/dev/dist/assets/ray-casting-modules-Dkfw4tdc.js +765 -0
  14. package/dev/dist/assets/sdf-CS0uBonx.js +66 -0
  15. package/dev/dist/assets/symbol-BL_7GJ0M.js +67 -0
  16. package/dev/dist/assets/tag-C0Vskpp8.js +833 -0
  17. package/dev/dist/assets/tetris-D8mokodr.js +344 -0
  18. package/dev/dist/assets/utils-2dzuv_bW.js +62 -0
  19. package/dev/dist/index.html +51 -0
  20. package/dist/chunk-BZDGEIOT.mjs +278 -0
  21. package/dist/chunk-BZDGEIOT.mjs.map +1 -0
  22. package/dist/{chunk-EYXOSM7B.mjs → chunk-X4M3LV4R.mjs} +60 -133
  23. package/dist/chunk-X4M3LV4R.mjs.map +1 -0
  24. package/dist/gl.d.mts +2 -2
  25. package/dist/gl.mjs +1 -1
  26. package/dist/index.d.mts +13 -4
  27. package/dist/index.mjs +7 -2
  28. package/dist/tag.d.mts +6 -1
  29. package/dist/tag.mjs +13 -5
  30. package/dist/tag.mjs.map +1 -1
  31. package/dist/{types-CyGyGi18.d.mts → types-CNdqh7tB.d.mts} +34 -4
  32. package/package.json +1 -1
  33. package/src/gl.ts +13 -2
  34. package/src/index.ts +55 -39
  35. package/src/tag.ts +18 -2
  36. package/src/types.ts +33 -3
  37. package/src/utils.ts +45 -10
  38. package/test/index.test.ts +111 -29
  39. package/test/mocks/webgl.ts +14 -0
  40. package/test/tag.test.ts +41 -0
  41. package/dist/chunk-CJTT7WPV.mjs +0 -75
  42. package/dist/chunk-CJTT7WPV.mjs.map +0 -1
  43. package/dist/chunk-EYXOSM7B.mjs.map +0 -1
@@ -0,0 +1,278 @@
1
+ // src/utils.ts
2
+ function assertedNotNullish(value, message) {
3
+ if (value === void 0 || value === null) throw new Error(message);
4
+ return value;
5
+ }
6
+ function once(teardown) {
7
+ let done = false;
8
+ return () => {
9
+ if (done) return;
10
+ done = true;
11
+ teardown();
12
+ };
13
+ }
14
+ var kindToUniformFnName = (kind) => {
15
+ switch (kind[0]) {
16
+ // mat
17
+ case "m":
18
+ return "Matrix" + kind.slice(3) + "fv";
19
+ // sampler/booleans/integer
20
+ case "s":
21
+ case "b":
22
+ case "i":
23
+ return (kind.match(/\d/)?.[0] ?? "1") + "i";
24
+ // unsigned integers
25
+ case "u":
26
+ return (kind[4] || "1") + "ui";
27
+ // vec
28
+ case "v":
29
+ return kind[3] + "f";
30
+ default:
31
+ switch (kind) {
32
+ case "float":
33
+ return "1f";
34
+ case "uint":
35
+ return "1ui";
36
+ default:
37
+ return "1i";
38
+ }
39
+ }
40
+ };
41
+ var kindToSize = (kind) => {
42
+ switch (kind[0]) {
43
+ case "m":
44
+ const [a, b] = kind.match(/\d+/g).map(Number);
45
+ return a * (b ?? a);
46
+ case "v":
47
+ case "i":
48
+ case "b":
49
+ case "u":
50
+ const match = kind.match(/\d/);
51
+ return match ? +match[0] : 1;
52
+ default:
53
+ return 1;
54
+ }
55
+ };
56
+ var isMatKind = (kind) => kind.includes("mat");
57
+ var isSamplerKind = (kind) => kind.includes("sampler");
58
+ function createTexture(gl, {
59
+ target = "TEXTURE_2D",
60
+ internalFormat = "RGBA",
61
+ format = "RGBA",
62
+ type = "UNSIGNED_BYTE",
63
+ minFilter = "NEAREST",
64
+ magFilter = "NEAREST",
65
+ wrapS = "CLAMP_TO_EDGE",
66
+ wrapT = "CLAMP_TO_EDGE",
67
+ width,
68
+ height
69
+ }, data, { signal } = {}) {
70
+ const texture = assertedNotNullish(gl.createTexture(), "Failed to create texture");
71
+ function getTextureConstant(name) {
72
+ if (!(name in gl)) {
73
+ throw new Error(`Attempted to create webgl2-only texture (${name}) in webgl1`);
74
+ }
75
+ return gl[name];
76
+ }
77
+ gl.bindTexture(gl[target], texture);
78
+ gl.texImage2D(
79
+ gl[target],
80
+ 0,
81
+ getTextureConstant(internalFormat),
82
+ width,
83
+ height,
84
+ 0,
85
+ getTextureConstant(format),
86
+ getTextureConstant(type),
87
+ data ?? null
88
+ );
89
+ gl.texParameteri(gl[target], gl.TEXTURE_MIN_FILTER, gl[minFilter]);
90
+ gl.texParameteri(gl[target], gl.TEXTURE_MAG_FILTER, gl[magFilter]);
91
+ gl.texParameteri(gl[target], gl.TEXTURE_WRAP_S, gl[wrapS]);
92
+ gl.texParameteri(gl[target], gl.TEXTURE_WRAP_T, gl[wrapT]);
93
+ signal?.addEventListener(
94
+ "abort",
95
+ once(() => gl.deleteTexture(texture))
96
+ );
97
+ return texture;
98
+ }
99
+ var FRAMEBUFFER_ATTACHMENT_MAP = {
100
+ color: "COLOR_ATTACHMENT0",
101
+ depth: "DEPTH_ATTACHMENT",
102
+ stencil: "STENCIL_ATTACHMENT",
103
+ depthStencil: "DEPTH_STENCIL_ATTACHMENT"
104
+ };
105
+ var FramebufferError = class extends Error {
106
+ constructor(gl, status) {
107
+ let errorMessage = "Framebuffer not complete. Status: ";
108
+ switch (status) {
109
+ case gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
110
+ errorMessage += "FRAMEBUFFER_INCOMPLETE_ATTACHMENT";
111
+ break;
112
+ case gl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
113
+ errorMessage += "FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT";
114
+ break;
115
+ case gl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
116
+ errorMessage += "FRAMEBUFFER_INCOMPLETE_DIMENSIONS";
117
+ break;
118
+ case gl.FRAMEBUFFER_UNSUPPORTED:
119
+ errorMessage += "FRAMEBUFFER_UNSUPPORTED";
120
+ break;
121
+ default:
122
+ errorMessage += `Unknown (${status})`;
123
+ }
124
+ super(errorMessage);
125
+ }
126
+ };
127
+ function createFramebuffer(gl, { attachment, texture: providedTexture, ...definition }, { signal } = {}) {
128
+ const texture = providedTexture ?? createTexture(gl, definition);
129
+ const framebuffer = assertedNotNullish(gl.createFramebuffer(), "Failed to create framebuffer");
130
+ gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
131
+ gl.framebufferTexture2D(
132
+ gl.FRAMEBUFFER,
133
+ // Determine attachment point based on attachment kind
134
+ gl[FRAMEBUFFER_ATTACHMENT_MAP[attachment]],
135
+ gl.TEXTURE_2D,
136
+ texture,
137
+ 0
138
+ );
139
+ const status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
140
+ if (status !== gl.FRAMEBUFFER_COMPLETE) {
141
+ throw new FramebufferError(gl, status);
142
+ }
143
+ const dispose = once(() => {
144
+ gl.deleteFramebuffer(framebuffer);
145
+ if (!providedTexture) {
146
+ gl.deleteTexture(texture);
147
+ }
148
+ });
149
+ signal?.addEventListener("abort", dispose);
150
+ return {
151
+ texture,
152
+ framebuffer,
153
+ dispose
154
+ };
155
+ }
156
+ function createUpsertMap(constructor) {
157
+ const map = new (constructor ?? Map)();
158
+ return Object.assign(map, {
159
+ getOrInsert(key, value) {
160
+ let result = map.get(key);
161
+ if (result) {
162
+ return result;
163
+ }
164
+ result = value();
165
+ map.set(key, result);
166
+ return result;
167
+ }
168
+ });
169
+ }
170
+ function forEachObject(value, callback) {
171
+ let index = 0;
172
+ for (const key in value) {
173
+ callback(value[key], key, index);
174
+ index++;
175
+ }
176
+ for (const key of Object.getOwnPropertySymbols(value)) {
177
+ callback(value[key], key, index);
178
+ index++;
179
+ }
180
+ }
181
+ function mapObject(value, callback) {
182
+ const result = {};
183
+ forEachObject(value, (value2, key, index) => {
184
+ result[key] = callback(value2, key, index);
185
+ });
186
+ return result;
187
+ }
188
+
189
+ // src/gl.ts
190
+ function isWebGL2RenderingContext(gl) {
191
+ return typeof WebGL2RenderingContext !== "undefined" && gl instanceof WebGL2RenderingContext;
192
+ }
193
+ function createShader(gl, type, source) {
194
+ const shader = gl.createShader(type);
195
+ if (!shader) throw new Error("Failed to create shader");
196
+ gl.shaderSource(shader, source);
197
+ gl.compileShader(shader);
198
+ if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
199
+ const info = gl.getShaderInfoLog(shader);
200
+ gl.deleteShader(shader);
201
+ throw new Error(
202
+ `Failed to compile ${type === gl.VERTEX_SHADER ? "vertex" : "fragment"} shader: ${info}`
203
+ );
204
+ }
205
+ return shader;
206
+ }
207
+ function createProgram(gl, vertexSource, fragmentSource, { signal } = {}) {
208
+ const program = gl.createProgram();
209
+ if (!program) throw new Error("Failed to create WebGL program");
210
+ const vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexSource);
211
+ const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentSource);
212
+ gl.attachShader(program, vertexShader);
213
+ gl.attachShader(program, fragmentShader);
214
+ gl.linkProgram(program);
215
+ if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
216
+ const info = gl.getProgramInfoLog(program);
217
+ gl.deleteProgram(program);
218
+ gl.deleteShader(vertexShader);
219
+ gl.deleteShader(fragmentShader);
220
+ throw new Error(`Failed to link program: ${info}`);
221
+ }
222
+ gl.deleteShader(vertexShader);
223
+ gl.deleteShader(fragmentShader);
224
+ signal?.addEventListener(
225
+ "abort",
226
+ once(() => gl.deleteProgram(program))
227
+ );
228
+ return program;
229
+ }
230
+ var INSTANCED_ARRAYS_WRAPPER_MAP = /* @__PURE__ */ new WeakMap();
231
+ function getInstancedArrays(gl) {
232
+ if (isWebGL2RenderingContext(gl)) return gl;
233
+ const cached = INSTANCED_ARRAYS_WRAPPER_MAP.get(gl);
234
+ if (cached) return cached;
235
+ const ext = gl.getExtension("ANGLE_instanced_arrays");
236
+ if (!ext) return void 0;
237
+ const wrapper = {
238
+ drawArraysInstanced: ext.drawArraysInstancedANGLE.bind(ext),
239
+ drawElementsInstanced: ext.drawElementsInstancedANGLE.bind(ext),
240
+ vertexAttribDivisor: ext.vertexAttribDivisorANGLE.bind(ext)
241
+ };
242
+ INSTANCED_ARRAYS_WRAPPER_MAP.set(gl, wrapper);
243
+ return wrapper;
244
+ }
245
+ var VERTEX_ARRAY_OBJECT_WRAPPER_MAP = /* @__PURE__ */ new WeakMap();
246
+ function getVertexArrayObject(gl) {
247
+ if (isWebGL2RenderingContext(gl)) return gl;
248
+ const cached = VERTEX_ARRAY_OBJECT_WRAPPER_MAP.get(gl);
249
+ if (cached) return cached;
250
+ const ext = gl.getExtension("OES_vertex_array_object");
251
+ if (!ext) return null;
252
+ const wrapper = {
253
+ bindVertexArray: ext.bindVertexArrayOES.bind(ext),
254
+ createVertexArray: ext.createVertexArrayOES.bind(ext),
255
+ deleteVertexArray: ext.deleteVertexArrayOES.bind(ext)
256
+ };
257
+ VERTEX_ARRAY_OBJECT_WRAPPER_MAP.set(gl, wrapper);
258
+ return wrapper;
259
+ }
260
+
261
+ export {
262
+ assertedNotNullish,
263
+ once,
264
+ kindToUniformFnName,
265
+ kindToSize,
266
+ isMatKind,
267
+ isSamplerKind,
268
+ createTexture,
269
+ createFramebuffer,
270
+ createUpsertMap,
271
+ forEachObject,
272
+ mapObject,
273
+ createShader,
274
+ createProgram,
275
+ getInstancedArrays,
276
+ getVertexArrayObject
277
+ };
278
+ //# sourceMappingURL=chunk-BZDGEIOT.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils.ts","../src/gl.ts"],"sourcesContent":["import type {\n AttributeKind,\n FramebufferDefinition,\n GL,\n KIND_TO_SIZE_MAP,\n KIND_TO_UNIFORM_FN_NAME_MAP,\n TextureDefinition,\n UniformKind,\n ViewOptions,\n} from './types'\n\nexport function assertedNotNullish<T>(value: T, message?: string): NonNullable<T> {\n if (value === undefined || value === null) throw new Error(message)\n return value\n}\n\n/**\n * Wraps a teardown so it runs once.\n *\n * Two reasons, and both bite. A disposer is per `bind()`, and undoing twice\n * would put back state a later `bind()` is relying on. And a `dispose()` that\n * deletes twice invalidates a handle someone else may still be holding — the\n * object survives while it is bound, so the fault surfaces later, somewhere\n * else. Idempotent is the only safe shape for either.\n */\nexport function once(teardown: () => void): () => void {\n let done = false\n return () => {\n if (done) return\n done = true\n teardown()\n }\n}\n\n/**********************************************************************************/\n/* */\n/* Kind To Uniform Name */\n/* */\n/**********************************************************************************/\n\nexport const kindToUniformFnName = <T extends UniformKind>(\n kind: T,\n): KIND_TO_UNIFORM_FN_NAME_MAP[T] => {\n switch (kind[0]) {\n // mat\n case 'm':\n return ('Matrix' + kind.slice(3) + 'fv') as KIND_TO_UNIFORM_FN_NAME_MAP[T]\n // sampler/booleans/integer\n case 's':\n case 'b':\n case 'i':\n return ((kind.match(/\\d/)?.[0] ?? '1') + 'i') as KIND_TO_UNIFORM_FN_NAME_MAP[T]\n // unsigned integers\n case 'u':\n return ((kind[4] || '1') + 'ui') as KIND_TO_UNIFORM_FN_NAME_MAP[T]\n // vec\n case 'v':\n return (kind[3] + 'f') as KIND_TO_UNIFORM_FN_NAME_MAP[T]\n default:\n switch (kind) {\n case 'float':\n return '1f' as KIND_TO_UNIFORM_FN_NAME_MAP[T]\n case 'uint':\n return '1ui' as KIND_TO_UNIFORM_FN_NAME_MAP[T]\n default:\n return '1i' as KIND_TO_UNIFORM_FN_NAME_MAP[T]\n }\n }\n}\n\n/**********************************************************************************/\n/* */\n/* Kind To Size */\n/* */\n/**********************************************************************************/\n\nexport const kindToSize = <T extends UniformKind>(kind: T): KIND_TO_SIZE_MAP[T] => {\n switch (kind[0]) {\n case 'm':\n const [a, b] = kind.match(/\\d+/g)!.map(Number) as [number, number?]\n return (a * (b ?? a)) as KIND_TO_SIZE_MAP[T]\n case 'v':\n case 'i':\n case 'b':\n case 'u':\n const match = kind.match(/\\d/) as [string] | undefined\n return (match ? +match[0] : 1) as KIND_TO_SIZE_MAP[T]\n default:\n return 1 as KIND_TO_SIZE_MAP[T]\n }\n}\n\n/**********************************************************************************/\n/* */\n/* Guards */\n/* */\n/**********************************************************************************/\n\nexport const isMatKind = <\n T extends UniformKind | AttributeKind,\n TPrefix extends string,\n TPostfix extends string,\n>(\n kind: T,\n): kind is T & `${TPrefix}mat${TPostfix}` => kind.includes('mat')\n\nexport const isSamplerKind = <TPrefix extends string, TPostfix extends string>(\n kind: UniformKind,\n): kind is UniformKind & `${TPrefix}sampler${TPostfix}` => kind.includes('sampler')\n\nexport const isVecKind = <TPrefix extends string, TPostfix extends string>(\n kind: UniformKind,\n): kind is UniformKind & `${TPrefix}vec${TPostfix}` => kind.includes('vec')\n\n/**********************************************************************************/\n/* */\n/* Create Texture */\n/* */\n/**********************************************************************************/\n\nexport function createTexture(\n gl: GL,\n {\n target = 'TEXTURE_2D',\n internalFormat = 'RGBA',\n format = 'RGBA',\n type = 'UNSIGNED_BYTE',\n minFilter = 'NEAREST',\n magFilter = 'NEAREST',\n wrapS = 'CLAMP_TO_EDGE',\n wrapT = 'CLAMP_TO_EDGE',\n width,\n height,\n }: TextureDefinition,\n data?: ArrayBufferView | null,\n { signal }: ViewOptions = {},\n): WebGLTexture {\n const texture = assertedNotNullish(gl.createTexture(), 'Failed to create texture')\n\n function getTextureConstant(name: string) {\n if (!(name in gl)) {\n throw new Error(`Attempted to create webgl2-only texture (${name}) in webgl1`)\n }\n return gl[name as keyof typeof gl] as any\n }\n\n gl.bindTexture(gl[target], texture)\n gl.texImage2D(\n gl[target],\n 0,\n getTextureConstant(internalFormat),\n width,\n height,\n 0,\n getTextureConstant(format),\n getTextureConstant(type),\n data ?? null,\n )\n\n // Set texture parameters\n gl.texParameteri(gl[target], gl.TEXTURE_MIN_FILTER, gl[minFilter])\n gl.texParameteri(gl[target], gl.TEXTURE_MAG_FILTER, gl[magFilter])\n gl.texParameteri(gl[target], gl.TEXTURE_WRAP_S, gl[wrapS])\n gl.texParameteri(gl[target], gl.TEXTURE_WRAP_T, gl[wrapT])\n\n signal?.addEventListener(\n 'abort',\n once(() => gl.deleteTexture(texture)),\n )\n\n return texture\n}\n\n/**********************************************************************************/\n/* */\n/* Create Framebuffer */\n/* */\n/**********************************************************************************/\n\nconst FRAMEBUFFER_ATTACHMENT_MAP = {\n color: 'COLOR_ATTACHMENT0',\n depth: 'DEPTH_ATTACHMENT',\n stencil: 'STENCIL_ATTACHMENT',\n depthStencil: 'DEPTH_STENCIL_ATTACHMENT',\n} as const\n\nclass FramebufferError extends Error {\n constructor(gl: GL, status: number) {\n let errorMessage = 'Framebuffer not complete. Status: '\n switch (status) {\n case gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT:\n errorMessage += 'FRAMEBUFFER_INCOMPLETE_ATTACHMENT'\n break\n case gl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:\n errorMessage += 'FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT'\n break\n case gl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS:\n errorMessage += 'FRAMEBUFFER_INCOMPLETE_DIMENSIONS'\n break\n case gl.FRAMEBUFFER_UNSUPPORTED:\n errorMessage += 'FRAMEBUFFER_UNSUPPORTED'\n break\n default:\n errorMessage += `Unknown (${status})`\n }\n super(errorMessage)\n }\n}\n\nexport function createFramebuffer(\n gl: GL,\n { attachment, texture: providedTexture, ...definition }: FramebufferDefinition,\n { signal }: ViewOptions = {},\n) {\n // Create texture for the framebuffer, unless one was handed in. Which one it\n // is decides who deletes it below — the same rule the attribute buffers follow.\n const texture = providedTexture ?? createTexture(gl, definition)\n\n // Create framebuffer\n const framebuffer = assertedNotNullish(gl.createFramebuffer(), 'Failed to create framebuffer')\n\n gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer)\n\n gl.framebufferTexture2D(\n gl.FRAMEBUFFER,\n // Determine attachment point based on attachment kind\n gl[FRAMEBUFFER_ATTACHMENT_MAP[attachment]],\n gl.TEXTURE_2D,\n texture,\n 0,\n )\n\n // Check framebuffer completeness\n const status = gl.checkFramebufferStatus(gl.FRAMEBUFFER)\n if (status !== gl.FRAMEBUFFER_COMPLETE) {\n throw new FramebufferError(gl, status)\n }\n\n const dispose = once(() => {\n gl.deleteFramebuffer(framebuffer)\n // A texture handed in belongs to whoever made it — it commonly outlives the\n // framebuffer, ping-ponged between two of them.\n if (!providedTexture) {\n gl.deleteTexture(texture)\n }\n })\n\n signal?.addEventListener('abort', dispose)\n\n return {\n texture,\n framebuffer,\n dispose,\n }\n}\n\n/**********************************************************************************/\n/* */\n/* Create Upsert */\n/* */\n/**********************************************************************************/\n\ntype UpsertMapKind<T, U> = Map<T, U> | (T extends WeakKey ? WeakMap<T, U> : never)\n\nexport function createUpsertMap<T extends UpsertMapKind<any, any> = Map<any, any>>(\n constructor?: new () => T,\n) {\n const map = new (constructor ?? Map)() as T\n return Object.assign(map, {\n getOrInsert(\n key: T extends UpsertMapKind<infer U, any> ? U : never,\n value: T extends UpsertMapKind<any, infer U> ? () => U : never,\n ) {\n let result = map.get(key)\n if (result) {\n return result\n }\n result = value()\n map.set(key, result)\n return result\n },\n })\n}\n\nexport function forEachObject<T extends Record<string, any>>(\n value: T,\n callback: (value: T[keyof T], key: keyof T, index: number) => void,\n) {\n let index = 0\n for (const key in value) {\n callback(value[key], key, index)\n index++\n }\n for (const key of Object.getOwnPropertySymbols(value)) {\n callback(value[key as keyof T], key as keyof T, index)\n index++\n }\n}\n\nexport function mapObject<T extends Record<string, any>, TReturn>(\n value: T,\n callback: (value: T[keyof T], key: keyof T, index: number) => TReturn,\n): { [TKey in keyof T]: TReturn } {\n const result = {} as { [TKey in keyof T]: TReturn }\n\n forEachObject(value, (value, key, index) => {\n result[key] = callback(value, key, index)\n })\n\n return result\n}\n","import { RemoveSuffix } from './type-utils'\nimport type { GL, ViewOptions } from './types'\nimport { once } from './utils'\n\nfunction isWebGL2RenderingContext(\n gl: WebGLRenderingContext | WebGL2RenderingContext,\n): gl is WebGL2RenderingContext {\n return typeof WebGL2RenderingContext !== 'undefined' && gl instanceof WebGL2RenderingContext\n}\n\n/**********************************************************************************/\n/* */\n/* Create Shader */\n/* */\n/**********************************************************************************/\n\nexport function createShader(gl: GL, type: number, source: string): WebGLShader {\n const shader = gl.createShader(type)\n if (!shader) throw new Error('Failed to create shader')\n\n gl.shaderSource(shader, source)\n gl.compileShader(shader)\n\n if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {\n const info = gl.getShaderInfoLog(shader)\n gl.deleteShader(shader)\n throw new Error(\n `Failed to compile ${type === gl.VERTEX_SHADER ? 'vertex' : 'fragment'} shader: ${info}`,\n )\n }\n\n return shader\n}\n\n/**********************************************************************************/\n/* */\n/* Create Program */\n/* */\n/**********************************************************************************/\n\nexport function createProgram(\n gl: GL,\n vertexSource: string,\n fragmentSource: string,\n { signal }: ViewOptions = {},\n): WebGLProgram {\n const program = gl.createProgram()\n if (!program) throw new Error('Failed to create WebGL program')\n\n const vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexSource)\n const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentSource)\n\n gl.attachShader(program, vertexShader)\n gl.attachShader(program, fragmentShader)\n gl.linkProgram(program)\n\n if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {\n const info = gl.getProgramInfoLog(program)\n gl.deleteProgram(program)\n gl.deleteShader(vertexShader)\n gl.deleteShader(fragmentShader)\n throw new Error(`Failed to link program: ${info}`)\n }\n\n // Clean up shaders after linking\n gl.deleteShader(vertexShader)\n gl.deleteShader(fragmentShader)\n\n signal?.addEventListener(\n 'abort',\n once(() => gl.deleteProgram(program)),\n )\n\n return program\n}\n\n/**********************************************************************************/\n/* */\n/* Get Instanced Arrays */\n/* */\n/**********************************************************************************/\n\nconst INSTANCED_ARRAYS_WRAPPER_MAP = new WeakMap<\n GL,\n RemoveSuffix<\n Pick<\n ANGLE_instanced_arrays,\n 'drawArraysInstancedANGLE' | 'drawElementsInstancedANGLE' | 'vertexAttribDivisorANGLE'\n >,\n 'ANGLE'\n > | null\n>()\nexport function getInstancedArrays(gl: GL) {\n if (isWebGL2RenderingContext(gl)) return gl\n\n const cached = INSTANCED_ARRAYS_WRAPPER_MAP.get(gl)\n if (cached) return cached\n\n const ext = gl.getExtension('ANGLE_instanced_arrays')\n if (!ext) return undefined\n\n const wrapper = {\n drawArraysInstanced: ext.drawArraysInstancedANGLE.bind(ext),\n drawElementsInstanced: ext.drawElementsInstancedANGLE.bind(ext),\n vertexAttribDivisor: ext.vertexAttribDivisorANGLE.bind(ext),\n }\n INSTANCED_ARRAYS_WRAPPER_MAP.set(gl, wrapper)\n\n return wrapper\n}\n\n/**********************************************************************************/\n/* */\n/* Get Vertex Array Object */\n/* */\n/**********************************************************************************/\n\nconst VERTEX_ARRAY_OBJECT_WRAPPER_MAP = new WeakMap<\n GL,\n RemoveSuffix<\n Pick<\n OES_vertex_array_object,\n 'bindVertexArrayOES' | 'createVertexArrayOES' | 'deleteVertexArrayOES'\n >,\n 'OES'\n > | null\n>()\nexport function getVertexArrayObject(gl: GL) {\n if (isWebGL2RenderingContext(gl)) return gl\n\n const cached = VERTEX_ARRAY_OBJECT_WRAPPER_MAP.get(gl)\n if (cached) return cached\n\n const ext = gl.getExtension('OES_vertex_array_object')\n if (!ext) return null\n\n const wrapper = {\n bindVertexArray: ext.bindVertexArrayOES.bind(ext),\n createVertexArray: ext.createVertexArrayOES.bind(ext),\n deleteVertexArray: ext.deleteVertexArrayOES.bind(ext),\n }\n VERTEX_ARRAY_OBJECT_WRAPPER_MAP.set(gl, wrapper)\n\n return wrapper\n}\n"],"mappings":"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","names":["value"]}
@@ -1,89 +1,16 @@
1
1
  import {
2
+ assertedNotNullish,
3
+ createUpsertMap,
4
+ forEachObject,
2
5
  getInstancedArrays,
3
- getVertexArrayObject
4
- } from "./chunk-CJTT7WPV.mjs";
5
-
6
- // src/utils.ts
7
- function assertedNotNullish(value, message) {
8
- if (value === void 0 || value === null) throw new Error(message);
9
- return value;
10
- }
11
- var kindToUniformFnName = (kind) => {
12
- switch (kind[0]) {
13
- // mat
14
- case "m":
15
- return "Matrix" + kind.slice(3) + "fv";
16
- // sampler/booleans/integer
17
- case "s":
18
- case "b":
19
- case "i":
20
- return (kind.match(/\d/)?.[0] ?? "1") + "i";
21
- // unsigned integers
22
- case "u":
23
- return (kind[4] || "1") + "ui";
24
- // vec
25
- case "v":
26
- return kind[3] + "f";
27
- default:
28
- switch (kind) {
29
- case "float":
30
- return "1f";
31
- case "uint":
32
- return "1ui";
33
- default:
34
- return "1i";
35
- }
36
- }
37
- };
38
- var kindToSize = (kind) => {
39
- switch (kind[0]) {
40
- case "m":
41
- const [a, b] = kind.match(/\d+/g).map(Number);
42
- return a * (b ?? a);
43
- case "v":
44
- case "i":
45
- case "b":
46
- case "u":
47
- const match = kind.match(/\d/);
48
- return match ? +match[0] : 1;
49
- default:
50
- return 1;
51
- }
52
- };
53
- var isMatKind = (kind) => kind.includes("mat");
54
- var isSamplerKind = (kind) => kind.includes("sampler");
55
- function createUpsertMap(constructor) {
56
- const map = new (constructor ?? Map)();
57
- return Object.assign(map, {
58
- getOrInsert(key, value) {
59
- let result = map.get(key);
60
- if (result) {
61
- return result;
62
- }
63
- result = value();
64
- map.set(key, result);
65
- return result;
66
- }
67
- });
68
- }
69
- function forEachObject(value, callback) {
70
- let index2 = 0;
71
- for (const key in value) {
72
- callback(value[key], key, index2);
73
- index2++;
74
- }
75
- for (const key of Object.getOwnPropertySymbols(value)) {
76
- callback(value[key], key, index2);
77
- index2++;
78
- }
79
- }
80
- function mapObject(value, callback) {
81
- const result = {};
82
- forEachObject(value, (value2, key, index2) => {
83
- result[key] = callback(value2, key, index2);
84
- });
85
- return result;
86
- }
6
+ getVertexArrayObject,
7
+ isMatKind,
8
+ isSamplerKind,
9
+ kindToSize,
10
+ kindToUniformFnName,
11
+ mapObject,
12
+ once
13
+ } from "./chunk-BZDGEIOT.mjs";
87
14
 
88
15
  // src/index.ts
89
16
  var index = 0;
@@ -107,15 +34,33 @@ function toID(key) {
107
34
  }
108
35
  return key.toString();
109
36
  }
110
- function view(gl, program, schema, options) {
37
+ function view(gl, program, schema, { signal } = {}) {
38
+ const attributes = !schema.attributes ? void 0 : attributeView(gl, program, schema.attributes);
39
+ const interleavedAttributes = !schema.interleavedAttributes ? void 0 : interleavedAttributeView(gl, program, schema.interleavedAttributes);
40
+ const vertexArrays = /* @__PURE__ */ new Set();
41
+ const dispose = once(() => {
42
+ if (attributes) forEachObject(attributes, (value) => value.dispose());
43
+ if (interleavedAttributes) forEachObject(interleavedAttributes, (value) => value.dispose());
44
+ vertexArrays.forEach((vertexArray) => vertexArray.dispose());
45
+ vertexArrays.clear();
46
+ });
47
+ signal?.addEventListener("abort", dispose);
111
48
  return {
112
49
  uniforms: !schema.uniforms ? void 0 : uniformView(gl, program, schema.uniforms),
113
- attributes: !schema.attributes ? void 0 : attributeView(gl, program, schema.attributes, options),
114
- interleavedAttributes: !schema.interleavedAttributes ? void 0 : interleavedAttributeView(gl, program, schema.interleavedAttributes, options),
115
- buffers: !schema.buffers ? void 0 : bufferView(gl, schema.buffers),
50
+ attributes,
51
+ interleavedAttributes,
116
52
  vao(participants) {
117
- return vaoView(gl, participants, options);
118
- }
53
+ const vertexArray = vaoView(gl, participants);
54
+ vertexArrays.add(vertexArray);
55
+ return {
56
+ ...vertexArray,
57
+ dispose: once(() => {
58
+ vertexArrays.delete(vertexArray);
59
+ vertexArray.dispose();
60
+ })
61
+ };
62
+ },
63
+ dispose
119
64
  };
120
65
  }
121
66
  function bindDefaultVertexArray(gl) {
@@ -160,9 +105,7 @@ function vaoView(gl, participants, { signal } = {}) {
160
105
  unbind() {
161
106
  feature.bindVertexArray(null);
162
107
  },
163
- dispose() {
164
- feature.deleteVertexArray(vertexArray);
165
- }
108
+ dispose: once(() => feature.deleteVertexArray(vertexArray))
166
109
  };
167
110
  })();
168
111
  signal?.addEventListener("abort", () => methods.dispose());
@@ -170,9 +113,9 @@ function vaoView(gl, participants, { signal } = {}) {
170
113
  }
171
114
  function uniformView(gl, program, schema) {
172
115
  return mapObject(schema, ({ kind, size }, key) => {
173
- const name2 = toID(key);
116
+ const name = toID(key);
174
117
  if (isSamplerKind(kind)) {
175
- const location2 = gl.getUniformLocation(program, name2);
118
+ const location2 = gl.getUniformLocation(program, name);
176
119
  return {
177
120
  set(arg) {
178
121
  gl.uniform1i(location2, arg);
@@ -182,12 +125,12 @@ function uniformView(gl, program, schema) {
182
125
  if (size) {
183
126
  const bulkFnName = `uniform${kindToUniformFnName(kind)}v`;
184
127
  const bulkFn = gl[bulkFnName].bind(gl);
185
- const bulkLocation = gl.getUniformLocation(program, name2);
128
+ const bulkLocation = gl.getUniformLocation(program, name);
186
129
  const elementFnName = `uniform${kindToUniformFnName(kind)}`;
187
130
  const elementFn = gl[elementFnName].bind(gl);
188
131
  const elements = [];
189
132
  for (let i = 0; i < size; i++) {
190
- const location2 = gl.getUniformLocation(program, `${name2}[${i}]`);
133
+ const location2 = gl.getUniformLocation(program, `${name}[${i}]`);
191
134
  elements[i] = {
192
135
  set(...args) {
193
136
  elementFn(location2, ...args);
@@ -200,7 +143,7 @@ function uniformView(gl, program, schema) {
200
143
  }
201
144
  });
202
145
  }
203
- const location = gl.getUniformLocation(program, name2);
146
+ const location = gl.getUniformLocation(program, name);
204
147
  const fnName = `uniform${kindToUniformFnName(kind)}`;
205
148
  const fn = gl[fnName].bind(gl);
206
149
  if (isMatKind(kind)) {
@@ -260,28 +203,15 @@ var VERTEX_ARRAY_BINDING = 34229;
260
203
  function readDivisor(gl, location) {
261
204
  return getInstancedArrays(gl) ? gl.getVertexAttrib(location, VERTEX_ATTRIB_ARRAY_DIVISOR) : 0;
262
205
  }
263
- function once(restore) {
264
- let done = false;
265
- return () => {
266
- if (done) return;
267
- done = true;
268
- restore();
269
- };
270
- }
271
206
  function attributeView(gl, program, schema, { signal } = {}) {
272
207
  const attributes = mapObject(
273
208
  schema,
274
- ({
275
- kind,
276
- format,
277
- normalized = false,
278
- instanced,
279
- buffer = assertedNotNullish(gl.createBuffer())
280
- }, key) => {
281
- const name2 = toID(key);
282
- const location = gl.getAttribLocation(program, name2);
209
+ ({ kind, format, normalized = false, instanced, buffer: providedBuffer }, key) => {
210
+ const name = toID(key);
211
+ const buffer = providedBuffer ?? assertedNotNullish(gl.createBuffer());
212
+ const location = gl.getAttribLocation(program, name);
283
213
  if (location < 0) {
284
- throw new Error(`Attribute '${name2}' not found`);
214
+ throw new Error(`Attribute '${name}' not found`);
285
215
  }
286
216
  const size = kindToSize(kind);
287
217
  const resolvedFormat = format ?? defaultFormat(kind);
@@ -306,9 +236,11 @@ function attributeView(gl, program, schema, { signal } = {}) {
306
236
  restoreVertexArray();
307
237
  });
308
238
  },
309
- dispose() {
310
- gl.deleteBuffer(buffer);
311
- },
239
+ dispose: once(() => {
240
+ if (!providedBuffer) {
241
+ gl.deleteBuffer(buffer);
242
+ }
243
+ }),
312
244
  set(data, usage = "STATIC_DRAW") {
313
245
  gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
314
246
  gl.bufferData(gl.ARRAY_BUFFER, data, gl[usage]);
@@ -325,16 +257,16 @@ function attributeView(gl, program, schema, { signal } = {}) {
325
257
  function constantSource(gl, layout, locations) {
326
258
  const setters = layout.map((entry, index2) => {
327
259
  const size = kindToSize(entry.kind);
328
- const name2 = toID(entry.key);
260
+ const name = toID(entry.key);
329
261
  if (isMatKind(entry.kind) || size > 4) {
330
262
  throw new Error(
331
- `'${name2}' is a ${entry.kind}, which cannot be a constant attribute: vertexAttrib* takes at most 4 floats and has no matrix form. Use the buffer source for it.`
263
+ `'${name}' is a ${entry.kind}, which cannot be a constant attribute: vertexAttrib* takes at most 4 floats and has no matrix form. Use the buffer source for it.`
332
264
  );
333
265
  }
334
266
  let values = [];
335
267
  const fn = gl[`vertexAttrib${size}f`].bind(gl);
336
268
  return {
337
- name: name2,
269
+ name,
338
270
  location: locations[index2],
339
271
  write: () => {
340
272
  if (values.length) {
@@ -377,10 +309,10 @@ function interleavedAttributeView(gl, program, schema, { signal } = {}) {
377
309
  let index2 = 0;
378
310
  const locations = [];
379
311
  const handles = layout.map((layout2) => {
380
- const name2 = toID(layout2.key);
381
- const location = gl.getAttribLocation(program, name2);
312
+ const name = toID(layout2.key);
313
+ const location = gl.getAttribLocation(program, name);
382
314
  if (location < 0) {
383
- throw new Error(`Attribute '${name2}' not found`);
315
+ throw new Error(`Attribute '${name}' not found`);
384
316
  }
385
317
  locations.push(location);
386
318
  const size = kindToSize(layout2.kind);
@@ -448,9 +380,7 @@ function interleavedAttributeView(gl, program, schema, { signal } = {}) {
448
380
  get constant() {
449
381
  return constant ??= constantSource(gl, layout, locations);
450
382
  },
451
- dispose() {
452
- gl.deleteBuffer(buffer);
453
- }
383
+ dispose: once(() => gl.deleteBuffer(buffer))
454
384
  };
455
385
  });
456
386
  signal?.addEventListener("abort", function dispose() {
@@ -478,9 +408,7 @@ function bufferView(gl, schema, { signal } = {}) {
478
408
  restoreVertexArray();
479
409
  });
480
410
  },
481
- dispose() {
482
- gl.deleteBuffer(buffer);
483
- },
411
+ dispose: once(() => gl.deleteBuffer(buffer)),
484
412
  set(data) {
485
413
  gl.bindBuffer(gl[target], buffer);
486
414
  gl.bufferData(gl[target], data, gl[usage]);
@@ -494,7 +422,6 @@ function bufferView(gl, schema, { signal } = {}) {
494
422
  }
495
423
 
496
424
  export {
497
- createUpsertMap,
498
425
  toID,
499
426
  view,
500
427
  vaoView,
@@ -503,4 +430,4 @@ export {
503
430
  interleavedAttributeView,
504
431
  bufferView
505
432
  };
506
- //# sourceMappingURL=chunk-EYXOSM7B.mjs.map
433
+ //# sourceMappingURL=chunk-X4M3LV4R.mjs.map