@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
@@ -1,473 +1,52 @@
1
- import type { Plugin, State, System } from "../../core";
2
- import { resource } from "../../core";
1
+ import type { Plugin, State } from "../../core";
3
2
  import { Compute } from "../compute";
4
- import type { ComputeNode, ExecutionContext } from "../compute";
3
+ import type { ComputeNode } from "../compute";
5
4
  import { WorldTransform } from "../transforms";
6
- import { Camera, getClearColor, hasSkyComponent } from "../render/camera";
7
- import {
8
- Mesh,
9
- collectBatches,
10
- updateBatches,
11
- MAX_BATCH_SLOTS,
12
- type Batch,
13
- type MeshBuffers,
14
- type BatchEntities,
15
- } from "../render/mesh";
5
+ import { Camera, Shadows, getClearColor, hasSkyComponent } from "../render/camera";
6
+ import { DirectionalLight, normalizeDirection } from "../render/light";
7
+ import { Mesh, MeshColors } from "../render/mesh";
16
8
  import { Surface, SurfaceType, getDefaultAllSurfaces } from "../render/surface";
17
- import type { SurfaceData } from "../render/surface";
18
- import { SCENE_STRUCT_WGSL, SKY_STRUCT_WGSL, WGSL_STRUCTS } from "../render/surface/structs";
9
+ import { Render, RenderPlugin, registerShadowSettings } from "../render";
10
+ import { setTraits } from "../../core/component";
19
11
  import {
20
- compileVertexBody,
21
- WGSL_LIGHTING_CALC,
22
- SKY_WGSL,
23
- SKY_DIR_WGSL,
24
- NOISE_WGSL,
25
- } from "../render/surface/shaders";
26
- import { COLOR_FORMAT } from "../render/scene";
27
- import { createIndirectBuffer, INDIRECT_SIZE } from "../render/indirect";
28
- import { Render, RenderPlugin } from "../render";
29
-
30
- export { createRasterPipeline, createSkyPipeline, executeRasterPass };
31
- export type { RasterPassConfig };
32
-
33
- interface RasterState {
34
- indirect: GPUBuffer;
35
- batches: (Batch | null)[];
36
- batchEntities: BatchEntities;
37
- buffers: Map<number, MeshBuffers>;
38
- }
39
-
40
- export const RasterResource = resource<RasterState>("raster");
41
-
42
- const RasterUploadSystem: System = {
43
- group: "draw",
44
-
45
- update(state: State) {
46
- const render = Render.from(state);
47
- const raster = RasterResource.from(state);
48
- const compute = Compute.from(state);
49
- if (!render || !raster || !compute) return;
50
-
51
- const { device } = compute;
52
- const meshEntities = state.query([Mesh, WorldTransform]);
53
-
54
- collectBatches(
55
- meshEntities,
56
- (eid) => Surface.type[eid] ?? SurfaceType.Default,
57
- raster.batchEntities
58
- );
59
- updateBatches(device, raster.batchEntities, raster, raster.indirect);
60
- },
12
+ createShadowAtlas,
13
+ createShadowBuffer,
14
+ createShadowUploadNode,
15
+ createShadowForwardNode,
16
+ } from "./shadow";
17
+ import { createBatch, uploadBatch, type Batch } from "./batch";
18
+ import { createRasterForwardNode } from "./forward";
19
+
20
+ export { compileRasterShader, compileCompositeShader } from "./forward";
21
+
22
+ export const RasterShadowSettings = {
23
+ softness: [] as number[],
24
+ samples: [] as number[],
25
+ distance: [] as number[],
61
26
  };
62
27
 
63
- function compileSurfaceVariant(id: number, data: SurfaceData): string {
64
- const vertexBody = compileVertexBody(data.vertex);
65
- const fragmentBody = data.fragment ?? "";
66
- const lit = data.lit !== false;
67
-
68
- return `
69
- fn userVertexTransform_${id}(worldPos: vec3<f32>, normal: vec3<f32>, eid: u32) -> vec3<f32> {
70
- ${vertexBody}
71
- }
72
-
73
- fn userFragment_${id}(surface: ptr<function, SurfaceData>, position: vec4<f32>) {
74
- ${fragmentBody}
75
- }
76
-
77
- fn applyLighting_${id}(surface: SurfaceData) -> vec3<f32> {
78
- ${
79
- lit
80
- ? `${WGSL_LIGHTING_CALC}
81
- return surface.baseColor * lighting + surface.emission;`
82
- : "return surface.baseColor;"
83
- }
84
- }
85
- `;
86
- }
87
-
88
- function compileDispatchFunctions(surfaceCount: number): string {
89
- const vertexCases = Array.from(
90
- { length: surfaceCount },
91
- (_, i) => ` case ${i}u: { return userVertexTransform_${i}(worldPos, normal, eid); }`
92
- ).join("\n");
93
-
94
- const fragmentCases = Array.from(
95
- { length: surfaceCount },
96
- (_, i) => ` case ${i}u: { userFragment_${i}(surface, position); }`
97
- ).join("\n");
98
-
99
- const lightingCases = Array.from(
100
- { length: surfaceCount },
101
- (_, i) => ` case ${i}u: { return applyLighting_${i}(surface); }`
102
- ).join("\n");
103
-
104
- return `
105
- fn dispatchVertexTransform(surfaceId: u32, worldPos: vec3<f32>, normal: vec3<f32>, eid: u32) -> vec3<f32> {
106
- switch surfaceId {
107
- ${vertexCases}
108
- default: { return userVertexTransform_0(worldPos, normal, eid); }
109
- }
110
- }
111
-
112
- fn dispatchFragment(surfaceId: u32, surface: ptr<function, SurfaceData>, position: vec4<f32>) {
113
- switch surfaceId {
114
- ${fragmentCases}
115
- default: { userFragment_0(surface, position); }
116
- }
117
- }
118
-
119
- fn dispatchLighting(surfaceId: u32, surface: SurfaceData) -> vec3<f32> {
120
- switch surfaceId {
121
- ${lightingCases}
122
- default: { return applyLighting_0(surface); }
123
- }
124
- }
125
- `;
126
- }
127
-
128
- function compileRasterShader(surfaces: SurfaceData[]): string {
129
- const surfaceVariants = surfaces.map((s, i) => compileSurfaceVariant(i, s)).join("\n");
130
- const dispatchFunctions = compileDispatchFunctions(surfaces.length);
131
-
132
- return /* wgsl */ `
133
- ${WGSL_STRUCTS}
134
- ${NOISE_WGSL}
135
-
136
- ${surfaceVariants}
137
- ${dispatchFunctions}
138
-
139
- @vertex
140
- fn vs(input: VertexInput) -> VertexOutput {
141
- let eid = entityIds[input.instance];
142
- let world = matrices[eid];
143
- let scaledPos = input.position * sizes[eid].xyz;
144
- let baseWorldPos = (world * vec4<f32>(scaledPos, 1.0)).xyz;
145
- let worldNormal = normalize((world * vec4<f32>(input.normal, 0.0)).xyz);
146
- let d = data[eid];
147
- let surfaceId = d.flags & 0xFFu;
148
- let finalWorldPos = dispatchVertexTransform(surfaceId, baseWorldPos, worldNormal, eid);
149
-
150
- var output: VertexOutput;
151
- output.position = scene.viewProj * vec4<f32>(finalWorldPos, 1.0);
152
- output.color = d.baseColor;
153
- output.worldNormal = worldNormal;
154
- output.entityId = eid;
155
- output.worldPos = finalWorldPos;
156
- return output;
157
- }
158
-
159
- @fragment
160
- fn fs(input: VertexOutput) -> FragmentOutput {
161
- let eid = input.entityId;
162
- let d = data[eid];
163
- let surfaceId = d.flags & 0xFFu;
164
-
165
- var surface: SurfaceData;
166
- surface.baseColor = input.color.rgb;
167
- surface.roughness = d.pbr.x;
168
- surface.metallic = d.pbr.y;
169
- surface.emission = d.emission.rgb * d.emission.a;
170
- surface.normal = normalize(input.worldNormal);
171
- surface.worldPos = input.worldPos;
172
-
173
- dispatchFragment(surfaceId, &surface, input.position);
174
-
175
- let litColor = dispatchLighting(surfaceId, surface);
176
-
177
- var output: FragmentOutput;
178
- output.color = vec4<f32>(litColor, input.color.a);
179
- output.entityId = input.entityId;
180
- return output;
181
- }
182
- `;
183
- }
184
-
185
- async function createRasterPipeline(
186
- device: GPUDevice,
187
- surfaces: SurfaceData[],
188
- colorFormat: GPUTextureFormat
189
- ): Promise<GPURenderPipeline> {
190
- const code = compileRasterShader(surfaces);
191
- const module = device.createShaderModule({ code });
192
-
193
- return device.createRenderPipelineAsync({
194
- layout: "auto",
195
- vertex: {
196
- module,
197
- entryPoint: "vs",
198
- buffers: [
199
- {
200
- arrayStride: 24,
201
- attributes: [
202
- { shaderLocation: 0, offset: 0, format: "float32x3" },
203
- { shaderLocation: 1, offset: 12, format: "float32x3" },
204
- ],
205
- },
206
- ],
207
- },
208
- fragment: {
209
- module,
210
- entryPoint: "fs",
211
- targets: [{ format: colorFormat }, { format: "r32uint" }],
212
- },
213
- depthStencil: {
214
- format: "depth24plus",
215
- depthWriteEnabled: true,
216
- depthCompare: "less",
217
- },
218
- primitive: {
219
- topology: "triangle-list",
220
- cullMode: "back",
221
- },
222
- });
223
- }
224
-
225
- function compileSkyShader(): string {
226
- return /* wgsl */ `
227
- ${SCENE_STRUCT_WGSL}
228
- ${SKY_STRUCT_WGSL}
229
-
230
- @group(0) @binding(0) var<uniform> scene: Scene;
231
- @group(0) @binding(1) var<uniform> sky: Sky;
232
-
233
- ${SKY_DIR_WGSL}
234
- ${NOISE_WGSL}
235
- ${SKY_WGSL}
236
-
237
- struct VertexOutput {
238
- @builtin(position) position: vec4<f32>,
239
- @location(0) uv: vec2<f32>,
240
- }
241
-
242
- @vertex
243
- fn vs(@builtin(vertex_index) vertexIndex: u32) -> VertexOutput {
244
- var positions = array<vec2<f32>, 3>(
245
- vec2(-1.0, -1.0),
246
- vec2(3.0, -1.0),
247
- vec2(-1.0, 3.0)
248
- );
249
- var output: VertexOutput;
250
- output.position = vec4(positions[vertexIndex], 0.0, 1.0);
251
- output.uv = (positions[vertexIndex] + 1.0) * 0.5;
252
- output.uv.y = 1.0 - output.uv.y;
253
- return output;
254
- }
255
-
256
- @fragment
257
- fn fs(input: VertexOutput) -> @location(0) vec4<f32> {
258
- let dir = computeSkyDir(input.uv.x, input.uv.y);
259
- let color = sampleSky(dir);
260
- return vec4(color, 1.0);
261
- }
262
- `;
263
- }
264
-
265
- async function createSkyPipeline(
266
- device: GPUDevice,
267
- colorFormat: GPUTextureFormat
268
- ): Promise<GPURenderPipeline> {
269
- const code = compileSkyShader();
270
- const module = device.createShaderModule({ code });
271
-
272
- return device.createRenderPipelineAsync({
273
- layout: "auto",
274
- vertex: { module, entryPoint: "vs" },
275
- fragment: {
276
- module,
277
- entryPoint: "fs",
278
- targets: [{ format: colorFormat }],
279
- },
280
- depthStencil: {
281
- format: "depth24plus",
282
- depthWriteEnabled: false,
283
- depthCompare: "always",
284
- },
285
- primitive: { topology: "triangle-list" },
286
- });
287
- }
288
-
289
- interface RasterPassConfig {
290
- scene: GPUBuffer;
291
- sky: GPUBuffer;
292
- data: GPUBuffer;
293
- matrices: GPUBuffer;
294
- sizes: GPUBuffer;
295
- indirect: GPUBuffer;
296
- rasterPipeline: GPURenderPipeline;
297
- skyPipeline: GPURenderPipeline | null;
298
- getClearColor: () => { r: number; g: number; b: number };
299
- getSky: () => boolean;
300
- batches: () => (Batch | null)[];
301
- skyBindGroup?: GPUBindGroup | null;
302
- batchBindGroups?: GPUBindGroup[];
303
- }
304
-
305
- function executeRasterPass(ctx: ExecutionContext, config: RasterPassConfig): void {
306
- const { device, encoder } = ctx;
307
-
308
- const colorView = ctx.getTextureView("color");
309
- const eidView = ctx.getTextureView("eid");
310
- const zView = ctx.getTextureView("z");
311
-
312
- if (!colorView || !eidView || !zView) {
313
- return;
314
- }
315
-
316
- const batches = config.batches();
317
- const colorTexture = ctx.getTexture("color");
318
- if (!colorTexture) return;
28
+ setTraits(RasterShadowSettings, {
29
+ defaults: () => ({
30
+ softness: 0,
31
+ samples: 1,
32
+ distance: 100,
33
+ }),
34
+ });
319
35
 
320
- const clearColor = config.getClearColor();
321
- const hasSky = config.getSky() && config.skyPipeline;
322
-
323
- if (hasSky) {
324
- const skyPass = encoder.beginRenderPass({
325
- colorAttachments: [
326
- {
327
- view: colorView as GPUTextureView,
328
- clearValue: { r: clearColor.r, g: clearColor.g, b: clearColor.b, a: 1 },
329
- loadOp: "clear" as const,
330
- storeOp: "store" as const,
331
- },
332
- ],
333
- depthStencilAttachment: {
334
- view: zView as GPUTextureView,
335
- depthClearValue: 1.0,
336
- depthLoadOp: "clear" as const,
337
- depthStoreOp: "store" as const,
338
- },
339
- });
340
-
341
- if (!config.skyBindGroup) {
342
- config.skyBindGroup = device.createBindGroup({
343
- layout: config.skyPipeline!.getBindGroupLayout(0),
344
- entries: [
345
- { binding: 0, resource: { buffer: config.scene } },
346
- { binding: 1, resource: { buffer: config.sky } },
347
- ],
348
- });
349
- }
350
-
351
- skyPass.setPipeline(config.skyPipeline!);
352
- skyPass.setBindGroup(0, config.skyBindGroup);
353
- skyPass.draw(3);
354
- skyPass.end();
355
- }
356
-
357
- const pass = encoder.beginRenderPass({
358
- colorAttachments: [
359
- {
360
- view: colorView as GPUTextureView,
361
- clearValue: { r: clearColor.r, g: clearColor.g, b: clearColor.b, a: 1 },
362
- loadOp: hasSky ? ("load" as const) : ("clear" as const),
363
- storeOp: "store" as const,
364
- },
365
- {
366
- view: eidView as GPUTextureView,
367
- clearValue: { r: 0, g: 0, b: 0, a: 0 },
368
- loadOp: "clear" as const,
369
- storeOp: "store" as const,
370
- },
371
- ],
372
- depthStencilAttachment: {
373
- view: zView as GPUTextureView,
374
- depthClearValue: 1.0,
375
- depthLoadOp: hasSky ? ("load" as const) : ("clear" as const),
376
- depthStoreOp: "store" as const,
377
- },
378
- });
379
-
380
- pass.setPipeline(config.rasterPipeline);
381
-
382
- if (!config.batchBindGroups) {
383
- config.batchBindGroups = [];
384
- }
385
-
386
- for (let i = 0; i < batches.length; i++) {
387
- const batch = batches[i];
388
- if (!batch || batch.count === 0) continue;
389
-
390
- if (!config.batchBindGroups[i]) {
391
- config.batchBindGroups[i] = device.createBindGroup({
392
- layout: config.rasterPipeline.getBindGroupLayout(0),
393
- entries: [
394
- { binding: 0, resource: { buffer: config.scene } },
395
- { binding: 1, resource: { buffer: batch.entityIds } },
396
- { binding: 2, resource: { buffer: config.matrices } },
397
- { binding: 3, resource: { buffer: config.sizes } },
398
- { binding: 4, resource: { buffer: config.data } },
399
- ],
400
- });
401
- }
402
-
403
- pass.setBindGroup(0, config.batchBindGroups[i]);
404
- pass.setVertexBuffer(0, batch.buffers.vertex);
405
- pass.setIndexBuffer(batch.buffers.index, "uint16");
406
- pass.drawIndexedIndirect(config.indirect, i * INDIRECT_SIZE);
407
- }
408
-
409
- pass.end();
410
- }
411
-
412
- interface RasterForwardConfig {
413
- scene: GPUBuffer;
414
- sky: GPUBuffer;
415
- data: GPUBuffer;
416
- indirect: GPUBuffer;
417
- matrices: GPUBuffer;
418
- sizes: GPUBuffer;
419
- getSurfaces: () => SurfaceData[];
420
- getClearColor: () => { r: number; g: number; b: number };
421
- getSky: () => boolean;
422
- batches: () => (Batch | null)[];
423
- }
424
-
425
- function createRasterForwardNode(config: RasterForwardConfig): ComputeNode {
426
- let rasterPipeline: GPURenderPipeline | null = null;
427
- let skyPipeline: GPURenderPipeline | null = null;
428
- let skyBindGroup: GPUBindGroup | null = null;
429
- let batchBindGroups: GPUBindGroup[] = [];
430
-
431
- return {
432
- id: "forward",
433
- inputs: [{ id: "data", access: "read" }],
434
- outputs: [
435
- { id: "color", access: "write" },
436
- { id: "eid", access: "write" },
437
- ],
438
-
439
- async prepare(device: GPUDevice) {
440
- const surfaces = config.getSurfaces();
441
- rasterPipeline = await createRasterPipeline(device, surfaces, COLOR_FORMAT);
442
- skyPipeline = await createSkyPipeline(device, COLOR_FORMAT);
443
- },
36
+ import { resource } from "../../core";
444
37
 
445
- execute(ctx: ExecutionContext) {
446
- if (!rasterPipeline) return;
447
- const passConfig: RasterPassConfig = {
448
- scene: config.scene,
449
- sky: config.sky,
450
- data: config.data,
451
- matrices: config.matrices,
452
- sizes: config.sizes,
453
- indirect: config.indirect,
454
- rasterPipeline,
455
- skyPipeline,
456
- getClearColor: config.getClearColor,
457
- getSky: config.getSky,
458
- batches: config.batches,
459
- skyBindGroup,
460
- batchBindGroups,
461
- };
462
- executeRasterPass(ctx, passConfig);
463
- skyBindGroup = passConfig.skyBindGroup ?? null;
464
- batchBindGroups = passConfig.batchBindGroups ?? [];
465
- },
466
- };
467
- }
38
+ export const RasterResource = resource<{
39
+ batching: Batch;
40
+ shadowAtlas: GPUTexture;
41
+ shadowBuffer: GPUBuffer;
42
+ rtRendered: boolean;
43
+ }>("raster");
468
44
 
469
45
  export const RasterPlugin: Plugin = {
470
- systems: [RasterUploadSystem],
46
+ systems: [],
47
+ components: {
48
+ RasterShadowSettings,
49
+ },
471
50
  dependencies: [RenderPlugin],
472
51
 
473
52
  async initialize(state: State) {
@@ -475,42 +54,137 @@ export const RasterPlugin: Plugin = {
475
54
  const render = Render.from(state);
476
55
  if (!compute || !render) return;
477
56
 
57
+ registerShadowSettings({
58
+ softness: RasterShadowSettings.softness,
59
+ samples: RasterShadowSettings.samples,
60
+ isActive: (s, eid) => s.hasComponent(eid, RasterShadowSettings),
61
+ });
62
+
478
63
  const { device } = compute;
479
64
 
480
- const rasterState: RasterState = {
481
- indirect: createIndirectBuffer(device, MAX_BATCH_SLOTS),
482
- batches: Array(MAX_BATCH_SLOTS).fill(null),
483
- batchEntities: Array(MAX_BATCH_SLOTS).fill(null),
484
- buffers: new Map(),
65
+ const shadowAtlas = createShadowAtlas(device);
66
+ const shadowBuffer = createShadowBuffer(device);
67
+ const batching = createBatch(device);
68
+
69
+ const rasterState = {
70
+ batching,
71
+ shadowAtlas,
72
+ shadowBuffer,
73
+ rtRendered: false,
485
74
  };
486
75
 
487
76
  state.setResource(RasterResource, rasterState);
488
77
 
489
- const getActiveClearColor = () => {
78
+ const getActiveCamera = () => {
490
79
  for (const eid of state.query([Camera])) {
491
- if (Camera.active[eid]) return getClearColor(eid);
80
+ if (Camera.active[eid]) return eid;
492
81
  }
82
+ return -1;
83
+ };
84
+
85
+ const getActiveClearColor = () => {
86
+ const eid = getActiveCamera();
87
+ if (eid >= 0) return getClearColor(eid);
493
88
  return { r: 0, g: 0, b: 0 };
494
89
  };
495
90
 
496
91
  const getSky = () => {
497
- for (const eid of state.query([Camera])) {
498
- if (Camera.active[eid]) return hasSkyComponent(state, eid);
92
+ const eid = getActiveCamera();
93
+ return eid >= 0 && hasSkyComponent(state, eid);
94
+ };
95
+
96
+ const getShadowsEnabled = () => {
97
+ const eid = getActiveCamera();
98
+ return eid >= 0 && state.hasComponent(eid, Shadows);
99
+ };
100
+
101
+ const getShadowDistance = () => {
102
+ const eid = getActiveCamera();
103
+ if (eid >= 0 && state.hasComponent(eid, RasterShadowSettings)) {
104
+ return RasterShadowSettings.distance[eid];
105
+ }
106
+ return 100;
107
+ };
108
+
109
+ const getCameraData = () => {
110
+ const eid = getActiveCamera();
111
+ if (eid >= 0) {
112
+ return {
113
+ world: WorldTransform.data.subarray(eid * 16, eid * 16 + 16),
114
+ fov: Camera.fov[eid],
115
+ near: Camera.near[eid],
116
+ far: Camera.far[eid],
117
+ width: render.width,
118
+ height: render.height,
119
+ };
499
120
  }
500
- return false;
121
+ return null;
501
122
  };
502
123
 
124
+ const getLightDir = (): [number, number, number] => {
125
+ for (const eid of state.query([DirectionalLight])) {
126
+ const [dx, dy, dz] = normalizeDirection(
127
+ DirectionalLight.directionX[eid],
128
+ DirectionalLight.directionY[eid],
129
+ DirectionalLight.directionZ[eid]
130
+ );
131
+ return [dx, dy, dz];
132
+ }
133
+ return [-0.5, -1.0, -0.5];
134
+ };
135
+
136
+ const getOpacity = (eid: number) => MeshColors.data[eid * 4 + 3];
137
+
138
+ const batchUploadNode: ComputeNode = {
139
+ id: "batch-upload",
140
+ inputs: [{ id: "data", access: "read" }],
141
+ outputs: [{ id: "batched", access: "write" }],
142
+ execute() {
143
+ const meshEntities = state.query([Mesh, WorldTransform]);
144
+ uploadBatch(
145
+ device,
146
+ meshEntities,
147
+ (eid) => Surface.type[eid] ?? SurfaceType.Default,
148
+ getOpacity,
149
+ rasterState.batching
150
+ );
151
+ },
152
+ };
153
+ compute.graph.add(batchUploadNode);
154
+
155
+ const shadowUploadNode = createShadowUploadNode({
156
+ shadow: shadowBuffer,
157
+ getCameraData,
158
+ getLightDir,
159
+ shadowsEnabled: getShadowsEnabled,
160
+ getShadowDistance,
161
+ });
162
+ compute.graph.add(shadowUploadNode);
163
+
164
+ const shadowNodes = createShadowForwardNode({
165
+ shadow: shadowBuffer,
166
+ matrices: render.matrices,
167
+ sizes: render.sizes,
168
+ batching: rasterState.batching,
169
+ atlas: shadowAtlas,
170
+ shadowsEnabled: getShadowsEnabled,
171
+ });
172
+ for (const node of shadowNodes) compute.graph.add(node);
173
+
503
174
  const forwardNode = createRasterForwardNode({
504
175
  scene: render.scene,
505
176
  sky: render.sky,
506
177
  data: render.data,
507
- indirect: rasterState.indirect,
508
178
  matrices: render.matrices,
509
179
  sizes: render.sizes,
180
+ batching: rasterState.batching,
181
+ shadowBuffer,
182
+ shadowAtlas,
510
183
  getSurfaces: getDefaultAllSurfaces,
511
184
  getClearColor: getActiveClearColor,
512
185
  getSky,
513
- batches: () => rasterState.batches,
186
+ getShadowsEnabled,
187
+ getRTRendered: () => rasterState.rtRendered,
514
188
  });
515
189
  compute.graph.add(forwardNode);
516
190
  },