@woosh/meep-engine 3.64.1 → 3.65.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (324) hide show
  1. package/CHANGELOG.md +85 -0
  2. package/build/bundle-worker-image-decoder.js +1 -1
  3. package/build/bundle-worker-terrain.js +1 -1
  4. package/package.json +1 -1
  5. package/src/core/binary/BinaryBuffer.d.ts +5 -0
  6. package/src/core/binary/BinaryBuffer.d.ts.map +1 -1
  7. package/src/core/binary/BinaryBuffer.js +39 -5
  8. package/src/core/geom/3d/topology/struct/binary/io/bt_boundary_loop.d.ts +65 -6
  9. package/src/core/geom/3d/topology/struct/binary/io/bt_boundary_loop.d.ts.map +1 -1
  10. package/src/core/geom/3d/topology/struct/binary/io/bt_boundary_loop.js +451 -101
  11. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_close_boundary_holes.d.ts +4 -2
  12. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_close_boundary_holes.d.ts.map +1 -1
  13. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_close_boundary_holes.js +4 -2
  14. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_fill_small_holes.d.ts +8 -1
  15. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_fill_small_holes.d.ts.map +1 -1
  16. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_fill_small_holes.js +45 -10
  17. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_face_find_path_polyanya.d.ts.map +1 -1
  18. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_face_find_path_polyanya.js +237 -119
  19. package/src/engine/graphics/ecs/grass/GrassSpecies.d.ts +143 -0
  20. package/src/engine/graphics/ecs/grass/GrassSpecies.d.ts.map +1 -0
  21. package/src/engine/graphics/ecs/grass/GrassSpecies.js +189 -0
  22. package/src/engine/graphics/ecs/grass/GrassSurface.d.ts +25 -0
  23. package/src/engine/graphics/ecs/grass/GrassSurface.d.ts.map +1 -0
  24. package/src/engine/graphics/ecs/grass/GrassSurface.js +32 -0
  25. package/src/engine/graphics/ecs/grass/GroundCover.d.ts +68 -0
  26. package/src/engine/graphics/ecs/grass/GroundCover.d.ts.map +1 -0
  27. package/src/engine/graphics/ecs/grass/GroundCover.js +85 -0
  28. package/src/engine/graphics3/GrassSurfaceSystem.d.ts +153 -0
  29. package/src/engine/graphics3/GrassSurfaceSystem.d.ts.map +1 -0
  30. package/src/engine/graphics3/GrassSurfaceSystem.js +1076 -0
  31. package/src/engine/graphics3/GroundCoverSystem.d.ts +33 -0
  32. package/src/engine/graphics3/GroundCoverSystem.d.ts.map +1 -0
  33. package/src/engine/graphics3/GroundCoverSystem.js +58 -0
  34. package/src/engine/network/NetworkSession.d.ts.map +1 -1
  35. package/src/engine/network/NetworkSession.js +372 -110
  36. package/src/engine/network/orchestrator/NetworkPeer.d.ts.map +1 -1
  37. package/src/engine/network/orchestrator/NetworkPeer.js +6 -1
  38. package/src/engine/network/orchestrator/ServerAuthoritativeClient.d.ts.map +1 -1
  39. package/src/engine/network/orchestrator/ServerAuthoritativeClient.js +9 -2
  40. package/src/engine/network/orchestrator/ServerAuthoritativeServer.d.ts.map +1 -1
  41. package/src/engine/network/orchestrator/ServerAuthoritativeServer.js +325 -140
  42. package/src/engine/network/sim/SimActionExecutor.d.ts +4 -1
  43. package/src/engine/network/sim/SimActionExecutor.d.ts.map +1 -1
  44. package/src/engine/network/sim/SimActionExecutor.js +65 -9
  45. package/src/engine/network/state/PredictionHistory.d.ts.map +1 -1
  46. package/src/engine/network/state/PredictionHistory.js +6 -1
  47. package/src/engine/network/time/AdaptiveRenderDelay.d.ts +25 -14
  48. package/src/engine/network/time/AdaptiveRenderDelay.d.ts.map +1 -1
  49. package/src/engine/network/time/AdaptiveRenderDelay.js +38 -20
  50. package/src/engine/network/time/RenderPlayout.d.ts +23 -3
  51. package/src/engine/network/time/RenderPlayout.d.ts.map +1 -1
  52. package/src/engine/network/time/RenderPlayout.js +40 -4
  53. package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
  54. package/src/engine/physics/fluid/obstacle/chunk_fluid_obstacle_record.d.ts +5 -1
  55. package/src/engine/physics/fluid/obstacle/chunk_fluid_obstacle_record.d.ts.map +1 -1
  56. package/src/engine/physics/fluid/obstacle/chunk_fluid_obstacle_record.js +27 -1
  57. package/src/shade/device/shader/ShaderModuleCompiler.d.ts +2 -0
  58. package/src/shade/device/shader/ShaderModuleCompiler.d.ts.map +1 -1
  59. package/src/shade/device/shader/ShaderModuleCompiler.js +25 -2
  60. package/src/shade/playground/grass_surfaces/index.html +75 -0
  61. package/src/shade/playground/grass_surfaces/main.d.ts +2 -0
  62. package/src/shade/playground/grass_surfaces/main.d.ts.map +1 -0
  63. package/src/shade/playground/grass_surfaces/main.js +593 -0
  64. package/src/shade/playground/grass_surfaces/read_grass_surfaces.d.ts +62 -0
  65. package/src/shade/playground/grass_surfaces/read_grass_surfaces.d.ts.map +1 -0
  66. package/src/shade/playground/grass_surfaces/read_grass_surfaces.js +188 -0
  67. package/src/shade/playground/probe_clipmap_box/README.md +5 -1
  68. package/src/shade/playground/probe_clipmap_box/index.html +12 -0
  69. package/src/shade/playground/probe_clipmap_box/main.d.ts.map +1 -1
  70. package/src/shade/playground/probe_clipmap_box/main.js +15 -0
  71. package/src/shade/playground/probe_clipmap_box/scene.d.ts +8 -1
  72. package/src/shade/playground/probe_clipmap_box/scene.d.ts.map +1 -1
  73. package/src/shade/playground/probe_clipmap_box/scene.js +74 -6
  74. package/src/shade/renderer/Renderer.d.ts.map +1 -1
  75. package/src/shade/renderer/Renderer.js +47 -43
  76. package/src/shade/renderer/extension/ViewTextures.d.ts +12 -5
  77. package/src/shade/renderer/extension/ViewTextures.d.ts.map +1 -1
  78. package/src/shade/renderer/extension/ViewTextures.js +19 -5
  79. package/src/shade/renderer/fluid/GPUFluidCascadeStack.d.ts +3 -1
  80. package/src/shade/renderer/fluid/GPUFluidCascadeStack.d.ts.map +1 -1
  81. package/src/shade/renderer/fluid/GPUFluidCascadeStack.js +6 -1
  82. package/src/shade/renderer/fluid/forcing/GPUFluidForcing.d.ts +7 -0
  83. package/src/shade/renderer/fluid/forcing/GPUFluidForcing.d.ts.map +1 -1
  84. package/src/shade/renderer/fluid/forcing/GPUFluidForcing.js +10 -0
  85. package/src/shade/renderer/geometry/sdf/GPUGeometrySDFManager.d.ts +7 -1
  86. package/src/shade/renderer/geometry/sdf/GPUGeometrySDFManager.d.ts.map +1 -1
  87. package/src/shade/renderer/geometry/sdf/GPUGeometrySDFManager.js +46 -15
  88. package/src/shade/renderer/geometry/sdf/bundle/ModelCacheBundle.d.ts +3 -2
  89. package/src/shade/renderer/geometry/sdf/bundle/ModelCacheBundle.d.ts.map +1 -1
  90. package/src/shade/renderer/geometry/sdf/bundle/ModelCacheBundle.js +3 -2
  91. package/src/shade/renderer/geometry/sdf/bundle/model_cache_bundle_sort_morton.d.ts +22 -0
  92. package/src/shade/renderer/geometry/sdf/bundle/model_cache_bundle_sort_morton.d.ts.map +1 -0
  93. package/src/shade/renderer/geometry/sdf/bundle/model_cache_bundle_sort_morton.js +247 -0
  94. package/src/shade/renderer/global_illumination/probe_clipmap/GPUProbeClipmapGI.d.ts +50 -0
  95. package/src/shade/renderer/global_illumination/probe_clipmap/GPUProbeClipmapGI.d.ts.map +1 -1
  96. package/src/shade/renderer/global_illumination/probe_clipmap/GPUProbeClipmapGI.js +101 -0
  97. package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSettings.d.ts +2 -1
  98. package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSettings.d.ts.map +1 -1
  99. package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSettings.js +2 -1
  100. package/src/shade/renderer/global_illumination/probe_clipmap/brick/GPUBrickClipmap.d.ts +8 -0
  101. package/src/shade/renderer/global_illumination/probe_clipmap/brick/GPUBrickClipmap.d.ts.map +1 -1
  102. package/src/shade/renderer/global_illumination/probe_clipmap/brick/GPUBrickClipmap.js +12 -0
  103. package/src/shade/renderer/global_illumination/probe_clipmap/brick/chunk_brick_clipmap_trace.d.ts +12 -6
  104. package/src/shade/renderer/global_illumination/probe_clipmap/brick/chunk_brick_clipmap_trace.d.ts.map +1 -1
  105. package/src/shade/renderer/global_illumination/probe_clipmap/brick/chunk_brick_clipmap_trace.js +35 -29
  106. package/src/shade/renderer/global_illumination/probe_clipmap/debug/graph_probe_clipmap_debug_brick_clipmap.d.ts.map +1 -1
  107. package/src/shade/renderer/global_illumination/probe_clipmap/debug/graph_probe_clipmap_debug_brick_clipmap.js +4 -2
  108. package/src/shade/renderer/global_illumination/probe_clipmap/debug/graph_probe_clipmap_debug_brick_material.d.ts.map +1 -1
  109. package/src/shade/renderer/global_illumination/probe_clipmap/debug/graph_probe_clipmap_debug_brick_material.js +6 -4
  110. package/src/shade/renderer/global_illumination/probe_clipmap/material/GPUBrickMaterial.d.ts.map +1 -1
  111. package/src/shade/renderer/global_illumination/probe_clipmap/material/GPUBrickMaterial.js +2 -1
  112. package/src/shade/renderer/global_illumination/probe_clipmap/material/chunk_brick_material.d.ts +6 -5
  113. package/src/shade/renderer/global_illumination/probe_clipmap/material/chunk_brick_material.d.ts.map +1 -1
  114. package/src/shade/renderer/global_illumination/probe_clipmap/material/chunk_brick_material.js +15 -14
  115. package/src/shade/renderer/global_illumination/probe_clipmap/material/shader_brick_material_gather.d.ts.map +1 -1
  116. package/src/shade/renderer/global_illumination/probe_clipmap/material/shader_brick_material_gather.js +4 -2
  117. package/src/shade/renderer/global_illumination/probe_clipmap/probe/GPUProbeClipmap.d.ts +19 -2
  118. package/src/shade/renderer/global_illumination/probe_clipmap/probe/GPUProbeClipmap.d.ts.map +1 -1
  119. package/src/shade/renderer/global_illumination/probe_clipmap/probe/GPUProbeClipmap.js +128 -10
  120. package/src/shade/renderer/global_illumination/probe_clipmap/probe/PROBE_CLIPMAP.d.ts +19 -1
  121. package/src/shade/renderer/global_illumination/probe_clipmap/probe/PROBE_CLIPMAP.d.ts.map +1 -1
  122. package/src/shade/renderer/global_illumination/probe_clipmap/probe/PROBE_CLIPMAP.js +27 -2
  123. package/src/shade/renderer/global_illumination/probe_clipmap/probe/ProbeClipmapScreenWants.d.ts +19 -11
  124. package/src/shade/renderer/global_illumination/probe_clipmap/probe/ProbeClipmapScreenWants.d.ts.map +1 -1
  125. package/src/shade/renderer/global_illumination/probe_clipmap/probe/ProbeClipmapScreenWants.js +35 -11
  126. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap.d.ts.map +1 -1
  127. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap.js +3 -1
  128. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_box_cages.d.ts +40 -0
  129. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_box_cages.d.ts.map +1 -0
  130. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_box_cages.js +174 -0
  131. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_hit_material.d.ts +4 -4
  132. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_hit_material.js +10 -10
  133. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_parallax.d.ts +31 -0
  134. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_parallax.d.ts.map +1 -0
  135. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_parallax.js +54 -0
  136. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_sample_radiance.d.ts +8 -4
  137. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_sample_radiance.d.ts.map +1 -1
  138. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_sample_radiance.js +10 -21
  139. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_want.d.ts +7 -2
  140. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_want.d.ts.map +1 -1
  141. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_want.js +33 -29
  142. package/src/shade/renderer/global_illumination/probe_clipmap/probe/graph_probe_clipmap_want_meshlets.d.ts +28 -0
  143. package/src/shade/renderer/global_illumination/probe_clipmap/probe/graph_probe_clipmap_want_meshlets.d.ts.map +1 -0
  144. package/src/shade/renderer/global_illumination/probe_clipmap/probe/graph_probe_clipmap_want_meshlets.js +138 -0
  145. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_due.d.ts +13 -9
  146. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_due.d.ts.map +1 -1
  147. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_due.js +18 -11
  148. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_light.js +1 -1
  149. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_relocate.d.ts.map +1 -1
  150. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_relocate.js +4 -2
  151. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_retire.d.ts.map +1 -1
  152. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_retire.js +2 -3
  153. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_schedule.d.ts +5 -3
  154. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_schedule.d.ts.map +1 -1
  155. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_schedule.js +15 -30
  156. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_shade.js +1 -1
  157. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_trace.d.ts.map +1 -1
  158. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_trace.js +4 -2
  159. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_want.d.ts +11 -0
  160. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_want.d.ts.map +1 -1
  161. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_want.js +29 -2
  162. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_want_late.d.ts +40 -0
  163. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_want_late.d.ts.map +1 -0
  164. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_want_late.js +126 -0
  165. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_want_meshlets.d.ts +36 -0
  166. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_want_meshlets.d.ts.map +1 -0
  167. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_want_meshlets.js +221 -0
  168. package/src/shade/renderer/gpu_primitive/age_eviction/chunk_age_eviction.d.ts +40 -0
  169. package/src/shade/renderer/gpu_primitive/age_eviction/chunk_age_eviction.d.ts.map +1 -0
  170. package/src/shade/renderer/gpu_primitive/age_eviction/chunk_age_eviction.js +86 -0
  171. package/src/shade/renderer/grass/blade/GPUGrassBlades.d.ts +135 -0
  172. package/src/shade/renderer/grass/blade/GPUGrassBlades.d.ts.map +1 -0
  173. package/src/shade/renderer/grass/blade/GPUGrassBlades.js +676 -0
  174. package/src/shade/renderer/grass/blade/GRASS_BLADE.d.ts +145 -0
  175. package/src/shade/renderer/grass/blade/GRASS_BLADE.d.ts.map +1 -0
  176. package/src/shade/renderer/grass/blade/GRASS_BLADE.js +179 -0
  177. package/src/shade/renderer/grass/blade/chunk_grass_blade.d.ts +63 -0
  178. package/src/shade/renderer/grass/blade/chunk_grass_blade.d.ts.map +1 -0
  179. package/src/shade/renderer/grass/blade/chunk_grass_blade.js +244 -0
  180. package/src/shade/renderer/grass/blade/chunk_grass_blade_wind.d.ts +58 -0
  181. package/src/shade/renderer/grass/blade/chunk_grass_blade_wind.d.ts.map +1 -0
  182. package/src/shade/renderer/grass/blade/chunk_grass_blade_wind.js +174 -0
  183. package/src/shade/renderer/grass/blade/grass_blade_id_draw.d.ts +25 -0
  184. package/src/shade/renderer/grass/blade/grass_blade_id_draw.d.ts.map +1 -0
  185. package/src/shade/renderer/grass/blade/grass_blade_id_draw.js +133 -0
  186. package/src/shade/renderer/grass/blade/grass_blade_mechanics.d.ts +80 -0
  187. package/src/shade/renderer/grass/blade/grass_blade_mechanics.d.ts.map +1 -0
  188. package/src/shade/renderer/grass/blade/grass_blade_mechanics.js +92 -0
  189. package/src/shade/renderer/grass/blade/grass_blade_resolve.d.ts +29 -0
  190. package/src/shade/renderer/grass/blade/grass_blade_resolve.d.ts.map +1 -0
  191. package/src/shade/renderer/grass/blade/grass_blade_resolve.js +175 -0
  192. package/src/shade/renderer/grass/blade/shader_grass_blade_draw_args.d.ts +14 -0
  193. package/src/shade/renderer/grass/blade/shader_grass_blade_draw_args.d.ts.map +1 -0
  194. package/src/shade/renderer/grass/blade/shader_grass_blade_draw_args.js +45 -0
  195. package/src/shade/renderer/grass/blade/shader_grass_blade_expand.d.ts +61 -0
  196. package/src/shade/renderer/grass/blade/shader_grass_blade_expand.d.ts.map +1 -0
  197. package/src/shade/renderer/grass/blade/shader_grass_blade_expand.js +340 -0
  198. package/src/shade/renderer/grass/blade/shader_grass_cell_count.d.ts +46 -0
  199. package/src/shade/renderer/grass/blade/shader_grass_cell_count.d.ts.map +1 -0
  200. package/src/shade/renderer/grass/blade/shader_grass_cell_count.js +280 -0
  201. package/src/shade/renderer/grass/population/chunk_grass_population.d.ts +60 -0
  202. package/src/shade/renderer/grass/population/chunk_grass_population.d.ts.map +1 -0
  203. package/src/shade/renderer/grass/population/chunk_grass_population.js +171 -0
  204. package/src/shade/renderer/grass/press/GPUGrassPress.d.ts +99 -0
  205. package/src/shade/renderer/grass/press/GPUGrassPress.d.ts.map +1 -0
  206. package/src/shade/renderer/grass/press/GPUGrassPress.js +436 -0
  207. package/src/shade/renderer/grass/press/GRASS_PRESS.d.ts +179 -0
  208. package/src/shade/renderer/grass/press/GRASS_PRESS.d.ts.map +1 -0
  209. package/src/shade/renderer/grass/press/GRASS_PRESS.js +217 -0
  210. package/src/shade/renderer/grass/press/chunk_grass_press.d.ts +65 -0
  211. package/src/shade/renderer/grass/press/chunk_grass_press.d.ts.map +1 -0
  212. package/src/shade/renderer/grass/press/chunk_grass_press.js +296 -0
  213. package/src/shade/renderer/grass/press/shader_grass_press_mark.d.ts +31 -0
  214. package/src/shade/renderer/grass/press/shader_grass_press_mark.d.ts.map +1 -0
  215. package/src/shade/renderer/grass/press/shader_grass_press_mark.js +169 -0
  216. package/src/shade/renderer/grass/press/shader_grass_press_stamp.d.ts +27 -0
  217. package/src/shade/renderer/grass/press/shader_grass_press_stamp.d.ts.map +1 -0
  218. package/src/shade/renderer/grass/press/shader_grass_press_stamp.js +183 -0
  219. package/src/shade/renderer/grass/surface/GPUGrassSurfaces.d.ts +193 -0
  220. package/src/shade/renderer/grass/surface/GPUGrassSurfaces.d.ts.map +1 -0
  221. package/src/shade/renderer/grass/surface/GPUGrassSurfaces.js +1253 -0
  222. package/src/shade/renderer/grass/surface/GRASS_SURFACE.d.ts +211 -0
  223. package/src/shade/renderer/grass/surface/GRASS_SURFACE.d.ts.map +1 -0
  224. package/src/shade/renderer/grass/surface/GRASS_SURFACE.js +241 -0
  225. package/src/shade/renderer/grass/surface/GRASS_SURFACE_VIEW.d.ts +82 -0
  226. package/src/shade/renderer/grass/surface/GRASS_SURFACE_VIEW.d.ts.map +1 -0
  227. package/src/shade/renderer/grass/surface/GRASS_SURFACE_VIEW.js +111 -0
  228. package/src/shade/renderer/grass/surface/chunk_grass_column_cross.d.ts +39 -0
  229. package/src/shade/renderer/grass/surface/chunk_grass_column_cross.d.ts.map +1 -0
  230. package/src/shade/renderer/grass/surface/chunk_grass_column_cross.js +85 -0
  231. package/src/shade/renderer/grass/surface/chunk_grass_directory.d.ts +29 -0
  232. package/src/shade/renderer/grass/surface/chunk_grass_directory.d.ts.map +1 -0
  233. package/src/shade/renderer/grass/surface/chunk_grass_directory.js +112 -0
  234. package/src/shade/renderer/grass/surface/chunk_grass_slot.d.ts +27 -0
  235. package/src/shade/renderer/grass/surface/chunk_grass_slot.d.ts.map +1 -0
  236. package/src/shade/renderer/grass/surface/chunk_grass_slot.js +50 -0
  237. package/src/shade/renderer/grass/surface/chunk_grass_surface_frame.d.ts +21 -0
  238. package/src/shade/renderer/grass/surface/chunk_grass_surface_frame.d.ts.map +1 -0
  239. package/src/shade/renderer/grass/surface/chunk_grass_surface_frame.js +41 -0
  240. package/src/shade/renderer/grass/surface/chunk_grass_surface_window.d.ts +32 -0
  241. package/src/shade/renderer/grass/surface/chunk_grass_surface_window.d.ts.map +1 -0
  242. package/src/shade/renderer/grass/surface/chunk_grass_surface_window.js +136 -0
  243. package/src/shade/renderer/grass/surface/chunk_grass_surfel.d.ts +37 -0
  244. package/src/shade/renderer/grass/surface/chunk_grass_surfel.d.ts.map +1 -0
  245. package/src/shade/renderer/grass/surface/chunk_grass_surfel.js +85 -0
  246. package/src/shade/renderer/grass/surface/chunk_grass_tile.d.ts +25 -0
  247. package/src/shade/renderer/grass/surface/chunk_grass_tile.d.ts.map +1 -0
  248. package/src/shade/renderer/grass/surface/chunk_grass_tile.js +52 -0
  249. package/src/shade/renderer/grass/surface/shader_grass_directory_fill.d.ts +23 -0
  250. package/src/shade/renderer/grass/surface/shader_grass_directory_fill.d.ts.map +1 -0
  251. package/src/shade/renderer/grass/surface/shader_grass_directory_fill.js +87 -0
  252. package/src/shade/renderer/grass/surface/shader_grass_surface_bake.d.ts +43 -0
  253. package/src/shade/renderer/grass/surface/shader_grass_surface_bake.d.ts.map +1 -0
  254. package/src/shade/renderer/grass/surface/shader_grass_surface_bake.js +265 -0
  255. package/src/shade/renderer/grass/surface/shader_grass_surface_bake_command.d.ts +13 -0
  256. package/src/shade/renderer/grass/surface/shader_grass_surface_bake_command.d.ts.map +1 -0
  257. package/src/shade/renderer/grass/surface/shader_grass_surface_bake_command.js +49 -0
  258. package/src/shade/renderer/grass/surface/shader_grass_surface_invalidate.d.ts +62 -0
  259. package/src/shade/renderer/grass/surface/shader_grass_surface_invalidate.d.ts.map +1 -0
  260. package/src/shade/renderer/grass/surface/shader_grass_surface_invalidate.js +162 -0
  261. package/src/shade/renderer/grass/surface/shader_grass_surface_retire.d.ts +40 -0
  262. package/src/shade/renderer/grass/surface/shader_grass_surface_retire.d.ts.map +1 -0
  263. package/src/shade/renderer/grass/surface/shader_grass_surface_retire.js +155 -0
  264. package/src/shade/renderer/grass/surface/shader_grass_surface_schedule.d.ts +19 -0
  265. package/src/shade/renderer/grass/surface/shader_grass_surface_schedule.d.ts.map +1 -0
  266. package/src/shade/renderer/grass/surface/shader_grass_surface_schedule.js +58 -0
  267. package/src/shade/renderer/grass/surface/shader_grass_surface_want.d.ts +30 -0
  268. package/src/shade/renderer/grass/surface/shader_grass_surface_want.d.ts.map +1 -0
  269. package/src/shade/renderer/grass/surface/shader_grass_surface_want.js +251 -0
  270. package/src/shade/renderer/grass/surface/shader_grass_surface_window_count.d.ts +21 -0
  271. package/src/shade/renderer/grass/surface/shader_grass_surface_window_count.d.ts.map +1 -0
  272. package/src/shade/renderer/grass/surface/shader_grass_surface_window_count.js +75 -0
  273. package/src/shade/renderer/postprocess/velocity/graph_add_per_object_velocity.d.ts.map +1 -1
  274. package/src/shade/renderer/postprocess/velocity/graph_add_per_object_velocity.js +4 -23
  275. package/src/shade/renderer/postprocess/velocity/view_velocity_matrices.d.ts +12 -0
  276. package/src/shade/renderer/postprocess/velocity/view_velocity_matrices.d.ts.map +1 -0
  277. package/src/shade/renderer/postprocess/velocity/view_velocity_matrices.js +29 -0
  278. package/src/shade/renderer/rasterize/native/avboit/graph_rasterize_meshes_transparent_avboit.d.ts.map +1 -1
  279. package/src/shade/renderer/rasterize/native/avboit/graph_rasterize_meshes_transparent_avboit.js +22 -0
  280. package/src/shade/renderer/scene/GPUSceneContext.d.ts.map +1 -1
  281. package/src/shade/renderer/scene/GPUSceneContext.js +3 -0
  282. package/src/shade/renderer/scene/GRASS_SURFACE_PARAMS_STRUCT.d.ts +27 -0
  283. package/src/shade/renderer/scene/GRASS_SURFACE_PARAMS_STRUCT.d.ts.map +1 -0
  284. package/src/shade/renderer/scene/GRASS_SURFACE_PARAMS_STRUCT.js +38 -0
  285. package/src/shade/renderer/scene/MESH_GROUND_COVER_STRUCT.d.ts +28 -0
  286. package/src/shade/renderer/scene/MESH_GROUND_COVER_STRUCT.d.ts.map +1 -0
  287. package/src/shade/renderer/scene/MESH_GROUND_COVER_STRUCT.js +58 -0
  288. package/src/shade/renderer/scene/SCENE_DATABASE_SPEC.d.ts.map +1 -1
  289. package/src/shade/renderer/scene/SCENE_DATABASE_SPEC.js +8 -0
  290. package/src/shade/renderer/shader/chunk/geometry/ray/chunk_ray_query_nearest_among.d.ts +37 -0
  291. package/src/shade/renderer/shader/chunk/geometry/ray/chunk_ray_query_nearest_among.d.ts.map +1 -0
  292. package/src/shade/renderer/shader/chunk/geometry/ray/chunk_ray_query_nearest_among.js +148 -0
  293. package/src/shade/renderer/shader/chunk/sdf/chunk_shape_record_line_span.d.ts +20 -0
  294. package/src/shade/renderer/shader/chunk/sdf/chunk_shape_record_line_span.d.ts.map +1 -0
  295. package/src/shade/renderer/shader/chunk/sdf/chunk_shape_record_line_span.js +169 -0
  296. package/src/shade/renderer/texture/atlas/chunk_tile_atlas_slot_origin.d.ts +18 -0
  297. package/src/shade/renderer/texture/atlas/chunk_tile_atlas_slot_origin.d.ts.map +1 -1
  298. package/src/shade/renderer/texture/atlas/chunk_tile_atlas_slot_origin.js +28 -0
  299. package/src/shade/renderer/texture/atlas/tile_atlas_slot_shift.d.ts +13 -0
  300. package/src/shade/renderer/texture/atlas/tile_atlas_slot_shift.d.ts.map +1 -0
  301. package/src/shade/renderer/texture/atlas/tile_atlas_slot_shift.js +22 -0
  302. package/src/shade/renderer/water/GPUWaterRenderer.d.ts.map +1 -1
  303. package/src/shade/renderer/water/GPUWaterRenderer.js +4 -0
  304. package/src/shade/renderer/wind/GPUSceneWind.d.ts +52 -5
  305. package/src/shade/renderer/wind/GPUSceneWind.d.ts.map +1 -1
  306. package/src/shade/renderer/wind/GPUSceneWind.js +164 -8
  307. package/src/shade/renderer/wind/WIND_FIELD_UNIFORM.d.ts +3 -1
  308. package/src/shade/renderer/wind/WIND_FIELD_UNIFORM.d.ts.map +1 -1
  309. package/src/shade/renderer/wind/WIND_FIELD_UNIFORM.js +3 -1
  310. package/src/shade/renderer/wind/chunk_wind_field_sample_cascades.d.ts +2 -1
  311. package/src/shade/renderer/wind/chunk_wind_field_sample_cascades.d.ts.map +1 -1
  312. package/src/shade/renderer/wind/chunk_wind_field_sample_cascades.js +18 -13
  313. package/src/shade/renderer/wind/chunk_wind_field_sample_previous.d.ts +13 -0
  314. package/src/shade/renderer/wind/chunk_wind_field_sample_previous.d.ts.map +1 -0
  315. package/src/shade/renderer/wind/chunk_wind_field_sample_previous.js +23 -0
  316. package/src/shade/renderer/wind/chunk_wind_field_sample_uniform.d.ts +1 -0
  317. package/src/shade/renderer/wind/chunk_wind_field_sample_uniform.d.ts.map +1 -1
  318. package/src/shade/renderer/wind/chunk_wind_field_sample_uniform.js +6 -1
  319. package/src/shade/renderer/wind/wind_field_add_resources.d.ts +11 -0
  320. package/src/shade/renderer/wind/wind_field_add_resources.d.ts.map +1 -1
  321. package/src/shade/renderer/wind/wind_field_add_resources.js +15 -0
  322. package/src/shade/wgsl/emulator/WGSLJavaScriptCompiler.js +1 -1
  323. package/src/shade/wgsl/emulator/WGSLLanguageCore.d.ts.map +1 -1
  324. package/src/shade/wgsl/emulator/WGSLLanguageCore.js +2 -0
@@ -29,8 +29,11 @@ import { NULL_POINTER } from "../BinaryTopology.js";
29
29
  * by UNFOLDING. Each node carries the 2D positions of its entry edge in a frame accumulated along its
30
30
  * own corridor; expanding a node flattens the next triangle into that frame by placing the apex from the
31
31
  * two 3D edge lengths |edge-vertex .. apex| (an isometry of the triangle), on the side fixed by the
32
- * face's winding. So distances in the frame are true geodesic distances and the cost `g` is exact on a
33
- * curved/folded surface, not just a planar one. Turning corners are detected from the 3D incident-angle
32
+ * face's winding. So distances in the frame are true distances along the corridor, and the result is
33
+ * exact wherever the surface unfolds flat -- a floor, a ramp, a fold along mesh edges -- not just in one
34
+ * plane. Where it does not (a hill), the result is a path on the surface but not always the shortest,
35
+ * and ground behind a saddle vertex can go unreached: paths turn at boundary corners only. Every face
36
+ * must be wound the same way round the surface. Turning corners are detected from the 3D incident-angle
34
37
  * sum (also intrinsic). The goal has no fixed position across frames, so the A* heuristic is the exact
35
38
  * shortest root->interval->goal measured with straight 3D chords (a lower bound on the geodesic, hence
36
39
  * admissible); the terminal node's *exact* cost unfolds the goal into the node's frame instead.
@@ -45,7 +48,6 @@ import { NULL_POINTER } from "../BinaryTopology.js";
45
48
  */
46
49
 
47
50
  const EPS = 1e-9; // area-sign slack (cone inside/outside tests)
48
- const COLLINEAR_EPS = 1e-4; // drop a path point whose perpendicular offset from its neighbours / span is below this
49
51
 
50
52
  /**
51
53
  * Relative spacing between adjacent float32 values: an axis holding `a` rounds by up to `|a| * FLOAT32_STEP`.
@@ -79,13 +81,33 @@ function dedup_tolerance_sqr(x, y, z) {
79
81
  return (x * x + y * y + z * z) * FLOAT32_STEP_SQR;
80
82
  }
81
83
 
82
- // Parameter-space tolerance for edge fractions, interval coverage and vertex snapping. Vertices are
83
- // stored as float32, so a point that is geometrically on an edge endpoint or already-covered carries
84
- // ~coord*2^-23 quantization noise in its edge fraction -- far above 1e-9. Tolerating less re-covers the
85
- // same edge stretches as slivers and the wavefront floods without converging. 1e-4 covers coordinates
86
- // into the thousands while staying far below any real feature size, and never moves a waypoint (corners
87
- // are exact vertex positions).
88
- const PEPS = 1e-4;
84
+ /**
85
+ * Relative spacing between adjacent float64 values -- what the unfolded frame is kept in.
86
+ * @readonly
87
+ * @type {number}
88
+ */
89
+ const FLOAT64_STEP = 2 ** -52;
90
+
91
+ /**
92
+ * Parameter-space tolerance: two fractions of an edge closer than this are one fraction. It decides
93
+ * whether a cone's ray passes through the end of a far edge or splits the edge, which stretches are too
94
+ * narrow to become a node, and when two covered stretches touch.
95
+ *
96
+ * It is held between two things that both scale with a face's aspect A (its length over its altitude),
97
+ * in opposite directions. Rounding in the frame reaches an edge fraction multiplied by about A, so the
98
+ * tolerance must stay above FLOAT64_STEP * A, or noise splits an edge a hair from its end and the
99
+ * stretch that should end on a corner no longer does. And a turn of angle theta round a corner appears
100
+ * on the first edge past it as a stretch of only tan(theta) / A, so the tolerance must stay below that,
101
+ * or the turn is dropped -- and with it everything that is reached only round that corner. The widest
102
+ * range of A that satisfies both belongs to the tolerance midway between them, the square root of the
103
+ * step: it serves faces up to 2^26 times longer than they are thin, where float32 vertices cannot
104
+ * describe one beyond 2^24. What it still drops is a turn shallower than PEPS * A.
105
+ *
106
+ * It never moves a waypoint (corners are exact vertex positions).
107
+ * @readonly
108
+ * @type {number}
109
+ */
110
+ const PEPS = Math.sqrt(FLOAT64_STEP);
89
111
 
90
112
  // ---- 2D primitives in the unfolded frame -----------------------------------------------------------
91
113
 
@@ -171,7 +193,13 @@ function unfold_apex(topology, face, e0v, e1v, e0x, e0y, e1x, e1y) {
171
193
  return apex;
172
194
  }
173
195
 
174
- /** Barycentric weights (for va,vb,vc, in order) of a 3D point projected onto their plane, into `out`. */
196
+ /**
197
+ * Barycentric weights (for va,vb,vc, in order) of a 3D point projected onto their plane, into `out`.
198
+ *
199
+ * Taken as ratios of cross products against the face normal, whose error grows with the face's aspect.
200
+ * (Solving the two dot-product equations for them squares it: a face 9 long and 10 microns thin placed
201
+ * its point a millimetre out.)
202
+ */
175
203
  function barycentric_3d(topology, va, vb, vc, px, py, pz, out) {
176
204
  topology.vertex_read_coordinate(_va, 0, va);
177
205
  topology.vertex_read_coordinate(_vb, 0, vb);
@@ -179,11 +207,12 @@ function barycentric_3d(topology, va, vb, vc, px, py, pz, out) {
179
207
  const e0x = _vb[0] - _va[0], e0y = _vb[1] - _va[1], e0z = _vb[2] - _va[2];
180
208
  const e1x = _vc[0] - _va[0], e1y = _vc[1] - _va[1], e1z = _vc[2] - _va[2];
181
209
  const v2x = px - _va[0], v2y = py - _va[1], v2z = pz - _va[2];
182
- const d00 = e0x * e0x + e0y * e0y + e0z * e0z, d01 = e0x * e1x + e0y * e1y + e0z * e1z, d11 = e1x * e1x + e1y * e1y + e1z * e1z;
183
- const d20 = v2x * e0x + v2y * e0y + v2z * e0z, d21 = v2x * e1x + v2y * e1y + v2z * e1z;
184
- const denom = d00 * d11 - d01 * d01;
185
- if (Math.abs(denom) < 1e-18) { out[0] = 1; out[1] = 0; out[2] = 0; return; }
186
- const v = (d11 * d20 - d01 * d21) / denom, w = (d00 * d21 - d01 * d20) / denom;
210
+ // n = e0 x e1; then (p - a) x e1 = v * n and e0 x (p - a) = w * n
211
+ const nx = e0y * e1z - e0z * e1y, ny = e0z * e1x - e0x * e1z, nz = e0x * e1y - e0y * e1x;
212
+ const nn = nx * nx + ny * ny + nz * nz;
213
+ if (nn === 0) { out[0] = 1; out[1] = 0; out[2] = 0; return; } // no area at all: any corner will do
214
+ const v = ((v2y * e1z - v2z * e1y) * nx + (v2z * e1x - v2x * e1z) * ny + (v2x * e1y - v2y * e1x) * nz) / nn;
215
+ const w = ((e0y * v2z - e0z * v2y) * nx + (e0z * v2x - e0x * v2z) * ny + (e0x * v2y - e0y * v2x) * nz) / nn;
187
216
  out[0] = 1 - v - w; out[1] = v; out[2] = w;
188
217
  }
189
218
 
@@ -248,8 +277,8 @@ function is_corner(topology, vid) {
248
277
 
249
278
  // ---- node storage ----------------------------------------------------------------------------------
250
279
 
251
- // Search nodes are fixed 16-word records packed into one flat ArrayBuffer, addressed by id*NODE_WORDS,
252
- // so the open list is a Uint32Heap4 (id + score) rather than an object heap -- NO per-node object
280
+ // Search nodes are fixed 104-byte records packed into one flat ArrayBuffer, addressed by id, so the
281
+ // open list is a Uint32Heap4 (id + score) rather than an object heap -- NO per-node object
253
282
  // allocation, so the hot path produces no garbage and never triggers a GC pause (which would otherwise
254
283
  // jitter the rest of the engine). Allocation is a monotonic bump counter reset per query; nodes are
255
284
  // never released mid-query, so there is no free list and no occupancy bookkeeping. The buffer only ever
@@ -257,25 +286,35 @@ function is_corner(topology, vid) {
257
286
  // after a grow. (A pooled allocator with a free list + occupancy bitset degenerates to exactly this
258
287
  // under a never-free, bump-allocated access pattern, so a raw buffer is the honest, lighter shape.)
259
288
  //
289
+ // A record is 13 float64 words. The first three are read as six uint32 fields (N_*, at
290
+ // id*NODE_U32 + field); the other ten are float64 fields (F_*, at id*NODE_F64 + field).
291
+ //
260
292
  // A node stores its entry edge as vertex ids (E0V,E1V) AND its 2D positions in the node's unfolded frame
261
293
  // (E0X,E0Y,E1X,E1Y), the root (ROOTVID + RX,RY in the frame; ROOTVID==NULL_ID is the start point), and
262
294
  // the interval as the fractions [T0,T1] along the edge. 3D is recovered at reconstruction from vertex
263
- // ids; a terminal node stores the goal edge (E0V,E1V) and the final bend's fraction along it (N_FCT,
295
+ // ids; a terminal node stores the goal edge (E0V,E1V) and the final bend's fraction along it (F_FCT,
264
296
  // -1 == a straight shot, since a real bend fraction is in [0,1]).
297
+ //
298
+ // The frame is float64 because unfolding multiplies whatever it is off by: an apex of altitude h over
299
+ // an entry edge of length L moves by L/h times the error in that edge's frame position, once for every
300
+ // face the corridor crosses. A frame kept at the vertices' own float32 resolution therefore places the
301
+ // apex of a face thinner than sqrt(L * that resolution) on the wrong side of its entry edge.
265
302
  const N_FACE = 0, N_E0V = 1, N_E1V = 2;
266
- const N_E0X = 3, N_E0Y = 4, N_E1X = 5, N_E1Y = 6;
267
- const N_ROOTVID = 7, N_RX = 8, N_RY = 9;
268
- const N_T0 = 10, N_T1 = 11;
269
- const N_G = 12, N_PARENT = 13, N_TERMINAL = 14, N_FCT = 15;
270
- const NODE_WORDS = 16;
303
+ const N_ROOTVID = 3, N_PARENT = 4, N_TERMINAL = 5;
304
+ const F_E0X = 3, F_E0Y = 4, F_E1X = 5, F_E1Y = 6;
305
+ const F_RX = 7, F_RY = 8;
306
+ const F_T0 = 9, F_T1 = 10;
307
+ const F_G = 11, F_FCT = 12;
308
+ const NODE_F64 = 13;
309
+ const NODE_U32 = NODE_F64 * 2;
271
310
  const NULL_ID = 0xFFFFFFFF;
272
311
 
273
312
  const _open = new Uint32Heap4();
274
313
 
275
- // node records: uint32 and float32 views overlaid on one ArrayBuffer (_node_df aliases _node_du's bytes)
314
+ // node records: float64 and uint32 views overlaid on one ArrayBuffer (_node_du aliases _node_df's bytes)
276
315
  let _node_capacity = 1024; // capacity in nodes; grows by doubling
277
- let _node_du = new Uint32Array(_node_capacity * NODE_WORDS);
278
- let _node_df = new Float32Array(_node_du.buffer);
316
+ let _node_df = new Float64Array(_node_capacity * NODE_F64);
317
+ let _node_du = new Uint32Array(_node_df.buffer);
279
318
  let _node_count = 0;
280
319
 
281
320
  /** Reserve the next node slot, growing (and re-caching the views) only when the buffer is full. */
@@ -283,14 +322,15 @@ function alloc_node() {
283
322
  const id = _node_count++;
284
323
  if (id >= _node_capacity) {
285
324
  _node_capacity *= 2;
286
- const grown = new Uint32Array(_node_capacity * NODE_WORDS);
325
+ const grown = new Uint32Array(_node_capacity * NODE_U32);
287
326
  grown.set(_node_du); // copy the existing records into the larger buffer
288
327
  _node_du = grown;
289
- _node_df = new Float32Array(grown.buffer);
328
+ _node_df = new Float64Array(grown.buffer);
290
329
  }
291
330
  return id;
292
331
  }
293
332
 
333
+
294
334
  // the start/goal 3D points (for the heuristic and for lifting the path back to 3D)
295
335
  let _sx = 0, _sy = 0, _sz = 0;
296
336
  let _gx = 0, _gy = 0, _gz = 0;
@@ -368,7 +408,7 @@ export function bt_mesh_face_find_path_polyanya(output, topology, sx, sy, sz, st
368
408
  let guard = 0;
369
409
  while (!_open.is_empty()) {
370
410
  const id = _open.pop_min();
371
- if (_node_du[id * NODE_WORDS + N_TERMINAL] === 1) { best = id; break; }
411
+ if (_node_du[id * NODE_U32 + N_TERMINAL] === 1) { best = id; break; }
372
412
  if (++guard > 5_000_000) break;
373
413
  expand(topology, id, goal_face);
374
414
  }
@@ -382,26 +422,26 @@ export function bt_mesh_face_find_path_polyanya(output, topology, sx, sy, sz, st
382
422
  // its 3D edge keeps every output segment inside one face (i.e. on the surface). Crossings that fall on
383
423
  // a straight (flat) stretch collapse away in push_point, so flat meshes still yield minimal paths.
384
424
  const du = _node_du, df = _node_df;
385
- const bb = best * NODE_WORDS;
425
+ const bu = best * NODE_U32, bf = best * NODE_F64;
386
426
 
387
427
  // the goal's position in the shared frame: unfold the goal-face apex from the terminal's entry edge
388
- const t_e0v = du[bb + N_E0V], t_e1v = du[bb + N_E1V];
389
- const t_e0x = df[bb + N_E0X], t_e0y = df[bb + N_E0Y], t_e1x = df[bb + N_E1X], t_e1y = df[bb + N_E1Y];
428
+ const t_e0v = du[bu + N_E0V], t_e1v = du[bu + N_E1V];
429
+ const t_e0x = df[bf + F_E0X], t_e0y = df[bf + F_E0Y], t_e1x = df[bf + F_E1X], t_e1y = df[bf + F_E1Y];
390
430
  const g_apex = unfold_apex(topology, goal_face, t_e0v, t_e1v, t_e0x, t_e0y, t_e1x, t_e1y);
391
431
  const goal_u = goal_weight(t_e0v) * t_e0x + goal_weight(t_e1v) * t_e1x + goal_weight(g_apex) * _apex[0];
392
432
  const goal_v = goal_weight(t_e0v) * t_e0y + goal_weight(t_e1v) * t_e1y + goal_weight(g_apex) * _apex[1];
393
- const t_fct = df[bb + N_FCT];
433
+ const t_fct = df[bf + F_FCT];
394
434
 
395
435
  // walk the chain (goal end -> start), recording each entry-edge crossing in reverse path order
396
436
  _recon.length = 0;
397
437
  let next_u = goal_u, next_v = goal_v; // the next waypoint toward the goal, in the shared frame
398
- let prev_root = du[bb + N_ROOTVID];
399
- let prev_rx = df[bb + N_RX], prev_ry = df[bb + N_RY];
438
+ let prev_root = du[bu + N_ROOTVID];
439
+ let prev_rx = df[bf + F_RX], prev_ry = df[bf + F_RY];
400
440
 
401
- for (let n = best; n !== NULL_ID; n = du[n * NODE_WORDS + N_PARENT]) {
402
- const base = n * NODE_WORDS;
403
- const rootVid = du[base + N_ROOTVID];
404
- const rx = df[base + N_RX], ry = df[base + N_RY];
441
+ for (let n = best; n !== NULL_ID; n = du[n * NODE_U32 + N_PARENT]) {
442
+ const base_u = n * NODE_U32, base_f = n * NODE_F64;
443
+ const rootVid = du[base_u + N_ROOTVID];
444
+ const rx = df[base_f + F_RX], ry = df[base_f + F_RY];
405
445
 
406
446
  // a change of root means the route turned at the previous corridor's root: emit that corner
407
447
  // (an exact mesh vertex -- the turning point) and aim subsequent crossings at it
@@ -414,23 +454,37 @@ export function bt_mesh_face_find_path_polyanya(output, topology, sx, sy, sz, st
414
454
  prev_root = rootVid;
415
455
  }
416
456
 
417
- const e0v = du[base + N_E0V], e1v = du[base + N_E1V];
418
- const e0x = df[base + N_E0X], e0y = df[base + N_E0Y], e1x = df[base + N_E1X], e1y = df[base + N_E1Y];
457
+ const e0v = du[base_u + N_E0V], e1v = du[base_u + N_E1V];
458
+ const e0x = df[base_f + F_E0X], e0y = df[base_f + F_E0Y], e1x = df[base_f + F_E1X], e1y = df[base_f + F_E1Y];
419
459
 
420
460
  // fraction along the entry edge where the route root->next crosses it (the terminal's goal-edge
421
- // bend is exact and pre-computed as N_FCT)
461
+ // bend is exact and pre-computed as F_FCT)
422
462
  let u;
423
463
  if (n === best && t_fct !== -1) {
424
464
  u = t_fct;
465
+
466
+ // the route turns here, on the goal edge: what comes before heads for this bend, not the goal
467
+ next_u = e0x + (e1x - e0x) * u;
468
+ next_v = e0y + (e1y - e0y) * u;
425
469
  } else {
426
470
  u = line2_segment_intersection_fraction_2d(rx, ry, next_u, next_v, e0x, e0y, e1x, e1y);
427
471
  if (u === -1) {
428
- // the route line grazes a shared vertex (passes through / just outside an edge endpoint,
429
- // common when the geodesic runs along a row of grid vertices): snap to the nearer endpoint
430
472
  const dx = next_u - rx, dy = next_v - ry;
431
- const s0 = Math.abs(dx * (e0y - ry) - dy * (e0x - rx));
432
- const s1 = Math.abs(dx * (e1y - ry) - dy * (e1x - rx));
433
- u = s0 <= s1 ? 0 : 1;
473
+ if (dx === 0 && dy === 0) {
474
+ // the root already stands where the route is headed (a start or goal placed on a
475
+ // mesh vertex), so there is no line to cross the edge with: the root is at one of
476
+ // the edge's ends, and that end is the crossing
477
+ const d0 = (e0x - rx) * (e0x - rx) + (e0y - ry) * (e0y - ry);
478
+ const d1 = (e1x - rx) * (e1x - rx) + (e1y - ry) * (e1y - ry);
479
+ u = d0 <= d1 ? 0 : 1;
480
+ } else {
481
+ // the route line grazes a shared vertex (passes through / just outside an edge
482
+ // endpoint, common when the geodesic runs along a row of grid vertices): snap to the
483
+ // nearer endpoint
484
+ const s0 = Math.abs(dx * (e0y - ry) - dy * (e0x - rx));
485
+ const s1 = Math.abs(dx * (e1y - ry) - dy * (e1x - rx));
486
+ u = s0 <= s1 ? 0 : 1;
487
+ }
434
488
  }
435
489
  }
436
490
 
@@ -465,8 +519,11 @@ export function bt_mesh_face_find_path_polyanya(output, topology, sx, sy, sz, st
465
519
  * Append a 3D point, dropping it when it says nothing new:
466
520
  *
467
521
  * - it repeats the previous point, to within that position's storage resolution ({@link dedup_tolerance_sqr});
468
- * - or the previous point lies (within {@link COLLINEAR_EPS}) on the segment between the one before it
469
- * and the new point, so it is a redundant middle of a straight run.
522
+ * - or the previous point is the redundant middle of a straight run: it lies on the line through the one
523
+ * before it and the new point, as nearly as float32 storage can tell. Each of the three is only known
524
+ * to its own storage step, so the middle one is on the line when it is within the largest of those
525
+ * steps of it. Nothing coarser will do: a bend round a corner that a thin face makes a few microns
526
+ * deep is still a bend, and a path that skips it passes through the obstacle.
470
527
  *
471
528
  * Either way the previous slot is overwritten rather than kept, so of two renditions of one place it is
472
529
  * the newer that survives -- which keeps the path ending on the caller's goal verbatim rather than on the
@@ -485,8 +542,11 @@ function push_point(output, count, x, y, z) {
485
542
  const abx = bx - ax, aby = by - ay, abz = bz - az;
486
543
  const acx = x - ax, acy = y - ay, acz = z - az;
487
544
  const cross_x = aby * acz - abz * acy, cross_y = abz * acx - abx * acz, cross_z = abx * acy - aby * acx;
488
- const ac_len = Math.hypot(acx, acy, acz);
489
- if (same_place || Math.hypot(cross_x, cross_y, cross_z) <= COLLINEAR_EPS * ac_len) {
545
+ // |ab x ac|^2 = |ac|^2 * (distance of b from the line through a and c)^2
546
+ const cross_sqr = cross_x * cross_x + cross_y * cross_y + cross_z * cross_z;
547
+ const ac_sqr = acx * acx + acy * acy + acz * acz;
548
+ const step_sqr = Math.max(dedup_tolerance_sqr(ax, ay, az), dedup_tolerance_sqr(bx, by, bz), dedup_tolerance_sqr(x, y, z));
549
+ if (same_place || cross_sqr <= ac_sqr * step_sqr) {
490
550
  output[count - 3] = x; output[count - 2] = y; output[count - 1] = z;
491
551
  return count;
492
552
  }
@@ -505,24 +565,24 @@ function start_edge(topology, su, sv, start_face, ea, eb, eax, eay, ebx, eby, go
505
565
 
506
566
  function expand(topology, id, goal_face) {
507
567
  const du = _node_du, df = _node_df;
508
- const base = id * NODE_WORDS;
509
-
510
- const face = du[base + N_FACE];
511
- const e0v = du[base + N_E0V], e1v = du[base + N_E1V];
512
- const e0x = df[base + N_E0X], e0y = df[base + N_E0Y];
513
- const e1x = df[base + N_E1X], e1y = df[base + N_E1Y];
514
- const rv = du[base + N_ROOTVID]; const rootVid = rv === NULL_ID ? -1 : rv;
515
- const rx = df[base + N_RX], ry = df[base + N_RY];
516
- const t0 = df[base + N_T0], t1 = df[base + N_T1];
517
- const g = df[base + N_G];
518
-
519
- // interval endpoints + their edge vertex ids, ordered so i1 is left of the ray root->i0
520
- let i0x = e0x + t0 * (e1x - e0x), i0y = e0y + t0 * (e1y - e0y), i0v = t0 <= PEPS ? e0v : (t0 >= 1 - PEPS ? e1v : -1);
521
- let i1x = e0x + t1 * (e1x - e0x), i1y = e0y + t1 * (e1y - e0y), i1v = t1 <= PEPS ? e0v : (t1 >= 1 - PEPS ? e1v : -1);
522
- if (tri2_signed_area(rx, ry, i0x, i0y, i1x, i1y) < 0) {
523
- const sxx = i0x, syy = i0y, svv = i0v;
524
- i0x = i1x; i0y = i1y; i0v = i1v; i1x = sxx; i1y = syy; i1v = svv;
525
- }
568
+ const base_u = id * NODE_U32, base_f = id * NODE_F64;
569
+
570
+ const face = du[base_u + N_FACE];
571
+ const e0v = du[base_u + N_E0V], e1v = du[base_u + N_E1V];
572
+ const e0x = df[base_f + F_E0X], e0y = df[base_f + F_E0Y];
573
+ const e1x = df[base_f + F_E1X], e1y = df[base_f + F_E1Y];
574
+ const rv = du[base_u + N_ROOTVID]; const rootVid = rv === NULL_ID ? -1 : rv;
575
+ const rx = df[base_f + F_RX], ry = df[base_f + F_RY];
576
+ const t0 = df[base_f + F_T0], t1 = df[base_f + F_T1];
577
+ const g = df[base_f + F_G];
578
+
579
+ // Interval endpoints + their edge vertex ids. i0, the end nearer e0, is the cone's right ray and i1
580
+ // its left: the entry edge runs e0 -> e1 with the entered face on its right and the root on its left
581
+ // or on its line, which is how every node is built (see add_successor). The order is never read back
582
+ // off the root's position -- a root that has just turned at e0 or e1 lies ON the edge, where the cone
583
+ // has no area and so no sign to read.
584
+ const i0x = e0x + t0 * (e1x - e0x), i0y = e0y + t0 * (e1y - e0y), i0v = t0 <= PEPS ? e0v : (t0 >= 1 - PEPS ? e1v : -1);
585
+ const i1x = e0x + t1 * (e1x - e0x), i1y = e0y + t1 * (e1y - e0y), i1v = t1 <= PEPS ? e0v : (t1 >= 1 - PEPS ? e1v : -1);
526
586
 
527
587
  // unfold the apex into this node's frame, giving the two far edges (e0v,apex) and (apex,e1v)
528
588
  const apex = unfold_apex(topology, face, e0v, e1v, e0x, e0y, e1x, e1y);
@@ -570,15 +630,29 @@ function process_far_edge(topology, from_face, Pv, Px, Py, Qv, Qx, Qy, rx, ry, r
570
630
  }
571
631
 
572
632
  /**
573
- * Per (entered edge, root) interval coverage. A fixed root implies a fixed g, so the first node to
574
- * cover a stretch of an edge is optimal there -- later nodes are clipped to the still-uncovered part
575
- * (or dropped). This is what makes the wavefront terminate instead of re-flooding the same edges.
633
+ * Per (entered edge, root) interval coverage: which arrival holds each stretch of the edge.
634
+ *
635
+ * Nodes that share a root reach a stretch of an edge along the same lines from that root, so they
636
+ * differ only in what reaching the root cost them. The cheapest to arrive holds the stretch, and one
637
+ * that cost the same finds it taken. A node is clipped to what it does not find held at its own cost or
638
+ * less -- which is what makes the wavefront terminate instead of re-flooding the same edges -- and it
639
+ * takes over what it finds held at more.
640
+ *
641
+ * A root does not fix its cost: A* pops by f, so a corner can be reached by a dearer route before the
642
+ * cheapest one, and what the dearer one covered must not shut the cheaper one out. Nor is the dearer
643
+ * one discarded, only clipped: on a surface that does not unfold flat, two routes to one corner can
644
+ * open different ground beyond it, and the dearer may be the only way into some of it.
645
+ *
646
+ * A list holds the covered stretches of one edge for one root as [lo, hi, g] triples, ascending and
647
+ * disjoint. {@link reserve} writes the stretches of [lo, hi] the caller takes into {@link _uncovered}
648
+ * as [lo, hi] pairs and returns how many numbers it wrote.
576
649
  */
577
650
  const _covered = new Map();
578
651
  const ROOT_STRIDE = 0x1000000;
579
- const _uncovered = new Float64Array(256);
652
+ const _uncovered = []; // reused across calls, like _rebuilt: a list has no fixed number of gaps
653
+ const _rebuilt = []; // the list being rewritten
580
654
 
581
- function reserve(from_face, neighbour, root_vid, lo, hi) {
655
+ function reserve(from_face, neighbour, root_vid, g, lo, hi) {
582
656
  let by_root = _covered.get(from_face);
583
657
  if (by_root === undefined) { by_root = new Map(); _covered.set(from_face, by_root); }
584
658
 
@@ -586,41 +660,88 @@ function reserve(from_face, neighbour, root_vid, lo, hi) {
586
660
  let ranges = by_root.get(inner_key);
587
661
  if (ranges === undefined) { ranges = []; by_root.set(inner_key, ranges); }
588
662
 
663
+ const n = ranges.length;
589
664
  let out = 0;
590
- let cur = lo;
591
- for (let i = 0; i < ranges.length; i += 2) {
592
- const r_lo = ranges[i], r_hi = ranges[i + 1];
593
- if (r_hi <= cur) continue;
594
- if (r_lo >= hi) break;
595
- if (r_lo > cur) { _uncovered[out++] = cur; _uncovered[out++] = r_lo < hi ? r_lo : hi; }
596
- if (r_hi > cur) cur = r_hi;
597
- if (cur >= hi) break;
665
+ let k = 0;
666
+ let i = 0;
667
+
668
+ // stretches wholly below lo stay as they are
669
+ for (; i < n && ranges[i + 1] <= lo; i += 3) {
670
+ _rebuilt[k++] = ranges[i]; _rebuilt[k++] = ranges[i + 1]; _rebuilt[k++] = ranges[i + 2];
598
671
  }
599
- if (cur < hi) { _uncovered[out++] = cur; _uncovered[out++] = hi; }
600
672
 
601
- let i = 0;
602
- while (i < ranges.length && ranges[i + 1] < lo - PEPS) i += 2;
603
- let merge_lo = lo, merge_hi = hi, j = i;
604
- while (j < ranges.length && ranges[j] <= hi + PEPS) {
605
- if (ranges[j] < merge_lo) merge_lo = ranges[j];
606
- if (ranges[j + 1] > merge_hi) merge_hi = ranges[j + 1];
607
- j += 2;
673
+ let pos = lo; // [lo, pos) is decided
674
+ let mine = -1; // where the stretch being taken began; -1 while none is open
675
+ let tail_hi = -1; // a dearer stretch reached past hi: what of it remains, [hi, tail_hi] at tail_g
676
+ let tail_g = 0;
677
+
678
+ for (; i < n && ranges[i] < hi; i += 3) {
679
+ const r_lo = ranges[i], r_hi = ranges[i + 1], r_g = ranges[i + 2];
680
+
681
+ if (r_g <= g) {
682
+ // held at this cost or less: it stays, and ends the stretch being taken before it
683
+ if (mine === -1 && r_lo > pos) mine = pos;
684
+ if (mine !== -1) {
685
+ if (r_lo > mine) {
686
+ _uncovered[out++] = mine; _uncovered[out++] = r_lo;
687
+ _rebuilt[k++] = mine; _rebuilt[k++] = r_lo; _rebuilt[k++] = g;
688
+ }
689
+ mine = -1;
690
+ }
691
+ _rebuilt[k++] = r_lo; _rebuilt[k++] = r_hi; _rebuilt[k++] = r_g;
692
+ if (r_hi > pos) pos = r_hi;
693
+ } else {
694
+ // held at more: taken over, but for what of it lies outside [lo, hi]
695
+ if (r_lo < lo) { _rebuilt[k++] = r_lo; _rebuilt[k++] = lo; _rebuilt[k++] = r_g; }
696
+ if (mine === -1) mine = pos;
697
+ if (r_hi > hi) { tail_hi = r_hi; tail_g = r_g; pos = hi; } else { pos = r_hi; }
698
+ }
608
699
  }
609
- ranges.splice(i, j - i, merge_lo, merge_hi);
700
+
701
+ if (pos < hi) {
702
+ if (mine === -1) mine = pos;
703
+ pos = hi;
704
+ }
705
+ if (mine !== -1 && hi > mine) {
706
+ _uncovered[out++] = mine; _uncovered[out++] = hi;
707
+ _rebuilt[k++] = mine; _rebuilt[k++] = hi; _rebuilt[k++] = g;
708
+ }
709
+ if (tail_hi !== -1) { _rebuilt[k++] = hi; _rebuilt[k++] = tail_hi; _rebuilt[k++] = tail_g; }
710
+
711
+ for (; i < n; i += 3) {
712
+ _rebuilt[k++] = ranges[i]; _rebuilt[k++] = ranges[i + 1]; _rebuilt[k++] = ranges[i + 2];
713
+ }
714
+
715
+ // write the list back, joining neighbours held at one cost that touch to within PEPS
716
+ let w = 0;
717
+ for (let r = 0; r < k; r += 3) {
718
+ if (w > 0 && ranges[w - 1] === _rebuilt[r + 2] && _rebuilt[r] - ranges[w - 2] <= PEPS) {
719
+ if (_rebuilt[r + 1] > ranges[w - 2]) ranges[w - 2] = _rebuilt[r + 1];
720
+ } else {
721
+ ranges[w++] = _rebuilt[r]; ranges[w++] = _rebuilt[r + 1]; ranges[w++] = _rebuilt[r + 2];
722
+ }
723
+ }
724
+ ranges.length = w;
610
725
 
611
726
  return out;
612
727
  }
613
728
 
614
729
  /**
615
730
  * Clip the interval (edge fractions [lo,hi] along Pv->Qv) against what has already been reached on this
616
- * edge from `rootVid`, then emit a search node (or terminal, if the entered face holds the goal) for
617
- * each still-uncovered piece.
731
+ * edge from `rootVid` at cost `g` or less, then emit a search node (or terminal, if the entered face
732
+ * holds the goal) for each piece that is left.
733
+ *
734
+ * Orientation, which every node carries: the edge runs Pv -> Qv with `from_face` on its left and the
735
+ * entered face `neighbour` on its right, and the root is on its left or on its line. The start face is
736
+ * laid out counter-clockwise and unfolding keeps each face's winding, so a face's own edges always run
737
+ * with it on their left; the root either looks at the edge through `from_face` or is one of that face's
738
+ * vertices.
618
739
  */
619
740
  function add_successor(topology, neighbour, from_face, Pv, Qv, Px, Py, Qx, Qy, rx, ry, rootVid, g, parent, lo, hi, goal_face) {
620
741
  if (neighbour === NULL_POINTER) return; // boundary far edge -- nothing to enter
621
742
  if (hi - lo < PEPS) return;
622
743
 
623
- const out = reserve(from_face, neighbour, rootVid, lo, hi);
744
+ const out = reserve(from_face, neighbour, rootVid, g, lo, hi);
624
745
  for (let i = 0; i < out; i += 2) {
625
746
  const ulo = _uncovered[i], uhi = _uncovered[i + 1];
626
747
  if (uhi - ulo < PEPS) continue;
@@ -635,11 +756,8 @@ function emit_node(topology, neighbour, Pv, Qv, Px, Py, Qx, Qy, rx, ry, rootVid,
635
756
  const ggx = goal_weight(Pv) * Px + goal_weight(Qv) * Qx + goal_weight(apex) * _apex[0];
636
757
  const ggy = goal_weight(Pv) * Py + goal_weight(Qv) * Qy + goal_weight(apex) * _apex[1];
637
758
 
638
- let i0x = Px + t0 * (Qx - Px), i0y = Py + t0 * (Qy - Py), i0p = t0;
639
- let i1x = Px + t1 * (Qx - Px), i1y = Py + t1 * (Qy - Py), i1p = t1;
640
- if (tri2_signed_area(rx, ry, i0x, i0y, i1x, i1y) < 0) {
641
- const sxx = i0x, syy = i0y, sp = i0p; i0x = i1x; i0y = i1y; i0p = i1p; i1x = sxx; i1y = syy; i1p = sp;
642
- }
759
+ const i0x = Px + t0 * (Qx - Px), i0y = Py + t0 * (Qy - Py);
760
+ const i1x = Px + t1 * (Qx - Px), i1y = Py + t1 * (Qy - Py);
643
761
 
644
762
  const fcost = g + touch_distance(rx, ry, i0x, i0y, i1x, i1y, ggx, ggy);
645
763
 
@@ -652,19 +770,19 @@ function emit_node(topology, neighbour, Pv, Qv, Px, Py, Qx, Qy, rx, ry, rootVid,
652
770
  if (t < -PEPS || t > 1 + PEPS) { // -1 (no crossing) is caught by `< -PEPS`
653
771
  const d0 = v2_distance(rx, ry, i0x, i0y) + v2_distance(i0x, i0y, ggx, ggy);
654
772
  const d1 = v2_distance(rx, ry, i1x, i1y) + v2_distance(i1x, i1y, ggx, ggy);
655
- fct = d0 <= d1 ? i0p : i1p;
773
+ fct = d0 <= d1 ? t0 : t1;
656
774
  }
657
775
 
658
776
  const id = alloc_node();
659
777
  const du = _node_du, df = _node_df;
660
- const b = id * NODE_WORDS;
661
- du[b + N_TERMINAL] = 1;
662
- du[b + N_ROOTVID] = rootVid === -1 ? NULL_ID : rootVid;
663
- du[b + N_E0V] = Pv; du[b + N_E1V] = Qv;
664
- df[b + N_E0X] = Px; df[b + N_E0Y] = Py; df[b + N_E1X] = Qx; df[b + N_E1Y] = Qy;
665
- df[b + N_RX] = rx; df[b + N_RY] = ry;
666
- du[b + N_PARENT] = parent;
667
- df[b + N_FCT] = fct;
778
+ const bu = id * NODE_U32, bf = id * NODE_F64;
779
+ du[bu + N_TERMINAL] = 1;
780
+ du[bu + N_ROOTVID] = rootVid === -1 ? NULL_ID : rootVid;
781
+ du[bu + N_E0V] = Pv; du[bu + N_E1V] = Qv;
782
+ df[bf + F_E0X] = Px; df[bf + F_E0Y] = Py; df[bf + F_E1X] = Qx; df[bf + F_E1Y] = Qy;
783
+ df[bf + F_RX] = rx; df[bf + F_RY] = ry;
784
+ du[bu + N_PARENT] = parent;
785
+ df[bf + F_FCT] = fct;
668
786
  _open.insert(id, fcost);
669
787
  return;
670
788
  }
@@ -683,15 +801,15 @@ function emit_node(topology, neighbour, Pv, Qv, Px, Py, Qx, Qy, rx, ry, rootVid,
683
801
 
684
802
  const id = alloc_node();
685
803
  const du = _node_du, df = _node_df;
686
- const b = id * NODE_WORDS;
687
- du[b + N_TERMINAL] = 0;
688
- du[b + N_FACE] = neighbour;
689
- du[b + N_E0V] = Pv; du[b + N_E1V] = Qv;
690
- df[b + N_E0X] = Px; df[b + N_E0Y] = Py; df[b + N_E1X] = Qx; df[b + N_E1Y] = Qy;
691
- du[b + N_ROOTVID] = rootVid === -1 ? NULL_ID : rootVid;
692
- df[b + N_RX] = rx; df[b + N_RY] = ry;
693
- df[b + N_T0] = t0; df[b + N_T1] = t1;
694
- df[b + N_G] = g;
695
- du[b + N_PARENT] = parent;
804
+ const bu = id * NODE_U32, bf = id * NODE_F64;
805
+ du[bu + N_TERMINAL] = 0;
806
+ du[bu + N_FACE] = neighbour;
807
+ du[bu + N_E0V] = Pv; du[bu + N_E1V] = Qv;
808
+ df[bf + F_E0X] = Px; df[bf + F_E0Y] = Py; df[bf + F_E1X] = Qx; df[bf + F_E1Y] = Qy;
809
+ du[bu + N_ROOTVID] = rootVid === -1 ? NULL_ID : rootVid;
810
+ df[bf + F_RX] = rx; df[bf + F_RY] = ry;
811
+ df[bf + F_T0] = t0; df[bf + F_T1] = t1;
812
+ df[bf + F_G] = g;
813
+ du[bu + N_PARENT] = parent;
696
814
  _open.insert(id, g + h);
697
815
  }