@multiplekex/shallot 0.2.3 → 0.2.5
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/src/extras/arrows/index.ts +3 -3
- package/src/extras/caustic.ts +37 -0
- package/src/extras/gradient/index.ts +63 -69
- package/src/extras/index.ts +3 -0
- package/src/extras/lines/index.ts +3 -3
- package/src/extras/skylab/index.ts +314 -0
- package/src/extras/text/font.ts +69 -14
- package/src/extras/text/index.ts +15 -7
- package/src/extras/text/sdf.ts +13 -2
- package/src/extras/water.ts +64 -0
- package/src/standard/defaults.ts +2 -0
- package/src/standard/index.ts +2 -0
- package/src/standard/raster/index.ts +517 -0
- package/src/standard/{render → raytracing}/bvh/blas.ts +3 -3
- package/src/standard/{render → raytracing}/bvh/tlas.ts +3 -0
- package/src/standard/{render → raytracing}/depth.ts +9 -9
- package/src/standard/raytracing/index.ts +380 -0
- package/src/standard/{render → raytracing}/instance.ts +3 -0
- package/src/standard/raytracing/shaders.ts +815 -0
- package/src/standard/{render → raytracing}/triangle.ts +1 -1
- package/src/standard/render/camera.ts +88 -80
- package/src/standard/render/index.ts +68 -208
- package/src/standard/render/indirect.ts +9 -10
- package/src/standard/render/mesh/index.ts +35 -166
- package/src/standard/render/overlay.ts +4 -4
- package/src/standard/render/pass.ts +1 -1
- package/src/standard/render/postprocess.ts +75 -50
- package/src/standard/render/scene.ts +28 -16
- package/src/standard/render/surface/compile.ts +6 -8
- package/src/standard/render/surface/noise.ts +15 -2
- package/src/standard/render/surface/shaders.ts +257 -0
- package/src/standard/render/surface/structs.ts +13 -6
- package/src/standard/render/forward/index.ts +0 -259
- package/src/standard/render/forward/raster.ts +0 -228
- package/src/standard/render/shaders.ts +0 -484
- package/src/standard/render/surface/wgsl.ts +0 -573
- /package/src/standard/{render → raytracing}/bvh/radix.ts +0 -0
- /package/src/standard/{render → raytracing}/bvh/structs.ts +0 -0
- /package/src/standard/{render → raytracing}/bvh/traverse.ts +0 -0
- /package/src/standard/{render → raytracing}/intersection.ts +0 -0
- /package/src/standard/{render → raytracing}/ray.ts +0 -0
package/src/extras/text/font.ts
CHANGED
|
@@ -82,7 +82,10 @@ function parseHead(r: Reader, table: TableEntry): { unitsPerEm: number; indexToL
|
|
|
82
82
|
return { unitsPerEm, indexToLocFormat };
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
-
function parseHhea(
|
|
85
|
+
function parseHhea(
|
|
86
|
+
r: Reader,
|
|
87
|
+
table: TableEntry
|
|
88
|
+
): { ascender: number; descender: number; lineGap: number; numHMetrics: number } {
|
|
86
89
|
seek(r, table.offset + 4);
|
|
87
90
|
const ascender = i16(r);
|
|
88
91
|
const descender = i16(r);
|
|
@@ -92,7 +95,12 @@ function parseHhea(r: Reader, table: TableEntry): { ascender: number; descender:
|
|
|
92
95
|
return { ascender, descender, lineGap, numHMetrics };
|
|
93
96
|
}
|
|
94
97
|
|
|
95
|
-
function parseHmtx(
|
|
98
|
+
function parseHmtx(
|
|
99
|
+
r: Reader,
|
|
100
|
+
table: TableEntry,
|
|
101
|
+
numHMetrics: number,
|
|
102
|
+
numGlyphs: number
|
|
103
|
+
): { advances: Uint16Array } {
|
|
96
104
|
const advances = new Uint16Array(numGlyphs);
|
|
97
105
|
seek(r, table.offset);
|
|
98
106
|
|
|
@@ -114,7 +122,12 @@ function parseMaxp(r: Reader, table: TableEntry): number {
|
|
|
114
122
|
return u16(r);
|
|
115
123
|
}
|
|
116
124
|
|
|
117
|
-
function parseLoca(
|
|
125
|
+
function parseLoca(
|
|
126
|
+
r: Reader,
|
|
127
|
+
table: TableEntry,
|
|
128
|
+
numGlyphs: number,
|
|
129
|
+
indexToLocFormat: number
|
|
130
|
+
): Uint32Array {
|
|
118
131
|
const offsets = new Uint32Array(numGlyphs + 1);
|
|
119
132
|
seek(r, table.offset);
|
|
120
133
|
|
|
@@ -210,7 +223,8 @@ function parseCmap(r: Reader, table: TableEntry): Map<number, number> {
|
|
|
210
223
|
if (rangeOffset === 0) {
|
|
211
224
|
glyphId = (c + delta) & 0xffff;
|
|
212
225
|
} else {
|
|
213
|
-
const glyphIdOffset =
|
|
226
|
+
const glyphIdOffset =
|
|
227
|
+
idRangeOffsetPos + i * 2 + rangeOffset + (c - start) * 2;
|
|
214
228
|
seek(r, glyphIdOffset);
|
|
215
229
|
glyphId = u16(r);
|
|
216
230
|
if (glyphId !== 0) {
|
|
@@ -291,7 +305,12 @@ const REPEAT = 8;
|
|
|
291
305
|
const X_SAME = 16;
|
|
292
306
|
const Y_SAME = 32;
|
|
293
307
|
|
|
294
|
-
function parseGlyph(
|
|
308
|
+
function parseGlyph(
|
|
309
|
+
r: Reader,
|
|
310
|
+
glyfOffset: number,
|
|
311
|
+
loca: Uint32Array,
|
|
312
|
+
glyphId: number
|
|
313
|
+
): { path: string; bounds: [number, number, number, number] } | null {
|
|
295
314
|
const start = loca[glyphId];
|
|
296
315
|
const end = loca[glyphId + 1];
|
|
297
316
|
if (start === end) return null;
|
|
@@ -372,7 +391,11 @@ function parseGlyph(r: Reader, glyfOffset: number, loca: Uint32Array, glyphId: n
|
|
|
372
391
|
while (firstOn < points.length && !points[firstOn].on) firstOn++;
|
|
373
392
|
|
|
374
393
|
if (firstOn === points.length) {
|
|
375
|
-
const mid = {
|
|
394
|
+
const mid = {
|
|
395
|
+
x: (points[0].x + points[1].x) / 2,
|
|
396
|
+
y: (points[0].y + points[1].y) / 2,
|
|
397
|
+
on: true,
|
|
398
|
+
};
|
|
376
399
|
points.unshift(mid);
|
|
377
400
|
firstOn = 0;
|
|
378
401
|
}
|
|
@@ -412,17 +435,28 @@ function parseGlyph(r: Reader, glyfOffset: number, loca: Uint32Array, glyphId: n
|
|
|
412
435
|
return { path, bounds: [xMin, yMin, xMax, yMax] };
|
|
413
436
|
}
|
|
414
437
|
|
|
415
|
-
function parseCompositeGlyph(
|
|
438
|
+
function parseCompositeGlyph(
|
|
439
|
+
r: Reader,
|
|
440
|
+
glyfOffset: number,
|
|
441
|
+
loca: Uint32Array
|
|
442
|
+
): { path: string; bounds: [number, number, number, number] } | null {
|
|
416
443
|
let path = "";
|
|
417
|
-
let xMin = Infinity,
|
|
444
|
+
let xMin = Infinity,
|
|
445
|
+
yMin = Infinity,
|
|
446
|
+
xMax = -Infinity,
|
|
447
|
+
yMax = -Infinity;
|
|
418
448
|
let hasMore = true;
|
|
419
449
|
|
|
420
450
|
while (hasMore) {
|
|
421
451
|
const flags = u16(r);
|
|
422
452
|
const glyphIndex = u16(r);
|
|
423
453
|
|
|
424
|
-
let dx = 0,
|
|
425
|
-
|
|
454
|
+
let dx = 0,
|
|
455
|
+
dy = 0;
|
|
456
|
+
let a = 1,
|
|
457
|
+
b = 0,
|
|
458
|
+
c = 0,
|
|
459
|
+
d = 1;
|
|
426
460
|
|
|
427
461
|
if (flags & 1) {
|
|
428
462
|
dx = i16(r);
|
|
@@ -464,7 +498,15 @@ function parseCompositeGlyph(r: Reader, glyfOffset: number, loca: Uint32Array):
|
|
|
464
498
|
return { path, bounds: [xMin, yMin, xMax, yMax] };
|
|
465
499
|
}
|
|
466
500
|
|
|
467
|
-
function transformPath(
|
|
501
|
+
function transformPath(
|
|
502
|
+
path: string,
|
|
503
|
+
a: number,
|
|
504
|
+
b: number,
|
|
505
|
+
c: number,
|
|
506
|
+
d: number,
|
|
507
|
+
dx: number,
|
|
508
|
+
dy: number
|
|
509
|
+
): string {
|
|
468
510
|
return path.replace(/(-?\d+(?:\.\d+)?),(-?\d+(?:\.\d+)?)/g, (_, x, y) => {
|
|
469
511
|
const nx = parseFloat(x) * a + parseFloat(y) * b + dx;
|
|
470
512
|
const ny = parseFloat(x) * c + parseFloat(y) * d + dy;
|
|
@@ -485,7 +527,15 @@ export function parseFont(buffer: ArrayBuffer): Font {
|
|
|
485
527
|
const glyfTable = tables.get("glyf");
|
|
486
528
|
const kernTable = tables.get("kern");
|
|
487
529
|
|
|
488
|
-
if (
|
|
530
|
+
if (
|
|
531
|
+
!headTable ||
|
|
532
|
+
!hheaTable ||
|
|
533
|
+
!hmtxTable ||
|
|
534
|
+
!maxpTable ||
|
|
535
|
+
!cmapTable ||
|
|
536
|
+
!locaTable ||
|
|
537
|
+
!glyfTable
|
|
538
|
+
) {
|
|
489
539
|
throw new Error("Missing required font tables");
|
|
490
540
|
}
|
|
491
541
|
|
|
@@ -497,14 +547,19 @@ export function parseFont(buffer: ArrayBuffer): Font {
|
|
|
497
547
|
const cmap = parseCmap(r, cmapTable);
|
|
498
548
|
const kern = kernTable ? parseKern(r, kernTable) : new Map<number, number>();
|
|
499
549
|
|
|
500
|
-
const glyphCache = new Map<
|
|
550
|
+
const glyphCache = new Map<
|
|
551
|
+
number,
|
|
552
|
+
{ path: string; bounds: [number, number, number, number] } | null
|
|
553
|
+
>();
|
|
501
554
|
const glyfOffset = glyfTable.offset;
|
|
502
555
|
|
|
503
556
|
function getGlyphId(char: string): number {
|
|
504
557
|
return cmap.get(char.codePointAt(0) ?? 0) ?? 0;
|
|
505
558
|
}
|
|
506
559
|
|
|
507
|
-
function getGlyph(
|
|
560
|
+
function getGlyph(
|
|
561
|
+
glyphId: number
|
|
562
|
+
): { path: string; bounds: [number, number, number, number] } | null {
|
|
508
563
|
if (glyphCache.has(glyphId)) return glyphCache.get(glyphId)!;
|
|
509
564
|
const glyph = parseGlyph(r, glyfOffset, loca, glyphId);
|
|
510
565
|
glyphCache.set(glyphId, glyph);
|
package/src/extras/text/index.ts
CHANGED
|
@@ -21,8 +21,8 @@ import {
|
|
|
21
21
|
type Draw,
|
|
22
22
|
type SharedPassContext,
|
|
23
23
|
} from "../../standard/render";
|
|
24
|
-
import {
|
|
25
|
-
import { SCENE_STRUCT_WGSL } from "../../standard/render/
|
|
24
|
+
import { Z_FORMAT } from "../../standard/render/scene";
|
|
25
|
+
import { SCENE_STRUCT_WGSL } from "../../standard/render/surface/structs";
|
|
26
26
|
import { Transform } from "../../standard/transforms";
|
|
27
27
|
|
|
28
28
|
const MAX_GLYPHS = 50000;
|
|
@@ -366,7 +366,7 @@ function layoutText(text: string, atlas: GlyphAtlas, fontSize: number): LayoutRe
|
|
|
366
366
|
if (!metrics) continue;
|
|
367
367
|
|
|
368
368
|
if (prevChar) {
|
|
369
|
-
cursorX += atlas.font.kerning(prevChar, char) / atlas.font.unitsPerEm * scale;
|
|
369
|
+
cursorX += (atlas.font.kerning(prevChar, char) / atlas.font.unitsPerEm) * scale;
|
|
370
370
|
}
|
|
371
371
|
|
|
372
372
|
const glyphW = metrics.glyphWidth * scale;
|
|
@@ -520,13 +520,18 @@ fn fs(input: VertexOutput) -> FragmentOutput {
|
|
|
520
520
|
// Smoothstep with adaptive band
|
|
521
521
|
let alpha = smoothstep(aaDist, -aaDist, signedDist);
|
|
522
522
|
|
|
523
|
-
|
|
523
|
+
// Dilate mask by FXAA span (8 pixels) to prevent edge bleeding
|
|
524
|
+
let fxaaSpan = aaDist * 8.0;
|
|
525
|
+
let inMaskRegion = signedDist < fxaaSpan;
|
|
526
|
+
|
|
527
|
+
// Only discard if outside both visible region and mask region
|
|
528
|
+
if alpha < 0.01 && !inMaskRegion {
|
|
524
529
|
discard;
|
|
525
530
|
}
|
|
526
531
|
|
|
527
532
|
var out: FragmentOutput;
|
|
528
533
|
out.color = vec4(input.color.rgb, input.color.a * alpha);
|
|
529
|
-
out.mask = select(0.0, 1.0,
|
|
534
|
+
out.mask = select(0.0, 1.0, inMaskRegion);
|
|
530
535
|
return out;
|
|
531
536
|
}
|
|
532
537
|
`;
|
|
@@ -574,7 +579,7 @@ function createTextPipeline(
|
|
|
574
579
|
cullMode: "none",
|
|
575
580
|
},
|
|
576
581
|
depthStencil: {
|
|
577
|
-
format:
|
|
582
|
+
format: Z_FORMAT,
|
|
578
583
|
depthCompare: "less",
|
|
579
584
|
depthWriteEnabled: false,
|
|
580
585
|
},
|
|
@@ -664,6 +669,8 @@ interface PendingGlyph {
|
|
|
664
669
|
a: number;
|
|
665
670
|
}
|
|
666
671
|
|
|
672
|
+
let glyphsByFont: PendingGlyph[][] = [];
|
|
673
|
+
|
|
667
674
|
const TextSystem: System = {
|
|
668
675
|
group: "draw",
|
|
669
676
|
|
|
@@ -676,7 +683,8 @@ const TextSystem: System = {
|
|
|
676
683
|
const { atlases, staging, ranges } = text;
|
|
677
684
|
const stagingU32 = new Uint32Array(staging.buffer);
|
|
678
685
|
|
|
679
|
-
|
|
686
|
+
while (glyphsByFont.length < atlases.length) glyphsByFont.push([]);
|
|
687
|
+
for (let i = 0; i < atlases.length; i++) glyphsByFont[i].length = 0;
|
|
680
688
|
|
|
681
689
|
for (const eid of state.query([Text, Transform])) {
|
|
682
690
|
if (!Text.visible[eid]) continue;
|
package/src/extras/text/sdf.ts
CHANGED
|
@@ -340,14 +340,25 @@ export class SDFGenerator {
|
|
|
340
340
|
const segments = segmentPath(path, this.curveSubdivisions);
|
|
341
341
|
if (segments.length === 0) return;
|
|
342
342
|
if (segments.length > this.maxSegments) {
|
|
343
|
-
console.warn(
|
|
343
|
+
console.warn(
|
|
344
|
+
`Too many segments (${segments.length}), truncating to ${this.maxSegments}`
|
|
345
|
+
);
|
|
344
346
|
segments.length = this.maxSegments;
|
|
345
347
|
}
|
|
346
348
|
|
|
347
349
|
const [xMin, yMin, xMax, yMax] = bounds;
|
|
348
350
|
const maxDist = Math.max(xMax - xMin, yMax - yMin) / 2;
|
|
349
351
|
|
|
350
|
-
const uniformData = new Float32Array([
|
|
352
|
+
const uniformData = new Float32Array([
|
|
353
|
+
xMin,
|
|
354
|
+
yMin,
|
|
355
|
+
xMax,
|
|
356
|
+
yMax,
|
|
357
|
+
maxDist,
|
|
358
|
+
this.exponent,
|
|
359
|
+
0,
|
|
360
|
+
0,
|
|
361
|
+
]);
|
|
351
362
|
this.device.queue.writeBuffer(this.uniformBuffer, 0, uniformData);
|
|
352
363
|
|
|
353
364
|
const segmentData = new Float32Array(segments.length * 4);
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { surface } from "../standard/render/surface";
|
|
2
|
+
import type { MeshData } from "../standard/render/mesh";
|
|
3
|
+
|
|
4
|
+
const SEED1 = "vec2(127.1, 311.7)";
|
|
5
|
+
const SEED2 = "vec2(269.5, 183.3)";
|
|
6
|
+
|
|
7
|
+
export const Water = surface({
|
|
8
|
+
fragment: `
|
|
9
|
+
let p = (*surface).worldPos.xz * 3.0;
|
|
10
|
+
let t = scene.time;
|
|
11
|
+
let t1 = vec2(t * 0.6, t * 0.4);
|
|
12
|
+
let t2 = vec2(t * 0.5, t * 0.7);
|
|
13
|
+
let eps = 0.1;
|
|
14
|
+
|
|
15
|
+
let h = (value2d(p + t1, ${SEED1}) + value2d(p - t2, ${SEED2})) * 0.5;
|
|
16
|
+
let hx = (value2d(p + vec2(eps, 0.0) + t1, ${SEED1}) + value2d(p + vec2(eps, 0.0) - t2, ${SEED2})) * 0.5;
|
|
17
|
+
let hz = (value2d(p + vec2(0.0, eps) + t1, ${SEED1}) + value2d(p + vec2(0.0, eps) - t2, ${SEED2})) * 0.5;
|
|
18
|
+
|
|
19
|
+
let dx = (hx - h) / eps;
|
|
20
|
+
let dz = (hz - h) / eps;
|
|
21
|
+
(*surface).normal = normalize(vec3(-dx * 0.015, 1.0, -dz * 0.015));`,
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
export function createSubdividedPlane(subdivisions: number = 32): MeshData {
|
|
25
|
+
const segments = subdivisions;
|
|
26
|
+
const vertexCount = (segments + 1) * (segments + 1);
|
|
27
|
+
const indexCount = segments * segments * 6;
|
|
28
|
+
|
|
29
|
+
const vertices = new Float32Array(vertexCount * 6);
|
|
30
|
+
const indices = new Uint16Array(indexCount);
|
|
31
|
+
|
|
32
|
+
let vi = 0;
|
|
33
|
+
for (let z = 0; z <= segments; z++) {
|
|
34
|
+
for (let x = 0; x <= segments; x++) {
|
|
35
|
+
const u = x / segments;
|
|
36
|
+
const v = z / segments;
|
|
37
|
+
vertices[vi++] = u - 0.5;
|
|
38
|
+
vertices[vi++] = 0;
|
|
39
|
+
vertices[vi++] = v - 0.5;
|
|
40
|
+
vertices[vi++] = 0;
|
|
41
|
+
vertices[vi++] = 1;
|
|
42
|
+
vertices[vi++] = 0;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
let ii = 0;
|
|
47
|
+
for (let z = 0; z < segments; z++) {
|
|
48
|
+
for (let x = 0; x < segments; x++) {
|
|
49
|
+
const topLeft = z * (segments + 1) + x;
|
|
50
|
+
const topRight = topLeft + 1;
|
|
51
|
+
const bottomLeft = (z + 1) * (segments + 1) + x;
|
|
52
|
+
const bottomRight = bottomLeft + 1;
|
|
53
|
+
|
|
54
|
+
indices[ii++] = topLeft;
|
|
55
|
+
indices[ii++] = bottomLeft;
|
|
56
|
+
indices[ii++] = topRight;
|
|
57
|
+
indices[ii++] = topRight;
|
|
58
|
+
indices[ii++] = bottomLeft;
|
|
59
|
+
indices[ii++] = bottomRight;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return { vertices, indices, vertexCount, indexCount };
|
|
64
|
+
}
|
package/src/standard/defaults.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { TransformsPlugin } from "./transforms";
|
|
|
3
3
|
import { InputPlugin } from "./input";
|
|
4
4
|
import { ComputePlugin, initCanvas } from "./compute";
|
|
5
5
|
import { RenderPlugin } from "./render";
|
|
6
|
+
import { RasterPlugin } from "./raster";
|
|
6
7
|
import { ActivityPlugin, spinnerDark } from "./activity";
|
|
7
8
|
import { shallotDark } from "./loading";
|
|
8
9
|
|
|
@@ -12,6 +13,7 @@ export const DEFAULT_PLUGINS: readonly Plugin[] = [
|
|
|
12
13
|
InputPlugin,
|
|
13
14
|
ComputePlugin,
|
|
14
15
|
RenderPlugin,
|
|
16
|
+
RasterPlugin,
|
|
15
17
|
];
|
|
16
18
|
|
|
17
19
|
StateBuilder.defaultPlugins = DEFAULT_PLUGINS;
|