@multiplekex/shallot 0.1.12 → 0.2.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 +3 -4
  2. package/src/core/builder.ts +71 -32
  3. package/src/core/component.ts +25 -11
  4. package/src/core/index.ts +14 -13
  5. package/src/core/math.ts +135 -0
  6. package/src/core/runtime.ts +0 -1
  7. package/src/core/state.ts +9 -68
  8. package/src/core/xml.ts +381 -265
  9. package/src/editor/format.ts +5 -0
  10. package/src/editor/index.ts +101 -0
  11. package/src/extras/arrows/index.ts +28 -69
  12. package/src/extras/gradient/index.ts +36 -52
  13. package/src/extras/lines/index.ts +51 -122
  14. package/src/extras/orbit/index.ts +40 -15
  15. package/src/extras/text/font.ts +546 -0
  16. package/src/extras/text/index.ts +158 -204
  17. package/src/extras/text/sdf.ts +429 -0
  18. package/src/standard/activity/index.ts +172 -0
  19. package/src/standard/compute/graph.ts +23 -23
  20. package/src/standard/compute/index.ts +76 -61
  21. package/src/standard/defaults.ts +8 -5
  22. package/src/standard/index.ts +1 -0
  23. package/src/standard/input/index.ts +30 -19
  24. package/src/standard/loading/index.ts +18 -13
  25. package/src/standard/render/bvh/blas.ts +752 -0
  26. package/src/standard/render/bvh/radix.ts +476 -0
  27. package/src/standard/render/bvh/structs.ts +167 -0
  28. package/src/standard/render/bvh/tlas.ts +886 -0
  29. package/src/standard/render/bvh/traverse.ts +467 -0
  30. package/src/standard/render/camera.ts +302 -27
  31. package/src/standard/render/data.ts +93 -0
  32. package/src/standard/render/depth.ts +117 -0
  33. package/src/standard/render/forward/index.ts +259 -0
  34. package/src/standard/render/forward/raster.ts +228 -0
  35. package/src/standard/render/index.ts +443 -70
  36. package/src/standard/render/indirect.ts +40 -0
  37. package/src/standard/render/instance.ts +214 -0
  38. package/src/standard/render/intersection.ts +72 -0
  39. package/src/standard/render/light.ts +16 -16
  40. package/src/standard/render/mesh/index.ts +67 -75
  41. package/src/standard/render/mesh/unified.ts +96 -0
  42. package/src/standard/render/{transparent.ts → overlay.ts} +14 -15
  43. package/src/standard/render/pass.ts +10 -4
  44. package/src/standard/render/postprocess.ts +142 -64
  45. package/src/standard/render/ray.ts +61 -0
  46. package/src/standard/render/scene.ts +38 -164
  47. package/src/standard/render/shaders.ts +484 -0
  48. package/src/standard/render/surface/compile.ts +3 -10
  49. package/src/standard/render/surface/index.ts +60 -30
  50. package/src/standard/render/surface/noise.ts +45 -0
  51. package/src/standard/render/surface/structs.ts +60 -19
  52. package/src/standard/render/surface/wgsl.ts +573 -0
  53. package/src/standard/render/triangle.ts +84 -0
  54. package/src/standard/transforms/index.ts +4 -6
  55. package/src/standard/tween/index.ts +10 -1
  56. package/src/standard/tween/sequence.ts +24 -16
  57. package/src/standard/tween/tween.ts +67 -16
  58. package/src/core/types.ts +0 -37
  59. package/src/standard/compute/inspect.ts +0 -201
  60. package/src/standard/compute/pass.ts +0 -23
  61. package/src/standard/compute/timing.ts +0 -139
  62. package/src/standard/render/forward.ts +0 -273
@@ -1,9 +1,29 @@
1
1
  import { Not } from "bitecs";
2
2
  import type { Plugin, State, System } from "../../core";
3
3
  import { MAX_ENTITIES, resource } from "../../core";
4
- import { Compute, ComputePlugin } from "../compute";
4
+ import { Canvas, Compute, ComputePlugin } from "../compute";
5
5
  import { WorldTransform } from "../transforms";
6
- import { Camera, Tonemap, FXAA, Vignette, uploadCamera } from "./camera";
6
+ import { Activity } from "../activity";
7
+ import {
8
+ Camera,
9
+ Tonemap,
10
+ FXAA,
11
+ Raytracing,
12
+ Vignette,
13
+ Bloom,
14
+ Quantize,
15
+ Shadows,
16
+ Reflections,
17
+ Refractions,
18
+ Haze,
19
+ Sky,
20
+ Moon,
21
+ Stars,
22
+ Clouds,
23
+ Sun,
24
+ Viewport,
25
+ uploadCamera,
26
+ } from "./camera";
7
27
  import { AmbientLight, DirectionalLight, packLightUniforms } from "./light";
8
28
  import {
9
29
  Mesh,
@@ -12,71 +32,254 @@ import {
12
32
  MeshSizes,
13
33
  MeshPBR,
14
34
  MeshEmission,
15
- collectByShapeAndSurface,
35
+ MeshVolumes,
36
+ collectBatches,
16
37
  updateBatches,
17
38
  MAX_BATCH_SLOTS,
18
- type ShapeBatch,
39
+ type Batch,
19
40
  type MeshBuffers,
41
+ type BatchEntities,
42
+ createShapeAtlas,
20
43
  } from "./mesh";
21
- import { Surface, SurfaceIds, SurfaceType } from "./surface";
22
- import { createSceneBuffer, ensureRenderTextures } from "./scene";
23
- import { createForwardNode, createIndirectBuffer } from "./forward";
44
+ import { Surface, SurfaceIds, SurfaceType, getDefaultAllSurfaces } from "./surface";
45
+ import { createSceneBuffer, ensureTextures } from "./scene";
46
+ import { createIndirectBuffer } from "./indirect";
47
+ import { createForwardNode } from "./forward";
48
+ import { createDepthConvertNode } from "./depth";
24
49
  import { createPostProcessNode, type PostProcessUniforms } from "./postprocess";
25
- import { createTransparentNode } from "./transparent";
26
- import { Draws, type DrawState } from "./pass";
27
-
28
- export * from "./camera";
29
- export * from "./light";
30
- export * from "./mesh";
31
- export * from "./surface";
32
- export * from "./scene";
33
- export * from "./forward";
34
- export * from "./postprocess";
35
- export * from "./transparent";
36
- export * from "./pass";
37
-
38
- export interface RenderState {
50
+ import { createOverlayNode } from "./overlay";
51
+ import { Draws } from "./pass";
52
+ import { createBLASAtlas, type BLASAtlas } from "./bvh/blas";
53
+ import { createTLASBuffers, createTLASNode, type TLASBuffers } from "./bvh/tlas";
54
+ import { getMesh } from "./mesh";
55
+ import { createInstanceNode } from "./instance";
56
+ import { createDataNode } from "./data";
57
+
58
+ export {
59
+ Camera,
60
+ CameraMode,
61
+ RenderMode,
62
+ Tonemap,
63
+ FXAA,
64
+ Raytracing,
65
+ Vignette,
66
+ Bloom,
67
+ Quantize,
68
+ Shadows,
69
+ Reflections,
70
+ Refractions,
71
+ Haze,
72
+ Sky,
73
+ Moon,
74
+ Stars,
75
+ Clouds,
76
+ Sun,
77
+ Viewport,
78
+ } from "./camera";
79
+ export { AmbientLight, DirectionalLight } from "./light";
80
+ export { Mesh, MeshShape, Volume, mesh, getMesh, clearMeshes } from "./mesh";
81
+ export { createBox } from "./mesh/box";
82
+ export { createSphere } from "./mesh/sphere";
83
+ export { createPlane } from "./mesh/plane";
84
+ export type { MeshData } from "./mesh";
85
+ export { Surface, SurfaceType } from "./surface";
86
+
87
+ export { surface, compose } from "./surface";
88
+ export type { SurfaceData } from "./surface";
89
+
90
+ export { Pass, registerDraw, unregisterDraw, getDrawsByPass, Draws } from "./pass";
91
+ export type { Draw, DrawContext, SharedPassContext } from "./pass";
92
+ export { createForwardNode, compileRasterShader, compileRTShader } from "./forward";
93
+ export type { ForwardConfig, ForwardNode } from "./forward";
94
+ export { createDepthConvertNode } from "./depth";
95
+ export { createPostProcessNode } from "./postprocess";
96
+ export type { PostProcessUniforms } from "./postprocess";
97
+ export { createOverlayNode } from "./overlay";
98
+
99
+ const EntityIds = {
100
+ data: new Uint32Array(MAX_ENTITIES),
101
+ };
102
+
103
+ const countBuffer = new Uint32Array(1);
104
+
105
+ export interface Render {
106
+ width: number;
107
+ height: number;
108
+ entityCount: number;
39
109
  scene: GPUBuffer;
40
110
  matrices: GPUBuffer;
111
+ }
112
+
113
+ interface RenderState extends Render {
41
114
  colors: GPUBuffer;
42
115
  sizes: GPUBuffer;
43
- shapes: GPUBuffer;
44
116
  pbr: GPUBuffer;
45
117
  emission: GPUBuffer;
46
- surfaceIds: GPUBuffer;
118
+ shapes: GPUBuffer;
119
+ surfaces: GPUBuffer;
120
+ data: GPUBuffer;
47
121
  indirect: GPUBuffer;
48
- batches: Map<string, ShapeBatch>;
122
+ batches: (Batch | null)[];
123
+ batchEntities: BatchEntities;
49
124
  buffers: Map<number, MeshBuffers>;
50
125
  postProcess: PostProcessUniforms;
51
- entityCount: number;
126
+ meshVertices: GPUBuffer;
127
+ meshIndices: GPUBuffer;
128
+ meshMeta: GPUBuffer;
129
+ blasAtlas: BLASAtlas;
130
+ instanceAABBs: GPUBuffer;
131
+ instanceInverses: GPUBuffer;
132
+ entityCountBuffer: GPUBuffer;
133
+ instanceCount: GPUBuffer;
134
+ tlas: TLASBuffers;
52
135
  }
53
136
 
54
- export const Render = resource<RenderState>("render");
137
+ export const Render = resource<Render>("render");
138
+
139
+ function getRenderState(state: State): RenderState | undefined {
140
+ return Render.from(state) as RenderState | undefined;
141
+ }
55
142
 
56
143
  const RenderSystem: System = {
57
144
  group: "draw",
58
145
  first: true,
59
146
 
60
147
  update(state: State) {
61
- const render = Render.from(state);
148
+ const render = getRenderState(state);
62
149
  const compute = Compute.from(state);
63
- if (!render || !compute || !state.canvas) return;
150
+ const canvas = Canvas.from(state);
151
+ if (!render || !compute || !canvas) return;
152
+
153
+ const { device } = compute;
154
+ const { element, format } = canvas;
155
+ const { resources } = compute;
156
+ const { width, height } = element;
157
+
158
+ let renderWidth = width;
159
+ let renderHeight = height;
64
160
 
65
- const { device, format, resources } = compute;
66
- const { width, height } = state.canvas;
161
+ for (const eid of state.query([Camera])) {
162
+ if (Camera.active[eid]) {
163
+ if (state.hasComponent(eid, Viewport)) {
164
+ const vw = Viewport.width[eid];
165
+ const vh = Viewport.height[eid];
166
+ if (vw > 0 && vh > 0) {
167
+ renderWidth = vw;
168
+ renderHeight = vh;
169
+ } else if (vh > 0 && height > 0) {
170
+ renderHeight = vh;
171
+ renderWidth = Math.max(1, Math.round(vh * (width / height)));
172
+ } else if (vw > 0 && width > 0) {
173
+ renderWidth = vw;
174
+ renderHeight = Math.max(1, Math.round(vw * (height / width)));
175
+ }
176
+ }
177
+ break;
178
+ }
179
+ }
180
+
181
+ render.width = renderWidth;
182
+ render.height = renderHeight;
67
183
 
68
- ensureRenderTextures(
184
+ ensureTextures(
69
185
  device,
70
186
  format,
71
- width,
72
- height,
187
+ renderWidth,
188
+ renderHeight,
73
189
  resources.textures,
74
190
  resources.textureViews
75
191
  );
76
192
 
193
+ render.entityCount = state.maxEid + 1;
194
+ const uploadCount = render.entityCount;
195
+
77
196
  for (const eid of state.query([Camera])) {
78
197
  if (Camera.active[eid]) {
79
- uploadCamera(device, render.scene, eid, width, height);
198
+ const hasShadows = state.hasComponent(eid, Shadows);
199
+ const shadowSoftness = hasShadows ? Shadows.softness[eid] : 0;
200
+ const shadowSamples = hasShadows ? Math.max(1, Shadows.samples[eid]) : 0;
201
+
202
+ const hasReflections = state.hasComponent(eid, Reflections);
203
+ const reflectionDepth = hasReflections
204
+ ? Math.min(4, Math.max(1, Reflections.depth[eid]))
205
+ : 0;
206
+
207
+ const hasRefractions = state.hasComponent(eid, Refractions);
208
+ const refractionDepth = hasRefractions
209
+ ? Math.min(4, Math.max(1, Refractions.depth[eid]))
210
+ : 0;
211
+
212
+ const hasHaze = state.hasComponent(eid, Haze);
213
+ const hazeParams = hasHaze
214
+ ? {
215
+ density: Haze.density[eid],
216
+ color: Haze.color[eid],
217
+ }
218
+ : undefined;
219
+
220
+ const hasSky = state.hasComponent(eid, Sky);
221
+ const skyParams = hasSky
222
+ ? {
223
+ zenith: Sky.zenith[eid],
224
+ horizon: Sky.horizon[eid],
225
+ }
226
+ : undefined;
227
+
228
+ const hasMoon = state.hasComponent(eid, Moon);
229
+ const moonParams = hasMoon
230
+ ? {
231
+ phase: Moon.phase[eid],
232
+ glow: Moon.glow[eid],
233
+ azimuth: Moon.azimuth[eid],
234
+ elevation: Moon.elevation[eid],
235
+ }
236
+ : undefined;
237
+
238
+ const hasStars = state.hasComponent(eid, Stars);
239
+ const starsParams = hasStars
240
+ ? {
241
+ intensity: Stars.intensity[eid],
242
+ amount: Stars.amount[eid],
243
+ }
244
+ : undefined;
245
+
246
+ const hasClouds = state.hasComponent(eid, Clouds);
247
+ const cloudsParams = hasClouds
248
+ ? {
249
+ coverage: Clouds.coverage[eid],
250
+ density: Clouds.density[eid],
251
+ height: Clouds.height[eid],
252
+ color: Clouds.color[eid],
253
+ }
254
+ : undefined;
255
+
256
+ const hasSun = state.hasComponent(eid, Sun);
257
+ const sunParams = hasSun
258
+ ? {
259
+ size: Sun.size[eid],
260
+ glow: Sun.glow[eid],
261
+ color: Sun.color[eid],
262
+ }
263
+ : undefined;
264
+
265
+ uploadCamera(
266
+ device,
267
+ render.scene,
268
+ eid,
269
+ renderWidth,
270
+ renderHeight,
271
+ shadowSoftness,
272
+ shadowSamples,
273
+ reflectionDepth,
274
+ refractionDepth,
275
+ uploadCount,
276
+ hazeParams,
277
+ skyParams,
278
+ moonParams,
279
+ starsParams,
280
+ cloudsParams,
281
+ sunParams
282
+ );
80
283
 
81
284
  render.postProcess.tonemap = state.hasComponent(eid, Tonemap);
82
285
  if (render.postProcess.tonemap) {
@@ -90,6 +293,20 @@ const RenderSystem: System = {
90
293
  } else {
91
294
  render.postProcess.vignetteStrength = 0;
92
295
  }
296
+ if (state.hasComponent(eid, Bloom)) {
297
+ render.postProcess.bloomIntensity = Bloom.intensity[eid];
298
+ render.postProcess.bloomThreshold = Bloom.threshold[eid];
299
+ render.postProcess.bloomRadius = Bloom.radius[eid];
300
+ } else {
301
+ render.postProcess.bloomIntensity = 0;
302
+ render.postProcess.bloomThreshold = 0.8;
303
+ render.postProcess.bloomRadius = 0.5;
304
+ }
305
+ if (state.hasComponent(eid, Quantize)) {
306
+ render.postProcess.quantize = Quantize.bands[eid];
307
+ } else {
308
+ render.postProcess.quantize = 0;
309
+ }
93
310
  break;
94
311
  }
95
312
  }
@@ -125,33 +342,52 @@ const RenderSystem: System = {
125
342
  const lightUniforms = packLightUniforms(ambientData, directionalData);
126
343
  device.queue.writeBuffer(render.scene, 128, lightUniforms as Float32Array<ArrayBuffer>);
127
344
 
128
- render.entityCount = state.maxEid + 1;
129
- const uploadCount = render.entityCount;
130
345
  device.queue.writeBuffer(
131
346
  render.matrices,
132
347
  0,
133
348
  WorldTransform.data as Float32Array<ArrayBuffer>,
134
349
  0,
135
- uploadCount * 64
350
+ uploadCount * 16
136
351
  );
137
352
 
138
353
  const meshEntities = state.query([Mesh, WorldTransform]);
139
- device.queue.writeBuffer(render.colors, 0, MeshColors.data, 0, uploadCount * 16);
140
- device.queue.writeBuffer(render.sizes, 0, MeshSizes.data, 0, uploadCount * 16);
141
- device.queue.writeBuffer(render.shapes, 0, MeshShapes.data, 0, uploadCount * 4);
142
- device.queue.writeBuffer(render.pbr, 0, MeshPBR.data, 0, uploadCount * 16);
143
- device.queue.writeBuffer(render.emission, 0, MeshEmission.data, 0, uploadCount * 16);
354
+ device.queue.writeBuffer(render.colors, 0, MeshColors.data, 0, uploadCount * 4);
355
+ device.queue.writeBuffer(render.sizes, 0, MeshSizes.data, 0, uploadCount * 4);
356
+ device.queue.writeBuffer(render.pbr, 0, MeshPBR.data, 0, uploadCount * 4);
357
+ device.queue.writeBuffer(render.emission, 0, MeshEmission.data, 0, uploadCount * 4);
358
+ device.queue.writeBuffer(render.shapes, 0, MeshShapes.data, 0, uploadCount);
359
+ countBuffer[0] = uploadCount;
360
+ device.queue.writeBuffer(render.entityCountBuffer, 0, countBuffer);
361
+
362
+ let meshCount = 0;
363
+ for (const eid of meshEntities) {
364
+ EntityIds.data[meshCount] = eid;
365
+ meshCount++;
366
+ }
367
+ device.queue.writeBuffer(
368
+ render.tlas.entityIds,
369
+ 0,
370
+ EntityIds.data,
371
+ 0,
372
+ Math.max(meshCount, 1)
373
+ );
374
+ countBuffer[0] = meshCount;
375
+ device.queue.writeBuffer(render.instanceCount, 0, countBuffer);
144
376
 
145
377
  for (const eid of state.query([Surface])) {
146
- SurfaceIds.data[eid] = Surface.type[eid];
378
+ const surfaceType = Surface.type[eid] & 0xff;
379
+ const volume = (MeshVolumes.data[eid] ?? 0) & 0xf;
380
+ const shapeId = (Mesh.shape[eid] ?? 0) & 0xffff;
381
+ SurfaceIds.data[eid] = surfaceType | (volume << 8) | (shapeId << 16);
147
382
  }
148
- device.queue.writeBuffer(render.surfaceIds, 0, SurfaceIds.data, 0, uploadCount * 4);
383
+ device.queue.writeBuffer(render.surfaces, 0, SurfaceIds.data, 0, uploadCount);
149
384
 
150
- const byShapeAndSurface = collectByShapeAndSurface(
385
+ collectBatches(
151
386
  meshEntities,
152
- (eid) => Surface.type[eid] ?? SurfaceType.Default
387
+ (eid) => Surface.type[eid] ?? SurfaceType.Default,
388
+ render.batchEntities
153
389
  );
154
- updateBatches(device, byShapeAndSurface, render, render.indirect);
390
+ updateBatches(device, render.batchEntities, render, render.indirect);
155
391
  },
156
392
  };
157
393
 
@@ -174,34 +410,52 @@ export const RenderPlugin: Plugin = {
174
410
  DirectionalLight,
175
411
  Tonemap,
176
412
  FXAA,
413
+ Raytracing,
177
414
  Vignette,
415
+ Bloom,
416
+ Quantize,
417
+ Shadows,
418
+ Reflections,
419
+ Refractions,
420
+ Haze,
421
+ Sky,
422
+ Moon,
423
+ Stars,
424
+ Clouds,
425
+ Sun,
426
+ Viewport,
178
427
  },
179
428
  dependencies: [ComputePlugin],
180
429
 
181
- initialize(state: State) {
430
+ async initialize(state: State, onProgress?: (progress: number) => void) {
182
431
  const compute = Compute.from(state);
183
432
  if (!compute) return;
184
433
 
185
434
  const { device } = compute;
186
435
 
187
- const createPropertyBuffer = (size: number) =>
436
+ const createPropertyBuffer = (size: number, label?: string) =>
188
437
  device.createBuffer({
189
- label: "property",
438
+ label: label ?? "property",
190
439
  size,
191
440
  usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST | GPUBufferUsage.COPY_SRC,
192
441
  });
193
442
 
443
+ const shapeAtlas = createShapeAtlas(device);
444
+ const blasAtlas = createBLASAtlas(device, getMesh);
445
+
194
446
  const renderState: RenderState = {
195
447
  scene: createSceneBuffer(device),
196
- matrices: createPropertyBuffer(MAX_ENTITIES * 64),
197
- colors: createPropertyBuffer(MAX_ENTITIES * 16),
198
- sizes: createPropertyBuffer(MAX_ENTITIES * 16),
199
- shapes: createPropertyBuffer(MAX_ENTITIES * 4),
200
- pbr: createPropertyBuffer(MAX_ENTITIES * 16),
201
- emission: createPropertyBuffer(MAX_ENTITIES * 16),
202
- surfaceIds: createPropertyBuffer(MAX_ENTITIES * 4),
448
+ matrices: createPropertyBuffer(MAX_ENTITIES * 64, "matrices"),
449
+ colors: createPropertyBuffer(MAX_ENTITIES * 16, "colors"),
450
+ sizes: createPropertyBuffer(MAX_ENTITIES * 16, "sizes"),
451
+ pbr: createPropertyBuffer(MAX_ENTITIES * 16, "pbr"),
452
+ emission: createPropertyBuffer(MAX_ENTITIES * 16, "emission"),
453
+ shapes: createPropertyBuffer(MAX_ENTITIES * 4, "shapes"),
454
+ surfaces: createPropertyBuffer(MAX_ENTITIES * 4, "surfaces"),
455
+ data: createPropertyBuffer(MAX_ENTITIES * 64, "data"),
203
456
  indirect: createIndirectBuffer(device, MAX_BATCH_SLOTS),
204
- batches: new Map(),
457
+ batches: Array(MAX_BATCH_SLOTS).fill(null),
458
+ batchEntities: Array(MAX_BATCH_SLOTS).fill(null),
205
459
  buffers: new Map(),
206
460
  entityCount: 1,
207
461
  postProcess: {
@@ -211,38 +465,157 @@ export const RenderPlugin: Plugin = {
211
465
  vignetteStrength: 0,
212
466
  vignetteInner: 0.4,
213
467
  vignetteOuter: 0.8,
468
+ bloomIntensity: 0,
469
+ bloomThreshold: 0.8,
470
+ bloomRadius: 0.5,
471
+ quantize: 0,
214
472
  },
473
+ meshVertices: shapeAtlas.vertices,
474
+ meshIndices: shapeAtlas.indices,
475
+ meshMeta: shapeAtlas.meta,
476
+ blasAtlas,
477
+ instanceAABBs: createPropertyBuffer(MAX_ENTITIES * 32, "instanceAABBs"),
478
+ instanceInverses: createPropertyBuffer(MAX_ENTITIES * 64, "instanceInverses"),
479
+ entityCountBuffer: device.createBuffer({
480
+ label: "entityCount",
481
+ size: 4,
482
+ usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST | GPUBufferUsage.COPY_SRC,
483
+ }),
484
+ instanceCount: device.createBuffer({
485
+ label: "instanceCount",
486
+ size: 4,
487
+ usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST | GPUBufferUsage.COPY_SRC,
488
+ }),
489
+ tlas: createTLASBuffers(device),
490
+ width: 0,
491
+ height: 0,
215
492
  };
216
493
 
217
494
  state.setResource(Render, renderState);
218
495
 
219
- const drawState: DrawState = {
496
+ const drawState: Draws = {
220
497
  draws: new Map(),
221
498
  };
222
499
  state.setResource(Draws, drawState);
223
500
 
501
+ const instanceNode = createInstanceNode({
502
+ matrices: renderState.matrices,
503
+ sizes: renderState.sizes,
504
+ shapes: renderState.shapes,
505
+ shapeAABBs: renderState.blasAtlas.shapeAABBs,
506
+ entityCount: renderState.entityCountBuffer,
507
+ instanceAABBs: renderState.instanceAABBs,
508
+ instanceInverses: renderState.instanceInverses,
509
+ getEntityCount: () => renderState.entityCount,
510
+ });
511
+ compute.graph.add(instanceNode);
512
+
513
+ const dataNode = createDataNode({
514
+ colors: renderState.colors,
515
+ pbr: renderState.pbr,
516
+ emission: renderState.emission,
517
+ surfaces: renderState.surfaces,
518
+ entityCount: renderState.entityCountBuffer,
519
+ data: renderState.data,
520
+ getEntityCount: () => renderState.entityCount,
521
+ });
522
+ compute.graph.add(dataNode);
523
+
524
+ const tlasNode = createTLASNode({
525
+ instanceAABBs: renderState.instanceAABBs,
526
+ instanceCount: renderState.instanceCount,
527
+ tlas: renderState.tlas,
528
+ getEntityCount: () => renderState.entityCount,
529
+ });
530
+ compute.graph.add(tlasNode);
531
+
532
+ const getRaytracing = () => {
533
+ for (const eid of state.query([Camera])) {
534
+ if (Camera.active[eid]) {
535
+ return state.hasComponent(eid, Raytracing);
536
+ }
537
+ }
538
+ return false;
539
+ };
540
+
541
+ const getClearColor = () => {
542
+ for (const eid of state.query([Camera])) {
543
+ if (Camera.active[eid]) {
544
+ const packed = Camera.clearColor[eid];
545
+ return {
546
+ r: ((packed >> 16) & 0xff) / 255,
547
+ g: ((packed >> 8) & 0xff) / 255,
548
+ b: (packed & 0xff) / 255,
549
+ };
550
+ }
551
+ }
552
+ return { r: 0, g: 0, b: 0 };
553
+ };
554
+
555
+ const getSky = () => {
556
+ for (const eid of state.query([Camera])) {
557
+ if (Camera.active[eid]) {
558
+ return state.hasComponent(eid, Sky);
559
+ }
560
+ }
561
+ return false;
562
+ };
563
+
564
+ const forwardNode = createForwardNode({
565
+ scene: renderState.scene,
566
+ matrices: renderState.matrices,
567
+ colors: renderState.colors,
568
+ sizes: renderState.sizes,
569
+ pbr: renderState.pbr,
570
+ emission: renderState.emission,
571
+ shapes: renderState.shapes,
572
+ surfaces: renderState.surfaces,
573
+ data: renderState.data,
574
+ getSurfaces: getDefaultAllSurfaces,
575
+ getRaytracing,
576
+ getClearColor,
577
+ getSky,
578
+ acquire: (message) => Activity.from(state)?.acquire(message),
579
+ batches: () => renderState.batches,
580
+ tlasNodes: renderState.tlas.bvhNodes,
581
+ tlasInstanceIds: renderState.tlas.instanceIds,
582
+ blasNodes: renderState.blasAtlas.nodesBuffer,
583
+ blasTriIds: renderState.blasAtlas.triIdsBuffer,
584
+ blasTriangles: renderState.blasAtlas.trianglesBuffer,
585
+ blasMeta: renderState.blasAtlas.metaBuffer,
586
+ instanceInverses: renderState.instanceInverses,
587
+ });
588
+ compute.graph.add(forwardNode);
589
+
224
590
  compute.graph.add(
225
- createForwardNode({
226
- state,
591
+ createDepthConvertNode({
227
592
  scene: renderState.scene,
228
- matrices: renderState.matrices,
229
- colors: renderState.colors,
230
- sizes: renderState.sizes,
231
- shapes: renderState.shapes,
232
- pbr: renderState.pbr,
233
- emission: renderState.emission,
234
- indirect: renderState.indirect,
235
- batches: renderState.batches,
593
+ getRaytracing,
236
594
  })
237
595
  );
238
596
 
239
- compute.graph.add(createTransparentNode({ state }));
597
+ compute.graph.add(createOverlayNode({ state }));
240
598
 
241
599
  compute.graph.add(
242
600
  createPostProcessNode({
243
601
  state,
244
602
  uniforms: renderState.postProcess,
603
+ getRenderSize: () => ({
604
+ width: renderState.width,
605
+ height: renderState.height,
606
+ }),
245
607
  })
246
608
  );
609
+
610
+ onProgress?.(1);
611
+ },
612
+
613
+ async warm(state: State, onProgress?: (progress: number) => void) {
614
+ const compute = Compute.from(state);
615
+ if (!compute) return;
616
+
617
+ await compute.graph.prepare(compute.device, (done, total) => {
618
+ onProgress?.(done / total);
619
+ });
247
620
  },
248
621
  };
@@ -0,0 +1,40 @@
1
+ export const INDIRECT_SIZE = 20;
2
+
3
+ export interface IndirectArgs {
4
+ indexCount: number;
5
+ instanceCount: number;
6
+ firstIndex: number;
7
+ baseVertex: number;
8
+ firstInstance: number;
9
+ }
10
+
11
+ export function createIndirectBuffer(device: GPUDevice, slotCount: number): GPUBuffer {
12
+ return device.createBuffer({
13
+ label: "indirect",
14
+ size: slotCount * INDIRECT_SIZE,
15
+ usage:
16
+ GPUBufferUsage.INDIRECT |
17
+ GPUBufferUsage.STORAGE |
18
+ GPUBufferUsage.COPY_DST |
19
+ GPUBufferUsage.COPY_SRC,
20
+ });
21
+ }
22
+
23
+ export function writeIndirect(
24
+ device: GPUDevice,
25
+ buffer: GPUBuffer,
26
+ slot: number,
27
+ args: IndirectArgs
28
+ ): void {
29
+ const offset = slot * INDIRECT_SIZE;
30
+ const data = new ArrayBuffer(INDIRECT_SIZE);
31
+ const view = new DataView(data);
32
+
33
+ view.setUint32(0, args.indexCount, true);
34
+ view.setUint32(4, args.instanceCount, true);
35
+ view.setUint32(8, args.firstIndex, true);
36
+ view.setInt32(12, args.baseVertex, true);
37
+ view.setUint32(16, args.firstInstance, true);
38
+
39
+ device.queue.writeBuffer(buffer, offset, data);
40
+ }