@woosh/meep-engine 3.60.4 → 3.60.5

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 (379) hide show
  1. package/CHANGELOG.md +2602 -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.map +1 -1
  63. package/src/shade/renderer/Renderer.js +7 -5
  64. package/src/shade/renderer/camera/orbital/OrbitalCameraController.d.ts +40 -80
  65. package/src/shade/renderer/camera/orbital/OrbitalCameraController.d.ts.map +1 -1
  66. package/src/shade/renderer/camera/orbital/OrbitalCameraController.js +197 -147
  67. package/src/shade/renderer/fluid/GPUFluidCascade.d.ts +3 -3
  68. package/src/shade/renderer/fluid/GPUFluidCascadeStack.d.ts +1 -1
  69. package/src/shade/renderer/fluid/forcing/GPUFluidForcing.d.ts +6 -6
  70. package/src/shade/renderer/fluid/pressure/GPUFluidPressureSolve.d.ts +1 -1
  71. package/src/shade/renderer/fluid/query/GPUFluidQueries.d.ts +1 -1
  72. package/src/shade/renderer/geometry/sdf/GPUGeometrySDFManager.d.ts +2 -2
  73. package/src/shade/renderer/geometry/sdf/GPUGeometrySDFManager.js +2 -2
  74. package/src/shade/renderer/geometry/sdf/cards/GPUMeshCardSets.d.ts +2 -1
  75. package/src/shade/renderer/geometry/sdf/cards/GPUMeshCardSets.d.ts.map +1 -1
  76. package/src/shade/renderer/geometry/sdf/cards/GPUMeshCardSets.js +18 -1
  77. package/src/shade/renderer/geometry/virtual/proxy/vgeo_to_proxy_mesh.d.ts +31 -0
  78. package/src/shade/renderer/geometry/virtual/proxy/vgeo_to_proxy_mesh.d.ts.map +1 -0
  79. package/src/shade/renderer/geometry/virtual/proxy/vgeo_to_proxy_mesh.js +343 -0
  80. package/src/shade/renderer/geometry/virtual/runtime/vgeo_meshlet_metadata_write.d.ts +17 -0
  81. package/src/shade/renderer/geometry/virtual/runtime/vgeo_meshlet_metadata_write.d.ts.map +1 -0
  82. package/src/shade/renderer/geometry/virtual/runtime/vgeo_meshlet_metadata_write.js +29 -0
  83. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_meshlet_batch.d.ts.map +1 -1
  84. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_meshlet_batch.js +7 -13
  85. package/src/shade/renderer/global_illumination/probe_clipmap/GPUProbeClipmapGI.d.ts.map +1 -1
  86. package/src/shade/renderer/global_illumination/probe_clipmap/GPUProbeClipmapGI.js +1 -8
  87. package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSceneHolds.d.ts +5 -5
  88. package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSceneHolds.d.ts.map +1 -1
  89. package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSceneHolds.js +10 -10
  90. package/src/shade/renderer/global_illumination/probe_clipmap/probe/GPUProbeClipmap.d.ts +1 -4
  91. package/src/shade/renderer/global_illumination/probe_clipmap/probe/GPUProbeClipmap.d.ts.map +1 -1
  92. package/src/shade/renderer/global_illumination/probe_clipmap/probe/GPUProbeClipmap.js +1 -10
  93. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_hit_surface.d.ts +5 -15
  94. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_hit_surface.d.ts.map +1 -1
  95. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_hit_surface.js +6 -34
  96. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_shade.d.ts.map +1 -1
  97. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_shade.js +1 -7
  98. package/src/shade/renderer/hiz/HZBFootprint.d.ts +14 -0
  99. package/src/shade/renderer/hiz/HZBFootprint.d.ts.map +1 -0
  100. package/src/shade/renderer/hiz/HZBFootprint.js +16 -0
  101. package/src/shade/renderer/hiz/HZB_FOOTPRINT_X.d.ts +10 -0
  102. package/src/shade/renderer/hiz/HZB_FOOTPRINT_X.d.ts.map +1 -0
  103. package/src/shade/renderer/hiz/HZB_FOOTPRINT_X.js +11 -0
  104. package/src/shade/renderer/hiz/HZB_FOOTPRINT_Y.d.ts +10 -0
  105. package/src/shade/renderer/hiz/HZB_FOOTPRINT_Y.d.ts.map +1 -0
  106. package/src/shade/renderer/hiz/HZB_FOOTPRINT_Y.js +11 -0
  107. package/src/shade/renderer/hiz/HierarchicalZBuffer.d.ts +0 -7
  108. package/src/shade/renderer/hiz/HierarchicalZBuffer.d.ts.map +1 -1
  109. package/src/shade/renderer/hiz/HierarchicalZBuffer.js +405 -350
  110. package/src/shade/renderer/hiz/chunk_hzb_footprint_x.d.ts +12 -0
  111. package/src/shade/renderer/hiz/chunk_hzb_footprint_x.d.ts.map +1 -0
  112. package/src/shade/renderer/hiz/chunk_hzb_footprint_x.js +27 -0
  113. package/src/shade/renderer/hiz/chunk_hzb_footprint_y.d.ts +12 -0
  114. package/src/shade/renderer/hiz/chunk_hzb_footprint_y.d.ts.map +1 -0
  115. package/src/shade/renderer/hiz/chunk_hzb_footprint_y.js +27 -0
  116. package/src/shade/renderer/hiz/hzb_footprint_texels.d.ts +14 -0
  117. package/src/shade/renderer/hiz/hzb_footprint_texels.d.ts.map +1 -0
  118. package/src/shade/renderer/hiz/hzb_footprint_texels.js +34 -0
  119. package/src/shade/renderer/hiz/shader_hzb_reduce_any.d.ts +0 -5
  120. package/src/shade/renderer/hiz/shader_hzb_reduce_any.d.ts.map +1 -1
  121. package/src/shade/renderer/hiz/shader_hzb_reduce_any.js +37 -57
  122. package/src/shade/renderer/hiz/shader_hzb_reduce_mip0.d.ts +11 -0
  123. package/src/shade/renderer/hiz/shader_hzb_reduce_mip0.d.ts.map +1 -0
  124. package/src/shade/renderer/hiz/{shader_hzb_reduce_mip0_clip.js → shader_hzb_reduce_mip0.js} +120 -111
  125. package/src/shade/renderer/loader/gltf/tiny-gltf.d.ts +1 -1
  126. package/src/shade/renderer/material/GPUMaterialManager.d.ts +7 -0
  127. package/src/shade/renderer/material/GPUMaterialManager.d.ts.map +1 -1
  128. package/src/shade/renderer/material/GPUMaterialManager.js +11 -0
  129. package/src/shade/renderer/material/resident/GPUResidentMaterialContext.d.ts +12 -12
  130. package/src/shade/renderer/material/resident/GPUResidentMaterialContext.d.ts.map +1 -1
  131. package/src/shade/renderer/material/resident/GPUResidentMaterialContext.js +17 -118
  132. package/src/shade/renderer/shadow/map/GPUSceneShadowmapContext.d.ts.map +1 -1
  133. package/src/shade/renderer/shadow/map/GPUSceneShadowmapContext.js +7 -2
  134. package/src/shade/renderer/water/GPUWaterRenderer.d.ts +0 -6
  135. package/src/shade/renderer/water/GPUWaterRenderer.d.ts.map +1 -1
  136. package/src/shade/renderer/water/GPUWaterRenderer.js +12 -17
  137. package/src/shade/renderer/water/medium/graph_water_meshlets.d.ts +35 -0
  138. package/src/shade/renderer/water/medium/graph_water_meshlets.d.ts.map +1 -0
  139. package/src/shade/renderer/water/medium/graph_water_meshlets.js +89 -0
  140. package/src/shade/renderer/water/query/GPUWaterQueries.d.ts +1 -1
  141. package/src/shade/renderer/water/shader_water.d.ts.map +1 -1
  142. package/src/shade/renderer/water/shader_water.js +22 -13
  143. package/src/shade/renderer/water/shader_water_avboit.d.ts.map +1 -1
  144. package/src/shade/renderer/water/shader_water_avboit.js +7 -7
  145. package/src/shade/renderer/water/surface/graph_water_tessellation.d.ts +1 -1
  146. package/src/shade/renderer/water/surface/graph_water_tessellation.js +1 -1
  147. package/src/shade/wgsl/emulator/WGSLJavaScriptCompiler.js +9 -1
  148. package/editor/REWORK_PLAN_2026_08.md +0 -692
  149. package/src/CODEC_LAYOUT_VERIFICATION.md +0 -419
  150. package/src/CORRECTNESS_HUNT_2026_07_24.md +0 -142
  151. package/src/DUPLICATION_AUDIT_2026_08_07/00_core_reuse_surface.md +0 -738
  152. package/src/DUPLICATION_AUDIT_2026_08_07/A_core_math_geom.md +0 -79
  153. package/src/DUPLICATION_AUDIT_2026_08_07/B_core_data.md +0 -93
  154. package/src/DUPLICATION_AUDIT_2026_08_07/C_gfx_render.md +0 -80
  155. package/src/DUPLICATION_AUDIT_2026_08_07/D_gfx_features.md +0 -164
  156. package/src/DUPLICATION_AUDIT_2026_08_07/E_physics.md +0 -69
  157. package/src/DUPLICATION_AUDIT_2026_08_07/F_sound.md +0 -110
  158. package/src/DUPLICATION_AUDIT_2026_08_07/G_net_save.md +0 -129
  159. package/src/DUPLICATION_AUDIT_2026_08_07/H_engine_misc.md +0 -72
  160. package/src/DUPLICATION_AUDIT_2026_08_07/I_generation_view.md +0 -74
  161. package/src/DUPLICATION_AUDIT_2026_08_07/K_crosscutting.md +0 -169
  162. package/src/DUPLICATION_AUDIT_2026_08_07.md +0 -254
  163. package/src/DUPLICATION_AUDIT_REMEDIATION.md +0 -250
  164. package/src/DUPLICATION_REVIEW_2026_10_24.md +0 -1072
  165. package/src/REVIEW_2026_08_06/adv-core.md +0 -511
  166. package/src/REVIEW_2026_08_06/adv-gfx.md +0 -480
  167. package/src/REVIEW_2026_08_06/adv-view.md +0 -446
  168. package/src/REVIEW_2026_08_06/api-core.md +0 -296
  169. package/src/REVIEW_2026_08_06/api-engine.md +0 -195
  170. package/src/REVIEW_2026_08_06/bvh-phys-sound-net.md +0 -507
  171. package/src/REVIEW_2026_08_06/core-binary.md +0 -348
  172. package/src/REVIEW_2026_08_06/core-collection.md +0 -376
  173. package/src/REVIEW_2026_08_06/core-math.md +0 -221
  174. package/src/REVIEW_2026_08_06/core-process.md +0 -266
  175. package/src/REVIEW_2026_08_06/core-science.md +0 -223
  176. package/src/REVIEW_2026_08_06/engine-ai-nav.md +0 -279
  177. package/src/REVIEW_2026_08_06/engine-animation.md +0 -167
  178. package/src/REVIEW_2026_08_06/engine-asset.md +0 -166
  179. package/src/REVIEW_2026_08_06/engine-ecs.md +0 -920
  180. package/src/REVIEW_2026_08_06/engine-input.md +0 -197
  181. package/src/REVIEW_2026_08_06/engine-misc.md +0 -248
  182. package/src/REVIEW_2026_08_06/engine-terrain.md +0 -52
  183. package/src/REVIEW_2026_08_06/generation.md +0 -176
  184. package/src/REVIEW_2026_08_06/geom-2d.md +0 -434
  185. package/src/REVIEW_2026_08_06/geom-3d.md +0 -117
  186. package/src/REVIEW_2026_08_06/gfx-gi.md +0 -143
  187. package/src/REVIEW_2026_08_06/gfx-render.md +0 -740
  188. package/src/REVIEW_2026_08_06/gfx-texture.md +0 -690
  189. package/src/REVIEW_2026_08_06/prior-criticals.md +0 -49
  190. package/src/REVIEW_2026_08_06/prior-highs.md +0 -138
  191. package/src/REVIEW_2026_08_06/test-gaps.md +0 -364
  192. package/src/REVIEW_2026_08_06/view.md +0 -667
  193. package/src/REVIEW_2026_08_06.md +0 -610
  194. package/src/core/binary/lz4/LZ4_HIGH_COMPRESSION_PLAN_2026_09_14.md +0 -542
  195. package/src/core/bvh2/bvh3/NOTES.md +0 -2
  196. package/src/core/bvh8/NOTES.md +0 -63
  197. package/src/core/cache/wtinylfu/benchmark/CacheWTinylfu.bench.md +0 -243
  198. package/src/core/color/COLOR_FIX_PLAN_2026_08_28.md +0 -417
  199. package/src/core/color/COLOR_REVIEW_2026_08_28.md +0 -779
  200. package/src/core/geom/2d/polygon/TRIANGULATION_DESIGN.md +0 -433
  201. package/src/core/geom/3d/atlas/ATLAS_PORT_PLAN.md +0 -486
  202. package/src/core/geom/3d/atlas/BFF_PLAN.md +0 -174
  203. package/src/core/geom/3d/atlas/REVIEW_02.md +0 -392
  204. package/src/core/geom/3d/gjk/GJK_REVIEW_NOTES.md +0 -146
  205. package/src/core/geom/3d/gjk/NOTES.md +0 -8
  206. package/src/core/geom/3d/topology/TOPO_MESH_RETIREMENT_PLAN.md +0 -135
  207. package/src/core/geom/GEOMETRY_REVIEW_002_API_ISSUES.md +0 -381
  208. package/src/core/geom/GEOMETRY_REVIEW_002_NAMING.md +0 -684
  209. package/src/core/geom/MANIFOLD_COMPARISON_2026_08_01.md +0 -554
  210. package/src/core/model/reactive/evaluation/PREDICATE_APPLICABILITY_PROPOSAL.md +0 -446
  211. package/src/engine/REVIEW_2026_07_03.md +0 -211
  212. package/src/engine/asset/loaders/image/IMAGE_CODEC_REVIEW_2026_08_28.md +0 -815
  213. package/src/engine/control/first-person/DESIGN.md +0 -658
  214. package/src/engine/control/first-person/DESIGN_COLLISION.md +0 -382
  215. package/src/engine/control/first-person/DESIGN_EXTENSIONS.md +0 -563
  216. package/src/engine/control/first-person/TODO.md +0 -160
  217. package/src/engine/ecs/ASYNC_SYSTEMS_PROPOSAL.md +0 -531
  218. package/src/engine/ecs/HIERARCHY_REVIEW.md +0 -717
  219. package/src/engine/ecs/async/WORKER_SYSTEM_PLAN.md +0 -262
  220. package/src/engine/ecs/ik/PLAN.md +0 -179
  221. package/src/engine/ecs/storage/binary/SERIALIZATION_COVERAGE.md +0 -272
  222. package/src/engine/ecs/storage/binary/object-v2/DESIGN.md +0 -199
  223. package/src/engine/ecs/transform/TRANSFORM64_MIGRATION_PLAN_2026_09_04.md +0 -521
  224. package/src/engine/graphics/CONTEXT_LOSS_RECOVERY_PLAN.md +0 -463
  225. package/src/engine/graphics/ecs/camera/pp/NOTES.md +0 -48
  226. package/src/engine/graphics/render/frame_graph/CORRECTNESS_FIXES_2026_09_04.md +0 -659
  227. package/src/engine/graphics/render/frame_graph/FRAME_GRAPH_RECOMMENDATIONS.md +0 -235
  228. package/src/engine/graphics/sh3/path_tracer/REVIEW_001_ACTION_PLAN.md +0 -490
  229. package/src/engine/graphics/texture/sampler/compare/FLIP_IMPLEMENTATION_PLAN.md +0 -466
  230. package/src/engine/input/GAMEPAD_API_PROPOSAL.md +0 -717
  231. package/src/engine/input/ecs/ism/ISM_DESIGN.md +0 -1523
  232. package/src/engine/input/ecs/ism/ISM_IMPLEMENTATION_PLAN.md +0 -627
  233. package/src/engine/navigation/mesh/OVERLAPPING_SURFACES_2026_08_25.md +0 -661
  234. package/src/engine/navigation/mesh/PATHFINDING_PLAN.md +0 -229
  235. package/src/engine/network/CONGESTION_CONTROL.md +0 -87
  236. package/src/engine/network/MESSAGE_TRANSPORT.md +0 -364
  237. package/src/engine/network/SMOOTHNESS_DESIGN.md +0 -400
  238. package/src/engine/physics/cloth/MEASUREMENTS.md +0 -5304
  239. package/src/engine/physics/cloth/PLAN.md +0 -3056
  240. package/src/engine/physics/fluid/GPU_FLUID_CASCADES_PROPOSAL_2026_10_16.md +0 -1382
  241. package/src/engine/physics/fluid/REVIEW_02_PLAN.md +0 -243
  242. package/src/engine/physics/fluid/solver/NOTES.md +0 -46
  243. package/src/engine/sound/SOUND_EMITTER_RETIREMENT_PLAN.md +0 -536
  244. package/src/engine/sound/simulation/IMPLEMENTATION_PLAN.md +0 -432
  245. package/src/engine/sound/simulation/PROBE_PLACEMENT.md +0 -250
  246. package/src/engine/sound/simulation/PROBE_TRANSFER_RESEARCH.md +0 -322
  247. package/src/engine/sound/simulation/REFLECTION_IR_PLAN.md +0 -261
  248. package/src/engine/sound/simulation/VISIBILITY_GRAPH_PLAN.md +0 -121
  249. package/src/engine/sound/sopra/IMPLEMENTATION_PLAN.md +0 -999
  250. package/src/format/image/avif/DECISIONS.md +0 -545
  251. package/src/format/image/jpeg/NOTES.md +0 -3
  252. package/src/format/scene/gltf/MESHOPT_COMPRESSION_PLAN.md +0 -814
  253. package/src/generation/COORDINATES.md +0 -54
  254. package/src/generation/REVIEW_01_ACTION_PLAN.md +0 -628
  255. package/src/generation/foliage/ANNUAL_SHOOTS_BRIEF_2026_10_25.md +0 -220
  256. package/src/generation/foliage/CROWN_FILLING_BRIEF_2026_10_26.md +0 -172
  257. package/src/generation/foliage/CROWN_FULLNESS_BRIEF_2026_10_26.md +0 -86
  258. package/src/generation/foliage/FOLIAGE_DESIGN.md +0 -547
  259. package/src/generation/foliage/FOLIAGE_PLAN.md +0 -198
  260. package/src/generation/foliage/LEAF_ARCHITECTURE_BRIEF_2026_10_26.md +0 -158
  261. package/src/generation/foliage/LOD_REWORK_BRIEF_2026_10_27.md +0 -521
  262. package/src/generation/foliage/REAL_TIME_TREES_BRIEF_2026_10_27.md +0 -86
  263. package/src/generation/foliage/SELF_ORGANIZING_FRAMEWORK_BRIEF_2026_10_26.md +0 -347
  264. package/src/generation/foliage/SHOOT_SYSTEMS_ALL_SPECIES_BRIEF_2026_10_26.md +0 -248
  265. package/src/generation/foliage/SPEEDTREE_COMPARISON_2026_10_24.md +0 -694
  266. package/src/generation/foliage/TWO_FRONT_ENDS_BRIEF_2026_10_28.md +0 -552
  267. package/src/shade/RENDERER_CONTRACT.md +0 -697
  268. package/src/shade/SHADER_F16_PROPOSAL_2026_10_24.md +0 -263
  269. package/src/shade/device/IMMEDIATE_DATA_PLAN_2026_08_28.md +0 -569
  270. package/src/shade/device/PASS_ENCODER_PROPOSAL_2026_08_31.md +0 -755
  271. package/src/shade/device/mock/EXTENSION_PROPOSAL_2026_08_22.md +0 -458
  272. package/src/shade/device/mock/IMPLEMENTATION_PLAN.md +0 -255
  273. package/src/shade/device/mock/REVIEW_2026_08_22.md +0 -527
  274. package/src/shade/device/timing/GPU_PROFILER_PROPOSAL_2026_08_28.md +0 -894
  275. package/src/shade/device/timing/profile/SGPT_FORMAT.md +0 -345
  276. package/src/shade/renderer/NOTES.md +0 -78
  277. package/src/shade/renderer/PLAN.md +0 -8
  278. package/src/shade/renderer/animation/ADDITIVE_BLENDING_PLAN.md +0 -145
  279. package/src/shade/renderer/animation/SKINNING_FRAME_PLAN.md +0 -357
  280. package/src/shade/renderer/atmosphere/NOTES.md +0 -6
  281. package/src/shade/renderer/camera/orbital/MomentumAxis.d.ts +0 -76
  282. package/src/shade/renderer/camera/orbital/MomentumAxis.d.ts.map +0 -1
  283. package/src/shade/renderer/camera/orbital/MomentumAxis.js +0 -164
  284. package/src/shade/renderer/camera/orbital/OrbitalCameraConfig.d.ts +0 -45
  285. package/src/shade/renderer/camera/orbital/OrbitalCameraConfig.d.ts.map +0 -1
  286. package/src/shade/renderer/camera/orbital/OrbitalCameraConfig.js +0 -66
  287. package/src/shade/renderer/camera/orbital/OrbitalCameraInput.d.ts +0 -98
  288. package/src/shade/renderer/camera/orbital/OrbitalCameraInput.d.ts.map +0 -1
  289. package/src/shade/renderer/camera/orbital/OrbitalCameraInput.js +0 -184
  290. package/src/shade/renderer/camera/orbital/OrbitalCameraRig.d.ts +0 -175
  291. package/src/shade/renderer/camera/orbital/OrbitalCameraRig.d.ts.map +0 -1
  292. package/src/shade/renderer/camera/orbital/OrbitalCameraRig.js +0 -411
  293. package/src/shade/renderer/camera/orbital/README.md +0 -81
  294. package/src/shade/renderer/camera/orbital/index.d.ts +0 -6
  295. package/src/shade/renderer/camera/orbital/index.d.ts.map +0 -1
  296. package/src/shade/renderer/camera/orbital/index.js +0 -9
  297. package/src/shade/renderer/extension/RENDER_EXTENSION_DESIGN.md +0 -964
  298. package/src/shade/renderer/fluid/GPU_FLUID_OCCUPANCY_AND_SOTA_REVIEW_2026_10_26.md +0 -361
  299. package/src/shade/renderer/geometry/bvh/BLAS_REFIT_BATCH_PLAN.md +0 -619
  300. package/src/shade/renderer/geometry/meshlet/COMPACTION_PLAN_2026_09_16.md +0 -674
  301. package/src/shade/renderer/geometry/sdf/NOTES.md +0 -6
  302. package/src/shade/renderer/geometry/virtual/ATTRIBUTE_PLAN_2026_09_18.md +0 -540
  303. package/src/shade/renderer/geometry/virtual/MICRON_COMPARISON_2026_08_20.md +0 -305
  304. package/src/shade/renderer/geometry/virtual/VGEO_FORMAT.md +0 -913
  305. package/src/shade/renderer/geometry/virtual/VIRTUAL_GEOMETRY_DESIGN.md +0 -787
  306. package/src/shade/renderer/geometry/virtual/VIRTUAL_GEOMETRY_PLAN.md +0 -481
  307. package/src/shade/renderer/geometry/virtual/VK_LOD_CLUSTERS_COMPARISON_2026_08_20.md +0 -301
  308. package/src/shade/renderer/geometry/virtual/WEDGE_PLAN_2026_09_18.md +0 -448
  309. package/src/shade/renderer/geometry/virtual/runtime/CULL_PLAN_2026_09_16.md +0 -385
  310. package/src/shade/renderer/geometry/virtual/runtime/CUT_PLAN_2026_09_14.md +0 -656
  311. package/src/shade/renderer/geometry/virtual/runtime/REVIEW_LEDGER.md +0 -308
  312. package/src/shade/renderer/geometry/virtual/runtime/RUNTIME_PLAN.md +0 -657
  313. package/src/shade/renderer/geometry/virtual/runtime/RUNTIME_PLAN_2026_09_14.md +0 -551
  314. package/src/shade/renderer/geometry/virtual/runtime/SHADOW_PLAN_2026_09_28.md +0 -529
  315. package/src/shade/renderer/global_illumination/lpv/NOTES.md +0 -3
  316. package/src/shade/renderer/global_illumination/probe_clipmap/PROBE_CLIPMAP_PROPOSAL_2026_10_27.md +0 -4306
  317. package/src/shade/renderer/global_illumination/probe_clipmap/PROBE_RAYS_VARIABLE_AND_SORTED_SCOPE_2026_10_30.md +0 -410
  318. package/src/shade/renderer/global_illumination/sharc/NOTES.md +0 -8
  319. package/src/shade/renderer/gpu_primitive/bvh/GPU_BVH_BUILD_PLAN.md +0 -1369
  320. package/src/shade/renderer/gpu_primitive/bvh/PHASE1_HANDOVER.md +0 -202
  321. package/src/shade/renderer/gpu_primitive/indirect/NOTES.md +0 -94
  322. package/src/shade/renderer/hiz/NOTES.md +0 -3
  323. package/src/shade/renderer/hiz/shader_hzb_reduce_mip0_clip.d.ts +0 -8
  324. package/src/shade/renderer/hiz/shader_hzb_reduce_mip0_clip.d.ts.map +0 -1
  325. package/src/shade/renderer/light/cluster/NOTES.md +0 -11
  326. package/src/shade/renderer/light/cluster/PROPOSAL_ALGORITHM_2026_10_16.md +0 -524
  327. package/src/shade/renderer/light/cluster/PROPOSAL_HIERARCHICAL_2026_10_16.md +0 -1101
  328. package/src/shade/renderer/material/resident/shader_resident_texture_means.d.ts +0 -24
  329. package/src/shade/renderer/material/resident/shader_resident_texture_means.d.ts.map +0 -1
  330. package/src/shade/renderer/material/resident/shader_resident_texture_means.js +0 -120
  331. package/src/shade/renderer/particles/DESIGN.md +0 -1011
  332. package/src/shade/renderer/particles/EMITTER_GLOBAL_STATE_PLAN_2026_10_29.md +0 -280
  333. package/src/shade/renderer/particles/ORIENTATION_PLAN_2026_10_15.md +0 -453
  334. package/src/shade/renderer/path_tracer/PATH_TRACED_MEDIA_PLAN_2026_10_02.md +0 -520
  335. package/src/shade/renderer/postprocess/bloom/NOTES.md +0 -6
  336. package/src/shade/renderer/postprocess/fsr/NOTES.md +0 -4
  337. package/src/shade/renderer/postprocess/gtao/NOTES.md +0 -6
  338. package/src/shade/renderer/postprocess/nss/NOTES.md +0 -332
  339. package/src/shade/renderer/postprocess/perspective/NOTES.md +0 -3
  340. package/src/shade/renderer/postprocess/sscs/NOTES.md +0 -5
  341. package/src/shade/renderer/postprocess/ssr/NOTES.md +0 -14
  342. package/src/shade/renderer/postprocess/ssr/REPROJECTION_PROPOSAL.md +0 -286
  343. package/src/shade/renderer/postprocess/ssr/prefilter/NOTES.md +0 -39
  344. package/src/shade/renderer/postprocess/taa/NOTES.md +0 -11
  345. package/src/shade/renderer/postprocess/upscale-dither/NOTES.md +0 -78
  346. package/src/shade/renderer/postprocess/vrs/NOTES.md +0 -4
  347. package/src/shade/renderer/rasterize/native/avboit/AVBOIT_LEDGER.md +0 -1218
  348. package/src/shade/renderer/rasterize/native/avboit/AVBOIT_PLAN_2026_09_03.md +0 -600
  349. package/src/shade/renderer/restir/reservoir/NOTES.md +0 -6
  350. package/src/shade/renderer/shader/chunk/atmosphere/NOTES.md +0 -6
  351. package/src/shade/renderer/shader/chunk/brdf/FRESNEL_BRIEF_2026_10_16.md +0 -391
  352. package/src/shade/renderer/shader/chunk/color/tonemap/ACES_TONE_MAPPING_BRIEF_2026_10_17.md +0 -153
  353. package/src/shade/renderer/shader/chunk/color/tonemap/HDR_TONE_MAPPING_FORMS_BRIEF_2026_10_16.md +0 -322
  354. package/src/shade/renderer/shader/chunk/color/tonemap/SDR_TONE_MAPPING_BRIEF_2026_10_16.md +0 -247
  355. package/src/shade/renderer/shader/chunk/fsr/NOTES.md +0 -3
  356. package/src/shade/renderer/shader/chunk/fsr/spd/NOTES.md +0 -4
  357. package/src/shade/renderer/shader/chunk/geometry/ray/NOTES.md +0 -4
  358. package/src/shade/renderer/shader/chunk/material/MULTISCATTER.md +0 -159
  359. package/src/shade/renderer/shader/chunk/mboit/NOTES.md +0 -38
  360. package/src/shade/renderer/shader/chunk/spherical_harmonics/l1i/NOTES.md +0 -3
  361. package/src/shade/renderer/shader/chunk/spherical_harmonics/sampling/NOTES.md +0 -8
  362. package/src/shade/renderer/shader/compiler/SHADER_SWITCH_PROPOSAL_2026_10_16.md +0 -927
  363. package/src/shade/renderer/shadow/contact/FINDINGS.md +0 -413
  364. package/src/shade/renderer/shadow/map/csm/NOTES.md +0 -5
  365. package/src/shade/renderer/shadow/ray/NOTES.md +0 -31
  366. package/src/shade/renderer/shadow/sdf/NOTES.md +0 -9
  367. package/src/shade/renderer/shadow/sdf/brixelizer/NOTES.md +0 -5
  368. package/src/shade/renderer/shadow/sdf/cascade/NOTES.md +0 -9
  369. package/src/shade/renderer/shadow/vsm/NOTES.md +0 -12
  370. package/src/shade/renderer/shadow/vsm/PLAN.md +0 -442
  371. package/src/shade/renderer/shadow/vsm/REVIEW_LEDGER.md +0 -43
  372. package/src/shade/renderer/texture/COMPRESSED_TEXTURE_PLAN_2026_09_01.md +0 -2444
  373. package/src/shade/renderer/texture/mipmaps/NOTES.md +0 -3
  374. package/src/shade/renderer/volumetrics/NOTES.md +0 -1329
  375. package/src/shade/renderer/volumetrics/VOLUMETRIC_NOISE_PLAN_2026_10_15.md +0 -336
  376. package/src/shade/renderer/water/medium/GPUWaterBodies.d.ts +0 -23
  377. package/src/shade/renderer/water/medium/GPUWaterBodies.d.ts.map +0 -1
  378. package/src/shade/renderer/water/medium/GPUWaterBodies.js +0 -75
  379. package/src/shade/wgsl/NOTES.md +0 -4
@@ -0,0 +1,319 @@
1
+ import { assert } from "../../../../core/assert.js";
2
+ import { normalize_angle_rad } from "../../../../core/geom/normalize_angle_rad.js";
3
+ import Vector2 from "../../../../core/geom/Vector2.js";
4
+ import Vector3 from "../../../../core/geom/Vector3.js";
5
+ import { clamp } from "../../../../core/math/clamp.js";
6
+ import TopDownCameraController from "../../ecs/camera/topdown/TopDownCameraController.js";
7
+ import { MomentumAxis } from "./MomentumAxis.js";
8
+
9
+ /**
10
+ * Seconds for a throw's velocity to halve once the gesture lets go.
11
+ * @type {number}
12
+ */
13
+ const MOMENTUM_HALF_LIFE = 0.1;
14
+
15
+ /**
16
+ * Seconds of the gesture's end averaged into the throw velocity.
17
+ * @type {number}
18
+ */
19
+ const VELOCITY_WINDOW = 0.1;
20
+
21
+ /**
22
+ * Radians per second below which an orbit throw stops. A pan throw stops at this times the orbit
23
+ * distance, the same speed across the screen at any zoom.
24
+ * @type {number}
25
+ */
26
+ const MOMENTUM_STOP_SPEED = 0.012;
27
+
28
+ /**
29
+ * Factor on the orbit distance per wheel notch towards the target.
30
+ * @type {number}
31
+ */
32
+ const ZOOM_STEP = 0.9;
33
+
34
+ const GESTURE_NONE = 0;
35
+ const GESTURE_ORBIT = 1;
36
+ const GESTURE_PAN = 2;
37
+
38
+ const v2_pan = new Vector2();
39
+
40
+ /**
41
+ * Orbit, pan and zoom of a {@link TopDownCameraController}, with the feel of **direct drive + release
42
+ * momentum**: the "throw" of Google Earth / Cesium, not a spring chasing a goal.
43
+ *
44
+ * - **A drag moves the controller as it happens**, one to one, so the view keeps up with the hand.
45
+ * - **Letting go throws**: the average velocity over the last {@link VELOCITY_WINDOW} carries on and
46
+ * decays with a half life of {@link MOMENTUM_HALF_LIFE}, at the same distance whatever the frame
47
+ * rate. Holding still before letting go throws nothing. Grabbing again stops every throw.
48
+ * - **Zoom is instant and multiplicative**: a wheel notch is a discrete step with nothing to catch up.
49
+ *
50
+ * The rig keeps velocities, not a pose. Each frame the coast is added to the controller as it finds
51
+ * it, so a writer that adds to the controller composes with a throw, and one that sets its values
52
+ * outright wins. A teleport should {@link stop} the rig, or the remains of a throw carry on from the
53
+ * new pose.
54
+ *
55
+ * The rig subscribes to nothing. Its host hands it the gesture — {@link begin}, {@link move},
56
+ * {@link end}, {@link dolly} — from whatever input it listens to, and calls {@link update} once a frame.
57
+ * A drag the device abandons without an end leaves the rig engaged and still, which the next
58
+ * {@link begin} clears.
59
+ *
60
+ * @author Alex Goldring
61
+ * @copyright Company Named Limited (c) 2026
62
+ */
63
+ export class OrbitalCameraRig {
64
+ /**
65
+ * @type {TopDownCameraController}
66
+ */
67
+ #controller;
68
+
69
+ /**
70
+ * @type {number}
71
+ */
72
+ #orbit_buttons;
73
+
74
+ /**
75
+ * @type {number}
76
+ */
77
+ #pan_buttons;
78
+
79
+ /**
80
+ * @type {number}
81
+ */
82
+ #pitch_min;
83
+
84
+ /**
85
+ * @type {number}
86
+ */
87
+ #pitch_max;
88
+
89
+ /**
90
+ * @type {Vector3}
91
+ */
92
+ #pan_axes;
93
+
94
+ #pitch = new MomentumAxis();
95
+ #yaw = new MomentumAxis();
96
+ #target_x = new MomentumAxis();
97
+ #target_y = new MomentumAxis();
98
+ #target_z = new MomentumAxis();
99
+
100
+ /**
101
+ * @type {number}
102
+ */
103
+ #gesture = GESTURE_NONE;
104
+
105
+ /**
106
+ * @param {TopDownCameraController} controller moved by the rig; a host that filters the camera
107
+ * hands over the controller its input is meant to drive
108
+ * @param {Object} options
109
+ * @param {number} options.orbit_buttons {@link MouseEvent.buttons} bits that start an orbit, see
110
+ * {@link MouseEventButtons}
111
+ * @param {number} options.pan_buttons {@link MouseEvent.buttons} bits that start a pan; orbit wins
112
+ * when both are held
113
+ * @param {number} options.pitch_min radians; 0 looks along the horizon
114
+ * @param {number} options.pitch_max radians; π/2 looks straight down
115
+ * @param {Vector3} options.pan_axes per-axis scale on a pan's world displacement, as
116
+ * {@link TopDownCameraController.pan} takes it; zero Y to keep the target on the ground
117
+ */
118
+ constructor(controller, { orbit_buttons, pan_buttons, pitch_min, pitch_max, pan_axes }) {
119
+ assert.defined(controller, 'controller');
120
+ assert.isNonNegativeInteger(orbit_buttons, 'orbit_buttons');
121
+ assert.isNonNegativeInteger(pan_buttons, 'pan_buttons');
122
+ assert.isNumber(pitch_min, 'pitch_min');
123
+ assert.isNumber(pitch_max, 'pitch_max');
124
+ assert.lessThanOrEqual(pitch_min, pitch_max, 'pitch_min must not exceed pitch_max');
125
+ assert.defined(pan_axes, 'pan_axes');
126
+
127
+ this.#controller = controller;
128
+ this.#orbit_buttons = orbit_buttons;
129
+ this.#pan_buttons = pan_buttons;
130
+ this.#pitch_min = pitch_min;
131
+ this.#pitch_max = pitch_max;
132
+ this.#pan_axes = pan_axes;
133
+ }
134
+
135
+ /**
136
+ * Start a drag. Stops every throw, then takes direct control of orbit or pan, chosen by the
137
+ * buttons held; with neither, the drag moves nothing.
138
+ *
139
+ * @param {number} buttons {@link MouseEvent.buttons} of the event that started the drag
140
+ */
141
+ begin(buttons) {
142
+ this.stop();
143
+
144
+ if ((buttons & this.#orbit_buttons) !== 0) {
145
+ this.#gesture = GESTURE_ORBIT;
146
+
147
+ this.#pitch.engage();
148
+ this.#yaw.engage();
149
+ } else if ((buttons & this.#pan_buttons) !== 0) {
150
+ this.#gesture = GESTURE_PAN;
151
+
152
+ this.#target_x.engage();
153
+ this.#target_y.engage();
154
+ this.#target_z.engage();
155
+ }
156
+ }
157
+
158
+ /**
159
+ * The pointer moved during a drag, applied to the controller at once.
160
+ *
161
+ * An orbit turns the view a full turn per viewport height of travel, pitch clamped to the rig's
162
+ * range; a pan carries the world under the pointer along with it.
163
+ *
164
+ * @param {number} delta_x pixels, the pointer's position less where it was
165
+ * @param {number} delta_y pixels, the pointer's position less where it was, down positive
166
+ * @param {number} viewport_height pixels
167
+ * @param {number} fov vertical field of view, in degrees
168
+ */
169
+ move(delta_x, delta_y, viewport_height, fov) {
170
+ if (this.#gesture === GESTURE_ORBIT) {
171
+ this.#orbit(delta_x, delta_y, viewport_height);
172
+ } else if (this.#gesture === GESTURE_PAN) {
173
+ this.#pan(delta_x, delta_y, viewport_height, fov);
174
+ }
175
+ }
176
+
177
+ /**
178
+ * End the drag, throwing with its recent velocity.
179
+ */
180
+ end() {
181
+ if (this.#gesture === GESTURE_ORBIT) {
182
+ this.#pitch.release(VELOCITY_WINDOW);
183
+ this.#yaw.release(VELOCITY_WINDOW);
184
+ } else if (this.#gesture === GESTURE_PAN) {
185
+ this.#target_x.release(VELOCITY_WINDOW);
186
+ this.#target_y.release(VELOCITY_WINDOW);
187
+ this.#target_z.release(VELOCITY_WINDOW);
188
+ }
189
+
190
+ this.#gesture = GESTURE_NONE;
191
+ }
192
+
193
+ /**
194
+ * Drop any drag and every throw, leaving the controller where it is.
195
+ */
196
+ stop() {
197
+ this.#gesture = GESTURE_NONE;
198
+
199
+ this.#pitch.stop();
200
+ this.#yaw.stop();
201
+ this.#target_x.stop();
202
+ this.#target_y.stop();
203
+ this.#target_z.stop();
204
+ }
205
+
206
+ /**
207
+ * Zoom by wheel notches, within the controller's distance range.
208
+ * @param {number} notches positive away from the target, as a wheel scrolled down reports it
209
+ */
210
+ dolly(notches) {
211
+ const controller = this.#controller;
212
+
213
+ controller.distance = clamp(
214
+ controller.distance * Math.pow(ZOOM_STEP, -notches),
215
+ controller.distanceMin,
216
+ controller.distanceMax
217
+ );
218
+ }
219
+
220
+ /**
221
+ * Advance one frame: sample an engaged drag for its throw, or carry a throw on.
222
+ * @param {number} dt seconds
223
+ */
224
+ update(dt) {
225
+ if (!(dt > 0)) {
226
+ return;
227
+ }
228
+
229
+ const controller = this.#controller;
230
+
231
+ const d_pitch = this.#pitch.update(dt, MOMENTUM_HALF_LIFE, MOMENTUM_STOP_SPEED);
232
+
233
+ if (d_pitch !== 0) {
234
+ const pitch = controller.pitch + d_pitch;
235
+
236
+ // a throw stops at the end of the range instead of pressing on into it
237
+ if (pitch <= this.#pitch_min) {
238
+ controller.pitch = this.#pitch_min;
239
+ this.#pitch.stop();
240
+ } else if (pitch >= this.#pitch_max) {
241
+ controller.pitch = this.#pitch_max;
242
+ this.#pitch.stop();
243
+ } else {
244
+ controller.pitch = pitch;
245
+ }
246
+ }
247
+
248
+ const d_yaw = this.#yaw.update(dt, MOMENTUM_HALF_LIFE, MOMENTUM_STOP_SPEED);
249
+
250
+ if (d_yaw !== 0) {
251
+ controller.yaw = normalize_angle_rad(controller.yaw + d_yaw);
252
+ }
253
+
254
+ const pan_stop_speed = MOMENTUM_STOP_SPEED * controller.distance;
255
+
256
+ const d_x = this.#target_x.update(dt, MOMENTUM_HALF_LIFE, pan_stop_speed);
257
+ const d_y = this.#target_y.update(dt, MOMENTUM_HALF_LIFE, pan_stop_speed);
258
+ const d_z = this.#target_z.update(dt, MOMENTUM_HALF_LIFE, pan_stop_speed);
259
+
260
+ if (d_x !== 0 || d_y !== 0 || d_z !== 0) {
261
+ controller.target._add(d_x, d_y, d_z);
262
+ }
263
+ }
264
+
265
+ /**
266
+ * @param {number} delta_x
267
+ * @param {number} delta_y
268
+ * @param {number} viewport_height
269
+ */
270
+ #orbit(delta_x, delta_y, viewport_height) {
271
+ if (!(viewport_height > 0)) {
272
+ return;
273
+ }
274
+
275
+ const controller = this.#controller;
276
+
277
+ const radians_per_pixel = 2 * Math.PI / viewport_height;
278
+
279
+ // a pitch found outside the range is brought into it, and the jump is no part of the gesture
280
+ const pitch_before = clamp(controller.pitch, this.#pitch_min, this.#pitch_max);
281
+ const pitch_after = clamp(pitch_before + delta_y * radians_per_pixel, this.#pitch_min, this.#pitch_max);
282
+
283
+ controller.pitch = pitch_after;
284
+ this.#pitch.record(pitch_after - pitch_before);
285
+
286
+ // dragging right swings the eye the other way around the target, so the world turns with the hand
287
+ const d_yaw = -delta_x * radians_per_pixel;
288
+
289
+ controller.yaw = normalize_angle_rad(controller.yaw + d_yaw);
290
+ this.#yaw.record(d_yaw);
291
+ }
292
+
293
+ /**
294
+ * @param {number} delta_x
295
+ * @param {number} delta_y
296
+ * @param {number} viewport_height
297
+ * @param {number} fov
298
+ */
299
+ #pan(delta_x, delta_y, viewport_height, fov) {
300
+ if (!(viewport_height > 0)) {
301
+ return;
302
+ }
303
+
304
+ const target = this.#controller.target;
305
+
306
+ const x = target.x;
307
+ const y = target.y;
308
+ const z = target.z;
309
+
310
+ // pan takes where the pointer was less where it is
311
+ v2_pan.set(-delta_x, -delta_y);
312
+
313
+ TopDownCameraController.pan(v2_pan, this.#controller, viewport_height, fov, this.#pan_axes);
314
+
315
+ this.#target_x.record(target.x - x);
316
+ this.#target_y.record(target.y - y);
317
+ this.#target_z.record(target.z - z);
318
+ }
319
+ }
@@ -1,11 +1,3 @@
1
- /**
2
- *
3
- * @param {number} displacement_x
4
- * @param {number} displacement_y
5
- * @param {TopDownCameraController} input source thing being rotated
6
- * @param {TopDownCameraController} output target thing where the result is written to
7
- */
8
- export function rotate_from_view(displacement_x: number, displacement_y: number, input: TopDownCameraController, output: TopDownCameraController): void;
9
1
  /**
10
2
  * The orientation a controller looks along. The camera's transform is built from this and the
11
3
  * distance, so anything that needs the view's own axes takes them from here rather than from
@@ -15,7 +7,41 @@ export function rotate_from_view(displacement_x: number, displacement_y: number,
15
7
  * @param {TopDownCameraController} controller
16
8
  */
17
9
  export function top_down_view_rotation(result: Quaternion, controller: TopDownCameraController): void;
10
+ /**
11
+ * Write a controller's pose onto a camera transform: the view rotation, and the eye its distance back
12
+ * from the target along it, the distance held within the controller's range.
13
+ *
14
+ * @param {TopDownCameraController} control
15
+ * @param {Transform64} result
16
+ */
17
+ export function computeTopDownTransform(control: TopDownCameraController, result: Transform64): void;
18
+ /**
19
+ * Move a controller's target `distance` world units along where its view faces: `right` and
20
+ * `forward` pick the direction in the view's own frame, so a target can be flown up or down a slope.
21
+ * A diagonal travels the same distance as a straight move.
22
+ *
23
+ * The viewer's right is the negated X axis of the view rotation, as {@link pan} explains.
24
+ *
25
+ * @param {TopDownCameraController} controller its {@link TopDownCameraController#target} is moved
26
+ * @param {number} right −1 to 1, positive to the viewer's right
27
+ * @param {number} forward −1 to 1, positive into the screen
28
+ * @param {number} distance world units
29
+ */
30
+ export function top_down_truck_in_view(controller: TopDownCameraController, right: number, forward: number, distance: number): void;
31
+ /**
32
+ * Move a controller's target `distance` world units across the ground, along the way its view is
33
+ * turned: `forward` is the view's heading with the pitch taken out, so it keeps its full speed
34
+ * however steeply the view looks down, straight down included, and never moves the target off the
35
+ * ground. A diagonal travels the same distance as a straight move.
36
+ *
37
+ * @param {TopDownCameraController} controller its {@link TopDownCameraController#target} is moved
38
+ * @param {number} right −1 to 1, positive to the viewer's right
39
+ * @param {number} forward −1 to 1, positive along the view's heading
40
+ * @param {number} distance world units
41
+ */
42
+ export function top_down_truck_on_ground(controller: TopDownCameraController, right: number, forward: number, distance: number): void;
18
43
  export default TopDownCameraController;
44
+ import Quaternion from "../../../../../core/geom/Quaternion.js";
19
45
  declare class TopDownCameraController {
20
46
  static fromJSON(j: any): TopDownCameraController;
21
47
  constructor(options: any);
@@ -86,9 +112,7 @@ declare class TopDownCameraController {
86
112
  declare namespace TopDownCameraController {
87
113
  export let typeName: string;
88
114
  export { pan };
89
- export { rotate_from_view as rotate };
90
115
  }
91
- import Quaternion from "../../../../../core/geom/Quaternion.js";
92
116
  import Vector3 from '../../../../../core/geom/Vector3.js';
93
117
  /**
94
118
  * Move a controller's target across the view plane it is looking at, by a displacement given in
@@ -1 +1 @@
1
- {"version":3,"file":"TopDownCameraController.d.ts","sourceRoot":"","sources":["../../../../../../../src/engine/graphics/ecs/camera/topdown/TopDownCameraController.js"],"names":[],"mappings":"AAyBA;;;;;;GAMG;AACH,iDALW,MAAM,kBACN,MAAM,SACN,uBAAuB,UACvB,uBAAuB,QAiBjC;AAED;;;;;;;GAOG;AACH,+CAHW,UAAU,cACV,uBAAuB,QAIjC;;AAkED;IA+JI,iDAMC;IA5JD,0BAKC;IAbD,gBAAuB;IACvB,cAAU;IACV,YAAQ;IACR,aAAS;IACT,iBAAa;IACb,oBAAgB;IAChB,oBAAkB;IASlB;;;;;;;;;OASG;IACH,yBAJW,OAAO,UACP,OAAO,OACP,OAAO,QAgCjB;IAED;;;;OAIG;IACH,cAHW,uBAAuB,GACrB,OAAO,CAWnB;IAED;;;OAGG;IACH,YAFW,uBAAuB,QAYjC;IAED;;;OAGG;IACH,SAFa,uBAAuB,CAQnC;IAED;;;;;OAKG;IACH,QAJW,uBAAuB,KACvB,uBAAuB,KACvB,MAAM,QAahB;IAED;;;;;;;;;;;;MAYC;IAED;;;;;;;;aAsBC;CASJ;;;;;;uBA7RsB,wCAAwC;oBAC3C,qCAAqC;AAsEzD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,iDAPW,uBAAuB,sBACvB,MAAM,OAEN,MAAM,eACN,OAAO,QA2BjB"}
1
+ {"version":3,"file":"TopDownCameraController.d.ts","sourceRoot":"","sources":["../../../../../../../src/engine/graphics/ecs/camera/topdown/TopDownCameraController.js"],"names":[],"mappings":"AAyBA;;;;;;;GAOG;AACH,+CAHW,UAAU,cACV,uBAAuB,QAIjC;AAiED;;;;;;GAMG;AACH,iDAHW,uBAAuB,6BA2BjC;AAED;;;;;;;;;;;GAWG;AACH,mDALW,uBAAuB,SACvB,MAAM,WACN,MAAM,YACN,MAAM,QAMhB;AAED;;;;;;;;;;GAUG;AACH,qDALW,uBAAuB,SACvB,MAAM,WACN,MAAM,YACN,MAAM,QAMhB;;uBAhKsB,wCAAwC;AAoM/D;IAyKI,iDAMC;IAtKD,0BAKC;IAbD,gBAAuB;IACvB,cAAU;IACV,YAAQ;IACR,aAAS;IACT,iBAAa;IACb,oBAAgB;IAChB,oBAAkB;IASlB;;;;;;;;;OASG;IACH,yBAJW,OAAO,UACP,OAAO,OACP,OAAO,QA0CjB;IAED;;;;OAIG;IACH,cAHW,uBAAuB,GACrB,OAAO,CAWnB;IAED;;;OAGG;IACH,YAFW,uBAAuB,QAYjC;IAED;;;OAGG;IACH,SAFa,uBAAuB,CAQnC;IAED;;;;;OAKG;IACH,QAJW,uBAAuB,KACvB,uBAAuB,KACvB,MAAM,QAahB;IAED;;;;;;;;;;;;MAYC;IAED;;;;;;;;aAsBC;CASJ;;;;;oBAnXmB,qCAAqC;AA8CzD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,iDAPW,uBAAuB,sBACvB,MAAM,OAEN,MAAM,eACN,OAAO,QA2BjB"}
@@ -6,9 +6,9 @@ import {
6
6
  import { normalize_angle_rad } from "../../../../../core/geom/normalize_angle_rad.js";
7
7
  import Quaternion from "../../../../../core/geom/Quaternion.js";
8
8
  import Vector3 from '../../../../../core/geom/Vector3.js';
9
+ import { clamp } from "../../../../../core/math/clamp.js";
9
10
  import { lerp } from "../../../../../core/math/lerp.js";
10
11
  import { max2 } from "../../../../../core/math/max2.js";
11
- import { sign_not_zero } from "../../../../../core/math/sign_not_zero.js";
12
12
  import { v3_distance } from "../../../../../core/geom/vec3/v3_distance.js";
13
13
 
14
14
  const m4 = [];
@@ -21,31 +21,7 @@ const q_view = new Quaternion();
21
21
 
22
22
  const v3_view_right = new Vector3();
23
23
  const v3_view_up = new Vector3();
24
-
25
-
26
- /**
27
- *
28
- * @param {number} displacement_x
29
- * @param {number} displacement_y
30
- * @param {TopDownCameraController} input source thing being rotated
31
- * @param {TopDownCameraController} output target thing where the result is written to
32
- */
33
- export function rotate_from_view(displacement_x, displacement_y, input, output) {
34
- // crop yaw and pitch to safe range of -PI to PI
35
- const i_pitch = normalize_angle_rad(input.pitch);
36
- const i_yaw = normalize_angle_rad(input.yaw);
37
- const i_roll = normalize_angle_rad(input.roll);
38
-
39
- const quaternion = Quaternion.fromEulerAngles(i_pitch, i_yaw, i_roll);
40
- const forward = Vector3.up.clone();
41
- forward.applyQuaternion(quaternion);
42
-
43
- // flip rotation axis when pitch is over 90deg in either direction, that is UP vector is pointing down
44
- const d_yaw = sign_not_zero(forward.y) * displacement_x;
45
-
46
- output.yaw = i_yaw + d_yaw;
47
- output.pitch = i_pitch - displacement_y;
48
- }
24
+ const v3_view_forward = new Vector3();
49
25
 
50
26
  /**
51
27
  * The orientation a controller looks along. The camera's transform is built from this and the
@@ -122,6 +98,107 @@ function pan(delta, controller, viewport_height_px, fov, multiplier = Vector3.on
122
98
  move_along(target, v3_view_up, -delta.y * m, multiplier);
123
99
  }
124
100
 
101
+ /**
102
+ * Write a controller's pose onto a camera transform: the view rotation, and the eye its distance back
103
+ * from the target along it, the distance held within the controller's range.
104
+ *
105
+ * @param {TopDownCameraController} control
106
+ * @param {Transform64} result
107
+ */
108
+ export function computeTopDownTransform(control, result) {
109
+
110
+ //clamp the distance
111
+ const distance = clamp(control.distance, control.distanceMin, control.distanceMax);
112
+ const target = control.target;
113
+
114
+ // compute rotation. The transform holds its rotation as four numbers rather than a Quaternion,
115
+ // so the euler composition runs in a scratch and lands in the transform afterwards.
116
+ top_down_view_rotation(q_view, control);
117
+
118
+ result.setRotation(q_view.x, q_view.y, q_view.z, q_view.w);
119
+
120
+ // compute camera position
121
+ v3_view_forward.copy(Vector3.forward);
122
+ v3_view_forward.applyQuaternion(q_view);
123
+
124
+ v3_view_forward.multiplyScalar(-distance);
125
+
126
+ v3_view_forward.add(target);
127
+
128
+ result.setTranslation(v3_view_forward.x, v3_view_forward.y, v3_view_forward.z);
129
+
130
+ // the rotation moved, so the matrix owes an update
131
+ result.updateMatrix();
132
+ }
133
+
134
+ /**
135
+ * Move a controller's target `distance` world units along where its view faces: `right` and
136
+ * `forward` pick the direction in the view's own frame, so a target can be flown up or down a slope.
137
+ * A diagonal travels the same distance as a straight move.
138
+ *
139
+ * The viewer's right is the negated X axis of the view rotation, as {@link pan} explains.
140
+ *
141
+ * @param {TopDownCameraController} controller its {@link TopDownCameraController#target} is moved
142
+ * @param {number} right −1 to 1, positive to the viewer's right
143
+ * @param {number} forward −1 to 1, positive into the screen
144
+ * @param {number} distance world units
145
+ */
146
+ export function top_down_truck_in_view(controller, right, forward, distance) {
147
+ top_down_view_rotation(q_view, controller);
148
+
149
+ truck_in_rotation(controller.target, q_view, right, forward, distance);
150
+ }
151
+
152
+ /**
153
+ * Move a controller's target `distance` world units across the ground, along the way its view is
154
+ * turned: `forward` is the view's heading with the pitch taken out, so it keeps its full speed
155
+ * however steeply the view looks down, straight down included, and never moves the target off the
156
+ * ground. A diagonal travels the same distance as a straight move.
157
+ *
158
+ * @param {TopDownCameraController} controller its {@link TopDownCameraController#target} is moved
159
+ * @param {number} right −1 to 1, positive to the viewer's right
160
+ * @param {number} forward −1 to 1, positive along the view's heading
161
+ * @param {number} distance world units
162
+ */
163
+ export function top_down_truck_on_ground(controller, right, forward, distance) {
164
+ q_view.fromEulerAnglesZYX(0, controller.yaw, 0);
165
+
166
+ truck_in_rotation(controller.target, q_view, right, forward, distance);
167
+ }
168
+
169
+ /**
170
+ * @param {Vector3} target
171
+ * @param {Quaternion} rotation
172
+ * @param {number} right
173
+ * @param {number} forward
174
+ * @param {number} distance
175
+ */
176
+ function truck_in_rotation(target, rotation, right, forward, distance) {
177
+ const length = Math.hypot(right, forward);
178
+
179
+ if (length === 0) {
180
+ return;
181
+ }
182
+
183
+ const step = distance / length;
184
+
185
+ v3_view_right.copy(Vector3.right);
186
+ v3_view_right.applyQuaternion(rotation);
187
+
188
+ v3_view_forward.copy(Vector3.forward);
189
+ v3_view_forward.applyQuaternion(rotation);
190
+
191
+ // the viewer's right is the negated X axis
192
+ const along_right = -right * step;
193
+ const along_forward = forward * step;
194
+
195
+ target._add(
196
+ v3_view_right.x * along_right + v3_view_forward.x * along_forward,
197
+ v3_view_right.y * along_right + v3_view_forward.y * along_forward,
198
+ v3_view_right.z * along_right + v3_view_forward.z * along_forward
199
+ );
200
+ }
201
+
125
202
 
126
203
  class TopDownCameraController {
127
204
  target = new Vector3();
@@ -169,9 +246,19 @@ class TopDownCameraController {
169
246
 
170
247
  m4_euler_angles(angles, m4, 2, 1, 0)
171
248
 
172
- this.pitch = angles[2];
173
- this.yaw = angles[1];
174
- this.roll = angles[0];
249
+ if (Math.abs(angles[2]) > Math.PI / 2) {
250
+ // The decomposition holds yaw within ±π/2 and, for about half of all views, answers with the
251
+ // view turned over — pitched past the vertical and rolled half a turn. That is the same
252
+ // rotation as the upright one, half a turn of yaw away, which is the pose an orbit expects:
253
+ // pitch within ±π/2 and, for an up of +Y, no roll.
254
+ this.pitch = normalize_angle_rad(angles[2] + Math.PI);
255
+ this.yaw = normalize_angle_rad(Math.PI - angles[1]);
256
+ this.roll = normalize_angle_rad(angles[0] + Math.PI);
257
+ } else {
258
+ this.pitch = angles[2];
259
+ this.yaw = angles[1];
260
+ this.roll = angles[0];
261
+ }
175
262
 
176
263
  this.distance = v3_distance(eye[0], eye[1], eye[2], target[0], target[1], target[2]);
177
264
 
@@ -294,7 +381,6 @@ class TopDownCameraController {
294
381
  TopDownCameraController.typeName = "TopDownCameraController";
295
382
 
296
383
  TopDownCameraController.pan = pan;
297
- TopDownCameraController.rotate = rotate_from_view;
298
384
 
299
385
  export default TopDownCameraController;
300
386
 
@@ -1,9 +1,3 @@
1
- /**
2
- *
3
- * @param {TopDownCameraController} control
4
- * @param {Transform64} result
5
- */
6
- export function computeTopDownTransform(control: TopDownCameraController, result: Transform64): void;
7
1
  /**
8
2
  *
9
3
  * @param {Transform64} transform
@@ -12,8 +6,8 @@ export function computeTopDownTransform(control: TopDownCameraController, result
12
6
  */
13
7
  export function setCameraControllerFromTransform(transform: Transform64, controller: TopDownCameraController, distance?: number): void;
14
8
  export default TopDownCameraControllerSystem;
15
- import TopDownCameraController from './TopDownCameraController.js';
16
9
  import { Transform64 } from '../../../../ecs/transform/Transform64.js';
10
+ import TopDownCameraController from './TopDownCameraController.js';
17
11
  declare class TopDownCameraControllerSystem extends System<any> {
18
12
  constructor();
19
13
  enabled: ObservedValue<boolean>;
@@ -1 +1 @@
1
- {"version":3,"file":"TopDownCameraControllerSystem.d.ts","sourceRoot":"","sources":["../../../../../../../src/engine/graphics/ecs/camera/topdown/TopDownCameraControllerSystem.js"],"names":[],"mappings":"AAqFA;;;;GAIG;AACH,iDAHW,uBAAuB,UACvB,WAAW,QA0BrB;AAED;;;;;GAKG;AACH,4DAJW,WAAW,cACX,uBAAuB,aACvB,MAAM,QAahB;;oCA1H+D,8BAA8B;4BAHlE,0CAA0C;AAoBtE;;IACI,gCAAkC;IAClC,iDAAyC;IACzC,mEAEE;IAEF;;;;;OAKG;IACH,8BA0BC;IAED,6BAaC;CACJ;uBA9EsB,2BAA2B;0BAHxB,4CAA4C;4CAE1B,0DAA0D"}
1
+ {"version":3,"file":"TopDownCameraControllerSystem.d.ts","sourceRoot":"","sources":["../../../../../../../src/engine/graphics/ecs/camera/topdown/TopDownCameraControllerSystem.js"],"names":[],"mappings":"AAgFA;;;;;GAKG;AACH,4DAJW,WAAW,cACX,uBAAuB,aACvB,MAAM,QAahB;;4BA1F2B,0CAA0C;oCAGL,8BAA8B;AAa/F;;IACI,gCAAkC;IAClC,iDAAyC;IACzC,mEAEE;IAEF;;;;;OAKG;IACH,8BA0BC;IAED,6BAaC;CACJ;uBA1EsB,2BAA2B;0BAHxB,4CAA4C;4CAE1B,0DAA0D"}
@@ -1,5 +1,4 @@
1
1
  import Vector3 from "../../../../../core/geom/Vector3.js";
2
- import { clamp } from "../../../../../core/math/clamp.js";
3
2
  import ObservedValue from "../../../../../core/model/ObservedValue.js";
4
3
  import { ResourceAccessKind } from "../../../../../core/model/ResourceAccessKind.js";
5
4
  import { ResourceAccessSpecification } from "../../../../../core/model/ResourceAccessSpecification.js";
@@ -9,14 +8,10 @@ import { t64_read_pose_array } from "../../../../ecs/transform/t64_read_pose_arr
9
8
  import { Transform64 } from '../../../../ecs/transform/Transform64.js';
10
9
 
11
10
 
12
- import TopDownCameraController, { top_down_view_rotation } from './TopDownCameraController.js';
13
- import Quaternion from "../../../../../core/geom/Quaternion.js";
11
+ import TopDownCameraController, { computeTopDownTransform } from './TopDownCameraController.js';
14
12
  import { v3_quaternion_apply } from "../../../../../core/geom/vec3/v3_quaternion_apply.js";
15
13
 
16
14
 
17
- const v3_scratch = new Vector3();
18
- const q_scratch = new Quaternion();
19
-
20
15
  /**
21
16
  * A camera's translation and rotation before and after its controller rewrites them, to tell a move from
22
17
  * a rewrite of the same pose.
@@ -83,37 +78,6 @@ class TopDownCameraControllerSystem extends System {
83
78
  }
84
79
  }
85
80
 
86
- /**
87
- *
88
- * @param {TopDownCameraController} control
89
- * @param {Transform64} result
90
- */
91
- export function computeTopDownTransform(control, result) {
92
-
93
- //clamp the distance
94
- const distance = clamp(control.distance, control.distanceMin, control.distanceMax);
95
- const target = control.target;
96
-
97
- // compute rotation. The transform holds its rotation as four numbers rather than a Quaternion,
98
- // so the euler composition runs in a scratch and lands in the transform afterwards.
99
- top_down_view_rotation(q_scratch, control);
100
-
101
- result.setRotation(q_scratch.x, q_scratch.y, q_scratch.z, q_scratch.w);
102
-
103
- // compute camera position
104
- v3_scratch.copy(Vector3.forward);
105
- v3_scratch.applyQuaternion(q_scratch);
106
-
107
- v3_scratch.multiplyScalar(-distance);
108
-
109
- v3_scratch.add(target);
110
-
111
- result.setTranslation(v3_scratch.x, v3_scratch.y, v3_scratch.z);
112
-
113
- // the rotation moved, so the matrix owes an update
114
- result.updateMatrix();
115
- }
116
-
117
81
  /**
118
82
  *
119
83
  * @param {Transform64} transform
@@ -3,6 +3,14 @@ export default Water;
3
3
  * Optical properties of an authored water surface. MeshletGeometry supplies its shape and
4
4
  * Transform64 supplies its placement.
5
5
  *
6
+ * **The geometry is the body's area: author it with the fewest triangles that cover it.** The renderer tessellates
7
+ * the surface on the GPU from the view, so subdividing a flat area adds no detail to the waves, and it costs every
8
+ * frame. The lattice is cut against each authored triangle: every lattice cell an internal edge crosses is clipped
9
+ * and drawn once per triangle it touches, and no 8 × 8 patch spans an internal edge. Measured on an integrated GPU
10
+ * (the many-waters example at 1280 × 720), a pond and a stream authored as six-segment grids (3,012 triangles) drew
11
+ * 669,324 lattice vertices a pass where two triangles each drew 146,772, and cost 11.65 ms more a frame. Use more
12
+ * triangles only where the area needs them: its outline, a hole, a crease, a change of normal.
13
+ *
6
14
  * Everything optical here is physical and in SI units (WATER_OPTICS_PLAN_2026_10_02.md §7.1): the
7
15
  * index of refraction, the micro-roughness of the surface, and what fills the body — a
8
16
  * {@link ParticipatingMedium}, the same description the path tracer, fog and glass read. Presets
@@ -1 +1 @@
1
- {"version":3,"file":"Water.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/graphics/ecs/water/Water.js"],"names":[],"mappings":";AAUA;;;;;;;;;;;;;;;GAeG;AACH;IA6CI,0BASC;IArDD;;;OAGG;IACH,KAFU,OAAO,CAEQ;IAEzB;;;;OAIG;IACH,WAFU,OAAO,CAEa;IAE9B;;;;;;;OAOG;IACH,oBAFU,eAAe,CAEuB;IAEhD,8CAA8C;IAC9C,kBAAyB;IAWzB,yCAAyC;IACzC,uCAMC;IAbD;;;;OAIG;IACH,kCAAqC;IAqBrC,0BAoBC;IAED;;;;;MAOC;;CACJ;;;;oBA/GmB,kCAAkC;4BAC1B,2CAA2C;mBAFpD,0CAA0C;oCAGzB,0DAA0D"}
1
+ {"version":3,"file":"Water.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/graphics/ecs/water/Water.js"],"names":[],"mappings":";AAUA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH;IA6CI,0BASC;IArDD;;;OAGG;IACH,KAFU,OAAO,CAEQ;IAEzB;;;;OAIG;IACH,WAFU,OAAO,CAEa;IAE9B;;;;;;;OAOG;IACH,oBAFU,eAAe,CAEuB;IAEhD,8CAA8C;IAC9C,kBAAyB;IAWzB,yCAAyC;IACzC,uCAMC;IAbD;;;;OAIG;IACH,kCAAqC;IAqBrC,0BAoBC;IAED;;;;;MAOC;;CACJ;;;;oBAvHmB,kCAAkC;4BAC1B,2CAA2C;mBAFpD,0CAA0C;oCAGzB,0DAA0D"}