q5 2.12.12 → 2.13.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/package.json +1 -1
- package/q5.js +162 -129
- package/q5.min.js +2 -2
- package/src/q5-core.js +1 -1
- package/src/q5-webgpu-canvas.js +69 -50
- package/src/q5-webgpu-drawing.js +9 -9
- package/src/q5-webgpu-image.js +74 -58
- package/src/q5-webgpu-text.js +9 -11
package/src/q5-webgpu-canvas.js
CHANGED
|
@@ -11,6 +11,9 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
11
11
|
c.width = $.width = 500;
|
|
12
12
|
c.height = $.height = 500;
|
|
13
13
|
|
|
14
|
+
// q2d graphics context
|
|
15
|
+
$._g = $.createGraphics(1, 1);
|
|
16
|
+
|
|
14
17
|
if ($.colorMode) $.colorMode('rgb', 1);
|
|
15
18
|
|
|
16
19
|
let pass,
|
|
@@ -146,7 +149,7 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
146
149
|
};
|
|
147
150
|
|
|
148
151
|
$._stroke = 0;
|
|
149
|
-
$._fill = 1;
|
|
152
|
+
$._fill = $._tint = 1;
|
|
150
153
|
$._doFill = $._doStroke = true;
|
|
151
154
|
|
|
152
155
|
$.fill = (r, g, b, a) => {
|
|
@@ -159,42 +162,51 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
159
162
|
$._doStroke = $._strokeSet = true;
|
|
160
163
|
$._stroke = colorIndex;
|
|
161
164
|
};
|
|
165
|
+
$.tint = (r, g, b, a) => {
|
|
166
|
+
addColor(r, g, b, a);
|
|
167
|
+
$._tint = colorIndex;
|
|
168
|
+
};
|
|
162
169
|
|
|
163
170
|
$.noFill = () => ($._doFill = false);
|
|
164
171
|
$.noStroke = () => ($._doStroke = false);
|
|
172
|
+
$.noTint = () => ($._tint = 1);
|
|
165
173
|
|
|
166
174
|
$._strokeWeight = 1;
|
|
167
175
|
$.strokeWeight = (v) => ($._strokeWeight = Math.abs(v));
|
|
168
176
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
0, 1, 0, 0,
|
|
176
|
-
0, 0, 1, 0,
|
|
177
|
-
0, 0, 0, 1
|
|
178
|
-
];
|
|
179
|
-
$._transformIndex = 0;
|
|
180
|
-
};
|
|
181
|
-
$.resetMatrix();
|
|
177
|
+
const MAX_TRANSFORMS = 1e7, // or whatever maximum you need
|
|
178
|
+
MATRIX_SIZE = 16, // 4x4 matrix
|
|
179
|
+
transforms = new Float32Array(MAX_TRANSFORMS * MATRIX_SIZE),
|
|
180
|
+
matrices = [],
|
|
181
|
+
matricesIndexStack = [];
|
|
182
|
+
let matrix;
|
|
182
183
|
|
|
183
184
|
// tracks if the matrix has been modified
|
|
184
185
|
$._matrixDirty = false;
|
|
185
186
|
|
|
186
|
-
//
|
|
187
|
-
|
|
187
|
+
// initialize with a 4x4 identity matrix
|
|
188
|
+
// prettier-ignore
|
|
189
|
+
matrices.push([
|
|
190
|
+
1, 0, 0, 0,
|
|
191
|
+
0, 1, 0, 0,
|
|
192
|
+
0, 0, 1, 0,
|
|
193
|
+
0, 0, 0, 1
|
|
194
|
+
]);
|
|
195
|
+
|
|
196
|
+
transforms.set(matrices[0]);
|
|
188
197
|
|
|
189
|
-
|
|
190
|
-
|
|
198
|
+
$.resetMatrix = () => {
|
|
199
|
+
matrix = matrices[0].slice();
|
|
200
|
+
$._matrixIndex = 0;
|
|
201
|
+
};
|
|
202
|
+
$.resetMatrix();
|
|
191
203
|
|
|
192
204
|
$.translate = (x, y, z) => {
|
|
193
205
|
if (!x && !y && !z) return;
|
|
194
206
|
// update the translation values
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
207
|
+
matrix[12] += x;
|
|
208
|
+
matrix[13] -= y;
|
|
209
|
+
matrix[14] += z || 0;
|
|
198
210
|
$._matrixDirty = true;
|
|
199
211
|
};
|
|
200
212
|
|
|
@@ -205,7 +217,7 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
205
217
|
let cosR = Math.cos(a);
|
|
206
218
|
let sinR = Math.sin(a);
|
|
207
219
|
|
|
208
|
-
let m =
|
|
220
|
+
let m = matrix;
|
|
209
221
|
|
|
210
222
|
let m0 = m[0],
|
|
211
223
|
m1 = m[1],
|
|
@@ -232,7 +244,7 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
232
244
|
$.scale = (x = 1, y, z = 1) => {
|
|
233
245
|
y ??= x;
|
|
234
246
|
|
|
235
|
-
let m =
|
|
247
|
+
let m = matrix;
|
|
236
248
|
|
|
237
249
|
m[0] *= x;
|
|
238
250
|
m[1] *= x;
|
|
@@ -256,13 +268,13 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
256
268
|
|
|
257
269
|
let tanAng = Math.tan(ang);
|
|
258
270
|
|
|
259
|
-
let m0 =
|
|
260
|
-
m1 =
|
|
261
|
-
m4 =
|
|
262
|
-
m5 =
|
|
271
|
+
let m0 = matrix[0],
|
|
272
|
+
m1 = matrix[1],
|
|
273
|
+
m4 = matrix[4],
|
|
274
|
+
m5 = matrix[5];
|
|
263
275
|
|
|
264
|
-
|
|
265
|
-
|
|
276
|
+
matrix[0] = m0 + m4 * tanAng;
|
|
277
|
+
matrix[1] = m1 + m5 * tanAng;
|
|
266
278
|
|
|
267
279
|
$._matrixDirty = true;
|
|
268
280
|
};
|
|
@@ -273,13 +285,13 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
273
285
|
|
|
274
286
|
let tanAng = Math.tan(ang);
|
|
275
287
|
|
|
276
|
-
let m0 =
|
|
277
|
-
m1 =
|
|
278
|
-
m4 =
|
|
279
|
-
m5 =
|
|
288
|
+
let m0 = matrix[0],
|
|
289
|
+
m1 = matrix[1],
|
|
290
|
+
m4 = matrix[4],
|
|
291
|
+
m5 = matrix[5];
|
|
280
292
|
|
|
281
|
-
|
|
282
|
-
|
|
293
|
+
matrix[4] = m4 + m0 * tanAng;
|
|
294
|
+
matrix[5] = m5 + m1 * tanAng;
|
|
283
295
|
|
|
284
296
|
$._matrixDirty = true;
|
|
285
297
|
};
|
|
@@ -297,31 +309,32 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
297
309
|
}
|
|
298
310
|
|
|
299
311
|
// overwrite the current transformation matrix
|
|
300
|
-
|
|
312
|
+
matrix = m.slice();
|
|
301
313
|
$._matrixDirty = true;
|
|
302
314
|
};
|
|
303
315
|
|
|
304
316
|
// function to save the current matrix state if dirty
|
|
305
317
|
$._saveMatrix = () => {
|
|
306
|
-
|
|
307
|
-
$.
|
|
318
|
+
transforms.set(matrix, matrices.length * MATRIX_SIZE);
|
|
319
|
+
$._matrixIndex = matrices.length;
|
|
320
|
+
matrices.push(matrix.slice());
|
|
308
321
|
$._matrixDirty = false;
|
|
309
322
|
};
|
|
310
323
|
|
|
311
324
|
// push the current matrix index onto the stack
|
|
312
325
|
$.pushMatrix = () => {
|
|
313
326
|
if ($._matrixDirty) $._saveMatrix();
|
|
314
|
-
|
|
327
|
+
matricesIndexStack.push($._matrixIndex);
|
|
315
328
|
};
|
|
316
329
|
|
|
317
330
|
$.popMatrix = () => {
|
|
318
|
-
if (
|
|
331
|
+
if (!matricesIndexStack.length) {
|
|
319
332
|
return console.warn('Matrix index stack is empty!');
|
|
320
333
|
}
|
|
321
334
|
// pop the last matrix index and set it as the current matrix index
|
|
322
|
-
let idx =
|
|
323
|
-
|
|
324
|
-
$.
|
|
335
|
+
let idx = matricesIndexStack.pop();
|
|
336
|
+
matrix = matrices[idx].slice();
|
|
337
|
+
$._matrixIndex = idx;
|
|
325
338
|
$._matrixDirty = false;
|
|
326
339
|
};
|
|
327
340
|
|
|
@@ -446,14 +459,14 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
446
459
|
};
|
|
447
460
|
|
|
448
461
|
$._render = () => {
|
|
449
|
-
if (
|
|
462
|
+
if (matrices.length > 1 || !$._transformBindGroup) {
|
|
450
463
|
let transformBuffer = Q5.device.createBuffer({
|
|
451
|
-
size:
|
|
464
|
+
size: matrices.length * MATRIX_SIZE * 4, // 4 bytes per float
|
|
452
465
|
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
|
|
453
466
|
mappedAtCreation: true
|
|
454
467
|
});
|
|
455
468
|
|
|
456
|
-
new Float32Array(transformBuffer.getMappedRange()).set(
|
|
469
|
+
new Float32Array(transformBuffer.getMappedRange()).set(transforms.slice(0, matrices.length * MATRIX_SIZE));
|
|
457
470
|
transformBuffer.unmap();
|
|
458
471
|
|
|
459
472
|
$._transformBindGroup = Q5.device.createBindGroup({
|
|
@@ -481,7 +494,7 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
481
494
|
entries: [{ binding: 0, resource: { buffer: colorsBuffer } }]
|
|
482
495
|
});
|
|
483
496
|
|
|
484
|
-
|
|
497
|
+
pass.setBindGroup(1, $._colorsBindGroup);
|
|
485
498
|
|
|
486
499
|
for (let m of $._hooks.preRender) m();
|
|
487
500
|
|
|
@@ -505,6 +518,7 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
505
518
|
pass.draw(v, 1, drawVertOffset);
|
|
506
519
|
drawVertOffset += v;
|
|
507
520
|
} else if (curPipelineIndex == 1) {
|
|
521
|
+
// let vertCount = drawStack[i + 2];
|
|
508
522
|
// draw images
|
|
509
523
|
if (curTextureIndex != v) {
|
|
510
524
|
// v is the texture index
|
|
@@ -512,6 +526,7 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
512
526
|
}
|
|
513
527
|
pass.draw(4, 1, imageVertOffset);
|
|
514
528
|
imageVertOffset += 4;
|
|
529
|
+
// i++;
|
|
515
530
|
} else if (curPipelineIndex == 2) {
|
|
516
531
|
// draw text
|
|
517
532
|
let o = drawStack[i + 2];
|
|
@@ -540,8 +555,9 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
540
555
|
colorIndex = 1;
|
|
541
556
|
colorStackIndex = 8;
|
|
542
557
|
rotation = 0;
|
|
543
|
-
|
|
544
|
-
|
|
558
|
+
transforms.length = MATRIX_SIZE;
|
|
559
|
+
matrices.length = 1;
|
|
560
|
+
matricesIndexStack.length = 0;
|
|
545
561
|
};
|
|
546
562
|
};
|
|
547
563
|
|
|
@@ -552,7 +568,10 @@ Q5.initWebGPU = async () => {
|
|
|
552
568
|
}
|
|
553
569
|
if (!Q5.device) {
|
|
554
570
|
let adapter = await navigator.gpu.requestAdapter();
|
|
555
|
-
if (!adapter)
|
|
571
|
+
if (!adapter) {
|
|
572
|
+
console.warn('q5 WebGPU could not start! No appropriate GPUAdapter found, vulkan may need to be enabled.');
|
|
573
|
+
return false;
|
|
574
|
+
}
|
|
556
575
|
Q5.device = await adapter.requestDevice();
|
|
557
576
|
}
|
|
558
577
|
return true;
|
package/src/q5-webgpu-drawing.js
CHANGED
|
@@ -10,7 +10,7 @@ Q5.renderers.webgpu.drawing = ($, q) => {
|
|
|
10
10
|
struct VertexInput {
|
|
11
11
|
@location(0) pos: vec2f,
|
|
12
12
|
@location(1) colorIndex: f32,
|
|
13
|
-
@location(2)
|
|
13
|
+
@location(2) matrixIndex: f32
|
|
14
14
|
}
|
|
15
15
|
struct VertexOutput {
|
|
16
16
|
@builtin(position) position: vec4f,
|
|
@@ -29,7 +29,7 @@ struct Uniforms {
|
|
|
29
29
|
@vertex
|
|
30
30
|
fn vertexMain(input: VertexInput) -> VertexOutput {
|
|
31
31
|
var vert = vec4f(input.pos, 0.0, 1.0);
|
|
32
|
-
vert = transforms[i32(input.
|
|
32
|
+
vert = transforms[i32(input.matrixIndex)] * vert;
|
|
33
33
|
vert.x /= uniforms.halfWidth;
|
|
34
34
|
vert.y /= uniforms.halfHeight;
|
|
35
35
|
|
|
@@ -56,7 +56,7 @@ fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
|
|
|
56
56
|
attributes: [
|
|
57
57
|
{ format: 'float32x2', offset: 0, shaderLocation: 0 }, // position
|
|
58
58
|
{ format: 'float32', offset: 8, shaderLocation: 1 }, // colorIndex
|
|
59
|
-
{ format: 'float32', offset: 12, shaderLocation: 2 } //
|
|
59
|
+
{ format: 'float32', offset: 12, shaderLocation: 2 } // matrixIndex
|
|
60
60
|
]
|
|
61
61
|
};
|
|
62
62
|
|
|
@@ -210,7 +210,7 @@ fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
|
|
|
210
210
|
let [l, r, t, b] = $._calcBox(x, y, w, h, $._rectMode);
|
|
211
211
|
let ci, ti;
|
|
212
212
|
if ($._matrixDirty) $._saveMatrix();
|
|
213
|
-
ti = $.
|
|
213
|
+
ti = $._matrixIndex;
|
|
214
214
|
|
|
215
215
|
if ($._doFill) {
|
|
216
216
|
ci = $._fill;
|
|
@@ -284,7 +284,7 @@ fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
|
|
|
284
284
|
let b = w == h ? a : Math.max(h, 1) / 2;
|
|
285
285
|
|
|
286
286
|
if ($._matrixDirty) $._saveMatrix();
|
|
287
|
-
let ti = $.
|
|
287
|
+
let ti = $._matrixIndex;
|
|
288
288
|
|
|
289
289
|
if ($._doFill) {
|
|
290
290
|
addEllipse(x, y, a, b, n, $._fill, ti);
|
|
@@ -300,7 +300,7 @@ fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
|
|
|
300
300
|
|
|
301
301
|
$.point = (x, y) => {
|
|
302
302
|
if ($._matrixDirty) $._saveMatrix();
|
|
303
|
-
let ti = $.
|
|
303
|
+
let ti = $._matrixIndex,
|
|
304
304
|
ci = $._stroke,
|
|
305
305
|
sw = $._strokeWeight;
|
|
306
306
|
|
|
@@ -320,7 +320,7 @@ fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
|
|
|
320
320
|
|
|
321
321
|
$.line = (x1, y1, x2, y2) => {
|
|
322
322
|
if ($._matrixDirty) $._saveMatrix();
|
|
323
|
-
let ti = $.
|
|
323
|
+
let ti = $._matrixIndex,
|
|
324
324
|
ci = $._stroke,
|
|
325
325
|
sw = $._strokeWeight,
|
|
326
326
|
hsw = sw / 2;
|
|
@@ -355,7 +355,7 @@ fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
|
|
|
355
355
|
|
|
356
356
|
$.vertex = (x, y) => {
|
|
357
357
|
if ($._matrixDirty) $._saveMatrix();
|
|
358
|
-
sv.push(x, -y, $._fill, $.
|
|
358
|
+
sv.push(x, -y, $._fill, $._matrixIndex);
|
|
359
359
|
shapeVertCount++;
|
|
360
360
|
};
|
|
361
361
|
|
|
@@ -400,7 +400,7 @@ fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
|
|
|
400
400
|
(2 * p0.y - 5 * p1.y + 4 * p2.y - p3.y) * t2 +
|
|
401
401
|
(-p0.y + 3 * p1.y - 3 * p2.y + p3.y) * t3);
|
|
402
402
|
|
|
403
|
-
sv.push(x, y, $._fill, $.
|
|
403
|
+
sv.push(x, y, $._fill, $._matrixIndex);
|
|
404
404
|
shapeVertCount++;
|
|
405
405
|
}
|
|
406
406
|
}
|
package/src/q5-webgpu-image.js
CHANGED
|
@@ -1,13 +1,21 @@
|
|
|
1
1
|
Q5.renderers.webgpu.image = ($, q) => {
|
|
2
2
|
$._textureBindGroups = [];
|
|
3
|
-
let vertexStack =
|
|
3
|
+
let vertexStack = new Float32Array(1e7),
|
|
4
|
+
vertIndex = 0;
|
|
4
5
|
|
|
5
|
-
let
|
|
6
|
-
label: '
|
|
6
|
+
let imageShader = Q5.device.createShaderModule({
|
|
7
|
+
label: 'imageShader',
|
|
7
8
|
code: `
|
|
9
|
+
struct VertexInput {
|
|
10
|
+
@location(0) pos: vec2f,
|
|
11
|
+
@location(1) texCoord: vec2f,
|
|
12
|
+
@location(2) tintIndex: f32,
|
|
13
|
+
@location(3) matrixIndex: f32
|
|
14
|
+
}
|
|
8
15
|
struct VertexOutput {
|
|
9
16
|
@builtin(position) position: vec4f,
|
|
10
|
-
@location(0) texCoord: vec2f
|
|
17
|
+
@location(0) texCoord: vec2f,
|
|
18
|
+
@location(1) tintIndex: f32
|
|
11
19
|
}
|
|
12
20
|
struct Uniforms {
|
|
13
21
|
halfWidth: f32,
|
|
@@ -17,31 +25,33 @@ struct Uniforms {
|
|
|
17
25
|
@group(0) @binding(0) var<uniform> uniforms: Uniforms;
|
|
18
26
|
@group(0) @binding(1) var<storage> transforms: array<mat4x4<f32>>;
|
|
19
27
|
|
|
28
|
+
@group(1) @binding(0) var<storage> colors : array<vec4f>;
|
|
29
|
+
|
|
30
|
+
@group(2) @binding(0) var samp: sampler;
|
|
31
|
+
@group(2) @binding(1) var texture: texture_2d<f32>;
|
|
32
|
+
|
|
20
33
|
@vertex
|
|
21
|
-
fn vertexMain(
|
|
22
|
-
var vert = vec4f(pos, 0.0, 1.0);
|
|
23
|
-
vert = transforms[i32(
|
|
34
|
+
fn vertexMain(input: VertexInput) -> VertexOutput {
|
|
35
|
+
var vert = vec4f(input.pos, 0.0, 1.0);
|
|
36
|
+
vert = transforms[i32(input.matrixIndex)] * vert;
|
|
24
37
|
vert.x /= uniforms.halfWidth;
|
|
25
38
|
vert.y /= uniforms.halfHeight;
|
|
26
39
|
|
|
27
40
|
var output: VertexOutput;
|
|
28
41
|
output.position = vert;
|
|
29
|
-
output.texCoord = texCoord;
|
|
42
|
+
output.texCoord = input.texCoord;
|
|
43
|
+
output.tintIndex = input.tintIndex;
|
|
30
44
|
return output;
|
|
31
45
|
}
|
|
32
|
-
`
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
let fragmentShader = Q5.device.createShaderModule({
|
|
36
|
-
label: 'imageFragmentShader',
|
|
37
|
-
code: `
|
|
38
|
-
@group(2) @binding(0) var samp: sampler;
|
|
39
|
-
@group(2) @binding(1) var texture: texture_2d<f32>;
|
|
40
46
|
|
|
41
47
|
@fragment
|
|
42
|
-
fn fragmentMain(@location(0) texCoord: vec2f) -> @location(0) vec4f {
|
|
43
|
-
|
|
44
|
-
|
|
48
|
+
fn fragmentMain(@location(0) texCoord: vec2f, @location(1) tintIndex: f32) -> @location(0) vec4f {
|
|
49
|
+
let texColor = textureSample(texture, samp, texCoord);
|
|
50
|
+
let tintColor = colors[i32(tintIndex)];
|
|
51
|
+
|
|
52
|
+
// Mix original and tinted colors using tint alpha as blend factor
|
|
53
|
+
let tinted = vec4f(texColor.rgb * tintColor.rgb, texColor.a);
|
|
54
|
+
return mix(texColor, tinted, tintColor.a);
|
|
45
55
|
}
|
|
46
56
|
`
|
|
47
57
|
});
|
|
@@ -63,11 +73,12 @@ fn fragmentMain(@location(0) texCoord: vec2f) -> @location(0) vec4f {
|
|
|
63
73
|
});
|
|
64
74
|
|
|
65
75
|
const vertexBufferLayout = {
|
|
66
|
-
arrayStride:
|
|
76
|
+
arrayStride: 24,
|
|
67
77
|
attributes: [
|
|
68
78
|
{ shaderLocation: 0, offset: 0, format: 'float32x2' },
|
|
69
79
|
{ shaderLocation: 1, offset: 8, format: 'float32x2' },
|
|
70
|
-
{ shaderLocation: 2, offset: 16, format: 'float32' } //
|
|
80
|
+
{ shaderLocation: 2, offset: 16, format: 'float32' }, // tintIndex
|
|
81
|
+
{ shaderLocation: 3, offset: 20, format: 'float32' } // matrixIndex
|
|
71
82
|
]
|
|
72
83
|
};
|
|
73
84
|
|
|
@@ -80,12 +91,12 @@ fn fragmentMain(@location(0) texCoord: vec2f) -> @location(0) vec4f {
|
|
|
80
91
|
label: 'imagePipeline',
|
|
81
92
|
layout: pipelineLayout,
|
|
82
93
|
vertex: {
|
|
83
|
-
module:
|
|
94
|
+
module: imageShader,
|
|
84
95
|
entryPoint: 'vertexMain',
|
|
85
96
|
buffers: [{ arrayStride: 0, attributes: [] }, vertexBufferLayout]
|
|
86
97
|
},
|
|
87
98
|
fragment: {
|
|
88
|
-
module:
|
|
99
|
+
module: imageShader,
|
|
89
100
|
entryPoint: 'fragmentMain',
|
|
90
101
|
targets: [{ format: 'bgra8unorm', blend: $.blendConfigs.normal }]
|
|
91
102
|
},
|
|
@@ -161,58 +172,63 @@ fn fragmentMain(@location(0) texCoord: vec2f) -> @location(0) vec4f {
|
|
|
161
172
|
|
|
162
173
|
$.loadImage = (src, cb) => {
|
|
163
174
|
q._preloadCount++;
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
// calculate the default width and height that the image
|
|
168
|
-
// should be drawn at if the user doesn't specify a display size
|
|
169
|
-
img.defaultWidth = img.width * $._defaultImageScale;
|
|
170
|
-
img.defaultHeight = img.height * $._defaultImageScale;
|
|
171
|
-
img.pixelDensity = 1;
|
|
172
|
-
|
|
175
|
+
let g = $._g.loadImage(src, (img) => {
|
|
176
|
+
g.defaultWidth = img.width * $._defaultImageScale;
|
|
177
|
+
g.defaultHeight = img.height * $._defaultImageScale;
|
|
173
178
|
$._createTexture(img);
|
|
174
179
|
q._preloadCount--;
|
|
175
180
|
if (cb) cb(img);
|
|
176
|
-
};
|
|
177
|
-
|
|
178
|
-
return img;
|
|
181
|
+
});
|
|
182
|
+
return g;
|
|
179
183
|
};
|
|
180
184
|
|
|
181
185
|
$.imageMode = (x) => ($._imageMode = x);
|
|
182
186
|
|
|
183
|
-
|
|
187
|
+
const addVert = (x, y, u, v, ci, ti) => {
|
|
188
|
+
let s = vertexStack,
|
|
189
|
+
i = vertIndex;
|
|
190
|
+
s[i++] = x;
|
|
191
|
+
s[i++] = y;
|
|
192
|
+
s[i++] = u;
|
|
193
|
+
s[i++] = v;
|
|
194
|
+
s[i++] = ci;
|
|
195
|
+
s[i++] = ti;
|
|
196
|
+
vertIndex = i;
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
$.image = (img, dx = 0, dy = 0, dw, dh, sx = 0, sy = 0, sw, sh) => {
|
|
200
|
+
let g = img;
|
|
184
201
|
if (img.canvas) img = img.canvas;
|
|
185
202
|
if (img.textureIndex == undefined) return;
|
|
186
203
|
|
|
187
204
|
if ($._matrixDirty) $._saveMatrix();
|
|
188
|
-
let ti = $._transformIndex;
|
|
189
205
|
|
|
190
|
-
let
|
|
191
|
-
|
|
206
|
+
let ti = $._matrixIndex,
|
|
207
|
+
w = img.width,
|
|
208
|
+
h = img.height;
|
|
192
209
|
|
|
193
|
-
dw ??=
|
|
194
|
-
dh ??=
|
|
210
|
+
dw ??= g.defaultWidth;
|
|
211
|
+
dh ??= g.defaultHeight;
|
|
195
212
|
sw ??= w;
|
|
196
213
|
sh ??= h;
|
|
197
214
|
|
|
198
|
-
let pd =
|
|
215
|
+
let pd = g._pixelDensity || 1;
|
|
199
216
|
dw *= pd;
|
|
200
217
|
dh *= pd;
|
|
201
218
|
|
|
202
219
|
let [l, r, t, b] = $._calcBox(dx, dy, dw, dh, $._imageMode);
|
|
203
220
|
|
|
204
|
-
let u0 = sx / w
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
);
|
|
221
|
+
let u0 = sx / w,
|
|
222
|
+
v0 = sy / h,
|
|
223
|
+
u1 = (sx + sw) / w,
|
|
224
|
+
v1 = (sy + sh) / h;
|
|
225
|
+
|
|
226
|
+
let ci = $._tint;
|
|
227
|
+
|
|
228
|
+
addVert(l, t, u0, v0, ci, ti);
|
|
229
|
+
addVert(r, t, u1, v0, ci, ti);
|
|
230
|
+
addVert(l, b, u0, v1, ci, ti);
|
|
231
|
+
addVert(r, b, u1, v1, ci, ti);
|
|
216
232
|
|
|
217
233
|
$.drawStack.push(1, img.textureIndex);
|
|
218
234
|
};
|
|
@@ -223,20 +239,20 @@ fn fragmentMain(@location(0) texCoord: vec2f) -> @location(0) vec4f {
|
|
|
223
239
|
// Switch to image pipeline
|
|
224
240
|
$.pass.setPipeline($._pipelines[1]);
|
|
225
241
|
|
|
226
|
-
|
|
227
|
-
size:
|
|
242
|
+
let vertexBuffer = Q5.device.createBuffer({
|
|
243
|
+
size: vertIndex * 4,
|
|
228
244
|
usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
|
|
229
245
|
mappedAtCreation: true
|
|
230
246
|
});
|
|
231
247
|
|
|
232
|
-
new Float32Array(vertexBuffer.getMappedRange()).set(vertexStack);
|
|
248
|
+
new Float32Array(vertexBuffer.getMappedRange()).set(vertexStack.slice(0, vertIndex));
|
|
233
249
|
vertexBuffer.unmap();
|
|
234
250
|
|
|
235
251
|
$.pass.setVertexBuffer(1, vertexBuffer);
|
|
236
252
|
});
|
|
237
253
|
|
|
238
254
|
$._hooks.postRender.push(() => {
|
|
239
|
-
|
|
255
|
+
vertIndex = 0;
|
|
240
256
|
});
|
|
241
257
|
};
|
|
242
258
|
|
package/src/q5-webgpu-text.js
CHANGED
|
@@ -23,7 +23,7 @@ struct Char {
|
|
|
23
23
|
struct Text {
|
|
24
24
|
pos: vec2f,
|
|
25
25
|
scale: f32,
|
|
26
|
-
|
|
26
|
+
matrixIndex: f32,
|
|
27
27
|
fillIndex: f32,
|
|
28
28
|
strokeIndex: f32
|
|
29
29
|
}
|
|
@@ -55,7 +55,7 @@ fn vertexMain(input : VertexInput) -> VertexOutput {
|
|
|
55
55
|
let charPos = ((pos[input.vertex] * fontChar.size + char.xy + fontChar.offset) * text.scale) + text.pos;
|
|
56
56
|
|
|
57
57
|
var vert = vec4f(charPos, 0.0, 1.0);
|
|
58
|
-
vert = transforms[i32(text.
|
|
58
|
+
vert = transforms[i32(text.matrixIndex)] * vert;
|
|
59
59
|
vert.x /= uniforms.halfWidth;
|
|
60
60
|
vert.y /= uniforms.halfHeight;
|
|
61
61
|
|
|
@@ -279,13 +279,11 @@ fn fragmentMain(input : VertexOutput) -> @location(0) vec4f {
|
|
|
279
279
|
if (cb) cb(fontName);
|
|
280
280
|
};
|
|
281
281
|
|
|
282
|
-
|
|
283
|
-
let g = $.createGraphics(1, 1);
|
|
284
|
-
g.colorMode($.RGB, 1);
|
|
282
|
+
$._g.colorMode($.RGB, 1);
|
|
285
283
|
|
|
286
284
|
$.loadFont = (url, cb) => {
|
|
287
285
|
let ext = url.slice(url.lastIndexOf('.') + 1);
|
|
288
|
-
if (ext != 'json') return
|
|
286
|
+
if (ext != 'json') return $._g.loadFont(url, cb);
|
|
289
287
|
let fontName = url.slice(url.lastIndexOf('/') + 1, url.lastIndexOf('-'));
|
|
290
288
|
createFont(url, fontName, cb);
|
|
291
289
|
return fontName;
|
|
@@ -470,7 +468,7 @@ fn fragmentMain(input : VertexOutput) -> @location(0) vec4f {
|
|
|
470
468
|
text[0] = x;
|
|
471
469
|
text[1] = -y;
|
|
472
470
|
text[2] = $._textSize / 44;
|
|
473
|
-
text[3] = $.
|
|
471
|
+
text[3] = $._matrixIndex;
|
|
474
472
|
text[4] = $._fillSet ? $._fill : 0;
|
|
475
473
|
text[5] = $._stroke;
|
|
476
474
|
|
|
@@ -484,18 +482,18 @@ fn fragmentMain(input : VertexOutput) -> @location(0) vec4f {
|
|
|
484
482
|
};
|
|
485
483
|
|
|
486
484
|
$.createTextImage = (str, w, h) => {
|
|
487
|
-
|
|
485
|
+
$._g.textSize($._textSize);
|
|
488
486
|
|
|
489
487
|
if ($._doFill) {
|
|
490
488
|
let fi = $._fill * 4;
|
|
491
|
-
|
|
489
|
+
$._g.fill(colorStack.slice(fi, fi + 4));
|
|
492
490
|
}
|
|
493
491
|
if ($._doStroke) {
|
|
494
492
|
let si = $._stroke * 4;
|
|
495
|
-
|
|
493
|
+
$._g.stroke(colorStack.slice(si, si + 4));
|
|
496
494
|
}
|
|
497
495
|
|
|
498
|
-
let img =
|
|
496
|
+
let img = $._g.createTextImage(str, w, h);
|
|
499
497
|
|
|
500
498
|
if (img.canvas.textureIndex == undefined) {
|
|
501
499
|
$._createTexture(img);
|