@multiplekex/shallot 0.2.5 → 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 (41) 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/orbit/index.ts +1 -1
  8. package/src/extras/text/index.ts +10 -65
  9. package/src/extras/{water.ts → water/index.ts} +59 -4
  10. package/src/standard/raster/batch.ts +149 -0
  11. package/src/standard/raster/forward.ts +832 -0
  12. package/src/standard/raster/index.ts +146 -472
  13. package/src/standard/raster/shadow.ts +408 -0
  14. package/src/standard/raytracing/bvh/blas.ts +335 -87
  15. package/src/standard/raytracing/bvh/radix.ts +225 -228
  16. package/src/standard/raytracing/bvh/refit.ts +711 -0
  17. package/src/standard/raytracing/bvh/structs.ts +0 -55
  18. package/src/standard/raytracing/bvh/tlas.ts +153 -141
  19. package/src/standard/raytracing/bvh/traverse.ts +72 -64
  20. package/src/standard/raytracing/index.ts +233 -204
  21. package/src/standard/raytracing/instance.ts +30 -18
  22. package/src/standard/raytracing/ray.ts +1 -1
  23. package/src/standard/raytracing/shaders.ts +23 -40
  24. package/src/standard/render/camera.ts +10 -28
  25. package/src/standard/render/data.ts +1 -1
  26. package/src/standard/render/index.ts +68 -12
  27. package/src/standard/render/light.ts +2 -2
  28. package/src/standard/render/mesh.ts +404 -0
  29. package/src/standard/render/overlay.ts +5 -2
  30. package/src/standard/render/postprocess.ts +263 -267
  31. package/src/standard/render/surface/index.ts +81 -12
  32. package/src/standard/render/surface/shaders.ts +265 -11
  33. package/src/standard/render/surface/structs.ts +10 -0
  34. package/src/standard/tween/tween.ts +44 -115
  35. package/src/standard/render/mesh/box.ts +0 -20
  36. package/src/standard/render/mesh/index.ts +0 -315
  37. package/src/standard/render/mesh/plane.ts +0 -11
  38. package/src/standard/render/mesh/sphere.ts +0 -40
  39. package/src/standard/render/mesh/unified.ts +0 -96
  40. package/src/standard/render/surface/compile.ts +0 -65
  41. package/src/standard/render/surface/noise.ts +0 -58
@@ -0,0 +1,404 @@
1
+ import { MAX_ENTITIES } from "../../core";
2
+ import { setTraits, createFieldProxy, type FieldProxy } from "../../core/component";
3
+
4
+ export const MAX_SURFACES = 16;
5
+ export const MAX_SHAPES = 256;
6
+ export const MAX_BATCH_SLOTS = MAX_SHAPES * MAX_SURFACES;
7
+ const INVALID_SHAPE = 0xffffffff;
8
+
9
+ let meshVersion = 0;
10
+
11
+ export function getMeshVersion(): number {
12
+ return meshVersion;
13
+ }
14
+
15
+ export function meshCount(): number {
16
+ ensureBuiltIns();
17
+ return meshes.length;
18
+ }
19
+
20
+ export interface MeshData {
21
+ vertices: Float32Array<ArrayBuffer>;
22
+ indices: Uint16Array<ArrayBuffer>;
23
+ vertexCount: number;
24
+ indexCount: number;
25
+ }
26
+
27
+ const meshes: MeshData[] = [];
28
+ const meshNames = new Map<string, number>();
29
+
30
+ function ensureBuiltIns(): void {
31
+ if (meshes.length === 0) {
32
+ meshes.push(createBox());
33
+ meshes.push(createSphere());
34
+ meshes.push(createPlane());
35
+ meshNames.set("box", 0);
36
+ meshNames.set("sphere", 1);
37
+ meshNames.set("plane", 2);
38
+ }
39
+ }
40
+
41
+ export const MeshShape = {
42
+ Box: 0,
43
+ Sphere: 1,
44
+ Plane: 2,
45
+ } as const;
46
+
47
+ export function mesh(data: MeshData, name?: string): number {
48
+ ensureBuiltIns();
49
+ if (meshes.length >= MAX_SHAPES) throw new Error(`mesh limit reached (${MAX_SHAPES})`);
50
+ const id = meshes.length;
51
+ meshes.push(data);
52
+ if (name) meshNames.set(name, id);
53
+ meshVersion++;
54
+ return id;
55
+ }
56
+
57
+ export function getMeshByName(name: string): number | undefined {
58
+ ensureBuiltIns();
59
+ return meshNames.get(name);
60
+ }
61
+
62
+ export function getMesh(id: number): MeshData | undefined {
63
+ ensureBuiltIns();
64
+ return meshes[id];
65
+ }
66
+
67
+ export function clearMeshes(): void {
68
+ meshes.length = 0;
69
+ meshNames.clear();
70
+ meshVersion++;
71
+ }
72
+
73
+ export const MeshShapes = {
74
+ data: new Uint32Array(MAX_ENTITIES).fill(INVALID_SHAPE),
75
+ };
76
+
77
+ export const MeshColors = {
78
+ data: new Float32Array(MAX_ENTITIES * 4),
79
+ };
80
+
81
+ export const MeshSizes = {
82
+ data: new Float32Array(MAX_ENTITIES * 4),
83
+ };
84
+
85
+ export const MeshPBR = {
86
+ data: new Float32Array(MAX_ENTITIES * 4),
87
+ };
88
+
89
+ export const MeshEmission = {
90
+ data: new Float32Array(MAX_ENTITIES * 4),
91
+ };
92
+
93
+ export const MeshVolumes = {
94
+ data: new Uint8Array(MAX_ENTITIES),
95
+ };
96
+
97
+ export const Volume = {
98
+ Solid: 0,
99
+ HalfSpace: 1,
100
+ } as const;
101
+
102
+ function hexColorProxy(data: Float32Array): FieldProxy {
103
+ function getValue(eid: number): number {
104
+ const offset = eid * 4;
105
+ const r = Math.round(data[offset] * 255);
106
+ const g = Math.round(data[offset + 1] * 255);
107
+ const b = Math.round(data[offset + 2] * 255);
108
+ return (r << 16) | (g << 8) | b;
109
+ }
110
+
111
+ function setValue(eid: number, value: number): void {
112
+ const offset = eid * 4;
113
+ data[offset] = ((value >> 16) & 0xff) / 255;
114
+ data[offset + 1] = ((value >> 8) & 0xff) / 255;
115
+ data[offset + 2] = (value & 0xff) / 255;
116
+ }
117
+
118
+ return new Proxy([] as unknown as FieldProxy, {
119
+ get(_, prop) {
120
+ if (prop === "get") return getValue;
121
+ if (prop === "set") return setValue;
122
+ const eid = Number(prop);
123
+ if (Number.isNaN(eid)) return undefined;
124
+ return getValue(eid);
125
+ },
126
+ set(_, prop, value) {
127
+ const eid = Number(prop);
128
+ if (Number.isNaN(eid)) return false;
129
+ setValue(eid, value);
130
+ return true;
131
+ },
132
+ });
133
+ }
134
+
135
+ export const Mesh: {
136
+ shape: Uint32Array;
137
+ color: FieldProxy;
138
+ colorR: FieldProxy;
139
+ colorG: FieldProxy;
140
+ colorB: FieldProxy;
141
+ opacity: FieldProxy;
142
+ sizeX: FieldProxy;
143
+ sizeY: FieldProxy;
144
+ sizeZ: FieldProxy;
145
+ roughness: FieldProxy;
146
+ metallic: FieldProxy;
147
+ ior: FieldProxy;
148
+ emission: FieldProxy;
149
+ emissionIntensity: FieldProxy;
150
+ volume: Uint8Array;
151
+ } = {
152
+ shape: MeshShapes.data,
153
+ color: hexColorProxy(MeshColors.data),
154
+ colorR: createFieldProxy(MeshColors.data, 4, 0),
155
+ colorG: createFieldProxy(MeshColors.data, 4, 1),
156
+ colorB: createFieldProxy(MeshColors.data, 4, 2),
157
+ opacity: createFieldProxy(MeshColors.data, 4, 3),
158
+ sizeX: createFieldProxy(MeshSizes.data, 4, 0),
159
+ sizeY: createFieldProxy(MeshSizes.data, 4, 1),
160
+ sizeZ: createFieldProxy(MeshSizes.data, 4, 2),
161
+ roughness: createFieldProxy(MeshPBR.data, 4, 0),
162
+ metallic: createFieldProxy(MeshPBR.data, 4, 1),
163
+ ior: createFieldProxy(MeshPBR.data, 4, 2),
164
+ emission: hexColorProxy(MeshEmission.data),
165
+ emissionIntensity: createFieldProxy(MeshEmission.data, 4, 3),
166
+ volume: MeshVolumes.data,
167
+ };
168
+
169
+ setTraits(Mesh, {
170
+ defaults: () => ({
171
+ shape: MeshShape.Box,
172
+ color: 0xffffff,
173
+ opacity: 1.0,
174
+ sizeX: 1,
175
+ sizeY: 1,
176
+ sizeZ: 1,
177
+ roughness: 1.0,
178
+ metallic: 0.0,
179
+ ior: 1.0,
180
+ emission: 0x000000,
181
+ emissionIntensity: 0.0,
182
+ volume: Volume.Solid,
183
+ }),
184
+ parse: { shape: getMeshByName },
185
+ });
186
+
187
+ export interface MeshBuffers {
188
+ vertex: GPUBuffer;
189
+ index: GPUBuffer;
190
+ indexCount: number;
191
+ }
192
+
193
+ export function createMeshBuffers(device: GPUDevice, mesh: MeshData): MeshBuffers {
194
+ const vertex = device.createBuffer({
195
+ label: "vertex",
196
+ size: mesh.vertices.byteLength,
197
+ usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
198
+ });
199
+ device.queue.writeBuffer(vertex, 0, mesh.vertices);
200
+
201
+ const index = device.createBuffer({
202
+ label: "index",
203
+ size: mesh.indices.byteLength,
204
+ usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST,
205
+ });
206
+ device.queue.writeBuffer(index, 0, mesh.indices);
207
+
208
+ return { vertex, index, indexCount: mesh.indexCount };
209
+ }
210
+
211
+ export interface AABB {
212
+ minX: number;
213
+ minY: number;
214
+ minZ: number;
215
+ maxX: number;
216
+ maxY: number;
217
+ maxZ: number;
218
+ }
219
+
220
+ export function computeShapeAABB(mesh: MeshData): AABB {
221
+ const { vertices, vertexCount } = mesh;
222
+ const stride = 6;
223
+
224
+ if (vertexCount === 0) {
225
+ return { minX: 0, minY: 0, minZ: 0, maxX: 0, maxY: 0, maxZ: 0 };
226
+ }
227
+
228
+ let minX = vertices[0];
229
+ let minY = vertices[1];
230
+ let minZ = vertices[2];
231
+ let maxX = vertices[0];
232
+ let maxY = vertices[1];
233
+ let maxZ = vertices[2];
234
+
235
+ for (let i = 1; i < vertexCount; i++) {
236
+ const x = vertices[i * stride];
237
+ const y = vertices[i * stride + 1];
238
+ const z = vertices[i * stride + 2];
239
+ if (x < minX) minX = x;
240
+ if (y < minY) minY = y;
241
+ if (z < minZ) minZ = z;
242
+ if (x > maxX) maxX = x;
243
+ if (y > maxY) maxY = y;
244
+ if (z > maxZ) maxZ = z;
245
+ }
246
+
247
+ return { minX, minY, minZ, maxX, maxY, maxZ };
248
+ }
249
+
250
+ export function createBox(): MeshData {
251
+ const vertices = new Float32Array([
252
+ -0.5, -0.5, 0.5, 0, 0, 1, 0.5, -0.5, 0.5, 0, 0, 1, 0.5, 0.5, 0.5, 0, 0, 1, -0.5, 0.5, 0.5,
253
+ 0, 0, 1, 0.5, -0.5, -0.5, 0, 0, -1, -0.5, -0.5, -0.5, 0, 0, -1, -0.5, 0.5, -0.5, 0, 0, -1,
254
+ 0.5, 0.5, -0.5, 0, 0, -1, -0.5, 0.5, 0.5, 0, 1, 0, 0.5, 0.5, 0.5, 0, 1, 0, 0.5, 0.5, -0.5,
255
+ 0, 1, 0, -0.5, 0.5, -0.5, 0, 1, 0, -0.5, -0.5, -0.5, 0, -1, 0, 0.5, -0.5, -0.5, 0, -1, 0,
256
+ 0.5, -0.5, 0.5, 0, -1, 0, -0.5, -0.5, 0.5, 0, -1, 0, 0.5, -0.5, 0.5, 1, 0, 0, 0.5, -0.5,
257
+ -0.5, 1, 0, 0, 0.5, 0.5, -0.5, 1, 0, 0, 0.5, 0.5, 0.5, 1, 0, 0, -0.5, -0.5, -0.5, -1, 0, 0,
258
+ -0.5, -0.5, 0.5, -1, 0, 0, -0.5, 0.5, 0.5, -1, 0, 0, -0.5, 0.5, -0.5, -1, 0, 0,
259
+ ]);
260
+
261
+ const indices = new Uint16Array([
262
+ 0, 1, 2, 0, 2, 3, 4, 5, 6, 4, 6, 7, 8, 9, 10, 8, 10, 11, 12, 13, 14, 12, 14, 15, 16, 17, 18,
263
+ 16, 18, 19, 20, 21, 22, 20, 22, 23,
264
+ ]);
265
+
266
+ return { vertices, indices, vertexCount: 24, indexCount: 36 };
267
+ }
268
+
269
+ export function createSphere(segments = 32, rings = 16): MeshData {
270
+ const vertices: number[] = [];
271
+ const indices: number[] = [];
272
+ const radius = 0.5;
273
+
274
+ for (let y = 0; y <= rings; y++) {
275
+ const v = y / rings;
276
+ const theta = v * Math.PI;
277
+
278
+ for (let x = 0; x <= segments; x++) {
279
+ const u = x / segments;
280
+ const phi = u * Math.PI * 2;
281
+
282
+ const nx = Math.sin(theta) * Math.cos(phi);
283
+ const ny = Math.cos(theta);
284
+ const nz = Math.sin(theta) * Math.sin(phi);
285
+
286
+ vertices.push(nx * radius, ny * radius, nz * radius, nx, ny, nz);
287
+ }
288
+ }
289
+
290
+ for (let y = 0; y < rings; y++) {
291
+ for (let x = 0; x < segments; x++) {
292
+ const a = y * (segments + 1) + x;
293
+ const b = a + segments + 1;
294
+
295
+ indices.push(a, a + 1, b);
296
+ indices.push(a + 1, b + 1, b);
297
+ }
298
+ }
299
+
300
+ return {
301
+ vertices: new Float32Array(vertices),
302
+ indices: new Uint16Array(indices),
303
+ vertexCount: (rings + 1) * (segments + 1),
304
+ indexCount: rings * segments * 6,
305
+ };
306
+ }
307
+
308
+ export function createPlane(): MeshData {
309
+ const vertices = new Float32Array([
310
+ -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,
311
+ ]);
312
+
313
+ const indices = new Uint16Array([0, 1, 2, 0, 2, 3]);
314
+
315
+ return { vertices, indices, vertexCount: 4, indexCount: 6 };
316
+ }
317
+
318
+ interface ShapeMeta {
319
+ vertexOffset: number;
320
+ indexOffset: number;
321
+ triCount: number;
322
+ }
323
+
324
+ export interface ShapeAtlas {
325
+ vertices: GPUBuffer;
326
+ indices: GPUBuffer;
327
+ meta: GPUBuffer;
328
+ shapeCount: number;
329
+ maxTriangles: number;
330
+ }
331
+
332
+ export function createShapeAtlas(device: GPUDevice): ShapeAtlas {
333
+ const allVertices: number[] = [];
334
+ const allIndices: number[] = [];
335
+ const shapeMetas: ShapeMeta[] = [];
336
+
337
+ let vertexOffset = 0;
338
+ let indexOffset = 0;
339
+ let maxTriangles = 0;
340
+
341
+ const shapeCount = meshCount();
342
+ for (let shapeId = 0; shapeId < shapeCount; shapeId++) {
343
+ const m = getMesh(shapeId);
344
+ if (!m) {
345
+ shapeMetas.push({ vertexOffset: 0, indexOffset: 0, triCount: 0 });
346
+ continue;
347
+ }
348
+
349
+ const triCount = m.indexCount / 3;
350
+ shapeMetas.push({ vertexOffset, indexOffset, triCount });
351
+
352
+ for (let i = 0; i < m.vertices.length; i++) {
353
+ allVertices.push(m.vertices[i]);
354
+ }
355
+
356
+ for (let i = 0; i < m.indices.length; i++) {
357
+ allIndices.push(m.indices[i]);
358
+ }
359
+
360
+ vertexOffset += m.vertices.length;
361
+ indexOffset += m.indices.length;
362
+ maxTriangles += triCount;
363
+ }
364
+
365
+ const verticesData = new Float32Array(allVertices);
366
+ const indicesData = new Uint32Array(allIndices);
367
+ const metaData = new Uint32Array(shapeCount * 4);
368
+
369
+ for (let i = 0; i < shapeMetas.length; i++) {
370
+ metaData[i * 4] = shapeMetas[i].vertexOffset;
371
+ metaData[i * 4 + 1] = shapeMetas[i].indexOffset;
372
+ metaData[i * 4 + 2] = shapeMetas[i].triCount;
373
+ metaData[i * 4 + 3] = 0;
374
+ }
375
+
376
+ const vertices = device.createBuffer({
377
+ label: "unified-vertices",
378
+ size: Math.max(verticesData.byteLength, 16),
379
+ usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
380
+ });
381
+ device.queue.writeBuffer(vertices, 0, verticesData);
382
+
383
+ const indices = device.createBuffer({
384
+ label: "unified-indices",
385
+ size: Math.max(indicesData.byteLength, 16),
386
+ usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
387
+ });
388
+ device.queue.writeBuffer(indices, 0, indicesData);
389
+
390
+ const meta = device.createBuffer({
391
+ label: "unified-meta",
392
+ size: metaData.byteLength,
393
+ usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
394
+ });
395
+ device.queue.writeBuffer(meta, 0, metaData);
396
+
397
+ return {
398
+ vertices,
399
+ indices,
400
+ meta,
401
+ shapeCount: shapeMetas.filter((m) => m.triCount > 0).length,
402
+ maxTriangles,
403
+ };
404
+ }
@@ -5,14 +5,17 @@ import { Pass, getDrawsByPass, type DrawContext, type SharedPassContext } from "
5
5
 
6
6
  export { MASK_FORMAT };
7
7
 
8
- export interface OverlayNodeConfig {
8
+ interface OverlayNodeConfig {
9
9
  state: State;
10
10
  }
11
11
 
12
12
  export function createOverlayNode(config: OverlayNodeConfig): ComputeNode {
13
13
  return {
14
14
  id: "overlay",
15
- inputs: [{ id: "z", access: "read" }],
15
+ inputs: [
16
+ { id: "z", access: "read" },
17
+ { id: "eid", access: "read" },
18
+ ],
16
19
  outputs: [
17
20
  { id: "color", access: "write" },
18
21
  { id: "mask", access: "write" },