@bigmistqke/view.gl 0.1.4 → 0.1.5

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.
@@ -0,0 +1 @@
1
+ README.md
package/README.md CHANGED
@@ -20,7 +20,7 @@
20
20
  - [🚀 Basic Usage](#-basic-usage-1)
21
21
  - [📝 glsl](#-glsl)
22
22
  - [🧩 GLSL Fragment](#-glsl-fragment)
23
- - [🔒 Symbol Scoping](#-symbol-scoping)
23
+ - [🔒 Symbol Variables](#-symbol-variables)
24
24
  - [WebGL Version Support](#webgl-version-support)
25
25
  - [⚙️ Resource Tags](#️-resource-tags)
26
26
  - [🎯 uniform[kind](name, options?)](#-uniformkindname-options)
@@ -52,8 +52,6 @@ The view system provides type-safe WebGL resource management for uniforms, attri
52
52
  ### 🚀 Basic Usage
53
53
 
54
54
  ```typescript
55
- import { view } from '@bigmistqke/view.gl'
56
-
57
55
  const { uniforms, attributes } = view(gl, program, {
58
56
  uniforms: {
59
57
  time: { kind: 'float' },
@@ -158,14 +156,6 @@ uniforms.lights.set(lightData) // Takes Float32Array for array uniforms
158
156
  **UniformSchema:**
159
157
  A mapping of uniform names to their configuration. Each uniform has a GLSL type and optional array size.
160
158
 
161
- ```typescript
162
- {
163
- time: { kind: 'float' },
164
- transform: { kind: 'mat4' },
165
- lights: { kind: 'vec3', size: 8 }, // Array uniform
166
- }
167
- ```
168
-
169
159
  - `kind`: GLSL type (see [Uniform Types](#-uniform-types) for full list and WebGL compatibility)
170
160
  - `size`: Array size (optional) - converts uniform to array type
171
161
 
@@ -194,7 +184,7 @@ type UniformKind =
194
184
 
195
185
  interface UniformOptions {
196
186
  kind: UniformKind
197
- size?: number // Creates array uniform with Float32Array setter
187
+ size?: number // Creates array uniform with Float32Array setter
198
188
  }
199
189
 
200
190
  type UniformSchema = Record<string | symbol, UniformOptions>
@@ -221,13 +211,6 @@ gl.drawArraysInstanced(gl.TRIANGLES, 0, 3, 100)
221
211
  **AttributeSchema:**
222
212
  A mapping of attribute names to their configuration. Each attribute has a GLSL type, optional instancing, and optional custom buffer.
223
213
 
224
- ```typescript
225
- {
226
- position: { kind: 'vec3' },
227
- instanceOffset: { kind: 'vec2', instanced: true },
228
- }
229
- ```
230
-
231
214
  - `kind`: GLSL type (see [Attribute Types](#-attribute-types) for full list and WebGL compatibility)
232
215
  - `instanced`: Boolean - enables instanced rendering
233
216
  - `buffer`: Custom WebGLBuffer (optional)
@@ -251,8 +234,8 @@ type AttributeKind =
251
234
 
252
235
  interface AttributeOptions {
253
236
  kind: AttributeKind
254
- instanced?: boolean // Enables vertexAttribDivisor
255
- buffer?: WebGLBuffer // Custom buffer, auto-created if not provided
237
+ instanced?: boolean // Enables vertexAttribDivisor
238
+ buffer?: WebGLBuffer // Custom buffer, auto-created if not provided
256
239
  }
257
240
 
258
241
  type AttributeSchema = Record<string | symbol, AttributeOptions>
@@ -281,19 +264,7 @@ interleavedAttributes.vertexData.set(interleavedVertexData).bind()
281
264
  **InterleavedAttributeSchema:**
282
265
  A mapping of interleaved buffer names to their layout configuration. Each layout defines multiple attributes packed into a single buffer.
283
266
 
284
- ```typescript
285
- {
286
- vertexData: {
287
- layout: [
288
- { key: 'position', kind: 'vec3' },
289
- { key: 'normal', kind: 'vec3' },
290
- { key: 'uv', kind: 'vec2' },
291
- ],
292
- },
293
- }
294
- ```
295
-
296
- - `layout`: Array of attribute definitions with `key` and `kind` (see [Attribute Types](#-attribute-types) for WebGL compatibility)
267
+ - `layout`: Array of attribute definitions with `key` and `kind` (see [Attribute Types](#-attribute-types))
297
268
  - `instanced`: Boolean - applies to all attributes in layout
298
269
 
299
270
  <details>
@@ -305,8 +276,8 @@ interface InterleavedAttributeOptions {
305
276
  key: string | symbol
306
277
  kind: AttributeKind
307
278
  }>
308
- instanced?: boolean // Applies vertexAttribDivisor to all attributes
309
- buffer?: WebGLBuffer // Custom buffer for interleaved data
279
+ instanced?: boolean // Applies vertexAttribDivisor to all attributes
280
+ buffer?: WebGLBuffer // Custom buffer for interleaved data
310
281
  }
311
282
 
312
283
  type InterleavedAttributeSchema = Record<string | symbol, InterleavedAttributeOptions>
@@ -331,13 +302,6 @@ buffers.data.set(dynamicData).bind()
331
302
  **BufferSchema:**
332
303
  A mapping of buffer names to their configuration. Each buffer has a target type and optional usage pattern.
333
304
 
334
- ```typescript
335
- {
336
- indices: { target: 'ELEMENT_ARRAY_BUFFER' },
337
- data: { target: 'ARRAY_BUFFER', usage: 'DYNAMIC_DRAW' },
338
- }
339
- ```
340
-
341
305
  - `target`: Buffer target (`'ARRAY_BUFFER'`, `'ELEMENT_ARRAY_BUFFER'`)
342
306
  - `usage`: Usage pattern (`'STATIC_DRAW'`, `'DYNAMIC_DRAW'`, `'STREAM_DRAW'`)
343
307
 
@@ -361,25 +325,21 @@ Type-safe GLSL template literals with automatic schema extraction.
361
325
 
362
326
  - **Embedded Resources**: Define uniforms, attributes, and interleaved layouts directly in GLSL
363
327
  - **Type Inference**: Automatically infers schema types and creates type-safe view
364
- - **Scoped Variables**: Prevent naming collisions using symbols for private shader variables
328
+ - **Unique Variables**: Prevent naming collisions using symbols for unique shader variables
365
329
  - **GLSL Composition**: Compose reusable GLSL fragments with automatic dependency resolution
366
330
 
367
331
  ### 🚀 Basic Usage
368
332
 
369
333
  ```typescript
370
- import { glsl, uniform, attribute, compile } from '@bigmistqke/view.gl/tag'
371
-
372
334
  const vertexShader = glsl`
373
335
  ${attribute.vec3('position')}
374
- ${attribute.vec2('uv')}
375
- ${uniform.mat4('projection')}
376
336
  ${uniform.mat4('model')}
377
337
 
378
338
  varying vec2 vUv;
379
339
 
380
340
  void main() {
381
341
  vUv = uv;
382
- gl_Position = projection * model * vec4(position, 1.0);
342
+ gl_Position = model * vec4(position, 1.0);
383
343
  }
384
344
  `
385
345
 
@@ -401,15 +361,23 @@ const { program, schema } = compile(gl, vertexShader, fragmentShader)
401
361
  Template literal processor that handles GLSL code and embedded resources. Supports interpolation of:
402
362
 
403
363
  - **[Resource tags](#️-resource-tags)**: `uniform.*()`, `attribute.*()`, `interleave()`
404
- - **[Composed GLSL fragments](#-glsl-fragment)**: Reusable shader code snippets
405
- - **[Symbols](#-symbol-scoping)**: Scoped variable names to prevent collisions
364
+ - **[GLSL fragments](#-glsl-fragment)**: Reusable shader code snippets
365
+ - **[Symbol Variables](#-symbol-variables)**: Unique variable names to prevent collisions
366
+ - **Strings**: Interpolated as-is into the shader code
367
+ - **Arrays**: Arrays of any supported interpolation types
406
368
 
407
369
  ```typescript
408
- import { glsl, uniform, attribute } from '@bigmistqke/view.gl/tag'
370
+ const precision = 'precision mediump float;'
371
+ const functionName = Symbol('function')
409
372
 
410
373
  const shader = glsl`
374
+ ${precision} // String interpolated as-is
411
375
  ${uniform.vec2('resolution')}
412
- ${attribute.vec3('position')}
376
+ ${[attribute.vec3('position'), attribute.vec2('uv')]} // Array interpolation
377
+
378
+ vec3 ${functionName}(vec2 uv) { // Symbol interpolated to unique identifier
379
+ return vec3(uv, 0.5);
380
+ }
413
381
 
414
382
  void main() {
415
383
  gl_Position = vec4(position, 1.0);
@@ -430,18 +398,19 @@ const lighting = glsl`
430
398
  `
431
399
 
432
400
  const vertexShader = glsl`
433
- ${attribute.vec3('position')}
401
+ ${attribute.vec3('direction')}
434
402
  ${attribute.vec3('normal')}
435
- ${lighting} // Include the lighting fragment
403
+ ${lighting} // Include the lighting fragment
404
+
405
+ varying vec3 vLighting;
436
406
 
437
407
  void main() {
438
- vec3 light = calculateLighting(normal, vec3(0.0, 1.0, 0.0));
439
- // Use the composed function
408
+ vLighting = calculateLighting(normal, direction);
440
409
  }
441
410
  `
442
411
  ```
443
412
 
444
- #### 🔒 Symbol Scoping
413
+ #### 🔒 Symbol Variables
445
414
 
446
415
  Use JavaScript symbols to prevent naming collisions:
447
416
 
@@ -462,7 +431,7 @@ void main(){
462
431
  `
463
432
  ```
464
433
 
465
- Symbols are converted to unique identifiers in the shader.
434
+ Symbols are converted to unique identifiers during the compilation of the shader.
466
435
 
467
436
  #### WebGL Version Support
468
437
 
@@ -471,7 +440,7 @@ The glsl-function supports both WebGL1 and WebGL2 syntax, automatically using th
471
440
  ```typescript
472
441
  // WebGL1 (default)
473
442
  const shader = glsl`
474
- ${attribute.vec3('position')} // → attribute vec3 position;
443
+ ${attribute.vec3('position')} // → attribute vec3 position;
475
444
  varying vec2 vUv;
476
445
  `
477
446
  ```
@@ -481,7 +450,7 @@ If the shader starts with `#version 300 es`, resource tags generate WebGL2 synta
481
450
  ```typescript
482
451
  // WebGL2
483
452
  const shader = glsl`#version 300 es
484
- ${attribute.vec3('position')} // → in vec3 position;
453
+ ${attribute.vec3('position')} // → in vec3 position;
485
454
  out vec2 vUv;
486
455
  `
487
456
  ```
@@ -492,48 +461,43 @@ Functions for defining WebGL resources directly in GLSL templates. These tags ge
492
461
 
493
462
  #### 🎯 uniform[kind](name, options?)
494
463
 
495
- Define uniform variables in GLSL templates. Supports all GLSL uniform types (see [Uniform Types](#-uniform-types) for WebGL version compatibility).
464
+ Define uniform variables in GLSL templates (see [Uniform Types](#-uniform-types)).
496
465
 
497
466
  ```typescript
498
- import { uniform, glsl } from '@bigmistqke/view.gl/tag'
467
+ const uniqueTime = Symbol('time')
499
468
 
500
469
  const shader = glsl`
501
- ${uniform.float('time')}
502
- ${uniform.vec3('lights', { size: 8 })} // Array uniform: vec3[8]
470
+ ${uniform.float('time')} // String key
471
+ ${uniform.float(uniqueTime)} // Symbol key
472
+ ${uniform.vec3('lights', { size: 8 })} // Array uniform: vec3[8]
503
473
 
504
474
  void main() {
505
- // Use uniforms in shader code
475
+ float wave = sin(time * 2.0 + ${uniqueTime});
476
+ vec3 totalLight = vec3(0.0);
477
+ for(int i = 0; i < 8; i++) {
478
+ totalLight += lights[i] * wave;
479
+ }
480
+ gl_FragColor = vec4(totalLight, 1.0);
506
481
  }
507
482
  `
508
483
  ```
509
484
 
510
- **Options**:
485
+ **Parameters**:
511
486
 
512
- - `size`: Array size (creates array uniform with Float32Array setter)
487
+ - `name`: Uniform name (string or symbol for unique variables)
488
+ - `options`: Optional configuration object
489
+ - `size`: Array size (creates array uniform with Float32Array setter)
513
490
 
514
491
  #### 📝 attribute[kind](name, options?)
515
492
 
516
- Define vertex attributes in GLSL templates (see [Attribute Types](#-attribute-types) for WebGL version compatibility).
517
-
518
- **Regular rendering:**
493
+ Define vertex attributes in GLSL templates (see [Attribute Types](#-attribute-types)).
519
494
 
520
495
  ```typescript
521
- import { attribute, glsl } from '@bigmistqke/view.gl/tag'
496
+ const uniquePosition = Symbol('position')
522
497
 
523
498
  const vertexShader = glsl`
524
- ${attribute.vec3('position')}
525
-
526
- void main() {
527
- gl_Position = vec4(position, 1.0);
528
- }
529
- `
530
- ```
531
-
532
- **Instanced rendering:**
533
-
534
- ```typescript
535
- const vertexShader = glsl`
536
- ${attribute.vec3('position')}
499
+ ${attribute.vec3('position')} // String key
500
+ ${attribute.vec3(uniquePosition)} // Symbol key
537
501
  ${attribute.vec2('offset', { instanced: true })}
538
502
 
539
503
  void main() {
@@ -542,23 +506,32 @@ const vertexShader = glsl`
542
506
  `
543
507
  ```
544
508
 
545
- **Options**:
509
+ **Parameters**:
546
510
 
547
- - `instanced`: Boolean - enables instanced rendering with `vertexAttribDivisor`
548
- - `buffer`: Custom WebGLBuffer (optional)
511
+ - `name`: Attribute name (string or symbol for unique variables)
512
+ - `options`: Optional configuration object
513
+ - `instanced`: Boolean - enables instanced rendering with `vertexAttribDivisor`
514
+ - `buffer`: Custom WebGLBuffer (optional)
549
515
 
550
516
  #### 🔗 interleave(name, layout, options?)
551
517
 
552
518
  Define interleaved attribute layouts for efficient vertex data.
553
519
 
554
520
  ```typescript
555
- import { interleave, glsl } from '@bigmistqke/view.gl/tag'
521
+ const uniqueVertexData = Symbol('vertexData')
522
+ const uniquePosition = Symbol('position')
556
523
 
557
524
  const vertexShader = glsl`
558
525
  ${interleave('vertexData', [
526
+ // String key
559
527
  { key: 'position', kind: 'vec3' },
560
528
  { key: 'uv', kind: 'vec2' },
561
529
  ])}
530
+ ${interleave(uniqueVertexData, [
531
+ // Symbol key
532
+ { key: uniquePosition, kind: 'vec3' }, // Symbol keys in layout
533
+ { key: 'uv', kind: 'vec2' },
534
+ ])}
562
535
 
563
536
  void main() {
564
537
  gl_Position = vec4(position + vec3(uv, 0.0), 1.0);
@@ -566,22 +539,20 @@ const vertexShader = glsl`
566
539
  `
567
540
  ```
568
541
 
569
- **Layout format**:
542
+ **Parameters**:
570
543
 
571
- - `key`: Attribute name
572
- - `kind`: GLSL type (see [Attribute Types](#-attribute-types) for WebGL compatibility)
573
-
574
- **Options**:
575
-
576
- - `instanced`: Boolean - applies `vertexAttribDivisor` to all attributes
544
+ - `name`: Buffer name (string or symbol for unique variables)
545
+ - `layout`: Array of attribute definitions
546
+ - `key`: Attribute name (string or symbol for unique variables)
547
+ - `kind`: GLSL type (see [Attribute Types](#-attribute-types))
548
+ - `options`: Optional configuration object
549
+ - `instanced`: Boolean - applies `vertexAttribDivisor` to all attributes
577
550
 
578
551
  ### ⚙️ compile(gl, vertex, fragment, overrideSchema?)
579
552
 
580
553
  Compiles shaders and extracts schema from embedded resources. Returns both the compiled program and a ready-to-use view.
581
554
 
582
555
  ```typescript
583
- import { compile } from '@bigmistqke/view.gl/tag'
584
-
585
556
  const { program, schema, view } = compile(gl, vertexShader, fragmentShader)
586
557
 
587
558
  // Use the view directly
@@ -589,8 +560,8 @@ view.uniforms.time.set(performance.now())
589
560
  view.attributes.position.set(vertexData).bind()
590
561
 
591
562
  // Or access the extracted schema
592
- console.log(schema.uniforms) // { time: { kind: 'float' }, ... }
593
- console.log(schema.attributes) // { position: { kind: 'vec3' }, ... }
563
+ console.log(schema.uniforms) // { time: { kind: 'float' }, ... }
564
+ console.log(schema.attributes) // { position: { kind: 'vec3' }, ... }
594
565
  ```
595
566
 
596
567
  **Override Schema:**
@@ -623,11 +594,9 @@ view.buffers.indices.set(indexData).bind()
623
594
  <details>
624
595
  <summary>Usage with vanilla view.gl</summary>
625
596
 
626
- When manually constructing GLSL strings, use `toID()` to convert symbols to valid identifiers:
597
+ When manually constructing GLSL strings, use `toID()` to convert symbols to valid, unique identifiers:
627
598
 
628
599
  ```typescript
629
- import { createProgram, view, toID } from 'view.gl'
630
-
631
600
  const u_time = Symbol('time')
632
601
  const a_position = Symbol('position')
633
602
 
@@ -669,8 +638,6 @@ uniforms[u_time].set(performance.now())
669
638
  Creates and links a WebGL program from vertex and fragment shader sources.
670
639
 
671
640
  ```typescript
672
- import { createProgram } from '@bigmistqke/view.gl/utils'
673
-
674
641
  const program = createProgram(gl, vertexShaderSource, fragmentShaderSource)
675
642
  ```
676
643
 
@@ -679,8 +646,6 @@ const program = createProgram(gl, vertexShaderSource, fragmentShaderSource)
679
646
  Creates a WebGL texture with specified parameters.
680
647
 
681
648
  ```typescript
682
- import { createTexture } from '@bigmistqke/view.gl/utils'
683
-
684
649
  const texture = createTexture(
685
650
  gl,
686
651
  {
@@ -705,8 +670,6 @@ Automatically validates WebGL2-only formats and provides fallbacks for WebGL1.
705
670
  Creates a framebuffer with attached texture for render-to-texture operations.
706
671
 
707
672
  ```typescript
708
- import { createFramebuffer } from '@bigmistqke/view.gl/utils'
709
-
710
673
  const { framebuffer, texture } = createFramebuffer(gl, {
711
674
  width: 512,
712
675
  height: 512,
@@ -78,7 +78,7 @@ const fragment = glsl`
78
78
  precision highp float;
79
79
 
80
80
  ${colors}
81
- ${uniform.float('u_time')}
81
+ ${[uniform.float('u_time')]}
82
82
 
83
83
  void main() {
84
84
  // Animated rainbow color
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { V as ViewSchema, G as GL, a as ViewOptions, b as View, U as UniformSchema, c as UniformView, A as AttributeSchema, d as AttributeView, I as InterleavedAttributeSchema, e as InterleavedAttributeView, B as BufferSchema, f as BufferView } from './types-CGTxl3Eq.mjs';
1
+ import { V as ViewSchema, G as GL, a as ViewOptions, b as View, U as UniformSchema, c as UniformView, A as AttributeSchema, d as AttributeView, I as InterleavedAttributeSchema, e as InterleavedAttributeView, B as BufferSchema, f as BufferView } from './types-DIIqZ8e6.mjs';
2
2
 
3
3
  declare function toID(key: string | number | symbol): any;
4
4
  /**********************************************************************************/
package/dist/tag.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { g as GLSLSlot, h as GLSLTag, i as UniformTagFn, j as AttributeTagFn, k as AttributeTag, l as AttributeKind, m as AttributeOptions, n as InterleaveTag, o as ViewSchemaPartial, V as ViewSchema, D as DeepMerge, S as ShallowMerge, p as UniformTag, P as Prettify, b as View } from './types-CGTxl3Eq.mjs';
1
+ import { g as GLSLSlot, h as GLSLTag, i as UniformTagFn, j as AttributeTagFn, k as AttributeTag, l as AttributeKind, m as AttributeOptions, n as InterleaveTag, o as ViewSchemaPartial, V as ViewSchema, D as DeepMerge, S as ShallowMerge, p as UniformTag, P as Prettify, b as View } from './types-DIIqZ8e6.mjs';
2
2
 
3
3
  declare function glsl<TSlot extends GLSLSlot, TSlots extends TSlot[]>(template: TemplateStringsArray, ...slots: [...TSlots]): GLSLTag<TSlots>;
4
4
  declare const uniform: {
package/dist/tag.mjs CHANGED
@@ -439,9 +439,13 @@ function glslTagToSchema(tag) {
439
439
  attributes: {},
440
440
  interleavedAttributes: {}
441
441
  };
442
- for (const slot of tag.slots) {
442
+ function handleSlot(slot) {
443
+ if (Array.isArray(slot)) {
444
+ slot.forEach(handleSlot);
445
+ return;
446
+ }
443
447
  if (typeof slot !== "object") {
444
- continue;
448
+ return;
445
449
  }
446
450
  if (slot.type === "glsl") {
447
451
  const { uniforms, attributes, interleavedAttributes } = glslTagToSchema(slot);
@@ -457,11 +461,12 @@ function glslTagToSchema(tag) {
457
461
  ...result.interleavedAttributes,
458
462
  ...interleavedAttributes
459
463
  };
460
- continue;
464
+ return;
461
465
  }
462
466
  const { key: name2, type, ...rest } = slot;
463
467
  result[`${type}s`][name2] = rest;
464
468
  }
469
+ tag.slots.forEach(handleSlot);
465
470
  return result;
466
471
  }
467
472
  function glslTagToString({ template: [initial, ...rest], slots }) {
@@ -476,6 +481,9 @@ function glslSlotToString(slot, v300) {
476
481
  if (typeof slot !== "object") {
477
482
  return toID(slot);
478
483
  }
484
+ if (Array.isArray(slot)) {
485
+ return slot.map((slot2) => glslSlotToString(slot2, v300)).join("");
486
+ }
479
487
  if (slot.type === "glsl") {
480
488
  return glslTagToString(slot);
481
489
  }
package/dist/tag.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/features.ts","../src/utils/object.ts","../src/utils/index.ts","../src/index.ts","../src/tag.ts"],"sourcesContent":["import type { GL, RemoveSuffix } from './types'\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 (gl instanceof WebGL2RenderingContext) 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\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 (gl instanceof WebGL2RenderingContext) 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","export function forEach<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 map<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 forEach(value, (value, key, index) => {\n result[key] = callback(value, key, index)\n })\n\n return result\n}\n","import type {\n AttributeKind,\n FramebufferOptions,\n GL,\n KIND_TO_SIZE_MAP,\n KIND_TO_UNIFORM_FN_NAME_MAP,\n TextureOptions,\n UniformKind,\n} from '../types'\n\nexport * as ObjectUtils from './object'\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\nfunction createGLShader(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\nexport function createProgram(gl: GL, vertexSource: string, fragmentSource: string): WebGLProgram {\n const program = gl.createProgram()\n if (!program) throw new Error('Failed to create WebGL program')\n\n const vertexShader = createGLShader(gl, gl.VERTEX_SHADER, vertexSource)\n const fragmentShader = createGLShader(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 return program\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 }: TextureOptions,\n data?: ArrayBufferView | null,\n): WebGLTexture {\n const texture = gl.createTexture()\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 console.log(minFilter)\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 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 '${name}' 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(gl: GL, { attachment, texture, ...options }: FramebufferOptions) {\n // Create texture for the framebuffer\n texture ??= createTexture(gl, options)\n\n // Create framebuffer\n const framebuffer = assertedNotNullish(\n gl.createFramebuffer(),\n `Failed to create framebuffer: ${name}`,\n )\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 return {\n texture,\n framebuffer,\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","import { getInstancedArrays, getVertexArrayObject } from './features'\nimport type {\n AttributeMethods,\n AttributeSchema,\n AttributeView,\n BufferSchema,\n BufferView,\n GL,\n UniformView as InferUniformView,\n InterleavedAttributeMethods,\n InterleavedAttributeSchema,\n InterleavedAttributeView,\n UniformSchema,\n View,\n ViewOptions,\n ViewSchema,\n} from './types'\nimport {\n assertedNotNullish,\n createUpsertMap,\n isMatKind,\n isSamplerKind,\n kindToSize,\n kindToUniformFnName,\n ObjectUtils,\n} from './utils'\n\n/**********************************************************************************/\n/* */\n/* To ID */\n/* */\n/**********************************************************************************/\n\nlet index = 0\nconst PREFIX = 'VIEW_GL_ALIAS'\n\nconst SYMBOL_MAP = createUpsertMap(WeakMap<symbol, string>)\n\nexport function toID(key: string | number | symbol) {\n if (typeof key === 'string') {\n return key\n }\n if (typeof key === 'symbol') {\n return SYMBOL_MAP.getOrInsert(key, () => `${PREFIX}_${index++}`)\n }\n return key.toString()\n}\n\n/**********************************************************************************/\n/* */\n/* View */\n/* */\n/**********************************************************************************/\n\nexport function view<TSchema extends ViewSchema>(\n gl: GL,\n program: WebGLProgram,\n schema: TSchema,\n options?: ViewOptions,\n): View<TSchema> {\n return {\n uniforms: !schema.uniforms ? undefined : uniformView(gl, program, schema.uniforms),\n attributes: !schema.attributes\n ? undefined\n : attributeView(gl, program, schema.attributes, options),\n interleavedAttributes: !schema.interleavedAttributes\n ? undefined\n : interleavedAttributeView(gl, program, schema.interleavedAttributes, options),\n buffers: !schema.buffers ? undefined : bufferView(gl, schema.buffers),\n } as View<TSchema>\n}\n\n/**********************************************************************************/\n/* */\n/* Uniform View */\n/* */\n/**********************************************************************************/\n\nexport function uniformView<T extends UniformSchema>(\n gl: GL,\n program: WebGLProgram,\n schema: T,\n): InferUniformView<T> {\n return ObjectUtils.map(schema, ({ kind, size }, key) => {\n const name = toID(key)\n\n const location = gl.getUniformLocation(program, name)\n\n if (isSamplerKind(kind)) {\n return {\n set(arg: number) {\n gl.uniform1i(location, arg)\n },\n }\n }\n\n const fnName = `uniform${kindToUniformFnName(kind)}${size ? 'v' : ''}`\n\n // @ts-ignore FIX WEBGL/WEBGL2 TYPES\n const fn = gl[fnName].bind(gl)\n\n if (isMatKind(kind)) {\n return {\n set(...args: any[]) {\n fn(location, false, args[0])\n },\n }\n }\n\n return {\n set(...args: any[]) {\n fn(location, ...args)\n },\n }\n })\n}\n\n/**********************************************************************************/\n/* */\n/* Attribute View */\n/* */\n/**********************************************************************************/\n\n// Shared attribute helper functions\n// between attributeView and interleavedAttributeView\nfunction handleAttribute(\n gl: GL,\n location: number,\n size: number,\n stride: number,\n offset: number,\n type: 'FLOAT' | 'INT',\n instanced?: boolean,\n) {\n gl.enableVertexAttribArray(location)\n gl.vertexAttribPointer(location, size, gl[type], false, stride, offset)\n\n if (instanced) {\n // Get instanced-arrays-feature: extension if webgl, gl if webgl2\n assertedNotNullish(getInstancedArrays(gl)).vertexAttribDivisor(location, 1)\n }\n}\n\nexport function attributeView<T extends AttributeSchema>(\n gl: GL,\n program: WebGLProgram,\n schema: T,\n { signal }: ViewOptions = {},\n): AttributeView<T> {\n const attributes = ObjectUtils.map(\n schema,\n (\n { kind, instanced, buffer = assertedNotNullish(gl.createBuffer()) },\n key,\n ): AttributeMethods => {\n const name = toID(key)\n\n const location = gl.getAttribLocation(program, name)\n if (location < 0) {\n throw new Error(`Attribute '${name}' not found`)\n }\n\n const size = kindToSize(kind)\n const type = kind.startsWith('i') ? 'INT' : 'FLOAT'\n\n return {\n buffer,\n bind() {\n gl.bindBuffer(gl.ARRAY_BUFFER, buffer)\n handleAttribute(gl, location, size, 0, 0, type, instanced)\n },\n dispose() {\n gl.deleteBuffer(buffer)\n },\n set(data, usage = 'STATIC_DRAW') {\n gl.bindBuffer(gl.ARRAY_BUFFER, buffer)\n gl.bufferData(gl.ARRAY_BUFFER, data, gl[usage])\n return this\n },\n }\n },\n )\n\n signal?.addEventListener('abort', function dispose() {\n ObjectUtils.forEach(attributes, value => value.dispose())\n })\n\n return attributes\n}\n\n/**********************************************************************************/\n/* */\n/* Interleaved Attribute View */\n/* */\n/**********************************************************************************/\n\nexport function interleavedAttributeView<T extends InterleavedAttributeSchema>(\n gl: GL,\n program: WebGLProgram,\n schema: T,\n { signal }: ViewOptions = {},\n): InterleavedAttributeView<T> {\n // Initialize interleaved attributes\n const interleavedAttributes = ObjectUtils.map(schema, ({ layout, instanced }) => {\n // Increment number to keep track of offset\n let index = 0\n\n // Calculate layout information\n const handles = layout.map(layout => {\n const name = toID(layout.key)\n\n const location = gl.getAttribLocation(program, name)\n\n if (location < 0) {\n throw new Error(`Attribute '${name}' not found`)\n }\n\n const size = kindToSize(layout.kind)\n const offset = index\n const type = layout.kind.startsWith('i') ? 'INT' : 'FLOAT'\n index += size * 4\n\n return () => handleAttribute(gl, location, size, stride, offset, type, instanced)\n })\n\n // Set stride to final index\n const stride = index\n\n // Create a buffer\n const buffer = assertedNotNullish(gl.createBuffer())\n\n // Create VAO to cache attribute state\n let vao: { unbind(): void; bind(): void; dispose(): void } | undefined = undefined\n\n // Get VAO-feature: extension if webgl1, gl if webgl2\n const feature = getVertexArrayObject(gl)\n if (feature) {\n const vertexArray = feature.createVertexArray()\n feature.bindVertexArray(vertexArray)\n gl.bindBuffer(gl.ARRAY_BUFFER, buffer)\n for (const handle of handles) {\n handle()\n }\n feature.bindVertexArray(null)\n vao = {\n unbind() {\n feature.bindVertexArray(null)\n },\n bind() {\n feature.bindVertexArray(vertexArray)\n },\n dispose() {\n feature.deleteVertexArray(vertexArray)\n },\n }\n }\n\n return {\n bind() {\n gl.bindBuffer(gl.ARRAY_BUFFER, buffer)\n if (vao) {\n vao.bind()\n } else {\n // Fallback: manual attribute setup\n for (const handle of handles) {\n handle()\n }\n }\n },\n unbind() {\n if (vao) {\n vao.unbind()\n }\n },\n dispose() {\n gl.deleteBuffer(buffer)\n if (vao) {\n vao.dispose()\n }\n },\n set(value, usage = 'STATIC_DRAW') {\n if (vao) {\n vao.bind()\n } else {\n gl.bindBuffer(gl.ARRAY_BUFFER, buffer)\n }\n gl.bufferData(gl.ARRAY_BUFFER, value, gl[usage])\n if (vao) {\n vao.unbind()\n }\n },\n } satisfies InterleavedAttributeMethods\n })\n\n signal?.addEventListener('abort', function dispose() {\n ObjectUtils.forEach(interleavedAttributes, value => value.dispose())\n })\n\n return interleavedAttributes\n}\n\n/**********************************************************************************/\n/* */\n/* Buffer View */\n/* */\n/**********************************************************************************/\n\nexport function bufferView<T extends BufferSchema>(\n gl: GL,\n schema: T,\n { signal }: ViewOptions = {},\n): BufferView<T> {\n // Initialize buffers\n const buffers = ObjectUtils.map(schema, ({ target = 'ARRAY_BUFFER', usage = 'STATIC_DRAW' }) => {\n const buffer = assertedNotNullish(gl.createBuffer())\n return {\n bind() {\n gl.bindBuffer(gl[target], buffer)\n },\n dispose() {\n gl.deleteBuffer(buffer)\n },\n set(data: Float32Array) {\n gl.bindBuffer(gl[target], buffer)\n gl.bufferData(gl[target], data, gl[usage])\n },\n }\n })\n\n signal?.addEventListener('abort', function dispose() {\n ObjectUtils.forEach(buffers, value => value.dispose())\n })\n\n return buffers\n}\n","import { toID, view } from '.'\nimport type {\n AttributeKind,\n AttributeOptions,\n AttributeTag,\n AttributeTagFn as AttributeTagMethod,\n DeepMerge,\n GLSLSlot,\n GLSLTag,\n InterleaveTag,\n Prettify,\n ShallowMerge,\n UniformKind,\n UniformOptions,\n UniformTag,\n UniformTagFn as UniformTagMethod,\n View,\n ViewSchema,\n ViewSchemaPartial,\n} from './types'\nimport { createProgram } from './utils'\n\nexport function glsl<TSlot extends GLSLSlot, TSlots extends TSlot[]>(\n template: TemplateStringsArray,\n ...slots: [...TSlots]\n): GLSLTag<TSlots> {\n return { template, slots, type: 'glsl' }\n}\n\nexport const uniform = new Proxy(\n {} as {\n [TKey in UniformKind]: UniformTagMethod<TKey>\n },\n {\n get(target, property) {\n // @ts-expect-error\n if (typeof property === 'symbol') return target[property]\n return (key: string, options?: Omit<UniformOptions, 'kind'>) => ({\n type: 'uniform',\n key,\n kind: property,\n ...options,\n })\n },\n },\n)\n\nexport const attribute = new Proxy(\n {} as {\n [TKey in AttributeKind]: AttributeTagMethod<TKey>\n },\n {\n get(target, property) {\n // @ts-expect-error\n if (typeof property === 'symbol') return target[property]\n return (key: string, options?: Omit<AttributeOptions, 'kind'>) => ({\n type: 'attribute',\n key,\n kind: property,\n ...options,\n })\n },\n },\n)\n\nexport function interleave<\n const TKey extends string | symbol,\n const TLayout extends Array<AttributeTag<string | symbol, AttributeKind, undefined>>,\n const TOptions extends Omit<AttributeOptions, 'kind'>,\n>(key: TKey, layout: TLayout, instanced: TOptions): InterleaveTag<TKey, TLayout, TOptions>\nexport function interleave<\n const TKey extends string | symbol,\n const TLayout extends Array<AttributeTag<string | symbol, AttributeKind, undefined>>,\n>(key: TKey, layout: TLayout): InterleaveTag<TKey, TLayout, { instanced: false }>\nexport function interleave<\n const TKey extends string | symbol,\n const TLayout extends Array<AttributeTag<string | symbol, AttributeKind, undefined>>,\n const TOptions extends Omit<AttributeOptions, 'kind'>,\n>(key: TKey, layout: TLayout, { instanced, buffer }: Partial<TOptions> = {}) {\n return {\n type: 'interleavedAttribute',\n key,\n // remove instanced- and type-property\n layout: layout.map(({ key, kind }) => ({\n key,\n kind,\n })),\n instanced: !!instanced,\n buffer,\n } as unknown as InterleaveTag<TKey, TLayout, TOptions>\n}\n\n/**********************************************************************************/\n/* */\n/* Compile */\n/* */\n/**********************************************************************************/\n\ntype glslTagToSchema<TTag extends GLSLTag> =\n FlattenSlots<TTag> extends infer TSlots\n ? TSlots extends Array<any>\n ? GLSLSlotsToSchema<TSlots>\n : never\n : never\n\ntype GLSLSlotsToSchema<TSlots extends Array<GLSLSlot>> = {\n uniforms: {\n [K in Extract<TSlots[number], UniformTag> as K['key']]: Prettify<Omit<K, 'name' | 'type'>>\n }\n attributes: {\n [K in Extract<TSlots[number], AttributeTag> as K['key']]: Prettify<Omit<K, 'name' | 'type'>>\n }\n interleavedAttributes: {\n [K in Extract<TSlots[number], InterleaveTag> as K['key']]: Prettify<Omit<K, 'name' | 'type'>>\n }\n}\n\ntype FlattenSlots<T> =\n T extends GLSLTag<infer TSlots>\n ? _FlattenSlots<TSlots>\n : T extends Array<GLSLSlot>\n ? _FlattenSlots<T>\n : never\n\ntype _FlattenSlots<T extends Array<GLSLSlot>> = T extends [infer First, ...infer Rest]\n ? First extends GLSLTag\n ? [...FlattenSlots<First>, ...FlattenSlots<Rest>]\n : First extends unknown[]\n ? [...FlattenSlots<First>, ...FlattenSlots<Rest>]\n : [First, ...FlattenSlots<Rest>]\n : []\n\ntype MergeGLSLSchema<\n TVertex extends ViewSchema,\n TFragment extends ViewSchema,\n TOverrides extends ViewSchemaPartial,\n> = {\n uniforms: DeepMerge<\n [ShallowMerge<[TVertex['uniforms'], TFragment['uniforms']]>, TOverrides['uniforms']]\n >\n attributes: DeepMerge<\n [ShallowMerge<[TVertex['attributes'], TFragment['attributes']]>, TOverrides['attributes']]\n >\n interleavedAttributes: DeepMerge<\n [\n ShallowMerge<[TVertex['interleavedAttributes'], TFragment['interleavedAttributes']]>,\n TOverrides['interleavedAttributes'],\n ]\n >\n}\n\ntype CompileResult<\n TVertex extends GLSLTag,\n TFragment extends GLSLTag,\n TOverrides extends ViewSchemaPartial,\n> =\n MergeGLSLSchema<\n glslTagToSchema<TVertex>,\n glslTagToSchema<TFragment>,\n TOverrides\n > extends infer TSchema extends ViewSchema\n ? {\n program: WebGLProgram\n schema: TSchema\n view: View<TSchema>\n }\n : never\n\nexport function compile<\n TVertex extends GLSLTag,\n TFragment extends GLSLTag,\n TOverride extends ViewSchemaPartial,\n>(gl: WebGLRenderingContext, vertex: TVertex, fragment: TFragment, overrideSchema?: TOverride) {\n const _vertex = resolveGLSLTag(vertex)\n const _fragment = resolveGLSLTag(fragment)\n\n const schema = {\n uniforms: {\n ..._vertex.schema.uniforms,\n ..._fragment.schema.uniforms,\n },\n attributes: {\n ..._vertex.schema.attributes,\n ..._fragment.schema.attributes,\n },\n interleavedAttributes: {\n ..._vertex.schema.interleavedAttributes,\n ..._fragment.schema.interleavedAttributes,\n },\n }\n\n for (const kind in overrideSchema) {\n const schemaKind = schema[kind as keyof typeof schema]\n const overrideSchemaKind = overrideSchema[kind]\n\n for (const key in overrideSchemaKind) {\n // @ts-expect-error\n schemaKind[key] = {\n // @ts-expect-error\n ...schemaKind[key],\n ...overrideSchemaKind[key],\n }\n }\n }\n\n const program = createProgram(gl, _vertex.template, _fragment.template)\n\n return {\n program,\n schema,\n view: view(gl, program, schema),\n } as CompileResult<TVertex, TFragment, TOverride>\n}\n\nfunction resolveGLSLTag<TTag extends GLSLTag>(tag: TTag) {\n return {\n template: glslTagToString(tag),\n schema: glslTagToSchema(tag),\n }\n}\n\nfunction glslTagToSchema<TTag extends GLSLTag>(tag: TTag) {\n const result = {\n uniforms: {},\n attributes: {},\n interleavedAttributes: {},\n }\n\n for (const slot of tag.slots) {\n if (typeof slot !== 'object') {\n continue\n }\n\n if (slot.type === 'glsl') {\n const { uniforms, attributes, interleavedAttributes } = glslTagToSchema(slot)\n\n result.uniforms = {\n ...result.uniforms,\n ...uniforms,\n }\n result.attributes = {\n ...result.attributes,\n ...attributes,\n }\n result.interleavedAttributes = {\n ...result.interleavedAttributes,\n ...interleavedAttributes,\n }\n\n continue\n }\n\n const { key: name, type, ...rest } = slot\n\n // @ts-expect-error\n result[`${type}s`][name] = rest\n }\n\n return result\n}\n\nfunction glslTagToString<TTag extends GLSLTag>({ template: [initial, ...rest], slots }: TTag) {\n const v300 = !!initial?.startsWith('#version 300 es')\n\n let template = initial ?? ''\n\n for (let i = 0; i < rest.length; i++) {\n template += `${glslSlotToString(slots[i]!, v300)}${rest[i]}`\n }\n\n return template\n}\n\nfunction glslSlotToString(slot: GLSLSlot, v300: boolean) {\n if (typeof slot !== 'object') {\n return toID(slot)\n }\n\n if (slot.type === 'glsl') {\n return glslTagToString(slot)\n }\n\n if (slot.type === 'interleavedAttribute') {\n return slot.layout.reduce(\n (a, v) =>\n v300 ? `${a}in ${v.kind} ${toID(v.key)};\\n` : `${a}attribute ${v.kind} ${toID(v.key)};\\n`,\n '',\n )\n }\n\n if (slot.type === 'uniform' && 'size' in slot) {\n return `${slot.type} ${slot.kind} ${toID(slot.key)}[${slot.size}];`\n }\n\n return `${slot.type === 'attribute' && v300 ? 'in' : slot.type} ${slot.kind} ${toID(slot.key)};`\n}\n"],"mappings":"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","names":["index","value","map","name","index","layout","key","name"]}
1
+ {"version":3,"sources":["../src/features.ts","../src/utils/object.ts","../src/utils/index.ts","../src/index.ts","../src/tag.ts"],"sourcesContent":["import type { GL, RemoveSuffix } from './types'\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 (gl instanceof WebGL2RenderingContext) 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\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 (gl instanceof WebGL2RenderingContext) 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","export function forEach<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 map<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 forEach(value, (value, key, index) => {\n result[key] = callback(value, key, index)\n })\n\n return result\n}\n","import type {\n AttributeKind,\n FramebufferOptions,\n GL,\n KIND_TO_SIZE_MAP,\n KIND_TO_UNIFORM_FN_NAME_MAP,\n TextureOptions,\n UniformKind,\n} from '../types'\n\nexport * as ObjectUtils from './object'\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\nfunction createGLShader(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\nexport function createProgram(gl: GL, vertexSource: string, fragmentSource: string): WebGLProgram {\n const program = gl.createProgram()\n if (!program) throw new Error('Failed to create WebGL program')\n\n const vertexShader = createGLShader(gl, gl.VERTEX_SHADER, vertexSource)\n const fragmentShader = createGLShader(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 return program\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 }: TextureOptions,\n data?: ArrayBufferView | null,\n): WebGLTexture {\n const texture = gl.createTexture()\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 console.log(minFilter)\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 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 '${name}' 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(gl: GL, { attachment, texture, ...options }: FramebufferOptions) {\n // Create texture for the framebuffer\n texture ??= createTexture(gl, options)\n\n // Create framebuffer\n const framebuffer = assertedNotNullish(\n gl.createFramebuffer(),\n `Failed to create framebuffer: ${name}`,\n )\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 return {\n texture,\n framebuffer,\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","import { getInstancedArrays, getVertexArrayObject } from './features'\nimport type {\n AttributeMethods,\n AttributeSchema,\n AttributeView,\n BufferSchema,\n BufferView,\n GL,\n UniformView as InferUniformView,\n InterleavedAttributeMethods,\n InterleavedAttributeSchema,\n InterleavedAttributeView,\n UniformSchema,\n View,\n ViewOptions,\n ViewSchema,\n} from './types'\nimport {\n assertedNotNullish,\n createUpsertMap,\n isMatKind,\n isSamplerKind,\n kindToSize,\n kindToUniformFnName,\n ObjectUtils,\n} from './utils'\n\n/**********************************************************************************/\n/* */\n/* To ID */\n/* */\n/**********************************************************************************/\n\nlet index = 0\nconst PREFIX = 'VIEW_GL_ALIAS'\n\nconst SYMBOL_MAP = createUpsertMap(WeakMap<symbol, string>)\n\nexport function toID(key: string | number | symbol) {\n if (typeof key === 'string') {\n return key\n }\n if (typeof key === 'symbol') {\n return SYMBOL_MAP.getOrInsert(key, () => `${PREFIX}_${index++}`)\n }\n return key.toString()\n}\n\n/**********************************************************************************/\n/* */\n/* View */\n/* */\n/**********************************************************************************/\n\nexport function view<TSchema extends ViewSchema>(\n gl: GL,\n program: WebGLProgram,\n schema: TSchema,\n options?: ViewOptions,\n): View<TSchema> {\n return {\n uniforms: !schema.uniforms ? undefined : uniformView(gl, program, schema.uniforms),\n attributes: !schema.attributes\n ? undefined\n : attributeView(gl, program, schema.attributes, options),\n interleavedAttributes: !schema.interleavedAttributes\n ? undefined\n : interleavedAttributeView(gl, program, schema.interleavedAttributes, options),\n buffers: !schema.buffers ? undefined : bufferView(gl, schema.buffers),\n } as View<TSchema>\n}\n\n/**********************************************************************************/\n/* */\n/* Uniform View */\n/* */\n/**********************************************************************************/\n\nexport function uniformView<T extends UniformSchema>(\n gl: GL,\n program: WebGLProgram,\n schema: T,\n): InferUniformView<T> {\n return ObjectUtils.map(schema, ({ kind, size }, key) => {\n const name = toID(key)\n\n const location = gl.getUniformLocation(program, name)\n\n if (isSamplerKind(kind)) {\n return {\n set(arg: number) {\n gl.uniform1i(location, arg)\n },\n }\n }\n\n const fnName = `uniform${kindToUniformFnName(kind)}${size ? 'v' : ''}`\n\n // @ts-ignore FIX WEBGL/WEBGL2 TYPES\n const fn = gl[fnName].bind(gl)\n\n if (isMatKind(kind)) {\n return {\n set(...args: any[]) {\n fn(location, false, args[0])\n },\n }\n }\n\n return {\n set(...args: any[]) {\n fn(location, ...args)\n },\n }\n })\n}\n\n/**********************************************************************************/\n/* */\n/* Attribute View */\n/* */\n/**********************************************************************************/\n\n// Shared attribute helper functions\n// between attributeView and interleavedAttributeView\nfunction handleAttribute(\n gl: GL,\n location: number,\n size: number,\n stride: number,\n offset: number,\n type: 'FLOAT' | 'INT',\n instanced?: boolean,\n) {\n gl.enableVertexAttribArray(location)\n gl.vertexAttribPointer(location, size, gl[type], false, stride, offset)\n\n if (instanced) {\n // Get instanced-arrays-feature: extension if webgl, gl if webgl2\n assertedNotNullish(getInstancedArrays(gl)).vertexAttribDivisor(location, 1)\n }\n}\n\nexport function attributeView<T extends AttributeSchema>(\n gl: GL,\n program: WebGLProgram,\n schema: T,\n { signal }: ViewOptions = {},\n): AttributeView<T> {\n const attributes = ObjectUtils.map(\n schema,\n (\n { kind, instanced, buffer = assertedNotNullish(gl.createBuffer()) },\n key,\n ): AttributeMethods => {\n const name = toID(key)\n\n const location = gl.getAttribLocation(program, name)\n if (location < 0) {\n throw new Error(`Attribute '${name}' not found`)\n }\n\n const size = kindToSize(kind)\n const type = kind.startsWith('i') ? 'INT' : 'FLOAT'\n\n return {\n buffer,\n bind() {\n gl.bindBuffer(gl.ARRAY_BUFFER, buffer)\n handleAttribute(gl, location, size, 0, 0, type, instanced)\n },\n dispose() {\n gl.deleteBuffer(buffer)\n },\n set(data, usage = 'STATIC_DRAW') {\n gl.bindBuffer(gl.ARRAY_BUFFER, buffer)\n gl.bufferData(gl.ARRAY_BUFFER, data, gl[usage])\n return this\n },\n }\n },\n )\n\n signal?.addEventListener('abort', function dispose() {\n ObjectUtils.forEach(attributes, value => value.dispose())\n })\n\n return attributes\n}\n\n/**********************************************************************************/\n/* */\n/* Interleaved Attribute View */\n/* */\n/**********************************************************************************/\n\nexport function interleavedAttributeView<T extends InterleavedAttributeSchema>(\n gl: GL,\n program: WebGLProgram,\n schema: T,\n { signal }: ViewOptions = {},\n): InterleavedAttributeView<T> {\n // Initialize interleaved attributes\n const interleavedAttributes = ObjectUtils.map(schema, ({ layout, instanced }) => {\n // Increment number to keep track of offset\n let index = 0\n\n // Calculate layout information\n const handles = layout.map(layout => {\n const name = toID(layout.key)\n\n const location = gl.getAttribLocation(program, name)\n\n if (location < 0) {\n throw new Error(`Attribute '${name}' not found`)\n }\n\n const size = kindToSize(layout.kind)\n const offset = index\n const type = layout.kind.startsWith('i') ? 'INT' : 'FLOAT'\n index += size * 4\n\n return () => handleAttribute(gl, location, size, stride, offset, type, instanced)\n })\n\n // Set stride to final index\n const stride = index\n\n // Create a buffer\n const buffer = assertedNotNullish(gl.createBuffer())\n\n // Create VAO to cache attribute state\n let vao: { unbind(): void; bind(): void; dispose(): void } | undefined = undefined\n\n // Get VAO-feature: extension if webgl1, gl if webgl2\n const feature = getVertexArrayObject(gl)\n if (feature) {\n const vertexArray = feature.createVertexArray()\n feature.bindVertexArray(vertexArray)\n gl.bindBuffer(gl.ARRAY_BUFFER, buffer)\n for (const handle of handles) {\n handle()\n }\n feature.bindVertexArray(null)\n vao = {\n unbind() {\n feature.bindVertexArray(null)\n },\n bind() {\n feature.bindVertexArray(vertexArray)\n },\n dispose() {\n feature.deleteVertexArray(vertexArray)\n },\n }\n }\n\n return {\n bind() {\n gl.bindBuffer(gl.ARRAY_BUFFER, buffer)\n if (vao) {\n vao.bind()\n } else {\n // Fallback: manual attribute setup\n for (const handle of handles) {\n handle()\n }\n }\n },\n unbind() {\n if (vao) {\n vao.unbind()\n }\n },\n dispose() {\n gl.deleteBuffer(buffer)\n if (vao) {\n vao.dispose()\n }\n },\n set(value, usage = 'STATIC_DRAW') {\n if (vao) {\n vao.bind()\n } else {\n gl.bindBuffer(gl.ARRAY_BUFFER, buffer)\n }\n gl.bufferData(gl.ARRAY_BUFFER, value, gl[usage])\n if (vao) {\n vao.unbind()\n }\n },\n } satisfies InterleavedAttributeMethods\n })\n\n signal?.addEventListener('abort', function dispose() {\n ObjectUtils.forEach(interleavedAttributes, value => value.dispose())\n })\n\n return interleavedAttributes\n}\n\n/**********************************************************************************/\n/* */\n/* Buffer View */\n/* */\n/**********************************************************************************/\n\nexport function bufferView<T extends BufferSchema>(\n gl: GL,\n schema: T,\n { signal }: ViewOptions = {},\n): BufferView<T> {\n // Initialize buffers\n const buffers = ObjectUtils.map(schema, ({ target = 'ARRAY_BUFFER', usage = 'STATIC_DRAW' }) => {\n const buffer = assertedNotNullish(gl.createBuffer())\n return {\n bind() {\n gl.bindBuffer(gl[target], buffer)\n },\n dispose() {\n gl.deleteBuffer(buffer)\n },\n set(data: Float32Array) {\n gl.bindBuffer(gl[target], buffer)\n gl.bufferData(gl[target], data, gl[usage])\n },\n }\n })\n\n signal?.addEventListener('abort', function dispose() {\n ObjectUtils.forEach(buffers, value => value.dispose())\n })\n\n return buffers\n}\n","import { toID, view } from '.'\nimport type {\n AttributeKind,\n AttributeOptions,\n AttributeTag,\n AttributeTagFn as AttributeTagMethod,\n DeepMerge,\n GLSLSlot,\n GLSLTag,\n InterleaveTag,\n Prettify,\n ShallowMerge,\n UniformKind,\n UniformOptions,\n UniformTag,\n UniformTagFn as UniformTagMethod,\n View,\n ViewSchema,\n ViewSchemaPartial,\n} from './types'\nimport { createProgram } from './utils'\n\nexport function glsl<TSlot extends GLSLSlot, TSlots extends TSlot[]>(\n template: TemplateStringsArray,\n ...slots: [...TSlots]\n): GLSLTag<TSlots> {\n return { template, slots, type: 'glsl' }\n}\n\nexport const uniform = new Proxy(\n {} as {\n [TKey in UniformKind]: UniformTagMethod<TKey>\n },\n {\n get(target, property) {\n // @ts-expect-error\n if (typeof property === 'symbol') return target[property]\n return (key: string, options?: Omit<UniformOptions, 'kind'>) => ({\n type: 'uniform',\n key,\n kind: property,\n ...options,\n })\n },\n },\n)\n\nexport const attribute = new Proxy(\n {} as {\n [TKey in AttributeKind]: AttributeTagMethod<TKey>\n },\n {\n get(target, property) {\n // @ts-expect-error\n if (typeof property === 'symbol') return target[property]\n return (key: string, options?: Omit<AttributeOptions, 'kind'>) => ({\n type: 'attribute',\n key,\n kind: property,\n ...options,\n })\n },\n },\n)\n\nexport function interleave<\n const TKey extends string | symbol,\n const TLayout extends Array<AttributeTag<string | symbol, AttributeKind, undefined>>,\n const TOptions extends Omit<AttributeOptions, 'kind'>,\n>(key: TKey, layout: TLayout, instanced: TOptions): InterleaveTag<TKey, TLayout, TOptions>\nexport function interleave<\n const TKey extends string | symbol,\n const TLayout extends Array<AttributeTag<string | symbol, AttributeKind, undefined>>,\n>(key: TKey, layout: TLayout): InterleaveTag<TKey, TLayout, { instanced: false }>\nexport function interleave<\n const TKey extends string | symbol,\n const TLayout extends Array<AttributeTag<string | symbol, AttributeKind, undefined>>,\n const TOptions extends Omit<AttributeOptions, 'kind'>,\n>(key: TKey, layout: TLayout, { instanced, buffer }: Partial<TOptions> = {}) {\n return {\n type: 'interleavedAttribute',\n key,\n // remove instanced- and type-property\n layout: layout.map(({ key, kind }) => ({\n key,\n kind,\n })),\n instanced: !!instanced,\n buffer,\n } as unknown as InterleaveTag<TKey, TLayout, TOptions>\n}\n\n/**********************************************************************************/\n/* */\n/* Compile */\n/* */\n/**********************************************************************************/\n\ntype glslTagToSchema<TTag extends GLSLTag> =\n FlattenSlots<TTag> extends infer TSlots\n ? TSlots extends Array<any>\n ? GLSLSlotsToSchema<TSlots>\n : never\n : never\n\ntype GLSLSlotsToSchema<TSlots extends Array<GLSLSlot>> = {\n uniforms: {\n [K in Extract<TSlots[number], UniformTag> as K['key']]: Prettify<Omit<K, 'name' | 'type'>>\n }\n attributes: {\n [K in Extract<TSlots[number], AttributeTag> as K['key']]: Prettify<Omit<K, 'name' | 'type'>>\n }\n interleavedAttributes: {\n [K in Extract<TSlots[number], InterleaveTag> as K['key']]: Prettify<Omit<K, 'name' | 'type'>>\n }\n}\n\ntype FlattenSlots<T> =\n T extends GLSLTag<infer TSlots>\n ? _FlattenSlots<TSlots>\n : T extends Array<GLSLSlot>\n ? _FlattenSlots<T>\n : never\n\ntype _FlattenSlots<T extends Array<GLSLSlot>> = T extends [infer First, ...infer Rest]\n ? First extends GLSLTag\n ? [...FlattenSlots<First>, ...FlattenSlots<Rest>]\n : First extends unknown[]\n ? [...FlattenSlots<First>, ...FlattenSlots<Rest>]\n : [First, ...FlattenSlots<Rest>]\n : []\n\ntype MergeGLSLSchema<\n TVertex extends ViewSchema,\n TFragment extends ViewSchema,\n TOverrides extends ViewSchemaPartial,\n> = {\n uniforms: DeepMerge<\n [ShallowMerge<[TVertex['uniforms'], TFragment['uniforms']]>, TOverrides['uniforms']]\n >\n attributes: DeepMerge<\n [ShallowMerge<[TVertex['attributes'], TFragment['attributes']]>, TOverrides['attributes']]\n >\n interleavedAttributes: DeepMerge<\n [\n ShallowMerge<[TVertex['interleavedAttributes'], TFragment['interleavedAttributes']]>,\n TOverrides['interleavedAttributes'],\n ]\n >\n}\n\ntype CompileResult<\n TVertex extends GLSLTag,\n TFragment extends GLSLTag,\n TOverrides extends ViewSchemaPartial,\n> =\n MergeGLSLSchema<\n glslTagToSchema<TVertex>,\n glslTagToSchema<TFragment>,\n TOverrides\n > extends infer TSchema extends ViewSchema\n ? {\n program: WebGLProgram\n schema: TSchema\n view: View<TSchema>\n }\n : never\n\nexport function compile<\n TVertex extends GLSLTag,\n TFragment extends GLSLTag,\n TOverride extends ViewSchemaPartial,\n>(gl: WebGLRenderingContext, vertex: TVertex, fragment: TFragment, overrideSchema?: TOverride) {\n const _vertex = resolveGLSLTag(vertex)\n const _fragment = resolveGLSLTag(fragment)\n\n const schema = {\n uniforms: {\n ..._vertex.schema.uniforms,\n ..._fragment.schema.uniforms,\n },\n attributes: {\n ..._vertex.schema.attributes,\n ..._fragment.schema.attributes,\n },\n interleavedAttributes: {\n ..._vertex.schema.interleavedAttributes,\n ..._fragment.schema.interleavedAttributes,\n },\n }\n\n for (const kind in overrideSchema) {\n const schemaKind = schema[kind as keyof typeof schema]\n const overrideSchemaKind = overrideSchema[kind]\n\n for (const key in overrideSchemaKind) {\n // @ts-expect-error\n schemaKind[key] = {\n // @ts-expect-error\n ...schemaKind[key],\n ...overrideSchemaKind[key],\n }\n }\n }\n\n const program = createProgram(gl, _vertex.template, _fragment.template)\n\n return {\n program,\n schema,\n view: view(gl, program, schema),\n } as CompileResult<TVertex, TFragment, TOverride>\n}\n\nfunction resolveGLSLTag<TTag extends GLSLTag>(tag: TTag) {\n return {\n template: glslTagToString(tag),\n schema: glslTagToSchema(tag),\n }\n}\n\nfunction glslTagToSchema<TTag extends GLSLTag>(tag: TTag) {\n const result = {\n uniforms: {},\n attributes: {},\n interleavedAttributes: {},\n }\n\n function handleSlot(slot: GLSLSlot) {\n if (Array.isArray(slot)) {\n slot.forEach(handleSlot)\n return\n }\n\n if (typeof slot !== 'object') {\n return\n }\n\n if (slot.type === 'glsl') {\n const { uniforms, attributes, interleavedAttributes } = glslTagToSchema(slot)\n\n result.uniforms = {\n ...result.uniforms,\n ...uniforms,\n }\n result.attributes = {\n ...result.attributes,\n ...attributes,\n }\n result.interleavedAttributes = {\n ...result.interleavedAttributes,\n ...interleavedAttributes,\n }\n\n return\n }\n\n const { key: name, type, ...rest } = slot\n\n // @ts-expect-error\n result[`${type}s`][name] = rest\n }\n\n tag.slots.forEach(handleSlot)\n\n return result\n}\n\n// function glslSlotToSchema(slot: GLSLSlot){\n// if(Array.isArray(slot)){\n\n// return\n// }\n\n// if (typeof slot !== 'object') {\n// return\n// }\n\n// if (slot.type === 'glsl') {\n// return glslTagToSchema(slot)\n\n// // result.uniforms = {\n// // ...result.uniforms,\n// // ...uniforms,\n// // }\n// // result.attributes = {\n// // ...result.attributes,\n// // ...attributes,\n// // }\n// // result.interleavedAttributes = {\n// // ...result.interleavedAttributes,\n// // ...interleavedAttributes,\n// // }\n\n// // continue\n// }\n\n// const { key: name, type, ...rest } = slot\n\n// // @ts-expect-error\n// result[`${type}s`][name] = rest\n// }\n\nfunction glslTagToString<TTag extends GLSLTag>({ template: [initial, ...rest], slots }: TTag) {\n const v300 = !!initial?.startsWith('#version 300 es')\n\n let template = initial ?? ''\n\n for (let i = 0; i < rest.length; i++) {\n template += `${glslSlotToString(slots[i]!, v300)}${rest[i]}`\n }\n\n return template\n}\n\nfunction glslSlotToString(slot: GLSLSlot, v300: boolean): string {\n if (typeof slot !== 'object') {\n return toID(slot)\n }\n\n if (Array.isArray(slot)) {\n return slot.map(slot => glslSlotToString(slot, v300)).join('')\n }\n\n if (slot.type === 'glsl') {\n return glslTagToString(slot)\n }\n\n if (slot.type === 'interleavedAttribute') {\n return slot.layout.reduce(\n (a, v) =>\n v300 ? `${a}in ${v.kind} ${toID(v.key)};\\n` : `${a}attribute ${v.kind} ${toID(v.key)};\\n`,\n '',\n )\n }\n\n if (slot.type === 'uniform' && 'size' in slot) {\n return `${slot.type} ${slot.kind} ${toID(slot.key)}[${slot.size}];`\n }\n\n return `${slot.type === 'attribute' && v300 ? 'in' : slot.type} ${slot.kind} ${toID(slot.key)};`\n}\n"],"mappings":"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","names":["index","value","map","name","index","layout","key","name","slot"]}
@@ -245,7 +245,8 @@ type View<T extends ViewSchema = ViewSchema> = {
245
245
  buffers: T['buffers'] extends BufferSchema ? BufferView<T['buffers']> : undefined;
246
246
  uniforms: T['uniforms'] extends UniformSchema ? UniformView<T['uniforms']> : never;
247
247
  };
248
- type GLSLSlot = GLSLTag | UniformTag | AttributeTag | InterleaveTag | string | number | symbol;
248
+ type GLSLSlotItem = GLSLTag | UniformTag | AttributeTag | InterleaveTag | string | number | symbol;
249
+ type GLSLSlot = GLSLSlotItem | Array<GLSLSlotItem>;
249
250
  interface GLSLTag<TSlots extends GLSLSlot[] = GLSLSlot[]> {
250
251
  type: 'glsl';
251
252
  template: TemplateStringsArray;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bigmistqke/view.gl",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "description": "🪟 Utilities for managing WebGL resources.",
5
5
  "exports": {
6
6
  ".": {
package/src/tag.ts CHANGED
@@ -226,9 +226,14 @@ function glslTagToSchema<TTag extends GLSLTag>(tag: TTag) {
226
226
  interleavedAttributes: {},
227
227
  }
228
228
 
229
- for (const slot of tag.slots) {
229
+ function handleSlot(slot: GLSLSlot) {
230
+ if (Array.isArray(slot)) {
231
+ slot.forEach(handleSlot)
232
+ return
233
+ }
234
+
230
235
  if (typeof slot !== 'object') {
231
- continue
236
+ return
232
237
  }
233
238
 
234
239
  if (slot.type === 'glsl') {
@@ -247,7 +252,7 @@ function glslTagToSchema<TTag extends GLSLTag>(tag: TTag) {
247
252
  ...interleavedAttributes,
248
253
  }
249
254
 
250
- continue
255
+ return
251
256
  }
252
257
 
253
258
  const { key: name, type, ...rest } = slot
@@ -256,9 +261,46 @@ function glslTagToSchema<TTag extends GLSLTag>(tag: TTag) {
256
261
  result[`${type}s`][name] = rest
257
262
  }
258
263
 
264
+ tag.slots.forEach(handleSlot)
265
+
259
266
  return result
260
267
  }
261
268
 
269
+ // function glslSlotToSchema(slot: GLSLSlot){
270
+ // if(Array.isArray(slot)){
271
+
272
+ // return
273
+ // }
274
+
275
+ // if (typeof slot !== 'object') {
276
+ // return
277
+ // }
278
+
279
+ // if (slot.type === 'glsl') {
280
+ // return glslTagToSchema(slot)
281
+
282
+ // // result.uniforms = {
283
+ // // ...result.uniforms,
284
+ // // ...uniforms,
285
+ // // }
286
+ // // result.attributes = {
287
+ // // ...result.attributes,
288
+ // // ...attributes,
289
+ // // }
290
+ // // result.interleavedAttributes = {
291
+ // // ...result.interleavedAttributes,
292
+ // // ...interleavedAttributes,
293
+ // // }
294
+
295
+ // // continue
296
+ // }
297
+
298
+ // const { key: name, type, ...rest } = slot
299
+
300
+ // // @ts-expect-error
301
+ // result[`${type}s`][name] = rest
302
+ // }
303
+
262
304
  function glslTagToString<TTag extends GLSLTag>({ template: [initial, ...rest], slots }: TTag) {
263
305
  const v300 = !!initial?.startsWith('#version 300 es')
264
306
 
@@ -271,11 +313,15 @@ function glslTagToString<TTag extends GLSLTag>({ template: [initial, ...rest], s
271
313
  return template
272
314
  }
273
315
 
274
- function glslSlotToString(slot: GLSLSlot, v300: boolean) {
316
+ function glslSlotToString(slot: GLSLSlot, v300: boolean): string {
275
317
  if (typeof slot !== 'object') {
276
318
  return toID(slot)
277
319
  }
278
320
 
321
+ if (Array.isArray(slot)) {
322
+ return slot.map(slot => glslSlotToString(slot, v300)).join('')
323
+ }
324
+
279
325
  if (slot.type === 'glsl') {
280
326
  return glslTagToString(slot)
281
327
  }
package/src/types.ts CHANGED
@@ -709,14 +709,9 @@ export interface GLSLResult {
709
709
  schema: ViewSchema
710
710
  }
711
711
 
712
- export type GLSLSlot =
713
- | GLSLTag
714
- | UniformTag
715
- | AttributeTag
716
- | InterleaveTag
717
- | string
718
- | number
719
- | symbol
712
+ type GLSLSlotItem = GLSLTag | UniformTag | AttributeTag | InterleaveTag | string | number | symbol
713
+
714
+ export type GLSLSlot = GLSLSlotItem | Array<GLSLSlotItem>
720
715
 
721
716
  export interface GLSLTag<TSlots extends GLSLSlot[] = GLSLSlot[]> {
722
717
  type: 'glsl'
package/test/tag.test.ts CHANGED
@@ -992,6 +992,275 @@ describe('compile with symbols', () => {
992
992
  })
993
993
  })
994
994
 
995
+ describe('glsl array interpolation', () => {
996
+ let gl: GL
997
+
998
+ beforeEach(() => {
999
+ gl = createMockGL()
1000
+ })
1001
+
1002
+ it('should interpolate arrays of resource tags', () => {
1003
+ const shader = glsl`
1004
+ ${[uniform.float('u_time'), uniform.vec2('u_resolution')]}
1005
+ ${[attribute.vec3('a_position'), attribute.vec2('a_uv')]}
1006
+
1007
+ void main() {
1008
+ gl_Position = vec4(a_position, 1.0);
1009
+ }
1010
+ `
1011
+
1012
+ const { schema } = compile(gl, shader, glsl``)
1013
+
1014
+ expect(schema.uniforms).toEqual({
1015
+ u_time: { kind: 'float' },
1016
+ u_resolution: { kind: 'vec2' },
1017
+ })
1018
+
1019
+ expect(schema.attributes).toEqual({
1020
+ a_position: { kind: 'vec3' },
1021
+ a_uv: { kind: 'vec2' },
1022
+ })
1023
+ })
1024
+
1025
+ it('should interpolate arrays with mixed types', () => {
1026
+ const sharedUniforms = glsl`
1027
+ ${uniform.float('u_time')}
1028
+ ${uniform.vec2('u_resolution')}
1029
+ `
1030
+
1031
+ const shader = glsl`
1032
+ ${[sharedUniforms, uniform.vec3('u_color')]}
1033
+ ${[attribute.vec3('a_position'), 'precision mediump float;']}
1034
+
1035
+ void main() {
1036
+ gl_Position = vec4(a_position, 1.0);
1037
+ }
1038
+ `
1039
+
1040
+ const { schema } = compile(gl, shader, glsl``)
1041
+
1042
+ expect(schema.uniforms).toEqual({
1043
+ u_time: { kind: 'float' },
1044
+ u_resolution: { kind: 'vec2' },
1045
+ u_color: { kind: 'vec3' },
1046
+ })
1047
+
1048
+ expect(schema.attributes).toEqual({
1049
+ a_position: { kind: 'vec3' },
1050
+ })
1051
+
1052
+ // Check that the string was interpolated in the shader source
1053
+ const shaderSource = (gl.shaderSource as any).mock.calls.find(
1054
+ (call: any[]) => call[1].includes('precision mediump float;')
1055
+ )
1056
+ expect(shaderSource).toBeTruthy()
1057
+ })
1058
+
1059
+ it('should interpolate arrays with symbols', () => {
1060
+ const u_time_symbol = Symbol('u_time')
1061
+ const a_pos_symbol = Symbol('a_position')
1062
+
1063
+ const shader = glsl`
1064
+ ${[uniform.float(u_time_symbol), uniform.vec3('u_color')]}
1065
+ ${[attribute.vec3(a_pos_symbol), attribute.vec2('a_uv')]}
1066
+
1067
+ void main() {
1068
+ gl_Position = vec4(${a_pos_symbol}, 1.0);
1069
+ }
1070
+ `
1071
+
1072
+ const { schema } = compile(gl, shader, glsl``)
1073
+
1074
+ expect(schema.uniforms).toEqual({
1075
+ [u_time_symbol]: { kind: 'float' },
1076
+ u_color: { kind: 'vec3' },
1077
+ })
1078
+
1079
+ expect(schema.attributes).toEqual({
1080
+ [a_pos_symbol]: { kind: 'vec3' },
1081
+ a_uv: { kind: 'vec2' },
1082
+ })
1083
+
1084
+ // Check that symbol was interpolated in the shader source
1085
+ const shaderSource = (gl.shaderSource as any).mock.calls.find(
1086
+ (call: any[]) => call[1].includes('attribute vec3 VIEW_GL_ALIAS_') &&
1087
+ call[1].includes('uniform float VIEW_GL_ALIAS_')
1088
+ )
1089
+ expect(shaderSource).toBeTruthy()
1090
+ })
1091
+
1092
+ it('should interpolate nested arrays', () => {
1093
+ const shader = glsl`
1094
+ ${[[uniform.float('u_time'), uniform.vec2('u_resolution')], uniform.vec3('u_color')]}
1095
+ ${[[attribute.vec3('a_position')], [attribute.vec2('a_uv'), attribute.vec3('a_normal')]]}
1096
+
1097
+ void main() {
1098
+ gl_Position = vec4(a_position, 1.0);
1099
+ }
1100
+ `
1101
+
1102
+ const { schema } = compile(gl, shader, glsl``)
1103
+
1104
+ expect(schema.uniforms).toEqual({
1105
+ u_time: { kind: 'float' },
1106
+ u_resolution: { kind: 'vec2' },
1107
+ u_color: { kind: 'vec3' },
1108
+ })
1109
+
1110
+ expect(schema.attributes).toEqual({
1111
+ a_position: { kind: 'vec3' },
1112
+ a_uv: { kind: 'vec2' },
1113
+ a_normal: { kind: 'vec3' },
1114
+ })
1115
+ })
1116
+
1117
+ it('should interpolate arrays with interleaved attributes', () => {
1118
+ const vertexData = interleave('vertexData', [
1119
+ attribute.vec2('a_position'),
1120
+ attribute.vec2('a_uv'),
1121
+ ])
1122
+
1123
+ const shader = glsl`
1124
+ ${[vertexData, uniform.mat4('u_mvpMatrix')]}
1125
+
1126
+ void main() {
1127
+ gl_Position = u_mvpMatrix * vec4(a_position, 0.0, 1.0);
1128
+ }
1129
+ `
1130
+
1131
+ const { schema } = compile(gl, shader, glsl``)
1132
+
1133
+ expect(schema.uniforms).toEqual({
1134
+ u_mvpMatrix: { kind: 'mat4' },
1135
+ })
1136
+
1137
+ expect(schema.interleavedAttributes).toEqual({
1138
+ vertexData: {
1139
+ layout: [
1140
+ { key: 'a_position', kind: 'vec2' },
1141
+ { key: 'a_uv', kind: 'vec2' },
1142
+ ],
1143
+ instanced: false,
1144
+ },
1145
+ })
1146
+ })
1147
+
1148
+ it('should interpolate arrays with composed glsl templates', () => {
1149
+ const sharedUniforms = glsl`
1150
+ ${uniform.float('u_time')}
1151
+ ${uniform.vec2('u_resolution')}
1152
+ `
1153
+
1154
+ const lightingCode = glsl`
1155
+ ${uniform.vec3('u_lightPos')}
1156
+
1157
+ vec3 calculateLighting(vec3 pos) {
1158
+ return normalize(u_lightPos - pos);
1159
+ }
1160
+ `
1161
+
1162
+ const shader = glsl`
1163
+ ${[sharedUniforms, lightingCode]}
1164
+ ${[attribute.vec3('a_position'), attribute.vec3('a_normal')]}
1165
+
1166
+ void main() {
1167
+ vec3 lighting = calculateLighting(a_position);
1168
+ gl_Position = vec4(a_position, 1.0);
1169
+ }
1170
+ `
1171
+
1172
+ const { schema } = compile(gl, shader, glsl``)
1173
+
1174
+ expect(schema.uniforms).toEqual({
1175
+ u_time: { kind: 'float' },
1176
+ u_resolution: { kind: 'vec2' },
1177
+ u_lightPos: { kind: 'vec3' },
1178
+ })
1179
+
1180
+ expect(schema.attributes).toEqual({
1181
+ a_position: { kind: 'vec3' },
1182
+ a_normal: { kind: 'vec3' },
1183
+ })
1184
+
1185
+ // Check that both templates and the function were interpolated
1186
+ const shaderSource = (gl.shaderSource as any).mock.calls.find(
1187
+ (call: any[]) => call[1].includes('calculateLighting') &&
1188
+ call[1].includes('uniform float u_time') &&
1189
+ call[1].includes('uniform vec3 u_lightPos')
1190
+ )
1191
+ expect(shaderSource).toBeTruthy()
1192
+ })
1193
+
1194
+ it('should handle empty arrays', () => {
1195
+ const shader = glsl`
1196
+ ${[]}
1197
+ ${uniform.float('u_time')}
1198
+ ${[]}
1199
+
1200
+ void main() {
1201
+ gl_Position = vec4(1.0);
1202
+ }
1203
+ `
1204
+
1205
+ const { schema } = compile(gl, shader, glsl``)
1206
+
1207
+ expect(schema.uniforms).toEqual({
1208
+ u_time: { kind: 'float' },
1209
+ })
1210
+
1211
+ expect(schema.attributes).toEqual({})
1212
+ expect(schema.interleavedAttributes).toEqual({})
1213
+ })
1214
+
1215
+ it('should interpolate arrays with WebGL2 syntax', () => {
1216
+ const shader = glsl`#version 300 es
1217
+ ${[uniform.float('u_time'), attribute.vec3('a_position')]}
1218
+
1219
+ void main() {
1220
+ gl_Position = vec4(a_position, 1.0);
1221
+ }
1222
+ `
1223
+
1224
+ compile(gl, shader, glsl``)
1225
+
1226
+ // Check that shaderSource was called with WebGL2 syntax
1227
+ const shaderSource = (gl.shaderSource as any).mock.calls.find(
1228
+ (call: any[]) => call[1].includes('uniform float u_time') &&
1229
+ call[1].includes('in vec3 a_position')
1230
+ )
1231
+ expect(shaderSource).toBeTruthy()
1232
+ })
1233
+
1234
+ it('should preserve order when interpolating arrays', () => {
1235
+ const shader = glsl`
1236
+ ${['// First comment', uniform.float('u_time'), '// Second comment']}
1237
+
1238
+ void main() {
1239
+ gl_Position = vec4(1.0);
1240
+ }
1241
+ `
1242
+
1243
+ compile(gl, shader, glsl``)
1244
+
1245
+ // Check that order is preserved in the shader source
1246
+ const shaderSource = (gl.shaderSource as any).mock.calls.find(
1247
+ (call: any[]) => call[1].includes('// First comment') &&
1248
+ call[1].includes('uniform float u_time') &&
1249
+ call[1].includes('// Second comment')
1250
+ )?.[1]
1251
+
1252
+ expect(shaderSource).toBeTruthy()
1253
+
1254
+ // Verify order
1255
+ const firstIndex = shaderSource.indexOf('// First comment')
1256
+ const uniformIndex = shaderSource.indexOf('uniform float u_time')
1257
+ const secondIndex = shaderSource.indexOf('// Second comment')
1258
+
1259
+ expect(firstIndex).toBeLessThan(uniformIndex)
1260
+ expect(uniformIndex).toBeLessThan(secondIndex)
1261
+ })
1262
+ })
1263
+
995
1264
  describe('compile', () => {
996
1265
  let gl: GL
997
1266
  let mockProgram: WebGLProgram