@woosh/meep-engine 3.15.0 → 3.16.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 (952) hide show
  1. package/editor/Editor.js +7 -7
  2. package/editor/actions/concrete/TransformModifyAction.d.ts +4 -4
  3. package/editor/actions/concrete/TransformModifyAction.d.ts.map +1 -1
  4. package/editor/actions/concrete/TransformModifyAction.js +4 -4
  5. package/editor/camera/camera_framing.js +7 -7
  6. package/editor/ecs/component/editors/ecs/Transform64Editor.d.ts +20 -0
  7. package/editor/ecs/component/editors/ecs/Transform64Editor.d.ts.map +1 -0
  8. package/editor/ecs/component/editors/ecs/Transform64Editor.js +70 -0
  9. package/editor/ecs/component/prototypeObjectEditor.js +2 -2
  10. package/editor/ecs/component/registerEngineComponentEditors.js +3 -3
  11. package/editor/particles/effect/ParticleEffectDocument.d.ts +396 -0
  12. package/editor/particles/effect/ParticleEffectDocument.d.ts.map +1 -0
  13. package/editor/particles/effect/ParticleEffectDocument.js +989 -0
  14. package/editor/particles/effect/ParticleReferenceSimulation.d.ts +113 -0
  15. package/editor/particles/effect/ParticleReferenceSimulation.d.ts.map +1 -0
  16. package/editor/particles/effect/ParticleReferenceSimulation.js +377 -0
  17. package/editor/particles/effect/actions/ParticleGroupDefineAction.d.ts +35 -0
  18. package/editor/particles/effect/actions/ParticleGroupDefineAction.d.ts.map +1 -0
  19. package/editor/particles/effect/actions/ParticleGroupDefineAction.js +45 -0
  20. package/editor/particles/effect/make_starter_particle_effect.d.ts +23 -0
  21. package/editor/particles/effect/make_starter_particle_effect.d.ts.map +1 -0
  22. package/editor/particles/effect/make_starter_particle_effect.js +177 -0
  23. package/editor/persistence/buildNewSceneDataset.d.ts.map +1 -1
  24. package/editor/persistence/buildNewSceneDataset.js +10 -8
  25. package/editor/process/symbolic/AudioEmitterSymbolicDisplay.js +67 -67
  26. package/editor/process/symbolic/CameraSymbolicDisplay.js +146 -146
  27. package/editor/process/symbolic/GridPositionSymbolicDisplay.js +6 -6
  28. package/editor/process/symbolic/LightSymbolicDisplay.js +172 -172
  29. package/editor/process/symbolic/ParticleEmitterSymbolicDisplay.d.ts.map +1 -1
  30. package/editor/process/symbolic/ParticleEmitterSymbolicDisplay.js +6 -3
  31. package/editor/process/symbolic/PositionedMarkerSymbolicDisplay.js +6 -6
  32. package/editor/selection/editor_pick.js +130 -130
  33. package/editor/templates/entity_templates.js +7 -7
  34. package/editor/tools/TransformToolV2.d.ts +3 -3
  35. package/editor/tools/TransformToolV2.d.ts.map +1 -1
  36. package/editor/tools/TransformToolV2.js +58 -25
  37. package/editor/tools/paint/TerrainHeightPaintTool.d.ts.map +1 -1
  38. package/editor/tools/paint/TerrainHeightPaintTool.js +11 -9
  39. package/editor/tools/v2/GizmoNode.js +2 -2
  40. package/editor/tools/v2/TransformControls.d.ts +3 -3
  41. package/editor/tools/v2/TransformControls.d.ts.map +1 -1
  42. package/editor/tools/v2/TransformControls.js +1003 -971
  43. package/editor/tools/v2/handle/gizmo_drag_plane.d.ts.map +1 -1
  44. package/editor/tools/v2/handle/gizmo_drag_plane.js +6 -1
  45. package/editor/view/ecs/components/common/AutoCanvasView.js +17 -0
  46. package/editor/view/node-graph/NodeGraphEditorView.d.ts +143 -2
  47. package/editor/view/node-graph/NodeGraphEditorView.d.ts.map +1 -1
  48. package/editor/view/node-graph/NodeGraphEditorView.js +358 -6
  49. package/editor/view/node-graph/NodeGraphMinimapView.d.ts +120 -0
  50. package/editor/view/node-graph/NodeGraphMinimapView.d.ts.map +1 -0
  51. package/editor/view/node-graph/NodeGraphMinimapView.js +859 -0
  52. package/editor/view/node-graph/NodeGraphPaletteView.d.ts +112 -0
  53. package/editor/view/node-graph/NodeGraphPaletteView.d.ts.map +1 -0
  54. package/editor/view/node-graph/NodeGraphPaletteView.js +352 -0
  55. package/editor/view/node-graph/NodeGraphSearchMenuView.d.ts +76 -0
  56. package/editor/view/node-graph/NodeGraphSearchMenuView.d.ts.map +1 -0
  57. package/editor/view/node-graph/NodeGraphSearchMenuView.js +247 -0
  58. package/editor/view/node-graph/NodeGraphView.d.ts +147 -5
  59. package/editor/view/node-graph/NodeGraphView.d.ts.map +1 -1
  60. package/editor/view/node-graph/NodeGraphView.js +809 -76
  61. package/editor/view/node-graph/NodeIconView.d.ts +33 -0
  62. package/editor/view/node-graph/NodeIconView.d.ts.map +1 -0
  63. package/editor/view/node-graph/NodeIconView.js +54 -0
  64. package/editor/view/node-graph/NodeView.d.ts +71 -1
  65. package/editor/view/node-graph/NodeView.d.ts.map +1 -1
  66. package/editor/view/node-graph/NodeView.js +318 -17
  67. package/editor/view/node-graph/PortView.d.ts +41 -1
  68. package/editor/view/node-graph/PortView.d.ts.map +1 -1
  69. package/editor/view/node-graph/PortView.js +121 -69
  70. package/editor/view/node-graph/actions/ConnectionDeleteAction.d.ts +16 -3
  71. package/editor/view/node-graph/actions/ConnectionDeleteAction.d.ts.map +1 -1
  72. package/editor/view/node-graph/actions/ConnectionDeleteAction.js +29 -3
  73. package/editor/view/node-graph/actions/NodeCreateAction.d.ts +65 -0
  74. package/editor/view/node-graph/actions/NodeCreateAction.d.ts.map +1 -0
  75. package/editor/view/node-graph/actions/NodeCreateAction.js +108 -0
  76. package/editor/view/node-graph/actions/NodeDeleteAction.d.ts +6 -0
  77. package/editor/view/node-graph/actions/NodeDeleteAction.d.ts.map +1 -1
  78. package/editor/view/node-graph/actions/NodeDeleteAction.js +24 -2
  79. package/editor/view/node-graph/connection_wire_geometry.d.ts +34 -0
  80. package/editor/view/node-graph/connection_wire_geometry.d.ts.map +1 -1
  81. package/editor/view/node-graph/connection_wire_geometry.js +79 -3
  82. package/editor/view/node-graph/node-graph.css +678 -0
  83. package/editor/view/particles/effect/ParticleCodeEditorView.d.ts +37 -0
  84. package/editor/view/particles/effect/ParticleCodeEditorView.d.ts.map +1 -0
  85. package/editor/view/particles/effect/ParticleCodeEditorView.js +479 -0
  86. package/editor/view/particles/effect/ParticleEffectEditorView.d.ts +61 -0
  87. package/editor/view/particles/effect/ParticleEffectEditorView.d.ts.map +1 -0
  88. package/editor/view/particles/effect/ParticleEffectEditorView.js +223 -0
  89. package/editor/view/particles/effect/ParticleEmitterInspectorView.d.ts +25 -0
  90. package/editor/view/particles/effect/ParticleEmitterInspectorView.d.ts.map +1 -0
  91. package/editor/view/particles/effect/ParticleEmitterInspectorView.js +606 -0
  92. package/editor/view/particles/effect/ParticleGraphEditorView.d.ts +156 -0
  93. package/editor/view/particles/effect/ParticleGraphEditorView.d.ts.map +1 -0
  94. package/editor/view/particles/effect/ParticleGraphEditorView.js +1636 -0
  95. package/editor/view/particles/effect/ParticleNodeParametersView.d.ts +22 -0
  96. package/editor/view/particles/effect/ParticleNodeParametersView.d.ts.map +1 -0
  97. package/editor/view/particles/effect/ParticleNodeParametersView.js +520 -0
  98. package/editor/view/particles/effect/ParticlePreviewView.d.ts +52 -0
  99. package/editor/view/particles/effect/ParticlePreviewView.d.ts.map +1 -0
  100. package/editor/view/particles/effect/ParticlePreviewView.js +533 -0
  101. package/editor/view/particles/effect/particle-editor.css +556 -0
  102. package/editor/view/prepareMeshLibrary.d.ts.map +1 -1
  103. package/editor/view/prepareMeshLibrary.js +8 -4
  104. package/package.json +1 -1
  105. package/samples/engine/first-scene/main.js +152 -152
  106. package/samples/generation/generators/mir_generator_place_bases.js +3 -3
  107. package/src/core/assert.d.ts +28 -24
  108. package/src/core/assert.d.ts.map +1 -1
  109. package/src/core/assert.js +34 -26
  110. package/src/core/geom/3d/shape/PosedShape3D.d.ts +17 -0
  111. package/src/core/geom/3d/shape/PosedShape3D.d.ts.map +1 -1
  112. package/src/core/geom/3d/shape/PosedShape3D.js +34 -2
  113. package/src/core/graph/format/graphviz/graphviz_escape_html_label.d.ts +17 -0
  114. package/src/core/graph/format/graphviz/graphviz_escape_html_label.d.ts.map +1 -0
  115. package/src/core/graph/format/graphviz/graphviz_escape_html_label.js +25 -0
  116. package/src/core/graph/format/graphviz/graphviz_escape_quoted.d.ts +16 -0
  117. package/src/core/graph/format/graphviz/graphviz_escape_quoted.d.ts.map +1 -0
  118. package/src/core/graph/format/graphviz/graphviz_escape_quoted.js +24 -0
  119. package/src/core/model/node-graph/NodeGraph.d.ts +12 -0
  120. package/src/core/model/node-graph/NodeGraph.d.ts.map +1 -1
  121. package/src/core/model/node-graph/NodeGraph.js +21 -6
  122. package/src/core/model/node-graph/node/NodeDescription.d.ts +36 -0
  123. package/src/core/model/node-graph/node/NodeDescription.d.ts.map +1 -1
  124. package/src/core/model/node-graph/node/NodeDescription.js +41 -0
  125. package/src/core/model/node-graph/visual/ConnectionVisualData.d.ts +119 -0
  126. package/src/core/model/node-graph/visual/ConnectionVisualData.d.ts.map +1 -0
  127. package/src/core/model/node-graph/visual/ConnectionVisualData.js +317 -0
  128. package/src/core/model/node-graph/visual/NodeDescriptionVisualRegistry.d.ts +4 -0
  129. package/src/core/model/node-graph/visual/NodeDescriptionVisualRegistry.d.ts.map +1 -1
  130. package/src/core/model/node-graph/visual/NodeDescriptionVisualRegistry.js +125 -22
  131. package/src/core/model/node-graph/visual/NodeGraphVisualData.d.ts +91 -2
  132. package/src/core/model/node-graph/visual/NodeGraphVisualData.d.ts.map +1 -1
  133. package/src/core/model/node-graph/visual/NodeGraphVisualData.js +164 -113
  134. package/src/core/model/node-graph/visual/layout/LayoutBoxGraph.d.ts +162 -0
  135. package/src/core/model/node-graph/visual/layout/LayoutBoxGraph.d.ts.map +1 -0
  136. package/src/core/model/node-graph/visual/layout/LayoutBoxGraph.js +288 -0
  137. package/src/core/model/node-graph/visual/layout/LayoutProblem.d.ts +109 -0
  138. package/src/core/model/node-graph/visual/layout/LayoutProblem.d.ts.map +1 -0
  139. package/src/core/model/node-graph/visual/layout/LayoutProblem.js +141 -0
  140. package/src/core/model/node-graph/visual/layout/NodeGraphLayoutOptions.d.ts +149 -0
  141. package/src/core/model/node-graph/visual/layout/NodeGraphLayoutOptions.d.ts.map +1 -0
  142. package/src/core/model/node-graph/visual/layout/NodeGraphLayoutOptions.js +175 -0
  143. package/src/core/model/node-graph/visual/layout/RouteGrid.d.ts +137 -0
  144. package/src/core/model/node-graph/visual/layout/RouteGrid.d.ts.map +1 -0
  145. package/src/core/model/node-graph/visual/layout/RouteGrid.js +242 -0
  146. package/src/core/model/node-graph/visual/layout/RouteSearch.d.ts +114 -0
  147. package/src/core/model/node-graph/visual/layout/RouteSearch.d.ts.map +1 -0
  148. package/src/core/model/node-graph/visual/layout/RouteSearch.js +300 -0
  149. package/src/core/model/node-graph/visual/layout/RouteSet.d.ts +86 -0
  150. package/src/core/model/node-graph/visual/layout/RouteSet.d.ts.map +1 -0
  151. package/src/core/model/node-graph/visual/layout/RouteSet.js +168 -0
  152. package/src/core/model/node-graph/visual/layout/layout_assign_coordinates.d.ts +48 -0
  153. package/src/core/model/node-graph/visual/layout/layout_assign_coordinates.d.ts.map +1 -0
  154. package/src/core/model/node-graph/visual/layout/layout_assign_coordinates.js +555 -0
  155. package/src/core/model/node-graph/visual/layout/layout_assign_layers.d.ts +34 -0
  156. package/src/core/model/node-graph/visual/layout/layout_assign_layers.d.ts.map +1 -0
  157. package/src/core/model/node-graph/visual/layout/layout_assign_layers.js +202 -0
  158. package/src/core/model/node-graph/visual/layout/layout_break_cycles.d.ts +34 -0
  159. package/src/core/model/node-graph/visual/layout/layout_break_cycles.d.ts.map +1 -0
  160. package/src/core/model/node-graph/visual/layout/layout_break_cycles.js +254 -0
  161. package/src/core/model/node-graph/visual/layout/layout_build_problem.d.ts +29 -0
  162. package/src/core/model/node-graph/visual/layout/layout_build_problem.d.ts.map +1 -0
  163. package/src/core/model/node-graph/visual/layout/layout_build_problem.js +184 -0
  164. package/src/core/model/node-graph/visual/layout/layout_insert_lanes.d.ts +29 -0
  165. package/src/core/model/node-graph/visual/layout/layout_insert_lanes.d.ts.map +1 -0
  166. package/src/core/model/node-graph/visual/layout/layout_insert_lanes.js +95 -0
  167. package/src/core/model/node-graph/visual/layout/layout_node_graph.d.ts +56 -0
  168. package/src/core/model/node-graph/visual/layout/layout_node_graph.d.ts.map +1 -0
  169. package/src/core/model/node-graph/visual/layout/layout_node_graph.js +131 -0
  170. package/src/core/model/node-graph/visual/layout/layout_order_layers.d.ts +36 -0
  171. package/src/core/model/node-graph/visual/layout/layout_order_layers.d.ts.map +1 -0
  172. package/src/core/model/node-graph/visual/layout/layout_order_layers.js +415 -0
  173. package/src/core/model/node-graph/visual/layout/layout_orient_links.d.ts +21 -0
  174. package/src/core/model/node-graph/visual/layout/layout_orient_links.d.ts.map +1 -0
  175. package/src/core/model/node-graph/visual/layout/layout_orient_links.js +49 -0
  176. package/src/core/model/node-graph/visual/layout/route_connections.d.ts +41 -0
  177. package/src/core/model/node-graph/visual/layout/route_connections.d.ts.map +1 -0
  178. package/src/core/model/node-graph/visual/layout/route_connections.js +309 -0
  179. package/src/core/model/node-graph/visual/layout/route_grid_build.d.ts +33 -0
  180. package/src/core/model/node-graph/visual/layout/route_grid_build.d.ts.map +1 -0
  181. package/src/core/model/node-graph/visual/layout/route_grid_build.js +237 -0
  182. package/src/core/model/node-graph/visual/layout/route_nudge.d.ts +25 -0
  183. package/src/core/model/node-graph/visual/layout/route_nudge.d.ts.map +1 -0
  184. package/src/core/model/node-graph/visual/layout/route_nudge.js +220 -0
  185. package/src/core/model/node-graph/visual/layout/route_to_spline.d.ts +29 -0
  186. package/src/core/model/node-graph/visual/layout/route_to_spline.d.ts.map +1 -0
  187. package/src/core/model/node-graph/visual/layout/route_to_spline.js +120 -0
  188. package/src/engine/EngineHarness.d.ts.map +1 -1
  189. package/src/engine/EngineHarness.js +593 -586
  190. package/src/engine/EntityCreator.js +116 -116
  191. package/src/engine/achievements/AchievementManager.js +437 -437
  192. package/src/engine/animation/AnimatedActions.js +111 -111
  193. package/src/engine/animation/AnimationUtils.d.ts.map +1 -1
  194. package/src/engine/animation/AnimationUtils.js +246 -234
  195. package/src/engine/animation/Animations.d.ts +1 -0
  196. package/src/engine/animation/Animations.d.ts.map +1 -1
  197. package/src/engine/animation/Animations.js +13 -5
  198. package/src/engine/animation/clip/bind_property_writer.d.ts.map +1 -1
  199. package/src/engine/animation/clip/bind_property_writer.js +12 -5
  200. package/src/engine/animation/curve/binding/BoundTransform64Writer.d.ts +29 -0
  201. package/src/engine/animation/curve/binding/BoundTransform64Writer.d.ts.map +1 -0
  202. package/src/engine/animation/curve/binding/BoundTransform64Writer.js +80 -0
  203. package/src/engine/animation/removeEntityWithMeshParticlesEffect.js +4 -4
  204. package/src/engine/asset/loaders/gltf_test_fixtures.d.ts.map +1 -1
  205. package/src/engine/asset/loaders/gltf_test_fixtures.js +5 -4
  206. package/src/engine/control/first-person/FirstPersonPlayerControllerSystem.d.ts +12 -12
  207. package/src/engine/control/first-person/FirstPersonPlayerControllerSystem.d.ts.map +1 -1
  208. package/src/engine/control/first-person/FirstPersonPlayerControllerSystem.js +77 -44
  209. package/src/engine/control/first-person/abilities/LedgeGrab.js +1 -1
  210. package/src/engine/control/first-person/abilities/Mantle.js +1 -1
  211. package/src/engine/control/first-person/abilities/Slide.js +1 -1
  212. package/src/engine/control/first-person/collision/KinematicMover.d.ts +5 -9
  213. package/src/engine/control/first-person/collision/KinematicMover.d.ts.map +1 -1
  214. package/src/engine/control/first-person/collision/KinematicMover.js +59 -29
  215. package/src/engine/control/first-person/sensors/FirstPersonSensorsSystem.d.ts +2 -2
  216. package/src/engine/control/first-person/sensors/FirstPersonSensorsSystem.d.ts.map +1 -1
  217. package/src/engine/control/first-person/sensors/FirstPersonSensorsSystem.js +7 -7
  218. package/src/engine/ecs/EventType.d.ts +1 -0
  219. package/src/engine/ecs/EventType.js +35 -10
  220. package/src/engine/ecs/dynamic_actions/actions/definition/SendRequestActionDescription.d.ts.map +1 -1
  221. package/src/engine/ecs/dynamic_actions/actions/definition/SendRequestActionDescription.js +12 -4
  222. package/src/engine/ecs/fow/FogOfWarRevealerSystem.d.ts +4 -4
  223. package/src/engine/ecs/fow/FogOfWarRevealerSystem.d.ts.map +1 -1
  224. package/src/engine/ecs/fow/FogOfWarRevealerSystem.js +10 -11
  225. package/src/engine/ecs/grid/pick.js +1 -1
  226. package/src/engine/ecs/gui/hud/HeadsUpDisplaySystem.d.ts +1 -1
  227. package/src/engine/ecs/gui/hud/HeadsUpDisplaySystem.js +13 -13
  228. package/src/engine/ecs/gui/hud/testHudEdgeStick.js +3 -3
  229. package/src/engine/ecs/ik/InverseKinematicsSystem.d.ts +10 -10
  230. package/src/engine/ecs/ik/InverseKinematicsSystem.d.ts.map +1 -1
  231. package/src/engine/ecs/ik/InverseKinematicsSystem.js +8 -8
  232. package/src/engine/ecs/ik/TwoBoneInverseKinematicsSolver.d.ts.map +1 -1
  233. package/src/engine/ecs/ik/TwoBoneInverseKinematicsSolver.js +120 -97
  234. package/src/engine/ecs/ik/ik_bone_chain.d.ts +16 -5
  235. package/src/engine/ecs/ik/ik_bone_chain.d.ts.map +1 -1
  236. package/src/engine/ecs/ik/ik_bone_chain.js +16 -4
  237. package/src/engine/ecs/ik/ik_probe_terrain_contact.d.ts +7 -3
  238. package/src/engine/ecs/ik/ik_probe_terrain_contact.d.ts.map +1 -1
  239. package/src/engine/ecs/ik/ik_probe_terrain_contact.js +20 -16
  240. package/src/engine/ecs/ik/ik_write_local_rotation.d.ts +18 -0
  241. package/src/engine/ecs/ik/ik_write_local_rotation.d.ts.map +1 -0
  242. package/src/engine/ecs/ik/ik_write_local_rotation.js +30 -0
  243. package/src/engine/ecs/parent/EntityNode.d.ts +24 -6
  244. package/src/engine/ecs/parent/EntityNode.d.ts.map +1 -1
  245. package/src/engine/ecs/parent/EntityNode.js +54 -42
  246. package/src/engine/ecs/parent/EntityNodeFlags.d.ts +0 -1
  247. package/src/engine/ecs/parent/EntityNodeFlags.js +7 -8
  248. package/src/engine/ecs/speaker/VoiceSystem.d.ts +2 -2
  249. package/src/engine/ecs/speaker/VoiceSystem.d.ts.map +1 -1
  250. package/src/engine/ecs/speaker/VoiceSystem.js +4 -4
  251. package/src/engine/ecs/storage/populateEngineSerializationRegistry.d.ts.map +1 -1
  252. package/src/engine/ecs/storage/populateEngineSerializationRegistry.js +2 -0
  253. package/src/engine/ecs/terrain/ecs/Terrain.d.ts +5 -2
  254. package/src/engine/ecs/terrain/ecs/Terrain.d.ts.map +1 -1
  255. package/src/engine/ecs/terrain/ecs/Terrain.js +7 -4
  256. package/src/engine/ecs/terrain/ecs/TerrainSystem.js +3 -3
  257. package/src/engine/ecs/terrain/ecs/cling/ClingToTerrainSystem.d.ts +7 -7
  258. package/src/engine/ecs/terrain/ecs/cling/ClingToTerrainSystem.d.ts.map +1 -1
  259. package/src/engine/ecs/terrain/ecs/cling/ClingToTerrainSystem.js +84 -36
  260. package/src/engine/ecs/transform/TRANSFORM64_MIGRATION_PLAN_2026_09_04.md +513 -0
  261. package/src/engine/ecs/transform/Transform64.d.ts +48 -11
  262. package/src/engine/ecs/transform/Transform64.d.ts.map +1 -1
  263. package/src/engine/ecs/transform/Transform64.js +54 -15
  264. package/src/engine/ecs/transform/Transform64SerializationAdapter.d.ts +37 -0
  265. package/src/engine/ecs/transform/Transform64SerializationAdapter.d.ts.map +1 -0
  266. package/src/engine/ecs/transform/Transform64SerializationAdapter.js +75 -0
  267. package/src/engine/ecs/transform/__probe.d.ts +4 -0
  268. package/src/engine/ecs/transform/__probe.d.ts.map +1 -0
  269. package/src/engine/ecs/transform/t64_allocation_probe.d.ts +31 -0
  270. package/src/engine/ecs/transform/t64_allocation_probe.d.ts.map +1 -0
  271. package/src/engine/ecs/transform/t64_allocation_probe.js +78 -0
  272. package/src/engine/ecs/transform/t64_announce_change.d.ts +33 -0
  273. package/src/engine/ecs/transform/t64_announce_change.d.ts.map +1 -0
  274. package/src/engine/ecs/transform/t64_announce_change.js +39 -0
  275. package/src/engine/ecs/transform/t64_equals_components.d.ts +30 -0
  276. package/src/engine/ecs/transform/t64_equals_components.d.ts.map +1 -0
  277. package/src/engine/ecs/transform/t64_equals_components.js +59 -0
  278. package/src/engine/ecs/transform/t64_read_rotation.d.ts +13 -0
  279. package/src/engine/ecs/transform/t64_read_rotation.d.ts.map +1 -0
  280. package/src/engine/ecs/transform/t64_read_rotation.js +16 -0
  281. package/src/engine/ecs/transform/t64_read_translation.d.ts +14 -0
  282. package/src/engine/ecs/transform/t64_read_translation.d.ts.map +1 -0
  283. package/src/engine/ecs/transform/t64_read_translation.js +17 -0
  284. package/src/engine/ecs/transform-attachment/TransformAttachment.d.ts +5 -5
  285. package/src/engine/ecs/transform-attachment/TransformAttachment.d.ts.map +1 -1
  286. package/src/engine/ecs/transform-attachment/TransformAttachment.js +4 -4
  287. package/src/engine/ecs/transform-attachment/TransformAttachmentSystem.d.ts +10 -10
  288. package/src/engine/ecs/transform-attachment/TransformAttachmentSystem.d.ts.map +1 -1
  289. package/src/engine/ecs/transform-attachment/TransformAttachmentSystem.js +71 -20
  290. package/src/engine/graphics/debug/createDebugLabel.js +3 -3
  291. package/src/engine/graphics/ecs/camera/pp/PerfectPanner.d.ts.map +1 -1
  292. package/src/engine/graphics/ecs/camera/pp/PerfectPanner.js +5 -4
  293. package/src/engine/graphics/ecs/camera/topdown/TopDownCameraController.d.ts.map +1 -1
  294. package/src/engine/graphics/ecs/camera/topdown/TopDownCameraController.js +8 -3
  295. package/src/engine/graphics/ecs/camera/topdown/TopDownCameraControllerSystem.d.ts +6 -6
  296. package/src/engine/graphics/ecs/camera/topdown/TopDownCameraControllerSystem.d.ts.map +1 -1
  297. package/src/engine/graphics/ecs/camera/topdown/TopDownCameraControllerSystem.js +25 -13
  298. package/src/engine/graphics/ecs/camera/topdown/TopDownCameraLanderSystem.js +1 -1
  299. package/src/engine/graphics/ecs/mesh/setMesh.js +2 -2
  300. package/src/engine/graphics/ecs/path/entity/EntityPath.d.ts.map +1 -1
  301. package/src/engine/graphics/ecs/path/entity/EntityPath.js +21 -7
  302. package/src/engine/graphics/ecs/path/entity/EntityPathMarkerDefinition.d.ts +4 -4
  303. package/src/engine/graphics/ecs/path/entity/EntityPathMarkerDefinition.d.ts.map +1 -1
  304. package/src/engine/graphics/ecs/path/entity/EntityPathMarkerDefinition.js +4 -4
  305. package/src/engine/graphics/ecs/sprite/SpriteSystemPE.d.ts +2 -2
  306. package/src/engine/graphics/ecs/sprite/SpriteSystemPE.d.ts.map +1 -1
  307. package/src/engine/graphics/ecs/sprite/SpriteSystemPE.js +145 -135
  308. package/src/engine/graphics/geometry/MikkT/Build4RuleGroups.js +2 -2
  309. package/src/engine/graphics/geometry/MikkT/DegenEpilogue.js +1 -1
  310. package/src/engine/graphics/geometry/MikkT/DegenPrologue.js +1 -1
  311. package/src/engine/graphics/geometry/MikkT/EvalTspace.js +1 -1
  312. package/src/engine/graphics/geometry/MikkT/GenerateTSpaces.js +3 -3
  313. package/src/engine/graphics/render/frame_graph/CORRECTNESS_FIXES_2026_09_04.md +659 -0
  314. package/src/engine/graphics/render/frame_graph/FRAME_GRAPH_RECOMMENDATIONS.md +21 -5
  315. package/src/engine/graphics/render/frame_graph/FrameGraph.d.ts +45 -0
  316. package/src/engine/graphics/render/frame_graph/FrameGraph.d.ts.map +1 -1
  317. package/src/engine/graphics/render/frame_graph/FrameGraph.js +282 -60
  318. package/src/engine/graphics/render/frame_graph/FramePassBuilder.d.ts.map +1 -1
  319. package/src/engine/graphics/render/frame_graph/FramePassBuilder.js +20 -9
  320. package/src/engine/graphics/render/frame_graph/FramePassNode.d.ts +12 -0
  321. package/src/engine/graphics/render/frame_graph/FramePassNode.d.ts.map +1 -1
  322. package/src/engine/graphics/render/frame_graph/FramePassNode.js +13 -0
  323. package/src/engine/graphics/render/frame_graph/FramePassResources.d.ts.map +1 -1
  324. package/src/engine/graphics/render/frame_graph/FramePassResources.js +5 -0
  325. package/src/engine/graphics/render/frame_graph/ResourceEntry.d.ts +19 -1
  326. package/src/engine/graphics/render/frame_graph/ResourceEntry.d.ts.map +1 -1
  327. package/src/engine/graphics/render/frame_graph/ResourceEntry.js +23 -1
  328. package/src/engine/graphics/render/frame_graph/backend/void/VoidRenderContext.d.ts +15 -1
  329. package/src/engine/graphics/render/frame_graph/backend/void/VoidRenderContext.d.ts.map +1 -1
  330. package/src/engine/graphics/render/frame_graph/backend/void/VoidRenderContext.js +33 -16
  331. package/src/engine/graphics/render/frame_graph/resource/RenderResourceManager.d.ts.map +1 -1
  332. package/src/engine/graphics/render/frame_graph/resource/RenderResourceManager.js +19 -1
  333. package/src/engine/graphics/sh3/path_tracer/populate_path_traced_scene_from_ecd.js +6 -6
  334. package/src/engine/graphics3/CameraSystem.d.ts +6 -6
  335. package/src/engine/graphics3/CameraSystem.d.ts.map +1 -1
  336. package/src/engine/graphics3/CameraSystem.js +170 -170
  337. package/src/engine/graphics3/DecalSystem.d.ts +6 -6
  338. package/src/engine/graphics3/DecalSystem.d.ts.map +1 -1
  339. package/src/engine/graphics3/DecalSystem.js +449 -449
  340. package/src/engine/graphics3/LightSystem.d.ts +6 -6
  341. package/src/engine/graphics3/LightSystem.d.ts.map +1 -1
  342. package/src/engine/graphics3/LightSystem.js +234 -221
  343. package/src/engine/graphics3/MeshSystem.d.ts +6 -6
  344. package/src/engine/graphics3/MeshSystem.d.ts.map +1 -1
  345. package/src/engine/graphics3/MeshSystem.js +53 -22
  346. package/src/engine/graphics3/ParticipatingMediaSystem.d.ts +20 -15
  347. package/src/engine/graphics3/ParticipatingMediaSystem.d.ts.map +1 -1
  348. package/src/engine/graphics3/ParticipatingMediaSystem.js +198 -184
  349. package/src/engine/graphics3/ParticleEmitterSystem.d.ts +6 -6
  350. package/src/engine/graphics3/ParticleEmitterSystem.d.ts.map +1 -1
  351. package/src/engine/graphics3/ParticleEmitterSystem.js +310 -289
  352. package/src/engine/graphics3/PickingSystem.d.ts +6 -6
  353. package/src/engine/graphics3/PickingSystem.d.ts.map +1 -1
  354. package/src/engine/graphics3/PickingSystem.js +273 -273
  355. package/src/engine/graphics3/ShadedGeometrySystem.d.ts +6 -6
  356. package/src/engine/graphics3/ShadedGeometrySystem.d.ts.map +1 -1
  357. package/src/engine/graphics3/ShadedGeometrySystem.js +180 -169
  358. package/src/engine/graphics3/TerrainSystem.d.ts.map +1 -1
  359. package/src/engine/graphics3/TerrainSystem.js +565 -566
  360. package/src/engine/graphics3/Trail3DSystem.d.ts +6 -6
  361. package/src/engine/graphics3/Trail3DSystem.d.ts.map +1 -1
  362. package/src/engine/graphics3/Trail3DSystem.js +221 -221
  363. package/src/engine/graphics3/camera_sync_from_transform.d.ts +2 -2
  364. package/src/engine/graphics3/camera_sync_from_transform.d.ts.map +1 -1
  365. package/src/engine/graphics3/camera_sync_from_transform.js +2 -1
  366. package/src/engine/graphics3/debug/gizmo_view_from_camera.js +4 -4
  367. package/src/engine/graphics3/particles/GPUParticularRenderer.d.ts +6 -0
  368. package/src/engine/graphics3/particles/GPUParticularRenderer.d.ts.map +1 -1
  369. package/src/engine/graphics3/particles/GPUParticularRenderer.js +82 -53
  370. package/src/engine/graphics3/particles/particle_gpu_records.d.ts +12 -8
  371. package/src/engine/graphics3/particles/particle_gpu_records.d.ts.map +1 -1
  372. package/src/engine/graphics3/particles/particle_gpu_records.js +53 -45
  373. package/src/engine/graphics3/preview/make_model_preview_scene.js +1 -1
  374. package/src/engine/graphics3/shade_camera_projection_ray.js +4 -4
  375. package/src/engine/graphics3/shade_node_to_entity_composition.d.ts.map +1 -1
  376. package/src/engine/graphics3/shade_node_to_entity_composition.js +88 -84
  377. package/src/engine/grid/grid2transform/GridPosition2TransformSystem.d.ts +7 -7
  378. package/src/engine/grid/grid2transform/GridPosition2TransformSystem.d.ts.map +1 -1
  379. package/src/engine/grid/grid2transform/GridPosition2TransformSystem.js +8 -8
  380. package/src/engine/grid/transform2grid/Transform2GridPositionSystem.d.ts +9 -8
  381. package/src/engine/grid/transform2grid/Transform2GridPositionSystem.d.ts.map +1 -1
  382. package/src/engine/grid/transform2grid/Transform2GridPositionSystem.js +29 -8
  383. package/src/engine/input/ecs/controllers/KeyboardCameraController.js +2 -2
  384. package/src/engine/intelligence/behavior/ecs/OrbitingBehavior.d.ts.map +1 -1
  385. package/src/engine/intelligence/behavior/ecs/OrbitingBehavior.js +35 -21
  386. package/src/engine/intelligence/behavior/util/RotationBehavior.js +5 -5
  387. package/src/engine/intelligence/mcts/StateNode.js +1 -1
  388. package/src/engine/interpolation/TransformPoseSerializationAdapter.d.ts +7 -7
  389. package/src/engine/interpolation/TransformPoseSerializationAdapter.d.ts.map +1 -1
  390. package/src/engine/interpolation/TransformPoseSerializationAdapter.js +14 -15
  391. package/src/engine/interpolation/pose_interpoland.d.ts +2 -2
  392. package/src/engine/interpolation/pose_interpoland.js +4 -4
  393. package/src/engine/navigation/ecs/path_following/PathFollowingSystem.d.ts +4 -4
  394. package/src/engine/navigation/ecs/path_following/PathFollowingSystem.d.ts.map +1 -1
  395. package/src/engine/navigation/ecs/path_following/PathFollowingSystem.js +39 -14
  396. package/src/engine/network/adapters/TransformReplicationAdapter.d.ts +7 -7
  397. package/src/engine/network/adapters/TransformReplicationAdapter.d.ts.map +1 -1
  398. package/src/engine/network/adapters/TransformReplicationAdapter.js +19 -20
  399. package/src/engine/network/replication/Replicator.d.ts +5 -3
  400. package/src/engine/network/replication/Replicator.d.ts.map +1 -1
  401. package/src/engine/network/replication/Replicator.js +65 -16
  402. package/src/engine/physics/broadphase/compute_fat_world_aabb.js +3 -3
  403. package/src/engine/physics/ccd/linear_sweep.d.ts.map +1 -1
  404. package/src/engine/physics/ccd/linear_sweep.js +9 -3
  405. package/src/engine/physics/constraint/solve_constraints.js +10 -10
  406. package/src/engine/physics/ecs/ColliderObserverSystem.d.ts +8 -8
  407. package/src/engine/physics/ecs/ColliderObserverSystem.d.ts.map +1 -1
  408. package/src/engine/physics/ecs/ColliderObserverSystem.js +6 -6
  409. package/src/engine/physics/ecs/PhysicsSystem.d.ts +22 -32
  410. package/src/engine/physics/ecs/PhysicsSystem.d.ts.map +1 -1
  411. package/src/engine/physics/ecs/PhysicsSystem.js +2337 -2310
  412. package/src/engine/physics/ecs/find_non_finite_physics_state.js +6 -6
  413. package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +7 -7
  414. package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts.map +1 -1
  415. package/src/engine/physics/fluid/ecs/FluidObstacleSystem.js +16 -8
  416. package/src/engine/physics/fluid/ecs/FluidSystem.d.ts +4 -4
  417. package/src/engine/physics/fluid/ecs/FluidSystem.d.ts.map +1 -1
  418. package/src/engine/physics/fluid/ecs/FluidSystem.js +6 -6
  419. package/src/engine/physics/fluid/ecs/WorkerFluidSystem.d.ts +2 -2
  420. package/src/engine/physics/fluid/ecs/WorkerFluidSystem.d.ts.map +1 -1
  421. package/src/engine/physics/fluid/ecs/WorkerFluidSystem.js +4 -4
  422. package/src/engine/physics/fluid/ecs/fluid_reanchor_field.js +7 -7
  423. package/src/engine/physics/fluid/effector/ImpulseFluidEffector.js +4 -4
  424. package/src/engine/physics/fluid/effector/WakeFluidEffector.js +7 -7
  425. package/src/engine/physics/inertia/world_inverse_inertia.js +1 -1
  426. package/src/engine/physics/integration/integrate_position.d.ts.map +1 -1
  427. package/src/engine/physics/integration/integrate_position.js +13 -12
  428. package/src/engine/physics/inverse_kinematics/fabrik/fabrik_solve.d.ts.map +1 -1
  429. package/src/engine/physics/inverse_kinematics/fabrik/fabrik_solve.js +23 -5
  430. package/src/engine/physics/narrowphase/compute_penetration.js +18 -14
  431. package/src/engine/physics/narrowphase/convex_convex_manifold.js +2 -2
  432. package/src/engine/physics/narrowphase/mesh_mesh_tet_manifold.js +9 -9
  433. package/src/engine/physics/narrowphase/narrowphase_step.d.ts.map +1 -1
  434. package/src/engine/physics/narrowphase/narrowphase_step.js +95 -68
  435. package/src/engine/physics/narrowphase/refine_ray_concave.js +1 -1
  436. package/src/engine/physics/narrowphase/refine_ray_hit.js +3 -3
  437. package/src/engine/physics/queries/overlap_shape.d.ts.map +1 -1
  438. package/src/engine/physics/queries/overlap_shape.js +19 -7
  439. package/src/engine/physics/queries/raycast.js +3 -3
  440. package/src/engine/physics/queries/shape_cast.d.ts.map +1 -1
  441. package/src/engine/physics/queries/shape_cast.js +628 -620
  442. package/src/engine/physics/solver/solve_contacts.js +10 -10
  443. package/src/engine/physics/vehicle/RaycastVehicle.d.ts +6 -5
  444. package/src/engine/physics/vehicle/RaycastVehicle.d.ts.map +1 -1
  445. package/src/engine/physics/vehicle/RaycastVehicle.js +10 -5
  446. package/src/engine/sound/ecs/SoundListenerSystem.d.ts +16 -4
  447. package/src/engine/sound/ecs/SoundListenerSystem.d.ts.map +1 -1
  448. package/src/engine/sound/ecs/SoundListenerSystem.js +70 -11
  449. package/src/engine/sound/ecs/audio/AudioEmitterSystem.d.ts +7 -7
  450. package/src/engine/sound/ecs/audio/AudioEmitterSystem.d.ts.map +1 -1
  451. package/src/engine/sound/ecs/audio/AudioEmitterSystem.js +343 -338
  452. package/src/engine/sound/ecs/audio/LiveEmitterSet.d.ts +2 -2
  453. package/src/engine/sound/ecs/audio/LiveEmitterSet.d.ts.map +1 -1
  454. package/src/engine/sound/ecs/audio/LiveEmitterSet.js +356 -345
  455. package/src/engine/sound/ecs/audio/SpatialAudioIndex.d.ts +23 -2
  456. package/src/engine/sound/ecs/audio/SpatialAudioIndex.d.ts.map +1 -1
  457. package/src/engine/sound/ecs/audio/SpatialAudioIndex.js +174 -140
  458. package/src/engine/sound/ecs/trigger/AudioEventTriggerSystem.d.ts +6 -6
  459. package/src/engine/sound/ecs/trigger/AudioEventTriggerSystem.d.ts.map +1 -1
  460. package/src/engine/sound/ecs/trigger/AudioEventTriggerSystem.js +185 -184
  461. package/src/engine/sound/material/detector/terrain/TerrainSoundMaterialSurfaceDetector.js +1 -1
  462. package/src/engine/sound/simulation/core/AcousticRegionIndex.d.ts +2 -1
  463. package/src/engine/sound/simulation/core/AcousticRegionIndex.d.ts.map +1 -1
  464. package/src/engine/sound/simulation/core/AcousticRegionIndex.js +4 -3
  465. package/src/engine/sound/simulation/core/AcousticVolumeRegion.d.ts +3 -2
  466. package/src/engine/sound/simulation/core/AcousticVolumeRegion.d.ts.map +1 -1
  467. package/src/engine/sound/simulation/core/AcousticVolumeRegion.js +6 -4
  468. package/src/engine/sound/simulation/ecs/AcousticSimulationSystem.d.ts +10 -6
  469. package/src/engine/sound/simulation/ecs/AcousticSimulationSystem.d.ts.map +1 -1
  470. package/src/engine/sound/simulation/ecs/AcousticSimulationSystem.js +32 -7
  471. package/src/engine/sound/simulation/ecs/AcousticVolumeSystem.d.ts +7 -7
  472. package/src/engine/sound/simulation/ecs/AcousticVolumeSystem.d.ts.map +1 -1
  473. package/src/engine/sound/simulation/ecs/AcousticVolumeSystem.js +6 -6
  474. package/src/engine/sound/sopra/runtime/EventInstance.d.ts.map +1 -1
  475. package/src/engine/sound/sopra/runtime/EventInstance.js +8 -2
  476. package/src/format/scene/gltf/gltf_component_type.d.ts.map +1 -1
  477. package/src/format/scene/gltf/gltf_component_type.js +3 -1
  478. package/src/format/texture/ktx2/ktx2_read.d.ts.map +1 -1
  479. package/src/format/texture/ktx2/ktx2_read.js +2 -1
  480. package/src/generation/grid/generation/GridTaskDensityMarkerDistribution.d.ts.map +1 -1
  481. package/src/generation/grid/generation/GridTaskDensityMarkerDistribution.js +8 -1
  482. package/src/generation/markers/GridCellActionPlaceMarker.d.ts +4 -4
  483. package/src/generation/markers/GridCellActionPlaceMarker.d.ts.map +1 -1
  484. package/src/generation/markers/GridCellActionPlaceMarker.js +16 -6
  485. package/src/generation/markers/MarkerNode.d.ts +3 -3
  486. package/src/generation/markers/MarkerNode.d.ts.map +1 -1
  487. package/src/generation/markers/MarkerNode.js +3 -3
  488. package/src/generation/markers/actions/MarkerNodeActionEntityPlacement.d.ts +4 -4
  489. package/src/generation/markers/actions/MarkerNodeActionEntityPlacement.d.ts.map +1 -1
  490. package/src/generation/markers/actions/MarkerNodeActionEntityPlacement.js +7 -7
  491. package/src/generation/markers/actions/MarkerNodeActionImaginary.d.ts.map +1 -1
  492. package/src/generation/markers/actions/MarkerNodeActionImaginary.js +20 -2
  493. package/src/generation/markers/actions/placement/MarkerNodeEntityProcessorClingToTerrain.js +1 -1
  494. package/src/generation/markers/actions/placement/MarkerNodeEntityProcessorRandomRotation.d.ts.map +1 -1
  495. package/src/generation/markers/actions/placement/MarkerNodeEntityProcessorRandomRotation.js +13 -3
  496. package/src/generation/markers/transform/MarkerNodeTransformRotateRandom.d.ts.map +1 -1
  497. package/src/generation/markers/transform/MarkerNodeTransformRotateRandom.js +11 -1
  498. package/src/generation/markers/transform/MarkerNodeTransformerAddPositionYFromFilter.d.ts.map +1 -1
  499. package/src/generation/markers/transform/MarkerNodeTransformerAddPositionYFromFilter.js +8 -4
  500. package/src/generation/markers/transform/MarkerNodeTransformerOffsetPosition.d.ts.map +1 -1
  501. package/src/generation/markers/transform/MarkerNodeTransformerOffsetPosition.js +13 -5
  502. package/src/generation/markers/transform/MarkerNodeTransformerYRotateByFilter.d.ts.map +1 -1
  503. package/src/generation/markers/transform/MarkerNodeTransformerYRotateByFilter.js +8 -2
  504. package/src/generation/markers/transform/MarkerNodeTransformerYRotateByFilterGradient.d.ts.map +1 -1
  505. package/src/generation/markers/transform/MarkerNodeTransformerYRotateByFilterGradient.js +16 -2
  506. package/src/shade/device/graph/BufferResourceDescriptor.d.ts.map +1 -1
  507. package/src/shade/device/graph/BufferResourceDescriptor.js +193 -184
  508. package/src/shade/device/graph/TextureResourceDescriptor.d.ts.map +1 -1
  509. package/src/shade/device/graph/TextureResourceDescriptor.js +12 -0
  510. package/src/shade/device/mock/SoftwareGPUDevice.d.ts.map +1 -1
  511. package/src/shade/device/mock/SoftwareGPUDevice.js +8 -0
  512. package/src/shade/playground/avboit/main.js +3 -3
  513. package/src/shade/playground/basis_textures/main.js +1 -1
  514. package/src/shade/playground/ground_seam/main.js +1 -1
  515. package/src/shade/playground/particle_editor/README.md +175 -0
  516. package/src/shade/playground/particle_editor/index.html +76 -0
  517. package/src/shade/playground/particle_editor/main.d.ts +2 -0
  518. package/src/shade/playground/particle_editor/main.d.ts.map +1 -0
  519. package/src/shade/playground/particle_editor/main.js +139 -0
  520. package/src/shade/playground/particle_system/README.md +92 -62
  521. package/src/shade/playground/particle_system/index.html +67 -21
  522. package/src/shade/playground/particle_system/main.js +247 -102
  523. package/src/shade/{renderer/particles → playground/particle_system}/particle_prototype.d.ts +21 -18
  524. package/src/shade/playground/particle_system/particle_prototype.d.ts.map +1 -0
  525. package/src/shade/{renderer/particles → playground/particle_system}/particle_prototype.js +197 -181
  526. package/src/shade/playground/particle_system/particle_scene.d.ts +136 -20
  527. package/src/shade/playground/particle_system/particle_scene.d.ts.map +1 -1
  528. package/src/shade/playground/particle_system/particle_scene.js +487 -670
  529. package/src/shade/playground/skinned_blas_refit/sweep_views.js +2 -2
  530. package/src/shade/playground/skinned_blas_refit/verify_instance_bounds.d.ts.map +1 -1
  531. package/src/shade/playground/skinned_blas_refit/verify_instance_bounds.js +2 -1
  532. package/src/shade/playground/skinned_blas_refit/verify_skinned_blas_refit.d.ts.map +1 -1
  533. package/src/shade/playground/skinned_blas_refit/verify_skinned_blas_refit.js +2 -1
  534. package/src/shade/playground/sponza_path_trace/main.js +1 -1
  535. package/src/shade/playground/volumetrics_froxel/main.js +917 -917
  536. package/src/shade/playground/volumetrics_froxel/measure.js +559 -559
  537. package/src/shade/renderer/Renderer.d.ts +28 -0
  538. package/src/shade/renderer/Renderer.d.ts.map +1 -1
  539. package/src/shade/renderer/Renderer.js +140 -8
  540. package/src/shade/renderer/atmosphere/GPUSky.d.ts.map +1 -1
  541. package/src/shade/renderer/atmosphere/GPUSky.js +4 -2
  542. package/src/shade/renderer/buffer/graph_read_buffer.d.ts.map +1 -1
  543. package/src/shade/renderer/buffer/graph_read_buffer.js +8 -1
  544. package/src/shade/renderer/buffer/table/GPUTypedTable.js +1 -1
  545. package/src/shade/renderer/buffer/table/GPUTypedTableDescriptor.d.ts +30 -0
  546. package/src/shade/renderer/buffer/table/GPUTypedTableDescriptor.d.ts.map +1 -1
  547. package/src/shade/renderer/buffer/table/GPUTypedTableDescriptor.js +84 -0
  548. package/src/shade/renderer/buffer/table/gpu_database_emulator_image.d.ts +27 -0
  549. package/src/shade/renderer/buffer/table/gpu_database_emulator_image.d.ts.map +1 -0
  550. package/src/shade/renderer/buffer/table/gpu_database_words.d.ts +24 -0
  551. package/src/shade/renderer/buffer/table/gpu_database_words.d.ts.map +1 -0
  552. package/src/shade/renderer/buffer/table/gpu_database_words.js +92 -0
  553. package/src/shade/renderer/camera/Camera.d.ts +2 -2
  554. package/src/shade/renderer/camera/Camera.d.ts.map +1 -1
  555. package/src/shade/renderer/camera/Camera.js +11 -5
  556. package/src/shade/renderer/camera/GPUCameraContext.d.ts.map +1 -1
  557. package/src/shade/renderer/camera/GPUCameraContext.js +2 -1
  558. package/src/shade/renderer/camera/OrbitalCameraController.d.ts +3 -3
  559. package/src/shade/renderer/camera/OrbitalCameraController.d.ts.map +1 -1
  560. package/src/shade/renderer/camera/OrbitalCameraController.js +31 -16
  561. package/src/shade/renderer/camera/orbital/OrbitalCameraController.d.ts +2 -2
  562. package/src/shade/renderer/camera/orbital/OrbitalCameraController.d.ts.map +1 -1
  563. package/src/shade/renderer/camera/orbital/OrbitalCameraController.js +4 -3
  564. package/src/shade/renderer/camera/orbital/OrbitalCameraRig.d.ts +10 -5
  565. package/src/shade/renderer/camera/orbital/OrbitalCameraRig.d.ts.map +1 -1
  566. package/src/shade/renderer/camera/orbital/OrbitalCameraRig.js +8 -4
  567. package/src/shade/renderer/extension/FrameContext.d.ts.map +1 -1
  568. package/src/shade/renderer/extension/FrameContext.js +5 -0
  569. package/src/shade/renderer/extension/FrameRecord.d.ts +16 -0
  570. package/src/shade/renderer/extension/FrameRecord.d.ts.map +1 -1
  571. package/src/shade/renderer/extension/FrameRecord.js +31 -0
  572. package/src/shade/renderer/extension/GBufferTextures.d.ts +11 -0
  573. package/src/shade/renderer/extension/GBufferTextures.d.ts.map +1 -1
  574. package/src/shade/renderer/extension/GBufferTextures.js +14 -0
  575. package/src/shade/renderer/geometry/attribute/gpu_vertex_buffer_layout_from_spec.js +1 -1
  576. package/src/shade/renderer/geometry/util/geometry_stripify.js +6 -6
  577. package/src/shade/renderer/geometry/virtual/format/prototypeVGEOFormatViewer.js +8 -8
  578. package/src/shade/renderer/global_illumination/lpv/lpv_visualise_probes.d.ts.map +1 -1
  579. package/src/shade/renderer/global_illumination/lpv/lpv_visualise_probes.js +14 -5
  580. package/src/shade/renderer/global_illumination/probe/octahedral/GPUProbeAtlas.d.ts.map +1 -1
  581. package/src/shade/renderer/global_illumination/probe/octahedral/GPUProbeAtlas.js +17 -9
  582. package/src/shade/renderer/global_illumination/probe/octahedral/border/graph_lpv_paint_border_depth.d.ts +2 -1
  583. package/src/shade/renderer/global_illumination/probe/octahedral/border/graph_lpv_paint_border_depth.d.ts.map +1 -1
  584. package/src/shade/renderer/global_illumination/probe/octahedral/border/graph_lpv_paint_border_depth.js +4 -1
  585. package/src/shade/renderer/global_illumination/probe/octahedral/convolve/graph_lvp_atlas_to_sh.d.ts +2 -1
  586. package/src/shade/renderer/global_illumination/probe/octahedral/convolve/graph_lvp_atlas_to_sh.d.ts.map +1 -1
  587. package/src/shade/renderer/global_illumination/probe/octahedral/convolve/graph_lvp_atlas_to_sh.js +11 -1
  588. package/src/shade/renderer/global_illumination/probe/octahedral/graph_lpv_shade.d.ts +1 -1
  589. package/src/shade/renderer/global_illumination/probe/octahedral/graph_lpv_shade.d.ts.map +1 -1
  590. package/src/shade/renderer/global_illumination/probe/octahedral/graph_lpv_shade.js +4 -2
  591. package/src/shade/renderer/global_illumination/sharc/SpatialHashRadianceCache.d.ts.map +1 -1
  592. package/src/shade/renderer/global_illumination/sharc/SpatialHashRadianceCache.js +5 -1
  593. package/src/shade/renderer/light/cluster/cull/graph_cull_lights.d.ts.map +1 -1
  594. package/src/shade/renderer/light/cluster/cull/graph_cull_lights.js +35 -11
  595. package/src/shade/renderer/loader/gltf/tiny-gltf.d.ts +1 -1
  596. package/src/shade/renderer/particles/DESIGN.md +271 -112
  597. package/src/shade/renderer/particles/GPUParticleSystem.d.ts +251 -86
  598. package/src/shade/renderer/particles/GPUParticleSystem.d.ts.map +1 -1
  599. package/src/shade/renderer/particles/GPUParticleSystem.js +822 -146
  600. package/src/shade/renderer/particles/ParticleConstants.d.ts +96 -3
  601. package/src/shade/renderer/particles/ParticleConstants.d.ts.map +1 -1
  602. package/src/shade/renderer/particles/ParticleConstants.js +103 -4
  603. package/src/shade/renderer/particles/coherence/graph_particle_bucket.d.ts +5 -4
  604. package/src/shade/renderer/particles/coherence/graph_particle_bucket.d.ts.map +1 -1
  605. package/src/shade/renderer/particles/coherence/graph_particle_bucket.js +11 -15
  606. package/src/shade/renderer/particles/coherence/shader_particle_bucket.d.ts +2 -2
  607. package/src/shade/renderer/particles/coherence/shader_particle_bucket.d.ts.map +1 -1
  608. package/src/shade/renderer/particles/coherence/shader_particle_bucket.js +20 -15
  609. package/src/shade/renderer/particles/data/PARTICLE_COUNTERS.d.ts +7 -8
  610. package/src/shade/renderer/particles/data/PARTICLE_COUNTERS.d.ts.map +1 -1
  611. package/src/shade/renderer/particles/data/PARTICLE_COUNTERS.js +7 -17
  612. package/src/shade/renderer/particles/data/PARTICLE_DATABASE_SPEC.d.ts +56 -0
  613. package/src/shade/renderer/particles/data/PARTICLE_DATABASE_SPEC.d.ts.map +1 -0
  614. package/src/shade/renderer/particles/data/PARTICLE_DATABASE_SPEC.js +68 -0
  615. package/src/shade/renderer/particles/data/PARTICLE_EMITTER_STATE.d.ts +39 -0
  616. package/src/shade/renderer/particles/data/PARTICLE_EMITTER_STATE.d.ts.map +1 -0
  617. package/src/shade/renderer/particles/data/PARTICLE_EMITTER_STATE.js +52 -0
  618. package/src/shade/renderer/particles/data/PARTICLE_EMITTER_STRUCT.d.ts +83 -0
  619. package/src/shade/renderer/particles/data/PARTICLE_EMITTER_STRUCT.d.ts.map +1 -0
  620. package/src/shade/renderer/particles/data/PARTICLE_EMITTER_STRUCT.js +166 -0
  621. package/src/shade/renderer/particles/data/chunk_particle_emitter.d.ts +21 -5
  622. package/src/shade/renderer/particles/data/chunk_particle_emitter.d.ts.map +1 -1
  623. package/src/shade/renderer/particles/data/chunk_particle_emitter.js +44 -85
  624. package/src/shade/renderer/particles/data/particle_header.d.ts +62 -0
  625. package/src/shade/renderer/particles/data/particle_header.d.ts.map +1 -0
  626. package/src/shade/renderer/particles/data/particle_header.js +103 -0
  627. package/src/shade/renderer/particles/graph/ParticleGroupBuilder.d.ts +179 -0
  628. package/src/shade/renderer/particles/graph/ParticleGroupBuilder.d.ts.map +1 -0
  629. package/src/shade/renderer/particles/graph/ParticleGroupBuilder.js +303 -0
  630. package/src/shade/renderer/particles/graph/ParticleNodeDescription.d.ts +120 -1
  631. package/src/shade/renderer/particles/graph/ParticleNodeDescription.d.ts.map +1 -1
  632. package/src/shade/renderer/particles/graph/ParticleNodeDescription.js +90 -1
  633. package/src/shade/renderer/particles/graph/ParticleNodeGroup.d.ts +283 -0
  634. package/src/shade/renderer/particles/graph/ParticleNodeGroup.d.ts.map +1 -0
  635. package/src/shade/renderer/particles/graph/ParticleNodeGroup.js +419 -0
  636. package/src/shade/renderer/particles/graph/ParticleNodeRegistry.d.ts +44 -2
  637. package/src/shade/renderer/particles/graph/ParticleNodeRegistry.d.ts.map +1 -1
  638. package/src/shade/renderer/particles/graph/ParticleNodeRegistry.js +476 -40
  639. package/src/shade/renderer/particles/graph/ParticleValueType.d.ts +188 -0
  640. package/src/shade/renderer/particles/graph/ParticleValueType.d.ts.map +1 -0
  641. package/src/shade/renderer/particles/graph/ParticleValueType.js +287 -0
  642. package/src/shade/renderer/particles/graph/compile_particle_graph.d.ts +16 -9
  643. package/src/shade/renderer/particles/graph/compile_particle_graph.d.ts.map +1 -1
  644. package/src/shade/renderer/particles/graph/compile_particle_graph.js +464 -205
  645. package/src/shade/renderer/particles/graph/flatten_particle_graph.d.ts +11 -0
  646. package/src/shade/renderer/particles/graph/flatten_particle_graph.d.ts.map +1 -0
  647. package/src/shade/renderer/particles/graph/flatten_particle_graph.js +213 -0
  648. package/src/shade/renderer/particles/graph/groups/particle_builtin_groups.d.ts +27 -0
  649. package/src/shade/renderer/particles/graph/groups/particle_builtin_groups.d.ts.map +1 -0
  650. package/src/shade/renderer/particles/graph/groups/particle_builtin_groups.js +75 -0
  651. package/src/shade/renderer/particles/graph/groups/particle_groups_geometry.d.ts +83 -0
  652. package/src/shade/renderer/particles/graph/groups/particle_groups_geometry.d.ts.map +1 -0
  653. package/src/shade/renderer/particles/graph/groups/particle_groups_geometry.js +319 -0
  654. package/src/shade/renderer/particles/graph/groups/particle_groups_noise.d.ts +22 -0
  655. package/src/shade/renderer/particles/graph/groups/particle_groups_noise.d.ts.map +1 -0
  656. package/src/shade/renderer/particles/graph/groups/particle_groups_noise.js +517 -0
  657. package/src/shade/renderer/particles/graph/groups/particle_groups_utility.d.ts +70 -0
  658. package/src/shade/renderer/particles/graph/groups/particle_groups_utility.d.ts.map +1 -0
  659. package/src/shade/renderer/particles/graph/groups/particle_groups_utility.js +161 -0
  660. package/src/shade/renderer/particles/graph/groups/simplex_reference.d.ts +8 -0
  661. package/src/shade/renderer/particles/graph/groups/simplex_reference.d.ts.map +1 -0
  662. package/src/shade/renderer/particles/graph/groups/simplex_reference.js +159 -0
  663. package/src/shade/renderer/particles/graph/infer_particle_graph_types.d.ts +88 -0
  664. package/src/shade/renderer/particles/graph/infer_particle_graph_types.d.ts.map +1 -0
  665. package/src/shade/renderer/particles/graph/infer_particle_graph_types.js +234 -0
  666. package/src/shade/renderer/particles/graph/make_particle_group.d.ts +98 -0
  667. package/src/shade/renderer/particles/graph/make_particle_group.d.ts.map +1 -0
  668. package/src/shade/renderer/particles/graph/make_particle_group.js +427 -0
  669. package/src/shade/renderer/particles/graph/particle_graph_authoring.d.ts +11 -4
  670. package/src/shade/renderer/particles/graph/particle_graph_authoring.d.ts.map +1 -1
  671. package/src/shade/renderer/particles/graph/particle_graph_authoring.js +121 -108
  672. package/src/shade/renderer/particles/graph/particle_graph_diagnostic.d.ts +38 -0
  673. package/src/shade/renderer/particles/graph/particle_graph_diagnostic.d.ts.map +1 -0
  674. package/src/shade/renderer/particles/graph/particle_graph_diagnostic.js +25 -0
  675. package/src/shade/renderer/particles/graph/particle_graph_distributions.d.ts +29 -0
  676. package/src/shade/renderer/particles/graph/particle_graph_distributions.d.ts.map +1 -0
  677. package/src/shade/renderer/particles/graph/particle_graph_distributions.js +170 -0
  678. package/src/shade/renderer/particles/graph/particle_graph_json.d.ts +87 -0
  679. package/src/shade/renderer/particles/graph/particle_graph_json.d.ts.map +1 -0
  680. package/src/shade/renderer/particles/graph/particle_graph_json.js +196 -0
  681. package/src/shade/renderer/particles/graph/particle_group_json.d.ts +86 -0
  682. package/src/shade/renderer/particles/graph/particle_group_json.d.ts.map +1 -0
  683. package/src/shade/renderer/particles/graph/particle_group_json.js +182 -0
  684. package/src/shade/renderer/particles/graph/particle_group_library.d.ts +33 -0
  685. package/src/shade/renderer/particles/graph/particle_group_library.d.ts.map +1 -0
  686. package/src/shade/renderer/particles/graph/particle_group_library.js +75 -0
  687. package/src/shade/renderer/particles/graph/particle_node_library.d.ts +37 -0
  688. package/src/shade/renderer/particles/graph/particle_node_library.d.ts.map +1 -0
  689. package/src/shade/renderer/particles/graph/particle_node_library.js +64 -0
  690. package/src/shade/renderer/particles/graph/particle_node_parameters.d.ts +146 -0
  691. package/src/shade/renderer/particles/graph/particle_node_parameters.d.ts.map +1 -0
  692. package/src/shade/renderer/particles/graph/particle_node_parameters.js +234 -0
  693. package/src/shade/renderer/particles/graph/particle_node_presentation.d.ts +30 -0
  694. package/src/shade/renderer/particles/graph/particle_node_presentation.d.ts.map +1 -0
  695. package/src/shade/renderer/particles/graph/particle_node_presentation.js +286 -0
  696. package/src/shade/renderer/particles/graph/validate_particle_graph.d.ts +66 -0
  697. package/src/shade/renderer/particles/graph/validate_particle_graph.d.ts.map +1 -0
  698. package/src/shade/renderer/particles/graph/validate_particle_graph.js +439 -0
  699. package/src/shade/renderer/particles/graph_particles.d.ts +143 -68
  700. package/src/shade/renderer/particles/graph_particles.d.ts.map +1 -1
  701. package/src/shade/renderer/particles/graph_particles.js +424 -71
  702. package/src/shade/renderer/particles/graph_particles_avboit.d.ts +107 -0
  703. package/src/shade/renderer/particles/graph_particles_avboit.d.ts.map +1 -0
  704. package/src/shade/renderer/particles/graph_particles_avboit.js +312 -0
  705. package/src/shade/renderer/particles/isa/InstructionStream.d.ts +41 -8
  706. package/src/shade/renderer/particles/isa/InstructionStream.d.ts.map +1 -1
  707. package/src/shade/renderer/particles/isa/InstructionStream.js +111 -33
  708. package/src/shade/renderer/particles/isa/ParticleAssembler.d.ts +13 -10
  709. package/src/shade/renderer/particles/isa/ParticleAssembler.d.ts.map +1 -1
  710. package/src/shade/renderer/particles/isa/ParticleAssembler.js +30 -26
  711. package/src/shade/renderer/particles/isa/ParticleProgram.d.ts +27 -15
  712. package/src/shade/renderer/particles/isa/ParticleProgram.d.ts.map +1 -1
  713. package/src/shade/renderer/particles/isa/ParticleProgram.js +139 -34
  714. package/src/shade/renderer/particles/isa/ParticleVMISA.d.ts +117 -9
  715. package/src/shade/renderer/particles/isa/ParticleVMISA.d.ts.map +1 -1
  716. package/src/shade/renderer/particles/isa/ParticleVMISA.js +296 -37
  717. package/src/shade/renderer/particles/isa/particle_assembly.d.ts +149 -0
  718. package/src/shade/renderer/particles/isa/particle_assembly.d.ts.map +1 -0
  719. package/src/shade/renderer/particles/isa/particle_assembly.js +999 -0
  720. package/src/shade/renderer/particles/layout/ParticleLayout.d.ts +25 -3
  721. package/src/shade/renderer/particles/layout/ParticleLayout.d.ts.map +1 -1
  722. package/src/shade/renderer/particles/layout/ParticleLayout.js +33 -5
  723. package/src/shade/renderer/particles/optimizer/optimize_particle_program.d.ts +160 -0
  724. package/src/shade/renderer/particles/optimizer/optimize_particle_program.d.ts.map +1 -0
  725. package/src/shade/renderer/particles/optimizer/optimize_particle_program.js +478 -0
  726. package/src/shade/renderer/particles/optimizer/particle_program_analysis.d.ts +291 -0
  727. package/src/shade/renderer/particles/optimizer/particle_program_analysis.d.ts.map +1 -0
  728. package/src/shade/renderer/particles/optimizer/particle_program_analysis.js +647 -0
  729. package/src/shade/renderer/particles/optimizer/particle_program_equivalence.d.ts +95 -0
  730. package/src/shade/renderer/particles/optimizer/particle_program_equivalence.d.ts.map +1 -0
  731. package/src/shade/renderer/particles/optimizer/particle_program_equivalence.js +213 -0
  732. package/src/shade/renderer/particles/optimizer/particle_program_fuzz.d.ts +16 -0
  733. package/src/shade/renderer/particles/optimizer/particle_program_fuzz.d.ts.map +1 -0
  734. package/src/shade/renderer/particles/optimizer/particle_program_fuzz.js +223 -0
  735. package/src/shade/renderer/particles/optimizer/particle_vm_semantics.d.ts +121 -0
  736. package/src/shade/renderer/particles/optimizer/particle_vm_semantics.d.ts.map +1 -0
  737. package/src/shade/renderer/particles/optimizer/particle_vm_semantics.js +206 -0
  738. package/src/shade/renderer/particles/optimizer/pass_dedup_constants.d.ts +11 -0
  739. package/src/shade/renderer/particles/optimizer/pass_dedup_constants.d.ts.map +1 -0
  740. package/src/shade/renderer/particles/optimizer/pass_dedup_constants.js +197 -0
  741. package/src/shade/renderer/particles/optimizer/pass_eliminate_dead_code.d.ts +4 -0
  742. package/src/shade/renderer/particles/optimizer/pass_eliminate_dead_code.d.ts.map +1 -0
  743. package/src/shade/renderer/particles/optimizer/pass_eliminate_dead_code.js +51 -0
  744. package/src/shade/renderer/particles/optimizer/pass_fold_constants.d.ts +4 -0
  745. package/src/shade/renderer/particles/optimizer/pass_fold_constants.d.ts.map +1 -0
  746. package/src/shade/renderer/particles/optimizer/pass_fold_constants.js +561 -0
  747. package/src/shade/renderer/particles/optimizer/pass_narrow_widths.d.ts +4 -0
  748. package/src/shade/renderer/particles/optimizer/pass_narrow_widths.d.ts.map +1 -0
  749. package/src/shade/renderer/particles/optimizer/pass_narrow_widths.js +72 -0
  750. package/src/shade/renderer/particles/optimizer/pass_pack_registers.d.ts +4 -0
  751. package/src/shade/renderer/particles/optimizer/pass_pack_registers.d.ts.map +1 -0
  752. package/src/shade/renderer/particles/optimizer/pass_pack_registers.js +86 -0
  753. package/src/shade/renderer/particles/optimizer/pass_peephole.d.ts +4 -0
  754. package/src/shade/renderer/particles/optimizer/pass_peephole.d.ts.map +1 -0
  755. package/src/shade/renderer/particles/optimizer/pass_peephole.js +271 -0
  756. package/src/shade/renderer/particles/optimizer/pass_propagate_copies.d.ts +4 -0
  757. package/src/shade/renderer/particles/optimizer/pass_propagate_copies.d.ts.map +1 -0
  758. package/src/shade/renderer/particles/optimizer/pass_propagate_copies.js +268 -0
  759. package/src/shade/renderer/particles/optimizer/pass_share_subexpressions.d.ts +4 -0
  760. package/src/shade/renderer/particles/optimizer/pass_share_subexpressions.d.ts.map +1 -0
  761. package/src/shade/renderer/particles/optimizer/pass_share_subexpressions.js +151 -0
  762. package/src/shade/renderer/particles/particle_test_fixtures.d.ts +131 -0
  763. package/src/shade/renderer/particles/particle_test_fixtures.d.ts.map +1 -0
  764. package/src/shade/renderer/particles/particle_test_fixtures.js +203 -0
  765. package/src/shade/renderer/particles/runtime/Emitter.d.ts +219 -0
  766. package/src/shade/renderer/particles/runtime/Emitter.d.ts.map +1 -0
  767. package/src/shade/renderer/particles/runtime/EmitterRegistry.d.ts +128 -60
  768. package/src/shade/renderer/particles/runtime/EmitterRegistry.d.ts.map +1 -1
  769. package/src/shade/renderer/particles/runtime/EmitterRegistry.js +286 -117
  770. package/src/shade/renderer/particles/runtime/ParticleEmitter.d.ts +264 -0
  771. package/src/shade/renderer/particles/runtime/ParticleEmitter.d.ts.map +1 -0
  772. package/src/shade/renderer/particles/runtime/ParticleEmitter.js +404 -0
  773. package/src/shade/renderer/particles/runtime/ParticleSpawnScheduler.d.ts +49 -15
  774. package/src/shade/renderer/particles/runtime/ParticleSpawnScheduler.d.ts.map +1 -1
  775. package/src/shade/renderer/particles/runtime/ProgramArena.d.ts +51 -18
  776. package/src/shade/renderer/particles/runtime/ProgramArena.d.ts.map +1 -1
  777. package/src/shade/renderer/particles/runtime/ProgramArena.js +72 -32
  778. package/src/shade/renderer/particles/runtime/create_particle_effect.d.ts +4 -4
  779. package/src/shade/renderer/particles/runtime/create_particle_effect.d.ts.map +1 -1
  780. package/src/shade/renderer/particles/runtime/create_particle_effect.js +4 -4
  781. package/src/shade/renderer/particles/runtime/emitter_database_image.d.ts +15 -0
  782. package/src/shade/renderer/particles/runtime/emitter_database_image.d.ts.map +1 -0
  783. package/src/shade/renderer/particles/shaders/chunk_particle_avboit_contribution.d.ts +17 -0
  784. package/src/shade/renderer/particles/shaders/chunk_particle_avboit_contribution.d.ts.map +1 -0
  785. package/src/shade/renderer/particles/shaders/chunk_particle_avboit_contribution.js +75 -0
  786. package/src/shade/renderer/particles/shaders/chunk_particle_billboard_vertex.d.ts +20 -0
  787. package/src/shade/renderer/particles/shaders/chunk_particle_billboard_vertex.d.ts.map +1 -0
  788. package/src/shade/renderer/particles/shaders/chunk_particle_billboard_vertex.js +94 -0
  789. package/src/shade/renderer/particles/shaders/chunk_particle_context.d.ts +15 -5
  790. package/src/shade/renderer/particles/shaders/chunk_particle_context.d.ts.map +1 -1
  791. package/src/shade/renderer/particles/shaders/chunk_particle_curve_animation.d.ts +1 -2
  792. package/src/shade/renderer/particles/shaders/chunk_particle_curve_animation.d.ts.map +1 -1
  793. package/src/shade/renderer/particles/shaders/chunk_particle_curve_animation.js +1 -2
  794. package/src/shade/renderer/particles/shaders/chunk_particle_curve_disabled.d.ts +11 -0
  795. package/src/shade/renderer/particles/shaders/chunk_particle_curve_disabled.d.ts.map +1 -0
  796. package/src/shade/renderer/particles/shaders/chunk_particle_curve_disabled.js +13 -0
  797. package/src/shade/renderer/particles/shaders/chunk_particle_emitter_age.d.ts +17 -0
  798. package/src/shade/renderer/particles/shaders/chunk_particle_emitter_age.d.ts.map +1 -0
  799. package/src/shade/renderer/particles/shaders/chunk_particle_emitter_age.js +22 -0
  800. package/src/shade/renderer/particles/shaders/chunk_particle_emitter_world_sphere.d.ts +14 -0
  801. package/src/shade/renderer/particles/shaders/chunk_particle_emitter_world_sphere.d.ts.map +1 -0
  802. package/src/shade/renderer/particles/shaders/chunk_particle_emitter_world_sphere.js +23 -0
  803. package/src/shade/renderer/particles/shaders/chunk_particle_render_math.js +1 -1
  804. package/src/shade/renderer/particles/shaders/chunk_particle_scene_view_depth.d.ts +18 -0
  805. package/src/shade/renderer/particles/shaders/chunk_particle_scene_view_depth.d.ts.map +1 -0
  806. package/src/shade/renderer/particles/shaders/chunk_particle_scene_view_depth.js +24 -0
  807. package/src/shade/renderer/particles/shaders/chunk_particle_setup_context.d.ts +26 -0
  808. package/src/shade/renderer/particles/shaders/chunk_particle_setup_context.d.ts.map +1 -0
  809. package/src/shade/renderer/particles/shaders/chunk_particle_setup_context.js +59 -0
  810. package/src/shade/renderer/particles/shaders/shader_particle_avboit_draw.d.ts +14 -0
  811. package/src/shade/renderer/particles/shaders/shader_particle_avboit_draw.d.ts.map +1 -0
  812. package/src/shade/renderer/particles/shaders/shader_particle_avboit_draw.js +153 -0
  813. package/src/shade/renderer/particles/shaders/shader_particle_avboit_occupancy.d.ts +3 -0
  814. package/src/shade/renderer/particles/shaders/shader_particle_avboit_occupancy.d.ts.map +1 -0
  815. package/src/shade/renderer/particles/shaders/shader_particle_avboit_occupancy.js +124 -0
  816. package/src/shade/renderer/particles/shaders/shader_particle_avboit_splat.d.ts +19 -0
  817. package/src/shade/renderer/particles/shaders/shader_particle_avboit_splat.d.ts.map +1 -0
  818. package/src/shade/renderer/particles/shaders/shader_particle_avboit_splat.js +147 -0
  819. package/src/shade/renderer/particles/shaders/shader_particle_emit.d.ts +18 -8
  820. package/src/shade/renderer/particles/shaders/shader_particle_emit.d.ts.map +1 -1
  821. package/src/shade/renderer/particles/shaders/shader_particle_emit.js +72 -37
  822. package/src/shade/renderer/particles/shaders/shader_particle_emitter_tick.d.ts +5 -0
  823. package/src/shade/renderer/particles/shaders/shader_particle_emitter_tick.d.ts.map +1 -0
  824. package/src/shade/renderer/particles/shaders/shader_particle_emitter_tick.js +216 -0
  825. package/src/shade/renderer/particles/shaders/shader_particle_finalize.d.ts.map +1 -1
  826. package/src/shade/renderer/particles/shaders/shader_particle_finalize.js +7 -3
  827. package/src/shade/renderer/particles/shaders/shader_particle_render.d.ts.map +1 -1
  828. package/src/shade/renderer/particles/shaders/shader_particle_render.js +132 -197
  829. package/src/shade/renderer/particles/shaders/shader_particle_simulate.d.ts.map +1 -1
  830. package/src/shade/renderer/particles/shaders/shader_particle_simulate.js +78 -24
  831. package/src/shade/renderer/particles/shaders/shader_particle_spawn_commands.d.ts +26 -0
  832. package/src/shade/renderer/particles/shaders/shader_particle_spawn_commands.d.ts.map +1 -0
  833. package/src/shade/renderer/particles/shaders/shader_particle_spawn_commands.js +66 -0
  834. package/src/shade/renderer/particles/sort/shader_particle_sort.d.ts +1 -1
  835. package/src/shade/renderer/particles/sort/shader_particle_sort.d.ts.map +1 -1
  836. package/src/shade/renderer/particles/sort/shader_particle_sort.js +21 -36
  837. package/src/shade/renderer/particles/vm/ParticleVMReference.d.ts +2 -2
  838. package/src/shade/renderer/particles/vm/ParticleVMReference.d.ts.map +1 -1
  839. package/src/shade/renderer/particles/vm/ParticleVMReference.js +100 -90
  840. package/src/shade/renderer/particles/vm/chunk_particle_vm.d.ts.map +1 -1
  841. package/src/shade/renderer/particles/vm/chunk_particle_vm.js +270 -138
  842. package/src/shade/renderer/postprocess/denoise/debug/graph_pass_debug_reprojection.d.ts +1 -2
  843. package/src/shade/renderer/postprocess/denoise/debug/graph_pass_debug_reprojection.d.ts.map +1 -1
  844. package/src/shade/renderer/postprocess/denoise/debug/graph_pass_debug_reprojection.js +0 -3
  845. package/src/shade/renderer/postprocess/dof/gather/DepthOfFieldU.js +1 -1
  846. package/src/shade/renderer/postprocess/dof/raymarch/DepthOfField.js +1 -1
  847. package/src/shade/renderer/postprocess/nss/NSS.d.ts +1 -1
  848. package/src/shade/renderer/postprocess/nss/NSS.d.ts.map +1 -1
  849. package/src/shade/renderer/postprocess/nss/NSS.js +3 -2
  850. package/src/shade/renderer/postprocess/nss/generate_NSS_WEIGHTS_ACTUAL.js +3 -2
  851. package/src/shade/renderer/rasterize/native/avboit/AVBOITSideChannel.d.ts +87 -0
  852. package/src/shade/renderer/rasterize/native/avboit/AVBOITSideChannel.d.ts.map +1 -0
  853. package/src/shade/renderer/rasterize/native/avboit/AVBOITSideChannel.js +61 -0
  854. package/src/shade/renderer/rasterize/native/avboit/AVBOIT_LEDGER.md +179 -0
  855. package/src/shade/renderer/rasterize/native/avboit/AVBOIT_PLAN_2026_09_03.md +27 -2
  856. package/src/shade/renderer/rasterize/native/avboit/avboit_draw_fragment_body.d.ts +9 -2
  857. package/src/shade/renderer/rasterize/native/avboit/avboit_draw_fragment_body.d.ts.map +1 -1
  858. package/src/shade/renderer/rasterize/native/avboit/avboit_draw_fragment_body.js +12 -3
  859. package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_splat_slice.d.ts +18 -0
  860. package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_splat_slice.d.ts.map +1 -0
  861. package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_splat_slice.js +58 -0
  862. package/src/shade/renderer/rasterize/native/avboit/graph_rasterize_meshes_transparent_avboit.d.ts +25 -1
  863. package/src/shade/renderer/rasterize/native/avboit/graph_rasterize_meshes_transparent_avboit.d.ts.map +1 -1
  864. package/src/shade/renderer/rasterize/native/avboit/graph_rasterize_meshes_transparent_avboit.js +69 -10
  865. package/src/shade/renderer/rasterize/native/avboit/shader_avboit_resolve.d.ts.map +1 -1
  866. package/src/shade/renderer/rasterize/native/avboit/shader_avboit_resolve.js +10 -4
  867. package/src/shade/renderer/rasterize/native/avboit/shader_avboit_splat.d.ts.map +1 -1
  868. package/src/shade/renderer/rasterize/native/avboit/shader_avboit_splat.js +4 -39
  869. package/src/shade/renderer/rasterize/standard/graph_rasterize_scene.d.ts +7 -2
  870. package/src/shade/renderer/rasterize/standard/graph_rasterize_scene.d.ts.map +1 -1
  871. package/src/shade/renderer/rasterize/standard/graph_rasterize_scene.js +16 -2
  872. package/src/shade/renderer/scene/optimization/geometry/geometry_deduplicate.d.ts +3 -3
  873. package/src/shade/renderer/scene/optimization/geometry/geometry_deduplicate.d.ts.map +1 -1
  874. package/src/shade/renderer/scene/optimization/geometry/geometry_deduplicate.js +29 -23
  875. package/src/shade/renderer/scene/optimization/geometry/internal/anchor_grow_topological.d.ts +2 -2
  876. package/src/shade/renderer/scene/optimization/geometry/internal/anchor_grow_topological.d.ts.map +1 -1
  877. package/src/shade/renderer/scene/optimization/geometry/internal/anchor_grow_topological.js +3 -3
  878. package/src/shade/renderer/scene/optimization/geometry/internal/cover_greedy.d.ts +4 -4
  879. package/src/shade/renderer/scene/optimization/geometry/internal/cover_greedy.d.ts.map +1 -1
  880. package/src/shade/renderer/scene/optimization/geometry/internal/cover_greedy.js +2 -2
  881. package/src/shade/renderer/scene/optimization/geometry/internal/match_refit_validate.d.ts +2 -2
  882. package/src/shade/renderer/scene/optimization/geometry/internal/match_refit_validate.d.ts.map +1 -1
  883. package/src/shade/renderer/scene/optimization/geometry/internal/match_refit_validate.js +2 -2
  884. package/src/shade/renderer/scene/optimization/geometry/internal/match_solve_transform.d.ts +7 -7
  885. package/src/shade/renderer/scene/optimization/geometry/internal/match_solve_transform.d.ts.map +1 -1
  886. package/src/shade/renderer/scene/optimization/geometry/internal/match_solve_transform.js +79 -71
  887. package/src/shade/renderer/shader/chunk/math/chunk_lower_bound_branchless.d.ts.map +1 -1
  888. package/src/shade/renderer/shader/chunk/math/chunk_lower_bound_branchless.js +3 -1
  889. package/src/shade/renderer/shader/chunk/random/chunk_hash_to_float01.d.ts +13 -3
  890. package/src/shade/renderer/shader/chunk/random/chunk_hash_to_float01.d.ts.map +1 -1
  891. package/src/shade/renderer/shader/chunk/random/chunk_hash_to_float01.js +15 -5
  892. package/src/shade/renderer/shader/chunk/random/chunk_random_uint.d.ts +8 -0
  893. package/src/shade/renderer/shader/chunk/random/chunk_random_uint.d.ts.map +1 -1
  894. package/src/shade/renderer/shader/chunk/random/chunk_random_uint.js +15 -5
  895. package/src/shade/renderer/shader/chunk/random/pcg/chunk_pcg.d.ts +7 -0
  896. package/src/shade/renderer/shader/chunk/random/pcg/chunk_pcg.d.ts.map +1 -1
  897. package/src/shade/renderer/shader/chunk/random/pcg/chunk_pcg.js +17 -7
  898. package/src/shade/renderer/shader/chunk/random/pcg/chunk_pcg_advance.d.ts +17 -0
  899. package/src/shade/renderer/shader/chunk/random/pcg/chunk_pcg_advance.d.ts.map +1 -0
  900. package/src/shade/renderer/shader/chunk/random/pcg/chunk_pcg_advance.js +26 -0
  901. package/src/shade/renderer/shader/chunk/random/pcg/chunk_pcg_output.d.ts +15 -0
  902. package/src/shade/renderer/shader/chunk/random/pcg/chunk_pcg_output.d.ts.map +1 -0
  903. package/src/shade/renderer/shader/chunk/random/pcg/chunk_pcg_output.js +26 -0
  904. package/src/shade/renderer/shadow/map/DirectionalLightShadowmap.d.ts.map +1 -1
  905. package/src/shade/renderer/shadow/map/DirectionalLightShadowmap.js +8 -2
  906. package/src/shade/renderer/shadow/map/PointLightShadowmap.d.ts.map +1 -1
  907. package/src/shade/renderer/shadow/map/PointLightShadowmap.js +7 -2
  908. package/src/shade/renderer/shadow/map/SpotLightShadowmap.d.ts.map +1 -1
  909. package/src/shade/renderer/shadow/map/SpotLightShadowmap.js +3 -15
  910. package/src/shade/renderer/shadow/map/TetrahedronPointLightShadowmap.d.ts.map +1 -1
  911. package/src/shade/renderer/shadow/map/TetrahedronPointLightShadowmap.js +10 -4
  912. package/src/shade/renderer/shadow/vsm/GPUViewShadowMaps.d.ts.map +1 -1
  913. package/src/shade/renderer/shadow/vsm/GPUViewShadowMaps.js +7 -2
  914. package/src/shade/renderer/volumetrics/GPUViewVolumetrics.js +2 -2
  915. package/src/shade/renderer/volumetrics/ParticipatingMediaVolume.d.ts +3 -3
  916. package/src/shade/renderer/volumetrics/ParticipatingMediaVolume.d.ts.map +1 -1
  917. package/src/shade/renderer/volumetrics/ParticipatingMediaVolume.js +3 -3
  918. package/src/shade/wgsl/emulator/WGSLJavaScriptCompiler.d.ts.map +1 -1
  919. package/src/shade/wgsl/emulator/WGSLJavaScriptCompiler.js +66 -5
  920. package/src/shade/wgsl/emulator/WGSLLanguageCore.d.ts.map +1 -1
  921. package/src/shade/wgsl/emulator/WGSLLanguageCore.js +35 -4
  922. package/src/view/minimap/Minimap.d.ts.map +1 -1
  923. package/src/view/minimap/Minimap.js +227 -225
  924. package/src/view/minimap/MinimapCanvasView.js +3 -3
  925. package/src/view/minimap/MinimapMarker.d.ts +45 -0
  926. package/src/view/minimap/MinimapMarker.d.ts.map +1 -0
  927. package/src/view/minimap/MinimapMarker.js +80 -0
  928. package/src/view/minimap/MinimapMarkerCollection.d.ts +6 -5
  929. package/src/view/minimap/MinimapMarkerCollection.d.ts.map +1 -1
  930. package/src/view/minimap/MinimapMarkerCollection.js +6 -6
  931. package/src/view/minimap/MinimapMarkerSerializationAdapter.d.ts +19 -0
  932. package/src/view/minimap/MinimapMarkerSerializationAdapter.d.ts.map +1 -0
  933. package/src/view/minimap/MinimapMarkerSerializationAdapter.js +42 -0
  934. package/src/view/minimap/dom/MinimapCameraView.d.ts +1 -1
  935. package/src/view/minimap/dom/MinimapCameraView.d.ts.map +1 -1
  936. package/src/view/minimap/dom/MinimapCameraView.js +23 -4
  937. package/src/view/minimap/dom/MinimapMarkerView.js +3 -3
  938. package/src/view/minimap/gl/MarkerGL.js +3 -3
  939. package/src/view/util/DomLayoutFrame.d.ts +53 -0
  940. package/src/view/util/DomLayoutFrame.d.ts.map +1 -0
  941. package/src/view/util/DomLayoutFrame.js +122 -0
  942. package/src/core/model/node-graph/visual/layout/BoxLayoutSpec.js +0 -19
  943. package/src/core/model/node-graph/visual/layout/ConnectedBoxLayouter.js +0 -241
  944. package/src/core/model/node-graph/visual/layout/ConnectionEndpointLayoutSpec.js +0 -40
  945. package/src/core/model/node-graph/visual/layout/ConnectionLayoutSpec.js +0 -28
  946. package/src/engine/ecs/util/hideEntityGracefully.js +0 -196
  947. package/src/shade/renderer/particles/cull/chunk_particle_cull.js +0 -38
  948. package/src/shade/renderer/particles/data/PARTICLE_EMITTER_LAYOUT.js +0 -214
  949. package/src/shade/renderer/particles/particle_prototype.d.ts.map +0 -1
  950. package/src/shade/renderer/particles/runtime/ParticleSpawnScheduler.js +0 -86
  951. package/src/shade/renderer/particles/runtime/spawn_request_encoding.js +0 -52
  952. package/src/shade/renderer/particles/shaders/chunk_particle_context.js +0 -58
@@ -1,917 +1,917 @@
1
- import package_json from "../../../../package.json";
2
- import Quaternion from "../../../core/geom/Quaternion.js";
3
- import Vector3 from "../../../core/geom/Vector3.js";
4
- import { DEG_TO_RAD } from "../../../core/math/DEG_TO_RAD.js";
5
- import { MIE_PARTICLES_STANDARD_PRECOMPUTED } from "../../../core/math/physics/mie/MIE_PARTICLES_STANDARD_PRECOMPUTED.js";
6
- import { GPUProfileLevel } from "../../device/timing/profile/GPUProfileLevel.js";
7
- import { GPUProfileSession } from "../../device/timing/profile/GPUProfileSession.js";
8
- import { OrbitalCameraController } from "../../renderer/camera/OrbitalCameraController.js";
9
- import { PerspectiveCamera } from "../../renderer/camera/PerspectiveCamera.js";
10
- import { create_frame_loop } from "../../renderer/create_frame_loop.js";
11
- import { make_sunlight } from "../../renderer/light/make_sunlight.js";
12
- import { load_gltf } from "../../renderer/loader/gltf/load_gltf.js";
13
- import { Renderer } from "../../renderer/Renderer.js";
14
- import { Scene } from "../../renderer/scene/Scene.js";
15
- import {
16
- ParticipatingMediaVolume,
17
- VolumetricsParticleSpec
18
- } from "../../renderer/volumetrics/ParticipatingMediaVolume.js";
19
- import { load_default_environment } from "../load_default_environment.js";
20
- import { make_playground_asset_manager } from "../make_playground_asset_manager.js";
21
-
22
- /**
23
- * The froxel volumetrics pass, with the controls that make its known defects visible.
24
- *
25
- * `shader_volumetrics_build_lighting` and the passes around it were reviewed statically and five
26
- * correctness findings came out of it (C-01 … C-05). Four are geometric, and geometry is the one
27
- * class of defect a unit test can prove but not show: an emulator can assert that a sample lands at
28
- * the wrong depth, and it still cannot tell you whether that reads as a smear, a seam, or nothing
29
- * at all. This page exists so each fix can be looked at before and after.
30
- *
31
- * Every control below is aimed at one finding. The panel says which.
32
- *
33
- * Runs under the dev server (`npm run dev`), which serves `test_assets/` and resolves the one bare
34
- * specifier the renderer reaches for. The panel is hand-rolled for the same reason the Sponza page
35
- * hand-rolls its own — a dozen inputs are not worth a dependency the library does not otherwise
36
- * have.
37
- *
38
- * @see src/shade/renderer/volumetrics/NOTES.md
39
- */
40
-
41
- /**
42
- * Served out of `test_assets/`, which is gitignored and unpublished — `test_assets/README.md` has
43
- * how it gets there.
44
- *
45
- * glTF rather than the `.shade` scene sitting next to it: that export's header reads format
46
- * version 1 and `deserialize_scene` accepts version 2 only, deliberately and with no backward
47
- * compatibility. Re-exporting it is the fix if the `.shade` is ever wanted.
48
- *
49
- * @type {string}
50
- */
51
- const SCENE_URL = '/test_assets/flying_world_-_battle_of_the_trash_god/v2/scene.gltf';
52
-
53
- /**
54
- * A low sun, which is what the volumetrics want: it rakes the scene, so the shafts have something
55
- * to be cast by, and it sits near the horizon where the sun can be swung in and out of the frustum
56
- * without also swinging it behind the terrain.
57
- *
58
- * @type {string}
59
- */
60
- const ENVIRONMENT_URL = '/test_assets/environments/octahedral/venice_sunset_2k.hdr';
61
-
62
- /**
63
- * Camera placements carried over from the sibling repo's playground, where they were found by
64
- * hand while looking at this exact scene. Keeping the numbers rather than re-deriving them means a
65
- * before/after pair is taken from the same place to the pixel.
66
- *
67
- * The `c` field names the finding each one is good for; the panel groups by it.
68
- */
69
- const VIEWS = [
70
- {
71
- id: 'overview',
72
- label: 'Dead overview',
73
- c: 'orientation',
74
- hint: 'the whole island — start here',
75
- position: { x: -44.23572420730413, y: 16.787419516572818, z: -0.3908097424888902 },
76
- rotation: { x: -0.10289617976426199, y: 0.8965686124184902, z: -0.32052909150868936, w: -0.2878162624184363 }
77
- },
78
- {
79
- id: 'vol_0',
80
- label: 'Fog bank, wide',
81
- c: 'C-01',
82
- hint: 'fog fills the frame corners, where the depth error is largest',
83
- position: { x: -39.165083083628595, y: 9.002002938463146, z: -12.822321096883556 },
84
- rotation: { x: 0.0461501744849993, y: 0.9414711984407541, z: -0.14428237367052793, w: 0.3011390717750397 }
85
- },
86
- {
87
- id: 'vol_1',
88
- label: 'Inside the fog',
89
- c: 'C-01',
90
- hint: 'camera inside the medium — radial thinning shows against a flat wall of fog',
91
- position: { x: -57.19543561067503, y: 4.497368033549733, z: -5.431935542965578 },
92
- rotation: {
93
- x: 0.0012158994167123557,
94
- y: 0.9584376528911036,
95
- z: -0.0040851224490818586,
96
- w: 0.28527022081489145
97
- }
98
- },
99
- {
100
- id: 'broadside',
101
- label: 'Island broadside',
102
- c: 'C-03',
103
- hint: 'the island against open sky — the clearest place to swing the sun in and out of frame',
104
- position: { x: -30, y: 11, z: -5 },
105
- rotation: {
106
- x: 0.08308534470573395,
107
- y: -0.7022085341941733,
108
- z: 0.08308534470573395,
109
- w: 0.7022085341941732
110
- }
111
- },
112
- {
113
- id: 'above',
114
- label: 'Island from above',
115
- c: 'C-03',
116
- hint: 'all six volumes at once, with sky behind them',
117
- position: { x: -18, y: 26, z: 26 },
118
- rotation: {
119
- x: -0.09605984305376475,
120
- y: 0.8751796663464206,
121
- z: -0.19438519850844194,
122
- w: -0.43248982966896177
123
- }
124
- },
125
- {
126
- id: 'cloud_deck',
127
- label: 'Inside the cloud deck',
128
- c: 'C-01',
129
- hint: 'uniform medium filling the frame — radial thinning reads as a vignette here',
130
- position: { x: -57, y: -10, z: 20 },
131
- rotation: { x: 0, y: 0.9999652844073138, z: 0.008332465433472727, w: 0 }
132
- },
133
- {
134
- id: 'aliasing_0',
135
- label: 'Volume edge',
136
- c: 'C-02',
137
- hint: 'a fog volume boundary against geometry — a half-froxel shift moves this edge',
138
- position: { x: -44.358600521646366, y: 8.13976892720825, z: 2.6707745658793725 },
139
- rotation: { x: -0.07197787209539422, y: 0.9277035010259875, z: -0.24609398688026018, w: -0.2713358614966388 }
140
- },
141
- {
142
- id: 'aliasing_1',
143
- label: 'Volume edge, above',
144
- c: 'C-02',
145
- hint: 'the same boundary from above, where the slab stack is edge-on',
146
- position: { x: -36.56903863396318, y: 19.1017887067011, z: 13.881382214134083 },
147
- rotation: { x: -0.0961072760435774, y: 0.9036695806244697, z: -0.3054532939528032, w: -0.28432897453277794 }
148
- },
149
- {
150
- id: 'fog_box_0',
151
- label: 'Beside the fog box',
152
- c: 'Q-04',
153
- hint: 'the sun crosses three units of medium here — where sixteen march steps read +58',
154
- position: { x: -49.64917207609583, y: 3.9195318273572317, z: -4.211048766762115 },
155
- rotation: {
156
- x: -0.037898077937503966,
157
- y: 0.6176094542736352,
158
- z: 0.029816629610983452,
159
- w: 0.7850052651282708
160
- }
161
- },
162
- {
163
- id: 'fog_box_1',
164
- label: 'Beside the fog box, deeper',
165
- c: 'Q-04',
166
- hint: 'the same medium a little further in; +70 for the march, and where the volume strobed',
167
- position: { x: -50.28590468756837, y: 4.239170044321892, z: -3.9966900541348767 },
168
- rotation: {
169
- x: -0.03789807793750395,
170
- y: 0.6176094542736352,
171
- z: 0.02981662961098345,
172
- w: 0.7850052651282708
173
- }
174
- },
175
- {
176
- id: 'inside_out',
177
- label: 'Under the island',
178
- c: 'C-05',
179
- hint: 'thin haze over a dark ground — per-froxel noise is most legible here',
180
- position: { x: -61.55864473831582, y: 23.7158848700074, z: -19.634434309820563 },
181
- rotation: { x: 0.27745576215003553, y: 0.0922110339369582, z: -0.026764015593898134, w: 0.9559284035634205 }
182
- },
183
- ];
184
-
185
- /**
186
- * The participating media, carried over from the sibling repo's setup for this scene. Positions are
187
- * tuned to the model, so they move with it and not with anything here.
188
- *
189
- * `target_extinction` rather than `density`: it normalizes across particle specs, so swapping a
190
- * preset changes how the medium *looks* without changing how much light it stops.
191
- *
192
- * @param {Scene} scene
193
- */
194
- function add_participating_media(scene) {
195
-
196
- /**
197
- * @param {object} spec
198
- * @param {number[]} spec.position
199
- * @param {number[]} spec.scale
200
- * @param {object} spec.particle from MIE_PARTICLES_STANDARD_PRECOMPUTED
201
- * @param {number} spec.extinction
202
- * @param {number} spec.fade
203
- * @param {number} [spec.yaw_degrees]
204
- */
205
- function volume({ position, scale, particle, extinction, fade, yaw_degrees = 0 }) {
206
- const v = new ParticipatingMediaVolume();
207
-
208
- v.transform.position.set(...position);
209
- v.transform.scale.set(...scale);
210
-
211
- if (yaw_degrees !== 0) {
212
- v.transform.rotation.fromAxisAngle(Vector3.up, yaw_degrees * DEG_TO_RAD);
213
- }
214
-
215
- v.fade_distance = fade;
216
- v.particle_spec = VolumetricsParticleSpec.fromMeep(particle);
217
- v.target_extinction = extinction;
218
-
219
- scene.volumetrics.add(v);
220
-
221
- return v;
222
- }
223
-
224
- const P = MIE_PARTICLES_STANDARD_PRECOMPUTED;
225
-
226
- // the dense bank along the western edge — the thickest medium in the scene, and the one that
227
- // fills the frame corners from the `vol_0` view
228
- volume({
229
- position: [-55, 4, -5.116],
230
- scale: [10, 3, 20],
231
- yaw_degrees: 83,
232
- particle: P.CLOUD_DROPLET_LARGE,
233
- extinction: 1,
234
- fade: 0.5,
235
- });
236
-
237
- // graveyard
238
- volume({
239
- position: [-51.98019820725889, 7.899311905935347, -15.3228393616169],
240
- scale: [7, 5, 7],
241
- particle: P.FOG_DROPLET_SMALL,
242
- extinction: 0.4,
243
- fade: 1,
244
- });
245
-
246
- // mine
247
- volume({
248
- position: [-50.846390607312934, 0.3159949821015605, 17.89429433940319],
249
- scale: [10, 7, 10],
250
- particle: P.COARSE_DUST_MEDIUM,
251
- extinction: 0.4,
252
- fade: 4,
253
- });
254
-
255
- // large crystal
256
- volume({
257
- position: [-53.41918585295826, 17.333878151116526, 29.90844167706601],
258
- scale: [18, 10, 18],
259
- particle: P.MARITIME_HAZE_MEDIUM,
260
- extinction: 0.1,
261
- fade: 4,
262
- });
263
-
264
- // the low cloud deck the island sits above — big and thin, so it is what a wide FOV sees at the
265
- // frame edges
266
- volume({
267
- position: [-57.36387203979457, -15, 2.607232848593789],
268
- scale: [400, 20, 400],
269
- particle: P.CLOUD_DROPLET_LARGE,
270
- extinction: 0.1,
271
- fade: 4,
272
- });
273
-
274
- // ball cloud overhead
275
- volume({
276
- position: [-51.137423384559284, 33, 5.593067265589105],
277
- scale: [15, 15, 15],
278
- particle: P.CLOUD_DROPLET_LARGE,
279
- extinction: 1,
280
- fade: 7,
281
- });
282
- }
283
-
284
- /**
285
- * A rolling mean frame time, so the readout is legible rather than jittering.
286
- */
287
- class FrameTimer {
288
- #samples = new Float64Array(60);
289
- #count = 0;
290
- #cursor = 0;
291
-
292
- add(seconds) {
293
- this.#samples[this.#cursor] = seconds * 1000;
294
- this.#cursor = (this.#cursor + 1) % this.#samples.length;
295
- this.#count = Math.min(this.#count + 1, this.#samples.length);
296
- }
297
-
298
- reset() {
299
- this.#count = 0;
300
- this.#cursor = 0;
301
- }
302
-
303
- get mean_ms() {
304
- if (this.#count === 0) {
305
- return 0;
306
- }
307
-
308
- let total = 0;
309
- for (let i = 0; i < this.#count; i++) {
310
- total += this.#samples[i];
311
- }
312
- return total / this.#count;
313
- }
314
- }
315
-
316
- /**
317
- * Point a directional light along a heading, in place.
318
- *
319
- * The sun's azimuth is the control C-03 turns on: the shadow ray is clipped against a frustum whose
320
- * far plane is degenerate, so a sun direction that lies inside the view cone never exits it. Swing
321
- * the sun through the frame and the media self-shadowing should be steady; if it switches on as the
322
- * sun leaves the frustum, that is the finding.
323
- *
324
- * @param {DirectionalLight} light
325
- * @param {number} azimuth_degrees 0 points along +X
326
- * @param {number} elevation_degrees above the horizon
327
- */
328
- function aim_light(light, azimuth_degrees, elevation_degrees) {
329
- const azimuth = azimuth_degrees * DEG_TO_RAD;
330
- const elevation = elevation_degrees * DEG_TO_RAD;
331
-
332
- const horizontal = Math.cos(elevation);
333
-
334
- // direction the light travels — downward for a sun above the horizon
335
- const direction = new Vector3(
336
- Math.cos(azimuth) * horizontal,
337
- -Math.sin(elevation),
338
- Math.sin(azimuth) * horizontal
339
- ).normalize();
340
-
341
- const q = new Quaternion();
342
- q.fromUnitVectors(Vector3.forward, direction);
343
- q.normalize();
344
-
345
- light.transform_local.setRotation(q.x, q.y, q.z, q.w);
346
- light.updateMatrices();
347
- }
348
-
349
- /**
350
- * How much each profile capture records. `WORKLOAD` is the highest of the three levels the proposal
351
- * calls shippable: pass spans, the frame graph, and dispatch/draw counts.
352
- *
353
- * Measured on this page it runs ~11 KB a frame over ~393 spans, so ~660 KB/s at 60 Hz — the
354
- * proposal's estimate of 6 KB assumed ~200 passes and this graph is about twice that. Still small
355
- * enough that a hand-driven recording will not notice. `VERBOSE` above it adds bind group contents
356
- * at roughly seven times the rate and wants a frame limit set, not a key you can lean on.
357
- *
358
- * @type {number}
359
- */
360
- const PROFILE_LEVEL = GPUProfileLevel.WORKLOAD;
361
-
362
- /**
363
- * Hand an ArrayBuffer to the browser as a file.
364
- *
365
- * @param {ArrayBuffer} bytes
366
- * @param {string} filename
367
- */
368
- function download_bytes(bytes, filename) {
369
- const url = URL.createObjectURL(new Blob([bytes], { type: 'application/octet-stream' }));
370
-
371
- const anchor = document.createElement('a');
372
-
373
- anchor.href = url;
374
- anchor.download = filename;
375
-
376
- document.body.appendChild(anchor);
377
- anchor.click();
378
- anchor.remove();
379
-
380
- // Revoking in the same task cancels the download in some browsers — the click only schedules it.
381
- setTimeout(() => URL.revokeObjectURL(url), 60000);
382
- }
383
-
384
- /**
385
- * `YYYYMMDD-HHMMSS` in local time, matching the naming the other `.sgpt` captures on this machine
386
- * already use — same sort order, and it reads as the clock you took it by rather than as UTC.
387
- *
388
- * @returns {string}
389
- */
390
- function file_timestamp() {
391
- const now = new Date();
392
-
393
- const pad = value => String(value).padStart(2, '0');
394
-
395
- return `${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(now.getDate())}`
396
- + `-${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}`;
397
- }
398
-
399
- /**
400
- * @param {number} bytes
401
- * @returns {string}
402
- */
403
- function format_bytes(bytes) {
404
- if (bytes < 1024) {
405
- return `${bytes} B`;
406
- }
407
-
408
- if (bytes < 1024 * 1024) {
409
- return `${(bytes / 1024).toFixed(1)} KiB`;
410
- }
411
-
412
- return `${(bytes / (1024 * 1024)).toFixed(1)} MiB`;
413
- }
414
-
415
- export async function start() {
416
-
417
- const element = id => document.getElementById(id);
418
-
419
- const status_element = element('status');
420
- const stats_element = element('stats');
421
-
422
- function set_status(text, kind = '') {
423
- status_element.textContent = text;
424
- status_element.className = kind;
425
- }
426
-
427
- const canvas = element('view');
428
-
429
- const context = canvas.getContext('webgpu');
430
-
431
- if (context === null) {
432
- set_status('no WebGPU context — this browser has none, or the page is not a secure context', 'fail');
433
- return;
434
- }
435
-
436
- const renderer = new Renderer();
437
-
438
- set_status('acquiring a device…');
439
-
440
- await renderer.initialize({ context });
441
-
442
- const camera = new PerspectiveCamera();
443
-
444
- camera.near = 0.1;
445
- camera.far = 120;
446
- camera.fov = 60 * DEG_TO_RAD;
447
- // aspect is set by the frame loop, which is the first place the viewport has a size
448
-
449
- const scene = new Scene();
450
-
451
- const controller = new OrbitalCameraController(camera, document.body);
452
-
453
- // handy from the console
454
- globalThis.renderer = renderer;
455
- globalThis.scene = scene;
456
- globalThis.camera = camera;
457
- globalThis.controller = controller;
458
-
459
- // ---- scene ----------------------------------------------------------------------------
460
-
461
- set_status(`loading ${SCENE_URL} …`);
462
-
463
- try {
464
- const bundle = await load_gltf(SCENE_URL, { assetManager: await make_playground_asset_manager() });
465
-
466
- scene.add(bundle.scenes);
467
- } catch (error) {
468
- set_status(
469
- `could not load the scene (${error.message}). Put the assets under test_assets/ — `
470
- + `see test_assets/README.md.`,
471
- 'fail'
472
- );
473
- console.error(error);
474
- return;
475
- }
476
-
477
- const sun = make_sunlight({ intensity: 0.6, temperature: 5500 });
478
-
479
- scene.lights.add(sun);
480
-
481
- add_participating_media(scene);
482
-
483
- load_default_environment(scene, ENVIRONMENT_URL).then(applied => {
484
- if (applied) {
485
- renderer.indicate_view_change();
486
- }
487
- });
488
-
489
- // ---- controls -------------------------------------------------------------------------
490
-
491
- const frame_timer = new FrameTimer();
492
-
493
- let paused = false;
494
-
495
- /** Re-render and drop the frame-time history, since a setting change invalidates it. */
496
- function repaint() {
497
- renderer.indicate_view_change();
498
- frame_timer.reset();
499
- }
500
-
501
- // -- views
502
-
503
- const views_element = element('views');
504
-
505
- /**
506
- * Which placement the camera was last sent to, for the profile capture's note — a capture that
507
- * cannot say where it was taken is much less use a week later.
508
- * @type {object|null}
509
- */
510
- let current_view = null;
511
-
512
- /** @param {object} view */
513
- function go_to(view) {
514
- current_view = view;
515
-
516
- camera.transform.fromJSON({ position: view.position, rotation: view.rotation });
517
-
518
- // the controller holds its own spherical state and would otherwise snap the camera back on
519
- // the next update
520
- controller.from_transform(camera.transform);
521
-
522
- repaint();
523
- }
524
-
525
- for (const view of VIEWS) {
526
- const button = document.createElement('button');
527
-
528
- button.className = 'view';
529
- button.title = view.hint;
530
- button.innerHTML = `<span class="tag">${view.c}</span>${view.label}`;
531
- button.addEventListener('click', () => {
532
- go_to(view);
533
- set_status(view.hint);
534
- });
535
-
536
- views_element.appendChild(button);
537
- }
538
-
539
- // -- sun
540
-
541
- let sun_azimuth = 200;
542
- let sun_elevation = 12;
543
-
544
- function update_sun() {
545
- aim_light(sun, sun_azimuth, sun_elevation);
546
-
547
- element('sun_azimuth_value').textContent = `${sun_azimuth}°`;
548
- element('sun_elevation_value').textContent = `${sun_elevation}°`;
549
-
550
- repaint();
551
- }
552
-
553
- element('sun_azimuth').addEventListener('input', event => {
554
- sun_azimuth = Number(event.target.value);
555
- update_sun();
556
- });
557
-
558
- element('sun_elevation').addEventListener('input', event => {
559
- sun_elevation = Number(event.target.value);
560
- update_sun();
561
- });
562
-
563
- /**
564
- * Put the sun directly in front of the camera, or directly behind it, at the current elevation.
565
- *
566
- * This is the C-03 A/B reduced to two clicks. "Into view" places the sun's direction inside the
567
- * frustum's direction cone, which is the condition under which `frustum_clip_ray` returns its
568
- * 1e20 sentinel.
569
- *
570
- * @param {boolean} in_front
571
- */
572
- function aim_sun_relative_to_camera(in_front) {
573
- // Straight off the view matrix rather than by rotating a basis vector through the camera's
574
- // quaternion by hand: the third row of a view matrix is the camera's backward axis, so this
575
- // is the renderer's own answer and cannot disagree with what is on screen. The hand-rolled
576
- // version this replaces had a sign error that put the sun behind the camera on both
577
- // branches, which is invisible in a screenshot and made the C-03 A/B read as a null result.
578
- // The matrices are rebuilt during render, so a placement made this tick is not in them yet
579
- // — reading straight after `go_to` would aim the sun at wherever the camera used to be.
580
- camera.update();
581
-
582
- const vm = camera.view_matrix;
583
-
584
- const forward_x = -vm[2];
585
- const forward_z = -vm[10];
586
-
587
- // `aim_light` takes the direction light *travels*, so the sun sits opposite it. Placing the
588
- // sun on the camera's forward axis therefore means aiming its travel back along that axis.
589
- const toward_sun_x = in_front ? forward_x : -forward_x;
590
- const toward_sun_z = in_front ? forward_z : -forward_z;
591
-
592
- const azimuth = Math.atan2(-toward_sun_z, -toward_sun_x) / DEG_TO_RAD;
593
-
594
- sun_azimuth = Math.round((azimuth % 360 + 360) % 360);
595
-
596
- element('sun_azimuth').value = String(sun_azimuth);
597
-
598
- update_sun();
599
- }
600
-
601
- element('sun_into_view').addEventListener('click', () => {
602
- aim_sun_relative_to_camera(true);
603
- set_status('sun placed in front of the camera — C-03 predicts media self-shadowing vanishes here', 'warn');
604
- });
605
-
606
- element('sun_behind').addEventListener('click', () => {
607
- aim_sun_relative_to_camera(false);
608
- set_status('sun placed behind the camera — C-03 predicts media self-shadowing returns here', 'pass');
609
- });
610
-
611
- // -- camera
612
-
613
- element('fov').addEventListener('input', event => {
614
- const degrees = Number(event.target.value);
615
-
616
- camera.fov = degrees * DEG_TO_RAD;
617
-
618
- element('fov_value').textContent = `${degrees}°`;
619
-
620
- repaint();
621
- });
622
-
623
- // -- volumetrics knobs, read by GPUViewVolumetrics on every metadata update
624
-
625
- element('vol_taa').addEventListener('change', event => {
626
- globalThis.__VOL_TAA_ENABLED = event.target.checked;
627
- repaint();
628
- });
629
-
630
- element('taa').addEventListener('change', event => {
631
- renderer.feature_taa_enabled = event.target.checked;
632
- repaint();
633
- });
634
-
635
- // Off by default on this page. Auto exposure keeps adapting for seconds after anything changes,
636
- // which makes a before/after pair differ by more than most of the fixes being judged here do —
637
- // it read as a large effect with the wrong sign more than once while this page was being built.
638
- renderer.feature_automatic_exposure_enabled = false;
639
-
640
- element('auto_exposure').addEventListener('change', event => {
641
- renderer.feature_automatic_exposure_enabled = event.target.checked;
642
- repaint();
643
- });
644
-
645
- element('froxel_far').addEventListener('input', event => {
646
- const value = Number(event.target.value);
647
-
648
- // the slider's top notch means "no split", which is the engine default
649
- globalThis.__VOL_FROXEL_FAR = value >= 120 ? Infinity : value;
650
-
651
- element('froxel_far_value').textContent = value >= 120 ? 'off' : `${value} m`;
652
-
653
- repaint();
654
- });
655
-
656
- element('ms_octaves').addEventListener('input', event => {
657
- const value = Number(event.target.value);
658
-
659
- globalThis.__VOL_MS_OCTAVES = value;
660
-
661
- element('ms_octaves_value').textContent = String(value);
662
-
663
- repaint();
664
- });
665
-
666
- // -- profiling: T starts a recording, T again stops it and downloads the capture
667
-
668
- const profile_status = element('profile_status');
669
-
670
- /**
671
- * The recording in progress, or null.
672
- *
673
- * The engine never constructs one of these — `Renderer.profile_session` is a nullable field and
674
- * the null checks around it are the entire integration, which is what keeps the profiler out of
675
- * a bundle that does not ask for it. The two imports at the top of this file are the whole
676
- * opt-in.
677
- *
678
- * @type {GPUProfileSession|null}
679
- */
680
- let profile_session = null;
681
-
682
- /** Guards against T being leaned on while a stop is still draining. */
683
- let profile_busy = false;
684
-
685
- function set_profile_status(text, recording = false) {
686
- profile_status.textContent = text;
687
- profile_status.className = recording ? 'recording' : '';
688
- }
689
-
690
- function start_profile() {
691
- const device = renderer.device;
692
-
693
- const settings = [
694
- current_view === null ? 'free camera' : current_view.label,
695
- `fov ${Math.round(camera.fov / DEG_TO_RAD)}°`,
696
- `volumetric TAA ${(globalThis.__VOL_TAA_ENABLED ?? true) ? 'on' : 'off'}`,
697
- `${renderer.pixel_ratio.toFixed(2)}x`,
698
- ];
699
-
700
- const session = new GPUProfileSession({
701
- level: PROFILE_LEVEL,
702
- note: `volumetrics_froxel — ${settings.join(' · ')}`,
703
- });
704
-
705
- // Populate before start(): the meta record is written into the stream there and not read
706
- // again. Without it the capture is unreadable in the sense that matters — a pass taking
707
- // 0.4 ms says nothing until you know which GPU and at what resolution.
708
- const meta = session.meta;
709
-
710
- // `GPUDevice.adapterInfo` is recent enough that a browser running this page may not have it,
711
- // and several browsers withhold the fields even when they do. Empty is what the format
712
- // expects in that case.
713
- const info = device.adapterInfo;
714
-
715
- if (info !== undefined && info !== null) {
716
- meta.adapter_vendor = info.vendor ?? "";
717
- meta.adapter_architecture = info.architecture ?? "";
718
- meta.adapter_device = info.device ?? "";
719
- meta.adapter_description = info.description ?? "";
720
- }
721
-
722
- meta.features = [...device.features];
723
- meta.engine_version = package_json.version;
724
-
725
- session.onBytesWritten.add(bytes => {
726
- set_profile_status(
727
- `● recording — ${session.frames_recorded} frames, ${format_bytes(bytes)} · T to stop`,
728
- true
729
- );
730
- });
731
-
732
- renderer.profile_session = session;
733
- session.start();
734
-
735
- profile_session = session;
736
-
737
- // The timers go quiet without this rather than failing, so the capture still carries the
738
- // frame graph and the workload — it just has no durations in it. Worth knowing before you
739
- // record for a minute and then look.
740
- const timed = device.features.has('timestamp-query');
741
-
742
- set_profile_status(
743
- timed
744
- ? '● recording — 0 frames · T to stop'
745
- : '● recording without timestamp-query — structure only, no durations · T to stop',
746
- true
747
- );
748
- }
749
-
750
- /**
751
- * Wait for frames already submitted to finish landing in the session.
752
- *
753
- * A frame is committed when its timestamp readback resolves — a `mapAsync` a frame or two behind
754
- * submit, see `ShadeGPUCommandContext.profiling_absorbed` — and `record_frame` silently ignores
755
- * anything arriving after `stop()`. Stopping the instant the key goes down therefore discards
756
- * the last few frames, which are the ones you were most likely looking at.
757
- *
758
- * Drains the queue, then waits for the frame count to stop moving. Capped, because a recording
759
- * taken while the page is paused never moves at all.
760
- *
761
- * @param {GPUProfileSession} session
762
- */
763
- async function drain_profile(session) {
764
- const DEADLINE_MS = 1000;
765
- const QUIET_MS = 60;
766
-
767
- await renderer.device.queue.onSubmittedWorkDone();
768
-
769
- const deadline = performance.now() + DEADLINE_MS;
770
-
771
- let previous = session.frames_recorded;
772
-
773
- while (performance.now() < deadline) {
774
- await new Promise(resolve => setTimeout(resolve, QUIET_MS));
775
-
776
- if (session.frames_recorded === previous) {
777
- return;
778
- }
779
-
780
- previous = session.frames_recorded;
781
- }
782
- }
783
-
784
- async function stop_profile() {
785
- const session = profile_session;
786
-
787
- profile_session = null;
788
-
789
- // Detach first, so nothing encoded during the drain below joins the recording. Frames
790
- // already encoded are unaffected: Renderer captures the session into a local before it
791
- // subscribes to the readback.
792
- renderer.profile_session = null;
793
-
794
- set_profile_status('stopping — draining in-flight frames…');
795
-
796
- await drain_profile(session);
797
-
798
- const bytes = session.stop();
799
-
800
- const filename = `volumetrics_froxel-${file_timestamp()}.sgpt`;
801
-
802
- download_bytes(bytes, filename);
803
-
804
- // Also to the console, because the status line is the only other place the name appears and
805
- // a browser that declines the save says nothing about which capture it declined.
806
- console.log(
807
- `profile: ${session.frames_recorded} frames, ${format_bytes(bytes.byteLength)}, `
808
- + `${session.topology_count} topolog${session.topology_count === 1 ? 'y' : 'ies'} -> ${filename}`
809
- );
810
-
811
- set_profile_status(
812
- `${session.frames_recorded} frames, ${format_bytes(bytes.byteLength)}`
813
- + `, ${session.topology_count} topolog${session.topology_count === 1 ? 'y' : 'ies'}`
814
- + ` · ${filename}`
815
- );
816
- }
817
-
818
- window.addEventListener('keydown', event => {
819
- if (event.ctrlKey || event.metaKey || event.altKey || event.repeat) {
820
- return;
821
- }
822
-
823
- // `key` first, because it is the letter actually printed on the key the user pressed —
824
- // `code` is physical position, so on AZERTY it names the key where T sits on QWERTY. `code`
825
- // is the fallback for layouts whose `key` is not Latin at all (Cyrillic gives "е" here).
826
- if (event.key.toLowerCase() !== 't' && event.code !== 'KeyT') {
827
- return;
828
- }
829
-
830
- // the panel's sliders and buttons are focusable; do not steal a key from a field
831
- const target = event.target;
832
-
833
- if (target instanceof HTMLInputElement || target instanceof HTMLTextAreaElement || target instanceof HTMLSelectElement) {
834
- return;
835
- }
836
-
837
- if (profile_busy) {
838
- return;
839
- }
840
-
841
- event.preventDefault();
842
-
843
- profile_busy = true;
844
-
845
- const done = profile_session === null
846
- ? Promise.resolve(start_profile())
847
- : stop_profile();
848
-
849
- done.catch(error => {
850
- set_profile_status(`profiling failed: ${error?.message ?? error}`);
851
- console.error(error);
852
-
853
- profile_session = null;
854
- renderer.profile_session = null;
855
- }).finally(() => {
856
- profile_busy = false;
857
- });
858
- });
859
-
860
- element('pause').addEventListener('click', () => {
861
- paused = !paused;
862
- element('pause').textContent = paused ? 'Resume' : 'Pause';
863
- });
864
-
865
- element('log_camera').addEventListener('click', () => {
866
- const t = camera.transform;
867
-
868
- const json = {
869
- position: { x: t.position.x, y: t.position.y, z: t.position.z },
870
- rotation: { x: t.rotation.x, y: t.rotation.y, z: t.rotation.z, w: t.rotation.w }
871
- };
872
-
873
- console.log(JSON.stringify(json, null, 4));
874
- set_status('camera written to the console', 'pass');
875
- });
876
-
877
- // ---- start ----------------------------------------------------------------------------
878
-
879
- update_sun();
880
- go_to(VIEWS[0]);
881
-
882
- controller.movement_speed_scale = 4;
883
-
884
- // the controller's keyboard device listens on body, and only a focused element gets key events
885
- document.body.focus();
886
-
887
- set_status(`loaded — ${scene.instances.instances.length} instances, ${scene.volumetrics.volumes.length} media volumes`, 'pass');
888
-
889
- // ---- frame loop -----------------------------------------------------------------------
890
-
891
- const FRAME_RATE_LIMIT = 60;
892
-
893
- create_frame_loop(time_delta_s => {
894
- const width = window.innerWidth;
895
- const height = window.innerHeight;
896
-
897
- renderer.resize(width, height);
898
- renderer.pixel_ratio = window.devicePixelRatio;
899
- camera.aspect = width / height;
900
-
901
- controller.update();
902
-
903
- if (paused) {
904
- return;
905
- }
906
-
907
- renderer.render(camera, scene, time_delta_s);
908
-
909
- frame_timer.add(time_delta_s);
910
-
911
- const mean = frame_timer.mean_ms;
912
-
913
- stats_element.textContent =
914
- `${mean.toFixed(1)} ms · ${mean > 0 ? (1000 / mean).toFixed(0) : '0'} fps`
915
- + ` · fov ${Math.round(camera.fov / DEG_TO_RAD)}°`;
916
- }, FRAME_RATE_LIMIT);
917
- }
1
+ import package_json from "../../../../package.json";
2
+ import Quaternion from "../../../core/geom/Quaternion.js";
3
+ import Vector3 from "../../../core/geom/Vector3.js";
4
+ import { DEG_TO_RAD } from "../../../core/math/DEG_TO_RAD.js";
5
+ import { MIE_PARTICLES_STANDARD_PRECOMPUTED } from "../../../core/math/physics/mie/MIE_PARTICLES_STANDARD_PRECOMPUTED.js";
6
+ import { GPUProfileLevel } from "../../device/timing/profile/GPUProfileLevel.js";
7
+ import { GPUProfileSession } from "../../device/timing/profile/GPUProfileSession.js";
8
+ import { OrbitalCameraController } from "../../renderer/camera/OrbitalCameraController.js";
9
+ import { PerspectiveCamera } from "../../renderer/camera/PerspectiveCamera.js";
10
+ import { create_frame_loop } from "../../renderer/create_frame_loop.js";
11
+ import { make_sunlight } from "../../renderer/light/make_sunlight.js";
12
+ import { load_gltf } from "../../renderer/loader/gltf/load_gltf.js";
13
+ import { Renderer } from "../../renderer/Renderer.js";
14
+ import { Scene } from "../../renderer/scene/Scene.js";
15
+ import {
16
+ ParticipatingMediaVolume,
17
+ VolumetricsParticleSpec
18
+ } from "../../renderer/volumetrics/ParticipatingMediaVolume.js";
19
+ import { load_default_environment } from "../load_default_environment.js";
20
+ import { make_playground_asset_manager } from "../make_playground_asset_manager.js";
21
+
22
+ /**
23
+ * The froxel volumetrics pass, with the controls that make its known defects visible.
24
+ *
25
+ * `shader_volumetrics_build_lighting` and the passes around it were reviewed statically and five
26
+ * correctness findings came out of it (C-01 … C-05). Four are geometric, and geometry is the one
27
+ * class of defect a unit test can prove but not show: an emulator can assert that a sample lands at
28
+ * the wrong depth, and it still cannot tell you whether that reads as a smear, a seam, or nothing
29
+ * at all. This page exists so each fix can be looked at before and after.
30
+ *
31
+ * Every control below is aimed at one finding. The panel says which.
32
+ *
33
+ * Runs under the dev server (`npm run dev`), which serves `test_assets/` and resolves the one bare
34
+ * specifier the renderer reaches for. The panel is hand-rolled for the same reason the Sponza page
35
+ * hand-rolls its own — a dozen inputs are not worth a dependency the library does not otherwise
36
+ * have.
37
+ *
38
+ * @see src/shade/renderer/volumetrics/NOTES.md
39
+ */
40
+
41
+ /**
42
+ * Served out of `test_assets/`, which is gitignored and unpublished — `test_assets/README.md` has
43
+ * how it gets there.
44
+ *
45
+ * glTF rather than the `.shade` scene sitting next to it: that export's header reads format
46
+ * version 1 and `deserialize_scene` accepts version 2 only, deliberately and with no backward
47
+ * compatibility. Re-exporting it is the fix if the `.shade` is ever wanted.
48
+ *
49
+ * @type {string}
50
+ */
51
+ const SCENE_URL = '/test_assets/flying_world_-_battle_of_the_trash_god/v2/scene.gltf';
52
+
53
+ /**
54
+ * A low sun, which is what the volumetrics want: it rakes the scene, so the shafts have something
55
+ * to be cast by, and it sits near the horizon where the sun can be swung in and out of the frustum
56
+ * without also swinging it behind the terrain.
57
+ *
58
+ * @type {string}
59
+ */
60
+ const ENVIRONMENT_URL = '/test_assets/environments/octahedral/venice_sunset_2k.hdr';
61
+
62
+ /**
63
+ * Camera placements carried over from the sibling repo's playground, where they were found by
64
+ * hand while looking at this exact scene. Keeping the numbers rather than re-deriving them means a
65
+ * before/after pair is taken from the same place to the pixel.
66
+ *
67
+ * The `c` field names the finding each one is good for; the panel groups by it.
68
+ */
69
+ const VIEWS = [
70
+ {
71
+ id: 'overview',
72
+ label: 'Dead overview',
73
+ c: 'orientation',
74
+ hint: 'the whole island — start here',
75
+ position: { x: -44.23572420730413, y: 16.787419516572818, z: -0.3908097424888902 },
76
+ rotation: { x: -0.10289617976426199, y: 0.8965686124184902, z: -0.32052909150868936, w: -0.2878162624184363 }
77
+ },
78
+ {
79
+ id: 'vol_0',
80
+ label: 'Fog bank, wide',
81
+ c: 'C-01',
82
+ hint: 'fog fills the frame corners, where the depth error is largest',
83
+ position: { x: -39.165083083628595, y: 9.002002938463146, z: -12.822321096883556 },
84
+ rotation: { x: 0.0461501744849993, y: 0.9414711984407541, z: -0.14428237367052793, w: 0.3011390717750397 }
85
+ },
86
+ {
87
+ id: 'vol_1',
88
+ label: 'Inside the fog',
89
+ c: 'C-01',
90
+ hint: 'camera inside the medium — radial thinning shows against a flat wall of fog',
91
+ position: { x: -57.19543561067503, y: 4.497368033549733, z: -5.431935542965578 },
92
+ rotation: {
93
+ x: 0.0012158994167123557,
94
+ y: 0.9584376528911036,
95
+ z: -0.0040851224490818586,
96
+ w: 0.28527022081489145
97
+ }
98
+ },
99
+ {
100
+ id: 'broadside',
101
+ label: 'Island broadside',
102
+ c: 'C-03',
103
+ hint: 'the island against open sky — the clearest place to swing the sun in and out of frame',
104
+ position: { x: -30, y: 11, z: -5 },
105
+ rotation: {
106
+ x: 0.08308534470573395,
107
+ y: -0.7022085341941733,
108
+ z: 0.08308534470573395,
109
+ w: 0.7022085341941732
110
+ }
111
+ },
112
+ {
113
+ id: 'above',
114
+ label: 'Island from above',
115
+ c: 'C-03',
116
+ hint: 'all six volumes at once, with sky behind them',
117
+ position: { x: -18, y: 26, z: 26 },
118
+ rotation: {
119
+ x: -0.09605984305376475,
120
+ y: 0.8751796663464206,
121
+ z: -0.19438519850844194,
122
+ w: -0.43248982966896177
123
+ }
124
+ },
125
+ {
126
+ id: 'cloud_deck',
127
+ label: 'Inside the cloud deck',
128
+ c: 'C-01',
129
+ hint: 'uniform medium filling the frame — radial thinning reads as a vignette here',
130
+ position: { x: -57, y: -10, z: 20 },
131
+ rotation: { x: 0, y: 0.9999652844073138, z: 0.008332465433472727, w: 0 }
132
+ },
133
+ {
134
+ id: 'aliasing_0',
135
+ label: 'Volume edge',
136
+ c: 'C-02',
137
+ hint: 'a fog volume boundary against geometry — a half-froxel shift moves this edge',
138
+ position: { x: -44.358600521646366, y: 8.13976892720825, z: 2.6707745658793725 },
139
+ rotation: { x: -0.07197787209539422, y: 0.9277035010259875, z: -0.24609398688026018, w: -0.2713358614966388 }
140
+ },
141
+ {
142
+ id: 'aliasing_1',
143
+ label: 'Volume edge, above',
144
+ c: 'C-02',
145
+ hint: 'the same boundary from above, where the slab stack is edge-on',
146
+ position: { x: -36.56903863396318, y: 19.1017887067011, z: 13.881382214134083 },
147
+ rotation: { x: -0.0961072760435774, y: 0.9036695806244697, z: -0.3054532939528032, w: -0.28432897453277794 }
148
+ },
149
+ {
150
+ id: 'fog_box_0',
151
+ label: 'Beside the fog box',
152
+ c: 'Q-04',
153
+ hint: 'the sun crosses three units of medium here — where sixteen march steps read +58',
154
+ position: { x: -49.64917207609583, y: 3.9195318273572317, z: -4.211048766762115 },
155
+ rotation: {
156
+ x: -0.037898077937503966,
157
+ y: 0.6176094542736352,
158
+ z: 0.029816629610983452,
159
+ w: 0.7850052651282708
160
+ }
161
+ },
162
+ {
163
+ id: 'fog_box_1',
164
+ label: 'Beside the fog box, deeper',
165
+ c: 'Q-04',
166
+ hint: 'the same medium a little further in; +70 for the march, and where the volume strobed',
167
+ position: { x: -50.28590468756837, y: 4.239170044321892, z: -3.9966900541348767 },
168
+ rotation: {
169
+ x: -0.03789807793750395,
170
+ y: 0.6176094542736352,
171
+ z: 0.02981662961098345,
172
+ w: 0.7850052651282708
173
+ }
174
+ },
175
+ {
176
+ id: 'inside_out',
177
+ label: 'Under the island',
178
+ c: 'C-05',
179
+ hint: 'thin haze over a dark ground — per-froxel noise is most legible here',
180
+ position: { x: -61.55864473831582, y: 23.7158848700074, z: -19.634434309820563 },
181
+ rotation: { x: 0.27745576215003553, y: 0.0922110339369582, z: -0.026764015593898134, w: 0.9559284035634205 }
182
+ },
183
+ ];
184
+
185
+ /**
186
+ * The participating media, carried over from the sibling repo's setup for this scene. Positions are
187
+ * tuned to the model, so they move with it and not with anything here.
188
+ *
189
+ * `target_extinction` rather than `density`: it normalizes across particle specs, so swapping a
190
+ * preset changes how the medium *looks* without changing how much light it stops.
191
+ *
192
+ * @param {Scene} scene
193
+ */
194
+ function add_participating_media(scene) {
195
+
196
+ /**
197
+ * @param {object} spec
198
+ * @param {number[]} spec.position
199
+ * @param {number[]} spec.scale
200
+ * @param {object} spec.particle from MIE_PARTICLES_STANDARD_PRECOMPUTED
201
+ * @param {number} spec.extinction
202
+ * @param {number} spec.fade
203
+ * @param {number} [spec.yaw_degrees]
204
+ */
205
+ function volume({ position, scale, particle, extinction, fade, yaw_degrees = 0 }) {
206
+ const v = new ParticipatingMediaVolume();
207
+
208
+ v.transform.setTranslation(...position);
209
+ v.transform.setScale(...scale);
210
+
211
+ if (yaw_degrees !== 0) {
212
+ v.transform.rotation.fromAxisAngle(Vector3.up, yaw_degrees * DEG_TO_RAD);
213
+ }
214
+
215
+ v.fade_distance = fade;
216
+ v.particle_spec = VolumetricsParticleSpec.fromMeep(particle);
217
+ v.target_extinction = extinction;
218
+
219
+ scene.volumetrics.add(v);
220
+
221
+ return v;
222
+ }
223
+
224
+ const P = MIE_PARTICLES_STANDARD_PRECOMPUTED;
225
+
226
+ // the dense bank along the western edge — the thickest medium in the scene, and the one that
227
+ // fills the frame corners from the `vol_0` view
228
+ volume({
229
+ position: [-55, 4, -5.116],
230
+ scale: [10, 3, 20],
231
+ yaw_degrees: 83,
232
+ particle: P.CLOUD_DROPLET_LARGE,
233
+ extinction: 1,
234
+ fade: 0.5,
235
+ });
236
+
237
+ // graveyard
238
+ volume({
239
+ position: [-51.98019820725889, 7.899311905935347, -15.3228393616169],
240
+ scale: [7, 5, 7],
241
+ particle: P.FOG_DROPLET_SMALL,
242
+ extinction: 0.4,
243
+ fade: 1,
244
+ });
245
+
246
+ // mine
247
+ volume({
248
+ position: [-50.846390607312934, 0.3159949821015605, 17.89429433940319],
249
+ scale: [10, 7, 10],
250
+ particle: P.COARSE_DUST_MEDIUM,
251
+ extinction: 0.4,
252
+ fade: 4,
253
+ });
254
+
255
+ // large crystal
256
+ volume({
257
+ position: [-53.41918585295826, 17.333878151116526, 29.90844167706601],
258
+ scale: [18, 10, 18],
259
+ particle: P.MARITIME_HAZE_MEDIUM,
260
+ extinction: 0.1,
261
+ fade: 4,
262
+ });
263
+
264
+ // the low cloud deck the island sits above — big and thin, so it is what a wide FOV sees at the
265
+ // frame edges
266
+ volume({
267
+ position: [-57.36387203979457, -15, 2.607232848593789],
268
+ scale: [400, 20, 400],
269
+ particle: P.CLOUD_DROPLET_LARGE,
270
+ extinction: 0.1,
271
+ fade: 4,
272
+ });
273
+
274
+ // ball cloud overhead
275
+ volume({
276
+ position: [-51.137423384559284, 33, 5.593067265589105],
277
+ scale: [15, 15, 15],
278
+ particle: P.CLOUD_DROPLET_LARGE,
279
+ extinction: 1,
280
+ fade: 7,
281
+ });
282
+ }
283
+
284
+ /**
285
+ * A rolling mean frame time, so the readout is legible rather than jittering.
286
+ */
287
+ class FrameTimer {
288
+ #samples = new Float64Array(60);
289
+ #count = 0;
290
+ #cursor = 0;
291
+
292
+ add(seconds) {
293
+ this.#samples[this.#cursor] = seconds * 1000;
294
+ this.#cursor = (this.#cursor + 1) % this.#samples.length;
295
+ this.#count = Math.min(this.#count + 1, this.#samples.length);
296
+ }
297
+
298
+ reset() {
299
+ this.#count = 0;
300
+ this.#cursor = 0;
301
+ }
302
+
303
+ get mean_ms() {
304
+ if (this.#count === 0) {
305
+ return 0;
306
+ }
307
+
308
+ let total = 0;
309
+ for (let i = 0; i < this.#count; i++) {
310
+ total += this.#samples[i];
311
+ }
312
+ return total / this.#count;
313
+ }
314
+ }
315
+
316
+ /**
317
+ * Point a directional light along a heading, in place.
318
+ *
319
+ * The sun's azimuth is the control C-03 turns on: the shadow ray is clipped against a frustum whose
320
+ * far plane is degenerate, so a sun direction that lies inside the view cone never exits it. Swing
321
+ * the sun through the frame and the media self-shadowing should be steady; if it switches on as the
322
+ * sun leaves the frustum, that is the finding.
323
+ *
324
+ * @param {DirectionalLight} light
325
+ * @param {number} azimuth_degrees 0 points along +X
326
+ * @param {number} elevation_degrees above the horizon
327
+ */
328
+ function aim_light(light, azimuth_degrees, elevation_degrees) {
329
+ const azimuth = azimuth_degrees * DEG_TO_RAD;
330
+ const elevation = elevation_degrees * DEG_TO_RAD;
331
+
332
+ const horizontal = Math.cos(elevation);
333
+
334
+ // direction the light travels — downward for a sun above the horizon
335
+ const direction = new Vector3(
336
+ Math.cos(azimuth) * horizontal,
337
+ -Math.sin(elevation),
338
+ Math.sin(azimuth) * horizontal
339
+ ).normalize();
340
+
341
+ const q = new Quaternion();
342
+ q.fromUnitVectors(Vector3.forward, direction);
343
+ q.normalize();
344
+
345
+ light.transform_local.setRotation(q.x, q.y, q.z, q.w);
346
+ light.updateMatrices();
347
+ }
348
+
349
+ /**
350
+ * How much each profile capture records. `WORKLOAD` is the highest of the three levels the proposal
351
+ * calls shippable: pass spans, the frame graph, and dispatch/draw counts.
352
+ *
353
+ * Measured on this page it runs ~11 KB a frame over ~393 spans, so ~660 KB/s at 60 Hz — the
354
+ * proposal's estimate of 6 KB assumed ~200 passes and this graph is about twice that. Still small
355
+ * enough that a hand-driven recording will not notice. `VERBOSE` above it adds bind group contents
356
+ * at roughly seven times the rate and wants a frame limit set, not a key you can lean on.
357
+ *
358
+ * @type {number}
359
+ */
360
+ const PROFILE_LEVEL = GPUProfileLevel.WORKLOAD;
361
+
362
+ /**
363
+ * Hand an ArrayBuffer to the browser as a file.
364
+ *
365
+ * @param {ArrayBuffer} bytes
366
+ * @param {string} filename
367
+ */
368
+ function download_bytes(bytes, filename) {
369
+ const url = URL.createObjectURL(new Blob([bytes], { type: 'application/octet-stream' }));
370
+
371
+ const anchor = document.createElement('a');
372
+
373
+ anchor.href = url;
374
+ anchor.download = filename;
375
+
376
+ document.body.appendChild(anchor);
377
+ anchor.click();
378
+ anchor.remove();
379
+
380
+ // Revoking in the same task cancels the download in some browsers — the click only schedules it.
381
+ setTimeout(() => URL.revokeObjectURL(url), 60000);
382
+ }
383
+
384
+ /**
385
+ * `YYYYMMDD-HHMMSS` in local time, matching the naming the other `.sgpt` captures on this machine
386
+ * already use — same sort order, and it reads as the clock you took it by rather than as UTC.
387
+ *
388
+ * @returns {string}
389
+ */
390
+ function file_timestamp() {
391
+ const now = new Date();
392
+
393
+ const pad = value => String(value).padStart(2, '0');
394
+
395
+ return `${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(now.getDate())}`
396
+ + `-${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}`;
397
+ }
398
+
399
+ /**
400
+ * @param {number} bytes
401
+ * @returns {string}
402
+ */
403
+ function format_bytes(bytes) {
404
+ if (bytes < 1024) {
405
+ return `${bytes} B`;
406
+ }
407
+
408
+ if (bytes < 1024 * 1024) {
409
+ return `${(bytes / 1024).toFixed(1)} KiB`;
410
+ }
411
+
412
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MiB`;
413
+ }
414
+
415
+ export async function start() {
416
+
417
+ const element = id => document.getElementById(id);
418
+
419
+ const status_element = element('status');
420
+ const stats_element = element('stats');
421
+
422
+ function set_status(text, kind = '') {
423
+ status_element.textContent = text;
424
+ status_element.className = kind;
425
+ }
426
+
427
+ const canvas = element('view');
428
+
429
+ const context = canvas.getContext('webgpu');
430
+
431
+ if (context === null) {
432
+ set_status('no WebGPU context — this browser has none, or the page is not a secure context', 'fail');
433
+ return;
434
+ }
435
+
436
+ const renderer = new Renderer();
437
+
438
+ set_status('acquiring a device…');
439
+
440
+ await renderer.initialize({ context });
441
+
442
+ const camera = new PerspectiveCamera();
443
+
444
+ camera.near = 0.1;
445
+ camera.far = 120;
446
+ camera.fov = 60 * DEG_TO_RAD;
447
+ // aspect is set by the frame loop, which is the first place the viewport has a size
448
+
449
+ const scene = new Scene();
450
+
451
+ const controller = new OrbitalCameraController(camera, document.body);
452
+
453
+ // handy from the console
454
+ globalThis.renderer = renderer;
455
+ globalThis.scene = scene;
456
+ globalThis.camera = camera;
457
+ globalThis.controller = controller;
458
+
459
+ // ---- scene ----------------------------------------------------------------------------
460
+
461
+ set_status(`loading ${SCENE_URL} …`);
462
+
463
+ try {
464
+ const bundle = await load_gltf(SCENE_URL, { assetManager: await make_playground_asset_manager() });
465
+
466
+ scene.add(bundle.scenes);
467
+ } catch (error) {
468
+ set_status(
469
+ `could not load the scene (${error.message}). Put the assets under test_assets/ — `
470
+ + `see test_assets/README.md.`,
471
+ 'fail'
472
+ );
473
+ console.error(error);
474
+ return;
475
+ }
476
+
477
+ const sun = make_sunlight({ intensity: 0.6, temperature: 5500 });
478
+
479
+ scene.lights.add(sun);
480
+
481
+ add_participating_media(scene);
482
+
483
+ load_default_environment(scene, ENVIRONMENT_URL).then(applied => {
484
+ if (applied) {
485
+ renderer.indicate_view_change();
486
+ }
487
+ });
488
+
489
+ // ---- controls -------------------------------------------------------------------------
490
+
491
+ const frame_timer = new FrameTimer();
492
+
493
+ let paused = false;
494
+
495
+ /** Re-render and drop the frame-time history, since a setting change invalidates it. */
496
+ function repaint() {
497
+ renderer.indicate_view_change();
498
+ frame_timer.reset();
499
+ }
500
+
501
+ // -- views
502
+
503
+ const views_element = element('views');
504
+
505
+ /**
506
+ * Which placement the camera was last sent to, for the profile capture's note — a capture that
507
+ * cannot say where it was taken is much less use a week later.
508
+ * @type {object|null}
509
+ */
510
+ let current_view = null;
511
+
512
+ /** @param {object} view */
513
+ function go_to(view) {
514
+ current_view = view;
515
+
516
+ camera.transform.fromJSON({ translation: view.position, rotation: view.rotation });
517
+
518
+ // the controller holds its own spherical state and would otherwise snap the camera back on
519
+ // the next update
520
+ controller.from_transform(camera.transform);
521
+
522
+ repaint();
523
+ }
524
+
525
+ for (const view of VIEWS) {
526
+ const button = document.createElement('button');
527
+
528
+ button.className = 'view';
529
+ button.title = view.hint;
530
+ button.innerHTML = `<span class="tag">${view.c}</span>${view.label}`;
531
+ button.addEventListener('click', () => {
532
+ go_to(view);
533
+ set_status(view.hint);
534
+ });
535
+
536
+ views_element.appendChild(button);
537
+ }
538
+
539
+ // -- sun
540
+
541
+ let sun_azimuth = 200;
542
+ let sun_elevation = 12;
543
+
544
+ function update_sun() {
545
+ aim_light(sun, sun_azimuth, sun_elevation);
546
+
547
+ element('sun_azimuth_value').textContent = `${sun_azimuth}°`;
548
+ element('sun_elevation_value').textContent = `${sun_elevation}°`;
549
+
550
+ repaint();
551
+ }
552
+
553
+ element('sun_azimuth').addEventListener('input', event => {
554
+ sun_azimuth = Number(event.target.value);
555
+ update_sun();
556
+ });
557
+
558
+ element('sun_elevation').addEventListener('input', event => {
559
+ sun_elevation = Number(event.target.value);
560
+ update_sun();
561
+ });
562
+
563
+ /**
564
+ * Put the sun directly in front of the camera, or directly behind it, at the current elevation.
565
+ *
566
+ * This is the C-03 A/B reduced to two clicks. "Into view" places the sun's direction inside the
567
+ * frustum's direction cone, which is the condition under which `frustum_clip_ray` returns its
568
+ * 1e20 sentinel.
569
+ *
570
+ * @param {boolean} in_front
571
+ */
572
+ function aim_sun_relative_to_camera(in_front) {
573
+ // Straight off the view matrix rather than by rotating a basis vector through the camera's
574
+ // quaternion by hand: the third row of a view matrix is the camera's backward axis, so this
575
+ // is the renderer's own answer and cannot disagree with what is on screen. The hand-rolled
576
+ // version this replaces had a sign error that put the sun behind the camera on both
577
+ // branches, which is invisible in a screenshot and made the C-03 A/B read as a null result.
578
+ // The matrices are rebuilt during render, so a placement made this tick is not in them yet
579
+ // — reading straight after `go_to` would aim the sun at wherever the camera used to be.
580
+ camera.update();
581
+
582
+ const vm = camera.view_matrix;
583
+
584
+ const forward_x = -vm[2];
585
+ const forward_z = -vm[10];
586
+
587
+ // `aim_light` takes the direction light *travels*, so the sun sits opposite it. Placing the
588
+ // sun on the camera's forward axis therefore means aiming its travel back along that axis.
589
+ const toward_sun_x = in_front ? forward_x : -forward_x;
590
+ const toward_sun_z = in_front ? forward_z : -forward_z;
591
+
592
+ const azimuth = Math.atan2(-toward_sun_z, -toward_sun_x) / DEG_TO_RAD;
593
+
594
+ sun_azimuth = Math.round((azimuth % 360 + 360) % 360);
595
+
596
+ element('sun_azimuth').value = String(sun_azimuth);
597
+
598
+ update_sun();
599
+ }
600
+
601
+ element('sun_into_view').addEventListener('click', () => {
602
+ aim_sun_relative_to_camera(true);
603
+ set_status('sun placed in front of the camera — C-03 predicts media self-shadowing vanishes here', 'warn');
604
+ });
605
+
606
+ element('sun_behind').addEventListener('click', () => {
607
+ aim_sun_relative_to_camera(false);
608
+ set_status('sun placed behind the camera — C-03 predicts media self-shadowing returns here', 'pass');
609
+ });
610
+
611
+ // -- camera
612
+
613
+ element('fov').addEventListener('input', event => {
614
+ const degrees = Number(event.target.value);
615
+
616
+ camera.fov = degrees * DEG_TO_RAD;
617
+
618
+ element('fov_value').textContent = `${degrees}°`;
619
+
620
+ repaint();
621
+ });
622
+
623
+ // -- volumetrics knobs, read by GPUViewVolumetrics on every metadata update
624
+
625
+ element('vol_taa').addEventListener('change', event => {
626
+ globalThis.__VOL_TAA_ENABLED = event.target.checked;
627
+ repaint();
628
+ });
629
+
630
+ element('taa').addEventListener('change', event => {
631
+ renderer.feature_taa_enabled = event.target.checked;
632
+ repaint();
633
+ });
634
+
635
+ // Off by default on this page. Auto exposure keeps adapting for seconds after anything changes,
636
+ // which makes a before/after pair differ by more than most of the fixes being judged here do —
637
+ // it read as a large effect with the wrong sign more than once while this page was being built.
638
+ renderer.feature_automatic_exposure_enabled = false;
639
+
640
+ element('auto_exposure').addEventListener('change', event => {
641
+ renderer.feature_automatic_exposure_enabled = event.target.checked;
642
+ repaint();
643
+ });
644
+
645
+ element('froxel_far').addEventListener('input', event => {
646
+ const value = Number(event.target.value);
647
+
648
+ // the slider's top notch means "no split", which is the engine default
649
+ globalThis.__VOL_FROXEL_FAR = value >= 120 ? Infinity : value;
650
+
651
+ element('froxel_far_value').textContent = value >= 120 ? 'off' : `${value} m`;
652
+
653
+ repaint();
654
+ });
655
+
656
+ element('ms_octaves').addEventListener('input', event => {
657
+ const value = Number(event.target.value);
658
+
659
+ globalThis.__VOL_MS_OCTAVES = value;
660
+
661
+ element('ms_octaves_value').textContent = String(value);
662
+
663
+ repaint();
664
+ });
665
+
666
+ // -- profiling: T starts a recording, T again stops it and downloads the capture
667
+
668
+ const profile_status = element('profile_status');
669
+
670
+ /**
671
+ * The recording in progress, or null.
672
+ *
673
+ * The engine never constructs one of these — `Renderer.profile_session` is a nullable field and
674
+ * the null checks around it are the entire integration, which is what keeps the profiler out of
675
+ * a bundle that does not ask for it. The two imports at the top of this file are the whole
676
+ * opt-in.
677
+ *
678
+ * @type {GPUProfileSession|null}
679
+ */
680
+ let profile_session = null;
681
+
682
+ /** Guards against T being leaned on while a stop is still draining. */
683
+ let profile_busy = false;
684
+
685
+ function set_profile_status(text, recording = false) {
686
+ profile_status.textContent = text;
687
+ profile_status.className = recording ? 'recording' : '';
688
+ }
689
+
690
+ function start_profile() {
691
+ const device = renderer.device;
692
+
693
+ const settings = [
694
+ current_view === null ? 'free camera' : current_view.label,
695
+ `fov ${Math.round(camera.fov / DEG_TO_RAD)}°`,
696
+ `volumetric TAA ${(globalThis.__VOL_TAA_ENABLED ?? true) ? 'on' : 'off'}`,
697
+ `${renderer.pixel_ratio.toFixed(2)}x`,
698
+ ];
699
+
700
+ const session = new GPUProfileSession({
701
+ level: PROFILE_LEVEL,
702
+ note: `volumetrics_froxel — ${settings.join(' · ')}`,
703
+ });
704
+
705
+ // Populate before start(): the meta record is written into the stream there and not read
706
+ // again. Without it the capture is unreadable in the sense that matters — a pass taking
707
+ // 0.4 ms says nothing until you know which GPU and at what resolution.
708
+ const meta = session.meta;
709
+
710
+ // `GPUDevice.adapterInfo` is recent enough that a browser running this page may not have it,
711
+ // and several browsers withhold the fields even when they do. Empty is what the format
712
+ // expects in that case.
713
+ const info = device.adapterInfo;
714
+
715
+ if (info !== undefined && info !== null) {
716
+ meta.adapter_vendor = info.vendor ?? "";
717
+ meta.adapter_architecture = info.architecture ?? "";
718
+ meta.adapter_device = info.device ?? "";
719
+ meta.adapter_description = info.description ?? "";
720
+ }
721
+
722
+ meta.features = [...device.features];
723
+ meta.engine_version = package_json.version;
724
+
725
+ session.onBytesWritten.add(bytes => {
726
+ set_profile_status(
727
+ `● recording — ${session.frames_recorded} frames, ${format_bytes(bytes)} · T to stop`,
728
+ true
729
+ );
730
+ });
731
+
732
+ renderer.profile_session = session;
733
+ session.start();
734
+
735
+ profile_session = session;
736
+
737
+ // The timers go quiet without this rather than failing, so the capture still carries the
738
+ // frame graph and the workload — it just has no durations in it. Worth knowing before you
739
+ // record for a minute and then look.
740
+ const timed = device.features.has('timestamp-query');
741
+
742
+ set_profile_status(
743
+ timed
744
+ ? '● recording — 0 frames · T to stop'
745
+ : '● recording without timestamp-query — structure only, no durations · T to stop',
746
+ true
747
+ );
748
+ }
749
+
750
+ /**
751
+ * Wait for frames already submitted to finish landing in the session.
752
+ *
753
+ * A frame is committed when its timestamp readback resolves — a `mapAsync` a frame or two behind
754
+ * submit, see `ShadeGPUCommandContext.profiling_absorbed` — and `record_frame` silently ignores
755
+ * anything arriving after `stop()`. Stopping the instant the key goes down therefore discards
756
+ * the last few frames, which are the ones you were most likely looking at.
757
+ *
758
+ * Drains the queue, then waits for the frame count to stop moving. Capped, because a recording
759
+ * taken while the page is paused never moves at all.
760
+ *
761
+ * @param {GPUProfileSession} session
762
+ */
763
+ async function drain_profile(session) {
764
+ const DEADLINE_MS = 1000;
765
+ const QUIET_MS = 60;
766
+
767
+ await renderer.device.queue.onSubmittedWorkDone();
768
+
769
+ const deadline = performance.now() + DEADLINE_MS;
770
+
771
+ let previous = session.frames_recorded;
772
+
773
+ while (performance.now() < deadline) {
774
+ await new Promise(resolve => setTimeout(resolve, QUIET_MS));
775
+
776
+ if (session.frames_recorded === previous) {
777
+ return;
778
+ }
779
+
780
+ previous = session.frames_recorded;
781
+ }
782
+ }
783
+
784
+ async function stop_profile() {
785
+ const session = profile_session;
786
+
787
+ profile_session = null;
788
+
789
+ // Detach first, so nothing encoded during the drain below joins the recording. Frames
790
+ // already encoded are unaffected: Renderer captures the session into a local before it
791
+ // subscribes to the readback.
792
+ renderer.profile_session = null;
793
+
794
+ set_profile_status('stopping — draining in-flight frames…');
795
+
796
+ await drain_profile(session);
797
+
798
+ const bytes = session.stop();
799
+
800
+ const filename = `volumetrics_froxel-${file_timestamp()}.sgpt`;
801
+
802
+ download_bytes(bytes, filename);
803
+
804
+ // Also to the console, because the status line is the only other place the name appears and
805
+ // a browser that declines the save says nothing about which capture it declined.
806
+ console.log(
807
+ `profile: ${session.frames_recorded} frames, ${format_bytes(bytes.byteLength)}, `
808
+ + `${session.topology_count} topolog${session.topology_count === 1 ? 'y' : 'ies'} -> ${filename}`
809
+ );
810
+
811
+ set_profile_status(
812
+ `${session.frames_recorded} frames, ${format_bytes(bytes.byteLength)}`
813
+ + `, ${session.topology_count} topolog${session.topology_count === 1 ? 'y' : 'ies'}`
814
+ + ` · ${filename}`
815
+ );
816
+ }
817
+
818
+ window.addEventListener('keydown', event => {
819
+ if (event.ctrlKey || event.metaKey || event.altKey || event.repeat) {
820
+ return;
821
+ }
822
+
823
+ // `key` first, because it is the letter actually printed on the key the user pressed —
824
+ // `code` is physical position, so on AZERTY it names the key where T sits on QWERTY. `code`
825
+ // is the fallback for layouts whose `key` is not Latin at all (Cyrillic gives "е" here).
826
+ if (event.key.toLowerCase() !== 't' && event.code !== 'KeyT') {
827
+ return;
828
+ }
829
+
830
+ // the panel's sliders and buttons are focusable; do not steal a key from a field
831
+ const target = event.target;
832
+
833
+ if (target instanceof HTMLInputElement || target instanceof HTMLTextAreaElement || target instanceof HTMLSelectElement) {
834
+ return;
835
+ }
836
+
837
+ if (profile_busy) {
838
+ return;
839
+ }
840
+
841
+ event.preventDefault();
842
+
843
+ profile_busy = true;
844
+
845
+ const done = profile_session === null
846
+ ? Promise.resolve(start_profile())
847
+ : stop_profile();
848
+
849
+ done.catch(error => {
850
+ set_profile_status(`profiling failed: ${error?.message ?? error}`);
851
+ console.error(error);
852
+
853
+ profile_session = null;
854
+ renderer.profile_session = null;
855
+ }).finally(() => {
856
+ profile_busy = false;
857
+ });
858
+ });
859
+
860
+ element('pause').addEventListener('click', () => {
861
+ paused = !paused;
862
+ element('pause').textContent = paused ? 'Resume' : 'Pause';
863
+ });
864
+
865
+ element('log_camera').addEventListener('click', () => {
866
+ const t = camera.transform;
867
+
868
+ const json = {
869
+ position: { x: t.translation_x, y: t.translation_y, z: t.translation_z },
870
+ rotation: { x: t.rotation_x, y: t.rotation_y, z: t.rotation_z, w: t.rotation_w }
871
+ };
872
+
873
+ console.log(JSON.stringify(json, null, 4));
874
+ set_status('camera written to the console', 'pass');
875
+ });
876
+
877
+ // ---- start ----------------------------------------------------------------------------
878
+
879
+ update_sun();
880
+ go_to(VIEWS[0]);
881
+
882
+ controller.movement_speed_scale = 4;
883
+
884
+ // the controller's keyboard device listens on body, and only a focused element gets key events
885
+ document.body.focus();
886
+
887
+ set_status(`loaded — ${scene.instances.instances.length} instances, ${scene.volumetrics.volumes.length} media volumes`, 'pass');
888
+
889
+ // ---- frame loop -----------------------------------------------------------------------
890
+
891
+ const FRAME_RATE_LIMIT = 60;
892
+
893
+ create_frame_loop(time_delta_s => {
894
+ const width = window.innerWidth;
895
+ const height = window.innerHeight;
896
+
897
+ renderer.resize(width, height);
898
+ renderer.pixel_ratio = window.devicePixelRatio;
899
+ camera.aspect = width / height;
900
+
901
+ controller.update();
902
+
903
+ if (paused) {
904
+ return;
905
+ }
906
+
907
+ renderer.render(camera, scene, time_delta_s);
908
+
909
+ frame_timer.add(time_delta_s);
910
+
911
+ const mean = frame_timer.mean_ms;
912
+
913
+ stats_element.textContent =
914
+ `${mean.toFixed(1)} ms · ${mean > 0 ? (1000 / mean).toFixed(0) : '0'} fps`
915
+ + ` · fov ${Math.round(camera.fov / DEG_TO_RAD)}°`;
916
+ }, FRAME_RATE_LIMIT);
917
+ }