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