@aethervtt/shard-render 0.0.1 → 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 (170) hide show
  1. package/dist/ablation.d.ts +74 -0
  2. package/dist/ablation.js +117 -0
  3. package/dist/assets.d.ts +3 -0
  4. package/dist/assets.js +43 -2
  5. package/dist/atmosphere-nodes.d.ts +9 -0
  6. package/dist/atmosphere-nodes.js +157 -30
  7. package/dist/atmosphere-plugin.js +8 -2
  8. package/dist/atmosphere-shaders.js +17 -5
  9. package/dist/baseline/atmosphere.d.ts +16 -0
  10. package/dist/baseline/atmosphere.js +301 -0
  11. package/dist/baseline/data-texture.d.ts +36 -0
  12. package/dist/baseline/data-texture.js +181 -0
  13. package/dist/baseline/environment.d.ts +14 -0
  14. package/dist/baseline/environment.js +368 -0
  15. package/dist/baseline/exposure.d.ts +25 -0
  16. package/dist/baseline/exposure.js +133 -0
  17. package/dist/baseline/lights.d.ts +33 -0
  18. package/dist/baseline/lights.js +133 -0
  19. package/dist/baseline/textures.d.ts +97 -0
  20. package/dist/baseline/textures.js +203 -0
  21. package/dist/camera.d.ts +2 -0
  22. package/dist/camera.js +4 -0
  23. package/dist/crowd.d.ts +21 -0
  24. package/dist/crowd.js +121 -0
  25. package/dist/culling-shaders.js +21 -2
  26. package/dist/culling.d.ts +11 -2
  27. package/dist/culling.js +44 -18
  28. package/dist/cutaway-plugin.d.ts +48 -0
  29. package/dist/cutaway-plugin.js +364 -0
  30. package/dist/cutaway.d.ts +27 -0
  31. package/dist/cutaway.js +49 -0
  32. package/dist/data-store.d.ts +69 -0
  33. package/dist/data-store.js +105 -0
  34. package/dist/debug-shaders.js +17 -3
  35. package/dist/debug-views.js +4 -2
  36. package/dist/decals-plugin.d.ts +5 -0
  37. package/dist/decals-plugin.js +54 -0
  38. package/dist/decals.d.ts +69 -0
  39. package/dist/decals.js +326 -0
  40. package/dist/deferred-shaders.js +2 -0
  41. package/dist/deferred.js +28 -4
  42. package/dist/deform.d.ts +5 -4
  43. package/dist/deform.js +4 -9
  44. package/dist/describe-plugin.d.ts +4 -2
  45. package/dist/describe-plugin.js +75 -3
  46. package/dist/display-nodes.js +37 -13
  47. package/dist/dynamic-resolution.d.ts +2 -0
  48. package/dist/dynamic-resolution.js +6 -1
  49. package/dist/environment-plugin.js +17 -2
  50. package/dist/environment-state.d.ts +7 -3
  51. package/dist/environment-state.js +20 -9
  52. package/dist/environment.js +7 -1
  53. package/dist/features.d.ts +49 -0
  54. package/dist/features.js +106 -0
  55. package/dist/foliage-budget.d.ts +64 -0
  56. package/dist/foliage-budget.js +108 -0
  57. package/dist/foliage-path.d.ts +19 -0
  58. package/dist/foliage-path.js +7 -0
  59. package/dist/foliage-shaders.d.ts +11 -0
  60. package/dist/foliage-shaders.js +504 -0
  61. package/dist/foliage.d.ts +194 -0
  62. package/dist/foliage.js +1027 -0
  63. package/dist/forward.d.ts +38 -2
  64. package/dist/forward.js +608 -186
  65. package/dist/fxaa.js +12 -2
  66. package/dist/gizmos-plugin.js +7 -0
  67. package/dist/gizmos.d.ts +3 -3
  68. package/dist/gizmos.js +11 -13
  69. package/dist/gpu-assets.d.ts +58 -4
  70. package/dist/gpu-assets.js +191 -8
  71. package/dist/graph.d.ts +45 -0
  72. package/dist/graph.js +169 -19
  73. package/dist/health.d.ts +58 -0
  74. package/dist/health.js +112 -0
  75. package/dist/index.d.ts +40 -11
  76. package/dist/index.js +343 -9
  77. package/dist/instances.d.ts +119 -8
  78. package/dist/instances.js +387 -78
  79. package/dist/interior-plugin.d.ts +121 -0
  80. package/dist/interior-plugin.js +465 -0
  81. package/dist/interior.d.ts +24 -0
  82. package/dist/interior.js +13 -0
  83. package/dist/layers.d.ts +35 -0
  84. package/dist/layers.js +49 -0
  85. package/dist/lens-plugin.d.ts +43 -0
  86. package/dist/lens-plugin.js +304 -0
  87. package/dist/lens.d.ts +41 -0
  88. package/dist/lens.js +99 -0
  89. package/dist/lights.d.ts +50 -5
  90. package/dist/lights.js +101 -22
  91. package/dist/material-pipelines.d.ts +37 -4
  92. package/dist/material-pipelines.js +102 -5
  93. package/dist/materials.d.ts +23 -0
  94. package/dist/materials.js +44 -1
  95. package/dist/outline-plugin.d.ts +48 -0
  96. package/dist/outline-plugin.js +529 -0
  97. package/dist/outline.d.ts +15 -0
  98. package/dist/outline.js +42 -0
  99. package/dist/overlay-registry.d.ts +3 -1
  100. package/dist/overlay-registry.js +3 -1
  101. package/dist/overlays.js +6 -2
  102. package/dist/perf-overlay.d.ts +9 -0
  103. package/dist/perf-overlay.js +125 -0
  104. package/dist/picking.d.ts +17 -0
  105. package/dist/picking.js +40 -11
  106. package/dist/pixel-perfect-plugin.js +10 -2
  107. package/dist/plugin.d.ts +69 -7
  108. package/dist/plugin.js +223 -25
  109. package/dist/post-common.d.ts +22 -2
  110. package/dist/post-common.js +82 -2
  111. package/dist/post-nodes.d.ts +7 -1
  112. package/dist/post-nodes.js +235 -111
  113. package/dist/post-plugin.js +5 -3
  114. package/dist/post-scene.d.ts +130 -0
  115. package/dist/post-scene.js +164 -0
  116. package/dist/post-shaders.js +85 -62
  117. package/dist/post.js +11 -1
  118. package/dist/projection.d.ts +24 -0
  119. package/dist/projection.js +87 -0
  120. package/dist/readback.d.ts +1 -0
  121. package/dist/readback.js +1 -0
  122. package/dist/render-scale.js +1 -0
  123. package/dist/ribbons-plugin.d.ts +5 -0
  124. package/dist/ribbons-plugin.js +59 -0
  125. package/dist/ribbons.d.ts +44 -0
  126. package/dist/ribbons.js +288 -0
  127. package/dist/scenario.d.ts +70 -0
  128. package/dist/scenario.js +125 -0
  129. package/dist/screen-effects.d.ts +97 -0
  130. package/dist/screen-effects.js +192 -0
  131. package/dist/shaders.d.ts +13 -0
  132. package/dist/shaders.js +189 -34
  133. package/dist/shadow-catcher.d.ts +9 -0
  134. package/dist/shadow-catcher.js +133 -0
  135. package/dist/shadows.d.ts +27 -2
  136. package/dist/shadows.js +100 -20
  137. package/dist/standard-fields.d.ts +1 -0
  138. package/dist/standard-fields.js +5 -0
  139. package/dist/standard.js +18 -0
  140. package/dist/stats.d.ts +45 -1
  141. package/dist/stats.js +91 -3
  142. package/dist/surface-model.d.ts +57 -0
  143. package/dist/surface-model.js +289 -0
  144. package/dist/surface-shaders.d.ts +3 -0
  145. package/dist/surface-shaders.js +160 -0
  146. package/dist/surface.d.ts +40 -0
  147. package/dist/surface.js +86 -0
  148. package/dist/target.d.ts +1 -13
  149. package/dist/target.js +1 -26
  150. package/dist/testing.d.ts +11 -0
  151. package/dist/testing.js +39 -1
  152. package/dist/tier.d.ts +13 -0
  153. package/dist/tier.js +14 -0
  154. package/dist/timer.d.ts +18 -1
  155. package/dist/timer.js +73 -20
  156. package/dist/variants.d.ts +37 -0
  157. package/dist/variants.js +104 -0
  158. package/dist/vfx-draw.d.ts +37 -0
  159. package/dist/vfx-draw.js +291 -0
  160. package/dist/vfx-state.d.ts +42 -0
  161. package/dist/vfx-state.js +81 -0
  162. package/dist/vfx.d.ts +69 -0
  163. package/dist/vfx.js +103 -0
  164. package/dist/view-visibility-plugin.d.ts +61 -0
  165. package/dist/view-visibility-plugin.js +267 -0
  166. package/dist/view-visibility.d.ts +28 -0
  167. package/dist/view-visibility.js +46 -0
  168. package/dist/view.d.ts +29 -1
  169. package/dist/view.js +57 -34
  170. package/package.json +23 -11
package/dist/shadows.js CHANGED
@@ -2,7 +2,11 @@ import { defineResource, frustum, mat4 } from "@aethervtt/shard-core";
2
2
  import { GpuBuffer } from "@aethervtt/shard-gpu";
3
3
  import { Culler } from "./culling.js";
4
4
  import { GpuAssetsResource } from "./gpu-assets.js";
5
- import { createDrawList, InstanceFlags, Instances } from "./instances.js";
5
+ import {
6
+ createDrawList,
7
+ InstanceFlags,
8
+ Instances
9
+ } from "./instances.js";
6
10
  import { setShadowIndex } from "./lights.js";
7
11
  import {
8
12
  materialVariant,
@@ -10,6 +14,7 @@ import {
10
14
  variantBlend,
11
15
  variantCull
12
16
  } from "./material-pipelines.js";
17
+ import { bindingDimension } from "./tier.js";
13
18
  const MAX_CASCADES = 4;
14
19
  const MAX_SHADOWED_SPOTS = 8;
15
20
  const MAX_SHADOWED_POINTS = 4;
@@ -31,7 +36,30 @@ function pointTanHalf(size) {
31
36
  }
32
37
  const VIEW_STRIDE = 256;
33
38
  function shadowView() {
34
- return { viewProj: mat4.create(), frustum: frustum.create(), draws: createDrawList(), offset: 0 };
39
+ return {
40
+ viewProj: mat4.create(),
41
+ frustum: frustum.create(),
42
+ cullPlanes: frustum.create(),
43
+ draws: createDrawList(),
44
+ offset: 0,
45
+ drawn: false,
46
+ drawnViewProj: mat4.create(),
47
+ drawnEpoch: -1,
48
+ redraw: true
49
+ };
50
+ }
51
+ function shadowRedraw(view, cached, store) {
52
+ if (!cached || !view.drawn || view.drawnEpoch !== store.shadowEpoch || store.deforming)
53
+ return true;
54
+ const a = view.viewProj;
55
+ const b = view.drawnViewProj;
56
+ for (let i = 0; i < 16; i++) if (a[i] !== b[i]) return true;
57
+ return store.movedCasterIn(view.cullPlanes);
58
+ }
59
+ function markShadowDrawn(view, store) {
60
+ view.drawn = true;
61
+ view.drawnViewProj.set(view.viewProj);
62
+ view.drawnEpoch = store.shadowEpoch;
35
63
  }
36
64
  class Cascades {
37
65
  count = 0;
@@ -46,11 +74,13 @@ class Cascades {
46
74
  ensureTexture(gpu, size, label) {
47
75
  if (!this.texture || this.size !== size || this.generation !== gpu.generation) {
48
76
  this.texture?.destroy();
77
+ for (const view of this.views) view.drawn = false;
49
78
  this.texture = gpu.device.createTexture({
50
79
  label: `${label}/cascades`,
51
80
  size: [size, size, MAX_CASCADES],
52
81
  format: "depth32float",
53
- usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_SRC
82
+ usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_SRC,
83
+ ...bindingDimension(gpu, "2d-array")
54
84
  });
55
85
  this.size = size;
56
86
  this.generation = gpu.generation;
@@ -160,6 +190,7 @@ function fitCascades(cascades, cam, sun, size, cull) {
160
190
  set(4, -basis[6], -basis[7], -basis[8], lz + radius);
161
191
  set(5, 0, 0, 0, 1);
162
192
  const view = cascades.views[i];
193
+ view.cullPlanes.set(p);
163
194
  const any = cull(view, p, scratchBox);
164
195
  let zNear = lz - radius;
165
196
  if (any) {
@@ -228,15 +259,18 @@ class LocalShadows {
228
259
  label,
229
260
  size: [size, size, layers],
230
261
  format: "depth32float",
231
- usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_SRC
262
+ usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_SRC,
263
+ ...bindingDimension(gpu, "2d-array")
232
264
  });
233
265
  if (fresh || !this.spotTexture || spotLayers > this.spotLayers) {
234
266
  this.spotTexture?.destroy();
267
+ for (const view of this.spotViews) view.drawn = false;
235
268
  this.spotLayers = Math.max(spotLayers, fresh ? 1 : this.spotLayers);
236
269
  this.spotTexture = make("shadows/spots", this.spotLayers);
237
270
  }
238
271
  if (fresh || !this.pointTexture || pointLayers > this.pointLayers) {
239
272
  this.pointTexture?.destroy();
273
+ for (const view of this.pointViews) view.drawn = false;
240
274
  this.pointLayers = Math.max(pointLayers, fresh ? 6 : this.pointLayers);
241
275
  this.pointTexture = make("shadows/points", this.pointLayers);
242
276
  }
@@ -317,6 +351,7 @@ function fitLocalShadows(shadows, size) {
317
351
  const view = shadows.spotViews[i];
318
352
  mat4.multiply(view.viewProj, scratchProj, scratchView);
319
353
  frustum.fromViewProjection(view.frustum, view.viewProj);
354
+ view.cullPlanes.set(view.frustum);
320
355
  shadows.spotParams[i * 4] = tanHalf2;
321
356
  shadows.spotParams[i * 4 + 1] = near;
322
357
  }
@@ -335,6 +370,7 @@ function fitLocalShadows(shadows, size) {
335
370
  const view = shadows.pointViews[i * 6 + f];
336
371
  mat4.multiply(view.viewProj, scratchProj, scratchView);
337
372
  frustum.fromViewProjection(view.frustum, view.viewProj);
373
+ view.cullPlanes.set(view.frustum);
338
374
  }
339
375
  }
340
376
  }
@@ -377,6 +413,11 @@ class ShadowPassUniforms {
377
413
  bindGroup;
378
414
  bound = "";
379
415
  gpu;
416
+ /** What the buffer holds, so unchanged shadow views write nothing (0055). */
417
+ sent = new Float32Array(0);
418
+ sentCount = 0;
419
+ sentVersion = -1;
420
+ sentGeneration = -1;
380
421
  constructor(gpu) {
381
422
  this.gpu = gpu;
382
423
  this.buffer = new GpuBuffer(gpu, {
@@ -400,7 +441,15 @@ class ShadowPassUniforms {
400
441
  return this.count++ * VIEW_STRIDE;
401
442
  }
402
443
  upload(layout, globals) {
403
- if (this.count > 0) this.buffer.write(this.data, 0, 0, this.count * VIEW_STRIDE / 4);
444
+ const floats = this.count * VIEW_STRIDE / 4;
445
+ if (this.count > 0 && !this.unchanged(floats)) {
446
+ this.buffer.write(this.data, 0, 0, floats);
447
+ if (this.sent.length < this.data.length) this.sent = new Float32Array(this.data.length);
448
+ this.sent.set(this.data.subarray(0, floats));
449
+ this.sentCount = this.count;
450
+ this.sentVersion = this.buffer.version;
451
+ this.sentGeneration = this.gpu.generation;
452
+ }
404
453
  const key = `${this.buffer.version}/${globals.version}/${this.gpu.generation}`;
405
454
  if (!this.bindGroup || this.bound !== key) {
406
455
  this.bindGroup = this.gpu.device.createBindGroup({
@@ -414,6 +463,15 @@ class ShadowPassUniforms {
414
463
  this.bound = key;
415
464
  }
416
465
  }
466
+ unchanged(floats) {
467
+ if (this.sentCount < this.count) return false;
468
+ if (this.sentVersion !== this.buffer.version || this.sentGeneration !== this.gpu.generation)
469
+ return false;
470
+ const data = this.data;
471
+ const sent = this.sent;
472
+ for (let i = 0; i < floats; i++) if (data[i] !== sent[i]) return false;
473
+ return true;
474
+ }
417
475
  }
418
476
  function shadowViewLayout(gpu) {
419
477
  return gpu.layouts.bindGroupLayout({
@@ -439,7 +497,7 @@ const SHADOW_BUFFERS = [
439
497
  { arrayStride: 8, attributes: [{ shaderLocation: 3, offset: 0, format: "float32x2" }] },
440
498
  { arrayStride: 16, attributes: [{ shaderLocation: 4, offset: 0, format: "float32x4" }] }
441
499
  ];
442
- function drawShadowCasters(ctx, target, view, offset, uniforms, pipelines, shadowView2, label) {
500
+ function drawShadowCasters(ctx, target, view, offset, uniforms, pipelines, shadowView2, label, foliage, cam, cascade = 0) {
443
501
  const world = ctx.world;
444
502
  const gpu = ctx.gpu;
445
503
  const store = world.resource(Instances);
@@ -458,6 +516,7 @@ function drawShadowCasters(ctx, target, view, offset, uniforms, pipelines, shado
458
516
  const draws0 = view.draws;
459
517
  const instances = draws0.cullView >= 0 ? store.gpuBindGroup : store.bindGroup;
460
518
  if (!instances || !uniforms.bindGroup) {
519
+ if (foliage && cam && uniforms.bindGroup) foliage.drawShadow(ctx, cam, pass, offset, cascade);
461
520
  pass.end();
462
521
  return;
463
522
  }
@@ -472,7 +531,8 @@ function drawShadowCasters(ctx, target, view, offset, uniforms, pipelines, shado
472
531
  const draws = view.draws;
473
532
  for (let d = 0; d < draws.length; d++) {
474
533
  const item = draws.items[d];
475
- const material = item.batch.material;
534
+ const fallback = pipelines.failing(item.batch.material.type);
535
+ const material = fallback ? pipelines.proxy(item.batch.material) : item.batch.material;
476
536
  const type = material.type;
477
537
  const variant = materialVariant(material);
478
538
  const masked = type.standard && variantBlend(variant) === "mask";
@@ -481,21 +541,38 @@ function drawShadowCasters(ctx, target, view, offset, uniforms, pipelines, shado
481
541
  if (!pipeline) {
482
542
  const module = pipelines.module(world, gpu, type, 8, "shard::pbr::shadow", void 0);
483
543
  if (!module) {
484
- gpu.pipelines.skipped++;
544
+ if (!fallback && pipelines.failing(type)) d--;
545
+ else gpu.pipelines.skipped++;
546
+ continue;
547
+ }
548
+ pipeline = pipelines.create(
549
+ gpu,
550
+ key,
551
+ {
552
+ label: `shadow/${type.name}/${masked ? "mask" : "opaque"}/${variantCull(variant)}`,
553
+ layout: gpu.layouts.pipelineLayout({
554
+ label: `shadow/${type.name}`,
555
+ bindGroupLayouts: [shadowView2, assets.layoutOf(type), store.layout]
556
+ }),
557
+ vertex: { module, entryPoint: "vs", buffers: SHADOW_BUFFERS },
558
+ fragment: masked ? { module, entryPoint: "fs_mask", targets: [] } : void 0,
559
+ primitive: {
560
+ topology: "triangle-list",
561
+ cullMode: variantCull(variant),
562
+ frontFace: "ccw"
563
+ },
564
+ depthStencil: {
565
+ format: "depth32float",
566
+ depthWriteEnabled: true,
567
+ depthCompare: "greater"
568
+ }
569
+ },
570
+ type
571
+ );
572
+ if (!pipeline) {
573
+ if (!fallback && pipelines.failing(type)) d--;
485
574
  continue;
486
575
  }
487
- pipeline = pipelines.create(gpu, key, {
488
- label: `shadow/${type.name}/${masked ? "mask" : "opaque"}/${variantCull(variant)}`,
489
- layout: gpu.layouts.pipelineLayout({
490
- label: `shadow/${type.name}`,
491
- bindGroupLayouts: [shadowView2, assets.layoutOf(type), store.layout]
492
- }),
493
- vertex: { module, entryPoint: "vs", buffers: SHADOW_BUFFERS },
494
- fragment: masked ? { module, entryPoint: "fs_mask", targets: [] } : void 0,
495
- primitive: { topology: "triangle-list", cullMode: variantCull(variant), frontFace: "ccw" },
496
- depthStencil: { format: "depth32float", depthWriteEnabled: true, depthCompare: "greater" }
497
- });
498
- if (!pipeline) continue;
499
576
  }
500
577
  const mat = assets.material(world, material);
501
578
  if (!mat?.bindGroup) continue;
@@ -531,6 +608,7 @@ function drawShadowCasters(ctx, target, view, offset, uniforms, pipelines, shado
531
608
  pass.draw(gm.count, item.count, 0, item.first);
532
609
  }
533
610
  }
611
+ if (foliage && cam) foliage.drawShadow(ctx, cam, pass, offset, cascade);
534
612
  pass.end();
535
613
  }
536
614
  const PASS_SHADOW = 2;
@@ -555,8 +633,10 @@ export {
555
633
  drawShadowCasters,
556
634
  fitCascades,
557
635
  fitLocalShadows,
636
+ markShadowDrawn,
558
637
  packShadowData,
559
638
  pointTanHalf,
639
+ shadowRedraw,
560
640
  shadowViewLayout,
561
641
  sliceSphere
562
642
  };
@@ -15,6 +15,7 @@ export declare const STANDARD_FIELDS: {
15
15
  emissive: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").Color, "f32">;
16
16
  emissiveLuminance: import("@aethervtt/shard-core").FieldType<number, "f32">;
17
17
  doubleSided: import("@aethervtt/shard-core").FieldType<boolean, "u8">;
18
+ deferUntilReady: import("@aethervtt/shard-core").FieldType<boolean, "u8">;
18
19
  alphaMode: import("@aethervtt/shard-core").FieldType<"additive" | "alpha" | "mask" | "opaque" | "premultiplied", "u8">;
19
20
  alphaCutoff: import("@aethervtt/shard-core").FieldType<number, "f32">;
20
21
  normalScale: import("@aethervtt/shard-core").FieldType<number, "f32">;
@@ -42,6 +42,11 @@ const STANDARD_FIELDS = {
42
42
  description: "Emitted luminance. 0 = not emissive."
43
43
  }),
44
44
  doubleSided: t.bool({ description: "Draw back faces too (no culling)." }),
45
+ deferUntilReady: t.bool({
46
+ // The asset layer reads it; shaders don't.
47
+ gpu: false,
48
+ description: "Baseline tier (0064): while a texture it binds waits for a copy in its slot's color space, skip its draws instead of showing the slot's loading fallback."
49
+ }),
45
50
  alphaMode: t.enum(["opaque", "mask", "alpha", "additive", "premultiplied"], {
46
51
  description: "opaque ignores alpha; mask discards pixels below alphaCutoff; alpha blends (transparent, drawn after opaque, sorted back to front); additive adds light (glows); premultiplied expects color already multiplied by alpha."
47
52
  }),
package/dist/standard.js CHANGED
@@ -1,16 +1,25 @@
1
1
  import { definePlugin } from "@aethervtt/shard-runtime";
2
2
  import { atmospherePlugin } from "./atmosphere-plugin.js";
3
+ import { cutawayPlugin } from "./cutaway-plugin.js";
4
+ import { decalsPlugin } from "./decals-plugin.js";
3
5
  import { deferredPlugin } from "./deferred-plugin.js";
4
6
  import { renderDescribePlugin } from "./describe-plugin.js";
5
7
  import { dynamicResolutionPlugin } from "./dynamic-resolution.js";
6
8
  import { environmentPlugin } from "./environment-plugin.js";
9
+ import { foliagePlugin } from "./foliage.js";
7
10
  import { forwardCorePlugin } from "./forward.js";
8
11
  import { fxaaPlugin } from "./fxaa.js";
9
12
  import { gizmosPlugin } from "./gizmos-plugin.js";
13
+ import { interiorPlugin } from "./interior-plugin.js";
14
+ import { lensPlugin } from "./lens-plugin.js";
15
+ import { outlinePlugin } from "./outline-plugin.js";
10
16
  import { pickingPlugin } from "./picking-plugin.js";
11
17
  import { pixelPerfectPlugin } from "./pixel-perfect-plugin.js";
12
18
  import { postPlugin } from "./post-plugin.js";
19
+ import { ribbonsPlugin } from "./ribbons-plugin.js";
20
+ import { shadowCatcherPlugin } from "./shadow-catcher.js";
13
21
  import { skinningPlugin } from "./skinning-plugin.js";
22
+ import { viewVisibilityPlugin } from "./view-visibility-plugin.js";
14
23
  function forwardPlugin(options = {}) {
15
24
  return definePlugin({
16
25
  name: "render/standard",
@@ -26,7 +35,16 @@ function forwardPlugin(options = {}) {
26
35
  deferredPlugin,
27
36
  skinningPlugin,
28
37
  pixelPerfectPlugin,
38
+ lensPlugin,
39
+ outlinePlugin,
40
+ cutawayPlugin,
41
+ viewVisibilityPlugin,
42
+ interiorPlugin,
43
+ shadowCatcherPlugin,
44
+ foliagePlugin,
29
45
  dynamicResolutionPlugin,
46
+ ribbonsPlugin,
47
+ decalsPlugin,
30
48
  renderDescribePlugin
31
49
  );
32
50
  }
package/dist/stats.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { type GpuUploads, type UploadCategory } from '@aethervtt/shard-gpu';
1
2
  export interface ViewStats {
2
3
  visible: number;
3
4
  culled: number;
@@ -8,7 +9,50 @@ export interface ViewStats {
8
9
  /** Pipeline changes in the opaque pass (sorting keeps these near the number of material types). */
9
10
  pipelineSwitches?: number;
10
11
  }
11
- export declare const RenderStats: import("@aethervtt/shard-core").ResourceDef<Map<string, ViewStats>>;
12
+ /** What one frame (or a window of frames) wrote to the GPU and rebuilt (0055). */
13
+ export interface FrameRecord {
14
+ /** Bytes written, by what they were for. */
15
+ bytes: Record<UploadCategory, number>;
16
+ /** Scene data: every byte but `view` (camera and per-frame uniforms). */
17
+ sceneBytes: number;
18
+ /** Buffers and textures created. */
19
+ created: number;
20
+ /** Structure chunks rebuilt (0055). */
21
+ chunksRebuilt: number;
22
+ /** Meshes uploaded to the GPU: new ones, and ones whose data changed. */
23
+ meshesRebuilt: number;
24
+ /** Shadow maps (cascades and local shadow views) drawn. */
25
+ shadowMapsRendered: number;
26
+ }
27
+ /** Counts a system adds to during a frame; they land in `lastFrame` when the frame is submitted. */
28
+ export interface FrameCounts {
29
+ chunksRebuilt: number;
30
+ meshesRebuilt: number;
31
+ shadowMapsRendered: number;
32
+ }
33
+ /** Frames `RenderStatsMap.recent` sums over. */
34
+ export declare const RECENT_FRAMES = 60;
35
+ /**
36
+ * Per-view counts from the last frame (a Map by view name), plus what the last frame wrote to the
37
+ * GPU and rebuilt (`lastFrame`) and the sum over the last 60 rendered frames (`recent`).
38
+ */
39
+ export declare class RenderStatsMap extends Map<string, ViewStats> {
40
+ readonly lastFrame: FrameRecord;
41
+ readonly recent: FrameRecord;
42
+ /** Counted during the frame being built. */
43
+ readonly current: FrameCounts;
44
+ /** Frames recorded. */
45
+ frames: number;
46
+ private readonly ring;
47
+ private readonly row;
48
+ private readonly seen;
49
+ /**
50
+ * Closes a frame: `uploads` is the owner's running total (`gpu.uploads(owner)`); the difference
51
+ * since the last call is this frame's. Allocates nothing.
52
+ */
53
+ endFrame(uploads: GpuUploads | undefined): void;
54
+ }
55
+ export declare const RenderStats: import("@aethervtt/shard-core").ResourceDef<RenderStatsMap>;
12
56
  export interface GpuMemoryData {
13
57
  /** Textures uploaded by the forward renderer. */
14
58
  textures: number;
package/dist/stats.js CHANGED
@@ -1,7 +1,93 @@
1
1
  import { defineResource } from "@aethervtt/shard-core";
2
+ import {
3
+ UPLOAD_CATEGORIES,
4
+ Upload
5
+ } from "@aethervtt/shard-gpu";
6
+ const RECENT_FRAMES = 60;
7
+ const FIELDS = UPLOAD_CATEGORIES.length + 4;
8
+ function frameRecord() {
9
+ const bytes = {};
10
+ for (const c of UPLOAD_CATEGORIES) bytes[c] = 0;
11
+ return {
12
+ bytes,
13
+ sceneBytes: 0,
14
+ created: 0,
15
+ chunksRebuilt: 0,
16
+ meshesRebuilt: 0,
17
+ shadowMapsRendered: 0
18
+ };
19
+ }
20
+ class RenderStatsMap extends Map {
21
+ lastFrame = frameRecord();
22
+ recent = frameRecord();
23
+ /** Counted during the frame being built. */
24
+ current = { chunksRebuilt: 0, meshesRebuilt: 0, shadowMapsRendered: 0 };
25
+ /** Frames recorded. */
26
+ frames = 0;
27
+ ring = new Float64Array(RECENT_FRAMES * FIELDS);
28
+ row = new Float64Array(FIELDS);
29
+ seen = new Float64Array(UPLOAD_CATEGORIES.length + 1);
30
+ /**
31
+ * Closes a frame: `uploads` is the owner's running total (`gpu.uploads(owner)`); the difference
32
+ * since the last call is this frame's. Allocates nothing.
33
+ */
34
+ endFrame(uploads) {
35
+ const row = this.row;
36
+ const seen = this.seen;
37
+ const n = UPLOAD_CATEGORIES.length;
38
+ for (let i = 0; i < n; i++) {
39
+ const total = uploads ? uploads.bytes[i] : seen[i];
40
+ row[i] = total - seen[i];
41
+ seen[i] = total;
42
+ }
43
+ const created = uploads ? uploads.created : seen[n];
44
+ row[n] = created - seen[n];
45
+ seen[n] = created;
46
+ const cur = this.current;
47
+ row[n + 1] = cur.chunksRebuilt;
48
+ row[n + 2] = cur.meshesRebuilt;
49
+ row[n + 3] = cur.shadowMapsRendered;
50
+ cur.chunksRebuilt = cur.meshesRebuilt = cur.shadowMapsRendered = 0;
51
+ const ring = this.ring;
52
+ const slot = this.frames % RECENT_FRAMES * FIELDS;
53
+ this.frames++;
54
+ const last = this.lastFrame;
55
+ const recent = this.recent;
56
+ let lastScene = 0;
57
+ let recentScene = 0;
58
+ for (let i = 0; i < FIELDS; i++) {
59
+ const sum = recentField(recent, i) - ring[slot + i] + row[i];
60
+ ring[slot + i] = row[i];
61
+ setField(last, i, row[i]);
62
+ setField(recent, i, sum);
63
+ if (i < n && i !== Upload.view) {
64
+ lastScene += row[i];
65
+ recentScene += sum;
66
+ }
67
+ }
68
+ last.sceneBytes = lastScene;
69
+ recent.sceneBytes = recentScene;
70
+ }
71
+ }
72
+ function recentField(r, i) {
73
+ const n = UPLOAD_CATEGORIES.length;
74
+ if (i < n) return r.bytes[UPLOAD_CATEGORIES[i]];
75
+ if (i === n) return r.created;
76
+ if (i === n + 1) return r.chunksRebuilt;
77
+ if (i === n + 2) return r.meshesRebuilt;
78
+ return r.shadowMapsRendered;
79
+ }
80
+ function setField(r, i, v) {
81
+ const n = UPLOAD_CATEGORIES.length;
82
+ if (i < n) r.bytes[UPLOAD_CATEGORIES[i]] = v;
83
+ else if (i === n) r.created = v;
84
+ else if (i === n + 1) r.chunksRebuilt = v;
85
+ else if (i === n + 2) r.meshesRebuilt = v;
86
+ else r.shadowMapsRendered = v;
87
+ }
2
88
  const RenderStats = defineResource("render/Stats", {
3
- description: "Per-view counts from the last frame: visible, culled, hidden, pending, draw calls.",
4
- init: () => /* @__PURE__ */ new Map()
89
+ description: "Per-view counts from the last frame (visible, culled, hidden, pending, draw calls), and what the last frame and the last 60 wrote to the GPU and rebuilt.",
90
+ init: () => new RenderStatsMap()
5
91
  });
6
92
  const GpuMemory = defineResource("render/GpuMemory", {
7
93
  description: "GPU memory used by uploaded textures.",
@@ -13,6 +99,8 @@ const RenderCounters = defineResource("render/Counters", {
13
99
  });
14
100
  export {
15
101
  GpuMemory,
102
+ RECENT_FRAMES,
16
103
  RenderCounters,
17
- RenderStats
104
+ RenderStats,
105
+ RenderStatsMap
18
106
  };
@@ -0,0 +1,57 @@
1
+ import { type InferFields } from '@aethervtt/shard-core';
2
+ export declare const VARIATION_PATTERNS: readonly ['mottle', 'streaks', 'grain', 'cells', 'stagger', 'brushed'];
3
+ export type VariationPattern = (typeof VARIATION_PATTERNS)[number];
4
+ /** The variation's fields: one schema for validation, the inspector, and the GPU struct. */
5
+ export declare const VARIATION_FIELDS: {
6
+ strength: import("@aethervtt/shard-core").FieldType<number, "f32">;
7
+ seed: import("@aethervtt/shard-core").FieldType<number, "u32">;
8
+ pattern: import("@aethervtt/shard-core").FieldType<"brushed" | "cells" | "grain" | "mottle" | "stagger" | "streaks", "u8">;
9
+ scale: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").Vec2, "f32">;
10
+ warp: import("@aethervtt/shard-core").FieldType<number, "f32">;
11
+ detail: import("@aethervtt/shard-core").FieldType<number, "f32">;
12
+ bands: import("@aethervtt/shard-core").FieldType<number, "f32">;
13
+ toneRange: import("@aethervtt/shard-core").FieldType<number, "f32">;
14
+ coolTint: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").Vec3, "f32">;
15
+ warmTint: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").Vec3, "f32">;
16
+ roughnessRange: import("@aethervtt/shard-core").FieldType<number, "f32">;
17
+ weathering: import("@aethervtt/shard-core").FieldType<number, "f32">;
18
+ weatherThreshold: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").Vec2, "f32">;
19
+ };
20
+ export type Variation = InferFields<typeof VARIATION_FIELDS>;
21
+ /** A partial variation: what presets and materials write (the rest takes the defaults). */
22
+ export type VariationInit = Partial<Variation>;
23
+ /** The GPU struct `surface::variation::VariationUniform`, generated from VARIATION_FIELDS. */
24
+ export declare const VariationUniform: import("@aethervtt/shard-core").ComponentDef<{
25
+ strength: import("@aethervtt/shard-core").FieldType<number, "f32">;
26
+ seed: import("@aethervtt/shard-core").FieldType<number, "u32">;
27
+ pattern: import("@aethervtt/shard-core").FieldType<"brushed" | "cells" | "grain" | "mottle" | "stagger" | "streaks", "u8">;
28
+ scale: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").Vec2, "f32">;
29
+ warp: import("@aethervtt/shard-core").FieldType<number, "f32">;
30
+ detail: import("@aethervtt/shard-core").FieldType<number, "f32">;
31
+ bands: import("@aethervtt/shard-core").FieldType<number, "f32">;
32
+ toneRange: import("@aethervtt/shard-core").FieldType<number, "f32">;
33
+ coolTint: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").Vec3, "f32">;
34
+ warmTint: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").Vec3, "f32">;
35
+ roughnessRange: import("@aethervtt/shard-core").FieldType<number, "f32">;
36
+ weathering: import("@aethervtt/shard-core").FieldType<number, "f32">;
37
+ weatherThreshold: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").Vec2, "f32">;
38
+ }>;
39
+ /** Where the WGSL struct lives: a material's generated module imports it from here. */
40
+ export declare const VARIATION_STRUCT = "surface::variation::VariationUniform";
41
+ /**
42
+ * Tuned variations, seeded from Aether's eight recipes (its patch sizes converted from pixels at
43
+ * 1.5 m per 70 px, its global intensity of 0.5 as `strength`). Plain data: spread one and
44
+ * override, or add your own. Nothing in the shader knows their names.
45
+ */
46
+ export declare const SURFACE_PRESETS: Record<string, Variation>;
47
+ export interface SurfaceSample {
48
+ /** RGB multiplier for the base colour. */
49
+ tint: [number, number, number];
50
+ /** Added to roughness. */
51
+ roughness: number;
52
+ }
53
+ /**
54
+ * The variation at surface point `p` (metres): a tint multiplier and a roughness offset. The CPU
55
+ * mirror of WGSL `surface_variation`, for tests and for tools that preview a preset.
56
+ */
57
+ export declare function surfaceVariation(v: Variation, p: ArrayLike<number>, out?: SurfaceSample): SurfaceSample;