@woosh/meep-engine 3.1.0 → 3.3.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 (1869) hide show
  1. package/LICENSE +188 -188
  2. package/README.md +152 -152
  3. package/editor/Editor.js +598 -598
  4. package/editor/EditorCameraNavigation.js +205 -205
  5. package/editor/EditorKeyMap.js +182 -182
  6. package/editor/SelectionVisualizer.js +127 -127
  7. package/editor/actions/concrete/ActionInvalidateSampler.js +27 -27
  8. package/editor/actions/concrete/PatchTerrainHeightAction.js +85 -85
  9. package/editor/camera/camera_framing.js +104 -104
  10. package/editor/clipboard.js +184 -184
  11. package/editor/ecs/component/editors/ObservedIntegerEditor.js +22 -22
  12. package/editor/ecs/component/editors/ObservedStringEditor.js +34 -34
  13. package/editor/ecs/component/editors/common/two_way_sync.js +50 -50
  14. package/editor/ecs/component/editors/ecs/GridObstacleEditor.js +19 -19
  15. package/editor/ecs/component/editors/ecs/ParameterLookupTableEditor.js +35 -35
  16. package/editor/ecs/component/editors/ecs/ParameterTrackEditor.js +17 -17
  17. package/editor/ecs/component/editors/ecs/SGMeshEditor.js +24 -24
  18. package/editor/ecs/component/editors/ecs/ShadedGeometryEditor.js +33 -33
  19. package/editor/ecs/component/editors/ecs/Trail3DEditor.js +45 -45
  20. package/editor/ecs/component/editors/ecs/buildGridObstaclePreview.js +19 -19
  21. package/editor/ecs/component/editors/ecs/terrain/TerrainEditor.js +72 -72
  22. package/editor/ecs/component/editors/shade/MeshletGeometryEditor.js +30 -30
  23. package/editor/ecs/component/editors/shade/ShadeImageEditor.js +70 -70
  24. package/editor/ecs/component/editors/shade/ShadeMaterialEditor.js +34 -34
  25. package/editor/ecs/component/editors/shade/ShadeTextureEditor.js +46 -46
  26. package/editor/ecs/component/editors/shade/StandardShadeMaterialEditor.js +24 -24
  27. package/editor/ecs/component/prototypeObjectEditor.js +265 -265
  28. package/editor/ecs/component/registerBasicTypeEditors.js +100 -100
  29. package/editor/ecs/component/registerEngineComponentEditors.js +95 -95
  30. package/editor/entity_world_bounds.js +27 -27
  31. package/editor/particles/lut_ops.js +148 -148
  32. package/editor/particles/particleEditEvents.js +12 -12
  33. package/editor/particles/rebuildParticleEmitter.js +31 -31
  34. package/editor/persistence/EditorLayoutStore.js +71 -71
  35. package/editor/persistence/SceneMetadata.js +52 -52
  36. package/editor/persistence/SceneSlotManager.js +144 -144
  37. package/editor/persistence/buildNewSceneDataset.js +67 -67
  38. package/editor/persistence/files.js +68 -68
  39. package/editor/persistence/scene_import.js +143 -143
  40. package/editor/persistence/scene_serialization.js +79 -79
  41. package/editor/persistence/string_buffer_codec.js +41 -41
  42. package/editor/process/GridDisplayProcess.js +125 -125
  43. package/editor/process/SymbolicDisplayProcess.js +106 -108
  44. package/editor/process/symbolic/CameraSymbolicDisplay.js +146 -146
  45. package/editor/process/symbolic/LightSymbolicDisplay.js +172 -172
  46. package/editor/process/symbolic/PathSymbolicDisplay.js +118 -118
  47. package/editor/process/symbolic/PositionedMarkerSymbolicDisplay.js +79 -79
  48. package/editor/process/symbolic/SoundEmitterSymbolicDisplay.js +57 -57
  49. package/editor/process/symbolic/SymbolicDisplay.js +62 -62
  50. package/editor/process/symbolic/gizmo_draw_wire_box_oriented.js +85 -85
  51. package/editor/process/symbolic/gizmo_draw_wire_circle.js +53 -53
  52. package/editor/prototypeEditorShell.js +156 -156
  53. package/editor/selection/editor_pick.js +130 -130
  54. package/editor/selection/screen_rect_frustum.js +119 -119
  55. package/editor/style/controls.scss +168 -168
  56. package/editor/style/inspector.scss +223 -223
  57. package/editor/templates/entity_templates.js +167 -167
  58. package/editor/tools/GridPaintTool.js +273 -273
  59. package/editor/tools/SelectionTool.js +307 -307
  60. package/editor/tools/TransformToolV2.js +378 -378
  61. package/editor/tools/engine/ToolEngine.js +188 -188
  62. package/editor/tools/paint/TerrainTexturePaintTool.js +250 -250
  63. package/editor/tools/v2/BlenderCameraOrientationGizmo.js +501 -501
  64. package/editor/tools/v2/TransformControls.js +971 -971
  65. package/editor/tools/v2/handle/gizmo_drag_plane.js +141 -141
  66. package/editor/tools/v2/handle/gizmo_draw.js +399 -399
  67. package/editor/tools/v2/handle/gizmo_handles.js +364 -364
  68. package/editor/tools/v2/handle/gizmo_ray_shapes.js +239 -239
  69. package/editor/view/ecs/HierarchicalEntityListView.js +339 -339
  70. package/editor/view/ecs/components/DatGuiController.js +2 -2
  71. package/editor/view/ecs/components/common/AutoCanvasView.js +100 -100
  72. package/editor/view/ecs/components/common/TextController.js +59 -59
  73. package/editor/view/library/MeshLibraryView.js +175 -175
  74. package/editor/view/library/mesh_library_drag.js +63 -63
  75. package/editor/view/library/model_thumbnail_source.js +86 -86
  76. package/editor/view/makeEntityDecorators.js +125 -125
  77. package/editor/view/node-graph/NodeGraphSelection.js +89 -89
  78. package/editor/view/node-graph/NodeView.js +211 -211
  79. package/editor/view/node-graph/actions/ConnectionDeleteAction.js +36 -36
  80. package/editor/view/node-graph/actions/NodesMoveAction.js +41 -41
  81. package/editor/view/node-graph/connection_wire_geometry.js +107 -107
  82. package/editor/view/particles/ColorLutGradientView.js +291 -291
  83. package/editor/view/particles/ScalarLutCurveView.js +279 -279
  84. package/editor/view/prepareMeshLibrary.js +188 -188
  85. package/editor/view/shell/SceneTreePanelView.js +119 -119
  86. package/editor/view/v2/SplitView.js +2 -2
  87. package/package.json +94 -94
  88. package/samples/generation/generators/interactive/mir_generator_place_buff_objects.js +362 -362
  89. package/src/DUPLICATION_AUDIT_2026_08_07/00_core_reuse_surface.md +738 -738
  90. package/src/DUPLICATION_AUDIT_2026_08_07/A_core_math_geom.md +79 -79
  91. package/src/DUPLICATION_AUDIT_2026_08_07/B_core_data.md +93 -93
  92. package/src/DUPLICATION_AUDIT_2026_08_07/C_gfx_render.md +80 -80
  93. package/src/DUPLICATION_AUDIT_2026_08_07/D_gfx_features.md +164 -164
  94. package/src/DUPLICATION_AUDIT_2026_08_07/E_physics.md +69 -69
  95. package/src/DUPLICATION_AUDIT_2026_08_07/F_sound.md +102 -102
  96. package/src/DUPLICATION_AUDIT_2026_08_07/G_net_save.md +129 -129
  97. package/src/DUPLICATION_AUDIT_2026_08_07/H_engine_misc.md +72 -72
  98. package/src/DUPLICATION_AUDIT_2026_08_07/I_generation_view.md +74 -74
  99. package/src/DUPLICATION_AUDIT_2026_08_07/K_crosscutting.md +169 -169
  100. package/src/DUPLICATION_AUDIT_2026_08_07.md +254 -254
  101. package/src/DUPLICATION_AUDIT_REMEDIATION.md +250 -250
  102. package/src/REVIEW_2026_08_06/adv-core.md +511 -511
  103. package/src/REVIEW_2026_08_06/adv-gfx.md +480 -480
  104. package/src/REVIEW_2026_08_06/adv-view.md +446 -446
  105. package/src/REVIEW_2026_08_06/api-core.md +296 -296
  106. package/src/REVIEW_2026_08_06/api-engine.md +195 -195
  107. package/src/REVIEW_2026_08_06/bvh-phys-sound-net.md +507 -507
  108. package/src/REVIEW_2026_08_06/core-binary.md +348 -348
  109. package/src/REVIEW_2026_08_06/core-collection.md +376 -376
  110. package/src/REVIEW_2026_08_06/core-math.md +221 -221
  111. package/src/REVIEW_2026_08_06/core-process.md +266 -266
  112. package/src/REVIEW_2026_08_06/core-science.md +223 -223
  113. package/src/REVIEW_2026_08_06/engine-ai-nav.md +279 -279
  114. package/src/REVIEW_2026_08_06/engine-animation.md +167 -167
  115. package/src/REVIEW_2026_08_06/engine-asset.md +166 -166
  116. package/src/REVIEW_2026_08_06/engine-ecs.md +920 -920
  117. package/src/REVIEW_2026_08_06/engine-input.md +197 -197
  118. package/src/REVIEW_2026_08_06/engine-misc.md +248 -248
  119. package/src/REVIEW_2026_08_06/engine-terrain.md +52 -52
  120. package/src/REVIEW_2026_08_06/generation.md +176 -176
  121. package/src/REVIEW_2026_08_06/geom-2d.md +434 -434
  122. package/src/REVIEW_2026_08_06/geom-3d.md +117 -117
  123. package/src/REVIEW_2026_08_06/gfx-gi.md +143 -143
  124. package/src/REVIEW_2026_08_06/gfx-render.md +740 -740
  125. package/src/REVIEW_2026_08_06/gfx-texture.md +690 -690
  126. package/src/REVIEW_2026_08_06/prior-criticals.md +49 -49
  127. package/src/REVIEW_2026_08_06/prior-highs.md +138 -138
  128. package/src/REVIEW_2026_08_06/test-gaps.md +364 -364
  129. package/src/REVIEW_2026_08_06/view.md +667 -667
  130. package/src/REVIEW_2026_08_06.md +610 -610
  131. package/src/avif/codec/dec/avif_dec.cpp +233 -233
  132. package/src/avif/codec/dec/avif_dec.js +16 -16
  133. package/src/avif/encoder_worker.js +5 -18
  134. package/src/core/binary/BitImage2.js +246 -246
  135. package/src/core/binary/align_32.js +21 -21
  136. package/src/core/binary/data_view/DataType2DataViewReaders.js +23 -23
  137. package/src/core/binary/data_view/DataType2DataViewWriters.js +25 -25
  138. package/src/core/binary/hash/XXH128Hash.js +55 -55
  139. package/src/core/binary/hash/XXH3_SECRET.js +25 -25
  140. package/src/core/binary/hash/xxh3_128.js +341 -341
  141. package/src/core/binary/hash/xxh3_64.js +241 -241
  142. package/src/core/binary/hash/xxh3_common.js +476 -476
  143. package/src/core/binary/lz4/LZ4_MAX_INPUT_SIZE.js +9 -9
  144. package/src/core/binary/lz4/lz4_compress_block.js +385 -385
  145. package/src/core/binary/lz4/lz4_compress_bound.js +25 -25
  146. package/src/core/binary/lz4/lz4_decompress_block.js +139 -139
  147. package/src/core/binary/lz4/lz4_decompress_bound.js +30 -30
  148. package/src/core/binary/meshopt/MESHOPT_GROUP_SIZE.js +18 -18
  149. package/src/core/binary/meshopt/MeshoptCompressionFilter.js +30 -30
  150. package/src/core/binary/meshopt/MeshoptCompressionMode.js +21 -21
  151. package/src/core/binary/meshopt/__meshopt_test_streams.js +237 -237
  152. package/src/core/binary/meshopt/meshopt_decode.js +73 -73
  153. package/src/core/binary/meshopt/meshopt_decode_byte_group.js +67 -67
  154. package/src/core/binary/meshopt/meshopt_decode_data_block.js +59 -59
  155. package/src/core/binary/meshopt/meshopt_decode_filter_color.js +69 -69
  156. package/src/core/binary/meshopt/meshopt_decode_filter_exponential.js +43 -43
  157. package/src/core/binary/meshopt/meshopt_decode_filter_octahedral.js +60 -60
  158. package/src/core/binary/meshopt/meshopt_decode_filter_quaternion.js +71 -71
  159. package/src/core/binary/meshopt/meshopt_decode_index_buffer.js +294 -294
  160. package/src/core/binary/meshopt/meshopt_decode_index_sequence.js +115 -115
  161. package/src/core/binary/meshopt/meshopt_decode_vertex_buffer.js +381 -381
  162. package/src/core/binary/meshopt/meshopt_read_uint_var.js +59 -59
  163. package/src/core/binary/reinterpret_float32_as_uint32.js +13 -13
  164. package/src/core/binary/reinterpret_uint32_as_float32.js +14 -14
  165. package/src/core/binary/type/DataType2TypedArrayConstructorMapping.js +35 -35
  166. package/src/core/bvh2/bvh3/ebvh_geometry_query_any_triangle_ray.js +149 -149
  167. package/src/core/bvh2/bvh3/ebvh_sort_for_traversal_depth_first.js +96 -96
  168. package/src/core/bvh2/bvh3/query/bvh_query_depth_range_in_frustum.js +101 -101
  169. package/src/core/bvh2/bvh3/query/bvh_query_leaves_generic.js +77 -77
  170. package/src/core/bvh2/bvh3/query/bvh_query_leaves_ray.js +90 -90
  171. package/src/core/bvh2/bvh3/query/bvh_query_leaves_ray_segment.js +94 -94
  172. package/src/core/bvh2/bvh3/query/bvh_query_user_data_generic.js +86 -86
  173. package/src/core/bvh2/bvh3/query/bvh_query_user_data_intersects_capsule.js +101 -101
  174. package/src/core/bvh2/bvh3/query/bvh_query_user_data_intersects_sphere.js +92 -92
  175. package/src/core/bvh2/bvh3/query/bvh_query_user_data_ray.js +96 -96
  176. package/src/core/bvh2/bvh3/query/bvh_query_user_data_ray_segment.js +100 -100
  177. package/src/core/bvh2/visual/BVHGeometry.js +131 -131
  178. package/src/core/bvh8/BVH8.js +436 -436
  179. package/src/core/bvh8/aabb3_round_to_float32.js +22 -22
  180. package/src/core/bvh8/build/BVH8Converter.js +588 -588
  181. package/src/core/bvh8/build/TriangleCluster.js +122 -122
  182. package/src/core/bvh8/build/aabb3_compute_merge_cost.js +28 -28
  183. package/src/core/bvh8/build/aabb3_from_triangle_by_index.js +17 -17
  184. package/src/core/bvh8/build/bvh8_build_for_geometry.js +303 -303
  185. package/src/core/bvh8/build/bvh8_from_proxy.js +256 -256
  186. package/src/core/bvh8/build/byte.js +9 -9
  187. package/src/core/bvh8/build/encode_bounds_e.js +11 -11
  188. package/src/core/bvh8/bvh8_convert_to_dot.js +132 -132
  189. package/src/core/bvh8/bvh8_count_primitives.js +98 -98
  190. package/src/core/bvh8/bvh8_geometry_validate.js +138 -138
  191. package/src/core/bvh8/bvh8_geometry_validate_indirect.js +166 -166
  192. package/src/core/bvh8/bvh8_get_node_bounds.js +34 -34
  193. package/src/core/bvh8/bvh8_get_node_child_bounds.js +52 -52
  194. package/src/core/bvh8/bvh8_node_child_surface_area.js +18 -18
  195. package/src/core/bvh8/bvh8_node_count_triangles.js +27 -27
  196. package/src/core/bvh8/bvh8_quality.js +72 -72
  197. package/src/core/bvh8/bvh8_validate_structure.js +87 -87
  198. package/src/core/clipboard/obtainClipBoard.js +29 -29
  199. package/src/core/clipboard/safeClipboardReadText.js +55 -55
  200. package/src/core/clipboard/safeClipboardWriteText.js +23 -23
  201. package/src/core/collection/array/typed/typed_array_value_denormalize.js +52 -52
  202. package/src/core/collection/array/typed/typed_array_value_normalize.js +51 -51
  203. package/src/core/collection/union-find/union_find.js +79 -79
  204. package/src/core/color/PQ/PQ_constants.d.ts +43 -0
  205. package/src/core/color/PQ/PQ_constants.d.ts.map +1 -0
  206. package/src/core/color/PQ/PQ_constants.js +50 -0
  207. package/src/core/color/PQ/PQ_to_linear.d.ts +24 -0
  208. package/src/core/color/PQ/PQ_to_linear.d.ts.map +1 -0
  209. package/src/core/color/PQ/PQ_to_linear.js +35 -0
  210. package/src/core/color/PQ/linear_to_PQ.d.ts +27 -0
  211. package/src/core/color/PQ/linear_to_PQ.d.ts.map +1 -0
  212. package/src/core/color/PQ/linear_to_PQ.js +32 -0
  213. package/src/core/color/YCoCg/rgb_to_ycocg.js +20 -20
  214. package/src/core/color/YCoCg/ycocg_to_rgb.js +17 -17
  215. package/src/core/color/colormap/MAGMA_LUT.js +26 -26
  216. package/src/core/color/illuminant/D65_TRISTIMULUS_XYZ.js +19 -19
  217. package/src/core/color/lab/lab_apply_hunt_adjustment.js +27 -27
  218. package/src/core/color/lab/lab_distance_hyab.js +23 -23
  219. package/src/core/color/lab/xyz_to_lab.js +62 -62
  220. package/src/core/color/operations/color_darken.js +24 -24
  221. package/src/core/color/operations/color_desaturate.js +24 -24
  222. package/src/core/color/operations/color_lighten.js +24 -24
  223. package/src/core/color/operations/color_saturate.js +24 -24
  224. package/src/core/color/operations/color_scale_okhsv_channel.js +45 -45
  225. package/src/core/color/tonemap/tonemap_aces.js +41 -41
  226. package/src/core/color/ycxcz/xyz_to_ycxcz.js +28 -28
  227. package/src/core/color/ycxcz/ycxcz_to_xyz.js +19 -19
  228. package/src/core/dom/isImageBitmap.js +12 -12
  229. package/src/core/function/frameThrottle.js +23 -23
  230. package/src/core/geom/2d/aabb/aabb2_array_combine.js +7 -7
  231. package/src/core/geom/2d/aabb/aabb2_compute_area.js +7 -7
  232. package/src/core/geom/2d/aabb/aabb2_compute_center_from_multiple.js +7 -7
  233. package/src/core/geom/2d/aabb/aabb2_compute_overlap.js +7 -7
  234. package/src/core/geom/2d/aabb/aabb2_from_v2_array.js +7 -7
  235. package/src/core/geom/2d/aabb/aabb2_intersects_point.js +7 -7
  236. package/src/core/geom/2d/aabb/aabb2_overlap_exists.js +7 -7
  237. package/src/core/geom/2d/circle/circle_compute_circle_intersection.js +7 -7
  238. package/src/core/geom/2d/circle/circle_compute_circle_penetration.js +7 -7
  239. package/src/core/geom/2d/circle/circle_intersects_point.js +7 -7
  240. package/src/core/geom/2d/compute_polygon_area_2d.js +7 -7
  241. package/src/core/geom/2d/convex-hull/fixed_convex_hull_humus.js +7 -7
  242. package/src/core/geom/2d/convex-hull/fixed_convex_hull_relaxation.js +7 -7
  243. package/src/core/geom/2d/convex-hull/orientation3_array.js +7 -7
  244. package/src/core/geom/2d/intersect_ray_2d.js +7 -7
  245. package/src/core/geom/2d/line/line2_distance_to_point_sqr.js +7 -7
  246. package/src/core/geom/2d/line/line2_segment_intersection_fraction_2d.js +44 -44
  247. package/src/core/geom/2d/line/line_segment_compute_line_segment_intersection_2d.js +7 -7
  248. package/src/core/geom/2d/line/line_segment_compute_line_segment_intersection_array_2d.js +7 -7
  249. package/src/core/geom/2d/line/line_segment_compute_line_segment_intersection_vectors_2d.js +7 -7
  250. package/src/core/geom/2d/line/line_segment_compute_line_segment_intersection_vectors_array_2d.js +7 -7
  251. package/src/core/geom/2d/line/line_segment_intersection_fraction_2d.js +7 -7
  252. package/src/core/geom/2d/line/line_segment_line_segment_intersection_exists_2d.js +7 -7
  253. package/src/core/geom/2d/line/segment2_intersects_segment2_2d.js +25 -25
  254. package/src/core/geom/2d/line/segment2_segment_compute_intersection_array_2d.js +46 -46
  255. package/src/core/geom/2d/line/segment2_segment_intersection_fractions.js +52 -52
  256. package/src/core/geom/2d/oriented-box/min_area_rect_2d.js +69 -69
  257. package/src/core/geom/2d/polygon/TRIANGULATION_DESIGN.md +433 -433
  258. package/src/core/geom/2d/polygon/polygon2_clip_axis_halfplane.js +51 -51
  259. package/src/core/geom/2d/polygon/polygon2_clip_halfplane.js +50 -50
  260. package/src/core/geom/2d/polygon/polygon2_is_counter_clockwise.js +84 -84
  261. package/src/core/geom/2d/polygon/polygon2_signed_area.js +52 -52
  262. package/src/core/geom/2d/polygon/polygon2_triangulate.corpus.js +86 -86
  263. package/src/core/geom/2d/polygon/polygon2_triangulate.js +1280 -1280
  264. package/src/core/geom/2d/polygon/polygon2_triangulation_deviation.js +77 -77
  265. package/src/core/geom/2d/quad-tree/qt_collect_by_circle.js +39 -39
  266. package/src/core/geom/2d/quad-tree/qt_match_data_by_circle.js +101 -101
  267. package/src/core/geom/2d/quad-tree/qt_query_data_nearest_to_point.js +7 -7
  268. package/src/core/geom/2d/rectangle_to_aabb.js +7 -7
  269. package/src/core/geom/2d/spline_bezier2_2d.js +7 -7
  270. package/src/core/geom/2d/triangle/tri2_rasterize_conservative.js +99 -99
  271. package/src/core/geom/2d/triangle/tri2_signed_area.js +35 -35
  272. package/src/core/geom/2d/triangle2d_compute_area.js +7 -7
  273. package/src/core/geom/2d/triangle2d_contains_point.js +7 -7
  274. package/src/core/geom/2d/triangle2d_get_barycentric.js +7 -7
  275. package/src/core/geom/2d/v2_morton_encode.js +23 -23
  276. package/src/core/geom/3d/aabb/aabb3_array_intersects_frustum_degree.js +7 -7
  277. package/src/core/geom/3d/aabb/aabb3_box_surface_area_2.js +7 -7
  278. package/src/core/geom/3d/aabb/aabb3_build_frustum.js +5 -5
  279. package/src/core/geom/3d/aabb/aabb3_closest_point_on_surface.js +57 -57
  280. package/src/core/geom/3d/aabb/aabb3_combined_surface_area.js +7 -7
  281. package/src/core/geom/3d/aabb/aabb3_compute_half_surface_area.js +7 -7
  282. package/src/core/geom/3d/aabb/aabb3_compute_projected_area.js +7 -7
  283. package/src/core/geom/3d/aabb/aabb3_compute_surface_area.js +7 -7
  284. package/src/core/geom/3d/aabb/aabb3_detailed_volume_intersection.js +7 -7
  285. package/src/core/geom/3d/aabb/aabb3_estimate_projected_area2.js +7 -7
  286. package/src/core/geom/3d/aabb/aabb3_from_threejs_geometry.js +7 -7
  287. package/src/core/geom/3d/aabb/aabb3_from_triangle_group.js +46 -46
  288. package/src/core/geom/3d/aabb/aabb3_intersects_frustum_degree.js +7 -7
  289. package/src/core/geom/3d/aabb/aabb3_intersects_line_segment.js +7 -7
  290. package/src/core/geom/3d/aabb/aabb3_near_distance_to_intersection_ray_segment.js +7 -7
  291. package/src/core/geom/3d/aabb/aabb3_nearest_point_on_surface.js +7 -7
  292. package/src/core/geom/3d/aabb/aabb3_ray_segment_entry_distance.js +95 -95
  293. package/src/core/geom/3d/aabb/aabb3_raycast.js +7 -7
  294. package/src/core/geom/3d/aabb/aabb3_transform_oriented.js +93 -93
  295. package/src/core/geom/3d/aabb/aabb3_transform_oriented_inverse.js +82 -82
  296. package/src/core/geom/3d/aabb/compute_aabb_from_points.js +7 -7
  297. package/src/core/geom/3d/aabb/compute_triangle_group_aabb3.js +7 -7
  298. package/src/core/geom/3d/atlas/ATLAS_PORT_PLAN.md +486 -486
  299. package/src/core/geom/3d/atlas/BFF_PLAN.md +174 -174
  300. package/src/core/geom/3d/atlas/Chart.js +77 -77
  301. package/src/core/geom/3d/atlas/REVIEW_02.md +392 -392
  302. package/src/core/geom/3d/atlas/atlas_bench_lib.js +315 -315
  303. package/src/core/geom/3d/atlas/atlas_chart_type.js +16 -16
  304. package/src/core/geom/3d/atlas/atlas_compute_charts.js +188 -188
  305. package/src/core/geom/3d/atlas/atlas_extract_chart.js +107 -107
  306. package/src/core/geom/3d/atlas/atlas_generate.js +50 -50
  307. package/src/core/geom/3d/atlas/atlas_test_fixtures.js +266 -266
  308. package/src/core/geom/3d/atlas/atlas_write_chart_uvs.js +36 -36
  309. package/src/core/geom/3d/atlas/data/atlas_build_edge_dihedrals.js +62 -62
  310. package/src/core/geom/3d/atlas/data/atlas_build_edge_lengths.js +33 -33
  311. package/src/core/geom/3d/atlas/data/atlas_build_face_areas.js +50 -50
  312. package/src/core/geom/3d/atlas/io/atlas_mesh_weld_colocals.js +39 -39
  313. package/src/core/geom/3d/atlas/io/bt_loop_uv.js +35 -35
  314. package/src/core/geom/3d/atlas/pack/ChartPlacement.js +46 -46
  315. package/src/core/geom/3d/atlas/param/atlas_chart_parameterize_bff.js +269 -269
  316. package/src/core/geom/3d/atlas/param/atlas_chart_parameterize_lscm.js +209 -209
  317. package/src/core/geom/3d/atlas/param/atlas_chart_parameterize_ortho.js +38 -38
  318. package/src/core/geom/3d/atlas/param/atlas_chart_stretch_metric.js +86 -86
  319. package/src/core/geom/3d/atlas/param/atlas_piecewise_resegment.js +183 -183
  320. package/src/core/geom/3d/atlas/param/fit_plane_basis.js +66 -66
  321. package/src/core/geom/3d/atlas/segment/RegionGraph.js +40 -40
  322. package/src/core/geom/3d/atlas/segment/atlas_build_region_graph.js +136 -136
  323. package/src/core/geom/3d/atlas/segment/atlas_chart_roundness_delta.js +30 -30
  324. package/src/core/geom/3d/atlas/segment/atlas_compute_planar_regions.js +77 -77
  325. package/src/core/geom/3d/atlas/segment/atlas_merge_charts.js +116 -116
  326. package/src/core/geom/3d/atlas/segment/atlas_segment_charts.js +271 -271
  327. package/src/core/geom/3d/atlas/segment/atlas_segment_charts_atomic.js +191 -191
  328. package/src/core/geom/3d/box/box3_projected_half_extent.js +35 -35
  329. package/src/core/geom/3d/capsule/capsule_intersects_aabb3_closed.js +67 -67
  330. package/src/core/geom/3d/capsule/capsule_intersects_aabb3_iterative.js +137 -137
  331. package/src/core/geom/3d/compute_circle_bounding_box.js +7 -7
  332. package/src/core/geom/3d/cone/computeConeBoundingBox.js +7 -7
  333. package/src/core/geom/3d/cone/computeConePlaneSide.js +7 -7
  334. package/src/core/geom/3d/cone/compute_bounding_cone_of_2_cones.js +7 -7
  335. package/src/core/geom/3d/cylinder/cylinder3_raycast.d.ts +38 -0
  336. package/src/core/geom/3d/cylinder/cylinder3_raycast.d.ts.map +1 -0
  337. package/src/core/geom/3d/cylinder/cylinder3_raycast.js +105 -0
  338. package/src/core/geom/3d/equirectangular/equirectangular_direction_to_uv.js +18 -18
  339. package/src/core/geom/3d/equirectangular/equirectangular_uv_to_direction.js +24 -24
  340. package/src/core/geom/3d/frustum/clipping_volume_matrix4_project.js +7 -7
  341. package/src/core/geom/3d/frustum/frustum3_computeNearestPointToPoint.js +7 -7
  342. package/src/core/geom/3d/frustum/frustum3_compute_closest_point.js +117 -117
  343. package/src/core/geom/3d/frustum/frustum_matrix4_project.js +7 -7
  344. package/src/core/geom/3d/frustum/hexahedron_from_aabb.js +7 -7
  345. package/src/core/geom/3d/frustum/read_frustum_corner.js +17 -17
  346. package/src/core/geom/3d/frustum/read_three_planes_to_array.js +7 -7
  347. package/src/core/geom/3d/frustum/slice_frustum_linear_to_points.js +7 -7
  348. package/src/core/geom/3d/gjk/GJK_REVIEW_NOTES.md +146 -146
  349. package/src/core/geom/3d/gjk/gjk.js +430 -430
  350. package/src/core/geom/3d/gjk/gjk_epa_penetration.js +520 -520
  351. package/src/core/geom/3d/gjk/minkowski_support.js +71 -71
  352. package/src/core/geom/3d/gjk/mpr.js +368 -368
  353. package/src/core/geom/3d/line/line3_closest_points_segment_segment.js +7 -7
  354. package/src/core/geom/3d/line/line3_compute_nearest_point_to_point.js +7 -7
  355. package/src/core/geom/3d/line/line3_compute_point_segment_point_distance_unfolded.js +64 -64
  356. package/src/core/geom/3d/line/line3_compute_segment_closest_point_to_aabb3_t.js +153 -153
  357. package/src/core/geom/3d/line/line3_compute_segment_nearest_point_to_aabb3_t.js +7 -7
  358. package/src/core/geom/3d/line/line3_compute_segment_nearest_point_to_point_t.js +7 -7
  359. package/src/core/geom/3d/line/line3_compute_segment_segment_closest_points.js +88 -88
  360. package/src/core/geom/3d/line/line3_segment_segment_closest_point_pair.js +71 -71
  361. package/src/core/geom/3d/line/segment3_point_distance_to_segments.js +25 -25
  362. package/src/core/geom/3d/mat4/allocate_m4.js +7 -7
  363. package/src/core/geom/3d/mat4/apply_mat4_transform_to_direction_v3_array.js +7 -7
  364. package/src/core/geom/3d/mat4/apply_mat4_transform_to_v3_array.js +7 -7
  365. package/src/core/geom/3d/mat4/compose_matrix4_array.js +7 -7
  366. package/src/core/geom/3d/mat4/decompose_matrix_4_array.js +7 -7
  367. package/src/core/geom/3d/mat4/eulerAnglesFromMatrix.js +7 -7
  368. package/src/core/geom/3d/mat4/m4_compose.js +22 -22
  369. package/src/core/geom/3d/mat4/m4_compute_normal_matrix3.js +7 -7
  370. package/src/core/geom/3d/mat4/m4_decompose.js +64 -64
  371. package/src/core/geom/3d/mat4/m4_decompose_array.js +157 -157
  372. package/src/core/geom/3d/mat4/m4_from_rotation_translation_scale.js +32 -32
  373. package/src/core/geom/3d/mat4/m4_from_rotation_translation_scale_scalar.js +67 -67
  374. package/src/core/geom/3d/mat4/m4_linear_matrix3.js +29 -29
  375. package/src/core/geom/3d/mat4/m4_look_at.js +96 -96
  376. package/src/core/geom/3d/mat4/m4_make_rotation_x.js +47 -47
  377. package/src/core/geom/3d/mat4/m4_make_rotation_y.js +47 -47
  378. package/src/core/geom/3d/mat4/m4_make_rotation_z.js +47 -47
  379. package/src/core/geom/3d/mat4/m4_normal_matrix3.js +39 -39
  380. package/src/core/geom/3d/mat4/m4_perspective.js +66 -66
  381. package/src/core/geom/3d/mat4/m4_transpose.js +48 -48
  382. package/src/core/geom/3d/morton/mortonEncode_LUT.js +7 -7
  383. package/src/core/geom/3d/morton/mortonEncode_magicbits.js +7 -7
  384. package/src/core/geom/3d/normal/hemioct/encode_unit3_hemioct.js +5 -5
  385. package/src/core/geom/3d/normal/hemioct/encode_unit_to_hemioct.js +28 -28
  386. package/src/core/geom/3d/normal/lambert_azimuth/decode_azimuth_to_unit.js +30 -30
  387. package/src/core/geom/3d/normal/lambert_azimuth/encode_unit_to_azimuth.js +19 -19
  388. package/src/core/geom/3d/normal/spherical/decode_spheremap_to_unit.js +38 -38
  389. package/src/core/geom/3d/normal/spherical/encode_unit_to_spheremap.js +30 -30
  390. package/src/core/geom/3d/plane/is_point_within_planes.js +7 -7
  391. package/src/core/geom/3d/plane/orient3d_fast_point.js +47 -47
  392. package/src/core/geom/3d/plane/plane3_compute_convex_3_plane_intersection.js +7 -7
  393. package/src/core/geom/3d/plane/plane3_compute_plane_intersection.js +7 -7
  394. package/src/core/geom/3d/plane/plane3_intersect_plane.js +7 -7
  395. package/src/core/geom/3d/plane/plane3_three_compute_convex_3_plane_intersection.js +7 -7
  396. package/src/core/geom/3d/point/point3_raycast.d.ts +33 -0
  397. package/src/core/geom/3d/point/point3_raycast.d.ts.map +1 -0
  398. package/src/core/geom/3d/point/point3_raycast.js +47 -0
  399. package/src/core/geom/3d/polygon/polygon3_compute_normal.js +60 -60
  400. package/src/core/geom/3d/polygon/polygon3_triangulate.js +94 -94
  401. package/src/core/geom/3d/polyhedron/convex_polyhedron3_raycast.d.ts +40 -0
  402. package/src/core/geom/3d/polyhedron/convex_polyhedron3_raycast.d.ts.map +1 -0
  403. package/src/core/geom/3d/polyhedron/convex_polyhedron3_raycast.js +94 -0
  404. package/src/core/geom/3d/quadric/QUADRIC3_SIZE.js +14 -14
  405. package/src/core/geom/3d/quadric/Quadric3.js +282 -282
  406. package/src/core/geom/3d/quadric/quadric3_add.js +33 -33
  407. package/src/core/geom/3d/quadric/quadric3_clear.js +25 -25
  408. package/src/core/geom/3d/quadric/quadric3_copy.js +27 -27
  409. package/src/core/geom/3d/quadric/quadric3_distance.js +36 -36
  410. package/src/core/geom/3d/quadric/quadric3_evaluate.js +25 -25
  411. package/src/core/geom/3d/quadric/quadric3_optimize.js +45 -45
  412. package/src/core/geom/3d/quadric/quadric3_scale.js +32 -32
  413. package/src/core/geom/3d/quadric/quadric3_set_from_plane_through_point.js +30 -30
  414. package/src/core/geom/3d/quadric/quadric3_set_from_vector4.js +35 -35
  415. package/src/core/geom/3d/quadric/quadric3_sub.js +36 -36
  416. package/src/core/geom/3d/quadric/quadric3_to_tensor_m3.js +28 -28
  417. package/src/core/geom/3d/quadric/quadric3_to_tensor_m3_inverse.js +56 -56
  418. package/src/core/geom/3d/quadric/quadric3_to_vector3.js +19 -19
  419. package/src/core/geom/3d/quaternion/quat3_createFromAxisAngle.js +7 -7
  420. package/src/core/geom/3d/quaternion/quat3_integrate.js +62 -62
  421. package/src/core/geom/3d/quaternion/quat3_multiply.js +31 -31
  422. package/src/core/geom/3d/quaternion/quat3_nlerp.js +72 -72
  423. package/src/core/geom/3d/quaternion/quat3_to_matrix3.js +69 -69
  424. package/src/core/geom/3d/quaternion/quat_decode_from_uint32.js +7 -7
  425. package/src/core/geom/3d/quaternion/quat_encode_to_uint32.js +7 -7
  426. package/src/core/geom/3d/ray/ray3_array_compose.js +7 -7
  427. package/src/core/geom/3d/ray/ray3_compute_nearest_point_to_point.js +7 -7
  428. package/src/core/geom/3d/ray/ray3_distance_to_point.js +7 -7
  429. package/src/core/geom/3d/ray/ray3_shift_origin_along_direction.js +7 -7
  430. package/src/core/geom/3d/shape/ConvexHullShape3D.d.ts +17 -0
  431. package/src/core/geom/3d/shape/ConvexHullShape3D.d.ts.map +1 -1
  432. package/src/core/geom/3d/shape/ConvexHullShape3D.js +43 -0
  433. package/src/core/geom/3d/shape/CylinderShape3D.d.ts +1 -0
  434. package/src/core/geom/3d/shape/CylinderShape3D.d.ts.map +1 -1
  435. package/src/core/geom/3d/shape/CylinderShape3D.js +21 -0
  436. package/src/core/geom/3d/shape/PointShape3D.d.ts +7 -0
  437. package/src/core/geom/3d/shape/PointShape3D.d.ts.map +1 -1
  438. package/src/core/geom/3d/shape/PointShape3D.js +88 -63
  439. package/src/core/geom/3d/shape/PosedShape3D.d.ts +6 -0
  440. package/src/core/geom/3d/shape/PosedShape3D.d.ts.map +1 -1
  441. package/src/core/geom/3d/shape/PosedShape3D.js +27 -0
  442. package/src/core/geom/3d/shape/TransformedShape3D.d.ts +10 -0
  443. package/src/core/geom/3d/shape/TransformedShape3D.d.ts.map +1 -1
  444. package/src/core/geom/3d/shape/TransformedShape3D.js +13 -0
  445. package/src/core/geom/3d/shape/Triangle3D.d.ts +1 -0
  446. package/src/core/geom/3d/shape/Triangle3D.d.ts.map +1 -1
  447. package/src/core/geom/3d/shape/Triangle3D.js +26 -0
  448. package/src/core/geom/3d/shape/UnionShape3D.d.ts +6 -0
  449. package/src/core/geom/3d/shape/UnionShape3D.d.ts.map +1 -1
  450. package/src/core/geom/3d/shape/UnionShape3D.js +7 -0
  451. package/src/core/geom/3d/shape/UnitCubeShape3D.js +45 -45
  452. package/src/core/geom/3d/shape/UnitSphereShape3D.js +44 -44
  453. package/src/core/geom/3d/shape/shape3_mesh_from_geometry.js +64 -64
  454. package/src/core/geom/3d/shape/shape_mesh_from_geometry.js +7 -7
  455. package/src/core/geom/3d/shape/util/compute_signed_distance_gradient_by_sampling.js +51 -51
  456. package/src/core/geom/3d/shape/util/shape3d_voxelize_to_grid.js +7 -7
  457. package/src/core/geom/3d/sphere/compute_bounding_sphere_of_2_spheres.js +7 -7
  458. package/src/core/geom/3d/sphere/harmonics/sh3_convolve_with_zh.js +53 -53
  459. package/src/core/geom/3d/sphere/harmonics/sh3_ggx_zh.js +30 -30
  460. package/src/core/geom/3d/sphere/harmonics/sh3_rotate_bl.js +7 -7
  461. package/src/core/geom/3d/sphere/harmonics/sh_compute_K.js +7 -7
  462. package/src/core/geom/3d/sphere/sphere_array_intersects_point.js +7 -7
  463. package/src/core/geom/3d/sphere/sphere_array_intersects_ray.js +7 -7
  464. package/src/core/geom/3d/sphere/sphere_intersects_point.js +7 -7
  465. package/src/core/geom/3d/sphere/sphere_matrix4_transform.js +5 -5
  466. package/src/core/geom/3d/sphere/sphere_projected_sphere_radius_sqr.js +7 -7
  467. package/src/core/geom/3d/sphere/sphere_radius_sqr_from_v3_array_transformed.js +7 -7
  468. package/src/core/geom/3d/sphere/spherical_from_cartesian.js +7 -7
  469. package/src/core/geom/3d/tetrahedra/compute_tetrahedral_mesh_from_surface.js +174 -174
  470. package/src/core/geom/3d/tetrahedra/compute_tetrahedron_quality.js +7 -7
  471. package/src/core/geom/3d/tetrahedra/compute_tetrahedron_volume.js +7 -7
  472. package/src/core/geom/3d/tetrahedra/delaunay/fill_in_a_cavity.js +7 -7
  473. package/src/core/geom/3d/tetrahedra/graphics/build_tetrahedral_mesh_geometry.js +39 -39
  474. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_build_vertex_to_tets_map.js +48 -48
  475. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_carve_outside_surface.js +173 -173
  476. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_compute_tet_quality.js +22 -22
  477. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_find_tets_around_edge.js +146 -146
  478. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_flip_23.js +232 -232
  479. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_flip_32.js +255 -255
  480. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_improve_quality.js +387 -387
  481. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_smooth_vertex.js +140 -140
  482. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_tet_get_neighbours.js +7 -7
  483. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_vertex_is_boundary.js +97 -97
  484. package/src/core/geom/3d/tetrahedra/tetrahedron_compute_quality.js +66 -66
  485. package/src/core/geom/3d/tetrahedra/triangle/triangle_decode_depth.js +59 -59
  486. package/src/core/geom/3d/tetrahedra/triangle/triangle_rasterized_depth_sample_index.js +54 -54
  487. package/src/core/geom/3d/tetrahedra/validate_neighbour.js +70 -70
  488. package/src/core/geom/3d/topology/struct/binary/BinaryDataBlock.js +314 -314
  489. package/src/core/geom/3d/topology/struct/binary/BinaryDataLayer.js +364 -364
  490. package/src/core/geom/3d/topology/struct/binary/BinaryElementPool.js +567 -567
  491. package/src/core/geom/3d/topology/struct/binary/BinaryTopology.js +1085 -1085
  492. package/src/core/geom/3d/topology/struct/binary/io/bt_boundary_loop.js +73 -73
  493. package/src/core/geom/3d/topology/struct/binary/io/bt_loop_kill.js +35 -35
  494. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_append.js +119 -119
  495. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_close_boundary_holes.js +84 -84
  496. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compact.js +172 -172
  497. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compute_face_normals.js +56 -56
  498. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compute_vertex_quadratics.js +7 -7
  499. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compute_vertex_quadrics.js +227 -227
  500. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_face_decouple.js +161 -161
  501. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_face_island_erode.js +490 -490
  502. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_fill_small_holes.js +120 -120
  503. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_from_indexed_geometry.js +120 -120
  504. package/src/core/geom/3d/topology/struct/binary/io/edge/bt_edge_collapse.js +52 -52
  505. package/src/core/geom/3d/topology/struct/binary/io/edge/bt_edge_create.js +115 -115
  506. package/src/core/geom/3d/topology/struct/binary/io/edge/bt_edge_flip.js +142 -142
  507. package/src/core/geom/3d/topology/struct/binary/io/edge/bt_edge_get_or_create.js +21 -21
  508. package/src/core/geom/3d/topology/struct/binary/io/edge/bt_edge_kill_parallels.js +92 -92
  509. package/src/core/geom/3d/topology/struct/binary/io/edge/bt_edge_split.js +179 -179
  510. package/src/core/geom/3d/topology/struct/binary/io/edge/bt_edge_swap_vertex_slots.js +28 -28
  511. package/src/core/geom/3d/topology/struct/binary/io/edge/bt_kill_only_edge.js +8 -8
  512. package/src/core/geom/3d/topology/struct/binary/io/edge/bt_mesh_fuse_duplicate_edges.js +83 -83
  513. package/src/core/geom/3d/topology/struct/binary/io/edge/bt_mesh_kill_short_edges.js +89 -89
  514. package/src/core/geom/3d/topology/struct/binary/io/face/bt_face_kill.js +25 -25
  515. package/src/core/geom/3d/topology/struct/binary/io/face/bt_face_poke.js +127 -127
  516. package/src/core/geom/3d/topology/struct/binary/io/face/bt_face_triangulate.js +268 -268
  517. package/src/core/geom/3d/topology/struct/binary/io/face/bt_kill_only_face.js +8 -8
  518. package/src/core/geom/3d/topology/struct/binary/io/face/bt_mesh_kill_degenerate_faces.js +72 -72
  519. package/src/core/geom/3d/topology/struct/binary/io/vertex/bt_kill_only_vert.js +7 -7
  520. package/src/core/geom/3d/topology/struct/binary/io/vertex/bt_merge_verts_by_distance.js +7 -7
  521. package/src/core/geom/3d/topology/struct/binary/io/vertex/bt_mesh_relative_merge_distance.js +48 -48
  522. package/src/core/geom/3d/topology/struct/binary/io/vertex/bt_mesh_split_pinched_vertices.js +256 -256
  523. package/src/core/geom/3d/topology/struct/binary/io/vertex/bt_mesh_vertex_merge_distance.js +77 -77
  524. package/src/core/geom/3d/topology/struct/binary/io/vertex/bt_vert_fuse_duplicate_edges.js +7 -7
  525. package/src/core/geom/3d/topology/struct/binary/io/vertex/bt_vert_kill.js +7 -7
  526. package/src/core/geom/3d/topology/struct/binary/io/vertex/bt_vertex_fuse_duplicate_edges.js +148 -148
  527. package/src/core/geom/3d/topology/struct/binary/io/vertex/bt_vertex_kill_only.js +7 -7
  528. package/src/core/geom/3d/topology/struct/binary/query/bt_collect_boundary_segments.js +29 -29
  529. package/src/core/geom/3d/topology/struct/binary/query/bt_edge_collapse_breaks_link_condition.js +107 -107
  530. package/src/core/geom/3d/topology/struct/binary/query/bt_edge_flip_is_legal.js +41 -41
  531. package/src/core/geom/3d/topology/struct/binary/query/bt_edge_flip_would_fold.js +120 -120
  532. package/src/core/geom/3d/topology/struct/binary/query/bt_edge_get.js +42 -42
  533. package/src/core/geom/3d/topology/struct/binary/query/bt_edge_is_boundary.js +20 -20
  534. package/src/core/geom/3d/topology/struct/binary/query/bt_edge_other_face.d.ts +32 -0
  535. package/src/core/geom/3d/topology/struct/binary/query/bt_edge_other_face.d.ts.map +1 -0
  536. package/src/core/geom/3d/topology/struct/binary/query/bt_edge_other_face.js +59 -0
  537. package/src/core/geom/3d/topology/struct/binary/query/bt_edge_read_diamond.js +96 -96
  538. package/src/core/geom/3d/topology/struct/binary/query/bt_face_compute_newell_normal.js +72 -72
  539. package/src/core/geom/3d/topology/struct/binary/query/bt_face_get_attached_faces.js +7 -7
  540. package/src/core/geom/3d/topology/struct/binary/query/bt_face_get_edge_adjacent_faces.js +39 -39
  541. package/src/core/geom/3d/topology/struct/binary/query/bt_face_get_edge_adjacent_faces_weighted.js +93 -93
  542. package/src/core/geom/3d/topology/struct/binary/query/bt_face_get_neighbour_faces.js +7 -7
  543. package/src/core/geom/3d/topology/struct/binary/query/bt_face_get_shared_loop.js +48 -48
  544. package/src/core/geom/3d/topology/struct/binary/query/bt_face_island_flood_fill.js +45 -45
  545. package/src/core/geom/3d/topology/struct/binary/query/bt_face_read_triangle.js +66 -66
  546. package/src/core/geom/3d/topology/struct/binary/query/bt_face_third_vertex.d.ts +26 -0
  547. package/src/core/geom/3d/topology/struct/binary/query/bt_face_third_vertex.d.ts.map +1 -0
  548. package/src/core/geom/3d/topology/struct/binary/query/bt_face_third_vertex.js +55 -0
  549. package/src/core/geom/3d/topology/struct/binary/query/bt_faces_shared_loop.js +7 -7
  550. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_assert_valid.js +44 -44
  551. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_build_boundary_euclidean_distance_field.js +32 -32
  552. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_compute_face_islands.js +53 -53
  553. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_face_find_path.js +703 -703
  554. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_sample_interior_grid_points.js +181 -181
  555. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_segment_penetrates_surface.js +133 -133
  556. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_surface_ray_parity.js +82 -82
  557. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_validate.js +599 -599
  558. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_walk_boundary_loops.js +108 -108
  559. package/src/core/geom/3d/topology/struct/binary/query/bt_query_edge_is_boundary.js +7 -7
  560. package/src/core/geom/3d/triangle/computeTriangleClosestPointToPointBarycentric.js +7 -7
  561. package/src/core/geom/3d/triangle/computeTriangleRayIntersection.js +7 -7
  562. package/src/core/geom/3d/triangle/computeTriangleRayIntersectionBarycentric.js +7 -7
  563. package/src/core/geom/3d/triangle/computeTriangleRayIntersectionBarycentricEdge.js +7 -7
  564. package/src/core/geom/3d/triangle/computeTriangleRayIntersectionBarycentricGeometry.js +7 -7
  565. package/src/core/geom/3d/triangle/compute_triangle_area_3d.js +7 -7
  566. package/src/core/geom/3d/triangle/compute_triangle_normal.js +7 -7
  567. package/src/core/geom/3d/triangle/rayTriangleIntersection.js +7 -7
  568. package/src/core/geom/3d/triangle/tri3_closest_point.js +65 -65
  569. package/src/core/geom/3d/triangle/tri3_closest_point_barycentric.js +171 -171
  570. package/src/core/geom/3d/triangle/tri3_flatten_isometric.js +79 -79
  571. package/src/core/geom/3d/triangle/tri3_ray_intersection.js +160 -160
  572. package/src/core/geom/3d/triangle/tri3_ray_intersection_barycentric.js +96 -96
  573. package/src/core/geom/3d/triangle/tri3_raycast.d.ts +42 -0
  574. package/src/core/geom/3d/triangle/tri3_raycast.d.ts.map +1 -0
  575. package/src/core/geom/3d/triangle/tri3_raycast.js +94 -0
  576. package/src/core/geom/3d/triangle/triangle3_incenter.js +7 -7
  577. package/src/core/geom/3d/triangle/triangle_compute_plane_side.js +7 -7
  578. package/src/core/geom/3d/triangle/triangle_compute_signed_volume.js +7 -7
  579. package/src/core/geom/3d/triangle/triangle_intersects_clipping_volume.js +7 -7
  580. package/src/core/geom/3d/triangle/triangle_mesh_compute_signed_volume.js +7 -7
  581. package/src/core/geom/3d/triangle/v3_compute_triangle_normal.js +7 -7
  582. package/src/core/geom/3d/util/make_justified_point_grid.js +7 -7
  583. package/src/core/geom/GEOMETRY_REVIEW_002_API_ISSUES.md +381 -381
  584. package/src/core/geom/GEOMETRY_REVIEW_002_NAMING.md +684 -684
  585. package/src/core/geom/MANIFOLD_COMPARISON_2026_08_01.md +554 -554
  586. package/src/core/geom/mat3/m3_cm_from_translation.js +7 -7
  587. package/src/core/geom/mat3/m3_cm_invert.js +7 -7
  588. package/src/core/geom/mat3/m3_make_rotation.js +5 -5
  589. package/src/core/geom/mat3/m3_make_rotation_TBN.js +31 -31
  590. package/src/core/geom/mat3/m3_multiply_vec3.js +7 -7
  591. package/src/core/geom/packing/computeBoundingSphereOfSpheres.js +7 -7
  592. package/src/core/geom/packing/max-rect/cost/costByBestShortSide.js +7 -7
  593. package/src/core/geom/packing/max-rect/cost/costByRemainingArea.js +7 -7
  594. package/src/core/geom/packing/max-rect/cutArea.js +7 -7
  595. package/src/core/geom/packing/max-rect/findBestContainer.js +7 -7
  596. package/src/core/geom/packing/max-rect/packMaxRectangles.js +7 -7
  597. package/src/core/geom/packing/max-rect/packOneBox.js +7 -7
  598. package/src/core/geom/packing/max-rect/removeRedundantBoxes.js +7 -7
  599. package/src/core/geom/packing/max-rect/removeRedundantBoxesArray.js +7 -7
  600. package/src/core/geom/random/randomPointInBox.js +7 -7
  601. package/src/core/geom/random/randomPointInCircle.js +7 -7
  602. package/src/core/geom/random/randomPointInPoint.js +7 -7
  603. package/src/core/geom/random/randomPointInSphere.js +7 -7
  604. package/src/core/geom/random/randomPointOnBox.js +7 -7
  605. package/src/core/geom/random/randomPointOnSphere.js +7 -7
  606. package/src/core/geom/random/random_orthonormal_basis.js +31 -31
  607. package/src/core/geom/vec/vector_axpy.js +15 -15
  608. package/src/core/geom/vec/vector_axpy_offset.js +20 -20
  609. package/src/core/geom/vec/vector_copy.js +7 -7
  610. package/src/core/geom/vec/vector_dot_offset.js +25 -25
  611. package/src/core/geom/vec/vector_scale_array.js +7 -7
  612. package/src/core/geom/vec2/v2_rotate.js +21 -21
  613. package/src/core/geom/vec2/v2_rotate_cos_sin.js +21 -21
  614. package/src/core/geom/vec3/v3_angle_atan2_between.js +7 -7
  615. package/src/core/geom/vec3/v3_angle_between.js +7 -7
  616. package/src/core/geom/vec3/v3_angle_cos_between.js +7 -7
  617. package/src/core/geom/vec3/v3_array_add_in_place.js +7 -7
  618. package/src/core/geom/vec3/v3_array_centroid.js +7 -7
  619. package/src/core/geom/vec3/v3_array_copy.js +7 -7
  620. package/src/core/geom/vec3/v3_array_displace_in_direction.js +7 -7
  621. package/src/core/geom/vec3/v3_array_equals.js +7 -7
  622. package/src/core/geom/vec3/v3_array_immediate_add.js +7 -7
  623. package/src/core/geom/vec3/v3_array_matrix4_rotate.js +7 -7
  624. package/src/core/geom/vec3/v3_array_normalize.js +7 -7
  625. package/src/core/geom/vec3/v3_array_scale.js +7 -7
  626. package/src/core/geom/vec3/v3_compute_interior_angle.js +7 -7
  627. package/src/core/geom/vec3/v3_cotangent.js +33 -33
  628. package/src/core/geom/vec3/v3_dot_array_array.js +7 -7
  629. package/src/core/geom/vec3/v3_matrix3_multiply.js +30 -30
  630. package/src/core/geom/vec3/v3_matrix3_rotate.js +49 -49
  631. package/src/core/geom/vec3/v3_matrix4_rotate.js +7 -7
  632. package/src/core/geom/vec3/v3_multiply.js +23 -23
  633. package/src/core/geom/vec3/v3_multiply_scalar.js +7 -7
  634. package/src/core/geom/vec3/v3_orthonormal_matrix_from_normal.js +49 -49
  635. package/src/core/geom/vec3/v3_quat3_apply.js +7 -7
  636. package/src/core/geom/vec3/v3_quat3_apply_inverse.js +7 -7
  637. package/src/core/geom/vec3/v3_quaternion_apply.js +39 -39
  638. package/src/core/geom/vec3/v3_quaternion_apply_inverse.js +41 -41
  639. package/src/core/geom/vec3/v3_reflect.js +24 -24
  640. package/src/core/geom/vec3/v3_scale_array.js +18 -18
  641. package/src/core/geom/vec3/v3_triple_cross.js +45 -45
  642. package/src/core/geom/vec3/v3_triple_cross_product.js +7 -7
  643. package/src/core/geom/vec3/v3_uniform_sample_cone.js +21 -21
  644. package/src/core/geom/vec4/v4_multiply_mat4.js +7 -7
  645. package/src/core/graph/csr/csr_graph_build_distance_field.js +112 -112
  646. package/src/core/graph/csr/csr_graph_find_edge.js +25 -25
  647. package/src/core/graph/csr/csr_graph_find_shortest_path.js +105 -105
  648. package/src/core/graph/csr/csr_graph_from_undirected_edge_list.js +94 -94
  649. package/src/core/graph/graph_k_means_cluster.js +250 -250
  650. package/src/core/graph/layout/CircleLayout.js +1 -1
  651. package/src/core/graph/layout/box/BoxLayouter.js +1 -1
  652. package/src/core/graph/layout/graph_peel_topological_layer.js +45 -45
  653. package/src/core/graph/metis/BinaryTopologyFaceGraph.js +49 -49
  654. package/src/core/graph/metis/bt_mesh_build_face_graph.js +111 -111
  655. package/src/core/graph/metis/cluster_mesh_metis.js +137 -137
  656. package/src/core/graph/metis/native/bisection/bisect_graph.js +100 -100
  657. package/src/core/graph/metis/native/bisection/compute_2way_params.js +86 -86
  658. package/src/core/graph/metis/native/bisection/fm_2way.js +297 -297
  659. package/src/core/graph/metis/native/bisection/grow_bisection.js +137 -137
  660. package/src/core/graph/metis/native/bisection/split_graph_two_way.js +119 -119
  661. package/src/core/graph/metis/native/coarsen/coarsen_graph.js +94 -94
  662. package/src/core/graph/metis/native/coarsen/create_coarse_graph.js +158 -158
  663. package/src/core/graph/metis/native/coarsen/match_shem.js +175 -175
  664. package/src/core/graph/metis/native/initial/initial_kway_bfs.js +122 -122
  665. package/src/core/graph/metis/native/initial/initial_kway_recursive_bisection.js +170 -170
  666. package/src/core/graph/metis/native/metis_partition_kway.js +126 -126
  667. package/src/core/graph/metis/native/refine/RefinementScratch.js +53 -53
  668. package/src/core/graph/metis/native/refine/fm_kway.js +487 -487
  669. package/src/core/graph/metis/native/refine/project_kway.js +43 -43
  670. package/src/core/graph/metis/native/refine/refine_kway.js +43 -43
  671. package/src/core/lang/reactive/pegjs/parser.js +3458 -3458
  672. package/src/core/math/complex/complex_horner_eval.js +31 -31
  673. package/src/core/math/computeWholeDivisorLow.js +33 -33
  674. package/src/core/math/f32_next_down.js +24 -24
  675. package/src/core/math/f32_next_up.js +27 -27
  676. package/src/core/math/hash/hash_mix2.js +22 -22
  677. package/src/core/math/hash/hash_mix3.js +19 -19
  678. package/src/core/math/linalg/cg/cg_solve.js +106 -106
  679. package/src/core/math/linalg/cg/cg_solve_normal_equations.js +59 -59
  680. package/src/core/math/linalg/cubic_residual_times_derivative_accumulate.js +43 -43
  681. package/src/core/math/linalg/eigen/matrix_householder_in_place.js +122 -122
  682. package/src/core/math/linalg/eigen/matrix_qr_in_place.js +190 -190
  683. package/src/core/math/linalg/eigen/matrix_symmetric_3x3_eigen.js +117 -117
  684. package/src/core/math/linalg/givens/givens_apply_rows.js +28 -28
  685. package/src/core/math/linalg/givens/givens_apply_rows_offset.js +30 -30
  686. package/src/core/math/linalg/givens/givens_rotation_coefficients.js +39 -39
  687. package/src/core/math/linalg/lu_factor_linear_system.js +108 -108
  688. package/src/core/math/linalg/lu_solve_linear_system.js +53 -53
  689. package/src/core/math/linalg/polynomial_add_into.js +29 -29
  690. package/src/core/math/linalg/polynomial_complex_roots_aberth_ehrlich.js +159 -159
  691. package/src/core/math/linalg/polynomial_cubic_derivative_eval.js +16 -16
  692. package/src/core/math/linalg/polynomial_cubic_horner_eval.js +19 -19
  693. package/src/core/math/linalg/polynomial_cubic_second_derivative_eval.js +14 -14
  694. package/src/core/math/linalg/polynomial_multiply.js +41 -41
  695. package/src/core/math/linalg/polynomial_real_roots_in_interval.js +211 -211
  696. package/src/core/math/linalg/polynomial_root_bound_cauchy.js +30 -30
  697. package/src/core/math/linalg/polynomial_scale_into.js +17 -17
  698. package/src/core/math/linalg/polynomial_sub_into.js +29 -29
  699. package/src/core/math/linalg/solve_linear_system_GEPP_2x2.js +96 -96
  700. package/src/core/math/linalg/sor_optimal_omega.js +42 -42
  701. package/src/core/math/linalg/sparse/SparseMatrixCSR.js +88 -88
  702. package/src/core/math/linalg/sparse/spm_matvec.js +22 -22
  703. package/src/core/math/linalg/sparse/spm_matvec_transpose.js +29 -29
  704. package/src/core/math/lookup/LUT_HEATMAP.js +30 -30
  705. package/src/core/math/lookup/ParameterLookupTable.js +495 -495
  706. package/src/core/math/lookup/ParameterLookupTableFlags.js +6 -6
  707. package/src/core/math/noise/curl_noise_3d.js +18 -18
  708. package/src/core/math/physics/brdf/cone_cosine_from_roughness.js +28 -28
  709. package/src/core/math/physics/brdf/reflection_sample_weight.js +48 -48
  710. package/src/core/math/physics/kinematics/computeInterceptPoint.js +79 -79
  711. package/src/core/math/random/randomSeed.js +16 -16
  712. package/src/core/math/random/random_pick_weighted_index.js +26 -26
  713. package/src/core/math/round_half_away_from_zero.js +13 -13
  714. package/src/core/math/spline/spline3_hermite_apply_transform.js +107 -107
  715. package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite_1d.js +125 -125
  716. package/src/core/math/spline/spline3_hermite_intersects_spline3_hermite.js +50 -50
  717. package/src/core/math/spline/spline3_hermite_reverse.js +49 -49
  718. package/src/core/math/spline/spline3_hermite_to_monomial.js +37 -37
  719. package/src/core/math/statistics/hammersley_sequence.js +53 -53
  720. package/src/core/model/reactive/model/util/createRandomReactiveExpression.js +185 -185
  721. package/src/core/path/convertPathToURL.js +107 -107
  722. package/src/core/process/delay.js +16 -16
  723. package/src/core/science/units/MEASUREMENT_UNITS_SCIENTIFIC.js +87 -87
  724. package/src/core/science/units/MEASUREMENT_UNITS_SI.js +17 -17
  725. package/src/core/science/units/NamedUnit.js +48 -48
  726. package/src/core/science/units/UNIT_DIMENSION_MAPPING.js +29 -29
  727. package/src/core/science/units/UnitDimension.js +28 -28
  728. package/src/core/science/units/format_quantity.js +74 -74
  729. package/src/core/science/units/magnitude_prefix.js +127 -127
  730. package/src/core/science/units/si/AMPERE.js +13 -13
  731. package/src/core/science/units/si/CANDELA.js +13 -13
  732. package/src/core/science/units/si/DIMENSIONLESS.js +14 -14
  733. package/src/core/science/units/si/KELVIN.js +13 -13
  734. package/src/core/science/units/si/KILOGRAM.js +13 -13
  735. package/src/core/science/units/si/METER.js +13 -13
  736. package/src/core/science/units/si/MOLE.js +13 -13
  737. package/src/core/science/units/si/SECOND.js +13 -13
  738. package/src/core/science/units/si/derived/BECQUEREL.js +16 -16
  739. package/src/core/science/units/si/derived/COULOMB.js +13 -13
  740. package/src/core/science/units/si/derived/FARAD.js +13 -13
  741. package/src/core/science/units/si/derived/GRAY.js +16 -16
  742. package/src/core/science/units/si/derived/HENRY.js +13 -13
  743. package/src/core/science/units/si/derived/HERTZ.js +16 -16
  744. package/src/core/science/units/si/derived/JOULE.js +13 -13
  745. package/src/core/science/units/si/derived/KATAL.js +13 -13
  746. package/src/core/science/units/si/derived/LUMEN.js +14 -14
  747. package/src/core/science/units/si/derived/LUX.js +13 -13
  748. package/src/core/science/units/si/derived/NEWTON.js +13 -13
  749. package/src/core/science/units/si/derived/OHM.js +13 -13
  750. package/src/core/science/units/si/derived/PASCAL.js +13 -13
  751. package/src/core/science/units/si/derived/SIEMENS.js +13 -13
  752. package/src/core/science/units/si/derived/SIEVERT.js +16 -16
  753. package/src/core/science/units/si/derived/TESLA.js +13 -13
  754. package/src/core/science/units/si/derived/VOLT.js +13 -13
  755. package/src/core/science/units/si/derived/WATT.js +13 -13
  756. package/src/core/science/units/si/derived/WEBER.js +13 -13
  757. package/src/core/science/units/unit_matrix_to_string.js +403 -403
  758. package/src/engine/Engine.js +726 -726
  759. package/src/engine/EngineHarness.js +590 -590
  760. package/src/engine/REVIEW_2026_07_03.md +211 -211
  761. package/src/engine/animation/AnimatedActions.js +81 -81
  762. package/src/engine/animation/AnimationUtils.js +252 -252
  763. package/src/engine/animation/async/TimeSeries.js +300 -300
  764. package/src/engine/animation/curve/animation_curve_binary_codec.js +60 -60
  765. package/src/engine/animation/curve/draw/canvas_point_to_curve.js +22 -22
  766. package/src/engine/animation/curve/draw/curve_point_to_canvas.js +38 -38
  767. package/src/engine/animation/curve/draw/position_canvas_to_curve.js +22 -22
  768. package/src/engine/animation/curve/draw/position_curve_to_canvas.js +22 -22
  769. package/src/engine/asset/GameAssetType.d.ts +0 -1
  770. package/src/engine/asset/GameAssetType.js +0 -1
  771. package/src/engine/asset/load_model_scene_bundle.js +27 -27
  772. package/src/engine/asset/loaders/font/FontAsset.js +21 -21
  773. package/src/engine/asset/loaders/font/FontAssetLoader.js +20 -20
  774. package/src/engine/asset/loaders/gltf_test_fixtures.js +358 -358
  775. package/src/engine/asset/loaders/image/ImageBitmapAssetLoader.js +52 -52
  776. package/src/engine/asset/loaders/image/ImageRGBADataLoader.js +94 -94
  777. package/src/engine/asset/loaders/image/png/filter/png_filter_unFilterAverage.js +59 -59
  778. package/src/engine/asset/loaders/image/png/filter/png_filter_unFilterNone.js +55 -55
  779. package/src/engine/asset/loaders/image/png/filter/png_filter_unFilterPaeth.js +74 -74
  780. package/src/engine/asset/loaders/image/png/filter/png_filter_unFilterSub.js +34 -34
  781. package/src/engine/asset/loaders/image/png/filter/png_filter_unFilterUp.js +46 -46
  782. package/src/engine/control/first-person/DESIGN.md +640 -640
  783. package/src/engine/control/first-person/DESIGN_EXTENSIONS.md +563 -563
  784. package/src/engine/control/first-person/FirstPersonMotionPhase.js +134 -134
  785. package/src/engine/control/first-person/FirstPersonPlayerControllerSystem.js +2045 -2045
  786. package/src/engine/control/first-person/mastery/DecisionPoint.js +30 -30
  787. package/src/engine/control/first-person/math/Spring.js +71 -71
  788. package/src/engine/control/first-person/math/computeJumpFromApex.js +23 -23
  789. package/src/engine/control/first-person/math/computeLRCBreathRate.js +41 -41
  790. package/src/engine/control/first-person/math/computeMassRatios.js +44 -44
  791. package/src/engine/control/first-person/math/criticallyDampedSpring.js +34 -34
  792. package/src/engine/control/first-person/math/dampedSpringStep.js +72 -72
  793. package/src/engine/control/first-person/math/jumpDynamics.js +108 -108
  794. package/src/engine/control/first-person/math/stepTowards.js +20 -20
  795. package/src/engine/control/first-person/pose/FirstPersonPose.js +137 -137
  796. package/src/engine/control/first-person/pose/FirstPersonPosture.js +27 -27
  797. package/src/engine/control/first-person/test/buildTestPlayer.js +36 -36
  798. package/src/engine/ecs/ASYNC_SYSTEMS_PROPOSAL.md +531 -531
  799. package/src/engine/ecs/EntityManager.d.ts +7 -3
  800. package/src/engine/ecs/EntityManager.d.ts.map +1 -1
  801. package/src/engine/ecs/EntityManager.js +10 -5
  802. package/src/engine/ecs/HIERARCHY_REVIEW.md +717 -717
  803. package/src/engine/ecs/async/SystemWorkerHost.js +329 -329
  804. package/src/engine/ecs/async/SystemWorkerLoopback.js +143 -143
  805. package/src/engine/ecs/async/WORKER_SYSTEM_PLAN.md +246 -246
  806. package/src/engine/ecs/async/WorkerSystem.js +702 -702
  807. package/src/engine/ecs/async/WorkerSystemProtocol.js +150 -150
  808. package/src/engine/ecs/fow/FogOfWar.js +707 -707
  809. package/src/engine/ecs/fow/FogOfWarEdgeMode.js +40 -40
  810. package/src/engine/ecs/fow/fog_of_war_mask_world_bounds.js +24 -24
  811. package/src/engine/ecs/fow/fog_of_war_uv_transform.js +30 -30
  812. package/src/engine/ecs/gui/hud/testHudEdgeStick.js +132 -134
  813. package/src/engine/ecs/hierarchy/EntityChildIndex.js +140 -140
  814. package/src/engine/ecs/ik/IKProblem.js +42 -42
  815. package/src/engine/ecs/ik/InverseKinematicsSystem.js +193 -193
  816. package/src/engine/ecs/ik/OneBoneSurfaceAlignmentSolver.js +134 -134
  817. package/src/engine/ecs/ik/TwoBoneInverseKinematicsSolver.js +97 -97
  818. package/src/engine/ecs/ik/ik_bone_chain.js +85 -85
  819. package/src/engine/ecs/ik/ik_probe_terrain_contact.js +119 -119
  820. package/src/engine/ecs/parent/EntityNode.d.ts +1 -1
  821. package/src/engine/ecs/parent/EntityNode.js +2 -2
  822. package/src/engine/ecs/parent/ParentEntitySystem.js +255 -255
  823. package/src/engine/ecs/speaker/VoiceSystem.d.ts +2 -2
  824. package/src/engine/ecs/speaker/VoiceSystem.d.ts.map +1 -1
  825. package/src/engine/ecs/speaker/VoiceSystem.js +13 -7
  826. package/src/engine/ecs/storage/binary/SERIALIZATION_COVERAGE.md +269 -269
  827. package/src/engine/ecs/storage/binary/object-v2/BinaryObjectDeSerializer.js +315 -315
  828. package/src/engine/ecs/storage/binary/object-v2/BinaryObjectSerializer.js +331 -331
  829. package/src/engine/ecs/storage/binary/object-v2/DESIGN.md +199 -199
  830. package/src/engine/ecs/storage/binary/object-v2/ObjectBinaryFormat.js +57 -57
  831. package/src/engine/ecs/storage/binary/object-v2/ObjectSerializationAdapter.js +56 -56
  832. package/src/engine/ecs/storage/binary/object-v2/README.md +110 -110
  833. package/src/engine/ecs/storage/binary/object-v2/objectStringCodec.js +48 -48
  834. package/src/engine/ecs/storage/binary/object-v2/serializerTestHarness.js +44 -44
  835. package/src/engine/ecs/storage/populateEngineSerializationRegistry.d.ts.map +1 -1
  836. package/src/engine/ecs/storage/populateEngineSerializationRegistry.js +5 -1
  837. package/src/engine/ecs/terrain/TerrainClouds.js +97 -97
  838. package/src/engine/ecs/terrain/ecs/StubTerrainBuildWorker.js +65 -65
  839. package/src/engine/ecs/terrain/ecs/Terrain.d.ts +4 -0
  840. package/src/engine/ecs/terrain/ecs/Terrain.d.ts.map +1 -1
  841. package/src/engine/ecs/terrain/ecs/Terrain.js +790 -790
  842. package/src/engine/ecs/terrain/ecs/TerrainSystem.js +209 -209
  843. package/src/engine/ecs/terrain/ecs/cling/ClingToTerrainSystem.js +316 -316
  844. package/src/engine/ecs/terrain/ecs/layers/TerrainLayers.js +416 -416
  845. package/src/engine/ecs/terrain/ecs/splat/SplatMapping.js +525 -525
  846. package/src/engine/ecs/terrain/overlay/TerrainOverlay.js +302 -302
  847. package/src/engine/ecs/terrain/tiles/TerrainTile.js +561 -561
  848. package/src/engine/ecs/terrain/tiles/TerrainTileManager.js +896 -896
  849. package/src/engine/ecs/terrain/tiles/tile_geometry_to_meshlets.js +36 -36
  850. package/src/engine/ecs/terrain/util/loadVisibleTerrainTiles.js +129 -129
  851. package/src/engine/ecs/transform/Transform64.js +814 -814
  852. package/src/engine/ecs/transform/t64_copy_from_transform.js +35 -35
  853. package/src/engine/ecs/transform/t64_equals_transform.js +55 -55
  854. package/src/engine/ecs/transform/t64_look_rotation.js +36 -36
  855. package/src/engine/ecs/transform/t64_transform_direction.js +25 -25
  856. package/src/engine/ecs/transform/transform_copy_from_t64.js +28 -28
  857. package/src/engine/ecs/transform-attachment/TransformAttachmentSystem.js +345 -345
  858. package/src/engine/ecs/transform-attachment/transform_attachment_find_descendant_by_name.js +65 -65
  859. package/src/engine/ecs/transform-attachment/transform_attachment_parent_of.js +24 -24
  860. package/src/engine/ecs/util/hideEntityGracefully.js +208 -208
  861. package/src/engine/graphics/CONTEXT_LOSS_RECOVERY_PLAN.md +446 -446
  862. package/src/engine/graphics/FULL_SCREEN_QUAD_VERTEX_SHADER.js +14 -14
  863. package/src/engine/graphics/FrameThrottle.js +2 -2
  864. package/src/engine/graphics/ecs/animation/animator/graph/AnimationState.js +207 -207
  865. package/src/engine/graphics/ecs/animation/animator/graph/AnimationTransition.js +151 -151
  866. package/src/engine/graphics/ecs/animation/animator/graph/definition/serialization/AnimationGraphDefinitionAssetLoader.js +22 -22
  867. package/src/engine/graphics/ecs/camera/Camera.js +165 -165
  868. package/src/engine/graphics/ecs/camera/camera_active_frustum.js +25 -25
  869. package/src/engine/graphics/ecs/camera/camera_find_active.js +28 -28
  870. package/src/engine/graphics/ecs/camera/camera_traverse_active.js +18 -18
  871. package/src/engine/graphics/ecs/camera/pp/PerfectPanner.js +170 -170
  872. package/src/engine/graphics/ecs/camera/serialization/CameraSerializationAdapter.js +68 -68
  873. package/src/engine/graphics/ecs/camera/topdown/TopDownCameraController.js +296 -296
  874. package/src/engine/graphics/ecs/decal/v2/Decal.js +220 -220
  875. package/src/engine/graphics/ecs/decal/v2/DecalSerializationAdapter.js +56 -56
  876. package/src/engine/graphics/ecs/decal/v2/DecalSerializationUpgrader_0_1.js +38 -38
  877. package/src/engine/graphics/ecs/light/shadow/compute_view_frustum_aabb_in_space.js +62 -62
  878. package/src/engine/graphics/ecs/light/shadow/extend_shadow_camera_near_for_casters.js +77 -77
  879. package/src/engine/graphics/ecs/make_bvh_depth_range_computer.js +33 -33
  880. package/src/engine/graphics/ecs/mesh-v2/DeferredBoundsQueue.js +88 -88
  881. package/src/engine/graphics/ecs/mesh-v2/ShadedGeometry.js +428 -428
  882. package/src/engine/graphics/ecs/mesh-v2/aggregate/SGMesh.js +303 -303
  883. package/src/engine/graphics/ecs/mesh-v2/aggregate/SGMeshSystem.d.ts +1 -1
  884. package/src/engine/graphics/ecs/mesh-v2/aggregate/serialization/SGMeshSerializationAdapter.js +39 -39
  885. package/src/engine/graphics/ecs/mesh-v2/sg_hierarchy_compute_bounding_box_via_parent_entity.js +36 -36
  886. package/src/engine/graphics/ecs/path/tube/build/GeometryStream.js +94 -94
  887. package/src/engine/graphics/ecs/path/tube/build/build_stream_catmullrom.js +310 -310
  888. package/src/engine/graphics/ecs/path/tube/build/build_stream_linear.js +73 -73
  889. package/src/engine/graphics/ecs/path/tube/build/computeFrenetFrames.js +199 -199
  890. package/src/engine/graphics/ecs/path/tube/build/makeTubeStream.js +217 -217
  891. package/src/engine/graphics/ecs/path/tube/build/make_cap.js +340 -340
  892. package/src/engine/graphics/ecs/path/tube/build/make_ring_faces.js +40 -40
  893. package/src/engine/graphics/ecs/path/tube/build/make_ring_vertices.js +153 -153
  894. package/src/engine/graphics/ecs/render_layers_compute_depth_range.js +64 -64
  895. package/src/engine/graphics/ecs/trail3d/Trail3D.js +483 -483
  896. package/src/engine/graphics/ecs/trail3d/Trail3DFlags.js +26 -26
  897. package/src/engine/graphics/ecs/trail3d/advance_trail.js +42 -42
  898. package/src/engine/graphics/ecs/trail3d/make_gradient_stroke.js +58 -58
  899. package/src/engine/graphics/ecs/trail3d/seed_trail_stroke.js +90 -90
  900. package/src/engine/graphics/ecs/trail3d/seed_trail_tube.js +75 -75
  901. package/src/engine/graphics/ecs/trail3d/trail_head_position.js +19 -19
  902. package/src/engine/graphics/ecs/water/Water.js +104 -104
  903. package/src/engine/graphics/geometry/AttributeData.js +84 -84
  904. package/src/engine/graphics/geometry/buffered/geometry_compute_vertex_normals_indexed.js +49 -49
  905. package/src/engine/graphics/geometry/buffered/query/GeometrySpatialQueryAccelerator.js +288 -288
  906. package/src/engine/graphics/geometry/buffered/query/nearest_point_on_triangle_record.js +87 -87
  907. package/src/engine/graphics/geometry/buffered/read_triangle_vertices.js +102 -102
  908. package/src/engine/graphics/geometry/bvh/buffered/bvh32_from_buffered_geometry.js +52 -52
  909. package/src/engine/graphics/geometry/bvh/buffered/bvh32_from_indexed_geometry.js +20 -20
  910. package/src/engine/graphics/geometry/bvh/buffered/bvh32_from_unindexed_geometry.js +19 -19
  911. package/src/engine/graphics/geometry/optimization/geometry_optimize_uv_tension.js +135 -135
  912. package/src/engine/graphics/impostors/voxel/README.md +149 -149
  913. package/src/engine/graphics/impostors/voxel/VoxelImpostorDescription.js +141 -141
  914. package/src/engine/graphics/particles/node-based/codegen/glsl/getTypeByteSize.js +20 -20
  915. package/src/engine/graphics/particles/node-based/nodes/FloatConstant.d.ts +2 -0
  916. package/src/engine/graphics/particles/node-based/nodes/FloatConstant.d.ts.map +1 -1
  917. package/src/engine/graphics/particles/node-based/nodes/ReadFloatUniform.d.ts +2 -0
  918. package/src/engine/graphics/particles/node-based/nodes/ReadFloatUniform.d.ts.map +1 -1
  919. package/src/engine/graphics/particles/node-based/nodes/ReadVelocity.d.ts +2 -0
  920. package/src/engine/graphics/particles/node-based/nodes/ReadVelocity.d.ts.map +1 -1
  921. package/src/engine/graphics/particles/node-based/nodes/ShaderNode.d.ts +1 -2
  922. package/src/engine/graphics/particles/node-based/nodes/ShaderNode.d.ts.map +1 -1
  923. package/src/engine/graphics/particles/node-based/nodes/Vector3Constant.d.ts +2 -0
  924. package/src/engine/graphics/particles/node-based/nodes/Vector3Constant.d.ts.map +1 -1
  925. package/src/engine/graphics/particles/node-based/nodes/Vector3Merge.d.ts +2 -0
  926. package/src/engine/graphics/particles/node-based/nodes/Vector3Merge.d.ts.map +1 -1
  927. package/src/engine/graphics/particles/node-based/nodes/Vector3Split.d.ts +2 -0
  928. package/src/engine/graphics/particles/node-based/nodes/Vector3Split.d.ts.map +1 -1
  929. package/src/engine/graphics/particles/node-based/nodes/WriteVelocity.d.ts +2 -0
  930. package/src/engine/graphics/particles/node-based/nodes/WriteVelocity.d.ts.map +1 -1
  931. package/src/engine/graphics/particles/node-based/nodes/attribute/ReadVector3Attribute.d.ts +2 -0
  932. package/src/engine/graphics/particles/node-based/nodes/attribute/ReadVector3Attribute.d.ts.map +1 -1
  933. package/src/engine/graphics/particles/node-based/nodes/attribute/WriteVector3Attribute.d.ts +2 -0
  934. package/src/engine/graphics/particles/node-based/nodes/attribute/WriteVector3Attribute.d.ts.map +1 -1
  935. package/src/engine/graphics/particles/node-based/nodes/math/AddFloatNode.d.ts +2 -0
  936. package/src/engine/graphics/particles/node-based/nodes/math/AddFloatNode.d.ts.map +1 -1
  937. package/src/engine/graphics/particles/node-based/nodes/math/Vector3Add.d.ts +2 -0
  938. package/src/engine/graphics/particles/node-based/nodes/math/Vector3Add.d.ts.map +1 -1
  939. package/src/engine/graphics/particles/node-based/nodes/math/Vector3Divide.d.ts +2 -0
  940. package/src/engine/graphics/particles/node-based/nodes/math/Vector3Divide.d.ts.map +1 -1
  941. package/src/engine/graphics/particles/node-based/nodes/math/Vector3Multiply.d.ts +2 -0
  942. package/src/engine/graphics/particles/node-based/nodes/math/Vector3Multiply.d.ts.map +1 -1
  943. package/src/engine/graphics/particles/node-based/nodes/math/Vector3Subtract.d.ts +2 -0
  944. package/src/engine/graphics/particles/node-based/nodes/math/Vector3Subtract.d.ts.map +1 -1
  945. package/src/engine/graphics/particles/node-based/nodes/noise/CurlNoiseNode.d.ts +2 -0
  946. package/src/engine/graphics/particles/node-based/nodes/noise/CurlNoiseNode.d.ts.map +1 -1
  947. package/src/engine/graphics/particles/node-based/nodes/particle_data_type_component_count.js +45 -45
  948. package/src/engine/graphics/particles/particular/engine/ParticularEngine.js +229 -229
  949. package/src/engine/graphics/particles/particular/engine/emitter/ParticleEmitter.d.ts +2 -10
  950. package/src/engine/graphics/particles/particular/engine/emitter/ParticleEmitter.d.ts.map +1 -1
  951. package/src/engine/graphics/particles/particular/engine/emitter/ParticleEmitter.js +1149 -1149
  952. package/src/engine/graphics/particles/particular/engine/emitter/ParticlePool.js +472 -472
  953. package/src/engine/graphics/particles/particular/engine/parameter/ParameterLookupTable.js +2 -2
  954. package/src/engine/graphics/particles/particular/engine/parameter/ParameterLookupTableFlags.js +2 -2
  955. package/src/engine/graphics/particles/particular/engine/parameter/ParameterSet.d.ts.map +1 -1
  956. package/src/engine/graphics/particles/particular/engine/parameter/ParameterSet.js +3 -5
  957. package/src/engine/graphics/particles/particular/engine/shader/ShaderManager.js +135 -135
  958. package/src/engine/graphics/particles/particular/engine/utils/distribute_points_on_indexed_triangles.js +168 -168
  959. package/src/engine/graphics/render/forward_plus/plugin/forwardPlusMaterialMarker.js +18 -18
  960. package/src/engine/graphics/render/forward_plus/read_frustum_corner.js +2 -2
  961. package/src/engine/graphics/render/frame_graph/FRAME_GRAPH_RECOMMENDATIONS.md +219 -219
  962. package/src/engine/graphics/render/frame_graph/FrameGraph.js +900 -900
  963. package/src/engine/graphics/render/frame_graph/FrameGraphScope.js +72 -72
  964. package/src/engine/graphics/render/frame_graph/FrameGraphState.js +39 -39
  965. package/src/engine/graphics/render/frame_graph/ResourceEntry.js +96 -96
  966. package/src/engine/graphics/render/gizmo/GizmoShapeBuffer.js +105 -105
  967. package/src/engine/graphics/render/gizmo/GizmoShapeRenderingInterface.js +162 -162
  968. package/src/engine/graphics/render/layers/RenderLayer.js +95 -95
  969. package/src/engine/graphics/sh3/build_injected_shader.js +56 -56
  970. package/src/engine/graphics/sh3/grid_dilate_average.js +175 -175
  971. package/src/engine/graphics/sh3/path_tracer/BufferedGeometryBVH.js +506 -506
  972. package/src/engine/graphics/sh3/path_tracer/PathTracedMesh.js +161 -161
  973. package/src/engine/graphics/sh3/path_tracer/PathTracedRenderer.js +205 -205
  974. package/src/engine/graphics/sh3/path_tracer/PathTracedScene.js +698 -698
  975. package/src/engine/graphics/sh3/path_tracer/REVIEW_001_ACTION_PLAN.md +490 -490
  976. package/src/engine/graphics/sh3/path_tracer/geometry/compute_triangle_group_aabb3.js +2 -2
  977. package/src/engine/graphics/sh3/path_tracer/make_sky_hosek.js +45 -45
  978. package/src/engine/graphics/sh3/path_tracer/populate_path_traced_scene_from_ecd.js +73 -73
  979. package/src/engine/graphics/sh3/path_tracer/ray_hit_apply_transform.js +32 -32
  980. package/src/engine/graphics/sh3/path_tracer/texture/apply_texture_clamping_to_coordinate.js +28 -28
  981. package/src/engine/graphics/sh3/path_tracer/texture/sample_material.js +142 -142
  982. package/src/engine/graphics/sh3/sky/hosek/render_hosek_sky_to_equirectangular.js +70 -70
  983. package/src/engine/graphics/sh3/sky/hosek/sky_hosek_compute_irradiance_by_direction.js +388 -388
  984. package/src/engine/graphics/shaders/chunks/glsl_octahedral_impostor.js +277 -277
  985. package/src/engine/graphics/shaders/chunks/glsl_voxel_impostor_splat.js +57 -57
  986. package/src/engine/graphics/shadows/vsm.glsl.js +43 -43
  987. package/src/engine/graphics/texture/3d/scs3d_sample_linear3.js +81 -81
  988. package/src/engine/graphics/texture/EnvironmentTextureProjection.js +15 -15
  989. package/src/engine/graphics/texture/channel_count_to_format_prefix.js +26 -26
  990. package/src/engine/graphics/texture/isImageBitmap.js +2 -2
  991. package/src/engine/graphics/texture/reflection/convolve_equirectangular_reflection.js +189 -189
  992. package/src/engine/graphics/texture/reflection/equirectangular_reflection_roughness.js +51 -51
  993. package/src/engine/graphics/texture/sampler/HarmonicDiffusionGrid.js +145 -145
  994. package/src/engine/graphics/texture/sampler/Sampler2DArray.js +172 -172
  995. package/src/engine/graphics/texture/sampler/compare/FLIP_IMPLEMENTATION_PLAN.md +466 -466
  996. package/src/engine/graphics/texture/sampler/compare/flip/FlipParameters.js +18 -18
  997. package/src/engine/graphics/texture/sampler/compare/flip/FlipPooling.js +33 -33
  998. package/src/engine/graphics/texture/sampler/compare/flip/flip_build_feature_kernel.js +147 -147
  999. package/src/engine/graphics/texture/sampler/compare/flip/flip_build_spatial_kernel.js +177 -177
  1000. package/src/engine/graphics/texture/sampler/compare/flip/flip_compute_color_difference.js +104 -104
  1001. package/src/engine/graphics/texture/sampler/compare/flip/flip_compute_feature_difference.js +76 -76
  1002. package/src/engine/graphics/texture/sampler/compare/flip/flip_compute_pixels_per_degree.js +34 -34
  1003. package/src/engine/graphics/texture/sampler/compare/flip/flip_constants.js +77 -77
  1004. package/src/engine/graphics/texture/sampler/compare/flip/flip_filter_feature.js +48 -48
  1005. package/src/engine/graphics/texture/sampler/compare/flip/flip_filter_spatial.js +86 -86
  1006. package/src/engine/graphics/texture/sampler/compare/flip/flip_pool.js +71 -71
  1007. package/src/engine/graphics/texture/sampler/compare/flip/flip_srgb_to_ycxcz.js +55 -55
  1008. package/src/engine/graphics/texture/sampler/compare/flip/sampler2d_flip_ldr.js +80 -80
  1009. package/src/engine/graphics/texture/sampler/filter/SeparableKernel.js +82 -82
  1010. package/src/engine/graphics/texture/sampler/filter/sampler2d_convolve_separable.js +122 -122
  1011. package/src/engine/graphics/texture/sampler/resize/sampler2d_scale_down_lanczos.js +148 -148
  1012. package/src/engine/graphics/texture/sampler/sampler2d_channel_apply_lut.js +82 -82
  1013. package/src/engine/graphics/texture/sampler/sampler2d_combine.js +67 -67
  1014. package/src/engine/graphics/texture/sampler/sampler2d_linear_f16_to_srgb_u8.js +54 -54
  1015. package/src/engine/graphics/texture/sampler/sampler2d_sample_equirectangular_direction.js +63 -63
  1016. package/src/engine/graphics/texture/sampler/sampler2d_srgb_to_linear.js +33 -33
  1017. package/src/engine/graphics/texture/sampler/sampler2d_srgb_u8_to_linear_f16.js +68 -68
  1018. package/src/engine/graphics/texture/shade_image_preview.js +103 -103
  1019. package/src/engine/graphics/trail/TrailSpawnMode.js +20 -20
  1020. package/src/engine/graphics/trail/tube/TubeX.js +573 -573
  1021. package/src/engine/graphics/trail/tube/TubeXMaterialSpec.js +58 -58
  1022. package/src/engine/graphics/trail/tube/simulator/TubeXFixedPhysicsSimulator.js +34 -34
  1023. package/src/engine/graphics/trail/tube/tube_attributes_spec.js +69 -69
  1024. package/src/engine/graphics/trail/tube/tube_frame.js +83 -83
  1025. package/src/engine/graphics3/AnimationGraphSystem3.js +329 -329
  1026. package/src/engine/graphics3/AnimationSystem3.js +352 -352
  1027. package/src/engine/graphics3/CameraSystem3.js +170 -170
  1028. package/src/engine/graphics3/DecalSystem3.js +441 -441
  1029. package/src/engine/graphics3/FogOfWarSystem3.js +265 -265
  1030. package/src/engine/graphics3/GraphicsEngine3.js +776 -776
  1031. package/src/engine/graphics3/HighlightSystem3.js +241 -241
  1032. package/src/engine/graphics3/LightSystem3.js +211 -211
  1033. package/src/engine/graphics3/MeshSystem3.js +646 -646
  1034. package/src/engine/graphics3/ParticleEmitterSystem3.js +289 -289
  1035. package/src/engine/graphics3/PathDisplaySystem3.js +309 -309
  1036. package/src/engine/graphics3/PickingQuery.js +48 -48
  1037. package/src/engine/graphics3/PickingResult.js +47 -47
  1038. package/src/engine/graphics3/PickingSystem3.js +273 -273
  1039. package/src/engine/graphics3/ShadeCameraAdapter.js +55 -55
  1040. package/src/engine/graphics3/ShadedGeometrySystem3.js +169 -169
  1041. package/src/engine/graphics3/TerrainSystem3.js +566 -566
  1042. package/src/engine/graphics3/TooltipComponentSystem3.js +328 -328
  1043. package/src/engine/graphics3/Trail3DSystem3.js +221 -221
  1044. package/src/engine/graphics3/VolumetricLightMap.d.ts +90 -0
  1045. package/src/engine/graphics3/VolumetricLightMap.d.ts.map +1 -0
  1046. package/src/engine/graphics3/VolumetricLightMap.js +160 -0
  1047. package/src/engine/graphics3/VolumetricLightMapSerializationAdapter.d.ts +32 -0
  1048. package/src/engine/graphics3/VolumetricLightMapSerializationAdapter.d.ts.map +1 -0
  1049. package/src/engine/graphics3/VolumetricLightMapSerializationAdapter.js +67 -0
  1050. package/src/engine/graphics3/VolumetricLightMapSystem3.d.ts +98 -0
  1051. package/src/engine/graphics3/VolumetricLightMapSystem3.d.ts.map +1 -0
  1052. package/src/engine/graphics3/VolumetricLightMapSystem3.js +261 -0
  1053. package/src/engine/graphics3/WaterSystem3.js +293 -293
  1054. package/src/engine/graphics3/animation/ClipPlayback.js +84 -84
  1055. package/src/engine/graphics3/animation/wrap_clip_time.js +23 -23
  1056. package/src/engine/graphics3/animation/write_graph_playback.js +48 -48
  1057. package/src/engine/graphics3/camera_sync_from_transform.js +29 -29
  1058. package/src/engine/graphics3/debug/gizmo_view_from_camera.js +48 -48
  1059. package/src/engine/graphics3/decal/DECAL_CLUSTER.js +61 -61
  1060. package/src/engine/graphics3/decal/DECAL_GPU_RECORD.js +117 -117
  1061. package/src/engine/graphics3/decal/DecalAtlas.js +194 -194
  1062. package/src/engine/graphics3/decal/DecalAtlasFill.js +32 -32
  1063. package/src/engine/graphics3/decal/GPUDecalRenderer.js +360 -360
  1064. package/src/engine/graphics3/decal/chunk_decal_intersects_frustum.js +59 -59
  1065. package/src/engine/graphics3/decal/chunk_decal_sampling_info.js +57 -57
  1066. package/src/engine/graphics3/decal/chunk_decal_surface_frame.js +54 -54
  1067. package/src/engine/graphics3/decal/decal_atlas_border.js +69 -69
  1068. package/src/engine/graphics3/decal/decal_gpu_records.js +120 -120
  1069. package/src/engine/graphics3/decal/decal_material_samplers.js +109 -109
  1070. package/src/engine/graphics3/decal/graph_build_decal_clusters.js +212 -212
  1071. package/src/engine/graphics3/decal/shader_cluster_assign_decals.js +207 -207
  1072. package/src/engine/graphics3/decal/shader_cull_decals_frustum.js +108 -108
  1073. package/src/engine/graphics3/highlight/GPUHighlightRenderer.js +151 -151
  1074. package/src/engine/graphics3/highlight/HIGHLIGHT_OUTLINE.js +23 -23
  1075. package/src/engine/graphics3/highlight/chunk_highlight_lookup.js +53 -53
  1076. package/src/engine/graphics3/highlight/pack_highlight_color.js +44 -44
  1077. package/src/engine/graphics3/highlight/pack_highlight_table.js +74 -74
  1078. package/src/engine/graphics3/highlight/shader_highlight_outline.js +150 -150
  1079. package/src/engine/graphics3/load_cube_environment.js +68 -68
  1080. package/src/engine/graphics3/make_default_environment.js +107 -107
  1081. package/src/engine/graphics3/particles/particle_gpu_records.js +221 -221
  1082. package/src/engine/graphics3/path/build_tube_path_streams.js +98 -98
  1083. package/src/engine/graphics3/path/tube_stream_write_geometry.js +90 -90
  1084. package/src/engine/graphics3/preview/ModelPreviewFraming.js +35 -35
  1085. package/src/engine/graphics3/preview/make_model_preview_scene.js +154 -154
  1086. package/src/engine/graphics3/preview/make_model_thumbnail.js +48 -48
  1087. package/src/engine/graphics3/preview/model_preview_framing.js +70 -70
  1088. package/src/engine/graphics3/shade_bundle_bounds.js +55 -55
  1089. package/src/engine/graphics3/shade_camera_projection_ray.js +77 -77
  1090. package/src/engine/graphics3/shade_node_to_entity_composition.js +84 -84
  1091. package/src/engine/graphics3/terrain/GPUTerrainSplatRenderer.js +481 -481
  1092. package/src/engine/graphics3/terrain/chunk_terrain_grid_overlay.js +61 -61
  1093. package/src/engine/graphics3/terrain/chunk_terrain_splat_mix.js +66 -66
  1094. package/src/engine/graphics3/terrain/pack_terrain_row_table.js +53 -53
  1095. package/src/engine/graphics3/terrain/shader_terrain_splat.js +215 -215
  1096. package/src/engine/graphics3/trail/trail_tube_write_geometry.js +158 -158
  1097. package/src/engine/input/GAMEPAD_API_PROPOSAL.md +717 -717
  1098. package/src/engine/input/analog/apply_radial_dead_zone.js +38 -38
  1099. package/src/engine/input/devices/GamepadDevice.js +466 -466
  1100. package/src/engine/input/devices/ManualInputDevice.js +152 -152
  1101. package/src/engine/input/devices/PointerCoordinates.js +26 -26
  1102. package/src/engine/input/devices/events/GamepadEvents.js +10 -10
  1103. package/src/engine/input/devices/gamepad/GamepadAxes.js +19 -19
  1104. package/src/engine/input/devices/gamepad/GamepadButtons.js +29 -29
  1105. package/src/engine/input/devices/gamepad/GamepadCoordinates.js +17 -17
  1106. package/src/engine/input/devices/gamepad/GamepadDeviceLayout.js +323 -323
  1107. package/src/engine/input/devices/gamepad/GamepadHandle.js +286 -286
  1108. package/src/engine/input/devices/gamepad/GamepadLayout.js +160 -160
  1109. package/src/engine/input/devices/gamepad/GamepadStandardMapping.js +31 -31
  1110. package/src/engine/input/devices/gamepad/GamepadStick.js +169 -169
  1111. package/src/engine/input/devices/gamepad/GamepadStickCalibration.js +119 -119
  1112. package/src/engine/input/devices/gamepad/GamepadStickCalibrator.js +250 -250
  1113. package/src/engine/input/devices/gamepad/gamepad_layout_database.js +144 -144
  1114. package/src/engine/input/devices/gamepad/gamepad_read_hat_axis.js +61 -61
  1115. package/src/engine/input/devices/gamepad/gamepad_write_dpad_vector.js +30 -30
  1116. package/src/engine/input/devices/gamepad/prototypeGamepadTester.js +2124 -2124
  1117. package/src/engine/input/devices/mouse/MouseButtons.js +22 -22
  1118. package/src/engine/input/ecs/components/Input.js +118 -118
  1119. package/src/engine/input/ecs/components/InputController.js +78 -78
  1120. package/src/engine/input/ecs/ism/ISM_DESIGN.md +1523 -1523
  1121. package/src/engine/input/ecs/ism/ISM_IMPLEMENTATION_PLAN.md +627 -627
  1122. package/src/engine/input/ecs/ism/InputActionEvent.js +40 -40
  1123. package/src/engine/input/ecs/ism/InputBinding.js +71 -71
  1124. package/src/engine/input/ecs/ism/InputCoordinateKind.js +25 -25
  1125. package/src/engine/input/ecs/ism/InputCoordinateSource.js +86 -86
  1126. package/src/engine/input/ecs/ism/InputDirection.js +33 -33
  1127. package/src/engine/input/ecs/ism/InputMapProgram.js +236 -236
  1128. package/src/engine/input/ecs/ism/InputMapSystem.js +976 -976
  1129. package/src/engine/input/ecs/ism/InputOcclusion.js +31 -31
  1130. package/src/engine/input/ecs/ism/InputSource.js +67 -67
  1131. package/src/engine/input/ecs/ism/InputSourceTable.js +107 -107
  1132. package/src/engine/input/ecs/ism/coordinate/InputCoordinateChannel.js +93 -93
  1133. package/src/engine/input/ecs/ism/coordinate/InputCoordinateChannelType.js +11 -11
  1134. package/src/engine/input/ecs/ism/coordinate/InputCoordinateDevice.js +153 -153
  1135. package/src/engine/input/ecs/ism/coordinate/InputCoordinateSwitches.js +160 -160
  1136. package/src/engine/input/ecs/ism/device/GAMEPAD_INPUT_DEVICE_ID.js +9 -9
  1137. package/src/engine/input/ecs/ism/device/GamepadInputDeviceAdapter.js +88 -88
  1138. package/src/engine/input/ecs/ism/device/InputDeviceAdapter.js +72 -72
  1139. package/src/engine/input/ecs/ism/device/KEYBOARD_INPUT_DEVICE_ID.js +9 -9
  1140. package/src/engine/input/ecs/ism/device/KeyboardInputDeviceAdapter.js +63 -63
  1141. package/src/engine/input/ecs/ism/device/ManualInputDeviceAdapter.js +55 -55
  1142. package/src/engine/input/ecs/ism/device/POINTER_INPUT_DEVICE_ID.js +9 -9
  1143. package/src/engine/input/ecs/ism/device/PointerInputDeviceAdapter.js +160 -160
  1144. package/src/engine/input/ecs/ism/map/InputBinding.js +88 -88
  1145. package/src/engine/input/ecs/ism/map/InputCoordinateBinding.js +102 -102
  1146. package/src/engine/input/ecs/ism/map/InputLayer.js +220 -220
  1147. package/src/engine/input/ecs/ism/map/InputMap.js +331 -331
  1148. package/src/engine/input/ecs/ism/prototypeInputMap.js +309 -309
  1149. package/src/engine/input/ecs/ism/trigger/InputTrigger.js +148 -148
  1150. package/src/engine/input/ecs/ism/trigger/InputTriggerChord.js +173 -173
  1151. package/src/engine/input/ecs/ism/trigger/InputTriggerGamepadButton.js +37 -37
  1152. package/src/engine/input/ecs/ism/trigger/InputTriggerHold.js +176 -176
  1153. package/src/engine/input/ecs/ism/trigger/InputTriggerKey.js +39 -39
  1154. package/src/engine/input/ecs/ism/trigger/InputTriggerMouseButton.js +38 -38
  1155. package/src/engine/input/ecs/ism/trigger/InputTriggerRelease.js +120 -120
  1156. package/src/engine/input/ecs/ism/trigger/InputTriggerSequence.js +315 -315
  1157. package/src/engine/input/ecs/ism/trigger/InputTriggerSwitch.js +105 -105
  1158. package/src/engine/input/ecs/ism/trigger/InputTriggerType.js +17 -17
  1159. package/src/engine/input/ecs/systems/InputControllerSystem.js +253 -253
  1160. package/src/engine/input/ecs/systems/InputSystem.js +162 -162
  1161. package/src/engine/intelligence/behavior/primitive/FailingBehaviorSerializationAdapter.js +25 -25
  1162. package/src/engine/intelligence/behavior/primitive/SucceedingBehaviorSerializationAdapter.js +26 -26
  1163. package/src/engine/intelligence/behavior/util/AsynchronousDelayAction.js +55 -55
  1164. package/src/engine/interpolation/BinaryInterpolationAdapter.js +56 -56
  1165. package/src/engine/interpolation/Interpoland.js +49 -49
  1166. package/src/engine/interpolation/Interpolated.js +149 -149
  1167. package/src/engine/interpolation/PoseInterpolationAdapter.js +61 -61
  1168. package/src/engine/interpolation/TransformPoseSerializationAdapter.js +57 -57
  1169. package/src/engine/interpolation/pose_interpoland.js +27 -27
  1170. package/src/engine/navigation/ecs/path_following/PathFollower.js +222 -222
  1171. package/src/engine/navigation/mesh/NavigationMesh.js +145 -145
  1172. package/src/engine/navigation/mesh/OVERLAPPING_SURFACES_2026_08_25.md +661 -0
  1173. package/src/engine/navigation/mesh/PATHFINDING_PLAN.md +229 -229
  1174. package/src/engine/navigation/mesh/build/clip_soup_against_overhangs.d.ts.map +1 -1
  1175. package/src/engine/navigation/mesh/build/clip_soup_against_overhangs.js +628 -520
  1176. package/src/engine/network/CONGESTION_CONTROL.md +87 -87
  1177. package/src/engine/network/README.md +52 -52
  1178. package/src/engine/network/adapters/QuaternionInterpolationAdapter.js +43 -43
  1179. package/src/engine/network/adapters/TransformInterpolationAdapter.js +84 -84
  1180. package/src/engine/network/adapters/TransformReplicationAdapter.js +87 -87
  1181. package/src/engine/network/adapters/Vector3InterpolationAdapter.js +46 -46
  1182. package/src/engine/network/convertPathToURL.js +2 -2
  1183. package/src/engine/network/core/MalformedPacketError.js +26 -26
  1184. package/src/engine/network/core/quantize/quantize_float.js +73 -73
  1185. package/src/engine/network/core/quantize/quantize_position.js +65 -65
  1186. package/src/engine/network/core/sequence/ack_bitfield.js +77 -77
  1187. package/src/engine/network/core/sequence/seq16.js +69 -69
  1188. package/src/engine/network/core/sequence/seq32.js +73 -73
  1189. package/src/engine/network/ecs/NetworkSystem.js +84 -84
  1190. package/src/engine/network/ecs/components/NetworkIdentity.js +73 -73
  1191. package/src/engine/network/ecs/owner_authorization.js +55 -55
  1192. package/src/engine/network/ecs/serialization/NetworkIdentitySerializationAdapter.js +64 -64
  1193. package/src/engine/network/replication/ScopeFilter.js +71 -71
  1194. package/src/engine/network/sim/ActionLog.js +152 -152
  1195. package/src/engine/network/sim/SimAction.js +273 -273
  1196. package/src/engine/network/sim/Snapshotter.js +222 -222
  1197. package/src/engine/network/state/FrameBufferRing.js +168 -168
  1198. package/src/engine/network/state/InputRing.js +134 -134
  1199. package/src/engine/network/state/PriorityAccumulator.js +180 -180
  1200. package/src/engine/network/time/TimeDilation.js +62 -62
  1201. package/src/engine/network/transport/Channel.js +336 -336
  1202. package/src/engine/network/transport/Transport.js +203 -203
  1203. package/src/engine/network/transport/adapters/NodeUDPTransport.js +186 -186
  1204. package/src/engine/network/transport/adapters/WebRTCDataChannelTransport.js +105 -105
  1205. package/src/engine/network/transport/fragments/fragment_send.js +147 -147
  1206. package/src/engine/network/transport/fragments/packet_size.js +101 -101
  1207. package/src/engine/network/xhr.js +23 -23
  1208. package/src/engine/physics/BULLET_REVIEW.md +945 -945
  1209. package/src/engine/physics/CANNON_REVIEW.md +1300 -1300
  1210. package/src/engine/physics/CONSTRAINT_SOLVER_BENCH_LOG.md +208 -208
  1211. package/src/engine/physics/CONSTRAINT_SOLVER_IMPROVEMENTS_PLAN.md +364 -364
  1212. package/src/engine/physics/INTEPOLATION_SYSTEM_PLAN.md +287 -287
  1213. package/src/engine/physics/JOLT_REVIEW.md +913 -913
  1214. package/src/engine/physics/PLAN.md +1120 -1094
  1215. package/src/engine/physics/RAPIER_REVIEW.md +934 -934
  1216. package/src/engine/physics/REVIEW_001_ACTION_PLAN.md +642 -642
  1217. package/src/engine/physics/REVIEW_002.md +151 -151
  1218. package/src/engine/physics/REVIEW_003.md +166 -166
  1219. package/src/engine/physics/broadphase/compute_fat_world_aabb.js +97 -97
  1220. package/src/engine/physics/constraint/DofMode.js +35 -35
  1221. package/src/engine/physics/constraint/solve_constraints.js +1 -1
  1222. package/src/engine/physics/contact/combine_material.js +35 -35
  1223. package/src/engine/physics/ecs/BodyKind.js +29 -29
  1224. package/src/engine/physics/ecs/Collider.js +177 -177
  1225. package/src/engine/physics/ecs/ColliderFlags.js +15 -15
  1226. package/src/engine/physics/ecs/ColliderSerializationAdapter.js +37 -37
  1227. package/src/engine/physics/ecs/Joint.js +475 -475
  1228. package/src/engine/physics/ecs/JointSerializationAdapter.js +72 -72
  1229. package/src/engine/physics/ecs/PhysicsSystem.d.ts +3 -0
  1230. package/src/engine/physics/ecs/PhysicsSystem.d.ts.map +1 -1
  1231. package/src/engine/physics/ecs/PhysicsSystem.js +7 -2
  1232. package/src/engine/physics/ecs/RigidBodySerializationUpgrader_0_1.js +37 -37
  1233. package/src/engine/physics/ecs/SleepState.js +12 -12
  1234. package/src/engine/physics/ecs/find_non_finite_physics_state.js +76 -76
  1235. package/src/engine/physics/fluid/FluidField.js +867 -867
  1236. package/src/engine/physics/fluid/FluidSimulator.js +832 -832
  1237. package/src/engine/physics/fluid/REVIEW_02_PLAN.md +243 -243
  1238. package/src/engine/physics/fluid/SliceVisualiser.js +190 -190
  1239. package/src/engine/physics/fluid/ecs/FluidComponent.js +239 -239
  1240. package/src/engine/physics/fluid/ecs/FluidEffectorsComponent.js +89 -89
  1241. package/src/engine/physics/fluid/ecs/FluidObstacle.js +97 -97
  1242. package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
  1243. package/src/engine/physics/fluid/ecs/FluidObstacleSystem.js +348 -348
  1244. package/src/engine/physics/fluid/ecs/FluidSystem.js +142 -142
  1245. package/src/engine/physics/fluid/ecs/FluidWorkerCore.js +123 -123
  1246. package/src/engine/physics/fluid/ecs/WorkerFluidSystem.js +395 -395
  1247. package/src/engine/physics/fluid/ecs/fluid.worker.js +12 -12
  1248. package/src/engine/physics/fluid/ecs/fluid_build_world_to_grid.js +32 -32
  1249. package/src/engine/physics/fluid/ecs/fluid_gather_visible_effectors.js +47 -47
  1250. package/src/engine/physics/fluid/ecs/fluid_reanchor_field.js +85 -85
  1251. package/src/engine/physics/fluid/ecs/fluid_sync_effectors_from_transform.js +16 -16
  1252. package/src/engine/physics/fluid/ecs/fluid_worker_protocol.js +111 -111
  1253. package/src/engine/physics/fluid/ecs/makeFluidWorker.js +18 -18
  1254. package/src/engine/physics/fluid/effector/AbstractFluidEffector.js +81 -81
  1255. package/src/engine/physics/fluid/effector/GlobalFluidEffector.js +202 -202
  1256. package/src/engine/physics/fluid/effector/ImpulseFluidEffector.js +257 -257
  1257. package/src/engine/physics/fluid/effector/WakeFluidEffector.js +405 -405
  1258. package/src/engine/physics/fluid/prototype.js +126 -126
  1259. package/src/engine/physics/fluid/solver/v3_grid_advect_maccormack_scalar.js +143 -143
  1260. package/src/engine/physics/fluid/solver/v3_grid_advect_maccormack_velocity.js +162 -162
  1261. package/src/engine/physics/fluid/solver/v3_grid_advect_sl_velocity.js +107 -107
  1262. package/src/engine/physics/fluid/solver/v3_grid_apply_vorticity_confinement.js +131 -131
  1263. package/src/engine/physics/fluid/solver/v3_grid_compute_solid_neighbour_mask.js +90 -90
  1264. package/src/engine/physics/fluid/solver/v3_grid_limit8_at_indices.js +38 -38
  1265. package/src/engine/physics/fluid/solver/v3_grid_patch_edges_constant.js +77 -77
  1266. package/src/engine/physics/fluid/solver/v3_grid_patch_edges_uniform.js +113 -113
  1267. package/src/engine/physics/fluid/solver/v3_grid_sample_scalar_masked.js +92 -92
  1268. package/src/engine/physics/fluid/solver/v3_grid_shift_in_place.js +107 -107
  1269. package/src/engine/physics/fluid/solver/v3_grid_solve_pressure.js +135 -135
  1270. package/src/engine/physics/fluid/solver/v3_grid_solve_pressure_pcg.js +511 -511
  1271. package/src/engine/physics/fluid/solver/v3_grid_subtract_pressure_gradient.js +108 -108
  1272. package/src/engine/physics/fluid/solver/v3_mac_advect_maccormack_velocity.js +317 -317
  1273. package/src/engine/physics/fluid/solver/v3_mac_advect_scalar.js +236 -236
  1274. package/src/engine/physics/fluid/solver/v3_mac_advect_sl_velocity.js +217 -217
  1275. package/src/engine/physics/fluid/solver/v3_mac_apply_vorticity_confinement.js +174 -174
  1276. package/src/engine/physics/fluid/solver/v3_mac_clip_trace.js +95 -95
  1277. package/src/engine/physics/fluid/solver/v3_mac_compute_divergence.js +77 -77
  1278. package/src/engine/physics/fluid/solver/v3_mac_compute_face_solid.js +131 -131
  1279. package/src/engine/physics/fluid/solver/v3_mac_subtract_pressure_gradient.js +104 -104
  1280. package/src/engine/physics/inertia/world_inverse_inertia.js +160 -160
  1281. package/src/engine/physics/integration/integrate_position.js +97 -97
  1282. package/src/engine/physics/narrowphase/box_box_manifold.js +681 -681
  1283. package/src/engine/physics/narrowphase/box_triangle_contact.js +906 -906
  1284. package/src/engine/physics/narrowphase/capsule_triangle_contact.js +395 -395
  1285. package/src/engine/physics/narrowphase/compute_penetration.js +369 -369
  1286. package/src/engine/physics/narrowphase/decomposition/decompose_to_triangles.js +55 -55
  1287. package/src/engine/physics/narrowphase/decomposition/triangle_buffer_layout.js +37 -37
  1288. package/src/engine/physics/narrowphase/mesh_mesh_tet_manifold.js +117 -117
  1289. package/src/engine/physics/narrowphase/narrowphase_step.js +1738 -1738
  1290. package/src/engine/physics/narrowphase/reduce_manifold_contacts.js +74 -74
  1291. package/src/engine/physics/narrowphase/refine_ray_hit.d.ts +12 -11
  1292. package/src/engine/physics/narrowphase/refine_ray_hit.d.ts.map +1 -1
  1293. package/src/engine/physics/narrowphase/refine_ray_hit.js +228 -36
  1294. package/src/engine/physics/narrowphase/sphere_sphere_contact.js +52 -52
  1295. package/src/engine/physics/narrowphase/sphere_triangle_contact.js +138 -138
  1296. package/src/engine/physics/persistence/solver_caches.js +309 -309
  1297. package/src/engine/physics/queries/overlap_shape.js +187 -187
  1298. package/src/engine/physics/queries/raycast.d.ts +7 -4
  1299. package/src/engine/physics/queries/raycast.d.ts.map +1 -1
  1300. package/src/engine/physics/queries/raycast.js +11 -5
  1301. package/src/engine/physics/queries/shape_cast.js +417 -417
  1302. package/src/engine/physics/solver/apply_impulse_to_body.js +86 -86
  1303. package/src/engine/physics/solver/friction_cone.js +37 -37
  1304. package/src/engine/physics/solver/solve_contacts.js +1 -1
  1305. package/src/engine/platform/WebEnginePlatform.d.ts.map +1 -1
  1306. package/src/engine/platform/WebEnginePlatform.js +1 -2
  1307. package/src/engine/scene/SerializedScene.d.ts +2 -2
  1308. package/src/engine/scene/SerializedScene.d.ts.map +1 -1
  1309. package/src/engine/scene/transitionToScene.js +121 -121
  1310. package/src/engine/simulation/Ticker.d.ts.map +1 -1
  1311. package/src/engine/simulation/Ticker.js +33 -0
  1312. package/src/engine/sound/ecs/audio/LiveEmitterSet.js +345 -345
  1313. package/src/engine/sound/ecs/audio/SpatialAudioIndex.js +140 -140
  1314. package/src/engine/sound/simulation/AcousticSimulator.js +646 -646
  1315. package/src/engine/sound/simulation/IMPLEMENTATION_PLAN.md +432 -432
  1316. package/src/engine/sound/simulation/PROBE_PLACEMENT.md +250 -250
  1317. package/src/engine/sound/simulation/PROBE_TRANSFER_RESEARCH.md +322 -322
  1318. package/src/engine/sound/simulation/README.md +277 -277
  1319. package/src/engine/sound/simulation/REFLECTION_IR_PLAN.md +261 -261
  1320. package/src/engine/sound/simulation/VISIBILITY_GRAPH_PLAN.md +121 -121
  1321. package/src/engine/sound/simulation/configureAcousticSimulation.js +48 -48
  1322. package/src/engine/sound/simulation/core/AcousticMedium.js +113 -113
  1323. package/src/engine/sound/simulation/core/AcousticMediumSerializationAdapter.js +27 -27
  1324. package/src/engine/sound/simulation/core/AcousticOccluderIndex.js +312 -312
  1325. package/src/engine/sound/simulation/core/AcousticRegionIndex.js +159 -159
  1326. package/src/engine/sound/simulation/core/AcousticSolution.js +90 -90
  1327. package/src/engine/sound/simulation/core/AcousticSourceState.js +109 -109
  1328. package/src/engine/sound/simulation/core/AcousticVolumeRegion.js +73 -73
  1329. package/src/engine/sound/simulation/core/OcclusionSolver.js +112 -112
  1330. package/src/engine/sound/simulation/core/RayHit.js +50 -50
  1331. package/src/engine/sound/simulation/core/VolumeField.js +149 -149
  1332. package/src/engine/sound/simulation/definition/AcousticDirectivity.js +114 -114
  1333. package/src/engine/sound/simulation/definition/AcousticDirectivitySerializationAdapter.js +30 -30
  1334. package/src/engine/sound/simulation/definition/AcousticMaterial.js +122 -122
  1335. package/src/engine/sound/simulation/definition/AcousticMaterialSerializationAdapter.js +32 -32
  1336. package/src/engine/sound/simulation/ecs/AcousticBody.js +48 -48
  1337. package/src/engine/sound/simulation/ecs/AcousticBodySerializationAdapter.js +34 -34
  1338. package/src/engine/sound/simulation/ecs/AcousticProbeFieldSystem.js +64 -64
  1339. package/src/engine/sound/simulation/ecs/AcousticSimulationSystem.js +75 -75
  1340. package/src/engine/sound/simulation/ecs/AcousticVolume.js +74 -74
  1341. package/src/engine/sound/simulation/ecs/AcousticVolumeSerializationAdapter.js +37 -37
  1342. package/src/engine/sound/simulation/ecs/AcousticVolumeSystem.js +66 -66
  1343. package/src/engine/sound/simulation/probe/AcousticProbeField.js +578 -578
  1344. package/src/engine/sound/simulation/probe/AcousticProbeFieldSerializationAdapter.js +60 -60
  1345. package/src/engine/sound/simulation/probe/ProbeFieldPathingSolution.js +64 -64
  1346. package/src/engine/sound/simulation/probe/ProbeVisibilityRecheck.js +132 -132
  1347. package/src/engine/sound/simulation/probe/acoustic_probe_transfer.js +103 -103
  1348. package/src/engine/sound/simulation/probe/acoustic_trace_energy.js +136 -136
  1349. package/src/engine/sound/simulation/probe/bakeProbeField.js +169 -169
  1350. package/src/engine/sound/simulation/probe/bakeProbeReflectors.js +110 -110
  1351. package/src/engine/sound/simulation/probe/bakeReverbBands.js +110 -110
  1352. package/src/engine/sound/simulation/probe/bakeVisibility.js +148 -148
  1353. package/src/engine/sound/simulation/probe/echogram_band_rt60.js +83 -83
  1354. package/src/engine/sound/simulation/probe/probe_delaunay_edges.js +215 -215
  1355. package/src/engine/sound/simulation/probe/probe_densify_portals.js +140 -140
  1356. package/src/engine/sound/simulation/probe/probe_detect_portals.js +196 -196
  1357. package/src/engine/sound/simulation/probe/probe_edge_key.js +38 -38
  1358. package/src/engine/sound/simulation/probe/probe_edge_visible.js +41 -41
  1359. package/src/engine/sound/simulation/probe/probe_place_sdf_cover.js +82 -82
  1360. package/src/engine/sound/simulation/probe/probe_refine_connectivity.js +181 -181
  1361. package/src/engine/sound/simulation/probe/probe_sdf_grid.js +171 -171
  1362. package/src/engine/sound/simulation/probe/probe_visibility_augment.js +146 -146
  1363. package/src/engine/sound/simulation/probe/probe_visibility_bridge_components.js +167 -167
  1364. package/src/engine/sound/simulation/render/FoaProbeReverbRenderer.js +354 -354
  1365. package/src/engine/sound/simulation/render/ProbeReverbRenderer.js +231 -231
  1366. package/src/engine/sound/simulation/render/foaReverbImpulseResponse.js +105 -105
  1367. package/src/engine/sound/simulation/render/reverbImpulseResponse.js +66 -66
  1368. package/src/engine/sound/simulation/render/reverb_band_tails.js +93 -93
  1369. package/src/engine/sound/simulation/serialization/populateAcousticSerializationRegistry.js +25 -25
  1370. package/src/engine/sound/sopra/README.md +643 -643
  1371. package/src/engine/sound/sopra/SopraEngine.js +432 -432
  1372. package/src/engine/sound/sopra/asset/AssetManagerBufferProvider.js +71 -71
  1373. package/src/engine/sound/sopra/asset/BufferProvider.js +29 -29
  1374. package/src/engine/sound/sopra/asset/StubBufferProvider.js +58 -58
  1375. package/src/engine/sound/sopra/definition/BusDefinition.js +142 -142
  1376. package/src/engine/sound/sopra/definition/BusDefinitionSerializationAdapter.js +54 -54
  1377. package/src/engine/sound/sopra/definition/DuckingRule.js +106 -106
  1378. package/src/engine/sound/sopra/definition/DuckingRuleSerializationAdapter.js +31 -31
  1379. package/src/engine/sound/sopra/definition/EventDescription.js +262 -262
  1380. package/src/engine/sound/sopra/definition/EventDescriptionSerializationAdapter.js +55 -55
  1381. package/src/engine/sound/sopra/definition/MixerSnapshot.js +83 -83
  1382. package/src/engine/sound/sopra/definition/MixerSnapshotSerializationAdapter.js +39 -39
  1383. package/src/engine/sound/sopra/definition/ParameterDefinition.js +117 -117
  1384. package/src/engine/sound/sopra/definition/ParameterDefinitionSerializationAdapter.js +31 -31
  1385. package/src/engine/sound/sopra/definition/VoiceStealMode.js +18 -18
  1386. package/src/engine/sound/sopra/definition/clip/AbstractAudioClip.js +109 -109
  1387. package/src/engine/sound/sopra/definition/clip/BlendContainerAudioClip.js +185 -185
  1388. package/src/engine/sound/sopra/definition/clip/BlendContainerAudioClipSerializationAdapter.js +59 -59
  1389. package/src/engine/sound/sopra/definition/clip/ContainerAudioClip.js +128 -128
  1390. package/src/engine/sound/sopra/definition/clip/RandomContainerAudioClip.js +226 -226
  1391. package/src/engine/sound/sopra/definition/clip/RandomContainerAudioClipSerializationAdapter.js +54 -54
  1392. package/src/engine/sound/sopra/definition/clip/SampleAudioClip.js +191 -191
  1393. package/src/engine/sound/sopra/definition/clip/SampleAudioClipSerializationAdapter.js +39 -39
  1394. package/src/engine/sound/sopra/definition/clip/SequenceContainerAudioClip.js +79 -79
  1395. package/src/engine/sound/sopra/definition/clip/SequenceContainerAudioClipSerializationAdapter.js +42 -42
  1396. package/src/engine/sound/sopra/definition/clip/SilenceAudioClip.js +77 -77
  1397. package/src/engine/sound/sopra/definition/clip/SilenceAudioClipSerializationAdapter.js +27 -27
  1398. package/src/engine/sound/sopra/definition/clip/SwitchContainerAudioClip.d.ts +5 -0
  1399. package/src/engine/sound/sopra/definition/clip/SwitchContainerAudioClip.d.ts.map +1 -1
  1400. package/src/engine/sound/sopra/definition/clip/SwitchContainerAudioClip.js +125 -125
  1401. package/src/engine/sound/sopra/definition/clip/SwitchContainerAudioClipSerializationAdapter.js +41 -41
  1402. package/src/engine/sound/sopra/definition/effect/AbstractAudioEffect.js +24 -24
  1403. package/src/engine/sound/sopra/definition/effect/CompressorEffect.js +120 -120
  1404. package/src/engine/sound/sopra/definition/effect/CompressorEffectSerializationAdapter.js +31 -31
  1405. package/src/engine/sound/sopra/definition/effect/EqEffect.js +128 -128
  1406. package/src/engine/sound/sopra/definition/effect/EqEffectSerializationAdapter.js +29 -29
  1407. package/src/engine/sound/sopra/definition/effect/ReverbEffect.js +113 -113
  1408. package/src/engine/sound/sopra/definition/effect/ReverbEffectSerializationAdapter.js +25 -25
  1409. package/src/engine/sound/sopra/runtime/BusGraph.js +231 -231
  1410. package/src/engine/sound/sopra/runtime/ParameterStore.js +98 -98
  1411. package/src/engine/sound/sopra/runtime/SopraPlaybackContext.js +68 -68
  1412. package/src/engine/sound/sopra/runtime/Voice.js +145 -145
  1413. package/src/engine/sound/sopra/runtime/VoiceManager.js +136 -136
  1414. package/src/engine/sound/sopra/runtime/VoicePool.js +17 -17
  1415. package/src/engine/sound/sopra/serialization/populateSopraSerializationRegistry.js +42 -42
  1416. package/src/engine/sound/sopra/serialization/sopraJSON.js +99 -99
  1417. package/src/engine/sound/sopra/serialization/sopraSerializationHarness.js +49 -49
  1418. package/src/engine/sound/sopra/util/MockAudioContext.js +224 -224
  1419. package/src/engine/sound/sopra/util/fadeOutAndStop.js +60 -60
  1420. package/src/engine/ui/DraggableAspect.js +115 -115
  1421. package/src/engine/ui/apply_pointer_tilt_3d.js +50 -50
  1422. package/src/generation/COORDINATES.md +54 -54
  1423. package/src/generation/GridGenerationResource.js +24 -24
  1424. package/src/generation/GridTaskGroup.js +214 -214
  1425. package/src/generation/REVIEW_01_ACTION_PLAN.md +628 -628
  1426. package/src/generation/automata/CaveGeneratorCellularAutomata.js +79 -79
  1427. package/src/generation/automata/CellularAutomata.js +22 -22
  1428. package/src/generation/compute_writes_union.js +25 -25
  1429. package/src/generation/filtering/CellFilter.js +117 -117
  1430. package/src/generation/filtering/core/CellFilterBinaryOperation.js +37 -37
  1431. package/src/generation/filtering/core/CellFilterOperationTertiary.js +43 -43
  1432. package/src/generation/filtering/core/CellFilterUnaryOperation.js +29 -29
  1433. package/src/generation/filtering/numeric/process/computeCellFilterSobel.js +36 -36
  1434. package/src/generation/filtering/numeric/process/computeFilterSurfaceNormal.js +42 -42
  1435. package/src/generation/filtering/numeric/util/populateSampler2DFromCellFilter.js +18 -18
  1436. package/src/generation/filtering/numeric/util/write_cell_filter_to_sampler2d.js +47 -47
  1437. package/src/generation/filtering/util/cell_filter_weights_pick.js +54 -54
  1438. package/src/generation/grid/GridData.js +233 -233
  1439. package/src/generation/grid/coords/grid_to_texel.js +10 -10
  1440. package/src/generation/grid/coords/texel_to_grid.js +10 -10
  1441. package/src/generation/grid/generation/GridTaskApplyActionToCells.js +90 -90
  1442. package/src/generation/grid/generation/GridTaskDensityMarkerDistribution.js +388 -388
  1443. package/src/generation/grid/generation/GridTaskExecuteRuleTimes.js +127 -127
  1444. package/src/generation/grid/generation/GridTaskSequence.js +58 -58
  1445. package/src/generation/grid/generation/NoopGridTaskGenerator.js +12 -12
  1446. package/src/generation/grid/generation/discrete/GridTaskActionRuleSet.js +48 -48
  1447. package/src/generation/grid/generation/discrete/GridTaskCellularAutomata.js +146 -146
  1448. package/src/generation/grid/generation/discrete/GridTaskConnectRooms.js +540 -540
  1449. package/src/generation/grid/generation/discrete/layer/GridTaskBuildSourceDistanceMap.js +222 -222
  1450. package/src/generation/grid/generation/grid/GridTaskAddNodesFixed.js +44 -44
  1451. package/src/generation/grid/generation/road/GridTaskGenerateRoads.js +614 -614
  1452. package/src/generation/markers/GridCellActionPlaceMarker.js +244 -244
  1453. package/src/generation/markers/GridCellActionPlaceMarkerGroup.js +86 -86
  1454. package/src/generation/markers/actions/terrain/MarkerNodeActionPaintTerrain.js +266 -266
  1455. package/src/generation/markers/emitter/MarkerNodeEmitterGridCellAction.js +49 -49
  1456. package/src/generation/placement/GridCellActionTransformNearbyMarkers.js +120 -120
  1457. package/src/generation/placement/action/GridCellAction.js +45 -45
  1458. package/src/generation/placement/action/GridCellActionPlaceTags.js +140 -140
  1459. package/src/generation/placement/action/GridCellActionWriteFilterToLayer.js +89 -89
  1460. package/src/generation/placement/action/random/CellActionSelectRandom.js +69 -69
  1461. package/src/generation/placement/action/random/weighted/CellActionSelectWeightedRandom.js +91 -91
  1462. package/src/generation/placement/action/util/GridCellActionSequence.js +61 -61
  1463. package/src/generation/placement/action/util/GridCellDisplacedAction.js +61 -61
  1464. package/src/generation/rules/cell/CellMatcherRemovalPreservesConnectivity.js +183 -183
  1465. package/src/generation/theme/ThemeEngine.js +666 -666
  1466. package/src/shade/RENDERER_CONTRACT.md +568 -568
  1467. package/src/shade/descriptor/texture/format/gpu_texture_format_info.js +267 -267
  1468. package/src/shade/device/ShadeDeviceFailure.js +104 -104
  1469. package/src/shade/device/ShadeDeviceFailureReason.js +41 -41
  1470. package/src/shade/device/mock/IMPLEMENTATION_PLAN.md +255 -255
  1471. package/src/shade/device/mock/REVIEW_2026_08_22.md +527 -527
  1472. package/src/shade/device/mock/SoftwareGPUBindGroup.js +23 -23
  1473. package/src/shade/device/mock/SoftwareGPUBindGroupLayout.js +22 -22
  1474. package/src/shade/device/mock/SoftwareGPUError.js +63 -63
  1475. package/src/shade/device/mock/SoftwareGPUPipeline.js +75 -75
  1476. package/src/shade/device/mock/SoftwareGPUQuerySet.js +113 -113
  1477. package/src/shade/device/mock/SoftwareGPUSampler.js +30 -30
  1478. package/src/shade/device/mock/SoftwareGPUShaderModule.js +32 -32
  1479. package/src/shade/device/mock/SoftwareGPUTextureView.js +22 -22
  1480. package/src/shade/device/mock/SoftwareGPUUncapturedErrorEvent.js +23 -23
  1481. package/src/shade/device/mock/normalize_dynamic_offsets.js +26 -26
  1482. package/src/shade/device/mock/texture_copy.js +287 -287
  1483. package/src/shade/device/timing/GPUTimer.js +285 -285
  1484. package/src/shade/device/timing/GPUTimerArray.js +205 -205
  1485. package/src/shade/playground/add_random_lights.js +49 -49
  1486. package/src/shade/playground/add_random_meshes.js +83 -83
  1487. package/src/shade/playground/animation_prototype.js +508 -508
  1488. package/src/shade/playground/animation_prototype_skinning.js +647 -647
  1489. package/src/shade/playground/animation_prototype_stress.js +765 -765
  1490. package/src/shade/playground/enable_picker.js +59 -59
  1491. package/src/shade/playground/gltf_skinned_prototype.js +560 -560
  1492. package/src/shade/playground/gltf_skinned_stress_prototype.js +387 -387
  1493. package/src/shade/playground/gltf_viewer.js +282 -282
  1494. package/src/shade/playground/load_default_environment.js +19 -19
  1495. package/src/shade/playground/main.js +2727 -2727
  1496. package/src/shade/playground/make_animated_instances.js +117 -117
  1497. package/src/shade/playground/prototype_virtual_texture.js +274 -274
  1498. package/src/shade/playground/prototype_volumetrics_01.js +206 -206
  1499. package/src/shade/playground/sample.js +234 -234
  1500. package/src/shade/playground/sample_load_gltf.js +102 -102
  1501. package/src/shade/playground/spread_node3d_grid.js +19 -19
  1502. package/src/shade/renderer/Renderer.js +2436 -2436
  1503. package/src/shade/renderer/ShadeIndirectLightingMode.js +14 -14
  1504. package/src/shade/renderer/animation/ShadeAnimationChannel.js +414 -414
  1505. package/src/shade/renderer/animation/compute_skinned_mesh_clip_bounding_sphere.js +531 -531
  1506. package/src/shade/renderer/animation/pose/PosePlayback.js +55 -55
  1507. package/src/shade/renderer/animation/pose/pose_evaluate_local.js +179 -179
  1508. package/src/shade/renderer/animation/pose/pose_evaluate_world.js +76 -76
  1509. package/src/shade/renderer/animation/skinning/GPUMeshSkinningContext.js +1024 -1024
  1510. package/src/shade/renderer/animation/skinning/SKINNING_BINDING_STRUCT.js +79 -79
  1511. package/src/shade/renderer/animation/skinning/chunk_skin_blend_mesh_local.js +56 -56
  1512. package/src/shade/renderer/binding/BindGroupManager.js +282 -282
  1513. package/src/shade/renderer/binding/adapt_cache_capacity.js +25 -25
  1514. package/src/shade/renderer/buffer/GPUBufferAllocator.js +116 -116
  1515. package/src/shade/renderer/buffer/buffer_pool_ops.js +123 -123
  1516. package/src/shade/renderer/buffer/table/GPU_DATABASE_PAGE_SIZE.js +9 -9
  1517. package/src/shade/renderer/buffer/table/GPU_DATABASE_UPLOAD_BUFFER_LIMIT.js +8 -8
  1518. package/src/shade/renderer/buffer/table/PageBufferPool.js +71 -71
  1519. package/src/shade/renderer/buffer/table/PageSlotAllocator.js +95 -95
  1520. package/src/shade/renderer/camera/Camera.js +281 -281
  1521. package/src/shade/renderer/camera/GPUCameraContext.js +233 -233
  1522. package/src/shade/renderer/camera/orbital/README.md +84 -84
  1523. package/src/shade/renderer/deferred/shader_composite_volumetrics.js +75 -75
  1524. package/src/shade/renderer/dynamic/DYNAMIC_MESH_VERTEX.js +25 -25
  1525. package/src/shade/renderer/dynamic/GPUDynamicMeshRenderer.js +342 -342
  1526. package/src/shade/renderer/dynamic/GPUDynamicMeshResidency.js +167 -167
  1527. package/src/shade/renderer/dynamic/shader_dynamic_mesh.js +170 -170
  1528. package/src/shade/renderer/extension/FrameContext.js +263 -263
  1529. package/src/shade/renderer/extension/FramePhase.js +104 -104
  1530. package/src/shade/renderer/extension/FrameRecord.js +126 -126
  1531. package/src/shade/renderer/extension/GBufferTextures.js +83 -83
  1532. package/src/shade/renderer/extension/PresentTarget.js +34 -34
  1533. package/src/shade/renderer/extension/RENDER_EXTENSION_DESIGN.md +918 -918
  1534. package/src/shade/renderer/extension/RenderExtension.js +69 -69
  1535. package/src/shade/renderer/extension/RenderExtensionRegistry.js +319 -319
  1536. package/src/shade/renderer/extension/SceneColor.js +40 -40
  1537. package/src/shade/renderer/extension/ViewTextures.js +78 -78
  1538. package/src/shade/renderer/fow/GPUFogOfWarRenderer.js +182 -182
  1539. package/src/shade/renderer/geometry/Geometry.js +608 -608
  1540. package/src/shade/renderer/geometry/geometry_build_from_meshlet_geometry.js +342 -342
  1541. package/src/shade/renderer/geometry/meshlet_geometry_transfer.js +41 -41
  1542. package/src/shade/renderer/geometry/primitives/make_box_geometry.js +153 -153
  1543. package/src/shade/renderer/geometry/primitives/make_cylinder_geometry.js +240 -240
  1544. package/src/shade/renderer/geometry/primitives/make_plane_geometry.js +145 -145
  1545. package/src/shade/renderer/geometry/primitives/make_polyhedron_geometry.js +327 -327
  1546. package/src/shade/renderer/geometry/primitives/make_torus_geometry.js +107 -107
  1547. package/src/shade/renderer/geometry/primitives/make_torus_knot_geometry.js +165 -165
  1548. package/src/shade/renderer/geometry/virtual/VGEO_FORMAT.md +607 -607
  1549. package/src/shade/renderer/geometry/virtual/build/VGeoBuildOptions.js +66 -66
  1550. package/src/shade/renderer/geometry/virtual/build/VGeoBuildResult.js +32 -32
  1551. package/src/shade/renderer/geometry/virtual/build/VGeoFaceGraph.js +103 -103
  1552. package/src/shade/renderer/geometry/virtual/build/VGeoGatheredFaces.js +25 -25
  1553. package/src/shade/renderer/geometry/virtual/build/VGeoLevel.js +33 -33
  1554. package/src/shade/renderer/geometry/virtual/build/VGeoLevelOptions.js +62 -62
  1555. package/src/shade/renderer/geometry/virtual/build/VGeoLevelSummary.js +49 -49
  1556. package/src/shade/renderer/geometry/virtual/build/VGeoSubmesh.js +20 -20
  1557. package/src/shade/renderer/geometry/virtual/build/VirtualGeometryMesh.js +214 -214
  1558. package/src/shade/renderer/geometry/virtual/build/gltf/GltfByteSource.js +122 -122
  1559. package/src/shade/renderer/geometry/virtual/build/gltf/GltfCollectReport.js +29 -29
  1560. package/src/shade/renderer/geometry/virtual/build/gltf/GltfDocument.js +210 -210
  1561. package/src/shade/renderer/geometry/virtual/build/gltf/GltfGeometry.js +48 -48
  1562. package/src/shade/renderer/geometry/virtual/build/gltf/GltfGeometryInstance.js +25 -25
  1563. package/src/shade/renderer/geometry/virtual/build/gltf/GltfGeometrySelection.js +28 -28
  1564. package/src/shade/renderer/geometry/virtual/build/gltf/gltf_collect_geometries.js +296 -296
  1565. package/src/shade/renderer/geometry/virtual/build/gltf/gltf_open_document.js +166 -166
  1566. package/src/shade/renderer/geometry/virtual/build/gltf/gltf_read_geometry.js +199 -199
  1567. package/src/shade/renderer/geometry/virtual/build/vgeo_assemble_pages.js +173 -173
  1568. package/src/shade/renderer/geometry/virtual/build/vgeo_bounding_sphere.js +28 -28
  1569. package/src/shade/renderer/geometry/virtual/build/vgeo_build.js +200 -200
  1570. package/src/shade/renderer/geometry/virtual/build/vgeo_build_levels.js +1786 -1786
  1571. package/src/shade/renderer/geometry/virtual/build/vgeo_build_page_nodes.js +157 -157
  1572. package/src/shade/renderer/geometry/virtual/build/vgeo_build_root_group.js +26 -26
  1573. package/src/shade/renderer/geometry/virtual/build/vgeo_compute_vertex_normals.js +75 -75
  1574. package/src/shade/renderer/geometry/virtual/build/vgeo_compute_vertex_tangents.js +143 -143
  1575. package/src/shade/renderer/geometry/virtual/build/vgeo_containing_sphere.js +44 -44
  1576. package/src/shade/renderer/geometry/virtual/build/vgeo_extract_submesh.js +41 -41
  1577. package/src/shade/renderer/geometry/virtual/build/vgeo_gather_faces.js +71 -71
  1578. package/src/shade/renderer/geometry/virtual/build/vgeo_graph_components.js +123 -123
  1579. package/src/shade/renderer/geometry/virtual/build/vgeo_narrow_indices.js +26 -26
  1580. package/src/shade/renderer/geometry/virtual/build/vgeo_normalize_source.js +112 -112
  1581. package/src/shade/renderer/geometry/virtual/build/vgeo_partition_graph.js +56 -56
  1582. package/src/shade/renderer/geometry/virtual/build/vgeo_sphere_inflate_to_contain.js +70 -70
  1583. package/src/shade/renderer/geometry/virtual/build/vgeo_split_face_set.js +187 -187
  1584. package/src/shade/renderer/geometry/virtual/build/vgeo_split_face_set_packed.js +207 -207
  1585. package/src/shade/renderer/geometry/virtual/format/VGEO_DEFECT.js +18 -18
  1586. package/src/shade/renderer/geometry/virtual/format/VGeoDefect.js +38 -38
  1587. package/src/shade/renderer/geometry/virtual/format/VGeoWriteOptions.js +64 -64
  1588. package/src/shade/renderer/geometry/virtual/format/attribute/VGEO_ATTRIBUTE.js +13 -13
  1589. package/src/shade/renderer/geometry/virtual/format/attribute/VGEO_ATTRIBUTES.js +50 -50
  1590. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_attribute_allocate.js +28 -28
  1591. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_attribute_mask.js +23 -23
  1592. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_attributes_read.js +31 -31
  1593. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_layers_copy_vertex.js +32 -32
  1594. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_layers_declare.js +46 -46
  1595. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_layers_like.js +19 -19
  1596. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_layers_test_fixtures.js +27 -27
  1597. package/src/shade/renderer/geometry/virtual/format/checksum/vgeo_asset_id_preimage.js +62 -62
  1598. package/src/shade/renderer/geometry/virtual/format/checksum/vgeo_compute_asset_id.js +13 -13
  1599. package/src/shade/renderer/geometry/virtual/format/checksum/vgeo_compute_directory_block_checksum.js +41 -41
  1600. package/src/shade/renderer/geometry/virtual/format/checksum/vgeo_compute_frame_checksum.js +41 -41
  1601. package/src/shade/renderer/geometry/virtual/format/checksum/vgeo_compute_header_checksum.js +25 -25
  1602. package/src/shade/renderer/geometry/virtual/format/cluster/VGEO_CLUSTER_OFFSET.js +20 -20
  1603. package/src/shade/renderer/geometry/virtual/format/cluster/VGEO_CLUSTER_RECORD_SIZE.js +5 -5
  1604. package/src/shade/renderer/geometry/virtual/format/cluster/VGEO_MAX_CHILD_REF.js +5 -5
  1605. package/src/shade/renderer/geometry/virtual/format/cluster/VGEO_MAX_CLUSTER_TRIANGLE_COUNT.js +5 -5
  1606. package/src/shade/renderer/geometry/virtual/format/cluster/VGEO_MAX_CLUSTER_VERTEX_COUNT.js +5 -5
  1607. package/src/shade/renderer/geometry/virtual/format/cluster/VGEO_NO_CHILD.js +5 -5
  1608. package/src/shade/renderer/geometry/virtual/format/cluster/VGEO_NO_GROUP.js +5 -5
  1609. package/src/shade/renderer/geometry/virtual/format/cluster/VGEO_SAME_PAGE.js +6 -6
  1610. package/src/shade/renderer/geometry/virtual/format/dag/VirtualGeometryCluster.js +81 -81
  1611. package/src/shade/renderer/geometry/virtual/format/dag/VirtualGeometryGroup.js +37 -37
  1612. package/src/shade/renderer/geometry/virtual/format/dag/VirtualGeometryPageNode.js +55 -55
  1613. package/src/shade/renderer/geometry/virtual/format/directory/VGEO_DIRECTORY_BLOCK_RECORDS.js +5 -5
  1614. package/src/shade/renderer/geometry/virtual/format/directory/VGEO_DIRECTORY_FLAG_ROOT_PAGE.js +5 -5
  1615. package/src/shade/renderer/geometry/virtual/format/directory/VGEO_DIRECTORY_RECORD_SIZE.js +5 -5
  1616. package/src/shade/renderer/geometry/virtual/format/directory/vgeo_directory_block_count.js +12 -12
  1617. package/src/shade/renderer/geometry/virtual/format/frame/VGEO_CHILD_OFFSET.js +11 -11
  1618. package/src/shade/renderer/geometry/virtual/format/frame/VGEO_CHILD_TABLE_ENTRY_SIZE.js +5 -5
  1619. package/src/shade/renderer/geometry/virtual/format/frame/VGEO_CODEC_LZ4.js +11 -11
  1620. package/src/shade/renderer/geometry/virtual/format/frame/VGEO_CODEC_RAW.js +5 -5
  1621. package/src/shade/renderer/geometry/virtual/format/frame/VGEO_FRAME_ALIGNMENT.js +5 -5
  1622. package/src/shade/renderer/geometry/virtual/format/frame/VGEO_FRAME_HEADER_SIZE.js +5 -5
  1623. package/src/shade/renderer/geometry/virtual/format/frame/VGEO_FRAME_OFFSET.js +13 -13
  1624. package/src/shade/renderer/geometry/virtual/format/frame/VGEO_PAGE_MAGIC.js +5 -5
  1625. package/src/shade/renderer/geometry/virtual/format/frame/VGeoFrameBlob.js +21 -21
  1626. package/src/shade/renderer/geometry/virtual/format/frame/VGeoFrameExtent.js +59 -59
  1627. package/src/shade/renderer/geometry/virtual/format/frame/vgeo_codec_name.js +27 -27
  1628. package/src/shade/renderer/geometry/virtual/format/frame/vgeo_decode_frame_blob.js +84 -84
  1629. package/src/shade/renderer/geometry/virtual/format/frame/vgeo_encode_frame_blob.js +56 -56
  1630. package/src/shade/renderer/geometry/virtual/format/frame/vgeo_frame_decode_bound.js +35 -35
  1631. package/src/shade/renderer/geometry/virtual/format/frame/vgeo_scan_frames.js +74 -74
  1632. package/src/shade/renderer/geometry/virtual/format/header/VGEO_FORMAT_VERSION.js +6 -6
  1633. package/src/shade/renderer/geometry/virtual/format/header/VGEO_HASH_ALGO_XXH3.js +5 -5
  1634. package/src/shade/renderer/geometry/virtual/format/header/VGEO_HEADER_OFFSET.js +31 -31
  1635. package/src/shade/renderer/geometry/virtual/format/header/VGEO_HEADER_SIZE.js +5 -5
  1636. package/src/shade/renderer/geometry/virtual/format/header/VGEO_LEVEL_OFFSET.js +20 -20
  1637. package/src/shade/renderer/geometry/virtual/format/header/VGEO_LEVEL_RECORD_SIZE.js +9 -9
  1638. package/src/shade/renderer/geometry/virtual/format/header/VGEO_MAGIC.js +5 -5
  1639. package/src/shade/renderer/geometry/virtual/format/header/VGEO_MIN_READER_VERSION.js +5 -5
  1640. package/src/shade/renderer/geometry/virtual/format/header/VGEO_PAYLOAD_ENCODING_SHADE_MESHLET_V1.js +5 -5
  1641. package/src/shade/renderer/geometry/virtual/format/header/VGeoLevelRecord.js +41 -41
  1642. package/src/shade/renderer/geometry/virtual/format/page/VGEO_GROUP_RECORD_SIZE.js +5 -5
  1643. package/src/shade/renderer/geometry/virtual/format/page/VGEO_MAX_GROUP_CLUSTER_COUNT.js +6 -6
  1644. package/src/shade/renderer/geometry/virtual/format/page/VGEO_METRIC_OFFSET.js +21 -21
  1645. package/src/shade/renderer/geometry/virtual/format/page/VGEO_METRIC_SIZE.js +5 -5
  1646. package/src/shade/renderer/geometry/virtual/format/page/VGEO_NODE_FANOUT.js +9 -9
  1647. package/src/shade/renderer/geometry/virtual/format/page/VGEO_NODE_FLAG_LEAF.js +5 -5
  1648. package/src/shade/renderer/geometry/virtual/format/page/VGEO_NODE_OFFSET.js +10 -10
  1649. package/src/shade/renderer/geometry/virtual/format/page/VGEO_NODE_RECORD_SIZE.js +5 -5
  1650. package/src/shade/renderer/geometry/virtual/format/page/VGEO_PAGE_HEADER_SIZE.js +5 -5
  1651. package/src/shade/renderer/geometry/virtual/format/page/VGEO_PAGE_OFFSET.js +16 -16
  1652. package/src/shade/renderer/geometry/virtual/format/payload/VGEO_ENCODING_FLAGS_MASK.js +6 -6
  1653. package/src/shade/renderer/geometry/virtual/format/payload/VGeoEncodedPayload.js +18 -18
  1654. package/src/shade/renderer/geometry/virtual/format/payload/vgeo_encode_cluster_payload.js +200 -200
  1655. package/src/shade/renderer/geometry/virtual/format/payload/vgeo_payload_attribute_offset.js +39 -39
  1656. package/src/shade/renderer/geometry/virtual/format/payload/vgeo_payload_size.js +20 -20
  1657. package/src/shade/renderer/geometry/virtual/format/prototypeVGEOFormatViewer.js +2063 -2063
  1658. package/src/shade/renderer/geometry/virtual/format/vgeo_validate_container.js +1622 -1622
  1659. package/src/shade/renderer/geometry/virtual/format/vgeo_write_container.js +485 -485
  1660. package/src/shade/renderer/global_illumination/brick4/cpu/brick4_empty_gpu_structure.d.ts +23 -0
  1661. package/src/shade/renderer/global_illumination/brick4/cpu/brick4_empty_gpu_structure.d.ts.map +1 -0
  1662. package/src/shade/renderer/global_illumination/brick4/cpu/brick4_empty_gpu_structure.js +34 -0
  1663. package/src/shade/renderer/gpu_primitive/sort/ffx/graph_radix_sort.js +115 -115
  1664. package/src/shade/renderer/gpu_primitive/sort/one_sweep/verify_radix_sort_one_sweep.js +110 -110
  1665. package/src/shade/renderer/light/environment/octahedral_resample_kernel.js +50 -50
  1666. package/src/shade/renderer/light/environment/octahedral_uv_to_direction.js +30 -30
  1667. package/src/shade/renderer/light/environment/resample_cube_to_octahedral.js +157 -157
  1668. package/src/shade/renderer/light/make_sunlight.js +35 -35
  1669. package/src/shade/renderer/light/model/io/light_record_point.js +39 -39
  1670. package/src/shade/renderer/loader/gltf/GLTF_PRIMITIVE_MODE.js +23 -23
  1671. package/src/shade/renderer/loader/gltf/MESHOPT_COMPRESSION_PLAN.md +793 -793
  1672. package/src/shade/renderer/loader/gltf/ext/GltfBufferViewExtension.js +54 -54
  1673. package/src/shade/renderer/loader/gltf/ext/GltfBufferViewExtensionSet.js +109 -109
  1674. package/src/shade/renderer/loader/gltf/ext/gltf_apply_buffer_view_extensions.js +69 -69
  1675. package/src/shade/renderer/loader/gltf/ext/gltf_buffer_view_extensions.js +22 -22
  1676. package/src/shade/renderer/loader/gltf/ext/gltf_geometry_extensions.js +39 -39
  1677. package/src/shade/renderer/loader/gltf/ext/meshopt/MeshoptBufferViewExtension.js +301 -301
  1678. package/src/shade/renderer/loader/gltf/gltf_component_type.js +223 -223
  1679. package/src/shade/renderer/loader/gltf/gltf_node_world_matrices.js +137 -137
  1680. package/src/shade/renderer/loader/gltf/gltf_parse_container.js +241 -241
  1681. package/src/shade/renderer/loader/gltf/gltf_read_accessor.js +294 -294
  1682. package/src/shade/renderer/loader/gltf/gltf_used_image_indices.js +49 -49
  1683. package/src/shade/renderer/loader/gltf/load_gltf.js +1253 -1253
  1684. package/src/shade/renderer/loader/gltf/tiny-gltf.js +646 -646
  1685. package/src/shade/renderer/loader/usd/UsdError.js +33 -33
  1686. package/src/shade/renderer/loader/usd/UsdExtensionRegistry.js +69 -69
  1687. package/src/shade/renderer/loader/usd/load_usd.js +226 -226
  1688. package/src/shade/renderer/loader/usd/parse_usda.js +931 -931
  1689. package/src/shade/renderer/loader/usd/unpack_usdz.js +106 -106
  1690. package/src/shade/renderer/loader/usd/usd_build_scene.js +658 -658
  1691. package/src/shade/renderer/loader/usd/usd_compose_transform.js +258 -258
  1692. package/src/shade/renderer/loader/usd/usd_decode_image.js +384 -384
  1693. package/src/shade/renderer/loader/usd/usd_lz4.js +182 -182
  1694. package/src/shade/renderer/loader/usd/usd_triangulate.js +159 -159
  1695. package/src/shade/renderer/material/ShadeMaterial.d.ts +2 -2
  1696. package/src/shade/renderer/material/ShadeMaterial.js +2 -2
  1697. package/src/shade/renderer/material/standard/fragment_gbuffer.js +334 -334
  1698. package/src/shade/renderer/material/standard/fragment_gbuffer_vt.js +169 -169
  1699. package/src/shade/renderer/material/standard/material_gbuffer_vertex.js +45 -45
  1700. package/src/shade/renderer/material/standard_material_to_struct.js +23 -23
  1701. package/src/shade/renderer/particles/prototypeParticleSystem.js +450 -450
  1702. package/src/shade/renderer/particles/runtime/spawn_request_encoding.js +52 -52
  1703. package/src/shade/renderer/pool/TransientResourcePool.js +392 -392
  1704. package/src/shade/renderer/postprocess/bloom/bloom_chain_resolution.js +29 -29
  1705. package/src/shade/renderer/postprocess/bloom/graph_bloom_downsample.js +109 -109
  1706. package/src/shade/renderer/postprocess/bloom/graph_postprocess_bloom.js +155 -155
  1707. package/src/shade/renderer/postprocess/cas/shader_ffx_rcas.js +114 -114
  1708. package/src/shade/renderer/postprocess/dof/DOF_AUTOFOCUS_PARAMS_STRUCT.js +25 -25
  1709. package/src/shade/renderer/postprocess/dof/DOF_FOCUS_STATE_STRUCT.js +23 -23
  1710. package/src/shade/renderer/postprocess/dof/DOF_PRESETS.js +27 -27
  1711. package/src/shade/renderer/postprocess/dof/chunk_dof_coc_band_diameter.js +41 -41
  1712. package/src/shade/renderer/postprocess/dof/chunk_dof_coc_band_radius.js +32 -32
  1713. package/src/shade/renderer/postprocess/dof/chunk_dof_coc_coefficient.js +26 -26
  1714. package/src/shade/renderer/postprocess/dof/compute_dof_autofocus_params.js +64 -64
  1715. package/src/shade/renderer/postprocess/dof/compute_dof_uniform.js +54 -54
  1716. package/src/shade/renderer/postprocess/dof/gather/DOF2_CONFIG.js +91 -91
  1717. package/src/shade/renderer/postprocess/dof/gather/DOF2_PRESETS.js +9 -9
  1718. package/src/shade/renderer/postprocess/dof/gather/DOF2_SETTINGS_STRUCT.js +66 -66
  1719. package/src/shade/renderer/postprocess/dof/gather/DOF2_TEXTURE_FORMAT.js +22 -22
  1720. package/src/shade/renderer/postprocess/dof/gather/DepthOfFieldU.js +292 -292
  1721. package/src/shade/renderer/postprocess/dof/gather/chunk_dof2_coc_to_near_far.js +25 -25
  1722. package/src/shade/renderer/postprocess/dof/gather/chunk_dof2_gather_weight.js +30 -30
  1723. package/src/shade/renderer/postprocess/dof/gather/compute_dof2_uniform.js +54 -54
  1724. package/src/shade/renderer/postprocess/dof/gather/graph_postprocess_dof2.js +194 -194
  1725. package/src/shade/renderer/postprocess/dof/gather/shader_dof2_fill.js +68 -68
  1726. package/src/shade/renderer/postprocess/dof/gather/shader_dof2_gather_far.js +140 -140
  1727. package/src/shade/renderer/postprocess/dof/gather/shader_dof2_gather_near.js +142 -142
  1728. package/src/shade/renderer/postprocess/dof/gather/shader_dof2_recombine.js +99 -99
  1729. package/src/shade/renderer/postprocess/dof/gather/shader_dof2_setup.js +102 -102
  1730. package/src/shade/renderer/postprocess/dof/gather/shader_dof2_tile_dilate.js +75 -75
  1731. package/src/shade/renderer/postprocess/dof/gather/shader_dof2_tile_max.js +73 -73
  1732. package/src/shade/renderer/postprocess/dof/raymarch/DOF_SETTINGS_STRUCT.js +49 -49
  1733. package/src/shade/renderer/postprocess/dof/raymarch/DepthOfField.js +298 -298
  1734. package/src/shade/renderer/postprocess/dof/raymarch/graph_postprocess_dof.js +244 -244
  1735. package/src/shade/renderer/postprocess/dof/raymarch/shader_dof_composite.js +91 -91
  1736. package/src/shade/renderer/postprocess/dof/raymarch/shader_dof_firefly_cleanup.js +61 -61
  1737. package/src/shade/renderer/postprocess/dof/raymarch/shader_dof_prefilter.js +90 -90
  1738. package/src/shade/renderer/postprocess/dof/raymarch/shader_dof_raymarch_compute.js +267 -267
  1739. package/src/shade/renderer/postprocess/dof/raymarch/shader_dof_slice_compute.js +271 -271
  1740. package/src/shade/renderer/postprocess/dof/raymarch/shader_dof_tile_dilate.js +70 -70
  1741. package/src/shade/renderer/postprocess/dof/raymarch/shader_dof_tile_max.js +72 -72
  1742. package/src/shade/renderer/postprocess/dof/shader_dof_autofocus.js +99 -99
  1743. package/src/shade/renderer/postprocess/gtao/generate_magic_square_noise.js +51 -51
  1744. package/src/shade/renderer/postprocess/motion_blur/MotionBlur.js +56 -56
  1745. package/src/shade/renderer/postprocess/upscale-dither/NOTES.md +78 -78
  1746. package/src/shade/renderer/postprocess/upscale-dither/UpscaleDither.js +95 -95
  1747. package/src/shade/renderer/postprocess/upscale-dither/fragment_shader_upscale_dither.js +204 -204
  1748. package/src/shade/renderer/rasterize/bucket/construct_primitive_state.js +58 -58
  1749. package/src/shade/renderer/rasterize/fast/graph_rasterize_scene_fast.js +230 -230
  1750. package/src/shade/renderer/rasterize/native/graph_rasterize_meshes_alpha_tested.js +278 -278
  1751. package/src/shade/renderer/rasterize/native/oit/chunk_material_side_should_draw.js +26 -26
  1752. package/src/shade/renderer/rasterize/native/oit/shader_oit_resolve_moments_ibl.js +178 -178
  1753. package/src/shade/renderer/rasterize/standard/graph_rasterize_partial_opaque.js +235 -235
  1754. package/src/shade/renderer/rasterize/standard/graph_rasterize_remaining_opaque.js +208 -208
  1755. package/src/shade/renderer/rasterize/standard/graph_rasterize_scene.js +206 -206
  1756. package/src/shade/renderer/scene/DynamicMesh.js +70 -70
  1757. package/src/shade/renderer/scene/DynamicMeshBatch.js +90 -90
  1758. package/src/shade/renderer/scene/GPUSceneContext.js +901 -901
  1759. package/src/shade/renderer/scene/SceneNodeBinding.js +56 -56
  1760. package/src/shade/renderer/scene/SkinnedMesh.js +55 -55
  1761. package/src/shade/renderer/scene/TransformAuthority.js +33 -33
  1762. package/src/shade/renderer/scene/optimization/deduplicate_geometries.js +38 -38
  1763. package/src/shade/renderer/scene/optimization/deduplicate_images.js +93 -93
  1764. package/src/shade/renderer/scene/optimization/geometry/internal/anchor_grow_topological.d.ts.map +1 -1
  1765. package/src/shade/renderer/scene/optimization/geometry/internal/anchor_grow_topological.js +12 -3
  1766. package/src/shade/renderer/scene/optimization/geometry/internal/match_refit_validate.js +127 -127
  1767. package/src/shade/renderer/scene/optimization/geometry/internal/match_solve_transform.js +492 -492
  1768. package/src/shade/renderer/scene/optimization/geometry/internal/match_validate_topology.js +3 -3
  1769. package/src/shade/renderer/scene/optimization/geometry/internal/seed_correspondence_from_window.js +3 -3
  1770. package/src/shade/renderer/scene/optimization/optimize_texture_set.js +81 -81
  1771. package/src/shade/renderer/scene/optimization/source_to_sampler2d.js +21 -21
  1772. package/src/shade/renderer/scene/serialization/STANDARD_MATERIAL_SERIALIZED_BYTE_SIZE.js +12 -12
  1773. package/src/shade/renderer/scene/serialization/deserialize_scene.d.ts.map +1 -1
  1774. package/src/shade/renderer/scene/serialization/deserialize_scene.js +0 -2
  1775. package/src/shade/renderer/shader/chunk/geometry/quaternion/dual/chunk_dual_quat_transform_point.js +26 -26
  1776. package/src/shade/renderer/shader/chunk/noise/chunk_interleaved_gradient_noise.js +20 -20
  1777. package/src/shade/renderer/shader/chunk/spherical_harmonics/dering/chunk_sh2_dering_optimize_positive.js +61 -61
  1778. package/src/shade/renderer/shader/chunk/texture/chunk_texture_filter_fxaa_lite.js +225 -225
  1779. package/src/shade/renderer/shader/chunk/texture/dither/chunk_dither_color_8bit_gradient_noise.js +25 -25
  1780. package/src/shade/renderer/shadow/map/DirectionalLightShadowmap.js +384 -384
  1781. package/src/shade/renderer/shadow/map/PointLightShadowmap.js +105 -105
  1782. package/src/shade/renderer/shadow/map/SpotLightShadowmap.js +63 -63
  1783. package/src/shade/renderer/shadow/map/TetrahedronPointLightShadowmap.js +89 -89
  1784. package/src/shade/renderer/shadow/map/shadowmap_update_score.js +232 -232
  1785. package/src/shade/renderer/texture/GPUTextureAllocator.js +85 -85
  1786. package/src/shade/renderer/texture/texture_pool_ops.js +171 -171
  1787. package/src/shade/renderer/texture/virtual/PageTableQuadAllocator.js +253 -253
  1788. package/src/shade/renderer/texture/virtual/README.md +276 -276
  1789. package/src/shade/renderer/texture/virtual/VTPageTable.js +330 -330
  1790. package/src/shade/renderer/texture/virtual/VTPhysicalCache.js +349 -349
  1791. package/src/shade/renderer/texture/virtual/VT_CONSTANTS.js +154 -154
  1792. package/src/shade/renderer/texture/virtual/VT_STACK_INFO_STRUCT.js +20 -20
  1793. package/src/shade/renderer/texture/virtual/VirtualTextureManager.js +1276 -1276
  1794. package/src/shade/renderer/texture/virtual/VirtualTextureStack.js +106 -106
  1795. package/src/shade/renderer/texture/virtual/build_chunk_vt_lookup.js +150 -150
  1796. package/src/shade/renderer/texture/virtual/build_shader_vt_feedback.js +232 -232
  1797. package/src/shade/renderer/texture/virtual/chunk_vt_request_key.js +28 -28
  1798. package/src/shade/renderer/texture/virtual/source/VTSourceImage.js +207 -207
  1799. package/src/shade/renderer/texture/virtual/source/VTSourceProcedural.js +135 -135
  1800. package/src/shade/renderer/texture/virtual/source/VTSourceTiled.js +132 -132
  1801. package/src/shade/renderer/water/GPUWaterRenderer.js +167 -167
  1802. package/src/shade/renderer/water/shader_water.js +246 -246
  1803. package/src/shade/renderer/water/water_height_texture_from_sampler.js +56 -56
  1804. package/src/shade/vitest.setup.mjs +89 -89
  1805. package/src/shade/wgsl/validate_wgsl_source.js +316 -316
  1806. package/src/view/controller/controls/NumericIntervalControl.js +90 -90
  1807. package/src/view/controller/controls/Vector2Control.js +83 -83
  1808. package/src/view/controller/controls/Vector3Control.js +90 -90
  1809. package/src/view/elements/Group.js +56 -56
  1810. package/src/view/elements/drawCheckers.js +33 -33
  1811. package/src/view/elements/windrose/WindRoseDiagram.js +369 -369
  1812. package/src/view/graphics/WebGLContextFailureView.js +76 -76
  1813. package/src/view/layout/DockPanel.js +51 -51
  1814. package/src/view/layout/SplitView.js +365 -365
  1815. package/src/view/layout/show_popup_menu.js +81 -81
  1816. package/src/view/minimap/MinimapCanvasView.js +411 -411
  1817. package/src/view/minimap/MinimapImageCache.js +88 -88
  1818. package/src/view/minimap/MinimapMarkerCollection.d.ts +0 -1
  1819. package/src/view/minimap/MinimapMarkerCollection.d.ts.map +1 -1
  1820. package/src/view/minimap/MinimapMarkerCollection.js +180 -180
  1821. package/src/view/minimap/dom/MinimapCameraView.js +206 -206
  1822. package/src/view/minimap/minimap_focus_area.js +97 -97
  1823. package/editor/process/symbolic/SocketsSymbolicDisplay.js +0 -192
  1824. package/src/core/geom/3d/topology/struct/binary/io/moved_tests.txt +0 -37
  1825. package/src/engine/ecs/attachment/Attachment.d.ts +0 -40
  1826. package/src/engine/ecs/attachment/Attachment.d.ts.map +0 -1
  1827. package/src/engine/ecs/attachment/Attachment.js +0 -57
  1828. package/src/engine/ecs/attachment/AttachmentBinding.d.ts +0 -28
  1829. package/src/engine/ecs/attachment/AttachmentBinding.d.ts.map +0 -1
  1830. package/src/engine/ecs/attachment/AttachmentBinding.js +0 -41
  1831. package/src/engine/ecs/attachment/AttachmentSystem.d.ts +0 -61
  1832. package/src/engine/ecs/attachment/AttachmentSystem.d.ts.map +0 -1
  1833. package/src/engine/ecs/attachment/AttachmentSystem.js +0 -307
  1834. package/src/engine/ecs/attachment/BoneAttachmentBinding.d.ts +0 -27
  1835. package/src/engine/ecs/attachment/BoneAttachmentBinding.d.ts.map +0 -1
  1836. package/src/engine/ecs/attachment/BoneAttachmentBinding.js +0 -69
  1837. package/src/engine/ecs/attachment/TransformAttachmentBinding.d.ts +0 -4
  1838. package/src/engine/ecs/attachment/TransformAttachmentBinding.d.ts.map +0 -1
  1839. package/src/engine/ecs/attachment/TransformAttachmentBinding.js +0 -51
  1840. package/src/engine/ecs/sockets/AttachmentSocket.d.ts +0 -32
  1841. package/src/engine/ecs/sockets/AttachmentSocket.d.ts.map +0 -1
  1842. package/src/engine/ecs/sockets/AttachmentSocket.js +0 -60
  1843. package/src/engine/ecs/sockets/AttachmentSocketType.d.ts +0 -6
  1844. package/src/engine/ecs/sockets/AttachmentSocketType.d.ts.map +0 -1
  1845. package/src/engine/ecs/sockets/AttachmentSocketType.js +0 -8
  1846. package/src/engine/ecs/sockets/AttachmentSockets.d.ts +0 -57
  1847. package/src/engine/ecs/sockets/AttachmentSockets.d.ts.map +0 -1
  1848. package/src/engine/ecs/sockets/AttachmentSockets.js +0 -130
  1849. package/src/engine/ecs/sockets/BoneAttachmentSocket.d.ts +0 -29
  1850. package/src/engine/ecs/sockets/BoneAttachmentSocket.d.ts.map +0 -1
  1851. package/src/engine/ecs/sockets/BoneAttachmentSocket.js +0 -56
  1852. package/src/engine/ecs/sockets/serialization/AttachmentSocketsAssetLoader.d.ts +0 -6
  1853. package/src/engine/ecs/sockets/serialization/AttachmentSocketsAssetLoader.d.ts.map +0 -1
  1854. package/src/engine/ecs/sockets/serialization/AttachmentSocketsAssetLoader.js +0 -19
  1855. package/src/shade/renderer/animation/_dump_resolve.d.mts +0 -2
  1856. package/src/shade/renderer/animation/_dump_resolve.d.mts.map +0 -1
  1857. package/src/shade/renderer/animation/_dump_resolve.mjs +0 -5
  1858. package/src/shade/renderer/scene/optimization/geometry/internal/bt_face_loop_helpers.d.ts +0 -32
  1859. package/src/shade/renderer/scene/optimization/geometry/internal/bt_face_loop_helpers.d.ts.map +0 -1
  1860. package/src/shade/renderer/scene/optimization/geometry/internal/bt_face_loop_helpers.js +0 -77
  1861. package/src/shade/renderer/scene/optimization/geometry/internal/bt_find_edge_between.d.ts +0 -12
  1862. package/src/shade/renderer/scene/optimization/geometry/internal/bt_find_edge_between.d.ts.map +0 -1
  1863. package/src/shade/renderer/scene/optimization/geometry/internal/bt_find_edge_between.js +0 -35
  1864. package/src/shade/renderer/scene/serialization/linear_to_pq.d.ts +0 -2
  1865. package/src/shade/renderer/scene/serialization/linear_to_pq.d.ts.map +0 -1
  1866. package/src/shade/renderer/scene/serialization/linear_to_pq.js +0 -14
  1867. package/src/shade/renderer/scene/serialization/pq_to_linear.d.ts +0 -9
  1868. package/src/shade/renderer/scene/serialization/pq_to_linear.d.ts.map +0 -1
  1869. package/src/shade/renderer/scene/serialization/pq_to_linear.js +0 -30
@@ -1,1094 +1,1120 @@
1
- # Physics engine — state of play
2
-
3
- Tracker for what's built, what's pending, and what's deferred.
4
-
5
- ---
6
-
7
- ## Context
8
-
9
- Deterministic JS rigid-body physics engine for the meep ECS. Target: game
10
- scenarios with up to millions of mostly-sleeping bodies, deterministic replays
11
- for netcode and reproducible debugging, broad shape coverage for common game
12
- collisions. Pure JS — no WASM, no SIMD, no worker threads.
13
-
14
- Architectural references for design choices:
15
- - **Jolt** — pre-allocated body pool, active-list iteration, two-tree
16
- broadphase (static + dynamic).
17
- - **Bullet** — `btPersistentManifold` cache layout with up to 4 points.
18
- - **Box2D / Catto** — sequential impulse with warm-starting, Sutherland-Hodgman
19
- face clipping for box-box.
20
-
21
- ---
22
-
23
- ## Done
24
-
25
- ### Foundations
26
- - `RigidBody`, `Collider`, `BodyKind`, `RigidBodyFlags`, `ColliderFlags`,
27
- `SleepState`, `PhysicsEvents`.
28
- - `BodyStorage`: SoA pool, generation-tracked stable IDs, dense awake list,
29
- min-heap free for deterministic ID reuse.
30
- - `PhysicsSystem`: full public API surface (gravity, force/impulse with and
31
- without application point, torque, velocity setter, wake/sleep, contact
32
- filter callback).
33
- - Binary serialization adapters for `RigidBody` and `Collider` (transient
34
- runtime state deliberately excluded).
35
- - `PairUint32Map`: open-addressed Robin Hood + Fibonacci hash for the
36
- pair → manifold-slot index (the one new collection added to `core/collection/`).
37
-
38
- ### Pipeline (`PhysicsSystem.fixedUpdate`)
39
- 1. Velocity integration (semi-implicit Euler, linear + angular, gravity,
40
- damping, world-frame inverse-inertia for torque)
41
- 2. Per-collider broadphase refit with fat AABB (Box2D-style velocity-padded
42
- slack)
43
- 3. Pair generation: per-leaf query against both BVHs (static + dynamic),
44
- canonical `(min, max)` pairs, dedup via manifold touched flag
45
- 4. Wake propagation for sleeping bodies in the pair list
46
- 5. Narrowphase cross-product over collider lists
47
- 6. Sequential-impulse solver (Catto-style, warm-start, friction, Baumgarte)
48
- 7. Position integration (linear + quaternion)
49
- 8. Sleep test (per-body velocity² below threshold for ≥ 0.5 s)
50
- 9. Manifold diff → `ContactBegin` / `Stay` / `End` event dispatch
51
- 10. `manifolds.advance_frame()` — roll touched bits, evict grace-expired slots
52
-
53
- ### Shape coverage
54
- | Pair | Path | Manifold |
55
- |---|---|---|
56
- | sphere-sphere | closed-form | 1 point |
57
- | sphere-box | closed-form (handles centre-inside-box) | 1 point |
58
- | capsule-sphere | point-on-segment closed-form | 1 point |
59
- | capsule-capsule | segment-segment closest pair | 1 point |
60
- | capsule-box | iterative segment-vs-OBB (primary) + cap-centre sphere-vs-OBB at each endpoint | up to 3 |
61
- | box-box face-face | SAT + Sutherland-Hodgman clipping | up to 4 |
62
- | box-box edge-edge | SAT + segment-segment closest-pair | 1 point |
63
- | sphere / box / capsule × concave (heightmap, mesh) | closed-form `*_triangle_contact` per triangle via decomposition dispatcher | up to a few points per triangle (deepest wins) |
64
- | other convex × concave | per-triangle GJK + EPA via decomposition dispatcher | 1 point per triangle |
65
- | anything else | GJK + EPA, MPR fallback on EPA non-convergence | 1 point |
66
-
67
- ### Non-convex shapes
68
- - **`is_convex` flag** on `AbstractShape3D.prototype` (default `true`).
69
- Overridden to `false` on `HeightMapShape3D`, `MeshShape3D`, `UnionShape3D`.
70
- `TransformedShape3D` inherits via getter that reads the wrapped subject.
71
- - **`HeightMapShape3D`** — orientation-vector + `Sampler2D`-backed terrain
72
- shape. Heights sampled via `sampleChannelCatmullRomUV` (matching the
73
- terrain system's geometry construction). Compute_bounding_box,
74
- contains_point, signed_distance, nearest_point_on_surface all
75
- implemented; `support` throws (non-convex by construction).
76
- - **`Triangle3D`** — buffer-flyweight convex shape. `bind(buffer, offset)`
77
- repoints at 9 consecutive floats in an external Float64Array. Zero
78
- allocation per emission; used by the decomposition path.
79
- - **Triangle decomposition machinery** under
80
- `engine/physics/narrowphase/decomposition/`:
81
- - `TRIANGLE_FLOAT_STRIDE = 10` per triangle (`vA.xyz`, `vB.xyz`,
82
- `vC.xyz`, `feature_id`).
83
- - `heightmap_enumerate_triangles(out, offset, shape, ...aabb)` —
84
- Arvo-projects the convex's AABB into heightmap-local, intersects
85
- with the footprint to derive a cell range, emits 2 triangles per
86
- cell with stable feature_ids.
87
- - `mesh_enumerate_triangles(out, offset, shape, ...aabb)` — linear
88
- O(N) scan over `MeshShape3D.indices` with tight per-triangle AABB
89
- filtering. feature_id = triangle index.
90
- - `aabb3_transform_oriented_inverse(out, world_aabb, pos, rot)` (now in `core/geom/3d/aabb/`) — 8-corner
91
- projection of a world AABB into a body's local frame.
92
- - `decompose_to_triangles(...)` — dispatcher switching on shape
93
- type marker.
94
- - **Narrowphase concave dispatch** in `narrowphase_step.js`: detects
95
- `is_convex === false`, computes convex's world AABB, projects to
96
- concave's local frame, decomposes, per-triangle GJK + EPA with
97
- one-sided face-normal rejection and contact-normal dedup. Concave-vs-
98
- concave dynamic pairs are explicitly refused.
99
-
100
- ### Solver
101
- - Sequential impulse with warm-starting (10 velocity iterations by default).
102
- - Coulomb friction with disk-clamped tangent impulses.
103
- - Baumgarte position correction folded into the velocity solve.
104
- - Full angular Jacobian (`I_w⁻¹ = R · diag · R^T`) and angular impulse
105
- application.
106
- - Public force/impulse-at-point API (`applyForceAt`, `applyImpulseAt`,
107
- `applyTorque`).
108
-
109
- ### Sleep + events
110
- - Per-island **atomic sleep**: an island sleeps when `max(|v|² + |ω|²)`
111
- across all members stays below the threshold long enough; the whole
112
- island sleeps in the same frame. Replaces the per-body chatter on
113
- weakly-connected piles.
114
- - **Atomic wake**: members of a sleeping island are threaded into a
115
- circular doubly-linked list (`sleep_group_next` / `sleep_group_prev`);
116
- waking any one member walks the chain and wakes the rest in the same
117
- call. A 100-block stack hit at the base wakes top-down in one frame
118
- rather than over 100 frames of broadphase propagation.
119
- - `DisableSleep` on any island member exempts the whole island.
120
- - ContactBegin / Stay / End buffer + dispatch through the per-entity
121
- `dataset.sendEvent(entity, PhysicsEvents.ContactBegin, ...)` channel — the
122
- sole contact-event path, delivered to each entity of the pair when a
123
- dataset is attached.
124
-
125
- ### Islands
126
- - **Union-find** with path halving + union by min-index over the awake-body
127
- + touched-contact graph (`core/collection/union-find/union_find.js`).
128
- - **`IslandBuilder`** produces deterministic CSR-style output: bodies and
129
- manifold slots grouped by island, sorted ascending within and across
130
- islands. Static / kinematic bodies are constraint anchors only — they
131
- don't merge islands, so disjoint piles on the same floor are separate
132
- islands.
133
- - **Islands feed the sleep test + grouping, not a per-island solver loop.**
134
- The TGS contact solver flattens every island's contacts into one
135
- Gauss-Seidel sweep (`solver/solve_contacts.js`) — islands share no bodies,
136
- so a single flat sweep is identical to per-island sweeps. The partition is
137
- still rebuilt every step and consumed by the atomic-island sleep test and the
138
- joint/contact island grouping; it is also the natural unit for a future
139
- worker-based parallel solve (see Performance / Scale). (Earlier revisions ran
140
- the solver per island; the TGS rewrite flattened it — same result, simpler
141
- loop.)
142
-
143
- ### Compound bodies
144
- - A body has 0..N attached colliders. Each collider has its own world
145
- transform and its own BVH leaf.
146
- - Same-entity colliders, child-entity colliders (via `ParentEntity`), or
147
- hybrids all supported.
148
- - `ColliderObserverSystem` auto-attaches colliders via the dataset when
149
- paired with `PhysicsSystem` in an EntityManager.
150
- - Narrowphase runs the cross-product over both bodies' collider lists per
151
- body-pair, accumulates candidates, reduces to ≤4 contacts by
152
- depth + spread.
153
-
154
- ### Public queries
155
- - `raycast(origin, dir, max_dist, filter?)` — nearest hit across both trees,
156
- **refined to the true shape surface** (narrowphase). `result.t` /
157
- `result.normal` are exact for sphere / box / capsule / mesh / heightmap
158
- colliders (per-leaf analytic ray tests + triangle Möller–Trumbore for
159
- concave); composite convex shapes fall back to the broadphase AABB hit. A ray
160
- crossing a fat leaf AABB but missing the true shape is correctly a miss.
161
- - `shapeCast(ray, shape, rotation, result, filter?)` broadphase swept
162
- AABB against both BVHs; per-candidate AABB-slab interval narrowing,
163
- coarse step over the narrowed window, GJK bisection to time-of-impact.
164
- Output normal is the true contact-surface normal at the kiss point,
165
- recovered by re-running GJK + EPA at `best_t` on the winning candidate.
166
- Falls back to `-ray.direction` only on EPA degeneracies (NaN / zero
167
- depth). Tests cover axis-aligned, off-axis, and oblique cube-vs-cube;
168
- sphere-vs-smooth-shape near-tangent has documented angular tolerance
169
- bands inherited from EPA on smooth supports.
170
- - `overlap(shape, position, rotation, output, output_offset, filter?)`
171
- broadphase + per-candidate GJK overlap detection. Writes body_ids
172
- into a caller-sized buffer; returns count. Convex query shapes only
173
- (concave throws). Concave candidates routed through the per-triangle
174
- decomposition path. Designed for speculative kinematic queries on
175
- kinematic bodies (character controllers, AOE selection).
176
-
177
- ### Standalone narrowphase utilities
178
- - `deepest_pair_penetration(out_normal, shapeA, posA, rotA, shapeB, posB,
179
- rotB)` (exported from `narrowphase_step.js`) — runs the **same**
180
- `dispatch_pair` the contact solver consumes for one posed shape pair and
181
- returns the DEEPEST contact's depth + world normal (B → A). The single
182
- source of truth for "minimum-translation between two posed shapes", reused by
183
- `compute_penetration` (and available to any other query).
184
- - `compute_penetration(out_direction, shape_a, pos_a, rot_a, shape_b,
185
- pos_b, rot_b)` non-system geometry primitive: positive penetration
186
- depth + outward direction (B A convention) on overlap, 0 otherwise.
187
- **Hardened** — delegates to `deepest_pair_penetration`, so it is correct
188
- (not "correct sometimes") for every shape pair the engine can build:
189
- - sphere / box / capsule pairs exact closed-form (box-box via SAT, so a
190
- small body resting on a large floor reports the few-cm near-face overlap,
191
- NOT the metres-deep "exit through the far side" that MPR's centroid-seeded
192
- portal used to return);
193
- - general convex pairs GJK + EPA (exact for polytopes; curved shapes never
194
- reach it they have closed forms);
195
- - convex × concave → triangle decomposition + the closed-form per-triangle
196
- solvers, bounded to each triangle's true 2-D extent (the old closed-mesh
197
- side-face over-report is gone).
198
- The previous per-triangle half-space test is retained ONLY as a recovery
199
- fallback for the one case the one-sided closed forms can't resolve: a convex
200
- shape that has fully tunnelled to the *inner* side of a concave surface (a
201
- depenetration query must still push it back out exact for heightmap terrain,
202
- a valid outward push for closed meshes). Concave × concave throws (M×N
203
- triangle pairs out of scope). The spec asserts an "applying out_direction ×
204
- depth separates the shapes" invariant across every convex+convex pair type and
205
- convex+concave, plus exact per-type depths and the small-box-on-huge-floor
206
- regression (3 m 0.05 m).
207
-
208
- ### Determinism the reconstruction contract (H-series, landed)
209
- Engine outcomes are a pure function of **body components (keyed by entity
210
- id) + the exportable solver-cache blob** — never of allocation history
211
- (body slots, manifold slots, joint ids, awake-list order, BVH shape,
212
- broadphase pad history). A world rebuilt from a snapshot onto a fresh
213
- engine, or by remove-all/re-add on a live one continues **bit-identically**
214
- to the engine that kept running. Verified by `determinism.spec.js`
215
- (T1 lockstep repeatability; T2 reconstruction equivalence incl. generation
216
- churn and pad-history wake timing).
217
-
218
- - Entity-canonical pair A/B roles assigned in the broadphase
219
- (`generate_pairs`) roles feed witness sides, normal orientation and the
220
- tangent basis, so they must derive from the cross-engine identity.
221
- - Outcome-coupled iteration orders are entity-keyed: island contacts
222
- (IslandBuilder), joints (`__joints_sorted`), contact events
223
- (`ContactEventBuffer.sort_canonical` + dispatch normal mirror), CCD pass.
224
- - `sleepState` + `sleep_timer` are persistent body state (adapter v1 +
225
- 0→1 upgrader); `link()` honours them, so restored sleepers stay asleep.
226
- - Stage-0 wake reads persistent manifolds (`prev_touched`), never the
227
- transient pair list; the precautionary pre-contact wake is an
228
- edge-triggered swept-tight-AABB proximity predicate (`FLAG_PROX`),
229
- a pure function of body state.
230
- - `persistence/solver_caches.js`: entity-keyed export/import of manifolds
231
- (warm-start impulses + lifecycle bits), joint `dofImpulse`, sleep-group
232
- chains. Identical worlds serialize to identical bytes.
233
- - Foundations that predate the H-series: min-heap free lists for
234
- deterministic slot reuse, no `Math.random` in the step, direct
235
- typed-array writes on hot paths (Transform writes still go through
236
- `set()` external systems subscribe).
237
- - Same-runtime bit-exact; cross-runtime is a known future seam (see
238
- Backlog Reconstruction & determinism).
239
-
240
- ### Migration
241
- - `Motion` / `MotionSystem` / `MotionSerializationAdapter` relocated from
242
- the meep core (`engine/ecs/`) to the game-domain layer
243
- (`mir-engine/model/game/ecs/`). meep no longer ships the legacy shim.
244
-
245
- ### Alternative narrowphase: MPR
246
- - `core/geom/3d/gjk/mpr.js` Minkowski Portal Refinement (XenoCollide,
247
- Snethen GDC 2009). Single-pass overlap test + MTV computation,
248
- output convention matches EPA so it's drop-in compatible at any
249
- narrowphase call site. Tends to converge in 5–15 iterations on
250
- smooth shapes where EPA stalls (the polytope-on-curved-surface
251
- failure mode the torus-knot reproducer exercised). **Wired as the EPA
252
- non-convergence fallback** in `narrowphase_step` at both the body-level
253
- and per-triangle GJK+EPA paths: when EPA returns a non-positive / non-finite
254
- depth, MPR is tried before giving up. `shape_cast` and `compute_penetration`
255
- use it for the same reason.
256
-
257
- ### Bonus utilities
258
- - `core/geom/3d/line/line3_closest_points_segment_segment.js` generally
259
- useful 3D segment-segment closest-pair via Ericson §5.1.9.
260
- - `core/collection/PairUint32Map.js` — non-allocating
261
- `Map<(u32, u32) → u32>` with Robin Hood + Fibonacci hash.
262
-
263
- ---
264
-
265
- ## Limitations / Known caveats
266
-
267
- - **Multi-collider material precision** — *resolved for contact materials.* The
268
- narrowphase now combines the specific source-collider pair's friction /
269
- restitution per contact and stores them in the manifold (CONTACT_STRIDE
270
- offsets 14/15); the solver reads them per contact, so mixed-material compound
271
- bodies are accurate (regression test: an asymmetric-friction body yaws when
272
- shoved). Still primary-collider only: the contact-filter callback's
273
- `colliderA/B` arguments and the body-level sensor / concave-dispatch flags —
274
- a smaller follow-up.
275
- - **EPA on smooth shapes**: degenerates (no flat face to converge on).
276
- Mitigated by closed-form paths for sphere/cube/capsule pairs and by the
277
- **MPR fallback** on EPA non-convergence; exotic convex shapes vs spheres can
278
- still occasionally fail if both EPA and MPR degenerate.
279
- - **EPA on `Triangle3D`** *resolved.* The concave dispatch now uses the
280
- closed-form `sphere_triangle_contact` / `box_triangle_contact` /
281
- `capsule_triangle_contact` solvers (P1.1a–c) instead of per-triangle GJK+EPA
282
- for those primitives, so a sphere/box/capsule on a heightmap or mesh decelerates
283
- and settles correctly; the `narrowphase_concave.spec.js` "drop and settle"
284
- cases and the mesh torus-knot settle test are **un-skipped**. Per-triangle
285
- GJK+EPA remains only as the fallback for *other* convex shapes vs triangles.
286
- (`compute_penetration` now routes through that same dispatch via
287
- `deepest_pair_penetration` see *Standalone narrowphase utilities* instead
288
- of its old half-space pre-test; the half-space test survives only as a
289
- tunnel-recovery fallback.)
290
- - **Box-box edge-edge contact**: a single point at the true closest-pair of the
291
- two edges (P3.2), not the old body-centre midpoint. This is geometrically
292
- correct — and an empirical SAT-source sweep confirms the edge-cross branch
293
- *only* fires for **transverse** edge crossings (inter-edge angle 83-90°),
294
- where two skew lines meet at a unique point. Near-parallel edge contacts
295
- cannot reach this branch (a near-parallel `edgeA × edgeB` never wins the SAT
296
- minimum) they resolve through the multi-point face-clipping path. So the
297
- once-planned "multi-point edge contact for near-parallel edges" refinement is
298
- **moot**; see the resolved Stability backlog entry.
299
- - **CCD floor only**: speculative margin via the fattened AABB prevents
300
- most tunnelling. No per-body swept shape-cast for very fast objects.
301
- - **Cross-runtime determinism is not guaranteed**: `Math.sin/cos/exp/log`
302
- are ULP-correct but not bit-exact across V8 / SpiderMonkey / JSC.
303
- - **Dynamic concave bodies under TGS** *resolved by per-substep re-detection
304
- (below); kept here for the rationale.* The substep loop normally re-derives
305
- contact geometry analytically from the per-triangle contact feature (witness
306
- anchors + normal) captured once by narrowphase and held fixed for the whole
307
- outer step. For a convex body the contact feature is stable under the small
308
- per-step motion, so this is exact; for a *dynamic concave mesh body* (e.g. a
309
- torus knot rocking on its own lobes) the supporting triangle itself changes
310
- as the body rocks, so freezing the feature would pump a little energy in and
311
- the body would rock / slowly sink instead of settling. Note this is NOT a
312
- contact-precision issue
313
- the knot already uses the exact closed-form box-triangle solver (P1.1b);
314
- the problem is purely that TGS freezes *which* feature is in contact across
315
- substeps. The common concave case a convex dynamic body on static concave
316
- terrain is unaffected (the convex side's feature is stable), and that is
317
- the only concave case the engine targets.
318
-
319
- **Interim fix (implemented): per-substep concave re-detection.** For
320
- contact pairs involving a concave body, the substep loop re-runs the
321
- concave narrowphase geometry at the current substep pose (instead of the
322
- analytic refresh that freezes the feature) and re-prepares those contacts
323
- from the fresh witness/normal/depth so the contact normal tracks the
324
- rocking body and no energy is pumped in. Convex pairs keep the cheap
325
- analytic refresh. This is ~Nx narrowphase cost on concave-involved pairs
326
- (acceptable they're rare), gated by collider convexity. Un-skips the
327
- torus-knot dynamic-settle test.
328
-
329
- **Better long-term fix: convex collision proxies (not raw concave).** Every
330
- major engine (Box2D, Jolt, PhysX, Rapier) requires dynamic bodies to be
331
- convex or convex-decomposed; raw concave meshes are static-only. The right
332
- granularity is a *few* convex pieces NOT the thousands of tets a
333
- volumetric mesher produces (tet count collider/BVH-leaf count, which
334
- explodes the broadphase for an awake body; tet meshing is for a future
335
- FEM/soft-body subsystem, not rigid collision). See the "Convex collision
336
- proxies for dynamic concave bodies" backlog item — a 3D convex hull builder
337
- (single-hull proxy covers most dynamic objects) plus an optional
338
- few-hull (V-HACD-style) decomposition. Those supersede the interim
339
- per-substep re-detection once built.
340
-
341
- ---
342
-
343
- ## Backlog (planned, in scope)
344
-
345
- ### Reconstruction & determinism (engine side done; consumers pending)
346
-
347
- The engine-side determinism contract is landed (see Done → Determinism).
348
- These are the follow-ups that build on it:
349
-
350
- - [ ] **Network late-join wiring (end-to-end).** The engine API exists
351
- (`write_solver_caches` / `read_solver_caches`, sleep-persistent
352
- components, the entity-id contract) but nothing consumes it yet.
353
- Build an end-to-end test through the network package's adapters:
354
- serialize a live world on the "server", reconstruct on a "client"
355
- (same entity ids, same link order), verify bit-identical continuation
356
- over N ticks. This validates the entity-replay assumptions
357
- (replicated entity ids, joint link order, system config transfer)
358
- before real netcode depends on them, and decides where the blob rides
359
- (snapshot message vs. component stream). System config (gravity,
360
- thresholds, iteration counts, `ccdEnabled`) is part of the snapshot
361
- contract — see `copy_system_config` in `determinism.spec.js` for the
362
- field list.
363
- - [ ] **Position correction under stacked load.** Settled penetration is
364
- pose-chaotic with a heavy tail reaching the limb radius (~0.3 m for
365
- the ragdoll's capsules) when a body settles wedged under the
366
- assembly's weight measured across seeds and under either contact-
367
- role assignment (see the 4-seed table in
368
- `PhysicsSystem.ragdoll.spec.js`). Pre-existing, not a regression.
369
- Investigation levers: position iterations (currently 1/substep),
370
- `MAX_POSITION_BIAS` clamp, depth-proportional stiffening for deep
371
- static contacts. Success metric: the cross-seed tail of
372
- `lastSecondMaxPenStatic`, not any single golden seed.
373
- - [ ] **Sleep groups derived from manifolds (redesign).** Wake-the-island
374
- currently rides on `sleep_group_next/prev` circular chains threaded at
375
- atomic-sleep time world-instance body indices that need their own
376
- section in the solver-cache blob. Deriving the wake set at wake time
377
- (flood over dormant contacting manifolds + joints among sleepers)
378
- would delete the fields, the blob section, and the group-restore step
379
- outright. Needs a per-body manifold adjacency (or an accepted
380
- O(live-slots) scan per wake call) and a perf check on wake-heavy
381
- frames. Behavioural delta to design for: force-slept touching bodies
382
- currently wake independently (separate singleton groups); a
383
- manifold-derived flood would wake them together.
384
- - [x] **KILLED pose-keyed narrowphase memo (measured 2026-06-11).** The
385
- idea: skip a pair's narrowphase when both collider poses are bit-equal
386
- to its last run (a pure cache; outcome-neutral by construction, proven
387
- with a memo-on/off lockstep test). Implemented per-ManifoldStore (a
388
- process-global cache is unsound two engines hosting the same pair
389
- ids at equal poses false-hit each other's slot content), then measured:
390
- **0.0% hit rate** in every phase of a settling column, including the
391
- final pre-sleep second. The premise is empirically false: warm-start
392
- cancellation holds *velocity* near zero, never exactly zero, so awake
393
- poses drift sub-nanometre every tick and never bit-freeze; sleeping
394
- pairs never reach narrowphase at all. Reverted as pure overhead.
395
- Dependent idea for the soft-solver work: a deterministic
396
- micro-velocity snap-to-zero at the end of the solve would both freeze
397
- resting poses bit-exact (making this memo viable) AND push
398
- forever-jiggling islands (deep-column N=20, KEVA) below the sleep
399
- threshold likely the cheaper end of the same win.
400
- - [x] **KILLED solver hot/cold row split (ceiling-measured 2026-06-11).**
401
- The idea: split the 23-float prepared-contact records by the velocity
402
- iteration's access pattern (15 hot lanes) for cache density. Measured
403
- the ceiling first instead of implementing: PADDING the stride 23→31
404
- (+35% row traffic) moved KEVA-mini by only ~2% so packing −35% can
405
- gain at most the same ~2%, far under the ≥10% gate. The velocity
406
- solve at this scale is dominated by the slot-scattered manifold reads
407
- (normal + impulses), the body-span gather/scatter and raw FLOPs, not
408
- by prepared-row density. Not implemented.
409
- - [x] **KILLED 4×4 manifold block solve (probe-measured 2026-06-11).**
410
- Pre-integration probe on the deep columns, sweeping iteration budgets:
411
- depth stability IS iteration-starved (N=30 explodes at velIters 4,
412
- holds intact at 8 the divergence is convergence failure), but the
413
- never-sleeping residual is NOT — at velIters 16 the N=20 column idles
414
- at residual 0.83, closer to rest than any other config, and still
415
- never crosses the sleep threshold; sleep behaviour is non-monotonic
416
- across budgets. That residual is a hard-constraint limit cycle
417
- hovering at the threshold a scheme property no amount of
418
- Gauss-Seidel (sequential or block) removes. A block solve would only
419
- buy the depth-stability half, which the soft-step scheme change
420
- covers anyway (Box2D v3 dropped block solving when it adopted soft
421
- step). Revisit only if post-soft-step iteration budgets remain
422
- depth-limited.
423
- - [x] **LANDED Box2D-v3 soft contact step (2026-06-11).** Contact normal
424
- rows are soft constraints: `b2MakeSoft(hertz, ζ, h)` scales (mass scale
425
- damps the velocity gain, the impulse-decay term bleeds the accumulator
426
- the critically-over-damped spring that killed the hard scheme's
427
- resting limit cycle), a penetration-bias push for penetrating contacts
428
- (clamped like the old position bias), and a per-substep RELAX pass
429
- after position integration that strips bias-injected momentum. The
430
- split-impulse position pass is skipped for contacts when soft; knobs:
431
- `contactHertz = 30` (0 = bit-exact legacy hard rows), = 10`,
432
- non-dynamic pairs at hertz, hertz clamped to an eighth of the
433
- substep rate all pinned from Box2D v3 source.
434
- A bias-free hybrid (decay without the bias push) was measured FIRST
435
- and is UNSOUND: the decay bleeds the load-carrying impulse with no
436
- restoring force, columns sank 3 m. Full v3 it is.
437
- Measured: deep columns 15/20/30 all sleep (55/57/85 ticks; 20 never
438
- slept and 30 DIVERGED under hard); KEVA-mini all-ticks median
439
- 90.8 14.8 ms (sleep arrives much sooner; awake ticks +23% from the
440
- relax sweep); 24-seed ragdoll sweep: settle-accel medians ~halved,
441
- impacts unchanged, settled-pen median 0.049 vs ~0.058 hard.
442
- Velocity-iteration cut REJECTED by measurement: KEVA-mini's
443
- convergence cliff sits between velIters 3 and 4 (2 → never sleeps,
444
- 3 sleeps far later); 4×4 stays.
445
- Known trades (re-pinned goldens, all documented in-test): settled
446
- penetration under load is the soft equilibrium droop (~0.8 mm per
447
- carried cube-weight); aligned-stack lateral creep grew ~5× (softer
448
- settle-phase normal impulses lower the friction caps — tuning
449
- candidate: stiffer staticSoftness or friction in the relax pass);
450
- ragdoll fields idle ~2× livelier (0.74 1.66 residual /704 bodies
451
- tuning candidate: align joint hertz policy with contacts). The
452
- ragdoll canonical seed moved to 0xBEEF42 (0xDEADB0D's drape pose
453
- under soft ends in an accelerating dome roll a growing-tail
454
- outlier; the sweep keeps covering it).
455
- - [ ] **Cross-process repeatability of symmetry-degenerate scenes.** The
456
- perfectly-aligned 10-cube tower's resting drift came out 42/57/73 mm
457
- across equivalent builds during the soft-step work — each value
458
- process-stable and ×3 reproducible, shifting across rebuilds whose
459
- source diffs were assertion-text only. In-process determinism is
460
- green (T1 lockstep, 8-trial reproducibility bench), so the seam is
461
- environmental (suspect: jest transform/module environment shaping
462
- sub-ulp float paths in symmetry-degenerate tie-breaks). Investigate
463
- whether genuinely identical code can produce different cross-process
464
- results (would matter for lockstep across restarts) or whether the
465
- rebuild artefacts differed; degenerate-scene goldens use CLASS bounds
466
- in the meantime.
467
- - [ ] **Cross-runtime float determinism audit.** Same-runtime bit-exactness
468
- is done; running lockstep across *different* JS engines (or versions)
469
- additionally requires auditing transcendentals `Math.sin/cos/exp/
470
- pow` are not spec-pinned (sqrt and arithmetic are IEEE-exact). Only
471
- worth doing if cross-engine lockstep becomes a real deployment target;
472
- same-engine replication does not need it.
473
-
474
- ### Solver quality (next major work)
475
-
476
- These items move the engine from "competent" to "great". TGS is the next
477
- significant solver-architecture change; joints come after, once the TGS
478
- scaffolding is in place.
479
-
480
- - **TGS (Temporal Gauss-Seidel) substepping with split-impulse** — Phases
481
- 1–3 **LANDED**. The solver is now a staged TGS pipeline
482
- (`solver/solve_contacts.js`: `prepare_contacts` per substep
483
- [`refresh_contacts` `warm_start_contacts` `solve_velocity`
484
- `solve_position`] `apply_restitution`), driven by the substep loop in
485
- `PhysicsSystem.fixedUpdate`. Defaults: `substeps = 4`,
486
- `velocityIterations = 4`, `positionIterations = 1` (all fields on
487
- `PhysicsSystem`).
488
- - **Phase 1split impulse.** Position correction runs on a per-body
489
- pseudo-velocity (`__pseudo_velocity`) folded into the pose by
490
- `integrate_position` and discarded; depth correction never
491
- contaminates persistent velocity.
492
- - **Phase 2 — one-shot restitution.** Velocity pass is pure
493
- non-penetration; restitution is a single post-loop pass driving
494
- `vn → -e·vn_approach`, gated on a running max normal impulse
495
- (`maxNormalImpulse`) so transient collisions still bounce under
496
- per-substep warm-start.
497
- - **Phase 3 — substep loop.** `substeps` sub-iterations at `h = dt/N`.
498
- Forces consumed once at full `dt` before the loop; gravity applied
499
- per substep; **warm-start replayed per substep** (the crux a
500
- per-substep impulse balances one substep of gravity, so resting
501
- stacks hold at zero velocity). Contact geometry is re-derived
502
- **analytically** each substep from frozen local witness anchors +
503
- the trusted prepare-time depth (a sign-robust delta), so narrowphase
504
- runs **once** per outer step — cheaper than the originally-planned
505
- per-substep match-and-merge refresh, and exact for convex
506
- primitives whose contact feature is stable under small motion.
507
-
508
- Results vs the single-step solver: a 100:1 mass ratio now stacks
509
- instead of crushing through (regression test added); 8-cube stacks
510
- settle to zero velocity and sleep (were impossible long-term under SI);
511
- falling-tower bench cost unchanged (~48 ms/1000 active bodies);
512
- `substeps = 1` reproduces the single-step result bit-for-bit-ish
513
- (one-frame restitution delay aside).
514
-
515
- **Hard-won lessons (for REVIEW_002):**
516
- - Warm-start MUST be per-substep, not once. Replaying a full-frame
517
- impulse once while gravity arrives per substep over-pushes resting
518
- contacts and *explodes* deep stacks. Per-substep warm-start +
519
- per-substep gravity cancel exactly at rest.
520
- - Restitution must gate on the *running max* normal impulse, not the
521
- end-of-loop value — per-substep warm-start relaxes a transient
522
- contact's `j_n` back to ~0 by the end, which would suppress the
523
- bounce.
524
- - Analytic separation re-derivation beats per-substep narrowphase
525
- for convex shapes (cheaper, no manifold-lifecycle churn) but is
526
- only as good as the frozen normal — see the concave caveat below.
527
-
528
- Follow-ups since the core landed:
529
- - [x] **Box-box SAT reference tie-break deadband** aligned cube
530
- stacks (4–10 high) now settle to zero velocity and sleep; the
531
- reference-face flip-flop that creeped/toppled them is gone.
532
- - [x] **Per-substep contact re-detection for concave pairs** — lifts
533
- the dynamic-concave-body limitation; the torus-knot dynamic-settle
534
- test is un-skipped. Concave pairs re-run narrowphase geometry each
535
- substep (`redetect_concave_contacts`); convex pairs keep the cheap
536
- analytic refresh.
537
-
538
- Remaining (Phases 4–6) now complete:
539
- - [x] Regression coverage: heavy-on-light pyramid (10× capstone on two
540
- light cubes settles + sleeps) and a ragdoll-stub (shoulder
541
- ball-socket + elbow hinge arm hangs, stays articulated, settles).
542
- - [x] REVIEW_002 retrospective — `engine/physics/REVIEW_002.md`.
543
-
544
- References: Catto 2018 ("Soft Constraints" GDC talk + the TGS
545
- follow-up); Box2D v3 source (`b2ApplyRestitution`, the substep solver
546
- stages); Rapier as the closest architectural sibling.
547
-
548
- - [x] **Constraints / joints DONE (phases 1–7 below).** One configurable
549
- 6-DOF joint (lock/free/limit/motor/spring + swing-twist cone-twist) plus
550
- the raycast vehicle. Covers chains/ropes, ragdolls, vehicles (incl.
551
- suspension), and the mechanical set (doors, pistons, welds, sliders,
552
- powered hinges/wheels). The design rationale below is kept as history; the
553
- phasing checklist records what landed. Solver/joint retrospective in
554
- `REVIEW_003.md`.
555
-
556
- Original framing (now satisfied): TGS unblocked it (joint-chain
557
- convergence is a TGS sweet spot), warm-start + per-substep + island
558
- machinery was reusable, and the SPOOK compliance dial gave spring
559
- constraints essentially for free.
560
-
561
- **Foundational work (do first): generalise the solver to constraint
562
- rows.** Today `solver/solve_contacts.js` is hard-coded to the
563
- contact-shape constraint (normal + 2 friction tangents, ≥0 clamp,
564
- restitution, penetration bias). Joints are equality / inequality
565
- constraints on relative velocity at anchors, generally bilateral
566
- (impulse may be ±) with optional limits and motors. The clean shape —
567
- and what Jolt / Box2D-v3 do is a **generic constraint row**: a
568
- Jacobian (linear + angular parts per body), an effective mass, a bias
569
- (position error × SPOOK gain, or motor target), and impulse bounds
570
- `[lo, hi]` (`[0,∞)` for a contact/limit, `(−∞,∞)` for an equality,
571
- `[−maxForce·h, +maxForce·h]` for a motor). Each joint type just fills
572
- in its rows; the existing per-body impulse-apply primitive
573
- (`apply_impulse_to_body` + `world_inverse_inertia_apply`), the
574
- per-substep warm-start, the islands, and the split-impulse / SPOOK
575
- position handling are all reused. Contacts become *one* constraint
576
- type among several rather than the hard-coded path.
577
-
578
- The specific constraint set, its use-case mapping, and per-type
579
- architecture-fit assessment are under review (see the constraints
580
- sketch). High level: ball-socket / distance / spring / weld and the
581
- grab constraint are near drop-ins on the row machinery; hinge /
582
- prismatic / cone-twist / motors / limits add angular-row + bounded-row
583
- mechanics (still within the impulse framework); raycast vehicles,
584
- conveyor surface-velocity, and gear/pulley coupling are higher-level
585
- systems or contact modifiers that sit *on top of* the primitives
586
- rather than being generic rows.
587
-
588
- **Decision: build ONE configurable 6-DOF constraint** (PhysX D6 / Jolt
589
- SixDOF), implemented mode-by-mode. The `Joint` ECS component carries
590
- `dofMode[6]` (3 linear, 3 angular) each `{locked|free|limited|spring|
591
- motor}` + per-DOF limit/spring/motor config + warm-start accumulators.
592
- Concrete joints are configs, not new code (ball-socket = lock 3 linear;
593
- hinge = lock 3 linear + 2 angular; weld = lock 6; cone-twist = lock 3
594
- linear + limit 3 angular; suspension = spring 1 linear + lock rest).
595
-
596
- Phasing:
597
- 1. [x] Constraint-row solver as a **parallel row set** in the TGS
598
- substep loop (contacts left untouched, not ported — lower risk).
599
- `constraint/solve_constraints.js` reuses `world_inverse_inertia`,
600
- per-substep warm-start, and the SPOOK position bias; `Joint`
601
- component + `link_joint`/`unlink_joint` in PhysicsSystem;
602
- `jointIterations` knob. Bodies need no collider.
603
- 2. [x] **LOCKED linear DOFs ball-socket.** Pendulum (anchor pinned
604
- to a world pivot, body swings) and a 2-link chain (body↔body,
605
- joints stay connected, chain hangs) pass. → **chains, ropes,
606
- pendulums working.**
607
- 3. [x] LOCKED angular + linear DOFs in the frame basis — **weld,
608
- hinge, prismatic done**. Joint frame bases
609
- (`localBasisA`/`localBasisB`); BOTH linear and angular rows now
610
- resolve in frame A's axes (cleared the world-axis linear debt — the
611
- solver is fully frame-relative). Angular: relative rotation
612
- `qD = conj(qA)·qB` small-angle error, ωB−ωA rows + SPOOK bias.
613
- Linear: `C·axis` error, vA−vB rows. `asWeld()` / `asHinge(axis)` /
614
- `asPrismatic(axis)` presets. Verified: weld holds pose + orientation
615
- against an off-centre torque; hinge swings about its free axis only
616
- (locked axes < 0.02); prismatic slides along its one free axis,
617
- locked on the others; all LOCKED-mode tests still green after the
618
- frame-basis rewrite.
619
- 4. [x] LIMITED + MOTOR (bounded rows) → doors, pistons, wheel
620
- spin/drive, joint ROM. **LIMITED done** (linear + angular):
621
- `setLinearLimit(axis,lo,hi)` / `setAngularLimit(axis,lo,hi)` set a
622
- per-DOF travel/ROM range. The whole row set is now **one mode-
623
- agnostic solve** parameterised by `(bias, clamp range)`: LOCKED is
624
- the bilateral case (Baumgarte bias, unclamped); LIMITED is a
625
- **speculative (β=1) one-sided velocity constraint** that removes
626
- exactly the approach velocity so the DOF *lands on* its stop (no
627
- penetration, no rebound an inelastic end-stop) and self-gates when
628
- far from the bound; only the push-out side of the bias is clamped so
629
- a teleport is eased out, not yanked. Verified: a vertical slider
630
- falls freely then stops dead on its lower stop (lands at the bound,
631
- no overshoot/rebound, locked axes held); a spun hinge stops dead on
632
- each ±end-stop with no rebound and holds. Angular position is the
633
- small-angle measure (`2·sin(θ/2)`) accurate for modest ROM, see
634
- phase 6 for wide cones. **MOTOR next** (target-velocity row, impulse
635
- clamped to `±maxForce·h`).
636
- 5. [x] SPRING (SPOOK soft) suspension, bungees, soft ragdolls.
637
- `setLinearSpring(axis,k,c)` / `setAngularSpring(axis,k,c)`. A
638
- compliant (regularised) row in the same unified solve: per substep
639
- `denom = c + h·k`, compliance = 1/(h·denom)`, restoring bias
640
- `(k/denom)·C`, softened mass `1/(K+γ)`; the iteration carries one
641
- extra `+ γ·λ_accum` term = 0 the LOCKED/LIMITED/MOTOR rows are
642
- bit-for-bit unchanged). Verified: a vertical strut settles at exactly
643
- the m·g/k deflection and a stiffer spring sags less and stays stable;
644
- an undamped spring oscillates about equilibrium (stores energy) while
645
- a damped one comes to rest; a torsional spring holds a gravity-loaded
646
- hinge at its balance angle. Suspension element ready (the simulated-
647
- wheel option for phase 7); also the soft basis for cone-twist.
648
- 6. [x] Cone-twist / swing-twist angular limits ragdolls. Opt-in
649
- `Joint.swingTwist` (or the `asConeTwist(twistLo,twistHi,swingY[,swingZ])`
650
- preset) switches the angular position measure from the per-axis
651
- small-angle vector to a swing-twist decomposition: angular X = twist
652
- about the bone, Y/Z = swing off it, each an **exact** angle. The
653
- existing LIMITED/SPRING/LOCKED rows are reused unchanged on those
654
- positions, so a twist/swing limit holds at the true angle at wide
655
- ROM (a 1.2 rad swing stops at 1.2, where the small-angle proxy
656
- drifts to ~1.287). Verified: exact swing/twist stops, free-within-
657
- cone, twist/swing independence; default (small-angle) path untouched.
658
- **Decision inlined, not the Quaternion method.** Benchmarked the
659
- allocation-free inlined `swing_twist_error` against
660
- `Quaternion.computeSwingAndTwist` (`swing_twist.bench.spec.js`): the
661
- inline is **~5x** faster than the method with reused out-params and
662
- **~10x** vs the naive fresh-allocation form (object property access +
663
- normalize + a quaternion multiply + GC). In the per-substep
664
- per-joint hot loop that margin is worth the duplicated math, so the
665
- solver inlines it (the Quaternion method stays for general callers).
666
- 7. [x] Vehicle layer **raycast-vehicle controller**
667
- (`vehicle/RaycastVehicle.js`): single chassis body + raycast wheels.
668
- Per frame (before `fixedUpdate`) each wheel casts its suspension ray,
669
- applies a spring+damper suspension force along the contact normal
670
- (`applyForceAt`), and a tyre-friction impulse (`applyImpulseAt`)
671
- lateral grip that cancels side-slip plus longitudinal drive/brake,
672
- clamped together to a friction circle μ·N. `addWheel`, `setSteering`,
673
- `setDriveForce`, `setBrake`; per-wheel runtime (contact, compression,
674
- normal, spin) for rendering. A controller on top of the public
675
- `raycast` + force API, not a new constraint; the 6-DOF spring+motor
676
- is the simulated-wheel alternative. Verified: hovers on its springs
677
- (4 contacts, settled), drives/coasts/brakes along its axis, tyre grip
678
- arrests a sideways shove, steering turns it upright, and it free-falls
679
- cleanly when airborne. Note: suspension is one dt-force per frame (not
680
- per-substep), so a resting chassis carries a ~g·h velocity-sample
681
- artifact (it hovers stably; position is steady to sub-cm). Ray
682
- accuracy follows `PhysicsSystem.raycast` now narrowphase-exact for
683
- sphere / box / capsule / mesh / heightmap ground.
684
- 8. [ ] Extras: pulley, gear, conveyor (contact surface-velocity),
685
- breakable-joint flag.
686
-
687
- Foundation gaps both now closed:
688
- - [x] **Island integration.** Jointed dynamic-dynamic bodies are
689
- unioned into one island (`IslandBuilder` Pass 1b), so a chain /
690
- ragdoll sleeps and wakes as a unit; `__wake_joints` propagates wake
691
- across a joint when one side is awake and the other asleep
692
- (e.g. a kinematic/motor driver pulling a sleeping chain). Verified:
693
- a damped chain settles and both links sleep in one sleep group.
694
- - [x] **Generation-checked body references.** `solve_joints`,
695
- `IslandBuilder` Pass 1b and `__wake_joints` all gate on
696
- `storage.is_valid(packedId)`, so a joint to an unlinked / slot-reused
697
- body goes inert instead of attaching to the wrong body or crashing.
698
- Verified: unlinking a jointed body leaves the joint inert and the
699
- survivor free.
700
-
701
- References: Catto / Box2D-v3 joint solvers; Jolt's `Constraint` base
702
- (`SetupVelocityConstraint` / `WarmStartVelocityConstraint` /
703
- `SolveVelocityConstraint` / `SolvePositionConstraint`); PhysX D6 /
704
- ODE joint taxonomy.
705
-
706
- ### Stability
707
- - [x] **Closed-form triangle-vs-primitive solvers** — `sphere_triangle_contact`
708
- / `box_triangle_contact` / `capsule_triangle_contact` (P1.1a–c), wired into
709
- the concave decomposition dispatch in place of per-triangle GJK+EPA for
710
- those primitives. Un-skipped the `narrowphase_concave.spec.js` ball-on-
711
- heightmap / mesh-cube settle tests and the `PhysicsSystem.spec.js`
712
- torus-knot test. Per-triangle GJK+EPA remains only as the fallback for
713
- *other* convex shapes vs triangles. `compute_penetration` now routes
714
- through the shared narrowphase dispatch (`deepest_pair_penetration`), so it
715
- uses the closed-form per-triangle solvers too the old closed-mesh
716
- over-report is gone; the half-space test is retained only as a
717
- tunnel-recovery fallback.
718
- - [x] **Edge-edge multi-point manifold** — *resolved by design (no code
719
- change needed).* An empirical SAT-source sweep over a wide range of
720
- box-box orientations shows the single-point edge-cross branch only ever
721
- wins for **transverse** edge crossings (inter-edge angle 83-90°), where
722
- a single closest-pair point is geometrically exact. A near-parallel edge
723
- pair gives a near-degenerate `edgeA × edgeB` that never becomes the SAT
724
- minimum, so near-parallel ("line") edge contacts resolve through the
725
- multi-point **face-clipping** path instead — confirmed by regression
726
- tests in `box_box_manifold.spec.js` (near-parallel tilted boxes → ≥ 2
727
- points; transverse crossing exactly 1 exact point). The originally
728
- planned refinement targeted a case the geometry can't produce, so it is
729
- closed rather than implemented.
730
- - [x] **Per-contact source-collider tracking (materials)** multi-material
731
- compound bodies now get accurate per-contact friction / restitution. The
732
- narrowphase combines the specific (colliderA, colliderB) pair's
733
- coefficients at dispatch time (the only place that knows the source
734
- collider on each side`contact/combine_material.js`) and stamps them
735
- into the manifold (CONTACT_STRIDE grown 14 16, offsets 14/15); the
736
- solver reads them per contact instead of from the body's primary collider.
737
- Regression test: an asymmetric-friction compound body yaws when shoved
738
- (the grippy collider drags), and a symmetric control does not. Still
739
- primary-collider-only: the contact-filter callback's collider args and the
740
- body-level sensor / concave flags (smaller follow-up).
741
- - [ ] **Joint-aware island sleep (ragdoll settle quality).** A draped,
742
- self-colliding 10-joint ragdoll does not fully sleep in 10 s surfaced by
743
- a 1000-seed Monte-Carlo sweep (`PhysicsSystem.ragdoll.spec.js`, `.skip`):
744
- for unlucky seeds a distal limb sustains a settled limit cycle (settled
745
- finite-difference accel up to ~1094 m/s² / ~1479 rad/s² at a limb end vs a
746
- ~55 m/s² median bounded, non-growing, penetration-free, so a quality gap
747
- not a divergence). The sleep test today is per-body `|v|²+|ω|²`; an island
748
- over-constrained by cone-twist limits + self-contacts keeps small residual
749
- jiggle above the per-body threshold so it never crosses into sleep.
750
- Candidate fixes: sleep a jointed/contacting island on its AGGREGATE motion
751
- rather than the per-body minimum, and/or a settled-regime relaxation (zero
752
- restitution + extra position iterations) once an island's energy is low.
753
- The sweep flags the worst seeds for replay. (Test infra also adds
754
- per-point kinematics tracking joint anchors + limb ends, with
755
- displacement→velocity→acceleration and the angular equivalents.)
756
- - [x] **Box-on-heightmap settling RESOLVED.** A dynamic box dropped onto a
757
- static HeightMapShape flat seam straddle AND the sloped dip — settles to
758
- full rest; the `PhysicsSystem.heightmap.spec.js` dip-drop reproducer is
759
- un-skipped and passing. Fixed by the combination of the same-feature-id
760
- contact de-dup in `redetect_pair_geometry` (1:1 claimed matching, so the
761
- several contacts one triangle emits all sharing its feature_idno longer
762
- collapse onto a single candidate) and the HeightMapShape3D
763
- collision-tessellation work; together they give the box a stable per-substep
764
- contact set instead of a churning / collapsing one.
765
-
766
- Root cause, for the record: the historical "never settles" rattle was a
767
- depth divergence between the once-per-step `narrowphase_step` and the
768
- per-substep `redetect_pair_geometry`. At the SAME pose, re-detection
769
- over-reported a box-vs-triangle penetration as ~1 m (the "exit through the
770
- far side" class) where narrowphase reported ~1 cm, launching the box into an
771
- eternal vertical bounce (touch ~1 m over-correction separate → fall back
772
- re-contact). Both paths call the identical `dispatch_pair`, so the
773
- divergence was the same-fid collapse corrupting the matched geometry; with
774
- the de-dup in place the two paths now agree (verified: both report
775
- [0.0100 ×4] at a 0.01-deep flat contact, in-cell and 4-triangle straddle).
776
-
777
- Guards: `narrowphase/redetect_pair_geometry.spec.js` now pins BOTH
778
- invariants contacts sharing a feature_id keep distinct witnesses, and
779
- re-detect depth == narrowphase depth at a fixed pose (in-cell and
780
- 4-triangle seam-straddle cases); `ecs/PhysicsSystem.heightmap.spec.js` pins
781
- the observable settle. The depth-equality regression test the earlier
782
- diagnostic trail asked for is in place, closing this out.
783
-
784
- ### Performance / Scale
785
- - [x] **Per-body linear CCD shape-cast** opt-in continuous collision for
786
- fast movers where the speculative margin isn't enough.
787
- `RigidBodyFlags.CCD` (off by default) + `ccd/linear_sweep.js`.
788
- - **Approach (Box2D `b2_continuousPhysics`-style conservative
789
- advancement).** After the substep solver produces each body's final
790
- pose (between `apply_restitution` and the sleep test), a flagged fast
791
- mover's primary collider is swept along its NET step translation
792
- (start-of-step final pose) through the existing `shape_cast` TOI
793
- engine. On the first blocker the body is clamped to the contact pose
794
- and its inbound normal velocity removed (an inelastic stop); the next
795
- discrete step resolves the now-touching contact with the real
796
- material / restitution. Reuses `shape_cast` wholesale no new
797
- geometry. Start positions captured in Stage 1 over the post-wake awake
798
- set; the pass iterates the awake list in storage order (deterministic).
799
- - **Motion gate absolute slop, NOT body extent.** A body is swept when
800
- it moved more than `CCD_MIN_SWEEP_DISTANCE` (1 mm) this step. The gate
801
- exists only to skip near-stationary bodies (degenerate sweep + cost).
802
- It is deliberately *not* a fraction of the body's own size: tunnelling
803
- risk is set by the **obstacle's** thickness, not the mover's — a 2 m
804
- sphere drifting 0.5 m/step still passes clean through a 1 cm floor, so
805
- an extent-based gate would (wrongly) wait until the body moved more
806
- than its own radius and miss every thin-obstacle tunnel below that
807
- speed.
808
- - **No self-clamp on resting/sliding contacts.** The sweep ignores an
809
- impact at `t 0` (`CCD_INITIAL_OVERLAP_EPS`): an initial overlap is a
810
- contact the body already sits/slides on, owned by the discrete solver,
811
- not a tunnel clamping there would freeze the body to the surface.
812
- - **Measured (falling-tower bench, 1000 random shapes onto a 1 cm floor,
813
- 600 ticks; clean A/B on identical code via `ccdEnabled`).** This bench
814
- is the WRONG validation vehicle for CCD, and the numbers prove it: CCD
815
- off → **10/1000 tunnel**, median **42.7 ms**/step; all 1000 flagged →
816
- **50/1000 tunnel**, median **61.6 ms**. CCD makes it *worse*. The bench
817
- is a dense-pile **squeeze-through** scenario 1000 bodies stacked on a
818
- 1 cm floor, forced through by the column's weight over many steps which
819
- is a *solver* limitation, not a missed single-step fly-through. CCD's
820
- post-solve clamp + velocity-kill fights the solver in a dense settling
821
- pile (it teleports mutually-stacking dynamics and breaks warm-start), so
822
- flagging a whole pile is an anti-pattern. CCD's real job stopping a
823
- sparse fast mover against thin geometry is validated by
824
- `ccd/linear_sweep.spec.js` (9 tests: a fast cube tunnels a thin floor
825
- without the flag and is stopped with it; deterministic; resting bodies
826
- undisturbed). **Correction:** an earlier version of this entry and the
827
- commit message (`42163b0d4`) claimed a "0/1000 tunnel, 58.2 ms" on-leg
828
- that was never measured (a session tooling glitch); the real on-leg
829
- is 50/1000 / 61.6 ms.
830
- - **Scope (v1, documented):** linear sweep only (orientation fixed
831
- through the sweep); primary same-entity collider (child-entity
832
- colliders, synced outside the step, are not swept); EXACT against
833
- static geometry, APPROXIMATE against other dynamics (their
834
- start-of-step broadphase AABBs); the CCD stop is inelastic (the impact
835
- itself doesn't bounce restitution applies on the next discrete
836
- contact); Dynamic bodies only. A body both resting on one surface and
837
- tunnelling another in the same step resolves only the resting contact
838
- (the `t 0` skip) rare; the next discrete step catches the rest.
839
- - **Follow-ups (the dense-pile finding points the way):** sweep against
840
- STATIC geometry only by default the dynamic-vs-dynamic clamp is the
841
- source of the dense-pile interference above and is only ever
842
- approximate anyway, so dropping it should make CCD purely additive
843
- (stops you at static walls/floors, never fights the dynamic stack);
844
- a proper TOI sub-solver for bullet-vs-dynamic; rotational / angular
845
- CCD; multi-collider sweep for compound bodies.
846
- - [x] **Broadphase BVH balance SAH rotation.** The dynamic AABB tree
847
- (`core/bvh2/bvh3/BVH.js`, a Box2D port) used SAH-cost insertion but a
848
- *height-only* AVL rotation (`balance_height`): height-balanced yet not
849
- SAH-balanced, so queries walked more nodes than needed. Replaced the
850
- rotation in `bubble_up_update` with `balance_rotate` the Box2D-v3 /
851
- Kensler SAH-reducing rotation (for node A with children B, C, evaluate the
852
- four child↔grandchild swaps and apply the one that most reduces the
853
- surface-area cost). Deterministic; identical pair set.
854
- - Measured (same-session A/B, heavy benches): raycast **−9%**
855
- (28.2→25.6 µs/ray), falling-tower median **−10%**, settling-grid
856
- median **−12%**, and the **990/1000-churn stress −27%**
857
- (63.95→46.68 ms mean over 10k ticks) — biggest where the tree churns
858
- hardest. Determinism (8-trial bit-identical) holds.
859
- - **Insertion cost (measured):** `balance_rotate` does 4 surface-area
860
- evaluations per bubble-up level vs `balance_height`'s single height
861
- compare, so *pure bulk insertion* is **~1.4–1.5× slower** — the 100k
862
- synthetic insert bench (`BVH.spec.js`, drift-controlled interleaved
863
- A/B) drops from **~37k ~25k inserts/sec** (~27→~40 µs/insert). This
864
- is the balancer's worst case (insert-only, zero queries/refits to
865
- amortise against). It does not show up end-to-end: static trees are
866
- built once then queried forever, dynamic bodies insert once then
867
- refit/query every frame, and even the 990/1000-swap stress test — the
868
- maximal insert-churn workload is net **−27%**. Accepted.
869
- - **Tradeoff (documented):** the contact solver's Gauss-Seidel order
870
- follows broadphase traversal order (see `generate_pairs`), so the
871
- different tree shape shifts convergence on near-aligned stacks the
872
- synthetic 128-cube wall now sleeps at ~10 s (was ~6.9 s). It still
873
- settles, doesn't creep / topple (all bug-guard assertions hold); only
874
- the sleep *time* moved (that test's budget was bumped 9→11 s with a
875
- note). Random-shape scenes (falling tower) were faster *and* settled
876
- fine.
877
- - **Follow-up:** decouple the solve order from tree shape — sort the
878
- broadphase pair list by `(idA, idB)` before narrowphase so contact
879
- order is body-id-deterministic regardless of tree shape. Then no tree
880
- change can affect convergence (and the stack settles identically under
881
- either balancer). Has a per-step sort cost + wide test re-baseline, so
882
- it's its own task. `balance_height` is retained for comparison /
883
- fallback.
884
- - [ ] **Per-island parallel solve**: today's island data layout would
885
- allow worker-based solving once `SharedArrayBuffer` is available.
886
- Out-of-scope unless / until SAB is universally usable.
887
-
888
- ### Features
889
- - [ ] **Convex collision proxies for dynamic concave bodies.** The long-term
890
- replacement for the interim per-substep concave re-detection (see
891
- Limitations) and how every major engine handles dynamic non-convex
892
- shapes: collide a *few* convex pieces, never the raw concave mesh.
893
- 1. **3D convex hull builder** (meep has only 2D hulls today
894
- `core/geom/2d/convex-hull/`). A single hull of a mesh is one
895
- collider / one broadphase leaf and covers the overwhelming majority
896
- of dynamic objects (thrown props, debris). Pairs with the existing
897
- "Convex hull shape + eigen-inertia" item below.
898
- 2. **Few-hull (V-HACD-style) approximate convex decomposition** for
899
- shapes whose concavity matters (a cup, a chair): ~8–64 fat convex
900
- hulls = 8–64 colliders, two orders of magnitude below a tet mesh.
901
- Each hull is convex stable contact feature the TGS analytic refresh
902
- is exact → no per-substep re-detection, no rocking. Granularity is the
903
- whole point: collider/BVH-leaf count must stay small for an *awake*
904
- dynamic body (the volumetric tet-mesher under `core/geom/3d/tetrahedra/`
905
- is the wrong tool here — thousands of pieces and belongs to a future
906
- FEM/soft-body subsystem, not rigid collision).
907
- - [ ] **Convex hull shape** with eigen-based principal-axes inertia
908
- derivation. Hooks `matrix_eigenvalues_in_place` from the existing
909
- linalg layer.
910
- - [~] **Cylinder / cone shapes.**
911
- - [x] **`CylinderShape3D`** Y-aligned solid cylinder (radius + full
912
- height, flat caps; the capsule's flat-cap sibling). Exact `support`,
913
- capped-cylinder SDF, bounds, `contains` / `nearest_point` /
914
- volume-sampling, equals/hash, `'cylinder'` JSON tag, `isCylinderShape3D`
915
- marker. Convex routes through the narrowphase **GJK + EPA** fallback
916
- (no marker dispatch needed); spec asserts overlap-detected +
917
- MTV-separates vs sphere/box. Closed-form cylinder-vs-X contact pairs
918
- are a future refinement (the curved side is the usual smooth-support
919
- EPA case same status as pre-closed-form sphere/capsule).
920
- - [ ] Closed-form cylinder contact pairs (cylinder × box / sphere / capsule
921
- / plane) for multi-point cap manifolds + stable resting.
922
- - [ ] **Cone shape** (+ closed-form / GJK fallback).
923
-
924
- ### Rendering integration
925
- - [ ] **Fixed-step → render interpolation.** Not implemented yet. Physics writes
926
- each body's pose straight into the ECS `Transform` once per fixed step
927
- (`EntityManager.fixedUpdateStepSize`); with a render rate that doesn't match
928
- the fixed rate, the rendered motion aliases (stutter / temporal aliasing,
929
- worst at low fixed rates). Designed as a cross-cutting system (render +
930
- physics-as-producer + network-as-producer), reusing the network package's
931
- agnostic interpolation primitives rather than a physics-local mechanism.
932
- Full design + phasing: **see
933
- [`INTEPOLATION_SYSTEM_PLAN.md`](./INTEPOLATION_SYSTEM_PLAN.md)**. Locked
934
- decisions: a neutral shared interpolation log/adapters (lifted out of
935
- `network/`); physics restores authoritative pose from the log at step start
936
- (no solver rewiring) and records moved bodies' snapshots at step end;
937
- blend `Transform` at `preRender`; O(moving) not O(N); rewind-safe via the
938
- tick-keyed log.
939
-
940
- ### API polish
941
- - [x] **`overlap(shape, position, rotation, output, output_offset,
942
- filter?)`** — broadphase + narrowphase overlap query for kinematic
943
- / AOE / selection use cases. Body_ids written into a caller-sized
944
- Uint32Array buffer. Convex query shape only; concave candidates
945
- are routed through the per-triangle decomposition path.
946
- - [x] **`shapeCast(ray, shape, rotation, result, filter?)`** for
947
- character controllers and kinematic shape sweeps. Broadphase
948
- swept-AABB against both BVHs; per-candidate AABB-slab interval
949
- narrowing + coarse step + GJK bisection for time-of-impact. The
950
- output `result.normal` is the true contact-surface normal at the
951
- kiss point, computed by re-running GJK + EPA at `best_t` on the
952
- winning candidate (falls back to `-ray.direction` only on EPA
953
- degeneracies).
954
- - [x] **`compute_penetration(out_direction, shape_a, pos_a, rot_a,
955
- shape_b, pos_b, rot_b)`** standalone geometry primitive (no
956
- PhysicsSystem) for resolving overlap between two shapes at given
957
- poses. Returns depth + outward direction. **Hardened** to route through
958
- the shared narrowphase dispatch (`deepest_pair_penetration`): exact
959
- closed-form for sphere/box/capsule pairs (box-box via SAT), GJK+EPA for
960
- general convex, closed-form per-triangle for convex × concave; the
961
- half-space test is retained only for tunnel recovery.
962
-
963
- ### Raycast narrowphase (done)
964
-
965
- **Problem.** `raycast` (and the suspension ray inside `RaycastVehicle`) resolves
966
- only to the nearest BVH leaf's *inflated* AABB: `result.t` is the distance to
967
- that fattened box and `result.normal` is its face normal. Exact for an
968
- axis-aligned box (modulo the broadphase margin), coarse for spheres / capsules /
969
- rotated boxes / meshes / heightmaps. Refine each candidate against the true
970
- shape to return the exact surface distance + normal. `shapeCast` already does
971
- this for swept convex shapes via GJK+EPA; `raycast` should get the same
972
- treatment with cheap analytic primitives on the hot path.
973
-
974
- **Design.** Mirror `narrowphase_step`'s dispatch: closed-form ray tests for the
975
- common primitives, a generic GJK fallback for the rest. The structural change is
976
- in the BVH walk the nearest *leaf AABB* is **not** the nearest *shape hit* (a
977
- ray can clip a near fat-AABB but miss its shape while hitting a farther one), so
978
- every crossing leaf must be refined, with subtrees pruned by inflated-AABB
979
- `t_near` vs the best *refined* `t` (conservative-correct: a shape hit is always
980
- its tight AABB entry its inflated AABB entry). A leaf whose ray crosses the
981
- fat AABB but misses the true shape now contributes **no hit** — the key
982
- correctness gain.
983
-
984
- Phasing (each phase: implement spec → run from `H:/git/moh` → commit):
985
-
986
- 1. [x] **Ray-primitive helpers** originally landed as `narrowphase/ray_shapes.js`,
987
- later EXTRACTED to `core/geom/3d/{sphere,box,capsule}/{sphere3,box3,capsule3}_raycast.js`
988
- (the (`t`, normal, miss = `Infinity`, first-hit-from-outside) convention is a
989
- fine general raycast contract, and a ray-vs-capsule primitive was otherwise
990
- missing engine-wide; the dispatch still shares one ray→local transform across
991
- them). Built local-frame (unit direction `t` preserved;
992
- rotate the local normal back). Triangle MT is inlined in the concave path
993
- (the existing `computeTriangleRayIntersection` writes a `SurfacePoint3` and
994
- returns no `t` unsuited to the buffer-flyweight loop). Colocated specs.
995
- 2. [x] **Ray-narrowphase dispatch** `narrowphase/refine_ray_hit.js`:
996
- `(shape, position, rotation, ox,oy,oz, dx,dy,dz, tMax, outNormal) → t`.
997
- Type-marker dispatch (`isUnitSphereShape3D` / `isBoxShape3D` /
998
- `isCapsuleShape3D`) to the analytic primitives; a generic convex fallback
999
- for `TransformedShape3D` / `UnionShape3D` / other (GJK ray-cast, or reuse
1000
- `shape_cast` with a zero-radius `PointShape3D`).
1001
- 3. [x] **Concave path** in the dispatch: for `is_convex === false` (mesh /
1002
- heightmap), enumerate the triangles overlapping the ray's swept AABB
1003
- (`mesh_enumerate_triangles` / `heightmap_enumerate_triangles`), Möller–
1004
- Trumbore each, take the nearest; normal from the triangle winding.
1005
- 4. [x] **Rewire `queries/raycast.js`**: at each leaf, call `refine_ray_hit` on
1006
- the true shape + pose instead of accepting the AABB `t_near`; track the best
1007
- refined `(t, body, normal)`; keep subtree pruning on inflated-AABB `t_near`.
1008
- Same signature / `PhysicsSurfacePoint` result; drop the AABB-face-normal
1009
- block. Multi-collider bodies still resolve the primary collider only
1010
- (inherited BVH-leaf limitation; note it).
1011
- 5. [x] **Tests**: per-shape exactness (sphere / OBB / capsule / mesh /
1012
- heightmap) exact `t` and true normal; the **fat-AABB-cross-but-shape-miss
1013
- no hit** case (the correctness win); nearest-of-several across a near miss;
1014
- `filter` and `tMax` honoured. Re-verify `RaycastVehicle` (ride height now
1015
- exact tighten the test bands if they shift by the old broadphase margin).
1016
- 6. [x] **Bench + docs**: a raycast micro-bench (analytic fast-path cost; confirm
1017
- the fat-AABB-miss rejection doesn't regress throughput); update the "Public
1018
- queries" entry, `raycast.js` header, and the `RaycastVehicle` "AABB-level"
1019
- caveat once exact.
1020
-
1021
- Note: this sharpens `RaycastVehicle` suspension on non-box ground and every
1022
- shape query; it does not change the broadphase or any API surface.
1023
-
1024
- ---
1025
-
1026
- ## Future / out-of-scope
1027
-
1028
- These are explicit architectural exclusions or post-v1 explorations.
1029
-
1030
- ### Architecture
1031
- - **Cross-runtime bit-exact determinism**: a soft-float library would
1032
- replace `Math.sin/cos/exp/log/pow` in the hot path. The codebase is
1033
- already structured to make this a swap-in at `quat3_integrate.js` and
1034
- tangent-basis construction in `build_manifold.js`. Not pursued because
1035
- the same-runtime determinism we have covers the common cases (single-
1036
- device replay, networked lockstep where all clients run the same JS
1037
- engine).
1038
- - **WASM / SIMD**: the engine targets pure-JS portability. SIMD would
1039
- invalidate the determinism story (V8 doesn't expose deterministic
1040
- Float64x2 ops).
1041
- - **Multi-threaded solver**: workers don't share memory cheaply without
1042
- `SharedArrayBuffer` plus the COOP/COEP HTTP headers, which are not
1043
- always available. Single-threaded is good-enough for the awake-body
1044
- budget that matters.
1045
- - **Packed-SoA body dynamics state — DECIDED AGAINST; do not re-open.** A
1046
- reviewer will note that `BodyStorage` is SoA for *identity* (entity /
1047
- generation / kind / flags / awake-set) but the per-body *dynamics* state —
1048
- velocity, inertia, mass, force/torque accumulators lives on the `RigidBody`
1049
- **component object** (each carrying several `Vector3` = `Float64Array` + an
1050
- `onChanged` Signal), in a sparse `__bodies[]` array reached by pointer-chase,
1051
- not packed by slot. The same is true of `Collider` and `Joint`. This is a
1052
- deliberate, settled choice, not an oversight: **meep is an ECS engine, and
1053
- `RigidBody` / `Collider` / `Joint` are public-API components.** Them being
1054
- first-class objects is a UX choice and uniformity with the rest of the engine —
1055
- it is what makes them authorable, serializable (`toJSON`/`fromJSON` + binary
1056
- adapters), value-diffable (`equals`/`hash` for netcode replication), and
1057
- observable (`Vector3.onChanged`), exactly like every other component. A
1058
- packed-SoA body pool would buy locality on the *awake* integration sweeps at
1059
- the cost of breaking that uniformity and the public component contract. The
1060
- design instead leans on "mostly-sleeping" (per-body iteration is over the
1061
- *awake* set only), keeps the genuinely O(contacts) inner loop in flat SoA
1062
- (manifolds + solver scratch), and has the hot paths index the component
1063
- vectors' `Float64Array` backing directly to skip the observer (see
1064
- Determinism). This is final do not resurface it as a "fix".
1065
- - **Single in-flight solve (module-scoped solver scratch).**
1066
- `solver/solve_contacts.js` keeps its cross-stage state (the `g_*` counters and
1067
- the `scratch_*` arrays) at module scope one copy shared by all worlds, not
1068
- per `PhysicsSystem`. Deliberate: every world reuses one set of scratch, and
1069
- it's safe because the engine is single-threaded and steps one `fixedUpdate` at
1070
- a time. The ceiling it sets: two `PhysicsSystem` instances cannot be stepped
1071
- concurrently or re-entrantly (the second clobbers the first's solver scratch).
1072
- Accepted for a single-world, single-threaded engine; lifting it would need
1073
- per-system scratch (or a solver-context object threaded through the stages).
1074
-
1075
- ### Simulation extensions
1076
- - **Soft body / cloth / fluids**: the SoA layout in `BodyStorage` and the
1077
- manifold cache are rigid-body shaped. A soft-body system would be a
1078
- parallel subsystem, not an extension.
1079
- - **Reduced-coordinate articulations** (MuJoCo / Featherstone-style):
1080
- game-physics audience runs in maximal coordinates by convention. Not
1081
- on the roadmap.
1082
-
1083
- ### Game-side
1084
- - **Vehicle physics** (suspensions, drivetrains): a domain layer that
1085
- sits on top of the rigid-body primitives, not in `meep/`.
1086
- - **Character controllers**: same `engine/control/first-person/` is the
1087
- natural home.
1088
-
1089
- ---
1090
-
1091
- ## Notable design files
1092
-
1093
- - Original design plan: `C:\Users\Alex\.claude\plans\let-s-plan-to-implement-transient-harp.md`
1094
- - This file (state of play): `engine/physics/PLAN.md`
1
+ # Physics engine — state of play
2
+
3
+ Tracker for what's built, what's pending, and what's deferred.
4
+
5
+ ---
6
+
7
+ ## Context
8
+
9
+ Deterministic JS rigid-body physics engine for the meep ECS. Target: game
10
+ scenarios with up to millions of mostly-sleeping bodies, deterministic replays
11
+ for netcode and reproducible debugging, broad shape coverage for common game
12
+ collisions. Pure JS — no WASM, no SIMD, no worker threads.
13
+
14
+ Architectural references for design choices:
15
+ - **Jolt** — pre-allocated body pool, active-list iteration, two-tree
16
+ broadphase (static + dynamic).
17
+ - **Bullet** — `btPersistentManifold` cache layout with up to 4 points.
18
+ - **Box2D / Catto** — sequential impulse with warm-starting, Sutherland-Hodgman
19
+ face clipping for box-box.
20
+
21
+ ---
22
+
23
+ ## Done
24
+
25
+ ### Foundations
26
+ - `RigidBody`, `Collider`, `BodyKind`, `RigidBodyFlags`, `ColliderFlags`,
27
+ `SleepState`, `PhysicsEvents`.
28
+ - `BodyStorage`: SoA pool, generation-tracked stable IDs, dense awake list,
29
+ min-heap free for deterministic ID reuse.
30
+ - `PhysicsSystem`: full public API surface (gravity, force/impulse with and
31
+ without application point, torque, velocity setter, wake/sleep, contact
32
+ filter callback).
33
+ - Binary serialization adapters for `RigidBody` and `Collider` (transient
34
+ runtime state deliberately excluded).
35
+ - `PairUint32Map`: open-addressed Robin Hood + Fibonacci hash for the
36
+ pair → manifold-slot index (the one new collection added to `core/collection/`).
37
+
38
+ ### Pipeline (`PhysicsSystem.fixedUpdate`)
39
+ 1. Velocity integration (semi-implicit Euler, linear + angular, gravity,
40
+ damping, world-frame inverse-inertia for torque)
41
+ 2. Per-collider broadphase refit with fat AABB (Box2D-style velocity-padded
42
+ slack)
43
+ 3. Pair generation: per-leaf query against both BVHs (static + dynamic),
44
+ canonical `(min, max)` pairs, dedup via manifold touched flag
45
+ 4. Wake propagation for sleeping bodies in the pair list
46
+ 5. Narrowphase cross-product over collider lists
47
+ 6. Sequential-impulse solver (Catto-style, warm-start, friction, Baumgarte)
48
+ 7. Position integration (linear + quaternion)
49
+ 8. Sleep test (per-body velocity² below threshold for ≥ 0.5 s)
50
+ 9. Manifold diff → `ContactBegin` / `Stay` / `End` event dispatch
51
+ 10. `manifolds.advance_frame()` — roll touched bits, evict grace-expired slots
52
+
53
+ ### Shape coverage
54
+ | Pair | Path | Manifold |
55
+ |---|---|---|
56
+ | sphere-sphere | closed-form | 1 point |
57
+ | sphere-box | closed-form (handles centre-inside-box) | 1 point |
58
+ | capsule-sphere | point-on-segment closed-form | 1 point |
59
+ | capsule-capsule | segment-segment closest pair | 1 point |
60
+ | capsule-box | iterative segment-vs-OBB (primary) + cap-centre sphere-vs-OBB at each endpoint | up to 3 |
61
+ | box-box face-face | SAT + Sutherland-Hodgman clipping | up to 4 |
62
+ | box-box edge-edge | SAT + segment-segment closest-pair | 1 point |
63
+ | sphere / box / capsule × concave (heightmap, mesh) | closed-form `*_triangle_contact` per triangle via decomposition dispatcher | up to a few points per triangle (deepest wins) |
64
+ | other convex × concave | per-triangle GJK + EPA via decomposition dispatcher | 1 point per triangle |
65
+ | anything else | GJK + EPA, MPR fallback on EPA non-convergence | 1 point |
66
+
67
+ ### Non-convex shapes
68
+ - **`is_convex` flag** on `AbstractShape3D.prototype` (default `true`).
69
+ Overridden to `false` on `HeightMapShape3D`, `MeshShape3D`, `UnionShape3D`.
70
+ `TransformedShape3D` inherits via getter that reads the wrapped subject.
71
+ - **`HeightMapShape3D`** — orientation-vector + `Sampler2D`-backed terrain
72
+ shape. Heights sampled via `sampleChannelCatmullRomUV` (matching the
73
+ terrain system's geometry construction). Compute_bounding_box,
74
+ contains_point, signed_distance, nearest_point_on_surface all
75
+ implemented; `support` throws (non-convex by construction).
76
+ - **`Triangle3D`** — buffer-flyweight convex shape. `bind(buffer, offset)`
77
+ repoints at 9 consecutive floats in an external Float64Array. Zero
78
+ allocation per emission; used by the decomposition path.
79
+ - **Triangle decomposition machinery** under
80
+ `engine/physics/narrowphase/decomposition/`:
81
+ - `TRIANGLE_FLOAT_STRIDE = 10` per triangle (`vA.xyz`, `vB.xyz`,
82
+ `vC.xyz`, `feature_id`).
83
+ - `heightmap_enumerate_triangles(out, offset, shape, ...aabb)` —
84
+ Arvo-projects the convex's AABB into heightmap-local, intersects
85
+ with the footprint to derive a cell range, emits 2 triangles per
86
+ cell with stable feature_ids.
87
+ - `mesh_enumerate_triangles(out, offset, shape, ...aabb)` — linear
88
+ O(N) scan over `MeshShape3D.indices` with tight per-triangle AABB
89
+ filtering. feature_id = triangle index.
90
+ - `aabb3_transform_oriented_inverse(out, world_aabb, pos, rot)` (now in `core/geom/3d/aabb/`) — 8-corner
91
+ projection of a world AABB into a body's local frame.
92
+ - `decompose_to_triangles(...)` — dispatcher switching on shape
93
+ type marker.
94
+ - **Narrowphase concave dispatch** in `narrowphase_step.js`: detects
95
+ `is_convex === false`, computes convex's world AABB, projects to
96
+ concave's local frame, decomposes, per-triangle GJK + EPA with
97
+ one-sided face-normal rejection and contact-normal dedup. Concave-vs-
98
+ concave dynamic pairs are explicitly refused.
99
+
100
+ ### Solver
101
+ - Sequential impulse with warm-starting (10 velocity iterations by default).
102
+ - Coulomb friction with disk-clamped tangent impulses.
103
+ - Baumgarte position correction folded into the velocity solve.
104
+ - Full angular Jacobian (`I_w⁻¹ = R · diag · R^T`) and angular impulse
105
+ application.
106
+ - Public force/impulse-at-point API (`applyForceAt`, `applyImpulseAt`,
107
+ `applyTorque`).
108
+
109
+ ### Sleep + events
110
+ - Per-island **atomic sleep**: an island sleeps when `max(|v|² + |ω|²)`
111
+ across all members stays below the threshold long enough; the whole
112
+ island sleeps in the same frame. Replaces the per-body chatter on
113
+ weakly-connected piles.
114
+ - **Atomic wake**: members of a sleeping island are threaded into a
115
+ circular doubly-linked list (`sleep_group_next` / `sleep_group_prev`);
116
+ waking any one member walks the chain and wakes the rest in the same
117
+ call. A 100-block stack hit at the base wakes top-down in one frame
118
+ rather than over 100 frames of broadphase propagation.
119
+ - `DisableSleep` on any island member exempts the whole island.
120
+ - ContactBegin / Stay / End buffer + dispatch through the per-entity
121
+ `dataset.sendEvent(entity, PhysicsEvents.ContactBegin, ...)` channel — the
122
+ sole contact-event path, delivered to each entity of the pair when a
123
+ dataset is attached.
124
+
125
+ ### Islands
126
+ - **Union-find** with path halving + union by min-index over the awake-body
127
+ + touched-contact graph (`core/collection/union-find/union_find.js`).
128
+ - **`IslandBuilder`** produces deterministic CSR-style output: bodies and
129
+ manifold slots grouped by island, sorted ascending within and across
130
+ islands. Static / kinematic bodies are constraint anchors only — they
131
+ don't merge islands, so disjoint piles on the same floor are separate
132
+ islands.
133
+ - **Islands feed the sleep test + grouping, not a per-island solver loop.**
134
+ The TGS contact solver flattens every island's contacts into one
135
+ Gauss-Seidel sweep (`solver/solve_contacts.js`) — islands share no bodies,
136
+ so a single flat sweep is identical to per-island sweeps. The partition is
137
+ still rebuilt every step and consumed by the atomic-island sleep test and the
138
+ joint/contact island grouping; it is also the natural unit for a future
139
+ worker-based parallel solve (see Performance / Scale). (Earlier revisions ran
140
+ the solver per island; the TGS rewrite flattened it — same result, simpler
141
+ loop.)
142
+
143
+ ### Compound bodies
144
+ - A body has 0..N attached colliders. Each collider has its own world
145
+ transform and its own BVH leaf.
146
+ - Same-entity colliders, child-entity colliders (via `ParentEntity`), or
147
+ hybrids all supported.
148
+ - `ColliderObserverSystem` auto-attaches colliders via the dataset when
149
+ paired with `PhysicsSystem` in an EntityManager.
150
+ - Narrowphase runs the cross-product over both bodies' collider lists per
151
+ body-pair, accumulates candidates, reduces to ≤4 contacts by
152
+ depth + spread.
153
+
154
+ ### Public queries
155
+ - `raycast(origin, dir, max_dist, filter?)` — nearest hit across both trees,
156
+ **refined to the true shape surface** (narrowphase). `result.t` /
157
+ `result.normal` are exact for every shape the engine defines: closed-form
158
+ per-leaf tests for sphere / box / capsule / cylinder / convex hull / triangle /
159
+ point, triangle Möller–Trumbore for mesh / heightmap, and recursion into the
160
+ subject for the `PosedShape3D` / `TransformedShape3D` / `UnionShape3D`
161
+ wrappers. Only a shape defined outside the engine falls back to the
162
+ broadphase AABB hit. A ray crossing a fat leaf AABB but missing the true
163
+ shape is correctly a miss.
164
+ - `shapeCast(ray, shape, rotation, result, filter?)` broadphase swept
165
+ AABB against both BVHs; per-candidate AABB-slab interval narrowing,
166
+ coarse step over the narrowed window, GJK bisection to time-of-impact.
167
+ Output normal is the true contact-surface normal at the kiss point,
168
+ recovered by re-running GJK + EPA at `best_t` on the winning candidate.
169
+ Falls back to `-ray.direction` only on EPA degeneracies (NaN / zero
170
+ depth). Tests cover axis-aligned, off-axis, and oblique cube-vs-cube;
171
+ sphere-vs-smooth-shape near-tangent has documented angular tolerance
172
+ bands inherited from EPA on smooth supports.
173
+ - `overlap(shape, position, rotation, output, output_offset, filter?)`
174
+ broadphase + per-candidate GJK overlap detection. Writes body_ids
175
+ into a caller-sized buffer; returns count. Convex query shapes only
176
+ (concave throws). Concave candidates routed through the per-triangle
177
+ decomposition path. Designed for speculative kinematic queries on
178
+ kinematic bodies (character controllers, AOE selection).
179
+
180
+ ### Standalone narrowphase utilities
181
+ - `deepest_pair_penetration(out_normal, shapeA, posA, rotA, shapeB, posB,
182
+ rotB)` (exported from `narrowphase_step.js`) runs the **same**
183
+ `dispatch_pair` the contact solver consumes for one posed shape pair and
184
+ returns the DEEPEST contact's depth + world normal (B A). The single
185
+ source of truth for "minimum-translation between two posed shapes", reused by
186
+ `compute_penetration` (and available to any other query).
187
+ - `compute_penetration(out_direction, shape_a, pos_a, rot_a, shape_b,
188
+ pos_b, rot_b)` non-system geometry primitive: positive penetration
189
+ depth + outward direction (BA convention) on overlap, 0 otherwise.
190
+ **Hardened** delegates to `deepest_pair_penetration`, so it is correct
191
+ (not "correct sometimes") for every shape pair the engine can build:
192
+ - sphere / box / capsule pairs → exact closed-form (box-box via SAT, so a
193
+ small body resting on a large floor reports the few-cm near-face overlap,
194
+ NOT the metres-deep "exit through the far side" that MPR's centroid-seeded
195
+ portal used to return);
196
+ - general convex pairs GJK + EPA (exact for polytopes; curved shapes never
197
+ reach it they have closed forms);
198
+ - convex × concave triangle decomposition + the closed-form per-triangle
199
+ solvers, bounded to each triangle's true 2-D extent (the old closed-mesh
200
+ side-face over-report is gone).
201
+ The previous per-triangle half-space test is retained ONLY as a recovery
202
+ fallback for the one case the one-sided closed forms can't resolve: a convex
203
+ shape that has fully tunnelled to the *inner* side of a concave surface (a
204
+ depenetration query must still push it back out exact for heightmap terrain,
205
+ a valid outward push for closed meshes). Concave × concave throws (M×N
206
+ triangle pairs out of scope). The spec asserts an "applying out_direction ×
207
+ depth separates the shapes" invariant across every convex+convex pair type and
208
+ convex+concave, plus exact per-type depths and the small-box-on-huge-floor
209
+ regression (3 m 0.05 m).
210
+
211
+ ### Determinism the reconstruction contract (H-series, landed)
212
+ Engine outcomes are a pure function of **body components (keyed by entity
213
+ id) + the exportable solver-cache blob** never of allocation history
214
+ (body slots, manifold slots, joint ids, awake-list order, BVH shape,
215
+ broadphase pad history). A world rebuilt from a snapshot — onto a fresh
216
+ engine, or by remove-all/re-add on a live one — continues **bit-identically**
217
+ to the engine that kept running. Verified by `determinism.spec.js`
218
+ (T1 lockstep repeatability; T2 reconstruction equivalence incl. generation
219
+ churn and pad-history wake timing).
220
+
221
+ - Entity-canonical pair A/B roles assigned in the broadphase
222
+ (`generate_pairs`) roles feed witness sides, normal orientation and the
223
+ tangent basis, so they must derive from the cross-engine identity.
224
+ - Outcome-coupled iteration orders are entity-keyed: island contacts
225
+ (IslandBuilder), joints (`__joints_sorted`), contact events
226
+ (`ContactEventBuffer.sort_canonical` + dispatch normal mirror), CCD pass.
227
+ - `sleepState` + `sleep_timer` are persistent body state (adapter v1 +
228
+ 0→1 upgrader); `link()` honours them, so restored sleepers stay asleep.
229
+ - Stage-0 wake reads persistent manifolds (`prev_touched`), never the
230
+ transient pair list; the precautionary pre-contact wake is an
231
+ edge-triggered swept-tight-AABB proximity predicate (`FLAG_PROX`),
232
+ a pure function of body state.
233
+ - `persistence/solver_caches.js`: entity-keyed export/import of manifolds
234
+ (warm-start impulses + lifecycle bits), joint `dofImpulse`, sleep-group
235
+ chains. Identical worlds serialize to identical bytes.
236
+ - Foundations that predate the H-series: min-heap free lists for
237
+ deterministic slot reuse, no `Math.random` in the step, direct
238
+ typed-array writes on hot paths (Transform writes still go through
239
+ `set()` — external systems subscribe).
240
+ - Same-runtime bit-exact; cross-runtime is a known future seam (see
241
+ Backlog Reconstruction & determinism).
242
+
243
+ ### Migration
244
+ - `Motion` / `MotionSystem` / `MotionSerializationAdapter` relocated from
245
+ the meep core (`engine/ecs/`) to the game-domain layer
246
+ (`mir-engine/model/game/ecs/`). meep no longer ships the legacy shim.
247
+
248
+ ### Alternative narrowphase: MPR
249
+ - `core/geom/3d/gjk/mpr.js` Minkowski Portal Refinement (XenoCollide,
250
+ Snethen GDC 2009). Single-pass overlap test + MTV computation,
251
+ output convention matches EPA so it's drop-in compatible at any
252
+ narrowphase call site. Tends to converge in 5–15 iterations on
253
+ smooth shapes where EPA stalls (the polytope-on-curved-surface
254
+ failure mode the torus-knot reproducer exercised). **Wired as the EPA
255
+ non-convergence fallback** in `narrowphase_step` at both the body-level
256
+ and per-triangle GJK+EPA paths: when EPA returns a non-positive / non-finite
257
+ depth, MPR is tried before giving up. `shape_cast` and `compute_penetration`
258
+ use it for the same reason.
259
+
260
+ ### Bonus utilities
261
+ - `core/geom/3d/line/line3_closest_points_segment_segment.js` generally
262
+ useful 3D segment-segment closest-pair via Ericson §5.1.9.
263
+ - `core/collection/PairUint32Map.js` — non-allocating
264
+ `Map<(u32, u32) → u32>` with Robin Hood + Fibonacci hash.
265
+
266
+ ---
267
+
268
+ ## Limitations / Known caveats
269
+
270
+ - **Multi-collider material precision** *resolved for contact materials.* The
271
+ narrowphase now combines the specific source-collider pair's friction /
272
+ restitution per contact and stores them in the manifold (CONTACT_STRIDE
273
+ offsets 14/15); the solver reads them per contact, so mixed-material compound
274
+ bodies are accurate (regression test: an asymmetric-friction body yaws when
275
+ shoved). Still primary-collider only: the contact-filter callback's
276
+ `colliderA/B` arguments and the body-level sensor / concave-dispatch flags
277
+ a smaller follow-up.
278
+ - **EPA on smooth shapes**: degenerates (no flat face to converge on).
279
+ Mitigated by closed-form paths for sphere/cube/capsule pairs and by the
280
+ **MPR fallback** on EPA non-convergence; exotic convex shapes vs spheres can
281
+ still occasionally fail if both EPA and MPR degenerate.
282
+ - **EPA on `Triangle3D`** *resolved.* The concave dispatch now uses the
283
+ closed-form `sphere_triangle_contact` / `box_triangle_contact` /
284
+ `capsule_triangle_contact` solvers (P1.1a–c) instead of per-triangle GJK+EPA
285
+ for those primitives, so a sphere/box/capsule on a heightmap or mesh decelerates
286
+ and settles correctly; the `narrowphase_concave.spec.js` "drop and settle"
287
+ cases and the mesh torus-knot settle test are **un-skipped**. Per-triangle
288
+ GJK+EPA remains only as the fallback for *other* convex shapes vs triangles.
289
+ (`compute_penetration` now routes through that same dispatch via
290
+ `deepest_pair_penetration` see *Standalone narrowphase utilities* instead
291
+ of its old half-space pre-test; the half-space test survives only as a
292
+ tunnel-recovery fallback.)
293
+ - **Box-box edge-edge contact**: a single point at the true closest-pair of the
294
+ two edges (P3.2), not the old body-centre midpoint. This is geometrically
295
+ correct and an empirical SAT-source sweep confirms the edge-cross branch
296
+ *only* fires for **transverse** edge crossings (inter-edge angle 83-90°),
297
+ where two skew lines meet at a unique point. Near-parallel edge contacts
298
+ cannot reach this branch (a near-parallel `edgeA × edgeB` never wins the SAT
299
+ minimum) they resolve through the multi-point face-clipping path. So the
300
+ once-planned "multi-point edge contact for near-parallel edges" refinement is
301
+ **moot**; see the resolved Stability backlog entry.
302
+ - **Raycast's AABB fallback** *resolved for every shape the engine defines.*
303
+ `refine_ray_hit` used to answer with the shape's tight AABB for anything it had
304
+ no exact test for, which was everything except sphere / box / capsule / mesh /
305
+ heightmap. The box is an over-estimate of the solid, so a ray crossing it but
306
+ missing the shape reported a phantom hit, and a ray starting in the empty part
307
+ of it reported `t = 0` with an axis-aligned face normal BUG-7. Two shapes
308
+ did not even reach that fallback: `UnionShape3D` and any `TransformedShape3D`
309
+ around a mesh report `is_convex === false`, which routed them into the concave
310
+ triangle decomposition, where the enumerator read `indices` off a shape that has
311
+ none and THREW out of the raycast. The dispatch is now total over the engine's
312
+ shapes — closed-form tests for hull / cylinder / triangle / point, recursion
313
+ into the subject for the three wrappers — and the fallback survives only for a
314
+ shape subclassed outside the library. It is a floor for foreign shapes, not a
315
+ design: it still reports the bounding box, and for a shape with a FLAT bounding
316
+ box (a triangle) its normal comes out of a division by a zero extent.
317
+
318
+ - **CCD floor only**: speculative margin via the fattened AABB prevents
319
+ most tunnelling. No per-body swept shape-cast for very fast objects.
320
+ - **Cross-runtime determinism is not guaranteed**: `Math.sin/cos/exp/log`
321
+ are ULP-correct but not bit-exact across V8 / SpiderMonkey / JSC.
322
+ - **Dynamic concave bodies under TGS** *resolved by per-substep re-detection
323
+ (below); kept here for the rationale.* The substep loop normally re-derives
324
+ contact geometry analytically from the per-triangle contact feature (witness
325
+ anchors + normal) captured once by narrowphase and held fixed for the whole
326
+ outer step. For a convex body the contact feature is stable under the small
327
+ per-step motion, so this is exact; for a *dynamic concave mesh body* (e.g. a
328
+ torus knot rocking on its own lobes) the supporting triangle itself changes
329
+ as the body rocks, so freezing the feature would pump a little energy in and
330
+ the body would rock / slowly sink instead of settling. Note this is NOT a
331
+ contact-precision issue
332
+ the knot already uses the exact closed-form box-triangle solver (P1.1b);
333
+ the problem is purely that TGS freezes *which* feature is in contact across
334
+ substeps. The common concave case a convex dynamic body on static concave
335
+ terrain is unaffected (the convex side's feature is stable), and that is
336
+ the only concave case the engine targets.
337
+
338
+ **Interim fix (implemented): per-substep concave re-detection.** For
339
+ contact pairs involving a concave body, the substep loop re-runs the
340
+ concave narrowphase geometry at the current substep pose (instead of the
341
+ analytic refresh that freezes the feature) and re-prepares those contacts
342
+ from the fresh witness/normal/depth — so the contact normal tracks the
343
+ rocking body and no energy is pumped in. Convex pairs keep the cheap
344
+ analytic refresh. This is ~Nx narrowphase cost on concave-involved pairs
345
+ (acceptable they're rare), gated by collider convexity. Un-skips the
346
+ torus-knot dynamic-settle test.
347
+
348
+ **Better long-term fix: convex collision proxies (not raw concave).** Every
349
+ major engine (Box2D, Jolt, PhysX, Rapier) requires dynamic bodies to be
350
+ convex or convex-decomposed; raw concave meshes are static-only. The right
351
+ granularity is a *few* convex pieces — NOT the thousands of tets a
352
+ volumetric mesher produces (tet count collider/BVH-leaf count, which
353
+ explodes the broadphase for an awake body; tet meshing is for a future
354
+ FEM/soft-body subsystem, not rigid collision). See the "Convex collision
355
+ proxies for dynamic concave bodies" backlog item a 3D convex hull builder
356
+ (single-hull proxy covers most dynamic objects) plus an optional
357
+ few-hull (V-HACD-style) decomposition. Those supersede the interim
358
+ per-substep re-detection once built.
359
+
360
+ ---
361
+
362
+ ## Backlog (planned, in scope)
363
+
364
+ ### Reconstruction & determinism (engine side done; consumers pending)
365
+
366
+ The engine-side determinism contract is landed (see Done Determinism).
367
+ These are the follow-ups that build on it:
368
+
369
+ - [ ] **Network late-join wiring (end-to-end).** The engine API exists
370
+ (`write_solver_caches` / `read_solver_caches`, sleep-persistent
371
+ components, the entity-id contract) but nothing consumes it yet.
372
+ Build an end-to-end test through the network package's adapters:
373
+ serialize a live world on the "server", reconstruct on a "client"
374
+ (same entity ids, same link order), verify bit-identical continuation
375
+ over N ticks. This validates the entity-replay assumptions
376
+ (replicated entity ids, joint link order, system config transfer)
377
+ before real netcode depends on them, and decides where the blob rides
378
+ (snapshot message vs. component stream). System config (gravity,
379
+ thresholds, iteration counts, `ccdEnabled`) is part of the snapshot
380
+ contract see `copy_system_config` in `determinism.spec.js` for the
381
+ field list.
382
+ - [ ] **Position correction under stacked load.** Settled penetration is
383
+ pose-chaotic with a heavy tail reaching the limb radius (~0.3 m for
384
+ the ragdoll's capsules) when a body settles wedged under the
385
+ assembly's weight measured across seeds and under either contact-
386
+ role assignment (see the 4-seed table in
387
+ `PhysicsSystem.ragdoll.spec.js`). Pre-existing, not a regression.
388
+ Investigation levers: position iterations (currently 1/substep),
389
+ `MAX_POSITION_BIAS` clamp, depth-proportional stiffening for deep
390
+ static contacts. Success metric: the cross-seed tail of
391
+ `lastSecondMaxPenStatic`, not any single golden seed.
392
+ - [ ] **Sleep groups derived from manifolds (redesign).** Wake-the-island
393
+ currently rides on `sleep_group_next/prev` circular chains threaded at
394
+ atomic-sleep time world-instance body indices that need their own
395
+ section in the solver-cache blob. Deriving the wake set at wake time
396
+ (flood over dormant contacting manifolds + joints among sleepers)
397
+ would delete the fields, the blob section, and the group-restore step
398
+ outright. Needs a per-body manifold adjacency (or an accepted
399
+ O(live-slots) scan per wake call) and a perf check on wake-heavy
400
+ frames. Behavioural delta to design for: force-slept touching bodies
401
+ currently wake independently (separate singleton groups); a
402
+ manifold-derived flood would wake them together.
403
+ - [x] **KILLED pose-keyed narrowphase memo (measured 2026-06-11).** The
404
+ idea: skip a pair's narrowphase when both collider poses are bit-equal
405
+ to its last run (a pure cache; outcome-neutral by construction, proven
406
+ with a memo-on/off lockstep test). Implemented per-ManifoldStore (a
407
+ process-global cache is unsound two engines hosting the same pair
408
+ ids at equal poses false-hit each other's slot content), then measured:
409
+ **0.0% hit rate** in every phase of a settling column, including the
410
+ final pre-sleep second. The premise is empirically false: warm-start
411
+ cancellation holds *velocity* near zero, never exactly zero, so awake
412
+ poses drift sub-nanometre every tick and never bit-freeze; sleeping
413
+ pairs never reach narrowphase at all. Reverted as pure overhead.
414
+ Dependent idea for the soft-solver work: a deterministic
415
+ micro-velocity snap-to-zero at the end of the solve would both freeze
416
+ resting poses bit-exact (making this memo viable) AND push
417
+ forever-jiggling islands (deep-column N=20, KEVA) below the sleep
418
+ threshold likely the cheaper end of the same win.
419
+ - [x] **KILLED solver hot/cold row split (ceiling-measured 2026-06-11).**
420
+ The idea: split the 23-float prepared-contact records by the velocity
421
+ iteration's access pattern (15 hot lanes) for cache density. Measured
422
+ the ceiling first instead of implementing: PADDING the stride 23→31
423
+ (+35% row traffic) moved KEVA-mini by only ~2% so packing −35% can
424
+ gain at most the same ~2%, far under the ≥10% gate. The velocity
425
+ solve at this scale is dominated by the slot-scattered manifold reads
426
+ (normal + impulses), the body-span gather/scatter and raw FLOPs, not
427
+ by prepared-row density. Not implemented.
428
+ - [x] **KILLED 4×4 manifold block solve (probe-measured 2026-06-11).**
429
+ Pre-integration probe on the deep columns, sweeping iteration budgets:
430
+ depth stability IS iteration-starved (N=30 explodes at velIters 4,
431
+ holds intact at 8 the divergence is convergence failure), but the
432
+ never-sleeping residual is NOT at velIters 16 the N=20 column idles
433
+ at residual 0.83, closer to rest than any other config, and still
434
+ never crosses the sleep threshold; sleep behaviour is non-monotonic
435
+ across budgets. That residual is a hard-constraint limit cycle
436
+ hovering at the threshold a scheme property no amount of
437
+ Gauss-Seidel (sequential or block) removes. A block solve would only
438
+ buy the depth-stability half, which the soft-step scheme change
439
+ covers anyway (Box2D v3 dropped block solving when it adopted soft
440
+ step). Revisit only if post-soft-step iteration budgets remain
441
+ depth-limited.
442
+ - [x] **LANDED Box2D-v3 soft contact step (2026-06-11).** Contact normal
443
+ rows are soft constraints: `b2MakeSoft(hertz, ζ, h)` scales (mass scale
444
+ damps the velocity gain, the impulse-decay term bleeds the accumulator
445
+ the critically-over-damped spring that killed the hard scheme's
446
+ resting limit cycle), a penetration-bias push for penetrating contacts
447
+ (clamped like the old position bias), and a per-substep RELAX pass
448
+ after position integration that strips bias-injected momentum. The
449
+ split-impulse position pass is skipped for contacts when soft; knobs:
450
+ `contactHertz = 30` (0 = bit-exact legacy hard rows), `ζ = 10`,
451
+ non-dynamic pairs at hertz, hertz clamped to an eighth of the
452
+ substep rate all pinned from Box2D v3 source.
453
+ A bias-free hybrid (decay without the bias push) was measured FIRST
454
+ and is UNSOUND: the decay bleeds the load-carrying impulse with no
455
+ restoring force, columns sank 3 m. Full v3 it is.
456
+ Measured: deep columns 15/20/30 all sleep (55/57/85 ticks; 20 never
457
+ slept and 30 DIVERGED under hard); KEVA-mini all-ticks median
458
+ 90.8 14.8 ms (sleep arrives much sooner; awake ticks +23% from the
459
+ relax sweep); 24-seed ragdoll sweep: settle-accel medians ~halved,
460
+ impacts unchanged, settled-pen median 0.049 vs ~0.058 hard.
461
+ Velocity-iteration cut REJECTED by measurement: KEVA-mini's
462
+ convergence cliff sits between velIters 3 and 4 (2 → never sleeps,
463
+ 3 sleeps far later); 4×4 stays.
464
+ Known trades (re-pinned goldens, all documented in-test): settled
465
+ penetration under load is the soft equilibrium droop (~0.8 mm per
466
+ carried cube-weight); aligned-stack lateral creep grew ~5× (softer
467
+ settle-phase normal impulses lower the friction caps tuning
468
+ candidate: stiffer staticSoftness or friction in the relax pass);
469
+ ragdoll fields idle ~2× livelier (0.74 → 1.66 residual /704 bodies —
470
+ tuning candidate: align joint hertz/ζ policy with contacts). The
471
+ ragdoll canonical seed moved to 0xBEEF42 (0xDEADB0D's drape pose
472
+ under soft ends in an accelerating dome roll — a growing-tail
473
+ outlier; the sweep keeps covering it).
474
+ - [ ] **Cross-process repeatability of symmetry-degenerate scenes.** The
475
+ perfectly-aligned 10-cube tower's resting drift came out 42/57/73 mm
476
+ across equivalent builds during the soft-step work each value
477
+ process-stable and ×3 reproducible, shifting across rebuilds whose
478
+ source diffs were assertion-text only. In-process determinism is
479
+ green (T1 lockstep, 8-trial reproducibility bench), so the seam is
480
+ environmental (suspect: jest transform/module environment shaping
481
+ sub-ulp float paths in symmetry-degenerate tie-breaks). Investigate
482
+ whether genuinely identical code can produce different cross-process
483
+ results (would matter for lockstep across restarts) or whether the
484
+ rebuild artefacts differed; degenerate-scene goldens use CLASS bounds
485
+ in the meantime.
486
+ - [ ] **Cross-runtime float determinism audit.** Same-runtime bit-exactness
487
+ is done; running lockstep across *different* JS engines (or versions)
488
+ additionally requires auditing transcendentals `Math.sin/cos/exp/
489
+ pow` are not spec-pinned (sqrt and arithmetic are IEEE-exact). Only
490
+ worth doing if cross-engine lockstep becomes a real deployment target;
491
+ same-engine replication does not need it.
492
+
493
+ ### Solver quality (next major work)
494
+
495
+ These items move the engine from "competent" to "great". TGS is the next
496
+ significant solver-architecture change; joints come after, once the TGS
497
+ scaffolding is in place.
498
+
499
+ - **TGS (Temporal Gauss-Seidel) substepping with split-impulse** — Phases
500
+ 1–3 **LANDED**. The solver is now a staged TGS pipeline
501
+ (`solver/solve_contacts.js`: `prepare_contacts` per substep
502
+ [`refresh_contacts` `warm_start_contacts` `solve_velocity`
503
+ `solve_position`] `apply_restitution`), driven by the substep loop in
504
+ `PhysicsSystem.fixedUpdate`. Defaults: `substeps = 4`,
505
+ `velocityIterations = 4`, `positionIterations = 1` (all fields on
506
+ `PhysicsSystem`).
507
+ - **Phase 1 — split impulse.** Position correction runs on a per-body
508
+ pseudo-velocity (`__pseudo_velocity`) folded into the pose by
509
+ `integrate_position` and discarded; depth correction never
510
+ contaminates persistent velocity.
511
+ - **Phase 2 one-shot restitution.** Velocity pass is pure
512
+ non-penetration; restitution is a single post-loop pass driving
513
+ `vn → -e·vn_approach`, gated on a running max normal impulse
514
+ (`maxNormalImpulse`) so transient collisions still bounce under
515
+ per-substep warm-start.
516
+ - **Phase 3 substep loop.** `substeps` sub-iterations at `h = dt/N`.
517
+ Forces consumed once at full `dt` before the loop; gravity applied
518
+ per substep; **warm-start replayed per substep** (the crux — a
519
+ per-substep impulse balances one substep of gravity, so resting
520
+ stacks hold at zero velocity). Contact geometry is re-derived
521
+ **analytically** each substep from frozen local witness anchors +
522
+ the trusted prepare-time depth (a sign-robust delta), so narrowphase
523
+ runs **once** per outer step — cheaper than the originally-planned
524
+ per-substep match-and-merge refresh, and exact for convex
525
+ primitives whose contact feature is stable under small motion.
526
+
527
+ Results vs the single-step solver: a 100:1 mass ratio now stacks
528
+ instead of crushing through (regression test added); 8-cube stacks
529
+ settle to zero velocity and sleep (were impossible long-term under SI);
530
+ falling-tower bench cost unchanged (~48 ms/1000 active bodies);
531
+ `substeps = 1` reproduces the single-step result bit-for-bit-ish
532
+ (one-frame restitution delay aside).
533
+
534
+ **Hard-won lessons (for REVIEW_002):**
535
+ - Warm-start MUST be per-substep, not once. Replaying a full-frame
536
+ impulse once while gravity arrives per substep over-pushes resting
537
+ contacts and *explodes* deep stacks. Per-substep warm-start +
538
+ per-substep gravity cancel exactly at rest.
539
+ - Restitution must gate on the *running max* normal impulse, not the
540
+ end-of-loop value per-substep warm-start relaxes a transient
541
+ contact's `j_n` back to ~0 by the end, which would suppress the
542
+ bounce.
543
+ - Analytic separation re-derivation beats per-substep narrowphase
544
+ for convex shapes (cheaper, no manifold-lifecycle churn) but is
545
+ only as good as the frozen normal — see the concave caveat below.
546
+
547
+ Follow-ups since the core landed:
548
+ - [x] **Box-box SAT reference tie-break deadband** aligned cube
549
+ stacks (4–10 high) now settle to zero velocity and sleep; the
550
+ reference-face flip-flop that creeped/toppled them is gone.
551
+ - [x] **Per-substep contact re-detection for concave pairs** — lifts
552
+ the dynamic-concave-body limitation; the torus-knot dynamic-settle
553
+ test is un-skipped. Concave pairs re-run narrowphase geometry each
554
+ substep (`redetect_concave_contacts`); convex pairs keep the cheap
555
+ analytic refresh.
556
+
557
+ Remaining (Phases 4–6) now complete:
558
+ - [x] Regression coverage: heavy-on-light pyramid (10× capstone on two
559
+ light cubes settles + sleeps) and a ragdoll-stub (shoulder
560
+ ball-socket + elbow hinge arm hangs, stays articulated, settles).
561
+ - [x] REVIEW_002 retrospective `engine/physics/REVIEW_002.md`.
562
+
563
+ References: Catto 2018 ("Soft Constraints" GDC talk + the TGS
564
+ follow-up); Box2D v3 source (`b2ApplyRestitution`, the substep solver
565
+ stages); Rapier as the closest architectural sibling.
566
+
567
+ - [x] **Constraints / jointsDONE (phases 1–7 below).** One configurable
568
+ 6-DOF joint (lock/free/limit/motor/spring + swing-twist cone-twist) plus
569
+ the raycast vehicle. Covers chains/ropes, ragdolls, vehicles (incl.
570
+ suspension), and the mechanical set (doors, pistons, welds, sliders,
571
+ powered hinges/wheels). The design rationale below is kept as history; the
572
+ phasing checklist records what landed. Solver/joint retrospective in
573
+ `REVIEW_003.md`.
574
+
575
+ Original framing (now satisfied): TGS unblocked it (joint-chain
576
+ convergence is a TGS sweet spot), warm-start + per-substep + island
577
+ machinery was reusable, and the SPOOK compliance dial gave spring
578
+ constraints essentially for free.
579
+
580
+ **Foundational work (do first): generalise the solver to constraint
581
+ rows.** Today `solver/solve_contacts.js` is hard-coded to the
582
+ contact-shape constraint (normal + 2 friction tangents, ≥0 clamp,
583
+ restitution, penetration bias). Joints are equality / inequality
584
+ constraints on relative velocity at anchors, generally bilateral
585
+ (impulse may be ±) with optional limits and motors. The clean shape —
586
+ and what Jolt / Box2D-v3 do — is a **generic constraint row**: a
587
+ Jacobian (linear + angular parts per body), an effective mass, a bias
588
+ (position error × SPOOK gain, or motor target), and impulse bounds
589
+ `[lo, hi]` (`[0,∞)` for a contact/limit, `(−∞,∞)` for an equality,
590
+ `[−maxForce·h, +maxForce·h]` for a motor). Each joint type just fills
591
+ in its rows; the existing per-body impulse-apply primitive
592
+ (`apply_impulse_to_body` + `world_inverse_inertia_apply`), the
593
+ per-substep warm-start, the islands, and the split-impulse / SPOOK
594
+ position handling are all reused. Contacts become *one* constraint
595
+ type among several rather than the hard-coded path.
596
+
597
+ The specific constraint set, its use-case mapping, and per-type
598
+ architecture-fit assessment are under review (see the constraints
599
+ sketch). High level: ball-socket / distance / spring / weld and the
600
+ grab constraint are near drop-ins on the row machinery; hinge /
601
+ prismatic / cone-twist / motors / limits add angular-row + bounded-row
602
+ mechanics (still within the impulse framework); raycast vehicles,
603
+ conveyor surface-velocity, and gear/pulley coupling are higher-level
604
+ systems or contact modifiers that sit *on top of* the primitives
605
+ rather than being generic rows.
606
+
607
+ **Decision: build ONE configurable 6-DOF constraint** (PhysX D6 / Jolt
608
+ SixDOF), implemented mode-by-mode. The `Joint` ECS component carries
609
+ `dofMode[6]` (3 linear, 3 angular) each `{locked|free|limited|spring|
610
+ motor}` + per-DOF limit/spring/motor config + warm-start accumulators.
611
+ Concrete joints are configs, not new code (ball-socket = lock 3 linear;
612
+ hinge = lock 3 linear + 2 angular; weld = lock 6; cone-twist = lock 3
613
+ linear + limit 3 angular; suspension = spring 1 linear + lock rest).
614
+
615
+ Phasing:
616
+ 1. [x] Constraint-row solver as a **parallel row set** in the TGS
617
+ substep loop (contacts left untouched, not ported lower risk).
618
+ `constraint/solve_constraints.js` reuses `world_inverse_inertia`,
619
+ per-substep warm-start, and the SPOOK position bias; `Joint`
620
+ component + `link_joint`/`unlink_joint` in PhysicsSystem;
621
+ `jointIterations` knob. Bodies need no collider.
622
+ 2. [x] **LOCKED linear DOFs ball-socket.** Pendulum (anchor pinned
623
+ to a world pivot, body swings) and a 2-link chain (body↔body,
624
+ joints stay connected, chain hangs) pass. **chains, ropes,
625
+ pendulums working.**
626
+ 3. [x] LOCKED angular + linear DOFs in the frame basis — **weld,
627
+ hinge, prismatic done**. Joint frame bases
628
+ (`localBasisA`/`localBasisB`); BOTH linear and angular rows now
629
+ resolve in frame A's axes (cleared the world-axis linear debt — the
630
+ solver is fully frame-relative). Angular: relative rotation
631
+ `qD = conj(qA)·qB` small-angle error, ωB−ωA rows + SPOOK bias.
632
+ Linear: `C·axis` error, vA−vB rows. `asWeld()` / `asHinge(axis)` /
633
+ `asPrismatic(axis)` presets. Verified: weld holds pose + orientation
634
+ against an off-centre torque; hinge swings about its free axis only
635
+ (locked axes < 0.02); prismatic slides along its one free axis,
636
+ locked on the others; all LOCKED-mode tests still green after the
637
+ frame-basis rewrite.
638
+ 4. [x] LIMITED + MOTOR (bounded rows) doors, pistons, wheel
639
+ spin/drive, joint ROM. **LIMITED done** (linear + angular):
640
+ `setLinearLimit(axis,lo,hi)` / `setAngularLimit(axis,lo,hi)` set a
641
+ per-DOF travel/ROM range. The whole row set is now **one mode-
642
+ agnostic solve** parameterised by `(bias, clamp range)`: LOCKED is
643
+ the bilateral case (Baumgarte bias, unclamped); LIMITED is a
644
+ **speculative (β=1) one-sided velocity constraint** that removes
645
+ exactly the approach velocity so the DOF *lands on* its stop (no
646
+ penetration, no rebound an inelastic end-stop) and self-gates when
647
+ far from the bound; only the push-out side of the bias is clamped so
648
+ a teleport is eased out, not yanked. Verified: a vertical slider
649
+ falls freely then stops dead on its lower stop (lands at the bound,
650
+ no overshoot/rebound, locked axes held); a spun hinge stops dead on
651
+ each ±end-stop with no rebound and holds. Angular position is the
652
+ small-angle measure (`2·sin(θ/2)`) accurate for modest ROM, see
653
+ phase 6 for wide cones. **MOTOR next** (target-velocity row, impulse
654
+ clamped to `±maxForce·h`).
655
+ 5. [x] SPRING (SPOOK soft) suspension, bungees, soft ragdolls.
656
+ `setLinearSpring(axis,k,c)` / `setAngularSpring(axis,k,c)`. A
657
+ compliant (regularised) row in the same unified solve: per substep
658
+ `denom = c + h·k`, compliance = 1/(h·denom)`, restoring bias
659
+ `(k/denom)·C`, softened mass `1/(K+γ)`; the iteration carries one
660
+ extra `+ γ·λ_accum` term (γ = 0 ⇒ the LOCKED/LIMITED/MOTOR rows are
661
+ bit-for-bit unchanged). Verified: a vertical strut settles at exactly
662
+ the m·g/k deflection and a stiffer spring sags less and stays stable;
663
+ an undamped spring oscillates about equilibrium (stores energy) while
664
+ a damped one comes to rest; a torsional spring holds a gravity-loaded
665
+ hinge at its balance angle. Suspension element ready (the simulated-
666
+ wheel option for phase 7); also the soft basis for cone-twist.
667
+ 6. [x] Cone-twist / swing-twist angular limits → ragdolls. Opt-in
668
+ `Joint.swingTwist` (or the `asConeTwist(twistLo,twistHi,swingY[,swingZ])`
669
+ preset) switches the angular position measure from the per-axis
670
+ small-angle vector to a swing-twist decomposition: angular X = twist
671
+ about the bone, Y/Z = swing off it, each an **exact** angle. The
672
+ existing LIMITED/SPRING/LOCKED rows are reused unchanged on those
673
+ positions, so a twist/swing limit holds at the true angle at wide
674
+ ROM (a 1.2 rad swing stops at 1.2, where the small-angle proxy
675
+ drifts to ~1.287). Verified: exact swing/twist stops, free-within-
676
+ cone, twist/swing independence; default (small-angle) path untouched.
677
+ **Decision inlined, not the Quaternion method.** Benchmarked the
678
+ allocation-free inlined `swing_twist_error` against
679
+ `Quaternion.computeSwingAndTwist` (`swing_twist.bench.spec.js`): the
680
+ inline is **~5x** faster than the method with reused out-params and
681
+ **~10x** vs the naive fresh-allocation form (object property access +
682
+ normalize + a quaternion multiply + GC). In the per-substep
683
+ per-joint hot loop that margin is worth the duplicated math, so the
684
+ solver inlines it (the Quaternion method stays for general callers).
685
+ 7. [x] Vehicle layer — **raycast-vehicle controller**
686
+ (`vehicle/RaycastVehicle.js`): single chassis body + raycast wheels.
687
+ Per frame (before `fixedUpdate`) each wheel casts its suspension ray,
688
+ applies a spring+damper suspension force along the contact normal
689
+ (`applyForceAt`), and a tyre-friction impulse (`applyImpulseAt`) —
690
+ lateral grip that cancels side-slip plus longitudinal drive/brake,
691
+ clamped together to a friction circle μ·N. `addWheel`, `setSteering`,
692
+ `setDriveForce`, `setBrake`; per-wheel runtime (contact, compression,
693
+ normal, spin) for rendering. A controller on top of the public
694
+ `raycast` + force API, not a new constraint; the 6-DOF spring+motor
695
+ is the simulated-wheel alternative. Verified: hovers on its springs
696
+ (4 contacts, settled), drives/coasts/brakes along its axis, tyre grip
697
+ arrests a sideways shove, steering turns it upright, and it free-falls
698
+ cleanly when airborne. Note: suspension is one dt-force per frame (not
699
+ per-substep), so a resting chassis carries a ~g·h velocity-sample
700
+ artifact (it hovers stably; position is steady to sub-cm). Ray
701
+ accuracy follows `PhysicsSystem.raycast` now narrowphase-exact for
702
+ sphere / box / capsule / mesh / heightmap ground.
703
+ 8. [ ] Extras: pulley, gear, conveyor (contact surface-velocity),
704
+ breakable-joint flag.
705
+
706
+ Foundation gaps — both now closed:
707
+ - [x] **Island integration.** Jointed dynamic-dynamic bodies are
708
+ unioned into one island (`IslandBuilder` Pass 1b), so a chain /
709
+ ragdoll sleeps and wakes as a unit; `__wake_joints` propagates wake
710
+ across a joint when one side is awake and the other asleep
711
+ (e.g. a kinematic/motor driver pulling a sleeping chain). Verified:
712
+ a damped chain settles and both links sleep in one sleep group.
713
+ - [x] **Generation-checked body references.** `solve_joints`,
714
+ `IslandBuilder` Pass 1b and `__wake_joints` all gate on
715
+ `storage.is_valid(packedId)`, so a joint to an unlinked / slot-reused
716
+ body goes inert instead of attaching to the wrong body or crashing.
717
+ Verified: unlinking a jointed body leaves the joint inert and the
718
+ survivor free.
719
+
720
+ References: Catto / Box2D-v3 joint solvers; Jolt's `Constraint` base
721
+ (`SetupVelocityConstraint` / `WarmStartVelocityConstraint` /
722
+ `SolveVelocityConstraint` / `SolvePositionConstraint`); PhysX D6 /
723
+ ODE joint taxonomy.
724
+
725
+ ### Stability
726
+ - [x] **Closed-form triangle-vs-primitive solvers** `sphere_triangle_contact`
727
+ / `box_triangle_contact` / `capsule_triangle_contact` (P1.1a–c), wired into
728
+ the concave decomposition dispatch in place of per-triangle GJK+EPA for
729
+ those primitives. Un-skipped the `narrowphase_concave.spec.js` ball-on-
730
+ heightmap / mesh-cube settle tests and the `PhysicsSystem.spec.js`
731
+ torus-knot test. Per-triangle GJK+EPA remains only as the fallback for
732
+ *other* convex shapes vs triangles. `compute_penetration` now routes
733
+ through the shared narrowphase dispatch (`deepest_pair_penetration`), so it
734
+ uses the closed-form per-triangle solvers too the old closed-mesh
735
+ over-report is gone; the half-space test is retained only as a
736
+ tunnel-recovery fallback.
737
+ - [x] **Edge-edge multi-point manifold** *resolved by design (no code
738
+ change needed).* An empirical SAT-source sweep over a wide range of
739
+ box-box orientations shows the single-point edge-cross branch only ever
740
+ wins for **transverse** edge crossings (inter-edge angle ≈ 83-90°), where
741
+ a single closest-pair point is geometrically exact. A near-parallel edge
742
+ pair gives a near-degenerate `edgeA × edgeB` that never becomes the SAT
743
+ minimum, so near-parallel ("line") edge contacts resolve through the
744
+ multi-point **face-clipping** path instead confirmed by regression
745
+ tests in `box_box_manifold.spec.js` (near-parallel tilted boxes 2
746
+ points; transverse crossing exactly 1 exact point). The originally
747
+ planned refinement targeted a case the geometry can't produce, so it is
748
+ closed rather than implemented.
749
+ - [x] **Per-contact source-collider tracking (materials)** multi-material
750
+ compound bodies now get accurate per-contact friction / restitution. The
751
+ narrowphase combines the specific (colliderA, colliderB) pair's
752
+ coefficients at dispatch time (the only place that knows the source
753
+ collider on each side `contact/combine_material.js`) and stamps them
754
+ into the manifold (CONTACT_STRIDE grown 14 16, offsets 14/15); the
755
+ solver reads them per contact instead of from the body's primary collider.
756
+ Regression test: an asymmetric-friction compound body yaws when shoved
757
+ (the grippy collider drags), and a symmetric control does not. Still
758
+ primary-collider-only: the contact-filter callback's collider args and the
759
+ body-level sensor / concave flags (smaller follow-up).
760
+ - [ ] **Joint-aware island sleep (ragdoll settle quality).** A draped,
761
+ self-colliding 10-joint ragdoll does not fully sleep in 10 ssurfaced by
762
+ a 1000-seed Monte-Carlo sweep (`PhysicsSystem.ragdoll.spec.js`, `.skip`):
763
+ for unlucky seeds a distal limb sustains a settled limit cycle (settled
764
+ finite-difference accel up to ~1094 m/s² / ~1479 rad/s² at a limb end vs a
765
+ ~55 m/s² median — bounded, non-growing, penetration-free, so a quality gap
766
+ not a divergence). The sleep test today is per-body `|v|²+|ω|²`; an island
767
+ over-constrained by cone-twist limits + self-contacts keeps small residual
768
+ jiggle above the per-body threshold so it never crosses into sleep.
769
+ Candidate fixes: sleep a jointed/contacting island on its AGGREGATE motion
770
+ rather than the per-body minimum, and/or a settled-regime relaxation (zero
771
+ restitution + extra position iterations) once an island's energy is low.
772
+ The sweep flags the worst seeds for replay. (Test infra also adds
773
+ per-point kinematics tracking joint anchors + limb ends, with
774
+ displacement→velocity→acceleration and the angular equivalents.)
775
+ - [x] **Box-on-heightmap settling RESOLVED.** A dynamic box dropped onto a
776
+ static HeightMapShape — flat seam straddle AND the sloped dip — settles to
777
+ full rest; the `PhysicsSystem.heightmap.spec.js` dip-drop reproducer is
778
+ un-skipped and passing. Fixed by the combination of the same-feature-id
779
+ contact de-dup in `redetect_pair_geometry` (1:1 claimed matching, so the
780
+ several contacts one triangle emits all sharing its feature_id — no longer
781
+ collapse onto a single candidate) and the HeightMapShape3D
782
+ collision-tessellation work; together they give the box a stable per-substep
783
+ contact set instead of a churning / collapsing one.
784
+
785
+ Root cause, for the record: the historical "never settles" rattle was a
786
+ depth divergence between the once-per-step `narrowphase_step` and the
787
+ per-substep `redetect_pair_geometry`. At the SAME pose, re-detection
788
+ over-reported a box-vs-triangle penetration as ~1 m (the "exit through the
789
+ far side" class) where narrowphase reported ~1 cm, launching the box into an
790
+ eternal vertical bounce (touch ~1 m over-correction separate fall back
791
+ re-contact). Both paths call the identical `dispatch_pair`, so the
792
+ divergence was the same-fid collapse corrupting the matched geometry; with
793
+ the de-dup in place the two paths now agree (verified: both report
794
+ [0.0100 ×4] at a 0.01-deep flat contact, in-cell and 4-triangle straddle).
795
+
796
+ Guards: `narrowphase/redetect_pair_geometry.spec.js` now pins BOTH
797
+ invariants contacts sharing a feature_id keep distinct witnesses, and
798
+ re-detect depth == narrowphase depth at a fixed pose (in-cell and
799
+ 4-triangle seam-straddle cases); `ecs/PhysicsSystem.heightmap.spec.js` pins
800
+ the observable settle. The depth-equality regression test the earlier
801
+ diagnostic trail asked for is in place, closing this out.
802
+
803
+ ### Performance / Scale
804
+ - [x] **Per-body linear CCD shape-cast** opt-in continuous collision for
805
+ fast movers where the speculative margin isn't enough.
806
+ `RigidBodyFlags.CCD` (off by default) + `ccd/linear_sweep.js`.
807
+ - **Approach (Box2D `b2_continuousPhysics`-style conservative
808
+ advancement).** After the substep solver produces each body's final
809
+ pose (between `apply_restitution` and the sleep test), a flagged fast
810
+ mover's primary collider is swept along its NET step translation
811
+ (start-of-step final pose) through the existing `shape_cast` TOI
812
+ engine. On the first blocker the body is clamped to the contact pose
813
+ and its inbound normal velocity removed (an inelastic stop); the next
814
+ discrete step resolves the now-touching contact with the real
815
+ material / restitution. Reuses `shape_cast` wholesale no new
816
+ geometry. Start positions captured in Stage 1 over the post-wake awake
817
+ set; the pass iterates the awake list in storage order (deterministic).
818
+ - **Motion gate absolute slop, NOT body extent.** A body is swept when
819
+ it moved more than `CCD_MIN_SWEEP_DISTANCE` (1 mm) this step. The gate
820
+ exists only to skip near-stationary bodies (degenerate sweep + cost).
821
+ It is deliberately *not* a fraction of the body's own size: tunnelling
822
+ risk is set by the **obstacle's** thickness, not the mover's a 2 m
823
+ sphere drifting 0.5 m/step still passes clean through a 1 cm floor, so
824
+ an extent-based gate would (wrongly) wait until the body moved more
825
+ than its own radius and miss every thin-obstacle tunnel below that
826
+ speed.
827
+ - **No self-clamp on resting/sliding contacts.** The sweep ignores an
828
+ impact at `t 0` (`CCD_INITIAL_OVERLAP_EPS`): an initial overlap is a
829
+ contact the body already sits/slides on, owned by the discrete solver,
830
+ not a tunnel clamping there would freeze the body to the surface.
831
+ - **Measured (falling-tower bench, 1000 random shapes onto a 1 cm floor,
832
+ 600 ticks; clean A/B on identical code via `ccdEnabled`).** This bench
833
+ is the WRONG validation vehicle for CCD, and the numbers prove it: CCD
834
+ off **10/1000 tunnel**, median **42.7 ms**/step; all 1000 flagged →
835
+ **50/1000 tunnel**, median **61.6 ms**. CCD makes it *worse*. The bench
836
+ is a dense-pile **squeeze-through** scenario 1000 bodies stacked on a
837
+ 1 cm floor, forced through by the column's weight over many steps which
838
+ is a *solver* limitation, not a missed single-step fly-through. CCD's
839
+ post-solve clamp + velocity-kill fights the solver in a dense settling
840
+ pile (it teleports mutually-stacking dynamics and breaks warm-start), so
841
+ flagging a whole pile is an anti-pattern. CCD's real job — stopping a
842
+ sparse fast mover against thin geometry is validated by
843
+ `ccd/linear_sweep.spec.js` (9 tests: a fast cube tunnels a thin floor
844
+ without the flag and is stopped with it; deterministic; resting bodies
845
+ undisturbed). **Correction:** an earlier version of this entry and the
846
+ commit message (`42163b0d4`) claimed a "0/1000 tunnel, 58.2 ms" on-leg
847
+ — that was never measured (a session tooling glitch); the real on-leg
848
+ is 50/1000 / 61.6 ms.
849
+ - **Scope (v1, documented):** linear sweep only (orientation fixed
850
+ through the sweep); primary same-entity collider (child-entity
851
+ colliders, synced outside the step, are not swept); EXACT against
852
+ static geometry, APPROXIMATE against other dynamics (their
853
+ start-of-step broadphase AABBs); the CCD stop is inelastic (the impact
854
+ itself doesn't bounce restitution applies on the next discrete
855
+ contact); Dynamic bodies only. A body both resting on one surface and
856
+ tunnelling another in the same step resolves only the resting contact
857
+ (the `t 0` skip) — rare; the next discrete step catches the rest.
858
+ - **Follow-ups (the dense-pile finding points the way):** sweep against
859
+ STATIC geometry only by default the dynamic-vs-dynamic clamp is the
860
+ source of the dense-pile interference above and is only ever
861
+ approximate anyway, so dropping it should make CCD purely additive
862
+ (stops you at static walls/floors, never fights the dynamic stack);
863
+ a proper TOI sub-solver for bullet-vs-dynamic; rotational / angular
864
+ CCD; multi-collider sweep for compound bodies.
865
+ - [x] **Broadphase BVH balance SAH rotation.** The dynamic AABB tree
866
+ (`core/bvh2/bvh3/BVH.js`, a Box2D port) used SAH-cost insertion but a
867
+ *height-only* AVL rotation (`balance_height`): height-balanced yet not
868
+ SAH-balanced, so queries walked more nodes than needed. Replaced the
869
+ rotation in `bubble_up_update` with `balance_rotate` the Box2D-v3 /
870
+ Kensler SAH-reducing rotation (for node A with children B, C, evaluate the
871
+ four child↔grandchild swaps and apply the one that most reduces the
872
+ surface-area cost). Deterministic; identical pair set.
873
+ - Measured (same-session A/B, heavy benches): raycast **−9%**
874
+ (28.2→25.6 µs/ray), falling-tower median **−10%**, settling-grid
875
+ median **−12%**, and the **990/1000-churn stress −27%**
876
+ (63.95→46.68 ms mean over 10k ticks) — biggest where the tree churns
877
+ hardest. Determinism (8-trial bit-identical) holds.
878
+ - **Insertion cost (measured):** `balance_rotate` does 4 surface-area
879
+ evaluations per bubble-up level vs `balance_height`'s single height
880
+ compare, so *pure bulk insertion* is **~1.4–1.5× slower** the 100k
881
+ synthetic insert bench (`BVH.spec.js`, drift-controlled interleaved
882
+ A/B) drops from **~37k ~25k inserts/sec** (~27→~40 µs/insert). This
883
+ is the balancer's worst case (insert-only, zero queries/refits to
884
+ amortise against). It does not show up end-to-end: static trees are
885
+ built once then queried forever, dynamic bodies insert once then
886
+ refit/query every frame, and even the 990/1000-swap stress test the
887
+ maximal insert-churn workload — is net **−27%**. Accepted.
888
+ - **Tradeoff (documented):** the contact solver's Gauss-Seidel order
889
+ follows broadphase traversal order (see `generate_pairs`), so the
890
+ different tree shape shifts convergence on near-aligned stacks — the
891
+ synthetic 128-cube wall now sleeps at ~10 s (was ~6.9 s). It still
892
+ settles, doesn't creep / topple (all bug-guard assertions hold); only
893
+ the sleep *time* moved (that test's budget was bumped 9→11 s with a
894
+ note). Random-shape scenes (falling tower) were faster *and* settled
895
+ fine.
896
+ - **Follow-up:** decouple the solve order from tree shape — sort the
897
+ broadphase pair list by `(idA, idB)` before narrowphase so contact
898
+ order is body-id-deterministic regardless of tree shape. Then no tree
899
+ change can affect convergence (and the stack settles identically under
900
+ either balancer). Has a per-step sort cost + wide test re-baseline, so
901
+ it's its own task. `balance_height` is retained for comparison /
902
+ fallback.
903
+ - [ ] **Per-island parallel solve**: today's island data layout would
904
+ allow worker-based solving once `SharedArrayBuffer` is available.
905
+ Out-of-scope unless / until SAB is universally usable.
906
+
907
+ ### Features
908
+ - [ ] **Convex collision proxies for dynamic concave bodies.** The long-term
909
+ replacement for the interim per-substep concave re-detection (see
910
+ Limitations) and how every major engine handles dynamic non-convex
911
+ shapes: collide a *few* convex pieces, never the raw concave mesh.
912
+ 1. **3D convex hull builder** (meep has only 2D hulls today —
913
+ `core/geom/2d/convex-hull/`). A single hull of a mesh is one
914
+ collider / one broadphase leaf and covers the overwhelming majority
915
+ of dynamic objects (thrown props, debris). Pairs with the existing
916
+ "Convex hull shape + eigen-inertia" item below.
917
+ 2. **Few-hull (V-HACD-style) approximate convex decomposition** for
918
+ shapes whose concavity matters (a cup, a chair): ~8–64 fat convex
919
+ hulls = 8–64 colliders, two orders of magnitude below a tet mesh.
920
+ Each hull is convex stable contact feature the TGS analytic refresh
921
+ is exact no per-substep re-detection, no rocking. Granularity is the
922
+ whole point: collider/BVH-leaf count must stay small for an *awake*
923
+ dynamic body (the volumetric tet-mesher under `core/geom/3d/tetrahedra/`
924
+ is the wrong tool here — thousands of pieces — and belongs to a future
925
+ FEM/soft-body subsystem, not rigid collision).
926
+ - [ ] **Convex hull shape** with eigen-based principal-axes inertia
927
+ derivation. Hooks `matrix_eigenvalues_in_place` from the existing
928
+ linalg layer.
929
+ - [~] **Cylinder / cone shapes.**
930
+ - [x] **`CylinderShape3D`** — Y-aligned solid cylinder (radius + full
931
+ height, flat caps; the capsule's flat-cap sibling). Exact `support`,
932
+ capped-cylinder SDF, bounds, `contains` / `nearest_point` /
933
+ volume-sampling, equals/hash, `'cylinder'` JSON tag, `isCylinderShape3D`
934
+ marker. Convex routes through the narrowphase **GJK + EPA** fallback
935
+ (no marker dispatch needed); spec asserts overlap-detected +
936
+ MTV-separates vs sphere/box. Closed-form cylinder-vs-X contact pairs
937
+ are a future refinement (the curved side is the usual smooth-support
938
+ EPA case — same status as pre-closed-form sphere/capsule).
939
+ - [ ] Closed-form cylinder contact pairs (cylinder × box / sphere / capsule
940
+ / plane) for multi-point cap manifolds + stable resting.
941
+ - [ ] **Cone shape** (+ closed-form / GJK fallback).
942
+
943
+ ### Rendering integration
944
+ - [ ] **Fixed-step render interpolation.** Not implemented yet. Physics writes
945
+ each body's pose straight into the ECS `Transform` once per fixed step
946
+ (`EntityManager.fixedUpdateStepSize`); with a render rate that doesn't match
947
+ the fixed rate, the rendered motion aliases (stutter / temporal aliasing,
948
+ worst at low fixed rates). Designed as a cross-cutting system (render +
949
+ physics-as-producer + network-as-producer), reusing the network package's
950
+ agnostic interpolation primitives rather than a physics-local mechanism.
951
+ Full design + phasing: **see
952
+ [`INTEPOLATION_SYSTEM_PLAN.md`](./INTEPOLATION_SYSTEM_PLAN.md)**. Locked
953
+ decisions: a neutral shared interpolation log/adapters (lifted out of
954
+ `network/`); physics restores authoritative pose from the log at step start
955
+ (no solver rewiring) and records moved bodies' snapshots at step end;
956
+ blend `Transform` at `preRender`; O(moving) not O(N); rewind-safe via the
957
+ tick-keyed log.
958
+
959
+ ### API polish
960
+ - [x] **`overlap(shape, position, rotation, output, output_offset,
961
+ filter?)`** broadphase + narrowphase overlap query for kinematic
962
+ / AOE / selection use cases. Body_ids written into a caller-sized
963
+ Uint32Array buffer. Convex query shape only; concave candidates
964
+ are routed through the per-triangle decomposition path.
965
+ - [x] **`shapeCast(ray, shape, rotation, result, filter?)`** for
966
+ character controllers and kinematic shape sweeps. Broadphase
967
+ swept-AABB against both BVHs; per-candidate AABB-slab interval
968
+ narrowing + coarse step + GJK bisection for time-of-impact. The
969
+ output `result.normal` is the true contact-surface normal at the
970
+ kiss point, computed by re-running GJK + EPA at `best_t` on the
971
+ winning candidate (falls back to `-ray.direction` only on EPA
972
+ degeneracies).
973
+ - [x] **`compute_penetration(out_direction, shape_a, pos_a, rot_a,
974
+ shape_b, pos_b, rot_b)`** standalone geometry primitive (no
975
+ PhysicsSystem) for resolving overlap between two shapes at given
976
+ poses. Returns depth + outward direction. **Hardened** to route through
977
+ the shared narrowphase dispatch (`deepest_pair_penetration`): exact
978
+ closed-form for sphere/box/capsule pairs (box-box via SAT), GJK+EPA for
979
+ general convex, closed-form per-triangle for convex × concave; the
980
+ half-space test is retained only for tunnel recovery.
981
+
982
+ ### Raycast narrowphase (done)
983
+
984
+ **Problem.** `raycast` (and the suspension ray inside `RaycastVehicle`) resolves
985
+ only to the nearest BVH leaf's *inflated* AABB: `result.t` is the distance to
986
+ that fattened box and `result.normal` is its face normal. Exact for an
987
+ axis-aligned box (modulo the broadphase margin), coarse for spheres / capsules /
988
+ rotated boxes / meshes / heightmaps. Refine each candidate against the true
989
+ shape to return the exact surface distance + normal. `shapeCast` already does
990
+ this for swept convex shapes via GJK+EPA; `raycast` should get the same
991
+ treatment with cheap analytic primitives on the hot path.
992
+
993
+ **Design.** Mirror `narrowphase_step`'s dispatch: closed-form ray tests for the
994
+ common primitives, a generic GJK fallback for the rest. The structural change is
995
+ in the BVH walk — the nearest *leaf AABB* is **not** the nearest *shape hit* (a
996
+ ray can clip a near fat-AABB but miss its shape while hitting a farther one), so
997
+ every crossing leaf must be refined, with subtrees pruned by inflated-AABB
998
+ `t_near` vs the best *refined* `t` (conservative-correct: a shape hit is always
999
+ its tight AABB entry its inflated AABB entry). A leaf whose ray crosses the
1000
+ fat AABB but misses the true shape now contributes **no hit** — the key
1001
+ correctness gain.
1002
+
1003
+ Phasing (each phase: implement → spec → run from `H:/git/moh` commit):
1004
+
1005
+ 1. [x] **Ray-primitive helpers** originally landed as `narrowphase/ray_shapes.js`,
1006
+ later EXTRACTED to `core/geom/3d/{sphere,box,capsule}/{sphere3,box3,capsule3}_raycast.js`
1007
+ (the (`t`, normal, miss = `Infinity`, first-hit-from-outside) convention is a
1008
+ fine general raycast contract, and a ray-vs-capsule primitive was otherwise
1009
+ missing engine-wide; the dispatch still shares one ray→local transform across
1010
+ them). Built local-frame (unit direction ⇒ `t` preserved;
1011
+ rotate the local normal back). Triangle MT is inlined in the concave path
1012
+ (the existing `computeTriangleRayIntersection` writes a `SurfacePoint3` and
1013
+ returns no `t` unsuited to the buffer-flyweight loop). Colocated specs.
1014
+ 2. [x] **Ray-narrowphase dispatch** `narrowphase/refine_ray_hit.js`:
1015
+ `(shape, position, rotation, ox,oy,oz, dx,dy,dz, tMax, outNormal) t`.
1016
+ Type-marker dispatch to the analytic primitives. Originally landed for
1017
+ `isSphereShape3D` / `isBoxShape3D` / `isCapsuleShape3D` only, with the other
1018
+ half — "a generic convex fallback for `TransformedShape3D` / `UnionShape3D` /
1019
+ other (GJK ray-cast, or reuse `shape_cast` with a zero-radius
1020
+ `PointShape3D`)" — ticked but **never written**; those shapes returned
1021
+ `RAY_REFINE_UNSUPPORTED` and were answered by their bounding box, which is
1022
+ what BUG-7 turned out to be. Closed instead by making the dispatch total over
1023
+ the engine's own shapes: closed-form tests for the convex hull, cylinder,
1024
+ triangle and point, and a local-frame recursion for the three wrappers. The
1025
+ generic GJK ray-cast is no longer needed — it was only ever the plan for
1026
+ shapes that now have exact tests.
1027
+ 3. [x] **Concave path** in the dispatch: for `is_convex === false` (mesh /
1028
+ heightmap), enumerate the triangles overlapping the ray's swept AABB
1029
+ (`mesh_enumerate_triangles` / `heightmap_enumerate_triangles`), Möller–
1030
+ Trumbore each, take the nearest; normal from the triangle winding.
1031
+ 4. [x] **Rewire `queries/raycast.js`**: at each leaf, call `refine_ray_hit` on
1032
+ the true shape + pose instead of accepting the AABB `t_near`; track the best
1033
+ refined `(t, body, normal)`; keep subtree pruning on inflated-AABB `t_near`.
1034
+ Same signature / `PhysicsSurfacePoint` result; drop the AABB-face-normal
1035
+ block. Multi-collider bodies still resolve the primary collider only
1036
+ (inherited BVH-leaf limitation; note it).
1037
+ 5. [x] **Tests**: per-shape exactness (sphere / OBB / capsule / mesh /
1038
+ heightmap) exact `t` and true normal; the **fat-AABB-cross-but-shape-miss
1039
+ no hit** case (the correctness win); nearest-of-several across a near miss;
1040
+ `filter` and `tMax` honoured. Re-verify `RaycastVehicle` (ride height now
1041
+ exact tighten the test bands if they shift by the old broadphase margin).
1042
+ 6. [x] **Bench + docs**: a raycast micro-bench (analytic fast-path cost; confirm
1043
+ the fat-AABB-miss rejection doesn't regress throughput); update the "Public
1044
+ queries" entry, `raycast.js` header, and the `RaycastVehicle` "AABB-level"
1045
+ caveat once exact.
1046
+
1047
+ Note: this sharpens `RaycastVehicle` suspension on non-box ground and every
1048
+ shape query; it does not change the broadphase or any API surface.
1049
+
1050
+ ---
1051
+
1052
+ ## Future / out-of-scope
1053
+
1054
+ These are explicit architectural exclusions or post-v1 explorations.
1055
+
1056
+ ### Architecture
1057
+ - **Cross-runtime bit-exact determinism**: a soft-float library would
1058
+ replace `Math.sin/cos/exp/log/pow` in the hot path. The codebase is
1059
+ already structured to make this a swap-in at `quat3_integrate.js` and
1060
+ tangent-basis construction in `build_manifold.js`. Not pursued because
1061
+ the same-runtime determinism we have covers the common cases (single-
1062
+ device replay, networked lockstep where all clients run the same JS
1063
+ engine).
1064
+ - **WASM / SIMD**: the engine targets pure-JS portability. SIMD would
1065
+ invalidate the determinism story (V8 doesn't expose deterministic
1066
+ Float64x2 ops).
1067
+ - **Multi-threaded solver**: workers don't share memory cheaply without
1068
+ `SharedArrayBuffer` plus the COOP/COEP HTTP headers, which are not
1069
+ always available. Single-threaded is good-enough for the awake-body
1070
+ budget that matters.
1071
+ - **Packed-SoA body dynamics state DECIDED AGAINST; do not re-open.** A
1072
+ reviewer will note that `BodyStorage` is SoA for *identity* (entity /
1073
+ generation / kind / flags / awake-set) but the per-body *dynamics* state —
1074
+ velocity, inertia, mass, force/torque accumulators — lives on the `RigidBody`
1075
+ **component object** (each carrying several `Vector3` = `Float64Array` + an
1076
+ `onChanged` Signal), in a sparse `__bodies[]` array reached by pointer-chase,
1077
+ not packed by slot. The same is true of `Collider` and `Joint`. This is a
1078
+ deliberate, settled choice, not an oversight: **meep is an ECS engine, and
1079
+ `RigidBody` / `Collider` / `Joint` are public-API components.** Them being
1080
+ first-class objects is a UX choice and uniformity with the rest of the engine —
1081
+ it is what makes them authorable, serializable (`toJSON`/`fromJSON` + binary
1082
+ adapters), value-diffable (`equals`/`hash` for netcode replication), and
1083
+ observable (`Vector3.onChanged`), exactly like every other component. A
1084
+ packed-SoA body pool would buy locality on the *awake* integration sweeps at
1085
+ the cost of breaking that uniformity and the public component contract. The
1086
+ design instead leans on "mostly-sleeping" (per-body iteration is over the
1087
+ *awake* set only), keeps the genuinely O(contacts) inner loop in flat SoA
1088
+ (manifolds + solver scratch), and has the hot paths index the component
1089
+ vectors' `Float64Array` backing directly to skip the observer (see
1090
+ Determinism). This is final — do not resurface it as a "fix".
1091
+ - **Single in-flight solve (module-scoped solver scratch).**
1092
+ `solver/solve_contacts.js` keeps its cross-stage state (the `g_*` counters and
1093
+ the `scratch_*` arrays) at module scope — one copy shared by all worlds, not
1094
+ per `PhysicsSystem`. Deliberate: every world reuses one set of scratch, and
1095
+ it's safe because the engine is single-threaded and steps one `fixedUpdate` at
1096
+ a time. The ceiling it sets: two `PhysicsSystem` instances cannot be stepped
1097
+ concurrently or re-entrantly (the second clobbers the first's solver scratch).
1098
+ Accepted for a single-world, single-threaded engine; lifting it would need
1099
+ per-system scratch (or a solver-context object threaded through the stages).
1100
+
1101
+ ### Simulation extensions
1102
+ - **Soft body / cloth / fluids**: the SoA layout in `BodyStorage` and the
1103
+ manifold cache are rigid-body shaped. A soft-body system would be a
1104
+ parallel subsystem, not an extension.
1105
+ - **Reduced-coordinate articulations** (MuJoCo / Featherstone-style):
1106
+ game-physics audience runs in maximal coordinates by convention. Not
1107
+ on the roadmap.
1108
+
1109
+ ### Game-side
1110
+ - **Vehicle physics** (suspensions, drivetrains): a domain layer that
1111
+ sits on top of the rigid-body primitives, not in `meep/`.
1112
+ - **Character controllers**: same — `engine/control/first-person/` is the
1113
+ natural home.
1114
+
1115
+ ---
1116
+
1117
+ ## Notable design files
1118
+
1119
+ - Original design plan: `C:\Users\Alex\.claude\plans\let-s-plan-to-implement-transient-harp.md`
1120
+ - This file (state of play): `engine/physics/PLAN.md`