@multiplekex/shallot 0.2.4 → 0.3.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.
Files changed (62) hide show
  1. package/package.json +1 -1
  2. package/src/core/component.ts +1 -1
  3. package/src/core/index.ts +1 -13
  4. package/src/core/math.ts +186 -0
  5. package/src/core/state.ts +1 -1
  6. package/src/core/xml.ts +56 -41
  7. package/src/extras/arrows/index.ts +3 -3
  8. package/src/extras/caustic.ts +37 -0
  9. package/src/extras/gradient/index.ts +63 -69
  10. package/src/extras/index.ts +3 -0
  11. package/src/extras/lines/index.ts +3 -3
  12. package/src/extras/orbit/index.ts +1 -1
  13. package/src/extras/skylab/index.ts +314 -0
  14. package/src/extras/text/font.ts +69 -14
  15. package/src/extras/text/index.ts +17 -69
  16. package/src/extras/text/sdf.ts +13 -2
  17. package/src/extras/water/index.ts +119 -0
  18. package/src/standard/defaults.ts +2 -0
  19. package/src/standard/index.ts +2 -0
  20. package/src/standard/raster/batch.ts +149 -0
  21. package/src/standard/raster/forward.ts +832 -0
  22. package/src/standard/raster/index.ts +191 -0
  23. package/src/standard/raster/shadow.ts +408 -0
  24. package/src/standard/{render → raytracing}/bvh/blas.ts +336 -88
  25. package/src/standard/raytracing/bvh/radix.ts +473 -0
  26. package/src/standard/raytracing/bvh/refit.ts +711 -0
  27. package/src/standard/{render → raytracing}/bvh/structs.ts +0 -55
  28. package/src/standard/{render → raytracing}/bvh/tlas.ts +155 -140
  29. package/src/standard/{render → raytracing}/bvh/traverse.ts +72 -64
  30. package/src/standard/{render → raytracing}/depth.ts +9 -9
  31. package/src/standard/raytracing/index.ts +409 -0
  32. package/src/standard/{render → raytracing}/instance.ts +31 -16
  33. package/src/standard/{render → raytracing}/ray.ts +1 -1
  34. package/src/standard/raytracing/shaders.ts +798 -0
  35. package/src/standard/{render → raytracing}/triangle.ts +1 -1
  36. package/src/standard/render/camera.ts +96 -106
  37. package/src/standard/render/data.ts +1 -1
  38. package/src/standard/render/index.ts +136 -220
  39. package/src/standard/render/indirect.ts +9 -10
  40. package/src/standard/render/light.ts +2 -2
  41. package/src/standard/render/mesh.ts +404 -0
  42. package/src/standard/render/overlay.ts +8 -5
  43. package/src/standard/render/pass.ts +1 -1
  44. package/src/standard/render/postprocess.ts +263 -242
  45. package/src/standard/render/scene.ts +28 -16
  46. package/src/standard/render/surface/index.ts +81 -12
  47. package/src/standard/render/surface/shaders.ts +511 -0
  48. package/src/standard/render/surface/structs.ts +23 -6
  49. package/src/standard/tween/tween.ts +44 -115
  50. package/src/standard/render/bvh/radix.ts +0 -476
  51. package/src/standard/render/forward/index.ts +0 -259
  52. package/src/standard/render/forward/raster.ts +0 -228
  53. package/src/standard/render/mesh/box.ts +0 -20
  54. package/src/standard/render/mesh/index.ts +0 -446
  55. package/src/standard/render/mesh/plane.ts +0 -11
  56. package/src/standard/render/mesh/sphere.ts +0 -40
  57. package/src/standard/render/mesh/unified.ts +0 -96
  58. package/src/standard/render/shaders.ts +0 -484
  59. package/src/standard/render/surface/compile.ts +0 -67
  60. package/src/standard/render/surface/noise.ts +0 -45
  61. package/src/standard/render/surface/wgsl.ts +0 -573
  62. /package/src/standard/{render → raytracing}/intersection.ts +0 -0
@@ -1,446 +0,0 @@
1
- import { MAX_ENTITIES } from "../../../core";
2
- import { setTraits, type FieldAccessor } from "../../../core/component";
3
- import { createEntityIdBuffer } from "../../compute";
4
- import { writeIndirect } from "../indirect";
5
- import { createBox } from "./box";
6
- import { createSphere } from "./sphere";
7
- import { createPlane } from "./plane";
8
-
9
- export const MAX_SURFACES = 16;
10
- export const MAX_BATCH_SLOTS = 64;
11
- const INVALID_SHAPE = 0xffffffff;
12
-
13
- const batchEntityIds = new Uint32Array(MAX_ENTITIES);
14
-
15
- export interface MeshData {
16
- vertices: Float32Array<ArrayBuffer>;
17
- indices: Uint16Array<ArrayBuffer>;
18
- vertexCount: number;
19
- indexCount: number;
20
- }
21
-
22
- const meshes: MeshData[] = [];
23
-
24
- function initBuiltIns(): void {
25
- if (meshes.length === 0) {
26
- meshes.push(createBox());
27
- meshes.push(createSphere());
28
- meshes.push(createPlane());
29
- }
30
- }
31
-
32
- initBuiltIns();
33
-
34
- export const MeshShape = {
35
- Box: 0,
36
- Sphere: 1,
37
- Plane: 2,
38
- } as const;
39
-
40
- export function mesh(data: MeshData): number {
41
- const id = meshes.length;
42
- meshes.push(data);
43
- return id;
44
- }
45
-
46
- export function getMesh(id: number): MeshData | undefined {
47
- return meshes[id];
48
- }
49
-
50
- export function clearMeshes(): void {
51
- meshes.length = 0;
52
- initBuiltIns();
53
- }
54
-
55
- export const MeshShapes = {
56
- data: new Uint32Array(MAX_ENTITIES).fill(INVALID_SHAPE),
57
- };
58
-
59
- export const MeshColors = {
60
- data: new Float32Array(MAX_ENTITIES * 4),
61
- };
62
-
63
- export const MeshSizes = {
64
- data: new Float32Array(MAX_ENTITIES * 4),
65
- };
66
-
67
- export const MeshPBR = {
68
- data: new Float32Array(MAX_ENTITIES * 4),
69
- };
70
-
71
- export const MeshEmission = {
72
- data: new Float32Array(MAX_ENTITIES * 4),
73
- };
74
-
75
- export const MeshVolumes = {
76
- data: new Uint8Array(MAX_ENTITIES),
77
- };
78
-
79
- export const Volume = {
80
- Solid: 0,
81
- HalfSpace: 1,
82
- } as const;
83
-
84
- interface ColorProxy extends Array<number>, FieldAccessor {}
85
-
86
- function colorProxy(): ColorProxy {
87
- const data = MeshColors.data;
88
-
89
- function getValue(eid: number): number {
90
- const offset = eid * 4;
91
- const r = Math.round(data[offset] * 255);
92
- const g = Math.round(data[offset + 1] * 255);
93
- const b = Math.round(data[offset + 2] * 255);
94
- return (r << 16) | (g << 8) | b;
95
- }
96
-
97
- function setValue(eid: number, value: number): void {
98
- const offset = eid * 4;
99
- data[offset] = ((value >> 16) & 0xff) / 255;
100
- data[offset + 1] = ((value >> 8) & 0xff) / 255;
101
- data[offset + 2] = (value & 0xff) / 255;
102
- }
103
-
104
- return new Proxy([] as unknown as ColorProxy, {
105
- get(_, prop) {
106
- if (prop === "get") return getValue;
107
- if (prop === "set") return setValue;
108
- const eid = Number(prop);
109
- if (Number.isNaN(eid)) return undefined;
110
- return getValue(eid);
111
- },
112
- set(_, prop, value) {
113
- const eid = Number(prop);
114
- if (Number.isNaN(eid)) return false;
115
- setValue(eid, value);
116
- return true;
117
- },
118
- });
119
- }
120
-
121
- interface ColorChannelProxy extends Array<number>, FieldAccessor {}
122
-
123
- function colorChannelProxy(channelIndex: number): ColorChannelProxy {
124
- const data = MeshColors.data;
125
-
126
- function getValue(eid: number): number {
127
- return data[eid * 4 + channelIndex];
128
- }
129
-
130
- function setValue(eid: number, value: number): void {
131
- data[eid * 4 + channelIndex] = value;
132
- }
133
-
134
- return new Proxy([] as unknown as ColorChannelProxy, {
135
- get(_, prop) {
136
- if (prop === "get") return getValue;
137
- if (prop === "set") return setValue;
138
- const eid = Number(prop);
139
- if (Number.isNaN(eid)) return undefined;
140
- return getValue(eid);
141
- },
142
- set(_, prop, value) {
143
- const eid = Number(prop);
144
- if (Number.isNaN(eid)) return false;
145
- setValue(eid, value);
146
- return true;
147
- },
148
- });
149
- }
150
-
151
- interface SizeProxy extends Array<number>, FieldAccessor {}
152
-
153
- function sizeProxy(component: number): SizeProxy {
154
- const data = MeshSizes.data;
155
-
156
- function getValue(eid: number): number {
157
- return data[eid * 4 + component];
158
- }
159
-
160
- function setValue(eid: number, value: number): void {
161
- data[eid * 4 + component] = value;
162
- }
163
-
164
- return new Proxy([] as unknown as SizeProxy, {
165
- get(_, prop) {
166
- if (prop === "get") return getValue;
167
- if (prop === "set") return setValue;
168
- const eid = Number(prop);
169
- if (Number.isNaN(eid)) return undefined;
170
- return getValue(eid);
171
- },
172
- set(_, prop, value) {
173
- const eid = Number(prop);
174
- if (Number.isNaN(eid)) return false;
175
- setValue(eid, value);
176
- return true;
177
- },
178
- });
179
- }
180
-
181
- interface PBRProxy extends Array<number>, FieldAccessor {}
182
-
183
- function pbrProxy(component: number, defaultValue: number): PBRProxy {
184
- const data = MeshPBR.data;
185
-
186
- function getValue(eid: number): number {
187
- const val = data[eid * 4 + component];
188
- return val === 0 && component === 0 ? defaultValue : val;
189
- }
190
-
191
- function setValue(eid: number, value: number): void {
192
- data[eid * 4 + component] = value;
193
- }
194
-
195
- return new Proxy([] as unknown as PBRProxy, {
196
- get(_, prop) {
197
- if (prop === "get") return getValue;
198
- if (prop === "set") return setValue;
199
- const eid = Number(prop);
200
- if (Number.isNaN(eid)) return undefined;
201
- return getValue(eid);
202
- },
203
- set(_, prop, value) {
204
- const eid = Number(prop);
205
- if (Number.isNaN(eid)) return false;
206
- setValue(eid, value);
207
- return true;
208
- },
209
- });
210
- }
211
-
212
- interface EmissionProxy extends Array<number>, FieldAccessor {}
213
-
214
- function emissionProxy(): EmissionProxy {
215
- const data = MeshEmission.data;
216
-
217
- function getValue(eid: number): number {
218
- const offset = eid * 4;
219
- const r = Math.round(data[offset] * 255);
220
- const g = Math.round(data[offset + 1] * 255);
221
- const b = Math.round(data[offset + 2] * 255);
222
- return (r << 16) | (g << 8) | b;
223
- }
224
-
225
- function setValue(eid: number, value: number): void {
226
- const offset = eid * 4;
227
- data[offset] = ((value >> 16) & 0xff) / 255;
228
- data[offset + 1] = ((value >> 8) & 0xff) / 255;
229
- data[offset + 2] = (value & 0xff) / 255;
230
- }
231
-
232
- return new Proxy([] as unknown as EmissionProxy, {
233
- get(_, prop) {
234
- if (prop === "get") return getValue;
235
- if (prop === "set") return setValue;
236
- const eid = Number(prop);
237
- if (Number.isNaN(eid)) return undefined;
238
- return getValue(eid);
239
- },
240
- set(_, prop, value) {
241
- const eid = Number(prop);
242
- if (Number.isNaN(eid)) return false;
243
- setValue(eid, value);
244
- return true;
245
- },
246
- });
247
- }
248
-
249
- function emissionIntensityProxy(): PBRProxy {
250
- const data = MeshEmission.data;
251
-
252
- function getValue(eid: number): number {
253
- return data[eid * 4 + 3];
254
- }
255
-
256
- function setValue(eid: number, value: number): void {
257
- data[eid * 4 + 3] = value;
258
- }
259
-
260
- return new Proxy([] as unknown as PBRProxy, {
261
- get(_, prop) {
262
- if (prop === "get") return getValue;
263
- if (prop === "set") return setValue;
264
- const eid = Number(prop);
265
- if (Number.isNaN(eid)) return undefined;
266
- return getValue(eid);
267
- },
268
- set(_, prop, value) {
269
- const eid = Number(prop);
270
- if (Number.isNaN(eid)) return false;
271
- setValue(eid, value);
272
- return true;
273
- },
274
- });
275
- }
276
-
277
- export const Mesh: {
278
- shape: Uint32Array;
279
- color: ColorProxy;
280
- colorR: ColorChannelProxy;
281
- colorG: ColorChannelProxy;
282
- colorB: ColorChannelProxy;
283
- opacity: ColorChannelProxy;
284
- sizeX: SizeProxy;
285
- sizeY: SizeProxy;
286
- sizeZ: SizeProxy;
287
- roughness: PBRProxy;
288
- metallic: PBRProxy;
289
- ior: PBRProxy;
290
- emission: EmissionProxy;
291
- emissionIntensity: PBRProxy;
292
- volume: Uint8Array;
293
- } = {
294
- shape: MeshShapes.data,
295
- color: colorProxy(),
296
- colorR: colorChannelProxy(0),
297
- colorG: colorChannelProxy(1),
298
- colorB: colorChannelProxy(2),
299
- opacity: colorChannelProxy(3),
300
- sizeX: sizeProxy(0),
301
- sizeY: sizeProxy(1),
302
- sizeZ: sizeProxy(2),
303
- roughness: pbrProxy(0, 0.9),
304
- metallic: pbrProxy(1, 0.0),
305
- ior: pbrProxy(2, 1.0),
306
- emission: emissionProxy(),
307
- emissionIntensity: emissionIntensityProxy(),
308
- volume: MeshVolumes.data,
309
- };
310
-
311
- setTraits(Mesh, {
312
- defaults: () => ({
313
- shape: MeshShape.Box,
314
- color: 0xffffff,
315
- opacity: 1.0,
316
- sizeX: 1,
317
- sizeY: 1,
318
- sizeZ: 1,
319
- roughness: 1.0,
320
- metallic: 0.0,
321
- ior: 1.0,
322
- emission: 0x000000,
323
- emissionIntensity: 0.0,
324
- volume: Volume.Solid,
325
- }),
326
- });
327
-
328
- export interface MeshBuffers {
329
- vertex: GPUBuffer;
330
- index: GPUBuffer;
331
- indexCount: number;
332
- }
333
-
334
- export function createMeshBuffers(device: GPUDevice, mesh: MeshData): MeshBuffers {
335
- const vertex = device.createBuffer({
336
- label: "vertex",
337
- size: mesh.vertices.byteLength,
338
- usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
339
- });
340
- device.queue.writeBuffer(vertex, 0, mesh.vertices);
341
-
342
- const index = device.createBuffer({
343
- label: "index",
344
- size: mesh.indices.byteLength,
345
- usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST,
346
- });
347
- device.queue.writeBuffer(index, 0, mesh.indices);
348
-
349
- return { vertex, index, indexCount: mesh.indexCount };
350
- }
351
-
352
- export type BatchEntities = (number[] | null)[];
353
-
354
- export function collectBatches(
355
- entities: Iterable<number>,
356
- getSurface: (eid: number) => number,
357
- out: BatchEntities
358
- ): void {
359
- for (let i = 0; i < MAX_BATCH_SLOTS; i++) {
360
- if (out[i]) out[i]!.length = 0;
361
- }
362
- for (const eid of entities) {
363
- const shape = Mesh.shape[eid];
364
- const surface = getSurface(eid);
365
- const batchIndex = shape * MAX_SURFACES + surface;
366
- if (batchIndex >= MAX_BATCH_SLOTS) continue;
367
- let entry = out[batchIndex];
368
- if (!entry) {
369
- entry = [];
370
- out[batchIndex] = entry;
371
- }
372
- entry.push(eid);
373
- }
374
- }
375
-
376
- export interface Batch {
377
- buffers: MeshBuffers;
378
- entityIds: GPUBuffer;
379
- count: number;
380
- }
381
-
382
- export interface BatchState {
383
- batches: (Batch | null)[];
384
- buffers: Map<number, MeshBuffers>;
385
- }
386
-
387
- export function updateBatches(
388
- device: GPUDevice,
389
- batchEntities: BatchEntities,
390
- state: BatchState,
391
- indirect: GPUBuffer
392
- ): void {
393
- for (let batchIndex = 0; batchIndex < MAX_BATCH_SLOTS; batchIndex++) {
394
- const entities = batchEntities[batchIndex];
395
- if (!entities || entities.length === 0) {
396
- const batch = state.batches[batchIndex];
397
- if (batch) {
398
- batch.count = 0;
399
- writeIndirect(device, indirect, batchIndex, {
400
- indexCount: 0,
401
- instanceCount: 0,
402
- firstIndex: 0,
403
- baseVertex: 0,
404
- firstInstance: 0,
405
- });
406
- }
407
- continue;
408
- }
409
-
410
- let batch = state.batches[batchIndex];
411
- if (!batch) {
412
- const shape = Math.floor(batchIndex / MAX_SURFACES);
413
- let buffers = state.buffers.get(shape);
414
- if (!buffers) {
415
- const data = getMesh(shape) ?? getMesh(MeshShape.Box)!;
416
- buffers = createMeshBuffers(device, data);
417
- state.buffers.set(shape, buffers);
418
- }
419
- batch = {
420
- buffers,
421
- entityIds: createEntityIdBuffer(device, MAX_ENTITIES),
422
- count: 0,
423
- };
424
- state.batches[batchIndex] = batch;
425
- }
426
-
427
- for (let i = 0; i < entities.length; i++) {
428
- batchEntityIds[i] = entities[i];
429
- }
430
- device.queue.writeBuffer(batch.entityIds, 0, batchEntityIds, 0, entities.length);
431
- batch.count = entities.length;
432
-
433
- writeIndirect(device, indirect, batchIndex, {
434
- indexCount: batch.buffers.indexCount,
435
- instanceCount: entities.length,
436
- firstIndex: 0,
437
- baseVertex: 0,
438
- firstInstance: 0,
439
- });
440
- }
441
- }
442
-
443
- export { createBox } from "./box";
444
- export { createSphere } from "./sphere";
445
- export { createPlane } from "./plane";
446
- export { createShapeAtlas } from "./unified";
@@ -1,11 +0,0 @@
1
- import type { MeshData } from "./index";
2
-
3
- export function createPlane(): MeshData {
4
- const vertices = new Float32Array([
5
- -0.5, 0, 0.5, 0, 1, 0, 0.5, 0, 0.5, 0, 1, 0, 0.5, 0, -0.5, 0, 1, 0, -0.5, 0, -0.5, 0, 1, 0,
6
- ]);
7
-
8
- const indices = new Uint16Array([0, 1, 2, 0, 2, 3]);
9
-
10
- return { vertices, indices, vertexCount: 4, indexCount: 6 };
11
- }
@@ -1,40 +0,0 @@
1
- import type { MeshData } from "./index";
2
-
3
- export function createSphere(segments = 32, rings = 16): MeshData {
4
- const vertices: number[] = [];
5
- const indices: number[] = [];
6
- const radius = 0.5;
7
-
8
- for (let y = 0; y <= rings; y++) {
9
- const v = y / rings;
10
- const theta = v * Math.PI;
11
-
12
- for (let x = 0; x <= segments; x++) {
13
- const u = x / segments;
14
- const phi = u * Math.PI * 2;
15
-
16
- const nx = Math.sin(theta) * Math.cos(phi);
17
- const ny = Math.cos(theta);
18
- const nz = Math.sin(theta) * Math.sin(phi);
19
-
20
- vertices.push(nx * radius, ny * radius, nz * radius, nx, ny, nz);
21
- }
22
- }
23
-
24
- for (let y = 0; y < rings; y++) {
25
- for (let x = 0; x < segments; x++) {
26
- const a = y * (segments + 1) + x;
27
- const b = a + segments + 1;
28
-
29
- indices.push(a, a + 1, b);
30
- indices.push(a + 1, b + 1, b);
31
- }
32
- }
33
-
34
- return {
35
- vertices: new Float32Array(vertices),
36
- indices: new Uint16Array(indices),
37
- vertexCount: (rings + 1) * (segments + 1),
38
- indexCount: rings * segments * 6,
39
- };
40
- }
@@ -1,96 +0,0 @@
1
- import { getMesh } from ".";
2
-
3
- export interface ShapeMeta {
4
- vertexOffset: number;
5
- indexOffset: number;
6
- triCount: number;
7
- _pad: number;
8
- }
9
-
10
- export interface ShapeAtlas {
11
- vertices: GPUBuffer;
12
- indices: GPUBuffer;
13
- meta: GPUBuffer;
14
- shapeCount: number;
15
- maxTriangles: number;
16
- }
17
-
18
- const MAX_SHAPES = 16;
19
-
20
- export function createShapeAtlas(device: GPUDevice): ShapeAtlas {
21
- const allVertices: number[] = [];
22
- const allIndices: number[] = [];
23
- const shapeMetas: ShapeMeta[] = [];
24
-
25
- let vertexOffset = 0;
26
- let indexOffset = 0;
27
- let maxTriangles = 0;
28
-
29
- for (let shapeId = 0; shapeId < MAX_SHAPES; shapeId++) {
30
- const mesh = getMesh(shapeId);
31
- if (!mesh) {
32
- shapeMetas.push({ vertexOffset: 0, indexOffset: 0, triCount: 0, _pad: 0 });
33
- continue;
34
- }
35
-
36
- const triCount = mesh.indexCount / 3;
37
- shapeMetas.push({
38
- vertexOffset,
39
- indexOffset,
40
- triCount,
41
- _pad: 0,
42
- });
43
-
44
- for (let i = 0; i < mesh.vertices.length; i++) {
45
- allVertices.push(mesh.vertices[i]);
46
- }
47
-
48
- for (let i = 0; i < mesh.indices.length; i++) {
49
- allIndices.push(mesh.indices[i]);
50
- }
51
-
52
- vertexOffset += mesh.vertices.length;
53
- indexOffset += mesh.indices.length;
54
- maxTriangles += triCount;
55
- }
56
-
57
- const verticesData = new Float32Array(allVertices);
58
- const indicesData = new Uint32Array(allIndices);
59
- const metaData = new Uint32Array(MAX_SHAPES * 4);
60
-
61
- for (let i = 0; i < shapeMetas.length; i++) {
62
- metaData[i * 4] = shapeMetas[i].vertexOffset;
63
- metaData[i * 4 + 1] = shapeMetas[i].indexOffset;
64
- metaData[i * 4 + 2] = shapeMetas[i].triCount;
65
- metaData[i * 4 + 3] = 0;
66
- }
67
-
68
- const vertices = device.createBuffer({
69
- label: "unified-vertices",
70
- size: Math.max(verticesData.byteLength, 16),
71
- usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
72
- });
73
- device.queue.writeBuffer(vertices, 0, verticesData);
74
-
75
- const indices = device.createBuffer({
76
- label: "unified-indices",
77
- size: Math.max(indicesData.byteLength, 16),
78
- usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
79
- });
80
- device.queue.writeBuffer(indices, 0, indicesData);
81
-
82
- const meta = device.createBuffer({
83
- label: "unified-meta",
84
- size: metaData.byteLength,
85
- usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
86
- });
87
- device.queue.writeBuffer(meta, 0, metaData);
88
-
89
- return {
90
- vertices,
91
- indices,
92
- meta,
93
- shapeCount: shapeMetas.filter((m) => m.triCount > 0).length,
94
- maxTriangles,
95
- };
96
- }