@woosh/meep-engine 3.0.2 → 3.2.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 (1823) hide show
  1. package/LICENSE +188 -188
  2. package/README.md +152 -145
  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/core/binary/BitImage2.js +246 -246
  134. package/src/core/binary/align_32.js +21 -21
  135. package/src/core/binary/data_view/DataType2DataViewReaders.js +23 -23
  136. package/src/core/binary/data_view/DataType2DataViewWriters.js +25 -25
  137. package/src/core/binary/hash/XXH128Hash.js +55 -55
  138. package/src/core/binary/hash/XXH3_SECRET.js +25 -25
  139. package/src/core/binary/hash/xxh3_128.js +341 -341
  140. package/src/core/binary/hash/xxh3_64.js +241 -241
  141. package/src/core/binary/hash/xxh3_common.js +476 -476
  142. package/src/core/binary/lz4/LZ4_MAX_INPUT_SIZE.js +9 -9
  143. package/src/core/binary/lz4/lz4_compress_block.js +385 -385
  144. package/src/core/binary/lz4/lz4_compress_bound.js +25 -25
  145. package/src/core/binary/lz4/lz4_decompress_block.js +139 -139
  146. package/src/core/binary/lz4/lz4_decompress_bound.js +30 -30
  147. package/src/core/binary/meshopt/MESHOPT_GROUP_SIZE.js +18 -18
  148. package/src/core/binary/meshopt/MeshoptCompressionFilter.js +30 -30
  149. package/src/core/binary/meshopt/MeshoptCompressionMode.js +21 -21
  150. package/src/core/binary/meshopt/__meshopt_test_streams.js +237 -237
  151. package/src/core/binary/meshopt/meshopt_decode.js +73 -73
  152. package/src/core/binary/meshopt/meshopt_decode_byte_group.js +67 -67
  153. package/src/core/binary/meshopt/meshopt_decode_data_block.js +59 -59
  154. package/src/core/binary/meshopt/meshopt_decode_filter_color.js +69 -69
  155. package/src/core/binary/meshopt/meshopt_decode_filter_exponential.js +43 -43
  156. package/src/core/binary/meshopt/meshopt_decode_filter_octahedral.js +60 -60
  157. package/src/core/binary/meshopt/meshopt_decode_filter_quaternion.js +71 -71
  158. package/src/core/binary/meshopt/meshopt_decode_index_buffer.js +294 -294
  159. package/src/core/binary/meshopt/meshopt_decode_index_sequence.js +115 -115
  160. package/src/core/binary/meshopt/meshopt_decode_vertex_buffer.js +381 -381
  161. package/src/core/binary/meshopt/meshopt_read_uint_var.js +59 -59
  162. package/src/core/binary/reinterpret_float32_as_uint32.js +13 -13
  163. package/src/core/binary/reinterpret_uint32_as_float32.js +14 -14
  164. package/src/core/binary/type/DataType2TypedArrayConstructorMapping.js +35 -35
  165. package/src/core/bvh2/bvh3/ebvh_geometry_query_any_triangle_ray.js +149 -149
  166. package/src/core/bvh2/bvh3/ebvh_sort_for_traversal_depth_first.js +96 -96
  167. package/src/core/bvh2/bvh3/query/bvh_query_depth_range_in_frustum.js +101 -101
  168. package/src/core/bvh2/bvh3/query/bvh_query_leaves_generic.js +77 -77
  169. package/src/core/bvh2/bvh3/query/bvh_query_leaves_ray.js +90 -90
  170. package/src/core/bvh2/bvh3/query/bvh_query_leaves_ray_segment.js +94 -94
  171. package/src/core/bvh2/bvh3/query/bvh_query_user_data_generic.js +86 -86
  172. package/src/core/bvh2/bvh3/query/bvh_query_user_data_intersects_capsule.js +101 -101
  173. package/src/core/bvh2/bvh3/query/bvh_query_user_data_intersects_sphere.js +92 -92
  174. package/src/core/bvh2/bvh3/query/bvh_query_user_data_ray.js +96 -96
  175. package/src/core/bvh2/bvh3/query/bvh_query_user_data_ray_segment.js +100 -100
  176. package/src/core/bvh2/visual/BVHGeometry.js +131 -131
  177. package/src/core/bvh8/BVH8.js +436 -436
  178. package/src/core/bvh8/aabb3_round_to_float32.js +22 -22
  179. package/src/core/bvh8/build/BVH8Converter.js +588 -588
  180. package/src/core/bvh8/build/TriangleCluster.js +122 -122
  181. package/src/core/bvh8/build/aabb3_compute_merge_cost.js +28 -28
  182. package/src/core/bvh8/build/aabb3_from_triangle_by_index.js +17 -17
  183. package/src/core/bvh8/build/bvh8_build_for_geometry.js +303 -303
  184. package/src/core/bvh8/build/bvh8_from_proxy.js +256 -256
  185. package/src/core/bvh8/build/byte.js +9 -9
  186. package/src/core/bvh8/build/encode_bounds_e.js +11 -11
  187. package/src/core/bvh8/bvh8_convert_to_dot.js +132 -132
  188. package/src/core/bvh8/bvh8_count_primitives.js +98 -98
  189. package/src/core/bvh8/bvh8_geometry_validate.js +138 -138
  190. package/src/core/bvh8/bvh8_geometry_validate_indirect.js +166 -166
  191. package/src/core/bvh8/bvh8_get_node_bounds.js +34 -34
  192. package/src/core/bvh8/bvh8_get_node_child_bounds.js +52 -52
  193. package/src/core/bvh8/bvh8_node_child_surface_area.js +18 -18
  194. package/src/core/bvh8/bvh8_node_count_triangles.js +27 -27
  195. package/src/core/bvh8/bvh8_quality.js +72 -72
  196. package/src/core/bvh8/bvh8_validate_structure.js +87 -87
  197. package/src/core/clipboard/obtainClipBoard.js +29 -29
  198. package/src/core/clipboard/safeClipboardReadText.js +55 -55
  199. package/src/core/clipboard/safeClipboardWriteText.js +23 -23
  200. package/src/core/collection/array/typed/typed_array_value_denormalize.js +52 -52
  201. package/src/core/collection/array/typed/typed_array_value_normalize.js +51 -51
  202. package/src/core/collection/union-find/union_find.js +79 -79
  203. package/src/core/color/YCoCg/rgb_to_ycocg.js +20 -20
  204. package/src/core/color/YCoCg/ycocg_to_rgb.js +17 -17
  205. package/src/core/color/colormap/MAGMA_LUT.js +26 -26
  206. package/src/core/color/illuminant/D65_TRISTIMULUS_XYZ.js +19 -19
  207. package/src/core/color/lab/lab_apply_hunt_adjustment.js +27 -27
  208. package/src/core/color/lab/lab_distance_hyab.js +23 -23
  209. package/src/core/color/lab/xyz_to_lab.js +62 -62
  210. package/src/core/color/operations/color_darken.js +24 -24
  211. package/src/core/color/operations/color_desaturate.js +24 -24
  212. package/src/core/color/operations/color_lighten.js +24 -24
  213. package/src/core/color/operations/color_saturate.js +24 -24
  214. package/src/core/color/operations/color_scale_okhsv_channel.js +45 -45
  215. package/src/core/color/tonemap/tonemap_aces.js +41 -41
  216. package/src/core/color/ycxcz/xyz_to_ycxcz.js +28 -28
  217. package/src/core/color/ycxcz/ycxcz_to_xyz.js +19 -19
  218. package/src/core/dom/isImageBitmap.js +12 -12
  219. package/src/core/function/frameThrottle.js +23 -23
  220. package/src/core/geom/2d/aabb/aabb2_array_combine.js +7 -7
  221. package/src/core/geom/2d/aabb/aabb2_compute_area.js +7 -7
  222. package/src/core/geom/2d/aabb/aabb2_compute_center_from_multiple.js +7 -7
  223. package/src/core/geom/2d/aabb/aabb2_compute_overlap.js +7 -7
  224. package/src/core/geom/2d/aabb/aabb2_from_v2_array.js +7 -7
  225. package/src/core/geom/2d/aabb/aabb2_intersects_point.js +7 -7
  226. package/src/core/geom/2d/aabb/aabb2_overlap_exists.js +7 -7
  227. package/src/core/geom/2d/circle/circle_compute_circle_intersection.js +7 -7
  228. package/src/core/geom/2d/circle/circle_compute_circle_penetration.js +7 -7
  229. package/src/core/geom/2d/circle/circle_intersects_point.js +7 -7
  230. package/src/core/geom/2d/compute_polygon_area_2d.js +7 -7
  231. package/src/core/geom/2d/convex-hull/fixed_convex_hull_humus.js +7 -7
  232. package/src/core/geom/2d/convex-hull/fixed_convex_hull_relaxation.js +7 -7
  233. package/src/core/geom/2d/convex-hull/orientation3_array.js +7 -7
  234. package/src/core/geom/2d/intersect_ray_2d.js +7 -7
  235. package/src/core/geom/2d/line/line2_distance_to_point_sqr.js +7 -7
  236. package/src/core/geom/2d/line/line2_segment_intersection_fraction_2d.js +44 -44
  237. package/src/core/geom/2d/line/line_segment_compute_line_segment_intersection_2d.js +7 -7
  238. package/src/core/geom/2d/line/line_segment_compute_line_segment_intersection_array_2d.js +7 -7
  239. package/src/core/geom/2d/line/line_segment_compute_line_segment_intersection_vectors_2d.js +7 -7
  240. package/src/core/geom/2d/line/line_segment_compute_line_segment_intersection_vectors_array_2d.js +7 -7
  241. package/src/core/geom/2d/line/line_segment_intersection_fraction_2d.js +7 -7
  242. package/src/core/geom/2d/line/line_segment_line_segment_intersection_exists_2d.js +7 -7
  243. package/src/core/geom/2d/line/segment2_intersects_segment2_2d.js +25 -25
  244. package/src/core/geom/2d/line/segment2_segment_compute_intersection_array_2d.js +46 -46
  245. package/src/core/geom/2d/line/segment2_segment_intersection_fractions.js +52 -52
  246. package/src/core/geom/2d/oriented-box/min_area_rect_2d.js +69 -69
  247. package/src/core/geom/2d/polygon/TRIANGULATION_DESIGN.md +433 -433
  248. package/src/core/geom/2d/polygon/polygon2_clip_axis_halfplane.js +51 -51
  249. package/src/core/geom/2d/polygon/polygon2_clip_halfplane.js +50 -50
  250. package/src/core/geom/2d/polygon/polygon2_is_counter_clockwise.js +84 -84
  251. package/src/core/geom/2d/polygon/polygon2_signed_area.js +52 -52
  252. package/src/core/geom/2d/polygon/polygon2_triangulate.corpus.js +86 -86
  253. package/src/core/geom/2d/polygon/polygon2_triangulate.js +1280 -1280
  254. package/src/core/geom/2d/polygon/polygon2_triangulation_deviation.js +77 -77
  255. package/src/core/geom/2d/quad-tree/qt_collect_by_circle.js +39 -39
  256. package/src/core/geom/2d/quad-tree/qt_match_data_by_circle.js +101 -101
  257. package/src/core/geom/2d/quad-tree/qt_query_data_nearest_to_point.js +7 -7
  258. package/src/core/geom/2d/rectangle_to_aabb.js +7 -7
  259. package/src/core/geom/2d/spline_bezier2_2d.js +7 -7
  260. package/src/core/geom/2d/triangle/tri2_rasterize_conservative.js +99 -99
  261. package/src/core/geom/2d/triangle/tri2_signed_area.js +35 -35
  262. package/src/core/geom/2d/triangle2d_compute_area.js +7 -7
  263. package/src/core/geom/2d/triangle2d_contains_point.js +7 -7
  264. package/src/core/geom/2d/triangle2d_get_barycentric.js +7 -7
  265. package/src/core/geom/2d/v2_morton_encode.js +23 -23
  266. package/src/core/geom/3d/aabb/aabb3_array_intersects_frustum_degree.js +7 -7
  267. package/src/core/geom/3d/aabb/aabb3_box_surface_area_2.js +7 -7
  268. package/src/core/geom/3d/aabb/aabb3_build_frustum.js +5 -5
  269. package/src/core/geom/3d/aabb/aabb3_closest_point_on_surface.js +57 -57
  270. package/src/core/geom/3d/aabb/aabb3_combined_surface_area.js +7 -7
  271. package/src/core/geom/3d/aabb/aabb3_compute_half_surface_area.js +7 -7
  272. package/src/core/geom/3d/aabb/aabb3_compute_projected_area.js +7 -7
  273. package/src/core/geom/3d/aabb/aabb3_compute_surface_area.js +7 -7
  274. package/src/core/geom/3d/aabb/aabb3_detailed_volume_intersection.js +7 -7
  275. package/src/core/geom/3d/aabb/aabb3_estimate_projected_area2.js +7 -7
  276. package/src/core/geom/3d/aabb/aabb3_from_threejs_geometry.js +7 -7
  277. package/src/core/geom/3d/aabb/aabb3_from_triangle_group.js +46 -46
  278. package/src/core/geom/3d/aabb/aabb3_intersects_frustum_degree.js +7 -7
  279. package/src/core/geom/3d/aabb/aabb3_intersects_line_segment.js +7 -7
  280. package/src/core/geom/3d/aabb/aabb3_near_distance_to_intersection_ray_segment.js +7 -7
  281. package/src/core/geom/3d/aabb/aabb3_nearest_point_on_surface.js +7 -7
  282. package/src/core/geom/3d/aabb/aabb3_ray_segment_entry_distance.js +95 -95
  283. package/src/core/geom/3d/aabb/aabb3_raycast.js +7 -7
  284. package/src/core/geom/3d/aabb/aabb3_transform_oriented.js +93 -93
  285. package/src/core/geom/3d/aabb/aabb3_transform_oriented_inverse.js +82 -82
  286. package/src/core/geom/3d/aabb/compute_aabb_from_points.js +7 -7
  287. package/src/core/geom/3d/aabb/compute_triangle_group_aabb3.js +7 -7
  288. package/src/core/geom/3d/atlas/ATLAS_PORT_PLAN.md +486 -486
  289. package/src/core/geom/3d/atlas/BFF_PLAN.md +174 -174
  290. package/src/core/geom/3d/atlas/Chart.js +77 -77
  291. package/src/core/geom/3d/atlas/REVIEW_02.md +392 -392
  292. package/src/core/geom/3d/atlas/atlas_bench_lib.js +315 -315
  293. package/src/core/geom/3d/atlas/atlas_chart_type.js +16 -16
  294. package/src/core/geom/3d/atlas/atlas_compute_charts.js +188 -188
  295. package/src/core/geom/3d/atlas/atlas_extract_chart.js +107 -107
  296. package/src/core/geom/3d/atlas/atlas_generate.js +50 -50
  297. package/src/core/geom/3d/atlas/atlas_test_fixtures.js +266 -266
  298. package/src/core/geom/3d/atlas/atlas_write_chart_uvs.js +36 -36
  299. package/src/core/geom/3d/atlas/data/atlas_build_edge_dihedrals.js +62 -62
  300. package/src/core/geom/3d/atlas/data/atlas_build_edge_lengths.js +33 -33
  301. package/src/core/geom/3d/atlas/data/atlas_build_face_areas.js +50 -50
  302. package/src/core/geom/3d/atlas/io/atlas_mesh_weld_colocals.js +39 -39
  303. package/src/core/geom/3d/atlas/io/bt_loop_uv.js +35 -35
  304. package/src/core/geom/3d/atlas/pack/ChartPlacement.js +46 -46
  305. package/src/core/geom/3d/atlas/param/atlas_chart_parameterize_bff.js +269 -269
  306. package/src/core/geom/3d/atlas/param/atlas_chart_parameterize_lscm.js +209 -209
  307. package/src/core/geom/3d/atlas/param/atlas_chart_parameterize_ortho.js +38 -38
  308. package/src/core/geom/3d/atlas/param/atlas_chart_stretch_metric.js +86 -86
  309. package/src/core/geom/3d/atlas/param/atlas_piecewise_resegment.js +183 -183
  310. package/src/core/geom/3d/atlas/param/fit_plane_basis.js +66 -66
  311. package/src/core/geom/3d/atlas/segment/RegionGraph.js +40 -40
  312. package/src/core/geom/3d/atlas/segment/atlas_build_region_graph.js +136 -136
  313. package/src/core/geom/3d/atlas/segment/atlas_chart_roundness_delta.js +30 -30
  314. package/src/core/geom/3d/atlas/segment/atlas_compute_planar_regions.js +77 -77
  315. package/src/core/geom/3d/atlas/segment/atlas_merge_charts.js +116 -116
  316. package/src/core/geom/3d/atlas/segment/atlas_segment_charts.js +271 -271
  317. package/src/core/geom/3d/atlas/segment/atlas_segment_charts_atomic.js +191 -191
  318. package/src/core/geom/3d/box/box3_projected_half_extent.js +35 -35
  319. package/src/core/geom/3d/capsule/capsule_intersects_aabb3_closed.js +67 -67
  320. package/src/core/geom/3d/capsule/capsule_intersects_aabb3_iterative.js +137 -137
  321. package/src/core/geom/3d/compute_circle_bounding_box.js +7 -7
  322. package/src/core/geom/3d/cone/computeConeBoundingBox.js +7 -7
  323. package/src/core/geom/3d/cone/computeConePlaneSide.js +7 -7
  324. package/src/core/geom/3d/cone/compute_bounding_cone_of_2_cones.js +7 -7
  325. package/src/core/geom/3d/cylinder/cylinder3_raycast.d.ts +38 -0
  326. package/src/core/geom/3d/cylinder/cylinder3_raycast.d.ts.map +1 -0
  327. package/src/core/geom/3d/cylinder/cylinder3_raycast.js +105 -0
  328. package/src/core/geom/3d/equirectangular/equirectangular_direction_to_uv.js +18 -18
  329. package/src/core/geom/3d/equirectangular/equirectangular_uv_to_direction.js +24 -24
  330. package/src/core/geom/3d/frustum/clipping_volume_matrix4_project.js +7 -7
  331. package/src/core/geom/3d/frustum/frustum3_computeNearestPointToPoint.js +7 -7
  332. package/src/core/geom/3d/frustum/frustum3_compute_closest_point.js +117 -117
  333. package/src/core/geom/3d/frustum/frustum_matrix4_project.js +7 -7
  334. package/src/core/geom/3d/frustum/hexahedron_from_aabb.js +7 -7
  335. package/src/core/geom/3d/frustum/read_frustum_corner.js +17 -17
  336. package/src/core/geom/3d/frustum/read_three_planes_to_array.js +7 -7
  337. package/src/core/geom/3d/frustum/slice_frustum_linear_to_points.js +7 -7
  338. package/src/core/geom/3d/gjk/GJK_REVIEW_NOTES.md +146 -146
  339. package/src/core/geom/3d/gjk/gjk.js +430 -430
  340. package/src/core/geom/3d/gjk/gjk_epa_penetration.js +520 -520
  341. package/src/core/geom/3d/gjk/minkowski_support.js +71 -71
  342. package/src/core/geom/3d/gjk/mpr.js +368 -368
  343. package/src/core/geom/3d/line/line3_closest_points_segment_segment.js +7 -7
  344. package/src/core/geom/3d/line/line3_compute_nearest_point_to_point.js +7 -7
  345. package/src/core/geom/3d/line/line3_compute_point_segment_point_distance_unfolded.js +64 -64
  346. package/src/core/geom/3d/line/line3_compute_segment_closest_point_to_aabb3_t.js +153 -153
  347. package/src/core/geom/3d/line/line3_compute_segment_nearest_point_to_aabb3_t.js +7 -7
  348. package/src/core/geom/3d/line/line3_compute_segment_nearest_point_to_point_t.js +7 -7
  349. package/src/core/geom/3d/line/line3_compute_segment_segment_closest_points.js +88 -88
  350. package/src/core/geom/3d/line/line3_segment_segment_closest_point_pair.js +71 -71
  351. package/src/core/geom/3d/line/segment3_point_distance_to_segments.js +25 -25
  352. package/src/core/geom/3d/mat4/allocate_m4.js +7 -7
  353. package/src/core/geom/3d/mat4/apply_mat4_transform_to_direction_v3_array.js +7 -7
  354. package/src/core/geom/3d/mat4/apply_mat4_transform_to_v3_array.js +7 -7
  355. package/src/core/geom/3d/mat4/compose_matrix4_array.js +7 -7
  356. package/src/core/geom/3d/mat4/decompose_matrix_4_array.js +7 -7
  357. package/src/core/geom/3d/mat4/eulerAnglesFromMatrix.js +7 -7
  358. package/src/core/geom/3d/mat4/m4_compose.js +22 -22
  359. package/src/core/geom/3d/mat4/m4_compute_normal_matrix3.js +7 -7
  360. package/src/core/geom/3d/mat4/m4_decompose.js +64 -64
  361. package/src/core/geom/3d/mat4/m4_decompose_array.js +157 -157
  362. package/src/core/geom/3d/mat4/m4_from_rotation_translation_scale.js +32 -32
  363. package/src/core/geom/3d/mat4/m4_from_rotation_translation_scale_scalar.js +67 -67
  364. package/src/core/geom/3d/mat4/m4_linear_matrix3.js +29 -29
  365. package/src/core/geom/3d/mat4/m4_look_at.js +96 -96
  366. package/src/core/geom/3d/mat4/m4_make_rotation_x.js +47 -47
  367. package/src/core/geom/3d/mat4/m4_make_rotation_y.js +47 -47
  368. package/src/core/geom/3d/mat4/m4_make_rotation_z.js +47 -47
  369. package/src/core/geom/3d/mat4/m4_normal_matrix3.js +39 -39
  370. package/src/core/geom/3d/mat4/m4_perspective.js +66 -66
  371. package/src/core/geom/3d/mat4/m4_transpose.js +48 -48
  372. package/src/core/geom/3d/morton/mortonEncode_LUT.js +7 -7
  373. package/src/core/geom/3d/morton/mortonEncode_magicbits.js +7 -7
  374. package/src/core/geom/3d/normal/hemioct/encode_unit3_hemioct.js +5 -5
  375. package/src/core/geom/3d/normal/hemioct/encode_unit_to_hemioct.js +28 -28
  376. package/src/core/geom/3d/normal/lambert_azimuth/decode_azimuth_to_unit.js +30 -30
  377. package/src/core/geom/3d/normal/lambert_azimuth/encode_unit_to_azimuth.js +19 -19
  378. package/src/core/geom/3d/normal/spherical/decode_spheremap_to_unit.js +38 -38
  379. package/src/core/geom/3d/normal/spherical/encode_unit_to_spheremap.js +30 -30
  380. package/src/core/geom/3d/plane/is_point_within_planes.js +7 -7
  381. package/src/core/geom/3d/plane/orient3d_fast_point.js +47 -47
  382. package/src/core/geom/3d/plane/plane3_compute_convex_3_plane_intersection.js +7 -7
  383. package/src/core/geom/3d/plane/plane3_compute_plane_intersection.js +7 -7
  384. package/src/core/geom/3d/plane/plane3_intersect_plane.js +7 -7
  385. package/src/core/geom/3d/plane/plane3_three_compute_convex_3_plane_intersection.js +7 -7
  386. package/src/core/geom/3d/point/point3_raycast.d.ts +33 -0
  387. package/src/core/geom/3d/point/point3_raycast.d.ts.map +1 -0
  388. package/src/core/geom/3d/point/point3_raycast.js +47 -0
  389. package/src/core/geom/3d/polygon/polygon3_compute_normal.js +60 -60
  390. package/src/core/geom/3d/polygon/polygon3_triangulate.js +94 -94
  391. package/src/core/geom/3d/polyhedron/convex_polyhedron3_raycast.d.ts +40 -0
  392. package/src/core/geom/3d/polyhedron/convex_polyhedron3_raycast.d.ts.map +1 -0
  393. package/src/core/geom/3d/polyhedron/convex_polyhedron3_raycast.js +94 -0
  394. package/src/core/geom/3d/quadric/QUADRIC3_SIZE.js +14 -14
  395. package/src/core/geom/3d/quadric/Quadric3.js +282 -282
  396. package/src/core/geom/3d/quadric/quadric3_add.js +33 -33
  397. package/src/core/geom/3d/quadric/quadric3_clear.js +25 -25
  398. package/src/core/geom/3d/quadric/quadric3_copy.js +27 -27
  399. package/src/core/geom/3d/quadric/quadric3_distance.js +36 -36
  400. package/src/core/geom/3d/quadric/quadric3_evaluate.js +25 -25
  401. package/src/core/geom/3d/quadric/quadric3_optimize.js +45 -45
  402. package/src/core/geom/3d/quadric/quadric3_scale.js +32 -32
  403. package/src/core/geom/3d/quadric/quadric3_set_from_plane_through_point.js +30 -30
  404. package/src/core/geom/3d/quadric/quadric3_set_from_vector4.js +35 -35
  405. package/src/core/geom/3d/quadric/quadric3_sub.js +36 -36
  406. package/src/core/geom/3d/quadric/quadric3_to_tensor_m3.js +28 -28
  407. package/src/core/geom/3d/quadric/quadric3_to_tensor_m3_inverse.js +56 -56
  408. package/src/core/geom/3d/quadric/quadric3_to_vector3.js +19 -19
  409. package/src/core/geom/3d/quaternion/quat3_createFromAxisAngle.js +7 -7
  410. package/src/core/geom/3d/quaternion/quat3_integrate.js +62 -62
  411. package/src/core/geom/3d/quaternion/quat3_multiply.js +31 -31
  412. package/src/core/geom/3d/quaternion/quat3_nlerp.js +72 -72
  413. package/src/core/geom/3d/quaternion/quat3_to_matrix3.js +69 -69
  414. package/src/core/geom/3d/quaternion/quat_decode_from_uint32.js +7 -7
  415. package/src/core/geom/3d/quaternion/quat_encode_to_uint32.js +7 -7
  416. package/src/core/geom/3d/ray/ray3_array_compose.js +7 -7
  417. package/src/core/geom/3d/ray/ray3_compute_nearest_point_to_point.js +7 -7
  418. package/src/core/geom/3d/ray/ray3_distance_to_point.js +7 -7
  419. package/src/core/geom/3d/ray/ray3_shift_origin_along_direction.js +7 -7
  420. package/src/core/geom/3d/shape/ConvexHullShape3D.d.ts +17 -0
  421. package/src/core/geom/3d/shape/ConvexHullShape3D.d.ts.map +1 -1
  422. package/src/core/geom/3d/shape/ConvexHullShape3D.js +43 -0
  423. package/src/core/geom/3d/shape/CylinderShape3D.d.ts +1 -0
  424. package/src/core/geom/3d/shape/CylinderShape3D.d.ts.map +1 -1
  425. package/src/core/geom/3d/shape/CylinderShape3D.js +21 -0
  426. package/src/core/geom/3d/shape/PointShape3D.d.ts +7 -0
  427. package/src/core/geom/3d/shape/PointShape3D.d.ts.map +1 -1
  428. package/src/core/geom/3d/shape/PointShape3D.js +88 -63
  429. package/src/core/geom/3d/shape/PosedShape3D.d.ts +6 -0
  430. package/src/core/geom/3d/shape/PosedShape3D.d.ts.map +1 -1
  431. package/src/core/geom/3d/shape/PosedShape3D.js +27 -0
  432. package/src/core/geom/3d/shape/TransformedShape3D.d.ts +10 -0
  433. package/src/core/geom/3d/shape/TransformedShape3D.d.ts.map +1 -1
  434. package/src/core/geom/3d/shape/TransformedShape3D.js +13 -0
  435. package/src/core/geom/3d/shape/Triangle3D.d.ts +1 -0
  436. package/src/core/geom/3d/shape/Triangle3D.d.ts.map +1 -1
  437. package/src/core/geom/3d/shape/Triangle3D.js +26 -0
  438. package/src/core/geom/3d/shape/UnionShape3D.d.ts +6 -0
  439. package/src/core/geom/3d/shape/UnionShape3D.d.ts.map +1 -1
  440. package/src/core/geom/3d/shape/UnionShape3D.js +7 -0
  441. package/src/core/geom/3d/shape/UnitCubeShape3D.js +45 -45
  442. package/src/core/geom/3d/shape/UnitSphereShape3D.js +44 -44
  443. package/src/core/geom/3d/shape/shape3_mesh_from_geometry.js +64 -64
  444. package/src/core/geom/3d/shape/shape_mesh_from_geometry.js +7 -7
  445. package/src/core/geom/3d/shape/util/compute_signed_distance_gradient_by_sampling.js +51 -51
  446. package/src/core/geom/3d/shape/util/shape3d_voxelize_to_grid.js +7 -7
  447. package/src/core/geom/3d/sphere/compute_bounding_sphere_of_2_spheres.js +7 -7
  448. package/src/core/geom/3d/sphere/harmonics/sh3_convolve_with_zh.js +53 -53
  449. package/src/core/geom/3d/sphere/harmonics/sh3_ggx_zh.js +30 -30
  450. package/src/core/geom/3d/sphere/harmonics/sh3_rotate_bl.js +7 -7
  451. package/src/core/geom/3d/sphere/harmonics/sh_compute_K.js +7 -7
  452. package/src/core/geom/3d/sphere/sphere_array_intersects_point.js +7 -7
  453. package/src/core/geom/3d/sphere/sphere_array_intersects_ray.js +7 -7
  454. package/src/core/geom/3d/sphere/sphere_intersects_point.js +7 -7
  455. package/src/core/geom/3d/sphere/sphere_matrix4_transform.js +5 -5
  456. package/src/core/geom/3d/sphere/sphere_projected_sphere_radius_sqr.js +7 -7
  457. package/src/core/geom/3d/sphere/sphere_radius_sqr_from_v3_array_transformed.js +7 -7
  458. package/src/core/geom/3d/sphere/spherical_from_cartesian.js +7 -7
  459. package/src/core/geom/3d/tetrahedra/compute_tetrahedral_mesh_from_surface.js +174 -174
  460. package/src/core/geom/3d/tetrahedra/compute_tetrahedron_quality.js +7 -7
  461. package/src/core/geom/3d/tetrahedra/compute_tetrahedron_volume.js +7 -7
  462. package/src/core/geom/3d/tetrahedra/delaunay/fill_in_a_cavity.js +7 -7
  463. package/src/core/geom/3d/tetrahedra/graphics/build_tetrahedral_mesh_geometry.js +39 -39
  464. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_build_vertex_to_tets_map.js +48 -48
  465. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_carve_outside_surface.js +173 -173
  466. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_compute_tet_quality.js +22 -22
  467. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_find_tets_around_edge.js +146 -146
  468. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_flip_23.js +232 -232
  469. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_flip_32.js +255 -255
  470. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_improve_quality.js +387 -387
  471. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_smooth_vertex.js +140 -140
  472. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_tet_get_neighbours.js +7 -7
  473. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_vertex_is_boundary.js +97 -97
  474. package/src/core/geom/3d/tetrahedra/tetrahedron_compute_quality.js +66 -66
  475. package/src/core/geom/3d/tetrahedra/triangle/triangle_decode_depth.js +59 -59
  476. package/src/core/geom/3d/tetrahedra/triangle/triangle_rasterized_depth_sample_index.js +54 -54
  477. package/src/core/geom/3d/tetrahedra/validate_neighbour.js +70 -70
  478. package/src/core/geom/3d/topology/struct/binary/BinaryDataBlock.js +314 -314
  479. package/src/core/geom/3d/topology/struct/binary/BinaryDataLayer.js +364 -364
  480. package/src/core/geom/3d/topology/struct/binary/BinaryElementPool.js +567 -567
  481. package/src/core/geom/3d/topology/struct/binary/BinaryTopology.js +1085 -1085
  482. package/src/core/geom/3d/topology/struct/binary/io/bt_boundary_loop.js +73 -73
  483. package/src/core/geom/3d/topology/struct/binary/io/bt_loop_kill.js +35 -35
  484. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_append.js +119 -119
  485. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_close_boundary_holes.js +84 -84
  486. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compact.js +172 -172
  487. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compute_face_normals.js +56 -56
  488. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compute_vertex_quadratics.js +7 -7
  489. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compute_vertex_quadrics.js +227 -227
  490. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_face_decouple.js +161 -161
  491. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_face_island_erode.js +490 -490
  492. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_fill_small_holes.js +120 -120
  493. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_from_indexed_geometry.js +120 -120
  494. package/src/core/geom/3d/topology/struct/binary/io/edge/bt_edge_collapse.js +52 -52
  495. package/src/core/geom/3d/topology/struct/binary/io/edge/bt_edge_create.js +115 -115
  496. package/src/core/geom/3d/topology/struct/binary/io/edge/bt_edge_flip.js +142 -142
  497. package/src/core/geom/3d/topology/struct/binary/io/edge/bt_edge_get_or_create.js +21 -21
  498. package/src/core/geom/3d/topology/struct/binary/io/edge/bt_edge_kill_parallels.js +92 -92
  499. package/src/core/geom/3d/topology/struct/binary/io/edge/bt_edge_split.js +179 -179
  500. package/src/core/geom/3d/topology/struct/binary/io/edge/bt_edge_swap_vertex_slots.js +28 -28
  501. package/src/core/geom/3d/topology/struct/binary/io/edge/bt_kill_only_edge.js +8 -8
  502. package/src/core/geom/3d/topology/struct/binary/io/edge/bt_mesh_fuse_duplicate_edges.js +83 -83
  503. package/src/core/geom/3d/topology/struct/binary/io/edge/bt_mesh_kill_short_edges.js +89 -89
  504. package/src/core/geom/3d/topology/struct/binary/io/face/bt_face_kill.js +25 -25
  505. package/src/core/geom/3d/topology/struct/binary/io/face/bt_face_poke.js +127 -127
  506. package/src/core/geom/3d/topology/struct/binary/io/face/bt_face_triangulate.js +268 -268
  507. package/src/core/geom/3d/topology/struct/binary/io/face/bt_kill_only_face.js +8 -8
  508. package/src/core/geom/3d/topology/struct/binary/io/face/bt_mesh_kill_degenerate_faces.js +72 -72
  509. package/src/core/geom/3d/topology/struct/binary/io/vertex/bt_kill_only_vert.js +7 -7
  510. package/src/core/geom/3d/topology/struct/binary/io/vertex/bt_merge_verts_by_distance.js +7 -7
  511. package/src/core/geom/3d/topology/struct/binary/io/vertex/bt_mesh_relative_merge_distance.js +48 -48
  512. package/src/core/geom/3d/topology/struct/binary/io/vertex/bt_mesh_split_pinched_vertices.js +256 -256
  513. package/src/core/geom/3d/topology/struct/binary/io/vertex/bt_mesh_vertex_merge_distance.js +77 -77
  514. package/src/core/geom/3d/topology/struct/binary/io/vertex/bt_vert_fuse_duplicate_edges.js +7 -7
  515. package/src/core/geom/3d/topology/struct/binary/io/vertex/bt_vert_kill.js +7 -7
  516. package/src/core/geom/3d/topology/struct/binary/io/vertex/bt_vertex_fuse_duplicate_edges.js +148 -148
  517. package/src/core/geom/3d/topology/struct/binary/io/vertex/bt_vertex_kill_only.js +7 -7
  518. package/src/core/geom/3d/topology/struct/binary/query/bt_collect_boundary_segments.js +29 -29
  519. package/src/core/geom/3d/topology/struct/binary/query/bt_edge_collapse_breaks_link_condition.js +107 -107
  520. package/src/core/geom/3d/topology/struct/binary/query/bt_edge_flip_is_legal.js +41 -41
  521. package/src/core/geom/3d/topology/struct/binary/query/bt_edge_flip_would_fold.js +120 -120
  522. package/src/core/geom/3d/topology/struct/binary/query/bt_edge_get.js +42 -42
  523. package/src/core/geom/3d/topology/struct/binary/query/bt_edge_is_boundary.js +20 -20
  524. package/src/core/geom/3d/topology/struct/binary/query/bt_edge_read_diamond.js +96 -96
  525. package/src/core/geom/3d/topology/struct/binary/query/bt_face_compute_newell_normal.js +72 -72
  526. package/src/core/geom/3d/topology/struct/binary/query/bt_face_get_attached_faces.js +7 -7
  527. package/src/core/geom/3d/topology/struct/binary/query/bt_face_get_edge_adjacent_faces.js +39 -39
  528. package/src/core/geom/3d/topology/struct/binary/query/bt_face_get_edge_adjacent_faces_weighted.js +93 -93
  529. package/src/core/geom/3d/topology/struct/binary/query/bt_face_get_neighbour_faces.js +7 -7
  530. package/src/core/geom/3d/topology/struct/binary/query/bt_face_get_shared_loop.js +48 -48
  531. package/src/core/geom/3d/topology/struct/binary/query/bt_face_island_flood_fill.js +45 -45
  532. package/src/core/geom/3d/topology/struct/binary/query/bt_face_read_triangle.js +66 -66
  533. package/src/core/geom/3d/topology/struct/binary/query/bt_faces_shared_loop.js +7 -7
  534. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_assert_valid.js +44 -44
  535. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_build_boundary_euclidean_distance_field.js +32 -32
  536. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_compute_face_islands.js +53 -53
  537. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_face_find_path.js +703 -703
  538. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_sample_interior_grid_points.js +181 -181
  539. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_segment_penetrates_surface.js +133 -133
  540. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_surface_ray_parity.js +82 -82
  541. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_validate.js +599 -599
  542. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_walk_boundary_loops.js +108 -108
  543. package/src/core/geom/3d/topology/struct/binary/query/bt_query_edge_is_boundary.js +7 -7
  544. package/src/core/geom/3d/triangle/computeTriangleClosestPointToPointBarycentric.js +7 -7
  545. package/src/core/geom/3d/triangle/computeTriangleRayIntersection.js +7 -7
  546. package/src/core/geom/3d/triangle/computeTriangleRayIntersectionBarycentric.js +7 -7
  547. package/src/core/geom/3d/triangle/computeTriangleRayIntersectionBarycentricEdge.js +7 -7
  548. package/src/core/geom/3d/triangle/computeTriangleRayIntersectionBarycentricGeometry.js +7 -7
  549. package/src/core/geom/3d/triangle/compute_triangle_area_3d.js +7 -7
  550. package/src/core/geom/3d/triangle/compute_triangle_normal.js +7 -7
  551. package/src/core/geom/3d/triangle/rayTriangleIntersection.js +7 -7
  552. package/src/core/geom/3d/triangle/tri3_closest_point.js +65 -65
  553. package/src/core/geom/3d/triangle/tri3_closest_point_barycentric.js +171 -171
  554. package/src/core/geom/3d/triangle/tri3_flatten_isometric.js +79 -79
  555. package/src/core/geom/3d/triangle/tri3_ray_intersection.js +160 -160
  556. package/src/core/geom/3d/triangle/tri3_ray_intersection_barycentric.js +96 -96
  557. package/src/core/geom/3d/triangle/tri3_raycast.d.ts +42 -0
  558. package/src/core/geom/3d/triangle/tri3_raycast.d.ts.map +1 -0
  559. package/src/core/geom/3d/triangle/tri3_raycast.js +94 -0
  560. package/src/core/geom/3d/triangle/triangle3_incenter.js +7 -7
  561. package/src/core/geom/3d/triangle/triangle_compute_plane_side.js +7 -7
  562. package/src/core/geom/3d/triangle/triangle_compute_signed_volume.js +7 -7
  563. package/src/core/geom/3d/triangle/triangle_intersects_clipping_volume.js +7 -7
  564. package/src/core/geom/3d/triangle/triangle_mesh_compute_signed_volume.js +7 -7
  565. package/src/core/geom/3d/triangle/v3_compute_triangle_normal.js +7 -7
  566. package/src/core/geom/3d/util/make_justified_point_grid.js +7 -7
  567. package/src/core/geom/GEOMETRY_REVIEW_002_API_ISSUES.md +381 -381
  568. package/src/core/geom/GEOMETRY_REVIEW_002_NAMING.md +684 -684
  569. package/src/core/geom/MANIFOLD_COMPARISON_2026_08_01.md +554 -554
  570. package/src/core/geom/mat3/m3_cm_from_translation.js +7 -7
  571. package/src/core/geom/mat3/m3_cm_invert.js +7 -7
  572. package/src/core/geom/mat3/m3_make_rotation.js +5 -5
  573. package/src/core/geom/mat3/m3_make_rotation_TBN.js +31 -31
  574. package/src/core/geom/mat3/m3_multiply_vec3.js +7 -7
  575. package/src/core/geom/packing/computeBoundingSphereOfSpheres.js +7 -7
  576. package/src/core/geom/packing/max-rect/cost/costByBestShortSide.js +7 -7
  577. package/src/core/geom/packing/max-rect/cost/costByRemainingArea.js +7 -7
  578. package/src/core/geom/packing/max-rect/cutArea.js +7 -7
  579. package/src/core/geom/packing/max-rect/findBestContainer.js +7 -7
  580. package/src/core/geom/packing/max-rect/packMaxRectangles.js +7 -7
  581. package/src/core/geom/packing/max-rect/packOneBox.js +7 -7
  582. package/src/core/geom/packing/max-rect/removeRedundantBoxes.js +7 -7
  583. package/src/core/geom/packing/max-rect/removeRedundantBoxesArray.js +7 -7
  584. package/src/core/geom/random/randomPointInBox.js +7 -7
  585. package/src/core/geom/random/randomPointInCircle.js +7 -7
  586. package/src/core/geom/random/randomPointInPoint.js +7 -7
  587. package/src/core/geom/random/randomPointInSphere.js +7 -7
  588. package/src/core/geom/random/randomPointOnBox.js +7 -7
  589. package/src/core/geom/random/randomPointOnSphere.js +7 -7
  590. package/src/core/geom/random/random_orthonormal_basis.js +31 -31
  591. package/src/core/geom/vec/vector_axpy.js +15 -15
  592. package/src/core/geom/vec/vector_axpy_offset.js +20 -20
  593. package/src/core/geom/vec/vector_copy.js +7 -7
  594. package/src/core/geom/vec/vector_dot_offset.js +25 -25
  595. package/src/core/geom/vec/vector_scale_array.js +7 -7
  596. package/src/core/geom/vec2/v2_rotate.js +21 -21
  597. package/src/core/geom/vec2/v2_rotate_cos_sin.js +21 -21
  598. package/src/core/geom/vec3/v3_angle_atan2_between.js +7 -7
  599. package/src/core/geom/vec3/v3_angle_between.js +7 -7
  600. package/src/core/geom/vec3/v3_angle_cos_between.js +7 -7
  601. package/src/core/geom/vec3/v3_array_add_in_place.js +7 -7
  602. package/src/core/geom/vec3/v3_array_centroid.js +7 -7
  603. package/src/core/geom/vec3/v3_array_copy.js +7 -7
  604. package/src/core/geom/vec3/v3_array_displace_in_direction.js +7 -7
  605. package/src/core/geom/vec3/v3_array_equals.js +7 -7
  606. package/src/core/geom/vec3/v3_array_immediate_add.js +7 -7
  607. package/src/core/geom/vec3/v3_array_matrix4_rotate.js +7 -7
  608. package/src/core/geom/vec3/v3_array_normalize.js +7 -7
  609. package/src/core/geom/vec3/v3_array_scale.js +7 -7
  610. package/src/core/geom/vec3/v3_compute_interior_angle.js +7 -7
  611. package/src/core/geom/vec3/v3_cotangent.js +33 -33
  612. package/src/core/geom/vec3/v3_dot_array_array.js +7 -7
  613. package/src/core/geom/vec3/v3_matrix3_multiply.js +30 -30
  614. package/src/core/geom/vec3/v3_matrix3_rotate.js +49 -49
  615. package/src/core/geom/vec3/v3_matrix4_rotate.js +7 -7
  616. package/src/core/geom/vec3/v3_multiply.js +23 -23
  617. package/src/core/geom/vec3/v3_multiply_scalar.js +7 -7
  618. package/src/core/geom/vec3/v3_orthonormal_matrix_from_normal.js +49 -49
  619. package/src/core/geom/vec3/v3_quat3_apply.js +7 -7
  620. package/src/core/geom/vec3/v3_quat3_apply_inverse.js +7 -7
  621. package/src/core/geom/vec3/v3_quaternion_apply.js +39 -39
  622. package/src/core/geom/vec3/v3_quaternion_apply_inverse.js +41 -41
  623. package/src/core/geom/vec3/v3_reflect.js +24 -24
  624. package/src/core/geom/vec3/v3_scale_array.js +18 -18
  625. package/src/core/geom/vec3/v3_triple_cross.js +45 -45
  626. package/src/core/geom/vec3/v3_triple_cross_product.js +7 -7
  627. package/src/core/geom/vec3/v3_uniform_sample_cone.js +21 -21
  628. package/src/core/geom/vec4/v4_multiply_mat4.js +7 -7
  629. package/src/core/graph/csr/csr_graph_build_distance_field.js +112 -112
  630. package/src/core/graph/csr/csr_graph_find_edge.js +25 -25
  631. package/src/core/graph/csr/csr_graph_find_shortest_path.js +105 -105
  632. package/src/core/graph/csr/csr_graph_from_undirected_edge_list.js +94 -94
  633. package/src/core/graph/graph_k_means_cluster.js +250 -250
  634. package/src/core/graph/layout/CircleLayout.js +1 -1
  635. package/src/core/graph/layout/box/BoxLayouter.js +1 -1
  636. package/src/core/graph/layout/graph_peel_topological_layer.js +45 -45
  637. package/src/core/graph/metis/BinaryTopologyFaceGraph.js +49 -49
  638. package/src/core/graph/metis/bt_mesh_build_face_graph.js +111 -111
  639. package/src/core/graph/metis/cluster_mesh_metis.js +137 -137
  640. package/src/core/graph/metis/native/bisection/bisect_graph.js +100 -100
  641. package/src/core/graph/metis/native/bisection/compute_2way_params.js +86 -86
  642. package/src/core/graph/metis/native/bisection/fm_2way.js +297 -297
  643. package/src/core/graph/metis/native/bisection/grow_bisection.js +137 -137
  644. package/src/core/graph/metis/native/bisection/split_graph_two_way.js +119 -119
  645. package/src/core/graph/metis/native/coarsen/coarsen_graph.js +94 -94
  646. package/src/core/graph/metis/native/coarsen/create_coarse_graph.js +158 -158
  647. package/src/core/graph/metis/native/coarsen/match_shem.js +175 -175
  648. package/src/core/graph/metis/native/initial/initial_kway_bfs.js +122 -122
  649. package/src/core/graph/metis/native/initial/initial_kway_recursive_bisection.js +170 -170
  650. package/src/core/graph/metis/native/metis_partition_kway.js +126 -126
  651. package/src/core/graph/metis/native/refine/RefinementScratch.js +53 -53
  652. package/src/core/graph/metis/native/refine/fm_kway.js +487 -487
  653. package/src/core/graph/metis/native/refine/project_kway.js +43 -43
  654. package/src/core/graph/metis/native/refine/refine_kway.js +43 -43
  655. package/src/core/lang/reactive/pegjs/parser.js +3458 -3458
  656. package/src/core/math/complex/complex_horner_eval.js +31 -31
  657. package/src/core/math/computeWholeDivisorLow.js +33 -33
  658. package/src/core/math/f32_next_down.js +24 -24
  659. package/src/core/math/f32_next_up.js +27 -27
  660. package/src/core/math/hash/hash_mix2.js +22 -22
  661. package/src/core/math/hash/hash_mix3.js +19 -19
  662. package/src/core/math/linalg/cg/cg_solve.js +106 -106
  663. package/src/core/math/linalg/cg/cg_solve_normal_equations.js +59 -59
  664. package/src/core/math/linalg/cubic_residual_times_derivative_accumulate.js +43 -43
  665. package/src/core/math/linalg/eigen/matrix_householder_in_place.js +122 -122
  666. package/src/core/math/linalg/eigen/matrix_qr_in_place.js +190 -190
  667. package/src/core/math/linalg/eigen/matrix_symmetric_3x3_eigen.js +117 -117
  668. package/src/core/math/linalg/givens/givens_apply_rows.js +28 -28
  669. package/src/core/math/linalg/givens/givens_apply_rows_offset.js +30 -30
  670. package/src/core/math/linalg/givens/givens_rotation_coefficients.js +39 -39
  671. package/src/core/math/linalg/lu_factor_linear_system.js +108 -108
  672. package/src/core/math/linalg/lu_solve_linear_system.js +53 -53
  673. package/src/core/math/linalg/polynomial_add_into.js +29 -29
  674. package/src/core/math/linalg/polynomial_complex_roots_aberth_ehrlich.js +159 -159
  675. package/src/core/math/linalg/polynomial_cubic_derivative_eval.js +16 -16
  676. package/src/core/math/linalg/polynomial_cubic_horner_eval.js +19 -19
  677. package/src/core/math/linalg/polynomial_cubic_second_derivative_eval.js +14 -14
  678. package/src/core/math/linalg/polynomial_multiply.js +41 -41
  679. package/src/core/math/linalg/polynomial_real_roots_in_interval.js +211 -211
  680. package/src/core/math/linalg/polynomial_root_bound_cauchy.js +30 -30
  681. package/src/core/math/linalg/polynomial_scale_into.js +17 -17
  682. package/src/core/math/linalg/polynomial_sub_into.js +29 -29
  683. package/src/core/math/linalg/solve_linear_system_GEPP_2x2.js +96 -96
  684. package/src/core/math/linalg/sor_optimal_omega.js +42 -42
  685. package/src/core/math/linalg/sparse/SparseMatrixCSR.js +88 -88
  686. package/src/core/math/linalg/sparse/spm_matvec.js +22 -22
  687. package/src/core/math/linalg/sparse/spm_matvec_transpose.js +29 -29
  688. package/src/core/math/lookup/LUT_HEATMAP.js +30 -30
  689. package/src/core/math/lookup/ParameterLookupTable.js +495 -495
  690. package/src/core/math/lookup/ParameterLookupTableFlags.js +6 -6
  691. package/src/core/math/noise/curl_noise_3d.js +18 -18
  692. package/src/core/math/physics/brdf/cone_cosine_from_roughness.js +28 -28
  693. package/src/core/math/physics/brdf/reflection_sample_weight.js +48 -48
  694. package/src/core/math/physics/kinematics/computeInterceptPoint.js +79 -79
  695. package/src/core/math/random/randomSeed.js +16 -16
  696. package/src/core/math/random/random_pick_weighted_index.js +26 -26
  697. package/src/core/math/round_half_away_from_zero.js +13 -13
  698. package/src/core/math/spline/spline3_hermite_apply_transform.js +107 -107
  699. package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite_1d.js +125 -125
  700. package/src/core/math/spline/spline3_hermite_intersects_spline3_hermite.js +50 -50
  701. package/src/core/math/spline/spline3_hermite_reverse.js +49 -49
  702. package/src/core/math/spline/spline3_hermite_to_monomial.js +37 -37
  703. package/src/core/math/statistics/hammersley_sequence.js +53 -53
  704. package/src/core/model/reactive/model/util/createRandomReactiveExpression.js +185 -185
  705. package/src/core/path/convertPathToURL.js +107 -107
  706. package/src/core/process/delay.js +16 -16
  707. package/src/core/science/units/MEASUREMENT_UNITS_SCIENTIFIC.js +87 -87
  708. package/src/core/science/units/MEASUREMENT_UNITS_SI.js +17 -17
  709. package/src/core/science/units/NamedUnit.js +48 -48
  710. package/src/core/science/units/UNIT_DIMENSION_MAPPING.js +29 -29
  711. package/src/core/science/units/UnitDimension.js +28 -28
  712. package/src/core/science/units/format_quantity.js +74 -74
  713. package/src/core/science/units/magnitude_prefix.js +127 -127
  714. package/src/core/science/units/si/AMPERE.js +13 -13
  715. package/src/core/science/units/si/CANDELA.js +13 -13
  716. package/src/core/science/units/si/DIMENSIONLESS.js +14 -14
  717. package/src/core/science/units/si/KELVIN.js +13 -13
  718. package/src/core/science/units/si/KILOGRAM.js +13 -13
  719. package/src/core/science/units/si/METER.js +13 -13
  720. package/src/core/science/units/si/MOLE.js +13 -13
  721. package/src/core/science/units/si/SECOND.js +13 -13
  722. package/src/core/science/units/si/derived/BECQUEREL.js +16 -16
  723. package/src/core/science/units/si/derived/COULOMB.js +13 -13
  724. package/src/core/science/units/si/derived/FARAD.js +13 -13
  725. package/src/core/science/units/si/derived/GRAY.js +16 -16
  726. package/src/core/science/units/si/derived/HENRY.js +13 -13
  727. package/src/core/science/units/si/derived/HERTZ.js +16 -16
  728. package/src/core/science/units/si/derived/JOULE.js +13 -13
  729. package/src/core/science/units/si/derived/KATAL.js +13 -13
  730. package/src/core/science/units/si/derived/LUMEN.js +14 -14
  731. package/src/core/science/units/si/derived/LUX.js +13 -13
  732. package/src/core/science/units/si/derived/NEWTON.js +13 -13
  733. package/src/core/science/units/si/derived/OHM.js +13 -13
  734. package/src/core/science/units/si/derived/PASCAL.js +13 -13
  735. package/src/core/science/units/si/derived/SIEMENS.js +13 -13
  736. package/src/core/science/units/si/derived/SIEVERT.js +16 -16
  737. package/src/core/science/units/si/derived/TESLA.js +13 -13
  738. package/src/core/science/units/si/derived/VOLT.js +13 -13
  739. package/src/core/science/units/si/derived/WATT.js +13 -13
  740. package/src/core/science/units/si/derived/WEBER.js +13 -13
  741. package/src/core/science/units/unit_matrix_to_string.js +403 -403
  742. package/src/engine/Engine.js +726 -726
  743. package/src/engine/EngineHarness.js +590 -590
  744. package/src/engine/REVIEW_2026_07_03.md +211 -211
  745. package/src/engine/animation/AnimatedActions.js +81 -81
  746. package/src/engine/animation/AnimationUtils.js +252 -252
  747. package/src/engine/animation/async/TimeSeries.js +300 -300
  748. package/src/engine/animation/curve/animation_curve_binary_codec.js +60 -60
  749. package/src/engine/animation/curve/draw/canvas_point_to_curve.js +22 -22
  750. package/src/engine/animation/curve/draw/curve_point_to_canvas.js +38 -38
  751. package/src/engine/animation/curve/draw/position_canvas_to_curve.js +22 -22
  752. package/src/engine/animation/curve/draw/position_curve_to_canvas.js +22 -22
  753. package/src/engine/asset/GameAssetType.d.ts +0 -1
  754. package/src/engine/asset/GameAssetType.js +0 -1
  755. package/src/engine/asset/load_model_scene_bundle.js +27 -27
  756. package/src/engine/asset/loaders/font/FontAsset.js +21 -21
  757. package/src/engine/asset/loaders/font/FontAssetLoader.js +20 -20
  758. package/src/engine/asset/loaders/gltf_test_fixtures.js +358 -358
  759. package/src/engine/asset/loaders/image/ImageBitmapAssetLoader.js +52 -52
  760. package/src/engine/asset/loaders/image/ImageRGBADataLoader.js +94 -94
  761. package/src/engine/asset/loaders/image/png/filter/png_filter_unFilterAverage.js +59 -59
  762. package/src/engine/asset/loaders/image/png/filter/png_filter_unFilterNone.js +55 -55
  763. package/src/engine/asset/loaders/image/png/filter/png_filter_unFilterPaeth.js +74 -74
  764. package/src/engine/asset/loaders/image/png/filter/png_filter_unFilterSub.js +34 -34
  765. package/src/engine/asset/loaders/image/png/filter/png_filter_unFilterUp.js +46 -46
  766. package/src/engine/control/first-person/DESIGN.md +640 -640
  767. package/src/engine/control/first-person/DESIGN_EXTENSIONS.md +563 -563
  768. package/src/engine/control/first-person/FirstPersonMotionPhase.js +134 -134
  769. package/src/engine/control/first-person/FirstPersonPlayerControllerSystem.js +2045 -2045
  770. package/src/engine/control/first-person/mastery/DecisionPoint.js +30 -30
  771. package/src/engine/control/first-person/math/Spring.js +71 -71
  772. package/src/engine/control/first-person/math/computeJumpFromApex.js +23 -23
  773. package/src/engine/control/first-person/math/computeLRCBreathRate.js +41 -41
  774. package/src/engine/control/first-person/math/computeMassRatios.js +44 -44
  775. package/src/engine/control/first-person/math/criticallyDampedSpring.js +34 -34
  776. package/src/engine/control/first-person/math/dampedSpringStep.js +72 -72
  777. package/src/engine/control/first-person/math/jumpDynamics.js +108 -108
  778. package/src/engine/control/first-person/math/stepTowards.js +20 -20
  779. package/src/engine/control/first-person/pose/FirstPersonPose.js +137 -137
  780. package/src/engine/control/first-person/pose/FirstPersonPosture.js +27 -27
  781. package/src/engine/control/first-person/test/buildTestPlayer.js +36 -36
  782. package/src/engine/ecs/ASYNC_SYSTEMS_PROPOSAL.md +531 -531
  783. package/src/engine/ecs/EntityManager.d.ts +7 -3
  784. package/src/engine/ecs/EntityManager.d.ts.map +1 -1
  785. package/src/engine/ecs/EntityManager.js +10 -5
  786. package/src/engine/ecs/HIERARCHY_REVIEW.md +717 -717
  787. package/src/engine/ecs/async/SystemWorkerHost.js +329 -329
  788. package/src/engine/ecs/async/SystemWorkerLoopback.js +143 -143
  789. package/src/engine/ecs/async/WORKER_SYSTEM_PLAN.md +246 -246
  790. package/src/engine/ecs/async/WorkerSystem.js +702 -702
  791. package/src/engine/ecs/async/WorkerSystemProtocol.js +150 -150
  792. package/src/engine/ecs/fow/FogOfWar.js +707 -707
  793. package/src/engine/ecs/fow/FogOfWarEdgeMode.js +40 -40
  794. package/src/engine/ecs/fow/fog_of_war_mask_world_bounds.js +24 -24
  795. package/src/engine/ecs/fow/fog_of_war_uv_transform.js +30 -30
  796. package/src/engine/ecs/gui/hud/testHudEdgeStick.js +132 -134
  797. package/src/engine/ecs/hierarchy/EntityChildIndex.js +140 -140
  798. package/src/engine/ecs/ik/IKProblem.js +42 -42
  799. package/src/engine/ecs/ik/InverseKinematicsSystem.js +193 -193
  800. package/src/engine/ecs/ik/OneBoneSurfaceAlignmentSolver.js +134 -134
  801. package/src/engine/ecs/ik/TwoBoneInverseKinematicsSolver.js +97 -97
  802. package/src/engine/ecs/ik/ik_bone_chain.js +85 -85
  803. package/src/engine/ecs/ik/ik_probe_terrain_contact.js +119 -119
  804. package/src/engine/ecs/parent/EntityNode.d.ts +1 -1
  805. package/src/engine/ecs/parent/EntityNode.js +2 -2
  806. package/src/engine/ecs/parent/ParentEntitySystem.js +255 -255
  807. package/src/engine/ecs/speaker/VoiceSystem.d.ts +2 -2
  808. package/src/engine/ecs/speaker/VoiceSystem.d.ts.map +1 -1
  809. package/src/engine/ecs/speaker/VoiceSystem.js +13 -7
  810. package/src/engine/ecs/storage/binary/SERIALIZATION_COVERAGE.md +269 -269
  811. package/src/engine/ecs/storage/binary/object-v2/BinaryObjectDeSerializer.js +315 -315
  812. package/src/engine/ecs/storage/binary/object-v2/BinaryObjectSerializer.js +331 -331
  813. package/src/engine/ecs/storage/binary/object-v2/DESIGN.md +199 -199
  814. package/src/engine/ecs/storage/binary/object-v2/ObjectBinaryFormat.js +57 -57
  815. package/src/engine/ecs/storage/binary/object-v2/ObjectSerializationAdapter.js +56 -56
  816. package/src/engine/ecs/storage/binary/object-v2/README.md +110 -110
  817. package/src/engine/ecs/storage/binary/object-v2/objectStringCodec.js +48 -48
  818. package/src/engine/ecs/storage/binary/object-v2/serializerTestHarness.js +44 -44
  819. package/src/engine/ecs/storage/populateEngineSerializationRegistry.js +265 -265
  820. package/src/engine/ecs/terrain/TerrainClouds.js +97 -97
  821. package/src/engine/ecs/terrain/ecs/StubTerrainBuildWorker.js +65 -65
  822. package/src/engine/ecs/terrain/ecs/Terrain.d.ts +4 -0
  823. package/src/engine/ecs/terrain/ecs/Terrain.d.ts.map +1 -1
  824. package/src/engine/ecs/terrain/ecs/Terrain.js +790 -790
  825. package/src/engine/ecs/terrain/ecs/TerrainSystem.js +209 -209
  826. package/src/engine/ecs/terrain/ecs/cling/ClingToTerrainSystem.js +316 -316
  827. package/src/engine/ecs/terrain/ecs/layers/TerrainLayers.js +416 -416
  828. package/src/engine/ecs/terrain/ecs/splat/SplatMapping.js +525 -525
  829. package/src/engine/ecs/terrain/overlay/TerrainOverlay.js +302 -302
  830. package/src/engine/ecs/terrain/tiles/TerrainTile.js +561 -561
  831. package/src/engine/ecs/terrain/tiles/TerrainTileManager.js +896 -896
  832. package/src/engine/ecs/terrain/tiles/tile_geometry_to_meshlets.js +36 -36
  833. package/src/engine/ecs/terrain/util/loadVisibleTerrainTiles.js +129 -129
  834. package/src/engine/ecs/transform/Transform64.js +814 -814
  835. package/src/engine/ecs/transform/t64_copy_from_transform.js +35 -35
  836. package/src/engine/ecs/transform/t64_equals_transform.js +55 -55
  837. package/src/engine/ecs/transform/t64_look_rotation.js +36 -36
  838. package/src/engine/ecs/transform/t64_transform_direction.js +25 -25
  839. package/src/engine/ecs/transform/transform_copy_from_t64.js +28 -28
  840. package/src/engine/ecs/transform-attachment/TransformAttachmentSystem.js +345 -345
  841. package/src/engine/ecs/transform-attachment/transform_attachment_find_descendant_by_name.js +65 -65
  842. package/src/engine/ecs/transform-attachment/transform_attachment_parent_of.js +24 -24
  843. package/src/engine/ecs/util/hideEntityGracefully.js +208 -208
  844. package/src/engine/graphics/CONTEXT_LOSS_RECOVERY_PLAN.md +446 -446
  845. package/src/engine/graphics/FULL_SCREEN_QUAD_VERTEX_SHADER.js +14 -14
  846. package/src/engine/graphics/FrameThrottle.js +2 -2
  847. package/src/engine/graphics/ecs/animation/animator/graph/AnimationState.js +207 -207
  848. package/src/engine/graphics/ecs/animation/animator/graph/AnimationTransition.js +151 -151
  849. package/src/engine/graphics/ecs/animation/animator/graph/definition/serialization/AnimationGraphDefinitionAssetLoader.js +22 -22
  850. package/src/engine/graphics/ecs/camera/Camera.js +165 -165
  851. package/src/engine/graphics/ecs/camera/camera_active_frustum.js +25 -25
  852. package/src/engine/graphics/ecs/camera/camera_find_active.js +28 -28
  853. package/src/engine/graphics/ecs/camera/camera_traverse_active.js +18 -18
  854. package/src/engine/graphics/ecs/camera/pp/PerfectPanner.js +170 -170
  855. package/src/engine/graphics/ecs/camera/serialization/CameraSerializationAdapter.js +68 -68
  856. package/src/engine/graphics/ecs/camera/topdown/TopDownCameraController.js +296 -296
  857. package/src/engine/graphics/ecs/decal/v2/Decal.js +220 -220
  858. package/src/engine/graphics/ecs/decal/v2/DecalSerializationAdapter.js +56 -56
  859. package/src/engine/graphics/ecs/decal/v2/DecalSerializationUpgrader_0_1.js +38 -38
  860. package/src/engine/graphics/ecs/light/shadow/compute_view_frustum_aabb_in_space.js +62 -62
  861. package/src/engine/graphics/ecs/light/shadow/extend_shadow_camera_near_for_casters.js +77 -77
  862. package/src/engine/graphics/ecs/make_bvh_depth_range_computer.js +33 -33
  863. package/src/engine/graphics/ecs/mesh-v2/DeferredBoundsQueue.js +88 -88
  864. package/src/engine/graphics/ecs/mesh-v2/ShadedGeometry.js +428 -428
  865. package/src/engine/graphics/ecs/mesh-v2/aggregate/SGMesh.js +303 -303
  866. package/src/engine/graphics/ecs/mesh-v2/aggregate/SGMeshSystem.d.ts +1 -1
  867. package/src/engine/graphics/ecs/mesh-v2/aggregate/serialization/SGMeshSerializationAdapter.js +39 -39
  868. package/src/engine/graphics/ecs/mesh-v2/sg_hierarchy_compute_bounding_box_via_parent_entity.js +36 -36
  869. package/src/engine/graphics/ecs/path/tube/build/GeometryStream.js +94 -94
  870. package/src/engine/graphics/ecs/path/tube/build/build_stream_catmullrom.js +310 -310
  871. package/src/engine/graphics/ecs/path/tube/build/build_stream_linear.js +73 -73
  872. package/src/engine/graphics/ecs/path/tube/build/computeFrenetFrames.js +199 -199
  873. package/src/engine/graphics/ecs/path/tube/build/makeTubeStream.js +217 -217
  874. package/src/engine/graphics/ecs/path/tube/build/make_cap.js +340 -340
  875. package/src/engine/graphics/ecs/path/tube/build/make_ring_faces.js +40 -40
  876. package/src/engine/graphics/ecs/path/tube/build/make_ring_vertices.js +153 -153
  877. package/src/engine/graphics/ecs/render_layers_compute_depth_range.js +64 -64
  878. package/src/engine/graphics/ecs/trail3d/Trail3D.js +483 -483
  879. package/src/engine/graphics/ecs/trail3d/Trail3DFlags.js +26 -26
  880. package/src/engine/graphics/ecs/trail3d/advance_trail.js +42 -42
  881. package/src/engine/graphics/ecs/trail3d/make_gradient_stroke.js +58 -58
  882. package/src/engine/graphics/ecs/trail3d/seed_trail_stroke.js +90 -90
  883. package/src/engine/graphics/ecs/trail3d/seed_trail_tube.js +75 -75
  884. package/src/engine/graphics/ecs/trail3d/trail_head_position.js +19 -19
  885. package/src/engine/graphics/ecs/water/Water.js +104 -104
  886. package/src/engine/graphics/geometry/AttributeData.js +84 -84
  887. package/src/engine/graphics/geometry/buffered/geometry_compute_vertex_normals_indexed.js +49 -49
  888. package/src/engine/graphics/geometry/buffered/query/GeometrySpatialQueryAccelerator.js +288 -288
  889. package/src/engine/graphics/geometry/buffered/query/nearest_point_on_triangle_record.js +87 -87
  890. package/src/engine/graphics/geometry/buffered/read_triangle_vertices.js +102 -102
  891. package/src/engine/graphics/geometry/bvh/buffered/bvh32_from_buffered_geometry.js +52 -52
  892. package/src/engine/graphics/geometry/bvh/buffered/bvh32_from_indexed_geometry.js +20 -20
  893. package/src/engine/graphics/geometry/bvh/buffered/bvh32_from_unindexed_geometry.js +19 -19
  894. package/src/engine/graphics/geometry/optimization/geometry_optimize_uv_tension.js +135 -135
  895. package/src/engine/graphics/impostors/voxel/README.md +149 -149
  896. package/src/engine/graphics/impostors/voxel/VoxelImpostorDescription.js +141 -141
  897. package/src/engine/graphics/particles/node-based/codegen/glsl/getTypeByteSize.js +20 -20
  898. package/src/engine/graphics/particles/node-based/nodes/FloatConstant.d.ts +2 -0
  899. package/src/engine/graphics/particles/node-based/nodes/FloatConstant.d.ts.map +1 -1
  900. package/src/engine/graphics/particles/node-based/nodes/ReadFloatUniform.d.ts +2 -0
  901. package/src/engine/graphics/particles/node-based/nodes/ReadFloatUniform.d.ts.map +1 -1
  902. package/src/engine/graphics/particles/node-based/nodes/ReadVelocity.d.ts +2 -0
  903. package/src/engine/graphics/particles/node-based/nodes/ReadVelocity.d.ts.map +1 -1
  904. package/src/engine/graphics/particles/node-based/nodes/ShaderNode.d.ts +1 -2
  905. package/src/engine/graphics/particles/node-based/nodes/ShaderNode.d.ts.map +1 -1
  906. package/src/engine/graphics/particles/node-based/nodes/Vector3Constant.d.ts +2 -0
  907. package/src/engine/graphics/particles/node-based/nodes/Vector3Constant.d.ts.map +1 -1
  908. package/src/engine/graphics/particles/node-based/nodes/Vector3Merge.d.ts +2 -0
  909. package/src/engine/graphics/particles/node-based/nodes/Vector3Merge.d.ts.map +1 -1
  910. package/src/engine/graphics/particles/node-based/nodes/Vector3Split.d.ts +2 -0
  911. package/src/engine/graphics/particles/node-based/nodes/Vector3Split.d.ts.map +1 -1
  912. package/src/engine/graphics/particles/node-based/nodes/WriteVelocity.d.ts +2 -0
  913. package/src/engine/graphics/particles/node-based/nodes/WriteVelocity.d.ts.map +1 -1
  914. package/src/engine/graphics/particles/node-based/nodes/attribute/ReadVector3Attribute.d.ts +2 -0
  915. package/src/engine/graphics/particles/node-based/nodes/attribute/ReadVector3Attribute.d.ts.map +1 -1
  916. package/src/engine/graphics/particles/node-based/nodes/attribute/WriteVector3Attribute.d.ts +2 -0
  917. package/src/engine/graphics/particles/node-based/nodes/attribute/WriteVector3Attribute.d.ts.map +1 -1
  918. package/src/engine/graphics/particles/node-based/nodes/math/AddFloatNode.d.ts +2 -0
  919. package/src/engine/graphics/particles/node-based/nodes/math/AddFloatNode.d.ts.map +1 -1
  920. package/src/engine/graphics/particles/node-based/nodes/math/Vector3Add.d.ts +2 -0
  921. package/src/engine/graphics/particles/node-based/nodes/math/Vector3Add.d.ts.map +1 -1
  922. package/src/engine/graphics/particles/node-based/nodes/math/Vector3Divide.d.ts +2 -0
  923. package/src/engine/graphics/particles/node-based/nodes/math/Vector3Divide.d.ts.map +1 -1
  924. package/src/engine/graphics/particles/node-based/nodes/math/Vector3Multiply.d.ts +2 -0
  925. package/src/engine/graphics/particles/node-based/nodes/math/Vector3Multiply.d.ts.map +1 -1
  926. package/src/engine/graphics/particles/node-based/nodes/math/Vector3Subtract.d.ts +2 -0
  927. package/src/engine/graphics/particles/node-based/nodes/math/Vector3Subtract.d.ts.map +1 -1
  928. package/src/engine/graphics/particles/node-based/nodes/noise/CurlNoiseNode.d.ts +2 -0
  929. package/src/engine/graphics/particles/node-based/nodes/noise/CurlNoiseNode.d.ts.map +1 -1
  930. package/src/engine/graphics/particles/node-based/nodes/particle_data_type_component_count.js +45 -45
  931. package/src/engine/graphics/particles/particular/engine/ParticularEngine.js +229 -229
  932. package/src/engine/graphics/particles/particular/engine/emitter/ParticleEmitter.d.ts +2 -10
  933. package/src/engine/graphics/particles/particular/engine/emitter/ParticleEmitter.d.ts.map +1 -1
  934. package/src/engine/graphics/particles/particular/engine/emitter/ParticleEmitter.js +1149 -1149
  935. package/src/engine/graphics/particles/particular/engine/emitter/ParticlePool.js +472 -472
  936. package/src/engine/graphics/particles/particular/engine/parameter/ParameterLookupTable.js +2 -2
  937. package/src/engine/graphics/particles/particular/engine/parameter/ParameterLookupTableFlags.js +2 -2
  938. package/src/engine/graphics/particles/particular/engine/parameter/ParameterSet.d.ts.map +1 -1
  939. package/src/engine/graphics/particles/particular/engine/parameter/ParameterSet.js +3 -5
  940. package/src/engine/graphics/particles/particular/engine/shader/ShaderManager.js +135 -135
  941. package/src/engine/graphics/particles/particular/engine/utils/distribute_points_on_indexed_triangles.js +168 -168
  942. package/src/engine/graphics/render/forward_plus/plugin/forwardPlusMaterialMarker.js +18 -18
  943. package/src/engine/graphics/render/forward_plus/read_frustum_corner.js +2 -2
  944. package/src/engine/graphics/render/frame_graph/FRAME_GRAPH_RECOMMENDATIONS.md +219 -219
  945. package/src/engine/graphics/render/frame_graph/FrameGraph.js +900 -900
  946. package/src/engine/graphics/render/frame_graph/FrameGraphScope.js +72 -72
  947. package/src/engine/graphics/render/frame_graph/FrameGraphState.js +39 -39
  948. package/src/engine/graphics/render/frame_graph/ResourceEntry.js +96 -96
  949. package/src/engine/graphics/render/gizmo/GizmoShapeBuffer.js +105 -105
  950. package/src/engine/graphics/render/gizmo/GizmoShapeRenderingInterface.js +162 -162
  951. package/src/engine/graphics/render/layers/RenderLayer.js +95 -95
  952. package/src/engine/graphics/sh3/build_injected_shader.js +56 -56
  953. package/src/engine/graphics/sh3/grid_dilate_average.js +175 -175
  954. package/src/engine/graphics/sh3/path_tracer/BufferedGeometryBVH.js +506 -506
  955. package/src/engine/graphics/sh3/path_tracer/PathTracedMesh.js +161 -161
  956. package/src/engine/graphics/sh3/path_tracer/PathTracedRenderer.js +205 -205
  957. package/src/engine/graphics/sh3/path_tracer/PathTracedScene.js +698 -698
  958. package/src/engine/graphics/sh3/path_tracer/REVIEW_001_ACTION_PLAN.md +490 -490
  959. package/src/engine/graphics/sh3/path_tracer/geometry/compute_triangle_group_aabb3.js +2 -2
  960. package/src/engine/graphics/sh3/path_tracer/make_sky_hosek.js +45 -45
  961. package/src/engine/graphics/sh3/path_tracer/populate_path_traced_scene_from_ecd.js +73 -73
  962. package/src/engine/graphics/sh3/path_tracer/ray_hit_apply_transform.js +32 -32
  963. package/src/engine/graphics/sh3/path_tracer/texture/apply_texture_clamping_to_coordinate.js +28 -28
  964. package/src/engine/graphics/sh3/path_tracer/texture/sample_material.js +142 -142
  965. package/src/engine/graphics/sh3/sky/hosek/render_hosek_sky_to_equirectangular.js +70 -70
  966. package/src/engine/graphics/sh3/sky/hosek/sky_hosek_compute_irradiance_by_direction.js +388 -388
  967. package/src/engine/graphics/shaders/chunks/glsl_octahedral_impostor.js +277 -277
  968. package/src/engine/graphics/shaders/chunks/glsl_voxel_impostor_splat.js +57 -57
  969. package/src/engine/graphics/shadows/vsm.glsl.js +43 -43
  970. package/src/engine/graphics/texture/3d/scs3d_sample_linear3.js +81 -81
  971. package/src/engine/graphics/texture/EnvironmentTextureProjection.js +15 -15
  972. package/src/engine/graphics/texture/channel_count_to_format_prefix.js +26 -26
  973. package/src/engine/graphics/texture/isImageBitmap.js +2 -2
  974. package/src/engine/graphics/texture/reflection/convolve_equirectangular_reflection.js +189 -189
  975. package/src/engine/graphics/texture/reflection/equirectangular_reflection_roughness.js +51 -51
  976. package/src/engine/graphics/texture/sampler/HarmonicDiffusionGrid.js +145 -145
  977. package/src/engine/graphics/texture/sampler/Sampler2DArray.js +172 -172
  978. package/src/engine/graphics/texture/sampler/compare/FLIP_IMPLEMENTATION_PLAN.md +466 -466
  979. package/src/engine/graphics/texture/sampler/compare/flip/FlipParameters.js +18 -18
  980. package/src/engine/graphics/texture/sampler/compare/flip/FlipPooling.js +33 -33
  981. package/src/engine/graphics/texture/sampler/compare/flip/flip_build_feature_kernel.js +147 -147
  982. package/src/engine/graphics/texture/sampler/compare/flip/flip_build_spatial_kernel.js +177 -177
  983. package/src/engine/graphics/texture/sampler/compare/flip/flip_compute_color_difference.js +104 -104
  984. package/src/engine/graphics/texture/sampler/compare/flip/flip_compute_feature_difference.js +76 -76
  985. package/src/engine/graphics/texture/sampler/compare/flip/flip_compute_pixels_per_degree.js +34 -34
  986. package/src/engine/graphics/texture/sampler/compare/flip/flip_constants.js +77 -77
  987. package/src/engine/graphics/texture/sampler/compare/flip/flip_filter_feature.js +48 -48
  988. package/src/engine/graphics/texture/sampler/compare/flip/flip_filter_spatial.js +86 -86
  989. package/src/engine/graphics/texture/sampler/compare/flip/flip_pool.js +71 -71
  990. package/src/engine/graphics/texture/sampler/compare/flip/flip_srgb_to_ycxcz.js +55 -55
  991. package/src/engine/graphics/texture/sampler/compare/flip/sampler2d_flip_ldr.js +80 -80
  992. package/src/engine/graphics/texture/sampler/filter/SeparableKernel.js +82 -82
  993. package/src/engine/graphics/texture/sampler/filter/sampler2d_convolve_separable.js +122 -122
  994. package/src/engine/graphics/texture/sampler/resize/sampler2d_scale_down_lanczos.js +148 -148
  995. package/src/engine/graphics/texture/sampler/sampler2d_channel_apply_lut.js +82 -82
  996. package/src/engine/graphics/texture/sampler/sampler2d_combine.js +67 -67
  997. package/src/engine/graphics/texture/sampler/sampler2d_linear_f16_to_srgb_u8.js +54 -54
  998. package/src/engine/graphics/texture/sampler/sampler2d_sample_equirectangular_direction.js +63 -63
  999. package/src/engine/graphics/texture/sampler/sampler2d_srgb_to_linear.js +33 -33
  1000. package/src/engine/graphics/texture/sampler/sampler2d_srgb_u8_to_linear_f16.js +68 -68
  1001. package/src/engine/graphics/texture/shade_image_preview.js +103 -103
  1002. package/src/engine/graphics/trail/TrailSpawnMode.js +20 -20
  1003. package/src/engine/graphics/trail/tube/TubeX.js +573 -573
  1004. package/src/engine/graphics/trail/tube/TubeXMaterialSpec.js +58 -58
  1005. package/src/engine/graphics/trail/tube/simulator/TubeXFixedPhysicsSimulator.js +34 -34
  1006. package/src/engine/graphics/trail/tube/tube_attributes_spec.js +69 -69
  1007. package/src/engine/graphics/trail/tube/tube_frame.js +83 -83
  1008. package/src/engine/graphics3/AnimationGraphSystem3.js +329 -329
  1009. package/src/engine/graphics3/AnimationSystem3.js +352 -352
  1010. package/src/engine/graphics3/CameraSystem3.js +170 -170
  1011. package/src/engine/graphics3/DecalSystem3.js +441 -441
  1012. package/src/engine/graphics3/FogOfWarSystem3.js +265 -265
  1013. package/src/engine/graphics3/GraphicsEngine3.js +776 -776
  1014. package/src/engine/graphics3/HighlightSystem3.js +241 -241
  1015. package/src/engine/graphics3/LightSystem3.js +211 -211
  1016. package/src/engine/graphics3/MeshSystem3.js +646 -646
  1017. package/src/engine/graphics3/ParticleEmitterSystem3.js +289 -289
  1018. package/src/engine/graphics3/PathDisplaySystem3.js +309 -309
  1019. package/src/engine/graphics3/PickingQuery.js +48 -48
  1020. package/src/engine/graphics3/PickingResult.js +47 -47
  1021. package/src/engine/graphics3/PickingSystem3.js +273 -273
  1022. package/src/engine/graphics3/ShadeCameraAdapter.js +55 -55
  1023. package/src/engine/graphics3/ShadedGeometrySystem3.js +169 -169
  1024. package/src/engine/graphics3/TerrainSystem3.js +566 -566
  1025. package/src/engine/graphics3/TooltipComponentSystem3.js +328 -328
  1026. package/src/engine/graphics3/Trail3DSystem3.js +221 -221
  1027. package/src/engine/graphics3/WaterSystem3.js +293 -293
  1028. package/src/engine/graphics3/animation/ClipPlayback.js +84 -84
  1029. package/src/engine/graphics3/animation/wrap_clip_time.js +23 -23
  1030. package/src/engine/graphics3/animation/write_graph_playback.js +48 -48
  1031. package/src/engine/graphics3/camera_sync_from_transform.js +29 -29
  1032. package/src/engine/graphics3/debug/gizmo_view_from_camera.js +48 -48
  1033. package/src/engine/graphics3/decal/DECAL_CLUSTER.js +61 -61
  1034. package/src/engine/graphics3/decal/DECAL_GPU_RECORD.js +117 -117
  1035. package/src/engine/graphics3/decal/DecalAtlas.js +194 -194
  1036. package/src/engine/graphics3/decal/DecalAtlasFill.js +32 -32
  1037. package/src/engine/graphics3/decal/GPUDecalRenderer.js +360 -360
  1038. package/src/engine/graphics3/decal/chunk_decal_intersects_frustum.js +59 -59
  1039. package/src/engine/graphics3/decal/chunk_decal_sampling_info.js +57 -57
  1040. package/src/engine/graphics3/decal/chunk_decal_surface_frame.js +54 -54
  1041. package/src/engine/graphics3/decal/decal_atlas_border.js +69 -69
  1042. package/src/engine/graphics3/decal/decal_gpu_records.js +120 -120
  1043. package/src/engine/graphics3/decal/decal_material_samplers.js +109 -109
  1044. package/src/engine/graphics3/decal/graph_build_decal_clusters.js +212 -212
  1045. package/src/engine/graphics3/decal/shader_cluster_assign_decals.js +207 -207
  1046. package/src/engine/graphics3/decal/shader_cull_decals_frustum.js +108 -108
  1047. package/src/engine/graphics3/highlight/GPUHighlightRenderer.js +151 -151
  1048. package/src/engine/graphics3/highlight/HIGHLIGHT_OUTLINE.js +23 -23
  1049. package/src/engine/graphics3/highlight/chunk_highlight_lookup.js +53 -53
  1050. package/src/engine/graphics3/highlight/pack_highlight_color.js +44 -44
  1051. package/src/engine/graphics3/highlight/pack_highlight_table.js +74 -74
  1052. package/src/engine/graphics3/highlight/shader_highlight_outline.js +150 -150
  1053. package/src/engine/graphics3/load_cube_environment.js +68 -68
  1054. package/src/engine/graphics3/make_default_environment.js +107 -107
  1055. package/src/engine/graphics3/particles/particle_gpu_records.js +221 -221
  1056. package/src/engine/graphics3/path/build_tube_path_streams.js +98 -98
  1057. package/src/engine/graphics3/path/tube_stream_write_geometry.js +90 -90
  1058. package/src/engine/graphics3/preview/ModelPreviewFraming.js +35 -35
  1059. package/src/engine/graphics3/preview/make_model_preview_scene.js +154 -154
  1060. package/src/engine/graphics3/preview/make_model_thumbnail.js +48 -48
  1061. package/src/engine/graphics3/preview/model_preview_framing.js +70 -70
  1062. package/src/engine/graphics3/shade_bundle_bounds.js +55 -55
  1063. package/src/engine/graphics3/shade_camera_projection_ray.js +77 -77
  1064. package/src/engine/graphics3/shade_node_to_entity_composition.js +84 -84
  1065. package/src/engine/graphics3/terrain/GPUTerrainSplatRenderer.js +481 -481
  1066. package/src/engine/graphics3/terrain/chunk_terrain_grid_overlay.js +61 -61
  1067. package/src/engine/graphics3/terrain/chunk_terrain_splat_mix.js +66 -66
  1068. package/src/engine/graphics3/terrain/pack_terrain_row_table.js +53 -53
  1069. package/src/engine/graphics3/terrain/shader_terrain_splat.js +215 -215
  1070. package/src/engine/graphics3/trail/trail_tube_write_geometry.js +158 -158
  1071. package/src/engine/grid/grid2transform/GridPosition2TransformSystem.d.ts +2 -2
  1072. package/src/engine/grid/grid2transform/GridPosition2TransformSystem.d.ts.map +1 -1
  1073. package/src/engine/input/GAMEPAD_API_PROPOSAL.md +717 -717
  1074. package/src/engine/input/analog/apply_radial_dead_zone.js +38 -38
  1075. package/src/engine/input/devices/GamepadDevice.js +466 -466
  1076. package/src/engine/input/devices/ManualInputDevice.js +152 -152
  1077. package/src/engine/input/devices/PointerCoordinates.js +26 -26
  1078. package/src/engine/input/devices/events/GamepadEvents.js +10 -10
  1079. package/src/engine/input/devices/gamepad/GamepadAxes.js +19 -19
  1080. package/src/engine/input/devices/gamepad/GamepadButtons.js +29 -29
  1081. package/src/engine/input/devices/gamepad/GamepadCoordinates.js +17 -17
  1082. package/src/engine/input/devices/gamepad/GamepadDeviceLayout.js +323 -323
  1083. package/src/engine/input/devices/gamepad/GamepadHandle.js +286 -286
  1084. package/src/engine/input/devices/gamepad/GamepadLayout.js +160 -160
  1085. package/src/engine/input/devices/gamepad/GamepadStandardMapping.js +31 -31
  1086. package/src/engine/input/devices/gamepad/GamepadStick.js +169 -169
  1087. package/src/engine/input/devices/gamepad/GamepadStickCalibration.js +119 -119
  1088. package/src/engine/input/devices/gamepad/GamepadStickCalibrator.js +250 -250
  1089. package/src/engine/input/devices/gamepad/gamepad_layout_database.js +144 -144
  1090. package/src/engine/input/devices/gamepad/gamepad_read_hat_axis.js +61 -61
  1091. package/src/engine/input/devices/gamepad/gamepad_write_dpad_vector.js +30 -30
  1092. package/src/engine/input/devices/gamepad/prototypeGamepadTester.js +2124 -2124
  1093. package/src/engine/input/devices/mouse/MouseButtons.js +22 -22
  1094. package/src/engine/input/ecs/components/Input.js +118 -118
  1095. package/src/engine/input/ecs/components/InputController.js +78 -78
  1096. package/src/engine/input/ecs/ism/ISM_DESIGN.md +1523 -1523
  1097. package/src/engine/input/ecs/ism/ISM_IMPLEMENTATION_PLAN.md +627 -627
  1098. package/src/engine/input/ecs/ism/InputActionEvent.js +40 -40
  1099. package/src/engine/input/ecs/ism/InputBinding.js +71 -71
  1100. package/src/engine/input/ecs/ism/InputCoordinateKind.js +25 -25
  1101. package/src/engine/input/ecs/ism/InputCoordinateSource.js +86 -86
  1102. package/src/engine/input/ecs/ism/InputDirection.js +33 -33
  1103. package/src/engine/input/ecs/ism/InputMapProgram.js +236 -236
  1104. package/src/engine/input/ecs/ism/InputMapSystem.js +976 -976
  1105. package/src/engine/input/ecs/ism/InputOcclusion.js +31 -31
  1106. package/src/engine/input/ecs/ism/InputSource.js +67 -67
  1107. package/src/engine/input/ecs/ism/InputSourceTable.js +107 -107
  1108. package/src/engine/input/ecs/ism/coordinate/InputCoordinateChannel.js +93 -93
  1109. package/src/engine/input/ecs/ism/coordinate/InputCoordinateChannelType.js +11 -11
  1110. package/src/engine/input/ecs/ism/coordinate/InputCoordinateDevice.js +153 -153
  1111. package/src/engine/input/ecs/ism/coordinate/InputCoordinateSwitches.js +160 -160
  1112. package/src/engine/input/ecs/ism/device/GAMEPAD_INPUT_DEVICE_ID.js +9 -9
  1113. package/src/engine/input/ecs/ism/device/GamepadInputDeviceAdapter.js +88 -88
  1114. package/src/engine/input/ecs/ism/device/InputDeviceAdapter.js +72 -72
  1115. package/src/engine/input/ecs/ism/device/KEYBOARD_INPUT_DEVICE_ID.js +9 -9
  1116. package/src/engine/input/ecs/ism/device/KeyboardInputDeviceAdapter.js +63 -63
  1117. package/src/engine/input/ecs/ism/device/ManualInputDeviceAdapter.js +55 -55
  1118. package/src/engine/input/ecs/ism/device/POINTER_INPUT_DEVICE_ID.js +9 -9
  1119. package/src/engine/input/ecs/ism/device/PointerInputDeviceAdapter.js +160 -160
  1120. package/src/engine/input/ecs/ism/map/InputBinding.js +88 -88
  1121. package/src/engine/input/ecs/ism/map/InputCoordinateBinding.js +102 -102
  1122. package/src/engine/input/ecs/ism/map/InputLayer.js +220 -220
  1123. package/src/engine/input/ecs/ism/map/InputMap.js +331 -331
  1124. package/src/engine/input/ecs/ism/prototypeInputMap.js +309 -309
  1125. package/src/engine/input/ecs/ism/trigger/InputTrigger.js +148 -148
  1126. package/src/engine/input/ecs/ism/trigger/InputTriggerChord.js +173 -173
  1127. package/src/engine/input/ecs/ism/trigger/InputTriggerGamepadButton.js +37 -37
  1128. package/src/engine/input/ecs/ism/trigger/InputTriggerHold.js +176 -176
  1129. package/src/engine/input/ecs/ism/trigger/InputTriggerKey.js +39 -39
  1130. package/src/engine/input/ecs/ism/trigger/InputTriggerMouseButton.js +38 -38
  1131. package/src/engine/input/ecs/ism/trigger/InputTriggerRelease.js +120 -120
  1132. package/src/engine/input/ecs/ism/trigger/InputTriggerSequence.js +315 -315
  1133. package/src/engine/input/ecs/ism/trigger/InputTriggerSwitch.js +105 -105
  1134. package/src/engine/input/ecs/ism/trigger/InputTriggerType.js +17 -17
  1135. package/src/engine/input/ecs/systems/InputControllerSystem.js +253 -253
  1136. package/src/engine/input/ecs/systems/InputSystem.js +162 -162
  1137. package/src/engine/intelligence/behavior/primitive/FailingBehaviorSerializationAdapter.js +25 -25
  1138. package/src/engine/intelligence/behavior/primitive/SucceedingBehaviorSerializationAdapter.js +26 -26
  1139. package/src/engine/intelligence/behavior/util/AsynchronousDelayAction.js +55 -55
  1140. package/src/engine/interpolation/BinaryInterpolationAdapter.js +56 -56
  1141. package/src/engine/interpolation/Interpoland.js +49 -49
  1142. package/src/engine/interpolation/Interpolated.js +149 -149
  1143. package/src/engine/interpolation/PoseInterpolationAdapter.js +61 -61
  1144. package/src/engine/interpolation/TransformPoseSerializationAdapter.js +57 -57
  1145. package/src/engine/interpolation/pose_interpoland.js +27 -27
  1146. package/src/engine/navigation/ecs/path_following/PathFollower.js +222 -222
  1147. package/src/engine/navigation/mesh/NavigationMesh.js +145 -145
  1148. package/src/engine/navigation/mesh/OVERLAPPING_SURFACES_2026_08_25.md +661 -0
  1149. package/src/engine/navigation/mesh/PATHFINDING_PLAN.md +229 -229
  1150. package/src/engine/navigation/mesh/build/clip_soup_against_overhangs.d.ts.map +1 -1
  1151. package/src/engine/navigation/mesh/build/clip_soup_against_overhangs.js +628 -520
  1152. package/src/engine/network/CONGESTION_CONTROL.md +87 -87
  1153. package/src/engine/network/README.md +52 -52
  1154. package/src/engine/network/adapters/QuaternionInterpolationAdapter.js +43 -43
  1155. package/src/engine/network/adapters/TransformInterpolationAdapter.js +84 -84
  1156. package/src/engine/network/adapters/TransformReplicationAdapter.js +87 -87
  1157. package/src/engine/network/adapters/Vector3InterpolationAdapter.js +46 -46
  1158. package/src/engine/network/convertPathToURL.js +2 -2
  1159. package/src/engine/network/core/MalformedPacketError.js +26 -26
  1160. package/src/engine/network/core/quantize/quantize_float.js +73 -73
  1161. package/src/engine/network/core/quantize/quantize_position.js +65 -65
  1162. package/src/engine/network/core/sequence/ack_bitfield.js +77 -77
  1163. package/src/engine/network/core/sequence/seq16.js +69 -69
  1164. package/src/engine/network/core/sequence/seq32.js +73 -73
  1165. package/src/engine/network/ecs/NetworkSystem.js +84 -84
  1166. package/src/engine/network/ecs/components/NetworkIdentity.js +73 -73
  1167. package/src/engine/network/ecs/owner_authorization.js +55 -55
  1168. package/src/engine/network/ecs/serialization/NetworkIdentitySerializationAdapter.js +64 -64
  1169. package/src/engine/network/replication/ScopeFilter.js +71 -71
  1170. package/src/engine/network/sim/ActionLog.js +152 -152
  1171. package/src/engine/network/sim/SimAction.js +273 -273
  1172. package/src/engine/network/sim/Snapshotter.js +222 -222
  1173. package/src/engine/network/state/FrameBufferRing.js +168 -168
  1174. package/src/engine/network/state/InputRing.js +134 -134
  1175. package/src/engine/network/state/PriorityAccumulator.js +180 -180
  1176. package/src/engine/network/time/TimeDilation.js +62 -62
  1177. package/src/engine/network/transport/Channel.js +336 -336
  1178. package/src/engine/network/transport/Transport.js +203 -203
  1179. package/src/engine/network/transport/adapters/NodeUDPTransport.js +186 -186
  1180. package/src/engine/network/transport/adapters/WebRTCDataChannelTransport.js +105 -105
  1181. package/src/engine/network/transport/fragments/fragment_send.js +147 -147
  1182. package/src/engine/network/transport/fragments/packet_size.js +101 -101
  1183. package/src/engine/network/xhr.js +23 -23
  1184. package/src/engine/physics/BULLET_REVIEW.md +945 -945
  1185. package/src/engine/physics/CANNON_REVIEW.md +1300 -1300
  1186. package/src/engine/physics/CONSTRAINT_SOLVER_BENCH_LOG.md +208 -208
  1187. package/src/engine/physics/CONSTRAINT_SOLVER_IMPROVEMENTS_PLAN.md +364 -364
  1188. package/src/engine/physics/INTEPOLATION_SYSTEM_PLAN.md +287 -287
  1189. package/src/engine/physics/JOLT_REVIEW.md +913 -913
  1190. package/src/engine/physics/PLAN.md +1120 -1094
  1191. package/src/engine/physics/RAPIER_REVIEW.md +934 -934
  1192. package/src/engine/physics/REVIEW_001_ACTION_PLAN.md +642 -642
  1193. package/src/engine/physics/REVIEW_002.md +151 -151
  1194. package/src/engine/physics/REVIEW_003.md +166 -166
  1195. package/src/engine/physics/broadphase/compute_fat_world_aabb.js +97 -97
  1196. package/src/engine/physics/constraint/DofMode.js +35 -35
  1197. package/src/engine/physics/constraint/solve_constraints.js +1 -1
  1198. package/src/engine/physics/contact/combine_material.js +35 -35
  1199. package/src/engine/physics/ecs/BodyKind.js +29 -29
  1200. package/src/engine/physics/ecs/Collider.js +177 -177
  1201. package/src/engine/physics/ecs/ColliderFlags.js +15 -15
  1202. package/src/engine/physics/ecs/ColliderSerializationAdapter.js +37 -37
  1203. package/src/engine/physics/ecs/Joint.js +475 -475
  1204. package/src/engine/physics/ecs/JointSerializationAdapter.js +72 -72
  1205. package/src/engine/physics/ecs/PhysicsSystem.d.ts +3 -0
  1206. package/src/engine/physics/ecs/PhysicsSystem.d.ts.map +1 -1
  1207. package/src/engine/physics/ecs/PhysicsSystem.js +7 -2
  1208. package/src/engine/physics/ecs/RigidBodySerializationUpgrader_0_1.js +37 -37
  1209. package/src/engine/physics/ecs/SleepState.js +12 -12
  1210. package/src/engine/physics/ecs/find_non_finite_physics_state.js +76 -76
  1211. package/src/engine/physics/fluid/FluidField.js +867 -867
  1212. package/src/engine/physics/fluid/FluidSimulator.js +832 -832
  1213. package/src/engine/physics/fluid/REVIEW_02_PLAN.md +243 -243
  1214. package/src/engine/physics/fluid/SliceVisualiser.js +190 -190
  1215. package/src/engine/physics/fluid/ecs/FluidComponent.js +239 -239
  1216. package/src/engine/physics/fluid/ecs/FluidEffectorsComponent.js +89 -89
  1217. package/src/engine/physics/fluid/ecs/FluidObstacle.js +97 -97
  1218. package/src/engine/physics/fluid/ecs/FluidObstacleSystem.js +348 -348
  1219. package/src/engine/physics/fluid/ecs/FluidSystem.js +142 -142
  1220. package/src/engine/physics/fluid/ecs/FluidWorkerCore.js +123 -123
  1221. package/src/engine/physics/fluid/ecs/WorkerFluidSystem.js +395 -395
  1222. package/src/engine/physics/fluid/ecs/fluid.worker.js +12 -12
  1223. package/src/engine/physics/fluid/ecs/fluid_build_world_to_grid.js +32 -32
  1224. package/src/engine/physics/fluid/ecs/fluid_gather_visible_effectors.js +47 -47
  1225. package/src/engine/physics/fluid/ecs/fluid_reanchor_field.js +85 -85
  1226. package/src/engine/physics/fluid/ecs/fluid_sync_effectors_from_transform.js +16 -16
  1227. package/src/engine/physics/fluid/ecs/fluid_worker_protocol.js +111 -111
  1228. package/src/engine/physics/fluid/ecs/makeFluidWorker.js +18 -18
  1229. package/src/engine/physics/fluid/effector/AbstractFluidEffector.js +81 -81
  1230. package/src/engine/physics/fluid/effector/GlobalFluidEffector.js +202 -202
  1231. package/src/engine/physics/fluid/effector/ImpulseFluidEffector.js +257 -257
  1232. package/src/engine/physics/fluid/effector/WakeFluidEffector.js +405 -405
  1233. package/src/engine/physics/fluid/prototype.js +126 -126
  1234. package/src/engine/physics/fluid/solver/v3_grid_advect_maccormack_scalar.js +143 -143
  1235. package/src/engine/physics/fluid/solver/v3_grid_advect_maccormack_velocity.js +162 -162
  1236. package/src/engine/physics/fluid/solver/v3_grid_advect_sl_velocity.js +107 -107
  1237. package/src/engine/physics/fluid/solver/v3_grid_apply_vorticity_confinement.js +131 -131
  1238. package/src/engine/physics/fluid/solver/v3_grid_compute_solid_neighbour_mask.js +90 -90
  1239. package/src/engine/physics/fluid/solver/v3_grid_limit8_at_indices.js +38 -38
  1240. package/src/engine/physics/fluid/solver/v3_grid_patch_edges_constant.js +77 -77
  1241. package/src/engine/physics/fluid/solver/v3_grid_patch_edges_uniform.js +113 -113
  1242. package/src/engine/physics/fluid/solver/v3_grid_sample_scalar_masked.js +92 -92
  1243. package/src/engine/physics/fluid/solver/v3_grid_shift_in_place.js +107 -107
  1244. package/src/engine/physics/fluid/solver/v3_grid_solve_pressure.js +135 -135
  1245. package/src/engine/physics/fluid/solver/v3_grid_solve_pressure_pcg.js +511 -511
  1246. package/src/engine/physics/fluid/solver/v3_grid_subtract_pressure_gradient.js +108 -108
  1247. package/src/engine/physics/fluid/solver/v3_mac_advect_maccormack_velocity.js +317 -317
  1248. package/src/engine/physics/fluid/solver/v3_mac_advect_scalar.js +236 -236
  1249. package/src/engine/physics/fluid/solver/v3_mac_advect_sl_velocity.js +217 -217
  1250. package/src/engine/physics/fluid/solver/v3_mac_apply_vorticity_confinement.js +174 -174
  1251. package/src/engine/physics/fluid/solver/v3_mac_clip_trace.js +95 -95
  1252. package/src/engine/physics/fluid/solver/v3_mac_compute_divergence.js +77 -77
  1253. package/src/engine/physics/fluid/solver/v3_mac_compute_face_solid.js +131 -131
  1254. package/src/engine/physics/fluid/solver/v3_mac_subtract_pressure_gradient.js +104 -104
  1255. package/src/engine/physics/inertia/world_inverse_inertia.js +160 -160
  1256. package/src/engine/physics/integration/integrate_position.js +97 -97
  1257. package/src/engine/physics/narrowphase/box_box_manifold.js +681 -681
  1258. package/src/engine/physics/narrowphase/box_triangle_contact.js +906 -906
  1259. package/src/engine/physics/narrowphase/capsule_triangle_contact.js +395 -395
  1260. package/src/engine/physics/narrowphase/compute_penetration.js +369 -369
  1261. package/src/engine/physics/narrowphase/decomposition/decompose_to_triangles.js +55 -55
  1262. package/src/engine/physics/narrowphase/decomposition/triangle_buffer_layout.js +37 -37
  1263. package/src/engine/physics/narrowphase/mesh_mesh_tet_manifold.js +117 -117
  1264. package/src/engine/physics/narrowphase/narrowphase_step.js +1738 -1738
  1265. package/src/engine/physics/narrowphase/reduce_manifold_contacts.js +74 -74
  1266. package/src/engine/physics/narrowphase/refine_ray_hit.d.ts +12 -11
  1267. package/src/engine/physics/narrowphase/refine_ray_hit.d.ts.map +1 -1
  1268. package/src/engine/physics/narrowphase/refine_ray_hit.js +228 -36
  1269. package/src/engine/physics/narrowphase/sphere_sphere_contact.js +52 -52
  1270. package/src/engine/physics/narrowphase/sphere_triangle_contact.js +138 -138
  1271. package/src/engine/physics/persistence/solver_caches.js +309 -309
  1272. package/src/engine/physics/queries/overlap_shape.js +187 -187
  1273. package/src/engine/physics/queries/raycast.d.ts +7 -4
  1274. package/src/engine/physics/queries/raycast.d.ts.map +1 -1
  1275. package/src/engine/physics/queries/raycast.js +11 -5
  1276. package/src/engine/physics/queries/shape_cast.js +417 -417
  1277. package/src/engine/physics/solver/apply_impulse_to_body.js +86 -86
  1278. package/src/engine/physics/solver/friction_cone.js +37 -37
  1279. package/src/engine/physics/solver/solve_contacts.js +1 -1
  1280. package/src/engine/platform/WebEnginePlatform.d.ts.map +1 -1
  1281. package/src/engine/platform/WebEnginePlatform.js +1 -2
  1282. package/src/engine/scene/SerializedScene.d.ts +2 -2
  1283. package/src/engine/scene/SerializedScene.d.ts.map +1 -1
  1284. package/src/engine/scene/transitionToScene.js +121 -121
  1285. package/src/engine/simulation/Ticker.d.ts.map +1 -1
  1286. package/src/engine/simulation/Ticker.js +33 -0
  1287. package/src/engine/sound/ecs/audio/LiveEmitterSet.js +345 -345
  1288. package/src/engine/sound/ecs/audio/SpatialAudioIndex.js +140 -140
  1289. package/src/engine/sound/simulation/AcousticSimulator.js +646 -646
  1290. package/src/engine/sound/simulation/IMPLEMENTATION_PLAN.md +432 -432
  1291. package/src/engine/sound/simulation/PROBE_PLACEMENT.md +250 -250
  1292. package/src/engine/sound/simulation/PROBE_TRANSFER_RESEARCH.md +322 -322
  1293. package/src/engine/sound/simulation/README.md +277 -277
  1294. package/src/engine/sound/simulation/REFLECTION_IR_PLAN.md +261 -261
  1295. package/src/engine/sound/simulation/VISIBILITY_GRAPH_PLAN.md +121 -121
  1296. package/src/engine/sound/simulation/configureAcousticSimulation.js +48 -48
  1297. package/src/engine/sound/simulation/core/AcousticMedium.js +113 -113
  1298. package/src/engine/sound/simulation/core/AcousticMediumSerializationAdapter.js +27 -27
  1299. package/src/engine/sound/simulation/core/AcousticOccluderIndex.js +312 -312
  1300. package/src/engine/sound/simulation/core/AcousticRegionIndex.js +159 -159
  1301. package/src/engine/sound/simulation/core/AcousticSolution.js +90 -90
  1302. package/src/engine/sound/simulation/core/AcousticSourceState.js +109 -109
  1303. package/src/engine/sound/simulation/core/AcousticVolumeRegion.js +73 -73
  1304. package/src/engine/sound/simulation/core/OcclusionSolver.js +112 -112
  1305. package/src/engine/sound/simulation/core/RayHit.js +50 -50
  1306. package/src/engine/sound/simulation/core/VolumeField.js +149 -149
  1307. package/src/engine/sound/simulation/definition/AcousticDirectivity.js +114 -114
  1308. package/src/engine/sound/simulation/definition/AcousticDirectivitySerializationAdapter.js +30 -30
  1309. package/src/engine/sound/simulation/definition/AcousticMaterial.js +122 -122
  1310. package/src/engine/sound/simulation/definition/AcousticMaterialSerializationAdapter.js +32 -32
  1311. package/src/engine/sound/simulation/ecs/AcousticBody.js +48 -48
  1312. package/src/engine/sound/simulation/ecs/AcousticBodySerializationAdapter.js +34 -34
  1313. package/src/engine/sound/simulation/ecs/AcousticProbeFieldSystem.js +64 -64
  1314. package/src/engine/sound/simulation/ecs/AcousticSimulationSystem.js +75 -75
  1315. package/src/engine/sound/simulation/ecs/AcousticVolume.js +74 -74
  1316. package/src/engine/sound/simulation/ecs/AcousticVolumeSerializationAdapter.js +37 -37
  1317. package/src/engine/sound/simulation/ecs/AcousticVolumeSystem.js +66 -66
  1318. package/src/engine/sound/simulation/probe/AcousticProbeField.js +578 -578
  1319. package/src/engine/sound/simulation/probe/AcousticProbeFieldSerializationAdapter.js +60 -60
  1320. package/src/engine/sound/simulation/probe/ProbeFieldPathingSolution.js +64 -64
  1321. package/src/engine/sound/simulation/probe/ProbeVisibilityRecheck.js +132 -132
  1322. package/src/engine/sound/simulation/probe/acoustic_probe_transfer.js +103 -103
  1323. package/src/engine/sound/simulation/probe/acoustic_trace_energy.js +136 -136
  1324. package/src/engine/sound/simulation/probe/bakeProbeField.js +169 -169
  1325. package/src/engine/sound/simulation/probe/bakeProbeReflectors.js +110 -110
  1326. package/src/engine/sound/simulation/probe/bakeReverbBands.js +110 -110
  1327. package/src/engine/sound/simulation/probe/bakeVisibility.js +148 -148
  1328. package/src/engine/sound/simulation/probe/echogram_band_rt60.js +83 -83
  1329. package/src/engine/sound/simulation/probe/probe_delaunay_edges.js +215 -215
  1330. package/src/engine/sound/simulation/probe/probe_densify_portals.js +140 -140
  1331. package/src/engine/sound/simulation/probe/probe_detect_portals.js +196 -196
  1332. package/src/engine/sound/simulation/probe/probe_edge_key.js +38 -38
  1333. package/src/engine/sound/simulation/probe/probe_edge_visible.js +41 -41
  1334. package/src/engine/sound/simulation/probe/probe_place_sdf_cover.js +82 -82
  1335. package/src/engine/sound/simulation/probe/probe_refine_connectivity.js +181 -181
  1336. package/src/engine/sound/simulation/probe/probe_sdf_grid.js +171 -171
  1337. package/src/engine/sound/simulation/probe/probe_visibility_augment.js +146 -146
  1338. package/src/engine/sound/simulation/probe/probe_visibility_bridge_components.js +167 -167
  1339. package/src/engine/sound/simulation/render/FoaProbeReverbRenderer.js +354 -354
  1340. package/src/engine/sound/simulation/render/ProbeReverbRenderer.js +231 -231
  1341. package/src/engine/sound/simulation/render/foaReverbImpulseResponse.js +105 -105
  1342. package/src/engine/sound/simulation/render/reverbImpulseResponse.js +66 -66
  1343. package/src/engine/sound/simulation/render/reverb_band_tails.js +93 -93
  1344. package/src/engine/sound/simulation/serialization/populateAcousticSerializationRegistry.js +25 -25
  1345. package/src/engine/sound/sopra/README.md +643 -643
  1346. package/src/engine/sound/sopra/SopraEngine.js +432 -432
  1347. package/src/engine/sound/sopra/asset/AssetManagerBufferProvider.js +71 -71
  1348. package/src/engine/sound/sopra/asset/BufferProvider.js +29 -29
  1349. package/src/engine/sound/sopra/asset/StubBufferProvider.js +58 -58
  1350. package/src/engine/sound/sopra/definition/BusDefinition.js +142 -142
  1351. package/src/engine/sound/sopra/definition/BusDefinitionSerializationAdapter.js +54 -54
  1352. package/src/engine/sound/sopra/definition/DuckingRule.js +106 -106
  1353. package/src/engine/sound/sopra/definition/DuckingRuleSerializationAdapter.js +31 -31
  1354. package/src/engine/sound/sopra/definition/EventDescription.js +262 -262
  1355. package/src/engine/sound/sopra/definition/EventDescriptionSerializationAdapter.js +55 -55
  1356. package/src/engine/sound/sopra/definition/MixerSnapshot.js +83 -83
  1357. package/src/engine/sound/sopra/definition/MixerSnapshotSerializationAdapter.js +39 -39
  1358. package/src/engine/sound/sopra/definition/ParameterDefinition.js +117 -117
  1359. package/src/engine/sound/sopra/definition/ParameterDefinitionSerializationAdapter.js +31 -31
  1360. package/src/engine/sound/sopra/definition/VoiceStealMode.js +18 -18
  1361. package/src/engine/sound/sopra/definition/clip/AbstractAudioClip.js +109 -109
  1362. package/src/engine/sound/sopra/definition/clip/BlendContainerAudioClip.js +185 -185
  1363. package/src/engine/sound/sopra/definition/clip/BlendContainerAudioClipSerializationAdapter.js +59 -59
  1364. package/src/engine/sound/sopra/definition/clip/ContainerAudioClip.js +128 -128
  1365. package/src/engine/sound/sopra/definition/clip/RandomContainerAudioClip.js +226 -226
  1366. package/src/engine/sound/sopra/definition/clip/RandomContainerAudioClipSerializationAdapter.js +54 -54
  1367. package/src/engine/sound/sopra/definition/clip/SampleAudioClip.js +191 -191
  1368. package/src/engine/sound/sopra/definition/clip/SampleAudioClipSerializationAdapter.js +39 -39
  1369. package/src/engine/sound/sopra/definition/clip/SequenceContainerAudioClip.js +79 -79
  1370. package/src/engine/sound/sopra/definition/clip/SequenceContainerAudioClipSerializationAdapter.js +42 -42
  1371. package/src/engine/sound/sopra/definition/clip/SilenceAudioClip.js +77 -77
  1372. package/src/engine/sound/sopra/definition/clip/SilenceAudioClipSerializationAdapter.js +27 -27
  1373. package/src/engine/sound/sopra/definition/clip/SwitchContainerAudioClip.d.ts +5 -0
  1374. package/src/engine/sound/sopra/definition/clip/SwitchContainerAudioClip.d.ts.map +1 -1
  1375. package/src/engine/sound/sopra/definition/clip/SwitchContainerAudioClip.js +125 -125
  1376. package/src/engine/sound/sopra/definition/clip/SwitchContainerAudioClipSerializationAdapter.js +41 -41
  1377. package/src/engine/sound/sopra/definition/effect/AbstractAudioEffect.js +24 -24
  1378. package/src/engine/sound/sopra/definition/effect/CompressorEffect.js +120 -120
  1379. package/src/engine/sound/sopra/definition/effect/CompressorEffectSerializationAdapter.js +31 -31
  1380. package/src/engine/sound/sopra/definition/effect/EqEffect.js +128 -128
  1381. package/src/engine/sound/sopra/definition/effect/EqEffectSerializationAdapter.js +29 -29
  1382. package/src/engine/sound/sopra/definition/effect/ReverbEffect.js +113 -113
  1383. package/src/engine/sound/sopra/definition/effect/ReverbEffectSerializationAdapter.js +25 -25
  1384. package/src/engine/sound/sopra/runtime/BusGraph.js +231 -231
  1385. package/src/engine/sound/sopra/runtime/ParameterStore.js +98 -98
  1386. package/src/engine/sound/sopra/runtime/SopraPlaybackContext.js +68 -68
  1387. package/src/engine/sound/sopra/runtime/Voice.js +145 -145
  1388. package/src/engine/sound/sopra/runtime/VoiceManager.js +136 -136
  1389. package/src/engine/sound/sopra/runtime/VoicePool.js +17 -17
  1390. package/src/engine/sound/sopra/serialization/populateSopraSerializationRegistry.js +42 -42
  1391. package/src/engine/sound/sopra/serialization/sopraJSON.js +99 -99
  1392. package/src/engine/sound/sopra/serialization/sopraSerializationHarness.js +49 -49
  1393. package/src/engine/sound/sopra/util/MockAudioContext.js +224 -224
  1394. package/src/engine/sound/sopra/util/fadeOutAndStop.js +60 -60
  1395. package/src/engine/ui/DraggableAspect.js +115 -115
  1396. package/src/engine/ui/apply_pointer_tilt_3d.js +50 -50
  1397. package/src/generation/COORDINATES.md +54 -54
  1398. package/src/generation/GridGenerationResource.js +24 -24
  1399. package/src/generation/GridTaskGroup.js +214 -214
  1400. package/src/generation/REVIEW_01_ACTION_PLAN.md +628 -628
  1401. package/src/generation/automata/CaveGeneratorCellularAutomata.js +79 -79
  1402. package/src/generation/automata/CellularAutomata.js +22 -22
  1403. package/src/generation/compute_writes_union.js +25 -25
  1404. package/src/generation/filtering/CellFilter.js +117 -117
  1405. package/src/generation/filtering/core/CellFilterBinaryOperation.js +37 -37
  1406. package/src/generation/filtering/core/CellFilterOperationTertiary.js +43 -43
  1407. package/src/generation/filtering/core/CellFilterUnaryOperation.js +29 -29
  1408. package/src/generation/filtering/numeric/process/computeCellFilterSobel.js +36 -36
  1409. package/src/generation/filtering/numeric/process/computeFilterSurfaceNormal.js +42 -42
  1410. package/src/generation/filtering/numeric/util/populateSampler2DFromCellFilter.js +18 -18
  1411. package/src/generation/filtering/numeric/util/write_cell_filter_to_sampler2d.js +47 -47
  1412. package/src/generation/filtering/util/cell_filter_weights_pick.js +54 -54
  1413. package/src/generation/grid/GridData.js +233 -233
  1414. package/src/generation/grid/coords/grid_to_texel.js +10 -10
  1415. package/src/generation/grid/coords/texel_to_grid.js +10 -10
  1416. package/src/generation/grid/generation/GridTaskApplyActionToCells.js +90 -90
  1417. package/src/generation/grid/generation/GridTaskDensityMarkerDistribution.js +388 -388
  1418. package/src/generation/grid/generation/GridTaskExecuteRuleTimes.js +127 -127
  1419. package/src/generation/grid/generation/GridTaskSequence.js +58 -58
  1420. package/src/generation/grid/generation/NoopGridTaskGenerator.js +12 -12
  1421. package/src/generation/grid/generation/discrete/GridTaskActionRuleSet.js +48 -48
  1422. package/src/generation/grid/generation/discrete/GridTaskCellularAutomata.js +146 -146
  1423. package/src/generation/grid/generation/discrete/GridTaskConnectRooms.js +540 -540
  1424. package/src/generation/grid/generation/discrete/layer/GridTaskBuildSourceDistanceMap.js +222 -222
  1425. package/src/generation/grid/generation/grid/GridTaskAddNodesFixed.js +44 -44
  1426. package/src/generation/grid/generation/road/GridTaskGenerateRoads.js +614 -614
  1427. package/src/generation/markers/GridCellActionPlaceMarker.js +244 -244
  1428. package/src/generation/markers/GridCellActionPlaceMarkerGroup.js +86 -86
  1429. package/src/generation/markers/actions/terrain/MarkerNodeActionPaintTerrain.js +266 -266
  1430. package/src/generation/markers/emitter/MarkerNodeEmitterGridCellAction.js +49 -49
  1431. package/src/generation/placement/GridCellActionTransformNearbyMarkers.js +120 -120
  1432. package/src/generation/placement/action/GridCellAction.js +45 -45
  1433. package/src/generation/placement/action/GridCellActionPlaceTags.js +140 -140
  1434. package/src/generation/placement/action/GridCellActionWriteFilterToLayer.js +89 -89
  1435. package/src/generation/placement/action/random/CellActionSelectRandom.js +69 -69
  1436. package/src/generation/placement/action/random/weighted/CellActionSelectWeightedRandom.js +91 -91
  1437. package/src/generation/placement/action/util/GridCellActionSequence.js +61 -61
  1438. package/src/generation/placement/action/util/GridCellDisplacedAction.js +61 -61
  1439. package/src/generation/rules/cell/CellMatcherRemovalPreservesConnectivity.js +183 -183
  1440. package/src/generation/theme/ThemeEngine.js +666 -666
  1441. package/src/shade/RENDERER_CONTRACT.md +568 -0
  1442. package/src/shade/descriptor/texture/format/gpu_texture_format_info.js +267 -267
  1443. package/src/shade/device/ShadeDeviceFailure.js +104 -104
  1444. package/src/shade/device/ShadeDeviceFailureReason.js +41 -41
  1445. package/src/shade/device/mock/IMPLEMENTATION_PLAN.md +255 -255
  1446. package/src/shade/device/mock/REVIEW_2026_08_22.md +527 -527
  1447. package/src/shade/device/mock/SoftwareGPUBindGroup.js +23 -23
  1448. package/src/shade/device/mock/SoftwareGPUBindGroupLayout.js +22 -22
  1449. package/src/shade/device/mock/SoftwareGPUError.js +63 -63
  1450. package/src/shade/device/mock/SoftwareGPUPipeline.js +75 -75
  1451. package/src/shade/device/mock/SoftwareGPUQuerySet.js +113 -113
  1452. package/src/shade/device/mock/SoftwareGPUSampler.js +30 -30
  1453. package/src/shade/device/mock/SoftwareGPUShaderModule.js +32 -32
  1454. package/src/shade/device/mock/SoftwareGPUTextureView.js +22 -22
  1455. package/src/shade/device/mock/SoftwareGPUUncapturedErrorEvent.js +23 -23
  1456. package/src/shade/device/mock/normalize_dynamic_offsets.js +26 -26
  1457. package/src/shade/device/mock/texture_copy.js +287 -287
  1458. package/src/shade/device/timing/GPUTimer.js +285 -285
  1459. package/src/shade/device/timing/GPUTimerArray.js +205 -205
  1460. package/src/shade/playground/add_random_lights.js +49 -49
  1461. package/src/shade/playground/add_random_meshes.js +83 -83
  1462. package/src/shade/playground/animation_prototype.js +508 -508
  1463. package/src/shade/playground/animation_prototype_skinning.js +647 -647
  1464. package/src/shade/playground/animation_prototype_stress.js +765 -765
  1465. package/src/shade/playground/enable_picker.js +59 -59
  1466. package/src/shade/playground/gltf_skinned_prototype.js +560 -560
  1467. package/src/shade/playground/gltf_skinned_stress_prototype.js +387 -387
  1468. package/src/shade/playground/gltf_viewer.js +282 -282
  1469. package/src/shade/playground/load_default_environment.js +19 -19
  1470. package/src/shade/playground/main.js +2727 -2727
  1471. package/src/shade/playground/make_animated_instances.js +117 -117
  1472. package/src/shade/playground/prototype_virtual_texture.js +274 -274
  1473. package/src/shade/playground/prototype_volumetrics_01.js +206 -206
  1474. package/src/shade/playground/sample.js +234 -234
  1475. package/src/shade/playground/sample_load_gltf.js +102 -102
  1476. package/src/shade/playground/spread_node3d_grid.js +19 -19
  1477. package/src/shade/renderer/Renderer.js +2436 -2436
  1478. package/src/shade/renderer/ShadeIndirectLightingMode.js +14 -14
  1479. package/src/shade/renderer/animation/ShadeAnimationChannel.js +414 -414
  1480. package/src/shade/renderer/animation/compute_skinned_mesh_clip_bounding_sphere.js +531 -531
  1481. package/src/shade/renderer/animation/pose/PosePlayback.js +55 -55
  1482. package/src/shade/renderer/animation/pose/pose_evaluate_local.js +179 -179
  1483. package/src/shade/renderer/animation/pose/pose_evaluate_world.js +76 -76
  1484. package/src/shade/renderer/animation/skinning/GPUMeshSkinningContext.js +1024 -1024
  1485. package/src/shade/renderer/animation/skinning/SKINNING_BINDING_STRUCT.js +79 -79
  1486. package/src/shade/renderer/animation/skinning/chunk_skin_blend_mesh_local.js +56 -56
  1487. package/src/shade/renderer/binding/BindGroupManager.js +282 -282
  1488. package/src/shade/renderer/binding/adapt_cache_capacity.js +25 -25
  1489. package/src/shade/renderer/buffer/GPUBufferAllocator.js +116 -116
  1490. package/src/shade/renderer/buffer/buffer_pool_ops.js +123 -123
  1491. package/src/shade/renderer/buffer/table/GPU_DATABASE_PAGE_SIZE.js +9 -9
  1492. package/src/shade/renderer/buffer/table/GPU_DATABASE_UPLOAD_BUFFER_LIMIT.js +8 -8
  1493. package/src/shade/renderer/buffer/table/PageBufferPool.js +71 -71
  1494. package/src/shade/renderer/buffer/table/PageSlotAllocator.js +95 -95
  1495. package/src/shade/renderer/camera/Camera.js +281 -281
  1496. package/src/shade/renderer/camera/GPUCameraContext.js +233 -233
  1497. package/src/shade/renderer/camera/orbital/README.md +84 -84
  1498. package/src/shade/renderer/deferred/shader_composite_volumetrics.js +75 -75
  1499. package/src/shade/renderer/dynamic/DYNAMIC_MESH_VERTEX.js +25 -25
  1500. package/src/shade/renderer/dynamic/GPUDynamicMeshRenderer.js +342 -342
  1501. package/src/shade/renderer/dynamic/GPUDynamicMeshResidency.js +167 -167
  1502. package/src/shade/renderer/dynamic/shader_dynamic_mesh.js +170 -170
  1503. package/src/shade/renderer/extension/FrameContext.js +263 -263
  1504. package/src/shade/renderer/extension/FramePhase.js +104 -104
  1505. package/src/shade/renderer/extension/FrameRecord.js +126 -126
  1506. package/src/shade/renderer/extension/GBufferTextures.js +83 -83
  1507. package/src/shade/renderer/extension/PresentTarget.js +34 -34
  1508. package/src/shade/renderer/extension/RENDER_EXTENSION_DESIGN.md +918 -918
  1509. package/src/shade/renderer/extension/RenderExtension.js +69 -69
  1510. package/src/shade/renderer/extension/RenderExtensionRegistry.js +319 -319
  1511. package/src/shade/renderer/extension/SceneColor.js +40 -40
  1512. package/src/shade/renderer/extension/ViewTextures.js +78 -78
  1513. package/src/shade/renderer/fow/GPUFogOfWarRenderer.js +182 -182
  1514. package/src/shade/renderer/geometry/Geometry.js +608 -608
  1515. package/src/shade/renderer/geometry/geometry_build_from_meshlet_geometry.js +342 -342
  1516. package/src/shade/renderer/geometry/meshlet_geometry_transfer.js +41 -41
  1517. package/src/shade/renderer/geometry/primitives/make_box_geometry.js +153 -153
  1518. package/src/shade/renderer/geometry/primitives/make_cylinder_geometry.js +240 -240
  1519. package/src/shade/renderer/geometry/primitives/make_plane_geometry.js +145 -145
  1520. package/src/shade/renderer/geometry/primitives/make_polyhedron_geometry.js +327 -327
  1521. package/src/shade/renderer/geometry/primitives/make_torus_geometry.js +107 -107
  1522. package/src/shade/renderer/geometry/primitives/make_torus_knot_geometry.js +165 -165
  1523. package/src/shade/renderer/geometry/virtual/VGEO_FORMAT.md +607 -607
  1524. package/src/shade/renderer/geometry/virtual/build/VGeoBuildOptions.js +66 -66
  1525. package/src/shade/renderer/geometry/virtual/build/VGeoBuildResult.js +32 -32
  1526. package/src/shade/renderer/geometry/virtual/build/VGeoFaceGraph.js +103 -103
  1527. package/src/shade/renderer/geometry/virtual/build/VGeoGatheredFaces.js +25 -25
  1528. package/src/shade/renderer/geometry/virtual/build/VGeoLevel.js +33 -33
  1529. package/src/shade/renderer/geometry/virtual/build/VGeoLevelOptions.js +62 -62
  1530. package/src/shade/renderer/geometry/virtual/build/VGeoLevelSummary.js +49 -49
  1531. package/src/shade/renderer/geometry/virtual/build/VGeoSubmesh.js +20 -20
  1532. package/src/shade/renderer/geometry/virtual/build/VirtualGeometryMesh.js +214 -214
  1533. package/src/shade/renderer/geometry/virtual/build/gltf/GltfByteSource.js +122 -122
  1534. package/src/shade/renderer/geometry/virtual/build/gltf/GltfCollectReport.js +29 -29
  1535. package/src/shade/renderer/geometry/virtual/build/gltf/GltfDocument.js +210 -210
  1536. package/src/shade/renderer/geometry/virtual/build/gltf/GltfGeometry.js +48 -48
  1537. package/src/shade/renderer/geometry/virtual/build/gltf/GltfGeometryInstance.js +25 -25
  1538. package/src/shade/renderer/geometry/virtual/build/gltf/GltfGeometrySelection.js +28 -28
  1539. package/src/shade/renderer/geometry/virtual/build/gltf/gltf_collect_geometries.js +296 -296
  1540. package/src/shade/renderer/geometry/virtual/build/gltf/gltf_open_document.js +166 -166
  1541. package/src/shade/renderer/geometry/virtual/build/gltf/gltf_read_geometry.js +199 -199
  1542. package/src/shade/renderer/geometry/virtual/build/vgeo_assemble_pages.js +173 -173
  1543. package/src/shade/renderer/geometry/virtual/build/vgeo_bounding_sphere.js +28 -28
  1544. package/src/shade/renderer/geometry/virtual/build/vgeo_build.js +200 -200
  1545. package/src/shade/renderer/geometry/virtual/build/vgeo_build_levels.js +1786 -1786
  1546. package/src/shade/renderer/geometry/virtual/build/vgeo_build_page_nodes.js +157 -157
  1547. package/src/shade/renderer/geometry/virtual/build/vgeo_build_root_group.js +26 -26
  1548. package/src/shade/renderer/geometry/virtual/build/vgeo_compute_vertex_normals.js +75 -75
  1549. package/src/shade/renderer/geometry/virtual/build/vgeo_compute_vertex_tangents.js +143 -143
  1550. package/src/shade/renderer/geometry/virtual/build/vgeo_containing_sphere.js +44 -44
  1551. package/src/shade/renderer/geometry/virtual/build/vgeo_extract_submesh.js +41 -41
  1552. package/src/shade/renderer/geometry/virtual/build/vgeo_gather_faces.js +71 -71
  1553. package/src/shade/renderer/geometry/virtual/build/vgeo_graph_components.js +123 -123
  1554. package/src/shade/renderer/geometry/virtual/build/vgeo_narrow_indices.js +26 -26
  1555. package/src/shade/renderer/geometry/virtual/build/vgeo_normalize_source.js +112 -112
  1556. package/src/shade/renderer/geometry/virtual/build/vgeo_partition_graph.js +56 -56
  1557. package/src/shade/renderer/geometry/virtual/build/vgeo_sphere_inflate_to_contain.js +70 -70
  1558. package/src/shade/renderer/geometry/virtual/build/vgeo_split_face_set.js +187 -187
  1559. package/src/shade/renderer/geometry/virtual/build/vgeo_split_face_set_packed.js +207 -207
  1560. package/src/shade/renderer/geometry/virtual/format/VGEO_DEFECT.js +18 -18
  1561. package/src/shade/renderer/geometry/virtual/format/VGeoDefect.js +38 -38
  1562. package/src/shade/renderer/geometry/virtual/format/VGeoWriteOptions.js +64 -64
  1563. package/src/shade/renderer/geometry/virtual/format/attribute/VGEO_ATTRIBUTE.js +13 -13
  1564. package/src/shade/renderer/geometry/virtual/format/attribute/VGEO_ATTRIBUTES.js +50 -50
  1565. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_attribute_allocate.js +28 -28
  1566. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_attribute_mask.js +23 -23
  1567. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_attributes_read.js +31 -31
  1568. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_layers_copy_vertex.js +32 -32
  1569. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_layers_declare.js +46 -46
  1570. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_layers_like.js +19 -19
  1571. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_layers_test_fixtures.js +27 -27
  1572. package/src/shade/renderer/geometry/virtual/format/checksum/vgeo_asset_id_preimage.js +62 -62
  1573. package/src/shade/renderer/geometry/virtual/format/checksum/vgeo_compute_asset_id.js +13 -13
  1574. package/src/shade/renderer/geometry/virtual/format/checksum/vgeo_compute_directory_block_checksum.js +41 -41
  1575. package/src/shade/renderer/geometry/virtual/format/checksum/vgeo_compute_frame_checksum.js +41 -41
  1576. package/src/shade/renderer/geometry/virtual/format/checksum/vgeo_compute_header_checksum.js +25 -25
  1577. package/src/shade/renderer/geometry/virtual/format/cluster/VGEO_CLUSTER_OFFSET.js +20 -20
  1578. package/src/shade/renderer/geometry/virtual/format/cluster/VGEO_CLUSTER_RECORD_SIZE.js +5 -5
  1579. package/src/shade/renderer/geometry/virtual/format/cluster/VGEO_MAX_CHILD_REF.js +5 -5
  1580. package/src/shade/renderer/geometry/virtual/format/cluster/VGEO_MAX_CLUSTER_TRIANGLE_COUNT.js +5 -5
  1581. package/src/shade/renderer/geometry/virtual/format/cluster/VGEO_MAX_CLUSTER_VERTEX_COUNT.js +5 -5
  1582. package/src/shade/renderer/geometry/virtual/format/cluster/VGEO_NO_CHILD.js +5 -5
  1583. package/src/shade/renderer/geometry/virtual/format/cluster/VGEO_NO_GROUP.js +5 -5
  1584. package/src/shade/renderer/geometry/virtual/format/cluster/VGEO_SAME_PAGE.js +6 -6
  1585. package/src/shade/renderer/geometry/virtual/format/dag/VirtualGeometryCluster.js +81 -81
  1586. package/src/shade/renderer/geometry/virtual/format/dag/VirtualGeometryGroup.js +37 -37
  1587. package/src/shade/renderer/geometry/virtual/format/dag/VirtualGeometryPageNode.js +55 -55
  1588. package/src/shade/renderer/geometry/virtual/format/directory/VGEO_DIRECTORY_BLOCK_RECORDS.js +5 -5
  1589. package/src/shade/renderer/geometry/virtual/format/directory/VGEO_DIRECTORY_FLAG_ROOT_PAGE.js +5 -5
  1590. package/src/shade/renderer/geometry/virtual/format/directory/VGEO_DIRECTORY_RECORD_SIZE.js +5 -5
  1591. package/src/shade/renderer/geometry/virtual/format/directory/vgeo_directory_block_count.js +12 -12
  1592. package/src/shade/renderer/geometry/virtual/format/frame/VGEO_CHILD_OFFSET.js +11 -11
  1593. package/src/shade/renderer/geometry/virtual/format/frame/VGEO_CHILD_TABLE_ENTRY_SIZE.js +5 -5
  1594. package/src/shade/renderer/geometry/virtual/format/frame/VGEO_CODEC_LZ4.js +11 -11
  1595. package/src/shade/renderer/geometry/virtual/format/frame/VGEO_CODEC_RAW.js +5 -5
  1596. package/src/shade/renderer/geometry/virtual/format/frame/VGEO_FRAME_ALIGNMENT.js +5 -5
  1597. package/src/shade/renderer/geometry/virtual/format/frame/VGEO_FRAME_HEADER_SIZE.js +5 -5
  1598. package/src/shade/renderer/geometry/virtual/format/frame/VGEO_FRAME_OFFSET.js +13 -13
  1599. package/src/shade/renderer/geometry/virtual/format/frame/VGEO_PAGE_MAGIC.js +5 -5
  1600. package/src/shade/renderer/geometry/virtual/format/frame/VGeoFrameBlob.js +21 -21
  1601. package/src/shade/renderer/geometry/virtual/format/frame/VGeoFrameExtent.js +59 -59
  1602. package/src/shade/renderer/geometry/virtual/format/frame/vgeo_codec_name.js +27 -27
  1603. package/src/shade/renderer/geometry/virtual/format/frame/vgeo_decode_frame_blob.js +84 -84
  1604. package/src/shade/renderer/geometry/virtual/format/frame/vgeo_encode_frame_blob.js +56 -56
  1605. package/src/shade/renderer/geometry/virtual/format/frame/vgeo_frame_decode_bound.js +35 -35
  1606. package/src/shade/renderer/geometry/virtual/format/frame/vgeo_scan_frames.js +74 -74
  1607. package/src/shade/renderer/geometry/virtual/format/header/VGEO_FORMAT_VERSION.js +6 -6
  1608. package/src/shade/renderer/geometry/virtual/format/header/VGEO_HASH_ALGO_XXH3.js +5 -5
  1609. package/src/shade/renderer/geometry/virtual/format/header/VGEO_HEADER_OFFSET.js +31 -31
  1610. package/src/shade/renderer/geometry/virtual/format/header/VGEO_HEADER_SIZE.js +5 -5
  1611. package/src/shade/renderer/geometry/virtual/format/header/VGEO_LEVEL_OFFSET.js +20 -20
  1612. package/src/shade/renderer/geometry/virtual/format/header/VGEO_LEVEL_RECORD_SIZE.js +9 -9
  1613. package/src/shade/renderer/geometry/virtual/format/header/VGEO_MAGIC.js +5 -5
  1614. package/src/shade/renderer/geometry/virtual/format/header/VGEO_MIN_READER_VERSION.js +5 -5
  1615. package/src/shade/renderer/geometry/virtual/format/header/VGEO_PAYLOAD_ENCODING_SHADE_MESHLET_V1.js +5 -5
  1616. package/src/shade/renderer/geometry/virtual/format/header/VGeoLevelRecord.js +41 -41
  1617. package/src/shade/renderer/geometry/virtual/format/page/VGEO_GROUP_RECORD_SIZE.js +5 -5
  1618. package/src/shade/renderer/geometry/virtual/format/page/VGEO_MAX_GROUP_CLUSTER_COUNT.js +6 -6
  1619. package/src/shade/renderer/geometry/virtual/format/page/VGEO_METRIC_OFFSET.js +21 -21
  1620. package/src/shade/renderer/geometry/virtual/format/page/VGEO_METRIC_SIZE.js +5 -5
  1621. package/src/shade/renderer/geometry/virtual/format/page/VGEO_NODE_FANOUT.js +9 -9
  1622. package/src/shade/renderer/geometry/virtual/format/page/VGEO_NODE_FLAG_LEAF.js +5 -5
  1623. package/src/shade/renderer/geometry/virtual/format/page/VGEO_NODE_OFFSET.js +10 -10
  1624. package/src/shade/renderer/geometry/virtual/format/page/VGEO_NODE_RECORD_SIZE.js +5 -5
  1625. package/src/shade/renderer/geometry/virtual/format/page/VGEO_PAGE_HEADER_SIZE.js +5 -5
  1626. package/src/shade/renderer/geometry/virtual/format/page/VGEO_PAGE_OFFSET.js +16 -16
  1627. package/src/shade/renderer/geometry/virtual/format/payload/VGEO_ENCODING_FLAGS_MASK.js +6 -6
  1628. package/src/shade/renderer/geometry/virtual/format/payload/VGeoEncodedPayload.js +18 -18
  1629. package/src/shade/renderer/geometry/virtual/format/payload/vgeo_encode_cluster_payload.js +200 -200
  1630. package/src/shade/renderer/geometry/virtual/format/payload/vgeo_payload_attribute_offset.js +39 -39
  1631. package/src/shade/renderer/geometry/virtual/format/payload/vgeo_payload_size.js +20 -20
  1632. package/src/shade/renderer/geometry/virtual/format/prototypeVGEOFormatViewer.js +2063 -2063
  1633. package/src/shade/renderer/geometry/virtual/format/vgeo_validate_container.js +1622 -1622
  1634. package/src/shade/renderer/geometry/virtual/format/vgeo_write_container.js +485 -485
  1635. package/src/shade/renderer/gpu_primitive/sort/ffx/graph_radix_sort.js +115 -115
  1636. package/src/shade/renderer/gpu_primitive/sort/one_sweep/verify_radix_sort_one_sweep.js +110 -110
  1637. package/src/shade/renderer/light/environment/octahedral_resample_kernel.js +50 -50
  1638. package/src/shade/renderer/light/environment/octahedral_uv_to_direction.js +30 -30
  1639. package/src/shade/renderer/light/environment/resample_cube_to_octahedral.js +157 -157
  1640. package/src/shade/renderer/light/make_sunlight.js +35 -35
  1641. package/src/shade/renderer/light/model/io/light_record_point.js +39 -39
  1642. package/src/shade/renderer/loader/gltf/GLTF_PRIMITIVE_MODE.js +23 -23
  1643. package/src/shade/renderer/loader/gltf/MESHOPT_COMPRESSION_PLAN.md +793 -793
  1644. package/src/shade/renderer/loader/gltf/ext/GltfBufferViewExtension.js +54 -54
  1645. package/src/shade/renderer/loader/gltf/ext/GltfBufferViewExtensionSet.js +109 -109
  1646. package/src/shade/renderer/loader/gltf/ext/gltf_apply_buffer_view_extensions.js +69 -69
  1647. package/src/shade/renderer/loader/gltf/ext/gltf_buffer_view_extensions.js +22 -22
  1648. package/src/shade/renderer/loader/gltf/ext/gltf_geometry_extensions.js +39 -39
  1649. package/src/shade/renderer/loader/gltf/ext/meshopt/MeshoptBufferViewExtension.js +301 -301
  1650. package/src/shade/renderer/loader/gltf/gltf_component_type.js +223 -223
  1651. package/src/shade/renderer/loader/gltf/gltf_node_world_matrices.js +137 -137
  1652. package/src/shade/renderer/loader/gltf/gltf_parse_container.js +241 -241
  1653. package/src/shade/renderer/loader/gltf/gltf_read_accessor.js +294 -294
  1654. package/src/shade/renderer/loader/gltf/gltf_used_image_indices.js +49 -49
  1655. package/src/shade/renderer/loader/gltf/load_gltf.js +1253 -1253
  1656. package/src/shade/renderer/loader/gltf/tiny-gltf.js +646 -646
  1657. package/src/shade/renderer/loader/usd/UsdError.js +33 -33
  1658. package/src/shade/renderer/loader/usd/UsdExtensionRegistry.js +69 -69
  1659. package/src/shade/renderer/loader/usd/load_usd.js +226 -226
  1660. package/src/shade/renderer/loader/usd/parse_usda.js +931 -931
  1661. package/src/shade/renderer/loader/usd/unpack_usdz.js +106 -106
  1662. package/src/shade/renderer/loader/usd/usd_build_scene.js +658 -658
  1663. package/src/shade/renderer/loader/usd/usd_compose_transform.js +258 -258
  1664. package/src/shade/renderer/loader/usd/usd_decode_image.js +384 -384
  1665. package/src/shade/renderer/loader/usd/usd_lz4.js +182 -182
  1666. package/src/shade/renderer/loader/usd/usd_triangulate.js +159 -159
  1667. package/src/shade/renderer/material/ShadeMaterial.d.ts +2 -2
  1668. package/src/shade/renderer/material/ShadeMaterial.js +2 -2
  1669. package/src/shade/renderer/material/standard/fragment_gbuffer.js +334 -334
  1670. package/src/shade/renderer/material/standard/fragment_gbuffer_vt.js +169 -169
  1671. package/src/shade/renderer/material/standard/material_gbuffer_vertex.js +45 -45
  1672. package/src/shade/renderer/material/standard_material_to_struct.js +23 -23
  1673. package/src/shade/renderer/particles/prototypeParticleSystem.js +450 -450
  1674. package/src/shade/renderer/particles/runtime/spawn_request_encoding.js +52 -52
  1675. package/src/shade/renderer/pool/TransientResourcePool.js +392 -392
  1676. package/src/shade/renderer/postprocess/bloom/bloom_chain_resolution.js +29 -29
  1677. package/src/shade/renderer/postprocess/bloom/graph_bloom_downsample.js +109 -109
  1678. package/src/shade/renderer/postprocess/bloom/graph_postprocess_bloom.js +155 -155
  1679. package/src/shade/renderer/postprocess/cas/shader_ffx_rcas.js +114 -114
  1680. package/src/shade/renderer/postprocess/dof/DOF_AUTOFOCUS_PARAMS_STRUCT.js +25 -25
  1681. package/src/shade/renderer/postprocess/dof/DOF_FOCUS_STATE_STRUCT.js +23 -23
  1682. package/src/shade/renderer/postprocess/dof/DOF_PRESETS.js +27 -27
  1683. package/src/shade/renderer/postprocess/dof/chunk_dof_coc_band_diameter.js +41 -41
  1684. package/src/shade/renderer/postprocess/dof/chunk_dof_coc_band_radius.js +32 -32
  1685. package/src/shade/renderer/postprocess/dof/chunk_dof_coc_coefficient.js +26 -26
  1686. package/src/shade/renderer/postprocess/dof/compute_dof_autofocus_params.js +64 -64
  1687. package/src/shade/renderer/postprocess/dof/compute_dof_uniform.js +54 -54
  1688. package/src/shade/renderer/postprocess/dof/gather/DOF2_CONFIG.js +91 -91
  1689. package/src/shade/renderer/postprocess/dof/gather/DOF2_PRESETS.js +9 -9
  1690. package/src/shade/renderer/postprocess/dof/gather/DOF2_SETTINGS_STRUCT.js +66 -66
  1691. package/src/shade/renderer/postprocess/dof/gather/DOF2_TEXTURE_FORMAT.js +22 -22
  1692. package/src/shade/renderer/postprocess/dof/gather/DepthOfFieldU.js +292 -292
  1693. package/src/shade/renderer/postprocess/dof/gather/chunk_dof2_coc_to_near_far.js +25 -25
  1694. package/src/shade/renderer/postprocess/dof/gather/chunk_dof2_gather_weight.js +30 -30
  1695. package/src/shade/renderer/postprocess/dof/gather/compute_dof2_uniform.js +54 -54
  1696. package/src/shade/renderer/postprocess/dof/gather/graph_postprocess_dof2.js +194 -194
  1697. package/src/shade/renderer/postprocess/dof/gather/shader_dof2_fill.js +68 -68
  1698. package/src/shade/renderer/postprocess/dof/gather/shader_dof2_gather_far.js +140 -140
  1699. package/src/shade/renderer/postprocess/dof/gather/shader_dof2_gather_near.js +142 -142
  1700. package/src/shade/renderer/postprocess/dof/gather/shader_dof2_recombine.js +99 -99
  1701. package/src/shade/renderer/postprocess/dof/gather/shader_dof2_setup.js +102 -102
  1702. package/src/shade/renderer/postprocess/dof/gather/shader_dof2_tile_dilate.js +75 -75
  1703. package/src/shade/renderer/postprocess/dof/gather/shader_dof2_tile_max.js +73 -73
  1704. package/src/shade/renderer/postprocess/dof/raymarch/DOF_SETTINGS_STRUCT.js +49 -49
  1705. package/src/shade/renderer/postprocess/dof/raymarch/DepthOfField.js +298 -298
  1706. package/src/shade/renderer/postprocess/dof/raymarch/graph_postprocess_dof.js +244 -244
  1707. package/src/shade/renderer/postprocess/dof/raymarch/shader_dof_composite.js +91 -91
  1708. package/src/shade/renderer/postprocess/dof/raymarch/shader_dof_firefly_cleanup.js +61 -61
  1709. package/src/shade/renderer/postprocess/dof/raymarch/shader_dof_prefilter.js +90 -90
  1710. package/src/shade/renderer/postprocess/dof/raymarch/shader_dof_raymarch_compute.js +267 -267
  1711. package/src/shade/renderer/postprocess/dof/raymarch/shader_dof_slice_compute.js +271 -271
  1712. package/src/shade/renderer/postprocess/dof/raymarch/shader_dof_tile_dilate.js +70 -70
  1713. package/src/shade/renderer/postprocess/dof/raymarch/shader_dof_tile_max.js +72 -72
  1714. package/src/shade/renderer/postprocess/dof/shader_dof_autofocus.js +99 -99
  1715. package/src/shade/renderer/postprocess/gtao/generate_magic_square_noise.js +51 -51
  1716. package/src/shade/renderer/postprocess/motion_blur/MotionBlur.js +56 -56
  1717. package/src/shade/renderer/postprocess/upscale-dither/NOTES.md +78 -78
  1718. package/src/shade/renderer/postprocess/upscale-dither/UpscaleDither.js +95 -95
  1719. package/src/shade/renderer/postprocess/upscale-dither/fragment_shader_upscale_dither.js +204 -204
  1720. package/src/shade/renderer/rasterize/bucket/construct_primitive_state.js +58 -58
  1721. package/src/shade/renderer/rasterize/fast/graph_rasterize_scene_fast.js +230 -230
  1722. package/src/shade/renderer/rasterize/native/graph_rasterize_meshes_alpha_tested.js +278 -278
  1723. package/src/shade/renderer/rasterize/native/oit/chunk_material_side_should_draw.js +26 -26
  1724. package/src/shade/renderer/rasterize/native/oit/shader_oit_resolve_moments_ibl.js +178 -178
  1725. package/src/shade/renderer/rasterize/standard/graph_rasterize_partial_opaque.js +235 -235
  1726. package/src/shade/renderer/rasterize/standard/graph_rasterize_remaining_opaque.js +208 -208
  1727. package/src/shade/renderer/rasterize/standard/graph_rasterize_scene.js +206 -206
  1728. package/src/shade/renderer/scene/DynamicMesh.js +70 -70
  1729. package/src/shade/renderer/scene/DynamicMeshBatch.js +90 -90
  1730. package/src/shade/renderer/scene/GPUSceneContext.js +901 -901
  1731. package/src/shade/renderer/scene/SceneNodeBinding.js +56 -56
  1732. package/src/shade/renderer/scene/SkinnedMesh.js +55 -55
  1733. package/src/shade/renderer/scene/TransformAuthority.js +33 -33
  1734. package/src/shade/renderer/scene/optimization/deduplicate_geometries.js +38 -38
  1735. package/src/shade/renderer/scene/optimization/deduplicate_images.js +93 -93
  1736. package/src/shade/renderer/scene/optimization/geometry/internal/match_refit_validate.js +127 -127
  1737. package/src/shade/renderer/scene/optimization/geometry/internal/match_solve_transform.js +492 -492
  1738. package/src/shade/renderer/scene/optimization/optimize_texture_set.js +81 -81
  1739. package/src/shade/renderer/scene/optimization/source_to_sampler2d.js +21 -21
  1740. package/src/shade/renderer/scene/serialization/STANDARD_MATERIAL_SERIALIZED_BYTE_SIZE.js +12 -12
  1741. package/src/shade/renderer/shader/chunk/geometry/quaternion/dual/chunk_dual_quat_transform_point.js +26 -26
  1742. package/src/shade/renderer/shader/chunk/noise/chunk_interleaved_gradient_noise.js +20 -20
  1743. package/src/shade/renderer/shader/chunk/spherical_harmonics/dering/chunk_sh2_dering_optimize_positive.js +61 -61
  1744. package/src/shade/renderer/shader/chunk/texture/chunk_texture_filter_fxaa_lite.js +225 -225
  1745. package/src/shade/renderer/shader/chunk/texture/dither/chunk_dither_color_8bit_gradient_noise.js +25 -25
  1746. package/src/shade/renderer/shadow/map/DirectionalLightShadowmap.js +384 -384
  1747. package/src/shade/renderer/shadow/map/PointLightShadowmap.js +105 -105
  1748. package/src/shade/renderer/shadow/map/SpotLightShadowmap.js +63 -63
  1749. package/src/shade/renderer/shadow/map/TetrahedronPointLightShadowmap.js +89 -89
  1750. package/src/shade/renderer/shadow/map/shadowmap_update_score.js +232 -232
  1751. package/src/shade/renderer/texture/GPUTextureAllocator.js +85 -85
  1752. package/src/shade/renderer/texture/texture_pool_ops.js +171 -171
  1753. package/src/shade/renderer/texture/virtual/PageTableQuadAllocator.js +253 -253
  1754. package/src/shade/renderer/texture/virtual/README.md +276 -276
  1755. package/src/shade/renderer/texture/virtual/VTPageTable.js +330 -330
  1756. package/src/shade/renderer/texture/virtual/VTPhysicalCache.js +349 -349
  1757. package/src/shade/renderer/texture/virtual/VT_CONSTANTS.js +154 -154
  1758. package/src/shade/renderer/texture/virtual/VT_STACK_INFO_STRUCT.js +20 -20
  1759. package/src/shade/renderer/texture/virtual/VirtualTextureManager.js +1276 -1276
  1760. package/src/shade/renderer/texture/virtual/VirtualTextureStack.js +106 -106
  1761. package/src/shade/renderer/texture/virtual/build_chunk_vt_lookup.js +150 -150
  1762. package/src/shade/renderer/texture/virtual/build_shader_vt_feedback.js +232 -232
  1763. package/src/shade/renderer/texture/virtual/chunk_vt_request_key.js +28 -28
  1764. package/src/shade/renderer/texture/virtual/source/VTSourceImage.js +207 -207
  1765. package/src/shade/renderer/texture/virtual/source/VTSourceProcedural.js +135 -135
  1766. package/src/shade/renderer/texture/virtual/source/VTSourceTiled.js +132 -132
  1767. package/src/shade/renderer/water/GPUWaterRenderer.js +167 -167
  1768. package/src/shade/renderer/water/shader_water.js +246 -246
  1769. package/src/shade/renderer/water/water_height_texture_from_sampler.js +56 -56
  1770. package/src/shade/vitest.setup.mjs +89 -89
  1771. package/src/shade/wgsl/validate_wgsl_source.js +316 -316
  1772. package/src/view/controller/controls/NumericIntervalControl.js +90 -90
  1773. package/src/view/controller/controls/Vector2Control.js +83 -83
  1774. package/src/view/controller/controls/Vector3Control.js +90 -90
  1775. package/src/view/elements/Group.js +56 -56
  1776. package/src/view/elements/drawCheckers.js +33 -33
  1777. package/src/view/elements/windrose/WindRoseDiagram.js +369 -369
  1778. package/src/view/graphics/WebGLContextFailureView.js +76 -76
  1779. package/src/view/layout/DockPanel.js +51 -51
  1780. package/src/view/layout/SplitView.js +365 -365
  1781. package/src/view/layout/show_popup_menu.js +81 -81
  1782. package/src/view/minimap/MinimapCanvasView.js +411 -411
  1783. package/src/view/minimap/MinimapImageCache.js +88 -88
  1784. package/src/view/minimap/MinimapMarkerCollection.d.ts +0 -1
  1785. package/src/view/minimap/MinimapMarkerCollection.d.ts.map +1 -1
  1786. package/src/view/minimap/MinimapMarkerCollection.js +180 -180
  1787. package/src/view/minimap/dom/MinimapCameraView.js +206 -206
  1788. package/src/view/minimap/minimap_focus_area.js +97 -97
  1789. package/editor/process/symbolic/SocketsSymbolicDisplay.js +0 -192
  1790. package/src/core/geom/3d/topology/struct/binary/io/moved_tests.txt +0 -37
  1791. package/src/engine/ecs/attachment/Attachment.d.ts +0 -40
  1792. package/src/engine/ecs/attachment/Attachment.d.ts.map +0 -1
  1793. package/src/engine/ecs/attachment/Attachment.js +0 -57
  1794. package/src/engine/ecs/attachment/AttachmentBinding.d.ts +0 -28
  1795. package/src/engine/ecs/attachment/AttachmentBinding.d.ts.map +0 -1
  1796. package/src/engine/ecs/attachment/AttachmentBinding.js +0 -41
  1797. package/src/engine/ecs/attachment/AttachmentSystem.d.ts +0 -61
  1798. package/src/engine/ecs/attachment/AttachmentSystem.d.ts.map +0 -1
  1799. package/src/engine/ecs/attachment/AttachmentSystem.js +0 -307
  1800. package/src/engine/ecs/attachment/BoneAttachmentBinding.d.ts +0 -27
  1801. package/src/engine/ecs/attachment/BoneAttachmentBinding.d.ts.map +0 -1
  1802. package/src/engine/ecs/attachment/BoneAttachmentBinding.js +0 -69
  1803. package/src/engine/ecs/attachment/TransformAttachmentBinding.d.ts +0 -4
  1804. package/src/engine/ecs/attachment/TransformAttachmentBinding.d.ts.map +0 -1
  1805. package/src/engine/ecs/attachment/TransformAttachmentBinding.js +0 -51
  1806. package/src/engine/ecs/sockets/AttachmentSocket.d.ts +0 -32
  1807. package/src/engine/ecs/sockets/AttachmentSocket.d.ts.map +0 -1
  1808. package/src/engine/ecs/sockets/AttachmentSocket.js +0 -60
  1809. package/src/engine/ecs/sockets/AttachmentSocketType.d.ts +0 -6
  1810. package/src/engine/ecs/sockets/AttachmentSocketType.d.ts.map +0 -1
  1811. package/src/engine/ecs/sockets/AttachmentSocketType.js +0 -8
  1812. package/src/engine/ecs/sockets/AttachmentSockets.d.ts +0 -57
  1813. package/src/engine/ecs/sockets/AttachmentSockets.d.ts.map +0 -1
  1814. package/src/engine/ecs/sockets/AttachmentSockets.js +0 -130
  1815. package/src/engine/ecs/sockets/BoneAttachmentSocket.d.ts +0 -29
  1816. package/src/engine/ecs/sockets/BoneAttachmentSocket.d.ts.map +0 -1
  1817. package/src/engine/ecs/sockets/BoneAttachmentSocket.js +0 -56
  1818. package/src/engine/ecs/sockets/serialization/AttachmentSocketsAssetLoader.d.ts +0 -6
  1819. package/src/engine/ecs/sockets/serialization/AttachmentSocketsAssetLoader.d.ts.map +0 -1
  1820. package/src/engine/ecs/sockets/serialization/AttachmentSocketsAssetLoader.js +0 -19
  1821. package/src/shade/renderer/animation/_dump_resolve.d.mts +0 -2
  1822. package/src/shade/renderer/animation/_dump_resolve.d.mts.map +0 -1
  1823. package/src/shade/renderer/animation/_dump_resolve.mjs +0 -5
@@ -1,717 +1,717 @@
1
- # Gamepad API — proposal
2
-
3
- Response to the external review *"how convenient is the meep gamepad API? (UX)"*.
4
-
5
- > **Revision 2.** P1, P4, P5, P6 accepted as written. **P2 replaced** with a persistent `main` handle (better than
6
- > slot pre-population — see §4.2). **P3 replaced** with learned per-stick calibration and a `GamepadStick`
7
- > abstraction, grounded in the survey in Appendix A. Revision 1's fixed `axisNoiseFloor` is withdrawn.
8
- >
9
- > **Revision 3 — implemented.** All of P1–P6 have landed. §4 describes the design **as shipped**: names,
10
- > constants and signatures there match the code. §6 records what changed between the proposal and the
11
- > implementation, and why. §1, §2 and §5 are unchanged — they are the argument, not the design.
12
-
13
- ## 0. Summary
14
-
15
- The review grades a hardware abstraction layer by how little game code the consumer had to write. That is the
16
- wrong axis, and it leads to the wrong headline recommendation.
17
-
18
- Checked against the source:
19
-
20
- * one papercut (**sparse `buttons`**) does not reproduce through any supported access path — the guard the
21
- reviewer wrote is dead code;
22
- * one technical claim (**"full-diagonal magnitude ~0.99"**) is not producible by this code at all — there is no
23
- rescaling path in `GamepadHandle#update`;
24
- * one papercut (**"the `.d.ts` doesn't carry the frame"**) is about a file that does not exist — `engine/input`
25
- contains zero `.d.ts` files, and the frame *is* documented, on the enum that defines it;
26
- * the headline recommendation (**`pad.getMovement({radialDeadzone})`**) moves gameplay policy, a coordinate
27
- convention, an options bag, and a per-frame allocation into the driver.
28
-
29
- The review did, however, bump into a **real structural gap and misdiagnose it**: the gamepad is effectively
30
- invisible to the engine's input binding layer (`InputSystem` / `InputControllerSystem`). That is *why* the
31
- reviewer ended up hand-fusing raw device state — not because the device lacks convenience methods. Closing that
32
- gap removes the "boilerplate every consumer will duplicate" without adding a single convenience method to
33
- `GamepadHandle`.
34
-
35
- Two of the underlying observations are worth acting on, in a different shape than proposed.
36
-
37
- ### Dispositions
38
-
39
- | # | Review item | Disposition |
40
- |---|---|---|
41
- | 1 | `buttons` sparse → add `pad.isDown(i)` | **Reject the mechanism, accept the goal.** Claim doesn't reproduce; fix uniformity at the source (P1), don't add a third accessor. |
42
- | 2 | `pad.getMovement({radialDeadzone})` | **Reject.** Policy in the driver. Decomposed into two device-agnostic primitives instead (P4, P5). |
43
- | 3 | Per-axis dead-zone leaks diagonals; shortens full diagonal to ~0.99 | **Half accept.** The `~0.99` claim is fabricated. The genuine defect is different and worse: the gate is destructive, misnamed, and *guesses* a number that varies 6× across hardware. Replaced by learned calibration (P3). |
44
- | 4 | Frame undocumented "at the type level" | **Reject as stated, accept a sliver.** No `.d.ts` exists; frame is documented in `GamepadAxes.js`. Add a cross-reference on `axes`/`stickLeft`/`stickRight` (P6). |
45
- | 5 | No digital view of stick / analog view of D-pad; fusion is on the caller | **Accept the observation, keep fusion on the caller.** Ship the two primitives, not the fused result (P5). |
46
- | 6 | Analog-vs-digital speed is game policy | **Agree, no action.** |
47
- | — | *(missed)* Gamepad is unbindable through the ISM path layer | **New work.** The actual defect (P1 + P2). |
48
-
49
- ---
50
-
51
- ## 1. The layering premise the review is missing
52
-
53
- meep input devices are **state mirrors of hardware**. They report what the hardware reports, plus the minimum
54
- hygiene needed to make polled hardware observable (edge detection, stuck-input release on disconnect/stop). They
55
- contain no game policy. This is not an accident of the gamepad implementation — it is the shape of the whole
56
- package:
57
-
58
- * `KeyboardDevice` exposes `keys.w.is_down`. It does not expose `keyboard.getMovement()` from WASD.
59
- * Nobody asks it to, because the layer that owns that question already exists:
60
- [`KeyboardCameraController.js`](ecs/controllers/KeyboardCameraController.js) — a *controller* that reads device
61
- switches and produces a camera-frame pan vector.
62
-
63
- `pad.getMovement()` is a request to put `KeyboardCameraController`'s job inside `KeyboardDevice`. Five concrete
64
- reasons it does not belong there:
65
-
66
- 1. **The game frame is not a property of the stick.** The reviewer wants `+y = up`. Up in *what*? meep's world is
67
- 3D; "stick up" maps to a world direction that depends on the camera yaw, the projection, and whether the game
68
- is top-down or third-person. `KeyboardCameraController` resolves exactly this and it needs a
69
- `TopDownCameraController` to do it. A `Vector2` on a driver cannot know the answer, so any answer it bakes in
70
- is wrong for some consumer — who then must un-negate it. That is strictly worse than never negating.
71
-
72
- 2. **Dead-zoning is not one policy.** Radial-with-rescale is right for locomotion; per-axis is right for menu
73
- navigation; inner+outer with a response curve is right for aiming; square-gate normalization is right for
74
- pads that report a square gate. `{radialDeadzone}` picks one and demotes the rest to "off the golden path".
75
-
76
- 3. **`{radialDeadzone}` is an options bag.** The codebase's position is typed and verbose over magic-looking
77
- configuration. An options object on a per-frame hot call is the opposite of that, and it grows monotonically
78
- — the next request is `{radialDeadzone, outerDeadzone, curve, squareGate}`.
79
-
80
- 4. **It allocates.** `getMovement()` returning a fresh `Vector2` allocates once per consumer per frame. The
81
- engine's convention for vector-producing calls is `f(result, ...)` writing into a caller-owned target.
82
-
83
- 5. **It creates a dual-mode API.** With `getMovement()` present, `stickLeft` becomes "the raw one" and
84
- `getMovement()` "the good one" — two ways to ask the same question with a silent policy difference between
85
- them. Uniform control flow means picking the common case as the *only* case at each layer, and letting
86
- callers who want something else drop to the lower-level primitive.
87
-
88
- What the reviewer actually wants is real. It just belongs one layer up, and it should be *composable functions*,
89
- not a method on the driver.
90
-
91
- ---
92
-
93
- ## 2. Item-by-item
94
-
95
- *Line references in this section point at the code **as reviewed**, before any of P1–P6 landed. They are part of
96
- the argument about what the reviewer was looking at, not navigation aids for the current tree.*
97
-
98
- ### 2.1 "`buttons` is lazily populated and sparse, so I wrote a guard"
99
-
100
- **This does not reproduce.** Trace the code:
101
-
102
- * [`GamepadHandle.js:140`](devices/gamepad/GamepadHandle.js:140) — `update()` iterates **every** button in the
103
- native snapshot and calls `getButton(i)` on each. After one update, `buttons[0 … n-1]` are all populated.
104
- * [`GamepadDevice.js:130`](devices/GamepadDevice.js:130) — `#connect()` calls `pad.update(...)` **before**
105
- setting `pad.connected = true`.
106
- * [`GamepadDevice.js:86`](devices/GamepadDevice.js:86) — `main` only returns pads with `connected === true`.
107
-
108
- So every handle reachable through `gamepad.main` has already had its full button set materialised.
109
- `pad.buttons[GamepadButtons.a].is_down` is safe, and the `button !== undefined &&` half of the reviewer's guard
110
- never evaluates false in a connected-pad path. The `?.` in the existing `@example` is belt-and-braces, and reads
111
- as a warning it isn't.
112
-
113
- The one residual hole is real but different: a **non-standard mapping** pad reporting fewer than 17 buttons.
114
- `buttons[GamepadButtons.dpad_up]` is genuinely `undefined` there, because the button does not exist.
115
- `pad.isDown(15)` would return `false` for it — converting "this hardware has no D-pad" into "the D-pad is not
116
- pressed", silently, forever. That is a worse API than the honest `undefined`, and it is the reason to reject the
117
- proposed mechanism specifically.
118
-
119
- **The goal is still right.** Keyboard/gamepad symmetry should not have an asterisk, and `pads`/`buttons`
120
- sparseness has a second, much more serious consequence (§3). Fix it at the source: pre-populate (P1/P2), so
121
- there is nothing to guard and no third accessor.
122
-
123
- ### 2.2 `getMovement()` — rejected
124
-
125
- See §1. Replaced by P4 + P5.
126
-
127
- ### 2.3 "Per-axis dead-zoning leaks diagonal drift and shortens the full diagonal to ~0.99"
128
-
129
- **First half: correct.** A per-axis gate at 0.1 admits up to `sqrt(0.1² + 0.1²) ≈ 0.141` of resting magnitude,
130
- and its boundary is anisotropic — a stick pushed to `(0.09, 0.09)` reads dead, `(0.11, 0.0)` reads live.
131
-
132
- **Second half: not producible by this code.** The gate is
133
- [`GamepadHandle.js:169`](devices/gamepad/GamepadHandle.js:169):
134
-
135
- ```js
136
- if (Math.abs(value) < deadZone) {
137
- value = 0;
138
- }
139
- ```
140
-
141
- There is no rescale, no normalization, no magnitude clamp anywhere in `update()`. A full-deflection axis passes
142
- through byte-identical to what the hardware reported. If the reviewer measured 0.99 at full diagonal, that was
143
- their pad's gate, not ours. This matters because it is the tell that the section was reasoned about rather than
144
- read — and the actual defect sitting three lines away was missed.
145
-
146
- **The actual defect** is threefold and worse than "leaks diagonals":
147
-
148
- 1. **It is destructive.** The gated value is what gets *stored* in `axes`, so a consumer who wants radial
149
- dead-zoning cannot recover the raw signal — the information is gone before they see it. Policy applied in a
150
- mirror layer is not just misplaced, it is lossy.
151
- 2. **It is discontinuous.** `0.0999 → 0`, `0.1001 → 0.1001`. Input magnitude jumps by the full dead-zone width
152
- the instant the threshold is crossed. Any consumer mapping magnitude to speed gets a visible speed pop.
153
- 3. **It is named for a policy it should not be implementing.** `deadZone` at `0.1` reads as "the engine has
154
- decided your gameplay dead-zone", which is precisely how the reviewer read it.
155
-
156
- There is a fourth problem the review could not have seen without measuring hardware, and it is the one that
157
- kills the whole "pick a good constant" approach: **the right number varies by more than 6× across pads.** XInput
158
- ships 0.24 (left stick) and 0.265 (right); SDL treats ±0.098 as at-rest; hall-effect and TMR pads need 0.02–0.03
159
- and stay there for life; a worn potentiometer pad needs 0.2+ and gets worse over time. Any single constant is
160
- sized for the worst pad in the population, and every good pad pays for it with lost range.
161
-
162
- Fixed in P3 + P4: the device *measures* its own stick instead of guessing (learned center + noise radius), and
163
- gameplay dead-zoning becomes a continuous, rescaling, radial primitive applied by the consumer.
164
-
165
- ### 2.4 "Frame is undocumented at the type level — the `.d.ts` doesn't carry it"
166
-
167
- Two problems.
168
-
169
- * **The frame is documented**, in the file that owns the concept —
170
- [`GamepadAxes.js:5`](devices/gamepad/GamepadAxes.js:5): *"Negative values are left/up, positive values are
171
- right/down"*. The review cites having read it, then calls it undocumented.
172
- * **There is no `.d.ts`.** `find src/engine/input -name "*.d.ts"` returns nothing. Hand-written `.d.ts` are not
173
- maintained in this project; whatever the reviewer was reading was IDE inference or a stale tree. If a `.d.ts`
174
- for this package appears, deleting it is the fix.
175
-
176
- **Accepted sliver:** the frame note lives on the *enum*, and a consumer touching `stickLeft` may never open
177
- `GamepadAxes.js`. One-line cross-references on `axes`, `stickLeft`, `stickRight` (P6).
178
-
179
- ### 2.5 "No digital view of the stick, no analog view of the D-pad; fusion is on the caller"
180
-
181
- Correct, and **fusion stays on the caller** — which of stick and D-pad wins, whether they sum or the last-touched
182
- device latches, and whether a held D-pad suppresses stick drift are all game decisions with no defensible
183
- default. What *is* mechanical and duplicated is the conversion on each side, so ship both directions as
184
- primitives (P5) and leave the `max`/`or`/`latch` to the consumer.
185
-
186
- ### 2.6 "Analog-vs-digital speed is a caller concern"
187
-
188
- Agreed, explicitly. No action.
189
-
190
- ---
191
-
192
- ## 3. What the review missed: the gamepad is unbindable through the ISM
193
-
194
- The engine's action-mapping layer resolves bindings by *path* against the device tree —
195
- [`InputSystem.js:18`](ecs/systems/InputSystem.js:18) and
196
- [`InputControllerSystem.js:159`](ecs/systems/InputControllerSystem.js:159), both via
197
- [`resolvePath`](../../core/json/resolvePath.js), which **throws when a path cannot be resolved**.
198
-
199
- Keyboard works because `KeyboardDevice` pre-populates every key from `KeyCodes` in its constructor, so
200
- `keyboard/keys/w/down` resolves at load time, before anyone touches the hardware.
201
-
202
- The gamepad cannot do this:
203
-
204
- * `pads` is sparse and keyed on the hardware slot, so `gamepad/pads/0/...` throws until a pad connects;
205
- * `buttons` is sparse until first poll, so `gamepad/pads/0/buttons/0/down` throws until the pad has been read.
206
-
207
- Consequences: an `InputController` carrying a gamepad binding **cannot be linked until the player has already
208
- plugged in and pressed something**; whether a given binding works depends on which physical slot the pad landed
209
- in; and a serialized `InputBinding` referencing a gamepad path is a load-order landmine. (Unlink is safe once
210
- link succeeded — handles are retained across disconnect — but that is the only part of the lifecycle that is.)
211
-
212
- This — not the absence of `getMovement()` — is why the integration was "entirely on the caller". The blessed
213
- mapping mechanism was unavailable, so the reviewer polled raw state and fused by hand, then reported the
214
- symptoms of doing so as API friction. P1 and P2 close it as a side effect of fixing uniformity.
215
-
216
- *(Separately: axis bindings have no per-axis granularity — only the aggregate `on.axis`, forcing listeners to
217
- filter by index. Noted, not proposed. The string-path ISM is already the odd one out against the codebase's
218
- typed-over-magic position; make it work, don't grow it. Direct typed state access — `pad.buttons[GamepadButtons.a].is_down`
219
- — remains the blessed path.)*
220
-
221
- ---
222
-
223
- ## 4. Proposed work
224
-
225
- ### P1 — `GamepadHandle` pre-populates the standard mapping
226
-
227
- ```js
228
- // gamepad/GamepadStandardMapping.js
229
- export const GAMEPAD_STANDARD_BUTTON_COUNT = 17;
230
- export const GAMEPAD_STANDARD_AXIS_COUNT = 4;
231
- ```
232
-
233
- `GamepadHandle` materialises those in its constructor: 17 `InputDeviceSwitch`, 17 zeroed `buttonValues`, 4 zeroed
234
- `axes`. `getButton` stays as-is and keeps growing the array for hardware exposing more than 17 buttons.
235
-
236
- Effect: `pad.buttons[GamepadButtons.dpad_up].is_down` is total. No guard, no `?.`, no `isDown()`. Exactly the
237
- keyboard's contract.
238
-
239
- **Tradeoff, stated explicitly:** on a non-standard pad with 6 buttons, `buttons[12]` now reads "up" rather than
240
- "absent". Absence is signalled by `pad.mapping !== "standard"`, which is where the Gamepad API puts it, not by
241
- holes in an array. Uniform beats expressive here — a hole is not a usable absence signal anyway, since the
242
- consumer sees a hole for "no such button" and (previously) also for "not polled yet".
243
-
244
- Cost: 17 small objects per handle, allocated once.
245
-
246
- ### P2 — `main` becomes a persistent handle bound to the active pad
247
-
248
- *(Replaces revision 1's slot pre-population. This is the better design: it gives the binding layer a stable path
249
- without pretending four pads exist, and it matches what a single-player consumer actually means by "the
250
- gamepad".)*
251
-
252
- `GamepadDevice.main` becomes a `GamepadHandle` constructed with the device, living **outside** `pads[]`, starting
253
- disconnected:
254
-
255
- * it binds to the first pad that connects;
256
- * when its bound pad disconnects, it rebinds to the **oldest still-connected** pad (connection order);
257
- * when nothing is connected, it stays present and `connected === false`, with all switches released.
258
-
259
- Mechanically `main` is a real handle fed the *same* native snapshot as its bound pad during `poll()` — not a
260
- proxy or a getter chain. That matters: `gamepad/main/buttons/0/down` is a stable `Signal` **object** that
261
- resolves at engine construction and never changes identity, which is exactly what `resolvePath` needs (§3).
262
-
263
- ```js
264
- // GamepadDevice
265
- #connection_sequence = 0;
266
-
267
- /**
268
- * The active gamepad. Always present; check {@link GamepadHandle#connected}.
269
- * Mirrors the first connected pad, falling back to the oldest remaining one when that pad is lost.
270
- * Not a member of {@link pads}.
271
- */
272
- main = new GamepadHandle();
273
- ```
274
-
275
- Notes on the implementation:
276
-
277
- * **Do not** forward `main.on.*` into the device-level `on.*` signals. The pad it mirrors already forwards, so
278
- wiring both would double-fire every press for every listener.
279
- * On unbind, `main.reset()` — releases everything, so a yank-the-cable never leaves input stuck. The next
280
- `update()` from the new source re-presses whatever is genuinely held. Free correctness from the existing edge
281
- detection.
282
- * `main.index` / `.id` / `.mapping` follow the mirrored pad, because `update()` already copies them. Useful:
283
- a settings screen can name the active pad without touching `pads`.
284
- * Connection order needs one counter — stamp `pad.connectionIndex = this.#connectionSequence++` inside
285
- `#connect`, pick the smallest among connected on rebind.
286
- * Binding, rebinding and refreshing all happen in one place at the end of `poll()`. Splitting them across
287
- `#connect`/`#disconnect` leaves `main` a frame stale on a swap — see §6.3.
288
-
289
- Cost: one extra handle update per poll (17 buttons + 4 axes) — noise.
290
-
291
- **Breaking, and an improvement:** `main` was `GamepadHandle | undefined`. It is now always a handle. Existing
292
- `if (pad !== undefined)` guards still compile and simply never trigger; the check becomes `main.connected`. This
293
- removes the only sentinel in the gamepad API, which is the direction the codebase wants anyway.
294
-
295
- **Deliberately unresolved:** per-slot paths (`gamepad/pads/1/...`) still don't resolve, because `pads` keeps its
296
- honest sparse hardware-slot semantics. Local multiplayer binds through the device-level `on.down`, which already
297
- dispatches `(buttonIndex, pad)`, or holds handles from the `connected` signal. If per-player path binding is ever
298
- wanted, the shape is an ordered player-assignment list of which `main` is element 0 — this design doesn't
299
- preclude it, and I'd rather add it when there's a consumer than guess the assignment policy now.
300
-
301
- ### P3 — Learned per-stick calibration, and a `GamepadStick` abstraction
302
-
303
- *(Replaces revision 1's fixed `axisNoiseFloor`. Appendix A is the supporting survey.)*
304
-
305
- Three findings drive this:
306
-
307
- 1. **In a browser there is no factory calibration, so learning is the only calibration available.** Native stacks
308
- can read hardware calibration — a Switch pad stores per-axis min/center/max in SPI flash, XInput and SDL apply
309
- vendor-tuned constants. The Gamepad API exposes none of it: normalized floats and nothing else. Every native
310
- stack calibrates; we're the only ones who'd be guessing.
311
- 2. **The dominant error is a center offset, not symmetric noise.** This is what "stick drift" *is*, and it is why
312
- every consumer calibration tool works by sampling the resting position and subtracting it. A fixed dead zone
313
- responds to an offset center by widening the dead area — asymmetrically, so you lose range on one side and
314
- still drift on the other. Learning the center fixes the actual defect; a dead zone only hides it.
315
- 3. **The right radius varies 6×+ across the installed base** (§2.3). Measuring is the only way to give a TMR pad
316
- its 0.02 and a worn pad its 0.15 from one code path.
317
-
318
- **Layering — why this doesn't contradict §1.** Calibration is *hardware characterization*: learning that this
319
- stick rests at `(0.03, -0.01)` with `0.018` of dispersion makes the mirror **more accurate**, which is squarely
320
- the device layer's job. Gameplay dead zone, response curve, and frame conversion remain policy and stay with the
321
- consumer via `apply_radial_dead_zone`. The line is: *the device removes what the hardware got wrong; the game
322
- decides what to do with what's left.* Getting this backwards is a known failure mode — SDL applying its own dead
323
- zone under a game that applies another is a long-standing complaint, and the result is exactly the doubled dead
324
- zone with all precision gone.
325
-
326
- #### The abstraction
327
-
328
- ```js
329
- export class GamepadStick {
330
- /** Indices into {@link GamepadHandle#axes} that this stick reads. */
331
- axisX = 0;
332
- axisY = 1;
333
-
334
- /** Hardware value, exactly as reported. */
335
- raw = new Vector2();
336
-
337
- /** Calibrated value: center-corrected and noise-gated. This is what gameplay should read. */
338
- value = new Vector2();
339
-
340
- /** @type {GamepadStickCalibration} */
341
- calibration = new GamepadStickCalibration();
342
-
343
- on = {
344
- /** Fires when {@link value} changes. A calibrated stick at rest is silent. */
345
- changed: new Signal()
346
- };
347
-
348
- /** Refresh from raw axis values, feeding the calibrator on the way through. */
349
- update(axes) {}
350
-
351
- /** Return to neutral, keeping the calibration — the hardware has not changed. */
352
- reset() {}
353
-
354
- /** Discard what was learned. For when the handle starts representing *different* hardware. */
355
- forgetCalibration() {}
356
- }
357
-
358
- export class GamepadStickCalibration {
359
- /** Learned rest position. */
360
- center = new Vector2();
361
-
362
- /** Learned dispersion around {@link center}; magnitudes inside this read as 0. */
363
- noiseRadius = 0;
364
-
365
- /** When true, learning is suspended and the current values are used as-is. */
366
- locked = false;
367
- }
368
- ```
369
-
370
- `stickLeft` / `stickRight` become `GamepadStick` rather than `Vector2`. **Breaking:** `pad.stickLeft.x` →
371
- `pad.stickLeft.value.x`. Changelog line; this is public surface.
372
-
373
- **Opt-out without a mode flag.** You asked for an opt-out, and the obvious form of it — a boolean that switches
374
- between two code paths — is the dual-mode shape we rejected for `getMovement()`. It isn't needed here: the
375
- transform is *always* applied, and an identity calibration (`center = (0,0)`, `noiseRadius = 0`, `locked =
376
- true`) is a bit-exact passthrough. One code path, and "no filtering" is just a particular calibration. The same
377
- mechanism covers a settings-screen manual dead zone and a saved per-controller profile — `GamepadStickCalibration`
378
- gets `to/fromBinaryBuffer`, keyed on `pad.id`.
379
-
380
- **Calibration belongs to hardware, not to a handle.** A handle outlives the pad it represents: slots get
381
- re-occupied, and `main` moves between pads. Carrying an estimate across that would apply one pad's correction to
382
- another — and because the calibrator only ever moves its estimate *closer* to the origin (below), a new pad
383
- resting further out could never adopt its own rest position at all. So `forgetCalibration()` is called on a fresh
384
- connection and when `main` binds a new source. It is a no-op on a locked calibration: a supplied profile is not
385
- ours to discard.
386
-
387
- #### The learning algorithm
388
-
389
- **Phase A — rest estimation.** Runs continuously and unattended; there is no separate "on connect" mode.
390
-
391
- * Collect `REST_SAMPLE_COUNT = 60` raw samples (≈1 s at rAF) as a fixed, non-overlapping window.
392
- * **Reject the whole window** if any sample magnitude exceeds `REST_CANDIDATE_MAX = 0.08`, or if per-axis spread
393
- (max − min) exceeds `REST_SPREAD_MAX = 0.015`. Resting and holding cannot be told apart inside one window —
394
- both are steady — so the bounds are biased hard toward rejection, on the asymmetry that failing to calibrate
395
- leaves an exact passthrough while calibrating wrongly injects phantom deflection. The spread bound is the
396
- primary defence, and it exploits a physical difference: a held stick carries hand tremor, a settled one
397
- carries only sensor noise, and the two differ by roughly an order of magnitude. (§6.1 and §6.4 record how
398
- these numbers were arrived at — the first version of this section got the reasoning wrong.)
399
- * **Center = per-axis median** of the window. Median, not mean: one spike shifts a 60-sample mean by 1.7% of its
400
- amplitude, and shifts the median by nothing.
401
- * **`noiseRadius` = median(d) + `NOISE_RADIUS_DEVIATIONS`·MAD(d)** where `d` are the sample distances from the
402
- learned center and MAD is the median absolute deviation. MAD is the standard robust dispersion estimator — a
403
- high quantile computed from 60 samples is dominated by its own tail, which is precisely the outliers we're
404
- trying not to fit. No clamp: the spread bound already constrains the result structurally, to
405
- `NOISE_RADIUS_MAX = REST_SPREAD_MAX · √2 · (1 + NOISE_RADIUS_DEVIATIONS)` ≈ 0.106.
406
- * **Adopt only if closer in.** A window is written to the calibration only when its center is nearer the origin
407
- than the accepted one. This is what makes a wrong estimate self-correcting: a stick that was being held when
408
- sampling started is superseded the moment it is released, with no separate recovery path. The trade is that
409
- drift *growing* within a session is not followed — a wear process measured in weeks, and calibration restarts
410
- whenever the pad reconnects.
411
- * **Recovery takes up to two windows.** Windows are fixed and non-overlapping, so the one straddling a release
412
- or a rebind still holds pre-transition samples and is rejected on spread; only the window after it is clean.
413
- This affects how quickly a correct estimate is found, never whether it is correct.
414
-
415
- **Phase B — range estimation.** Proposed as **optional and off by default.** Pads differ in gate shape (square
416
- gates reach ~1.41 at full diagonal, circular ones ~1.0), so a learned per-direction max would let us normalize to
417
- a unit circle. But a running max is a self-poisoning estimator — one spike raises the ceiling permanently and
418
- silently compresses everything afterwards. If we do it: require K consecutive samples above the current maximum
419
- before raising it, clamp to `[0.8, 1.5]`, and decay slowly. I'd rather ship Phase A, which is high-value and
420
- low-risk, and leave this behind a flag until a real pad demands it.
421
-
422
- **Not proposed, but worth knowing about:** dead-zoning only addresses error *near center*. Jitter while the stick
423
- is **deflected** is a separate problem, and the state of the art for it is the 1€ filter (Casiez et al., CHI
424
- 2012) — a speed-adaptive low-pass with two parameters: low cutoff at low speed kills jitter, rising cutoff at
425
- speed kills lag. It's ~30 lines and would slot in as an optional stage on `GamepadStick` between `raw` and
426
- `value`. I'd hold it until someone reports jitter that calibration doesn't fix, but the abstraction should leave
427
- room for it — which it does, since the stage lives inside the stick and consumers only see `value`.
428
-
429
- #### Consequences for `axes` and `deadZone`
430
-
431
- * **`pad.axes` becomes raw** — a true mirror, no destructive gating (fixes §2.3's real defect). Non-stick axes on
432
- exotic mappings are unaffected by stick calibration, as they should be.
433
- * **`GamepadDevice.deadZone` is removed**, replaced by a `@deprecated` accessor whose setter writes
434
- `noiseRadius` into every stick — existing and subsequently connected — and locks them. That is a faithful
435
- migration: the old behaviour *was* a manually-specified fixed gate, and a locked calibration is exactly that.
436
- Locking also means such a value survives reconnection, unlike a learned one.
437
- * **`on.axis` now fires at hardware sample rate on a drifting pad**, where the gate used to silence it. Decided
438
- in favour of leaving it: the raw layer is honest, and the recommended path (`stick.on.changed`) is silent at
439
- rest because the calibrated value genuinely doesn't change. The rejected alternative was gating `on.axis` at
440
- an epsilon, which is the destructive gate creeping back in under a smaller number. This is a behaviour change
441
- for anything bound to `on.axis`.
442
-
443
- ### P4 — `apply_radial_dead_zone(result, x, y, inner, outer)`
444
-
445
- `engine/input/analog/apply_radial_dead_zone.js` — device-agnostic, no allocation, no options bag, continuous:
446
-
447
- ```js
448
- export function apply_radial_dead_zone(result, x, y, inner, outer) {
449
- const m = Math.sqrt(x * x + y * y);
450
-
451
- if (m <= inner) {
452
- result.set(0, 0);
453
- return;
454
- }
455
-
456
- const scale = Math.min((m - inner) / (outer - inner), 1) / m;
457
-
458
- result.set(x * scale, y * scale);
459
- }
460
- ```
461
-
462
- Isotropic boundary (no diagonal leak), continuous at `inner` (no speed pop), saturates to unit magnitude at
463
- `outer` (no `>1` from square-gate pads). Applies equally to a stick, a touch nub, or a mouse delta.
464
-
465
- ### P5 — `gamepad_write_dpad_vector(result, pad)`
466
-
467
- `engine/input/devices/gamepad/gamepad_write_dpad_vector.js` — the D-pad as an analog vector, **in hardware
468
- frame** (`+y` down), so it composes with `stickLeft` without the consumer tracking which of the two is flipped:
469
-
470
- ```js
471
- export function gamepad_write_dpad_vector(result, pad) {
472
- const buttons = pad.buttons;
473
-
474
- const up = buttons[GamepadButtons.dpad_up].is_down;
475
- const down = buttons[GamepadButtons.dpad_down].is_down;
476
- const left = buttons[GamepadButtons.dpad_left].is_down;
477
- const right = buttons[GamepadButtons.dpad_right].is_down;
478
-
479
- result.set(
480
- (right ? 1 : 0) - (left ? 1 : 0),
481
- (down ? 1 : 0) - (up ? 1 : 0)
482
- );
483
- }
484
- ```
485
-
486
- (Total only because of P1.) The digital view of the stick is the caller's threshold on
487
- `apply_radial_dead_zone`'s output, which is a comparison, not an API.
488
-
489
- ### P6 — Documentation
490
-
491
- * Cross-reference the frame on `axes`, `stickLeft`, `stickRight`: *"Hardware frame: `+x` right, `+y` down. See
492
- {@link GamepadAxes}. Game-frame conversion belongs in the controller — see `KeyboardCameraController`."*
493
- * Drop the misleading `?.` from the `buttons` `@example` once P1 lands.
494
- * Worked gamepad-locomotion example in the `GamepadDevice` class doc (below), so the next consumer copies the
495
- intended composition instead of inventing one.
496
-
497
- ### Tests
498
-
499
- `GamepadDevice.spec.js` currently has 7 tests and does not cover any of this. Add:
500
-
501
- * P1: all standard buttons defined on a fresh handle, before any poll; `getButton(20)` still grows.
502
- * P2: `main` exists and is disconnected on a fresh device; binds to the first pad that connects; mirrors its
503
- buttons and axes; on disconnect of the bound pad falls back to the oldest remaining connected pad; goes
504
- disconnected with everything released when the last pad is lost; a button held at disconnect is released
505
- exactly once; a press on the bound pad fires device-level `on.down` exactly once (double-forwarding guard).
506
- * §3 regression: `resolvePath(devices, "gamepad/main/buttons/0/down")` resolves to a Signal on a fresh engine
507
- with no hardware, is the *same object* after connect→disconnect→reconnect, and dispatches across a pad swap.
508
- * P3 calibration: a window centered at `(0.03, -0.01)` with small noise yields a center within tolerance and a
509
- non-zero, structurally bounded `noiseRadius`; a window containing a held stick is **rejected**; a window with
510
- one 0.9 spike is rejected; a swept window is rejected; a window with one mild outlier still yields a
511
- median-based center unmoved beyond tolerance; identity + `locked` is a bit-exact passthrough of `raw` to
512
- `value`; calibrated `value` is exactly `(0,0)` for samples inside the learned radius; `axes` are byte-identical
513
- to source at all deflections (mirror contract); the deprecated `deadZone` setter reaches pads connected later.
514
- * P3 adversarial (§6.4): connecting while the stick is fully deflected, diagonally deflected, or resting at an
515
- axis extreme never calibrates; connecting while it is *slightly* deflected stays bounded below a usable
516
- gameplay dead zone and recovers; `main` and a reused slot handle each forget a previous pad's calibration, and
517
- can then learn a rest position further out than it; a locked calibration survives reconnection.
518
- * P4: rest → `(0,0)`; just inside `inner` → `(0,0)`; just outside `inner` → magnitude ≈ 0 (continuity, no jump);
519
- at `outer` → magnitude 1; beyond `outer` → magnitude 1; full diagonal on a square-gate pad → magnitude 1, not
520
- 1.41; isotropy — `(0.14, 0.14)` and `(0.198, 0)` land within epsilon of the same magnitude.
521
- * P5: each direction; opposing pair cancels to 0; diagonal → `(1, 1)` pre-normalization.
522
-
523
- All black-box, against real `GamepadHandle` / `InputDeviceSwitch` / a plain object standing in for the native
524
- `Gamepad` snapshot (a platform type we cannot construct in node — the only fake here).
525
-
526
- ### After this proposal, the reviewer's integration reads:
527
-
528
- ```js
529
- const pad = engine.devices.gamepad.main; // always present
530
-
531
- if (pad.connected) {
532
- const left = pad.stickLeft.value; // already center-corrected for this specific stick
533
-
534
- gamepad_write_dpad_vector(dpad, pad);
535
- apply_radial_dead_zone(stick, left.x, left.y, 0.15, 0.95);
536
-
537
- // fusion + frame: game policy, and this is the whole of it
538
- const x = stick.x !== 0 || stick.y !== 0 ? stick.x : dpad.x;
539
- const y = stick.x !== 0 || stick.y !== 0 ? stick.y : dpad.y;
540
-
541
- move.set(x, -y); // this game is y-up; the negation is stated once, here, on purpose
542
- }
543
- ```
544
-
545
- Shorter than what the review reported writing, with zero allocation per frame, no `undefined` guard, every policy
546
- decision visible at the call site instead of hidden behind an options bag — and a gameplay dead zone the game
547
- picks for feel rather than to paper over hardware, because the hardware error is already gone by this line.
548
-
549
- Hot-swapping pads mid-session requires no code here at all: `main` rebinds and the loop doesn't notice.
550
-
551
- ---
552
-
553
- ## 5. Explicitly not doing
554
-
555
- * **`pad.isDown(i)`** — a third way to ask a question `buttons[i].is_down` and `getButton(i).is_down` already
556
- answer, which silently converts absent hardware into an unpressed button. P1 removes the need.
557
- * **`pad.getMovement()`** — §1.
558
- * **Frame negation anywhere in the device layer** — the device reports hardware frame, full stop. One negation,
559
- in the controller, where the camera is known.
560
- * **Growing the ISM string-path layer with gamepad-specific concepts** — make the existing paths resolve (P1/P2);
561
- do not build more magic strings.
562
- * **Per-axis signals on `GamepadHandle`** — noted as a real granularity gap in §3. Partly answered by
563
- `GamepadStick.on.changed`; the remaining gap is non-stick axes on exotic mappings. Revisit if a consumer needs
564
- it.
565
- * **A calibration wizard** ("rotate the stick in circles") — the standard consumer-tool flow, and the only way to
566
- learn true per-direction range. Out of scope: Phase A needs no UI, and Phase B is deferred. If a settings
567
- screen wants one later, `GamepadStickCalibration` is already the serializable thing it would write to.
568
- * **1€ filter** — right tool for deflected-stick jitter, wrong time. Left room for it inside `GamepadStick`; see
569
- P3.
570
-
571
- ---
572
-
573
- ## 6. What changed during implementation
574
-
575
- Two corrections, both found by tests that failed against the design as written above.
576
-
577
- ### 6.1 The rest-window guard did not do what §4 claimed
578
-
579
- *(§4 now describes the shipped bounds; what it said before this correction is quoted here.)*
580
-
581
- Revision 2's P3 asserted that `REST_CANDIDATE_MAX = 0.30` plus a spread check together catch "the player was holding the
582
- stick", *"because a stick held steady at 0.25 passes the first test alone"*. That reasoning is wrong: a stick
583
- held **steady** at 0.25 also has near-zero spread, so it passes the second test too. The first implementation
584
- duly learned a held position as the rest position, producing a phantom −0.23 deflection once the player let go.
585
- The test asserting it wouldn't is what caught it.
586
-
587
- There is no way to tell resting from holding inside a single window — both are steady. So the bounds are now
588
- biased hard toward rejection, on the reasoning that **failing to calibrate is harmless** (identity is an exact
589
- passthrough) **while calibrating wrongly is actively harmful**:
590
-
591
- * `REST_CANDIDATE_MAX` 0.30 → **0.15**. Drift worse than this is a pad heading for replacement, and past some
592
- deflection a steady reading is likelier to be a hand than a stick. *(Tightened again to 0.08 in §6.4, on a
593
- better argument than this one.)*
594
- * `REST_SPREAD_MAX` 0.05 → **0.015**. This is now the primary defence, and it exploits a physical difference the
595
- original design ignored: a held stick carries the hand's tremor, a settled one carries only sensor noise, and
596
- the two differ by roughly an order of magnitude. The cost is that an unusually noisy stick never calibrates,
597
- which is the safe failure.
598
-
599
- The residual hole is documented rather than papered over: a player parked at a small steady deflection for a
600
- full second can still poison the estimate until they release, bounded by `REST_CANDIDATE_MAX`, and the
601
- minimum-magnitude rule then supersedes it. Both the poisoning and the recovery are tested.
602
-
603
- ### 6.2 `NOISE_RADIUS_MAX` became unreachable, so the clamp went
604
-
605
- With the spread bound at 0.015, a window that passes it cannot produce a radius anywhere near 0.20 — the result
606
- is structurally bounded at `REST_SPREAD_MAX · √2 · (1 + NOISE_RADIUS_DEVIATIONS)` ≈ 0.106. The clamp was a guard
607
- for a condition that can no longer occur, so it was removed and the constant kept as a derived, documented
608
- bound. The test asserts the structural property instead of the removed clamp.
609
-
610
- ### 6.3 Two smaller notes
611
-
612
- * **`main` rebinding was one frame stale.** Binding logic initially lived in `#connect`/`#disconnect`, so a
613
- successor picked up mid-poll wasn't fed hardware until the *next* poll — `main.connected` read true while
614
- `main.id` still named the unplugged pad. Binding, rebinding and refreshing now all happen in one place at the
615
- end of `poll()`, which resolves a swap within a single poll and, incidentally, removed a pre-existing
616
- duplicate `pad.update()` per poll.
617
- * **The deprecated `deadZone` setter is plumbed properly**, not just applied to pads that happen to exist when
618
- it is written — new handles pick the override up in `#obtainHandle`. A half-working compatibility shim would
619
- have been worse than none.
620
-
621
- ### 6.4 Adversarial pass: connecting with a deflected stick
622
-
623
- Two scenarios were put to the implementation afterwards. One was safe, one was not, and chasing them turned up a
624
- third defect neither scenario named.
625
-
626
- **Connecting with a *fully* deflected stick — safe by construction.** Every sample is far outside
627
- `REST_CANDIDATE_MAX`, so every window is rejected for as long as the stick is held, and release leaves an exact
628
- passthrough rather than a phantom pull the other way. Same for a diagonal hold, and for the non-standard
629
- mappings that report a released trigger as an axis resting at −1: those never calibrate, which is the correct
630
- outcome, not a silent wrong one.
631
-
632
- **Connecting with a *partially* deflected stick — this was the real gap.** A steady hold inside the plausibility
633
- bound is genuinely indistinguishable from drift in a single window, so it was adopted as the rest position and
634
- produced a phantom deflection of the same size on release. It self-corrected, but "bounded at 0.15 and recovers
635
- in about two seconds" is a weaker answer than it needs to be, because the bound was set too generously:
636
-
637
- * `REST_CANDIDATE_MAX` 0.15 → **0.08**. The reasoning that fixes it is an asymmetry the earlier revisions
638
- missed. Auto-calibration earns its keep on *small* drift — the kind that sits under a gameplay dead zone and
639
- still causes slow creep. Drift larger than that is already absorbed by any sane gameplay dead zone (XInput
640
- ships 0.24), so declining to calibrate it costs almost nothing. Poisoning, on the other hand, costs a phantom
641
- deflection. Tightening trades away the case that needs us least, and in exchange the worst wrong estimate now
642
- lands **below any usable gameplay dead zone** — it never reaches the game at all before being superseded.
643
- That is asserted directly rather than implied.
644
-
645
- **The defect neither scenario named: a handle outliving the hardware it learned.** `main` rebinding to a
646
- different pad, and a slot handle being reused when a different pad is plugged into that slot, both carried the
647
- previous pad's calibration onto new hardware. Worse than a stale offset: because the calibrator only ever moves
648
- its estimate *closer* to the origin, a new pad whose rest position sat further out than the old one's could
649
- **never** have its own rest position adopted — the wrong calibration would have been permanent. Both paths now
650
- call `forgetCalibration()`, which is a no-op on a locked calibration, since a supplied profile is not ours to
651
- discard. Regression tests cover both, including a new pad resting further out and the other way.
652
-
653
- **Recovery takes up to two windows, not one.** Windows are fixed and non-overlapping, so the one straddling a
654
- release or a rebind still contains pre-transition samples and is rejected on spread; only the window after it is
655
- clean. This affects how fast a correct estimate is found, never whether it is correct. Stated here because the
656
- first tests written assumed one window and were wrong about the contract, not about the code.
657
-
658
- **Tests: 101 passing across 13 suites in `engine/input`.** New: `GamepadHandle.spec.js`,
659
- `GamepadStick.spec.js`, `GamepadStickCalibration.spec.js`, `gamepad_write_dpad_vector.spec.js`,
660
- `apply_radial_dead_zone.spec.js`, plus P2/P3 coverage and the `resolvePath` regression in
661
- `GamepadDevice.spec.js`.
662
-
663
- ---
664
-
665
- ## Appendix A — What the state of the art actually does
666
-
667
- Surveyed while designing P3. Three things are worth having on the record.
668
-
669
- **Nobody good uses a single hard-coded constant, and everybody who ships one ships a different number.**
670
-
671
- | Stack | At-rest threshold | Notes |
672
- |---|---|---|
673
- | XInput (Microsoft) | 7849/32767 ≈ **0.240** left, 8689/32767 ≈ **0.265** right | Vendor-recommended constants, per-stick — even Microsoft doesn't use one number for both sticks on the same pad |
674
- | SDL | ±3200/32767 ≈ **0.098** | Documented as "consider at rest"; SDL notes advanced UI should let users set or **autodetect** it |
675
- | Hall-effect / TMR pads (current gen) | **0.02–0.03** | Manufacturer guidance; stable for the life of the pad rather than degrading |
676
- | Worn potentiometer pads | **0.2+**, rising | The population XInput's 0.24 is sized for |
677
- | meep today | 0.1, per-axis | Between the two, correct for neither |
678
-
679
- That spread is the argument: a constant is a bet on the worst pad you expect to see, and the modern population is
680
- bimodal — drift-free magnetic sticks alongside worn potentiometer ones. Measuring costs 60 samples once.
681
-
682
- **Native stacks calibrate against hardware data we cannot get.** A Switch pad carries factory calibration in SPI
683
- flash (per-axis min/center/max, at `0x8010`); desktop stacks apply vendor tables. The Gamepad API gives us
684
- normalized floats and `id`, and nothing else. So the choice isn't "learn vs. read the real values" — it's "learn
685
- vs. guess".
686
-
687
- **Consumer calibration tools all do the same two things**, which is a decent signal about what actually works:
688
- sample the resting position over a short window (≈60 frames is the common figure) and subtract it; then optionally
689
- sweep for range. Firmware-level fixes write the corrected center point to controller NVS. This is Phase A and
690
- Phase B, and it's why P3 puts center estimation first — offset is the defect people call "drift", and a dead zone
691
- never fixes an offset, it only hides half of one.
692
-
693
- **Dead-zone geometry.** Josh Sutphin's taxonomy (axial / radial / scaled radial / sloped-scaled-axial / hybrid)
694
- is still the reference, with a good annotated implementation set at
695
- [Minimuino/thumbstick-deadzones](https://github.com/Minimuino/thumbstick-deadzones). Scaled radial with inner and
696
- outer bounds is P4, and it's the right default: isotropic boundary, full `[0,1]` output range preserved,
697
- continuous at the inner edge. Axial has legitimate uses (menus, anything wanting pure horizontal/vertical), which
698
- is another reason it belongs in a consumer-side function rather than baked into the device.
699
-
700
- **Jitter vs. drift are different problems.** Dead zones address error near center. Error while deflected is
701
- jitter, and the state of the art is the 1€ filter (Casiez, Roussel & Vogel, CHI 2012) — a first-order low-pass
702
- whose cutoff rises with signal speed, so it stabilizes at rest and doesn't lag under motion. Two parameters,
703
- trivial to implement, measurably less lag than fixed low-pass at equal jitter reduction. Deferred, not dismissed.
704
-
705
- Sources:
706
- [XInput deadzone constants](https://learn.microsoft.com/en-us/previous-versions/windows/xna/bb197229(v=xnagamestudio.42)) ·
707
- [SDL3 gamepad axis](https://wiki.libsdl.org/SDL3/SDL_GamepadAxis) ·
708
- [SDL: stacked deadzones issue](https://github.com/libsdl-org/SDL/issues/3781) ·
709
- [Doing Thumbstick Dead Zones Right — Josh Sutphin](https://joshsutphin.com/blog/doing-thumbstick-dead-zones-right.html) ·
710
- [Understanding thumbstick deadzones](https://minimuino.github.io/thumbstick-deadzones/) ·
711
- [Switch SPI calibration layout](https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/spi_flash_notes.md) ·
712
- [Analog stick calibration data — Hand Held Legend](https://wiki.handheldlegend.com/analog-stick-calibration-data) ·
713
- [Hall-effect vs traditional thumbsticks](https://www.turtlebeach.com/blog/hall-effect-thumbsticks-vs-traditional-joysticks) ·
714
- [TMR vs hall-effect](https://www.easysmx.com/blogs/easysmx-controllers/tmr-vs-hall-effect-joysticks) ·
715
- [Controller calibration & drift — SCUF](https://www.scufgaming.com/us/en/gaming/products/scuf-products/controller-calibration-dead-zones-and-stick-drift-explained/) ·
716
- [1€ filter (CHI 2012, PDF)](https://direction.bordeaux.inria.fr/~roussel/publications/2012-CHI-one-euro-filter.pdf) ·
717
- [1€ filter reference implementations](https://github.com/casiez/OneEuroFilter)
1
+ # Gamepad API — proposal
2
+
3
+ Response to the external review *"how convenient is the meep gamepad API? (UX)"*.
4
+
5
+ > **Revision 2.** P1, P4, P5, P6 accepted as written. **P2 replaced** with a persistent `main` handle (better than
6
+ > slot pre-population — see §4.2). **P3 replaced** with learned per-stick calibration and a `GamepadStick`
7
+ > abstraction, grounded in the survey in Appendix A. Revision 1's fixed `axisNoiseFloor` is withdrawn.
8
+ >
9
+ > **Revision 3 — implemented.** All of P1–P6 have landed. §4 describes the design **as shipped**: names,
10
+ > constants and signatures there match the code. §6 records what changed between the proposal and the
11
+ > implementation, and why. §1, §2 and §5 are unchanged — they are the argument, not the design.
12
+
13
+ ## 0. Summary
14
+
15
+ The review grades a hardware abstraction layer by how little game code the consumer had to write. That is the
16
+ wrong axis, and it leads to the wrong headline recommendation.
17
+
18
+ Checked against the source:
19
+
20
+ * one papercut (**sparse `buttons`**) does not reproduce through any supported access path — the guard the
21
+ reviewer wrote is dead code;
22
+ * one technical claim (**"full-diagonal magnitude ~0.99"**) is not producible by this code at all — there is no
23
+ rescaling path in `GamepadHandle#update`;
24
+ * one papercut (**"the `.d.ts` doesn't carry the frame"**) is about a file that does not exist — `engine/input`
25
+ contains zero `.d.ts` files, and the frame *is* documented, on the enum that defines it;
26
+ * the headline recommendation (**`pad.getMovement({radialDeadzone})`**) moves gameplay policy, a coordinate
27
+ convention, an options bag, and a per-frame allocation into the driver.
28
+
29
+ The review did, however, bump into a **real structural gap and misdiagnose it**: the gamepad is effectively
30
+ invisible to the engine's input binding layer (`InputSystem` / `InputControllerSystem`). That is *why* the
31
+ reviewer ended up hand-fusing raw device state — not because the device lacks convenience methods. Closing that
32
+ gap removes the "boilerplate every consumer will duplicate" without adding a single convenience method to
33
+ `GamepadHandle`.
34
+
35
+ Two of the underlying observations are worth acting on, in a different shape than proposed.
36
+
37
+ ### Dispositions
38
+
39
+ | # | Review item | Disposition |
40
+ |---|---|---|
41
+ | 1 | `buttons` sparse → add `pad.isDown(i)` | **Reject the mechanism, accept the goal.** Claim doesn't reproduce; fix uniformity at the source (P1), don't add a third accessor. |
42
+ | 2 | `pad.getMovement({radialDeadzone})` | **Reject.** Policy in the driver. Decomposed into two device-agnostic primitives instead (P4, P5). |
43
+ | 3 | Per-axis dead-zone leaks diagonals; shortens full diagonal to ~0.99 | **Half accept.** The `~0.99` claim is fabricated. The genuine defect is different and worse: the gate is destructive, misnamed, and *guesses* a number that varies 6× across hardware. Replaced by learned calibration (P3). |
44
+ | 4 | Frame undocumented "at the type level" | **Reject as stated, accept a sliver.** No `.d.ts` exists; frame is documented in `GamepadAxes.js`. Add a cross-reference on `axes`/`stickLeft`/`stickRight` (P6). |
45
+ | 5 | No digital view of stick / analog view of D-pad; fusion is on the caller | **Accept the observation, keep fusion on the caller.** Ship the two primitives, not the fused result (P5). |
46
+ | 6 | Analog-vs-digital speed is game policy | **Agree, no action.** |
47
+ | — | *(missed)* Gamepad is unbindable through the ISM path layer | **New work.** The actual defect (P1 + P2). |
48
+
49
+ ---
50
+
51
+ ## 1. The layering premise the review is missing
52
+
53
+ meep input devices are **state mirrors of hardware**. They report what the hardware reports, plus the minimum
54
+ hygiene needed to make polled hardware observable (edge detection, stuck-input release on disconnect/stop). They
55
+ contain no game policy. This is not an accident of the gamepad implementation — it is the shape of the whole
56
+ package:
57
+
58
+ * `KeyboardDevice` exposes `keys.w.is_down`. It does not expose `keyboard.getMovement()` from WASD.
59
+ * Nobody asks it to, because the layer that owns that question already exists:
60
+ [`KeyboardCameraController.js`](ecs/controllers/KeyboardCameraController.js) — a *controller* that reads device
61
+ switches and produces a camera-frame pan vector.
62
+
63
+ `pad.getMovement()` is a request to put `KeyboardCameraController`'s job inside `KeyboardDevice`. Five concrete
64
+ reasons it does not belong there:
65
+
66
+ 1. **The game frame is not a property of the stick.** The reviewer wants `+y = up`. Up in *what*? meep's world is
67
+ 3D; "stick up" maps to a world direction that depends on the camera yaw, the projection, and whether the game
68
+ is top-down or third-person. `KeyboardCameraController` resolves exactly this and it needs a
69
+ `TopDownCameraController` to do it. A `Vector2` on a driver cannot know the answer, so any answer it bakes in
70
+ is wrong for some consumer — who then must un-negate it. That is strictly worse than never negating.
71
+
72
+ 2. **Dead-zoning is not one policy.** Radial-with-rescale is right for locomotion; per-axis is right for menu
73
+ navigation; inner+outer with a response curve is right for aiming; square-gate normalization is right for
74
+ pads that report a square gate. `{radialDeadzone}` picks one and demotes the rest to "off the golden path".
75
+
76
+ 3. **`{radialDeadzone}` is an options bag.** The codebase's position is typed and verbose over magic-looking
77
+ configuration. An options object on a per-frame hot call is the opposite of that, and it grows monotonically
78
+ — the next request is `{radialDeadzone, outerDeadzone, curve, squareGate}`.
79
+
80
+ 4. **It allocates.** `getMovement()` returning a fresh `Vector2` allocates once per consumer per frame. The
81
+ engine's convention for vector-producing calls is `f(result, ...)` writing into a caller-owned target.
82
+
83
+ 5. **It creates a dual-mode API.** With `getMovement()` present, `stickLeft` becomes "the raw one" and
84
+ `getMovement()` "the good one" — two ways to ask the same question with a silent policy difference between
85
+ them. Uniform control flow means picking the common case as the *only* case at each layer, and letting
86
+ callers who want something else drop to the lower-level primitive.
87
+
88
+ What the reviewer actually wants is real. It just belongs one layer up, and it should be *composable functions*,
89
+ not a method on the driver.
90
+
91
+ ---
92
+
93
+ ## 2. Item-by-item
94
+
95
+ *Line references in this section point at the code **as reviewed**, before any of P1–P6 landed. They are part of
96
+ the argument about what the reviewer was looking at, not navigation aids for the current tree.*
97
+
98
+ ### 2.1 "`buttons` is lazily populated and sparse, so I wrote a guard"
99
+
100
+ **This does not reproduce.** Trace the code:
101
+
102
+ * [`GamepadHandle.js:140`](devices/gamepad/GamepadHandle.js:140) — `update()` iterates **every** button in the
103
+ native snapshot and calls `getButton(i)` on each. After one update, `buttons[0 … n-1]` are all populated.
104
+ * [`GamepadDevice.js:130`](devices/GamepadDevice.js:130) — `#connect()` calls `pad.update(...)` **before**
105
+ setting `pad.connected = true`.
106
+ * [`GamepadDevice.js:86`](devices/GamepadDevice.js:86) — `main` only returns pads with `connected === true`.
107
+
108
+ So every handle reachable through `gamepad.main` has already had its full button set materialised.
109
+ `pad.buttons[GamepadButtons.a].is_down` is safe, and the `button !== undefined &&` half of the reviewer's guard
110
+ never evaluates false in a connected-pad path. The `?.` in the existing `@example` is belt-and-braces, and reads
111
+ as a warning it isn't.
112
+
113
+ The one residual hole is real but different: a **non-standard mapping** pad reporting fewer than 17 buttons.
114
+ `buttons[GamepadButtons.dpad_up]` is genuinely `undefined` there, because the button does not exist.
115
+ `pad.isDown(15)` would return `false` for it — converting "this hardware has no D-pad" into "the D-pad is not
116
+ pressed", silently, forever. That is a worse API than the honest `undefined`, and it is the reason to reject the
117
+ proposed mechanism specifically.
118
+
119
+ **The goal is still right.** Keyboard/gamepad symmetry should not have an asterisk, and `pads`/`buttons`
120
+ sparseness has a second, much more serious consequence (§3). Fix it at the source: pre-populate (P1/P2), so
121
+ there is nothing to guard and no third accessor.
122
+
123
+ ### 2.2 `getMovement()` — rejected
124
+
125
+ See §1. Replaced by P4 + P5.
126
+
127
+ ### 2.3 "Per-axis dead-zoning leaks diagonal drift and shortens the full diagonal to ~0.99"
128
+
129
+ **First half: correct.** A per-axis gate at 0.1 admits up to `sqrt(0.1² + 0.1²) ≈ 0.141` of resting magnitude,
130
+ and its boundary is anisotropic — a stick pushed to `(0.09, 0.09)` reads dead, `(0.11, 0.0)` reads live.
131
+
132
+ **Second half: not producible by this code.** The gate is
133
+ [`GamepadHandle.js:169`](devices/gamepad/GamepadHandle.js:169):
134
+
135
+ ```js
136
+ if (Math.abs(value) < deadZone) {
137
+ value = 0;
138
+ }
139
+ ```
140
+
141
+ There is no rescale, no normalization, no magnitude clamp anywhere in `update()`. A full-deflection axis passes
142
+ through byte-identical to what the hardware reported. If the reviewer measured 0.99 at full diagonal, that was
143
+ their pad's gate, not ours. This matters because it is the tell that the section was reasoned about rather than
144
+ read — and the actual defect sitting three lines away was missed.
145
+
146
+ **The actual defect** is threefold and worse than "leaks diagonals":
147
+
148
+ 1. **It is destructive.** The gated value is what gets *stored* in `axes`, so a consumer who wants radial
149
+ dead-zoning cannot recover the raw signal — the information is gone before they see it. Policy applied in a
150
+ mirror layer is not just misplaced, it is lossy.
151
+ 2. **It is discontinuous.** `0.0999 → 0`, `0.1001 → 0.1001`. Input magnitude jumps by the full dead-zone width
152
+ the instant the threshold is crossed. Any consumer mapping magnitude to speed gets a visible speed pop.
153
+ 3. **It is named for a policy it should not be implementing.** `deadZone` at `0.1` reads as "the engine has
154
+ decided your gameplay dead-zone", which is precisely how the reviewer read it.
155
+
156
+ There is a fourth problem the review could not have seen without measuring hardware, and it is the one that
157
+ kills the whole "pick a good constant" approach: **the right number varies by more than 6× across pads.** XInput
158
+ ships 0.24 (left stick) and 0.265 (right); SDL treats ±0.098 as at-rest; hall-effect and TMR pads need 0.02–0.03
159
+ and stay there for life; a worn potentiometer pad needs 0.2+ and gets worse over time. Any single constant is
160
+ sized for the worst pad in the population, and every good pad pays for it with lost range.
161
+
162
+ Fixed in P3 + P4: the device *measures* its own stick instead of guessing (learned center + noise radius), and
163
+ gameplay dead-zoning becomes a continuous, rescaling, radial primitive applied by the consumer.
164
+
165
+ ### 2.4 "Frame is undocumented at the type level — the `.d.ts` doesn't carry it"
166
+
167
+ Two problems.
168
+
169
+ * **The frame is documented**, in the file that owns the concept —
170
+ [`GamepadAxes.js:5`](devices/gamepad/GamepadAxes.js:5): *"Negative values are left/up, positive values are
171
+ right/down"*. The review cites having read it, then calls it undocumented.
172
+ * **There is no `.d.ts`.** `find src/engine/input -name "*.d.ts"` returns nothing. Hand-written `.d.ts` are not
173
+ maintained in this project; whatever the reviewer was reading was IDE inference or a stale tree. If a `.d.ts`
174
+ for this package appears, deleting it is the fix.
175
+
176
+ **Accepted sliver:** the frame note lives on the *enum*, and a consumer touching `stickLeft` may never open
177
+ `GamepadAxes.js`. One-line cross-references on `axes`, `stickLeft`, `stickRight` (P6).
178
+
179
+ ### 2.5 "No digital view of the stick, no analog view of the D-pad; fusion is on the caller"
180
+
181
+ Correct, and **fusion stays on the caller** — which of stick and D-pad wins, whether they sum or the last-touched
182
+ device latches, and whether a held D-pad suppresses stick drift are all game decisions with no defensible
183
+ default. What *is* mechanical and duplicated is the conversion on each side, so ship both directions as
184
+ primitives (P5) and leave the `max`/`or`/`latch` to the consumer.
185
+
186
+ ### 2.6 "Analog-vs-digital speed is a caller concern"
187
+
188
+ Agreed, explicitly. No action.
189
+
190
+ ---
191
+
192
+ ## 3. What the review missed: the gamepad is unbindable through the ISM
193
+
194
+ The engine's action-mapping layer resolves bindings by *path* against the device tree —
195
+ [`InputSystem.js:18`](ecs/systems/InputSystem.js:18) and
196
+ [`InputControllerSystem.js:159`](ecs/systems/InputControllerSystem.js:159), both via
197
+ [`resolvePath`](../../core/json/resolvePath.js), which **throws when a path cannot be resolved**.
198
+
199
+ Keyboard works because `KeyboardDevice` pre-populates every key from `KeyCodes` in its constructor, so
200
+ `keyboard/keys/w/down` resolves at load time, before anyone touches the hardware.
201
+
202
+ The gamepad cannot do this:
203
+
204
+ * `pads` is sparse and keyed on the hardware slot, so `gamepad/pads/0/...` throws until a pad connects;
205
+ * `buttons` is sparse until first poll, so `gamepad/pads/0/buttons/0/down` throws until the pad has been read.
206
+
207
+ Consequences: an `InputController` carrying a gamepad binding **cannot be linked until the player has already
208
+ plugged in and pressed something**; whether a given binding works depends on which physical slot the pad landed
209
+ in; and a serialized `InputBinding` referencing a gamepad path is a load-order landmine. (Unlink is safe once
210
+ link succeeded — handles are retained across disconnect — but that is the only part of the lifecycle that is.)
211
+
212
+ This — not the absence of `getMovement()` — is why the integration was "entirely on the caller". The blessed
213
+ mapping mechanism was unavailable, so the reviewer polled raw state and fused by hand, then reported the
214
+ symptoms of doing so as API friction. P1 and P2 close it as a side effect of fixing uniformity.
215
+
216
+ *(Separately: axis bindings have no per-axis granularity — only the aggregate `on.axis`, forcing listeners to
217
+ filter by index. Noted, not proposed. The string-path ISM is already the odd one out against the codebase's
218
+ typed-over-magic position; make it work, don't grow it. Direct typed state access — `pad.buttons[GamepadButtons.a].is_down`
219
+ — remains the blessed path.)*
220
+
221
+ ---
222
+
223
+ ## 4. Proposed work
224
+
225
+ ### P1 — `GamepadHandle` pre-populates the standard mapping
226
+
227
+ ```js
228
+ // gamepad/GamepadStandardMapping.js
229
+ export const GAMEPAD_STANDARD_BUTTON_COUNT = 17;
230
+ export const GAMEPAD_STANDARD_AXIS_COUNT = 4;
231
+ ```
232
+
233
+ `GamepadHandle` materialises those in its constructor: 17 `InputDeviceSwitch`, 17 zeroed `buttonValues`, 4 zeroed
234
+ `axes`. `getButton` stays as-is and keeps growing the array for hardware exposing more than 17 buttons.
235
+
236
+ Effect: `pad.buttons[GamepadButtons.dpad_up].is_down` is total. No guard, no `?.`, no `isDown()`. Exactly the
237
+ keyboard's contract.
238
+
239
+ **Tradeoff, stated explicitly:** on a non-standard pad with 6 buttons, `buttons[12]` now reads "up" rather than
240
+ "absent". Absence is signalled by `pad.mapping !== "standard"`, which is where the Gamepad API puts it, not by
241
+ holes in an array. Uniform beats expressive here — a hole is not a usable absence signal anyway, since the
242
+ consumer sees a hole for "no such button" and (previously) also for "not polled yet".
243
+
244
+ Cost: 17 small objects per handle, allocated once.
245
+
246
+ ### P2 — `main` becomes a persistent handle bound to the active pad
247
+
248
+ *(Replaces revision 1's slot pre-population. This is the better design: it gives the binding layer a stable path
249
+ without pretending four pads exist, and it matches what a single-player consumer actually means by "the
250
+ gamepad".)*
251
+
252
+ `GamepadDevice.main` becomes a `GamepadHandle` constructed with the device, living **outside** `pads[]`, starting
253
+ disconnected:
254
+
255
+ * it binds to the first pad that connects;
256
+ * when its bound pad disconnects, it rebinds to the **oldest still-connected** pad (connection order);
257
+ * when nothing is connected, it stays present and `connected === false`, with all switches released.
258
+
259
+ Mechanically `main` is a real handle fed the *same* native snapshot as its bound pad during `poll()` — not a
260
+ proxy or a getter chain. That matters: `gamepad/main/buttons/0/down` is a stable `Signal` **object** that
261
+ resolves at engine construction and never changes identity, which is exactly what `resolvePath` needs (§3).
262
+
263
+ ```js
264
+ // GamepadDevice
265
+ #connection_sequence = 0;
266
+
267
+ /**
268
+ * The active gamepad. Always present; check {@link GamepadHandle#connected}.
269
+ * Mirrors the first connected pad, falling back to the oldest remaining one when that pad is lost.
270
+ * Not a member of {@link pads}.
271
+ */
272
+ main = new GamepadHandle();
273
+ ```
274
+
275
+ Notes on the implementation:
276
+
277
+ * **Do not** forward `main.on.*` into the device-level `on.*` signals. The pad it mirrors already forwards, so
278
+ wiring both would double-fire every press for every listener.
279
+ * On unbind, `main.reset()` — releases everything, so a yank-the-cable never leaves input stuck. The next
280
+ `update()` from the new source re-presses whatever is genuinely held. Free correctness from the existing edge
281
+ detection.
282
+ * `main.index` / `.id` / `.mapping` follow the mirrored pad, because `update()` already copies them. Useful:
283
+ a settings screen can name the active pad without touching `pads`.
284
+ * Connection order needs one counter — stamp `pad.connectionIndex = this.#connectionSequence++` inside
285
+ `#connect`, pick the smallest among connected on rebind.
286
+ * Binding, rebinding and refreshing all happen in one place at the end of `poll()`. Splitting them across
287
+ `#connect`/`#disconnect` leaves `main` a frame stale on a swap — see §6.3.
288
+
289
+ Cost: one extra handle update per poll (17 buttons + 4 axes) — noise.
290
+
291
+ **Breaking, and an improvement:** `main` was `GamepadHandle | undefined`. It is now always a handle. Existing
292
+ `if (pad !== undefined)` guards still compile and simply never trigger; the check becomes `main.connected`. This
293
+ removes the only sentinel in the gamepad API, which is the direction the codebase wants anyway.
294
+
295
+ **Deliberately unresolved:** per-slot paths (`gamepad/pads/1/...`) still don't resolve, because `pads` keeps its
296
+ honest sparse hardware-slot semantics. Local multiplayer binds through the device-level `on.down`, which already
297
+ dispatches `(buttonIndex, pad)`, or holds handles from the `connected` signal. If per-player path binding is ever
298
+ wanted, the shape is an ordered player-assignment list of which `main` is element 0 — this design doesn't
299
+ preclude it, and I'd rather add it when there's a consumer than guess the assignment policy now.
300
+
301
+ ### P3 — Learned per-stick calibration, and a `GamepadStick` abstraction
302
+
303
+ *(Replaces revision 1's fixed `axisNoiseFloor`. Appendix A is the supporting survey.)*
304
+
305
+ Three findings drive this:
306
+
307
+ 1. **In a browser there is no factory calibration, so learning is the only calibration available.** Native stacks
308
+ can read hardware calibration — a Switch pad stores per-axis min/center/max in SPI flash, XInput and SDL apply
309
+ vendor-tuned constants. The Gamepad API exposes none of it: normalized floats and nothing else. Every native
310
+ stack calibrates; we're the only ones who'd be guessing.
311
+ 2. **The dominant error is a center offset, not symmetric noise.** This is what "stick drift" *is*, and it is why
312
+ every consumer calibration tool works by sampling the resting position and subtracting it. A fixed dead zone
313
+ responds to an offset center by widening the dead area — asymmetrically, so you lose range on one side and
314
+ still drift on the other. Learning the center fixes the actual defect; a dead zone only hides it.
315
+ 3. **The right radius varies 6×+ across the installed base** (§2.3). Measuring is the only way to give a TMR pad
316
+ its 0.02 and a worn pad its 0.15 from one code path.
317
+
318
+ **Layering — why this doesn't contradict §1.** Calibration is *hardware characterization*: learning that this
319
+ stick rests at `(0.03, -0.01)` with `0.018` of dispersion makes the mirror **more accurate**, which is squarely
320
+ the device layer's job. Gameplay dead zone, response curve, and frame conversion remain policy and stay with the
321
+ consumer via `apply_radial_dead_zone`. The line is: *the device removes what the hardware got wrong; the game
322
+ decides what to do with what's left.* Getting this backwards is a known failure mode — SDL applying its own dead
323
+ zone under a game that applies another is a long-standing complaint, and the result is exactly the doubled dead
324
+ zone with all precision gone.
325
+
326
+ #### The abstraction
327
+
328
+ ```js
329
+ export class GamepadStick {
330
+ /** Indices into {@link GamepadHandle#axes} that this stick reads. */
331
+ axisX = 0;
332
+ axisY = 1;
333
+
334
+ /** Hardware value, exactly as reported. */
335
+ raw = new Vector2();
336
+
337
+ /** Calibrated value: center-corrected and noise-gated. This is what gameplay should read. */
338
+ value = new Vector2();
339
+
340
+ /** @type {GamepadStickCalibration} */
341
+ calibration = new GamepadStickCalibration();
342
+
343
+ on = {
344
+ /** Fires when {@link value} changes. A calibrated stick at rest is silent. */
345
+ changed: new Signal()
346
+ };
347
+
348
+ /** Refresh from raw axis values, feeding the calibrator on the way through. */
349
+ update(axes) {}
350
+
351
+ /** Return to neutral, keeping the calibration — the hardware has not changed. */
352
+ reset() {}
353
+
354
+ /** Discard what was learned. For when the handle starts representing *different* hardware. */
355
+ forgetCalibration() {}
356
+ }
357
+
358
+ export class GamepadStickCalibration {
359
+ /** Learned rest position. */
360
+ center = new Vector2();
361
+
362
+ /** Learned dispersion around {@link center}; magnitudes inside this read as 0. */
363
+ noiseRadius = 0;
364
+
365
+ /** When true, learning is suspended and the current values are used as-is. */
366
+ locked = false;
367
+ }
368
+ ```
369
+
370
+ `stickLeft` / `stickRight` become `GamepadStick` rather than `Vector2`. **Breaking:** `pad.stickLeft.x` →
371
+ `pad.stickLeft.value.x`. Changelog line; this is public surface.
372
+
373
+ **Opt-out without a mode flag.** You asked for an opt-out, and the obvious form of it — a boolean that switches
374
+ between two code paths — is the dual-mode shape we rejected for `getMovement()`. It isn't needed here: the
375
+ transform is *always* applied, and an identity calibration (`center = (0,0)`, `noiseRadius = 0`, `locked =
376
+ true`) is a bit-exact passthrough. One code path, and "no filtering" is just a particular calibration. The same
377
+ mechanism covers a settings-screen manual dead zone and a saved per-controller profile — `GamepadStickCalibration`
378
+ gets `to/fromBinaryBuffer`, keyed on `pad.id`.
379
+
380
+ **Calibration belongs to hardware, not to a handle.** A handle outlives the pad it represents: slots get
381
+ re-occupied, and `main` moves between pads. Carrying an estimate across that would apply one pad's correction to
382
+ another — and because the calibrator only ever moves its estimate *closer* to the origin (below), a new pad
383
+ resting further out could never adopt its own rest position at all. So `forgetCalibration()` is called on a fresh
384
+ connection and when `main` binds a new source. It is a no-op on a locked calibration: a supplied profile is not
385
+ ours to discard.
386
+
387
+ #### The learning algorithm
388
+
389
+ **Phase A — rest estimation.** Runs continuously and unattended; there is no separate "on connect" mode.
390
+
391
+ * Collect `REST_SAMPLE_COUNT = 60` raw samples (≈1 s at rAF) as a fixed, non-overlapping window.
392
+ * **Reject the whole window** if any sample magnitude exceeds `REST_CANDIDATE_MAX = 0.08`, or if per-axis spread
393
+ (max − min) exceeds `REST_SPREAD_MAX = 0.015`. Resting and holding cannot be told apart inside one window —
394
+ both are steady — so the bounds are biased hard toward rejection, on the asymmetry that failing to calibrate
395
+ leaves an exact passthrough while calibrating wrongly injects phantom deflection. The spread bound is the
396
+ primary defence, and it exploits a physical difference: a held stick carries hand tremor, a settled one
397
+ carries only sensor noise, and the two differ by roughly an order of magnitude. (§6.1 and §6.4 record how
398
+ these numbers were arrived at — the first version of this section got the reasoning wrong.)
399
+ * **Center = per-axis median** of the window. Median, not mean: one spike shifts a 60-sample mean by 1.7% of its
400
+ amplitude, and shifts the median by nothing.
401
+ * **`noiseRadius` = median(d) + `NOISE_RADIUS_DEVIATIONS`·MAD(d)** where `d` are the sample distances from the
402
+ learned center and MAD is the median absolute deviation. MAD is the standard robust dispersion estimator — a
403
+ high quantile computed from 60 samples is dominated by its own tail, which is precisely the outliers we're
404
+ trying not to fit. No clamp: the spread bound already constrains the result structurally, to
405
+ `NOISE_RADIUS_MAX = REST_SPREAD_MAX · √2 · (1 + NOISE_RADIUS_DEVIATIONS)` ≈ 0.106.
406
+ * **Adopt only if closer in.** A window is written to the calibration only when its center is nearer the origin
407
+ than the accepted one. This is what makes a wrong estimate self-correcting: a stick that was being held when
408
+ sampling started is superseded the moment it is released, with no separate recovery path. The trade is that
409
+ drift *growing* within a session is not followed — a wear process measured in weeks, and calibration restarts
410
+ whenever the pad reconnects.
411
+ * **Recovery takes up to two windows.** Windows are fixed and non-overlapping, so the one straddling a release
412
+ or a rebind still holds pre-transition samples and is rejected on spread; only the window after it is clean.
413
+ This affects how quickly a correct estimate is found, never whether it is correct.
414
+
415
+ **Phase B — range estimation.** Proposed as **optional and off by default.** Pads differ in gate shape (square
416
+ gates reach ~1.41 at full diagonal, circular ones ~1.0), so a learned per-direction max would let us normalize to
417
+ a unit circle. But a running max is a self-poisoning estimator — one spike raises the ceiling permanently and
418
+ silently compresses everything afterwards. If we do it: require K consecutive samples above the current maximum
419
+ before raising it, clamp to `[0.8, 1.5]`, and decay slowly. I'd rather ship Phase A, which is high-value and
420
+ low-risk, and leave this behind a flag until a real pad demands it.
421
+
422
+ **Not proposed, but worth knowing about:** dead-zoning only addresses error *near center*. Jitter while the stick
423
+ is **deflected** is a separate problem, and the state of the art for it is the 1€ filter (Casiez et al., CHI
424
+ 2012) — a speed-adaptive low-pass with two parameters: low cutoff at low speed kills jitter, rising cutoff at
425
+ speed kills lag. It's ~30 lines and would slot in as an optional stage on `GamepadStick` between `raw` and
426
+ `value`. I'd hold it until someone reports jitter that calibration doesn't fix, but the abstraction should leave
427
+ room for it — which it does, since the stage lives inside the stick and consumers only see `value`.
428
+
429
+ #### Consequences for `axes` and `deadZone`
430
+
431
+ * **`pad.axes` becomes raw** — a true mirror, no destructive gating (fixes §2.3's real defect). Non-stick axes on
432
+ exotic mappings are unaffected by stick calibration, as they should be.
433
+ * **`GamepadDevice.deadZone` is removed**, replaced by a `@deprecated` accessor whose setter writes
434
+ `noiseRadius` into every stick — existing and subsequently connected — and locks them. That is a faithful
435
+ migration: the old behaviour *was* a manually-specified fixed gate, and a locked calibration is exactly that.
436
+ Locking also means such a value survives reconnection, unlike a learned one.
437
+ * **`on.axis` now fires at hardware sample rate on a drifting pad**, where the gate used to silence it. Decided
438
+ in favour of leaving it: the raw layer is honest, and the recommended path (`stick.on.changed`) is silent at
439
+ rest because the calibrated value genuinely doesn't change. The rejected alternative was gating `on.axis` at
440
+ an epsilon, which is the destructive gate creeping back in under a smaller number. This is a behaviour change
441
+ for anything bound to `on.axis`.
442
+
443
+ ### P4 — `apply_radial_dead_zone(result, x, y, inner, outer)`
444
+
445
+ `engine/input/analog/apply_radial_dead_zone.js` — device-agnostic, no allocation, no options bag, continuous:
446
+
447
+ ```js
448
+ export function apply_radial_dead_zone(result, x, y, inner, outer) {
449
+ const m = Math.sqrt(x * x + y * y);
450
+
451
+ if (m <= inner) {
452
+ result.set(0, 0);
453
+ return;
454
+ }
455
+
456
+ const scale = Math.min((m - inner) / (outer - inner), 1) / m;
457
+
458
+ result.set(x * scale, y * scale);
459
+ }
460
+ ```
461
+
462
+ Isotropic boundary (no diagonal leak), continuous at `inner` (no speed pop), saturates to unit magnitude at
463
+ `outer` (no `>1` from square-gate pads). Applies equally to a stick, a touch nub, or a mouse delta.
464
+
465
+ ### P5 — `gamepad_write_dpad_vector(result, pad)`
466
+
467
+ `engine/input/devices/gamepad/gamepad_write_dpad_vector.js` — the D-pad as an analog vector, **in hardware
468
+ frame** (`+y` down), so it composes with `stickLeft` without the consumer tracking which of the two is flipped:
469
+
470
+ ```js
471
+ export function gamepad_write_dpad_vector(result, pad) {
472
+ const buttons = pad.buttons;
473
+
474
+ const up = buttons[GamepadButtons.dpad_up].is_down;
475
+ const down = buttons[GamepadButtons.dpad_down].is_down;
476
+ const left = buttons[GamepadButtons.dpad_left].is_down;
477
+ const right = buttons[GamepadButtons.dpad_right].is_down;
478
+
479
+ result.set(
480
+ (right ? 1 : 0) - (left ? 1 : 0),
481
+ (down ? 1 : 0) - (up ? 1 : 0)
482
+ );
483
+ }
484
+ ```
485
+
486
+ (Total only because of P1.) The digital view of the stick is the caller's threshold on
487
+ `apply_radial_dead_zone`'s output, which is a comparison, not an API.
488
+
489
+ ### P6 — Documentation
490
+
491
+ * Cross-reference the frame on `axes`, `stickLeft`, `stickRight`: *"Hardware frame: `+x` right, `+y` down. See
492
+ {@link GamepadAxes}. Game-frame conversion belongs in the controller — see `KeyboardCameraController`."*
493
+ * Drop the misleading `?.` from the `buttons` `@example` once P1 lands.
494
+ * Worked gamepad-locomotion example in the `GamepadDevice` class doc (below), so the next consumer copies the
495
+ intended composition instead of inventing one.
496
+
497
+ ### Tests
498
+
499
+ `GamepadDevice.spec.js` currently has 7 tests and does not cover any of this. Add:
500
+
501
+ * P1: all standard buttons defined on a fresh handle, before any poll; `getButton(20)` still grows.
502
+ * P2: `main` exists and is disconnected on a fresh device; binds to the first pad that connects; mirrors its
503
+ buttons and axes; on disconnect of the bound pad falls back to the oldest remaining connected pad; goes
504
+ disconnected with everything released when the last pad is lost; a button held at disconnect is released
505
+ exactly once; a press on the bound pad fires device-level `on.down` exactly once (double-forwarding guard).
506
+ * §3 regression: `resolvePath(devices, "gamepad/main/buttons/0/down")` resolves to a Signal on a fresh engine
507
+ with no hardware, is the *same object* after connect→disconnect→reconnect, and dispatches across a pad swap.
508
+ * P3 calibration: a window centered at `(0.03, -0.01)` with small noise yields a center within tolerance and a
509
+ non-zero, structurally bounded `noiseRadius`; a window containing a held stick is **rejected**; a window with
510
+ one 0.9 spike is rejected; a swept window is rejected; a window with one mild outlier still yields a
511
+ median-based center unmoved beyond tolerance; identity + `locked` is a bit-exact passthrough of `raw` to
512
+ `value`; calibrated `value` is exactly `(0,0)` for samples inside the learned radius; `axes` are byte-identical
513
+ to source at all deflections (mirror contract); the deprecated `deadZone` setter reaches pads connected later.
514
+ * P3 adversarial (§6.4): connecting while the stick is fully deflected, diagonally deflected, or resting at an
515
+ axis extreme never calibrates; connecting while it is *slightly* deflected stays bounded below a usable
516
+ gameplay dead zone and recovers; `main` and a reused slot handle each forget a previous pad's calibration, and
517
+ can then learn a rest position further out than it; a locked calibration survives reconnection.
518
+ * P4: rest → `(0,0)`; just inside `inner` → `(0,0)`; just outside `inner` → magnitude ≈ 0 (continuity, no jump);
519
+ at `outer` → magnitude 1; beyond `outer` → magnitude 1; full diagonal on a square-gate pad → magnitude 1, not
520
+ 1.41; isotropy — `(0.14, 0.14)` and `(0.198, 0)` land within epsilon of the same magnitude.
521
+ * P5: each direction; opposing pair cancels to 0; diagonal → `(1, 1)` pre-normalization.
522
+
523
+ All black-box, against real `GamepadHandle` / `InputDeviceSwitch` / a plain object standing in for the native
524
+ `Gamepad` snapshot (a platform type we cannot construct in node — the only fake here).
525
+
526
+ ### After this proposal, the reviewer's integration reads:
527
+
528
+ ```js
529
+ const pad = engine.devices.gamepad.main; // always present
530
+
531
+ if (pad.connected) {
532
+ const left = pad.stickLeft.value; // already center-corrected for this specific stick
533
+
534
+ gamepad_write_dpad_vector(dpad, pad);
535
+ apply_radial_dead_zone(stick, left.x, left.y, 0.15, 0.95);
536
+
537
+ // fusion + frame: game policy, and this is the whole of it
538
+ const x = stick.x !== 0 || stick.y !== 0 ? stick.x : dpad.x;
539
+ const y = stick.x !== 0 || stick.y !== 0 ? stick.y : dpad.y;
540
+
541
+ move.set(x, -y); // this game is y-up; the negation is stated once, here, on purpose
542
+ }
543
+ ```
544
+
545
+ Shorter than what the review reported writing, with zero allocation per frame, no `undefined` guard, every policy
546
+ decision visible at the call site instead of hidden behind an options bag — and a gameplay dead zone the game
547
+ picks for feel rather than to paper over hardware, because the hardware error is already gone by this line.
548
+
549
+ Hot-swapping pads mid-session requires no code here at all: `main` rebinds and the loop doesn't notice.
550
+
551
+ ---
552
+
553
+ ## 5. Explicitly not doing
554
+
555
+ * **`pad.isDown(i)`** — a third way to ask a question `buttons[i].is_down` and `getButton(i).is_down` already
556
+ answer, which silently converts absent hardware into an unpressed button. P1 removes the need.
557
+ * **`pad.getMovement()`** — §1.
558
+ * **Frame negation anywhere in the device layer** — the device reports hardware frame, full stop. One negation,
559
+ in the controller, where the camera is known.
560
+ * **Growing the ISM string-path layer with gamepad-specific concepts** — make the existing paths resolve (P1/P2);
561
+ do not build more magic strings.
562
+ * **Per-axis signals on `GamepadHandle`** — noted as a real granularity gap in §3. Partly answered by
563
+ `GamepadStick.on.changed`; the remaining gap is non-stick axes on exotic mappings. Revisit if a consumer needs
564
+ it.
565
+ * **A calibration wizard** ("rotate the stick in circles") — the standard consumer-tool flow, and the only way to
566
+ learn true per-direction range. Out of scope: Phase A needs no UI, and Phase B is deferred. If a settings
567
+ screen wants one later, `GamepadStickCalibration` is already the serializable thing it would write to.
568
+ * **1€ filter** — right tool for deflected-stick jitter, wrong time. Left room for it inside `GamepadStick`; see
569
+ P3.
570
+
571
+ ---
572
+
573
+ ## 6. What changed during implementation
574
+
575
+ Two corrections, both found by tests that failed against the design as written above.
576
+
577
+ ### 6.1 The rest-window guard did not do what §4 claimed
578
+
579
+ *(§4 now describes the shipped bounds; what it said before this correction is quoted here.)*
580
+
581
+ Revision 2's P3 asserted that `REST_CANDIDATE_MAX = 0.30` plus a spread check together catch "the player was holding the
582
+ stick", *"because a stick held steady at 0.25 passes the first test alone"*. That reasoning is wrong: a stick
583
+ held **steady** at 0.25 also has near-zero spread, so it passes the second test too. The first implementation
584
+ duly learned a held position as the rest position, producing a phantom −0.23 deflection once the player let go.
585
+ The test asserting it wouldn't is what caught it.
586
+
587
+ There is no way to tell resting from holding inside a single window — both are steady. So the bounds are now
588
+ biased hard toward rejection, on the reasoning that **failing to calibrate is harmless** (identity is an exact
589
+ passthrough) **while calibrating wrongly is actively harmful**:
590
+
591
+ * `REST_CANDIDATE_MAX` 0.30 → **0.15**. Drift worse than this is a pad heading for replacement, and past some
592
+ deflection a steady reading is likelier to be a hand than a stick. *(Tightened again to 0.08 in §6.4, on a
593
+ better argument than this one.)*
594
+ * `REST_SPREAD_MAX` 0.05 → **0.015**. This is now the primary defence, and it exploits a physical difference the
595
+ original design ignored: a held stick carries the hand's tremor, a settled one carries only sensor noise, and
596
+ the two differ by roughly an order of magnitude. The cost is that an unusually noisy stick never calibrates,
597
+ which is the safe failure.
598
+
599
+ The residual hole is documented rather than papered over: a player parked at a small steady deflection for a
600
+ full second can still poison the estimate until they release, bounded by `REST_CANDIDATE_MAX`, and the
601
+ minimum-magnitude rule then supersedes it. Both the poisoning and the recovery are tested.
602
+
603
+ ### 6.2 `NOISE_RADIUS_MAX` became unreachable, so the clamp went
604
+
605
+ With the spread bound at 0.015, a window that passes it cannot produce a radius anywhere near 0.20 — the result
606
+ is structurally bounded at `REST_SPREAD_MAX · √2 · (1 + NOISE_RADIUS_DEVIATIONS)` ≈ 0.106. The clamp was a guard
607
+ for a condition that can no longer occur, so it was removed and the constant kept as a derived, documented
608
+ bound. The test asserts the structural property instead of the removed clamp.
609
+
610
+ ### 6.3 Two smaller notes
611
+
612
+ * **`main` rebinding was one frame stale.** Binding logic initially lived in `#connect`/`#disconnect`, so a
613
+ successor picked up mid-poll wasn't fed hardware until the *next* poll — `main.connected` read true while
614
+ `main.id` still named the unplugged pad. Binding, rebinding and refreshing now all happen in one place at the
615
+ end of `poll()`, which resolves a swap within a single poll and, incidentally, removed a pre-existing
616
+ duplicate `pad.update()` per poll.
617
+ * **The deprecated `deadZone` setter is plumbed properly**, not just applied to pads that happen to exist when
618
+ it is written — new handles pick the override up in `#obtainHandle`. A half-working compatibility shim would
619
+ have been worse than none.
620
+
621
+ ### 6.4 Adversarial pass: connecting with a deflected stick
622
+
623
+ Two scenarios were put to the implementation afterwards. One was safe, one was not, and chasing them turned up a
624
+ third defect neither scenario named.
625
+
626
+ **Connecting with a *fully* deflected stick — safe by construction.** Every sample is far outside
627
+ `REST_CANDIDATE_MAX`, so every window is rejected for as long as the stick is held, and release leaves an exact
628
+ passthrough rather than a phantom pull the other way. Same for a diagonal hold, and for the non-standard
629
+ mappings that report a released trigger as an axis resting at −1: those never calibrate, which is the correct
630
+ outcome, not a silent wrong one.
631
+
632
+ **Connecting with a *partially* deflected stick — this was the real gap.** A steady hold inside the plausibility
633
+ bound is genuinely indistinguishable from drift in a single window, so it was adopted as the rest position and
634
+ produced a phantom deflection of the same size on release. It self-corrected, but "bounded at 0.15 and recovers
635
+ in about two seconds" is a weaker answer than it needs to be, because the bound was set too generously:
636
+
637
+ * `REST_CANDIDATE_MAX` 0.15 → **0.08**. The reasoning that fixes it is an asymmetry the earlier revisions
638
+ missed. Auto-calibration earns its keep on *small* drift — the kind that sits under a gameplay dead zone and
639
+ still causes slow creep. Drift larger than that is already absorbed by any sane gameplay dead zone (XInput
640
+ ships 0.24), so declining to calibrate it costs almost nothing. Poisoning, on the other hand, costs a phantom
641
+ deflection. Tightening trades away the case that needs us least, and in exchange the worst wrong estimate now
642
+ lands **below any usable gameplay dead zone** — it never reaches the game at all before being superseded.
643
+ That is asserted directly rather than implied.
644
+
645
+ **The defect neither scenario named: a handle outliving the hardware it learned.** `main` rebinding to a
646
+ different pad, and a slot handle being reused when a different pad is plugged into that slot, both carried the
647
+ previous pad's calibration onto new hardware. Worse than a stale offset: because the calibrator only ever moves
648
+ its estimate *closer* to the origin, a new pad whose rest position sat further out than the old one's could
649
+ **never** have its own rest position adopted — the wrong calibration would have been permanent. Both paths now
650
+ call `forgetCalibration()`, which is a no-op on a locked calibration, since a supplied profile is not ours to
651
+ discard. Regression tests cover both, including a new pad resting further out and the other way.
652
+
653
+ **Recovery takes up to two windows, not one.** Windows are fixed and non-overlapping, so the one straddling a
654
+ release or a rebind still contains pre-transition samples and is rejected on spread; only the window after it is
655
+ clean. This affects how fast a correct estimate is found, never whether it is correct. Stated here because the
656
+ first tests written assumed one window and were wrong about the contract, not about the code.
657
+
658
+ **Tests: 101 passing across 13 suites in `engine/input`.** New: `GamepadHandle.spec.js`,
659
+ `GamepadStick.spec.js`, `GamepadStickCalibration.spec.js`, `gamepad_write_dpad_vector.spec.js`,
660
+ `apply_radial_dead_zone.spec.js`, plus P2/P3 coverage and the `resolvePath` regression in
661
+ `GamepadDevice.spec.js`.
662
+
663
+ ---
664
+
665
+ ## Appendix A — What the state of the art actually does
666
+
667
+ Surveyed while designing P3. Three things are worth having on the record.
668
+
669
+ **Nobody good uses a single hard-coded constant, and everybody who ships one ships a different number.**
670
+
671
+ | Stack | At-rest threshold | Notes |
672
+ |---|---|---|
673
+ | XInput (Microsoft) | 7849/32767 ≈ **0.240** left, 8689/32767 ≈ **0.265** right | Vendor-recommended constants, per-stick — even Microsoft doesn't use one number for both sticks on the same pad |
674
+ | SDL | ±3200/32767 ≈ **0.098** | Documented as "consider at rest"; SDL notes advanced UI should let users set or **autodetect** it |
675
+ | Hall-effect / TMR pads (current gen) | **0.02–0.03** | Manufacturer guidance; stable for the life of the pad rather than degrading |
676
+ | Worn potentiometer pads | **0.2+**, rising | The population XInput's 0.24 is sized for |
677
+ | meep today | 0.1, per-axis | Between the two, correct for neither |
678
+
679
+ That spread is the argument: a constant is a bet on the worst pad you expect to see, and the modern population is
680
+ bimodal — drift-free magnetic sticks alongside worn potentiometer ones. Measuring costs 60 samples once.
681
+
682
+ **Native stacks calibrate against hardware data we cannot get.** A Switch pad carries factory calibration in SPI
683
+ flash (per-axis min/center/max, at `0x8010`); desktop stacks apply vendor tables. The Gamepad API gives us
684
+ normalized floats and `id`, and nothing else. So the choice isn't "learn vs. read the real values" — it's "learn
685
+ vs. guess".
686
+
687
+ **Consumer calibration tools all do the same two things**, which is a decent signal about what actually works:
688
+ sample the resting position over a short window (≈60 frames is the common figure) and subtract it; then optionally
689
+ sweep for range. Firmware-level fixes write the corrected center point to controller NVS. This is Phase A and
690
+ Phase B, and it's why P3 puts center estimation first — offset is the defect people call "drift", and a dead zone
691
+ never fixes an offset, it only hides half of one.
692
+
693
+ **Dead-zone geometry.** Josh Sutphin's taxonomy (axial / radial / scaled radial / sloped-scaled-axial / hybrid)
694
+ is still the reference, with a good annotated implementation set at
695
+ [Minimuino/thumbstick-deadzones](https://github.com/Minimuino/thumbstick-deadzones). Scaled radial with inner and
696
+ outer bounds is P4, and it's the right default: isotropic boundary, full `[0,1]` output range preserved,
697
+ continuous at the inner edge. Axial has legitimate uses (menus, anything wanting pure horizontal/vertical), which
698
+ is another reason it belongs in a consumer-side function rather than baked into the device.
699
+
700
+ **Jitter vs. drift are different problems.** Dead zones address error near center. Error while deflected is
701
+ jitter, and the state of the art is the 1€ filter (Casiez, Roussel & Vogel, CHI 2012) — a first-order low-pass
702
+ whose cutoff rises with signal speed, so it stabilizes at rest and doesn't lag under motion. Two parameters,
703
+ trivial to implement, measurably less lag than fixed low-pass at equal jitter reduction. Deferred, not dismissed.
704
+
705
+ Sources:
706
+ [XInput deadzone constants](https://learn.microsoft.com/en-us/previous-versions/windows/xna/bb197229(v=xnagamestudio.42)) ·
707
+ [SDL3 gamepad axis](https://wiki.libsdl.org/SDL3/SDL_GamepadAxis) ·
708
+ [SDL: stacked deadzones issue](https://github.com/libsdl-org/SDL/issues/3781) ·
709
+ [Doing Thumbstick Dead Zones Right — Josh Sutphin](https://joshsutphin.com/blog/doing-thumbstick-dead-zones-right.html) ·
710
+ [Understanding thumbstick deadzones](https://minimuino.github.io/thumbstick-deadzones/) ·
711
+ [Switch SPI calibration layout](https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/spi_flash_notes.md) ·
712
+ [Analog stick calibration data — Hand Held Legend](https://wiki.handheldlegend.com/analog-stick-calibration-data) ·
713
+ [Hall-effect vs traditional thumbsticks](https://www.turtlebeach.com/blog/hall-effect-thumbsticks-vs-traditional-joysticks) ·
714
+ [TMR vs hall-effect](https://www.easysmx.com/blogs/easysmx-controllers/tmr-vs-hall-effect-joysticks) ·
715
+ [Controller calibration & drift — SCUF](https://www.scufgaming.com/us/en/gaming/products/scuf-products/controller-calibration-dead-zones-and-stick-drift-explained/) ·
716
+ [1€ filter (CHI 2012, PDF)](https://direction.bordeaux.inria.fr/~roussel/publications/2012-CHI-one-euro-filter.pdf) ·
717
+ [1€ filter reference implementations](https://github.com/casiez/OneEuroFilter)