@woosh/meep-engine 3.60.4 → 3.60.6

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 (419) hide show
  1. package/CHANGELOG.md +2621 -0
  2. package/CONTRIBUTING.md +7 -0
  3. package/MIGRATION_2_TO_3.md +136 -0
  4. package/SECURITY.md +4 -0
  5. package/editor/EditorCameraNavigation.d.ts +2 -1
  6. package/editor/EditorCameraNavigation.d.ts.map +1 -1
  7. package/editor/EditorCameraNavigation.js +51 -57
  8. package/package.json +6 -2
  9. package/src/engine/EngineHarness.d.ts +3 -9
  10. package/src/engine/EngineHarness.d.ts.map +1 -1
  11. package/src/engine/EngineHarness.js +6 -8
  12. package/src/engine/graphics/camera/makeOrbitalCameraController.d.ts +9 -6
  13. package/src/engine/graphics/camera/makeOrbitalCameraController.d.ts.map +1 -1
  14. package/src/engine/graphics/camera/makeOrbitalCameraController.js +44 -71
  15. package/src/engine/graphics/camera/orbital/MomentumAxis.d.ts +64 -0
  16. package/src/engine/graphics/camera/orbital/MomentumAxis.d.ts.map +1 -0
  17. package/src/engine/graphics/camera/orbital/MomentumAxis.js +187 -0
  18. package/src/engine/graphics/camera/orbital/OrbitalCameraRig.d.ts +86 -0
  19. package/src/engine/graphics/camera/orbital/OrbitalCameraRig.d.ts.map +1 -0
  20. package/src/engine/graphics/camera/orbital/OrbitalCameraRig.js +319 -0
  21. package/src/engine/graphics/ecs/camera/topdown/TopDownCameraController.d.ts +34 -10
  22. package/src/engine/graphics/ecs/camera/topdown/TopDownCameraController.d.ts.map +1 -1
  23. package/src/engine/graphics/ecs/camera/topdown/TopDownCameraController.js +116 -30
  24. package/src/engine/graphics/ecs/camera/topdown/TopDownCameraControllerSystem.d.ts +1 -7
  25. package/src/engine/graphics/ecs/camera/topdown/TopDownCameraControllerSystem.d.ts.map +1 -1
  26. package/src/engine/graphics/ecs/camera/topdown/TopDownCameraControllerSystem.js +1 -37
  27. package/src/engine/graphics/ecs/water/Water.d.ts +8 -0
  28. package/src/engine/graphics/ecs/water/Water.d.ts.map +1 -1
  29. package/src/engine/graphics/ecs/water/Water.js +8 -0
  30. package/src/engine/graphics3/WaterSystem.d.ts.map +1 -1
  31. package/src/engine/graphics3/WaterSystem.js +731 -734
  32. package/src/engine/graphics3/decal/DecalTileShader.d.ts +61 -0
  33. package/src/engine/graphics3/decal/DecalTileShader.d.ts.map +1 -0
  34. package/src/engine/graphics3/decal/DecalTileShader.js +138 -0
  35. package/src/engine/graphics3/decal/GPUDecalRenderer.d.ts +10 -4
  36. package/src/engine/graphics3/decal/GPUDecalRenderer.d.ts.map +1 -1
  37. package/src/engine/graphics3/decal/GPUDecalRenderer.js +149 -44
  38. package/src/engine/graphics3/decal/chunk_decal_tile_vertex.d.ts +25 -0
  39. package/src/engine/graphics3/decal/chunk_decal_tile_vertex.d.ts.map +1 -0
  40. package/src/engine/graphics3/decal/chunk_decal_tile_vertex.js +55 -0
  41. package/src/engine/graphics3/decal/graph_gather_decal_tiles.d.ts +22 -0
  42. package/src/engine/graphics3/decal/graph_gather_decal_tiles.d.ts.map +1 -0
  43. package/src/engine/graphics3/decal/graph_gather_decal_tiles.js +70 -0
  44. package/src/engine/graphics3/decal/shader_decal_composite.d.ts +7 -5
  45. package/src/engine/graphics3/decal/shader_decal_composite.d.ts.map +1 -1
  46. package/src/engine/graphics3/decal/shader_decal_composite.js +48 -44
  47. package/src/engine/graphics3/decal/shader_decal_tile_copy.d.ts +12 -0
  48. package/src/engine/graphics3/decal/shader_decal_tile_copy.d.ts.map +1 -0
  49. package/src/engine/graphics3/decal/shader_decal_tile_copy.js +78 -0
  50. package/src/engine/graphics3/decal/shader_gather_decal_tiles.d.ts +11 -0
  51. package/src/engine/graphics3/decal/shader_gather_decal_tiles.d.ts.map +1 -0
  52. package/src/engine/graphics3/decal/shader_gather_decal_tiles.js +80 -0
  53. package/src/engine/input/devices/mouse/MouseEventButtons.d.ts +13 -0
  54. package/src/engine/input/devices/mouse/MouseEventButtons.d.ts.map +1 -0
  55. package/src/engine/input/devices/mouse/MouseEventButtons.js +23 -0
  56. package/src/engine/input/ecs/controllers/KeyboardCameraController.d.ts +4 -0
  57. package/src/engine/input/ecs/controllers/KeyboardCameraController.d.ts.map +1 -1
  58. package/src/engine/input/ecs/controllers/KeyboardCameraController.js +9 -45
  59. package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
  60. package/src/shade/playground/water_refraction/README.md +5 -6
  61. package/src/shade/playground/water_refraction/index.html +1 -1
  62. package/src/shade/renderer/Renderer.d.ts +11 -0
  63. package/src/shade/renderer/Renderer.d.ts.map +1 -1
  64. package/src/shade/renderer/Renderer.js +50 -6
  65. package/src/shade/renderer/camera/orbital/OrbitalCameraController.d.ts +40 -80
  66. package/src/shade/renderer/camera/orbital/OrbitalCameraController.d.ts.map +1 -1
  67. package/src/shade/renderer/camera/orbital/OrbitalCameraController.js +197 -147
  68. package/src/shade/renderer/fluid/GPUFluidCascade.d.ts +3 -3
  69. package/src/shade/renderer/fluid/GPUFluidCascadeStack.d.ts +1 -1
  70. package/src/shade/renderer/fluid/forcing/GPUFluidForcing.d.ts +6 -6
  71. package/src/shade/renderer/fluid/pressure/GPUFluidPressureSolve.d.ts +1 -1
  72. package/src/shade/renderer/fluid/query/GPUFluidQueries.d.ts +1 -1
  73. package/src/shade/renderer/geometry/sdf/GPUGeometrySDFManager.d.ts +28 -45
  74. package/src/shade/renderer/geometry/sdf/GPUGeometrySDFManager.d.ts.map +1 -1
  75. package/src/shade/renderer/geometry/sdf/GPUGeometrySDFManager.js +111 -119
  76. package/src/shade/renderer/geometry/sdf/cards/GPUMeshCardSets.d.ts +22 -3
  77. package/src/shade/renderer/geometry/sdf/cards/GPUMeshCardSets.d.ts.map +1 -1
  78. package/src/shade/renderer/geometry/sdf/cards/GPUMeshCardSets.js +84 -3
  79. package/src/shade/renderer/geometry/virtual/proxy/vgeo_proxy_triangle_limit.d.ts +19 -0
  80. package/src/shade/renderer/geometry/virtual/proxy/vgeo_proxy_triangle_limit.d.ts.map +1 -0
  81. package/src/shade/renderer/geometry/virtual/proxy/vgeo_proxy_triangle_limit.js +25 -0
  82. package/src/shade/renderer/geometry/virtual/proxy/vgeo_to_proxy_mesh.d.ts +31 -0
  83. package/src/shade/renderer/geometry/virtual/proxy/vgeo_to_proxy_mesh.d.ts.map +1 -0
  84. package/src/shade/renderer/geometry/virtual/proxy/vgeo_to_proxy_mesh.js +343 -0
  85. package/src/shade/renderer/geometry/virtual/runtime/VirtualGeometry.d.ts +15 -0
  86. package/src/shade/renderer/geometry/virtual/runtime/VirtualGeometry.d.ts.map +1 -1
  87. package/src/shade/renderer/geometry/virtual/runtime/VirtualGeometry.js +38 -0
  88. package/src/shade/renderer/geometry/virtual/runtime/vgeo_meshlet_metadata_write.d.ts +17 -0
  89. package/src/shade/renderer/geometry/virtual/runtime/vgeo_meshlet_metadata_write.d.ts.map +1 -0
  90. package/src/shade/renderer/geometry/virtual/runtime/vgeo_meshlet_metadata_write.js +29 -0
  91. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_meshlet_batch.d.ts.map +1 -1
  92. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_meshlet_batch.js +7 -13
  93. package/src/shade/renderer/geometry/virtual/sdf/VGeoSDFProxies.d.ts +28 -59
  94. package/src/shade/renderer/geometry/virtual/sdf/VGeoSDFProxies.d.ts.map +1 -1
  95. package/src/shade/renderer/geometry/virtual/sdf/VGeoSDFProxies.js +84 -789
  96. package/src/shade/renderer/global_illumination/probe_clipmap/GPUProbeClipmapGI.d.ts.map +1 -1
  97. package/src/shade/renderer/global_illumination/probe_clipmap/GPUProbeClipmapGI.js +1 -8
  98. package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSceneHolds.d.ts +7 -6
  99. package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSceneHolds.d.ts.map +1 -1
  100. package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSceneHolds.js +12 -11
  101. package/src/shade/renderer/global_illumination/probe_clipmap/brick/mesh/chunk_mesh_brick_instance.d.ts +2 -2
  102. package/src/shade/renderer/global_illumination/probe_clipmap/brick/mesh/chunk_mesh_brick_instance.js +2 -2
  103. package/src/shade/renderer/global_illumination/probe_clipmap/probe/GPUProbeClipmap.d.ts +1 -4
  104. package/src/shade/renderer/global_illumination/probe_clipmap/probe/GPUProbeClipmap.d.ts.map +1 -1
  105. package/src/shade/renderer/global_illumination/probe_clipmap/probe/GPUProbeClipmap.js +1 -10
  106. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_hit_surface.d.ts +5 -15
  107. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_hit_surface.d.ts.map +1 -1
  108. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_hit_surface.js +6 -34
  109. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_shade.d.ts.map +1 -1
  110. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_shade.js +1 -7
  111. package/src/shade/renderer/hiz/HZBFootprint.d.ts +14 -0
  112. package/src/shade/renderer/hiz/HZBFootprint.d.ts.map +1 -0
  113. package/src/shade/renderer/hiz/HZBFootprint.js +16 -0
  114. package/src/shade/renderer/hiz/HZB_FOOTPRINT_X.d.ts +10 -0
  115. package/src/shade/renderer/hiz/HZB_FOOTPRINT_X.d.ts.map +1 -0
  116. package/src/shade/renderer/hiz/HZB_FOOTPRINT_X.js +11 -0
  117. package/src/shade/renderer/hiz/HZB_FOOTPRINT_Y.d.ts +10 -0
  118. package/src/shade/renderer/hiz/HZB_FOOTPRINT_Y.d.ts.map +1 -0
  119. package/src/shade/renderer/hiz/HZB_FOOTPRINT_Y.js +11 -0
  120. package/src/shade/renderer/hiz/HierarchicalZBuffer.d.ts +0 -7
  121. package/src/shade/renderer/hiz/HierarchicalZBuffer.d.ts.map +1 -1
  122. package/src/shade/renderer/hiz/HierarchicalZBuffer.js +405 -350
  123. package/src/shade/renderer/hiz/chunk_hzb_footprint_x.d.ts +12 -0
  124. package/src/shade/renderer/hiz/chunk_hzb_footprint_x.d.ts.map +1 -0
  125. package/src/shade/renderer/hiz/chunk_hzb_footprint_x.js +27 -0
  126. package/src/shade/renderer/hiz/chunk_hzb_footprint_y.d.ts +12 -0
  127. package/src/shade/renderer/hiz/chunk_hzb_footprint_y.d.ts.map +1 -0
  128. package/src/shade/renderer/hiz/chunk_hzb_footprint_y.js +27 -0
  129. package/src/shade/renderer/hiz/hzb_footprint_texels.d.ts +14 -0
  130. package/src/shade/renderer/hiz/hzb_footprint_texels.d.ts.map +1 -0
  131. package/src/shade/renderer/hiz/hzb_footprint_texels.js +34 -0
  132. package/src/shade/renderer/hiz/shader_hzb_reduce_any.d.ts +0 -5
  133. package/src/shade/renderer/hiz/shader_hzb_reduce_any.d.ts.map +1 -1
  134. package/src/shade/renderer/hiz/shader_hzb_reduce_any.js +37 -57
  135. package/src/shade/renderer/hiz/shader_hzb_reduce_mip0.d.ts +11 -0
  136. package/src/shade/renderer/hiz/shader_hzb_reduce_mip0.d.ts.map +1 -0
  137. package/src/shade/renderer/hiz/{shader_hzb_reduce_mip0_clip.js → shader_hzb_reduce_mip0.js} +120 -111
  138. package/src/shade/renderer/loader/gltf/tiny-gltf.d.ts +1 -1
  139. package/src/shade/renderer/material/GPUMaterialManager.d.ts +11 -1
  140. package/src/shade/renderer/material/GPUMaterialManager.d.ts.map +1 -1
  141. package/src/shade/renderer/material/GPUMaterialManager.js +16 -8
  142. package/src/shade/renderer/material/resident/GPUResidentMaterialContext.d.ts +12 -12
  143. package/src/shade/renderer/material/resident/GPUResidentMaterialContext.d.ts.map +1 -1
  144. package/src/shade/renderer/material/resident/GPUResidentMaterialContext.js +17 -118
  145. package/src/shade/renderer/postprocess/gtao/GTAO.d.ts +7 -3
  146. package/src/shade/renderer/postprocess/gtao/GTAO.d.ts.map +1 -1
  147. package/src/shade/renderer/postprocess/gtao/GTAO.js +26 -13
  148. package/src/shade/renderer/postprocess/gtao/ao_reproject_shader.d.ts +14 -0
  149. package/src/shade/renderer/postprocess/gtao/ao_reproject_shader.d.ts.map +1 -1
  150. package/src/shade/renderer/postprocess/gtao/ao_reproject_shader.js +196 -68
  151. package/src/shade/renderer/postprocess/gtao/fragment_shader_gtao.d.ts.map +1 -1
  152. package/src/shade/renderer/postprocess/gtao/fragment_shader_gtao.js +42 -28
  153. package/src/shade/renderer/postprocess/gtao/fragment_shader_gtao_with_irradiance.d.ts.map +1 -1
  154. package/src/shade/renderer/postprocess/gtao/fragment_shader_gtao_with_irradiance.js +429 -414
  155. package/src/shade/renderer/postprocess/gtao/shader_gtao_spatial_denoise.d.ts +16 -0
  156. package/src/shade/renderer/postprocess/gtao/shader_gtao_spatial_denoise.d.ts.map +1 -0
  157. package/src/shade/renderer/postprocess/gtao/shader_gtao_spatial_denoise.js +156 -0
  158. package/src/shade/renderer/scene/GPUSceneContext.d.ts +2 -2
  159. package/src/shade/renderer/scene/GPUSceneContext.js +5 -5
  160. package/src/shade/renderer/shadow/map/GPUSceneShadowmapContext.d.ts.map +1 -1
  161. package/src/shade/renderer/shadow/map/GPUSceneShadowmapContext.js +7 -2
  162. package/src/shade/renderer/water/GPUWaterRenderer.d.ts +0 -6
  163. package/src/shade/renderer/water/GPUWaterRenderer.d.ts.map +1 -1
  164. package/src/shade/renderer/water/GPUWaterRenderer.js +12 -17
  165. package/src/shade/renderer/water/medium/graph_water_meshlets.d.ts +35 -0
  166. package/src/shade/renderer/water/medium/graph_water_meshlets.d.ts.map +1 -0
  167. package/src/shade/renderer/water/medium/graph_water_meshlets.js +89 -0
  168. package/src/shade/renderer/water/query/GPUWaterQueries.d.ts +1 -1
  169. package/src/shade/renderer/water/shader_water.d.ts.map +1 -1
  170. package/src/shade/renderer/water/shader_water.js +22 -13
  171. package/src/shade/renderer/water/shader_water_avboit.d.ts.map +1 -1
  172. package/src/shade/renderer/water/shader_water_avboit.js +7 -7
  173. package/src/shade/renderer/water/surface/graph_water_tessellation.d.ts +1 -1
  174. package/src/shade/renderer/water/surface/graph_water_tessellation.js +1 -1
  175. package/src/shade/wgsl/emulator/WGSLJavaScriptCompiler.js +9 -1
  176. package/editor/REWORK_PLAN_2026_08.md +0 -692
  177. package/src/CODEC_LAYOUT_VERIFICATION.md +0 -419
  178. package/src/CORRECTNESS_HUNT_2026_07_24.md +0 -142
  179. package/src/DUPLICATION_AUDIT_2026_08_07/00_core_reuse_surface.md +0 -738
  180. package/src/DUPLICATION_AUDIT_2026_08_07/A_core_math_geom.md +0 -79
  181. package/src/DUPLICATION_AUDIT_2026_08_07/B_core_data.md +0 -93
  182. package/src/DUPLICATION_AUDIT_2026_08_07/C_gfx_render.md +0 -80
  183. package/src/DUPLICATION_AUDIT_2026_08_07/D_gfx_features.md +0 -164
  184. package/src/DUPLICATION_AUDIT_2026_08_07/E_physics.md +0 -69
  185. package/src/DUPLICATION_AUDIT_2026_08_07/F_sound.md +0 -110
  186. package/src/DUPLICATION_AUDIT_2026_08_07/G_net_save.md +0 -129
  187. package/src/DUPLICATION_AUDIT_2026_08_07/H_engine_misc.md +0 -72
  188. package/src/DUPLICATION_AUDIT_2026_08_07/I_generation_view.md +0 -74
  189. package/src/DUPLICATION_AUDIT_2026_08_07/K_crosscutting.md +0 -169
  190. package/src/DUPLICATION_AUDIT_2026_08_07.md +0 -254
  191. package/src/DUPLICATION_AUDIT_REMEDIATION.md +0 -250
  192. package/src/DUPLICATION_REVIEW_2026_10_24.md +0 -1072
  193. package/src/REVIEW_2026_08_06/adv-core.md +0 -511
  194. package/src/REVIEW_2026_08_06/adv-gfx.md +0 -480
  195. package/src/REVIEW_2026_08_06/adv-view.md +0 -446
  196. package/src/REVIEW_2026_08_06/api-core.md +0 -296
  197. package/src/REVIEW_2026_08_06/api-engine.md +0 -195
  198. package/src/REVIEW_2026_08_06/bvh-phys-sound-net.md +0 -507
  199. package/src/REVIEW_2026_08_06/core-binary.md +0 -348
  200. package/src/REVIEW_2026_08_06/core-collection.md +0 -376
  201. package/src/REVIEW_2026_08_06/core-math.md +0 -221
  202. package/src/REVIEW_2026_08_06/core-process.md +0 -266
  203. package/src/REVIEW_2026_08_06/core-science.md +0 -223
  204. package/src/REVIEW_2026_08_06/engine-ai-nav.md +0 -279
  205. package/src/REVIEW_2026_08_06/engine-animation.md +0 -167
  206. package/src/REVIEW_2026_08_06/engine-asset.md +0 -166
  207. package/src/REVIEW_2026_08_06/engine-ecs.md +0 -920
  208. package/src/REVIEW_2026_08_06/engine-input.md +0 -197
  209. package/src/REVIEW_2026_08_06/engine-misc.md +0 -248
  210. package/src/REVIEW_2026_08_06/engine-terrain.md +0 -52
  211. package/src/REVIEW_2026_08_06/generation.md +0 -176
  212. package/src/REVIEW_2026_08_06/geom-2d.md +0 -434
  213. package/src/REVIEW_2026_08_06/geom-3d.md +0 -117
  214. package/src/REVIEW_2026_08_06/gfx-gi.md +0 -143
  215. package/src/REVIEW_2026_08_06/gfx-render.md +0 -740
  216. package/src/REVIEW_2026_08_06/gfx-texture.md +0 -690
  217. package/src/REVIEW_2026_08_06/prior-criticals.md +0 -49
  218. package/src/REVIEW_2026_08_06/prior-highs.md +0 -138
  219. package/src/REVIEW_2026_08_06/test-gaps.md +0 -364
  220. package/src/REVIEW_2026_08_06/view.md +0 -667
  221. package/src/REVIEW_2026_08_06.md +0 -610
  222. package/src/core/binary/lz4/LZ4_HIGH_COMPRESSION_PLAN_2026_09_14.md +0 -542
  223. package/src/core/bvh2/bvh3/NOTES.md +0 -2
  224. package/src/core/bvh8/NOTES.md +0 -63
  225. package/src/core/cache/wtinylfu/benchmark/CacheWTinylfu.bench.md +0 -243
  226. package/src/core/color/COLOR_FIX_PLAN_2026_08_28.md +0 -417
  227. package/src/core/color/COLOR_REVIEW_2026_08_28.md +0 -779
  228. package/src/core/geom/2d/polygon/TRIANGULATION_DESIGN.md +0 -433
  229. package/src/core/geom/3d/atlas/ATLAS_PORT_PLAN.md +0 -486
  230. package/src/core/geom/3d/atlas/BFF_PLAN.md +0 -174
  231. package/src/core/geom/3d/atlas/REVIEW_02.md +0 -392
  232. package/src/core/geom/3d/gjk/GJK_REVIEW_NOTES.md +0 -146
  233. package/src/core/geom/3d/gjk/NOTES.md +0 -8
  234. package/src/core/geom/3d/topology/TOPO_MESH_RETIREMENT_PLAN.md +0 -135
  235. package/src/core/geom/GEOMETRY_REVIEW_002_API_ISSUES.md +0 -381
  236. package/src/core/geom/GEOMETRY_REVIEW_002_NAMING.md +0 -684
  237. package/src/core/geom/MANIFOLD_COMPARISON_2026_08_01.md +0 -554
  238. package/src/core/model/reactive/evaluation/PREDICATE_APPLICABILITY_PROPOSAL.md +0 -446
  239. package/src/engine/REVIEW_2026_07_03.md +0 -211
  240. package/src/engine/asset/loaders/image/IMAGE_CODEC_REVIEW_2026_08_28.md +0 -815
  241. package/src/engine/control/first-person/DESIGN.md +0 -658
  242. package/src/engine/control/first-person/DESIGN_COLLISION.md +0 -382
  243. package/src/engine/control/first-person/DESIGN_EXTENSIONS.md +0 -563
  244. package/src/engine/control/first-person/TODO.md +0 -160
  245. package/src/engine/ecs/ASYNC_SYSTEMS_PROPOSAL.md +0 -531
  246. package/src/engine/ecs/HIERARCHY_REVIEW.md +0 -717
  247. package/src/engine/ecs/async/WORKER_SYSTEM_PLAN.md +0 -262
  248. package/src/engine/ecs/ik/PLAN.md +0 -179
  249. package/src/engine/ecs/storage/binary/SERIALIZATION_COVERAGE.md +0 -272
  250. package/src/engine/ecs/storage/binary/object-v2/DESIGN.md +0 -199
  251. package/src/engine/ecs/transform/TRANSFORM64_MIGRATION_PLAN_2026_09_04.md +0 -521
  252. package/src/engine/graphics/CONTEXT_LOSS_RECOVERY_PLAN.md +0 -463
  253. package/src/engine/graphics/ecs/camera/pp/NOTES.md +0 -48
  254. package/src/engine/graphics/render/frame_graph/CORRECTNESS_FIXES_2026_09_04.md +0 -659
  255. package/src/engine/graphics/render/frame_graph/FRAME_GRAPH_RECOMMENDATIONS.md +0 -235
  256. package/src/engine/graphics/sh3/path_tracer/REVIEW_001_ACTION_PLAN.md +0 -490
  257. package/src/engine/graphics/texture/sampler/compare/FLIP_IMPLEMENTATION_PLAN.md +0 -466
  258. package/src/engine/input/GAMEPAD_API_PROPOSAL.md +0 -717
  259. package/src/engine/input/ecs/ism/ISM_DESIGN.md +0 -1523
  260. package/src/engine/input/ecs/ism/ISM_IMPLEMENTATION_PLAN.md +0 -627
  261. package/src/engine/navigation/mesh/OVERLAPPING_SURFACES_2026_08_25.md +0 -661
  262. package/src/engine/navigation/mesh/PATHFINDING_PLAN.md +0 -229
  263. package/src/engine/network/CONGESTION_CONTROL.md +0 -87
  264. package/src/engine/network/MESSAGE_TRANSPORT.md +0 -364
  265. package/src/engine/network/SMOOTHNESS_DESIGN.md +0 -400
  266. package/src/engine/physics/cloth/MEASUREMENTS.md +0 -5304
  267. package/src/engine/physics/cloth/PLAN.md +0 -3056
  268. package/src/engine/physics/fluid/GPU_FLUID_CASCADES_PROPOSAL_2026_10_16.md +0 -1382
  269. package/src/engine/physics/fluid/REVIEW_02_PLAN.md +0 -243
  270. package/src/engine/physics/fluid/solver/NOTES.md +0 -46
  271. package/src/engine/sound/SOUND_EMITTER_RETIREMENT_PLAN.md +0 -536
  272. package/src/engine/sound/simulation/IMPLEMENTATION_PLAN.md +0 -432
  273. package/src/engine/sound/simulation/PROBE_PLACEMENT.md +0 -250
  274. package/src/engine/sound/simulation/PROBE_TRANSFER_RESEARCH.md +0 -322
  275. package/src/engine/sound/simulation/REFLECTION_IR_PLAN.md +0 -261
  276. package/src/engine/sound/simulation/VISIBILITY_GRAPH_PLAN.md +0 -121
  277. package/src/engine/sound/sopra/IMPLEMENTATION_PLAN.md +0 -999
  278. package/src/format/image/avif/DECISIONS.md +0 -545
  279. package/src/format/image/jpeg/NOTES.md +0 -3
  280. package/src/format/scene/gltf/MESHOPT_COMPRESSION_PLAN.md +0 -814
  281. package/src/generation/COORDINATES.md +0 -54
  282. package/src/generation/REVIEW_01_ACTION_PLAN.md +0 -628
  283. package/src/generation/foliage/ANNUAL_SHOOTS_BRIEF_2026_10_25.md +0 -220
  284. package/src/generation/foliage/CROWN_FILLING_BRIEF_2026_10_26.md +0 -172
  285. package/src/generation/foliage/CROWN_FULLNESS_BRIEF_2026_10_26.md +0 -86
  286. package/src/generation/foliage/FOLIAGE_DESIGN.md +0 -547
  287. package/src/generation/foliage/FOLIAGE_PLAN.md +0 -198
  288. package/src/generation/foliage/LEAF_ARCHITECTURE_BRIEF_2026_10_26.md +0 -158
  289. package/src/generation/foliage/LOD_REWORK_BRIEF_2026_10_27.md +0 -521
  290. package/src/generation/foliage/REAL_TIME_TREES_BRIEF_2026_10_27.md +0 -86
  291. package/src/generation/foliage/SELF_ORGANIZING_FRAMEWORK_BRIEF_2026_10_26.md +0 -347
  292. package/src/generation/foliage/SHOOT_SYSTEMS_ALL_SPECIES_BRIEF_2026_10_26.md +0 -248
  293. package/src/generation/foliage/SPEEDTREE_COMPARISON_2026_10_24.md +0 -694
  294. package/src/generation/foliage/TWO_FRONT_ENDS_BRIEF_2026_10_28.md +0 -552
  295. package/src/shade/RENDERER_CONTRACT.md +0 -697
  296. package/src/shade/SHADER_F16_PROPOSAL_2026_10_24.md +0 -263
  297. package/src/shade/device/IMMEDIATE_DATA_PLAN_2026_08_28.md +0 -569
  298. package/src/shade/device/PASS_ENCODER_PROPOSAL_2026_08_31.md +0 -755
  299. package/src/shade/device/mock/EXTENSION_PROPOSAL_2026_08_22.md +0 -458
  300. package/src/shade/device/mock/IMPLEMENTATION_PLAN.md +0 -255
  301. package/src/shade/device/mock/REVIEW_2026_08_22.md +0 -527
  302. package/src/shade/device/timing/GPU_PROFILER_PROPOSAL_2026_08_28.md +0 -894
  303. package/src/shade/device/timing/profile/SGPT_FORMAT.md +0 -345
  304. package/src/shade/renderer/NOTES.md +0 -78
  305. package/src/shade/renderer/PLAN.md +0 -8
  306. package/src/shade/renderer/animation/ADDITIVE_BLENDING_PLAN.md +0 -145
  307. package/src/shade/renderer/animation/SKINNING_FRAME_PLAN.md +0 -357
  308. package/src/shade/renderer/atmosphere/NOTES.md +0 -6
  309. package/src/shade/renderer/camera/orbital/MomentumAxis.d.ts +0 -76
  310. package/src/shade/renderer/camera/orbital/MomentumAxis.d.ts.map +0 -1
  311. package/src/shade/renderer/camera/orbital/MomentumAxis.js +0 -164
  312. package/src/shade/renderer/camera/orbital/OrbitalCameraConfig.d.ts +0 -45
  313. package/src/shade/renderer/camera/orbital/OrbitalCameraConfig.d.ts.map +0 -1
  314. package/src/shade/renderer/camera/orbital/OrbitalCameraConfig.js +0 -66
  315. package/src/shade/renderer/camera/orbital/OrbitalCameraInput.d.ts +0 -98
  316. package/src/shade/renderer/camera/orbital/OrbitalCameraInput.d.ts.map +0 -1
  317. package/src/shade/renderer/camera/orbital/OrbitalCameraInput.js +0 -184
  318. package/src/shade/renderer/camera/orbital/OrbitalCameraRig.d.ts +0 -175
  319. package/src/shade/renderer/camera/orbital/OrbitalCameraRig.d.ts.map +0 -1
  320. package/src/shade/renderer/camera/orbital/OrbitalCameraRig.js +0 -411
  321. package/src/shade/renderer/camera/orbital/README.md +0 -81
  322. package/src/shade/renderer/camera/orbital/index.d.ts +0 -6
  323. package/src/shade/renderer/camera/orbital/index.d.ts.map +0 -1
  324. package/src/shade/renderer/camera/orbital/index.js +0 -9
  325. package/src/shade/renderer/extension/RENDER_EXTENSION_DESIGN.md +0 -964
  326. package/src/shade/renderer/fluid/GPU_FLUID_OCCUPANCY_AND_SOTA_REVIEW_2026_10_26.md +0 -361
  327. package/src/shade/renderer/geometry/bvh/BLAS_REFIT_BATCH_PLAN.md +0 -619
  328. package/src/shade/renderer/geometry/meshlet/COMPACTION_PLAN_2026_09_16.md +0 -674
  329. package/src/shade/renderer/geometry/sdf/NOTES.md +0 -6
  330. package/src/shade/renderer/geometry/virtual/ATTRIBUTE_PLAN_2026_09_18.md +0 -540
  331. package/src/shade/renderer/geometry/virtual/MICRON_COMPARISON_2026_08_20.md +0 -305
  332. package/src/shade/renderer/geometry/virtual/VGEO_FORMAT.md +0 -913
  333. package/src/shade/renderer/geometry/virtual/VIRTUAL_GEOMETRY_DESIGN.md +0 -787
  334. package/src/shade/renderer/geometry/virtual/VIRTUAL_GEOMETRY_PLAN.md +0 -481
  335. package/src/shade/renderer/geometry/virtual/VK_LOD_CLUSTERS_COMPARISON_2026_08_20.md +0 -301
  336. package/src/shade/renderer/geometry/virtual/WEDGE_PLAN_2026_09_18.md +0 -448
  337. package/src/shade/renderer/geometry/virtual/runtime/CULL_PLAN_2026_09_16.md +0 -385
  338. package/src/shade/renderer/geometry/virtual/runtime/CUT_PLAN_2026_09_14.md +0 -656
  339. package/src/shade/renderer/geometry/virtual/runtime/REVIEW_LEDGER.md +0 -308
  340. package/src/shade/renderer/geometry/virtual/runtime/RUNTIME_PLAN.md +0 -657
  341. package/src/shade/renderer/geometry/virtual/runtime/RUNTIME_PLAN_2026_09_14.md +0 -551
  342. package/src/shade/renderer/geometry/virtual/runtime/SHADOW_PLAN_2026_09_28.md +0 -529
  343. package/src/shade/renderer/geometry/virtual/sdf/VGEO_SDF.d.ts +0 -47
  344. package/src/shade/renderer/geometry/virtual/sdf/VGEO_SDF.d.ts.map +0 -1
  345. package/src/shade/renderer/geometry/virtual/sdf/VGEO_SDF.js +0 -80
  346. package/src/shade/renderer/geometry/virtual/sdf/shader_vgeo_sdf_collect.d.ts +0 -16
  347. package/src/shade/renderer/geometry/virtual/sdf/shader_vgeo_sdf_collect.d.ts.map +0 -1
  348. package/src/shade/renderer/geometry/virtual/sdf/shader_vgeo_sdf_collect.js +0 -67
  349. package/src/shade/renderer/geometry/virtual/sdf/shader_vgeo_sdf_decode.d.ts +0 -17
  350. package/src/shade/renderer/geometry/virtual/sdf/shader_vgeo_sdf_decode.d.ts.map +0 -1
  351. package/src/shade/renderer/geometry/virtual/sdf/shader_vgeo_sdf_decode.js +0 -145
  352. package/src/shade/renderer/geometry/virtual/sdf/shader_vgeo_sdf_traverse.d.ts +0 -12
  353. package/src/shade/renderer/geometry/virtual/sdf/shader_vgeo_sdf_traverse.d.ts.map +0 -1
  354. package/src/shade/renderer/geometry/virtual/sdf/shader_vgeo_sdf_traverse.js +0 -105
  355. package/src/shade/renderer/global_illumination/lpv/NOTES.md +0 -3
  356. package/src/shade/renderer/global_illumination/probe_clipmap/PROBE_CLIPMAP_PROPOSAL_2026_10_27.md +0 -4306
  357. package/src/shade/renderer/global_illumination/probe_clipmap/PROBE_RAYS_VARIABLE_AND_SORTED_SCOPE_2026_10_30.md +0 -410
  358. package/src/shade/renderer/global_illumination/sharc/NOTES.md +0 -8
  359. package/src/shade/renderer/gpu_primitive/bvh/GPU_BVH_BUILD_PLAN.md +0 -1369
  360. package/src/shade/renderer/gpu_primitive/bvh/PHASE1_HANDOVER.md +0 -202
  361. package/src/shade/renderer/gpu_primitive/indirect/NOTES.md +0 -94
  362. package/src/shade/renderer/hiz/NOTES.md +0 -3
  363. package/src/shade/renderer/hiz/shader_hzb_reduce_mip0_clip.d.ts +0 -8
  364. package/src/shade/renderer/hiz/shader_hzb_reduce_mip0_clip.d.ts.map +0 -1
  365. package/src/shade/renderer/light/cluster/NOTES.md +0 -11
  366. package/src/shade/renderer/light/cluster/PROPOSAL_ALGORITHM_2026_10_16.md +0 -524
  367. package/src/shade/renderer/light/cluster/PROPOSAL_HIERARCHICAL_2026_10_16.md +0 -1101
  368. package/src/shade/renderer/material/resident/shader_resident_texture_means.d.ts +0 -24
  369. package/src/shade/renderer/material/resident/shader_resident_texture_means.d.ts.map +0 -1
  370. package/src/shade/renderer/material/resident/shader_resident_texture_means.js +0 -120
  371. package/src/shade/renderer/particles/DESIGN.md +0 -1011
  372. package/src/shade/renderer/particles/EMITTER_GLOBAL_STATE_PLAN_2026_10_29.md +0 -280
  373. package/src/shade/renderer/particles/ORIENTATION_PLAN_2026_10_15.md +0 -453
  374. package/src/shade/renderer/path_tracer/PATH_TRACED_MEDIA_PLAN_2026_10_02.md +0 -520
  375. package/src/shade/renderer/postprocess/bloom/NOTES.md +0 -6
  376. package/src/shade/renderer/postprocess/fsr/NOTES.md +0 -4
  377. package/src/shade/renderer/postprocess/gtao/NOTES.md +0 -6
  378. package/src/shade/renderer/postprocess/nss/NOTES.md +0 -332
  379. package/src/shade/renderer/postprocess/perspective/NOTES.md +0 -3
  380. package/src/shade/renderer/postprocess/sscs/NOTES.md +0 -5
  381. package/src/shade/renderer/postprocess/ssr/NOTES.md +0 -14
  382. package/src/shade/renderer/postprocess/ssr/REPROJECTION_PROPOSAL.md +0 -286
  383. package/src/shade/renderer/postprocess/ssr/prefilter/NOTES.md +0 -39
  384. package/src/shade/renderer/postprocess/taa/NOTES.md +0 -11
  385. package/src/shade/renderer/postprocess/upscale-dither/NOTES.md +0 -78
  386. package/src/shade/renderer/postprocess/vrs/NOTES.md +0 -4
  387. package/src/shade/renderer/rasterize/native/avboit/AVBOIT_LEDGER.md +0 -1218
  388. package/src/shade/renderer/rasterize/native/avboit/AVBOIT_PLAN_2026_09_03.md +0 -600
  389. package/src/shade/renderer/restir/reservoir/NOTES.md +0 -6
  390. package/src/shade/renderer/shader/chunk/atmosphere/NOTES.md +0 -6
  391. package/src/shade/renderer/shader/chunk/brdf/FRESNEL_BRIEF_2026_10_16.md +0 -391
  392. package/src/shade/renderer/shader/chunk/color/tonemap/ACES_TONE_MAPPING_BRIEF_2026_10_17.md +0 -153
  393. package/src/shade/renderer/shader/chunk/color/tonemap/HDR_TONE_MAPPING_FORMS_BRIEF_2026_10_16.md +0 -322
  394. package/src/shade/renderer/shader/chunk/color/tonemap/SDR_TONE_MAPPING_BRIEF_2026_10_16.md +0 -247
  395. package/src/shade/renderer/shader/chunk/fsr/NOTES.md +0 -3
  396. package/src/shade/renderer/shader/chunk/fsr/spd/NOTES.md +0 -4
  397. package/src/shade/renderer/shader/chunk/geometry/ray/NOTES.md +0 -4
  398. package/src/shade/renderer/shader/chunk/material/MULTISCATTER.md +0 -159
  399. package/src/shade/renderer/shader/chunk/mboit/NOTES.md +0 -38
  400. package/src/shade/renderer/shader/chunk/spherical_harmonics/l1i/NOTES.md +0 -3
  401. package/src/shade/renderer/shader/chunk/spherical_harmonics/sampling/NOTES.md +0 -8
  402. package/src/shade/renderer/shader/compiler/SHADER_SWITCH_PROPOSAL_2026_10_16.md +0 -927
  403. package/src/shade/renderer/shadow/contact/FINDINGS.md +0 -413
  404. package/src/shade/renderer/shadow/map/csm/NOTES.md +0 -5
  405. package/src/shade/renderer/shadow/ray/NOTES.md +0 -31
  406. package/src/shade/renderer/shadow/sdf/NOTES.md +0 -9
  407. package/src/shade/renderer/shadow/sdf/brixelizer/NOTES.md +0 -5
  408. package/src/shade/renderer/shadow/sdf/cascade/NOTES.md +0 -9
  409. package/src/shade/renderer/shadow/vsm/NOTES.md +0 -12
  410. package/src/shade/renderer/shadow/vsm/PLAN.md +0 -442
  411. package/src/shade/renderer/shadow/vsm/REVIEW_LEDGER.md +0 -43
  412. package/src/shade/renderer/texture/COMPRESSED_TEXTURE_PLAN_2026_09_01.md +0 -2444
  413. package/src/shade/renderer/texture/mipmaps/NOTES.md +0 -3
  414. package/src/shade/renderer/volumetrics/NOTES.md +0 -1329
  415. package/src/shade/renderer/volumetrics/VOLUMETRIC_NOISE_PLAN_2026_10_15.md +0 -336
  416. package/src/shade/renderer/water/medium/GPUWaterBodies.d.ts +0 -23
  417. package/src/shade/renderer/water/medium/GPUWaterBodies.d.ts.map +0 -1
  418. package/src/shade/renderer/water/medium/GPUWaterBodies.js +0 -75
  419. package/src/shade/wgsl/NOTES.md +0 -4
@@ -1,697 +0,0 @@
1
- # The renderer contract
2
-
3
- meep 3.0 has one renderer, and this is what it promises.
4
-
5
- It is written for somebody who has never seen the inside of it: by the end of §3 you have a model on
6
- the screen, and by the end of §6 you have your own work in the frame. Everything here is the public
7
- surface — the types you construct, the calls you make, and the two or three places where the answer
8
- is "no, and here is why".
9
-
10
- If you are looking for how it works rather than what it promises, the design documents are next to
11
- the code they describe: `renderer/extension/RENDER_EXTENSION_DESIGN.md`,
12
- `renderer/geometry/virtual/VIRTUAL_GEOMETRY_DESIGN.md`, `renderer/geometry/meshlet/README.md`,
13
- `renderer/NOTES.md`.
14
-
15
- ---
16
-
17
- ## 1. What it is
18
-
19
- A **GPU-driven, visibility-buffer deferred renderer on WebGPU.** Geometry is clustered into meshlets
20
- ahead of time and lives in one large GPU buffer; culling runs on the GPU; the rasterizer writes a
21
- visibility buffer and a G-buffer rather than shading as it draws; lights are binned into froxels and
22
- applied deferred; and the result is temporally resolved, upscaled, tonemapped and presented.
23
-
24
- Three consequences that shape everything below:
25
-
26
- - **A mesh is a small GPU-resident record, not a draw call you own.** You place a model at entities
27
- of the scene's dataset and each primitive is a row; what is drawn, in what order, and whether it
28
- is culled is the renderer's.
29
- - **Preparing geometry is a precompute.** Meshlet building, encoding and BVH construction happen
30
- once per geometry. Content that rewrites its vertices every frame goes down a separate path (§8).
31
- - **The frame is a closed sequence with declared injection points.** You do not insert a pass
32
- anywhere; you register an extension against a phase (§6).
33
-
34
- ### What is deliberately absent
35
-
36
- | | |
37
- |---|---|
38
- | **No RHI, no backend abstraction** | There is one target, WebGPU. There is no seam where a second one would go, and adding one is not a small change. |
39
- | **No WebGL fallback** | Below-floor hardware gets a clean failure message (§2), not a degraded picture. |
40
- | **No degradation tiers** | There is one pipeline. Individual features can be switched off (`feature_shadows_enabled`, `feature_taa_enabled`, the resolution scale), but there is no "low quality" build of the frame. |
41
- | **No compressed textures** | DDS, Draco and Basis are not supported anywhere. glTF files carrying `MSFT_texture_dds` load from their base PNG source. |
42
-
43
- ---
44
-
45
- ## 2. The device floor
46
-
47
- The renderer requires a WebGPU device that meets a fixed floor. There is no feature detection
48
- beyond it and no branch that produces a lesser frame.
49
-
50
- **Required features** — the adapter must have all three, or startup fails:
51
-
52
- - `indirect-first-instance` — indirect draw is used throughout
53
- - `float32-blendable` — the MBOIT transparency mode blends into 32-bit float targets. The default mode,
54
- AVBOIT, blends `rgba16float` only and does not need it. The feature stays on the floor while MBOIT is
55
- selectable at all; retiring it from the floor means retiring MBOIT.
56
- - `texture-formats-tier1` — the hierarchical Z buffer is reduced in compute into `rg16float` storage
57
- textures, which core WebGPU cannot write. Without it the HZB texture and every HZB pipeline fail
58
- validation, and occlusion culling, SSR, screen-space shadows and light culling all read the HZB.
59
-
60
- **Required limits:**
61
-
62
- - `maxStorageBuffersPerShaderStage` ≥ **10**
63
- - `maxColorAttachmentBytesPerSample` ≥ **32** — the G-buffer is wide
64
- - `maxBufferSize` and `maxStorageBufferBindingSize` are requested at whatever the adapter offers,
65
- because the defaults are far too small
66
-
67
- The required features and the two fixed limits are one definition, `device/SHADE_DEVICE_FLOOR.js`.
68
- All of it is checked against the adapter before a device is requested, so a refusal names every
69
- feature and limit the adapter lacks, with the value it offers, rather than the first one found.
70
-
71
- **Taken when offered, never required:** `timestamp-query`, `subgroups`.
72
- `timestamp-query` drives GPU timers and nothing else, and some browsers withhold it on hardware that
73
- is otherwise fine — so the timers go quiet when it is absent rather than the renderer refusing to
74
- start.
75
-
76
- ### When it is not met
77
-
78
- `Renderer.initialize()` throws a `ShadeDeviceFailure`, whose `message` is written to be shown to a
79
- person rather than logged. It distinguishes:
80
-
81
- | | |
82
- |---|---|
83
- | `webgpu_unavailable` | no `navigator.gpu` — WebGPU disabled or unsupported |
84
- | `adapter_unavailable` | no adapter at all |
85
- | `below_floor` | an adapter that does not meet §2; the message lists every unmet feature and limit |
86
- | `device_request_failed` | the adapter refused the device |
87
- | `device_lost` | a working device went away while running |
88
-
89
- A device lost while running arrives on `Renderer.onDeviceFailure` instead of being thrown. **The
90
- renderer does not recover a lost device.** Everything it built lived on it, and the honest response
91
- is a message and a reload. The signal exists so an application can say so and save what it has.
92
-
93
- ---
94
-
95
- ## 3. Putting a model on the screen
96
-
97
- meep has no module aliases: every import is a relative path, and the ones below are written from
98
- `src/shade/` so they read like the tree in §10.
99
-
100
- ```js
101
- import { Renderer } from "./renderer/Renderer.js";
102
- import { Scene } from "./renderer/scene/Scene.js";
103
- import { PerspectiveCamera } from "./renderer/camera/PerspectiveCamera.js";
104
- import { make_sunlight } from "./renderer/light/make_sunlight.js";
105
- import { place_light } from "./renderer/light/place_light.js";
106
- import { EntityComponentDataset } from "../engine/ecs/EntityComponentDataset.js";
107
- import { Transform64 } from "../engine/ecs/transform/Transform64.js";
108
- import { prefab_compile } from "../engine/graphics3/prefab/prefab_compile.js";
109
- import { prefab_place } from "../engine/graphics3/prefab/prefab_place.js";
110
- import { load_gltf } from "./renderer/loader/gltf/load_gltf.js";
111
- import { create_frame_loop } from "./renderer/create_frame_loop.js";
112
- import { DEG_TO_RAD } from "../core/math/DEG_TO_RAD.js";
113
- import Vector3 from "../core/geom/Vector3.js";
114
-
115
- const canvas = document.querySelector("canvas");
116
-
117
- const renderer = new Renderer();
118
-
119
- await renderer.initialize({ context: canvas.getContext("webgpu") });
120
-
121
- const camera = new PerspectiveCamera();
122
-
123
- camera.transform.position.set(0, 2, -5);
124
- camera.transform.lookAt(Vector3.zero);
125
- camera.near = 0.1;
126
- camera.far = 200;
127
- camera.fov = 72 * DEG_TO_RAD;
128
- camera.aspect = renderer.aspect_ratio;
129
-
130
- const scene = new Scene();
131
-
132
- // everything in a scene stands at an entity, so the scene names a dataset — a map of components,
133
- // no systems
134
- scene.dataset = new EntityComponentDataset();
135
-
136
- // nothing is lit without one, and the deferred shading pass has no default light
137
- place_light(scene, make_sunlight({ intensity: 2.2 }));
138
-
139
- const bundle = await load_gltf("model.glb", { assetManager });
140
-
141
- // the model as loaded is compiled once; a placement of it is entities of the dataset, one per node
142
- // of the file, each with a row of the scene's GPU database — the scene's context is where the rows
143
- // live
144
- prefab_place(renderer.scenes.obtain(scene), scene.dataset, prefab_compile(bundle), new Transform64());
145
-
146
- create_frame_loop(dt => renderer.render(camera, scene, dt));
147
- ```
148
-
149
- That is the whole of it. Five things in it are worth stating outright.
150
-
151
- **`initialize` is asynchronous and may fail normally.** Acquiring a WebGPU device has no synchronous
152
- form and being refused one is an ordinary outcome, so it is a rejected promise carrying a
153
- `ShadeDeviceFailure` rather than a constructor that throws. Omit `context` and a full-viewport canvas
154
- is created for you; pass a `device` to share one you already have.
155
-
156
- **`assetManager` is required, and here is where one comes from.** A glTF is several files — a
157
- container, its `.bin` buffers, its images — and which of them are already in memory is not the
158
- parser's business to know. Every request the parse makes goes back through the manager, so they are
159
- cached, counted and cancellable like anything else the application loads. Two loaders are all
160
- `load_gltf` asks for:
161
-
162
- ```js
163
- const assetManager = new AssetManager({ context: {}, executor: new ConcurrentExecutor() });
164
-
165
- await assetManager.registerLoader(GameAssetType.ArrayBuffer, new ArrayBufferLoader());
166
- await assetManager.registerLoader(GameAssetType.ImageBitmap, new ImageBitmapAssetLoader());
167
-
168
- assetManager.startup();
169
- ```
170
-
171
- Pass a `scope` alongside it to tie a whole load to one cancellable unit, and a `fileMap` when the
172
- files come from a drop rather than from the network. An application already running meep's `Engine`
173
- has a configured manager on it and should use that one.
174
-
175
- **A model does not have to come from a file.** Everything `load_gltf` produces you can build
176
- directly, which is §4 in the other direction and needs no asset manager at all:
177
-
178
- ```js
179
- const transform = new Transform64();
180
-
181
- transform.setTranslation(0, 1, 0);
182
- transform.updateMatrix();
183
-
184
- place_primitive(
185
- renderer.scenes.obtain(scene),
186
- meshlet_geometry_build_from_geometry(make_torus_knot_geometry(1, 0.3, 128, 16)),
187
- new StandardShadeMaterial(),
188
- transform
189
- );
190
- ```
191
-
192
- **`render` returns a boolean.** `false` means the previous frame has not finished and this one was
193
- not submitted. It is not an error.
194
-
195
- **Everything drawn or posed is an entity of the scene's dataset, holding a row of the scene's GPU
196
- database.** `prefab_place` answers what it placed — the root entity, the instance, its animation
197
- registration — and `prefab_unplace` takes it all back; `place_primitive` answers the entity,
198
- `move_primitive` moves it and `remove_primitive` takes it back. A game calls none of them: its
199
- systems take rows as components appear. A light is the same shape — it stands at an entity, and
200
- `scene.lights.add(light, entity)` — `place_light(scene, light)`, for a caller with no entity of its
201
- own — is what puts it in the scene. Its record's pose is that entity's row, read on the GPU every
202
- frame.
203
-
204
- ---
205
-
206
- ## 4. The type model
207
-
208
- Everything in a scene is an **entity** of the scene's dataset, and what the renderer knows about it
209
- is **a row** of the scene's GPU database, at `entity + 1` in two tables: `transforms` — its local TRS
210
- and the row of its parent — and, for a primitive, `meshes` — which geometry, which material, and the
211
- world bounds. Row 0 is the scene root, identity. There is no CPU object per node, no parent pointer
212
- to follow and no world matrix to recompose: the GPU composes every chain each frame, from the rows,
213
- and derives every mesh row's bounds under the composed matrix.
214
-
215
- **An entity is placed through its `Transform64`, and what the GPU holds is derived from it.** A
216
- root entity's row carries its world pose; an entity attached under another (`TransformAttachment`)
217
- carries its local offset and names its parent's row. A game's systems write these as components
218
- change; a scene with no systems places through `prefab_place` and `place_primitive`, and moves a
219
- primitive through `move_primitive`, which marks the row for the next build. The box that is drawn
220
- is the GPU's — `GPUSceneContext.bounds` reads it back, a frame or two behind — and nothing on the
221
- CPU keeps a copy of it.
222
-
223
- **An entity's transform and a camera's are different types, deliberately, and this is the one place
224
- a newcomer will trip.** `Transform64` is a flat `Float64Array` holding its own matrix, posed through
225
- `setTranslation(x, y, z)` / `setRotation(x, y, z, w)` / `setScale(x, y, z)` and refreshed with
226
- `updateMatrix()`, with the buffer layout part of its contract so hot code can write it directly. A
227
- `Camera` is **not** an entity — it carries meep's ECS `Transform`, whose `position`, `rotation` and
228
- `scale` are observable vectors you assign into. The two keep distinct shapes on purpose and neither
229
- has the other's accessors: `transform.position` is `undefined` on a `Transform64`, and so is
230
- `camera.transform.translation`.
231
-
232
- ```js
233
- transform.setTranslation(0, 1, 0); // Transform64 — then updateMatrix()
234
- camera.transform.position.set(0, 2, -5); // Transform — reactive, no refresh call
235
- ```
236
-
237
- ```
238
- Scene
239
- ├── dataset EntityComponentDataset — every entity the scene draws or poses; its rows
240
- ├── lights LightCollection — each at an entity; their own pipeline, not a mesh row
241
- ├── light_probe_volume
242
- └── volumetrics
243
- ```
244
-
245
- A **primitive** is an entity holding a `meshes` row. Its `ShadedGeometry` component names what the
246
- row draws, so a tool can read it back:
247
-
248
- ```js
249
- geometry // MeshletGeometry — what to draw
250
- material // ShadeMaterial — how to draw it
251
- ```
252
-
253
- and the row carries the world-space `bounding_box` / `bounding_sphere` the renderer derives for
254
- culling.
255
-
256
- ### Geometry: two types, one of which is a precompute
257
-
258
- **`Geometry`** is the authoring form: named `Attribute`s over typed arrays, an optional index, a
259
- `bounding_box`/`bounding_sphere` computed on demand by `ensureBounds()`, and a `version` you bump
260
- through `needsUpdate = true` when you rewrite the arrays. Attributes are read by name:
261
-
262
- ```js
263
- geometry.getAttribute(StandardAttributes.Position).data // Float32Array
264
- geometry.index.data // Uint32Array
265
- ```
266
-
267
- Ten generators ship, all returning a `Geometry`, in `renderer/geometry/primitives/`:
268
- `make_box_geometry`, `make_plane_geometry`, `make_polyhedron_geometry`, `make_octahedron_geometry`,
269
- `make_uv_sphere_geometry`, `make_capsule_geometry`, `make_cylinder_geometry`, `make_cone_geometry`,
270
- `make_torus_geometry`, `make_torus_knot_geometry`.
271
-
272
- **`MeshletGeometry`** is the GPU form: triangles clustered into meshlets, attributes encoded, and a
273
- BVH over the clusters. This is what a mesh row names, and it is what buys cluster culling and a
274
- compact residency.
275
-
276
- ```js
277
- const meshlets = meshlet_geometry_build_from_geometry(geometry);
278
- ```
279
-
280
- **That build is a precompute.** Do it once, when the content is created or loaded. If your vertices
281
- change every frame, you are on the wrong path — see §8.
282
-
283
- Seven of the primitives also come ready built, as `MeshletGeometry` subclasses one per module in
284
- `renderer/geometry/`: `BoxGeometry`, `PlaneGeometry`, `UVSphereGeometry`, `CapsuleGeometry`,
285
- `CylinderGeometry`, `ConeGeometry` and `TorusGeometry`. Each constructor runs its generator and then
286
- this build, so constructing one is the same precompute.
287
-
288
- ### Materials
289
-
290
- `ShadeMaterial` is the base: `transparency_mode`, `draw_mode` and `draw_side`.
291
-
292
- **`draw_side` changes what is drawn.** `Front` (the default) draws the front face, `Back` the back
293
- face, `Double` both. A face seen from behind is shaded with its normal flipped, so `Double` is a
294
- working double-sided material and not a hint. glTF `doubleSided: true` arrives as `Double` and
295
- stays that way; glTF cannot ask for `Back`, which comes from authoring.
296
-
297
- **Only `Double` costs a pipeline of its own.** It culls nothing, and no amount of winding expresses
298
- that. `Front` and `Back` differ by winding alone, and the vertex shader reverses winding per
299
- instance, so the two share a rasterization bucket and draw in one pass — which matters because a
300
- bucket is not a pipeline here but a whole chain: extract, expand to meshlets, select a
301
- virtual-geometry cut, one render pass.
302
-
303
- **That is also why a mirrored instance works.** Place a mesh under a negative scale, or under a
304
- parent that reflects, and it draws right way out, with the handedness of its tangent frame flipped
305
- to match — the reflection and the material's side are the same one bit, combined before a corner is
306
- chosen. glTF makes this the rule rather than an extra: the determinant of a node's world transform
307
- decides its winding, and a conformant renderer has to read it.
308
-
309
- Shadow passes cull the other face — so a one-sided surface casts from its back face and does not
310
- shadow-acne against its own lit side. `Double` has no opposite and stays unculled in both.
311
-
312
- `StandardShadeMaterial` is the PBR one everything uses:
313
-
314
- | | |
315
- |---|---|
316
- | `texture_albedo` | base colour, alpha carries transparency |
317
- | `diffuse_color` | multiplied with the albedo |
318
- | `texture_normal` | tangent-space normals |
319
- | `texture_orm` | occlusion / roughness (G) / metalness (B), as glTF packs it |
320
- | `texture_emissive`, `emissive_factor` | |
321
- | `roughness_factor`, `metallic_factor` | multiplied with the ORM texture |
322
- | `transmission_factor`, `ior_factor` | |
323
- | `ambient_factors` | a `LinearModifier` over the indirect contribution |
324
- | `vt_stack` | opt-in virtual texturing; when set, albedo/normal/ORM come from streamed pages instead. Opaque materials only |
325
-
326
- ### Textures
327
-
328
- Two objects, and the split matters: **`ShadeImage`** is the pixels, **`ShadeTexture`** is how to
329
- sample them. Two materials sampling one image differently are two textures over one image, and the
330
- image is uploaded once.
331
-
332
- ```js
333
- const image = ShadeImage.fromImageBitmap(bitmap); // or fromSampler2D, or fromArrayBuffer
334
- const texture = ShadeTexture.from(image);
335
-
336
- texture.wrapS = TextureWrapType.Repeat;
337
- texture.magFilter = TextureFilterType.Linear;
338
- ```
339
-
340
- `ShadeTexture` carries filters, wrap modes and flags (mipmap generation is on by default), and both
341
- types answer `equals`/`hash`, which is what lets the renderer de-duplicate uploads.
342
-
343
- ### What a glTF becomes
344
-
345
- `load_gltf` returns a **`SceneBundle`** — three arrays out of the one file:
346
-
347
- ```js
348
- bundle.nodes // SceneNode[] — the file's nodes in pre-order: name, parent, local TRS, geometry, material
349
- bundle.skins // Skin[] — joints and meshes as indices into nodes
350
- bundle.clips // ShadeAnimationClip[] — each channel targets an index into nodes
351
- bundle.lights // {light, node}[] — anchored by index
352
- ```
353
-
354
- The bundle is **the model as loaded, not a placement of it** — records, with no transform state
355
- anyone could pose. It is compiled once into a `Prefab` (`prefab_compile`), and every placement is
356
- spawned from that — `prefab_place` in a scene with no systems, `prefab_instantiate` under a game's
357
- own root entity — with its own entities, its own skins and its own retargeted clips, addressed by
358
- entity, which is what makes two of one model possible.
359
-
360
- ---
361
-
362
- ## 5. The frame
363
-
364
- ```
365
- FrameStart view updated, nothing rasterized
366
- ↓
367
- [ visibility buffer + G-buffer ]
368
- AfterGBuffer surface properties written and readable, nothing lit
369
- ↓
370
- [ clustered lighting, deferred shading, volumetrics ]
371
- AfterLighting opaque lit and composited
372
- ↓
373
- [ transparency ]
374
- AfterTransparency the complete scene, HDR, internal resolution
375
- ↓
376
- [ TAA / NSS upscale ] ← resolution changes here
377
- BeforePresent HDR, output resolution, immediately before tonemapping
378
- ↓
379
- [ tonemap → canvas ]
380
- Overlay display space, drawing onto the canvas
381
- ```
382
-
383
- **The upscale is a phase boundary and it is the one thing about the frame you cannot assume away.**
384
- Three phases are before it and two are after. With `feature_taa_enabled === false` there is no
385
- upscale and the two sides are the same size. Nothing in this document states a resolution as a
386
- constant: ask `frame.resolution`, which is derived from the live handle and is right in every
387
- configuration, including under dynamic resolution scaling.
388
-
389
- ---
390
-
391
- ## 6. Adding your own work to the frame — render extensions
392
-
393
- An extension is **a named object with declarations and one method**.
394
-
395
- ```js
396
- import { RenderExtension } from "./renderer/extension/RenderExtension.js";
397
- import { FramePhase } from "./renderer/extension/FramePhase.js";
398
- import { SceneColor } from "./renderer/extension/SceneColor.js";
399
- import { ViewTextures } from "./renderer/extension/ViewTextures.js";
400
-
401
- class PathExtension extends RenderExtension {
402
- name = "path display";
403
-
404
- phase = FramePhase.AfterTransparency;
405
-
406
- record(frame) {
407
- if (this.#batch.count === 0) {
408
- // nothing to draw, and the frame's colour is left exactly as it was
409
- return;
410
- }
411
-
412
- const scene = frame.get(SceneColor);
413
- const view = frame.get(ViewTextures);
414
-
415
- const drawn = this.#renderer.graph_draw({
416
- graph: frame.graph,
417
- batch: this.#batch,
418
- color: scene.color,
419
- depth: view.depth,
420
- camera: frame.view.camera.buffer
421
- });
422
-
423
- scene.color = drawn.color;
424
- view.depth = drawn.depth;
425
- }
426
- }
427
-
428
- const registered = renderer.add_extension(new PathExtension());
429
- // ...
430
- renderer.remove_extension(registered);
431
- ```
432
-
433
- That is the whole mechanism. The rest of this section is what the four lines of it mean.
434
-
435
- **An extension draws over transparency, never inside it.** At `AfterTransparency` the transparent
436
- surfaces are already resolved into the colour, so what an extension draws there covers them whatever
437
- their depth. Content that has to sit among transparent surfaces — in front of some, behind others —
438
- is a transparency channel instead (§8).
439
-
440
- ### Records are the channel
441
-
442
- `record(frame)` **returns nothing.** Everything an extension exchanges with the frame goes through
443
- typed **records**: you read a frame-graph handle out of one and write a replacement back, and
444
- **writing a field is publication** — everything after you carries what you left.
445
-
446
- There is no privileged resource. The scene colour is published exactly the way a G-buffer target is,
447
- and exactly the way a record of your own is.
448
-
449
- | record | carried at | writable |
450
- |---|---|---|
451
- | `SceneColor` — `.color`, HDR | `AfterLighting` … `BeforePresent` | yes |
452
- | `GBufferTextures` — `.albedo` (AO in alpha), `.normal`, `.pbr`, `.emissive` | `AfterGBuffer` … `BeforePresent` | **`AfterGBuffer` only** |
453
- | `ViewTextures` — `.depth`, `.depth_previous`, `.visibility_mesh`, `.visibility_triangle` | `AfterGBuffer` … `BeforePresent` | yes |
454
- | `PresentTarget` — `.canvas` | `Overlay` only | yes |
455
-
456
- Three of those rows are the whole reason phases exist:
457
-
458
- - **`SceneColor` is not carried at `Overlay`.** A texture may not be a render attachment and a
459
- sampled source in the same pass, so at `Overlay` the canvas is written and cannot be read. An
460
- effect that needs the finished image as *input* — a colour grade, a full-screen re-composite —
461
- belongs at `BeforePresent`. You find that out from `frame.get(SceneColor)`, which fails with a
462
- sentence naming the phases that do carry it, rather than from a `-1` failing inside a binding
463
- builder.
464
- - **`GBufferTextures` is in/out at `AfterGBuffer` and frozen afterwards.** Changing surface
465
- properties means reading the targets, writing replacements and publishing the new handles — two of
466
- the four are integer formats and cannot be blended, and nothing can be attachment and source at
467
- once. It stays *readable* to `BeforePresent` because outline and selection effects legitimately
468
- read it late; at that phase it is on the far side of the upscale and is therefore **a different
469
- size from the colour beside it**. `frame.describe(handle)` is how a pass working across that
470
- boundary learns the ratio.
471
- - **`PresentTarget` has one field and is not called `SceneColor` on purpose.** There is no colour
472
- there to be tempted by. Keep the existing contents with a `load` op, write only your own pixels,
473
- and publish the result back — that is what lets two overlay extensions compose.
474
-
475
- ### Extending the vocabulary
476
-
477
- An extension can publish a record of its own for another extension to read, and doing so costs no
478
- edit to the renderer:
479
-
480
- ```js
481
- class VisibilityGrid extends FrameRecord {
482
- #grid = -1;
483
-
484
- static is_present_at(phase) { return true; }
485
-
486
- get grid() { return this.read(this.#grid); }
487
- set grid(handle) { this.#grid = this.publish(handle); }
488
- }
489
-
490
- // at FrameStart
491
- frame.create(VisibilityGrid).grid = record_the_grid_update(frame.graph);
492
-
493
- // at AfterTransparency, in another extension
494
- if (frame.has(VisibilityGrid)) { … frame.get(VisibilityGrid).grid … }
495
- ```
496
-
497
- `has` / `get` / `create` are three calls rather than one `get_or_create` because **absent has to stay
498
- distinguishable from empty.**
499
-
500
- ### Ordering
501
-
502
- Order is **declared**, never emergent:
503
-
504
- ```js
505
- class DecalExtension extends RenderExtension {
506
- phase = FramePhase.AfterGBuffer;
507
- relative_order = 1; // a decal composites over surfaces already written
508
- }
509
- ```
510
-
511
- `relative_order` defaults to `0`; lower records first, and any finite number will do — negative to
512
- pull forward, positive to push back. Two extensions on one phase with the same value record in
513
- **ascending `name` order**, which is reproducible rather than correct and is chosen precisely so
514
- that adding an `await` to a system's startup cannot change the picture.
515
-
516
- **It names nothing.** An extension states where it belongs from what it does — a fog conceals, so it
517
- is late; a debug overlay must not be concealed, so it is later still — and needs no import of, or
518
- reference to, whatever else is on the phase. That is deliberately less precise than a dependency
519
- would be: it cannot say "immediately after that one", and there is no cycle to report because there
520
- is no graph. An effect that genuinely needs one particular extension's result should read a record
521
- that extension published, which is a real dependency rather than a positional one.
522
-
523
- ### What keeps your work alive
524
-
525
- The graph elides work whose output nothing consumes, and that applies to an extension exactly as it
526
- does to a built-in pass. Thread a result into a record and it is consumed. Write something imported —
527
- the canvas, an imported texture — and it is a side effect. Want work that runs regardless and
528
- `make_side_effect()` on the pass builder says so.
529
-
530
- **An extension whose whole recording is elided is a correct, cheaper frame, not a failure**, and
531
- nothing reports it as one. It is a grey cluster with your extension's name on it in a graph dump.
532
-
533
- ### The rest of `FrameContext`
534
-
535
- | | |
536
- |---|---|
537
- | `frame.graph` | the frame's `FrameGraph`. Record into it with the `graph_*` helpers, exactly as the built-in passes do |
538
- | `frame.view` | the `GPUViewContext` — camera, uniforms, frame index |
539
- | `frame.phase` | which phase this is |
540
- | `frame.resolution` | `[width, height]` of what you are drawing into, from the live handle |
541
- | `frame.describe(handle)` | the descriptor of any resource, for a pass that has to relate two of them |
542
- | `frame.is_open` | records are per-frame. A record held past the end of the frame is a retained handle by another name, and both reading and publishing through one say so |
543
-
544
- ### Not built yet
545
-
546
- Two pieces of the extension design are named in `RENDER_EXTENSION_DESIGN.md` and **do not exist**:
547
- `RenderExtensionRegistry.describe()` (printing the pipeline as data) and `FrameProduct` (opting into
548
- optional frame products such as velocity or a readable copy of the colour). Do not write against
549
- them.
550
-
551
- ---
552
-
553
- ## 7. Rendering somewhere other than the canvas
554
-
555
- ```js
556
- import { GPUTextureContext } from "./renderer/texture/GPUTextureContext.js";
557
-
558
- renderer.render_to_target(camera, scene, dt, GPUTextureContext.fromTexture(texture, renderer.device));
559
- ```
560
-
561
- `render()` *is* this call with the canvas's current texture as the target — the canvas is one target
562
- among others, not a privileged one.
563
-
564
- **The target says where the picture goes, not how big it is.** Everything the frame is built out of —
565
- the G-buffer, the visibility buffer, the depth ping-pong, the temporal history — is sized from
566
- `renderer.output_resolution`, so **the target must be that size**. A caller wanting a different one
567
- calls `renderer.resize()` first and puts the size back afterwards.
568
-
569
- This is what makes a frame readable at all: a canvas texture is configured by the presentation path
570
- and cannot carry `COPY_SRC`; a texture you own can. Thumbnails, asset previews and image-based tests
571
- all go through here.
572
-
573
- ---
574
-
575
- ## 8. Content whose vertices change — the dynamic-mesh path
576
-
577
- A trail behind a projectile, a ribbon, a tube along a path, a batch of CPU-simulated particles, a
578
- debug line: content rewritten every frame cannot pay for a meshlet build, and rebuilding meshlets per
579
- frame to force it down the standard path is explicitly not the answer.
580
-
581
- ```js
582
- const mesh = DynamicMesh.from(geometry, [1, 0.5, 0, 1]); // Geometry, not MeshletGeometry
583
-
584
- batch.add(mesh); // DynamicMeshBatch, yours — not the Scene's
585
- mesh.visible = false; // hide without leaving the batch
586
- geometry.needsUpdate = true; // you say when the vertices changed; nothing watches the arrays
587
- ```
588
-
589
- This is **a separate path, not a variant of the standard one.** A `DynamicMesh` holds a plain
590
- `Geometry` and its own `transform`, is no entity of the scene, gets no row in the scene database,
591
- and is drawn by a pipeline of its own after the standard geometry path has finished with the frame.
592
-
593
- Drawing it over the scene is a `GPUDynamicMeshRenderer` inside an extension of yours (§6); drawing it
594
- inside the transparency pipeline is a `DynamicMeshAVBOITChannel` (below). Two things to know either
595
- way:
596
-
597
- **There is one vertex layout and it is fixed** — 8 floats: position at `[0..2]`, one reserved slot at
598
- `[3]`, RGBA colour at `[4..7]`. A dynamic mesh is drawn by a single pipeline, so letting each geometry
599
- declare its own attribute set would buy nothing and cost either a pipeline per layout or a shader
600
- that branches per vertex. Your geometry needs a position attribute and an index; a colour attribute is
601
- optional and is multiplied by the mesh's flat `color`.
602
-
603
- **`GPUDynamicMeshRenderer.opaque` chooses blending or depth**, and it is a property of the pass
604
- rather than of a mesh — the whole batch moves together. Left `false` the content is blended and only depth-tests,
605
- which is what effects want. Set it and the pass writes depth instead: a solid surface with no meshlet
606
- build behind it has no depth ordering otherwise, and no CPU sort can give it one, because a painter's
607
- order over triangles is wrong wherever two of them interpenetrate.
608
-
609
- `graph_draw` hands back `{ color, depth }`, and an extension publishes both, as in §6. The depth is a
610
- new handle when the pass wrote it. Anything reading the scene depth after you — depth of field,
611
- motion blur, the NSS upscaler, the next extension — has to be given that handle; a development build
612
- refuses the frame when it is not. Left `false`, the depth comes back as it went in.
613
-
614
- That is also what decides the phase for solid content. At `AfterLighting` transparency has not run
615
- yet, and it tests against the depth you published, so glass in front of the batch covers it. At
616
- `AfterTransparency` the glass is already resolved into the colour, and the batch is drawn over it.
617
-
618
- Residency is keyed on **the geometry**, not on batch membership, so a mesh that blinks off for a
619
- frame or is removed and re-added keeps its buffers.
620
-
621
- ### Inside the transparency pipeline instead of over it
622
-
623
- Blended content drawn by an extension composites over transparency that is already resolved: a trail
624
- behind a pane of glass is drawn on top of the glass. A batch that has to interleave with transparent
625
- surfaces by depth is registered as a **transparency channel** instead:
626
-
627
- ```js
628
- const channel = new DynamicMeshAVBOITChannel(batch);
629
-
630
- renderer.add_transparency_channel(channel);
631
- // ...
632
- renderer.remove_transparency_channel(channel);
633
- channel.destroy(); // the channel owns the batch's GPU buffers
634
- channel.release(geometry); // ... and, like the renderer, frees a geometry gone for good
635
- ```
636
-
637
- The channel is an `AVBOITSideChannel`, the contract the GPU particles already implement: its
638
- fragments go into the same extinction volume and accumulators as the transparent meshes and the
639
- particles, so all three occlude each other order-independently, within AVBOIT's own limits (two
640
- surfaces in one depth slice blend rather than sort). What is drawn is straight alpha, unlit, fogged
641
- by the volumetrics at its depth, depth-tested against the opaque scene and never written. A mesh is
642
- drawn when it is visible, has positive flat alpha and has a triangle with a vertex of positive alpha;
643
- a batch with none says so, and costs the frame nothing. **Under `ShadeTransparencyMode.MBOIT`, or
644
- with transparency off, a channel is not drawn at all** — as the particles are not.
645
-
646
- What that costs and changes: a channel with something live makes the transparency pipeline run for
647
- the frame even when nothing else is transparent; its depth range shares the pipeline's 128 slices with
648
- every transparent surface on screen (off-screen meshes mark none); and what extensions draw at
649
- `AfterTransparency` — path display, the selection highlight — lands over it, since it is resolved
650
- before that phase. Registering the same channel twice keeps one.
651
-
652
- From the ECS side the same registration goes through `GraphicsEngine.add_transparency_channel`, which
653
- holds channels across a restart the way `add_extension` holds extensions. `Trail3DSystem` draws
654
- trails this way.
655
-
656
- ---
657
-
658
- ## 9. Frame-level switches
659
-
660
- | | |
661
- |---|---|
662
- | `feature_shadows_enabled` | |
663
- | `feature_taa_enabled` | when off, there is no upscale and the phases either side of it are the same size |
664
- | `transparency_mode` | `ShadeTransparencyMode.AVBOIT` (default) or `.MBOIT`; both run at the one transparency call site with the same inputs, safe to flip per frame — except that side channels (the GPU particles, transparency channels such as trails) are drawn under AVBOIT only |
665
- | `avboit` | `AVBOITSettings`: `transmission_tint_from_base_color` (default off) filters transmitted light by base colour per channel under AVBOIT — off, a flip of `transparency_mode` changes no material's meaning |
666
- | `internal_resolution_scale` | render internally below output resolution; `DynamicResolutionScaling` drives it from frame time |
667
- | `pixel_ratio`, `output_resolution`, `aspect_ratio`, `resize(x, y)` | |
668
- | `motion_blur`, `dof`, `dof_unreal`, `nss`, `restir_di`, `path_tracer` | sub-objects with their own settings |
669
- | `frame_count` | |
670
- | `profile_session` | assign a `GPUProfileSession` to record GPU timings; null when nothing is profiling |
671
-
672
- ---
673
-
674
- ## 10. Where things live
675
-
676
- ```
677
- shade/renderer/
678
- ├── Renderer.js the whole surface in §2, §7, §9
679
- ├── scene/ Scene, GPUSceneContext, rows/ — GPUSceneRows, place_primitive; DynamicMesh
680
- ├── geometry/ Geometry, MeshletGeometry, the builders, primitives/
681
- ├── material/ ShadeMaterial, StandardShadeMaterial
682
- ├── texture/ ShadeTexture, GPUTextureContext, source/ShadeImage
683
- ├── light/ LightCollection, make_sunlight, model/{Light,DirectionalLight,PointLight,SpotLight}
684
- ├── camera/ PerspectiveCamera
685
- ├── loader/gltf/ load_gltf, SceneBundle
686
- ├── extension/ RenderExtension, FrameContext, FrameRecord, the four records
687
- ├── dynamic/ GPUDynamicMeshRenderer, DynamicMeshAVBOITChannel, and their one vertex layout
688
- └── animation/ GPUAnimationManager — skins and clips
689
- ```
690
-
691
- `prefab_compile` and `prefab_place` live with the ECS, in `engine/graphics3/prefab/`, because a
692
- placement is entities.
693
-
694
- `shade/device/mock/` holds a **validating software device**: no shader execution, but real validation
695
- of bindings, passes, resources and texture copies. It is how the renderer's own tests run without a
696
- GPU, and it is available to yours. Specs must never acquire a real device — see `vitest.config.mjs`,
697
- which explains at length why.