q5 2.5.5 → 2.7.0
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/package.json +3 -4
- package/q5.d.ts +697 -564
- package/q5.js +641 -542
- package/q5.min.js +2 -2
- package/src/q5-2d-canvas.js +2 -4
- package/src/q5-2d-drawing.js +1 -1
- package/src/q5-2d-image.js +2 -1
- package/src/q5-ai.js +2 -3
- package/src/q5-canvas.js +7 -0
- package/src/q5-core.js +28 -18
- package/src/q5-input.js +3 -3
- package/src/q5-webgpu-canvas.js +176 -44
- package/src/q5-webgpu-drawing.js +298 -291
- package/src/q5-webgpu-image.js +25 -38
- package/src/q5-webgpu-text.js +95 -137
- package/src/readme.md +8 -18
package/q5.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* q5.js
|
|
3
|
-
* @version 2.
|
|
3
|
+
* @version 2.7
|
|
4
4
|
* @author quinton-ashley, Tezumie, and LingDong-
|
|
5
5
|
* @license LGPL-3.0
|
|
6
6
|
* @class Q5
|
|
@@ -89,7 +89,13 @@ function Q5(scope, parent, renderer) {
|
|
|
89
89
|
$.resetMatrix();
|
|
90
90
|
if ($._beginRender) $._beginRender();
|
|
91
91
|
for (let m of Q5.methods.pre) m.call($);
|
|
92
|
-
|
|
92
|
+
try {
|
|
93
|
+
$.draw();
|
|
94
|
+
} catch (e) {
|
|
95
|
+
if (!Q5.disableFriendlyErrors && $._askAI) $._askAI(e);
|
|
96
|
+
if (!Q5.errorTolerant) $.noLoop();
|
|
97
|
+
throw e;
|
|
98
|
+
}
|
|
93
99
|
for (let m of Q5.methods.post) m.call($);
|
|
94
100
|
if ($._render) $._render();
|
|
95
101
|
if ($._finishRender) $._finishRender();
|
|
@@ -128,7 +134,7 @@ function Q5(scope, parent, renderer) {
|
|
|
128
134
|
}
|
|
129
135
|
return $._frameRate;
|
|
130
136
|
};
|
|
131
|
-
$.getTargetFrameRate = () => $._targetFrameRate;
|
|
137
|
+
$.getTargetFrameRate = () => $._targetFrameRate || 60;
|
|
132
138
|
$.getFPS = () => $._fps;
|
|
133
139
|
|
|
134
140
|
$.Element = function (a) {
|
|
@@ -196,10 +202,10 @@ function Q5(scope, parent, renderer) {
|
|
|
196
202
|
let t = globalScope || $;
|
|
197
203
|
$._isTouchAware = t.touchStarted || t.touchMoved || t.mouseReleased;
|
|
198
204
|
let preloadDefined = t.preload;
|
|
205
|
+
$.preload ??= () => {};
|
|
206
|
+
$.setup ??= () => {};
|
|
207
|
+
$.draw ??= () => {};
|
|
199
208
|
let userFns = [
|
|
200
|
-
'setup',
|
|
201
|
-
'draw',
|
|
202
|
-
'preload',
|
|
203
209
|
'mouseMoved',
|
|
204
210
|
'mousePressed',
|
|
205
211
|
'mouseReleased',
|
|
@@ -220,15 +226,13 @@ function Q5(scope, parent, renderer) {
|
|
|
220
226
|
try {
|
|
221
227
|
return t[k]();
|
|
222
228
|
} catch (e) {
|
|
223
|
-
if ($.
|
|
229
|
+
if ($._askAI) $._askAI(e);
|
|
224
230
|
throw e;
|
|
225
231
|
}
|
|
226
232
|
};
|
|
227
233
|
}
|
|
228
234
|
}
|
|
229
235
|
|
|
230
|
-
if (!($.setup || $.draw)) return;
|
|
231
|
-
|
|
232
236
|
async function _start() {
|
|
233
237
|
$._startDone = true;
|
|
234
238
|
if ($._preloadCount > 0) return raf(_start);
|
|
@@ -236,19 +240,25 @@ function Q5(scope, parent, renderer) {
|
|
|
236
240
|
await $.setup();
|
|
237
241
|
$._setupDone = true;
|
|
238
242
|
if ($.frameCount) return;
|
|
239
|
-
if ($.ctx === null) $.createCanvas(
|
|
243
|
+
if ($.ctx === null) $.createCanvas(200, 200);
|
|
240
244
|
if ($.ctx) $.resetMatrix();
|
|
241
245
|
raf($._draw);
|
|
242
246
|
}
|
|
243
247
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
} else {
|
|
248
|
-
t.preload = $.preload = () => {
|
|
248
|
+
function _preStart() {
|
|
249
|
+
try {
|
|
250
|
+
$.preload();
|
|
249
251
|
if (!$._startDone) _start();
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
+
} catch (e) {
|
|
253
|
+
if ($._askAI) $._askAI(e);
|
|
254
|
+
throw e;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
if (preloadDefined || (arguments.length && scope != 'instance' && renderer != 'webgpu')) {
|
|
259
|
+
_preStart();
|
|
260
|
+
} else {
|
|
261
|
+
setTimeout(_preStart, 32);
|
|
252
262
|
}
|
|
253
263
|
}
|
|
254
264
|
|
|
@@ -259,7 +269,7 @@ Q5._nodejs = typeof process == 'object';
|
|
|
259
269
|
|
|
260
270
|
Q5._instanceCount = 0;
|
|
261
271
|
Q5._friendlyError = (msg, func) => {
|
|
262
|
-
|
|
272
|
+
if (!Q5.disableFriendlyErrors) console.error(func + ': ' + msg);
|
|
263
273
|
};
|
|
264
274
|
Q5._validateParameters = () => true;
|
|
265
275
|
|
|
@@ -386,8 +396,15 @@ Q5.modules.canvas = ($, q) => {
|
|
|
386
396
|
if ($._scope != 'image') {
|
|
387
397
|
if ($._scope == 'graphics') $._pixelDensity = this._pixelDensity;
|
|
388
398
|
else if (window.IntersectionObserver) {
|
|
399
|
+
$._wasLooping = $._loop;
|
|
389
400
|
new IntersectionObserver((e) => {
|
|
390
401
|
c.visible = e[0].isIntersecting;
|
|
402
|
+
if (c.visible) {
|
|
403
|
+
if ($._wasLooping && !$._loop) $.loop();
|
|
404
|
+
} else {
|
|
405
|
+
$._wasLooping = $._loop;
|
|
406
|
+
$.noLoop();
|
|
407
|
+
}
|
|
391
408
|
}).observe(c);
|
|
392
409
|
}
|
|
393
410
|
}
|
|
@@ -635,8 +652,7 @@ Q5.renderers.q2d.canvas = ($, q) => {
|
|
|
635
652
|
};
|
|
636
653
|
|
|
637
654
|
$.fill = function (c) {
|
|
638
|
-
$._doFill = true;
|
|
639
|
-
$._fillSet = true;
|
|
655
|
+
$._doFill = $._fillSet = true;
|
|
640
656
|
if (Q5.Color) {
|
|
641
657
|
if (!c._q5Color) {
|
|
642
658
|
if (typeof c != 'string') c = $.color(...arguments);
|
|
@@ -648,8 +664,7 @@ Q5.renderers.q2d.canvas = ($, q) => {
|
|
|
648
664
|
};
|
|
649
665
|
$.noFill = () => ($._doFill = false);
|
|
650
666
|
$.stroke = function (c) {
|
|
651
|
-
$._doStroke = true;
|
|
652
|
-
$._strokeSet = true;
|
|
667
|
+
$._doStroke = $._strokeSet = true;
|
|
653
668
|
if (Q5.Color) {
|
|
654
669
|
if (!c._q5Color) {
|
|
655
670
|
if (typeof c != 'string') c = $.color(...arguments);
|
|
@@ -753,7 +768,7 @@ Q5.renderers.q2d.drawing = ($) => {
|
|
|
753
768
|
$.background = function (c) {
|
|
754
769
|
$.ctx.save();
|
|
755
770
|
$.ctx.resetTransform();
|
|
756
|
-
if (c.canvas) $.image(c, 0, 0, $.width, $.height);
|
|
771
|
+
if (c.canvas) $.image(c, 0, 0, $.canvas.width, $.canvas.height);
|
|
757
772
|
else {
|
|
758
773
|
if (Q5.Color && !c._q5Color) {
|
|
759
774
|
if (typeof c != 'string') c = $.color(...arguments);
|
|
@@ -1226,7 +1241,8 @@ Q5.renderers.q2d.image = ($, q) => {
|
|
|
1226
1241
|
|
|
1227
1242
|
$.imageMode = (mode) => ($._imageMode = mode);
|
|
1228
1243
|
$.image = (img, dx, dy, dw, dh, sx = 0, sy = 0, sw, sh) => {
|
|
1229
|
-
|
|
1244
|
+
if (!img) return;
|
|
1245
|
+
let drawable = img?.canvas || img;
|
|
1230
1246
|
if (Q5._createNodeJSCanvas) {
|
|
1231
1247
|
drawable = drawable.context.canvas;
|
|
1232
1248
|
}
|
|
@@ -1680,13 +1696,12 @@ Q5.renderers.q2d.text = ($, q) => {
|
|
|
1680
1696
|
};
|
|
1681
1697
|
Q5.modules.ai = ($) => {
|
|
1682
1698
|
$.askAI = (question = '') => {
|
|
1699
|
+
Q5.disableFriendlyErrors = false;
|
|
1683
1700
|
throw Error('Ask AI ✨ ' + question);
|
|
1684
1701
|
};
|
|
1685
1702
|
|
|
1686
|
-
$.
|
|
1703
|
+
$._askAI = async (e) => {
|
|
1687
1704
|
let askAI = e.message?.includes('Ask AI ✨');
|
|
1688
|
-
if (Q5.disableFriendlyErrors && !askAI) return;
|
|
1689
|
-
if (askAI || !Q5.errorTolerant) $.noLoop();
|
|
1690
1705
|
let stackLines = e.stack?.split('\n');
|
|
1691
1706
|
if (!e.stack || stackLines.length <= 1) return;
|
|
1692
1707
|
|
|
@@ -2108,11 +2123,11 @@ Q5.modules.input = ($, q) => {
|
|
|
2108
2123
|
$.pmouseX = 0;
|
|
2109
2124
|
$.pmouseY = 0;
|
|
2110
2125
|
$.touches = [];
|
|
2111
|
-
$.mouseButton =
|
|
2126
|
+
$.mouseButton = '';
|
|
2112
2127
|
$.keyIsPressed = false;
|
|
2113
2128
|
$.mouseIsPressed = false;
|
|
2114
|
-
$.key =
|
|
2115
|
-
$.keyCode =
|
|
2129
|
+
$.key = '';
|
|
2130
|
+
$.keyCode = 0;
|
|
2116
2131
|
|
|
2117
2132
|
$.UP_ARROW = 38;
|
|
2118
2133
|
$.DOWN_ARROW = 40;
|
|
@@ -3075,18 +3090,30 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
3075
3090
|
c.width = $.width = 500;
|
|
3076
3091
|
c.height = $.height = 500;
|
|
3077
3092
|
|
|
3078
|
-
if ($.colorMode) $.colorMode('rgb',
|
|
3093
|
+
if ($.colorMode) $.colorMode('rgb', 1);
|
|
3079
3094
|
|
|
3080
|
-
let pass
|
|
3095
|
+
let pass,
|
|
3096
|
+
mainView,
|
|
3097
|
+
colorsLayout,
|
|
3098
|
+
colorIndex = 1,
|
|
3099
|
+
colorStackIndex = 8;
|
|
3081
3100
|
|
|
3082
|
-
$.
|
|
3101
|
+
$._pipelineConfigs = [];
|
|
3102
|
+
$._pipelines = [];
|
|
3083
3103
|
|
|
3084
3104
|
// local variables used for slightly better performance
|
|
3085
3105
|
// stores pipeline shifts and vertex counts/image indices
|
|
3086
3106
|
let drawStack = ($.drawStack = []);
|
|
3087
3107
|
|
|
3088
3108
|
// colors used for each draw call
|
|
3089
|
-
|
|
3109
|
+
|
|
3110
|
+
let colorStack = ($.colorStack = new Float32Array(1e6));
|
|
3111
|
+
|
|
3112
|
+
// prettier-ignore
|
|
3113
|
+
colorStack.set([
|
|
3114
|
+
0, 0, 0, 1, // black
|
|
3115
|
+
1, 1, 1, 1 // white
|
|
3116
|
+
]);
|
|
3090
3117
|
|
|
3091
3118
|
$._transformLayout = Q5.device.createBindGroupLayout({
|
|
3092
3119
|
label: 'transformLayout',
|
|
@@ -3110,32 +3137,70 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
3110
3137
|
]
|
|
3111
3138
|
});
|
|
3112
3139
|
|
|
3113
|
-
|
|
3140
|
+
colorsLayout = Q5.device.createBindGroupLayout({
|
|
3141
|
+
label: 'colorsLayout',
|
|
3142
|
+
entries: [
|
|
3143
|
+
{
|
|
3144
|
+
binding: 0,
|
|
3145
|
+
visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT,
|
|
3146
|
+
buffer: {
|
|
3147
|
+
type: 'read-only-storage',
|
|
3148
|
+
hasDynamicOffset: false
|
|
3149
|
+
}
|
|
3150
|
+
}
|
|
3151
|
+
]
|
|
3152
|
+
});
|
|
3153
|
+
|
|
3154
|
+
$.bindGroupLayouts = [$._transformLayout, colorsLayout];
|
|
3114
3155
|
|
|
3115
3156
|
let uniformBuffer = Q5.device.createBuffer({
|
|
3116
3157
|
size: 8, // Size of two floats
|
|
3117
3158
|
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
|
|
3118
3159
|
});
|
|
3119
3160
|
|
|
3161
|
+
let createMainView = () => {
|
|
3162
|
+
mainView = Q5.device
|
|
3163
|
+
.createTexture({
|
|
3164
|
+
size: [$.canvas.width, $.canvas.height],
|
|
3165
|
+
sampleCount: 4,
|
|
3166
|
+
format: 'bgra8unorm',
|
|
3167
|
+
usage: GPUTextureUsage.RENDER_ATTACHMENT
|
|
3168
|
+
})
|
|
3169
|
+
.createView();
|
|
3170
|
+
};
|
|
3171
|
+
|
|
3120
3172
|
$._createCanvas = (w, h, opt) => {
|
|
3121
3173
|
q.ctx = q.drawingContext = c.getContext('webgpu');
|
|
3122
3174
|
|
|
3123
3175
|
opt.format ??= navigator.gpu.getPreferredCanvasFormat();
|
|
3124
3176
|
opt.device ??= Q5.device;
|
|
3125
3177
|
|
|
3178
|
+
// needed for other blend modes but couldn't get it working
|
|
3179
|
+
// opt.alphaMode = 'premultiplied';
|
|
3180
|
+
|
|
3126
3181
|
$.ctx.configure(opt);
|
|
3127
3182
|
|
|
3128
3183
|
Q5.device.queue.writeBuffer(uniformBuffer, 0, new Float32Array([$.canvas.hw, $.canvas.hh]));
|
|
3129
3184
|
|
|
3185
|
+
createMainView();
|
|
3186
|
+
|
|
3130
3187
|
return c;
|
|
3131
3188
|
};
|
|
3132
3189
|
|
|
3133
3190
|
$._resizeCanvas = (w, h) => {
|
|
3134
3191
|
$._setCanvasSize(w, h);
|
|
3192
|
+
createMainView();
|
|
3193
|
+
};
|
|
3194
|
+
|
|
3195
|
+
$.pixelDensity = (v) => {
|
|
3196
|
+
if (!v || v == $._pixelDensity) return $._pixelDensity;
|
|
3197
|
+
$._pixelDensity = v;
|
|
3198
|
+
$._setCanvasSize(c.w, c.h);
|
|
3199
|
+
createMainView();
|
|
3200
|
+
return v;
|
|
3135
3201
|
};
|
|
3136
3202
|
|
|
3137
3203
|
// current color index, used to associate a vertex with a color
|
|
3138
|
-
let colorIndex = 0;
|
|
3139
3204
|
let addColor = (r, g, b, a = 1) => {
|
|
3140
3205
|
if (typeof r == 'string') r = $.color(r);
|
|
3141
3206
|
else if (b == undefined) {
|
|
@@ -3143,21 +3208,35 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
3143
3208
|
a = g ?? 1;
|
|
3144
3209
|
g = b = r;
|
|
3145
3210
|
}
|
|
3146
|
-
if (r._q5Color)
|
|
3147
|
-
|
|
3211
|
+
if (r._q5Color) {
|
|
3212
|
+
a = r.a;
|
|
3213
|
+
b = r.b;
|
|
3214
|
+
g = r.g;
|
|
3215
|
+
r = r.r;
|
|
3216
|
+
}
|
|
3217
|
+
|
|
3218
|
+
let cs = colorStack,
|
|
3219
|
+
i = colorStackIndex;
|
|
3220
|
+
cs[i++] = r;
|
|
3221
|
+
cs[i++] = g;
|
|
3222
|
+
cs[i++] = b;
|
|
3223
|
+
cs[i++] = a;
|
|
3224
|
+
colorStackIndex = i;
|
|
3225
|
+
|
|
3148
3226
|
colorIndex++;
|
|
3149
3227
|
};
|
|
3150
3228
|
|
|
3151
|
-
$._fillIndex = $._strokeIndex =
|
|
3229
|
+
$._fillIndex = $._strokeIndex = 0;
|
|
3230
|
+
$._doFill = $._doStroke = true;
|
|
3152
3231
|
|
|
3153
3232
|
$.fill = (r, g, b, a) => {
|
|
3154
3233
|
addColor(r, g, b, a);
|
|
3155
|
-
$._doFill = true;
|
|
3234
|
+
$._doFill = $._fillSet = true;
|
|
3156
3235
|
$._fillIndex = colorIndex;
|
|
3157
3236
|
};
|
|
3158
3237
|
$.stroke = (r, g, b, a) => {
|
|
3159
3238
|
addColor(r, g, b, a);
|
|
3160
|
-
$._doStroke = true;
|
|
3239
|
+
$._doStroke = $._strokeSet = true;
|
|
3161
3240
|
$._strokeIndex = colorIndex;
|
|
3162
3241
|
};
|
|
3163
3242
|
|
|
@@ -3359,6 +3438,68 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
3359
3438
|
return [l, r, t, b];
|
|
3360
3439
|
};
|
|
3361
3440
|
|
|
3441
|
+
// prettier-ignore
|
|
3442
|
+
let blendFactors = [
|
|
3443
|
+
'zero', // 0
|
|
3444
|
+
'one', // 1
|
|
3445
|
+
'src-alpha', // 2
|
|
3446
|
+
'one-minus-src-alpha', // 3
|
|
3447
|
+
'dst', // 4
|
|
3448
|
+
'dst-alpha', // 5
|
|
3449
|
+
'one-minus-dst-alpha', // 6
|
|
3450
|
+
'one-minus-src' // 7
|
|
3451
|
+
];
|
|
3452
|
+
let blendOps = [
|
|
3453
|
+
'add', // 0
|
|
3454
|
+
'subtract', // 1
|
|
3455
|
+
'reverse-subtract', // 2
|
|
3456
|
+
'min', // 3
|
|
3457
|
+
'max' // 4
|
|
3458
|
+
];
|
|
3459
|
+
|
|
3460
|
+
const blendModes = {
|
|
3461
|
+
normal: [2, 3, 0, 2, 3, 0],
|
|
3462
|
+
// destination_over: [6, 1, 0, 6, 1, 0],
|
|
3463
|
+
additive: [1, 1, 0, 1, 1, 0]
|
|
3464
|
+
// source_in: [5, 0, 0, 5, 0, 0],
|
|
3465
|
+
// destination_in: [0, 2, 0, 0, 2, 0],
|
|
3466
|
+
// source_out: [6, 0, 0, 6, 0, 0],
|
|
3467
|
+
// destination_out: [0, 3, 0, 0, 3, 0],
|
|
3468
|
+
// source_atop: [5, 3, 0, 5, 3, 0],
|
|
3469
|
+
// destination_atop: [6, 2, 0, 6, 2, 0]
|
|
3470
|
+
};
|
|
3471
|
+
|
|
3472
|
+
$.blendConfigs = {};
|
|
3473
|
+
|
|
3474
|
+
for (const [name, mode] of Object.entries(blendModes)) {
|
|
3475
|
+
$.blendConfigs[name] = {
|
|
3476
|
+
color: {
|
|
3477
|
+
srcFactor: blendFactors[mode[0]],
|
|
3478
|
+
dstFactor: blendFactors[mode[1]],
|
|
3479
|
+
operation: blendOps[mode[2]]
|
|
3480
|
+
},
|
|
3481
|
+
alpha: {
|
|
3482
|
+
srcFactor: blendFactors[mode[3]],
|
|
3483
|
+
dstFactor: blendFactors[mode[4]],
|
|
3484
|
+
operation: blendOps[mode[5]]
|
|
3485
|
+
}
|
|
3486
|
+
};
|
|
3487
|
+
}
|
|
3488
|
+
|
|
3489
|
+
$._blendMode = 'normal';
|
|
3490
|
+
$.blendMode = (mode) => {
|
|
3491
|
+
if (mode == $._blendMode) return;
|
|
3492
|
+
if (mode == 'source-over') mode = 'normal';
|
|
3493
|
+
if (mode == 'lighter') mode = 'additive';
|
|
3494
|
+
mode = mode.toLowerCase().replace(/[ -]/g, '_');
|
|
3495
|
+
$._blendMode = mode;
|
|
3496
|
+
|
|
3497
|
+
for (let i = 0; i < $._pipelines.length; i++) {
|
|
3498
|
+
$._pipelineConfigs[i].fragment.targets[0].blend = $.blendConfigs[mode];
|
|
3499
|
+
$._pipelines[i] = Q5.device.createRenderPipeline($._pipelineConfigs[i]);
|
|
3500
|
+
}
|
|
3501
|
+
};
|
|
3502
|
+
|
|
3362
3503
|
$.clear = () => {};
|
|
3363
3504
|
|
|
3364
3505
|
$._beginRender = () => {
|
|
@@ -3368,7 +3509,8 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
3368
3509
|
label: 'q5-webgpu',
|
|
3369
3510
|
colorAttachments: [
|
|
3370
3511
|
{
|
|
3371
|
-
view:
|
|
3512
|
+
view: mainView,
|
|
3513
|
+
resolveTarget: $.ctx.getCurrentTexture().createView(),
|
|
3372
3514
|
loadOp: 'clear',
|
|
3373
3515
|
storeOp: 'store'
|
|
3374
3516
|
}
|
|
@@ -3379,58 +3521,62 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
3379
3521
|
$._render = () => {
|
|
3380
3522
|
if (transformStates.length > 1 || !$._transformBindGroup) {
|
|
3381
3523
|
let transformBuffer = Q5.device.createBuffer({
|
|
3382
|
-
size: transformStates.length * 64, //
|
|
3383
|
-
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST
|
|
3524
|
+
size: transformStates.length * 64, // 64 is the size of 16 floats
|
|
3525
|
+
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
|
|
3526
|
+
mappedAtCreation: true
|
|
3384
3527
|
});
|
|
3385
3528
|
|
|
3386
|
-
|
|
3529
|
+
new Float32Array(transformBuffer.getMappedRange()).set(transformStates.flat());
|
|
3530
|
+
transformBuffer.unmap();
|
|
3387
3531
|
|
|
3388
3532
|
$._transformBindGroup = Q5.device.createBindGroup({
|
|
3389
3533
|
layout: $._transformLayout,
|
|
3390
3534
|
entries: [
|
|
3391
|
-
{
|
|
3392
|
-
|
|
3393
|
-
resource: {
|
|
3394
|
-
buffer: uniformBuffer
|
|
3395
|
-
}
|
|
3396
|
-
},
|
|
3397
|
-
{
|
|
3398
|
-
binding: 1,
|
|
3399
|
-
resource: {
|
|
3400
|
-
buffer: transformBuffer
|
|
3401
|
-
}
|
|
3402
|
-
}
|
|
3535
|
+
{ binding: 0, resource: { buffer: uniformBuffer } },
|
|
3536
|
+
{ binding: 1, resource: { buffer: transformBuffer } }
|
|
3403
3537
|
]
|
|
3404
3538
|
});
|
|
3405
3539
|
}
|
|
3406
3540
|
|
|
3407
3541
|
pass.setBindGroup(0, $._transformBindGroup);
|
|
3408
3542
|
|
|
3543
|
+
let colorsBuffer = Q5.device.createBuffer({
|
|
3544
|
+
size: colorStackIndex * 4,
|
|
3545
|
+
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
|
|
3546
|
+
mappedAtCreation: true
|
|
3547
|
+
});
|
|
3548
|
+
|
|
3549
|
+
new Float32Array(colorsBuffer.getMappedRange()).set(colorStack.slice(0, colorStackIndex));
|
|
3550
|
+
colorsBuffer.unmap();
|
|
3551
|
+
|
|
3552
|
+
$._colorsBindGroup = Q5.device.createBindGroup({
|
|
3553
|
+
layout: colorsLayout,
|
|
3554
|
+
entries: [{ binding: 0, resource: { buffer: colorsBuffer } }]
|
|
3555
|
+
});
|
|
3556
|
+
|
|
3557
|
+
$.pass.setBindGroup(1, $._colorsBindGroup);
|
|
3558
|
+
|
|
3409
3559
|
for (let m of $._hooks.preRender) m();
|
|
3410
3560
|
|
|
3411
|
-
let drawVertOffset = 0
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3561
|
+
let drawVertOffset = 0,
|
|
3562
|
+
imageVertOffset = 0,
|
|
3563
|
+
textCharOffset = 0,
|
|
3564
|
+
curPipelineIndex = -1,
|
|
3565
|
+
curTextureIndex = -1;
|
|
3416
3566
|
|
|
3417
|
-
for (let i = 0; i < drawStack.length; i +=
|
|
3567
|
+
for (let i = 0; i < drawStack.length; i += 3) {
|
|
3418
3568
|
let v = drawStack[i + 1];
|
|
3419
|
-
|
|
3420
|
-
if (drawStack[i] == -1) {
|
|
3421
|
-
v();
|
|
3422
|
-
continue;
|
|
3423
|
-
}
|
|
3569
|
+
let o = drawStack[i + 2];
|
|
3424
3570
|
|
|
3425
3571
|
if (curPipelineIndex != drawStack[i]) {
|
|
3426
3572
|
curPipelineIndex = drawStack[i];
|
|
3427
|
-
pass.setPipeline($.
|
|
3573
|
+
pass.setPipeline($._pipelines[curPipelineIndex]);
|
|
3428
3574
|
}
|
|
3429
3575
|
|
|
3430
3576
|
if (curPipelineIndex == 0) {
|
|
3431
3577
|
// v is the number of vertices
|
|
3432
|
-
pass.
|
|
3433
|
-
drawVertOffset +=
|
|
3578
|
+
pass.drawIndexed(v, 1, 0, drawVertOffset);
|
|
3579
|
+
drawVertOffset += o;
|
|
3434
3580
|
} else if (curPipelineIndex == 1) {
|
|
3435
3581
|
if (curTextureIndex != v) {
|
|
3436
3582
|
// v is the texture index
|
|
@@ -3439,7 +3585,7 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
3439
3585
|
pass.draw(6, 1, imageVertOffset);
|
|
3440
3586
|
imageVertOffset += 6;
|
|
3441
3587
|
} else if (curPipelineIndex == 2) {
|
|
3442
|
-
pass.setBindGroup(2, $.
|
|
3588
|
+
pass.setBindGroup(2, $._fonts[o].bindGroup);
|
|
3443
3589
|
pass.setBindGroup(3, $._textBindGroup);
|
|
3444
3590
|
|
|
3445
3591
|
// v is the number of characters in the text
|
|
@@ -3455,12 +3601,13 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
3455
3601
|
pass.end();
|
|
3456
3602
|
let commandBuffer = $.encoder.finish();
|
|
3457
3603
|
Q5.device.queue.submit([commandBuffer]);
|
|
3604
|
+
|
|
3458
3605
|
q.pass = $.encoder = null;
|
|
3459
3606
|
|
|
3460
3607
|
// clear the stacks for the next frame
|
|
3461
3608
|
$.drawStack.length = 0;
|
|
3462
|
-
|
|
3463
|
-
|
|
3609
|
+
colorIndex = 1;
|
|
3610
|
+
colorStackIndex = 8;
|
|
3464
3611
|
rotation = 0;
|
|
3465
3612
|
transformStates.length = 1;
|
|
3466
3613
|
$._transformIndexStack.length = 0;
|
|
@@ -3491,41 +3638,47 @@ Q5.webgpu = async function (scope, parent) {
|
|
|
3491
3638
|
return new Q5(scope, parent, 'webgpu');
|
|
3492
3639
|
};
|
|
3493
3640
|
Q5.renderers.webgpu.drawing = ($, q) => {
|
|
3494
|
-
let c = $.canvas
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
|
|
3501
|
-
|
|
3641
|
+
let c = $.canvas,
|
|
3642
|
+
drawStack = $.drawStack,
|
|
3643
|
+
vertexStack = new Float32Array(1e7),
|
|
3644
|
+
indexStack = new Uint32Array(1e6),
|
|
3645
|
+
vertIndex = 0,
|
|
3646
|
+
vertCount = 0,
|
|
3647
|
+
idxBufferIndex = 0,
|
|
3648
|
+
colorIndex;
|
|
3502
3649
|
|
|
3503
3650
|
let vertexShader = Q5.device.createShaderModule({
|
|
3504
3651
|
label: 'drawingVertexShader',
|
|
3505
3652
|
code: `
|
|
3653
|
+
struct VertexInput {
|
|
3654
|
+
@location(0) pos: vec2f,
|
|
3655
|
+
@location(1) colorIndex: f32,
|
|
3656
|
+
@location(2) transformIndex: f32
|
|
3657
|
+
}
|
|
3506
3658
|
struct VertexOutput {
|
|
3507
3659
|
@builtin(position) position: vec4f,
|
|
3508
|
-
@location(0)
|
|
3509
|
-
}
|
|
3510
|
-
|
|
3660
|
+
@location(0) color: vec4f
|
|
3661
|
+
}
|
|
3511
3662
|
struct Uniforms {
|
|
3512
3663
|
halfWidth: f32,
|
|
3513
3664
|
halfHeight: f32
|
|
3514
|
-
}
|
|
3665
|
+
}
|
|
3515
3666
|
|
|
3516
3667
|
@group(0) @binding(0) var<uniform> uniforms: Uniforms;
|
|
3517
|
-
@group(0) @binding(1) var<storage
|
|
3668
|
+
@group(0) @binding(1) var<storage> transforms: array<mat4x4<f32>>;
|
|
3669
|
+
|
|
3670
|
+
@group(1) @binding(0) var<storage> colors : array<vec4f>;
|
|
3518
3671
|
|
|
3519
3672
|
@vertex
|
|
3520
|
-
fn vertexMain(
|
|
3521
|
-
var vert = vec4f(pos, 0.0, 1.0);
|
|
3522
|
-
vert = transforms[i32(transformIndex)] * vert;
|
|
3673
|
+
fn vertexMain(input: VertexInput) -> VertexOutput {
|
|
3674
|
+
var vert = vec4f(input.pos, 0.0, 1.0);
|
|
3675
|
+
vert = transforms[i32(input.transformIndex)] * vert;
|
|
3523
3676
|
vert.x /= uniforms.halfWidth;
|
|
3524
3677
|
vert.y /= uniforms.halfHeight;
|
|
3525
3678
|
|
|
3526
3679
|
var output: VertexOutput;
|
|
3527
3680
|
output.position = vert;
|
|
3528
|
-
output.
|
|
3681
|
+
output.color = colors[i32(input.colorIndex)];
|
|
3529
3682
|
return output;
|
|
3530
3683
|
}
|
|
3531
3684
|
`
|
|
@@ -3534,32 +3687,13 @@ fn vertexMain(@location(0) pos: vec2f, @location(1) colorIndex: f32, @location(2
|
|
|
3534
3687
|
let fragmentShader = Q5.device.createShaderModule({
|
|
3535
3688
|
label: 'drawingFragmentShader',
|
|
3536
3689
|
code: `
|
|
3537
|
-
@group(1) @binding(0) var<storage, read> colors : array<vec4f>;
|
|
3538
|
-
|
|
3539
3690
|
@fragment
|
|
3540
|
-
fn fragmentMain(@location(0)
|
|
3541
|
-
|
|
3542
|
-
return mix(colors[index], colors[index + 1], fract(colorIndex));
|
|
3691
|
+
fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
|
|
3692
|
+
return color;
|
|
3543
3693
|
}
|
|
3544
3694
|
`
|
|
3545
3695
|
});
|
|
3546
3696
|
|
|
3547
|
-
colorsLayout = Q5.device.createBindGroupLayout({
|
|
3548
|
-
label: 'colorsLayout',
|
|
3549
|
-
entries: [
|
|
3550
|
-
{
|
|
3551
|
-
binding: 0,
|
|
3552
|
-
visibility: GPUShaderStage.FRAGMENT,
|
|
3553
|
-
buffer: {
|
|
3554
|
-
type: 'read-only-storage',
|
|
3555
|
-
hasDynamicOffset: false
|
|
3556
|
-
}
|
|
3557
|
-
}
|
|
3558
|
-
]
|
|
3559
|
-
});
|
|
3560
|
-
|
|
3561
|
-
$.bindGroupLayouts.push(colorsLayout);
|
|
3562
|
-
|
|
3563
3697
|
let vertexBufferLayout = {
|
|
3564
3698
|
arrayStride: 16, // 2 coordinates + 1 color index + 1 transform index * 4 bytes each
|
|
3565
3699
|
attributes: [
|
|
@@ -3569,193 +3703,212 @@ fn fragmentMain(@location(0) colorIndex: f32) -> @location(0) vec4f {
|
|
|
3569
3703
|
]
|
|
3570
3704
|
};
|
|
3571
3705
|
|
|
3572
|
-
// prettier-ignore
|
|
3573
|
-
let blendFactors = [
|
|
3574
|
-
'zero', // 0
|
|
3575
|
-
'one', // 1
|
|
3576
|
-
'src-alpha', // 2
|
|
3577
|
-
'one-minus-src-alpha', // 3
|
|
3578
|
-
'dst', // 4
|
|
3579
|
-
'dst-alpha', // 5
|
|
3580
|
-
'one-minus-dst-alpha', // 6
|
|
3581
|
-
'one-minus-src' // 7
|
|
3582
|
-
];
|
|
3583
|
-
let blendOps = [
|
|
3584
|
-
'add', // 0
|
|
3585
|
-
'subtract', // 1
|
|
3586
|
-
'reverse-subtract', // 2
|
|
3587
|
-
'min', // 3
|
|
3588
|
-
'max' // 4
|
|
3589
|
-
];
|
|
3590
|
-
|
|
3591
|
-
const blendModes = {
|
|
3592
|
-
normal: [2, 3, 0, 2, 3, 0],
|
|
3593
|
-
lighter: [2, 1, 0, 2, 1, 0],
|
|
3594
|
-
subtract: [2, 1, 2, 2, 1, 2],
|
|
3595
|
-
multiply: [4, 0, 0, 5, 0, 0],
|
|
3596
|
-
screen: [1, 3, 0, 1, 3, 0],
|
|
3597
|
-
darken: [1, 3, 3, 1, 3, 3],
|
|
3598
|
-
lighten: [1, 3, 4, 1, 3, 4],
|
|
3599
|
-
overlay: [2, 3, 0, 2, 3, 0],
|
|
3600
|
-
hard_light: [2, 3, 0, 2, 3, 0],
|
|
3601
|
-
soft_light: [2, 3, 0, 2, 3, 0],
|
|
3602
|
-
difference: [2, 3, 2, 2, 3, 2],
|
|
3603
|
-
exclusion: [2, 3, 0, 2, 3, 0],
|
|
3604
|
-
color_dodge: [1, 7, 0, 1, 7, 0],
|
|
3605
|
-
color_burn: [6, 1, 0, 6, 1, 0],
|
|
3606
|
-
linear_dodge: [2, 1, 0, 2, 1, 0],
|
|
3607
|
-
linear_burn: [2, 7, 1, 2, 7, 1],
|
|
3608
|
-
vivid_light: [2, 7, 0, 2, 7, 0],
|
|
3609
|
-
pin_light: [2, 7, 0, 2, 7, 0],
|
|
3610
|
-
hard_mix: [2, 7, 0, 2, 7, 0]
|
|
3611
|
-
};
|
|
3612
|
-
|
|
3613
|
-
$.blendConfigs = {};
|
|
3614
|
-
|
|
3615
|
-
for (const [name, mode] of Object.entries(blendModes)) {
|
|
3616
|
-
$.blendConfigs[name] = {
|
|
3617
|
-
color: {
|
|
3618
|
-
srcFactor: blendFactors[mode[0]],
|
|
3619
|
-
dstFactor: blendFactors[mode[1]],
|
|
3620
|
-
operation: blendOps[mode[2]]
|
|
3621
|
-
},
|
|
3622
|
-
alpha: {
|
|
3623
|
-
srcFactor: blendFactors[mode[3]],
|
|
3624
|
-
dstFactor: blendFactors[mode[4]],
|
|
3625
|
-
operation: blendOps[mode[5]]
|
|
3626
|
-
}
|
|
3627
|
-
};
|
|
3628
|
-
}
|
|
3629
|
-
|
|
3630
|
-
$._blendMode = 'normal';
|
|
3631
|
-
$.blendMode = (mode) => {
|
|
3632
|
-
if (mode == $._blendMode) return;
|
|
3633
|
-
if (mode == 'source-over') mode = 'normal';
|
|
3634
|
-
mode = mode.toLowerCase().replace(/[ -]/g, '_');
|
|
3635
|
-
$._blendMode = mode;
|
|
3636
|
-
$.pipelines[0] = $._createPipeline($.blendConfigs[mode]);
|
|
3637
|
-
};
|
|
3638
|
-
|
|
3639
3706
|
let pipelineLayout = Q5.device.createPipelineLayout({
|
|
3640
3707
|
label: 'drawingPipelineLayout',
|
|
3641
3708
|
bindGroupLayouts: $.bindGroupLayouts
|
|
3642
3709
|
});
|
|
3643
3710
|
|
|
3644
|
-
$.
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
|
|
3652
|
-
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
|
|
3656
|
-
|
|
3657
|
-
|
|
3658
|
-
|
|
3659
|
-
|
|
3711
|
+
$._pipelineConfigs[0] = {
|
|
3712
|
+
label: 'drawingPipeline',
|
|
3713
|
+
layout: pipelineLayout,
|
|
3714
|
+
vertex: {
|
|
3715
|
+
module: vertexShader,
|
|
3716
|
+
entryPoint: 'vertexMain',
|
|
3717
|
+
buffers: [vertexBufferLayout]
|
|
3718
|
+
},
|
|
3719
|
+
fragment: {
|
|
3720
|
+
module: fragmentShader,
|
|
3721
|
+
entryPoint: 'fragmentMain',
|
|
3722
|
+
targets: [{ format: 'bgra8unorm', blend: $.blendConfigs.normal }]
|
|
3723
|
+
},
|
|
3724
|
+
primitive: { topology: 'triangle-list' },
|
|
3725
|
+
multisample: {
|
|
3726
|
+
count: 4
|
|
3727
|
+
}
|
|
3728
|
+
};
|
|
3729
|
+
|
|
3730
|
+
$._pipelines[0] = Q5.device.createRenderPipeline($._pipelineConfigs[0]);
|
|
3731
|
+
|
|
3732
|
+
const addVert = (x, y, ci, ti) => {
|
|
3733
|
+
let v = vertexStack,
|
|
3734
|
+
i = vertIndex;
|
|
3735
|
+
v[i++] = x;
|
|
3736
|
+
v[i++] = y;
|
|
3737
|
+
v[i++] = ci;
|
|
3738
|
+
v[i++] = ti;
|
|
3739
|
+
vertIndex = i;
|
|
3740
|
+
vertCount++;
|
|
3741
|
+
};
|
|
3742
|
+
|
|
3743
|
+
const addIndex = (i1, i2, i3) => {
|
|
3744
|
+
let is = indexStack,
|
|
3745
|
+
ii = idxBufferIndex;
|
|
3746
|
+
is[ii++] = i1;
|
|
3747
|
+
is[ii++] = i2;
|
|
3748
|
+
is[ii++] = i3;
|
|
3749
|
+
idxBufferIndex = ii;
|
|
3750
|
+
};
|
|
3751
|
+
|
|
3752
|
+
const addQuad = (x1, y1, x2, y2, x3, y3, x4, y4, ci, ti) => {
|
|
3753
|
+
let v = vertexStack,
|
|
3754
|
+
i = vertIndex;
|
|
3755
|
+
|
|
3756
|
+
let i1 = vertCount++;
|
|
3757
|
+
v[i++] = x1;
|
|
3758
|
+
v[i++] = y1;
|
|
3759
|
+
v[i++] = ci;
|
|
3760
|
+
v[i++] = ti;
|
|
3761
|
+
|
|
3762
|
+
let i2 = vertCount++;
|
|
3763
|
+
v[i++] = x2;
|
|
3764
|
+
v[i++] = y2;
|
|
3765
|
+
v[i++] = ci;
|
|
3766
|
+
v[i++] = ti;
|
|
3767
|
+
|
|
3768
|
+
let i3 = vertCount++;
|
|
3769
|
+
v[i++] = x3;
|
|
3770
|
+
v[i++] = y3;
|
|
3771
|
+
v[i++] = ci;
|
|
3772
|
+
v[i++] = ti;
|
|
3773
|
+
|
|
3774
|
+
let i4 = vertCount++;
|
|
3775
|
+
v[i++] = x4;
|
|
3776
|
+
v[i++] = y4;
|
|
3777
|
+
v[i++] = ci;
|
|
3778
|
+
v[i++] = ti;
|
|
3779
|
+
|
|
3780
|
+
vertIndex = i;
|
|
3781
|
+
|
|
3782
|
+
let is = indexStack,
|
|
3783
|
+
ii = idxBufferIndex;
|
|
3784
|
+
is[ii++] = i1;
|
|
3785
|
+
is[ii++] = i2;
|
|
3786
|
+
is[ii++] = i3;
|
|
3787
|
+
is[ii++] = i1;
|
|
3788
|
+
is[ii++] = i3;
|
|
3789
|
+
is[ii++] = i4;
|
|
3790
|
+
idxBufferIndex = ii;
|
|
3791
|
+
|
|
3792
|
+
drawStack.push(0, 6, 4);
|
|
3793
|
+
};
|
|
3794
|
+
|
|
3795
|
+
const addEllipse = (x, y, a, b, n, ci, ti) => {
|
|
3796
|
+
let t = 0,
|
|
3797
|
+
angleIncrement = $.TAU / n,
|
|
3798
|
+
indicesStart = vertIndex / 4;
|
|
3799
|
+
addVert(x, y, ci, ti); // Center vertex
|
|
3800
|
+
for (let i = 0; i <= n; i++) {
|
|
3801
|
+
let vx = x + a * Math.cos(t),
|
|
3802
|
+
vy = y + b * Math.sin(t);
|
|
3803
|
+
addVert(vx, vy, ci, ti);
|
|
3804
|
+
if (i > 0) {
|
|
3805
|
+
addIndex(indicesStart, indicesStart + i, indicesStart + i + 1);
|
|
3806
|
+
}
|
|
3807
|
+
t += angleIncrement;
|
|
3808
|
+
}
|
|
3809
|
+
drawStack.push(0, n * 3, n + 2);
|
|
3660
3810
|
};
|
|
3661
3811
|
|
|
3662
|
-
$.
|
|
3812
|
+
$.rectMode = (x) => ($._rectMode = x);
|
|
3663
3813
|
|
|
3664
|
-
|
|
3814
|
+
$.rect = (x, y, w, h) => {
|
|
3815
|
+
let [l, r, t, b] = $._calcBox(x, y, w, h, $._rectMode);
|
|
3816
|
+
let ci, ti;
|
|
3817
|
+
if ($._matrixDirty) $._saveMatrix();
|
|
3818
|
+
ti = $._transformIndex;
|
|
3665
3819
|
|
|
3666
|
-
|
|
3667
|
-
|
|
3668
|
-
};
|
|
3820
|
+
if ($._doStroke) {
|
|
3821
|
+
ci = $._strokeIndex;
|
|
3669
3822
|
|
|
3670
|
-
|
|
3671
|
-
|
|
3672
|
-
|
|
3673
|
-
|
|
3823
|
+
// outer rectangle coordinates
|
|
3824
|
+
let sw = $._strokeWeight / 2;
|
|
3825
|
+
let to = t + sw,
|
|
3826
|
+
bo = b - sw,
|
|
3827
|
+
lo = l - sw,
|
|
3828
|
+
ro = r + sw;
|
|
3674
3829
|
|
|
3675
|
-
|
|
3676
|
-
|
|
3677
|
-
|
|
3678
|
-
|
|
3679
|
-
|
|
3680
|
-
|
|
3681
|
-
|
|
3682
|
-
|
|
3683
|
-
}
|
|
3684
|
-
if (close) {
|
|
3685
|
-
// Close the shape by adding the first vertex at the end
|
|
3686
|
-
v.push(v[0], v[1], v[2], v[3]);
|
|
3687
|
-
}
|
|
3688
|
-
// Convert the shape to triangles
|
|
3689
|
-
let triangles = [];
|
|
3690
|
-
for (let i = 4; i < v.length; i += 4) {
|
|
3691
|
-
triangles.push(
|
|
3692
|
-
v[0], // First vertex
|
|
3693
|
-
v[1],
|
|
3694
|
-
v[2],
|
|
3695
|
-
v[3],
|
|
3696
|
-
v[i - 4], // Previous vertex
|
|
3697
|
-
v[i - 3],
|
|
3698
|
-
v[i - 2],
|
|
3699
|
-
v[i - 1],
|
|
3700
|
-
v[i], // Current vertex
|
|
3701
|
-
v[i + 1],
|
|
3702
|
-
v[i + 2],
|
|
3703
|
-
v[i + 3]
|
|
3704
|
-
);
|
|
3830
|
+
// stroke is simply a bigger rectangle drawn first
|
|
3831
|
+
addQuad(lo, to, ro, to, ro, bo, lo, bo, ci, ti);
|
|
3832
|
+
|
|
3833
|
+
// inner rectangle coordinates
|
|
3834
|
+
t -= sw;
|
|
3835
|
+
b += sw;
|
|
3836
|
+
l += sw;
|
|
3837
|
+
r -= sw;
|
|
3705
3838
|
}
|
|
3706
|
-
shapeVertices = [];
|
|
3707
3839
|
|
|
3708
|
-
|
|
3709
|
-
|
|
3710
|
-
};
|
|
3840
|
+
if ($._doFill) {
|
|
3841
|
+
ci = colorIndex ?? $._fillIndex;
|
|
3711
3842
|
|
|
3712
|
-
|
|
3713
|
-
|
|
3714
|
-
|
|
3715
|
-
$.vertex(x2, y2);
|
|
3716
|
-
$.vertex(x3, y3);
|
|
3717
|
-
$.endShape(1);
|
|
3843
|
+
// two triangles make a rectangle
|
|
3844
|
+
addQuad(l, t, r, t, r, b, l, b, ci, ti);
|
|
3845
|
+
}
|
|
3718
3846
|
};
|
|
3719
3847
|
|
|
3720
|
-
$.
|
|
3721
|
-
$.beginShape();
|
|
3722
|
-
$.vertex(x1, y1);
|
|
3723
|
-
$.vertex(x2, y2);
|
|
3724
|
-
$.vertex(x3, y3);
|
|
3725
|
-
$.vertex(x4, y4);
|
|
3726
|
-
$.endShape(1);
|
|
3727
|
-
};
|
|
3848
|
+
$.square = (x, y, s) => $.rect(x, y, s, s);
|
|
3728
3849
|
|
|
3729
|
-
|
|
3850
|
+
// prettier-ignore
|
|
3851
|
+
const getArcSegments = (d) =>
|
|
3852
|
+
d < 4 ? 6 :
|
|
3853
|
+
d < 6 ? 8 :
|
|
3854
|
+
d < 10 ? 10 :
|
|
3855
|
+
d < 16 ? 12 :
|
|
3856
|
+
d < 20 ? 14 :
|
|
3857
|
+
d < 22 ? 16 :
|
|
3858
|
+
d < 24 ? 18 :
|
|
3859
|
+
d < 28 ? 20 :
|
|
3860
|
+
d < 34 ? 22 :
|
|
3861
|
+
d < 42 ? 24 :
|
|
3862
|
+
d < 48 ? 26 :
|
|
3863
|
+
d < 56 ? 28 :
|
|
3864
|
+
d < 64 ? 30 :
|
|
3865
|
+
d < 72 ? 32 :
|
|
3866
|
+
d < 84 ? 34 :
|
|
3867
|
+
d < 96 ? 36 :
|
|
3868
|
+
d < 98 ? 38 :
|
|
3869
|
+
d < 113 ? 40 :
|
|
3870
|
+
d < 149 ? 44 :
|
|
3871
|
+
d < 199 ? 48 :
|
|
3872
|
+
d < 261 ? 52 :
|
|
3873
|
+
d < 353 ? 56 :
|
|
3874
|
+
d < 461 ? 60 :
|
|
3875
|
+
d < 585 ? 64 :
|
|
3876
|
+
d < 1200 ? 70 :
|
|
3877
|
+
d < 1800 ? 80 :
|
|
3878
|
+
d < 2400 ? 90 :
|
|
3879
|
+
100;
|
|
3730
3880
|
|
|
3731
|
-
$.
|
|
3732
|
-
let [l, r, t, b] = $._calcBox(x, y, w, h, $._rectMode);
|
|
3881
|
+
$.ellipseMode = (x) => ($._ellipseMode = x);
|
|
3733
3882
|
|
|
3734
|
-
|
|
3883
|
+
$.ellipse = (x, y, w, h) => {
|
|
3884
|
+
let n = getArcSegments(w == h ? w : Math.max(w, h));
|
|
3885
|
+
let a = Math.max(w, 1) / 2;
|
|
3886
|
+
let b = w == h ? a : Math.max(h, 1) / 2;
|
|
3887
|
+
let ci;
|
|
3735
3888
|
if ($._matrixDirty) $._saveMatrix();
|
|
3736
3889
|
let ti = $._transformIndex;
|
|
3737
|
-
|
|
3738
|
-
|
|
3739
|
-
|
|
3740
|
-
|
|
3741
|
-
|
|
3742
|
-
|
|
3743
|
-
|
|
3744
|
-
|
|
3745
|
-
|
|
3746
|
-
);
|
|
3747
|
-
drawStack.push(0, 6);
|
|
3890
|
+
if ($._doStroke) {
|
|
3891
|
+
let sw = $._strokeWeight / 2;
|
|
3892
|
+
addEllipse(x, y, a + sw, b + sw, n, $._strokeIndex, ti);
|
|
3893
|
+
a -= sw;
|
|
3894
|
+
b -= sw;
|
|
3895
|
+
}
|
|
3896
|
+
if ($._doFill) {
|
|
3897
|
+
addEllipse(x, y, a, b, n, colorIndex ?? $._fillIndex, ti);
|
|
3898
|
+
}
|
|
3748
3899
|
};
|
|
3749
3900
|
|
|
3750
|
-
$.
|
|
3901
|
+
$.circle = (x, y, d) => $.ellipse(x, y, d, d);
|
|
3751
3902
|
|
|
3752
3903
|
$.point = (x, y) => {
|
|
3753
3904
|
colorIndex = $._strokeIndex;
|
|
3905
|
+
$._doStroke = false;
|
|
3754
3906
|
let sw = $._strokeWeight;
|
|
3755
3907
|
if (sw < 2) {
|
|
3756
3908
|
sw = Math.round(sw);
|
|
3757
3909
|
$.rect(x, y, sw, sw);
|
|
3758
3910
|
} else $.ellipse(x, y, sw, sw);
|
|
3911
|
+
$._doStroke = true;
|
|
3759
3912
|
colorIndex = null;
|
|
3760
3913
|
};
|
|
3761
3914
|
|
|
@@ -3763,19 +3916,97 @@ fn fragmentMain(@location(0) colorIndex: f32) -> @location(0) vec4f {
|
|
|
3763
3916
|
colorIndex = $._strokeIndex;
|
|
3764
3917
|
|
|
3765
3918
|
$.push();
|
|
3766
|
-
$.
|
|
3919
|
+
$._doStroke = false;
|
|
3920
|
+
$.translate(x1, -y1);
|
|
3767
3921
|
$.rotate($.atan2(y2 - y1, x2 - x1));
|
|
3768
3922
|
let length = Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2);
|
|
3769
|
-
let sw = $._strokeWeight
|
|
3770
|
-
|
|
3923
|
+
let sw = $._strokeWeight,
|
|
3924
|
+
hsw = sw / 2;
|
|
3925
|
+
$._rectMode = 'corner';
|
|
3926
|
+
if (sw < 4) {
|
|
3927
|
+
$.rect(-hsw, -hsw, length + hsw, sw);
|
|
3928
|
+
} else {
|
|
3929
|
+
$._ellipseMode = 'center';
|
|
3930
|
+
$.ellipse(0, 0, sw, sw);
|
|
3931
|
+
$.ellipse(length, 0, sw, sw);
|
|
3932
|
+
$.rect(0, -hsw, length, sw);
|
|
3933
|
+
}
|
|
3934
|
+
|
|
3771
3935
|
$.pop();
|
|
3772
3936
|
|
|
3773
3937
|
colorIndex = null;
|
|
3774
3938
|
};
|
|
3775
3939
|
|
|
3940
|
+
let shapeVertCount;
|
|
3941
|
+
|
|
3942
|
+
$.beginShape = () => {
|
|
3943
|
+
shapeVertCount = 0;
|
|
3944
|
+
};
|
|
3945
|
+
|
|
3946
|
+
$.vertex = (x, y) => {
|
|
3947
|
+
if ($._matrixDirty) $._saveMatrix();
|
|
3948
|
+
addVert(x, -y, $._fillIndex, $._transformIndex);
|
|
3949
|
+
shapeVertCount++;
|
|
3950
|
+
};
|
|
3951
|
+
|
|
3952
|
+
$.endShape = (close) => {
|
|
3953
|
+
if (shapeVertCount < 3) {
|
|
3954
|
+
throw new Error('A shape must have at least 3 vertices.');
|
|
3955
|
+
}
|
|
3956
|
+
|
|
3957
|
+
let firstVert = vertCount - shapeVertCount;
|
|
3958
|
+
|
|
3959
|
+
if ($._doFill) {
|
|
3960
|
+
// make a simple triangle fan, starting from the first vertex
|
|
3961
|
+
for (let i = firstVert + 1; i < vertCount - 1; i++) {
|
|
3962
|
+
addIndex(firstVert, i, i + 1);
|
|
3963
|
+
}
|
|
3964
|
+
drawStack.push(0, (shapeVertCount - 2) * 3, shapeVertCount);
|
|
3965
|
+
}
|
|
3966
|
+
|
|
3967
|
+
if ($._doStroke) {
|
|
3968
|
+
let first = firstVert * 4,
|
|
3969
|
+
last = vertIndex - 4;
|
|
3970
|
+
for (let i = first; i < last; i += 4) {
|
|
3971
|
+
let x1 = vertexStack[i],
|
|
3972
|
+
y1 = vertexStack[i + 1],
|
|
3973
|
+
x2 = vertexStack[i + 4],
|
|
3974
|
+
y2 = vertexStack[i + 5];
|
|
3975
|
+
$.line(x1, y1, x2, y2);
|
|
3976
|
+
}
|
|
3977
|
+
if (close) {
|
|
3978
|
+
let x1 = vertexStack[last],
|
|
3979
|
+
y1 = vertexStack[last + 1],
|
|
3980
|
+
x2 = vertexStack[first],
|
|
3981
|
+
y2 = vertexStack[first + 1];
|
|
3982
|
+
$.line(x1, y1, x2, y2);
|
|
3983
|
+
}
|
|
3984
|
+
}
|
|
3985
|
+
|
|
3986
|
+
shapeVertCount = 0;
|
|
3987
|
+
};
|
|
3988
|
+
|
|
3989
|
+
$.triangle = (x1, y1, x2, y2, x3, y3) => {
|
|
3990
|
+
$.beginShape();
|
|
3991
|
+
$.vertex(x1, y1);
|
|
3992
|
+
$.vertex(x2, y2);
|
|
3993
|
+
$.vertex(x3, y3);
|
|
3994
|
+
$.endShape();
|
|
3995
|
+
};
|
|
3996
|
+
|
|
3997
|
+
$.quad = (x1, y1, x2, y2, x3, y3, x4, y4) => {
|
|
3998
|
+
$.beginShape();
|
|
3999
|
+
$.vertex(x1, y1);
|
|
4000
|
+
$.vertex(x2, y2);
|
|
4001
|
+
$.vertex(x3, y3);
|
|
4002
|
+
$.vertex(x4, y4);
|
|
4003
|
+
$.endShape();
|
|
4004
|
+
};
|
|
4005
|
+
|
|
3776
4006
|
$.background = (r, g, b, a) => {
|
|
3777
4007
|
$.push();
|
|
3778
4008
|
$.resetMatrix();
|
|
4009
|
+
$._doStroke = false;
|
|
3779
4010
|
if (r.src) {
|
|
3780
4011
|
let og = $._imageMode;
|
|
3781
4012
|
$._imageMode = 'corner';
|
|
@@ -3789,121 +4020,44 @@ fn fragmentMain(@location(0) colorIndex: f32) -> @location(0) vec4f {
|
|
|
3789
4020
|
$._rectMode = og;
|
|
3790
4021
|
}
|
|
3791
4022
|
$.pop();
|
|
4023
|
+
if (!$._fillSet) $._fillIndex = 1;
|
|
3792
4024
|
};
|
|
3793
4025
|
|
|
3794
|
-
/**
|
|
3795
|
-
* Derived from: ceil(Math.log(d) * 7) * 2 - ceil(28)
|
|
3796
|
-
* This lookup table is used for better performance.
|
|
3797
|
-
* @param {Number} d diameter of the circle
|
|
3798
|
-
* @returns n number of segments
|
|
3799
|
-
*/
|
|
3800
|
-
// prettier-ignore
|
|
3801
|
-
const getArcSegments = (d) =>
|
|
3802
|
-
d < 4 ? 6 :
|
|
3803
|
-
d < 6 ? 8 :
|
|
3804
|
-
d < 10 ? 10 :
|
|
3805
|
-
d < 16 ? 12 :
|
|
3806
|
-
d < 20 ? 14 :
|
|
3807
|
-
d < 22 ? 16 :
|
|
3808
|
-
d < 24 ? 18 :
|
|
3809
|
-
d < 28 ? 20 :
|
|
3810
|
-
d < 34 ? 22 :
|
|
3811
|
-
d < 42 ? 24 :
|
|
3812
|
-
d < 48 ? 26 :
|
|
3813
|
-
d < 56 ? 28 :
|
|
3814
|
-
d < 64 ? 30 :
|
|
3815
|
-
d < 72 ? 32 :
|
|
3816
|
-
d < 84 ? 34 :
|
|
3817
|
-
d < 96 ? 36 :
|
|
3818
|
-
d < 98 ? 38 :
|
|
3819
|
-
d < 113 ? 40 :
|
|
3820
|
-
d < 149 ? 44 :
|
|
3821
|
-
d < 199 ? 48 :
|
|
3822
|
-
d < 261 ? 52 :
|
|
3823
|
-
d < 353 ? 56 :
|
|
3824
|
-
d < 461 ? 60 :
|
|
3825
|
-
d < 585 ? 64 :
|
|
3826
|
-
d < 1200 ? 70 :
|
|
3827
|
-
d < 1800 ? 80 :
|
|
3828
|
-
d < 2400 ? 90 :
|
|
3829
|
-
100;
|
|
3830
|
-
|
|
3831
|
-
$.ellipseMode = (x) => ($._ellipseMode = x);
|
|
3832
|
-
|
|
3833
|
-
$.ellipse = (x, y, w, h) => {
|
|
3834
|
-
const n = getArcSegments(w == h ? w : Math.max(w, h));
|
|
3835
|
-
|
|
3836
|
-
let a = Math.max(w, 1) / 2;
|
|
3837
|
-
let b = w == h ? a : Math.max(h, 1) / 2;
|
|
3838
|
-
|
|
3839
|
-
let t = 0; // theta
|
|
3840
|
-
const angleIncrement = $.TAU / n;
|
|
3841
|
-
const ci = colorIndex ?? $._fillIndex;
|
|
3842
|
-
if ($._matrixDirty) $._saveMatrix();
|
|
3843
|
-
const ti = $._transformIndex;
|
|
3844
|
-
let vx1, vy1, vx2, vy2;
|
|
3845
|
-
for (let i = 0; i <= n; i++) {
|
|
3846
|
-
vx1 = vx2;
|
|
3847
|
-
vy1 = vy2;
|
|
3848
|
-
vx2 = x + a * Math.cos(t);
|
|
3849
|
-
vy2 = y + b * Math.sin(t);
|
|
3850
|
-
t += angleIncrement;
|
|
3851
|
-
|
|
3852
|
-
if (i == 0) continue;
|
|
3853
|
-
|
|
3854
|
-
verticesStack.push(x, y, ci, ti, vx1, vy1, ci, ti, vx2, vy2, ci, ti);
|
|
3855
|
-
}
|
|
3856
|
-
|
|
3857
|
-
drawStack.push(0, n * 3);
|
|
3858
|
-
};
|
|
3859
|
-
|
|
3860
|
-
$.circle = (x, y, d) => $.ellipse(x, y, d, d);
|
|
3861
|
-
|
|
3862
4026
|
$._hooks.preRender.push(() => {
|
|
3863
|
-
$.pass.setPipeline($.
|
|
3864
|
-
|
|
3865
|
-
const vertices = new Float32Array(verticesStack);
|
|
4027
|
+
$.pass.setPipeline($._pipelines[0]);
|
|
3866
4028
|
|
|
3867
|
-
|
|
3868
|
-
size:
|
|
3869
|
-
usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST
|
|
4029
|
+
let vertexBuffer = Q5.device.createBuffer({
|
|
4030
|
+
size: vertIndex * 4,
|
|
4031
|
+
usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
|
|
4032
|
+
mappedAtCreation: true
|
|
3870
4033
|
});
|
|
3871
4034
|
|
|
3872
|
-
|
|
4035
|
+
new Float32Array(vertexBuffer.getMappedRange()).set(vertexStack.slice(0, vertIndex));
|
|
4036
|
+
vertexBuffer.unmap();
|
|
4037
|
+
|
|
3873
4038
|
$.pass.setVertexBuffer(0, vertexBuffer);
|
|
3874
4039
|
|
|
3875
|
-
|
|
3876
|
-
size:
|
|
3877
|
-
usage: GPUBufferUsage.
|
|
4040
|
+
let indexBuffer = Q5.device.createBuffer({
|
|
4041
|
+
size: idxBufferIndex * 4,
|
|
4042
|
+
usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST,
|
|
4043
|
+
mappedAtCreation: true
|
|
3878
4044
|
});
|
|
3879
4045
|
|
|
3880
|
-
|
|
3881
|
-
|
|
3882
|
-
$._colorsBindGroup = Q5.device.createBindGroup({
|
|
3883
|
-
layout: colorsLayout,
|
|
3884
|
-
entries: [
|
|
3885
|
-
{
|
|
3886
|
-
binding: 0,
|
|
3887
|
-
resource: {
|
|
3888
|
-
buffer: colorsBuffer,
|
|
3889
|
-
offset: 0,
|
|
3890
|
-
size: colorsStack.length * 4
|
|
3891
|
-
}
|
|
3892
|
-
}
|
|
3893
|
-
]
|
|
3894
|
-
});
|
|
4046
|
+
new Uint32Array(indexBuffer.getMappedRange()).set(indexStack.slice(0, idxBufferIndex));
|
|
4047
|
+
indexBuffer.unmap();
|
|
3895
4048
|
|
|
3896
|
-
|
|
3897
|
-
$.pass.setBindGroup(1, $._colorsBindGroup);
|
|
4049
|
+
$.pass.setIndexBuffer(indexBuffer, 'uint32');
|
|
3898
4050
|
});
|
|
3899
4051
|
|
|
3900
4052
|
$._hooks.postRender.push(() => {
|
|
3901
|
-
|
|
4053
|
+
vertIndex = 0;
|
|
4054
|
+
vertCount = 0;
|
|
4055
|
+
idxBufferIndex = 0;
|
|
3902
4056
|
});
|
|
3903
4057
|
};
|
|
3904
4058
|
Q5.renderers.webgpu.image = ($, q) => {
|
|
3905
4059
|
$._textureBindGroups = [];
|
|
3906
|
-
let
|
|
4060
|
+
let vertexStack = [];
|
|
3907
4061
|
|
|
3908
4062
|
let vertexShader = Q5.device.createShaderModule({
|
|
3909
4063
|
label: 'imageVertexShader',
|
|
@@ -3911,15 +4065,14 @@ Q5.renderers.webgpu.image = ($, q) => {
|
|
|
3911
4065
|
struct VertexOutput {
|
|
3912
4066
|
@builtin(position) position: vec4f,
|
|
3913
4067
|
@location(0) texCoord: vec2f
|
|
3914
|
-
}
|
|
3915
|
-
|
|
4068
|
+
}
|
|
3916
4069
|
struct Uniforms {
|
|
3917
4070
|
halfWidth: f32,
|
|
3918
4071
|
halfHeight: f32
|
|
3919
|
-
}
|
|
4072
|
+
}
|
|
3920
4073
|
|
|
3921
4074
|
@group(0) @binding(0) var<uniform> uniforms: Uniforms;
|
|
3922
|
-
@group(0) @binding(1) var<storage
|
|
4075
|
+
@group(0) @binding(1) var<storage> transforms: array<mat4x4<f32>>;
|
|
3923
4076
|
|
|
3924
4077
|
@vertex
|
|
3925
4078
|
fn vertexMain(@location(0) pos: vec2f, @location(1) texCoord: vec2f, @location(2) transformIndex: f32) -> VertexOutput {
|
|
@@ -3980,7 +4133,7 @@ fn fragmentMain(@location(0) texCoord: vec2f) -> @location(0) vec4f {
|
|
|
3980
4133
|
bindGroupLayouts: [...$.bindGroupLayouts, textureLayout]
|
|
3981
4134
|
});
|
|
3982
4135
|
|
|
3983
|
-
$.
|
|
4136
|
+
$._pipelineConfigs[1] = {
|
|
3984
4137
|
label: 'imagePipeline',
|
|
3985
4138
|
layout: pipelineLayout,
|
|
3986
4139
|
vertex: {
|
|
@@ -3991,28 +4144,12 @@ fn fragmentMain(@location(0) texCoord: vec2f) -> @location(0) vec4f {
|
|
|
3991
4144
|
fragment: {
|
|
3992
4145
|
module: fragmentShader,
|
|
3993
4146
|
entryPoint: 'fragmentMain',
|
|
3994
|
-
targets: [
|
|
3995
|
-
{
|
|
3996
|
-
format: 'bgra8unorm',
|
|
3997
|
-
blend: $.blendConfigs?.normal || {
|
|
3998
|
-
color: {
|
|
3999
|
-
srcFactor: 'src-alpha',
|
|
4000
|
-
dstFactor: 'one-minus-src-alpha',
|
|
4001
|
-
operation: 'add'
|
|
4002
|
-
},
|
|
4003
|
-
alpha: {
|
|
4004
|
-
srcFactor: 'src-alpha',
|
|
4005
|
-
dstFactor: 'one-minus-src-alpha',
|
|
4006
|
-
operation: 'add'
|
|
4007
|
-
}
|
|
4008
|
-
}
|
|
4009
|
-
}
|
|
4010
|
-
]
|
|
4147
|
+
targets: [{ format: 'bgra8unorm', blend: $.blendConfigs.normal }]
|
|
4011
4148
|
},
|
|
4012
|
-
primitive: {
|
|
4013
|
-
|
|
4014
|
-
|
|
4015
|
-
|
|
4149
|
+
primitive: { topology: 'triangle-list' }
|
|
4150
|
+
};
|
|
4151
|
+
|
|
4152
|
+
$._pipelines[1] = Q5.device.createRenderPipeline($._pipelineConfigs[1]);
|
|
4016
4153
|
|
|
4017
4154
|
let sampler = Q5.device.createSampler({
|
|
4018
4155
|
magFilter: 'linear',
|
|
@@ -4037,7 +4174,11 @@ fn fragmentMain(@location(0) texCoord: vec2f) -> @location(0) vec4f {
|
|
|
4037
4174
|
|
|
4038
4175
|
Q5.device.queue.copyExternalImageToTexture(
|
|
4039
4176
|
{ source: img },
|
|
4040
|
-
{
|
|
4177
|
+
{
|
|
4178
|
+
texture,
|
|
4179
|
+
colorSpace: $.canvas.colorSpace
|
|
4180
|
+
// premultipliedAlpha: true
|
|
4181
|
+
},
|
|
4041
4182
|
textureSize
|
|
4042
4183
|
);
|
|
4043
4184
|
|
|
@@ -4090,7 +4231,7 @@ fn fragmentMain(@location(0) texCoord: vec2f) -> @location(0) vec4f {
|
|
|
4090
4231
|
let [l, r, t, b] = $._calcBox(x, y, w, h, $._imageMode);
|
|
4091
4232
|
|
|
4092
4233
|
// prettier-ignore
|
|
4093
|
-
|
|
4234
|
+
vertexStack.push(
|
|
4094
4235
|
l, t, 0, 0, ti,
|
|
4095
4236
|
r, t, 1, 0, ti,
|
|
4096
4237
|
l, b, 0, 1, ti,
|
|
@@ -4099,29 +4240,29 @@ fn fragmentMain(@location(0) texCoord: vec2f) -> @location(0) vec4f {
|
|
|
4099
4240
|
r, b, 1, 1, ti
|
|
4100
4241
|
);
|
|
4101
4242
|
|
|
4102
|
-
$.drawStack.push(1, img.textureIndex);
|
|
4243
|
+
$.drawStack.push(1, img.textureIndex, 0);
|
|
4103
4244
|
};
|
|
4104
4245
|
|
|
4105
4246
|
$._hooks.preRender.push(() => {
|
|
4106
4247
|
if (!$._textureBindGroups.length) return;
|
|
4107
4248
|
|
|
4108
4249
|
// Switch to image pipeline
|
|
4109
|
-
$.pass.setPipeline($.
|
|
4110
|
-
|
|
4111
|
-
// Create a vertex buffer for the image quads
|
|
4112
|
-
const vertices = new Float32Array(verticesStack);
|
|
4250
|
+
$.pass.setPipeline($._pipelines[1]);
|
|
4113
4251
|
|
|
4114
4252
|
const vertexBuffer = Q5.device.createBuffer({
|
|
4115
|
-
size:
|
|
4116
|
-
usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST
|
|
4253
|
+
size: vertexStack.length * 4,
|
|
4254
|
+
usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
|
|
4255
|
+
mappedAtCreation: true
|
|
4117
4256
|
});
|
|
4118
4257
|
|
|
4119
|
-
|
|
4258
|
+
new Float32Array(vertexBuffer.getMappedRange()).set(vertices);
|
|
4259
|
+
vertexBuffer.unmap();
|
|
4260
|
+
|
|
4120
4261
|
$.pass.setVertexBuffer(1, vertexBuffer);
|
|
4121
4262
|
});
|
|
4122
4263
|
|
|
4123
4264
|
$._hooks.postRender.push(() => {
|
|
4124
|
-
|
|
4265
|
+
vertexStack.length = 0;
|
|
4125
4266
|
});
|
|
4126
4267
|
};
|
|
4127
4268
|
|
|
@@ -4142,35 +4283,35 @@ const pos = array(vec2f(0, -1), vec2f(1, -1), vec2f(0, 0), vec2f(1, 0));
|
|
|
4142
4283
|
|
|
4143
4284
|
struct VertexInput {
|
|
4144
4285
|
@builtin(vertex_index) vertex : u32,
|
|
4145
|
-
@builtin(instance_index) instance : u32
|
|
4146
|
-
}
|
|
4286
|
+
@builtin(instance_index) instance : u32
|
|
4287
|
+
}
|
|
4147
4288
|
struct VertexOutput {
|
|
4148
4289
|
@builtin(position) position : vec4f,
|
|
4149
|
-
@location(0)
|
|
4150
|
-
@location(1)
|
|
4151
|
-
}
|
|
4290
|
+
@location(0) texCoord : vec2f,
|
|
4291
|
+
@location(1) fillColor : vec4f
|
|
4292
|
+
}
|
|
4152
4293
|
struct Char {
|
|
4153
4294
|
texOffset: vec2f,
|
|
4154
4295
|
texExtent: vec2f,
|
|
4155
4296
|
size: vec2f,
|
|
4156
4297
|
offset: vec2f,
|
|
4157
|
-
}
|
|
4298
|
+
}
|
|
4158
4299
|
struct Text {
|
|
4159
4300
|
pos: vec2f,
|
|
4160
4301
|
scale: f32,
|
|
4161
4302
|
transformIndex: f32,
|
|
4162
4303
|
fillIndex: f32,
|
|
4163
4304
|
strokeIndex: f32
|
|
4164
|
-
}
|
|
4305
|
+
}
|
|
4165
4306
|
struct Uniforms {
|
|
4166
4307
|
halfWidth: f32,
|
|
4167
4308
|
halfHeight: f32
|
|
4168
|
-
}
|
|
4309
|
+
}
|
|
4169
4310
|
|
|
4170
4311
|
@group(0) @binding(0) var<uniform> uniforms: Uniforms;
|
|
4171
|
-
@group(0) @binding(1) var<storage
|
|
4312
|
+
@group(0) @binding(1) var<storage> transforms: array<mat4x4<f32>>;
|
|
4172
4313
|
|
|
4173
|
-
@group(1) @binding(0) var<storage
|
|
4314
|
+
@group(1) @binding(0) var<storage> colors : array<vec4f>;
|
|
4174
4315
|
|
|
4175
4316
|
@group(2) @binding(0) var fontTexture: texture_2d<f32>;
|
|
4176
4317
|
@group(2) @binding(1) var fontSampler: sampler;
|
|
@@ -4196,13 +4337,13 @@ fn vertexMain(input : VertexInput) -> VertexOutput {
|
|
|
4196
4337
|
|
|
4197
4338
|
var output : VertexOutput;
|
|
4198
4339
|
output.position = vert;
|
|
4199
|
-
output.
|
|
4200
|
-
output.
|
|
4340
|
+
output.texCoord = (pos[input.vertex] * vec2f(1, -1)) * fontChar.texExtent + fontChar.texOffset;
|
|
4341
|
+
output.fillColor = colors[i32(text.fillIndex)];
|
|
4201
4342
|
return output;
|
|
4202
4343
|
}
|
|
4203
4344
|
|
|
4204
|
-
fn sampleMsdf(
|
|
4205
|
-
let c = textureSample(fontTexture, fontSampler,
|
|
4345
|
+
fn sampleMsdf(texCoord: vec2f) -> f32 {
|
|
4346
|
+
let c = textureSample(fontTexture, fontSampler, texCoord);
|
|
4206
4347
|
return max(min(c.r, c.g), min(max(c.r, c.g), c.b));
|
|
4207
4348
|
}
|
|
4208
4349
|
|
|
@@ -4212,25 +4353,83 @@ fn fragmentMain(input : VertexOutput) -> @location(0) vec4f {
|
|
|
4212
4353
|
// uses the default which is 4.
|
|
4213
4354
|
let pxRange = 4.0;
|
|
4214
4355
|
let sz = vec2f(textureDimensions(fontTexture, 0));
|
|
4215
|
-
let dx = sz.x*length(vec2f(dpdxFine(input.
|
|
4216
|
-
let dy = sz.y*length(vec2f(dpdxFine(input.
|
|
4356
|
+
let dx = sz.x*length(vec2f(dpdxFine(input.texCoord.x), dpdyFine(input.texCoord.x)));
|
|
4357
|
+
let dy = sz.y*length(vec2f(dpdxFine(input.texCoord.y), dpdyFine(input.texCoord.y)));
|
|
4217
4358
|
let toPixels = pxRange * inverseSqrt(dx * dx + dy * dy);
|
|
4218
|
-
let sigDist = sampleMsdf(input.
|
|
4359
|
+
let sigDist = sampleMsdf(input.texCoord) - 0.5;
|
|
4219
4360
|
let pxDist = sigDist * toPixels;
|
|
4220
4361
|
let edgeWidth = 0.5;
|
|
4221
4362
|
let alpha = smoothstep(-edgeWidth, edgeWidth, pxDist);
|
|
4222
4363
|
if (alpha < 0.001) {
|
|
4223
4364
|
discard;
|
|
4224
4365
|
}
|
|
4225
|
-
|
|
4226
|
-
return vec4f(fillColor.rgb, fillColor.a * alpha);
|
|
4366
|
+
return vec4f(input.fillColor.rgb, input.fillColor.a * alpha);
|
|
4227
4367
|
}
|
|
4228
4368
|
`
|
|
4229
4369
|
});
|
|
4230
4370
|
|
|
4371
|
+
let textBindGroupLayout = Q5.device.createBindGroupLayout({
|
|
4372
|
+
label: 'MSDF text group layout',
|
|
4373
|
+
entries: [
|
|
4374
|
+
{
|
|
4375
|
+
binding: 0,
|
|
4376
|
+
visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT,
|
|
4377
|
+
buffer: { type: 'read-only-storage' }
|
|
4378
|
+
},
|
|
4379
|
+
{
|
|
4380
|
+
binding: 1,
|
|
4381
|
+
visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT,
|
|
4382
|
+
buffer: { type: 'read-only-storage' }
|
|
4383
|
+
}
|
|
4384
|
+
]
|
|
4385
|
+
});
|
|
4386
|
+
|
|
4387
|
+
let fontSampler = Q5.device.createSampler({
|
|
4388
|
+
minFilter: 'linear',
|
|
4389
|
+
magFilter: 'linear',
|
|
4390
|
+
mipmapFilter: 'linear',
|
|
4391
|
+
maxAnisotropy: 16
|
|
4392
|
+
});
|
|
4393
|
+
let fontBindGroupLayout = Q5.device.createBindGroupLayout({
|
|
4394
|
+
label: 'MSDF font group layout',
|
|
4395
|
+
entries: [
|
|
4396
|
+
{
|
|
4397
|
+
binding: 0,
|
|
4398
|
+
visibility: GPUShaderStage.FRAGMENT,
|
|
4399
|
+
texture: {}
|
|
4400
|
+
},
|
|
4401
|
+
{
|
|
4402
|
+
binding: 1,
|
|
4403
|
+
visibility: GPUShaderStage.FRAGMENT,
|
|
4404
|
+
sampler: {}
|
|
4405
|
+
},
|
|
4406
|
+
{
|
|
4407
|
+
binding: 2,
|
|
4408
|
+
visibility: GPUShaderStage.VERTEX,
|
|
4409
|
+
buffer: { type: 'read-only-storage' }
|
|
4410
|
+
}
|
|
4411
|
+
]
|
|
4412
|
+
});
|
|
4413
|
+
|
|
4414
|
+
let fontPipelineLayout = Q5.device.createPipelineLayout({
|
|
4415
|
+
bindGroupLayouts: [...$.bindGroupLayouts, fontBindGroupLayout, textBindGroupLayout]
|
|
4416
|
+
});
|
|
4417
|
+
|
|
4418
|
+
$._pipelineConfigs[2] = {
|
|
4419
|
+
label: 'msdf font pipeline',
|
|
4420
|
+
layout: fontPipelineLayout,
|
|
4421
|
+
vertex: { module: textShader, entryPoint: 'vertexMain' },
|
|
4422
|
+
fragment: {
|
|
4423
|
+
module: textShader,
|
|
4424
|
+
entryPoint: 'fragmentMain',
|
|
4425
|
+
targets: [{ format: 'bgra8unorm', blend: $.blendConfigs.normal }]
|
|
4426
|
+
},
|
|
4427
|
+
primitive: { topology: 'triangle-strip', stripIndexFormat: 'uint32' }
|
|
4428
|
+
};
|
|
4429
|
+
$._pipelines[2] = Q5.device.createRenderPipeline($._pipelineConfigs[2]);
|
|
4430
|
+
|
|
4231
4431
|
class MsdfFont {
|
|
4232
|
-
constructor(
|
|
4233
|
-
this.pipeline = pipeline;
|
|
4432
|
+
constructor(bindGroup, lineHeight, chars, kernings) {
|
|
4234
4433
|
this.bindGroup = bindGroup;
|
|
4235
4434
|
this.lineHeight = lineHeight;
|
|
4236
4435
|
this.chars = chars;
|
|
@@ -4256,22 +4455,7 @@ fn fragmentMain(input : VertexOutput) -> @location(0) vec4f {
|
|
|
4256
4455
|
}
|
|
4257
4456
|
}
|
|
4258
4457
|
|
|
4259
|
-
|
|
4260
|
-
label: 'MSDF text group layout',
|
|
4261
|
-
entries: [
|
|
4262
|
-
{
|
|
4263
|
-
binding: 0,
|
|
4264
|
-
visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT,
|
|
4265
|
-
buffer: { type: 'read-only-storage' }
|
|
4266
|
-
},
|
|
4267
|
-
{
|
|
4268
|
-
binding: 1,
|
|
4269
|
-
visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT,
|
|
4270
|
-
buffer: { type: 'read-only-storage' }
|
|
4271
|
-
}
|
|
4272
|
-
]
|
|
4273
|
-
});
|
|
4274
|
-
|
|
4458
|
+
$._fonts = [];
|
|
4275
4459
|
let fonts = {};
|
|
4276
4460
|
|
|
4277
4461
|
let createFont = async (fontJsonUrl, fontName, cb) => {
|
|
@@ -4334,74 +4518,11 @@ fn fragmentMain(input : VertexOutput) -> @location(0) vec4f {
|
|
|
4334
4518
|
}
|
|
4335
4519
|
charsBuffer.unmap();
|
|
4336
4520
|
|
|
4337
|
-
let fontSampler = Q5.device.createSampler({
|
|
4338
|
-
minFilter: 'linear',
|
|
4339
|
-
magFilter: 'linear',
|
|
4340
|
-
mipmapFilter: 'linear',
|
|
4341
|
-
maxAnisotropy: 16
|
|
4342
|
-
});
|
|
4343
|
-
let fontBindGroupLayout = Q5.device.createBindGroupLayout({
|
|
4344
|
-
label: 'MSDF font group layout',
|
|
4345
|
-
entries: [
|
|
4346
|
-
{
|
|
4347
|
-
binding: 0,
|
|
4348
|
-
visibility: GPUShaderStage.FRAGMENT,
|
|
4349
|
-
texture: {}
|
|
4350
|
-
},
|
|
4351
|
-
{
|
|
4352
|
-
binding: 1,
|
|
4353
|
-
visibility: GPUShaderStage.FRAGMENT,
|
|
4354
|
-
sampler: {}
|
|
4355
|
-
},
|
|
4356
|
-
{
|
|
4357
|
-
binding: 2,
|
|
4358
|
-
visibility: GPUShaderStage.VERTEX,
|
|
4359
|
-
buffer: { type: 'read-only-storage' }
|
|
4360
|
-
}
|
|
4361
|
-
]
|
|
4362
|
-
});
|
|
4363
|
-
let fontPipeline = Q5.device.createRenderPipeline({
|
|
4364
|
-
label: 'msdf font pipeline',
|
|
4365
|
-
layout: Q5.device.createPipelineLayout({
|
|
4366
|
-
bindGroupLayouts: [...$.bindGroupLayouts, fontBindGroupLayout, textBindGroupLayout]
|
|
4367
|
-
}),
|
|
4368
|
-
vertex: {
|
|
4369
|
-
module: textShader,
|
|
4370
|
-
entryPoint: 'vertexMain'
|
|
4371
|
-
},
|
|
4372
|
-
fragment: {
|
|
4373
|
-
module: textShader,
|
|
4374
|
-
entryPoint: 'fragmentMain',
|
|
4375
|
-
targets: [
|
|
4376
|
-
{
|
|
4377
|
-
format: 'bgra8unorm',
|
|
4378
|
-
blend: {
|
|
4379
|
-
color: {
|
|
4380
|
-
srcFactor: 'src-alpha',
|
|
4381
|
-
dstFactor: 'one-minus-src-alpha'
|
|
4382
|
-
},
|
|
4383
|
-
alpha: {
|
|
4384
|
-
srcFactor: 'one',
|
|
4385
|
-
dstFactor: 'one'
|
|
4386
|
-
}
|
|
4387
|
-
}
|
|
4388
|
-
}
|
|
4389
|
-
]
|
|
4390
|
-
},
|
|
4391
|
-
primitive: {
|
|
4392
|
-
topology: 'triangle-strip',
|
|
4393
|
-
stripIndexFormat: 'uint32'
|
|
4394
|
-
}
|
|
4395
|
-
});
|
|
4396
|
-
|
|
4397
4521
|
let fontBindGroup = Q5.device.createBindGroup({
|
|
4398
4522
|
label: 'msdf font bind group',
|
|
4399
4523
|
layout: fontBindGroupLayout,
|
|
4400
4524
|
entries: [
|
|
4401
|
-
{
|
|
4402
|
-
binding: 0,
|
|
4403
|
-
resource: texture.createView()
|
|
4404
|
-
},
|
|
4525
|
+
{ binding: 0, resource: texture.createView() },
|
|
4405
4526
|
{ binding: 1, resource: fontSampler },
|
|
4406
4527
|
{ binding: 2, resource: { buffer: charsBuffer } }
|
|
4407
4528
|
]
|
|
@@ -4419,10 +4540,11 @@ fn fragmentMain(input : VertexOutput) -> @location(0) vec4f {
|
|
|
4419
4540
|
}
|
|
4420
4541
|
}
|
|
4421
4542
|
|
|
4422
|
-
$._font = new MsdfFont(
|
|
4543
|
+
$._font = new MsdfFont(fontBindGroup, atlas.common.lineHeight, chars, kernings);
|
|
4423
4544
|
|
|
4545
|
+
$._font.index = $._fonts.length;
|
|
4546
|
+
$._fonts.push($._font);
|
|
4424
4547
|
fonts[fontName] = $._font;
|
|
4425
|
-
$.pipelines[2] = $._font.pipeline;
|
|
4426
4548
|
|
|
4427
4549
|
q._preloadCount--;
|
|
4428
4550
|
|
|
@@ -4451,12 +4573,6 @@ fn fragmentMain(input : VertexOutput) -> @location(0) vec4f {
|
|
|
4451
4573
|
|
|
4452
4574
|
$.textFont = (fontName) => {
|
|
4453
4575
|
$._font = fonts[fontName];
|
|
4454
|
-
|
|
4455
|
-
// replay the change of font in the draw stack
|
|
4456
|
-
$.drawStack.push(-1, () => {
|
|
4457
|
-
$._font = fonts[fontName];
|
|
4458
|
-
$.pipelines[2] = $._font.pipeline;
|
|
4459
|
-
});
|
|
4460
4576
|
};
|
|
4461
4577
|
$.textSize = (size) => {
|
|
4462
4578
|
$._textSize = size;
|
|
@@ -4569,7 +4685,7 @@ fn fragmentMain(input : VertexOutput) -> @location(0) vec4f {
|
|
|
4569
4685
|
}
|
|
4570
4686
|
}
|
|
4571
4687
|
|
|
4572
|
-
let charsData =
|
|
4688
|
+
let charsData = [];
|
|
4573
4689
|
|
|
4574
4690
|
let ta = $._textAlign,
|
|
4575
4691
|
tb = $._textBaseline,
|
|
@@ -4615,7 +4731,7 @@ fn fragmentMain(input : VertexOutput) -> @location(0) vec4f {
|
|
|
4615
4731
|
}
|
|
4616
4732
|
$._charStack.push(charsData);
|
|
4617
4733
|
|
|
4618
|
-
let text =
|
|
4734
|
+
let text = [];
|
|
4619
4735
|
|
|
4620
4736
|
if ($._matrixDirty) $._saveMatrix();
|
|
4621
4737
|
|
|
@@ -4623,11 +4739,11 @@ fn fragmentMain(input : VertexOutput) -> @location(0) vec4f {
|
|
|
4623
4739
|
text[1] = -y;
|
|
4624
4740
|
text[2] = $._textSize / 44;
|
|
4625
4741
|
text[3] = $._transformIndex;
|
|
4626
|
-
text[4] = $._fillIndex;
|
|
4742
|
+
text[4] = $._fillSet ? $._fillIndex : 0;
|
|
4627
4743
|
text[5] = $._strokeIndex;
|
|
4628
4744
|
|
|
4629
4745
|
$._textStack.push(text);
|
|
4630
|
-
$.drawStack.push(2, measurements.printedCharCount);
|
|
4746
|
+
$.drawStack.push(2, measurements.printedCharCount, $._font.index);
|
|
4631
4747
|
};
|
|
4632
4748
|
|
|
4633
4749
|
$.textWidth = (str) => {
|
|
@@ -4640,11 +4756,11 @@ fn fragmentMain(input : VertexOutput) -> @location(0) vec4f {
|
|
|
4640
4756
|
|
|
4641
4757
|
if ($._doFill) {
|
|
4642
4758
|
let fi = $._fillIndex * 4;
|
|
4643
|
-
g.fill(
|
|
4759
|
+
g.fill(colorStack.slice(fi, fi + 4));
|
|
4644
4760
|
}
|
|
4645
4761
|
if ($._doStroke) {
|
|
4646
4762
|
let si = $._strokeIndex * 4;
|
|
4647
|
-
g.stroke(
|
|
4763
|
+
g.stroke(colorStack.slice(si, si + 4));
|
|
4648
4764
|
}
|
|
4649
4765
|
|
|
4650
4766
|
let img = g.createTextImage(str, w, h);
|
|
@@ -4695,21 +4811,15 @@ fn fragmentMain(input : VertexOutput) -> @location(0) vec4f {
|
|
|
4695
4811
|
totalTextSize += charsData.length * 4;
|
|
4696
4812
|
}
|
|
4697
4813
|
|
|
4698
|
-
// Create a single buffer for all
|
|
4814
|
+
// Create a single buffer for all char data
|
|
4699
4815
|
let charBuffer = Q5.device.createBuffer({
|
|
4700
|
-
label: 'charBuffer',
|
|
4701
4816
|
size: totalTextSize,
|
|
4702
4817
|
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
|
|
4703
4818
|
mappedAtCreation: true
|
|
4704
4819
|
});
|
|
4705
4820
|
|
|
4706
4821
|
// Copy all text data into the buffer
|
|
4707
|
-
|
|
4708
|
-
let o = 0;
|
|
4709
|
-
for (let array of $._charStack) {
|
|
4710
|
-
textArray.set(array, o);
|
|
4711
|
-
o += array.length;
|
|
4712
|
-
}
|
|
4822
|
+
new Float32Array(charBuffer.getMappedRange()).set($._charStack.flat());
|
|
4713
4823
|
charBuffer.unmap();
|
|
4714
4824
|
|
|
4715
4825
|
// Calculate total buffer size for metadata
|
|
@@ -4724,12 +4834,7 @@ fn fragmentMain(input : VertexOutput) -> @location(0) vec4f {
|
|
|
4724
4834
|
});
|
|
4725
4835
|
|
|
4726
4836
|
// Copy all metadata into the buffer
|
|
4727
|
-
|
|
4728
|
-
o = 0;
|
|
4729
|
-
for (let array of $._textStack) {
|
|
4730
|
-
metadataArray.set(array, o);
|
|
4731
|
-
o += array.length;
|
|
4732
|
-
}
|
|
4837
|
+
new Float32Array(textBuffer.getMappedRange()).set($._textStack.flat());
|
|
4733
4838
|
textBuffer.unmap();
|
|
4734
4839
|
|
|
4735
4840
|
// Create a single bind group for the text buffer and metadata buffer
|
|
@@ -4737,14 +4842,8 @@ fn fragmentMain(input : VertexOutput) -> @location(0) vec4f {
|
|
|
4737
4842
|
label: 'msdf text bind group',
|
|
4738
4843
|
layout: textBindGroupLayout,
|
|
4739
4844
|
entries: [
|
|
4740
|
-
{
|
|
4741
|
-
|
|
4742
|
-
resource: { buffer: charBuffer }
|
|
4743
|
-
},
|
|
4744
|
-
{
|
|
4745
|
-
binding: 1,
|
|
4746
|
-
resource: { buffer: textBuffer }
|
|
4747
|
-
}
|
|
4845
|
+
{ binding: 0, resource: { buffer: charBuffer } },
|
|
4846
|
+
{ binding: 1, resource: { buffer: textBuffer } }
|
|
4748
4847
|
]
|
|
4749
4848
|
});
|
|
4750
4849
|
});
|