q5 2.2.3 → 2.4.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/README.md +8 -2
- package/package.json +3 -4
- package/q5.js +633 -447
- package/q5.min.js +2 -2
- package/src/q5-2d-canvas.js +9 -14
- package/src/q5-2d-drawing.js +3 -45
- package/src/q5-2d-image.js +8 -4
- package/src/q5-2d-text.js +4 -14
- package/src/q5-ai.js +4 -5
- package/src/q5-canvas.js +63 -2
- package/src/q5-core.js +4 -3
- package/src/q5-display.js +6 -0
- package/src/q5-math.js +27 -20
- package/src/q5-vector.js +57 -10
- package/src/q5-webgpu-canvas.js +132 -101
- package/src/q5-webgpu-drawing.js +120 -85
- package/src/q5-webgpu-image.js +161 -141
- package/src/q5-webgpu-text.js +38 -1
- package/src/readme.md +37 -6
- package/q5-q2d.js +0 -3085
- package/q5-q2d.min.js +0 -8
package/q5.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* q5.js
|
|
3
|
-
* @version 2.
|
|
3
|
+
* @version 2.4
|
|
4
4
|
* @author quinton-ashley, Tezumie, and LingDong-
|
|
5
5
|
* @license LGPL-3.0
|
|
6
6
|
* @class Q5
|
|
@@ -44,7 +44,8 @@ function Q5(scope, parent, renderer) {
|
|
|
44
44
|
$._loop = true;
|
|
45
45
|
$._hooks = {
|
|
46
46
|
postCanvas: [],
|
|
47
|
-
preRender: []
|
|
47
|
+
preRender: [],
|
|
48
|
+
postRender: []
|
|
48
49
|
};
|
|
49
50
|
|
|
50
51
|
let millisStart = 0;
|
|
@@ -219,7 +220,7 @@ function Q5(scope, parent, renderer) {
|
|
|
219
220
|
return t[k]();
|
|
220
221
|
} catch (e) {
|
|
221
222
|
if ($._aiErrorAssistance) $._aiErrorAssistance(e);
|
|
222
|
-
|
|
223
|
+
throw e;
|
|
223
224
|
}
|
|
224
225
|
};
|
|
225
226
|
}
|
|
@@ -280,6 +281,58 @@ if (typeof document == 'object') {
|
|
|
280
281
|
});
|
|
281
282
|
}
|
|
282
283
|
Q5.modules.canvas = ($, q) => {
|
|
284
|
+
$.CENTER = 'center';
|
|
285
|
+
$.LEFT = 'left';
|
|
286
|
+
$.RIGHT = 'right';
|
|
287
|
+
$.TOP = 'top';
|
|
288
|
+
$.BOTTOM = 'bottom';
|
|
289
|
+
|
|
290
|
+
$.BASELINE = 'alphabetic';
|
|
291
|
+
|
|
292
|
+
$.NORMAL = 'normal';
|
|
293
|
+
$.ITALIC = 'italic';
|
|
294
|
+
$.BOLD = 'bold';
|
|
295
|
+
$.BOLDITALIC = 'italic bold';
|
|
296
|
+
|
|
297
|
+
$.ROUND = 'round';
|
|
298
|
+
$.SQUARE = 'butt';
|
|
299
|
+
$.PROJECT = 'square';
|
|
300
|
+
$.MITER = 'miter';
|
|
301
|
+
$.BEVEL = 'bevel';
|
|
302
|
+
|
|
303
|
+
$.CHORD = 0;
|
|
304
|
+
$.PIE = 1;
|
|
305
|
+
$.OPEN = 2;
|
|
306
|
+
|
|
307
|
+
$.RADIUS = 'radius';
|
|
308
|
+
$.CORNER = 'corner';
|
|
309
|
+
$.CORNERS = 'corners';
|
|
310
|
+
|
|
311
|
+
$.CLOSE = 1;
|
|
312
|
+
|
|
313
|
+
$.LANDSCAPE = 'landscape';
|
|
314
|
+
$.PORTRAIT = 'portrait';
|
|
315
|
+
|
|
316
|
+
$.BLEND = 'source-over';
|
|
317
|
+
$.REMOVE = 'destination-out';
|
|
318
|
+
$.ADD = 'lighter';
|
|
319
|
+
$.DARKEST = 'darken';
|
|
320
|
+
$.LIGHTEST = 'lighten';
|
|
321
|
+
$.DIFFERENCE = 'difference';
|
|
322
|
+
$.SUBTRACT = 'subtract';
|
|
323
|
+
$.EXCLUSION = 'exclusion';
|
|
324
|
+
$.MULTIPLY = 'multiply';
|
|
325
|
+
$.SCREEN = 'screen';
|
|
326
|
+
$.REPLACE = 'copy';
|
|
327
|
+
$.OVERLAY = 'overlay';
|
|
328
|
+
$.HARD_LIGHT = 'hard-light';
|
|
329
|
+
$.SOFT_LIGHT = 'soft-light';
|
|
330
|
+
$.DODGE = 'color-dodge';
|
|
331
|
+
$.BURN = 'color-burn';
|
|
332
|
+
|
|
333
|
+
$.P2D = '2d';
|
|
334
|
+
$.WEBGL = 'webgl';
|
|
335
|
+
|
|
283
336
|
$._OffscreenCanvas =
|
|
284
337
|
window.OffscreenCanvas ||
|
|
285
338
|
function () {
|
|
@@ -309,7 +362,7 @@ Q5.modules.canvas = ($, q) => {
|
|
|
309
362
|
c.renderer = $._renderer;
|
|
310
363
|
c[$._renderer] = true;
|
|
311
364
|
}
|
|
312
|
-
$._pixelDensity = 1;
|
|
365
|
+
$._pixelDensity = window?.devicePixelRatio || 1;
|
|
313
366
|
|
|
314
367
|
$._adjustDisplay = () => {
|
|
315
368
|
if (c.style) {
|
|
@@ -346,6 +399,15 @@ Q5.modules.canvas = ($, q) => {
|
|
|
346
399
|
return rend;
|
|
347
400
|
};
|
|
348
401
|
|
|
402
|
+
$.createGraphics = function (w, h, opt) {
|
|
403
|
+
let g = new Q5('graphics');
|
|
404
|
+
opt ??= {};
|
|
405
|
+
opt.alpha ??= true;
|
|
406
|
+
opt.colorSpace ??= $.canvas.colorSpace;
|
|
407
|
+
g.createCanvas.call($, w, h, opt);
|
|
408
|
+
return g;
|
|
409
|
+
};
|
|
410
|
+
|
|
349
411
|
$._save = async (data, name, ext) => {
|
|
350
412
|
name = name || 'untitled';
|
|
351
413
|
ext = ext || 'png';
|
|
@@ -454,7 +516,7 @@ Q5.modules.canvas = ($, q) => {
|
|
|
454
516
|
$.canvas.resize = $.resizeCanvas;
|
|
455
517
|
$.canvas.save = $.saveCanvas = $.save;
|
|
456
518
|
|
|
457
|
-
$.displayDensity = () => window.devicePixelRatio;
|
|
519
|
+
$.displayDensity = () => window.devicePixelRatio || 1;
|
|
458
520
|
$.pixelDensity = (v) => {
|
|
459
521
|
if (!v || v == $._pixelDensity) return $._pixelDensity;
|
|
460
522
|
$._pixelDensity = v;
|
|
@@ -528,8 +590,10 @@ Q5.renderers.q2d.canvas = ($, q) => {
|
|
|
528
590
|
$._doFill = true;
|
|
529
591
|
$._fillSet = true;
|
|
530
592
|
if (Q5.Color) {
|
|
531
|
-
if (!c._q5Color
|
|
532
|
-
|
|
593
|
+
if (!c._q5Color) {
|
|
594
|
+
if (typeof c != 'string') c = $.color(...arguments);
|
|
595
|
+
else if ($._namedColors[c]) c = $.color(...$._namedColors[c]);
|
|
596
|
+
}
|
|
533
597
|
if (c.a <= 0) return ($._doFill = false);
|
|
534
598
|
}
|
|
535
599
|
$.ctx.fillStyle = c.toString();
|
|
@@ -539,8 +603,10 @@ Q5.renderers.q2d.canvas = ($, q) => {
|
|
|
539
603
|
$._doStroke = true;
|
|
540
604
|
$._strokeSet = true;
|
|
541
605
|
if (Q5.Color) {
|
|
542
|
-
if (!c._q5Color
|
|
543
|
-
|
|
606
|
+
if (!c._q5Color) {
|
|
607
|
+
if (typeof c != 'string') c = $.color(...arguments);
|
|
608
|
+
else if ($._namedColors[c]) c = $.color(...$._namedColors[c]);
|
|
609
|
+
}
|
|
544
610
|
if (c.a <= 0) return ($._doStroke = false);
|
|
545
611
|
}
|
|
546
612
|
$.ctx.strokeStyle = c.toString();
|
|
@@ -568,7 +634,7 @@ Q5.renderers.q2d.canvas = ($, q) => {
|
|
|
568
634
|
$.ctx.translate(x, y);
|
|
569
635
|
};
|
|
570
636
|
$.rotate = (r) => {
|
|
571
|
-
if ($._angleMode
|
|
637
|
+
if ($._angleMode) r = $.radians(r);
|
|
572
638
|
$.ctx.rotate(r);
|
|
573
639
|
};
|
|
574
640
|
$.scale = (x, y) => {
|
|
@@ -630,15 +696,6 @@ Q5.renderers.q2d.canvas = ($, q) => {
|
|
|
630
696
|
return vid;
|
|
631
697
|
};
|
|
632
698
|
|
|
633
|
-
$.createGraphics = function (w, h, opt) {
|
|
634
|
-
let g = new Q5('graphics');
|
|
635
|
-
opt ??= {};
|
|
636
|
-
opt.alpha ??= true;
|
|
637
|
-
opt.colorSpace ??= $.canvas.colorSpace;
|
|
638
|
-
g.createCanvas.call($, w, h, opt);
|
|
639
|
-
return g;
|
|
640
|
-
};
|
|
641
|
-
|
|
642
699
|
if (window && $._scope != 'graphics') {
|
|
643
700
|
window.addEventListener('resize', () => {
|
|
644
701
|
$._shouldResize = true;
|
|
@@ -649,48 +706,6 @@ Q5.renderers.q2d.canvas = ($, q) => {
|
|
|
649
706
|
}
|
|
650
707
|
};
|
|
651
708
|
Q5.renderers.q2d.drawing = ($) => {
|
|
652
|
-
$.CHORD = 0;
|
|
653
|
-
$.PIE = 1;
|
|
654
|
-
$.OPEN = 2;
|
|
655
|
-
|
|
656
|
-
$.RADIUS = 'radius';
|
|
657
|
-
$.CORNER = 'corner';
|
|
658
|
-
$.CORNERS = 'corners';
|
|
659
|
-
|
|
660
|
-
$.ROUND = 'round';
|
|
661
|
-
$.SQUARE = 'butt';
|
|
662
|
-
$.PROJECT = 'square';
|
|
663
|
-
$.MITER = 'miter';
|
|
664
|
-
$.BEVEL = 'bevel';
|
|
665
|
-
|
|
666
|
-
$.CLOSE = 1;
|
|
667
|
-
|
|
668
|
-
$.CENTER = 'center';
|
|
669
|
-
$.LEFT = 'left';
|
|
670
|
-
$.RIGHT = 'right';
|
|
671
|
-
$.TOP = 'top';
|
|
672
|
-
$.BOTTOM = 'bottom';
|
|
673
|
-
|
|
674
|
-
$.LANDSCAPE = 'landscape';
|
|
675
|
-
$.PORTRAIT = 'portrait';
|
|
676
|
-
|
|
677
|
-
$.BLEND = 'source-over';
|
|
678
|
-
$.REMOVE = 'destination-out';
|
|
679
|
-
$.ADD = 'lighter';
|
|
680
|
-
$.DARKEST = 'darken';
|
|
681
|
-
$.LIGHTEST = 'lighten';
|
|
682
|
-
$.DIFFERENCE = 'difference';
|
|
683
|
-
$.SUBTRACT = 'subtract';
|
|
684
|
-
$.EXCLUSION = 'exclusion';
|
|
685
|
-
$.MULTIPLY = 'multiply';
|
|
686
|
-
$.SCREEN = 'screen';
|
|
687
|
-
$.REPLACE = 'copy';
|
|
688
|
-
$.OVERLAY = 'overlay';
|
|
689
|
-
$.HARD_LIGHT = 'hard-light';
|
|
690
|
-
$.SOFT_LIGHT = 'soft-light';
|
|
691
|
-
$.DODGE = 'color-dodge';
|
|
692
|
-
$.BURN = 'color-burn';
|
|
693
|
-
|
|
694
709
|
$._doStroke = true;
|
|
695
710
|
$._doFill = true;
|
|
696
711
|
$._strokeSet = false;
|
|
@@ -726,8 +741,8 @@ Q5.renderers.q2d.drawing = ($) => {
|
|
|
726
741
|
$.ctx.resetTransform();
|
|
727
742
|
if (c.canvas) $.image(c, 0, 0, $.width, $.height);
|
|
728
743
|
else {
|
|
729
|
-
if (Q5.Color) {
|
|
730
|
-
if (
|
|
744
|
+
if (Q5.Color && !c._q5Color) {
|
|
745
|
+
if (typeof c != 'string') c = $.color(...arguments);
|
|
731
746
|
else if ($._namedColors[c]) c = $.color(...$._namedColors[c]);
|
|
732
747
|
}
|
|
733
748
|
$.ctx.fillStyle = c.toString();
|
|
@@ -753,7 +768,7 @@ Q5.renderers.q2d.drawing = ($) => {
|
|
|
753
768
|
|
|
754
769
|
function arc(x, y, w, h, lo, hi, mode, detail) {
|
|
755
770
|
if (!$._doFill && !$._doStroke) return;
|
|
756
|
-
let d = $._angleMode
|
|
771
|
+
let d = $._angleMode;
|
|
757
772
|
let full = d ? 360 : $.TAU;
|
|
758
773
|
lo %= full;
|
|
759
774
|
hi %= full;
|
|
@@ -1274,11 +1289,15 @@ Q5.renderers.q2d.image = ($, q) => {
|
|
|
1274
1289
|
};
|
|
1275
1290
|
}
|
|
1276
1291
|
|
|
1292
|
+
$._getImageData = (x, y, w, h) => {
|
|
1293
|
+
return $.ctx.getImageData(x, y, w, h, { colorSpace: $.canvas.colorSpace });
|
|
1294
|
+
};
|
|
1295
|
+
|
|
1277
1296
|
$.trim = () => {
|
|
1278
1297
|
let pd = $._pixelDensity || 1;
|
|
1279
1298
|
let w = $.canvas.width;
|
|
1280
1299
|
let h = $.canvas.height;
|
|
1281
|
-
let data = $.
|
|
1300
|
+
let data = $._getImageData(0, 0, w, h).data;
|
|
1282
1301
|
let left = w,
|
|
1283
1302
|
right = 0,
|
|
1284
1303
|
top = h,
|
|
@@ -1317,7 +1336,7 @@ Q5.renderers.q2d.image = ($, q) => {
|
|
|
1317
1336
|
$.get = (x, y, w, h) => {
|
|
1318
1337
|
let pd = $._pixelDensity || 1;
|
|
1319
1338
|
if (x !== undefined && w === undefined) {
|
|
1320
|
-
let c = $.
|
|
1339
|
+
let c = $._getImageData(x * pd, y * pd, 1, 1).data;
|
|
1321
1340
|
return new $.Color(c[0], c[1], c[2], c[3] / 255);
|
|
1322
1341
|
}
|
|
1323
1342
|
x = (x || 0) * pd;
|
|
@@ -1327,7 +1346,7 @@ Q5.renderers.q2d.image = ($, q) => {
|
|
|
1327
1346
|
w *= pd;
|
|
1328
1347
|
h *= pd;
|
|
1329
1348
|
let img = $.createImage(w, h);
|
|
1330
|
-
let imgData = $.
|
|
1349
|
+
let imgData = $._getImageData(x, y, w, h);
|
|
1331
1350
|
img.ctx.putImageData(imgData, 0, 0);
|
|
1332
1351
|
img._pixelDensity = pd;
|
|
1333
1352
|
img.width = _w;
|
|
@@ -1357,7 +1376,7 @@ Q5.renderers.q2d.image = ($, q) => {
|
|
|
1357
1376
|
};
|
|
1358
1377
|
|
|
1359
1378
|
$.loadPixels = () => {
|
|
1360
|
-
imgData = $.
|
|
1379
|
+
imgData = $._getImageData(0, 0, $.canvas.width, $.canvas.height);
|
|
1361
1380
|
q.pixels = imgData.data;
|
|
1362
1381
|
};
|
|
1363
1382
|
$.updatePixels = () => {
|
|
@@ -1384,18 +1403,6 @@ Q5.DILATE = 6;
|
|
|
1384
1403
|
Q5.ERODE = 7;
|
|
1385
1404
|
Q5.BLUR = 8;
|
|
1386
1405
|
Q5.renderers.q2d.text = ($, q) => {
|
|
1387
|
-
$.NORMAL = 'normal';
|
|
1388
|
-
$.ITALIC = 'italic';
|
|
1389
|
-
$.BOLD = 'bold';
|
|
1390
|
-
$.BOLDITALIC = 'italic bold';
|
|
1391
|
-
|
|
1392
|
-
$.CENTER = 'center';
|
|
1393
|
-
$.LEFT = 'left';
|
|
1394
|
-
$.RIGHT = 'right';
|
|
1395
|
-
$.TOP = 'top';
|
|
1396
|
-
$.BOTTOM = 'bottom';
|
|
1397
|
-
$.BASELINE = 'alphabetic';
|
|
1398
|
-
|
|
1399
1406
|
$._textFont = 'sans-serif';
|
|
1400
1407
|
$._textSize = 12;
|
|
1401
1408
|
$._textLeading = 15;
|
|
@@ -1507,12 +1514,14 @@ Q5.renderers.q2d.text = ($, q) => {
|
|
|
1507
1514
|
);
|
|
1508
1515
|
};
|
|
1509
1516
|
$.createTextImage = (str, w, h) => {
|
|
1517
|
+
let k = $._genTextImageKey(str, w, h);
|
|
1518
|
+
if ($._tic.get(k)) return $._tic.get(k);
|
|
1519
|
+
|
|
1510
1520
|
let og = $._textCache;
|
|
1511
1521
|
$._textCache = true;
|
|
1512
1522
|
$._genTextImage = true;
|
|
1513
1523
|
$.text(str, 0, 0, w, h);
|
|
1514
1524
|
$._genTextImage = false;
|
|
1515
|
-
let k = $._genTextImageKey(str, w, h);
|
|
1516
1525
|
$._textCache = og;
|
|
1517
1526
|
return $._tic.get(k);
|
|
1518
1527
|
};
|
|
@@ -1566,7 +1575,7 @@ Q5.renderers.q2d.text = ($, q) => {
|
|
|
1566
1575
|
}
|
|
1567
1576
|
if (!$._fillSet) c.fillStyle = f;
|
|
1568
1577
|
if (useCache) {
|
|
1569
|
-
ti = tg.
|
|
1578
|
+
ti = tg.canvas;
|
|
1570
1579
|
ti._ascent = _ascent;
|
|
1571
1580
|
ti._descent = _descent;
|
|
1572
1581
|
$._tic.set(k, ti);
|
|
@@ -1603,8 +1612,7 @@ Q5.modules.ai = ($) => {
|
|
|
1603
1612
|
|
|
1604
1613
|
$._aiErrorAssistance = async (e) => {
|
|
1605
1614
|
let askAI = e.message?.includes('Ask AI ✨');
|
|
1606
|
-
if (!askAI)
|
|
1607
|
-
if (Q5.disableFriendlyErrors) return;
|
|
1615
|
+
if (Q5.disableFriendlyErrors && !askAI) return;
|
|
1608
1616
|
if (askAI || !Q5.errorTolerant) $.noLoop();
|
|
1609
1617
|
let stackLines = e.stack?.split('\n');
|
|
1610
1618
|
if (!e.stack || stackLines.length <= 1) return;
|
|
@@ -1615,7 +1623,7 @@ Q5.modules.ai = ($) => {
|
|
|
1615
1623
|
idx = 0;
|
|
1616
1624
|
sep = '@';
|
|
1617
1625
|
}
|
|
1618
|
-
while (stackLines[idx].indexOf('q5
|
|
1626
|
+
while (stackLines[idx].indexOf('q5') >= 0) idx++;
|
|
1619
1627
|
|
|
1620
1628
|
let parts = stackLines[idx].split(sep).at(-1);
|
|
1621
1629
|
parts = parts.split(':');
|
|
@@ -1653,11 +1661,11 @@ Q5.modules.ai = ($) => {
|
|
|
1653
1661
|
'%0A%0AExcerpt+for+context%3A%0A%0A' +
|
|
1654
1662
|
encodeURIComponent(context);
|
|
1655
1663
|
|
|
1656
|
-
|
|
1664
|
+
console.warn('Error in ' + fileBase + ' on line ' + lineNum + ':\n\n' + errLine);
|
|
1657
1665
|
|
|
1658
1666
|
console.warn('Ask AI ✨ ' + url);
|
|
1659
1667
|
|
|
1660
|
-
if (askAI) window.open(url, '_blank');
|
|
1668
|
+
if (askAI) return window.open(url, '_blank');
|
|
1661
1669
|
} catch (err) {}
|
|
1662
1670
|
};
|
|
1663
1671
|
};
|
|
@@ -1750,12 +1758,7 @@ Q5.modules.color = ($, q) => {
|
|
|
1750
1758
|
if (c3) c3 /= 255;
|
|
1751
1759
|
}
|
|
1752
1760
|
}
|
|
1753
|
-
if (Array.isArray(c0))
|
|
1754
|
-
c1 = c0[1];
|
|
1755
|
-
c2 = c0[2];
|
|
1756
|
-
c3 = c0[3];
|
|
1757
|
-
c0 = c0[0];
|
|
1758
|
-
}
|
|
1761
|
+
if (Array.isArray(c0)) [c0, c1, c2, c3] = c0;
|
|
1759
1762
|
}
|
|
1760
1763
|
|
|
1761
1764
|
if (c2 == undefined) return new C(c0, c0, c0, c1);
|
|
@@ -1767,11 +1770,11 @@ Q5.modules.color = ($, q) => {
|
|
|
1767
1770
|
$.blue = (c) => c.b;
|
|
1768
1771
|
$.alpha = (c) => c.a;
|
|
1769
1772
|
$.lightness = (c) => {
|
|
1770
|
-
if (
|
|
1773
|
+
if (c.l) return c.l;
|
|
1771
1774
|
return ((0.2126 * c.r + 0.7152 * c.g + 0.0722 * c.b) * 100) / 255;
|
|
1772
1775
|
};
|
|
1773
1776
|
$.hue = (c) => {
|
|
1774
|
-
if (
|
|
1777
|
+
if (c.h) return c.h;
|
|
1775
1778
|
let r = c.r;
|
|
1776
1779
|
let g = c.g;
|
|
1777
1780
|
let b = c.b;
|
|
@@ -1930,6 +1933,12 @@ Q5.modules.display = ($) => {
|
|
|
1930
1933
|
|
|
1931
1934
|
let c = $.canvas;
|
|
1932
1935
|
|
|
1936
|
+
$.CENTERED = 'centered';
|
|
1937
|
+
$.FULLSCREEN = 'fullscreen';
|
|
1938
|
+
$.MAXED = 'maxed';
|
|
1939
|
+
|
|
1940
|
+
$.PIXELATED = 'pixelated';
|
|
1941
|
+
|
|
1933
1942
|
if (Q5._instanceCount == 0 && !Q5._nodejs) {
|
|
1934
1943
|
document.head.insertAdjacentHTML(
|
|
1935
1944
|
'beforeend',
|
|
@@ -2195,8 +2204,8 @@ Q5.modules.input = ($, q) => {
|
|
|
2195
2204
|
}
|
|
2196
2205
|
};
|
|
2197
2206
|
Q5.modules.math = ($, q) => {
|
|
2198
|
-
$.
|
|
2199
|
-
$.
|
|
2207
|
+
$.RADIANS = 0;
|
|
2208
|
+
$.DEGREES = 1;
|
|
2200
2209
|
|
|
2201
2210
|
$.PI = Math.PI;
|
|
2202
2211
|
$.HALF_PI = Math.PI / 2;
|
|
@@ -2217,9 +2226,14 @@ Q5.modules.math = ($, q) => {
|
|
|
2217
2226
|
$.SHR3 = 1;
|
|
2218
2227
|
$.LCG = 2;
|
|
2219
2228
|
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
$.
|
|
2229
|
+
let angleMode = 0;
|
|
2230
|
+
|
|
2231
|
+
$.angleMode = (mode) => {
|
|
2232
|
+
if (mode == 'radians') mode = 0;
|
|
2233
|
+
angleMode = $._angleMode = mode;
|
|
2234
|
+
};
|
|
2235
|
+
let DEGTORAD = ($._DEGTORAD = Math.PI / 180);
|
|
2236
|
+
let RADTODEG = ($._RADTODEG = 180 / Math.PI);
|
|
2223
2237
|
$.degrees = (x) => x * $._RADTODEG;
|
|
2224
2238
|
$.radians = (x) => x * $._DEGTORAD;
|
|
2225
2239
|
|
|
@@ -2245,24 +2259,26 @@ Q5.modules.math = ($, q) => {
|
|
|
2245
2259
|
$.sq = (x) => x * x;
|
|
2246
2260
|
$.fract = (x) => x - Math.floor(x);
|
|
2247
2261
|
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2262
|
+
$.sin = (a) => Math.sin(!angleMode ? a : a * DEGTORAD);
|
|
2263
|
+
$.cos = (a) => Math.cos(!angleMode ? a : a * DEGTORAD);
|
|
2264
|
+
$.tan = (a) => Math.tan(!angleMode ? a : a * DEGTORAD);
|
|
2265
|
+
|
|
2266
|
+
$.asin = (x) => {
|
|
2267
|
+
let a = Math.asin(x);
|
|
2268
|
+
return !angleMode ? a : a * RADTODEG;
|
|
2269
|
+
};
|
|
2270
|
+
$.acos = (x) => {
|
|
2271
|
+
let a = Math.acos(x);
|
|
2272
|
+
return !angleMode ? a : a * RADTODEG;
|
|
2273
|
+
};
|
|
2274
|
+
$.atan = (x) => {
|
|
2275
|
+
let a = Math.atan(x);
|
|
2276
|
+
return !angleMode ? a : a * RADTODEG;
|
|
2277
|
+
};
|
|
2254
2278
|
|
|
2255
|
-
for (let fn of ['asin', 'acos', 'atan']) {
|
|
2256
|
-
$[fn] = (x) => {
|
|
2257
|
-
let a = Math[fn](x);
|
|
2258
|
-
if ($._angleMode == 'degrees') a = $.degrees(a);
|
|
2259
|
-
return a;
|
|
2260
|
-
};
|
|
2261
|
-
}
|
|
2262
2279
|
$.atan2 = (y, x) => {
|
|
2263
2280
|
let a = Math.atan2(y, x);
|
|
2264
|
-
|
|
2265
|
-
return a;
|
|
2281
|
+
return !angleMode ? a : a * RADTODEG;
|
|
2266
2282
|
};
|
|
2267
2283
|
|
|
2268
2284
|
function lcg() {
|
|
@@ -2687,18 +2703,19 @@ Q5.modules.vector = ($) => {
|
|
|
2687
2703
|
};
|
|
2688
2704
|
|
|
2689
2705
|
Q5.Vector = class {
|
|
2690
|
-
constructor(
|
|
2691
|
-
this.x =
|
|
2692
|
-
this.y =
|
|
2693
|
-
this.z =
|
|
2694
|
-
this._$ =
|
|
2706
|
+
constructor(x, y, z, $) {
|
|
2707
|
+
this.x = x || 0;
|
|
2708
|
+
this.y = y || 0;
|
|
2709
|
+
this.z = z || 0;
|
|
2710
|
+
this._$ = $ || window;
|
|
2695
2711
|
this._cn = null;
|
|
2696
2712
|
this._cnsq = null;
|
|
2697
2713
|
}
|
|
2698
|
-
set(
|
|
2699
|
-
this.x =
|
|
2700
|
-
this.y =
|
|
2701
|
-
this.z =
|
|
2714
|
+
set(x, y, z) {
|
|
2715
|
+
this.x = x?.x || x || 0;
|
|
2716
|
+
this.y = x?.y || y || 0;
|
|
2717
|
+
this.z = x?.z || z || 0;
|
|
2718
|
+
return this;
|
|
2702
2719
|
}
|
|
2703
2720
|
copy() {
|
|
2704
2721
|
return new Q5.Vector(this.x, this.y, this.z);
|
|
@@ -2820,6 +2837,12 @@ Q5.Vector = class {
|
|
|
2820
2837
|
heading() {
|
|
2821
2838
|
return this._$.atan2(this.y, this.x);
|
|
2822
2839
|
}
|
|
2840
|
+
setHeading(ang) {
|
|
2841
|
+
let mag = this.mag(); // Calculate the magnitude of the vector
|
|
2842
|
+
this.x = mag * this._$.cos(ang); // Set the new x component
|
|
2843
|
+
this.y = mag * this._$.sin(ang); // Set the new y component
|
|
2844
|
+
return this;
|
|
2845
|
+
}
|
|
2823
2846
|
rotate(ang) {
|
|
2824
2847
|
let costh = this._$.cos(ang);
|
|
2825
2848
|
let sinth = this._$.sin(ang);
|
|
@@ -2837,13 +2860,52 @@ Q5.Vector = class {
|
|
|
2837
2860
|
}
|
|
2838
2861
|
lerp() {
|
|
2839
2862
|
let args = [...arguments];
|
|
2840
|
-
let u = this._arg2v(...args.slice(0, -1));
|
|
2841
2863
|
let amt = args.at(-1);
|
|
2864
|
+
if (amt == 0) return this;
|
|
2865
|
+
let u = this._arg2v(...args.slice(0, -1));
|
|
2842
2866
|
this.x += (u.x - this.x) * amt;
|
|
2843
2867
|
this.y += (u.y - this.y) * amt;
|
|
2844
2868
|
this.z += (u.z - this.z) * amt;
|
|
2845
2869
|
return this;
|
|
2846
2870
|
}
|
|
2871
|
+
slerp() {
|
|
2872
|
+
let args = [...arguments];
|
|
2873
|
+
let amt = args.at(-1);
|
|
2874
|
+
if (amt == 0) return this;
|
|
2875
|
+
let u = this._arg2v(...args.slice(0, -1));
|
|
2876
|
+
if (amt == 1) return this.set(u);
|
|
2877
|
+
|
|
2878
|
+
let v0Mag = this.mag();
|
|
2879
|
+
let v1Mag = u.mag();
|
|
2880
|
+
|
|
2881
|
+
if (v0Mag == 0 || v1Mag == 0) {
|
|
2882
|
+
return this.mult(1 - amt).add(u.mult(amt));
|
|
2883
|
+
}
|
|
2884
|
+
|
|
2885
|
+
let axis = Q5.Vector.cross(this, u);
|
|
2886
|
+
let axisMag = axis.mag();
|
|
2887
|
+
let theta = Math.atan2(axisMag, this.dot(u));
|
|
2888
|
+
|
|
2889
|
+
if (axisMag > 0) {
|
|
2890
|
+
axis.div(axisMag);
|
|
2891
|
+
} else if (theta < this._$.HALF_PI) {
|
|
2892
|
+
return this.mult(1 - amt).add(u.mult(amt));
|
|
2893
|
+
} else {
|
|
2894
|
+
if (this.z == 0 && u.z == 0) axis.set(0, 0, 1);
|
|
2895
|
+
else if (this.x != 0) axis.set(this.y, -this.x, 0).normalize();
|
|
2896
|
+
else axis.set(1, 0, 0);
|
|
2897
|
+
}
|
|
2898
|
+
|
|
2899
|
+
let ey = axis.cross(this);
|
|
2900
|
+
let lerpedMagFactor = 1 - amt + (amt * v1Mag) / v0Mag;
|
|
2901
|
+
let cosMultiplier = lerpedMagFactor * Math.cos(amt * theta);
|
|
2902
|
+
let sinMultiplier = lerpedMagFactor * Math.sin(amt * theta);
|
|
2903
|
+
|
|
2904
|
+
this.x = this.x * cosMultiplier + ey.x * sinMultiplier;
|
|
2905
|
+
this.y = this.y * cosMultiplier + ey.y * sinMultiplier;
|
|
2906
|
+
this.z = this.z * cosMultiplier + ey.z * sinMultiplier;
|
|
2907
|
+
return this;
|
|
2908
|
+
}
|
|
2847
2909
|
reflect(n) {
|
|
2848
2910
|
n.normalize();
|
|
2849
2911
|
return this.sub(n.mult(2 * this.dot(n)));
|
|
@@ -2896,6 +2958,7 @@ Q5.Vector.div = (v, u) => v.copy().div(u);
|
|
|
2896
2958
|
Q5.Vector.dot = (v, u) => v.copy().dot(u);
|
|
2897
2959
|
Q5.Vector.equals = (v, u, epsilon) => v.equals(u, epsilon);
|
|
2898
2960
|
Q5.Vector.lerp = (v, u, amt) => v.copy().lerp(u, amt);
|
|
2961
|
+
Q5.Vector.slerp = (v, u, amt) => v.copy().slerp(u, amt);
|
|
2899
2962
|
Q5.Vector.limit = (v, m) => v.copy().limit(m);
|
|
2900
2963
|
Q5.Vector.heading = (v) => this._$.atan2(v.y, v.x);
|
|
2901
2964
|
Q5.Vector.magSq = (v) => v.x * v.x + v.y * v.y + v.z * v.z;
|
|
@@ -2922,70 +2985,62 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
2922
2985
|
|
|
2923
2986
|
if ($.colorMode) $.colorMode('rgb', 'float');
|
|
2924
2987
|
|
|
2925
|
-
let pass
|
|
2926
|
-
|
|
2927
|
-
$._createCanvas = (w, h, opt) => {
|
|
2928
|
-
q.ctx = q.drawingContext = c.getContext('webgpu');
|
|
2929
|
-
|
|
2930
|
-
opt.format = navigator.gpu.getPreferredCanvasFormat();
|
|
2931
|
-
opt.device = Q5.device;
|
|
2988
|
+
let pass;
|
|
2932
2989
|
|
|
2933
|
-
|
|
2990
|
+
$.pipelines = [];
|
|
2934
2991
|
|
|
2935
|
-
|
|
2992
|
+
// local variables used for slightly better performance
|
|
2993
|
+
// stores pipeline shifts and vertex counts/image indices
|
|
2994
|
+
let drawStack = ($.drawStack = []);
|
|
2936
2995
|
|
|
2937
|
-
|
|
2938
|
-
|
|
2996
|
+
// colors used for each draw call
|
|
2997
|
+
let colorsStack = ($.colorsStack = [1, 1, 1, 1]);
|
|
2939
2998
|
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2999
|
+
$._envLayout = Q5.device.createBindGroupLayout({
|
|
3000
|
+
entries: [
|
|
3001
|
+
{
|
|
3002
|
+
binding: 0,
|
|
3003
|
+
visibility: GPUShaderStage.VERTEX,
|
|
3004
|
+
buffer: {
|
|
3005
|
+
type: 'uniform',
|
|
3006
|
+
hasDynamicOffset: false
|
|
3007
|
+
}
|
|
3008
|
+
}
|
|
3009
|
+
]
|
|
3010
|
+
});
|
|
2945
3011
|
|
|
2946
|
-
|
|
2947
|
-
|
|
3012
|
+
$._transformLayout = Q5.device.createBindGroupLayout({
|
|
3013
|
+
entries: [
|
|
3014
|
+
{
|
|
3015
|
+
binding: 0,
|
|
3016
|
+
visibility: GPUShaderStage.VERTEX,
|
|
3017
|
+
buffer: {
|
|
3018
|
+
type: 'read-only-storage',
|
|
3019
|
+
hasDynamicOffset: false
|
|
3020
|
+
}
|
|
3021
|
+
}
|
|
3022
|
+
]
|
|
3023
|
+
});
|
|
2948
3024
|
|
|
2949
|
-
|
|
2950
|
-
$._colorIndex = 0;
|
|
3025
|
+
$.bindGroupLayouts = [$._envLayout, $._transformLayout];
|
|
2951
3026
|
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
visibility: GPUShaderStage.VERTEX,
|
|
2957
|
-
buffer: {
|
|
2958
|
-
type: 'uniform',
|
|
2959
|
-
hasDynamicOffset: false
|
|
2960
|
-
}
|
|
2961
|
-
}
|
|
2962
|
-
]
|
|
2963
|
-
});
|
|
3027
|
+
const uniformBuffer = Q5.device.createBuffer({
|
|
3028
|
+
size: 8, // Size of two floats
|
|
3029
|
+
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
|
|
3030
|
+
});
|
|
2964
3031
|
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
{
|
|
2968
|
-
binding: 0,
|
|
2969
|
-
visibility: GPUShaderStage.VERTEX,
|
|
2970
|
-
buffer: {
|
|
2971
|
-
type: 'read-only-storage',
|
|
2972
|
-
hasDynamicOffset: false
|
|
2973
|
-
}
|
|
2974
|
-
}
|
|
2975
|
-
]
|
|
2976
|
-
});
|
|
3032
|
+
$._createCanvas = (w, h, opt) => {
|
|
3033
|
+
q.ctx = q.drawingContext = c.getContext('webgpu');
|
|
2977
3034
|
|
|
2978
|
-
|
|
3035
|
+
opt.format = navigator.gpu.getPreferredCanvasFormat();
|
|
3036
|
+
opt.device = Q5.device;
|
|
2979
3037
|
|
|
2980
|
-
|
|
2981
|
-
size: 8, // Size of two floats
|
|
2982
|
-
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
|
|
2983
|
-
});
|
|
3038
|
+
$.ctx.configure(opt);
|
|
2984
3039
|
|
|
2985
3040
|
Q5.device.queue.writeBuffer(uniformBuffer, 0, new Float32Array([$.canvas.hw, $.canvas.hh]));
|
|
2986
3041
|
|
|
2987
|
-
|
|
2988
|
-
layout:
|
|
3042
|
+
$._envBindGroup = Q5.device.createBindGroup({
|
|
3043
|
+
layout: $._envLayout,
|
|
2989
3044
|
entries: [
|
|
2990
3045
|
{
|
|
2991
3046
|
binding: 0,
|
|
@@ -3044,14 +3099,14 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
3044
3099
|
if (!x && !y && !z) return;
|
|
3045
3100
|
// Update the translation values
|
|
3046
3101
|
$._matrix[3] += x;
|
|
3047
|
-
$._matrix[7]
|
|
3102
|
+
$._matrix[7] -= y;
|
|
3048
3103
|
$._matrix[11] += z || 0;
|
|
3049
3104
|
$._matrixDirty = true;
|
|
3050
3105
|
};
|
|
3051
3106
|
|
|
3052
3107
|
$.rotate = (r) => {
|
|
3053
3108
|
if (!r) return;
|
|
3054
|
-
if ($._angleMode
|
|
3109
|
+
if ($._angleMode) r *= $._DEGTORAD;
|
|
3055
3110
|
|
|
3056
3111
|
let cosR = Math.cos(r);
|
|
3057
3112
|
let sinR = Math.sin(r);
|
|
@@ -3092,10 +3147,72 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
3092
3147
|
$._matrixDirty = false;
|
|
3093
3148
|
};
|
|
3094
3149
|
|
|
3150
|
+
// current color index, used to associate a vertex with a color
|
|
3151
|
+
let colorIndex = 0;
|
|
3152
|
+
const addColor = (r, g, b, a = 1) => {
|
|
3153
|
+
if (typeof r == 'string') r = $.color(r);
|
|
3154
|
+
else if (b == undefined) {
|
|
3155
|
+
// grayscale mode `fill(1, 0.5)`
|
|
3156
|
+
a = g ?? 1;
|
|
3157
|
+
g = b = r;
|
|
3158
|
+
}
|
|
3159
|
+
if (r._q5Color) colorsStack.push(r.r, r.g, r.b, r.a);
|
|
3160
|
+
else colorsStack.push(r, g, b, a);
|
|
3161
|
+
colorIndex++;
|
|
3162
|
+
};
|
|
3163
|
+
|
|
3164
|
+
$.fill = (r, g, b, a) => {
|
|
3165
|
+
addColor(r, g, b, a);
|
|
3166
|
+
$._doFill = true;
|
|
3167
|
+
$._fillIndex = colorIndex;
|
|
3168
|
+
};
|
|
3169
|
+
$.stroke = (r, g, b, a) => {
|
|
3170
|
+
addColor(r, g, b, a);
|
|
3171
|
+
$._doStroke = true;
|
|
3172
|
+
$._strokeIndex = colorIndex;
|
|
3173
|
+
};
|
|
3174
|
+
|
|
3175
|
+
$.noFill = () => ($._doFill = false);
|
|
3176
|
+
$.noStroke = () => ($._doStroke = false);
|
|
3177
|
+
|
|
3178
|
+
$._strokeWeight = 1;
|
|
3179
|
+
$.strokeWeight = (v) => ($._strokeWeight = Math.abs(v));
|
|
3180
|
+
|
|
3181
|
+
$._calcBox = (x, y, w, h, mode) => {
|
|
3182
|
+
let hw = w / 2;
|
|
3183
|
+
let hh = h / 2;
|
|
3184
|
+
|
|
3185
|
+
// left, right, top, bottom
|
|
3186
|
+
let l, r, t, b;
|
|
3187
|
+
if (!mode || mode == 'corner') {
|
|
3188
|
+
// CORNER
|
|
3189
|
+
l = x;
|
|
3190
|
+
r = x + w;
|
|
3191
|
+
t = -y;
|
|
3192
|
+
b = -(y + h);
|
|
3193
|
+
} else if (mode == 'center') {
|
|
3194
|
+
l = x - hw;
|
|
3195
|
+
r = x + hw;
|
|
3196
|
+
t = -(y - hh);
|
|
3197
|
+
b = -(y + hh);
|
|
3198
|
+
} else {
|
|
3199
|
+
// CORNERS
|
|
3200
|
+
l = x;
|
|
3201
|
+
r = w;
|
|
3202
|
+
t = -y;
|
|
3203
|
+
b = -h;
|
|
3204
|
+
}
|
|
3205
|
+
|
|
3206
|
+
return [l, r, t, b];
|
|
3207
|
+
};
|
|
3208
|
+
|
|
3209
|
+
$.clear = () => {};
|
|
3210
|
+
|
|
3095
3211
|
$._beginRender = () => {
|
|
3096
3212
|
$.encoder = Q5.device.createCommandEncoder();
|
|
3097
3213
|
|
|
3098
3214
|
pass = q.pass = $.encoder.beginRenderPass({
|
|
3215
|
+
label: 'q5-webgpu',
|
|
3099
3216
|
colorAttachments: [
|
|
3100
3217
|
{
|
|
3101
3218
|
view: ctx.getCurrentTexture().createView(),
|
|
@@ -3107,9 +3224,7 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
3107
3224
|
};
|
|
3108
3225
|
|
|
3109
3226
|
$._render = () => {
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
if (transformStates.length > 1 || !transformBindGroup) {
|
|
3227
|
+
if (transformStates.length > 1 || !$._transformBindGroup) {
|
|
3113
3228
|
const transformBuffer = Q5.device.createBuffer({
|
|
3114
3229
|
size: transformStates.length * 64, // Size of 16 floats
|
|
3115
3230
|
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST
|
|
@@ -3117,8 +3232,8 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
3117
3232
|
|
|
3118
3233
|
Q5.device.queue.writeBuffer(transformBuffer, 0, new Float32Array(transformStates.flat()));
|
|
3119
3234
|
|
|
3120
|
-
|
|
3121
|
-
layout: $.
|
|
3235
|
+
$._transformBindGroup = Q5.device.createBindGroup({
|
|
3236
|
+
layout: $._transformLayout,
|
|
3122
3237
|
entries: [
|
|
3123
3238
|
{
|
|
3124
3239
|
binding: 0,
|
|
@@ -3130,27 +3245,39 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
3130
3245
|
});
|
|
3131
3246
|
}
|
|
3132
3247
|
|
|
3133
|
-
pass.setBindGroup(
|
|
3248
|
+
pass.setBindGroup(0, $._envBindGroup);
|
|
3249
|
+
pass.setBindGroup(1, $._transformBindGroup);
|
|
3134
3250
|
|
|
3135
|
-
// run pre-render methods
|
|
3136
3251
|
for (let m of $._hooks.preRender) m();
|
|
3137
3252
|
|
|
3138
|
-
// local variables used for performance
|
|
3139
|
-
let drawStack = $.drawStack;
|
|
3140
|
-
|
|
3141
3253
|
let drawVertOffset = 0;
|
|
3254
|
+
let imageVertOffset = 0;
|
|
3142
3255
|
let curPipelineIndex = -1;
|
|
3256
|
+
let curTextureIndex = -1;
|
|
3257
|
+
|
|
3258
|
+
pass.setPipeline($.pipelines[0]);
|
|
3143
3259
|
|
|
3144
3260
|
for (let i = 0; i < drawStack.length; i += 2) {
|
|
3261
|
+
let v = drawStack[i + 1];
|
|
3262
|
+
|
|
3145
3263
|
if (curPipelineIndex != drawStack[i]) {
|
|
3146
3264
|
curPipelineIndex = drawStack[i];
|
|
3147
3265
|
pass.setPipeline($.pipelines[curPipelineIndex]);
|
|
3148
3266
|
}
|
|
3149
3267
|
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3268
|
+
if (curPipelineIndex == 0) {
|
|
3269
|
+
pass.draw(v, 1, drawVertOffset, 0);
|
|
3270
|
+
drawVertOffset += v;
|
|
3271
|
+
} else if (curPipelineIndex == 1) {
|
|
3272
|
+
if (curTextureIndex != v) {
|
|
3273
|
+
pass.setBindGroup(3, $._textureBindGroups[v]);
|
|
3274
|
+
}
|
|
3275
|
+
pass.draw(6, 1, imageVertOffset, 0);
|
|
3276
|
+
imageVertOffset += 6;
|
|
3277
|
+
}
|
|
3153
3278
|
}
|
|
3279
|
+
|
|
3280
|
+
for (let m of $._hooks.postRender) m();
|
|
3154
3281
|
};
|
|
3155
3282
|
|
|
3156
3283
|
$._finishRender = () => {
|
|
@@ -3160,48 +3287,15 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
3160
3287
|
q.pass = $.encoder = null;
|
|
3161
3288
|
|
|
3162
3289
|
// clear the stacks for the next frame
|
|
3163
|
-
$.verticesStack.length = 0;
|
|
3164
3290
|
$.drawStack.length = 0;
|
|
3165
3291
|
$.colorsStack.length = 4;
|
|
3166
|
-
|
|
3167
|
-
$._colorIndex = 0;
|
|
3292
|
+
colorIndex = 0;
|
|
3168
3293
|
rotation = 0;
|
|
3169
3294
|
$.resetMatrix();
|
|
3170
3295
|
$._matrixDirty = false;
|
|
3171
3296
|
$.transformStates.length = 1;
|
|
3172
3297
|
$._transformIndexStack.length = 0;
|
|
3173
3298
|
};
|
|
3174
|
-
|
|
3175
|
-
function addColor(r, g, b, a = 1) {
|
|
3176
|
-
if (typeof r == 'string') r = Q5.color(r);
|
|
3177
|
-
// grayscale mode `fill(1, 0.5)`
|
|
3178
|
-
if (b == undefined) {
|
|
3179
|
-
a = g;
|
|
3180
|
-
g = b = r;
|
|
3181
|
-
}
|
|
3182
|
-
if (r._q5Color) colorsStack.push(...r.levels);
|
|
3183
|
-
else colorsStack.push(r, g, b, a);
|
|
3184
|
-
$._colorIndex++;
|
|
3185
|
-
}
|
|
3186
|
-
|
|
3187
|
-
$.fill = function () {
|
|
3188
|
-
addColor(...arguments);
|
|
3189
|
-
$._doFill = true;
|
|
3190
|
-
$._fillIndex = $._colorIndex;
|
|
3191
|
-
};
|
|
3192
|
-
$.stroke = function () {
|
|
3193
|
-
addColor(...arguments);
|
|
3194
|
-
$._doStroke = true;
|
|
3195
|
-
$._fillIndex = $._colorIndex;
|
|
3196
|
-
};
|
|
3197
|
-
|
|
3198
|
-
$.noFill = () => ($._doFill = false);
|
|
3199
|
-
$.noStroke = () => ($._doStroke = false);
|
|
3200
|
-
|
|
3201
|
-
$._strokeWeight = 1;
|
|
3202
|
-
$.strokeWeight = (v) => ($._strokeWeight = v);
|
|
3203
|
-
|
|
3204
|
-
$.clear = () => {};
|
|
3205
3299
|
};
|
|
3206
3300
|
|
|
3207
3301
|
Q5.webgpu = async function (scope, parent) {
|
|
@@ -3220,41 +3314,18 @@ Q5.webgpu = async function (scope, parent) {
|
|
|
3220
3314
|
return new Q5(scope, parent, 'webgpu');
|
|
3221
3315
|
};
|
|
3222
3316
|
Q5.renderers.webgpu.drawing = ($, q) => {
|
|
3223
|
-
|
|
3224
|
-
|
|
3225
|
-
let verticesStack, drawStack, colorsStack;
|
|
3226
|
-
|
|
3227
|
-
$._hooks.postCanvas.push(() => {
|
|
3228
|
-
let colorsLayout = Q5.device.createBindGroupLayout({
|
|
3229
|
-
entries: [
|
|
3230
|
-
{
|
|
3231
|
-
binding: 0,
|
|
3232
|
-
visibility: GPUShaderStage.FRAGMENT,
|
|
3233
|
-
buffer: {
|
|
3234
|
-
type: 'read-only-storage',
|
|
3235
|
-
hasDynamicOffset: false
|
|
3236
|
-
}
|
|
3237
|
-
}
|
|
3238
|
-
]
|
|
3239
|
-
});
|
|
3317
|
+
let c = $.canvas;
|
|
3240
3318
|
|
|
3241
|
-
|
|
3319
|
+
let drawStack = $.drawStack;
|
|
3320
|
+
let colorsStack = $.colorsStack;
|
|
3242
3321
|
|
|
3243
|
-
|
|
3244
|
-
drawStack = $.drawStack;
|
|
3245
|
-
colorsStack = $.colorsStack;
|
|
3322
|
+
let verticesStack = [];
|
|
3246
3323
|
|
|
3247
|
-
|
|
3248
|
-
arrayStride: 16, // 2 coordinates + 1 color index + 1 transform index * 4 bytes each
|
|
3249
|
-
attributes: [
|
|
3250
|
-
{ format: 'float32x2', offset: 0, shaderLocation: 0 }, // position
|
|
3251
|
-
{ format: 'float32', offset: 8, shaderLocation: 1 }, // colorIndex
|
|
3252
|
-
{ format: 'float32', offset: 12, shaderLocation: 2 } // transformIndex
|
|
3253
|
-
]
|
|
3254
|
-
};
|
|
3324
|
+
let colorIndex, colorsLayout;
|
|
3255
3325
|
|
|
3256
|
-
|
|
3257
|
-
|
|
3326
|
+
let vertexShader = Q5.device.createShaderModule({
|
|
3327
|
+
label: 'drawingVertexShader',
|
|
3328
|
+
code: `
|
|
3258
3329
|
struct VertexOutput {
|
|
3259
3330
|
@builtin(position) position: vec4<f32>,
|
|
3260
3331
|
@location(1) colorIndex: f32
|
|
@@ -3281,10 +3352,11 @@ fn vertexMain(@location(0) pos: vec2<f32>, @location(1) colorIndex: f32, @locati
|
|
|
3281
3352
|
return output;
|
|
3282
3353
|
}
|
|
3283
3354
|
`
|
|
3284
|
-
|
|
3355
|
+
});
|
|
3285
3356
|
|
|
3286
|
-
|
|
3287
|
-
|
|
3357
|
+
let fragmentShader = Q5.device.createShaderModule({
|
|
3358
|
+
label: 'drawingFragmentShader',
|
|
3359
|
+
code: `
|
|
3288
3360
|
@group(2) @binding(0) var<storage, read> uColors : array<vec4<f32>>;
|
|
3289
3361
|
|
|
3290
3362
|
@fragment
|
|
@@ -3293,31 +3365,31 @@ fn fragmentMain(@location(1) colorIndex: f32) -> @location(0) vec4<f32> {
|
|
|
3293
3365
|
return mix(uColors[index], uColors[index + 1u], fract(colorIndex));
|
|
3294
3366
|
}
|
|
3295
3367
|
`
|
|
3296
|
-
|
|
3368
|
+
});
|
|
3297
3369
|
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
|
|
3370
|
+
colorsLayout = Q5.device.createBindGroupLayout({
|
|
3371
|
+
entries: [
|
|
3372
|
+
{
|
|
3373
|
+
binding: 0,
|
|
3374
|
+
visibility: GPUShaderStage.FRAGMENT,
|
|
3375
|
+
buffer: {
|
|
3376
|
+
type: 'read-only-storage',
|
|
3377
|
+
hasDynamicOffset: false
|
|
3378
|
+
}
|
|
3379
|
+
}
|
|
3380
|
+
]
|
|
3381
|
+
});
|
|
3301
3382
|
|
|
3302
|
-
|
|
3303
|
-
return Q5.device.createRenderPipeline({
|
|
3304
|
-
layout: pipelineLayout,
|
|
3305
|
-
vertex: {
|
|
3306
|
-
module: vertexShader,
|
|
3307
|
-
entryPoint: 'vertexMain',
|
|
3308
|
-
buffers: [vertexBufferLayout]
|
|
3309
|
-
},
|
|
3310
|
-
fragment: {
|
|
3311
|
-
module: fragmentShader,
|
|
3312
|
-
entryPoint: 'fragmentMain',
|
|
3313
|
-
targets: [{ format: 'bgra8unorm', blend: blendConfig }]
|
|
3314
|
-
},
|
|
3315
|
-
primitive: { topology: 'triangle-list' }
|
|
3316
|
-
});
|
|
3317
|
-
};
|
|
3383
|
+
$.bindGroupLayouts.push(colorsLayout);
|
|
3318
3384
|
|
|
3319
|
-
|
|
3320
|
-
|
|
3385
|
+
let vertexBufferLayout = {
|
|
3386
|
+
arrayStride: 16, // 2 coordinates + 1 color index + 1 transform index * 4 bytes each
|
|
3387
|
+
attributes: [
|
|
3388
|
+
{ format: 'float32x2', offset: 0, shaderLocation: 0 }, // position
|
|
3389
|
+
{ format: 'float32', offset: 8, shaderLocation: 1 }, // colorIndex
|
|
3390
|
+
{ format: 'float32', offset: 12, shaderLocation: 2 } // transformIndex
|
|
3391
|
+
]
|
|
3392
|
+
};
|
|
3321
3393
|
|
|
3322
3394
|
// prettier-ignore
|
|
3323
3395
|
let blendFactors = [
|
|
@@ -3362,7 +3434,7 @@ fn fragmentMain(@location(1) colorIndex: f32) -> @location(0) vec4<f32> {
|
|
|
3362
3434
|
|
|
3363
3435
|
$.blendConfigs = {};
|
|
3364
3436
|
|
|
3365
|
-
|
|
3437
|
+
for (const [name, mode] of Object.entries(blendModes)) {
|
|
3366
3438
|
$.blendConfigs[name] = {
|
|
3367
3439
|
color: {
|
|
3368
3440
|
srcFactor: blendFactors[mode[0]],
|
|
@@ -3375,7 +3447,7 @@ fn fragmentMain(@location(1) colorIndex: f32) -> @location(0) vec4<f32> {
|
|
|
3375
3447
|
operation: blendOps[mode[5]]
|
|
3376
3448
|
}
|
|
3377
3449
|
};
|
|
3378
|
-
}
|
|
3450
|
+
}
|
|
3379
3451
|
|
|
3380
3452
|
$._blendMode = 'normal';
|
|
3381
3453
|
$.blendMode = (mode) => {
|
|
@@ -3386,6 +3458,31 @@ fn fragmentMain(@location(1) colorIndex: f32) -> @location(0) vec4<f32> {
|
|
|
3386
3458
|
$.pipelines[0] = $._createPipeline($.blendConfigs[mode]);
|
|
3387
3459
|
};
|
|
3388
3460
|
|
|
3461
|
+
let pipelineLayout = Q5.device.createPipelineLayout({
|
|
3462
|
+
label: 'drawingPipelineLayout',
|
|
3463
|
+
bindGroupLayouts: $.bindGroupLayouts
|
|
3464
|
+
});
|
|
3465
|
+
|
|
3466
|
+
$._createPipeline = (blendConfig) => {
|
|
3467
|
+
return Q5.device.createRenderPipeline({
|
|
3468
|
+
label: 'drawingPipeline',
|
|
3469
|
+
layout: pipelineLayout,
|
|
3470
|
+
vertex: {
|
|
3471
|
+
module: vertexShader,
|
|
3472
|
+
entryPoint: 'vertexMain',
|
|
3473
|
+
buffers: [vertexBufferLayout]
|
|
3474
|
+
},
|
|
3475
|
+
fragment: {
|
|
3476
|
+
module: fragmentShader,
|
|
3477
|
+
entryPoint: 'fragmentMain',
|
|
3478
|
+
targets: [{ format: 'bgra8unorm', blend: blendConfig }]
|
|
3479
|
+
},
|
|
3480
|
+
primitive: { topology: 'triangle-list' }
|
|
3481
|
+
});
|
|
3482
|
+
};
|
|
3483
|
+
|
|
3484
|
+
$.pipelines[0] = $._createPipeline($.blendConfigs.normal);
|
|
3485
|
+
|
|
3389
3486
|
let shapeVertices;
|
|
3390
3487
|
|
|
3391
3488
|
$.beginShape = () => {
|
|
@@ -3394,7 +3491,7 @@ fn fragmentMain(@location(1) colorIndex: f32) -> @location(0) vec4<f32> {
|
|
|
3394
3491
|
|
|
3395
3492
|
$.vertex = (x, y) => {
|
|
3396
3493
|
if ($._matrixDirty) $._saveMatrix();
|
|
3397
|
-
shapeVertices.push(x, -y, $.
|
|
3494
|
+
shapeVertices.push(x, -y, $._fillIndex, $._transformIndex);
|
|
3398
3495
|
};
|
|
3399
3496
|
|
|
3400
3497
|
$.endShape = (close) => {
|
|
@@ -3438,38 +3535,61 @@ fn fragmentMain(@location(1) colorIndex: f32) -> @location(0) vec4<f32> {
|
|
|
3438
3535
|
$.endShape(1);
|
|
3439
3536
|
};
|
|
3440
3537
|
|
|
3441
|
-
$.
|
|
3442
|
-
let hw = w / 2;
|
|
3443
|
-
let hh = h / 2;
|
|
3538
|
+
$.rectMode = (x) => ($._rectMode = x);
|
|
3444
3539
|
|
|
3445
|
-
|
|
3446
|
-
let
|
|
3447
|
-
let top = -(y - hh); // y is inverted in WebGPU
|
|
3448
|
-
let bottom = -(y + hh);
|
|
3540
|
+
$.rect = (x, y, w, h) => {
|
|
3541
|
+
let [l, r, t, b] = $._calcBox(x, y, w, h, $._rectMode);
|
|
3449
3542
|
|
|
3450
|
-
let ci = $.
|
|
3543
|
+
let ci = colorIndex ?? $._fillIndex;
|
|
3451
3544
|
if ($._matrixDirty) $._saveMatrix();
|
|
3452
3545
|
let ti = $._transformIndex;
|
|
3453
3546
|
// two triangles make a rectangle
|
|
3454
3547
|
// prettier-ignore
|
|
3455
3548
|
verticesStack.push(
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3549
|
+
l, t, ci, ti,
|
|
3550
|
+
r, t, ci, ti,
|
|
3551
|
+
l, b, ci, ti,
|
|
3552
|
+
r, t, ci, ti,
|
|
3553
|
+
l, b, ci, ti,
|
|
3554
|
+
r, b, ci, ti
|
|
3462
3555
|
);
|
|
3463
3556
|
drawStack.push(0, 6);
|
|
3464
3557
|
};
|
|
3465
3558
|
|
|
3466
3559
|
$.point = (x, y) => {
|
|
3560
|
+
colorIndex = $._strokeIndex;
|
|
3467
3561
|
let sw = $._strokeWeight;
|
|
3468
|
-
if (sw
|
|
3469
|
-
|
|
3562
|
+
if (sw < 2) {
|
|
3563
|
+
sw = Math.round(sw);
|
|
3564
|
+
$.rect(x, y, sw, sw);
|
|
3565
|
+
} else $.ellipse(x, y, sw, sw);
|
|
3566
|
+
colorIndex = null;
|
|
3470
3567
|
};
|
|
3471
3568
|
|
|
3472
|
-
$.
|
|
3569
|
+
$.line = (x1, y1, x2, y2) => {
|
|
3570
|
+
colorIndex = $._strokeIndex;
|
|
3571
|
+
|
|
3572
|
+
$.push();
|
|
3573
|
+
$.translate(x1, y1);
|
|
3574
|
+
$.rotate($.atan2(y2 - y1, x2 - x1));
|
|
3575
|
+
let length = Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2);
|
|
3576
|
+
let sw = $._strokeWeight;
|
|
3577
|
+
$.rect(0, -sw / 2, length, sw);
|
|
3578
|
+
$.pop();
|
|
3579
|
+
|
|
3580
|
+
colorIndex = null;
|
|
3581
|
+
};
|
|
3582
|
+
|
|
3583
|
+
$.background = (r, g, b, a) => {
|
|
3584
|
+
$.push();
|
|
3585
|
+
$.resetMatrix();
|
|
3586
|
+
if (r.src) $.image(r, -c.hw, -c.hh, c.w, c.h);
|
|
3587
|
+
else {
|
|
3588
|
+
$.fill(r, g, b, a);
|
|
3589
|
+
$.rect(-c.hw, -c.hh, c.w, c.h);
|
|
3590
|
+
}
|
|
3591
|
+
$.pop();
|
|
3592
|
+
};
|
|
3473
3593
|
|
|
3474
3594
|
/**
|
|
3475
3595
|
* Derived from: ceil(Math.log(d) * 7) * 2 - ceil(28)
|
|
@@ -3479,9 +3599,10 @@ fn fragmentMain(@location(1) colorIndex: f32) -> @location(0) vec4<f32> {
|
|
|
3479
3599
|
*/
|
|
3480
3600
|
// prettier-ignore
|
|
3481
3601
|
const getArcSegments = (d) =>
|
|
3482
|
-
|
|
3483
|
-
d <
|
|
3484
|
-
d <
|
|
3602
|
+
d < 4 ? 6 :
|
|
3603
|
+
d < 6 ? 8 :
|
|
3604
|
+
d < 10 ? 10 :
|
|
3605
|
+
d < 16 ? 12 :
|
|
3485
3606
|
d < 20 ? 14 :
|
|
3486
3607
|
d < 22 ? 16 :
|
|
3487
3608
|
d < 24 ? 18 :
|
|
@@ -3515,7 +3636,7 @@ fn fragmentMain(@location(1) colorIndex: f32) -> @location(0) vec4<f32> {
|
|
|
3515
3636
|
|
|
3516
3637
|
let t = 0; // theta
|
|
3517
3638
|
const angleIncrement = $.TAU / n;
|
|
3518
|
-
const ci = $.
|
|
3639
|
+
const ci = colorIndex ?? $._fillIndex;
|
|
3519
3640
|
if ($._matrixDirty) $._saveMatrix();
|
|
3520
3641
|
const ti = $._transformIndex;
|
|
3521
3642
|
let vx1, vy1, vx2, vy2;
|
|
@@ -3537,12 +3658,16 @@ fn fragmentMain(@location(1) colorIndex: f32) -> @location(0) vec4<f32> {
|
|
|
3537
3658
|
$.circle = (x, y, d) => $.ellipse(x, y, d, d);
|
|
3538
3659
|
|
|
3539
3660
|
$._hooks.preRender.push(() => {
|
|
3661
|
+
$.pass.setPipeline($.pipelines[0]);
|
|
3662
|
+
|
|
3663
|
+
const vertices = new Float32Array(verticesStack);
|
|
3664
|
+
|
|
3540
3665
|
const vertexBuffer = Q5.device.createBuffer({
|
|
3541
|
-
size:
|
|
3666
|
+
size: vertices.byteLength,
|
|
3542
3667
|
usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST
|
|
3543
3668
|
});
|
|
3544
3669
|
|
|
3545
|
-
Q5.device.queue.writeBuffer(vertexBuffer, 0,
|
|
3670
|
+
Q5.device.queue.writeBuffer(vertexBuffer, 0, vertices);
|
|
3546
3671
|
$.pass.setVertexBuffer(0, vertexBuffer);
|
|
3547
3672
|
|
|
3548
3673
|
const colorsBuffer = Q5.device.createBuffer({
|
|
@@ -3552,8 +3677,8 @@ fn fragmentMain(@location(1) colorIndex: f32) -> @location(0) vec4<f32> {
|
|
|
3552
3677
|
|
|
3553
3678
|
Q5.device.queue.writeBuffer(colorsBuffer, 0, new Float32Array(colorsStack));
|
|
3554
3679
|
|
|
3555
|
-
|
|
3556
|
-
layout:
|
|
3680
|
+
$._colorsBindGroup = Q5.device.createBindGroup({
|
|
3681
|
+
layout: colorsLayout,
|
|
3557
3682
|
entries: [
|
|
3558
3683
|
{
|
|
3559
3684
|
binding: 0,
|
|
@@ -3567,136 +3692,206 @@ fn fragmentMain(@location(1) colorIndex: f32) -> @location(0) vec4<f32> {
|
|
|
3567
3692
|
});
|
|
3568
3693
|
|
|
3569
3694
|
// set the bind group once before rendering
|
|
3570
|
-
$.pass.setBindGroup(2,
|
|
3695
|
+
$.pass.setBindGroup(2, $._colorsBindGroup);
|
|
3696
|
+
});
|
|
3697
|
+
|
|
3698
|
+
$._hooks.postRender.push(() => {
|
|
3699
|
+
verticesStack.length = 0;
|
|
3571
3700
|
});
|
|
3572
3701
|
};
|
|
3573
3702
|
Q5.renderers.webgpu.image = ($, q) => {
|
|
3574
|
-
$.
|
|
3575
|
-
|
|
3703
|
+
$._textureBindGroups = [];
|
|
3704
|
+
let verticesStack = [];
|
|
3576
3705
|
|
|
3577
|
-
|
|
3578
|
-
|
|
3579
|
-
|
|
3706
|
+
let vertexShader = Q5.device.createShaderModule({
|
|
3707
|
+
label: 'imageVertexShader',
|
|
3708
|
+
code: `
|
|
3580
3709
|
struct VertexOutput {
|
|
3581
|
-
|
|
3582
|
-
|
|
3583
|
-
@location(1) textureIndex: f32
|
|
3710
|
+
@builtin(position) position: vec4<f32>,
|
|
3711
|
+
@location(0) texCoord: vec2<f32>
|
|
3584
3712
|
};
|
|
3585
3713
|
|
|
3586
3714
|
struct Uniforms {
|
|
3587
|
-
|
|
3588
|
-
|
|
3715
|
+
halfWidth: f32,
|
|
3716
|
+
halfHeight: f32
|
|
3589
3717
|
};
|
|
3590
3718
|
|
|
3591
3719
|
@group(0) @binding(0) var<uniform> uniforms: Uniforms;
|
|
3592
3720
|
@group(1) @binding(0) var<storage, read> transforms: array<mat4x4<f32>>;
|
|
3593
3721
|
|
|
3594
3722
|
@vertex
|
|
3595
|
-
fn vertexMain(@location(0) pos: vec2<f32>, @location(1) texCoord: vec2<f32>, @location(2) transformIndex: f32
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
|
|
3604
|
-
|
|
3605
|
-
return output;
|
|
3723
|
+
fn vertexMain(@location(0) pos: vec2<f32>, @location(1) texCoord: vec2<f32>, @location(2) transformIndex: f32) -> VertexOutput {
|
|
3724
|
+
var vert = vec4<f32>(pos, 0.0, 1.0);
|
|
3725
|
+
vert *= transforms[i32(transformIndex)];
|
|
3726
|
+
vert.x /= uniforms.halfWidth;
|
|
3727
|
+
vert.y /= uniforms.halfHeight;
|
|
3728
|
+
|
|
3729
|
+
var output: VertexOutput;
|
|
3730
|
+
output.position = vert;
|
|
3731
|
+
output.texCoord = texCoord;
|
|
3732
|
+
return output;
|
|
3606
3733
|
}
|
|
3607
|
-
`
|
|
3608
|
-
|
|
3734
|
+
`
|
|
3735
|
+
});
|
|
3609
3736
|
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
@group(
|
|
3737
|
+
let fragmentShader = Q5.device.createShaderModule({
|
|
3738
|
+
label: 'imageFragmentShader',
|
|
3739
|
+
code: `
|
|
3740
|
+
@group(3) @binding(0) var samp: sampler;
|
|
3741
|
+
@group(3) @binding(1) var texture: texture_2d<f32>;
|
|
3614
3742
|
|
|
3615
3743
|
@fragment
|
|
3616
|
-
fn fragmentMain(@location(0) texCoord: vec2<f32
|
|
3617
|
-
|
|
3744
|
+
fn fragmentMain(@location(0) texCoord: vec2<f32>) -> @location(0) vec4<f32> {
|
|
3745
|
+
// Sample the texture using the interpolated texture coordinate
|
|
3746
|
+
return textureSample(texture, samp, texCoord);
|
|
3618
3747
|
}
|
|
3619
|
-
`
|
|
3620
|
-
|
|
3748
|
+
`
|
|
3749
|
+
});
|
|
3621
3750
|
|
|
3622
|
-
|
|
3623
|
-
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
|
|
3629
|
-
}
|
|
3630
|
-
|
|
3631
|
-
|
|
3632
|
-
|
|
3633
|
-
|
|
3751
|
+
let textureLayout = Q5.device.createBindGroupLayout({
|
|
3752
|
+
label: 'textureLayout',
|
|
3753
|
+
entries: [
|
|
3754
|
+
{
|
|
3755
|
+
binding: 0,
|
|
3756
|
+
visibility: GPUShaderStage.FRAGMENT,
|
|
3757
|
+
sampler: { type: 'filtering' }
|
|
3758
|
+
},
|
|
3759
|
+
{
|
|
3760
|
+
binding: 1,
|
|
3761
|
+
visibility: GPUShaderStage.FRAGMENT,
|
|
3762
|
+
texture: { viewDimension: '2d', sampleType: 'float' }
|
|
3763
|
+
}
|
|
3764
|
+
]
|
|
3765
|
+
});
|
|
3634
3766
|
|
|
3635
|
-
|
|
3636
|
-
|
|
3637
|
-
|
|
3767
|
+
const vertexBufferLayout = {
|
|
3768
|
+
arrayStride: 20,
|
|
3769
|
+
attributes: [
|
|
3770
|
+
{ shaderLocation: 0, offset: 0, format: 'float32x2' },
|
|
3771
|
+
{ shaderLocation: 1, offset: 8, format: 'float32x2' },
|
|
3772
|
+
{ shaderLocation: 2, offset: 16, format: 'float32' } // transformIndex
|
|
3773
|
+
]
|
|
3774
|
+
};
|
|
3638
3775
|
|
|
3639
|
-
|
|
3640
|
-
|
|
3641
|
-
|
|
3642
|
-
|
|
3643
|
-
|
|
3644
|
-
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
|
|
3776
|
+
$.bindGroupLayouts.push(textureLayout);
|
|
3777
|
+
|
|
3778
|
+
const pipelineLayout = Q5.device.createPipelineLayout({
|
|
3779
|
+
label: 'imagePipelineLayout',
|
|
3780
|
+
bindGroupLayouts: $.bindGroupLayouts
|
|
3781
|
+
});
|
|
3782
|
+
|
|
3783
|
+
$.pipelines[1] = Q5.device.createRenderPipeline({
|
|
3784
|
+
label: 'imagePipeline',
|
|
3785
|
+
layout: pipelineLayout,
|
|
3786
|
+
vertex: {
|
|
3787
|
+
module: vertexShader,
|
|
3788
|
+
entryPoint: 'vertexMain',
|
|
3789
|
+
buffers: [{ arrayStride: 0, attributes: [] }, vertexBufferLayout]
|
|
3790
|
+
},
|
|
3791
|
+
fragment: {
|
|
3792
|
+
module: fragmentShader,
|
|
3793
|
+
entryPoint: 'fragmentMain',
|
|
3794
|
+
targets: [
|
|
3795
|
+
{
|
|
3796
|
+
format: 'bgra8unorm',
|
|
3797
|
+
blend: $.blendConfigs?.normal || {
|
|
3798
|
+
color: {
|
|
3799
|
+
srcFactor: 'src-alpha',
|
|
3800
|
+
dstFactor: 'one-minus-src-alpha',
|
|
3801
|
+
operation: 'add'
|
|
3802
|
+
},
|
|
3803
|
+
alpha: {
|
|
3804
|
+
srcFactor: 'src-alpha',
|
|
3805
|
+
dstFactor: 'one-minus-src-alpha',
|
|
3806
|
+
operation: 'add'
|
|
3807
|
+
}
|
|
3652
3808
|
}
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
|
|
3656
|
-
|
|
3657
|
-
|
|
3658
|
-
|
|
3659
|
-
|
|
3660
|
-
primitive: {
|
|
3661
|
-
topology: 'triangle-list'
|
|
3662
|
-
}
|
|
3663
|
-
});
|
|
3809
|
+
}
|
|
3810
|
+
]
|
|
3811
|
+
},
|
|
3812
|
+
primitive: {
|
|
3813
|
+
topology: 'triangle-list'
|
|
3814
|
+
}
|
|
3815
|
+
});
|
|
3664
3816
|
|
|
3665
|
-
|
|
3666
|
-
|
|
3667
|
-
|
|
3668
|
-
});
|
|
3817
|
+
let sampler = Q5.device.createSampler({
|
|
3818
|
+
magFilter: 'linear',
|
|
3819
|
+
minFilter: 'linear'
|
|
3669
3820
|
});
|
|
3670
3821
|
|
|
3671
|
-
$.
|
|
3672
|
-
|
|
3673
|
-
|
|
3674
|
-
|
|
3675
|
-
|
|
3676
|
-
|
|
3677
|
-
|
|
3678
|
-
|
|
3679
|
-
});
|
|
3822
|
+
$._createTexture = (img) => {
|
|
3823
|
+
let textureSize = [img.width, img.height, 1];
|
|
3824
|
+
|
|
3825
|
+
const texture = Q5.device.createTexture({
|
|
3826
|
+
size: textureSize,
|
|
3827
|
+
format: 'bgra8unorm',
|
|
3828
|
+
usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.RENDER_ATTACHMENT
|
|
3829
|
+
});
|
|
3680
3830
|
|
|
3681
|
-
|
|
3831
|
+
Q5.device.queue.copyExternalImageToTexture({ source: img }, { texture }, textureSize);
|
|
3682
3832
|
|
|
3683
|
-
|
|
3684
|
-
|
|
3685
|
-
|
|
3833
|
+
img.textureIndex = $._textureBindGroups.length;
|
|
3834
|
+
|
|
3835
|
+
const textureBindGroup = Q5.device.createBindGroup({
|
|
3836
|
+
layout: textureLayout,
|
|
3837
|
+
entries: [
|
|
3838
|
+
{ binding: 0, resource: sampler },
|
|
3839
|
+
{ binding: 1, resource: texture.createView() }
|
|
3840
|
+
]
|
|
3841
|
+
});
|
|
3842
|
+
$._textureBindGroups.push(textureBindGroup);
|
|
3843
|
+
};
|
|
3844
|
+
|
|
3845
|
+
$.loadImage = $.loadTexture = (src) => {
|
|
3846
|
+
q._preloadCount++;
|
|
3847
|
+
const img = new Image();
|
|
3848
|
+
img.crossOrigin = 'Anonymous';
|
|
3849
|
+
img.onload = () => {
|
|
3850
|
+
$._createTexture(img);
|
|
3851
|
+
q._preloadCount--;
|
|
3686
3852
|
};
|
|
3687
|
-
img.onerror = reject;
|
|
3688
3853
|
img.src = src;
|
|
3689
3854
|
return img;
|
|
3690
3855
|
};
|
|
3691
3856
|
|
|
3857
|
+
$.imageMode = (x) => ($._imageMode = x);
|
|
3858
|
+
|
|
3859
|
+
$.image = (img, x, y, w, h) => {
|
|
3860
|
+
if (img.canvas) img = img.canvas;
|
|
3861
|
+
if (img.textureIndex == undefined) return;
|
|
3862
|
+
|
|
3863
|
+
if ($._matrixDirty) $._saveMatrix();
|
|
3864
|
+
let ti = $._transformIndex;
|
|
3865
|
+
|
|
3866
|
+
w ??= img.width;
|
|
3867
|
+
h ??= img.height;
|
|
3868
|
+
|
|
3869
|
+
w /= $._pixelDensity;
|
|
3870
|
+
h /= $._pixelDensity;
|
|
3871
|
+
|
|
3872
|
+
let [l, r, t, b] = $._calcBox(x, y, w, h, $._imageMode);
|
|
3873
|
+
|
|
3874
|
+
// prettier-ignore
|
|
3875
|
+
verticesStack.push(
|
|
3876
|
+
l, t, 0, 0, ti,
|
|
3877
|
+
r, t, 1, 0, ti,
|
|
3878
|
+
l, b, 0, 1, ti,
|
|
3879
|
+
r, t, 1, 0, ti,
|
|
3880
|
+
l, b, 0, 1, ti,
|
|
3881
|
+
r, b, 1, 1, ti
|
|
3882
|
+
);
|
|
3883
|
+
|
|
3884
|
+
$.drawStack.push(1, img.textureIndex);
|
|
3885
|
+
};
|
|
3886
|
+
|
|
3692
3887
|
$._hooks.preRender.push(() => {
|
|
3693
|
-
if (!$.
|
|
3888
|
+
if (!$._textureBindGroups.length) return;
|
|
3694
3889
|
|
|
3695
3890
|
// Switch to image pipeline
|
|
3696
3891
|
$.pass.setPipeline($.pipelines[1]);
|
|
3697
3892
|
|
|
3698
3893
|
// Create a vertex buffer for the image quads
|
|
3699
|
-
const vertices = new Float32Array(
|
|
3894
|
+
const vertices = new Float32Array(verticesStack);
|
|
3700
3895
|
|
|
3701
3896
|
const vertexBuffer = Q5.device.createBuffer({
|
|
3702
3897
|
size: vertices.byteLength,
|
|
@@ -3704,57 +3899,48 @@ fn fragmentMain(@location(0) texCoord: vec2<f32>, @location(1) textureIndex: f32
|
|
|
3704
3899
|
});
|
|
3705
3900
|
|
|
3706
3901
|
Q5.device.queue.writeBuffer(vertexBuffer, 0, vertices);
|
|
3707
|
-
$.pass.setVertexBuffer(
|
|
3708
|
-
|
|
3709
|
-
// Set the bind group for the sampler and textures
|
|
3710
|
-
if ($.textures.length !== previousTextureCount) {
|
|
3711
|
-
previousTextureCount = $.textures.length;
|
|
3712
|
-
|
|
3713
|
-
// Create the bind group for all textures
|
|
3714
|
-
const textureViews = $.textures.map((tex) => tex.createView());
|
|
3715
|
-
|
|
3716
|
-
$.textureBindGroup = Q5.device.createBindGroup({
|
|
3717
|
-
layout: $.pipelines[1].getBindGroupLayout(0),
|
|
3718
|
-
entries: [
|
|
3719
|
-
{ binding: 0, resource: $.sampler },
|
|
3720
|
-
...textureViews.map((view, i) => ({ binding: i + 1, resource: view }))
|
|
3721
|
-
]
|
|
3722
|
-
});
|
|
3723
|
-
}
|
|
3724
|
-
|
|
3725
|
-
// Set the bind group for the sampler and textures
|
|
3726
|
-
$.pass.setBindGroup(0, $.textureBindGroup);
|
|
3902
|
+
$.pass.setVertexBuffer(1, vertexBuffer);
|
|
3727
3903
|
});
|
|
3728
3904
|
|
|
3729
|
-
$.
|
|
3730
|
-
|
|
3731
|
-
|
|
3732
|
-
|
|
3733
|
-
|
|
3734
|
-
|
|
3735
|
-
// Calculate half-width and half-height
|
|
3736
|
-
let hw = w / 2;
|
|
3737
|
-
let hh = h / 2;
|
|
3738
|
-
|
|
3739
|
-
// Calculate vertices positions
|
|
3740
|
-
let left = x - hw;
|
|
3741
|
-
let right = x + hw;
|
|
3742
|
-
let top = -(y - hh); // y is inverted in WebGPU
|
|
3743
|
-
let bottom = -(y + hh);
|
|
3905
|
+
$._hooks.postRender.push(() => {
|
|
3906
|
+
verticesStack.length = 0;
|
|
3907
|
+
});
|
|
3908
|
+
};
|
|
3909
|
+
Q5.renderers.webgpu.text = ($, q) => {
|
|
3910
|
+
let t = $.createGraphics(1, 1);
|
|
3744
3911
|
|
|
3745
|
-
|
|
3912
|
+
$.loadFont = (f) => {
|
|
3913
|
+
q._preloadCount++;
|
|
3914
|
+
return t.loadFont(f, () => {
|
|
3915
|
+
q._preloadCount--;
|
|
3916
|
+
});
|
|
3917
|
+
};
|
|
3918
|
+
$.textFont = t.textFont;
|
|
3919
|
+
$.textSize = t.textSize;
|
|
3920
|
+
$.textLeading = t.textLeading;
|
|
3921
|
+
$.textStyle = t.textStyle;
|
|
3922
|
+
$.textAlign = t.textAlign;
|
|
3923
|
+
$.textWidth = t.textWidth;
|
|
3924
|
+
$.textAscent = t.textAscent;
|
|
3925
|
+
$.textDescent = t.textDescent;
|
|
3746
3926
|
|
|
3747
|
-
|
|
3748
|
-
|
|
3749
|
-
left, top, 0, 0, ti, ii,
|
|
3750
|
-
right, top, 1, 0, ti, ii,
|
|
3751
|
-
left, bottom, 0, 1, ti, ii,
|
|
3752
|
-
right, top, 1, 0, ti, ii,
|
|
3753
|
-
left, bottom, 0, 1, ti, ii,
|
|
3754
|
-
right, bottom, 1, 1, ti, ii
|
|
3755
|
-
);
|
|
3927
|
+
$.textFill = (r, g, b, a) => t.fill($.color(r, g, b, a));
|
|
3928
|
+
$.textStroke = (r, g, b, a) => t.stroke($.color(r, g, b, a));
|
|
3756
3929
|
|
|
3757
|
-
|
|
3930
|
+
$.text = (str, x, y, w, h) => {
|
|
3931
|
+
let img = t.createTextImage(str, w, h);
|
|
3932
|
+
|
|
3933
|
+
if (img.textureIndex == undefined) $._createTexture(img);
|
|
3934
|
+
|
|
3935
|
+
let og = t._imageMode;
|
|
3936
|
+
t._imageMode = 'corner';
|
|
3937
|
+
if (t.ctx.textAlign == 'center') x -= img.width * 0.5;
|
|
3938
|
+
else if (t.ctx.textAlign == 'right') x -= img.width;
|
|
3939
|
+
if (t.ctx.textBaseline == 'alphabetic') y -= t._textLeading;
|
|
3940
|
+
if (t.ctx.textBaseline == 'middle') y -= img._descent + img._ascent * 0.5 + t._textLeadDiff;
|
|
3941
|
+
else if (t.ctx.textBaseline == 'bottom') y -= img._ascent + img._descent + t._textLeadDiff;
|
|
3942
|
+
else if (t.ctx.textBaseline == 'top') y -= img._descent + t._textLeadDiff;
|
|
3943
|
+
$.image(img, x, y);
|
|
3944
|
+
t._imageMode = og;
|
|
3758
3945
|
};
|
|
3759
3946
|
};
|
|
3760
|
-
Q5.renderers.webgpu.text = ($, q) => {};
|