@bigmistqke/view.gl 0.1.3 → 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.
- package/.claude/settings.local.json +4 -1
- package/.prettierignore +1 -0
- package/README.md +429 -213
- package/dev/src/examples/bezier.ts +3 -9
- package/dev/src/examples/{game_of_life.ts → game-of-life.ts} +2 -9
- package/dev/src/examples/glsl-composition.ts +131 -0
- package/dev/src/examples/grid.ts +4 -3
- package/dev/src/examples/overview.ts +7 -6
- package/dev/src/examples/{pix_sim.ts → pix-sim.ts} +3 -14
- package/dev/src/examples/symbol.ts +4 -3
- package/dev/src/index.ts +1 -1
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs.map +1 -1
- package/dist/tag.d.mts +39 -21
- package/dist/tag.mjs +395 -47
- package/dist/tag.mjs.map +1 -1
- package/dist/{types-DoFAJoh-.d.mts → types-DIIqZ8e6.d.mts} +34 -11
- package/package.json +3 -2
- package/src/index.ts +4 -4
- package/src/tag.ts +251 -110
- package/src/types.ts +65 -5
- package/src/utils/index.ts +9 -9
- package/test/tag.test.ts +800 -82
package/test/tag.test.ts
CHANGED
|
@@ -230,8 +230,14 @@ describe('interleave', () => {
|
|
|
230
230
|
})
|
|
231
231
|
|
|
232
232
|
describe('glsl', () => {
|
|
233
|
+
let gl: GL
|
|
234
|
+
|
|
235
|
+
beforeEach(() => {
|
|
236
|
+
gl = createMockGL()
|
|
237
|
+
})
|
|
238
|
+
|
|
233
239
|
it('should create GLSL template with uniforms and attributes', () => {
|
|
234
|
-
const
|
|
240
|
+
const vertex = glsl`
|
|
235
241
|
${uniform.vec2('u_resolution')}
|
|
236
242
|
${attribute.vec2('a_position')}
|
|
237
243
|
|
|
@@ -240,23 +246,21 @@ describe('glsl', () => {
|
|
|
240
246
|
}
|
|
241
247
|
`
|
|
242
248
|
|
|
243
|
-
|
|
244
|
-
expect(result.template).toContain('attribute vec2 a_position;')
|
|
245
|
-
expect(result.template).toContain('void main()')
|
|
249
|
+
const { schema } = compile(gl, vertex, glsl``)
|
|
246
250
|
|
|
247
|
-
expect(
|
|
251
|
+
expect(schema.uniforms).toEqual({
|
|
248
252
|
u_resolution: { kind: 'vec2' },
|
|
249
253
|
})
|
|
250
254
|
|
|
251
|
-
expect(
|
|
255
|
+
expect(schema.attributes).toEqual({
|
|
252
256
|
a_position: { kind: 'vec2' },
|
|
253
257
|
})
|
|
254
258
|
|
|
255
|
-
expect(
|
|
259
|
+
expect(schema.interleavedAttributes).toEqual({})
|
|
256
260
|
})
|
|
257
261
|
|
|
258
262
|
it('should handle WebGL2 syntax with #version 300 es', () => {
|
|
259
|
-
const
|
|
263
|
+
const vertex = glsl`#version 300 es
|
|
260
264
|
${uniform.vec2('u_resolution')}
|
|
261
265
|
${attribute.vec2('a_position')}
|
|
262
266
|
|
|
@@ -265,12 +269,18 @@ describe('glsl', () => {
|
|
|
265
269
|
}
|
|
266
270
|
`
|
|
267
271
|
|
|
268
|
-
|
|
269
|
-
|
|
272
|
+
// For this test, we need to check the shader compilation calls
|
|
273
|
+
compile(gl, vertex, glsl``)
|
|
274
|
+
|
|
275
|
+
// Check that shaderSource was called with WebGL2 syntax
|
|
276
|
+
const vertexShaderSource = (gl.shaderSource as any).mock.calls.find(
|
|
277
|
+
(call: any[]) => call[1].includes('in vec2 a_position')
|
|
278
|
+
)
|
|
279
|
+
expect(vertexShaderSource).toBeTruthy()
|
|
270
280
|
})
|
|
271
281
|
|
|
272
282
|
it('should handle interleaved attributes', () => {
|
|
273
|
-
const
|
|
283
|
+
const vertex = glsl`
|
|
274
284
|
${interleave('vertexData', [attribute.vec2('a_position'), attribute.vec4('a_color')])}
|
|
275
285
|
|
|
276
286
|
void main() {
|
|
@@ -278,10 +288,9 @@ describe('glsl', () => {
|
|
|
278
288
|
}
|
|
279
289
|
`
|
|
280
290
|
|
|
281
|
-
|
|
282
|
-
expect(result.template).toContain('attribute vec4 a_color;')
|
|
291
|
+
const { schema } = compile(gl, vertex, glsl``)
|
|
283
292
|
|
|
284
|
-
expect(
|
|
293
|
+
expect(schema.interleavedAttributes).toEqual({
|
|
285
294
|
vertexData: {
|
|
286
295
|
layout: [
|
|
287
296
|
{ key: 'a_position', kind: 'vec2' },
|
|
@@ -293,7 +302,7 @@ describe('glsl', () => {
|
|
|
293
302
|
})
|
|
294
303
|
|
|
295
304
|
it('should handle interleaved attributes in WebGL2', () => {
|
|
296
|
-
const
|
|
305
|
+
const vertex = glsl`#version 300 es
|
|
297
306
|
${interleave('vertexData', [attribute.vec2('a_position'), attribute.vec4('a_color')])}
|
|
298
307
|
|
|
299
308
|
void main() {
|
|
@@ -301,12 +310,17 @@ describe('glsl', () => {
|
|
|
301
310
|
}
|
|
302
311
|
`
|
|
303
312
|
|
|
304
|
-
|
|
305
|
-
|
|
313
|
+
compile(gl, vertex, glsl``)
|
|
314
|
+
|
|
315
|
+
// Check that shaderSource was called with WebGL2 syntax
|
|
316
|
+
const vertexShaderSource = (gl.shaderSource as any).mock.calls.find(
|
|
317
|
+
(call: any[]) => call[1].includes('in vec2 a_position') && call[1].includes('in vec4 a_color')
|
|
318
|
+
)
|
|
319
|
+
expect(vertexShaderSource).toBeTruthy()
|
|
306
320
|
})
|
|
307
321
|
|
|
308
322
|
it('should handle uniform arrays', () => {
|
|
309
|
-
const
|
|
323
|
+
const fragment = glsl`
|
|
310
324
|
${uniform.vec3('u_palette', { size: 7 })}
|
|
311
325
|
|
|
312
326
|
void main() {
|
|
@@ -314,9 +328,9 @@ describe('glsl', () => {
|
|
|
314
328
|
}
|
|
315
329
|
`
|
|
316
330
|
|
|
317
|
-
|
|
331
|
+
const { schema } = compile(gl, glsl``, fragment)
|
|
318
332
|
|
|
319
|
-
expect(
|
|
333
|
+
expect(schema.uniforms).toEqual({
|
|
320
334
|
u_palette: { kind: 'vec3', size: 7 },
|
|
321
335
|
})
|
|
322
336
|
})
|
|
@@ -325,7 +339,7 @@ describe('glsl', () => {
|
|
|
325
339
|
const maxLights = 8
|
|
326
340
|
const shaderName = 'MyShader'
|
|
327
341
|
|
|
328
|
-
const
|
|
342
|
+
const fragment = glsl`
|
|
329
343
|
// ${shaderName}
|
|
330
344
|
#define MAX_LIGHTS ${maxLights}
|
|
331
345
|
${uniform.float('u_time')}
|
|
@@ -335,13 +349,19 @@ describe('glsl', () => {
|
|
|
335
349
|
}
|
|
336
350
|
`
|
|
337
351
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
352
|
+
compile(gl, glsl``, fragment)
|
|
353
|
+
|
|
354
|
+
// Check that shaderSource was called with the interpolated values
|
|
355
|
+
const fragmentShaderSource = (gl.shaderSource as any).mock.calls.find(
|
|
356
|
+
(call: any[]) => call[1].includes('// MyShader') &&
|
|
357
|
+
call[1].includes('#define MAX_LIGHTS 8') &&
|
|
358
|
+
call[1].includes('uniform float u_time;')
|
|
359
|
+
)
|
|
360
|
+
expect(fragmentShaderSource).toBeTruthy()
|
|
341
361
|
})
|
|
342
362
|
|
|
343
363
|
it('should handle multiple attributes and uniforms', () => {
|
|
344
|
-
const
|
|
364
|
+
const vertex = glsl`
|
|
345
365
|
${uniform.mat4('u_mvpMatrix')}
|
|
346
366
|
${uniform.sampler2D('u_texture')}
|
|
347
367
|
${attribute.vec3('a_position')}
|
|
@@ -353,12 +373,14 @@ describe('glsl', () => {
|
|
|
353
373
|
}
|
|
354
374
|
`
|
|
355
375
|
|
|
356
|
-
|
|
376
|
+
const { schema } = compile(gl, vertex, glsl``)
|
|
377
|
+
|
|
378
|
+
expect(schema.uniforms).toEqual({
|
|
357
379
|
u_mvpMatrix: { kind: 'mat4' },
|
|
358
380
|
u_texture: { kind: 'sampler2D' },
|
|
359
381
|
})
|
|
360
382
|
|
|
361
|
-
expect(
|
|
383
|
+
expect(schema.attributes).toEqual({
|
|
362
384
|
a_position: { kind: 'vec3' },
|
|
363
385
|
a_uv: { kind: 'vec2' },
|
|
364
386
|
a_color: { kind: 'vec4', instanced: true },
|
|
@@ -366,16 +388,15 @@ describe('glsl', () => {
|
|
|
366
388
|
})
|
|
367
389
|
|
|
368
390
|
it('should handle empty template', () => {
|
|
369
|
-
const
|
|
391
|
+
const { schema } = compile(gl, glsl``, glsl``)
|
|
370
392
|
|
|
371
|
-
expect(
|
|
372
|
-
expect(
|
|
373
|
-
expect(
|
|
374
|
-
expect(result.interleavedAttributes).toEqual({})
|
|
393
|
+
expect(schema.uniforms).toEqual({})
|
|
394
|
+
expect(schema.attributes).toEqual({})
|
|
395
|
+
expect(schema.interleavedAttributes).toEqual({})
|
|
375
396
|
})
|
|
376
397
|
|
|
377
398
|
it('should skip string resources when collecting schema', () => {
|
|
378
|
-
const
|
|
399
|
+
const fragment = glsl`
|
|
379
400
|
${'// This is a comment'}
|
|
380
401
|
${42}
|
|
381
402
|
${uniform.float('u_time')}
|
|
@@ -384,19 +405,27 @@ describe('glsl', () => {
|
|
|
384
405
|
void main() {}
|
|
385
406
|
`
|
|
386
407
|
|
|
387
|
-
|
|
408
|
+
const { schema } = compile(gl, glsl``, fragment)
|
|
409
|
+
|
|
410
|
+
expect(schema.uniforms).toEqual({
|
|
388
411
|
u_time: { kind: 'float' },
|
|
389
412
|
})
|
|
390
|
-
expect(Object.keys(
|
|
413
|
+
expect(Object.keys(schema.uniforms).length).toBe(1)
|
|
391
414
|
})
|
|
392
415
|
})
|
|
393
416
|
|
|
394
417
|
describe('glsl with symbols', () => {
|
|
418
|
+
let gl: GL
|
|
419
|
+
|
|
420
|
+
beforeEach(() => {
|
|
421
|
+
gl = createMockGL()
|
|
422
|
+
})
|
|
423
|
+
|
|
395
424
|
it('should support symbols in uniform tags', () => {
|
|
396
425
|
const u_time_symbol = Symbol('u_time')
|
|
397
426
|
const u_color_symbol = Symbol('u_color')
|
|
398
427
|
|
|
399
|
-
const
|
|
428
|
+
const fragment = glsl`
|
|
400
429
|
precision mediump float;
|
|
401
430
|
|
|
402
431
|
${uniform.float(u_time_symbol)}
|
|
@@ -406,17 +435,24 @@ describe('glsl with symbols', () => {
|
|
|
406
435
|
gl_FragColor = vec4(${u_color_symbol} * ${u_time_symbol}, 1.0);
|
|
407
436
|
}`
|
|
408
437
|
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
expect(
|
|
412
|
-
expect(
|
|
438
|
+
const { schema } = compile(gl, glsl``, fragment)
|
|
439
|
+
|
|
440
|
+
expect(schema.uniforms[u_time_symbol]).toEqual({ kind: 'float' })
|
|
441
|
+
expect(schema.uniforms[u_color_symbol]).toEqual({ kind: 'vec3' })
|
|
442
|
+
|
|
443
|
+
// Check that shaderSource was called with symbol aliases
|
|
444
|
+
const fragmentShaderSource = (gl.shaderSource as any).mock.calls.find(
|
|
445
|
+
(call: any[]) => call[1].includes('uniform float VIEW_GL_ALIAS_') &&
|
|
446
|
+
call[1].includes('uniform vec3 VIEW_GL_ALIAS_')
|
|
447
|
+
)
|
|
448
|
+
expect(fragmentShaderSource).toBeTruthy()
|
|
413
449
|
})
|
|
414
450
|
|
|
415
451
|
it('should support symbols in attribute tags', () => {
|
|
416
452
|
const a_position_symbol = Symbol('a_position')
|
|
417
453
|
const a_color_symbol = Symbol('a_color')
|
|
418
454
|
|
|
419
|
-
const
|
|
455
|
+
const vertex = glsl`
|
|
420
456
|
${attribute.vec2(a_position_symbol)}
|
|
421
457
|
${attribute.vec4(a_color_symbol, { instanced: true })}
|
|
422
458
|
|
|
@@ -424,10 +460,17 @@ describe('glsl with symbols', () => {
|
|
|
424
460
|
gl_Position = vec4(${a_position_symbol}, 0.0, 1.0);
|
|
425
461
|
}`
|
|
426
462
|
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
expect(
|
|
430
|
-
expect(
|
|
463
|
+
const { schema } = compile(gl, vertex, glsl``)
|
|
464
|
+
|
|
465
|
+
expect(schema.attributes[a_position_symbol]).toEqual({ kind: 'vec2' })
|
|
466
|
+
expect(schema.attributes[a_color_symbol]).toEqual({ kind: 'vec4', instanced: true })
|
|
467
|
+
|
|
468
|
+
// Check that shaderSource was called with symbol aliases
|
|
469
|
+
const vertexShaderSource = (gl.shaderSource as any).mock.calls.find(
|
|
470
|
+
(call: any[]) => call[1].includes('attribute vec2 VIEW_GL_ALIAS_') &&
|
|
471
|
+
call[1].includes('attribute vec4 VIEW_GL_ALIAS_')
|
|
472
|
+
)
|
|
473
|
+
expect(vertexShaderSource).toBeTruthy()
|
|
431
474
|
})
|
|
432
475
|
|
|
433
476
|
it('should support symbols in interleaved attribute tags', () => {
|
|
@@ -435,74 +478,117 @@ describe('glsl with symbols', () => {
|
|
|
435
478
|
const a_pos_symbol = Symbol('a_pos')
|
|
436
479
|
const a_col_symbol = Symbol('a_col')
|
|
437
480
|
|
|
438
|
-
const
|
|
481
|
+
const vertex = glsl`
|
|
439
482
|
${interleave(data_symbol, [attribute.vec3(a_pos_symbol), attribute.vec4(a_col_symbol)])}
|
|
440
483
|
|
|
441
484
|
void main() {
|
|
442
485
|
gl_Position = vec4(${a_pos_symbol}, 1.0);
|
|
443
486
|
}`
|
|
444
487
|
|
|
445
|
-
|
|
488
|
+
const { schema } = compile(gl, vertex, glsl``)
|
|
489
|
+
|
|
490
|
+
expect(schema.interleavedAttributes[data_symbol]).toEqual({
|
|
446
491
|
layout: [
|
|
447
492
|
{ key: a_pos_symbol, kind: 'vec3' },
|
|
448
493
|
{ key: a_col_symbol, kind: 'vec4' },
|
|
449
494
|
],
|
|
450
495
|
instanced: false,
|
|
451
496
|
})
|
|
452
|
-
|
|
453
|
-
|
|
497
|
+
|
|
498
|
+
// Check that shaderSource was called with symbol aliases
|
|
499
|
+
const vertexShaderSource = (gl.shaderSource as any).mock.calls.find(
|
|
500
|
+
(call: any[]) => call[1].includes('attribute vec3 VIEW_GL_ALIAS_') &&
|
|
501
|
+
call[1].includes('attribute vec4 VIEW_GL_ALIAS_')
|
|
502
|
+
)
|
|
503
|
+
expect(vertexShaderSource).toBeTruthy()
|
|
454
504
|
})
|
|
455
505
|
|
|
456
506
|
it('should support mixed string and symbol keys', () => {
|
|
457
507
|
const u_color_symbol = Symbol('u_color')
|
|
458
508
|
const a_normal_symbol = Symbol('a_normal')
|
|
459
509
|
|
|
460
|
-
const
|
|
461
|
-
${uniform.float('u_time')}
|
|
462
|
-
${uniform.vec3(u_color_symbol)}
|
|
510
|
+
const vertex = glsl`
|
|
463
511
|
${attribute.vec3('a_position')}
|
|
464
512
|
${attribute.vec3(a_normal_symbol)}
|
|
513
|
+
${uniform.float('u_time')}
|
|
465
514
|
|
|
466
515
|
void main() {
|
|
467
516
|
gl_Position = vec4(a_position + ${a_normal_symbol}, u_time);
|
|
517
|
+
}`
|
|
518
|
+
|
|
519
|
+
const fragment = glsl`
|
|
520
|
+
${uniform.float('u_time')}
|
|
521
|
+
${uniform.vec3(u_color_symbol)}
|
|
522
|
+
|
|
523
|
+
void main() {
|
|
468
524
|
gl_FragColor = vec4(${u_color_symbol}, 1.0);
|
|
469
525
|
}`
|
|
470
526
|
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
expect(
|
|
474
|
-
expect(
|
|
527
|
+
const { schema } = compile(gl, vertex, fragment)
|
|
528
|
+
|
|
529
|
+
expect(schema.uniforms.u_time).toEqual({ kind: 'float' })
|
|
530
|
+
expect(schema.uniforms[u_color_symbol]).toEqual({ kind: 'vec3' })
|
|
531
|
+
expect(schema.attributes.a_position).toEqual({ kind: 'vec3' })
|
|
532
|
+
expect(schema.attributes[a_normal_symbol]).toEqual({ kind: 'vec3' })
|
|
475
533
|
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
534
|
+
// Check vertex shader source
|
|
535
|
+
const vertexShaderSource = (gl.shaderSource as any).mock.calls.find(
|
|
536
|
+
(call: any[]) => call[1].includes('attribute vec3 a_position') &&
|
|
537
|
+
call[1].includes('attribute vec3 VIEW_GL_ALIAS_')
|
|
538
|
+
)
|
|
539
|
+
expect(vertexShaderSource).toBeTruthy()
|
|
540
|
+
|
|
541
|
+
// Check fragment shader source
|
|
542
|
+
const fragmentShaderSource = (gl.shaderSource as any).mock.calls.find(
|
|
543
|
+
(call: any[]) => call[1].includes('uniform float u_time') &&
|
|
544
|
+
call[1].includes('uniform vec3 VIEW_GL_ALIAS_')
|
|
545
|
+
)
|
|
546
|
+
expect(fragmentShaderSource).toBeTruthy()
|
|
480
547
|
})
|
|
481
548
|
|
|
482
549
|
it('should support symbols in array uniforms', () => {
|
|
483
550
|
const u_positions_symbol = Symbol('u_positions')
|
|
484
551
|
|
|
485
|
-
const
|
|
552
|
+
const fragment = glsl`
|
|
486
553
|
${uniform.vec3(u_positions_symbol, { size: 10 })}
|
|
487
554
|
|
|
488
555
|
void main() {
|
|
489
556
|
gl_FragColor = vec4(${u_positions_symbol}[0], 1.0);
|
|
490
557
|
}`
|
|
491
558
|
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
expect(
|
|
559
|
+
const { schema } = compile(gl, glsl``, fragment)
|
|
560
|
+
|
|
561
|
+
expect(schema.uniforms[u_positions_symbol]).toEqual({ kind: 'vec3', size: 10 })
|
|
562
|
+
|
|
563
|
+
// Check that shaderSource was called with array syntax
|
|
564
|
+
const fragmentShaderSource = (gl.shaderSource as any).mock.calls.find(
|
|
565
|
+
(call: any[]) => call[1].includes('uniform vec3 VIEW_GL_ALIAS_') &&
|
|
566
|
+
call[1].includes('[10];')
|
|
567
|
+
)
|
|
568
|
+
expect(fragmentShaderSource).toBeTruthy()
|
|
495
569
|
})
|
|
496
570
|
|
|
497
571
|
it('should preserve symbol identity across multiple uses', () => {
|
|
498
572
|
const u_shared_symbol = Symbol('u_shared')
|
|
499
573
|
|
|
500
|
-
const
|
|
501
|
-
|
|
574
|
+
const fragment1 = glsl`${uniform.float(u_shared_symbol)}
|
|
575
|
+
void main() { gl_FragColor = vec4(1.0); }`
|
|
576
|
+
const fragment2 = glsl`${uniform.float(u_shared_symbol)}
|
|
577
|
+
void main() { gl_FragColor = vec4(0.5); }`
|
|
578
|
+
|
|
579
|
+
compile(gl, glsl``, fragment1)
|
|
580
|
+
compile(gl, glsl``, fragment2)
|
|
502
581
|
|
|
503
582
|
// Both should generate the same alias
|
|
504
|
-
const
|
|
505
|
-
|
|
583
|
+
const shader1Source = (gl.shaderSource as any).mock.calls.find(
|
|
584
|
+
(call: any[]) => call[1].includes('gl_FragColor = vec4(1.0)')
|
|
585
|
+
)?.[1]
|
|
586
|
+
const shader2Source = (gl.shaderSource as any).mock.calls.find(
|
|
587
|
+
(call: any[]) => call[1].includes('gl_FragColor = vec4(0.5)')
|
|
588
|
+
)?.[1]
|
|
589
|
+
|
|
590
|
+
const alias1 = shader1Source?.match(/VIEW_GL_ALIAS_\d+/)?.[0]
|
|
591
|
+
const alias2 = shader2Source?.match(/VIEW_GL_ALIAS_\d+/)?.[0]
|
|
506
592
|
|
|
507
593
|
expect(alias1).toBe(alias2)
|
|
508
594
|
})
|
|
@@ -511,7 +597,7 @@ describe('glsl with symbols', () => {
|
|
|
511
597
|
const u_matrix_symbol = Symbol('u_matrix')
|
|
512
598
|
const a_vertex_symbol = Symbol('a_vertex')
|
|
513
599
|
|
|
514
|
-
const
|
|
600
|
+
const vertex = glsl`#version 300 es
|
|
515
601
|
precision mediump float;
|
|
516
602
|
|
|
517
603
|
${uniform.mat4(u_matrix_symbol)}
|
|
@@ -521,22 +607,310 @@ describe('glsl with symbols', () => {
|
|
|
521
607
|
gl_Position = ${u_matrix_symbol} * vec4(${a_vertex_symbol}, 1.0);
|
|
522
608
|
}`
|
|
523
609
|
|
|
524
|
-
|
|
525
|
-
|
|
610
|
+
compile(gl, vertex, glsl``)
|
|
611
|
+
|
|
612
|
+
// Check that shaderSource was called with WebGL2 syntax and symbol aliases
|
|
613
|
+
const vertexShaderSource = (gl.shaderSource as any).mock.calls.find(
|
|
614
|
+
(call: any[]) => call[1].includes('uniform mat4 VIEW_GL_ALIAS_') &&
|
|
615
|
+
call[1].includes('in vec3 VIEW_GL_ALIAS_')
|
|
616
|
+
)
|
|
617
|
+
expect(vertexShaderSource).toBeTruthy()
|
|
526
618
|
})
|
|
527
619
|
|
|
528
620
|
it('should handle symbol interpolation in template', () => {
|
|
529
621
|
const color_symbol = Symbol('color')
|
|
530
622
|
|
|
531
|
-
const
|
|
623
|
+
const fragment = glsl`
|
|
532
624
|
void main() {
|
|
533
625
|
vec3 ${color_symbol} = vec3(1.0, 0.0, 0.0);
|
|
534
626
|
gl_FragColor = vec4(${color_symbol}, 1.0);
|
|
535
627
|
}`
|
|
536
628
|
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
629
|
+
compile(gl, glsl``, fragment)
|
|
630
|
+
|
|
631
|
+
// Check that shaderSource was called with symbol aliases in template
|
|
632
|
+
const fragmentShaderSource = (gl.shaderSource as any).mock.calls.find(
|
|
633
|
+
(call: any[]) => call[1].includes('vec3') && call[1].includes('VIEW_GL_ALIAS_')
|
|
634
|
+
)?.[1]
|
|
635
|
+
|
|
636
|
+
expect(fragmentShaderSource).toMatch(/vec3 VIEW_GL_ALIAS_\d+ = vec3/)
|
|
637
|
+
expect(fragmentShaderSource).toMatch(/vec4\(VIEW_GL_ALIAS_\d+, 1\.0\)/)
|
|
638
|
+
})
|
|
639
|
+
})
|
|
640
|
+
|
|
641
|
+
describe('glsl composition', () => {
|
|
642
|
+
let gl: GL
|
|
643
|
+
|
|
644
|
+
beforeEach(() => {
|
|
645
|
+
gl = createMockGL()
|
|
646
|
+
})
|
|
647
|
+
|
|
648
|
+
it('should compose glsl templates inside other glsl templates', () => {
|
|
649
|
+
const sharedUniforms = glsl`
|
|
650
|
+
${uniform.vec2('u_resolution')}
|
|
651
|
+
${uniform.float('u_time')}
|
|
652
|
+
`
|
|
653
|
+
|
|
654
|
+
const vertex = glsl`
|
|
655
|
+
${sharedUniforms}
|
|
656
|
+
${attribute.vec2('a_position')}
|
|
657
|
+
|
|
658
|
+
void main() {
|
|
659
|
+
gl_Position = vec4(a_position * u_resolution, 0.0, 1.0);
|
|
660
|
+
}
|
|
661
|
+
`
|
|
662
|
+
|
|
663
|
+
const { schema } = compile(gl, vertex, glsl``)
|
|
664
|
+
|
|
665
|
+
expect(schema.uniforms).toEqual({
|
|
666
|
+
u_resolution: { kind: 'vec2' },
|
|
667
|
+
u_time: { kind: 'float' },
|
|
668
|
+
})
|
|
669
|
+
|
|
670
|
+
expect(schema.attributes).toEqual({
|
|
671
|
+
a_position: { kind: 'vec2' },
|
|
672
|
+
})
|
|
673
|
+
})
|
|
674
|
+
|
|
675
|
+
it('should compose nested glsl with symbols', () => {
|
|
676
|
+
const u_time_symbol = Symbol('u_time')
|
|
677
|
+
const a_pos_symbol = Symbol('a_position')
|
|
678
|
+
|
|
679
|
+
const sharedCode = glsl`
|
|
680
|
+
${uniform.float(u_time_symbol)}
|
|
681
|
+
${uniform.vec3('u_color')}
|
|
682
|
+
`
|
|
683
|
+
|
|
684
|
+
const vertex = glsl`
|
|
685
|
+
${sharedCode}
|
|
686
|
+
${attribute.vec2(a_pos_symbol)}
|
|
687
|
+
|
|
688
|
+
void main() {
|
|
689
|
+
gl_Position = vec4(${a_pos_symbol}, 0.0, 1.0);
|
|
690
|
+
}
|
|
691
|
+
`
|
|
692
|
+
|
|
693
|
+
const { schema } = compile(gl, vertex, glsl``)
|
|
694
|
+
|
|
695
|
+
expect(schema.uniforms).toEqual({
|
|
696
|
+
[u_time_symbol]: { kind: 'float' },
|
|
697
|
+
u_color: { kind: 'vec3' },
|
|
698
|
+
})
|
|
699
|
+
|
|
700
|
+
expect(schema.attributes).toEqual({
|
|
701
|
+
[a_pos_symbol]: { kind: 'vec2' },
|
|
702
|
+
})
|
|
703
|
+
})
|
|
704
|
+
|
|
705
|
+
it('should compose multiple levels of nesting', () => {
|
|
706
|
+
const basicUniforms = glsl`
|
|
707
|
+
${uniform.float('u_time')}
|
|
708
|
+
${uniform.vec2('u_resolution')}
|
|
709
|
+
`
|
|
710
|
+
|
|
711
|
+
const lightingUniforms = glsl`
|
|
712
|
+
${basicUniforms}
|
|
713
|
+
${uniform.vec3('u_lightPos')}
|
|
714
|
+
${uniform.vec3('u_lightColor')}
|
|
715
|
+
`
|
|
716
|
+
|
|
717
|
+
const materialUniforms = glsl`
|
|
718
|
+
${lightingUniforms}
|
|
719
|
+
${uniform.float('u_roughness')}
|
|
720
|
+
${uniform.float('u_metallic')}
|
|
721
|
+
`
|
|
722
|
+
|
|
723
|
+
const vertex = glsl`
|
|
724
|
+
${materialUniforms}
|
|
725
|
+
${attribute.vec3('a_position')}
|
|
726
|
+
${attribute.vec3('a_normal')}
|
|
727
|
+
|
|
728
|
+
void main() {
|
|
729
|
+
gl_Position = vec4(a_position, 1.0);
|
|
730
|
+
}
|
|
731
|
+
`
|
|
732
|
+
|
|
733
|
+
const { schema } = compile(gl, vertex, glsl``)
|
|
734
|
+
|
|
735
|
+
expect(schema.uniforms).toEqual({
|
|
736
|
+
u_time: { kind: 'float' },
|
|
737
|
+
u_resolution: { kind: 'vec2' },
|
|
738
|
+
u_lightPos: { kind: 'vec3' },
|
|
739
|
+
u_lightColor: { kind: 'vec3' },
|
|
740
|
+
u_roughness: { kind: 'float' },
|
|
741
|
+
u_metallic: { kind: 'float' },
|
|
742
|
+
})
|
|
743
|
+
|
|
744
|
+
expect(schema.attributes).toEqual({
|
|
745
|
+
a_position: { kind: 'vec3' },
|
|
746
|
+
a_normal: { kind: 'vec3' },
|
|
747
|
+
})
|
|
748
|
+
})
|
|
749
|
+
|
|
750
|
+
it('should compose glsl with interleaved attributes', () => {
|
|
751
|
+
const vertexLayout = glsl`
|
|
752
|
+
${interleave('vertexData', [
|
|
753
|
+
attribute.vec3('a_position'),
|
|
754
|
+
attribute.vec2('a_uv'),
|
|
755
|
+
attribute.vec3('a_normal')
|
|
756
|
+
])}
|
|
757
|
+
`
|
|
758
|
+
|
|
759
|
+
const instanceLayout = glsl`
|
|
760
|
+
${interleave('instanceData', [
|
|
761
|
+
attribute.vec3('a_instancePos'),
|
|
762
|
+
attribute.float('a_instanceScale')
|
|
763
|
+
], { instanced: true })}
|
|
764
|
+
`
|
|
765
|
+
|
|
766
|
+
const vertex = glsl`
|
|
767
|
+
${vertexLayout}
|
|
768
|
+
${instanceLayout}
|
|
769
|
+
${uniform.mat4('u_mvpMatrix')}
|
|
770
|
+
|
|
771
|
+
void main() {
|
|
772
|
+
vec3 worldPos = a_position * a_instanceScale + a_instancePos;
|
|
773
|
+
gl_Position = u_mvpMatrix * vec4(worldPos, 1.0);
|
|
774
|
+
}
|
|
775
|
+
`
|
|
776
|
+
|
|
777
|
+
const { schema } = compile(gl, vertex, glsl``)
|
|
778
|
+
|
|
779
|
+
expect(schema.uniforms).toEqual({
|
|
780
|
+
u_mvpMatrix: { kind: 'mat4' },
|
|
781
|
+
})
|
|
782
|
+
|
|
783
|
+
expect(schema.interleavedAttributes).toEqual({
|
|
784
|
+
vertexData: {
|
|
785
|
+
layout: [
|
|
786
|
+
{ key: 'a_position', kind: 'vec3' },
|
|
787
|
+
{ key: 'a_uv', kind: 'vec2' },
|
|
788
|
+
{ key: 'a_normal', kind: 'vec3' },
|
|
789
|
+
],
|
|
790
|
+
instanced: false,
|
|
791
|
+
},
|
|
792
|
+
instanceData: {
|
|
793
|
+
layout: [
|
|
794
|
+
{ key: 'a_instancePos', kind: 'vec3' },
|
|
795
|
+
{ key: 'a_instanceScale', kind: 'float' },
|
|
796
|
+
],
|
|
797
|
+
instanced: true,
|
|
798
|
+
},
|
|
799
|
+
})
|
|
800
|
+
})
|
|
801
|
+
|
|
802
|
+
it('should compose glsl in both vertex and fragment shaders', () => {
|
|
803
|
+
const sharedUniforms = glsl`
|
|
804
|
+
${uniform.float('u_time')}
|
|
805
|
+
${uniform.vec2('u_resolution')}
|
|
806
|
+
`
|
|
807
|
+
|
|
808
|
+
const lightingCode = glsl`
|
|
809
|
+
${uniform.vec3('u_lightPos')}
|
|
810
|
+
|
|
811
|
+
vec3 calculateLighting(vec3 pos, vec3 normal) {
|
|
812
|
+
vec3 lightDir = normalize(u_lightPos - pos);
|
|
813
|
+
return max(dot(normal, lightDir), 0.0) * vec3(1.0);
|
|
814
|
+
}
|
|
815
|
+
`
|
|
816
|
+
|
|
817
|
+
const vertex = glsl`
|
|
818
|
+
${sharedUniforms}
|
|
819
|
+
${attribute.vec3('a_position')}
|
|
820
|
+
${attribute.vec3('a_normal')}
|
|
821
|
+
|
|
822
|
+
varying vec3 v_normal;
|
|
823
|
+
varying vec3 v_position;
|
|
824
|
+
|
|
825
|
+
void main() {
|
|
826
|
+
v_normal = a_normal;
|
|
827
|
+
v_position = a_position;
|
|
828
|
+
gl_Position = vec4(a_position, 1.0);
|
|
829
|
+
}
|
|
830
|
+
`
|
|
831
|
+
|
|
832
|
+
const fragment = glsl`
|
|
833
|
+
${sharedUniforms}
|
|
834
|
+
${lightingCode}
|
|
835
|
+
|
|
836
|
+
varying vec3 v_normal;
|
|
837
|
+
varying vec3 v_position;
|
|
838
|
+
|
|
839
|
+
void main() {
|
|
840
|
+
vec3 lighting = calculateLighting(v_position, v_normal);
|
|
841
|
+
gl_FragColor = vec4(lighting, 1.0);
|
|
842
|
+
}
|
|
843
|
+
`
|
|
844
|
+
|
|
845
|
+
const { schema } = compile(gl, vertex, fragment)
|
|
846
|
+
|
|
847
|
+
expect(schema.uniforms).toEqual({
|
|
848
|
+
u_time: { kind: 'float' },
|
|
849
|
+
u_resolution: { kind: 'vec2' },
|
|
850
|
+
u_lightPos: { kind: 'vec3' },
|
|
851
|
+
})
|
|
852
|
+
|
|
853
|
+
expect(schema.attributes).toEqual({
|
|
854
|
+
a_position: { kind: 'vec3' },
|
|
855
|
+
a_normal: { kind: 'vec3' },
|
|
856
|
+
})
|
|
857
|
+
})
|
|
858
|
+
|
|
859
|
+
it('should handle empty composed glsl templates', () => {
|
|
860
|
+
const emptyCode = glsl``
|
|
861
|
+
|
|
862
|
+
const vertex = glsl`
|
|
863
|
+
${emptyCode}
|
|
864
|
+
${attribute.vec2('a_position')}
|
|
865
|
+
|
|
866
|
+
void main() {
|
|
867
|
+
gl_Position = vec4(a_position, 0.0, 1.0);
|
|
868
|
+
}
|
|
869
|
+
`
|
|
870
|
+
|
|
871
|
+
const { schema } = compile(gl, vertex, glsl``)
|
|
872
|
+
|
|
873
|
+
expect(schema.attributes).toEqual({
|
|
874
|
+
a_position: { kind: 'vec2' },
|
|
875
|
+
})
|
|
876
|
+
|
|
877
|
+
expect(schema.uniforms).toEqual({})
|
|
878
|
+
})
|
|
879
|
+
|
|
880
|
+
it('should deduplicate uniforms across composed templates', () => {
|
|
881
|
+
const timeUniform = glsl`${uniform.float('u_time')}`
|
|
882
|
+
const resolutionUniform = glsl`${uniform.vec2('u_resolution')}`
|
|
883
|
+
|
|
884
|
+
const sharedUniforms1 = glsl`
|
|
885
|
+
${timeUniform}
|
|
886
|
+
${resolutionUniform}
|
|
887
|
+
`
|
|
888
|
+
|
|
889
|
+
const sharedUniforms2 = glsl`
|
|
890
|
+
${timeUniform} // Same uniform appears again
|
|
891
|
+
${uniform.vec3('u_color')}
|
|
892
|
+
`
|
|
893
|
+
|
|
894
|
+
const vertex = glsl`
|
|
895
|
+
${sharedUniforms1}
|
|
896
|
+
${sharedUniforms2}
|
|
897
|
+
${attribute.vec2('a_position')}
|
|
898
|
+
|
|
899
|
+
void main() {
|
|
900
|
+
gl_Position = vec4(a_position * u_resolution, 0.0, 1.0);
|
|
901
|
+
}
|
|
902
|
+
`
|
|
903
|
+
|
|
904
|
+
const { schema } = compile(gl, vertex, glsl``)
|
|
905
|
+
|
|
906
|
+
// Should only have one instance of u_time, despite appearing multiple times
|
|
907
|
+
expect(schema.uniforms).toEqual({
|
|
908
|
+
u_time: { kind: 'float' },
|
|
909
|
+
u_resolution: { kind: 'vec2' },
|
|
910
|
+
u_color: { kind: 'vec3' },
|
|
911
|
+
})
|
|
912
|
+
|
|
913
|
+
expect(Object.keys(schema.uniforms)).toHaveLength(3)
|
|
540
914
|
})
|
|
541
915
|
})
|
|
542
916
|
|
|
@@ -618,6 +992,275 @@ describe('compile with symbols', () => {
|
|
|
618
992
|
})
|
|
619
993
|
})
|
|
620
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
|
+
|
|
621
1264
|
describe('compile', () => {
|
|
622
1265
|
let gl: GL
|
|
623
1266
|
let mockProgram: WebGLProgram
|
|
@@ -697,6 +1340,87 @@ describe('compile', () => {
|
|
|
697
1340
|
})
|
|
698
1341
|
})
|
|
699
1342
|
|
|
1343
|
+
it('should merge attributes and interleaved attributes from vertex and fragment', () => {
|
|
1344
|
+
const vertex = glsl`
|
|
1345
|
+
${attribute.vec3('a_position')}
|
|
1346
|
+
${interleave('vertexData', [attribute.vec2('a_uv'), attribute.vec3('a_normal')])}
|
|
1347
|
+
|
|
1348
|
+
void main() {}
|
|
1349
|
+
`
|
|
1350
|
+
|
|
1351
|
+
const fragment = glsl`
|
|
1352
|
+
${attribute.vec4('a_color')} // Fragment attributes (rare but valid)
|
|
1353
|
+
${interleave('materialData', [attribute.float('a_roughness'), attribute.float('a_metallic')])}
|
|
1354
|
+
|
|
1355
|
+
void main() {}
|
|
1356
|
+
`
|
|
1357
|
+
|
|
1358
|
+
const result = compile(gl, vertex, fragment)
|
|
1359
|
+
|
|
1360
|
+
expect(result.schema.attributes).toEqual({
|
|
1361
|
+
a_position: { kind: 'vec3' },
|
|
1362
|
+
a_color: { kind: 'vec4' },
|
|
1363
|
+
})
|
|
1364
|
+
|
|
1365
|
+
expect(result.schema.interleavedAttributes).toEqual({
|
|
1366
|
+
vertexData: {
|
|
1367
|
+
layout: [
|
|
1368
|
+
{ key: 'a_uv', kind: 'vec2' },
|
|
1369
|
+
{ key: 'a_normal', kind: 'vec3' },
|
|
1370
|
+
],
|
|
1371
|
+
instanced: false,
|
|
1372
|
+
},
|
|
1373
|
+
materialData: {
|
|
1374
|
+
layout: [
|
|
1375
|
+
{ key: 'a_roughness', kind: 'float' },
|
|
1376
|
+
{ key: 'a_metallic', kind: 'float' },
|
|
1377
|
+
],
|
|
1378
|
+
instanced: false,
|
|
1379
|
+
},
|
|
1380
|
+
})
|
|
1381
|
+
})
|
|
1382
|
+
|
|
1383
|
+
it('should merge schemas with symbol keys', () => {
|
|
1384
|
+
const u_time_symbol = Symbol('u_time')
|
|
1385
|
+
const a_pos_symbol = Symbol('a_pos')
|
|
1386
|
+
const data_symbol = Symbol('data')
|
|
1387
|
+
|
|
1388
|
+
const vertex = glsl`
|
|
1389
|
+
${uniform.float(u_time_symbol)}
|
|
1390
|
+
${attribute.vec2(a_pos_symbol)}
|
|
1391
|
+
${interleave(data_symbol, [attribute.vec3('a_normal')])}
|
|
1392
|
+
|
|
1393
|
+
void main() {}
|
|
1394
|
+
`
|
|
1395
|
+
|
|
1396
|
+
const fragment = glsl`
|
|
1397
|
+
${uniform.float(u_time_symbol)} // Same symbol in both shaders
|
|
1398
|
+
${uniform.vec3('u_color')}
|
|
1399
|
+
|
|
1400
|
+
void main() {}
|
|
1401
|
+
`
|
|
1402
|
+
|
|
1403
|
+
const result = compile(gl, vertex, fragment)
|
|
1404
|
+
|
|
1405
|
+
expect(result.schema.uniforms).toEqual({
|
|
1406
|
+
[u_time_symbol]: { kind: 'float' },
|
|
1407
|
+
u_color: { kind: 'vec3' },
|
|
1408
|
+
})
|
|
1409
|
+
|
|
1410
|
+
expect(result.schema.attributes).toEqual({
|
|
1411
|
+
[a_pos_symbol]: { kind: 'vec2' },
|
|
1412
|
+
})
|
|
1413
|
+
|
|
1414
|
+
expect(result.schema.interleavedAttributes).toEqual({
|
|
1415
|
+
[data_symbol]: {
|
|
1416
|
+
layout: [
|
|
1417
|
+
{ key: 'a_normal', kind: 'vec3' },
|
|
1418
|
+
],
|
|
1419
|
+
instanced: false,
|
|
1420
|
+
},
|
|
1421
|
+
})
|
|
1422
|
+
})
|
|
1423
|
+
|
|
700
1424
|
it('should handle interleaved attributes in schema', () => {
|
|
701
1425
|
const vertexData = interleave('vertexData', [
|
|
702
1426
|
attribute.vec2('a_position'),
|
|
@@ -718,13 +1442,7 @@ describe('compile', () => {
|
|
|
718
1442
|
}
|
|
719
1443
|
`
|
|
720
1444
|
|
|
721
|
-
const
|
|
722
|
-
void main() {
|
|
723
|
-
gl_FragColor = vec4(1.0);
|
|
724
|
-
}
|
|
725
|
-
`
|
|
726
|
-
|
|
727
|
-
const result = compile(gl, vertex, fragment)
|
|
1445
|
+
const result = compile(gl, vertex, glsl``)
|
|
728
1446
|
|
|
729
1447
|
expect(result.schema.interleavedAttributes).toEqual({
|
|
730
1448
|
vertexData: {
|