@woosh/meep-engine 3.18.0 → 3.20.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 (228) hide show
  1. package/build/bundle-worker-image-decoder.js +1 -1
  2. package/build/bundle-worker-terrain.js +1 -1
  3. package/editor/view/particles/effect/ParticleCurveEditorView.d.ts.map +1 -1
  4. package/editor/view/particles/effect/ParticleCurveEditorView.js +303 -120
  5. package/editor/view/particles/effect/ParticleGradientEditorView.d.ts.map +1 -1
  6. package/editor/view/particles/effect/ParticleGradientEditorView.js +108 -63
  7. package/editor/view/particles/effect/ParticleGraphEditorView.js +2 -2
  8. package/editor/view/particles/effect/ParticleNodeParametersView.d.ts.map +1 -1
  9. package/editor/view/particles/effect/ParticleNodeParametersView.js +7 -46
  10. package/editor/view/particles/effect/particle-editor.css +60 -30
  11. package/package.json +1 -2
  12. package/samples/engine/README.md +1 -1
  13. package/src/core/binary/compression/decompress_bytes.d.ts +13 -0
  14. package/src/core/binary/compression/decompress_bytes.d.ts.map +1 -0
  15. package/src/core/binary/compression/decompress_bytes.js +28 -0
  16. package/src/core/collection/array/array_group_by.d.ts.map +1 -1
  17. package/src/core/collection/array/array_group_by.js +2 -11
  18. package/src/core/geom/3d/shape/HeightMapShape3D.d.ts.map +1 -1
  19. package/src/core/geom/3d/shape/HeightMapShape3D.js +4 -9
  20. package/src/core/model/node-graph/visual/layout/layout_assign_coordinates.js +67 -22
  21. package/src/core/model/object/optional_clone.d.ts +7 -0
  22. package/src/core/model/object/optional_clone.d.ts.map +1 -0
  23. package/src/core/model/object/optional_equality.d.ts +10 -0
  24. package/src/core/model/object/optional_equality.d.ts.map +1 -0
  25. package/src/{shade/descriptor/util → core/model/object}/optional_equality.js +2 -2
  26. package/src/core/model/object/optional_hash.d.ts +9 -0
  27. package/src/core/model/object/optional_hash.d.ts.map +1 -0
  28. package/src/{shade/descriptor/util → core/model/object}/optional_hash.js +3 -3
  29. package/src/engine/asset/Asset.d.ts +1 -1
  30. package/src/engine/asset/Asset.d.ts.map +1 -1
  31. package/src/engine/asset/Asset.js +3 -1
  32. package/src/engine/asset/loaders/image/ImageDecoderWorker.js +12 -27
  33. package/src/engine/asset/loaders/image/prototypePNG.js +8 -7
  34. package/src/engine/ecs/gui/hud/HeadsUpDisplaySystem.d.ts.map +1 -1
  35. package/src/engine/ecs/gui/hud/HeadsUpDisplaySystem.js +9 -10
  36. package/src/engine/ecs/storage/populateEngineSerializationRegistry.d.ts.map +1 -1
  37. package/src/engine/ecs/storage/populateEngineSerializationRegistry.js +4 -0
  38. package/src/engine/graphics/ecs/particles/ParticleEffect.d.ts +266 -0
  39. package/src/engine/graphics/ecs/particles/ParticleEffect.d.ts.map +1 -0
  40. package/src/engine/graphics/ecs/particles/ParticleEffect.js +439 -0
  41. package/src/engine/graphics/ecs/particles/ParticleEffectSerializationAdapter.d.ts +58 -0
  42. package/src/engine/graphics/ecs/particles/ParticleEffectSerializationAdapter.d.ts.map +1 -0
  43. package/src/engine/graphics/ecs/particles/ParticleEffectSerializationAdapter.js +219 -0
  44. package/src/engine/graphics3/GPUParticleEmitterSystem.d.ts +178 -25
  45. package/src/engine/graphics3/GPUParticleEmitterSystem.d.ts.map +1 -1
  46. package/src/engine/graphics3/GPUParticleEmitterSystem.js +809 -310
  47. package/src/format/image/png/PNGReader.d.ts +7 -6
  48. package/src/format/image/png/PNGReader.d.ts.map +1 -1
  49. package/src/format/image/png/PNGReader.js +13 -12
  50. package/src/format/image/png/chunk/png_chunk_decode_iTXt.d.ts +3 -2
  51. package/src/format/image/png/chunk/png_chunk_decode_iTXt.d.ts.map +1 -1
  52. package/src/format/image/png/chunk/png_chunk_decode_iTXt.js +5 -4
  53. package/src/format/image/png/chunk/png_chunk_decode_zTXt.d.ts +3 -2
  54. package/src/format/image/png/chunk/png_chunk_decode_zTXt.d.ts.map +1 -1
  55. package/src/format/image/png/chunk/png_chunk_decode_zTXt.js +5 -4
  56. package/src/format/image/png/png_inflate.d.ts +3 -3
  57. package/src/format/image/png/png_inflate.d.ts.map +1 -1
  58. package/src/format/image/png/png_inflate.js +29 -39
  59. package/src/format/texture/ktx2/ktx2_read.d.ts +4 -4
  60. package/src/format/texture/ktx2/ktx2_read.d.ts.map +1 -1
  61. package/src/format/texture/ktx2/ktx2_read.js +18 -21
  62. package/src/shade/descriptor/binding/BindGroupLayoutDescriptor.js +1 -1
  63. package/src/shade/descriptor/binding/BindGroupLayoutEntry.js +3 -3
  64. package/src/shade/descriptor/pipeline/PipelineDescriptorBase.js +1 -1
  65. package/src/shade/descriptor/pipeline/PipelineLayoutDescriptor.js +1 -1
  66. package/src/shade/descriptor/pipeline/render/ColorTargetState.js +3 -3
  67. package/src/shade/descriptor/pipeline/render/FragmentState.js +1 -1
  68. package/src/shade/descriptor/pipeline/render/RenderPipelineDescriptor.js +3 -3
  69. package/src/shade/playground/particle_ecs/README.md +203 -0
  70. package/src/shade/playground/particle_ecs/bonfire_editor.d.ts +39 -0
  71. package/src/shade/playground/particle_ecs/bonfire_editor.d.ts.map +1 -0
  72. package/src/shade/playground/particle_ecs/bonfire_editor.js +315 -0
  73. package/src/shade/playground/particle_ecs/bonfire_effects.d.ts +145 -0
  74. package/src/shade/playground/particle_ecs/bonfire_effects.d.ts.map +1 -0
  75. package/src/shade/playground/particle_ecs/bonfire_effects.js +202 -0
  76. package/src/shade/playground/particle_ecs/bonfire_sprites.d.ts +23 -0
  77. package/src/shade/playground/particle_ecs/bonfire_sprites.d.ts.map +1 -0
  78. package/src/shade/playground/particle_ecs/bonfire_sprites.js +315 -0
  79. package/src/shade/playground/particle_ecs/bonfire_world.d.ts +86 -0
  80. package/src/shade/playground/particle_ecs/bonfire_world.d.ts.map +1 -0
  81. package/src/shade/playground/particle_ecs/bonfire_world.js +303 -0
  82. package/src/shade/playground/particle_ecs/effects/embers.json +1634 -0
  83. package/src/shade/playground/particle_ecs/effects/flame.json +1882 -0
  84. package/src/shade/playground/particle_ecs/effects/smoke.json +1860 -0
  85. package/src/shade/playground/particle_ecs/effects/soot.json +1606 -0
  86. package/src/shade/playground/particle_ecs/index.html +330 -0
  87. package/src/shade/playground/particle_ecs/main.d.ts +2 -0
  88. package/src/shade/playground/particle_ecs/main.d.ts.map +1 -0
  89. package/src/shade/playground/particle_ecs/main.js +566 -0
  90. package/src/shade/playground/particle_ecs/moonlit_environment.d.ts +16 -0
  91. package/src/shade/playground/particle_ecs/moonlit_environment.d.ts.map +1 -0
  92. package/src/shade/playground/particle_ecs/moonlit_environment.js +144 -0
  93. package/src/shade/playground/particle_editor/README.md +5 -4
  94. package/src/shade/playground/particle_system/particle_prototype.d.ts +7 -9
  95. package/src/shade/playground/particle_system/particle_prototype.d.ts.map +1 -1
  96. package/src/shade/playground/particle_system/particle_prototype.js +7 -9
  97. package/src/shade/playground/profile_hotkey.d.ts +58 -0
  98. package/src/shade/playground/profile_hotkey.d.ts.map +1 -0
  99. package/src/shade/playground/profile_hotkey.js +325 -0
  100. package/src/shade/playground/ssr_motion/README.md +22 -0
  101. package/src/shade/playground/ssr_motion/index.html +18 -0
  102. package/src/shade/playground/ssr_motion/main.d.ts +2 -0
  103. package/src/shade/playground/ssr_motion/main.d.ts.map +1 -0
  104. package/src/shade/playground/ssr_motion/main.js +149 -0
  105. package/src/shade/playground/ssr_variance/README.md +105 -0
  106. package/src/shade/playground/ssr_variance/capture.d.ts +16 -0
  107. package/src/shade/playground/ssr_variance/capture.d.ts.map +1 -0
  108. package/src/shade/playground/ssr_variance/capture.js +96 -0
  109. package/src/shade/playground/ssr_variance/index.html +23 -0
  110. package/src/shade/playground/ssr_variance/main.d.ts +2 -0
  111. package/src/shade/playground/ssr_variance/main.d.ts.map +1 -0
  112. package/src/shade/playground/ssr_variance/main.js +246 -0
  113. package/src/shade/playground/ssr_variance/reference.d.ts +18 -0
  114. package/src/shade/playground/ssr_variance/reference.d.ts.map +1 -0
  115. package/src/shade/playground/ssr_variance/reference.js +78 -0
  116. package/src/shade/playground/ssr_variance/scene.d.ts +11 -0
  117. package/src/shade/playground/ssr_variance/scene.d.ts.map +1 -0
  118. package/src/shade/playground/ssr_variance/scene.js +61 -0
  119. package/src/shade/playground/ssr_variance/statistics.d.ts +44 -0
  120. package/src/shade/playground/ssr_variance/statistics.d.ts.map +1 -0
  121. package/src/shade/playground/ssr_variance/statistics.js +51 -0
  122. package/src/shade/renderer/geometry/Geometry.d.ts.map +1 -1
  123. package/src/shade/renderer/geometry/Geometry.js +4 -7
  124. package/src/shade/renderer/loader/gltf/tiny-gltf.d.ts.map +1 -1
  125. package/src/shade/renderer/loader/gltf/tiny-gltf.js +9 -3
  126. package/src/shade/renderer/loader/usd/usd_decode_image.d.ts +4 -4
  127. package/src/shade/renderer/loader/usd/usd_decode_image.d.ts.map +1 -1
  128. package/src/shade/renderer/loader/usd/usd_decode_image.js +4 -4
  129. package/src/shade/renderer/material/StandardShadeMaterial.js +2 -2
  130. package/src/shade/renderer/particles/DESIGN.md +745 -702
  131. package/src/shade/renderer/particles/GPUParticleSystem.d.ts.map +1 -1
  132. package/src/shade/renderer/particles/GPUParticleSystem.js +3 -1
  133. package/src/shade/renderer/particles/graph/ParticleGroupBuilder.d.ts +1 -1
  134. package/src/shade/renderer/particles/graph/ParticleGroupBuilder.js +1 -1
  135. package/src/shade/renderer/particles/graph/ParticleNodeRegistry.d.ts.map +1 -1
  136. package/src/shade/renderer/particles/graph/ParticleNodeRegistry.js +9 -15
  137. package/src/shade/renderer/particles/graph/particle_group_library.d.ts.map +1 -1
  138. package/src/shade/renderer/particles/graph/particle_group_library.js +8 -3
  139. package/src/shade/renderer/particles/graph/particle_node_parameters.d.ts +0 -2
  140. package/src/shade/renderer/particles/graph/particle_node_parameters.d.ts.map +1 -1
  141. package/src/shade/renderer/particles/graph/particle_node_parameters.js +0 -9
  142. package/src/shade/renderer/particles/graph/particle_node_presentation.d.ts.map +1 -1
  143. package/src/shade/renderer/particles/graph/particle_node_presentation.js +0 -10
  144. package/src/shade/renderer/particles/graph/particle_ramp.d.ts +2 -8
  145. package/src/shade/renderer/particles/graph/particle_ramp.d.ts.map +1 -1
  146. package/src/shade/renderer/particles/graph/particle_ramp.js +2 -8
  147. package/src/shade/renderer/particles/graph/validate_particle_graph.js +0 -6
  148. package/src/shade/renderer/particles/graph_particles_avboit.d.ts.map +1 -1
  149. package/src/shade/renderer/particles/graph_particles_avboit.js +13 -4
  150. package/src/shade/renderer/particles/isa/InstructionStream.d.ts +1 -12
  151. package/src/shade/renderer/particles/isa/InstructionStream.d.ts.map +1 -1
  152. package/src/shade/renderer/particles/isa/InstructionStream.js +1 -12
  153. package/src/shade/renderer/particles/isa/ParticleVMISA.d.ts +54 -60
  154. package/src/shade/renderer/particles/isa/ParticleVMISA.d.ts.map +1 -1
  155. package/src/shade/renderer/particles/isa/ParticleVMISA.js +9 -17
  156. package/src/shade/renderer/particles/isa/particle_assembly.d.ts +0 -1
  157. package/src/shade/renderer/particles/isa/particle_assembly.d.ts.map +1 -1
  158. package/src/shade/renderer/particles/isa/particle_assembly.js +2 -16
  159. package/src/shade/renderer/particles/optimizer/dag/particle_vector_graph.d.ts +1 -1
  160. package/src/shade/renderer/particles/optimizer/dag/particle_vector_graph.js +1 -1
  161. package/src/shade/renderer/particles/optimizer/dag/particle_vector_lift.js +0 -14
  162. package/src/shade/renderer/particles/optimizer/dag/particle_vector_pack.js +3 -17
  163. package/src/shade/renderer/particles/optimizer/particle_program_analysis.d.ts.map +1 -1
  164. package/src/shade/renderer/particles/optimizer/particle_program_analysis.js +15 -36
  165. package/src/shade/renderer/particles/optimizer/particle_program_equivalence.d.ts.map +1 -1
  166. package/src/shade/renderer/particles/optimizer/particle_program_equivalence.js +1 -5
  167. package/src/shade/renderer/particles/optimizer/particle_program_fuzz.d.ts.map +1 -1
  168. package/src/shade/renderer/particles/optimizer/particle_program_fuzz.js +1 -8
  169. package/src/shade/renderer/particles/optimizer/particle_vm_semantics.d.ts +2 -11
  170. package/src/shade/renderer/particles/optimizer/particle_vm_semantics.d.ts.map +1 -1
  171. package/src/shade/renderer/particles/optimizer/particle_vm_semantics.js +2 -11
  172. package/src/shade/renderer/particles/runtime/ParticleEmitter.d.ts +11 -4
  173. package/src/shade/renderer/particles/runtime/ParticleEmitter.d.ts.map +1 -1
  174. package/src/shade/renderer/particles/runtime/ParticleEmitter.js +431 -424
  175. package/src/shade/renderer/particles/shaders/chunk_particle_avboit_contribution.d.ts.map +1 -1
  176. package/src/shade/renderer/particles/shaders/chunk_particle_avboit_contribution.js +13 -0
  177. package/src/shade/renderer/particles/shaders/shader_particle_avboit_draw.d.ts +3 -6
  178. package/src/shade/renderer/particles/shaders/shader_particle_avboit_draw.d.ts.map +1 -1
  179. package/src/shade/renderer/particles/shaders/shader_particle_avboit_draw.js +49 -17
  180. package/src/shade/renderer/particles/shaders/shader_particle_emit.d.ts.map +1 -1
  181. package/src/shade/renderer/particles/shaders/shader_particle_emit.js +0 -2
  182. package/src/shade/renderer/particles/shaders/shader_particle_emit_wide.d.ts.map +1 -1
  183. package/src/shade/renderer/particles/shaders/shader_particle_emit_wide.js +0 -2
  184. package/src/shade/renderer/particles/shaders/shader_particle_simulate.d.ts.map +1 -1
  185. package/src/shade/renderer/particles/shaders/shader_particle_simulate.js +1 -5
  186. package/src/shade/renderer/particles/shaders/shader_particle_simulate_wide.d.ts.map +1 -1
  187. package/src/shade/renderer/particles/shaders/shader_particle_simulate_wide.js +0 -2
  188. package/src/shade/renderer/particles/vm/ParticleVMReference.d.ts +1 -3
  189. package/src/shade/renderer/particles/vm/ParticleVMReference.d.ts.map +1 -1
  190. package/src/shade/renderer/particles/vm/ParticleVMReference.js +4 -20
  191. package/src/shade/renderer/particles/vm/chunk_particle_vm.d.ts.map +1 -1
  192. package/src/shade/renderer/particles/vm/chunk_particle_vm.js +2 -5
  193. package/src/shade/renderer/particles/warmup/shader_particle_warmup_advance.d.ts.map +1 -1
  194. package/src/shade/renderer/particles/warmup/shader_particle_warmup_advance.js +0 -2
  195. package/src/shade/renderer/particles/warmup/shader_particle_warmup_advance_wide.d.ts.map +1 -1
  196. package/src/shade/renderer/particles/warmup/shader_particle_warmup_advance_wide.js +0 -2
  197. package/src/shade/renderer/postprocess/ssr/SSR.d.ts.map +1 -1
  198. package/src/shade/renderer/postprocess/ssr/SSR.js +2 -1
  199. package/src/shade/renderer/postprocess/ssr/reproject/chunk_ssr_reprojection.d.ts.map +1 -1
  200. package/src/shade/renderer/postprocess/ssr/reproject/chunk_ssr_reprojection.js +20 -5
  201. package/src/shade/renderer/postprocess/ssr/reproject/chunk_ssr_sample_history.d.ts.map +1 -1
  202. package/src/shade/renderer/postprocess/ssr/reproject/chunk_ssr_sample_history.js +19 -5
  203. package/src/shade/renderer/postprocess/ssr/reproject/chunk_ssr_temporal_accumulate.d.ts +4 -0
  204. package/src/shade/renderer/postprocess/ssr/reproject/chunk_ssr_temporal_accumulate.d.ts.map +1 -0
  205. package/src/shade/renderer/postprocess/ssr/reproject/chunk_ssr_temporal_accumulate.js +137 -0
  206. package/src/shade/renderer/postprocess/ssr/ssr_reproject_shader.d.ts +0 -8
  207. package/src/shade/renderer/postprocess/ssr/ssr_reproject_shader.d.ts.map +1 -1
  208. package/src/shade/renderer/postprocess/ssr/ssr_reproject_shader.js +18 -114
  209. package/src/shade/renderer/scene/Mesh.js +1 -1
  210. package/src/shade/renderer/texture/ShadeTexture.js +1 -1
  211. package/src/shade/renderer/texture/source/texel_data_from_ktx2.d.ts +2 -2
  212. package/src/shade/renderer/texture/source/texel_data_from_ktx2.d.ts.map +1 -1
  213. package/src/shade/renderer/texture/source/texel_data_from_ktx2.js +3 -3
  214. package/src/shade/renderer/particles/shaders/chunk_particle_curve_animation.d.ts +0 -19
  215. package/src/shade/renderer/particles/shaders/chunk_particle_curve_animation.d.ts.map +0 -1
  216. package/src/shade/renderer/particles/shaders/chunk_particle_curve_animation.js +0 -30
  217. package/src/shade/renderer/particles/shaders/chunk_particle_curve_disabled.d.ts +0 -11
  218. package/src/shade/renderer/particles/shaders/chunk_particle_curve_disabled.d.ts.map +0 -1
  219. package/src/shade/renderer/particles/shaders/chunk_particle_curve_disabled.js +0 -13
  220. package/src/shade/renderer/particles/shaders/chunk_particle_emitter_warmup.d.ts +0 -20
  221. package/src/shade/renderer/particles/shaders/chunk_particle_emitter_warmup.d.ts.map +0 -1
  222. package/src/shade/renderer/particles/shaders/chunk_particle_emitter_world_sphere.d.ts +0 -14
  223. package/src/shade/renderer/particles/shaders/chunk_particle_emitter_world_sphere.d.ts.map +0 -1
  224. package/src/shade/renderer/particles/shaders/shader_particle_reclaim.d.ts +0 -50
  225. package/src/shade/renderer/particles/shaders/shader_particle_reclaim.d.ts.map +0 -1
  226. package/src/shade/renderer/postprocess/ssr/reproject/shader_ffx_denoiser_reflections_reproject.d.ts +0 -17
  227. package/src/shade/renderer/postprocess/ssr/reproject/shader_ffx_denoiser_reflections_reproject.d.ts.map +0 -1
  228. /package/src/{shade/descriptor/util → core/model/object}/optional_clone.js +0 -0
@@ -0,0 +1,246 @@
1
+ import Vector3 from "../../../core/geom/Vector3.js";
2
+ import { PerspectiveCamera } from "../../renderer/camera/PerspectiveCamera.js";
3
+ import { Renderer } from "../../renderer/Renderer.js";
4
+ import { ShadeIndirectLightingMode } from "../../renderer/ShadeIndirectLightingMode.js";
5
+ import { CaptureSSR, FixedProjection } from "./capture.js";
6
+ import { create_scene } from "./scene.js";
7
+ import { measure_variance } from "./statistics.js";
8
+ import { ReflectionReference, reflection_error } from "./reference.js";
9
+
10
+ const element = id => document.getElementById(id);
11
+ const params = new URLSearchParams(location.search);
12
+ const report = text => { element('status').textContent = text; };
13
+ function png(canvas) {
14
+ const url = canvas.toDataURL('image/png');
15
+ return { url, bytes: Uint8Array.from(atob(url.split(',')[1]), c => c.charCodeAt(0)) };
16
+ }
17
+ function json_download(id, name, value, files) {
18
+ const json = JSON.stringify(value, null, 2);
19
+ files.set(name, new TextEncoder().encode(json));
20
+ element(id).href = `data:application/json;charset=utf-8,${encodeURIComponent(json)}`;
21
+ element(id).download = name;
22
+ }
23
+ function restart(mode) {
24
+ const query = new URLSearchParams(Object.fromEntries(['spatial', 'temporal', 'jitter', 'taa'].map(name => [name, element(name).value])));
25
+ if (mode) { query.set(mode, ''); }
26
+ location.search = query.toString();
27
+ }
28
+ element('restart').onclick = () => restart();
29
+ for (const name of ['spatial', 'temporal', 'jitter', 'taa']) {
30
+ element(name).value = name === 'jitter' || name === 'taa' ? (params.get(name) === 'on' ? 'on' : 'off') : (params.get(name) === 'off' ? 'off' : 'on');
31
+ element(name).onchange = () => restart();
32
+ }
33
+
34
+ async function start() {
35
+ const canvas = element('view');
36
+ const renderer = new Renderer();
37
+ await renderer.initialize({ context: canvas.getContext('webgpu'), pixelRatio: 1 });
38
+ renderer.feature_ssr_enabled = true;
39
+ renderer.indirect_lighting_mode = ShadeIndirectLightingMode.IBL;
40
+ for (const name of ['taa', 'ssao', 'bloom', 'sharpening', 'motion_blur', 'automatic_exposure']) {
41
+ renderer[`feature_${name}_enabled`] = false;
42
+ }
43
+ renderer.exposure_compensation = 1;
44
+ renderer.feature_taa_enabled = element('taa').value === 'on';
45
+ renderer.internal_resolution_scale = 1;
46
+ renderer.ssr.spatial_denoising_enabled = element('spatial').value === 'on';
47
+ renderer.ssr.reprojection_mode = element('temporal').value === 'off' ? 2 : 0;
48
+ const world = await create_scene();
49
+ const camera = new PerspectiveCamera();
50
+ camera.near = 0.1; camera.far = 50; camera.fov = 50 * Math.PI / 180;
51
+ camera.aspect = canvas.width / canvas.height;
52
+ camera.transform.setTranslation(0, 3.2, 7);
53
+ camera.transform.lookAt(new Vector3(0, 0.2, -2));
54
+ camera.transform.updateMatrix();
55
+ renderer.resize(canvas.width, canvas.height);
56
+ const device = renderer.graphics.device;
57
+ let failure, busy = false, frame_count = 0;
58
+ const fail = error => {
59
+ failure = error; report(error.message ?? String(error));
60
+ element('status').classList.add('error'); console.error(error);
61
+ element('step').disabled = element('run').disabled = element('converge').disabled = true;
62
+ };
63
+ device.addEventListener('uncapturederror', event => fail(event.error));
64
+ device.lost.then(info => fail(new Error(`GPU device lost: ${info.message}`)));
65
+ const capture = new CaptureSSR(device);
66
+ const reference = new ReflectionReference(device);
67
+ renderer.add_extension(reference);
68
+ if (element('jitter').value === 'off') { renderer.add_extension(new FixedProjection()); }
69
+ renderer.add_extension(capture);
70
+ const frames = [], files = new Map();
71
+ // A run owns hundreds of MB of HDR captures. Release them when navigating
72
+ // between controls, including when the browser keeps a page in its back cache.
73
+ addEventListener('pagehide', () => {
74
+ frames.length = 0; files.clear();
75
+ renderer.destroy(); device.destroy();
76
+ });
77
+ addEventListener('pageshow', event => { if (event.persisted) { location.reload(); } });
78
+ async function render_frame(record = false) {
79
+ if (failure) { throw failure; }
80
+ capture.enabled = record;
81
+ const rendered = renderer.render(camera, world.scene, 1 / 60);
82
+ if (rendered === false) { throw new Error('Renderer did not submit the requested frame'); }
83
+ frame_count++;
84
+ if (renderer.frame_count !== frame_count) { throw new Error('Renderer frame count diverged from the experiment'); }
85
+ // Snapshot before yielding: the browser may discard the presented canvas after compositing.
86
+ let display;
87
+ if (record) {
88
+ display = document.createElement('canvas'); display.width = canvas.width; display.height = canvas.height;
89
+ display.getContext('2d').drawImage(canvas, 0, 0);
90
+ }
91
+ await device.queue.onSubmittedWorkDone();
92
+ if (failure) { throw failure; }
93
+ if (record) {
94
+ const stages = await capture.read();
95
+ const output = png(display);
96
+ const frame = { frame: frame_count, ssr_frame_index: renderer.ssr.frame_index, jitter: capture.jitter, stages, png: output.bytes, png_url: output.url };
97
+ frames.push(frame);
98
+ files.set(`frame-${frame_count}.png`, output.bytes);
99
+ for (const [name, data] of Object.entries(stages)) {
100
+ // Keep one CPU copy. Hundreds of HDR blobs can exhaust the browser's
101
+ // disk-backed Blob quota before a batch exporter gets to read them.
102
+ files.set(`frame-${frame_count}-${name}.f32`, data);
103
+ }
104
+ }
105
+ capture.enabled = false;
106
+ report(`Frame ${frame_count} · ${frames.length} captures · idle between steps`);
107
+ return frame_count;
108
+ }
109
+ async function exclusive(action) {
110
+ if (busy) { throw new Error('A render operation is already running'); }
111
+ busy = true;
112
+ for (const id of ['step', 'run', 'spatial', 'temporal', 'jitter', 'taa', 'restart', 'converge']) { element(id).disabled = true; }
113
+ try { return await action(); }
114
+ catch (error) { fail(error); throw error; }
115
+ finally {
116
+ busy = false;
117
+ for (const id of ['step', 'run', 'spatial', 'temporal', 'jitter', 'taa', 'restart', 'converge']) { element(id).disabled = false; }
118
+ if (failure) { element('step').disabled = element('run').disabled = element('converge').disabled = true; }
119
+ }
120
+ }
121
+ const step = (n = 1, record = false) => exclusive(async () => {
122
+ if (!Number.isSafeInteger(n) || n < 1) { throw new Error('Step count must be a positive integer'); }
123
+ for (let i = 0; i < n; i++) { await render_frame(record); }
124
+ return frame_count;
125
+ });
126
+ async function run() {
127
+ if (frame_count !== 0) { throw new Error('Restart at frame zero before a measurement run'); }
128
+ return exclusive(async () => {
129
+ // No initial render, animation loop, or readback-only render is hidden in these counts.
130
+ for (let i = 1; i <= 115; i++) { await render_frame(i >= 100); }
131
+ const summary = {
132
+ width: canvas.width, height: canvas.height, frames: frames.map(f => f.frame),
133
+ ssr_frame_indices: frames.map(f => f.ssr_frame_index), dt: 1 / 60,
134
+ raster_jitter: frames.map(f => f.jitter),
135
+ automatic_exposure: false, exposure_compensation: 1,
136
+ roughness: 0.3, metallic: 1, spatial: renderer.ssr.spatial_denoising_enabled,
137
+ temporal: renderer.ssr.reprojection_mode !== 2, taa: renderer.feature_taa_enabled,
138
+ camera: { position: [0, 3.2, 7], target: [0, 0.2, -2], fov_degrees: 50 },
139
+ raw_format: 'Little-endian float32 RGBA, row-major, top-left origin. SSR radiance alpha is internal variance; scene alpha is opacity; mask is ground=1 elsewhere=0.',
140
+ variance_definition: 'Per-pixel Rec.709 linear luminance population variance across frames 100–115 (divide by 16). Spatial mean of those variances in each region.',
141
+ stages: {},
142
+ };
143
+ let scene_measurement;
144
+ for (const name of Object.keys(frames[0].stages).filter(n => n !== 'mask')) {
145
+ const measurement = measure_variance(frames.map(f => f.stages[name]), frames[0].stages.mask, canvas.width, canvas.height);
146
+ summary.roi = measurement.roi;
147
+ summary.stages[name] = measurement.summary;
148
+ files.set(`${name}-mean-luminance.f32`, measurement.mean);
149
+ files.set(`${name}-variance.f32`, measurement.variance);
150
+ if (name === 'scene') { scene_measurement = measurement; }
151
+ const r = measurement.summary.reflection;
152
+ const row = document.createElement('tr');
153
+ for (const value of [name, r.mean_population_variance.toExponential(5), r.rms_temporal_sigma.toFixed(6), r.p95_temporal_sigma.toFixed(6), r.mean_peak_to_peak.toFixed(6)]) {
154
+ const cell = document.createElement('td'); cell.textContent = value; row.append(cell);
155
+ }
156
+ element('metrics').append(row);
157
+ }
158
+ const heatmap = element('heatmap'), ctx = heatmap.getContext('2d');
159
+ const image = ctx.createImageData(canvas.width, canvas.height);
160
+ for (let p = 0; p < scene_measurement.variance.length; p++) {
161
+ const v = Math.min(1, Math.sqrt(scene_measurement.variance[p]) / 0.1);
162
+ image.data[p * 4] = 255 * Math.min(1, v * 2);
163
+ image.data[p * 4 + 1] = 255 * v;
164
+ image.data[p * 4 + 2] = 40 * v; image.data[p * 4 + 3] = 255;
165
+ }
166
+ ctx.putImageData(image, 0, 0);
167
+ const roi = summary.roi;
168
+ ctx.strokeStyle = '#ff4444'; ctx.strokeRect(roi.x, roi.y, roi.width, roi.height);
169
+ files.set('scene-temporal-sigma.png', png(heatmap).bytes);
170
+ json_download('download', 'measurements.json', summary, files);
171
+ for (const frame of frames) {
172
+ const figure = document.createElement('figure'), link = document.createElement('a'), img = document.createElement('img'), caption = document.createElement('figcaption');
173
+ img.src = link.href = frame.png_url; img.alt = `Frame ${frame.frame}`;
174
+ link.download = `frame-${frame.frame}.png`; link.append(img); caption.textContent = `Frame ${frame.frame}`;
175
+ figure.append(link, caption); element('frames').append(figure);
176
+ }
177
+ element('results').hidden = false;
178
+ lab.summary = summary;
179
+ report(`Complete · exactly 115 submitted frames · 16 captures (100–115) · renderer stopped`);
180
+ return summary;
181
+ });
182
+ }
183
+ async function convergence() {
184
+ if (frame_count !== 0) { throw new Error('Restart at frame zero before measuring convergence'); }
185
+ if (element('jitter').value !== 'off') {
186
+ const message = 'Convergence requires raster jitter off so checkpoints and reference use the same pixel grid.';
187
+ report(message);
188
+ throw new Error(message);
189
+ }
190
+ return exclusive(async () => {
191
+ const checkpoints = [4, 8, 16, 32, 64, 100, 128, 256, 512];
192
+ try {
193
+ for (let i = 1; i <= 1536; i++) {
194
+ reference.enabled = i > 512;
195
+ await render_frame(checkpoints.includes(i));
196
+ report(i <= 512 ? `Convergence · frame ${i}/512` : `Building separate reference · ${i - 512}/1024 samples`);
197
+ }
198
+ } finally { reference.enabled = false; }
199
+ const target = await reference.read();
200
+ const mask = frames.at(-1).stages.mask;
201
+ const summary = { width: canvas.width, height: canvas.height, checkpoints,
202
+ ssr_frame_indices: frames.map(f => f.ssr_frame_index), dt: 1 / 60,
203
+ reference_frames: [513, 1536], reference_sample_count: reference.count,
204
+ automatic_exposure: false, exposure_compensation: 1,
205
+ roughness: 0.3, metallic: 1, spatial: renderer.ssr.spatial_denoising_enabled,
206
+ temporal: renderer.ssr.reprojection_mode !== 2,
207
+ taa: renderer.feature_taa_enabled, raster_jitter: element('jitter').value,
208
+ camera: { position: [0, 3.2, 7], target: [0, 0.2, -2], fov_degrees: 50 },
209
+ definition: 'Per-pixel linear-luminance error in the reflection ROI against the unrectified float32 mean of pre-temporal samples from later frames. The reference is never fed into SSR.',
210
+ samples: frames.map(frame => ({ frame: frame.frame,
211
+ input: reflection_error(frame.stages.spatial1, target, mask, canvas.width, canvas.height),
212
+ temporal: reflection_error(frame.stages.temporal, target, mask, canvas.width, canvas.height),
213
+ spatial: reflection_error(frame.stages.spatial4, target, mask, canvas.width, canvas.height) })),
214
+ };
215
+ files.set('reference.f32', target);
216
+ json_download('convergence-download', 'convergence.json', summary, files);
217
+ for (const sample of summary.samples) {
218
+ const row = document.createElement('tr');
219
+ for (const value of [sample.frame, sample.input.rmse.toFixed(6), sample.temporal.rmse.toFixed(6), sample.spatial.rmse.toFixed(6), sample.temporal.bias.toFixed(6)]) {
220
+ const cell = document.createElement('td'); cell.textContent = String(value); row.append(cell);
221
+ }
222
+ element('convergence-metrics').append(row);
223
+ }
224
+ element('convergence-results').hidden = false;
225
+ lab.summary = summary;
226
+ report('Convergence complete · 9 checkpoints · 1,024 later reference samples · frame 1536 · renderer stopped');
227
+ return summary;
228
+ });
229
+ }
230
+ const lab = globalThis.ssr_variance_lab = {
231
+ renderer, camera, world, step, run, convergence, frames, files, get frame_count() { return frame_count; },
232
+ };
233
+ element('step').disabled = element('run').disabled = false;
234
+ element('converge').disabled = false;
235
+ element('converge').onclick = () => frame_count === 0 ? convergence().catch(() => {}) : restart('convergence');
236
+ element('step').onclick = () => step().catch(() => {});
237
+ element('run').onclick = () => frame_count === 0 ? run().catch(() => {}) : restart('run');
238
+ report('Ready · frame 0 · no frames submitted');
239
+ if (params.has('convergence')) { await convergence(); }
240
+ else if (params.has('run')) { await run(); }
241
+ return lab;
242
+ }
243
+ globalThis.ssr_variance_ready = start().catch(error => {
244
+ report(error.message ?? String(error)); element('status').classList.add('error'); console.error(error);
245
+ throw error;
246
+ });
@@ -0,0 +1,18 @@
1
+ export function reflection_error(sample: any, reference: any, mask: any, width: any, height: any): {
2
+ pixels: number;
3
+ mse: number;
4
+ rmse: number;
5
+ bias: number;
6
+ };
7
+ /** Unclipped float32 average of later pre-temporal samples, never fed into rendering. */
8
+ export class ReflectionReference extends RenderExtension {
9
+ constructor(device: any);
10
+ enabled: boolean;
11
+ count: number;
12
+ textures: any[];
13
+ device: any;
14
+ record(frame: any): void;
15
+ read(): Promise<TypedArray>;
16
+ }
17
+ import { RenderExtension } from "../../renderer/extension/RenderExtension.js";
18
+ //# sourceMappingURL=reference.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reference.d.ts","sourceRoot":"","sources":["../../../../../src/shade/playground/ssr_variance/reference.js"],"names":[],"mappings":"AA8DA;;;;;EAeC;AAjDD,yFAAyF;AACzF;IAKI,yBAAsD;IAHtD,iBAAgB;IAChB,cAAU;IACV,gBAAc;IACiB,YAAoB;IACnD,yBAmBC;IACD,4BAIC;CACJ;gCA5D+B,6CAA6C"}
@@ -0,0 +1,78 @@
1
+ import { RenderExtension } from "../../renderer/extension/RenderExtension.js";
2
+ import { ImageShader } from "../../renderer/shader/ImageShader.js";
3
+ import { CodeChunk } from "../../renderer/shader/compiler/CodeChunk.js";
4
+ import { ShaderDescriptor } from "../../renderer/shader/ShaderDescriptor.js";
5
+ import { ShaderResourceSetDescriptor } from "../../renderer/shader/resource/ShaderResourceSetDescriptor.js";
6
+ import { WebGPUStruct } from "../../renderer/shader/type/WebGPUStruct.js";
7
+ import { f32 } from "../../renderer/shader/type/WEBGPU_WGSL_PRIMITIVE_TYPES.js";
8
+ import { graph_image_pass } from "../../renderer/shader/graph/graph_image_pass.js";
9
+ import { graph_import_texture } from "../../renderer/shader/graph/graph_import_texture.js";
10
+ import { GPUTextureContext } from "../../renderer/texture/GPUTextureContext.js";
11
+ import { download_texture_data } from "../../renderer/texture/download_texture_data.js";
12
+
13
+ const settings = WebGPUStruct.from({ count: f32 });
14
+ const resources = new ShaderResourceSetDescriptor();
15
+ resources.setImmediate('settings', settings);
16
+ resources.createGroup().addTexture('tInput', 'unfilterable-float').addTexture('tPrevious', 'unfilterable-float');
17
+ const shader = new ImageShader({ descriptor: ShaderDescriptor.from({
18
+ label: 'SSR lab / later-sample reference', resources,
19
+ body: CodeChunk.from(/* language=WGSL */ `
20
+ @fragment fn main(@builtin(position) coord: vec4f) -> @location(0) vec4f {
21
+ let p = vec2i(coord.xy);
22
+ let sample = textureLoad(tInput, p, 0);
23
+ if (settings.count <= 1.0) { return sample; }
24
+ let previous = textureLoad(tPrevious, p, 0);
25
+ return previous + (sample - previous) / settings.count;
26
+ }`, [settings.declaration_chunk]),
27
+ }), targets: [{ format: 'rgba32float' }] });
28
+
29
+ /** Unclipped float32 average of later pre-temporal samples, never fed into rendering. */
30
+ export class ReflectionReference extends RenderExtension {
31
+ name = 'SSR lab / later-sample reference';
32
+ enabled = false;
33
+ count = 0;
34
+ textures = [];
35
+ constructor(device) { super(); this.device = device; }
36
+ record(frame) {
37
+ if (!this.enabled) { return; }
38
+ const temporal = frame.graph.__pass_nodes.find(p => p.name === 'SSR Reproject');
39
+ if (!temporal) { throw new Error('SSR is required for a reflection reference'); }
40
+ if (!this.textures.length) {
41
+ for (let i = 0; i < 2; i++) {
42
+ this.textures.push(GPUTextureContext.fromTexture(this.device.createTexture({
43
+ size: [frame.resolution[0], frame.resolution[1], 1], format: 'rgba32float',
44
+ usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC,
45
+ }), this.device));
46
+ }
47
+ }
48
+ const index = this.count % 2;
49
+ const previous = graph_import_texture(frame.graph, this.textures[1 - index], 'SSR reference previous');
50
+ const output = graph_import_texture(frame.graph, this.textures[index], 'SSR reference output');
51
+ graph_image_pass({ graph: frame.graph, shader,
52
+ inputs: { tInput: temporal.data.tInput, tPrevious: previous, settings: { count: this.count + 1 } },
53
+ output: [output] });
54
+ this.count++;
55
+ }
56
+ async read() {
57
+ if (!this.count) { throw new Error('Reference has no samples'); }
58
+ return download_texture_data({ device: this.device,
59
+ source: this.textures[(this.count - 1) % 2].gpu_texture });
60
+ }
61
+ }
62
+
63
+ export function reflection_error(sample, reference, mask, width, height) {
64
+ let count = 0, squared_error = 0, bias = 0;
65
+ for (let y = Math.floor(height * 0.5); y < Math.floor(height * 0.92); y++) {
66
+ for (let x = Math.floor(width * 0.25); x < Math.floor(width * 0.75); x++) {
67
+ const p = (y * width + x) * 4;
68
+ if (mask[p] <= 0.5) { continue; }
69
+ const error = (sample[p] - reference[p]) * 0.2126
70
+ + (sample[p + 1] - reference[p + 1]) * 0.7152
71
+ + (sample[p + 2] - reference[p + 2]) * 0.0722;
72
+ if (!Number.isFinite(error)) { throw new Error('Non-finite convergence sample'); }
73
+ squared_error += error * error; bias += error; count++;
74
+ }
75
+ }
76
+ if (!count) { throw new Error('No reflection pixels in convergence sample'); }
77
+ return { pixels: count, mse: squared_error / count, rmse: Math.sqrt(squared_error / count), bias: bias / count };
78
+ }
@@ -0,0 +1,11 @@
1
+ /** Two quads. The cross is a texture, with no extra geometry or surface-normal edges. */
2
+ export function create_scene(): Promise<{
3
+ scene: Scene;
4
+ ground: Mesh;
5
+ card: Mesh;
6
+ ground_material: StandardShadeMaterial;
7
+ }>;
8
+ import { Scene } from "../../renderer/scene/Scene.js";
9
+ import { Mesh } from "../../renderer/scene/Mesh.js";
10
+ import { StandardShadeMaterial } from "../../renderer/material/StandardShadeMaterial.js";
11
+ //# sourceMappingURL=scene.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scene.d.ts","sourceRoot":"","sources":["../../../../../src/shade/playground/ssr_variance/scene.js"],"names":[],"mappings":"AAeA,yFAAyF;AACzF;;;;;GA4CC;sBAnDqB,+BAA+B;qBADhC,8BAA8B;sCADb,kDAAkD"}
@@ -0,0 +1,61 @@
1
+ import { Color } from "../../../core/color/Color.js";
2
+ import Quaternion from "../../../core/geom/Quaternion.js";
3
+ import Vector3 from "../../../core/geom/Vector3.js";
4
+ import { MeshoptClusterizer } from "../../renderer/geometry/meshlet/build/meshoptimizer/meshopt_clusterizer.js";
5
+ import { make_plane_geometry } from "../../renderer/geometry/primitives/make_plane_geometry.js";
6
+ import { MeshletGeometry } from "../../renderer/geometry/MeshletGeometry.js";
7
+ import { meshlet_geometry_build_from_geometry } from "../../renderer/geometry/meshlet_geometry_build_from_geometry.js";
8
+ import { StandardShadeMaterial } from "../../renderer/material/StandardShadeMaterial.js";
9
+ import { Mesh } from "../../renderer/scene/Mesh.js";
10
+ import { Scene } from "../../renderer/scene/Scene.js";
11
+ import { ColorSpace } from "../../renderer/texture/ColorSpace.js";
12
+ import { ShadeTexture } from "../../renderer/texture/ShadeTexture.js";
13
+ import { ShadeImage } from "../../renderer/texture/source/ShadeImage.js";
14
+ import { make_flat_environment } from "../skinned_blas_refit/flat_environment.js";
15
+
16
+ /** Two quads. The cross is a texture, with no extra geometry or surface-normal edges. */
17
+ export async function create_scene() {
18
+ await MeshoptClusterizer.ready;
19
+ const scene = new Scene();
20
+ const quad = (name, width, height, material) => {
21
+ const geometry = new MeshletGeometry();
22
+ meshlet_geometry_build_from_geometry(make_plane_geometry(width, height), geometry);
23
+ const mesh = Mesh.from(geometry, material);
24
+ mesh.name = name;
25
+ scene.add(mesh);
26
+ return mesh;
27
+ };
28
+ const ground_material = new StandardShadeMaterial();
29
+ ground_material.diffuse_color.copy(new Color(0.85, 0.85, 0.85));
30
+ ground_material.roughness_factor = 0.3;
31
+ ground_material.metallic_factor = 1;
32
+ const ground = quad('Ground / roughness 0.30', 18, 18, ground_material);
33
+ const rotation = Quaternion.fromAxisAngle(Vector3.right, -Math.PI / 2);
34
+ ground.transform_local.setRotation(rotation.x, rotation.y, rotation.z, rotation.w);
35
+ ground.transform_local.setTranslation(0, 0, -2);
36
+ ground.updateMatrices();
37
+
38
+ const canvas = document.createElement('canvas');
39
+ canvas.width = canvas.height = 512;
40
+ const ctx = canvas.getContext('2d');
41
+ ctx.fillStyle = '#181818'; ctx.fillRect(0, 0, 512, 512);
42
+ ctx.fillStyle = '#f4f4f4';
43
+ ctx.fillRect(216, 64, 80, 384); ctx.fillRect(64, 216, 384, 80);
44
+ // Small inset bars expose detail loss at a second scale.
45
+ ctx.fillStyle = '#ff8822'; ctx.fillRect(56, 56, 96, 24);
46
+ ctx.fillStyle = '#33bbff'; ctx.fillRect(360, 408, 96, 48);
47
+ const image = ShadeImage.fromImageBitmap(await createImageBitmap(canvas, { premultiplyAlpha: 'none' }));
48
+ image.color_space = ColorSpace.SRGB;
49
+ const pattern = ShadeTexture.from(image);
50
+ pattern.label = 'SSR variance / cross';
51
+ const card_material = new StandardShadeMaterial();
52
+ card_material.diffuse_color.copy(new Color(0, 0, 0));
53
+ card_material.texture_emissive = pattern;
54
+ card_material.emissive_factor.copy(new Color(1, 1, 1));
55
+ card_material.roughness_factor = 1;
56
+ const card = quad('Pattern card', 3, 3, card_material);
57
+ card.transform_local.setTranslation(0, 2, -2);
58
+ card.updateMatrices();
59
+ scene.lights.environment = make_flat_environment([0.025, 0.025, 0.025]);
60
+ return { scene, ground, card, ground_material };
61
+ }
@@ -0,0 +1,44 @@
1
+ /** Per-pixel temporal moments of linear Rec.709 luminance. No spatial pooling before variance. */
2
+ export function measure_variance(samples: any, mask: any, width: any, height: any): {
3
+ mean: Float32Array;
4
+ variance: Float32Array;
5
+ roi: {
6
+ x: number;
7
+ y: number;
8
+ width: number;
9
+ height: number;
10
+ };
11
+ summary: {
12
+ all: {
13
+ pixels: number;
14
+ mean_luminance: number;
15
+ mean_population_variance: number;
16
+ mean_sample_variance: number;
17
+ rms_temporal_sigma: number;
18
+ p95_temporal_sigma: number;
19
+ mean_peak_to_peak: number;
20
+ max_peak_to_peak: number;
21
+ };
22
+ ground: {
23
+ pixels: number;
24
+ mean_luminance: number;
25
+ mean_population_variance: number;
26
+ mean_sample_variance: number;
27
+ rms_temporal_sigma: number;
28
+ p95_temporal_sigma: number;
29
+ mean_peak_to_peak: number;
30
+ max_peak_to_peak: number;
31
+ };
32
+ reflection: {
33
+ pixels: number;
34
+ mean_luminance: number;
35
+ mean_population_variance: number;
36
+ mean_sample_variance: number;
37
+ rms_temporal_sigma: number;
38
+ p95_temporal_sigma: number;
39
+ mean_peak_to_peak: number;
40
+ max_peak_to_peak: number;
41
+ };
42
+ };
43
+ };
44
+ //# sourceMappingURL=statistics.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"statistics.d.ts","sourceRoot":"","sources":["../../../../../src/shade/playground/ssr_variance/statistics.js"],"names":[],"mappings":"AAAA,kGAAkG;AAClG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAiDC"}
@@ -0,0 +1,51 @@
1
+ /** Per-pixel temporal moments of linear Rec.709 luminance. No spatial pooling before variance. */
2
+ export function measure_variance(samples, mask, width, height) {
3
+ if (samples.length < 2) { throw new Error('At least two samples are required'); }
4
+ const pixels = width * height;
5
+ if (mask.length !== pixels * 4 || samples.some(s => s.length !== pixels * 4)) {
6
+ throw new Error('Capture dimensions do not match');
7
+ }
8
+ const mean = new Float64Array(pixels), m2 = new Float64Array(pixels);
9
+ const minimum = new Float64Array(pixels).fill(Infinity), maximum = new Float64Array(pixels).fill(-Infinity);
10
+ for (let n = 0; n < samples.length; n++) {
11
+ const sample = samples[n];
12
+ for (let p = 0; p < pixels; p++) {
13
+ const i = p * 4;
14
+ if (![sample[i], sample[i + 1], sample[i + 2], sample[i + 3]].every(Number.isFinite)) {
15
+ throw new Error(`Non-finite capture at sample ${n}, pixel ${p}`);
16
+ }
17
+ const y = sample[i] * 0.2126 + sample[i + 1] * 0.7152 + sample[i + 2] * 0.0722;
18
+ const delta = y - mean[p];
19
+ mean[p] += delta / (n + 1);
20
+ m2[p] += delta * (y - mean[p]);
21
+ minimum[p] = Math.min(minimum[p], y); maximum[p] = Math.max(maximum[p], y);
22
+ }
23
+ }
24
+ const variance = Float32Array.from(m2, v => v / samples.length);
25
+ const summarize = predicate => {
26
+ const sigmas = [];
27
+ let variance_sum = 0, mean_sum = 0, range_sum = 0, max_range = 0;
28
+ for (let p = 0; p < pixels; p++) {
29
+ if (!predicate(p)) { continue; }
30
+ sigmas.push(Math.sqrt(variance[p])); variance_sum += variance[p]; mean_sum += mean[p];
31
+ const range = maximum[p] - minimum[p]; range_sum += range; max_range = Math.max(max_range, range);
32
+ }
33
+ sigmas.sort((a, b) => a - b);
34
+ const count = sigmas.length;
35
+ if (!count) { throw new Error('Empty measurement region'); }
36
+ return { pixels: count, mean_luminance: mean_sum / count,
37
+ mean_population_variance: variance_sum / count,
38
+ mean_sample_variance: variance_sum / count * samples.length / (samples.length - 1),
39
+ rms_temporal_sigma: Math.sqrt(variance_sum / count),
40
+ p95_temporal_sigma: sigmas[Math.ceil(count * 0.95) - 1],
41
+ mean_peak_to_peak: range_sum / count, max_peak_to_peak: max_range };
42
+ };
43
+ // Fixed screen rectangle shared by every stage/control, intersected with ground normals.
44
+ const roi = { x: Math.floor(width * 0.25), y: Math.floor(height * 0.50),
45
+ width: Math.floor(width * 0.5), height: Math.floor(height * 0.42) };
46
+ const ground = p => mask[p * 4] > 0.5;
47
+ return { mean: Float32Array.from(mean), variance, roi,
48
+ summary: { all: summarize(() => true), ground: summarize(ground),
49
+ reflection: summarize(p => ground(p) && p % width >= roi.x && p % width < roi.x + roi.width
50
+ && Math.floor(p / width) >= roi.y && Math.floor(p / width) < roi.y + roi.height) } };
51
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"Geometry.d.ts","sourceRoot":"","sources":["../../../../../src/shade/renderer/geometry/Geometry.js"],"names":[],"mappings":"4BA2BU,MAAM;;;;AAQhB;;GAEG;AACH;IAQI;;;;OAIG;IACH,iBAEC;IAED;;;OAGG;IACH,MAFU,MAAM,CAEN;IAEV;;;OAGG;IACH,YAFU,SAAS,EAAE,CAEL;IAEhB;;;OAGG;IACH,OAFU,SAAS,GAAC,IAAI,CAEX;IAEb;;;OAGG;IACH,cAFU,YAAY,CAEa;IACnC;;;OAGG;IACH,iBAFU,YAAY,CAEgB;IAEtC;;;OAGG;IACH,OAFU,MAAM,CAEM;IAOtB;;;;;;;;;;OAUG;IACH,sBAEC;IAED;;OAEG;IACH,8BAIC;IAGD;;;;OAIG;IACH,cAHW,MAAM,GAAC,aAAa,GAClB,IAAI,CAIhB;IAED;;;;OAIG;IACH,gBAHW,MAAM,GAAC,aAAa,GAClB,IAAI,CAIhB;IAED;;;;OAIG;IACH,gBAHW,MAAM,GAAC,aAAa,SACpB,OAAO,QAQjB;IAED;;;;OAIG;IACH,cAHW,MAAM,GAAC,aAAa,GAClB,OAAO,CAInB;IAED;;;;OAIG;IACH,cAHW,QAAQ,GACN,OAAO,CAYnB;IAED,eAQC;IAED;;;;OAIG;IACH,mBAHW,MAAM,GACL,SAAS,GAAC,SAAS,CAkB9B;IAED;;;;OAIG;IACH,mBAHW,MAAM,GACJ,OAAO,CAMnB;IAED;;;OAGG;IACH,wBAFW,SAAS,QAQnB;IAED;;;OAGG;IACH,wBAFW,SAAS,QASnB;IAED;;;;OAIG;IACH,sBAHW,MAAM,GACL,SAAS,GAAC,SAAS,CAiB9B;IAED;;;OAGG;IACH,kBAFY,MAAM,CAWjB;IAED;;;OAGG;IACH,iBAFY,MAAM,CAYjB;IAED;;;;OAIG;IACH,qBAFa,MAAM,CAIlB;IAED;;;OAGG;IACH,oBAIC;IAED;;OAEG;IACH,mBASC;IAED,qBAIC;IAED;;OAEG;IACH,qCAoBC;IAED,8BAaC;IAED,8BAKC;IAED,2BAeC;IAED,sBAOC;IAED,uBA4BC;IAoFD,uBAOC;IAED,wBAWC;IAED;;;OAGG;IACH,YAFW,QAAQ,QAmBlB;IAED;;;OAGG;IACH,SAFY,QAAQ,CAQnB;IAED,eAEC;IAED;;;OAGG;IACH,iCAYC;IAGL;;;OAGG;IACH,qBAFU,OAAO,CAEY;;CAN5B;0BAvkByB,gBAAgB"}
1
+ {"version":3,"file":"Geometry.d.ts","sourceRoot":"","sources":["../../../../../src/shade/renderer/geometry/Geometry.js"],"names":[],"mappings":"4BA4BU,MAAM;;;;AAQhB;;GAEG;AACH;IAQI;;;;OAIG;IACH,iBAEC;IAED;;;OAGG;IACH,MAFU,MAAM,CAEN;IAEV;;;OAGG;IACH,YAFU,SAAS,EAAE,CAEL;IAEhB;;;OAGG;IACH,OAFU,SAAS,GAAC,IAAI,CAEX;IAEb;;;OAGG;IACH,cAFU,YAAY,CAEa;IACnC;;;OAGG;IACH,iBAFU,YAAY,CAEgB;IAEtC;;;OAGG;IACH,OAFU,MAAM,CAEM;IAOtB;;;;;;;;;;OAUG;IACH,sBAEC;IAED;;OAEG;IACH,8BAIC;IAGD;;;;OAIG;IACH,cAHW,MAAM,GAAC,aAAa,GAClB,IAAI,CAIhB;IAED;;;;OAIG;IACH,gBAHW,MAAM,GAAC,aAAa,GAClB,IAAI,CAIhB;IAED;;;;OAIG;IACH,gBAHW,MAAM,GAAC,aAAa,SACpB,OAAO,QAQjB;IAED;;;;OAIG;IACH,cAHW,MAAM,GAAC,aAAa,GAClB,OAAO,CAInB;IAED;;;;OAIG;IACH,cAHW,QAAQ,GACN,OAAO,CAYnB;IAED,eAQC;IAED;;;;OAIG;IACH,mBAHW,MAAM,GACL,SAAS,GAAC,SAAS,CAkB9B;IAED;;;;OAIG;IACH,mBAHW,MAAM,GACJ,OAAO,CAMnB;IAED;;;OAGG;IACH,wBAFW,SAAS,QAQnB;IAED;;;OAGG;IACH,wBAFW,SAAS,QASnB;IAED;;;;OAIG;IACH,sBAHW,MAAM,GACL,SAAS,GAAC,SAAS,CAiB9B;IAED;;;OAGG;IACH,kBAFY,MAAM,CAWjB;IAED;;;OAGG;IACH,iBAFY,MAAM,CAYjB;IAED;;;;OAIG;IACH,qBAFa,MAAM,CAIlB;IAED;;;OAGG;IACH,oBAIC;IAED;;OAEG;IACH,mBASC;IAED,qBAIC;IAED;;OAEG;IACH,qCAoBC;IAED,8BAaC;IAED,8BAKC;IAED,2BAeC;IAED,sBAOC;IAED,uBA4BC;IAoFD,uBAOC;IAED,wBAWC;IAED;;;OAGG;IACH,YAFW,QAAQ,QAelB;IAED;;;OAGG;IACH,SAFY,QAAQ,CAQnB;IAED,eAEC;IAED;;;OAGG;IACH,iCAYC;IAGL;;;OAGG;IACH,qBAFU,OAAO,CAEY;;CAN5B;0BAnkByB,gBAAgB"}
@@ -14,8 +14,9 @@ import {
14
14
  import {
15
15
  geometry_compute_vertex_normals_unindexed
16
16
  } from "../../../engine/graphics/geometry/buffered/geometry_compute_vertex_normals_unindexed.js";
17
- import { optional_equality } from "../../descriptor/util/optional_equality.js";
18
- import { optional_hash } from "../../descriptor/util/optional_hash.js";
17
+ import { optional_clone } from "../../../core/model/object/optional_clone.js";
18
+ import { optional_equality } from "../../../core/model/object/optional_equality.js";
19
+ import { optional_hash } from "../../../core/model/object/optional_hash.js";
19
20
  import { Attribute } from "./Attribute.js";
20
21
  import { build_tangent_from_normal } from "./normals/build_tangent_from_normal.js";
21
22
  import { geometry_build_tangents } from "./normals/geometry_build_tangents.js";
@@ -551,11 +552,7 @@ export class Geometry {
551
552
 
552
553
  this.name = other.name;
553
554
 
554
- if (other.index !== null) {
555
- this.index = other.index.clone();
556
- } else {
557
- this.index = null;
558
- }
555
+ this.index = optional_clone(other.index);
559
556
 
560
557
  this.attributes = other.attributes.map(a => a.clone());
561
558
 
@@ -1 +1 @@
1
- {"version":3,"file":"tiny-gltf.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/loader/gltf/tiny-gltf.js"],"names":[],"mappings":"AA4UA;IAoEI;;;;;;;;;;;;;OAaG;IACH,+CAFU,aAAa,CAEgC;IA3BvD;;;;OAIG;IACH,qFAFW,qBAAqB,EAQ/B;IAjED,oBAA2B;IAE3B;;;;;;;;OAQG;IACH,iDAAmD;IAEnD;;;;;;;;;OASG;IACH,SAFU,IAAI,MAAM,EAAE,IAAI,GAAC,IAAI,GAAC,WAAW,CAAC,GAAC,SAAS,CAElC;IAqKpB;;;OAGG;IACH,iBAHW,MAAM,GACJ,QAAQ,QAAQ,CAAC,CAgB7B;IAED;;;;;;OAMG;IACH,4BAJW,WAAW,WACX,MAAM,GACL,QAAQ;QAAC,KAAK,CAAA;KAAC,MAAE,CAAC,CAM7B;IAED;;;;;;OAMG;IACH;;;mBALwC,EAAE;eAAO,EAAE;mBAAW,EAAE;iBAAS,EAAE;mBAAW,EAAE;kBAAW,EAAE;gBAAS,EAAE;gBAAQ,EAAE;gBAC/G,MAAM,gBACN,UAAU,GACT,QAAQ;QAAC,KAAK,CAAA;KAAC,MAAE,CAAC,CAiR7B;;CAGJ;8BAtyB6B,qDAAqD"}
1
+ {"version":3,"file":"tiny-gltf.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/loader/gltf/tiny-gltf.js"],"names":[],"mappings":"AA4UA;IAoEI;;;;;;;;;;;;;OAaG;IACH,+CAFU,aAAa,CAEgC;IA3BvD;;;;OAIG;IACH,qFAFW,qBAAqB,EAQ/B;IAjED,oBAA2B;IAE3B;;;;;;;;OAQG;IACH,iDAAmD;IAEnD;;;;;;;;;OASG;IACH,SAFU,IAAI,MAAM,EAAE,IAAI,GAAC,IAAI,GAAC,WAAW,CAAC,GAAC,SAAS,CAElC;IAqKpB;;;OAGG;IACH,iBAHW,MAAM,GACJ,QAAQ,QAAQ,CAAC,CAgB7B;IAED;;;;;;OAMG;IACH,4BAJW,WAAW,WACX,MAAM,GACL,QAAQ;QAAC,KAAK,CAAA;KAAC,MAAE,CAAC,CAM7B;IAED;;;;;;OAMG;IACH;;;mBALwC,EAAE;eAAO,EAAE;mBAAW,EAAE;iBAAS,EAAE;mBAAW,EAAE;kBAAW,EAAE;gBAAS,EAAE;gBAAQ,EAAE;gBAC/G,MAAM,gBACN,UAAU,GACT,QAAQ;QAAC,KAAK,CAAA;KAAC,MAAE,CAAC,CAuR7B;;CAGJ;8BA5yB6B,qDAAqD"}
@@ -498,7 +498,7 @@ export class TinyGltf {
498
498
  * Read a KTX2 image into texels, transcoding it where its payload is Basis.
499
499
  *
500
500
  * @param {ArrayBuffer|Uint8Array} bytes the whole file
501
- * @returns {ShadeTexelData}
501
+ * @returns {Promise<ShadeTexelData>}
502
502
  * @param {TextureRole} [role] what the image is for, which orders the candidate targets
503
503
  * @throws {Error} naming the feature, for a container this reader does not read
504
504
  */
@@ -811,7 +811,13 @@ export class TinyGltf {
811
811
  place_nodes(json);
812
812
 
813
813
  // Replace the resolved resources in the JSON structure.
814
- const buffers = await Promise.all(pendingBuffers);
814
+ // Observe image failures while buffers are still loading. Native decompression can reject
815
+ // during another resource's I/O; deferring its handler until all buffers finish would leave
816
+ // that rejection unhandled (and could leave the load waiting indefinitely).
817
+ const [buffers, images] = await Promise.all([
818
+ Promise.all(pendingBuffers),
819
+ Promise.all(pendingImages),
820
+ ]);
815
821
 
816
822
  /*
817
823
  Views that some extension produces the bytes for become ordinary ones here, over decoded
@@ -827,7 +833,7 @@ export class TinyGltf {
827
833
  );
828
834
 
829
835
  json.buffers = buffers;
830
- json.images = await Promise.all(pendingImages);
836
+ json.images = images;
831
837
 
832
838
  return json;
833
839
  }
@@ -13,17 +13,17 @@ export function decode_data_url(url: string): {
13
13
  *
14
14
  * Accepts anything `PNGReader` accepts; see the module comment for what that covers and how
15
15
  * other layouts are brought to RGBA8. Errors from the reader (bad signature, truncated stream,
16
- * corrupt deflate payload) propagate as thrown.
16
+ * corrupt deflate payload) reject the returned promise.
17
17
  *
18
18
  * @param {Uint8Array|ArrayBuffer} png_bytes
19
- * @returns {{ width: number, height: number, channels: number, pixels: Uint8Array }}
19
+ * @returns {Promise<{ width: number, height: number, channels: number, pixels: Uint8Array }>}
20
20
  */
21
- export function decode_png(png_bytes: Uint8Array | ArrayBuffer): {
21
+ export function decode_png(png_bytes: Uint8Array | ArrayBuffer): Promise<{
22
22
  width: number;
23
23
  height: number;
24
24
  channels: number;
25
25
  pixels: Uint8Array;
26
- };
26
+ }>;
27
27
  /**
28
28
  * Create a ShadeTexture from decoded image data.
29
29
  *
@@ -1 +1 @@
1
- {"version":3,"file":"usd_decode_image.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/loader/usd/usd_decode_image.js"],"names":[],"mappings":"AA4BA;;;;;GAKG;AACH,qCAHW,MAAM;cACQ,MAAM;UAAQ,UAAU;EAgChD;AAGD;;;;;;;;;GASG;AACH,sCAHW,UAAU,GAAC,WAAW;WACX,MAAM;YAAU,MAAM;cAAY,MAAM;YAAU,UAAU;EA0DjF;AAGD;;;;;GAKG;AACH,2DAHW;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,UAAU,CAAA;CAAE,GACrE,YAAY,CAexB;6BAnI4B,+BAA+B"}
1
+ {"version":3,"file":"usd_decode_image.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/loader/usd/usd_decode_image.js"],"names":[],"mappings":"AA4BA;;;;;GAKG;AACH,qCAHW,MAAM;cACQ,MAAM;UAAQ,UAAU;EAgChD;AAGD;;;;;;;;;GASG;AACH,sCAHW,UAAU,GAAC,WAAW;WACH,MAAM;YAAU,MAAM;cAAY,MAAM;YAAU,UAAU;GA0DzF;AAGD;;;;;GAKG;AACH,2DAHW;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,UAAU,CAAA;CAAE,GACrE,YAAY,CAexB;6BAnI4B,+BAA+B"}