p5 2.2.1-rc.0 → 2.2.1
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/dist/accessibility/color_namer.js +5 -6
- package/dist/accessibility/describe.js +4 -26
- package/dist/accessibility/index.js +5 -6
- package/dist/accessibility/outputs.js +6 -38
- package/dist/app.js +5 -6
- package/dist/color/color_conversion.js +5 -6
- package/dist/color/creating_reading.js +1 -1
- package/dist/color/index.js +2 -2
- package/dist/color/p5.Color.js +1 -1
- package/dist/color/setting.js +59 -357
- package/dist/{constants-DEJVKr9Z.js → constants-DQyACdzq.js} +11 -61
- package/dist/core/constants.js +1 -1
- package/dist/core/environment.js +26 -158
- package/dist/core/filterShaders.js +1 -1
- package/dist/core/friendly_errors/fes_core.js +1 -1
- package/dist/core/friendly_errors/file_errors.js +1 -1
- package/dist/core/friendly_errors/index.js +1 -1
- package/dist/core/friendly_errors/param_validator.js +1 -1
- package/dist/core/friendly_errors/sketch_verifier.js +1 -1
- package/dist/core/helpers.js +1 -1
- package/dist/core/init.js +5 -6
- package/dist/core/internationalization.js +1 -1
- package/dist/core/legacy.js +5 -6
- package/dist/core/main.js +5 -6
- package/dist/core/p5.Graphics.js +4 -5
- package/dist/core/p5.Renderer.js +3 -4
- package/dist/core/p5.Renderer2D.js +5 -6
- package/dist/core/p5.Renderer3D.js +4 -5
- package/dist/core/rendering.js +4 -5
- package/dist/core/structure.js +13 -52
- package/dist/core/transform.js +32 -176
- package/dist/{creating_reading-CgHCHxqN.js → creating_reading-ZXzcZEsb.js} +3 -196
- package/dist/data/local_storage.js +4 -30
- package/dist/dom/dom.js +24 -159
- package/dist/dom/index.js +2 -2
- package/dist/dom/p5.Element.js +31 -208
- package/dist/dom/p5.File.js +1 -32
- package/dist/dom/p5.MediaElement.js +10 -113
- package/dist/events/acceleration.js +11 -64
- package/dist/events/keyboard.js +13 -81
- package/dist/events/pointer.js +18 -160
- package/dist/image/const.js +1 -1
- package/dist/image/filterRenderer2D.js +4 -5
- package/dist/image/image.js +4 -5
- package/dist/image/index.js +4 -5
- package/dist/image/loading_displaying.js +4 -5
- package/dist/image/p5.Image.js +3 -4
- package/dist/image/pixels.js +17 -100
- package/dist/io/files.js +4 -5
- package/dist/io/index.js +4 -5
- package/dist/io/p5.Table.js +66 -158
- package/dist/io/p5.TableRow.js +48 -71
- package/dist/io/p5.XML.js +6 -99
- package/dist/io/utilities.js +8 -3
- package/dist/{main-_RXV5Lx8.js → main-DvN69W3f.js} +13 -42
- package/dist/math/Matrices/Matrix.js +87 -126
- package/dist/math/Matrices/MatrixNumjs.js +1 -5
- package/dist/math/calculation.js +10 -112
- package/dist/math/index.js +1 -1
- package/dist/math/math.js +2 -12
- package/dist/math/noise.js +5 -32
- package/dist/math/p5.Matrix.js +3 -3
- package/dist/math/p5.Vector.js +104 -345
- package/dist/math/random.js +5 -32
- package/dist/math/trigonometry.js +15 -105
- package/dist/{p5.Renderer-QoFcvj3f.js → p5.Renderer-D-5LdCRz.js} +25 -178
- package/dist/{rendering-CsICjEXA.js → rendering-h9unX5K0.js} +254 -1156
- package/dist/shape/2d_primitives.js +33 -194
- package/dist/shape/attributes.js +12 -73
- package/dist/shape/curves.js +30 -95
- package/dist/shape/custom_shapes.js +63 -144
- package/dist/shape/index.js +2 -2
- package/dist/shape/vertex.js +21 -106
- package/dist/strands/p5.strands.js +248 -46
- package/dist/type/index.js +3 -4
- package/dist/type/p5.Font.js +4 -49
- package/dist/type/textCore.js +5 -158
- package/dist/utilities/conversion.js +17 -104
- package/dist/utilities/time_date.js +3 -40
- package/dist/utilities/utility_functions.js +6 -48
- package/dist/webgl/3d_primitives.js +4 -5
- package/dist/webgl/GeometryBuilder.js +1 -2
- package/dist/webgl/ShapeBuilder.js +22 -2
- package/dist/webgl/enums.js +1 -1
- package/dist/webgl/index.js +4 -5
- package/dist/webgl/interaction.js +6 -33
- package/dist/webgl/light.js +4 -5
- package/dist/webgl/loading.js +12 -46
- package/dist/webgl/material.js +4 -5
- package/dist/webgl/p5.Camera.js +4 -5
- package/dist/webgl/p5.DataArray.js +0 -4
- package/dist/webgl/p5.Framebuffer.js +4 -5
- package/dist/webgl/p5.Geometry.js +12 -106
- package/dist/webgl/p5.Quat.js +1 -1
- package/dist/webgl/p5.RendererGL.js +7 -18
- package/dist/webgl/p5.Shader.js +12 -36
- package/dist/webgl/p5.Texture.js +4 -5
- package/dist/webgl/text.js +4 -5
- package/dist/webgl/utils.js +4 -5
- package/dist/webgpu/index.js +1 -1
- package/dist/webgpu/p5.RendererWebGPU.js +529 -208
- package/dist/webgpu/shaders/color.js +32 -17
- package/dist/webgpu/shaders/filters/base.js +18 -7
- package/dist/webgpu/shaders/font.js +52 -40
- package/dist/webgpu/shaders/line.js +50 -36
- package/dist/webgpu/shaders/material.js +90 -83
- package/dist/webgpu/strands_wgslBackend.js +5 -2
- package/lib/p5.esm.js +5576 -7811
- package/lib/p5.esm.min.js +1 -1
- package/lib/p5.js +5576 -7811
- package/lib/p5.min.js +1 -1
- package/lib/p5.webgpu.esm.js +786 -453
- package/lib/p5.webgpu.js +786 -453
- package/lib/p5.webgpu.min.js +1 -1
- package/package.json +13 -13
- package/types/global.d.ts +16905 -16783
- package/types/p5.d.ts +11142 -11081
package/lib/p5.webgpu.esm.js
CHANGED
|
@@ -11,7 +11,7 @@ const _PI = Math.PI;
|
|
|
11
11
|
* @property {String} VERSION
|
|
12
12
|
* @final
|
|
13
13
|
*/
|
|
14
|
-
const VERSION = '2.2.1
|
|
14
|
+
const VERSION = '2.2.1';
|
|
15
15
|
|
|
16
16
|
// GRAPHICS RENDERER
|
|
17
17
|
/**
|
|
@@ -146,8 +146,6 @@ const WAIT = 'wait';
|
|
|
146
146
|
* @final
|
|
147
147
|
*
|
|
148
148
|
* @example
|
|
149
|
-
* <div>
|
|
150
|
-
* <code>
|
|
151
149
|
* function setup() {
|
|
152
150
|
* createCanvas(100, 100);
|
|
153
151
|
*
|
|
@@ -158,11 +156,8 @@ const WAIT = 'wait';
|
|
|
158
156
|
*
|
|
159
157
|
* describe('The bottom-right quarter of a circle drawn in white on a gray background.');
|
|
160
158
|
* }
|
|
161
|
-
* </code>
|
|
162
|
-
* </div>
|
|
163
159
|
*
|
|
164
|
-
*
|
|
165
|
-
* <code>
|
|
160
|
+
* @example
|
|
166
161
|
* function setup() {
|
|
167
162
|
* createCanvas(100, 100);
|
|
168
163
|
*
|
|
@@ -182,11 +177,8 @@ const WAIT = 'wait';
|
|
|
182
177
|
*
|
|
183
178
|
* describe('Two black lines on a gray background. One line extends from the center to the right. The other line extends from the center to the bottom.');
|
|
184
179
|
* }
|
|
185
|
-
* </code>
|
|
186
|
-
* </div>
|
|
187
180
|
*
|
|
188
|
-
*
|
|
189
|
-
* <code>
|
|
181
|
+
* @example
|
|
190
182
|
* function setup() {
|
|
191
183
|
* createCanvas(100, 100);
|
|
192
184
|
*
|
|
@@ -216,8 +208,6 @@ const WAIT = 'wait';
|
|
|
216
208
|
* fill(0, 0, 255);
|
|
217
209
|
* circle(x2, 0, 20);
|
|
218
210
|
* }
|
|
219
|
-
* </code>
|
|
220
|
-
* </div>
|
|
221
211
|
*/
|
|
222
212
|
const HALF_PI = _PI / 2;
|
|
223
213
|
|
|
@@ -235,8 +225,6 @@ const HALF_PI = _PI / 2;
|
|
|
235
225
|
* @final
|
|
236
226
|
*
|
|
237
227
|
* @example
|
|
238
|
-
* <div>
|
|
239
|
-
* <code>
|
|
240
228
|
* function setup() {
|
|
241
229
|
* createCanvas(100, 100);
|
|
242
230
|
*
|
|
@@ -247,11 +235,8 @@ const HALF_PI = _PI / 2;
|
|
|
247
235
|
*
|
|
248
236
|
* describe('The bottom half of a circle drawn in white on a gray background.');
|
|
249
237
|
* }
|
|
250
|
-
* </code>
|
|
251
|
-
* </div>
|
|
252
238
|
*
|
|
253
|
-
*
|
|
254
|
-
* <code>
|
|
239
|
+
* @example
|
|
255
240
|
* function setup() {
|
|
256
241
|
* createCanvas(100, 100);
|
|
257
242
|
*
|
|
@@ -271,11 +256,8 @@ const HALF_PI = _PI / 2;
|
|
|
271
256
|
*
|
|
272
257
|
* describe('A horizontal black line on a gray background.');
|
|
273
258
|
* }
|
|
274
|
-
* </code>
|
|
275
|
-
* </div>
|
|
276
259
|
*
|
|
277
|
-
*
|
|
278
|
-
* <code>
|
|
260
|
+
* @example
|
|
279
261
|
* function setup() {
|
|
280
262
|
* createCanvas(100, 100);
|
|
281
263
|
*
|
|
@@ -305,8 +287,6 @@ const HALF_PI = _PI / 2;
|
|
|
305
287
|
* fill(0, 0, 255);
|
|
306
288
|
* circle(x2, 0, 20);
|
|
307
289
|
* }
|
|
308
|
-
* </code>
|
|
309
|
-
* </div>
|
|
310
290
|
*/
|
|
311
291
|
const PI = _PI;
|
|
312
292
|
|
|
@@ -325,8 +305,6 @@ const PI = _PI;
|
|
|
325
305
|
* @final
|
|
326
306
|
*
|
|
327
307
|
* @example
|
|
328
|
-
* <div>
|
|
329
|
-
* <code>
|
|
330
308
|
* function setup() {
|
|
331
309
|
* createCanvas(100, 100);
|
|
332
310
|
*
|
|
@@ -337,11 +315,8 @@ const PI = _PI;
|
|
|
337
315
|
*
|
|
338
316
|
* describe('A one-eighth slice of a circle drawn in white on a gray background.');
|
|
339
317
|
* }
|
|
340
|
-
* </code>
|
|
341
|
-
* </div>
|
|
342
318
|
*
|
|
343
|
-
*
|
|
344
|
-
* <code>
|
|
319
|
+
* @example
|
|
345
320
|
* function setup() {
|
|
346
321
|
* createCanvas(100, 100);
|
|
347
322
|
*
|
|
@@ -361,11 +336,8 @@ const PI = _PI;
|
|
|
361
336
|
*
|
|
362
337
|
* describe('Two black lines that form a "V" opening towards the bottom-right corner of a gray square.');
|
|
363
338
|
* }
|
|
364
|
-
* </code>
|
|
365
|
-
* </div>
|
|
366
339
|
*
|
|
367
|
-
*
|
|
368
|
-
* <code>
|
|
340
|
+
* @example
|
|
369
341
|
* function setup() {
|
|
370
342
|
* createCanvas(100, 100);
|
|
371
343
|
*
|
|
@@ -395,8 +367,6 @@ const PI = _PI;
|
|
|
395
367
|
* fill(0, 0, 255);
|
|
396
368
|
* circle(x2, 0, 20);
|
|
397
369
|
* }
|
|
398
|
-
* </code>
|
|
399
|
-
* </div>
|
|
400
370
|
*/
|
|
401
371
|
const QUARTER_PI = _PI / 4;
|
|
402
372
|
|
|
@@ -415,8 +385,6 @@ const QUARTER_PI = _PI / 4;
|
|
|
415
385
|
* @final
|
|
416
386
|
*
|
|
417
387
|
* @example
|
|
418
|
-
* <div>
|
|
419
|
-
* <code>
|
|
420
388
|
* function setup() {
|
|
421
389
|
* createCanvas(100, 100);
|
|
422
390
|
*
|
|
@@ -427,11 +395,8 @@ const QUARTER_PI = _PI / 4;
|
|
|
427
395
|
*
|
|
428
396
|
* describe('A white circle drawn on a gray background.');
|
|
429
397
|
* }
|
|
430
|
-
* </code>
|
|
431
|
-
* </div>
|
|
432
398
|
*
|
|
433
|
-
*
|
|
434
|
-
* <code>
|
|
399
|
+
* @example
|
|
435
400
|
* function setup() {
|
|
436
401
|
* createCanvas(100, 100);
|
|
437
402
|
*
|
|
@@ -456,11 +421,8 @@ const QUARTER_PI = _PI / 4;
|
|
|
456
421
|
* 'Two horizontal black lines on a gray background. A thick line extends from the center toward the right. A thin line extends from the end of the thick line.'
|
|
457
422
|
* );
|
|
458
423
|
* }
|
|
459
|
-
* </code>
|
|
460
|
-
* </div>
|
|
461
424
|
*
|
|
462
|
-
*
|
|
463
|
-
* <code>
|
|
425
|
+
* @example
|
|
464
426
|
* function setup() {
|
|
465
427
|
* createCanvas(100, 100);
|
|
466
428
|
*
|
|
@@ -490,8 +452,6 @@ const QUARTER_PI = _PI / 4;
|
|
|
490
452
|
* fill(0, 0, 255);
|
|
491
453
|
* circle(x2, 0, 10);
|
|
492
454
|
* }
|
|
493
|
-
* </code>
|
|
494
|
-
* </div>
|
|
495
455
|
*/
|
|
496
456
|
const TAU = _PI * 2;
|
|
497
457
|
|
|
@@ -510,8 +470,6 @@ const TAU = _PI * 2;
|
|
|
510
470
|
* @final
|
|
511
471
|
*
|
|
512
472
|
* @example
|
|
513
|
-
* <div>
|
|
514
|
-
* <code>
|
|
515
473
|
* function setup() {
|
|
516
474
|
* createCanvas(100, 100);
|
|
517
475
|
*
|
|
@@ -522,11 +480,8 @@ const TAU = _PI * 2;
|
|
|
522
480
|
*
|
|
523
481
|
* describe('A white circle drawn on a gray background.');
|
|
524
482
|
* }
|
|
525
|
-
* </code>
|
|
526
|
-
* </div>
|
|
527
483
|
*
|
|
528
|
-
*
|
|
529
|
-
* <code>
|
|
484
|
+
* @example
|
|
530
485
|
* function setup() {
|
|
531
486
|
* createCanvas(100, 100);
|
|
532
487
|
*
|
|
@@ -551,11 +506,8 @@ const TAU = _PI * 2;
|
|
|
551
506
|
* 'Two horizontal black lines on a gray background. A thick line extends from the center toward the right. A thin line extends from the end of the thick line.'
|
|
552
507
|
* );
|
|
553
508
|
* }
|
|
554
|
-
* </code>
|
|
555
|
-
* </div>
|
|
556
509
|
*
|
|
557
|
-
*
|
|
558
|
-
* <code>
|
|
510
|
+
* @example
|
|
559
511
|
* function setup() {
|
|
560
512
|
* createCanvas(100, 100);
|
|
561
513
|
*
|
|
@@ -585,8 +537,6 @@ const TAU = _PI * 2;
|
|
|
585
537
|
* fill(0, 0, 255);
|
|
586
538
|
* circle(x2, 0, 10);
|
|
587
539
|
* }
|
|
588
|
-
* </code>
|
|
589
|
-
* </div>
|
|
590
540
|
*/
|
|
591
541
|
const TWO_PI = _PI * 2;
|
|
592
542
|
|
|
@@ -1626,21 +1576,32 @@ Object.fromEntries(
|
|
|
1626
1576
|
);
|
|
1627
1577
|
|
|
1628
1578
|
const uniforms$5 = `
|
|
1629
|
-
|
|
1579
|
+
// Group 0: Material Properties
|
|
1580
|
+
struct MaterialUniforms {
|
|
1581
|
+
uUseVertexColor: u32,
|
|
1582
|
+
}
|
|
1583
|
+
|
|
1584
|
+
// Group 1: Model Transform
|
|
1585
|
+
struct ModelUniforms {
|
|
1630
1586
|
// @p5 ifdef Vertex getWorldInputs
|
|
1631
1587
|
uModelMatrix: mat4x4<f32>,
|
|
1632
|
-
uViewMatrix: mat4x4<f32>,
|
|
1633
1588
|
uModelNormalMatrix: mat3x3<f32>,
|
|
1634
|
-
uCameraNormalMatrix: mat3x3<f32>,
|
|
1635
1589
|
// @p5 endif
|
|
1636
1590
|
// @p5 ifndef Vertex getWorldInputs
|
|
1637
1591
|
uModelViewMatrix: mat4x4<f32>,
|
|
1638
1592
|
uNormalMatrix: mat3x3<f32>,
|
|
1639
1593
|
// @p5 endif
|
|
1640
|
-
uProjectionMatrix: mat4x4<f32>,
|
|
1641
1594
|
uMaterialColor: vec4<f32>,
|
|
1642
|
-
|
|
1643
|
-
|
|
1595
|
+
}
|
|
1596
|
+
|
|
1597
|
+
// Group 2: Camera and Projection
|
|
1598
|
+
struct CameraUniforms {
|
|
1599
|
+
uProjectionMatrix: mat4x4<f32>,
|
|
1600
|
+
// @p5 ifdef Vertex getWorldInputs
|
|
1601
|
+
uViewMatrix: mat4x4<f32>,
|
|
1602
|
+
// @p5 endif
|
|
1603
|
+
uCameraNormalMatrix: mat3x3<f32>,
|
|
1604
|
+
}
|
|
1644
1605
|
`;
|
|
1645
1606
|
|
|
1646
1607
|
const colorVertexShader = `
|
|
@@ -1659,7 +1620,9 @@ struct VertexOutput {
|
|
|
1659
1620
|
};
|
|
1660
1621
|
|
|
1661
1622
|
${uniforms$5}
|
|
1662
|
-
@group(0) @binding(0) var<uniform>
|
|
1623
|
+
@group(0) @binding(0) var<uniform> material: MaterialUniforms;
|
|
1624
|
+
@group(1) @binding(0) var<uniform> model: ModelUniforms;
|
|
1625
|
+
@group(2) @binding(0) var<uniform> camera: CameraUniforms;
|
|
1663
1626
|
|
|
1664
1627
|
struct Vertex {
|
|
1665
1628
|
position: vec3<f32>,
|
|
@@ -1673,12 +1636,12 @@ fn main(input: VertexInput) -> VertexOutput {
|
|
|
1673
1636
|
HOOK_beforeVertex();
|
|
1674
1637
|
var output: VertexOutput;
|
|
1675
1638
|
|
|
1676
|
-
let useVertexColor = (
|
|
1639
|
+
let useVertexColor = (material.uUseVertexColor != 0 && input.aVertexColor.x >= 0.0);
|
|
1677
1640
|
var inputs = Vertex(
|
|
1678
1641
|
input.aPosition,
|
|
1679
1642
|
input.aNormal,
|
|
1680
1643
|
input.aTexCoord,
|
|
1681
|
-
select(
|
|
1644
|
+
select(model.uMaterialColor, input.aVertexColor, useVertexColor)
|
|
1682
1645
|
);
|
|
1683
1646
|
|
|
1684
1647
|
// @p5 ifdef Vertex getObjectInputs
|
|
@@ -1686,20 +1649,20 @@ fn main(input: VertexInput) -> VertexOutput {
|
|
|
1686
1649
|
// @p5 endif
|
|
1687
1650
|
|
|
1688
1651
|
// @p5 ifdef Vertex getWorldInputs
|
|
1689
|
-
inputs.position = (
|
|
1690
|
-
inputs.normal =
|
|
1652
|
+
inputs.position = (model.uModelMatrix * vec4<f32>(inputs.position, 1.0)).xyz;
|
|
1653
|
+
inputs.normal = model.uModelNormalMatrix * inputs.normal;
|
|
1691
1654
|
inputs = HOOK_getWorldInputs(inputs);
|
|
1692
1655
|
// @p5 endif
|
|
1693
1656
|
|
|
1694
1657
|
// @p5 ifdef Vertex getWorldInputs
|
|
1695
1658
|
// Already multiplied by the model matrix, just apply view
|
|
1696
|
-
inputs.position = (
|
|
1697
|
-
inputs.normal =
|
|
1659
|
+
inputs.position = (camera.uViewMatrix * vec4<f32>(inputs.position, 1.0)).xyz;
|
|
1660
|
+
inputs.normal = camera.uCameraNormalMatrix * inputs.normal;
|
|
1698
1661
|
// @p5 endif
|
|
1699
1662
|
// @p5 ifndef Vertex getWorldInputs
|
|
1700
1663
|
// Apply both at once
|
|
1701
|
-
inputs.position = (
|
|
1702
|
-
inputs.normal =
|
|
1664
|
+
inputs.position = (model.uModelViewMatrix * vec4<f32>(inputs.position, 1.0)).xyz;
|
|
1665
|
+
inputs.normal = model.uNormalMatrix * inputs.normal;
|
|
1703
1666
|
// @p5 endif
|
|
1704
1667
|
|
|
1705
1668
|
// @p5 ifdef Vertex getCameraInputs
|
|
@@ -1710,7 +1673,7 @@ fn main(input: VertexInput) -> VertexOutput {
|
|
|
1710
1673
|
output.vVertexNormal = normalize(inputs.normal);
|
|
1711
1674
|
output.vColor = inputs.color;
|
|
1712
1675
|
|
|
1713
|
-
output.Position =
|
|
1676
|
+
output.Position = camera.uProjectionMatrix * vec4<f32>(inputs.position, 1.0);
|
|
1714
1677
|
|
|
1715
1678
|
HOOK_afterVertex();
|
|
1716
1679
|
return output;
|
|
@@ -1725,7 +1688,9 @@ struct FragmentInput {
|
|
|
1725
1688
|
};
|
|
1726
1689
|
|
|
1727
1690
|
${uniforms$5}
|
|
1728
|
-
@group(0) @binding(0) var<uniform>
|
|
1691
|
+
@group(0) @binding(0) var<uniform> material: MaterialUniforms;
|
|
1692
|
+
@group(1) @binding(0) var<uniform> model: ModelUniforms;
|
|
1693
|
+
@group(2) @binding(0) var<uniform> camera: CameraUniforms;
|
|
1729
1694
|
|
|
1730
1695
|
|
|
1731
1696
|
@fragment
|
|
@@ -1739,7 +1704,17 @@ fn main(input: FragmentInput) -> @location(0) vec4<f32> {
|
|
|
1739
1704
|
`;
|
|
1740
1705
|
|
|
1741
1706
|
const uniforms$4 = `
|
|
1742
|
-
|
|
1707
|
+
// Group 0: Stroke Properties
|
|
1708
|
+
struct StrokeUniforms {
|
|
1709
|
+
uStrokeWeight: f32,
|
|
1710
|
+
uUseLineColor: f32,
|
|
1711
|
+
uSimpleLines: f32,
|
|
1712
|
+
uStrokeCap: u32,
|
|
1713
|
+
uStrokeJoin: u32,
|
|
1714
|
+
}
|
|
1715
|
+
|
|
1716
|
+
// Group 1: Model Transform
|
|
1717
|
+
struct ModelUniforms {
|
|
1743
1718
|
// @p5 ifdef StrokeVertex getWorldInputs
|
|
1744
1719
|
uModelMatrix: mat4x4<f32>,
|
|
1745
1720
|
uViewMatrix: mat4x4<f32>,
|
|
@@ -1748,15 +1723,15 @@ struct Uniforms {
|
|
|
1748
1723
|
uModelViewMatrix: mat4x4<f32>,
|
|
1749
1724
|
// @p5 endif
|
|
1750
1725
|
uMaterialColor: vec4<f32>,
|
|
1726
|
+
}
|
|
1727
|
+
|
|
1728
|
+
// Group 2: Camera and Projection
|
|
1729
|
+
struct CameraUniforms {
|
|
1751
1730
|
uProjectionMatrix: mat4x4<f32>,
|
|
1752
|
-
uStrokeWeight: f32,
|
|
1753
|
-
uUseLineColor: f32,
|
|
1754
|
-
uSimpleLines: f32,
|
|
1755
1731
|
uViewport: vec4<f32>,
|
|
1756
1732
|
uPerspective: u32,
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
}`;
|
|
1733
|
+
}
|
|
1734
|
+
`;
|
|
1760
1735
|
|
|
1761
1736
|
const lineVertexShader = `
|
|
1762
1737
|
struct StrokeVertexInput {
|
|
@@ -1780,7 +1755,9 @@ struct StrokeVertexOutput {
|
|
|
1780
1755
|
};
|
|
1781
1756
|
|
|
1782
1757
|
${uniforms$4}
|
|
1783
|
-
@group(0) @binding(0) var<uniform>
|
|
1758
|
+
@group(0) @binding(0) var<uniform> stroke: StrokeUniforms;
|
|
1759
|
+
@group(1) @binding(0) var<uniform> model: ModelUniforms;
|
|
1760
|
+
@group(2) @binding(0) var<uniform> camera: CameraUniforms;
|
|
1784
1761
|
|
|
1785
1762
|
struct StrokeVertex {
|
|
1786
1763
|
position: vec3<f32>,
|
|
@@ -1813,7 +1790,7 @@ fn lineIntersection(aPoint: vec2f, aDir: vec2f, bPoint: vec2f, bDir: vec2f) -> v
|
|
|
1813
1790
|
fn main(input: StrokeVertexInput) -> StrokeVertexOutput {
|
|
1814
1791
|
HOOK_beforeVertex();
|
|
1815
1792
|
var output: StrokeVertexOutput;
|
|
1816
|
-
let simpleLines = (
|
|
1793
|
+
let simpleLines = (stroke.uSimpleLines != 0.);
|
|
1817
1794
|
if (!simpleLines) {
|
|
1818
1795
|
if (all(input.aTangentIn == vec3<f32>()) != all(input.aTangentOut == vec3<f32>())) {
|
|
1819
1796
|
output.vCap = 1.;
|
|
@@ -1830,17 +1807,17 @@ fn main(input: StrokeVertexInput) -> StrokeVertexOutput {
|
|
|
1830
1807
|
}
|
|
1831
1808
|
}
|
|
1832
1809
|
var lineColor: vec4<f32>;
|
|
1833
|
-
if (
|
|
1810
|
+
if (stroke.uUseLineColor != 0.) {
|
|
1834
1811
|
lineColor = input.aVertexColor;
|
|
1835
1812
|
} else {
|
|
1836
|
-
lineColor =
|
|
1813
|
+
lineColor = model.uMaterialColor;
|
|
1837
1814
|
}
|
|
1838
1815
|
var inputs = StrokeVertex(
|
|
1839
1816
|
input.aPosition.xyz,
|
|
1840
1817
|
input.aTangentIn,
|
|
1841
1818
|
input.aTangentOut,
|
|
1842
1819
|
lineColor,
|
|
1843
|
-
|
|
1820
|
+
stroke.uStrokeWeight
|
|
1844
1821
|
);
|
|
1845
1822
|
|
|
1846
1823
|
// @p5 ifdef StrokeVertex getObjectInputs
|
|
@@ -1848,23 +1825,23 @@ fn main(input: StrokeVertexInput) -> StrokeVertexOutput {
|
|
|
1848
1825
|
// @p5 endif
|
|
1849
1826
|
|
|
1850
1827
|
// @p5 ifdef StrokeVertex getWorldInputs
|
|
1851
|
-
inputs.position = (
|
|
1852
|
-
inputs.tangentIn = (
|
|
1853
|
-
inputs.tangentOut = (
|
|
1828
|
+
inputs.position = (model.uModelMatrix * vec4<f32>(inputs.position, 1.)).xyz;
|
|
1829
|
+
inputs.tangentIn = (model.uModelMatrix * vec4<f32>(input.aTangentIn, 1.)).xyz;
|
|
1830
|
+
inputs.tangentOut = (model.uModelMatrix * vec4<f32>(input.aTangentOut, 1.)).xyz;
|
|
1854
1831
|
inputs = HOOK_getWorldInputs(inputs);
|
|
1855
1832
|
// @p5 endif
|
|
1856
1833
|
|
|
1857
1834
|
// @p5 ifdef StrokeVertex getWorldInputs
|
|
1858
1835
|
// Already multiplied by the model matrix, just apply view
|
|
1859
|
-
inputs.position = (
|
|
1860
|
-
inputs.tangentIn = (
|
|
1861
|
-
inputs.tangentOut = (
|
|
1836
|
+
inputs.position = (model.uViewMatrix * vec4<f32>(inputs.position, 1.)).xyz;
|
|
1837
|
+
inputs.tangentIn = (model.uViewMatrix * vec4<f32>(input.aTangentIn, 0.)).xyz;
|
|
1838
|
+
inputs.tangentOut = (model.uViewMatrix * vec4<f32>(input.aTangentOut, 0.)).xyz;
|
|
1862
1839
|
// @p5 endif
|
|
1863
1840
|
// @p5 ifndef StrokeVertex getWorldInputs
|
|
1864
1841
|
// Apply both at once
|
|
1865
|
-
inputs.position = (
|
|
1866
|
-
inputs.tangentIn = (
|
|
1867
|
-
inputs.tangentOut = (
|
|
1842
|
+
inputs.position = (model.uModelViewMatrix * vec4<f32>(inputs.position, 1.)).xyz;
|
|
1843
|
+
inputs.tangentIn = (model.uModelViewMatrix * vec4<f32>(input.aTangentIn, 0.)).xyz;
|
|
1844
|
+
inputs.tangentOut = (model.uModelViewMatrix * vec4<f32>(input.aTangentOut, 0.)).xyz;
|
|
1868
1845
|
// @p5 endif
|
|
1869
1846
|
// @p5 ifdef StrokeVertex getCameraInputs
|
|
1870
1847
|
inputs = HOOK_getCameraInputs(inputs);
|
|
@@ -1914,27 +1891,27 @@ fn main(input: StrokeVertexInput) -> StrokeVertexOutput {
|
|
|
1914
1891
|
posqIn.z -= dynamicZAdjustment;
|
|
1915
1892
|
posqOut.z -= dynamicZAdjustment;
|
|
1916
1893
|
|
|
1917
|
-
var p =
|
|
1918
|
-
var qIn =
|
|
1919
|
-
var qOut =
|
|
1894
|
+
var p = camera.uProjectionMatrix * posp;
|
|
1895
|
+
var qIn = camera.uProjectionMatrix * posqIn;
|
|
1896
|
+
var qOut = camera.uProjectionMatrix * posqOut;
|
|
1920
1897
|
|
|
1921
|
-
var tangentIn = normalize((qIn.xy * p.w - p.xy * qIn.w) *
|
|
1922
|
-
var tangentOut = normalize((qOut.xy * p.w - p.xy * qOut.w) *
|
|
1898
|
+
var tangentIn = normalize((qIn.xy * p.w - p.xy * qIn.w) * camera.uViewport.zw);
|
|
1899
|
+
var tangentOut = normalize((qOut.xy * p.w - p.xy * qOut.w) * camera.uViewport.zw);
|
|
1923
1900
|
|
|
1924
1901
|
var curPerspScale = vec2<f32>();
|
|
1925
|
-
if (
|
|
1902
|
+
if (camera.uPerspective == 1) {
|
|
1926
1903
|
// Perspective ---
|
|
1927
1904
|
// convert from world to clip by multiplying with projection scaling factor
|
|
1928
1905
|
// to get the right thickness (see https://github.com/processing/processing/issues/5182)
|
|
1929
1906
|
|
|
1930
1907
|
// The y value of the projection matrix may be flipped if rendering to a Framebuffer.
|
|
1931
1908
|
// Multiplying again by its sign here negates the flip to get just the scale.
|
|
1932
|
-
curPerspScale = (
|
|
1909
|
+
curPerspScale = (camera.uProjectionMatrix * vec4(1., sign(camera.uProjectionMatrix[1][1]), 0., 0.)).xy;
|
|
1933
1910
|
} else {
|
|
1934
1911
|
// No Perspective ---
|
|
1935
1912
|
// multiply by W (to cancel out division by W later in the pipeline) and
|
|
1936
1913
|
// convert from screen to clip (derived from clip to screen above)
|
|
1937
|
-
curPerspScale = p.w / (0.5 *
|
|
1914
|
+
curPerspScale = p.w / (0.5 * camera.uViewport.zw);
|
|
1938
1915
|
}
|
|
1939
1916
|
|
|
1940
1917
|
var offset = vec2<f32>();
|
|
@@ -1957,7 +1934,7 @@ fn main(input: StrokeVertexInput) -> StrokeVertexOutput {
|
|
|
1957
1934
|
if (sideEnum == 2.) {
|
|
1958
1935
|
// Calculate the position + tangent on either side of the join, and
|
|
1959
1936
|
// find where the lines intersect to find the elbow of the join
|
|
1960
|
-
var c = (posp.xy / posp.w + vec2<f32>(1.)) * 0.5 *
|
|
1937
|
+
var c = (posp.xy / posp.w + vec2<f32>(1.)) * 0.5 * camera.uViewport.zw;
|
|
1961
1938
|
|
|
1962
1939
|
var intersection = lineIntersection(
|
|
1963
1940
|
c + (side * normalIn * inputs.weight / 2.),
|
|
@@ -1983,7 +1960,7 @@ fn main(input: StrokeVertexInput) -> StrokeVertexOutput {
|
|
|
1983
1960
|
offset = side * normalOut * inputs.weight / 2.;
|
|
1984
1961
|
}
|
|
1985
1962
|
}
|
|
1986
|
-
if (
|
|
1963
|
+
if (stroke.uStrokeJoin == 2) {
|
|
1987
1964
|
var avgNormal = vec2<f32>(-output.vTangent.y, output.vTangent.x);
|
|
1988
1965
|
output.vMaxDist = abs(dot(avgNormal, normalIn * inputs.weight / 2.));
|
|
1989
1966
|
} else {
|
|
@@ -2032,7 +2009,9 @@ struct StrokeFragmentInput {
|
|
|
2032
2009
|
}
|
|
2033
2010
|
|
|
2034
2011
|
${uniforms$4}
|
|
2035
|
-
@group(0) @binding(0) var<uniform>
|
|
2012
|
+
@group(0) @binding(0) var<uniform> stroke: StrokeUniforms;
|
|
2013
|
+
@group(1) @binding(0) var<uniform> model: ModelUniforms;
|
|
2014
|
+
@group(2) @binding(0) var<uniform> camera: CameraUniforms;
|
|
2036
2015
|
|
|
2037
2016
|
|
|
2038
2017
|
fn distSquared(a: vec2<f32>, b: vec2<f32>) -> f32 {
|
|
@@ -2061,12 +2040,12 @@ fn main(input: StrokeFragmentInput) -> @location(0) vec4<f32> {
|
|
|
2061
2040
|
|
|
2062
2041
|
if (input.vCap > 0.) {
|
|
2063
2042
|
if (
|
|
2064
|
-
|
|
2043
|
+
stroke.uStrokeCap == STROKE_CAP_ROUND &&
|
|
2065
2044
|
HOOK_shouldDiscard(distSquared(inputs.position, inputs.center) > inputs.strokeWeight * inputs.strokeWeight * 0.25)
|
|
2066
2045
|
) {
|
|
2067
2046
|
discard;
|
|
2068
2047
|
} else if (
|
|
2069
|
-
|
|
2048
|
+
stroke.uStrokeCap == STROKE_CAP_SQUARE &&
|
|
2070
2049
|
HOOK_shouldDiscard(dot(inputs.position - inputs.center, inputs.tangent) > 0.)
|
|
2071
2050
|
) {
|
|
2072
2051
|
discard;
|
|
@@ -2075,11 +2054,11 @@ fn main(input: StrokeFragmentInput) -> @location(0) vec4<f32> {
|
|
|
2075
2054
|
}
|
|
2076
2055
|
} else if (input.vJoin > 0.) {
|
|
2077
2056
|
if (
|
|
2078
|
-
|
|
2057
|
+
stroke.uStrokeJoin == STROKE_JOIN_ROUND &&
|
|
2079
2058
|
HOOK_shouldDiscard(distSquared(inputs.position, inputs.center) > inputs.strokeWeight * inputs.strokeWeight * 0.25)
|
|
2080
2059
|
) {
|
|
2081
2060
|
discard;
|
|
2082
|
-
} else if (
|
|
2061
|
+
} else if (stroke.uStrokeJoin == STROKE_JOIN_BEVEL) {
|
|
2083
2062
|
let normal = vec2<f32>(-inputs.tangent.y, -inputs.tangent.x);
|
|
2084
2063
|
if (HOOK_shouldDiscard(abs(dot(inputs.position - inputs.center, normal)) > input.vMaxDist)) {
|
|
2085
2064
|
discard;
|
|
@@ -2096,42 +2075,31 @@ fn main(input: StrokeFragmentInput) -> @location(0) vec4<f32> {
|
|
|
2096
2075
|
`;
|
|
2097
2076
|
|
|
2098
2077
|
const uniforms$3 = `
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
uModelMatrix: mat4x4<f32>,
|
|
2102
|
-
uModelNormalMatrix: mat3x3<f32>,
|
|
2103
|
-
uCameraNormalMatrix: mat3x3<f32>,
|
|
2104
|
-
// @p5 endif
|
|
2105
|
-
// @p5 ifndef Vertex getWorldInputs
|
|
2106
|
-
uModelViewMatrix: mat4x4<f32>,
|
|
2107
|
-
uNormalMatrix: mat3x3<f32>,
|
|
2108
|
-
// @p5 endif
|
|
2109
|
-
uViewMatrix: mat4x4<f32>,
|
|
2110
|
-
uProjectionMatrix: mat4x4<f32>,
|
|
2111
|
-
uMaterialColor: vec4<f32>,
|
|
2078
|
+
// Group 0: Material Properties
|
|
2079
|
+
struct MaterialUniforms {
|
|
2112
2080
|
uUseVertexColor: u32,
|
|
2113
|
-
|
|
2114
2081
|
uHasSetAmbient: u32,
|
|
2115
2082
|
uAmbientColor: vec3<f32>,
|
|
2116
2083
|
uSpecularMatColor: vec4<f32>,
|
|
2117
2084
|
uAmbientMatColor: vec4<f32>,
|
|
2118
2085
|
uEmissiveMatColor: vec4<f32>,
|
|
2119
|
-
|
|
2120
2086
|
uTint: vec4<f32>,
|
|
2121
2087
|
isTexture: u32,
|
|
2088
|
+
uSpecular: u32,
|
|
2089
|
+
uShininess: f32,
|
|
2090
|
+
uMetallic: f32,
|
|
2091
|
+
}
|
|
2122
2092
|
|
|
2123
|
-
|
|
2124
|
-
|
|
2093
|
+
// Group 0: Lighting
|
|
2094
|
+
struct LightingUniforms {
|
|
2125
2095
|
uDirectionalLightCount: i32,
|
|
2126
2096
|
uLightingDirection: array<vec3<f32>, 5>,
|
|
2127
2097
|
uDirectionalDiffuseColors: array<vec3<f32>, 5>,
|
|
2128
2098
|
uDirectionalSpecularColors: array<vec3<f32>, 5>,
|
|
2129
|
-
|
|
2130
2099
|
uPointLightCount: i32,
|
|
2131
2100
|
uPointLightLocation: array<vec3<f32>, 5>,
|
|
2132
2101
|
uPointLightDiffuseColors: array<vec3<f32>, 5>,
|
|
2133
2102
|
uPointLightSpecularColors: array<vec3<f32>, 5>,
|
|
2134
|
-
|
|
2135
2103
|
uSpotLightCount: i32,
|
|
2136
2104
|
uSpotLightAngle: vec4<f32>,
|
|
2137
2105
|
uSpotLightConc: vec4<f32>,
|
|
@@ -2139,18 +2107,32 @@ struct Uniforms {
|
|
|
2139
2107
|
uSpotLightSpecularColors: array<vec3<f32>, 4>,
|
|
2140
2108
|
uSpotLightLocation: array<vec3<f32>, 4>,
|
|
2141
2109
|
uSpotLightDirection: array<vec3<f32>, 4>,
|
|
2142
|
-
|
|
2143
|
-
uSpecular: u32,
|
|
2144
|
-
uShininess: f32,
|
|
2145
|
-
uMetallic: f32,
|
|
2146
|
-
|
|
2147
2110
|
uConstantAttenuation: f32,
|
|
2148
2111
|
uLinearAttenuation: f32,
|
|
2149
2112
|
uQuadraticAttenuation: f32,
|
|
2150
|
-
|
|
2151
2113
|
uUseImageLight: u32,
|
|
2152
2114
|
uUseLighting: u32,
|
|
2153
|
-
}
|
|
2115
|
+
}
|
|
2116
|
+
|
|
2117
|
+
// Group 1: Model Transform
|
|
2118
|
+
struct ModelUniforms {
|
|
2119
|
+
// @p5 ifdef Vertex getWorldInputs
|
|
2120
|
+
uModelMatrix: mat4x4<f32>,
|
|
2121
|
+
uModelNormalMatrix: mat3x3<f32>,
|
|
2122
|
+
// @p5 endif
|
|
2123
|
+
// @p5 ifndef Vertex getWorldInputs
|
|
2124
|
+
uModelViewMatrix: mat4x4<f32>,
|
|
2125
|
+
uNormalMatrix: mat3x3<f32>,
|
|
2126
|
+
// @p5 endif
|
|
2127
|
+
uMaterialColor: vec4<f32>,
|
|
2128
|
+
}
|
|
2129
|
+
|
|
2130
|
+
// Group 2: Camera and Projection
|
|
2131
|
+
struct CameraUniforms {
|
|
2132
|
+
uViewMatrix: mat4x4<f32>,
|
|
2133
|
+
uProjectionMatrix: mat4x4<f32>,
|
|
2134
|
+
uCameraNormalMatrix: mat3x3<f32>,
|
|
2135
|
+
}
|
|
2154
2136
|
`;
|
|
2155
2137
|
|
|
2156
2138
|
const materialVertexShader = `
|
|
@@ -2170,7 +2152,10 @@ struct VertexOutput {
|
|
|
2170
2152
|
};
|
|
2171
2153
|
|
|
2172
2154
|
${uniforms$3}
|
|
2173
|
-
@group(0) @binding(0) var<uniform>
|
|
2155
|
+
@group(0) @binding(0) var<uniform> material: MaterialUniforms;
|
|
2156
|
+
@group(0) @binding(1) var<uniform> lighting: LightingUniforms;
|
|
2157
|
+
@group(1) @binding(0) var<uniform> model: ModelUniforms;
|
|
2158
|
+
@group(2) @binding(0) var<uniform> camera: CameraUniforms;
|
|
2174
2159
|
|
|
2175
2160
|
struct Vertex {
|
|
2176
2161
|
position: vec3<f32>,
|
|
@@ -2184,12 +2169,12 @@ fn main(input: VertexInput) -> VertexOutput {
|
|
|
2184
2169
|
HOOK_beforeVertex();
|
|
2185
2170
|
var output: VertexOutput;
|
|
2186
2171
|
|
|
2187
|
-
let useVertexColor = (
|
|
2172
|
+
let useVertexColor = (material.uUseVertexColor != 0 && input.aVertexColor.x >= 0.0);
|
|
2188
2173
|
var inputs = Vertex(
|
|
2189
2174
|
input.aPosition,
|
|
2190
2175
|
input.aNormal,
|
|
2191
2176
|
input.aTexCoord,
|
|
2192
|
-
select(
|
|
2177
|
+
select(model.uMaterialColor, input.aVertexColor, useVertexColor)
|
|
2193
2178
|
);
|
|
2194
2179
|
|
|
2195
2180
|
// @p5 ifdef Vertex getObjectInputs
|
|
@@ -2197,20 +2182,20 @@ fn main(input: VertexInput) -> VertexOutput {
|
|
|
2197
2182
|
// @p5 endif
|
|
2198
2183
|
|
|
2199
2184
|
// @p5 ifdef Vertex getWorldInputs
|
|
2200
|
-
inputs.position = (
|
|
2201
|
-
inputs.normal =
|
|
2185
|
+
inputs.position = (model.uModelMatrix * vec4<f32>(inputs.position, 1.0)).xyz;
|
|
2186
|
+
inputs.normal = model.uModelNormalMatrix * inputs.normal;
|
|
2202
2187
|
inputs = HOOK_getWorldInputs(inputs);
|
|
2203
2188
|
// @p5 endif
|
|
2204
2189
|
|
|
2205
2190
|
// @p5 ifdef Vertex getWorldInputs
|
|
2206
2191
|
// Already multiplied by the model matrix, just apply view
|
|
2207
|
-
inputs.position = (
|
|
2208
|
-
inputs.normal =
|
|
2192
|
+
inputs.position = (camera.uViewMatrix * vec4<f32>(inputs.position, 1.0)).xyz;
|
|
2193
|
+
inputs.normal = camera.uCameraNormalMatrix * inputs.normal;
|
|
2209
2194
|
// @p5 endif
|
|
2210
2195
|
// @p5 ifndef Vertex getWorldInputs
|
|
2211
2196
|
// Apply both at once
|
|
2212
|
-
inputs.position = (
|
|
2213
|
-
inputs.normal =
|
|
2197
|
+
inputs.position = (model.uModelViewMatrix * vec4<f32>(inputs.position, 1.0)).xyz;
|
|
2198
|
+
inputs.normal = model.uNormalMatrix * inputs.normal;
|
|
2214
2199
|
// @p5 endif
|
|
2215
2200
|
|
|
2216
2201
|
// @p5 ifdef Vertex getCameraInputs
|
|
@@ -2222,7 +2207,7 @@ fn main(input: VertexInput) -> VertexOutput {
|
|
|
2222
2207
|
output.vNormal = normalize(inputs.normal);
|
|
2223
2208
|
output.vColor = inputs.color;
|
|
2224
2209
|
|
|
2225
|
-
output.Position =
|
|
2210
|
+
output.Position = camera.uProjectionMatrix * vec4<f32>(inputs.position, 1.0);
|
|
2226
2211
|
|
|
2227
2212
|
HOOK_afterVertex();
|
|
2228
2213
|
return output;
|
|
@@ -2238,15 +2223,16 @@ struct FragmentInput {
|
|
|
2238
2223
|
};
|
|
2239
2224
|
|
|
2240
2225
|
${uniforms$3}
|
|
2241
|
-
@group(0) @binding(0) var<uniform>
|
|
2242
|
-
|
|
2243
|
-
@group(0) @binding(
|
|
2244
|
-
@group(0) @binding(
|
|
2245
|
-
|
|
2246
|
-
@group(0) @binding(
|
|
2247
|
-
@group(0) @binding(
|
|
2248
|
-
@group(0) @binding(
|
|
2249
|
-
@group(
|
|
2226
|
+
@group(0) @binding(0) var<uniform> material: MaterialUniforms;
|
|
2227
|
+
@group(0) @binding(1) var<uniform> lighting: LightingUniforms;
|
|
2228
|
+
@group(0) @binding(2) var uSampler: texture_2d<f32>;
|
|
2229
|
+
@group(0) @binding(3) var uSampler_sampler: sampler;
|
|
2230
|
+
@group(0) @binding(4) var environmentMapDiffused: texture_2d<f32>;
|
|
2231
|
+
@group(0) @binding(5) var environmentMapDiffused_sampler: sampler;
|
|
2232
|
+
@group(0) @binding(6) var environmentMapSpecular: texture_2d<f32>;
|
|
2233
|
+
@group(0) @binding(7) var environmentMapSpecular_sampler: sampler;
|
|
2234
|
+
@group(1) @binding(0) var<uniform> model: ModelUniforms;
|
|
2235
|
+
@group(2) @binding(0) var<uniform> camera: CameraUniforms;
|
|
2250
2236
|
|
|
2251
2237
|
struct ColorComponents {
|
|
2252
2238
|
baseColor: vec3<f32>,
|
|
@@ -2309,7 +2295,7 @@ fn mapTextureToNormal(v: vec3<f32>) -> vec2<f32> {
|
|
|
2309
2295
|
fn calculateImageDiffuse(vNormal: vec3<f32>, vViewPosition: vec3<f32>, metallic: f32) -> vec3<f32> {
|
|
2310
2296
|
// make 2 seperate builds
|
|
2311
2297
|
let worldCameraPosition = vec3<f32>(0.0, 0.0, 0.0); // hardcoded world camera position
|
|
2312
|
-
let worldNormal = normalize(vNormal *
|
|
2298
|
+
let worldNormal = normalize(vNormal * camera.uCameraNormalMatrix);
|
|
2313
2299
|
let newTexCoord = mapTextureToNormal(worldNormal);
|
|
2314
2300
|
let texture = textureSample(environmentMapDiffused, environmentMapDiffused_sampler, newTexCoord);
|
|
2315
2301
|
// this is to make the darker sections more dark
|
|
@@ -2321,7 +2307,7 @@ fn calculateImageSpecular(vNormal: vec3<f32>, vViewPosition: vec3<f32>, shinines
|
|
|
2321
2307
|
let worldCameraPosition = vec3<f32>(0.0, 0.0, 0.0);
|
|
2322
2308
|
let worldNormal = normalize(vNormal);
|
|
2323
2309
|
let lightDirection = normalize(vViewPosition - worldCameraPosition);
|
|
2324
|
-
let R = reflect(lightDirection, worldNormal) *
|
|
2310
|
+
let R = reflect(lightDirection, worldNormal) * camera.uCameraNormalMatrix;
|
|
2325
2311
|
let newTexCoord = mapTextureToNormal(R);
|
|
2326
2312
|
|
|
2327
2313
|
// In p5js the range of shininess is >= 1,
|
|
@@ -2370,7 +2356,7 @@ fn singleLight(
|
|
|
2370
2356
|
let specular = select(
|
|
2371
2357
|
0.,
|
|
2372
2358
|
phongSpecular(lightDir, viewDirection, normal, shininess) * specularIntensity,
|
|
2373
|
-
|
|
2359
|
+
material.uSpecular == 1
|
|
2374
2360
|
);
|
|
2375
2361
|
return LightIntensityResult(diffuse, specular);
|
|
2376
2362
|
}
|
|
@@ -2384,69 +2370,69 @@ fn totalLight(
|
|
|
2384
2370
|
var totalSpecular = vec3<f32>(0.0, 0.0, 0.0);
|
|
2385
2371
|
var totalDiffuse = vec3<f32>(0.0, 0.0, 0.0);
|
|
2386
2372
|
|
|
2387
|
-
if (
|
|
2373
|
+
if (lighting.uUseLighting == 0) {
|
|
2388
2374
|
return LightResult(vec3<f32>(1.0, 1.0, 1.0), totalSpecular);
|
|
2389
2375
|
}
|
|
2390
2376
|
|
|
2391
2377
|
let viewDirection = normalize(-modelPosition);
|
|
2392
2378
|
|
|
2393
2379
|
for (var j = 0; j < 5; j++) {
|
|
2394
|
-
if (j <
|
|
2395
|
-
let lightVector = (
|
|
2396
|
-
|
|
2380
|
+
if (j < lighting.uDirectionalLightCount) {
|
|
2381
|
+
let lightVector = (camera.uViewMatrix * vec4<f32>(
|
|
2382
|
+
lighting.uLightingDirection[j],
|
|
2397
2383
|
0.0
|
|
2398
2384
|
)).xyz;
|
|
2399
|
-
let lightColor =
|
|
2400
|
-
let specularColor =
|
|
2385
|
+
let lightColor = lighting.uDirectionalDiffuseColors[j];
|
|
2386
|
+
let specularColor = lighting.uDirectionalSpecularColors[j];
|
|
2401
2387
|
let result = singleLight(viewDirection, normal, lightVector, shininess, metallic);
|
|
2402
2388
|
totalDiffuse += result.diffuse * lightColor;
|
|
2403
2389
|
totalSpecular += result.specular * specularColor;
|
|
2404
2390
|
}
|
|
2405
2391
|
|
|
2406
|
-
if (j <
|
|
2407
|
-
let lightPosition = (
|
|
2408
|
-
|
|
2392
|
+
if (j < lighting.uPointLightCount) {
|
|
2393
|
+
let lightPosition = (camera.uViewMatrix * vec4<f32>(
|
|
2394
|
+
lighting.uPointLightLocation[j],
|
|
2409
2395
|
1.0
|
|
2410
2396
|
)).xyz;
|
|
2411
2397
|
let lightVector = modelPosition - lightPosition;
|
|
2412
2398
|
let lightDistance = length(lightVector);
|
|
2413
2399
|
let lightFalloff = 1.0 / (
|
|
2414
|
-
|
|
2415
|
-
lightDistance *
|
|
2416
|
-
lightDistance * lightDistance *
|
|
2400
|
+
lighting.uConstantAttenuation +
|
|
2401
|
+
lightDistance * lighting.uLinearAttenuation +
|
|
2402
|
+
lightDistance * lightDistance * lighting.uQuadraticAttenuation
|
|
2417
2403
|
);
|
|
2418
|
-
let lightColor =
|
|
2419
|
-
let specularColor =
|
|
2404
|
+
let lightColor = lighting.uPointLightDiffuseColors[j] * lightFalloff;
|
|
2405
|
+
let specularColor = lighting.uPointLightSpecularColors[j] * lightFalloff;
|
|
2420
2406
|
let result = singleLight(viewDirection, normal, lightVector, shininess, metallic);
|
|
2421
2407
|
totalDiffuse += result.diffuse * lightColor;
|
|
2422
2408
|
totalSpecular += result.specular * specularColor;
|
|
2423
2409
|
}
|
|
2424
2410
|
|
|
2425
|
-
if (j <
|
|
2426
|
-
let lightPosition = (
|
|
2427
|
-
|
|
2411
|
+
if (j < lighting.uSpotLightCount) {
|
|
2412
|
+
let lightPosition = (camera.uViewMatrix * vec4<f32>(
|
|
2413
|
+
lighting.uSpotLightLocation[j],
|
|
2428
2414
|
1.0
|
|
2429
2415
|
)).xyz;
|
|
2430
2416
|
let lightVector = modelPosition - lightPosition;
|
|
2431
2417
|
let lightDistance = length(lightVector);
|
|
2432
2418
|
var lightFalloff = 1.0 / (
|
|
2433
|
-
|
|
2434
|
-
lightDistance *
|
|
2435
|
-
lightDistance * lightDistance *
|
|
2419
|
+
lighting.uConstantAttenuation +
|
|
2420
|
+
lightDistance * lighting.uLinearAttenuation +
|
|
2421
|
+
lightDistance * lightDistance * lighting.uQuadraticAttenuation
|
|
2436
2422
|
);
|
|
2437
|
-
let lightDirection = (
|
|
2438
|
-
|
|
2423
|
+
let lightDirection = (camera.uViewMatrix * vec4<f32>(
|
|
2424
|
+
lighting.uSpotLightDirection[j],
|
|
2439
2425
|
0.0
|
|
2440
2426
|
)).xyz;
|
|
2441
2427
|
let spotDot = dot(normalize(lightVector), normalize(lightDirection));
|
|
2442
2428
|
let spotFalloff = select(
|
|
2443
2429
|
0.0,
|
|
2444
|
-
pow(spotDot,
|
|
2445
|
-
spotDot <
|
|
2430
|
+
pow(spotDot, lighting.uSpotLightConc[j]),
|
|
2431
|
+
spotDot < lighting.uSpotLightAngle[j]
|
|
2446
2432
|
);
|
|
2447
2433
|
lightFalloff *= spotFalloff;
|
|
2448
|
-
let lightColor =
|
|
2449
|
-
let specularColor =
|
|
2434
|
+
let lightColor = lighting.uSpotLightDiffuseColors[j];
|
|
2435
|
+
let specularColor = lighting.uSpotLightSpecularColors[j];
|
|
2450
2436
|
let result = singleLight(viewDirection, normal, lightVector, shininess, metallic);
|
|
2451
2437
|
totalDiffuse += result.diffuse * lightColor;
|
|
2452
2438
|
totalSpecular += result.specular * specularColor;
|
|
@@ -2454,7 +2440,7 @@ fn totalLight(
|
|
|
2454
2440
|
}
|
|
2455
2441
|
|
|
2456
2442
|
// Image light contribution
|
|
2457
|
-
if (
|
|
2443
|
+
if (lighting.uUseImageLight != 0) {
|
|
2458
2444
|
totalDiffuse += calculateImageDiffuse(normal, modelPosition, metallic);
|
|
2459
2445
|
totalSpecular += calculateImageSpecular(normal, modelPosition, shininess, metallic);
|
|
2460
2446
|
}
|
|
@@ -2471,19 +2457,19 @@ fn main(input: FragmentInput) -> @location(0) vec4<f32> {
|
|
|
2471
2457
|
|
|
2472
2458
|
let color = select(
|
|
2473
2459
|
input.vColor,
|
|
2474
|
-
textureSample(uSampler, uSampler_sampler, input.vTexCoord) * (
|
|
2475
|
-
|
|
2460
|
+
textureSample(uSampler, uSampler_sampler, input.vTexCoord) * (material.uTint/255.0),
|
|
2461
|
+
material.isTexture == 1
|
|
2476
2462
|
); // TODO: check isTexture and apply tint
|
|
2477
2463
|
var inputs = Inputs(
|
|
2478
2464
|
normalize(input.vNormal),
|
|
2479
2465
|
input.vTexCoord,
|
|
2480
|
-
|
|
2481
|
-
select(color.rgb,
|
|
2482
|
-
|
|
2483
|
-
|
|
2466
|
+
material.uAmbientColor,
|
|
2467
|
+
select(color.rgb, material.uAmbientMatColor.rgb, material.uHasSetAmbient == 1),
|
|
2468
|
+
material.uSpecularMatColor.rgb,
|
|
2469
|
+
material.uEmissiveMatColor.rgb,
|
|
2484
2470
|
color,
|
|
2485
|
-
|
|
2486
|
-
|
|
2471
|
+
material.uShininess,
|
|
2472
|
+
material.uMetallic
|
|
2487
2473
|
);
|
|
2488
2474
|
inputs = HOOK_getPixelInputs(inputs);
|
|
2489
2475
|
|
|
@@ -2516,9 +2502,8 @@ fn main(input: FragmentInput) -> @location(0) vec4<f32> {
|
|
|
2516
2502
|
`;
|
|
2517
2503
|
|
|
2518
2504
|
const uniforms$2 = `
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
uProjectionMatrix: mat4x4<f32>,
|
|
2505
|
+
// Group 0: Font Properties
|
|
2506
|
+
struct FontUniforms {
|
|
2522
2507
|
uStrokeImageSize: vec2<i32>,
|
|
2523
2508
|
uCellsImageSize: vec2<i32>,
|
|
2524
2509
|
uGridImageSize: vec2<i32>,
|
|
@@ -2527,7 +2512,17 @@ struct Uniforms {
|
|
|
2527
2512
|
uGlyphRect: vec4<f32>,
|
|
2528
2513
|
uGlyphOffset: f32,
|
|
2529
2514
|
uMaterialColor: vec4<f32>,
|
|
2530
|
-
}
|
|
2515
|
+
}
|
|
2516
|
+
|
|
2517
|
+
// Group 1: Model Transform
|
|
2518
|
+
struct ModelUniforms {
|
|
2519
|
+
uModelViewMatrix: mat4x4<f32>,
|
|
2520
|
+
}
|
|
2521
|
+
|
|
2522
|
+
// Group 2: Camera and Projection
|
|
2523
|
+
struct CameraUniforms {
|
|
2524
|
+
uProjectionMatrix: mat4x4<f32>,
|
|
2525
|
+
}
|
|
2531
2526
|
`;
|
|
2532
2527
|
|
|
2533
2528
|
const fontVertexShader = `
|
|
@@ -2542,7 +2537,9 @@ struct VertexOutput {
|
|
|
2542
2537
|
};
|
|
2543
2538
|
|
|
2544
2539
|
${uniforms$2}
|
|
2545
|
-
@group(0) @binding(0) var<uniform>
|
|
2540
|
+
@group(0) @binding(0) var<uniform> font: FontUniforms;
|
|
2541
|
+
@group(1) @binding(0) var<uniform> model: ModelUniforms;
|
|
2542
|
+
@group(2) @binding(0) var<uniform> camera: CameraUniforms;
|
|
2546
2543
|
|
|
2547
2544
|
@vertex
|
|
2548
2545
|
fn main(input: VertexInput) -> VertexOutput {
|
|
@@ -2550,35 +2547,35 @@ fn main(input: VertexInput) -> VertexOutput {
|
|
|
2550
2547
|
var positionVec4 = vec4<f32>(input.aPosition, 1.0);
|
|
2551
2548
|
|
|
2552
2549
|
// scale by the size of the glyph's rectangle
|
|
2553
|
-
positionVec4.x = positionVec4.x * (
|
|
2554
|
-
positionVec4.y = positionVec4.y * (
|
|
2550
|
+
positionVec4.x = positionVec4.x * (font.uGlyphRect.z - font.uGlyphRect.x);
|
|
2551
|
+
positionVec4.y = positionVec4.y * (font.uGlyphRect.w - font.uGlyphRect.y);
|
|
2555
2552
|
|
|
2556
2553
|
// Expand glyph bounding boxes by 1px on each side to give a bit of room
|
|
2557
2554
|
// for antialiasing
|
|
2558
|
-
let newOrigin = (
|
|
2559
|
-
let newDX = (
|
|
2560
|
-
let newDY = (
|
|
2555
|
+
let newOrigin = (model.uModelViewMatrix * vec4<f32>(0.0, 0.0, 0.0, 1.0)).xyz;
|
|
2556
|
+
let newDX = (model.uModelViewMatrix * vec4<f32>(1.0, 0.0, 0.0, 1.0)).xyz;
|
|
2557
|
+
let newDY = (model.uModelViewMatrix * vec4<f32>(0.0, 1.0, 0.0, 1.0)).xyz;
|
|
2561
2558
|
let pixelScale = vec2<f32>(
|
|
2562
2559
|
1.0 / length(newOrigin - newDX),
|
|
2563
2560
|
1.0 / length(newOrigin - newDY)
|
|
2564
2561
|
);
|
|
2565
2562
|
let offset = pixelScale * normalize(input.aTexCoord - vec2<f32>(0.5, 0.5));
|
|
2566
2563
|
let textureOffset = offset * (1.0 / vec2<f32>(
|
|
2567
|
-
|
|
2568
|
-
|
|
2564
|
+
font.uGlyphRect.z - font.uGlyphRect.x,
|
|
2565
|
+
font.uGlyphRect.w - font.uGlyphRect.y
|
|
2569
2566
|
));
|
|
2570
2567
|
|
|
2571
2568
|
// move to the corner of the glyph
|
|
2572
|
-
positionVec4.x = positionVec4.x +
|
|
2573
|
-
positionVec4.y = positionVec4.y +
|
|
2569
|
+
positionVec4.x = positionVec4.x + font.uGlyphRect.x;
|
|
2570
|
+
positionVec4.y = positionVec4.y + font.uGlyphRect.y;
|
|
2574
2571
|
|
|
2575
2572
|
// move to the letter's line offset
|
|
2576
|
-
positionVec4.x = positionVec4.x +
|
|
2573
|
+
positionVec4.x = positionVec4.x + font.uGlyphOffset;
|
|
2577
2574
|
|
|
2578
2575
|
positionVec4.x = positionVec4.x + offset.x;
|
|
2579
2576
|
positionVec4.y = positionVec4.y + offset.y;
|
|
2580
2577
|
|
|
2581
|
-
output.Position =
|
|
2578
|
+
output.Position = camera.uProjectionMatrix * model.uModelViewMatrix * positionVec4;
|
|
2582
2579
|
output.vTexCoord = input.aTexCoord + textureOffset;
|
|
2583
2580
|
|
|
2584
2581
|
return output;
|
|
@@ -2591,18 +2588,19 @@ struct FragmentInput {
|
|
|
2591
2588
|
};
|
|
2592
2589
|
|
|
2593
2590
|
${uniforms$2}
|
|
2594
|
-
@group(0) @binding(0) var<uniform>
|
|
2595
|
-
|
|
2596
|
-
@group(
|
|
2597
|
-
@group(
|
|
2598
|
-
@group(
|
|
2599
|
-
@group(
|
|
2600
|
-
@group(
|
|
2601
|
-
@group(
|
|
2602
|
-
@group(
|
|
2603
|
-
@group(
|
|
2604
|
-
@group(
|
|
2605
|
-
@group(1) @binding(
|
|
2591
|
+
@group(0) @binding(0) var<uniform> font: FontUniforms;
|
|
2592
|
+
@group(0) @binding(1) var uSamplerStrokes: texture_2d<f32>;
|
|
2593
|
+
@group(0) @binding(2) var uSamplerStrokes_sampler: sampler;
|
|
2594
|
+
@group(0) @binding(3) var uSamplerRowStrokes: texture_2d<f32>;
|
|
2595
|
+
@group(0) @binding(4) var uSamplerRowStrokes_sampler: sampler;
|
|
2596
|
+
@group(0) @binding(5) var uSamplerRows: texture_2d<f32>;
|
|
2597
|
+
@group(0) @binding(6) var uSamplerRows_sampler: sampler;
|
|
2598
|
+
@group(0) @binding(7) var uSamplerColStrokes: texture_2d<f32>;
|
|
2599
|
+
@group(0) @binding(8) var uSamplerColStrokes_sampler: sampler;
|
|
2600
|
+
@group(0) @binding(9) var uSamplerCols: texture_2d<f32>;
|
|
2601
|
+
@group(0) @binding(10) var uSamplerCols_sampler: sampler;
|
|
2602
|
+
@group(1) @binding(0) var<uniform> model: ModelUniforms;
|
|
2603
|
+
@group(2) @binding(0) var<uniform> camera: CameraUniforms;
|
|
2606
2604
|
|
|
2607
2605
|
// some helper functions
|
|
2608
2606
|
fn ROUND_f32(v: f32) -> i32 { return i32(floor(v + 0.5)); }
|
|
@@ -2734,14 +2732,14 @@ fn main(input: FragmentInput) -> @location(0) vec4<f32> {
|
|
|
2734
2732
|
let pixelScale = hardness / fwidth(input.vTexCoord);
|
|
2735
2733
|
|
|
2736
2734
|
// which grid cell is this pixel in?
|
|
2737
|
-
let gridCoord = vec2<i32>(floor(input.vTexCoord * vec2<f32>(
|
|
2735
|
+
let gridCoord = vec2<i32>(floor(input.vTexCoord * vec2<f32>(font.uGridSize)));
|
|
2738
2736
|
|
|
2739
2737
|
// intersect curves in this row
|
|
2740
2738
|
{
|
|
2741
2739
|
// the index into the row info bitmap
|
|
2742
|
-
let rowIndex = gridCoord.y +
|
|
2740
|
+
let rowIndex = gridCoord.y + font.uGridOffset.y;
|
|
2743
2741
|
// fetch the info texel
|
|
2744
|
-
let rowInfo = getTexel(uSamplerRows, uSamplerRows_sampler, rowIndex,
|
|
2742
|
+
let rowInfo = getTexel(uSamplerRows, uSamplerRows_sampler, rowIndex, font.uGridImageSize);
|
|
2745
2743
|
// unpack the rowInfo
|
|
2746
2744
|
let rowStrokeIndex = getInt16(rowInfo.xy);
|
|
2747
2745
|
let rowStrokeCount = getInt16(rowInfo.zw);
|
|
@@ -2754,14 +2752,14 @@ fn main(input: FragmentInput) -> @location(0) vec4<f32> {
|
|
|
2754
2752
|
// each stroke is made up of 3 points: the start and control point
|
|
2755
2753
|
// and the start of the next curve.
|
|
2756
2754
|
// fetch the indices of this pair of strokes:
|
|
2757
|
-
let strokeIndices = getTexel(uSamplerRowStrokes, uSamplerRowStrokes_sampler, rowStrokeIndex + iRowStroke,
|
|
2755
|
+
let strokeIndices = getTexel(uSamplerRowStrokes, uSamplerRowStrokes_sampler, rowStrokeIndex + iRowStroke, font.uCellsImageSize);
|
|
2758
2756
|
|
|
2759
2757
|
// unpack the stroke index
|
|
2760
2758
|
let strokePos = getInt16(strokeIndices.xy);
|
|
2761
2759
|
|
|
2762
2760
|
// fetch the two strokes
|
|
2763
|
-
let stroke0 = getTexel(uSamplerStrokes, uSamplerStrokes_sampler, strokePos + 0,
|
|
2764
|
-
let stroke1 = getTexel(uSamplerStrokes, uSamplerStrokes_sampler, strokePos + 1,
|
|
2761
|
+
let stroke0 = getTexel(uSamplerStrokes, uSamplerStrokes_sampler, strokePos + 0, font.uStrokeImageSize);
|
|
2762
|
+
let stroke1 = getTexel(uSamplerStrokes, uSamplerStrokes_sampler, strokePos + 1, font.uStrokeImageSize);
|
|
2765
2763
|
|
|
2766
2764
|
// calculate the coverage
|
|
2767
2765
|
coverageX(stroke0.xy, stroke0.zw, stroke1.xy, input.vTexCoord, pixelScale, &coverage, &weight);
|
|
@@ -2770,8 +2768,8 @@ fn main(input: FragmentInput) -> @location(0) vec4<f32> {
|
|
|
2770
2768
|
|
|
2771
2769
|
// intersect curves in this column
|
|
2772
2770
|
{
|
|
2773
|
-
let colIndex = gridCoord.x +
|
|
2774
|
-
let colInfo = getTexel(uSamplerCols, uSamplerCols_sampler, colIndex,
|
|
2771
|
+
let colIndex = gridCoord.x + font.uGridOffset.x;
|
|
2772
|
+
let colInfo = getTexel(uSamplerCols, uSamplerCols_sampler, colIndex, font.uGridImageSize);
|
|
2775
2773
|
let colStrokeIndex = getInt16(colInfo.xy);
|
|
2776
2774
|
let colStrokeCount = getInt16(colInfo.zw);
|
|
2777
2775
|
|
|
@@ -2780,11 +2778,11 @@ fn main(input: FragmentInput) -> @location(0) vec4<f32> {
|
|
|
2780
2778
|
break;
|
|
2781
2779
|
}
|
|
2782
2780
|
|
|
2783
|
-
let strokeIndices = getTexel(uSamplerColStrokes, uSamplerColStrokes_sampler, colStrokeIndex + iColStroke,
|
|
2781
|
+
let strokeIndices = getTexel(uSamplerColStrokes, uSamplerColStrokes_sampler, colStrokeIndex + iColStroke, font.uCellsImageSize);
|
|
2784
2782
|
|
|
2785
2783
|
let strokePos = getInt16(strokeIndices.xy);
|
|
2786
|
-
let stroke0 = getTexel(uSamplerStrokes, uSamplerStrokes_sampler, strokePos + 0,
|
|
2787
|
-
let stroke1 = getTexel(uSamplerStrokes, uSamplerStrokes_sampler, strokePos + 1,
|
|
2784
|
+
let stroke0 = getTexel(uSamplerStrokes, uSamplerStrokes_sampler, strokePos + 0, font.uStrokeImageSize);
|
|
2785
|
+
let stroke1 = getTexel(uSamplerStrokes, uSamplerStrokes_sampler, strokePos + 1, font.uStrokeImageSize);
|
|
2788
2786
|
coverageY(stroke0.xy, stroke0.zw, stroke1.xy, input.vTexCoord, pixelScale, &coverage, &weight);
|
|
2789
2787
|
}
|
|
2790
2788
|
}
|
|
@@ -2793,7 +2791,7 @@ fn main(input: FragmentInput) -> @location(0) vec4<f32> {
|
|
|
2793
2791
|
let distance = max(weight.x + weight.y, minDistance); // manhattan approx.
|
|
2794
2792
|
let antialias = abs(dot(coverage, weight) / distance);
|
|
2795
2793
|
let cover = min(abs(coverage.x), abs(coverage.y));
|
|
2796
|
-
var outColor = vec4<f32>(
|
|
2794
|
+
var outColor = vec4<f32>(font.uMaterialColor.rgb, 1.0) * font.uMaterialColor.a;
|
|
2797
2795
|
outColor = outColor * saturate_f32(max(antialias, cover));
|
|
2798
2796
|
return outColor;
|
|
2799
2797
|
}
|
|
@@ -3783,7 +3781,10 @@ const wgslBackend = {
|
|
|
3783
3781
|
if (!strandsContext.renderer || !strandsContext.baseShader) return;
|
|
3784
3782
|
|
|
3785
3783
|
// Get the next available binding index from the renderer
|
|
3786
|
-
let bindingIndex = strandsContext.renderer.getNextBindingIndex(
|
|
3784
|
+
let bindingIndex = strandsContext.renderer.getNextBindingIndex({
|
|
3785
|
+
vert: strandsContext.baseShader.vertSrc(),
|
|
3786
|
+
frag: strandsContext.baseShader.fragSrc(),
|
|
3787
|
+
});
|
|
3787
3788
|
|
|
3788
3789
|
for (const {name, typeInfo} of strandsContext.uniforms) {
|
|
3789
3790
|
if (typeInfo.baseType === 'sampler2D') {
|
|
@@ -3952,7 +3953,7 @@ const wgslBackend = {
|
|
|
3952
3953
|
// Check if this is a uniform variable (but not a texture)
|
|
3953
3954
|
const uniform = generationContext.strandsContext?.uniforms?.find(uniform => uniform.name === node.identifier);
|
|
3954
3955
|
if (uniform && uniform.typeInfo.baseType !== 'sampler2D') {
|
|
3955
|
-
return `
|
|
3956
|
+
return `hooks.${node.identifier}`;
|
|
3956
3957
|
}
|
|
3957
3958
|
|
|
3958
3959
|
return node.identifier;
|
|
@@ -4202,16 +4203,27 @@ fn noise(st: vec3<f32>, octaves: i32, ampFalloff: f32) -> f32 {
|
|
|
4202
4203
|
}`;
|
|
4203
4204
|
|
|
4204
4205
|
const filterUniforms = `
|
|
4205
|
-
|
|
4206
|
-
|
|
4207
|
-
uProjectionMatrix: mat4x4<f32>,
|
|
4206
|
+
// Group 0: Filter Properties
|
|
4207
|
+
struct FilterUniforms {
|
|
4208
4208
|
canvasSize: vec2<f32>,
|
|
4209
4209
|
texelSize: vec2<f32>,
|
|
4210
4210
|
}
|
|
4211
4211
|
|
|
4212
|
-
|
|
4212
|
+
// Group 1: Model Transform
|
|
4213
|
+
struct ModelUniforms {
|
|
4214
|
+
uModelViewMatrix: mat4x4<f32>,
|
|
4215
|
+
}
|
|
4216
|
+
|
|
4217
|
+
// Group 2: Camera and Projection
|
|
4218
|
+
struct CameraUniforms {
|
|
4219
|
+
uProjectionMatrix: mat4x4<f32>,
|
|
4220
|
+
}
|
|
4221
|
+
|
|
4222
|
+
@group(0) @binding(0) var<uniform> filterParams: FilterUniforms;
|
|
4213
4223
|
@group(0) @binding(1) var tex0: texture_2d<f32>;
|
|
4214
4224
|
@group(0) @binding(2) var tex0_sampler: sampler;
|
|
4225
|
+
@group(1) @binding(0) var<uniform> model: ModelUniforms;
|
|
4226
|
+
@group(2) @binding(0) var<uniform> camera: CameraUniforms;
|
|
4215
4227
|
`;
|
|
4216
4228
|
|
|
4217
4229
|
const baseFilterVertexShader = filterUniforms + `
|
|
@@ -4236,7 +4248,7 @@ fn main(input: VertexInput) -> VertexOutput {
|
|
|
4236
4248
|
let positionVec4 = vec4<f32>(input.aPosition, 1.0);
|
|
4237
4249
|
|
|
4238
4250
|
// project to 3D space
|
|
4239
|
-
output.position =
|
|
4251
|
+
output.position = camera.uProjectionMatrix * model.uModelViewMatrix * positionVec4;
|
|
4240
4252
|
|
|
4241
4253
|
return output;
|
|
4242
4254
|
}
|
|
@@ -4262,8 +4274,8 @@ fn main(input: FragmentInput) -> FragmentOutput {
|
|
|
4262
4274
|
var output: FragmentOutput;
|
|
4263
4275
|
var inputs: FilterInputs;
|
|
4264
4276
|
inputs.texCoord = input.vTexCoord;
|
|
4265
|
-
inputs.canvasSize =
|
|
4266
|
-
inputs.texelSize =
|
|
4277
|
+
inputs.canvasSize = filterParams.canvasSize;
|
|
4278
|
+
inputs.texelSize = filterParams.texelSize;
|
|
4267
4279
|
|
|
4268
4280
|
var outColor = HOOK_getColor(inputs, tex0, tex0_sampler);
|
|
4269
4281
|
outColor = vec4<f32>(outColor.rgb * outColor.a, outColor.a);
|
|
@@ -4525,10 +4537,27 @@ function rendererWebGPU(p5, fn) {
|
|
|
4525
4537
|
constructor(pInst, w, h, isMainCanvas, elt) {
|
|
4526
4538
|
super(pInst, w, h, isMainCanvas, elt);
|
|
4527
4539
|
|
|
4528
|
-
|
|
4540
|
+
// Used to group draws into one big render pass
|
|
4541
|
+
this.activeRenderPass = null;
|
|
4542
|
+
this.activeRenderPassEncoder = null;
|
|
4543
|
+
this.activeShaderOptions = null;
|
|
4544
|
+
this.activeShader = null;
|
|
4529
4545
|
|
|
4530
4546
|
this.samplers = new Map();
|
|
4531
4547
|
|
|
4548
|
+
// Some uniforms update every frame, like model matrices and sometimes colors.
|
|
4549
|
+
// The fastest way to handle these is to use mapped memory. We'll batch those
|
|
4550
|
+
// into bigger buffers with dynamic offsets, separate from the usual system
|
|
4551
|
+
// where bind groups have their own little buffers that get cached when they
|
|
4552
|
+
// are unchanged
|
|
4553
|
+
this.uniformBufferAlignment = 256;
|
|
4554
|
+
this.activeUniformBuffers = [];
|
|
4555
|
+
this.currentUniformBuffer = undefined;
|
|
4556
|
+
this.uniformBufferPool = [];
|
|
4557
|
+
this.resettingUniformBuffers = [];
|
|
4558
|
+
|
|
4559
|
+
this.dynamicEntryOffsets = new Uint32Array(64);
|
|
4560
|
+
|
|
4532
4561
|
// Cache for current frame's canvas texture view
|
|
4533
4562
|
this.currentCanvasColorTexture = null;
|
|
4534
4563
|
this.currentCanvasColorTextureView = null;
|
|
@@ -4692,6 +4721,68 @@ function rendererWebGPU(p5, fn) {
|
|
|
4692
4721
|
return this.currentCanvasColorTextureView;
|
|
4693
4722
|
}
|
|
4694
4723
|
|
|
4724
|
+
_beginActiveRenderPass() {
|
|
4725
|
+
if (this.activeRenderPass) return;
|
|
4726
|
+
|
|
4727
|
+
// Use framebuffer texture if active, otherwise use canvas texture
|
|
4728
|
+
const activeFramebuffer = this.activeFramebuffer();
|
|
4729
|
+
|
|
4730
|
+
const colorAttachment = {
|
|
4731
|
+
view: activeFramebuffer
|
|
4732
|
+
? (activeFramebuffer.aaColorTexture
|
|
4733
|
+
? activeFramebuffer.aaColorTextureView
|
|
4734
|
+
: activeFramebuffer.colorTextureView)
|
|
4735
|
+
: this._getCanvasColorTextureView(),
|
|
4736
|
+
loadOp: "load",
|
|
4737
|
+
storeOp: "store",
|
|
4738
|
+
// If using multisampled texture, resolve to non-multisampled texture
|
|
4739
|
+
resolveTarget: activeFramebuffer && activeFramebuffer.aaColorTexture
|
|
4740
|
+
? activeFramebuffer.colorTextureView
|
|
4741
|
+
: undefined,
|
|
4742
|
+
};
|
|
4743
|
+
|
|
4744
|
+
// Use framebuffer depth texture if active, otherwise use canvas depth texture
|
|
4745
|
+
const depthTextureView = activeFramebuffer
|
|
4746
|
+
? (activeFramebuffer.aaDepthTexture
|
|
4747
|
+
? activeFramebuffer.aaDepthTextureView
|
|
4748
|
+
: activeFramebuffer.depthTextureView)
|
|
4749
|
+
: this.depthTextureView;
|
|
4750
|
+
const renderPassDescriptor = {
|
|
4751
|
+
colorAttachments: [colorAttachment],
|
|
4752
|
+
depthStencilAttachment: depthTextureView
|
|
4753
|
+
? {
|
|
4754
|
+
view: depthTextureView,
|
|
4755
|
+
depthLoadOp: "load",
|
|
4756
|
+
depthStoreOp: "store",
|
|
4757
|
+
depthClearValue: 1.0,
|
|
4758
|
+
stencilLoadOp: "load",
|
|
4759
|
+
stencilStoreOp: "store",
|
|
4760
|
+
depthReadOnly: false,
|
|
4761
|
+
stencilReadOnly: false,
|
|
4762
|
+
}
|
|
4763
|
+
: undefined,
|
|
4764
|
+
};
|
|
4765
|
+
const commandEncoder = this.device.createCommandEncoder();
|
|
4766
|
+
const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
|
|
4767
|
+
this.activeRenderPassEncoder = commandEncoder;
|
|
4768
|
+
this.activeRenderPass = passEncoder;
|
|
4769
|
+
}
|
|
4770
|
+
|
|
4771
|
+
_finishActiveRenderPass() {
|
|
4772
|
+
if (!this.activeRenderPass) return;
|
|
4773
|
+
|
|
4774
|
+
const commandEncoder = this.activeRenderPassEncoder;
|
|
4775
|
+
const passEncoder = this.activeRenderPass;
|
|
4776
|
+
passEncoder.end();
|
|
4777
|
+
|
|
4778
|
+
// Store the command encoder for later submission
|
|
4779
|
+
this._pendingCommandEncoders.push(commandEncoder.finish());
|
|
4780
|
+
this.activeRenderPassEncoder = null;
|
|
4781
|
+
this.activeRenderPass = null;
|
|
4782
|
+
this.activeShader = null;
|
|
4783
|
+
this.activeShaderOptions = null;
|
|
4784
|
+
}
|
|
4785
|
+
|
|
4695
4786
|
clear(...args) {
|
|
4696
4787
|
const _r = args[0] || 0;
|
|
4697
4788
|
const _g = args[1] || 0;
|
|
@@ -4703,6 +4794,8 @@ function rendererWebGPU(p5, fn) {
|
|
|
4703
4794
|
this._frameState = FRAME_STATE.UNPROMOTED;
|
|
4704
4795
|
}
|
|
4705
4796
|
|
|
4797
|
+
this._finishActiveRenderPass();
|
|
4798
|
+
|
|
4706
4799
|
const commandEncoder = this.device.createCommandEncoder();
|
|
4707
4800
|
|
|
4708
4801
|
// Use framebuffer texture if active, otherwise use canvas texture
|
|
@@ -4757,6 +4850,7 @@ function rendererWebGPU(p5, fn) {
|
|
|
4757
4850
|
* occlude anything subsequently drawn.
|
|
4758
4851
|
*/
|
|
4759
4852
|
clearDepth(depth = 1) {
|
|
4853
|
+
this._finishActiveRenderPass();
|
|
4760
4854
|
const commandEncoder = this.device.createCommandEncoder();
|
|
4761
4855
|
|
|
4762
4856
|
// Use framebuffer texture if active, otherwise use canvas texture
|
|
@@ -4847,7 +4941,6 @@ function rendererWebGPU(p5, fn) {
|
|
|
4847
4941
|
const loc = attr.location;
|
|
4848
4942
|
if (!this.registerEnabled.has(loc)) {
|
|
4849
4943
|
// TODO
|
|
4850
|
-
// this.renderPass.setVertexBuffer(loc, buffer);
|
|
4851
4944
|
this.registerEnabled.add(loc);
|
|
4852
4945
|
}
|
|
4853
4946
|
}
|
|
@@ -4930,6 +5023,14 @@ function rendererWebGPU(p5, fn) {
|
|
|
4930
5023
|
}
|
|
4931
5024
|
}
|
|
4932
5025
|
|
|
5026
|
+
_shaderOptionsDifferent(newOptions) {
|
|
5027
|
+
if (!this.activeShaderOptions) return true;
|
|
5028
|
+
for (const key in this.activeShaderOptions) {
|
|
5029
|
+
if (this.activeShaderOptions[key] !== newOptions[key]) return true;
|
|
5030
|
+
}
|
|
5031
|
+
return false;
|
|
5032
|
+
}
|
|
5033
|
+
|
|
4933
5034
|
_initShader(shader) {
|
|
4934
5035
|
const device = this.device;
|
|
4935
5036
|
|
|
@@ -4984,39 +5085,39 @@ function rendererWebGPU(p5, fn) {
|
|
|
4984
5085
|
}
|
|
4985
5086
|
|
|
4986
5087
|
_finalizeShader(shader) {
|
|
4987
|
-
|
|
4988
|
-
|
|
4989
|
-
|
|
4990
|
-
|
|
4991
|
-
|
|
4992
|
-
shader.
|
|
4993
|
-
|
|
4994
|
-
|
|
4995
|
-
|
|
4996
|
-
|
|
4997
|
-
|
|
4998
|
-
|
|
4999
|
-
|
|
5000
|
-
|
|
5001
|
-
|
|
5002
|
-
|
|
5003
|
-
|
|
5004
|
-
|
|
5005
|
-
|
|
5006
|
-
|
|
5007
|
-
|
|
5008
|
-
|
|
5009
|
-
|
|
5010
|
-
|
|
5011
|
-
|
|
5012
|
-
|
|
5013
|
-
|
|
5014
|
-
|
|
5015
|
-
|
|
5016
|
-
|
|
5017
|
-
|
|
5018
|
-
|
|
5019
|
-
|
|
5088
|
+
// Per-group buffer pools. We will pull from these when we draw multiple
|
|
5089
|
+
// times using the shader in a render pass. These are per group instead of
|
|
5090
|
+
// global so that we can reuse the last used buffer when uniform values
|
|
5091
|
+
// don't change.
|
|
5092
|
+
shader._uniformBufferGroups = [];
|
|
5093
|
+
shader.buffersDirty = new Set();
|
|
5094
|
+
|
|
5095
|
+
for (const group of shader._uniformGroups) {
|
|
5096
|
+
// Calculate the size needed for this group's uniforms
|
|
5097
|
+
const groupUniforms = Object.values(group.uniforms);
|
|
5098
|
+
const rawSize = Math.max(
|
|
5099
|
+
0,
|
|
5100
|
+
...groupUniforms.map(u => u.offsetEnd)
|
|
5101
|
+
);
|
|
5102
|
+
const alignedSize = Math.ceil(rawSize / 16) * 16;
|
|
5103
|
+
|
|
5104
|
+
shader._uniformBufferGroups.push({
|
|
5105
|
+
group: group.group,
|
|
5106
|
+
binding: group.binding,
|
|
5107
|
+
cacheKey: group.group * 1000 + group.binding,
|
|
5108
|
+
varName: group.varName,
|
|
5109
|
+
structType: group.structType,
|
|
5110
|
+
uniforms: groupUniforms,
|
|
5111
|
+
size: alignedSize,
|
|
5112
|
+
|
|
5113
|
+
bufferPool: [],
|
|
5114
|
+
nextBufferPool: [],
|
|
5115
|
+
|
|
5116
|
+
dynamic: groupUniforms.some(u => u.name.startsWith('uModel')),
|
|
5117
|
+
buffersInUse: new Set(),
|
|
5118
|
+
currentBuffer: null, // For caching
|
|
5119
|
+
});
|
|
5120
|
+
}
|
|
5020
5121
|
|
|
5021
5122
|
// Register this shader in our registry for pool cleanup
|
|
5022
5123
|
this._shadersWithPools.push(shader);
|
|
@@ -5024,12 +5125,22 @@ function rendererWebGPU(p5, fn) {
|
|
|
5024
5125
|
const bindGroupLayouts = new Map(); // group index -> bindGroupLayout
|
|
5025
5126
|
const groupEntries = new Map(); // group index -> array of entries
|
|
5026
5127
|
|
|
5027
|
-
//
|
|
5028
|
-
|
|
5029
|
-
|
|
5030
|
-
|
|
5031
|
-
|
|
5032
|
-
|
|
5128
|
+
// Add all uniform group bindings to group 0
|
|
5129
|
+
const structEntries = new Map();
|
|
5130
|
+
for (const bufferGroup of shader._uniformBufferGroups) {
|
|
5131
|
+
const entries = structEntries.get(bufferGroup.group) || [];
|
|
5132
|
+
entries.push({
|
|
5133
|
+
bufferGroup,
|
|
5134
|
+
binding: bufferGroup.binding,
|
|
5135
|
+
visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT,
|
|
5136
|
+
buffer: { type: 'uniform', hasDynamicOffset: bufferGroup.dynamic },
|
|
5137
|
+
});
|
|
5138
|
+
structEntries.set(bufferGroup.group, entries);
|
|
5139
|
+
}
|
|
5140
|
+
for (const [group, entries] of structEntries.entries()) {
|
|
5141
|
+
entries.sort((a, b) => a.binding - b.binding);
|
|
5142
|
+
groupEntries.set(group, entries);
|
|
5143
|
+
}
|
|
5033
5144
|
|
|
5034
5145
|
// Add the variable amount of samplers and texture bindings that can come after
|
|
5035
5146
|
for (const sampler of shader.samplers) {
|
|
@@ -5051,17 +5162,25 @@ function rendererWebGPU(p5, fn) {
|
|
|
5051
5162
|
uniform: sampler,
|
|
5052
5163
|
});
|
|
5053
5164
|
|
|
5165
|
+
entries.sort((a, b) => a.binding - b.binding);
|
|
5054
5166
|
groupEntries.set(group, entries);
|
|
5055
5167
|
}
|
|
5056
5168
|
|
|
5057
5169
|
// Create layouts and bind groups
|
|
5170
|
+
const groupEntriesArr = [];
|
|
5058
5171
|
for (const [group, entries] of groupEntries) {
|
|
5059
5172
|
const layout = this.device.createBindGroupLayout({ entries });
|
|
5060
5173
|
bindGroupLayouts.set(group, layout);
|
|
5174
|
+
groupEntriesArr.push([group, entries]);
|
|
5061
5175
|
}
|
|
5062
5176
|
|
|
5063
|
-
shader._groupEntries =
|
|
5177
|
+
shader._groupEntries = groupEntriesArr;
|
|
5064
5178
|
shader._bindGroupLayouts = [...bindGroupLayouts.values()];
|
|
5179
|
+
// Reuse bind groups if they don't change
|
|
5180
|
+
shader._cachedBindGroup = {};
|
|
5181
|
+
// Remember which dynamic buffer we last used, so that we can
|
|
5182
|
+
// possibly cache bind groups if unchanged
|
|
5183
|
+
shader._lastDynamicBuffer = {};
|
|
5065
5184
|
shader._pipelineLayout = this.device.createPipelineLayout({
|
|
5066
5185
|
bindGroupLayouts: shader._bindGroupLayouts,
|
|
5067
5186
|
});
|
|
@@ -5250,22 +5369,25 @@ function rendererWebGPU(p5, fn) {
|
|
|
5250
5369
|
}
|
|
5251
5370
|
|
|
5252
5371
|
_getVertexBuffers(shader) {
|
|
5253
|
-
|
|
5372
|
+
if (!shader._vertexBuffers) {
|
|
5373
|
+
const buffers = [];
|
|
5254
5374
|
|
|
5255
|
-
|
|
5256
|
-
|
|
5257
|
-
|
|
5375
|
+
for (const attrName in shader.attributes) {
|
|
5376
|
+
const attr = shader.attributes[attrName];
|
|
5377
|
+
if (!attr || attr.location === -1) continue;
|
|
5258
5378
|
|
|
5259
|
-
|
|
5260
|
-
|
|
5261
|
-
|
|
5262
|
-
|
|
5263
|
-
|
|
5379
|
+
// Get the vertex buffer info associated with this attribute
|
|
5380
|
+
const renderBuffer =
|
|
5381
|
+
this.buffers[shader.shaderType].find(buf => buf.attr === attrName) ||
|
|
5382
|
+
this.buffers.user.find(buf => buf.attr === attrName);
|
|
5383
|
+
if (!renderBuffer) continue;
|
|
5264
5384
|
|
|
5265
|
-
|
|
5385
|
+
buffers.push(renderBuffer);
|
|
5386
|
+
}
|
|
5387
|
+
shader._vertexBuffers = buffers;
|
|
5266
5388
|
}
|
|
5267
5389
|
|
|
5268
|
-
return
|
|
5390
|
+
return shader._vertexBuffers;
|
|
5269
5391
|
}
|
|
5270
5392
|
|
|
5271
5393
|
_getFormatFromSize(size) {
|
|
@@ -5307,6 +5429,7 @@ function rendererWebGPU(p5, fn) {
|
|
|
5307
5429
|
}
|
|
5308
5430
|
|
|
5309
5431
|
_resetBuffersBeforeDraw() {
|
|
5432
|
+
this._finishActiveRenderPass();
|
|
5310
5433
|
// Set state to PENDING - we'll decide on first draw
|
|
5311
5434
|
this._frameState = FRAME_STATE.PENDING;
|
|
5312
5435
|
|
|
@@ -5560,50 +5683,106 @@ function rendererWebGPU(p5, fn) {
|
|
|
5560
5683
|
// Uniform buffer pool management
|
|
5561
5684
|
//////////////////////////////////////////////
|
|
5562
5685
|
|
|
5563
|
-
_getUniformBufferFromPool(
|
|
5686
|
+
_getUniformBufferFromPool(bufferGroup) {
|
|
5564
5687
|
// Try to get a buffer from the pool
|
|
5565
|
-
if (
|
|
5566
|
-
const bufferInfo =
|
|
5567
|
-
|
|
5688
|
+
if (bufferGroup.bufferPool.length > 0) {
|
|
5689
|
+
const bufferInfo = bufferGroup.bufferPool.pop();
|
|
5690
|
+
bufferGroup.buffersInUse.add(bufferInfo);
|
|
5568
5691
|
return bufferInfo;
|
|
5569
5692
|
}
|
|
5570
5693
|
|
|
5571
5694
|
// No buffers available, create a new one
|
|
5572
5695
|
const newBuffer = this.device.createBuffer({
|
|
5573
|
-
size:
|
|
5696
|
+
size: bufferGroup.size,
|
|
5574
5697
|
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
|
|
5575
5698
|
});
|
|
5576
|
-
const newData = new Float32Array(
|
|
5699
|
+
const newData = new Float32Array(bufferGroup.size / 4);
|
|
5577
5700
|
const newDataView = new DataView(newData.buffer);
|
|
5578
|
-
|
|
5579
5701
|
const bufferInfo = {
|
|
5580
5702
|
buffer: newBuffer,
|
|
5581
5703
|
data: newData,
|
|
5582
5704
|
dataView: newDataView
|
|
5583
5705
|
};
|
|
5584
5706
|
|
|
5585
|
-
|
|
5707
|
+
bufferGroup.buffersInUse.add(bufferInfo);
|
|
5586
5708
|
return bufferInfo;
|
|
5587
5709
|
}
|
|
5588
5710
|
|
|
5711
|
+
_getDynamicUniformBufferFromPool(bufferGroup) {
|
|
5712
|
+
//
|
|
5713
|
+
let buffer;
|
|
5714
|
+
if (
|
|
5715
|
+
this.currentUniformBuffer &&
|
|
5716
|
+
this.currentUniformBuffer.offset + bufferGroup.size < this.currentUniformBuffer.size
|
|
5717
|
+
) {
|
|
5718
|
+
// We can fit this next block of uniforms into the current active memory chunk
|
|
5719
|
+
buffer = this.currentUniformBuffer;
|
|
5720
|
+
} else if (this.uniformBufferPool.length > 0) {
|
|
5721
|
+
buffer = this.uniformBufferPool.pop();
|
|
5722
|
+
this.activeUniformBuffers.push(buffer);
|
|
5723
|
+
} else {
|
|
5724
|
+
// Kinda arbitrary. Each dynamic offset has to be in groups of 256, but then
|
|
5725
|
+
// we can choose how many things we want to be able to fit into a block.
|
|
5726
|
+
// There's some overhead to each block so if we're drawing a lot of stuff,
|
|
5727
|
+
// bigger is better. But it's also a lot of wasted memory if we AREN'T drawing
|
|
5728
|
+
// a lot of stuff. So.... right now it's 40. Feel free to update this if
|
|
5729
|
+
// a better balance can be achieved.
|
|
5730
|
+
const size = 256 * 40;
|
|
5731
|
+
buffer = {
|
|
5732
|
+
dynamic: true,
|
|
5733
|
+
lastOffset: 0,
|
|
5734
|
+
offset: 0,
|
|
5735
|
+
size,
|
|
5736
|
+
buffer: this.device.createBuffer({
|
|
5737
|
+
size,
|
|
5738
|
+
usage: GPUBufferUsage.MAP_WRITE | GPUBufferUsage.COPY_SRC,
|
|
5739
|
+
mappedAtCreation: true,
|
|
5740
|
+
}),
|
|
5741
|
+
uniformBuffer: this.device.createBuffer({
|
|
5742
|
+
size,
|
|
5743
|
+
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
|
|
5744
|
+
}),
|
|
5745
|
+
};
|
|
5746
|
+
|
|
5747
|
+
buffer.data = new Float32Array(buffer.buffer.getMappedRange());
|
|
5748
|
+
buffer.dataView = new DataView(buffer.data.buffer);
|
|
5749
|
+
|
|
5750
|
+
this.activeUniformBuffers.push(buffer);
|
|
5751
|
+
}
|
|
5752
|
+
|
|
5753
|
+
this.currentUniformBuffer = buffer;
|
|
5754
|
+
|
|
5755
|
+
return buffer;
|
|
5756
|
+
}
|
|
5757
|
+
|
|
5589
5758
|
_returnUniformBuffersToPool() {
|
|
5590
5759
|
// Return all used buffers back to their pools for all registered shaders
|
|
5591
5760
|
for (const shader of this._shadersWithPools) {
|
|
5592
|
-
|
|
5593
|
-
this._returnShaderBuffersToPool(shader);
|
|
5594
|
-
}
|
|
5761
|
+
this._returnShaderBuffersToPool(shader);
|
|
5595
5762
|
}
|
|
5596
5763
|
}
|
|
5597
5764
|
|
|
5598
5765
|
_returnShaderBuffersToPool(shader) {
|
|
5599
|
-
|
|
5600
|
-
|
|
5601
|
-
|
|
5602
|
-
|
|
5766
|
+
if (shader._uniformBufferGroups) {
|
|
5767
|
+
for (const bufferGroup of shader._uniformBufferGroups) {
|
|
5768
|
+
while (bufferGroup.nextBufferPool.length > 0) {
|
|
5769
|
+
bufferGroup.bufferPool.push(bufferGroup.nextBufferPool.pop());
|
|
5770
|
+
}
|
|
5771
|
+
for (const bufferInfo of bufferGroup.buffersInUse.keys()) {
|
|
5772
|
+
if (bufferInfo !== bufferGroup.currentBuffer) {
|
|
5773
|
+
bufferGroup.nextBufferPool.push(bufferInfo);
|
|
5774
|
+
}
|
|
5775
|
+
}
|
|
5776
|
+
bufferGroup.buffersInUse.clear();
|
|
5777
|
+
if (bufferGroup.currentBuffer) {
|
|
5778
|
+
bufferGroup.buffersInUse.add(bufferGroup.currentBuffer);
|
|
5779
|
+
}
|
|
5780
|
+
}
|
|
5603
5781
|
}
|
|
5604
5782
|
}
|
|
5605
5783
|
|
|
5606
5784
|
flushDraw() {
|
|
5785
|
+
this._finishActiveRenderPass();
|
|
5607
5786
|
// Only submit if we actually had any draws
|
|
5608
5787
|
if (this._hasPendingDraws) {
|
|
5609
5788
|
// Create a copy of pending command encoders
|
|
@@ -5611,9 +5790,41 @@ function rendererWebGPU(p5, fn) {
|
|
|
5611
5790
|
this._pendingCommandEncoders = [];
|
|
5612
5791
|
this._hasPendingDraws = false;
|
|
5613
5792
|
|
|
5793
|
+
if (this.activeUniformBuffers.length > 0) {
|
|
5794
|
+
const encoder = this.device.createCommandEncoder();
|
|
5795
|
+
for (const bufferInfo of this.activeUniformBuffers) {
|
|
5796
|
+
bufferInfo.buffer.unmap();
|
|
5797
|
+
encoder.copyBufferToBuffer(
|
|
5798
|
+
bufferInfo.buffer,
|
|
5799
|
+
bufferInfo.uniformBuffer,
|
|
5800
|
+
);
|
|
5801
|
+
}
|
|
5802
|
+
commandsToSubmit.unshift(encoder.finish());
|
|
5803
|
+
}
|
|
5804
|
+
|
|
5614
5805
|
// Submit the commands
|
|
5615
5806
|
this.queue.submit(commandsToSubmit);
|
|
5616
5807
|
|
|
5808
|
+
for (const buf of this.activeUniformBuffers) {
|
|
5809
|
+
// buf.buffer = this.device.createBuffer({
|
|
5810
|
+
// size: buf.size,
|
|
5811
|
+
// usage: GPUBufferUsage.MAP_WRITE | GPUBufferUsage.COPY_SRC,
|
|
5812
|
+
// mappedAtCreation: true,
|
|
5813
|
+
// });
|
|
5814
|
+
buf.offset = 0;
|
|
5815
|
+
buf.lastOffset = 0;
|
|
5816
|
+
// this.resettingUniformBuffers.push(
|
|
5817
|
+
buf.buffer.mapAsync(GPUMapMode.WRITE).then(() => {
|
|
5818
|
+
buf.data = new Float32Array(buf.buffer.getMappedRange());
|
|
5819
|
+
buf.dataView = new DataView(buf.data.buffer);
|
|
5820
|
+
this.uniformBufferPool.push(buf);
|
|
5821
|
+
return buf;
|
|
5822
|
+
});
|
|
5823
|
+
// )
|
|
5824
|
+
}
|
|
5825
|
+
this.activeUniformBuffers = [];
|
|
5826
|
+
this.currentUniformBuffer = undefined;
|
|
5827
|
+
|
|
5617
5828
|
// Execute post-submit callbacks after GPU work completes
|
|
5618
5829
|
if (this._postSubmitCallbacks.length > 0) {
|
|
5619
5830
|
const callbacks = this._postSubmitCallbacks;
|
|
@@ -5687,15 +5898,21 @@ function rendererWebGPU(p5, fn) {
|
|
|
5687
5898
|
this._markGeometryBuffersForReturn(geometry);
|
|
5688
5899
|
}
|
|
5689
5900
|
|
|
5901
|
+
// this.uniformBufferPool.push(...(await Promise.all(this.resettingUniformBuffers)));
|
|
5902
|
+
this.resettingUniformBuffers = [];
|
|
5903
|
+
|
|
5690
5904
|
// Return all vertex buffers to their pools
|
|
5691
5905
|
this._returnVertexBuffersToPool();
|
|
5692
5906
|
|
|
5693
5907
|
// Destroy all retired buffers
|
|
5694
|
-
|
|
5695
|
-
|
|
5696
|
-
|
|
5908
|
+
const retired = this._retiredBuffers;
|
|
5909
|
+
this._postSubmitCallbacks.push(() => {
|
|
5910
|
+
for (const buffer of retired) {
|
|
5911
|
+
if (buffer && buffer.destroy) {
|
|
5912
|
+
buffer.destroy();
|
|
5913
|
+
}
|
|
5697
5914
|
}
|
|
5698
|
-
}
|
|
5915
|
+
});
|
|
5699
5916
|
this._retiredBuffers = [];
|
|
5700
5917
|
|
|
5701
5918
|
if (this._frameState === FRAME_STATE.PROMOTED) {
|
|
@@ -5723,50 +5940,15 @@ function rendererWebGPU(p5, fn) {
|
|
|
5723
5940
|
this._promoteToFramebufferWithoutCopy();
|
|
5724
5941
|
}
|
|
5725
5942
|
|
|
5726
|
-
|
|
5727
|
-
|
|
5728
|
-
// Use framebuffer texture if active, otherwise use canvas texture
|
|
5729
|
-
const activeFramebuffer = this.activeFramebuffer();
|
|
5730
|
-
|
|
5731
|
-
const colorAttachment = {
|
|
5732
|
-
view: activeFramebuffer
|
|
5733
|
-
? (activeFramebuffer.aaColorTexture
|
|
5734
|
-
? activeFramebuffer.aaColorTextureView
|
|
5735
|
-
: activeFramebuffer.colorTextureView)
|
|
5736
|
-
: this._getCanvasColorTextureView(),
|
|
5737
|
-
loadOp: "load",
|
|
5738
|
-
storeOp: "store",
|
|
5739
|
-
// If using multisampled texture, resolve to non-multisampled texture
|
|
5740
|
-
resolveTarget: activeFramebuffer && activeFramebuffer.aaColorTexture
|
|
5741
|
-
? activeFramebuffer.colorTextureView
|
|
5742
|
-
: undefined,
|
|
5743
|
-
};
|
|
5744
|
-
|
|
5745
|
-
// Use framebuffer depth texture if active, otherwise use canvas depth texture
|
|
5746
|
-
const depthTextureView = activeFramebuffer
|
|
5747
|
-
? (activeFramebuffer.aaDepthTexture
|
|
5748
|
-
? activeFramebuffer.aaDepthTextureView
|
|
5749
|
-
: activeFramebuffer.depthTextureView)
|
|
5750
|
-
: this.depthTextureView;
|
|
5751
|
-
const renderPassDescriptor = {
|
|
5752
|
-
colorAttachments: [colorAttachment],
|
|
5753
|
-
depthStencilAttachment: depthTextureView
|
|
5754
|
-
? {
|
|
5755
|
-
view: depthTextureView,
|
|
5756
|
-
depthLoadOp: "load",
|
|
5757
|
-
depthStoreOp: "store",
|
|
5758
|
-
depthClearValue: 1.0,
|
|
5759
|
-
stencilLoadOp: "load",
|
|
5760
|
-
stencilStoreOp: "store",
|
|
5761
|
-
depthReadOnly: false,
|
|
5762
|
-
stencilReadOnly: false,
|
|
5763
|
-
}
|
|
5764
|
-
: undefined,
|
|
5765
|
-
};
|
|
5766
|
-
|
|
5767
|
-
const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
|
|
5943
|
+
this._beginActiveRenderPass();
|
|
5944
|
+
const passEncoder = this.activeRenderPass;
|
|
5768
5945
|
const currentShader = this._curShader;
|
|
5769
|
-
|
|
5946
|
+
const shaderOptions = this._shaderOptions({ mode });
|
|
5947
|
+
if (this.activeShader !== currentShader || this._shaderOptionsDifferent(shaderOptions)) {
|
|
5948
|
+
passEncoder.setPipeline(currentShader.getPipeline(shaderOptions));
|
|
5949
|
+
}
|
|
5950
|
+
this.activeShader = currentShader;
|
|
5951
|
+
this.activeShaderOptions = shaderOptions;
|
|
5770
5952
|
|
|
5771
5953
|
// Set stencil reference value for clipping
|
|
5772
5954
|
const drawTarget = this.drawTarget();
|
|
@@ -5780,52 +5962,125 @@ function rendererWebGPU(p5, fn) {
|
|
|
5780
5962
|
passEncoder.setStencilReference(1);
|
|
5781
5963
|
}
|
|
5782
5964
|
// Bind vertex buffers
|
|
5783
|
-
for (const buffer of this._getVertexBuffers(currentShader)) {
|
|
5965
|
+
for (const buffer of currentShader._vertexBuffers || this._getVertexBuffers(currentShader)) {
|
|
5784
5966
|
const location = currentShader.attributes[buffer.attr].location;
|
|
5785
5967
|
const gpuBuffer = buffers[buffer.dst];
|
|
5786
5968
|
passEncoder.setVertexBuffer(location, gpuBuffer, 0);
|
|
5787
5969
|
}
|
|
5788
|
-
// Bind uniforms - get a buffer from the pool
|
|
5789
|
-
const uniformBufferInfo = this._getUniformBufferFromPool(currentShader);
|
|
5790
|
-
this._packUniforms(currentShader, uniformBufferInfo);
|
|
5791
|
-
this.device.queue.writeBuffer(
|
|
5792
|
-
uniformBufferInfo.buffer,
|
|
5793
|
-
0,
|
|
5794
|
-
uniformBufferInfo.data.buffer,
|
|
5795
|
-
uniformBufferInfo.data.byteOffset,
|
|
5796
|
-
uniformBufferInfo.data.byteLength
|
|
5797
|
-
);
|
|
5798
5970
|
|
|
5799
|
-
|
|
5800
|
-
|
|
5801
|
-
|
|
5802
|
-
|
|
5803
|
-
|
|
5804
|
-
|
|
5805
|
-
|
|
5806
|
-
|
|
5971
|
+
for (const bufferGroup of currentShader._uniformBufferGroups) {
|
|
5972
|
+
if (bufferGroup.dynamic) {
|
|
5973
|
+
// Bind uniforms into a part of a big dynamic memory block because
|
|
5974
|
+
// the group changes often
|
|
5975
|
+
const uniformBufferInfo = this._getDynamicUniformBufferFromPool(bufferGroup);
|
|
5976
|
+
if (currentShader._lastDynamicBuffer[bufferGroup.cacheKey] !== uniformBufferInfo) {
|
|
5977
|
+
currentShader._cachedBindGroup[bufferGroup.group] = undefined;
|
|
5978
|
+
currentShader._lastDynamicBuffer[bufferGroup.cacheKey] = uniformBufferInfo;
|
|
5807
5979
|
}
|
|
5980
|
+
this._packUniformGroup(currentShader, bufferGroup.uniforms, uniformBufferInfo);
|
|
5981
|
+
uniformBufferInfo.lastOffset = uniformBufferInfo.offset;
|
|
5982
|
+
uniformBufferInfo.offset += Math.ceil(bufferGroup.size / this.uniformBufferAlignment) * this.uniformBufferAlignment;
|
|
5808
5983
|
|
|
5809
|
-
|
|
5810
|
-
|
|
5811
|
-
|
|
5984
|
+
// Make a shallow copy so that we keep track of the last offset for this uniform
|
|
5985
|
+
bufferGroup.currentDynamicBuffer = uniformBufferInfo;
|
|
5986
|
+
bufferGroup.lastOffset = uniformBufferInfo.lastOffset;
|
|
5987
|
+
} else {
|
|
5988
|
+
// Bind uniforms to a binding-specific buffer, which may be cached for performance
|
|
5989
|
+
let bufferInfo;
|
|
5990
|
+
const dataChanged = this._hasGroupDataChanged(currentShader, bufferGroup);
|
|
5991
|
+
|
|
5992
|
+
if (!dataChanged && bufferGroup.currentBuffer) {
|
|
5993
|
+
// Reuse the cached buffer - no need to pack or write
|
|
5994
|
+
bufferInfo = bufferGroup.currentBuffer;
|
|
5995
|
+
bufferGroup.buffersInUse.add(bufferInfo);
|
|
5996
|
+
} else {
|
|
5997
|
+
// Data changed - get a new buffer and write to it
|
|
5998
|
+
bufferInfo = this._getUniformBufferFromPool(bufferGroup);
|
|
5999
|
+
this._packUniformGroup(currentShader, bufferGroup.uniforms, bufferInfo);
|
|
6000
|
+
this.device.queue.writeBuffer(
|
|
6001
|
+
bufferInfo.buffer,
|
|
6002
|
+
0,
|
|
6003
|
+
bufferInfo.data.buffer,
|
|
6004
|
+
bufferInfo.data.byteOffset,
|
|
6005
|
+
bufferInfo.data.byteLength
|
|
5812
6006
|
);
|
|
6007
|
+
|
|
6008
|
+
currentShader.buffersDirty.delete(bufferGroup.group * 1000 + bufferGroup.binding);
|
|
6009
|
+
currentShader._cachedBindGroup[bufferGroup.group] = undefined;
|
|
6010
|
+
|
|
6011
|
+
// Cache this buffer and data for next frame
|
|
6012
|
+
bufferGroup.currentBuffer = bufferInfo;
|
|
5813
6013
|
}
|
|
6014
|
+
}
|
|
6015
|
+
}
|
|
6016
|
+
for (const sampler of currentShader.samplers) {
|
|
6017
|
+
const key = sampler.group * 1000 + sampler.binding;
|
|
6018
|
+
if (currentShader.buffersDirty.has(key)) {
|
|
6019
|
+
currentShader._cachedBindGroup[sampler.group] = undefined;
|
|
6020
|
+
currentShader.buffersDirty.delete(key);
|
|
6021
|
+
}
|
|
6022
|
+
}
|
|
5814
6023
|
|
|
5815
|
-
|
|
5816
|
-
|
|
5817
|
-
|
|
5818
|
-
|
|
5819
|
-
|
|
5820
|
-
|
|
5821
|
-
|
|
6024
|
+
// Bind sampler/texture uniforms and uniform buffers
|
|
6025
|
+
for (const iter of currentShader._groupEntries) {
|
|
6026
|
+
const group = iter[0];
|
|
6027
|
+
const entries = iter[1];
|
|
6028
|
+
let dynamicOffsetIdx = 0;
|
|
6029
|
+
const bgEntries = [];
|
|
6030
|
+
let bindGroup = currentShader._cachedBindGroup[group];
|
|
6031
|
+
for (const entry of entries) {
|
|
6032
|
+
const bufferGroup = entry.bufferGroup;
|
|
6033
|
+
// Check if this is a uniform buffer binding
|
|
6034
|
+
const uniformBufferInfo =
|
|
6035
|
+
bufferGroup?.currentBuffer || bufferGroup?.currentDynamicBuffer;
|
|
6036
|
+
if (uniformBufferInfo) {
|
|
6037
|
+
if (bufferGroup.dynamic) {
|
|
6038
|
+
this.dynamicEntryOffsets[dynamicOffsetIdx++] = bufferGroup.lastOffset;
|
|
6039
|
+
}
|
|
6040
|
+
if (!bindGroup) {
|
|
6041
|
+
bgEntries.push({
|
|
6042
|
+
binding: entry.binding,
|
|
6043
|
+
resource: bufferGroup.dynamic
|
|
6044
|
+
? {
|
|
6045
|
+
buffer: uniformBufferInfo.uniformBuffer,
|
|
6046
|
+
offset: 0,
|
|
6047
|
+
size: Math.ceil(bufferGroup.size / this.uniformBufferAlignment) * this.uniformBufferAlignment,
|
|
6048
|
+
}
|
|
6049
|
+
: { buffer: uniformBufferInfo.buffer },
|
|
6050
|
+
});
|
|
6051
|
+
}
|
|
6052
|
+
} else if (!bindGroup) {
|
|
6053
|
+
bgEntries.push({
|
|
6054
|
+
binding: entry.binding,
|
|
6055
|
+
resource: entry.uniform.type === 'sampler'
|
|
6056
|
+
? (entry.uniform.textureSource.texture || this._getEmptyTexture()).getSampler()
|
|
6057
|
+
: (entry.uniform.texture || this._getEmptyTexture()).textureHandle.view,
|
|
6058
|
+
});
|
|
6059
|
+
}
|
|
6060
|
+
}
|
|
5822
6061
|
|
|
5823
6062
|
const layout = currentShader._bindGroupLayouts[group];
|
|
5824
|
-
|
|
5825
|
-
|
|
5826
|
-
|
|
5827
|
-
|
|
5828
|
-
|
|
6063
|
+
if (!bindGroup) {
|
|
6064
|
+
bindGroup = this.device.createBindGroup({
|
|
6065
|
+
layout,
|
|
6066
|
+
entries: bgEntries,
|
|
6067
|
+
});
|
|
6068
|
+
}
|
|
6069
|
+
currentShader._cachedBindGroup[group] = bindGroup;
|
|
6070
|
+
if (dynamicOffsetIdx === 0) {
|
|
6071
|
+
passEncoder.setBindGroup(
|
|
6072
|
+
group,
|
|
6073
|
+
bindGroup,
|
|
6074
|
+
);
|
|
6075
|
+
} else {
|
|
6076
|
+
passEncoder.setBindGroup(
|
|
6077
|
+
group,
|
|
6078
|
+
bindGroup,
|
|
6079
|
+
this.dynamicEntryOffsets,
|
|
6080
|
+
0,
|
|
6081
|
+
dynamicOffsetIdx
|
|
6082
|
+
);
|
|
6083
|
+
}
|
|
5829
6084
|
}
|
|
5830
6085
|
|
|
5831
6086
|
if (currentShader.shaderType === "fill") {
|
|
@@ -5850,11 +6105,6 @@ function rendererWebGPU(p5, fn) {
|
|
|
5850
6105
|
passEncoder.draw(geometry.lineVertices.length / 3, count, 0, 0);
|
|
5851
6106
|
}
|
|
5852
6107
|
|
|
5853
|
-
passEncoder.end();
|
|
5854
|
-
|
|
5855
|
-
// Store the command encoder for later submission
|
|
5856
|
-
this._pendingCommandEncoders.push(commandEncoder.finish());
|
|
5857
|
-
|
|
5858
6108
|
// Mark that we have pending draws that need submission
|
|
5859
6109
|
this._hasPendingDraws = true;
|
|
5860
6110
|
}
|
|
@@ -5863,46 +6113,62 @@ function rendererWebGPU(p5, fn) {
|
|
|
5863
6113
|
// SHADER
|
|
5864
6114
|
//////////////////////////////////////////////
|
|
5865
6115
|
|
|
5866
|
-
|
|
6116
|
+
_packUniformGroup(shader, groupUniforms, bufferInfo) {
|
|
6117
|
+
// Pack a single group's uniforms into a buffer
|
|
5867
6118
|
const data = bufferInfo.data;
|
|
5868
6119
|
const dataView = bufferInfo.dataView;
|
|
5869
6120
|
|
|
5870
|
-
|
|
5871
|
-
|
|
5872
|
-
|
|
6121
|
+
const offset = bufferInfo.offset || 0;
|
|
6122
|
+
for (const uniform of groupUniforms) {
|
|
6123
|
+
const fullUniform = shader.uniforms[uniform.name];
|
|
6124
|
+
if (!fullUniform || fullUniform.isSampler) continue;
|
|
6125
|
+
const uniformData = fullUniform._mappedData;
|
|
5873
6126
|
|
|
5874
|
-
if (
|
|
5875
|
-
if (
|
|
5876
|
-
|
|
5877
|
-
dataView.setUint32(uniform.offset, uniform._cachedData, true);
|
|
6127
|
+
if (fullUniform.baseType === 'u32') {
|
|
6128
|
+
if (fullUniform.size === 4) {
|
|
6129
|
+
dataView.setUint32(offset + fullUniform.offset, uniformData, true);
|
|
5878
6130
|
} else {
|
|
5879
|
-
// Vector of u32s
|
|
5880
|
-
const uniformData = uniform._cachedData;
|
|
5881
6131
|
for (let i = 0; i < uniformData.length; i++) {
|
|
5882
|
-
dataView.setUint32(
|
|
6132
|
+
dataView.setUint32(offset + fullUniform.offset + i * 4, uniformData[i], true);
|
|
5883
6133
|
}
|
|
5884
6134
|
}
|
|
5885
|
-
} else if (
|
|
5886
|
-
if (
|
|
5887
|
-
|
|
5888
|
-
dataView.setInt32(uniform.offset, uniform._cachedData, true);
|
|
6135
|
+
} else if (fullUniform.baseType === 'i32') {
|
|
6136
|
+
if (fullUniform.size === 4) {
|
|
6137
|
+
dataView.setInt32(offset + fullUniform.offset, uniformData, true);
|
|
5889
6138
|
} else {
|
|
5890
|
-
// Vector of i32s
|
|
5891
|
-
const uniformData = uniform._cachedData;
|
|
5892
6139
|
for (let i = 0; i < uniformData.length; i++) {
|
|
5893
|
-
dataView.setInt32(
|
|
6140
|
+
dataView.setInt32(offset + fullUniform.offset + i * 4, uniformData[i], true);
|
|
5894
6141
|
}
|
|
5895
6142
|
}
|
|
5896
|
-
} else if (
|
|
5897
|
-
//
|
|
5898
|
-
|
|
5899
|
-
|
|
5900
|
-
|
|
5901
|
-
data
|
|
6143
|
+
} else if (fullUniform.packInPlace) {
|
|
6144
|
+
// In-place packing for mat3: write directly to buffer with padding
|
|
6145
|
+
const baseOffset = (offset + fullUniform.offset) / 4;
|
|
6146
|
+
// Column 0
|
|
6147
|
+
data[baseOffset + 0] = uniformData[0];
|
|
6148
|
+
data[baseOffset + 1] = uniformData[1];
|
|
6149
|
+
data[baseOffset + 2] = uniformData[2];
|
|
6150
|
+
// Column 1
|
|
6151
|
+
data[baseOffset + 4] = uniformData[3];
|
|
6152
|
+
data[baseOffset + 5] = uniformData[4];
|
|
6153
|
+
data[baseOffset + 6] = uniformData[5];
|
|
6154
|
+
// Column 2
|
|
6155
|
+
data[baseOffset + 8] = uniformData[6];
|
|
6156
|
+
data[baseOffset + 9] = uniformData[7];
|
|
6157
|
+
data[baseOffset + 10] = uniformData[8];
|
|
6158
|
+
} else if (fullUniform.size === 4) {
|
|
6159
|
+
data.set([uniformData], (offset + fullUniform.offset) / 4);
|
|
6160
|
+
} else if (uniformData !== undefined) {
|
|
6161
|
+
data.set(uniformData, (offset + fullUniform.offset) / 4);
|
|
5902
6162
|
}
|
|
5903
6163
|
}
|
|
5904
6164
|
}
|
|
5905
6165
|
|
|
6166
|
+
_hasGroupDataChanged(shader, bufferGroup) {
|
|
6167
|
+
// First time
|
|
6168
|
+
if (!bufferGroup.currentBuffer) return true;
|
|
6169
|
+
return shader.buffersDirty.has(bufferGroup.group * 1000 + bufferGroup.binding);
|
|
6170
|
+
}
|
|
6171
|
+
|
|
5906
6172
|
_parseStruct(shaderSource, structName) {
|
|
5907
6173
|
const structMatch = shaderSource.match(
|
|
5908
6174
|
new RegExp(`struct\\s+${structName}\\s*\\{([^\\}]+)\\}`)
|
|
@@ -5946,17 +6212,16 @@ function rendererWebGPU(p5, fn) {
|
|
|
5946
6212
|
const align = dim === 2 ? 8 : 16;
|
|
5947
6213
|
// Each column must be aligned
|
|
5948
6214
|
const size = Math.ceil(dim * 4 / align) * align * dim;
|
|
6215
|
+
// For mat3, use in-place packing to avoid array allocation
|
|
5949
6216
|
const pack = dim === 3
|
|
5950
6217
|
? (data) => [
|
|
5951
6218
|
...data.slice(0, 3),
|
|
5952
|
-
0,
|
|
5953
6219
|
...data.slice(3, 6),
|
|
5954
|
-
0,
|
|
5955
6220
|
...data.slice(6, 9),
|
|
5956
|
-
0
|
|
5957
6221
|
]
|
|
5958
6222
|
: undefined;
|
|
5959
|
-
|
|
6223
|
+
const packInPlace = dim === 3;
|
|
6224
|
+
return { align, size, pack, packInPlace, items: dim * dim, baseType: 'f32' };
|
|
5960
6225
|
}
|
|
5961
6226
|
if (/^array<.+>$/.test(type)) {
|
|
5962
6227
|
const [, subtype, rawLength] = type.match(/^array<(.+),\s*(\d+)>/);
|
|
@@ -5993,7 +6258,7 @@ function rendererWebGPU(p5, fn) {
|
|
|
5993
6258
|
|
|
5994
6259
|
while ((match = elementRegex.exec(structBody)) !== null) {
|
|
5995
6260
|
const [_, location, name, type] = match;
|
|
5996
|
-
const { size, align, pack, baseType } = baseAlignAndSize(type);
|
|
6261
|
+
const { size, align, pack, packInPlace, baseType } = baseAlignAndSize(type);
|
|
5997
6262
|
offset = Math.ceil(offset / align) * align;
|
|
5998
6263
|
const offsetEnd = offset + size;
|
|
5999
6264
|
elements[name] = {
|
|
@@ -6005,6 +6270,7 @@ function rendererWebGPU(p5, fn) {
|
|
|
6005
6270
|
offset,
|
|
6006
6271
|
offsetEnd,
|
|
6007
6272
|
pack,
|
|
6273
|
+
packInPlace,
|
|
6008
6274
|
baseType
|
|
6009
6275
|
};
|
|
6010
6276
|
index++;
|
|
@@ -6030,22 +6296,64 @@ function rendererWebGPU(p5, fn) {
|
|
|
6030
6296
|
}
|
|
6031
6297
|
|
|
6032
6298
|
getUniformMetadata(shader) {
|
|
6033
|
-
//
|
|
6034
|
-
//
|
|
6035
|
-
|
|
6036
|
-
|
|
6037
|
-
//
|
|
6038
|
-
|
|
6039
|
-
const
|
|
6040
|
-
|
|
6041
|
-
|
|
6042
|
-
|
|
6043
|
-
|
|
6044
|
-
|
|
6299
|
+
// Parse all uniform struct bindings in group 0.
|
|
6300
|
+
// TODO: support non-sampler uniforms being in other groups
|
|
6301
|
+
|
|
6302
|
+
// Each binding represents a logical group of uniforms, since they get
|
|
6303
|
+
// updated or cached all at once.
|
|
6304
|
+
|
|
6305
|
+
const uniformGroups = [];
|
|
6306
|
+
const uniformVarRegex = /@group\((\d+)\)\s+@binding\((\d+)\)\s+var<uniform>\s+(\w+)\s*:\s*(\w+);/g;
|
|
6307
|
+
|
|
6308
|
+
let match;
|
|
6309
|
+
while ((match = uniformVarRegex.exec(shader.vertSrc())) !== null) {
|
|
6310
|
+
const [_, groupNum, binding, varName, structType] = match;
|
|
6311
|
+
const bindingIndex = parseInt(binding);
|
|
6312
|
+
const uniforms = this._parseStruct(shader.vertSrc(), structType);
|
|
6313
|
+
|
|
6314
|
+
uniformGroups.push({
|
|
6315
|
+
group: parseInt(groupNum),
|
|
6316
|
+
binding: bindingIndex,
|
|
6317
|
+
varName,
|
|
6318
|
+
structType,
|
|
6319
|
+
uniforms
|
|
6320
|
+
});
|
|
6321
|
+
}
|
|
6322
|
+
|
|
6323
|
+
if (uniformGroups.length === 0) {
|
|
6324
|
+
throw new Error('Expected at least one uniform struct bound to @group(0)');
|
|
6325
|
+
}
|
|
6326
|
+
|
|
6327
|
+
// While we're also keeping track of the groups, the API we expose
|
|
6328
|
+
// to users of p5 is just a flat list of uniforms (which can be the
|
|
6329
|
+
// individual struct items in the group.)
|
|
6330
|
+
const allUniforms = {};
|
|
6331
|
+
for (const group of uniformGroups) {
|
|
6332
|
+
for (const [uniformName, uniformData] of Object.entries(group.uniforms)) {
|
|
6333
|
+
allUniforms[uniformName] = {
|
|
6334
|
+
...uniformData,
|
|
6335
|
+
group: group.group,
|
|
6336
|
+
binding: group.binding,
|
|
6337
|
+
varName: group.varName
|
|
6338
|
+
};
|
|
6339
|
+
}
|
|
6340
|
+
}
|
|
6341
|
+
|
|
6342
|
+
// Store uniform groups for buffer pooling
|
|
6343
|
+
shader._uniformGroups = uniformGroups;
|
|
6344
|
+
|
|
6045
6345
|
// Extract samplers from group bindings
|
|
6046
6346
|
const samplers = {};
|
|
6047
6347
|
// TODO: support other texture types
|
|
6048
6348
|
const samplerRegex = /@group\((\d+)\)\s*@binding\((\d+)\)\s*var\s+(\w+)\s*:\s*(texture_2d<f32>|sampler);/g;
|
|
6349
|
+
|
|
6350
|
+
// Track which bindings are taken by the struct properties we've parsed
|
|
6351
|
+
// (the rest should be textures/samplers)
|
|
6352
|
+
const structUniformBindings = {};
|
|
6353
|
+
for (const g of uniformGroups) {
|
|
6354
|
+
structUniformBindings[g.group + ',' + g.binding] = true;
|
|
6355
|
+
}
|
|
6356
|
+
|
|
6049
6357
|
for (const [src, visibility] of [
|
|
6050
6358
|
[shader.vertSrc(), GPUShaderStage.VERTEX],
|
|
6051
6359
|
[shader.fragSrc(), GPUShaderStage.FRAGMENT]
|
|
@@ -6055,10 +6363,8 @@ function rendererWebGPU(p5, fn) {
|
|
|
6055
6363
|
const [_, group, binding, name, type] = match;
|
|
6056
6364
|
const groupIndex = parseInt(group);
|
|
6057
6365
|
const bindingIndex = parseInt(binding);
|
|
6058
|
-
//
|
|
6059
|
-
|
|
6060
|
-
// uniforms
|
|
6061
|
-
if (groupIndex === 0 && bindingIndex === 0) continue;
|
|
6366
|
+
// Skip struct uniform bindings which we've already parsed
|
|
6367
|
+
if (structUniformBindings[groupIndex + ',' + bindingIndex]) continue;
|
|
6062
6368
|
|
|
6063
6369
|
const key = `${groupIndex},${bindingIndex}`;
|
|
6064
6370
|
samplers[key] = {
|
|
@@ -6087,17 +6393,17 @@ function rendererWebGPU(p5, fn) {
|
|
|
6087
6393
|
}
|
|
6088
6394
|
}
|
|
6089
6395
|
}
|
|
6090
|
-
return [...Object.values(
|
|
6396
|
+
return [...Object.values(allUniforms).sort((a, b) => a.index - b.index), ...Object.values(samplers)];
|
|
6091
6397
|
}
|
|
6092
6398
|
|
|
6093
|
-
getNextBindingIndex(
|
|
6399
|
+
getNextBindingIndex({ vert, frag }, group = 0) {
|
|
6094
6400
|
// Get the highest binding index in the specified group and return the next available
|
|
6095
6401
|
const samplerRegex = /@group\((\d+)\)\s*@binding\((\d+)\)\s*var\s+(\w+)\s*:\s*(texture_2d<f32>|sampler|uniform)/g;
|
|
6096
6402
|
let maxBindingIndex = -1;
|
|
6097
6403
|
|
|
6098
6404
|
for (const [src, visibility] of [
|
|
6099
|
-
[
|
|
6100
|
-
[
|
|
6405
|
+
[vert, GPUShaderStage.VERTEX],
|
|
6406
|
+
[frag, GPUShaderStage.FRAGMENT]
|
|
6101
6407
|
]) {
|
|
6102
6408
|
let match;
|
|
6103
6409
|
while ((match = samplerRegex.exec(src)) !== null) {
|
|
@@ -6111,11 +6417,14 @@ function rendererWebGPU(p5, fn) {
|
|
|
6111
6417
|
return maxBindingIndex + 1;
|
|
6112
6418
|
}
|
|
6113
6419
|
|
|
6114
|
-
updateUniformValue(
|
|
6420
|
+
updateUniformValue(shader, uniform, data) {
|
|
6115
6421
|
if (uniform.isSampler) {
|
|
6116
6422
|
uniform.texture =
|
|
6117
6423
|
data instanceof Texture ? data : this.getTexture(data);
|
|
6424
|
+
} else {
|
|
6425
|
+
uniform._mappedData = this._mapUniformData(uniform, uniform._cachedData);
|
|
6118
6426
|
}
|
|
6427
|
+
shader.buffersDirty.add(uniform.group * 1000 + uniform.binding);
|
|
6119
6428
|
}
|
|
6120
6429
|
|
|
6121
6430
|
_updateTexture(uniform, tex) {
|
|
@@ -6352,6 +6661,7 @@ function rendererWebGPU(p5, fn) {
|
|
|
6352
6661
|
}
|
|
6353
6662
|
|
|
6354
6663
|
_clearClipBuffer() {
|
|
6664
|
+
this._finishActiveRenderPass();
|
|
6355
6665
|
const commandEncoder = this.device.createCommandEncoder();
|
|
6356
6666
|
|
|
6357
6667
|
const activeFramebuffer = this.activeFramebuffer();
|
|
@@ -6435,12 +6745,33 @@ function rendererWebGPU(p5, fn) {
|
|
|
6435
6745
|
}
|
|
6436
6746
|
}
|
|
6437
6747
|
|
|
6438
|
-
|
|
6748
|
+
// Inject hook uniforms as a separate struct at a new binding
|
|
6749
|
+
let hookUniformFields = '';
|
|
6439
6750
|
for (const key in shader.hooks.uniforms) {
|
|
6440
6751
|
// WGSL format: "name: type"
|
|
6441
|
-
|
|
6752
|
+
hookUniformFields += ` ${key},\n`;
|
|
6753
|
+
}
|
|
6754
|
+
|
|
6755
|
+
if (hookUniformFields) {
|
|
6756
|
+
// Find the next available binding in group 0
|
|
6757
|
+
// Use the source we're currently building (preMain) which has texture bindings. We can't call `fragSrc()`
|
|
6758
|
+
// or `vertSrc()` because we may be in one of those calls already, and might infinite loop
|
|
6759
|
+
const nextBinding = this.getNextBindingIndex({
|
|
6760
|
+
vert: shaderType === 'vertex' ? preMain + (shader.hooks.vertex?.declarations ?? '') + shader.hooks.declarations : shader._vertSrc,
|
|
6761
|
+
frag: shaderType === 'fragment' ? preMain + (shader.hooks.fragment?.declarations ?? '') + shader.hooks.declarations : shader._fragSrc,
|
|
6762
|
+
}, 0);
|
|
6763
|
+
|
|
6764
|
+
// Create HookUniforms struct and binding
|
|
6765
|
+
const hookUniformsDecl = `
|
|
6766
|
+
// Hook Uniforms (from .modify())
|
|
6767
|
+
struct HookUniforms {
|
|
6768
|
+
${hookUniformFields}}
|
|
6769
|
+
|
|
6770
|
+
@group(0) @binding(${nextBinding}) var<uniform> hooks: HookUniforms;
|
|
6771
|
+
`;
|
|
6772
|
+
// Insert before the first @group binding
|
|
6773
|
+
preMain = preMain.replace(/(@group\(0\)\s+@binding)/, `${hookUniformsDecl}\n$1`);
|
|
6442
6774
|
}
|
|
6443
|
-
preMain = preMain.replace(/struct\s+Uniforms\s+\{/, `$&\n${uniforms}`);
|
|
6444
6775
|
|
|
6445
6776
|
// Handle varying variables by injecting them into VertexOutput and FragmentInput structs
|
|
6446
6777
|
if (shader.hooks.varyingVariables && shader.hooks.varyingVariables.length > 0) {
|
|
@@ -6911,6 +7242,7 @@ function rendererWebGPU(p5, fn) {
|
|
|
6911
7242
|
}
|
|
6912
7243
|
|
|
6913
7244
|
_clearFramebufferTextures(framebuffer) {
|
|
7245
|
+
this._finishActiveRenderPass();
|
|
6914
7246
|
const commandEncoder = this.device.createCommandEncoder();
|
|
6915
7247
|
|
|
6916
7248
|
// Clear the color texture (and multisampled texture if it exists)
|
|
@@ -7351,6 +7683,7 @@ function rendererWebGPU(p5, fn) {
|
|
|
7351
7683
|
* Copy framebuffer content directly to WebGPU texture mip level
|
|
7352
7684
|
*/
|
|
7353
7685
|
_accumulateMipLevel(framebuffer, mipmapData, mipLevel, width, height) {
|
|
7686
|
+
this.flushDraw();
|
|
7354
7687
|
// Copy from framebuffer texture to the mip level
|
|
7355
7688
|
const commandEncoder = this.device.createCommandEncoder();
|
|
7356
7689
|
|